File: | dev/pci/drm/i915/display/intel_combo_phy.c |
Warning: | line 442, column 33 The left operand of '+' is a garbage value due to array index out of bounds |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | // SPDX-License-Identifier: MIT | |||
2 | /* | |||
3 | * Copyright © 2018 Intel Corporation | |||
4 | */ | |||
5 | ||||
6 | #include "intel_combo_phy.h" | |||
7 | #include "intel_display_types.h" | |||
8 | ||||
9 | #define for_each_combo_phy(__dev_priv, __phy)for ((__phy) = PHY_A; (__phy) < I915_MAX_PHYS; (__phy)++) if (!(intel_phy_is_combo(__dev_priv, __phy))) {} else \ | |||
10 | for ((__phy) = PHY_A; (__phy) < I915_MAX_PHYS; (__phy)++) \ | |||
11 | for_each_if(intel_phy_is_combo(__dev_priv, __phy))if (!(intel_phy_is_combo(__dev_priv, __phy))) {} else | |||
12 | ||||
13 | #define for_each_combo_phy_reverse(__dev_priv, __phy)for ((__phy) = I915_MAX_PHYS; (__phy)-- > PHY_A;) if (!(intel_phy_is_combo (__dev_priv, __phy))) {} else \ | |||
14 | for ((__phy) = I915_MAX_PHYS; (__phy)-- > PHY_A;) \ | |||
15 | for_each_if(intel_phy_is_combo(__dev_priv, __phy))if (!(intel_phy_is_combo(__dev_priv, __phy))) {} else | |||
16 | ||||
17 | enum { | |||
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 | ||||
25 | static 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 gen11 has a set for each combo PHY. | |||
42 | * The CNL registers are equivalent to the gen11 PHY A registers, that's why we | |||
43 | * call the ICL macros even though the function has CNL on its name. | |||
44 | */ | |||
45 | static const struct cnl_procmon * | |||
46 | cnl_get_procmon_ref_values(struct drm_i915_privateinteldrm_softc *dev_priv, enum phy phy) | |||
47 | { | |||
48 | const struct cnl_procmon *procmon; | |||
49 | u32 val; | |||
50 | ||||
51 | val = intel_de_read(dev_priv, ICL_PORT_COMP_DW3(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 0x100 + 4 * (3))) })); | |||
52 | switch (val & (PROCESS_INFO_MASK(7 << 26) | VOLTAGE_INFO_MASK(3 << 24))) { | |||
53 | default: | |||
54 | MISSING_CASE(val)({ int __ret = !!(1); if (__ret) printf("Missing case (%s == %ld)\n" , "val", (long)(val)); __builtin_expect(!!(__ret), 0); }); | |||
55 | fallthroughdo {} while (0); | |||
56 | case VOLTAGE_INFO_0_85V(0 << 24) | PROCESS_INFO_DOT_0(0 << 26): | |||
57 | procmon = &cnl_procmon_values[PROCMON_0_85V_DOT_0]; | |||
58 | break; | |||
59 | case VOLTAGE_INFO_0_95V(1 << 24) | PROCESS_INFO_DOT_0(0 << 26): | |||
60 | procmon = &cnl_procmon_values[PROCMON_0_95V_DOT_0]; | |||
61 | break; | |||
62 | case VOLTAGE_INFO_0_95V(1 << 24) | PROCESS_INFO_DOT_1(1 << 26): | |||
63 | procmon = &cnl_procmon_values[PROCMON_0_95V_DOT_1]; | |||
64 | break; | |||
65 | case VOLTAGE_INFO_1_05V(2 << 24) | PROCESS_INFO_DOT_0(0 << 26): | |||
66 | procmon = &cnl_procmon_values[PROCMON_1_05V_DOT_0]; | |||
67 | break; | |||
68 | case VOLTAGE_INFO_1_05V(2 << 24) | PROCESS_INFO_DOT_1(1 << 26): | |||
69 | procmon = &cnl_procmon_values[PROCMON_1_05V_DOT_1]; | |||
70 | break; | |||
71 | } | |||
72 | ||||
73 | return procmon; | |||
74 | } | |||
75 | ||||
76 | static void cnl_set_procmon_ref_values(struct drm_i915_privateinteldrm_softc *dev_priv, | |||
77 | enum phy phy) | |||
78 | { | |||
79 | const struct cnl_procmon *procmon; | |||
80 | u32 val; | |||
81 | ||||
82 | procmon = cnl_get_procmon_ref_values(dev_priv, phy); | |||
83 | ||||
84 | val = intel_de_read(dev_priv, ICL_PORT_COMP_DW1(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 0x100 + 4 * (1))) })); | |||
85 | val &= ~((0xff << 16) | 0xff); | |||
86 | val |= procmon->dw1; | |||
87 | intel_de_write(dev_priv, ICL_PORT_COMP_DW1(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 0x100 + 4 * (1))) }), val); | |||
88 | ||||
89 | intel_de_write(dev_priv, ICL_PORT_COMP_DW9(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 0x100 + 4 * (9))) }), procmon->dw9); | |||
90 | intel_de_write(dev_priv, ICL_PORT_COMP_DW10(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 0x100 + 4 * (10))) }), procmon->dw10); | |||
91 | } | |||
92 | ||||
93 | static bool_Bool check_phy_reg(struct drm_i915_privateinteldrm_softc *dev_priv, | |||
94 | enum phy phy, i915_reg_t reg, u32 mask, | |||
95 | u32 expected_val) | |||
96 | { | |||
97 | u32 val = intel_de_read(dev_priv, reg); | |||
98 | ||||
99 | if ((val & mask) != expected_val) { | |||
100 | drm_dbg(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Combo PHY %c reg %08x state mismatch: " "current %08x mask %08x expected %08x\n", ((phy) + 'A'), reg .reg, val, mask, expected_val) | |||
101 | "Combo PHY %c reg %08x state mismatch: "drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Combo PHY %c reg %08x state mismatch: " "current %08x mask %08x expected %08x\n", ((phy) + 'A'), reg .reg, val, mask, expected_val) | |||
102 | "current %08x mask %08x expected %08x\n",drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Combo PHY %c reg %08x state mismatch: " "current %08x mask %08x expected %08x\n", ((phy) + 'A'), reg .reg, val, mask, expected_val) | |||
103 | phy_name(phy),drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Combo PHY %c reg %08x state mismatch: " "current %08x mask %08x expected %08x\n", ((phy) + 'A'), reg .reg, val, mask, expected_val) | |||
104 | reg.reg, val, mask, expected_val)drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Combo PHY %c reg %08x state mismatch: " "current %08x mask %08x expected %08x\n", ((phy) + 'A'), reg .reg, val, mask, expected_val); | |||
105 | return false0; | |||
106 | } | |||
107 | ||||
108 | return true1; | |||
109 | } | |||
110 | ||||
111 | static bool_Bool cnl_verify_procmon_ref_values(struct drm_i915_privateinteldrm_softc *dev_priv, | |||
112 | enum phy phy) | |||
113 | { | |||
114 | const struct cnl_procmon *procmon; | |||
115 | bool_Bool ret; | |||
116 | ||||
117 | procmon = cnl_get_procmon_ref_values(dev_priv, phy); | |||
118 | ||||
119 | ret = check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW1(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 0x100 + 4 * (1))) }), | |||
120 | (0xff << 16) | 0xff, procmon->dw1); | |||
121 | ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW9(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 0x100 + 4 * (9))) }), | |||
122 | -1U, procmon->dw9); | |||
123 | ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW10(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 0x100 + 4 * (10))) }), | |||
124 | -1U, procmon->dw10); | |||
125 | ||||
126 | return ret; | |||
127 | } | |||
128 | ||||
129 | static bool_Bool cnl_combo_phy_enabled(struct drm_i915_privateinteldrm_softc *dev_priv) | |||
130 | { | |||
131 | return !(intel_de_read(dev_priv, CHICKEN_MISC_2((const i915_reg_t){ .reg = (0x42084) })) & CNL_COMP_PWR_DOWN(1 << 23)) && | |||
132 | (intel_de_read(dev_priv, CNL_PORT_COMP_DW0((const i915_reg_t){ .reg = (0x162100) })) & COMP_INIT(1 << 31)); | |||
133 | } | |||
134 | ||||
135 | static bool_Bool cnl_combo_phy_verify_state(struct drm_i915_privateinteldrm_softc *dev_priv) | |||
136 | { | |||
137 | enum phy phy = PHY_A; | |||
138 | bool_Bool ret; | |||
139 | ||||
140 | if (!cnl_combo_phy_enabled(dev_priv)) | |||
141 | return false0; | |||
142 | ||||
143 | ret = cnl_verify_procmon_ref_values(dev_priv, phy); | |||
144 | ||||
145 | ret &= check_phy_reg(dev_priv, phy, CNL_PORT_CL1CM_DW5((const i915_reg_t){ .reg = (0x162014) }), | |||
146 | CL_POWER_DOWN_ENABLE(1 << 4), CL_POWER_DOWN_ENABLE(1 << 4)); | |||
147 | ||||
148 | return ret; | |||
149 | } | |||
150 | ||||
151 | static void cnl_combo_phys_init(struct drm_i915_privateinteldrm_softc *dev_priv) | |||
152 | { | |||
153 | u32 val; | |||
154 | ||||
155 | val = intel_de_read(dev_priv, CHICKEN_MISC_2((const i915_reg_t){ .reg = (0x42084) })); | |||
156 | val &= ~CNL_COMP_PWR_DOWN(1 << 23); | |||
157 | intel_de_write(dev_priv, CHICKEN_MISC_2((const i915_reg_t){ .reg = (0x42084) }), val); | |||
158 | ||||
159 | /* Dummy PORT_A to get the correct CNL register from the ICL macro */ | |||
160 | cnl_set_procmon_ref_values(dev_priv, PHY_A); | |||
161 | ||||
162 | val = intel_de_read(dev_priv, CNL_PORT_COMP_DW0((const i915_reg_t){ .reg = (0x162100) })); | |||
163 | val |= COMP_INIT(1 << 31); | |||
164 | intel_de_write(dev_priv, CNL_PORT_COMP_DW0((const i915_reg_t){ .reg = (0x162100) }), val); | |||
165 | ||||
166 | val = intel_de_read(dev_priv, CNL_PORT_CL1CM_DW5((const i915_reg_t){ .reg = (0x162014) })); | |||
167 | val |= CL_POWER_DOWN_ENABLE(1 << 4); | |||
168 | intel_de_write(dev_priv, CNL_PORT_CL1CM_DW5((const i915_reg_t){ .reg = (0x162014) }), val); | |||
169 | } | |||
170 | ||||
171 | static void cnl_combo_phys_uninit(struct drm_i915_privateinteldrm_softc *dev_priv) | |||
172 | { | |||
173 | u32 val; | |||
174 | ||||
175 | if (!cnl_combo_phy_verify_state(dev_priv)) | |||
176 | drm_warn(&dev_priv->drm,printf("drm:pid%d:%s *WARNING* " "[drm] " "Combo PHY HW state changed unexpectedly.\n" , ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc->p_p->ps_pid, __func__) | |||
177 | "Combo PHY HW state changed unexpectedly.\n")printf("drm:pid%d:%s *WARNING* " "[drm] " "Combo PHY HW state changed unexpectedly.\n" , ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc->p_p->ps_pid, __func__); | |||
178 | ||||
179 | val = intel_de_read(dev_priv, CHICKEN_MISC_2((const i915_reg_t){ .reg = (0x42084) })); | |||
180 | val |= CNL_COMP_PWR_DOWN(1 << 23); | |||
181 | intel_de_write(dev_priv, CHICKEN_MISC_2((const i915_reg_t){ .reg = (0x42084) }), val); | |||
182 | } | |||
183 | ||||
184 | static bool_Bool has_phy_misc(struct drm_i915_privateinteldrm_softc *i915, enum phy phy) | |||
185 | { | |||
186 | /* | |||
187 | * Some platforms only expect PHY_MISC to be programmed for PHY-A and | |||
188 | * PHY-B and may not even have instances of the register for the | |||
189 | * other combo PHY's. | |||
190 | */ | |||
191 | if (IS_ELKHARTLAKE(i915)IS_PLATFORM(i915, INTEL_ELKHARTLAKE) || | |||
192 | IS_ROCKETLAKE(i915)IS_PLATFORM(i915, INTEL_ROCKETLAKE)) | |||
193 | return phy < PHY_C; | |||
194 | ||||
195 | return true1; | |||
196 | } | |||
197 | ||||
198 | static bool_Bool icl_combo_phy_enabled(struct drm_i915_privateinteldrm_softc *dev_priv, | |||
199 | enum phy phy) | |||
200 | { | |||
201 | /* The PHY C added by EHL has no PHY_MISC register */ | |||
202 | if (!has_phy_misc(dev_priv, phy)) | |||
203 | return intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 0x100 + 4 * (0))) })) & COMP_INIT(1 << 31); | |||
204 | else | |||
205 | return !(intel_de_read(dev_priv, ICL_PHY_MISC(phy)((const i915_reg_t){ .reg = (((0x64C00) + (phy) * ((0x64C04) - (0x64C00)))) })) & | |||
206 | ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN(1 << 23)) && | |||
207 | (intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 0x100 + 4 * (0))) })) & COMP_INIT(1 << 31)); | |||
208 | } | |||
209 | ||||
210 | static bool_Bool ehl_vbt_ddi_d_present(struct drm_i915_privateinteldrm_softc *i915) | |||
211 | { | |||
212 | bool_Bool ddi_a_present = intel_bios_is_port_present(i915, PORT_A); | |||
213 | bool_Bool ddi_d_present = intel_bios_is_port_present(i915, PORT_D); | |||
214 | bool_Bool dsi_present = intel_bios_is_dsi_present(i915, NULL((void *)0)); | |||
215 | ||||
216 | /* | |||
217 | * VBT's 'dvo port' field for child devices references the DDI, not | |||
218 | * the PHY. So if combo PHY A is wired up to drive an external | |||
219 | * display, we should see a child device present on PORT_D and | |||
220 | * nothing on PORT_A and no DSI. | |||
221 | */ | |||
222 | if (ddi_d_present && !ddi_a_present && !dsi_present) | |||
223 | return true1; | |||
224 | ||||
225 | /* | |||
226 | * If we encounter a VBT that claims to have an external display on | |||
227 | * DDI-D _and_ an internal display on DDI-A/DSI leave an error message | |||
228 | * in the log and let the internal display win. | |||
229 | */ | |||
230 | if (ddi_d_present) | |||
231 | drm_err(&i915->drm,printf("drm:pid%d:%s *ERROR* " "[drm] " "*ERROR* " "VBT claims to have both internal and external displays on PHY A. Configuring for internal.\n" , ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc->p_p->ps_pid, __func__) | |||
232 | "VBT claims to have both internal and external displays on PHY A. Configuring for internal.\n")printf("drm:pid%d:%s *ERROR* " "[drm] " "*ERROR* " "VBT claims to have both internal and external displays on PHY A. Configuring for internal.\n" , ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc->p_p->ps_pid, __func__); | |||
233 | ||||
234 | return false0; | |||
235 | } | |||
236 | ||||
237 | static bool_Bool phy_is_master(struct drm_i915_privateinteldrm_softc *dev_priv, enum phy phy) | |||
238 | { | |||
239 | /* | |||
240 | * Certain PHYs are connected to compensation resistors and act | |||
241 | * as masters to other PHYs. | |||
242 | * | |||
243 | * ICL,TGL: | |||
244 | * A(master) -> B(slave), C(slave) | |||
245 | * RKL: | |||
246 | * A(master) -> B(slave) | |||
247 | * C(master) -> D(slave) | |||
248 | * | |||
249 | * We must set the IREFGEN bit for any PHY acting as a master | |||
250 | * to another PHY. | |||
251 | */ | |||
252 | if (IS_ROCKETLAKE(dev_priv)IS_PLATFORM(dev_priv, INTEL_ROCKETLAKE) && phy == PHY_C) | |||
253 | return true1; | |||
254 | ||||
255 | return phy == PHY_A; | |||
256 | } | |||
257 | ||||
258 | static bool_Bool icl_combo_phy_verify_state(struct drm_i915_privateinteldrm_softc *dev_priv, | |||
259 | enum phy phy) | |||
260 | { | |||
261 | bool_Bool ret = true1; | |||
262 | u32 expected_val = 0; | |||
263 | ||||
264 | if (!icl_combo_phy_enabled(dev_priv, phy)) | |||
265 | return false0; | |||
266 | ||||
267 | if (INTEL_GEN(dev_priv)((&(dev_priv)->__info)->gen) >= 12) { | |||
268 | ret &= check_phy_reg(dev_priv, phy, ICL_PORT_TX_DW8_LN0(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + (0x880 + (0) * 0x100) + 4 * ( 8))) }), | |||
269 | ICL_PORT_TX_DW8_ODCC_CLK_SEL((u32)((1UL << (31)) + 0)) | | |||
270 | ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_MASK((u32)((((~0UL) >> (64 - (30) - 1)) & ((~0UL) << (29))) + 0)), | |||
271 | ICL_PORT_TX_DW8_ODCC_CLK_SEL((u32)((1UL << (31)) + 0)) | | |||
272 | ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2((u32)((((typeof(((u32)((((~0UL) >> (64 - (30) - 1)) & ((~0UL) << (29))) + 0))))(0x1) << (__builtin_ffsll (((u32)((((~0UL) >> (64 - (30) - 1)) & ((~0UL) << (29))) + 0))) - 1)) & (((u32)((((~0UL) >> (64 - (30 ) - 1)) & ((~0UL) << (29))) + 0)))) + 0 + 0 + 0 + 0 ))); | |||
273 | ||||
274 | ret &= check_phy_reg(dev_priv, phy, ICL_PORT_PCS_DW1_LN0(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + (0x800 + (0) * 0x100) + 4 * ( 1))) }), | |||
275 | DCC_MODE_SELECT_MASK(0x3 << 20), | |||
276 | DCC_MODE_SELECT_CONTINUOSLY(0x3 << 20)); | |||
277 | } | |||
278 | ||||
279 | ret &= cnl_verify_procmon_ref_values(dev_priv, phy); | |||
280 | ||||
281 | if (phy_is_master(dev_priv, phy)) { | |||
282 | ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW8(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 0x100 + 4 * (8))) }), | |||
283 | IREFGEN(1 << 24), IREFGEN(1 << 24)); | |||
284 | ||||
285 | if (IS_ELKHARTLAKE(dev_priv)IS_PLATFORM(dev_priv, INTEL_ELKHARTLAKE)) { | |||
286 | if (ehl_vbt_ddi_d_present(dev_priv)) | |||
287 | expected_val = ICL_PHY_MISC_MUX_DDID(1 << 28); | |||
288 | ||||
289 | ret &= check_phy_reg(dev_priv, phy, ICL_PHY_MISC(phy)((const i915_reg_t){ .reg = (((0x64C00) + (phy) * ((0x64C04) - (0x64C00)))) }), | |||
290 | ICL_PHY_MISC_MUX_DDID(1 << 28), | |||
291 | expected_val); | |||
292 | } | |||
293 | } | |||
294 | ||||
295 | ret &= check_phy_reg(dev_priv, phy, ICL_PORT_CL_DW5(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 4 * (5))) }), | |||
296 | CL_POWER_DOWN_ENABLE(1 << 4), CL_POWER_DOWN_ENABLE(1 << 4)); | |||
297 | ||||
298 | return ret; | |||
299 | } | |||
300 | ||||
301 | void intel_combo_phy_power_up_lanes(struct drm_i915_privateinteldrm_softc *dev_priv, | |||
302 | enum phy phy, bool_Bool is_dsi, | |||
303 | int lane_count, bool_Bool lane_reversal) | |||
304 | { | |||
305 | u8 lane_mask; | |||
306 | u32 val; | |||
307 | ||||
308 | if (is_dsi) { | |||
309 | drm_WARN_ON(&dev_priv->drm, lane_reversal)({ int __ret = !!((lane_reversal)); if (__ret) printf("%s %s: " "%s", dev_driver_string(((&dev_priv->drm))->dev), "" , "drm_WARN_ON(" "lane_reversal" ")"); __builtin_expect(!!(__ret ), 0); }); | |||
310 | ||||
311 | switch (lane_count) { | |||
312 | case 1: | |||
313 | lane_mask = PWR_DOWN_LN_3_1_0(0xb << 4); | |||
314 | break; | |||
315 | case 2: | |||
316 | lane_mask = PWR_DOWN_LN_3_1(0xa << 4); | |||
317 | break; | |||
318 | case 3: | |||
319 | lane_mask = PWR_DOWN_LN_3(0x8 << 4); | |||
320 | break; | |||
321 | default: | |||
322 | MISSING_CASE(lane_count)({ int __ret = !!(1); if (__ret) printf("Missing case (%s == %ld)\n" , "lane_count", (long)(lane_count)); __builtin_expect(!!(__ret ), 0); }); | |||
323 | fallthroughdo {} while (0); | |||
324 | case 4: | |||
325 | lane_mask = PWR_UP_ALL_LANES(0x0 << 4); | |||
326 | break; | |||
327 | } | |||
328 | } else { | |||
329 | switch (lane_count) { | |||
330 | case 1: | |||
331 | lane_mask = lane_reversal ? PWR_DOWN_LN_2_1_0(0x7 << 4) : | |||
332 | PWR_DOWN_LN_3_2_1(0xe << 4); | |||
333 | break; | |||
334 | case 2: | |||
335 | lane_mask = lane_reversal ? PWR_DOWN_LN_1_0(0x3 << 4) : | |||
336 | PWR_DOWN_LN_3_2(0xc << 4); | |||
337 | break; | |||
338 | default: | |||
339 | MISSING_CASE(lane_count)({ int __ret = !!(1); if (__ret) printf("Missing case (%s == %ld)\n" , "lane_count", (long)(lane_count)); __builtin_expect(!!(__ret ), 0); }); | |||
340 | fallthroughdo {} while (0); | |||
341 | case 4: | |||
342 | lane_mask = PWR_UP_ALL_LANES(0x0 << 4); | |||
343 | break; | |||
344 | } | |||
345 | } | |||
346 | ||||
347 | val = intel_de_read(dev_priv, ICL_PORT_CL_DW10(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 4 * (10))) })); | |||
348 | val &= ~PWR_DOWN_LN_MASK(0xf << 4); | |||
349 | val |= lane_mask << PWR_DOWN_LN_SHIFT4; | |||
350 | intel_de_write(dev_priv, ICL_PORT_CL_DW10(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 4 * (10))) }), val); | |||
351 | } | |||
352 | ||||
353 | static void icl_combo_phys_init(struct drm_i915_privateinteldrm_softc *dev_priv) | |||
354 | { | |||
355 | enum phy phy; | |||
356 | ||||
357 | for_each_combo_phy(dev_priv, phy)for ((phy) = PHY_A; (phy) < I915_MAX_PHYS; (phy)++) if (!( intel_phy_is_combo(dev_priv, phy))) {} else { | |||
358 | u32 val; | |||
359 | ||||
360 | if (icl_combo_phy_verify_state(dev_priv, phy)) { | |||
361 | drm_dbg(&dev_priv->drm,drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Combo PHY %c already enabled, won't reprogram it.\n" , ((phy) + 'A')) | |||
362 | "Combo PHY %c already enabled, won't reprogram it.\n",drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Combo PHY %c already enabled, won't reprogram it.\n" , ((phy) + 'A')) | |||
363 | phy_name(phy))drm_dev_dbg((&dev_priv->drm)->dev, DRM_UT_DRIVER, "Combo PHY %c already enabled, won't reprogram it.\n" , ((phy) + 'A')); | |||
364 | continue; | |||
365 | } | |||
366 | ||||
367 | if (!has_phy_misc(dev_priv, phy)) | |||
368 | goto skip_phy_misc; | |||
369 | ||||
370 | /* | |||
371 | * EHL's combo PHY A can be hooked up to either an external | |||
372 | * display (via DDI-D) or an internal display (via DDI-A or | |||
373 | * the DSI DPHY). This is a motherboard design decision that | |||
374 | * can't be changed on the fly, so initialize the PHY's mux | |||
375 | * based on whether our VBT indicates the presence of any | |||
376 | * "internal" child devices. | |||
377 | */ | |||
378 | val = intel_de_read(dev_priv, ICL_PHY_MISC(phy)((const i915_reg_t){ .reg = (((0x64C00) + (phy) * ((0x64C04) - (0x64C00)))) })); | |||
379 | if (IS_ELKHARTLAKE(dev_priv)IS_PLATFORM(dev_priv, INTEL_ELKHARTLAKE) && phy == PHY_A) { | |||
380 | val &= ~ICL_PHY_MISC_MUX_DDID(1 << 28); | |||
381 | ||||
382 | if (ehl_vbt_ddi_d_present(dev_priv)) | |||
383 | val |= ICL_PHY_MISC_MUX_DDID(1 << 28); | |||
384 | } | |||
385 | ||||
386 | val &= ~ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN(1 << 23); | |||
387 | intel_de_write(dev_priv, ICL_PHY_MISC(phy)((const i915_reg_t){ .reg = (((0x64C00) + (phy) * ((0x64C04) - (0x64C00)))) }), val); | |||
388 | ||||
389 | skip_phy_misc: | |||
390 | if (INTEL_GEN(dev_priv)((&(dev_priv)->__info)->gen) >= 12) { | |||
391 | val = intel_de_read(dev_priv, ICL_PORT_TX_DW8_LN0(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + (0x880 + (0) * 0x100) + 4 * ( 8))) })); | |||
392 | val &= ~ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_MASK((u32)((((~0UL) >> (64 - (30) - 1)) & ((~0UL) << (29))) + 0)); | |||
393 | val |= ICL_PORT_TX_DW8_ODCC_CLK_SEL((u32)((1UL << (31)) + 0)); | |||
394 | val |= ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2((u32)((((typeof(((u32)((((~0UL) >> (64 - (30) - 1)) & ((~0UL) << (29))) + 0))))(0x1) << (__builtin_ffsll (((u32)((((~0UL) >> (64 - (30) - 1)) & ((~0UL) << (29))) + 0))) - 1)) & (((u32)((((~0UL) >> (64 - (30 ) - 1)) & ((~0UL) << (29))) + 0)))) + 0 + 0 + 0 + 0 )); | |||
395 | intel_de_write(dev_priv, ICL_PORT_TX_DW8_GRP(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 0x680 + 4 * (8))) }), val); | |||
396 | ||||
397 | val = intel_de_read(dev_priv, ICL_PORT_PCS_DW1_LN0(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + (0x800 + (0) * 0x100) + 4 * ( 1))) })); | |||
398 | val &= ~DCC_MODE_SELECT_MASK(0x3 << 20); | |||
399 | val |= DCC_MODE_SELECT_CONTINUOSLY(0x3 << 20); | |||
400 | intel_de_write(dev_priv, ICL_PORT_PCS_DW1_GRP(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 0x600 + 4 * (1))) }), val); | |||
401 | } | |||
402 | ||||
403 | cnl_set_procmon_ref_values(dev_priv, phy); | |||
404 | ||||
405 | if (phy_is_master(dev_priv, phy)) { | |||
406 | val = intel_de_read(dev_priv, ICL_PORT_COMP_DW8(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 0x100 + 4 * (8))) })); | |||
407 | val |= IREFGEN(1 << 24); | |||
408 | intel_de_write(dev_priv, ICL_PORT_COMP_DW8(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 0x100 + 4 * (8))) }), val); | |||
409 | } | |||
410 | ||||
411 | val = intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 0x100 + 4 * (0))) })); | |||
412 | val |= COMP_INIT(1 << 31); | |||
413 | intel_de_write(dev_priv, ICL_PORT_COMP_DW0(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 0x100 + 4 * (0))) }), val); | |||
414 | ||||
415 | val = intel_de_read(dev_priv, ICL_PORT_CL_DW5(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 4 * (5))) })); | |||
416 | val |= CL_POWER_DOWN_ENABLE(1 << 4); | |||
417 | intel_de_write(dev_priv, ICL_PORT_CL_DW5(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 4 * (5))) }), val); | |||
418 | } | |||
419 | } | |||
420 | ||||
421 | static void icl_combo_phys_uninit(struct drm_i915_privateinteldrm_softc *dev_priv) | |||
422 | { | |||
423 | enum phy phy; | |||
424 | ||||
425 | for_each_combo_phy_reverse(dev_priv, phy)for ((phy) = I915_MAX_PHYS; (phy)-- > PHY_A;) if (!(intel_phy_is_combo (dev_priv, phy))) {} else { | |||
426 | u32 val; | |||
427 | ||||
428 | if (phy
| |||
429 | !icl_combo_phy_verify_state(dev_priv, phy)) | |||
430 | drm_warn(&dev_priv->drm,printf("drm:pid%d:%s *WARNING* " "[drm] " "Combo PHY %c HW state changed unexpectedly\n" , ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc->p_p->ps_pid, __func__ , ((phy) + 'A')) | |||
431 | "Combo PHY %c HW state changed unexpectedly\n",printf("drm:pid%d:%s *WARNING* " "[drm] " "Combo PHY %c HW state changed unexpectedly\n" , ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc->p_p->ps_pid, __func__ , ((phy) + 'A')) | |||
432 | phy_name(phy))printf("drm:pid%d:%s *WARNING* " "[drm] " "Combo PHY %c HW state changed unexpectedly\n" , ({struct cpu_info *__ci; asm volatile("movq %%gs:%P1,%0" : "=r" (__ci) :"n" (__builtin_offsetof(struct cpu_info, ci_self))); __ci;})->ci_curproc->p_p->ps_pid, __func__ , ((phy) + 'A')); | |||
433 | ||||
434 | if (!has_phy_misc(dev_priv, phy)) | |||
435 | goto skip_phy_misc; | |||
436 | ||||
437 | val = intel_de_read(dev_priv, ICL_PHY_MISC(phy)((const i915_reg_t){ .reg = (((0x64C00) + (phy) * ((0x64C04) - (0x64C00)))) })); | |||
438 | val |= ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN(1 << 23); | |||
439 | intel_de_write(dev_priv, ICL_PHY_MISC(phy)((const i915_reg_t){ .reg = (((0x64C00) + (phy) * ((0x64C04) - (0x64C00)))) }), val); | |||
440 | ||||
441 | skip_phy_misc: | |||
442 | val = intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 0x100 + 4 * (0))) })); | |||
| ||||
443 | val &= ~COMP_INIT(1 << 31); | |||
444 | intel_de_write(dev_priv, ICL_PORT_COMP_DW0(phy)((const i915_reg_t){ .reg = (((((const u32 []){ 0x162000, 0x6C000 , 0x160000, 0x161000 })[phy]) + 0x100 + 4 * (0))) }), val); | |||
445 | } | |||
446 | } | |||
447 | ||||
448 | void intel_combo_phy_init(struct drm_i915_privateinteldrm_softc *i915) | |||
449 | { | |||
450 | if (INTEL_GEN(i915)((&(i915)->__info)->gen) >= 11) | |||
451 | icl_combo_phys_init(i915); | |||
452 | else if (IS_CANNONLAKE(i915)IS_PLATFORM(i915, INTEL_CANNONLAKE)) | |||
453 | cnl_combo_phys_init(i915); | |||
454 | } | |||
455 | ||||
456 | void intel_combo_phy_uninit(struct drm_i915_privateinteldrm_softc *i915) | |||
457 | { | |||
458 | if (INTEL_GEN(i915)((&(i915)->__info)->gen) >= 11) | |||
| ||||
459 | icl_combo_phys_uninit(i915); | |||
460 | else if (IS_CANNONLAKE(i915)IS_PLATFORM(i915, INTEL_CANNONLAKE)) | |||
461 | cnl_combo_phys_uninit(i915); | |||
462 | } |