diff options
Diffstat (limited to 'drivers/gpu/drm/msm/dp/dp_power.c')
-rw-r--r-- | drivers/gpu/drm/msm/dp/dp_power.c | 211 |
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; +} |