blob: ace76b2e85a7d42dc6979fc13f95bc93c0eb77a8 [file] [log] [blame]
Meng Wang43bbb872018-12-10 12:32:05 +08001// SPDX-License-Identifier: GPL-2.0-only
Banajit Goswami06183682018-07-31 00:31:14 -07002/*
3 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
4 */
5
6#include <linux/platform_device.h>
7#include <linux/slab.h>
8#include <linux/module.h>
9#include <linux/of_device.h>
10#include <soc/snd_event.h>
11
12struct snd_event_client {
13 struct list_head node;
14
15 struct device *dev;
16 const struct snd_event_ops *ops;
17 void *data;
18
19 bool attached;
20 bool state;
21};
22
23struct snd_event_client_array {
24 struct device *dev;
25 struct snd_event_client *clnt;
26 void *data;
27 int (*compare)(struct device *, void *);
28};
29
30struct snd_event_clients {
31 size_t num_clients;
32 struct snd_event_client_array *cl_arr;
33};
34
35struct snd_master {
36 struct device *dev;
37 const struct snd_event_ops *ops;
38 void *data;
39
40 bool state;
41 bool fwk_state;
42 bool clients_found;
43 struct snd_event_clients *clients;
44};
45
46static DEFINE_MUTEX(snd_event_mutex);
47static LIST_HEAD(snd_event_client_list);
48static struct snd_master *master;
49
50static struct snd_event_client *find_snd_event_client(struct device *dev)
51{
52 struct snd_event_client *c;
53
54 list_for_each_entry(c, &snd_event_client_list, node)
55 if ((c->dev == dev) && c->ops)
56 return c;
57
58 return NULL;
59}
60
61static int check_and_update_fwk_state(void)
62{
63 bool new_fwk_state = true;
64 struct snd_event_client *c;
65 int ret = 0;
66 int i = 0;
67
68 for (i = 0; i < master->clients->num_clients; i++) {
69 c = master->clients->cl_arr[i].clnt;
70 new_fwk_state &= c->state;
71 }
72 new_fwk_state &= master->state;
73
74 if (master->fwk_state ^ new_fwk_state) {
75 if (new_fwk_state) {
76 for (i = 0; i < master->clients->num_clients; i++) {
77 c = master->clients->cl_arr[i].clnt;
78 if (c->ops->enable) {
79 ret = c->ops->enable(c->dev, c->data);
80 if (ret) {
81 dev_err(c->dev,
82 "%s: enable failed\n",
83 __func__);
84 goto dev_en_failed;
85 }
86 }
87 }
88 if (master->ops->enable) {
89 ret = master->ops->enable(master->dev,
90 master->data);
91 if (ret) {
92 dev_err(master->dev,
93 "%s: enable failed\n",
94 __func__);
95 goto mstr_en_failed;
96 }
97 }
98 } else {
99 if (master->ops->disable)
100 master->ops->disable(master->dev,
101 master->data);
102 for (i = 0; i < master->clients->num_clients; i++) {
103 c = master->clients->cl_arr[i].clnt;
104 if (c->ops->disable)
105 c->ops->disable(c->dev, c->data);
106 }
107 }
108 master->fwk_state = new_fwk_state;
109 }
110 goto exit;
111
112mstr_en_failed:
113 i = master->clients->num_clients;
114dev_en_failed:
115 for (; i > 0; i--) {
116 c = master->clients->cl_arr[i - 1].clnt;
117 if (c->ops->disable)
118 c->ops->disable(c->dev, c->data);
119 }
120exit:
121 return ret;
122}
123
124static int snd_event_find_clients(struct snd_master *master)
125{
126 struct snd_event_clients *clients = master->clients;
127 int i = 0;
128 int ret = 0;
129
130 for (i = 0; i < clients->num_clients; i++) {
131 struct snd_event_client_array *c_arr = &clients->cl_arr[i];
132 struct snd_event_client *c;
133
134 if (c_arr->dev) {
135 pr_err("%s: client already present dev=%pK\n",
136 __func__, c_arr->dev);
137 continue;
138 }
139
140 list_for_each_entry(c, &snd_event_client_list, node) {
141 if (c->attached)
142 continue;
143
144 if (c_arr->compare(c->dev, c_arr->data)) {
145 dev_dbg(master->dev,
146 "%s: found client, dev=%pK\n",
147 __func__, c->dev);
148 c_arr->dev = c->dev;
149 c_arr->clnt = c;
150 c->attached = true;
151 break;
152 }
153 }
154 if (!c_arr->dev) {
155 dev_dbg(master->dev,
156 "%s: failed to find some client\n",
157 __func__);
158 ret = -ENXIO;
159 break;
160 }
161 }
162
163 return ret;
164}
165
166/*
167 * snd_event_client_register - Register a client with the SND event FW
168 *
169 * @dev: Pointer to the "struct device" associated with the client
170 * @snd_ev_ops: Pointer to the snd_event_ops struct for the client containing
171 * callback functions
172 * @data: Pointer to any additional data that the caller wants to get back
173 * with callback functions
174 *
175 * Returns 0 on success or error on failure.
176 */
177int snd_event_client_register(struct device *dev,
178 const struct snd_event_ops *snd_ev_ops,
179 void *data)
180{
181 struct snd_event_client *c;
182
183 if (!dev) {
184 pr_err("%s: dev is NULL\n", __func__);
185 return -EINVAL;
186 }
187
188 c = kzalloc(sizeof(*c), GFP_KERNEL);
189 if (!c)
190 return -ENOMEM;
191
192 c->dev = dev;
193 c->ops = snd_ev_ops;
194 c->data = data;
195
196 dev_dbg(dev, "%s: adding client to SND event FW (ops %pK)\n",
197 __func__, snd_ev_ops);
198
199 mutex_lock(&snd_event_mutex);
200 list_add_tail(&c->node, &snd_event_client_list);
201
202 if (master && !master->clients_found) {
203 if (snd_event_find_clients(master)) {
204 dev_dbg(dev, "%s: Failed to find all clients\n",
205 __func__);
206 goto exit;
207 }
208 master->clients_found = true;
209 }
210
211exit:
212 mutex_unlock(&snd_event_mutex);
213 return 0;
214}
215EXPORT_SYMBOL(snd_event_client_register);
216
217/*
218 * snd_event_client_deregister - Remove a client from the SND event FW
219 *
220 * @dev: Pointer to the "struct device" associated with the client
221 *
222 * Returns 0 on success or error on failure.
223 */
224int snd_event_client_deregister(struct device *dev)
225{
226 struct snd_event_client *c;
227 int ret = 0;
228 int i = 0;
229
230 if (!dev) {
231 pr_err("%s: dev is NULL\n", __func__);
232 return -EINVAL;
233 }
234
235 mutex_lock(&snd_event_mutex);
236 if (list_empty(&snd_event_client_list)) {
237 dev_dbg(dev, "%s: No SND client registered\n", __func__);
238 ret = -ENODEV;
239 goto exit;
240 }
241
242 c = find_snd_event_client(dev);
243 if (!c || (c->dev != dev)) {
244 dev_dbg(dev, "%s: No matching snd dev found\n", __func__);
245 ret = -ENODEV;
246 goto exit;
247 }
248
249 c->state = false;
250
251 if (master && master->clients_found) {
252 struct snd_event_client *d;
253 bool dev_found = false;
254
255 for (i = 0; i < master->clients->num_clients; i++) {
256 d = master->clients->cl_arr[i].clnt;
257 if (c->dev == d->dev) {
258 dev_found = true;
259 break;
260 }
261 }
262 if (dev_found) {
263 ret = check_and_update_fwk_state();
264 master->clients_found = false;
265 }
266 }
267
268 list_del(&c->node);
269 kfree(c);
270exit:
271 mutex_unlock(&snd_event_mutex);
272 return ret;
273}
274EXPORT_SYMBOL(snd_event_client_deregister);
275
276/*
277 * snd_event_mstr_add_client - Add a client to the master's list of clients
278 *
279 * @snd_clients: list of clients associated with this master
280 * @compare: Pointer to the compare callback function that master will use to
281 * confirm the clients
282 * @data: Address to any additional data that the master wants to get back with
283 * compare callback functions
284 */
285void snd_event_mstr_add_client(struct snd_event_clients **snd_clients,
286 int (*compare)(struct device *, void *),
287 void *data)
288{
289 struct snd_event_clients *client = *snd_clients;
290
291 if (IS_ERR(client)) {
292 pr_err("%s: snd_clients is invalid\n", __func__);
293 return;
294 }
295
296 if (!client) {
297 client = kzalloc(sizeof(*client), GFP_KERNEL);
298 if (!client) {
299 *snd_clients = ERR_PTR(-ENOMEM);
300 return;
301 }
302 client->cl_arr = kzalloc(sizeof(struct snd_event_client_array),
303 GFP_KERNEL);
Tanya Dixitd7a63902018-10-17 17:20:04 +0530304 if (!client->cl_arr) {
305 *snd_clients = ERR_PTR(-ENOMEM);
306 return;
307 }
Banajit Goswami06183682018-07-31 00:31:14 -0700308 *snd_clients = client;
309 } else {
310 struct snd_event_client_array *new;
311
312 new = krealloc(client->cl_arr,
313 (client->num_clients + 1) * sizeof(*new),
314 GFP_KERNEL | __GFP_ZERO);
315 if (!new) {
316 *snd_clients = ERR_PTR(-ENOMEM);
317 return;
318 }
319 client->cl_arr = new;
320 }
321
322 client->cl_arr[client->num_clients].dev = NULL;
323 client->cl_arr[client->num_clients].data = data;
324 client->cl_arr[client->num_clients].compare = compare;
325 client->num_clients++;
326}
327EXPORT_SYMBOL(snd_event_mstr_add_client);
328
329/*
330 * snd_event_master_register - Register a master with the SND event FW
331 *
332 * @dev: Pointer to the "struct device" associated with the master
333 * @ops: Pointer to the snd_event_ops struct for the master containing
334 * callback functions
335 * @clients: List of clients for the master
336 * @data: Pointer to any additional data that the caller wants to get back
337 * with callback functions
338 *
339 * Returns 0 on success or error on failure.
340 *
341 * Prerequisite:
342 * clients list must not be empty.
343 * All clients for the master must have to be registered by calling
344 * snd_event_mstr_add_client() before calling this API to register a
345 * master with SND event fwk.
346 */
347int snd_event_master_register(struct device *dev,
348 const struct snd_event_ops *ops,
349 struct snd_event_clients *clients,
350 void *data)
351{
352 struct snd_master *new_master;
353 int ret = 0;
354
355 if (!dev) {
356 pr_err("%s: dev is NULL\n", __func__);
357 return -EINVAL;
358 }
359
360 mutex_lock(&snd_event_mutex);
361 if (master) {
362 dev_err(dev, "%s: master already allocated with %pK\n",
363 __func__, master->dev);
364 ret = -EALREADY;
365 goto exit;
366 }
367 mutex_unlock(&snd_event_mutex);
368
369 if (!clients || IS_ERR(clients)) {
370 dev_err(dev, "%s: Invalid clients ptr\n", __func__);
371 return -EINVAL;
372 }
373
374 new_master = kzalloc(sizeof(*new_master), GFP_KERNEL);
375 if (!new_master)
376 return -ENOMEM;
377
378 new_master->dev = dev;
379 new_master->ops = ops;
380 new_master->data = data;
381 new_master->clients = clients;
382
383 dev_dbg(dev, "adding master to SND event FW (ops %pK)\n", ops);
384
385 mutex_lock(&snd_event_mutex);
386
387 master = new_master;
388
389 ret = snd_event_find_clients(master);
390 if (ret) {
391 dev_dbg(dev, "%s: Failed to find all clients\n", __func__);
392 ret = 0;
393 goto exit;
394 }
395 master->clients_found = true;
396
397exit:
398 mutex_unlock(&snd_event_mutex);
399 return ret;
400}
401EXPORT_SYMBOL(snd_event_master_register);
402
403/*
404 * snd_event_master_deregister - Remove a master from the SND event FW
405 *
406 * @dev: Pointer to the "struct device" associated with the master
407 *
408 * Returns 0 on success or error on failure.
409 */
410int snd_event_master_deregister(struct device *dev)
411{
412 int ret = 0;
413
414 if (!dev) {
415 pr_err("%s: dev is NULL\n", __func__);
416 return -EINVAL;
417 }
418
419 mutex_lock(&snd_event_mutex);
420 if (!master) {
421 dev_dbg(dev, "%s: No master found\n", __func__);
422 ret = -ENODEV;
423 goto exit;
424 }
425
426 if (master->dev != dev) {
427 dev_dbg(dev, "%s: device is not a Master\n", __func__);
428 ret = -ENXIO;
429 goto exit;
430 }
431
432 master->state = false;
433
434 if (master && master->clients_found)
435 ret = check_and_update_fwk_state();
436
437 kfree(master->clients->cl_arr);
438 kfree(master->clients);
439 kfree(master);
440 master = NULL;
441exit:
442 mutex_unlock(&snd_event_mutex);
443 return ret;
444}
445EXPORT_SYMBOL(snd_event_master_deregister);
446
447/*
448 * snd_event_notify - Update the state of the Master/client in the SND event FW
449 *
450 * @dev: Pointer to the "struct device" associated with the master/client
451 * @state: UP/DOWN state of the caller (master/client)
452 *
453 * Returns 0 on success or error on failure.
454 */
455int snd_event_notify(struct device *dev, unsigned int state)
456{
457 struct snd_event_client *c;
458 int ret = 0;
459
460 if (!dev) {
461 pr_err("%s: dev is NULL\n", __func__);
462 return -EINVAL;
463 }
464
465 mutex_lock(&snd_event_mutex);
466 if (list_empty(&snd_event_client_list) && !master) {
467 dev_err(dev, "%s: No device registered\n", __func__);
468 ret = -ENODEV;
469 goto exit;
470 }
471
472 c = find_snd_event_client(dev);
473 if (!c && (!master || (master->dev != dev))) {
474 dev_err(dev, "%s: No snd dev entry found\n", __func__);
475 ret = -ENXIO;
476 goto exit;
477 }
478
479 if (c)
480 c->state = !!state;
481 else
482 master->state = !!state;
483
484 if (master && master->clients_found)
485 ret = check_and_update_fwk_state();
486
487exit:
488 mutex_unlock(&snd_event_mutex);
489 return ret;
490}
491EXPORT_SYMBOL(snd_event_notify);
492
493MODULE_LICENSE("GPL v2");
494MODULE_DESCRIPTION("SND event module");