// SPDX-License-Identifier: MIT /* * Copyright © 2023 Intel Corporation */ #include "i915_drv.h" #include "i915_reg.h" #include "intel_de.h" #include "intel_display_irq.h" #include "intel_display_types.h" #include "intel_dp_aux.h" #include "intel_gmbus.h" #include "intel_hotplug.h" #include "intel_hotplug_irq.h" typedef bool (*long_pulse_detect_func)(enum hpd_pin pin, u32 val); typedef u32 (*hotplug_enables_func)(struct intel_encoder *encoder); typedef u32 (*hotplug_mask_func)(enum hpd_pin pin); static const u32 hpd_ilk[HPD_NUM_PINS] = { [HPD_PORT_A] = DE_DP_A_HOTPLUG, }; static const u32 hpd_ivb[HPD_NUM_PINS] = { [HPD_PORT_A] = DE_DP_A_HOTPLUG_IVB, }; static const u32 hpd_bdw[HPD_NUM_PINS] = { [HPD_PORT_A] = GEN8_DE_PORT_HOTPLUG(HPD_PORT_A), }; static const u32 hpd_ibx[HPD_NUM_PINS] = { [HPD_CRT] = SDE_CRT_HOTPLUG, [HPD_SDVO_B] = SDE_SDVOB_HOTPLUG, [HPD_PORT_B] = SDE_PORTB_HOTPLUG, [HPD_PORT_C] = SDE_PORTC_HOTPLUG, [HPD_PORT_D] = SDE_PORTD_HOTPLUG, }; static const u32 hpd_cpt[HPD_NUM_PINS] = { [HPD_CRT] = SDE_CRT_HOTPLUG_CPT, [HPD_SDVO_B] = SDE_SDVOB_HOTPLUG_CPT, [HPD_PORT_B] = SDE_PORTB_HOTPLUG_CPT, [HPD_PORT_C] = SDE_PORTC_HOTPLUG_CPT, [HPD_PORT_D] = SDE_PORTD_HOTPLUG_CPT, }; static const u32 hpd_spt[HPD_NUM_PINS] = { [HPD_PORT_A] = SDE_PORTA_HOTPLUG_SPT, [HPD_PORT_B] = SDE_PORTB_HOTPLUG_CPT, [HPD_PORT_C] = SDE_PORTC_HOTPLUG_CPT, [HPD_PORT_D] = SDE_PORTD_HOTPLUG_CPT, [HPD_PORT_E] = SDE_PORTE_HOTPLUG_SPT, }; static const u32 hpd_mask_i915[HPD_NUM_PINS] = { [HPD_CRT] = CRT_HOTPLUG_INT_EN, [HPD_SDVO_B] = SDVOB_HOTPLUG_INT_EN, [HPD_SDVO_C] = SDVOC_HOTPLUG_INT_EN, [HPD_PORT_B] = PORTB_HOTPLUG_INT_EN, [HPD_PORT_C] = PORTC_HOTPLUG_INT_EN, [HPD_PORT_D] = PORTD_HOTPLUG_INT_EN, }; static const u32 hpd_status_g4x[HPD_NUM_PINS] = { [HPD_CRT] = CRT_HOTPLUG_INT_STATUS, [HPD_SDVO_B] = SDVOB_HOTPLUG_INT_STATUS_G4X, [HPD_SDVO_C] = SDVOC_HOTPLUG_INT_STATUS_G4X, [HPD_PORT_B] = PORTB_HOTPLUG_INT_STATUS, [HPD_PORT_C] = PORTC_HOTPLUG_INT_STATUS, [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS, }; static const u32 hpd_status_i915[HPD_NUM_PINS] = { [HPD_CRT] = CRT_HOTPLUG_INT_STATUS, [HPD_SDVO_B] = SDVOB_HOTPLUG_INT_STATUS_I915, [HPD_SDVO_C] = SDVOC_HOTPLUG_INT_STATUS_I915, [HPD_PORT_B] = PORTB_HOTPLUG_INT_STATUS, [HPD_PORT_C] = PORTC_HOTPLUG_INT_STATUS, [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS, }; static const u32 hpd_bxt[HPD_NUM_PINS] = { [HPD_PORT_A] = GEN8_DE_PORT_HOTPLUG(HPD_PORT_A), [HPD_PORT_B] = GEN8_DE_PORT_HOTPLUG(HPD_PORT_B), [HPD_PORT_C] = GEN8_DE_PORT_HOTPLUG(HPD_PORT_C), }; static const u32 hpd_gen11[HPD_NUM_PINS] = { [HPD_PORT_TC1] = GEN11_TC_HOTPLUG(HPD_PORT_TC1) | GEN11_TBT_HOTPLUG(HPD_PORT_TC1), [HPD_PORT_TC2] = GEN11_TC_HOTPLUG(HPD_PORT_TC2) | GEN11_TBT_HOTPLUG(HPD_PORT_TC2), [HPD_PORT_TC3] = GEN11_TC_HOTPLUG(HPD_PORT_TC3) | GEN11_TBT_HOTPLUG(HPD_PORT_TC3), [HPD_PORT_TC4] = GEN11_TC_HOTPLUG(HPD_PORT_TC4) | GEN11_TBT_HOTPLUG(HPD_PORT_TC4), [HPD_PORT_TC5] = GEN11_TC_HOTPLUG(HPD_PORT_TC5) | GEN11_TBT_HOTPLUG(HPD_PORT_TC5), [HPD_PORT_TC6] = GEN11_TC_HOTPLUG(HPD_PORT_TC6) | GEN11_TBT_HOTPLUG(HPD_PORT_TC6), }; static const u32 hpd_xelpdp[HPD_NUM_PINS] = { [HPD_PORT_TC1] = XELPDP_TBT_HOTPLUG(HPD_PORT_TC1) | XELPDP_DP_ALT_HOTPLUG(HPD_PORT_TC1), [HPD_PORT_TC2] = XELPDP_TBT_HOTPLUG(HPD_PORT_TC2) | XELPDP_DP_ALT_HOTPLUG(HPD_PORT_TC2), [HPD_PORT_TC3] = XELPDP_TBT_HOTPLUG(HPD_PORT_TC3) | XELPDP_DP_ALT_HOTPLUG(HPD_PORT_TC3), [HPD_PORT_TC4] = XELPDP_TBT_HOTPLUG(HPD_PORT_TC4) | XELPDP_DP_ALT_HOTPLUG(HPD_PORT_TC4), }; static const u32 hpd_icp[HPD_NUM_PINS] = { [HPD_PORT_A] = SDE_DDI_HOTPLUG_ICP(HPD_PORT_A), [HPD_PORT_B] = SDE_DDI_HOTPLUG_ICP(HPD_PORT_B), [HPD_PORT_C] = SDE_DDI_HOTPLUG_ICP(HPD_PORT_C), [HPD_PORT_TC1] = SDE_TC_HOTPLUG_ICP(HPD_PORT_TC1), [HPD_PORT_TC2] = SDE_TC_HOTPLUG_ICP(HPD_PORT_TC2), [HPD_PORT_TC3] = SDE_TC_HOTPLUG_ICP(HPD_PORT_TC3), [HPD_PORT_TC4] = SDE_TC_HOTPLUG_ICP(HPD_PORT_TC4), [HPD_PORT_TC5] = SDE_TC_HOTPLUG_ICP(HPD_PORT_TC5), [HPD_PORT_TC6] = SDE_TC_HOTPLUG_ICP(HPD_PORT_TC6), }; static const u32 hpd_sde_dg1[HPD_NUM_PINS] = { [HPD_PORT_A] = SDE_DDI_HOTPLUG_ICP(HPD_PORT_A), [HPD_PORT_B] = SDE_DDI_HOTPLUG_ICP(HPD_PORT_B), [HPD_PORT_C] = SDE_DDI_HOTPLUG_ICP(HPD_PORT_C), [HPD_PORT_D] = SDE_DDI_HOTPLUG_ICP(HPD_PORT_D), [HPD_PORT_TC1] = SDE_TC_HOTPLUG_DG2(HPD_PORT_TC1), }; static const u32 hpd_mtp[HPD_NUM_PINS] = { [HPD_PORT_A] = SDE_DDI_HOTPLUG_ICP(HPD_PORT_A), [HPD_PORT_B] = SDE_DDI_HOTPLUG_ICP(HPD_PORT_B), [HPD_PORT_TC1] = SDE_TC_HOTPLUG_ICP(HPD_PORT_TC1), [HPD_PORT_TC2] = SDE_TC_HOTPLUG_ICP(HPD_PORT_TC2), [HPD_PORT_TC3] = SDE_TC_HOTPLUG_ICP(HPD_PORT_TC3), [HPD_PORT_TC4] = SDE_TC_HOTPLUG_ICP(HPD_PORT_TC4), }; static void intel_hpd_init_pins(struct drm_i915_private *dev_priv) { struct intel_hotplug *hpd = &dev_priv->display.hotplug; if (HAS_GMCH(dev_priv)) { if (IS_G4X(dev_priv) || IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) hpd->hpd = hpd_status_g4x; else hpd->hpd = hpd_status_i915; return; } if (DISPLAY_VER(dev_priv) >= 14) hpd->hpd = hpd_xelpdp; else if (DISPLAY_VER(dev_priv) >= 11) hpd->hpd = hpd_gen11; else if (IS_GEMINILAKE(dev_priv) || IS_BROXTON(dev_priv)) hpd->hpd = hpd_bxt; else if (DISPLAY_VER(dev_priv) == 9) hpd->hpd = NULL; /* no north HPD on SKL */ else if (DISPLAY_VER(dev_priv) >= 8) hpd->hpd = hpd_bdw; else if (DISPLAY_VER(dev_priv) >= 7) hpd->hpd = hpd_ivb; else hpd->hpd = hpd_ilk; if ((INTEL_PCH_TYPE(dev_priv) < PCH_DG1) && (!HAS_PCH_SPLIT(dev_priv) || HAS_PCH_NOP(dev_priv))) return; if (INTEL_PCH_TYPE(dev_priv) >= PCH_MTL) hpd->pch_hpd = hpd_mtp; else if (INTEL_PCH_TYPE(dev_priv) >= PCH_DG1) hpd->pch_hpd = hpd_sde_dg1; else if (INTEL_PCH_TYPE(dev_priv) >= PCH_ICP) hpd->pch_hpd = hpd_icp; else if (HAS_PCH_CNP(dev_priv) || HAS_PCH_SPT(dev_priv)) hpd->pch_hpd = hpd_spt; else if (HAS_PCH_LPT(dev_priv) || HAS_PCH_CPT(dev_priv)) hpd->pch_hpd = hpd_cpt; else if (HAS_PCH_IBX(dev_priv)) hpd->pch_hpd = hpd_ibx; else MISSING_CASE(INTEL_PCH_TYPE(dev_priv)); } /* For display hotplug interrupt */ void i915_hotplug_interrupt_update_locked(struct drm_i915_private *dev_priv, u32 mask, u32 bits) { lockdep_assert_held(&dev_priv->irq_lock); drm_WARN_ON(&dev_priv->drm, bits & ~mask); intel_uncore_rmw(&dev_priv->uncore, PORT_HOTPLUG_EN(dev_priv), mask, bits); } /** * i915_hotplug_interrupt_update - update hotplug interrupt enable * @dev_priv: driver private * @mask: bits to update * @bits: bits to enable * NOTE: the HPD enable bits are modified both inside and outside * of an interrupt context. To avoid that read-modify-write cycles * interfer, these bits are protected by a spinlock. Since this * function is usually not called from a context where the lock is * held already, this function acquires the lock itself. A non-locking * version is also available. */ void i915_hotplug_interrupt_update(struct drm_i915_private *dev_priv, u32 mask, u32 bits) { spin_lock_irq(&dev_priv->irq_lock); i915_hotplug_interrupt_update_locked(dev_priv, mask, bits); spin_unlock_irq(&dev_priv->irq_lock); } static bool gen11_port_hotplug_long_detect(enum hpd_pin pin, u32 val) { switch (pin) { case HPD_PORT_TC1: case HPD_PORT_TC2: case HPD_PORT_TC3: case HPD_PORT_TC4: case HPD_PORT_TC5: case HPD_PORT_TC6: return val & GEN11_HOTPLUG_CTL_LONG_DETECT(pin); default: return false; } } static bool bxt_port_hotplug_long_detect(enum hpd_pin pin, u32 val) { switch (pin) { case HPD_PORT_A: return val & PORTA_HOTPLUG_LONG_DETECT; case HPD_PORT_B: return val & PORTB_HOTPLUG_LONG_DETECT; case HPD_PORT_C: return val & PORTC_HOTPLUG_LONG_DETECT; default: return false; } } static bool icp_ddi_port_hotplug_long_detect(enum hpd_pin pin, u32 val) { switch (pin) { case HPD_PORT_A: case HPD_PORT_B: case HPD_PORT_C: case HPD_PORT_D: return val & SHOTPLUG_CTL_DDI_HPD_LONG_DETECT(pin); default: return false; } } static bool icp_tc_port_hotplug_long_detect(enum hpd_pin pin, u32 val) { switch (pin) { case HPD_PORT_TC1: case HPD_PORT_TC2: case HPD_PORT_TC3: case HPD_PORT_TC4: case HPD_PORT_TC5: case HPD_PORT_TC6: return val & ICP_TC_HPD_LONG_DETECT(pin); default: return false; } } static bool spt_port_hotplug2_long_detect(enum hpd_pin pin, u32 val) { switch (pin) { case HPD_PORT_E: return val & PORTE_HOTPLUG_LONG_DETECT; default: return false; } } static bool spt_port_hotplug_long_detect(enum hpd_pin pin, u32 val) { switch (pin) { case HPD_PORT_A: return val & PORTA_HOTPLUG_LONG_DETECT; case HPD_PORT_B: return val & PORTB_HOTPLUG_LONG_DETECT; case HPD_PORT_C: return val & PORTC_HOTPLUG_LONG_DETECT; case HPD_PORT_D: return val & PORTD_HOTPLUG_LONG_DETECT; default: return false; } } static bool ilk_port_hotplug_long_detect(enum hpd_pin pin, u32 val) { switch (pin) { case HPD_PORT_A: return val & DIGITAL_PORTA_HOTPLUG_LONG_DETECT; default: return false; } } static bool pch_port_hotplug_long_detect(enum hpd_pin pin, u32 val) { switch (pin) { case HPD_PORT_B: return val & PORTB_HOTPLUG_LONG_DETECT; case HPD_PORT_C: return val & PORTC_HOTPLUG_LONG_DETECT; case HPD_PORT_D: return val & PORTD_HOTPLUG_LONG_DETECT; default: return false; } } static bool i9xx_port_hotplug_long_detect(enum hpd_pin pin, u32 val) { switch (pin) { case HPD_PORT_B: return val & PORTB_HOTPLUG_INT_LONG_PULSE; case HPD_PORT_C: return val & PORTC_HOTPLUG_INT_LONG_PULSE; case HPD_PORT_D: return val & PORTD_HOTPLUG_INT_LONG_PULSE; default: return false; } } /* * Get a bit mask of pins that have triggered, and which ones may be long. * This can be called multiple times with the same masks to accumulate * hotplug detection results from several registers. * * Note that the caller is expected to zero out the masks initially. */ static void intel_get_hpd_pins(struct drm_i915_private *dev_priv, u32 *pin_mask, u32 *long_mask, u32 hotplug_trigger, u32 dig_hotplug_reg, const u32 hpd[HPD_NUM_PINS], bool long_pulse_detect(enum hpd_pin pin, u32 val)) { enum hpd_pin pin; BUILD_BUG_ON(BITS_PER_TYPE(*pin_mask) < HPD_NUM_PINS); for_each_hpd_pin(pin) { if ((hpd[pin] & hotplug_trigger) == 0) continue; *pin_mask |= BIT(pin); if (long_pulse_detect(pin, dig_hotplug_reg)) *long_mask |= BIT(pin); } drm_dbg(&dev_priv->drm, "hotplug event received, stat 0x%08x, dig 0x%08x, pins 0x%08x, long 0x%08x\n", hotplug_trigger, dig_hotplug_reg, *pin_mask, *long_mask); } static u32 intel_hpd_enabled_irqs(struct drm_i915_private *dev_priv, const u32 hpd[HPD_NUM_PINS]) { struct intel_encoder *encoder; u32 enabled_irqs = 0; for_each_intel_encoder(&dev_priv->drm, encoder) if (dev_priv->display.hotplug.stats[encoder->hpd_pin].state == HPD_ENABLED) enabled_irqs |= hpd[encoder->hpd_pin]; return enabled_irqs; } static u32 intel_hpd_hotplug_irqs(struct drm_i915_private *dev_priv, const u32 hpd[HPD_NUM_PINS]) { struct intel_encoder *encoder; u32 hotplug_irqs = 0; for_each_intel_encoder(&dev_priv->drm, encoder) hotplug_irqs |= hpd[encoder->hpd_pin]; return hotplug_irqs; } static u32 intel_hpd_hotplug_mask(struct drm_i915_private *i915, hotplug_mask_func hotplug_mask) { enum hpd_pin pin; u32 hotplug = 0; for_each_hpd_pin(pin) hotplug |= hotplug_mask(pin); return hotplug; } static u32 intel_hpd_hotplug_enables(struct drm_i915_private *i915, hotplug_enables_func hotplug_enables) { struct intel_encoder *encoder; u32 hotplug = 0; for_each_intel_encoder(&i915->drm, encoder) hotplug |= hotplug_enables(encoder); return hotplug; } u32 i9xx_hpd_irq_ack(struct drm_i915_private *dev_priv) { u32 hotplug_status = 0, hotplug_status_mask; int i; if (IS_G4X(dev_priv) || IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) hotplug_status_mask = HOTPLUG_INT_STATUS_G4X | DP_AUX_CHANNEL_MASK_INT_STATUS_G4X; else hotplug_status_mask = HOTPLUG_INT_STATUS_I915; /* * We absolutely have to clear all the pending interrupt * bits in PORT_HOTPLUG_STAT. Otherwise the ISR port * interrupt bit won't have an edge, and the i965/g4x * edge triggered IIR will not notice that an interrupt * is still pending. We can't use PORT_HOTPLUG_EN to * guarantee the edge as the act of toggling the enable * bits can itself generate a new hotplug interrupt :( */ for (i = 0; i < 10; i++) { u32 tmp = intel_uncore_read(&dev_priv->uncore, PORT_HOTPLUG_STAT(dev_priv)) & hotplug_status_mask; if (tmp == 0) return hotplug_status; hotplug_status |= tmp; intel_uncore_write(&dev_priv->uncore, PORT_HOTPLUG_STAT(dev_priv), hotplug_status); } drm_WARN_ONCE(&dev_priv->drm, 1, "PORT_HOTPLUG_STAT did not clear (0x%08x)\n", intel_uncore_read(&dev_priv->uncore, PORT_HOTPLUG_STAT(dev_priv))); return hotplug_status; } void i9xx_hpd_irq_handler(struct drm_i915_private *dev_priv, u32 hotplug_status) { struct intel_display *display = &dev_priv->display; u32 pin_mask = 0, long_mask = 0; u32 hotplug_trigger; if (IS_G4X(dev_priv) || IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_G4X; else hotplug_trigger = hotplug_status & HOTPLUG_INT_STATUS_I915; if (hotplug_trigger) { intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, hotplug_trigger, hotplug_trigger, dev_priv->display.hotplug.hpd, i9xx_port_hotplug_long_detect); intel_hpd_irq_handler(dev_priv, pin_mask, long_mask); } if ((IS_G4X(dev_priv) || IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) && hotplug_status & DP_AUX_CHANNEL_MASK_INT_STATUS_G4X) intel_dp_aux_irq_handler(display); } void ibx_hpd_irq_handler(struct drm_i915_private *dev_priv, u32 hotplug_trigger) { u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; /* * Somehow the PCH doesn't seem to really ack the interrupt to the CPU * unless we touch the hotplug register, even if hotplug_trigger is * zero. Not acking leads to "The master control interrupt lied (SDE)!" * errors. */ dig_hotplug_reg = intel_uncore_read(&dev_priv->uncore, PCH_PORT_HOTPLUG); if (!hotplug_trigger) { u32 mask = PORTA_HOTPLUG_STATUS_MASK | PORTD_HOTPLUG_STATUS_MASK | PORTC_HOTPLUG_STATUS_MASK | PORTB_HOTPLUG_STATUS_MASK; dig_hotplug_reg &= ~mask; } intel_uncore_write(&dev_priv->uncore, PCH_PORT_HOTPLUG, dig_hotplug_reg); if (!hotplug_trigger) return; intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, hotplug_trigger, dig_hotplug_reg, dev_priv->display.hotplug.pch_hpd, pch_port_hotplug_long_detect); intel_hpd_irq_handler(dev_priv, pin_mask, long_mask); } void xelpdp_pica_irq_handler(struct drm_i915_private *i915, u32 iir) { struct intel_display *display = &i915->display; enum hpd_pin pin; u32 hotplug_trigger = iir & (XELPDP_DP_ALT_HOTPLUG_MASK | XELPDP_TBT_HOTPLUG_MASK); u32 trigger_aux = iir & XELPDP_AUX_TC_MASK; u32 pin_mask = 0, long_mask = 0; if (DISPLAY_VER(i915) >= 20) trigger_aux |= iir & XE2LPD_AUX_DDI_MASK; for (pin = HPD_PORT_TC1; pin <= HPD_PORT_TC4; pin++) { u32 val; if (!(i915->display.hotplug.hpd[pin] & hotplug_trigger)) continue; pin_mask |= BIT(pin); val = intel_de_read(i915, XELPDP_PORT_HOTPLUG_CTL(pin)); intel_de_write(i915, XELPDP_PORT_HOTPLUG_CTL(pin), val); if (val & (XELPDP_DP_ALT_HPD_LONG_DETECT | XELPDP_TBT_HPD_LONG_DETECT)) long_mask |= BIT(pin); } if (pin_mask) { drm_dbg(&i915->drm, "pica hotplug event received, stat 0x%08x, pins 0x%08x, long 0x%08x\n", hotplug_trigger, pin_mask, long_mask); intel_hpd_irq_handler(i915, pin_mask, long_mask); } if (trigger_aux) intel_dp_aux_irq_handler(display); if (!pin_mask && !trigger_aux) drm_err(&i915->drm, "Unexpected DE HPD/AUX interrupt 0x%08x\n", iir); } void icp_irq_handler(struct drm_i915_private *dev_priv, u32 pch_iir) { u32 ddi_hotplug_trigger = pch_iir & SDE_DDI_HOTPLUG_MASK_ICP; u32 tc_hotplug_trigger = pch_iir & SDE_TC_HOTPLUG_MASK_ICP; u32 pin_mask = 0, long_mask = 0; if (ddi_hotplug_trigger) { u32 dig_hotplug_reg; /* Locking due to DSI native GPIO sequences */ spin_lock(&dev_priv->irq_lock); dig_hotplug_reg = intel_uncore_rmw(&dev_priv->uncore, SHOTPLUG_CTL_DDI, 0, 0); spin_unlock(&dev_priv->irq_lock); intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, ddi_hotplug_trigger, dig_hotplug_reg, dev_priv->display.hotplug.pch_hpd, icp_ddi_port_hotplug_long_detect); } if (tc_hotplug_trigger) { u32 dig_hotplug_reg; dig_hotplug_reg = intel_uncore_rmw(&dev_priv->uncore, SHOTPLUG_CTL_TC, 0, 0); intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, tc_hotplug_trigger, dig_hotplug_reg, dev_priv->display.hotplug.pch_hpd, icp_tc_port_hotplug_long_detect); } if (pin_mask) intel_hpd_irq_handler(dev_priv, pin_mask, long_mask); if (pch_iir & SDE_GMBUS_ICP) intel_gmbus_irq_handler(dev_priv); } void spt_irq_handler(struct drm_i915_private *dev_priv, u32 pch_iir) { u32 hotplug_trigger = pch_iir & SDE_HOTPLUG_MASK_SPT & ~SDE_PORTE_HOTPLUG_SPT; u32 hotplug2_trigger = pch_iir & SDE_PORTE_HOTPLUG_SPT; u32 pin_mask = 0, long_mask = 0; if (hotplug_trigger) { u32 dig_hotplug_reg; dig_hotplug_reg = intel_uncore_rmw(&dev_priv->uncore, PCH_PORT_HOTPLUG, 0, 0); intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, hotplug_trigger, dig_hotplug_reg, dev_priv->display.hotplug.pch_hpd, spt_port_hotplug_long_detect); } if (hotplug2_trigger) { u32 dig_hotplug_reg; dig_hotplug_reg = intel_uncore_rmw(&dev_priv->uncore, PCH_PORT_HOTPLUG2, 0, 0); intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, hotplug2_trigger, dig_hotplug_reg, dev_priv->display.hotplug.pch_hpd, spt_port_hotplug2_long_detect); } if (pin_mask) intel_hpd_irq_handler(dev_priv, pin_mask, long_mask); if (pch_iir & SDE_GMBUS_CPT) intel_gmbus_irq_handler(dev_priv); } void ilk_hpd_irq_handler(struct drm_i915_private *dev_priv, u32 hotplug_trigger) { u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; dig_hotplug_reg = intel_uncore_rmw(&dev_priv->uncore, DIGITAL_PORT_HOTPLUG_CNTRL, 0, 0); intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, hotplug_trigger, dig_hotplug_reg, dev_priv->display.hotplug.hpd, ilk_port_hotplug_long_detect); intel_hpd_irq_handler(dev_priv, pin_mask, long_mask); } void bxt_hpd_irq_handler(struct drm_i915_private *dev_priv, u32 hotplug_trigger) { u32 dig_hotplug_reg, pin_mask = 0, long_mask = 0; dig_hotplug_reg = intel_uncore_rmw(&dev_priv->uncore, PCH_PORT_HOTPLUG, 0, 0); intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, hotplug_trigger, dig_hotplug_reg, dev_priv->display.hotplug.hpd, bxt_port_hotplug_long_detect); intel_hpd_irq_handler(dev_priv, pin_mask, long_mask); } void gen11_hpd_irq_handler(struct drm_i915_private *dev_priv, u32 iir) { u32 pin_mask = 0, long_mask = 0; u32 trigger_tc = iir & GEN11_DE_TC_HOTPLUG_MASK; u32 trigger_tbt = iir & GEN11_DE_TBT_HOTPLUG_MASK; if (trigger_tc) { u32 dig_hotplug_reg; dig_hotplug_reg = intel_uncore_rmw(&dev_priv->uncore, GEN11_TC_HOTPLUG_CTL, 0, 0); intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, trigger_tc, dig_hotplug_reg, dev_priv->display.hotplug.hpd, gen11_port_hotplug_long_detect); } if (trigger_tbt) { u32 dig_hotplug_reg; dig_hotplug_reg = intel_uncore_rmw(&dev_priv->uncore, GEN11_TBT_HOTPLUG_CTL, 0, 0); intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask, trigger_tbt, dig_hotplug_reg, dev_priv->display.hotplug.hpd, gen11_port_hotplug_long_detect); } if (pin_mask) intel_hpd_irq_handler(dev_priv, pin_mask, long_mask); else drm_err(&dev_priv->drm, "Unexpected DE HPD interrupt 0x%08x\n", iir); } static u32 ibx_hotplug_mask(enum hpd_pin hpd_pin) { switch (hpd_pin) { case HPD_PORT_A: return PORTA_HOTPLUG_ENABLE; case HPD_PORT_B: return PORTB_HOTPLUG_ENABLE | PORTB_PULSE_DURATION_MASK; case HPD_PORT_C: return PORTC_HOTPLUG_ENABLE | PORTC_PULSE_DURATION_MASK; case HPD_PORT_D: return PORTD_HOTPLUG_ENABLE | PORTD_PULSE_DURATION_MASK; default: return 0; } } static u32 ibx_hotplug_enables(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); switch (encoder->hpd_pin) { case HPD_PORT_A: /* * When CPU and PCH are on the same package, port A * HPD must be enabled in both north and south. */ return HAS_PCH_LPT_LP(i915) ? PORTA_HOTPLUG_ENABLE : 0; case HPD_PORT_B: return PORTB_HOTPLUG_ENABLE | PORTB_PULSE_DURATION_2ms; case HPD_PORT_C: return PORTC_HOTPLUG_ENABLE | PORTC_PULSE_DURATION_2ms; case HPD_PORT_D: return PORTD_HOTPLUG_ENABLE | PORTD_PULSE_DURATION_2ms; default: return 0; } } static void ibx_hpd_detection_setup(struct drm_i915_private *dev_priv) { /* * Enable digital hotplug on the PCH, and configure the DP short pulse * duration to 2ms (which is the minimum in the Display Port spec). * The pulse duration bits are reserved on LPT+. */ intel_uncore_rmw(&dev_priv->uncore, PCH_PORT_HOTPLUG, intel_hpd_hotplug_mask(dev_priv, ibx_hotplug_mask), intel_hpd_hotplug_enables(dev_priv, ibx_hotplug_enables)); } static void ibx_hpd_enable_detection(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); intel_uncore_rmw(&i915->uncore, PCH_PORT_HOTPLUG, ibx_hotplug_mask(encoder->hpd_pin), ibx_hotplug_enables(encoder)); } static void ibx_hpd_irq_setup(struct drm_i915_private *dev_priv) { u32 hotplug_irqs, enabled_irqs; enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->display.hotplug.pch_hpd); hotplug_irqs = intel_hpd_hotplug_irqs(dev_priv, dev_priv->display.hotplug.pch_hpd); ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs); ibx_hpd_detection_setup(dev_priv); } static u32 icp_ddi_hotplug_mask(enum hpd_pin hpd_pin) { switch (hpd_pin) { case HPD_PORT_A: case HPD_PORT_B: case HPD_PORT_C: case HPD_PORT_D: return SHOTPLUG_CTL_DDI_HPD_ENABLE(hpd_pin); default: return 0; } } static u32 icp_ddi_hotplug_enables(struct intel_encoder *encoder) { return icp_ddi_hotplug_mask(encoder->hpd_pin); } static u32 icp_tc_hotplug_mask(enum hpd_pin hpd_pin) { switch (hpd_pin) { case HPD_PORT_TC1: case HPD_PORT_TC2: case HPD_PORT_TC3: case HPD_PORT_TC4: case HPD_PORT_TC5: case HPD_PORT_TC6: return ICP_TC_HPD_ENABLE(hpd_pin); default: return 0; } } static u32 icp_tc_hotplug_enables(struct intel_encoder *encoder) { return icp_tc_hotplug_mask(encoder->hpd_pin); } static void icp_ddi_hpd_detection_setup(struct drm_i915_private *dev_priv) { intel_uncore_rmw(&dev_priv->uncore, SHOTPLUG_CTL_DDI, intel_hpd_hotplug_mask(dev_priv, icp_ddi_hotplug_mask), intel_hpd_hotplug_enables(dev_priv, icp_ddi_hotplug_enables)); } static void icp_ddi_hpd_enable_detection(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); intel_uncore_rmw(&i915->uncore, SHOTPLUG_CTL_DDI, icp_ddi_hotplug_mask(encoder->hpd_pin), icp_ddi_hotplug_enables(encoder)); } static void icp_tc_hpd_detection_setup(struct drm_i915_private *dev_priv) { intel_uncore_rmw(&dev_priv->uncore, SHOTPLUG_CTL_TC, intel_hpd_hotplug_mask(dev_priv, icp_tc_hotplug_mask), intel_hpd_hotplug_enables(dev_priv, icp_tc_hotplug_enables)); } static void icp_tc_hpd_enable_detection(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); intel_uncore_rmw(&i915->uncore, SHOTPLUG_CTL_TC, icp_tc_hotplug_mask(encoder->hpd_pin), icp_tc_hotplug_enables(encoder)); } static void icp_hpd_enable_detection(struct intel_encoder *encoder) { icp_ddi_hpd_enable_detection(encoder); icp_tc_hpd_enable_detection(encoder); } static void icp_hpd_irq_setup(struct drm_i915_private *dev_priv) { u32 hotplug_irqs, enabled_irqs; enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->display.hotplug.pch_hpd); hotplug_irqs = intel_hpd_hotplug_irqs(dev_priv, dev_priv->display.hotplug.pch_hpd); if (INTEL_PCH_TYPE(dev_priv) <= PCH_TGP) intel_uncore_write(&dev_priv->uncore, SHPD_FILTER_CNT, SHPD_FILTER_CNT_500_ADJ); else intel_uncore_write(&dev_priv->uncore, SHPD_FILTER_CNT, SHPD_FILTER_CNT_250); ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs); icp_ddi_hpd_detection_setup(dev_priv); icp_tc_hpd_detection_setup(dev_priv); } static u32 gen11_hotplug_mask(enum hpd_pin hpd_pin) { switch (hpd_pin) { case HPD_PORT_TC1: case HPD_PORT_TC2: case HPD_PORT_TC3: case HPD_PORT_TC4: case HPD_PORT_TC5: case HPD_PORT_TC6: return GEN11_HOTPLUG_CTL_ENABLE(hpd_pin); default: return 0; } } static u32 gen11_hotplug_enables(struct intel_encoder *encoder) { return gen11_hotplug_mask(encoder->hpd_pin); } static void dg1_hpd_invert(struct drm_i915_private *i915) { u32 val = (INVERT_DDIA_HPD | INVERT_DDIB_HPD | INVERT_DDIC_HPD | INVERT_DDID_HPD); intel_uncore_rmw(&i915->uncore, SOUTH_CHICKEN1, 0, val); } static void dg1_hpd_enable_detection(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); dg1_hpd_invert(i915); icp_hpd_enable_detection(encoder); } static void dg1_hpd_irq_setup(struct drm_i915_private *dev_priv) { dg1_hpd_invert(dev_priv); icp_hpd_irq_setup(dev_priv); } static void gen11_tc_hpd_detection_setup(struct drm_i915_private *dev_priv) { intel_uncore_rmw(&dev_priv->uncore, GEN11_TC_HOTPLUG_CTL, intel_hpd_hotplug_mask(dev_priv, gen11_hotplug_mask), intel_hpd_hotplug_enables(dev_priv, gen11_hotplug_enables)); } static void gen11_tc_hpd_enable_detection(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); intel_uncore_rmw(&i915->uncore, GEN11_TC_HOTPLUG_CTL, gen11_hotplug_mask(encoder->hpd_pin), gen11_hotplug_enables(encoder)); } static void gen11_tbt_hpd_detection_setup(struct drm_i915_private *dev_priv) { intel_uncore_rmw(&dev_priv->uncore, GEN11_TBT_HOTPLUG_CTL, intel_hpd_hotplug_mask(dev_priv, gen11_hotplug_mask), intel_hpd_hotplug_enables(dev_priv, gen11_hotplug_enables)); } static void gen11_tbt_hpd_enable_detection(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); intel_uncore_rmw(&i915->uncore, GEN11_TBT_HOTPLUG_CTL, gen11_hotplug_mask(encoder->hpd_pin), gen11_hotplug_enables(encoder)); } static void gen11_hpd_enable_detection(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); gen11_tc_hpd_enable_detection(encoder); gen11_tbt_hpd_enable_detection(encoder); if (INTEL_PCH_TYPE(i915) >= PCH_ICP) icp_hpd_enable_detection(encoder); } static void gen11_hpd_irq_setup(struct drm_i915_private *dev_priv) { u32 hotplug_irqs, enabled_irqs; enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->display.hotplug.hpd); hotplug_irqs = intel_hpd_hotplug_irqs(dev_priv, dev_priv->display.hotplug.hpd); intel_uncore_rmw(&dev_priv->uncore, GEN11_DE_HPD_IMR, hotplug_irqs, ~enabled_irqs & hotplug_irqs); intel_uncore_posting_read(&dev_priv->uncore, GEN11_DE_HPD_IMR); gen11_tc_hpd_detection_setup(dev_priv); gen11_tbt_hpd_detection_setup(dev_priv); if (INTEL_PCH_TYPE(dev_priv) >= PCH_ICP) icp_hpd_irq_setup(dev_priv); } static u32 mtp_ddi_hotplug_mask(enum hpd_pin hpd_pin) { switch (hpd_pin) { case HPD_PORT_A: case HPD_PORT_B: return SHOTPLUG_CTL_DDI_HPD_ENABLE(hpd_pin); default: return 0; } } static u32 mtp_ddi_hotplug_enables(struct intel_encoder *encoder) { return mtp_ddi_hotplug_mask(encoder->hpd_pin); } static u32 mtp_tc_hotplug_mask(enum hpd_pin hpd_pin) { switch (hpd_pin) { case HPD_PORT_TC1: case HPD_PORT_TC2: case HPD_PORT_TC3: case HPD_PORT_TC4: return ICP_TC_HPD_ENABLE(hpd_pin); default: return 0; } } static u32 mtp_tc_hotplug_enables(struct intel_encoder *encoder) { return mtp_tc_hotplug_mask(encoder->hpd_pin); } static void mtp_ddi_hpd_detection_setup(struct drm_i915_private *i915) { intel_de_rmw(i915, SHOTPLUG_CTL_DDI, intel_hpd_hotplug_mask(i915, mtp_ddi_hotplug_mask), intel_hpd_hotplug_enables(i915, mtp_ddi_hotplug_enables)); } static void mtp_ddi_hpd_enable_detection(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); intel_de_rmw(i915, SHOTPLUG_CTL_DDI, mtp_ddi_hotplug_mask(encoder->hpd_pin), mtp_ddi_hotplug_enables(encoder)); } static void mtp_tc_hpd_detection_setup(struct drm_i915_private *i915) { intel_de_rmw(i915, SHOTPLUG_CTL_TC, intel_hpd_hotplug_mask(i915, mtp_tc_hotplug_mask), intel_hpd_hotplug_enables(i915, mtp_tc_hotplug_enables)); } static void mtp_tc_hpd_enable_detection(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); intel_de_rmw(i915, SHOTPLUG_CTL_DDI, mtp_tc_hotplug_mask(encoder->hpd_pin), mtp_tc_hotplug_enables(encoder)); } static void mtp_hpd_invert(struct drm_i915_private *i915) { u32 val = (INVERT_DDIA_HPD | INVERT_DDIB_HPD | INVERT_DDIC_HPD | INVERT_TC1_HPD | INVERT_TC2_HPD | INVERT_TC3_HPD | INVERT_TC4_HPD | INVERT_DDID_HPD_MTP | INVERT_DDIE_HPD); intel_de_rmw(i915, SOUTH_CHICKEN1, 0, val); } static void mtp_hpd_enable_detection(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); mtp_hpd_invert(i915); mtp_ddi_hpd_enable_detection(encoder); mtp_tc_hpd_enable_detection(encoder); } static void mtp_hpd_irq_setup(struct drm_i915_private *i915) { u32 hotplug_irqs, enabled_irqs; enabled_irqs = intel_hpd_enabled_irqs(i915, i915->display.hotplug.pch_hpd); hotplug_irqs = intel_hpd_hotplug_irqs(i915, i915->display.hotplug.pch_hpd); intel_de_write(i915, SHPD_FILTER_CNT, SHPD_FILTER_CNT_250); mtp_hpd_invert(i915); ibx_display_interrupt_update(i915, hotplug_irqs, enabled_irqs); mtp_ddi_hpd_detection_setup(i915); mtp_tc_hpd_detection_setup(i915); } static void xe2lpd_sde_hpd_irq_setup(struct drm_i915_private *i915) { u32 hotplug_irqs, enabled_irqs; enabled_irqs = intel_hpd_enabled_irqs(i915, i915->display.hotplug.pch_hpd); hotplug_irqs = intel_hpd_hotplug_irqs(i915, i915->display.hotplug.pch_hpd); ibx_display_interrupt_update(i915, hotplug_irqs, enabled_irqs); mtp_ddi_hpd_detection_setup(i915); mtp_tc_hpd_detection_setup(i915); } static bool is_xelpdp_pica_hpd_pin(enum hpd_pin hpd_pin) { return hpd_pin >= HPD_PORT_TC1 && hpd_pin <= HPD_PORT_TC4; } static void _xelpdp_pica_hpd_detection_setup(struct drm_i915_private *i915, enum hpd_pin hpd_pin, bool enable) { u32 mask = XELPDP_TBT_HOTPLUG_ENABLE | XELPDP_DP_ALT_HOTPLUG_ENABLE; if (!is_xelpdp_pica_hpd_pin(hpd_pin)) return; intel_de_rmw(i915, XELPDP_PORT_HOTPLUG_CTL(hpd_pin), mask, enable ? mask : 0); } static void xelpdp_pica_hpd_enable_detection(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); _xelpdp_pica_hpd_detection_setup(i915, encoder->hpd_pin, true); } static void xelpdp_pica_hpd_detection_setup(struct drm_i915_private *i915) { struct intel_encoder *encoder; u32 available_pins = 0; enum hpd_pin pin; BUILD_BUG_ON(BITS_PER_TYPE(available_pins) < HPD_NUM_PINS); for_each_intel_encoder(&i915->drm, encoder) available_pins |= BIT(encoder->hpd_pin); for_each_hpd_pin(pin) _xelpdp_pica_hpd_detection_setup(i915, pin, available_pins & BIT(pin)); } static void xelpdp_hpd_enable_detection(struct intel_encoder *encoder) { xelpdp_pica_hpd_enable_detection(encoder); mtp_hpd_enable_detection(encoder); } static void xelpdp_hpd_irq_setup(struct drm_i915_private *i915) { u32 hotplug_irqs, enabled_irqs; enabled_irqs = intel_hpd_enabled_irqs(i915, i915->display.hotplug.hpd); hotplug_irqs = intel_hpd_hotplug_irqs(i915, i915->display.hotplug.hpd); intel_de_rmw(i915, PICAINTERRUPT_IMR, hotplug_irqs, ~enabled_irqs & hotplug_irqs); intel_uncore_posting_read(&i915->uncore, PICAINTERRUPT_IMR); xelpdp_pica_hpd_detection_setup(i915); if (INTEL_PCH_TYPE(i915) >= PCH_LNL) xe2lpd_sde_hpd_irq_setup(i915); else if (INTEL_PCH_TYPE(i915) >= PCH_MTL) mtp_hpd_irq_setup(i915); } static u32 spt_hotplug_mask(enum hpd_pin hpd_pin) { switch (hpd_pin) { case HPD_PORT_A: return PORTA_HOTPLUG_ENABLE; case HPD_PORT_B: return PORTB_HOTPLUG_ENABLE; case HPD_PORT_C: return PORTC_HOTPLUG_ENABLE; case HPD_PORT_D: return PORTD_HOTPLUG_ENABLE; default: return 0; } } static u32 spt_hotplug_enables(struct intel_encoder *encoder) { return spt_hotplug_mask(encoder->hpd_pin); } static u32 spt_hotplug2_mask(enum hpd_pin hpd_pin) { switch (hpd_pin) { case HPD_PORT_E: return PORTE_HOTPLUG_ENABLE; default: return 0; } } static u32 spt_hotplug2_enables(struct intel_encoder *encoder) { return spt_hotplug2_mask(encoder->hpd_pin); } static void spt_hpd_detection_setup(struct drm_i915_private *dev_priv) { /* Display WA #1179 WaHardHangonHotPlug: cnp */ if (HAS_PCH_CNP(dev_priv)) { intel_uncore_rmw(&dev_priv->uncore, SOUTH_CHICKEN1, CHASSIS_CLK_REQ_DURATION_MASK, CHASSIS_CLK_REQ_DURATION(0xf)); } /* Enable digital hotplug on the PCH */ intel_uncore_rmw(&dev_priv->uncore, PCH_PORT_HOTPLUG, intel_hpd_hotplug_mask(dev_priv, spt_hotplug_mask), intel_hpd_hotplug_enables(dev_priv, spt_hotplug_enables)); intel_uncore_rmw(&dev_priv->uncore, PCH_PORT_HOTPLUG2, intel_hpd_hotplug_mask(dev_priv, spt_hotplug2_mask), intel_hpd_hotplug_enables(dev_priv, spt_hotplug2_enables)); } static void spt_hpd_enable_detection(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); /* Display WA #1179 WaHardHangonHotPlug: cnp */ if (HAS_PCH_CNP(i915)) { intel_uncore_rmw(&i915->uncore, SOUTH_CHICKEN1, CHASSIS_CLK_REQ_DURATION_MASK, CHASSIS_CLK_REQ_DURATION(0xf)); } intel_uncore_rmw(&i915->uncore, PCH_PORT_HOTPLUG, spt_hotplug_mask(encoder->hpd_pin), spt_hotplug_enables(encoder)); intel_uncore_rmw(&i915->uncore, PCH_PORT_HOTPLUG2, spt_hotplug2_mask(encoder->hpd_pin), spt_hotplug2_enables(encoder)); } static void spt_hpd_irq_setup(struct drm_i915_private *dev_priv) { u32 hotplug_irqs, enabled_irqs; if (INTEL_PCH_TYPE(dev_priv) >= PCH_CNP) intel_uncore_write(&dev_priv->uncore, SHPD_FILTER_CNT, SHPD_FILTER_CNT_500_ADJ); enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->display.hotplug.pch_hpd); hotplug_irqs = intel_hpd_hotplug_irqs(dev_priv, dev_priv->display.hotplug.pch_hpd); ibx_display_interrupt_update(dev_priv, hotplug_irqs, enabled_irqs); spt_hpd_detection_setup(dev_priv); } static u32 ilk_hotplug_mask(enum hpd_pin hpd_pin) { switch (hpd_pin) { case HPD_PORT_A: return DIGITAL_PORTA_HOTPLUG_ENABLE | DIGITAL_PORTA_PULSE_DURATION_MASK; default: return 0; } } static u32 ilk_hotplug_enables(struct intel_encoder *encoder) { switch (encoder->hpd_pin) { case HPD_PORT_A: return DIGITAL_PORTA_HOTPLUG_ENABLE | DIGITAL_PORTA_PULSE_DURATION_2ms; default: return 0; } } static void ilk_hpd_detection_setup(struct drm_i915_private *dev_priv) { /* * Enable digital hotplug on the CPU, and configure the DP short pulse * duration to 2ms (which is the minimum in the Display Port spec) * The pulse duration bits are reserved on HSW+. */ intel_uncore_rmw(&dev_priv->uncore, DIGITAL_PORT_HOTPLUG_CNTRL, intel_hpd_hotplug_mask(dev_priv, ilk_hotplug_mask), intel_hpd_hotplug_enables(dev_priv, ilk_hotplug_enables)); } static void ilk_hpd_enable_detection(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); intel_uncore_rmw(&i915->uncore, DIGITAL_PORT_HOTPLUG_CNTRL, ilk_hotplug_mask(encoder->hpd_pin), ilk_hotplug_enables(encoder)); ibx_hpd_enable_detection(encoder); } static void ilk_hpd_irq_setup(struct drm_i915_private *dev_priv) { u32 hotplug_irqs, enabled_irqs; enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->display.hotplug.hpd); hotplug_irqs = intel_hpd_hotplug_irqs(dev_priv, dev_priv->display.hotplug.hpd); if (DISPLAY_VER(dev_priv) >= 8) bdw_update_port_irq(dev_priv, hotplug_irqs, enabled_irqs); else ilk_update_display_irq(dev_priv, hotplug_irqs, enabled_irqs); ilk_hpd_detection_setup(dev_priv); ibx_hpd_irq_setup(dev_priv); } static u32 bxt_hotplug_mask(enum hpd_pin hpd_pin) { switch (hpd_pin) { case HPD_PORT_A: return PORTA_HOTPLUG_ENABLE | BXT_DDIA_HPD_INVERT; case HPD_PORT_B: return PORTB_HOTPLUG_ENABLE | BXT_DDIB_HPD_INVERT; case HPD_PORT_C: return PORTC_HOTPLUG_ENABLE | BXT_DDIC_HPD_INVERT; default: return 0; } } static u32 bxt_hotplug_enables(struct intel_encoder *encoder) { u32 hotplug; switch (encoder->hpd_pin) { case HPD_PORT_A: hotplug = PORTA_HOTPLUG_ENABLE; if (intel_bios_encoder_hpd_invert(encoder->devdata)) hotplug |= BXT_DDIA_HPD_INVERT; return hotplug; case HPD_PORT_B: hotplug = PORTB_HOTPLUG_ENABLE; if (intel_bios_encoder_hpd_invert(encoder->devdata)) hotplug |= BXT_DDIB_HPD_INVERT; return hotplug; case HPD_PORT_C: hotplug = PORTC_HOTPLUG_ENABLE; if (intel_bios_encoder_hpd_invert(encoder->devdata)) hotplug |= BXT_DDIC_HPD_INVERT; return hotplug; default: return 0; } } static void bxt_hpd_detection_setup(struct drm_i915_private *dev_priv) { intel_uncore_rmw(&dev_priv->uncore, PCH_PORT_HOTPLUG, intel_hpd_hotplug_mask(dev_priv, bxt_hotplug_mask), intel_hpd_hotplug_enables(dev_priv, bxt_hotplug_enables)); } static void bxt_hpd_enable_detection(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); intel_uncore_rmw(&i915->uncore, PCH_PORT_HOTPLUG, bxt_hotplug_mask(encoder->hpd_pin), bxt_hotplug_enables(encoder)); } static void bxt_hpd_irq_setup(struct drm_i915_private *dev_priv) { u32 hotplug_irqs, enabled_irqs; enabled_irqs = intel_hpd_enabled_irqs(dev_priv, dev_priv->display.hotplug.hpd); hotplug_irqs = intel_hpd_hotplug_irqs(dev_priv, dev_priv->display.hotplug.hpd); bdw_update_port_irq(dev_priv, hotplug_irqs, enabled_irqs); bxt_hpd_detection_setup(dev_priv); } static void g45_hpd_peg_band_gap_wa(struct drm_i915_private *i915) { /* * For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written * 0xd. Failure to do so will result in spurious interrupts being * generated on the port when a cable is not attached. */ intel_de_rmw(i915, PEG_BAND_GAP_DATA, 0xf, 0xd); } static void i915_hpd_enable_detection(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); u32 hotplug_en = hpd_mask_i915[encoder->hpd_pin]; if (IS_G45(i915)) g45_hpd_peg_band_gap_wa(i915); /* HPD sense and interrupt enable are one and the same */ i915_hotplug_interrupt_update(i915, hotplug_en, hotplug_en); } static void i915_hpd_irq_setup(struct drm_i915_private *dev_priv) { u32 hotplug_en; lockdep_assert_held(&dev_priv->irq_lock); /* * Note HDMI and DP share hotplug bits. Enable bits are the same for all * generations. */ hotplug_en = intel_hpd_enabled_irqs(dev_priv, hpd_mask_i915); /* * Programming the CRT detection parameters tends to generate a spurious * hotplug event about three seconds later. So just do it once. */ if (IS_G4X(dev_priv)) hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64; hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50; if (IS_G45(dev_priv)) g45_hpd_peg_band_gap_wa(dev_priv); /* Ignore TV since it's buggy */ i915_hotplug_interrupt_update_locked(dev_priv, HOTPLUG_INT_EN_MASK | CRT_HOTPLUG_VOLTAGE_COMPARE_MASK | CRT_HOTPLUG_ACTIVATION_PERIOD_64, hotplug_en); } struct intel_hotplug_funcs { /* Enable HPD sense and interrupts for all present encoders */ void (*hpd_irq_setup)(struct drm_i915_private *i915); /* Enable HPD sense for a single encoder */ void (*hpd_enable_detection)(struct intel_encoder *encoder); }; #define HPD_FUNCS(platform) \ static const struct intel_hotplug_funcs platform##_hpd_funcs = { \ .hpd_irq_setup = platform##_hpd_irq_setup, \ .hpd_enable_detection = platform##_hpd_enable_detection, \ } HPD_FUNCS(i915); HPD_FUNCS(xelpdp); HPD_FUNCS(dg1); HPD_FUNCS(gen11); HPD_FUNCS(bxt); HPD_FUNCS(icp); HPD_FUNCS(spt); HPD_FUNCS(ilk); #undef HPD_FUNCS void intel_hpd_enable_detection(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); if (i915->display.funcs.hotplug) i915->display.funcs.hotplug->hpd_enable_detection(encoder); } void intel_hpd_irq_setup(struct drm_i915_private *i915) { if (i915->display.irq.display_irqs_enabled && i915->display.funcs.hotplug) i915->display.funcs.hotplug->hpd_irq_setup(i915); } void intel_hotplug_irq_init(struct drm_i915_private *i915) { intel_hpd_init_pins(i915); intel_hpd_init_early(i915); if (HAS_GMCH(i915)) { if (I915_HAS_HOTPLUG(i915)) i915->display.funcs.hotplug = &i915_hpd_funcs; } else { if (HAS_PCH_DG2(i915)) i915->display.funcs.hotplug = &icp_hpd_funcs; else if (HAS_PCH_DG1(i915)) i915->display.funcs.hotplug = &dg1_hpd_funcs; else if (DISPLAY_VER(i915) >= 14) i915->display.funcs.hotplug = &xelpdp_hpd_funcs; else if (DISPLAY_VER(i915) >= 11) i915->display.funcs.hotplug = &gen11_hpd_funcs; else if (IS_GEMINILAKE(i915) || IS_BROXTON(i915)) i915->display.funcs.hotplug = &bxt_hpd_funcs; else if (INTEL_PCH_TYPE(i915) >= PCH_ICP) i915->display.funcs.hotplug = &icp_hpd_funcs; else if (INTEL_PCH_TYPE(i915) >= PCH_SPT) i915->display.funcs.hotplug = &spt_hpd_funcs; else i915->display.funcs.hotplug = &ilk_hpd_funcs; } }