1 | // SPDX-License-Identifier: MIT |
2 | /* |
3 | * Copyright © 2021 Intel Corporation |
4 | */ |
5 | |
6 | #include "i915_reg.h" |
7 | #include "intel_de.h" |
8 | #include "intel_display_types.h" |
9 | #include "intel_panel.h" |
10 | #include "intel_pch_refclk.h" |
11 | #include "intel_sbi.h" |
12 | |
13 | static void lpt_fdi_reset_mphy(struct drm_i915_private *dev_priv) |
14 | { |
15 | intel_de_rmw(i915: dev_priv, SOUTH_CHICKEN2, clear: 0, FDI_MPHY_IOSFSB_RESET_CTL); |
16 | |
17 | if (wait_for_us(intel_de_read(dev_priv, SOUTH_CHICKEN2) & |
18 | FDI_MPHY_IOSFSB_RESET_STATUS, 100)) |
19 | drm_err(&dev_priv->drm, "FDI mPHY reset assert timeout\n" ); |
20 | |
21 | intel_de_rmw(i915: dev_priv, SOUTH_CHICKEN2, FDI_MPHY_IOSFSB_RESET_CTL, set: 0); |
22 | |
23 | if (wait_for_us((intel_de_read(dev_priv, SOUTH_CHICKEN2) & |
24 | FDI_MPHY_IOSFSB_RESET_STATUS) == 0, 100)) |
25 | drm_err(&dev_priv->drm, "FDI mPHY reset de-assert timeout\n" ); |
26 | } |
27 | |
28 | /* WaMPhyProgramming:hsw */ |
29 | static void lpt_fdi_program_mphy(struct drm_i915_private *dev_priv) |
30 | { |
31 | u32 tmp; |
32 | |
33 | lpt_fdi_reset_mphy(dev_priv); |
34 | |
35 | tmp = intel_sbi_read(i915: dev_priv, reg: 0x8008, destination: SBI_MPHY); |
36 | tmp &= ~(0xFF << 24); |
37 | tmp |= (0x12 << 24); |
38 | intel_sbi_write(i915: dev_priv, reg: 0x8008, value: tmp, destination: SBI_MPHY); |
39 | |
40 | tmp = intel_sbi_read(i915: dev_priv, reg: 0x2008, destination: SBI_MPHY); |
41 | tmp |= (1 << 11); |
42 | intel_sbi_write(i915: dev_priv, reg: 0x2008, value: tmp, destination: SBI_MPHY); |
43 | |
44 | tmp = intel_sbi_read(i915: dev_priv, reg: 0x2108, destination: SBI_MPHY); |
45 | tmp |= (1 << 11); |
46 | intel_sbi_write(i915: dev_priv, reg: 0x2108, value: tmp, destination: SBI_MPHY); |
47 | |
48 | tmp = intel_sbi_read(i915: dev_priv, reg: 0x206C, destination: SBI_MPHY); |
49 | tmp |= (1 << 24) | (1 << 21) | (1 << 18); |
50 | intel_sbi_write(i915: dev_priv, reg: 0x206C, value: tmp, destination: SBI_MPHY); |
51 | |
52 | tmp = intel_sbi_read(i915: dev_priv, reg: 0x216C, destination: SBI_MPHY); |
53 | tmp |= (1 << 24) | (1 << 21) | (1 << 18); |
54 | intel_sbi_write(i915: dev_priv, reg: 0x216C, value: tmp, destination: SBI_MPHY); |
55 | |
56 | tmp = intel_sbi_read(i915: dev_priv, reg: 0x2080, destination: SBI_MPHY); |
57 | tmp &= ~(7 << 13); |
58 | tmp |= (5 << 13); |
59 | intel_sbi_write(i915: dev_priv, reg: 0x2080, value: tmp, destination: SBI_MPHY); |
60 | |
61 | tmp = intel_sbi_read(i915: dev_priv, reg: 0x2180, destination: SBI_MPHY); |
62 | tmp &= ~(7 << 13); |
63 | tmp |= (5 << 13); |
64 | intel_sbi_write(i915: dev_priv, reg: 0x2180, value: tmp, destination: SBI_MPHY); |
65 | |
66 | tmp = intel_sbi_read(i915: dev_priv, reg: 0x208C, destination: SBI_MPHY); |
67 | tmp &= ~0xFF; |
68 | tmp |= 0x1C; |
69 | intel_sbi_write(i915: dev_priv, reg: 0x208C, value: tmp, destination: SBI_MPHY); |
70 | |
71 | tmp = intel_sbi_read(i915: dev_priv, reg: 0x218C, destination: SBI_MPHY); |
72 | tmp &= ~0xFF; |
73 | tmp |= 0x1C; |
74 | intel_sbi_write(i915: dev_priv, reg: 0x218C, value: tmp, destination: SBI_MPHY); |
75 | |
76 | tmp = intel_sbi_read(i915: dev_priv, reg: 0x2098, destination: SBI_MPHY); |
77 | tmp &= ~(0xFF << 16); |
78 | tmp |= (0x1C << 16); |
79 | intel_sbi_write(i915: dev_priv, reg: 0x2098, value: tmp, destination: SBI_MPHY); |
80 | |
81 | tmp = intel_sbi_read(i915: dev_priv, reg: 0x2198, destination: SBI_MPHY); |
82 | tmp &= ~(0xFF << 16); |
83 | tmp |= (0x1C << 16); |
84 | intel_sbi_write(i915: dev_priv, reg: 0x2198, value: tmp, destination: SBI_MPHY); |
85 | |
86 | tmp = intel_sbi_read(i915: dev_priv, reg: 0x20C4, destination: SBI_MPHY); |
87 | tmp |= (1 << 27); |
88 | intel_sbi_write(i915: dev_priv, reg: 0x20C4, value: tmp, destination: SBI_MPHY); |
89 | |
90 | tmp = intel_sbi_read(i915: dev_priv, reg: 0x21C4, destination: SBI_MPHY); |
91 | tmp |= (1 << 27); |
92 | intel_sbi_write(i915: dev_priv, reg: 0x21C4, value: tmp, destination: SBI_MPHY); |
93 | |
94 | tmp = intel_sbi_read(i915: dev_priv, reg: 0x20EC, destination: SBI_MPHY); |
95 | tmp &= ~(0xF << 28); |
96 | tmp |= (4 << 28); |
97 | intel_sbi_write(i915: dev_priv, reg: 0x20EC, value: tmp, destination: SBI_MPHY); |
98 | |
99 | tmp = intel_sbi_read(i915: dev_priv, reg: 0x21EC, destination: SBI_MPHY); |
100 | tmp &= ~(0xF << 28); |
101 | tmp |= (4 << 28); |
102 | intel_sbi_write(i915: dev_priv, reg: 0x21EC, value: tmp, destination: SBI_MPHY); |
103 | } |
104 | |
105 | void lpt_disable_iclkip(struct drm_i915_private *dev_priv) |
106 | { |
107 | u32 temp; |
108 | |
109 | intel_de_write(i915: dev_priv, PIXCLK_GATE, PIXCLK_GATE_GATE); |
110 | |
111 | mutex_lock(&dev_priv->sb_lock); |
112 | |
113 | temp = intel_sbi_read(i915: dev_priv, SBI_SSCCTL6, destination: SBI_ICLK); |
114 | temp |= SBI_SSCCTL_DISABLE; |
115 | intel_sbi_write(i915: dev_priv, SBI_SSCCTL6, value: temp, destination: SBI_ICLK); |
116 | |
117 | mutex_unlock(lock: &dev_priv->sb_lock); |
118 | } |
119 | |
120 | struct iclkip_params { |
121 | u32 iclk_virtual_root_freq; |
122 | u32 iclk_pi_range; |
123 | u32 divsel, phaseinc, auxdiv, phasedir, desired_divisor; |
124 | }; |
125 | |
126 | static void iclkip_params_init(struct iclkip_params *p) |
127 | { |
128 | memset(p, 0, sizeof(*p)); |
129 | |
130 | p->iclk_virtual_root_freq = 172800 * 1000; |
131 | p->iclk_pi_range = 64; |
132 | } |
133 | |
134 | static int lpt_iclkip_freq(struct iclkip_params *p) |
135 | { |
136 | return DIV_ROUND_CLOSEST(p->iclk_virtual_root_freq, |
137 | p->desired_divisor << p->auxdiv); |
138 | } |
139 | |
140 | static void lpt_compute_iclkip(struct iclkip_params *p, int clock) |
141 | { |
142 | iclkip_params_init(p); |
143 | |
144 | /* The iCLK virtual clock root frequency is in MHz, |
145 | * but the adjusted_mode->crtc_clock in KHz. To get the |
146 | * divisors, it is necessary to divide one by another, so we |
147 | * convert the virtual clock precision to KHz here for higher |
148 | * precision. |
149 | */ |
150 | for (p->auxdiv = 0; p->auxdiv < 2; p->auxdiv++) { |
151 | p->desired_divisor = DIV_ROUND_CLOSEST(p->iclk_virtual_root_freq, |
152 | clock << p->auxdiv); |
153 | p->divsel = (p->desired_divisor / p->iclk_pi_range) - 2; |
154 | p->phaseinc = p->desired_divisor % p->iclk_pi_range; |
155 | |
156 | /* |
157 | * Near 20MHz is a corner case which is |
158 | * out of range for the 7-bit divisor |
159 | */ |
160 | if (p->divsel <= 0x7f) |
161 | break; |
162 | } |
163 | } |
164 | |
165 | int lpt_iclkip(const struct intel_crtc_state *crtc_state) |
166 | { |
167 | struct iclkip_params p; |
168 | |
169 | lpt_compute_iclkip(p: &p, clock: crtc_state->hw.adjusted_mode.crtc_clock); |
170 | |
171 | return lpt_iclkip_freq(p: &p); |
172 | } |
173 | |
174 | /* Program iCLKIP clock to the desired frequency */ |
175 | void lpt_program_iclkip(const struct intel_crtc_state *crtc_state) |
176 | { |
177 | struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); |
178 | struct drm_i915_private *dev_priv = to_i915(dev: crtc->base.dev); |
179 | int clock = crtc_state->hw.adjusted_mode.crtc_clock; |
180 | struct iclkip_params p; |
181 | u32 temp; |
182 | |
183 | lpt_disable_iclkip(dev_priv); |
184 | |
185 | lpt_compute_iclkip(p: &p, clock); |
186 | drm_WARN_ON(&dev_priv->drm, lpt_iclkip_freq(&p) != clock); |
187 | |
188 | /* This should not happen with any sane values */ |
189 | drm_WARN_ON(&dev_priv->drm, SBI_SSCDIVINTPHASE_DIVSEL(p.divsel) & |
190 | ~SBI_SSCDIVINTPHASE_DIVSEL_MASK); |
191 | drm_WARN_ON(&dev_priv->drm, SBI_SSCDIVINTPHASE_DIR(p.phasedir) & |
192 | ~SBI_SSCDIVINTPHASE_INCVAL_MASK); |
193 | |
194 | drm_dbg_kms(&dev_priv->drm, |
195 | "iCLKIP clock: found settings for %dKHz refresh rate: auxdiv=%x, divsel=%x, phasedir=%x, phaseinc=%x\n" , |
196 | clock, p.auxdiv, p.divsel, p.phasedir, p.phaseinc); |
197 | |
198 | mutex_lock(&dev_priv->sb_lock); |
199 | |
200 | /* Program SSCDIVINTPHASE6 */ |
201 | temp = intel_sbi_read(i915: dev_priv, SBI_SSCDIVINTPHASE6, destination: SBI_ICLK); |
202 | temp &= ~SBI_SSCDIVINTPHASE_DIVSEL_MASK; |
203 | temp |= SBI_SSCDIVINTPHASE_DIVSEL(p.divsel); |
204 | temp &= ~SBI_SSCDIVINTPHASE_INCVAL_MASK; |
205 | temp |= SBI_SSCDIVINTPHASE_INCVAL(p.phaseinc); |
206 | temp |= SBI_SSCDIVINTPHASE_DIR(p.phasedir); |
207 | temp |= SBI_SSCDIVINTPHASE_PROPAGATE; |
208 | intel_sbi_write(i915: dev_priv, SBI_SSCDIVINTPHASE6, value: temp, destination: SBI_ICLK); |
209 | |
210 | /* Program SSCAUXDIV */ |
211 | temp = intel_sbi_read(i915: dev_priv, SBI_SSCAUXDIV6, destination: SBI_ICLK); |
212 | temp &= ~SBI_SSCAUXDIV_FINALDIV2SEL(1); |
213 | temp |= SBI_SSCAUXDIV_FINALDIV2SEL(p.auxdiv); |
214 | intel_sbi_write(i915: dev_priv, SBI_SSCAUXDIV6, value: temp, destination: SBI_ICLK); |
215 | |
216 | /* Enable modulator and associated divider */ |
217 | temp = intel_sbi_read(i915: dev_priv, SBI_SSCCTL6, destination: SBI_ICLK); |
218 | temp &= ~SBI_SSCCTL_DISABLE; |
219 | intel_sbi_write(i915: dev_priv, SBI_SSCCTL6, value: temp, destination: SBI_ICLK); |
220 | |
221 | mutex_unlock(lock: &dev_priv->sb_lock); |
222 | |
223 | /* Wait for initialization time */ |
224 | udelay(24); |
225 | |
226 | intel_de_write(i915: dev_priv, PIXCLK_GATE, PIXCLK_GATE_UNGATE); |
227 | } |
228 | |
229 | int lpt_get_iclkip(struct drm_i915_private *dev_priv) |
230 | { |
231 | struct iclkip_params p; |
232 | u32 temp; |
233 | |
234 | if ((intel_de_read(i915: dev_priv, PIXCLK_GATE) & PIXCLK_GATE_UNGATE) == 0) |
235 | return 0; |
236 | |
237 | iclkip_params_init(p: &p); |
238 | |
239 | mutex_lock(&dev_priv->sb_lock); |
240 | |
241 | temp = intel_sbi_read(i915: dev_priv, SBI_SSCCTL6, destination: SBI_ICLK); |
242 | if (temp & SBI_SSCCTL_DISABLE) { |
243 | mutex_unlock(lock: &dev_priv->sb_lock); |
244 | return 0; |
245 | } |
246 | |
247 | temp = intel_sbi_read(i915: dev_priv, SBI_SSCDIVINTPHASE6, destination: SBI_ICLK); |
248 | p.divsel = (temp & SBI_SSCDIVINTPHASE_DIVSEL_MASK) >> |
249 | SBI_SSCDIVINTPHASE_DIVSEL_SHIFT; |
250 | p.phaseinc = (temp & SBI_SSCDIVINTPHASE_INCVAL_MASK) >> |
251 | SBI_SSCDIVINTPHASE_INCVAL_SHIFT; |
252 | |
253 | temp = intel_sbi_read(i915: dev_priv, SBI_SSCAUXDIV6, destination: SBI_ICLK); |
254 | p.auxdiv = (temp & SBI_SSCAUXDIV_FINALDIV2SEL_MASK) >> |
255 | SBI_SSCAUXDIV_FINALDIV2SEL_SHIFT; |
256 | |
257 | mutex_unlock(lock: &dev_priv->sb_lock); |
258 | |
259 | p.desired_divisor = (p.divsel + 2) * p.iclk_pi_range + p.phaseinc; |
260 | |
261 | return lpt_iclkip_freq(p: &p); |
262 | } |
263 | |
264 | /* Implements 3 different sequences from BSpec chapter "Display iCLK |
265 | * Programming" based on the parameters passed: |
266 | * - Sequence to enable CLKOUT_DP |
267 | * - Sequence to enable CLKOUT_DP without spread |
268 | * - Sequence to enable CLKOUT_DP for FDI usage and configure PCH FDI I/O |
269 | */ |
270 | static void lpt_enable_clkout_dp(struct drm_i915_private *dev_priv, |
271 | bool with_spread, bool with_fdi) |
272 | { |
273 | u32 reg, tmp; |
274 | |
275 | if (drm_WARN(&dev_priv->drm, with_fdi && !with_spread, |
276 | "FDI requires downspread\n" )) |
277 | with_spread = true; |
278 | if (drm_WARN(&dev_priv->drm, HAS_PCH_LPT_LP(dev_priv) && |
279 | with_fdi, "LP PCH doesn't have FDI\n" )) |
280 | with_fdi = false; |
281 | |
282 | mutex_lock(&dev_priv->sb_lock); |
283 | |
284 | tmp = intel_sbi_read(i915: dev_priv, SBI_SSCCTL, destination: SBI_ICLK); |
285 | tmp &= ~SBI_SSCCTL_DISABLE; |
286 | tmp |= SBI_SSCCTL_PATHALT; |
287 | intel_sbi_write(i915: dev_priv, SBI_SSCCTL, value: tmp, destination: SBI_ICLK); |
288 | |
289 | udelay(24); |
290 | |
291 | if (with_spread) { |
292 | tmp = intel_sbi_read(i915: dev_priv, SBI_SSCCTL, destination: SBI_ICLK); |
293 | tmp &= ~SBI_SSCCTL_PATHALT; |
294 | intel_sbi_write(i915: dev_priv, SBI_SSCCTL, value: tmp, destination: SBI_ICLK); |
295 | |
296 | if (with_fdi) |
297 | lpt_fdi_program_mphy(dev_priv); |
298 | } |
299 | |
300 | reg = HAS_PCH_LPT_LP(dev_priv) ? SBI_GEN0 : SBI_DBUFF0; |
301 | tmp = intel_sbi_read(i915: dev_priv, reg, destination: SBI_ICLK); |
302 | tmp |= SBI_GEN0_CFG_BUFFENABLE_DISABLE; |
303 | intel_sbi_write(i915: dev_priv, reg, value: tmp, destination: SBI_ICLK); |
304 | |
305 | mutex_unlock(lock: &dev_priv->sb_lock); |
306 | } |
307 | |
308 | /* Sequence to disable CLKOUT_DP */ |
309 | void lpt_disable_clkout_dp(struct drm_i915_private *dev_priv) |
310 | { |
311 | u32 reg, tmp; |
312 | |
313 | mutex_lock(&dev_priv->sb_lock); |
314 | |
315 | reg = HAS_PCH_LPT_LP(dev_priv) ? SBI_GEN0 : SBI_DBUFF0; |
316 | tmp = intel_sbi_read(i915: dev_priv, reg, destination: SBI_ICLK); |
317 | tmp &= ~SBI_GEN0_CFG_BUFFENABLE_DISABLE; |
318 | intel_sbi_write(i915: dev_priv, reg, value: tmp, destination: SBI_ICLK); |
319 | |
320 | tmp = intel_sbi_read(i915: dev_priv, SBI_SSCCTL, destination: SBI_ICLK); |
321 | if (!(tmp & SBI_SSCCTL_DISABLE)) { |
322 | if (!(tmp & SBI_SSCCTL_PATHALT)) { |
323 | tmp |= SBI_SSCCTL_PATHALT; |
324 | intel_sbi_write(i915: dev_priv, SBI_SSCCTL, value: tmp, destination: SBI_ICLK); |
325 | udelay(32); |
326 | } |
327 | tmp |= SBI_SSCCTL_DISABLE; |
328 | intel_sbi_write(i915: dev_priv, SBI_SSCCTL, value: tmp, destination: SBI_ICLK); |
329 | } |
330 | |
331 | mutex_unlock(lock: &dev_priv->sb_lock); |
332 | } |
333 | |
334 | #define BEND_IDX(steps) ((50 + (steps)) / 5) |
335 | |
336 | static const u16 sscdivintphase[] = { |
337 | [BEND_IDX( 50)] = 0x3B23, |
338 | [BEND_IDX( 45)] = 0x3B23, |
339 | [BEND_IDX( 40)] = 0x3C23, |
340 | [BEND_IDX( 35)] = 0x3C23, |
341 | [BEND_IDX( 30)] = 0x3D23, |
342 | [BEND_IDX( 25)] = 0x3D23, |
343 | [BEND_IDX( 20)] = 0x3E23, |
344 | [BEND_IDX( 15)] = 0x3E23, |
345 | [BEND_IDX( 10)] = 0x3F23, |
346 | [BEND_IDX( 5)] = 0x3F23, |
347 | [BEND_IDX( 0)] = 0x0025, |
348 | [BEND_IDX( -5)] = 0x0025, |
349 | [BEND_IDX(-10)] = 0x0125, |
350 | [BEND_IDX(-15)] = 0x0125, |
351 | [BEND_IDX(-20)] = 0x0225, |
352 | [BEND_IDX(-25)] = 0x0225, |
353 | [BEND_IDX(-30)] = 0x0325, |
354 | [BEND_IDX(-35)] = 0x0325, |
355 | [BEND_IDX(-40)] = 0x0425, |
356 | [BEND_IDX(-45)] = 0x0425, |
357 | [BEND_IDX(-50)] = 0x0525, |
358 | }; |
359 | |
360 | /* |
361 | * Bend CLKOUT_DP |
362 | * steps -50 to 50 inclusive, in steps of 5 |
363 | * < 0 slow down the clock, > 0 speed up the clock, 0 == no bend (135MHz) |
364 | * change in clock period = -(steps / 10) * 5.787 ps |
365 | */ |
366 | static void lpt_bend_clkout_dp(struct drm_i915_private *dev_priv, int steps) |
367 | { |
368 | u32 tmp; |
369 | int idx = BEND_IDX(steps); |
370 | |
371 | if (drm_WARN_ON(&dev_priv->drm, steps % 5 != 0)) |
372 | return; |
373 | |
374 | if (drm_WARN_ON(&dev_priv->drm, idx >= ARRAY_SIZE(sscdivintphase))) |
375 | return; |
376 | |
377 | mutex_lock(&dev_priv->sb_lock); |
378 | |
379 | if (steps % 10 != 0) |
380 | tmp = 0xAAAAAAAB; |
381 | else |
382 | tmp = 0x00000000; |
383 | intel_sbi_write(i915: dev_priv, SBI_SSCDITHPHASE, value: tmp, destination: SBI_ICLK); |
384 | |
385 | tmp = intel_sbi_read(i915: dev_priv, SBI_SSCDIVINTPHASE, destination: SBI_ICLK); |
386 | tmp &= 0xffff0000; |
387 | tmp |= sscdivintphase[idx]; |
388 | intel_sbi_write(i915: dev_priv, SBI_SSCDIVINTPHASE, value: tmp, destination: SBI_ICLK); |
389 | |
390 | mutex_unlock(lock: &dev_priv->sb_lock); |
391 | } |
392 | |
393 | #undef BEND_IDX |
394 | |
395 | static bool spll_uses_pch_ssc(struct drm_i915_private *dev_priv) |
396 | { |
397 | u32 fuse_strap = intel_de_read(i915: dev_priv, FUSE_STRAP); |
398 | u32 ctl = intel_de_read(i915: dev_priv, SPLL_CTL); |
399 | |
400 | if ((ctl & SPLL_PLL_ENABLE) == 0) |
401 | return false; |
402 | |
403 | if ((ctl & SPLL_REF_MASK) == SPLL_REF_MUXED_SSC && |
404 | (fuse_strap & HSW_CPU_SSC_ENABLE) == 0) |
405 | return true; |
406 | |
407 | if (IS_BROADWELL(dev_priv) && |
408 | (ctl & SPLL_REF_MASK) == SPLL_REF_PCH_SSC_BDW) |
409 | return true; |
410 | |
411 | return false; |
412 | } |
413 | |
414 | static bool wrpll_uses_pch_ssc(struct drm_i915_private *dev_priv, |
415 | enum intel_dpll_id id) |
416 | { |
417 | u32 fuse_strap = intel_de_read(i915: dev_priv, FUSE_STRAP); |
418 | u32 ctl = intel_de_read(i915: dev_priv, WRPLL_CTL(id)); |
419 | |
420 | if ((ctl & WRPLL_PLL_ENABLE) == 0) |
421 | return false; |
422 | |
423 | if ((ctl & WRPLL_REF_MASK) == WRPLL_REF_PCH_SSC) |
424 | return true; |
425 | |
426 | if ((IS_BROADWELL(dev_priv) || IS_HASWELL_ULT(dev_priv)) && |
427 | (ctl & WRPLL_REF_MASK) == WRPLL_REF_MUXED_SSC_BDW && |
428 | (fuse_strap & HSW_CPU_SSC_ENABLE) == 0) |
429 | return true; |
430 | |
431 | return false; |
432 | } |
433 | |
434 | static void lpt_init_pch_refclk(struct drm_i915_private *dev_priv) |
435 | { |
436 | struct intel_encoder *encoder; |
437 | bool has_fdi = false; |
438 | |
439 | for_each_intel_encoder(&dev_priv->drm, encoder) { |
440 | switch (encoder->type) { |
441 | case INTEL_OUTPUT_ANALOG: |
442 | has_fdi = true; |
443 | break; |
444 | default: |
445 | break; |
446 | } |
447 | } |
448 | |
449 | /* |
450 | * The BIOS may have decided to use the PCH SSC |
451 | * reference so we must not disable it until the |
452 | * relevant PLLs have stopped relying on it. We'll |
453 | * just leave the PCH SSC reference enabled in case |
454 | * any active PLL is using it. It will get disabled |
455 | * after runtime suspend if we don't have FDI. |
456 | * |
457 | * TODO: Move the whole reference clock handling |
458 | * to the modeset sequence proper so that we can |
459 | * actually enable/disable/reconfigure these things |
460 | * safely. To do that we need to introduce a real |
461 | * clock hierarchy. That would also allow us to do |
462 | * clock bending finally. |
463 | */ |
464 | dev_priv->display.dpll.pch_ssc_use = 0; |
465 | |
466 | if (spll_uses_pch_ssc(dev_priv)) { |
467 | drm_dbg_kms(&dev_priv->drm, "SPLL using PCH SSC\n" ); |
468 | dev_priv->display.dpll.pch_ssc_use |= BIT(DPLL_ID_SPLL); |
469 | } |
470 | |
471 | if (wrpll_uses_pch_ssc(dev_priv, id: DPLL_ID_WRPLL1)) { |
472 | drm_dbg_kms(&dev_priv->drm, "WRPLL1 using PCH SSC\n" ); |
473 | dev_priv->display.dpll.pch_ssc_use |= BIT(DPLL_ID_WRPLL1); |
474 | } |
475 | |
476 | if (wrpll_uses_pch_ssc(dev_priv, id: DPLL_ID_WRPLL2)) { |
477 | drm_dbg_kms(&dev_priv->drm, "WRPLL2 using PCH SSC\n" ); |
478 | dev_priv->display.dpll.pch_ssc_use |= BIT(DPLL_ID_WRPLL2); |
479 | } |
480 | |
481 | if (dev_priv->display.dpll.pch_ssc_use) |
482 | return; |
483 | |
484 | if (has_fdi) { |
485 | lpt_bend_clkout_dp(dev_priv, steps: 0); |
486 | lpt_enable_clkout_dp(dev_priv, with_spread: true, with_fdi: true); |
487 | } else { |
488 | lpt_disable_clkout_dp(dev_priv); |
489 | } |
490 | } |
491 | |
492 | static void ilk_init_pch_refclk(struct drm_i915_private *dev_priv) |
493 | { |
494 | struct intel_encoder *encoder; |
495 | struct intel_shared_dpll *pll; |
496 | int i; |
497 | u32 val, final; |
498 | bool has_lvds = false; |
499 | bool has_cpu_edp = false; |
500 | bool has_panel = false; |
501 | bool has_ck505 = false; |
502 | bool can_ssc = false; |
503 | bool using_ssc_source = false; |
504 | |
505 | /* We need to take the global config into account */ |
506 | for_each_intel_encoder(&dev_priv->drm, encoder) { |
507 | switch (encoder->type) { |
508 | case INTEL_OUTPUT_LVDS: |
509 | has_panel = true; |
510 | has_lvds = true; |
511 | break; |
512 | case INTEL_OUTPUT_EDP: |
513 | has_panel = true; |
514 | if (encoder->port == PORT_A) |
515 | has_cpu_edp = true; |
516 | break; |
517 | default: |
518 | break; |
519 | } |
520 | } |
521 | |
522 | if (HAS_PCH_IBX(dev_priv)) { |
523 | has_ck505 = dev_priv->display.vbt.display_clock_mode; |
524 | can_ssc = has_ck505; |
525 | } else { |
526 | has_ck505 = false; |
527 | can_ssc = true; |
528 | } |
529 | |
530 | /* Check if any DPLLs are using the SSC source */ |
531 | for_each_shared_dpll(dev_priv, pll, i) { |
532 | u32 temp; |
533 | |
534 | temp = intel_de_read(i915: dev_priv, PCH_DPLL(pll->info->id)); |
535 | |
536 | if (!(temp & DPLL_VCO_ENABLE)) |
537 | continue; |
538 | |
539 | if ((temp & PLL_REF_INPUT_MASK) == |
540 | PLLB_REF_INPUT_SPREADSPECTRUMIN) { |
541 | using_ssc_source = true; |
542 | break; |
543 | } |
544 | } |
545 | |
546 | drm_dbg_kms(&dev_priv->drm, |
547 | "has_panel %d has_lvds %d has_ck505 %d using_ssc_source %d\n" , |
548 | has_panel, has_lvds, has_ck505, using_ssc_source); |
549 | |
550 | /* Ironlake: try to setup display ref clock before DPLL |
551 | * enabling. This is only under driver's control after |
552 | * PCH B stepping, previous chipset stepping should be |
553 | * ignoring this setting. |
554 | */ |
555 | val = intel_de_read(i915: dev_priv, PCH_DREF_CONTROL); |
556 | |
557 | /* As we must carefully and slowly disable/enable each source in turn, |
558 | * compute the final state we want first and check if we need to |
559 | * make any changes at all. |
560 | */ |
561 | final = val; |
562 | final &= ~DREF_NONSPREAD_SOURCE_MASK; |
563 | if (has_ck505) |
564 | final |= DREF_NONSPREAD_CK505_ENABLE; |
565 | else |
566 | final |= DREF_NONSPREAD_SOURCE_ENABLE; |
567 | |
568 | final &= ~DREF_SSC_SOURCE_MASK; |
569 | final &= ~DREF_CPU_SOURCE_OUTPUT_MASK; |
570 | final &= ~DREF_SSC1_ENABLE; |
571 | |
572 | if (has_panel) { |
573 | final |= DREF_SSC_SOURCE_ENABLE; |
574 | |
575 | if (intel_panel_use_ssc(i915: dev_priv) && can_ssc) |
576 | final |= DREF_SSC1_ENABLE; |
577 | |
578 | if (has_cpu_edp) { |
579 | if (intel_panel_use_ssc(i915: dev_priv) && can_ssc) |
580 | final |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD; |
581 | else |
582 | final |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD; |
583 | } else { |
584 | final |= DREF_CPU_SOURCE_OUTPUT_DISABLE; |
585 | } |
586 | } else if (using_ssc_source) { |
587 | final |= DREF_SSC_SOURCE_ENABLE; |
588 | final |= DREF_SSC1_ENABLE; |
589 | } |
590 | |
591 | if (final == val) |
592 | return; |
593 | |
594 | /* Always enable nonspread source */ |
595 | val &= ~DREF_NONSPREAD_SOURCE_MASK; |
596 | |
597 | if (has_ck505) |
598 | val |= DREF_NONSPREAD_CK505_ENABLE; |
599 | else |
600 | val |= DREF_NONSPREAD_SOURCE_ENABLE; |
601 | |
602 | if (has_panel) { |
603 | val &= ~DREF_SSC_SOURCE_MASK; |
604 | val |= DREF_SSC_SOURCE_ENABLE; |
605 | |
606 | /* SSC must be turned on before enabling the CPU output */ |
607 | if (intel_panel_use_ssc(i915: dev_priv) && can_ssc) { |
608 | drm_dbg_kms(&dev_priv->drm, "Using SSC on panel\n" ); |
609 | val |= DREF_SSC1_ENABLE; |
610 | } else { |
611 | val &= ~DREF_SSC1_ENABLE; |
612 | } |
613 | |
614 | /* Get SSC going before enabling the outputs */ |
615 | intel_de_write(i915: dev_priv, PCH_DREF_CONTROL, val); |
616 | intel_de_posting_read(i915: dev_priv, PCH_DREF_CONTROL); |
617 | udelay(200); |
618 | |
619 | val &= ~DREF_CPU_SOURCE_OUTPUT_MASK; |
620 | |
621 | /* Enable CPU source on CPU attached eDP */ |
622 | if (has_cpu_edp) { |
623 | if (intel_panel_use_ssc(i915: dev_priv) && can_ssc) { |
624 | drm_dbg_kms(&dev_priv->drm, |
625 | "Using SSC on eDP\n" ); |
626 | val |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD; |
627 | } else { |
628 | val |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD; |
629 | } |
630 | } else { |
631 | val |= DREF_CPU_SOURCE_OUTPUT_DISABLE; |
632 | } |
633 | |
634 | intel_de_write(i915: dev_priv, PCH_DREF_CONTROL, val); |
635 | intel_de_posting_read(i915: dev_priv, PCH_DREF_CONTROL); |
636 | udelay(200); |
637 | } else { |
638 | drm_dbg_kms(&dev_priv->drm, "Disabling CPU source output\n" ); |
639 | |
640 | val &= ~DREF_CPU_SOURCE_OUTPUT_MASK; |
641 | |
642 | /* Turn off CPU output */ |
643 | val |= DREF_CPU_SOURCE_OUTPUT_DISABLE; |
644 | |
645 | intel_de_write(i915: dev_priv, PCH_DREF_CONTROL, val); |
646 | intel_de_posting_read(i915: dev_priv, PCH_DREF_CONTROL); |
647 | udelay(200); |
648 | |
649 | if (!using_ssc_source) { |
650 | drm_dbg_kms(&dev_priv->drm, "Disabling SSC source\n" ); |
651 | |
652 | /* Turn off the SSC source */ |
653 | val &= ~DREF_SSC_SOURCE_MASK; |
654 | val |= DREF_SSC_SOURCE_DISABLE; |
655 | |
656 | /* Turn off SSC1 */ |
657 | val &= ~DREF_SSC1_ENABLE; |
658 | |
659 | intel_de_write(i915: dev_priv, PCH_DREF_CONTROL, val); |
660 | intel_de_posting_read(i915: dev_priv, PCH_DREF_CONTROL); |
661 | udelay(200); |
662 | } |
663 | } |
664 | |
665 | drm_WARN_ON(&dev_priv->drm, val != final); |
666 | } |
667 | |
668 | /* |
669 | * Initialize reference clocks when the driver loads |
670 | */ |
671 | void intel_init_pch_refclk(struct drm_i915_private *dev_priv) |
672 | { |
673 | if (HAS_PCH_IBX(dev_priv) || HAS_PCH_CPT(dev_priv)) |
674 | ilk_init_pch_refclk(dev_priv); |
675 | else if (HAS_PCH_LPT(dev_priv)) |
676 | lpt_init_pch_refclk(dev_priv); |
677 | } |
678 | |