Commit 82b81b3e authored by Thierry Reding's avatar Thierry Reding

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: default avatarThierry Reding <treding@nvidia.com>
parent 36e90221
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/pinctrl/pinconf-generic.h> #include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h> #include <linux/pinctrl/pinmux.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
...@@ -467,52 +468,37 @@ static int tegra_dpaux_probe(struct platform_device *pdev) ...@@ -467,52 +468,37 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
return PTR_ERR(dpaux->clk); return PTR_ERR(dpaux->clk);
} }
err = clk_prepare_enable(dpaux->clk);
if (err < 0) {
dev_err(&pdev->dev, "failed to enable module clock: %d\n",
err);
return err;
}
if (dpaux->rst)
reset_control_deassert(dpaux->rst);
dpaux->clk_parent = devm_clk_get(&pdev->dev, "parent"); dpaux->clk_parent = devm_clk_get(&pdev->dev, "parent");
if (IS_ERR(dpaux->clk_parent)) { if (IS_ERR(dpaux->clk_parent)) {
dev_err(&pdev->dev, "failed to get parent clock: %ld\n", dev_err(&pdev->dev, "failed to get parent clock: %ld\n",
PTR_ERR(dpaux->clk_parent)); PTR_ERR(dpaux->clk_parent));
err = PTR_ERR(dpaux->clk_parent); return PTR_ERR(dpaux->clk_parent);
goto assert_reset;
}
err = clk_prepare_enable(dpaux->clk_parent);
if (err < 0) {
dev_err(&pdev->dev, "failed to enable parent clock: %d\n",
err);
goto assert_reset;
} }
err = clk_set_rate(dpaux->clk_parent, 270000000); err = clk_set_rate(dpaux->clk_parent, 270000000);
if (err < 0) { if (err < 0) {
dev_err(&pdev->dev, "failed to set clock to 270 MHz: %d\n", dev_err(&pdev->dev, "failed to set clock to 270 MHz: %d\n",
err); err);
goto disable_parent_clk; return err;
} }
dpaux->vdd = devm_regulator_get(&pdev->dev, "vdd"); dpaux->vdd = devm_regulator_get(&pdev->dev, "vdd");
if (IS_ERR(dpaux->vdd)) { if (IS_ERR(dpaux->vdd)) {
dev_err(&pdev->dev, "failed to get VDD supply: %ld\n", dev_err(&pdev->dev, "failed to get VDD supply: %ld\n",
PTR_ERR(dpaux->vdd)); PTR_ERR(dpaux->vdd));
err = PTR_ERR(dpaux->vdd); return PTR_ERR(dpaux->vdd);
goto disable_parent_clk;
} }
platform_set_drvdata(pdev, dpaux);
pm_runtime_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
err = devm_request_irq(dpaux->dev, dpaux->irq, tegra_dpaux_irq, 0, err = devm_request_irq(dpaux->dev, dpaux->irq, tegra_dpaux_irq, 0,
dev_name(dpaux->dev), dpaux); dev_name(dpaux->dev), dpaux);
if (err < 0) { if (err < 0) {
dev_err(dpaux->dev, "failed to request IRQ#%u: %d\n", dev_err(dpaux->dev, "failed to request IRQ#%u: %d\n",
dpaux->irq, err); dpaux->irq, err);
goto disable_parent_clk; return err;
} }
disable_irq(dpaux->irq); disable_irq(dpaux->irq);
...@@ -522,7 +508,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev) ...@@ -522,7 +508,7 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
err = drm_dp_aux_register(&dpaux->aux); err = drm_dp_aux_register(&dpaux->aux);
if (err < 0) if (err < 0)
goto disable_parent_clk; return err;
/* /*
* Assume that by default the DPAUX/I2C pads will be used for HDMI, * 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,45 +546,94 @@ static int tegra_dpaux_probe(struct platform_device *pdev)
list_add_tail(&dpaux->list, &dpaux_list); list_add_tail(&dpaux->list, &dpaux_list);
mutex_unlock(&dpaux_lock); mutex_unlock(&dpaux_lock);
platform_set_drvdata(pdev, dpaux);
return 0; return 0;
disable_parent_clk:
clk_disable_unprepare(dpaux->clk_parent);
assert_reset:
if (dpaux->rst)
reset_control_assert(dpaux->rst);
clk_disable_unprepare(dpaux->clk);
return err;
} }
static int tegra_dpaux_remove(struct platform_device *pdev) static int tegra_dpaux_remove(struct platform_device *pdev)
{ {
struct tegra_dpaux *dpaux = platform_get_drvdata(pdev); struct tegra_dpaux *dpaux = platform_get_drvdata(pdev);
cancel_work_sync(&dpaux->work);
/* make sure pads are powered down when not in use */ /* make sure pads are powered down when not in use */
tegra_dpaux_pad_power_down(dpaux); tegra_dpaux_pad_power_down(dpaux);
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
drm_dp_aux_unregister(&dpaux->aux); drm_dp_aux_unregister(&dpaux->aux);
mutex_lock(&dpaux_lock); mutex_lock(&dpaux_lock);
list_del(&dpaux->list); list_del(&dpaux->list);
mutex_unlock(&dpaux_lock); mutex_unlock(&dpaux_lock);
cancel_work_sync(&dpaux->work); return 0;
}
clk_disable_unprepare(dpaux->clk_parent); #ifdef CONFIG_PM
static int tegra_dpaux_suspend(struct device *dev)
{
struct tegra_dpaux *dpaux = dev_get_drvdata(dev);
int err = 0;
if (dpaux->rst) {
err = reset_control_assert(dpaux->rst);
if (err < 0) {
dev_err(dev, "failed to assert reset: %d\n", err);
return err;
}
}
if (dpaux->rst) usleep_range(1000, 2000);
reset_control_assert(dpaux->rst);
clk_disable_unprepare(dpaux->clk_parent);
clk_disable_unprepare(dpaux->clk); clk_disable_unprepare(dpaux->clk);
return err;
}
static int tegra_dpaux_resume(struct device *dev)
{
struct tegra_dpaux *dpaux = dev_get_drvdata(dev);
int err;
err = clk_prepare_enable(dpaux->clk);
if (err < 0) {
dev_err(dev, "failed to enable clock: %d\n", err);
return err;
}
err = clk_prepare_enable(dpaux->clk_parent);
if (err < 0) {
dev_err(dev, "failed to enable parent clock: %d\n", err);
goto disable_clk;
}
usleep_range(1000, 2000);
if (dpaux->rst) {
err = reset_control_deassert(dpaux->rst);
if (err < 0) {
dev_err(dev, "failed to deassert reset: %d\n", err);
goto disable_parent;
}
usleep_range(1000, 2000);
}
return 0; return 0;
disable_parent:
clk_disable_unprepare(dpaux->clk_parent);
disable_clk:
clk_disable_unprepare(dpaux->clk);
return err;
} }
#endif
static const struct dev_pm_ops tegra_dpaux_pm_ops = {
SET_RUNTIME_PM_OPS(tegra_dpaux_suspend, tegra_dpaux_resume, NULL)
};
static const struct of_device_id tegra_dpaux_of_match[] = { static const struct of_device_id tegra_dpaux_of_match[] = {
{ .compatible = "nvidia,tegra210-dpaux", }, { .compatible = "nvidia,tegra210-dpaux", },
...@@ -611,6 +646,7 @@ struct platform_driver tegra_dpaux_driver = { ...@@ -611,6 +646,7 @@ struct platform_driver tegra_dpaux_driver = {
.driver = { .driver = {
.name = "tegra-dpaux", .name = "tegra-dpaux",
.of_match_table = tegra_dpaux_of_match, .of_match_table = tegra_dpaux_of_match,
.pm = &tegra_dpaux_pm_ops,
}, },
.probe = tegra_dpaux_probe, .probe = tegra_dpaux_probe,
.remove = tegra_dpaux_remove, .remove = tegra_dpaux_remove,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment