[Intel-gfx] [PATCH 2/2] drm/i915: Add Baytrail PSR Support.

Rodrigo Vivi rodrigo.vivi at gmail.com
Fri Feb 7 19:05:26 CET 2014


On Fri, Feb 7, 2014 at 3:24 PM, Ville Syrjälä
<ville.syrjala at linux.intel.com> wrote:
> On Wed, Feb 05, 2014 at 05:04:31PM -0200, Rodrigo Vivi wrote:
>> This patch adds PSR Support to Baytrail.
>>
>> Baytrail cannot easily detect screen updates and force PSR exit.
>> So we inactivate it on {busy_ioctl, set_domain, sw_finish and mark_busy}
>> and update to enable it back on next display mark_idle.
>>
>> v2: Also inactivate PSR on cursor update.
>> v3: Inactivate PSR on mark_busy, dset_domain and sw_finish_ioctl, and
>>     early on page flip besides avoid initializing inactive/active flag
>>     more than once.
>> v4: Fix identation issues.
>> v5: Rebase and add Baytrail per pipe support although leaving PIPE_B
>>     support disabled by for now since it isn't working properly yet.
>> v6: Removing forgotten comment and useless clkgating definition.
>> v7: Remove inactivate from set_domain. Chris warned this was semanticaly
>>     wrong.
>> v8: Accept Ville's suggestions: Use register's names matching spec and
>>     warn if transition took longer than it should.
>> v9: New version with delayed work to get PSR back. Disabling it on
>>     set_domain but not rescheduing it back until next finish_page_flip.
>>
>> Cc: Chris Wilson <chris at chris-wilson.co.uk>
>> Cc: Ville Syrjälä <ville.syrjala at linux.intel.com>
>> Signed-off-by: Rodrigo Vivi <rodrigo.vivi at gmail.com>
>> ---
>>  drivers/gpu/drm/i915/i915_debugfs.c  |  36 ++++-
>>  drivers/gpu/drm/i915/i915_drv.h      |   5 +-
>>  drivers/gpu/drm/i915/i915_gem.c      |  12 ++
>>  drivers/gpu/drm/i915/i915_reg.h      |  37 +++++
>>  drivers/gpu/drm/i915/i915_suspend.c  |   2 +-
>>  drivers/gpu/drm/i915/intel_display.c |  18 ++-
>>  drivers/gpu/drm/i915/intel_dp.c      | 256 ++++++++++++++++++++++++++++++-----
>>  drivers/gpu/drm/i915/intel_drv.h     |   1 +
>>  8 files changed, 323 insertions(+), 44 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
>> index bc8707f..2949c48 100644
>> --- a/drivers/gpu/drm/i915/i915_debugfs.c
>> +++ b/drivers/gpu/drm/i915/i915_debugfs.c
>> @@ -1900,6 +1900,8 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
>>       struct drm_device *dev = node->minor->dev;
>>       struct drm_i915_private *dev_priv = dev->dev_private;
>>       u32 psrperf = 0;
>> +     u32 statA = 0;
>> +     u32 statB = 0;
>>       bool enabled = false;
>>
>>       intel_runtime_pm_get(dev_priv);
>> @@ -1907,14 +1909,38 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
>>       seq_printf(m, "Sink_Support: %s\n", yesno(dev_priv->psr.sink_support));
>>       seq_printf(m, "Source_OK: %s\n", yesno(dev_priv->psr.source_ok));
>>
>> -     enabled = HAS_PSR(dev) &&
>> -             I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
>> -     seq_printf(m, "Enabled: %s\n", yesno(enabled));
>> +     if (HAS_PSR(dev)) {
>> +             if (IS_VALLEYVIEW(dev)) {
>> +                     statA = I915_READ(VLV_PSRSTAT(PIPE_A)) &
>> +                             VLV_EDP_PSR_CURR_STATE_MASK;
>> +                     statB = I915_READ(VLV_PSRSTAT(PIPE_B)) &
>> +                             VLV_EDP_PSR_CURR_STATE_MASK;
>> +                     enabled = ((statA == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
>> +                                (statA == VLV_EDP_PSR_ACTIVE_SF_UPDATE) ||
>> +                                (statB == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
>> +                                (statB == VLV_EDP_PSR_ACTIVE_SF_UPDATE));
>> +             } else
>> +                     enabled = I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
>> +     }
>> +     seq_printf(m, "Enabled: %s", yesno(enabled));
>>
>> -     if (HAS_PSR(dev))
>> +     if (IS_VALLEYVIEW(dev)) {
>> +             if ((statA == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
>> +                 (statA == VLV_EDP_PSR_ACTIVE_SF_UPDATE))
>> +                     seq_puts(m, " pipe A");
>> +             if ((statB == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
>> +                 (statB == VLV_EDP_PSR_ACTIVE_SF_UPDATE))
>> +                     seq_puts(m, " pipe B");
>> +     }
>> +
>> +     seq_puts(m, "\n");
>> +
>> +     /* VLV PSR has no kind of performance counter */
>> +     if (HAS_PSR(dev) && !IS_VALLEYVIEW(dev)) {
>>               psrperf = I915_READ(EDP_PSR_PERF_CNT(dev)) &
>>                       EDP_PSR_PERF_CNT_MASK;
>> -     seq_printf(m, "Performance_Counter: %u\n", psrperf);
>> +             seq_printf(m, "Performance_Counter: %u\n", psrperf);
>> +     }
>>
>>       intel_runtime_pm_put(dev_priv);
>>       return 0;
>> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
>> index 21470be..87c346a 100644
>> --- a/drivers/gpu/drm/i915/i915_drv.h
>> +++ b/drivers/gpu/drm/i915/i915_drv.h
>> @@ -747,7 +747,9 @@ struct i915_psr {
>>       bool sink_support;
>>       bool source_ok;
>>       bool setup_done;
>> +     bool active;
>>       struct mutex lock;
>> +     struct delayed_work work;
>>  };
>>
>>  enum intel_pch {
>> @@ -1867,7 +1869,8 @@ struct drm_i915_file_private {
>>
>>  #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))
>> +#define HAS_PSR(dev)         (IS_HASWELL(dev) || IS_BROADWELL(dev) || \
>> +                              IS_VALLEYVIEW(dev))
>>  #define HAS_PC8(dev)         (IS_HASWELL(dev)) /* XXX HSW:ULX */
>>  #define HAS_RUNTIME_PM(dev)  (IS_HASWELL(dev))
>>
>> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
>> index 39770f7..5ef1380 100644
>> --- a/drivers/gpu/drm/i915/i915_gem.c
>> +++ b/drivers/gpu/drm/i915/i915_gem.c
>> @@ -1256,6 +1256,14 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
>>               goto unlock;
>>       }
>>
>> +     /* Here we don't reschedule work to get PSR back because userspace
>> +     * can set domain and take as much as time it wants to write it.
>> +     * We only reschedule it back on next finish_page_flip. There is the
>> +     * risk of PSR be inactive forever depending on how compositor works,
>> +     * but this is the safest way to avoid loosing screen updates.
>> +     */
>> +     intel_edp_psr_inactivate(dev, false);
>
> This will still block until PSR is deactivated (aux communication and
> all). That still seems like a bad idea to do while holding struct_mutex.

So, what do you suggest here?

>
>> +
>>       /* 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.
>> @@ -1299,6 +1307,8 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
>>       if (ret)
>>               return ret;
>>
>> +     intel_edp_psr_inactivate(dev, true);
>> +
>>       obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle));
>>       if (&obj->base == NULL) {
>>               ret = -ENOENT;
>> @@ -4059,6 +4069,8 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
>>       if (ret)
>>               return ret;
>>
>> +     intel_edp_psr_inactivate(dev, true);
>> +
>>       obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle));
>>       if (&obj->base == NULL) {
>>               ret = -ENOENT;
>> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
>> index cbbaf26..a5815d0 100644
>> --- a/drivers/gpu/drm/i915/i915_reg.h
>> +++ b/drivers/gpu/drm/i915/i915_reg.h
>> @@ -1968,6 +1968,43 @@
>>  #define BCLRPAT(pipe) _PIPE(pipe, _BCLRPAT_A, _BCLRPAT_B)
>>  #define VSYNCSHIFT(trans) _TRANSCODER(trans, _VSYNCSHIFT_A, _VSYNCSHIFT_B)
>>
>> +/* VLV eDP PSR registers */
>> +#define _PSRCTLA                             (VLV_DISPLAY_BASE + 0x60090)
>> +#define _PSRCTLB                             (VLV_DISPLAY_BASE + 0x61090)
>> +#define  VLV_EDP_PSR_ENABLE                  (1<<0)
>> +#define  VLV_EDP_PSR_RESET                   (1<<1)
>> +#define  VLV_EDP_PSR_MODE_MASK                       (7<<2)
>> +#define  VLV_EDP_PSR_MODE_HW_TIMER           (1<<3)
>> +#define  VLV_EDP_PSR_MODE_SW_TIMER           (1<<2)
>> +#define  VLV_EDP_PSR_SINGLE_FRAME_UPDATE     (1<<7)
>> +#define  VLV_EDP_PSR_ACTIVE_ENTRY            (1<<8)
>> +#define  VLV_EDP_PSR_SRC_TRANSMITTER_STATE   (1<<9)
>> +#define  VLV_EDP_PSR_DBL_FRAME                       (1<<10)
>> +#define  VLV_EDP_PSR_FRAME_COUNT_MASK                (0xff<<16)
>> +#define  VLV_EDP_PSR_IDLE_FRAME_SHIFT                16
>> +#define  VLV_EDP_PSR_INT_TRANSITION          (1<<24)
>> +#define VLV_PSRCTL(pipe) _PIPE(pipe, _PSRCTLA, _PSRCTLB)
>> +
>> +#define _VSCSDPA                     (VLV_DISPLAY_BASE + 0x600a0)
>> +#define _VSCSDPB                     (VLV_DISPLAY_BASE + 0x610a0)
>> +#define  VLV_EDP_PSR_SDP_FREQ_MASK   (3<<30)
>> +#define  VLV_EDP_PSR_SDP_FREQ_ONCE   (1<<31)
>> +#define  VLV_EDP_PSR_SDP_FREQ_EVFRAME        (1<<30)
>> +#define VLV_VSCSDP(pipe)     _PIPE(pipe, _VSCSDPA, _VSCSDPB)
>> +
>> +#define _PSRSTATA                    (VLV_DISPLAY_BASE + 0x60094)
>> +#define _PSRSTATB                    (VLV_DISPLAY_BASE + 0x61094)
>> +#define  VLV_EDP_PSR_LAST_STATE_MASK (7<<3)
>> +#define  VLV_EDP_PSR_CURR_STATE_MASK 7
>> +#define  VLV_EDP_PSR_DISABLED                (0<<0)
>> +#define  VLV_EDP_PSR_INACTIVE                (1<<0)
>> +#define  VLV_EDP_PSR_IN_TRANS_TO_ACTIVE      (2<<0)
>> +#define  VLV_EDP_PSR_ACTIVE_NORFB_UP (3<<0)
>> +#define  VLV_EDP_PSR_ACTIVE_SF_UPDATE        (4<<0)
>> +#define  VLV_EDP_PSR_EXIT            (5<<0)
>> +#define  VLV_EDP_PSR_IN_TRANS                (1<<7)
>> +#define VLV_PSRSTAT(pipe) _PIPE(pipe, _PSRSTATA, _PSRSTATB)
>> +
>>  /* HSW+ eDP PSR registers */
>>  #define EDP_PSR_BASE(dev)                       (IS_HASWELL(dev) ? 0x64800 : 0x6f800)
>>  #define EDP_PSR_CTL(dev)                     (EDP_PSR_BASE(dev) + 0)
>> diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c
>> index ffcba21..6b31020 100644
>> --- a/drivers/gpu/drm/i915/i915_suspend.c
>> +++ b/drivers/gpu/drm/i915/i915_suspend.c
>> @@ -289,8 +289,8 @@ static void i915_restore_display(struct drm_device *dev)
>>       }
>>
>>       /* Force a full PSR setup on resume */
>> +     intel_edp_psr_inactivate(dev, false);
>>       dev_priv->psr.setup_done = false;
>> -     intel_edp_psr_update(dev);
>>
>>       /* only restore FBC info on the platform that supports FBC*/
>>       intel_disable_fbc(dev);
>> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
>> index 1a9aa19..92d6df4 100644
>> --- a/drivers/gpu/drm/i915/intel_display.c
>> +++ b/drivers/gpu/drm/i915/intel_display.c
>> @@ -4451,9 +4451,13 @@ static void intel_connector_check_state(struct intel_connector *connector)
>>   * consider. */
>>  void intel_connector_dpms(struct drm_connector *connector, int mode)
>>  {
>> +     struct drm_device *dev = connector->dev;
>> +
>>       /* All the simple cases only support two dpms states. */
>> -     if (mode != DRM_MODE_DPMS_ON)
>> +     if (mode != DRM_MODE_DPMS_ON) {
>>               mode = DRM_MODE_DPMS_OFF;
>> +             intel_edp_psr_inactivate(dev, false);
>
> Maybe check if this connector even has PSR enabled?

the psr_inactivate already does that by checking psr.setup_done and psr.active.

>
>> +     }
>>
>>       if (mode == connector->dpms)
>>               return;
>> @@ -7501,6 +7505,8 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
>>       u32 base = 0, pos = 0;
>>       bool visible;
>>
>> +     intel_edp_psr_inactivate(dev, true);
>
> Don't need to inactivate unless the cursor is or was visible.

so, below next retur after check visible and cursor_visible should be
enough, right?
>
>> +
>>       if (on)
>>               base = intel_crtc->cursor_addr;
>>
>> @@ -8230,12 +8236,15 @@ void intel_mark_idle(struct drm_device *dev)
>>               gen6_rps_idle(dev->dev_private);
>>  }
>>
>> +
>>  void intel_mark_fb_busy(struct drm_i915_gem_object *obj,
>>                       struct intel_ring_buffer *ring)
>>  {
>>       struct drm_device *dev = obj->base.dev;
>>       struct drm_crtc *crtc;
>>
>> +     intel_edp_psr_inactivate(dev, true);
>> +
>>       if (!i915.powersave)
>>               return;
>>
>> @@ -8336,6 +8345,10 @@ static void do_intel_finish_page_flip(struct drm_device *dev,
>>       queue_work(dev_priv->wq, &work->work);
>>
>>       trace_i915_flip_complete(intel_crtc->plane, work->pending_flip_obj);
>> +
>> +     /* Get PSR back after set_domain inactivated it without rescheduling
>> +     * it back. */
>> +     schedule_delayed_work(&dev_priv->psr.work, msecs_to_jiffies(5000));
>>  }
>>
>>  void intel_finish_page_flip(struct drm_device *dev, int pipe)
>> @@ -8688,6 +8701,9 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
>>       if (work == NULL)
>>               return -ENOMEM;
>>
>> +     /* Inactivate PSR early in page flip */
>> +     intel_edp_psr_inactivate(dev, true);
>> +
>>       work->event = event;
>>       work->crtc = crtc;
>>       work->old_fb_obj = to_intel_framebuffer(old_fb)->obj;
>> diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
>> index 80054bb..576c204 100644
>> --- a/drivers/gpu/drm/i915/intel_dp.c
>> +++ b/drivers/gpu/drm/i915/intel_dp.c
>> @@ -1578,21 +1578,35 @@ static void intel_dp_get_config(struct intel_encoder *encoder,
>>       }
>>  }
>>
>> -static bool is_edp_psr(struct drm_device *dev)
>> +static bool is_edp_psr(struct intel_dp *intel_dp)
>> +{
>> +     return intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED;
>> +}
>> +
>> +static bool vlv_edp_is_psr_enabled_on_pipe(struct drm_device *dev, int pipe)
>>  {
>>       struct drm_i915_private *dev_priv = dev->dev_private;
>> +     uint32_t val;
>>
>> -     return dev_priv->psr.sink_support;
>> +     val = I915_READ(VLV_PSRSTAT(pipe)) &
>> +             VLV_EDP_PSR_CURR_STATE_MASK;
>> +     return (val == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
>> +             (val == VLV_EDP_PSR_ACTIVE_SF_UPDATE);
>>  }
>>
>>  static bool intel_edp_is_psr_enabled(struct drm_device *dev)
>>  {
>>       struct drm_i915_private *dev_priv = dev->dev_private;
>>
>> -     if (!HAS_PSR(dev))
>> -             return false;
>> +     if (HAS_PSR(dev)) {
>> +             if (IS_VALLEYVIEW(dev))
>> +                     return vlv_edp_is_psr_enabled_on_pipe(dev, PIPE_A) ||
>> +                             vlv_edp_is_psr_enabled_on_pipe(dev, PIPE_B);
>> +             else
>> +                     return I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
>> +     }
>>
>> -     return I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
>> +     return false;
>>  }
>>
>>  static void intel_edp_psr_write_vsc(struct intel_dp *intel_dp,
>> @@ -1624,32 +1638,60 @@ static void intel_edp_psr_write_vsc(struct intel_dp *intel_dp,
>>       POSTING_READ(ctl_reg);
>>  }
>>
>> +static void intel_edp_psr_work(struct work_struct *work)
>> +{
>> +     struct drm_i915_private *dev_priv =
>> +             container_of(work, typeof(*dev_priv), psr.work.work);
>> +     struct drm_device *dev = dev_priv->dev;
>> +
>> +     intel_edp_psr_update(dev);
>> +}
>> +
>>  static void intel_edp_psr_setup(struct intel_dp *intel_dp)
>>  {
>> -     struct drm_device *dev = intel_dp_to_dev(intel_dp);
>> +     struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
>> +     struct drm_device *dev = intel_dig_port->base.base.dev;
>>       struct drm_i915_private *dev_priv = dev->dev_private;
>>       struct edp_vsc_psr psr_vsc;
>> +     uint32_t val;
>>
>>       if (dev_priv->psr.setup_done)
>>               return;
>>
>> -     mutex_init(&dev_priv->psr.lock);
>> -
>> -     /* Prepare VSC packet as per EDP 1.3 spec, Table 3.10 */
>> -     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;
>> -     intel_edp_psr_write_vsc(intel_dp, &psr_vsc);
>> -
>> -     /* Avoid continuous PSR exit by masking memup and hpd */
>> -     I915_WRITE(EDP_PSR_DEBUG_CTL(dev), EDP_PSR_DEBUG_MASK_MEMUP |
>> -                EDP_PSR_DEBUG_MASK_HPD | EDP_PSR_DEBUG_MASK_LPSP);
>> +     if (IS_VALLEYVIEW(dev)) {
>> +             val  = I915_READ(VLV_VSCSDP(PIPE_A));
>> +             val &= ~VLV_EDP_PSR_SDP_FREQ_MASK;
>> +             val |= VLV_EDP_PSR_SDP_FREQ_EVFRAME;
>> +             I915_WRITE(VLV_VSCSDP(PIPE_A), val);
>> +
>> +             val  = I915_READ(VLV_VSCSDP(PIPE_B));
>> +             val &= ~VLV_EDP_PSR_SDP_FREQ_MASK;
>> +             val |= VLV_EDP_PSR_SDP_FREQ_EVFRAME;
>> +             I915_WRITE(VLV_VSCSDP(PIPE_B), val);
>> +     } else {
>> +             /* Prepare VSC packet as per EDP 1.3 spec, Table 3.10 */
>> +             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;
>> +             intel_edp_psr_write_vsc(intel_dp, &psr_vsc);
>> +
>> +             /* Avoid continuous PSR exit by masking memup and hpd */
>> +             I915_WRITE(EDP_PSR_DEBUG_CTL(dev), EDP_PSR_DEBUG_MASK_MEMUP |
>> +                        EDP_PSR_DEBUG_MASK_HPD | EDP_PSR_DEBUG_MASK_LPSP);
>> +     }
>>
>>       dev_priv->psr.setup_done = true;
>>  }
>>
>> +static void vlv_edp_psr_enable_sink(struct intel_dp *intel_dp)
>> +{
>> +     /* Enable PSR in sink */
>> +     intel_dp_aux_native_write_1(intel_dp, DP_PSR_EN_CFG,
>> +                                         DP_PSR_ENABLE);
>> +}
>> +
>>  static void intel_edp_psr_enable_sink(struct intel_dp *intel_dp)
>>  {
>>       struct drm_device *dev = intel_dp_to_dev(intel_dp);
>> @@ -1680,6 +1722,27 @@ static void intel_edp_psr_enable_sink(struct intel_dp *intel_dp)
>>                  (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT));
>>  }
>>
>> +static void vlv_edp_psr_enable_source(struct intel_dp *intel_dp)
>> +{
>> +     struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
>> +     struct drm_device *dev = intel_dig_port->base.base.dev;
>> +     struct drm_i915_private *dev_priv = dev->dev_private;
>> +     struct intel_crtc *intel_crtc =
>> +             to_intel_crtc(intel_dig_port->base.base.crtc);
>> +
>> +     uint32_t idle_frames = 1;
>> +     uint32_t val = 0;
>> +
>> +     val |= VLV_EDP_PSR_ENABLE;
>> +     val &= ~VLV_EDP_PSR_MODE_MASK;
>> +
>> +     val |= VLV_EDP_PSR_MODE_HW_TIMER;
>> +     val &= ~VLV_EDP_PSR_FRAME_COUNT_MASK;
>> +     val |= idle_frames << EDP_PSR_IDLE_FRAME_SHIFT;
>> +
>> +     I915_WRITE(VLV_PSRCTL(intel_crtc->pipe), val);
>> +}
>> +
>>  static void intel_edp_psr_enable_source(struct intel_dp *intel_dp)
>>  {
>>       struct drm_device *dev = intel_dp_to_dev(intel_dp);
>> @@ -1721,8 +1784,8 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp)
>>               return false;
>>       }
>>
>> -     if ((intel_encoder->type != INTEL_OUTPUT_EDP) ||
>> -         (dig_port->port != PORT_A)) {
>> +     if (HAS_DDI(dev) && ((intel_encoder->type != INTEL_OUTPUT_EDP) ||
>> +                          (dig_port->port != PORT_A))) {
>>               DRM_DEBUG_KMS("HSW ties PSR to DDI A (eDP)\n");
>>               return false;
>>       }
>> @@ -1767,23 +1830,45 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp)
>>               return false;
>>       }
>>
>> +     /* Baytrail supports per-pipe PSR configuration, however PSR on
>> +     * PIPE_B isn't working properly. So let's keep it disabled for now. */
>> +     if (IS_VALLEYVIEW(dev) && intel_crtc->pipe != PIPE_A) {
>> +             DRM_DEBUG_KMS("PSR on BYT isn't enabled on pipe B.\n");
>> +             return false;
>> +     }
>> +
>>       dev_priv->psr.source_ok = true;
>>       return true;
>>  }
>>
>>  static void intel_edp_psr_do_enable(struct intel_dp *intel_dp)
>>  {
>> -     struct drm_device *dev = intel_dp_to_dev(intel_dp);
>> +     struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
>> +     struct drm_device *dev = intel_dig_port->base.base.dev;
>> +     struct drm_i915_private *dev_priv = dev->dev_private;
>> +     struct intel_crtc *intel_crtc =
>> +             to_intel_crtc(intel_dig_port->base.base.crtc);
>>
>> -     if (!intel_edp_psr_match_conditions(intel_dp) ||
>> -         intel_edp_is_psr_enabled(dev))
>> -             return;
>> +     if (IS_VALLEYVIEW(dev)) {
>> +             if (vlv_edp_is_psr_enabled_on_pipe(dev, intel_crtc->pipe))
>> +                     return;
>> +     } else
>> +             if (intel_edp_is_psr_enabled(dev))
>> +                     return;
>>
>>       /* Enable PSR on the panel */
>> -     intel_edp_psr_enable_sink(intel_dp);
>> +     if (IS_VALLEYVIEW(dev))
>> +             vlv_edp_psr_enable_sink(intel_dp);
>> +     else
>> +             intel_edp_psr_enable_sink(intel_dp);
>>
>>       /* Enable PSR on the host */
>> -     intel_edp_psr_enable_source(intel_dp);
>> +     if (IS_VALLEYVIEW(dev))
>> +             vlv_edp_psr_enable_source(intel_dp);
>> +     else
>> +             intel_edp_psr_enable_source(intel_dp);
>> +
>> +     dev_priv->psr.active = true;
>>  }
>>
>>  void intel_edp_psr_enable(struct intel_dp *intel_dp)
>> @@ -1791,16 +1876,43 @@ void intel_edp_psr_enable(struct intel_dp *intel_dp)
>>       struct drm_device *dev = intel_dp_to_dev(intel_dp);
>>       struct drm_i915_private *dev_priv = dev->dev_private;
>>
>> +     if (!is_edp_psr(intel_dp))
>> +             return;
>> +
>>       /* Setup PSR once */
>>       intel_edp_psr_setup(intel_dp);
>>
>>       mutex_lock(&dev_priv->psr.lock);
>> -     if (intel_edp_psr_match_conditions(intel_dp) &&
>> -         !intel_edp_is_psr_enabled(dev))
>> +     if (intel_edp_psr_match_conditions(intel_dp))
>>               intel_edp_psr_do_enable(intel_dp);
>>       mutex_unlock(&dev_priv->psr.lock);
>>  }
>>
>> +void vlv_edp_psr_disable(struct intel_dp *intel_dp)
>> +{
>> +     struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
>> +     struct drm_device *dev = intel_dig_port->base.base.dev;
>> +     struct drm_i915_private *dev_priv = dev->dev_private;
>> +     struct intel_crtc *intel_crtc =
>> +             to_intel_crtc(intel_dig_port->base.base.crtc);
>> +     uint32_t val;
>> +
>> +     if (!dev_priv->psr.setup_done)
>> +             return;
>> +
>> +     intel_edp_psr_inactivate(dev, false);
>> +
>> +     if (wait_for((I915_READ(VLV_PSRSTAT(intel_crtc->pipe)) &
>> +                   VLV_EDP_PSR_IN_TRANS) == 0, 250))
>> +             WARN(1, "PSR transition took longer than expected\n");
>> +
>> +     val = I915_READ(VLV_PSRCTL(intel_crtc->pipe));
>> +     val &= ~VLV_EDP_PSR_ACTIVE_ENTRY;
>> +     val &= ~VLV_EDP_PSR_ENABLE;
>> +     val &= ~VLV_EDP_PSR_MODE_MASK;
>> +     I915_WRITE(VLV_PSRCTL(intel_crtc->pipe), val);
>> +}
>> +
>>  void intel_edp_psr_disable(struct intel_dp *intel_dp)
>>  {
>>       struct drm_device *dev = intel_dp_to_dev(intel_dp);
>> @@ -1823,6 +1935,7 @@ void intel_edp_psr_update(struct drm_device *dev)
>>       struct drm_i915_private *dev_priv = dev->dev_private;
>>       struct intel_encoder *encoder;
>>       struct intel_dp *intel_dp = NULL;
>> +     struct intel_crtc *intel_crtc;
>>
>>       if (!dev_priv->psr.setup_done)
>>               return;
>> @@ -1830,26 +1943,93 @@ void intel_edp_psr_update(struct drm_device *dev)
>>       list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head)
>>               if (encoder->type == INTEL_OUTPUT_EDP) {
>>                       intel_dp = enc_to_intel_dp(&encoder->base);
>> -
>> -                     if (!is_edp_psr(dev))
>> -                             return;
>> +                     intel_crtc = to_intel_crtc(encoder->base.crtc);
>>
>>                       mutex_lock(&dev_priv->psr.lock);
>> -                     if (!intel_edp_psr_match_conditions(intel_dp))
>> -                             intel_edp_psr_disable(intel_dp);
>> -                     else
>> +                     if (!intel_edp_psr_match_conditions(intel_dp)) {
>> +                             if (IS_VALLEYVIEW(dev))
>> +                                     vlv_edp_psr_disable(intel_dp);
>> +                             else
>> +                                     intel_edp_psr_disable(intel_dp);
>> +                     } else
>>                               if (!intel_edp_is_psr_enabled(dev))
>>                                       intel_edp_psr_do_enable(intel_dp);
>>                       mutex_unlock(&dev_priv->psr.lock);
>>               }
>>  }
>>
>> +void intel_edp_psr_do_inactivate(struct drm_device *dev)
>> +{
>> +     struct drm_i915_private *dev_priv = dev->dev_private;
>> +     struct intel_connector *connector;
>> +     struct intel_encoder *encoder;
>> +     struct intel_crtc *intel_crtc;
>> +     struct intel_dp *intel_dp = NULL;
>> +
>> +     list_for_each_entry(connector, &dev->mode_config.connector_list,
>> +                         base.head) {
>> +
>> +             if (connector->base.dpms != DRM_MODE_DPMS_ON)
>> +                     continue;
>> +
>> +             encoder = to_intel_encoder(connector->base.encoder);
>> +             if (encoder->type == INTEL_OUTPUT_EDP) {
>> +
>> +                     intel_dp = enc_to_intel_dp(&encoder->base);
>> +                     intel_crtc = to_intel_crtc(encoder->base.crtc);
>
> Still digging through the encoder and crtc pointers w/o holding any
> modeset locks. Feels dangerous. intel_edp_psr_match_conditions() does
> the same kind of stuff, and that too now gets called outside modeset
> codepaths.

I don't see an easy way to solve this with current sturcture here
since we need the pipe and intel_dp pointer.
Maybe on the future rework putiing psr inside pipe_config and in a
scruct with all needed info without require to interate anywhere.
But on the current structure I don't see an easy solution, do you?

>> +
>> +                     if (!vlv_edp_is_psr_enabled_on_pipe(dev,
>> +                                                         intel_crtc->pipe))
>> +                             continue;
>> +
>> +                     dev_priv->psr.active = false;
>> +
>> +                     I915_WRITE(VLV_PSRCTL(intel_crtc->pipe),
>> +                                VLV_EDP_PSR_RESET);
>> +                     /* WaClearPSRReset:vlv */
>> +                     I915_WRITE(VLV_PSRCTL(intel_crtc->pipe), 0);
>> +
>> +                     intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
>> +             }
>> +     }
>> +}
>> +
>> +void intel_edp_psr_inactivate(struct drm_device *dev, bool schedule_back)
>> +{
>> +     struct drm_i915_private *dev_priv = dev->dev_private;
>> +
>> +     if (!IS_VALLEYVIEW(dev))
>> +             return;
>> +
>> +     if (!dev_priv->psr.setup_done)
>> +             return;
>> +
>> +     /* If it was requested to not turn psr back delayed work must be
>> +      * canceled even if it is already on inactivated state. */
>> +     if (!schedule_back)
>> +             cancel_delayed_work_sync(&dev_priv->psr.work);
>> +
>> +     if (!dev_priv->psr.active)
>> +             return;
>
> What if psr gets re-activated just after you checked above?

Shouldn't be dangerus... psr.activate idea is to minimize I915_WRITE,
mainly when calling busy ioctls in sequence...
But I'm open to suggestions.

>
>> +
>> +     cancel_delayed_work_sync(&dev_priv->psr.work);
>> +
>> +     intel_edp_psr_do_inactivate(dev);
>> +
>> +     if (schedule_back)
>> +             schedule_delayed_work(&dev_priv->psr.work,
>> +                                   msecs_to_jiffies(5000));
>> +}
>> +
>>  static void intel_disable_dp(struct intel_encoder *encoder)
>>  {
>>       struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
>>       enum port port = dp_to_dig_port(intel_dp)->port;
>>       struct drm_device *dev = encoder->base.dev;
>>
>> +     if (IS_VALLEYVIEW(dev))
>> +             vlv_edp_psr_disable(intel_dp);
>> +
>>       /* Make sure the panel is off before trying to change the mode. But also
>>        * ensure that we have vdd while we switch off the panel. */
>>       intel_edp_backlight_off(intel_dp);
>> @@ -1906,6 +2086,7 @@ static void vlv_enable_dp(struct intel_encoder *encoder)
>>       struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
>>
>>       intel_edp_backlight_on(intel_dp);
>> +     intel_edp_psr_enable(intel_dp);
>>  }
>>
>>  static void g4x_pre_enable_dp(struct intel_encoder *encoder)
>> @@ -3846,8 +4027,11 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
>>               BUG();
>>       }
>>
>> -     if (is_edp(intel_dp))
>> +     if (is_edp(intel_dp)) {
>>               intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq);
>> +             INIT_DELAYED_WORK(&dev_priv->psr.work, intel_edp_psr_work);
>> +             mutex_init(&dev_priv->psr.lock);
>
> Initializing global state from a per-connector init function seems a bit
> weird.

I thought it as well, but didn't find another place.
My first attempt was at psr_setup, but it is called again after
suspend/resume and it was breaking things up.
suggestions?

>
>> +     }
>>
>>       error = intel_dp_i2c_init(intel_dp, intel_connector, name);
>>       WARN(error, "intel_dp_i2c_init failed with error %d for port %c\n",
>> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
>> index 71c1371..d8103f9 100644
>> --- a/drivers/gpu/drm/i915/intel_drv.h
>> +++ b/drivers/gpu/drm/i915/intel_drv.h
>> @@ -748,6 +748,7 @@ void intel_edp_panel_off(struct intel_dp *intel_dp);
>>  void intel_edp_psr_enable(struct intel_dp *intel_dp);
>>  void intel_edp_psr_disable(struct intel_dp *intel_dp);
>>  void intel_edp_psr_update(struct drm_device *dev);
>> +void intel_edp_psr_inactivate(struct drm_device *dev, bool schedule_back);
>>
>>
>>  /* intel_dsi.c */
>> --
>> 1.8.1.2
>
> --
> Ville Syrjälä
> Intel OTC

Hi Ville, thank you very much for your comments. Coding is improving a
lot along with stability here.
Please help me to get this structure merged than we rework all psr
later to avoid that loop over connectors and encoders....

Thanks,

-- 
Rodrigo Vivi
Blog: http://blog.vivi.eng.br



More information about the Intel-gfx mailing list