linux/drivers/gpu/drm/i915/display/intel_combo_phy.c
<<
>>
Prefs
   1// SPDX-License-Identifier: MIT
   2/*
   3 * Copyright © 2018 Intel Corporation
   4 */
   5
   6#include "intel_combo_phy.h"
   7#include "intel_drv.h"
   8
   9#define for_each_combo_port(__dev_priv, __port) \
  10        for ((__port) = PORT_A; (__port) < I915_MAX_PORTS; (__port)++)  \
  11                for_each_if(intel_port_is_combophy(__dev_priv, __port))
  12
  13#define for_each_combo_port_reverse(__dev_priv, __port) \
  14        for ((__port) = I915_MAX_PORTS; (__port)-- > PORT_A;) \
  15                for_each_if(intel_port_is_combophy(__dev_priv, __port))
  16
  17enum {
  18        PROCMON_0_85V_DOT_0,
  19        PROCMON_0_95V_DOT_0,
  20        PROCMON_0_95V_DOT_1,
  21        PROCMON_1_05V_DOT_0,
  22        PROCMON_1_05V_DOT_1,
  23};
  24
  25static const struct cnl_procmon {
  26        u32 dw1, dw9, dw10;
  27} cnl_procmon_values[] = {
  28        [PROCMON_0_85V_DOT_0] =
  29                { .dw1 = 0x00000000, .dw9 = 0x62AB67BB, .dw10 = 0x51914F96, },
  30        [PROCMON_0_95V_DOT_0] =
  31                { .dw1 = 0x00000000, .dw9 = 0x86E172C7, .dw10 = 0x77CA5EAB, },
  32        [PROCMON_0_95V_DOT_1] =
  33                { .dw1 = 0x00000000, .dw9 = 0x93F87FE1, .dw10 = 0x8AE871C5, },
  34        [PROCMON_1_05V_DOT_0] =
  35                { .dw1 = 0x00000000, .dw9 = 0x98FA82DD, .dw10 = 0x89E46DC1, },
  36        [PROCMON_1_05V_DOT_1] =
  37                { .dw1 = 0x00440000, .dw9 = 0x9A00AB25, .dw10 = 0x8AE38FF1, },
  38};
  39
  40/*
  41 * CNL has just one set of registers, while ICL has two sets: one for port A and
  42 * the other for port B. The CNL registers are equivalent to the ICL port A
  43 * registers, that's why we call the ICL macros even though the function has CNL
  44 * on its name.
  45 */
  46static const struct cnl_procmon *
  47cnl_get_procmon_ref_values(struct drm_i915_private *dev_priv, enum port port)
  48{
  49        const struct cnl_procmon *procmon;
  50        u32 val;
  51
  52        val = I915_READ(ICL_PORT_COMP_DW3(port));
  53        switch (val & (PROCESS_INFO_MASK | VOLTAGE_INFO_MASK)) {
  54        default:
  55                MISSING_CASE(val);
  56                /* fall through */
  57        case VOLTAGE_INFO_0_85V | PROCESS_INFO_DOT_0:
  58                procmon = &cnl_procmon_values[PROCMON_0_85V_DOT_0];
  59                break;
  60        case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_0:
  61                procmon = &cnl_procmon_values[PROCMON_0_95V_DOT_0];
  62                break;
  63        case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_1:
  64                procmon = &cnl_procmon_values[PROCMON_0_95V_DOT_1];
  65                break;
  66        case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_0:
  67                procmon = &cnl_procmon_values[PROCMON_1_05V_DOT_0];
  68                break;
  69        case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_1:
  70                procmon = &cnl_procmon_values[PROCMON_1_05V_DOT_1];
  71                break;
  72        }
  73
  74        return procmon;
  75}
  76
  77static void cnl_set_procmon_ref_values(struct drm_i915_private *dev_priv,
  78                                       enum port port)
  79{
  80        const struct cnl_procmon *procmon;
  81        u32 val;
  82
  83        procmon = cnl_get_procmon_ref_values(dev_priv, port);
  84
  85        val = I915_READ(ICL_PORT_COMP_DW1(port));
  86        val &= ~((0xff << 16) | 0xff);
  87        val |= procmon->dw1;
  88        I915_WRITE(ICL_PORT_COMP_DW1(port), val);
  89
  90        I915_WRITE(ICL_PORT_COMP_DW9(port), procmon->dw9);
  91        I915_WRITE(ICL_PORT_COMP_DW10(port), procmon->dw10);
  92}
  93
  94static bool check_phy_reg(struct drm_i915_private *dev_priv,
  95                          enum port port, i915_reg_t reg, u32 mask,
  96                          u32 expected_val)
  97{
  98        u32 val = I915_READ(reg);
  99
 100        if ((val & mask) != expected_val) {
 101                DRM_DEBUG_DRIVER("Port %c combo PHY reg %08x state mismatch: "
 102                                 "current %08x mask %08x expected %08x\n",
 103                                 port_name(port),
 104                                 reg.reg, val, mask, expected_val);
 105                return false;
 106        }
 107
 108        return true;
 109}
 110
 111static bool cnl_verify_procmon_ref_values(struct drm_i915_private *dev_priv,
 112                                          enum port port)
 113{
 114        const struct cnl_procmon *procmon;
 115        bool ret;
 116
 117        procmon = cnl_get_procmon_ref_values(dev_priv, port);
 118
 119        ret = check_phy_reg(dev_priv, port, ICL_PORT_COMP_DW1(port),
 120                            (0xff << 16) | 0xff, procmon->dw1);
 121        ret &= check_phy_reg(dev_priv, port, ICL_PORT_COMP_DW9(port),
 122                             -1U, procmon->dw9);
 123        ret &= check_phy_reg(dev_priv, port, ICL_PORT_COMP_DW10(port),
 124                             -1U, procmon->dw10);
 125
 126        return ret;
 127}
 128
 129static bool cnl_combo_phy_enabled(struct drm_i915_private *dev_priv)
 130{
 131        return !(I915_READ(CHICKEN_MISC_2) & CNL_COMP_PWR_DOWN) &&
 132                (I915_READ(CNL_PORT_COMP_DW0) & COMP_INIT);
 133}
 134
 135static bool cnl_combo_phy_verify_state(struct drm_i915_private *dev_priv)
 136{
 137        enum port port = PORT_A;
 138        bool ret;
 139
 140        if (!cnl_combo_phy_enabled(dev_priv))
 141                return false;
 142
 143        ret = cnl_verify_procmon_ref_values(dev_priv, port);
 144
 145        ret &= check_phy_reg(dev_priv, port, CNL_PORT_CL1CM_DW5,
 146                             CL_POWER_DOWN_ENABLE, CL_POWER_DOWN_ENABLE);
 147
 148        return ret;
 149}
 150
 151static void cnl_combo_phys_init(struct drm_i915_private *dev_priv)
 152{
 153        u32 val;
 154
 155        val = I915_READ(CHICKEN_MISC_2);
 156        val &= ~CNL_COMP_PWR_DOWN;
 157        I915_WRITE(CHICKEN_MISC_2, val);
 158
 159        /* Dummy PORT_A to get the correct CNL register from the ICL macro */
 160        cnl_set_procmon_ref_values(dev_priv, PORT_A);
 161
 162        val = I915_READ(CNL_PORT_COMP_DW0);
 163        val |= COMP_INIT;
 164        I915_WRITE(CNL_PORT_COMP_DW0, val);
 165
 166        val = I915_READ(CNL_PORT_CL1CM_DW5);
 167        val |= CL_POWER_DOWN_ENABLE;
 168        I915_WRITE(CNL_PORT_CL1CM_DW5, val);
 169}
 170
 171static void cnl_combo_phys_uninit(struct drm_i915_private *dev_priv)
 172{
 173        u32 val;
 174
 175        if (!cnl_combo_phy_verify_state(dev_priv))
 176                DRM_WARN("Combo PHY HW state changed unexpectedly.\n");
 177
 178        val = I915_READ(CHICKEN_MISC_2);
 179        val |= CNL_COMP_PWR_DOWN;
 180        I915_WRITE(CHICKEN_MISC_2, val);
 181}
 182
 183static bool icl_combo_phy_enabled(struct drm_i915_private *dev_priv,
 184                                  enum port port)
 185{
 186        return !(I915_READ(ICL_PHY_MISC(port)) &
 187                 ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN) &&
 188                (I915_READ(ICL_PORT_COMP_DW0(port)) & COMP_INIT);
 189}
 190
 191static bool icl_combo_phy_verify_state(struct drm_i915_private *dev_priv,
 192                                       enum port port)
 193{
 194        bool ret;
 195
 196        if (!icl_combo_phy_enabled(dev_priv, port))
 197                return false;
 198
 199        ret = cnl_verify_procmon_ref_values(dev_priv, port);
 200
 201        if (port == PORT_A)
 202                ret &= check_phy_reg(dev_priv, port, ICL_PORT_COMP_DW8(port),
 203                                     IREFGEN, IREFGEN);
 204
 205        ret &= check_phy_reg(dev_priv, port, ICL_PORT_CL_DW5(port),
 206                             CL_POWER_DOWN_ENABLE, CL_POWER_DOWN_ENABLE);
 207
 208        return ret;
 209}
 210
 211void intel_combo_phy_power_up_lanes(struct drm_i915_private *dev_priv,
 212                                    enum port port, bool is_dsi,
 213                                    int lane_count, bool lane_reversal)
 214{
 215        u8 lane_mask;
 216        u32 val;
 217
 218        if (is_dsi) {
 219                WARN_ON(lane_reversal);
 220
 221                switch (lane_count) {
 222                case 1:
 223                        lane_mask = PWR_DOWN_LN_3_1_0;
 224                        break;
 225                case 2:
 226                        lane_mask = PWR_DOWN_LN_3_1;
 227                        break;
 228                case 3:
 229                        lane_mask = PWR_DOWN_LN_3;
 230                        break;
 231                default:
 232                        MISSING_CASE(lane_count);
 233                        /* fall-through */
 234                case 4:
 235                        lane_mask = PWR_UP_ALL_LANES;
 236                        break;
 237                }
 238        } else {
 239                switch (lane_count) {
 240                case 1:
 241                        lane_mask = lane_reversal ? PWR_DOWN_LN_2_1_0 :
 242                                                    PWR_DOWN_LN_3_2_1;
 243                        break;
 244                case 2:
 245                        lane_mask = lane_reversal ? PWR_DOWN_LN_1_0 :
 246                                                    PWR_DOWN_LN_3_2;
 247                        break;
 248                default:
 249                        MISSING_CASE(lane_count);
 250                        /* fall-through */
 251                case 4:
 252                        lane_mask = PWR_UP_ALL_LANES;
 253                        break;
 254                }
 255        }
 256
 257        val = I915_READ(ICL_PORT_CL_DW10(port));
 258        val &= ~PWR_DOWN_LN_MASK;
 259        val |= lane_mask << PWR_DOWN_LN_SHIFT;
 260        I915_WRITE(ICL_PORT_CL_DW10(port), val);
 261}
 262
 263static void icl_combo_phys_init(struct drm_i915_private *dev_priv)
 264{
 265        enum port port;
 266
 267        for_each_combo_port(dev_priv, port) {
 268                u32 val;
 269
 270                if (icl_combo_phy_verify_state(dev_priv, port)) {
 271                        DRM_DEBUG_DRIVER("Port %c combo PHY already enabled, won't reprogram it.\n",
 272                                         port_name(port));
 273                        continue;
 274                }
 275
 276                val = I915_READ(ICL_PHY_MISC(port));
 277                val &= ~ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN;
 278                I915_WRITE(ICL_PHY_MISC(port), val);
 279
 280                cnl_set_procmon_ref_values(dev_priv, port);
 281
 282                if (port == PORT_A) {
 283                        val = I915_READ(ICL_PORT_COMP_DW8(port));
 284                        val |= IREFGEN;
 285                        I915_WRITE(ICL_PORT_COMP_DW8(port), val);
 286                }
 287
 288                val = I915_READ(ICL_PORT_COMP_DW0(port));
 289                val |= COMP_INIT;
 290                I915_WRITE(ICL_PORT_COMP_DW0(port), val);
 291
 292                val = I915_READ(ICL_PORT_CL_DW5(port));
 293                val |= CL_POWER_DOWN_ENABLE;
 294                I915_WRITE(ICL_PORT_CL_DW5(port), val);
 295        }
 296}
 297
 298static void icl_combo_phys_uninit(struct drm_i915_private *dev_priv)
 299{
 300        enum port port;
 301
 302        for_each_combo_port_reverse(dev_priv, port) {
 303                u32 val;
 304
 305                if (port == PORT_A &&
 306                    !icl_combo_phy_verify_state(dev_priv, port))
 307                        DRM_WARN("Port %c combo PHY HW state changed unexpectedly\n",
 308                                 port_name(port));
 309
 310                val = I915_READ(ICL_PHY_MISC(port));
 311                val |= ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN;
 312                I915_WRITE(ICL_PHY_MISC(port), val);
 313
 314                val = I915_READ(ICL_PORT_COMP_DW0(port));
 315                val &= ~COMP_INIT;
 316                I915_WRITE(ICL_PORT_COMP_DW0(port), val);
 317        }
 318}
 319
 320void intel_combo_phy_init(struct drm_i915_private *i915)
 321{
 322        if (INTEL_GEN(i915) >= 11)
 323                icl_combo_phys_init(i915);
 324        else if (IS_CANNONLAKE(i915))
 325                cnl_combo_phys_init(i915);
 326}
 327
 328void intel_combo_phy_uninit(struct drm_i915_private *i915)
 329{
 330        if (INTEL_GEN(i915) >= 11)
 331                icl_combo_phys_uninit(i915);
 332        else if (IS_CANNONLAKE(i915))
 333                cnl_combo_phys_uninit(i915);
 334}
 335