From: Shobhit Kumar shobhit.kumar@intel.com
SDP header and SDP VSC header as per eDP 1.3 spec, section 3.5, chapter "PSR Secondary Data Package Support".
v2: Modified and corrected the structures to be more in line for kernel coding guidelines and rebased the code on Paulo's DP patchset v3: removing unecessary identation at DP_RECEIVER_CAP_SIZE v4: moving them to include/drm/drm_dp_helper.h and also already icluding EDP_PSR_RECEIVER_CAP_SIZE to add everything needed for PSR at once at drm_dp_helper.h v5: Fix SDP VSC header and identation by (Paulo Zanoni) and remove i915 from title (Daniel Vetter) v6: Fix spec version and move comments from code to commit message since numbers might change in the future (by Paulo Zanoni).
CC: Paulo Zanoni paulo.r.zanoni@intel.com Reviewed-by: Paulo Zanoni paulo.r.zanoni@intel.com Signed-off-by: Sateesh Kavuri sateesh.kavuri@intel.com Signed-off-by: Shobhit Kumar shobhit.kumar@intel.com Signed-off-by: Rodrigo Vivi rodrigo.vivi@gmail.com --- include/drm/drm_dp_helper.h | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-)
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index e8e1417..ae8dbfb 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -342,13 +342,42 @@ u8 drm_dp_get_adjust_request_voltage(u8 link_status[DP_LINK_STATUS_SIZE], u8 drm_dp_get_adjust_request_pre_emphasis(u8 link_status[DP_LINK_STATUS_SIZE], int lane);
-#define DP_RECEIVER_CAP_SIZE 0xf +#define DP_RECEIVER_CAP_SIZE 0xf +#define EDP_PSR_RECEIVER_CAP_SIZE 2 + void drm_dp_link_train_clock_recovery_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]); void drm_dp_link_train_channel_eq_delay(u8 dpcd[DP_RECEIVER_CAP_SIZE]);
u8 drm_dp_link_rate_to_bw_code(int link_rate); int drm_dp_bw_code_to_link_rate(u8 link_bw);
+struct edp_sdp_header { + u8 HB0; /* Secondary Data Packet ID */ + u8 HB1; /* Secondary Data Packet Type */ + u8 HB2; /* 7:5 reserved, 4:0 revision number */ + u8 HB3; /* 7:5 reserved, 4:0 number of valid data bytes */ +} __packed; + +#define EDP_SDP_HEADER_REVISION_MASK 0x1F +#define EDP_SDP_HEADER_VALID_PAYLOAD_BYTES 0x1F + +struct edp_vsc_psr { + struct edp_sdp_header sdp_header; + u8 DB0; /* Stereo Interface */ + u8 DB1; /* 0 - PSR State; 1 - Update RFB; 2 - CRC Valid */ + u8 DB2; /* CRC value bits 7:0 of the R or Cr component */ + u8 DB3; /* CRC value bits 15:8 of the R or Cr component */ + u8 DB4; /* CRC value bits 7:0 of the G or Y component */ + u8 DB5; /* CRC value bits 15:8 of the G or Y component */ + u8 DB6; /* CRC value bits 7:0 of the B or Cb component */ + u8 DB7; /* CRC value bits 15:8 of the B or Cb component */ + u8 DB8_31[24]; /* Reserved */ +} __packed; + +#define EDP_VSC_PSR_STATE_ACTIVE (1<<0) +#define EDP_VSC_PSR_UPDATE_RFB (1<<1) +#define EDP_VSC_PSR_CRC_VALUES_VALID (1<<2) + static inline int drm_dp_max_link_rate(u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
From: Shobhit Kumar shobhit.kumar@intel.com
v2: reuse of just created is_edp_psr and put it at right place. v3: move is_edp_psr above intel_edp_disable
Reviewed-by: Paulo Zanoni paulo.r.zanoni@intel.com Reviewed-by: Jani Nikula jani.nikula@intel.com Signed-off-by: Shobhit Kumar shobhit.kumar@intel.com Signed-off-by: Rodrigo Vivi rodrigo.vivi@gmail.com --- drivers/gpu/drm/i915/intel_dp.c | 13 +++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 1 + 2 files changed, 14 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 24a44ed..709e9d9 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1342,6 +1342,12 @@ static void intel_dp_get_config(struct intel_encoder *encoder, pipe_config->adjusted_mode.flags |= flags; }
+static bool is_edp_psr(struct intel_dp *intel_dp) +{ + return (is_edp(intel_dp) && + (intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED)); +} + static void intel_disable_dp(struct intel_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); @@ -2255,6 +2261,13 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) if (intel_dp->dpcd[DP_DPCD_REV] == 0) return false; /* DPCD not present */
+ /* Check if the panel supports PSR */ + memset(intel_dp->psr_dpcd, 0, sizeof(intel_dp->psr_dpcd)); + intel_dp_aux_native_read_retry(intel_dp, DP_PSR_SUPPORT, + intel_dp->psr_dpcd, + sizeof(intel_dp->psr_dpcd)); + if (is_edp_psr(intel_dp)) + DRM_DEBUG_KMS("Detected EDP PSR Panel.\n"); if (!(intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT)) return true; /* native DP sink */ diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index c5b463c..9b264ee 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -486,6 +486,7 @@ struct intel_dp { uint8_t link_bw; uint8_t lane_count; uint8_t dpcd[DP_RECEIVER_CAP_SIZE]; + uint8_t psr_dpcd[EDP_PSR_RECEIVER_CAP_SIZE]; uint8_t downstream_ports[DP_MAX_DOWNSTREAM_PORTS]; struct i2c_adapter adapter; struct i2c_algo_dp_aux_data algo;
Prep patch for reuse aux_clock_divider with EDP_PSR_AUX_CTL setup.
Reviewed-by: Paulo Zanoni paulo.r.zanoni@intel.com Signed-off-by: Rodrigo Vivi rodrigo.vivi@gmail.com --- drivers/gpu/drm/i915/intel_dp.c | 58 +++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 25 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 709e9d9..5cd276b 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -271,29 +271,12 @@ intel_dp_aux_wait_done(struct intel_dp *intel_dp, bool has_aux_irq) return status; }
-static int -intel_dp_aux_ch(struct intel_dp *intel_dp, - uint8_t *send, int send_bytes, - uint8_t *recv, int recv_size) +static uint32_t get_aux_clock_divider(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; - uint32_t ch_ctl = intel_dp->aux_ch_ctl_reg; - uint32_t ch_data = ch_ctl + 4; - int i, ret, recv_bytes; - uint32_t status; - uint32_t aux_clock_divider; - int try, precharge; - bool has_aux_irq = INTEL_INFO(dev)->gen >= 5 && !IS_VALLEYVIEW(dev);
- /* dp aux is extremely sensitive to irq latency, hence request the - * lowest possible wakeup latency and so prevent the cpu from going into - * deep sleep states. - */ - pm_qos_update_request(&dev_priv->pm_qos, 0); - - intel_dp_check_edp(intel_dp); /* The clock divider is based off the hrawclk, * and would like to run at 2MHz. So, take the * hrawclk value and divide by 2 and use that @@ -302,23 +285,48 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, * clock divider. */ if (IS_VALLEYVIEW(dev)) { - aux_clock_divider = 100; + return 100; } else if (intel_dig_port->port == PORT_A) { if (HAS_DDI(dev)) - aux_clock_divider = DIV_ROUND_CLOSEST( + return DIV_ROUND_CLOSEST( intel_ddi_get_cdclk_freq(dev_priv), 2000); else if (IS_GEN6(dev) || IS_GEN7(dev)) - aux_clock_divider = 200; /* SNB & IVB eDP input clock at 400Mhz */ + return 200; /* SNB & IVB eDP input clock at 400Mhz */ else - aux_clock_divider = 225; /* eDP input clock at 450Mhz */ + return 225; /* eDP input clock at 450Mhz */ } else if (dev_priv->pch_id == INTEL_PCH_LPT_DEVICE_ID_TYPE) { /* Workaround for non-ULT HSW */ - aux_clock_divider = 74; + return 74; } else if (HAS_PCH_SPLIT(dev)) { - aux_clock_divider = DIV_ROUND_UP(intel_pch_rawclk(dev), 2); + return DIV_ROUND_UP(intel_pch_rawclk(dev), 2); } else { - aux_clock_divider = intel_hrawclk(dev) / 2; + return intel_hrawclk(dev) / 2; } +} + +static int +intel_dp_aux_ch(struct intel_dp *intel_dp, + uint8_t *send, int send_bytes, + uint8_t *recv, int recv_size) +{ + 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; + uint32_t ch_ctl = intel_dp->aux_ch_ctl_reg; + uint32_t ch_data = ch_ctl + 4; + int i, ret, recv_bytes; + uint32_t status; + uint32_t aux_clock_divider = get_aux_clock_divider(intel_dp); + int try, precharge; + bool has_aux_irq = INTEL_INFO(dev)->gen >= 5 && !IS_VALLEYVIEW(dev); + + /* dp aux is extremely sensitive to irq latency, hence request the + * lowest possible wakeup latency and so prevent the cpu from going into + * deep sleep states. + */ + pm_qos_update_request(&dev_priv->pm_qos, 0); + + intel_dp_check_edp(intel_dp);
if (IS_GEN6(dev)) precharge = 3;
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.
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 --- 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) +#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 5cd276b..144f216 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) +{ + 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); + 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); + + /* As per eDP spec, wait for vblank to send SDP VSC packet */ + intel_wait_for_vblank(dev, crtc->pipe); + + /* 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.*/ + 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); + 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) */ + + /* 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; + } + + /* 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); + + /* Disable unused interrupts */ + I915_WRITE(GEN6_PMINTRMSK, GEN6_PM_RP_UP_EI_EXPIRED | + GEN6_PM_RP_DOWN_EI_EXPIRED); + + 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; + + /* 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); + struct intel_crtc *intel_crtc = to_intel_crtc(dig_port->base.base.crtc); + uint32_t val; + + if (!intel_edp_is_psr_enabled(dev)) + return; + + 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); +} + 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..f86d9c5 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__ */
Adding support for PSR Status, PSR entry counter and performance counters. Heavily based on initial work from Shobhit.
v2: Fix PSR Status Link bits by Paulo Zanoni.
CC: Paulo Zanoni paulo.r.zanoni@intel.com Credits-by: Shobhit Kumar shobhit.kumar@intel.com Signed-off-by: Rodrigo Vivi rodrigo.vivi@gmail.com --- drivers/gpu/drm/i915/i915_debugfs.c | 90 +++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/i915_reg.h | 24 ++++++++++ 2 files changed, 114 insertions(+)
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index a188624..b81adf7 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1877,6 +1877,95 @@ static int i915_dpio_info(struct seq_file *m, void *data) return 0; }
+static int i915_edp_psr_status(struct seq_file *m, void *data) +{ + struct drm_info_node *node = m->private; + struct drm_device *dev = node->minor->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 psrctl, psrstat, psrperf; + + psrctl = I915_READ(EDP_PSR_CTL); + seq_printf(m, "PSR Enabled: %s\n", + yesno(psrctl & EDP_PSR_ENABLE)); + + psrstat = I915_READ(EDP_PSR_STATUS_CTL); + + seq_printf(m, "PSR Current State: "); + switch (psrstat & EDP_PSR_STATUS_STATE_MASK) { + case EDP_PSR_STATUS_STATE_IDLE: + seq_printf(m, "Reset state\n"); + break; + case EDP_PSR_STATUS_STATE_SRDONACK: + seq_printf(m, "Wait for TG/Stream to send on frame of data after SRD conditions are met\n"); + break; + case EDP_PSR_STATUS_STATE_SRDENT: + seq_printf(m, "SRD entry\n"); + break; + case EDP_PSR_STATUS_STATE_BUFOFF: + seq_printf(m, "Wait for buffer turn off\n"); + break; + case EDP_PSR_STATUS_STATE_BUFON: + seq_printf(m, "Wait for buffer turn on\n"); + break; + case EDP_PSR_STATUS_STATE_AUXACK: + seq_printf(m, "Wait for AUX to acknowledge on SRD exit\n"); + break; + case EDP_PSR_STATUS_STATE_SRDOFFACK: + seq_printf(m, "Wait for TG/Stream to acknowledge the SRD VDM exit\n"); + break; + default: + seq_printf(m, "Unknown\n"); + break; + } + + seq_printf(m, "Link Status: "); + switch (psrstat & EDP_PSR_STATUS_LINK_MASK) { + case EDP_PSR_STATUS_LINK_FULL_OFF: + seq_printf(m, "Link is fully off\n"); + break; + case EDP_PSR_STATUS_LINK_FULL_ON: + seq_printf(m, "Link is fully on\n"); + break; + case EDP_PSR_STATUS_LINK_STANDBY: + seq_printf(m, "Link is in standby\n"); + break; + default: + seq_printf(m, "Unknown\n"); + break; + } + + seq_printf(m, "PSR Entry Count: %u\n", + psrstat >> EDP_PSR_STATUS_COUNT_SHIFT & + EDP_PSR_STATUS_COUNT_MASK); + + seq_printf(m, "Max Sleep Timer Counter: %u\n", + psrstat >> EDP_PSR_STATUS_MAX_SLEEP_TIMER_SHIFT & + EDP_PSR_STATUS_MAX_SLEEP_TIMER_MASK); + + seq_printf(m, "Had AUX error: %s\n", + yesno(psrstat & EDP_PSR_STATUS_AUX_ERROR)); + + seq_printf(m, "Sending AUX: %s\n", + yesno(psrstat & EDP_PSR_STATUS_AUX_SENDING)); + + seq_printf(m, "Sending Idle: %s\n", + yesno(psrstat & EDP_PSR_STATUS_SENDING_IDLE)); + + seq_printf(m, "Sending TP2 TP3: %s\n", + yesno(psrstat & EDP_PSR_STATUS_SENDING_TP2_TP3)); + + seq_printf(m, "Sending TP1: %s\n", + yesno(psrstat & EDP_PSR_STATUS_SENDING_TP1)); + + seq_printf(m, "Idle Count: %u\n", + psrstat & EDP_PSR_STATUS_IDLE_MASK); + + psrperf = (I915_READ(EDP_PSR_PERF_CNT)) & EDP_PSR_PERF_CNT_MASK; + seq_printf(m, "Performance Counter: %u\n", psrperf); + + return 0; +} + static int i915_wedged_get(void *data, u64 *val) { @@ -2306,6 +2395,7 @@ static struct drm_info_list i915_debugfs_list[] = { {"i915_swizzle_info", i915_swizzle_info, 0}, {"i915_ppgtt_info", i915_ppgtt_info, 0}, {"i915_dpio", i915_dpio_info, 0}, + {"i915_edp_psr_status", i915_edp_psr_status, 0}, }; #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index caf57d8..ab5d597 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1812,6 +1812,30 @@
#define EDP_PSR_STATUS_CTL 0x64840 #define EDP_PSR_STATUS_STATE_MASK (7<<29) +#define EDP_PSR_STATUS_STATE_IDLE (0<<29) +#define EDP_PSR_STATUS_STATE_SRDONACK (1<<29) +#define EDP_PSR_STATUS_STATE_SRDENT (2<<29) +#define EDP_PSR_STATUS_STATE_BUFOFF (3<<29) +#define EDP_PSR_STATUS_STATE_BUFON (4<<29) +#define EDP_PSR_STATUS_STATE_AUXACK (5<<29) +#define EDP_PSR_STATUS_STATE_SRDOFFACK (6<<29) +#define EDP_PSR_STATUS_LINK_MASK (3<<26) +#define EDP_PSR_STATUS_LINK_FULL_OFF (0<<26) +#define EDP_PSR_STATUS_LINK_FULL_ON (1<<26) +#define EDP_PSR_STATUS_LINK_STANDBY (2<<26) +#define EDP_PSR_STATUS_MAX_SLEEP_TIMER_SHIFT 20 +#define EDP_PSR_STATUS_MAX_SLEEP_TIMER_MASK 0x1f +#define EDP_PSR_STATUS_COUNT_SHIFT 16 +#define EDP_PSR_STATUS_COUNT_MASK 0xf +#define EDP_PSR_STATUS_AUX_ERROR (1<<15) +#define EDP_PSR_STATUS_AUX_SENDING (1<<12) +#define EDP_PSR_STATUS_SENDING_IDLE (1<<9) +#define EDP_PSR_STATUS_SENDING_TP2_TP3 (1<<8) +#define EDP_PSR_STATUS_SENDING_TP1 (1<<4) +#define EDP_PSR_STATUS_IDLE_MASK 0xf + +#define EDP_PSR_PERF_CNT 0x64844 +#define EDP_PSR_PERF_CNT_MASK 0xffffff
#define EDP_PSR_DEBUG_CTL 0x64860 #define EDP_PSR_DEBUG_MASK_LPSP (1<<27)
Signed-off-by: Rodrigo Vivi rodrigo.vivi@gmail.com --- drivers/gpu/drm/i915/i915_debugfs.c | 39 ++++++++++++++++++--- drivers/gpu/drm/i915/i915_drv.h | 12 +++++++ drivers/gpu/drm/i915/intel_dp.c | 68 ++++++++++++++++++++++++++++++++++++- 3 files changed, 114 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index b81adf7..85b7dd2 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1882,11 +1882,42 @@ static int i915_edp_psr_status(struct seq_file *m, void *data) struct drm_info_node *node = m->private; struct drm_device *dev = node->minor->dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 psrctl, psrstat, psrperf; + u32 psrstat, psrperf;
- psrctl = I915_READ(EDP_PSR_CTL); - seq_printf(m, "PSR Enabled: %s\n", - yesno(psrctl & EDP_PSR_ENABLE)); + if (I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE) { + seq_printf(m, "PSR enabled\n"); + } else { + seq_printf(m, "PSR disabled: "); + switch (dev_priv->no_psr_reason) { + case PSR_NO_SOURCE: + seq_printf(m, "not supported on this platform"); + break; + case PSR_NO_SINK: + seq_printf(m, "not supported by panel"); + break; + case PSR_CRTC_NOT_ACTIVE: + seq_printf(m, "crtc not active"); + break; + case PSR_PWR_WELL_ENABLED: + seq_printf(m, "power well enabled"); + break; + case PSR_NOT_TILED: + seq_printf(m, "not tiled"); + break; + case PSR_SPRITE_ENABLED: + seq_printf(m, "sprite enabled"); + break; + case PSR_INTERLACED_ENABLED: + seq_printf(m, "interlaced enabled"); + break; + case PSR_HSW_NOT_DDIA: + seq_printf(m, "HSW ties PSR to DDI A (eDP)"); + break; + default: + seq_printf(m, "unknown reason"); + } + seq_printf(m, "\n"); + }
psrstat = I915_READ(EDP_PSR_STATUS_CTL);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 56bd82b..f08c1d9 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -543,6 +543,17 @@ enum no_fbc_reason { FBC_CHIP_DEFAULT, /* disabled by default on this chip */ };
+enum no_psr_reason { + PSR_NO_SOURCE, /* Not supported on platform */ + PSR_NO_SINK, /* Not supported by panel */ + PSR_CRTC_NOT_ACTIVE, + PSR_PWR_WELL_ENABLED, + PSR_NOT_TILED, + PSR_SPRITE_ENABLED, + PSR_INTERLACED_ENABLED, + PSR_HSW_NOT_DDIA, +}; + enum intel_pch { PCH_NONE = 0, /* No PCH present */ PCH_IBX, /* Ibexpeak PCH */ @@ -1146,6 +1157,7 @@ typedef struct drm_i915_private { struct i915_power_well power_well;
enum no_fbc_reason no_fbc_reason; + enum no_psr_reason no_psr_reason;
struct drm_mm_node *compressed_fb; struct drm_mm_node *compressed_llb; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 144f216..13b0155 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1471,11 +1471,77 @@ static void intel_edp_psr_enable_source(struct intel_dp *intel_dp) EDP_PSR_ENABLE); }
+static bool intel_edp_psr_match_conditions(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); + struct drm_crtc *crtc = dig_port->base.base.crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct drm_i915_gem_object *obj = to_intel_framebuffer(crtc->fb)->obj; + struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base; + + if (!IS_HASWELL(dev)) { + DRM_DEBUG_KMS("PSR not supported on this platform\n"); + dev_priv->no_psr_reason = PSR_NO_SOURCE; + return false; + } + + if ((intel_encoder->type != INTEL_OUTPUT_EDP) || + (enc_to_dig_port(&intel_encoder->base)->port != PORT_A)) { + DRM_DEBUG_KMS("HSW ties PSR to DDI A (eDP)\n"); + dev_priv->no_psr_reason = PSR_HSW_NOT_DDIA; + return false; + } + + if (!is_edp_psr(intel_dp)) { + DRM_DEBUG_KMS("PSR not supported by this panel\n"); + dev_priv->no_psr_reason = PSR_NO_SINK; + return false; + } + + if (!intel_crtc->active || !crtc->fb || !crtc->mode.clock) { + DRM_DEBUG_KMS("crtc not active for PSR\n"); + dev_priv->no_psr_reason = PSR_CRTC_NOT_ACTIVE; + return false; + } + + if ((I915_READ(HSW_PWR_WELL_DRIVER) & HSW_PWR_WELL_ENABLE) | + (I915_READ(HSW_PWR_WELL_KVMR) & HSW_PWR_WELL_ENABLE)) { + DRM_DEBUG_KMS("PSR condition failed: Power Well is Enabled\n"); + dev_priv->no_psr_reason = PSR_PWR_WELL_ENABLED; + return false; + } + + if (obj->tiling_mode != I915_TILING_X || + obj->fence_reg == I915_FENCE_REG_NONE) { + DRM_DEBUG_KMS("PSR condition failed: fb not tiled or fenced\n"); + dev_priv->no_psr_reason = PSR_NOT_TILED; + return false; + } + + if (I915_READ(SPRCTL(intel_crtc->pipe)) & SPRITE_ENABLE) { + DRM_DEBUG_KMS("PSR condition failed: Sprite is Enabled\n"); + dev_priv->no_psr_reason = PSR_SPRITE_ENABLED; + return false; + } + + if ((I915_READ(PIPECONF(intel_crtc->config.cpu_transcoder)) & + PIPECONF_INTERLACE_MASK) == PIPECONF_INTERLACED_ILK) { + DRM_DEBUG_KMS("PSR condition failed: Interlaced is Enabled\n"); + dev_priv->no_psr_reason = PSR_INTERLACED_ENABLED; + return false; + } + + return true; +} + 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)) + if (!intel_edp_psr_match_conditions(intel_dp) || + intel_edp_is_psr_enabled(dev)) return;
/* Enable PSR on the panel */
Required function to disable PSR when going to console mode. But also can be used whenever PSR mode entry conditions changed.
v2: Add it before PSR Hook. Update function not really been called yet. --- drivers/gpu/drm/i915/intel_dp.c | 37 ++++++++++++++++++++++++++++++------- drivers/gpu/drm/i915/intel_drv.h | 1 + 2 files changed, 31 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 13b0155..91ba018 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1536,14 +1536,8 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp) return true; }
-void intel_edp_psr_enable(struct intel_dp* intel_dp) +void intel_edp_psr_do_enable(struct intel_dp* intel_dp) { - struct drm_device *dev = intel_dp_to_dev(intel_dp); - - if (!intel_edp_psr_match_conditions(intel_dp) || - intel_edp_is_psr_enabled(dev)) - return; - /* Enable PSR on the panel */ intel_edp_psr_enable_sink(intel_dp);
@@ -1551,6 +1545,15 @@ void intel_edp_psr_enable(struct intel_dp* intel_dp) intel_edp_psr_enable_source(intel_dp); }
+void intel_edp_psr_enable(struct intel_dp* intel_dp) +{ + struct drm_device *dev = intel_dp_to_dev(intel_dp); + + if (intel_edp_psr_match_conditions(intel_dp) && + !intel_edp_is_psr_enabled(dev)) + intel_edp_psr_do_enable(intel_dp); +} + void intel_edp_psr_disable(struct intel_dp* intel_dp) { struct drm_device *dev = intel_dp_to_dev(intel_dp); @@ -1573,6 +1576,26 @@ void intel_edp_psr_disable(struct intel_dp* intel_dp) intel_wait_for_vblank(dev, intel_crtc->pipe); }
+void intel_edp_psr_update(struct drm_device *dev) +{ + struct intel_encoder *encoder; + struct intel_dp *intel_dp = NULL; + + 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(intel_dp)) + return; + + if (!intel_edp_psr_match_conditions(intel_dp)) + intel_edp_psr_disable(intel_dp); + else + if (!intel_edp_is_psr_enabled(dev)) + intel_edp_psr_do_enable(intel_dp); + } +} + 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 f86d9c5..69224d2 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -842,5 +842,6 @@ extern bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
extern void intel_edp_psr_enable(struct intel_dp* intel_dp); extern void intel_edp_psr_disable(struct intel_dp* intel_dp); +extern void intel_edp_psr_update(struct drm_device *dev);
#endif /* __INTEL_DRV_H__ */
PSR is enabled by default but can be disabled.
Signed-off-by: Rodrigo Vivi rodrigo.vivi@gmail.com --- drivers/gpu/drm/i915/i915_debugfs.c | 3 +++ drivers/gpu/drm/i915/i915_drv.c | 4 ++++ drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/intel_dp.c | 6 ++++++ 4 files changed, 15 insertions(+)
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 85b7dd2..8b235c5 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1895,6 +1895,9 @@ static int i915_edp_psr_status(struct seq_file *m, void *data) case PSR_NO_SINK: seq_printf(m, "not supported by panel"); break; + case PSR_MODULE_PARAM: + seq_printf(m, "disabled by flag"); + break; case PSR_CRTC_NOT_ACTIVE: seq_printf(m, "crtc not active"); break; diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 062cbda..7920c6f 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -118,6 +118,10 @@ module_param_named(i915_enable_ppgtt, i915_enable_ppgtt, int, 0600); MODULE_PARM_DESC(i915_enable_ppgtt, "Enable PPGTT (default: true)");
+int i915_enable_psr __read_mostly = 1; +module_param_named(enable_psr, i915_enable_psr, int, 0600); +MODULE_PARM_DESC(enable_psr, "Enable PSR (default: true)"); + unsigned int i915_preliminary_hw_support __read_mostly = 0; module_param_named(preliminary_hw_support, i915_preliminary_hw_support, int, 0600); MODULE_PARM_DESC(preliminary_hw_support, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index f08c1d9..dda992a 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -546,6 +546,7 @@ enum no_fbc_reason { enum no_psr_reason { PSR_NO_SOURCE, /* Not supported on platform */ PSR_NO_SINK, /* Not supported by panel */ + PSR_MODULE_PARAM, PSR_CRTC_NOT_ACTIVE, PSR_PWR_WELL_ENABLED, PSR_NOT_TILED, @@ -1555,6 +1556,7 @@ extern int i915_enable_rc6 __read_mostly; extern int i915_enable_fbc __read_mostly; extern bool i915_enable_hangcheck __read_mostly; extern int i915_enable_ppgtt __read_mostly; +extern int i915_enable_psr __read_mostly; extern unsigned int i915_preliminary_hw_support __read_mostly; extern int i915_disable_power_well __read_mostly; extern int i915_enable_ips __read_mostly; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 91ba018..91d3bd6 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1500,6 +1500,12 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp) return false; }
+ if (!i915_enable_psr) { + DRM_DEBUG_KMS("PSR disable by flag\n"); + dev_priv->no_psr_reason = PSR_MODULE_PARAM; + return false; + } + if (!intel_crtc->active || !crtc->fb || !crtc->mode.clock) { DRM_DEBUG_KMS("crtc not active for PSR\n"); dev_priv->no_psr_reason = PSR_CRTC_NOT_ACTIVE;
This global value allows userspace know when PSR is enabled and active, i.e. in SRD entry state. This will allow userspace emit more busy_ioctl when doing directly copy_area operations through scanout allowing forced psr exit.
Signed-off-by: Rodrigo Vivi rodrigo.vivi@gmail.com --- drivers/gpu/drm/i915/i915_dma.c | 3 +++ include/uapi/drm/i915_drm.h | 1 + 2 files changed, 4 insertions(+)
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index adb319b..de20c4a 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1000,6 +1000,9 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_HAS_EXEC_HANDLE_LUT: value = 1; break; + case I915_PARAM_PSR_ACTIVE: + value = I915_READ(EDP_PSR_STATUS_CTL) & EDP_PSR_STATUS_STATE_SRDENT; + break; default: DRM_DEBUG("Unknown parameter %d\n", param->param); return -EINVAL; diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 923ed7f..191a7fc 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -310,6 +310,7 @@ typedef struct drm_i915_irq_wait { #define I915_PARAM_HAS_PINNED_BATCHES 24 #define I915_PARAM_HAS_EXEC_NO_RELOC 25 #define I915_PARAM_HAS_EXEC_HANDLE_LUT 26 +#define I915_PARAM_PSR_ACTIVE 27
typedef struct drm_i915_getparam { int param;
On Wed, Jun 26, 2013 at 06:55:20PM -0300, Rodrigo Vivi wrote:
I don't want PSR_ACTIVE, but PSR_ENABLED. I'd rather check once after updating the mode configuration instead of everytime the ddx is about to sleep. -Chris
PSR tracking engine in HSW doesn't detect automagically some directly copy area operations through scanout so we will have to kick it manually and reschedule it to come back to normal operation as soon as possible.
v2: Before PSR Hook. Don't force it when busy yet.
Signed-off-by: Rodrigo Vivi rodrigo.vivi@gmail.com --- drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_dp.c | 59 ++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 4 +++ 3 files changed, 64 insertions(+)
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index ab5d597..cea646b 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1838,6 +1838,7 @@ #define EDP_PSR_PERF_CNT_MASK 0xffffff
#define EDP_PSR_DEBUG_CTL 0x64860 +#define EDP_PSR_DEBUG_FORCE_EXIT (3<<30) #define EDP_PSR_DEBUG_MASK_LPSP (1<<27) #define EDP_PSR_DEBUG_MASK_MEMUP (1<<26) #define EDP_PSR_DEBUG_MASK_HPD (1<<25) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 91d3bd6..9986484 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1366,6 +1366,48 @@ static bool intel_edp_is_psr_enabled(struct drm_device *dev) return I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE; }
+static void intel_edp_psr_delayed_normal_work(struct work_struct *__work) +{ + struct intel_dp *intel_dp = container_of(to_delayed_work(__work), + struct intel_dp, + edp_psr_delayed_normal_work); + struct drm_device *dev = intel_dp_to_dev(intel_dp); + struct drm_i915_private *dev_priv = dev->dev_private; + + mutex_lock(&intel_dp->psr_exit_mutex); + I915_WRITE(EDP_PSR_DEBUG_CTL, I915_READ(EDP_PSR_DEBUG_CTL) & + ~EDP_PSR_DEBUG_FORCE_EXIT); + mutex_unlock(&intel_dp->psr_exit_mutex); +} + +void intel_edp_psr_force_exit(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_encoder *encoder; + struct intel_dp *intel_dp = NULL; + + if (!intel_edp_is_psr_enabled(dev)) + return; + + 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 (!intel_dp) + return; + + if (WARN_ON(!intel_dp->psr_setup_done)) + return; + + mutex_lock(&intel_dp->psr_exit_mutex); + I915_WRITE(EDP_PSR_DEBUG_CTL, I915_READ(EDP_PSR_DEBUG_CTL) | + EDP_PSR_DEBUG_FORCE_EXIT); + mutex_unlock(&intel_dp->psr_exit_mutex); + + schedule_delayed_work(&intel_dp->edp_psr_delayed_normal_work, + msecs_to_jiffies(100)); +} + void intel_edp_psr_write_vsc(struct intel_dp* intel_dp, struct edp_vsc_psr *vsc_psr) { @@ -1400,6 +1442,18 @@ void intel_edp_psr_write_vsc(struct intel_dp* intel_dp, POSTING_READ(ctl_reg); }
+static void intel_edp_psr_setup(struct intel_dp *intel_dp) +{ + if (intel_dp->psr_setup_done) + return; + + INIT_DELAYED_WORK(&intel_dp->edp_psr_delayed_normal_work, + intel_edp_psr_delayed_normal_work); + mutex_init(&intel_dp->psr_exit_mutex); + + 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); @@ -1544,6 +1598,9 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp)
void intel_edp_psr_do_enable(struct intel_dp* intel_dp) { + /* Setup PSR once */ + intel_edp_psr_setup(intel_dp); + /* Enable PSR on the panel */ intel_edp_psr_enable_sink(intel_dp);
@@ -3413,6 +3470,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port, 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)) { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 69224d2..b3d5a97 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -497,6 +497,9 @@ struct intel_dp { int backlight_on_delay; int backlight_off_delay; struct delayed_work panel_vdd_work; + struct delayed_work edp_psr_delayed_normal_work; + struct mutex psr_exit_mutex; + bool psr_setup_done; bool want_panel_vdd; struct intel_connector *attached_connector; }; @@ -843,5 +846,6 @@ extern bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev, extern void intel_edp_psr_enable(struct intel_dp* intel_dp); extern void intel_edp_psr_disable(struct intel_dp* intel_dp); extern void intel_edp_psr_update(struct drm_device *dev); +extern void intel_edp_psr_force_exit(struct drm_device *dev);
#endif /* __INTEL_DRV_H__ */
PSR must be enabled after transcoder and port are running. And it is only available for HSW.
v2: move enable/disable to intel_ddi v3: The spec suggests PSR should be disabled even before backlight (by pzanoni) v4: also disabling and enabling whenever panel is disabled/enabled. v5: make it last patch to avoid breaking whenever bisecting. So calling for update and force exit came to this patch along with enable/disable calls.
CC: Paulo Zanoni paulo.r.zanoni@intel.com Signed-off-by: Rodrigo Vivi rodrigo.vivi@gmail.com --- drivers/gpu/drm/i915/i915_gem.c | 2 ++ drivers/gpu/drm/i915/intel_ddi.c | 2 ++ drivers/gpu/drm/i915/intel_display.c | 1 + drivers/gpu/drm/i915/intel_dp.c | 2 ++ 4 files changed, 7 insertions(+)
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index c68b90f..f2e135b 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3786,6 +3786,8 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data, goto unlock; }
+ intel_edp_psr_force_exit(dev); + /* Count all active objects as busy, even if they are currently not used * by the gpu. Users of this interface expect objects to eventually * become non-busy without any further actions, therefore emit any diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 324211a..4211925 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1117,6 +1117,7 @@ static void intel_enable_ddi(struct intel_encoder *intel_encoder) intel_dp_stop_link_train(intel_dp);
ironlake_edp_backlight_on(intel_dp); + intel_edp_psr_enable(intel_dp); }
if (intel_crtc->eld_vld && type != INTEL_OUTPUT_EDP) { @@ -1147,6 +1148,7 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder) if (type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+ intel_edp_psr_disable(intel_dp); ironlake_edp_backlight_off(intel_dp); } } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6fafa43..5bbfed0 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2236,6 +2236,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, }
intel_update_fbc(dev); + intel_edp_psr_update(dev); mutex_unlock(&dev->struct_mutex);
intel_crtc_update_sarea_pos(crtc, x, y); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 9986484..019b1ff 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1668,6 +1668,7 @@ static void intel_disable_dp(struct intel_encoder *encoder) /* 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. */ ironlake_edp_panel_vdd_on(intel_dp); + intel_edp_psr_disable(intel_dp); ironlake_edp_backlight_off(intel_dp); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); ironlake_edp_panel_off(intel_dp); @@ -1708,6 +1709,7 @@ static void intel_enable_dp(struct intel_encoder *encoder) intel_dp_complete_link_train(intel_dp); intel_dp_stop_link_train(intel_dp); ironlake_edp_backlight_on(intel_dp); + intel_edp_psr_enable(intel_dp);
if (IS_VALLEYVIEW(dev)) { struct intel_digital_port *dport =
2013/6/26 Rodrigo Vivi rodrigo.vivi@gmail.com:
Functions intel_enable_dp and intel_disable_dp are not used on Haswell, so I guess we don't need those calls, right?
dri-devel@lists.freedesktop.org