Merge tag 'drm-next-2018-08-17' of git://anongit.freedesktop.org/drm/drm
[sfrench/cifs-2.6.git] / drivers / gpu / drm / msm / disp / dpu1 / dpu_power_handle.c
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
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
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
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
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
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
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
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
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
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 }