diff options
| author | Ben Skeggs <bskeggs@redhat.com> | 2011-07-03 07:16:12 -0400 |
|---|---|---|
| committer | Ben Skeggs <bskeggs@redhat.com> | 2011-09-20 02:04:36 -0400 |
| commit | 591b06d73bb8a2da879b1159342b8be192bf1119 (patch) | |
| tree | 0907df5bb26c726266d8313685fe3192ea33673f | |
| parent | 16cd399c65bc68332a860b0b572079d0316df3ca (diff) | |
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: Martin Peres <martin.peres@ensi-bourges.fr>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
| -rw-r--r-- | drivers/gpu/drm/nouveau/nv04_timer.c | 108 |
1 files changed, 82 insertions, 26 deletions
diff --git a/drivers/gpu/drm/nouveau/nv04_timer.c b/drivers/gpu/drm/nouveau/nv04_timer.c index 1d09ddd57399..afb9d4b6a029 100644 --- a/drivers/gpu/drm/nouveau/nv04_timer.c +++ b/drivers/gpu/drm/nouveau/nv04_timer.c | |||
| @@ -3,46 +3,102 @@ | |||
| 3 | #include "nouveau_drv.h" | 3 | #include "nouveau_drv.h" |
| 4 | #include "nouveau_drm.h" | 4 | #include "nouveau_drm.h" |
| 5 | 5 | ||
| 6 | static u32 | ||
| 7 | nv04_crystal_freq(struct drm_device *dev) | ||
| 8 | { | ||
| 9 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
| 10 | u32 extdev_boot0 = nv_rd32(dev, 0x101000); | ||
| 11 | int type; | ||
| 12 | |||
| 13 | type = !!(extdev_boot0 & 0x00000040); | ||
| 14 | if ((dev_priv->chipset >= 0x17 && dev_priv->chipset < 0x20) || | ||
| 15 | dev_priv->chipset >= 0x25) | ||
| 16 | type |= (extdev_boot0 & 0x00400000) ? 2 : 0; | ||
| 17 | |||
| 18 | switch (type) { | ||
| 19 | case 0: return 13500000; | ||
| 20 | case 1: return 14318180; | ||
| 21 | case 2: return 27000000; | ||
| 22 | case 3: return 25000000; | ||
| 23 | default: | ||
| 24 | break; | ||
| 25 | } | ||
| 26 | |||
| 27 | return 0; | ||
| 28 | } | ||
| 29 | |||
| 6 | int | 30 | int |
| 7 | nv04_timer_init(struct drm_device *dev) | 31 | nv04_timer_init(struct drm_device *dev) |
| 8 | { | 32 | { |
| 33 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
| 34 | u32 m, n, d; | ||
| 35 | |||
| 9 | nv_wr32(dev, NV04_PTIMER_INTR_EN_0, 0x00000000); | 36 | nv_wr32(dev, NV04_PTIMER_INTR_EN_0, 0x00000000); |
| 10 | nv_wr32(dev, NV04_PTIMER_INTR_0, 0xFFFFFFFF); | 37 | nv_wr32(dev, NV04_PTIMER_INTR_0, 0xFFFFFFFF); |
| 11 | 38 | ||
| 12 | /* Just use the pre-existing values when possible for now; these regs | 39 | /* aim for 31.25MHz, which gives us nanosecond timestamps */ |
| 13 | * are not written in nv (driver writer missed a /4 on the address), and | 40 | d = 1000000000 / 32; |
| 14 | * writing 8 and 3 to the correct regs breaks the timings on the LVDS | 41 | |
| 15 | * hardware sequencing microcode. | 42 | /* determine base clock for timer source */ |
| 16 | * A correct solution (involving calculations with the GPU PLL) can | 43 | if (dev_priv->chipset < 0x40) { |
| 17 | * be done when kernel modesetting lands | 44 | n = dev_priv->engine.pm.clock_get(dev, PLL_CORE); |
| 18 | */ | 45 | } else |
| 19 | if (!nv_rd32(dev, NV04_PTIMER_NUMERATOR) || | 46 | if (dev_priv->chipset == 0x40) { |
| 20 | !nv_rd32(dev, NV04_PTIMER_DENOMINATOR)) { | 47 | /*XXX: figure this out */ |
| 21 | nv_wr32(dev, NV04_PTIMER_NUMERATOR, 0x00000008); | 48 | n = 0; |
| 22 | nv_wr32(dev, NV04_PTIMER_DENOMINATOR, 0x00000003); | 49 | } else { |
| 50 | n = nv04_crystal_freq(dev); | ||
| 51 | m = 1; | ||
| 52 | while (n < (d * 2)) { | ||
| 53 | n += (n / m); | ||
| 54 | m++; | ||
| 55 | } | ||
| 56 | |||
| 57 | nv_wr32(dev, 0x009220, m - 1); | ||
| 23 | } | 58 | } |
| 24 | 59 | ||
| 60 | if (!n) { | ||
| 61 | NV_WARN(dev, "PTIMER: unknown input clock freq\n"); | ||
| 62 | if (!nv_rd32(dev, NV04_PTIMER_NUMERATOR) || | ||
| 63 | !nv_rd32(dev, NV04_PTIMER_DENOMINATOR)) { | ||
| 64 | nv_wr32(dev, NV04_PTIMER_NUMERATOR, 1); | ||
| 65 | nv_wr32(dev, NV04_PTIMER_DENOMINATOR, 1); | ||
| 66 | } | ||
| 67 | return 0; | ||
| 68 | } | ||
| 69 | |||
| 70 | /* reduce ratio to acceptable values */ | ||
| 71 | while (((n % 5) == 0) && ((d % 5) == 0)) { | ||
| 72 | n /= 5; | ||
| 73 | d /= 5; | ||
| 74 | } | ||
| 75 | |||
| 76 | while (((n % 2) == 0) && ((d % 2) == 0)) { | ||
| 77 | n /= 2; | ||
| 78 | d /= 2; | ||
| 79 | } | ||
| 80 | |||
| 81 | while (n > 0xffff || d > 0xffff) { | ||
| 82 | n >>= 1; | ||
| 83 | d >>= 1; | ||
| 84 | } | ||
| 85 | |||
| 86 | nv_wr32(dev, NV04_PTIMER_NUMERATOR, n); | ||
| 87 | nv_wr32(dev, NV04_PTIMER_DENOMINATOR, d); | ||
| 25 | return 0; | 88 | return 0; |
| 26 | } | 89 | } |
| 27 | 90 | ||
| 28 | uint64_t | 91 | u64 |
| 29 | nv04_timer_read(struct drm_device *dev) | 92 | nv04_timer_read(struct drm_device *dev) |
| 30 | { | 93 | { |
| 31 | uint32_t low; | 94 | u32 hi, lo; |
| 32 | /* From kmmio dumps on nv28 this looks like how the blob does this. | 95 | |
| 33 | * It reads the high dword twice, before and after. | ||
| 34 | * The only explanation seems to be that the 64-bit timer counter | ||
| 35 | * advances between high and low dword reads and may corrupt the | ||
| 36 | * result. Not confirmed. | ||
| 37 | */ | ||
| 38 | uint32_t high2 = nv_rd32(dev, NV04_PTIMER_TIME_1); | ||
| 39 | uint32_t high1; | ||
| 40 | do { | 96 | do { |
| 41 | high1 = high2; | 97 | hi = nv_rd32(dev, NV04_PTIMER_TIME_1); |
| 42 | low = nv_rd32(dev, NV04_PTIMER_TIME_0); | 98 | lo = nv_rd32(dev, NV04_PTIMER_TIME_0); |
| 43 | high2 = nv_rd32(dev, NV04_PTIMER_TIME_1); | 99 | } while (hi != nv_rd32(dev, NV04_PTIMER_TIME_1)); |
| 44 | } while (high1 != high2); | 100 | |
| 45 | return (((uint64_t)high2) << 32) | (uint64_t)low; | 101 | return ((u64)hi << 32 | lo); |
| 46 | } | 102 | } |
| 47 | 103 | ||
| 48 | void | 104 | void |
