Commit | Line | Data |
---|---|---|
6b6b6042 TR |
1 | /* |
2 | * Copyright (C) 2013 NVIDIA Corporation | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | */ | |
8 | ||
9 | #include <linux/clk.h> | |
a82752e1 | 10 | #include <linux/debugfs.h> |
6b6b6042 TR |
11 | #include <linux/io.h> |
12 | #include <linux/platform_device.h> | |
13 | #include <linux/reset.h> | |
306a7f91 | 14 | |
7232398a | 15 | #include <soc/tegra/pmc.h> |
6b6b6042 TR |
16 | |
17 | #include <drm/drm_dp_helper.h> | |
18 | ||
19 | #include "dc.h" | |
20 | #include "drm.h" | |
21 | #include "sor.h" | |
22 | ||
23 | struct tegra_sor { | |
24 | struct host1x_client client; | |
25 | struct tegra_output output; | |
26 | struct device *dev; | |
27 | ||
28 | void __iomem *regs; | |
29 | ||
30 | struct reset_control *rst; | |
31 | struct clk *clk_parent; | |
32 | struct clk *clk_safe; | |
33 | struct clk *clk_dp; | |
34 | struct clk *clk; | |
35 | ||
36 | struct tegra_dpaux *dpaux; | |
37 | ||
86f5c52d | 38 | struct mutex lock; |
6b6b6042 | 39 | bool enabled; |
a82752e1 TR |
40 | |
41 | struct dentry *debugfs; | |
6b6b6042 TR |
42 | }; |
43 | ||
34fa183b TR |
44 | struct tegra_sor_config { |
45 | u32 bits_per_pixel; | |
46 | ||
47 | u32 active_polarity; | |
48 | u32 active_count; | |
49 | u32 tu_size; | |
50 | u32 active_frac; | |
51 | u32 watermark; | |
7890b576 TR |
52 | |
53 | u32 hblank_symbols; | |
54 | u32 vblank_symbols; | |
34fa183b TR |
55 | }; |
56 | ||
6b6b6042 TR |
57 | static inline struct tegra_sor * |
58 | host1x_client_to_sor(struct host1x_client *client) | |
59 | { | |
60 | return container_of(client, struct tegra_sor, client); | |
61 | } | |
62 | ||
63 | static inline struct tegra_sor *to_sor(struct tegra_output *output) | |
64 | { | |
65 | return container_of(output, struct tegra_sor, output); | |
66 | } | |
67 | ||
68 | static inline unsigned long tegra_sor_readl(struct tegra_sor *sor, | |
69 | unsigned long offset) | |
70 | { | |
71 | return readl(sor->regs + (offset << 2)); | |
72 | } | |
73 | ||
74 | static inline void tegra_sor_writel(struct tegra_sor *sor, unsigned long value, | |
75 | unsigned long offset) | |
76 | { | |
77 | writel(value, sor->regs + (offset << 2)); | |
78 | } | |
79 | ||
80 | static int tegra_sor_dp_train_fast(struct tegra_sor *sor, | |
81 | struct drm_dp_link *link) | |
82 | { | |
83 | unsigned long value; | |
84 | unsigned int i; | |
85 | u8 pattern; | |
86 | int err; | |
87 | ||
88 | /* setup lane parameters */ | |
89 | value = SOR_LANE_DRIVE_CURRENT_LANE3(0x40) | | |
90 | SOR_LANE_DRIVE_CURRENT_LANE2(0x40) | | |
91 | SOR_LANE_DRIVE_CURRENT_LANE1(0x40) | | |
92 | SOR_LANE_DRIVE_CURRENT_LANE0(0x40); | |
93 | tegra_sor_writel(sor, value, SOR_LANE_DRIVE_CURRENT_0); | |
94 | ||
95 | value = SOR_LANE_PREEMPHASIS_LANE3(0x0f) | | |
96 | SOR_LANE_PREEMPHASIS_LANE2(0x0f) | | |
97 | SOR_LANE_PREEMPHASIS_LANE1(0x0f) | | |
98 | SOR_LANE_PREEMPHASIS_LANE0(0x0f); | |
99 | tegra_sor_writel(sor, value, SOR_LANE_PREEMPHASIS_0); | |
100 | ||
101 | value = SOR_LANE_POST_CURSOR_LANE3(0x00) | | |
102 | SOR_LANE_POST_CURSOR_LANE2(0x00) | | |
103 | SOR_LANE_POST_CURSOR_LANE1(0x00) | | |
104 | SOR_LANE_POST_CURSOR_LANE0(0x00); | |
105 | tegra_sor_writel(sor, value, SOR_LANE_POST_CURSOR_0); | |
106 | ||
107 | /* disable LVDS mode */ | |
108 | tegra_sor_writel(sor, 0, SOR_LVDS); | |
109 | ||
110 | value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); | |
111 | value |= SOR_DP_PADCTL_TX_PU_ENABLE; | |
112 | value &= ~SOR_DP_PADCTL_TX_PU_MASK; | |
113 | value |= SOR_DP_PADCTL_TX_PU(2); /* XXX: don't hardcode? */ | |
114 | tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); | |
115 | ||
116 | value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); | |
117 | value |= SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 | | |
118 | SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0; | |
119 | tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); | |
120 | ||
121 | usleep_range(10, 100); | |
122 | ||
123 | value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); | |
124 | value &= ~(SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 | | |
125 | SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0); | |
126 | tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); | |
127 | ||
128 | err = tegra_dpaux_prepare(sor->dpaux, DP_SET_ANSI_8B10B); | |
129 | if (err < 0) | |
130 | return err; | |
131 | ||
132 | for (i = 0, value = 0; i < link->num_lanes; i++) { | |
133 | unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | | |
134 | SOR_DP_TPG_SCRAMBLER_NONE | | |
135 | SOR_DP_TPG_PATTERN_TRAIN1; | |
136 | value = (value << 8) | lane; | |
137 | } | |
138 | ||
139 | tegra_sor_writel(sor, value, SOR_DP_TPG); | |
140 | ||
141 | pattern = DP_TRAINING_PATTERN_1; | |
142 | ||
143 | err = tegra_dpaux_train(sor->dpaux, link, pattern); | |
144 | if (err < 0) | |
145 | return err; | |
146 | ||
147 | value = tegra_sor_readl(sor, SOR_DP_SPARE_0); | |
148 | value |= SOR_DP_SPARE_SEQ_ENABLE; | |
149 | value &= ~SOR_DP_SPARE_PANEL_INTERNAL; | |
150 | value |= SOR_DP_SPARE_MACRO_SOR_CLK; | |
151 | tegra_sor_writel(sor, value, SOR_DP_SPARE_0); | |
152 | ||
153 | for (i = 0, value = 0; i < link->num_lanes; i++) { | |
154 | unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | | |
155 | SOR_DP_TPG_SCRAMBLER_NONE | | |
156 | SOR_DP_TPG_PATTERN_TRAIN2; | |
157 | value = (value << 8) | lane; | |
158 | } | |
159 | ||
160 | tegra_sor_writel(sor, value, SOR_DP_TPG); | |
161 | ||
162 | pattern = DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_2; | |
163 | ||
164 | err = tegra_dpaux_train(sor->dpaux, link, pattern); | |
165 | if (err < 0) | |
166 | return err; | |
167 | ||
168 | for (i = 0, value = 0; i < link->num_lanes; i++) { | |
169 | unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | | |
170 | SOR_DP_TPG_SCRAMBLER_GALIOS | | |
171 | SOR_DP_TPG_PATTERN_NONE; | |
172 | value = (value << 8) | lane; | |
173 | } | |
174 | ||
175 | tegra_sor_writel(sor, value, SOR_DP_TPG); | |
176 | ||
177 | pattern = DP_TRAINING_PATTERN_DISABLE; | |
178 | ||
179 | err = tegra_dpaux_train(sor->dpaux, link, pattern); | |
180 | if (err < 0) | |
181 | return err; | |
182 | ||
183 | return 0; | |
184 | } | |
185 | ||
186 | static void tegra_sor_super_update(struct tegra_sor *sor) | |
187 | { | |
188 | tegra_sor_writel(sor, 0, SOR_SUPER_STATE_0); | |
189 | tegra_sor_writel(sor, 1, SOR_SUPER_STATE_0); | |
190 | tegra_sor_writel(sor, 0, SOR_SUPER_STATE_0); | |
191 | } | |
192 | ||
193 | static void tegra_sor_update(struct tegra_sor *sor) | |
194 | { | |
195 | tegra_sor_writel(sor, 0, SOR_STATE_0); | |
196 | tegra_sor_writel(sor, 1, SOR_STATE_0); | |
197 | tegra_sor_writel(sor, 0, SOR_STATE_0); | |
198 | } | |
199 | ||
200 | static int tegra_sor_setup_pwm(struct tegra_sor *sor, unsigned long timeout) | |
201 | { | |
202 | unsigned long value; | |
203 | ||
204 | value = tegra_sor_readl(sor, SOR_PWM_DIV); | |
205 | value &= ~SOR_PWM_DIV_MASK; | |
206 | value |= 0x400; /* period */ | |
207 | tegra_sor_writel(sor, value, SOR_PWM_DIV); | |
208 | ||
209 | value = tegra_sor_readl(sor, SOR_PWM_CTL); | |
210 | value &= ~SOR_PWM_CTL_DUTY_CYCLE_MASK; | |
211 | value |= 0x400; /* duty cycle */ | |
212 | value &= ~SOR_PWM_CTL_CLK_SEL; /* clock source: PCLK */ | |
213 | value |= SOR_PWM_CTL_TRIGGER; | |
214 | tegra_sor_writel(sor, value, SOR_PWM_CTL); | |
215 | ||
216 | timeout = jiffies + msecs_to_jiffies(timeout); | |
217 | ||
218 | while (time_before(jiffies, timeout)) { | |
219 | value = tegra_sor_readl(sor, SOR_PWM_CTL); | |
220 | if ((value & SOR_PWM_CTL_TRIGGER) == 0) | |
221 | return 0; | |
222 | ||
223 | usleep_range(25, 100); | |
224 | } | |
225 | ||
226 | return -ETIMEDOUT; | |
227 | } | |
228 | ||
229 | static int tegra_sor_attach(struct tegra_sor *sor) | |
230 | { | |
231 | unsigned long value, timeout; | |
232 | ||
233 | /* wake up in normal mode */ | |
234 | value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); | |
235 | value |= SOR_SUPER_STATE_HEAD_MODE_AWAKE; | |
236 | value |= SOR_SUPER_STATE_MODE_NORMAL; | |
237 | tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); | |
238 | tegra_sor_super_update(sor); | |
239 | ||
240 | /* attach */ | |
241 | value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); | |
242 | value |= SOR_SUPER_STATE_ATTACHED; | |
243 | tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); | |
244 | tegra_sor_super_update(sor); | |
245 | ||
246 | timeout = jiffies + msecs_to_jiffies(250); | |
247 | ||
248 | while (time_before(jiffies, timeout)) { | |
249 | value = tegra_sor_readl(sor, SOR_TEST); | |
250 | if ((value & SOR_TEST_ATTACHED) != 0) | |
251 | return 0; | |
252 | ||
253 | usleep_range(25, 100); | |
254 | } | |
255 | ||
256 | return -ETIMEDOUT; | |
257 | } | |
258 | ||
259 | static int tegra_sor_wakeup(struct tegra_sor *sor) | |
260 | { | |
261 | struct tegra_dc *dc = to_tegra_dc(sor->output.encoder.crtc); | |
262 | unsigned long value, timeout; | |
263 | ||
264 | /* enable display controller outputs */ | |
265 | value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); | |
266 | value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | | |
267 | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; | |
268 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); | |
269 | ||
270 | tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); | |
271 | tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); | |
272 | ||
273 | timeout = jiffies + msecs_to_jiffies(250); | |
274 | ||
275 | /* wait for head to wake up */ | |
276 | while (time_before(jiffies, timeout)) { | |
277 | value = tegra_sor_readl(sor, SOR_TEST); | |
278 | value &= SOR_TEST_HEAD_MODE_MASK; | |
279 | ||
280 | if (value == SOR_TEST_HEAD_MODE_AWAKE) | |
281 | return 0; | |
282 | ||
283 | usleep_range(25, 100); | |
284 | } | |
285 | ||
286 | return -ETIMEDOUT; | |
287 | } | |
288 | ||
289 | static int tegra_sor_power_up(struct tegra_sor *sor, unsigned long timeout) | |
290 | { | |
291 | unsigned long value; | |
292 | ||
293 | value = tegra_sor_readl(sor, SOR_PWR); | |
294 | value |= SOR_PWR_TRIGGER | SOR_PWR_NORMAL_STATE_PU; | |
295 | tegra_sor_writel(sor, value, SOR_PWR); | |
296 | ||
297 | timeout = jiffies + msecs_to_jiffies(timeout); | |
298 | ||
299 | while (time_before(jiffies, timeout)) { | |
300 | value = tegra_sor_readl(sor, SOR_PWR); | |
301 | if ((value & SOR_PWR_TRIGGER) == 0) | |
302 | return 0; | |
303 | ||
304 | usleep_range(25, 100); | |
305 | } | |
306 | ||
307 | return -ETIMEDOUT; | |
308 | } | |
309 | ||
34fa183b TR |
310 | struct tegra_sor_params { |
311 | /* number of link clocks per line */ | |
312 | unsigned int num_clocks; | |
313 | /* ratio between input and output */ | |
314 | u64 ratio; | |
315 | /* precision factor */ | |
316 | u64 precision; | |
317 | ||
318 | unsigned int active_polarity; | |
319 | unsigned int active_count; | |
320 | unsigned int active_frac; | |
321 | unsigned int tu_size; | |
322 | unsigned int error; | |
323 | }; | |
324 | ||
325 | static int tegra_sor_compute_params(struct tegra_sor *sor, | |
326 | struct tegra_sor_params *params, | |
327 | unsigned int tu_size) | |
328 | { | |
329 | u64 active_sym, active_count, frac, approx; | |
330 | u32 active_polarity, active_frac = 0; | |
331 | const u64 f = params->precision; | |
332 | s64 error; | |
333 | ||
334 | active_sym = params->ratio * tu_size; | |
335 | active_count = div_u64(active_sym, f) * f; | |
336 | frac = active_sym - active_count; | |
337 | ||
338 | /* fraction < 0.5 */ | |
339 | if (frac >= (f / 2)) { | |
340 | active_polarity = 1; | |
341 | frac = f - frac; | |
342 | } else { | |
343 | active_polarity = 0; | |
344 | } | |
345 | ||
346 | if (frac != 0) { | |
347 | frac = div_u64(f * f, frac); /* 1/fraction */ | |
348 | if (frac <= (15 * f)) { | |
349 | active_frac = div_u64(frac, f); | |
350 | ||
351 | /* round up */ | |
352 | if (active_polarity) | |
353 | active_frac++; | |
354 | } else { | |
355 | active_frac = active_polarity ? 1 : 15; | |
356 | } | |
357 | } | |
358 | ||
359 | if (active_frac == 1) | |
360 | active_polarity = 0; | |
361 | ||
362 | if (active_polarity == 1) { | |
363 | if (active_frac) { | |
364 | approx = active_count + (active_frac * (f - 1)) * f; | |
365 | approx = div_u64(approx, active_frac * f); | |
366 | } else { | |
367 | approx = active_count + f; | |
368 | } | |
369 | } else { | |
370 | if (active_frac) | |
371 | approx = active_count + div_u64(f, active_frac); | |
372 | else | |
373 | approx = active_count; | |
374 | } | |
375 | ||
376 | error = div_s64(active_sym - approx, tu_size); | |
377 | error *= params->num_clocks; | |
378 | ||
379 | if (error <= 0 && abs64(error) < params->error) { | |
380 | params->active_count = div_u64(active_count, f); | |
381 | params->active_polarity = active_polarity; | |
382 | params->active_frac = active_frac; | |
383 | params->error = abs64(error); | |
384 | params->tu_size = tu_size; | |
385 | ||
386 | if (error == 0) | |
387 | return true; | |
388 | } | |
389 | ||
390 | return false; | |
391 | } | |
392 | ||
393 | static int tegra_sor_calc_config(struct tegra_sor *sor, | |
394 | struct drm_display_mode *mode, | |
395 | struct tegra_sor_config *config, | |
396 | struct drm_dp_link *link) | |
397 | { | |
398 | const u64 f = 100000, link_rate = link->rate * 1000; | |
399 | const u64 pclk = mode->clock * 1000; | |
7890b576 | 400 | u64 input, output, watermark, num; |
34fa183b | 401 | struct tegra_sor_params params; |
34fa183b TR |
402 | u32 num_syms_per_line; |
403 | unsigned int i; | |
404 | ||
405 | if (!link_rate || !link->num_lanes || !pclk || !config->bits_per_pixel) | |
406 | return -EINVAL; | |
407 | ||
408 | output = link_rate * 8 * link->num_lanes; | |
409 | input = pclk * config->bits_per_pixel; | |
410 | ||
411 | if (input >= output) | |
412 | return -ERANGE; | |
413 | ||
414 | memset(¶ms, 0, sizeof(params)); | |
415 | params.ratio = div64_u64(input * f, output); | |
416 | params.num_clocks = div_u64(link_rate * mode->hdisplay, pclk); | |
417 | params.precision = f; | |
418 | params.error = 64 * f; | |
419 | params.tu_size = 64; | |
420 | ||
421 | for (i = params.tu_size; i >= 32; i--) | |
422 | if (tegra_sor_compute_params(sor, ¶ms, i)) | |
423 | break; | |
424 | ||
425 | if (params.active_frac == 0) { | |
426 | config->active_polarity = 0; | |
427 | config->active_count = params.active_count; | |
428 | ||
429 | if (!params.active_polarity) | |
430 | config->active_count--; | |
431 | ||
432 | config->tu_size = params.tu_size; | |
433 | config->active_frac = 1; | |
434 | } else { | |
435 | config->active_polarity = params.active_polarity; | |
436 | config->active_count = params.active_count; | |
437 | config->active_frac = params.active_frac; | |
438 | config->tu_size = params.tu_size; | |
439 | } | |
440 | ||
441 | dev_dbg(sor->dev, | |
442 | "polarity: %d active count: %d tu size: %d active frac: %d\n", | |
443 | config->active_polarity, config->active_count, | |
444 | config->tu_size, config->active_frac); | |
445 | ||
446 | watermark = params.ratio * config->tu_size * (f - params.ratio); | |
447 | watermark = div_u64(watermark, f); | |
448 | ||
449 | watermark = div_u64(watermark + params.error, f); | |
450 | config->watermark = watermark + (config->bits_per_pixel / 8) + 2; | |
451 | num_syms_per_line = (mode->hdisplay * config->bits_per_pixel) * | |
452 | (link->num_lanes * 8); | |
453 | ||
454 | if (config->watermark > 30) { | |
455 | config->watermark = 30; | |
456 | dev_err(sor->dev, | |
457 | "unable to compute TU size, forcing watermark to %u\n", | |
458 | config->watermark); | |
459 | } else if (config->watermark > num_syms_per_line) { | |
460 | config->watermark = num_syms_per_line; | |
461 | dev_err(sor->dev, "watermark too high, forcing to %u\n", | |
462 | config->watermark); | |
463 | } | |
464 | ||
7890b576 TR |
465 | /* compute the number of symbols per horizontal blanking interval */ |
466 | num = ((mode->htotal - mode->hdisplay) - 7) * link_rate; | |
467 | config->hblank_symbols = div_u64(num, pclk); | |
468 | ||
469 | if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING) | |
470 | config->hblank_symbols -= 3; | |
471 | ||
472 | config->hblank_symbols -= 12 / link->num_lanes; | |
473 | ||
474 | /* compute the number of symbols per vertical blanking interval */ | |
475 | num = (mode->hdisplay - 25) * link_rate; | |
476 | config->vblank_symbols = div_u64(num, pclk); | |
477 | config->vblank_symbols -= 36 / link->num_lanes + 4; | |
478 | ||
479 | dev_dbg(sor->dev, "blank symbols: H:%u V:%u\n", config->hblank_symbols, | |
480 | config->vblank_symbols); | |
481 | ||
34fa183b TR |
482 | return 0; |
483 | } | |
484 | ||
6b6b6042 TR |
485 | static int tegra_output_sor_enable(struct tegra_output *output) |
486 | { | |
487 | struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); | |
488 | struct drm_display_mode *mode = &dc->base.mode; | |
489 | unsigned int vbe, vse, hbe, hse, vbs, hbs, i; | |
490 | struct tegra_sor *sor = to_sor(output); | |
34fa183b TR |
491 | struct tegra_sor_config config; |
492 | struct drm_dp_link link; | |
493 | struct drm_dp_aux *aux; | |
6b6b6042 | 494 | unsigned long value; |
86f5c52d TR |
495 | int err = 0; |
496 | ||
497 | mutex_lock(&sor->lock); | |
6b6b6042 TR |
498 | |
499 | if (sor->enabled) | |
86f5c52d | 500 | goto unlock; |
6b6b6042 TR |
501 | |
502 | err = clk_prepare_enable(sor->clk); | |
503 | if (err < 0) | |
86f5c52d | 504 | goto unlock; |
6b6b6042 TR |
505 | |
506 | reset_control_deassert(sor->rst); | |
507 | ||
34fa183b TR |
508 | /* FIXME: properly convert to struct drm_dp_aux */ |
509 | aux = (struct drm_dp_aux *)sor->dpaux; | |
510 | ||
6b6b6042 TR |
511 | if (sor->dpaux) { |
512 | err = tegra_dpaux_enable(sor->dpaux); | |
513 | if (err < 0) | |
514 | dev_err(sor->dev, "failed to enable DP: %d\n", err); | |
34fa183b TR |
515 | |
516 | err = drm_dp_link_probe(aux, &link); | |
517 | if (err < 0) { | |
518 | dev_err(sor->dev, "failed to probe eDP link: %d\n", | |
519 | err); | |
2263c460 | 520 | goto unlock; |
34fa183b | 521 | } |
6b6b6042 TR |
522 | } |
523 | ||
524 | err = clk_set_parent(sor->clk, sor->clk_safe); | |
525 | if (err < 0) | |
526 | dev_err(sor->dev, "failed to set safe parent clock: %d\n", err); | |
527 | ||
34fa183b | 528 | memset(&config, 0, sizeof(config)); |
054b1bd1 | 529 | config.bits_per_pixel = output->connector.display_info.bpc * 3; |
34fa183b TR |
530 | |
531 | err = tegra_sor_calc_config(sor, mode, &config, &link); | |
532 | if (err < 0) | |
533 | dev_err(sor->dev, "failed to compute link configuration: %d\n", | |
534 | err); | |
535 | ||
6b6b6042 TR |
536 | value = tegra_sor_readl(sor, SOR_CLK_CNTRL); |
537 | value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK; | |
538 | value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK; | |
539 | tegra_sor_writel(sor, value, SOR_CLK_CNTRL); | |
540 | ||
541 | value = tegra_sor_readl(sor, SOR_PLL_2); | |
542 | value &= ~SOR_PLL_2_BANDGAP_POWERDOWN; | |
543 | tegra_sor_writel(sor, value, SOR_PLL_2); | |
544 | usleep_range(20, 100); | |
545 | ||
546 | value = tegra_sor_readl(sor, SOR_PLL_3); | |
547 | value |= SOR_PLL_3_PLL_VDD_MODE_V3_3; | |
548 | tegra_sor_writel(sor, value, SOR_PLL_3); | |
549 | ||
550 | value = SOR_PLL_0_ICHPMP(0xf) | SOR_PLL_0_VCOCAP_RST | | |
551 | SOR_PLL_0_PLLREG_LEVEL_V45 | SOR_PLL_0_RESISTOR_EXT; | |
552 | tegra_sor_writel(sor, value, SOR_PLL_0); | |
553 | ||
554 | value = tegra_sor_readl(sor, SOR_PLL_2); | |
555 | value |= SOR_PLL_2_SEQ_PLLCAPPD; | |
556 | value &= ~SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE; | |
557 | value |= SOR_PLL_2_LVDS_ENABLE; | |
558 | tegra_sor_writel(sor, value, SOR_PLL_2); | |
559 | ||
560 | value = SOR_PLL_1_TERM_COMPOUT | SOR_PLL_1_TMDS_TERM; | |
561 | tegra_sor_writel(sor, value, SOR_PLL_1); | |
562 | ||
563 | while (true) { | |
564 | value = tegra_sor_readl(sor, SOR_PLL_2); | |
565 | if ((value & SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE) == 0) | |
566 | break; | |
567 | ||
568 | usleep_range(250, 1000); | |
569 | } | |
570 | ||
571 | value = tegra_sor_readl(sor, SOR_PLL_2); | |
572 | value &= ~SOR_PLL_2_POWERDOWN_OVERRIDE; | |
573 | value &= ~SOR_PLL_2_PORT_POWERDOWN; | |
574 | tegra_sor_writel(sor, value, SOR_PLL_2); | |
575 | ||
576 | /* | |
577 | * power up | |
578 | */ | |
579 | ||
580 | /* set safe link bandwidth (1.62 Gbps) */ | |
581 | value = tegra_sor_readl(sor, SOR_CLK_CNTRL); | |
582 | value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; | |
583 | value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G1_62; | |
584 | tegra_sor_writel(sor, value, SOR_CLK_CNTRL); | |
585 | ||
586 | /* step 1 */ | |
587 | value = tegra_sor_readl(sor, SOR_PLL_2); | |
588 | value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE | SOR_PLL_2_PORT_POWERDOWN | | |
589 | SOR_PLL_2_BANDGAP_POWERDOWN; | |
590 | tegra_sor_writel(sor, value, SOR_PLL_2); | |
591 | ||
592 | value = tegra_sor_readl(sor, SOR_PLL_0); | |
593 | value |= SOR_PLL_0_VCOPD | SOR_PLL_0_POWER_OFF; | |
594 | tegra_sor_writel(sor, value, SOR_PLL_0); | |
595 | ||
596 | value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); | |
597 | value &= ~SOR_DP_PADCTL_PAD_CAL_PD; | |
598 | tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); | |
599 | ||
600 | /* step 2 */ | |
601 | err = tegra_io_rail_power_on(TEGRA_IO_RAIL_LVDS); | |
602 | if (err < 0) { | |
603 | dev_err(sor->dev, "failed to power on I/O rail: %d\n", err); | |
86f5c52d | 604 | goto unlock; |
6b6b6042 TR |
605 | } |
606 | ||
607 | usleep_range(5, 100); | |
608 | ||
609 | /* step 3 */ | |
610 | value = tegra_sor_readl(sor, SOR_PLL_2); | |
611 | value &= ~SOR_PLL_2_BANDGAP_POWERDOWN; | |
612 | tegra_sor_writel(sor, value, SOR_PLL_2); | |
613 | ||
614 | usleep_range(20, 100); | |
615 | ||
616 | /* step 4 */ | |
617 | value = tegra_sor_readl(sor, SOR_PLL_0); | |
618 | value &= ~SOR_PLL_0_POWER_OFF; | |
619 | value &= ~SOR_PLL_0_VCOPD; | |
620 | tegra_sor_writel(sor, value, SOR_PLL_0); | |
621 | ||
622 | value = tegra_sor_readl(sor, SOR_PLL_2); | |
623 | value &= ~SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE; | |
624 | tegra_sor_writel(sor, value, SOR_PLL_2); | |
625 | ||
626 | usleep_range(200, 1000); | |
627 | ||
628 | /* step 5 */ | |
629 | value = tegra_sor_readl(sor, SOR_PLL_2); | |
630 | value &= ~SOR_PLL_2_PORT_POWERDOWN; | |
631 | tegra_sor_writel(sor, value, SOR_PLL_2); | |
632 | ||
633 | /* switch to DP clock */ | |
634 | err = clk_set_parent(sor->clk, sor->clk_dp); | |
635 | if (err < 0) | |
636 | dev_err(sor->dev, "failed to set DP parent clock: %d\n", err); | |
637 | ||
899451b7 | 638 | /* power DP lanes */ |
6b6b6042 | 639 | value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); |
899451b7 TR |
640 | |
641 | if (link.num_lanes <= 2) | |
642 | value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_2); | |
643 | else | |
644 | value |= SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_2; | |
645 | ||
646 | if (link.num_lanes <= 1) | |
647 | value &= ~SOR_DP_PADCTL_PD_TXD_1; | |
648 | else | |
649 | value |= SOR_DP_PADCTL_PD_TXD_1; | |
650 | ||
651 | if (link.num_lanes == 0) | |
652 | value &= ~SOR_DP_PADCTL_PD_TXD_0; | |
653 | else | |
654 | value |= SOR_DP_PADCTL_PD_TXD_0; | |
655 | ||
6b6b6042 TR |
656 | tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); |
657 | ||
658 | value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0); | |
659 | value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; | |
0c90a184 | 660 | value |= SOR_DP_LINKCTL_LANE_COUNT(link.num_lanes); |
6b6b6042 TR |
661 | tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0); |
662 | ||
663 | /* start lane sequencer */ | |
664 | value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_DOWN | | |
665 | SOR_LANE_SEQ_CTL_POWER_STATE_UP; | |
666 | tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL); | |
667 | ||
668 | while (true) { | |
669 | value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL); | |
670 | if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0) | |
671 | break; | |
672 | ||
673 | usleep_range(250, 1000); | |
674 | } | |
675 | ||
a4263fed | 676 | /* set link bandwidth */ |
6b6b6042 TR |
677 | value = tegra_sor_readl(sor, SOR_CLK_CNTRL); |
678 | value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; | |
a4263fed | 679 | value |= drm_dp_link_rate_to_bw_code(link.rate) << 2; |
6b6b6042 TR |
680 | tegra_sor_writel(sor, value, SOR_CLK_CNTRL); |
681 | ||
682 | /* set linkctl */ | |
683 | value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0); | |
684 | value |= SOR_DP_LINKCTL_ENABLE; | |
685 | ||
686 | value &= ~SOR_DP_LINKCTL_TU_SIZE_MASK; | |
34fa183b | 687 | value |= SOR_DP_LINKCTL_TU_SIZE(config.tu_size); |
6b6b6042 TR |
688 | |
689 | value |= SOR_DP_LINKCTL_ENHANCED_FRAME; | |
690 | tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0); | |
691 | ||
692 | for (i = 0, value = 0; i < 4; i++) { | |
693 | unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | | |
694 | SOR_DP_TPG_SCRAMBLER_GALIOS | | |
695 | SOR_DP_TPG_PATTERN_NONE; | |
696 | value = (value << 8) | lane; | |
697 | } | |
698 | ||
699 | tegra_sor_writel(sor, value, SOR_DP_TPG); | |
700 | ||
701 | value = tegra_sor_readl(sor, SOR_DP_CONFIG_0); | |
702 | value &= ~SOR_DP_CONFIG_WATERMARK_MASK; | |
34fa183b | 703 | value |= SOR_DP_CONFIG_WATERMARK(config.watermark); |
6b6b6042 TR |
704 | |
705 | value &= ~SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK; | |
34fa183b | 706 | value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(config.active_count); |
6b6b6042 TR |
707 | |
708 | value &= ~SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK; | |
34fa183b | 709 | value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(config.active_frac); |
6b6b6042 | 710 | |
34fa183b TR |
711 | if (config.active_polarity) |
712 | value |= SOR_DP_CONFIG_ACTIVE_SYM_POLARITY; | |
713 | else | |
714 | value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY; | |
6b6b6042 TR |
715 | |
716 | value |= SOR_DP_CONFIG_ACTIVE_SYM_ENABLE; | |
1f64ae7c | 717 | value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE; |
6b6b6042 TR |
718 | tegra_sor_writel(sor, value, SOR_DP_CONFIG_0); |
719 | ||
720 | value = tegra_sor_readl(sor, SOR_DP_AUDIO_HBLANK_SYMBOLS); | |
721 | value &= ~SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK; | |
7890b576 | 722 | value |= config.hblank_symbols & 0xffff; |
6b6b6042 TR |
723 | tegra_sor_writel(sor, value, SOR_DP_AUDIO_HBLANK_SYMBOLS); |
724 | ||
725 | value = tegra_sor_readl(sor, SOR_DP_AUDIO_VBLANK_SYMBOLS); | |
726 | value &= ~SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK; | |
7890b576 | 727 | value |= config.vblank_symbols & 0xffff; |
6b6b6042 TR |
728 | tegra_sor_writel(sor, value, SOR_DP_AUDIO_VBLANK_SYMBOLS); |
729 | ||
730 | /* enable pad calibration logic */ | |
731 | value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); | |
732 | value |= SOR_DP_PADCTL_PAD_CAL_PD; | |
733 | tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); | |
734 | ||
735 | if (sor->dpaux) { | |
6b6b6042 TR |
736 | u8 rate, lanes; |
737 | ||
738 | err = drm_dp_link_probe(aux, &link); | |
739 | if (err < 0) { | |
740 | dev_err(sor->dev, "failed to probe eDP link: %d\n", | |
741 | err); | |
86f5c52d | 742 | goto unlock; |
6b6b6042 TR |
743 | } |
744 | ||
745 | err = drm_dp_link_power_up(aux, &link); | |
746 | if (err < 0) { | |
747 | dev_err(sor->dev, "failed to power up eDP link: %d\n", | |
748 | err); | |
86f5c52d | 749 | goto unlock; |
6b6b6042 TR |
750 | } |
751 | ||
752 | err = drm_dp_link_configure(aux, &link); | |
753 | if (err < 0) { | |
754 | dev_err(sor->dev, "failed to configure eDP link: %d\n", | |
755 | err); | |
86f5c52d | 756 | goto unlock; |
6b6b6042 TR |
757 | } |
758 | ||
759 | rate = drm_dp_link_rate_to_bw_code(link.rate); | |
760 | lanes = link.num_lanes; | |
761 | ||
762 | value = tegra_sor_readl(sor, SOR_CLK_CNTRL); | |
763 | value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; | |
764 | value |= SOR_CLK_CNTRL_DP_LINK_SPEED(rate); | |
765 | tegra_sor_writel(sor, value, SOR_CLK_CNTRL); | |
766 | ||
767 | value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0); | |
768 | value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; | |
769 | value |= SOR_DP_LINKCTL_LANE_COUNT(lanes); | |
770 | ||
771 | if (link.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) | |
772 | value |= SOR_DP_LINKCTL_ENHANCED_FRAME; | |
773 | ||
774 | tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0); | |
775 | ||
776 | /* disable training pattern generator */ | |
777 | ||
778 | for (i = 0; i < link.num_lanes; i++) { | |
779 | unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | | |
780 | SOR_DP_TPG_SCRAMBLER_GALIOS | | |
781 | SOR_DP_TPG_PATTERN_NONE; | |
782 | value = (value << 8) | lane; | |
783 | } | |
784 | ||
785 | tegra_sor_writel(sor, value, SOR_DP_TPG); | |
786 | ||
787 | err = tegra_sor_dp_train_fast(sor, &link); | |
788 | if (err < 0) { | |
789 | dev_err(sor->dev, "DP fast link training failed: %d\n", | |
790 | err); | |
86f5c52d | 791 | goto unlock; |
6b6b6042 TR |
792 | } |
793 | ||
794 | dev_dbg(sor->dev, "fast link training succeeded\n"); | |
795 | } | |
796 | ||
797 | err = tegra_sor_power_up(sor, 250); | |
798 | if (err < 0) { | |
799 | dev_err(sor->dev, "failed to power up SOR: %d\n", err); | |
86f5c52d | 800 | goto unlock; |
6b6b6042 TR |
801 | } |
802 | ||
803 | /* start display controller in continuous mode */ | |
804 | value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS); | |
805 | value |= WRITE_MUX; | |
806 | tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS); | |
807 | ||
808 | tegra_dc_writel(dc, VSYNC_H_POSITION(1), DC_DISP_DISP_TIMING_OPTIONS); | |
809 | tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND); | |
810 | ||
811 | value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS); | |
812 | value &= ~WRITE_MUX; | |
813 | tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS); | |
814 | ||
815 | /* | |
816 | * configure panel (24bpp, vsync-, hsync-, DP-A protocol, complete | |
817 | * raster, associate with display controller) | |
818 | */ | |
3f4f3b5f | 819 | value = SOR_STATE_ASY_PROTOCOL_DP_A | |
6b6b6042 TR |
820 | SOR_STATE_ASY_CRC_MODE_COMPLETE | |
821 | SOR_STATE_ASY_OWNER(dc->pipe + 1); | |
34fa183b | 822 | |
3f4f3b5f TR |
823 | if (mode->flags & DRM_MODE_FLAG_PHSYNC) |
824 | value &= ~SOR_STATE_ASY_HSYNCPOL; | |
825 | ||
826 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) | |
827 | value |= SOR_STATE_ASY_HSYNCPOL; | |
828 | ||
829 | if (mode->flags & DRM_MODE_FLAG_PVSYNC) | |
830 | value &= ~SOR_STATE_ASY_VSYNCPOL; | |
831 | ||
832 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) | |
833 | value |= SOR_STATE_ASY_VSYNCPOL; | |
834 | ||
34fa183b TR |
835 | switch (config.bits_per_pixel) { |
836 | case 24: | |
837 | value |= SOR_STATE_ASY_PIXELDEPTH_BPP_24_444; | |
838 | break; | |
839 | ||
840 | case 18: | |
841 | value |= SOR_STATE_ASY_PIXELDEPTH_BPP_18_444; | |
842 | break; | |
843 | ||
844 | default: | |
845 | BUG(); | |
846 | break; | |
847 | } | |
848 | ||
6b6b6042 TR |
849 | tegra_sor_writel(sor, value, SOR_STATE_1); |
850 | ||
851 | /* | |
852 | * TODO: The video timing programming below doesn't seem to match the | |
853 | * register definitions. | |
854 | */ | |
855 | ||
856 | value = ((mode->vtotal & 0x7fff) << 16) | (mode->htotal & 0x7fff); | |
857 | tegra_sor_writel(sor, value, SOR_HEAD_STATE_1(0)); | |
858 | ||
859 | vse = mode->vsync_end - mode->vsync_start - 1; | |
860 | hse = mode->hsync_end - mode->hsync_start - 1; | |
861 | ||
862 | value = ((vse & 0x7fff) << 16) | (hse & 0x7fff); | |
863 | tegra_sor_writel(sor, value, SOR_HEAD_STATE_2(0)); | |
864 | ||
865 | vbe = vse + (mode->vsync_start - mode->vdisplay); | |
866 | hbe = hse + (mode->hsync_start - mode->hdisplay); | |
867 | ||
868 | value = ((vbe & 0x7fff) << 16) | (hbe & 0x7fff); | |
869 | tegra_sor_writel(sor, value, SOR_HEAD_STATE_3(0)); | |
870 | ||
871 | vbs = vbe + mode->vdisplay; | |
872 | hbs = hbe + mode->hdisplay; | |
873 | ||
874 | value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff); | |
875 | tegra_sor_writel(sor, value, SOR_HEAD_STATE_4(0)); | |
876 | ||
6b6b6042 | 877 | /* CSTM (LVDS, link A/B, upper) */ |
143b1df2 | 878 | value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B | |
6b6b6042 TR |
879 | SOR_CSTM_UPPER; |
880 | tegra_sor_writel(sor, value, SOR_CSTM); | |
881 | ||
882 | /* PWM setup */ | |
883 | err = tegra_sor_setup_pwm(sor, 250); | |
884 | if (err < 0) { | |
885 | dev_err(sor->dev, "failed to setup PWM: %d\n", err); | |
86f5c52d | 886 | goto unlock; |
6b6b6042 TR |
887 | } |
888 | ||
889 | value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); | |
890 | value |= SOR_ENABLE; | |
891 | tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); | |
892 | ||
893 | tegra_sor_update(sor); | |
894 | ||
895 | err = tegra_sor_attach(sor); | |
896 | if (err < 0) { | |
897 | dev_err(sor->dev, "failed to attach SOR: %d\n", err); | |
86f5c52d | 898 | goto unlock; |
6b6b6042 TR |
899 | } |
900 | ||
901 | err = tegra_sor_wakeup(sor); | |
902 | if (err < 0) { | |
903 | dev_err(sor->dev, "failed to enable DC: %d\n", err); | |
86f5c52d | 904 | goto unlock; |
6b6b6042 TR |
905 | } |
906 | ||
907 | sor->enabled = true; | |
908 | ||
86f5c52d TR |
909 | unlock: |
910 | mutex_unlock(&sor->lock); | |
911 | return err; | |
6b6b6042 TR |
912 | } |
913 | ||
914 | static int tegra_sor_detach(struct tegra_sor *sor) | |
915 | { | |
916 | unsigned long value, timeout; | |
917 | ||
918 | /* switch to safe mode */ | |
919 | value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); | |
920 | value &= ~SOR_SUPER_STATE_MODE_NORMAL; | |
921 | tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); | |
922 | tegra_sor_super_update(sor); | |
923 | ||
924 | timeout = jiffies + msecs_to_jiffies(250); | |
925 | ||
926 | while (time_before(jiffies, timeout)) { | |
927 | value = tegra_sor_readl(sor, SOR_PWR); | |
928 | if (value & SOR_PWR_MODE_SAFE) | |
929 | break; | |
930 | } | |
931 | ||
932 | if ((value & SOR_PWR_MODE_SAFE) == 0) | |
933 | return -ETIMEDOUT; | |
934 | ||
935 | /* go to sleep */ | |
936 | value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); | |
937 | value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK; | |
938 | tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); | |
939 | tegra_sor_super_update(sor); | |
940 | ||
941 | /* detach */ | |
942 | value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); | |
943 | value &= ~SOR_SUPER_STATE_ATTACHED; | |
944 | tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); | |
945 | tegra_sor_super_update(sor); | |
946 | ||
947 | timeout = jiffies + msecs_to_jiffies(250); | |
948 | ||
949 | while (time_before(jiffies, timeout)) { | |
950 | value = tegra_sor_readl(sor, SOR_TEST); | |
951 | if ((value & SOR_TEST_ATTACHED) == 0) | |
952 | break; | |
953 | ||
954 | usleep_range(25, 100); | |
955 | } | |
956 | ||
957 | if ((value & SOR_TEST_ATTACHED) != 0) | |
958 | return -ETIMEDOUT; | |
959 | ||
960 | return 0; | |
961 | } | |
962 | ||
963 | static int tegra_sor_power_down(struct tegra_sor *sor) | |
964 | { | |
965 | unsigned long value, timeout; | |
966 | int err; | |
967 | ||
968 | value = tegra_sor_readl(sor, SOR_PWR); | |
969 | value &= ~SOR_PWR_NORMAL_STATE_PU; | |
970 | value |= SOR_PWR_TRIGGER; | |
971 | tegra_sor_writel(sor, value, SOR_PWR); | |
972 | ||
973 | timeout = jiffies + msecs_to_jiffies(250); | |
974 | ||
975 | while (time_before(jiffies, timeout)) { | |
976 | value = tegra_sor_readl(sor, SOR_PWR); | |
977 | if ((value & SOR_PWR_TRIGGER) == 0) | |
978 | return 0; | |
979 | ||
980 | usleep_range(25, 100); | |
981 | } | |
982 | ||
983 | if ((value & SOR_PWR_TRIGGER) != 0) | |
984 | return -ETIMEDOUT; | |
985 | ||
986 | err = clk_set_parent(sor->clk, sor->clk_safe); | |
987 | if (err < 0) | |
988 | dev_err(sor->dev, "failed to set safe parent clock: %d\n", err); | |
989 | ||
990 | value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); | |
991 | value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 | | |
992 | SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2); | |
993 | tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); | |
994 | ||
995 | /* stop lane sequencer */ | |
ca185c68 | 996 | value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP | |
6b6b6042 TR |
997 | SOR_LANE_SEQ_CTL_POWER_STATE_DOWN; |
998 | tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL); | |
999 | ||
1000 | timeout = jiffies + msecs_to_jiffies(250); | |
1001 | ||
1002 | while (time_before(jiffies, timeout)) { | |
1003 | value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL); | |
1004 | if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0) | |
1005 | break; | |
1006 | ||
1007 | usleep_range(25, 100); | |
1008 | } | |
1009 | ||
1010 | if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0) | |
1011 | return -ETIMEDOUT; | |
1012 | ||
1013 | value = tegra_sor_readl(sor, SOR_PLL_2); | |
1014 | value |= SOR_PLL_2_PORT_POWERDOWN; | |
1015 | tegra_sor_writel(sor, value, SOR_PLL_2); | |
1016 | ||
1017 | usleep_range(20, 100); | |
1018 | ||
1019 | value = tegra_sor_readl(sor, SOR_PLL_0); | |
1020 | value |= SOR_PLL_0_POWER_OFF; | |
1021 | value |= SOR_PLL_0_VCOPD; | |
1022 | tegra_sor_writel(sor, value, SOR_PLL_0); | |
1023 | ||
1024 | value = tegra_sor_readl(sor, SOR_PLL_2); | |
1025 | value |= SOR_PLL_2_SEQ_PLLCAPPD; | |
1026 | value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE; | |
1027 | tegra_sor_writel(sor, value, SOR_PLL_2); | |
1028 | ||
1029 | usleep_range(20, 100); | |
1030 | ||
1031 | return 0; | |
1032 | } | |
1033 | ||
1034 | static int tegra_output_sor_disable(struct tegra_output *output) | |
1035 | { | |
1036 | struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); | |
1037 | struct tegra_sor *sor = to_sor(output); | |
1038 | unsigned long value; | |
86f5c52d TR |
1039 | int err = 0; |
1040 | ||
1041 | mutex_lock(&sor->lock); | |
6b6b6042 TR |
1042 | |
1043 | if (!sor->enabled) | |
86f5c52d | 1044 | goto unlock; |
6b6b6042 TR |
1045 | |
1046 | err = tegra_sor_detach(sor); | |
1047 | if (err < 0) { | |
1048 | dev_err(sor->dev, "failed to detach SOR: %d\n", err); | |
86f5c52d | 1049 | goto unlock; |
6b6b6042 TR |
1050 | } |
1051 | ||
1052 | tegra_sor_writel(sor, 0, SOR_STATE_1); | |
1053 | tegra_sor_update(sor); | |
1054 | ||
1055 | /* | |
1056 | * The following accesses registers of the display controller, so make | |
1057 | * sure it's only executed when the output is attached to one. | |
1058 | */ | |
1059 | if (dc) { | |
1060 | /* | |
1061 | * XXX: We can't do this here because it causes the SOR to go | |
1062 | * into an erroneous state and the output will look scrambled | |
1063 | * the next time it is enabled. Presumably this is because we | |
1064 | * should be doing this only on the next VBLANK. A possible | |
1065 | * solution would be to queue a "power-off" event to trigger | |
1066 | * this code to be run during the next VBLANK. | |
1067 | */ | |
1068 | /* | |
1069 | value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); | |
1070 | value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | | |
1071 | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); | |
1072 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); | |
1073 | */ | |
1074 | ||
1075 | value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); | |
1076 | value &= ~DISP_CTRL_MODE_MASK; | |
1077 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); | |
1078 | ||
1079 | value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); | |
1080 | value &= ~SOR_ENABLE; | |
1081 | tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); | |
1082 | ||
1083 | tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); | |
1084 | tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); | |
1085 | } | |
1086 | ||
1087 | err = tegra_sor_power_down(sor); | |
1088 | if (err < 0) { | |
1089 | dev_err(sor->dev, "failed to power down SOR: %d\n", err); | |
86f5c52d | 1090 | goto unlock; |
6b6b6042 TR |
1091 | } |
1092 | ||
1093 | if (sor->dpaux) { | |
1094 | err = tegra_dpaux_disable(sor->dpaux); | |
1095 | if (err < 0) { | |
1096 | dev_err(sor->dev, "failed to disable DP: %d\n", err); | |
86f5c52d | 1097 | goto unlock; |
6b6b6042 TR |
1098 | } |
1099 | } | |
1100 | ||
1101 | err = tegra_io_rail_power_off(TEGRA_IO_RAIL_LVDS); | |
1102 | if (err < 0) { | |
1103 | dev_err(sor->dev, "failed to power off I/O rail: %d\n", err); | |
86f5c52d | 1104 | goto unlock; |
6b6b6042 TR |
1105 | } |
1106 | ||
1107 | reset_control_assert(sor->rst); | |
1108 | clk_disable_unprepare(sor->clk); | |
1109 | ||
1110 | sor->enabled = false; | |
1111 | ||
86f5c52d TR |
1112 | unlock: |
1113 | mutex_unlock(&sor->lock); | |
1114 | return err; | |
6b6b6042 TR |
1115 | } |
1116 | ||
1117 | static int tegra_output_sor_setup_clock(struct tegra_output *output, | |
91eded9b TR |
1118 | struct clk *clk, unsigned long pclk, |
1119 | unsigned int *div) | |
6b6b6042 TR |
1120 | { |
1121 | struct tegra_sor *sor = to_sor(output); | |
1122 | int err; | |
1123 | ||
6b6b6042 TR |
1124 | err = clk_set_parent(clk, sor->clk_parent); |
1125 | if (err < 0) { | |
1126 | dev_err(sor->dev, "failed to set parent clock: %d\n", err); | |
1127 | return err; | |
1128 | } | |
1129 | ||
1130 | err = clk_set_rate(sor->clk_parent, pclk); | |
1131 | if (err < 0) { | |
91eded9b | 1132 | dev_err(sor->dev, "failed to set clock rate to %lu Hz\n", pclk); |
6b6b6042 TR |
1133 | return err; |
1134 | } | |
1135 | ||
91eded9b TR |
1136 | *div = 0; |
1137 | ||
6b6b6042 TR |
1138 | return 0; |
1139 | } | |
1140 | ||
1141 | static int tegra_output_sor_check_mode(struct tegra_output *output, | |
1142 | struct drm_display_mode *mode, | |
1143 | enum drm_mode_status *status) | |
1144 | { | |
1145 | /* | |
1146 | * FIXME: For now, always assume that the mode is okay. | |
1147 | */ | |
1148 | ||
1149 | *status = MODE_OK; | |
1150 | ||
1151 | return 0; | |
1152 | } | |
1153 | ||
1154 | static enum drm_connector_status | |
1155 | tegra_output_sor_detect(struct tegra_output *output) | |
1156 | { | |
1157 | struct tegra_sor *sor = to_sor(output); | |
1158 | ||
1159 | if (sor->dpaux) | |
1160 | return tegra_dpaux_detect(sor->dpaux); | |
1161 | ||
1162 | return connector_status_unknown; | |
1163 | } | |
1164 | ||
1165 | static const struct tegra_output_ops sor_ops = { | |
1166 | .enable = tegra_output_sor_enable, | |
1167 | .disable = tegra_output_sor_disable, | |
1168 | .setup_clock = tegra_output_sor_setup_clock, | |
1169 | .check_mode = tegra_output_sor_check_mode, | |
1170 | .detect = tegra_output_sor_detect, | |
1171 | }; | |
1172 | ||
a82752e1 TR |
1173 | static int tegra_sor_crc_open(struct inode *inode, struct file *file) |
1174 | { | |
1175 | file->private_data = inode->i_private; | |
1176 | ||
1177 | return 0; | |
1178 | } | |
1179 | ||
1180 | static int tegra_sor_crc_release(struct inode *inode, struct file *file) | |
1181 | { | |
1182 | return 0; | |
1183 | } | |
1184 | ||
1185 | static int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout) | |
1186 | { | |
1187 | u32 value; | |
1188 | ||
1189 | timeout = jiffies + msecs_to_jiffies(timeout); | |
1190 | ||
1191 | while (time_before(jiffies, timeout)) { | |
1192 | value = tegra_sor_readl(sor, SOR_CRC_A); | |
1193 | if (value & SOR_CRC_A_VALID) | |
1194 | return 0; | |
1195 | ||
1196 | usleep_range(100, 200); | |
1197 | } | |
1198 | ||
1199 | return -ETIMEDOUT; | |
1200 | } | |
1201 | ||
1202 | static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer, | |
1203 | size_t size, loff_t *ppos) | |
1204 | { | |
1205 | struct tegra_sor *sor = file->private_data; | |
86f5c52d | 1206 | ssize_t num, err; |
a82752e1 | 1207 | char buf[10]; |
a82752e1 | 1208 | u32 value; |
86f5c52d TR |
1209 | |
1210 | mutex_lock(&sor->lock); | |
1211 | ||
1212 | if (!sor->enabled) { | |
1213 | err = -EAGAIN; | |
1214 | goto unlock; | |
1215 | } | |
a82752e1 TR |
1216 | |
1217 | value = tegra_sor_readl(sor, SOR_STATE_1); | |
1218 | value &= ~SOR_STATE_ASY_CRC_MODE_MASK; | |
1219 | tegra_sor_writel(sor, value, SOR_STATE_1); | |
1220 | ||
1221 | value = tegra_sor_readl(sor, SOR_CRC_CNTRL); | |
1222 | value |= SOR_CRC_CNTRL_ENABLE; | |
1223 | tegra_sor_writel(sor, value, SOR_CRC_CNTRL); | |
1224 | ||
1225 | value = tegra_sor_readl(sor, SOR_TEST); | |
1226 | value &= ~SOR_TEST_CRC_POST_SERIALIZE; | |
1227 | tegra_sor_writel(sor, value, SOR_TEST); | |
1228 | ||
1229 | err = tegra_sor_crc_wait(sor, 100); | |
1230 | if (err < 0) | |
86f5c52d | 1231 | goto unlock; |
a82752e1 TR |
1232 | |
1233 | tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A); | |
1234 | value = tegra_sor_readl(sor, SOR_CRC_B); | |
1235 | ||
1236 | num = scnprintf(buf, sizeof(buf), "%08x\n", value); | |
1237 | ||
86f5c52d TR |
1238 | err = simple_read_from_buffer(buffer, size, ppos, buf, num); |
1239 | ||
1240 | unlock: | |
1241 | mutex_unlock(&sor->lock); | |
1242 | return err; | |
a82752e1 TR |
1243 | } |
1244 | ||
1245 | static const struct file_operations tegra_sor_crc_fops = { | |
1246 | .owner = THIS_MODULE, | |
1247 | .open = tegra_sor_crc_open, | |
1248 | .read = tegra_sor_crc_read, | |
1249 | .release = tegra_sor_crc_release, | |
1250 | }; | |
1251 | ||
1b0c7b48 TR |
1252 | static int tegra_sor_debugfs_init(struct tegra_sor *sor, |
1253 | struct drm_minor *minor) | |
a82752e1 TR |
1254 | { |
1255 | struct dentry *entry; | |
1256 | int err = 0; | |
1257 | ||
1b0c7b48 | 1258 | sor->debugfs = debugfs_create_dir("sor", minor->debugfs_root); |
a82752e1 TR |
1259 | if (!sor->debugfs) |
1260 | return -ENOMEM; | |
1261 | ||
1262 | entry = debugfs_create_file("crc", 0644, sor->debugfs, sor, | |
1263 | &tegra_sor_crc_fops); | |
1264 | if (!entry) { | |
1265 | dev_err(sor->dev, | |
1266 | "cannot create /sys/kernel/debug/dri/%s/sor/crc\n", | |
1b0c7b48 | 1267 | minor->debugfs_root->d_name.name); |
a82752e1 TR |
1268 | err = -ENOMEM; |
1269 | goto remove; | |
1270 | } | |
1271 | ||
1272 | return err; | |
1273 | ||
1274 | remove: | |
1275 | debugfs_remove(sor->debugfs); | |
1276 | sor->debugfs = NULL; | |
1277 | return err; | |
1278 | } | |
1279 | ||
1280 | static int tegra_sor_debugfs_exit(struct tegra_sor *sor) | |
1281 | { | |
9578184e | 1282 | debugfs_remove_recursive(sor->debugfs); |
a82752e1 TR |
1283 | sor->debugfs = NULL; |
1284 | ||
1285 | return 0; | |
1286 | } | |
1287 | ||
6b6b6042 TR |
1288 | static int tegra_sor_init(struct host1x_client *client) |
1289 | { | |
9910f5c4 | 1290 | struct drm_device *drm = dev_get_drvdata(client->parent); |
6b6b6042 TR |
1291 | struct tegra_sor *sor = host1x_client_to_sor(client); |
1292 | int err; | |
1293 | ||
1294 | if (!sor->dpaux) | |
1295 | return -ENODEV; | |
1296 | ||
1297 | sor->output.type = TEGRA_OUTPUT_EDP; | |
1298 | ||
1299 | sor->output.dev = sor->dev; | |
1300 | sor->output.ops = &sor_ops; | |
1301 | ||
9910f5c4 | 1302 | err = tegra_output_init(drm, &sor->output); |
6b6b6042 TR |
1303 | if (err < 0) { |
1304 | dev_err(sor->dev, "output setup failed: %d\n", err); | |
1305 | return err; | |
1306 | } | |
1307 | ||
a82752e1 | 1308 | if (IS_ENABLED(CONFIG_DEBUG_FS)) { |
1b0c7b48 | 1309 | err = tegra_sor_debugfs_init(sor, drm->primary); |
a82752e1 TR |
1310 | if (err < 0) |
1311 | dev_err(sor->dev, "debugfs setup failed: %d\n", err); | |
1312 | } | |
1313 | ||
6b6b6042 TR |
1314 | if (sor->dpaux) { |
1315 | err = tegra_dpaux_attach(sor->dpaux, &sor->output); | |
1316 | if (err < 0) { | |
1317 | dev_err(sor->dev, "failed to attach DP: %d\n", err); | |
1318 | return err; | |
1319 | } | |
1320 | } | |
1321 | ||
1322 | return 0; | |
1323 | } | |
1324 | ||
1325 | static int tegra_sor_exit(struct host1x_client *client) | |
1326 | { | |
1327 | struct tegra_sor *sor = host1x_client_to_sor(client); | |
1328 | int err; | |
1329 | ||
1330 | err = tegra_output_disable(&sor->output); | |
1331 | if (err < 0) { | |
1332 | dev_err(sor->dev, "output failed to disable: %d\n", err); | |
1333 | return err; | |
1334 | } | |
1335 | ||
1336 | if (sor->dpaux) { | |
1337 | err = tegra_dpaux_detach(sor->dpaux); | |
1338 | if (err < 0) { | |
1339 | dev_err(sor->dev, "failed to detach DP: %d\n", err); | |
1340 | return err; | |
1341 | } | |
1342 | } | |
1343 | ||
a82752e1 TR |
1344 | if (IS_ENABLED(CONFIG_DEBUG_FS)) { |
1345 | err = tegra_sor_debugfs_exit(sor); | |
1346 | if (err < 0) | |
1347 | dev_err(sor->dev, "debugfs cleanup failed: %d\n", err); | |
1348 | } | |
1349 | ||
6b6b6042 TR |
1350 | err = tegra_output_exit(&sor->output); |
1351 | if (err < 0) { | |
1352 | dev_err(sor->dev, "output cleanup failed: %d\n", err); | |
1353 | return err; | |
1354 | } | |
1355 | ||
1356 | return 0; | |
1357 | } | |
1358 | ||
1359 | static const struct host1x_client_ops sor_client_ops = { | |
1360 | .init = tegra_sor_init, | |
1361 | .exit = tegra_sor_exit, | |
1362 | }; | |
1363 | ||
1364 | static int tegra_sor_probe(struct platform_device *pdev) | |
1365 | { | |
1366 | struct device_node *np; | |
1367 | struct tegra_sor *sor; | |
1368 | struct resource *regs; | |
1369 | int err; | |
1370 | ||
1371 | sor = devm_kzalloc(&pdev->dev, sizeof(*sor), GFP_KERNEL); | |
1372 | if (!sor) | |
1373 | return -ENOMEM; | |
1374 | ||
1375 | sor->output.dev = sor->dev = &pdev->dev; | |
1376 | ||
1377 | np = of_parse_phandle(pdev->dev.of_node, "nvidia,dpaux", 0); | |
1378 | if (np) { | |
1379 | sor->dpaux = tegra_dpaux_find_by_of_node(np); | |
1380 | of_node_put(np); | |
1381 | ||
1382 | if (!sor->dpaux) | |
1383 | return -EPROBE_DEFER; | |
1384 | } | |
1385 | ||
1386 | err = tegra_output_probe(&sor->output); | |
1387 | if (err < 0) | |
1388 | return err; | |
1389 | ||
1390 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
1391 | sor->regs = devm_ioremap_resource(&pdev->dev, regs); | |
1392 | if (IS_ERR(sor->regs)) | |
1393 | return PTR_ERR(sor->regs); | |
1394 | ||
1395 | sor->rst = devm_reset_control_get(&pdev->dev, "sor"); | |
1396 | if (IS_ERR(sor->rst)) | |
1397 | return PTR_ERR(sor->rst); | |
1398 | ||
1399 | sor->clk = devm_clk_get(&pdev->dev, NULL); | |
1400 | if (IS_ERR(sor->clk)) | |
1401 | return PTR_ERR(sor->clk); | |
1402 | ||
1403 | sor->clk_parent = devm_clk_get(&pdev->dev, "parent"); | |
1404 | if (IS_ERR(sor->clk_parent)) | |
1405 | return PTR_ERR(sor->clk_parent); | |
1406 | ||
1407 | err = clk_prepare_enable(sor->clk_parent); | |
1408 | if (err < 0) | |
1409 | return err; | |
1410 | ||
1411 | sor->clk_safe = devm_clk_get(&pdev->dev, "safe"); | |
1412 | if (IS_ERR(sor->clk_safe)) | |
1413 | return PTR_ERR(sor->clk_safe); | |
1414 | ||
1415 | err = clk_prepare_enable(sor->clk_safe); | |
1416 | if (err < 0) | |
1417 | return err; | |
1418 | ||
1419 | sor->clk_dp = devm_clk_get(&pdev->dev, "dp"); | |
1420 | if (IS_ERR(sor->clk_dp)) | |
1421 | return PTR_ERR(sor->clk_dp); | |
1422 | ||
1423 | err = clk_prepare_enable(sor->clk_dp); | |
1424 | if (err < 0) | |
1425 | return err; | |
1426 | ||
1427 | INIT_LIST_HEAD(&sor->client.list); | |
1428 | sor->client.ops = &sor_client_ops; | |
1429 | sor->client.dev = &pdev->dev; | |
1430 | ||
86f5c52d TR |
1431 | mutex_init(&sor->lock); |
1432 | ||
6b6b6042 TR |
1433 | err = host1x_client_register(&sor->client); |
1434 | if (err < 0) { | |
1435 | dev_err(&pdev->dev, "failed to register host1x client: %d\n", | |
1436 | err); | |
1437 | return err; | |
1438 | } | |
1439 | ||
1440 | platform_set_drvdata(pdev, sor); | |
1441 | ||
1442 | return 0; | |
1443 | } | |
1444 | ||
1445 | static int tegra_sor_remove(struct platform_device *pdev) | |
1446 | { | |
1447 | struct tegra_sor *sor = platform_get_drvdata(pdev); | |
1448 | int err; | |
1449 | ||
1450 | err = host1x_client_unregister(&sor->client); | |
1451 | if (err < 0) { | |
1452 | dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", | |
1453 | err); | |
1454 | return err; | |
1455 | } | |
1456 | ||
1457 | clk_disable_unprepare(sor->clk_parent); | |
1458 | clk_disable_unprepare(sor->clk_safe); | |
1459 | clk_disable_unprepare(sor->clk_dp); | |
1460 | clk_disable_unprepare(sor->clk); | |
1461 | ||
1462 | return 0; | |
1463 | } | |
1464 | ||
1465 | static const struct of_device_id tegra_sor_of_match[] = { | |
1466 | { .compatible = "nvidia,tegra124-sor", }, | |
1467 | { }, | |
1468 | }; | |
ef70728c | 1469 | MODULE_DEVICE_TABLE(of, tegra_sor_of_match); |
6b6b6042 TR |
1470 | |
1471 | struct platform_driver tegra_sor_driver = { | |
1472 | .driver = { | |
1473 | .name = "tegra-sor", | |
1474 | .of_match_table = tegra_sor_of_match, | |
1475 | }, | |
1476 | .probe = tegra_sor_probe, | |
1477 | .remove = tegra_sor_remove, | |
1478 | }; |