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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [ARM Kernel]     [Linux ARM]     [Linux ARM MSM]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux