blob: 6e60db106ef32deb83e6305107d95cddbbc12e6d [file] [log] [blame]
Joonwoo Parkb91e48e2013-02-21 10:24:56 -08001/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070018#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>
28#include <linux/slab.h>
Joonwoo Parkb91e48e2013-02-21 10:24:56 -080029#include <sound/apr_audio-v2.h>
Swaminathan Sathappan2fea0da2011-12-21 12:20:53 -080030#include <asm/mach-types.h>
Stephen Boyd77db8bb2012-06-27 15:15:16 -070031#include <mach/subsystem_restart.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032#include <mach/msm_smd.h>
33#include <mach/qdsp6v2/apr.h>
34#include <mach/qdsp6v2/apr_tal.h>
35#include <mach/qdsp6v2/dsp_debug.h>
36#include <mach/subsystem_notif.h>
37#include <mach/subsystem_restart.h>
38
Joonwoo Park91d95462012-08-02 10:55:54 -070039static struct apr_q6 q6;
40static struct apr_client client[APR_DEST_MAX][APR_CLIENT_MAX];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070041
Joonwoo Park91d95462012-08-02 10:55:54 -070042static wait_queue_head_t dsp_wait;
43static wait_queue_head_t modem_wait;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070044/* Subsystem restart: QDSP6 data, functions */
45static struct workqueue_struct *apr_reset_workqueue;
46static void apr_reset_deregister(struct work_struct *work);
47struct apr_reset_work {
48 void *handle;
49 struct work_struct work;
50};
51
Joonwoo Park91d95462012-08-02 10:55:54 -070052struct apr_svc_table {
53 char name[64];
54 int idx;
55 int id;
56 int client_id;
57};
58
Arun Menonc490f762012-10-02 15:57:08 -070059static const struct apr_svc_table svc_tbl_qdsp6[] = {
Joonwoo Park91d95462012-08-02 10:55:54 -070060 {
61 .name = "AFE",
62 .idx = 0,
63 .id = APR_SVC_AFE,
64 .client_id = APR_CLIENT_AUDIO,
65 },
66 {
67 .name = "ASM",
68 .idx = 1,
69 .id = APR_SVC_ASM,
70 .client_id = APR_CLIENT_AUDIO,
71 },
72 {
73 .name = "ADM",
74 .idx = 2,
75 .id = APR_SVC_ADM,
76 .client_id = APR_CLIENT_AUDIO,
77 },
78 {
79 .name = "CORE",
80 .idx = 3,
81 .id = APR_SVC_ADSP_CORE,
82 .client_id = APR_CLIENT_AUDIO,
83 },
84 {
85 .name = "TEST",
86 .idx = 4,
87 .id = APR_SVC_TEST_CLIENT,
88 .client_id = APR_CLIENT_AUDIO,
89 },
90 {
91 .name = "MVM",
92 .idx = 5,
93 .id = APR_SVC_ADSP_MVM,
94 .client_id = APR_CLIENT_AUDIO,
95 },
96 {
97 .name = "CVS",
98 .idx = 6,
99 .id = APR_SVC_ADSP_CVS,
100 .client_id = APR_CLIENT_AUDIO,
101 },
102 {
103 .name = "CVP",
104 .idx = 7,
105 .id = APR_SVC_ADSP_CVP,
106 .client_id = APR_CLIENT_AUDIO,
107 },
108 {
109 .name = "USM",
110 .idx = 8,
111 .id = APR_SVC_USM,
112 .client_id = APR_CLIENT_AUDIO,
113 },
Arun Menonc490f762012-10-02 15:57:08 -0700114 {
115 .name = "VIDC",
116 .idx = 9,
117 .id = APR_SVC_VIDC,
Joonwoo Parkb91e48e2013-02-21 10:24:56 -0800118 },
119 {
120 .name = "LSM",
121 .idx = 9,
122 .id = APR_SVC_LSM,
Arun Menonc490f762012-10-02 15:57:08 -0700123 .client_id = APR_CLIENT_AUDIO,
124 },
Joonwoo Park91d95462012-08-02 10:55:54 -0700125};
126
127static struct apr_svc_table svc_tbl_voice[] = {
128 {
129 .name = "VSM",
130 .idx = 0,
131 .id = APR_SVC_VSM,
132 .client_id = APR_CLIENT_VOICE,
133 },
134 {
135 .name = "VPM",
136 .idx = 1,
137 .id = APR_SVC_VPM,
138 .client_id = APR_CLIENT_VOICE,
139 },
140 {
141 .name = "MVS",
142 .idx = 2,
143 .id = APR_SVC_MVS,
144 .client_id = APR_CLIENT_VOICE,
145 },
146 {
147 .name = "MVM",
148 .idx = 3,
149 .id = APR_SVC_MVM,
150 .client_id = APR_CLIENT_VOICE,
151 },
152 {
153 .name = "CVS",
154 .idx = 4,
155 .id = APR_SVC_CVS,
156 .client_id = APR_CLIENT_VOICE,
157 },
158 {
159 .name = "CVP",
160 .idx = 5,
161 .id = APR_SVC_CVP,
162 .client_id = APR_CLIENT_VOICE,
163 },
164 {
165 .name = "SRD",
166 .idx = 6,
167 .id = APR_SVC_SRD,
168 .client_id = APR_CLIENT_VOICE,
169 },
170 {
171 .name = "TEST",
172 .idx = 7,
173 .id = APR_SVC_TEST_CLIENT,
174 .client_id = APR_CLIENT_VOICE,
175 },
176};
177
178enum apr_subsys_state apr_get_modem_state(void)
179{
180 return atomic_read(&q6.modem_state);
181}
182
183void apr_set_modem_state(enum apr_subsys_state state)
184{
185 atomic_set(&q6.modem_state, state);
186}
187
188enum apr_subsys_state apr_cmpxchg_modem_state(enum apr_subsys_state prev,
189 enum apr_subsys_state new)
190{
191 return atomic_cmpxchg(&q6.modem_state, prev, new);
192}
193
194enum apr_subsys_state apr_get_q6_state(void)
195{
196 return atomic_read(&q6.q6_state);
197}
198EXPORT_SYMBOL_GPL(apr_get_q6_state);
199
200int apr_set_q6_state(enum apr_subsys_state state)
201{
202 pr_debug("%s: setting adsp state %d\n", __func__, state);
203 if (state < APR_SUBSYS_DOWN || state > APR_SUBSYS_LOADED)
204 return -EINVAL;
205 atomic_set(&q6.q6_state, state);
206 return 0;
207}
208EXPORT_SYMBOL_GPL(apr_set_q6_state);
209
210enum apr_subsys_state apr_cmpxchg_q6_state(enum apr_subsys_state prev,
211 enum apr_subsys_state new)
212{
213 return atomic_cmpxchg(&q6.q6_state, prev, new);
214}
215
216int apr_wait_for_device_up(int dest_id)
217{
218 int rc = -1;
219 if (dest_id == APR_DEST_MODEM)
220 rc = wait_event_interruptible_timeout(modem_wait,
221 (apr_get_modem_state() == APR_SUBSYS_UP),
222 (1 * HZ));
223 else if (dest_id == APR_DEST_QDSP6)
224 rc = wait_event_interruptible_timeout(dsp_wait,
225 (apr_get_q6_state() == APR_SUBSYS_UP),
226 (1 * HZ));
227 else
228 pr_err("%s: unknown dest_id %d\n", __func__, dest_id);
229 /* returns left time */
230 return rc;
231}
232
233int apr_load_adsp_image(void)
234{
235 int rc = 0;
236 mutex_lock(&q6.lock);
237 if (apr_get_q6_state() == APR_SUBSYS_UP) {
Stephen Boyd77db8bb2012-06-27 15:15:16 -0700238 q6.pil = subsystem_get("adsp");
Joonwoo Park91d95462012-08-02 10:55:54 -0700239 if (IS_ERR(q6.pil)) {
240 rc = PTR_ERR(q6.pil);
241 pr_err("APR: Unable to load q6 image, error:%d\n", rc);
242 } else {
243 apr_set_q6_state(APR_SUBSYS_LOADED);
244 pr_debug("APR: Image is loaded, stated\n");
245 }
Ravishankar Sarawadi14dfeac2012-09-25 19:28:57 -0700246 } else if (apr_get_q6_state() == APR_SUBSYS_LOADED) {
247 pr_debug("APR: q6 image already loaded\n");
248 } else {
Joonwoo Park91d95462012-08-02 10:55:54 -0700249 pr_debug("APR: cannot load state %d\n", apr_get_q6_state());
Ravishankar Sarawadi14dfeac2012-09-25 19:28:57 -0700250 }
Joonwoo Park91d95462012-08-02 10:55:54 -0700251 mutex_unlock(&q6.lock);
252 return rc;
253}
254
255struct apr_client *apr_get_client(int dest_id, int client_id)
256{
257 return &client[dest_id][client_id];
258}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700259
260int apr_send_pkt(void *handle, uint32_t *buf)
261{
262 struct apr_svc *svc = handle;
263 struct apr_client *clnt;
264 struct apr_hdr *hdr;
265 uint16_t dest_id;
266 uint16_t client_id;
267 uint16_t w_len;
268 unsigned long flags;
269
270 if (!handle || !buf) {
271 pr_err("APR: Wrong parameters\n");
272 return -EINVAL;
273 }
274 if (svc->need_reset) {
275 pr_err("apr: send_pkt service need reset\n");
276 return -ENETRESET;
277 }
278
279 if ((svc->dest_id == APR_DEST_QDSP6) &&
Joonwoo Park91d95462012-08-02 10:55:54 -0700280 (apr_get_q6_state() != APR_SUBSYS_LOADED)) {
281 pr_err("%s: Still dsp is not Up\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700282 return -ENETRESET;
283 } else if ((svc->dest_id == APR_DEST_MODEM) &&
Joonwoo Park91d95462012-08-02 10:55:54 -0700284 (apr_get_modem_state() == APR_SUBSYS_DOWN)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700285 pr_err("apr: Still Modem is not Up\n");
286 return -ENETRESET;
287 }
288
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700289 spin_lock_irqsave(&svc->w_lock, flags);
290 dest_id = svc->dest_id;
291 client_id = svc->client_id;
292 clnt = &client[dest_id][client_id];
293
294 if (!client[dest_id][client_id].handle) {
295 pr_err("APR: Still service is not yet opened\n");
296 spin_unlock_irqrestore(&svc->w_lock, flags);
297 return -EINVAL;
298 }
299 hdr = (struct apr_hdr *)buf;
300
301 hdr->src_domain = APR_DOMAIN_APPS;
302 hdr->src_svc = svc->id;
303 if (dest_id == APR_DEST_MODEM)
304 hdr->dest_domain = APR_DOMAIN_MODEM;
305 else if (dest_id == APR_DEST_QDSP6)
306 hdr->dest_domain = APR_DOMAIN_ADSP;
307
308 hdr->dest_svc = svc->id;
309
310 w_len = apr_tal_write(clnt->handle, buf, hdr->pkt_size);
311 if (w_len != hdr->pkt_size)
312 pr_err("Unable to write APR pkt successfully: %d\n", w_len);
313 spin_unlock_irqrestore(&svc->w_lock, flags);
314
315 return w_len;
316}
317
Joonwoo Park91d95462012-08-02 10:55:54 -0700318void apr_cb_func(void *buf, int len, void *priv)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700319{
320 struct apr_client_data data;
321 struct apr_client *apr_client;
322 struct apr_svc *c_svc;
323 struct apr_hdr *hdr;
324 uint16_t hdr_size;
325 uint16_t msg_type;
326 uint16_t ver;
327 uint16_t src;
328 uint16_t svc;
329 uint16_t clnt;
330 int i;
331 int temp_port = 0;
332 uint32_t *ptr;
333
334 pr_debug("APR2: len = %d\n", len);
335 ptr = buf;
336 pr_debug("\n*****************\n");
337 for (i = 0; i < len/4; i++)
338 pr_debug("%x ", ptr[i]);
339 pr_debug("\n");
340 pr_debug("\n*****************\n");
341
342 if (!buf || len <= APR_HDR_SIZE) {
Joonwoo Park91d95462012-08-02 10:55:54 -0700343 pr_err("APR: Improper apr pkt received:%p %d\n", buf, len);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700344 return;
345 }
346 hdr = buf;
347
348 ver = hdr->hdr_field;
349 ver = (ver & 0x000F);
350 if (ver > APR_PKT_VER + 1) {
351 pr_err("APR: Wrong version: %d\n", ver);
352 return;
353 }
354
355 hdr_size = hdr->hdr_field;
356 hdr_size = ((hdr_size & 0x00F0) >> 0x4) * 4;
357 if (hdr_size < APR_HDR_SIZE) {
358 pr_err("APR: Wrong hdr size:%d\n", hdr_size);
359 return;
360 }
361
362 if (hdr->pkt_size < APR_HDR_SIZE) {
363 pr_err("APR: Wrong paket size\n");
364 return;
365 }
366 msg_type = hdr->hdr_field;
367 msg_type = (msg_type >> 0x08) & 0x0003;
Joonwoo Park91d95462012-08-02 10:55:54 -0700368 if (msg_type >= APR_MSG_TYPE_MAX && msg_type != APR_BASIC_RSP_RESULT) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700369 pr_err("APR: Wrong message type: %d\n", msg_type);
370 return;
371 }
372
373 if (hdr->src_domain >= APR_DOMAIN_MAX ||
374 hdr->dest_domain >= APR_DOMAIN_MAX ||
375 hdr->src_svc >= APR_SVC_MAX ||
376 hdr->dest_svc >= APR_SVC_MAX) {
377 pr_err("APR: Wrong APR header\n");
378 return;
379 }
380
381 svc = hdr->dest_svc;
382 if (hdr->src_domain == APR_DOMAIN_MODEM) {
383 src = APR_DEST_MODEM;
384 if (svc == APR_SVC_MVS || svc == APR_SVC_MVM ||
Joonwoo Park91d95462012-08-02 10:55:54 -0700385 svc == APR_SVC_CVS || svc == APR_SVC_CVP ||
386 svc == APR_SVC_TEST_CLIENT)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700387 clnt = APR_CLIENT_VOICE;
388 else {
389 pr_err("APR: Wrong svc :%d\n", svc);
390 return;
391 }
392 } else if (hdr->src_domain == APR_DOMAIN_ADSP) {
393 src = APR_DEST_QDSP6;
394 if (svc == APR_SVC_AFE || svc == APR_SVC_ASM ||
Joonwoo Park91d95462012-08-02 10:55:54 -0700395 svc == APR_SVC_VSM || svc == APR_SVC_VPM ||
396 svc == APR_SVC_ADM || svc == APR_SVC_ADSP_CORE ||
397 svc == APR_SVC_USM ||
398 svc == APR_SVC_TEST_CLIENT || svc == APR_SVC_ADSP_MVM ||
Joonwoo Parkb91e48e2013-02-21 10:24:56 -0800399 svc == APR_SVC_ADSP_CVS || svc == APR_SVC_ADSP_CVP ||
400 svc == APR_SVC_LSM)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700401 clnt = APR_CLIENT_AUDIO;
Arun Menonc490f762012-10-02 15:57:08 -0700402 else if (svc == APR_SVC_VIDC)
403 clnt = APR_CLIENT_AUDIO;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700404 else {
405 pr_err("APR: Wrong svc :%d\n", svc);
406 return;
407 }
408 } else {
409 pr_err("APR: Pkt from wrong source: %d\n", hdr->src_domain);
410 return;
411 }
412
413 pr_debug("src =%d clnt = %d\n", src, clnt);
414 apr_client = &client[src][clnt];
415 for (i = 0; i < APR_SVC_MAX; i++)
416 if (apr_client->svc[i].id == svc) {
417 pr_debug("%d\n", apr_client->svc[i].id);
418 c_svc = &apr_client->svc[i];
419 break;
420 }
421
422 if (i == APR_SVC_MAX) {
423 pr_err("APR: service is not registered\n");
424 return;
425 }
426 pr_debug("svc_idx = %d\n", i);
427 pr_debug("%x %x %x %p %p\n", c_svc->id, c_svc->dest_id,
Joonwoo Park91d95462012-08-02 10:55:54 -0700428 c_svc->client_id, c_svc->fn, c_svc->priv);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700429 data.payload_size = hdr->pkt_size - hdr_size;
430 data.opcode = hdr->opcode;
431 data.src = src;
432 data.src_port = hdr->src_port;
433 data.dest_port = hdr->dest_port;
434 data.token = hdr->token;
435 data.msg_type = msg_type;
436 if (data.payload_size > 0)
437 data.payload = (char *)hdr + hdr_size;
438
439 temp_port = ((data.src_port >> 8) * 8) + (data.src_port & 0xFF);
440 pr_debug("port = %d t_port = %d\n", data.src_port, temp_port);
441 if (c_svc->port_cnt && c_svc->port_fn[temp_port])
442 c_svc->port_fn[temp_port](&data, c_svc->port_priv[temp_port]);
443 else if (c_svc->fn)
444 c_svc->fn(&data, c_svc->priv);
445 else
446 pr_err("APR: Rxed a packet for NULL callback\n");
447}
448
Joonwoo Park91d95462012-08-02 10:55:54 -0700449int apr_get_svc(const char *svc_name, int dest_id, int *client_id,
450 int *svc_idx, int *svc_id)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700451{
Joonwoo Park91d95462012-08-02 10:55:54 -0700452 int i;
453 int size;
454 struct apr_svc_table *tbl;
455 int ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700456
Joonwoo Park91d95462012-08-02 10:55:54 -0700457 if (dest_id == APR_DEST_QDSP6) {
Arun Menonc490f762012-10-02 15:57:08 -0700458 tbl = (struct apr_svc_table *)&svc_tbl_qdsp6;
459 size = ARRAY_SIZE(svc_tbl_qdsp6);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700460 } else {
Joonwoo Park91d95462012-08-02 10:55:54 -0700461 tbl = (struct apr_svc_table *)&svc_tbl_voice;
462 size = ARRAY_SIZE(svc_tbl_voice);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700463 }
464
Joonwoo Park91d95462012-08-02 10:55:54 -0700465 for (i = 0; i < size; i++) {
466 if (!strncmp(svc_name, tbl[i].name, strlen(tbl[i].name))) {
467 *client_id = tbl[i].client_id;
468 *svc_idx = tbl[i].idx;
469 *svc_id = tbl[i].id;
470 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700471 }
472 }
473
Joonwoo Park91d95462012-08-02 10:55:54 -0700474 pr_debug("%s: svc_name = %s c_id = %d dest_id = %d\n",
475 __func__, svc_name, *client_id, dest_id);
476 if (i == size) {
477 pr_err("%s: APR: Wrong svc name %s\n", __func__, svc_name);
478 ret = -EINVAL;
479 }
480
481 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700482}
483
484static void apr_reset_deregister(struct work_struct *work)
485{
486 struct apr_svc *handle = NULL;
487 struct apr_reset_work *apr_reset =
488 container_of(work, struct apr_reset_work, work);
489
490 handle = apr_reset->handle;
491 pr_debug("%s:handle[%p]\n", __func__, handle);
492 apr_deregister(handle);
493 kfree(apr_reset);
494}
495
496int apr_deregister(void *handle)
497{
498 struct apr_svc *svc = handle;
499 struct apr_client *clnt;
500 uint16_t dest_id;
501 uint16_t client_id;
502
503 if (!handle)
504 return -EINVAL;
505
506 mutex_lock(&svc->m_lock);
507 dest_id = svc->dest_id;
508 client_id = svc->client_id;
509 clnt = &client[dest_id][client_id];
510
511 if (svc->port_cnt > 0 || svc->svc_cnt > 0) {
512 if (svc->port_cnt)
513 svc->port_cnt--;
514 else if (svc->svc_cnt)
515 svc->svc_cnt--;
516 if (!svc->port_cnt && !svc->svc_cnt) {
517 client[dest_id][client_id].svc_cnt--;
518 svc->need_reset = 0x0;
519 }
520 } else if (client[dest_id][client_id].svc_cnt > 0) {
521 client[dest_id][client_id].svc_cnt--;
522 if (!client[dest_id][client_id].svc_cnt) {
523 svc->need_reset = 0x0;
524 pr_debug("%s: service is reset %p\n", __func__, svc);
525 }
526 }
527
528 if (!svc->port_cnt && !svc->svc_cnt) {
529 svc->priv = NULL;
530 svc->id = 0;
531 svc->fn = NULL;
532 svc->dest_id = 0;
533 svc->client_id = 0;
534 svc->need_reset = 0x0;
535 }
536 if (client[dest_id][client_id].handle &&
Joonwoo Park91d95462012-08-02 10:55:54 -0700537 !client[dest_id][client_id].svc_cnt) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700538 apr_tal_close(client[dest_id][client_id].handle);
539 client[dest_id][client_id].handle = NULL;
540 }
541 mutex_unlock(&svc->m_lock);
542
543 return 0;
544}
545
546void apr_reset(void *handle)
547{
548 struct apr_reset_work *apr_reset_worker = NULL;
549
550 if (!handle)
551 return;
552 pr_debug("%s: handle[%p]\n", __func__, handle);
553
Shiv Maliyappanahalli1abc5152012-01-19 14:41:03 -0800554 if (apr_reset_workqueue == NULL) {
555 pr_err("%s: apr_reset_workqueue is NULL\n", __func__);
556 return;
557 }
558
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700559 apr_reset_worker = kzalloc(sizeof(struct apr_reset_work),
Shiv Maliyappanahalli1abc5152012-01-19 14:41:03 -0800560 GFP_ATOMIC);
561
562 if (apr_reset_worker == NULL) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700563 pr_err("%s: mem failure\n", __func__);
564 return;
565 }
Shiv Maliyappanahalli1abc5152012-01-19 14:41:03 -0800566
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700567 apr_reset_worker->handle = handle;
568 INIT_WORK(&apr_reset_worker->work, apr_reset_deregister);
569 queue_work(apr_reset_workqueue, &apr_reset_worker->work);
570}
571
Joonwoo Park91d95462012-08-02 10:55:54 -0700572static int adsp_state(int state)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700573{
574 pr_info("dsp state = %d\n", state);
575 return 0;
576}
577
578/* Dispatch the Reset events to Modem and audio clients */
579void dispatch_event(unsigned long code, unsigned short proc)
580{
581 struct apr_client *apr_client;
582 struct apr_client_data data;
583 struct apr_svc *svc;
584 uint16_t clnt;
585 int i, j;
586
587 data.opcode = RESET_EVENTS;
588 data.reset_event = code;
589 data.reset_proc = proc;
590
591 clnt = APR_CLIENT_AUDIO;
592 apr_client = &client[proc][clnt];
593 for (i = 0; i < APR_SVC_MAX; i++) {
594 mutex_lock(&apr_client->svc[i].m_lock);
595 if (apr_client->svc[i].fn) {
596 apr_client->svc[i].need_reset = 0x1;
597 apr_client->svc[i].fn(&data, apr_client->svc[i].priv);
598 }
599 if (apr_client->svc[i].port_cnt) {
600 svc = &(apr_client->svc[i]);
601 svc->need_reset = 0x1;
602 for (j = 0; j < APR_MAX_PORTS; j++)
603 if (svc->port_fn[j])
604 svc->port_fn[j](&data,
605 svc->port_priv[j]);
606 }
607 mutex_unlock(&apr_client->svc[i].m_lock);
608 }
609
610 clnt = APR_CLIENT_VOICE;
611 apr_client = &client[proc][clnt];
612 for (i = 0; i < APR_SVC_MAX; i++) {
613 mutex_lock(&apr_client->svc[i].m_lock);
614 if (apr_client->svc[i].fn) {
615 apr_client->svc[i].need_reset = 0x1;
616 apr_client->svc[i].fn(&data, apr_client->svc[i].priv);
617 }
618 if (apr_client->svc[i].port_cnt) {
619 svc = &(apr_client->svc[i]);
620 svc->need_reset = 0x1;
621 for (j = 0; j < APR_MAX_PORTS; j++)
622 if (svc->port_fn[j])
623 svc->port_fn[j](&data,
624 svc->port_priv[j]);
625 }
626 mutex_unlock(&apr_client->svc[i].m_lock);
627 }
628}
629
630static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
Joonwoo Park91d95462012-08-02 10:55:54 -0700631 void *_cmd)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700632{
633 switch (code) {
634 case SUBSYS_BEFORE_SHUTDOWN:
635 pr_debug("M-Notify: Shutdown started\n");
Joonwoo Park91d95462012-08-02 10:55:54 -0700636 apr_set_modem_state(APR_SUBSYS_DOWN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700637 dispatch_event(code, APR_DEST_MODEM);
638 break;
639 case SUBSYS_AFTER_SHUTDOWN:
640 pr_debug("M-Notify: Shutdown Completed\n");
641 break;
642 case SUBSYS_BEFORE_POWERUP:
643 pr_debug("M-notify: Bootup started\n");
644 break;
645 case SUBSYS_AFTER_POWERUP:
Joonwoo Park91d95462012-08-02 10:55:54 -0700646 if (apr_cmpxchg_modem_state(APR_SUBSYS_DOWN, APR_SUBSYS_UP) ==
647 APR_SUBSYS_DOWN)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700648 wake_up(&modem_wait);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700649 pr_debug("M-Notify: Bootup Completed\n");
650 break;
651 default:
652 pr_err("M-Notify: General: %lu\n", code);
653 break;
654 }
655 return NOTIFY_DONE;
656}
657
658static struct notifier_block mnb = {
659 .notifier_call = modem_notifier_cb,
660};
661
662static int lpass_notifier_cb(struct notifier_block *this, unsigned long code,
Joonwoo Park91d95462012-08-02 10:55:54 -0700663 void *_cmd)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700664{
665 switch (code) {
666 case SUBSYS_BEFORE_SHUTDOWN:
667 pr_debug("L-Notify: Shutdown started\n");
Joonwoo Park91d95462012-08-02 10:55:54 -0700668 apr_set_q6_state(APR_SUBSYS_DOWN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700669 dispatch_event(code, APR_DEST_QDSP6);
670 break;
671 case SUBSYS_AFTER_SHUTDOWN:
672 pr_debug("L-Notify: Shutdown Completed\n");
673 break;
674 case SUBSYS_BEFORE_POWERUP:
675 pr_debug("L-notify: Bootup started\n");
676 break;
677 case SUBSYS_AFTER_POWERUP:
Ravishankar Sarawadi14dfeac2012-09-25 19:28:57 -0700678 if (apr_cmpxchg_q6_state(APR_SUBSYS_DOWN,
679 APR_SUBSYS_LOADED) == APR_SUBSYS_DOWN)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700680 wake_up(&dsp_wait);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700681 pr_debug("L-Notify: Bootup Completed\n");
682 break;
683 default:
684 pr_err("L-Notify: Generel: %lu\n", code);
685 break;
686 }
687 return NOTIFY_DONE;
688}
689
690static struct notifier_block lnb = {
691 .notifier_call = lpass_notifier_cb,
692};
693
694
695static int __init apr_init(void)
696{
697 int i, j, k;
698
699 for (i = 0; i < APR_DEST_MAX; i++)
700 for (j = 0; j < APR_CLIENT_MAX; j++) {
701 mutex_init(&client[i][j].m_lock);
702 for (k = 0; k < APR_SVC_MAX; k++) {
703 mutex_init(&client[i][j].svc[k].m_lock);
704 spin_lock_init(&client[i][j].svc[k].w_lock);
705 }
706 }
Joonwoo Parkddc46df2012-08-23 11:25:26 -0700707 apr_set_subsys_state();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700708 mutex_init(&q6.lock);
709 dsp_debug_register(adsp_state);
Joonwoo Park91d95462012-08-02 10:55:54 -0700710 apr_reset_workqueue = create_singlethread_workqueue("apr_driver");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700711 if (!apr_reset_workqueue)
712 return -ENOMEM;
713 return 0;
714}
715device_initcall(apr_init);
716
717static int __init apr_late_init(void)
718{
Bharath Ramachandramurthy94ad7e22012-02-28 18:44:07 -0800719 int ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700720 init_waitqueue_head(&dsp_wait);
721 init_waitqueue_head(&modem_wait);
Bharath Ramachandramurthy94ad7e22012-02-28 18:44:07 -0800722 subsys_notif_register_notifier("modem", &mnb);
Ravishankar Sarawadi14dfeac2012-09-25 19:28:57 -0700723 subsys_notif_register_notifier(apr_get_lpass_subsys_name(), &lnb);
Bharath Ramachandramurthy94ad7e22012-02-28 18:44:07 -0800724 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700725}
726late_initcall(apr_late_init);