blob: 25b9b76d7cf9989ca05b3d3a0db651b69e6af340 [file] [log] [blame]
Duy Truonge833aca2013-02-12 13:35:08 -08001/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
Bharath Ramachandramurthy2e3168f2012-05-03 16:29:09 -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/slab.h>
14#include <linux/wait.h>
15#include <linux/sched.h>
16#include <linux/jiffies.h>
17#include <linux/uaccess.h>
18#include <linux/atomic.h>
Joonwoo Park6572ac52012-07-10 17:17:00 -070019#include <linux/wait.h>
Bharath Ramachandramurthy2e3168f2012-05-03 16:29:09 -070020
21#include <mach/qdsp6v2/audio_acdb.h>
22#include <mach/qdsp6v2/rtac.h>
23
24#include <sound/apr_audio-v2.h>
25#include <mach/qdsp6v2/apr.h>
26#include <sound/q6adm-v2.h>
27#include <sound/q6audio-v2.h>
Joonwoo Park6572ac52012-07-10 17:17:00 -070028#include <sound/q6afe-v2.h>
Bharath Ramachandramurthy2e3168f2012-05-03 16:29:09 -070029
30#define TIMEOUT_MS 1000
31
32#define RESET_COPP_ID 99
33#define INVALID_COPP_ID 0xFF
34
35struct adm_ctl {
36 void *apr;
37 atomic_t copp_id[Q6_AFE_MAX_PORTS];
38 atomic_t copp_cnt[Q6_AFE_MAX_PORTS];
39 atomic_t copp_stat[Q6_AFE_MAX_PORTS];
40 u32 mem_map_handle[Q6_AFE_MAX_PORTS];
41 wait_queue_head_t wait[Q6_AFE_MAX_PORTS];
42};
43
44static struct adm_ctl this_adm;
45
46static int32_t adm_callback(struct apr_client_data *data, void *priv)
47{
48 uint32_t *payload;
49 int i, index;
50 payload = data->payload;
51
52 if (data->opcode == RESET_EVENTS) {
53 pr_debug("adm_callback: Reset event is received: %d %d apr[%p]\n",
54 data->reset_event, data->reset_proc,
55 this_adm.apr);
56 if (this_adm.apr) {
57 apr_reset(this_adm.apr);
58 for (i = 0; i < Q6_AFE_MAX_PORTS; i++) {
59 atomic_set(&this_adm.copp_id[i],
60 RESET_COPP_ID);
61 atomic_set(&this_adm.copp_cnt[i], 0);
62 atomic_set(&this_adm.copp_stat[i], 0);
63 }
64 this_adm.apr = NULL;
65 }
66 return 0;
67 }
68
69 pr_debug("%s: code = 0x%x PL#0[%x], PL#1[%x], size = %d\n", __func__,
70 data->opcode, payload[0], payload[1],
71 data->payload_size);
72
73 if (data->payload_size) {
74 index = q6audio_get_port_index(data->token);
75 if (index < 0 || index >= Q6_AFE_MAX_PORTS) {
76 pr_err("%s: invalid port idx %d token %d\n",
77 __func__, index, data->token);
78 return 0;
79 }
80 if (data->opcode == APR_BASIC_RSP_RESULT) {
Joonwoo Park6572ac52012-07-10 17:17:00 -070081 pr_debug("APR_BASIC_RSP_RESULT id %x\n", payload[0]);
Bharath Ramachandramurthy2e3168f2012-05-03 16:29:09 -070082 switch (payload[0]) {
83 case ADM_CMD_SET_PP_PARAMS_V5:
84 if (rtac_make_adm_callback(
85 payload, data->payload_size))
86 pr_debug("%s: payload[0]: 0x%x\n",
87 __func__, payload[0]);
88 break;
89 case ADM_CMD_DEVICE_CLOSE_V5:
90 case ADM_CMD_SHARED_MEM_UNMAP_REGIONS:
91 case ADM_CMD_SHARED_MEM_MAP_REGIONS:
92 case ADM_CMD_MATRIX_MAP_ROUTINGS_V5:
93 pr_debug("ADM_CMD_MATRIX_MAP_ROUTINGS\n");
94 atomic_set(&this_adm.copp_stat[index], 1);
95 wake_up(&this_adm.wait[index]);
96 break;
97 default:
98 pr_err("%s: Unknown Cmd: 0x%x\n", __func__,
99 payload[0]);
100 break;
101 }
102 return 0;
103 }
104
105 switch (data->opcode) {
106 case ADM_CMDRSP_DEVICE_OPEN_V5: {
107 struct adm_cmd_rsp_device_open_v5 *open =
108 (struct adm_cmd_rsp_device_open_v5 *)data->payload;
109 if (open->copp_id == INVALID_COPP_ID) {
110 pr_err("%s: invalid coppid rxed %d\n",
111 __func__, open->copp_id);
112 atomic_set(&this_adm.copp_stat[index], 1);
113 wake_up(&this_adm.wait[index]);
114 break;
115 }
116 atomic_set(&this_adm.copp_id[index], open->copp_id);
117 atomic_set(&this_adm.copp_stat[index], 1);
118 pr_debug("%s: coppid rxed=%d\n", __func__,
119 open->copp_id);
120 wake_up(&this_adm.wait[index]);
121 }
122 break;
123 case ADM_CMD_GET_PP_PARAMS_V5:
124 pr_debug("%s: ADM_CMD_GET_PP_PARAMS_V5\n", __func__);
125 rtac_make_adm_callback(payload,
126 data->payload_size);
127 break;
128 default:
129 pr_err("%s: Unknown cmd:0x%x\n", __func__,
130 data->opcode);
131 break;
132 }
133 }
134 return 0;
135}
136
137/* TODO: send_adm_cal_block function to be defined
138 when calibration available for 8974 */
139static void send_adm_cal(int port_id, int path)
140{
141 /* function to be defined when calibration available for 8974 */
142 pr_debug("%s\n", __func__);
143}
144
Joonwoo Park6572ac52012-07-10 17:17:00 -0700145int adm_connect_afe_port(int mode, int session_id, int port_id)
146{
147 struct adm_cmd_connect_afe_port_v5 cmd;
148 int ret = 0;
149 int index;
150
151 pr_debug("%s: port %d session id:%d mode:%d\n", __func__,
152 port_id, session_id, mode);
153
154 port_id = afe_convert_virtual_to_portid(port_id);
155
156 if (afe_validate_port(port_id) < 0) {
157 pr_err("%s port idi[%d] is invalid\n", __func__, port_id);
158 return -ENODEV;
159 }
160 if (this_adm.apr == NULL) {
161 this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
162 0xFFFFFFFF, &this_adm);
163 if (this_adm.apr == NULL) {
164 pr_err("%s: Unable to register ADM\n", __func__);
165 ret = -ENODEV;
166 return ret;
167 }
168 rtac_set_adm_handle(this_adm.apr);
169 }
170 index = afe_get_port_index(port_id);
171 pr_debug("%s: Port ID %d, index %d\n", __func__, port_id, index);
172
173 cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
174 APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
175 cmd.hdr.pkt_size = sizeof(cmd);
176 cmd.hdr.src_svc = APR_SVC_ADM;
177 cmd.hdr.src_domain = APR_DOMAIN_APPS;
178 cmd.hdr.src_port = port_id;
179 cmd.hdr.dest_svc = APR_SVC_ADM;
180 cmd.hdr.dest_domain = APR_DOMAIN_ADSP;
181 cmd.hdr.dest_port = port_id;
182 cmd.hdr.token = port_id;
183 cmd.hdr.opcode = ADM_CMD_CONNECT_AFE_PORT_V5;
184
185 cmd.mode = mode;
186 cmd.session_id = session_id;
187 cmd.afe_port_id = port_id;
188
189 atomic_set(&this_adm.copp_stat[index], 0);
190 ret = apr_send_pkt(this_adm.apr, (uint32_t *)&cmd);
191 if (ret < 0) {
192 pr_err("%s:ADM enable for port %d failed\n",
193 __func__, port_id);
194 ret = -EINVAL;
195 goto fail_cmd;
196 }
197 /* Wait for the callback with copp id */
198 ret = wait_event_timeout(this_adm.wait[index],
199 atomic_read(&this_adm.copp_stat[index]),
200 msecs_to_jiffies(TIMEOUT_MS));
201 if (!ret) {
202 pr_err("%s ADM connect AFE failed for port %d\n", __func__,
203 port_id);
204 ret = -EINVAL;
205 goto fail_cmd;
206 }
207 atomic_inc(&this_adm.copp_cnt[index]);
208 return 0;
209
210fail_cmd:
211
212 return ret;
213}
214
Bharath Ramachandramurthy2e3168f2012-05-03 16:29:09 -0700215int adm_open(int port_id, int path, int rate, int channel_mode, int topology)
216{
217 struct adm_cmd_device_open_v5 open;
218 int ret = 0;
219 int index;
220 int tmp_port = q6audio_get_port_id(port_id);
221
222 pr_debug("%s: port %d path:%d rate:%d mode:%d\n", __func__,
223 port_id, path, rate, channel_mode);
224
225 port_id = q6audio_convert_virtual_to_portid(port_id);
226
227 if (q6audio_validate_port(port_id) < 0) {
228 pr_err("%s port idi[%d] is invalid\n", __func__, port_id);
229 return -ENODEV;
230 }
231
232 index = q6audio_get_port_index(port_id);
233 pr_debug("%s: Port ID %d, index %d\n", __func__, port_id, index);
234
235 if (this_adm.apr == NULL) {
236 this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
237 0xFFFFFFFF, &this_adm);
238 if (this_adm.apr == NULL) {
239 pr_err("%s: Unable to register ADM\n", __func__);
240 ret = -ENODEV;
241 return ret;
242 }
243 rtac_set_adm_handle(this_adm.apr);
244 }
245
246
247 /* Create a COPP if port id are not enabled */
248 if (atomic_read(&this_adm.copp_cnt[index]) == 0) {
249
250 open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
251 APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
252 open.hdr.pkt_size = sizeof(open);
253 open.hdr.src_svc = APR_SVC_ADM;
254 open.hdr.src_domain = APR_DOMAIN_APPS;
255 open.hdr.src_port = tmp_port;
256 open.hdr.dest_svc = APR_SVC_ADM;
257 open.hdr.dest_domain = APR_DOMAIN_ADSP;
258 open.hdr.dest_port = tmp_port;
259 open.hdr.token = port_id;
260 open.hdr.opcode = ADM_CMD_DEVICE_OPEN_V5;
261
262 open.mode_of_operation = path;
263 /* Reserved for future use, need to set this to 0 */
264 open.flags = 0x00;
265 open.endpoint_id_1 = tmp_port;
266 open.endpoint_id_2 = 0xFFFF;
267
268 /* convert path to acdb path */
269 if (path == ADM_PATH_PLAYBACK)
270 open.topology_id = get_adm_rx_topology();
271 else {
272 open.topology_id = get_adm_tx_topology();
273 if ((open.topology_id ==
274 VPM_TX_SM_ECNS_COPP_TOPOLOGY) ||
275 (open.topology_id ==
276 VPM_TX_DM_FLUENCE_COPP_TOPOLOGY))
277 rate = 16000;
278 }
279
280 if (open.topology_id == 0)
281 open.topology_id = topology;
282
283 open.dev_num_channel = channel_mode & 0x00FF;
284 open.bit_width = 16;
285 open.sample_rate = rate;
286 memset(open.dev_channel_mapping, 0, 8);
287
288 if (channel_mode == 1) {
289 open.dev_channel_mapping[0] = PCM_CHANNEL_FC;
290 } else if (channel_mode == 2) {
291 open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
292 open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
293 } else if (channel_mode == 6) {
294 open.dev_channel_mapping[0] = PCM_CHANNEL_FL;
295 open.dev_channel_mapping[1] = PCM_CHANNEL_FR;
296 open.dev_channel_mapping[2] = PCM_CHANNEL_LFE;
297 open.dev_channel_mapping[3] = PCM_CHANNEL_FC;
298 open.dev_channel_mapping[4] = PCM_CHANNEL_LB;
299 open.dev_channel_mapping[5] = PCM_CHANNEL_RB;
300 } else {
301 pr_err("%s invalid num_chan %d\n", __func__,
302 channel_mode);
303 return -EINVAL;
304 }
305
306 pr_debug("%s: port_id=%d rate=%d"
307 "topology_id=0x%X\n", __func__, open.endpoint_id_1, \
308 open.sample_rate, open.topology_id);
309
310 atomic_set(&this_adm.copp_stat[index], 0);
311
312 ret = apr_send_pkt(this_adm.apr, (uint32_t *)&open);
313 if (ret < 0) {
314 pr_err("%s:ADM enable for port %d for[%d] failed\n",
315 __func__, tmp_port, port_id);
316 ret = -EINVAL;
317 goto fail_cmd;
318 }
319 /* Wait for the callback with copp id */
320 ret = wait_event_timeout(this_adm.wait[index],
321 atomic_read(&this_adm.copp_stat[index]),
322 msecs_to_jiffies(TIMEOUT_MS));
323 if (!ret) {
324 pr_err("%s ADM open failed for port %d"
325 "for [%d]\n", __func__, tmp_port, port_id);
326 ret = -EINVAL;
327 goto fail_cmd;
328 }
329 }
330 atomic_inc(&this_adm.copp_cnt[index]);
331 return 0;
332
333fail_cmd:
334
335 return ret;
336}
337
338
339int adm_multi_ch_copp_open(int port_id, int path, int rate, int channel_mode,
340 int topology)
341{
342 int ret = 0;
343
344 ret = adm_open(port_id, path, rate, channel_mode, topology);
345
346 return ret;
347}
348
349int adm_matrix_map(int session_id, int path, int num_copps,
350 unsigned int *port_id, int copp_id)
351{
352 struct adm_cmd_matrix_map_routings_v5 *route;
353 struct adm_session_map_node_v5 *node;
354 uint32_t *copps_list;
355 int cmd_size = 0;
356 int ret = 0, i = 0;
357 void *payload = NULL;
358 void *matrix_map = NULL;
359
360 /* Assumes port_ids have already been validated during adm_open */
361 int index = q6audio_get_port_index(copp_id);
362 if (index < 0 || index >= Q6_AFE_MAX_PORTS) {
363 pr_err("%s: invalid port idx %d token %d\n",
364 __func__, index, copp_id);
365 return 0;
366 }
367 cmd_size = (sizeof(struct adm_cmd_matrix_map_routings_v5) +
368 sizeof(struct adm_session_map_node_v5) +
369 (sizeof(uint32_t) * num_copps));
370 matrix_map = kzalloc(cmd_size, GFP_KERNEL);
371 if (matrix_map == NULL) {
372 pr_err("%s: Mem alloc failed\n", __func__);
373 ret = -EINVAL;
374 return ret;
375 }
376 route = (struct adm_cmd_matrix_map_routings_v5 *)matrix_map;
377
378 pr_debug("%s: session 0x%x path:%d num_copps:%d port_id[0] :%d coppid[%d]\n",
379 __func__, session_id, path, num_copps, port_id[0], copp_id);
380
381 route->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
382 APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
383 route->hdr.pkt_size = cmd_size;
384 route->hdr.src_svc = 0;
385 route->hdr.src_domain = APR_DOMAIN_APPS;
386 route->hdr.src_port = copp_id;
387 route->hdr.dest_svc = APR_SVC_ADM;
388 route->hdr.dest_domain = APR_DOMAIN_ADSP;
389 route->hdr.dest_port = atomic_read(&this_adm.copp_id[index]);
390 route->hdr.token = copp_id;
391 route->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5;
392 route->num_sessions = 1;
393
394 switch (path) {
395 case 0x1:
396 route->matrix_id = ADM_MATRIX_ID_AUDIO_RX;
397 break;
398 case 0x2:
399 case 0x3:
400 route->matrix_id = ADM_MATRIX_ID_AUDIO_TX;
401 break;
402 default:
403 pr_err("%s: Wrong path set[%d]\n", __func__, path);
404 break;
405 }
406 payload = ((u8 *)matrix_map +
407 sizeof(struct adm_cmd_matrix_map_routings_v5));
408 node = (struct adm_session_map_node_v5 *)payload;
409
410 node->session_id = session_id;
411 node->num_copps = num_copps;
412 payload = (u8 *)node + sizeof(struct adm_session_map_node_v5);
413 copps_list = (uint32_t *)payload;
414 for (i = 0; i < num_copps; i++) {
415 int tmp;
416 port_id[i] = q6audio_convert_virtual_to_portid(port_id[i]);
417
418 tmp = q6audio_get_port_index(port_id[i]);
419
420
421 if (tmp >= 0 && tmp < Q6_AFE_MAX_PORTS)
422 copps_list[i] =
423 atomic_read(&this_adm.copp_id[tmp]);
424 pr_debug("%s: port_id[%d]: %d, index: %d act coppid[0x%x]\n",
425 __func__, i, port_id[i], tmp,
426 atomic_read(&this_adm.copp_id[tmp]));
427 }
428 atomic_set(&this_adm.copp_stat[index], 0);
429
430 ret = apr_send_pkt(this_adm.apr, (uint32_t *)matrix_map);
431 if (ret < 0) {
432 pr_err("%s: ADM routing for port %d failed\n",
433 __func__, port_id[0]);
434 ret = -EINVAL;
435 goto fail_cmd;
436 }
437 ret = wait_event_timeout(this_adm.wait[index],
438 atomic_read(&this_adm.copp_stat[index]),
439 msecs_to_jiffies(TIMEOUT_MS));
440 if (!ret) {
441 pr_err("%s: ADM cmd Route failed for port %d\n",
442 __func__, port_id[0]);
443 ret = -EINVAL;
444 goto fail_cmd;
445 }
446 for (i = 0; i < num_copps; i++)
447 send_adm_cal(port_id[i], path);
448
449fail_cmd:
450 kfree(matrix_map);
451 return ret;
452}
453
454int adm_memory_map_regions(int port_id,
455 uint32_t *buf_add, uint32_t mempool_id,
456 uint32_t *bufsz, uint32_t bufcnt)
457{
458 struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL;
459 struct avs_shared_map_region_payload *mregions = NULL;
460 void *mmap_region_cmd = NULL;
461 void *payload = NULL;
462 int ret = 0;
463 int i = 0;
464 int cmd_size = 0;
465 int index = 0;
466
467 pr_debug("%s\n", __func__);
468 if (this_adm.apr == NULL) {
469 this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
470 0xFFFFFFFF, &this_adm);
471 if (this_adm.apr == NULL) {
472 pr_err("%s: Unable to register ADM\n", __func__);
473 ret = -ENODEV;
474 return ret;
475 }
476 rtac_set_adm_handle(this_adm.apr);
477 }
478
479 port_id = q6audio_convert_virtual_to_portid(port_id);
480
481 if (q6audio_validate_port(port_id) < 0) {
482 pr_err("%s port id[%d] is invalid\n", __func__, port_id);
483 return -ENODEV;
484 }
485
486 index = q6audio_get_port_index(port_id);
487
488 cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions)
489 + sizeof(struct avs_shared_map_region_payload)
490 * bufcnt;
491
492 mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
493 if (!mmap_region_cmd) {
494 pr_err("%s: allocate mmap_region_cmd failed\n", __func__);
495 return -ENOMEM;
496 }
497 mmap_regions = (struct avs_cmd_shared_mem_map_regions *)mmap_region_cmd;
498 mmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
499 APR_HDR_LEN(APR_HDR_SIZE),
500 APR_PKT_VER);
501 mmap_regions->hdr.pkt_size = cmd_size;
502 mmap_regions->hdr.src_port = 0;
503 mmap_regions->hdr.dest_port = 0;
504 mmap_regions->hdr.token = 0;
505 mmap_regions->hdr.opcode = ADM_CMD_SHARED_MEM_MAP_REGIONS;
Harmandeep Singhac1671b2012-06-22 15:34:45 -0700506 mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL & 0x00ff;
Bharath Ramachandramurthy2e3168f2012-05-03 16:29:09 -0700507 mmap_regions->num_regions = bufcnt & 0x00ff;
508 mmap_regions->property_flag = 0x00;
509
510 pr_debug("%s: map_regions->num_regions = %d\n", __func__,
511 mmap_regions->num_regions);
512 payload = ((u8 *) mmap_region_cmd +
513 sizeof(struct avs_cmd_shared_mem_map_regions));
514 mregions = (struct avs_shared_map_region_payload *)payload;
515
516 for (i = 0; i < bufcnt; i++) {
517 mregions->shm_addr_lsw = buf_add[i];
518 mregions->shm_addr_msw = 0x00;
519 mregions->mem_size_bytes = bufsz[i];
520 ++mregions;
521 }
522
523 atomic_set(&this_adm.copp_stat[0], 0);
524 ret = apr_send_pkt(this_adm.apr, (uint32_t *) mmap_region_cmd);
525 if (ret < 0) {
526 pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__,
527 mmap_regions->hdr.opcode, ret);
528 ret = -EINVAL;
529 goto fail_cmd;
530 }
531
532 ret = wait_event_timeout(this_adm.wait[index],
533 atomic_read(&this_adm.copp_stat[0]), 5 * HZ);
534 if (!ret) {
535 pr_err("%s: timeout. waited for memory_map\n", __func__);
536 ret = -EINVAL;
537 goto fail_cmd;
538 }
539fail_cmd:
540 kfree(mmap_region_cmd);
541 return ret;
542}
543
544int adm_memory_unmap_regions(int32_t port_id, uint32_t *buf_add,
545 uint32_t *bufsz, uint32_t bufcnt)
546{
547 struct avs_cmd_shared_mem_unmap_regions unmap_regions;
548 int ret = 0;
549 int cmd_size = 0;
550 int index = 0;
551
552 pr_debug("%s\n", __func__);
553
554 if (this_adm.apr == NULL) {
555 pr_err("%s APR handle NULL\n", __func__);
556 return -EINVAL;
557 }
558 port_id = q6audio_convert_virtual_to_portid(port_id);
559
560 if (q6audio_validate_port(port_id) < 0) {
561 pr_err("%s port idi[%d] is invalid\n", __func__, port_id);
562 return -ENODEV;
563 }
564
565 index = q6audio_get_port_index(port_id);
566
567 unmap_regions.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
568 APR_HDR_LEN(APR_HDR_SIZE),
569 APR_PKT_VER);
570 unmap_regions.hdr.pkt_size = cmd_size;
571 unmap_regions.hdr.src_port = 0;
572 unmap_regions.hdr.dest_port = 0;
573 unmap_regions.hdr.token = 0;
574 unmap_regions.hdr.opcode = ADM_CMD_SHARED_MEM_UNMAP_REGIONS;
575 unmap_regions.mem_map_handle = this_adm.mem_map_handle[index];
576 atomic_set(&this_adm.copp_stat[0], 0);
577 ret = apr_send_pkt(this_adm.apr, (uint32_t *) &unmap_regions);
578 if (ret < 0) {
579 pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__,
580 unmap_regions.hdr.opcode, ret);
581 ret = -EINVAL;
582 goto fail_cmd;
583 }
584
585 ret = wait_event_timeout(this_adm.wait[index],
586 atomic_read(&this_adm.copp_stat[0]), 5 * HZ);
587 if (!ret) {
588 pr_err("%s: timeout. waited for memory_unmap\n", __func__);
589 ret = -EINVAL;
590 goto fail_cmd;
591 }
592fail_cmd:
593 return ret;
594}
595
596int adm_get_copp_id(int port_index)
597{
598 pr_debug("%s\n", __func__);
599
600 if (port_index < 0) {
601 pr_err("%s: invalid port_id = %d\n", __func__, port_index);
602 return -EINVAL;
603 }
604
605 return atomic_read(&this_adm.copp_id[port_index]);
606}
607
608int adm_close(int port_id)
609{
610 struct apr_hdr close;
611
612 int ret = 0;
613 int index = 0;
614
615 port_id = q6audio_convert_virtual_to_portid(port_id);
616
617 index = q6audio_get_port_index(port_id);
618 if (q6audio_validate_port(port_id) < 0)
619 return -EINVAL;
620
621 pr_debug("%s port_id=%d index %d\n", __func__, port_id, index);
622
623 if (!(atomic_read(&this_adm.copp_cnt[index]))) {
624 pr_err("%s: copp count for port[%d]is 0\n", __func__, port_id);
625
626 goto fail_cmd;
627 }
628 atomic_dec(&this_adm.copp_cnt[index]);
629 if (!(atomic_read(&this_adm.copp_cnt[index]))) {
630
631 close.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
632 APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
633 close.pkt_size = sizeof(close);
634 close.src_svc = APR_SVC_ADM;
635 close.src_domain = APR_DOMAIN_APPS;
636 close.src_port = port_id;
637 close.dest_svc = APR_SVC_ADM;
638 close.dest_domain = APR_DOMAIN_ADSP;
639 close.dest_port = atomic_read(&this_adm.copp_id[index]);
640 close.token = port_id;
641 close.opcode = ADM_CMD_DEVICE_CLOSE_V5;
642
643 atomic_set(&this_adm.copp_id[index], RESET_COPP_ID);
644 atomic_set(&this_adm.copp_stat[index], 0);
645
646
647 pr_debug("%s:coppid %d portid=%d index=%d coppcnt=%d\n",
648 __func__,
649 atomic_read(&this_adm.copp_id[index]),
650 port_id, index,
651 atomic_read(&this_adm.copp_cnt[index]));
652
653 ret = apr_send_pkt(this_adm.apr, (uint32_t *)&close);
654 if (ret < 0) {
655 pr_err("%s ADM close failed\n", __func__);
656 ret = -EINVAL;
657 goto fail_cmd;
658 }
659
660 ret = wait_event_timeout(this_adm.wait[index],
661 atomic_read(&this_adm.copp_stat[index]),
662 msecs_to_jiffies(TIMEOUT_MS));
663 if (!ret) {
664 pr_err("%s: ADM cmd Route failed for port %d\n",
665 __func__, port_id);
666 ret = -EINVAL;
667 goto fail_cmd;
668 }
669
670 rtac_remove_adm_device(port_id);
671 }
672
673fail_cmd:
674 return ret;
675}
676
677static int __init adm_init(void)
678{
679 int i = 0;
680 this_adm.apr = NULL;
681
682 for (i = 0; i < Q6_AFE_MAX_PORTS; i++) {
683 atomic_set(&this_adm.copp_id[i], RESET_COPP_ID);
684 atomic_set(&this_adm.copp_cnt[i], 0);
685 atomic_set(&this_adm.copp_stat[i], 0);
686 init_waitqueue_head(&this_adm.wait[i]);
687 }
688 return 0;
689}
690
691device_initcall(adm_init);