]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0 |
00f37327 SG |
2 | /* |
3 | * Copyright (c) 2011-2013, NVIDIA Corporation. | |
00f37327 SG |
4 | */ |
5 | ||
6 | #include <common.h> | |
d7659212 | 7 | #include <dm.h> |
00f37327 | 8 | #include <errno.h> |
f7ae49fc | 9 | #include <log.h> |
00f37327 | 10 | #include <malloc.h> |
d7659212 | 11 | #include <panel.h> |
079ff3b9 | 12 | #include <syscon.h> |
d7659212 | 13 | #include <video_bridge.h> |
00f37327 SG |
14 | #include <asm/io.h> |
15 | #include <asm/arch/clock.h> | |
16 | #include <asm/arch-tegra/dc.h> | |
c05ed00a | 17 | #include <linux/delay.h> |
00f37327 SG |
18 | #include "displayport.h" |
19 | #include "sor.h" | |
61b29b82 | 20 | #include <linux/err.h> |
00f37327 | 21 | |
00f37327 SG |
22 | #define DEBUG_SOR 0 |
23 | ||
24 | #define APBDEV_PMC_DPD_SAMPLE 0x20 | |
25 | #define APBDEV_PMC_DPD_SAMPLE_ON_DISABLE 0 | |
26 | #define APBDEV_PMC_DPD_SAMPLE_ON_ENABLE 1 | |
27 | #define APBDEV_PMC_SEL_DPD_TIM 0x1c8 | |
28 | #define APBDEV_PMC_SEL_DPD_TIM_SEL_DPD_TIM_DEFAULT 0x7f | |
29 | #define APBDEV_PMC_IO_DPD2_REQ 0x1c0 | |
30 | #define APBDEV_PMC_IO_DPD2_REQ_LVDS_SHIFT 25 | |
31 | #define APBDEV_PMC_IO_DPD2_REQ_LVDS_OFF (0 << 25) | |
32 | #define APBDEV_PMC_IO_DPD2_REQ_LVDS_ON (1 << 25) | |
33 | #define APBDEV_PMC_IO_DPD2_REQ_CODE_SHIFT 30 | |
34 | #define APBDEV_PMC_IO_DPD2_REQ_CODE_DEFAULT_MASK (0x3 << 30) | |
35 | #define APBDEV_PMC_IO_DPD2_REQ_CODE_IDLE (0 << 30) | |
36 | #define APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_OFF (1 << 30) | |
37 | #define APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_ON (2 << 30) | |
38 | #define APBDEV_PMC_IO_DPD2_STATUS 0x1c4 | |
39 | #define APBDEV_PMC_IO_DPD2_STATUS_LVDS_SHIFT 25 | |
40 | #define APBDEV_PMC_IO_DPD2_STATUS_LVDS_OFF (0 << 25) | |
41 | #define APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON (1 << 25) | |
42 | ||
d7659212 SG |
43 | struct tegra_dc_sor_data { |
44 | void *base; | |
45 | void *pmc_base; | |
46 | u8 portnum; /* 0 or 1 */ | |
47 | int power_is_up; | |
48 | struct udevice *panel; | |
49 | }; | |
50 | ||
00f37327 SG |
51 | static inline u32 tegra_sor_readl(struct tegra_dc_sor_data *sor, u32 reg) |
52 | { | |
53 | return readl((u32 *)sor->base + reg); | |
54 | } | |
55 | ||
56 | static inline void tegra_sor_writel(struct tegra_dc_sor_data *sor, u32 reg, | |
57 | u32 val) | |
58 | { | |
59 | writel(val, (u32 *)sor->base + reg); | |
60 | } | |
61 | ||
62 | static inline void tegra_sor_write_field(struct tegra_dc_sor_data *sor, | |
63 | u32 reg, u32 mask, u32 val) | |
64 | { | |
65 | u32 reg_val = tegra_sor_readl(sor, reg); | |
66 | reg_val &= ~mask; | |
67 | reg_val |= val; | |
68 | tegra_sor_writel(sor, reg, reg_val); | |
69 | } | |
70 | ||
d7659212 | 71 | void tegra_dp_disable_tx_pu(struct udevice *dev) |
dedc44b4 | 72 | { |
d7659212 SG |
73 | struct tegra_dc_sor_data *sor = dev_get_priv(dev); |
74 | ||
dedc44b4 SG |
75 | tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), |
76 | DP_PADCTL_TX_PU_MASK, DP_PADCTL_TX_PU_DISABLE); | |
77 | } | |
78 | ||
d7659212 | 79 | void tegra_dp_set_pe_vs_pc(struct udevice *dev, u32 mask, u32 pe_reg, |
dedc44b4 SG |
80 | u32 vs_reg, u32 pc_reg, u8 pc_supported) |
81 | { | |
d7659212 SG |
82 | struct tegra_dc_sor_data *sor = dev_get_priv(dev); |
83 | ||
dedc44b4 SG |
84 | tegra_sor_write_field(sor, PR(sor->portnum), mask, pe_reg); |
85 | tegra_sor_write_field(sor, DC(sor->portnum), mask, vs_reg); | |
86 | if (pc_supported) { | |
87 | tegra_sor_write_field(sor, POSTCURSOR(sor->portnum), mask, | |
88 | pc_reg); | |
89 | } | |
90 | } | |
91 | ||
00f37327 SG |
92 | static int tegra_dc_sor_poll_register(struct tegra_dc_sor_data *sor, u32 reg, |
93 | u32 mask, u32 exp_val, | |
94 | int poll_interval_us, int timeout_ms) | |
95 | { | |
96 | u32 reg_val = 0; | |
97 | ulong start; | |
98 | ||
99 | start = get_timer(0); | |
100 | do { | |
101 | reg_val = tegra_sor_readl(sor, reg); | |
102 | if (((reg_val & mask) == exp_val)) | |
103 | return 0; | |
104 | udelay(poll_interval_us); | |
105 | } while (get_timer(start) < timeout_ms); | |
106 | ||
107 | debug("sor_poll_register 0x%x: timeout, (reg_val)0x%08x & (mask)0x%08x != (exp_val)0x%08x\n", | |
108 | reg, reg_val, mask, exp_val); | |
109 | ||
110 | return -ETIMEDOUT; | |
111 | } | |
112 | ||
d7659212 | 113 | int tegra_dc_sor_set_power_state(struct udevice *dev, int pu_pd) |
00f37327 | 114 | { |
d7659212 | 115 | struct tegra_dc_sor_data *sor = dev_get_priv(dev); |
00f37327 SG |
116 | u32 reg_val; |
117 | u32 orig_val; | |
118 | ||
119 | orig_val = tegra_sor_readl(sor, PWR); | |
120 | ||
121 | reg_val = pu_pd ? PWR_NORMAL_STATE_PU : | |
122 | PWR_NORMAL_STATE_PD; /* normal state only */ | |
123 | ||
124 | if (reg_val == orig_val) | |
125 | return 0; /* No update needed */ | |
126 | ||
127 | reg_val |= PWR_SETTING_NEW_TRIGGER; | |
128 | tegra_sor_writel(sor, PWR, reg_val); | |
129 | ||
130 | /* Poll to confirm it is done */ | |
131 | if (tegra_dc_sor_poll_register(sor, PWR, | |
132 | PWR_SETTING_NEW_DEFAULT_MASK, | |
133 | PWR_SETTING_NEW_DONE, | |
134 | 100, TEGRA_SOR_TIMEOUT_MS)) { | |
135 | debug("dc timeout waiting for SOR_PWR = NEW_DONE\n"); | |
136 | return -EFAULT; | |
137 | } | |
138 | ||
139 | return 0; | |
140 | } | |
141 | ||
d7659212 | 142 | void tegra_dc_sor_set_dp_linkctl(struct udevice *dev, int ena, |
00f37327 SG |
143 | u8 training_pattern, |
144 | const struct tegra_dp_link_config *link_cfg) | |
145 | { | |
d7659212 | 146 | struct tegra_dc_sor_data *sor = dev_get_priv(dev); |
00f37327 SG |
147 | u32 reg_val; |
148 | ||
149 | reg_val = tegra_sor_readl(sor, DP_LINKCTL(sor->portnum)); | |
150 | ||
151 | if (ena) | |
152 | reg_val |= DP_LINKCTL_ENABLE_YES; | |
153 | else | |
154 | reg_val &= DP_LINKCTL_ENABLE_NO; | |
155 | ||
156 | reg_val &= ~DP_LINKCTL_TUSIZE_MASK; | |
157 | reg_val |= (link_cfg->tu_size << DP_LINKCTL_TUSIZE_SHIFT); | |
158 | ||
159 | if (link_cfg->enhanced_framing) | |
160 | reg_val |= DP_LINKCTL_ENHANCEDFRAME_ENABLE; | |
161 | ||
162 | tegra_sor_writel(sor, DP_LINKCTL(sor->portnum), reg_val); | |
163 | ||
164 | switch (training_pattern) { | |
165 | case training_pattern_1: | |
166 | tegra_sor_writel(sor, DP_TPG, 0x41414141); | |
167 | break; | |
168 | case training_pattern_2: | |
169 | case training_pattern_3: | |
170 | reg_val = (link_cfg->link_bw == SOR_LINK_SPEED_G5_4) ? | |
171 | 0x43434343 : 0x42424242; | |
172 | tegra_sor_writel(sor, DP_TPG, reg_val); | |
173 | break; | |
174 | default: | |
175 | tegra_sor_writel(sor, DP_TPG, 0x50505050); | |
176 | break; | |
177 | } | |
178 | } | |
179 | ||
180 | static int tegra_dc_sor_enable_lane_sequencer(struct tegra_dc_sor_data *sor, | |
181 | int pu, int is_lvds) | |
182 | { | |
183 | u32 reg_val; | |
184 | ||
185 | /* SOR lane sequencer */ | |
186 | if (pu) { | |
187 | reg_val = LANE_SEQ_CTL_SETTING_NEW_TRIGGER | | |
188 | LANE_SEQ_CTL_SEQUENCE_DOWN | | |
189 | LANE_SEQ_CTL_NEW_POWER_STATE_PU; | |
190 | } else { | |
191 | reg_val = LANE_SEQ_CTL_SETTING_NEW_TRIGGER | | |
192 | LANE_SEQ_CTL_SEQUENCE_UP | | |
193 | LANE_SEQ_CTL_NEW_POWER_STATE_PD; | |
194 | } | |
195 | ||
196 | if (is_lvds) | |
197 | reg_val |= 15 << LANE_SEQ_CTL_DELAY_SHIFT; | |
198 | else | |
199 | reg_val |= 1 << LANE_SEQ_CTL_DELAY_SHIFT; | |
200 | ||
201 | tegra_sor_writel(sor, LANE_SEQ_CTL, reg_val); | |
202 | ||
203 | if (tegra_dc_sor_poll_register(sor, LANE_SEQ_CTL, | |
204 | LANE_SEQ_CTL_SETTING_MASK, | |
205 | LANE_SEQ_CTL_SETTING_NEW_DONE, | |
206 | 100, TEGRA_SOR_TIMEOUT_MS)) { | |
207 | debug("dp: timeout while waiting for SOR lane sequencer to power down lanes\n"); | |
208 | return -1; | |
209 | } | |
210 | ||
211 | return 0; | |
212 | } | |
213 | ||
d7659212 | 214 | static int tegra_dc_sor_power_dplanes(struct udevice *dev, |
00f37327 SG |
215 | u32 lane_count, int pu) |
216 | { | |
d7659212 | 217 | struct tegra_dc_sor_data *sor = dev_get_priv(dev); |
00f37327 SG |
218 | u32 reg_val; |
219 | ||
220 | reg_val = tegra_sor_readl(sor, DP_PADCTL(sor->portnum)); | |
221 | ||
222 | if (pu) { | |
223 | switch (lane_count) { | |
224 | case 4: | |
225 | reg_val |= (DP_PADCTL_PD_TXD_3_NO | | |
226 | DP_PADCTL_PD_TXD_2_NO); | |
227 | /* fall through */ | |
228 | case 2: | |
229 | reg_val |= DP_PADCTL_PD_TXD_1_NO; | |
230 | case 1: | |
231 | reg_val |= DP_PADCTL_PD_TXD_0_NO; | |
232 | break; | |
233 | default: | |
234 | debug("dp: invalid lane number %d\n", lane_count); | |
235 | return -1; | |
236 | } | |
237 | ||
238 | tegra_sor_writel(sor, DP_PADCTL(sor->portnum), reg_val); | |
d7659212 | 239 | tegra_dc_sor_set_lane_count(dev, lane_count); |
00f37327 SG |
240 | } |
241 | ||
242 | return tegra_dc_sor_enable_lane_sequencer(sor, pu, 0); | |
243 | } | |
244 | ||
d7659212 | 245 | void tegra_dc_sor_set_panel_power(struct udevice *dev, int power_up) |
00f37327 | 246 | { |
d7659212 | 247 | struct tegra_dc_sor_data *sor = dev_get_priv(dev); |
00f37327 SG |
248 | u32 reg_val; |
249 | ||
250 | reg_val = tegra_sor_readl(sor, DP_PADCTL(sor->portnum)); | |
251 | ||
252 | if (power_up) | |
253 | reg_val |= DP_PADCTL_PAD_CAL_PD_POWERUP; | |
254 | else | |
255 | reg_val &= ~DP_PADCTL_PAD_CAL_PD_POWERUP; | |
256 | ||
257 | tegra_sor_writel(sor, DP_PADCTL(sor->portnum), reg_val); | |
258 | } | |
259 | ||
260 | static void tegra_dc_sor_config_pwm(struct tegra_dc_sor_data *sor, u32 pwm_div, | |
261 | u32 pwm_dutycycle) | |
262 | { | |
263 | tegra_sor_writel(sor, PWM_DIV, pwm_div); | |
264 | tegra_sor_writel(sor, PWM_CTL, | |
265 | (pwm_dutycycle & PWM_CTL_DUTY_CYCLE_MASK) | | |
266 | PWM_CTL_SETTING_NEW_TRIGGER); | |
267 | ||
268 | if (tegra_dc_sor_poll_register(sor, PWM_CTL, | |
269 | PWM_CTL_SETTING_NEW_SHIFT, | |
270 | PWM_CTL_SETTING_NEW_DONE, | |
271 | 100, TEGRA_SOR_TIMEOUT_MS)) { | |
272 | debug("dp: timeout while waiting for SOR PWM setting\n"); | |
273 | } | |
274 | } | |
275 | ||
d7659212 | 276 | static void tegra_dc_sor_set_dp_mode(struct udevice *dev, |
00f37327 SG |
277 | const struct tegra_dp_link_config *link_cfg) |
278 | { | |
d7659212 | 279 | struct tegra_dc_sor_data *sor = dev_get_priv(dev); |
00f37327 SG |
280 | u32 reg_val; |
281 | ||
d7659212 | 282 | tegra_dc_sor_set_link_bandwidth(dev, link_cfg->link_bw); |
00f37327 | 283 | |
d7659212 | 284 | tegra_dc_sor_set_dp_linkctl(dev, 1, training_pattern_none, link_cfg); |
00f37327 SG |
285 | reg_val = tegra_sor_readl(sor, DP_CONFIG(sor->portnum)); |
286 | reg_val &= ~DP_CONFIG_WATERMARK_MASK; | |
287 | reg_val |= link_cfg->watermark; | |
288 | reg_val &= ~DP_CONFIG_ACTIVESYM_COUNT_MASK; | |
289 | reg_val |= (link_cfg->active_count << | |
290 | DP_CONFIG_ACTIVESYM_COUNT_SHIFT); | |
291 | reg_val &= ~DP_CONFIG_ACTIVESYM_FRAC_MASK; | |
292 | reg_val |= (link_cfg->active_frac << | |
293 | DP_CONFIG_ACTIVESYM_FRAC_SHIFT); | |
294 | if (link_cfg->activepolarity) | |
295 | reg_val |= DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE; | |
296 | else | |
297 | reg_val &= ~DP_CONFIG_ACTIVESYM_POLARITY_POSITIVE; | |
298 | reg_val |= (DP_CONFIG_ACTIVESYM_CNTL_ENABLE | | |
299 | DP_CONFIG_RD_RESET_VAL_NEGATIVE); | |
300 | ||
301 | tegra_sor_writel(sor, DP_CONFIG(sor->portnum), reg_val); | |
302 | ||
303 | /* program h/vblank sym */ | |
304 | tegra_sor_write_field(sor, DP_AUDIO_HBLANK_SYMBOLS, | |
305 | DP_AUDIO_HBLANK_SYMBOLS_MASK, | |
306 | link_cfg->hblank_sym); | |
307 | ||
308 | tegra_sor_write_field(sor, DP_AUDIO_VBLANK_SYMBOLS, | |
309 | DP_AUDIO_VBLANK_SYMBOLS_MASK, | |
310 | link_cfg->vblank_sym); | |
311 | } | |
312 | ||
313 | static inline void tegra_dc_sor_super_update(struct tegra_dc_sor_data *sor) | |
314 | { | |
315 | tegra_sor_writel(sor, SUPER_STATE0, 0); | |
316 | tegra_sor_writel(sor, SUPER_STATE0, 1); | |
317 | tegra_sor_writel(sor, SUPER_STATE0, 0); | |
318 | } | |
319 | ||
320 | static inline void tegra_dc_sor_update(struct tegra_dc_sor_data *sor) | |
321 | { | |
322 | tegra_sor_writel(sor, STATE0, 0); | |
323 | tegra_sor_writel(sor, STATE0, 1); | |
324 | tegra_sor_writel(sor, STATE0, 0); | |
325 | } | |
326 | ||
327 | static int tegra_dc_sor_io_set_dpd(struct tegra_dc_sor_data *sor, int up) | |
328 | { | |
329 | u32 reg_val; | |
330 | void *pmc_base = sor->pmc_base; | |
331 | ||
332 | if (up) { | |
333 | writel(APBDEV_PMC_DPD_SAMPLE_ON_ENABLE, | |
334 | pmc_base + APBDEV_PMC_DPD_SAMPLE); | |
335 | writel(10, pmc_base + APBDEV_PMC_SEL_DPD_TIM); | |
336 | } | |
337 | ||
338 | reg_val = readl(pmc_base + APBDEV_PMC_IO_DPD2_REQ); | |
339 | reg_val &= ~(APBDEV_PMC_IO_DPD2_REQ_LVDS_ON || | |
340 | APBDEV_PMC_IO_DPD2_REQ_CODE_DEFAULT_MASK); | |
341 | ||
342 | reg_val = up ? APBDEV_PMC_IO_DPD2_REQ_LVDS_ON | | |
343 | APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_OFF : | |
344 | APBDEV_PMC_IO_DPD2_REQ_LVDS_OFF | | |
345 | APBDEV_PMC_IO_DPD2_REQ_CODE_DPD_ON; | |
346 | ||
347 | writel(reg_val, pmc_base + APBDEV_PMC_IO_DPD2_REQ); | |
348 | ||
349 | /* Polling */ | |
350 | u32 temp = 10 * 1000; | |
351 | do { | |
352 | udelay(20); | |
353 | reg_val = readl(pmc_base + APBDEV_PMC_IO_DPD2_STATUS); | |
354 | if (temp > 20) | |
355 | temp -= 20; | |
356 | else | |
357 | break; | |
358 | } while ((reg_val & APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON) != 0); | |
359 | ||
360 | if ((reg_val & APBDEV_PMC_IO_DPD2_STATUS_LVDS_ON) != 0) { | |
361 | debug("PMC_IO_DPD2 polling failed (0x%x)\n", reg_val); | |
362 | return -EIO; | |
363 | } | |
364 | ||
365 | if (up) { | |
366 | writel(APBDEV_PMC_DPD_SAMPLE_ON_DISABLE, | |
367 | pmc_base + APBDEV_PMC_DPD_SAMPLE); | |
368 | } | |
369 | ||
370 | return 0; | |
371 | } | |
372 | ||
d7659212 | 373 | void tegra_dc_sor_set_internal_panel(struct udevice *dev, int is_int) |
00f37327 | 374 | { |
d7659212 | 375 | struct tegra_dc_sor_data *sor = dev_get_priv(dev); |
00f37327 SG |
376 | u32 reg_val; |
377 | ||
378 | reg_val = tegra_sor_readl(sor, DP_SPARE(sor->portnum)); | |
379 | if (is_int) | |
380 | reg_val |= DP_SPARE_PANEL_INTERNAL; | |
381 | else | |
382 | reg_val &= ~DP_SPARE_PANEL_INTERNAL; | |
383 | ||
384 | reg_val |= DP_SPARE_SOR_CLK_SEL_MACRO_SORCLK | | |
385 | DP_SPARE_SEQ_ENABLE_YES; | |
386 | tegra_sor_writel(sor, DP_SPARE(sor->portnum), reg_val); | |
387 | } | |
388 | ||
d7659212 | 389 | void tegra_dc_sor_read_link_config(struct udevice *dev, u8 *link_bw, |
00f37327 SG |
390 | u8 *lane_count) |
391 | { | |
d7659212 | 392 | struct tegra_dc_sor_data *sor = dev_get_priv(dev); |
00f37327 SG |
393 | u32 reg_val; |
394 | ||
395 | reg_val = tegra_sor_readl(sor, CLK_CNTRL); | |
396 | *link_bw = (reg_val & CLK_CNTRL_DP_LINK_SPEED_MASK) | |
397 | >> CLK_CNTRL_DP_LINK_SPEED_SHIFT; | |
398 | reg_val = tegra_sor_readl(sor, | |
399 | DP_LINKCTL(sor->portnum)); | |
400 | ||
401 | switch (reg_val & DP_LINKCTL_LANECOUNT_MASK) { | |
402 | case DP_LINKCTL_LANECOUNT_ZERO: | |
403 | *lane_count = 0; | |
404 | break; | |
405 | case DP_LINKCTL_LANECOUNT_ONE: | |
406 | *lane_count = 1; | |
407 | break; | |
408 | case DP_LINKCTL_LANECOUNT_TWO: | |
409 | *lane_count = 2; | |
410 | break; | |
411 | case DP_LINKCTL_LANECOUNT_FOUR: | |
412 | *lane_count = 4; | |
413 | break; | |
414 | default: | |
415 | printf("Unknown lane count\n"); | |
416 | } | |
417 | } | |
418 | ||
d7659212 | 419 | void tegra_dc_sor_set_link_bandwidth(struct udevice *dev, u8 link_bw) |
00f37327 | 420 | { |
d7659212 SG |
421 | struct tegra_dc_sor_data *sor = dev_get_priv(dev); |
422 | ||
00f37327 SG |
423 | tegra_sor_write_field(sor, CLK_CNTRL, |
424 | CLK_CNTRL_DP_LINK_SPEED_MASK, | |
425 | link_bw << CLK_CNTRL_DP_LINK_SPEED_SHIFT); | |
426 | } | |
427 | ||
d7659212 | 428 | void tegra_dc_sor_set_lane_count(struct udevice *dev, u8 lane_count) |
00f37327 | 429 | { |
d7659212 | 430 | struct tegra_dc_sor_data *sor = dev_get_priv(dev); |
00f37327 SG |
431 | u32 reg_val; |
432 | ||
433 | reg_val = tegra_sor_readl(sor, DP_LINKCTL(sor->portnum)); | |
434 | reg_val &= ~DP_LINKCTL_LANECOUNT_MASK; | |
435 | switch (lane_count) { | |
436 | case 0: | |
437 | break; | |
438 | case 1: | |
439 | reg_val |= DP_LINKCTL_LANECOUNT_ONE; | |
440 | break; | |
441 | case 2: | |
442 | reg_val |= DP_LINKCTL_LANECOUNT_TWO; | |
443 | break; | |
444 | case 4: | |
445 | reg_val |= DP_LINKCTL_LANECOUNT_FOUR; | |
446 | break; | |
447 | default: | |
448 | /* 0 should be handled earlier. */ | |
449 | printf("dp: Invalid lane count %d\n", lane_count); | |
450 | return; | |
451 | } | |
452 | tegra_sor_writel(sor, DP_LINKCTL(sor->portnum), reg_val); | |
453 | } | |
454 | ||
455 | /* | |
456 | * The SOR power sequencer does not work for t124 so SW has to | |
457 | * go through the power sequence manually | |
458 | * Power up steps from spec: | |
459 | * STEP PDPORT PDPLL PDBG PLLVCOD PLLCAPD E_DPD PDCAL | |
460 | * 1 1 1 1 1 1 1 1 | |
461 | * 2 1 1 1 1 1 0 1 | |
462 | * 3 1 1 0 1 1 0 1 | |
463 | * 4 1 0 0 0 0 0 1 | |
464 | * 5 0 0 0 0 0 0 1 | |
465 | */ | |
d7659212 | 466 | static int tegra_dc_sor_power_up(struct udevice *dev, int is_lvds) |
00f37327 | 467 | { |
d7659212 | 468 | struct tegra_dc_sor_data *sor = dev_get_priv(dev); |
505907a4 | 469 | u32 reg; |
00f37327 SG |
470 | int ret; |
471 | ||
472 | if (sor->power_is_up) | |
473 | return 0; | |
474 | ||
505907a4 SG |
475 | /* |
476 | * If for some reason it is already powered up, don't do it again. | |
477 | * This can happen if U-Boot is the secondary boot loader. | |
478 | */ | |
479 | reg = tegra_sor_readl(sor, DP_PADCTL(sor->portnum)); | |
480 | if (reg & DP_PADCTL_PD_TXD_0_NO) | |
481 | return 0; | |
482 | ||
00f37327 | 483 | /* Set link bw */ |
d7659212 | 484 | tegra_dc_sor_set_link_bandwidth(dev, is_lvds ? |
00f37327 SG |
485 | CLK_CNTRL_DP_LINK_SPEED_LVDS : |
486 | CLK_CNTRL_DP_LINK_SPEED_G1_62); | |
487 | ||
488 | /* step 1 */ | |
489 | tegra_sor_write_field(sor, PLL2, | |
490 | PLL2_AUX7_PORT_POWERDOWN_MASK | /* PDPORT */ | |
491 | PLL2_AUX6_BANDGAP_POWERDOWN_MASK | /* PDBG */ | |
492 | PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK, /* PLLCAPD */ | |
493 | PLL2_AUX7_PORT_POWERDOWN_ENABLE | | |
494 | PLL2_AUX6_BANDGAP_POWERDOWN_ENABLE | | |
495 | PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_ENABLE); | |
496 | tegra_sor_write_field(sor, PLL0, PLL0_PWR_MASK | /* PDPLL */ | |
497 | PLL0_VCOPD_MASK, /* PLLVCOPD */ | |
498 | PLL0_PWR_OFF | PLL0_VCOPD_ASSERT); | |
499 | tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), | |
500 | DP_PADCTL_PAD_CAL_PD_POWERDOWN, /* PDCAL */ | |
501 | DP_PADCTL_PAD_CAL_PD_POWERDOWN); | |
502 | ||
503 | /* step 2 */ | |
504 | ret = tegra_dc_sor_io_set_dpd(sor, 1); | |
505 | if (ret) | |
506 | return ret; | |
507 | udelay(15); | |
508 | ||
509 | /* step 3 */ | |
510 | tegra_sor_write_field(sor, PLL2, | |
511 | PLL2_AUX6_BANDGAP_POWERDOWN_MASK, | |
512 | PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE); | |
513 | udelay(25); | |
514 | ||
515 | /* step 4 */ | |
516 | tegra_sor_write_field(sor, PLL0, | |
517 | PLL0_PWR_MASK | /* PDPLL */ | |
518 | PLL0_VCOPD_MASK, /* PLLVCOPD */ | |
519 | PLL0_PWR_ON | PLL0_VCOPD_RESCIND); | |
520 | /* PLLCAPD */ | |
521 | tegra_sor_write_field(sor, PLL2, | |
522 | PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK, | |
523 | PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE); | |
524 | udelay(225); | |
525 | ||
526 | /* step 5 PDPORT */ | |
527 | tegra_sor_write_field(sor, PLL2, | |
528 | PLL2_AUX7_PORT_POWERDOWN_MASK, | |
529 | PLL2_AUX7_PORT_POWERDOWN_DISABLE); | |
530 | ||
531 | sor->power_is_up = 1; | |
532 | ||
533 | return 0; | |
534 | } | |
535 | ||
536 | #if DEBUG_SOR | |
537 | static void dump_sor_reg(struct tegra_dc_sor_data *sor) | |
538 | { | |
07bc873c SG |
539 | #define DUMP_REG(a) printk(BIOS_INFO, \ |
540 | "%-32s %03x %08x\n", \ | |
00f37327 SG |
541 | #a, a, tegra_sor_readl(sor, a)); |
542 | ||
543 | DUMP_REG(SUPER_STATE0); | |
544 | DUMP_REG(SUPER_STATE1); | |
545 | DUMP_REG(STATE0); | |
546 | DUMP_REG(STATE1); | |
547 | DUMP_REG(NV_HEAD_STATE0(0)); | |
548 | DUMP_REG(NV_HEAD_STATE0(1)); | |
549 | DUMP_REG(NV_HEAD_STATE1(0)); | |
550 | DUMP_REG(NV_HEAD_STATE1(1)); | |
551 | DUMP_REG(NV_HEAD_STATE2(0)); | |
552 | DUMP_REG(NV_HEAD_STATE2(1)); | |
553 | DUMP_REG(NV_HEAD_STATE3(0)); | |
554 | DUMP_REG(NV_HEAD_STATE3(1)); | |
555 | DUMP_REG(NV_HEAD_STATE4(0)); | |
556 | DUMP_REG(NV_HEAD_STATE4(1)); | |
557 | DUMP_REG(NV_HEAD_STATE5(0)); | |
558 | DUMP_REG(NV_HEAD_STATE5(1)); | |
559 | DUMP_REG(CRC_CNTRL); | |
560 | DUMP_REG(CLK_CNTRL); | |
561 | DUMP_REG(CAP); | |
562 | DUMP_REG(PWR); | |
563 | DUMP_REG(TEST); | |
564 | DUMP_REG(PLL0); | |
565 | DUMP_REG(PLL1); | |
566 | DUMP_REG(PLL2); | |
567 | DUMP_REG(PLL3); | |
568 | DUMP_REG(CSTM); | |
569 | DUMP_REG(LVDS); | |
570 | DUMP_REG(CRCA); | |
571 | DUMP_REG(CRCB); | |
572 | DUMP_REG(SEQ_CTL); | |
573 | DUMP_REG(LANE_SEQ_CTL); | |
574 | DUMP_REG(SEQ_INST(0)); | |
575 | DUMP_REG(SEQ_INST(1)); | |
576 | DUMP_REG(SEQ_INST(2)); | |
577 | DUMP_REG(SEQ_INST(3)); | |
578 | DUMP_REG(SEQ_INST(4)); | |
579 | DUMP_REG(SEQ_INST(5)); | |
580 | DUMP_REG(SEQ_INST(6)); | |
581 | DUMP_REG(SEQ_INST(7)); | |
582 | DUMP_REG(SEQ_INST(8)); | |
583 | DUMP_REG(PWM_DIV); | |
584 | DUMP_REG(PWM_CTL); | |
585 | DUMP_REG(MSCHECK); | |
586 | DUMP_REG(XBAR_CTRL); | |
587 | DUMP_REG(DP_LINKCTL(0)); | |
588 | DUMP_REG(DP_LINKCTL(1)); | |
589 | DUMP_REG(DC(0)); | |
590 | DUMP_REG(DC(1)); | |
591 | DUMP_REG(LANE_DRIVE_CURRENT(0)); | |
592 | DUMP_REG(PR(0)); | |
593 | DUMP_REG(LANE4_PREEMPHASIS(0)); | |
594 | DUMP_REG(POSTCURSOR(0)); | |
595 | DUMP_REG(DP_CONFIG(0)); | |
596 | DUMP_REG(DP_CONFIG(1)); | |
597 | DUMP_REG(DP_MN(0)); | |
598 | DUMP_REG(DP_MN(1)); | |
599 | DUMP_REG(DP_PADCTL(0)); | |
600 | DUMP_REG(DP_PADCTL(1)); | |
601 | DUMP_REG(DP_DEBUG(0)); | |
602 | DUMP_REG(DP_DEBUG(1)); | |
603 | DUMP_REG(DP_SPARE(0)); | |
604 | DUMP_REG(DP_SPARE(1)); | |
605 | DUMP_REG(DP_TPG); | |
606 | ||
607 | return; | |
608 | } | |
609 | #endif | |
610 | ||
611 | static void tegra_dc_sor_config_panel(struct tegra_dc_sor_data *sor, | |
612 | int is_lvds, | |
613 | const struct tegra_dp_link_config *link_cfg, | |
614 | const struct display_timing *timing) | |
615 | { | |
616 | const int head_num = 0; | |
617 | u32 reg_val = STATE1_ASY_OWNER_HEAD0 << head_num; | |
618 | u32 vtotal, htotal; | |
619 | u32 vsync_end, hsync_end; | |
620 | u32 vblank_end, hblank_end; | |
621 | u32 vblank_start, hblank_start; | |
622 | ||
623 | reg_val |= is_lvds ? STATE1_ASY_PROTOCOL_LVDS_CUSTOM : | |
624 | STATE1_ASY_PROTOCOL_DP_A; | |
625 | reg_val |= STATE1_ASY_SUBOWNER_NONE | | |
626 | STATE1_ASY_CRCMODE_COMPLETE_RASTER; | |
627 | ||
628 | reg_val |= STATE1_ASY_HSYNCPOL_NEGATIVE_TRUE; | |
629 | reg_val |= STATE1_ASY_VSYNCPOL_NEGATIVE_TRUE; | |
630 | reg_val |= (link_cfg->bits_per_pixel > 18) ? | |
631 | STATE1_ASY_PIXELDEPTH_BPP_24_444 : | |
632 | STATE1_ASY_PIXELDEPTH_BPP_18_444; | |
633 | ||
634 | tegra_sor_writel(sor, STATE1, reg_val); | |
635 | ||
636 | /* | |
637 | * Skipping programming NV_HEAD_STATE0, assuming: | |
638 | * interlacing: PROGRESSIVE, dynamic range: VESA, colorspace: RGB | |
639 | */ | |
640 | vtotal = timing->vsync_len.typ + timing->vback_porch.typ + | |
641 | timing->vactive.typ + timing->vfront_porch.typ; | |
642 | htotal = timing->hsync_len.typ + timing->hback_porch.typ + | |
643 | timing->hactive.typ + timing->hfront_porch.typ; | |
644 | ||
645 | tegra_sor_writel(sor, NV_HEAD_STATE1(head_num), | |
646 | vtotal << NV_HEAD_STATE1_VTOTAL_SHIFT | | |
647 | htotal << NV_HEAD_STATE1_HTOTAL_SHIFT); | |
648 | ||
649 | vsync_end = timing->vsync_len.typ - 1; | |
650 | hsync_end = timing->hsync_len.typ - 1; | |
651 | tegra_sor_writel(sor, NV_HEAD_STATE2(head_num), | |
652 | vsync_end << NV_HEAD_STATE2_VSYNC_END_SHIFT | | |
653 | hsync_end << NV_HEAD_STATE2_HSYNC_END_SHIFT); | |
654 | ||
655 | vblank_end = vsync_end + timing->vback_porch.typ; | |
656 | hblank_end = hsync_end + timing->hback_porch.typ; | |
657 | tegra_sor_writel(sor, NV_HEAD_STATE3(head_num), | |
658 | vblank_end << NV_HEAD_STATE3_VBLANK_END_SHIFT | | |
659 | hblank_end << NV_HEAD_STATE3_HBLANK_END_SHIFT); | |
660 | ||
661 | vblank_start = vblank_end + timing->vactive.typ; | |
662 | hblank_start = hblank_end + timing->hactive.typ; | |
663 | tegra_sor_writel(sor, NV_HEAD_STATE4(head_num), | |
664 | vblank_start << NV_HEAD_STATE4_VBLANK_START_SHIFT | | |
665 | hblank_start << NV_HEAD_STATE4_HBLANK_START_SHIFT); | |
666 | ||
667 | /* TODO: adding interlace mode support */ | |
668 | tegra_sor_writel(sor, NV_HEAD_STATE5(head_num), 0x1); | |
669 | ||
670 | tegra_sor_write_field(sor, CSTM, | |
671 | CSTM_ROTCLK_DEFAULT_MASK | | |
672 | CSTM_LVDS_EN_ENABLE, | |
673 | 2 << CSTM_ROTCLK_SHIFT | | |
674 | is_lvds ? CSTM_LVDS_EN_ENABLE : | |
675 | CSTM_LVDS_EN_DISABLE); | |
676 | ||
677 | tegra_dc_sor_config_pwm(sor, 1024, 1024); | |
678 | } | |
679 | ||
680 | static void tegra_dc_sor_enable_dc(struct dc_ctlr *disp_ctrl) | |
681 | { | |
682 | u32 reg_val = readl(&disp_ctrl->cmd.state_access); | |
683 | ||
684 | writel(reg_val | WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access); | |
685 | writel(VSYNC_H_POSITION(1), &disp_ctrl->disp.disp_timing_opt); | |
686 | ||
687 | /* Enable DC now - otherwise pure text console may not show. */ | |
688 | writel(CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT, | |
689 | &disp_ctrl->cmd.disp_cmd); | |
690 | writel(reg_val, &disp_ctrl->cmd.state_access); | |
691 | } | |
692 | ||
d7659212 | 693 | int tegra_dc_sor_enable_dp(struct udevice *dev, |
00f37327 SG |
694 | const struct tegra_dp_link_config *link_cfg) |
695 | { | |
d7659212 | 696 | struct tegra_dc_sor_data *sor = dev_get_priv(dev); |
00f37327 SG |
697 | int ret; |
698 | ||
699 | tegra_sor_write_field(sor, CLK_CNTRL, | |
700 | CLK_CNTRL_DP_CLK_SEL_MASK, | |
701 | CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK); | |
702 | ||
703 | tegra_sor_write_field(sor, PLL2, | |
704 | PLL2_AUX6_BANDGAP_POWERDOWN_MASK, | |
705 | PLL2_AUX6_BANDGAP_POWERDOWN_DISABLE); | |
706 | udelay(25); | |
707 | ||
708 | tegra_sor_write_field(sor, PLL3, | |
709 | PLL3_PLLVDD_MODE_MASK, | |
710 | PLL3_PLLVDD_MODE_V3_3); | |
711 | tegra_sor_writel(sor, PLL0, | |
712 | 0xf << PLL0_ICHPMP_SHFIT | | |
713 | 0x3 << PLL0_VCOCAP_SHIFT | | |
714 | PLL0_PLLREG_LEVEL_V45 | | |
715 | PLL0_RESISTORSEL_EXT | | |
716 | PLL0_PWR_ON | PLL0_VCOPD_RESCIND); | |
717 | tegra_sor_write_field(sor, PLL2, | |
718 | PLL2_AUX1_SEQ_MASK | | |
719 | PLL2_AUX9_LVDSEN_OVERRIDE | | |
720 | PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK, | |
721 | PLL2_AUX1_SEQ_PLLCAPPD_OVERRIDE | | |
722 | PLL2_AUX9_LVDSEN_OVERRIDE | | |
723 | PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE); | |
724 | tegra_sor_writel(sor, PLL1, PLL1_TERM_COMPOUT_HIGH | | |
725 | PLL1_TMDS_TERM_ENABLE); | |
726 | ||
727 | if (tegra_dc_sor_poll_register(sor, PLL2, | |
728 | PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_MASK, | |
729 | PLL2_AUX8_SEQ_PLLCAPPD_ENFORCE_DISABLE, | |
730 | 100, TEGRA_SOR_TIMEOUT_MS)) { | |
731 | printf("DP failed to lock PLL\n"); | |
732 | return -EIO; | |
733 | } | |
734 | ||
735 | tegra_sor_write_field(sor, PLL2, PLL2_AUX2_MASK | | |
736 | PLL2_AUX7_PORT_POWERDOWN_MASK, | |
737 | PLL2_AUX2_OVERRIDE_POWERDOWN | | |
738 | PLL2_AUX7_PORT_POWERDOWN_DISABLE); | |
739 | ||
d7659212 | 740 | ret = tegra_dc_sor_power_up(dev, 0); |
00f37327 SG |
741 | if (ret) { |
742 | debug("DP failed to power up\n"); | |
743 | return ret; | |
744 | } | |
745 | ||
746 | /* re-enable SOR clock */ | |
747 | clock_sor_enable_edp_clock(); | |
748 | ||
749 | /* Power up lanes */ | |
d7659212 | 750 | tegra_dc_sor_power_dplanes(dev, link_cfg->lane_count, 1); |
00f37327 | 751 | |
d7659212 | 752 | tegra_dc_sor_set_dp_mode(dev, link_cfg); |
00f37327 SG |
753 | debug("%s ret\n", __func__); |
754 | ||
755 | return 0; | |
756 | } | |
757 | ||
d7659212 | 758 | int tegra_dc_sor_attach(struct udevice *dc_dev, struct udevice *dev, |
00f37327 SG |
759 | const struct tegra_dp_link_config *link_cfg, |
760 | const struct display_timing *timing) | |
761 | { | |
d7659212 | 762 | struct tegra_dc_sor_data *sor = dev_get_priv(dev); |
00f37327 SG |
763 | struct dc_ctlr *disp_ctrl; |
764 | u32 reg_val; | |
00f37327 SG |
765 | |
766 | /* Use the first display controller */ | |
767 | debug("%s\n", __func__); | |
079ff3b9 | 768 | disp_ctrl = (struct dc_ctlr *)dev_read_addr(dc_dev); |
00f37327 SG |
769 | |
770 | tegra_dc_sor_enable_dc(disp_ctrl); | |
771 | tegra_dc_sor_config_panel(sor, 0, link_cfg, timing); | |
772 | ||
773 | writel(0x9f00, &disp_ctrl->cmd.state_ctrl); | |
774 | writel(0x9f, &disp_ctrl->cmd.state_ctrl); | |
775 | ||
776 | writel(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | | |
777 | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE, | |
778 | &disp_ctrl->cmd.disp_pow_ctrl); | |
779 | ||
780 | reg_val = tegra_sor_readl(sor, TEST); | |
781 | if (reg_val & TEST_ATTACHED_TRUE) | |
782 | return -EEXIST; | |
783 | ||
784 | tegra_sor_writel(sor, SUPER_STATE1, | |
785 | SUPER_STATE1_ATTACHED_NO); | |
786 | ||
787 | /* | |
788 | * Enable display2sor clock at least 2 cycles before DC start, | |
789 | * to clear sor internal valid signal. | |
790 | */ | |
791 | writel(SOR_ENABLE, &disp_ctrl->disp.disp_win_opt); | |
792 | writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl); | |
793 | writel(0, &disp_ctrl->disp.disp_win_opt); | |
794 | writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl); | |
795 | ||
796 | /* Attach head */ | |
797 | tegra_dc_sor_update(sor); | |
798 | tegra_sor_writel(sor, SUPER_STATE1, | |
799 | SUPER_STATE1_ATTACHED_YES); | |
800 | tegra_sor_writel(sor, SUPER_STATE1, | |
801 | SUPER_STATE1_ATTACHED_YES | | |
802 | SUPER_STATE1_ASY_HEAD_OP_AWAKE | | |
803 | SUPER_STATE1_ASY_ORMODE_NORMAL); | |
804 | tegra_dc_sor_super_update(sor); | |
805 | ||
806 | /* Enable dc */ | |
807 | reg_val = readl(&disp_ctrl->cmd.state_access); | |
808 | writel(reg_val | WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access); | |
809 | writel(CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT, | |
810 | &disp_ctrl->cmd.disp_cmd); | |
811 | writel(SOR_ENABLE, &disp_ctrl->disp.disp_win_opt); | |
812 | writel(reg_val, &disp_ctrl->cmd.state_access); | |
813 | ||
814 | if (tegra_dc_sor_poll_register(sor, TEST, | |
815 | TEST_ACT_HEAD_OPMODE_DEFAULT_MASK, | |
816 | TEST_ACT_HEAD_OPMODE_AWAKE, | |
817 | 100, | |
818 | TEGRA_SOR_ATTACH_TIMEOUT_MS)) { | |
819 | printf("dc timeout waiting for OPMOD = AWAKE\n"); | |
820 | return -ETIMEDOUT; | |
821 | } else { | |
822 | debug("%s: sor is attached\n", __func__); | |
823 | } | |
824 | ||
825 | #if DEBUG_SOR | |
826 | dump_sor_reg(sor); | |
827 | #endif | |
828 | debug("%s: ret=%d\n", __func__, 0); | |
829 | ||
830 | return 0; | |
831 | } | |
832 | ||
d7659212 | 833 | void tegra_dc_sor_set_lane_parm(struct udevice *dev, |
00f37327 SG |
834 | const struct tegra_dp_link_config *link_cfg) |
835 | { | |
d7659212 SG |
836 | struct tegra_dc_sor_data *sor = dev_get_priv(dev); |
837 | ||
00f37327 SG |
838 | tegra_sor_writel(sor, LANE_DRIVE_CURRENT(sor->portnum), |
839 | link_cfg->drive_current); | |
840 | tegra_sor_writel(sor, PR(sor->portnum), | |
841 | link_cfg->preemphasis); | |
842 | tegra_sor_writel(sor, POSTCURSOR(sor->portnum), | |
843 | link_cfg->postcursor); | |
844 | tegra_sor_writel(sor, LVDS, 0); | |
845 | ||
d7659212 SG |
846 | tegra_dc_sor_set_link_bandwidth(dev, link_cfg->link_bw); |
847 | tegra_dc_sor_set_lane_count(dev, link_cfg->lane_count); | |
00f37327 SG |
848 | |
849 | tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), | |
850 | DP_PADCTL_TX_PU_ENABLE | | |
851 | DP_PADCTL_TX_PU_VALUE_DEFAULT_MASK, | |
852 | DP_PADCTL_TX_PU_ENABLE | | |
853 | 2 << DP_PADCTL_TX_PU_VALUE_SHIFT); | |
854 | ||
855 | /* Precharge */ | |
856 | tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), 0xf0, 0xf0); | |
857 | udelay(20); | |
858 | ||
859 | tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), 0xf0, 0x0); | |
860 | } | |
861 | ||
d7659212 | 862 | int tegra_dc_sor_set_voltage_swing(struct udevice *dev, |
dedc44b4 SG |
863 | const struct tegra_dp_link_config *link_cfg) |
864 | { | |
d7659212 | 865 | struct tegra_dc_sor_data *sor = dev_get_priv(dev); |
dedc44b4 SG |
866 | u32 drive_current = 0; |
867 | u32 pre_emphasis = 0; | |
868 | ||
869 | /* Set to a known-good pre-calibrated setting */ | |
870 | switch (link_cfg->link_bw) { | |
871 | case SOR_LINK_SPEED_G1_62: | |
872 | case SOR_LINK_SPEED_G2_7: | |
873 | drive_current = 0x13131313; | |
874 | pre_emphasis = 0; | |
875 | break; | |
876 | case SOR_LINK_SPEED_G5_4: | |
877 | debug("T124 does not support 5.4G link clock.\n"); | |
878 | default: | |
879 | debug("Invalid sor link bandwidth: %d\n", link_cfg->link_bw); | |
880 | return -ENOLINK; | |
881 | } | |
882 | ||
883 | tegra_sor_writel(sor, LANE_DRIVE_CURRENT(sor->portnum), drive_current); | |
884 | tegra_sor_writel(sor, PR(sor->portnum), pre_emphasis); | |
885 | ||
886 | return 0; | |
887 | } | |
888 | ||
d7659212 | 889 | void tegra_dc_sor_power_down_unused_lanes(struct udevice *dev, |
00f37327 SG |
890 | const struct tegra_dp_link_config *link_cfg) |
891 | { | |
d7659212 | 892 | struct tegra_dc_sor_data *sor = dev_get_priv(dev); |
00f37327 | 893 | u32 pad_ctrl = 0; |
00f37327 SG |
894 | int err = 0; |
895 | ||
896 | switch (link_cfg->lane_count) { | |
897 | case 4: | |
898 | pad_ctrl = DP_PADCTL_PD_TXD_0_NO | | |
899 | DP_PADCTL_PD_TXD_1_NO | | |
900 | DP_PADCTL_PD_TXD_2_NO | | |
901 | DP_PADCTL_PD_TXD_3_NO; | |
902 | break; | |
903 | case 2: | |
904 | pad_ctrl = DP_PADCTL_PD_TXD_0_NO | | |
905 | DP_PADCTL_PD_TXD_1_NO | | |
906 | DP_PADCTL_PD_TXD_2_YES | | |
907 | DP_PADCTL_PD_TXD_3_YES; | |
908 | break; | |
909 | case 1: | |
910 | pad_ctrl = DP_PADCTL_PD_TXD_0_NO | | |
911 | DP_PADCTL_PD_TXD_1_YES | | |
912 | DP_PADCTL_PD_TXD_2_YES | | |
913 | DP_PADCTL_PD_TXD_3_YES; | |
914 | break; | |
915 | default: | |
916 | printf("Invalid sor lane count: %u\n", link_cfg->lane_count); | |
917 | return; | |
918 | } | |
919 | ||
920 | pad_ctrl |= DP_PADCTL_PAD_CAL_PD_POWERDOWN; | |
921 | tegra_sor_writel(sor, DP_PADCTL(sor->portnum), pad_ctrl); | |
922 | ||
923 | err = tegra_dc_sor_enable_lane_sequencer(sor, 0, 0); | |
924 | if (err) { | |
925 | debug("Wait for lane power down failed: %d\n", err); | |
926 | return; | |
927 | } | |
dedc44b4 | 928 | } |
00f37327 | 929 | |
d7659212 | 930 | int tegra_sor_precharge_lanes(struct udevice *dev, |
dedc44b4 SG |
931 | const struct tegra_dp_link_config *cfg) |
932 | { | |
d7659212 | 933 | struct tegra_dc_sor_data *sor = dev_get_priv(dev); |
dedc44b4 SG |
934 | u32 val = 0; |
935 | ||
936 | switch (cfg->lane_count) { | |
937 | case 4: | |
938 | val |= (DP_PADCTL_PD_TXD_3_NO | | |
939 | DP_PADCTL_PD_TXD_2_NO); | |
940 | /* fall through */ | |
941 | case 2: | |
942 | val |= DP_PADCTL_PD_TXD_1_NO; | |
943 | /* fall through */ | |
944 | case 1: | |
945 | val |= DP_PADCTL_PD_TXD_0_NO; | |
00f37327 | 946 | break; |
00f37327 | 947 | default: |
dedc44b4 SG |
948 | debug("dp: invalid lane number %d\n", cfg->lane_count); |
949 | return -EINVAL; | |
00f37327 SG |
950 | } |
951 | ||
dedc44b4 SG |
952 | tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), |
953 | (0xf << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT), | |
954 | (val << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT)); | |
955 | udelay(100); | |
956 | tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), | |
957 | (0xf << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT), | |
958 | 0); | |
959 | ||
960 | return 0; | |
961 | } | |
962 | ||
963 | static void tegra_dc_sor_enable_sor(struct dc_ctlr *disp_ctrl, bool enable) | |
964 | { | |
965 | u32 reg_val = readl(&disp_ctrl->disp.disp_win_opt); | |
966 | ||
967 | reg_val = enable ? reg_val | SOR_ENABLE : reg_val & ~SOR_ENABLE; | |
968 | writel(reg_val, &disp_ctrl->disp.disp_win_opt); | |
969 | } | |
970 | ||
d7659212 | 971 | int tegra_dc_sor_detach(struct udevice *dc_dev, struct udevice *dev) |
dedc44b4 | 972 | { |
d7659212 | 973 | struct tegra_dc_sor_data *sor = dev_get_priv(dev); |
dedc44b4 | 974 | int dc_reg_ctx[DC_REG_SAVE_SPACE]; |
dedc44b4 SG |
975 | struct dc_ctlr *disp_ctrl; |
976 | unsigned long dc_int_mask; | |
dedc44b4 SG |
977 | int ret; |
978 | ||
979 | debug("%s\n", __func__); | |
980 | /* Use the first display controller */ | |
079ff3b9 | 981 | disp_ctrl = (struct dc_ctlr *)dev_read_addr(dev); |
dedc44b4 SG |
982 | |
983 | /* Sleep mode */ | |
984 | tegra_sor_writel(sor, SUPER_STATE1, SUPER_STATE1_ASY_HEAD_OP_SLEEP | | |
985 | SUPER_STATE1_ASY_ORMODE_SAFE | | |
986 | SUPER_STATE1_ATTACHED_YES); | |
987 | tegra_dc_sor_super_update(sor); | |
988 | ||
989 | tegra_dc_sor_disable_win_short_raster(disp_ctrl, dc_reg_ctx); | |
990 | ||
991 | if (tegra_dc_sor_poll_register(sor, TEST, | |
992 | TEST_ACT_HEAD_OPMODE_DEFAULT_MASK, | |
993 | TEST_ACT_HEAD_OPMODE_SLEEP, 100, | |
994 | TEGRA_SOR_ATTACH_TIMEOUT_MS)) { | |
995 | debug("dc timeout waiting for OPMOD = SLEEP\n"); | |
996 | ret = -ETIMEDOUT; | |
997 | goto err; | |
998 | } | |
999 | ||
1000 | tegra_sor_writel(sor, SUPER_STATE1, SUPER_STATE1_ASY_HEAD_OP_SLEEP | | |
1001 | SUPER_STATE1_ASY_ORMODE_SAFE | | |
1002 | SUPER_STATE1_ATTACHED_NO); | |
1003 | ||
1004 | /* Mask DC interrupts during the 2 dummy frames required for detach */ | |
1005 | dc_int_mask = readl(&disp_ctrl->cmd.int_mask); | |
1006 | writel(0, &disp_ctrl->cmd.int_mask); | |
1007 | ||
1008 | /* Stop DC->SOR path */ | |
1009 | tegra_dc_sor_enable_sor(disp_ctrl, false); | |
1010 | ret = tegra_dc_sor_general_act(disp_ctrl); | |
1011 | if (ret) | |
1012 | goto err; | |
1013 | ||
1014 | /* Stop DC */ | |
1015 | writel(CTRL_MODE_STOP << CTRL_MODE_SHIFT, &disp_ctrl->cmd.disp_cmd); | |
1016 | ret = tegra_dc_sor_general_act(disp_ctrl); | |
1017 | if (ret) | |
1018 | goto err; | |
1019 | ||
1020 | tegra_dc_sor_restore_win_and_raster(disp_ctrl, dc_reg_ctx); | |
1021 | ||
1022 | writel(dc_int_mask, &disp_ctrl->cmd.int_mask); | |
1023 | ||
1024 | return 0; | |
1025 | err: | |
1026 | debug("%s: ret=%d\n", __func__, ret); | |
1027 | ||
1028 | return ret; | |
00f37327 SG |
1029 | } |
1030 | ||
d7659212 | 1031 | static int tegra_sor_set_backlight(struct udevice *dev, int percent) |
00f37327 | 1032 | { |
d7659212 SG |
1033 | struct tegra_dc_sor_data *priv = dev_get_priv(dev); |
1034 | int ret; | |
1035 | ||
1036 | ret = panel_enable_backlight(priv->panel); | |
1037 | if (ret) { | |
1038 | debug("sor: Cannot enable panel backlight\n"); | |
1039 | return ret; | |
1040 | } | |
1041 | ||
1042 | return 0; | |
1043 | } | |
1044 | ||
1045 | static int tegra_sor_ofdata_to_platdata(struct udevice *dev) | |
1046 | { | |
1047 | struct tegra_dc_sor_data *priv = dev_get_priv(dev); | |
d7659212 | 1048 | int ret; |
00f37327 | 1049 | |
079ff3b9 | 1050 | priv->base = (void *)dev_read_addr(dev); |
00f37327 | 1051 | |
079ff3b9 SG |
1052 | priv->pmc_base = (void *)syscon_get_first_range(TEGRA_SYSCON_PMC); |
1053 | if (IS_ERR(priv->pmc_base)) | |
1054 | return PTR_ERR(priv->pmc_base); | |
00f37327 | 1055 | |
d7659212 SG |
1056 | ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev, "nvidia,panel", |
1057 | &priv->panel); | |
1058 | if (ret) { | |
1059 | debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__, | |
1060 | dev->name, ret); | |
1061 | return ret; | |
1062 | } | |
00f37327 SG |
1063 | |
1064 | return 0; | |
1065 | } | |
d7659212 SG |
1066 | |
1067 | static const struct video_bridge_ops tegra_sor_ops = { | |
1068 | .set_backlight = tegra_sor_set_backlight, | |
1069 | }; | |
1070 | ||
1071 | static const struct udevice_id tegra_sor_ids[] = { | |
1072 | { .compatible = "nvidia,tegra124-sor" }, | |
1073 | { } | |
1074 | }; | |
1075 | ||
1076 | U_BOOT_DRIVER(sor_tegra) = { | |
1077 | .name = "sor_tegra", | |
1078 | .id = UCLASS_VIDEO_BRIDGE, | |
1079 | .of_match = tegra_sor_ids, | |
1080 | .ofdata_to_platdata = tegra_sor_ofdata_to_platdata, | |
1081 | .ops = &tegra_sor_ops, | |
41575d8e | 1082 | .priv_auto = sizeof(struct tegra_dc_sor_data), |
d7659212 | 1083 | }; |