162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright 2008 Advanced Micro Devices, Inc. 362306a36Sopenharmony_ci * Copyright 2008 Red Hat Inc. 462306a36Sopenharmony_ci * Copyright 2009 Jerome Glisse. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 762306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 862306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 962306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 1062306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 1162306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 1462306a36Sopenharmony_ci * all copies or substantial portions of the Software. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1762306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1862306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1962306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 2062306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 2162306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 2262306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * Authors: Dave Airlie 2562306a36Sopenharmony_ci * Alex Deucher 2662306a36Sopenharmony_ci * Jerome Glisse 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/pci.h> 3062306a36Sopenharmony_ci#include <linux/pm_runtime.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include <drm/drm_device.h> 3362306a36Sopenharmony_ci#include <drm/drm_drv.h> 3462306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 3562306a36Sopenharmony_ci#include <drm/drm_vblank.h> 3662306a36Sopenharmony_ci#include <drm/radeon_drm.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include "atom.h" 3962306a36Sopenharmony_ci#include "radeon.h" 4062306a36Sopenharmony_ci#include "radeon_kms.h" 4162306a36Sopenharmony_ci#include "radeon_reg.h" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define RADEON_WAIT_IDLE_TIMEOUT 200 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * radeon_driver_irq_handler_kms - irq handler for KMS 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * This is the irq handler for the radeon KMS driver (all asics). 5062306a36Sopenharmony_ci * radeon_irq_process is a macro that points to the per-asic 5162306a36Sopenharmony_ci * irq handler callback. 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_cistatic irqreturn_t radeon_driver_irq_handler_kms(int irq, void *arg) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct drm_device *dev = (struct drm_device *) arg; 5662306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 5762306a36Sopenharmony_ci irqreturn_t ret; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci ret = radeon_irq_process(rdev); 6062306a36Sopenharmony_ci if (ret == IRQ_HANDLED) 6162306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev->dev); 6262306a36Sopenharmony_ci return ret; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* 6662306a36Sopenharmony_ci * Handle hotplug events outside the interrupt handler proper. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci/** 6962306a36Sopenharmony_ci * radeon_hotplug_work_func - display hotplug work handler 7062306a36Sopenharmony_ci * 7162306a36Sopenharmony_ci * @work: work struct 7262306a36Sopenharmony_ci * 7362306a36Sopenharmony_ci * This is the hot plug event work handler (all asics). 7462306a36Sopenharmony_ci * The work gets scheduled from the irq handler if there 7562306a36Sopenharmony_ci * was a hot plug interrupt. It walks the connector table 7662306a36Sopenharmony_ci * and calls the hotplug handler for each one, then sends 7762306a36Sopenharmony_ci * a drm hotplug event to alert userspace. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_cistatic void radeon_hotplug_work_func(struct work_struct *work) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct radeon_device *rdev = container_of(work, struct radeon_device, 8262306a36Sopenharmony_ci hotplug_work.work); 8362306a36Sopenharmony_ci struct drm_device *dev = rdev->ddev; 8462306a36Sopenharmony_ci struct drm_mode_config *mode_config = &dev->mode_config; 8562306a36Sopenharmony_ci struct drm_connector *connector; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* we can race here at startup, some boards seem to trigger 8862306a36Sopenharmony_ci * hotplug irqs when they shouldn't. */ 8962306a36Sopenharmony_ci if (!rdev->mode_info.mode_config_initialized) 9062306a36Sopenharmony_ci return; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci mutex_lock(&mode_config->mutex); 9362306a36Sopenharmony_ci list_for_each_entry(connector, &mode_config->connector_list, head) 9462306a36Sopenharmony_ci radeon_connector_hotplug(connector); 9562306a36Sopenharmony_ci mutex_unlock(&mode_config->mutex); 9662306a36Sopenharmony_ci /* Just fire off a uevent and let userspace tell us what to do */ 9762306a36Sopenharmony_ci drm_helper_hpd_irq_event(dev); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic void radeon_dp_work_func(struct work_struct *work) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct radeon_device *rdev = container_of(work, struct radeon_device, 10362306a36Sopenharmony_ci dp_work); 10462306a36Sopenharmony_ci struct drm_device *dev = rdev->ddev; 10562306a36Sopenharmony_ci struct drm_mode_config *mode_config = &dev->mode_config; 10662306a36Sopenharmony_ci struct drm_connector *connector; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci mutex_lock(&mode_config->mutex); 10962306a36Sopenharmony_ci list_for_each_entry(connector, &mode_config->connector_list, head) 11062306a36Sopenharmony_ci radeon_connector_hotplug(connector); 11162306a36Sopenharmony_ci mutex_unlock(&mode_config->mutex); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/** 11562306a36Sopenharmony_ci * radeon_driver_irq_preinstall_kms - drm irq preinstall callback 11662306a36Sopenharmony_ci * 11762306a36Sopenharmony_ci * @dev: drm dev pointer 11862306a36Sopenharmony_ci * 11962306a36Sopenharmony_ci * Gets the hw ready to enable irqs (all asics). 12062306a36Sopenharmony_ci * This function disables all interrupt sources on the GPU. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_cistatic void radeon_driver_irq_preinstall_kms(struct drm_device *dev) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 12562306a36Sopenharmony_ci unsigned long irqflags; 12662306a36Sopenharmony_ci unsigned i; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 12962306a36Sopenharmony_ci /* Disable *all* interrupts */ 13062306a36Sopenharmony_ci for (i = 0; i < RADEON_NUM_RINGS; i++) 13162306a36Sopenharmony_ci atomic_set(&rdev->irq.ring_int[i], 0); 13262306a36Sopenharmony_ci rdev->irq.dpm_thermal = false; 13362306a36Sopenharmony_ci for (i = 0; i < RADEON_MAX_HPD_PINS; i++) 13462306a36Sopenharmony_ci rdev->irq.hpd[i] = false; 13562306a36Sopenharmony_ci for (i = 0; i < RADEON_MAX_CRTCS; i++) { 13662306a36Sopenharmony_ci rdev->irq.crtc_vblank_int[i] = false; 13762306a36Sopenharmony_ci atomic_set(&rdev->irq.pflip[i], 0); 13862306a36Sopenharmony_ci rdev->irq.afmt[i] = false; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci radeon_irq_set(rdev); 14162306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 14262306a36Sopenharmony_ci /* Clear bits */ 14362306a36Sopenharmony_ci radeon_irq_process(rdev); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/** 14762306a36Sopenharmony_ci * radeon_driver_irq_postinstall_kms - drm irq preinstall callback 14862306a36Sopenharmony_ci * 14962306a36Sopenharmony_ci * @dev: drm dev pointer 15062306a36Sopenharmony_ci * 15162306a36Sopenharmony_ci * Handles stuff to be done after enabling irqs (all asics). 15262306a36Sopenharmony_ci * Returns 0 on success. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_cistatic int radeon_driver_irq_postinstall_kms(struct drm_device *dev) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (ASIC_IS_AVIVO(rdev)) 15962306a36Sopenharmony_ci dev->max_vblank_count = 0x00ffffff; 16062306a36Sopenharmony_ci else 16162306a36Sopenharmony_ci dev->max_vblank_count = 0x001fffff; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/** 16762306a36Sopenharmony_ci * radeon_driver_irq_uninstall_kms - drm irq uninstall callback 16862306a36Sopenharmony_ci * 16962306a36Sopenharmony_ci * @dev: drm dev pointer 17062306a36Sopenharmony_ci * 17162306a36Sopenharmony_ci * This function disables all interrupt sources on the GPU (all asics). 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_cistatic void radeon_driver_irq_uninstall_kms(struct drm_device *dev) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct radeon_device *rdev = dev->dev_private; 17662306a36Sopenharmony_ci unsigned long irqflags; 17762306a36Sopenharmony_ci unsigned i; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (rdev == NULL) { 18062306a36Sopenharmony_ci return; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 18362306a36Sopenharmony_ci /* Disable *all* interrupts */ 18462306a36Sopenharmony_ci for (i = 0; i < RADEON_NUM_RINGS; i++) 18562306a36Sopenharmony_ci atomic_set(&rdev->irq.ring_int[i], 0); 18662306a36Sopenharmony_ci rdev->irq.dpm_thermal = false; 18762306a36Sopenharmony_ci for (i = 0; i < RADEON_MAX_HPD_PINS; i++) 18862306a36Sopenharmony_ci rdev->irq.hpd[i] = false; 18962306a36Sopenharmony_ci for (i = 0; i < RADEON_MAX_CRTCS; i++) { 19062306a36Sopenharmony_ci rdev->irq.crtc_vblank_int[i] = false; 19162306a36Sopenharmony_ci atomic_set(&rdev->irq.pflip[i], 0); 19262306a36Sopenharmony_ci rdev->irq.afmt[i] = false; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci radeon_irq_set(rdev); 19562306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int radeon_irq_install(struct radeon_device *rdev, int irq) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct drm_device *dev = rdev->ddev; 20162306a36Sopenharmony_ci int ret; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (irq == IRQ_NOTCONNECTED) 20462306a36Sopenharmony_ci return -ENOTCONN; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci radeon_driver_irq_preinstall_kms(dev); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* PCI devices require shared interrupts. */ 20962306a36Sopenharmony_ci ret = request_irq(irq, radeon_driver_irq_handler_kms, 21062306a36Sopenharmony_ci IRQF_SHARED, dev->driver->name, dev); 21162306a36Sopenharmony_ci if (ret) 21262306a36Sopenharmony_ci return ret; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci radeon_driver_irq_postinstall_kms(dev); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void radeon_irq_uninstall(struct radeon_device *rdev) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct drm_device *dev = rdev->ddev; 22262306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev->dev); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci radeon_driver_irq_uninstall_kms(dev); 22562306a36Sopenharmony_ci free_irq(pdev->irq, dev); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/** 22962306a36Sopenharmony_ci * radeon_msi_ok - asic specific msi checks 23062306a36Sopenharmony_ci * 23162306a36Sopenharmony_ci * @rdev: radeon device pointer 23262306a36Sopenharmony_ci * 23362306a36Sopenharmony_ci * Handles asic specific MSI checks to determine if 23462306a36Sopenharmony_ci * MSIs should be enabled on a particular chip (all asics). 23562306a36Sopenharmony_ci * Returns true if MSIs should be enabled, false if MSIs 23662306a36Sopenharmony_ci * should not be enabled. 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_cistatic bool radeon_msi_ok(struct radeon_device *rdev) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci /* RV370/RV380 was first asic with MSI support */ 24162306a36Sopenharmony_ci if (rdev->family < CHIP_RV380) 24262306a36Sopenharmony_ci return false; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* MSIs don't work on AGP */ 24562306a36Sopenharmony_ci if (rdev->flags & RADEON_IS_AGP) 24662306a36Sopenharmony_ci return false; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* 24962306a36Sopenharmony_ci * Older chips have a HW limitation, they can only generate 40 bits 25062306a36Sopenharmony_ci * of address for "64-bit" MSIs which breaks on some platforms, notably 25162306a36Sopenharmony_ci * IBM POWER servers, so we limit them 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_ci if (rdev->family < CHIP_BONAIRE) { 25462306a36Sopenharmony_ci dev_info(rdev->dev, "radeon: MSI limited to 32-bit\n"); 25562306a36Sopenharmony_ci rdev->pdev->no_64bit_msi = 1; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* force MSI on */ 25962306a36Sopenharmony_ci if (radeon_msi == 1) 26062306a36Sopenharmony_ci return true; 26162306a36Sopenharmony_ci else if (radeon_msi == 0) 26262306a36Sopenharmony_ci return false; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* Quirks */ 26562306a36Sopenharmony_ci /* HP RS690 only seems to work with MSIs. */ 26662306a36Sopenharmony_ci if ((rdev->pdev->device == 0x791f) && 26762306a36Sopenharmony_ci (rdev->pdev->subsystem_vendor == 0x103c) && 26862306a36Sopenharmony_ci (rdev->pdev->subsystem_device == 0x30c2)) 26962306a36Sopenharmony_ci return true; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Dell RS690 only seems to work with MSIs. */ 27262306a36Sopenharmony_ci if ((rdev->pdev->device == 0x791f) && 27362306a36Sopenharmony_ci (rdev->pdev->subsystem_vendor == 0x1028) && 27462306a36Sopenharmony_ci (rdev->pdev->subsystem_device == 0x01fc)) 27562306a36Sopenharmony_ci return true; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* Dell RS690 only seems to work with MSIs. */ 27862306a36Sopenharmony_ci if ((rdev->pdev->device == 0x791f) && 27962306a36Sopenharmony_ci (rdev->pdev->subsystem_vendor == 0x1028) && 28062306a36Sopenharmony_ci (rdev->pdev->subsystem_device == 0x01fd)) 28162306a36Sopenharmony_ci return true; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* Gateway RS690 only seems to work with MSIs. */ 28462306a36Sopenharmony_ci if ((rdev->pdev->device == 0x791f) && 28562306a36Sopenharmony_ci (rdev->pdev->subsystem_vendor == 0x107b) && 28662306a36Sopenharmony_ci (rdev->pdev->subsystem_device == 0x0185)) 28762306a36Sopenharmony_ci return true; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* try and enable MSIs by default on all RS690s */ 29062306a36Sopenharmony_ci if (rdev->family == CHIP_RS690) 29162306a36Sopenharmony_ci return true; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* RV515 seems to have MSI issues where it loses 29462306a36Sopenharmony_ci * MSI rearms occasionally. This leads to lockups and freezes. 29562306a36Sopenharmony_ci * disable it by default. 29662306a36Sopenharmony_ci */ 29762306a36Sopenharmony_ci if (rdev->family == CHIP_RV515) 29862306a36Sopenharmony_ci return false; 29962306a36Sopenharmony_ci if (rdev->flags & RADEON_IS_IGP) { 30062306a36Sopenharmony_ci /* APUs work fine with MSIs */ 30162306a36Sopenharmony_ci if (rdev->family >= CHIP_PALM) 30262306a36Sopenharmony_ci return true; 30362306a36Sopenharmony_ci /* lots of IGPs have problems with MSIs */ 30462306a36Sopenharmony_ci return false; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci return true; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci/** 31162306a36Sopenharmony_ci * radeon_irq_kms_init - init driver interrupt info 31262306a36Sopenharmony_ci * 31362306a36Sopenharmony_ci * @rdev: radeon device pointer 31462306a36Sopenharmony_ci * 31562306a36Sopenharmony_ci * Sets up the work irq handlers, vblank init, MSIs, etc. (all asics). 31662306a36Sopenharmony_ci * Returns 0 for success, error for failure. 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_ciint radeon_irq_kms_init(struct radeon_device *rdev) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci int r = 0; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci spin_lock_init(&rdev->irq.lock); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* Disable vblank irqs aggressively for power-saving */ 32562306a36Sopenharmony_ci rdev->ddev->vblank_disable_immediate = true; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci r = drm_vblank_init(rdev->ddev, rdev->num_crtc); 32862306a36Sopenharmony_ci if (r) { 32962306a36Sopenharmony_ci return r; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* enable msi */ 33362306a36Sopenharmony_ci rdev->msi_enabled = 0; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (radeon_msi_ok(rdev)) { 33662306a36Sopenharmony_ci int ret = pci_enable_msi(rdev->pdev); 33762306a36Sopenharmony_ci if (!ret) { 33862306a36Sopenharmony_ci rdev->msi_enabled = 1; 33962306a36Sopenharmony_ci dev_info(rdev->dev, "radeon: using MSI.\n"); 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci INIT_DELAYED_WORK(&rdev->hotplug_work, radeon_hotplug_work_func); 34462306a36Sopenharmony_ci INIT_WORK(&rdev->dp_work, radeon_dp_work_func); 34562306a36Sopenharmony_ci INIT_WORK(&rdev->audio_work, r600_audio_update_hdmi); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci rdev->irq.installed = true; 34862306a36Sopenharmony_ci r = radeon_irq_install(rdev, rdev->pdev->irq); 34962306a36Sopenharmony_ci if (r) { 35062306a36Sopenharmony_ci rdev->irq.installed = false; 35162306a36Sopenharmony_ci flush_delayed_work(&rdev->hotplug_work); 35262306a36Sopenharmony_ci return r; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci DRM_INFO("radeon: irq initialized.\n"); 35662306a36Sopenharmony_ci return 0; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci/** 36062306a36Sopenharmony_ci * radeon_irq_kms_fini - tear down driver interrupt info 36162306a36Sopenharmony_ci * 36262306a36Sopenharmony_ci * @rdev: radeon device pointer 36362306a36Sopenharmony_ci * 36462306a36Sopenharmony_ci * Tears down the work irq handlers, vblank handlers, MSIs, etc. (all asics). 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_civoid radeon_irq_kms_fini(struct radeon_device *rdev) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci if (rdev->irq.installed) { 36962306a36Sopenharmony_ci radeon_irq_uninstall(rdev); 37062306a36Sopenharmony_ci rdev->irq.installed = false; 37162306a36Sopenharmony_ci if (rdev->msi_enabled) 37262306a36Sopenharmony_ci pci_disable_msi(rdev->pdev); 37362306a36Sopenharmony_ci flush_delayed_work(&rdev->hotplug_work); 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci/** 37862306a36Sopenharmony_ci * radeon_irq_kms_sw_irq_get - enable software interrupt 37962306a36Sopenharmony_ci * 38062306a36Sopenharmony_ci * @rdev: radeon device pointer 38162306a36Sopenharmony_ci * @ring: ring whose interrupt you want to enable 38262306a36Sopenharmony_ci * 38362306a36Sopenharmony_ci * Enables the software interrupt for a specific ring (all asics). 38462306a36Sopenharmony_ci * The software interrupt is generally used to signal a fence on 38562306a36Sopenharmony_ci * a particular ring. 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_civoid radeon_irq_kms_sw_irq_get(struct radeon_device *rdev, int ring) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci unsigned long irqflags; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (!rdev->irq.installed) 39262306a36Sopenharmony_ci return; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (atomic_inc_return(&rdev->irq.ring_int[ring]) == 1) { 39562306a36Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 39662306a36Sopenharmony_ci radeon_irq_set(rdev); 39762306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci/** 40262306a36Sopenharmony_ci * radeon_irq_kms_sw_irq_get_delayed - enable software interrupt 40362306a36Sopenharmony_ci * 40462306a36Sopenharmony_ci * @rdev: radeon device pointer 40562306a36Sopenharmony_ci * @ring: ring whose interrupt you want to enable 40662306a36Sopenharmony_ci * 40762306a36Sopenharmony_ci * Enables the software interrupt for a specific ring (all asics). 40862306a36Sopenharmony_ci * The software interrupt is generally used to signal a fence on 40962306a36Sopenharmony_ci * a particular ring. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_cibool radeon_irq_kms_sw_irq_get_delayed(struct radeon_device *rdev, int ring) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci return atomic_inc_return(&rdev->irq.ring_int[ring]) == 1; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci/** 41762306a36Sopenharmony_ci * radeon_irq_kms_sw_irq_put - disable software interrupt 41862306a36Sopenharmony_ci * 41962306a36Sopenharmony_ci * @rdev: radeon device pointer 42062306a36Sopenharmony_ci * @ring: ring whose interrupt you want to disable 42162306a36Sopenharmony_ci * 42262306a36Sopenharmony_ci * Disables the software interrupt for a specific ring (all asics). 42362306a36Sopenharmony_ci * The software interrupt is generally used to signal a fence on 42462306a36Sopenharmony_ci * a particular ring. 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_civoid radeon_irq_kms_sw_irq_put(struct radeon_device *rdev, int ring) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci unsigned long irqflags; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (!rdev->irq.installed) 43162306a36Sopenharmony_ci return; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (atomic_dec_and_test(&rdev->irq.ring_int[ring])) { 43462306a36Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 43562306a36Sopenharmony_ci radeon_irq_set(rdev); 43662306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci/** 44162306a36Sopenharmony_ci * radeon_irq_kms_pflip_irq_get - enable pageflip interrupt 44262306a36Sopenharmony_ci * 44362306a36Sopenharmony_ci * @rdev: radeon device pointer 44462306a36Sopenharmony_ci * @crtc: crtc whose interrupt you want to enable 44562306a36Sopenharmony_ci * 44662306a36Sopenharmony_ci * Enables the pageflip interrupt for a specific crtc (all asics). 44762306a36Sopenharmony_ci * For pageflips we use the vblank interrupt source. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_civoid radeon_irq_kms_pflip_irq_get(struct radeon_device *rdev, int crtc) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci unsigned long irqflags; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (crtc < 0 || crtc >= rdev->num_crtc) 45462306a36Sopenharmony_ci return; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (!rdev->irq.installed) 45762306a36Sopenharmony_ci return; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (atomic_inc_return(&rdev->irq.pflip[crtc]) == 1) { 46062306a36Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 46162306a36Sopenharmony_ci radeon_irq_set(rdev); 46262306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci/** 46762306a36Sopenharmony_ci * radeon_irq_kms_pflip_irq_put - disable pageflip interrupt 46862306a36Sopenharmony_ci * 46962306a36Sopenharmony_ci * @rdev: radeon device pointer 47062306a36Sopenharmony_ci * @crtc: crtc whose interrupt you want to disable 47162306a36Sopenharmony_ci * 47262306a36Sopenharmony_ci * Disables the pageflip interrupt for a specific crtc (all asics). 47362306a36Sopenharmony_ci * For pageflips we use the vblank interrupt source. 47462306a36Sopenharmony_ci */ 47562306a36Sopenharmony_civoid radeon_irq_kms_pflip_irq_put(struct radeon_device *rdev, int crtc) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci unsigned long irqflags; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (crtc < 0 || crtc >= rdev->num_crtc) 48062306a36Sopenharmony_ci return; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (!rdev->irq.installed) 48362306a36Sopenharmony_ci return; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci if (atomic_dec_and_test(&rdev->irq.pflip[crtc])) { 48662306a36Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 48762306a36Sopenharmony_ci radeon_irq_set(rdev); 48862306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci/** 49362306a36Sopenharmony_ci * radeon_irq_kms_enable_afmt - enable audio format change interrupt 49462306a36Sopenharmony_ci * 49562306a36Sopenharmony_ci * @rdev: radeon device pointer 49662306a36Sopenharmony_ci * @block: afmt block whose interrupt you want to enable 49762306a36Sopenharmony_ci * 49862306a36Sopenharmony_ci * Enables the afmt change interrupt for a specific afmt block (all asics). 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_civoid radeon_irq_kms_enable_afmt(struct radeon_device *rdev, int block) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci unsigned long irqflags; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (!rdev->irq.installed) 50562306a36Sopenharmony_ci return; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 50862306a36Sopenharmony_ci rdev->irq.afmt[block] = true; 50962306a36Sopenharmony_ci radeon_irq_set(rdev); 51062306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci/** 51562306a36Sopenharmony_ci * radeon_irq_kms_disable_afmt - disable audio format change interrupt 51662306a36Sopenharmony_ci * 51762306a36Sopenharmony_ci * @rdev: radeon device pointer 51862306a36Sopenharmony_ci * @block: afmt block whose interrupt you want to disable 51962306a36Sopenharmony_ci * 52062306a36Sopenharmony_ci * Disables the afmt change interrupt for a specific afmt block (all asics). 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_civoid radeon_irq_kms_disable_afmt(struct radeon_device *rdev, int block) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci unsigned long irqflags; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (!rdev->irq.installed) 52762306a36Sopenharmony_ci return; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 53062306a36Sopenharmony_ci rdev->irq.afmt[block] = false; 53162306a36Sopenharmony_ci radeon_irq_set(rdev); 53262306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci/** 53662306a36Sopenharmony_ci * radeon_irq_kms_enable_hpd - enable hotplug detect interrupt 53762306a36Sopenharmony_ci * 53862306a36Sopenharmony_ci * @rdev: radeon device pointer 53962306a36Sopenharmony_ci * @hpd_mask: mask of hpd pins you want to enable. 54062306a36Sopenharmony_ci * 54162306a36Sopenharmony_ci * Enables the hotplug detect interrupt for a specific hpd pin (all asics). 54262306a36Sopenharmony_ci */ 54362306a36Sopenharmony_civoid radeon_irq_kms_enable_hpd(struct radeon_device *rdev, unsigned hpd_mask) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci unsigned long irqflags; 54662306a36Sopenharmony_ci int i; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (!rdev->irq.installed) 54962306a36Sopenharmony_ci return; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 55262306a36Sopenharmony_ci for (i = 0; i < RADEON_MAX_HPD_PINS; ++i) 55362306a36Sopenharmony_ci rdev->irq.hpd[i] |= !!(hpd_mask & (1 << i)); 55462306a36Sopenharmony_ci radeon_irq_set(rdev); 55562306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci/** 55962306a36Sopenharmony_ci * radeon_irq_kms_disable_hpd - disable hotplug detect interrupt 56062306a36Sopenharmony_ci * 56162306a36Sopenharmony_ci * @rdev: radeon device pointer 56262306a36Sopenharmony_ci * @hpd_mask: mask of hpd pins you want to disable. 56362306a36Sopenharmony_ci * 56462306a36Sopenharmony_ci * Disables the hotplug detect interrupt for a specific hpd pin (all asics). 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_civoid radeon_irq_kms_disable_hpd(struct radeon_device *rdev, unsigned hpd_mask) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci unsigned long irqflags; 56962306a36Sopenharmony_ci int i; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (!rdev->irq.installed) 57262306a36Sopenharmony_ci return; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci spin_lock_irqsave(&rdev->irq.lock, irqflags); 57562306a36Sopenharmony_ci for (i = 0; i < RADEON_MAX_HPD_PINS; ++i) 57662306a36Sopenharmony_ci rdev->irq.hpd[i] &= !(hpd_mask & (1 << i)); 57762306a36Sopenharmony_ci radeon_irq_set(rdev); 57862306a36Sopenharmony_ci spin_unlock_irqrestore(&rdev->irq.lock, irqflags); 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci/** 58262306a36Sopenharmony_ci * radeon_irq_kms_set_irq_n_enabled - helper for updating interrupt enable registers 58362306a36Sopenharmony_ci * 58462306a36Sopenharmony_ci * @rdev: radeon device pointer 58562306a36Sopenharmony_ci * @reg: the register to write to enable/disable interrupts 58662306a36Sopenharmony_ci * @mask: the mask that enables the interrupts 58762306a36Sopenharmony_ci * @enable: whether to enable or disable the interrupt register 58862306a36Sopenharmony_ci * @name: the name of the interrupt register to print to the kernel log 58962306a36Sopenharmony_ci * @n: the number of the interrupt register to print to the kernel log 59062306a36Sopenharmony_ci * 59162306a36Sopenharmony_ci * Helper for updating the enable state of interrupt registers. Checks whether 59262306a36Sopenharmony_ci * or not the interrupt matches the enable state we want. If it doesn't, then 59362306a36Sopenharmony_ci * we update it and print a debugging message to the kernel log indicating the 59462306a36Sopenharmony_ci * new state of the interrupt register. 59562306a36Sopenharmony_ci * 59662306a36Sopenharmony_ci * Used for updating sequences of interrupts registers like HPD1, HPD2, etc. 59762306a36Sopenharmony_ci */ 59862306a36Sopenharmony_civoid radeon_irq_kms_set_irq_n_enabled(struct radeon_device *rdev, 59962306a36Sopenharmony_ci u32 reg, u32 mask, 60062306a36Sopenharmony_ci bool enable, const char *name, unsigned n) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci u32 tmp = RREG32(reg); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* Interrupt state didn't change */ 60562306a36Sopenharmony_ci if (!!(tmp & mask) == enable) 60662306a36Sopenharmony_ci return; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (enable) { 60962306a36Sopenharmony_ci DRM_DEBUG("%s%d interrupts enabled\n", name, n); 61062306a36Sopenharmony_ci WREG32(reg, tmp |= mask); 61162306a36Sopenharmony_ci } else { 61262306a36Sopenharmony_ci DRM_DEBUG("%s%d interrupts disabled\n", name, n); 61362306a36Sopenharmony_ci WREG32(reg, tmp & ~mask); 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci} 616