riscv: Kconfig: Style cleanups
[linux-block.git] / drivers / gpu / drm / i915 / display / intel_combo_phy.c
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2018 Intel Corporation
4  */
5
6 #include "intel_combo_phy.h"
7 #include "intel_combo_phy_regs.h"
8 #include "intel_de.h"
9 #include "intel_display_types.h"
10
11 #define for_each_combo_phy(__dev_priv, __phy) \
12         for ((__phy) = PHY_A; (__phy) < I915_MAX_PHYS; (__phy)++)       \
13                 for_each_if(intel_phy_is_combo(__dev_priv, __phy))
14
15 #define for_each_combo_phy_reverse(__dev_priv, __phy) \
16         for ((__phy) = I915_MAX_PHYS; (__phy)-- > PHY_A;) \
17                 for_each_if(intel_phy_is_combo(__dev_priv, __phy))
18
19 enum {
20         PROCMON_0_85V_DOT_0,
21         PROCMON_0_95V_DOT_0,
22         PROCMON_0_95V_DOT_1,
23         PROCMON_1_05V_DOT_0,
24         PROCMON_1_05V_DOT_1,
25 };
26
27 static const struct icl_procmon {
28         const char *name;
29         u32 dw1, dw9, dw10;
30 } icl_procmon_values[] = {
31         [PROCMON_0_85V_DOT_0] = {
32                 .name = "0.85V dot0 (low-voltage)",
33                 .dw1 = 0x00000000, .dw9 = 0x62AB67BB, .dw10 = 0x51914F96,
34         },
35         [PROCMON_0_95V_DOT_0] = {
36                 .name = "0.95V dot0",
37                 .dw1 = 0x00000000, .dw9 = 0x86E172C7, .dw10 = 0x77CA5EAB,
38         },
39         [PROCMON_0_95V_DOT_1] = {
40                 .name = "0.95V dot1",
41                 .dw1 = 0x00000000, .dw9 = 0x93F87FE1, .dw10 = 0x8AE871C5,
42         },
43         [PROCMON_1_05V_DOT_0] = {
44                 .name = "1.05V dot0",
45                 .dw1 = 0x00000000, .dw9 = 0x98FA82DD, .dw10 = 0x89E46DC1,
46         },
47         [PROCMON_1_05V_DOT_1] = {
48                 .name = "1.05V dot1",
49                 .dw1 = 0x00440000, .dw9 = 0x9A00AB25, .dw10 = 0x8AE38FF1,
50         },
51 };
52
53 static const struct icl_procmon *
54 icl_get_procmon_ref_values(struct drm_i915_private *dev_priv, enum phy phy)
55 {
56         const struct icl_procmon *procmon;
57         u32 val;
58
59         val = intel_de_read(dev_priv, ICL_PORT_COMP_DW3(phy));
60         switch (val & (PROCESS_INFO_MASK | VOLTAGE_INFO_MASK)) {
61         default:
62                 MISSING_CASE(val);
63                 fallthrough;
64         case VOLTAGE_INFO_0_85V | PROCESS_INFO_DOT_0:
65                 procmon = &icl_procmon_values[PROCMON_0_85V_DOT_0];
66                 break;
67         case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_0:
68                 procmon = &icl_procmon_values[PROCMON_0_95V_DOT_0];
69                 break;
70         case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_1:
71                 procmon = &icl_procmon_values[PROCMON_0_95V_DOT_1];
72                 break;
73         case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_0:
74                 procmon = &icl_procmon_values[PROCMON_1_05V_DOT_0];
75                 break;
76         case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_1:
77                 procmon = &icl_procmon_values[PROCMON_1_05V_DOT_1];
78                 break;
79         }
80
81         return procmon;
82 }
83
84 static void icl_set_procmon_ref_values(struct drm_i915_private *dev_priv,
85                                        enum phy phy)
86 {
87         const struct icl_procmon *procmon;
88         u32 val;
89
90         procmon = icl_get_procmon_ref_values(dev_priv, phy);
91
92         val = intel_de_read(dev_priv, ICL_PORT_COMP_DW1(phy));
93         val &= ~((0xff << 16) | 0xff);
94         val |= procmon->dw1;
95         intel_de_write(dev_priv, ICL_PORT_COMP_DW1(phy), val);
96
97         intel_de_write(dev_priv, ICL_PORT_COMP_DW9(phy), procmon->dw9);
98         intel_de_write(dev_priv, ICL_PORT_COMP_DW10(phy), procmon->dw10);
99 }
100
101 static bool check_phy_reg(struct drm_i915_private *dev_priv,
102                           enum phy phy, i915_reg_t reg, u32 mask,
103                           u32 expected_val)
104 {
105         u32 val = intel_de_read(dev_priv, reg);
106
107         if ((val & mask) != expected_val) {
108                 drm_dbg(&dev_priv->drm,
109                         "Combo PHY %c reg %08x state mismatch: "
110                         "current %08x mask %08x expected %08x\n",
111                         phy_name(phy),
112                         reg.reg, val, mask, expected_val);
113                 return false;
114         }
115
116         return true;
117 }
118
119 static bool icl_verify_procmon_ref_values(struct drm_i915_private *dev_priv,
120                                           enum phy phy)
121 {
122         const struct icl_procmon *procmon;
123         bool ret;
124
125         procmon = icl_get_procmon_ref_values(dev_priv, phy);
126
127         drm_dbg_kms(&dev_priv->drm,
128                     "Combo PHY %c Voltage/Process Info : %s\n",
129                     phy_name(phy), procmon->name);
130
131         ret = check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW1(phy),
132                             (0xff << 16) | 0xff, procmon->dw1);
133         ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW9(phy),
134                              -1U, procmon->dw9);
135         ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW10(phy),
136                              -1U, procmon->dw10);
137
138         return ret;
139 }
140
141 static bool has_phy_misc(struct drm_i915_private *i915, enum phy phy)
142 {
143         /*
144          * Some platforms only expect PHY_MISC to be programmed for PHY-A and
145          * PHY-B and may not even have instances of the register for the
146          * other combo PHY's.
147          *
148          * ADL-S technically has three instances of PHY_MISC, but only requires
149          * that we program it for PHY A.
150          */
151
152         if (IS_ALDERLAKE_S(i915))
153                 return phy == PHY_A;
154         else if (IS_JSL_EHL(i915) ||
155                  IS_ROCKETLAKE(i915) ||
156                  IS_DG1(i915))
157                 return phy < PHY_C;
158
159         return true;
160 }
161
162 static bool icl_combo_phy_enabled(struct drm_i915_private *dev_priv,
163                                   enum phy phy)
164 {
165         /* The PHY C added by EHL has no PHY_MISC register */
166         if (!has_phy_misc(dev_priv, phy))
167                 return intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)) & COMP_INIT;
168         else
169                 return !(intel_de_read(dev_priv, ICL_PHY_MISC(phy)) &
170                          ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN) &&
171                         (intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)) & COMP_INIT);
172 }
173
174 static bool ehl_vbt_ddi_d_present(struct drm_i915_private *i915)
175 {
176         bool ddi_a_present = intel_bios_is_port_present(i915, PORT_A);
177         bool ddi_d_present = intel_bios_is_port_present(i915, PORT_D);
178         bool dsi_present = intel_bios_is_dsi_present(i915, NULL);
179
180         /*
181          * VBT's 'dvo port' field for child devices references the DDI, not
182          * the PHY.  So if combo PHY A is wired up to drive an external
183          * display, we should see a child device present on PORT_D and
184          * nothing on PORT_A and no DSI.
185          */
186         if (ddi_d_present && !ddi_a_present && !dsi_present)
187                 return true;
188
189         /*
190          * If we encounter a VBT that claims to have an external display on
191          * DDI-D _and_ an internal display on DDI-A/DSI leave an error message
192          * in the log and let the internal display win.
193          */
194         if (ddi_d_present)
195                 drm_err(&i915->drm,
196                         "VBT claims to have both internal and external displays on PHY A.  Configuring for internal.\n");
197
198         return false;
199 }
200
201 static bool phy_is_master(struct drm_i915_private *dev_priv, enum phy phy)
202 {
203         /*
204          * Certain PHYs are connected to compensation resistors and act
205          * as masters to other PHYs.
206          *
207          * ICL,TGL:
208          *   A(master) -> B(slave), C(slave)
209          * RKL,DG1:
210          *   A(master) -> B(slave)
211          *   C(master) -> D(slave)
212          * ADL-S:
213          *   A(master) -> B(slave), C(slave)
214          *   D(master) -> E(slave)
215          *
216          * We must set the IREFGEN bit for any PHY acting as a master
217          * to another PHY.
218          */
219         if (phy == PHY_A)
220                 return true;
221         else if (IS_ALDERLAKE_S(dev_priv))
222                 return phy == PHY_D;
223         else if (IS_DG1(dev_priv) || IS_ROCKETLAKE(dev_priv))
224                 return phy == PHY_C;
225
226         return false;
227 }
228
229 static bool icl_combo_phy_verify_state(struct drm_i915_private *dev_priv,
230                                        enum phy phy)
231 {
232         bool ret = true;
233         u32 expected_val = 0;
234
235         if (!icl_combo_phy_enabled(dev_priv, phy))
236                 return false;
237
238         if (DISPLAY_VER(dev_priv) >= 12) {
239                 ret &= check_phy_reg(dev_priv, phy, ICL_PORT_TX_DW8_LN(0, phy),
240                                      ICL_PORT_TX_DW8_ODCC_CLK_SEL |
241                                      ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_MASK,
242                                      ICL_PORT_TX_DW8_ODCC_CLK_SEL |
243                                      ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2);
244
245                 ret &= check_phy_reg(dev_priv, phy, ICL_PORT_PCS_DW1_LN(0, phy),
246                                      DCC_MODE_SELECT_MASK,
247                                      DCC_MODE_SELECT_CONTINUOSLY);
248         }
249
250         ret &= icl_verify_procmon_ref_values(dev_priv, phy);
251
252         if (phy_is_master(dev_priv, phy)) {
253                 ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW8(phy),
254                                      IREFGEN, IREFGEN);
255
256                 if (IS_JSL_EHL(dev_priv)) {
257                         if (ehl_vbt_ddi_d_present(dev_priv))
258                                 expected_val = ICL_PHY_MISC_MUX_DDID;
259
260                         ret &= check_phy_reg(dev_priv, phy, ICL_PHY_MISC(phy),
261                                              ICL_PHY_MISC_MUX_DDID,
262                                              expected_val);
263                 }
264         }
265
266         ret &= check_phy_reg(dev_priv, phy, ICL_PORT_CL_DW5(phy),
267                              CL_POWER_DOWN_ENABLE, CL_POWER_DOWN_ENABLE);
268
269         return ret;
270 }
271
272 void intel_combo_phy_power_up_lanes(struct drm_i915_private *dev_priv,
273                                     enum phy phy, bool is_dsi,
274                                     int lane_count, bool lane_reversal)
275 {
276         u8 lane_mask;
277         u32 val;
278
279         if (is_dsi) {
280                 drm_WARN_ON(&dev_priv->drm, lane_reversal);
281
282                 switch (lane_count) {
283                 case 1:
284                         lane_mask = PWR_DOWN_LN_3_1_0;
285                         break;
286                 case 2:
287                         lane_mask = PWR_DOWN_LN_3_1;
288                         break;
289                 case 3:
290                         lane_mask = PWR_DOWN_LN_3;
291                         break;
292                 default:
293                         MISSING_CASE(lane_count);
294                         fallthrough;
295                 case 4:
296                         lane_mask = PWR_UP_ALL_LANES;
297                         break;
298                 }
299         } else {
300                 switch (lane_count) {
301                 case 1:
302                         lane_mask = lane_reversal ? PWR_DOWN_LN_2_1_0 :
303                                                     PWR_DOWN_LN_3_2_1;
304                         break;
305                 case 2:
306                         lane_mask = lane_reversal ? PWR_DOWN_LN_1_0 :
307                                                     PWR_DOWN_LN_3_2;
308                         break;
309                 default:
310                         MISSING_CASE(lane_count);
311                         fallthrough;
312                 case 4:
313                         lane_mask = PWR_UP_ALL_LANES;
314                         break;
315                 }
316         }
317
318         val = intel_de_read(dev_priv, ICL_PORT_CL_DW10(phy));
319         val &= ~PWR_DOWN_LN_MASK;
320         val |= lane_mask;
321         intel_de_write(dev_priv, ICL_PORT_CL_DW10(phy), val);
322 }
323
324 static void icl_combo_phys_init(struct drm_i915_private *dev_priv)
325 {
326         enum phy phy;
327
328         for_each_combo_phy(dev_priv, phy) {
329                 u32 val;
330
331                 if (icl_combo_phy_verify_state(dev_priv, phy)) {
332                         drm_dbg(&dev_priv->drm,
333                                 "Combo PHY %c already enabled, won't reprogram it.\n",
334                                 phy_name(phy));
335                         continue;
336                 }
337
338                 if (!has_phy_misc(dev_priv, phy))
339                         goto skip_phy_misc;
340
341                 /*
342                  * EHL's combo PHY A can be hooked up to either an external
343                  * display (via DDI-D) or an internal display (via DDI-A or
344                  * the DSI DPHY).  This is a motherboard design decision that
345                  * can't be changed on the fly, so initialize the PHY's mux
346                  * based on whether our VBT indicates the presence of any
347                  * "internal" child devices.
348                  */
349                 val = intel_de_read(dev_priv, ICL_PHY_MISC(phy));
350                 if (IS_JSL_EHL(dev_priv) && phy == PHY_A) {
351                         val &= ~ICL_PHY_MISC_MUX_DDID;
352
353                         if (ehl_vbt_ddi_d_present(dev_priv))
354                                 val |= ICL_PHY_MISC_MUX_DDID;
355                 }
356
357                 val &= ~ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN;
358                 intel_de_write(dev_priv, ICL_PHY_MISC(phy), val);
359
360 skip_phy_misc:
361                 if (DISPLAY_VER(dev_priv) >= 12) {
362                         val = intel_de_read(dev_priv, ICL_PORT_TX_DW8_LN(0, phy));
363                         val &= ~ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_MASK;
364                         val |= ICL_PORT_TX_DW8_ODCC_CLK_SEL;
365                         val |= ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2;
366                         intel_de_write(dev_priv, ICL_PORT_TX_DW8_GRP(phy), val);
367
368                         val = intel_de_read(dev_priv, ICL_PORT_PCS_DW1_LN(0, phy));
369                         val &= ~DCC_MODE_SELECT_MASK;
370                         val |= DCC_MODE_SELECT_CONTINUOSLY;
371                         intel_de_write(dev_priv, ICL_PORT_PCS_DW1_GRP(phy), val);
372                 }
373
374                 icl_set_procmon_ref_values(dev_priv, phy);
375
376                 if (phy_is_master(dev_priv, phy)) {
377                         val = intel_de_read(dev_priv, ICL_PORT_COMP_DW8(phy));
378                         val |= IREFGEN;
379                         intel_de_write(dev_priv, ICL_PORT_COMP_DW8(phy), val);
380                 }
381
382                 val = intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy));
383                 val |= COMP_INIT;
384                 intel_de_write(dev_priv, ICL_PORT_COMP_DW0(phy), val);
385
386                 val = intel_de_read(dev_priv, ICL_PORT_CL_DW5(phy));
387                 val |= CL_POWER_DOWN_ENABLE;
388                 intel_de_write(dev_priv, ICL_PORT_CL_DW5(phy), val);
389         }
390 }
391
392 static void icl_combo_phys_uninit(struct drm_i915_private *dev_priv)
393 {
394         enum phy phy;
395
396         for_each_combo_phy_reverse(dev_priv, phy) {
397                 u32 val;
398
399                 if (phy == PHY_A &&
400                     !icl_combo_phy_verify_state(dev_priv, phy)) {
401                         if (IS_TIGERLAKE(dev_priv) || IS_DG1(dev_priv)) {
402                                 /*
403                                  * A known problem with old ifwi:
404                                  * https://gitlab.freedesktop.org/drm/intel/-/issues/2411
405                                  * Suppress the warning for CI. Remove ASAP!
406                                  */
407                                 drm_dbg_kms(&dev_priv->drm,
408                                             "Combo PHY %c HW state changed unexpectedly\n",
409                                             phy_name(phy));
410                         } else {
411                                 drm_warn(&dev_priv->drm,
412                                          "Combo PHY %c HW state changed unexpectedly\n",
413                                          phy_name(phy));
414                         }
415                 }
416
417                 if (!has_phy_misc(dev_priv, phy))
418                         goto skip_phy_misc;
419
420                 val = intel_de_read(dev_priv, ICL_PHY_MISC(phy));
421                 val |= ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN;
422                 intel_de_write(dev_priv, ICL_PHY_MISC(phy), val);
423
424 skip_phy_misc:
425                 val = intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy));
426                 val &= ~COMP_INIT;
427                 intel_de_write(dev_priv, ICL_PORT_COMP_DW0(phy), val);
428         }
429 }
430
431 void intel_combo_phy_init(struct drm_i915_private *i915)
432 {
433         icl_combo_phys_init(i915);
434 }
435
436 void intel_combo_phy_uninit(struct drm_i915_private *i915)
437 {
438         icl_combo_phys_uninit(i915);
439 }