The DPU has some kind of idea that it wants to be able to bring up power using GPIO lines. The struct dss_gpio is however completely unused and should this be done, it should be done using the GPIO descriptor framework rather than this API which relies on the global GPIO numberspace. Delete this code before anyone hurt themselves. The inclusion of <linux/gpio.h> was abused to get some OF and IRQ headers implicitly included into the DPU utilities, make these includes explicit and push them down into the actual implementation. Cc: Rob Clark <robdclark@gmail.com> Cc: Sean Paul <sean@poorly.run> Cc: linux-arm-msm@vger.kernel.org Cc: freedreno@lists.freedesktop.org Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Rob Clark <robdclark@chromium.org>
199 lines
4.2 KiB
C
199 lines
4.2 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/* Copyright (c) 2012-2015, 2017-2018, The Linux Foundation.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/clk/clk-conf.h>
|
|
#include <linux/err.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/of.h>
|
|
|
|
#include <drm/drm_print.h>
|
|
|
|
#include "dpu_io_util.h"
|
|
|
|
void msm_dss_put_clk(struct dss_clk *clk_arry, int num_clk)
|
|
{
|
|
int i;
|
|
|
|
for (i = num_clk - 1; i >= 0; i--) {
|
|
if (clk_arry[i].clk)
|
|
clk_put(clk_arry[i].clk);
|
|
clk_arry[i].clk = NULL;
|
|
}
|
|
}
|
|
|
|
int msm_dss_get_clk(struct device *dev, struct dss_clk *clk_arry, int num_clk)
|
|
{
|
|
int i, rc = 0;
|
|
|
|
for (i = 0; i < num_clk; i++) {
|
|
clk_arry[i].clk = clk_get(dev, clk_arry[i].clk_name);
|
|
rc = PTR_ERR_OR_ZERO(clk_arry[i].clk);
|
|
if (rc) {
|
|
DEV_ERR("%pS->%s: '%s' get failed. rc=%d\n",
|
|
__builtin_return_address(0), __func__,
|
|
clk_arry[i].clk_name, rc);
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
|
|
error:
|
|
for (i--; i >= 0; i--) {
|
|
if (clk_arry[i].clk)
|
|
clk_put(clk_arry[i].clk);
|
|
clk_arry[i].clk = NULL;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int msm_dss_clk_set_rate(struct dss_clk *clk_arry, int num_clk)
|
|
{
|
|
int i, rc = 0;
|
|
|
|
for (i = 0; i < num_clk; i++) {
|
|
if (clk_arry[i].clk) {
|
|
if (clk_arry[i].type != DSS_CLK_AHB) {
|
|
DEV_DBG("%pS->%s: '%s' rate %ld\n",
|
|
__builtin_return_address(0), __func__,
|
|
clk_arry[i].clk_name,
|
|
clk_arry[i].rate);
|
|
rc = clk_set_rate(clk_arry[i].clk,
|
|
clk_arry[i].rate);
|
|
if (rc) {
|
|
DEV_ERR("%pS->%s: %s failed. rc=%d\n",
|
|
__builtin_return_address(0),
|
|
__func__,
|
|
clk_arry[i].clk_name, rc);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
DEV_ERR("%pS->%s: '%s' is not available\n",
|
|
__builtin_return_address(0), __func__,
|
|
clk_arry[i].clk_name);
|
|
rc = -EPERM;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int msm_dss_enable_clk(struct dss_clk *clk_arry, int num_clk, int enable)
|
|
{
|
|
int i, rc = 0;
|
|
|
|
if (enable) {
|
|
for (i = 0; i < num_clk; i++) {
|
|
DEV_DBG("%pS->%s: enable '%s'\n",
|
|
__builtin_return_address(0), __func__,
|
|
clk_arry[i].clk_name);
|
|
if (clk_arry[i].clk) {
|
|
rc = clk_prepare_enable(clk_arry[i].clk);
|
|
if (rc)
|
|
DEV_ERR("%pS->%s: %s en fail. rc=%d\n",
|
|
__builtin_return_address(0),
|
|
__func__,
|
|
clk_arry[i].clk_name, rc);
|
|
} else {
|
|
DEV_ERR("%pS->%s: '%s' is not available\n",
|
|
__builtin_return_address(0), __func__,
|
|
clk_arry[i].clk_name);
|
|
rc = -EPERM;
|
|
}
|
|
|
|
if (rc && i) {
|
|
msm_dss_enable_clk(&clk_arry[i - 1],
|
|
i - 1, false);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for (i = num_clk - 1; i >= 0; i--) {
|
|
DEV_DBG("%pS->%s: disable '%s'\n",
|
|
__builtin_return_address(0), __func__,
|
|
clk_arry[i].clk_name);
|
|
|
|
if (clk_arry[i].clk)
|
|
clk_disable_unprepare(clk_arry[i].clk);
|
|
else
|
|
DEV_ERR("%pS->%s: '%s' is not available\n",
|
|
__builtin_return_address(0), __func__,
|
|
clk_arry[i].clk_name);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int msm_dss_parse_clock(struct platform_device *pdev,
|
|
struct dss_module_power *mp)
|
|
{
|
|
u32 i, rc = 0;
|
|
const char *clock_name;
|
|
int num_clk = 0;
|
|
|
|
if (!pdev || !mp)
|
|
return -EINVAL;
|
|
|
|
mp->num_clk = 0;
|
|
num_clk = of_property_count_strings(pdev->dev.of_node, "clock-names");
|
|
if (num_clk <= 0) {
|
|
pr_debug("clocks are not defined\n");
|
|
return 0;
|
|
}
|
|
|
|
mp->clk_config = devm_kcalloc(&pdev->dev,
|
|
num_clk, sizeof(struct dss_clk),
|
|
GFP_KERNEL);
|
|
if (!mp->clk_config)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < num_clk; i++) {
|
|
rc = of_property_read_string_index(pdev->dev.of_node,
|
|
"clock-names", i,
|
|
&clock_name);
|
|
if (rc) {
|
|
DRM_DEV_ERROR(&pdev->dev, "Failed to get clock name for %d\n",
|
|
i);
|
|
break;
|
|
}
|
|
strlcpy(mp->clk_config[i].clk_name, clock_name,
|
|
sizeof(mp->clk_config[i].clk_name));
|
|
|
|
mp->clk_config[i].type = DSS_CLK_AHB;
|
|
}
|
|
|
|
rc = msm_dss_get_clk(&pdev->dev, mp->clk_config, num_clk);
|
|
if (rc) {
|
|
DRM_DEV_ERROR(&pdev->dev, "Failed to get clock refs %d\n", rc);
|
|
goto err;
|
|
}
|
|
|
|
rc = of_clk_set_defaults(pdev->dev.of_node, false);
|
|
if (rc) {
|
|
DRM_DEV_ERROR(&pdev->dev, "Failed to set clock defaults %d\n", rc);
|
|
goto err;
|
|
}
|
|
|
|
for (i = 0; i < num_clk; i++) {
|
|
u32 rate = clk_get_rate(mp->clk_config[i].clk);
|
|
if (!rate)
|
|
continue;
|
|
mp->clk_config[i].rate = rate;
|
|
mp->clk_config[i].type = DSS_CLK_PCLK;
|
|
}
|
|
|
|
mp->num_clk = num_clk;
|
|
return 0;
|
|
|
|
err:
|
|
msm_dss_put_clk(mp->clk_config, num_clk);
|
|
return rc;
|
|
}
|