[PATCH v1 2/2] drm/tegra: Support disabled CONFIG_PM

From: Dmitry Osipenko
Date: Sun Dec 10 2017 - 18:20:30 EST


Add manual HW power management to drivers probe/remove in order to
not fail in a case of runtime power management being disabled in kernel
config.

Signed-off-by: Dmitry Osipenko <digetx@xxxxxxxxx>
---
drivers/gpu/drm/tegra/dc.c | 164 +++++++++++++++++++++++++++----------------
drivers/gpu/drm/tegra/dsi.c | 138 +++++++++++++++++++++---------------
drivers/gpu/drm/tegra/hdmi.c | 90 ++++++++++++++++--------
drivers/gpu/drm/tegra/sor.c | 103 +++++++++++++++++----------
4 files changed, 310 insertions(+), 185 deletions(-)

diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 6139d3e9cedf..9d442be081c5 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1967,6 +1967,88 @@ static int tegra_dc_parse_dt(struct tegra_dc *dc)
return 0;
}

+static int tegra_dc_power_off(struct tegra_dc *dc)
+{
+ int err;
+
+ err = reset_control_assert(dc->rst);
+ if (err) {
+ dev_err(dc->dev, "failed to assert reset: %d\n", err);
+ return err;
+ }
+
+ usleep_range(2000, 4000);
+
+ if (dc->soc->has_powergate)
+ tegra_powergate_power_off(dc->powergate);
+
+ clk_disable_unprepare(dc->clk);
+
+ return 0;
+}
+
+static int tegra_dc_power_on(struct tegra_dc *dc)
+{
+ int err;
+
+ if (dc->soc->has_powergate) {
+ err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk,
+ dc->rst);
+ if (err) {
+ dev_err(dc->dev, "failed to power partition: %d\n",
+ err);
+ return err;
+ }
+ } else {
+ err = clk_prepare_enable(dc->clk);
+ if (err) {
+ dev_err(dc->dev, "failed to enable clock: %d\n", err);
+ return err;
+ }
+
+ err = reset_control_deassert(dc->rst);
+ if (err) {
+ dev_err(dc->dev, "failed to deassert reset: %d\n", err);
+ return err;
+ }
+ }
+
+ return err;
+}
+
+static int tegra_dc_hw_init(struct tegra_dc *dc)
+{
+ int err;
+
+ err = clk_prepare_enable(dc->clk);
+ if (err) {
+ dev_err(dc->dev, "failed to enable clock: %d\n", err);
+ return err;
+ }
+
+ usleep_range(2000, 4000);
+ err = reset_control_assert(dc->rst);
+ usleep_range(2000, 4000);
+
+ clk_disable_unprepare(dc->clk);
+
+ if (err) {
+ dev_err(dc->dev, "failed to assert reset: %d\n", err);
+ return err;
+ }
+
+ if (dc->soc->has_powergate) {
+ if (dc->pipe == 0)
+ dc->powergate = TEGRA_POWERGATE_DIS;
+ else
+ dc->powergate = TEGRA_POWERGATE_DISB;
+
+ tegra_powergate_power_off(dc->powergate);
+ }
+
+ return 0;
+}
+
static int tegra_dc_probe(struct platform_device *pdev)
{
struct resource *regs;
@@ -2017,30 +2099,10 @@ static int tegra_dc_probe(struct platform_device *pdev)
return PTR_ERR(dc->rst);
}

- /* assert reset and disable clock */
- err = clk_prepare_enable(dc->clk);
- if (err < 0)
+ err = tegra_dc_hw_init(dc);
+ if (err)
return err;

- usleep_range(2000, 4000);
-
- err = reset_control_assert(dc->rst);
- if (err < 0)
- return err;
-
- usleep_range(2000, 4000);
-
- clk_disable_unprepare(dc->clk);
-
- if (dc->soc->has_powergate) {
- if (dc->pipe == 0)
- dc->powergate = TEGRA_POWERGATE_DIS;
- else
- dc->powergate = TEGRA_POWERGATE_DISB;
-
- tegra_powergate_power_off(dc->powergate);
- }
-
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dc->regs = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(dc->regs))
@@ -2061,6 +2123,12 @@ static int tegra_dc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dc);
pm_runtime_enable(&pdev->dev);

+ if (!pm_runtime_enabled(&pdev->dev)) {
+ err = tegra_dc_power_on(dc);
+ if (err)
+ return err;
+ }
+
INIT_LIST_HEAD(&dc->client.list);
dc->client.ops = &dc_client_ops;
dc->client.dev = &pdev->dev;
@@ -2069,13 +2137,19 @@ static int tegra_dc_probe(struct platform_device *pdev)
if (err < 0) {
dev_err(&pdev->dev, "failed to register host1x client: %d\n",
err);
- return err;
+ goto power_off;
}

if (dc->pipe == 0)
dc0 = dc;

return 0;
+
+power_off:
+ if (!pm_runtime_enabled(&pdev->dev))
+ tegra_dc_power_off(dc);
+
+ return err;
}

static int tegra_dc_remove(struct platform_device *pdev)
@@ -2096,6 +2170,12 @@ static int tegra_dc_remove(struct platform_device *pdev)
return err;
}

+ if (!pm_runtime_enabled(&pdev->dev)) {
+ err = tegra_dc_power_off(dc);
+ if (err)
+ return err;
+ }
+
pm_runtime_disable(&pdev->dev);

if (dc == dc0)
@@ -2108,49 +2188,15 @@ static int tegra_dc_remove(struct platform_device *pdev)
static int tegra_dc_suspend(struct device *dev)
{
struct tegra_dc *dc = dev_get_drvdata(dev);
- int err;

- err = reset_control_assert(dc->rst);
- if (err < 0) {
- dev_err(dev, "failed to assert reset: %d\n", err);
- return err;
- }
-
- if (dc->soc->has_powergate)
- tegra_powergate_power_off(dc->powergate);
-
- clk_disable_unprepare(dc->clk);
-
- return 0;
+ return tegra_dc_power_off(dc);
}

static int tegra_dc_resume(struct device *dev)
{
struct tegra_dc *dc = dev_get_drvdata(dev);
- int err;

- if (dc->soc->has_powergate) {
- err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk,
- dc->rst);
- if (err < 0) {
- dev_err(dev, "failed to power partition: %d\n", err);
- return err;
- }
- } else {
- err = clk_prepare_enable(dc->clk);
- if (err < 0) {
- dev_err(dev, "failed to enable clock: %d\n", err);
- return err;
- }
-
- err = reset_control_deassert(dc->rst);
- if (err < 0) {
- dev_err(dev, "failed to deassert reset: %d\n", err);
- return err;
- }
- }
-
- return 0;
+ return tegra_dc_power_on(dc);
}
#endif

diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index 046649ec9441..540101a84311 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -1489,6 +1489,71 @@ static int tegra_dsi_ganged_probe(struct tegra_dsi *dsi)
return 0;
}

+static int tegra_dsi_power_off(struct tegra_dsi *dsi)
+{
+ int err;
+
+ if (dsi->rst) {
+ err = reset_control_assert(dsi->rst);
+ if (err < 0) {
+ dev_err(dsi->dev, "failed to assert reset: %d\n", err);
+ return err;
+ }
+ }
+
+ usleep_range(1000, 2000);
+
+ clk_disable_unprepare(dsi->clk_lp);
+ clk_disable_unprepare(dsi->clk);
+
+ regulator_disable(dsi->vdd);
+
+ return 0;
+}
+
+static int tegra_dsi_power_on(struct tegra_dsi *dsi)
+{
+ int err;
+
+ err = regulator_enable(dsi->vdd);
+ if (err < 0) {
+ dev_err(dsi->dev, "failed to enable VDD supply: %d\n", err);
+ return err;
+ }
+
+ err = clk_prepare_enable(dsi->clk);
+ if (err < 0) {
+ dev_err(dsi->dev, "cannot enable DSI clock: %d\n", err);
+ goto disable_vdd;
+ }
+
+ err = clk_prepare_enable(dsi->clk_lp);
+ if (err < 0) {
+ dev_err(dsi->dev, "cannot enable low-power clock: %d\n", err);
+ goto disable_clk;
+ }
+
+ usleep_range(1000, 2000);
+
+ if (dsi->rst) {
+ err = reset_control_deassert(dsi->rst);
+ if (err < 0) {
+ dev_err(dsi->dev, "cannot assert reset: %d\n", err);
+ goto disable_clk_lp;
+ }
+ }
+
+ return 0;
+
+disable_clk_lp:
+ clk_disable_unprepare(dsi->clk_lp);
+disable_clk:
+ clk_disable_unprepare(dsi->clk);
+disable_vdd:
+ regulator_disable(dsi->vdd);
+ return err;
+}
+
static int tegra_dsi_probe(struct platform_device *pdev)
{
struct tegra_dsi *dsi;
@@ -1579,6 +1644,12 @@ static int tegra_dsi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dsi);
pm_runtime_enable(&pdev->dev);

+ if (!pm_runtime_enabled(&pdev->dev)) {
+ err = tegra_dsi_power_on(dsi);
+ if (err)
+ goto unregister;
+ }
+
INIT_LIST_HEAD(&dsi->client.list);
dsi->client.ops = &dsi_client_ops;
dsi->client.dev = &pdev->dev;
@@ -1604,8 +1675,6 @@ static int tegra_dsi_remove(struct platform_device *pdev)
struct tegra_dsi *dsi = platform_get_drvdata(pdev);
int err;

- pm_runtime_disable(&pdev->dev);
-
err = host1x_client_unregister(&dsi->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
@@ -1618,6 +1687,14 @@ static int tegra_dsi_remove(struct platform_device *pdev)
mipi_dsi_host_unregister(&dsi->host);
tegra_mipi_free(dsi->mipi);

+ if (!pm_runtime_enabled(&pdev->dev)) {
+ err = tegra_dsi_power_off(dsi);
+ if (err)
+ return err;
+ }
+
+ pm_runtime_disable(&pdev->dev);
+
return 0;
}

@@ -1625,68 +1702,15 @@ static int tegra_dsi_remove(struct platform_device *pdev)
static int tegra_dsi_suspend(struct device *dev)
{
struct tegra_dsi *dsi = dev_get_drvdata(dev);
- int err;
-
- if (dsi->rst) {
- err = reset_control_assert(dsi->rst);
- if (err < 0) {
- dev_err(dev, "failed to assert reset: %d\n", err);
- return err;
- }
- }
-
- usleep_range(1000, 2000);
-
- clk_disable_unprepare(dsi->clk_lp);
- clk_disable_unprepare(dsi->clk);
-
- regulator_disable(dsi->vdd);

- return 0;
+ return tegra_dsi_power_off(dsi);
}

static int tegra_dsi_resume(struct device *dev)
{
struct tegra_dsi *dsi = dev_get_drvdata(dev);
- int err;

- err = regulator_enable(dsi->vdd);
- if (err < 0) {
- dev_err(dsi->dev, "failed to enable VDD supply: %d\n", err);
- return err;
- }
-
- err = clk_prepare_enable(dsi->clk);
- if (err < 0) {
- dev_err(dev, "cannot enable DSI clock: %d\n", err);
- goto disable_vdd;
- }
-
- err = clk_prepare_enable(dsi->clk_lp);
- if (err < 0) {
- dev_err(dev, "cannot enable low-power clock: %d\n", err);
- goto disable_clk;
- }
-
- usleep_range(1000, 2000);
-
- if (dsi->rst) {
- err = reset_control_deassert(dsi->rst);
- if (err < 0) {
- dev_err(dev, "cannot assert reset: %d\n", err);
- goto disable_clk_lp;
- }
- }
-
- return 0;
-
-disable_clk_lp:
- clk_disable_unprepare(dsi->clk_lp);
-disable_clk:
- clk_disable_unprepare(dsi->clk);
-disable_vdd:
- regulator_disable(dsi->vdd);
- return err;
+ return tegra_dsi_power_on(dsi);
}
#endif

diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index 6434b3d3d1ba..c8c668b44a4b 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -1664,6 +1664,45 @@ static irqreturn_t tegra_hdmi_irq(int irq, void *data)
return IRQ_HANDLED;
}

+static int tegra_hdmi_power_off(struct tegra_hdmi *hdmi)
+{
+ int err;
+
+ err = reset_control_assert(hdmi->rst);
+ if (err < 0) {
+ dev_err(hdmi->dev, "failed to assert reset: %d\n", err);
+ return err;
+ }
+
+ usleep_range(1000, 2000);
+
+ clk_disable_unprepare(hdmi->clk);
+
+ return 0;
+}
+
+static int tegra_hdmi_power_on(struct tegra_hdmi *hdmi)
+{
+ int err;
+
+ err = clk_prepare_enable(hdmi->clk);
+ if (err < 0) {
+ dev_err(hdmi->dev, "failed to enable clock: %d\n", err);
+ return err;
+ }
+
+ usleep_range(1000, 2000);
+
+ err = reset_control_deassert(hdmi->rst);
+ if (err < 0) {
+ dev_err(hdmi->dev, "failed to deassert reset: %d\n", err);
+ clk_disable_unprepare(hdmi->clk);
+ return err;
+ }
+
+ return 0;
+}
+
static int tegra_hdmi_probe(struct platform_device *pdev)
{
struct tegra_hdmi *hdmi;
@@ -1755,6 +1794,12 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, hdmi);
pm_runtime_enable(&pdev->dev);

+ if (!pm_runtime_enabled(&pdev->dev)) {
+ err = tegra_hdmi_power_on(hdmi);
+ if (err)
+ return err;
+ }
+
INIT_LIST_HEAD(&hdmi->client.list);
hdmi->client.ops = &hdmi_client_ops;
hdmi->client.dev = &pdev->dev;
@@ -1763,6 +1808,10 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
if (err < 0) {
dev_err(&pdev->dev, "failed to register host1x client: %d\n",
err);
+
+ if (!pm_runtime_enabled(&pdev->dev))
+ tegra_hdmi_power_off(hdmi);
+
return err;
}

@@ -1774,8 +1823,6 @@ static int tegra_hdmi_remove(struct platform_device *pdev)
struct tegra_hdmi *hdmi = platform_get_drvdata(pdev);
int err;

- pm_runtime_disable(&pdev->dev);
-
err = host1x_client_unregister(&hdmi->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
@@ -1788,6 +1835,14 @@ static int tegra_hdmi_remove(struct platform_device *pdev)
if (hdmi->output.notifier)
cec_notifier_put(hdmi->output.notifier);

+ if (!pm_runtime_enabled(&pdev->dev)) {
+ err = tegra_hdmi_power_off(hdmi);
+ if (err)
+ return err;
+ }
+
+ pm_runtime_disable(&pdev->dev);
+
return 0;
}

@@ -1795,42 +1850,15 @@ static int tegra_hdmi_remove(struct platform_device *pdev)
static int tegra_hdmi_suspend(struct device *dev)
{
struct tegra_hdmi *hdmi = dev_get_drvdata(dev);
- int err;

- err = reset_control_assert(hdmi->rst);
- if (err < 0) {
- dev_err(dev, "failed to assert reset: %d\n", err);
- return err;
- }
-
- usleep_range(1000, 2000);
-
- clk_disable_unprepare(hdmi->clk);
-
- return 0;
+ return tegra_hdmi_power_off(hdmi);
}

static int tegra_hdmi_resume(struct device *dev)
{
struct tegra_hdmi *hdmi = dev_get_drvdata(dev);
- int err;

- err = clk_prepare_enable(hdmi->clk);
- if (err < 0) {
- dev_err(dev, "failed to enable clock: %d\n", err);
- return err;
- }
-
- usleep_range(1000, 2000);
-
- err = reset_control_deassert(hdmi->rst);
- if (err < 0) {
- dev_err(dev, "failed to deassert reset: %d\n", err);
- clk_disable_unprepare(hdmi->clk);
- return err;
- }
-
- return 0;
+ return tegra_hdmi_power_on(hdmi);
}
#endif

diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index b0a1dedac802..a98b2b0bd679 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -2542,6 +2542,50 @@ static const struct of_device_id tegra_sor_of_match[] = {
};
MODULE_DEVICE_TABLE(of, tegra_sor_of_match);

+static int tegra_sor_power_off(struct tegra_sor *sor)
+{
+ int err;
+
+ if (sor->rst) {
+ err = reset_control_assert(sor->rst);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to assert reset: %d\n", err);
+ return err;
+ }
+ }
+
+ usleep_range(1000, 2000);
+
+ clk_disable_unprepare(sor->clk);
+
+ return 0;
+}
+
+static int tegra_sor_power_on(struct tegra_sor *sor)
+{
+ int err;
+
+ err = clk_prepare_enable(sor->clk);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to enable clock: %d\n", err);
+ return err;
+ }
+
+ usleep_range(1000, 2000);
+
+ if (sor->rst) {
+ err = reset_control_deassert(sor->rst);
+ if (err < 0) {
+ dev_err(sor->dev, "failed to deassert reset: %d\n",
+ err);
+ clk_disable_unprepare(sor->clk);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
static int tegra_sor_probe(struct platform_device *pdev)
{
struct device_node *np;
@@ -2712,6 +2756,12 @@ static int tegra_sor_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, sor);
pm_runtime_enable(&pdev->dev);

+ if (!pm_runtime_enabled(&pdev->dev)) {
+ err = tegra_sor_power_on(sor);
+ if (err)
+ goto remove;
+ }
+
/*
* On Tegra210 and earlier, provide our own implementation for the
* pad output clock.
@@ -2721,7 +2771,7 @@ static int tegra_sor_probe(struct platform_device *pdev)
if (err < 0) {
dev_err(&pdev->dev, "failed to get runtime PM: %d\n",
err);
- goto remove;
+ goto poweroff;
}

sor->clk_pad = tegra_clk_sor_pad_register(sor,
@@ -2733,7 +2783,7 @@ static int tegra_sor_probe(struct platform_device *pdev)
err = PTR_ERR(sor->clk_pad);
dev_err(&pdev->dev, "failed to register SOR pad clock: %d\n",
err);
- goto remove;
+ goto poweroff;
}

INIT_LIST_HEAD(&sor->client.list);
@@ -2744,11 +2794,14 @@ static int tegra_sor_probe(struct platform_device *pdev)
if (err < 0) {
dev_err(&pdev->dev, "failed to register host1x client: %d\n",
err);
- goto remove;
+ goto poweroff;
}

return 0;

+poweroff:
+ if (!pm_runtime_enabled(&pdev->dev))
+ tegra_sor_power_off(sor);
remove:
if (sor->ops && sor->ops->remove)
sor->ops->remove(sor);
@@ -2762,8 +2815,6 @@ static int tegra_sor_remove(struct platform_device *pdev)
struct tegra_sor *sor = platform_get_drvdata(pdev);
int err;

- pm_runtime_disable(&pdev->dev);
-
err = host1x_client_unregister(&sor->client);
if (err < 0) {
dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
@@ -2779,6 +2830,13 @@ static int tegra_sor_remove(struct platform_device *pdev)

tegra_output_remove(&sor->output);

+ if (!pm_runtime_enabled(&pdev->dev)) {
+ err = tegra_sor_power_off(sor);
+ if (err)
+ return err;
+ }
+ pm_runtime_disable(&pdev->dev);
+
return 0;
}

@@ -2786,46 +2844,15 @@ static int tegra_sor_remove(struct platform_device *pdev)
static int tegra_sor_suspend(struct device *dev)
{
struct tegra_sor *sor = dev_get_drvdata(dev);
- int err;
-
- if (sor->rst) {
- err = reset_control_assert(sor->rst);
- if (err < 0) {
- dev_err(dev, "failed to assert reset: %d\n", err);
- return err;
- }
- }
-
- usleep_range(1000, 2000);

- clk_disable_unprepare(sor->clk);
-
- return 0;
+ return tegra_sor_power_off(sor);
}

static int tegra_sor_resume(struct device *dev)
{
struct tegra_sor *sor = dev_get_drvdata(dev);
- int err;
-
- err = clk_prepare_enable(sor->clk);
- if (err < 0) {
- dev_err(dev, "failed to enable clock: %d\n", err);
- return err;
- }
-
- usleep_range(1000, 2000);
-
- if (sor->rst) {
- err = reset_control_deassert(sor->rst);
- if (err < 0) {
- dev_err(dev, "failed to deassert reset: %d\n", err);
- clk_disable_unprepare(sor->clk);
- return err;
- }
- }

- return 0;
+ return tegra_sor_power_on(sor);
}
#endif

--
2.15.1