On Mon, Sep 18, 2017 at 03:57:38PM +0200, Maarten Lankhorst wrote: > Op 18-09-17 om 15:32 schreef Vidya Srinivas: > > From: Uma Shankar <uma.shankar@xxxxxxxxx> > > > > For gen9 platforms, dsi timings are driven from port instead of pipe > > (unlike ddi). Thus, we can't rely on pipe registers to get the timing > > information. Even scanline register read will not be functional. > > This is causing vblank evasion logic to fail since it relies on > > scanline, causing atomic update failure warnings. > > > > This patch uses pipe framestamp and current timestamp registers > > to calculate scanline. This is an indirect way to get the scanline. > > It helps resolve atomic update failure for gen9 dsi platforms. > > > > v2: Addressed Ville and Daniel's review comments. Updated the > > register MACROs, handled race condition for register reads, > > extracted timings from the hwmode. Removed the dependency on > > crtc->config to get the encoder type. > > > > v3: Made get scanline function generic > > > > Credits-to: Ville Syrjälä <ville.syrjala@xxxxxxxxxxxxxxx> > > Signed-off-by: Uma Shankar <uma.shankar@xxxxxxxxx> > > Signed-off-by: Chandra Konduru <chandra.konduru@xxxxxxxxx> > > Signed-off-by: Vidya Srinivas <vidya.srinivas@xxxxxxxxx> > > --- > > drivers/gpu/drm/i915/i915_drv.h | 2 ++ > > drivers/gpu/drm/i915/i915_irq.c | 7 +++++ > > drivers/gpu/drm/i915/i915_reg.h | 11 +++++++ > > drivers/gpu/drm/i915/intel_display.c | 60 ++++++++++++++++++++++++++++++++++++ > > 4 files changed, 80 insertions(+) > > > > diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h > > index 1cc31a5..d9efe83 100644 > > --- a/drivers/gpu/drm/i915/i915_drv.h > > +++ b/drivers/gpu/drm/i915/i915_drv.h > > @@ -4085,6 +4085,8 @@ void intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value, > > u32 vlv_flisdsi_read(struct drm_i915_private *dev_priv, u32 reg); > > void vlv_flisdsi_write(struct drm_i915_private *dev_priv, u32 reg, u32 val); > > > > +u32 gen9_get_scanline(struct intel_crtc *crtc); > > + > > /* intel_dpio_phy.c */ > > void bxt_port_to_phy_channel(struct drm_i915_private *dev_priv, enum port port, > > enum dpio_phy *phy, enum dpio_channel *ch); > > diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c > > index 5d391e6..47668dd 100644 > > --- a/drivers/gpu/drm/i915/i915_irq.c > > +++ b/drivers/gpu/drm/i915/i915_irq.c > > @@ -781,6 +781,7 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc) > > struct drm_vblank_crtc *vblank; > > enum pipe pipe = crtc->pipe; > > int position, vtotal; > > + struct intel_encoder *encoder; > > > > if (!crtc->active) > > return -1; > > @@ -792,6 +793,12 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc) > > if (mode->flags & DRM_MODE_FLAG_INTERLACE) > > vtotal /= 2; > > > > + if (IS_BROXTON(dev_priv) || IS_GEMINILAKE(dev_priv)) { > > + for_each_encoder_on_crtc(crtc->base.dev, &crtc->base, encoder) > > + if (encoder->type == INTEL_OUTPUT_DSI) > > + return gen9_get_scanline(crtc); > I really think we shouldn't loop over all encoders for something as critical as __intel_get_crtc_scanline.. > > + } > > + > > if (IS_GEN2(dev_priv)) > > position = I915_READ_FW(PIPEDSL(pipe)) & DSL_LINEMASK_GEN2; > > else > > diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h > > index 0b03260..85168ee 100644 > > --- a/drivers/gpu/drm/i915/i915_reg.h > > +++ b/drivers/gpu/drm/i915/i915_reg.h > > @@ -8802,6 +8802,17 @@ 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_CTR _MMIO(MCHBAR_MIRROR_BASE + 0x2358) > > +#define GEN7_TIMESTAMP_CTR _MMIO(0x44070) > > + > > +#define _PIPE_FRMTMSTMP_A 0x70048 > > +#define _PIPE_FRMTMSTMP_B 0x71048 > > +#define _IVB_PIPE_FRMTMSTMP_C 0x72048 > > +#define PIPE_FRMTMSTMP(pipe) \ > > + _MMIO_PIPE3((pipe), _PIPE_FRMTMSTMP_A, \ > > + _PIPE_FRMTMSTMP_B, _IVB_PIPE_FRMTMSTMP_C) > > + > > /* BXT MIPI clock controls */ > > #define BXT_MAX_VAR_OUTPUT_KHZ 39500 > > > > diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c > > index 0871807..601032f 100644 > > --- a/drivers/gpu/drm/i915/intel_display.c > > +++ b/drivers/gpu/drm/i915/intel_display.c > > @@ -10352,6 +10352,66 @@ static bool needs_scaling(const struct intel_plane_state *state) > > return (src_w != dst_w || src_h != dst_h); > > } > > > > +/* > > + * For Gen9 DSI, pipe scanline register will not > > + * work to get the scanline since the timings > > + * are driven from the PORT (unlike DDI encoders). > > + * This function will use Framestamp and current > > + * timestamp registers to calculate the scanline. > > + */ > > +u32 gen9_get_scanline(struct intel_crtc *crtc) > > +{ > > + struct drm_device *dev = crtc->base.dev; > > + struct drm_i915_private *dev_priv = to_i915(dev); > > + u32 crtc_vblank_start = crtc->base.mode.crtc_vblank_start; > > + u32 crtc_vtotal = crtc->base.mode.crtc_vtotal; > > + u32 crtc_htotal = crtc->base.mode.crtc_htotal; > > + u32 crtc_clock = crtc->base.mode.crtc_clock; > > + u64 scanline = 0, scan_prev_time, scan_curr_time, scan_post_time; > Aren't all those 0 since they're only set for crtc_state->adjusted_mode, while crtc->mode is assigned to crtc_state->mode? You'd probably need to look at dev->vblank[crtc->pipe].hwmode for those, which should have been passed as parameter.. > With a bit of luck you could even use the derived ns values set in drm_calc_timestamping_constants, so you don't have to do the math here yourself. IIRC the derived values ended up having quite a bit of rounding error in them. Or maybe that just the pixel duration (which IIRC I nuked already). I would have nuked the line duration too if it wasn't for nouveau using it. So I think I'd rather not expand its use. Not sure we'd actually save anything by using the derived value anyway. We'll still have to do the expensive division at least. Hmm. I guess we could get rid of one multiplication. > > + WARN_ON(!crtc_vtotal); > > + if (!crtc_vtotal) > > + return scanline; > > + > > + /* 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(GEN7_TIMESTAMP_CTR); > > + > > + scan_post_time = I915_READ_FW(PIPE_FRMTMSTMP(crtc->pipe)); > > + } while (scan_post_time != scan_prev_time); > > + > > + /* > > + * Since the register is 32 bit and the values > > + * can overflow and wrap around, making sure > > + * current time accounts for the register > > + * wrap > > + */ > > + if (scan_curr_time < scan_prev_time) > > + scan_curr_time += 0x100000000; > > + > > + scanline = div_u64(mul_u64_u32_shr((scan_curr_time - scan_prev_time), > Isn't mul_u64_u32_div exactly what you want here? I think we'd actually want a mul_u32_u32_div(). But that doesn't seem to exist. > > + crtc_clock, 0), 1000 * crtc_htotal); > > + scanline = min(scanline, (u64)(crtc_vtotal - 1)); > > + scanline = (scanline + crtc_vblank_start) % crtc_vtotal; > > + > > + return scanline; > > +} > > + > > int intel_plane_atomic_calc_changes(const struct intel_crtc_state *old_crtc_state, > > struct drm_crtc_state *crtc_state, > > const struct intel_plane_state *old_plane_state, > > > _______________________________________________ > Intel-gfx mailing list > Intel-gfx@xxxxxxxxxxxxxxxxxxxxx > https://lists.freedesktop.org/mailman/listinfo/intel-gfx -- Ville Syrjälä Intel OTC _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx