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 /drivers/gpu/drm/nouveau/nv04_timer.c | |
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>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv04_timer.c')
-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 |