diff options
author | Thierry Reding <treding@nvidia.com> | 2013-11-15 10:06:05 -0500 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2014-04-04 03:12:50 -0400 |
commit | 6b6b604215c64666fbf0fed939a5c312cc7b12fe (patch) | |
tree | 965dec0ece2b01700f4f68b4e75542219ee37bec /drivers/gpu | |
parent | 64400c3791d9fcebf23318a289f9da964547a6f3 (diff) |
drm/tegra: Add eDP support
Add support for eDP functionality found on Tegra124 and later SoCs. Only
fast link training is currently supported.
Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/tegra/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/dc.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/dpaux.c | 544 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/dpaux.h | 73 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/drm.c | 19 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/drm.h | 20 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/output.c | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/sor.c | 1092 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/sor.h | 278 |
9 files changed, 2035 insertions, 2 deletions
diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile index 8d220afbd85f..d43f21bb4596 100644 --- a/drivers/gpu/drm/tegra/Makefile +++ b/drivers/gpu/drm/tegra/Makefile | |||
@@ -11,6 +11,8 @@ tegra-drm-y := \ | |||
11 | hdmi.o \ | 11 | hdmi.o \ |
12 | mipi-phy.o \ | 12 | mipi-phy.o \ |
13 | dsi.o \ | 13 | dsi.o \ |
14 | sor.o \ | ||
15 | dpaux.o \ | ||
14 | gr2d.o \ | 16 | gr2d.o \ |
15 | gr3d.o | 17 | gr3d.o |
16 | 18 | ||
diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h index 3c2c0ea1cd87..c94101494826 100644 --- a/drivers/gpu/drm/tegra/dc.h +++ b/drivers/gpu/drm/tegra/dc.h | |||
@@ -118,6 +118,7 @@ | |||
118 | #define DC_DISP_DISP_WIN_OPTIONS 0x402 | 118 | #define DC_DISP_DISP_WIN_OPTIONS 0x402 |
119 | #define HDMI_ENABLE (1 << 30) | 119 | #define HDMI_ENABLE (1 << 30) |
120 | #define DSI_ENABLE (1 << 29) | 120 | #define DSI_ENABLE (1 << 29) |
121 | #define SOR_ENABLE (1 << 25) | ||
121 | 122 | ||
122 | #define DC_DISP_DISP_MEM_HIGH_PRIORITY 0x403 | 123 | #define DC_DISP_DISP_MEM_HIGH_PRIORITY 0x403 |
123 | #define CURSOR_THRESHOLD(x) (((x) & 0x03) << 24) | 124 | #define CURSOR_THRESHOLD(x) (((x) & 0x03) << 24) |
diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c new file mode 100644 index 000000000000..d536ed381fbd --- /dev/null +++ b/drivers/gpu/drm/tegra/dpaux.c | |||
@@ -0,0 +1,544 @@ | |||
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> | ||
10 | #include <linux/delay.h> | ||
11 | #include <linux/gpio.h> | ||
12 | #include <linux/interrupt.h> | ||
13 | #include <linux/io.h> | ||
14 | #include <linux/of_gpio.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/reset.h> | ||
17 | #include <linux/regulator/consumer.h> | ||
18 | |||
19 | #include <drm/drm_dp_helper.h> | ||
20 | #include <drm/drm_panel.h> | ||
21 | |||
22 | #include "dpaux.h" | ||
23 | #include "drm.h" | ||
24 | |||
25 | static DEFINE_MUTEX(dpaux_lock); | ||
26 | static LIST_HEAD(dpaux_list); | ||
27 | |||
28 | struct tegra_dpaux { | ||
29 | struct drm_dp_aux aux; | ||
30 | struct device *dev; | ||
31 | |||
32 | void __iomem *regs; | ||
33 | int irq; | ||
34 | |||
35 | struct tegra_output *output; | ||
36 | |||
37 | struct reset_control *rst; | ||
38 | struct clk *clk_parent; | ||
39 | struct clk *clk; | ||
40 | |||
41 | struct regulator *vdd; | ||
42 | |||
43 | struct completion complete; | ||
44 | struct list_head list; | ||
45 | }; | ||
46 | |||
47 | static inline struct tegra_dpaux *to_dpaux(struct drm_dp_aux *aux) | ||
48 | { | ||
49 | return container_of(aux, struct tegra_dpaux, aux); | ||
50 | } | ||
51 | |||
52 | static inline unsigned long tegra_dpaux_readl(struct tegra_dpaux *dpaux, | ||
53 | unsigned long offset) | ||
54 | { | ||
55 | return readl(dpaux->regs + (offset << 2)); | ||
56 | } | ||
57 | |||
58 | static inline void tegra_dpaux_writel(struct tegra_dpaux *dpaux, | ||
59 | unsigned long value, | ||
60 | unsigned long offset) | ||
61 | { | ||
62 | writel(value, dpaux->regs + (offset << 2)); | ||
63 | } | ||
64 | |||
65 | static void tegra_dpaux_write_fifo(struct tegra_dpaux *dpaux, const u8 *buffer, | ||
66 | size_t size) | ||
67 | { | ||
68 | unsigned long offset = DPAUX_DP_AUXDATA_WRITE(0); | ||
69 | size_t i, j; | ||
70 | |||
71 | for (i = 0; i < size; i += 4) { | ||
72 | size_t num = min_t(size_t, size - i, 4); | ||
73 | unsigned long value = 0; | ||
74 | |||
75 | for (j = 0; j < num; j++) | ||
76 | value |= buffer[i + j] << (j * 8); | ||
77 | |||
78 | tegra_dpaux_writel(dpaux, value, offset++); | ||
79 | } | ||
80 | } | ||
81 | |||
82 | static void tegra_dpaux_read_fifo(struct tegra_dpaux *dpaux, u8 *buffer, | ||
83 | size_t size) | ||
84 | { | ||
85 | unsigned long offset = DPAUX_DP_AUXDATA_READ(0); | ||
86 | size_t i, j; | ||
87 | |||
88 | for (i = 0; i < size; i += 4) { | ||
89 | size_t num = min_t(size_t, size - i, 4); | ||
90 | unsigned long value; | ||
91 | |||
92 | value = tegra_dpaux_readl(dpaux, offset++); | ||
93 | |||
94 | for (j = 0; j < num; j++) | ||
95 | buffer[i + j] = value >> (j * 8); | ||
96 | } | ||
97 | } | ||
98 | |||
99 | static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux, | ||
100 | struct drm_dp_aux_msg *msg) | ||
101 | { | ||
102 | unsigned long value = DPAUX_DP_AUXCTL_TRANSACTREQ; | ||
103 | unsigned long timeout = msecs_to_jiffies(250); | ||
104 | struct tegra_dpaux *dpaux = to_dpaux(aux); | ||
105 | unsigned long status; | ||
106 | ssize_t ret = 0; | ||
107 | |||
108 | if (msg->size < 1 || msg->size > 16) | ||
109 | return -EINVAL; | ||
110 | |||
111 | tegra_dpaux_writel(dpaux, msg->address, DPAUX_DP_AUXADDR); | ||
112 | |||
113 | switch (msg->request & ~DP_AUX_I2C_MOT) { | ||
114 | case DP_AUX_I2C_WRITE: | ||
115 | if (msg->request & DP_AUX_I2C_MOT) | ||
116 | value = DPAUX_DP_AUXCTL_CMD_MOT_WR; | ||
117 | else | ||
118 | value = DPAUX_DP_AUXCTL_CMD_I2C_WR; | ||
119 | |||
120 | break; | ||
121 | |||
122 | case DP_AUX_I2C_READ: | ||
123 | if (msg->request & DP_AUX_I2C_MOT) | ||
124 | value = DPAUX_DP_AUXCTL_CMD_MOT_RD; | ||
125 | else | ||
126 | value = DPAUX_DP_AUXCTL_CMD_I2C_RD; | ||
127 | |||
128 | break; | ||
129 | |||
130 | case DP_AUX_I2C_STATUS: | ||
131 | if (msg->request & DP_AUX_I2C_MOT) | ||
132 | value = DPAUX_DP_AUXCTL_CMD_MOT_RQ; | ||
133 | else | ||
134 | value = DPAUX_DP_AUXCTL_CMD_I2C_RQ; | ||
135 | |||
136 | break; | ||
137 | |||
138 | case DP_AUX_NATIVE_WRITE: | ||
139 | value = DPAUX_DP_AUXCTL_CMD_AUX_WR; | ||
140 | break; | ||
141 | |||
142 | case DP_AUX_NATIVE_READ: | ||
143 | value = DPAUX_DP_AUXCTL_CMD_AUX_RD; | ||
144 | break; | ||
145 | |||
146 | default: | ||
147 | return -EINVAL; | ||
148 | } | ||
149 | |||
150 | value |= DPAUX_DP_AUXCTL_CMDLEN(msg->size - 1); | ||
151 | tegra_dpaux_writel(dpaux, value, DPAUX_DP_AUXCTL); | ||
152 | |||
153 | if ((msg->request & DP_AUX_I2C_READ) == 0) { | ||
154 | tegra_dpaux_write_fifo(dpaux, msg->buffer, msg->size); | ||
155 | ret = msg->size; | ||
156 | } | ||
157 | |||
158 | /* start transaction */ | ||
159 | value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXCTL); | ||
160 | value |= DPAUX_DP_AUXCTL_TRANSACTREQ; | ||
161 | tegra_dpaux_writel(dpaux, value, DPAUX_DP_AUXCTL); | ||
162 | |||
163 | status = wait_for_completion_timeout(&dpaux->complete, timeout); | ||
164 | if (!status) | ||
165 | return -ETIMEDOUT; | ||
166 | |||
167 | /* read status and clear errors */ | ||
168 | value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXSTAT); | ||
169 | tegra_dpaux_writel(dpaux, 0xf00, DPAUX_DP_AUXSTAT); | ||
170 | |||
171 | if (value & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR) | ||
172 | return -ETIMEDOUT; | ||
173 | |||
174 | if ((value & DPAUX_DP_AUXSTAT_RX_ERROR) || | ||
175 | (value & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR) || | ||
176 | (value & DPAUX_DP_AUXSTAT_NO_STOP_ERROR)) | ||
177 | return -EIO; | ||
178 | |||
179 | switch ((value & DPAUX_DP_AUXSTAT_REPLY_TYPE_MASK) >> 16) { | ||
180 | case 0x00: | ||
181 | msg->reply = DP_AUX_NATIVE_REPLY_ACK; | ||
182 | break; | ||
183 | |||
184 | case 0x01: | ||
185 | msg->reply = DP_AUX_NATIVE_REPLY_NACK; | ||
186 | break; | ||
187 | |||
188 | case 0x02: | ||
189 | msg->reply = DP_AUX_NATIVE_REPLY_DEFER; | ||
190 | break; | ||
191 | |||
192 | case 0x04: | ||
193 | msg->reply = DP_AUX_I2C_REPLY_NACK; | ||
194 | break; | ||
195 | |||
196 | case 0x08: | ||
197 | msg->reply = DP_AUX_I2C_REPLY_DEFER; | ||
198 | break; | ||
199 | } | ||
200 | |||
201 | if (msg->reply == DP_AUX_NATIVE_REPLY_ACK) { | ||
202 | if (msg->request & DP_AUX_I2C_READ) { | ||
203 | size_t count = value & DPAUX_DP_AUXSTAT_REPLY_MASK; | ||
204 | |||
205 | if (WARN_ON(count != msg->size)) | ||
206 | count = min_t(size_t, count, msg->size); | ||
207 | |||
208 | tegra_dpaux_read_fifo(dpaux, msg->buffer, count); | ||
209 | ret = count; | ||
210 | } | ||
211 | } | ||
212 | |||
213 | return ret; | ||
214 | } | ||
215 | |||
216 | static irqreturn_t tegra_dpaux_irq(int irq, void *data) | ||
217 | { | ||
218 | struct tegra_dpaux *dpaux = data; | ||
219 | irqreturn_t ret = IRQ_HANDLED; | ||
220 | unsigned long value; | ||
221 | |||
222 | /* clear interrupts */ | ||
223 | value = tegra_dpaux_readl(dpaux, DPAUX_INTR_AUX); | ||
224 | tegra_dpaux_writel(dpaux, value, DPAUX_INTR_AUX); | ||
225 | |||
226 | if (value & DPAUX_INTR_PLUG_EVENT) { | ||
227 | if (dpaux->output) { | ||
228 | drm_helper_hpd_irq_event(dpaux->output->connector.dev); | ||
229 | } | ||
230 | } | ||
231 | |||
232 | if (value & DPAUX_INTR_UNPLUG_EVENT) { | ||
233 | if (dpaux->output) | ||
234 | drm_helper_hpd_irq_event(dpaux->output->connector.dev); | ||
235 | } | ||
236 | |||
237 | if (value & DPAUX_INTR_IRQ_EVENT) { | ||
238 | /* TODO: handle this */ | ||
239 | } | ||
240 | |||
241 | if (value & DPAUX_INTR_AUX_DONE) | ||
242 | complete(&dpaux->complete); | ||
243 | |||
244 | return ret; | ||
245 | } | ||
246 | |||
247 | static int tegra_dpaux_probe(struct platform_device *pdev) | ||
248 | { | ||
249 | struct tegra_dpaux *dpaux; | ||
250 | struct resource *regs; | ||
251 | unsigned long value; | ||
252 | int err; | ||
253 | |||
254 | dpaux = devm_kzalloc(&pdev->dev, sizeof(*dpaux), GFP_KERNEL); | ||
255 | if (!dpaux) | ||
256 | return -ENOMEM; | ||
257 | |||
258 | init_completion(&dpaux->complete); | ||
259 | INIT_LIST_HEAD(&dpaux->list); | ||
260 | dpaux->dev = &pdev->dev; | ||
261 | |||
262 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
263 | dpaux->regs = devm_ioremap_resource(&pdev->dev, regs); | ||
264 | if (IS_ERR(dpaux->regs)) | ||
265 | return PTR_ERR(dpaux->regs); | ||
266 | |||
267 | dpaux->irq = platform_get_irq(pdev, 0); | ||
268 | if (dpaux->irq < 0) { | ||
269 | dev_err(&pdev->dev, "failed to get IRQ\n"); | ||
270 | return -ENXIO; | ||
271 | } | ||
272 | |||
273 | dpaux->rst = devm_reset_control_get(&pdev->dev, "dpaux"); | ||
274 | if (IS_ERR(dpaux->rst)) | ||
275 | return PTR_ERR(dpaux->rst); | ||
276 | |||
277 | dpaux->clk = devm_clk_get(&pdev->dev, NULL); | ||
278 | if (IS_ERR(dpaux->clk)) | ||
279 | return PTR_ERR(dpaux->clk); | ||
280 | |||
281 | err = clk_prepare_enable(dpaux->clk); | ||
282 | if (err < 0) | ||
283 | return err; | ||
284 | |||
285 | reset_control_deassert(dpaux->rst); | ||
286 | |||
287 | dpaux->clk_parent = devm_clk_get(&pdev->dev, "parent"); | ||
288 | if (IS_ERR(dpaux->clk_parent)) | ||
289 | return PTR_ERR(dpaux->clk_parent); | ||
290 | |||
291 | err = clk_prepare_enable(dpaux->clk_parent); | ||
292 | if (err < 0) | ||
293 | return err; | ||
294 | |||
295 | err = clk_set_rate(dpaux->clk_parent, 270000000); | ||
296 | if (err < 0) { | ||
297 | dev_err(&pdev->dev, "failed to set clock to 270 MHz: %d\n", | ||
298 | err); | ||
299 | return err; | ||
300 | } | ||
301 | |||
302 | dpaux->vdd = devm_regulator_get(&pdev->dev, "vdd"); | ||
303 | if (IS_ERR(dpaux->vdd)) | ||
304 | return PTR_ERR(dpaux->vdd); | ||
305 | |||
306 | err = devm_request_irq(dpaux->dev, dpaux->irq, tegra_dpaux_irq, 0, | ||
307 | dev_name(dpaux->dev), dpaux); | ||
308 | if (err < 0) { | ||
309 | dev_err(dpaux->dev, "failed to request IRQ#%u: %d\n", | ||
310 | dpaux->irq, err); | ||
311 | return err; | ||
312 | } | ||
313 | |||
314 | dpaux->aux.transfer = tegra_dpaux_transfer; | ||
315 | dpaux->aux.dev = &pdev->dev; | ||
316 | |||
317 | err = drm_dp_aux_register_i2c_bus(&dpaux->aux); | ||
318 | if (err < 0) | ||
319 | return err; | ||
320 | |||
321 | /* enable and clear all interrupts */ | ||
322 | value = DPAUX_INTR_AUX_DONE | DPAUX_INTR_IRQ_EVENT | | ||
323 | DPAUX_INTR_UNPLUG_EVENT | DPAUX_INTR_PLUG_EVENT; | ||
324 | tegra_dpaux_writel(dpaux, value, DPAUX_INTR_EN_AUX); | ||
325 | tegra_dpaux_writel(dpaux, value, DPAUX_INTR_AUX); | ||
326 | |||
327 | mutex_lock(&dpaux_lock); | ||
328 | list_add_tail(&dpaux->list, &dpaux_list); | ||
329 | mutex_unlock(&dpaux_lock); | ||
330 | |||
331 | platform_set_drvdata(pdev, dpaux); | ||
332 | |||
333 | return 0; | ||
334 | } | ||
335 | |||
336 | static int tegra_dpaux_remove(struct platform_device *pdev) | ||
337 | { | ||
338 | struct tegra_dpaux *dpaux = platform_get_drvdata(pdev); | ||
339 | |||
340 | drm_dp_aux_unregister_i2c_bus(&dpaux->aux); | ||
341 | |||
342 | mutex_lock(&dpaux_lock); | ||
343 | list_del(&dpaux->list); | ||
344 | mutex_unlock(&dpaux_lock); | ||
345 | |||
346 | clk_disable_unprepare(dpaux->clk_parent); | ||
347 | reset_control_assert(dpaux->rst); | ||
348 | clk_disable_unprepare(dpaux->clk); | ||
349 | |||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | static const struct of_device_id tegra_dpaux_of_match[] = { | ||
354 | { .compatible = "nvidia,tegra124-dpaux", }, | ||
355 | { }, | ||
356 | }; | ||
357 | |||
358 | struct platform_driver tegra_dpaux_driver = { | ||
359 | .driver = { | ||
360 | .name = "tegra-dpaux", | ||
361 | .of_match_table = tegra_dpaux_of_match, | ||
362 | }, | ||
363 | .probe = tegra_dpaux_probe, | ||
364 | .remove = tegra_dpaux_remove, | ||
365 | }; | ||
366 | |||
367 | struct tegra_dpaux *tegra_dpaux_find_by_of_node(struct device_node *np) | ||
368 | { | ||
369 | struct tegra_dpaux *dpaux; | ||
370 | |||
371 | mutex_lock(&dpaux_lock); | ||
372 | |||
373 | list_for_each_entry(dpaux, &dpaux_list, list) | ||
374 | if (np == dpaux->dev->of_node) { | ||
375 | mutex_unlock(&dpaux_lock); | ||
376 | return dpaux; | ||
377 | } | ||
378 | |||
379 | mutex_unlock(&dpaux_lock); | ||
380 | |||
381 | return NULL; | ||
382 | } | ||
383 | |||
384 | int tegra_dpaux_attach(struct tegra_dpaux *dpaux, struct tegra_output *output) | ||
385 | { | ||
386 | unsigned long timeout; | ||
387 | int err; | ||
388 | |||
389 | dpaux->output = output; | ||
390 | |||
391 | err = regulator_enable(dpaux->vdd); | ||
392 | if (err < 0) | ||
393 | return err; | ||
394 | |||
395 | timeout = jiffies + msecs_to_jiffies(250); | ||
396 | |||
397 | while (time_before(jiffies, timeout)) { | ||
398 | enum drm_connector_status status; | ||
399 | |||
400 | status = tegra_dpaux_detect(dpaux); | ||
401 | if (status == connector_status_connected) | ||
402 | return 0; | ||
403 | |||
404 | usleep_range(1000, 2000); | ||
405 | } | ||
406 | |||
407 | return -ETIMEDOUT; | ||
408 | } | ||
409 | |||
410 | int tegra_dpaux_detach(struct tegra_dpaux *dpaux) | ||
411 | { | ||
412 | unsigned long timeout; | ||
413 | int err; | ||
414 | |||
415 | err = regulator_disable(dpaux->vdd); | ||
416 | if (err < 0) | ||
417 | return err; | ||
418 | |||
419 | timeout = jiffies + msecs_to_jiffies(250); | ||
420 | |||
421 | while (time_before(jiffies, timeout)) { | ||
422 | enum drm_connector_status status; | ||
423 | |||
424 | status = tegra_dpaux_detect(dpaux); | ||
425 | if (status == connector_status_disconnected) { | ||
426 | dpaux->output = NULL; | ||
427 | return 0; | ||
428 | } | ||
429 | |||
430 | usleep_range(1000, 2000); | ||
431 | } | ||
432 | |||
433 | return -ETIMEDOUT; | ||
434 | } | ||
435 | |||
436 | enum drm_connector_status tegra_dpaux_detect(struct tegra_dpaux *dpaux) | ||
437 | { | ||
438 | unsigned long value; | ||
439 | |||
440 | value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXSTAT); | ||
441 | |||
442 | if (value & DPAUX_DP_AUXSTAT_HPD_STATUS) | ||
443 | return connector_status_connected; | ||
444 | |||
445 | return connector_status_disconnected; | ||
446 | } | ||
447 | |||
448 | int tegra_dpaux_enable(struct tegra_dpaux *dpaux) | ||
449 | { | ||
450 | unsigned long value; | ||
451 | |||
452 | value = DPAUX_HYBRID_PADCTL_AUX_CMH(2) | | ||
453 | DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) | | ||
454 | DPAUX_HYBRID_PADCTL_AUX_DRVI(0x18) | | ||
455 | DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV | | ||
456 | DPAUX_HYBRID_PADCTL_MODE_AUX; | ||
457 | tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_PADCTL); | ||
458 | |||
459 | value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE); | ||
460 | value &= ~DPAUX_HYBRID_SPARE_PAD_POWER_DOWN; | ||
461 | tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE); | ||
462 | |||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | int tegra_dpaux_disable(struct tegra_dpaux *dpaux) | ||
467 | { | ||
468 | unsigned long value; | ||
469 | |||
470 | value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE); | ||
471 | value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN; | ||
472 | tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE); | ||
473 | |||
474 | return 0; | ||
475 | } | ||
476 | |||
477 | int tegra_dpaux_prepare(struct tegra_dpaux *dpaux, u8 encoding) | ||
478 | { | ||
479 | int err; | ||
480 | |||
481 | err = drm_dp_dpcd_writeb(&dpaux->aux, DP_MAIN_LINK_CHANNEL_CODING_SET, | ||
482 | encoding); | ||
483 | if (err < 0) | ||
484 | return err; | ||
485 | |||
486 | return 0; | ||
487 | } | ||
488 | |||
489 | int tegra_dpaux_train(struct tegra_dpaux *dpaux, struct drm_dp_link *link, | ||
490 | u8 pattern) | ||
491 | { | ||
492 | u8 tp = pattern & DP_TRAINING_PATTERN_MASK; | ||
493 | u8 status[DP_LINK_STATUS_SIZE], values[4]; | ||
494 | unsigned int i; | ||
495 | int err; | ||
496 | |||
497 | err = drm_dp_dpcd_writeb(&dpaux->aux, DP_TRAINING_PATTERN_SET, pattern); | ||
498 | if (err < 0) | ||
499 | return err; | ||
500 | |||
501 | if (tp == DP_TRAINING_PATTERN_DISABLE) | ||
502 | return 0; | ||
503 | |||
504 | for (i = 0; i < link->num_lanes; i++) | ||
505 | values[i] = DP_TRAIN_MAX_PRE_EMPHASIS_REACHED | | ||
506 | DP_TRAIN_PRE_EMPHASIS_0 | | ||
507 | DP_TRAIN_MAX_SWING_REACHED | | ||
508 | DP_TRAIN_VOLTAGE_SWING_400; | ||
509 | |||
510 | err = drm_dp_dpcd_write(&dpaux->aux, DP_TRAINING_LANE0_SET, values, | ||
511 | link->num_lanes); | ||
512 | if (err < 0) | ||
513 | return err; | ||
514 | |||
515 | usleep_range(500, 1000); | ||
516 | |||
517 | err = drm_dp_dpcd_read_link_status(&dpaux->aux, status); | ||
518 | if (err < 0) | ||
519 | return err; | ||
520 | |||
521 | switch (tp) { | ||
522 | case DP_TRAINING_PATTERN_1: | ||
523 | if (!drm_dp_clock_recovery_ok(status, link->num_lanes)) | ||
524 | return -EAGAIN; | ||
525 | |||
526 | break; | ||
527 | |||
528 | case DP_TRAINING_PATTERN_2: | ||
529 | if (!drm_dp_channel_eq_ok(status, link->num_lanes)) | ||
530 | return -EAGAIN; | ||
531 | |||
532 | break; | ||
533 | |||
534 | default: | ||
535 | dev_err(dpaux->dev, "unsupported training pattern %u\n", tp); | ||
536 | return -EINVAL; | ||
537 | } | ||
538 | |||
539 | err = drm_dp_dpcd_writeb(&dpaux->aux, DP_EDP_CONFIGURATION_SET, 0); | ||
540 | if (err < 0) | ||
541 | return err; | ||
542 | |||
543 | return 0; | ||
544 | } | ||
diff --git a/drivers/gpu/drm/tegra/dpaux.h b/drivers/gpu/drm/tegra/dpaux.h new file mode 100644 index 000000000000..4f5bf10fdff9 --- /dev/null +++ b/drivers/gpu/drm/tegra/dpaux.h | |||
@@ -0,0 +1,73 @@ | |||
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 | #ifndef DRM_TEGRA_DPAUX_H | ||
10 | #define DRM_TEGRA_DPAUX_H | ||
11 | |||
12 | #define DPAUX_CTXSW 0x00 | ||
13 | |||
14 | #define DPAUX_INTR_EN_AUX 0x01 | ||
15 | #define DPAUX_INTR_AUX 0x05 | ||
16 | #define DPAUX_INTR_AUX_DONE (1 << 3) | ||
17 | #define DPAUX_INTR_IRQ_EVENT (1 << 2) | ||
18 | #define DPAUX_INTR_UNPLUG_EVENT (1 << 1) | ||
19 | #define DPAUX_INTR_PLUG_EVENT (1 << 0) | ||
20 | |||
21 | #define DPAUX_DP_AUXDATA_WRITE(x) (0x09 + ((x) << 2)) | ||
22 | #define DPAUX_DP_AUXDATA_READ(x) (0x19 + ((x) << 2)) | ||
23 | #define DPAUX_DP_AUXADDR 0x29 | ||
24 | |||
25 | #define DPAUX_DP_AUXCTL 0x2d | ||
26 | #define DPAUX_DP_AUXCTL_TRANSACTREQ (1 << 16) | ||
27 | #define DPAUX_DP_AUXCTL_CMD_AUX_RD (9 << 12) | ||
28 | #define DPAUX_DP_AUXCTL_CMD_AUX_WR (8 << 12) | ||
29 | #define DPAUX_DP_AUXCTL_CMD_MOT_RQ (6 << 12) | ||
30 | #define DPAUX_DP_AUXCTL_CMD_MOT_RD (5 << 12) | ||
31 | #define DPAUX_DP_AUXCTL_CMD_MOT_WR (4 << 12) | ||
32 | #define DPAUX_DP_AUXCTL_CMD_I2C_RQ (2 << 12) | ||
33 | #define DPAUX_DP_AUXCTL_CMD_I2C_RD (1 << 12) | ||
34 | #define DPAUX_DP_AUXCTL_CMD_I2C_WR (0 << 12) | ||
35 | #define DPAUX_DP_AUXCTL_CMDLEN(x) ((x) & 0xff) | ||
36 | |||
37 | #define DPAUX_DP_AUXSTAT 0x31 | ||
38 | #define DPAUX_DP_AUXSTAT_HPD_STATUS (1 << 28) | ||
39 | #define DPAUX_DP_AUXSTAT_REPLY_TYPE_MASK (0xf0000) | ||
40 | #define DPAUX_DP_AUXSTAT_NO_STOP_ERROR (1 << 11) | ||
41 | #define DPAUX_DP_AUXSTAT_SINKSTAT_ERROR (1 << 10) | ||
42 | #define DPAUX_DP_AUXSTAT_RX_ERROR (1 << 9) | ||
43 | #define DPAUX_DP_AUXSTAT_TIMEOUT_ERROR (1 << 8) | ||
44 | #define DPAUX_DP_AUXSTAT_REPLY_MASK (0xff) | ||
45 | |||
46 | #define DPAUX_DP_AUX_SINKSTAT_LO 0x35 | ||
47 | #define DPAUX_DP_AUX_SINKSTAT_HI 0x39 | ||
48 | |||
49 | #define DPAUX_HPD_CONFIG 0x3d | ||
50 | #define DPAUX_HPD_CONFIG_UNPLUG_MIN_TIME(x) (((x) & 0xffff) << 16) | ||
51 | #define DPAUX_HPD_CONFIG_PLUG_MIN_TIME(x) ((x) & 0xffff) | ||
52 | |||
53 | #define DPAUX_HPD_IRQ_CONFIG 0x41 | ||
54 | #define DPAUX_HPD_IRQ_CONFIG_MIN_LOW_TIME(x) ((x) & 0xffff) | ||
55 | |||
56 | #define DPAUX_DP_AUX_CONFIG 0x45 | ||
57 | |||
58 | #define DPAUX_HYBRID_PADCTL 0x49 | ||
59 | #define DPAUX_HYBRID_PADCTL_AUX_CMH(x) (((x) & 0x3) << 12) | ||
60 | #define DPAUX_HYBRID_PADCTL_AUX_DRVZ(x) (((x) & 0x7) << 8) | ||
61 | #define DPAUX_HYBRID_PADCTL_AUX_DRVI(x) (((x) & 0x3f) << 2) | ||
62 | #define DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV (1 << 1) | ||
63 | #define DPAUX_HYBRID_PADCTL_MODE_I2C (1 << 0) | ||
64 | #define DPAUX_HYBRID_PADCTL_MODE_AUX (0 << 0) | ||
65 | |||
66 | #define DPAUX_HYBRID_SPARE 0x4d | ||
67 | #define DPAUX_HYBRID_SPARE_PAD_POWER_DOWN (1 << 0) | ||
68 | |||
69 | #define DPAUX_SCRATCH_REG0 0x51 | ||
70 | #define DPAUX_SCRATCH_REG1 0x55 | ||
71 | #define DPAUX_SCRATCH_REG2 0x59 | ||
72 | |||
73 | #endif | ||
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 88a529008ce0..4c583d58334a 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c | |||
@@ -665,6 +665,7 @@ static const struct of_device_id host1x_drm_subdevs[] = { | |||
665 | { .compatible = "nvidia,tegra114-hdmi", }, | 665 | { .compatible = "nvidia,tegra114-hdmi", }, |
666 | { .compatible = "nvidia,tegra114-gr3d", }, | 666 | { .compatible = "nvidia,tegra114-gr3d", }, |
667 | { .compatible = "nvidia,tegra124-dc", }, | 667 | { .compatible = "nvidia,tegra124-dc", }, |
668 | { .compatible = "nvidia,tegra124-sor", }, | ||
668 | { /* sentinel */ } | 669 | { /* sentinel */ } |
669 | }; | 670 | }; |
670 | 671 | ||
@@ -691,14 +692,22 @@ static int __init host1x_drm_init(void) | |||
691 | if (err < 0) | 692 | if (err < 0) |
692 | goto unregister_dc; | 693 | goto unregister_dc; |
693 | 694 | ||
694 | err = platform_driver_register(&tegra_hdmi_driver); | 695 | err = platform_driver_register(&tegra_sor_driver); |
695 | if (err < 0) | 696 | if (err < 0) |
696 | goto unregister_dsi; | 697 | goto unregister_dsi; |
697 | 698 | ||
698 | err = platform_driver_register(&tegra_gr2d_driver); | 699 | err = platform_driver_register(&tegra_hdmi_driver); |
700 | if (err < 0) | ||
701 | goto unregister_sor; | ||
702 | |||
703 | err = platform_driver_register(&tegra_dpaux_driver); | ||
699 | if (err < 0) | 704 | if (err < 0) |
700 | goto unregister_hdmi; | 705 | goto unregister_hdmi; |
701 | 706 | ||
707 | err = platform_driver_register(&tegra_gr2d_driver); | ||
708 | if (err < 0) | ||
709 | goto unregister_dpaux; | ||
710 | |||
702 | err = platform_driver_register(&tegra_gr3d_driver); | 711 | err = platform_driver_register(&tegra_gr3d_driver); |
703 | if (err < 0) | 712 | if (err < 0) |
704 | goto unregister_gr2d; | 713 | goto unregister_gr2d; |
@@ -707,8 +716,12 @@ static int __init host1x_drm_init(void) | |||
707 | 716 | ||
708 | unregister_gr2d: | 717 | unregister_gr2d: |
709 | platform_driver_unregister(&tegra_gr2d_driver); | 718 | platform_driver_unregister(&tegra_gr2d_driver); |
719 | unregister_dpaux: | ||
720 | platform_driver_unregister(&tegra_dpaux_driver); | ||
710 | unregister_hdmi: | 721 | unregister_hdmi: |
711 | platform_driver_unregister(&tegra_hdmi_driver); | 722 | platform_driver_unregister(&tegra_hdmi_driver); |
723 | unregister_sor: | ||
724 | platform_driver_unregister(&tegra_sor_driver); | ||
712 | unregister_dsi: | 725 | unregister_dsi: |
713 | platform_driver_unregister(&tegra_dsi_driver); | 726 | platform_driver_unregister(&tegra_dsi_driver); |
714 | unregister_dc: | 727 | unregister_dc: |
@@ -723,7 +736,9 @@ static void __exit host1x_drm_exit(void) | |||
723 | { | 736 | { |
724 | platform_driver_unregister(&tegra_gr3d_driver); | 737 | platform_driver_unregister(&tegra_gr3d_driver); |
725 | platform_driver_unregister(&tegra_gr2d_driver); | 738 | platform_driver_unregister(&tegra_gr2d_driver); |
739 | platform_driver_unregister(&tegra_dpaux_driver); | ||
726 | platform_driver_unregister(&tegra_hdmi_driver); | 740 | platform_driver_unregister(&tegra_hdmi_driver); |
741 | platform_driver_unregister(&tegra_sor_driver); | ||
727 | platform_driver_unregister(&tegra_dsi_driver); | 742 | platform_driver_unregister(&tegra_dsi_driver); |
728 | platform_driver_unregister(&tegra_dc_driver); | 743 | platform_driver_unregister(&tegra_dc_driver); |
729 | host1x_driver_unregister(&host1x_drm_driver); | 744 | host1x_driver_unregister(&host1x_drm_driver); |
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index bf1cac7658f8..126332c3ecbb 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h | |||
@@ -179,12 +179,14 @@ struct tegra_output_ops { | |||
179 | int (*check_mode)(struct tegra_output *output, | 179 | int (*check_mode)(struct tegra_output *output, |
180 | struct drm_display_mode *mode, | 180 | struct drm_display_mode *mode, |
181 | enum drm_mode_status *status); | 181 | enum drm_mode_status *status); |
182 | enum drm_connector_status (*detect)(struct tegra_output *output); | ||
182 | }; | 183 | }; |
183 | 184 | ||
184 | enum tegra_output_type { | 185 | enum tegra_output_type { |
185 | TEGRA_OUTPUT_RGB, | 186 | TEGRA_OUTPUT_RGB, |
186 | TEGRA_OUTPUT_HDMI, | 187 | TEGRA_OUTPUT_HDMI, |
187 | TEGRA_OUTPUT_DSI, | 188 | TEGRA_OUTPUT_DSI, |
189 | TEGRA_OUTPUT_EDP, | ||
188 | }; | 190 | }; |
189 | 191 | ||
190 | struct tegra_output { | 192 | struct tegra_output { |
@@ -265,6 +267,22 @@ extern int tegra_output_remove(struct tegra_output *output); | |||
265 | extern int tegra_output_init(struct drm_device *drm, struct tegra_output *output); | 267 | extern int tegra_output_init(struct drm_device *drm, struct tegra_output *output); |
266 | extern int tegra_output_exit(struct tegra_output *output); | 268 | extern int tegra_output_exit(struct tegra_output *output); |
267 | 269 | ||
270 | /* from dpaux.c */ | ||
271 | |||
272 | struct tegra_dpaux; | ||
273 | struct drm_dp_link; | ||
274 | struct drm_dp_aux; | ||
275 | |||
276 | struct tegra_dpaux *tegra_dpaux_find_by_of_node(struct device_node *np); | ||
277 | enum drm_connector_status tegra_dpaux_detect(struct tegra_dpaux *dpaux); | ||
278 | int tegra_dpaux_attach(struct tegra_dpaux *dpaux, struct tegra_output *output); | ||
279 | int tegra_dpaux_detach(struct tegra_dpaux *dpaux); | ||
280 | int tegra_dpaux_enable(struct tegra_dpaux *dpaux); | ||
281 | int tegra_dpaux_disable(struct tegra_dpaux *dpaux); | ||
282 | int tegra_dpaux_prepare(struct tegra_dpaux *dpaux, u8 encoding); | ||
283 | int tegra_dpaux_train(struct tegra_dpaux *dpaux, struct drm_dp_link *link, | ||
284 | u8 pattern); | ||
285 | |||
268 | /* from fb.c */ | 286 | /* from fb.c */ |
269 | struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, | 287 | struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, |
270 | unsigned int index); | 288 | unsigned int index); |
@@ -278,7 +296,9 @@ extern void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev); | |||
278 | 296 | ||
279 | extern struct platform_driver tegra_dc_driver; | 297 | extern struct platform_driver tegra_dc_driver; |
280 | extern struct platform_driver tegra_dsi_driver; | 298 | extern struct platform_driver tegra_dsi_driver; |
299 | extern struct platform_driver tegra_sor_driver; | ||
281 | extern struct platform_driver tegra_hdmi_driver; | 300 | extern struct platform_driver tegra_hdmi_driver; |
301 | extern struct platform_driver tegra_dpaux_driver; | ||
282 | extern struct platform_driver tegra_gr2d_driver; | 302 | extern struct platform_driver tegra_gr2d_driver; |
283 | extern struct platform_driver tegra_gr3d_driver; | 303 | extern struct platform_driver tegra_gr3d_driver; |
284 | 304 | ||
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 57cecbd18ca8..a3e4f1eca6f7 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c | |||
@@ -77,6 +77,9 @@ tegra_connector_detect(struct drm_connector *connector, bool force) | |||
77 | struct tegra_output *output = connector_to_output(connector); | 77 | struct tegra_output *output = connector_to_output(connector); |
78 | enum drm_connector_status status = connector_status_unknown; | 78 | enum drm_connector_status status = connector_status_unknown; |
79 | 79 | ||
80 | if (output->ops->detect) | ||
81 | return output->ops->detect(output); | ||
82 | |||
80 | if (gpio_is_valid(output->hpd_gpio)) { | 83 | if (gpio_is_valid(output->hpd_gpio)) { |
81 | if (gpio_get_value(output->hpd_gpio) == 0) | 84 | if (gpio_get_value(output->hpd_gpio) == 0) |
82 | status = connector_status_disconnected; | 85 | status = connector_status_disconnected; |
@@ -292,6 +295,11 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output) | |||
292 | encoder = DRM_MODE_ENCODER_DSI; | 295 | encoder = DRM_MODE_ENCODER_DSI; |
293 | break; | 296 | break; |
294 | 297 | ||
298 | case TEGRA_OUTPUT_EDP: | ||
299 | connector = DRM_MODE_CONNECTOR_eDP; | ||
300 | encoder = DRM_MODE_ENCODER_TMDS; | ||
301 | break; | ||
302 | |||
295 | default: | 303 | default: |
296 | connector = DRM_MODE_CONNECTOR_Unknown; | 304 | connector = DRM_MODE_CONNECTOR_Unknown; |
297 | encoder = DRM_MODE_ENCODER_NONE; | 305 | encoder = DRM_MODE_ENCODER_NONE; |
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c new file mode 100644 index 000000000000..49ef5729f435 --- /dev/null +++ b/drivers/gpu/drm/tegra/sor.c | |||
@@ -0,0 +1,1092 @@ | |||
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> | ||
10 | #include <linux/io.h> | ||
11 | #include <linux/platform_device.h> | ||
12 | #include <linux/reset.h> | ||
13 | #include <linux/tegra-powergate.h> | ||
14 | |||
15 | #include <drm/drm_dp_helper.h> | ||
16 | |||
17 | #include "dc.h" | ||
18 | #include "drm.h" | ||
19 | #include "sor.h" | ||
20 | |||
21 | struct tegra_sor { | ||
22 | struct host1x_client client; | ||
23 | struct tegra_output output; | ||
24 | struct device *dev; | ||
25 | |||
26 | void __iomem *regs; | ||
27 | |||
28 | struct reset_control *rst; | ||
29 | struct clk *clk_parent; | ||
30 | struct clk *clk_safe; | ||
31 | struct clk *clk_dp; | ||
32 | struct clk *clk; | ||
33 | |||
34 | struct tegra_dpaux *dpaux; | ||
35 | |||
36 | bool enabled; | ||
37 | }; | ||
38 | |||
39 | static inline struct tegra_sor * | ||
40 | host1x_client_to_sor(struct host1x_client *client) | ||
41 | { | ||
42 | return container_of(client, struct tegra_sor, client); | ||
43 | } | ||
44 | |||
45 | static inline struct tegra_sor *to_sor(struct tegra_output *output) | ||
46 | { | ||
47 | return container_of(output, struct tegra_sor, output); | ||
48 | } | ||
49 | |||
50 | static inline unsigned long tegra_sor_readl(struct tegra_sor *sor, | ||
51 | unsigned long offset) | ||
52 | { | ||
53 | return readl(sor->regs + (offset << 2)); | ||
54 | } | ||
55 | |||
56 | static inline void tegra_sor_writel(struct tegra_sor *sor, unsigned long value, | ||
57 | unsigned long offset) | ||
58 | { | ||
59 | writel(value, sor->regs + (offset << 2)); | ||
60 | } | ||
61 | |||
62 | static int tegra_sor_dp_train_fast(struct tegra_sor *sor, | ||
63 | struct drm_dp_link *link) | ||
64 | { | ||
65 | unsigned long value; | ||
66 | unsigned int i; | ||
67 | u8 pattern; | ||
68 | int err; | ||
69 | |||
70 | /* setup lane parameters */ | ||
71 | value = SOR_LANE_DRIVE_CURRENT_LANE3(0x40) | | ||
72 | SOR_LANE_DRIVE_CURRENT_LANE2(0x40) | | ||
73 | SOR_LANE_DRIVE_CURRENT_LANE1(0x40) | | ||
74 | SOR_LANE_DRIVE_CURRENT_LANE0(0x40); | ||
75 | tegra_sor_writel(sor, value, SOR_LANE_DRIVE_CURRENT_0); | ||
76 | |||
77 | value = SOR_LANE_PREEMPHASIS_LANE3(0x0f) | | ||
78 | SOR_LANE_PREEMPHASIS_LANE2(0x0f) | | ||
79 | SOR_LANE_PREEMPHASIS_LANE1(0x0f) | | ||
80 | SOR_LANE_PREEMPHASIS_LANE0(0x0f); | ||
81 | tegra_sor_writel(sor, value, SOR_LANE_PREEMPHASIS_0); | ||
82 | |||
83 | value = SOR_LANE_POST_CURSOR_LANE3(0x00) | | ||
84 | SOR_LANE_POST_CURSOR_LANE2(0x00) | | ||
85 | SOR_LANE_POST_CURSOR_LANE1(0x00) | | ||
86 | SOR_LANE_POST_CURSOR_LANE0(0x00); | ||
87 | tegra_sor_writel(sor, value, SOR_LANE_POST_CURSOR_0); | ||
88 | |||
89 | /* disable LVDS mode */ | ||
90 | tegra_sor_writel(sor, 0, SOR_LVDS); | ||
91 | |||
92 | value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); | ||
93 | value |= SOR_DP_PADCTL_TX_PU_ENABLE; | ||
94 | value &= ~SOR_DP_PADCTL_TX_PU_MASK; | ||
95 | value |= SOR_DP_PADCTL_TX_PU(2); /* XXX: don't hardcode? */ | ||
96 | tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); | ||
97 | |||
98 | value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); | ||
99 | value |= SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 | | ||
100 | SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0; | ||
101 | tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); | ||
102 | |||
103 | usleep_range(10, 100); | ||
104 | |||
105 | value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); | ||
106 | value &= ~(SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 | | ||
107 | SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0); | ||
108 | tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); | ||
109 | |||
110 | err = tegra_dpaux_prepare(sor->dpaux, DP_SET_ANSI_8B10B); | ||
111 | if (err < 0) | ||
112 | return err; | ||
113 | |||
114 | for (i = 0, value = 0; i < link->num_lanes; i++) { | ||
115 | unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | | ||
116 | SOR_DP_TPG_SCRAMBLER_NONE | | ||
117 | SOR_DP_TPG_PATTERN_TRAIN1; | ||
118 | value = (value << 8) | lane; | ||
119 | } | ||
120 | |||
121 | tegra_sor_writel(sor, value, SOR_DP_TPG); | ||
122 | |||
123 | pattern = DP_TRAINING_PATTERN_1; | ||
124 | |||
125 | err = tegra_dpaux_train(sor->dpaux, link, pattern); | ||
126 | if (err < 0) | ||
127 | return err; | ||
128 | |||
129 | value = tegra_sor_readl(sor, SOR_DP_SPARE_0); | ||
130 | value |= SOR_DP_SPARE_SEQ_ENABLE; | ||
131 | value &= ~SOR_DP_SPARE_PANEL_INTERNAL; | ||
132 | value |= SOR_DP_SPARE_MACRO_SOR_CLK; | ||
133 | tegra_sor_writel(sor, value, SOR_DP_SPARE_0); | ||
134 | |||
135 | for (i = 0, value = 0; i < link->num_lanes; i++) { | ||
136 | unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | | ||
137 | SOR_DP_TPG_SCRAMBLER_NONE | | ||
138 | SOR_DP_TPG_PATTERN_TRAIN2; | ||
139 | value = (value << 8) | lane; | ||
140 | } | ||
141 | |||
142 | tegra_sor_writel(sor, value, SOR_DP_TPG); | ||
143 | |||
144 | pattern = DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_2; | ||
145 | |||
146 | err = tegra_dpaux_train(sor->dpaux, link, pattern); | ||
147 | if (err < 0) | ||
148 | return err; | ||
149 | |||
150 | for (i = 0, value = 0; i < link->num_lanes; i++) { | ||
151 | unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | | ||
152 | SOR_DP_TPG_SCRAMBLER_GALIOS | | ||
153 | SOR_DP_TPG_PATTERN_NONE; | ||
154 | value = (value << 8) | lane; | ||
155 | } | ||
156 | |||
157 | tegra_sor_writel(sor, value, SOR_DP_TPG); | ||
158 | |||
159 | pattern = DP_TRAINING_PATTERN_DISABLE; | ||
160 | |||
161 | err = tegra_dpaux_train(sor->dpaux, link, pattern); | ||
162 | if (err < 0) | ||
163 | return err; | ||
164 | |||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static void tegra_sor_super_update(struct tegra_sor *sor) | ||
169 | { | ||
170 | tegra_sor_writel(sor, 0, SOR_SUPER_STATE_0); | ||
171 | tegra_sor_writel(sor, 1, SOR_SUPER_STATE_0); | ||
172 | tegra_sor_writel(sor, 0, SOR_SUPER_STATE_0); | ||
173 | } | ||
174 | |||
175 | static void tegra_sor_update(struct tegra_sor *sor) | ||
176 | { | ||
177 | tegra_sor_writel(sor, 0, SOR_STATE_0); | ||
178 | tegra_sor_writel(sor, 1, SOR_STATE_0); | ||
179 | tegra_sor_writel(sor, 0, SOR_STATE_0); | ||
180 | } | ||
181 | |||
182 | static int tegra_sor_setup_pwm(struct tegra_sor *sor, unsigned long timeout) | ||
183 | { | ||
184 | unsigned long value; | ||
185 | |||
186 | value = tegra_sor_readl(sor, SOR_PWM_DIV); | ||
187 | value &= ~SOR_PWM_DIV_MASK; | ||
188 | value |= 0x400; /* period */ | ||
189 | tegra_sor_writel(sor, value, SOR_PWM_DIV); | ||
190 | |||
191 | value = tegra_sor_readl(sor, SOR_PWM_CTL); | ||
192 | value &= ~SOR_PWM_CTL_DUTY_CYCLE_MASK; | ||
193 | value |= 0x400; /* duty cycle */ | ||
194 | value &= ~SOR_PWM_CTL_CLK_SEL; /* clock source: PCLK */ | ||
195 | value |= SOR_PWM_CTL_TRIGGER; | ||
196 | tegra_sor_writel(sor, value, SOR_PWM_CTL); | ||
197 | |||
198 | timeout = jiffies + msecs_to_jiffies(timeout); | ||
199 | |||
200 | while (time_before(jiffies, timeout)) { | ||
201 | value = tegra_sor_readl(sor, SOR_PWM_CTL); | ||
202 | if ((value & SOR_PWM_CTL_TRIGGER) == 0) | ||
203 | return 0; | ||
204 | |||
205 | usleep_range(25, 100); | ||
206 | } | ||
207 | |||
208 | return -ETIMEDOUT; | ||
209 | } | ||
210 | |||
211 | static int tegra_sor_attach(struct tegra_sor *sor) | ||
212 | { | ||
213 | unsigned long value, timeout; | ||
214 | |||
215 | /* wake up in normal mode */ | ||
216 | value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); | ||
217 | value |= SOR_SUPER_STATE_HEAD_MODE_AWAKE; | ||
218 | value |= SOR_SUPER_STATE_MODE_NORMAL; | ||
219 | tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); | ||
220 | tegra_sor_super_update(sor); | ||
221 | |||
222 | /* attach */ | ||
223 | value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); | ||
224 | value |= SOR_SUPER_STATE_ATTACHED; | ||
225 | tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); | ||
226 | tegra_sor_super_update(sor); | ||
227 | |||
228 | timeout = jiffies + msecs_to_jiffies(250); | ||
229 | |||
230 | while (time_before(jiffies, timeout)) { | ||
231 | value = tegra_sor_readl(sor, SOR_TEST); | ||
232 | if ((value & SOR_TEST_ATTACHED) != 0) | ||
233 | return 0; | ||
234 | |||
235 | usleep_range(25, 100); | ||
236 | } | ||
237 | |||
238 | return -ETIMEDOUT; | ||
239 | } | ||
240 | |||
241 | static int tegra_sor_wakeup(struct tegra_sor *sor) | ||
242 | { | ||
243 | struct tegra_dc *dc = to_tegra_dc(sor->output.encoder.crtc); | ||
244 | unsigned long value, timeout; | ||
245 | |||
246 | /* enable display controller outputs */ | ||
247 | value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); | ||
248 | value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | | ||
249 | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; | ||
250 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); | ||
251 | |||
252 | tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); | ||
253 | tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); | ||
254 | |||
255 | timeout = jiffies + msecs_to_jiffies(250); | ||
256 | |||
257 | /* wait for head to wake up */ | ||
258 | while (time_before(jiffies, timeout)) { | ||
259 | value = tegra_sor_readl(sor, SOR_TEST); | ||
260 | value &= SOR_TEST_HEAD_MODE_MASK; | ||
261 | |||
262 | if (value == SOR_TEST_HEAD_MODE_AWAKE) | ||
263 | return 0; | ||
264 | |||
265 | usleep_range(25, 100); | ||
266 | } | ||
267 | |||
268 | return -ETIMEDOUT; | ||
269 | } | ||
270 | |||
271 | static int tegra_sor_power_up(struct tegra_sor *sor, unsigned long timeout) | ||
272 | { | ||
273 | unsigned long value; | ||
274 | |||
275 | value = tegra_sor_readl(sor, SOR_PWR); | ||
276 | value |= SOR_PWR_TRIGGER | SOR_PWR_NORMAL_STATE_PU; | ||
277 | tegra_sor_writel(sor, value, SOR_PWR); | ||
278 | |||
279 | timeout = jiffies + msecs_to_jiffies(timeout); | ||
280 | |||
281 | while (time_before(jiffies, timeout)) { | ||
282 | value = tegra_sor_readl(sor, SOR_PWR); | ||
283 | if ((value & SOR_PWR_TRIGGER) == 0) | ||
284 | return 0; | ||
285 | |||
286 | usleep_range(25, 100); | ||
287 | } | ||
288 | |||
289 | return -ETIMEDOUT; | ||
290 | } | ||
291 | |||
292 | static int tegra_output_sor_enable(struct tegra_output *output) | ||
293 | { | ||
294 | struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); | ||
295 | struct drm_display_mode *mode = &dc->base.mode; | ||
296 | unsigned int vbe, vse, hbe, hse, vbs, hbs, i; | ||
297 | struct tegra_sor *sor = to_sor(output); | ||
298 | unsigned long value; | ||
299 | int err; | ||
300 | |||
301 | if (sor->enabled) | ||
302 | return 0; | ||
303 | |||
304 | err = clk_prepare_enable(sor->clk); | ||
305 | if (err < 0) | ||
306 | return err; | ||
307 | |||
308 | reset_control_deassert(sor->rst); | ||
309 | |||
310 | if (sor->dpaux) { | ||
311 | err = tegra_dpaux_enable(sor->dpaux); | ||
312 | if (err < 0) | ||
313 | dev_err(sor->dev, "failed to enable DP: %d\n", err); | ||
314 | } | ||
315 | |||
316 | err = clk_set_parent(sor->clk, sor->clk_safe); | ||
317 | if (err < 0) | ||
318 | dev_err(sor->dev, "failed to set safe parent clock: %d\n", err); | ||
319 | |||
320 | value = tegra_sor_readl(sor, SOR_CLK_CNTRL); | ||
321 | value &= ~SOR_CLK_CNTRL_DP_CLK_SEL_MASK; | ||
322 | value |= SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK; | ||
323 | tegra_sor_writel(sor, value, SOR_CLK_CNTRL); | ||
324 | |||
325 | value = tegra_sor_readl(sor, SOR_PLL_2); | ||
326 | value &= ~SOR_PLL_2_BANDGAP_POWERDOWN; | ||
327 | tegra_sor_writel(sor, value, SOR_PLL_2); | ||
328 | usleep_range(20, 100); | ||
329 | |||
330 | value = tegra_sor_readl(sor, SOR_PLL_3); | ||
331 | value |= SOR_PLL_3_PLL_VDD_MODE_V3_3; | ||
332 | tegra_sor_writel(sor, value, SOR_PLL_3); | ||
333 | |||
334 | value = SOR_PLL_0_ICHPMP(0xf) | SOR_PLL_0_VCOCAP_RST | | ||
335 | SOR_PLL_0_PLLREG_LEVEL_V45 | SOR_PLL_0_RESISTOR_EXT; | ||
336 | tegra_sor_writel(sor, value, SOR_PLL_0); | ||
337 | |||
338 | value = tegra_sor_readl(sor, SOR_PLL_2); | ||
339 | value |= SOR_PLL_2_SEQ_PLLCAPPD; | ||
340 | value &= ~SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE; | ||
341 | value |= SOR_PLL_2_LVDS_ENABLE; | ||
342 | tegra_sor_writel(sor, value, SOR_PLL_2); | ||
343 | |||
344 | value = SOR_PLL_1_TERM_COMPOUT | SOR_PLL_1_TMDS_TERM; | ||
345 | tegra_sor_writel(sor, value, SOR_PLL_1); | ||
346 | |||
347 | while (true) { | ||
348 | value = tegra_sor_readl(sor, SOR_PLL_2); | ||
349 | if ((value & SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE) == 0) | ||
350 | break; | ||
351 | |||
352 | usleep_range(250, 1000); | ||
353 | } | ||
354 | |||
355 | value = tegra_sor_readl(sor, SOR_PLL_2); | ||
356 | value &= ~SOR_PLL_2_POWERDOWN_OVERRIDE; | ||
357 | value &= ~SOR_PLL_2_PORT_POWERDOWN; | ||
358 | tegra_sor_writel(sor, value, SOR_PLL_2); | ||
359 | |||
360 | /* | ||
361 | * power up | ||
362 | */ | ||
363 | |||
364 | /* set safe link bandwidth (1.62 Gbps) */ | ||
365 | value = tegra_sor_readl(sor, SOR_CLK_CNTRL); | ||
366 | value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; | ||
367 | value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G1_62; | ||
368 | tegra_sor_writel(sor, value, SOR_CLK_CNTRL); | ||
369 | |||
370 | /* step 1 */ | ||
371 | value = tegra_sor_readl(sor, SOR_PLL_2); | ||
372 | value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE | SOR_PLL_2_PORT_POWERDOWN | | ||
373 | SOR_PLL_2_BANDGAP_POWERDOWN; | ||
374 | tegra_sor_writel(sor, value, SOR_PLL_2); | ||
375 | |||
376 | value = tegra_sor_readl(sor, SOR_PLL_0); | ||
377 | value |= SOR_PLL_0_VCOPD | SOR_PLL_0_POWER_OFF; | ||
378 | tegra_sor_writel(sor, value, SOR_PLL_0); | ||
379 | |||
380 | value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); | ||
381 | value &= ~SOR_DP_PADCTL_PAD_CAL_PD; | ||
382 | tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); | ||
383 | |||
384 | /* step 2 */ | ||
385 | err = tegra_io_rail_power_on(TEGRA_IO_RAIL_LVDS); | ||
386 | if (err < 0) { | ||
387 | dev_err(sor->dev, "failed to power on I/O rail: %d\n", err); | ||
388 | return err; | ||
389 | } | ||
390 | |||
391 | usleep_range(5, 100); | ||
392 | |||
393 | /* step 3 */ | ||
394 | value = tegra_sor_readl(sor, SOR_PLL_2); | ||
395 | value &= ~SOR_PLL_2_BANDGAP_POWERDOWN; | ||
396 | tegra_sor_writel(sor, value, SOR_PLL_2); | ||
397 | |||
398 | usleep_range(20, 100); | ||
399 | |||
400 | /* step 4 */ | ||
401 | value = tegra_sor_readl(sor, SOR_PLL_0); | ||
402 | value &= ~SOR_PLL_0_POWER_OFF; | ||
403 | value &= ~SOR_PLL_0_VCOPD; | ||
404 | tegra_sor_writel(sor, value, SOR_PLL_0); | ||
405 | |||
406 | value = tegra_sor_readl(sor, SOR_PLL_2); | ||
407 | value &= ~SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE; | ||
408 | tegra_sor_writel(sor, value, SOR_PLL_2); | ||
409 | |||
410 | usleep_range(200, 1000); | ||
411 | |||
412 | /* step 5 */ | ||
413 | value = tegra_sor_readl(sor, SOR_PLL_2); | ||
414 | value &= ~SOR_PLL_2_PORT_POWERDOWN; | ||
415 | tegra_sor_writel(sor, value, SOR_PLL_2); | ||
416 | |||
417 | /* switch to DP clock */ | ||
418 | err = clk_set_parent(sor->clk, sor->clk_dp); | ||
419 | if (err < 0) | ||
420 | dev_err(sor->dev, "failed to set DP parent clock: %d\n", err); | ||
421 | |||
422 | /* power dplanes (XXX parameterize based on link?) */ | ||
423 | value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); | ||
424 | value |= SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 | | ||
425 | SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2; | ||
426 | tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); | ||
427 | |||
428 | value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0); | ||
429 | value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; | ||
430 | value |= SOR_DP_LINKCTL_LANE_COUNT(4); | ||
431 | tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0); | ||
432 | |||
433 | /* start lane sequencer */ | ||
434 | value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_DOWN | | ||
435 | SOR_LANE_SEQ_CTL_POWER_STATE_UP; | ||
436 | tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL); | ||
437 | |||
438 | while (true) { | ||
439 | value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL); | ||
440 | if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0) | ||
441 | break; | ||
442 | |||
443 | usleep_range(250, 1000); | ||
444 | } | ||
445 | |||
446 | /* set link bandwidth (2.7 GHz, XXX: parameterize based on link?) */ | ||
447 | value = tegra_sor_readl(sor, SOR_CLK_CNTRL); | ||
448 | value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; | ||
449 | value |= SOR_CLK_CNTRL_DP_LINK_SPEED_G2_70; | ||
450 | tegra_sor_writel(sor, value, SOR_CLK_CNTRL); | ||
451 | |||
452 | /* set linkctl */ | ||
453 | value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0); | ||
454 | value |= SOR_DP_LINKCTL_ENABLE; | ||
455 | |||
456 | value &= ~SOR_DP_LINKCTL_TU_SIZE_MASK; | ||
457 | value |= SOR_DP_LINKCTL_TU_SIZE(59); /* XXX: don't hardcode? */ | ||
458 | |||
459 | value |= SOR_DP_LINKCTL_ENHANCED_FRAME; | ||
460 | tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0); | ||
461 | |||
462 | for (i = 0, value = 0; i < 4; i++) { | ||
463 | unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | | ||
464 | SOR_DP_TPG_SCRAMBLER_GALIOS | | ||
465 | SOR_DP_TPG_PATTERN_NONE; | ||
466 | value = (value << 8) | lane; | ||
467 | } | ||
468 | |||
469 | tegra_sor_writel(sor, value, SOR_DP_TPG); | ||
470 | |||
471 | value = tegra_sor_readl(sor, SOR_DP_CONFIG_0); | ||
472 | value &= ~SOR_DP_CONFIG_WATERMARK_MASK; | ||
473 | value |= SOR_DP_CONFIG_WATERMARK(14); /* XXX: don't hardcode? */ | ||
474 | |||
475 | value &= ~SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK; | ||
476 | value |= SOR_DP_CONFIG_ACTIVE_SYM_COUNT(47); /* XXX: don't hardcode? */ | ||
477 | |||
478 | value &= ~SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK; | ||
479 | value |= SOR_DP_CONFIG_ACTIVE_SYM_FRAC(9); /* XXX: don't hardcode? */ | ||
480 | |||
481 | value &= ~SOR_DP_CONFIG_ACTIVE_SYM_POLARITY; /* XXX: don't hardcode? */ | ||
482 | |||
483 | value |= SOR_DP_CONFIG_ACTIVE_SYM_ENABLE; | ||
484 | value |= SOR_DP_CONFIG_DISPARITY_NEGATIVE; /* XXX: don't hardcode? */ | ||
485 | tegra_sor_writel(sor, value, SOR_DP_CONFIG_0); | ||
486 | |||
487 | value = tegra_sor_readl(sor, SOR_DP_AUDIO_HBLANK_SYMBOLS); | ||
488 | value &= ~SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK; | ||
489 | value |= 137; /* XXX: don't hardcode? */ | ||
490 | tegra_sor_writel(sor, value, SOR_DP_AUDIO_HBLANK_SYMBOLS); | ||
491 | |||
492 | value = tegra_sor_readl(sor, SOR_DP_AUDIO_VBLANK_SYMBOLS); | ||
493 | value &= ~SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK; | ||
494 | value |= 2368; /* XXX: don't hardcode? */ | ||
495 | tegra_sor_writel(sor, value, SOR_DP_AUDIO_VBLANK_SYMBOLS); | ||
496 | |||
497 | /* enable pad calibration logic */ | ||
498 | value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); | ||
499 | value |= SOR_DP_PADCTL_PAD_CAL_PD; | ||
500 | tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); | ||
501 | |||
502 | if (sor->dpaux) { | ||
503 | /* FIXME: properly convert to struct drm_dp_aux */ | ||
504 | struct drm_dp_aux *aux = (struct drm_dp_aux *)sor->dpaux; | ||
505 | struct drm_dp_link link; | ||
506 | u8 rate, lanes; | ||
507 | |||
508 | err = drm_dp_link_probe(aux, &link); | ||
509 | if (err < 0) { | ||
510 | dev_err(sor->dev, "failed to probe eDP link: %d\n", | ||
511 | err); | ||
512 | return err; | ||
513 | } | ||
514 | |||
515 | err = drm_dp_link_power_up(aux, &link); | ||
516 | if (err < 0) { | ||
517 | dev_err(sor->dev, "failed to power up eDP link: %d\n", | ||
518 | err); | ||
519 | return err; | ||
520 | } | ||
521 | |||
522 | err = drm_dp_link_configure(aux, &link); | ||
523 | if (err < 0) { | ||
524 | dev_err(sor->dev, "failed to configure eDP link: %d\n", | ||
525 | err); | ||
526 | return err; | ||
527 | } | ||
528 | |||
529 | rate = drm_dp_link_rate_to_bw_code(link.rate); | ||
530 | lanes = link.num_lanes; | ||
531 | |||
532 | value = tegra_sor_readl(sor, SOR_CLK_CNTRL); | ||
533 | value &= ~SOR_CLK_CNTRL_DP_LINK_SPEED_MASK; | ||
534 | value |= SOR_CLK_CNTRL_DP_LINK_SPEED(rate); | ||
535 | tegra_sor_writel(sor, value, SOR_CLK_CNTRL); | ||
536 | |||
537 | value = tegra_sor_readl(sor, SOR_DP_LINKCTL_0); | ||
538 | value &= ~SOR_DP_LINKCTL_LANE_COUNT_MASK; | ||
539 | value |= SOR_DP_LINKCTL_LANE_COUNT(lanes); | ||
540 | |||
541 | if (link.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) | ||
542 | value |= SOR_DP_LINKCTL_ENHANCED_FRAME; | ||
543 | |||
544 | tegra_sor_writel(sor, value, SOR_DP_LINKCTL_0); | ||
545 | |||
546 | /* disable training pattern generator */ | ||
547 | |||
548 | for (i = 0; i < link.num_lanes; i++) { | ||
549 | unsigned long lane = SOR_DP_TPG_CHANNEL_CODING | | ||
550 | SOR_DP_TPG_SCRAMBLER_GALIOS | | ||
551 | SOR_DP_TPG_PATTERN_NONE; | ||
552 | value = (value << 8) | lane; | ||
553 | } | ||
554 | |||
555 | tegra_sor_writel(sor, value, SOR_DP_TPG); | ||
556 | |||
557 | err = tegra_sor_dp_train_fast(sor, &link); | ||
558 | if (err < 0) { | ||
559 | dev_err(sor->dev, "DP fast link training failed: %d\n", | ||
560 | err); | ||
561 | return err; | ||
562 | } | ||
563 | |||
564 | dev_dbg(sor->dev, "fast link training succeeded\n"); | ||
565 | } | ||
566 | |||
567 | err = tegra_sor_power_up(sor, 250); | ||
568 | if (err < 0) { | ||
569 | dev_err(sor->dev, "failed to power up SOR: %d\n", err); | ||
570 | return err; | ||
571 | } | ||
572 | |||
573 | /* start display controller in continuous mode */ | ||
574 | value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS); | ||
575 | value |= WRITE_MUX; | ||
576 | tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS); | ||
577 | |||
578 | tegra_dc_writel(dc, VSYNC_H_POSITION(1), DC_DISP_DISP_TIMING_OPTIONS); | ||
579 | tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND); | ||
580 | |||
581 | value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS); | ||
582 | value &= ~WRITE_MUX; | ||
583 | tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS); | ||
584 | |||
585 | /* | ||
586 | * configure panel (24bpp, vsync-, hsync-, DP-A protocol, complete | ||
587 | * raster, associate with display controller) | ||
588 | */ | ||
589 | value = SOR_STATE_ASY_PIXELDEPTH_BPP_24_444 | | ||
590 | SOR_STATE_ASY_VSYNCPOL | | ||
591 | SOR_STATE_ASY_HSYNCPOL | | ||
592 | SOR_STATE_ASY_PROTOCOL_DP_A | | ||
593 | SOR_STATE_ASY_CRC_MODE_COMPLETE | | ||
594 | SOR_STATE_ASY_OWNER(dc->pipe + 1); | ||
595 | tegra_sor_writel(sor, value, SOR_STATE_1); | ||
596 | |||
597 | /* | ||
598 | * TODO: The video timing programming below doesn't seem to match the | ||
599 | * register definitions. | ||
600 | */ | ||
601 | |||
602 | value = ((mode->vtotal & 0x7fff) << 16) | (mode->htotal & 0x7fff); | ||
603 | tegra_sor_writel(sor, value, SOR_HEAD_STATE_1(0)); | ||
604 | |||
605 | vse = mode->vsync_end - mode->vsync_start - 1; | ||
606 | hse = mode->hsync_end - mode->hsync_start - 1; | ||
607 | |||
608 | value = ((vse & 0x7fff) << 16) | (hse & 0x7fff); | ||
609 | tegra_sor_writel(sor, value, SOR_HEAD_STATE_2(0)); | ||
610 | |||
611 | vbe = vse + (mode->vsync_start - mode->vdisplay); | ||
612 | hbe = hse + (mode->hsync_start - mode->hdisplay); | ||
613 | |||
614 | value = ((vbe & 0x7fff) << 16) | (hbe & 0x7fff); | ||
615 | tegra_sor_writel(sor, value, SOR_HEAD_STATE_3(0)); | ||
616 | |||
617 | vbs = vbe + mode->vdisplay; | ||
618 | hbs = hbe + mode->hdisplay; | ||
619 | |||
620 | value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff); | ||
621 | tegra_sor_writel(sor, value, SOR_HEAD_STATE_4(0)); | ||
622 | |||
623 | /* XXX interlaced mode */ | ||
624 | tegra_sor_writel(sor, 0x00000001, SOR_HEAD_STATE_5(0)); | ||
625 | |||
626 | /* CSTM (LVDS, link A/B, upper) */ | ||
627 | value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_B | SOR_CSTM_LINK_ACT_B | | ||
628 | SOR_CSTM_UPPER; | ||
629 | tegra_sor_writel(sor, value, SOR_CSTM); | ||
630 | |||
631 | /* PWM setup */ | ||
632 | err = tegra_sor_setup_pwm(sor, 250); | ||
633 | if (err < 0) { | ||
634 | dev_err(sor->dev, "failed to setup PWM: %d\n", err); | ||
635 | return err; | ||
636 | } | ||
637 | |||
638 | value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); | ||
639 | value |= SOR_ENABLE; | ||
640 | tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); | ||
641 | |||
642 | tegra_sor_update(sor); | ||
643 | |||
644 | err = tegra_sor_attach(sor); | ||
645 | if (err < 0) { | ||
646 | dev_err(sor->dev, "failed to attach SOR: %d\n", err); | ||
647 | return err; | ||
648 | } | ||
649 | |||
650 | err = tegra_sor_wakeup(sor); | ||
651 | if (err < 0) { | ||
652 | dev_err(sor->dev, "failed to enable DC: %d\n", err); | ||
653 | return err; | ||
654 | } | ||
655 | |||
656 | sor->enabled = true; | ||
657 | |||
658 | return 0; | ||
659 | } | ||
660 | |||
661 | static int tegra_sor_detach(struct tegra_sor *sor) | ||
662 | { | ||
663 | unsigned long value, timeout; | ||
664 | |||
665 | /* switch to safe mode */ | ||
666 | value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); | ||
667 | value &= ~SOR_SUPER_STATE_MODE_NORMAL; | ||
668 | tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); | ||
669 | tegra_sor_super_update(sor); | ||
670 | |||
671 | timeout = jiffies + msecs_to_jiffies(250); | ||
672 | |||
673 | while (time_before(jiffies, timeout)) { | ||
674 | value = tegra_sor_readl(sor, SOR_PWR); | ||
675 | if (value & SOR_PWR_MODE_SAFE) | ||
676 | break; | ||
677 | } | ||
678 | |||
679 | if ((value & SOR_PWR_MODE_SAFE) == 0) | ||
680 | return -ETIMEDOUT; | ||
681 | |||
682 | /* go to sleep */ | ||
683 | value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); | ||
684 | value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK; | ||
685 | tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); | ||
686 | tegra_sor_super_update(sor); | ||
687 | |||
688 | /* detach */ | ||
689 | value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); | ||
690 | value &= ~SOR_SUPER_STATE_ATTACHED; | ||
691 | tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); | ||
692 | tegra_sor_super_update(sor); | ||
693 | |||
694 | timeout = jiffies + msecs_to_jiffies(250); | ||
695 | |||
696 | while (time_before(jiffies, timeout)) { | ||
697 | value = tegra_sor_readl(sor, SOR_TEST); | ||
698 | if ((value & SOR_TEST_ATTACHED) == 0) | ||
699 | break; | ||
700 | |||
701 | usleep_range(25, 100); | ||
702 | } | ||
703 | |||
704 | if ((value & SOR_TEST_ATTACHED) != 0) | ||
705 | return -ETIMEDOUT; | ||
706 | |||
707 | return 0; | ||
708 | } | ||
709 | |||
710 | static int tegra_sor_power_down(struct tegra_sor *sor) | ||
711 | { | ||
712 | unsigned long value, timeout; | ||
713 | int err; | ||
714 | |||
715 | value = tegra_sor_readl(sor, SOR_PWR); | ||
716 | value &= ~SOR_PWR_NORMAL_STATE_PU; | ||
717 | value |= SOR_PWR_TRIGGER; | ||
718 | tegra_sor_writel(sor, value, SOR_PWR); | ||
719 | |||
720 | timeout = jiffies + msecs_to_jiffies(250); | ||
721 | |||
722 | while (time_before(jiffies, timeout)) { | ||
723 | value = tegra_sor_readl(sor, SOR_PWR); | ||
724 | if ((value & SOR_PWR_TRIGGER) == 0) | ||
725 | return 0; | ||
726 | |||
727 | usleep_range(25, 100); | ||
728 | } | ||
729 | |||
730 | if ((value & SOR_PWR_TRIGGER) != 0) | ||
731 | return -ETIMEDOUT; | ||
732 | |||
733 | err = clk_set_parent(sor->clk, sor->clk_safe); | ||
734 | if (err < 0) | ||
735 | dev_err(sor->dev, "failed to set safe parent clock: %d\n", err); | ||
736 | |||
737 | value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); | ||
738 | value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 | | ||
739 | SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2); | ||
740 | tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); | ||
741 | |||
742 | /* stop lane sequencer */ | ||
743 | value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_DOWN | | ||
744 | SOR_LANE_SEQ_CTL_POWER_STATE_DOWN; | ||
745 | tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL); | ||
746 | |||
747 | timeout = jiffies + msecs_to_jiffies(250); | ||
748 | |||
749 | while (time_before(jiffies, timeout)) { | ||
750 | value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL); | ||
751 | if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0) | ||
752 | break; | ||
753 | |||
754 | usleep_range(25, 100); | ||
755 | } | ||
756 | |||
757 | if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0) | ||
758 | return -ETIMEDOUT; | ||
759 | |||
760 | value = tegra_sor_readl(sor, SOR_PLL_2); | ||
761 | value |= SOR_PLL_2_PORT_POWERDOWN; | ||
762 | tegra_sor_writel(sor, value, SOR_PLL_2); | ||
763 | |||
764 | usleep_range(20, 100); | ||
765 | |||
766 | value = tegra_sor_readl(sor, SOR_PLL_0); | ||
767 | value |= SOR_PLL_0_POWER_OFF; | ||
768 | value |= SOR_PLL_0_VCOPD; | ||
769 | tegra_sor_writel(sor, value, SOR_PLL_0); | ||
770 | |||
771 | value = tegra_sor_readl(sor, SOR_PLL_2); | ||
772 | value |= SOR_PLL_2_SEQ_PLLCAPPD; | ||
773 | value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE; | ||
774 | tegra_sor_writel(sor, value, SOR_PLL_2); | ||
775 | |||
776 | usleep_range(20, 100); | ||
777 | |||
778 | return 0; | ||
779 | } | ||
780 | |||
781 | static int tegra_output_sor_disable(struct tegra_output *output) | ||
782 | { | ||
783 | struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); | ||
784 | struct tegra_sor *sor = to_sor(output); | ||
785 | unsigned long value; | ||
786 | int err; | ||
787 | |||
788 | if (!sor->enabled) | ||
789 | return 0; | ||
790 | |||
791 | err = tegra_sor_detach(sor); | ||
792 | if (err < 0) { | ||
793 | dev_err(sor->dev, "failed to detach SOR: %d\n", err); | ||
794 | return err; | ||
795 | } | ||
796 | |||
797 | tegra_sor_writel(sor, 0, SOR_STATE_1); | ||
798 | tegra_sor_update(sor); | ||
799 | |||
800 | /* | ||
801 | * The following accesses registers of the display controller, so make | ||
802 | * sure it's only executed when the output is attached to one. | ||
803 | */ | ||
804 | if (dc) { | ||
805 | /* | ||
806 | * XXX: We can't do this here because it causes the SOR to go | ||
807 | * into an erroneous state and the output will look scrambled | ||
808 | * the next time it is enabled. Presumably this is because we | ||
809 | * should be doing this only on the next VBLANK. A possible | ||
810 | * solution would be to queue a "power-off" event to trigger | ||
811 | * this code to be run during the next VBLANK. | ||
812 | */ | ||
813 | /* | ||
814 | value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); | ||
815 | value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | | ||
816 | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); | ||
817 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); | ||
818 | */ | ||
819 | |||
820 | value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); | ||
821 | value &= ~DISP_CTRL_MODE_MASK; | ||
822 | tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); | ||
823 | |||
824 | value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); | ||
825 | value &= ~SOR_ENABLE; | ||
826 | tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); | ||
827 | |||
828 | tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); | ||
829 | tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); | ||
830 | } | ||
831 | |||
832 | err = tegra_sor_power_down(sor); | ||
833 | if (err < 0) { | ||
834 | dev_err(sor->dev, "failed to power down SOR: %d\n", err); | ||
835 | return err; | ||
836 | } | ||
837 | |||
838 | if (sor->dpaux) { | ||
839 | err = tegra_dpaux_disable(sor->dpaux); | ||
840 | if (err < 0) { | ||
841 | dev_err(sor->dev, "failed to disable DP: %d\n", err); | ||
842 | return err; | ||
843 | } | ||
844 | } | ||
845 | |||
846 | err = tegra_io_rail_power_off(TEGRA_IO_RAIL_LVDS); | ||
847 | if (err < 0) { | ||
848 | dev_err(sor->dev, "failed to power off I/O rail: %d\n", err); | ||
849 | return err; | ||
850 | } | ||
851 | |||
852 | reset_control_assert(sor->rst); | ||
853 | clk_disable_unprepare(sor->clk); | ||
854 | |||
855 | sor->enabled = false; | ||
856 | |||
857 | return 0; | ||
858 | } | ||
859 | |||
860 | static int tegra_output_sor_setup_clock(struct tegra_output *output, | ||
861 | struct clk *clk, unsigned long pclk) | ||
862 | { | ||
863 | struct tegra_sor *sor = to_sor(output); | ||
864 | int err; | ||
865 | |||
866 | /* round to next MHz */ | ||
867 | pclk = DIV_ROUND_UP(pclk / 2, 1000000) * 1000000; | ||
868 | |||
869 | err = clk_set_parent(clk, sor->clk_parent); | ||
870 | if (err < 0) { | ||
871 | dev_err(sor->dev, "failed to set parent clock: %d\n", err); | ||
872 | return err; | ||
873 | } | ||
874 | |||
875 | err = clk_set_rate(sor->clk_parent, pclk); | ||
876 | if (err < 0) { | ||
877 | dev_err(sor->dev, "failed to set base clock rate to %lu Hz\n", | ||
878 | pclk * 2); | ||
879 | return err; | ||
880 | } | ||
881 | |||
882 | return 0; | ||
883 | } | ||
884 | |||
885 | static int tegra_output_sor_check_mode(struct tegra_output *output, | ||
886 | struct drm_display_mode *mode, | ||
887 | enum drm_mode_status *status) | ||
888 | { | ||
889 | /* | ||
890 | * FIXME: For now, always assume that the mode is okay. | ||
891 | */ | ||
892 | |||
893 | *status = MODE_OK; | ||
894 | |||
895 | return 0; | ||
896 | } | ||
897 | |||
898 | static enum drm_connector_status | ||
899 | tegra_output_sor_detect(struct tegra_output *output) | ||
900 | { | ||
901 | struct tegra_sor *sor = to_sor(output); | ||
902 | |||
903 | if (sor->dpaux) | ||
904 | return tegra_dpaux_detect(sor->dpaux); | ||
905 | |||
906 | return connector_status_unknown; | ||
907 | } | ||
908 | |||
909 | static const struct tegra_output_ops sor_ops = { | ||
910 | .enable = tegra_output_sor_enable, | ||
911 | .disable = tegra_output_sor_disable, | ||
912 | .setup_clock = tegra_output_sor_setup_clock, | ||
913 | .check_mode = tegra_output_sor_check_mode, | ||
914 | .detect = tegra_output_sor_detect, | ||
915 | }; | ||
916 | |||
917 | static int tegra_sor_init(struct host1x_client *client) | ||
918 | { | ||
919 | struct tegra_drm *tegra = dev_get_drvdata(client->parent); | ||
920 | struct tegra_sor *sor = host1x_client_to_sor(client); | ||
921 | int err; | ||
922 | |||
923 | if (!sor->dpaux) | ||
924 | return -ENODEV; | ||
925 | |||
926 | sor->output.type = TEGRA_OUTPUT_EDP; | ||
927 | |||
928 | sor->output.dev = sor->dev; | ||
929 | sor->output.ops = &sor_ops; | ||
930 | |||
931 | err = tegra_output_init(tegra->drm, &sor->output); | ||
932 | if (err < 0) { | ||
933 | dev_err(sor->dev, "output setup failed: %d\n", err); | ||
934 | return err; | ||
935 | } | ||
936 | |||
937 | if (sor->dpaux) { | ||
938 | err = tegra_dpaux_attach(sor->dpaux, &sor->output); | ||
939 | if (err < 0) { | ||
940 | dev_err(sor->dev, "failed to attach DP: %d\n", err); | ||
941 | return err; | ||
942 | } | ||
943 | } | ||
944 | |||
945 | return 0; | ||
946 | } | ||
947 | |||
948 | static int tegra_sor_exit(struct host1x_client *client) | ||
949 | { | ||
950 | struct tegra_sor *sor = host1x_client_to_sor(client); | ||
951 | int err; | ||
952 | |||
953 | err = tegra_output_disable(&sor->output); | ||
954 | if (err < 0) { | ||
955 | dev_err(sor->dev, "output failed to disable: %d\n", err); | ||
956 | return err; | ||
957 | } | ||
958 | |||
959 | if (sor->dpaux) { | ||
960 | err = tegra_dpaux_detach(sor->dpaux); | ||
961 | if (err < 0) { | ||
962 | dev_err(sor->dev, "failed to detach DP: %d\n", err); | ||
963 | return err; | ||
964 | } | ||
965 | } | ||
966 | |||
967 | err = tegra_output_exit(&sor->output); | ||
968 | if (err < 0) { | ||
969 | dev_err(sor->dev, "output cleanup failed: %d\n", err); | ||
970 | return err; | ||
971 | } | ||
972 | |||
973 | return 0; | ||
974 | } | ||
975 | |||
976 | static const struct host1x_client_ops sor_client_ops = { | ||
977 | .init = tegra_sor_init, | ||
978 | .exit = tegra_sor_exit, | ||
979 | }; | ||
980 | |||
981 | static int tegra_sor_probe(struct platform_device *pdev) | ||
982 | { | ||
983 | struct device_node *np; | ||
984 | struct tegra_sor *sor; | ||
985 | struct resource *regs; | ||
986 | int err; | ||
987 | |||
988 | sor = devm_kzalloc(&pdev->dev, sizeof(*sor), GFP_KERNEL); | ||
989 | if (!sor) | ||
990 | return -ENOMEM; | ||
991 | |||
992 | sor->output.dev = sor->dev = &pdev->dev; | ||
993 | |||
994 | np = of_parse_phandle(pdev->dev.of_node, "nvidia,dpaux", 0); | ||
995 | if (np) { | ||
996 | sor->dpaux = tegra_dpaux_find_by_of_node(np); | ||
997 | of_node_put(np); | ||
998 | |||
999 | if (!sor->dpaux) | ||
1000 | return -EPROBE_DEFER; | ||
1001 | } | ||
1002 | |||
1003 | err = tegra_output_probe(&sor->output); | ||
1004 | if (err < 0) | ||
1005 | return err; | ||
1006 | |||
1007 | regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1008 | sor->regs = devm_ioremap_resource(&pdev->dev, regs); | ||
1009 | if (IS_ERR(sor->regs)) | ||
1010 | return PTR_ERR(sor->regs); | ||
1011 | |||
1012 | sor->rst = devm_reset_control_get(&pdev->dev, "sor"); | ||
1013 | if (IS_ERR(sor->rst)) | ||
1014 | return PTR_ERR(sor->rst); | ||
1015 | |||
1016 | sor->clk = devm_clk_get(&pdev->dev, NULL); | ||
1017 | if (IS_ERR(sor->clk)) | ||
1018 | return PTR_ERR(sor->clk); | ||
1019 | |||
1020 | sor->clk_parent = devm_clk_get(&pdev->dev, "parent"); | ||
1021 | if (IS_ERR(sor->clk_parent)) | ||
1022 | return PTR_ERR(sor->clk_parent); | ||
1023 | |||
1024 | err = clk_prepare_enable(sor->clk_parent); | ||
1025 | if (err < 0) | ||
1026 | return err; | ||
1027 | |||
1028 | sor->clk_safe = devm_clk_get(&pdev->dev, "safe"); | ||
1029 | if (IS_ERR(sor->clk_safe)) | ||
1030 | return PTR_ERR(sor->clk_safe); | ||
1031 | |||
1032 | err = clk_prepare_enable(sor->clk_safe); | ||
1033 | if (err < 0) | ||
1034 | return err; | ||
1035 | |||
1036 | sor->clk_dp = devm_clk_get(&pdev->dev, "dp"); | ||
1037 | if (IS_ERR(sor->clk_dp)) | ||
1038 | return PTR_ERR(sor->clk_dp); | ||
1039 | |||
1040 | err = clk_prepare_enable(sor->clk_dp); | ||
1041 | if (err < 0) | ||
1042 | return err; | ||
1043 | |||
1044 | INIT_LIST_HEAD(&sor->client.list); | ||
1045 | sor->client.ops = &sor_client_ops; | ||
1046 | sor->client.dev = &pdev->dev; | ||
1047 | |||
1048 | err = host1x_client_register(&sor->client); | ||
1049 | if (err < 0) { | ||
1050 | dev_err(&pdev->dev, "failed to register host1x client: %d\n", | ||
1051 | err); | ||
1052 | return err; | ||
1053 | } | ||
1054 | |||
1055 | platform_set_drvdata(pdev, sor); | ||
1056 | |||
1057 | return 0; | ||
1058 | } | ||
1059 | |||
1060 | static int tegra_sor_remove(struct platform_device *pdev) | ||
1061 | { | ||
1062 | struct tegra_sor *sor = platform_get_drvdata(pdev); | ||
1063 | int err; | ||
1064 | |||
1065 | err = host1x_client_unregister(&sor->client); | ||
1066 | if (err < 0) { | ||
1067 | dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", | ||
1068 | err); | ||
1069 | return err; | ||
1070 | } | ||
1071 | |||
1072 | clk_disable_unprepare(sor->clk_parent); | ||
1073 | clk_disable_unprepare(sor->clk_safe); | ||
1074 | clk_disable_unprepare(sor->clk_dp); | ||
1075 | clk_disable_unprepare(sor->clk); | ||
1076 | |||
1077 | return 0; | ||
1078 | } | ||
1079 | |||
1080 | static const struct of_device_id tegra_sor_of_match[] = { | ||
1081 | { .compatible = "nvidia,tegra124-sor", }, | ||
1082 | { }, | ||
1083 | }; | ||
1084 | |||
1085 | struct platform_driver tegra_sor_driver = { | ||
1086 | .driver = { | ||
1087 | .name = "tegra-sor", | ||
1088 | .of_match_table = tegra_sor_of_match, | ||
1089 | }, | ||
1090 | .probe = tegra_sor_probe, | ||
1091 | .remove = tegra_sor_remove, | ||
1092 | }; | ||
diff --git a/drivers/gpu/drm/tegra/sor.h b/drivers/gpu/drm/tegra/sor.h new file mode 100644 index 000000000000..f4156d54cd05 --- /dev/null +++ b/drivers/gpu/drm/tegra/sor.h | |||
@@ -0,0 +1,278 @@ | |||
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 | #ifndef DRM_TEGRA_SOR_H | ||
10 | #define DRM_TEGRA_SOR_H | ||
11 | |||
12 | #define SOR_CTXSW 0x00 | ||
13 | |||
14 | #define SOR_SUPER_STATE_0 0x01 | ||
15 | |||
16 | #define SOR_SUPER_STATE_1 0x02 | ||
17 | #define SOR_SUPER_STATE_ATTACHED (1 << 3) | ||
18 | #define SOR_SUPER_STATE_MODE_NORMAL (1 << 2) | ||
19 | #define SOR_SUPER_STATE_HEAD_MODE_MASK (3 << 0) | ||
20 | #define SOR_SUPER_STATE_HEAD_MODE_AWAKE (2 << 0) | ||
21 | #define SOR_SUPER_STATE_HEAD_MODE_SNOOZE (1 << 0) | ||
22 | #define SOR_SUPER_STATE_HEAD_MODE_SLEEP (0 << 0) | ||
23 | |||
24 | #define SOR_STATE_0 0x03 | ||
25 | |||
26 | #define SOR_STATE_1 0x04 | ||
27 | #define SOR_STATE_ASY_PIXELDEPTH_MASK (0xf << 17) | ||
28 | #define SOR_STATE_ASY_PIXELDEPTH_BPP_18_444 (0x2 << 17) | ||
29 | #define SOR_STATE_ASY_PIXELDEPTH_BPP_24_444 (0x5 << 17) | ||
30 | #define SOR_STATE_ASY_VSYNCPOL (1 << 13) | ||
31 | #define SOR_STATE_ASY_HSYNCPOL (1 << 12) | ||
32 | #define SOR_STATE_ASY_PROTOCOL_MASK (0xf << 8) | ||
33 | #define SOR_STATE_ASY_PROTOCOL_CUSTOM (0xf << 8) | ||
34 | #define SOR_STATE_ASY_PROTOCOL_DP_A (0x8 << 8) | ||
35 | #define SOR_STATE_ASY_PROTOCOL_DP_B (0x9 << 8) | ||
36 | #define SOR_STATE_ASY_PROTOCOL_LVDS (0x0 << 8) | ||
37 | #define SOR_STATE_ASY_CRC_MODE_MASK (0x3 << 6) | ||
38 | #define SOR_STATE_ASY_CRC_MODE_NON_ACTIVE (0x2 << 6) | ||
39 | #define SOR_STATE_ASY_CRC_MODE_COMPLETE (0x1 << 6) | ||
40 | #define SOR_STATE_ASY_CRC_MODE_ACTIVE (0x0 << 6) | ||
41 | #define SOR_STATE_ASY_OWNER(x) (((x) & 0xf) << 0) | ||
42 | |||
43 | #define SOR_HEAD_STATE_0(x) (0x05 + (x)) | ||
44 | #define SOR_HEAD_STATE_1(x) (0x07 + (x)) | ||
45 | #define SOR_HEAD_STATE_2(x) (0x09 + (x)) | ||
46 | #define SOR_HEAD_STATE_3(x) (0x0b + (x)) | ||
47 | #define SOR_HEAD_STATE_4(x) (0x0d + (x)) | ||
48 | #define SOR_HEAD_STATE_5(x) (0x0f + (x)) | ||
49 | #define SOR_CRC_CNTRL 0x11 | ||
50 | #define SOR_DP_DEBUG_MVID 0x12 | ||
51 | |||
52 | #define SOR_CLK_CNTRL 0x13 | ||
53 | #define SOR_CLK_CNTRL_DP_LINK_SPEED_MASK (0x1f << 2) | ||
54 | #define SOR_CLK_CNTRL_DP_LINK_SPEED(x) (((x) & 0x1f) << 2) | ||
55 | #define SOR_CLK_CNTRL_DP_LINK_SPEED_G1_62 (0x06 << 2) | ||
56 | #define SOR_CLK_CNTRL_DP_LINK_SPEED_G2_70 (0x0a << 2) | ||
57 | #define SOR_CLK_CNTRL_DP_LINK_SPEED_G5_40 (0x14 << 2) | ||
58 | #define SOR_CLK_CNTRL_DP_CLK_SEL_MASK (3 << 0) | ||
59 | #define SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_PCLK (0 << 0) | ||
60 | #define SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_PCLK (1 << 0) | ||
61 | #define SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK (2 << 0) | ||
62 | #define SOR_CLK_CNTRL_DP_CLK_SEL_DIFF_DPCLK (3 << 0) | ||
63 | |||
64 | #define SOR_CAP 0x14 | ||
65 | |||
66 | #define SOR_PWR 0x15 | ||
67 | #define SOR_PWR_TRIGGER (1 << 31) | ||
68 | #define SOR_PWR_MODE_SAFE (1 << 28) | ||
69 | #define SOR_PWR_NORMAL_STATE_PU (1 << 0) | ||
70 | |||
71 | #define SOR_TEST 0x16 | ||
72 | #define SOR_TEST_ATTACHED (1 << 10) | ||
73 | #define SOR_TEST_HEAD_MODE_MASK (3 << 8) | ||
74 | #define SOR_TEST_HEAD_MODE_AWAKE (2 << 8) | ||
75 | |||
76 | #define SOR_PLL_0 0x17 | ||
77 | #define SOR_PLL_0_ICHPMP_MASK (0xf << 24) | ||
78 | #define SOR_PLL_0_ICHPMP(x) (((x) & 0xf) << 24) | ||
79 | #define SOR_PLL_0_VCOCAP_MASK (0xf << 8) | ||
80 | #define SOR_PLL_0_VCOCAP(x) (((x) & 0xf) << 8) | ||
81 | #define SOR_PLL_0_VCOCAP_RST SOR_PLL_0_VCOCAP(3) | ||
82 | #define SOR_PLL_0_PLLREG_MASK (0x3 << 6) | ||
83 | #define SOR_PLL_0_PLLREG_LEVEL(x) (((x) & 0x3) << 6) | ||
84 | #define SOR_PLL_0_PLLREG_LEVEL_V25 SOR_PLL_0_PLLREG_LEVEL(0) | ||
85 | #define SOR_PLL_0_PLLREG_LEVEL_V15 SOR_PLL_0_PLLREG_LEVEL(1) | ||
86 | #define SOR_PLL_0_PLLREG_LEVEL_V35 SOR_PLL_0_PLLREG_LEVEL(2) | ||
87 | #define SOR_PLL_0_PLLREG_LEVEL_V45 SOR_PLL_0_PLLREG_LEVEL(3) | ||
88 | #define SOR_PLL_0_PULLDOWN (1 << 5) | ||
89 | #define SOR_PLL_0_RESISTOR_EXT (1 << 4) | ||
90 | #define SOR_PLL_0_VCOPD (1 << 2) | ||
91 | #define SOR_PLL_0_POWER_OFF (1 << 0) | ||
92 | |||
93 | #define SOR_PLL_1 0x18 | ||
94 | /* XXX: read-only bit? */ | ||
95 | #define SOR_PLL_1_TERM_COMPOUT (1 << 15) | ||
96 | #define SOR_PLL_1_TMDS_TERM (1 << 8) | ||
97 | |||
98 | #define SOR_PLL_2 0x19 | ||
99 | #define SOR_PLL_2_LVDS_ENABLE (1 << 25) | ||
100 | #define SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE (1 << 24) | ||
101 | #define SOR_PLL_2_PORT_POWERDOWN (1 << 23) | ||
102 | #define SOR_PLL_2_BANDGAP_POWERDOWN (1 << 22) | ||
103 | #define SOR_PLL_2_POWERDOWN_OVERRIDE (1 << 18) | ||
104 | #define SOR_PLL_2_SEQ_PLLCAPPD (1 << 17) | ||
105 | |||
106 | #define SOR_PLL_3 0x1a | ||
107 | #define SOR_PLL_3_PLL_VDD_MODE_V1_8 (0 << 13) | ||
108 | #define SOR_PLL_3_PLL_VDD_MODE_V3_3 (1 << 13) | ||
109 | |||
110 | #define SOR_CSTM 0x1b | ||
111 | #define SOR_CSTM_LVDS (1 << 16) | ||
112 | #define SOR_CSTM_LINK_ACT_B (1 << 15) | ||
113 | #define SOR_CSTM_LINK_ACT_A (1 << 14) | ||
114 | #define SOR_CSTM_UPPER (1 << 11) | ||
115 | |||
116 | #define SOR_LVDS 0x1c | ||
117 | #define SOR_CRC_A 0x1d | ||
118 | #define SOR_CRC_B 0x1e | ||
119 | #define SOR_BLANK 0x1f | ||
120 | #define SOR_SEQ_CTL 0x20 | ||
121 | |||
122 | #define SOR_LANE_SEQ_CTL 0x21 | ||
123 | #define SOR_LANE_SEQ_CTL_TRIGGER (1 << 31) | ||
124 | #define SOR_LANE_SEQ_CTL_SEQUENCE_UP (0 << 20) | ||
125 | #define SOR_LANE_SEQ_CTL_SEQUENCE_DOWN (1 << 20) | ||
126 | #define SOR_LANE_SEQ_CTL_POWER_STATE_UP (0 << 16) | ||
127 | #define SOR_LANE_SEQ_CTL_POWER_STATE_DOWN (1 << 16) | ||
128 | |||
129 | #define SOR_SEQ_INST(x) (0x22 + (x)) | ||
130 | |||
131 | #define SOR_PWM_DIV 0x32 | ||
132 | #define SOR_PWM_DIV_MASK 0xffffff | ||
133 | |||
134 | #define SOR_PWM_CTL 0x33 | ||
135 | #define SOR_PWM_CTL_TRIGGER (1 << 31) | ||
136 | #define SOR_PWM_CTL_CLK_SEL (1 << 30) | ||
137 | #define SOR_PWM_CTL_DUTY_CYCLE_MASK 0xffffff | ||
138 | |||
139 | #define SOR_VCRC_A_0 0x34 | ||
140 | #define SOR_VCRC_A_1 0x35 | ||
141 | #define SOR_VCRC_B_0 0x36 | ||
142 | #define SOR_VCRC_B_1 0x37 | ||
143 | #define SOR_CCRC_A_0 0x38 | ||
144 | #define SOR_CCRC_A_1 0x39 | ||
145 | #define SOR_CCRC_B_0 0x3a | ||
146 | #define SOR_CCRC_B_1 0x3b | ||
147 | #define SOR_EDATA_A_0 0x3c | ||
148 | #define SOR_EDATA_A_1 0x3d | ||
149 | #define SOR_EDATA_B_0 0x3e | ||
150 | #define SOR_EDATA_B_1 0x3f | ||
151 | #define SOR_COUNT_A_0 0x40 | ||
152 | #define SOR_COUNT_A_1 0x41 | ||
153 | #define SOR_COUNT_B_0 0x42 | ||
154 | #define SOR_COUNT_B_1 0x43 | ||
155 | #define SOR_DEBUG_A_0 0x44 | ||
156 | #define SOR_DEBUG_A_1 0x45 | ||
157 | #define SOR_DEBUG_B_0 0x46 | ||
158 | #define SOR_DEBUG_B_1 0x47 | ||
159 | #define SOR_TRIG 0x48 | ||
160 | #define SOR_MSCHECK 0x49 | ||
161 | #define SOR_XBAR_CTRL 0x4a | ||
162 | #define SOR_XBAR_POL 0x4b | ||
163 | |||
164 | #define SOR_DP_LINKCTL_0 0x4c | ||
165 | #define SOR_DP_LINKCTL_LANE_COUNT_MASK (0x1f << 16) | ||
166 | #define SOR_DP_LINKCTL_LANE_COUNT(x) (((1 << (x)) - 1) << 16) | ||
167 | #define SOR_DP_LINKCTL_ENHANCED_FRAME (1 << 14) | ||
168 | #define SOR_DP_LINKCTL_TU_SIZE_MASK (0x7f << 2) | ||
169 | #define SOR_DP_LINKCTL_TU_SIZE(x) (((x) & 0x7f) << 2) | ||
170 | #define SOR_DP_LINKCTL_ENABLE (1 << 0) | ||
171 | |||
172 | #define SOR_DP_LINKCTL_1 0x4d | ||
173 | |||
174 | #define SOR_LANE_DRIVE_CURRENT_0 0x4e | ||
175 | #define SOR_LANE_DRIVE_CURRENT_1 0x4f | ||
176 | #define SOR_LANE4_DRIVE_CURRENT_0 0x50 | ||
177 | #define SOR_LANE4_DRIVE_CURRENT_1 0x51 | ||
178 | #define SOR_LANE_DRIVE_CURRENT_LANE3(x) (((x) & 0xff) << 24) | ||
179 | #define SOR_LANE_DRIVE_CURRENT_LANE2(x) (((x) & 0xff) << 16) | ||
180 | #define SOR_LANE_DRIVE_CURRENT_LANE1(x) (((x) & 0xff) << 8) | ||
181 | #define SOR_LANE_DRIVE_CURRENT_LANE0(x) (((x) & 0xff) << 0) | ||
182 | |||
183 | #define SOR_LANE_PREEMPHASIS_0 0x52 | ||
184 | #define SOR_LANE_PREEMPHASIS_1 0x53 | ||
185 | #define SOR_LANE4_PREEMPHASIS_0 0x54 | ||
186 | #define SOR_LANE4_PREEMPHASIS_1 0x55 | ||
187 | #define SOR_LANE_PREEMPHASIS_LANE3(x) (((x) & 0xff) << 24) | ||
188 | #define SOR_LANE_PREEMPHASIS_LANE2(x) (((x) & 0xff) << 16) | ||
189 | #define SOR_LANE_PREEMPHASIS_LANE1(x) (((x) & 0xff) << 8) | ||
190 | #define SOR_LANE_PREEMPHASIS_LANE0(x) (((x) & 0xff) << 0) | ||
191 | |||
192 | #define SOR_LANE_POST_CURSOR_0 0x56 | ||
193 | #define SOR_LANE_POST_CURSOR_1 0x57 | ||
194 | #define SOR_LANE_POST_CURSOR_LANE3(x) (((x) & 0xff) << 24) | ||
195 | #define SOR_LANE_POST_CURSOR_LANE2(x) (((x) & 0xff) << 16) | ||
196 | #define SOR_LANE_POST_CURSOR_LANE1(x) (((x) & 0xff) << 8) | ||
197 | #define SOR_LANE_POST_CURSOR_LANE0(x) (((x) & 0xff) << 0) | ||
198 | |||
199 | #define SOR_DP_CONFIG_0 0x58 | ||
200 | #define SOR_DP_CONFIG_DISPARITY_NEGATIVE (1 << 31) | ||
201 | #define SOR_DP_CONFIG_ACTIVE_SYM_ENABLE (1 << 26) | ||
202 | #define SOR_DP_CONFIG_ACTIVE_SYM_POLARITY (1 << 24) | ||
203 | #define SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK (0xf << 16) | ||
204 | #define SOR_DP_CONFIG_ACTIVE_SYM_FRAC(x) (((x) & 0xf) << 16) | ||
205 | #define SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK (0x7f << 8) | ||
206 | #define SOR_DP_CONFIG_ACTIVE_SYM_COUNT(x) (((x) & 0x7f) << 8) | ||
207 | #define SOR_DP_CONFIG_WATERMARK_MASK (0x3f << 0) | ||
208 | #define SOR_DP_CONFIG_WATERMARK(x) (((x) & 0x3f) << 0) | ||
209 | |||
210 | #define SOR_DP_CONFIG_1 0x59 | ||
211 | #define SOR_DP_MN_0 0x5a | ||
212 | #define SOR_DP_MN_1 0x5b | ||
213 | |||
214 | #define SOR_DP_PADCTL_0 0x5c | ||
215 | #define SOR_DP_PADCTL_PAD_CAL_PD (1 << 23) | ||
216 | #define SOR_DP_PADCTL_TX_PU_ENABLE (1 << 22) | ||
217 | #define SOR_DP_PADCTL_TX_PU_MASK (0xff << 8) | ||
218 | #define SOR_DP_PADCTL_TX_PU(x) (((x) & 0xff) << 8) | ||
219 | #define SOR_DP_PADCTL_CM_TXD_3 (1 << 7) | ||
220 | #define SOR_DP_PADCTL_CM_TXD_2 (1 << 6) | ||
221 | #define SOR_DP_PADCTL_CM_TXD_1 (1 << 5) | ||
222 | #define SOR_DP_PADCTL_CM_TXD_0 (1 << 4) | ||
223 | #define SOR_DP_PADCTL_PD_TXD_3 (1 << 3) | ||
224 | #define SOR_DP_PADCTL_PD_TXD_0 (1 << 2) | ||
225 | #define SOR_DP_PADCTL_PD_TXD_1 (1 << 1) | ||
226 | #define SOR_DP_PADCTL_PD_TXD_2 (1 << 0) | ||
227 | |||
228 | #define SOR_DP_PADCTL_1 0x5d | ||
229 | |||
230 | #define SOR_DP_DEBUG_0 0x5e | ||
231 | #define SOR_DP_DEBUG_1 0x5f | ||
232 | |||
233 | #define SOR_DP_SPARE_0 0x60 | ||
234 | #define SOR_DP_SPARE_MACRO_SOR_CLK (1 << 2) | ||
235 | #define SOR_DP_SPARE_PANEL_INTERNAL (1 << 1) | ||
236 | #define SOR_DP_SPARE_SEQ_ENABLE (1 << 0) | ||
237 | |||
238 | #define SOR_DP_SPARE_1 0x61 | ||
239 | #define SOR_DP_AUDIO_CTRL 0x62 | ||
240 | |||
241 | #define SOR_DP_AUDIO_HBLANK_SYMBOLS 0x63 | ||
242 | #define SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK (0x01ffff << 0) | ||
243 | |||
244 | #define SOR_DP_AUDIO_VBLANK_SYMBOLS 0x64 | ||
245 | #define SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK (0x1fffff << 0) | ||
246 | |||
247 | #define SOR_DP_GENERIC_INFOFRAME_HEADER 0x65 | ||
248 | #define SOR_DP_GENERIC_INFOFRAME_SUBPACK_0 0x66 | ||
249 | #define SOR_DP_GENERIC_INFOFRAME_SUBPACK_1 0x67 | ||
250 | #define SOR_DP_GENERIC_INFOFRAME_SUBPACK_2 0x68 | ||
251 | #define SOR_DP_GENERIC_INFOFRAME_SUBPACK_3 0x69 | ||
252 | #define SOR_DP_GENERIC_INFOFRAME_SUBPACK_4 0x6a | ||
253 | #define SOR_DP_GENERIC_INFOFRAME_SUBPACK_5 0x6b | ||
254 | #define SOR_DP_GENERIC_INFOFRAME_SUBPACK_6 0x6c | ||
255 | |||
256 | #define SOR_DP_TPG 0x6d | ||
257 | #define SOR_DP_TPG_CHANNEL_CODING (1 << 6) | ||
258 | #define SOR_DP_TPG_SCRAMBLER_MASK (3 << 4) | ||
259 | #define SOR_DP_TPG_SCRAMBLER_FIBONACCI (2 << 4) | ||
260 | #define SOR_DP_TPG_SCRAMBLER_GALIOS (1 << 4) | ||
261 | #define SOR_DP_TPG_SCRAMBLER_NONE (0 << 4) | ||
262 | #define SOR_DP_TPG_PATTERN_MASK (0xf << 0) | ||
263 | #define SOR_DP_TPG_PATTERN_HBR2 (0x8 << 0) | ||
264 | #define SOR_DP_TPG_PATTERN_CSTM (0x7 << 0) | ||
265 | #define SOR_DP_TPG_PATTERN_PRBS7 (0x6 << 0) | ||
266 | #define SOR_DP_TPG_PATTERN_SBLERRRATE (0x5 << 0) | ||
267 | #define SOR_DP_TPG_PATTERN_D102 (0x4 << 0) | ||
268 | #define SOR_DP_TPG_PATTERN_TRAIN3 (0x3 << 0) | ||
269 | #define SOR_DP_TPG_PATTERN_TRAIN2 (0x2 << 0) | ||
270 | #define SOR_DP_TPG_PATTERN_TRAIN1 (0x1 << 0) | ||
271 | #define SOR_DP_TPG_PATTERN_NONE (0x0 << 0) | ||
272 | |||
273 | #define SOR_DP_TPG_CONFIG 0x6e | ||
274 | #define SOR_DP_LQ_CSTM_0 0x6f | ||
275 | #define SOR_DP_LQ_CSTM_1 0x70 | ||
276 | #define SOR_DP_LQ_CSTM_2 0x71 | ||
277 | |||
278 | #endif | ||