about summary refs log tree commit diff
path: root/modules
diff options
context:
space:
mode:
authoraszlig <aszlig@redmoonstudios.org>2016-02-23 11:43:01 +0100
committeraszlig <aszlig@redmoonstudios.org>2016-02-23 11:43:01 +0100
commit1cc76f5b17353f1f1aceb6c1f79d7a250a8c8d63 (patch)
treeacc60e7a6915de286b38a8f21aeca451fc5fe276 /modules
parent3f8a602aeacc03e94bb163f8586b8a831861cb88 (diff)
hardware/t100ha: Add custom kernel + patches
The custom kernel has built in modules for the MMC storage and a small
patch which is essentially diff of the merge of the "drm-intel-fixes"
branch from git://anongit.freedesktop.org/drm-intel against 4.5-rc5.

This is the HEAD of what I have merged:

https://cgit.freedesktop.org/drm-intel/commit/?h=drm-intel-fixes&id=9b18572e83bfd5378b3fcff3acf123f7bddf558a

Eventually these fixes will hit mainline so we can drop them very soon.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
Diffstat (limited to 'modules')
-rw-r--r--modules/hardware/t100ha/default.nix44
-rw-r--r--modules/hardware/t100ha/drm-fixes.patch1019
2 files changed, 1057 insertions, 6 deletions
diff --git a/modules/hardware/t100ha/default.nix b/modules/hardware/t100ha/default.nix
index f0beea65..c55a1acc 100644
--- a/modules/hardware/t100ha/default.nix
+++ b/modules/hardware/t100ha/default.nix
@@ -8,13 +8,45 @@ in {
   options.vuizvui.hardware.t100ha.enable = lib.mkEnableOption desc;
 
   config = lib.mkIf cfg.enable {
-    # Needed for booting from MMC:
-    boot.initrd.availableKernelModules = [
-      "xhci_pci" "sdhci_acpi" "mmc_block"
-    ];
+    # It's a CherryTrail SoC, so we want to have the latest and greatest with a
+    # few additional patches:
+    boot.kernelPackages = let
+      nixpkgs = import ../../../nixpkgs-path.nix;
+      mkKernel = import "${nixpkgs}/pkgs/os-specific/linux/kernel/generic.nix";
+      t100haKernel = mkKernel rec {
+        version = "4.5-rc5";
+        modDirVersion = "4.5.0-rc5";
+        extraMeta.branch = "4.5";
 
-    # It's a CherryTrail SoC, so we want to have the latest and greatest:
-    boot.kernelPackages = pkgs.linuxPackages_latest;
+        src = pkgs.fetchurl {
+          url = "mirror://kernel/linux/kernel/v4.x/testing/"
+              + "linux-${version}.tar.xz";
+          sha256 = "06qlypnrlkckxhf3clq6l2d3kps7rwfw811sxapjbnhzjd75fcx8";
+        };
+
+        kernelPatches = lib.singleton {
+          name = "drm-fixes.patch";
+          patch = ./drm-fixes.patch;
+        };
+
+        extraConfig = ''
+          MMC y
+          MMC_BLOCK y
+          MMC_SDHCI y
+          MMC_SDHCI_ACPI y
+          PINCTRL_CHERRYVIEW y
+        '';
+
+        features.iwlwifi = true;
+        features.efiBootStub = true;
+        features.needsCifsUtils = true;
+        features.canDisableNetfilterConntrackHelpers = true;
+        features.netfilterRPFilter = true;
+
+        inherit (pkgs) stdenv perl buildLinux;
+      };
+      self = pkgs.linuxPackagesFor t100haKernel self;
+    in self;
 
     # By default the console is rotated by 90 degrees to the right.
     boot.kernelParams = [ "fbcon=rotate:3" ];
diff --git a/modules/hardware/t100ha/drm-fixes.patch b/modules/hardware/t100ha/drm-fixes.patch
new file mode 100644
index 00000000..2aceb0dc
--- /dev/null
+++ b/modules/hardware/t100ha/drm-fixes.patch
@@ -0,0 +1,1019 @@
+diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
+index 0fc38bb..cf39ed3 100644
+--- a/drivers/gpu/drm/i915/i915_debugfs.c
++++ b/drivers/gpu/drm/i915/i915_debugfs.c
+@@ -825,8 +825,11 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
+ 		}
+ 
+ 		for_each_pipe(dev_priv, pipe) {
+-			if (!intel_display_power_is_enabled(dev_priv,
+-						POWER_DOMAIN_PIPE(pipe))) {
++			enum intel_display_power_domain power_domain;
++
++			power_domain = POWER_DOMAIN_PIPE(pipe);
++			if (!intel_display_power_get_if_enabled(dev_priv,
++								power_domain)) {
+ 				seq_printf(m, "Pipe %c power disabled\n",
+ 					   pipe_name(pipe));
+ 				continue;
+@@ -840,6 +843,8 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
+ 			seq_printf(m, "Pipe %c IER:\t%08x\n",
+ 				   pipe_name(pipe),
+ 				   I915_READ(GEN8_DE_PIPE_IER(pipe)));
++
++			intel_display_power_put(dev_priv, power_domain);
+ 		}
+ 
+ 		seq_printf(m, "Display Engine port interrupt mask:\t%08x\n",
+@@ -3985,6 +3990,7 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe,
+ 	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));
++	enum intel_display_power_domain power_domain;
+ 	u32 val = 0; /* shut up gcc */
+ 	int ret;
+ 
+@@ -3995,7 +4001,8 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe,
+ 	if (pipe_crc->source && source)
+ 		return -EINVAL;
+ 
+-	if (!intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_PIPE(pipe))) {
++	power_domain = POWER_DOMAIN_PIPE(pipe);
++	if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) {
+ 		DRM_DEBUG_KMS("Trying to capture CRC while pipe is off\n");
+ 		return -EIO;
+ 	}
+@@ -4012,7 +4019,7 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe,
+ 		ret = ivb_pipe_crc_ctl_reg(dev, pipe, &source, &val);
+ 
+ 	if (ret != 0)
+-		return ret;
++		goto out;
+ 
+ 	/* none -> real source transition */
+ 	if (source) {
+@@ -4024,8 +4031,10 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe,
+ 		entries = kcalloc(INTEL_PIPE_CRC_ENTRIES_NR,
+ 				  sizeof(pipe_crc->entries[0]),
+ 				  GFP_KERNEL);
+-		if (!entries)
+-			return -ENOMEM;
++		if (!entries) {
++			ret = -ENOMEM;
++			goto out;
++		}
+ 
+ 		/*
+ 		 * When IPS gets enabled, the pipe CRC changes. Since IPS gets
+@@ -4081,7 +4090,12 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe,
+ 		hsw_enable_ips(crtc);
+ 	}
+ 
+-	return 0;
++	ret = 0;
++
++out:
++	intel_display_power_put(dev_priv, power_domain);
++
++	return ret;
+ }
+ 
+ /*
+diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
+index e7cd311..b0847b9 100644
+--- a/drivers/gpu/drm/i915/i915_drv.h
++++ b/drivers/gpu/drm/i915/i915_drv.h
+@@ -751,6 +751,7 @@ struct intel_csr {
+ 	uint32_t mmio_count;
+ 	i915_reg_t mmioaddr[8];
+ 	uint32_t mmiodata[8];
++	uint32_t dc_state;
+ };
+ 
+ #define DEV_INFO_FOR_EACH_FLAG(func, sep) \
+diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
+index 9c89df1..a7b4a524 100644
+--- a/drivers/gpu/drm/i915/intel_crt.c
++++ b/drivers/gpu/drm/i915/intel_crt.c
+@@ -71,22 +71,29 @@ static bool intel_crt_get_hw_state(struct intel_encoder *encoder,
+ 	struct intel_crt *crt = intel_encoder_to_crt(encoder);
+ 	enum intel_display_power_domain power_domain;
+ 	u32 tmp;
++	bool ret;
+ 
+ 	power_domain = intel_display_port_power_domain(encoder);
+-	if (!intel_display_power_is_enabled(dev_priv, power_domain))
++	if (!intel_display_power_get_if_enabled(dev_priv, power_domain))
+ 		return false;
+ 
++	ret = false;
++
+ 	tmp = I915_READ(crt->adpa_reg);
+ 
+ 	if (!(tmp & ADPA_DAC_ENABLE))
+-		return false;
++		goto out;
+ 
+ 	if (HAS_PCH_CPT(dev))
+ 		*pipe = PORT_TO_PIPE_CPT(tmp);
+ 	else
+ 		*pipe = PORT_TO_PIPE(tmp);
+ 
+-	return true;
++	ret = true;
++out:
++	intel_display_power_put(dev_priv, power_domain);
++
++	return ret;
+ }
+ 
+ static unsigned int intel_crt_get_flags(struct intel_encoder *encoder)
+diff --git a/drivers/gpu/drm/i915/intel_csr.c b/drivers/gpu/drm/i915/intel_csr.c
+index 9bb63a8..647d85e 100644
+--- a/drivers/gpu/drm/i915/intel_csr.c
++++ b/drivers/gpu/drm/i915/intel_csr.c
+@@ -240,6 +240,8 @@ void intel_csr_load_program(struct drm_i915_private *dev_priv)
+ 		I915_WRITE(dev_priv->csr.mmioaddr[i],
+ 			   dev_priv->csr.mmiodata[i]);
+ 	}
++
++	dev_priv->csr.dc_state = 0;
+ }
+ 
+ static uint32_t *parse_csr_fw(struct drm_i915_private *dev_priv,
+diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
+index 54a165b..0f3df2c 100644
+--- a/drivers/gpu/drm/i915/intel_ddi.c
++++ b/drivers/gpu/drm/i915/intel_ddi.c
+@@ -1969,13 +1969,16 @@ bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector)
+ 	enum transcoder cpu_transcoder;
+ 	enum intel_display_power_domain power_domain;
+ 	uint32_t tmp;
++	bool ret;
+ 
+ 	power_domain = intel_display_port_power_domain(intel_encoder);
+-	if (!intel_display_power_is_enabled(dev_priv, power_domain))
++	if (!intel_display_power_get_if_enabled(dev_priv, power_domain))
+ 		return false;
+ 
+-	if (!intel_encoder->get_hw_state(intel_encoder, &pipe))
+-		return false;
++	if (!intel_encoder->get_hw_state(intel_encoder, &pipe)) {
++		ret = false;
++		goto out;
++	}
+ 
+ 	if (port == PORT_A)
+ 		cpu_transcoder = TRANSCODER_EDP;
+@@ -1987,23 +1990,33 @@ bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector)
+ 	switch (tmp & TRANS_DDI_MODE_SELECT_MASK) {
+ 	case TRANS_DDI_MODE_SELECT_HDMI:
+ 	case TRANS_DDI_MODE_SELECT_DVI:
+-		return (type == DRM_MODE_CONNECTOR_HDMIA);
++		ret = type == DRM_MODE_CONNECTOR_HDMIA;
++		break;
+ 
+ 	case TRANS_DDI_MODE_SELECT_DP_SST:
+-		if (type == DRM_MODE_CONNECTOR_eDP)
+-			return true;
+-		return (type == DRM_MODE_CONNECTOR_DisplayPort);
++		ret = type == DRM_MODE_CONNECTOR_eDP ||
++		      type == DRM_MODE_CONNECTOR_DisplayPort;
++		break;
++
+ 	case TRANS_DDI_MODE_SELECT_DP_MST:
+ 		/* if the transcoder is in MST state then
+ 		 * connector isn't connected */
+-		return false;
++		ret = false;
++		break;
+ 
+ 	case TRANS_DDI_MODE_SELECT_FDI:
+-		return (type == DRM_MODE_CONNECTOR_VGA);
++		ret = type == DRM_MODE_CONNECTOR_VGA;
++		break;
+ 
+ 	default:
+-		return false;
++		ret = false;
++		break;
+ 	}
++
++out:
++	intel_display_power_put(dev_priv, power_domain);
++
++	return ret;
+ }
+ 
+ bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
+@@ -2015,15 +2028,18 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
+ 	enum intel_display_power_domain power_domain;
+ 	u32 tmp;
+ 	int i;
++	bool ret;
+ 
+ 	power_domain = intel_display_port_power_domain(encoder);
+-	if (!intel_display_power_is_enabled(dev_priv, power_domain))
++	if (!intel_display_power_get_if_enabled(dev_priv, power_domain))
+ 		return false;
+ 
++	ret = false;
++
+ 	tmp = I915_READ(DDI_BUF_CTL(port));
+ 
+ 	if (!(tmp & DDI_BUF_CTL_ENABLE))
+-		return false;
++		goto out;
+ 
+ 	if (port == PORT_A) {
+ 		tmp = I915_READ(TRANS_DDI_FUNC_CTL(TRANSCODER_EDP));
+@@ -2041,25 +2057,32 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
+ 			break;
+ 		}
+ 
+-		return true;
+-	} else {
+-		for (i = TRANSCODER_A; i <= TRANSCODER_C; i++) {
+-			tmp = I915_READ(TRANS_DDI_FUNC_CTL(i));
++		ret = true;
+ 
+-			if ((tmp & TRANS_DDI_PORT_MASK)
+-			    == TRANS_DDI_SELECT_PORT(port)) {
+-				if ((tmp & TRANS_DDI_MODE_SELECT_MASK) == TRANS_DDI_MODE_SELECT_DP_MST)
+-					return false;
++		goto out;
++	}
+ 
+-				*pipe = i;
+-				return true;
+-			}
++	for (i = TRANSCODER_A; i <= TRANSCODER_C; i++) {
++		tmp = I915_READ(TRANS_DDI_FUNC_CTL(i));
++
++		if ((tmp & TRANS_DDI_PORT_MASK) == TRANS_DDI_SELECT_PORT(port)) {
++			if ((tmp & TRANS_DDI_MODE_SELECT_MASK) ==
++			    TRANS_DDI_MODE_SELECT_DP_MST)
++				goto out;
++
++			*pipe = i;
++			ret = true;
++
++			goto out;
+ 		}
+ 	}
+ 
+ 	DRM_DEBUG_KMS("No pipe for ddi port %c found\n", port_name(port));
+ 
+-	return false;
++out:
++	intel_display_power_put(dev_priv, power_domain);
++
++	return ret;
+ }
+ 
+ void intel_ddi_enable_pipe_clock(struct intel_crtc *intel_crtc)
+@@ -2508,12 +2531,14 @@ static bool hsw_ddi_wrpll_get_hw_state(struct drm_i915_private *dev_priv,
+ {
+ 	uint32_t val;
+ 
+-	if (!intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_PLLS))
++	if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS))
+ 		return false;
+ 
+ 	val = I915_READ(WRPLL_CTL(pll->id));
+ 	hw_state->wrpll = val;
+ 
++	intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS);
++
+ 	return val & WRPLL_PLL_ENABLE;
+ }
+ 
+@@ -2523,12 +2548,14 @@ static bool hsw_ddi_spll_get_hw_state(struct drm_i915_private *dev_priv,
+ {
+ 	uint32_t val;
+ 
+-	if (!intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_PLLS))
++	if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS))
+ 		return false;
+ 
+ 	val = I915_READ(SPLL_CTL);
+ 	hw_state->spll = val;
+ 
++	intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS);
++
+ 	return val & SPLL_PLL_ENABLE;
+ }
+ 
+@@ -2645,16 +2672,19 @@ static bool skl_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv,
+ 	uint32_t val;
+ 	unsigned int dpll;
+ 	const struct skl_dpll_regs *regs = skl_dpll_regs;
++	bool ret;
+ 
+-	if (!intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_PLLS))
++	if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS))
+ 		return false;
+ 
++	ret = false;
++
+ 	/* DPLL0 is not part of the shared DPLLs, so pll->id is 0 for DPLL1 */
+ 	dpll = pll->id + 1;
+ 
+ 	val = I915_READ(regs[pll->id].ctl);
+ 	if (!(val & LCPLL_PLL_ENABLE))
+-		return false;
++		goto out;
+ 
+ 	val = I915_READ(DPLL_CTRL1);
+ 	hw_state->ctrl1 = (val >> (dpll * 6)) & 0x3f;
+@@ -2664,8 +2694,12 @@ static bool skl_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv,
+ 		hw_state->cfgcr1 = I915_READ(regs[pll->id].cfgcr1);
+ 		hw_state->cfgcr2 = I915_READ(regs[pll->id].cfgcr2);
+ 	}
++	ret = true;
+ 
+-	return true;
++out:
++	intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS);
++
++	return ret;
+ }
+ 
+ static void skl_shared_dplls_init(struct drm_i915_private *dev_priv)
+@@ -2932,13 +2966,16 @@ static bool bxt_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv,
+ {
+ 	enum port port = (enum port)pll->id;	/* 1:1 port->PLL mapping */
+ 	uint32_t val;
++	bool ret;
+ 
+-	if (!intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_PLLS))
++	if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS))
+ 		return false;
+ 
++	ret = false;
++
+ 	val = I915_READ(BXT_PORT_PLL_ENABLE(port));
+ 	if (!(val & PORT_PLL_ENABLE))
+-		return false;
++		goto out;
+ 
+ 	hw_state->ebb0 = I915_READ(BXT_PORT_PLL_EBB_0(port));
+ 	hw_state->ebb0 &= PORT_PLL_P1_MASK | PORT_PLL_P2_MASK;
+@@ -2985,7 +3022,12 @@ static bool bxt_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv,
+ 				 I915_READ(BXT_PORT_PCS_DW12_LN23(port)));
+ 	hw_state->pcsdw12 &= LANE_STAGGER_MASK | LANESTAGGER_STRAP_OVRD;
+ 
+-	return true;
++	ret = true;
++
++out:
++	intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS);
++
++	return ret;
+ }
+ 
+ static void bxt_shared_dplls_init(struct drm_i915_private *dev_priv)
+@@ -3120,11 +3162,15 @@ bool intel_ddi_is_audio_enabled(struct drm_i915_private *dev_priv,
+ {
+ 	u32 temp;
+ 
+-	if (intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_AUDIO)) {
++	if (intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_AUDIO)) {
+ 		temp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD);
++
++		intel_display_power_put(dev_priv, POWER_DOMAIN_AUDIO);
++
+ 		if (temp & AUDIO_OUTPUT_ENABLE(intel_crtc->pipe))
+ 			return true;
+ 	}
++
+ 	return false;
+ }
+ 
+diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
+index 5feb657..46947ff 100644
+--- a/drivers/gpu/drm/i915/intel_display.c
++++ b/drivers/gpu/drm/i915/intel_display.c
+@@ -1351,18 +1351,21 @@ void assert_pipe(struct drm_i915_private *dev_priv,
+ 	bool cur_state;
+ 	enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
+ 								      pipe);
++	enum intel_display_power_domain power_domain;
+ 
+ 	/* if we need the pipe quirk it must be always on */
+ 	if ((pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) ||
+ 	    (pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE))
+ 		state = true;
+ 
+-	if (!intel_display_power_is_enabled(dev_priv,
+-				POWER_DOMAIN_TRANSCODER(cpu_transcoder))) {
+-		cur_state = false;
+-	} else {
++	power_domain = POWER_DOMAIN_TRANSCODER(cpu_transcoder);
++	if (intel_display_power_get_if_enabled(dev_priv, power_domain)) {
+ 		u32 val = I915_READ(PIPECONF(cpu_transcoder));
+ 		cur_state = !!(val & PIPECONF_ENABLE);
++
++		intel_display_power_put(dev_priv, power_domain);
++	} else {
++		cur_state = false;
+ 	}
+ 
+ 	I915_STATE_WARN(cur_state != state,
+@@ -8171,18 +8174,22 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
+ {
+ 	struct drm_device *dev = crtc->base.dev;
+ 	struct drm_i915_private *dev_priv = dev->dev_private;
++	enum intel_display_power_domain power_domain;
+ 	uint32_t tmp;
++	bool ret;
+ 
+-	if (!intel_display_power_is_enabled(dev_priv,
+-					    POWER_DOMAIN_PIPE(crtc->pipe)))
++	power_domain = POWER_DOMAIN_PIPE(crtc->pipe);
++	if (!intel_display_power_get_if_enabled(dev_priv, power_domain))
+ 		return false;
+ 
+ 	pipe_config->cpu_transcoder = (enum transcoder) crtc->pipe;
+ 	pipe_config->shared_dpll = DPLL_ID_PRIVATE;
+ 
++	ret = false;
++
+ 	tmp = I915_READ(PIPECONF(crtc->pipe));
+ 	if (!(tmp & PIPECONF_ENABLE))
+-		return false;
++		goto out;
+ 
+ 	if (IS_G4X(dev) || IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) {
+ 		switch (tmp & PIPECONF_BPC_MASK) {
+@@ -8262,7 +8269,12 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
+ 	pipe_config->base.adjusted_mode.crtc_clock =
+ 		pipe_config->port_clock / pipe_config->pixel_multiplier;
+ 
+-	return true;
++	ret = true;
++
++out:
++	intel_display_power_put(dev_priv, power_domain);
++
++	return ret;
+ }
+ 
+ static void ironlake_init_pch_refclk(struct drm_device *dev)
+@@ -9366,18 +9378,21 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc,
+ {
+ 	struct drm_device *dev = crtc->base.dev;
+ 	struct drm_i915_private *dev_priv = dev->dev_private;
++	enum intel_display_power_domain power_domain;
+ 	uint32_t tmp;
++	bool ret;
+ 
+-	if (!intel_display_power_is_enabled(dev_priv,
+-					    POWER_DOMAIN_PIPE(crtc->pipe)))
++	power_domain = POWER_DOMAIN_PIPE(crtc->pipe);
++	if (!intel_display_power_get_if_enabled(dev_priv, power_domain))
+ 		return false;
+ 
+ 	pipe_config->cpu_transcoder = (enum transcoder) crtc->pipe;
+ 	pipe_config->shared_dpll = DPLL_ID_PRIVATE;
+ 
++	ret = false;
+ 	tmp = I915_READ(PIPECONF(crtc->pipe));
+ 	if (!(tmp & PIPECONF_ENABLE))
+-		return false;
++		goto out;
+ 
+ 	switch (tmp & PIPECONF_BPC_MASK) {
+ 	case PIPECONF_6BPC:
+@@ -9440,7 +9455,12 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc,
+ 
+ 	ironlake_get_pfit_config(crtc, pipe_config);
+ 
+-	return true;
++	ret = true;
++
++out:
++	intel_display_power_put(dev_priv, power_domain);
++
++	return ret;
+ }
+ 
+ static void assert_can_disable_lcpll(struct drm_i915_private *dev_priv)
+@@ -9950,12 +9970,17 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc,
+ {
+ 	struct drm_device *dev = crtc->base.dev;
+ 	struct drm_i915_private *dev_priv = dev->dev_private;
+-	enum intel_display_power_domain pfit_domain;
++	enum intel_display_power_domain power_domain;
++	unsigned long power_domain_mask;
+ 	uint32_t tmp;
++	bool ret;
+ 
+-	if (!intel_display_power_is_enabled(dev_priv,
+-					 POWER_DOMAIN_PIPE(crtc->pipe)))
++	power_domain = POWER_DOMAIN_PIPE(crtc->pipe);
++	if (!intel_display_power_get_if_enabled(dev_priv, power_domain))
+ 		return false;
++	power_domain_mask = BIT(power_domain);
++
++	ret = false;
+ 
+ 	pipe_config->cpu_transcoder = (enum transcoder) crtc->pipe;
+ 	pipe_config->shared_dpll = DPLL_ID_PRIVATE;
+@@ -9982,13 +10007,14 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc,
+ 			pipe_config->cpu_transcoder = TRANSCODER_EDP;
+ 	}
+ 
+-	if (!intel_display_power_is_enabled(dev_priv,
+-			POWER_DOMAIN_TRANSCODER(pipe_config->cpu_transcoder)))
+-		return false;
++	power_domain = POWER_DOMAIN_TRANSCODER(pipe_config->cpu_transcoder);
++	if (!intel_display_power_get_if_enabled(dev_priv, power_domain))
++		goto out;
++	power_domain_mask |= BIT(power_domain);
+ 
+ 	tmp = I915_READ(PIPECONF(pipe_config->cpu_transcoder));
+ 	if (!(tmp & PIPECONF_ENABLE))
+-		return false;
++		goto out;
+ 
+ 	haswell_get_ddi_port_state(crtc, pipe_config);
+ 
+@@ -9998,14 +10024,14 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc,
+ 		skl_init_scalers(dev, crtc, pipe_config);
+ 	}
+ 
+-	pfit_domain = POWER_DOMAIN_PIPE_PANEL_FITTER(crtc->pipe);
+-
+ 	if (INTEL_INFO(dev)->gen >= 9) {
+ 		pipe_config->scaler_state.scaler_id = -1;
+ 		pipe_config->scaler_state.scaler_users &= ~(1 << SKL_CRTC_INDEX);
+ 	}
+ 
+-	if (intel_display_power_is_enabled(dev_priv, pfit_domain)) {
++	power_domain = POWER_DOMAIN_PIPE_PANEL_FITTER(crtc->pipe);
++	if (intel_display_power_get_if_enabled(dev_priv, power_domain)) {
++		power_domain_mask |= BIT(power_domain);
+ 		if (INTEL_INFO(dev)->gen >= 9)
+ 			skylake_get_pfit_config(crtc, pipe_config);
+ 		else
+@@ -10023,7 +10049,13 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc,
+ 		pipe_config->pixel_multiplier = 1;
+ 	}
+ 
+-	return true;
++	ret = true;
++
++out:
++	for_each_power_domain(power_domain, power_domain_mask)
++		intel_display_power_put(dev_priv, power_domain);
++
++	return ret;
+ }
+ 
+ static void i845_update_cursor(struct drm_crtc *crtc, u32 base, bool on)
+@@ -13630,7 +13662,7 @@ static bool ibx_pch_dpll_get_hw_state(struct drm_i915_private *dev_priv,
+ {
+ 	uint32_t val;
+ 
+-	if (!intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_PLLS))
++	if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS))
+ 		return false;
+ 
+ 	val = I915_READ(PCH_DPLL(pll->id));
+@@ -13638,6 +13670,8 @@ static bool ibx_pch_dpll_get_hw_state(struct drm_i915_private *dev_priv,
+ 	hw_state->fp0 = I915_READ(PCH_FP0(pll->id));
+ 	hw_state->fp1 = I915_READ(PCH_FP1(pll->id));
+ 
++	intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS);
++
+ 	return val & DPLL_VCO_ENABLE;
+ }
+ 
+@@ -15568,10 +15602,12 @@ void i915_redisable_vga(struct drm_device *dev)
+ 	 * level, just check if the power well is enabled instead of trying to
+ 	 * follow the "don't touch the power well if we don't need it" policy
+ 	 * the rest of the driver uses. */
+-	if (!intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_VGA))
++	if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_VGA))
+ 		return;
+ 
+ 	i915_redisable_vga_power_on(dev);
++
++	intel_display_power_put(dev_priv, POWER_DOMAIN_VGA);
+ }
+ 
+ static bool primary_get_hw_state(struct intel_plane *plane)
+diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
+index 1bbd67b..1d8de43 100644
+--- a/drivers/gpu/drm/i915/intel_dp.c
++++ b/drivers/gpu/drm/i915/intel_dp.c
+@@ -2362,15 +2362,18 @@ static bool intel_dp_get_hw_state(struct intel_encoder *encoder,
+ 	struct drm_i915_private *dev_priv = dev->dev_private;
+ 	enum intel_display_power_domain power_domain;
+ 	u32 tmp;
++	bool ret;
+ 
+ 	power_domain = intel_display_port_power_domain(encoder);
+-	if (!intel_display_power_is_enabled(dev_priv, power_domain))
++	if (!intel_display_power_get_if_enabled(dev_priv, power_domain))
+ 		return false;
+ 
++	ret = false;
++
+ 	tmp = I915_READ(intel_dp->output_reg);
+ 
+ 	if (!(tmp & DP_PORT_EN))
+-		return false;
++		goto out;
+ 
+ 	if (IS_GEN7(dev) && port == PORT_A) {
+ 		*pipe = PORT_TO_PIPE_CPT(tmp);
+@@ -2381,7 +2384,9 @@ static bool intel_dp_get_hw_state(struct intel_encoder *encoder,
+ 			u32 trans_dp = I915_READ(TRANS_DP_CTL(p));
+ 			if (TRANS_DP_PIPE_TO_PORT(trans_dp) == port) {
+ 				*pipe = p;
+-				return true;
++				ret = true;
++
++				goto out;
+ 			}
+ 		}
+ 
+@@ -2393,7 +2398,12 @@ static bool intel_dp_get_hw_state(struct intel_encoder *encoder,
+ 		*pipe = PORT_TO_PIPE(tmp);
+ 	}
+ 
+-	return true;
++	ret = true;
++
++out:
++	intel_display_power_put(dev_priv, power_domain);
++
++	return ret;
+ }
+ 
+ static void intel_dp_get_config(struct intel_encoder *encoder,
+diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
+index ea54158..df7f3cb 100644
+--- a/drivers/gpu/drm/i915/intel_drv.h
++++ b/drivers/gpu/drm/i915/intel_drv.h
+@@ -1428,6 +1428,8 @@ bool __intel_display_power_is_enabled(struct drm_i915_private *dev_priv,
+ 				      enum intel_display_power_domain domain);
+ void intel_display_power_get(struct drm_i915_private *dev_priv,
+ 			     enum intel_display_power_domain domain);
++bool intel_display_power_get_if_enabled(struct drm_i915_private *dev_priv,
++					enum intel_display_power_domain domain);
+ void intel_display_power_put(struct drm_i915_private *dev_priv,
+ 			     enum intel_display_power_domain domain);
+ 
+@@ -1514,6 +1516,7 @@ enable_rpm_wakeref_asserts(struct drm_i915_private *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);
+ void intel_runtime_pm_put(struct drm_i915_private *dev_priv);
+ 
+diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c
+index 44742fa..0193c62a 100644
+--- a/drivers/gpu/drm/i915/intel_dsi.c
++++ b/drivers/gpu/drm/i915/intel_dsi.c
+@@ -664,13 +664,16 @@ static bool intel_dsi_get_hw_state(struct intel_encoder *encoder,
+ 	struct drm_device *dev = encoder->base.dev;
+ 	enum intel_display_power_domain power_domain;
+ 	enum port port;
++	bool ret;
+ 
+ 	DRM_DEBUG_KMS("\n");
+ 
+ 	power_domain = intel_display_port_power_domain(encoder);
+-	if (!intel_display_power_is_enabled(dev_priv, power_domain))
++	if (!intel_display_power_get_if_enabled(dev_priv, power_domain))
+ 		return false;
+ 
++	ret = false;
++
+ 	/* XXX: this only works for one DSI output */
+ 	for_each_dsi_port(port, intel_dsi->ports) {
+ 		i915_reg_t ctrl_reg = IS_BROXTON(dev) ?
+@@ -691,12 +694,16 @@ static bool intel_dsi_get_hw_state(struct intel_encoder *encoder,
+ 		if (dpi_enabled || (func & CMD_MODE_DATA_WIDTH_MASK)) {
+ 			if (I915_READ(MIPI_DEVICE_READY(port)) & DEVICE_READY) {
+ 				*pipe = port == PORT_A ? PIPE_A : PIPE_B;
+-				return true;
++				ret = true;
++
++				goto out;
+ 			}
+ 		}
+ 	}
++out:
++	intel_display_power_put(dev_priv, power_domain);
+ 
+-	return false;
++	return ret;
+ }
+ 
+ static void intel_dsi_get_config(struct intel_encoder *encoder,
+diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
+index 4a77639..cb5d1b1 100644
+--- a/drivers/gpu/drm/i915/intel_hdmi.c
++++ b/drivers/gpu/drm/i915/intel_hdmi.c
+@@ -880,15 +880,18 @@ static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder,
+ 	struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
+ 	enum intel_display_power_domain power_domain;
+ 	u32 tmp;
++	bool ret;
+ 
+ 	power_domain = intel_display_port_power_domain(encoder);
+-	if (!intel_display_power_is_enabled(dev_priv, power_domain))
++	if (!intel_display_power_get_if_enabled(dev_priv, power_domain))
+ 		return false;
+ 
++	ret = false;
++
+ 	tmp = I915_READ(intel_hdmi->hdmi_reg);
+ 
+ 	if (!(tmp & SDVO_ENABLE))
+-		return false;
++		goto out;
+ 
+ 	if (HAS_PCH_CPT(dev))
+ 		*pipe = PORT_TO_PIPE_CPT(tmp);
+@@ -897,7 +900,12 @@ static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder,
+ 	else
+ 		*pipe = PORT_TO_PIPE(tmp);
+ 
+-	return true;
++	ret = true;
++
++out:
++	intel_display_power_put(dev_priv, power_domain);
++
++	return ret;
+ }
+ 
+ static void intel_hdmi_get_config(struct intel_encoder *encoder,
+diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
+index 0da0240..bc04d8d 100644
+--- a/drivers/gpu/drm/i915/intel_lvds.c
++++ b/drivers/gpu/drm/i915/intel_lvds.c
+@@ -75,22 +75,30 @@ static bool intel_lvds_get_hw_state(struct intel_encoder *encoder,
+ 	struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
+ 	enum intel_display_power_domain power_domain;
+ 	u32 tmp;
++	bool ret;
+ 
+ 	power_domain = intel_display_port_power_domain(encoder);
+-	if (!intel_display_power_is_enabled(dev_priv, power_domain))
++	if (!intel_display_power_get_if_enabled(dev_priv, power_domain))
+ 		return false;
+ 
++	ret = false;
++
+ 	tmp = I915_READ(lvds_encoder->reg);
+ 
+ 	if (!(tmp & LVDS_PORT_EN))
+-		return false;
++		goto out;
+ 
+ 	if (HAS_PCH_CPT(dev))
+ 		*pipe = PORT_TO_PIPE_CPT(tmp);
+ 	else
+ 		*pipe = PORT_TO_PIPE(tmp);
+ 
+-	return true;
++	ret = true;
++
++out:
++	intel_display_power_put(dev_priv, power_domain);
++
++	return ret;
+ }
+ 
+ static void intel_lvds_get_config(struct intel_encoder *encoder,
+diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
+index a234687..b28c29f 100644
+--- a/drivers/gpu/drm/i915/intel_pm.c
++++ b/drivers/gpu/drm/i915/intel_pm.c
+@@ -2829,7 +2829,10 @@ void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv,
+ 	memset(ddb, 0, sizeof(*ddb));
+ 
+ 	for_each_pipe(dev_priv, pipe) {
+-		if (!intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_PIPE(pipe)))
++		enum intel_display_power_domain power_domain;
++
++		power_domain = POWER_DOMAIN_PIPE(pipe);
++		if (!intel_display_power_get_if_enabled(dev_priv, power_domain))
+ 			continue;
+ 
+ 		for_each_plane(dev_priv, pipe, plane) {
+@@ -2841,6 +2844,8 @@ void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv,
+ 		val = I915_READ(CUR_BUF_CFG(pipe));
+ 		skl_ddb_entry_init_from_hw(&ddb->plane[pipe][PLANE_CURSOR],
+ 					   val);
++
++		intel_display_power_put(dev_priv, power_domain);
+ 	}
+ }
+ 
+diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c
+index ddbdbff..678ed34 100644
+--- a/drivers/gpu/drm/i915/intel_runtime_pm.c
++++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
+@@ -470,6 +470,43 @@ static void gen9_set_dc_state_debugmask_memory_up(
+ 	}
+ }
+ 
++static void gen9_write_dc_state(struct drm_i915_private *dev_priv,
++				u32 state)
++{
++	int rewrites = 0;
++	int rereads = 0;
++	u32 v;
++
++	I915_WRITE(DC_STATE_EN, state);
++
++	/* It has been observed that disabling the dc6 state sometimes
++	 * doesn't stick and dmc keeps returning old value. Make sure
++	 * the write really sticks enough times and also force rewrite until
++	 * we are confident that state is exactly what we want.
++	 */
++	do  {
++		v = I915_READ(DC_STATE_EN);
++
++		if (v != state) {
++			I915_WRITE(DC_STATE_EN, state);
++			rewrites++;
++			rereads = 0;
++		} else if (rereads++ > 5) {
++			break;
++		}
++
++	} while (rewrites < 100);
++
++	if (v != state)
++		DRM_ERROR("Writing dc state to 0x%x failed, now 0x%x\n",
++			  state, v);
++
++	/* Most of the times we need one retry, avoid spam */
++	if (rewrites > 1)
++		DRM_DEBUG_KMS("Rewrote dc state to 0x%x %d times\n",
++			      state, rewrites);
++}
++
+ static void gen9_set_dc_state(struct drm_i915_private *dev_priv, uint32_t state)
+ {
+ 	uint32_t val;
+@@ -494,10 +531,18 @@ static void gen9_set_dc_state(struct drm_i915_private *dev_priv, uint32_t state)
+ 	val = I915_READ(DC_STATE_EN);
+ 	DRM_DEBUG_KMS("Setting DC state from %02x to %02x\n",
+ 		      val & mask, state);
++
++	/* Check if DMC is ignoring our DC state requests */
++	if ((val & mask) != dev_priv->csr.dc_state)
++		DRM_ERROR("DC state mismatch (0x%x -> 0x%x)\n",
++			  dev_priv->csr.dc_state, val & mask);
++
+ 	val &= ~mask;
+ 	val |= state;
+-	I915_WRITE(DC_STATE_EN, val);
+-	POSTING_READ(DC_STATE_EN);
++
++	gen9_write_dc_state(dev_priv, val);
++
++	dev_priv->csr.dc_state = val & mask;
+ }
+ 
+ void bxt_enable_dc9(struct drm_i915_private *dev_priv)
+@@ -1442,6 +1487,22 @@ static void chv_pipe_power_well_disable(struct drm_i915_private *dev_priv,
+ 	chv_set_pipe_power_well(dev_priv, power_well, false);
+ }
+ 
++static void
++__intel_display_power_get_domain(struct drm_i915_private *dev_priv,
++				 enum intel_display_power_domain domain)
++{
++	struct i915_power_domains *power_domains = &dev_priv->power_domains;
++	struct i915_power_well *power_well;
++	int i;
++
++	for_each_power_well(i, power_well, BIT(domain), power_domains) {
++		if (!power_well->count++)
++			intel_power_well_enable(dev_priv, power_well);
++	}
++
++	power_domains->domain_use_count[domain]++;
++}
++
+ /**
+  * intel_display_power_get - grab a power domain reference
+  * @dev_priv: i915 device instance
+@@ -1457,24 +1518,53 @@ static void chv_pipe_power_well_disable(struct drm_i915_private *dev_priv,
+ void intel_display_power_get(struct drm_i915_private *dev_priv,
+ 			     enum intel_display_power_domain domain)
+ {
+-	struct i915_power_domains *power_domains;
+-	struct i915_power_well *power_well;
+-	int i;
++	struct i915_power_domains *power_domains = &dev_priv->power_domains;
+ 
+ 	intel_runtime_pm_get(dev_priv);
+ 
+-	power_domains = &dev_priv->power_domains;
++	mutex_lock(&power_domains->lock);
++
++	__intel_display_power_get_domain(dev_priv, domain);
++
++	mutex_unlock(&power_domains->lock);
++}
++
++/**
++ * intel_display_power_get_if_enabled - grab a reference for an enabled display power domain
++ * @dev_priv: i915 device instance
++ * @domain: power domain to reference
++ *
++ * This function grabs a power domain reference for @domain and ensures that the
++ * power domain and all its parents are powered up. Therefore users should only
++ * grab a reference to the innermost power domain they need.
++ *
++ * Any power domain reference obtained by this function must have a symmetric
++ * call to intel_display_power_put() to release the reference again.
++ */
++bool intel_display_power_get_if_enabled(struct drm_i915_private *dev_priv,
++					enum intel_display_power_domain domain)
++{
++	struct i915_power_domains *power_domains = &dev_priv->power_domains;
++	bool is_enabled;
++
++	if (!intel_runtime_pm_get_if_in_use(dev_priv))
++		return false;
+ 
+ 	mutex_lock(&power_domains->lock);
+ 
+-	for_each_power_well(i, power_well, BIT(domain), power_domains) {
+-		if (!power_well->count++)
+-			intel_power_well_enable(dev_priv, power_well);
++	if (__intel_display_power_is_enabled(dev_priv, domain)) {
++		__intel_display_power_get_domain(dev_priv, domain);
++		is_enabled = true;
++	} else {
++		is_enabled = false;
+ 	}
+ 
+-	power_domains->domain_use_count[domain]++;
+-
+ 	mutex_unlock(&power_domains->lock);
++
++	if (!is_enabled)
++		intel_runtime_pm_put(dev_priv);
++
++	return is_enabled;
+ }
+ 
+ /**
+@@ -2246,6 +2336,43 @@ void intel_runtime_pm_get(struct drm_i915_private *dev_priv)
+ }
+ 
+ /**
++ * intel_runtime_pm_get_if_in_use - grab a runtime pm reference if device in use
++ * @dev_priv: i915 device instance
++ *
++ * This function grabs a device-level runtime pm reference if the device is
++ * already in use and ensures that it is powered up.
++ *
++ * Any runtime pm reference obtained by this function must have a symmetric
++ * call to intel_runtime_pm_put() to release the reference again.
++ */
++bool intel_runtime_pm_get_if_in_use(struct drm_i915_private *dev_priv)
++{
++	struct drm_device *dev = dev_priv->dev;
++	struct device *device = &dev->pdev->dev;
++	int ret;
++
++	if (!IS_ENABLED(CONFIG_PM))
++		return true;
++
++	ret = pm_runtime_get_if_in_use(device);
++
++	/*
++	 * In cases runtime PM is disabled by the RPM core and we get an
++	 * -EINVAL return value we are not supposed to call this function,
++	 * since the power state is undefined. This applies atm to the
++	 * late/early system suspend/resume handlers.
++	 */
++	WARN_ON_ONCE(ret < 0);
++	if (ret <= 0)
++		return false;
++
++	atomic_inc(&dev_priv->pm.wakeref_count);
++	assert_rpm_wakelock_held(dev_priv);
++
++	return true;
++}
++
++/**
+  * intel_runtime_pm_get_noresume - grab a runtime pm reference
+  * @dev_priv: i915 device instance
+  *