diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_fifo_underrun.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_fifo_underrun.c | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/intel_fifo_underrun.c b/drivers/gpu/drm/i915/intel_fifo_underrun.c new file mode 100644 index 000000000000..77af512d2d35 --- /dev/null +++ b/drivers/gpu/drm/i915/intel_fifo_underrun.c | |||
@@ -0,0 +1,381 @@ | |||
1 | /* | ||
2 | * Copyright © 2014 Intel Corporation | ||
3 | * | ||
4 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
5 | * copy of this software and associated documentation files (the "Software"), | ||
6 | * to deal in the Software without restriction, including without limitation | ||
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
8 | * and/or sell copies of the Software, and to permit persons to whom the | ||
9 | * Software is furnished to do so, subject to the following conditions: | ||
10 | * | ||
11 | * The above copyright notice and this permission notice (including the next | ||
12 | * paragraph) shall be included in all copies or substantial portions of the | ||
13 | * Software. | ||
14 | * | ||
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
21 | * IN THE SOFTWARE. | ||
22 | * | ||
23 | * Authors: | ||
24 | * Daniel Vetter <daniel.vetter@ffwll.ch> | ||
25 | * | ||
26 | */ | ||
27 | |||
28 | #include "i915_drv.h" | ||
29 | #include "intel_drv.h" | ||
30 | |||
31 | /** | ||
32 | * DOC: fifo underrun handling | ||
33 | * | ||
34 | * The i915 driver checks for display fifo underruns using the interrupt signals | ||
35 | * provided by the hardware. This is enabled by default and fairly useful to | ||
36 | * debug display issues, especially watermark settings. | ||
37 | * | ||
38 | * If an underrun is detected this is logged into dmesg. To avoid flooding logs | ||
39 | * and occupying the cpu underrun interrupts are disabled after the first | ||
40 | * occurrence until the next modeset on a given pipe. | ||
41 | * | ||
42 | * Note that underrun detection on gmch platforms is a bit more ugly since there | ||
43 | * is no interrupt (despite that the signalling bit is in the PIPESTAT pipe | ||
44 | * interrupt register). Also on some other platforms underrun interrupts are | ||
45 | * shared, which means that if we detect an underrun we need to disable underrun | ||
46 | * reporting on all pipes. | ||
47 | * | ||
48 | * The code also supports underrun detection on the PCH transcoder. | ||
49 | */ | ||
50 | |||
51 | static bool ivb_can_enable_err_int(struct drm_device *dev) | ||
52 | { | ||
53 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
54 | struct intel_crtc *crtc; | ||
55 | enum pipe pipe; | ||
56 | |||
57 | assert_spin_locked(&dev_priv->irq_lock); | ||
58 | |||
59 | for_each_pipe(dev_priv, pipe) { | ||
60 | crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); | ||
61 | |||
62 | if (crtc->cpu_fifo_underrun_disabled) | ||
63 | return false; | ||
64 | } | ||
65 | |||
66 | return true; | ||
67 | } | ||
68 | |||
69 | static bool cpt_can_enable_serr_int(struct drm_device *dev) | ||
70 | { | ||
71 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
72 | enum pipe pipe; | ||
73 | struct intel_crtc *crtc; | ||
74 | |||
75 | assert_spin_locked(&dev_priv->irq_lock); | ||
76 | |||
77 | for_each_pipe(dev_priv, pipe) { | ||
78 | crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); | ||
79 | |||
80 | if (crtc->pch_fifo_underrun_disabled) | ||
81 | return false; | ||
82 | } | ||
83 | |||
84 | return true; | ||
85 | } | ||
86 | |||
87 | /** | ||
88 | * i9xx_check_fifo_underruns - check for fifo underruns | ||
89 | * @dev_priv: i915 device instance | ||
90 | * | ||
91 | * This function checks for fifo underruns on GMCH platforms. This needs to be | ||
92 | * done manually on modeset to make sure that we catch all underruns since they | ||
93 | * do not generate an interrupt by themselves on these platforms. | ||
94 | */ | ||
95 | void i9xx_check_fifo_underruns(struct drm_i915_private *dev_priv) | ||
96 | { | ||
97 | struct intel_crtc *crtc; | ||
98 | |||
99 | spin_lock_irq(&dev_priv->irq_lock); | ||
100 | |||
101 | for_each_intel_crtc(dev_priv->dev, crtc) { | ||
102 | u32 reg = PIPESTAT(crtc->pipe); | ||
103 | u32 pipestat; | ||
104 | |||
105 | if (crtc->cpu_fifo_underrun_disabled) | ||
106 | continue; | ||
107 | |||
108 | pipestat = I915_READ(reg) & 0xffff0000; | ||
109 | if ((pipestat & PIPE_FIFO_UNDERRUN_STATUS) == 0) | ||
110 | continue; | ||
111 | |||
112 | I915_WRITE(reg, pipestat | PIPE_FIFO_UNDERRUN_STATUS); | ||
113 | POSTING_READ(reg); | ||
114 | |||
115 | DRM_ERROR("pipe %c underrun\n", pipe_name(crtc->pipe)); | ||
116 | } | ||
117 | |||
118 | spin_unlock_irq(&dev_priv->irq_lock); | ||
119 | } | ||
120 | |||
121 | static void i9xx_set_fifo_underrun_reporting(struct drm_device *dev, | ||
122 | enum pipe pipe, | ||
123 | bool enable, bool old) | ||
124 | { | ||
125 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
126 | u32 reg = PIPESTAT(pipe); | ||
127 | u32 pipestat = I915_READ(reg) & 0xffff0000; | ||
128 | |||
129 | assert_spin_locked(&dev_priv->irq_lock); | ||
130 | |||
131 | if (enable) { | ||
132 | I915_WRITE(reg, pipestat | PIPE_FIFO_UNDERRUN_STATUS); | ||
133 | POSTING_READ(reg); | ||
134 | } else { | ||
135 | if (old && pipestat & PIPE_FIFO_UNDERRUN_STATUS) | ||
136 | DRM_ERROR("pipe %c underrun\n", pipe_name(pipe)); | ||
137 | } | ||
138 | } | ||
139 | |||
140 | static void ironlake_set_fifo_underrun_reporting(struct drm_device *dev, | ||
141 | enum pipe pipe, bool enable) | ||
142 | { | ||
143 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
144 | uint32_t bit = (pipe == PIPE_A) ? DE_PIPEA_FIFO_UNDERRUN : | ||
145 | DE_PIPEB_FIFO_UNDERRUN; | ||
146 | |||
147 | if (enable) | ||
148 | ironlake_enable_display_irq(dev_priv, bit); | ||
149 | else | ||
150 | ironlake_disable_display_irq(dev_priv, bit); | ||
151 | } | ||
152 | |||
153 | static void ivybridge_set_fifo_underrun_reporting(struct drm_device *dev, | ||
154 | enum pipe pipe, | ||
155 | bool enable, bool old) | ||
156 | { | ||
157 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
158 | if (enable) { | ||
159 | I915_WRITE(GEN7_ERR_INT, ERR_INT_FIFO_UNDERRUN(pipe)); | ||
160 | |||
161 | if (!ivb_can_enable_err_int(dev)) | ||
162 | return; | ||
163 | |||
164 | ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB); | ||
165 | } else { | ||
166 | ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB); | ||
167 | |||
168 | if (old && | ||
169 | I915_READ(GEN7_ERR_INT) & ERR_INT_FIFO_UNDERRUN(pipe)) { | ||
170 | DRM_ERROR("uncleared fifo underrun on pipe %c\n", | ||
171 | pipe_name(pipe)); | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | |||
176 | static void broadwell_set_fifo_underrun_reporting(struct drm_device *dev, | ||
177 | enum pipe pipe, bool enable) | ||
178 | { | ||
179 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
180 | |||
181 | assert_spin_locked(&dev_priv->irq_lock); | ||
182 | |||
183 | if (enable) | ||
184 | dev_priv->de_irq_mask[pipe] &= ~GEN8_PIPE_FIFO_UNDERRUN; | ||
185 | else | ||
186 | dev_priv->de_irq_mask[pipe] |= GEN8_PIPE_FIFO_UNDERRUN; | ||
187 | I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]); | ||
188 | POSTING_READ(GEN8_DE_PIPE_IMR(pipe)); | ||
189 | } | ||
190 | |||
191 | static void ibx_set_fifo_underrun_reporting(struct drm_device *dev, | ||
192 | enum transcoder pch_transcoder, | ||
193 | bool enable) | ||
194 | { | ||
195 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
196 | uint32_t bit = (pch_transcoder == TRANSCODER_A) ? | ||
197 | SDE_TRANSA_FIFO_UNDER : SDE_TRANSB_FIFO_UNDER; | ||
198 | |||
199 | if (enable) | ||
200 | ibx_enable_display_interrupt(dev_priv, bit); | ||
201 | else | ||
202 | ibx_disable_display_interrupt(dev_priv, bit); | ||
203 | } | ||
204 | |||
205 | static void cpt_set_fifo_underrun_reporting(struct drm_device *dev, | ||
206 | enum transcoder pch_transcoder, | ||
207 | bool enable, bool old) | ||
208 | { | ||
209 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
210 | |||
211 | if (enable) { | ||
212 | I915_WRITE(SERR_INT, | ||
213 | SERR_INT_TRANS_FIFO_UNDERRUN(pch_transcoder)); | ||
214 | |||
215 | if (!cpt_can_enable_serr_int(dev)) | ||
216 | return; | ||
217 | |||
218 | ibx_enable_display_interrupt(dev_priv, SDE_ERROR_CPT); | ||
219 | } else { | ||
220 | ibx_disable_display_interrupt(dev_priv, SDE_ERROR_CPT); | ||
221 | |||
222 | if (old && I915_READ(SERR_INT) & | ||
223 | SERR_INT_TRANS_FIFO_UNDERRUN(pch_transcoder)) { | ||
224 | DRM_ERROR("uncleared pch fifo underrun on pch transcoder %c\n", | ||
225 | transcoder_name(pch_transcoder)); | ||
226 | } | ||
227 | } | ||
228 | } | ||
229 | |||
230 | static bool __intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev, | ||
231 | enum pipe pipe, bool enable) | ||
232 | { | ||
233 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
234 | struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; | ||
235 | struct intel_crtc *intel_crtc = to_intel_crtc(crtc); | ||
236 | bool old; | ||
237 | |||
238 | assert_spin_locked(&dev_priv->irq_lock); | ||
239 | |||
240 | old = !intel_crtc->cpu_fifo_underrun_disabled; | ||
241 | intel_crtc->cpu_fifo_underrun_disabled = !enable; | ||
242 | |||
243 | if (HAS_GMCH_DISPLAY(dev)) | ||
244 | i9xx_set_fifo_underrun_reporting(dev, pipe, enable, old); | ||
245 | else if (IS_GEN5(dev) || IS_GEN6(dev)) | ||
246 | ironlake_set_fifo_underrun_reporting(dev, pipe, enable); | ||
247 | else if (IS_GEN7(dev)) | ||
248 | ivybridge_set_fifo_underrun_reporting(dev, pipe, enable, old); | ||
249 | else if (IS_GEN8(dev) || IS_GEN9(dev)) | ||
250 | broadwell_set_fifo_underrun_reporting(dev, pipe, enable); | ||
251 | |||
252 | return old; | ||
253 | } | ||
254 | |||
255 | /** | ||
256 | * intel_set_cpu_fifo_underrun_reporting - set cpu fifo underrrun reporting state | ||
257 | * @dev_priv: i915 device instance | ||
258 | * @pipe: (CPU) pipe to set state for | ||
259 | * @enable: whether underruns should be reported or not | ||
260 | * | ||
261 | * This function sets the fifo underrun state for @pipe. It is used in the | ||
262 | * modeset code to avoid false positives since on many platforms underruns are | ||
263 | * expected when disabling or enabling the pipe. | ||
264 | * | ||
265 | * Notice that on some platforms disabling underrun reports for one pipe | ||
266 | * disables for all due to shared interrupts. Actual reporting is still per-pipe | ||
267 | * though. | ||
268 | * | ||
269 | * Returns the previous state of underrun reporting. | ||
270 | */ | ||
271 | bool intel_set_cpu_fifo_underrun_reporting(struct drm_i915_private *dev_priv, | ||
272 | enum pipe pipe, bool enable) | ||
273 | { | ||
274 | unsigned long flags; | ||
275 | bool ret; | ||
276 | |||
277 | spin_lock_irqsave(&dev_priv->irq_lock, flags); | ||
278 | ret = __intel_set_cpu_fifo_underrun_reporting(dev_priv->dev, pipe, | ||
279 | enable); | ||
280 | spin_unlock_irqrestore(&dev_priv->irq_lock, flags); | ||
281 | |||
282 | return ret; | ||
283 | } | ||
284 | |||
285 | static bool | ||
286 | __cpu_fifo_underrun_reporting_enabled(struct drm_i915_private *dev_priv, | ||
287 | enum pipe pipe) | ||
288 | { | ||
289 | struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; | ||
290 | struct intel_crtc *intel_crtc = to_intel_crtc(crtc); | ||
291 | |||
292 | return !intel_crtc->cpu_fifo_underrun_disabled; | ||
293 | } | ||
294 | |||
295 | /** | ||
296 | * intel_set_pch_fifo_underrun_reporting - set PCH fifo underrun reporting state | ||
297 | * @dev_priv: i915 device instance | ||
298 | * @pch_transcoder: the PCH transcoder (same as pipe on IVB and older) | ||
299 | * @enable: whether underruns should be reported or not | ||
300 | * | ||
301 | * This function makes us disable or enable PCH fifo underruns for a specific | ||
302 | * PCH transcoder. Notice that on some PCHs (e.g. CPT/PPT), disabling FIFO | ||
303 | * underrun reporting for one transcoder may also disable all the other PCH | ||
304 | * error interruts for the other transcoders, due to the fact that there's just | ||
305 | * one interrupt mask/enable bit for all the transcoders. | ||
306 | * | ||
307 | * Returns the previous state of underrun reporting. | ||
308 | */ | ||
309 | bool intel_set_pch_fifo_underrun_reporting(struct drm_i915_private *dev_priv, | ||
310 | enum transcoder pch_transcoder, | ||
311 | bool enable) | ||
312 | { | ||
313 | struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pch_transcoder]; | ||
314 | struct intel_crtc *intel_crtc = to_intel_crtc(crtc); | ||
315 | unsigned long flags; | ||
316 | bool old; | ||
317 | |||
318 | /* | ||
319 | * NOTE: Pre-LPT has a fixed cpu pipe -> pch transcoder mapping, but LPT | ||
320 | * has only one pch transcoder A that all pipes can use. To avoid racy | ||
321 | * pch transcoder -> pipe lookups from interrupt code simply store the | ||
322 | * underrun statistics in crtc A. Since we never expose this anywhere | ||
323 | * nor use it outside of the fifo underrun code here using the "wrong" | ||
324 | * crtc on LPT won't cause issues. | ||
325 | */ | ||
326 | |||
327 | spin_lock_irqsave(&dev_priv->irq_lock, flags); | ||
328 | |||
329 | old = !intel_crtc->pch_fifo_underrun_disabled; | ||
330 | intel_crtc->pch_fifo_underrun_disabled = !enable; | ||
331 | |||
332 | if (HAS_PCH_IBX(dev_priv->dev)) | ||
333 | ibx_set_fifo_underrun_reporting(dev_priv->dev, pch_transcoder, | ||
334 | enable); | ||
335 | else | ||
336 | cpt_set_fifo_underrun_reporting(dev_priv->dev, pch_transcoder, | ||
337 | enable, old); | ||
338 | |||
339 | spin_unlock_irqrestore(&dev_priv->irq_lock, flags); | ||
340 | return old; | ||
341 | } | ||
342 | |||
343 | /** | ||
344 | * intel_pch_fifo_underrun_irq_handler - handle PCH fifo underrun interrupt | ||
345 | * @dev_priv: i915 device instance | ||
346 | * @pipe: (CPU) pipe to set state for | ||
347 | * | ||
348 | * This handles a CPU fifo underrun interrupt, generating an underrun warning | ||
349 | * into dmesg if underrun reporting is enabled and then disables the underrun | ||
350 | * interrupt to avoid an irq storm. | ||
351 | */ | ||
352 | void intel_cpu_fifo_underrun_irq_handler(struct drm_i915_private *dev_priv, | ||
353 | enum pipe pipe) | ||
354 | { | ||
355 | /* GMCH can't disable fifo underruns, filter them. */ | ||
356 | if (HAS_GMCH_DISPLAY(dev_priv->dev) && | ||
357 | !__cpu_fifo_underrun_reporting_enabled(dev_priv, pipe)) | ||
358 | return; | ||
359 | |||
360 | if (intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, false)) | ||
361 | DRM_ERROR("CPU pipe %c FIFO underrun\n", | ||
362 | pipe_name(pipe)); | ||
363 | } | ||
364 | |||
365 | /** | ||
366 | * intel_pch_fifo_underrun_irq_handler - handle PCH fifo underrun interrupt | ||
367 | * @dev_priv: i915 device instance | ||
368 | * @pch_transcoder: the PCH transcoder (same as pipe on IVB and older) | ||
369 | * | ||
370 | * This handles a PCH fifo underrun interrupt, generating an underrun warning | ||
371 | * into dmesg if underrun reporting is enabled and then disables the underrun | ||
372 | * interrupt to avoid an irq storm. | ||
373 | */ | ||
374 | void intel_pch_fifo_underrun_irq_handler(struct drm_i915_private *dev_priv, | ||
375 | enum transcoder pch_transcoder) | ||
376 | { | ||
377 | if (intel_set_pch_fifo_underrun_reporting(dev_priv, pch_transcoder, | ||
378 | false)) | ||
379 | DRM_ERROR("PCH transcoder %c FIFO underrun\n", | ||
380 | transcoder_name(pch_transcoder)); | ||
381 | } | ||