diff options
-rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 54 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_reg.h | 9 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_drv.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_dsi.c | 8 |
4 files changed, 73 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 6a07ef38fb47..0b7562135d1c 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -783,6 +783,57 @@ static u32 g4x_get_vblank_counter(struct drm_device *dev, unsigned int pipe) return I915_READ(PIPE_FRMCOUNT_G4X(pipe)); } +/* + * On certain encoders on certain platforms, pipe + * scanline register will not work to get the scanline, + * since the timings are driven from the PORT or issues + * with scanline register updates. + * This function will use Framestamp and current + * timestamp registers to calculate the scanline. + */ +static u32 __intel_get_crtc_scanline_from_timestamp(struct intel_crtc *crtc) +{ + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + struct drm_vblank_crtc *vblank = + &crtc->base.dev->vblank[drm_crtc_index(&crtc->base)]; + const struct drm_display_mode *mode = &vblank->hwmode; + u32 vblank_start = mode->crtc_vblank_start; + u32 vtotal = mode->crtc_vtotal; + u32 htotal = mode->crtc_htotal; + u32 clock = mode->crtc_clock; + u32 scanline, scan_prev_time, scan_curr_time, scan_post_time; + + /* + * To avoid the race condition where we might cross into the + * next vblank just between the PIPE_FRMTMSTMP and TIMESTAMP_CTR + * reads. We make sure we read PIPE_FRMTMSTMP and TIMESTAMP_CTR + * during the same frame. + */ + do { + /* + * This field provides read back of the display + * pipe frame time stamp. The time stamp value + * is sampled at every start of vertical blank. + */ + scan_prev_time = I915_READ_FW(PIPE_FRMTMSTMP(crtc->pipe)); + + /* + * The TIMESTAMP_CTR register has the current + * time stamp value. + */ + scan_curr_time = I915_READ_FW(IVB_TIMESTAMP_CTR); + + scan_post_time = I915_READ_FW(PIPE_FRMTMSTMP(crtc->pipe)); + } while (scan_post_time != scan_prev_time); + + scanline = div_u64(mul_u32_u32(scan_curr_time - scan_prev_time, + clock), 1000 * htotal); + scanline = min(scanline, vtotal - 1); + scanline = (scanline + vblank_start) % vtotal; + + return scanline; +} + /* I915_READ_FW, only for fast reads of display block, no need for forcewake etc. */ static int __intel_get_crtc_scanline(struct intel_crtc *crtc) { @@ -799,6 +850,9 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc) vblank = &crtc->base.dev->vblank[drm_crtc_index(&crtc->base)]; mode = &vblank->hwmode; + if (mode->private_flags & I915_MODE_FLAG_GET_SCANLINE_FROM_TIMESTAMP) + return __intel_get_crtc_scanline_from_timestamp(crtc); + vtotal = mode->crtc_vtotal; if (mode->flags & DRM_MODE_FLAG_INTERLACE) vtotal /= 2; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 82f36dd0cd94..76fd8fda46d2 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -8808,6 +8808,15 @@ enum skl_power_gate { #define MIPIO_TXESC_CLK_DIV2 _MMIO(0x160008) #define GLK_TX_ESC_CLK_DIV2_MASK 0x3FF +/* Gen4+ Timestamp and Pipe Frame time stamp registers */ +#define GEN4_TIMESTAMP _MMIO(0x2358) +#define ILK_TIMESTAMP_HI _MMIO(0x70070) +#define IVB_TIMESTAMP_CTR _MMIO(0x44070) + +#define _PIPE_FRMTMSTMP_A 0x70048 +#define PIPE_FRMTMSTMP(pipe) \ + _MMIO_PIPE2(pipe, _PIPE_FRMTMSTMP_A) + /* BXT MIPI clock controls */ #define BXT_MAX_VAR_OUTPUT_KHZ 39500 diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 64358d2f2422..0cab667fff57 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -494,6 +494,8 @@ struct intel_crtc_scaler_state { /* drm_mode->private_flags */ #define I915_MODE_FLAG_INHERITED 1 +/* Flag to get scanline using frame time stamps */ +#define I915_MODE_FLAG_GET_SCANLINE_FROM_TIMESTAMP (1<<1) struct intel_pipe_wm { struct intel_wm_level wm[5]; diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c index 578254ac799c..20a7b004ffd7 100644 --- a/drivers/gpu/drm/i915/intel_dsi.c +++ b/drivers/gpu/drm/i915/intel_dsi.c @@ -330,6 +330,10 @@ static bool intel_dsi_compute_config(struct intel_encoder *encoder, adjusted_mode->flags = 0; if (IS_GEN9_LP(dev_priv)) { + /* Enable Frame time stamp based scanline reporting */ + adjusted_mode->private_flags |= + I915_MODE_FLAG_GET_SCANLINE_FROM_TIMESTAMP; + /* Dual link goes to DSI transcoder A. */ if (intel_dsi->ports == BIT(PORT_C)) pipe_config->cpu_transcoder = TRANSCODER_DSI_C; @@ -1102,6 +1106,10 @@ static void bxt_dsi_get_pipe_config(struct intel_encoder *encoder, pixel_format_from_register_bits(fmt)); bpp = pipe_config->pipe_bpp; + /* Enable Frame time stamo based scanline reporting */ + adjusted_mode->private_flags |= + I915_MODE_FLAG_GET_SCANLINE_FROM_TIMESTAMP; + /* In terms of pixels */ adjusted_mode->crtc_hdisplay = I915_READ(BXT_MIPI_TRANS_HACTIVE(port)); |