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