[Intel-gfx] [PATCH] drm/i915: Enable/Disable PSR

Paulo Zanoni przanoni at gmail.com
Fri Jun 28 21:31:41 CEST 2013


Hi

2013/6/28 Rodrigo Vivi <rodrigo.vivi at gmail.com>:
> Adding Enable and Disable PSR functionalities. This includes setting the
> PSR configuration over AUX, sending SDP VSC DIP over the eDP PIPE config,
> enabling PSR in the sink via DPCD register and finally enabling PSR on
> the host.
>
> This patch is based on initial PSR code by Sateesh Kavuri and Kumar Shobhit
> but in a different implementation.
>
> v2: * moved functions around and changed its names.
>     * removed VSC DIP unset from disable.
>     * remove FBC wa.
>     * don't mask LSPS anymore.
>     * incorporate new crtc usage after a rebase.
> v3: Make a clear separation between Sink (Panel) and Source (HW) enabling.
> v4: Fix identation and other style issues raised by checkpatch (by Paulo).
>

A few of the comments here were already present in previous reviews.
If you think they're not needed, please reply saying why.


> Credits-by: Sateesh Kavuri <sateesh.kavuri at intel.com>
> Credits-by: Shobhit Kumar <shobhit.kumar at intel.com>
> Signed-off-by: Rodrigo Vivi <rodrigo.vivi at gmail.com>
> ---
>  drivers/gpu/drm/i915/i915_reg.h  |  42 +++++++++++
>  drivers/gpu/drm/i915/intel_dp.c  | 151 +++++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/i915/intel_drv.h |   3 +
>  3 files changed, 196 insertions(+)
>
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index 137be4c..caf57d8 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -1777,6 +1777,47 @@
>  #define BCLRPAT(pipe) _PIPE(pipe, _BCLRPAT_A, _BCLRPAT_B)
>  #define VSYNCSHIFT(trans) _TRANSCODER(trans, _VSYNCSHIFT_A, _VSYNCSHIFT_B)
>
> +/* HSW eDP PSR registers */
> +#define EDP_PSR_CTL                            0x64800
> +#define   EDP_PSR_ENABLE                       (1<<31)
> +#define   EDP_PSR_LINK_DISABLE                 (0<<27)
> +#define   EDP_PSR_LINK_STANDBY                 (1<<27)
> +#define   EDP_PSR_MIN_LINK_ENTRY_TIME_MASK     (3<<25)
> +#define   EDP_PSR_MIN_LINK_ENTRY_TIME_8_LINES  (0<<25)
> +#define   EDP_PSR_MIN_LINK_ENTRY_TIME_4_LINES  (1<<25)
> +#define   EDP_PSR_MIN_LINK_ENTRY_TIME_2_LINES  (2<<25)
> +#define   EDP_PSR_MIN_LINK_ENTRY_TIME_0_LINES  (3<<25)
> +#define   EDP_PSR_MAX_SLEEP_TIME_SHIFT         20
> +#define   EDP_PSR_SKIP_AUX_EXIT                        (1<<12)
> +#define   EDP_PSR_TP1_TP2_SEL                  (0<<11)
> +#define   EDP_PSR_TP1_TP3_SEL                  (1<<11)
> +#define   EDP_PSR_TP2_TP3_TIME_500us           (0<<8)
> +#define   EDP_PSR_TP2_TP3_TIME_100us           (1<<8)
> +#define   EDP_PSR_TP2_TP3_TIME_2500us          (2<<8)
> +#define   EDP_PSR_TP2_TP3_TIME_0us             (3<<8)
> +#define   EDP_PSR_TP1_TIME_500us               (0<<4)
> +#define   EDP_PSR_TP1_TIME_100us               (1<<4)
> +#define   EDP_PSR_TP1_TIME_2500us              (2<<4)
> +#define   EDP_PSR_TP1_TIME_0us                 (3<<4)
> +#define   EDP_PSR_IDLE_FRAME_SHIFT             0
> +
> +#define EDP_PSR_AUX_CTL                        0x64810
> +#define EDP_PSR_AUX_DATA1              0x64814
> +#define   EDP_PSR_DPCD_COMMAND         0x80060000
> +#define EDP_PSR_AUX_DATA2              0x64818
> +#define   EDP_PSR_DPCD_NORMAL_OPERATION        (1<<24)

I know our documentation explicitly says "0x80060000" and
"0x01000000", but to me these magic values are just magic... I think
we could try to reuse the same mechanism we use for the other aux
messages. Check the usage of pack_aux inside intel_dp_aux_ch and also
intel_dp_aux_native_write. Maybe this could be done in a follow-up
patch. Jani gave a suggestion on how to implement this, see email "Re:
[PATCH 5/9] drm/i915: Setup EDP PSR AUX Registers" from January 31.


> +#define EDP_PSR_AUX_DATA3              0x6481c
> +#define EDP_PSR_AUX_DATA4              0x64820
> +#define EDP_PSR_AUX_DATA5              0x64824
> +
> +#define EDP_PSR_STATUS_CTL                     0x64840
> +#define   EDP_PSR_STATUS_STATE_MASK            (7<<29)
> +
> +#define EDP_PSR_DEBUG_CTL              0x64860
> +#define   EDP_PSR_DEBUG_MASK_LPSP      (1<<27)
> +#define   EDP_PSR_DEBUG_MASK_MEMUP     (1<<26)
> +#define   EDP_PSR_DEBUG_MASK_HPD       (1<<25)
> +
>  /* VGA port control */
>  #define ADPA                   0x61100
>  #define PCH_ADPA                0xe1100
> @@ -2046,6 +2087,7 @@
>   * (Haswell and newer) to see which VIDEO_DIP_DATA byte corresponds to each byte
>   * of the infoframe structure specified by CEA-861. */
>  #define   VIDEO_DIP_DATA_SIZE  32
> +#define   VIDEO_DIP_VSC_DATA_SIZE      36
>  #define VIDEO_DIP_CTL          0x61170
>  /* Pre HSW: */
>  #define   VIDEO_DIP_ENABLE             (1 << 31)
> diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
> index dca8fa6..c10be94 100644
> --- a/drivers/gpu/drm/i915/intel_dp.c
> +++ b/drivers/gpu/drm/i915/intel_dp.c
> @@ -1356,6 +1356,157 @@ static bool is_edp_psr(struct intel_dp *intel_dp)
>                 intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED;
>  }
>
> +static bool intel_edp_is_psr_enabled(struct drm_device *dev)
> +{
> +       struct drm_i915_private *dev_priv = dev->dev_private;
> +
> +       if (!IS_HASWELL(dev))
> +               return false;
> +
> +       return I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE;
> +}
> +
> +void intel_edp_psr_write_vsc(struct intel_dp *intel_dp,
> +                            struct edp_vsc_psr *vsc_psr)

This function should be static (or included in a .h file).


> +{
> +       struct drm_device *dev = intel_dp_to_dev(intel_dp);

Function intel_dp_to_dev calls dp_to_dig_port, but you're already
calling dp_to_dig_port below. You could remove this intel_dp_to_dev
call by reordering the definitions.


> +       struct drm_i915_private *dev_priv = dev->dev_private;
> +       struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
> +       struct intel_crtc *crtc = to_intel_crtc(dig_port->base.base.crtc);
> +
> +       u32 ctl_reg = HSW_TVIDEO_DIP_CTL(crtc->config.cpu_transcoder);
> +       u32 data_reg = HSW_TVIDEO_DIP_VSC_DATA(crtc->config.cpu_transcoder);
> +       uint32_t *data = (uint32_t *) vsc_psr;
> +       unsigned int i;
> +       u32 val = I915_READ(ctl_reg);

We don't use this register for anything else on eDP, so instead of
preserving its contents with this read we should just set the bits we
want, zeroing everything else. We don't want to preserve bogus values.
Also, read below: if we move this code to mode_set time it will make
even more sense.


> +
> +       /* As per eDP spec, wait for vblank to send SDP VSC packet */
> +       intel_wait_for_vblank(dev, crtc->pipe);

The spec says the SDP VSC packets should be sent during the vblank,
but the HW does this automatically and intel_wait_for_vblank doesn't
guarantee us anything regarding that. So that wait seems useless.


> +
> +       /* As per BSPec (Pipe Video Data Island Packet), besides wait for
> +          vsync we need to disable the video DIP being updated before program
> +          video DIP data buffer registers for DIP being updated.*/

My interpretation of the spec is that we need to wait until exactly
after the VSync so we don't send incomplete packets, but we don't have
a good way to do this, and intel_wait_for_vblank doesn't help us with
that. If you take a look at the HDMI code you'll notice that we set
the video DIP registers inside intel_hdmi_mode_set, because at that
point the pipe is stopped and we don't need to worry about waiting for
the exact vblank period. Perhaps we should do the same here: load the
contents of the video dip registers at mode_set time? Is there any
problem in keeping those values there even when PSR or the pipe is
disabled?


> +       I915_WRITE(ctl_reg, val & ~VIDEO_DIP_ENABLE_VSC_HSW);
> +       POSTING_READ(ctl_reg);
> +
> +       for (i = 0; i < VIDEO_DIP_VSC_DATA_SIZE; i += 4) {
> +               if (i < sizeof(struct edp_vsc_psr))
> +                       I915_WRITE(data_reg + i, *data++);
> +               else
> +                       I915_WRITE(data_reg + i, 0);
> +       }
> +
> +       I915_WRITE(ctl_reg, val | VIDEO_DIP_ENABLE_VSC_HSW);

The PSR enabling documentation says that the "HW also enables VSC DIP
when required", so maybe we should not turn the
VIDEO_DIP_ENABLE_VSC_HSW bit and see if the HW does that for us? My
fear is that setting this unconditionally may make some panels
confused. But I'm not really sure if the HW really sets this bit for
us or just sends/stops-sending the DIP in case this bit is on.


> +       POSTING_READ(ctl_reg);
> +}
> +
> +static void intel_edp_psr_enable_sink(struct intel_dp *intel_dp)
> +{
> +       struct drm_device *dev = intel_dp_to_dev(intel_dp);
> +       struct drm_i915_private *dev_priv = dev->dev_private;
> +       struct edp_vsc_psr psr_vsc;
> +       uint32_t aux_clock_divider = get_aux_clock_divider(intel_dp);
> +       int precharge = 0x3;
> +       int msg_size = 5;       /* Header(4) + Message(1) */

If you implement our suggestion of replacing the magic 0x80060000
value with code this magic msg_size will also disappear.


> +
> +       /* 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);
> +
> +       /* Enable PSR in sink */
> +       if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT)
> +               intel_dp_aux_native_write_1(intel_dp, DP_PSR_EN_CFG,
> +                                           DP_PSR_ENABLE &
> +                                           ~DP_PSR_MAIN_LINK_ACTIVE);
> +       else
> +               intel_dp_aux_native_write_1(intel_dp, DP_PSR_EN_CFG,
> +                                           DP_PSR_ENABLE |
> +                                           DP_PSR_MAIN_LINK_ACTIVE);
> +
> +       /* Setup AUX registers */
> +       I915_WRITE(EDP_PSR_AUX_DATA1, EDP_PSR_DPCD_COMMAND);
> +       I915_WRITE(EDP_PSR_AUX_DATA2, EDP_PSR_DPCD_NORMAL_OPERATION);
> +       I915_WRITE(EDP_PSR_AUX_CTL,
> +                  DP_AUX_CH_CTL_TIME_OUT_400us |
> +                  (msg_size << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
> +                  (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
> +                  (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT));
> +}
> +
> +static void intel_edp_psr_enable_source(struct intel_dp *intel_dp)
> +{
> +       struct drm_device *dev = intel_dp_to_dev(intel_dp);
> +       struct drm_i915_private *dev_priv = dev->dev_private;
> +       uint32_t max_sleep_time = 0x1f;
> +       uint32_t idle_frames = 1;
> +       uint32_t val = 0x0;
> +
> +       if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT) {
> +               val |= EDP_PSR_LINK_STANDBY;
> +               val |= EDP_PSR_TP2_TP3_TIME_0us;
> +               val |= EDP_PSR_TP1_TIME_0us;
> +               val |= EDP_PSR_SKIP_AUX_EXIT;
> +       } else {
> +               val |= EDP_PSR_LINK_DISABLE;
> +               val |= EDP_PSR_TP1_TIME_500us;
> +               val |= EDP_PSR_TP2_TP3_TIME_500us;

Why are we using these 500us values? I couldn't find a place that
tells us which ones to use.


> +       }
> +
> +       /* Avoid continuous PSR exit by masking memup and hpd */
> +       I915_WRITE(EDP_PSR_DEBUG_CTL, EDP_PSR_DEBUG_MASK_MEMUP |
> +                  EDP_PSR_DEBUG_MASK_HPD);

Do we have a workaround name for the line above? Damien recently made
a nice effort to document all the WA names we implement. We should at
least say this is a WA.


> +
> +       /* Disable unused interrupts */
> +       I915_WRITE(GEN6_PMINTRMSK, GEN6_PM_RP_UP_EI_EXPIRED |
> +                  GEN6_PM_RP_DOWN_EI_EXPIRED);

The line above needs a very big comment explaining it. Also, I'm not
sure the code is correct. Don't we need irq_lock or rps_lock, also
adjust some dev_priv->xyz_iir variable? I'll leave the review of this
line to Ben and Daniel since they're currently touching these things.


> +
> +       I915_WRITE(EDP_PSR_CTL, val |
> +                  EDP_PSR_MIN_LINK_ENTRY_TIME_8_LINES |
> +                  max_sleep_time << EDP_PSR_MAX_SLEEP_TIME_SHIFT |
> +                  idle_frames << EDP_PSR_IDLE_FRAME_SHIFT |
> +                  EDP_PSR_ENABLE);
> +}
> +
> +void intel_edp_psr_enable(struct intel_dp *intel_dp)
> +{
> +       struct drm_device *dev = intel_dp_to_dev(intel_dp);
> +
> +       if (!is_edp_psr(intel_dp) || intel_edp_is_psr_enabled(dev))

One of the nice things about the modeset rework is that, for most of
the display code, we don't call "enable" twice of "disable" twice. On
a brief look at patch 11 it seems we do respect this, so how about you
turn this check for intel_edp_is_psr_enabled into a WARN?


> +               return;
> +
> +       /* Enable PSR on the panel */
> +       intel_edp_psr_enable_sink(intel_dp);
> +
> +       /* Enable PSR on the host */
> +       intel_edp_psr_enable_source(intel_dp);
> +}
> +
> +void intel_edp_psr_disable(struct intel_dp *intel_dp)
> +{
> +       struct drm_device *dev = intel_dp_to_dev(intel_dp);
> +       struct drm_i915_private *dev_priv = dev->dev_private;
> +       struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);

Same as above: you could avoid intel_dp_to_dev since you're already
calling dp_to_dig_port.


> +       struct intel_crtc *intel_crtc = to_intel_crtc(dig_port->base.base.crtc);
> +       uint32_t val;
> +
> +       if (!intel_edp_is_psr_enabled(dev))
> +               return;

Same as above: replace the check above with a WARN?


> +
> +       val = I915_READ(EDP_PSR_CTL);
> +       I915_WRITE(EDP_PSR_CTL, val & ~EDP_PSR_ENABLE);
> +
> +       /* Wait till PSR is idle */
> +       if (_wait_for((I915_READ(EDP_PSR_STATUS_CTL) &
> +                      EDP_PSR_STATUS_STATE_MASK) == 0, 2000, 10))
> +               DRM_ERROR("Timed out waiting for PSR Idle State\n");
> +
> +       intel_wait_for_vblank(dev, intel_crtc->pipe);

The spec says we need to wait for a vblank and then disable the VSC
DIP. As stated above, it is not clear whether the hardware does this
automatically or not. Also, do we need this at all? I imagine the spec
says we need to wait for a vblank just to avoid sending incomplete
DIPs, but on this case the intel_wait_for_vblank won't help us here.
Maybe we could fully just enable/disable the bit at mode_set time and
not worry about it later?


> +}
> +
>  static void intel_disable_dp(struct intel_encoder *encoder)
>  {
>         struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 9b264ee..ff09c4c 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -840,4 +840,7 @@ extern bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
>                                                  enum transcoder pch_transcoder,
>                                                  bool enable);
>
> +extern void intel_edp_psr_enable(struct intel_dp *intel_dp);
> +extern void intel_edp_psr_disable(struct intel_dp *intel_dp);
> +
>  #endif /* __INTEL_DRV_H__ */
> --
> 1.8.1.4
>
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx



-- 
Paulo Zanoni



More information about the Intel-gfx mailing list