1 /* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License version 2 and
5  * only version 2 as published by the Free Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  */
13 
14 #define pr_fmt(fmt)	"[drm:%s:%d]: " fmt, __func__, __LINE__
15 
16 #include <linux/kernel.h>
17 #include <linux/of.h>
18 #include <linux/string.h>
19 #include <linux/of_address.h>
20 #include <linux/slab.h>
21 #include <linux/mutex.h>
22 #include <linux/of_platform.h>
23 
24 #include "dpu_power_handle.h"
25 #include "dpu_trace.h"
26 
27 static const char *data_bus_name[DPU_POWER_HANDLE_DBUS_ID_MAX] = {
28 	[DPU_POWER_HANDLE_DBUS_ID_MNOC] = "qcom,dpu-data-bus",
29 	[DPU_POWER_HANDLE_DBUS_ID_LLCC] = "qcom,dpu-llcc-bus",
30 	[DPU_POWER_HANDLE_DBUS_ID_EBI] = "qcom,dpu-ebi-bus",
31 };
32 
dpu_power_handle_get_dbus_name(u32 bus_id)33 const char *dpu_power_handle_get_dbus_name(u32 bus_id)
34 {
35 	if (bus_id < DPU_POWER_HANDLE_DBUS_ID_MAX)
36 		return data_bus_name[bus_id];
37 
38 	return NULL;
39 }
40 
dpu_power_event_trigger_locked(struct dpu_power_handle * phandle,u32 event_type)41 static void dpu_power_event_trigger_locked(struct dpu_power_handle *phandle,
42 		u32 event_type)
43 {
44 	struct dpu_power_event *event;
45 
46 	list_for_each_entry(event, &phandle->event_list, list) {
47 		if (event->event_type & event_type)
48 			event->cb_fnc(event_type, event->usr);
49 	}
50 }
51 
dpu_power_client_create(struct dpu_power_handle * phandle,char * client_name)52 struct dpu_power_client *dpu_power_client_create(
53 	struct dpu_power_handle *phandle, char *client_name)
54 {
55 	struct dpu_power_client *client;
56 	static u32 id;
57 
58 	if (!client_name || !phandle) {
59 		pr_err("client name is null or invalid power data\n");
60 		return ERR_PTR(-EINVAL);
61 	}
62 
63 	client = kzalloc(sizeof(struct dpu_power_client), GFP_KERNEL);
64 	if (!client)
65 		return ERR_PTR(-ENOMEM);
66 
67 	mutex_lock(&phandle->phandle_lock);
68 	strlcpy(client->name, client_name, MAX_CLIENT_NAME_LEN);
69 	client->usecase_ndx = VOTE_INDEX_DISABLE;
70 	client->id = id;
71 	client->active = true;
72 	pr_debug("client %s created:%pK id :%d\n", client_name,
73 		client, id);
74 	id++;
75 	list_add(&client->list, &phandle->power_client_clist);
76 	mutex_unlock(&phandle->phandle_lock);
77 
78 	return client;
79 }
80 
dpu_power_client_destroy(struct dpu_power_handle * phandle,struct dpu_power_client * client)81 void dpu_power_client_destroy(struct dpu_power_handle *phandle,
82 	struct dpu_power_client *client)
83 {
84 	if (!client  || !phandle) {
85 		pr_err("reg bus vote: invalid client handle\n");
86 	} else if (!client->active) {
87 		pr_err("dpu power deinit already done\n");
88 		kfree(client);
89 	} else {
90 		pr_debug("bus vote client %s destroyed:%pK id:%u\n",
91 			client->name, client, client->id);
92 		mutex_lock(&phandle->phandle_lock);
93 		list_del_init(&client->list);
94 		mutex_unlock(&phandle->phandle_lock);
95 		kfree(client);
96 	}
97 }
98 
dpu_power_resource_init(struct platform_device * pdev,struct dpu_power_handle * phandle)99 void dpu_power_resource_init(struct platform_device *pdev,
100 	struct dpu_power_handle *phandle)
101 {
102 	phandle->dev = &pdev->dev;
103 
104 	INIT_LIST_HEAD(&phandle->power_client_clist);
105 	INIT_LIST_HEAD(&phandle->event_list);
106 
107 	mutex_init(&phandle->phandle_lock);
108 }
109 
dpu_power_resource_deinit(struct platform_device * pdev,struct dpu_power_handle * phandle)110 void dpu_power_resource_deinit(struct platform_device *pdev,
111 	struct dpu_power_handle *phandle)
112 {
113 	struct dpu_power_client *curr_client, *next_client;
114 	struct dpu_power_event *curr_event, *next_event;
115 
116 	if (!phandle || !pdev) {
117 		pr_err("invalid input param\n");
118 		return;
119 	}
120 
121 	mutex_lock(&phandle->phandle_lock);
122 	list_for_each_entry_safe(curr_client, next_client,
123 			&phandle->power_client_clist, list) {
124 		pr_err("client:%s-%d still registered with refcount:%d\n",
125 				curr_client->name, curr_client->id,
126 				curr_client->refcount);
127 		curr_client->active = false;
128 		list_del(&curr_client->list);
129 	}
130 
131 	list_for_each_entry_safe(curr_event, next_event,
132 			&phandle->event_list, list) {
133 		pr_err("event:%d, client:%s still registered\n",
134 				curr_event->event_type,
135 				curr_event->client_name);
136 		curr_event->active = false;
137 		list_del(&curr_event->list);
138 	}
139 	mutex_unlock(&phandle->phandle_lock);
140 }
141 
dpu_power_resource_enable(struct dpu_power_handle * phandle,struct dpu_power_client * pclient,bool enable)142 int dpu_power_resource_enable(struct dpu_power_handle *phandle,
143 	struct dpu_power_client *pclient, bool enable)
144 {
145 	bool changed = false;
146 	u32 max_usecase_ndx = VOTE_INDEX_DISABLE, prev_usecase_ndx;
147 	struct dpu_power_client *client;
148 
149 	if (!phandle || !pclient) {
150 		pr_err("invalid input argument\n");
151 		return -EINVAL;
152 	}
153 
154 	mutex_lock(&phandle->phandle_lock);
155 	if (enable)
156 		pclient->refcount++;
157 	else if (pclient->refcount)
158 		pclient->refcount--;
159 
160 	if (pclient->refcount)
161 		pclient->usecase_ndx = VOTE_INDEX_LOW;
162 	else
163 		pclient->usecase_ndx = VOTE_INDEX_DISABLE;
164 
165 	list_for_each_entry(client, &phandle->power_client_clist, list) {
166 		if (client->usecase_ndx < VOTE_INDEX_MAX &&
167 		    client->usecase_ndx > max_usecase_ndx)
168 			max_usecase_ndx = client->usecase_ndx;
169 	}
170 
171 	if (phandle->current_usecase_ndx != max_usecase_ndx) {
172 		changed = true;
173 		prev_usecase_ndx = phandle->current_usecase_ndx;
174 		phandle->current_usecase_ndx = max_usecase_ndx;
175 	}
176 
177 	pr_debug("%pS: changed=%d current idx=%d request client %s id:%u enable:%d refcount:%d\n",
178 		__builtin_return_address(0), changed, max_usecase_ndx,
179 		pclient->name, pclient->id, enable, pclient->refcount);
180 
181 	if (!changed)
182 		goto end;
183 
184 	if (enable) {
185 		dpu_power_event_trigger_locked(phandle,
186 				DPU_POWER_EVENT_PRE_ENABLE);
187 		dpu_power_event_trigger_locked(phandle,
188 				DPU_POWER_EVENT_POST_ENABLE);
189 
190 	} else {
191 		dpu_power_event_trigger_locked(phandle,
192 				DPU_POWER_EVENT_PRE_DISABLE);
193 		dpu_power_event_trigger_locked(phandle,
194 				DPU_POWER_EVENT_POST_DISABLE);
195 	}
196 
197 end:
198 	mutex_unlock(&phandle->phandle_lock);
199 	return 0;
200 }
201 
dpu_power_handle_register_event(struct dpu_power_handle * phandle,u32 event_type,void (* cb_fnc)(u32 event_type,void * usr),void * usr,char * client_name)202 struct dpu_power_event *dpu_power_handle_register_event(
203 		struct dpu_power_handle *phandle,
204 		u32 event_type, void (*cb_fnc)(u32 event_type, void *usr),
205 		void *usr, char *client_name)
206 {
207 	struct dpu_power_event *event;
208 
209 	if (!phandle) {
210 		pr_err("invalid power handle\n");
211 		return ERR_PTR(-EINVAL);
212 	} else if (!cb_fnc || !event_type) {
213 		pr_err("no callback fnc or event type\n");
214 		return ERR_PTR(-EINVAL);
215 	}
216 
217 	event = kzalloc(sizeof(struct dpu_power_event), GFP_KERNEL);
218 	if (!event)
219 		return ERR_PTR(-ENOMEM);
220 
221 	event->event_type = event_type;
222 	event->cb_fnc = cb_fnc;
223 	event->usr = usr;
224 	strlcpy(event->client_name, client_name, MAX_CLIENT_NAME_LEN);
225 	event->active = true;
226 
227 	mutex_lock(&phandle->phandle_lock);
228 	list_add(&event->list, &phandle->event_list);
229 	mutex_unlock(&phandle->phandle_lock);
230 
231 	return event;
232 }
233 
dpu_power_handle_unregister_event(struct dpu_power_handle * phandle,struct dpu_power_event * event)234 void dpu_power_handle_unregister_event(
235 		struct dpu_power_handle *phandle,
236 		struct dpu_power_event *event)
237 {
238 	if (!phandle || !event) {
239 		pr_err("invalid phandle or event\n");
240 	} else if (!event->active) {
241 		pr_err("power handle deinit already done\n");
242 		kfree(event);
243 	} else {
244 		mutex_lock(&phandle->phandle_lock);
245 		list_del_init(&event->list);
246 		mutex_unlock(&phandle->phandle_lock);
247 		kfree(event);
248 	}
249 }
250