diff options
Diffstat (limited to 'drivers/gpu/drm/drm_irq.c')
-rw-r--r-- | drivers/gpu/drm/drm_irq.c | 109 |
1 files changed, 65 insertions, 44 deletions
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index af9662e58272..b50fa0afd907 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c | |||
@@ -74,6 +74,36 @@ module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); | |||
74 | module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); | 74 | module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); |
75 | module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); | 75 | module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); |
76 | 76 | ||
77 | static void store_vblank(struct drm_device *dev, int crtc, | ||
78 | u32 vblank_count_inc, | ||
79 | struct timeval *t_vblank) | ||
80 | { | ||
81 | struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; | ||
82 | u32 tslot; | ||
83 | |||
84 | assert_spin_locked(&dev->vblank_time_lock); | ||
85 | |||
86 | if (t_vblank) { | ||
87 | /* All writers hold the spinlock, but readers are serialized by | ||
88 | * the latching of vblank->count below. | ||
89 | */ | ||
90 | tslot = vblank->count + vblank_count_inc; | ||
91 | vblanktimestamp(dev, crtc, tslot) = *t_vblank; | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * vblank timestamp updates are protected on the write side with | ||
96 | * vblank_time_lock, but on the read side done locklessly using a | ||
97 | * sequence-lock on the vblank counter. Ensure correct ordering using | ||
98 | * memory barrriers. We need the barrier both before and also after the | ||
99 | * counter update to synchronize with the next timestamp write. | ||
100 | * The read-side barriers for this are in drm_vblank_count_and_time. | ||
101 | */ | ||
102 | smp_wmb(); | ||
103 | vblank->count += vblank_count_inc; | ||
104 | smp_wmb(); | ||
105 | } | ||
106 | |||
77 | /** | 107 | /** |
78 | * drm_update_vblank_count - update the master vblank counter | 108 | * drm_update_vblank_count - update the master vblank counter |
79 | * @dev: DRM device | 109 | * @dev: DRM device |
@@ -93,14 +123,14 @@ module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); | |||
93 | static void drm_update_vblank_count(struct drm_device *dev, int crtc) | 123 | static void drm_update_vblank_count(struct drm_device *dev, int crtc) |
94 | { | 124 | { |
95 | struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; | 125 | struct drm_vblank_crtc *vblank = &dev->vblank[crtc]; |
96 | u32 cur_vblank, diff, tslot; | 126 | u32 cur_vblank, diff; |
97 | bool rc; | 127 | bool rc; |
98 | struct timeval t_vblank; | 128 | struct timeval t_vblank; |
99 | 129 | ||
100 | /* | 130 | /* |
101 | * Interrupts were disabled prior to this call, so deal with counter | 131 | * Interrupts were disabled prior to this call, so deal with counter |
102 | * wrap if needed. | 132 | * wrap if needed. |
103 | * NOTE! It's possible we lost a full dev->max_vblank_count events | 133 | * NOTE! It's possible we lost a full dev->max_vblank_count + 1 events |
104 | * here if the register is small or we had vblank interrupts off for | 134 | * here if the register is small or we had vblank interrupts off for |
105 | * a long time. | 135 | * a long time. |
106 | * | 136 | * |
@@ -117,7 +147,7 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc) | |||
117 | /* Deal with counter wrap */ | 147 | /* Deal with counter wrap */ |
118 | diff = cur_vblank - vblank->last; | 148 | diff = cur_vblank - vblank->last; |
119 | if (cur_vblank < vblank->last) { | 149 | if (cur_vblank < vblank->last) { |
120 | diff += dev->max_vblank_count; | 150 | diff += dev->max_vblank_count + 1; |
121 | 151 | ||
122 | DRM_DEBUG("last_vblank[%d]=0x%x, cur_vblank=0x%x => diff=0x%x\n", | 152 | DRM_DEBUG("last_vblank[%d]=0x%x, cur_vblank=0x%x => diff=0x%x\n", |
123 | crtc, vblank->last, cur_vblank, diff); | 153 | crtc, vblank->last, cur_vblank, diff); |
@@ -129,17 +159,15 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc) | |||
129 | if (diff == 0) | 159 | if (diff == 0) |
130 | return; | 160 | return; |
131 | 161 | ||
132 | /* Reinitialize corresponding vblank timestamp if high-precision query | 162 | /* |
133 | * available. Skip this step if query unsupported or failed. Will | 163 | * Only reinitialize corresponding vblank timestamp if high-precision query |
134 | * reinitialize delayed at next vblank interrupt in that case and | 164 | * available and didn't fail. Otherwise reinitialize delayed at next vblank |
135 | * assign 0 for now, to mark the vblanktimestamp as invalid. | 165 | * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid. |
136 | */ | 166 | */ |
137 | tslot = atomic_read(&vblank->count) + diff; | 167 | if (!rc) |
138 | vblanktimestamp(dev, crtc, tslot) = rc ? t_vblank : (struct timeval) {0, 0}; | 168 | t_vblank = (struct timeval) {0, 0}; |
139 | 169 | ||
140 | smp_mb__before_atomic(); | 170 | store_vblank(dev, crtc, diff, &t_vblank); |
141 | atomic_add(diff, &vblank->count); | ||
142 | smp_mb__after_atomic(); | ||
143 | } | 171 | } |
144 | 172 | ||
145 | /* | 173 | /* |
@@ -217,7 +245,7 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc) | |||
217 | /* Compute time difference to stored timestamp of last vblank | 245 | /* Compute time difference to stored timestamp of last vblank |
218 | * as updated by last invocation of drm_handle_vblank() in vblank irq. | 246 | * as updated by last invocation of drm_handle_vblank() in vblank irq. |
219 | */ | 247 | */ |
220 | vblcount = atomic_read(&vblank->count); | 248 | vblcount = vblank->count; |
221 | diff_ns = timeval_to_ns(&tvblank) - | 249 | diff_ns = timeval_to_ns(&tvblank) - |
222 | timeval_to_ns(&vblanktimestamp(dev, crtc, vblcount)); | 250 | timeval_to_ns(&vblanktimestamp(dev, crtc, vblcount)); |
223 | 251 | ||
@@ -233,17 +261,8 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc) | |||
233 | * available. In that case we can't account for this and just | 261 | * available. In that case we can't account for this and just |
234 | * hope for the best. | 262 | * hope for the best. |
235 | */ | 263 | */ |
236 | if (vblrc && (abs64(diff_ns) > 1000000)) { | 264 | if (vblrc && (abs64(diff_ns) > 1000000)) |
237 | /* Store new timestamp in ringbuffer. */ | 265 | store_vblank(dev, crtc, 1, &tvblank); |
238 | vblanktimestamp(dev, crtc, vblcount + 1) = tvblank; | ||
239 | |||
240 | /* Increment cooked vblank count. This also atomically commits | ||
241 | * the timestamp computed above. | ||
242 | */ | ||
243 | smp_mb__before_atomic(); | ||
244 | atomic_inc(&vblank->count); | ||
245 | smp_mb__after_atomic(); | ||
246 | } | ||
247 | 266 | ||
248 | spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); | 267 | spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); |
249 | } | 268 | } |
@@ -336,6 +355,13 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) | |||
336 | else | 355 | else |
337 | DRM_INFO("No driver support for vblank timestamp query.\n"); | 356 | DRM_INFO("No driver support for vblank timestamp query.\n"); |
338 | 357 | ||
358 | /* Must have precise timestamping for reliable vblank instant disable */ | ||
359 | if (dev->vblank_disable_immediate && !dev->driver->get_vblank_timestamp) { | ||
360 | dev->vblank_disable_immediate = false; | ||
361 | DRM_INFO("Setting vblank_disable_immediate to false because " | ||
362 | "get_vblank_timestamp == NULL\n"); | ||
363 | } | ||
364 | |||
339 | dev->vblank_disable_allowed = false; | 365 | dev->vblank_disable_allowed = false; |
340 | 366 | ||
341 | return 0; | 367 | return 0; |
@@ -851,7 +877,7 @@ u32 drm_vblank_count(struct drm_device *dev, int crtc) | |||
851 | 877 | ||
852 | if (WARN_ON(crtc >= dev->num_crtcs)) | 878 | if (WARN_ON(crtc >= dev->num_crtcs)) |
853 | return 0; | 879 | return 0; |
854 | return atomic_read(&vblank->count); | 880 | return vblank->count; |
855 | } | 881 | } |
856 | EXPORT_SYMBOL(drm_vblank_count); | 882 | EXPORT_SYMBOL(drm_vblank_count); |
857 | 883 | ||
@@ -896,16 +922,17 @@ u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc, | |||
896 | if (WARN_ON(crtc >= dev->num_crtcs)) | 922 | if (WARN_ON(crtc >= dev->num_crtcs)) |
897 | return 0; | 923 | return 0; |
898 | 924 | ||
899 | /* Read timestamp from slot of _vblank_time ringbuffer | 925 | /* |
900 | * that corresponds to current vblank count. Retry if | 926 | * Vblank timestamps are read lockless. To ensure consistency the vblank |
901 | * count has incremented during readout. This works like | 927 | * counter is rechecked and ordering is ensured using memory barriers. |
902 | * a seqlock. | 928 | * This works like a seqlock. The write-side barriers are in store_vblank. |
903 | */ | 929 | */ |
904 | do { | 930 | do { |
905 | cur_vblank = atomic_read(&vblank->count); | 931 | cur_vblank = vblank->count; |
932 | smp_rmb(); | ||
906 | *vblanktime = vblanktimestamp(dev, crtc, cur_vblank); | 933 | *vblanktime = vblanktimestamp(dev, crtc, cur_vblank); |
907 | smp_rmb(); | 934 | smp_rmb(); |
908 | } while (cur_vblank != atomic_read(&vblank->count)); | 935 | } while (cur_vblank != vblank->count); |
909 | 936 | ||
910 | return cur_vblank; | 937 | return cur_vblank; |
911 | } | 938 | } |
@@ -1029,6 +1056,9 @@ int drm_vblank_get(struct drm_device *dev, int crtc) | |||
1029 | unsigned long irqflags; | 1056 | unsigned long irqflags; |
1030 | int ret = 0; | 1057 | int ret = 0; |
1031 | 1058 | ||
1059 | if (!dev->num_crtcs) | ||
1060 | return -EINVAL; | ||
1061 | |||
1032 | if (WARN_ON(crtc >= dev->num_crtcs)) | 1062 | if (WARN_ON(crtc >= dev->num_crtcs)) |
1033 | return -EINVAL; | 1063 | return -EINVAL; |
1034 | 1064 | ||
@@ -1714,7 +1744,7 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc) | |||
1714 | */ | 1744 | */ |
1715 | 1745 | ||
1716 | /* Get current timestamp and count. */ | 1746 | /* Get current timestamp and count. */ |
1717 | vblcount = atomic_read(&vblank->count); | 1747 | vblcount = vblank->count; |
1718 | drm_get_last_vbltimestamp(dev, crtc, &tvblank, DRM_CALLED_FROM_VBLIRQ); | 1748 | drm_get_last_vbltimestamp(dev, crtc, &tvblank, DRM_CALLED_FROM_VBLIRQ); |
1719 | 1749 | ||
1720 | /* Compute time difference to timestamp of last vblank */ | 1750 | /* Compute time difference to timestamp of last vblank */ |
@@ -1730,20 +1760,11 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc) | |||
1730 | * e.g., due to spurious vblank interrupts. We need to | 1760 | * e.g., due to spurious vblank interrupts. We need to |
1731 | * ignore those for accounting. | 1761 | * ignore those for accounting. |
1732 | */ | 1762 | */ |
1733 | if (abs64(diff_ns) > DRM_REDUNDANT_VBLIRQ_THRESH_NS) { | 1763 | if (abs64(diff_ns) > DRM_REDUNDANT_VBLIRQ_THRESH_NS) |
1734 | /* Store new timestamp in ringbuffer. */ | 1764 | store_vblank(dev, crtc, 1, &tvblank); |
1735 | vblanktimestamp(dev, crtc, vblcount + 1) = tvblank; | 1765 | else |
1736 | |||
1737 | /* Increment cooked vblank count. This also atomically commits | ||
1738 | * the timestamp computed above. | ||
1739 | */ | ||
1740 | smp_mb__before_atomic(); | ||
1741 | atomic_inc(&vblank->count); | ||
1742 | smp_mb__after_atomic(); | ||
1743 | } else { | ||
1744 | DRM_DEBUG("crtc %d: Redundant vblirq ignored. diff_ns = %d\n", | 1766 | DRM_DEBUG("crtc %d: Redundant vblirq ignored. diff_ns = %d\n", |
1745 | crtc, (int) diff_ns); | 1767 | crtc, (int) diff_ns); |
1746 | } | ||
1747 | 1768 | ||
1748 | spin_unlock(&dev->vblank_time_lock); | 1769 | spin_unlock(&dev->vblank_time_lock); |
1749 | 1770 | ||