blob: 485c703d25b1884ed1a64c12dd88883d8fb8d622 [file] [log] [blame]
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301/* Copyright (c) 2016-2017, 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#include <linux/module.h>
14#include <linux/slab.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053015#include <soc/qcom/subsystem_notif.h>
16#include <soc/qcom/service-notifier.h>
Laxminath Kasam605b42f2017-08-01 22:02:15 +053017#include <dsp/audio_notifier.h>
18#include "audio_ssr.h"
19#include "audio_pdr.h"
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053020
21/* Audio states internal to notifier. Client */
22/* used states defined in audio_notifier.h */
23/* for AUDIO_NOTIFIER_SERVICE_DOWN & UP */
24#define NO_SERVICE -2
25#define UNINIT_SERVICE -1
26
27/*
28 * Used for each client registered with audio notifier
29 */
30struct client_data {
31 struct list_head list;
32 /* Notifier block given by client */
33 struct notifier_block *nb;
34 char client_name[20];
35 int service;
36 int domain;
37};
38
39/*
40 * Used for each service and domain combination
41 * Tracks information specific to the underlying
42 * service.
43 */
44struct service_info {
45 const char name[20];
46 int domain_id;
47 int state;
48 void *handle;
49 /* Notifier block registered to service */
50 struct notifier_block *nb;
51 /* Used to determine when to register and deregister service */
52 int num_of_clients;
53 /* List of all clients registered to the service and domain */
54 struct srcu_notifier_head client_nb_list;
55};
56
57static int audio_notifer_ssr_adsp_cb(struct notifier_block *this,
58 unsigned long opcode, void *data);
59static int audio_notifer_ssr_modem_cb(struct notifier_block *this,
60 unsigned long opcode, void *data);
61static int audio_notifer_pdr_adsp_cb(struct notifier_block *this,
62 unsigned long opcode, void *data);
63
64static struct notifier_block notifier_ssr_adsp_nb = {
65 .notifier_call = audio_notifer_ssr_adsp_cb,
66 .priority = 0,
67};
68
69static struct notifier_block notifier_ssr_modem_nb = {
70 .notifier_call = audio_notifer_ssr_modem_cb,
71 .priority = 0,
72};
73
74static struct notifier_block notifier_pdr_adsp_nb = {
75 .notifier_call = audio_notifer_pdr_adsp_cb,
76 .priority = 0,
77};
78
79static struct service_info service_data[AUDIO_NOTIFIER_MAX_SERVICES]
80 [AUDIO_NOTIFIER_MAX_DOMAINS] = {
81
82 {{
83 .name = "SSR_ADSP",
84 .domain_id = AUDIO_SSR_DOMAIN_ADSP,
85 .state = AUDIO_NOTIFIER_SERVICE_DOWN,
86 .nb = &notifier_ssr_adsp_nb
87 },
88 {
89 .name = "SSR_MODEM",
90 .domain_id = AUDIO_SSR_DOMAIN_MODEM,
91 .state = AUDIO_NOTIFIER_SERVICE_DOWN,
92 .nb = &notifier_ssr_modem_nb
93 } },
94
95 {{
96 .name = "PDR_ADSP",
97 .domain_id = AUDIO_PDR_DOMAIN_ADSP,
98 .state = UNINIT_SERVICE,
99 .nb = &notifier_pdr_adsp_nb
100 },
101 { /* PDR MODEM service not enabled */
102 .name = "INVALID",
103 .state = NO_SERVICE,
104 .nb = NULL
105 } }
106};
107
108/* Master list of all audio notifier clients */
109struct list_head client_list;
110struct mutex notifier_mutex;
111
112static int audio_notifer_get_default_service(int domain)
113{
114 int service = NO_SERVICE;
115
116 /* initial service to connect per domain */
117 switch (domain) {
118 case AUDIO_NOTIFIER_ADSP_DOMAIN:
119 service = AUDIO_NOTIFIER_PDR_SERVICE;
120 break;
121 case AUDIO_NOTIFIER_MODEM_DOMAIN:
122 service = AUDIO_NOTIFIER_SSR_SERVICE;
123 break;
124 }
125
126 return service;
127}
128
129static void audio_notifer_disable_service(int service)
130{
131 int i;
132
133 for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++)
134 service_data[service][i].state = NO_SERVICE;
135}
136
137static bool audio_notifer_is_service_enabled(int service)
138{
139 int i;
140
141 for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++)
142 if (service_data[service][i].state != NO_SERVICE)
143 return true;
144 return false;
145}
146
147static void audio_notifer_init_service(int service)
148{
149 int i;
150
151 for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++) {
152 if (service_data[service][i].state == UNINIT_SERVICE)
153 service_data[service][i].state =
154 AUDIO_NOTIFIER_SERVICE_DOWN;
155 }
156}
157
158static int audio_notifer_reg_service(int service, int domain)
159{
160 void *handle;
161 int ret = 0;
162 int curr_state = AUDIO_NOTIFIER_SERVICE_DOWN;
163
164 switch (service) {
165 case AUDIO_NOTIFIER_SSR_SERVICE:
166 handle = audio_ssr_register(
167 service_data[service][domain].domain_id,
168 service_data[service][domain].nb);
169 break;
170 case AUDIO_NOTIFIER_PDR_SERVICE:
171 handle = audio_pdr_service_register(
172 service_data[service][domain].domain_id,
173 service_data[service][domain].nb, &curr_state);
174
175 if (curr_state == SERVREG_NOTIF_SERVICE_STATE_UP_V01)
176 curr_state = AUDIO_NOTIFIER_SERVICE_UP;
177 else
178 curr_state = AUDIO_NOTIFIER_SERVICE_DOWN;
179 break;
180 default:
181 pr_err("%s: Invalid service %d\n",
182 __func__, service);
183 ret = -EINVAL;
184 goto done;
185 }
186 if (IS_ERR_OR_NULL(handle)) {
187 pr_err("%s: handle is incorrect for service %s\n",
188 __func__, service_data[service][domain].name);
189 ret = -EINVAL;
190 goto done;
191 }
192 service_data[service][domain].state = curr_state;
193 service_data[service][domain].handle = handle;
194
195 pr_info("%s: service %s is in use\n",
196 __func__, service_data[service][domain].name);
197 pr_debug("%s: service %s has current state %d, handle 0x%pK\n",
198 __func__, service_data[service][domain].name,
199 service_data[service][domain].state,
200 service_data[service][domain].handle);
201done:
202 return ret;
203}
204
205static int audio_notifer_dereg_service(int service, int domain)
206{
207 int ret;
208
209 switch (service) {
210 case AUDIO_NOTIFIER_SSR_SERVICE:
211 ret = audio_ssr_deregister(
212 service_data[service][domain].handle,
213 service_data[service][domain].nb);
214 break;
215 case AUDIO_NOTIFIER_PDR_SERVICE:
216 ret = audio_pdr_service_deregister(
217 service_data[service][domain].handle,
218 service_data[service][domain].nb);
219 break;
220 default:
221 pr_err("%s: Invalid service %d\n",
222 __func__, service);
223 ret = -EINVAL;
224 goto done;
225 }
226 if (ret < 0) {
227 pr_err("%s: deregister failed for service %s, ret %d\n",
228 __func__, service_data[service][domain].name, ret);
229 goto done;
230 }
231
232 pr_debug("%s: service %s with handle 0x%pK deregistered\n",
233 __func__, service_data[service][domain].name,
234 service_data[service][domain].handle);
235
236 service_data[service][domain].state = AUDIO_NOTIFIER_SERVICE_DOWN;
237 service_data[service][domain].handle = NULL;
238done:
239 return ret;
240}
241
242static int audio_notifer_reg_client_service(struct client_data *client_data,
243 int service)
244{
245 int ret = 0;
246 int domain = client_data->domain;
247 struct audio_notifier_cb_data data;
248
249 switch (service) {
250 case AUDIO_NOTIFIER_SSR_SERVICE:
251 case AUDIO_NOTIFIER_PDR_SERVICE:
252 if (service_data[service][domain].num_of_clients == 0)
253 ret = audio_notifer_reg_service(service, domain);
254 break;
255 default:
256 pr_err("%s: Invalid service for client %s, service %d, domain %d\n",
257 __func__, client_data->client_name, service, domain);
258 ret = -EINVAL;
259 goto done;
260 }
261
262 if (ret < 0) {
263 pr_err("%s: service registration failed on service %s for client %s\n",
264 __func__, service_data[service][domain].name,
265 client_data->client_name);
266 goto done;
267 }
268
269 client_data->service = service;
270 srcu_notifier_chain_register(
271 &service_data[service][domain].client_nb_list,
272 client_data->nb);
273 service_data[service][domain].num_of_clients++;
274
275 pr_debug("%s: registered client %s on service %s, current state 0x%x\n",
276 __func__, client_data->client_name,
277 service_data[service][domain].name,
278 service_data[service][domain].state);
279
280 /*
281 * PDR registration returns current state
282 * Force callback of client with current state for PDR
283 */
284 if (client_data->service == AUDIO_NOTIFIER_PDR_SERVICE) {
285 data.service = service;
286 data.domain = domain;
287 (void)client_data->nb->notifier_call(client_data->nb,
288 service_data[service][domain].state, &data);
289 }
290done:
291 return ret;
292}
293
294static int audio_notifer_reg_client(struct client_data *client_data)
295{
296 int ret = 0;
297 int service;
298 int domain = client_data->domain;
299
300 service = audio_notifer_get_default_service(domain);
301 if (service < 0) {
302 pr_err("%s: service %d is incorrect\n", __func__, service);
303 ret = -EINVAL;
304 goto done;
305 }
306
307 /* Search through services to find a valid one to register client on. */
308 for (; service >= 0; service--) {
309 /* If a service is not initialized, wait for it to come up. */
310 if (service_data[service][domain].state == UNINIT_SERVICE)
311 goto done;
312 /* Skip unsupported service and domain combinations. */
313 if (service_data[service][domain].state < 0)
314 continue;
315 /* Only register clients who have not acquired a service. */
316 if (client_data->service != NO_SERVICE)
317 continue;
318
319 /*
320 * Only register clients, who have not acquired a service, on
321 * the best available service for their domain. Uninitialized
322 * services will try to register all of their clients after
323 * they initialize correctly or will disable their service and
324 * register clients on the next best avaialable service.
325 */
326 pr_debug("%s: register client %s on service %s",
327 __func__, client_data->client_name,
328 service_data[service][domain].name);
329
330 ret = audio_notifer_reg_client_service(client_data, service);
331 if (ret < 0)
332 pr_err("%s: client %s failed to register on service %s",
333 __func__, client_data->client_name,
334 service_data[service][domain].name);
335 }
336
337done:
338 return ret;
339}
340
341static int audio_notifer_dereg_client(struct client_data *client_data)
342{
343 int ret = 0;
344 int service = client_data->service;
345 int domain = client_data->domain;
346
347 switch (client_data->service) {
348 case AUDIO_NOTIFIER_SSR_SERVICE:
349 case AUDIO_NOTIFIER_PDR_SERVICE:
350 if (service_data[service][domain].num_of_clients == 1)
351 ret = audio_notifer_dereg_service(service, domain);
352 break;
353 case NO_SERVICE:
354 goto done;
355 default:
356 pr_err("%s: Invalid service for client %s, service %d\n",
357 __func__, client_data->client_name,
358 client_data->service);
359 ret = -EINVAL;
360 goto done;
361 }
362
363 if (ret < 0) {
364 pr_err("%s: deregister failed for client %s on service %s, ret %d\n",
365 __func__, client_data->client_name,
366 service_data[service][domain].name, ret);
367 goto done;
368 }
369
370 ret = srcu_notifier_chain_unregister(&service_data[service][domain].
371 client_nb_list, client_data->nb);
372 if (ret < 0) {
373 pr_err("%s: srcu_notifier_chain_unregister failed, ret %d\n",
374 __func__, ret);
375 goto done;
376 }
377
378 pr_debug("%s: deregistered client %s on service %s\n",
379 __func__, client_data->client_name,
380 service_data[service][domain].name);
381
382 client_data->service = NO_SERVICE;
383 if (service_data[service][domain].num_of_clients > 0)
384 service_data[service][domain].num_of_clients--;
385done:
386 return ret;
387}
388
389static void audio_notifer_reg_all_clients(void)
390{
391 struct list_head *ptr, *next;
392 struct client_data *client_data;
393 int ret;
394
395 list_for_each_safe(ptr, next, &client_list) {
396 client_data = list_entry(ptr, struct client_data, list);
397
398 ret = audio_notifer_reg_client(client_data);
399 if (ret < 0)
400 pr_err("%s: audio_notifer_reg_client failed for client %s, ret %d\n",
401 __func__, client_data->client_name,
402 ret);
403 }
404}
405
406static int audio_notifer_pdr_callback(struct notifier_block *this,
407 unsigned long opcode, void *data)
408{
409 pr_debug("%s: Audio PDR framework state 0x%lx\n",
410 __func__, opcode);
411 mutex_lock(&notifier_mutex);
412 if (opcode == AUDIO_PDR_FRAMEWORK_DOWN)
413 audio_notifer_disable_service(AUDIO_NOTIFIER_PDR_SERVICE);
414 else
415 audio_notifer_init_service(AUDIO_NOTIFIER_PDR_SERVICE);
416
417 audio_notifer_reg_all_clients();
418 mutex_unlock(&notifier_mutex);
419 return 0;
420}
421
422static struct notifier_block pdr_nb = {
423 .notifier_call = audio_notifer_pdr_callback,
424 .priority = 0,
425};
426
427static int audio_notifer_convert_opcode(unsigned long opcode,
428 unsigned long *notifier_opcode)
429{
430 int ret = 0;
431
432 switch (opcode) {
433 case SUBSYS_BEFORE_SHUTDOWN:
434 case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01:
435 *notifier_opcode = AUDIO_NOTIFIER_SERVICE_DOWN;
436 break;
437 case SUBSYS_AFTER_POWERUP:
438 case SERVREG_NOTIF_SERVICE_STATE_UP_V01:
439 *notifier_opcode = AUDIO_NOTIFIER_SERVICE_UP;
440 break;
441 default:
442 pr_debug("%s: Unused opcode 0x%lx\n", __func__, opcode);
443 ret = -EINVAL;
444 }
445
446 return ret;
447}
448
449static int audio_notifer_service_cb(unsigned long opcode,
450 int service, int domain)
451{
452 int ret = 0;
453 unsigned long notifier_opcode;
454 struct audio_notifier_cb_data data;
455
456 if (audio_notifer_convert_opcode(opcode, &notifier_opcode) < 0)
457 goto done;
458
459 data.service = service;
460 data.domain = domain;
461
462 pr_debug("%s: service %s, opcode 0x%lx\n",
463 __func__, service_data[service][domain].name, notifier_opcode);
464
465 mutex_lock(&notifier_mutex);
466
467 service_data[service][domain].state = notifier_opcode;
468 ret = srcu_notifier_call_chain(&service_data[service][domain].
469 client_nb_list, notifier_opcode, &data);
470 if (ret < 0)
471 pr_err("%s: srcu_notifier_call_chain returned %d, service %s, opcode 0x%lx\n",
472 __func__, ret, service_data[service][domain].name,
473 notifier_opcode);
474
475 mutex_unlock(&notifier_mutex);
476done:
477 return NOTIFY_OK;
478}
479
480static int audio_notifer_pdr_adsp_cb(struct notifier_block *this,
481 unsigned long opcode, void *data)
482{
483 return audio_notifer_service_cb(opcode,
484 AUDIO_NOTIFIER_PDR_SERVICE,
485 AUDIO_NOTIFIER_ADSP_DOMAIN);
486}
487
488static int audio_notifer_ssr_adsp_cb(struct notifier_block *this,
489 unsigned long opcode, void *data)
490{
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530491 return audio_notifer_service_cb(opcode,
492 AUDIO_NOTIFIER_SSR_SERVICE,
493 AUDIO_NOTIFIER_ADSP_DOMAIN);
494}
495
496static int audio_notifer_ssr_modem_cb(struct notifier_block *this,
497 unsigned long opcode, void *data)
498{
499 return audio_notifer_service_cb(opcode,
500 AUDIO_NOTIFIER_SSR_SERVICE,
501 AUDIO_NOTIFIER_MODEM_DOMAIN);
502}
503
504int audio_notifier_deregister(char *client_name)
505{
506 int ret = 0;
507 int ret2;
508 struct list_head *ptr, *next;
Laxminath Kasam8f7ccc22017-08-28 17:35:04 +0530509 struct client_data *client_data = NULL;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530510
511 if (client_name == NULL) {
512 pr_err("%s: client_name is NULL\n", __func__);
513 ret = -EINVAL;
514 goto done;
515 }
516 mutex_lock(&notifier_mutex);
517 list_for_each_safe(ptr, next, &client_list) {
518 client_data = list_entry(ptr, struct client_data, list);
519 if (!strcmp(client_name, client_data->client_name)) {
520 ret2 = audio_notifer_dereg_client(client_data);
521 if (ret2 < 0) {
522 pr_err("%s: audio_notifer_dereg_client failed, ret %d\n, service %d, domain %d",
523 __func__, ret2, client_data->service,
524 client_data->domain);
525 ret = ret2;
526 continue;
527 }
528 list_del(&client_data->list);
529 kfree(client_data);
530 }
531 }
532 mutex_unlock(&notifier_mutex);
533done:
534 return ret;
535}
536EXPORT_SYMBOL(audio_notifier_deregister);
537
538int audio_notifier_register(char *client_name, int domain,
539 struct notifier_block *nb)
540{
541 int ret;
542 struct client_data *client_data;
543
544 if (client_name == NULL) {
545 pr_err("%s: client_name is NULL\n", __func__);
546 ret = -EINVAL;
547 goto done;
548 } else if (nb == NULL) {
549 pr_err("%s: Notifier block is NULL\n", __func__);
550 ret = -EINVAL;
551 goto done;
552 }
553
554 client_data = kmalloc(sizeof(*client_data), GFP_KERNEL);
555 if (client_data == NULL) {
556 ret = -ENOMEM;
557 goto done;
558 }
559 INIT_LIST_HEAD(&client_data->list);
560 client_data->nb = nb;
561 strlcpy(client_data->client_name, client_name,
562 sizeof(client_data->client_name));
563 client_data->service = NO_SERVICE;
564 client_data->domain = domain;
565
566 mutex_lock(&notifier_mutex);
567 ret = audio_notifer_reg_client(client_data);
568 if (ret < 0) {
569 mutex_unlock(&notifier_mutex);
570 pr_err("%s: audio_notifer_reg_client for client %s failed ret = %d\n",
571 __func__, client_data->client_name,
572 ret);
573 kfree(client_data);
574 goto done;
575 }
576 list_add_tail(&client_data->list, &client_list);
577 mutex_unlock(&notifier_mutex);
578done:
579 return ret;
580}
581EXPORT_SYMBOL(audio_notifier_register);
582
583static int __init audio_notifier_subsys_init(void)
584{
585 int i, j;
586
587 mutex_init(&notifier_mutex);
588 INIT_LIST_HEAD(&client_list);
589 for (i = 0; i < AUDIO_NOTIFIER_MAX_SERVICES; i++) {
590 for (j = 0; j < AUDIO_NOTIFIER_MAX_DOMAINS; j++) {
591 if (service_data[i][j].state <= NO_SERVICE)
592 continue;
593
594 srcu_init_notifier_head(
595 &service_data[i][j].client_nb_list);
596 }
597 }
598
599 return 0;
600}
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530601
602static int __init audio_notifier_late_init(void)
603{
604 /*
605 * If pdr registration failed, register clients on next service
606 * Do in late init to ensure that SSR subsystem is initialized
607 */
608 mutex_lock(&notifier_mutex);
609 if (!audio_notifer_is_service_enabled(AUDIO_NOTIFIER_PDR_SERVICE))
610 audio_notifer_reg_all_clients();
611
612 mutex_unlock(&notifier_mutex);
613 return 0;
614}
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530615
616static int __init audio_notifier_init(void)
617{
618 int ret;
619
620 audio_notifier_subsys_init();
621
622 ret = audio_pdr_register(&pdr_nb);
623 if (ret < 0) {
624 pr_err("%s: PDR register failed, ret = %d, disable service\n",
625 __func__, ret);
626 audio_notifer_disable_service(AUDIO_NOTIFIER_PDR_SERVICE);
627 }
628
629 /* Do not return error since PDR enablement is not critical */
630 audio_notifier_late_init();
631
632 return 0;
633}
634module_init(audio_notifier_init);
635
636static void __exit audio_notifier_exit(void)
637{
638 audio_pdr_deregister(&pdr_nb);
639}
640module_exit(audio_notifier_exit);
641
642MODULE_DESCRIPTION("Audio notifier driver");
643MODULE_LICENSE("GPL v2");