1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
4 */
5
6 #define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
7
8 #include <linux/clk.h>
9 #include <linux/clk-provider.h>
10 #include <linux/regulator/consumer.h>
11 #include <linux/pm_opp.h>
12 #include "dp_power.h"
13 #include "msm_drv.h"
14
15 struct dp_power_private {
16 struct dp_parser *parser;
17 struct device *dev;
18 struct drm_device *drm_dev;
19 struct clk *link_clk_src;
20 struct clk *pixel_provider;
21 struct clk *link_provider;
22
23 struct dp_power dp_power;
24 };
25
dp_power_clk_init(struct dp_power_private * power)26 static int dp_power_clk_init(struct dp_power_private *power)
27 {
28 int rc = 0;
29 struct dss_module_power *core, *ctrl, *stream;
30 struct device *dev = power->dev;
31
32 core = &power->parser->mp[DP_CORE_PM];
33 ctrl = &power->parser->mp[DP_CTRL_PM];
34 stream = &power->parser->mp[DP_STREAM_PM];
35
36 rc = devm_clk_bulk_get(dev, core->num_clk, core->clocks);
37 if (rc)
38 return rc;
39
40 rc = devm_clk_bulk_get(dev, ctrl->num_clk, ctrl->clocks);
41 if (rc)
42 return -ENODEV;
43
44 rc = devm_clk_bulk_get(dev, stream->num_clk, stream->clocks);
45 if (rc)
46 return -ENODEV;
47
48 return 0;
49 }
50
dp_power_clk_status(struct dp_power * dp_power,enum dp_pm_type pm_type)51 int dp_power_clk_status(struct dp_power *dp_power, enum dp_pm_type pm_type)
52 {
53 struct dp_power_private *power;
54
55 power = container_of(dp_power, struct dp_power_private, dp_power);
56
57 drm_dbg_dp(power->drm_dev,
58 "core_clk_on=%d link_clk_on=%d stream_clk_on=%d\n",
59 dp_power->core_clks_on, dp_power->link_clks_on, dp_power->stream_clks_on);
60
61 if (pm_type == DP_CORE_PM)
62 return dp_power->core_clks_on;
63
64 if (pm_type == DP_CTRL_PM)
65 return dp_power->link_clks_on;
66
67 if (pm_type == DP_STREAM_PM)
68 return dp_power->stream_clks_on;
69
70 return 0;
71 }
72
dp_power_clk_enable(struct dp_power * dp_power,enum dp_pm_type pm_type,bool enable)73 int dp_power_clk_enable(struct dp_power *dp_power,
74 enum dp_pm_type pm_type, bool enable)
75 {
76 int rc = 0;
77 struct dp_power_private *power;
78 struct dss_module_power *mp;
79
80 power = container_of(dp_power, struct dp_power_private, dp_power);
81
82 if (pm_type != DP_CORE_PM && pm_type != DP_CTRL_PM &&
83 pm_type != DP_STREAM_PM) {
84 DRM_ERROR("unsupported power module: %s\n",
85 dp_parser_pm_name(pm_type));
86 return -EINVAL;
87 }
88
89 if (enable) {
90 if (pm_type == DP_CORE_PM && dp_power->core_clks_on) {
91 drm_dbg_dp(power->drm_dev,
92 "core clks already enabled\n");
93 return 0;
94 }
95
96 if (pm_type == DP_CTRL_PM && dp_power->link_clks_on) {
97 drm_dbg_dp(power->drm_dev,
98 "links clks already enabled\n");
99 return 0;
100 }
101
102 if (pm_type == DP_STREAM_PM && dp_power->stream_clks_on) {
103 drm_dbg_dp(power->drm_dev,
104 "pixel clks already enabled\n");
105 return 0;
106 }
107
108 if ((pm_type == DP_CTRL_PM) && (!dp_power->core_clks_on)) {
109 drm_dbg_dp(power->drm_dev,
110 "Enable core clks before link clks\n");
111 mp = &power->parser->mp[DP_CORE_PM];
112
113 rc = clk_bulk_prepare_enable(mp->num_clk, mp->clocks);
114 if (rc)
115 return rc;
116
117 dp_power->core_clks_on = true;
118 }
119 }
120
121 mp = &power->parser->mp[pm_type];
122 if (enable) {
123 rc = clk_bulk_prepare_enable(mp->num_clk, mp->clocks);
124 if (rc)
125 return rc;
126 } else {
127 clk_bulk_disable_unprepare(mp->num_clk, mp->clocks);
128 }
129
130 if (pm_type == DP_CORE_PM)
131 dp_power->core_clks_on = enable;
132 else if (pm_type == DP_STREAM_PM)
133 dp_power->stream_clks_on = enable;
134 else
135 dp_power->link_clks_on = enable;
136
137 drm_dbg_dp(power->drm_dev, "%s clocks for %s\n",
138 enable ? "enable" : "disable",
139 dp_parser_pm_name(pm_type));
140 drm_dbg_dp(power->drm_dev,
141 "strem_clks:%s link_clks:%s core_clks:%s\n",
142 dp_power->stream_clks_on ? "on" : "off",
143 dp_power->link_clks_on ? "on" : "off",
144 dp_power->core_clks_on ? "on" : "off");
145
146 return 0;
147 }
148
dp_power_client_init(struct dp_power * dp_power)149 int dp_power_client_init(struct dp_power *dp_power)
150 {
151 struct dp_power_private *power;
152
153 power = container_of(dp_power, struct dp_power_private, dp_power);
154
155 pm_runtime_enable(power->dev);
156
157 return dp_power_clk_init(power);
158 }
159
dp_power_client_deinit(struct dp_power * dp_power)160 void dp_power_client_deinit(struct dp_power *dp_power)
161 {
162 struct dp_power_private *power;
163
164 power = container_of(dp_power, struct dp_power_private, dp_power);
165
166 pm_runtime_disable(power->dev);
167 }
168
dp_power_init(struct dp_power * dp_power)169 int dp_power_init(struct dp_power *dp_power)
170 {
171 int rc = 0;
172 struct dp_power_private *power = NULL;
173
174 power = container_of(dp_power, struct dp_power_private, dp_power);
175
176 pm_runtime_get_sync(power->dev);
177
178 rc = dp_power_clk_enable(dp_power, DP_CORE_PM, true);
179 if (rc)
180 pm_runtime_put_sync(power->dev);
181
182 return rc;
183 }
184
dp_power_deinit(struct dp_power * dp_power)185 int dp_power_deinit(struct dp_power *dp_power)
186 {
187 struct dp_power_private *power;
188
189 power = container_of(dp_power, struct dp_power_private, dp_power);
190
191 dp_power_clk_enable(dp_power, DP_CORE_PM, false);
192 pm_runtime_put_sync(power->dev);
193 return 0;
194 }
195
dp_power_get(struct device * dev,struct dp_parser * parser)196 struct dp_power *dp_power_get(struct device *dev, struct dp_parser *parser)
197 {
198 struct dp_power_private *power;
199 struct dp_power *dp_power;
200
201 power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL);
202 if (!power)
203 return ERR_PTR(-ENOMEM);
204
205 power->parser = parser;
206 power->dev = dev;
207
208 dp_power = &power->dp_power;
209
210 return dp_power;
211 }
212