Commit f5395ba3 authored by Dave Airlie's avatar Dave Airlie

Merge branch 'drm-vbl-timestamp' of git://gitorious.org/vsyrjala/linux into drm-next

Here's the vblank timestamp pull request you wanted.

I addressed the few bugs that Mario pointed out and added
the r-bs.

As it has been a while since I made the changes, I gave it a
quick spin on a few different i915 machines. Fortunately
everything still seems to be fine.

* 'drm-vbl-timestamp' of git://gitorious.org/vsyrjala/linux:
  drm/i915: Add a kludge for DSL incrementing too late and ISR not working
  drm/radeon: Move the early vblank IRQ fixup to radeon_get_crtc_scanoutpos()
  drm: Pass 'flags' from the caller to .get_scanout_position()
  drm: Fix vblank timestamping constants for interlaced modes
  drm/i915: Fix scanoutpos calculations for interlaced modes
  drm: Change {pixel,line,frame}dur_ns from s64 to int
  drm: Use crtc_clock in drm_calc_timestamping_constants()
  drm/radeon: Populate crtc_clock in radeon_atom_get_tv_timings()
  drm: Simplify the math in drm_calc_timestamping_constants()
  drm: Improve drm_calc_timestamping_constants() documentation
  drm/i915: Call drm_calc_timestamping_constants() earlier
  drm/i915: Kill hwmode save/restore
  drm: Pass the display mode to drm_calc_vbltimestamp_from_scanoutpos()
  drm: Pass the display mode to drm_calc_timestamping_constants()
parents 2b76a676 095163ba
...@@ -536,7 +536,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, ...@@ -536,7 +536,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
* are later needed by vblank and swap-completion * are later needed by vblank and swap-completion
* timestamping. They are derived from true hwmode. * timestamping. They are derived from true hwmode.
*/ */
drm_calc_timestamping_constants(crtc); drm_calc_timestamping_constants(crtc, &crtc->hwmode);
/* FIXME: add subpixel order */ /* FIXME: add subpixel order */
done: done:
......
...@@ -436,45 +436,41 @@ int drm_control(struct drm_device *dev, void *data, ...@@ -436,45 +436,41 @@ int drm_control(struct drm_device *dev, void *data,
} }
/** /**
* drm_calc_timestamping_constants - Calculate and * drm_calc_timestamping_constants - Calculate vblank timestamp constants
* store various constants which are later needed by
* vblank and swap-completion timestamping, e.g, by
* drm_calc_vbltimestamp_from_scanoutpos().
* They are derived from crtc's true scanout timing,
* so they take things like panel scaling or other
* adjustments into account.
* *
* @crtc drm_crtc whose timestamp constants should be updated. * @crtc drm_crtc whose timestamp constants should be updated.
* @mode display mode containing the scanout timings
* *
* Calculate and store various constants which are later
* needed by vblank and swap-completion timestamping, e.g,
* by drm_calc_vbltimestamp_from_scanoutpos(). They are
* derived from crtc's true scanout timing, so they take
* things like panel scaling or other adjustments into account.
*/ */
void drm_calc_timestamping_constants(struct drm_crtc *crtc) void drm_calc_timestamping_constants(struct drm_crtc *crtc,
const struct drm_display_mode *mode)
{ {
s64 linedur_ns = 0, pixeldur_ns = 0, framedur_ns = 0; int linedur_ns = 0, pixeldur_ns = 0, framedur_ns = 0;
u64 dotclock; int dotclock = mode->crtc_clock;
/* Dot clock in Hz: */
dotclock = (u64) crtc->hwmode.clock * 1000;
/* Fields of interlaced scanout modes are only half a frame duration.
* Double the dotclock to get half the frame-/line-/pixelduration.
*/
if (crtc->hwmode.flags & DRM_MODE_FLAG_INTERLACE)
dotclock *= 2;
/* Valid dotclock? */ /* Valid dotclock? */
if (dotclock > 0) { if (dotclock > 0) {
int frame_size; int frame_size = mode->crtc_htotal * mode->crtc_vtotal;
/* Convert scanline length in pixels and video dot clock to
* line duration, frame duration and pixel duration in /*
* nanoseconds: * Convert scanline length in pixels and video
* dot clock to line duration, frame duration
* and pixel duration in nanoseconds:
*/ */
pixeldur_ns = (s64) div64_u64(1000000000, dotclock); pixeldur_ns = 1000000 / dotclock;
linedur_ns = (s64) div64_u64(((u64) crtc->hwmode.crtc_htotal * linedur_ns = div_u64((u64) mode->crtc_htotal * 1000000, dotclock);
1000000000), dotclock); framedur_ns = div_u64((u64) frame_size * 1000000, dotclock);
frame_size = crtc->hwmode.crtc_htotal *
crtc->hwmode.crtc_vtotal; /*
framedur_ns = (s64) div64_u64((u64) frame_size * 1000000000, * Fields of interlaced scanout modes are only half a frame duration.
dotclock); */
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
framedur_ns /= 2;
} else } else
DRM_ERROR("crtc %d: Can't calculate constants, dotclock = 0!\n", DRM_ERROR("crtc %d: Can't calculate constants, dotclock = 0!\n",
crtc->base.id); crtc->base.id);
...@@ -484,11 +480,11 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc) ...@@ -484,11 +480,11 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc)
crtc->framedur_ns = framedur_ns; crtc->framedur_ns = framedur_ns;
DRM_DEBUG("crtc %d: hwmode: htotal %d, vtotal %d, vdisplay %d\n", DRM_DEBUG("crtc %d: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
crtc->base.id, crtc->hwmode.crtc_htotal, crtc->base.id, mode->crtc_htotal,
crtc->hwmode.crtc_vtotal, crtc->hwmode.crtc_vdisplay); mode->crtc_vtotal, mode->crtc_vdisplay);
DRM_DEBUG("crtc %d: clock %d kHz framedur %d linedur %d, pixeldur %d\n", DRM_DEBUG("crtc %d: clock %d kHz framedur %d linedur %d, pixeldur %d\n",
crtc->base.id, (int) dotclock/1000, (int) framedur_ns, crtc->base.id, dotclock, framedur_ns,
(int) linedur_ns, (int) pixeldur_ns); linedur_ns, pixeldur_ns);
} }
EXPORT_SYMBOL(drm_calc_timestamping_constants); EXPORT_SYMBOL(drm_calc_timestamping_constants);
...@@ -521,6 +517,7 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants); ...@@ -521,6 +517,7 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
* 0 = Default. * 0 = Default.
* DRM_CALLED_FROM_VBLIRQ = If function is called from vbl irq handler. * DRM_CALLED_FROM_VBLIRQ = If function is called from vbl irq handler.
* @refcrtc: drm_crtc* of crtc which defines scanout timing. * @refcrtc: drm_crtc* of crtc which defines scanout timing.
* @mode: mode which defines the scanout timings
* *
* Returns negative value on error, failure or if not supported in current * Returns negative value on error, failure or if not supported in current
* video mode: * video mode:
...@@ -540,14 +537,14 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, ...@@ -540,14 +537,14 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
int *max_error, int *max_error,
struct timeval *vblank_time, struct timeval *vblank_time,
unsigned flags, unsigned flags,
struct drm_crtc *refcrtc) const struct drm_crtc *refcrtc,
const struct drm_display_mode *mode)
{ {
ktime_t stime, etime, mono_time_offset; ktime_t stime, etime, mono_time_offset;
struct timeval tv_etime; struct timeval tv_etime;
struct drm_display_mode *mode; int vbl_status;
int vbl_status, vtotal, vdisplay;
int vpos, hpos, i; int vpos, hpos, i;
s64 framedur_ns, linedur_ns, pixeldur_ns, delta_ns, duration_ns; int framedur_ns, linedur_ns, pixeldur_ns, delta_ns, duration_ns;
bool invbl; bool invbl;
if (crtc < 0 || crtc >= dev->num_crtcs) { if (crtc < 0 || crtc >= dev->num_crtcs) {
...@@ -561,10 +558,6 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, ...@@ -561,10 +558,6 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
return -EIO; return -EIO;
} }
mode = &refcrtc->hwmode;
vtotal = mode->crtc_vtotal;
vdisplay = mode->crtc_vdisplay;
/* Durations of frames, lines, pixels in nanoseconds. */ /* Durations of frames, lines, pixels in nanoseconds. */
framedur_ns = refcrtc->framedur_ns; framedur_ns = refcrtc->framedur_ns;
linedur_ns = refcrtc->linedur_ns; linedur_ns = refcrtc->linedur_ns;
...@@ -573,7 +566,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, ...@@ -573,7 +566,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
/* If mode timing undefined, just return as no-op: /* If mode timing undefined, just return as no-op:
* Happens during initial modesetting of a crtc. * Happens during initial modesetting of a crtc.
*/ */
if (vtotal <= 0 || vdisplay <= 0 || framedur_ns == 0) { if (framedur_ns == 0) {
DRM_DEBUG("crtc %d: Noop due to uninitialized mode.\n", crtc); DRM_DEBUG("crtc %d: Noop due to uninitialized mode.\n", crtc);
return -EAGAIN; return -EAGAIN;
} }
...@@ -590,7 +583,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, ...@@ -590,7 +583,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
* Get vertical and horizontal scanout position vpos, hpos, * Get vertical and horizontal scanout position vpos, hpos,
* and bounding timestamps stime, etime, pre/post query. * and bounding timestamps stime, etime, pre/post query.
*/ */
vbl_status = dev->driver->get_scanout_position(dev, crtc, &vpos, vbl_status = dev->driver->get_scanout_position(dev, crtc, flags, &vpos,
&hpos, &stime, &etime); &hpos, &stime, &etime);
/* /*
...@@ -611,18 +604,18 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, ...@@ -611,18 +604,18 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime); duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime);
/* Accept result with < max_error nsecs timing uncertainty. */ /* Accept result with < max_error nsecs timing uncertainty. */
if (duration_ns <= (s64) *max_error) if (duration_ns <= *max_error)
break; break;
} }
/* Noisy system timing? */ /* Noisy system timing? */
if (i == DRM_TIMESTAMP_MAXRETRIES) { if (i == DRM_TIMESTAMP_MAXRETRIES) {
DRM_DEBUG("crtc %d: Noisy timestamp %d us > %d us [%d reps].\n", DRM_DEBUG("crtc %d: Noisy timestamp %d us > %d us [%d reps].\n",
crtc, (int) duration_ns/1000, *max_error/1000, i); crtc, duration_ns/1000, *max_error/1000, i);
} }
/* Return upper bound of timestamp precision error. */ /* Return upper bound of timestamp precision error. */
*max_error = (int) duration_ns; *max_error = duration_ns;
/* Check if in vblank area: /* Check if in vblank area:
* vpos is >=0 in video scanout area, but negative * vpos is >=0 in video scanout area, but negative
...@@ -635,25 +628,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, ...@@ -635,25 +628,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
* since start of scanout at first display scanline. delta_ns * since start of scanout at first display scanline. delta_ns
* can be negative if start of scanout hasn't happened yet. * can be negative if start of scanout hasn't happened yet.
*/ */
delta_ns = (s64) vpos * linedur_ns + (s64) hpos * pixeldur_ns; delta_ns = vpos * linedur_ns + hpos * pixeldur_ns;
/* Is vpos outside nominal vblank area, but less than
* 1/100 of a frame height away from start of vblank?
* If so, assume this isn't a massively delayed vblank
* interrupt, but a vblank interrupt that fired a few
* microseconds before true start of vblank. Compensate
* by adding a full frame duration to the final timestamp.
* Happens, e.g., on ATI R500, R600.
*
* We only do this if DRM_CALLED_FROM_VBLIRQ.
*/
if ((flags & DRM_CALLED_FROM_VBLIRQ) && !invbl &&
((vdisplay - vpos) < vtotal / 100)) {
delta_ns = delta_ns - framedur_ns;
/* Signal this correction as "applied". */
vbl_status |= 0x8;
}
if (!drm_timestamp_monotonic) if (!drm_timestamp_monotonic)
etime = ktime_sub(etime, mono_time_offset); etime = ktime_sub(etime, mono_time_offset);
...@@ -673,7 +648,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, ...@@ -673,7 +648,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
crtc, (int)vbl_status, hpos, vpos, crtc, (int)vbl_status, hpos, vpos,
(long)tv_etime.tv_sec, (long)tv_etime.tv_usec, (long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
(long)vblank_time->tv_sec, (long)vblank_time->tv_usec, (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
(int)duration_ns/1000, i); duration_ns/1000, i);
vbl_status = DRM_VBLANKTIME_SCANOUTPOS_METHOD; vbl_status = DRM_VBLANKTIME_SCANOUTPOS_METHOD;
if (invbl) if (invbl)
......
...@@ -621,36 +621,15 @@ static u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe) ...@@ -621,36 +621,15 @@ static u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
#define __raw_i915_read32(dev_priv__, reg__) readl((dev_priv__)->regs + (reg__)) #define __raw_i915_read32(dev_priv__, reg__) readl((dev_priv__)->regs + (reg__))
#define __raw_i915_read16(dev_priv__, reg__) readw((dev_priv__)->regs + (reg__)) #define __raw_i915_read16(dev_priv__, reg__) readw((dev_priv__)->regs + (reg__))
static bool intel_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe) static bool ilk_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t status; uint32_t status;
int reg;
if (IS_VALLEYVIEW(dev)) { if (INTEL_INFO(dev)->gen < 7) {
status = pipe == PIPE_A ?
I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT :
I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
reg = VLV_ISR;
} else if (IS_GEN2(dev)) {
status = pipe == PIPE_A ?
I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT :
I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
reg = ISR;
} else if (INTEL_INFO(dev)->gen < 5) {
status = pipe == PIPE_A ?
I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT :
I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;
reg = ISR;
} else if (INTEL_INFO(dev)->gen < 7) {
status = pipe == PIPE_A ? status = pipe == PIPE_A ?
DE_PIPEA_VBLANK : DE_PIPEA_VBLANK :
DE_PIPEB_VBLANK; DE_PIPEB_VBLANK;
reg = DEISR;
} else { } else {
switch (pipe) { switch (pipe) {
default: default:
...@@ -664,18 +643,14 @@ static bool intel_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe) ...@@ -664,18 +643,14 @@ static bool intel_pipe_in_vblank_locked(struct drm_device *dev, enum pipe pipe)
status = DE_PIPEC_VBLANK_IVB; status = DE_PIPEC_VBLANK_IVB;
break; break;
} }
reg = DEISR;
} }
if (IS_GEN2(dev)) return __raw_i915_read32(dev_priv, DEISR) & status;
return __raw_i915_read16(dev_priv, reg) & status;
else
return __raw_i915_read32(dev_priv, reg) & status;
} }
static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe, static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
int *vpos, int *hpos, ktime_t *stime, ktime_t *etime) unsigned int flags, int *vpos, int *hpos,
ktime_t *stime, ktime_t *etime)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
...@@ -698,6 +673,12 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe, ...@@ -698,6 +673,12 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
vbl_start = mode->crtc_vblank_start; vbl_start = mode->crtc_vblank_start;
vbl_end = mode->crtc_vblank_end; vbl_end = mode->crtc_vblank_end;
if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
vbl_start = DIV_ROUND_UP(vbl_start, 2);
vbl_end /= 2;
vtotal /= 2;
}
ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE; ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;
/* /*
...@@ -722,17 +703,42 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe, ...@@ -722,17 +703,42 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, int pipe,
else else
position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN3; position = __raw_i915_read32(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN3;
/* if (HAS_PCH_SPLIT(dev)) {
* The scanline counter increments at the leading edge /*
* of hsync, ie. it completely misses the active portion * The scanline counter increments at the leading edge
* of the line. Fix up the counter at both edges of vblank * of hsync, ie. it completely misses the active portion
* to get a more accurate picture whether we're in vblank * of the line. Fix up the counter at both edges of vblank
* or not. * to get a more accurate picture whether we're in vblank
*/ * or not.
in_vbl = intel_pipe_in_vblank_locked(dev, pipe); */
if ((in_vbl && position == vbl_start - 1) || in_vbl = ilk_pipe_in_vblank_locked(dev, pipe);
(!in_vbl && position == vbl_end - 1)) if ((in_vbl && position == vbl_start - 1) ||
position = (position + 1) % vtotal; (!in_vbl && position == vbl_end - 1))
position = (position + 1) % vtotal;
} else {
/*
* ISR vblank status bits don't work the way we'd want
* them to work on non-PCH platforms (for
* ilk_pipe_in_vblank_locked()), and there doesn't
* appear any other way to determine if we're currently
* in vblank.
*
* Instead let's assume that we're already in vblank if
* we got called from the vblank interrupt and the
* scanline counter value indicates that we're on the
* line just prior to vblank start. This should result
* in the correct answer, unless the vblank interrupt
* delivery really got delayed for almost exactly one
* full frame/field.
*/
if (flags & DRM_CALLED_FROM_VBLIRQ &&
position == vbl_start - 1) {
position = (position + 1) % vtotal;
/* Signal this correction as "applied". */
ret |= 0x8;
}
}
} else { } else {
/* Have access to pixelcount since start of frame. /* Have access to pixelcount since start of frame.
* We can split this into vertical and horizontal * We can split this into vertical and horizontal
...@@ -809,7 +815,8 @@ static int i915_get_vblank_timestamp(struct drm_device *dev, int pipe, ...@@ -809,7 +815,8 @@ static int i915_get_vblank_timestamp(struct drm_device *dev, int pipe,
/* Helper routine in DRM core does all the work: */ /* Helper routine in DRM core does all the work: */
return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error, return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
vblank_time, flags, vblank_time, flags,
crtc); crtc,
&to_intel_crtc(crtc)->config.adjusted_mode);
} }
static bool intel_hpd_irq_event(struct drm_device *dev, static bool intel_hpd_irq_event(struct drm_device *dev,
......
...@@ -9597,21 +9597,19 @@ static int __intel_set_mode(struct drm_crtc *crtc, ...@@ -9597,21 +9597,19 @@ static int __intel_set_mode(struct drm_crtc *crtc,
{ {
struct drm_device *dev = crtc->dev; struct drm_device *dev = crtc->dev;
drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_display_mode *saved_mode, *saved_hwmode; struct drm_display_mode *saved_mode;
struct intel_crtc_config *pipe_config = NULL; struct intel_crtc_config *pipe_config = NULL;
struct intel_crtc *intel_crtc; struct intel_crtc *intel_crtc;
unsigned disable_pipes, prepare_pipes, modeset_pipes; unsigned disable_pipes, prepare_pipes, modeset_pipes;
int ret = 0; int ret = 0;
saved_mode = kcalloc(2, sizeof(*saved_mode), GFP_KERNEL); saved_mode = kmalloc(sizeof(*saved_mode), GFP_KERNEL);
if (!saved_mode) if (!saved_mode)
return -ENOMEM; return -ENOMEM;
saved_hwmode = saved_mode + 1;
intel_modeset_affected_pipes(crtc, &modeset_pipes, intel_modeset_affected_pipes(crtc, &modeset_pipes,
&prepare_pipes, &disable_pipes); &prepare_pipes, &disable_pipes);
*saved_hwmode = crtc->hwmode;
*saved_mode = crtc->mode; *saved_mode = crtc->mode;
/* Hack: Because we don't (yet) support global modeset on multiple /* Hack: Because we don't (yet) support global modeset on multiple
...@@ -9662,6 +9660,14 @@ static int __intel_set_mode(struct drm_crtc *crtc, ...@@ -9662,6 +9660,14 @@ static int __intel_set_mode(struct drm_crtc *crtc,
/* mode_set/enable/disable functions rely on a correct pipe /* mode_set/enable/disable functions rely on a correct pipe
* config. */ * config. */
to_intel_crtc(crtc)->config = *pipe_config; to_intel_crtc(crtc)->config = *pipe_config;
/*
* Calculate and store various constants which
* are later needed by vblank and swap-completion
* timestamping. They are derived from true hwmode.
*/
drm_calc_timestamping_constants(crtc,
&pipe_config->adjusted_mode);
} }
/* Only after disabling all output pipelines that will be changed can we /* Only after disabling all output pipelines that will be changed can we
...@@ -9685,23 +9691,10 @@ static int __intel_set_mode(struct drm_crtc *crtc, ...@@ -9685,23 +9691,10 @@ static int __intel_set_mode(struct drm_crtc *crtc,
for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc)
dev_priv->display.crtc_enable(&intel_crtc->base); dev_priv->display.crtc_enable(&intel_crtc->base);
if (modeset_pipes) {
/* Store real post-adjustment hardware mode. */
crtc->hwmode = pipe_config->adjusted_mode;
/* Calculate and store various constants which
* are later needed by vblank and swap-completion
* timestamping. They are derived from true hwmode.
*/
drm_calc_timestamping_constants(crtc);
}
/* FIXME: add subpixel order */ /* FIXME: add subpixel order */
done: done:
if (ret && crtc->enabled) { if (ret && crtc->enabled)
crtc->hwmode = *saved_hwmode;
crtc->mode = *saved_mode; crtc->mode = *saved_mode;
}
out: out:
kfree(pipe_config); kfree(pipe_config);
......
...@@ -1799,7 +1799,8 @@ bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index, ...@@ -1799,7 +1799,8 @@ bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index,
if (misc & ATOM_DOUBLE_CLOCK_MODE) if (misc & ATOM_DOUBLE_CLOCK_MODE)
mode->flags |= DRM_MODE_FLAG_DBLSCAN; mode->flags |= DRM_MODE_FLAG_DBLSCAN;
mode->clock = le16_to_cpu(tv_info->aModeTimings[index].usPixelClock) * 10; mode->crtc_clock = mode->clock =
le16_to_cpu(tv_info->aModeTimings[index].usPixelClock) * 10;
if (index == 1) { if (index == 1) {
/* PAL timings appear to have wrong values for totals */ /* PAL timings appear to have wrong values for totals */
...@@ -1842,7 +1843,8 @@ bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index, ...@@ -1842,7 +1843,8 @@ bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index,
if (misc & ATOM_DOUBLE_CLOCK_MODE) if (misc & ATOM_DOUBLE_CLOCK_MODE)
mode->flags |= DRM_MODE_FLAG_DBLSCAN; mode->flags |= DRM_MODE_FLAG_DBLSCAN;
mode->clock = le16_to_cpu(dtd_timings->usPixClk) * 10; mode->crtc_clock = mode->clock =
le16_to_cpu(dtd_timings->usPixClk) * 10;
break; break;
} }
return true; return true;
......
...@@ -306,7 +306,7 @@ void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id) ...@@ -306,7 +306,7 @@ void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id)
* to complete in this vblank? * to complete in this vblank?
*/ */
if (update_pending && if (update_pending &&
(DRM_SCANOUTPOS_VALID & radeon_get_crtc_scanoutpos(rdev->ddev, crtc_id, (DRM_SCANOUTPOS_VALID & radeon_get_crtc_scanoutpos(rdev->ddev, crtc_id, 0,
&vpos, &hpos, NULL, NULL)) && &vpos, &hpos, NULL, NULL)) &&
((vpos >= (99 * rdev->mode_info.crtcs[crtc_id]->base.hwmode.crtc_vdisplay)/100) || ((vpos >= (99 * rdev->mode_info.crtcs[crtc_id]->base.hwmode.crtc_vdisplay)/100) ||
(vpos < 0 && !ASIC_IS_AVIVO(rdev)))) { (vpos < 0 && !ASIC_IS_AVIVO(rdev)))) {
...@@ -1610,6 +1610,7 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, ...@@ -1610,6 +1610,7 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
* *
* \param dev Device to query. * \param dev Device to query.
* \param crtc Crtc to query. * \param crtc Crtc to query.
* \param flags Flags from caller (DRM_CALLED_FROM_VBLIRQ or 0).
* \param *vpos Location where vertical scanout position should be stored. * \param *vpos Location where vertical scanout position should be stored.
* \param *hpos Location where horizontal scanout position should go. * \param *hpos Location where horizontal scanout position should go.
* \param *stime Target location for timestamp taken immediately before * \param *stime Target location for timestamp taken immediately before
...@@ -1631,8 +1632,8 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, ...@@ -1631,8 +1632,8 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
* unknown small number of scanlines wrt. real scanout position. * unknown small number of scanlines wrt. real scanout position.
* *
*/ */
int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, int *vpos, int *hpos, int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, unsigned int flags,
ktime_t *stime, ktime_t *etime) int *vpos, int *hpos, ktime_t *stime, ktime_t *etime)
{ {
u32 stat_crtc = 0, vbl = 0, position = 0; u32 stat_crtc = 0, vbl = 0, position = 0;
int vbl_start, vbl_end, vtotal, ret = 0; int vbl_start, vbl_end, vtotal, ret = 0;
...@@ -1774,5 +1775,27 @@ int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, int *vpos, int ...@@ -1774,5 +1775,27 @@ int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, int *vpos, int
if (in_vbl) if (in_vbl)
ret |= DRM_SCANOUTPOS_INVBL; ret |= DRM_SCANOUTPOS_INVBL;
/* Is vpos outside nominal vblank area, but less than
* 1/100 of a frame height away from start of vblank?
* If so, assume this isn't a massively delayed vblank
* interrupt, but a vblank interrupt that fired a few
* microseconds before true start of vblank. Compensate
* by adding a full frame duration to the final timestamp.
* Happens, e.g., on ATI R500, R600.
*
* We only do this if DRM_CALLED_FROM_VBLIRQ.
*/
if ((flags & DRM_CALLED_FROM_VBLIRQ) && !in_vbl) {
vbl_start = rdev->mode_info.crtcs[crtc]->base.hwmode.crtc_vdisplay;
vtotal = rdev->mode_info.crtcs[crtc]->base.hwmode.crtc_vtotal;
if (vbl_start - *vpos < vtotal / 100) {
*vpos -= vtotal;
/* Signal this correction as "applied". */
ret |= 0x8;
}
}
return ret; return ret;
} }
...@@ -109,6 +109,7 @@ int radeon_gem_object_open(struct drm_gem_object *obj, ...@@ -109,6 +109,7 @@ int radeon_gem_object_open(struct drm_gem_object *obj,
void radeon_gem_object_close(struct drm_gem_object *obj, void radeon_gem_object_close(struct drm_gem_object *obj,
struct drm_file *file_priv); struct drm_file *file_priv);
extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc,
unsigned int flags,
int *vpos, int *hpos, ktime_t *stime, int *vpos, int *hpos, ktime_t *stime,
ktime_t *etime); ktime_t *etime);
extern const struct drm_ioctl_desc radeon_ioctls_kms[]; extern const struct drm_ioctl_desc radeon_ioctls_kms[];
......
...@@ -719,7 +719,7 @@ int radeon_get_vblank_timestamp_kms(struct drm_device *dev, int crtc, ...@@ -719,7 +719,7 @@ int radeon_get_vblank_timestamp_kms(struct drm_device *dev, int crtc,
/* Helper routine in DRM core does all the work: */ /* Helper routine in DRM core does all the work: */
return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error, return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error,
vblank_time, flags, vblank_time, flags,
drmcrtc); drmcrtc, &drmcrtc->hwmode);
} }
#define KMS_INVALID_IOCTL(name) \ #define KMS_INVALID_IOCTL(name) \
......
...@@ -801,6 +801,7 @@ extern int radeon_crtc_cursor_move(struct drm_crtc *crtc, ...@@ -801,6 +801,7 @@ extern int radeon_crtc_cursor_move(struct drm_crtc *crtc,
int x, int y); int x, int y);
extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc, extern int radeon_get_crtc_scanoutpos(struct drm_device *dev, int crtc,
unsigned int flags,
int *vpos, int *hpos, ktime_t *stime, int *vpos, int *hpos, ktime_t *stime,
ktime_t *etime); ktime_t *etime);
......
...@@ -1486,7 +1486,7 @@ static bool radeon_pm_in_vbl(struct radeon_device *rdev) ...@@ -1486,7 +1486,7 @@ static bool radeon_pm_in_vbl(struct radeon_device *rdev)
*/ */
for (crtc = 0; (crtc < rdev->num_crtc) && in_vbl; crtc++) { for (crtc = 0; (crtc < rdev->num_crtc) && in_vbl; crtc++) {
if (rdev->pm.active_crtcs & (1 << crtc)) { if (rdev->pm.active_crtcs & (1 << crtc)) {
vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev, crtc, &vpos, &hpos, NULL, NULL); vbl_status = radeon_get_crtc_scanoutpos(rdev->ddev, crtc, 0, &vpos, &hpos, NULL, NULL);
if ((vbl_status & DRM_SCANOUTPOS_VALID) && if ((vbl_status & DRM_SCANOUTPOS_VALID) &&
!(vbl_status & DRM_SCANOUTPOS_INVBL)) !(vbl_status & DRM_SCANOUTPOS_INVBL))
in_vbl = false; in_vbl = false;
......
...@@ -845,6 +845,7 @@ struct drm_driver { ...@@ -845,6 +845,7 @@ struct drm_driver {
* *
* \param dev DRM device. * \param dev DRM device.
* \param crtc Id of the crtc to query. * \param crtc Id of the crtc to query.
* \param flags Flags from the caller (DRM_CALLED_FROM_VBLIRQ or 0).
* \param *vpos Target location for current vertical scanout position. * \param *vpos Target location for current vertical scanout position.
* \param *hpos Target location for current horizontal scanout position. * \param *hpos Target location for current horizontal scanout position.
* \param *stime Target location for timestamp taken immediately before * \param *stime Target location for timestamp taken immediately before
...@@ -867,6 +868,7 @@ struct drm_driver { ...@@ -867,6 +868,7 @@ struct drm_driver {
* *
*/ */
int (*get_scanout_position) (struct drm_device *dev, int crtc, int (*get_scanout_position) (struct drm_device *dev, int crtc,
unsigned int flags,
int *vpos, int *hpos, ktime_t *stime, int *vpos, int *hpos, ktime_t *stime,
ktime_t *etime); ktime_t *etime);
...@@ -1401,8 +1403,10 @@ extern int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, ...@@ -1401,8 +1403,10 @@ extern int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
int crtc, int *max_error, int crtc, int *max_error,
struct timeval *vblank_time, struct timeval *vblank_time,
unsigned flags, unsigned flags,
struct drm_crtc *refcrtc); const struct drm_crtc *refcrtc,
extern void drm_calc_timestamping_constants(struct drm_crtc *crtc); const struct drm_display_mode *mode);
extern void drm_calc_timestamping_constants(struct drm_crtc *crtc,
const struct drm_display_mode *mode);
extern bool extern bool
drm_mode_parse_command_line_for_connector(const char *mode_option, drm_mode_parse_command_line_for_connector(const char *mode_option,
......
...@@ -449,7 +449,7 @@ struct drm_crtc { ...@@ -449,7 +449,7 @@ struct drm_crtc {
uint16_t *gamma_store; uint16_t *gamma_store;
/* Constants needed for precise vblank and swap timestamping. */ /* Constants needed for precise vblank and swap timestamping. */
s64 framedur_ns, linedur_ns, pixeldur_ns; int framedur_ns, linedur_ns, pixeldur_ns;
/* if you are using the helper */ /* if you are using the helper */
void *helper_private; void *helper_private;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment