Commit 591b06d7 authored by Ben Skeggs's avatar Ben Skeggs

drm/nouveau/tmr: calibrate for ns timestamps on init

We previously assumed (incorrectly a lot of the time) that PTIMER would
be programmed at a frequency which'd give its 64-bit timestamps in
nanoseconds.

By programming PTIMER ourselves, we avoid this problem.
Reviewed-by: default avatarMartin Peres <martin.peres@ensi-bourges.fr>
Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
parent 16cd399c
......@@ -3,46 +3,102 @@
#include "nouveau_drv.h"
#include "nouveau_drm.h"
static u32
nv04_crystal_freq(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
u32 extdev_boot0 = nv_rd32(dev, 0x101000);
int type;
type = !!(extdev_boot0 & 0x00000040);
if ((dev_priv->chipset >= 0x17 && dev_priv->chipset < 0x20) ||
dev_priv->chipset >= 0x25)
type |= (extdev_boot0 & 0x00400000) ? 2 : 0;
switch (type) {
case 0: return 13500000;
case 1: return 14318180;
case 2: return 27000000;
case 3: return 25000000;
default:
break;
}
return 0;
}
int
nv04_timer_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
u32 m, n, d;
nv_wr32(dev, NV04_PTIMER_INTR_EN_0, 0x00000000);
nv_wr32(dev, NV04_PTIMER_INTR_0, 0xFFFFFFFF);
/* Just use the pre-existing values when possible for now; these regs
* are not written in nv (driver writer missed a /4 on the address), and
* writing 8 and 3 to the correct regs breaks the timings on the LVDS
* hardware sequencing microcode.
* A correct solution (involving calculations with the GPU PLL) can
* be done when kernel modesetting lands
*/
if (!nv_rd32(dev, NV04_PTIMER_NUMERATOR) ||
!nv_rd32(dev, NV04_PTIMER_DENOMINATOR)) {
nv_wr32(dev, NV04_PTIMER_NUMERATOR, 0x00000008);
nv_wr32(dev, NV04_PTIMER_DENOMINATOR, 0x00000003);
/* aim for 31.25MHz, which gives us nanosecond timestamps */
d = 1000000000 / 32;
/* determine base clock for timer source */
if (dev_priv->chipset < 0x40) {
n = dev_priv->engine.pm.clock_get(dev, PLL_CORE);
} else
if (dev_priv->chipset == 0x40) {
/*XXX: figure this out */
n = 0;
} else {
n = nv04_crystal_freq(dev);
m = 1;
while (n < (d * 2)) {
n += (n / m);
m++;
}
nv_wr32(dev, 0x009220, m - 1);
}
if (!n) {
NV_WARN(dev, "PTIMER: unknown input clock freq\n");
if (!nv_rd32(dev, NV04_PTIMER_NUMERATOR) ||
!nv_rd32(dev, NV04_PTIMER_DENOMINATOR)) {
nv_wr32(dev, NV04_PTIMER_NUMERATOR, 1);
nv_wr32(dev, NV04_PTIMER_DENOMINATOR, 1);
}
return 0;
}
/* reduce ratio to acceptable values */
while (((n % 5) == 0) && ((d % 5) == 0)) {
n /= 5;
d /= 5;
}
while (((n % 2) == 0) && ((d % 2) == 0)) {
n /= 2;
d /= 2;
}
while (n > 0xffff || d > 0xffff) {
n >>= 1;
d >>= 1;
}
nv_wr32(dev, NV04_PTIMER_NUMERATOR, n);
nv_wr32(dev, NV04_PTIMER_DENOMINATOR, d);
return 0;
}
uint64_t
u64
nv04_timer_read(struct drm_device *dev)
{
uint32_t low;
/* From kmmio dumps on nv28 this looks like how the blob does this.
* It reads the high dword twice, before and after.
* The only explanation seems to be that the 64-bit timer counter
* advances between high and low dword reads and may corrupt the
* result. Not confirmed.
*/
uint32_t high2 = nv_rd32(dev, NV04_PTIMER_TIME_1);
uint32_t high1;
u32 hi, lo;
do {
high1 = high2;
low = nv_rd32(dev, NV04_PTIMER_TIME_0);
high2 = nv_rd32(dev, NV04_PTIMER_TIME_1);
} while (high1 != high2);
return (((uint64_t)high2) << 32) | (uint64_t)low;
hi = nv_rd32(dev, NV04_PTIMER_TIME_1);
lo = nv_rd32(dev, NV04_PTIMER_TIME_0);
} while (hi != nv_rd32(dev, NV04_PTIMER_TIME_1));
return ((u64)hi << 32 | lo);
}
void
......
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