Merge tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rdma/rdma
[platform/kernel/linux-starfive.git] / drivers / gpu / drm / msm / dp / dp_power.c
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
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
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
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
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
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
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
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
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 }