linux/include/drm/drm_vblank.h
Hamza Mahfooz 0d5040e406
drm/vblank: add dynamic per-crtc vblank configuration support
We would like to be able to enable vblank_disable_immediate
unconditionally, however there are a handful of cases where a small off
delay is necessary (e.g. with PSR enabled). So, we would like to be able
to adjust the vblank off delay and disable imminent values dynamically
for a given CRTC. Since, it will allow drivers to apply static screen
optimizations more quickly and consequently allow users to benefit more
so from the power savings afforded by the aforementioned optimizations,
while avoiding issues in cases where an off delay is still warranted.
In particular, the PSR case requires a small off delay of 2 frames,
otherwise display firmware isn't able to keep up with all of the
requests made to amdgpu. So, introduce drm_crtc_vblank_on_config() which
is like drm_crtc_vblank_on(), but it allows drivers to specify the
vblank CRTC configuration before enabling vblanking support for a given
CRTC.

Signed-off-by: Hamza Mahfooz <hamza.mahfooz@amd.com>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/20240725205109.209743-1-hamza.mahfooz@amd.com
2024-07-26 17:40:10 -04:00

316 lines
10 KiB
C

/*
* Copyright 2016 Intel Corp.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _DRM_VBLANK_H_
#define _DRM_VBLANK_H_
#include <linux/seqlock.h>
#include <linux/idr.h>
#include <linux/poll.h>
#include <linux/kthread.h>
#include <drm/drm_file.h>
#include <drm/drm_modes.h>
struct drm_device;
struct drm_crtc;
struct drm_vblank_work;
/**
* struct drm_pending_vblank_event - pending vblank event tracking
*/
struct drm_pending_vblank_event {
/**
* @base: Base structure for tracking pending DRM events.
*/
struct drm_pending_event base;
/**
* @pipe: drm_crtc_index() of the &drm_crtc this event is for.
*/
unsigned int pipe;
/**
* @sequence: frame event should be triggered at
*/
u64 sequence;
/**
* @event: Actual event which will be sent to userspace.
*/
union {
/**
* @event.base: DRM event base class.
*/
struct drm_event base;
/**
* @event.vbl:
*
* Event payload for vblank events, requested through
* either the MODE_PAGE_FLIP or MODE_ATOMIC IOCTL. Also
* generated by the legacy WAIT_VBLANK IOCTL, but new userspace
* should use MODE_QUEUE_SEQUENCE and &event.seq instead.
*/
struct drm_event_vblank vbl;
/**
* @event.seq: Event payload for the MODE_QUEUEU_SEQUENCE IOCTL.
*/
struct drm_event_crtc_sequence seq;
} event;
};
/**
* struct drm_vblank_crtc_config - vblank configuration for a CRTC
*/
struct drm_vblank_crtc_config {
/**
* @offdelay_ms: Vblank off delay in ms, used to determine how long
* &drm_vblank_crtc.disable_timer waits before disabling.
*
* Defaults to the value of drm_vblank_offdelay in drm_crtc_vblank_on().
*/
int offdelay_ms;
/**
* @disable_immediate: See &drm_device.vblank_disable_immediate
* for the exact semantics of immediate vblank disabling.
*
* Additionally, this tracks the disable immediate value per crtc, just
* in case it needs to differ from the default value for a given device.
*
* Defaults to the value of &drm_device.vblank_disable_immediate in
* drm_crtc_vblank_on().
*/
bool disable_immediate;
};
/**
* struct drm_vblank_crtc - vblank tracking for a CRTC
*
* This structure tracks the vblank state for one CRTC.
*
* Note that for historical reasons - the vblank handling code is still shared
* with legacy/non-kms drivers - this is a free-standing structure not directly
* connected to &struct drm_crtc. But all public interface functions are taking
* a &struct drm_crtc to hide this implementation detail.
*/
struct drm_vblank_crtc {
/**
* @dev: Pointer to the &drm_device.
*/
struct drm_device *dev;
/**
* @queue: Wait queue for vblank waiters.
*/
wait_queue_head_t queue;
/**
* @disable_timer: Disable timer for the delayed vblank disabling
* hysteresis logic. Vblank disabling is controlled through
* &drm_vblank_crtc_config.offdelay_ms and the setting of the
* &drm_device.max_vblank_count value.
*/
struct timer_list disable_timer;
/**
* @seqlock: Protect vblank count and time.
*/
seqlock_t seqlock;
/**
* @count:
*
* Current software vblank counter.
*
* Note that for a given vblank counter value drm_crtc_handle_vblank()
* and drm_crtc_vblank_count() or drm_crtc_vblank_count_and_time()
* provide a barrier: Any writes done before calling
* drm_crtc_handle_vblank() will be visible to callers of the later
* functions, iff the vblank count is the same or a later one.
*
* IMPORTANT: This guarantee requires barriers, therefor never access
* this field directly. Use drm_crtc_vblank_count() instead.
*/
atomic64_t count;
/**
* @time: Vblank timestamp corresponding to @count.
*/
ktime_t time;
/**
* @refcount: Number of users/waiters of the vblank interrupt. Only when
* this refcount reaches 0 can the hardware interrupt be disabled using
* @disable_timer.
*/
atomic_t refcount;
/**
* @last: Protected by &drm_device.vbl_lock, used for wraparound handling.
*/
u32 last;
/**
* @max_vblank_count:
*
* Maximum value of the vblank registers for this crtc. This value +1
* will result in a wrap-around of the vblank register. It is used
* by the vblank core to handle wrap-arounds.
*
* If set to zero the vblank core will try to guess the elapsed vblanks
* between times when the vblank interrupt is disabled through
* high-precision timestamps. That approach is suffering from small
* races and imprecision over longer time periods, hence exposing a
* hardware vblank counter is always recommended.
*
* This is the runtime configurable per-crtc maximum set through
* drm_crtc_set_max_vblank_count(). If this is used the driver
* must leave the device wide &drm_device.max_vblank_count at zero.
*
* If non-zero, &drm_crtc_funcs.get_vblank_counter must be set.
*/
u32 max_vblank_count;
/**
* @inmodeset: Tracks whether the vblank is disabled due to a modeset.
* For legacy driver bit 2 additionally tracks whether an additional
* temporary vblank reference has been acquired to paper over the
* hardware counter resetting/jumping. KMS drivers should instead just
* call drm_crtc_vblank_off() and drm_crtc_vblank_on(), which explicitly
* save and restore the vblank count.
*/
unsigned int inmodeset;
/**
* @pipe: drm_crtc_index() of the &drm_crtc corresponding to this
* structure.
*/
unsigned int pipe;
/**
* @framedur_ns: Frame/Field duration in ns, used by
* drm_crtc_vblank_helper_get_vblank_timestamp() and computed by
* drm_calc_timestamping_constants().
*/
int framedur_ns;
/**
* @linedur_ns: Line duration in ns, used by
* drm_crtc_vblank_helper_get_vblank_timestamp() and computed by
* drm_calc_timestamping_constants().
*/
int linedur_ns;
/**
* @hwmode:
*
* Cache of the current hardware display mode. Only valid when @enabled
* is set. This is used by helpers like
* drm_crtc_vblank_helper_get_vblank_timestamp(). We can't just access
* the hardware mode by e.g. looking at &drm_crtc_state.adjusted_mode,
* because that one is really hard to get from interrupt context.
*/
struct drm_display_mode hwmode;
/**
* @config: Stores vblank configuration values for a given CRTC.
* Also, see drm_crtc_vblank_on_config().
*/
struct drm_vblank_crtc_config config;
/**
* @enabled: Tracks the enabling state of the corresponding &drm_crtc to
* avoid double-disabling and hence corrupting saved state. Needed by
* drivers not using atomic KMS, since those might go through their CRTC
* disabling functions multiple times.
*/
bool enabled;
/**
* @worker: The &kthread_worker used for executing vblank works.
*/
struct kthread_worker *worker;
/**
* @pending_work: A list of scheduled &drm_vblank_work items that are
* waiting for a future vblank.
*/
struct list_head pending_work;
/**
* @work_wait_queue: The wait queue used for signaling that a
* &drm_vblank_work item has either finished executing, or was
* cancelled.
*/
wait_queue_head_t work_wait_queue;
};
struct drm_vblank_crtc *drm_crtc_vblank_crtc(struct drm_crtc *crtc);
int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs);
bool drm_dev_has_vblank(const struct drm_device *dev);
u64 drm_crtc_vblank_count(struct drm_crtc *crtc);
u64 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
ktime_t *vblanktime);
int drm_crtc_next_vblank_start(struct drm_crtc *crtc, ktime_t *vblanktime);
void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
struct drm_pending_vblank_event *e);
void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
struct drm_pending_vblank_event *e);
void drm_vblank_set_event(struct drm_pending_vblank_event *e,
u64 *seq,
ktime_t *now);
bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe);
bool drm_crtc_handle_vblank(struct drm_crtc *crtc);
int drm_crtc_vblank_get(struct drm_crtc *crtc);
void drm_crtc_vblank_put(struct drm_crtc *crtc);
void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe);
void drm_crtc_wait_one_vblank(struct drm_crtc *crtc);
void drm_crtc_vblank_off(struct drm_crtc *crtc);
void drm_crtc_vblank_reset(struct drm_crtc *crtc);
void drm_crtc_vblank_on_config(struct drm_crtc *crtc,
const struct drm_vblank_crtc_config *config);
void drm_crtc_vblank_on(struct drm_crtc *crtc);
u64 drm_crtc_accurate_vblank_count(struct drm_crtc *crtc);
void drm_crtc_vblank_restore(struct drm_crtc *crtc);
void drm_calc_timestamping_constants(struct drm_crtc *crtc,
const struct drm_display_mode *mode);
wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc);
void drm_crtc_set_max_vblank_count(struct drm_crtc *crtc,
u32 max_vblank_count);
/*
* Helpers for struct drm_crtc_funcs
*/
typedef bool (*drm_vblank_get_scanout_position_func)(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
drm_crtc_vblank_helper_get_vblank_timestamp_internal(struct drm_crtc *crtc,
int *max_error,
ktime_t *vblank_time,
bool in_vblank_irq,
drm_vblank_get_scanout_position_func get_scanout_position);
bool drm_crtc_vblank_helper_get_vblank_timestamp(struct drm_crtc *crtc,
int *max_error,
ktime_t *vblank_time,
bool in_vblank_irq);
#endif