mirror of
https://github.com/torvalds/linux.git
synced 2026-04-19 23:34:00 -04:00
Currently DP driver is executed independent of PM runtime framework. This leads msm eDP panel can not being detected by edp_panel driver during generic_edp_panel_probe() due to AUX DPCD read failed at edp panel driver. Incorporate PM runtime framework into DP driver so that host controller's power and clocks are enable/disable through PM runtime mechanism. Once PM runtime framework is incorporated into DP driver, waking up device from power up path is not necessary. Hence remove it. After incorporating pm_runtime framework into eDP/DP driver, dp_pm_suspend() to handle power off both DP phy and controller during suspend and dp_pm_resume() to handle power on both DP phy and controller during resume are not necessary. Therefore both dp_pm_suspend() and dp_pm_resume() are dropped and replace with dp_pm_runtime_suspend() and dp_pm_runtime_resume() respectively. Changes in v9: -- silent compiler warning message at dp_power_init() and dp_power_deinit() with W1 flag Changes in v7: -- add comments to dp_pm_runtime_resume() -- add comments to dp_bridge_hpd_enable() -- delete dp->hpd_state = ST_DISCONNECTED from dp_bridge_hpd_notify() Changes in v6: -- delete dp_power_client_deinit(dp->power); -- remove if (!dp->dp_display.is_edp) condition checkout at plug_handle() -- remove if (!dp->dp_display.is_edp) condition checkout at unplug_handle() -- add IRQF_NO_AUTOEN to devm_request_irq() -- add enable_irq() and disable_irq() to pm_runtime_resume()/suspend() -- del dp->hpd_state = ST_DISCONNECTED from dp_bridge_hpd_disable() Changes in v5: -- remove pm_runtime_put_autosuspend feature, use pm_runtime_put_sync() -- squash add pm_runtime_force_suspend()/resume() patch into this patch Changes in v4: -- reworded commit text to explain why pm_framework is required for edp panel -- reworded commit text to explain autosuspend is choiced -- delete EV_POWER_PM_GET and PM_EV_POWER_PUT from changes #3 -- delete dp_display_pm_get() and dp_display_pm_Put() from changes #3 -- return value from pm_runtime_resume_and_get() directly -- check return value of devm_pm_runtime_enable() -- delete pm_runtime_xxx from dp_display_remove() -- drop dp_display_host_init() from EV_HPD_INIT_SETUP -- drop both dp_pm_prepare() and dp_pm_compete() from this change -- delete ST_SUSPENDED state -- rewording commit text to add more details regrading the purpose of this change Changes in v3: -- incorporate removing pm_runtime_xx() from dp_pwer.c to this patch -- use pm_runtime_resume_and_get() instead of pm_runtime_get() -- error checking pm_runtime_resume_and_get() return value -- add EV_POWER_PM_GET and PM_EV_POWER_PUT to handle HPD_GPIO case -- replace dp_pm_suspend() with pm_runtime_force_suspend() -- replace dp_pm_resume() with pm_runtime_force_resume() Signed-off-by: Kuogee Hsieh <quic_khsieh@quicinc.com> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> Patchwork: https://patchwork.freedesktop.org/patch/570073/ Link: https://lore.kernel.org/r/1701472789-25951-6-git-send-email-quic_khsieh@quicinc.com Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
184 lines
4.2 KiB
C
184 lines
4.2 KiB
C
// 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);
|
|
|
|
return dp_power_clk_init(power);
|
|
}
|
|
|
|
int dp_power_init(struct dp_power *dp_power)
|
|
{
|
|
return dp_power_clk_enable(dp_power, DP_CORE_PM, true);
|
|
}
|
|
|
|
int dp_power_deinit(struct dp_power *dp_power)
|
|
{
|
|
return dp_power_clk_enable(dp_power, DP_CORE_PM, false);
|
|
}
|
|
|
|
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;
|
|
}
|