blob: 2d3225f153c963643c64b7db4a0e1d01c9f7bb08 [file] [log] [blame]
Karthikeyan Mani989ccac2019-01-15 16:36:08 -08001/* Copyright (c) 2010-2014, 2016-2019 The Linux Foundation. All rights reserved.
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05302 *
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/kernel.h>
14#include <linux/module.h>
15#include <linux/types.h>
16#include <linux/uaccess.h>
17#include <linux/spinlock.h>
18#include <linux/list.h>
19#include <linux/sched.h>
20#include <linux/wait.h>
21#include <linux/errno.h>
22#include <linux/fs.h>
23#include <linux/delay.h>
24#include <linux/debugfs.h>
25#include <linux/platform_device.h>
26#include <linux/sysfs.h>
27#include <linux/device.h>
Laxminath Kasam31b26c52018-02-12 16:32:01 +053028#include <linux/of.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053029#include <linux/slab.h>
Laxminath Kasam605b42f2017-08-01 22:02:15 +053030#include <linux/ipc_logging.h>
Laxminath Kasamdad93a32019-03-13 13:05:48 +053031#include <linux/of_device.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053032#include <soc/qcom/subsystem_restart.h>
33#include <soc/qcom/scm.h>
Laxminath Kasam605b42f2017-08-01 22:02:15 +053034#include <dsp/apr_audio-v2.h>
35#include <dsp/audio_notifier.h>
36#include <ipc/apr.h>
37#include <ipc/apr_tal.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053038
39#define APR_PKT_IPC_LOG_PAGE_CNT 2
40
Laxminath Kasam31b26c52018-02-12 16:32:01 +053041static struct device *apr_dev_ptr;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053042static struct apr_q6 q6;
43static struct apr_client client[APR_DEST_MAX][APR_CLIENT_MAX];
44static void *apr_pkt_ctx;
45static wait_queue_head_t dsp_wait;
46static wait_queue_head_t modem_wait;
47static bool is_modem_up;
48static bool is_initial_boot;
Laxminath Kasam31b26c52018-02-12 16:32:01 +053049static bool is_child_devices_loaded;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053050/* Subsystem restart: QDSP6 data, functions */
51static struct workqueue_struct *apr_reset_workqueue;
52static void apr_reset_deregister(struct work_struct *work);
53static void dispatch_event(unsigned long code, uint16_t proc);
54struct apr_reset_work {
55 void *handle;
56 struct work_struct work;
57};
58
59static bool apr_cf_debug;
Laxminath Kasamdad93a32019-03-13 13:05:48 +053060static struct delayed_work add_chld_dev_work;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053061
62#ifdef CONFIG_DEBUG_FS
63static struct dentry *debugfs_apr_debug;
64static ssize_t apr_debug_write(struct file *filp, const char __user *ubuf,
65 size_t cnt, loff_t *ppos)
66{
67 char cmd;
68
69 if (copy_from_user(&cmd, ubuf, 1))
70 return -EFAULT;
71
72 apr_cf_debug = (cmd == '1') ? true : false;
73
74 return cnt;
75}
76
77static const struct file_operations apr_debug_ops = {
78 .write = apr_debug_write,
79};
80#endif
81
82#define APR_PKT_INFO(x...) \
83do { \
84 if (apr_pkt_ctx) \
85 ipc_log_string(apr_pkt_ctx, "<APR>: "x); \
86} while (0)
87
88
89struct apr_svc_table {
90 char name[64];
91 int idx;
92 int id;
93 int client_id;
94};
95
96static const struct apr_svc_table svc_tbl_qdsp6[] = {
97 {
98 .name = "AFE",
99 .idx = 0,
100 .id = APR_SVC_AFE,
101 .client_id = APR_CLIENT_AUDIO,
102 },
103 {
104 .name = "ASM",
105 .idx = 1,
106 .id = APR_SVC_ASM,
107 .client_id = APR_CLIENT_AUDIO,
108 },
109 {
110 .name = "ADM",
111 .idx = 2,
112 .id = APR_SVC_ADM,
113 .client_id = APR_CLIENT_AUDIO,
114 },
115 {
116 .name = "CORE",
117 .idx = 3,
118 .id = APR_SVC_ADSP_CORE,
119 .client_id = APR_CLIENT_AUDIO,
120 },
121 {
122 .name = "TEST",
123 .idx = 4,
124 .id = APR_SVC_TEST_CLIENT,
125 .client_id = APR_CLIENT_AUDIO,
126 },
127 {
128 .name = "MVM",
129 .idx = 5,
130 .id = APR_SVC_ADSP_MVM,
131 .client_id = APR_CLIENT_AUDIO,
132 },
133 {
134 .name = "CVS",
135 .idx = 6,
136 .id = APR_SVC_ADSP_CVS,
137 .client_id = APR_CLIENT_AUDIO,
138 },
139 {
140 .name = "CVP",
141 .idx = 7,
142 .id = APR_SVC_ADSP_CVP,
143 .client_id = APR_CLIENT_AUDIO,
144 },
145 {
146 .name = "USM",
147 .idx = 8,
148 .id = APR_SVC_USM,
149 .client_id = APR_CLIENT_AUDIO,
150 },
151 {
152 .name = "VIDC",
153 .idx = 9,
154 .id = APR_SVC_VIDC,
155 },
156 {
157 .name = "LSM",
158 .idx = 10,
159 .id = APR_SVC_LSM,
160 .client_id = APR_CLIENT_AUDIO,
161 },
162};
163
164static struct apr_svc_table svc_tbl_voice[] = {
165 {
166 .name = "VSM",
167 .idx = 0,
168 .id = APR_SVC_VSM,
169 .client_id = APR_CLIENT_VOICE,
170 },
171 {
172 .name = "VPM",
173 .idx = 1,
174 .id = APR_SVC_VPM,
175 .client_id = APR_CLIENT_VOICE,
176 },
177 {
178 .name = "MVS",
179 .idx = 2,
180 .id = APR_SVC_MVS,
181 .client_id = APR_CLIENT_VOICE,
182 },
183 {
184 .name = "MVM",
185 .idx = 3,
186 .id = APR_SVC_MVM,
187 .client_id = APR_CLIENT_VOICE,
188 },
189 {
190 .name = "CVS",
191 .idx = 4,
192 .id = APR_SVC_CVS,
193 .client_id = APR_CLIENT_VOICE,
194 },
195 {
196 .name = "CVP",
197 .idx = 5,
198 .id = APR_SVC_CVP,
199 .client_id = APR_CLIENT_VOICE,
200 },
201 {
202 .name = "SRD",
203 .idx = 6,
204 .id = APR_SVC_SRD,
205 .client_id = APR_CLIENT_VOICE,
206 },
207 {
208 .name = "TEST",
209 .idx = 7,
210 .id = APR_SVC_TEST_CLIENT,
211 .client_id = APR_CLIENT_VOICE,
212 },
213};
214
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530215/**
216 * apr_get_modem_state:
217 *
218 * Returns current modem load status
219 *
220 */
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530221enum apr_subsys_state apr_get_modem_state(void)
222{
223 return atomic_read(&q6.modem_state);
224}
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530225EXPORT_SYMBOL(apr_get_modem_state);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530226
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530227/**
228 * apr_set_modem_state - Update modem load status.
229 *
230 * @state: State to update modem load status
231 *
232 */
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530233void apr_set_modem_state(enum apr_subsys_state state)
234{
235 atomic_set(&q6.modem_state, state);
236}
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530237EXPORT_SYMBOL(apr_set_modem_state);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530238
239enum apr_subsys_state apr_cmpxchg_modem_state(enum apr_subsys_state prev,
240 enum apr_subsys_state new)
241{
242 return atomic_cmpxchg(&q6.modem_state, prev, new);
243}
244
245static void apr_modem_down(unsigned long opcode)
246{
247 apr_set_modem_state(APR_SUBSYS_DOWN);
248 dispatch_event(opcode, APR_DEST_MODEM);
249}
250
251static void apr_modem_up(void)
252{
253 if (apr_cmpxchg_modem_state(APR_SUBSYS_DOWN, APR_SUBSYS_UP) ==
254 APR_SUBSYS_DOWN)
255 wake_up(&modem_wait);
256 is_modem_up = 1;
257}
258
259enum apr_subsys_state apr_get_q6_state(void)
260{
261 return atomic_read(&q6.q6_state);
262}
263EXPORT_SYMBOL(apr_get_q6_state);
264
265int apr_set_q6_state(enum apr_subsys_state state)
266{
267 pr_debug("%s: setting adsp state %d\n", __func__, state);
268 if (state < APR_SUBSYS_DOWN || state > APR_SUBSYS_LOADED)
269 return -EINVAL;
270 atomic_set(&q6.q6_state, state);
271 return 0;
272}
273EXPORT_SYMBOL(apr_set_q6_state);
274
275enum apr_subsys_state apr_cmpxchg_q6_state(enum apr_subsys_state prev,
276 enum apr_subsys_state new)
277{
278 return atomic_cmpxchg(&q6.q6_state, prev, new);
279}
280
281static void apr_adsp_down(unsigned long opcode)
282{
283 apr_set_q6_state(APR_SUBSYS_DOWN);
284 dispatch_event(opcode, APR_DEST_QDSP6);
285}
286
Laxminath Kasamdad93a32019-03-13 13:05:48 +0530287static void apr_add_child_devices(struct work_struct *work)
288{
289 int ret;
290
291 ret = of_platform_populate(apr_dev_ptr->of_node,
292 NULL, NULL, apr_dev_ptr);
293 if (ret)
294 dev_err(apr_dev_ptr, "%s: failed to add child nodes, ret=%d\n",
295 __func__, ret);
296}
297
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530298static void apr_adsp_up(void)
299{
300 if (apr_cmpxchg_q6_state(APR_SUBSYS_DOWN, APR_SUBSYS_LOADED) ==
301 APR_SUBSYS_DOWN)
302 wake_up(&dsp_wait);
Laxminath Kasam31b26c52018-02-12 16:32:01 +0530303
304 if (!is_child_devices_loaded) {
Laxminath Kasamdad93a32019-03-13 13:05:48 +0530305 schedule_delayed_work(&add_chld_dev_work,
306 msecs_to_jiffies(100));
Laxminath Kasam31b26c52018-02-12 16:32:01 +0530307 is_child_devices_loaded = true;
308 }
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530309}
310
311int apr_wait_for_device_up(int dest_id)
312{
313 int rc = -1;
314
315 if (dest_id == APR_DEST_MODEM)
316 rc = wait_event_interruptible_timeout(modem_wait,
317 (apr_get_modem_state() == APR_SUBSYS_UP),
318 (1 * HZ));
319 else if (dest_id == APR_DEST_QDSP6)
320 rc = wait_event_interruptible_timeout(dsp_wait,
321 (apr_get_q6_state() == APR_SUBSYS_UP),
322 (1 * HZ));
323 else
324 pr_err("%s: unknown dest_id %d\n", __func__, dest_id);
325 /* returns left time */
326 return rc;
327}
328
329int apr_load_adsp_image(void)
330{
331 int rc = 0;
332
333 mutex_lock(&q6.lock);
334 if (apr_get_q6_state() == APR_SUBSYS_UP) {
335 q6.pil = subsystem_get("adsp");
336 if (IS_ERR(q6.pil)) {
337 rc = PTR_ERR(q6.pil);
338 pr_err("APR: Unable to load q6 image, error:%d\n", rc);
339 } else {
340 apr_set_q6_state(APR_SUBSYS_LOADED);
341 pr_debug("APR: Image is loaded, stated\n");
342 }
343 } else if (apr_get_q6_state() == APR_SUBSYS_LOADED) {
344 pr_debug("APR: q6 image already loaded\n");
345 } else {
346 pr_debug("APR: cannot load state %d\n", apr_get_q6_state());
347 }
348 mutex_unlock(&q6.lock);
349 return rc;
350}
351
352struct apr_client *apr_get_client(int dest_id, int client_id)
353{
354 return &client[dest_id][client_id];
355}
356
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530357/**
358 * apr_send_pkt - Clients call to send packet
359 * to destination processor.
360 *
361 * @handle: APR service handle
362 * @buf: payload to send to destination processor.
363 *
364 * Returns Bytes(>0)pkt_size on success or error on failure.
365 */
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530366int apr_send_pkt(void *handle, uint32_t *buf)
367{
368 struct apr_svc *svc = handle;
369 struct apr_client *clnt;
370 struct apr_hdr *hdr;
371 uint16_t dest_id;
372 uint16_t client_id;
373 uint16_t w_len;
374 int rc;
375 unsigned long flags;
376
377 if (!handle || !buf) {
378 pr_err("APR: Wrong parameters\n");
379 return -EINVAL;
380 }
381 if (svc->need_reset) {
Laxminath Kasamc1880702018-04-04 10:59:57 +0530382 pr_err_ratelimited("apr: send_pkt service need reset\n");
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530383 return -ENETRESET;
384 }
385
386 if ((svc->dest_id == APR_DEST_QDSP6) &&
387 (apr_get_q6_state() != APR_SUBSYS_LOADED)) {
Laxminath Kasamc1880702018-04-04 10:59:57 +0530388 pr_err_ratelimited("%s: Still dsp is not Up\n", __func__);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530389 return -ENETRESET;
390 } else if ((svc->dest_id == APR_DEST_MODEM) &&
391 (apr_get_modem_state() == APR_SUBSYS_DOWN)) {
392 pr_err("apr: Still Modem is not Up\n");
393 return -ENETRESET;
394 }
395
396 spin_lock_irqsave(&svc->w_lock, flags);
397 dest_id = svc->dest_id;
398 client_id = svc->client_id;
399 clnt = &client[dest_id][client_id];
400
401 if (!client[dest_id][client_id].handle) {
Laxminath Kasamc1880702018-04-04 10:59:57 +0530402 pr_err_ratelimited("APR: Still service is not yet opened\n");
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530403 spin_unlock_irqrestore(&svc->w_lock, flags);
404 return -EINVAL;
405 }
406 hdr = (struct apr_hdr *)buf;
407
408 hdr->src_domain = APR_DOMAIN_APPS;
409 hdr->src_svc = svc->id;
410 hdr->dest_domain = svc->dest_domain;
411 hdr->dest_svc = svc->id;
412
413 if (unlikely(apr_cf_debug)) {
414 APR_PKT_INFO(
415 "Tx: src_addr[0x%X] dest_addr[0x%X] opcode[0x%X] token[0x%X]",
416 (hdr->src_domain << 8) | hdr->src_svc,
417 (hdr->dest_domain << 8) | hdr->dest_svc, hdr->opcode,
418 hdr->token);
419 }
420
421 rc = apr_tal_write(clnt->handle, buf,
422 (struct apr_pkt_priv *)&svc->pkt_owner,
423 hdr->pkt_size);
424 if (rc >= 0) {
425 w_len = rc;
426 if (w_len != hdr->pkt_size) {
427 pr_err("%s: Unable to write whole APR pkt successfully: %d\n",
428 __func__, rc);
429 rc = -EINVAL;
430 }
431 } else {
432 pr_err("%s: Write APR pkt failed with error %d\n",
433 __func__, rc);
434 }
435 spin_unlock_irqrestore(&svc->w_lock, flags);
436
437 return rc;
438}
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530439EXPORT_SYMBOL(apr_send_pkt);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530440
441int apr_pkt_config(void *handle, struct apr_pkt_cfg *cfg)
442{
443 struct apr_svc *svc = (struct apr_svc *)handle;
444 uint16_t dest_id;
445 uint16_t client_id;
446 struct apr_client *clnt;
447
448 if (!handle) {
449 pr_err("%s: Invalid handle\n", __func__);
450 return -EINVAL;
451 }
452
453 if (svc->need_reset) {
454 pr_err("%s: service need reset\n", __func__);
455 return -ENETRESET;
456 }
457
458 svc->pkt_owner = cfg->pkt_owner;
459 dest_id = svc->dest_id;
460 client_id = svc->client_id;
461 clnt = &client[dest_id][client_id];
462
463 return apr_tal_rx_intents_config(clnt->handle,
464 cfg->intents.num_of_intents, cfg->intents.size);
465}
466
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530467/**
468 * apr_register - Clients call to register
469 * to APR.
470 *
471 * @dest: destination processor
472 * @svc_name: name of service to register as
473 * @svc_fn: callback function to trigger when response
474 * ack or packets received from destination processor.
475 * @src_port: Port number within a service
476 * @priv: private data of client, passed back in cb fn.
477 *
478 * Returns apr_svc handle on success or NULL on failure.
479 */
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530480struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn,
481 uint32_t src_port, void *priv)
482{
483 struct apr_client *clnt;
484 int client_id = 0;
485 int svc_idx = 0;
486 int svc_id = 0;
487 int dest_id = 0;
488 int domain_id = 0;
489 int temp_port = 0;
490 struct apr_svc *svc = NULL;
491 int rc = 0;
492 bool can_open_channel = true;
493
494 if (!dest || !svc_name || !svc_fn)
495 return NULL;
496
497 if (!strcmp(dest, "ADSP"))
498 domain_id = APR_DOMAIN_ADSP;
499 else if (!strcmp(dest, "MODEM")) {
500 /* Don't request for SMD channels if destination is MODEM,
501 * as these channels are no longer used and these clients
502 * are to listen only for MODEM SSR events
503 */
504 can_open_channel = false;
505 domain_id = APR_DOMAIN_MODEM;
506 } else {
507 pr_err("APR: wrong destination\n");
508 goto done;
509 }
510
511 dest_id = apr_get_dest_id(dest);
512
513 if (dest_id == APR_DEST_QDSP6) {
514 if (apr_get_q6_state() != APR_SUBSYS_LOADED) {
Laxminath Kasamc1880702018-04-04 10:59:57 +0530515 pr_err_ratelimited("%s: adsp not up\n", __func__);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530516 return NULL;
517 }
518 pr_debug("%s: adsp Up\n", __func__);
519 } else if (dest_id == APR_DEST_MODEM) {
520 if (apr_get_modem_state() == APR_SUBSYS_DOWN) {
521 if (is_modem_up) {
522 pr_err("%s: modem shutdown due to SSR, ret",
523 __func__);
524 return NULL;
525 }
526 pr_debug("%s: Wait for modem to bootup\n", __func__);
527 rc = apr_wait_for_device_up(APR_DEST_MODEM);
528 if (rc == 0) {
529 pr_err("%s: Modem is not Up\n", __func__);
530 return NULL;
531 }
532 }
533 pr_debug("%s: modem Up\n", __func__);
534 }
535
536 if (apr_get_svc(svc_name, domain_id, &client_id, &svc_idx, &svc_id)) {
Laxminath Kasamc1880702018-04-04 10:59:57 +0530537 pr_err_ratelimited("%s: apr_get_svc failed\n", __func__);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530538 goto done;
539 }
540
541 clnt = &client[dest_id][client_id];
542 mutex_lock(&clnt->m_lock);
543 if (!clnt->handle && can_open_channel) {
544 clnt->handle = apr_tal_open(client_id, dest_id,
545 APR_DL_SMD, apr_cb_func, NULL);
546 if (!clnt->handle) {
547 svc = NULL;
Laxminath Kasamc1880702018-04-04 10:59:57 +0530548 pr_err_ratelimited("APR: Unable to open handle\n");
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530549 mutex_unlock(&clnt->m_lock);
550 goto done;
551 }
552 }
553 mutex_unlock(&clnt->m_lock);
554 svc = &clnt->svc[svc_idx];
555 mutex_lock(&svc->m_lock);
556 clnt->id = client_id;
557 if (svc->need_reset) {
558 mutex_unlock(&svc->m_lock);
Laxminath Kasamc1880702018-04-04 10:59:57 +0530559 pr_err_ratelimited("APR: Service needs reset\n");
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530560 goto done;
561 }
562 svc->id = svc_id;
563 svc->dest_id = dest_id;
564 svc->client_id = client_id;
565 svc->dest_domain = domain_id;
566 svc->pkt_owner = APR_PKT_OWNER_DRIVER;
567
568 if (src_port != 0xFFFFFFFF) {
569 temp_port = ((src_port >> 8) * 8) + (src_port & 0xFF);
570 pr_debug("port = %d t_port = %d\n", src_port, temp_port);
571 if (temp_port >= APR_MAX_PORTS || temp_port < 0) {
572 pr_err("APR: temp_port out of bounds\n");
573 mutex_unlock(&svc->m_lock);
574 return NULL;
575 }
576 if (!svc->svc_cnt)
577 clnt->svc_cnt++;
578 svc->port_cnt++;
579 svc->port_fn[temp_port] = svc_fn;
580 svc->port_priv[temp_port] = priv;
581 svc->svc_cnt++;
582 } else {
583 if (!svc->fn) {
584 if (!svc->svc_cnt)
585 clnt->svc_cnt++;
586 svc->fn = svc_fn;
587 svc->priv = priv;
588 svc->svc_cnt++;
589 }
590 }
591
592 mutex_unlock(&svc->m_lock);
593done:
594 return svc;
595}
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530596EXPORT_SYMBOL(apr_register);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530597
598
599void apr_cb_func(void *buf, int len, void *priv)
600{
601 struct apr_client_data data;
602 struct apr_client *apr_client;
603 struct apr_svc *c_svc;
604 struct apr_hdr *hdr;
605 uint16_t hdr_size;
606 uint16_t msg_type;
607 uint16_t ver;
608 uint16_t src;
609 uint16_t svc;
610 uint16_t clnt;
611 int i;
612 int temp_port = 0;
613 uint32_t *ptr;
614
615 pr_debug("APR2: len = %d\n", len);
616 ptr = buf;
617 pr_debug("\n*****************\n");
618 for (i = 0; i < len/4; i++)
619 pr_debug("%x ", ptr[i]);
620 pr_debug("\n");
621 pr_debug("\n*****************\n");
622
623 if (!buf || len <= APR_HDR_SIZE) {
624 pr_err("APR: Improper apr pkt received:%pK %d\n", buf, len);
625 return;
626 }
627 hdr = buf;
628
629 ver = hdr->hdr_field;
630 ver = (ver & 0x000F);
631 if (ver > APR_PKT_VER + 1) {
632 pr_err("APR: Wrong version: %d\n", ver);
633 return;
634 }
635
636 hdr_size = hdr->hdr_field;
637 hdr_size = ((hdr_size & 0x00F0) >> 0x4) * 4;
638 if (hdr_size < APR_HDR_SIZE) {
639 pr_err("APR: Wrong hdr size:%d\n", hdr_size);
640 return;
641 }
642
643 if (hdr->pkt_size < APR_HDR_SIZE) {
644 pr_err("APR: Wrong paket size\n");
645 return;
646 }
Karthikeyan Mani989ccac2019-01-15 16:36:08 -0800647
648 if (hdr->pkt_size < hdr_size) {
649 pr_err("APR: Packet size less than header size\n");
650 return;
651 }
652
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530653 msg_type = hdr->hdr_field;
654 msg_type = (msg_type >> 0x08) & 0x0003;
655 if (msg_type >= APR_MSG_TYPE_MAX && msg_type != APR_BASIC_RSP_RESULT) {
656 pr_err("APR: Wrong message type: %d\n", msg_type);
657 return;
658 }
659
660 if (hdr->src_domain >= APR_DOMAIN_MAX ||
661 hdr->dest_domain >= APR_DOMAIN_MAX ||
662 hdr->src_svc >= APR_SVC_MAX ||
663 hdr->dest_svc >= APR_SVC_MAX) {
664 pr_err("APR: Wrong APR header\n");
665 return;
666 }
667
668 svc = hdr->dest_svc;
669 if (hdr->src_domain == APR_DOMAIN_MODEM) {
670 if (svc == APR_SVC_MVS || svc == APR_SVC_MVM ||
671 svc == APR_SVC_CVS || svc == APR_SVC_CVP ||
672 svc == APR_SVC_TEST_CLIENT)
673 clnt = APR_CLIENT_VOICE;
674 else {
675 pr_err("APR: Wrong svc :%d\n", svc);
676 return;
677 }
678 } else if (hdr->src_domain == APR_DOMAIN_ADSP) {
679 if (svc == APR_SVC_AFE || svc == APR_SVC_ASM ||
680 svc == APR_SVC_VSM || svc == APR_SVC_VPM ||
681 svc == APR_SVC_ADM || svc == APR_SVC_ADSP_CORE ||
682 svc == APR_SVC_USM ||
683 svc == APR_SVC_TEST_CLIENT || svc == APR_SVC_ADSP_MVM ||
684 svc == APR_SVC_ADSP_CVS || svc == APR_SVC_ADSP_CVP ||
685 svc == APR_SVC_LSM)
686 clnt = APR_CLIENT_AUDIO;
687 else if (svc == APR_SVC_VIDC)
688 clnt = APR_CLIENT_AUDIO;
689 else {
690 pr_err("APR: Wrong svc :%d\n", svc);
691 return;
692 }
693 } else {
694 pr_err("APR: Pkt from wrong source: %d\n", hdr->src_domain);
695 return;
696 }
697
698 src = apr_get_data_src(hdr);
699 if (src == APR_DEST_MAX)
700 return;
701
702 pr_debug("src =%d clnt = %d\n", src, clnt);
703 apr_client = &client[src][clnt];
704 for (i = 0; i < APR_SVC_MAX; i++)
705 if (apr_client->svc[i].id == svc) {
706 pr_debug("%d\n", apr_client->svc[i].id);
707 c_svc = &apr_client->svc[i];
708 break;
709 }
710
711 if (i == APR_SVC_MAX) {
712 pr_err("APR: service is not registered\n");
713 return;
714 }
715 pr_debug("svc_idx = %d\n", i);
716 pr_debug("%x %x %x %pK %pK\n", c_svc->id, c_svc->dest_id,
717 c_svc->client_id, c_svc->fn, c_svc->priv);
718 data.payload_size = hdr->pkt_size - hdr_size;
719 data.opcode = hdr->opcode;
720 data.src = src;
721 data.src_port = hdr->src_port;
722 data.dest_port = hdr->dest_port;
723 data.token = hdr->token;
724 data.msg_type = msg_type;
725 data.payload = NULL;
726 if (data.payload_size > 0)
727 data.payload = (char *)hdr + hdr_size;
728
729 if (unlikely(apr_cf_debug)) {
730 if (hdr->opcode == APR_BASIC_RSP_RESULT && data.payload) {
731 uint32_t *ptr = data.payload;
732
733 APR_PKT_INFO(
734 "Rx: src_addr[0x%X] dest_addr[0x%X] opcode[0x%X] token[0x%X] rc[0x%X]",
735 (hdr->src_domain << 8) | hdr->src_svc,
736 (hdr->dest_domain << 8) | hdr->dest_svc,
737 hdr->opcode, hdr->token, ptr[1]);
738 } else {
739 APR_PKT_INFO(
740 "Rx: src_addr[0x%X] dest_addr[0x%X] opcode[0x%X] token[0x%X]",
741 (hdr->src_domain << 8) | hdr->src_svc,
742 (hdr->dest_domain << 8) | hdr->dest_svc, hdr->opcode,
743 hdr->token);
744 }
745 }
746
747 temp_port = ((data.dest_port >> 8) * 8) + (data.dest_port & 0xFF);
Aditya Bavanarid3047b22017-11-22 15:29:52 +0530748 if (((temp_port >= 0) && (temp_port < APR_MAX_PORTS))
749 && (c_svc->port_cnt && c_svc->port_fn[temp_port]))
750 c_svc->port_fn[temp_port](&data,
751 c_svc->port_priv[temp_port]);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530752 else if (c_svc->fn)
753 c_svc->fn(&data, c_svc->priv);
754 else
755 pr_err("APR: Rxed a packet for NULL callback\n");
756}
757
758int apr_get_svc(const char *svc_name, int domain_id, int *client_id,
759 int *svc_idx, int *svc_id)
760{
761 int i;
762 int size;
763 struct apr_svc_table *tbl;
764 int ret = 0;
765
766 if (domain_id == APR_DOMAIN_ADSP) {
767 tbl = (struct apr_svc_table *)&svc_tbl_qdsp6;
768 size = ARRAY_SIZE(svc_tbl_qdsp6);
769 } else {
770 tbl = (struct apr_svc_table *)&svc_tbl_voice;
771 size = ARRAY_SIZE(svc_tbl_voice);
772 }
773
774 for (i = 0; i < size; i++) {
775 if (!strcmp(svc_name, tbl[i].name)) {
776 *client_id = tbl[i].client_id;
777 *svc_idx = tbl[i].idx;
778 *svc_id = tbl[i].id;
779 break;
780 }
781 }
782
783 pr_debug("%s: svc_name = %s c_id = %d domain_id = %d\n",
784 __func__, svc_name, *client_id, domain_id);
785 if (i == size) {
786 pr_err("%s: APR: Wrong svc name %s\n", __func__, svc_name);
787 ret = -EINVAL;
788 }
789
790 return ret;
791}
792
793static void apr_reset_deregister(struct work_struct *work)
794{
795 struct apr_svc *handle = NULL;
796 struct apr_reset_work *apr_reset =
797 container_of(work, struct apr_reset_work, work);
798
799 handle = apr_reset->handle;
800 pr_debug("%s:handle[%pK]\n", __func__, handle);
801 apr_deregister(handle);
802 kfree(apr_reset);
803}
804
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530805/**
Banajit Goswami88327322017-12-08 10:51:58 -0800806 * apr_start_rx_rt - Clients call to vote for thread
807 * priority upgrade whenever needed.
808 *
809 * @handle: APR service handle
810 *
811 * Returns 0 on success or error otherwise.
812 */
813int apr_start_rx_rt(void *handle)
814{
815 int rc = 0;
816 struct apr_svc *svc = handle;
817 uint16_t dest_id = 0;
818 uint16_t client_id = 0;
819
820 if (!svc) {
821 pr_err("%s: Invalid APR handle\n", __func__);
822 return -EINVAL;
823 }
824
825 mutex_lock(&svc->m_lock);
826 dest_id = svc->dest_id;
827 client_id = svc->client_id;
828
829 if ((client_id >= APR_CLIENT_MAX) || (dest_id >= APR_DEST_MAX)) {
830 pr_err("%s: %s invalid. client_id = %u, dest_id = %u\n",
831 __func__,
832 client_id >= APR_CLIENT_MAX ? "Client ID" : "Dest ID",
833 client_id, dest_id);
834 rc = -EINVAL;
835 goto exit;
836 }
837
838 if (!client[dest_id][client_id].handle) {
839 pr_err("%s: Client handle is NULL\n", __func__);
840 rc = -EINVAL;
841 goto exit;
842 }
843
844 rc = apr_tal_start_rx_rt(client[dest_id][client_id].handle);
845 if (rc)
846 pr_err("%s: failed to set RT thread priority for APR RX. rc = %d\n",
847 __func__, rc);
848
849exit:
850 mutex_unlock(&svc->m_lock);
851 return rc;
852}
853EXPORT_SYMBOL(apr_start_rx_rt);
854
855/**
856 * apr_end_rx_rt - Clients call to unvote for thread
857 * priority upgrade (perviously voted with
858 * apr_start_rx_rt()).
859 *
860 * @handle: APR service handle
861 *
862 * Returns 0 on success or error otherwise.
863 */
864int apr_end_rx_rt(void *handle)
865{
866 int rc = 0;
867 struct apr_svc *svc = handle;
868 uint16_t dest_id = 0;
869 uint16_t client_id = 0;
870
871 if (!svc) {
872 pr_err("%s: Invalid APR handle\n", __func__);
873 return -EINVAL;
874 }
875
876 mutex_lock(&svc->m_lock);
877 dest_id = svc->dest_id;
878 client_id = svc->client_id;
879
880 if ((client_id >= APR_CLIENT_MAX) || (dest_id >= APR_DEST_MAX)) {
881 pr_err("%s: %s invalid. client_id = %u, dest_id = %u\n",
882 __func__,
883 client_id >= APR_CLIENT_MAX ? "Client ID" : "Dest ID",
884 client_id, dest_id);
885 rc = -EINVAL;
886 goto exit;
887 }
888
889 if (!client[dest_id][client_id].handle) {
890 pr_err("%s: Client handle is NULL\n", __func__);
891 rc = -EINVAL;
892 goto exit;
893 }
894
895 rc = apr_tal_end_rx_rt(client[dest_id][client_id].handle);
896 if (rc)
897 pr_err("%s: failed to reset RT thread priority for APR RX. rc = %d\n",
898 __func__, rc);
899
900exit:
901 mutex_unlock(&svc->m_lock);
902 return rc;
903}
904EXPORT_SYMBOL(apr_end_rx_rt);
905
906/**
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530907 * apr_deregister - Clients call to de-register
908 * from APR.
909 *
910 * @handle: APR service handle to de-register
911 *
912 * Returns 0 on success or -EINVAL on error.
913 */
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530914int apr_deregister(void *handle)
915{
916 struct apr_svc *svc = handle;
917 struct apr_client *clnt;
918 uint16_t dest_id;
919 uint16_t client_id;
920
921 if (!handle)
922 return -EINVAL;
923
924 mutex_lock(&svc->m_lock);
925 if (!svc->svc_cnt) {
926 pr_err("%s: svc already deregistered. svc = %pK\n",
927 __func__, svc);
928 mutex_unlock(&svc->m_lock);
929 return -EINVAL;
930 }
931
932 dest_id = svc->dest_id;
933 client_id = svc->client_id;
934 clnt = &client[dest_id][client_id];
935
936 if (svc->svc_cnt > 0) {
937 if (svc->port_cnt)
938 svc->port_cnt--;
939 svc->svc_cnt--;
940 if (!svc->svc_cnt) {
941 client[dest_id][client_id].svc_cnt--;
942 pr_debug("%s: service is reset %pK\n", __func__, svc);
943 }
944 }
945
946 if (!svc->svc_cnt) {
947 svc->priv = NULL;
948 svc->id = 0;
949 svc->fn = NULL;
950 svc->dest_id = 0;
951 svc->client_id = 0;
952 svc->need_reset = 0x0;
953 }
954 if (client[dest_id][client_id].handle &&
955 !client[dest_id][client_id].svc_cnt) {
956 apr_tal_close(client[dest_id][client_id].handle);
957 client[dest_id][client_id].handle = NULL;
958 }
959 mutex_unlock(&svc->m_lock);
960
961 return 0;
962}
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530963EXPORT_SYMBOL(apr_deregister);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530964
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530965/**
966 * apr_reset - sets up workqueue to de-register
967 * the given APR service handle.
968 *
969 * @handle: APR service handle
970 *
971 */
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530972void apr_reset(void *handle)
973{
974 struct apr_reset_work *apr_reset_worker = NULL;
975
976 if (!handle)
977 return;
978 pr_debug("%s: handle[%pK]\n", __func__, handle);
979
980 if (apr_reset_workqueue == NULL) {
981 pr_err("%s: apr_reset_workqueue is NULL\n", __func__);
982 return;
983 }
984
985 apr_reset_worker = kzalloc(sizeof(struct apr_reset_work),
986 GFP_ATOMIC);
987
988 if (apr_reset_worker == NULL) {
989 pr_err("%s: mem failure\n", __func__);
990 return;
991 }
992
993 apr_reset_worker->handle = handle;
994 INIT_WORK(&apr_reset_worker->work, apr_reset_deregister);
995 queue_work(apr_reset_workqueue, &apr_reset_worker->work);
996}
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530997EXPORT_SYMBOL(apr_reset);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530998
999/* Dispatch the Reset events to Modem and audio clients */
1000static void dispatch_event(unsigned long code, uint16_t proc)
1001{
1002 struct apr_client *apr_client;
1003 struct apr_client_data data;
1004 struct apr_svc *svc;
1005 uint16_t clnt;
1006 int i, j;
1007
Laxminath Kasam8f7ccc22017-08-28 17:35:04 +05301008 memset(&data, 0, sizeof(data));
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301009 data.opcode = RESET_EVENTS;
1010 data.reset_event = code;
1011
1012 /* Service domain can be different from the processor */
1013 data.reset_proc = apr_get_reset_domain(proc);
1014
1015 clnt = APR_CLIENT_AUDIO;
1016 apr_client = &client[proc][clnt];
1017 for (i = 0; i < APR_SVC_MAX; i++) {
1018 mutex_lock(&apr_client->svc[i].m_lock);
1019 if (apr_client->svc[i].fn) {
1020 apr_client->svc[i].need_reset = 0x1;
1021 apr_client->svc[i].fn(&data, apr_client->svc[i].priv);
1022 }
1023 if (apr_client->svc[i].port_cnt) {
1024 svc = &(apr_client->svc[i]);
1025 svc->need_reset = 0x1;
1026 for (j = 0; j < APR_MAX_PORTS; j++)
1027 if (svc->port_fn[j])
1028 svc->port_fn[j](&data,
1029 svc->port_priv[j]);
1030 }
1031 mutex_unlock(&apr_client->svc[i].m_lock);
1032 }
1033
1034 clnt = APR_CLIENT_VOICE;
1035 apr_client = &client[proc][clnt];
1036 for (i = 0; i < APR_SVC_MAX; i++) {
1037 mutex_lock(&apr_client->svc[i].m_lock);
1038 if (apr_client->svc[i].fn) {
1039 apr_client->svc[i].need_reset = 0x1;
1040 apr_client->svc[i].fn(&data, apr_client->svc[i].priv);
1041 }
1042 if (apr_client->svc[i].port_cnt) {
1043 svc = &(apr_client->svc[i]);
1044 svc->need_reset = 0x1;
1045 for (j = 0; j < APR_MAX_PORTS; j++)
1046 if (svc->port_fn[j])
1047 svc->port_fn[j](&data,
1048 svc->port_priv[j]);
1049 }
1050 mutex_unlock(&apr_client->svc[i].m_lock);
1051 }
1052}
1053
1054static int apr_notifier_service_cb(struct notifier_block *this,
1055 unsigned long opcode, void *data)
1056{
1057 struct audio_notifier_cb_data *cb_data = data;
1058
1059 if (cb_data == NULL) {
1060 pr_err("%s: Callback data is NULL!\n", __func__);
1061 goto done;
1062 }
1063
1064 pr_debug("%s: Service opcode 0x%lx, domain %d\n",
1065 __func__, opcode, cb_data->domain);
1066
1067 switch (opcode) {
1068 case AUDIO_NOTIFIER_SERVICE_DOWN:
1069 /*
1070 * Use flag to ignore down notifications during
1071 * initial boot. There is no benefit from error
1072 * recovery notifications during initial boot
1073 * up since everything is expected to be down.
1074 */
1075 if (is_initial_boot) {
1076 is_initial_boot = false;
1077 break;
1078 }
1079 if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN)
1080 apr_modem_down(opcode);
1081 else
1082 apr_adsp_down(opcode);
1083 break;
1084 case AUDIO_NOTIFIER_SERVICE_UP:
1085 is_initial_boot = false;
1086 if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN)
1087 apr_modem_up();
1088 else
1089 apr_adsp_up();
1090 break;
1091 default:
1092 break;
1093 }
1094done:
1095 return NOTIFY_OK;
1096}
1097
1098static struct notifier_block adsp_service_nb = {
1099 .notifier_call = apr_notifier_service_cb,
1100 .priority = 0,
1101};
1102
1103static struct notifier_block modem_service_nb = {
1104 .notifier_call = apr_notifier_service_cb,
1105 .priority = 0,
1106};
1107
Laxminath Kasam8b1366a2017-10-05 01:44:16 +05301108#ifdef CONFIG_DEBUG_FS
1109static int __init apr_debug_init(void)
1110{
1111 debugfs_apr_debug = debugfs_create_file("msm_apr_debug",
1112 S_IFREG | 0444, NULL, NULL,
1113 &apr_debug_ops);
1114 return 0;
1115}
1116#else
1117static int __init apr_debug_init(void)
1118(
1119 return 0;
1120)
1121#endif
1122
Laxminath Kasam31b26c52018-02-12 16:32:01 +05301123static void apr_cleanup(void)
1124{
1125 int i, j, k;
1126
1127 subsys_notif_deregister("apr_modem");
1128 subsys_notif_deregister("apr_adsp");
1129 if (apr_reset_workqueue)
1130 destroy_workqueue(apr_reset_workqueue);
1131 mutex_destroy(&q6.lock);
1132 for (i = 0; i < APR_DEST_MAX; i++) {
1133 for (j = 0; j < APR_CLIENT_MAX; j++) {
1134 mutex_destroy(&client[i][j].m_lock);
1135 for (k = 0; k < APR_SVC_MAX; k++)
1136 mutex_destroy(&client[i][j].svc[k].m_lock);
1137 }
1138 }
1139}
1140
1141static int apr_probe(struct platform_device *pdev)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301142{
1143 int i, j, k;
1144
Laxminath Kasam8b1366a2017-10-05 01:44:16 +05301145 init_waitqueue_head(&dsp_wait);
1146 init_waitqueue_head(&modem_wait);
1147
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301148 for (i = 0; i < APR_DEST_MAX; i++)
1149 for (j = 0; j < APR_CLIENT_MAX; j++) {
1150 mutex_init(&client[i][j].m_lock);
1151 for (k = 0; k < APR_SVC_MAX; k++) {
1152 mutex_init(&client[i][j].svc[k].m_lock);
1153 spin_lock_init(&client[i][j].svc[k].w_lock);
1154 }
1155 }
1156 apr_set_subsys_state();
1157 mutex_init(&q6.lock);
1158 apr_reset_workqueue = create_singlethread_workqueue("apr_driver");
1159 if (!apr_reset_workqueue)
1160 return -ENOMEM;
1161
1162 apr_pkt_ctx = ipc_log_context_create(APR_PKT_IPC_LOG_PAGE_CNT,
1163 "apr", 0);
1164 if (!apr_pkt_ctx)
1165 pr_err("%s: Unable to create ipc log context\n", __func__);
1166
1167 is_initial_boot = true;
1168 subsys_notif_register("apr_adsp", AUDIO_NOTIFIER_ADSP_DOMAIN,
1169 &adsp_service_nb);
1170 subsys_notif_register("apr_modem", AUDIO_NOTIFIER_MODEM_DOMAIN,
1171 &modem_service_nb);
1172
Laxminath Kasam8b1366a2017-10-05 01:44:16 +05301173 apr_tal_init();
Laxminath Kasam31b26c52018-02-12 16:32:01 +05301174 apr_dev_ptr = &pdev->dev;
Laxminath Kasamdad93a32019-03-13 13:05:48 +05301175 INIT_DELAYED_WORK(&add_chld_dev_work, apr_add_child_devices);
Laxminath Kasam8b1366a2017-10-05 01:44:16 +05301176 return apr_debug_init();
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301177}
Laxminath Kasam31b26c52018-02-12 16:32:01 +05301178
1179static int apr_remove(struct platform_device *pdev)
1180{
1181 apr_cleanup();
1182 return 0;
1183}
1184
1185static const struct of_device_id apr_machine_of_match[] = {
1186 { .compatible = "qcom,msm-audio-apr", },
1187 {},
1188};
1189
1190static struct platform_driver apr_driver = {
1191 .probe = apr_probe,
1192 .remove = apr_remove,
1193 .driver = {
1194 .name = "audio_apr",
1195 .owner = THIS_MODULE,
1196 .of_match_table = apr_machine_of_match,
1197 }
1198};
1199
1200static int __init apr_init(void)
1201{
1202 platform_driver_register(&apr_driver);
1203 apr_dummy_init();
1204 return 0;
1205}
Laxminath Kasam8b1366a2017-10-05 01:44:16 +05301206module_init(apr_init);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301207
Laxminath Kasam31b26c52018-02-12 16:32:01 +05301208static void __exit apr_exit(void)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301209{
Laxminath Kasam31b26c52018-02-12 16:32:01 +05301210 apr_dummy_exit();
1211 platform_driver_unregister(&apr_driver);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301212}
Laxminath Kasam8b1366a2017-10-05 01:44:16 +05301213module_exit(apr_exit);
Laxminath Kasam31b26c52018-02-12 16:32:01 +05301214
1215MODULE_DESCRIPTION("APR DRIVER");
Laxminath Kasam8b1366a2017-10-05 01:44:16 +05301216MODULE_LICENSE("GPL v2");
Laxminath Kasam31b26c52018-02-12 16:32:01 +05301217MODULE_DEVICE_TABLE(of, apr_machine_of_match);