blob: 378432b66c07c76620757fac3005c4cd1dcb0c84 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* arch/arm/mach-msm/qdsp6/dal.c
2 *
3 * Copyright (C) 2009 Google, Inc.
4 * Author: Brian Swetland <swetland@google.com>
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17#include <linux/slab.h>
18#include <linux/kernel.h>
19#include <linux/spinlock.h>
20#include <linux/mutex.h>
21#include <linux/list.h>
22#include <linux/sched.h>
23#include <linux/wait.h>
24#include <linux/errno.h>
25
26#include <linux/delay.h>
27
28#include <mach/msm_smd.h>
29#include <mach/debug_mm.h>
30#include <mach/msm_qdsp6_audio.h>
31
32#include "dal.h"
33
34#define DAL_TRACE 0
35
36struct dal_hdr {
37 uint32_t length:16; /* message length (header inclusive) */
38 uint32_t version:8; /* DAL protocol version */
39 uint32_t priority:7;
40 uint32_t async:1;
41 uint32_t ddi:16; /* DDI method number */
42 uint32_t prototype:8; /* DDI serialization format */
43 uint32_t msgid:8; /* message id (DDI, ATTACH, DETACH, ...) */
44 void *from;
45 void *to;
46} __attribute__((packed));
47
48#define TRACE_DATA_MAX 128
49#define TRACE_LOG_MAX 32
50#define TRACE_LOG_MASK (TRACE_LOG_MAX - 1)
51
52struct dal_trace {
53 unsigned timestamp;
54 struct dal_hdr hdr;
55 uint32_t data[TRACE_DATA_MAX];
56};
57
58#define DAL_HDR_SIZE (sizeof(struct dal_hdr))
59#define DAL_DATA_MAX 512
60#define DAL_MSG_MAX (DAL_HDR_SIZE + DAL_DATA_MAX)
61
62#define DAL_VERSION 0x11
63
64#define DAL_MSGID_DDI 0x00
65#define DAL_MSGID_ATTACH 0x01
66#define DAL_MSGID_DETACH 0x02
67#define DAL_MSGID_ASYNCH 0xC0
68#define DAL_MSGID_REPLY 0x80
69
70struct dal_channel {
71 struct list_head list;
72 struct list_head clients;
73
74 /* synchronization for changing channel state,
75 * adding/removing clients, smd callbacks, etc
76 */
77 spinlock_t lock;
78
79 struct smd_channel *sch;
80 char *name;
81
82 /* events are delivered at IRQ context immediately, so
83 * we only need one assembly buffer for the entire channel
84 */
85 struct dal_hdr hdr;
86 unsigned char data[DAL_DATA_MAX];
87
88 unsigned count;
89 void *ptr;
90
91 /* client which the current inbound message is for */
92 struct dal_client *active;
93};
94
95struct dal_client {
96 struct list_head list;
97 struct dal_channel *dch;
98 void *cookie;
99 dal_event_func_t event;
100
101 /* opaque handle for the far side */
102 void *remote;
103
104 /* dal rpc calls are fully synchronous -- only one call may be
105 * active per client at a time
106 */
107 struct mutex write_lock;
108 wait_queue_head_t wait;
109
110 unsigned char data[DAL_DATA_MAX];
111
112 void *reply;
113 int reply_max;
114 int status;
115 unsigned msgid; /* msgid of expected reply */
116
117 spinlock_t tr_lock;
118 unsigned tr_head;
119 unsigned tr_tail;
120 struct dal_trace *tr_log;
121};
122
123static unsigned now(void)
124{
125 struct timespec ts;
126 ktime_get_ts(&ts);
127 return (ts.tv_nsec / 1000000) + (ts.tv_sec * 1000);
128}
129
130void dal_trace(struct dal_client *c)
131{
132 if (c->tr_log)
133 return;
134 c->tr_log = kzalloc(sizeof(struct dal_trace) * TRACE_LOG_MAX,
135 GFP_KERNEL);
136}
137
138void dal_trace_print(struct dal_hdr *hdr, unsigned *data, int len, unsigned when)
139{
140 int i;
141 printk("DAL %08x -> %08x L=%03x A=%d D=%04x P=%02x M=%02x T=%d",
142 (unsigned) hdr->from, (unsigned) hdr->to,
143 hdr->length, hdr->async,
144 hdr->ddi, hdr->prototype, hdr->msgid,
145 when);
146 len /= 4;
147 for (i = 0; i < len; i++) {
148 if (!(i & 7))
149 printk("\n%03x", i * 4);
150 printk(" %08x", data[i]);
151 }
152 printk("\n");
153}
154
155void dal_trace_dump(struct dal_client *c)
156{
157 struct dal_trace *dt;
158 unsigned n, len;
159
160 if (!c->tr_log)
161 return;
162
163 for (n = c->tr_tail; n != c->tr_head; n = (n + 1) & TRACE_LOG_MASK) {
164 dt = c->tr_log + n;
165 len = dt->hdr.length - sizeof(dt->hdr);
166 if (len > TRACE_DATA_MAX)
167 len = TRACE_DATA_MAX;
168 dal_trace_print(&dt->hdr, dt->data, len, dt->timestamp);
169 }
170}
171
172static void dal_trace_log(struct dal_client *c,
173 struct dal_hdr *hdr, void *data, unsigned len)
174{
175 unsigned long flags;
176 unsigned t, n;
177 struct dal_trace *dt;
178
179 t = now();
180 if (len > TRACE_DATA_MAX)
181 len = TRACE_DATA_MAX;
182
183 spin_lock_irqsave(&c->tr_lock, flags);
184 n = (c->tr_head + 1) & TRACE_LOG_MASK;
185 if (c->tr_tail == n)
186 c->tr_tail = (c->tr_tail + 1) & TRACE_LOG_MASK;
187 dt = c->tr_log + n;
188 dt->timestamp = t;
189 memcpy(&dt->hdr, hdr, sizeof(struct dal_hdr));
190 memcpy(dt->data, data, len);
191 c->tr_head = n;
192
193 spin_unlock_irqrestore(&c->tr_lock, flags);
194}
195
196
197static void dal_channel_notify(void *priv, unsigned event)
198{
199 struct dal_channel *dch = priv;
200 struct dal_hdr *hdr = &dch->hdr;
201 struct dal_client *client;
202 unsigned long flags;
203 int len;
204 int r;
205
206 spin_lock_irqsave(&dch->lock, flags);
207
208again:
209 if (dch->count == 0) {
210 if (smd_read_avail(dch->sch) < DAL_HDR_SIZE)
211 goto done;
212
213 smd_read(dch->sch, hdr, DAL_HDR_SIZE);
214
215 if (hdr->length < DAL_HDR_SIZE)
216 goto done;
217
218 if (hdr->length > DAL_MSG_MAX)
219 panic("oversize message");
220
221 dch->count = hdr->length - DAL_HDR_SIZE;
222
223 /* locate the client this message is targeted to */
224 list_for_each_entry(client, &dch->clients, list) {
225 if (dch->hdr.to == client) {
226 dch->active = client;
227 dch->ptr = client->data;
228 goto check_data;
229 }
230 }
231 pr_err("[%s:%s] $$$ receiving unknown message len = %d $$$\n",
232 __MM_FILE__, __func__, dch->count);
233 dch->active = 0;
234 dch->ptr = dch->data;
235 }
236
237check_data:
238 len = dch->count;
239 if (len > 0) {
240 if (smd_read_avail(dch->sch) < len)
241 goto done;
242
243 r = smd_read(dch->sch, dch->ptr, len);
244 if (r != len)
245 panic("invalid read");
246
247#if DAL_TRACE
248 pr_info("[%s:%s] dal recv %p <- %p %02x:%04x:%02x %d\n",
249 __MM_FILE__, __func__, hdr->to, hdr->from, hdr->msgid,
250 hdr->ddi, hdr->prototype, hdr->length - sizeof(*hdr));
251 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, dch->ptr, len);
252#endif
253 dch->count = 0;
254
255 client = dch->active;
256 if (!client) {
257 pr_err("[%s:%s] message to %p discarded\n",
258 __MM_FILE__, __func__, dch->hdr.to);
259 goto again;
260 }
261
262 if (client->tr_log)
263 dal_trace_log(client, hdr, dch->ptr, len);
264
265 if (hdr->msgid == DAL_MSGID_ASYNCH) {
266 if (client->event)
267 client->event(dch->ptr, len, client->cookie);
268 else
269 pr_err("[%s:%s] client %p has no event \
270 handler\n", __MM_FILE__, __func__,
271 client);
272 goto again;
273 }
274
275 if (hdr->msgid == client->msgid) {
276 if (!client->remote)
277 client->remote = hdr->from;
278 if (len > client->reply_max)
279 len = client->reply_max;
280 memcpy(client->reply, client->data, len);
281 client->status = len;
282 wake_up(&client->wait);
283 goto again;
284 }
285
286 pr_err("[%s:%s] cannot find client %p\n", __MM_FILE__,
287 __func__, dch->hdr.to);
288 goto again;
289 }
290
291done:
292 spin_unlock_irqrestore(&dch->lock, flags);
293}
294
295static LIST_HEAD(dal_channel_list);
296static DEFINE_MUTEX(dal_channel_list_lock);
297
298static struct dal_channel *dal_open_channel(const char *name, uint32_t cpu)
299{
300 struct dal_channel *dch;
301
302 pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
303 mutex_lock(&dal_channel_list_lock);
304
305 list_for_each_entry(dch, &dal_channel_list, list) {
306 if (!strcmp(dch->name, name))
307 goto found_it;
308 }
309
310 dch = kzalloc(sizeof(*dch) + strlen(name) + 1, GFP_KERNEL);
311 if (!dch)
312 goto fail;
313
314 dch->name = (char *) (dch + 1);
315 strcpy(dch->name, name);
316 spin_lock_init(&dch->lock);
317 INIT_LIST_HEAD(&dch->clients);
318
319 list_add(&dch->list, &dal_channel_list);
320
321found_it:
322 if (!dch->sch) {
323 if (smd_named_open_on_edge(name, cpu, &dch->sch,
324 dch, dal_channel_notify)) {
325 pr_err("[%s:%s] smd open failed\n", __MM_FILE__,
326 __func__);
327 dch = NULL;
328 }
329 /* FIXME: wait for channel to open before returning */
330 msleep(100);
331 }
332
333fail:
334 mutex_unlock(&dal_channel_list_lock);
335
336 return dch;
337}
338
339int dal_call_raw(struct dal_client *client,
340 struct dal_hdr *hdr,
341 void *data, int data_len,
342 void *reply, int reply_max)
343{
344 struct dal_channel *dch = client->dch;
345 unsigned long flags;
346
347 client->reply = reply;
348 client->reply_max = reply_max;
349 client->msgid = hdr->msgid | DAL_MSGID_REPLY;
350 client->status = -EBUSY;
351
352#if DAL_TRACE
353 pr_info("[%s:%s:%x] dal send %p -> %p %02x:%04x:%02x %d\n",
354 __MM_FILE__, __func__, (unsigned int)client, hdr->from, hdr->to,
355 hdr->msgid, hdr->ddi, hdr->prototype,
356 hdr->length - sizeof(*hdr));
357 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data, data_len);
358#endif
359
360 if (client->tr_log)
361 dal_trace_log(client, hdr, data, data_len);
362
363 spin_lock_irqsave(&dch->lock, flags);
364 /* FIXME: ensure entire message is written or none. */
365 smd_write(dch->sch, hdr, sizeof(*hdr));
366 smd_write(dch->sch, data, data_len);
367 spin_unlock_irqrestore(&dch->lock, flags);
368
369 if (!wait_event_timeout(client->wait, (client->status != -EBUSY), 5*HZ)) {
370 dal_trace_dump(client);
371 pr_err("[%s:%s] call timed out. dsp is probably dead.\n",
372 __MM_FILE__, __func__);
373 dal_trace_print(hdr, data, data_len, 0);
374 q6audio_dsp_not_responding();
375 }
376
377 return client->status;
378}
379
380int dal_call(struct dal_client *client,
381 unsigned ddi, unsigned prototype,
382 void *data, int data_len,
383 void *reply, int reply_max)
384{
385 struct dal_hdr hdr;
386 int r;
387
388 memset(&hdr, 0, sizeof(hdr));
389
390 hdr.length = data_len + sizeof(hdr);
391 hdr.version = DAL_VERSION;
392 hdr.msgid = DAL_MSGID_DDI;
393 hdr.ddi = ddi;
394 hdr.prototype = prototype;
395 hdr.from = client;
396 hdr.to = client->remote;
397
398 if (hdr.length > DAL_MSG_MAX)
399 return -EINVAL;
400
401 mutex_lock(&client->write_lock);
402 r = dal_call_raw(client, &hdr, data, data_len, reply, reply_max);
403 mutex_unlock(&client->write_lock);
404
405 return r;
406}
407
408struct dal_msg_attach {
409 uint32_t device_id;
410 char attach[64];
411 char service_name[32];
412} __attribute__((packed));
413
414struct dal_reply_attach {
415 uint32_t status;
416 char name[64];
417};
418
419struct dal_client *dal_attach(uint32_t device_id, const char *name,
420 uint32_t cpu, dal_event_func_t func, void *cookie)
421{
422 struct dal_hdr hdr;
423 struct dal_msg_attach msg;
424 struct dal_reply_attach reply;
425 struct dal_channel *dch;
426 struct dal_client *client;
427 unsigned long flags;
428 int r;
429
430 pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
431 dch = dal_open_channel(name, cpu);
432 if (!dch)
433 return 0;
434
435 client = kzalloc(sizeof(*client), GFP_KERNEL);
436 if (!client)
437 return 0;
438
439 client->dch = dch;
440 client->event = func;
441 client->cookie = cookie;
442 mutex_init(&client->write_lock);
443 spin_lock_init(&client->tr_lock);
444 init_waitqueue_head(&client->wait);
445
446 spin_lock_irqsave(&dch->lock, flags);
447 list_add(&client->list, &dch->clients);
448 spin_unlock_irqrestore(&dch->lock, flags);
449
450 memset(&hdr, 0, sizeof(hdr));
451 memset(&msg, 0, sizeof(msg));
452
453 hdr.length = sizeof(hdr) + sizeof(msg);
454 hdr.version = DAL_VERSION;
455 hdr.msgid = DAL_MSGID_ATTACH;
456 hdr.from = client;
457 msg.device_id = device_id;
458
459 r = dal_call_raw(client, &hdr, &msg, sizeof(msg),
460 &reply, sizeof(reply));
461
462 if ((r == sizeof(reply)) && (reply.status == 0)) {
463 reply.name[63] = 0;
464 pr_info("[%s:%s] status = %d, name = '%s' dal_client %x\n",
465 __MM_FILE__, __func__, reply.status,
466 reply.name, (unsigned int)client);
467 return client;
468 }
469
470 pr_err("[%s:%s] failure\n", __MM_FILE__, __func__);
471
472 dal_detach(client);
473 return 0;
474}
475
476int dal_detach(struct dal_client *client)
477{
478 struct dal_channel *dch;
479 unsigned long flags;
480
481 pr_debug("[%s:%s]\n", __MM_FILE__, __func__);
482 mutex_lock(&client->write_lock);
483 if (client->remote) {
484 struct dal_hdr hdr;
485 uint32_t data;
486
487 memset(&hdr, 0, sizeof(hdr));
488 hdr.length = sizeof(hdr) + sizeof(data);
489 hdr.version = DAL_VERSION;
490 hdr.msgid = DAL_MSGID_DETACH;
491 hdr.from = client;
492 hdr.to = client->remote;
493 data = (uint32_t) client;
494
495 dal_call_raw(client, &hdr, &data, sizeof(data),
496 &data, sizeof(data));
497 }
498
499 dch = client->dch;
500 spin_lock_irqsave(&dch->lock, flags);
501 if (dch->active == client) {
502 /* We have received a message header for this client
503 * but not the body of the message. Ensure that when
504 * the body arrives we don't write it into the now-closed
505 * client. In *theory* this should never happen.
506 */
507 dch->active = 0;
508 dch->ptr = dch->data;
509 }
510 list_del(&client->list);
511 spin_unlock_irqrestore(&dch->lock, flags);
512
513 mutex_unlock(&client->write_lock);
514
515 kfree(client);
516 return 0;
517}
518
519void *dal_get_remote_handle(struct dal_client *client)
520{
521 return client->remote;
522}
523
524/* convenience wrappers */
525
526int dal_call_f0(struct dal_client *client, uint32_t ddi, uint32_t arg1)
527{
528 uint32_t tmp = arg1;
529 int res;
530 res = dal_call(client, ddi, 0, &tmp, sizeof(tmp), &tmp, sizeof(tmp));
531 if (res >= 4)
532 return (int) tmp;
533 return res;
534}
535
536int dal_call_f1(struct dal_client *client, uint32_t ddi, uint32_t arg1,
537 uint32_t arg2)
538{
539 uint32_t tmp[2];
540 int res;
541 tmp[0] = arg1;
542 tmp[1] = arg2;
543 res = dal_call(client, ddi, 1, tmp, sizeof(tmp), tmp, sizeof(uint32_t));
544 if (res >= 4)
545 return (int) tmp[0];
546 return res;
547}
548
549int dal_call_f5(struct dal_client *client, uint32_t ddi, void *ibuf, uint32_t ilen)
550{
551 uint32_t tmp[128];
552 int res;
553 int param_idx = 0;
554
555 if (ilen + 4 > DAL_DATA_MAX)
556 return -EINVAL;
557
558 tmp[param_idx] = ilen;
559 param_idx++;
560
561 memcpy(&tmp[param_idx], ibuf, ilen);
562 param_idx += DIV_ROUND_UP(ilen, 4);
563
564 res = dal_call(client, ddi, 5, tmp, param_idx * 4, tmp, sizeof(tmp));
565
566 if (res >= 4)
567 return (int) tmp[0];
568 return res;
569}
570
571int dal_call_f6(struct dal_client *client, uint32_t ddi, uint32_t s1,
572 void *ibuf, uint32_t ilen)
573{
574 uint32_t tmp[128];
575 int res;
576 int param_idx = 0;
577
578 if (ilen + 8 > DAL_DATA_MAX)
579 return -EINVAL;
580
581 tmp[param_idx] = s1;
582 param_idx++;
583 tmp[param_idx] = ilen;
584 param_idx++;
585 memcpy(&tmp[param_idx], ibuf, ilen);
586 param_idx += DIV_ROUND_UP(ilen, 4);
587
588 res = dal_call(client, ddi, 6, tmp, param_idx * 4, tmp, sizeof(tmp));
589
590 if (res >= 4)
591 return (int) tmp[0];
592
593 return res;
594}
595
596int dal_call_f9(struct dal_client *client, uint32_t ddi, void *obuf,
597 uint32_t olen)
598{
599 uint32_t tmp[128];
600 int res;
601
602 if (olen > sizeof(tmp) - 8)
603 return -EINVAL;
604 tmp[0] = olen;
605
606 res = dal_call(client, ddi, 9, tmp, sizeof(uint32_t), tmp,
607 sizeof(tmp));
608
609 if (res >= 4)
610 res = (int)tmp[0];
611
612 if (!res) {
613 if (tmp[1] > olen)
614 return -EIO;
615 memcpy(obuf, &tmp[2], tmp[1]);
616 }
617 return res;
618}
619
620int dal_call_f11(struct dal_client *client, uint32_t ddi, uint32_t s1,
621 void *obuf, uint32_t olen)
622{
623 uint32_t tmp[DAL_DATA_MAX/4] = {0};
624 int res;
625 int param_idx = 0;
626 int num_bytes = 4;
627
628 num_bytes += (DIV_ROUND_UP(olen, 4)) * 4;
629
630 if ((num_bytes > DAL_DATA_MAX - 12) || (olen > DAL_DATA_MAX - 8))
631 return -EINVAL;
632
633 tmp[param_idx] = s1;
634 param_idx++;
635 tmp[param_idx] = olen;
636 param_idx += DIV_ROUND_UP(olen, 4);
637
638 res = dal_call(client, ddi, 11, tmp, param_idx * 4, tmp, sizeof(tmp));
639
640 if (res >= 4)
641 res = (int) tmp[0];
642 if (!res) {
643 if (tmp[1] > olen)
644 return -EIO;
645 memcpy(obuf, &tmp[2], tmp[1]);
646 }
647 return res;
648}
649
650int dal_call_f13(struct dal_client *client, uint32_t ddi, void *ibuf1,
651 uint32_t ilen1, void *ibuf2, uint32_t ilen2, void *obuf,
652 uint32_t olen)
653{
654 uint32_t tmp[DAL_DATA_MAX/4];
655 int res;
656 int param_idx = 0;
657 int num_bytes = 0;
658
659 num_bytes = (DIV_ROUND_UP(ilen1, 4)) * 4;
660 num_bytes += (DIV_ROUND_UP(ilen2, 4)) * 4;
661
662 if ((num_bytes > DAL_DATA_MAX - 12) || (olen > DAL_DATA_MAX - 8) ||
663 (ilen1 > DAL_DATA_MAX) || (ilen2 > DAL_DATA_MAX))
664 return -EINVAL;
665
666 tmp[param_idx] = ilen1;
667 param_idx++;
668
669 memcpy(&tmp[param_idx], ibuf1, ilen1);
670 param_idx += DIV_ROUND_UP(ilen1, 4);
671
672 tmp[param_idx++] = ilen2;
673 memcpy(&tmp[param_idx], ibuf2, ilen2);
674 param_idx += DIV_ROUND_UP(ilen2, 4);
675
676 tmp[param_idx++] = olen;
677 res = dal_call(client, ddi, 13, tmp, param_idx * 4, tmp,
678 sizeof(tmp));
679
680 if (res >= 4)
681 res = (int)tmp[0];
682
683 if (!res) {
684 if (tmp[1] > olen)
685 return -EIO;
686 memcpy(obuf, &tmp[2], tmp[1]);
687 }
688 return res;
689}
690int dal_call_f14(struct dal_client *client, uint32_t ddi, void *ibuf,
691 uint32_t ilen, void *obuf1, uint32_t olen1, void *obuf2,
692 uint32_t olen2, uint32_t *oalen2)
693{
694 uint32_t tmp[128];
695 int res;
696 int param_idx = 0;
697
698 if (olen1 + olen2 + 8 > DAL_DATA_MAX ||
699 ilen + 12 > DAL_DATA_MAX)
700 return -EINVAL;
701
702 tmp[param_idx] = ilen;
703 param_idx++;
704
705 memcpy(&tmp[param_idx], ibuf, ilen);
706 param_idx += DIV_ROUND_UP(ilen, 4);
707
708 tmp[param_idx++] = olen1;
709 tmp[param_idx++] = olen2;
710 res = dal_call(client, ddi, 14, tmp, param_idx * 4, tmp, sizeof(tmp));
711
712 if (res >= 4)
713 res = (int)tmp[0];
714
715 if (!res) {
716 if (tmp[1] > olen1)
717 return -EIO;
718 param_idx = DIV_ROUND_UP(tmp[1], 4) + 2;
719 if (tmp[param_idx] > olen2)
720 return -EIO;
721
722 memcpy(obuf1, &tmp[2], tmp[1]);
723 memcpy(obuf2, &tmp[param_idx+1], tmp[param_idx]);
724 *oalen2 = tmp[param_idx];
725 }
726 return res;
727}