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