[PATCH 1/5] drm/i915/vlv: power well support for VLV/BYT

Jesse Barnes
2013-10-14T23:14:34Z

Had to conditionalize some HSW bits and add virtual functions for
get/set on the power wells.

Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
---
drivers/gpu/drm/i915/i915_drv.h | 10 +-
drivers/gpu/drm/i915/intel_drv.h | 2 +
drivers/gpu/drm/i915/intel_pm.c | 128 +++-------------------
drivers/gpu/drm/i915/intel_uncore.c | 211 ++++++++++++++++++++++++++++++++++++
4 files changed, 238 insertions(+), 113 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index ca74ef2..4e97840 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -401,6 +401,14 @@ struct drm_i915_display_funcs {
struct intel_uncore_funcs {
void (*force_wake_get)(struct drm_i915_private *dev_priv);
void (*force_wake_put)(struct drm_i915_private *dev_priv);
+ void (*set_display_power)(struct drm_i915_private *dev_priv,
+ bool enable);
+ bool (*display_power_enabled)(struct drm_i915_private *dev_priv,
+ enum intel_display_power_domain domain);
+ void (*display_power_get)(struct drm_i915_private *dev_priv,
+ enum intel_display_power_domain domain);
+ void (*display_power_put)(struct drm_i915_private *dev_priv,
+ enum intel_display_power_domain domain);
};

struct intel_uncore {
@@ -1702,7 +1710,7 @@ struct drm_i915_file_private {
#define HAS_IPS(dev) (IS_ULT(dev))

#define HAS_DDI(dev) (INTEL_INFO(dev)->has_ddi)
-#define HAS_POWER_WELL(dev) (IS_HASWELL(dev))
+#define HAS_POWER_WELL(dev) (IS_HASWELL(dev) || IS_VALLEYVIEW(dev))
#define HAS_FPGA_DBG_UNCLAIMED(dev) (INTEL_INFO(dev)->has_fpga_dbg)
#define HAS_PSR(dev) (IS_HASWELL(dev))

diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 476d98b..9317383 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -798,6 +798,8 @@ void intel_display_power_get(struct drm_device *dev,
enum intel_display_power_domain domain);
void intel_display_power_put(struct drm_device *dev,
enum intel_display_power_domain domain);
+void __intel_power_well_get(struct i915_power_well *power_well);
+void __intel_power_well_put(struct i915_power_well *power_well);
void intel_init_power_well(struct drm_device *dev);
void intel_set_power_well(struct drm_device *dev, bool enable);
void intel_enable_gt_powersave(struct drm_device *dev);
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 0b4de57..d1abc42 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -5367,146 +5367,48 @@ bool intel_display_power_enabled(struct drm_device *dev,
if (!HAS_POWER_WELL(dev))
return true;

- switch (domain) {
- case POWER_DOMAIN_PIPE_A:
- case POWER_DOMAIN_TRANSCODER_EDP:
- return true;
- case POWER_DOMAIN_VGA:
- case POWER_DOMAIN_PIPE_B:
- case POWER_DOMAIN_PIPE_C:
- case POWER_DOMAIN_PIPE_A_PANEL_FITTER:
- case POWER_DOMAIN_PIPE_B_PANEL_FITTER:
- case POWER_DOMAIN_PIPE_C_PANEL_FITTER:
- case POWER_DOMAIN_TRANSCODER_A:
- case POWER_DOMAIN_TRANSCODER_B:
- case POWER_DOMAIN_TRANSCODER_C:
- return I915_READ(HSW_PWR_WELL_DRIVER) ==
- (HSW_PWR_WELL_ENABLE_REQUEST | HSW_PWR_WELL_STATE_ENABLED);
- default:
- BUG();
- }
+ return dev_priv->uncore.funcs.display_power_enabled(dev_priv, domain);
}

-static void __intel_set_power_well(struct drm_device *dev, bool enable)
+void __intel_power_well_get(struct i915_power_well *power_well)
{
+ struct drm_device *dev = power_well->device;
struct drm_i915_private *dev_priv = dev->dev_private;
- bool is_enabled, enable_requested;
- uint32_t tmp;

- tmp = I915_READ(HSW_PWR_WELL_DRIVER);
- is_enabled = tmp & HSW_PWR_WELL_STATE_ENABLED;
- enable_requested = tmp & HSW_PWR_WELL_ENABLE_REQUEST;
-
- if (enable) {
- if (!enable_requested)
- I915_WRITE(HSW_PWR_WELL_DRIVER,
- HSW_PWR_WELL_ENABLE_REQUEST);
-
- if (!is_enabled) {
- DRM_DEBUG_KMS("Enabling power well\n");
- if (wait_for((I915_READ(HSW_PWR_WELL_DRIVER) &
- HSW_PWR_WELL_STATE_ENABLED), 20))
- DRM_ERROR("Timeout enabling power well\n");
- }
- } else {
- if (enable_requested) {
- unsigned long irqflags;
- enum pipe p;
-
- I915_WRITE(HSW_PWR_WELL_DRIVER, 0);
- POSTING_READ(HSW_PWR_WELL_DRIVER);
- DRM_DEBUG_KMS("Requesting to disable the power well\n");
-
- /*
- * After this, the registers on the pipes that are part
- * of the power well will become zero, so we have to
- * adjust our counters according to that.
- *
- * FIXME: Should we do this in general in
- * drm_vblank_post_modeset?
- */
- spin_lock_irqsave(&dev->vbl_lock, irqflags);
- for_each_pipe(p)
- if (p != PIPE_A)
- dev->last_vblank[p] = 0;
- spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
- }
- }
-}
-
-static void __intel_power_well_get(struct i915_power_well *power_well)
-{
if (!power_well->count++)
- __intel_set_power_well(power_well->device, true);
+ dev_priv->uncore.funcs.set_display_power(dev_priv, true);
}

-static void __intel_power_well_put(struct i915_power_well *power_well)
+void __intel_power_well_put(struct i915_power_well *power_well)
{
+ struct drm_device *dev = power_well->device;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
WARN_ON(!power_well->count);
if (!--power_well->count)
- __intel_set_power_well(power_well->device, false);
+ dev_priv->uncore.funcs.set_display_power(dev_priv, false);
}

void intel_display_power_get(struct drm_device *dev,
enum intel_display_power_domain domain)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct i915_power_well *power_well = &dev_priv->power_well;

if (!HAS_POWER_WELL(dev))
return;

- switch (domain) {
- case POWER_DOMAIN_PIPE_A:
- case POWER_DOMAIN_TRANSCODER_EDP:
- return;
- case POWER_DOMAIN_VGA:
- case POWER_DOMAIN_PIPE_B:
- case POWER_DOMAIN_PIPE_C:
- case POWER_DOMAIN_PIPE_A_PANEL_FITTER:
- case POWER_DOMAIN_PIPE_B_PANEL_FITTER:
- case POWER_DOMAIN_PIPE_C_PANEL_FITTER:
- case POWER_DOMAIN_TRANSCODER_A:
- case POWER_DOMAIN_TRANSCODER_B:
- case POWER_DOMAIN_TRANSCODER_C:
- spin_lock_irq(&power_well->lock);
- __intel_power_well_get(power_well);
- spin_unlock_irq(&power_well->lock);
- return;
- default:
- BUG();
- }
+ dev_priv->uncore.funcs.display_power_get(dev_priv, domain);
}

void intel_display_power_put(struct drm_device *dev,
enum intel_display_power_domain domain)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- struct i915_power_well *power_well = &dev_priv->power_well;

if (!HAS_POWER_WELL(dev))
return;

- switch (domain) {
- case POWER_DOMAIN_PIPE_A:
- case POWER_DOMAIN_TRANSCODER_EDP:
- return;
- case POWER_DOMAIN_VGA:
- case POWER_DOMAIN_PIPE_B:
- case POWER_DOMAIN_PIPE_C:
- case POWER_DOMAIN_PIPE_A_PANEL_FITTER:
- case POWER_DOMAIN_PIPE_B_PANEL_FITTER:
- case POWER_DOMAIN_PIPE_C_PANEL_FITTER:
- case POWER_DOMAIN_TRANSCODER_A:
- case POWER_DOMAIN_TRANSCODER_B:
- case POWER_DOMAIN_TRANSCODER_C:
- spin_lock_irq(&power_well->lock);
- __intel_power_well_put(power_well);
- spin_unlock_irq(&power_well->lock);
- return;
- default:
- BUG();
- }
+ dev_priv->uncore.funcs.display_power_put(dev_priv, domain);
}

static struct i915_power_well *hsw_pwr;
@@ -5595,7 +5497,8 @@ static void intel_resume_power_well(struct drm_device *dev)
return;

spin_lock_irq(&power_well->lock);
- __intel_set_power_well(dev, power_well->count > 0);
+ dev_priv->uncore.funcs.set_display_power(dev_priv,
+ power_well->count > 0);
spin_unlock_irq(&power_well->lock);
}

@@ -5618,8 +5521,9 @@ void intel_init_power_well(struct drm_device *dev)

/* We're taking over the BIOS, so clear any requests made by it since
* the driver is in charge now. */
- if (I915_READ(HSW_PWR_WELL_BIOS) & HSW_PWR_WELL_ENABLE_REQUEST)
- I915_WRITE(HSW_PWR_WELL_BIOS, 0);
+ if (IS_HASWELL(dev))
+ if (I915_READ(HSW_PWR_WELL_BIOS) & HSW_PWR_WELL_ENABLE_REQUEST)
+ I915_WRITE(HSW_PWR_WELL_BIOS, 0);
}

/* Disables PC8 so we can use the GMBUS and DP AUX interrupts. */
diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
index 288a3a6..ef5d7fd 100644
--- a/drivers/gpu/drm/i915/intel_uncore.c
+++ b/drivers/gpu/drm/i915/intel_uncore.c
@@ -216,6 +216,209 @@ static void gen6_force_wake_work(struct work_struct *work)
spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
}

+static void hsw_display_power_get(struct drm_i915_private *dev_priv,
+ enum intel_display_power_domain domain)
+{
+ struct i915_power_well *power_well = &dev_priv->power_well;
+
+ switch (domain) {
+ case POWER_DOMAIN_PIPE_A:
+ case POWER_DOMAIN_TRANSCODER_EDP:
+ return;
+ case POWER_DOMAIN_VGA:
+ case POWER_DOMAIN_PIPE_B:
+ case POWER_DOMAIN_PIPE_C:
+ case POWER_DOMAIN_PIPE_A_PANEL_FITTER:
+ case POWER_DOMAIN_PIPE_B_PANEL_FITTER:
+ case POWER_DOMAIN_PIPE_C_PANEL_FITTER:
+ case POWER_DOMAIN_TRANSCODER_A:
+ case POWER_DOMAIN_TRANSCODER_B:
+ case POWER_DOMAIN_TRANSCODER_C:
+ spin_lock_irq(&power_well->lock);
+ __intel_power_well_get(power_well);
+ spin_unlock_irq(&power_well->lock);
+ return;
+ default:
+ BUG();
+ }
+}
+
+static void hsw_display_power_put(struct drm_i915_private *dev_priv,
+ enum intel_display_power_domain domain)
+{
+ struct i915_power_well *power_well = &dev_priv->power_well;
+ switch (domain) {
+ case POWER_DOMAIN_PIPE_A:
+ case POWER_DOMAIN_TRANSCODER_EDP:
+ return;
+ case POWER_DOMAIN_VGA:
+ case POWER_DOMAIN_PIPE_B:
+ case POWER_DOMAIN_PIPE_C:
+ case POWER_DOMAIN_PIPE_A_PANEL_FITTER:
+ case POWER_DOMAIN_PIPE_B_PANEL_FITTER:
+ case POWER_DOMAIN_PIPE_C_PANEL_FITTER:
+ case POWER_DOMAIN_TRANSCODER_A:
+ case POWER_DOMAIN_TRANSCODER_B:
+ case POWER_DOMAIN_TRANSCODER_C:
+ spin_lock_irq(&power_well->lock);
+ __intel_power_well_put(power_well);
+ spin_unlock_irq(&power_well->lock);
+ return;
+ default:
+ BUG();
+ }
+}
+
+static void vlv_display_power_get(struct drm_i915_private *dev_priv,
+ enum intel_display_power_domain domain)
+{
+ struct i915_power_well *power_well = &dev_priv->power_well;
+
+ spin_lock_irq(&power_well->lock);
+ __intel_power_well_get(power_well);
+ spin_unlock_irq(&power_well->lock);
+}
+
+static void vlv_display_power_put(struct drm_i915_private *dev_priv,
+ enum intel_display_power_domain domain)
+{
+ struct i915_power_well *power_well = &dev_priv->power_well;
+
+ spin_lock_irq(&power_well->lock);
+ __intel_power_well_put(power_well);
+ spin_unlock_irq(&power_well->lock);
+}
+
+static bool hsw_display_power_enabled(struct drm_i915_private *dev_priv,
+ enum intel_display_power_domain domain)
+{
+ switch (domain) {
+ case POWER_DOMAIN_PIPE_A:
+ case POWER_DOMAIN_TRANSCODER_EDP:
+ return true;
+ case POWER_DOMAIN_VGA:
+ case POWER_DOMAIN_PIPE_B:
+ case POWER_DOMAIN_PIPE_C:
+ case POWER_DOMAIN_PIPE_A_PANEL_FITTER:
+ case POWER_DOMAIN_PIPE_B_PANEL_FITTER:
+ case POWER_DOMAIN_PIPE_C_PANEL_FITTER:
+ case POWER_DOMAIN_TRANSCODER_A:
+ case POWER_DOMAIN_TRANSCODER_B:
+ case POWER_DOMAIN_TRANSCODER_C:
+ return I915_READ(HSW_PWR_WELL_DRIVER) ==
+ (HSW_PWR_WELL_ENABLE_REQUEST |
+ HSW_PWR_WELL_STATE_ENABLED);
+ default:
+ BUG();
+ }
+}
+
+static bool __vlv_get_power_well(struct drm_i915_private *dev_priv,
+ u32 pwrgt_mask)
+{
+ u32 reg_val;
+
+ mutex_lock(&dev_priv->rps.hw_lock);
+ reg_val = vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_STATUS);
+ mutex_unlock(&dev_priv->rps.hw_lock);
+
+ return !(reg_val & pwrgt_mask);
+}
+
+static bool vlv_display_power_enabled(struct drm_i915_private *dev_priv,
+ enum intel_display_power_domain domain)
+{
+ return __vlv_get_power_well(dev_priv, DISP2D_PWRGT);
+}
+
+static void hsw_set_display_power(struct drm_i915_private *dev_priv,
+ bool enable)
+{
+ struct drm_device *dev = dev_priv->dev;
+ bool is_enabled, enable_requested;
+ uint32_t tmp;
+
+ tmp = I915_READ(HSW_PWR_WELL_DRIVER);
+ is_enabled = tmp & HSW_PWR_WELL_STATE_ENABLED;
+ enable_requested = tmp & HSW_PWR_WELL_ENABLE_REQUEST;
+
+ if (enable) {
+ if (!enable_requested)
+ I915_WRITE(HSW_PWR_WELL_DRIVER,
+ HSW_PWR_WELL_ENABLE_REQUEST);
+
+ if (!is_enabled) {
+ DRM_DEBUG_KMS("Enabling power well\n");
+ if (wait_for((I915_READ(HSW_PWR_WELL_DRIVER) &
+ HSW_PWR_WELL_STATE_ENABLED), 20))
+ DRM_ERROR("Timeout enabling power well\n");
+ }
+ } else {
+ if (enable_requested) {
+ unsigned long irqflags;
+ enum pipe p;
+
+ I915_WRITE(HSW_PWR_WELL_DRIVER, 0);
+ POSTING_READ(HSW_PWR_WELL_DRIVER);
+ DRM_DEBUG_KMS("Requesting to disable the power well\n");
+
+ /*
+ * After this, the registers on the pipes that are part
+ * of the power well will become zero, so we have to
+ * adjust our counters according to that.
+ *
+ * FIXME: Should we do this in general in
+ * drm_vblank_post_modeset?
+ */
+ spin_lock_irqsave(&dev->vbl_lock, irqflags);
+ for_each_pipe(p)
+ if (p != PIPE_A)
+ dev->last_vblank[p] = 0;
+ spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
+ }
+ }
+}
+
+static void __vlv_set_power_well(struct drm_i915_private *dev_priv,
+ u32 pwrgt_mask, bool enable)
+{
+ u32 reg_val;
+
+ mutex_lock(&dev_priv->rps.hw_lock);
+ reg_val = vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_STATUS);
+ if (enable) {
+ if (reg_val & pwrgt_mask) {
+ reg_val &= ~pwrgt_mask;
+ vlv_punit_write(dev_priv, PUNIT_REG_PWRGT_CTRL,
+ reg_val);
+ if (wait_for(!(vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_STATUS) & pwrgt_mask), 100))
+ DRM_ERROR("timed out waiting for power well enable\n");
+ }
+ } else {
+ reg_val |= pwrgt_mask;
+ vlv_punit_write(dev_priv, PUNIT_REG_PWRGT_CTRL, reg_val);
+ if (wait_for(vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_STATUS) & pwrgt_mask, 100))
+ DRM_ERROR("timed out waiting for power well disable\n"); }
+ reg_val = vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_STATUS);
+ mutex_unlock(&dev_priv->rps.hw_lock);
+}
+
+static void vlv_set_display_power(struct drm_i915_private *dev_priv,
+ bool enable)
+{
+ __vlv_set_power_well(dev_priv, DISP2D_PWRGT, enable);
+}
+
+static void vlv_set_render_power(struct drm_i915_private *dev_priv, bool enable)
+{
+ __vlv_set_power_well(dev_priv, RENDER_PWRGT, enable);
+}
+
+static void vlv_set_media_power(struct drm_i915_private *dev_priv, bool enable)
+{
+ __vlv_set_power_well(dev_priv, MEDIA_PWRGT, enable);
+}
+
void intel_uncore_early_sanitize(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -234,9 +437,17 @@ void intel_uncore_init(struct drm_device *dev)
if (IS_VALLEYVIEW(dev)) {
dev_priv->uncore.funcs.force_wake_get = vlv_force_wake_get;
dev_priv->uncore.funcs.force_wake_put = vlv_force_wake_put;
+ dev_priv->uncore.funcs.set_display_power = vlv_set_display_power;
+ dev_priv->uncore.funcs.display_power_enabled = vlv_display_power_enabled;
+ dev_priv->uncore.funcs.display_power_get = vlv_display_power_get;
+ dev_priv->uncore.funcs.display_power_put = vlv_display_power_put;
} else if (IS_HASWELL(dev)) {
dev_priv->uncore.funcs.force_wake_get = __gen6_gt_force_wake_mt_get;
dev_priv->uncore.funcs.force_wake_put = __gen6_gt_force_wake_mt_put;
+ dev_priv->uncore.funcs.set_display_power = hsw_set_display_power;
+ dev_priv->uncore.funcs.display_power_enabled = hsw_display_power_enabled;
+ dev_priv->uncore.funcs.display_power_get = hsw_display_power_get;
+ dev_priv->uncore.funcs.display_power_put = hsw_display_power_put;
} else if (IS_IVYBRIDGE(dev)) {
u32 ecobus;

--
1.8.3.1