+ usual progress on cleanups
+ dsi vs EPROBE_DEFER fixes
+ msm8998 (snapdragon 835 support)
+ a540 gpu support (mesa support already landed)
+ dsi, dsi-phy support
+ mdp5 and dpu interconnect (bus/memory scaling) support
+ initial prep work for per-context pagetables (at least the parts that
don't have external dependencies like iommu/arm-smmu)
There is one more patch for fixing DSI cmd mode panels (part of a set of
patches to get things working on nexus5), but it would be conflicty with
1cff7440a8
in drm-next without rebasing or back-merge,
and since it doesn't conflict with anything in msm-next, I think it best
if Sean merges that through drm-mix-fixes instead.
(In other news, I've been making some progress w/ getting efifb working
properly on sdm850 laptop without horrible hacks, and drm/msm + clk stuff
not totally falling over when bootloader enables display and things are
already running when driver probes.. but not quite ready yet, hopefully
we can post some of that for 5.4.. should help for both the sdm835 and
sdm850 laptops.)
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Rob Clark <robdclark@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/CAF6AEGsj3N4XzDLSDoa+4RHZ9wXObYmhcep0M3LjnRg48BeLvg@mail.gmail.com
198 lines
4.2 KiB
C
198 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 <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;
|
|
}
|