summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/msm/dp/dp_power.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/msm/dp/dp_power.c')
-rw-r--r--drivers/gpu/drm/msm/dp/dp_power.c211
1 files changed, 211 insertions, 0 deletions
diff --git a/drivers/gpu/drm/msm/dp/dp_power.c b/drivers/gpu/drm/msm/dp/dp_power.c
new file mode 100644
index 0000000000..5cb84ca40e
--- /dev/null
+++ b/drivers/gpu/drm/msm/dp/dp_power.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
+ */
+
+#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_opp.h>
+#include "dp_power.h"
+#include "msm_drv.h"
+
+struct dp_power_private {
+ struct dp_parser *parser;
+ struct device *dev;
+ struct drm_device *drm_dev;
+ struct clk *link_clk_src;
+ struct clk *pixel_provider;
+ struct clk *link_provider;
+
+ struct dp_power dp_power;
+};
+
+static int dp_power_clk_init(struct dp_power_private *power)
+{
+ int rc = 0;
+ struct dss_module_power *core, *ctrl, *stream;
+ struct device *dev = power->dev;
+
+ core = &power->parser->mp[DP_CORE_PM];
+ ctrl = &power->parser->mp[DP_CTRL_PM];
+ stream = &power->parser->mp[DP_STREAM_PM];
+
+ rc = devm_clk_bulk_get(dev, core->num_clk, core->clocks);
+ if (rc)
+ return rc;
+
+ rc = devm_clk_bulk_get(dev, ctrl->num_clk, ctrl->clocks);
+ if (rc)
+ return -ENODEV;
+
+ rc = devm_clk_bulk_get(dev, stream->num_clk, stream->clocks);
+ if (rc)
+ return -ENODEV;
+
+ return 0;
+}
+
+int dp_power_clk_status(struct dp_power *dp_power, enum dp_pm_type pm_type)
+{
+ struct dp_power_private *power;
+
+ power = container_of(dp_power, struct dp_power_private, dp_power);
+
+ drm_dbg_dp(power->drm_dev,
+ "core_clk_on=%d link_clk_on=%d stream_clk_on=%d\n",
+ dp_power->core_clks_on, dp_power->link_clks_on, dp_power->stream_clks_on);
+
+ if (pm_type == DP_CORE_PM)
+ return dp_power->core_clks_on;
+
+ if (pm_type == DP_CTRL_PM)
+ return dp_power->link_clks_on;
+
+ if (pm_type == DP_STREAM_PM)
+ return dp_power->stream_clks_on;
+
+ return 0;
+}
+
+int dp_power_clk_enable(struct dp_power *dp_power,
+ enum dp_pm_type pm_type, bool enable)
+{
+ int rc = 0;
+ struct dp_power_private *power;
+ struct dss_module_power *mp;
+
+ power = container_of(dp_power, struct dp_power_private, dp_power);
+
+ if (pm_type != DP_CORE_PM && pm_type != DP_CTRL_PM &&
+ pm_type != DP_STREAM_PM) {
+ DRM_ERROR("unsupported power module: %s\n",
+ dp_parser_pm_name(pm_type));
+ return -EINVAL;
+ }
+
+ if (enable) {
+ if (pm_type == DP_CORE_PM && dp_power->core_clks_on) {
+ drm_dbg_dp(power->drm_dev,
+ "core clks already enabled\n");
+ return 0;
+ }
+
+ if (pm_type == DP_CTRL_PM && dp_power->link_clks_on) {
+ drm_dbg_dp(power->drm_dev,
+ "links clks already enabled\n");
+ return 0;
+ }
+
+ if (pm_type == DP_STREAM_PM && dp_power->stream_clks_on) {
+ drm_dbg_dp(power->drm_dev,
+ "pixel clks already enabled\n");
+ return 0;
+ }
+
+ if ((pm_type == DP_CTRL_PM) && (!dp_power->core_clks_on)) {
+ drm_dbg_dp(power->drm_dev,
+ "Enable core clks before link clks\n");
+ mp = &power->parser->mp[DP_CORE_PM];
+
+ rc = clk_bulk_prepare_enable(mp->num_clk, mp->clocks);
+ if (rc)
+ return rc;
+
+ dp_power->core_clks_on = true;
+ }
+ }
+
+ mp = &power->parser->mp[pm_type];
+ if (enable) {
+ rc = clk_bulk_prepare_enable(mp->num_clk, mp->clocks);
+ if (rc)
+ return rc;
+ } else {
+ clk_bulk_disable_unprepare(mp->num_clk, mp->clocks);
+ }
+
+ if (pm_type == DP_CORE_PM)
+ dp_power->core_clks_on = enable;
+ else if (pm_type == DP_STREAM_PM)
+ dp_power->stream_clks_on = enable;
+ else
+ dp_power->link_clks_on = enable;
+
+ drm_dbg_dp(power->drm_dev, "%s clocks for %s\n",
+ enable ? "enable" : "disable",
+ dp_parser_pm_name(pm_type));
+ drm_dbg_dp(power->drm_dev,
+ "strem_clks:%s link_clks:%s core_clks:%s\n",
+ dp_power->stream_clks_on ? "on" : "off",
+ dp_power->link_clks_on ? "on" : "off",
+ dp_power->core_clks_on ? "on" : "off");
+
+ return 0;
+}
+
+int dp_power_client_init(struct dp_power *dp_power)
+{
+ struct dp_power_private *power;
+
+ power = container_of(dp_power, struct dp_power_private, dp_power);
+
+ pm_runtime_enable(power->dev);
+
+ return dp_power_clk_init(power);
+}
+
+void dp_power_client_deinit(struct dp_power *dp_power)
+{
+ struct dp_power_private *power;
+
+ power = container_of(dp_power, struct dp_power_private, dp_power);
+
+ pm_runtime_disable(power->dev);
+}
+
+int dp_power_init(struct dp_power *dp_power)
+{
+ int rc = 0;
+ struct dp_power_private *power = NULL;
+
+ power = container_of(dp_power, struct dp_power_private, dp_power);
+
+ pm_runtime_get_sync(power->dev);
+
+ rc = dp_power_clk_enable(dp_power, DP_CORE_PM, true);
+ if (rc)
+ pm_runtime_put_sync(power->dev);
+
+ return rc;
+}
+
+int dp_power_deinit(struct dp_power *dp_power)
+{
+ struct dp_power_private *power;
+
+ power = container_of(dp_power, struct dp_power_private, dp_power);
+
+ dp_power_clk_enable(dp_power, DP_CORE_PM, false);
+ pm_runtime_put_sync(power->dev);
+ return 0;
+}
+
+struct dp_power *dp_power_get(struct device *dev, struct dp_parser *parser)
+{
+ struct dp_power_private *power;
+ struct dp_power *dp_power;
+
+ power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL);
+ if (!power)
+ return ERR_PTR(-ENOMEM);
+
+ power->parser = parser;
+ power->dev = dev;
+
+ dp_power = &power->dp_power;
+
+ return dp_power;
+}