Skip to content

Commit

Permalink
drm/vblank: drop the mode argument from drm_calc_vbltimestamp_from_sc…
Browse files Browse the repository at this point in the history
…anoutpos

If we restrict this helper to only kms drivers (which is the case) we
can look up the correct mode easily ourselves. But it's a bit tricky:

- All legacy drivers look at crtc->hwmode. But that is updated already
  at the beginning of the modeset helper, which means when we disable
  a pipe. Hence the final timestamps might be a bit off. But since
  this is an existing bug I'm not going to change it, but just try to
  be bug-for-bug compatible with the current code. This only applies
  to radeon&amdgpu.

- i915 tries to get it perfect by updating crtc->hwmode when the pipe
  is off (i.e. vblank->enabled = false).

- All other atomic drivers look at crtc->state->adjusted_mode. Those
  that look at state->requested_mode simply don't adjust their mode,
  so it's the same. That has two problems: Accessing crtc->state from
  interrupt handling code is unsafe, and it's updated before we shut
  down the pipe. For nonblocking modesets it's even worse.

For atomic drivers try to implement what i915 does. To do that we add
a new hwmode field to the vblank structure, and update it from
drm_calc_timestamping_constants(). For atomic drivers that's called
from the right spot by the helper library already, so all fine. But
for safety let's enforce that.

For legacy driver this function is only called at the end (oh the
fun), which is broken, so again let's not bother and just stay
bug-for-bug compatible.

The  benefit is that we can use drm_calc_vbltimestamp_from_scanoutpos
directly to implement ->get_vblank_timestamp in every driver, deleting
a lot of code.

v2: Completely new approach, trying to mimick the i915 solution.

v3: Fixup kerneldoc.

v4: Drop the WARN_ON to check that the vblank is off, atomic helpers
currently unconditionally call this. Recomputing the same stuff should
be harmless.

v5: Fix typos and move misplaced hunks to the right patches (Neil).

v6: Undo hunk movement (kbuild).

Cc: Mario Kleiner <mario.kleiner@tuebingen.mpg.de>
Cc: Eric Anholt <eric@anholt.net>
Cc: Rob Clark <robdclark@gmail.com>
Cc: linux-arm-msm@vger.kernel.org
Cc: freedreno@lists.freedesktop.org
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Christian König <christian.koenig@amd.com>
Cc: Ben Skeggs <bskeggs@redhat.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Acked-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20170509140329.24114-4-daniel.vetter@ffwll.ch
  • Loading branch information
Daniel Vetter committed May 10, 2017
1 parent 2a39b88 commit 1bf6ad6
Show file tree
Hide file tree
Showing 19 changed files with 121 additions and 277 deletions.
4 changes: 0 additions & 4 deletions drivers/gpu/drm/amd/amdgpu/amdgpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -1910,10 +1910,6 @@ int amdgpu_device_resume(struct drm_device *dev, bool resume, bool fbcon);
u32 amdgpu_get_vblank_counter_kms(struct drm_device *dev, unsigned int pipe);
int amdgpu_enable_vblank_kms(struct drm_device *dev, unsigned int pipe);
void amdgpu_disable_vblank_kms(struct drm_device *dev, unsigned int pipe);
bool amdgpu_get_vblank_timestamp_kms(struct drm_device *dev, unsigned int pipe,
int *max_error,
struct timeval *vblank_time,
bool in_vblank_irq);
long amdgpu_kms_compat_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg);

Expand Down
14 changes: 12 additions & 2 deletions drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,16 @@ static const struct file_operations amdgpu_driver_kms_fops = {
#endif
};

static bool
amdgpu_get_crtc_scanout_position(struct drm_device *dev, unsigned int pipe,
bool in_vblank_irq, int *vpos, int *hpos,
ktime_t *stime, ktime_t *etime,
const struct drm_display_mode *mode)
{
return amdgpu_get_crtc_scanoutpos(dev, pipe, 0, vpos, hpos,
stime, etime, mode);
}

static struct drm_driver kms_driver = {
.driver_features =
DRIVER_USE_AGP |
Expand All @@ -725,8 +735,8 @@ static struct drm_driver kms_driver = {
.get_vblank_counter = amdgpu_get_vblank_counter_kms,
.enable_vblank = amdgpu_enable_vblank_kms,
.disable_vblank = amdgpu_disable_vblank_kms,
.get_vblank_timestamp = amdgpu_get_vblank_timestamp_kms,
.get_scanout_position = amdgpu_get_crtc_scanoutpos,
.get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
.get_scanout_position = amdgpu_get_crtc_scanout_position,
#if defined(CONFIG_DEBUG_FS)
.debugfs_init = amdgpu_debugfs_init,
#endif
Expand Down
41 changes: 0 additions & 41 deletions drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
Original file line number Diff line number Diff line change
Expand Up @@ -934,47 +934,6 @@ void amdgpu_disable_vblank_kms(struct drm_device *dev, unsigned int pipe)
amdgpu_irq_put(adev, &adev->crtc_irq, idx);
}

/**
* amdgpu_get_vblank_timestamp_kms - get vblank timestamp
*
* @dev: drm dev pointer
* @crtc: crtc to get the timestamp for
* @max_error: max error
* @vblank_time: time value
* @in_vblank_irq: called from drm_handle_vblank()
*
* Gets the timestamp on the requested crtc based on the
* scanout position. (all asics).
* Returns true on success, false on failure.
*/
bool amdgpu_get_vblank_timestamp_kms(struct drm_device *dev, unsigned int pipe,
int *max_error,
struct timeval *vblank_time,
bool in_vblank_irq)
{
struct drm_crtc *crtc;
struct amdgpu_device *adev = dev->dev_private;

if (pipe >= dev->num_crtcs) {
DRM_ERROR("Invalid crtc %u\n", pipe);
return false;
}

/* Get associated drm_crtc: */
crtc = &adev->mode_info.crtcs[pipe]->base;
if (!crtc) {
/* This can occur on driver load if some component fails to
* initialize completely and driver is unloaded */
DRM_ERROR("Uninitialized crtc %d\n", pipe);
return false;
}

/* Helper routine in DRM core does all the work: */
return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
vblank_time, in_vblank_irq,
&crtc->hwmode);
}

const struct drm_ioctl_desc amdgpu_ioctls_kms[] = {
DRM_IOCTL_DEF_DRV(AMDGPU_GEM_CREATE, amdgpu_gem_create_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(AMDGPU_CTX, amdgpu_ctx_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
Expand Down
3 changes: 3 additions & 0 deletions drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,9 @@ struct amdgpu_framebuffer {
((em) == ATOM_ENCODER_MODE_DP_MST))

/* Driver internal use only flags of amdgpu_get_crtc_scanoutpos() */
#define DRM_SCANOUTPOS_VALID (1 << 0)
#define DRM_SCANOUTPOS_IN_VBLANK (1 << 1)
#define DRM_SCANOUTPOS_ACCURATE (1 << 2)
#define USE_REAL_VBLANKSTART (1 << 30)
#define GET_DISTANCE_TO_VBLANKSTART (1 << 31)

Expand Down
43 changes: 31 additions & 12 deletions drivers/gpu/drm/drm_irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,7 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc,

vblank->linedur_ns = linedur_ns;
vblank->framedur_ns = framedur_ns;
vblank->hwmode = *mode;

DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
crtc->base.id, mode->crtc_htotal,
Expand All @@ -704,7 +705,6 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
* True when called from drm_crtc_handle_vblank(). Some drivers
* need to apply some workarounds for gpu-specific vblank irq quirks
* if flag is set.
* @mode: mode which defines the scanout timings
*
* Implements calculation of exact vblank timestamps from given drm_display_mode
* timings and current video scanout position of a CRTC. This can be called from
Expand All @@ -724,6 +724,13 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
* returns as no operation if a doublescan or interlaced video mode is
* active. Higher level code is expected to handle this.
*
* This function can be used to implement the &drm_driver.get_vblank_timestamp
* directly, if the driver implements the &drm_driver.get_scanout_position hook.
*
* Note that atomic drivers must call drm_calc_timestamping_constants() before
* enabling a CRTC. The atomic helpers already take care of that in
* drm_atomic_helper_update_legacy_modeset_state().
*
* Returns:
*
* Returns true on success, and false on failure, i.e. when no accurate
Expand All @@ -733,17 +740,23 @@ bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
unsigned int pipe,
int *max_error,
struct timeval *vblank_time,
bool in_vblank_irq,
const struct drm_display_mode *mode)
bool in_vblank_irq)
{
struct timeval tv_etime;
ktime_t stime, etime;
unsigned int vbl_status;
bool vbl_status;
struct drm_crtc *crtc;
const struct drm_display_mode *mode;
struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
int vpos, hpos, i;
int delta_ns, duration_ns;
unsigned flags = in_vblank_irq ? DRM_CALLED_FROM_VBLIRQ : 0;

if (pipe >= dev->num_crtcs) {
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return false;

crtc = drm_crtc_from_index(dev, pipe);

if (pipe >= dev->num_crtcs || !crtc) {
DRM_ERROR("Invalid crtc %u\n", pipe);
return false;
}
Expand All @@ -754,6 +767,11 @@ bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
return false;
}

if (drm_drv_uses_atomic_modeset(dev))
mode = &vblank->hwmode;
else
mode = &crtc->hwmode;

/* If mode timing undefined, just return as no-op:
* Happens during initial modesetting of a crtc.
*/
Expand All @@ -774,15 +792,16 @@ bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
* Get vertical and horizontal scanout position vpos, hpos,
* and bounding timestamps stime, etime, pre/post query.
*/
vbl_status = dev->driver->get_scanout_position(dev, pipe, flags,
vbl_status = dev->driver->get_scanout_position(dev, pipe,
in_vblank_irq,
&vpos, &hpos,
&stime, &etime,
mode);

/* Return as no-op if scanout query unsupported or failed. */
if (!(vbl_status & DRM_SCANOUTPOS_VALID)) {
DRM_DEBUG("crtc %u : scanoutpos query failed [0x%x].\n",
pipe, vbl_status);
if (!vbl_status) {
DRM_DEBUG("crtc %u : scanoutpos query failed.\n",
pipe);
return false;
}

Expand Down Expand Up @@ -821,8 +840,8 @@ bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
etime = ktime_sub_ns(etime, delta_ns);
*vblank_time = ktime_to_timeval(etime);

DRM_DEBUG_VBL("crtc %u : v 0x%x p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
pipe, vbl_status, hpos, vpos,
DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
pipe, hpos, vpos,
(long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
(long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
duration_ns/1000, i);
Expand Down
52 changes: 7 additions & 45 deletions drivers/gpu/drm/i915/i915_irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -827,24 +827,23 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
return (position + crtc->scanline_offset) % vtotal;
}

static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
unsigned int flags, int *vpos, int *hpos,
ktime_t *stime, ktime_t *etime,
const struct drm_display_mode *mode)
static bool i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
bool in_vblank_irq, int *vpos, int *hpos,
ktime_t *stime, ktime_t *etime,
const struct drm_display_mode *mode)
{
struct drm_i915_private *dev_priv = to_i915(dev);
struct intel_crtc *intel_crtc = intel_get_crtc_for_pipe(dev_priv,
pipe);
int position;
int vbl_start, vbl_end, hsync_start, htotal, vtotal;
bool in_vbl = true;
int ret = 0;
unsigned long irqflags;

if (WARN_ON(!mode->crtc_clock)) {
DRM_DEBUG_DRIVER("trying to get scanoutpos for disabled "
"pipe %c\n", pipe_name(pipe));
return 0;
return false;
}

htotal = mode->crtc_htotal;
Expand All @@ -859,8 +858,6 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
vtotal /= 2;
}

ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;

/*
* Lock uncore.lock, as we will do multiple timing critical raw
* register reads, potentially with preemption disabled, so the
Expand Down Expand Up @@ -944,11 +941,7 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
*hpos = position - (*vpos * htotal);
}

/* In vblank? */
if (in_vbl)
ret |= DRM_SCANOUTPOS_IN_VBLANK;

return ret;
return true;
}

int intel_get_crtc_scanline(struct intel_crtc *crtc)
Expand All @@ -964,37 +957,6 @@ int intel_get_crtc_scanline(struct intel_crtc *crtc)
return position;
}

static bool i915_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe,
int *max_error,
struct timeval *vblank_time,
bool in_vblank_irq)
{
struct drm_i915_private *dev_priv = to_i915(dev);
struct intel_crtc *crtc;

if (pipe >= INTEL_INFO(dev_priv)->num_pipes) {
DRM_ERROR("Invalid crtc %u\n", pipe);
return false;
}

/* Get drm_crtc to timestamp: */
crtc = intel_get_crtc_for_pipe(dev_priv, pipe);
if (crtc == NULL) {
DRM_ERROR("Invalid crtc %u\n", pipe);
return false;
}

if (!crtc->base.hwmode.crtc_clock) {
DRM_DEBUG_KMS("crtc %u is disabled\n", pipe);
return false;
}

/* Helper routine in DRM core does all the work: */
return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
vblank_time, in_vblank_irq,
&crtc->base.hwmode);
}

static void ironlake_rps_change_irq_handler(struct drm_i915_private *dev_priv)
{
u32 busy_up, busy_down, max_avg, min_avg;
Expand Down Expand Up @@ -4294,7 +4256,7 @@ void intel_irq_init(struct drm_i915_private *dev_priv)

dev_priv->hotplug.hpd_storm_threshold = HPD_STORM_DEFAULT_THRESHOLD;

dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp;
dev->driver->get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos;
dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;

if (IS_CHERRYVIEW(dev_priv)) {
Expand Down
45 changes: 8 additions & 37 deletions drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
Original file line number Diff line number Diff line change
Expand Up @@ -527,31 +527,28 @@ static struct drm_encoder *get_encoder_from_crtc(struct drm_crtc *crtc)
return NULL;
}

static int mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
unsigned int flags, int *vpos, int *hpos,
ktime_t *stime, ktime_t *etime,
const struct drm_display_mode *mode)
static bool mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
bool in_vblank_irq, int *vpos, int *hpos,
ktime_t *stime, ktime_t *etime,
const struct drm_display_mode *mode)
{
struct msm_drm_private *priv = dev->dev_private;
struct drm_crtc *crtc;
struct drm_encoder *encoder;
int line, vsw, vbp, vactive_start, vactive_end, vfp_end;
int ret = 0;

crtc = priv->crtcs[pipe];
if (!crtc) {
DRM_ERROR("Invalid crtc %d\n", pipe);
return 0;
return false;
}

encoder = get_encoder_from_crtc(crtc);
if (!encoder) {
DRM_ERROR("no encoder found for crtc %d\n", pipe);
return 0;
return false;
}

ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;

vsw = mode->crtc_vsync_end - mode->crtc_vsync_start;
vbp = mode->crtc_vtotal - mode->crtc_vsync_end;

Expand All @@ -575,10 +572,8 @@ static int mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,

if (line < vactive_start) {
line -= vactive_start;
ret |= DRM_SCANOUTPOS_IN_VBLANK;
} else if (line > vactive_end) {
line = line - vfp_end - vactive_start;
ret |= DRM_SCANOUTPOS_IN_VBLANK;
} else {
line -= vactive_start;
}
Expand All @@ -589,31 +584,7 @@ static int mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
if (etime)
*etime = ktime_get();

return ret;
}

static bool mdp5_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe,
int *max_error,
struct timeval *vblank_time,
bool in_vblank_irq)
{
struct msm_drm_private *priv = dev->dev_private;
struct drm_crtc *crtc;

if (pipe < 0 || pipe >= priv->num_crtcs) {
DRM_ERROR("Invalid crtc %d\n", pipe);
return false;
}

crtc = priv->crtcs[pipe];
if (!crtc) {
DRM_ERROR("Invalid crtc %d\n", pipe);
return false;
}

return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
vblank_time, in_vblank_irq,
&crtc->mode);
return true;
}

static u32 mdp5_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
Expand Down Expand Up @@ -725,7 +696,7 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
dev->mode_config.max_width = 0xffff;
dev->mode_config.max_height = 0xffff;

dev->driver->get_vblank_timestamp = mdp5_get_vblank_timestamp;
dev->driver->get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos;
dev->driver->get_scanout_position = mdp5_get_scanoutpos;
dev->driver->get_vblank_counter = mdp5_get_vblank_counter;
dev->max_vblank_count = 0xffffffff;
Expand Down
Loading

0 comments on commit 1bf6ad6

Please sign in to comment.