18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright 2008 Advanced Micro Devices, Inc. 38c2ecf20Sopenharmony_ci * Copyright 2008 Red Hat Inc. 48c2ecf20Sopenharmony_ci * Copyright 2009 Jerome Glisse. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 78c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 88c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 98c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 108c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 118c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 148c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 178c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 188c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 198c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 208c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 218c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 228c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * Authors: Dave Airlie 258c2ecf20Sopenharmony_ci * Alex Deucher 268c2ecf20Sopenharmony_ci * Jerome Glisse 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <linux/pci.h> 308c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h> 338c2ecf20Sopenharmony_ci#include <drm/drm_device.h> 348c2ecf20Sopenharmony_ci#include <drm/drm_irq.h> 358c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 368c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 378c2ecf20Sopenharmony_ci#include <drm/radeon_drm.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#include "atom.h" 408c2ecf20Sopenharmony_ci#include "radeon.h" 418c2ecf20Sopenharmony_ci#include "radeon_reg.h" 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define RADEON_WAIT_IDLE_TIMEOUT 200 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/** 478c2ecf20Sopenharmony_ci * radeon_driver_irq_handler_kms - irq handler for KMS 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci * @int irq, void *arg: args 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * This is the irq handler for the radeon KMS driver (all asics). 528c2ecf20Sopenharmony_ci * radeon_irq_process is a macro that points to the per-asic 538c2ecf20Sopenharmony_ci * irq handler callback. 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_ciirqreturn_t radeon_driver_irq_handler_kms(int irq, void *arg) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct drm_device *dev = (struct drm_device *) arg; 588c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 598c2ecf20Sopenharmony_ci irqreturn_t ret; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci ret = radeon_irq_process(rdev); 628c2ecf20Sopenharmony_ci if (ret == IRQ_HANDLED) 638c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev->dev); 648c2ecf20Sopenharmony_ci return ret; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* 688c2ecf20Sopenharmony_ci * Handle hotplug events outside the interrupt handler proper. 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_ci/** 718c2ecf20Sopenharmony_ci * radeon_hotplug_work_func - display hotplug work handler 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * @work: work struct 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * This is the hot plug event work handler (all asics). 768c2ecf20Sopenharmony_ci * The work gets scheduled from the irq handler if there 778c2ecf20Sopenharmony_ci * was a hot plug interrupt. It walks the connector table 788c2ecf20Sopenharmony_ci * and calls the hotplug handler for each one, then sends 798c2ecf20Sopenharmony_ci * a drm hotplug event to alert userspace. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_cistatic void radeon_hotplug_work_func(struct work_struct *work) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct radeon_device *rdev = container_of(work, struct radeon_device, 848c2ecf20Sopenharmony_ci hotplug_work.work); 858c2ecf20Sopenharmony_ci struct drm_device *dev = rdev->ddev; 868c2ecf20Sopenharmony_ci struct drm_mode_config *mode_config = &dev->mode_config; 878c2ecf20Sopenharmony_ci struct drm_connector *connector; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* we can race here at startup, some boards seem to trigger 908c2ecf20Sopenharmony_ci * hotplug irqs when they shouldn't. */ 918c2ecf20Sopenharmony_ci if (!rdev->mode_info.mode_config_initialized) 928c2ecf20Sopenharmony_ci return; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci mutex_lock(&mode_config->mutex); 958c2ecf20Sopenharmony_ci list_for_each_entry(connector, &mode_config->connector_list, head) 968c2ecf20Sopenharmony_ci radeon_connector_hotplug(connector); 978c2ecf20Sopenharmony_ci mutex_unlock(&mode_config->mutex); 988c2ecf20Sopenharmony_ci /* Just fire off a uevent and let userspace tell us what to do */ 998c2ecf20Sopenharmony_ci drm_helper_hpd_irq_event(dev); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic void radeon_dp_work_func(struct work_struct *work) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct radeon_device *rdev = container_of(work, struct radeon_device, 1058c2ecf20Sopenharmony_ci dp_work); 1068c2ecf20Sopenharmony_ci struct drm_device *dev = rdev->ddev; 1078c2ecf20Sopenharmony_ci struct drm_mode_config *mode_config = &dev->mode_config; 1088c2ecf20Sopenharmony_ci struct drm_connector *connector; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* this should take a mutex */ 1118c2ecf20Sopenharmony_ci list_for_each_entry(connector, &mode_config->connector_list, head) 1128c2ecf20Sopenharmony_ci radeon_connector_hotplug(connector); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci/** 1158c2ecf20Sopenharmony_ci * radeon_driver_irq_preinstall_kms - drm irq preinstall callback 1168c2ecf20Sopenharmony_ci * 1178c2ecf20Sopenharmony_ci * @dev: drm dev pointer 1188c2ecf20Sopenharmony_ci * 1198c2ecf20Sopenharmony_ci * Gets the hw ready to enable irqs (all asics). 1208c2ecf20Sopenharmony_ci * This function disables all interrupt sources on the GPU. 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_civoid radeon_driver_irq_preinstall_kms(struct drm_device *dev) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 1258c2ecf20Sopenharmony_ci unsigned long irqflags; 1268c2ecf20Sopenharmony_ci unsigned i; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 1298c2ecf20Sopenharmony_ci /* Disable *all* interrupts */ 1308c2ecf20Sopenharmony_ci for (i = 0; i < RADEON_NUM_RINGS; i++) 1318c2ecf20Sopenharmony_ci atomic_set(&rdev->irq.ring_int[i], 0); 1328c2ecf20Sopenharmony_ci rdev->irq.dpm_thermal = false; 1338c2ecf20Sopenharmony_ci for (i = 0; i < RADEON_MAX_HPD_PINS; i++) 1348c2ecf20Sopenharmony_ci rdev->irq.hpd[i] = false; 1358c2ecf20Sopenharmony_ci for (i = 0; i < RADEON_MAX_CRTCS; i++) { 1368c2ecf20Sopenharmony_ci rdev->irq.crtc_vblank_int[i] = false; 1378c2ecf20Sopenharmony_ci atomic_set(&rdev->irq.pflip[i], 0); 1388c2ecf20Sopenharmony_ci rdev->irq.afmt[i] = false; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci radeon_irq_set(rdev); 1418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 1428c2ecf20Sopenharmony_ci /* Clear bits */ 1438c2ecf20Sopenharmony_ci radeon_irq_process(rdev); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/** 1478c2ecf20Sopenharmony_ci * radeon_driver_irq_postinstall_kms - drm irq preinstall callback 1488c2ecf20Sopenharmony_ci * 1498c2ecf20Sopenharmony_ci * @dev: drm dev pointer 1508c2ecf20Sopenharmony_ci * 1518c2ecf20Sopenharmony_ci * Handles stuff to be done after enabling irqs (all asics). 1528c2ecf20Sopenharmony_ci * Returns 0 on success. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ciint radeon_driver_irq_postinstall_kms(struct drm_device *dev) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (ASIC_IS_AVIVO(rdev)) 1598c2ecf20Sopenharmony_ci dev->max_vblank_count = 0x00ffffff; 1608c2ecf20Sopenharmony_ci else 1618c2ecf20Sopenharmony_ci dev->max_vblank_count = 0x001fffff; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci/** 1678c2ecf20Sopenharmony_ci * radeon_driver_irq_uninstall_kms - drm irq uninstall callback 1688c2ecf20Sopenharmony_ci * 1698c2ecf20Sopenharmony_ci * @dev: drm dev pointer 1708c2ecf20Sopenharmony_ci * 1718c2ecf20Sopenharmony_ci * This function disables all interrupt sources on the GPU (all asics). 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_civoid radeon_driver_irq_uninstall_kms(struct drm_device *dev) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 1768c2ecf20Sopenharmony_ci unsigned long irqflags; 1778c2ecf20Sopenharmony_ci unsigned i; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (rdev == NULL) { 1808c2ecf20Sopenharmony_ci return; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 1838c2ecf20Sopenharmony_ci /* Disable *all* interrupts */ 1848c2ecf20Sopenharmony_ci for (i = 0; i < RADEON_NUM_RINGS; i++) 1858c2ecf20Sopenharmony_ci atomic_set(&rdev->irq.ring_int[i], 0); 1868c2ecf20Sopenharmony_ci rdev->irq.dpm_thermal = false; 1878c2ecf20Sopenharmony_ci for (i = 0; i < RADEON_MAX_HPD_PINS; i++) 1888c2ecf20Sopenharmony_ci rdev->irq.hpd[i] = false; 1898c2ecf20Sopenharmony_ci for (i = 0; i < RADEON_MAX_CRTCS; i++) { 1908c2ecf20Sopenharmony_ci rdev->irq.crtc_vblank_int[i] = false; 1918c2ecf20Sopenharmony_ci atomic_set(&rdev->irq.pflip[i], 0); 1928c2ecf20Sopenharmony_ci rdev->irq.afmt[i] = false; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci radeon_irq_set(rdev); 1958c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/** 1998c2ecf20Sopenharmony_ci * radeon_msi_ok - asic specific msi checks 2008c2ecf20Sopenharmony_ci * 2018c2ecf20Sopenharmony_ci * @rdev: radeon device pointer 2028c2ecf20Sopenharmony_ci * 2038c2ecf20Sopenharmony_ci * Handles asic specific MSI checks to determine if 2048c2ecf20Sopenharmony_ci * MSIs should be enabled on a particular chip (all asics). 2058c2ecf20Sopenharmony_ci * Returns true if MSIs should be enabled, false if MSIs 2068c2ecf20Sopenharmony_ci * should not be enabled. 2078c2ecf20Sopenharmony_ci */ 2088c2ecf20Sopenharmony_cistatic bool radeon_msi_ok(struct radeon_device *rdev) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci /* RV370/RV380 was first asic with MSI support */ 2118c2ecf20Sopenharmony_ci if (rdev->family < CHIP_RV380) 2128c2ecf20Sopenharmony_ci return false; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* MSIs don't work on AGP */ 2158c2ecf20Sopenharmony_ci if (rdev->flags & RADEON_IS_AGP) 2168c2ecf20Sopenharmony_ci return false; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* 2198c2ecf20Sopenharmony_ci * Older chips have a HW limitation, they can only generate 40 bits 2208c2ecf20Sopenharmony_ci * of address for "64-bit" MSIs which breaks on some platforms, notably 2218c2ecf20Sopenharmony_ci * IBM POWER servers, so we limit them 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_ci if (rdev->family < CHIP_BONAIRE) { 2248c2ecf20Sopenharmony_ci dev_info(rdev->dev, "radeon: MSI limited to 32-bit\n"); 2258c2ecf20Sopenharmony_ci rdev->pdev->no_64bit_msi = 1; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* force MSI on */ 2298c2ecf20Sopenharmony_ci if (radeon_msi == 1) 2308c2ecf20Sopenharmony_ci return true; 2318c2ecf20Sopenharmony_ci else if (radeon_msi == 0) 2328c2ecf20Sopenharmony_ci return false; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* Quirks */ 2358c2ecf20Sopenharmony_ci /* HP RS690 only seems to work with MSIs. */ 2368c2ecf20Sopenharmony_ci if ((rdev->pdev->device == 0x791f) && 2378c2ecf20Sopenharmony_ci (rdev->pdev->subsystem_vendor == 0x103c) && 2388c2ecf20Sopenharmony_ci (rdev->pdev->subsystem_device == 0x30c2)) 2398c2ecf20Sopenharmony_ci return true; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* Dell RS690 only seems to work with MSIs. */ 2428c2ecf20Sopenharmony_ci if ((rdev->pdev->device == 0x791f) && 2438c2ecf20Sopenharmony_ci (rdev->pdev->subsystem_vendor == 0x1028) && 2448c2ecf20Sopenharmony_ci (rdev->pdev->subsystem_device == 0x01fc)) 2458c2ecf20Sopenharmony_ci return true; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* Dell RS690 only seems to work with MSIs. */ 2488c2ecf20Sopenharmony_ci if ((rdev->pdev->device == 0x791f) && 2498c2ecf20Sopenharmony_ci (rdev->pdev->subsystem_vendor == 0x1028) && 2508c2ecf20Sopenharmony_ci (rdev->pdev->subsystem_device == 0x01fd)) 2518c2ecf20Sopenharmony_ci return true; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* Gateway RS690 only seems to work with MSIs. */ 2548c2ecf20Sopenharmony_ci if ((rdev->pdev->device == 0x791f) && 2558c2ecf20Sopenharmony_ci (rdev->pdev->subsystem_vendor == 0x107b) && 2568c2ecf20Sopenharmony_ci (rdev->pdev->subsystem_device == 0x0185)) 2578c2ecf20Sopenharmony_ci return true; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* try and enable MSIs by default on all RS690s */ 2608c2ecf20Sopenharmony_ci if (rdev->family == CHIP_RS690) 2618c2ecf20Sopenharmony_ci return true; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* RV515 seems to have MSI issues where it loses 2648c2ecf20Sopenharmony_ci * MSI rearms occasionally. This leads to lockups and freezes. 2658c2ecf20Sopenharmony_ci * disable it by default. 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_ci if (rdev->family == CHIP_RV515) 2688c2ecf20Sopenharmony_ci return false; 2698c2ecf20Sopenharmony_ci if (rdev->flags & RADEON_IS_IGP) { 2708c2ecf20Sopenharmony_ci /* APUs work fine with MSIs */ 2718c2ecf20Sopenharmony_ci if (rdev->family >= CHIP_PALM) 2728c2ecf20Sopenharmony_ci return true; 2738c2ecf20Sopenharmony_ci /* lots of IGPs have problems with MSIs */ 2748c2ecf20Sopenharmony_ci return false; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return true; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci/** 2818c2ecf20Sopenharmony_ci * radeon_irq_kms_init - init driver interrupt info 2828c2ecf20Sopenharmony_ci * 2838c2ecf20Sopenharmony_ci * @rdev: radeon device pointer 2848c2ecf20Sopenharmony_ci * 2858c2ecf20Sopenharmony_ci * Sets up the work irq handlers, vblank init, MSIs, etc. (all asics). 2868c2ecf20Sopenharmony_ci * Returns 0 for success, error for failure. 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_ciint radeon_irq_kms_init(struct radeon_device *rdev) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci int r = 0; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci spin_lock_init(&rdev->irq.lock); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* Disable vblank irqs aggressively for power-saving */ 2958c2ecf20Sopenharmony_ci rdev->ddev->vblank_disable_immediate = true; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci r = drm_vblank_init(rdev->ddev, rdev->num_crtc); 2988c2ecf20Sopenharmony_ci if (r) { 2998c2ecf20Sopenharmony_ci return r; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* enable msi */ 3038c2ecf20Sopenharmony_ci rdev->msi_enabled = 0; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (radeon_msi_ok(rdev)) { 3068c2ecf20Sopenharmony_ci int ret = pci_enable_msi(rdev->pdev); 3078c2ecf20Sopenharmony_ci if (!ret) { 3088c2ecf20Sopenharmony_ci rdev->msi_enabled = 1; 3098c2ecf20Sopenharmony_ci dev_info(rdev->dev, "radeon: using MSI.\n"); 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&rdev->hotplug_work, radeon_hotplug_work_func); 3148c2ecf20Sopenharmony_ci INIT_WORK(&rdev->dp_work, radeon_dp_work_func); 3158c2ecf20Sopenharmony_ci INIT_WORK(&rdev->audio_work, r600_audio_update_hdmi); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci rdev->irq.installed = true; 3188c2ecf20Sopenharmony_ci r = drm_irq_install(rdev->ddev, rdev->ddev->pdev->irq); 3198c2ecf20Sopenharmony_ci if (r) { 3208c2ecf20Sopenharmony_ci rdev->irq.installed = false; 3218c2ecf20Sopenharmony_ci flush_delayed_work(&rdev->hotplug_work); 3228c2ecf20Sopenharmony_ci return r; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci DRM_INFO("radeon: irq initialized.\n"); 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci/** 3308c2ecf20Sopenharmony_ci * radeon_irq_kms_fini - tear down driver interrupt info 3318c2ecf20Sopenharmony_ci * 3328c2ecf20Sopenharmony_ci * @rdev: radeon device pointer 3338c2ecf20Sopenharmony_ci * 3348c2ecf20Sopenharmony_ci * Tears down the work irq handlers, vblank handlers, MSIs, etc. (all asics). 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_civoid radeon_irq_kms_fini(struct radeon_device *rdev) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci if (rdev->irq.installed) { 3398c2ecf20Sopenharmony_ci drm_irq_uninstall(rdev->ddev); 3408c2ecf20Sopenharmony_ci rdev->irq.installed = false; 3418c2ecf20Sopenharmony_ci if (rdev->msi_enabled) 3428c2ecf20Sopenharmony_ci pci_disable_msi(rdev->pdev); 3438c2ecf20Sopenharmony_ci flush_delayed_work(&rdev->hotplug_work); 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci/** 3488c2ecf20Sopenharmony_ci * radeon_irq_kms_sw_irq_get - enable software interrupt 3498c2ecf20Sopenharmony_ci * 3508c2ecf20Sopenharmony_ci * @rdev: radeon device pointer 3518c2ecf20Sopenharmony_ci * @ring: ring whose interrupt you want to enable 3528c2ecf20Sopenharmony_ci * 3538c2ecf20Sopenharmony_ci * Enables the software interrupt for a specific ring (all asics). 3548c2ecf20Sopenharmony_ci * The software interrupt is generally used to signal a fence on 3558c2ecf20Sopenharmony_ci * a particular ring. 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_civoid radeon_irq_kms_sw_irq_get(struct radeon_device *rdev, int ring) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci unsigned long irqflags; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (!rdev->ddev->irq_enabled) 3628c2ecf20Sopenharmony_ci return; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (atomic_inc_return(&rdev->irq.ring_int[ring]) == 1) { 3658c2ecf20Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 3668c2ecf20Sopenharmony_ci radeon_irq_set(rdev); 3678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci/** 3728c2ecf20Sopenharmony_ci * radeon_irq_kms_sw_irq_get_delayed - enable software interrupt 3738c2ecf20Sopenharmony_ci * 3748c2ecf20Sopenharmony_ci * @rdev: radeon device pointer 3758c2ecf20Sopenharmony_ci * @ring: ring whose interrupt you want to enable 3768c2ecf20Sopenharmony_ci * 3778c2ecf20Sopenharmony_ci * Enables the software interrupt for a specific ring (all asics). 3788c2ecf20Sopenharmony_ci * The software interrupt is generally used to signal a fence on 3798c2ecf20Sopenharmony_ci * a particular ring. 3808c2ecf20Sopenharmony_ci */ 3818c2ecf20Sopenharmony_cibool radeon_irq_kms_sw_irq_get_delayed(struct radeon_device *rdev, int ring) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci return atomic_inc_return(&rdev->irq.ring_int[ring]) == 1; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/** 3878c2ecf20Sopenharmony_ci * radeon_irq_kms_sw_irq_put - disable software interrupt 3888c2ecf20Sopenharmony_ci * 3898c2ecf20Sopenharmony_ci * @rdev: radeon device pointer 3908c2ecf20Sopenharmony_ci * @ring: ring whose interrupt you want to disable 3918c2ecf20Sopenharmony_ci * 3928c2ecf20Sopenharmony_ci * Disables the software interrupt for a specific ring (all asics). 3938c2ecf20Sopenharmony_ci * The software interrupt is generally used to signal a fence on 3948c2ecf20Sopenharmony_ci * a particular ring. 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_civoid radeon_irq_kms_sw_irq_put(struct radeon_device *rdev, int ring) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci unsigned long irqflags; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (!rdev->ddev->irq_enabled) 4018c2ecf20Sopenharmony_ci return; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&rdev->irq.ring_int[ring])) { 4048c2ecf20Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 4058c2ecf20Sopenharmony_ci radeon_irq_set(rdev); 4068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci/** 4118c2ecf20Sopenharmony_ci * radeon_irq_kms_pflip_irq_get - enable pageflip interrupt 4128c2ecf20Sopenharmony_ci * 4138c2ecf20Sopenharmony_ci * @rdev: radeon device pointer 4148c2ecf20Sopenharmony_ci * @crtc: crtc whose interrupt you want to enable 4158c2ecf20Sopenharmony_ci * 4168c2ecf20Sopenharmony_ci * Enables the pageflip interrupt for a specific crtc (all asics). 4178c2ecf20Sopenharmony_ci * For pageflips we use the vblank interrupt source. 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_civoid radeon_irq_kms_pflip_irq_get(struct radeon_device *rdev, int crtc) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci unsigned long irqflags; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (crtc < 0 || crtc >= rdev->num_crtc) 4248c2ecf20Sopenharmony_ci return; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (!rdev->ddev->irq_enabled) 4278c2ecf20Sopenharmony_ci return; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (atomic_inc_return(&rdev->irq.pflip[crtc]) == 1) { 4308c2ecf20Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 4318c2ecf20Sopenharmony_ci radeon_irq_set(rdev); 4328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci/** 4378c2ecf20Sopenharmony_ci * radeon_irq_kms_pflip_irq_put - disable pageflip interrupt 4388c2ecf20Sopenharmony_ci * 4398c2ecf20Sopenharmony_ci * @rdev: radeon device pointer 4408c2ecf20Sopenharmony_ci * @crtc: crtc whose interrupt you want to disable 4418c2ecf20Sopenharmony_ci * 4428c2ecf20Sopenharmony_ci * Disables the pageflip interrupt for a specific crtc (all asics). 4438c2ecf20Sopenharmony_ci * For pageflips we use the vblank interrupt source. 4448c2ecf20Sopenharmony_ci */ 4458c2ecf20Sopenharmony_civoid radeon_irq_kms_pflip_irq_put(struct radeon_device *rdev, int crtc) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci unsigned long irqflags; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (crtc < 0 || crtc >= rdev->num_crtc) 4508c2ecf20Sopenharmony_ci return; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (!rdev->ddev->irq_enabled) 4538c2ecf20Sopenharmony_ci return; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&rdev->irq.pflip[crtc])) { 4568c2ecf20Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 4578c2ecf20Sopenharmony_ci radeon_irq_set(rdev); 4588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci/** 4638c2ecf20Sopenharmony_ci * radeon_irq_kms_enable_afmt - enable audio format change interrupt 4648c2ecf20Sopenharmony_ci * 4658c2ecf20Sopenharmony_ci * @rdev: radeon device pointer 4668c2ecf20Sopenharmony_ci * @block: afmt block whose interrupt you want to enable 4678c2ecf20Sopenharmony_ci * 4688c2ecf20Sopenharmony_ci * Enables the afmt change interrupt for a specific afmt block (all asics). 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_civoid radeon_irq_kms_enable_afmt(struct radeon_device *rdev, int block) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci unsigned long irqflags; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (!rdev->ddev->irq_enabled) 4758c2ecf20Sopenharmony_ci return; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 4788c2ecf20Sopenharmony_ci rdev->irq.afmt[block] = true; 4798c2ecf20Sopenharmony_ci radeon_irq_set(rdev); 4808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci/** 4858c2ecf20Sopenharmony_ci * radeon_irq_kms_disable_afmt - disable audio format change interrupt 4868c2ecf20Sopenharmony_ci * 4878c2ecf20Sopenharmony_ci * @rdev: radeon device pointer 4888c2ecf20Sopenharmony_ci * @block: afmt block whose interrupt you want to disable 4898c2ecf20Sopenharmony_ci * 4908c2ecf20Sopenharmony_ci * Disables the afmt change interrupt for a specific afmt block (all asics). 4918c2ecf20Sopenharmony_ci */ 4928c2ecf20Sopenharmony_civoid radeon_irq_kms_disable_afmt(struct radeon_device *rdev, int block) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci unsigned long irqflags; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (!rdev->ddev->irq_enabled) 4978c2ecf20Sopenharmony_ci return; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 5008c2ecf20Sopenharmony_ci rdev->irq.afmt[block] = false; 5018c2ecf20Sopenharmony_ci radeon_irq_set(rdev); 5028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci/** 5068c2ecf20Sopenharmony_ci * radeon_irq_kms_enable_hpd - enable hotplug detect interrupt 5078c2ecf20Sopenharmony_ci * 5088c2ecf20Sopenharmony_ci * @rdev: radeon device pointer 5098c2ecf20Sopenharmony_ci * @hpd_mask: mask of hpd pins you want to enable. 5108c2ecf20Sopenharmony_ci * 5118c2ecf20Sopenharmony_ci * Enables the hotplug detect interrupt for a specific hpd pin (all asics). 5128c2ecf20Sopenharmony_ci */ 5138c2ecf20Sopenharmony_civoid radeon_irq_kms_enable_hpd(struct radeon_device *rdev, unsigned hpd_mask) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci unsigned long irqflags; 5168c2ecf20Sopenharmony_ci int i; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (!rdev->ddev->irq_enabled) 5198c2ecf20Sopenharmony_ci return; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 5228c2ecf20Sopenharmony_ci for (i = 0; i < RADEON_MAX_HPD_PINS; ++i) 5238c2ecf20Sopenharmony_ci rdev->irq.hpd[i] |= !!(hpd_mask & (1 << i)); 5248c2ecf20Sopenharmony_ci radeon_irq_set(rdev); 5258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci/** 5298c2ecf20Sopenharmony_ci * radeon_irq_kms_disable_hpd - disable hotplug detect interrupt 5308c2ecf20Sopenharmony_ci * 5318c2ecf20Sopenharmony_ci * @rdev: radeon device pointer 5328c2ecf20Sopenharmony_ci * @hpd_mask: mask of hpd pins you want to disable. 5338c2ecf20Sopenharmony_ci * 5348c2ecf20Sopenharmony_ci * Disables the hotplug detect interrupt for a specific hpd pin (all asics). 5358c2ecf20Sopenharmony_ci */ 5368c2ecf20Sopenharmony_civoid radeon_irq_kms_disable_hpd(struct radeon_device *rdev, unsigned hpd_mask) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci unsigned long irqflags; 5398c2ecf20Sopenharmony_ci int i; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (!rdev->ddev->irq_enabled) 5428c2ecf20Sopenharmony_ci return; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 5458c2ecf20Sopenharmony_ci for (i = 0; i < RADEON_MAX_HPD_PINS; ++i) 5468c2ecf20Sopenharmony_ci rdev->irq.hpd[i] &= !(hpd_mask & (1 << i)); 5478c2ecf20Sopenharmony_ci radeon_irq_set(rdev); 5488c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci/** 5528c2ecf20Sopenharmony_ci * radeon_irq_kms_update_int_n - helper for updating interrupt enable registers 5538c2ecf20Sopenharmony_ci * 5548c2ecf20Sopenharmony_ci * @rdev: radeon device pointer 5558c2ecf20Sopenharmony_ci * @reg: the register to write to enable/disable interrupts 5568c2ecf20Sopenharmony_ci * @mask: the mask that enables the interrupts 5578c2ecf20Sopenharmony_ci * @enable: whether to enable or disable the interrupt register 5588c2ecf20Sopenharmony_ci * @name: the name of the interrupt register to print to the kernel log 5598c2ecf20Sopenharmony_ci * @num: the number of the interrupt register to print to the kernel log 5608c2ecf20Sopenharmony_ci * 5618c2ecf20Sopenharmony_ci * Helper for updating the enable state of interrupt registers. Checks whether 5628c2ecf20Sopenharmony_ci * or not the interrupt matches the enable state we want. If it doesn't, then 5638c2ecf20Sopenharmony_ci * we update it and print a debugging message to the kernel log indicating the 5648c2ecf20Sopenharmony_ci * new state of the interrupt register. 5658c2ecf20Sopenharmony_ci * 5668c2ecf20Sopenharmony_ci * Used for updating sequences of interrupts registers like HPD1, HPD2, etc. 5678c2ecf20Sopenharmony_ci */ 5688c2ecf20Sopenharmony_civoid radeon_irq_kms_set_irq_n_enabled(struct radeon_device *rdev, 5698c2ecf20Sopenharmony_ci u32 reg, u32 mask, 5708c2ecf20Sopenharmony_ci bool enable, const char *name, unsigned n) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci u32 tmp = RREG32(reg); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* Interrupt state didn't change */ 5758c2ecf20Sopenharmony_ci if (!!(tmp & mask) == enable) 5768c2ecf20Sopenharmony_ci return; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (enable) { 5798c2ecf20Sopenharmony_ci DRM_DEBUG("%s%d interrupts enabled\n", name, n); 5808c2ecf20Sopenharmony_ci WREG32(reg, tmp |= mask); 5818c2ecf20Sopenharmony_ci } else { 5828c2ecf20Sopenharmony_ci DRM_DEBUG("%s%d interrupts disabled\n", name, n); 5838c2ecf20Sopenharmony_ci WREG32(reg, tmp & ~mask); 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci} 586