diff --git a/msm/dp/dp_power.c b/msm/dp/dp_power.c index ccb7063bf5..cc44c7d146 100644 --- a/msm/dp/dp_power.c +++ b/msm/dp/dp_power.c @@ -11,6 +11,7 @@ #include "dp_pll.h" #define DP_CLIENT_NAME_SIZE 20 +#define XO_CLK_KHZ 19200 struct dp_power_private { struct dp_parser *parser; @@ -19,6 +20,7 @@ struct dp_power_private { struct clk *pixel_clk_rcg; struct clk *pixel_parent; struct clk *pixel1_clk_rcg; + struct clk *xo_clk; struct dp_power dp_power; @@ -26,6 +28,8 @@ struct dp_power_private { bool link_clks_on; bool strm0_clks_on; bool strm1_clks_on; + bool strm0_clks_parked; + bool strm1_clks_parked; }; static int dp_power_regulator_init(struct dp_power_private *power) @@ -220,6 +224,14 @@ static int dp_power_clk_init(struct dp_power_private *power, bool enable) goto err_pixel_parent; } + power->xo_clk = clk_get(dev, "rpmh_cxo_clk"); + if (IS_ERR(power->xo_clk)) { + DP_ERR("Unable to get XO clk: %d\n", PTR_ERR(power->xo_clk)); + rc = PTR_ERR(power->xo_clk); + power->xo_clk = NULL; + goto err_xo_clk; + } + if (power->parser->has_mst) { power->pixel1_clk_rcg = clk_get(dev, "pixel1_clk_rcg"); if (IS_ERR(power->pixel1_clk_rcg)) { @@ -244,8 +256,9 @@ static int dp_power_clk_init(struct dp_power_private *power, bool enable) } return rc; - err_pixel1_clk_rcg: + clk_put(power->xo_clk); +err_xo_clk: clk_put(power->pixel_parent); err_pixel_parent: clk_put(power->pixel_clk_rcg); @@ -255,6 +268,59 @@ exit: return rc; } +static int dp_power_park_module(struct dp_power_private *power, enum dp_pm_type module) +{ + struct dss_module_power *mp; + struct clk *clk = NULL; + int rc = 0; + bool *parked; + + mp = &power->parser->mp[module]; + + if (module == DP_STREAM0_PM) { + clk = power->pixel_clk_rcg; + parked = &power->strm0_clks_parked; + } else if (module == DP_STREAM1_PM) { + clk = power->pixel1_clk_rcg; + parked = &power->strm1_clks_parked; + } else { + goto exit; + } + + if (!clk) { + DP_WARN("clk type %d not supported\n", module); + rc = -EINVAL; + goto exit; + } + + if (!power->xo_clk) { + rc = -EINVAL; + goto exit; + } + + if (*parked) + goto exit; + + rc = clk_set_parent(clk, power->xo_clk); + if (rc) { + DP_ERR("unable to set xo parent on clk %d\n", module); + goto exit; + } + + mp->clk_config->rate = XO_CLK_KHZ; + rc = msm_dss_clk_set_rate(mp->clk_config, mp->num_clk); + if (rc) { + DP_ERR("failed to set clk rate.\n"); + goto exit; + } + + *parked = true; + +exit: + return rc; +} + + static int dp_power_clk_set_rate(struct dp_power_private *power, enum dp_pm_type module, bool enable) { @@ -287,6 +353,8 @@ static int dp_power_clk_set_rate(struct dp_power_private *power, DP_ERR("failed to disable clks\n"); goto exit; } + + dp_power_park_module(power, module); } exit: return rc; @@ -367,6 +435,11 @@ static int dp_power_clk_enable(struct dp_power *dp_power, else if (pm_type == DP_LINK_PM) power->link_clks_on = enable; + if (pm_type == DP_STREAM0_PM) + power->strm0_clks_parked = false; + if (pm_type == DP_STREAM1_PM) + power->strm1_clks_parked = false; + /* * This log is printed only when user connects or disconnects * a DP cable. As this is a user-action and not a frequent @@ -581,6 +654,34 @@ static void dp_power_client_deinit(struct dp_power *dp_power) dp_power_regulator_deinit(power); } +static int dp_power_park_clocks(struct dp_power *dp_power) +{ + int rc = 0; + struct dp_power_private *power; + + if (!dp_power) { + DP_ERR("invalid power data\n"); + return -EINVAL; + } + + power = container_of(dp_power, struct dp_power_private, dp_power); + + rc = dp_power_park_module(power, DP_STREAM0_PM); + if (rc) { + DP_ERR("failed to park stream 0. err=%d\n", rc); + goto error; + } + + rc = dp_power_park_module(power, DP_STREAM1_PM); + if (rc) { + DP_ERR("failed to park stream 1. err=%d\n", rc); + goto error; + } + +error: + return rc; +} + static int dp_power_set_pixel_clk_parent(struct dp_power *dp_power, u32 strm_id) { int rc = 0; @@ -764,6 +865,7 @@ struct dp_power *dp_power_get(struct dp_parser *parser, struct dp_pll *pll) dp_power->clk_enable = dp_power_clk_enable; dp_power->clk_status = dp_power_clk_status; dp_power->set_pixel_clk_parent = dp_power_set_pixel_clk_parent; + dp_power->park_clocks = dp_power_park_clocks; dp_power->clk_get_rate = dp_power_clk_get_rate; dp_power->power_client_init = dp_power_client_init; dp_power->power_client_deinit = dp_power_client_deinit; diff --git a/msm/dp/dp_power.h b/msm/dp/dp_power.h index 8d0c7d08f4..86d7247a38 100644 --- a/msm/dp/dp_power.h +++ b/msm/dp/dp_power.h @@ -18,6 +18,7 @@ * @clk_enable: enable/disable the DP clocks * @clk_status: check for clock status * @set_pixel_clk_parent: set the parent of DP pixel clock + * @park_clocks: park all clocks driven by PLL * @clk_get_rate: get the current rate for provided clk_name * @power_client_init: configures clocks and regulators * @power_client_deinit: frees clock and regulator resources @@ -32,6 +33,7 @@ struct dp_power { bool enable); bool (*clk_status)(struct dp_power *power, enum dp_pm_type pm_type); int (*set_pixel_clk_parent)(struct dp_power *power, u32 stream_id); + int (*park_clocks)(struct dp_power *power); u64 (*clk_get_rate)(struct dp_power *power, char *clk_name); int (*power_client_init)(struct dp_power *power, struct sde_power_handle *phandle,