// 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 #include #include #include #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; }