// SPDX-License-Identifier: MIT /* * Copyright © 2022-2023 Intel Corporation */ #include "i915_drv.h" #include "i915_reg.h" #include "intel_color.h" #include "intel_crtc.h" #include "intel_de.h" #include "intel_display_types.h" #include "intel_vblank.h" #include "intel_vrr.h" /* * This timing diagram depicts the video signal in and * around the vertical blanking period. * * Assumptions about the fictitious mode used in this example: * vblank_start >= 3 * vsync_start = vblank_start + 1 * vsync_end = vblank_start + 2 * vtotal = vblank_start + 3 * * start of vblank: * latch double buffered registers * increment frame counter (ctg+) * generate start of vblank interrupt (gen4+) * | * | frame start: * | generate frame start interrupt (aka. vblank interrupt) (gmch) * | may be shifted forward 1-3 extra lines via TRANSCONF * | | * | | start of vsync: * | | generate vsync interrupt * | | | * ___xxxx___ ___xxxx___ ___xxxx___ ___xxxx___ ___xxxx___ ___xxxx * . \hs/ . \hs/ \hs/ \hs/ . \hs/ * ----va---> <-----------------vb--------------------> <--------va------------- * | | <----vs-----> | * -vbs-----> <---vbs+1---> <---vbs+2---> <-----0-----> <-----1-----> <-----2--- (scanline counter gen2) * -vbs-2---> <---vbs-1---> <---vbs-----> <---vbs+1---> <---vbs+2---> <-----0--- (scanline counter gen3+) * -vbs-2---> <---vbs-2---> <---vbs-1---> <---vbs-----> <---vbs+1---> <---vbs+2- (scanline counter hsw+ hdmi) * | | | * last visible pixel first visible pixel * | increment frame counter (gen3/4) * pixel counter = vblank_start * htotal pixel counter = 0 (gen3/4) * * x = horizontal active * _ = horizontal blanking * hs = horizontal sync * va = vertical active * vb = vertical blanking * vs = vertical sync * vbs = vblank_start (number) * * Summary: * - most events happen at the start of horizontal sync * - frame start happens at the start of horizontal blank, 1-4 lines * (depending on TRANSCONF settings) after the start of vblank * - gen3/4 pixel and frame counter are synchronized with the start * of horizontal active on the first line of vertical active */ /* * Called from drm generic code, passed a 'crtc', which we use as a pipe index. */ u32 i915_get_vblank_counter(struct drm_crtc *crtc) { … } u32 g4x_get_vblank_counter(struct drm_crtc *crtc) { … } static u32 intel_crtc_scanlines_since_frame_timestamp(struct intel_crtc *crtc) { … } /* * 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) { … } int intel_crtc_scanline_offset(const struct intel_crtc_state *crtc_state) { … } /* * intel_de_read_fw(), only for fast reads of display block, no need for * forcewake etc. */ static int __intel_get_crtc_scanline(struct intel_crtc *crtc) { … } /* * The uncore version of the spin lock functions is used to decide * whether we need to lock the uncore lock or not. This is only * needed in i915, not in Xe. * * This lock in i915 is needed because some old platforms (at least * IVB and possibly HSW as well), which are not supported in Xe, need * all register accesses to the same cacheline to be serialized, * otherwise they may hang. */ #ifdef I915 static void intel_vblank_section_enter(struct intel_display *display) __acquires(i915->uncore.lock) { … } static void intel_vblank_section_exit(struct intel_display *display) __releases(i915->uncore.lock) { … } #else static void intel_vblank_section_enter(struct intel_display *display) { } static void intel_vblank_section_exit(struct intel_display *display) { } #endif static bool i915_get_crtc_scanoutpos(struct drm_crtc *_crtc, bool in_vblank_irq, int *vpos, int *hpos, ktime_t *stime, ktime_t *etime, const struct drm_display_mode *mode) { … } bool intel_crtc_get_vblank_timestamp(struct drm_crtc *crtc, int *max_error, ktime_t *vblank_time, bool in_vblank_irq) { … } int intel_get_crtc_scanline(struct intel_crtc *crtc) { … } static bool pipe_scanline_is_moving(struct intel_display *display, enum pipe pipe) { … } static void wait_for_pipe_scanline_moving(struct intel_crtc *crtc, bool state) { … } void intel_wait_for_pipe_scanline_stopped(struct intel_crtc *crtc) { … } void intel_wait_for_pipe_scanline_moving(struct intel_crtc *crtc) { … } void intel_crtc_update_active_timings(const struct intel_crtc_state *crtc_state, bool vrr_enable) { … } int intel_mode_vdisplay(const struct drm_display_mode *mode) { … } int intel_mode_vblank_start(const struct drm_display_mode *mode) { … } int intel_mode_vblank_end(const struct drm_display_mode *mode) { … } int intel_mode_vtotal(const struct drm_display_mode *mode) { … } void intel_vblank_evade_init(const struct intel_crtc_state *old_crtc_state, const struct intel_crtc_state *new_crtc_state, struct intel_vblank_evade_ctx *evade) { … } /* must be called with vblank interrupt already enabled! */ int intel_vblank_evade(struct intel_vblank_evade_ctx *evade) { … }