| From 339b1baee30650c3ac63c2790c78960ba12bbfdc Mon Sep 17 00:00:00 2001 |
| From: Rodrigo Vivi <rodrigo.vivi@gmail.com> |
| Date: Thu, 11 Jul 2013 18:44:58 -0300 |
| Subject: drm/i915: Enable/Disable PSR |
| |
| 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). |
| v5: Changes according to Paulo's review: |
| static on write_vsc; |
| avoid using dp_to_dev when already calling dp_to_dig_port; |
| remove unecessary TP default time setting; |
| remove unecessary interrupts disabling; |
| remove unecessary wait_for_vblank when disabling psr; |
| v6: remove unecessary wait_for_vblank when writing vsc; |
| v7: adding setup once function to avoid unnecessarily write to vsc |
| and set debug_ctl every time we enable or disable psr. |
| |
| Cc: Paulo Zanoni <paulo.r.zanoni@intel.com> |
| Credits-by: Sateesh Kavuri <sateesh.kavuri@intel.com> |
| Credits-by: Shobhit Kumar <shobhit.kumar@intel.com> |
| Signed-off-by: Rodrigo Vivi <rodrigo.vivi@gmail.com> |
| Reviewed-by: Paulo Zanoni <paulo.r.zanoni@intel.com> |
| Reviewed-by: Shobhit Kumar <shobhit.kumar@intel.com> |
| [danvet: Apply Paulo's suggestion for unconditionally clearing the |
| control register when writing the DIP.] |
| Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> |
| |
| (cherry picked from commit 2b28bb1b6440fadececc4cf8f29c55d510c6db09) |
| Signed-off-by: Darren Hart <dvhart@linux.intel.com> |
| --- |
| drivers/gpu/drm/i915/i915_reg.h | 42 ++++++++++ |
| drivers/gpu/drm/i915/intel_dp.c | 149 +++++++++++++++++++++++++++++++++++++++ |
| drivers/gpu/drm/i915/intel_drv.h | 4 + |
| 3 files changed, 195 insertions(+) |
| |
| --- a/drivers/gpu/drm/i915/i915_reg.h |
| +++ b/drivers/gpu/drm/i915/i915_reg.h |
| @@ -1783,6 +1783,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) |
| +#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 |
| @@ -2062,6 +2103,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) |
| --- a/drivers/gpu/drm/i915/intel_dp.c |
| +++ b/drivers/gpu/drm/i915/intel_dp.c |
| @@ -1394,6 +1394,153 @@ static bool is_edp_psr(struct 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; |
| +} |
| + |
| +static void intel_edp_psr_write_vsc(struct intel_dp *intel_dp, |
| + struct edp_vsc_psr *vsc_psr) |
| +{ |
| + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); |
| + struct drm_device *dev = dig_port->base.base.dev; |
| + struct drm_i915_private *dev_priv = dev->dev_private; |
| + 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; |
| + |
| + /* As per BSPec (Pipe Video Data Island Packet), we need to disable |
| + the video DIP being updated before program video DIP data buffer |
| + registers for DIP being updated. */ |
| + I915_WRITE(ctl_reg, 0); |
| + 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, VIDEO_DIP_ENABLE_VSC_HSW); |
| + POSTING_READ(ctl_reg); |
| +} |
| + |
| +static void intel_edp_psr_setup(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; |
| + |
| + if (intel_dp->psr_setup_done) |
| + return; |
| + |
| + /* 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, EDP_PSR_DEBUG_MASK_MEMUP | |
| + EDP_PSR_DEBUG_MASK_HPD); |
| + |
| + intel_dp->psr_setup_done = true; |
| +} |
| + |
| +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; |
| + uint32_t aux_clock_divider = get_aux_clock_divider(intel_dp); |
| + int precharge = 0x3; |
| + int msg_size = 5; /* Header(4) + Message(1) */ |
| + |
| + /* 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; |
| + |
| + 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)) |
| + return; |
| + |
| + /* Setup PSR once */ |
| + intel_edp_psr_setup(intel_dp); |
| + |
| + /* 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; |
| + |
| + if (!intel_edp_is_psr_enabled(dev)) |
| + return; |
| + |
| + I915_WRITE(EDP_PSR_CTL, I915_READ(EDP_PSR_CTL) & ~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"); |
| +} |
| + |
| static void intel_disable_dp(struct intel_encoder *encoder) |
| { |
| struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); |
| @@ -3221,6 +3368,8 @@ intel_dp_init_connector(struct intel_dig |
| WARN(error, "intel_dp_i2c_init failed with error %d for port %c\n", |
| error, port_name(port)); |
| |
| + intel_dp->psr_setup_done = false; |
| + |
| if (!intel_edp_init_connector(intel_dp, intel_connector)) { |
| i2c_del_adapter(&intel_dp->adapter); |
| if (is_edp(intel_dp)) { |
| --- a/drivers/gpu/drm/i915/intel_drv.h |
| +++ b/drivers/gpu/drm/i915/intel_drv.h |
| @@ -499,6 +499,7 @@ struct intel_dp { |
| int backlight_off_delay; |
| struct delayed_work panel_vdd_work; |
| bool want_panel_vdd; |
| + bool psr_setup_done; |
| struct intel_connector *attached_connector; |
| }; |
| |
| @@ -834,4 +835,7 @@ extern bool intel_set_pch_fifo_underrun_ |
| 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__ */ |