diff options
Diffstat (limited to 'drivers/gpu/drm/tegra/dpaux.c')
-rw-r--r-- | drivers/gpu/drm/tegra/dpaux.c | 245 |
1 files changed, 202 insertions, 43 deletions
diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c index b24a0f14821a..059f409556d5 100644 --- a/drivers/gpu/drm/tegra/dpaux.c +++ b/drivers/gpu/drm/tegra/dpaux.c | |||
@@ -12,6 +12,9 @@ | |||
12 | #include <linux/interrupt.h> | 12 | #include <linux/interrupt.h> |
13 | #include <linux/io.h> | 13 | #include <linux/io.h> |
14 | #include <linux/of_gpio.h> | 14 | #include <linux/of_gpio.h> |
15 | #include <linux/pinctrl/pinconf-generic.h> | ||
16 | #include <linux/pinctrl/pinctrl.h> | ||
17 | #include <linux/pinctrl/pinmux.h> | ||
15 | #include <linux/platform_device.h> | 18 | #include <linux/platform_device.h> |
16 | #include <linux/reset.h> | 19 | #include <linux/reset.h> |
17 | #include <linux/regulator/consumer.h> | 20 | #include <linux/regulator/consumer.h> |
@@ -44,6 +47,11 @@ struct tegra_dpaux { | |||
44 | struct completion complete; | 47 | struct completion complete; |
45 | struct work_struct work; | 48 | struct work_struct work; |
46 | struct list_head list; | 49 | struct list_head list; |
50 | |||
51 | #ifdef CONFIG_GENERIC_PINCONF | ||
52 | struct pinctrl_dev *pinctrl; | ||
53 | struct pinctrl_desc desc; | ||
54 | #endif | ||
47 | }; | 55 | }; |
48 | 56 | ||
49 | static inline struct tegra_dpaux *to_dpaux(struct drm_dp_aux *aux) | 57 | static inline struct tegra_dpaux *to_dpaux(struct drm_dp_aux *aux) |
@@ -267,6 +275,148 @@ static irqreturn_t tegra_dpaux_irq(int irq, void *data) | |||
267 | return ret; | 275 | return ret; |
268 | } | 276 | } |
269 | 277 | ||
278 | enum tegra_dpaux_functions { | ||
279 | DPAUX_PADCTL_FUNC_AUX, | ||
280 | DPAUX_PADCTL_FUNC_I2C, | ||
281 | DPAUX_PADCTL_FUNC_OFF, | ||
282 | }; | ||
283 | |||
284 | static void tegra_dpaux_pad_power_down(struct tegra_dpaux *dpaux) | ||
285 | { | ||
286 | u32 value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE); | ||
287 | |||
288 | value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN; | ||
289 | |||
290 | tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE); | ||
291 | } | ||
292 | |||
293 | static void tegra_dpaux_pad_power_up(struct tegra_dpaux *dpaux) | ||
294 | { | ||
295 | u32 value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE); | ||
296 | |||
297 | value &= ~DPAUX_HYBRID_SPARE_PAD_POWER_DOWN; | ||
298 | |||
299 | tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE); | ||
300 | } | ||
301 | |||
302 | static int tegra_dpaux_pad_config(struct tegra_dpaux *dpaux, unsigned function) | ||
303 | { | ||
304 | u32 value; | ||
305 | |||
306 | switch (function) { | ||
307 | case DPAUX_PADCTL_FUNC_AUX: | ||
308 | value = DPAUX_HYBRID_PADCTL_AUX_CMH(2) | | ||
309 | DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) | | ||
310 | DPAUX_HYBRID_PADCTL_AUX_DRVI(0x18) | | ||
311 | DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV | | ||
312 | DPAUX_HYBRID_PADCTL_MODE_AUX; | ||
313 | break; | ||
314 | |||
315 | case DPAUX_PADCTL_FUNC_I2C: | ||
316 | value = DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV | | ||
317 | DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV | | ||
318 | DPAUX_HYBRID_PADCTL_MODE_I2C; | ||
319 | break; | ||
320 | |||
321 | case DPAUX_PADCTL_FUNC_OFF: | ||
322 | tegra_dpaux_pad_power_down(dpaux); | ||
323 | return 0; | ||
324 | |||
325 | default: | ||
326 | return -ENOTSUPP; | ||
327 | } | ||
328 | |||
329 | tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_PADCTL); | ||
330 | tegra_dpaux_pad_power_up(dpaux); | ||
331 | |||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | #ifdef CONFIG_GENERIC_PINCONF | ||
336 | static const struct pinctrl_pin_desc tegra_dpaux_pins[] = { | ||
337 | PINCTRL_PIN(0, "DP_AUX_CHx_P"), | ||
338 | PINCTRL_PIN(1, "DP_AUX_CHx_N"), | ||
339 | }; | ||
340 | |||
341 | static const unsigned tegra_dpaux_pin_numbers[] = { 0, 1 }; | ||
342 | |||
343 | static const char * const tegra_dpaux_groups[] = { | ||
344 | "dpaux-io", | ||
345 | }; | ||
346 | |||
347 | static const char * const tegra_dpaux_functions[] = { | ||
348 | "aux", | ||
349 | "i2c", | ||
350 | "off", | ||
351 | }; | ||
352 | |||
353 | static int tegra_dpaux_get_groups_count(struct pinctrl_dev *pinctrl) | ||
354 | { | ||
355 | return ARRAY_SIZE(tegra_dpaux_groups); | ||
356 | } | ||
357 | |||
358 | static const char *tegra_dpaux_get_group_name(struct pinctrl_dev *pinctrl, | ||
359 | unsigned int group) | ||
360 | { | ||
361 | return tegra_dpaux_groups[group]; | ||
362 | } | ||
363 | |||
364 | static int tegra_dpaux_get_group_pins(struct pinctrl_dev *pinctrl, | ||
365 | unsigned group, const unsigned **pins, | ||
366 | unsigned *num_pins) | ||
367 | { | ||
368 | *pins = tegra_dpaux_pin_numbers; | ||
369 | *num_pins = ARRAY_SIZE(tegra_dpaux_pin_numbers); | ||
370 | |||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | static const struct pinctrl_ops tegra_dpaux_pinctrl_ops = { | ||
375 | .get_groups_count = tegra_dpaux_get_groups_count, | ||
376 | .get_group_name = tegra_dpaux_get_group_name, | ||
377 | .get_group_pins = tegra_dpaux_get_group_pins, | ||
378 | .dt_node_to_map = pinconf_generic_dt_node_to_map_group, | ||
379 | .dt_free_map = pinconf_generic_dt_free_map, | ||
380 | }; | ||
381 | |||
382 | static int tegra_dpaux_get_functions_count(struct pinctrl_dev *pinctrl) | ||
383 | { | ||
384 | return ARRAY_SIZE(tegra_dpaux_functions); | ||
385 | } | ||
386 | |||
387 | static const char *tegra_dpaux_get_function_name(struct pinctrl_dev *pinctrl, | ||
388 | unsigned int function) | ||
389 | { | ||
390 | return tegra_dpaux_functions[function]; | ||
391 | } | ||
392 | |||
393 | static int tegra_dpaux_get_function_groups(struct pinctrl_dev *pinctrl, | ||
394 | unsigned int function, | ||
395 | const char * const **groups, | ||
396 | unsigned * const num_groups) | ||
397 | { | ||
398 | *num_groups = ARRAY_SIZE(tegra_dpaux_groups); | ||
399 | *groups = tegra_dpaux_groups; | ||
400 | |||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static int tegra_dpaux_set_mux(struct pinctrl_dev *pinctrl, | ||
405 | unsigned int function, unsigned int group) | ||
406 | { | ||
407 | struct tegra_dpaux *dpaux = pinctrl_dev_get_drvdata(pinctrl); | ||
408 | |||
409 | return tegra_dpaux_pad_config(dpaux, function); | ||
410 | } | ||
411 | |||
412 | static const struct pinmux_ops tegra_dpaux_pinmux_ops = { | ||
413 | .get_functions_count = tegra_dpaux_get_functions_count, | ||
414 | .get_function_name = tegra_dpaux_get_function_name, | ||
415 | .get_function_groups = tegra_dpaux_get_function_groups, | ||
416 | .set_mux = tegra_dpaux_set_mux, | ||
417 | }; | ||
418 | #endif | ||
419 | |||
270 | static int tegra_dpaux_probe(struct platform_device *pdev) | 420 | static int tegra_dpaux_probe(struct platform_device *pdev) |
271 | { | 421 | { |
272 | struct tegra_dpaux *dpaux; | 422 | struct tegra_dpaux *dpaux; |
@@ -294,11 +444,14 @@ static int tegra_dpaux_probe(struct platform_device *pdev) | |||
294 | return -ENXIO; | 444 | return -ENXIO; |
295 | } | 445 | } |
296 | 446 | ||
297 | dpaux->rst = devm_reset_control_get(&pdev->dev, "dpaux"); | 447 | if (!pdev->dev.pm_domain) { |
298 | if (IS_ERR(dpaux->rst)) { | 448 | dpaux->rst = devm_reset_control_get(&pdev->dev, "dpaux"); |
299 | dev_err(&pdev->dev, "failed to get reset control: %ld\n", | 449 | if (IS_ERR(dpaux->rst)) { |
300 | PTR_ERR(dpaux->rst)); | 450 | dev_err(&pdev->dev, |
301 | return PTR_ERR(dpaux->rst); | 451 | "failed to get reset control: %ld\n", |
452 | PTR_ERR(dpaux->rst)); | ||
453 | return PTR_ERR(dpaux->rst); | ||
454 | } | ||
302 | } | 455 | } |
303 | 456 | ||
304 | dpaux->clk = devm_clk_get(&pdev->dev, NULL); | 457 | dpaux->clk = devm_clk_get(&pdev->dev, NULL); |
@@ -315,34 +468,37 @@ static int tegra_dpaux_probe(struct platform_device *pdev) | |||
315 | return err; | 468 | return err; |
316 | } | 469 | } |
317 | 470 | ||
318 | reset_control_deassert(dpaux->rst); | 471 | if (dpaux->rst) |
472 | reset_control_deassert(dpaux->rst); | ||
319 | 473 | ||
320 | dpaux->clk_parent = devm_clk_get(&pdev->dev, "parent"); | 474 | dpaux->clk_parent = devm_clk_get(&pdev->dev, "parent"); |
321 | if (IS_ERR(dpaux->clk_parent)) { | 475 | if (IS_ERR(dpaux->clk_parent)) { |
322 | dev_err(&pdev->dev, "failed to get parent clock: %ld\n", | 476 | dev_err(&pdev->dev, "failed to get parent clock: %ld\n", |
323 | PTR_ERR(dpaux->clk_parent)); | 477 | PTR_ERR(dpaux->clk_parent)); |
324 | return PTR_ERR(dpaux->clk_parent); | 478 | err = PTR_ERR(dpaux->clk_parent); |
479 | goto assert_reset; | ||
325 | } | 480 | } |
326 | 481 | ||
327 | err = clk_prepare_enable(dpaux->clk_parent); | 482 | err = clk_prepare_enable(dpaux->clk_parent); |
328 | if (err < 0) { | 483 | if (err < 0) { |
329 | dev_err(&pdev->dev, "failed to enable parent clock: %d\n", | 484 | dev_err(&pdev->dev, "failed to enable parent clock: %d\n", |
330 | err); | 485 | err); |
331 | return err; | 486 | goto assert_reset; |
332 | } | 487 | } |
333 | 488 | ||
334 | err = clk_set_rate(dpaux->clk_parent, 270000000); | 489 | err = clk_set_rate(dpaux->clk_parent, 270000000); |
335 | if (err < 0) { | 490 | if (err < 0) { |
336 | dev_err(&pdev->dev, "failed to set clock to 270 MHz: %d\n", | 491 | dev_err(&pdev->dev, "failed to set clock to 270 MHz: %d\n", |
337 | err); | 492 | err); |
338 | return err; | 493 | goto disable_parent_clk; |
339 | } | 494 | } |
340 | 495 | ||
341 | dpaux->vdd = devm_regulator_get(&pdev->dev, "vdd"); | 496 | dpaux->vdd = devm_regulator_get(&pdev->dev, "vdd"); |
342 | if (IS_ERR(dpaux->vdd)) { | 497 | if (IS_ERR(dpaux->vdd)) { |
343 | dev_err(&pdev->dev, "failed to get VDD supply: %ld\n", | 498 | dev_err(&pdev->dev, "failed to get VDD supply: %ld\n", |
344 | PTR_ERR(dpaux->vdd)); | 499 | PTR_ERR(dpaux->vdd)); |
345 | return PTR_ERR(dpaux->vdd); | 500 | err = PTR_ERR(dpaux->vdd); |
501 | goto disable_parent_clk; | ||
346 | } | 502 | } |
347 | 503 | ||
348 | err = devm_request_irq(dpaux->dev, dpaux->irq, tegra_dpaux_irq, 0, | 504 | err = devm_request_irq(dpaux->dev, dpaux->irq, tegra_dpaux_irq, 0, |
@@ -350,7 +506,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev) | |||
350 | if (err < 0) { | 506 | if (err < 0) { |
351 | dev_err(dpaux->dev, "failed to request IRQ#%u: %d\n", | 507 | dev_err(dpaux->dev, "failed to request IRQ#%u: %d\n", |
352 | dpaux->irq, err); | 508 | dpaux->irq, err); |
353 | return err; | 509 | goto disable_parent_clk; |
354 | } | 510 | } |
355 | 511 | ||
356 | disable_irq(dpaux->irq); | 512 | disable_irq(dpaux->irq); |
@@ -360,7 +516,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev) | |||
360 | 516 | ||
361 | err = drm_dp_aux_register(&dpaux->aux); | 517 | err = drm_dp_aux_register(&dpaux->aux); |
362 | if (err < 0) | 518 | if (err < 0) |
363 | return err; | 519 | goto disable_parent_clk; |
364 | 520 | ||
365 | /* | 521 | /* |
366 | * Assume that by default the DPAUX/I2C pads will be used for HDMI, | 522 | * Assume that by default the DPAUX/I2C pads will be used for HDMI, |
@@ -370,16 +526,24 @@ static int tegra_dpaux_probe(struct platform_device *pdev) | |||
370 | * is no possibility to perform the I2C mode configuration in the | 526 | * is no possibility to perform the I2C mode configuration in the |
371 | * HDMI path. | 527 | * HDMI path. |
372 | */ | 528 | */ |
373 | value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE); | 529 | err = tegra_dpaux_pad_config(dpaux, DPAUX_HYBRID_PADCTL_MODE_I2C); |
374 | value &= ~DPAUX_HYBRID_SPARE_PAD_POWER_DOWN; | 530 | if (err < 0) |
375 | tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE); | 531 | return err; |
376 | |||
377 | value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_PADCTL); | ||
378 | value = DPAUX_HYBRID_PADCTL_I2C_SDA_INPUT_RCV | | ||
379 | DPAUX_HYBRID_PADCTL_I2C_SCL_INPUT_RCV | | ||
380 | DPAUX_HYBRID_PADCTL_MODE_I2C; | ||
381 | tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_PADCTL); | ||
382 | 532 | ||
533 | #ifdef CONFIG_GENERIC_PINCONF | ||
534 | dpaux->desc.name = dev_name(&pdev->dev); | ||
535 | dpaux->desc.pins = tegra_dpaux_pins; | ||
536 | dpaux->desc.npins = ARRAY_SIZE(tegra_dpaux_pins); | ||
537 | dpaux->desc.pctlops = &tegra_dpaux_pinctrl_ops; | ||
538 | dpaux->desc.pmxops = &tegra_dpaux_pinmux_ops; | ||
539 | dpaux->desc.owner = THIS_MODULE; | ||
540 | |||
541 | dpaux->pinctrl = devm_pinctrl_register(&pdev->dev, &dpaux->desc, dpaux); | ||
542 | if (!dpaux->pinctrl) { | ||
543 | dev_err(&pdev->dev, "failed to register pincontrol\n"); | ||
544 | return -ENODEV; | ||
545 | } | ||
546 | #endif | ||
383 | /* enable and clear all interrupts */ | 547 | /* enable and clear all interrupts */ |
384 | value = DPAUX_INTR_AUX_DONE | DPAUX_INTR_IRQ_EVENT | | 548 | value = DPAUX_INTR_AUX_DONE | DPAUX_INTR_IRQ_EVENT | |
385 | DPAUX_INTR_UNPLUG_EVENT | DPAUX_INTR_PLUG_EVENT; | 549 | DPAUX_INTR_UNPLUG_EVENT | DPAUX_INTR_PLUG_EVENT; |
@@ -393,17 +557,24 @@ static int tegra_dpaux_probe(struct platform_device *pdev) | |||
393 | platform_set_drvdata(pdev, dpaux); | 557 | platform_set_drvdata(pdev, dpaux); |
394 | 558 | ||
395 | return 0; | 559 | return 0; |
560 | |||
561 | disable_parent_clk: | ||
562 | clk_disable_unprepare(dpaux->clk_parent); | ||
563 | assert_reset: | ||
564 | if (dpaux->rst) | ||
565 | reset_control_assert(dpaux->rst); | ||
566 | |||
567 | clk_disable_unprepare(dpaux->clk); | ||
568 | |||
569 | return err; | ||
396 | } | 570 | } |
397 | 571 | ||
398 | static int tegra_dpaux_remove(struct platform_device *pdev) | 572 | static int tegra_dpaux_remove(struct platform_device *pdev) |
399 | { | 573 | { |
400 | struct tegra_dpaux *dpaux = platform_get_drvdata(pdev); | 574 | struct tegra_dpaux *dpaux = platform_get_drvdata(pdev); |
401 | u32 value; | ||
402 | 575 | ||
403 | /* make sure pads are powered down when not in use */ | 576 | /* make sure pads are powered down when not in use */ |
404 | value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE); | 577 | tegra_dpaux_pad_power_down(dpaux); |
405 | value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN; | ||
406 | tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE); | ||
407 | 578 | ||
408 | drm_dp_aux_unregister(&dpaux->aux); | 579 | drm_dp_aux_unregister(&dpaux->aux); |
409 | 580 | ||
@@ -414,7 +585,10 @@ static int tegra_dpaux_remove(struct platform_device *pdev) | |||
414 | cancel_work_sync(&dpaux->work); | 585 | cancel_work_sync(&dpaux->work); |
415 | 586 | ||
416 | clk_disable_unprepare(dpaux->clk_parent); | 587 | clk_disable_unprepare(dpaux->clk_parent); |
417 | reset_control_assert(dpaux->rst); | 588 | |
589 | if (dpaux->rst) | ||
590 | reset_control_assert(dpaux->rst); | ||
591 | |||
418 | clk_disable_unprepare(dpaux->clk); | 592 | clk_disable_unprepare(dpaux->clk); |
419 | 593 | ||
420 | return 0; | 594 | return 0; |
@@ -528,30 +702,15 @@ enum drm_connector_status drm_dp_aux_detect(struct drm_dp_aux *aux) | |||
528 | int drm_dp_aux_enable(struct drm_dp_aux *aux) | 702 | int drm_dp_aux_enable(struct drm_dp_aux *aux) |
529 | { | 703 | { |
530 | struct tegra_dpaux *dpaux = to_dpaux(aux); | 704 | struct tegra_dpaux *dpaux = to_dpaux(aux); |
531 | u32 value; | ||
532 | |||
533 | value = DPAUX_HYBRID_PADCTL_AUX_CMH(2) | | ||
534 | DPAUX_HYBRID_PADCTL_AUX_DRVZ(4) | | ||
535 | DPAUX_HYBRID_PADCTL_AUX_DRVI(0x18) | | ||
536 | DPAUX_HYBRID_PADCTL_AUX_INPUT_RCV | | ||
537 | DPAUX_HYBRID_PADCTL_MODE_AUX; | ||
538 | tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_PADCTL); | ||
539 | |||
540 | value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE); | ||
541 | value &= ~DPAUX_HYBRID_SPARE_PAD_POWER_DOWN; | ||
542 | tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE); | ||
543 | 705 | ||
544 | return 0; | 706 | return tegra_dpaux_pad_config(dpaux, DPAUX_PADCTL_FUNC_AUX); |
545 | } | 707 | } |
546 | 708 | ||
547 | int drm_dp_aux_disable(struct drm_dp_aux *aux) | 709 | int drm_dp_aux_disable(struct drm_dp_aux *aux) |
548 | { | 710 | { |
549 | struct tegra_dpaux *dpaux = to_dpaux(aux); | 711 | struct tegra_dpaux *dpaux = to_dpaux(aux); |
550 | u32 value; | ||
551 | 712 | ||
552 | value = tegra_dpaux_readl(dpaux, DPAUX_HYBRID_SPARE); | 713 | tegra_dpaux_pad_power_down(dpaux); |
553 | value |= DPAUX_HYBRID_SPARE_PAD_POWER_DOWN; | ||
554 | tegra_dpaux_writel(dpaux, value, DPAUX_HYBRID_SPARE); | ||
555 | 714 | ||
556 | return 0; | 715 | return 0; |
557 | } | 716 | } |