1 | /* |
2 | * Copyright 2008 Advanced Micro Devices, Inc. |
3 | * Copyright 2008 Red Hat Inc. |
4 | * Copyright 2009 Jerome Glisse. |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a |
7 | * copy of this software and associated documentation files (the "Software"), |
8 | * to deal in the Software without restriction, including without limitation |
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
10 | * and/or sell copies of the Software, and to permit persons to whom the |
11 | * Software is furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be included in |
14 | * all copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
19 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
20 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
21 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
22 | * OTHER DEALINGS IN THE SOFTWARE. |
23 | * |
24 | * Authors: Dave Airlie |
25 | * Alex Deucher |
26 | * Jerome Glisse |
27 | */ |
28 | |
29 | #include <linux/pci.h> |
30 | #include <linux/pm_runtime.h> |
31 | |
32 | #include <drm/drm_device.h> |
33 | #include <drm/drm_drv.h> |
34 | #include <drm/drm_probe_helper.h> |
35 | #include <drm/drm_vblank.h> |
36 | #include <drm/radeon_drm.h> |
37 | |
38 | #include "atom.h" |
39 | #include "radeon.h" |
40 | #include "radeon_kms.h" |
41 | #include "radeon_reg.h" |
42 | |
43 | |
44 | #define RADEON_WAIT_IDLE_TIMEOUT 200 |
45 | |
46 | /* |
47 | * radeon_driver_irq_handler_kms - irq handler for KMS |
48 | * |
49 | * This is the irq handler for the radeon KMS driver (all asics). |
50 | * radeon_irq_process is a macro that points to the per-asic |
51 | * irq handler callback. |
52 | */ |
53 | static irqreturn_t radeon_driver_irq_handler_kms(int irq, void *arg) |
54 | { |
55 | struct drm_device *dev = (struct drm_device *) arg; |
56 | struct radeon_device *rdev = dev->dev_private; |
57 | irqreturn_t ret; |
58 | |
59 | ret = radeon_irq_process(rdev); |
60 | if (ret == IRQ_HANDLED) |
61 | pm_runtime_mark_last_busy(dev: dev->dev); |
62 | return ret; |
63 | } |
64 | |
65 | /* |
66 | * Handle hotplug events outside the interrupt handler proper. |
67 | */ |
68 | /** |
69 | * radeon_hotplug_work_func - display hotplug work handler |
70 | * |
71 | * @work: work struct |
72 | * |
73 | * This is the hot plug event work handler (all asics). |
74 | * The work gets scheduled from the irq handler if there |
75 | * was a hot plug interrupt. It walks the connector table |
76 | * and calls the hotplug handler for each one, then sends |
77 | * a drm hotplug event to alert userspace. |
78 | */ |
79 | static void radeon_hotplug_work_func(struct work_struct *work) |
80 | { |
81 | struct radeon_device *rdev = container_of(work, struct radeon_device, |
82 | hotplug_work.work); |
83 | struct drm_device *dev = rdev->ddev; |
84 | struct drm_mode_config *mode_config = &dev->mode_config; |
85 | struct drm_connector *connector; |
86 | |
87 | /* we can race here at startup, some boards seem to trigger |
88 | * hotplug irqs when they shouldn't. */ |
89 | if (!rdev->mode_info.mode_config_initialized) |
90 | return; |
91 | |
92 | mutex_lock(&mode_config->mutex); |
93 | list_for_each_entry(connector, &mode_config->connector_list, head) |
94 | radeon_connector_hotplug(connector); |
95 | mutex_unlock(lock: &mode_config->mutex); |
96 | /* Just fire off a uevent and let userspace tell us what to do */ |
97 | drm_helper_hpd_irq_event(dev); |
98 | } |
99 | |
100 | static void radeon_dp_work_func(struct work_struct *work) |
101 | { |
102 | struct radeon_device *rdev = container_of(work, struct radeon_device, |
103 | dp_work); |
104 | struct drm_device *dev = rdev->ddev; |
105 | struct drm_mode_config *mode_config = &dev->mode_config; |
106 | struct drm_connector *connector; |
107 | |
108 | mutex_lock(&mode_config->mutex); |
109 | list_for_each_entry(connector, &mode_config->connector_list, head) |
110 | radeon_connector_hotplug(connector); |
111 | mutex_unlock(lock: &mode_config->mutex); |
112 | } |
113 | |
114 | /** |
115 | * radeon_driver_irq_preinstall_kms - drm irq preinstall callback |
116 | * |
117 | * @dev: drm dev pointer |
118 | * |
119 | * Gets the hw ready to enable irqs (all asics). |
120 | * This function disables all interrupt sources on the GPU. |
121 | */ |
122 | static void radeon_driver_irq_preinstall_kms(struct drm_device *dev) |
123 | { |
124 | struct radeon_device *rdev = dev->dev_private; |
125 | unsigned long irqflags; |
126 | unsigned i; |
127 | |
128 | spin_lock_irqsave(&rdev->irq.lock, irqflags); |
129 | /* Disable *all* interrupts */ |
130 | for (i = 0; i < RADEON_NUM_RINGS; i++) |
131 | atomic_set(v: &rdev->irq.ring_int[i], i: 0); |
132 | rdev->irq.dpm_thermal = false; |
133 | for (i = 0; i < RADEON_MAX_HPD_PINS; i++) |
134 | rdev->irq.hpd[i] = false; |
135 | for (i = 0; i < RADEON_MAX_CRTCS; i++) { |
136 | rdev->irq.crtc_vblank_int[i] = false; |
137 | atomic_set(v: &rdev->irq.pflip[i], i: 0); |
138 | rdev->irq.afmt[i] = false; |
139 | } |
140 | radeon_irq_set(rdev); |
141 | spin_unlock_irqrestore(lock: &rdev->irq.lock, flags: irqflags); |
142 | /* Clear bits */ |
143 | radeon_irq_process(rdev); |
144 | } |
145 | |
146 | /** |
147 | * radeon_driver_irq_postinstall_kms - drm irq preinstall callback |
148 | * |
149 | * @dev: drm dev pointer |
150 | * |
151 | * Handles stuff to be done after enabling irqs (all asics). |
152 | * Returns 0 on success. |
153 | */ |
154 | static int radeon_driver_irq_postinstall_kms(struct drm_device *dev) |
155 | { |
156 | struct radeon_device *rdev = dev->dev_private; |
157 | |
158 | if (ASIC_IS_AVIVO(rdev)) |
159 | dev->max_vblank_count = 0x00ffffff; |
160 | else |
161 | dev->max_vblank_count = 0x001fffff; |
162 | |
163 | return 0; |
164 | } |
165 | |
166 | /** |
167 | * radeon_driver_irq_uninstall_kms - drm irq uninstall callback |
168 | * |
169 | * @dev: drm dev pointer |
170 | * |
171 | * This function disables all interrupt sources on the GPU (all asics). |
172 | */ |
173 | static void radeon_driver_irq_uninstall_kms(struct drm_device *dev) |
174 | { |
175 | struct radeon_device *rdev = dev->dev_private; |
176 | unsigned long irqflags; |
177 | unsigned i; |
178 | |
179 | if (rdev == NULL) { |
180 | return; |
181 | } |
182 | spin_lock_irqsave(&rdev->irq.lock, irqflags); |
183 | /* Disable *all* interrupts */ |
184 | for (i = 0; i < RADEON_NUM_RINGS; i++) |
185 | atomic_set(v: &rdev->irq.ring_int[i], i: 0); |
186 | rdev->irq.dpm_thermal = false; |
187 | for (i = 0; i < RADEON_MAX_HPD_PINS; i++) |
188 | rdev->irq.hpd[i] = false; |
189 | for (i = 0; i < RADEON_MAX_CRTCS; i++) { |
190 | rdev->irq.crtc_vblank_int[i] = false; |
191 | atomic_set(v: &rdev->irq.pflip[i], i: 0); |
192 | rdev->irq.afmt[i] = false; |
193 | } |
194 | radeon_irq_set(rdev); |
195 | spin_unlock_irqrestore(lock: &rdev->irq.lock, flags: irqflags); |
196 | } |
197 | |
198 | static int radeon_irq_install(struct radeon_device *rdev, int irq) |
199 | { |
200 | struct drm_device *dev = rdev->ddev; |
201 | int ret; |
202 | |
203 | if (irq == IRQ_NOTCONNECTED) |
204 | return -ENOTCONN; |
205 | |
206 | radeon_driver_irq_preinstall_kms(dev); |
207 | |
208 | /* PCI devices require shared interrupts. */ |
209 | ret = request_irq(irq, handler: radeon_driver_irq_handler_kms, |
210 | IRQF_SHARED, name: dev->driver->name, dev); |
211 | if (ret) |
212 | return ret; |
213 | |
214 | radeon_driver_irq_postinstall_kms(dev); |
215 | |
216 | return 0; |
217 | } |
218 | |
219 | static void radeon_irq_uninstall(struct radeon_device *rdev) |
220 | { |
221 | struct drm_device *dev = rdev->ddev; |
222 | struct pci_dev *pdev = to_pci_dev(dev->dev); |
223 | |
224 | radeon_driver_irq_uninstall_kms(dev); |
225 | free_irq(pdev->irq, dev); |
226 | } |
227 | |
228 | /** |
229 | * radeon_msi_ok - asic specific msi checks |
230 | * |
231 | * @rdev: radeon device pointer |
232 | * |
233 | * Handles asic specific MSI checks to determine if |
234 | * MSIs should be enabled on a particular chip (all asics). |
235 | * Returns true if MSIs should be enabled, false if MSIs |
236 | * should not be enabled. |
237 | */ |
238 | static bool radeon_msi_ok(struct radeon_device *rdev) |
239 | { |
240 | /* RV370/RV380 was first asic with MSI support */ |
241 | if (rdev->family < CHIP_RV380) |
242 | return false; |
243 | |
244 | /* MSIs don't work on AGP */ |
245 | if (rdev->flags & RADEON_IS_AGP) |
246 | return false; |
247 | |
248 | /* |
249 | * Older chips have a HW limitation, they can only generate 40 bits |
250 | * of address for "64-bit" MSIs which breaks on some platforms, notably |
251 | * IBM POWER servers, so we limit them |
252 | */ |
253 | if (rdev->family < CHIP_BONAIRE) { |
254 | dev_info(rdev->dev, "radeon: MSI limited to 32-bit\n" ); |
255 | rdev->pdev->no_64bit_msi = 1; |
256 | } |
257 | |
258 | /* force MSI on */ |
259 | if (radeon_msi == 1) |
260 | return true; |
261 | else if (radeon_msi == 0) |
262 | return false; |
263 | |
264 | /* Quirks */ |
265 | /* HP RS690 only seems to work with MSIs. */ |
266 | if ((rdev->pdev->device == 0x791f) && |
267 | (rdev->pdev->subsystem_vendor == 0x103c) && |
268 | (rdev->pdev->subsystem_device == 0x30c2)) |
269 | return true; |
270 | |
271 | /* Dell RS690 only seems to work with MSIs. */ |
272 | if ((rdev->pdev->device == 0x791f) && |
273 | (rdev->pdev->subsystem_vendor == 0x1028) && |
274 | (rdev->pdev->subsystem_device == 0x01fc)) |
275 | return true; |
276 | |
277 | /* Dell RS690 only seems to work with MSIs. */ |
278 | if ((rdev->pdev->device == 0x791f) && |
279 | (rdev->pdev->subsystem_vendor == 0x1028) && |
280 | (rdev->pdev->subsystem_device == 0x01fd)) |
281 | return true; |
282 | |
283 | /* Gateway RS690 only seems to work with MSIs. */ |
284 | if ((rdev->pdev->device == 0x791f) && |
285 | (rdev->pdev->subsystem_vendor == 0x107b) && |
286 | (rdev->pdev->subsystem_device == 0x0185)) |
287 | return true; |
288 | |
289 | /* try and enable MSIs by default on all RS690s */ |
290 | if (rdev->family == CHIP_RS690) |
291 | return true; |
292 | |
293 | /* RV515 seems to have MSI issues where it loses |
294 | * MSI rearms occasionally. This leads to lockups and freezes. |
295 | * disable it by default. |
296 | */ |
297 | if (rdev->family == CHIP_RV515) |
298 | return false; |
299 | if (rdev->flags & RADEON_IS_IGP) { |
300 | /* APUs work fine with MSIs */ |
301 | if (rdev->family >= CHIP_PALM) |
302 | return true; |
303 | /* lots of IGPs have problems with MSIs */ |
304 | return false; |
305 | } |
306 | |
307 | return true; |
308 | } |
309 | |
310 | /** |
311 | * radeon_irq_kms_init - init driver interrupt info |
312 | * |
313 | * @rdev: radeon device pointer |
314 | * |
315 | * Sets up the work irq handlers, vblank init, MSIs, etc. (all asics). |
316 | * Returns 0 for success, error for failure. |
317 | */ |
318 | int radeon_irq_kms_init(struct radeon_device *rdev) |
319 | { |
320 | int r = 0; |
321 | |
322 | spin_lock_init(&rdev->irq.lock); |
323 | |
324 | /* Disable vblank irqs aggressively for power-saving */ |
325 | rdev->ddev->vblank_disable_immediate = true; |
326 | |
327 | r = drm_vblank_init(dev: rdev->ddev, num_crtcs: rdev->num_crtc); |
328 | if (r) { |
329 | return r; |
330 | } |
331 | |
332 | /* enable msi */ |
333 | rdev->msi_enabled = 0; |
334 | |
335 | if (radeon_msi_ok(rdev)) { |
336 | int ret = pci_enable_msi(dev: rdev->pdev); |
337 | if (!ret) { |
338 | rdev->msi_enabled = 1; |
339 | dev_info(rdev->dev, "radeon: using MSI.\n" ); |
340 | } |
341 | } |
342 | |
343 | INIT_DELAYED_WORK(&rdev->hotplug_work, radeon_hotplug_work_func); |
344 | INIT_WORK(&rdev->dp_work, radeon_dp_work_func); |
345 | INIT_WORK(&rdev->audio_work, r600_audio_update_hdmi); |
346 | |
347 | rdev->irq.installed = true; |
348 | r = radeon_irq_install(rdev, irq: rdev->pdev->irq); |
349 | if (r) { |
350 | rdev->irq.installed = false; |
351 | flush_delayed_work(dwork: &rdev->hotplug_work); |
352 | return r; |
353 | } |
354 | |
355 | DRM_INFO("radeon: irq initialized.\n" ); |
356 | return 0; |
357 | } |
358 | |
359 | /** |
360 | * radeon_irq_kms_fini - tear down driver interrupt info |
361 | * |
362 | * @rdev: radeon device pointer |
363 | * |
364 | * Tears down the work irq handlers, vblank handlers, MSIs, etc. (all asics). |
365 | */ |
366 | void radeon_irq_kms_fini(struct radeon_device *rdev) |
367 | { |
368 | if (rdev->irq.installed) { |
369 | radeon_irq_uninstall(rdev); |
370 | rdev->irq.installed = false; |
371 | if (rdev->msi_enabled) |
372 | pci_disable_msi(dev: rdev->pdev); |
373 | flush_delayed_work(dwork: &rdev->hotplug_work); |
374 | } |
375 | } |
376 | |
377 | /** |
378 | * radeon_irq_kms_sw_irq_get - enable software interrupt |
379 | * |
380 | * @rdev: radeon device pointer |
381 | * @ring: ring whose interrupt you want to enable |
382 | * |
383 | * Enables the software interrupt for a specific ring (all asics). |
384 | * The software interrupt is generally used to signal a fence on |
385 | * a particular ring. |
386 | */ |
387 | void radeon_irq_kms_sw_irq_get(struct radeon_device *rdev, int ring) |
388 | { |
389 | unsigned long irqflags; |
390 | |
391 | if (!rdev->irq.installed) |
392 | return; |
393 | |
394 | if (atomic_inc_return(v: &rdev->irq.ring_int[ring]) == 1) { |
395 | spin_lock_irqsave(&rdev->irq.lock, irqflags); |
396 | radeon_irq_set(rdev); |
397 | spin_unlock_irqrestore(lock: &rdev->irq.lock, flags: irqflags); |
398 | } |
399 | } |
400 | |
401 | /** |
402 | * radeon_irq_kms_sw_irq_get_delayed - enable software interrupt |
403 | * |
404 | * @rdev: radeon device pointer |
405 | * @ring: ring whose interrupt you want to enable |
406 | * |
407 | * Enables the software interrupt for a specific ring (all asics). |
408 | * The software interrupt is generally used to signal a fence on |
409 | * a particular ring. |
410 | */ |
411 | bool radeon_irq_kms_sw_irq_get_delayed(struct radeon_device *rdev, int ring) |
412 | { |
413 | return atomic_inc_return(v: &rdev->irq.ring_int[ring]) == 1; |
414 | } |
415 | |
416 | /** |
417 | * radeon_irq_kms_sw_irq_put - disable software interrupt |
418 | * |
419 | * @rdev: radeon device pointer |
420 | * @ring: ring whose interrupt you want to disable |
421 | * |
422 | * Disables the software interrupt for a specific ring (all asics). |
423 | * The software interrupt is generally used to signal a fence on |
424 | * a particular ring. |
425 | */ |
426 | void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev, int ring) |
427 | { |
428 | unsigned long irqflags; |
429 | |
430 | if (!rdev->irq.installed) |
431 | return; |
432 | |
433 | if (atomic_dec_and_test(v: &rdev->irq.ring_int[ring])) { |
434 | spin_lock_irqsave(&rdev->irq.lock, irqflags); |
435 | radeon_irq_set(rdev); |
436 | spin_unlock_irqrestore(lock: &rdev->irq.lock, flags: irqflags); |
437 | } |
438 | } |
439 | |
440 | /** |
441 | * radeon_irq_kms_pflip_irq_get - enable pageflip interrupt |
442 | * |
443 | * @rdev: radeon device pointer |
444 | * @crtc: crtc whose interrupt you want to enable |
445 | * |
446 | * Enables the pageflip interrupt for a specific crtc (all asics). |
447 | * For pageflips we use the vblank interrupt source. |
448 | */ |
449 | void radeon_irq_kms_pflip_irq_get(struct radeon_device *rdev, int crtc) |
450 | { |
451 | unsigned long irqflags; |
452 | |
453 | if (crtc < 0 || crtc >= rdev->num_crtc) |
454 | return; |
455 | |
456 | if (!rdev->irq.installed) |
457 | return; |
458 | |
459 | if (atomic_inc_return(v: &rdev->irq.pflip[crtc]) == 1) { |
460 | spin_lock_irqsave(&rdev->irq.lock, irqflags); |
461 | radeon_irq_set(rdev); |
462 | spin_unlock_irqrestore(lock: &rdev->irq.lock, flags: irqflags); |
463 | } |
464 | } |
465 | |
466 | /** |
467 | * radeon_irq_kms_pflip_irq_put - disable pageflip interrupt |
468 | * |
469 | * @rdev: radeon device pointer |
470 | * @crtc: crtc whose interrupt you want to disable |
471 | * |
472 | * Disables the pageflip interrupt for a specific crtc (all asics). |
473 | * For pageflips we use the vblank interrupt source. |
474 | */ |
475 | void radeon_irq_kms_pflip_irq_put(struct radeon_device *rdev, int crtc) |
476 | { |
477 | unsigned long irqflags; |
478 | |
479 | if (crtc < 0 || crtc >= rdev->num_crtc) |
480 | return; |
481 | |
482 | if (!rdev->irq.installed) |
483 | return; |
484 | |
485 | if (atomic_dec_and_test(v: &rdev->irq.pflip[crtc])) { |
486 | spin_lock_irqsave(&rdev->irq.lock, irqflags); |
487 | radeon_irq_set(rdev); |
488 | spin_unlock_irqrestore(lock: &rdev->irq.lock, flags: irqflags); |
489 | } |
490 | } |
491 | |
492 | /** |
493 | * radeon_irq_kms_enable_afmt - enable audio format change interrupt |
494 | * |
495 | * @rdev: radeon device pointer |
496 | * @block: afmt block whose interrupt you want to enable |
497 | * |
498 | * Enables the afmt change interrupt for a specific afmt block (all asics). |
499 | */ |
500 | void radeon_irq_kms_enable_afmt(struct radeon_device *rdev, int block) |
501 | { |
502 | unsigned long irqflags; |
503 | |
504 | if (!rdev->irq.installed) |
505 | return; |
506 | |
507 | spin_lock_irqsave(&rdev->irq.lock, irqflags); |
508 | rdev->irq.afmt[block] = true; |
509 | radeon_irq_set(rdev); |
510 | spin_unlock_irqrestore(lock: &rdev->irq.lock, flags: irqflags); |
511 | |
512 | } |
513 | |
514 | /** |
515 | * radeon_irq_kms_disable_afmt - disable audio format change interrupt |
516 | * |
517 | * @rdev: radeon device pointer |
518 | * @block: afmt block whose interrupt you want to disable |
519 | * |
520 | * Disables the afmt change interrupt for a specific afmt block (all asics). |
521 | */ |
522 | void radeon_irq_kms_disable_afmt(struct radeon_device *rdev, int block) |
523 | { |
524 | unsigned long irqflags; |
525 | |
526 | if (!rdev->irq.installed) |
527 | return; |
528 | |
529 | spin_lock_irqsave(&rdev->irq.lock, irqflags); |
530 | rdev->irq.afmt[block] = false; |
531 | radeon_irq_set(rdev); |
532 | spin_unlock_irqrestore(lock: &rdev->irq.lock, flags: irqflags); |
533 | } |
534 | |
535 | /** |
536 | * radeon_irq_kms_enable_hpd - enable hotplug detect interrupt |
537 | * |
538 | * @rdev: radeon device pointer |
539 | * @hpd_mask: mask of hpd pins you want to enable. |
540 | * |
541 | * Enables the hotplug detect interrupt for a specific hpd pin (all asics). |
542 | */ |
543 | void radeon_irq_kms_enable_hpd(struct radeon_device *rdev, unsigned hpd_mask) |
544 | { |
545 | unsigned long irqflags; |
546 | int i; |
547 | |
548 | if (!rdev->irq.installed) |
549 | return; |
550 | |
551 | spin_lock_irqsave(&rdev->irq.lock, irqflags); |
552 | for (i = 0; i < RADEON_MAX_HPD_PINS; ++i) |
553 | rdev->irq.hpd[i] |= !!(hpd_mask & (1 << i)); |
554 | radeon_irq_set(rdev); |
555 | spin_unlock_irqrestore(lock: &rdev->irq.lock, flags: irqflags); |
556 | } |
557 | |
558 | /** |
559 | * radeon_irq_kms_disable_hpd - disable hotplug detect interrupt |
560 | * |
561 | * @rdev: radeon device pointer |
562 | * @hpd_mask: mask of hpd pins you want to disable. |
563 | * |
564 | * Disables the hotplug detect interrupt for a specific hpd pin (all asics). |
565 | */ |
566 | void radeon_irq_kms_disable_hpd(struct radeon_device *rdev, unsigned hpd_mask) |
567 | { |
568 | unsigned long irqflags; |
569 | int i; |
570 | |
571 | if (!rdev->irq.installed) |
572 | return; |
573 | |
574 | spin_lock_irqsave(&rdev->irq.lock, irqflags); |
575 | for (i = 0; i < RADEON_MAX_HPD_PINS; ++i) |
576 | rdev->irq.hpd[i] &= !(hpd_mask & (1 << i)); |
577 | radeon_irq_set(rdev); |
578 | spin_unlock_irqrestore(lock: &rdev->irq.lock, flags: irqflags); |
579 | } |
580 | |
581 | /** |
582 | * radeon_irq_kms_set_irq_n_enabled - helper for updating interrupt enable registers |
583 | * |
584 | * @rdev: radeon device pointer |
585 | * @reg: the register to write to enable/disable interrupts |
586 | * @mask: the mask that enables the interrupts |
587 | * @enable: whether to enable or disable the interrupt register |
588 | * @name: the name of the interrupt register to print to the kernel log |
589 | * @n: the number of the interrupt register to print to the kernel log |
590 | * |
591 | * Helper for updating the enable state of interrupt registers. Checks whether |
592 | * or not the interrupt matches the enable state we want. If it doesn't, then |
593 | * we update it and print a debugging message to the kernel log indicating the |
594 | * new state of the interrupt register. |
595 | * |
596 | * Used for updating sequences of interrupts registers like HPD1, HPD2, etc. |
597 | */ |
598 | void radeon_irq_kms_set_irq_n_enabled(struct radeon_device *rdev, |
599 | u32 reg, u32 mask, |
600 | bool enable, const char *name, unsigned n) |
601 | { |
602 | u32 tmp = RREG32(reg); |
603 | |
604 | /* Interrupt state didn't change */ |
605 | if (!!(tmp & mask) == enable) |
606 | return; |
607 | |
608 | if (enable) { |
609 | DRM_DEBUG("%s%d interrupts enabled\n" , name, n); |
610 | WREG32(reg, tmp |= mask); |
611 | } else { |
612 | DRM_DEBUG("%s%d interrupts disabled\n" , name, n); |
613 | WREG32(reg, tmp & ~mask); |
614 | } |
615 | } |
616 | |