Commit 0cffbde2 authored by Thierry Reding's avatar Thierry Reding
Browse files

drm/tegra: hub: Enable all required clocks



The display architecture on Tegra186 and Tegra194 requires that there be
some valid clock on all domains before accessing any display register. A
further requirement is that in addition to the host1x, hub, disp and dsc
clocks, all the head clocks (pclk0-2 on Tegra186 or pclk0-3 on Tegra194)
must also be enabled.

Implement this logic within the display hub driver to ensure the clocks
are always enabled at the right time.

Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent d6b9bc02
Loading
Loading
Loading
Loading
+46 −2
Original line number Diff line number Diff line
@@ -742,7 +742,9 @@ static const struct host1x_client_ops tegra_display_hub_ops = {

static int tegra_display_hub_probe(struct platform_device *pdev)
{
	struct device_node *child = NULL;
	struct tegra_display_hub *hub;
	struct clk *clk;
	unsigned int i;
	int err;

@@ -801,6 +803,34 @@ static int tegra_display_hub_probe(struct platform_device *pdev)
			return err;
	}

	hub->num_heads = of_get_child_count(pdev->dev.of_node);

	hub->clk_heads = devm_kcalloc(&pdev->dev, hub->num_heads, sizeof(clk),
				      GFP_KERNEL);
	if (!hub->clk_heads)
		return -ENOMEM;

	for (i = 0; i < hub->num_heads; i++) {
		child = of_get_next_child(pdev->dev.of_node, child);
		if (!child) {
			dev_err(&pdev->dev, "failed to find node for head %u\n",
				i);
			return -ENODEV;
		}

		clk = devm_get_clk_from_child(&pdev->dev, child, "dc");
		if (IS_ERR(clk)) {
			dev_err(&pdev->dev, "failed to get clock for head %u\n",
				i);
			of_node_put(child);
			return PTR_ERR(clk);
		}

		hub->clk_heads[i] = clk;
	}

	of_node_put(child);

	/* XXX: enable clock across reset? */
	err = reset_control_assert(hub->rst);
	if (err < 0)
@@ -840,12 +870,16 @@ static int tegra_display_hub_remove(struct platform_device *pdev)
static int __maybe_unused tegra_display_hub_suspend(struct device *dev)
{
	struct tegra_display_hub *hub = dev_get_drvdata(dev);
	unsigned int i = hub->num_heads;
	int err;

	err = reset_control_assert(hub->rst);
	if (err < 0)
		return err;

	while (i--)
		clk_disable_unprepare(hub->clk_heads[i]);

	clk_disable_unprepare(hub->clk_hub);
	clk_disable_unprepare(hub->clk_dsc);
	clk_disable_unprepare(hub->clk_disp);
@@ -856,6 +890,7 @@ static int __maybe_unused tegra_display_hub_suspend(struct device *dev)
static int __maybe_unused tegra_display_hub_resume(struct device *dev)
{
	struct tegra_display_hub *hub = dev_get_drvdata(dev);
	unsigned int i;
	int err;

	err = clk_prepare_enable(hub->clk_disp);
@@ -870,13 +905,22 @@ static int __maybe_unused tegra_display_hub_resume(struct device *dev)
	if (err < 0)
		goto disable_dsc;

	for (i = 0; i < hub->num_heads; i++) {
		err = clk_prepare_enable(hub->clk_heads[i]);
		if (err < 0)
			goto disable_heads;
	}

	err = reset_control_deassert(hub->rst);
	if (err < 0)
		goto disable_hub;
		goto disable_heads;

	return 0;

disable_hub:
disable_heads:
	while (i--)
		clk_disable_unprepare(hub->clk_heads[i]);

	clk_disable_unprepare(hub->clk_hub);
disable_dsc:
	clk_disable_unprepare(hub->clk_dsc);
+3 −0
Original line number Diff line number Diff line
@@ -49,6 +49,9 @@ struct tegra_display_hub {
	struct clk *clk_hub;
	struct reset_control *rst;

	unsigned int num_heads;
	struct clk **clk_heads;

	const struct tegra_display_hub_soc *soc;
	struct tegra_windowgroup *wgrps;
};