forked from Minki/linux
c6e1f8cc88
Bspec 21257 says "DDIA PHY is the comp master, so it must not be un-initialized if other combo PHYs are in use". Here we are shutting down all phys, so it's not strictly required. However let's be consistent on deinitializing things in the reversed order we initialized them. v2: simplify protection for enum port being unsigned in future v3: spell out reverse rather than rev Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com> Reviewed-by: Imre Deak <imre.deak@intel.com> Reviewed-by: Jani Nikula <jani.nikula@intel.com> Signed-off-by: José Roberto de Souza <jose.souza@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20181114011509.3667-2-lucas.demarchi@intel.com
255 lines
6.6 KiB
C
255 lines
6.6 KiB
C
// SPDX-License-Identifier: MIT
|
|
/*
|
|
* Copyright © 2018 Intel Corporation
|
|
*/
|
|
|
|
#include "intel_drv.h"
|
|
|
|
#define for_each_combo_port(__dev_priv, __port) \
|
|
for ((__port) = PORT_A; (__port) < I915_MAX_PORTS; (__port)++) \
|
|
for_each_if(intel_port_is_combophy(__dev_priv, __port))
|
|
|
|
#define for_each_combo_port_reverse(__dev_priv, __port) \
|
|
for ((__port) = I915_MAX_PORTS; (__port)-- > PORT_A;) \
|
|
for_each_if(intel_port_is_combophy(__dev_priv, __port))
|
|
|
|
enum {
|
|
PROCMON_0_85V_DOT_0,
|
|
PROCMON_0_95V_DOT_0,
|
|
PROCMON_0_95V_DOT_1,
|
|
PROCMON_1_05V_DOT_0,
|
|
PROCMON_1_05V_DOT_1,
|
|
};
|
|
|
|
static const struct cnl_procmon {
|
|
u32 dw1, dw9, dw10;
|
|
} cnl_procmon_values[] = {
|
|
[PROCMON_0_85V_DOT_0] =
|
|
{ .dw1 = 0x00000000, .dw9 = 0x62AB67BB, .dw10 = 0x51914F96, },
|
|
[PROCMON_0_95V_DOT_0] =
|
|
{ .dw1 = 0x00000000, .dw9 = 0x86E172C7, .dw10 = 0x77CA5EAB, },
|
|
[PROCMON_0_95V_DOT_1] =
|
|
{ .dw1 = 0x00000000, .dw9 = 0x93F87FE1, .dw10 = 0x8AE871C5, },
|
|
[PROCMON_1_05V_DOT_0] =
|
|
{ .dw1 = 0x00000000, .dw9 = 0x98FA82DD, .dw10 = 0x89E46DC1, },
|
|
[PROCMON_1_05V_DOT_1] =
|
|
{ .dw1 = 0x00440000, .dw9 = 0x9A00AB25, .dw10 = 0x8AE38FF1, },
|
|
};
|
|
|
|
/*
|
|
* CNL has just one set of registers, while ICL has two sets: one for port A and
|
|
* the other for port B. The CNL registers are equivalent to the ICL port A
|
|
* registers, that's why we call the ICL macros even though the function has CNL
|
|
* on its name.
|
|
*/
|
|
static const struct cnl_procmon *
|
|
cnl_get_procmon_ref_values(struct drm_i915_private *dev_priv, enum port port)
|
|
{
|
|
const struct cnl_procmon *procmon;
|
|
u32 val;
|
|
|
|
val = I915_READ(ICL_PORT_COMP_DW3(port));
|
|
switch (val & (PROCESS_INFO_MASK | VOLTAGE_INFO_MASK)) {
|
|
default:
|
|
MISSING_CASE(val);
|
|
/* fall through */
|
|
case VOLTAGE_INFO_0_85V | PROCESS_INFO_DOT_0:
|
|
procmon = &cnl_procmon_values[PROCMON_0_85V_DOT_0];
|
|
break;
|
|
case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_0:
|
|
procmon = &cnl_procmon_values[PROCMON_0_95V_DOT_0];
|
|
break;
|
|
case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_1:
|
|
procmon = &cnl_procmon_values[PROCMON_0_95V_DOT_1];
|
|
break;
|
|
case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_0:
|
|
procmon = &cnl_procmon_values[PROCMON_1_05V_DOT_0];
|
|
break;
|
|
case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_1:
|
|
procmon = &cnl_procmon_values[PROCMON_1_05V_DOT_1];
|
|
break;
|
|
}
|
|
|
|
return procmon;
|
|
}
|
|
|
|
static void cnl_set_procmon_ref_values(struct drm_i915_private *dev_priv,
|
|
enum port port)
|
|
{
|
|
const struct cnl_procmon *procmon;
|
|
u32 val;
|
|
|
|
procmon = cnl_get_procmon_ref_values(dev_priv, port);
|
|
|
|
val = I915_READ(ICL_PORT_COMP_DW1(port));
|
|
val &= ~((0xff << 16) | 0xff);
|
|
val |= procmon->dw1;
|
|
I915_WRITE(ICL_PORT_COMP_DW1(port), val);
|
|
|
|
I915_WRITE(ICL_PORT_COMP_DW9(port), procmon->dw9);
|
|
I915_WRITE(ICL_PORT_COMP_DW10(port), procmon->dw10);
|
|
}
|
|
|
|
static bool check_phy_reg(struct drm_i915_private *dev_priv,
|
|
enum port port, i915_reg_t reg, u32 mask,
|
|
u32 expected_val)
|
|
{
|
|
u32 val = I915_READ(reg);
|
|
|
|
if ((val & mask) != expected_val) {
|
|
DRM_DEBUG_DRIVER("Port %c combo PHY reg %08x state mismatch: "
|
|
"current %08x mask %08x expected %08x\n",
|
|
port_name(port),
|
|
reg.reg, val, mask, expected_val);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool cnl_verify_procmon_ref_values(struct drm_i915_private *dev_priv,
|
|
enum port port)
|
|
{
|
|
const struct cnl_procmon *procmon;
|
|
bool ret;
|
|
|
|
procmon = cnl_get_procmon_ref_values(dev_priv, port);
|
|
|
|
ret = check_phy_reg(dev_priv, port, ICL_PORT_COMP_DW1(port),
|
|
(0xff << 16) | 0xff, procmon->dw1);
|
|
ret &= check_phy_reg(dev_priv, port, ICL_PORT_COMP_DW9(port),
|
|
-1U, procmon->dw9);
|
|
ret &= check_phy_reg(dev_priv, port, ICL_PORT_COMP_DW10(port),
|
|
-1U, procmon->dw10);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool cnl_combo_phy_enabled(struct drm_i915_private *dev_priv)
|
|
{
|
|
return !(I915_READ(CHICKEN_MISC_2) & CNL_COMP_PWR_DOWN) &&
|
|
(I915_READ(CNL_PORT_COMP_DW0) & COMP_INIT);
|
|
}
|
|
|
|
static bool cnl_combo_phy_verify_state(struct drm_i915_private *dev_priv)
|
|
{
|
|
enum port port = PORT_A;
|
|
bool ret;
|
|
|
|
if (!cnl_combo_phy_enabled(dev_priv))
|
|
return false;
|
|
|
|
ret = cnl_verify_procmon_ref_values(dev_priv, port);
|
|
|
|
ret &= check_phy_reg(dev_priv, port, CNL_PORT_CL1CM_DW5,
|
|
CL_POWER_DOWN_ENABLE, CL_POWER_DOWN_ENABLE);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void cnl_combo_phys_init(struct drm_i915_private *dev_priv)
|
|
{
|
|
u32 val;
|
|
|
|
val = I915_READ(CHICKEN_MISC_2);
|
|
val &= ~CNL_COMP_PWR_DOWN;
|
|
I915_WRITE(CHICKEN_MISC_2, val);
|
|
|
|
/* Dummy PORT_A to get the correct CNL register from the ICL macro */
|
|
cnl_set_procmon_ref_values(dev_priv, PORT_A);
|
|
|
|
val = I915_READ(CNL_PORT_COMP_DW0);
|
|
val |= COMP_INIT;
|
|
I915_WRITE(CNL_PORT_COMP_DW0, val);
|
|
|
|
val = I915_READ(CNL_PORT_CL1CM_DW5);
|
|
val |= CL_POWER_DOWN_ENABLE;
|
|
I915_WRITE(CNL_PORT_CL1CM_DW5, val);
|
|
}
|
|
|
|
void cnl_combo_phys_uninit(struct drm_i915_private *dev_priv)
|
|
{
|
|
u32 val;
|
|
|
|
if (!cnl_combo_phy_verify_state(dev_priv))
|
|
DRM_WARN("Combo PHY HW state changed unexpectedly.\n");
|
|
|
|
val = I915_READ(CHICKEN_MISC_2);
|
|
val |= CNL_COMP_PWR_DOWN;
|
|
I915_WRITE(CHICKEN_MISC_2, val);
|
|
}
|
|
|
|
static bool icl_combo_phy_enabled(struct drm_i915_private *dev_priv,
|
|
enum port port)
|
|
{
|
|
return !(I915_READ(ICL_PHY_MISC(port)) &
|
|
ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN) &&
|
|
(I915_READ(ICL_PORT_COMP_DW0(port)) & COMP_INIT);
|
|
}
|
|
|
|
static bool icl_combo_phy_verify_state(struct drm_i915_private *dev_priv,
|
|
enum port port)
|
|
{
|
|
bool ret;
|
|
|
|
if (!icl_combo_phy_enabled(dev_priv, port))
|
|
return false;
|
|
|
|
ret = cnl_verify_procmon_ref_values(dev_priv, port);
|
|
|
|
ret &= check_phy_reg(dev_priv, port, ICL_PORT_CL_DW5(port),
|
|
CL_POWER_DOWN_ENABLE, CL_POWER_DOWN_ENABLE);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void icl_combo_phys_init(struct drm_i915_private *dev_priv)
|
|
{
|
|
enum port port;
|
|
|
|
for_each_combo_port(dev_priv, port) {
|
|
u32 val;
|
|
|
|
if (icl_combo_phy_verify_state(dev_priv, port)) {
|
|
DRM_DEBUG_DRIVER("Port %c combo PHY already enabled, won't reprogram it.\n",
|
|
port_name(port));
|
|
continue;
|
|
}
|
|
|
|
val = I915_READ(ICL_PHY_MISC(port));
|
|
val &= ~ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN;
|
|
I915_WRITE(ICL_PHY_MISC(port), val);
|
|
|
|
cnl_set_procmon_ref_values(dev_priv, port);
|
|
|
|
val = I915_READ(ICL_PORT_COMP_DW0(port));
|
|
val |= COMP_INIT;
|
|
I915_WRITE(ICL_PORT_COMP_DW0(port), val);
|
|
|
|
val = I915_READ(ICL_PORT_CL_DW5(port));
|
|
val |= CL_POWER_DOWN_ENABLE;
|
|
I915_WRITE(ICL_PORT_CL_DW5(port), val);
|
|
}
|
|
}
|
|
|
|
void icl_combo_phys_uninit(struct drm_i915_private *dev_priv)
|
|
{
|
|
enum port port;
|
|
|
|
for_each_combo_port_reverse(dev_priv, port) {
|
|
u32 val;
|
|
|
|
if (!icl_combo_phy_verify_state(dev_priv, port))
|
|
DRM_WARN("Port %c combo PHY HW state changed unexpectedly\n",
|
|
port_name(port));
|
|
|
|
val = I915_READ(ICL_PHY_MISC(port));
|
|
val |= ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN;
|
|
I915_WRITE(ICL_PHY_MISC(port), val);
|
|
|
|
val = I915_READ(ICL_PORT_COMP_DW0(port));
|
|
val &= ~COMP_INIT;
|
|
I915_WRITE(ICL_PORT_COMP_DW0(port), val);
|
|
}
|
|
}
|