Rev 6935 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5060 | serge | 1 | /* |
2 | * drm_irq.c IRQ and vblank support |
||
1963 | serge | 3 | * |
4 | * \author Rickard E. (Rik) Faith |
||
5 | * \author Gareth Hughes |
||
6 | */ |
||
7 | |||
8 | /* |
||
9 | * Created: Fri Mar 19 14:30:16 1999 by faith@valinux.com |
||
10 | * |
||
11 | * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. |
||
12 | * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. |
||
13 | * All Rights Reserved. |
||
14 | * |
||
15 | * Permission is hereby granted, free of charge, to any person obtaining a |
||
16 | * copy of this software and associated documentation files (the "Software"), |
||
17 | * to deal in the Software without restriction, including without limitation |
||
18 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||
19 | * and/or sell copies of the Software, and to permit persons to whom the |
||
20 | * Software is furnished to do so, subject to the following conditions: |
||
21 | * |
||
22 | * The above copyright notice and this permission notice (including the next |
||
23 | * paragraph) shall be included in all copies or substantial portions of the |
||
24 | * Software. |
||
25 | * |
||
26 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||
27 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||
28 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||
29 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR |
||
30 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
||
31 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
||
32 | * OTHER DEALINGS IN THE SOFTWARE. |
||
33 | */ |
||
34 | |||
3031 | serge | 35 | #include |
1963 | serge | 36 | //#include "drm_trace.h" |
5271 | serge | 37 | #include "drm_internal.h" |
1963 | serge | 38 | |
6935 | serge | 39 | #include |
1963 | serge | 40 | #include |
41 | |||
5271 | serge | 42 | #include |
3031 | serge | 43 | #include |
1963 | serge | 44 | |
6084 | serge | 45 | ktime_t ktime_get(void); |
46 | |||
47 | static inline ktime_t ktime_get_real(void) |
||
48 | { |
||
49 | return ktime_get(); |
||
50 | } |
||
51 | |||
52 | static inline ktime_t ktime_mono_to_real(ktime_t mono) |
||
53 | { |
||
54 | return mono; |
||
55 | } |
||
56 | |||
6131 | serge | 57 | irqreturn_t device_irq_handler(struct drm_device *dev) |
58 | { |
||
59 | return dev->driver->irq_handler(0, dev); |
||
60 | } |
||
6084 | serge | 61 | |
1963 | serge | 62 | /* Access macro for slots in vblank timestamp ringbuffer. */ |
6084 | serge | 63 | #define vblanktimestamp(dev, pipe, count) \ |
64 | ((dev)->vblank[pipe].time[(count) % DRM_VBLANKTIME_RBSIZE]) |
||
1963 | serge | 65 | |
66 | /* Retry timestamp calculation up to 3 times to satisfy |
||
67 | * drm_timestamp_precision before giving up. |
||
68 | */ |
||
69 | #define DRM_TIMESTAMP_MAXRETRIES 3 |
||
70 | |||
71 | /* Threshold in nanoseconds for detection of redundant |
||
72 | * vblank irq in drm_handle_vblank(). 1 msec should be ok. |
||
73 | */ |
||
74 | #define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000 |
||
75 | |||
5271 | serge | 76 | static bool |
6084 | serge | 77 | drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe, |
5271 | serge | 78 | struct timeval *tvblank, unsigned flags); |
79 | |||
80 | static unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */ |
||
81 | |||
5060 | serge | 82 | /* |
6084 | serge | 83 | * Default to use monotonic timestamps for wait-for-vblank and page-flip |
84 | * complete events. |
||
5060 | serge | 85 | */ |
6084 | serge | 86 | unsigned int drm_timestamp_monotonic = 1; |
1963 | serge | 87 | |
6084 | serge | 88 | static int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */ |
5060 | serge | 89 | |
6084 | serge | 90 | module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); |
91 | module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); |
||
92 | module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); |
||
93 | |||
94 | static void store_vblank(struct drm_device *dev, unsigned int pipe, |
||
95 | u32 vblank_count_inc, |
||
96 | struct timeval *t_vblank, u32 last) |
||
97 | { |
||
98 | struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; |
||
99 | u32 tslot; |
||
100 | |||
101 | assert_spin_locked(&dev->vblank_time_lock); |
||
102 | |||
103 | vblank->last = last; |
||
104 | |||
105 | /* All writers hold the spinlock, but readers are serialized by |
||
106 | * the latching of vblank->count below. |
||
107 | */ |
||
108 | tslot = vblank->count + vblank_count_inc; |
||
109 | vblanktimestamp(dev, pipe, tslot) = *t_vblank; |
||
110 | |||
111 | /* |
||
112 | * vblank timestamp updates are protected on the write side with |
||
113 | * vblank_time_lock, but on the read side done locklessly using a |
||
114 | * sequence-lock on the vblank counter. Ensure correct ordering using |
||
115 | * memory barrriers. We need the barrier both before and also after the |
||
116 | * counter update to synchronize with the next timestamp write. |
||
117 | * The read-side barriers for this are in drm_vblank_count_and_time. |
||
118 | */ |
||
119 | smp_wmb(); |
||
120 | vblank->count += vblank_count_inc; |
||
121 | smp_wmb(); |
||
122 | } |
||
123 | |||
5060 | serge | 124 | /** |
6084 | serge | 125 | * drm_reset_vblank_timestamp - reset the last timestamp to the last vblank |
126 | * @dev: DRM device |
||
127 | * @pipe: index of CRTC for which to reset the timestamp |
||
128 | * |
||
129 | * Reset the stored timestamp for the current vblank count to correspond |
||
130 | * to the last vblank occurred. |
||
131 | * |
||
132 | * Only to be called from drm_vblank_on(). |
||
133 | * |
||
134 | * Note: caller must hold dev->vbl_lock since this reads & writes |
||
135 | * device vblank fields. |
||
136 | */ |
||
137 | static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe) |
||
138 | { |
||
139 | u32 cur_vblank; |
||
140 | bool rc; |
||
141 | struct timeval t_vblank; |
||
142 | int count = DRM_TIMESTAMP_MAXRETRIES; |
||
143 | |||
144 | spin_lock(&dev->vblank_time_lock); |
||
145 | |||
146 | /* |
||
147 | * sample the current counter to avoid random jumps |
||
148 | * when drm_vblank_enable() applies the diff |
||
149 | */ |
||
150 | do { |
||
151 | cur_vblank = dev->driver->get_vblank_counter(dev, pipe); |
||
152 | rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, 0); |
||
153 | } while (cur_vblank != dev->driver->get_vblank_counter(dev, pipe) && --count > 0); |
||
154 | |||
155 | /* |
||
156 | * Only reinitialize corresponding vblank timestamp if high-precision query |
||
157 | * available and didn't fail. Otherwise reinitialize delayed at next vblank |
||
158 | * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid. |
||
159 | */ |
||
160 | if (!rc) |
||
161 | t_vblank = (struct timeval) {0, 0}; |
||
162 | |||
163 | /* |
||
164 | * +1 to make sure user will never see the same |
||
165 | * vblank counter value before and after a modeset |
||
166 | */ |
||
167 | store_vblank(dev, pipe, 1, &t_vblank, cur_vblank); |
||
168 | |||
169 | spin_unlock(&dev->vblank_time_lock); |
||
170 | } |
||
171 | |||
172 | /** |
||
173 | * drm_update_vblank_count - update the master vblank counter |
||
174 | * @dev: DRM device |
||
175 | * @pipe: counter to update |
||
176 | * |
||
177 | * Call back into the driver to update the appropriate vblank counter |
||
178 | * (specified by @pipe). Deal with wraparound, if it occurred, and |
||
179 | * update the last read value so we can deal with wraparound on the next |
||
180 | * call if necessary. |
||
181 | * |
||
182 | * Only necessary when going from off->on, to account for frames we |
||
183 | * didn't get an interrupt for. |
||
184 | * |
||
185 | * Note: caller must hold dev->vbl_lock since this reads & writes |
||
186 | * device vblank fields. |
||
187 | */ |
||
188 | static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, |
||
189 | unsigned long flags) |
||
190 | { |
||
191 | struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; |
||
192 | u32 cur_vblank, diff; |
||
193 | bool rc; |
||
194 | struct timeval t_vblank; |
||
195 | int count = DRM_TIMESTAMP_MAXRETRIES; |
||
196 | int framedur_ns = vblank->framedur_ns; |
||
197 | |||
198 | /* |
||
199 | * Interrupts were disabled prior to this call, so deal with counter |
||
200 | * wrap if needed. |
||
201 | * NOTE! It's possible we lost a full dev->max_vblank_count + 1 events |
||
202 | * here if the register is small or we had vblank interrupts off for |
||
203 | * a long time. |
||
204 | * |
||
205 | * We repeat the hardware vblank counter & timestamp query until |
||
206 | * we get consistent results. This to prevent races between gpu |
||
207 | * updating its hardware counter while we are retrieving the |
||
208 | * corresponding vblank timestamp. |
||
209 | */ |
||
210 | do { |
||
211 | cur_vblank = dev->driver->get_vblank_counter(dev, pipe); |
||
212 | rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, flags); |
||
213 | } while (cur_vblank != dev->driver->get_vblank_counter(dev, pipe) && --count > 0); |
||
214 | |||
215 | if (dev->max_vblank_count != 0) { |
||
216 | /* trust the hw counter when it's around */ |
||
217 | diff = (cur_vblank - vblank->last) & dev->max_vblank_count; |
||
218 | } else if (rc && framedur_ns) { |
||
219 | const struct timeval *t_old; |
||
220 | u64 diff_ns; |
||
221 | |||
222 | t_old = &vblanktimestamp(dev, pipe, vblank->count); |
||
223 | diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old); |
||
224 | |||
225 | /* |
||
226 | * Figure out how many vblanks we've missed based |
||
227 | * on the difference in the timestamps and the |
||
228 | * frame/field duration. |
||
229 | */ |
||
230 | diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns); |
||
231 | |||
232 | if (diff == 0 && flags & DRM_CALLED_FROM_VBLIRQ) |
||
233 | DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored." |
||
234 | " diff_ns = %lld, framedur_ns = %d)\n", |
||
235 | pipe, (long long) diff_ns, framedur_ns); |
||
236 | } else { |
||
237 | /* some kind of default for drivers w/o accurate vbl timestamping */ |
||
238 | diff = (flags & DRM_CALLED_FROM_VBLIRQ) != 0; |
||
239 | } |
||
240 | |||
6320 | serge | 241 | /* |
242 | * Within a drm_vblank_pre_modeset - drm_vblank_post_modeset |
||
243 | * interval? If so then vblank irqs keep running and it will likely |