Contributors: 33
Author Tokens Token Proportion Commits Commit Proportion
Jani Nikula 4479 69.98% 17 13.49%
Ville Syrjälä 500 7.81% 35 27.78%
Egbert Eich 218 3.41% 3 2.38%
Mika Kahola 134 2.09% 1 0.79%
Lucas De Marchi 101 1.58% 5 3.97%
Shashank Sharma 97 1.52% 1 0.79%
Jesse Barnes 94 1.47% 8 6.35%
Chris Wilson 90 1.41% 7 5.56%
Paulo Zanoni 88 1.38% 2 1.59%
Gustavo Sousa 85 1.33% 2 1.59%
Daniel Vetter 80 1.25% 10 7.94%
Adam Jackson 65 1.02% 1 0.79%
José Roberto de Souza 65 1.02% 1 0.79%
Stephen Chandler Paul 41 0.64% 2 1.59%
Deepak S 35 0.55% 1 0.79%
Ma Ling 27 0.42% 1 0.79%
Anusha Srivatsa 25 0.39% 2 1.59%
Sonika Jindal 23 0.36% 1 0.79%
Tvrtko A. Ursulin 21 0.33% 5 3.97%
Dave Airlie 21 0.33% 1 0.79%
Matt Roper 18 0.28% 5 3.97%
Oscar Mateo 18 0.28% 1 0.79%
Xiong Zhang 17 0.27% 1 0.79%
Imre Deak 13 0.20% 2 1.59%
Thomas Zimmermann 10 0.16% 1 0.79%
Dhinakaran Pandiyan 10 0.16% 2 1.59%
Suraj Kandpal 8 0.12% 1 0.79%
Ander Conselvan de Oliveira 6 0.09% 2 1.59%
Chandra Konduru 4 0.06% 1 0.79%
Ben Widawsky 2 0.03% 1 0.79%
Mika Kuoppala 2 0.03% 1 0.79%
Andi Shyti 2 0.03% 1 0.79%
Matt Atwood 1 0.02% 1 0.79%
Total 6400 126


// 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_LNL)
		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_MTP)
		hpd->pch_hpd = hpd_mtp;
	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, 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) & hotplug_status_mask;

		if (tmp == 0)
			return hotplug_status;

		hotplug_status |= tmp;
		intel_uncore_write(&dev_priv->uncore, PORT_HOTPLUG_STAT, 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));

	return hotplug_status;
}

void i9xx_hpd_irq_handler(struct drm_i915_private *dev_priv, u32 hotplug_status)
{
	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(dev_priv);
}

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)
{
	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(i915);

	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_MTP)
		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 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];

	/* 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;

	/* 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_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;
	}
}