On Wed, Apr 18, 2012 at 11:51:15AM -0300, Eugeni Dodonov wrote:
> This moves DRPS, RPS and RC6-related functionality into intel_pm module.
> 
> It also removes the linux/cpufreq.h include from intel_display, as its
> only user was the GPU turbo-related functionality in Gen6+ code path.
> 
> Acked-by: Jesse Barnes <[email protected]>
> Acked-by: Ben Widawsky <[email protected]>
> Signed-off-by: Eugeni Dodonov <[email protected]>
> @@ -6609,232 +6437,6 @@ void intel_init_emon(struct drm_device *dev)
>       dev_priv->corr = (lcfuse & LCFUSE_HIV_MASK);
>  }
>  
> -int intel_enable_rc6(const struct drm_device *dev)
> -{
> -     /*
> -      * Respect the kernel parameter if it is set
> -      */
> -     if (i915_enable_rc6 >= 0)
> -             return i915_enable_rc6;
> -
> -     /*
> -      * Disable RC6 on Ironlake
> -      */
> -     if (INTEL_INFO(dev)->gen == 5)
> -             return 0;
> -
> -     /* Sorry Haswell, no RC6 for you for now. */
> -     if (IS_HASWELL(dev))
> -             return 0;

You remove this line but don't readd it. Not good.
-Daniel

> -
> -     /*
> -      * Disable rc6 on Sandybridge
> -      */
> -     if (INTEL_INFO(dev)->gen == 6) {
> -             DRM_DEBUG_DRIVER("Sandybridge: deep RC6 disabled\n");
> -             return INTEL_RC6_ENABLE;
> -     }
> -     DRM_DEBUG_DRIVER("RC6 and deep RC6 enabled\n");
> -     return (INTEL_RC6_ENABLE | INTEL_RC6p_ENABLE);
> -}
> -
> -void gen6_enable_rps(struct drm_i915_private *dev_priv)
> -{
> -     u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
> -     u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
> -     u32 pcu_mbox, rc6_mask = 0;
> -     u32 gtfifodbg;
> -     int cur_freq, min_freq, max_freq;
> -     int rc6_mode;
> -     int i;
> -
> -     /* Here begins a magic sequence of register writes to enable
> -      * auto-downclocking.
> -      *
> -      * Perhaps there might be some value in exposing these to
> -      * userspace...
> -      */
> -     I915_WRITE(GEN6_RC_STATE, 0);
> -     mutex_lock(&dev_priv->dev->struct_mutex);
> -
> -     /* Clear the DBG now so we don't confuse earlier errors */
> -     if ((gtfifodbg = I915_READ(GTFIFODBG))) {
> -             DRM_ERROR("GT fifo had a previous error %x\n", gtfifodbg);
> -             I915_WRITE(GTFIFODBG, gtfifodbg);
> -     }
> -
> -     gen6_gt_force_wake_get(dev_priv);
> -
> -     /* disable the counters and set deterministic thresholds */
> -     I915_WRITE(GEN6_RC_CONTROL, 0);
> -
> -     I915_WRITE(GEN6_RC1_WAKE_RATE_LIMIT, 1000 << 16);
> -     I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 40 << 16 | 30);
> -     I915_WRITE(GEN6_RC6pp_WAKE_RATE_LIMIT, 30);
> -     I915_WRITE(GEN6_RC_EVALUATION_INTERVAL, 125000);
> -     I915_WRITE(GEN6_RC_IDLE_HYSTERSIS, 25);
> -
> -     for (i = 0; i < I915_NUM_RINGS; i++)
> -             I915_WRITE(RING_MAX_IDLE(dev_priv->ring[i].mmio_base), 10);
> -
> -     I915_WRITE(GEN6_RC_SLEEP, 0);
> -     I915_WRITE(GEN6_RC1e_THRESHOLD, 1000);
> -     I915_WRITE(GEN6_RC6_THRESHOLD, 50000);
> -     I915_WRITE(GEN6_RC6p_THRESHOLD, 100000);
> -     I915_WRITE(GEN6_RC6pp_THRESHOLD, 64000); /* unused */
> -
> -     rc6_mode = intel_enable_rc6(dev_priv->dev);
> -     if (rc6_mode & INTEL_RC6_ENABLE)
> -             rc6_mask |= GEN6_RC_CTL_RC6_ENABLE;
> -
> -     if (rc6_mode & INTEL_RC6p_ENABLE)
> -             rc6_mask |= GEN6_RC_CTL_RC6p_ENABLE;
> -
> -     if (rc6_mode & INTEL_RC6pp_ENABLE)
> -             rc6_mask |= GEN6_RC_CTL_RC6pp_ENABLE;
> -
> -     DRM_INFO("Enabling RC6 states: RC6 %s, RC6p %s, RC6pp %s\n",
> -                     (rc6_mode & INTEL_RC6_ENABLE) ? "on" : "off",
> -                     (rc6_mode & INTEL_RC6p_ENABLE) ? "on" : "off",
> -                     (rc6_mode & INTEL_RC6pp_ENABLE) ? "on" : "off");
> -
> -     I915_WRITE(GEN6_RC_CONTROL,
> -                rc6_mask |
> -                GEN6_RC_CTL_EI_MODE(1) |
> -                GEN6_RC_CTL_HW_ENABLE);
> -
> -     I915_WRITE(GEN6_RPNSWREQ,
> -                GEN6_FREQUENCY(10) |
> -                GEN6_OFFSET(0) |
> -                GEN6_AGGRESSIVE_TURBO);
> -     I915_WRITE(GEN6_RC_VIDEO_FREQ,
> -                GEN6_FREQUENCY(12));
> -
> -     I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 1000000);
> -     I915_WRITE(GEN6_RP_INTERRUPT_LIMITS,
> -                18 << 24 |
> -                6 << 16);
> -     I915_WRITE(GEN6_RP_UP_THRESHOLD, 10000);
> -     I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 1000000);
> -     I915_WRITE(GEN6_RP_UP_EI, 100000);
> -     I915_WRITE(GEN6_RP_DOWN_EI, 5000000);
> -     I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10);
> -     I915_WRITE(GEN6_RP_CONTROL,
> -                GEN6_RP_MEDIA_TURBO |
> -                GEN6_RP_MEDIA_HW_MODE |
> -                GEN6_RP_MEDIA_IS_GFX |
> -                GEN6_RP_ENABLE |
> -                GEN6_RP_UP_BUSY_AVG |
> -                GEN6_RP_DOWN_IDLE_CONT);
> -
> -     if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
> -                  500))
> -             DRM_ERROR("timeout waiting for pcode mailbox to become idle\n");
> -
> -     I915_WRITE(GEN6_PCODE_DATA, 0);
> -     I915_WRITE(GEN6_PCODE_MAILBOX,
> -                GEN6_PCODE_READY |
> -                GEN6_PCODE_WRITE_MIN_FREQ_TABLE);
> -     if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
> -                  500))
> -             DRM_ERROR("timeout waiting for pcode mailbox to finish\n");
> -
> -     min_freq = (rp_state_cap & 0xff0000) >> 16;
> -     max_freq = rp_state_cap & 0xff;
> -     cur_freq = (gt_perf_status & 0xff00) >> 8;
> -
> -     /* Check for overclock support */
> -     if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
> -                  500))
> -             DRM_ERROR("timeout waiting for pcode mailbox to become idle\n");
> -     I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_READ_OC_PARAMS);
> -     pcu_mbox = I915_READ(GEN6_PCODE_DATA);
> -     if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
> -                  500))
> -             DRM_ERROR("timeout waiting for pcode mailbox to finish\n");
> -     if (pcu_mbox & (1<<31)) { /* OC supported */
> -             max_freq = pcu_mbox & 0xff;
> -             DRM_DEBUG_DRIVER("overclocking supported, adjusting frequency 
> max to %dMHz\n", pcu_mbox * 50);
> -     }
> -
> -     /* In units of 100MHz */
> -     dev_priv->max_delay = max_freq;
> -     dev_priv->min_delay = min_freq;
> -     dev_priv->cur_delay = cur_freq;
> -
> -     /* requires MSI enabled */
> -     I915_WRITE(GEN6_PMIER,
> -                GEN6_PM_MBOX_EVENT |
> -                GEN6_PM_THERMAL_EVENT |
> -                GEN6_PM_RP_DOWN_TIMEOUT |
> -                GEN6_PM_RP_UP_THRESHOLD |
> -                GEN6_PM_RP_DOWN_THRESHOLD |
> -                GEN6_PM_RP_UP_EI_EXPIRED |
> -                GEN6_PM_RP_DOWN_EI_EXPIRED);
> -     spin_lock_irq(&dev_priv->rps_lock);
> -     WARN_ON(dev_priv->pm_iir != 0);
> -     I915_WRITE(GEN6_PMIMR, 0);
> -     spin_unlock_irq(&dev_priv->rps_lock);
> -     /* enable all PM interrupts */
> -     I915_WRITE(GEN6_PMINTRMSK, 0);
> -
> -     gen6_gt_force_wake_put(dev_priv);
> -     mutex_unlock(&dev_priv->dev->struct_mutex);
> -}
> -
> -void gen6_update_ring_freq(struct drm_i915_private *dev_priv)
> -{
> -     int min_freq = 15;
> -     int gpu_freq, ia_freq, max_ia_freq;
> -     int scaling_factor = 180;
> -
> -     max_ia_freq = cpufreq_quick_get_max(0);
> -     /*
> -      * Default to measured freq if none found, PCU will ensure we don't go
> -      * over
> -      */
> -     if (!max_ia_freq)
> -             max_ia_freq = tsc_khz;
> -
> -     /* Convert from kHz to MHz */
> -     max_ia_freq /= 1000;
> -
> -     mutex_lock(&dev_priv->dev->struct_mutex);
> -
> -     /*
> -      * For each potential GPU frequency, load a ring frequency we'd like
> -      * to use for memory access.  We do this by specifying the IA frequency
> -      * the PCU should use as a reference to determine the ring frequency.
> -      */
> -     for (gpu_freq = dev_priv->max_delay; gpu_freq >= dev_priv->min_delay;
> -          gpu_freq--) {
> -             int diff = dev_priv->max_delay - gpu_freq;
> -
> -             /*
> -              * For GPU frequencies less than 750MHz, just use the lowest
> -              * ring freq.
> -              */
> -             if (gpu_freq < min_freq)
> -                     ia_freq = 800;
> -             else
> -                     ia_freq = max_ia_freq - ((diff * scaling_factor) / 2);
> -             ia_freq = DIV_ROUND_CLOSEST(ia_freq, 100);
> -
> -             I915_WRITE(GEN6_PCODE_DATA,
> -                        (ia_freq << GEN6_PCODE_FREQ_IA_RATIO_SHIFT) |
> -                        gpu_freq);
> -             I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY |
> -                        GEN6_PCODE_WRITE_MIN_FREQ_TABLE);
> -             if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) &
> -                           GEN6_PCODE_READY) == 0, 10)) {
> -                     DRM_ERROR("pcode write of freq table timed out\n");
> -                     continue;
> -             }
> -     }
> -
> -     mutex_unlock(&dev_priv->dev->struct_mutex);
> -}
> -
>  static void ironlake_init_clock_gating(struct drm_device *dev)
>  {
>       struct drm_i915_private *dev_priv = dev->dev_private;
> @@ -7178,121 +6780,6 @@ static void cpt_init_clock_gating(struct drm_device 
> *dev)
>               I915_WRITE(TRANS_CHICKEN2(pipe), TRANS_AUTOTRAIN_GEN_STALL_DIS);
>  }
>  
> -static void ironlake_teardown_rc6(struct drm_device *dev)
> -{
> -     struct drm_i915_private *dev_priv = dev->dev_private;
> -
> -     if (dev_priv->renderctx) {
> -             i915_gem_object_unpin(dev_priv->renderctx);
> -             drm_gem_object_unreference(&dev_priv->renderctx->base);
> -             dev_priv->renderctx = NULL;
> -     }
> -
> -     if (dev_priv->pwrctx) {
> -             i915_gem_object_unpin(dev_priv->pwrctx);
> -             drm_gem_object_unreference(&dev_priv->pwrctx->base);
> -             dev_priv->pwrctx = NULL;
> -     }
> -}
> -
> -static void ironlake_disable_rc6(struct drm_device *dev)
> -{
> -     struct drm_i915_private *dev_priv = dev->dev_private;
> -
> -     if (I915_READ(PWRCTXA)) {
> -             /* Wake the GPU, prevent RC6, then restore RSTDBYCTL */
> -             I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) | RCX_SW_EXIT);
> -             wait_for(((I915_READ(RSTDBYCTL) & RSX_STATUS_MASK) == 
> RSX_STATUS_ON),
> -                      50);
> -
> -             I915_WRITE(PWRCTXA, 0);
> -             POSTING_READ(PWRCTXA);
> -
> -             I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT);
> -             POSTING_READ(RSTDBYCTL);
> -     }
> -
> -     ironlake_teardown_rc6(dev);
> -}
> -
> -static int ironlake_setup_rc6(struct drm_device *dev)
> -{
> -     struct drm_i915_private *dev_priv = dev->dev_private;
> -
> -     if (dev_priv->renderctx == NULL)
> -             dev_priv->renderctx = intel_alloc_context_page(dev);
> -     if (!dev_priv->renderctx)
> -             return -ENOMEM;
> -
> -     if (dev_priv->pwrctx == NULL)
> -             dev_priv->pwrctx = intel_alloc_context_page(dev);
> -     if (!dev_priv->pwrctx) {
> -             ironlake_teardown_rc6(dev);
> -             return -ENOMEM;
> -     }
> -
> -     return 0;
> -}
> -
> -void ironlake_enable_rc6(struct drm_device *dev)
> -{
> -     struct drm_i915_private *dev_priv = dev->dev_private;
> -     int ret;
> -
> -     /* rc6 disabled by default due to repeated reports of hanging during
> -      * boot and resume.
> -      */
> -     if (!intel_enable_rc6(dev))
> -             return;
> -
> -     mutex_lock(&dev->struct_mutex);
> -     ret = ironlake_setup_rc6(dev);
> -     if (ret) {
> -             mutex_unlock(&dev->struct_mutex);
> -             return;
> -     }
> -
> -     /*
> -      * GPU can automatically power down the render unit if given a page
> -      * to save state.
> -      */
> -     ret = BEGIN_LP_RING(6);
> -     if (ret) {
> -             ironlake_teardown_rc6(dev);
> -             mutex_unlock(&dev->struct_mutex);
> -             return;
> -     }
> -
> -     OUT_RING(MI_SUSPEND_FLUSH | MI_SUSPEND_FLUSH_EN);
> -     OUT_RING(MI_SET_CONTEXT);
> -     OUT_RING(dev_priv->renderctx->gtt_offset |
> -              MI_MM_SPACE_GTT |
> -              MI_SAVE_EXT_STATE_EN |
> -              MI_RESTORE_EXT_STATE_EN |
> -              MI_RESTORE_INHIBIT);
> -     OUT_RING(MI_SUSPEND_FLUSH);
> -     OUT_RING(MI_NOOP);
> -     OUT_RING(MI_FLUSH);
> -     ADVANCE_LP_RING();
> -
> -     /*
> -      * Wait for the command parser to advance past MI_SET_CONTEXT. The HW
> -      * does an implicit flush, combined with MI_FLUSH above, it should be
> -      * safe to assume that renderctx is valid
> -      */
> -     ret = intel_wait_ring_idle(LP_RING(dev_priv));
> -     if (ret) {
> -             DRM_ERROR("failed to enable ironlake power power savings\n");
> -             ironlake_teardown_rc6(dev);
> -             mutex_unlock(&dev->struct_mutex);
> -             return;
> -     }
> -
> -     I915_WRITE(PWRCTXA, dev_priv->pwrctx->gtt_offset | PWRCTX_EN);
> -     I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT);
> -     mutex_unlock(&dev->struct_mutex);
> -}
> -
>  void intel_init_clock_gating(struct drm_device *dev)
>  {
>       struct drm_i915_private *dev_priv = dev->dev_private;
> diff --git a/drivers/gpu/drm/i915/intel_drv.h 
> b/drivers/gpu/drm/i915/intel_drv.h
> index f1e27ce..c87f29a 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -396,6 +396,7 @@ extern void intel_crtc_fb_gamma_set(struct drm_crtc 
> *crtc, u16 red, u16 green,
>  extern void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 
> *green,
>                                   u16 *blue, int regno);
>  extern void intel_enable_clock_gating(struct drm_device *dev);
> +extern void ironlake_disable_rc6(struct drm_device *dev);
>  extern void ironlake_enable_drps(struct drm_device *dev);
>  extern void ironlake_disable_drps(struct drm_device *dev);
>  extern void gen6_enable_rps(struct drm_i915_private *dev_priv);
> diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
> index c5bc4c4..58b26df0 100644
> --- a/drivers/gpu/drm/i915/intel_pm.c
> +++ b/drivers/gpu/drm/i915/intel_pm.c
> @@ -25,6 +25,7 @@
>   *
>   */
>  
> +#include <linux/cpufreq.h>
>  #include "i915_drv.h"
>  #include "intel_drv.h"
>  
> @@ -1979,3 +1980,511 @@ void intel_update_sprite_watermarks(struct drm_device 
> *dev, int pipe,
>                                                  pixel_size);
>  }
>  
> +static struct drm_i915_gem_object *
> +intel_alloc_context_page(struct drm_device *dev)
> +{
> +     struct drm_i915_gem_object *ctx;
> +     int ret;
> +
> +     WARN_ON(!mutex_is_locked(&dev->struct_mutex));
> +
> +     ctx = i915_gem_alloc_object(dev, 4096);
> +     if (!ctx) {
> +             DRM_DEBUG("failed to alloc power context, RC6 disabled\n");
> +             return NULL;
> +     }
> +
> +     ret = i915_gem_object_pin(ctx, 4096, true);
> +     if (ret) {
> +             DRM_ERROR("failed to pin power context: %d\n", ret);
> +             goto err_unref;
> +     }
> +
> +     ret = i915_gem_object_set_to_gtt_domain(ctx, 1);
> +     if (ret) {
> +             DRM_ERROR("failed to set-domain on power context: %d\n", ret);
> +             goto err_unpin;
> +     }
> +
> +     return ctx;
> +
> +err_unpin:
> +     i915_gem_object_unpin(ctx);
> +err_unref:
> +     drm_gem_object_unreference(&ctx->base);
> +     mutex_unlock(&dev->struct_mutex);
> +     return NULL;
> +}
> +
> +bool ironlake_set_drps(struct drm_device *dev, u8 val)
> +{
> +     struct drm_i915_private *dev_priv = dev->dev_private;
> +     u16 rgvswctl;
> +
> +     rgvswctl = I915_READ16(MEMSWCTL);
> +     if (rgvswctl & MEMCTL_CMD_STS) {
> +             DRM_DEBUG("gpu busy, RCS change rejected\n");
> +             return false; /* still busy with another command */
> +     }
> +
> +     rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) |
> +             (val << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM;
> +     I915_WRITE16(MEMSWCTL, rgvswctl);
> +     POSTING_READ16(MEMSWCTL);
> +
> +     rgvswctl |= MEMCTL_CMD_STS;
> +     I915_WRITE16(MEMSWCTL, rgvswctl);
> +
> +     return true;
> +}
> +
> +void ironlake_enable_drps(struct drm_device *dev)
> +{
> +     struct drm_i915_private *dev_priv = dev->dev_private;
> +     u32 rgvmodectl = I915_READ(MEMMODECTL);
> +     u8 fmax, fmin, fstart, vstart;
> +
> +     /* Enable temp reporting */
> +     I915_WRITE16(PMMISC, I915_READ(PMMISC) | MCPPCE_EN);
> +     I915_WRITE16(TSC1, I915_READ(TSC1) | TSE);
> +
> +     /* 100ms RC evaluation intervals */
> +     I915_WRITE(RCUPEI, 100000);
> +     I915_WRITE(RCDNEI, 100000);
> +
> +     /* Set max/min thresholds to 90ms and 80ms respectively */
> +     I915_WRITE(RCBMAXAVG, 90000);
> +     I915_WRITE(RCBMINAVG, 80000);
> +
> +     I915_WRITE(MEMIHYST, 1);
> +
> +     /* Set up min, max, and cur for interrupt handling */
> +     fmax = (rgvmodectl & MEMMODE_FMAX_MASK) >> MEMMODE_FMAX_SHIFT;
> +     fmin = (rgvmodectl & MEMMODE_FMIN_MASK);
> +     fstart = (rgvmodectl & MEMMODE_FSTART_MASK) >>
> +             MEMMODE_FSTART_SHIFT;
> +
> +     vstart = (I915_READ(PXVFREQ_BASE + (fstart * 4)) & PXVFREQ_PX_MASK) >>
> +             PXVFREQ_PX_SHIFT;
> +
> +     dev_priv->fmax = fmax; /* IPS callback will increase this */
> +     dev_priv->fstart = fstart;
> +
> +     dev_priv->max_delay = fstart;
> +     dev_priv->min_delay = fmin;
> +     dev_priv->cur_delay = fstart;
> +
> +     DRM_DEBUG_DRIVER("fmax: %d, fmin: %d, fstart: %d\n",
> +                      fmax, fmin, fstart);
> +
> +     I915_WRITE(MEMINTREN, MEMINT_CX_SUPR_EN | MEMINT_EVAL_CHG_EN);
> +
> +     /*
> +      * Interrupts will be enabled in ironlake_irq_postinstall
> +      */
> +
> +     I915_WRITE(VIDSTART, vstart);
> +     POSTING_READ(VIDSTART);
> +
> +     rgvmodectl |= MEMMODE_SWMODE_EN;
> +     I915_WRITE(MEMMODECTL, rgvmodectl);
> +
> +     if (wait_for((I915_READ(MEMSWCTL) & MEMCTL_CMD_STS) == 0, 10))
> +             DRM_ERROR("stuck trying to change perf mode\n");
> +     msleep(1);
> +
> +     ironlake_set_drps(dev, fstart);
> +
> +     dev_priv->last_count1 = I915_READ(0x112e4) + I915_READ(0x112e8) +
> +             I915_READ(0x112e0);
> +     dev_priv->last_time1 = jiffies_to_msecs(jiffies);
> +     dev_priv->last_count2 = I915_READ(0x112f4);
> +     getrawmonotonic(&dev_priv->last_time2);
> +}
> +
> +void ironlake_disable_drps(struct drm_device *dev)
> +{
> +     struct drm_i915_private *dev_priv = dev->dev_private;
> +     u16 rgvswctl = I915_READ16(MEMSWCTL);
> +
> +     /* Ack interrupts, disable EFC interrupt */
> +     I915_WRITE(MEMINTREN, I915_READ(MEMINTREN) & ~MEMINT_EVAL_CHG_EN);
> +     I915_WRITE(MEMINTRSTS, MEMINT_EVAL_CHG);
> +     I915_WRITE(DEIER, I915_READ(DEIER) & ~DE_PCU_EVENT);
> +     I915_WRITE(DEIIR, DE_PCU_EVENT);
> +     I915_WRITE(DEIMR, I915_READ(DEIMR) | DE_PCU_EVENT);
> +
> +     /* Go back to the starting frequency */
> +     ironlake_set_drps(dev, dev_priv->fstart);
> +     msleep(1);
> +     rgvswctl |= MEMCTL_CMD_STS;
> +     I915_WRITE(MEMSWCTL, rgvswctl);
> +     msleep(1);
> +
> +}
> +
> +void gen6_set_rps(struct drm_device *dev, u8 val)
> +{
> +     struct drm_i915_private *dev_priv = dev->dev_private;
> +     u32 swreq;
> +
> +     swreq = (val & 0x3ff) << 25;
> +     I915_WRITE(GEN6_RPNSWREQ, swreq);
> +}
> +
> +void gen6_disable_rps(struct drm_device *dev)
> +{
> +     struct drm_i915_private *dev_priv = dev->dev_private;
> +
> +     I915_WRITE(GEN6_RPNSWREQ, 1 << 31);
> +     I915_WRITE(GEN6_PMINTRMSK, 0xffffffff);
> +     I915_WRITE(GEN6_PMIER, 0);
> +     /* Complete PM interrupt masking here doesn't race with the rps work
> +      * item again unmasking PM interrupts because that is using a different
> +      * register (PMIMR) to mask PM interrupts. The only risk is in leaving
> +      * stale bits in PMIIR and PMIMR which gen6_enable_rps will clean up. */
> +
> +     spin_lock_irq(&dev_priv->rps_lock);
> +     dev_priv->pm_iir = 0;
> +     spin_unlock_irq(&dev_priv->rps_lock);
> +
> +     I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR));
> +}
> +
> +int intel_enable_rc6(const struct drm_device *dev)
> +{
> +     /*
> +      * Respect the kernel parameter if it is set
> +      */
> +     if (i915_enable_rc6 >= 0)
> +             return i915_enable_rc6;
> +
> +     /*
> +      * Disable RC6 on Ironlake
> +      */
> +     if (INTEL_INFO(dev)->gen == 5)
> +             return 0;
> +
> +     /*
> +      * Disable rc6 on Sandybridge
> +      */
> +     if (INTEL_INFO(dev)->gen == 6) {
> +             DRM_DEBUG_DRIVER("Sandybridge: deep RC6 disabled\n");
> +             return INTEL_RC6_ENABLE;
> +     }
> +     DRM_DEBUG_DRIVER("RC6 and deep RC6 enabled\n");
> +     return (INTEL_RC6_ENABLE | INTEL_RC6p_ENABLE);
> +}
> +
> +void gen6_enable_rps(struct drm_i915_private *dev_priv)
> +{
> +     u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
> +     u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
> +     u32 pcu_mbox, rc6_mask = 0;
> +     u32 gtfifodbg;
> +     int cur_freq, min_freq, max_freq;
> +     int rc6_mode;
> +     int i;
> +
> +     /* Here begins a magic sequence of register writes to enable
> +      * auto-downclocking.
> +      *
> +      * Perhaps there might be some value in exposing these to
> +      * userspace...
> +      */
> +     I915_WRITE(GEN6_RC_STATE, 0);
> +     mutex_lock(&dev_priv->dev->struct_mutex);
> +
> +     /* Clear the DBG now so we don't confuse earlier errors */
> +     if ((gtfifodbg = I915_READ(GTFIFODBG))) {
> +             DRM_ERROR("GT fifo had a previous error %x\n", gtfifodbg);
> +             I915_WRITE(GTFIFODBG, gtfifodbg);
> +     }
> +
> +     gen6_gt_force_wake_get(dev_priv);
> +
> +     /* disable the counters and set deterministic thresholds */
> +     I915_WRITE(GEN6_RC_CONTROL, 0);
> +
> +     I915_WRITE(GEN6_RC1_WAKE_RATE_LIMIT, 1000 << 16);
> +     I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 40 << 16 | 30);
> +     I915_WRITE(GEN6_RC6pp_WAKE_RATE_LIMIT, 30);
> +     I915_WRITE(GEN6_RC_EVALUATION_INTERVAL, 125000);
> +     I915_WRITE(GEN6_RC_IDLE_HYSTERSIS, 25);
> +
> +     for (i = 0; i < I915_NUM_RINGS; i++)
> +             I915_WRITE(RING_MAX_IDLE(dev_priv->ring[i].mmio_base), 10);
> +
> +     I915_WRITE(GEN6_RC_SLEEP, 0);
> +     I915_WRITE(GEN6_RC1e_THRESHOLD, 1000);
> +     I915_WRITE(GEN6_RC6_THRESHOLD, 50000);
> +     I915_WRITE(GEN6_RC6p_THRESHOLD, 100000);
> +     I915_WRITE(GEN6_RC6pp_THRESHOLD, 64000); /* unused */
> +
> +     rc6_mode = intel_enable_rc6(dev_priv->dev);
> +     if (rc6_mode & INTEL_RC6_ENABLE)
> +             rc6_mask |= GEN6_RC_CTL_RC6_ENABLE;
> +
> +     if (rc6_mode & INTEL_RC6p_ENABLE)
> +             rc6_mask |= GEN6_RC_CTL_RC6p_ENABLE;
> +
> +     if (rc6_mode & INTEL_RC6pp_ENABLE)
> +             rc6_mask |= GEN6_RC_CTL_RC6pp_ENABLE;
> +
> +     DRM_INFO("Enabling RC6 states: RC6 %s, RC6p %s, RC6pp %s\n",
> +                     (rc6_mode & INTEL_RC6_ENABLE) ? "on" : "off",
> +                     (rc6_mode & INTEL_RC6p_ENABLE) ? "on" : "off",
> +                     (rc6_mode & INTEL_RC6pp_ENABLE) ? "on" : "off");
> +
> +     I915_WRITE(GEN6_RC_CONTROL,
> +                rc6_mask |
> +                GEN6_RC_CTL_EI_MODE(1) |
> +                GEN6_RC_CTL_HW_ENABLE);
> +
> +     I915_WRITE(GEN6_RPNSWREQ,
> +                GEN6_FREQUENCY(10) |
> +                GEN6_OFFSET(0) |
> +                GEN6_AGGRESSIVE_TURBO);
> +     I915_WRITE(GEN6_RC_VIDEO_FREQ,
> +                GEN6_FREQUENCY(12));
> +
> +     I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 1000000);
> +     I915_WRITE(GEN6_RP_INTERRUPT_LIMITS,
> +                18 << 24 |
> +                6 << 16);
> +     I915_WRITE(GEN6_RP_UP_THRESHOLD, 10000);
> +     I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 1000000);
> +     I915_WRITE(GEN6_RP_UP_EI, 100000);
> +     I915_WRITE(GEN6_RP_DOWN_EI, 5000000);
> +     I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10);
> +     I915_WRITE(GEN6_RP_CONTROL,
> +                GEN6_RP_MEDIA_TURBO |
> +                GEN6_RP_MEDIA_HW_MODE |
> +                GEN6_RP_MEDIA_IS_GFX |
> +                GEN6_RP_ENABLE |
> +                GEN6_RP_UP_BUSY_AVG |
> +                GEN6_RP_DOWN_IDLE_CONT);
> +
> +     if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
> +                  500))
> +             DRM_ERROR("timeout waiting for pcode mailbox to become idle\n");
> +
> +     I915_WRITE(GEN6_PCODE_DATA, 0);
> +     I915_WRITE(GEN6_PCODE_MAILBOX,
> +                GEN6_PCODE_READY |
> +                GEN6_PCODE_WRITE_MIN_FREQ_TABLE);
> +     if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
> +                  500))
> +             DRM_ERROR("timeout waiting for pcode mailbox to finish\n");
> +
> +     min_freq = (rp_state_cap & 0xff0000) >> 16;
> +     max_freq = rp_state_cap & 0xff;
> +     cur_freq = (gt_perf_status & 0xff00) >> 8;
> +
> +     /* Check for overclock support */
> +     if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
> +                  500))
> +             DRM_ERROR("timeout waiting for pcode mailbox to become idle\n");
> +     I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_READ_OC_PARAMS);
> +     pcu_mbox = I915_READ(GEN6_PCODE_DATA);
> +     if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
> +                  500))
> +             DRM_ERROR("timeout waiting for pcode mailbox to finish\n");
> +     if (pcu_mbox & (1<<31)) { /* OC supported */
> +             max_freq = pcu_mbox & 0xff;
> +             DRM_DEBUG_DRIVER("overclocking supported, adjusting frequency 
> max to %dMHz\n", pcu_mbox * 50);
> +     }
> +
> +     /* In units of 100MHz */
> +     dev_priv->max_delay = max_freq;
> +     dev_priv->min_delay = min_freq;
> +     dev_priv->cur_delay = cur_freq;
> +
> +     /* requires MSI enabled */
> +     I915_WRITE(GEN6_PMIER,
> +                GEN6_PM_MBOX_EVENT |
> +                GEN6_PM_THERMAL_EVENT |
> +                GEN6_PM_RP_DOWN_TIMEOUT |
> +                GEN6_PM_RP_UP_THRESHOLD |
> +                GEN6_PM_RP_DOWN_THRESHOLD |
> +                GEN6_PM_RP_UP_EI_EXPIRED |
> +                GEN6_PM_RP_DOWN_EI_EXPIRED);
> +     spin_lock_irq(&dev_priv->rps_lock);
> +     WARN_ON(dev_priv->pm_iir != 0);
> +     I915_WRITE(GEN6_PMIMR, 0);
> +     spin_unlock_irq(&dev_priv->rps_lock);
> +     /* enable all PM interrupts */
> +     I915_WRITE(GEN6_PMINTRMSK, 0);
> +
> +     gen6_gt_force_wake_put(dev_priv);
> +     mutex_unlock(&dev_priv->dev->struct_mutex);
> +}
> +
> +void gen6_update_ring_freq(struct drm_i915_private *dev_priv)
> +{
> +     int min_freq = 15;
> +     int gpu_freq, ia_freq, max_ia_freq;
> +     int scaling_factor = 180;
> +
> +     max_ia_freq = cpufreq_quick_get_max(0);
> +     /*
> +      * Default to measured freq if none found, PCU will ensure we don't go
> +      * over
> +      */
> +     if (!max_ia_freq)
> +             max_ia_freq = tsc_khz;
> +
> +     /* Convert from kHz to MHz */
> +     max_ia_freq /= 1000;
> +
> +     mutex_lock(&dev_priv->dev->struct_mutex);
> +
> +     /*
> +      * For each potential GPU frequency, load a ring frequency we'd like
> +      * to use for memory access.  We do this by specifying the IA frequency
> +      * the PCU should use as a reference to determine the ring frequency.
> +      */
> +     for (gpu_freq = dev_priv->max_delay; gpu_freq >= dev_priv->min_delay;
> +          gpu_freq--) {
> +             int diff = dev_priv->max_delay - gpu_freq;
> +
> +             /*
> +              * For GPU frequencies less than 750MHz, just use the lowest
> +              * ring freq.
> +              */
> +             if (gpu_freq < min_freq)
> +                     ia_freq = 800;
> +             else
> +                     ia_freq = max_ia_freq - ((diff * scaling_factor) / 2);
> +             ia_freq = DIV_ROUND_CLOSEST(ia_freq, 100);
> +
> +             I915_WRITE(GEN6_PCODE_DATA,
> +                        (ia_freq << GEN6_PCODE_FREQ_IA_RATIO_SHIFT) |
> +                        gpu_freq);
> +             I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY |
> +                        GEN6_PCODE_WRITE_MIN_FREQ_TABLE);
> +             if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) &
> +                           GEN6_PCODE_READY) == 0, 10)) {
> +                     DRM_ERROR("pcode write of freq table timed out\n");
> +                     continue;
> +             }
> +     }
> +
> +     mutex_unlock(&dev_priv->dev->struct_mutex);
> +}
> +
> +static void ironlake_teardown_rc6(struct drm_device *dev)
> +{
> +     struct drm_i915_private *dev_priv = dev->dev_private;
> +
> +     if (dev_priv->renderctx) {
> +             i915_gem_object_unpin(dev_priv->renderctx);
> +             drm_gem_object_unreference(&dev_priv->renderctx->base);
> +             dev_priv->renderctx = NULL;
> +     }
> +
> +     if (dev_priv->pwrctx) {
> +             i915_gem_object_unpin(dev_priv->pwrctx);
> +             drm_gem_object_unreference(&dev_priv->pwrctx->base);
> +             dev_priv->pwrctx = NULL;
> +     }
> +}
> +
> +void ironlake_disable_rc6(struct drm_device *dev)
> +{
> +     struct drm_i915_private *dev_priv = dev->dev_private;
> +
> +     if (I915_READ(PWRCTXA)) {
> +             /* Wake the GPU, prevent RC6, then restore RSTDBYCTL */
> +             I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) | RCX_SW_EXIT);
> +             wait_for(((I915_READ(RSTDBYCTL) & RSX_STATUS_MASK) == 
> RSX_STATUS_ON),
> +                      50);
> +
> +             I915_WRITE(PWRCTXA, 0);
> +             POSTING_READ(PWRCTXA);
> +
> +             I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT);
> +             POSTING_READ(RSTDBYCTL);
> +     }
> +
> +     ironlake_teardown_rc6(dev);
> +}
> +
> +static int ironlake_setup_rc6(struct drm_device *dev)
> +{
> +     struct drm_i915_private *dev_priv = dev->dev_private;
> +
> +     if (dev_priv->renderctx == NULL)
> +             dev_priv->renderctx = intel_alloc_context_page(dev);
> +     if (!dev_priv->renderctx)
> +             return -ENOMEM;
> +
> +     if (dev_priv->pwrctx == NULL)
> +             dev_priv->pwrctx = intel_alloc_context_page(dev);
> +     if (!dev_priv->pwrctx) {
> +             ironlake_teardown_rc6(dev);
> +             return -ENOMEM;
> +     }
> +
> +     return 0;
> +}
> +
> +void ironlake_enable_rc6(struct drm_device *dev)
> +{
> +     struct drm_i915_private *dev_priv = dev->dev_private;
> +     int ret;
> +
> +     /* rc6 disabled by default due to repeated reports of hanging during
> +      * boot and resume.
> +      */
> +     if (!intel_enable_rc6(dev))
> +             return;
> +
> +     mutex_lock(&dev->struct_mutex);
> +     ret = ironlake_setup_rc6(dev);
> +     if (ret) {
> +             mutex_unlock(&dev->struct_mutex);
> +             return;
> +     }
> +
> +     /*
> +      * GPU can automatically power down the render unit if given a page
> +      * to save state.
> +      */
> +     ret = BEGIN_LP_RING(6);
> +     if (ret) {
> +             ironlake_teardown_rc6(dev);
> +             mutex_unlock(&dev->struct_mutex);
> +             return;
> +     }
> +
> +     OUT_RING(MI_SUSPEND_FLUSH | MI_SUSPEND_FLUSH_EN);
> +     OUT_RING(MI_SET_CONTEXT);
> +     OUT_RING(dev_priv->renderctx->gtt_offset |
> +              MI_MM_SPACE_GTT |
> +              MI_SAVE_EXT_STATE_EN |
> +              MI_RESTORE_EXT_STATE_EN |
> +              MI_RESTORE_INHIBIT);
> +     OUT_RING(MI_SUSPEND_FLUSH);
> +     OUT_RING(MI_NOOP);
> +     OUT_RING(MI_FLUSH);
> +     ADVANCE_LP_RING();
> +
> +     /*
> +      * Wait for the command parser to advance past MI_SET_CONTEXT. The HW
> +      * does an implicit flush, combined with MI_FLUSH above, it should be
> +      * safe to assume that renderctx is valid
> +      */
> +     ret = intel_wait_ring_idle(LP_RING(dev_priv));
> +     if (ret) {
> +             DRM_ERROR("failed to enable ironlake power power savings\n");
> +             ironlake_teardown_rc6(dev);
> +             mutex_unlock(&dev->struct_mutex);
> +             return;
> +     }
> +
> +     I915_WRITE(PWRCTXA, dev_priv->pwrctx->gtt_offset | PWRCTX_EN);
> +     I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT);
> +     mutex_unlock(&dev->struct_mutex);
> +}
> +
> -- 
> 1.7.10
> 
> _______________________________________________
> Intel-gfx mailing list
> [email protected]
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx

-- 
Daniel Vetter
Mail: [email protected]
Mobile: +41 (0)79 365 57 48
_______________________________________________
Intel-gfx mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to