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 platform_device *pdev;
18 struct device *dev;
19 struct drm_device *drm_dev;
20 struct clk *link_clk_src;
21 struct clk *pixel_provider;
22 struct clk *link_provider;
23
24 struct dp_power dp_power;
25 };
26
dp_power_clk_init(struct dp_power_private * power)27 static int dp_power_clk_init(struct dp_power_private *power)
28 {
29 int rc = 0;
30 struct dss_module_power *core, *ctrl, *stream;
31 struct device *dev = &power->pdev->dev;
32
33 core = &power->parser->mp[DP_CORE_PM];
34 ctrl = &power->parser->mp[DP_CTRL_PM];
35 stream = &power->parser->mp[DP_STREAM_PM];
36
37 rc = devm_clk_bulk_get(dev, core->num_clk, core->clocks);
38 if (rc) {
39 DRM_ERROR("failed to get %s clk. err=%d\n",
40 dp_parser_pm_name(DP_CORE_PM), rc);
41 return rc;
42 }
43
44 rc = devm_clk_bulk_get(dev, ctrl->num_clk, ctrl->clocks);
45 if (rc) {
46 DRM_ERROR("failed to get %s clk. err=%d\n",
47 dp_parser_pm_name(DP_CTRL_PM), rc);
48 return -ENODEV;
49 }
50
51 rc = devm_clk_bulk_get(dev, stream->num_clk, stream->clocks);
52 if (rc) {
53 DRM_ERROR("failed to get %s clk. err=%d\n",
54 dp_parser_pm_name(DP_CTRL_PM), rc);
55 return -ENODEV;
56 }
57
58 return 0;
59 }
60
dp_power_clk_status(struct dp_power * dp_power,enum dp_pm_type pm_type)61 int dp_power_clk_status(struct dp_power *dp_power, enum dp_pm_type pm_type)
62 {
63 struct dp_power_private *power;
64
65 power = container_of(dp_power, struct dp_power_private, dp_power);
66
67 drm_dbg_dp(power->drm_dev,
68 "core_clk_on=%d link_clk_on=%d stream_clk_on=%d\n",
69 dp_power->core_clks_on, dp_power->link_clks_on, dp_power->stream_clks_on);
70
71 if (pm_type == DP_CORE_PM)
72 return dp_power->core_clks_on;
73
74 if (pm_type == DP_CTRL_PM)
75 return dp_power->link_clks_on;
76
77 if (pm_type == DP_STREAM_PM)
78 return dp_power->stream_clks_on;
79
80 return 0;
81 }
82
dp_power_clk_enable(struct dp_power * dp_power,enum dp_pm_type pm_type,bool enable)83 int dp_power_clk_enable(struct dp_power *dp_power,
84 enum dp_pm_type pm_type, bool enable)
85 {
86 int rc = 0;
87 struct dp_power_private *power;
88 struct dss_module_power *mp;
89
90 power = container_of(dp_power, struct dp_power_private, dp_power);
91
92 if (pm_type != DP_CORE_PM && pm_type != DP_CTRL_PM &&
93 pm_type != DP_STREAM_PM) {
94 DRM_ERROR("unsupported power module: %s\n",
95 dp_parser_pm_name(pm_type));
96 return -EINVAL;
97 }
98
99 if (enable) {
100 if (pm_type == DP_CORE_PM && dp_power->core_clks_on) {
101 drm_dbg_dp(power->drm_dev,
102 "core clks already enabled\n");
103 return 0;
104 }
105
106 if (pm_type == DP_CTRL_PM && dp_power->link_clks_on) {
107 drm_dbg_dp(power->drm_dev,
108 "links clks already enabled\n");
109 return 0;
110 }
111
112 if (pm_type == DP_STREAM_PM && dp_power->stream_clks_on) {
113 drm_dbg_dp(power->drm_dev,
114 "pixel clks already enabled\n");
115 return 0;
116 }
117
118 if ((pm_type == DP_CTRL_PM) && (!dp_power->core_clks_on)) {
119 drm_dbg_dp(power->drm_dev,
120 "Enable core clks before link clks\n");
121 mp = &power->parser->mp[DP_CORE_PM];
122
123 rc = clk_bulk_prepare_enable(mp->num_clk, mp->clocks);
124 if (rc) {
125 DRM_ERROR("fail to enable clks: %s. err=%d\n",
126 dp_parser_pm_name(DP_CORE_PM), rc);
127 return rc;
128 }
129 dp_power->core_clks_on = true;
130 }
131 }
132
133 mp = &power->parser->mp[pm_type];
134 if (enable) {
135 rc = clk_bulk_prepare_enable(mp->num_clk, mp->clocks);
136 if (rc) {
137 DRM_ERROR("failed to enable clks, err: %d\n", rc);
138 return rc;
139 }
140 } else {
141 clk_bulk_disable_unprepare(mp->num_clk, mp->clocks);
142 }
143
144 if (pm_type == DP_CORE_PM)
145 dp_power->core_clks_on = enable;
146 else if (pm_type == DP_STREAM_PM)
147 dp_power->stream_clks_on = enable;
148 else
149 dp_power->link_clks_on = enable;
150
151 drm_dbg_dp(power->drm_dev, "%s clocks for %s\n",
152 enable ? "enable" : "disable",
153 dp_parser_pm_name(pm_type));
154 drm_dbg_dp(power->drm_dev,
155 "strem_clks:%s link_clks:%s core_clks:%s\n",
156 dp_power->stream_clks_on ? "on" : "off",
157 dp_power->link_clks_on ? "on" : "off",
158 dp_power->core_clks_on ? "on" : "off");
159
160 return 0;
161 }
162
dp_power_client_init(struct dp_power * dp_power)163 int dp_power_client_init(struct dp_power *dp_power)
164 {
165 int rc = 0;
166 struct dp_power_private *power;
167
168 if (!dp_power) {
169 DRM_ERROR("invalid power data\n");
170 return -EINVAL;
171 }
172
173 power = container_of(dp_power, struct dp_power_private, dp_power);
174
175 pm_runtime_enable(&power->pdev->dev);
176
177 rc = dp_power_clk_init(power);
178 if (rc)
179 DRM_ERROR("failed to init clocks %d\n", rc);
180
181 return rc;
182 }
183
dp_power_client_deinit(struct dp_power * dp_power)184 void dp_power_client_deinit(struct dp_power *dp_power)
185 {
186 struct dp_power_private *power;
187
188 if (!dp_power) {
189 DRM_ERROR("invalid power data\n");
190 return;
191 }
192
193 power = container_of(dp_power, struct dp_power_private, dp_power);
194
195 pm_runtime_disable(&power->pdev->dev);
196 }
197
dp_power_init(struct dp_power * dp_power,bool flip)198 int dp_power_init(struct dp_power *dp_power, bool flip)
199 {
200 int rc = 0;
201 struct dp_power_private *power = NULL;
202
203 if (!dp_power) {
204 DRM_ERROR("invalid power data\n");
205 return -EINVAL;
206 }
207
208 power = container_of(dp_power, struct dp_power_private, dp_power);
209
210 pm_runtime_get_sync(&power->pdev->dev);
211
212 rc = dp_power_clk_enable(dp_power, DP_CORE_PM, true);
213 if (rc) {
214 DRM_ERROR("failed to enable DP core clocks, %d\n", rc);
215 goto exit;
216 }
217
218 return 0;
219
220 exit:
221 pm_runtime_put_sync(&power->pdev->dev);
222 return rc;
223 }
224
dp_power_deinit(struct dp_power * dp_power)225 int dp_power_deinit(struct dp_power *dp_power)
226 {
227 struct dp_power_private *power;
228
229 power = container_of(dp_power, struct dp_power_private, dp_power);
230
231 dp_power_clk_enable(dp_power, DP_CORE_PM, false);
232 pm_runtime_put_sync(&power->pdev->dev);
233 return 0;
234 }
235
dp_power_get(struct device * dev,struct dp_parser * parser)236 struct dp_power *dp_power_get(struct device *dev, struct dp_parser *parser)
237 {
238 struct dp_power_private *power;
239 struct dp_power *dp_power;
240
241 if (!parser) {
242 DRM_ERROR("invalid input\n");
243 return ERR_PTR(-EINVAL);
244 }
245
246 power = devm_kzalloc(&parser->pdev->dev, sizeof(*power), GFP_KERNEL);
247 if (!power)
248 return ERR_PTR(-ENOMEM);
249
250 power->parser = parser;
251 power->pdev = parser->pdev;
252 power->dev = dev;
253
254 dp_power = &power->dp_power;
255
256 return dp_power;
257 }
258