mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-24 05:04:00 +08:00
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: Thierry Reding <treding@nvidia.com>
This commit is contained in:
parent
d6b9bc0258
commit
0cffbde2e3
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user