On TGP, the RTC (always running) was reduced from 3MHz to 32KHz. As a result of this change, when HPD active going low pulse or HPD IRQ is presented and the refclk (19.2MHz) is not toggling already toggling, there is a 60 to 90us synchronization delay which effectively reduces the duration of the IRQ pulse to less than the programmed 500us filter value and the hot plug interrupt is NOT registered. Solution was to Reduce SHPD_FILTER to 250us for ADL and above. This solution was derived when the below patch was floated. [1]https://patchwork.freedesktop.org/patch/532187 and after some internal discussion Ville's suggestion made sense. Bspec: 68970 Cc: Uma Shankar <uma.shankar@intel.com> Cc: Ville Syrjala <ville.syrjala@linux.intel.com> Suggested-by: Ville Syrjala <ville.syrjala@linux.intel.com> Signed-off-by: Suraj Kandpal <suraj.kandpal@intel.com> Reviewed-by: Uma Shankar <uma.shankar@intel.com> Signed-off-by: Uma Shankar <uma.shankar@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20230720104624.3063544-1-suraj.kandpal@intel.com
1444 lines
40 KiB
C
1444 lines
40 KiB
C
// 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_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;
|
|
|
|
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 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_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;
|
|
}
|
|
}
|