xref: /openbmc/linux/drivers/gpu/drm/msm/dp/dp_power.c (revision 9edac2ee)
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