1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2013 NVIDIA Corporation |
4 | */ |
5 | |
6 | #include <linux/clk.h> |
7 | #include <linux/delay.h> |
8 | #include <linux/interrupt.h> |
9 | #include <linux/io.h> |
10 | #include <linux/module.h> |
11 | #include <linux/of.h> |
12 | #include <linux/pinctrl/pinconf-generic.h> |
13 | #include <linux/pinctrl/pinctrl.h> |
14 | #include <linux/pinctrl/pinmux.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/pm_runtime.h> |
17 | #include <linux/regulator/consumer.h> |
18 | #include <linux/reset.h> |
19 | #include <linux/workqueue.h> |
20 | |
21 | #include <drm/display/drm_dp_helper.h> |
22 | #include <drm/display/drm_dp_aux_bus.h> |
23 | #include <drm/drm_panel.h> |
24 | |
25 | #include "dp.h" |
26 | #include "dpaux.h" |
27 | #include "drm.h" |
28 | #include "trace.h" |
29 | |
30 | static DEFINE_MUTEX(dpaux_lock); |
31 | static LIST_HEAD(dpaux_list); |
32 | |
33 | struct tegra_dpaux_soc { |
34 | unsigned int cmh; |
35 | unsigned int drvz; |
36 | unsigned int drvi; |
37 | }; |
38 | |
39 | struct tegra_dpaux { |
40 | struct drm_dp_aux aux; |
41 | struct device *dev; |
42 | |
43 | const struct tegra_dpaux_soc *soc; |
44 | |
45 | void __iomem *regs; |
46 | int irq; |
47 | |
48 | struct tegra_output *output; |
49 | |
50 | struct reset_control *rst; |
51 | struct clk *clk_parent; |
52 | struct clk *clk; |
53 | |
54 | struct regulator *vdd; |
55 | |
56 | struct completion complete; |
57 | struct work_struct work; |
58 | struct list_head list; |
59 | |
60 | #ifdef CONFIG_GENERIC_PINCONF |
61 | struct pinctrl_dev *pinctrl; |
62 | struct pinctrl_desc desc; |
63 | #endif |
64 | }; |
65 | |
66 | static inline struct tegra_dpaux *to_dpaux(struct drm_dp_aux *aux) |
67 | { |
68 | return container_of(aux, struct tegra_dpaux, aux); |
69 | } |
70 | |
71 | static inline struct tegra_dpaux *work_to_dpaux(struct work_struct *work) |
72 | { |
73 | return container_of(work, struct tegra_dpaux, work); |
74 | } |
75 | |
76 | static inline u32 tegra_dpaux_readl(struct tegra_dpaux *dpaux, |
77 | unsigned int offset) |
78 | { |
79 | u32 value = readl(addr: dpaux->regs + (offset << 2)); |
80 | |
81 | trace_dpaux_readl(dev: dpaux->dev, offset, value); |
82 | |
83 | return value; |
84 | } |
85 | |
86 | static inline void tegra_dpaux_writel(struct tegra_dpaux *dpaux, |
87 | u32 value, unsigned int offset) |
88 | { |
89 | trace_dpaux_writel(dev: dpaux->dev, offset, value); |
90 | writel(val: value, addr: dpaux->regs + (offset << 2)); |
91 | } |
92 | |
93 | static void tegra_dpaux_write_fifo(struct tegra_dpaux *dpaux, const u8 *buffer, |
94 | size_t size) |
95 | { |
96 | size_t i, j; |
97 | |
98 | for (i = 0; i < DIV_ROUND_UP(size, 4); i++) { |
99 | size_t num = min_t(size_t, size - i * 4, 4); |
100 | u32 value = 0; |
101 | |
102 | for (j = 0; j < num; j++) |
103 | value |= buffer[i * 4 + j] << (j * 8); |
104 | |
105 | tegra_dpaux_writel(dpaux, value, DPAUX_DP_AUXDATA_WRITE(i)); |
106 | } |
107 | } |
108 | |
109 | static void tegra_dpaux_read_fifo(struct tegra_dpaux *dpaux, u8 *buffer, |
110 | size_t size) |
111 | { |
112 | size_t i, j; |
113 | |
114 | for (i = 0; i < DIV_ROUND_UP(size, 4); i++) { |
115 | size_t num = min_t(size_t, size - i * 4, 4); |
116 | u32 value; |
117 | |
118 | value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXDATA_READ(i)); |
119 | |
120 | for (j = 0; j < num; j++) |
121 | buffer[i * 4 + j] = value >> (j * 8); |
122 | } |
123 | } |
124 | |
125 | static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux, |
126 | struct drm_dp_aux_msg *msg) |
127 | { |
128 | unsigned long timeout = msecs_to_jiffies(m: 250); |
129 | struct tegra_dpaux *dpaux = to_dpaux(aux); |
130 | unsigned long status; |
131 | ssize_t ret = 0; |
132 | u8 reply = 0; |
133 | u32 value; |
134 | |
135 | /* Tegra has 4x4 byte DP AUX transmit and receive FIFOs. */ |
136 | if (msg->size > 16) |
137 | return -EINVAL; |
138 | |
139 | /* |
140 | * Allow zero-sized messages only for I2C, in which case they specify |
141 | * address-only transactions. |
142 | */ |
143 | if (msg->size < 1) { |
144 | switch (msg->request & ~DP_AUX_I2C_MOT) { |
145 | case DP_AUX_I2C_WRITE_STATUS_UPDATE: |
146 | case DP_AUX_I2C_WRITE: |
147 | case DP_AUX_I2C_READ: |
148 | value = DPAUX_DP_AUXCTL_CMD_ADDRESS_ONLY; |
149 | break; |
150 | |
151 | default: |
152 | return -EINVAL; |
153 | } |
154 | } else { |
155 | /* For non-zero-sized messages, set the CMDLEN field. */ |
156 | value = DPAUX_DP_AUXCTL_CMDLEN(msg->size - 1); |
157 | } |
158 | |
159 | switch (msg->request & ~DP_AUX_I2C_MOT) { |
160 | case DP_AUX_I2C_WRITE: |
161 | if (msg->request & DP_AUX_I2C_MOT) |
162 | value |= DPAUX_DP_AUXCTL_CMD_MOT_WR; |
163 | else |
164 | value |= DPAUX_DP_AUXCTL_CMD_I2C_WR; |
165 | |
166 | break; |
167 | |
168 | case DP_AUX_I2C_READ: |
169 | if (msg->request & DP_AUX_I2C_MOT) |
170 | value |= DPAUX_DP_AUXCTL_CMD_MOT_RD; |
171 | else |
172 | value |= DPAUX_DP_AUXCTL_CMD_I2C_RD; |
173 | |
174 | break; |
175 | |
176 | case DP_AUX_I2C_WRITE_STATUS_UPDATE: |
177 | if (msg->request & DP_AUX_I2C_MOT) |
178 | value |= DPAUX_DP_AUXCTL_CMD_MOT_RQ; |
179 | else |
180 | value |= DPAUX_DP_AUXCTL_CMD_I2C_RQ; |
181 | |
182 | break; |
183 | |
184 | case DP_AUX_NATIVE_WRITE: |
185 | value |= DPAUX_DP_AUXCTL_CMD_AUX_WR; |
186 | break; |
187 | |
188 | case DP_AUX_NATIVE_READ: |
189 | value |= DPAUX_DP_AUXCTL_CMD_AUX_RD; |
190 | break; |
191 | |
192 | default: |
193 | return -EINVAL; |
194 | } |
195 | |
196 | tegra_dpaux_writel(dpaux, value: msg->address, DPAUX_DP_AUXADDR); |
197 | tegra_dpaux_writel(dpaux, value, DPAUX_DP_AUXCTL); |
198 | |
199 | if ((msg->request & DP_AUX_I2C_READ) == 0) { |
200 | tegra_dpaux_write_fifo(dpaux, buffer: msg->buffer, size: msg->size); |
201 | ret = msg->size; |
202 | } |
203 | |
204 | /* start transaction */ |
205 | value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXCTL); |
206 | value |= DPAUX_DP_AUXCTL_TRANSACTREQ; |
207 | tegra_dpaux_writel(dpaux, value, DPAUX_DP_AUXCTL); |
208 | |
209 | status = wait_for_completion_timeout(x: &dpaux->complete, timeout); |
210 | if (!status) |
211 | return -ETIMEDOUT; |
212 | |
213 | /* read status and clear errors */ |
214 | value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXSTAT); |
215 | tegra_dpaux_writel(dpaux, value: 0xf00, DPAUX_DP_AUXSTAT); |
216 | |
217 | if (value & DPAUX_DP_AUXSTAT_TIMEOUT_ERROR) |
218 | return -ETIMEDOUT; |
219 | |
220 | if ((value & DPAUX_DP_AUXSTAT_RX_ERROR) || |
221 | (value & DPAUX_DP_AUXSTAT_SINKSTAT_ERROR) || |
222 | (value & DPAUX_DP_AUXSTAT_NO_STOP_ERROR)) |
223 | return -EIO; |
224 | |
225 | switch ((value & DPAUX_DP_AUXSTAT_REPLY_TYPE_MASK) >> 16) { |
226 | case 0x00: |
227 | reply = DP_AUX_NATIVE_REPLY_ACK; |
228 | break; |
229 | |
230 | case 0x01: |
231 | reply = DP_AUX_NATIVE_REPLY_NACK; |
232 | break; |
233 | |
234 | case 0x02: |
235 | reply = DP_AUX_NATIVE_REPLY_DEFER; |
236 | break; |
237 | |
238 | case 0x04: |
239 | reply = DP_AUX_I2C_REPLY_NACK; |
240 | break; |
241 | |
242 | case 0x08: |
243 | reply = DP_AUX_I2C_REPLY_DEFER; |
244 | break; |
245 | } |
246 | |
247 | if ((msg->size > 0) && (msg->reply == DP_AUX_NATIVE_REPLY_ACK)) { |
248 | if (msg->request & DP_AUX_I2C_READ) { |
249 | size_t count = value & DPAUX_DP_AUXSTAT_REPLY_MASK; |
250 | |
251 | /* |
252 | * There might be a smarter way to do this, but since |
253 | * the DP helpers will already retry transactions for |
254 | * an -EBUSY return value, simply reuse that instead. |
255 | */ |
256 | if (count != msg->size) { |
257 | ret = -EBUSY; |
258 | goto out; |
259 | } |
260 | |
261 | tegra_dpaux_read_fifo(dpaux, buffer: msg->buffer, size: count); |
262 | ret = count; |
263 | } |
264 | } |
265 | |
266 | msg->reply = reply; |
267 | |
268 | out: |
269 | return ret; |
270 | } |
271 | |
272 | static void tegra_dpaux_hotplug(struct work_struct *work) |
273 | { |
274 | struct tegra_dpaux *dpaux = work_to_dpaux(work); |
275 | |
276 | if (dpaux->output) |
277 | drm_helper_hpd_irq_event(dev: dpaux->output->connector.dev); |
278 | } |
279 | |
280 | static irqreturn_t tegra_dpaux_irq(int irq, void *data) |
281 | { |
282 | struct tegra_dpaux *dpaux = data; |
283 | u32 value; |
284 | |
285 | /* clear interrupts */ |
286 | value = tegra_dpaux_readl(dpaux, DPAUX_INTR_AUX); |
287 | tegra_dpaux_writel(dpaux, value, DPAUX_INTR_AUX); |
288 | |
289 | if (value & (DPAUX_INTR_PLUG_EVENT | DPAUX_INTR_UNPLUG_EVENT)) |
290 | schedule_work(work: &dpaux->work); |
291 | |
292 | if (value & DPAUX_INTR_IRQ_EVENT) { |
293 | /* TODO: handle this */ |
294 | } |
295 | |
296 | if (value & DPAUX_INTR_AUX_DONE) |
297 | complete(&dpaux->complete); |
298 | |
299 | return IRQ_HANDLED; |
300 | } |
301 | |
302 | enum tegra_dpaux_functions { |
303 | DPAUX_PADCTL_FUNC_AUX, |
304 | DPAUX_PADCTL_FUNC_I2C, |
305 | DPAUX_PADCTL_FUNC_OFF, |
306 | }; |
307 | |
308 | static void tegra_dpaux_pad_power_down(struct tegra_dpaux *dpaux) |
309 | { |
310 | u32 value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE); |
311 | |
312 | value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN; |
313 | |
314 | tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE); |
315 | } |
316 | |
317 | static void tegra_dpaux_pad_power_up(struct tegra_dpaux *dpaux) |
318 | { |
319 | u32 value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE); |
320 | |
321 | value &= ~DPAUX_HYBRID_SPARE_PAD_POWER_DOWN; |
322 | |
323 | tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE); |
324 | } |
325 | |
326 | static int tegra_dpaux_pad_config(struct tegra_dpaux *dpaux, unsigned function) |
327 | { |
328 | u32 value; |
329 | |
330 | switch (function) { |
331 | case DPAUX_PADCTL_FUNC_AUX: |
332 | value = DPAUX_HYBRID_PADCTL_AUX_CMH(dpaux->soc->cmh) | |
333 | DPAUX_HYBRID_PADCTL_AUX_DRVZ(dpaux->soc->drvz) | |
334 | DPAUX_HYBRID_PADCTL_AUX_DRVI(dpaux->soc->drvi) | |
335 | DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV | |
336 | DPAUX_HYBRID_PADCTL_MODE_AUX; |
337 | break; |
338 | |
339 | case DPAUX_PADCTL_FUNC_I2C: |
340 | value = DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV | |
341 | DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV | |
342 | DPAUX_HYBRID_PADCTL_AUX_CMH(dpaux->soc->cmh) | |
343 | DPAUX_HYBRID_PADCTL_AUX_DRVZ(dpaux->soc->drvz) | |
344 | DPAUX_HYBRID_PADCTL_AUX_DRVI(dpaux->soc->drvi) | |
345 | DPAUX_HYBRID_PADCTL_MODE_I2C; |
346 | break; |
347 | |
348 | case DPAUX_PADCTL_FUNC_OFF: |
349 | tegra_dpaux_pad_power_down(dpaux); |
350 | return 0; |
351 | |
352 | default: |
353 | return -ENOTSUPP; |
354 | } |
355 | |
356 | tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_PADCTL); |
357 | tegra_dpaux_pad_power_up(dpaux); |
358 | |
359 | return 0; |
360 | } |
361 | |
362 | #ifdef CONFIG_GENERIC_PINCONF |
363 | static const struct pinctrl_pin_desc tegra_dpaux_pins[] = { |
364 | PINCTRL_PIN(0, "DP_AUX_CHx_P" ), |
365 | PINCTRL_PIN(1, "DP_AUX_CHx_N" ), |
366 | }; |
367 | |
368 | static const unsigned tegra_dpaux_pin_numbers[] = { 0, 1 }; |
369 | |
370 | static const char * const tegra_dpaux_groups[] = { |
371 | "dpaux-io" , |
372 | }; |
373 | |
374 | static const char * const tegra_dpaux_functions[] = { |
375 | "aux" , |
376 | "i2c" , |
377 | "off" , |
378 | }; |
379 | |
380 | static int tegra_dpaux_get_groups_count(struct pinctrl_dev *pinctrl) |
381 | { |
382 | return ARRAY_SIZE(tegra_dpaux_groups); |
383 | } |
384 | |
385 | static const char *tegra_dpaux_get_group_name(struct pinctrl_dev *pinctrl, |
386 | unsigned int group) |
387 | { |
388 | return tegra_dpaux_groups[group]; |
389 | } |
390 | |
391 | static int tegra_dpaux_get_group_pins(struct pinctrl_dev *pinctrl, |
392 | unsigned group, const unsigned **pins, |
393 | unsigned *num_pins) |
394 | { |
395 | *pins = tegra_dpaux_pin_numbers; |
396 | *num_pins = ARRAY_SIZE(tegra_dpaux_pin_numbers); |
397 | |
398 | return 0; |
399 | } |
400 | |
401 | static const struct pinctrl_ops tegra_dpaux_pinctrl_ops = { |
402 | .get_groups_count = tegra_dpaux_get_groups_count, |
403 | .get_group_name = tegra_dpaux_get_group_name, |
404 | .get_group_pins = tegra_dpaux_get_group_pins, |
405 | .dt_node_to_map = pinconf_generic_dt_node_to_map_group, |
406 | .dt_free_map = pinconf_generic_dt_free_map, |
407 | }; |
408 | |
409 | static int tegra_dpaux_get_functions_count(struct pinctrl_dev *pinctrl) |
410 | { |
411 | return ARRAY_SIZE(tegra_dpaux_functions); |
412 | } |
413 | |
414 | static const char *tegra_dpaux_get_function_name(struct pinctrl_dev *pinctrl, |
415 | unsigned int function) |
416 | { |
417 | return tegra_dpaux_functions[function]; |
418 | } |
419 | |
420 | static int tegra_dpaux_get_function_groups(struct pinctrl_dev *pinctrl, |
421 | unsigned int function, |
422 | const char * const **groups, |
423 | unsigned * const num_groups) |
424 | { |
425 | *num_groups = ARRAY_SIZE(tegra_dpaux_groups); |
426 | *groups = tegra_dpaux_groups; |
427 | |
428 | return 0; |
429 | } |
430 | |
431 | static int tegra_dpaux_set_mux(struct pinctrl_dev *pinctrl, |
432 | unsigned int function, unsigned int group) |
433 | { |
434 | struct tegra_dpaux *dpaux = pinctrl_dev_get_drvdata(pctldev: pinctrl); |
435 | |
436 | return tegra_dpaux_pad_config(dpaux, function); |
437 | } |
438 | |
439 | static const struct pinmux_ops tegra_dpaux_pinmux_ops = { |
440 | .get_functions_count = tegra_dpaux_get_functions_count, |
441 | .get_function_name = tegra_dpaux_get_function_name, |
442 | .get_function_groups = tegra_dpaux_get_function_groups, |
443 | .set_mux = tegra_dpaux_set_mux, |
444 | }; |
445 | #endif |
446 | |
447 | static int tegra_dpaux_probe(struct platform_device *pdev) |
448 | { |
449 | struct tegra_dpaux *dpaux; |
450 | u32 value; |
451 | int err; |
452 | |
453 | dpaux = devm_kzalloc(dev: &pdev->dev, size: sizeof(*dpaux), GFP_KERNEL); |
454 | if (!dpaux) |
455 | return -ENOMEM; |
456 | |
457 | dpaux->soc = of_device_get_match_data(dev: &pdev->dev); |
458 | INIT_WORK(&dpaux->work, tegra_dpaux_hotplug); |
459 | init_completion(x: &dpaux->complete); |
460 | INIT_LIST_HEAD(list: &dpaux->list); |
461 | dpaux->dev = &pdev->dev; |
462 | |
463 | dpaux->regs = devm_platform_ioremap_resource(pdev, index: 0); |
464 | if (IS_ERR(ptr: dpaux->regs)) |
465 | return PTR_ERR(ptr: dpaux->regs); |
466 | |
467 | dpaux->irq = platform_get_irq(pdev, 0); |
468 | if (dpaux->irq < 0) |
469 | return dpaux->irq; |
470 | |
471 | if (!pdev->dev.pm_domain) { |
472 | dpaux->rst = devm_reset_control_get(dev: &pdev->dev, id: "dpaux" ); |
473 | if (IS_ERR(ptr: dpaux->rst)) { |
474 | dev_err(&pdev->dev, |
475 | "failed to get reset control: %ld\n" , |
476 | PTR_ERR(dpaux->rst)); |
477 | return PTR_ERR(ptr: dpaux->rst); |
478 | } |
479 | } |
480 | |
481 | dpaux->clk = devm_clk_get(dev: &pdev->dev, NULL); |
482 | if (IS_ERR(ptr: dpaux->clk)) { |
483 | dev_err(&pdev->dev, "failed to get module clock: %ld\n" , |
484 | PTR_ERR(dpaux->clk)); |
485 | return PTR_ERR(ptr: dpaux->clk); |
486 | } |
487 | |
488 | dpaux->clk_parent = devm_clk_get(dev: &pdev->dev, id: "parent" ); |
489 | if (IS_ERR(ptr: dpaux->clk_parent)) { |
490 | dev_err(&pdev->dev, "failed to get parent clock: %ld\n" , |
491 | PTR_ERR(dpaux->clk_parent)); |
492 | return PTR_ERR(ptr: dpaux->clk_parent); |
493 | } |
494 | |
495 | err = clk_set_rate(clk: dpaux->clk_parent, rate: 270000000); |
496 | if (err < 0) { |
497 | dev_err(&pdev->dev, "failed to set clock to 270 MHz: %d\n" , |
498 | err); |
499 | return err; |
500 | } |
501 | |
502 | dpaux->vdd = devm_regulator_get_optional(dev: &pdev->dev, id: "vdd" ); |
503 | if (IS_ERR(ptr: dpaux->vdd)) { |
504 | if (PTR_ERR(ptr: dpaux->vdd) != -ENODEV) { |
505 | if (PTR_ERR(ptr: dpaux->vdd) != -EPROBE_DEFER) |
506 | dev_err(&pdev->dev, |
507 | "failed to get VDD supply: %ld\n" , |
508 | PTR_ERR(dpaux->vdd)); |
509 | |
510 | return PTR_ERR(ptr: dpaux->vdd); |
511 | } |
512 | |
513 | dpaux->vdd = NULL; |
514 | } |
515 | |
516 | platform_set_drvdata(pdev, data: dpaux); |
517 | pm_runtime_enable(dev: &pdev->dev); |
518 | pm_runtime_get_sync(dev: &pdev->dev); |
519 | |
520 | err = devm_request_irq(dev: dpaux->dev, irq: dpaux->irq, handler: tegra_dpaux_irq, irqflags: 0, |
521 | devname: dev_name(dev: dpaux->dev), dev_id: dpaux); |
522 | if (err < 0) { |
523 | dev_err(dpaux->dev, "failed to request IRQ#%u: %d\n" , |
524 | dpaux->irq, err); |
525 | return err; |
526 | } |
527 | |
528 | disable_irq(irq: dpaux->irq); |
529 | |
530 | dpaux->aux.transfer = tegra_dpaux_transfer; |
531 | dpaux->aux.dev = &pdev->dev; |
532 | |
533 | drm_dp_aux_init(aux: &dpaux->aux); |
534 | |
535 | /* |
536 | * Assume that by default the DPAUX/I2C pads will be used for HDMI, |
537 | * so power them up and configure them in I2C mode. |
538 | * |
539 | * The DPAUX code paths reconfigure the pads in AUX mode, but there |
540 | * is no possibility to perform the I2C mode configuration in the |
541 | * HDMI path. |
542 | */ |
543 | err = tegra_dpaux_pad_config(dpaux, function: DPAUX_PADCTL_FUNC_I2C); |
544 | if (err < 0) |
545 | return err; |
546 | |
547 | #ifdef CONFIG_GENERIC_PINCONF |
548 | dpaux->desc.name = dev_name(dev: &pdev->dev); |
549 | dpaux->desc.pins = tegra_dpaux_pins; |
550 | dpaux->desc.npins = ARRAY_SIZE(tegra_dpaux_pins); |
551 | dpaux->desc.pctlops = &tegra_dpaux_pinctrl_ops; |
552 | dpaux->desc.pmxops = &tegra_dpaux_pinmux_ops; |
553 | dpaux->desc.owner = THIS_MODULE; |
554 | |
555 | dpaux->pinctrl = devm_pinctrl_register(dev: &pdev->dev, pctldesc: &dpaux->desc, driver_data: dpaux); |
556 | if (IS_ERR(ptr: dpaux->pinctrl)) { |
557 | dev_err(&pdev->dev, "failed to register pincontrol\n" ); |
558 | return PTR_ERR(ptr: dpaux->pinctrl); |
559 | } |
560 | #endif |
561 | /* enable and clear all interrupts */ |
562 | value = DPAUX_INTR_AUX_DONE | DPAUX_INTR_IRQ_EVENT | |
563 | DPAUX_INTR_UNPLUG_EVENT | DPAUX_INTR_PLUG_EVENT; |
564 | tegra_dpaux_writel(dpaux, value, DPAUX_INTR_EN_AUX); |
565 | tegra_dpaux_writel(dpaux, value, DPAUX_INTR_AUX); |
566 | |
567 | mutex_lock(&dpaux_lock); |
568 | list_add_tail(new: &dpaux->list, head: &dpaux_list); |
569 | mutex_unlock(lock: &dpaux_lock); |
570 | |
571 | err = devm_of_dp_aux_populate_ep_devices(aux: &dpaux->aux); |
572 | if (err < 0) { |
573 | dev_err(dpaux->dev, "failed to populate AUX bus: %d\n" , err); |
574 | return err; |
575 | } |
576 | |
577 | return 0; |
578 | } |
579 | |
580 | static void tegra_dpaux_remove(struct platform_device *pdev) |
581 | { |
582 | struct tegra_dpaux *dpaux = platform_get_drvdata(pdev); |
583 | |
584 | cancel_work_sync(work: &dpaux->work); |
585 | |
586 | /* make sure pads are powered down when not in use */ |
587 | tegra_dpaux_pad_power_down(dpaux); |
588 | |
589 | pm_runtime_put_sync(dev: &pdev->dev); |
590 | pm_runtime_disable(dev: &pdev->dev); |
591 | |
592 | mutex_lock(&dpaux_lock); |
593 | list_del(entry: &dpaux->list); |
594 | mutex_unlock(lock: &dpaux_lock); |
595 | } |
596 | |
597 | static int tegra_dpaux_suspend(struct device *dev) |
598 | { |
599 | struct tegra_dpaux *dpaux = dev_get_drvdata(dev); |
600 | int err = 0; |
601 | |
602 | if (dpaux->rst) { |
603 | err = reset_control_assert(rstc: dpaux->rst); |
604 | if (err < 0) { |
605 | dev_err(dev, "failed to assert reset: %d\n" , err); |
606 | return err; |
607 | } |
608 | } |
609 | |
610 | usleep_range(min: 1000, max: 2000); |
611 | |
612 | clk_disable_unprepare(clk: dpaux->clk_parent); |
613 | clk_disable_unprepare(clk: dpaux->clk); |
614 | |
615 | return err; |
616 | } |
617 | |
618 | static int tegra_dpaux_resume(struct device *dev) |
619 | { |
620 | struct tegra_dpaux *dpaux = dev_get_drvdata(dev); |
621 | int err; |
622 | |
623 | err = clk_prepare_enable(clk: dpaux->clk); |
624 | if (err < 0) { |
625 | dev_err(dev, "failed to enable clock: %d\n" , err); |
626 | return err; |
627 | } |
628 | |
629 | err = clk_prepare_enable(clk: dpaux->clk_parent); |
630 | if (err < 0) { |
631 | dev_err(dev, "failed to enable parent clock: %d\n" , err); |
632 | goto disable_clk; |
633 | } |
634 | |
635 | usleep_range(min: 1000, max: 2000); |
636 | |
637 | if (dpaux->rst) { |
638 | err = reset_control_deassert(rstc: dpaux->rst); |
639 | if (err < 0) { |
640 | dev_err(dev, "failed to deassert reset: %d\n" , err); |
641 | goto disable_parent; |
642 | } |
643 | |
644 | usleep_range(min: 1000, max: 2000); |
645 | } |
646 | |
647 | return 0; |
648 | |
649 | disable_parent: |
650 | clk_disable_unprepare(clk: dpaux->clk_parent); |
651 | disable_clk: |
652 | clk_disable_unprepare(clk: dpaux->clk); |
653 | return err; |
654 | } |
655 | |
656 | static const struct dev_pm_ops tegra_dpaux_pm_ops = { |
657 | RUNTIME_PM_OPS(tegra_dpaux_suspend, tegra_dpaux_resume, NULL) |
658 | }; |
659 | |
660 | static const struct tegra_dpaux_soc tegra124_dpaux_soc = { |
661 | .cmh = 0x02, |
662 | .drvz = 0x04, |
663 | .drvi = 0x18, |
664 | }; |
665 | |
666 | static const struct tegra_dpaux_soc tegra210_dpaux_soc = { |
667 | .cmh = 0x02, |
668 | .drvz = 0x04, |
669 | .drvi = 0x30, |
670 | }; |
671 | |
672 | static const struct tegra_dpaux_soc tegra194_dpaux_soc = { |
673 | .cmh = 0x02, |
674 | .drvz = 0x04, |
675 | .drvi = 0x2c, |
676 | }; |
677 | |
678 | static const struct of_device_id tegra_dpaux_of_match[] = { |
679 | { .compatible = "nvidia,tegra194-dpaux" , .data = &tegra194_dpaux_soc }, |
680 | { .compatible = "nvidia,tegra186-dpaux" , .data = &tegra210_dpaux_soc }, |
681 | { .compatible = "nvidia,tegra210-dpaux" , .data = &tegra210_dpaux_soc }, |
682 | { .compatible = "nvidia,tegra124-dpaux" , .data = &tegra124_dpaux_soc }, |
683 | { }, |
684 | }; |
685 | MODULE_DEVICE_TABLE(of, tegra_dpaux_of_match); |
686 | |
687 | struct platform_driver tegra_dpaux_driver = { |
688 | .driver = { |
689 | .name = "tegra-dpaux" , |
690 | .of_match_table = tegra_dpaux_of_match, |
691 | .pm = pm_ptr(&tegra_dpaux_pm_ops), |
692 | }, |
693 | .probe = tegra_dpaux_probe, |
694 | .remove_new = tegra_dpaux_remove, |
695 | }; |
696 | |
697 | struct drm_dp_aux *drm_dp_aux_find_by_of_node(struct device_node *np) |
698 | { |
699 | struct tegra_dpaux *dpaux; |
700 | |
701 | mutex_lock(&dpaux_lock); |
702 | |
703 | list_for_each_entry(dpaux, &dpaux_list, list) |
704 | if (np == dpaux->dev->of_node) { |
705 | mutex_unlock(lock: &dpaux_lock); |
706 | return &dpaux->aux; |
707 | } |
708 | |
709 | mutex_unlock(lock: &dpaux_lock); |
710 | |
711 | return NULL; |
712 | } |
713 | |
714 | int drm_dp_aux_attach(struct drm_dp_aux *aux, struct tegra_output *output) |
715 | { |
716 | struct tegra_dpaux *dpaux = to_dpaux(aux); |
717 | unsigned long timeout; |
718 | int err; |
719 | |
720 | aux->drm_dev = output->connector.dev; |
721 | err = drm_dp_aux_register(aux); |
722 | if (err < 0) |
723 | return err; |
724 | |
725 | output->connector.polled = DRM_CONNECTOR_POLL_HPD; |
726 | dpaux->output = output; |
727 | |
728 | if (output->panel) { |
729 | enum drm_connector_status status; |
730 | |
731 | if (dpaux->vdd) { |
732 | err = regulator_enable(regulator: dpaux->vdd); |
733 | if (err < 0) |
734 | return err; |
735 | } |
736 | |
737 | timeout = jiffies + msecs_to_jiffies(m: 250); |
738 | |
739 | while (time_before(jiffies, timeout)) { |
740 | status = drm_dp_aux_detect(aux); |
741 | |
742 | if (status == connector_status_connected) |
743 | break; |
744 | |
745 | usleep_range(min: 1000, max: 2000); |
746 | } |
747 | |
748 | if (status != connector_status_connected) |
749 | return -ETIMEDOUT; |
750 | } |
751 | |
752 | enable_irq(irq: dpaux->irq); |
753 | return 0; |
754 | } |
755 | |
756 | int drm_dp_aux_detach(struct drm_dp_aux *aux) |
757 | { |
758 | struct tegra_dpaux *dpaux = to_dpaux(aux); |
759 | unsigned long timeout; |
760 | int err; |
761 | |
762 | drm_dp_aux_unregister(aux); |
763 | disable_irq(irq: dpaux->irq); |
764 | |
765 | if (dpaux->output->panel) { |
766 | enum drm_connector_status status; |
767 | |
768 | if (dpaux->vdd) { |
769 | err = regulator_disable(regulator: dpaux->vdd); |
770 | if (err < 0) |
771 | return err; |
772 | } |
773 | |
774 | timeout = jiffies + msecs_to_jiffies(m: 250); |
775 | |
776 | while (time_before(jiffies, timeout)) { |
777 | status = drm_dp_aux_detect(aux); |
778 | |
779 | if (status == connector_status_disconnected) |
780 | break; |
781 | |
782 | usleep_range(min: 1000, max: 2000); |
783 | } |
784 | |
785 | if (status != connector_status_disconnected) |
786 | return -ETIMEDOUT; |
787 | |
788 | dpaux->output = NULL; |
789 | } |
790 | |
791 | return 0; |
792 | } |
793 | |
794 | enum drm_connector_status drm_dp_aux_detect(struct drm_dp_aux *aux) |
795 | { |
796 | struct tegra_dpaux *dpaux = to_dpaux(aux); |
797 | u32 value; |
798 | |
799 | value = tegra_dpaux_readl(dpaux, DPAUX_DP_AUXSTAT); |
800 | |
801 | if (value & DPAUX_DP_AUXSTAT_HPD_STATUS) |
802 | return connector_status_connected; |
803 | |
804 | return connector_status_disconnected; |
805 | } |
806 | |
807 | int drm_dp_aux_enable(struct drm_dp_aux *aux) |
808 | { |
809 | struct tegra_dpaux *dpaux = to_dpaux(aux); |
810 | |
811 | return tegra_dpaux_pad_config(dpaux, function: DPAUX_PADCTL_FUNC_AUX); |
812 | } |
813 | |
814 | int drm_dp_aux_disable(struct drm_dp_aux *aux) |
815 | { |
816 | struct tegra_dpaux *dpaux = to_dpaux(aux); |
817 | |
818 | tegra_dpaux_pad_power_down(dpaux); |
819 | |
820 | return 0; |
821 | } |
822 | |