Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/i915/bxt: Broxton decoupled MMIO

Decoupled MMIO is an alternative way to access forcewake domain
registers, which requires less cycles for a single read/write and
avoids frequent software forcewake.
This certainly gives advantage over the forcewake as this new
mechanism “decouples” CPU cycles and allow them to complete even
when GT is in a CPD (frequency change) or C6 state.

This can co-exist with forcewake and we will continue to use forcewake
as appropriate. E.g. 64-bit register writes to avoid writing 2 dwords
separately and land into funny situations.

v2:
- Moved platform check out of the function and got rid of duplicate
functions to find out decoupled power domain (Chris)
- Added a check for forcewake already held and skipped decoupled
access (Chris)
- Skipped writing 64 bit registers through decoupled MMIO (Chris)

v3:
- Improved commit message with more info on decoupled mmio (Tvrtko)
- Changed decoupled operation to enum and used u32 instead of
uint_32 data type for register offset (Tvrtko)
- Moved HAS_DECOUPLED_MMIO to device info (Tvrtko)
- Added lookup table for converting fw_engine to pd_engine (Tvrtko)
- Improved __gen9_decoupled_read and __gen9_decoupled_write
routines (Tvrtko)

v4:
- Fixed alignment and variable names (Chris)
- Write GEN9_DECOUPLED_REG0_DW1 register in just one go (Zhe Wang)

v5:
- Changed HAS_DECOUPLED_MMIO() argument name to dev_priv (Tvrtko)
- Sanitize info->had_decoupled_mmio at init (Chris)

Signed-off-by: Zhe Wang <zhe1.wang@intel.com>
Signed-off-by: Praveen Paneri <praveen.paneri@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Reviewed-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1479230360-22395-1-git-send-email-praveen.paneri@intel.com

authored by

Praveen Paneri and committed by
Tvrtko Ursulin
85ee17eb 5eff503b

+139 -1
+16 -1
drivers/gpu/drm/i915/i915_drv.h
··· 554 554 #define FW_REG_READ (1) 555 555 #define FW_REG_WRITE (2) 556 556 557 + enum decoupled_power_domain { 558 + GEN9_DECOUPLED_PD_BLITTER = 0, 559 + GEN9_DECOUPLED_PD_RENDER, 560 + GEN9_DECOUPLED_PD_MEDIA, 561 + GEN9_DECOUPLED_PD_ALL 562 + }; 563 + 564 + enum decoupled_ops { 565 + GEN9_DECOUPLED_OP_WRITE = 0, 566 + GEN9_DECOUPLED_OP_READ 567 + }; 568 + 557 569 enum forcewake_domains 558 570 intel_uncore_forcewake_for_reg(struct drm_i915_private *dev_priv, 559 571 i915_reg_t reg, unsigned int op); ··· 700 688 func(cursor_needs_physical); \ 701 689 func(hws_needs_physical); \ 702 690 func(overlay_needs_physical); \ 703 - func(supports_tv) 691 + func(supports_tv); \ 692 + func(has_decoupled_mmio) 704 693 705 694 struct sseu_dev_info { 706 695 u8 slice_mask; ··· 2662 2649 2663 2650 #define GT_FREQUENCY_MULTIPLIER 50 2664 2651 #define GEN9_FREQ_SCALER 3 2652 + 2653 + #define HAS_DECOUPLED_MMIO(dev_priv) (INTEL_INFO(dev_priv)->has_decoupled_mmio) 2665 2654 2666 2655 #include "i915_trace.h" 2667 2656
+1
drivers/gpu/drm/i915/i915_pci.c
··· 363 363 .has_hw_contexts = 1, 364 364 .has_logical_ring_contexts = 1, 365 365 .has_guc = 1, 366 + .has_decoupled_mmio = 1, 366 367 .ddb_size = 512, 367 368 GEN_DEFAULT_PIPEOFFSETS, 368 369 IVB_CURSOR_OFFSETS,
+7
drivers/gpu/drm/i915/i915_reg.h
··· 7342 7342 #define SKL_FUSE_PG1_DIST_STATUS (1<<26) 7343 7343 #define SKL_FUSE_PG2_DIST_STATUS (1<<25) 7344 7344 7345 + /* Decoupled MMIO register pair for kernel driver */ 7346 + #define GEN9_DECOUPLED_REG0_DW0 _MMIO(0xF00) 7347 + #define GEN9_DECOUPLED_REG0_DW1 _MMIO(0xF04) 7348 + #define GEN9_DECOUPLED_DW1_GO (1<<31) 7349 + #define GEN9_DECOUPLED_PD_SHIFT 28 7350 + #define GEN9_DECOUPLED_OP_SHIFT 24 7351 + 7345 7352 /* Per-pipe DDI Function Control */ 7346 7353 #define _TRANS_DDI_FUNC_CTL_A 0x60400 7347 7354 #define _TRANS_DDI_FUNC_CTL_B 0x61400
+115
drivers/gpu/drm/i915/intel_uncore.c
··· 402 402 static void __intel_uncore_early_sanitize(struct drm_i915_private *dev_priv, 403 403 bool restore_forcewake) 404 404 { 405 + struct intel_device_info *info = mkwrite_device_info(dev_priv); 406 + 405 407 /* clear out unclaimed reg detection bit */ 406 408 if (check_for_unclaimed_mmio(dev_priv)) 407 409 DRM_DEBUG("unclaimed mmio detected on uncore init, clearing\n"); ··· 420 418 GT_FIFO_CTL_BLOCK_ALL_POLICY_STALL | 421 419 GT_FIFO_CTL_RC6_POLICY_STALL); 422 420 } 421 + 422 + /* Enable Decoupled MMIO only on BXT C stepping onwards */ 423 + if (!IS_BXT_REVID(dev_priv, BXT_REVID_C0, REVID_FOREVER)) 424 + info->has_decoupled_mmio = false; 423 425 424 426 intel_uncore_forcewake_reset(dev_priv, restore_forcewake); 425 427 } ··· 837 831 __unclaimed_reg_debug(dev_priv, reg, read, before); 838 832 } 839 833 834 + static const enum decoupled_power_domain fw2dpd_domain[] = { 835 + GEN9_DECOUPLED_PD_RENDER, 836 + GEN9_DECOUPLED_PD_BLITTER, 837 + GEN9_DECOUPLED_PD_ALL, 838 + GEN9_DECOUPLED_PD_MEDIA, 839 + GEN9_DECOUPLED_PD_ALL, 840 + GEN9_DECOUPLED_PD_ALL, 841 + GEN9_DECOUPLED_PD_ALL 842 + }; 843 + 844 + /* 845 + * Decoupled MMIO access for only 1 DWORD 846 + */ 847 + static void __gen9_decoupled_mmio_access(struct drm_i915_private *dev_priv, 848 + u32 reg, 849 + enum forcewake_domains fw_domain, 850 + enum decoupled_ops operation) 851 + { 852 + enum decoupled_power_domain dp_domain; 853 + u32 ctrl_reg_data = 0; 854 + 855 + dp_domain = fw2dpd_domain[fw_domain - 1]; 856 + 857 + ctrl_reg_data |= reg; 858 + ctrl_reg_data |= (operation << GEN9_DECOUPLED_OP_SHIFT); 859 + ctrl_reg_data |= (dp_domain << GEN9_DECOUPLED_PD_SHIFT); 860 + ctrl_reg_data |= GEN9_DECOUPLED_DW1_GO; 861 + __raw_i915_write32(dev_priv, GEN9_DECOUPLED_REG0_DW1, ctrl_reg_data); 862 + 863 + if (wait_for_atomic((__raw_i915_read32(dev_priv, 864 + GEN9_DECOUPLED_REG0_DW1) & 865 + GEN9_DECOUPLED_DW1_GO) == 0, 866 + FORCEWAKE_ACK_TIMEOUT_MS)) 867 + DRM_ERROR("Decoupled MMIO wait timed out\n"); 868 + } 869 + 870 + static inline u32 871 + __gen9_decoupled_mmio_read32(struct drm_i915_private *dev_priv, 872 + u32 reg, 873 + enum forcewake_domains fw_domain) 874 + { 875 + __gen9_decoupled_mmio_access(dev_priv, reg, fw_domain, 876 + GEN9_DECOUPLED_OP_READ); 877 + 878 + return __raw_i915_read32(dev_priv, GEN9_DECOUPLED_REG0_DW0); 879 + } 880 + 881 + static inline void 882 + __gen9_decoupled_mmio_write(struct drm_i915_private *dev_priv, 883 + u32 reg, u32 data, 884 + enum forcewake_domains fw_domain) 885 + { 886 + 887 + __raw_i915_write32(dev_priv, GEN9_DECOUPLED_REG0_DW0, data); 888 + 889 + __gen9_decoupled_mmio_access(dev_priv, reg, fw_domain, 890 + GEN9_DECOUPLED_OP_WRITE); 891 + } 892 + 893 + 840 894 #define GEN2_READ_HEADER(x) \ 841 895 u##x val = 0; \ 842 896 assert_rpm_wakelock_held(dev_priv); ··· 1001 935 GEN6_READ_FOOTER; \ 1002 936 } 1003 937 938 + #define __gen9_decoupled_read(x) \ 939 + static u##x \ 940 + gen9_decoupled_read##x(struct drm_i915_private *dev_priv, \ 941 + i915_reg_t reg, bool trace) { \ 942 + enum forcewake_domains fw_engine; \ 943 + GEN6_READ_HEADER(x); \ 944 + fw_engine = __fwtable_reg_read_fw_domains(offset); \ 945 + if (fw_engine & ~dev_priv->uncore.fw_domains_active) { \ 946 + unsigned i; \ 947 + u32 *ptr_data = (u32 *) &val; \ 948 + for (i = 0; i < x/32; i++, offset += sizeof(u32), ptr_data++) \ 949 + *ptr_data = __gen9_decoupled_mmio_read32(dev_priv, \ 950 + offset, \ 951 + fw_engine); \ 952 + } else { \ 953 + val = __raw_i915_read##x(dev_priv, reg); \ 954 + } \ 955 + GEN6_READ_FOOTER; \ 956 + } 957 + 958 + __gen9_decoupled_read(32) 959 + __gen9_decoupled_read(64) 1004 960 __fwtable_read(8) 1005 961 __fwtable_read(16) 1006 962 __fwtable_read(32) ··· 1152 1064 GEN6_WRITE_FOOTER; \ 1153 1065 } 1154 1066 1067 + #define __gen9_decoupled_write(x) \ 1068 + static void \ 1069 + gen9_decoupled_write##x(struct drm_i915_private *dev_priv, \ 1070 + i915_reg_t reg, u##x val, \ 1071 + bool trace) { \ 1072 + enum forcewake_domains fw_engine; \ 1073 + GEN6_WRITE_HEADER; \ 1074 + fw_engine = __fwtable_reg_write_fw_domains(offset); \ 1075 + if (fw_engine & ~dev_priv->uncore.fw_domains_active) \ 1076 + __gen9_decoupled_mmio_write(dev_priv, \ 1077 + offset, \ 1078 + val, \ 1079 + fw_engine); \ 1080 + else \ 1081 + __raw_i915_write##x(dev_priv, reg, val); \ 1082 + GEN6_WRITE_FOOTER; \ 1083 + } 1084 + 1085 + __gen9_decoupled_write(32) 1155 1086 __fwtable_write(8) 1156 1087 __fwtable_write(16) 1157 1088 __fwtable_write(32) ··· 1394 1287 ASSIGN_FW_DOMAINS_TABLE(__gen9_fw_ranges); 1395 1288 ASSIGN_WRITE_MMIO_VFUNCS(fwtable); 1396 1289 ASSIGN_READ_MMIO_VFUNCS(fwtable); 1290 + if (HAS_DECOUPLED_MMIO(dev_priv)) { 1291 + dev_priv->uncore.funcs.mmio_readl = 1292 + gen9_decoupled_read32; 1293 + dev_priv->uncore.funcs.mmio_readq = 1294 + gen9_decoupled_read64; 1295 + dev_priv->uncore.funcs.mmio_writel = 1296 + gen9_decoupled_write32; 1297 + } 1397 1298 break; 1398 1299 case 8: 1399 1300 if (IS_CHERRYVIEW(dev_priv)) {