1443 lines
40 KiB
C
1443 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);
|
|
|
|
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_500_ADJ);
|
|
|
|
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;
|
|
}
|
|
}
|