18c2ecf20Sopenharmony_ci/* i915_irq.c -- IRQ support for the I915 -*- linux-c -*- 28c2ecf20Sopenharmony_ci */ 38c2ecf20Sopenharmony_ci/* 48c2ecf20Sopenharmony_ci * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. 58c2ecf20Sopenharmony_ci * All Rights Reserved. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 88c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the 98c2ecf20Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 108c2ecf20Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 118c2ecf20Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to 128c2ecf20Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 138c2ecf20Sopenharmony_ci * the following conditions: 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the 168c2ecf20Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 178c2ecf20Sopenharmony_ci * of the Software. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 208c2ecf20Sopenharmony_ci * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 218c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 228c2ecf20Sopenharmony_ci * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR 238c2ecf20Sopenharmony_ci * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 248c2ecf20Sopenharmony_ci * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 258c2ecf20Sopenharmony_ci * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <linux/circ_buf.h> 328c2ecf20Sopenharmony_ci#include <linux/slab.h> 338c2ecf20Sopenharmony_ci#include <linux/sysrq.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <drm/drm_drv.h> 368c2ecf20Sopenharmony_ci#include <drm/drm_irq.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include "display/intel_display_types.h" 398c2ecf20Sopenharmony_ci#include "display/intel_fifo_underrun.h" 408c2ecf20Sopenharmony_ci#include "display/intel_hotplug.h" 418c2ecf20Sopenharmony_ci#include "display/intel_lpe_audio.h" 428c2ecf20Sopenharmony_ci#include "display/intel_psr.h" 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#include "gt/intel_breadcrumbs.h" 458c2ecf20Sopenharmony_ci#include "gt/intel_gt.h" 468c2ecf20Sopenharmony_ci#include "gt/intel_gt_irq.h" 478c2ecf20Sopenharmony_ci#include "gt/intel_gt_pm_irq.h" 488c2ecf20Sopenharmony_ci#include "gt/intel_rps.h" 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#include "i915_drv.h" 518c2ecf20Sopenharmony_ci#include "i915_irq.h" 528c2ecf20Sopenharmony_ci#include "i915_trace.h" 538c2ecf20Sopenharmony_ci#include "intel_pm.h" 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/** 568c2ecf20Sopenharmony_ci * DOC: interrupt handling 578c2ecf20Sopenharmony_ci * 588c2ecf20Sopenharmony_ci * These functions provide the basic support for enabling and disabling the 598c2ecf20Sopenharmony_ci * interrupt handling support. There's a lot more functionality in i915_irq.c 608c2ecf20Sopenharmony_ci * and related files, but that will be described in separate chapters. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_citypedef bool (*long_pulse_detect_func)(enum hpd_pin pin, u32 val); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic const u32 hpd_ilk[HPD_NUM_PINS] = { 668c2ecf20Sopenharmony_ci [HPD_PORT_A] = DE_DP_A_HOTPLUG, 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic const u32 hpd_ivb[HPD_NUM_PINS] = { 708c2ecf20Sopenharmony_ci [HPD_PORT_A] = DE_DP_A_HOTPLUG_IVB, 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic const u32 hpd_bdw[HPD_NUM_PINS] = { 748c2ecf20Sopenharmony_ci [HPD_PORT_A] = GEN8_PORT_DP_A_HOTPLUG, 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic const u32 hpd_ibx[HPD_NUM_PINS] = { 788c2ecf20Sopenharmony_ci [HPD_CRT] = SDE_CRT_HOTPLUG, 798c2ecf20Sopenharmony_ci [HPD_SDVO_B] = SDE_SDVOB_HOTPLUG, 808c2ecf20Sopenharmony_ci [HPD_PORT_B] = SDE_PORTB_HOTPLUG, 818c2ecf20Sopenharmony_ci [HPD_PORT_C] = SDE_PORTC_HOTPLUG, 828c2ecf20Sopenharmony_ci [HPD_PORT_D] = SDE_PORTD_HOTPLUG, 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic const u32 hpd_cpt[HPD_NUM_PINS] = { 868c2ecf20Sopenharmony_ci [HPD_CRT] = SDE_CRT_HOTPLUG_CPT, 878c2ecf20Sopenharmony_ci [HPD_SDVO_B] = SDE_SDVOB_HOTPLUG_CPT, 888c2ecf20Sopenharmony_ci [HPD_PORT_B] = SDE_PORTB_HOTPLUG_CPT, 898c2ecf20Sopenharmony_ci [HPD_PORT_C] = SDE_PORTC_HOTPLUG_CPT, 908c2ecf20Sopenharmony_ci [HPD_PORT_D] = SDE_PORTD_HOTPLUG_CPT, 918c2ecf20Sopenharmony_ci}; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic const u32 hpd_spt[HPD_NUM_PINS] = { 948c2ecf20Sopenharmony_ci [HPD_PORT_A] = SDE_PORTA_HOTPLUG_SPT, 958c2ecf20Sopenharmony_ci [HPD_PORT_B] = SDE_PORTB_HOTPLUG_CPT, 968c2ecf20Sopenharmony_ci [HPD_PORT_C] = SDE_PORTC_HOTPLUG_CPT, 978c2ecf20Sopenharmony_ci [HPD_PORT_D] = SDE_PORTD_HOTPLUG_CPT, 988c2ecf20Sopenharmony_ci [HPD_PORT_E] = SDE_PORTE_HOTPLUG_SPT, 998c2ecf20Sopenharmony_ci}; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic const u32 hpd_mask_i915[HPD_NUM_PINS] = { 1028c2ecf20Sopenharmony_ci [HPD_CRT] = CRT_HOTPLUG_INT_EN, 1038c2ecf20Sopenharmony_ci [HPD_SDVO_B] = SDVOB_HOTPLUG_INT_EN, 1048c2ecf20Sopenharmony_ci [HPD_SDVO_C] = SDVOC_HOTPLUG_INT_EN, 1058c2ecf20Sopenharmony_ci [HPD_PORT_B] = PORTB_HOTPLUG_INT_EN, 1068c2ecf20Sopenharmony_ci [HPD_PORT_C] = PORTC_HOTPLUG_INT_EN, 1078c2ecf20Sopenharmony_ci [HPD_PORT_D] = PORTD_HOTPLUG_INT_EN, 1088c2ecf20Sopenharmony_ci}; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic const u32 hpd_status_g4x[HPD_NUM_PINS] = { 1118c2ecf20Sopenharmony_ci [HPD_CRT] = CRT_HOTPLUG_INT_STATUS, 1128c2ecf20Sopenharmony_ci [HPD_SDVO_B] = SDVOB_HOTPLUG_INT_STATUS_G4X, 1138c2ecf20Sopenharmony_ci [HPD_SDVO_C] = SDVOC_HOTPLUG_INT_STATUS_G4X, 1148c2ecf20Sopenharmony_ci [HPD_PORT_B] = PORTB_HOTPLUG_INT_STATUS, 1158c2ecf20Sopenharmony_ci [HPD_PORT_C] = PORTC_HOTPLUG_INT_STATUS, 1168c2ecf20Sopenharmony_ci [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS, 1178c2ecf20Sopenharmony_ci}; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic const u32 hpd_status_i915[HPD_NUM_PINS] = { 1208c2ecf20Sopenharmony_ci [HPD_CRT] = CRT_HOTPLUG_INT_STATUS, 1218c2ecf20Sopenharmony_ci [HPD_SDVO_B] = SDVOB_HOTPLUG_INT_STATUS_I915, 1228c2ecf20Sopenharmony_ci [HPD_SDVO_C] = SDVOC_HOTPLUG_INT_STATUS_I915, 1238c2ecf20Sopenharmony_ci [HPD_PORT_B] = PORTB_HOTPLUG_INT_STATUS, 1248c2ecf20Sopenharmony_ci [HPD_PORT_C] = PORTC_HOTPLUG_INT_STATUS, 1258c2ecf20Sopenharmony_ci [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS, 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic const u32 hpd_bxt[HPD_NUM_PINS] = { 1298c2ecf20Sopenharmony_ci [HPD_PORT_A] = BXT_DE_PORT_HP_DDIA, 1308c2ecf20Sopenharmony_ci [HPD_PORT_B] = BXT_DE_PORT_HP_DDIB, 1318c2ecf20Sopenharmony_ci [HPD_PORT_C] = BXT_DE_PORT_HP_DDIC, 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic const u32 hpd_gen11[HPD_NUM_PINS] = { 1358c2ecf20Sopenharmony_ci [HPD_PORT_TC1] = GEN11_TC_HOTPLUG(PORT_TC1) | GEN11_TBT_HOTPLUG(PORT_TC1), 1368c2ecf20Sopenharmony_ci [HPD_PORT_TC2] = GEN11_TC_HOTPLUG(PORT_TC2) | GEN11_TBT_HOTPLUG(PORT_TC2), 1378c2ecf20Sopenharmony_ci [HPD_PORT_TC3] = GEN11_TC_HOTPLUG(PORT_TC3) | GEN11_TBT_HOTPLUG(PORT_TC3), 1388c2ecf20Sopenharmony_ci [HPD_PORT_TC4] = GEN11_TC_HOTPLUG(PORT_TC4) | GEN11_TBT_HOTPLUG(PORT_TC4), 1398c2ecf20Sopenharmony_ci [HPD_PORT_TC5] = GEN11_TC_HOTPLUG(PORT_TC5) | GEN11_TBT_HOTPLUG(PORT_TC5), 1408c2ecf20Sopenharmony_ci [HPD_PORT_TC6] = GEN11_TC_HOTPLUG(PORT_TC6) | GEN11_TBT_HOTPLUG(PORT_TC6), 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic const u32 hpd_icp[HPD_NUM_PINS] = { 1448c2ecf20Sopenharmony_ci [HPD_PORT_A] = SDE_DDI_HOTPLUG_ICP(PORT_A), 1458c2ecf20Sopenharmony_ci [HPD_PORT_B] = SDE_DDI_HOTPLUG_ICP(PORT_B), 1468c2ecf20Sopenharmony_ci [HPD_PORT_C] = SDE_DDI_HOTPLUG_ICP(PORT_C), 1478c2ecf20Sopenharmony_ci [HPD_PORT_TC1] = SDE_TC_HOTPLUG_ICP(PORT_TC1), 1488c2ecf20Sopenharmony_ci [HPD_PORT_TC2] = SDE_TC_HOTPLUG_ICP(PORT_TC2), 1498c2ecf20Sopenharmony_ci [HPD_PORT_TC3] = SDE_TC_HOTPLUG_ICP(PORT_TC3), 1508c2ecf20Sopenharmony_ci [HPD_PORT_TC4] = SDE_TC_HOTPLUG_ICP(PORT_TC4), 1518c2ecf20Sopenharmony_ci [HPD_PORT_TC5] = SDE_TC_HOTPLUG_ICP(PORT_TC5), 1528c2ecf20Sopenharmony_ci [HPD_PORT_TC6] = SDE_TC_HOTPLUG_ICP(PORT_TC6), 1538c2ecf20Sopenharmony_ci}; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic void intel_hpd_init_pins(struct drm_i915_private *dev_priv) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct i915_hotplug *hpd = &dev_priv->hotplug; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (HAS_GMCH(dev_priv)) { 1608c2ecf20Sopenharmony_ci if (IS_G4X(dev_priv) || IS_VALLEYVIEW(dev_priv) || 1618c2ecf20Sopenharmony_ci IS_CHERRYVIEW(dev_priv)) 1628c2ecf20Sopenharmony_ci hpd->hpd = hpd_status_g4x; 1638c2ecf20Sopenharmony_ci else 1648c2ecf20Sopenharmony_ci hpd->hpd = hpd_status_i915; 1658c2ecf20Sopenharmony_ci return; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 11) 1698c2ecf20Sopenharmony_ci hpd->hpd = hpd_gen11; 1708c2ecf20Sopenharmony_ci else if (IS_GEN9_LP(dev_priv)) 1718c2ecf20Sopenharmony_ci hpd->hpd = hpd_bxt; 1728c2ecf20Sopenharmony_ci else if (INTEL_GEN(dev_priv) >= 8) 1738c2ecf20Sopenharmony_ci hpd->hpd = hpd_bdw; 1748c2ecf20Sopenharmony_ci else if (INTEL_GEN(dev_priv) >= 7) 1758c2ecf20Sopenharmony_ci hpd->hpd = hpd_ivb; 1768c2ecf20Sopenharmony_ci else 1778c2ecf20Sopenharmony_ci hpd->hpd = hpd_ilk; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (!HAS_PCH_SPLIT(dev_priv) || HAS_PCH_NOP(dev_priv)) 1808c2ecf20Sopenharmony_ci return; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (HAS_PCH_TGP(dev_priv) || HAS_PCH_JSP(dev_priv) || 1838c2ecf20Sopenharmony_ci HAS_PCH_ICP(dev_priv) || HAS_PCH_MCC(dev_priv)) 1848c2ecf20Sopenharmony_ci hpd->pch_hpd = hpd_icp; 1858c2ecf20Sopenharmony_ci else if (HAS_PCH_CNP(dev_priv) || HAS_PCH_SPT(dev_priv)) 1868c2ecf20Sopenharmony_ci hpd->pch_hpd = hpd_spt; 1878c2ecf20Sopenharmony_ci else if (HAS_PCH_LPT(dev_priv) || HAS_PCH_CPT(dev_priv)) 1888c2ecf20Sopenharmony_ci hpd->pch_hpd = hpd_cpt; 1898c2ecf20Sopenharmony_ci else if (HAS_PCH_IBX(dev_priv)) 1908c2ecf20Sopenharmony_ci hpd->pch_hpd = hpd_ibx; 1918c2ecf20Sopenharmony_ci else 1928c2ecf20Sopenharmony_ci MISSING_CASE(INTEL_PCH_TYPE(dev_priv)); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic void 1968c2ecf20Sopenharmony_ciintel_handle_vblank(struct drm_i915_private *dev_priv, enum pipe pipe) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv, pipe); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci drm_crtc_handle_vblank(&crtc->base); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_civoid gen3_irq_reset(struct intel_uncore *uncore, i915_reg_t imr, 2048c2ecf20Sopenharmony_ci i915_reg_t iir, i915_reg_t ier) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci intel_uncore_write(uncore, imr, 0xffffffff); 2078c2ecf20Sopenharmony_ci intel_uncore_posting_read(uncore, imr); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci intel_uncore_write(uncore, ier, 0); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* IIR can theoretically queue up two events. Be paranoid. */ 2128c2ecf20Sopenharmony_ci intel_uncore_write(uncore, iir, 0xffffffff); 2138c2ecf20Sopenharmony_ci intel_uncore_posting_read(uncore, iir); 2148c2ecf20Sopenharmony_ci intel_uncore_write(uncore, iir, 0xffffffff); 2158c2ecf20Sopenharmony_ci intel_uncore_posting_read(uncore, iir); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_civoid gen2_irq_reset(struct intel_uncore *uncore) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci intel_uncore_write16(uncore, GEN2_IMR, 0xffff); 2218c2ecf20Sopenharmony_ci intel_uncore_posting_read16(uncore, GEN2_IMR); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci intel_uncore_write16(uncore, GEN2_IER, 0); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* IIR can theoretically queue up two events. Be paranoid. */ 2268c2ecf20Sopenharmony_ci intel_uncore_write16(uncore, GEN2_IIR, 0xffff); 2278c2ecf20Sopenharmony_ci intel_uncore_posting_read16(uncore, GEN2_IIR); 2288c2ecf20Sopenharmony_ci intel_uncore_write16(uncore, GEN2_IIR, 0xffff); 2298c2ecf20Sopenharmony_ci intel_uncore_posting_read16(uncore, GEN2_IIR); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/* 2338c2ecf20Sopenharmony_ci * We should clear IMR at preinstall/uninstall, and just check at postinstall. 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_cistatic void gen3_assert_iir_is_zero(struct intel_uncore *uncore, i915_reg_t reg) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci u32 val = intel_uncore_read(uncore, reg); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (val == 0) 2408c2ecf20Sopenharmony_ci return; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci drm_WARN(&uncore->i915->drm, 1, 2438c2ecf20Sopenharmony_ci "Interrupt register 0x%x is not zero: 0x%08x\n", 2448c2ecf20Sopenharmony_ci i915_mmio_reg_offset(reg), val); 2458c2ecf20Sopenharmony_ci intel_uncore_write(uncore, reg, 0xffffffff); 2468c2ecf20Sopenharmony_ci intel_uncore_posting_read(uncore, reg); 2478c2ecf20Sopenharmony_ci intel_uncore_write(uncore, reg, 0xffffffff); 2488c2ecf20Sopenharmony_ci intel_uncore_posting_read(uncore, reg); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic void gen2_assert_iir_is_zero(struct intel_uncore *uncore) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci u16 val = intel_uncore_read16(uncore, GEN2_IIR); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (val == 0) 2568c2ecf20Sopenharmony_ci return; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci drm_WARN(&uncore->i915->drm, 1, 2598c2ecf20Sopenharmony_ci "Interrupt register 0x%x is not zero: 0x%08x\n", 2608c2ecf20Sopenharmony_ci i915_mmio_reg_offset(GEN2_IIR), val); 2618c2ecf20Sopenharmony_ci intel_uncore_write16(uncore, GEN2_IIR, 0xffff); 2628c2ecf20Sopenharmony_ci intel_uncore_posting_read16(uncore, GEN2_IIR); 2638c2ecf20Sopenharmony_ci intel_uncore_write16(uncore, GEN2_IIR, 0xffff); 2648c2ecf20Sopenharmony_ci intel_uncore_posting_read16(uncore, GEN2_IIR); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_civoid gen3_irq_init(struct intel_uncore *uncore, 2688c2ecf20Sopenharmony_ci i915_reg_t imr, u32 imr_val, 2698c2ecf20Sopenharmony_ci i915_reg_t ier, u32 ier_val, 2708c2ecf20Sopenharmony_ci i915_reg_t iir) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci gen3_assert_iir_is_zero(uncore, iir); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci intel_uncore_write(uncore, ier, ier_val); 2758c2ecf20Sopenharmony_ci intel_uncore_write(uncore, imr, imr_val); 2768c2ecf20Sopenharmony_ci intel_uncore_posting_read(uncore, imr); 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_civoid gen2_irq_init(struct intel_uncore *uncore, 2808c2ecf20Sopenharmony_ci u32 imr_val, u32 ier_val) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci gen2_assert_iir_is_zero(uncore); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci intel_uncore_write16(uncore, GEN2_IER, ier_val); 2858c2ecf20Sopenharmony_ci intel_uncore_write16(uncore, GEN2_IMR, imr_val); 2868c2ecf20Sopenharmony_ci intel_uncore_posting_read16(uncore, GEN2_IMR); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci/* For display hotplug interrupt */ 2908c2ecf20Sopenharmony_cistatic inline void 2918c2ecf20Sopenharmony_cii915_hotplug_interrupt_update_locked(struct drm_i915_private *dev_priv, 2928c2ecf20Sopenharmony_ci u32 mask, 2938c2ecf20Sopenharmony_ci u32 bits) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci u32 val; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci lockdep_assert_held(&dev_priv->irq_lock); 2988c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, bits & ~mask); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci val = I915_READ(PORT_HOTPLUG_EN); 3018c2ecf20Sopenharmony_ci val &= ~mask; 3028c2ecf20Sopenharmony_ci val |= bits; 3038c2ecf20Sopenharmony_ci I915_WRITE(PORT_HOTPLUG_EN, val); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci/** 3078c2ecf20Sopenharmony_ci * i915_hotplug_interrupt_update - update hotplug interrupt enable 3088c2ecf20Sopenharmony_ci * @dev_priv: driver private 3098c2ecf20Sopenharmony_ci * @mask: bits to update 3108c2ecf20Sopenharmony_ci * @bits: bits to enable 3118c2ecf20Sopenharmony_ci * NOTE: the HPD enable bits are modified both inside and outside 3128c2ecf20Sopenharmony_ci * of an interrupt context. To avoid that read-modify-write cycles 3138c2ecf20Sopenharmony_ci * interfer, these bits are protected by a spinlock. Since this 3148c2ecf20Sopenharmony_ci * function is usually not called from a context where the lock is 3158c2ecf20Sopenharmony_ci * held already, this function acquires the lock itself. A non-locking 3168c2ecf20Sopenharmony_ci * version is also available. 3178c2ecf20Sopenharmony_ci */ 3188c2ecf20Sopenharmony_civoid i915_hotplug_interrupt_update(struct drm_i915_private *dev_priv, 3198c2ecf20Sopenharmony_ci u32 mask, 3208c2ecf20Sopenharmony_ci u32 bits) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci spin_lock_irq(&dev_priv->irq_lock); 3238c2ecf20Sopenharmony_ci i915_hotplug_interrupt_update_locked(dev_priv, mask, bits); 3248c2ecf20Sopenharmony_ci spin_unlock_irq(&dev_priv->irq_lock); 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci/** 3288c2ecf20Sopenharmony_ci * ilk_update_display_irq - update DEIMR 3298c2ecf20Sopenharmony_ci * @dev_priv: driver private 3308c2ecf20Sopenharmony_ci * @interrupt_mask: mask of interrupt bits to update 3318c2ecf20Sopenharmony_ci * @enabled_irq_mask: mask of interrupt bits to enable 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_civoid ilk_update_display_irq(struct drm_i915_private *dev_priv, 3348c2ecf20Sopenharmony_ci u32 interrupt_mask, 3358c2ecf20Sopenharmony_ci u32 enabled_irq_mask) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci u32 new_val; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci lockdep_assert_held(&dev_priv->irq_lock); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, enabled_irq_mask & ~interrupt_mask); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (drm_WARN_ON(&dev_priv->drm, !intel_irqs_enabled(dev_priv))) 3448c2ecf20Sopenharmony_ci return; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci new_val = dev_priv->irq_mask; 3478c2ecf20Sopenharmony_ci new_val &= ~interrupt_mask; 3488c2ecf20Sopenharmony_ci new_val |= (~enabled_irq_mask & interrupt_mask); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (new_val != dev_priv->irq_mask) { 3518c2ecf20Sopenharmony_ci dev_priv->irq_mask = new_val; 3528c2ecf20Sopenharmony_ci I915_WRITE(DEIMR, dev_priv->irq_mask); 3538c2ecf20Sopenharmony_ci POSTING_READ(DEIMR); 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci/** 3588c2ecf20Sopenharmony_ci * bdw_update_port_irq - update DE port interrupt 3598c2ecf20Sopenharmony_ci * @dev_priv: driver private 3608c2ecf20Sopenharmony_ci * @interrupt_mask: mask of interrupt bits to update 3618c2ecf20Sopenharmony_ci * @enabled_irq_mask: mask of interrupt bits to enable 3628c2ecf20Sopenharmony_ci */ 3638c2ecf20Sopenharmony_cistatic void bdw_update_port_irq(struct drm_i915_private *dev_priv, 3648c2ecf20Sopenharmony_ci u32 interrupt_mask, 3658c2ecf20Sopenharmony_ci u32 enabled_irq_mask) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci u32 new_val; 3688c2ecf20Sopenharmony_ci u32 old_val; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci lockdep_assert_held(&dev_priv->irq_lock); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, enabled_irq_mask & ~interrupt_mask); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (drm_WARN_ON(&dev_priv->drm, !intel_irqs_enabled(dev_priv))) 3758c2ecf20Sopenharmony_ci return; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci old_val = I915_READ(GEN8_DE_PORT_IMR); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci new_val = old_val; 3808c2ecf20Sopenharmony_ci new_val &= ~interrupt_mask; 3818c2ecf20Sopenharmony_ci new_val |= (~enabled_irq_mask & interrupt_mask); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (new_val != old_val) { 3848c2ecf20Sopenharmony_ci I915_WRITE(GEN8_DE_PORT_IMR, new_val); 3858c2ecf20Sopenharmony_ci POSTING_READ(GEN8_DE_PORT_IMR); 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci/** 3908c2ecf20Sopenharmony_ci * bdw_update_pipe_irq - update DE pipe interrupt 3918c2ecf20Sopenharmony_ci * @dev_priv: driver private 3928c2ecf20Sopenharmony_ci * @pipe: pipe whose interrupt to update 3938c2ecf20Sopenharmony_ci * @interrupt_mask: mask of interrupt bits to update 3948c2ecf20Sopenharmony_ci * @enabled_irq_mask: mask of interrupt bits to enable 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_civoid bdw_update_pipe_irq(struct drm_i915_private *dev_priv, 3978c2ecf20Sopenharmony_ci enum pipe pipe, 3988c2ecf20Sopenharmony_ci u32 interrupt_mask, 3998c2ecf20Sopenharmony_ci u32 enabled_irq_mask) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci u32 new_val; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci lockdep_assert_held(&dev_priv->irq_lock); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, enabled_irq_mask & ~interrupt_mask); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (drm_WARN_ON(&dev_priv->drm, !intel_irqs_enabled(dev_priv))) 4088c2ecf20Sopenharmony_ci return; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci new_val = dev_priv->de_irq_mask[pipe]; 4118c2ecf20Sopenharmony_ci new_val &= ~interrupt_mask; 4128c2ecf20Sopenharmony_ci new_val |= (~enabled_irq_mask & interrupt_mask); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (new_val != dev_priv->de_irq_mask[pipe]) { 4158c2ecf20Sopenharmony_ci dev_priv->de_irq_mask[pipe] = new_val; 4168c2ecf20Sopenharmony_ci I915_WRITE(GEN8_DE_PIPE_IMR(pipe), dev_priv->de_irq_mask[pipe]); 4178c2ecf20Sopenharmony_ci POSTING_READ(GEN8_DE_PIPE_IMR(pipe)); 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci/** 4228c2ecf20Sopenharmony_ci * ibx_display_interrupt_update - update SDEIMR 4238c2ecf20Sopenharmony_ci * @dev_priv: driver private 4248c2ecf20Sopenharmony_ci * @interrupt_mask: mask of interrupt bits to update 4258c2ecf20Sopenharmony_ci * @enabled_irq_mask: mask of interrupt bits to enable 4268c2ecf20Sopenharmony_ci */ 4278c2ecf20Sopenharmony_civoid ibx_display_interrupt_update(struct drm_i915_private *dev_priv, 4288c2ecf20Sopenharmony_ci u32 interrupt_mask, 4298c2ecf20Sopenharmony_ci u32 enabled_irq_mask) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci u32 sdeimr = I915_READ(SDEIMR); 4328c2ecf20Sopenharmony_ci sdeimr &= ~interrupt_mask; 4338c2ecf20Sopenharmony_ci sdeimr |= (~enabled_irq_mask & interrupt_mask); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, enabled_irq_mask & ~interrupt_mask); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci lockdep_assert_held(&dev_priv->irq_lock); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (drm_WARN_ON(&dev_priv->drm, !intel_irqs_enabled(dev_priv))) 4408c2ecf20Sopenharmony_ci return; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci I915_WRITE(SDEIMR, sdeimr); 4438c2ecf20Sopenharmony_ci POSTING_READ(SDEIMR); 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ciu32 i915_pipestat_enable_mask(struct drm_i915_private *dev_priv, 4478c2ecf20Sopenharmony_ci enum pipe pipe) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci u32 status_mask = dev_priv->pipestat_irq_mask[pipe]; 4508c2ecf20Sopenharmony_ci u32 enable_mask = status_mask << 16; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci lockdep_assert_held(&dev_priv->irq_lock); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) < 5) 4558c2ecf20Sopenharmony_ci goto out; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* 4588c2ecf20Sopenharmony_ci * On pipe A we don't support the PSR interrupt yet, 4598c2ecf20Sopenharmony_ci * on pipe B and C the same bit MBZ. 4608c2ecf20Sopenharmony_ci */ 4618c2ecf20Sopenharmony_ci if (drm_WARN_ON_ONCE(&dev_priv->drm, 4628c2ecf20Sopenharmony_ci status_mask & PIPE_A_PSR_STATUS_VLV)) 4638c2ecf20Sopenharmony_ci return 0; 4648c2ecf20Sopenharmony_ci /* 4658c2ecf20Sopenharmony_ci * On pipe B and C we don't support the PSR interrupt yet, on pipe 4668c2ecf20Sopenharmony_ci * A the same bit is for perf counters which we don't use either. 4678c2ecf20Sopenharmony_ci */ 4688c2ecf20Sopenharmony_ci if (drm_WARN_ON_ONCE(&dev_priv->drm, 4698c2ecf20Sopenharmony_ci status_mask & PIPE_B_PSR_STATUS_VLV)) 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci enable_mask &= ~(PIPE_FIFO_UNDERRUN_STATUS | 4738c2ecf20Sopenharmony_ci SPRITE0_FLIP_DONE_INT_EN_VLV | 4748c2ecf20Sopenharmony_ci SPRITE1_FLIP_DONE_INT_EN_VLV); 4758c2ecf20Sopenharmony_ci if (status_mask & SPRITE0_FLIP_DONE_INT_STATUS_VLV) 4768c2ecf20Sopenharmony_ci enable_mask |= SPRITE0_FLIP_DONE_INT_EN_VLV; 4778c2ecf20Sopenharmony_ci if (status_mask & SPRITE1_FLIP_DONE_INT_STATUS_VLV) 4788c2ecf20Sopenharmony_ci enable_mask |= SPRITE1_FLIP_DONE_INT_EN_VLV; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ciout: 4818c2ecf20Sopenharmony_ci drm_WARN_ONCE(&dev_priv->drm, 4828c2ecf20Sopenharmony_ci enable_mask & ~PIPESTAT_INT_ENABLE_MASK || 4838c2ecf20Sopenharmony_ci status_mask & ~PIPESTAT_INT_STATUS_MASK, 4848c2ecf20Sopenharmony_ci "pipe %c: enable_mask=0x%x, status_mask=0x%x\n", 4858c2ecf20Sopenharmony_ci pipe_name(pipe), enable_mask, status_mask); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci return enable_mask; 4888c2ecf20Sopenharmony_ci} 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_civoid i915_enable_pipestat(struct drm_i915_private *dev_priv, 4918c2ecf20Sopenharmony_ci enum pipe pipe, u32 status_mask) 4928c2ecf20Sopenharmony_ci{ 4938c2ecf20Sopenharmony_ci i915_reg_t reg = PIPESTAT(pipe); 4948c2ecf20Sopenharmony_ci u32 enable_mask; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci drm_WARN_ONCE(&dev_priv->drm, status_mask & ~PIPESTAT_INT_STATUS_MASK, 4978c2ecf20Sopenharmony_ci "pipe %c: status_mask=0x%x\n", 4988c2ecf20Sopenharmony_ci pipe_name(pipe), status_mask); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci lockdep_assert_held(&dev_priv->irq_lock); 5018c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, !intel_irqs_enabled(dev_priv)); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci if ((dev_priv->pipestat_irq_mask[pipe] & status_mask) == status_mask) 5048c2ecf20Sopenharmony_ci return; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci dev_priv->pipestat_irq_mask[pipe] |= status_mask; 5078c2ecf20Sopenharmony_ci enable_mask = i915_pipestat_enable_mask(dev_priv, pipe); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci I915_WRITE(reg, enable_mask | status_mask); 5108c2ecf20Sopenharmony_ci POSTING_READ(reg); 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_civoid i915_disable_pipestat(struct drm_i915_private *dev_priv, 5148c2ecf20Sopenharmony_ci enum pipe pipe, u32 status_mask) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci i915_reg_t reg = PIPESTAT(pipe); 5178c2ecf20Sopenharmony_ci u32 enable_mask; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci drm_WARN_ONCE(&dev_priv->drm, status_mask & ~PIPESTAT_INT_STATUS_MASK, 5208c2ecf20Sopenharmony_ci "pipe %c: status_mask=0x%x\n", 5218c2ecf20Sopenharmony_ci pipe_name(pipe), status_mask); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci lockdep_assert_held(&dev_priv->irq_lock); 5248c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, !intel_irqs_enabled(dev_priv)); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci if ((dev_priv->pipestat_irq_mask[pipe] & status_mask) == 0) 5278c2ecf20Sopenharmony_ci return; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci dev_priv->pipestat_irq_mask[pipe] &= ~status_mask; 5308c2ecf20Sopenharmony_ci enable_mask = i915_pipestat_enable_mask(dev_priv, pipe); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci I915_WRITE(reg, enable_mask | status_mask); 5338c2ecf20Sopenharmony_ci POSTING_READ(reg); 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic bool i915_has_asle(struct drm_i915_private *dev_priv) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci if (!dev_priv->opregion.asle) 5398c2ecf20Sopenharmony_ci return false; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci return IS_PINEVIEW(dev_priv) || IS_MOBILE(dev_priv); 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci/** 5458c2ecf20Sopenharmony_ci * i915_enable_asle_pipestat - enable ASLE pipestat for OpRegion 5468c2ecf20Sopenharmony_ci * @dev_priv: i915 device private 5478c2ecf20Sopenharmony_ci */ 5488c2ecf20Sopenharmony_cistatic void i915_enable_asle_pipestat(struct drm_i915_private *dev_priv) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci if (!i915_has_asle(dev_priv)) 5518c2ecf20Sopenharmony_ci return; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci spin_lock_irq(&dev_priv->irq_lock); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci i915_enable_pipestat(dev_priv, PIPE_B, PIPE_LEGACY_BLC_EVENT_STATUS); 5568c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 4) 5578c2ecf20Sopenharmony_ci i915_enable_pipestat(dev_priv, PIPE_A, 5588c2ecf20Sopenharmony_ci PIPE_LEGACY_BLC_EVENT_STATUS); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci spin_unlock_irq(&dev_priv->irq_lock); 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci/* 5648c2ecf20Sopenharmony_ci * This timing diagram depicts the video signal in and 5658c2ecf20Sopenharmony_ci * around the vertical blanking period. 5668c2ecf20Sopenharmony_ci * 5678c2ecf20Sopenharmony_ci * Assumptions about the fictitious mode used in this example: 5688c2ecf20Sopenharmony_ci * vblank_start >= 3 5698c2ecf20Sopenharmony_ci * vsync_start = vblank_start + 1 5708c2ecf20Sopenharmony_ci * vsync_end = vblank_start + 2 5718c2ecf20Sopenharmony_ci * vtotal = vblank_start + 3 5728c2ecf20Sopenharmony_ci * 5738c2ecf20Sopenharmony_ci * start of vblank: 5748c2ecf20Sopenharmony_ci * latch double buffered registers 5758c2ecf20Sopenharmony_ci * increment frame counter (ctg+) 5768c2ecf20Sopenharmony_ci * generate start of vblank interrupt (gen4+) 5778c2ecf20Sopenharmony_ci * | 5788c2ecf20Sopenharmony_ci * | frame start: 5798c2ecf20Sopenharmony_ci * | generate frame start interrupt (aka. vblank interrupt) (gmch) 5808c2ecf20Sopenharmony_ci * | may be shifted forward 1-3 extra lines via PIPECONF 5818c2ecf20Sopenharmony_ci * | | 5828c2ecf20Sopenharmony_ci * | | start of vsync: 5838c2ecf20Sopenharmony_ci * | | generate vsync interrupt 5848c2ecf20Sopenharmony_ci * | | | 5858c2ecf20Sopenharmony_ci * ___xxxx___ ___xxxx___ ___xxxx___ ___xxxx___ ___xxxx___ ___xxxx 5868c2ecf20Sopenharmony_ci * . \hs/ . \hs/ \hs/ \hs/ . \hs/ 5878c2ecf20Sopenharmony_ci * ----va---> <-----------------vb--------------------> <--------va------------- 5888c2ecf20Sopenharmony_ci * | | <----vs-----> | 5898c2ecf20Sopenharmony_ci * -vbs-----> <---vbs+1---> <---vbs+2---> <-----0-----> <-----1-----> <-----2--- (scanline counter gen2) 5908c2ecf20Sopenharmony_ci * -vbs-2---> <---vbs-1---> <---vbs-----> <---vbs+1---> <---vbs+2---> <-----0--- (scanline counter gen3+) 5918c2ecf20Sopenharmony_ci * -vbs-2---> <---vbs-2---> <---vbs-1---> <---vbs-----> <---vbs+1---> <---vbs+2- (scanline counter hsw+ hdmi) 5928c2ecf20Sopenharmony_ci * | | | 5938c2ecf20Sopenharmony_ci * last visible pixel first visible pixel 5948c2ecf20Sopenharmony_ci * | increment frame counter (gen3/4) 5958c2ecf20Sopenharmony_ci * pixel counter = vblank_start * htotal pixel counter = 0 (gen3/4) 5968c2ecf20Sopenharmony_ci * 5978c2ecf20Sopenharmony_ci * x = horizontal active 5988c2ecf20Sopenharmony_ci * _ = horizontal blanking 5998c2ecf20Sopenharmony_ci * hs = horizontal sync 6008c2ecf20Sopenharmony_ci * va = vertical active 6018c2ecf20Sopenharmony_ci * vb = vertical blanking 6028c2ecf20Sopenharmony_ci * vs = vertical sync 6038c2ecf20Sopenharmony_ci * vbs = vblank_start (number) 6048c2ecf20Sopenharmony_ci * 6058c2ecf20Sopenharmony_ci * Summary: 6068c2ecf20Sopenharmony_ci * - most events happen at the start of horizontal sync 6078c2ecf20Sopenharmony_ci * - frame start happens at the start of horizontal blank, 1-4 lines 6088c2ecf20Sopenharmony_ci * (depending on PIPECONF settings) after the start of vblank 6098c2ecf20Sopenharmony_ci * - gen3/4 pixel and frame counter are synchronized with the start 6108c2ecf20Sopenharmony_ci * of horizontal active on the first line of vertical active 6118c2ecf20Sopenharmony_ci */ 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci/* Called from drm generic code, passed a 'crtc', which 6148c2ecf20Sopenharmony_ci * we use as a pipe index 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_ciu32 i915_get_vblank_counter(struct drm_crtc *crtc) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->dev); 6198c2ecf20Sopenharmony_ci struct drm_vblank_crtc *vblank = &dev_priv->drm.vblank[drm_crtc_index(crtc)]; 6208c2ecf20Sopenharmony_ci const struct drm_display_mode *mode = &vblank->hwmode; 6218c2ecf20Sopenharmony_ci enum pipe pipe = to_intel_crtc(crtc)->pipe; 6228c2ecf20Sopenharmony_ci i915_reg_t high_frame, low_frame; 6238c2ecf20Sopenharmony_ci u32 high1, high2, low, pixel, vbl_start, hsync_start, htotal; 6248c2ecf20Sopenharmony_ci unsigned long irqflags; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci /* 6278c2ecf20Sopenharmony_ci * On i965gm TV output the frame counter only works up to 6288c2ecf20Sopenharmony_ci * the point when we enable the TV encoder. After that the 6298c2ecf20Sopenharmony_ci * frame counter ceases to work and reads zero. We need a 6308c2ecf20Sopenharmony_ci * vblank wait before enabling the TV encoder and so we 6318c2ecf20Sopenharmony_ci * have to enable vblank interrupts while the frame counter 6328c2ecf20Sopenharmony_ci * is still in a working state. However the core vblank code 6338c2ecf20Sopenharmony_ci * does not like us returning non-zero frame counter values 6348c2ecf20Sopenharmony_ci * when we've told it that we don't have a working frame 6358c2ecf20Sopenharmony_ci * counter. Thus we must stop non-zero values leaking out. 6368c2ecf20Sopenharmony_ci */ 6378c2ecf20Sopenharmony_ci if (!vblank->max_vblank_count) 6388c2ecf20Sopenharmony_ci return 0; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci htotal = mode->crtc_htotal; 6418c2ecf20Sopenharmony_ci hsync_start = mode->crtc_hsync_start; 6428c2ecf20Sopenharmony_ci vbl_start = mode->crtc_vblank_start; 6438c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_INTERLACE) 6448c2ecf20Sopenharmony_ci vbl_start = DIV_ROUND_UP(vbl_start, 2); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci /* Convert to pixel count */ 6478c2ecf20Sopenharmony_ci vbl_start *= htotal; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci /* Start of vblank event occurs at start of hsync */ 6508c2ecf20Sopenharmony_ci vbl_start -= htotal - hsync_start; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci high_frame = PIPEFRAME(pipe); 6538c2ecf20Sopenharmony_ci low_frame = PIPEFRAMEPIXEL(pipe); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci /* 6588c2ecf20Sopenharmony_ci * High & low register fields aren't synchronized, so make sure 6598c2ecf20Sopenharmony_ci * we get a low value that's stable across two reads of the high 6608c2ecf20Sopenharmony_ci * register. 6618c2ecf20Sopenharmony_ci */ 6628c2ecf20Sopenharmony_ci do { 6638c2ecf20Sopenharmony_ci high1 = intel_de_read_fw(dev_priv, high_frame) & PIPE_FRAME_HIGH_MASK; 6648c2ecf20Sopenharmony_ci low = intel_de_read_fw(dev_priv, low_frame); 6658c2ecf20Sopenharmony_ci high2 = intel_de_read_fw(dev_priv, high_frame) & PIPE_FRAME_HIGH_MASK; 6668c2ecf20Sopenharmony_ci } while (high1 != high2); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci high1 >>= PIPE_FRAME_HIGH_SHIFT; 6718c2ecf20Sopenharmony_ci pixel = low & PIPE_PIXEL_MASK; 6728c2ecf20Sopenharmony_ci low >>= PIPE_FRAME_LOW_SHIFT; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci /* 6758c2ecf20Sopenharmony_ci * The frame counter increments at beginning of active. 6768c2ecf20Sopenharmony_ci * Cook up a vblank counter by also checking the pixel 6778c2ecf20Sopenharmony_ci * counter against vblank start. 6788c2ecf20Sopenharmony_ci */ 6798c2ecf20Sopenharmony_ci return (((high1 << 8) | low) + (pixel >= vbl_start)) & 0xffffff; 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ciu32 g4x_get_vblank_counter(struct drm_crtc *crtc) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->dev); 6858c2ecf20Sopenharmony_ci enum pipe pipe = to_intel_crtc(crtc)->pipe; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci return I915_READ(PIPE_FRMCOUNT_G4X(pipe)); 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci/* 6918c2ecf20Sopenharmony_ci * On certain encoders on certain platforms, pipe 6928c2ecf20Sopenharmony_ci * scanline register will not work to get the scanline, 6938c2ecf20Sopenharmony_ci * since the timings are driven from the PORT or issues 6948c2ecf20Sopenharmony_ci * with scanline register updates. 6958c2ecf20Sopenharmony_ci * This function will use Framestamp and current 6968c2ecf20Sopenharmony_ci * timestamp registers to calculate the scanline. 6978c2ecf20Sopenharmony_ci */ 6988c2ecf20Sopenharmony_cistatic u32 __intel_get_crtc_scanline_from_timestamp(struct intel_crtc *crtc) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 7018c2ecf20Sopenharmony_ci struct drm_vblank_crtc *vblank = 7028c2ecf20Sopenharmony_ci &crtc->base.dev->vblank[drm_crtc_index(&crtc->base)]; 7038c2ecf20Sopenharmony_ci const struct drm_display_mode *mode = &vblank->hwmode; 7048c2ecf20Sopenharmony_ci u32 vblank_start = mode->crtc_vblank_start; 7058c2ecf20Sopenharmony_ci u32 vtotal = mode->crtc_vtotal; 7068c2ecf20Sopenharmony_ci u32 htotal = mode->crtc_htotal; 7078c2ecf20Sopenharmony_ci u32 clock = mode->crtc_clock; 7088c2ecf20Sopenharmony_ci u32 scanline, scan_prev_time, scan_curr_time, scan_post_time; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci /* 7118c2ecf20Sopenharmony_ci * To avoid the race condition where we might cross into the 7128c2ecf20Sopenharmony_ci * next vblank just between the PIPE_FRMTMSTMP and TIMESTAMP_CTR 7138c2ecf20Sopenharmony_ci * reads. We make sure we read PIPE_FRMTMSTMP and TIMESTAMP_CTR 7148c2ecf20Sopenharmony_ci * during the same frame. 7158c2ecf20Sopenharmony_ci */ 7168c2ecf20Sopenharmony_ci do { 7178c2ecf20Sopenharmony_ci /* 7188c2ecf20Sopenharmony_ci * This field provides read back of the display 7198c2ecf20Sopenharmony_ci * pipe frame time stamp. The time stamp value 7208c2ecf20Sopenharmony_ci * is sampled at every start of vertical blank. 7218c2ecf20Sopenharmony_ci */ 7228c2ecf20Sopenharmony_ci scan_prev_time = intel_de_read_fw(dev_priv, 7238c2ecf20Sopenharmony_ci PIPE_FRMTMSTMP(crtc->pipe)); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci /* 7268c2ecf20Sopenharmony_ci * The TIMESTAMP_CTR register has the current 7278c2ecf20Sopenharmony_ci * time stamp value. 7288c2ecf20Sopenharmony_ci */ 7298c2ecf20Sopenharmony_ci scan_curr_time = intel_de_read_fw(dev_priv, IVB_TIMESTAMP_CTR); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci scan_post_time = intel_de_read_fw(dev_priv, 7328c2ecf20Sopenharmony_ci PIPE_FRMTMSTMP(crtc->pipe)); 7338c2ecf20Sopenharmony_ci } while (scan_post_time != scan_prev_time); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci scanline = div_u64(mul_u32_u32(scan_curr_time - scan_prev_time, 7368c2ecf20Sopenharmony_ci clock), 1000 * htotal); 7378c2ecf20Sopenharmony_ci scanline = min(scanline, vtotal - 1); 7388c2ecf20Sopenharmony_ci scanline = (scanline + vblank_start) % vtotal; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci return scanline; 7418c2ecf20Sopenharmony_ci} 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci/* 7448c2ecf20Sopenharmony_ci * intel_de_read_fw(), only for fast reads of display block, no need for 7458c2ecf20Sopenharmony_ci * forcewake etc. 7468c2ecf20Sopenharmony_ci */ 7478c2ecf20Sopenharmony_cistatic int __intel_get_crtc_scanline(struct intel_crtc *crtc) 7488c2ecf20Sopenharmony_ci{ 7498c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->base.dev; 7508c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(dev); 7518c2ecf20Sopenharmony_ci const struct drm_display_mode *mode; 7528c2ecf20Sopenharmony_ci struct drm_vblank_crtc *vblank; 7538c2ecf20Sopenharmony_ci enum pipe pipe = crtc->pipe; 7548c2ecf20Sopenharmony_ci int position, vtotal; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci if (!crtc->active) 7578c2ecf20Sopenharmony_ci return -1; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci vblank = &crtc->base.dev->vblank[drm_crtc_index(&crtc->base)]; 7608c2ecf20Sopenharmony_ci mode = &vblank->hwmode; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci if (crtc->mode_flags & I915_MODE_FLAG_GET_SCANLINE_FROM_TIMESTAMP) 7638c2ecf20Sopenharmony_ci return __intel_get_crtc_scanline_from_timestamp(crtc); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci vtotal = mode->crtc_vtotal; 7668c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_INTERLACE) 7678c2ecf20Sopenharmony_ci vtotal /= 2; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci if (IS_GEN(dev_priv, 2)) 7708c2ecf20Sopenharmony_ci position = intel_de_read_fw(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN2; 7718c2ecf20Sopenharmony_ci else 7728c2ecf20Sopenharmony_ci position = intel_de_read_fw(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN3; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci /* 7758c2ecf20Sopenharmony_ci * On HSW, the DSL reg (0x70000) appears to return 0 if we 7768c2ecf20Sopenharmony_ci * read it just before the start of vblank. So try it again 7778c2ecf20Sopenharmony_ci * so we don't accidentally end up spanning a vblank frame 7788c2ecf20Sopenharmony_ci * increment, causing the pipe_update_end() code to squak at us. 7798c2ecf20Sopenharmony_ci * 7808c2ecf20Sopenharmony_ci * The nature of this problem means we can't simply check the ISR 7818c2ecf20Sopenharmony_ci * bit and return the vblank start value; nor can we use the scanline 7828c2ecf20Sopenharmony_ci * debug register in the transcoder as it appears to have the same 7838c2ecf20Sopenharmony_ci * problem. We may need to extend this to include other platforms, 7848c2ecf20Sopenharmony_ci * but so far testing only shows the problem on HSW. 7858c2ecf20Sopenharmony_ci */ 7868c2ecf20Sopenharmony_ci if (HAS_DDI(dev_priv) && !position) { 7878c2ecf20Sopenharmony_ci int i, temp; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci for (i = 0; i < 100; i++) { 7908c2ecf20Sopenharmony_ci udelay(1); 7918c2ecf20Sopenharmony_ci temp = intel_de_read_fw(dev_priv, PIPEDSL(pipe)) & DSL_LINEMASK_GEN3; 7928c2ecf20Sopenharmony_ci if (temp != position) { 7938c2ecf20Sopenharmony_ci position = temp; 7948c2ecf20Sopenharmony_ci break; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci /* 8008c2ecf20Sopenharmony_ci * See update_scanline_offset() for the details on the 8018c2ecf20Sopenharmony_ci * scanline_offset adjustment. 8028c2ecf20Sopenharmony_ci */ 8038c2ecf20Sopenharmony_ci return (position + crtc->scanline_offset) % vtotal; 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_cistatic bool i915_get_crtc_scanoutpos(struct drm_crtc *_crtc, 8078c2ecf20Sopenharmony_ci bool in_vblank_irq, 8088c2ecf20Sopenharmony_ci int *vpos, int *hpos, 8098c2ecf20Sopenharmony_ci ktime_t *stime, ktime_t *etime, 8108c2ecf20Sopenharmony_ci const struct drm_display_mode *mode) 8118c2ecf20Sopenharmony_ci{ 8128c2ecf20Sopenharmony_ci struct drm_device *dev = _crtc->dev; 8138c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(dev); 8148c2ecf20Sopenharmony_ci struct intel_crtc *crtc = to_intel_crtc(_crtc); 8158c2ecf20Sopenharmony_ci enum pipe pipe = crtc->pipe; 8168c2ecf20Sopenharmony_ci int position; 8178c2ecf20Sopenharmony_ci int vbl_start, vbl_end, hsync_start, htotal, vtotal; 8188c2ecf20Sopenharmony_ci unsigned long irqflags; 8198c2ecf20Sopenharmony_ci bool use_scanline_counter = INTEL_GEN(dev_priv) >= 5 || 8208c2ecf20Sopenharmony_ci IS_G4X(dev_priv) || IS_GEN(dev_priv, 2) || 8218c2ecf20Sopenharmony_ci crtc->mode_flags & I915_MODE_FLAG_USE_SCANLINE_COUNTER; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci if (drm_WARN_ON(&dev_priv->drm, !mode->crtc_clock)) { 8248c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, 8258c2ecf20Sopenharmony_ci "trying to get scanoutpos for disabled " 8268c2ecf20Sopenharmony_ci "pipe %c\n", pipe_name(pipe)); 8278c2ecf20Sopenharmony_ci return false; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci htotal = mode->crtc_htotal; 8318c2ecf20Sopenharmony_ci hsync_start = mode->crtc_hsync_start; 8328c2ecf20Sopenharmony_ci vtotal = mode->crtc_vtotal; 8338c2ecf20Sopenharmony_ci vbl_start = mode->crtc_vblank_start; 8348c2ecf20Sopenharmony_ci vbl_end = mode->crtc_vblank_end; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_INTERLACE) { 8378c2ecf20Sopenharmony_ci vbl_start = DIV_ROUND_UP(vbl_start, 2); 8388c2ecf20Sopenharmony_ci vbl_end /= 2; 8398c2ecf20Sopenharmony_ci vtotal /= 2; 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci /* 8438c2ecf20Sopenharmony_ci * Lock uncore.lock, as we will do multiple timing critical raw 8448c2ecf20Sopenharmony_ci * register reads, potentially with preemption disabled, so the 8458c2ecf20Sopenharmony_ci * following code must not block on uncore.lock. 8468c2ecf20Sopenharmony_ci */ 8478c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */ 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci /* Get optional system timestamp before query. */ 8528c2ecf20Sopenharmony_ci if (stime) 8538c2ecf20Sopenharmony_ci *stime = ktime_get(); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci if (use_scanline_counter) { 8568c2ecf20Sopenharmony_ci /* No obvious pixelcount register. Only query vertical 8578c2ecf20Sopenharmony_ci * scanout position from Display scan line register. 8588c2ecf20Sopenharmony_ci */ 8598c2ecf20Sopenharmony_ci position = __intel_get_crtc_scanline(crtc); 8608c2ecf20Sopenharmony_ci } else { 8618c2ecf20Sopenharmony_ci /* Have access to pixelcount since start of frame. 8628c2ecf20Sopenharmony_ci * We can split this into vertical and horizontal 8638c2ecf20Sopenharmony_ci * scanout position. 8648c2ecf20Sopenharmony_ci */ 8658c2ecf20Sopenharmony_ci position = (intel_de_read_fw(dev_priv, PIPEFRAMEPIXEL(pipe)) & PIPE_PIXEL_MASK) >> PIPE_PIXEL_SHIFT; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci /* convert to pixel counts */ 8688c2ecf20Sopenharmony_ci vbl_start *= htotal; 8698c2ecf20Sopenharmony_ci vbl_end *= htotal; 8708c2ecf20Sopenharmony_ci vtotal *= htotal; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci /* 8738c2ecf20Sopenharmony_ci * In interlaced modes, the pixel counter counts all pixels, 8748c2ecf20Sopenharmony_ci * so one field will have htotal more pixels. In order to avoid 8758c2ecf20Sopenharmony_ci * the reported position from jumping backwards when the pixel 8768c2ecf20Sopenharmony_ci * counter is beyond the length of the shorter field, just 8778c2ecf20Sopenharmony_ci * clamp the position the length of the shorter field. This 8788c2ecf20Sopenharmony_ci * matches how the scanline counter based position works since 8798c2ecf20Sopenharmony_ci * the scanline counter doesn't count the two half lines. 8808c2ecf20Sopenharmony_ci */ 8818c2ecf20Sopenharmony_ci if (position >= vtotal) 8828c2ecf20Sopenharmony_ci position = vtotal - 1; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci /* 8858c2ecf20Sopenharmony_ci * Start of vblank interrupt is triggered at start of hsync, 8868c2ecf20Sopenharmony_ci * just prior to the first active line of vblank. However we 8878c2ecf20Sopenharmony_ci * consider lines to start at the leading edge of horizontal 8888c2ecf20Sopenharmony_ci * active. So, should we get here before we've crossed into 8898c2ecf20Sopenharmony_ci * the horizontal active of the first line in vblank, we would 8908c2ecf20Sopenharmony_ci * not set the DRM_SCANOUTPOS_INVBL flag. In order to fix that, 8918c2ecf20Sopenharmony_ci * always add htotal-hsync_start to the current pixel position. 8928c2ecf20Sopenharmony_ci */ 8938c2ecf20Sopenharmony_ci position = (position + htotal - hsync_start) % vtotal; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci /* Get optional system timestamp after query. */ 8978c2ecf20Sopenharmony_ci if (etime) 8988c2ecf20Sopenharmony_ci *etime = ktime_get(); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci /* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */ 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci /* 9058c2ecf20Sopenharmony_ci * While in vblank, position will be negative 9068c2ecf20Sopenharmony_ci * counting up towards 0 at vbl_end. And outside 9078c2ecf20Sopenharmony_ci * vblank, position will be positive counting 9088c2ecf20Sopenharmony_ci * up since vbl_end. 9098c2ecf20Sopenharmony_ci */ 9108c2ecf20Sopenharmony_ci if (position >= vbl_start) 9118c2ecf20Sopenharmony_ci position -= vbl_end; 9128c2ecf20Sopenharmony_ci else 9138c2ecf20Sopenharmony_ci position += vtotal - vbl_end; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci if (use_scanline_counter) { 9168c2ecf20Sopenharmony_ci *vpos = position; 9178c2ecf20Sopenharmony_ci *hpos = 0; 9188c2ecf20Sopenharmony_ci } else { 9198c2ecf20Sopenharmony_ci *vpos = position / htotal; 9208c2ecf20Sopenharmony_ci *hpos = position - (*vpos * htotal); 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci return true; 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_cibool intel_crtc_get_vblank_timestamp(struct drm_crtc *crtc, int *max_error, 9278c2ecf20Sopenharmony_ci ktime_t *vblank_time, bool in_vblank_irq) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci return drm_crtc_vblank_helper_get_vblank_timestamp_internal( 9308c2ecf20Sopenharmony_ci crtc, max_error, vblank_time, in_vblank_irq, 9318c2ecf20Sopenharmony_ci i915_get_crtc_scanoutpos); 9328c2ecf20Sopenharmony_ci} 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ciint intel_get_crtc_scanline(struct intel_crtc *crtc) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 9378c2ecf20Sopenharmony_ci unsigned long irqflags; 9388c2ecf20Sopenharmony_ci int position; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->uncore.lock, irqflags); 9418c2ecf20Sopenharmony_ci position = __intel_get_crtc_scanline(crtc); 9428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci return position; 9458c2ecf20Sopenharmony_ci} 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci/** 9488c2ecf20Sopenharmony_ci * ivb_parity_work - Workqueue called when a parity error interrupt 9498c2ecf20Sopenharmony_ci * occurred. 9508c2ecf20Sopenharmony_ci * @work: workqueue struct 9518c2ecf20Sopenharmony_ci * 9528c2ecf20Sopenharmony_ci * Doesn't actually do anything except notify userspace. As a consequence of 9538c2ecf20Sopenharmony_ci * this event, userspace should try to remap the bad rows since statistically 9548c2ecf20Sopenharmony_ci * it is likely the same row is more likely to go bad again. 9558c2ecf20Sopenharmony_ci */ 9568c2ecf20Sopenharmony_cistatic void ivb_parity_work(struct work_struct *work) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = 9598c2ecf20Sopenharmony_ci container_of(work, typeof(*dev_priv), l3_parity.error_work); 9608c2ecf20Sopenharmony_ci struct intel_gt *gt = &dev_priv->gt; 9618c2ecf20Sopenharmony_ci u32 error_status, row, bank, subbank; 9628c2ecf20Sopenharmony_ci char *parity_event[6]; 9638c2ecf20Sopenharmony_ci u32 misccpctl; 9648c2ecf20Sopenharmony_ci u8 slice = 0; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci /* We must turn off DOP level clock gating to access the L3 registers. 9678c2ecf20Sopenharmony_ci * In order to prevent a get/put style interface, acquire struct mutex 9688c2ecf20Sopenharmony_ci * any time we access those registers. 9698c2ecf20Sopenharmony_ci */ 9708c2ecf20Sopenharmony_ci mutex_lock(&dev_priv->drm.struct_mutex); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci /* If we've screwed up tracking, just let the interrupt fire again */ 9738c2ecf20Sopenharmony_ci if (drm_WARN_ON(&dev_priv->drm, !dev_priv->l3_parity.which_slice)) 9748c2ecf20Sopenharmony_ci goto out; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci misccpctl = I915_READ(GEN7_MISCCPCTL); 9778c2ecf20Sopenharmony_ci I915_WRITE(GEN7_MISCCPCTL, misccpctl & ~GEN7_DOP_CLOCK_GATE_ENABLE); 9788c2ecf20Sopenharmony_ci POSTING_READ(GEN7_MISCCPCTL); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci while ((slice = ffs(dev_priv->l3_parity.which_slice)) != 0) { 9818c2ecf20Sopenharmony_ci i915_reg_t reg; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci slice--; 9848c2ecf20Sopenharmony_ci if (drm_WARN_ON_ONCE(&dev_priv->drm, 9858c2ecf20Sopenharmony_ci slice >= NUM_L3_SLICES(dev_priv))) 9868c2ecf20Sopenharmony_ci break; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci dev_priv->l3_parity.which_slice &= ~(1<<slice); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci reg = GEN7_L3CDERRST1(slice); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci error_status = I915_READ(reg); 9938c2ecf20Sopenharmony_ci row = GEN7_PARITY_ERROR_ROW(error_status); 9948c2ecf20Sopenharmony_ci bank = GEN7_PARITY_ERROR_BANK(error_status); 9958c2ecf20Sopenharmony_ci subbank = GEN7_PARITY_ERROR_SUBBANK(error_status); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci I915_WRITE(reg, GEN7_PARITY_ERROR_VALID | GEN7_L3CDERRST1_ENABLE); 9988c2ecf20Sopenharmony_ci POSTING_READ(reg); 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci parity_event[0] = I915_L3_PARITY_UEVENT "=1"; 10018c2ecf20Sopenharmony_ci parity_event[1] = kasprintf(GFP_KERNEL, "ROW=%d", row); 10028c2ecf20Sopenharmony_ci parity_event[2] = kasprintf(GFP_KERNEL, "BANK=%d", bank); 10038c2ecf20Sopenharmony_ci parity_event[3] = kasprintf(GFP_KERNEL, "SUBBANK=%d", subbank); 10048c2ecf20Sopenharmony_ci parity_event[4] = kasprintf(GFP_KERNEL, "SLICE=%d", slice); 10058c2ecf20Sopenharmony_ci parity_event[5] = NULL; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci kobject_uevent_env(&dev_priv->drm.primary->kdev->kobj, 10088c2ecf20Sopenharmony_ci KOBJ_CHANGE, parity_event); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci DRM_DEBUG("Parity error: Slice = %d, Row = %d, Bank = %d, Sub bank = %d.\n", 10118c2ecf20Sopenharmony_ci slice, row, bank, subbank); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci kfree(parity_event[4]); 10148c2ecf20Sopenharmony_ci kfree(parity_event[3]); 10158c2ecf20Sopenharmony_ci kfree(parity_event[2]); 10168c2ecf20Sopenharmony_ci kfree(parity_event[1]); 10178c2ecf20Sopenharmony_ci } 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci I915_WRITE(GEN7_MISCCPCTL, misccpctl); 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ciout: 10228c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, dev_priv->l3_parity.which_slice); 10238c2ecf20Sopenharmony_ci spin_lock_irq(>->irq_lock); 10248c2ecf20Sopenharmony_ci gen5_gt_enable_irq(gt, GT_PARITY_ERROR(dev_priv)); 10258c2ecf20Sopenharmony_ci spin_unlock_irq(>->irq_lock); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci mutex_unlock(&dev_priv->drm.struct_mutex); 10288c2ecf20Sopenharmony_ci} 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_cistatic bool gen11_port_hotplug_long_detect(enum hpd_pin pin, u32 val) 10318c2ecf20Sopenharmony_ci{ 10328c2ecf20Sopenharmony_ci switch (pin) { 10338c2ecf20Sopenharmony_ci case HPD_PORT_TC1: 10348c2ecf20Sopenharmony_ci return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC1); 10358c2ecf20Sopenharmony_ci case HPD_PORT_TC2: 10368c2ecf20Sopenharmony_ci return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC2); 10378c2ecf20Sopenharmony_ci case HPD_PORT_TC3: 10388c2ecf20Sopenharmony_ci return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC3); 10398c2ecf20Sopenharmony_ci case HPD_PORT_TC4: 10408c2ecf20Sopenharmony_ci return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC4); 10418c2ecf20Sopenharmony_ci case HPD_PORT_TC5: 10428c2ecf20Sopenharmony_ci return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC5); 10438c2ecf20Sopenharmony_ci case HPD_PORT_TC6: 10448c2ecf20Sopenharmony_ci return val & GEN11_HOTPLUG_CTL_LONG_DETECT(PORT_TC6); 10458c2ecf20Sopenharmony_ci default: 10468c2ecf20Sopenharmony_ci return false; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci} 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_cistatic bool bxt_port_hotplug_long_detect(enum hpd_pin pin, u32 val) 10518c2ecf20Sopenharmony_ci{ 10528c2ecf20Sopenharmony_ci switch (pin) { 10538c2ecf20Sopenharmony_ci case HPD_PORT_A: 10548c2ecf20Sopenharmony_ci return val & PORTA_HOTPLUG_LONG_DETECT; 10558c2ecf20Sopenharmony_ci case HPD_PORT_B: 10568c2ecf20Sopenharmony_ci return val & PORTB_HOTPLUG_LONG_DETECT; 10578c2ecf20Sopenharmony_ci case HPD_PORT_C: 10588c2ecf20Sopenharmony_ci return val & PORTC_HOTPLUG_LONG_DETECT; 10598c2ecf20Sopenharmony_ci default: 10608c2ecf20Sopenharmony_ci return false; 10618c2ecf20Sopenharmony_ci } 10628c2ecf20Sopenharmony_ci} 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_cistatic bool icp_ddi_port_hotplug_long_detect(enum hpd_pin pin, u32 val) 10658c2ecf20Sopenharmony_ci{ 10668c2ecf20Sopenharmony_ci switch (pin) { 10678c2ecf20Sopenharmony_ci case HPD_PORT_A: 10688c2ecf20Sopenharmony_ci return val & SHOTPLUG_CTL_DDI_HPD_LONG_DETECT(PORT_A); 10698c2ecf20Sopenharmony_ci case HPD_PORT_B: 10708c2ecf20Sopenharmony_ci return val & SHOTPLUG_CTL_DDI_HPD_LONG_DETECT(PORT_B); 10718c2ecf20Sopenharmony_ci case HPD_PORT_C: 10728c2ecf20Sopenharmony_ci return val & SHOTPLUG_CTL_DDI_HPD_LONG_DETECT(PORT_C); 10738c2ecf20Sopenharmony_ci default: 10748c2ecf20Sopenharmony_ci return false; 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci} 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_cistatic bool icp_tc_port_hotplug_long_detect(enum hpd_pin pin, u32 val) 10798c2ecf20Sopenharmony_ci{ 10808c2ecf20Sopenharmony_ci switch (pin) { 10818c2ecf20Sopenharmony_ci case HPD_PORT_TC1: 10828c2ecf20Sopenharmony_ci return val & ICP_TC_HPD_LONG_DETECT(PORT_TC1); 10838c2ecf20Sopenharmony_ci case HPD_PORT_TC2: 10848c2ecf20Sopenharmony_ci return val & ICP_TC_HPD_LONG_DETECT(PORT_TC2); 10858c2ecf20Sopenharmony_ci case HPD_PORT_TC3: 10868c2ecf20Sopenharmony_ci return val & ICP_TC_HPD_LONG_DETECT(PORT_TC3); 10878c2ecf20Sopenharmony_ci case HPD_PORT_TC4: 10888c2ecf20Sopenharmony_ci return val & ICP_TC_HPD_LONG_DETECT(PORT_TC4); 10898c2ecf20Sopenharmony_ci case HPD_PORT_TC5: 10908c2ecf20Sopenharmony_ci return val & ICP_TC_HPD_LONG_DETECT(PORT_TC5); 10918c2ecf20Sopenharmony_ci case HPD_PORT_TC6: 10928c2ecf20Sopenharmony_ci return val & ICP_TC_HPD_LONG_DETECT(PORT_TC6); 10938c2ecf20Sopenharmony_ci default: 10948c2ecf20Sopenharmony_ci return false; 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci} 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_cistatic bool spt_port_hotplug2_long_detect(enum hpd_pin pin, u32 val) 10998c2ecf20Sopenharmony_ci{ 11008c2ecf20Sopenharmony_ci switch (pin) { 11018c2ecf20Sopenharmony_ci case HPD_PORT_E: 11028c2ecf20Sopenharmony_ci return val & PORTE_HOTPLUG_LONG_DETECT; 11038c2ecf20Sopenharmony_ci default: 11048c2ecf20Sopenharmony_ci return false; 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci} 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_cistatic bool spt_port_hotplug_long_detect(enum hpd_pin pin, u32 val) 11098c2ecf20Sopenharmony_ci{ 11108c2ecf20Sopenharmony_ci switch (pin) { 11118c2ecf20Sopenharmony_ci case HPD_PORT_A: 11128c2ecf20Sopenharmony_ci return val & PORTA_HOTPLUG_LONG_DETECT; 11138c2ecf20Sopenharmony_ci case HPD_PORT_B: 11148c2ecf20Sopenharmony_ci return val & PORTB_HOTPLUG_LONG_DETECT; 11158c2ecf20Sopenharmony_ci case HPD_PORT_C: 11168c2ecf20Sopenharmony_ci return val & PORTC_HOTPLUG_LONG_DETECT; 11178c2ecf20Sopenharmony_ci case HPD_PORT_D: 11188c2ecf20Sopenharmony_ci return val & PORTD_HOTPLUG_LONG_DETECT; 11198c2ecf20Sopenharmony_ci default: 11208c2ecf20Sopenharmony_ci return false; 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci} 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_cistatic bool ilk_port_hotplug_long_detect(enum hpd_pin pin, u32 val) 11258c2ecf20Sopenharmony_ci{ 11268c2ecf20Sopenharmony_ci switch (pin) { 11278c2ecf20Sopenharmony_ci case HPD_PORT_A: 11288c2ecf20Sopenharmony_ci return val & DIGITAL_PORTA_HOTPLUG_LONG_DETECT; 11298c2ecf20Sopenharmony_ci default: 11308c2ecf20Sopenharmony_ci return false; 11318c2ecf20Sopenharmony_ci } 11328c2ecf20Sopenharmony_ci} 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_cistatic bool pch_port_hotplug_long_detect(enum hpd_pin pin, u32 val) 11358c2ecf20Sopenharmony_ci{ 11368c2ecf20Sopenharmony_ci switch (pin) { 11378c2ecf20Sopenharmony_ci case HPD_PORT_B: 11388c2ecf20Sopenharmony_ci return val & PORTB_HOTPLUG_LONG_DETECT; 11398c2ecf20Sopenharmony_ci case HPD_PORT_C: 11408c2ecf20Sopenharmony_ci return val & PORTC_HOTPLUG_LONG_DETECT; 11418c2ecf20Sopenharmony_ci case HPD_PORT_D: 11428c2ecf20Sopenharmony_ci return val & PORTD_HOTPLUG_LONG_DETECT; 11438c2ecf20Sopenharmony_ci default: 11448c2ecf20Sopenharmony_ci return false; 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci} 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_cistatic bool i9xx_port_hotplug_long_detect(enum hpd_pin pin, u32 val) 11498c2ecf20Sopenharmony_ci{ 11508c2ecf20Sopenharmony_ci switch (pin) { 11518c2ecf20Sopenharmony_ci case HPD_PORT_B: 11528c2ecf20Sopenharmony_ci return val & PORTB_HOTPLUG_INT_LONG_PULSE; 11538c2ecf20Sopenharmony_ci case HPD_PORT_C: 11548c2ecf20Sopenharmony_ci return val & PORTC_HOTPLUG_INT_LONG_PULSE; 11558c2ecf20Sopenharmony_ci case HPD_PORT_D: 11568c2ecf20Sopenharmony_ci return val & PORTD_HOTPLUG_INT_LONG_PULSE; 11578c2ecf20Sopenharmony_ci default: 11588c2ecf20Sopenharmony_ci return false; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci} 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci/* 11638c2ecf20Sopenharmony_ci * Get a bit mask of pins that have triggered, and which ones may be long. 11648c2ecf20Sopenharmony_ci * This can be called multiple times with the same masks to accumulate 11658c2ecf20Sopenharmony_ci * hotplug detection results from several registers. 11668c2ecf20Sopenharmony_ci * 11678c2ecf20Sopenharmony_ci * Note that the caller is expected to zero out the masks initially. 11688c2ecf20Sopenharmony_ci */ 11698c2ecf20Sopenharmony_cistatic void intel_get_hpd_pins(struct drm_i915_private *dev_priv, 11708c2ecf20Sopenharmony_ci u32 *pin_mask, u32 *long_mask, 11718c2ecf20Sopenharmony_ci u32 hotplug_trigger, u32 dig_hotplug_reg, 11728c2ecf20Sopenharmony_ci const u32 hpd[HPD_NUM_PINS], 11738c2ecf20Sopenharmony_ci bool long_pulse_detect(enum hpd_pin pin, u32 val)) 11748c2ecf20Sopenharmony_ci{ 11758c2ecf20Sopenharmony_ci enum hpd_pin pin; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci BUILD_BUG_ON(BITS_PER_TYPE(*pin_mask) < HPD_NUM_PINS); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci for_each_hpd_pin(pin) { 11808c2ecf20Sopenharmony_ci if ((hpd[pin] & hotplug_trigger) == 0) 11818c2ecf20Sopenharmony_ci continue; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci *pin_mask |= BIT(pin); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci if (long_pulse_detect(pin, dig_hotplug_reg)) 11868c2ecf20Sopenharmony_ci *long_mask |= BIT(pin); 11878c2ecf20Sopenharmony_ci } 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, 11908c2ecf20Sopenharmony_ci "hotplug event received, stat 0x%08x, dig 0x%08x, pins 0x%08x, long 0x%08x\n", 11918c2ecf20Sopenharmony_ci hotplug_trigger, dig_hotplug_reg, *pin_mask, *long_mask); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci} 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_cistatic void gmbus_irq_handler(struct drm_i915_private *dev_priv) 11968c2ecf20Sopenharmony_ci{ 11978c2ecf20Sopenharmony_ci wake_up_all(&dev_priv->gmbus_wait_queue); 11988c2ecf20Sopenharmony_ci} 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_cistatic void dp_aux_irq_handler(struct drm_i915_private *dev_priv) 12018c2ecf20Sopenharmony_ci{ 12028c2ecf20Sopenharmony_ci wake_up_all(&dev_priv->gmbus_wait_queue); 12038c2ecf20Sopenharmony_ci} 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci#if defined(CONFIG_DEBUG_FS) 12068c2ecf20Sopenharmony_cistatic void display_pipe_crc_irq_handler(struct drm_i915_private *dev_priv, 12078c2ecf20Sopenharmony_ci enum pipe pipe, 12088c2ecf20Sopenharmony_ci u32 crc0, u32 crc1, 12098c2ecf20Sopenharmony_ci u32 crc2, u32 crc3, 12108c2ecf20Sopenharmony_ci u32 crc4) 12118c2ecf20Sopenharmony_ci{ 12128c2ecf20Sopenharmony_ci struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv, pipe); 12138c2ecf20Sopenharmony_ci struct intel_pipe_crc *pipe_crc = &crtc->pipe_crc; 12148c2ecf20Sopenharmony_ci u32 crcs[5] = { crc0, crc1, crc2, crc3, crc4 }; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci trace_intel_pipe_crc(crtc, crcs); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci spin_lock(&pipe_crc->lock); 12198c2ecf20Sopenharmony_ci /* 12208c2ecf20Sopenharmony_ci * For some not yet identified reason, the first CRC is 12218c2ecf20Sopenharmony_ci * bonkers. So let's just wait for the next vblank and read 12228c2ecf20Sopenharmony_ci * out the buggy result. 12238c2ecf20Sopenharmony_ci * 12248c2ecf20Sopenharmony_ci * On GEN8+ sometimes the second CRC is bonkers as well, so 12258c2ecf20Sopenharmony_ci * don't trust that one either. 12268c2ecf20Sopenharmony_ci */ 12278c2ecf20Sopenharmony_ci if (pipe_crc->skipped <= 0 || 12288c2ecf20Sopenharmony_ci (INTEL_GEN(dev_priv) >= 8 && pipe_crc->skipped == 1)) { 12298c2ecf20Sopenharmony_ci pipe_crc->skipped++; 12308c2ecf20Sopenharmony_ci spin_unlock(&pipe_crc->lock); 12318c2ecf20Sopenharmony_ci return; 12328c2ecf20Sopenharmony_ci } 12338c2ecf20Sopenharmony_ci spin_unlock(&pipe_crc->lock); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci drm_crtc_add_crc_entry(&crtc->base, true, 12368c2ecf20Sopenharmony_ci drm_crtc_accurate_vblank_count(&crtc->base), 12378c2ecf20Sopenharmony_ci crcs); 12388c2ecf20Sopenharmony_ci} 12398c2ecf20Sopenharmony_ci#else 12408c2ecf20Sopenharmony_cistatic inline void 12418c2ecf20Sopenharmony_cidisplay_pipe_crc_irq_handler(struct drm_i915_private *dev_priv, 12428c2ecf20Sopenharmony_ci enum pipe pipe, 12438c2ecf20Sopenharmony_ci u32 crc0, u32 crc1, 12448c2ecf20Sopenharmony_ci u32 crc2, u32 crc3, 12458c2ecf20Sopenharmony_ci u32 crc4) {} 12468c2ecf20Sopenharmony_ci#endif 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_cistatic void hsw_pipe_crc_irq_handler(struct drm_i915_private *dev_priv, 12508c2ecf20Sopenharmony_ci enum pipe pipe) 12518c2ecf20Sopenharmony_ci{ 12528c2ecf20Sopenharmony_ci display_pipe_crc_irq_handler(dev_priv, pipe, 12538c2ecf20Sopenharmony_ci I915_READ(PIPE_CRC_RES_1_IVB(pipe)), 12548c2ecf20Sopenharmony_ci 0, 0, 0, 0); 12558c2ecf20Sopenharmony_ci} 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_cistatic void ivb_pipe_crc_irq_handler(struct drm_i915_private *dev_priv, 12588c2ecf20Sopenharmony_ci enum pipe pipe) 12598c2ecf20Sopenharmony_ci{ 12608c2ecf20Sopenharmony_ci display_pipe_crc_irq_handler(dev_priv, pipe, 12618c2ecf20Sopenharmony_ci I915_READ(PIPE_CRC_RES_1_IVB(pipe)), 12628c2ecf20Sopenharmony_ci I915_READ(PIPE_CRC_RES_2_IVB(pipe)), 12638c2ecf20Sopenharmony_ci I915_READ(PIPE_CRC_RES_3_IVB(pipe)), 12648c2ecf20Sopenharmony_ci I915_READ(PIPE_CRC_RES_4_IVB(pipe)), 12658c2ecf20Sopenharmony_ci I915_READ(PIPE_CRC_RES_5_IVB(pipe))); 12668c2ecf20Sopenharmony_ci} 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_cistatic void i9xx_pipe_crc_irq_handler(struct drm_i915_private *dev_priv, 12698c2ecf20Sopenharmony_ci enum pipe pipe) 12708c2ecf20Sopenharmony_ci{ 12718c2ecf20Sopenharmony_ci u32 res1, res2; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 3) 12748c2ecf20Sopenharmony_ci res1 = I915_READ(PIPE_CRC_RES_RES1_I915(pipe)); 12758c2ecf20Sopenharmony_ci else 12768c2ecf20Sopenharmony_ci res1 = 0; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv)) 12798c2ecf20Sopenharmony_ci res2 = I915_READ(PIPE_CRC_RES_RES2_G4X(pipe)); 12808c2ecf20Sopenharmony_ci else 12818c2ecf20Sopenharmony_ci res2 = 0; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci display_pipe_crc_irq_handler(dev_priv, pipe, 12848c2ecf20Sopenharmony_ci I915_READ(PIPE_CRC_RES_RED(pipe)), 12858c2ecf20Sopenharmony_ci I915_READ(PIPE_CRC_RES_GREEN(pipe)), 12868c2ecf20Sopenharmony_ci I915_READ(PIPE_CRC_RES_BLUE(pipe)), 12878c2ecf20Sopenharmony_ci res1, res2); 12888c2ecf20Sopenharmony_ci} 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_cistatic void i9xx_pipestat_irq_reset(struct drm_i915_private *dev_priv) 12918c2ecf20Sopenharmony_ci{ 12928c2ecf20Sopenharmony_ci enum pipe pipe; 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) { 12958c2ecf20Sopenharmony_ci I915_WRITE(PIPESTAT(pipe), 12968c2ecf20Sopenharmony_ci PIPESTAT_INT_STATUS_MASK | 12978c2ecf20Sopenharmony_ci PIPE_FIFO_UNDERRUN_STATUS); 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci dev_priv->pipestat_irq_mask[pipe] = 0; 13008c2ecf20Sopenharmony_ci } 13018c2ecf20Sopenharmony_ci} 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_cistatic void i9xx_pipestat_irq_ack(struct drm_i915_private *dev_priv, 13048c2ecf20Sopenharmony_ci u32 iir, u32 pipe_stats[I915_MAX_PIPES]) 13058c2ecf20Sopenharmony_ci{ 13068c2ecf20Sopenharmony_ci enum pipe pipe; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci spin_lock(&dev_priv->irq_lock); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci if (!dev_priv->display_irqs_enabled) { 13118c2ecf20Sopenharmony_ci spin_unlock(&dev_priv->irq_lock); 13128c2ecf20Sopenharmony_ci return; 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) { 13168c2ecf20Sopenharmony_ci i915_reg_t reg; 13178c2ecf20Sopenharmony_ci u32 status_mask, enable_mask, iir_bit = 0; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci /* 13208c2ecf20Sopenharmony_ci * PIPESTAT bits get signalled even when the interrupt is 13218c2ecf20Sopenharmony_ci * disabled with the mask bits, and some of the status bits do 13228c2ecf20Sopenharmony_ci * not generate interrupts at all (like the underrun bit). Hence 13238c2ecf20Sopenharmony_ci * we need to be careful that we only handle what we want to 13248c2ecf20Sopenharmony_ci * handle. 13258c2ecf20Sopenharmony_ci */ 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci /* fifo underruns are filterered in the underrun handler. */ 13288c2ecf20Sopenharmony_ci status_mask = PIPE_FIFO_UNDERRUN_STATUS; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci switch (pipe) { 13318c2ecf20Sopenharmony_ci default: 13328c2ecf20Sopenharmony_ci case PIPE_A: 13338c2ecf20Sopenharmony_ci iir_bit = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; 13348c2ecf20Sopenharmony_ci break; 13358c2ecf20Sopenharmony_ci case PIPE_B: 13368c2ecf20Sopenharmony_ci iir_bit = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; 13378c2ecf20Sopenharmony_ci break; 13388c2ecf20Sopenharmony_ci case PIPE_C: 13398c2ecf20Sopenharmony_ci iir_bit = I915_DISPLAY_PIPE_C_EVENT_INTERRUPT; 13408c2ecf20Sopenharmony_ci break; 13418c2ecf20Sopenharmony_ci } 13428c2ecf20Sopenharmony_ci if (iir & iir_bit) 13438c2ecf20Sopenharmony_ci status_mask |= dev_priv->pipestat_irq_mask[pipe]; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci if (!status_mask) 13468c2ecf20Sopenharmony_ci continue; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci reg = PIPESTAT(pipe); 13498c2ecf20Sopenharmony_ci pipe_stats[pipe] = I915_READ(reg) & status_mask; 13508c2ecf20Sopenharmony_ci enable_mask = i915_pipestat_enable_mask(dev_priv, pipe); 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci /* 13538c2ecf20Sopenharmony_ci * Clear the PIPE*STAT regs before the IIR 13548c2ecf20Sopenharmony_ci * 13558c2ecf20Sopenharmony_ci * Toggle the enable bits to make sure we get an 13568c2ecf20Sopenharmony_ci * edge in the ISR pipe event bit if we don't clear 13578c2ecf20Sopenharmony_ci * all the enabled status bits. Otherwise the edge 13588c2ecf20Sopenharmony_ci * triggered IIR on i965/g4x wouldn't notice that 13598c2ecf20Sopenharmony_ci * an interrupt is still pending. 13608c2ecf20Sopenharmony_ci */ 13618c2ecf20Sopenharmony_ci if (pipe_stats[pipe]) { 13628c2ecf20Sopenharmony_ci I915_WRITE(reg, pipe_stats[pipe]); 13638c2ecf20Sopenharmony_ci I915_WRITE(reg, enable_mask); 13648c2ecf20Sopenharmony_ci } 13658c2ecf20Sopenharmony_ci } 13668c2ecf20Sopenharmony_ci spin_unlock(&dev_priv->irq_lock); 13678c2ecf20Sopenharmony_ci} 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_cistatic void i8xx_pipestat_irq_handler(struct drm_i915_private *dev_priv, 13708c2ecf20Sopenharmony_ci u16 iir, u32 pipe_stats[I915_MAX_PIPES]) 13718c2ecf20Sopenharmony_ci{ 13728c2ecf20Sopenharmony_ci enum pipe pipe; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) { 13758c2ecf20Sopenharmony_ci if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS) 13768c2ecf20Sopenharmony_ci intel_handle_vblank(dev_priv, pipe); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS) 13798c2ecf20Sopenharmony_ci i9xx_pipe_crc_irq_handler(dev_priv, pipe); 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) 13828c2ecf20Sopenharmony_ci intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe); 13838c2ecf20Sopenharmony_ci } 13848c2ecf20Sopenharmony_ci} 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_cistatic void i915_pipestat_irq_handler(struct drm_i915_private *dev_priv, 13878c2ecf20Sopenharmony_ci u32 iir, u32 pipe_stats[I915_MAX_PIPES]) 13888c2ecf20Sopenharmony_ci{ 13898c2ecf20Sopenharmony_ci bool blc_event = false; 13908c2ecf20Sopenharmony_ci enum pipe pipe; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) { 13938c2ecf20Sopenharmony_ci if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS) 13948c2ecf20Sopenharmony_ci intel_handle_vblank(dev_priv, pipe); 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) 13978c2ecf20Sopenharmony_ci blc_event = true; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS) 14008c2ecf20Sopenharmony_ci i9xx_pipe_crc_irq_handler(dev_priv, pipe); 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) 14038c2ecf20Sopenharmony_ci intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe); 14048c2ecf20Sopenharmony_ci } 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci if (blc_event || (iir & I915_ASLE_INTERRUPT)) 14078c2ecf20Sopenharmony_ci intel_opregion_asle_intr(dev_priv); 14088c2ecf20Sopenharmony_ci} 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_cistatic void i965_pipestat_irq_handler(struct drm_i915_private *dev_priv, 14118c2ecf20Sopenharmony_ci u32 iir, u32 pipe_stats[I915_MAX_PIPES]) 14128c2ecf20Sopenharmony_ci{ 14138c2ecf20Sopenharmony_ci bool blc_event = false; 14148c2ecf20Sopenharmony_ci enum pipe pipe; 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) { 14178c2ecf20Sopenharmony_ci if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS) 14188c2ecf20Sopenharmony_ci intel_handle_vblank(dev_priv, pipe); 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS) 14218c2ecf20Sopenharmony_ci blc_event = true; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS) 14248c2ecf20Sopenharmony_ci i9xx_pipe_crc_irq_handler(dev_priv, pipe); 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) 14278c2ecf20Sopenharmony_ci intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe); 14288c2ecf20Sopenharmony_ci } 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci if (blc_event || (iir & I915_ASLE_INTERRUPT)) 14318c2ecf20Sopenharmony_ci intel_opregion_asle_intr(dev_priv); 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS) 14348c2ecf20Sopenharmony_ci gmbus_irq_handler(dev_priv); 14358c2ecf20Sopenharmony_ci} 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_cistatic void valleyview_pipestat_irq_handler(struct drm_i915_private *dev_priv, 14388c2ecf20Sopenharmony_ci u32 pipe_stats[I915_MAX_PIPES]) 14398c2ecf20Sopenharmony_ci{ 14408c2ecf20Sopenharmony_ci enum pipe pipe; 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) { 14438c2ecf20Sopenharmony_ci if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS) 14448c2ecf20Sopenharmony_ci intel_handle_vblank(dev_priv, pipe); 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS) 14478c2ecf20Sopenharmony_ci i9xx_pipe_crc_irq_handler(dev_priv, pipe); 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS) 14508c2ecf20Sopenharmony_ci intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe); 14518c2ecf20Sopenharmony_ci } 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS) 14548c2ecf20Sopenharmony_ci gmbus_irq_handler(dev_priv); 14558c2ecf20Sopenharmony_ci} 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_cistatic u32 i9xx_hpd_irq_ack(struct drm_i915_private *dev_priv) 14588c2ecf20Sopenharmony_ci{ 14598c2ecf20Sopenharmony_ci u32 hotplug_status = 0, hotplug_status_mask; 14608c2ecf20Sopenharmony_ci int i; 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci if (IS_G4X(dev_priv) || 14638c2ecf20Sopenharmony_ci IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) 14648c2ecf20Sopenharmony_ci hotplug_status_mask = HOTPLUG_INT_STATUS_G4X | 14658c2ecf20Sopenharmony_ci DP_AUX_CHANNEL_MASK_INT_STATUS_G4X; 14668c2ecf20Sopenharmony_ci else 14678c2ecf20Sopenharmony_ci hotplug_status_mask = HOTPLUG_INT_STATUS_I915; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci /* 14708c2ecf20Sopenharmony_ci * We absolutely have to clear all the pending interrupt 14718c2ecf20Sopenharmony_ci * bits in PORT_HOTPLUG_STAT. Otherwise the ISR port 14728c2ecf20Sopenharmony_ci * interrupt bit won't have an edge, and the i965/g4x 14738c2ecf20Sopenharmony_ci * edge triggered IIR will not notice that an interrupt 14748c2ecf20Sopenharmony_ci * is still pending. We can't use PORT_HOTPLUG_EN to 14758c2ecf20Sopenharmony_ci * guarantee the edge as the act of toggling the enable 14768c2ecf20Sopenharmony_ci * bits can itself generate a new hotplug interrupt :( 14778c2ecf20Sopenharmony_ci */ 14788c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 14798c2ecf20Sopenharmony_ci u32 tmp = I915_READ(PORT_HOTPLUG_STAT) & hotplug_status_mask; 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci if (tmp == 0) 14828c2ecf20Sopenharmony_ci return hotplug_status; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci hotplug_status |= tmp; 14858c2ecf20Sopenharmony_ci I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); 14868c2ecf20Sopenharmony_ci } 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci drm_WARN_ONCE(&dev_priv->drm, 1, 14898c2ecf20Sopenharmony_ci "PORT_HOTPLUG_STAT did not clear (0x%08x)\n", 14908c2ecf20Sopenharmony_ci I915_READ(PORT_HOTPLUG_STAT)); 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci return hotplug_status; 14938c2ecf20Sopenharmony_ci} 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_cistatic void i9xx_hpd_irq_handler(struct drm_i915_private *dev_priv, 14968c2ecf20Sopenharmony_ci u32 hotplug_status) 14978c2ecf20Sopenharmony_ci{ 14988c2ecf20Sopenharmony_ci u32 pin_mask = 0, long_mask = 0; 14998c2ecf20Sopenharmony_ci u32 hotplug_trigger; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci if (IS_G4X(dev_priv) || 15028c2ecf20Sopenharmony_ci IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) 15038c2ecf20Sopenharmony_ci hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_G4X; 15048c2ecf20Sopenharmony_ci else 15058c2ecf20Sopenharmony_ci hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_I915; 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci if (hotplug_trigger) { 15088c2ecf20Sopenharmony_ci intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, 15098c2ecf20Sopenharmony_ci hotplug_trigger, hotplug_trigger, 15108c2ecf20Sopenharmony_ci dev_priv->hotplug.hpd, 15118c2ecf20Sopenharmony_ci i9xx_port_hotplug_long_detect); 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci intel_hpd_irq_handler(dev_priv, pin_mask, long_mask); 15148c2ecf20Sopenharmony_ci } 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci if ((IS_G4X(dev_priv) || 15178c2ecf20Sopenharmony_ci IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) && 15188c2ecf20Sopenharmony_ci hotplug_status & DP_AUX_CHANNEL_MASK_INT_STATUS_G4X) 15198c2ecf20Sopenharmony_ci dp_aux_irq_handler(dev_priv); 15208c2ecf20Sopenharmony_ci} 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_cistatic irqreturn_t valleyview_irq_handler(int irq, void *arg) 15238c2ecf20Sopenharmony_ci{ 15248c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = arg; 15258c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci if (!intel_irqs_enabled(dev_priv)) 15288c2ecf20Sopenharmony_ci return IRQ_NONE; 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci /* IRQs are synced during runtime_suspend, we don't require a wakeref */ 15318c2ecf20Sopenharmony_ci disable_rpm_wakeref_asserts(&dev_priv->runtime_pm); 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci do { 15348c2ecf20Sopenharmony_ci u32 iir, gt_iir, pm_iir; 15358c2ecf20Sopenharmony_ci u32 pipe_stats[I915_MAX_PIPES] = {}; 15368c2ecf20Sopenharmony_ci u32 hotplug_status = 0; 15378c2ecf20Sopenharmony_ci u32 ier = 0; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci gt_iir = I915_READ(GTIIR); 15408c2ecf20Sopenharmony_ci pm_iir = I915_READ(GEN6_PMIIR); 15418c2ecf20Sopenharmony_ci iir = I915_READ(VLV_IIR); 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci if (gt_iir == 0 && pm_iir == 0 && iir == 0) 15448c2ecf20Sopenharmony_ci break; 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci /* 15498c2ecf20Sopenharmony_ci * Theory on interrupt generation, based on empirical evidence: 15508c2ecf20Sopenharmony_ci * 15518c2ecf20Sopenharmony_ci * x = ((VLV_IIR & VLV_IER) || 15528c2ecf20Sopenharmony_ci * (((GT_IIR & GT_IER) || (GEN6_PMIIR & GEN6_PMIER)) && 15538c2ecf20Sopenharmony_ci * (VLV_MASTER_IER & MASTER_INTERRUPT_ENABLE))); 15548c2ecf20Sopenharmony_ci * 15558c2ecf20Sopenharmony_ci * A CPU interrupt will only be raised when 'x' has a 0->1 edge. 15568c2ecf20Sopenharmony_ci * Hence we clear MASTER_INTERRUPT_ENABLE and VLV_IER to 15578c2ecf20Sopenharmony_ci * guarantee the CPU interrupt will be raised again even if we 15588c2ecf20Sopenharmony_ci * don't end up clearing all the VLV_IIR, GT_IIR, GEN6_PMIIR 15598c2ecf20Sopenharmony_ci * bits this time around. 15608c2ecf20Sopenharmony_ci */ 15618c2ecf20Sopenharmony_ci I915_WRITE(VLV_MASTER_IER, 0); 15628c2ecf20Sopenharmony_ci ier = I915_READ(VLV_IER); 15638c2ecf20Sopenharmony_ci I915_WRITE(VLV_IER, 0); 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci if (gt_iir) 15668c2ecf20Sopenharmony_ci I915_WRITE(GTIIR, gt_iir); 15678c2ecf20Sopenharmony_ci if (pm_iir) 15688c2ecf20Sopenharmony_ci I915_WRITE(GEN6_PMIIR, pm_iir); 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci if (iir & I915_DISPLAY_PORT_INTERRUPT) 15718c2ecf20Sopenharmony_ci hotplug_status = i9xx_hpd_irq_ack(dev_priv); 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci /* Call regardless, as some status bits might not be 15748c2ecf20Sopenharmony_ci * signalled in iir */ 15758c2ecf20Sopenharmony_ci i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats); 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci if (iir & (I915_LPE_PIPE_A_INTERRUPT | 15788c2ecf20Sopenharmony_ci I915_LPE_PIPE_B_INTERRUPT)) 15798c2ecf20Sopenharmony_ci intel_lpe_audio_irq_handler(dev_priv); 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci /* 15828c2ecf20Sopenharmony_ci * VLV_IIR is single buffered, and reflects the level 15838c2ecf20Sopenharmony_ci * from PIPESTAT/PORT_HOTPLUG_STAT, hence clear it last. 15848c2ecf20Sopenharmony_ci */ 15858c2ecf20Sopenharmony_ci if (iir) 15868c2ecf20Sopenharmony_ci I915_WRITE(VLV_IIR, iir); 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci I915_WRITE(VLV_IER, ier); 15898c2ecf20Sopenharmony_ci I915_WRITE(VLV_MASTER_IER, MASTER_INTERRUPT_ENABLE); 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci if (gt_iir) 15928c2ecf20Sopenharmony_ci gen6_gt_irq_handler(&dev_priv->gt, gt_iir); 15938c2ecf20Sopenharmony_ci if (pm_iir) 15948c2ecf20Sopenharmony_ci gen6_rps_irq_handler(&dev_priv->gt.rps, pm_iir); 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci if (hotplug_status) 15978c2ecf20Sopenharmony_ci i9xx_hpd_irq_handler(dev_priv, hotplug_status); 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci valleyview_pipestat_irq_handler(dev_priv, pipe_stats); 16008c2ecf20Sopenharmony_ci } while (0); 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci enable_rpm_wakeref_asserts(&dev_priv->runtime_pm); 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci return ret; 16058c2ecf20Sopenharmony_ci} 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_cistatic irqreturn_t cherryview_irq_handler(int irq, void *arg) 16088c2ecf20Sopenharmony_ci{ 16098c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = arg; 16108c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci if (!intel_irqs_enabled(dev_priv)) 16138c2ecf20Sopenharmony_ci return IRQ_NONE; 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci /* IRQs are synced during runtime_suspend, we don't require a wakeref */ 16168c2ecf20Sopenharmony_ci disable_rpm_wakeref_asserts(&dev_priv->runtime_pm); 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci do { 16198c2ecf20Sopenharmony_ci u32 master_ctl, iir; 16208c2ecf20Sopenharmony_ci u32 pipe_stats[I915_MAX_PIPES] = {}; 16218c2ecf20Sopenharmony_ci u32 hotplug_status = 0; 16228c2ecf20Sopenharmony_ci u32 ier = 0; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci master_ctl = I915_READ(GEN8_MASTER_IRQ) & ~GEN8_MASTER_IRQ_CONTROL; 16258c2ecf20Sopenharmony_ci iir = I915_READ(VLV_IIR); 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci if (master_ctl == 0 && iir == 0) 16288c2ecf20Sopenharmony_ci break; 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci /* 16338c2ecf20Sopenharmony_ci * Theory on interrupt generation, based on empirical evidence: 16348c2ecf20Sopenharmony_ci * 16358c2ecf20Sopenharmony_ci * x = ((VLV_IIR & VLV_IER) || 16368c2ecf20Sopenharmony_ci * ((GEN8_MASTER_IRQ & ~GEN8_MASTER_IRQ_CONTROL) && 16378c2ecf20Sopenharmony_ci * (GEN8_MASTER_IRQ & GEN8_MASTER_IRQ_CONTROL))); 16388c2ecf20Sopenharmony_ci * 16398c2ecf20Sopenharmony_ci * A CPU interrupt will only be raised when 'x' has a 0->1 edge. 16408c2ecf20Sopenharmony_ci * Hence we clear GEN8_MASTER_IRQ_CONTROL and VLV_IER to 16418c2ecf20Sopenharmony_ci * guarantee the CPU interrupt will be raised again even if we 16428c2ecf20Sopenharmony_ci * don't end up clearing all the VLV_IIR and GEN8_MASTER_IRQ_CONTROL 16438c2ecf20Sopenharmony_ci * bits this time around. 16448c2ecf20Sopenharmony_ci */ 16458c2ecf20Sopenharmony_ci I915_WRITE(GEN8_MASTER_IRQ, 0); 16468c2ecf20Sopenharmony_ci ier = I915_READ(VLV_IER); 16478c2ecf20Sopenharmony_ci I915_WRITE(VLV_IER, 0); 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci gen8_gt_irq_handler(&dev_priv->gt, master_ctl); 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci if (iir & I915_DISPLAY_PORT_INTERRUPT) 16528c2ecf20Sopenharmony_ci hotplug_status = i9xx_hpd_irq_ack(dev_priv); 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci /* Call regardless, as some status bits might not be 16558c2ecf20Sopenharmony_ci * signalled in iir */ 16568c2ecf20Sopenharmony_ci i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats); 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci if (iir & (I915_LPE_PIPE_A_INTERRUPT | 16598c2ecf20Sopenharmony_ci I915_LPE_PIPE_B_INTERRUPT | 16608c2ecf20Sopenharmony_ci I915_LPE_PIPE_C_INTERRUPT)) 16618c2ecf20Sopenharmony_ci intel_lpe_audio_irq_handler(dev_priv); 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci /* 16648c2ecf20Sopenharmony_ci * VLV_IIR is single buffered, and reflects the level 16658c2ecf20Sopenharmony_ci * from PIPESTAT/PORT_HOTPLUG_STAT, hence clear it last. 16668c2ecf20Sopenharmony_ci */ 16678c2ecf20Sopenharmony_ci if (iir) 16688c2ecf20Sopenharmony_ci I915_WRITE(VLV_IIR, iir); 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci I915_WRITE(VLV_IER, ier); 16718c2ecf20Sopenharmony_ci I915_WRITE(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL); 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci if (hotplug_status) 16748c2ecf20Sopenharmony_ci i9xx_hpd_irq_handler(dev_priv, hotplug_status); 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci valleyview_pipestat_irq_handler(dev_priv, pipe_stats); 16778c2ecf20Sopenharmony_ci } while (0); 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci enable_rpm_wakeref_asserts(&dev_priv->runtime_pm); 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci return ret; 16828c2ecf20Sopenharmony_ci} 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_cistatic void ibx_hpd_irq_handler(struct drm_i915_private *dev_priv, 16858c2ecf20Sopenharmony_ci u32 hotplug_trigger) 16868c2ecf20Sopenharmony_ci{ 16878c2ecf20Sopenharmony_ci u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci /* 16908c2ecf20Sopenharmony_ci * Somehow the PCH doesn't seem to really ack the interrupt to the CPU 16918c2ecf20Sopenharmony_ci * unless we touch the hotplug register, even if hotplug_trigger is 16928c2ecf20Sopenharmony_ci * zero. Not acking leads to "The master control interrupt lied (SDE)!" 16938c2ecf20Sopenharmony_ci * errors. 16948c2ecf20Sopenharmony_ci */ 16958c2ecf20Sopenharmony_ci dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG); 16968c2ecf20Sopenharmony_ci if (!hotplug_trigger) { 16978c2ecf20Sopenharmony_ci u32 mask = PORTA_HOTPLUG_STATUS_MASK | 16988c2ecf20Sopenharmony_ci PORTD_HOTPLUG_STATUS_MASK | 16998c2ecf20Sopenharmony_ci PORTC_HOTPLUG_STATUS_MASK | 17008c2ecf20Sopenharmony_ci PORTB_HOTPLUG_STATUS_MASK; 17018c2ecf20Sopenharmony_ci dig_hotplug_reg &= ~mask; 17028c2ecf20Sopenharmony_ci } 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg); 17058c2ecf20Sopenharmony_ci if (!hotplug_trigger) 17068c2ecf20Sopenharmony_ci return; 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, 17098c2ecf20Sopenharmony_ci hotplug_trigger, dig_hotplug_reg, 17108c2ecf20Sopenharmony_ci dev_priv->hotplug.pch_hpd, 17118c2ecf20Sopenharmony_ci pch_port_hotplug_long_detect); 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci intel_hpd_irq_handler(dev_priv, pin_mask, long_mask); 17148c2ecf20Sopenharmony_ci} 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_cistatic void ibx_irq_handler(struct drm_i915_private *dev_priv, u32 pch_iir) 17178c2ecf20Sopenharmony_ci{ 17188c2ecf20Sopenharmony_ci enum pipe pipe; 17198c2ecf20Sopenharmony_ci u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK; 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci ibx_hpd_irq_handler(dev_priv, hotplug_trigger); 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci if (pch_iir & SDE_AUDIO_POWER_MASK) { 17248c2ecf20Sopenharmony_ci int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK) >> 17258c2ecf20Sopenharmony_ci SDE_AUDIO_POWER_SHIFT); 17268c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, "PCH audio power change on port %d\n", 17278c2ecf20Sopenharmony_ci port_name(port)); 17288c2ecf20Sopenharmony_ci } 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci if (pch_iir & SDE_AUX_MASK) 17318c2ecf20Sopenharmony_ci dp_aux_irq_handler(dev_priv); 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci if (pch_iir & SDE_GMBUS) 17348c2ecf20Sopenharmony_ci gmbus_irq_handler(dev_priv); 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci if (pch_iir & SDE_AUDIO_HDCP_MASK) 17378c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, "PCH HDCP audio interrupt\n"); 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci if (pch_iir & SDE_AUDIO_TRANS_MASK) 17408c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, "PCH transcoder audio interrupt\n"); 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci if (pch_iir & SDE_POISON) 17438c2ecf20Sopenharmony_ci drm_err(&dev_priv->drm, "PCH poison interrupt\n"); 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci if (pch_iir & SDE_FDI_MASK) { 17468c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) 17478c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, " pipe %c FDI IIR: 0x%08x\n", 17488c2ecf20Sopenharmony_ci pipe_name(pipe), 17498c2ecf20Sopenharmony_ci I915_READ(FDI_RX_IIR(pipe))); 17508c2ecf20Sopenharmony_ci } 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci if (pch_iir & (SDE_TRANSB_CRC_DONE | SDE_TRANSA_CRC_DONE)) 17538c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, "PCH transcoder CRC done interrupt\n"); 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci if (pch_iir & (SDE_TRANSB_CRC_ERR | SDE_TRANSA_CRC_ERR)) 17568c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, 17578c2ecf20Sopenharmony_ci "PCH transcoder CRC error interrupt\n"); 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci if (pch_iir & SDE_TRANSA_FIFO_UNDER) 17608c2ecf20Sopenharmony_ci intel_pch_fifo_underrun_irq_handler(dev_priv, PIPE_A); 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci if (pch_iir & SDE_TRANSB_FIFO_UNDER) 17638c2ecf20Sopenharmony_ci intel_pch_fifo_underrun_irq_handler(dev_priv, PIPE_B); 17648c2ecf20Sopenharmony_ci} 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_cistatic void ivb_err_int_handler(struct drm_i915_private *dev_priv) 17678c2ecf20Sopenharmony_ci{ 17688c2ecf20Sopenharmony_ci u32 err_int = I915_READ(GEN7_ERR_INT); 17698c2ecf20Sopenharmony_ci enum pipe pipe; 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci if (err_int & ERR_INT_POISON) 17728c2ecf20Sopenharmony_ci drm_err(&dev_priv->drm, "Poison interrupt\n"); 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) { 17758c2ecf20Sopenharmony_ci if (err_int & ERR_INT_FIFO_UNDERRUN(pipe)) 17768c2ecf20Sopenharmony_ci intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe); 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci if (err_int & ERR_INT_PIPE_CRC_DONE(pipe)) { 17798c2ecf20Sopenharmony_ci if (IS_IVYBRIDGE(dev_priv)) 17808c2ecf20Sopenharmony_ci ivb_pipe_crc_irq_handler(dev_priv, pipe); 17818c2ecf20Sopenharmony_ci else 17828c2ecf20Sopenharmony_ci hsw_pipe_crc_irq_handler(dev_priv, pipe); 17838c2ecf20Sopenharmony_ci } 17848c2ecf20Sopenharmony_ci } 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci I915_WRITE(GEN7_ERR_INT, err_int); 17878c2ecf20Sopenharmony_ci} 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_cistatic void cpt_serr_int_handler(struct drm_i915_private *dev_priv) 17908c2ecf20Sopenharmony_ci{ 17918c2ecf20Sopenharmony_ci u32 serr_int = I915_READ(SERR_INT); 17928c2ecf20Sopenharmony_ci enum pipe pipe; 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci if (serr_int & SERR_INT_POISON) 17958c2ecf20Sopenharmony_ci drm_err(&dev_priv->drm, "PCH poison interrupt\n"); 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) 17988c2ecf20Sopenharmony_ci if (serr_int & SERR_INT_TRANS_FIFO_UNDERRUN(pipe)) 17998c2ecf20Sopenharmony_ci intel_pch_fifo_underrun_irq_handler(dev_priv, pipe); 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci I915_WRITE(SERR_INT, serr_int); 18028c2ecf20Sopenharmony_ci} 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_cistatic void cpt_irq_handler(struct drm_i915_private *dev_priv, u32 pch_iir) 18058c2ecf20Sopenharmony_ci{ 18068c2ecf20Sopenharmony_ci enum pipe pipe; 18078c2ecf20Sopenharmony_ci u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_CPT; 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci ibx_hpd_irq_handler(dev_priv, hotplug_trigger); 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci if (pch_iir & SDE_AUDIO_POWER_MASK_CPT) { 18128c2ecf20Sopenharmony_ci int port = ffs((pch_iir & SDE_AUDIO_POWER_MASK_CPT) >> 18138c2ecf20Sopenharmony_ci SDE_AUDIO_POWER_SHIFT_CPT); 18148c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, "PCH audio power change on port %c\n", 18158c2ecf20Sopenharmony_ci port_name(port)); 18168c2ecf20Sopenharmony_ci } 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci if (pch_iir & SDE_AUX_MASK_CPT) 18198c2ecf20Sopenharmony_ci dp_aux_irq_handler(dev_priv); 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci if (pch_iir & SDE_GMBUS_CPT) 18228c2ecf20Sopenharmony_ci gmbus_irq_handler(dev_priv); 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci if (pch_iir & SDE_AUDIO_CP_REQ_CPT) 18258c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, "Audio CP request interrupt\n"); 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci if (pch_iir & SDE_AUDIO_CP_CHG_CPT) 18288c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, "Audio CP change interrupt\n"); 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci if (pch_iir & SDE_FDI_MASK_CPT) { 18318c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) 18328c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, " pipe %c FDI IIR: 0x%08x\n", 18338c2ecf20Sopenharmony_ci pipe_name(pipe), 18348c2ecf20Sopenharmony_ci I915_READ(FDI_RX_IIR(pipe))); 18358c2ecf20Sopenharmony_ci } 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci if (pch_iir & SDE_ERROR_CPT) 18388c2ecf20Sopenharmony_ci cpt_serr_int_handler(dev_priv); 18398c2ecf20Sopenharmony_ci} 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_cistatic void icp_irq_handler(struct drm_i915_private *dev_priv, u32 pch_iir) 18428c2ecf20Sopenharmony_ci{ 18438c2ecf20Sopenharmony_ci u32 ddi_hotplug_trigger, tc_hotplug_trigger; 18448c2ecf20Sopenharmony_ci u32 pin_mask = 0, long_mask = 0; 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci if (HAS_PCH_TGP(dev_priv)) { 18478c2ecf20Sopenharmony_ci ddi_hotplug_trigger = pch_iir & SDE_DDI_MASK_TGP; 18488c2ecf20Sopenharmony_ci tc_hotplug_trigger = pch_iir & SDE_TC_MASK_TGP; 18498c2ecf20Sopenharmony_ci } else if (HAS_PCH_JSP(dev_priv)) { 18508c2ecf20Sopenharmony_ci ddi_hotplug_trigger = pch_iir & SDE_DDI_MASK_TGP; 18518c2ecf20Sopenharmony_ci tc_hotplug_trigger = 0; 18528c2ecf20Sopenharmony_ci } else if (HAS_PCH_MCC(dev_priv)) { 18538c2ecf20Sopenharmony_ci ddi_hotplug_trigger = pch_iir & SDE_DDI_MASK_ICP; 18548c2ecf20Sopenharmony_ci tc_hotplug_trigger = pch_iir & SDE_TC_HOTPLUG_ICP(PORT_TC1); 18558c2ecf20Sopenharmony_ci } else { 18568c2ecf20Sopenharmony_ci drm_WARN(&dev_priv->drm, !HAS_PCH_ICP(dev_priv), 18578c2ecf20Sopenharmony_ci "Unrecognized PCH type 0x%x\n", 18588c2ecf20Sopenharmony_ci INTEL_PCH_TYPE(dev_priv)); 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci ddi_hotplug_trigger = pch_iir & SDE_DDI_MASK_ICP; 18618c2ecf20Sopenharmony_ci tc_hotplug_trigger = pch_iir & SDE_TC_MASK_ICP; 18628c2ecf20Sopenharmony_ci } 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci if (ddi_hotplug_trigger) { 18658c2ecf20Sopenharmony_ci u32 dig_hotplug_reg; 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci dig_hotplug_reg = I915_READ(SHOTPLUG_CTL_DDI); 18688c2ecf20Sopenharmony_ci I915_WRITE(SHOTPLUG_CTL_DDI, dig_hotplug_reg); 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, 18718c2ecf20Sopenharmony_ci ddi_hotplug_trigger, dig_hotplug_reg, 18728c2ecf20Sopenharmony_ci dev_priv->hotplug.pch_hpd, 18738c2ecf20Sopenharmony_ci icp_ddi_port_hotplug_long_detect); 18748c2ecf20Sopenharmony_ci } 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_ci if (tc_hotplug_trigger) { 18778c2ecf20Sopenharmony_ci u32 dig_hotplug_reg; 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci dig_hotplug_reg = I915_READ(SHOTPLUG_CTL_TC); 18808c2ecf20Sopenharmony_ci I915_WRITE(SHOTPLUG_CTL_TC, dig_hotplug_reg); 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, 18838c2ecf20Sopenharmony_ci tc_hotplug_trigger, dig_hotplug_reg, 18848c2ecf20Sopenharmony_ci dev_priv->hotplug.pch_hpd, 18858c2ecf20Sopenharmony_ci icp_tc_port_hotplug_long_detect); 18868c2ecf20Sopenharmony_ci } 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci if (pin_mask) 18898c2ecf20Sopenharmony_ci intel_hpd_irq_handler(dev_priv, pin_mask, long_mask); 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci if (pch_iir & SDE_GMBUS_ICP) 18928c2ecf20Sopenharmony_ci gmbus_irq_handler(dev_priv); 18938c2ecf20Sopenharmony_ci} 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_cistatic void spt_irq_handler(struct drm_i915_private *dev_priv, u32 pch_iir) 18968c2ecf20Sopenharmony_ci{ 18978c2ecf20Sopenharmony_ci u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_SPT & 18988c2ecf20Sopenharmony_ci ~SDE_PORTE_HOTPLUG_SPT; 18998c2ecf20Sopenharmony_ci u32 hotplug2_trigger = pch_iir & SDE_PORTE_HOTPLUG_SPT; 19008c2ecf20Sopenharmony_ci u32 pin_mask = 0, long_mask = 0; 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci if (hotplug_trigger) { 19038c2ecf20Sopenharmony_ci u32 dig_hotplug_reg; 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG); 19068c2ecf20Sopenharmony_ci I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg); 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, 19098c2ecf20Sopenharmony_ci hotplug_trigger, dig_hotplug_reg, 19108c2ecf20Sopenharmony_ci dev_priv->hotplug.pch_hpd, 19118c2ecf20Sopenharmony_ci spt_port_hotplug_long_detect); 19128c2ecf20Sopenharmony_ci } 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci if (hotplug2_trigger) { 19158c2ecf20Sopenharmony_ci u32 dig_hotplug_reg; 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG2); 19188c2ecf20Sopenharmony_ci I915_WRITE(PCH_PORT_HOTPLUG2, dig_hotplug_reg); 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, 19218c2ecf20Sopenharmony_ci hotplug2_trigger, dig_hotplug_reg, 19228c2ecf20Sopenharmony_ci dev_priv->hotplug.pch_hpd, 19238c2ecf20Sopenharmony_ci spt_port_hotplug2_long_detect); 19248c2ecf20Sopenharmony_ci } 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci if (pin_mask) 19278c2ecf20Sopenharmony_ci intel_hpd_irq_handler(dev_priv, pin_mask, long_mask); 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci if (pch_iir & SDE_GMBUS_CPT) 19308c2ecf20Sopenharmony_ci gmbus_irq_handler(dev_priv); 19318c2ecf20Sopenharmony_ci} 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_cistatic void ilk_hpd_irq_handler(struct drm_i915_private *dev_priv, 19348c2ecf20Sopenharmony_ci u32 hotplug_trigger) 19358c2ecf20Sopenharmony_ci{ 19368c2ecf20Sopenharmony_ci u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci dig_hotplug_reg = I915_READ(DIGITAL_PORT_HOTPLUG_CNTRL); 19398c2ecf20Sopenharmony_ci I915_WRITE(DIGITAL_PORT_HOTPLUG_CNTRL, dig_hotplug_reg); 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, 19428c2ecf20Sopenharmony_ci hotplug_trigger, dig_hotplug_reg, 19438c2ecf20Sopenharmony_ci dev_priv->hotplug.hpd, 19448c2ecf20Sopenharmony_ci ilk_port_hotplug_long_detect); 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci intel_hpd_irq_handler(dev_priv, pin_mask, long_mask); 19478c2ecf20Sopenharmony_ci} 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_cistatic void ilk_display_irq_handler(struct drm_i915_private *dev_priv, 19508c2ecf20Sopenharmony_ci u32 de_iir) 19518c2ecf20Sopenharmony_ci{ 19528c2ecf20Sopenharmony_ci enum pipe pipe; 19538c2ecf20Sopenharmony_ci u32 hotplug_trigger = de_iir & DE_DP_A_HOTPLUG; 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci if (hotplug_trigger) 19568c2ecf20Sopenharmony_ci ilk_hpd_irq_handler(dev_priv, hotplug_trigger); 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci if (de_iir & DE_AUX_CHANNEL_A) 19598c2ecf20Sopenharmony_ci dp_aux_irq_handler(dev_priv); 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci if (de_iir & DE_GSE) 19628c2ecf20Sopenharmony_ci intel_opregion_asle_intr(dev_priv); 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci if (de_iir & DE_POISON) 19658c2ecf20Sopenharmony_ci drm_err(&dev_priv->drm, "Poison interrupt\n"); 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) { 19688c2ecf20Sopenharmony_ci if (de_iir & DE_PIPE_VBLANK(pipe)) 19698c2ecf20Sopenharmony_ci intel_handle_vblank(dev_priv, pipe); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci if (de_iir & DE_PIPE_FIFO_UNDERRUN(pipe)) 19728c2ecf20Sopenharmony_ci intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe); 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci if (de_iir & DE_PIPE_CRC_DONE(pipe)) 19758c2ecf20Sopenharmony_ci i9xx_pipe_crc_irq_handler(dev_priv, pipe); 19768c2ecf20Sopenharmony_ci } 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci /* check event from PCH */ 19798c2ecf20Sopenharmony_ci if (de_iir & DE_PCH_EVENT) { 19808c2ecf20Sopenharmony_ci u32 pch_iir = I915_READ(SDEIIR); 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci if (HAS_PCH_CPT(dev_priv)) 19838c2ecf20Sopenharmony_ci cpt_irq_handler(dev_priv, pch_iir); 19848c2ecf20Sopenharmony_ci else 19858c2ecf20Sopenharmony_ci ibx_irq_handler(dev_priv, pch_iir); 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci /* should clear PCH hotplug event before clear CPU irq */ 19888c2ecf20Sopenharmony_ci I915_WRITE(SDEIIR, pch_iir); 19898c2ecf20Sopenharmony_ci } 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci if (IS_GEN(dev_priv, 5) && de_iir & DE_PCU_EVENT) 19928c2ecf20Sopenharmony_ci gen5_rps_irq_handler(&dev_priv->gt.rps); 19938c2ecf20Sopenharmony_ci} 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_cistatic void ivb_display_irq_handler(struct drm_i915_private *dev_priv, 19968c2ecf20Sopenharmony_ci u32 de_iir) 19978c2ecf20Sopenharmony_ci{ 19988c2ecf20Sopenharmony_ci enum pipe pipe; 19998c2ecf20Sopenharmony_ci u32 hotplug_trigger = de_iir & DE_DP_A_HOTPLUG_IVB; 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci if (hotplug_trigger) 20028c2ecf20Sopenharmony_ci ilk_hpd_irq_handler(dev_priv, hotplug_trigger); 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci if (de_iir & DE_ERR_INT_IVB) 20058c2ecf20Sopenharmony_ci ivb_err_int_handler(dev_priv); 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci if (de_iir & DE_EDP_PSR_INT_HSW) { 20088c2ecf20Sopenharmony_ci u32 psr_iir = I915_READ(EDP_PSR_IIR); 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_ci intel_psr_irq_handler(dev_priv, psr_iir); 20118c2ecf20Sopenharmony_ci I915_WRITE(EDP_PSR_IIR, psr_iir); 20128c2ecf20Sopenharmony_ci } 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci if (de_iir & DE_AUX_CHANNEL_A_IVB) 20158c2ecf20Sopenharmony_ci dp_aux_irq_handler(dev_priv); 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_ci if (de_iir & DE_GSE_IVB) 20188c2ecf20Sopenharmony_ci intel_opregion_asle_intr(dev_priv); 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) { 20218c2ecf20Sopenharmony_ci if (de_iir & (DE_PIPE_VBLANK_IVB(pipe))) 20228c2ecf20Sopenharmony_ci intel_handle_vblank(dev_priv, pipe); 20238c2ecf20Sopenharmony_ci } 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci /* check event from PCH */ 20268c2ecf20Sopenharmony_ci if (!HAS_PCH_NOP(dev_priv) && (de_iir & DE_PCH_EVENT_IVB)) { 20278c2ecf20Sopenharmony_ci u32 pch_iir = I915_READ(SDEIIR); 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_ci cpt_irq_handler(dev_priv, pch_iir); 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci /* clear PCH hotplug event before clear CPU irq */ 20328c2ecf20Sopenharmony_ci I915_WRITE(SDEIIR, pch_iir); 20338c2ecf20Sopenharmony_ci } 20348c2ecf20Sopenharmony_ci} 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci/* 20378c2ecf20Sopenharmony_ci * To handle irqs with the minimum potential races with fresh interrupts, we: 20388c2ecf20Sopenharmony_ci * 1 - Disable Master Interrupt Control. 20398c2ecf20Sopenharmony_ci * 2 - Find the source(s) of the interrupt. 20408c2ecf20Sopenharmony_ci * 3 - Clear the Interrupt Identity bits (IIR). 20418c2ecf20Sopenharmony_ci * 4 - Process the interrupt(s) that had bits set in the IIRs. 20428c2ecf20Sopenharmony_ci * 5 - Re-enable Master Interrupt Control. 20438c2ecf20Sopenharmony_ci */ 20448c2ecf20Sopenharmony_cistatic irqreturn_t ilk_irq_handler(int irq, void *arg) 20458c2ecf20Sopenharmony_ci{ 20468c2ecf20Sopenharmony_ci struct drm_i915_private *i915 = arg; 20478c2ecf20Sopenharmony_ci void __iomem * const regs = i915->uncore.regs; 20488c2ecf20Sopenharmony_ci u32 de_iir, gt_iir, de_ier, sde_ier = 0; 20498c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci if (unlikely(!intel_irqs_enabled(i915))) 20528c2ecf20Sopenharmony_ci return IRQ_NONE; 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci /* IRQs are synced during runtime_suspend, we don't require a wakeref */ 20558c2ecf20Sopenharmony_ci disable_rpm_wakeref_asserts(&i915->runtime_pm); 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci /* disable master interrupt before clearing iir */ 20588c2ecf20Sopenharmony_ci de_ier = raw_reg_read(regs, DEIER); 20598c2ecf20Sopenharmony_ci raw_reg_write(regs, DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL); 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_ci /* Disable south interrupts. We'll only write to SDEIIR once, so further 20628c2ecf20Sopenharmony_ci * interrupts will will be stored on its back queue, and then we'll be 20638c2ecf20Sopenharmony_ci * able to process them after we restore SDEIER (as soon as we restore 20648c2ecf20Sopenharmony_ci * it, we'll get an interrupt if SDEIIR still has something to process 20658c2ecf20Sopenharmony_ci * due to its back queue). */ 20668c2ecf20Sopenharmony_ci if (!HAS_PCH_NOP(i915)) { 20678c2ecf20Sopenharmony_ci sde_ier = raw_reg_read(regs, SDEIER); 20688c2ecf20Sopenharmony_ci raw_reg_write(regs, SDEIER, 0); 20698c2ecf20Sopenharmony_ci } 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci /* Find, clear, then process each source of interrupt */ 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci gt_iir = raw_reg_read(regs, GTIIR); 20748c2ecf20Sopenharmony_ci if (gt_iir) { 20758c2ecf20Sopenharmony_ci raw_reg_write(regs, GTIIR, gt_iir); 20768c2ecf20Sopenharmony_ci if (INTEL_GEN(i915) >= 6) 20778c2ecf20Sopenharmony_ci gen6_gt_irq_handler(&i915->gt, gt_iir); 20788c2ecf20Sopenharmony_ci else 20798c2ecf20Sopenharmony_ci gen5_gt_irq_handler(&i915->gt, gt_iir); 20808c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 20818c2ecf20Sopenharmony_ci } 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci de_iir = raw_reg_read(regs, DEIIR); 20848c2ecf20Sopenharmony_ci if (de_iir) { 20858c2ecf20Sopenharmony_ci raw_reg_write(regs, DEIIR, de_iir); 20868c2ecf20Sopenharmony_ci if (INTEL_GEN(i915) >= 7) 20878c2ecf20Sopenharmony_ci ivb_display_irq_handler(i915, de_iir); 20888c2ecf20Sopenharmony_ci else 20898c2ecf20Sopenharmony_ci ilk_display_irq_handler(i915, de_iir); 20908c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 20918c2ecf20Sopenharmony_ci } 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci if (INTEL_GEN(i915) >= 6) { 20948c2ecf20Sopenharmony_ci u32 pm_iir = raw_reg_read(regs, GEN6_PMIIR); 20958c2ecf20Sopenharmony_ci if (pm_iir) { 20968c2ecf20Sopenharmony_ci raw_reg_write(regs, GEN6_PMIIR, pm_iir); 20978c2ecf20Sopenharmony_ci gen6_rps_irq_handler(&i915->gt.rps, pm_iir); 20988c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 20998c2ecf20Sopenharmony_ci } 21008c2ecf20Sopenharmony_ci } 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ci raw_reg_write(regs, DEIER, de_ier); 21038c2ecf20Sopenharmony_ci if (sde_ier) 21048c2ecf20Sopenharmony_ci raw_reg_write(regs, SDEIER, sde_ier); 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci /* IRQs are synced during runtime_suspend, we don't require a wakeref */ 21078c2ecf20Sopenharmony_ci enable_rpm_wakeref_asserts(&i915->runtime_pm); 21088c2ecf20Sopenharmony_ci 21098c2ecf20Sopenharmony_ci return ret; 21108c2ecf20Sopenharmony_ci} 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_cistatic void bxt_hpd_irq_handler(struct drm_i915_private *dev_priv, 21138c2ecf20Sopenharmony_ci u32 hotplug_trigger) 21148c2ecf20Sopenharmony_ci{ 21158c2ecf20Sopenharmony_ci u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci dig_hotplug_reg = I915_READ(PCH_PORT_HOTPLUG); 21188c2ecf20Sopenharmony_ci I915_WRITE(PCH_PORT_HOTPLUG, dig_hotplug_reg); 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, 21218c2ecf20Sopenharmony_ci hotplug_trigger, dig_hotplug_reg, 21228c2ecf20Sopenharmony_ci dev_priv->hotplug.hpd, 21238c2ecf20Sopenharmony_ci bxt_port_hotplug_long_detect); 21248c2ecf20Sopenharmony_ci 21258c2ecf20Sopenharmony_ci intel_hpd_irq_handler(dev_priv, pin_mask, long_mask); 21268c2ecf20Sopenharmony_ci} 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_cistatic void gen11_hpd_irq_handler(struct drm_i915_private *dev_priv, u32 iir) 21298c2ecf20Sopenharmony_ci{ 21308c2ecf20Sopenharmony_ci u32 pin_mask = 0, long_mask = 0; 21318c2ecf20Sopenharmony_ci u32 trigger_tc = iir & GEN11_DE_TC_HOTPLUG_MASK; 21328c2ecf20Sopenharmony_ci u32 trigger_tbt = iir & GEN11_DE_TBT_HOTPLUG_MASK; 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci if (trigger_tc) { 21358c2ecf20Sopenharmony_ci u32 dig_hotplug_reg; 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci dig_hotplug_reg = I915_READ(GEN11_TC_HOTPLUG_CTL); 21388c2ecf20Sopenharmony_ci I915_WRITE(GEN11_TC_HOTPLUG_CTL, dig_hotplug_reg); 21398c2ecf20Sopenharmony_ci 21408c2ecf20Sopenharmony_ci intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, 21418c2ecf20Sopenharmony_ci trigger_tc, dig_hotplug_reg, 21428c2ecf20Sopenharmony_ci dev_priv->hotplug.hpd, 21438c2ecf20Sopenharmony_ci gen11_port_hotplug_long_detect); 21448c2ecf20Sopenharmony_ci } 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci if (trigger_tbt) { 21478c2ecf20Sopenharmony_ci u32 dig_hotplug_reg; 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci dig_hotplug_reg = I915_READ(GEN11_TBT_HOTPLUG_CTL); 21508c2ecf20Sopenharmony_ci I915_WRITE(GEN11_TBT_HOTPLUG_CTL, dig_hotplug_reg); 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, 21538c2ecf20Sopenharmony_ci trigger_tbt, dig_hotplug_reg, 21548c2ecf20Sopenharmony_ci dev_priv->hotplug.hpd, 21558c2ecf20Sopenharmony_ci gen11_port_hotplug_long_detect); 21568c2ecf20Sopenharmony_ci } 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci if (pin_mask) 21598c2ecf20Sopenharmony_ci intel_hpd_irq_handler(dev_priv, pin_mask, long_mask); 21608c2ecf20Sopenharmony_ci else 21618c2ecf20Sopenharmony_ci drm_err(&dev_priv->drm, 21628c2ecf20Sopenharmony_ci "Unexpected DE HPD interrupt 0x%08x\n", iir); 21638c2ecf20Sopenharmony_ci} 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_cistatic u32 gen8_de_port_aux_mask(struct drm_i915_private *dev_priv) 21668c2ecf20Sopenharmony_ci{ 21678c2ecf20Sopenharmony_ci u32 mask; 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 12) 21708c2ecf20Sopenharmony_ci return TGL_DE_PORT_AUX_DDIA | 21718c2ecf20Sopenharmony_ci TGL_DE_PORT_AUX_DDIB | 21728c2ecf20Sopenharmony_ci TGL_DE_PORT_AUX_DDIC | 21738c2ecf20Sopenharmony_ci TGL_DE_PORT_AUX_USBC1 | 21748c2ecf20Sopenharmony_ci TGL_DE_PORT_AUX_USBC2 | 21758c2ecf20Sopenharmony_ci TGL_DE_PORT_AUX_USBC3 | 21768c2ecf20Sopenharmony_ci TGL_DE_PORT_AUX_USBC4 | 21778c2ecf20Sopenharmony_ci TGL_DE_PORT_AUX_USBC5 | 21788c2ecf20Sopenharmony_ci TGL_DE_PORT_AUX_USBC6; 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci mask = GEN8_AUX_CHANNEL_A; 21828c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 9) 21838c2ecf20Sopenharmony_ci mask |= GEN9_AUX_CHANNEL_B | 21848c2ecf20Sopenharmony_ci GEN9_AUX_CHANNEL_C | 21858c2ecf20Sopenharmony_ci GEN9_AUX_CHANNEL_D; 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_ci if (IS_CNL_WITH_PORT_F(dev_priv) || IS_GEN(dev_priv, 11)) 21888c2ecf20Sopenharmony_ci mask |= CNL_AUX_CHANNEL_F; 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ci if (IS_GEN(dev_priv, 11)) 21918c2ecf20Sopenharmony_ci mask |= ICL_AUX_CHANNEL_E; 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci return mask; 21948c2ecf20Sopenharmony_ci} 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_cistatic u32 gen8_de_pipe_fault_mask(struct drm_i915_private *dev_priv) 21978c2ecf20Sopenharmony_ci{ 21988c2ecf20Sopenharmony_ci if (IS_ROCKETLAKE(dev_priv)) 21998c2ecf20Sopenharmony_ci return RKL_DE_PIPE_IRQ_FAULT_ERRORS; 22008c2ecf20Sopenharmony_ci else if (INTEL_GEN(dev_priv) >= 11) 22018c2ecf20Sopenharmony_ci return GEN11_DE_PIPE_IRQ_FAULT_ERRORS; 22028c2ecf20Sopenharmony_ci else if (INTEL_GEN(dev_priv) >= 9) 22038c2ecf20Sopenharmony_ci return GEN9_DE_PIPE_IRQ_FAULT_ERRORS; 22048c2ecf20Sopenharmony_ci else 22058c2ecf20Sopenharmony_ci return GEN8_DE_PIPE_IRQ_FAULT_ERRORS; 22068c2ecf20Sopenharmony_ci} 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_cistatic void 22098c2ecf20Sopenharmony_cigen8_de_misc_irq_handler(struct drm_i915_private *dev_priv, u32 iir) 22108c2ecf20Sopenharmony_ci{ 22118c2ecf20Sopenharmony_ci bool found = false; 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci if (iir & GEN8_DE_MISC_GSE) { 22148c2ecf20Sopenharmony_ci intel_opregion_asle_intr(dev_priv); 22158c2ecf20Sopenharmony_ci found = true; 22168c2ecf20Sopenharmony_ci } 22178c2ecf20Sopenharmony_ci 22188c2ecf20Sopenharmony_ci if (iir & GEN8_DE_EDP_PSR) { 22198c2ecf20Sopenharmony_ci u32 psr_iir; 22208c2ecf20Sopenharmony_ci i915_reg_t iir_reg; 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 12) 22238c2ecf20Sopenharmony_ci iir_reg = TRANS_PSR_IIR(dev_priv->psr.transcoder); 22248c2ecf20Sopenharmony_ci else 22258c2ecf20Sopenharmony_ci iir_reg = EDP_PSR_IIR; 22268c2ecf20Sopenharmony_ci 22278c2ecf20Sopenharmony_ci psr_iir = I915_READ(iir_reg); 22288c2ecf20Sopenharmony_ci I915_WRITE(iir_reg, psr_iir); 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci if (psr_iir) 22318c2ecf20Sopenharmony_ci found = true; 22328c2ecf20Sopenharmony_ci 22338c2ecf20Sopenharmony_ci intel_psr_irq_handler(dev_priv, psr_iir); 22348c2ecf20Sopenharmony_ci } 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_ci if (!found) 22378c2ecf20Sopenharmony_ci drm_err(&dev_priv->drm, "Unexpected DE Misc interrupt\n"); 22388c2ecf20Sopenharmony_ci} 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_cistatic irqreturn_t 22418c2ecf20Sopenharmony_cigen8_de_irq_handler(struct drm_i915_private *dev_priv, u32 master_ctl) 22428c2ecf20Sopenharmony_ci{ 22438c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 22448c2ecf20Sopenharmony_ci u32 iir; 22458c2ecf20Sopenharmony_ci enum pipe pipe; 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci if (master_ctl & GEN8_DE_MISC_IRQ) { 22488c2ecf20Sopenharmony_ci iir = I915_READ(GEN8_DE_MISC_IIR); 22498c2ecf20Sopenharmony_ci if (iir) { 22508c2ecf20Sopenharmony_ci I915_WRITE(GEN8_DE_MISC_IIR, iir); 22518c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 22528c2ecf20Sopenharmony_ci gen8_de_misc_irq_handler(dev_priv, iir); 22538c2ecf20Sopenharmony_ci } else { 22548c2ecf20Sopenharmony_ci drm_err(&dev_priv->drm, 22558c2ecf20Sopenharmony_ci "The master control interrupt lied (DE MISC)!\n"); 22568c2ecf20Sopenharmony_ci } 22578c2ecf20Sopenharmony_ci } 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 11 && (master_ctl & GEN11_DE_HPD_IRQ)) { 22608c2ecf20Sopenharmony_ci iir = I915_READ(GEN11_DE_HPD_IIR); 22618c2ecf20Sopenharmony_ci if (iir) { 22628c2ecf20Sopenharmony_ci I915_WRITE(GEN11_DE_HPD_IIR, iir); 22638c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 22648c2ecf20Sopenharmony_ci gen11_hpd_irq_handler(dev_priv, iir); 22658c2ecf20Sopenharmony_ci } else { 22668c2ecf20Sopenharmony_ci drm_err(&dev_priv->drm, 22678c2ecf20Sopenharmony_ci "The master control interrupt lied, (DE HPD)!\n"); 22688c2ecf20Sopenharmony_ci } 22698c2ecf20Sopenharmony_ci } 22708c2ecf20Sopenharmony_ci 22718c2ecf20Sopenharmony_ci if (master_ctl & GEN8_DE_PORT_IRQ) { 22728c2ecf20Sopenharmony_ci iir = I915_READ(GEN8_DE_PORT_IIR); 22738c2ecf20Sopenharmony_ci if (iir) { 22748c2ecf20Sopenharmony_ci u32 tmp_mask; 22758c2ecf20Sopenharmony_ci bool found = false; 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_ci I915_WRITE(GEN8_DE_PORT_IIR, iir); 22788c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 22798c2ecf20Sopenharmony_ci 22808c2ecf20Sopenharmony_ci if (iir & gen8_de_port_aux_mask(dev_priv)) { 22818c2ecf20Sopenharmony_ci dp_aux_irq_handler(dev_priv); 22828c2ecf20Sopenharmony_ci found = true; 22838c2ecf20Sopenharmony_ci } 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci if (IS_GEN9_LP(dev_priv)) { 22868c2ecf20Sopenharmony_ci tmp_mask = iir & BXT_DE_PORT_HOTPLUG_MASK; 22878c2ecf20Sopenharmony_ci if (tmp_mask) { 22888c2ecf20Sopenharmony_ci bxt_hpd_irq_handler(dev_priv, tmp_mask); 22898c2ecf20Sopenharmony_ci found = true; 22908c2ecf20Sopenharmony_ci } 22918c2ecf20Sopenharmony_ci } else if (IS_BROADWELL(dev_priv)) { 22928c2ecf20Sopenharmony_ci tmp_mask = iir & GEN8_PORT_DP_A_HOTPLUG; 22938c2ecf20Sopenharmony_ci if (tmp_mask) { 22948c2ecf20Sopenharmony_ci ilk_hpd_irq_handler(dev_priv, tmp_mask); 22958c2ecf20Sopenharmony_ci found = true; 22968c2ecf20Sopenharmony_ci } 22978c2ecf20Sopenharmony_ci } 22988c2ecf20Sopenharmony_ci 22998c2ecf20Sopenharmony_ci if (IS_GEN9_LP(dev_priv) && (iir & BXT_DE_PORT_GMBUS)) { 23008c2ecf20Sopenharmony_ci gmbus_irq_handler(dev_priv); 23018c2ecf20Sopenharmony_ci found = true; 23028c2ecf20Sopenharmony_ci } 23038c2ecf20Sopenharmony_ci 23048c2ecf20Sopenharmony_ci if (!found) 23058c2ecf20Sopenharmony_ci drm_err(&dev_priv->drm, 23068c2ecf20Sopenharmony_ci "Unexpected DE Port interrupt\n"); 23078c2ecf20Sopenharmony_ci } 23088c2ecf20Sopenharmony_ci else 23098c2ecf20Sopenharmony_ci drm_err(&dev_priv->drm, 23108c2ecf20Sopenharmony_ci "The master control interrupt lied (DE PORT)!\n"); 23118c2ecf20Sopenharmony_ci } 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) { 23148c2ecf20Sopenharmony_ci u32 fault_errors; 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci if (!(master_ctl & GEN8_DE_PIPE_IRQ(pipe))) 23178c2ecf20Sopenharmony_ci continue; 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci iir = I915_READ(GEN8_DE_PIPE_IIR(pipe)); 23208c2ecf20Sopenharmony_ci if (!iir) { 23218c2ecf20Sopenharmony_ci drm_err(&dev_priv->drm, 23228c2ecf20Sopenharmony_ci "The master control interrupt lied (DE PIPE)!\n"); 23238c2ecf20Sopenharmony_ci continue; 23248c2ecf20Sopenharmony_ci } 23258c2ecf20Sopenharmony_ci 23268c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 23278c2ecf20Sopenharmony_ci I915_WRITE(GEN8_DE_PIPE_IIR(pipe), iir); 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_ci if (iir & GEN8_PIPE_VBLANK) 23308c2ecf20Sopenharmony_ci intel_handle_vblank(dev_priv, pipe); 23318c2ecf20Sopenharmony_ci 23328c2ecf20Sopenharmony_ci if (iir & GEN8_PIPE_CDCLK_CRC_DONE) 23338c2ecf20Sopenharmony_ci hsw_pipe_crc_irq_handler(dev_priv, pipe); 23348c2ecf20Sopenharmony_ci 23358c2ecf20Sopenharmony_ci if (iir & GEN8_PIPE_FIFO_UNDERRUN) 23368c2ecf20Sopenharmony_ci intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe); 23378c2ecf20Sopenharmony_ci 23388c2ecf20Sopenharmony_ci fault_errors = iir & gen8_de_pipe_fault_mask(dev_priv); 23398c2ecf20Sopenharmony_ci if (fault_errors) 23408c2ecf20Sopenharmony_ci drm_err(&dev_priv->drm, 23418c2ecf20Sopenharmony_ci "Fault errors on pipe %c: 0x%08x\n", 23428c2ecf20Sopenharmony_ci pipe_name(pipe), 23438c2ecf20Sopenharmony_ci fault_errors); 23448c2ecf20Sopenharmony_ci } 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ci if (HAS_PCH_SPLIT(dev_priv) && !HAS_PCH_NOP(dev_priv) && 23478c2ecf20Sopenharmony_ci master_ctl & GEN8_DE_PCH_IRQ) { 23488c2ecf20Sopenharmony_ci /* 23498c2ecf20Sopenharmony_ci * FIXME(BDW): Assume for now that the new interrupt handling 23508c2ecf20Sopenharmony_ci * scheme also closed the SDE interrupt handling race we've seen 23518c2ecf20Sopenharmony_ci * on older pch-split platforms. But this needs testing. 23528c2ecf20Sopenharmony_ci */ 23538c2ecf20Sopenharmony_ci iir = I915_READ(SDEIIR); 23548c2ecf20Sopenharmony_ci if (iir) { 23558c2ecf20Sopenharmony_ci I915_WRITE(SDEIIR, iir); 23568c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci if (INTEL_PCH_TYPE(dev_priv) >= PCH_ICP) 23598c2ecf20Sopenharmony_ci icp_irq_handler(dev_priv, iir); 23608c2ecf20Sopenharmony_ci else if (INTEL_PCH_TYPE(dev_priv) >= PCH_SPT) 23618c2ecf20Sopenharmony_ci spt_irq_handler(dev_priv, iir); 23628c2ecf20Sopenharmony_ci else 23638c2ecf20Sopenharmony_ci cpt_irq_handler(dev_priv, iir); 23648c2ecf20Sopenharmony_ci } else { 23658c2ecf20Sopenharmony_ci /* 23668c2ecf20Sopenharmony_ci * Like on previous PCH there seems to be something 23678c2ecf20Sopenharmony_ci * fishy going on with forwarding PCH interrupts. 23688c2ecf20Sopenharmony_ci */ 23698c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, 23708c2ecf20Sopenharmony_ci "The master control interrupt lied (SDE)!\n"); 23718c2ecf20Sopenharmony_ci } 23728c2ecf20Sopenharmony_ci } 23738c2ecf20Sopenharmony_ci 23748c2ecf20Sopenharmony_ci return ret; 23758c2ecf20Sopenharmony_ci} 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_cistatic inline u32 gen8_master_intr_disable(void __iomem * const regs) 23788c2ecf20Sopenharmony_ci{ 23798c2ecf20Sopenharmony_ci raw_reg_write(regs, GEN8_MASTER_IRQ, 0); 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_ci /* 23828c2ecf20Sopenharmony_ci * Now with master disabled, get a sample of level indications 23838c2ecf20Sopenharmony_ci * for this interrupt. Indications will be cleared on related acks. 23848c2ecf20Sopenharmony_ci * New indications can and will light up during processing, 23858c2ecf20Sopenharmony_ci * and will generate new interrupt after enabling master. 23868c2ecf20Sopenharmony_ci */ 23878c2ecf20Sopenharmony_ci return raw_reg_read(regs, GEN8_MASTER_IRQ); 23888c2ecf20Sopenharmony_ci} 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_cistatic inline void gen8_master_intr_enable(void __iomem * const regs) 23918c2ecf20Sopenharmony_ci{ 23928c2ecf20Sopenharmony_ci raw_reg_write(regs, GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL); 23938c2ecf20Sopenharmony_ci} 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_cistatic irqreturn_t gen8_irq_handler(int irq, void *arg) 23968c2ecf20Sopenharmony_ci{ 23978c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = arg; 23988c2ecf20Sopenharmony_ci void __iomem * const regs = dev_priv->uncore.regs; 23998c2ecf20Sopenharmony_ci u32 master_ctl; 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci if (!intel_irqs_enabled(dev_priv)) 24028c2ecf20Sopenharmony_ci return IRQ_NONE; 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_ci master_ctl = gen8_master_intr_disable(regs); 24058c2ecf20Sopenharmony_ci if (!master_ctl) { 24068c2ecf20Sopenharmony_ci gen8_master_intr_enable(regs); 24078c2ecf20Sopenharmony_ci return IRQ_NONE; 24088c2ecf20Sopenharmony_ci } 24098c2ecf20Sopenharmony_ci 24108c2ecf20Sopenharmony_ci /* Find, queue (onto bottom-halves), then clear each source */ 24118c2ecf20Sopenharmony_ci gen8_gt_irq_handler(&dev_priv->gt, master_ctl); 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_ci /* IRQs are synced during runtime_suspend, we don't require a wakeref */ 24148c2ecf20Sopenharmony_ci if (master_ctl & ~GEN8_GT_IRQS) { 24158c2ecf20Sopenharmony_ci disable_rpm_wakeref_asserts(&dev_priv->runtime_pm); 24168c2ecf20Sopenharmony_ci gen8_de_irq_handler(dev_priv, master_ctl); 24178c2ecf20Sopenharmony_ci enable_rpm_wakeref_asserts(&dev_priv->runtime_pm); 24188c2ecf20Sopenharmony_ci } 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_ci gen8_master_intr_enable(regs); 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_ci return IRQ_HANDLED; 24238c2ecf20Sopenharmony_ci} 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_cistatic u32 24268c2ecf20Sopenharmony_cigen11_gu_misc_irq_ack(struct intel_gt *gt, const u32 master_ctl) 24278c2ecf20Sopenharmony_ci{ 24288c2ecf20Sopenharmony_ci void __iomem * const regs = gt->uncore->regs; 24298c2ecf20Sopenharmony_ci u32 iir; 24308c2ecf20Sopenharmony_ci 24318c2ecf20Sopenharmony_ci if (!(master_ctl & GEN11_GU_MISC_IRQ)) 24328c2ecf20Sopenharmony_ci return 0; 24338c2ecf20Sopenharmony_ci 24348c2ecf20Sopenharmony_ci iir = raw_reg_read(regs, GEN11_GU_MISC_IIR); 24358c2ecf20Sopenharmony_ci if (likely(iir)) 24368c2ecf20Sopenharmony_ci raw_reg_write(regs, GEN11_GU_MISC_IIR, iir); 24378c2ecf20Sopenharmony_ci 24388c2ecf20Sopenharmony_ci return iir; 24398c2ecf20Sopenharmony_ci} 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_cistatic void 24428c2ecf20Sopenharmony_cigen11_gu_misc_irq_handler(struct intel_gt *gt, const u32 iir) 24438c2ecf20Sopenharmony_ci{ 24448c2ecf20Sopenharmony_ci if (iir & GEN11_GU_MISC_GSE) 24458c2ecf20Sopenharmony_ci intel_opregion_asle_intr(gt->i915); 24468c2ecf20Sopenharmony_ci} 24478c2ecf20Sopenharmony_ci 24488c2ecf20Sopenharmony_cistatic inline u32 gen11_master_intr_disable(void __iomem * const regs) 24498c2ecf20Sopenharmony_ci{ 24508c2ecf20Sopenharmony_ci raw_reg_write(regs, GEN11_GFX_MSTR_IRQ, 0); 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci /* 24538c2ecf20Sopenharmony_ci * Now with master disabled, get a sample of level indications 24548c2ecf20Sopenharmony_ci * for this interrupt. Indications will be cleared on related acks. 24558c2ecf20Sopenharmony_ci * New indications can and will light up during processing, 24568c2ecf20Sopenharmony_ci * and will generate new interrupt after enabling master. 24578c2ecf20Sopenharmony_ci */ 24588c2ecf20Sopenharmony_ci return raw_reg_read(regs, GEN11_GFX_MSTR_IRQ); 24598c2ecf20Sopenharmony_ci} 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_cistatic inline void gen11_master_intr_enable(void __iomem * const regs) 24628c2ecf20Sopenharmony_ci{ 24638c2ecf20Sopenharmony_ci raw_reg_write(regs, GEN11_GFX_MSTR_IRQ, GEN11_MASTER_IRQ); 24648c2ecf20Sopenharmony_ci} 24658c2ecf20Sopenharmony_ci 24668c2ecf20Sopenharmony_cistatic void 24678c2ecf20Sopenharmony_cigen11_display_irq_handler(struct drm_i915_private *i915) 24688c2ecf20Sopenharmony_ci{ 24698c2ecf20Sopenharmony_ci void __iomem * const regs = i915->uncore.regs; 24708c2ecf20Sopenharmony_ci const u32 disp_ctl = raw_reg_read(regs, GEN11_DISPLAY_INT_CTL); 24718c2ecf20Sopenharmony_ci 24728c2ecf20Sopenharmony_ci disable_rpm_wakeref_asserts(&i915->runtime_pm); 24738c2ecf20Sopenharmony_ci /* 24748c2ecf20Sopenharmony_ci * GEN11_DISPLAY_INT_CTL has same format as GEN8_MASTER_IRQ 24758c2ecf20Sopenharmony_ci * for the display related bits. 24768c2ecf20Sopenharmony_ci */ 24778c2ecf20Sopenharmony_ci raw_reg_write(regs, GEN11_DISPLAY_INT_CTL, 0x0); 24788c2ecf20Sopenharmony_ci gen8_de_irq_handler(i915, disp_ctl); 24798c2ecf20Sopenharmony_ci raw_reg_write(regs, GEN11_DISPLAY_INT_CTL, 24808c2ecf20Sopenharmony_ci GEN11_DISPLAY_IRQ_ENABLE); 24818c2ecf20Sopenharmony_ci 24828c2ecf20Sopenharmony_ci enable_rpm_wakeref_asserts(&i915->runtime_pm); 24838c2ecf20Sopenharmony_ci} 24848c2ecf20Sopenharmony_ci 24858c2ecf20Sopenharmony_cistatic __always_inline irqreturn_t 24868c2ecf20Sopenharmony_ci__gen11_irq_handler(struct drm_i915_private * const i915, 24878c2ecf20Sopenharmony_ci u32 (*intr_disable)(void __iomem * const regs), 24888c2ecf20Sopenharmony_ci void (*intr_enable)(void __iomem * const regs)) 24898c2ecf20Sopenharmony_ci{ 24908c2ecf20Sopenharmony_ci void __iomem * const regs = i915->uncore.regs; 24918c2ecf20Sopenharmony_ci struct intel_gt *gt = &i915->gt; 24928c2ecf20Sopenharmony_ci u32 master_ctl; 24938c2ecf20Sopenharmony_ci u32 gu_misc_iir; 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_ci if (!intel_irqs_enabled(i915)) 24968c2ecf20Sopenharmony_ci return IRQ_NONE; 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci master_ctl = intr_disable(regs); 24998c2ecf20Sopenharmony_ci if (!master_ctl) { 25008c2ecf20Sopenharmony_ci intr_enable(regs); 25018c2ecf20Sopenharmony_ci return IRQ_NONE; 25028c2ecf20Sopenharmony_ci } 25038c2ecf20Sopenharmony_ci 25048c2ecf20Sopenharmony_ci /* Find, queue (onto bottom-halves), then clear each source */ 25058c2ecf20Sopenharmony_ci gen11_gt_irq_handler(gt, master_ctl); 25068c2ecf20Sopenharmony_ci 25078c2ecf20Sopenharmony_ci /* IRQs are synced during runtime_suspend, we don't require a wakeref */ 25088c2ecf20Sopenharmony_ci if (master_ctl & GEN11_DISPLAY_IRQ) 25098c2ecf20Sopenharmony_ci gen11_display_irq_handler(i915); 25108c2ecf20Sopenharmony_ci 25118c2ecf20Sopenharmony_ci gu_misc_iir = gen11_gu_misc_irq_ack(gt, master_ctl); 25128c2ecf20Sopenharmony_ci 25138c2ecf20Sopenharmony_ci intr_enable(regs); 25148c2ecf20Sopenharmony_ci 25158c2ecf20Sopenharmony_ci gen11_gu_misc_irq_handler(gt, gu_misc_iir); 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci return IRQ_HANDLED; 25188c2ecf20Sopenharmony_ci} 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_cistatic irqreturn_t gen11_irq_handler(int irq, void *arg) 25218c2ecf20Sopenharmony_ci{ 25228c2ecf20Sopenharmony_ci return __gen11_irq_handler(arg, 25238c2ecf20Sopenharmony_ci gen11_master_intr_disable, 25248c2ecf20Sopenharmony_ci gen11_master_intr_enable); 25258c2ecf20Sopenharmony_ci} 25268c2ecf20Sopenharmony_ci 25278c2ecf20Sopenharmony_cistatic u32 dg1_master_intr_disable_and_ack(void __iomem * const regs) 25288c2ecf20Sopenharmony_ci{ 25298c2ecf20Sopenharmony_ci u32 val; 25308c2ecf20Sopenharmony_ci 25318c2ecf20Sopenharmony_ci /* First disable interrupts */ 25328c2ecf20Sopenharmony_ci raw_reg_write(regs, DG1_MSTR_UNIT_INTR, 0); 25338c2ecf20Sopenharmony_ci 25348c2ecf20Sopenharmony_ci /* Get the indication levels and ack the master unit */ 25358c2ecf20Sopenharmony_ci val = raw_reg_read(regs, DG1_MSTR_UNIT_INTR); 25368c2ecf20Sopenharmony_ci if (unlikely(!val)) 25378c2ecf20Sopenharmony_ci return 0; 25388c2ecf20Sopenharmony_ci 25398c2ecf20Sopenharmony_ci raw_reg_write(regs, DG1_MSTR_UNIT_INTR, val); 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_ci /* 25428c2ecf20Sopenharmony_ci * Now with master disabled, get a sample of level indications 25438c2ecf20Sopenharmony_ci * for this interrupt and ack them right away - we keep GEN11_MASTER_IRQ 25448c2ecf20Sopenharmony_ci * out as this bit doesn't exist anymore for DG1 25458c2ecf20Sopenharmony_ci */ 25468c2ecf20Sopenharmony_ci val = raw_reg_read(regs, GEN11_GFX_MSTR_IRQ) & ~GEN11_MASTER_IRQ; 25478c2ecf20Sopenharmony_ci if (unlikely(!val)) 25488c2ecf20Sopenharmony_ci return 0; 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ci raw_reg_write(regs, GEN11_GFX_MSTR_IRQ, val); 25518c2ecf20Sopenharmony_ci 25528c2ecf20Sopenharmony_ci return val; 25538c2ecf20Sopenharmony_ci} 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_cistatic inline void dg1_master_intr_enable(void __iomem * const regs) 25568c2ecf20Sopenharmony_ci{ 25578c2ecf20Sopenharmony_ci raw_reg_write(regs, DG1_MSTR_UNIT_INTR, DG1_MSTR_IRQ); 25588c2ecf20Sopenharmony_ci} 25598c2ecf20Sopenharmony_ci 25608c2ecf20Sopenharmony_cistatic irqreturn_t dg1_irq_handler(int irq, void *arg) 25618c2ecf20Sopenharmony_ci{ 25628c2ecf20Sopenharmony_ci return __gen11_irq_handler(arg, 25638c2ecf20Sopenharmony_ci dg1_master_intr_disable_and_ack, 25648c2ecf20Sopenharmony_ci dg1_master_intr_enable); 25658c2ecf20Sopenharmony_ci} 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_ci/* Called from drm generic code, passed 'crtc' which 25688c2ecf20Sopenharmony_ci * we use as a pipe index 25698c2ecf20Sopenharmony_ci */ 25708c2ecf20Sopenharmony_ciint i8xx_enable_vblank(struct drm_crtc *crtc) 25718c2ecf20Sopenharmony_ci{ 25728c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->dev); 25738c2ecf20Sopenharmony_ci enum pipe pipe = to_intel_crtc(crtc)->pipe; 25748c2ecf20Sopenharmony_ci unsigned long irqflags; 25758c2ecf20Sopenharmony_ci 25768c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->irq_lock, irqflags); 25778c2ecf20Sopenharmony_ci i915_enable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_STATUS); 25788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci return 0; 25818c2ecf20Sopenharmony_ci} 25828c2ecf20Sopenharmony_ci 25838c2ecf20Sopenharmony_ciint i915gm_enable_vblank(struct drm_crtc *crtc) 25848c2ecf20Sopenharmony_ci{ 25858c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->dev); 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_ci /* 25888c2ecf20Sopenharmony_ci * Vblank interrupts fail to wake the device up from C2+. 25898c2ecf20Sopenharmony_ci * Disabling render clock gating during C-states avoids 25908c2ecf20Sopenharmony_ci * the problem. There is a small power cost so we do this 25918c2ecf20Sopenharmony_ci * only when vblank interrupts are actually enabled. 25928c2ecf20Sopenharmony_ci */ 25938c2ecf20Sopenharmony_ci if (dev_priv->vblank_enabled++ == 0) 25948c2ecf20Sopenharmony_ci I915_WRITE(SCPD0, _MASKED_BIT_ENABLE(CSTATE_RENDER_CLOCK_GATE_DISABLE)); 25958c2ecf20Sopenharmony_ci 25968c2ecf20Sopenharmony_ci return i8xx_enable_vblank(crtc); 25978c2ecf20Sopenharmony_ci} 25988c2ecf20Sopenharmony_ci 25998c2ecf20Sopenharmony_ciint i965_enable_vblank(struct drm_crtc *crtc) 26008c2ecf20Sopenharmony_ci{ 26018c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->dev); 26028c2ecf20Sopenharmony_ci enum pipe pipe = to_intel_crtc(crtc)->pipe; 26038c2ecf20Sopenharmony_ci unsigned long irqflags; 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->irq_lock, irqflags); 26068c2ecf20Sopenharmony_ci i915_enable_pipestat(dev_priv, pipe, 26078c2ecf20Sopenharmony_ci PIPE_START_VBLANK_INTERRUPT_STATUS); 26088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_ci return 0; 26118c2ecf20Sopenharmony_ci} 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_ciint ilk_enable_vblank(struct drm_crtc *crtc) 26148c2ecf20Sopenharmony_ci{ 26158c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->dev); 26168c2ecf20Sopenharmony_ci enum pipe pipe = to_intel_crtc(crtc)->pipe; 26178c2ecf20Sopenharmony_ci unsigned long irqflags; 26188c2ecf20Sopenharmony_ci u32 bit = INTEL_GEN(dev_priv) >= 7 ? 26198c2ecf20Sopenharmony_ci DE_PIPE_VBLANK_IVB(pipe) : DE_PIPE_VBLANK(pipe); 26208c2ecf20Sopenharmony_ci 26218c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->irq_lock, irqflags); 26228c2ecf20Sopenharmony_ci ilk_enable_display_irq(dev_priv, bit); 26238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_ci /* Even though there is no DMC, frame counter can get stuck when 26268c2ecf20Sopenharmony_ci * PSR is active as no frames are generated. 26278c2ecf20Sopenharmony_ci */ 26288c2ecf20Sopenharmony_ci if (HAS_PSR(dev_priv)) 26298c2ecf20Sopenharmony_ci drm_crtc_vblank_restore(crtc); 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_ci return 0; 26328c2ecf20Sopenharmony_ci} 26338c2ecf20Sopenharmony_ci 26348c2ecf20Sopenharmony_ciint bdw_enable_vblank(struct drm_crtc *crtc) 26358c2ecf20Sopenharmony_ci{ 26368c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->dev); 26378c2ecf20Sopenharmony_ci enum pipe pipe = to_intel_crtc(crtc)->pipe; 26388c2ecf20Sopenharmony_ci unsigned long irqflags; 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->irq_lock, irqflags); 26418c2ecf20Sopenharmony_ci bdw_enable_pipe_irq(dev_priv, pipe, GEN8_PIPE_VBLANK); 26428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); 26438c2ecf20Sopenharmony_ci 26448c2ecf20Sopenharmony_ci /* Even if there is no DMC, frame counter can get stuck when 26458c2ecf20Sopenharmony_ci * PSR is active as no frames are generated, so check only for PSR. 26468c2ecf20Sopenharmony_ci */ 26478c2ecf20Sopenharmony_ci if (HAS_PSR(dev_priv)) 26488c2ecf20Sopenharmony_ci drm_crtc_vblank_restore(crtc); 26498c2ecf20Sopenharmony_ci 26508c2ecf20Sopenharmony_ci return 0; 26518c2ecf20Sopenharmony_ci} 26528c2ecf20Sopenharmony_ci 26538c2ecf20Sopenharmony_ci/* Called from drm generic code, passed 'crtc' which 26548c2ecf20Sopenharmony_ci * we use as a pipe index 26558c2ecf20Sopenharmony_ci */ 26568c2ecf20Sopenharmony_civoid i8xx_disable_vblank(struct drm_crtc *crtc) 26578c2ecf20Sopenharmony_ci{ 26588c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->dev); 26598c2ecf20Sopenharmony_ci enum pipe pipe = to_intel_crtc(crtc)->pipe; 26608c2ecf20Sopenharmony_ci unsigned long irqflags; 26618c2ecf20Sopenharmony_ci 26628c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->irq_lock, irqflags); 26638c2ecf20Sopenharmony_ci i915_disable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_STATUS); 26648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); 26658c2ecf20Sopenharmony_ci} 26668c2ecf20Sopenharmony_ci 26678c2ecf20Sopenharmony_civoid i915gm_disable_vblank(struct drm_crtc *crtc) 26688c2ecf20Sopenharmony_ci{ 26698c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->dev); 26708c2ecf20Sopenharmony_ci 26718c2ecf20Sopenharmony_ci i8xx_disable_vblank(crtc); 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_ci if (--dev_priv->vblank_enabled == 0) 26748c2ecf20Sopenharmony_ci I915_WRITE(SCPD0, _MASKED_BIT_DISABLE(CSTATE_RENDER_CLOCK_GATE_DISABLE)); 26758c2ecf20Sopenharmony_ci} 26768c2ecf20Sopenharmony_ci 26778c2ecf20Sopenharmony_civoid i965_disable_vblank(struct drm_crtc *crtc) 26788c2ecf20Sopenharmony_ci{ 26798c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->dev); 26808c2ecf20Sopenharmony_ci enum pipe pipe = to_intel_crtc(crtc)->pipe; 26818c2ecf20Sopenharmony_ci unsigned long irqflags; 26828c2ecf20Sopenharmony_ci 26838c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->irq_lock, irqflags); 26848c2ecf20Sopenharmony_ci i915_disable_pipestat(dev_priv, pipe, 26858c2ecf20Sopenharmony_ci PIPE_START_VBLANK_INTERRUPT_STATUS); 26868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); 26878c2ecf20Sopenharmony_ci} 26888c2ecf20Sopenharmony_ci 26898c2ecf20Sopenharmony_civoid ilk_disable_vblank(struct drm_crtc *crtc) 26908c2ecf20Sopenharmony_ci{ 26918c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->dev); 26928c2ecf20Sopenharmony_ci enum pipe pipe = to_intel_crtc(crtc)->pipe; 26938c2ecf20Sopenharmony_ci unsigned long irqflags; 26948c2ecf20Sopenharmony_ci u32 bit = INTEL_GEN(dev_priv) >= 7 ? 26958c2ecf20Sopenharmony_ci DE_PIPE_VBLANK_IVB(pipe) : DE_PIPE_VBLANK(pipe); 26968c2ecf20Sopenharmony_ci 26978c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->irq_lock, irqflags); 26988c2ecf20Sopenharmony_ci ilk_disable_display_irq(dev_priv, bit); 26998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); 27008c2ecf20Sopenharmony_ci} 27018c2ecf20Sopenharmony_ci 27028c2ecf20Sopenharmony_civoid bdw_disable_vblank(struct drm_crtc *crtc) 27038c2ecf20Sopenharmony_ci{ 27048c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(crtc->dev); 27058c2ecf20Sopenharmony_ci enum pipe pipe = to_intel_crtc(crtc)->pipe; 27068c2ecf20Sopenharmony_ci unsigned long irqflags; 27078c2ecf20Sopenharmony_ci 27088c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_priv->irq_lock, irqflags); 27098c2ecf20Sopenharmony_ci bdw_disable_pipe_irq(dev_priv, pipe, GEN8_PIPE_VBLANK); 27108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); 27118c2ecf20Sopenharmony_ci} 27128c2ecf20Sopenharmony_ci 27138c2ecf20Sopenharmony_cistatic void ibx_irq_reset(struct drm_i915_private *dev_priv) 27148c2ecf20Sopenharmony_ci{ 27158c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 27168c2ecf20Sopenharmony_ci 27178c2ecf20Sopenharmony_ci if (HAS_PCH_NOP(dev_priv)) 27188c2ecf20Sopenharmony_ci return; 27198c2ecf20Sopenharmony_ci 27208c2ecf20Sopenharmony_ci GEN3_IRQ_RESET(uncore, SDE); 27218c2ecf20Sopenharmony_ci 27228c2ecf20Sopenharmony_ci if (HAS_PCH_CPT(dev_priv) || HAS_PCH_LPT(dev_priv)) 27238c2ecf20Sopenharmony_ci I915_WRITE(SERR_INT, 0xffffffff); 27248c2ecf20Sopenharmony_ci} 27258c2ecf20Sopenharmony_ci 27268c2ecf20Sopenharmony_ci/* 27278c2ecf20Sopenharmony_ci * SDEIER is also touched by the interrupt handler to work around missed PCH 27288c2ecf20Sopenharmony_ci * interrupts. Hence we can't update it after the interrupt handler is enabled - 27298c2ecf20Sopenharmony_ci * instead we unconditionally enable all PCH interrupt sources here, but then 27308c2ecf20Sopenharmony_ci * only unmask them as needed with SDEIMR. 27318c2ecf20Sopenharmony_ci * 27328c2ecf20Sopenharmony_ci * This function needs to be called before interrupts are enabled. 27338c2ecf20Sopenharmony_ci */ 27348c2ecf20Sopenharmony_cistatic void ibx_irq_pre_postinstall(struct drm_i915_private *dev_priv) 27358c2ecf20Sopenharmony_ci{ 27368c2ecf20Sopenharmony_ci if (HAS_PCH_NOP(dev_priv)) 27378c2ecf20Sopenharmony_ci return; 27388c2ecf20Sopenharmony_ci 27398c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, I915_READ(SDEIER) != 0); 27408c2ecf20Sopenharmony_ci I915_WRITE(SDEIER, 0xffffffff); 27418c2ecf20Sopenharmony_ci POSTING_READ(SDEIER); 27428c2ecf20Sopenharmony_ci} 27438c2ecf20Sopenharmony_ci 27448c2ecf20Sopenharmony_cistatic void vlv_display_irq_reset(struct drm_i915_private *dev_priv) 27458c2ecf20Sopenharmony_ci{ 27468c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 27478c2ecf20Sopenharmony_ci 27488c2ecf20Sopenharmony_ci if (IS_CHERRYVIEW(dev_priv)) 27498c2ecf20Sopenharmony_ci intel_uncore_write(uncore, DPINVGTT, DPINVGTT_STATUS_MASK_CHV); 27508c2ecf20Sopenharmony_ci else 27518c2ecf20Sopenharmony_ci intel_uncore_write(uncore, DPINVGTT, DPINVGTT_STATUS_MASK); 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_ci i915_hotplug_interrupt_update_locked(dev_priv, 0xffffffff, 0); 27548c2ecf20Sopenharmony_ci intel_uncore_write(uncore, PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); 27558c2ecf20Sopenharmony_ci 27568c2ecf20Sopenharmony_ci i9xx_pipestat_irq_reset(dev_priv); 27578c2ecf20Sopenharmony_ci 27588c2ecf20Sopenharmony_ci GEN3_IRQ_RESET(uncore, VLV_); 27598c2ecf20Sopenharmony_ci dev_priv->irq_mask = ~0u; 27608c2ecf20Sopenharmony_ci} 27618c2ecf20Sopenharmony_ci 27628c2ecf20Sopenharmony_cistatic void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv) 27638c2ecf20Sopenharmony_ci{ 27648c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 27658c2ecf20Sopenharmony_ci 27668c2ecf20Sopenharmony_ci u32 pipestat_mask; 27678c2ecf20Sopenharmony_ci u32 enable_mask; 27688c2ecf20Sopenharmony_ci enum pipe pipe; 27698c2ecf20Sopenharmony_ci 27708c2ecf20Sopenharmony_ci pipestat_mask = PIPE_CRC_DONE_INTERRUPT_STATUS; 27718c2ecf20Sopenharmony_ci 27728c2ecf20Sopenharmony_ci i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS); 27738c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) 27748c2ecf20Sopenharmony_ci i915_enable_pipestat(dev_priv, pipe, pipestat_mask); 27758c2ecf20Sopenharmony_ci 27768c2ecf20Sopenharmony_ci enable_mask = I915_DISPLAY_PORT_INTERRUPT | 27778c2ecf20Sopenharmony_ci I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | 27788c2ecf20Sopenharmony_ci I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | 27798c2ecf20Sopenharmony_ci I915_LPE_PIPE_A_INTERRUPT | 27808c2ecf20Sopenharmony_ci I915_LPE_PIPE_B_INTERRUPT; 27818c2ecf20Sopenharmony_ci 27828c2ecf20Sopenharmony_ci if (IS_CHERRYVIEW(dev_priv)) 27838c2ecf20Sopenharmony_ci enable_mask |= I915_DISPLAY_PIPE_C_EVENT_INTERRUPT | 27848c2ecf20Sopenharmony_ci I915_LPE_PIPE_C_INTERRUPT; 27858c2ecf20Sopenharmony_ci 27868c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, dev_priv->irq_mask != ~0u); 27878c2ecf20Sopenharmony_ci 27888c2ecf20Sopenharmony_ci dev_priv->irq_mask = ~enable_mask; 27898c2ecf20Sopenharmony_ci 27908c2ecf20Sopenharmony_ci GEN3_IRQ_INIT(uncore, VLV_, dev_priv->irq_mask, enable_mask); 27918c2ecf20Sopenharmony_ci} 27928c2ecf20Sopenharmony_ci 27938c2ecf20Sopenharmony_ci/* drm_dma.h hooks 27948c2ecf20Sopenharmony_ci*/ 27958c2ecf20Sopenharmony_cistatic void ilk_irq_reset(struct drm_i915_private *dev_priv) 27968c2ecf20Sopenharmony_ci{ 27978c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 27988c2ecf20Sopenharmony_ci 27998c2ecf20Sopenharmony_ci GEN3_IRQ_RESET(uncore, DE); 28008c2ecf20Sopenharmony_ci if (IS_GEN(dev_priv, 7)) 28018c2ecf20Sopenharmony_ci intel_uncore_write(uncore, GEN7_ERR_INT, 0xffffffff); 28028c2ecf20Sopenharmony_ci 28038c2ecf20Sopenharmony_ci if (IS_HASWELL(dev_priv)) { 28048c2ecf20Sopenharmony_ci intel_uncore_write(uncore, EDP_PSR_IMR, 0xffffffff); 28058c2ecf20Sopenharmony_ci intel_uncore_write(uncore, EDP_PSR_IIR, 0xffffffff); 28068c2ecf20Sopenharmony_ci } 28078c2ecf20Sopenharmony_ci 28088c2ecf20Sopenharmony_ci gen5_gt_irq_reset(&dev_priv->gt); 28098c2ecf20Sopenharmony_ci 28108c2ecf20Sopenharmony_ci ibx_irq_reset(dev_priv); 28118c2ecf20Sopenharmony_ci} 28128c2ecf20Sopenharmony_ci 28138c2ecf20Sopenharmony_cistatic void valleyview_irq_reset(struct drm_i915_private *dev_priv) 28148c2ecf20Sopenharmony_ci{ 28158c2ecf20Sopenharmony_ci I915_WRITE(VLV_MASTER_IER, 0); 28168c2ecf20Sopenharmony_ci POSTING_READ(VLV_MASTER_IER); 28178c2ecf20Sopenharmony_ci 28188c2ecf20Sopenharmony_ci gen5_gt_irq_reset(&dev_priv->gt); 28198c2ecf20Sopenharmony_ci 28208c2ecf20Sopenharmony_ci spin_lock_irq(&dev_priv->irq_lock); 28218c2ecf20Sopenharmony_ci if (dev_priv->display_irqs_enabled) 28228c2ecf20Sopenharmony_ci vlv_display_irq_reset(dev_priv); 28238c2ecf20Sopenharmony_ci spin_unlock_irq(&dev_priv->irq_lock); 28248c2ecf20Sopenharmony_ci} 28258c2ecf20Sopenharmony_ci 28268c2ecf20Sopenharmony_cistatic void gen8_irq_reset(struct drm_i915_private *dev_priv) 28278c2ecf20Sopenharmony_ci{ 28288c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 28298c2ecf20Sopenharmony_ci enum pipe pipe; 28308c2ecf20Sopenharmony_ci 28318c2ecf20Sopenharmony_ci gen8_master_intr_disable(dev_priv->uncore.regs); 28328c2ecf20Sopenharmony_ci 28338c2ecf20Sopenharmony_ci gen8_gt_irq_reset(&dev_priv->gt); 28348c2ecf20Sopenharmony_ci 28358c2ecf20Sopenharmony_ci intel_uncore_write(uncore, EDP_PSR_IMR, 0xffffffff); 28368c2ecf20Sopenharmony_ci intel_uncore_write(uncore, EDP_PSR_IIR, 0xffffffff); 28378c2ecf20Sopenharmony_ci 28388c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) 28398c2ecf20Sopenharmony_ci if (intel_display_power_is_enabled(dev_priv, 28408c2ecf20Sopenharmony_ci POWER_DOMAIN_PIPE(pipe))) 28418c2ecf20Sopenharmony_ci GEN8_IRQ_RESET_NDX(uncore, DE_PIPE, pipe); 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_ci GEN3_IRQ_RESET(uncore, GEN8_DE_PORT_); 28448c2ecf20Sopenharmony_ci GEN3_IRQ_RESET(uncore, GEN8_DE_MISC_); 28458c2ecf20Sopenharmony_ci GEN3_IRQ_RESET(uncore, GEN8_PCU_); 28468c2ecf20Sopenharmony_ci 28478c2ecf20Sopenharmony_ci if (HAS_PCH_SPLIT(dev_priv)) 28488c2ecf20Sopenharmony_ci ibx_irq_reset(dev_priv); 28498c2ecf20Sopenharmony_ci} 28508c2ecf20Sopenharmony_ci 28518c2ecf20Sopenharmony_cistatic void gen11_display_irq_reset(struct drm_i915_private *dev_priv) 28528c2ecf20Sopenharmony_ci{ 28538c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 28548c2ecf20Sopenharmony_ci enum pipe pipe; 28558c2ecf20Sopenharmony_ci u32 trans_mask = BIT(TRANSCODER_A) | BIT(TRANSCODER_B) | 28568c2ecf20Sopenharmony_ci BIT(TRANSCODER_C) | BIT(TRANSCODER_D); 28578c2ecf20Sopenharmony_ci 28588c2ecf20Sopenharmony_ci intel_uncore_write(uncore, GEN11_DISPLAY_INT_CTL, 0); 28598c2ecf20Sopenharmony_ci 28608c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 12) { 28618c2ecf20Sopenharmony_ci enum transcoder trans; 28628c2ecf20Sopenharmony_ci 28638c2ecf20Sopenharmony_ci for_each_cpu_transcoder_masked(dev_priv, trans, trans_mask) { 28648c2ecf20Sopenharmony_ci enum intel_display_power_domain domain; 28658c2ecf20Sopenharmony_ci 28668c2ecf20Sopenharmony_ci domain = POWER_DOMAIN_TRANSCODER(trans); 28678c2ecf20Sopenharmony_ci if (!intel_display_power_is_enabled(dev_priv, domain)) 28688c2ecf20Sopenharmony_ci continue; 28698c2ecf20Sopenharmony_ci 28708c2ecf20Sopenharmony_ci intel_uncore_write(uncore, TRANS_PSR_IMR(trans), 0xffffffff); 28718c2ecf20Sopenharmony_ci intel_uncore_write(uncore, TRANS_PSR_IIR(trans), 0xffffffff); 28728c2ecf20Sopenharmony_ci } 28738c2ecf20Sopenharmony_ci } else { 28748c2ecf20Sopenharmony_ci intel_uncore_write(uncore, EDP_PSR_IMR, 0xffffffff); 28758c2ecf20Sopenharmony_ci intel_uncore_write(uncore, EDP_PSR_IIR, 0xffffffff); 28768c2ecf20Sopenharmony_ci } 28778c2ecf20Sopenharmony_ci 28788c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) 28798c2ecf20Sopenharmony_ci if (intel_display_power_is_enabled(dev_priv, 28808c2ecf20Sopenharmony_ci POWER_DOMAIN_PIPE(pipe))) 28818c2ecf20Sopenharmony_ci GEN8_IRQ_RESET_NDX(uncore, DE_PIPE, pipe); 28828c2ecf20Sopenharmony_ci 28838c2ecf20Sopenharmony_ci GEN3_IRQ_RESET(uncore, GEN8_DE_PORT_); 28848c2ecf20Sopenharmony_ci GEN3_IRQ_RESET(uncore, GEN8_DE_MISC_); 28858c2ecf20Sopenharmony_ci GEN3_IRQ_RESET(uncore, GEN11_DE_HPD_); 28868c2ecf20Sopenharmony_ci 28878c2ecf20Sopenharmony_ci if (INTEL_PCH_TYPE(dev_priv) >= PCH_ICP) 28888c2ecf20Sopenharmony_ci GEN3_IRQ_RESET(uncore, SDE); 28898c2ecf20Sopenharmony_ci 28908c2ecf20Sopenharmony_ci /* Wa_14010685332:icl,jsl,ehl,tgl,rkl */ 28918c2ecf20Sopenharmony_ci if (INTEL_PCH_TYPE(dev_priv) >= PCH_ICP) { 28928c2ecf20Sopenharmony_ci intel_uncore_rmw(uncore, SOUTH_CHICKEN1, 28938c2ecf20Sopenharmony_ci SBCLK_RUN_REFCLK_DIS, SBCLK_RUN_REFCLK_DIS); 28948c2ecf20Sopenharmony_ci intel_uncore_rmw(uncore, SOUTH_CHICKEN1, 28958c2ecf20Sopenharmony_ci SBCLK_RUN_REFCLK_DIS, 0); 28968c2ecf20Sopenharmony_ci } 28978c2ecf20Sopenharmony_ci} 28988c2ecf20Sopenharmony_ci 28998c2ecf20Sopenharmony_cistatic void gen11_irq_reset(struct drm_i915_private *dev_priv) 29008c2ecf20Sopenharmony_ci{ 29018c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 29028c2ecf20Sopenharmony_ci 29038c2ecf20Sopenharmony_ci if (HAS_MASTER_UNIT_IRQ(dev_priv)) 29048c2ecf20Sopenharmony_ci dg1_master_intr_disable_and_ack(dev_priv->uncore.regs); 29058c2ecf20Sopenharmony_ci else 29068c2ecf20Sopenharmony_ci gen11_master_intr_disable(dev_priv->uncore.regs); 29078c2ecf20Sopenharmony_ci 29088c2ecf20Sopenharmony_ci gen11_gt_irq_reset(&dev_priv->gt); 29098c2ecf20Sopenharmony_ci gen11_display_irq_reset(dev_priv); 29108c2ecf20Sopenharmony_ci 29118c2ecf20Sopenharmony_ci GEN3_IRQ_RESET(uncore, GEN11_GU_MISC_); 29128c2ecf20Sopenharmony_ci GEN3_IRQ_RESET(uncore, GEN8_PCU_); 29138c2ecf20Sopenharmony_ci} 29148c2ecf20Sopenharmony_ci 29158c2ecf20Sopenharmony_civoid gen8_irq_power_well_post_enable(struct drm_i915_private *dev_priv, 29168c2ecf20Sopenharmony_ci u8 pipe_mask) 29178c2ecf20Sopenharmony_ci{ 29188c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 29198c2ecf20Sopenharmony_ci 29208c2ecf20Sopenharmony_ci u32 extra_ier = GEN8_PIPE_VBLANK | GEN8_PIPE_FIFO_UNDERRUN; 29218c2ecf20Sopenharmony_ci enum pipe pipe; 29228c2ecf20Sopenharmony_ci 29238c2ecf20Sopenharmony_ci spin_lock_irq(&dev_priv->irq_lock); 29248c2ecf20Sopenharmony_ci 29258c2ecf20Sopenharmony_ci if (!intel_irqs_enabled(dev_priv)) { 29268c2ecf20Sopenharmony_ci spin_unlock_irq(&dev_priv->irq_lock); 29278c2ecf20Sopenharmony_ci return; 29288c2ecf20Sopenharmony_ci } 29298c2ecf20Sopenharmony_ci 29308c2ecf20Sopenharmony_ci for_each_pipe_masked(dev_priv, pipe, pipe_mask) 29318c2ecf20Sopenharmony_ci GEN8_IRQ_INIT_NDX(uncore, DE_PIPE, pipe, 29328c2ecf20Sopenharmony_ci dev_priv->de_irq_mask[pipe], 29338c2ecf20Sopenharmony_ci ~dev_priv->de_irq_mask[pipe] | extra_ier); 29348c2ecf20Sopenharmony_ci 29358c2ecf20Sopenharmony_ci spin_unlock_irq(&dev_priv->irq_lock); 29368c2ecf20Sopenharmony_ci} 29378c2ecf20Sopenharmony_ci 29388c2ecf20Sopenharmony_civoid gen8_irq_power_well_pre_disable(struct drm_i915_private *dev_priv, 29398c2ecf20Sopenharmony_ci u8 pipe_mask) 29408c2ecf20Sopenharmony_ci{ 29418c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 29428c2ecf20Sopenharmony_ci enum pipe pipe; 29438c2ecf20Sopenharmony_ci 29448c2ecf20Sopenharmony_ci spin_lock_irq(&dev_priv->irq_lock); 29458c2ecf20Sopenharmony_ci 29468c2ecf20Sopenharmony_ci if (!intel_irqs_enabled(dev_priv)) { 29478c2ecf20Sopenharmony_ci spin_unlock_irq(&dev_priv->irq_lock); 29488c2ecf20Sopenharmony_ci return; 29498c2ecf20Sopenharmony_ci } 29508c2ecf20Sopenharmony_ci 29518c2ecf20Sopenharmony_ci for_each_pipe_masked(dev_priv, pipe, pipe_mask) 29528c2ecf20Sopenharmony_ci GEN8_IRQ_RESET_NDX(uncore, DE_PIPE, pipe); 29538c2ecf20Sopenharmony_ci 29548c2ecf20Sopenharmony_ci spin_unlock_irq(&dev_priv->irq_lock); 29558c2ecf20Sopenharmony_ci 29568c2ecf20Sopenharmony_ci /* make sure we're done processing display irqs */ 29578c2ecf20Sopenharmony_ci intel_synchronize_irq(dev_priv); 29588c2ecf20Sopenharmony_ci} 29598c2ecf20Sopenharmony_ci 29608c2ecf20Sopenharmony_cistatic void cherryview_irq_reset(struct drm_i915_private *dev_priv) 29618c2ecf20Sopenharmony_ci{ 29628c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 29638c2ecf20Sopenharmony_ci 29648c2ecf20Sopenharmony_ci I915_WRITE(GEN8_MASTER_IRQ, 0); 29658c2ecf20Sopenharmony_ci POSTING_READ(GEN8_MASTER_IRQ); 29668c2ecf20Sopenharmony_ci 29678c2ecf20Sopenharmony_ci gen8_gt_irq_reset(&dev_priv->gt); 29688c2ecf20Sopenharmony_ci 29698c2ecf20Sopenharmony_ci GEN3_IRQ_RESET(uncore, GEN8_PCU_); 29708c2ecf20Sopenharmony_ci 29718c2ecf20Sopenharmony_ci spin_lock_irq(&dev_priv->irq_lock); 29728c2ecf20Sopenharmony_ci if (dev_priv->display_irqs_enabled) 29738c2ecf20Sopenharmony_ci vlv_display_irq_reset(dev_priv); 29748c2ecf20Sopenharmony_ci spin_unlock_irq(&dev_priv->irq_lock); 29758c2ecf20Sopenharmony_ci} 29768c2ecf20Sopenharmony_ci 29778c2ecf20Sopenharmony_cistatic u32 intel_hpd_enabled_irqs(struct drm_i915_private *dev_priv, 29788c2ecf20Sopenharmony_ci const u32 hpd[HPD_NUM_PINS]) 29798c2ecf20Sopenharmony_ci{ 29808c2ecf20Sopenharmony_ci struct intel_encoder *encoder; 29818c2ecf20Sopenharmony_ci u32 enabled_irqs = 0; 29828c2ecf20Sopenharmony_ci 29838c2ecf20Sopenharmony_ci for_each_intel_encoder(&dev_priv->drm, encoder) 29848c2ecf20Sopenharmony_ci if (dev_priv->hotplug.stats[encoder->hpd_pin].state == HPD_ENABLED) 29858c2ecf20Sopenharmony_ci enabled_irqs |= hpd[encoder->hpd_pin]; 29868c2ecf20Sopenharmony_ci 29878c2ecf20Sopenharmony_ci return enabled_irqs; 29888c2ecf20Sopenharmony_ci} 29898c2ecf20Sopenharmony_ci 29908c2ecf20Sopenharmony_cistatic u32 intel_hpd_hotplug_irqs(struct drm_i915_private *dev_priv, 29918c2ecf20Sopenharmony_ci const u32 hpd[HPD_NUM_PINS]) 29928c2ecf20Sopenharmony_ci{ 29938c2ecf20Sopenharmony_ci struct intel_encoder *encoder; 29948c2ecf20Sopenharmony_ci u32 hotplug_irqs = 0; 29958c2ecf20Sopenharmony_ci 29968c2ecf20Sopenharmony_ci for_each_intel_encoder(&dev_priv->drm, encoder) 29978c2ecf20Sopenharmony_ci hotplug_irqs |= hpd[encoder->hpd_pin]; 29988c2ecf20Sopenharmony_ci 29998c2ecf20Sopenharmony_ci return hotplug_irqs; 30008c2ecf20Sopenharmony_ci} 30018c2ecf20Sopenharmony_ci 30028c2ecf20Sopenharmony_cistatic void ibx_hpd_detection_setup(struct drm_i915_private *dev_priv) 30038c2ecf20Sopenharmony_ci{ 30048c2ecf20Sopenharmony_ci u32 hotplug; 30058c2ecf20Sopenharmony_ci 30068c2ecf20Sopenharmony_ci /* 30078c2ecf20Sopenharmony_ci * Enable digital hotplug on the PCH, and configure the DP short pulse 30088c2ecf20Sopenharmony_ci * duration to 2ms (which is the minimum in the Display Port spec). 30098c2ecf20Sopenharmony_ci * The pulse duration bits are reserved on LPT+. 30108c2ecf20Sopenharmony_ci */ 30118c2ecf20Sopenharmony_ci hotplug = I915_READ(PCH_PORT_HOTPLUG); 30128c2ecf20Sopenharmony_ci hotplug &= ~(PORTB_PULSE_DURATION_MASK | 30138c2ecf20Sopenharmony_ci PORTC_PULSE_DURATION_MASK | 30148c2ecf20Sopenharmony_ci PORTD_PULSE_DURATION_MASK); 30158c2ecf20Sopenharmony_ci hotplug |= PORTB_HOTPLUG_ENABLE | PORTB_PULSE_DURATION_2ms; 30168c2ecf20Sopenharmony_ci hotplug |= PORTC_HOTPLUG_ENABLE | PORTC_PULSE_DURATION_2ms; 30178c2ecf20Sopenharmony_ci hotplug |= PORTD_HOTPLUG_ENABLE | PORTD_PULSE_DURATION_2ms; 30188c2ecf20Sopenharmony_ci /* 30198c2ecf20Sopenharmony_ci * When CPU and PCH are on the same package, port A 30208c2ecf20Sopenharmony_ci * HPD must be enabled in both north and south. 30218c2ecf20Sopenharmony_ci */ 30228c2ecf20Sopenharmony_ci if (HAS_PCH_LPT_LP(dev_priv)) 30238c2ecf20Sopenharmony_ci hotplug |= PORTA_HOTPLUG_ENABLE; 30248c2ecf20Sopenharmony_ci I915_WRITE(PCH_PORT_HOTPLUG, hotplug); 30258c2ecf20Sopenharmony_ci} 30268c2ecf20Sopenharmony_ci 30278c2ecf20Sopenharmony_cistatic void ibx_hpd_irq_setup(struct drm_i915_private *dev_priv) 30288c2ecf20Sopenharmony_ci{ 30298c2ecf20Sopenharmony_ci u32 hotplug_irqs, enabled_irqs; 30308c2ecf20Sopenharmony_ci 30318c2ecf20Sopenharmony_ci enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->hotplug.pch_hpd); 30328c2ecf20Sopenharmony_ci hotplug_irqs = intel_hpd_hotplug_irqs(dev_priv, dev_priv->hotplug.pch_hpd); 30338c2ecf20Sopenharmony_ci 30348c2ecf20Sopenharmony_ci ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs); 30358c2ecf20Sopenharmony_ci 30368c2ecf20Sopenharmony_ci ibx_hpd_detection_setup(dev_priv); 30378c2ecf20Sopenharmony_ci} 30388c2ecf20Sopenharmony_ci 30398c2ecf20Sopenharmony_cistatic void icp_ddi_hpd_detection_setup(struct drm_i915_private *dev_priv, 30408c2ecf20Sopenharmony_ci u32 enable_mask) 30418c2ecf20Sopenharmony_ci{ 30428c2ecf20Sopenharmony_ci u32 hotplug; 30438c2ecf20Sopenharmony_ci 30448c2ecf20Sopenharmony_ci hotplug = I915_READ(SHOTPLUG_CTL_DDI); 30458c2ecf20Sopenharmony_ci hotplug |= enable_mask; 30468c2ecf20Sopenharmony_ci I915_WRITE(SHOTPLUG_CTL_DDI, hotplug); 30478c2ecf20Sopenharmony_ci} 30488c2ecf20Sopenharmony_ci 30498c2ecf20Sopenharmony_cistatic void icp_tc_hpd_detection_setup(struct drm_i915_private *dev_priv, 30508c2ecf20Sopenharmony_ci u32 enable_mask) 30518c2ecf20Sopenharmony_ci{ 30528c2ecf20Sopenharmony_ci u32 hotplug; 30538c2ecf20Sopenharmony_ci 30548c2ecf20Sopenharmony_ci hotplug = I915_READ(SHOTPLUG_CTL_TC); 30558c2ecf20Sopenharmony_ci hotplug |= enable_mask; 30568c2ecf20Sopenharmony_ci I915_WRITE(SHOTPLUG_CTL_TC, hotplug); 30578c2ecf20Sopenharmony_ci} 30588c2ecf20Sopenharmony_ci 30598c2ecf20Sopenharmony_cistatic void icp_hpd_irq_setup(struct drm_i915_private *dev_priv, 30608c2ecf20Sopenharmony_ci u32 ddi_enable_mask, u32 tc_enable_mask) 30618c2ecf20Sopenharmony_ci{ 30628c2ecf20Sopenharmony_ci u32 hotplug_irqs, enabled_irqs; 30638c2ecf20Sopenharmony_ci 30648c2ecf20Sopenharmony_ci enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->hotplug.pch_hpd); 30658c2ecf20Sopenharmony_ci hotplug_irqs = intel_hpd_hotplug_irqs(dev_priv, dev_priv->hotplug.pch_hpd); 30668c2ecf20Sopenharmony_ci 30678c2ecf20Sopenharmony_ci if (INTEL_PCH_TYPE(dev_priv) <= PCH_TGP) 30688c2ecf20Sopenharmony_ci I915_WRITE(SHPD_FILTER_CNT, SHPD_FILTER_CNT_500_ADJ); 30698c2ecf20Sopenharmony_ci 30708c2ecf20Sopenharmony_ci ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs); 30718c2ecf20Sopenharmony_ci 30728c2ecf20Sopenharmony_ci icp_ddi_hpd_detection_setup(dev_priv, ddi_enable_mask); 30738c2ecf20Sopenharmony_ci if (tc_enable_mask) 30748c2ecf20Sopenharmony_ci icp_tc_hpd_detection_setup(dev_priv, tc_enable_mask); 30758c2ecf20Sopenharmony_ci} 30768c2ecf20Sopenharmony_ci 30778c2ecf20Sopenharmony_ci/* 30788c2ecf20Sopenharmony_ci * EHL doesn't need most of gen11_hpd_irq_setup, it's handling only the 30798c2ecf20Sopenharmony_ci * equivalent of SDE. 30808c2ecf20Sopenharmony_ci */ 30818c2ecf20Sopenharmony_cistatic void mcc_hpd_irq_setup(struct drm_i915_private *dev_priv) 30828c2ecf20Sopenharmony_ci{ 30838c2ecf20Sopenharmony_ci icp_hpd_irq_setup(dev_priv, 30848c2ecf20Sopenharmony_ci ICP_DDI_HPD_ENABLE_MASK, ICP_TC_HPD_ENABLE(PORT_TC1)); 30858c2ecf20Sopenharmony_ci} 30868c2ecf20Sopenharmony_ci 30878c2ecf20Sopenharmony_ci/* 30888c2ecf20Sopenharmony_ci * JSP behaves exactly the same as MCC above except that port C is mapped to 30898c2ecf20Sopenharmony_ci * the DDI-C pins instead of the TC1 pins. This means we should follow TGP's 30908c2ecf20Sopenharmony_ci * masks & tables rather than ICP's masks & tables. 30918c2ecf20Sopenharmony_ci */ 30928c2ecf20Sopenharmony_cistatic void jsp_hpd_irq_setup(struct drm_i915_private *dev_priv) 30938c2ecf20Sopenharmony_ci{ 30948c2ecf20Sopenharmony_ci icp_hpd_irq_setup(dev_priv, 30958c2ecf20Sopenharmony_ci TGP_DDI_HPD_ENABLE_MASK, 0); 30968c2ecf20Sopenharmony_ci} 30978c2ecf20Sopenharmony_ci 30988c2ecf20Sopenharmony_cistatic void gen11_hpd_detection_setup(struct drm_i915_private *dev_priv) 30998c2ecf20Sopenharmony_ci{ 31008c2ecf20Sopenharmony_ci u32 hotplug; 31018c2ecf20Sopenharmony_ci 31028c2ecf20Sopenharmony_ci hotplug = I915_READ(GEN11_TC_HOTPLUG_CTL); 31038c2ecf20Sopenharmony_ci hotplug |= GEN11_HOTPLUG_CTL_ENABLE(PORT_TC1) | 31048c2ecf20Sopenharmony_ci GEN11_HOTPLUG_CTL_ENABLE(PORT_TC2) | 31058c2ecf20Sopenharmony_ci GEN11_HOTPLUG_CTL_ENABLE(PORT_TC3) | 31068c2ecf20Sopenharmony_ci GEN11_HOTPLUG_CTL_ENABLE(PORT_TC4) | 31078c2ecf20Sopenharmony_ci GEN11_HOTPLUG_CTL_ENABLE(PORT_TC5) | 31088c2ecf20Sopenharmony_ci GEN11_HOTPLUG_CTL_ENABLE(PORT_TC6); 31098c2ecf20Sopenharmony_ci I915_WRITE(GEN11_TC_HOTPLUG_CTL, hotplug); 31108c2ecf20Sopenharmony_ci 31118c2ecf20Sopenharmony_ci hotplug = I915_READ(GEN11_TBT_HOTPLUG_CTL); 31128c2ecf20Sopenharmony_ci hotplug |= GEN11_HOTPLUG_CTL_ENABLE(PORT_TC1) | 31138c2ecf20Sopenharmony_ci GEN11_HOTPLUG_CTL_ENABLE(PORT_TC2) | 31148c2ecf20Sopenharmony_ci GEN11_HOTPLUG_CTL_ENABLE(PORT_TC3) | 31158c2ecf20Sopenharmony_ci GEN11_HOTPLUG_CTL_ENABLE(PORT_TC4) | 31168c2ecf20Sopenharmony_ci GEN11_HOTPLUG_CTL_ENABLE(PORT_TC5) | 31178c2ecf20Sopenharmony_ci GEN11_HOTPLUG_CTL_ENABLE(PORT_TC6); 31188c2ecf20Sopenharmony_ci I915_WRITE(GEN11_TBT_HOTPLUG_CTL, hotplug); 31198c2ecf20Sopenharmony_ci} 31208c2ecf20Sopenharmony_ci 31218c2ecf20Sopenharmony_cistatic void gen11_hpd_irq_setup(struct drm_i915_private *dev_priv) 31228c2ecf20Sopenharmony_ci{ 31238c2ecf20Sopenharmony_ci u32 hotplug_irqs, enabled_irqs; 31248c2ecf20Sopenharmony_ci u32 val; 31258c2ecf20Sopenharmony_ci 31268c2ecf20Sopenharmony_ci enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->hotplug.hpd); 31278c2ecf20Sopenharmony_ci hotplug_irqs = intel_hpd_hotplug_irqs(dev_priv, dev_priv->hotplug.hpd); 31288c2ecf20Sopenharmony_ci 31298c2ecf20Sopenharmony_ci val = I915_READ(GEN11_DE_HPD_IMR); 31308c2ecf20Sopenharmony_ci val &= ~hotplug_irqs; 31318c2ecf20Sopenharmony_ci val |= ~enabled_irqs & hotplug_irqs; 31328c2ecf20Sopenharmony_ci I915_WRITE(GEN11_DE_HPD_IMR, val); 31338c2ecf20Sopenharmony_ci POSTING_READ(GEN11_DE_HPD_IMR); 31348c2ecf20Sopenharmony_ci 31358c2ecf20Sopenharmony_ci gen11_hpd_detection_setup(dev_priv); 31368c2ecf20Sopenharmony_ci 31378c2ecf20Sopenharmony_ci if (INTEL_PCH_TYPE(dev_priv) >= PCH_TGP) 31388c2ecf20Sopenharmony_ci icp_hpd_irq_setup(dev_priv, 31398c2ecf20Sopenharmony_ci TGP_DDI_HPD_ENABLE_MASK, TGP_TC_HPD_ENABLE_MASK); 31408c2ecf20Sopenharmony_ci else if (INTEL_PCH_TYPE(dev_priv) >= PCH_ICP) 31418c2ecf20Sopenharmony_ci icp_hpd_irq_setup(dev_priv, 31428c2ecf20Sopenharmony_ci ICP_DDI_HPD_ENABLE_MASK, ICP_TC_HPD_ENABLE_MASK); 31438c2ecf20Sopenharmony_ci} 31448c2ecf20Sopenharmony_ci 31458c2ecf20Sopenharmony_cistatic void spt_hpd_detection_setup(struct drm_i915_private *dev_priv) 31468c2ecf20Sopenharmony_ci{ 31478c2ecf20Sopenharmony_ci u32 val, hotplug; 31488c2ecf20Sopenharmony_ci 31498c2ecf20Sopenharmony_ci /* Display WA #1179 WaHardHangonHotPlug: cnp */ 31508c2ecf20Sopenharmony_ci if (HAS_PCH_CNP(dev_priv)) { 31518c2ecf20Sopenharmony_ci val = I915_READ(SOUTH_CHICKEN1); 31528c2ecf20Sopenharmony_ci val &= ~CHASSIS_CLK_REQ_DURATION_MASK; 31538c2ecf20Sopenharmony_ci val |= CHASSIS_CLK_REQ_DURATION(0xf); 31548c2ecf20Sopenharmony_ci I915_WRITE(SOUTH_CHICKEN1, val); 31558c2ecf20Sopenharmony_ci } 31568c2ecf20Sopenharmony_ci 31578c2ecf20Sopenharmony_ci /* Enable digital hotplug on the PCH */ 31588c2ecf20Sopenharmony_ci hotplug = I915_READ(PCH_PORT_HOTPLUG); 31598c2ecf20Sopenharmony_ci hotplug |= PORTA_HOTPLUG_ENABLE | 31608c2ecf20Sopenharmony_ci PORTB_HOTPLUG_ENABLE | 31618c2ecf20Sopenharmony_ci PORTC_HOTPLUG_ENABLE | 31628c2ecf20Sopenharmony_ci PORTD_HOTPLUG_ENABLE; 31638c2ecf20Sopenharmony_ci I915_WRITE(PCH_PORT_HOTPLUG, hotplug); 31648c2ecf20Sopenharmony_ci 31658c2ecf20Sopenharmony_ci hotplug = I915_READ(PCH_PORT_HOTPLUG2); 31668c2ecf20Sopenharmony_ci hotplug |= PORTE_HOTPLUG_ENABLE; 31678c2ecf20Sopenharmony_ci I915_WRITE(PCH_PORT_HOTPLUG2, hotplug); 31688c2ecf20Sopenharmony_ci} 31698c2ecf20Sopenharmony_ci 31708c2ecf20Sopenharmony_cistatic void spt_hpd_irq_setup(struct drm_i915_private *dev_priv) 31718c2ecf20Sopenharmony_ci{ 31728c2ecf20Sopenharmony_ci u32 hotplug_irqs, enabled_irqs; 31738c2ecf20Sopenharmony_ci 31748c2ecf20Sopenharmony_ci if (INTEL_PCH_TYPE(dev_priv) >= PCH_CNP) 31758c2ecf20Sopenharmony_ci I915_WRITE(SHPD_FILTER_CNT, SHPD_FILTER_CNT_500_ADJ); 31768c2ecf20Sopenharmony_ci 31778c2ecf20Sopenharmony_ci enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->hotplug.pch_hpd); 31788c2ecf20Sopenharmony_ci hotplug_irqs = intel_hpd_hotplug_irqs(dev_priv, dev_priv->hotplug.pch_hpd); 31798c2ecf20Sopenharmony_ci 31808c2ecf20Sopenharmony_ci ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs); 31818c2ecf20Sopenharmony_ci 31828c2ecf20Sopenharmony_ci spt_hpd_detection_setup(dev_priv); 31838c2ecf20Sopenharmony_ci} 31848c2ecf20Sopenharmony_ci 31858c2ecf20Sopenharmony_cistatic void ilk_hpd_detection_setup(struct drm_i915_private *dev_priv) 31868c2ecf20Sopenharmony_ci{ 31878c2ecf20Sopenharmony_ci u32 hotplug; 31888c2ecf20Sopenharmony_ci 31898c2ecf20Sopenharmony_ci /* 31908c2ecf20Sopenharmony_ci * Enable digital hotplug on the CPU, and configure the DP short pulse 31918c2ecf20Sopenharmony_ci * duration to 2ms (which is the minimum in the Display Port spec) 31928c2ecf20Sopenharmony_ci * The pulse duration bits are reserved on HSW+. 31938c2ecf20Sopenharmony_ci */ 31948c2ecf20Sopenharmony_ci hotplug = I915_READ(DIGITAL_PORT_HOTPLUG_CNTRL); 31958c2ecf20Sopenharmony_ci hotplug &= ~DIGITAL_PORTA_PULSE_DURATION_MASK; 31968c2ecf20Sopenharmony_ci hotplug |= DIGITAL_PORTA_HOTPLUG_ENABLE | 31978c2ecf20Sopenharmony_ci DIGITAL_PORTA_PULSE_DURATION_2ms; 31988c2ecf20Sopenharmony_ci I915_WRITE(DIGITAL_PORT_HOTPLUG_CNTRL, hotplug); 31998c2ecf20Sopenharmony_ci} 32008c2ecf20Sopenharmony_ci 32018c2ecf20Sopenharmony_cistatic void ilk_hpd_irq_setup(struct drm_i915_private *dev_priv) 32028c2ecf20Sopenharmony_ci{ 32038c2ecf20Sopenharmony_ci u32 hotplug_irqs, enabled_irqs; 32048c2ecf20Sopenharmony_ci 32058c2ecf20Sopenharmony_ci enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->hotplug.hpd); 32068c2ecf20Sopenharmony_ci hotplug_irqs = intel_hpd_hotplug_irqs(dev_priv, dev_priv->hotplug.hpd); 32078c2ecf20Sopenharmony_ci 32088c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 8) 32098c2ecf20Sopenharmony_ci bdw_update_port_irq(dev_priv, hotplug_irqs, enabled_irqs); 32108c2ecf20Sopenharmony_ci else 32118c2ecf20Sopenharmony_ci ilk_update_display_irq(dev_priv, hotplug_irqs, enabled_irqs); 32128c2ecf20Sopenharmony_ci 32138c2ecf20Sopenharmony_ci ilk_hpd_detection_setup(dev_priv); 32148c2ecf20Sopenharmony_ci 32158c2ecf20Sopenharmony_ci ibx_hpd_irq_setup(dev_priv); 32168c2ecf20Sopenharmony_ci} 32178c2ecf20Sopenharmony_ci 32188c2ecf20Sopenharmony_cistatic void __bxt_hpd_detection_setup(struct drm_i915_private *dev_priv, 32198c2ecf20Sopenharmony_ci u32 enabled_irqs) 32208c2ecf20Sopenharmony_ci{ 32218c2ecf20Sopenharmony_ci u32 hotplug; 32228c2ecf20Sopenharmony_ci 32238c2ecf20Sopenharmony_ci hotplug = I915_READ(PCH_PORT_HOTPLUG); 32248c2ecf20Sopenharmony_ci hotplug |= PORTA_HOTPLUG_ENABLE | 32258c2ecf20Sopenharmony_ci PORTB_HOTPLUG_ENABLE | 32268c2ecf20Sopenharmony_ci PORTC_HOTPLUG_ENABLE; 32278c2ecf20Sopenharmony_ci 32288c2ecf20Sopenharmony_ci drm_dbg_kms(&dev_priv->drm, 32298c2ecf20Sopenharmony_ci "Invert bit setting: hp_ctl:%x hp_port:%x\n", 32308c2ecf20Sopenharmony_ci hotplug, enabled_irqs); 32318c2ecf20Sopenharmony_ci hotplug &= ~BXT_DDI_HPD_INVERT_MASK; 32328c2ecf20Sopenharmony_ci 32338c2ecf20Sopenharmony_ci /* 32348c2ecf20Sopenharmony_ci * For BXT invert bit has to be set based on AOB design 32358c2ecf20Sopenharmony_ci * for HPD detection logic, update it based on VBT fields. 32368c2ecf20Sopenharmony_ci */ 32378c2ecf20Sopenharmony_ci if ((enabled_irqs & BXT_DE_PORT_HP_DDIA) && 32388c2ecf20Sopenharmony_ci intel_bios_is_port_hpd_inverted(dev_priv, PORT_A)) 32398c2ecf20Sopenharmony_ci hotplug |= BXT_DDIA_HPD_INVERT; 32408c2ecf20Sopenharmony_ci if ((enabled_irqs & BXT_DE_PORT_HP_DDIB) && 32418c2ecf20Sopenharmony_ci intel_bios_is_port_hpd_inverted(dev_priv, PORT_B)) 32428c2ecf20Sopenharmony_ci hotplug |= BXT_DDIB_HPD_INVERT; 32438c2ecf20Sopenharmony_ci if ((enabled_irqs & BXT_DE_PORT_HP_DDIC) && 32448c2ecf20Sopenharmony_ci intel_bios_is_port_hpd_inverted(dev_priv, PORT_C)) 32458c2ecf20Sopenharmony_ci hotplug |= BXT_DDIC_HPD_INVERT; 32468c2ecf20Sopenharmony_ci 32478c2ecf20Sopenharmony_ci I915_WRITE(PCH_PORT_HOTPLUG, hotplug); 32488c2ecf20Sopenharmony_ci} 32498c2ecf20Sopenharmony_ci 32508c2ecf20Sopenharmony_cistatic void bxt_hpd_detection_setup(struct drm_i915_private *dev_priv) 32518c2ecf20Sopenharmony_ci{ 32528c2ecf20Sopenharmony_ci __bxt_hpd_detection_setup(dev_priv, BXT_DE_PORT_HOTPLUG_MASK); 32538c2ecf20Sopenharmony_ci} 32548c2ecf20Sopenharmony_ci 32558c2ecf20Sopenharmony_cistatic void bxt_hpd_irq_setup(struct drm_i915_private *dev_priv) 32568c2ecf20Sopenharmony_ci{ 32578c2ecf20Sopenharmony_ci u32 hotplug_irqs, enabled_irqs; 32588c2ecf20Sopenharmony_ci 32598c2ecf20Sopenharmony_ci enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->hotplug.hpd); 32608c2ecf20Sopenharmony_ci hotplug_irqs = intel_hpd_hotplug_irqs(dev_priv, dev_priv->hotplug.hpd); 32618c2ecf20Sopenharmony_ci 32628c2ecf20Sopenharmony_ci bdw_update_port_irq(dev_priv, hotplug_irqs, enabled_irqs); 32638c2ecf20Sopenharmony_ci 32648c2ecf20Sopenharmony_ci __bxt_hpd_detection_setup(dev_priv, enabled_irqs); 32658c2ecf20Sopenharmony_ci} 32668c2ecf20Sopenharmony_ci 32678c2ecf20Sopenharmony_cistatic void ibx_irq_postinstall(struct drm_i915_private *dev_priv) 32688c2ecf20Sopenharmony_ci{ 32698c2ecf20Sopenharmony_ci u32 mask; 32708c2ecf20Sopenharmony_ci 32718c2ecf20Sopenharmony_ci if (HAS_PCH_NOP(dev_priv)) 32728c2ecf20Sopenharmony_ci return; 32738c2ecf20Sopenharmony_ci 32748c2ecf20Sopenharmony_ci if (HAS_PCH_IBX(dev_priv)) 32758c2ecf20Sopenharmony_ci mask = SDE_GMBUS | SDE_AUX_MASK | SDE_POISON; 32768c2ecf20Sopenharmony_ci else if (HAS_PCH_CPT(dev_priv) || HAS_PCH_LPT(dev_priv)) 32778c2ecf20Sopenharmony_ci mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT; 32788c2ecf20Sopenharmony_ci else 32798c2ecf20Sopenharmony_ci mask = SDE_GMBUS_CPT; 32808c2ecf20Sopenharmony_ci 32818c2ecf20Sopenharmony_ci gen3_assert_iir_is_zero(&dev_priv->uncore, SDEIIR); 32828c2ecf20Sopenharmony_ci I915_WRITE(SDEIMR, ~mask); 32838c2ecf20Sopenharmony_ci 32848c2ecf20Sopenharmony_ci if (HAS_PCH_IBX(dev_priv) || HAS_PCH_CPT(dev_priv) || 32858c2ecf20Sopenharmony_ci HAS_PCH_LPT(dev_priv)) 32868c2ecf20Sopenharmony_ci ibx_hpd_detection_setup(dev_priv); 32878c2ecf20Sopenharmony_ci else 32888c2ecf20Sopenharmony_ci spt_hpd_detection_setup(dev_priv); 32898c2ecf20Sopenharmony_ci} 32908c2ecf20Sopenharmony_ci 32918c2ecf20Sopenharmony_cistatic void ilk_irq_postinstall(struct drm_i915_private *dev_priv) 32928c2ecf20Sopenharmony_ci{ 32938c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 32948c2ecf20Sopenharmony_ci u32 display_mask, extra_mask; 32958c2ecf20Sopenharmony_ci 32968c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 7) { 32978c2ecf20Sopenharmony_ci display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE_IVB | 32988c2ecf20Sopenharmony_ci DE_PCH_EVENT_IVB | DE_AUX_CHANNEL_A_IVB); 32998c2ecf20Sopenharmony_ci extra_mask = (DE_PIPEC_VBLANK_IVB | DE_PIPEB_VBLANK_IVB | 33008c2ecf20Sopenharmony_ci DE_PIPEA_VBLANK_IVB | DE_ERR_INT_IVB | 33018c2ecf20Sopenharmony_ci DE_DP_A_HOTPLUG_IVB); 33028c2ecf20Sopenharmony_ci } else { 33038c2ecf20Sopenharmony_ci display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT | 33048c2ecf20Sopenharmony_ci DE_AUX_CHANNEL_A | DE_PIPEB_CRC_DONE | 33058c2ecf20Sopenharmony_ci DE_PIPEA_CRC_DONE | DE_POISON); 33068c2ecf20Sopenharmony_ci extra_mask = (DE_PIPEA_VBLANK | DE_PIPEB_VBLANK | DE_PCU_EVENT | 33078c2ecf20Sopenharmony_ci DE_PIPEB_FIFO_UNDERRUN | DE_PIPEA_FIFO_UNDERRUN | 33088c2ecf20Sopenharmony_ci DE_DP_A_HOTPLUG); 33098c2ecf20Sopenharmony_ci } 33108c2ecf20Sopenharmony_ci 33118c2ecf20Sopenharmony_ci if (IS_HASWELL(dev_priv)) { 33128c2ecf20Sopenharmony_ci gen3_assert_iir_is_zero(uncore, EDP_PSR_IIR); 33138c2ecf20Sopenharmony_ci display_mask |= DE_EDP_PSR_INT_HSW; 33148c2ecf20Sopenharmony_ci } 33158c2ecf20Sopenharmony_ci 33168c2ecf20Sopenharmony_ci dev_priv->irq_mask = ~display_mask; 33178c2ecf20Sopenharmony_ci 33188c2ecf20Sopenharmony_ci ibx_irq_pre_postinstall(dev_priv); 33198c2ecf20Sopenharmony_ci 33208c2ecf20Sopenharmony_ci GEN3_IRQ_INIT(uncore, DE, dev_priv->irq_mask, 33218c2ecf20Sopenharmony_ci display_mask | extra_mask); 33228c2ecf20Sopenharmony_ci 33238c2ecf20Sopenharmony_ci gen5_gt_irq_postinstall(&dev_priv->gt); 33248c2ecf20Sopenharmony_ci 33258c2ecf20Sopenharmony_ci ilk_hpd_detection_setup(dev_priv); 33268c2ecf20Sopenharmony_ci 33278c2ecf20Sopenharmony_ci ibx_irq_postinstall(dev_priv); 33288c2ecf20Sopenharmony_ci 33298c2ecf20Sopenharmony_ci if (IS_IRONLAKE_M(dev_priv)) { 33308c2ecf20Sopenharmony_ci /* Enable PCU event interrupts 33318c2ecf20Sopenharmony_ci * 33328c2ecf20Sopenharmony_ci * spinlocking not required here for correctness since interrupt 33338c2ecf20Sopenharmony_ci * setup is guaranteed to run in single-threaded context. But we 33348c2ecf20Sopenharmony_ci * need it to make the assert_spin_locked happy. */ 33358c2ecf20Sopenharmony_ci spin_lock_irq(&dev_priv->irq_lock); 33368c2ecf20Sopenharmony_ci ilk_enable_display_irq(dev_priv, DE_PCU_EVENT); 33378c2ecf20Sopenharmony_ci spin_unlock_irq(&dev_priv->irq_lock); 33388c2ecf20Sopenharmony_ci } 33398c2ecf20Sopenharmony_ci} 33408c2ecf20Sopenharmony_ci 33418c2ecf20Sopenharmony_civoid valleyview_enable_display_irqs(struct drm_i915_private *dev_priv) 33428c2ecf20Sopenharmony_ci{ 33438c2ecf20Sopenharmony_ci lockdep_assert_held(&dev_priv->irq_lock); 33448c2ecf20Sopenharmony_ci 33458c2ecf20Sopenharmony_ci if (dev_priv->display_irqs_enabled) 33468c2ecf20Sopenharmony_ci return; 33478c2ecf20Sopenharmony_ci 33488c2ecf20Sopenharmony_ci dev_priv->display_irqs_enabled = true; 33498c2ecf20Sopenharmony_ci 33508c2ecf20Sopenharmony_ci if (intel_irqs_enabled(dev_priv)) { 33518c2ecf20Sopenharmony_ci vlv_display_irq_reset(dev_priv); 33528c2ecf20Sopenharmony_ci vlv_display_irq_postinstall(dev_priv); 33538c2ecf20Sopenharmony_ci } 33548c2ecf20Sopenharmony_ci} 33558c2ecf20Sopenharmony_ci 33568c2ecf20Sopenharmony_civoid valleyview_disable_display_irqs(struct drm_i915_private *dev_priv) 33578c2ecf20Sopenharmony_ci{ 33588c2ecf20Sopenharmony_ci lockdep_assert_held(&dev_priv->irq_lock); 33598c2ecf20Sopenharmony_ci 33608c2ecf20Sopenharmony_ci if (!dev_priv->display_irqs_enabled) 33618c2ecf20Sopenharmony_ci return; 33628c2ecf20Sopenharmony_ci 33638c2ecf20Sopenharmony_ci dev_priv->display_irqs_enabled = false; 33648c2ecf20Sopenharmony_ci 33658c2ecf20Sopenharmony_ci if (intel_irqs_enabled(dev_priv)) 33668c2ecf20Sopenharmony_ci vlv_display_irq_reset(dev_priv); 33678c2ecf20Sopenharmony_ci} 33688c2ecf20Sopenharmony_ci 33698c2ecf20Sopenharmony_ci 33708c2ecf20Sopenharmony_cistatic void valleyview_irq_postinstall(struct drm_i915_private *dev_priv) 33718c2ecf20Sopenharmony_ci{ 33728c2ecf20Sopenharmony_ci gen5_gt_irq_postinstall(&dev_priv->gt); 33738c2ecf20Sopenharmony_ci 33748c2ecf20Sopenharmony_ci spin_lock_irq(&dev_priv->irq_lock); 33758c2ecf20Sopenharmony_ci if (dev_priv->display_irqs_enabled) 33768c2ecf20Sopenharmony_ci vlv_display_irq_postinstall(dev_priv); 33778c2ecf20Sopenharmony_ci spin_unlock_irq(&dev_priv->irq_lock); 33788c2ecf20Sopenharmony_ci 33798c2ecf20Sopenharmony_ci I915_WRITE(VLV_MASTER_IER, MASTER_INTERRUPT_ENABLE); 33808c2ecf20Sopenharmony_ci POSTING_READ(VLV_MASTER_IER); 33818c2ecf20Sopenharmony_ci} 33828c2ecf20Sopenharmony_ci 33838c2ecf20Sopenharmony_cistatic void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv) 33848c2ecf20Sopenharmony_ci{ 33858c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 33868c2ecf20Sopenharmony_ci 33878c2ecf20Sopenharmony_ci u32 de_pipe_masked = gen8_de_pipe_fault_mask(dev_priv) | 33888c2ecf20Sopenharmony_ci GEN8_PIPE_CDCLK_CRC_DONE; 33898c2ecf20Sopenharmony_ci u32 de_pipe_enables; 33908c2ecf20Sopenharmony_ci u32 de_port_masked = gen8_de_port_aux_mask(dev_priv); 33918c2ecf20Sopenharmony_ci u32 de_port_enables; 33928c2ecf20Sopenharmony_ci u32 de_misc_masked = GEN8_DE_EDP_PSR; 33938c2ecf20Sopenharmony_ci u32 trans_mask = BIT(TRANSCODER_A) | BIT(TRANSCODER_B) | 33948c2ecf20Sopenharmony_ci BIT(TRANSCODER_C) | BIT(TRANSCODER_D); 33958c2ecf20Sopenharmony_ci enum pipe pipe; 33968c2ecf20Sopenharmony_ci 33978c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) <= 10) 33988c2ecf20Sopenharmony_ci de_misc_masked |= GEN8_DE_MISC_GSE; 33998c2ecf20Sopenharmony_ci 34008c2ecf20Sopenharmony_ci if (IS_GEN9_LP(dev_priv)) 34018c2ecf20Sopenharmony_ci de_port_masked |= BXT_DE_PORT_GMBUS; 34028c2ecf20Sopenharmony_ci 34038c2ecf20Sopenharmony_ci de_pipe_enables = de_pipe_masked | GEN8_PIPE_VBLANK | 34048c2ecf20Sopenharmony_ci GEN8_PIPE_FIFO_UNDERRUN; 34058c2ecf20Sopenharmony_ci 34068c2ecf20Sopenharmony_ci de_port_enables = de_port_masked; 34078c2ecf20Sopenharmony_ci if (IS_GEN9_LP(dev_priv)) 34088c2ecf20Sopenharmony_ci de_port_enables |= BXT_DE_PORT_HOTPLUG_MASK; 34098c2ecf20Sopenharmony_ci else if (IS_BROADWELL(dev_priv)) 34108c2ecf20Sopenharmony_ci de_port_enables |= GEN8_PORT_DP_A_HOTPLUG; 34118c2ecf20Sopenharmony_ci 34128c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 12) { 34138c2ecf20Sopenharmony_ci enum transcoder trans; 34148c2ecf20Sopenharmony_ci 34158c2ecf20Sopenharmony_ci for_each_cpu_transcoder_masked(dev_priv, trans, trans_mask) { 34168c2ecf20Sopenharmony_ci enum intel_display_power_domain domain; 34178c2ecf20Sopenharmony_ci 34188c2ecf20Sopenharmony_ci domain = POWER_DOMAIN_TRANSCODER(trans); 34198c2ecf20Sopenharmony_ci if (!intel_display_power_is_enabled(dev_priv, domain)) 34208c2ecf20Sopenharmony_ci continue; 34218c2ecf20Sopenharmony_ci 34228c2ecf20Sopenharmony_ci gen3_assert_iir_is_zero(uncore, TRANS_PSR_IIR(trans)); 34238c2ecf20Sopenharmony_ci } 34248c2ecf20Sopenharmony_ci } else { 34258c2ecf20Sopenharmony_ci gen3_assert_iir_is_zero(uncore, EDP_PSR_IIR); 34268c2ecf20Sopenharmony_ci } 34278c2ecf20Sopenharmony_ci 34288c2ecf20Sopenharmony_ci for_each_pipe(dev_priv, pipe) { 34298c2ecf20Sopenharmony_ci dev_priv->de_irq_mask[pipe] = ~de_pipe_masked; 34308c2ecf20Sopenharmony_ci 34318c2ecf20Sopenharmony_ci if (intel_display_power_is_enabled(dev_priv, 34328c2ecf20Sopenharmony_ci POWER_DOMAIN_PIPE(pipe))) 34338c2ecf20Sopenharmony_ci GEN8_IRQ_INIT_NDX(uncore, DE_PIPE, pipe, 34348c2ecf20Sopenharmony_ci dev_priv->de_irq_mask[pipe], 34358c2ecf20Sopenharmony_ci de_pipe_enables); 34368c2ecf20Sopenharmony_ci } 34378c2ecf20Sopenharmony_ci 34388c2ecf20Sopenharmony_ci GEN3_IRQ_INIT(uncore, GEN8_DE_PORT_, ~de_port_masked, de_port_enables); 34398c2ecf20Sopenharmony_ci GEN3_IRQ_INIT(uncore, GEN8_DE_MISC_, ~de_misc_masked, de_misc_masked); 34408c2ecf20Sopenharmony_ci 34418c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 11) { 34428c2ecf20Sopenharmony_ci u32 de_hpd_masked = 0; 34438c2ecf20Sopenharmony_ci u32 de_hpd_enables = GEN11_DE_TC_HOTPLUG_MASK | 34448c2ecf20Sopenharmony_ci GEN11_DE_TBT_HOTPLUG_MASK; 34458c2ecf20Sopenharmony_ci 34468c2ecf20Sopenharmony_ci GEN3_IRQ_INIT(uncore, GEN11_DE_HPD_, ~de_hpd_masked, 34478c2ecf20Sopenharmony_ci de_hpd_enables); 34488c2ecf20Sopenharmony_ci gen11_hpd_detection_setup(dev_priv); 34498c2ecf20Sopenharmony_ci } else if (IS_GEN9_LP(dev_priv)) { 34508c2ecf20Sopenharmony_ci bxt_hpd_detection_setup(dev_priv); 34518c2ecf20Sopenharmony_ci } else if (IS_BROADWELL(dev_priv)) { 34528c2ecf20Sopenharmony_ci ilk_hpd_detection_setup(dev_priv); 34538c2ecf20Sopenharmony_ci } 34548c2ecf20Sopenharmony_ci} 34558c2ecf20Sopenharmony_ci 34568c2ecf20Sopenharmony_cistatic void gen8_irq_postinstall(struct drm_i915_private *dev_priv) 34578c2ecf20Sopenharmony_ci{ 34588c2ecf20Sopenharmony_ci if (HAS_PCH_SPLIT(dev_priv)) 34598c2ecf20Sopenharmony_ci ibx_irq_pre_postinstall(dev_priv); 34608c2ecf20Sopenharmony_ci 34618c2ecf20Sopenharmony_ci gen8_gt_irq_postinstall(&dev_priv->gt); 34628c2ecf20Sopenharmony_ci gen8_de_irq_postinstall(dev_priv); 34638c2ecf20Sopenharmony_ci 34648c2ecf20Sopenharmony_ci if (HAS_PCH_SPLIT(dev_priv)) 34658c2ecf20Sopenharmony_ci ibx_irq_postinstall(dev_priv); 34668c2ecf20Sopenharmony_ci 34678c2ecf20Sopenharmony_ci gen8_master_intr_enable(dev_priv->uncore.regs); 34688c2ecf20Sopenharmony_ci} 34698c2ecf20Sopenharmony_ci 34708c2ecf20Sopenharmony_cistatic void icp_irq_postinstall(struct drm_i915_private *dev_priv) 34718c2ecf20Sopenharmony_ci{ 34728c2ecf20Sopenharmony_ci u32 mask = SDE_GMBUS_ICP; 34738c2ecf20Sopenharmony_ci 34748c2ecf20Sopenharmony_ci drm_WARN_ON(&dev_priv->drm, I915_READ(SDEIER) != 0); 34758c2ecf20Sopenharmony_ci I915_WRITE(SDEIER, 0xffffffff); 34768c2ecf20Sopenharmony_ci POSTING_READ(SDEIER); 34778c2ecf20Sopenharmony_ci 34788c2ecf20Sopenharmony_ci gen3_assert_iir_is_zero(&dev_priv->uncore, SDEIIR); 34798c2ecf20Sopenharmony_ci I915_WRITE(SDEIMR, ~mask); 34808c2ecf20Sopenharmony_ci 34818c2ecf20Sopenharmony_ci if (HAS_PCH_TGP(dev_priv)) { 34828c2ecf20Sopenharmony_ci icp_ddi_hpd_detection_setup(dev_priv, TGP_DDI_HPD_ENABLE_MASK); 34838c2ecf20Sopenharmony_ci icp_tc_hpd_detection_setup(dev_priv, TGP_TC_HPD_ENABLE_MASK); 34848c2ecf20Sopenharmony_ci } else if (HAS_PCH_JSP(dev_priv)) { 34858c2ecf20Sopenharmony_ci icp_ddi_hpd_detection_setup(dev_priv, TGP_DDI_HPD_ENABLE_MASK); 34868c2ecf20Sopenharmony_ci } else if (HAS_PCH_MCC(dev_priv)) { 34878c2ecf20Sopenharmony_ci icp_ddi_hpd_detection_setup(dev_priv, ICP_DDI_HPD_ENABLE_MASK); 34888c2ecf20Sopenharmony_ci icp_tc_hpd_detection_setup(dev_priv, ICP_TC_HPD_ENABLE(PORT_TC1)); 34898c2ecf20Sopenharmony_ci } else { 34908c2ecf20Sopenharmony_ci icp_ddi_hpd_detection_setup(dev_priv, ICP_DDI_HPD_ENABLE_MASK); 34918c2ecf20Sopenharmony_ci icp_tc_hpd_detection_setup(dev_priv, ICP_TC_HPD_ENABLE_MASK); 34928c2ecf20Sopenharmony_ci } 34938c2ecf20Sopenharmony_ci} 34948c2ecf20Sopenharmony_ci 34958c2ecf20Sopenharmony_cistatic void gen11_irq_postinstall(struct drm_i915_private *dev_priv) 34968c2ecf20Sopenharmony_ci{ 34978c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 34988c2ecf20Sopenharmony_ci u32 gu_misc_masked = GEN11_GU_MISC_GSE; 34998c2ecf20Sopenharmony_ci 35008c2ecf20Sopenharmony_ci if (INTEL_PCH_TYPE(dev_priv) >= PCH_ICP) 35018c2ecf20Sopenharmony_ci icp_irq_postinstall(dev_priv); 35028c2ecf20Sopenharmony_ci 35038c2ecf20Sopenharmony_ci gen11_gt_irq_postinstall(&dev_priv->gt); 35048c2ecf20Sopenharmony_ci gen8_de_irq_postinstall(dev_priv); 35058c2ecf20Sopenharmony_ci 35068c2ecf20Sopenharmony_ci GEN3_IRQ_INIT(uncore, GEN11_GU_MISC_, ~gu_misc_masked, gu_misc_masked); 35078c2ecf20Sopenharmony_ci 35088c2ecf20Sopenharmony_ci I915_WRITE(GEN11_DISPLAY_INT_CTL, GEN11_DISPLAY_IRQ_ENABLE); 35098c2ecf20Sopenharmony_ci 35108c2ecf20Sopenharmony_ci if (HAS_MASTER_UNIT_IRQ(dev_priv)) { 35118c2ecf20Sopenharmony_ci dg1_master_intr_enable(uncore->regs); 35128c2ecf20Sopenharmony_ci POSTING_READ(DG1_MSTR_UNIT_INTR); 35138c2ecf20Sopenharmony_ci } else { 35148c2ecf20Sopenharmony_ci gen11_master_intr_enable(uncore->regs); 35158c2ecf20Sopenharmony_ci POSTING_READ(GEN11_GFX_MSTR_IRQ); 35168c2ecf20Sopenharmony_ci } 35178c2ecf20Sopenharmony_ci} 35188c2ecf20Sopenharmony_ci 35198c2ecf20Sopenharmony_cistatic void cherryview_irq_postinstall(struct drm_i915_private *dev_priv) 35208c2ecf20Sopenharmony_ci{ 35218c2ecf20Sopenharmony_ci gen8_gt_irq_postinstall(&dev_priv->gt); 35228c2ecf20Sopenharmony_ci 35238c2ecf20Sopenharmony_ci spin_lock_irq(&dev_priv->irq_lock); 35248c2ecf20Sopenharmony_ci if (dev_priv->display_irqs_enabled) 35258c2ecf20Sopenharmony_ci vlv_display_irq_postinstall(dev_priv); 35268c2ecf20Sopenharmony_ci spin_unlock_irq(&dev_priv->irq_lock); 35278c2ecf20Sopenharmony_ci 35288c2ecf20Sopenharmony_ci I915_WRITE(GEN8_MASTER_IRQ, GEN8_MASTER_IRQ_CONTROL); 35298c2ecf20Sopenharmony_ci POSTING_READ(GEN8_MASTER_IRQ); 35308c2ecf20Sopenharmony_ci} 35318c2ecf20Sopenharmony_ci 35328c2ecf20Sopenharmony_cistatic void i8xx_irq_reset(struct drm_i915_private *dev_priv) 35338c2ecf20Sopenharmony_ci{ 35348c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 35358c2ecf20Sopenharmony_ci 35368c2ecf20Sopenharmony_ci i9xx_pipestat_irq_reset(dev_priv); 35378c2ecf20Sopenharmony_ci 35388c2ecf20Sopenharmony_ci GEN2_IRQ_RESET(uncore); 35398c2ecf20Sopenharmony_ci} 35408c2ecf20Sopenharmony_ci 35418c2ecf20Sopenharmony_cistatic void i8xx_irq_postinstall(struct drm_i915_private *dev_priv) 35428c2ecf20Sopenharmony_ci{ 35438c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 35448c2ecf20Sopenharmony_ci u16 enable_mask; 35458c2ecf20Sopenharmony_ci 35468c2ecf20Sopenharmony_ci intel_uncore_write16(uncore, 35478c2ecf20Sopenharmony_ci EMR, 35488c2ecf20Sopenharmony_ci ~(I915_ERROR_PAGE_TABLE | 35498c2ecf20Sopenharmony_ci I915_ERROR_MEMORY_REFRESH)); 35508c2ecf20Sopenharmony_ci 35518c2ecf20Sopenharmony_ci /* Unmask the interrupts that we always want on. */ 35528c2ecf20Sopenharmony_ci dev_priv->irq_mask = 35538c2ecf20Sopenharmony_ci ~(I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | 35548c2ecf20Sopenharmony_ci I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | 35558c2ecf20Sopenharmony_ci I915_MASTER_ERROR_INTERRUPT); 35568c2ecf20Sopenharmony_ci 35578c2ecf20Sopenharmony_ci enable_mask = 35588c2ecf20Sopenharmony_ci I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | 35598c2ecf20Sopenharmony_ci I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | 35608c2ecf20Sopenharmony_ci I915_MASTER_ERROR_INTERRUPT | 35618c2ecf20Sopenharmony_ci I915_USER_INTERRUPT; 35628c2ecf20Sopenharmony_ci 35638c2ecf20Sopenharmony_ci GEN2_IRQ_INIT(uncore, dev_priv->irq_mask, enable_mask); 35648c2ecf20Sopenharmony_ci 35658c2ecf20Sopenharmony_ci /* Interrupt setup is already guaranteed to be single-threaded, this is 35668c2ecf20Sopenharmony_ci * just to make the assert_spin_locked check happy. */ 35678c2ecf20Sopenharmony_ci spin_lock_irq(&dev_priv->irq_lock); 35688c2ecf20Sopenharmony_ci i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_INTERRUPT_STATUS); 35698c2ecf20Sopenharmony_ci i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_INTERRUPT_STATUS); 35708c2ecf20Sopenharmony_ci spin_unlock_irq(&dev_priv->irq_lock); 35718c2ecf20Sopenharmony_ci} 35728c2ecf20Sopenharmony_ci 35738c2ecf20Sopenharmony_cistatic void i8xx_error_irq_ack(struct drm_i915_private *i915, 35748c2ecf20Sopenharmony_ci u16 *eir, u16 *eir_stuck) 35758c2ecf20Sopenharmony_ci{ 35768c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &i915->uncore; 35778c2ecf20Sopenharmony_ci u16 emr; 35788c2ecf20Sopenharmony_ci 35798c2ecf20Sopenharmony_ci *eir = intel_uncore_read16(uncore, EIR); 35808c2ecf20Sopenharmony_ci 35818c2ecf20Sopenharmony_ci if (*eir) 35828c2ecf20Sopenharmony_ci intel_uncore_write16(uncore, EIR, *eir); 35838c2ecf20Sopenharmony_ci 35848c2ecf20Sopenharmony_ci *eir_stuck = intel_uncore_read16(uncore, EIR); 35858c2ecf20Sopenharmony_ci if (*eir_stuck == 0) 35868c2ecf20Sopenharmony_ci return; 35878c2ecf20Sopenharmony_ci 35888c2ecf20Sopenharmony_ci /* 35898c2ecf20Sopenharmony_ci * Toggle all EMR bits to make sure we get an edge 35908c2ecf20Sopenharmony_ci * in the ISR master error bit if we don't clear 35918c2ecf20Sopenharmony_ci * all the EIR bits. Otherwise the edge triggered 35928c2ecf20Sopenharmony_ci * IIR on i965/g4x wouldn't notice that an interrupt 35938c2ecf20Sopenharmony_ci * is still pending. Also some EIR bits can't be 35948c2ecf20Sopenharmony_ci * cleared except by handling the underlying error 35958c2ecf20Sopenharmony_ci * (or by a GPU reset) so we mask any bit that 35968c2ecf20Sopenharmony_ci * remains set. 35978c2ecf20Sopenharmony_ci */ 35988c2ecf20Sopenharmony_ci emr = intel_uncore_read16(uncore, EMR); 35998c2ecf20Sopenharmony_ci intel_uncore_write16(uncore, EMR, 0xffff); 36008c2ecf20Sopenharmony_ci intel_uncore_write16(uncore, EMR, emr | *eir_stuck); 36018c2ecf20Sopenharmony_ci} 36028c2ecf20Sopenharmony_ci 36038c2ecf20Sopenharmony_cistatic void i8xx_error_irq_handler(struct drm_i915_private *dev_priv, 36048c2ecf20Sopenharmony_ci u16 eir, u16 eir_stuck) 36058c2ecf20Sopenharmony_ci{ 36068c2ecf20Sopenharmony_ci DRM_DEBUG("Master Error: EIR 0x%04x\n", eir); 36078c2ecf20Sopenharmony_ci 36088c2ecf20Sopenharmony_ci if (eir_stuck) 36098c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, "EIR stuck: 0x%04x, masked\n", 36108c2ecf20Sopenharmony_ci eir_stuck); 36118c2ecf20Sopenharmony_ci} 36128c2ecf20Sopenharmony_ci 36138c2ecf20Sopenharmony_cistatic void i9xx_error_irq_ack(struct drm_i915_private *dev_priv, 36148c2ecf20Sopenharmony_ci u32 *eir, u32 *eir_stuck) 36158c2ecf20Sopenharmony_ci{ 36168c2ecf20Sopenharmony_ci u32 emr; 36178c2ecf20Sopenharmony_ci 36188c2ecf20Sopenharmony_ci *eir = I915_READ(EIR); 36198c2ecf20Sopenharmony_ci 36208c2ecf20Sopenharmony_ci I915_WRITE(EIR, *eir); 36218c2ecf20Sopenharmony_ci 36228c2ecf20Sopenharmony_ci *eir_stuck = I915_READ(EIR); 36238c2ecf20Sopenharmony_ci if (*eir_stuck == 0) 36248c2ecf20Sopenharmony_ci return; 36258c2ecf20Sopenharmony_ci 36268c2ecf20Sopenharmony_ci /* 36278c2ecf20Sopenharmony_ci * Toggle all EMR bits to make sure we get an edge 36288c2ecf20Sopenharmony_ci * in the ISR master error bit if we don't clear 36298c2ecf20Sopenharmony_ci * all the EIR bits. Otherwise the edge triggered 36308c2ecf20Sopenharmony_ci * IIR on i965/g4x wouldn't notice that an interrupt 36318c2ecf20Sopenharmony_ci * is still pending. Also some EIR bits can't be 36328c2ecf20Sopenharmony_ci * cleared except by handling the underlying error 36338c2ecf20Sopenharmony_ci * (or by a GPU reset) so we mask any bit that 36348c2ecf20Sopenharmony_ci * remains set. 36358c2ecf20Sopenharmony_ci */ 36368c2ecf20Sopenharmony_ci emr = I915_READ(EMR); 36378c2ecf20Sopenharmony_ci I915_WRITE(EMR, 0xffffffff); 36388c2ecf20Sopenharmony_ci I915_WRITE(EMR, emr | *eir_stuck); 36398c2ecf20Sopenharmony_ci} 36408c2ecf20Sopenharmony_ci 36418c2ecf20Sopenharmony_cistatic void i9xx_error_irq_handler(struct drm_i915_private *dev_priv, 36428c2ecf20Sopenharmony_ci u32 eir, u32 eir_stuck) 36438c2ecf20Sopenharmony_ci{ 36448c2ecf20Sopenharmony_ci DRM_DEBUG("Master Error, EIR 0x%08x\n", eir); 36458c2ecf20Sopenharmony_ci 36468c2ecf20Sopenharmony_ci if (eir_stuck) 36478c2ecf20Sopenharmony_ci drm_dbg(&dev_priv->drm, "EIR stuck: 0x%08x, masked\n", 36488c2ecf20Sopenharmony_ci eir_stuck); 36498c2ecf20Sopenharmony_ci} 36508c2ecf20Sopenharmony_ci 36518c2ecf20Sopenharmony_cistatic irqreturn_t i8xx_irq_handler(int irq, void *arg) 36528c2ecf20Sopenharmony_ci{ 36538c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = arg; 36548c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 36558c2ecf20Sopenharmony_ci 36568c2ecf20Sopenharmony_ci if (!intel_irqs_enabled(dev_priv)) 36578c2ecf20Sopenharmony_ci return IRQ_NONE; 36588c2ecf20Sopenharmony_ci 36598c2ecf20Sopenharmony_ci /* IRQs are synced during runtime_suspend, we don't require a wakeref */ 36608c2ecf20Sopenharmony_ci disable_rpm_wakeref_asserts(&dev_priv->runtime_pm); 36618c2ecf20Sopenharmony_ci 36628c2ecf20Sopenharmony_ci do { 36638c2ecf20Sopenharmony_ci u32 pipe_stats[I915_MAX_PIPES] = {}; 36648c2ecf20Sopenharmony_ci u16 eir = 0, eir_stuck = 0; 36658c2ecf20Sopenharmony_ci u16 iir; 36668c2ecf20Sopenharmony_ci 36678c2ecf20Sopenharmony_ci iir = intel_uncore_read16(&dev_priv->uncore, GEN2_IIR); 36688c2ecf20Sopenharmony_ci if (iir == 0) 36698c2ecf20Sopenharmony_ci break; 36708c2ecf20Sopenharmony_ci 36718c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 36728c2ecf20Sopenharmony_ci 36738c2ecf20Sopenharmony_ci /* Call regardless, as some status bits might not be 36748c2ecf20Sopenharmony_ci * signalled in iir */ 36758c2ecf20Sopenharmony_ci i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats); 36768c2ecf20Sopenharmony_ci 36778c2ecf20Sopenharmony_ci if (iir & I915_MASTER_ERROR_INTERRUPT) 36788c2ecf20Sopenharmony_ci i8xx_error_irq_ack(dev_priv, &eir, &eir_stuck); 36798c2ecf20Sopenharmony_ci 36808c2ecf20Sopenharmony_ci intel_uncore_write16(&dev_priv->uncore, GEN2_IIR, iir); 36818c2ecf20Sopenharmony_ci 36828c2ecf20Sopenharmony_ci if (iir & I915_USER_INTERRUPT) 36838c2ecf20Sopenharmony_ci intel_engine_signal_breadcrumbs(dev_priv->gt.engine[RCS0]); 36848c2ecf20Sopenharmony_ci 36858c2ecf20Sopenharmony_ci if (iir & I915_MASTER_ERROR_INTERRUPT) 36868c2ecf20Sopenharmony_ci i8xx_error_irq_handler(dev_priv, eir, eir_stuck); 36878c2ecf20Sopenharmony_ci 36888c2ecf20Sopenharmony_ci i8xx_pipestat_irq_handler(dev_priv, iir, pipe_stats); 36898c2ecf20Sopenharmony_ci } while (0); 36908c2ecf20Sopenharmony_ci 36918c2ecf20Sopenharmony_ci enable_rpm_wakeref_asserts(&dev_priv->runtime_pm); 36928c2ecf20Sopenharmony_ci 36938c2ecf20Sopenharmony_ci return ret; 36948c2ecf20Sopenharmony_ci} 36958c2ecf20Sopenharmony_ci 36968c2ecf20Sopenharmony_cistatic void i915_irq_reset(struct drm_i915_private *dev_priv) 36978c2ecf20Sopenharmony_ci{ 36988c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 36998c2ecf20Sopenharmony_ci 37008c2ecf20Sopenharmony_ci if (I915_HAS_HOTPLUG(dev_priv)) { 37018c2ecf20Sopenharmony_ci i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0); 37028c2ecf20Sopenharmony_ci I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); 37038c2ecf20Sopenharmony_ci } 37048c2ecf20Sopenharmony_ci 37058c2ecf20Sopenharmony_ci i9xx_pipestat_irq_reset(dev_priv); 37068c2ecf20Sopenharmony_ci 37078c2ecf20Sopenharmony_ci GEN3_IRQ_RESET(uncore, GEN2_); 37088c2ecf20Sopenharmony_ci} 37098c2ecf20Sopenharmony_ci 37108c2ecf20Sopenharmony_cistatic void i915_irq_postinstall(struct drm_i915_private *dev_priv) 37118c2ecf20Sopenharmony_ci{ 37128c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 37138c2ecf20Sopenharmony_ci u32 enable_mask; 37148c2ecf20Sopenharmony_ci 37158c2ecf20Sopenharmony_ci I915_WRITE(EMR, ~(I915_ERROR_PAGE_TABLE | 37168c2ecf20Sopenharmony_ci I915_ERROR_MEMORY_REFRESH)); 37178c2ecf20Sopenharmony_ci 37188c2ecf20Sopenharmony_ci /* Unmask the interrupts that we always want on. */ 37198c2ecf20Sopenharmony_ci dev_priv->irq_mask = 37208c2ecf20Sopenharmony_ci ~(I915_ASLE_INTERRUPT | 37218c2ecf20Sopenharmony_ci I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | 37228c2ecf20Sopenharmony_ci I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | 37238c2ecf20Sopenharmony_ci I915_MASTER_ERROR_INTERRUPT); 37248c2ecf20Sopenharmony_ci 37258c2ecf20Sopenharmony_ci enable_mask = 37268c2ecf20Sopenharmony_ci I915_ASLE_INTERRUPT | 37278c2ecf20Sopenharmony_ci I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | 37288c2ecf20Sopenharmony_ci I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | 37298c2ecf20Sopenharmony_ci I915_MASTER_ERROR_INTERRUPT | 37308c2ecf20Sopenharmony_ci I915_USER_INTERRUPT; 37318c2ecf20Sopenharmony_ci 37328c2ecf20Sopenharmony_ci if (I915_HAS_HOTPLUG(dev_priv)) { 37338c2ecf20Sopenharmony_ci /* Enable in IER... */ 37348c2ecf20Sopenharmony_ci enable_mask |= I915_DISPLAY_PORT_INTERRUPT; 37358c2ecf20Sopenharmony_ci /* and unmask in IMR */ 37368c2ecf20Sopenharmony_ci dev_priv->irq_mask &= ~I915_DISPLAY_PORT_INTERRUPT; 37378c2ecf20Sopenharmony_ci } 37388c2ecf20Sopenharmony_ci 37398c2ecf20Sopenharmony_ci GEN3_IRQ_INIT(uncore, GEN2_, dev_priv->irq_mask, enable_mask); 37408c2ecf20Sopenharmony_ci 37418c2ecf20Sopenharmony_ci /* Interrupt setup is already guaranteed to be single-threaded, this is 37428c2ecf20Sopenharmony_ci * just to make the assert_spin_locked check happy. */ 37438c2ecf20Sopenharmony_ci spin_lock_irq(&dev_priv->irq_lock); 37448c2ecf20Sopenharmony_ci i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_INTERRUPT_STATUS); 37458c2ecf20Sopenharmony_ci i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_INTERRUPT_STATUS); 37468c2ecf20Sopenharmony_ci spin_unlock_irq(&dev_priv->irq_lock); 37478c2ecf20Sopenharmony_ci 37488c2ecf20Sopenharmony_ci i915_enable_asle_pipestat(dev_priv); 37498c2ecf20Sopenharmony_ci} 37508c2ecf20Sopenharmony_ci 37518c2ecf20Sopenharmony_cistatic irqreturn_t i915_irq_handler(int irq, void *arg) 37528c2ecf20Sopenharmony_ci{ 37538c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = arg; 37548c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 37558c2ecf20Sopenharmony_ci 37568c2ecf20Sopenharmony_ci if (!intel_irqs_enabled(dev_priv)) 37578c2ecf20Sopenharmony_ci return IRQ_NONE; 37588c2ecf20Sopenharmony_ci 37598c2ecf20Sopenharmony_ci /* IRQs are synced during runtime_suspend, we don't require a wakeref */ 37608c2ecf20Sopenharmony_ci disable_rpm_wakeref_asserts(&dev_priv->runtime_pm); 37618c2ecf20Sopenharmony_ci 37628c2ecf20Sopenharmony_ci do { 37638c2ecf20Sopenharmony_ci u32 pipe_stats[I915_MAX_PIPES] = {}; 37648c2ecf20Sopenharmony_ci u32 eir = 0, eir_stuck = 0; 37658c2ecf20Sopenharmony_ci u32 hotplug_status = 0; 37668c2ecf20Sopenharmony_ci u32 iir; 37678c2ecf20Sopenharmony_ci 37688c2ecf20Sopenharmony_ci iir = I915_READ(GEN2_IIR); 37698c2ecf20Sopenharmony_ci if (iir == 0) 37708c2ecf20Sopenharmony_ci break; 37718c2ecf20Sopenharmony_ci 37728c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 37738c2ecf20Sopenharmony_ci 37748c2ecf20Sopenharmony_ci if (I915_HAS_HOTPLUG(dev_priv) && 37758c2ecf20Sopenharmony_ci iir & I915_DISPLAY_PORT_INTERRUPT) 37768c2ecf20Sopenharmony_ci hotplug_status = i9xx_hpd_irq_ack(dev_priv); 37778c2ecf20Sopenharmony_ci 37788c2ecf20Sopenharmony_ci /* Call regardless, as some status bits might not be 37798c2ecf20Sopenharmony_ci * signalled in iir */ 37808c2ecf20Sopenharmony_ci i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats); 37818c2ecf20Sopenharmony_ci 37828c2ecf20Sopenharmony_ci if (iir & I915_MASTER_ERROR_INTERRUPT) 37838c2ecf20Sopenharmony_ci i9xx_error_irq_ack(dev_priv, &eir, &eir_stuck); 37848c2ecf20Sopenharmony_ci 37858c2ecf20Sopenharmony_ci I915_WRITE(GEN2_IIR, iir); 37868c2ecf20Sopenharmony_ci 37878c2ecf20Sopenharmony_ci if (iir & I915_USER_INTERRUPT) 37888c2ecf20Sopenharmony_ci intel_engine_signal_breadcrumbs(dev_priv->gt.engine[RCS0]); 37898c2ecf20Sopenharmony_ci 37908c2ecf20Sopenharmony_ci if (iir & I915_MASTER_ERROR_INTERRUPT) 37918c2ecf20Sopenharmony_ci i9xx_error_irq_handler(dev_priv, eir, eir_stuck); 37928c2ecf20Sopenharmony_ci 37938c2ecf20Sopenharmony_ci if (hotplug_status) 37948c2ecf20Sopenharmony_ci i9xx_hpd_irq_handler(dev_priv, hotplug_status); 37958c2ecf20Sopenharmony_ci 37968c2ecf20Sopenharmony_ci i915_pipestat_irq_handler(dev_priv, iir, pipe_stats); 37978c2ecf20Sopenharmony_ci } while (0); 37988c2ecf20Sopenharmony_ci 37998c2ecf20Sopenharmony_ci enable_rpm_wakeref_asserts(&dev_priv->runtime_pm); 38008c2ecf20Sopenharmony_ci 38018c2ecf20Sopenharmony_ci return ret; 38028c2ecf20Sopenharmony_ci} 38038c2ecf20Sopenharmony_ci 38048c2ecf20Sopenharmony_cistatic void i965_irq_reset(struct drm_i915_private *dev_priv) 38058c2ecf20Sopenharmony_ci{ 38068c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 38078c2ecf20Sopenharmony_ci 38088c2ecf20Sopenharmony_ci i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0); 38098c2ecf20Sopenharmony_ci I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT)); 38108c2ecf20Sopenharmony_ci 38118c2ecf20Sopenharmony_ci i9xx_pipestat_irq_reset(dev_priv); 38128c2ecf20Sopenharmony_ci 38138c2ecf20Sopenharmony_ci GEN3_IRQ_RESET(uncore, GEN2_); 38148c2ecf20Sopenharmony_ci} 38158c2ecf20Sopenharmony_ci 38168c2ecf20Sopenharmony_cistatic void i965_irq_postinstall(struct drm_i915_private *dev_priv) 38178c2ecf20Sopenharmony_ci{ 38188c2ecf20Sopenharmony_ci struct intel_uncore *uncore = &dev_priv->uncore; 38198c2ecf20Sopenharmony_ci u32 enable_mask; 38208c2ecf20Sopenharmony_ci u32 error_mask; 38218c2ecf20Sopenharmony_ci 38228c2ecf20Sopenharmony_ci /* 38238c2ecf20Sopenharmony_ci * Enable some error detection, note the instruction error mask 38248c2ecf20Sopenharmony_ci * bit is reserved, so we leave it masked. 38258c2ecf20Sopenharmony_ci */ 38268c2ecf20Sopenharmony_ci if (IS_G4X(dev_priv)) { 38278c2ecf20Sopenharmony_ci error_mask = ~(GM45_ERROR_PAGE_TABLE | 38288c2ecf20Sopenharmony_ci GM45_ERROR_MEM_PRIV | 38298c2ecf20Sopenharmony_ci GM45_ERROR_CP_PRIV | 38308c2ecf20Sopenharmony_ci I915_ERROR_MEMORY_REFRESH); 38318c2ecf20Sopenharmony_ci } else { 38328c2ecf20Sopenharmony_ci error_mask = ~(I915_ERROR_PAGE_TABLE | 38338c2ecf20Sopenharmony_ci I915_ERROR_MEMORY_REFRESH); 38348c2ecf20Sopenharmony_ci } 38358c2ecf20Sopenharmony_ci I915_WRITE(EMR, error_mask); 38368c2ecf20Sopenharmony_ci 38378c2ecf20Sopenharmony_ci /* Unmask the interrupts that we always want on. */ 38388c2ecf20Sopenharmony_ci dev_priv->irq_mask = 38398c2ecf20Sopenharmony_ci ~(I915_ASLE_INTERRUPT | 38408c2ecf20Sopenharmony_ci I915_DISPLAY_PORT_INTERRUPT | 38418c2ecf20Sopenharmony_ci I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | 38428c2ecf20Sopenharmony_ci I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | 38438c2ecf20Sopenharmony_ci I915_MASTER_ERROR_INTERRUPT); 38448c2ecf20Sopenharmony_ci 38458c2ecf20Sopenharmony_ci enable_mask = 38468c2ecf20Sopenharmony_ci I915_ASLE_INTERRUPT | 38478c2ecf20Sopenharmony_ci I915_DISPLAY_PORT_INTERRUPT | 38488c2ecf20Sopenharmony_ci I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | 38498c2ecf20Sopenharmony_ci I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | 38508c2ecf20Sopenharmony_ci I915_MASTER_ERROR_INTERRUPT | 38518c2ecf20Sopenharmony_ci I915_USER_INTERRUPT; 38528c2ecf20Sopenharmony_ci 38538c2ecf20Sopenharmony_ci if (IS_G4X(dev_priv)) 38548c2ecf20Sopenharmony_ci enable_mask |= I915_BSD_USER_INTERRUPT; 38558c2ecf20Sopenharmony_ci 38568c2ecf20Sopenharmony_ci GEN3_IRQ_INIT(uncore, GEN2_, dev_priv->irq_mask, enable_mask); 38578c2ecf20Sopenharmony_ci 38588c2ecf20Sopenharmony_ci /* Interrupt setup is already guaranteed to be single-threaded, this is 38598c2ecf20Sopenharmony_ci * just to make the assert_spin_locked check happy. */ 38608c2ecf20Sopenharmony_ci spin_lock_irq(&dev_priv->irq_lock); 38618c2ecf20Sopenharmony_ci i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS); 38628c2ecf20Sopenharmony_ci i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_INTERRUPT_STATUS); 38638c2ecf20Sopenharmony_ci i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_INTERRUPT_STATUS); 38648c2ecf20Sopenharmony_ci spin_unlock_irq(&dev_priv->irq_lock); 38658c2ecf20Sopenharmony_ci 38668c2ecf20Sopenharmony_ci i915_enable_asle_pipestat(dev_priv); 38678c2ecf20Sopenharmony_ci} 38688c2ecf20Sopenharmony_ci 38698c2ecf20Sopenharmony_cistatic void i915_hpd_irq_setup(struct drm_i915_private *dev_priv) 38708c2ecf20Sopenharmony_ci{ 38718c2ecf20Sopenharmony_ci u32 hotplug_en; 38728c2ecf20Sopenharmony_ci 38738c2ecf20Sopenharmony_ci lockdep_assert_held(&dev_priv->irq_lock); 38748c2ecf20Sopenharmony_ci 38758c2ecf20Sopenharmony_ci /* Note HDMI and DP share hotplug bits */ 38768c2ecf20Sopenharmony_ci /* enable bits are the same for all generations */ 38778c2ecf20Sopenharmony_ci hotplug_en = intel_hpd_enabled_irqs(dev_priv, hpd_mask_i915); 38788c2ecf20Sopenharmony_ci /* Programming the CRT detection parameters tends 38798c2ecf20Sopenharmony_ci to generate a spurious hotplug event about three 38808c2ecf20Sopenharmony_ci seconds later. So just do it once. 38818c2ecf20Sopenharmony_ci */ 38828c2ecf20Sopenharmony_ci if (IS_G4X(dev_priv)) 38838c2ecf20Sopenharmony_ci hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64; 38848c2ecf20Sopenharmony_ci hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50; 38858c2ecf20Sopenharmony_ci 38868c2ecf20Sopenharmony_ci /* Ignore TV since it's buggy */ 38878c2ecf20Sopenharmony_ci i915_hotplug_interrupt_update_locked(dev_priv, 38888c2ecf20Sopenharmony_ci HOTPLUG_INT_EN_MASK | 38898c2ecf20Sopenharmony_ci CRT_HOTPLUG_VOLTAGE_COMPARE_MASK | 38908c2ecf20Sopenharmony_ci CRT_HOTPLUG_ACTIVATION_PERIOD_64, 38918c2ecf20Sopenharmony_ci hotplug_en); 38928c2ecf20Sopenharmony_ci} 38938c2ecf20Sopenharmony_ci 38948c2ecf20Sopenharmony_cistatic irqreturn_t i965_irq_handler(int irq, void *arg) 38958c2ecf20Sopenharmony_ci{ 38968c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = arg; 38978c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 38988c2ecf20Sopenharmony_ci 38998c2ecf20Sopenharmony_ci if (!intel_irqs_enabled(dev_priv)) 39008c2ecf20Sopenharmony_ci return IRQ_NONE; 39018c2ecf20Sopenharmony_ci 39028c2ecf20Sopenharmony_ci /* IRQs are synced during runtime_suspend, we don't require a wakeref */ 39038c2ecf20Sopenharmony_ci disable_rpm_wakeref_asserts(&dev_priv->runtime_pm); 39048c2ecf20Sopenharmony_ci 39058c2ecf20Sopenharmony_ci do { 39068c2ecf20Sopenharmony_ci u32 pipe_stats[I915_MAX_PIPES] = {}; 39078c2ecf20Sopenharmony_ci u32 eir = 0, eir_stuck = 0; 39088c2ecf20Sopenharmony_ci u32 hotplug_status = 0; 39098c2ecf20Sopenharmony_ci u32 iir; 39108c2ecf20Sopenharmony_ci 39118c2ecf20Sopenharmony_ci iir = I915_READ(GEN2_IIR); 39128c2ecf20Sopenharmony_ci if (iir == 0) 39138c2ecf20Sopenharmony_ci break; 39148c2ecf20Sopenharmony_ci 39158c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 39168c2ecf20Sopenharmony_ci 39178c2ecf20Sopenharmony_ci if (iir & I915_DISPLAY_PORT_INTERRUPT) 39188c2ecf20Sopenharmony_ci hotplug_status = i9xx_hpd_irq_ack(dev_priv); 39198c2ecf20Sopenharmony_ci 39208c2ecf20Sopenharmony_ci /* Call regardless, as some status bits might not be 39218c2ecf20Sopenharmony_ci * signalled in iir */ 39228c2ecf20Sopenharmony_ci i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats); 39238c2ecf20Sopenharmony_ci 39248c2ecf20Sopenharmony_ci if (iir & I915_MASTER_ERROR_INTERRUPT) 39258c2ecf20Sopenharmony_ci i9xx_error_irq_ack(dev_priv, &eir, &eir_stuck); 39268c2ecf20Sopenharmony_ci 39278c2ecf20Sopenharmony_ci I915_WRITE(GEN2_IIR, iir); 39288c2ecf20Sopenharmony_ci 39298c2ecf20Sopenharmony_ci if (iir & I915_USER_INTERRUPT) 39308c2ecf20Sopenharmony_ci intel_engine_signal_breadcrumbs(dev_priv->gt.engine[RCS0]); 39318c2ecf20Sopenharmony_ci 39328c2ecf20Sopenharmony_ci if (iir & I915_BSD_USER_INTERRUPT) 39338c2ecf20Sopenharmony_ci intel_engine_signal_breadcrumbs(dev_priv->gt.engine[VCS0]); 39348c2ecf20Sopenharmony_ci 39358c2ecf20Sopenharmony_ci if (iir & I915_MASTER_ERROR_INTERRUPT) 39368c2ecf20Sopenharmony_ci i9xx_error_irq_handler(dev_priv, eir, eir_stuck); 39378c2ecf20Sopenharmony_ci 39388c2ecf20Sopenharmony_ci if (hotplug_status) 39398c2ecf20Sopenharmony_ci i9xx_hpd_irq_handler(dev_priv, hotplug_status); 39408c2ecf20Sopenharmony_ci 39418c2ecf20Sopenharmony_ci i965_pipestat_irq_handler(dev_priv, iir, pipe_stats); 39428c2ecf20Sopenharmony_ci } while (0); 39438c2ecf20Sopenharmony_ci 39448c2ecf20Sopenharmony_ci enable_rpm_wakeref_asserts(&dev_priv->runtime_pm); 39458c2ecf20Sopenharmony_ci 39468c2ecf20Sopenharmony_ci return ret; 39478c2ecf20Sopenharmony_ci} 39488c2ecf20Sopenharmony_ci 39498c2ecf20Sopenharmony_ci/** 39508c2ecf20Sopenharmony_ci * intel_irq_init - initializes irq support 39518c2ecf20Sopenharmony_ci * @dev_priv: i915 device instance 39528c2ecf20Sopenharmony_ci * 39538c2ecf20Sopenharmony_ci * This function initializes all the irq support including work items, timers 39548c2ecf20Sopenharmony_ci * and all the vtables. It does not setup the interrupt itself though. 39558c2ecf20Sopenharmony_ci */ 39568c2ecf20Sopenharmony_civoid intel_irq_init(struct drm_i915_private *dev_priv) 39578c2ecf20Sopenharmony_ci{ 39588c2ecf20Sopenharmony_ci struct drm_device *dev = &dev_priv->drm; 39598c2ecf20Sopenharmony_ci int i; 39608c2ecf20Sopenharmony_ci 39618c2ecf20Sopenharmony_ci intel_hpd_init_pins(dev_priv); 39628c2ecf20Sopenharmony_ci 39638c2ecf20Sopenharmony_ci intel_hpd_init_work(dev_priv); 39648c2ecf20Sopenharmony_ci 39658c2ecf20Sopenharmony_ci INIT_WORK(&dev_priv->l3_parity.error_work, ivb_parity_work); 39668c2ecf20Sopenharmony_ci for (i = 0; i < MAX_L3_SLICES; ++i) 39678c2ecf20Sopenharmony_ci dev_priv->l3_parity.remap_info[i] = NULL; 39688c2ecf20Sopenharmony_ci 39698c2ecf20Sopenharmony_ci /* pre-gen11 the guc irqs bits are in the upper 16 bits of the pm reg */ 39708c2ecf20Sopenharmony_ci if (HAS_GT_UC(dev_priv) && INTEL_GEN(dev_priv) < 11) 39718c2ecf20Sopenharmony_ci dev_priv->gt.pm_guc_events = GUC_INTR_GUC2HOST << 16; 39728c2ecf20Sopenharmony_ci 39738c2ecf20Sopenharmony_ci dev->vblank_disable_immediate = true; 39748c2ecf20Sopenharmony_ci 39758c2ecf20Sopenharmony_ci /* Most platforms treat the display irq block as an always-on 39768c2ecf20Sopenharmony_ci * power domain. vlv/chv can disable it at runtime and need 39778c2ecf20Sopenharmony_ci * special care to avoid writing any of the display block registers 39788c2ecf20Sopenharmony_ci * outside of the power domain. We defer setting up the display irqs 39798c2ecf20Sopenharmony_ci * in this case to the runtime pm. 39808c2ecf20Sopenharmony_ci */ 39818c2ecf20Sopenharmony_ci dev_priv->display_irqs_enabled = true; 39828c2ecf20Sopenharmony_ci if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) 39838c2ecf20Sopenharmony_ci dev_priv->display_irqs_enabled = false; 39848c2ecf20Sopenharmony_ci 39858c2ecf20Sopenharmony_ci dev_priv->hotplug.hpd_storm_threshold = HPD_STORM_DEFAULT_THRESHOLD; 39868c2ecf20Sopenharmony_ci /* If we have MST support, we want to avoid doing short HPD IRQ storm 39878c2ecf20Sopenharmony_ci * detection, as short HPD storms will occur as a natural part of 39888c2ecf20Sopenharmony_ci * sideband messaging with MST. 39898c2ecf20Sopenharmony_ci * On older platforms however, IRQ storms can occur with both long and 39908c2ecf20Sopenharmony_ci * short pulses, as seen on some G4x systems. 39918c2ecf20Sopenharmony_ci */ 39928c2ecf20Sopenharmony_ci dev_priv->hotplug.hpd_short_storm_enabled = !HAS_DP_MST(dev_priv); 39938c2ecf20Sopenharmony_ci 39948c2ecf20Sopenharmony_ci if (HAS_GMCH(dev_priv)) { 39958c2ecf20Sopenharmony_ci if (I915_HAS_HOTPLUG(dev_priv)) 39968c2ecf20Sopenharmony_ci dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup; 39978c2ecf20Sopenharmony_ci } else { 39988c2ecf20Sopenharmony_ci if (HAS_PCH_JSP(dev_priv)) 39998c2ecf20Sopenharmony_ci dev_priv->display.hpd_irq_setup = jsp_hpd_irq_setup; 40008c2ecf20Sopenharmony_ci else if (HAS_PCH_MCC(dev_priv)) 40018c2ecf20Sopenharmony_ci dev_priv->display.hpd_irq_setup = mcc_hpd_irq_setup; 40028c2ecf20Sopenharmony_ci else if (INTEL_GEN(dev_priv) >= 11) 40038c2ecf20Sopenharmony_ci dev_priv->display.hpd_irq_setup = gen11_hpd_irq_setup; 40048c2ecf20Sopenharmony_ci else if (IS_GEN9_LP(dev_priv)) 40058c2ecf20Sopenharmony_ci dev_priv->display.hpd_irq_setup = bxt_hpd_irq_setup; 40068c2ecf20Sopenharmony_ci else if (INTEL_PCH_TYPE(dev_priv) >= PCH_SPT) 40078c2ecf20Sopenharmony_ci dev_priv->display.hpd_irq_setup = spt_hpd_irq_setup; 40088c2ecf20Sopenharmony_ci else 40098c2ecf20Sopenharmony_ci dev_priv->display.hpd_irq_setup = ilk_hpd_irq_setup; 40108c2ecf20Sopenharmony_ci } 40118c2ecf20Sopenharmony_ci} 40128c2ecf20Sopenharmony_ci 40138c2ecf20Sopenharmony_ci/** 40148c2ecf20Sopenharmony_ci * intel_irq_fini - deinitializes IRQ support 40158c2ecf20Sopenharmony_ci * @i915: i915 device instance 40168c2ecf20Sopenharmony_ci * 40178c2ecf20Sopenharmony_ci * This function deinitializes all the IRQ support. 40188c2ecf20Sopenharmony_ci */ 40198c2ecf20Sopenharmony_civoid intel_irq_fini(struct drm_i915_private *i915) 40208c2ecf20Sopenharmony_ci{ 40218c2ecf20Sopenharmony_ci int i; 40228c2ecf20Sopenharmony_ci 40238c2ecf20Sopenharmony_ci for (i = 0; i < MAX_L3_SLICES; ++i) 40248c2ecf20Sopenharmony_ci kfree(i915->l3_parity.remap_info[i]); 40258c2ecf20Sopenharmony_ci} 40268c2ecf20Sopenharmony_ci 40278c2ecf20Sopenharmony_cistatic irq_handler_t intel_irq_handler(struct drm_i915_private *dev_priv) 40288c2ecf20Sopenharmony_ci{ 40298c2ecf20Sopenharmony_ci if (HAS_GMCH(dev_priv)) { 40308c2ecf20Sopenharmony_ci if (IS_CHERRYVIEW(dev_priv)) 40318c2ecf20Sopenharmony_ci return cherryview_irq_handler; 40328c2ecf20Sopenharmony_ci else if (IS_VALLEYVIEW(dev_priv)) 40338c2ecf20Sopenharmony_ci return valleyview_irq_handler; 40348c2ecf20Sopenharmony_ci else if (IS_GEN(dev_priv, 4)) 40358c2ecf20Sopenharmony_ci return i965_irq_handler; 40368c2ecf20Sopenharmony_ci else if (IS_GEN(dev_priv, 3)) 40378c2ecf20Sopenharmony_ci return i915_irq_handler; 40388c2ecf20Sopenharmony_ci else 40398c2ecf20Sopenharmony_ci return i8xx_irq_handler; 40408c2ecf20Sopenharmony_ci } else { 40418c2ecf20Sopenharmony_ci if (HAS_MASTER_UNIT_IRQ(dev_priv)) 40428c2ecf20Sopenharmony_ci return dg1_irq_handler; 40438c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 11) 40448c2ecf20Sopenharmony_ci return gen11_irq_handler; 40458c2ecf20Sopenharmony_ci else if (INTEL_GEN(dev_priv) >= 8) 40468c2ecf20Sopenharmony_ci return gen8_irq_handler; 40478c2ecf20Sopenharmony_ci else 40488c2ecf20Sopenharmony_ci return ilk_irq_handler; 40498c2ecf20Sopenharmony_ci } 40508c2ecf20Sopenharmony_ci} 40518c2ecf20Sopenharmony_ci 40528c2ecf20Sopenharmony_cistatic void intel_irq_reset(struct drm_i915_private *dev_priv) 40538c2ecf20Sopenharmony_ci{ 40548c2ecf20Sopenharmony_ci if (HAS_GMCH(dev_priv)) { 40558c2ecf20Sopenharmony_ci if (IS_CHERRYVIEW(dev_priv)) 40568c2ecf20Sopenharmony_ci cherryview_irq_reset(dev_priv); 40578c2ecf20Sopenharmony_ci else if (IS_VALLEYVIEW(dev_priv)) 40588c2ecf20Sopenharmony_ci valleyview_irq_reset(dev_priv); 40598c2ecf20Sopenharmony_ci else if (IS_GEN(dev_priv, 4)) 40608c2ecf20Sopenharmony_ci i965_irq_reset(dev_priv); 40618c2ecf20Sopenharmony_ci else if (IS_GEN(dev_priv, 3)) 40628c2ecf20Sopenharmony_ci i915_irq_reset(dev_priv); 40638c2ecf20Sopenharmony_ci else 40648c2ecf20Sopenharmony_ci i8xx_irq_reset(dev_priv); 40658c2ecf20Sopenharmony_ci } else { 40668c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 11) 40678c2ecf20Sopenharmony_ci gen11_irq_reset(dev_priv); 40688c2ecf20Sopenharmony_ci else if (INTEL_GEN(dev_priv) >= 8) 40698c2ecf20Sopenharmony_ci gen8_irq_reset(dev_priv); 40708c2ecf20Sopenharmony_ci else 40718c2ecf20Sopenharmony_ci ilk_irq_reset(dev_priv); 40728c2ecf20Sopenharmony_ci } 40738c2ecf20Sopenharmony_ci} 40748c2ecf20Sopenharmony_ci 40758c2ecf20Sopenharmony_cistatic void intel_irq_postinstall(struct drm_i915_private *dev_priv) 40768c2ecf20Sopenharmony_ci{ 40778c2ecf20Sopenharmony_ci if (HAS_GMCH(dev_priv)) { 40788c2ecf20Sopenharmony_ci if (IS_CHERRYVIEW(dev_priv)) 40798c2ecf20Sopenharmony_ci cherryview_irq_postinstall(dev_priv); 40808c2ecf20Sopenharmony_ci else if (IS_VALLEYVIEW(dev_priv)) 40818c2ecf20Sopenharmony_ci valleyview_irq_postinstall(dev_priv); 40828c2ecf20Sopenharmony_ci else if (IS_GEN(dev_priv, 4)) 40838c2ecf20Sopenharmony_ci i965_irq_postinstall(dev_priv); 40848c2ecf20Sopenharmony_ci else if (IS_GEN(dev_priv, 3)) 40858c2ecf20Sopenharmony_ci i915_irq_postinstall(dev_priv); 40868c2ecf20Sopenharmony_ci else 40878c2ecf20Sopenharmony_ci i8xx_irq_postinstall(dev_priv); 40888c2ecf20Sopenharmony_ci } else { 40898c2ecf20Sopenharmony_ci if (INTEL_GEN(dev_priv) >= 11) 40908c2ecf20Sopenharmony_ci gen11_irq_postinstall(dev_priv); 40918c2ecf20Sopenharmony_ci else if (INTEL_GEN(dev_priv) >= 8) 40928c2ecf20Sopenharmony_ci gen8_irq_postinstall(dev_priv); 40938c2ecf20Sopenharmony_ci else 40948c2ecf20Sopenharmony_ci ilk_irq_postinstall(dev_priv); 40958c2ecf20Sopenharmony_ci } 40968c2ecf20Sopenharmony_ci} 40978c2ecf20Sopenharmony_ci 40988c2ecf20Sopenharmony_ci/** 40998c2ecf20Sopenharmony_ci * intel_irq_install - enables the hardware interrupt 41008c2ecf20Sopenharmony_ci * @dev_priv: i915 device instance 41018c2ecf20Sopenharmony_ci * 41028c2ecf20Sopenharmony_ci * This function enables the hardware interrupt handling, but leaves the hotplug 41038c2ecf20Sopenharmony_ci * handling still disabled. It is called after intel_irq_init(). 41048c2ecf20Sopenharmony_ci * 41058c2ecf20Sopenharmony_ci * In the driver load and resume code we need working interrupts in a few places 41068c2ecf20Sopenharmony_ci * but don't want to deal with the hassle of concurrent probe and hotplug 41078c2ecf20Sopenharmony_ci * workers. Hence the split into this two-stage approach. 41088c2ecf20Sopenharmony_ci */ 41098c2ecf20Sopenharmony_ciint intel_irq_install(struct drm_i915_private *dev_priv) 41108c2ecf20Sopenharmony_ci{ 41118c2ecf20Sopenharmony_ci int irq = dev_priv->drm.pdev->irq; 41128c2ecf20Sopenharmony_ci int ret; 41138c2ecf20Sopenharmony_ci 41148c2ecf20Sopenharmony_ci /* 41158c2ecf20Sopenharmony_ci * We enable some interrupt sources in our postinstall hooks, so mark 41168c2ecf20Sopenharmony_ci * interrupts as enabled _before_ actually enabling them to avoid 41178c2ecf20Sopenharmony_ci * special cases in our ordering checks. 41188c2ecf20Sopenharmony_ci */ 41198c2ecf20Sopenharmony_ci dev_priv->runtime_pm.irqs_enabled = true; 41208c2ecf20Sopenharmony_ci 41218c2ecf20Sopenharmony_ci dev_priv->drm.irq_enabled = true; 41228c2ecf20Sopenharmony_ci 41238c2ecf20Sopenharmony_ci intel_irq_reset(dev_priv); 41248c2ecf20Sopenharmony_ci 41258c2ecf20Sopenharmony_ci ret = request_irq(irq, intel_irq_handler(dev_priv), 41268c2ecf20Sopenharmony_ci IRQF_SHARED, DRIVER_NAME, dev_priv); 41278c2ecf20Sopenharmony_ci if (ret < 0) { 41288c2ecf20Sopenharmony_ci dev_priv->drm.irq_enabled = false; 41298c2ecf20Sopenharmony_ci return ret; 41308c2ecf20Sopenharmony_ci } 41318c2ecf20Sopenharmony_ci 41328c2ecf20Sopenharmony_ci intel_irq_postinstall(dev_priv); 41338c2ecf20Sopenharmony_ci 41348c2ecf20Sopenharmony_ci return ret; 41358c2ecf20Sopenharmony_ci} 41368c2ecf20Sopenharmony_ci 41378c2ecf20Sopenharmony_ci/** 41388c2ecf20Sopenharmony_ci * intel_irq_uninstall - finilizes all irq handling 41398c2ecf20Sopenharmony_ci * @dev_priv: i915 device instance 41408c2ecf20Sopenharmony_ci * 41418c2ecf20Sopenharmony_ci * This stops interrupt and hotplug handling and unregisters and frees all 41428c2ecf20Sopenharmony_ci * resources acquired in the init functions. 41438c2ecf20Sopenharmony_ci */ 41448c2ecf20Sopenharmony_civoid intel_irq_uninstall(struct drm_i915_private *dev_priv) 41458c2ecf20Sopenharmony_ci{ 41468c2ecf20Sopenharmony_ci int irq = dev_priv->drm.pdev->irq; 41478c2ecf20Sopenharmony_ci 41488c2ecf20Sopenharmony_ci /* 41498c2ecf20Sopenharmony_ci * FIXME we can get called twice during driver probe 41508c2ecf20Sopenharmony_ci * error handling as well as during driver remove due to 41518c2ecf20Sopenharmony_ci * intel_modeset_driver_remove() calling us out of sequence. 41528c2ecf20Sopenharmony_ci * Would be nice if it didn't do that... 41538c2ecf20Sopenharmony_ci */ 41548c2ecf20Sopenharmony_ci if (!dev_priv->drm.irq_enabled) 41558c2ecf20Sopenharmony_ci return; 41568c2ecf20Sopenharmony_ci 41578c2ecf20Sopenharmony_ci dev_priv->drm.irq_enabled = false; 41588c2ecf20Sopenharmony_ci 41598c2ecf20Sopenharmony_ci intel_irq_reset(dev_priv); 41608c2ecf20Sopenharmony_ci 41618c2ecf20Sopenharmony_ci free_irq(irq, dev_priv); 41628c2ecf20Sopenharmony_ci 41638c2ecf20Sopenharmony_ci intel_hpd_cancel_work(dev_priv); 41648c2ecf20Sopenharmony_ci dev_priv->runtime_pm.irqs_enabled = false; 41658c2ecf20Sopenharmony_ci} 41668c2ecf20Sopenharmony_ci 41678c2ecf20Sopenharmony_ci/** 41688c2ecf20Sopenharmony_ci * intel_runtime_pm_disable_interrupts - runtime interrupt disabling 41698c2ecf20Sopenharmony_ci * @dev_priv: i915 device instance 41708c2ecf20Sopenharmony_ci * 41718c2ecf20Sopenharmony_ci * This function is used to disable interrupts at runtime, both in the runtime 41728c2ecf20Sopenharmony_ci * pm and the system suspend/resume code. 41738c2ecf20Sopenharmony_ci */ 41748c2ecf20Sopenharmony_civoid intel_runtime_pm_disable_interrupts(struct drm_i915_private *dev_priv) 41758c2ecf20Sopenharmony_ci{ 41768c2ecf20Sopenharmony_ci intel_irq_reset(dev_priv); 41778c2ecf20Sopenharmony_ci dev_priv->runtime_pm.irqs_enabled = false; 41788c2ecf20Sopenharmony_ci intel_synchronize_irq(dev_priv); 41798c2ecf20Sopenharmony_ci} 41808c2ecf20Sopenharmony_ci 41818c2ecf20Sopenharmony_ci/** 41828c2ecf20Sopenharmony_ci * intel_runtime_pm_enable_interrupts - runtime interrupt enabling 41838c2ecf20Sopenharmony_ci * @dev_priv: i915 device instance 41848c2ecf20Sopenharmony_ci * 41858c2ecf20Sopenharmony_ci * This function is used to enable interrupts at runtime, both in the runtime 41868c2ecf20Sopenharmony_ci * pm and the system suspend/resume code. 41878c2ecf20Sopenharmony_ci */ 41888c2ecf20Sopenharmony_civoid intel_runtime_pm_enable_interrupts(struct drm_i915_private *dev_priv) 41898c2ecf20Sopenharmony_ci{ 41908c2ecf20Sopenharmony_ci dev_priv->runtime_pm.irqs_enabled = true; 41918c2ecf20Sopenharmony_ci intel_irq_reset(dev_priv); 41928c2ecf20Sopenharmony_ci intel_irq_postinstall(dev_priv); 41938c2ecf20Sopenharmony_ci} 41948c2ecf20Sopenharmony_ci 41958c2ecf20Sopenharmony_cibool intel_irqs_enabled(struct drm_i915_private *dev_priv) 41968c2ecf20Sopenharmony_ci{ 41978c2ecf20Sopenharmony_ci /* 41988c2ecf20Sopenharmony_ci * We only use drm_irq_uninstall() at unload and VT switch, so 41998c2ecf20Sopenharmony_ci * this is the only thing we need to check. 42008c2ecf20Sopenharmony_ci */ 42018c2ecf20Sopenharmony_ci return dev_priv->runtime_pm.irqs_enabled; 42028c2ecf20Sopenharmony_ci} 42038c2ecf20Sopenharmony_ci 42048c2ecf20Sopenharmony_civoid intel_synchronize_irq(struct drm_i915_private *i915) 42058c2ecf20Sopenharmony_ci{ 42068c2ecf20Sopenharmony_ci synchronize_irq(i915->drm.pdev->irq); 42078c2ecf20Sopenharmony_ci} 4208