aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2017-10-12 11:32:52 -0400
committerThierry Reding <treding@nvidia.com>2017-12-13 08:36:38 -0500
commit82b81b3ec1a796d10206fd8333437db20ef215fc (patch)
treefa28c3e5f7536751614786eb667b272081675135
parent36e90221acf37dd0eb5dee70cd189cc60f2e501a (diff)
drm/tegra: dpaux: Implement runtime PM
Move clock and reset management into runtime PM callbacks and hook them up. This cleans up the code structure so that power management code does not clutter up the rest. Signed-off-by: Thierry Reding <treding@nvidia.com>
-rw-r--r--drivers/gpu/drm/tegra/dpaux.c116
1 files changed, 76 insertions, 40 deletions
diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c
index e4da041ba89b..2cd671b1a9cc 100644
--- a/drivers/gpu/drm/tegra/dpaux.c
+++ b/drivers/gpu/drm/tegra/dpaux.c
@@ -15,6 +15,7 @@
15#include <linux/pinctrl/pinconf-generic.h> 15#include <linux/pinctrl/pinconf-generic.h>
16#include <linux/pinctrl/pinctrl.h> 16#include <linux/pinctrl/pinctrl.h>
17#include <linux/pinctrl/pinmux.h> 17#include <linux/pinctrl/pinmux.h>
18#include <linux/pm_runtime.h>
18#include <linux/platform_device.h> 19#include <linux/platform_device.h>
19#include <linux/reset.h> 20#include <linux/reset.h>
20#include <linux/regulator/consumer.h> 21#include <linux/regulator/consumer.h>
@@ -467,52 +468,37 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
467 return PTR_ERR(dpaux->clk); 468 return PTR_ERR(dpaux->clk);
468 } 469 }
469 470
470 err = clk_prepare_enable(dpaux->clk);
471 if (err < 0) {
472 dev_err(&pdev->dev, "failed to enable module clock: %d\n",
473 err);
474 return err;
475 }
476
477 if (dpaux->rst)
478 reset_control_deassert(dpaux->rst);
479
480 dpaux->clk_parent = devm_clk_get(&pdev->dev, "parent"); 471 dpaux->clk_parent = devm_clk_get(&pdev->dev, "parent");
481 if (IS_ERR(dpaux->clk_parent)) { 472 if (IS_ERR(dpaux->clk_parent)) {
482 dev_err(&pdev->dev, "failed to get parent clock: %ld\n", 473 dev_err(&pdev->dev, "failed to get parent clock: %ld\n",
483 PTR_ERR(dpaux->clk_parent)); 474 PTR_ERR(dpaux->clk_parent));
484 err = PTR_ERR(dpaux->clk_parent); 475 return PTR_ERR(dpaux->clk_parent);
485 goto assert_reset;
486 }
487
488 err = clk_prepare_enable(dpaux->clk_parent);
489 if (err < 0) {
490 dev_err(&pdev->dev, "failed to enable parent clock: %d\n",
491 err);
492 goto assert_reset;
493 } 476 }
494 477
495 err = clk_set_rate(dpaux->clk_parent, 270000000); 478 err = clk_set_rate(dpaux->clk_parent, 270000000);
496 if (err < 0) { 479 if (err < 0) {
497 dev_err(&pdev->dev, "failed to set clock to 270 MHz: %d\n", 480 dev_err(&pdev->dev, "failed to set clock to 270 MHz: %d\n",
498 err); 481 err);
499 goto disable_parent_clk; 482 return err;
500 } 483 }
501 484
502 dpaux->vdd = devm_regulator_get(&pdev->dev, "vdd"); 485 dpaux->vdd = devm_regulator_get(&pdev->dev, "vdd");
503 if (IS_ERR(dpaux->vdd)) { 486 if (IS_ERR(dpaux->vdd)) {
504 dev_err(&pdev->dev, "failed to get VDD supply: %ld\n", 487 dev_err(&pdev->dev, "failed to get VDD supply: %ld\n",
505 PTR_ERR(dpaux->vdd)); 488 PTR_ERR(dpaux->vdd));
506 err = PTR_ERR(dpaux->vdd); 489 return PTR_ERR(dpaux->vdd);
507 goto disable_parent_clk;
508 } 490 }
509 491
492 platform_set_drvdata(pdev, dpaux);
493 pm_runtime_enable(&pdev->dev);
494 pm_runtime_get_sync(&pdev->dev);
495
510 err = devm_request_irq(dpaux->dev, dpaux->irq, tegra_dpaux_irq, 0, 496 err = devm_request_irq(dpaux->dev, dpaux->irq, tegra_dpaux_irq, 0,
511 dev_name(dpaux->dev), dpaux); 497 dev_name(dpaux->dev), dpaux);
512 if (err < 0) { 498 if (err < 0) {
513 dev_err(dpaux->dev, "failed to request IRQ#%u: %d\n", 499 dev_err(dpaux->dev, "failed to request IRQ#%u: %d\n",
514 dpaux->irq, err); 500 dpaux->irq, err);
515 goto disable_parent_clk; 501 return err;
516 } 502 }
517 503
518 disable_irq(dpaux->irq); 504 disable_irq(dpaux->irq);
@@ -522,7 +508,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
522 508
523 err = drm_dp_aux_register(&dpaux->aux); 509 err = drm_dp_aux_register(&dpaux->aux);
524 if (err < 0) 510 if (err < 0)
525 goto disable_parent_clk; 511 return err;
526 512
527 /* 513 /*
528 * Assume that by default the DPAUX/I2C pads will be used for HDMI, 514 * Assume that by default the DPAUX/I2C pads will be used for HDMI,
@@ -560,45 +546,94 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
560 list_add_tail(&dpaux->list, &dpaux_list); 546 list_add_tail(&dpaux->list, &dpaux_list);
561 mutex_unlock(&dpaux_lock); 547 mutex_unlock(&dpaux_lock);
562 548
563 platform_set_drvdata(pdev, dpaux);
564
565 return 0; 549 return 0;
566
567disable_parent_clk:
568 clk_disable_unprepare(dpaux->clk_parent);
569assert_reset:
570 if (dpaux->rst)
571 reset_control_assert(dpaux->rst);
572
573 clk_disable_unprepare(dpaux->clk);
574
575 return err;
576} 550}
577 551
578static int tegra_dpaux_remove(struct platform_device *pdev) 552static int tegra_dpaux_remove(struct platform_device *pdev)
579{ 553{
580 struct tegra_dpaux *dpaux = platform_get_drvdata(pdev); 554 struct tegra_dpaux *dpaux = platform_get_drvdata(pdev);
581 555
556 cancel_work_sync(&dpaux->work);
557
582 /* make sure pads are powered down when not in use */ 558 /* make sure pads are powered down when not in use */
583 tegra_dpaux_pad_power_down(dpaux); 559 tegra_dpaux_pad_power_down(dpaux);
584 560
561 pm_runtime_put(&pdev->dev);
562 pm_runtime_disable(&pdev->dev);
563
585 drm_dp_aux_unregister(&dpaux->aux); 564 drm_dp_aux_unregister(&dpaux->aux);
586 565
587 mutex_lock(&dpaux_lock); 566 mutex_lock(&dpaux_lock);
588 list_del(&dpaux->list); 567 list_del(&dpaux->list);
589 mutex_unlock(&dpaux_lock); 568 mutex_unlock(&dpaux_lock);
590 569
591 cancel_work_sync(&dpaux->work); 570 return 0;
571}
592 572
593 clk_disable_unprepare(dpaux->clk_parent); 573#ifdef CONFIG_PM
574static int tegra_dpaux_suspend(struct device *dev)
575{
576 struct tegra_dpaux *dpaux = dev_get_drvdata(dev);
577 int err = 0;
578
579 if (dpaux->rst) {
580 err = reset_control_assert(dpaux->rst);
581 if (err < 0) {
582 dev_err(dev, "failed to assert reset: %d\n", err);
583 return err;
584 }
585 }
594 586
595 if (dpaux->rst) 587 usleep_range(1000, 2000);
596 reset_control_assert(dpaux->rst);
597 588
589 clk_disable_unprepare(dpaux->clk_parent);
598 clk_disable_unprepare(dpaux->clk); 590 clk_disable_unprepare(dpaux->clk);
599 591
592 return err;
593}
594
595static int tegra_dpaux_resume(struct device *dev)
596{
597 struct tegra_dpaux *dpaux = dev_get_drvdata(dev);
598 int err;
599
600 err = clk_prepare_enable(dpaux->clk);
601 if (err < 0) {
602 dev_err(dev, "failed to enable clock: %d\n", err);
603 return err;
604 }
605
606 err = clk_prepare_enable(dpaux->clk_parent);
607 if (err < 0) {
608 dev_err(dev, "failed to enable parent clock: %d\n", err);
609 goto disable_clk;
610 }
611
612 usleep_range(1000, 2000);
613
614 if (dpaux->rst) {
615 err = reset_control_deassert(dpaux->rst);
616 if (err < 0) {
617 dev_err(dev, "failed to deassert reset: %d\n", err);
618 goto disable_parent;
619 }
620
621 usleep_range(1000, 2000);
622 }
623
600 return 0; 624 return 0;
625
626disable_parent:
627 clk_disable_unprepare(dpaux->clk_parent);
628disable_clk:
629 clk_disable_unprepare(dpaux->clk);
630 return err;
601} 631}
632#endif
633
634static const struct dev_pm_ops tegra_dpaux_pm_ops = {
635 SET_RUNTIME_PM_OPS(tegra_dpaux_suspend, tegra_dpaux_resume, NULL)
636};
602 637
603static const struct of_device_id tegra_dpaux_of_match[] = { 638static const struct of_device_id tegra_dpaux_of_match[] = {
604 { .compatible = "nvidia,tegra210-dpaux", }, 639 { .compatible = "nvidia,tegra210-dpaux", },
@@ -611,6 +646,7 @@ struct platform_driver tegra_dpaux_driver = {
611 .driver = { 646 .driver = {
612 .name = "tegra-dpaux", 647 .name = "tegra-dpaux",
613 .of_match_table = tegra_dpaux_of_match, 648 .of_match_table = tegra_dpaux_of_match,
649 .pm = &tegra_dpaux_pm_ops,
614 }, 650 },
615 .probe = tegra_dpaux_probe, 651 .probe = tegra_dpaux_probe,
616 .remove = tegra_dpaux_remove, 652 .remove = tegra_dpaux_remove,