blob: 7598255599b1f0782a59cc5cb6d40868d778a319 [file] [log] [blame]
Aditya Bavanari2e3341d2018-02-23 12:58:57 +05301/* Copyright (c) 2017-2018, 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/init.h>
14#include <linux/err.h>
15#include <linux/module.h>
16#include <linux/moduleparam.h>
17#include <linux/time.h>
18#include <linux/math64.h>
19#include <linux/wait.h>
20#include <linux/platform_device.h>
21#include <linux/slab.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053022#include <sound/core.h>
23#include <sound/soc.h>
24#include <sound/soc-dapm.h>
25#include <sound/pcm.h>
26#include <sound/initval.h>
27#include <sound/control.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053028#include <sound/pcm_params.h>
29#include <sound/timer.h>
30#include <sound/tlv.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053031#include <sound/compress_params.h>
32#include <sound/compress_offload.h>
33#include <sound/compress_driver.h>
Laxminath Kasam605b42f2017-08-01 22:02:15 +053034#include <dsp/msm_audio_ion.h>
35#include <dsp/apr_audio-v2.h>
36#include <dsp/q6asm-v2.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053037
38#include "msm-pcm-routing-v2.h"
39#include "msm-qti-pp-config.h"
40
41#define LOOPBACK_SESSION_MAX_NUM_STREAMS 2
42
43static DEFINE_MUTEX(transcode_loopback_session_lock);
44
45struct trans_loopback_pdata {
46 struct snd_compr_stream *cstream[MSM_FRONTEND_DAI_MAX];
47};
48
49struct loopback_stream {
50 struct snd_compr_stream *cstream;
51 uint32_t codec_format;
52 bool start;
53};
54
55enum loopback_session_state {
56 /* One or both streams not opened */
57 LOOPBACK_SESSION_CLOSE = 0,
58 /* Loopback streams opened */
59 LOOPBACK_SESSION_READY,
60 /* Loopback streams opened and formats configured */
61 LOOPBACK_SESSION_START,
62 /* Trigger issued on either of streams when in START state */
63 LOOPBACK_SESSION_RUN
64};
65
66struct msm_transcode_loopback {
67 struct loopback_stream source;
68 struct loopback_stream sink;
69
70 struct snd_compr_caps source_compr_cap;
71 struct snd_compr_caps sink_compr_cap;
72
73 uint32_t instance;
74 uint32_t num_streams;
75 int session_state;
76
77 struct mutex lock;
78
79 int session_id;
80 struct audio_client *audio_client;
81};
82
83/* Transcode loopback global info struct */
84static struct msm_transcode_loopback transcode_info;
85
86static void loopback_event_handler(uint32_t opcode,
87 uint32_t token, uint32_t *payload, void *priv)
88{
89 struct msm_transcode_loopback *trans =
90 (struct msm_transcode_loopback *)priv;
91 struct snd_soc_pcm_runtime *rtd;
92 struct snd_compr_stream *cstream;
93 struct audio_client *ac;
94 int stream_id;
95 int ret;
96
97 if (!trans || !payload) {
98 pr_err("%s: rtd or payload is NULL\n", __func__);
99 return;
100 }
101
102 cstream = trans->source.cstream;
103 ac = trans->audio_client;
104
105 /*
106 * Token for rest of the compressed commands use to set
107 * session id, stream id, dir etc.
108 */
109 stream_id = q6asm_get_stream_id_from_token(token);
110
111 switch (opcode) {
112 case ASM_STREAM_CMD_ENCDEC_EVENTS:
113 case ASM_IEC_61937_MEDIA_FMT_EVENT:
114 pr_debug("%s: ASM_IEC_61937_MEDIA_FMT_EVENT\n", __func__);
115 rtd = cstream->private_data;
116 if (!rtd) {
117 pr_err("%s: rtd is NULL\n", __func__);
118 return;
119 }
120
121 ret = msm_adsp_inform_mixer_ctl(rtd, payload);
122 if (ret) {
123 pr_err("%s: failed to inform mixer ctrl. err = %d\n",
124 __func__, ret);
125 return;
126 }
127 break;
128 case APR_BASIC_RSP_RESULT: {
129 switch (payload[0]) {
130 case ASM_SESSION_CMD_RUN_V2:
131 pr_debug("%s: ASM_SESSION_CMD_RUN_V2:", __func__);
132 pr_debug("token 0x%x, stream id %d\n", token,
133 stream_id);
134 break;
135 case ASM_STREAM_CMD_CLOSE:
136 pr_debug("%s: ASM_DATA_CMD_CLOSE:", __func__);
137 pr_debug("token 0x%x, stream id %d\n", token,
138 stream_id);
139 break;
140 default:
141 break;
142 }
143 break;
144 }
145 default:
146 pr_debug("%s: Not Supported Event opcode[0x%x]\n",
147 __func__, opcode);
148 break;
149 }
150}
151
152static void populate_codec_list(struct msm_transcode_loopback *trans,
153 struct snd_compr_stream *cstream)
154{
155 struct snd_compr_caps compr_cap;
156
157 pr_debug("%s\n", __func__);
158
159 memset(&compr_cap, 0, sizeof(struct snd_compr_caps));
160
161 if (cstream->direction == SND_COMPRESS_CAPTURE) {
162 compr_cap.direction = SND_COMPRESS_CAPTURE;
163 compr_cap.num_codecs = 3;
164 compr_cap.codecs[0] = SND_AUDIOCODEC_PCM;
165 compr_cap.codecs[1] = SND_AUDIOCODEC_AC3;
166 compr_cap.codecs[2] = SND_AUDIOCODEC_EAC3;
167 memcpy(&trans->source_compr_cap, &compr_cap,
168 sizeof(struct snd_compr_caps));
169 }
170
171 if (cstream->direction == SND_COMPRESS_PLAYBACK) {
172 compr_cap.direction = SND_COMPRESS_PLAYBACK;
173 compr_cap.num_codecs = 1;
174 compr_cap.codecs[0] = SND_AUDIOCODEC_PCM;
175 memcpy(&trans->sink_compr_cap, &compr_cap,
176 sizeof(struct snd_compr_caps));
177 }
178}
179
180static int msm_transcode_loopback_open(struct snd_compr_stream *cstream)
181{
182 int ret = 0;
183 struct snd_compr_runtime *runtime;
184 struct snd_soc_pcm_runtime *rtd;
185 struct msm_transcode_loopback *trans = &transcode_info;
186 struct trans_loopback_pdata *pdata;
187
188 if (cstream == NULL) {
189 pr_err("%s: Invalid substream\n", __func__);
190 return -EINVAL;
191 }
192 runtime = cstream->runtime;
193 rtd = snd_pcm_substream_chip(cstream);
194 pdata = snd_soc_platform_get_drvdata(rtd->platform);
195 pdata->cstream[rtd->dai_link->id] = cstream;
196
197 mutex_lock(&trans->lock);
198 if (trans->num_streams > LOOPBACK_SESSION_MAX_NUM_STREAMS) {
199 pr_err("msm_transcode_open failed..invalid stream\n");
200 ret = -EINVAL;
201 goto exit;
202 }
203
204 if (cstream->direction == SND_COMPRESS_CAPTURE) {
205 if (trans->source.cstream == NULL) {
206 trans->source.cstream = cstream;
207 trans->num_streams++;
208 } else {
209 pr_err("%s: capture stream already opened\n",
210 __func__);
211 ret = -EINVAL;
212 goto exit;
213 }
214 } else if (cstream->direction == SND_COMPRESS_PLAYBACK) {
215 if (trans->sink.cstream == NULL) {
216 trans->sink.cstream = cstream;
217 trans->num_streams++;
218 } else {
219 pr_debug("%s: playback stream already opened\n",
220 __func__);
221 ret = -EINVAL;
222 goto exit;
223 }
224 }
225
226 pr_debug("%s: num stream%d, stream name %s\n", __func__,
227 trans->num_streams, cstream->name);
228
229 populate_codec_list(trans, cstream);
230
231 if (trans->num_streams == LOOPBACK_SESSION_MAX_NUM_STREAMS) {
232 pr_debug("%s: Moving loopback session to READY state %d\n",
233 __func__, trans->session_state);
234 trans->session_state = LOOPBACK_SESSION_READY;
235 }
236
237 runtime->private_data = trans;
238 if (trans->num_streams == 1)
239 msm_adsp_init_mixer_ctl_pp_event_queue(rtd);
240exit:
241 mutex_unlock(&trans->lock);
242 return ret;
243}
244
245static void stop_transcoding(struct msm_transcode_loopback *trans)
246{
247 struct snd_soc_pcm_runtime *soc_pcm_rx;
248 struct snd_soc_pcm_runtime *soc_pcm_tx;
249
250 if (trans->audio_client != NULL) {
251 q6asm_cmd(trans->audio_client, CMD_CLOSE);
252
253 if (trans->sink.cstream != NULL) {
254 soc_pcm_rx = trans->sink.cstream->private_data;
255 msm_pcm_routing_dereg_phy_stream(
256 soc_pcm_rx->dai_link->id,
257 SND_COMPRESS_PLAYBACK);
258 }
259 if (trans->source.cstream != NULL) {
260 soc_pcm_tx = trans->source.cstream->private_data;
261 msm_pcm_routing_dereg_phy_stream(
262 soc_pcm_tx->dai_link->id,
263 SND_COMPRESS_CAPTURE);
264 }
265 q6asm_audio_client_free(trans->audio_client);
266 trans->audio_client = NULL;
267 }
268}
269
270static int msm_transcode_loopback_free(struct snd_compr_stream *cstream)
271{
272 struct snd_compr_runtime *runtime = cstream->runtime;
273 struct msm_transcode_loopback *trans = runtime->private_data;
274 struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(cstream);
275 int ret = 0;
276
277 mutex_lock(&trans->lock);
278
279 pr_debug("%s: Transcode loopback end:%d, streams %d\n", __func__,
280 cstream->direction, trans->num_streams);
281 trans->num_streams--;
282 stop_transcoding(trans);
283
284 if (cstream->direction == SND_COMPRESS_PLAYBACK)
285 memset(&trans->sink, 0, sizeof(struct loopback_stream));
286 else if (cstream->direction == SND_COMPRESS_CAPTURE)
287 memset(&trans->source, 0, sizeof(struct loopback_stream));
288
289 trans->session_state = LOOPBACK_SESSION_CLOSE;
290 if (trans->num_streams == 1)
291 msm_adsp_clean_mixer_ctl_pp_event_queue(rtd);
292 mutex_unlock(&trans->lock);
293 return ret;
294}
295
296static int msm_transcode_loopback_trigger(struct snd_compr_stream *cstream,
297 int cmd)
298{
299 struct snd_compr_runtime *runtime = cstream->runtime;
300 struct msm_transcode_loopback *trans = runtime->private_data;
301
302 switch (cmd) {
303 case SNDRV_PCM_TRIGGER_START:
304 case SNDRV_PCM_TRIGGER_RESUME:
305 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
306
307 if (trans->session_state == LOOPBACK_SESSION_START) {
308 pr_debug("%s: Issue Loopback session %d RUN\n",
309 __func__, trans->instance);
310 q6asm_run_nowait(trans->audio_client, 0, 0, 0);
311 trans->session_state = LOOPBACK_SESSION_RUN;
312 }
313 break;
314 case SNDRV_PCM_TRIGGER_SUSPEND:
315 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
316 case SNDRV_PCM_TRIGGER_STOP:
317 pr_debug("%s: Issue Loopback session %d STOP\n", __func__,
318 trans->instance);
319 if (trans->session_state == LOOPBACK_SESSION_RUN)
320 q6asm_cmd_nowait(trans->audio_client, CMD_PAUSE);
321 trans->session_state = LOOPBACK_SESSION_START;
322 break;
323
324 default:
325 break;
326 }
327 return 0;
328}
329
330static int msm_transcode_loopback_set_params(struct snd_compr_stream *cstream,
331 struct snd_compr_params *codec_param)
332{
333
334 struct snd_compr_runtime *runtime = cstream->runtime;
335 struct msm_transcode_loopback *trans = runtime->private_data;
336 struct snd_soc_pcm_runtime *soc_pcm_rx;
337 struct snd_soc_pcm_runtime *soc_pcm_tx;
338 uint32_t bit_width = 16;
339 int ret = 0;
340
341 if (trans == NULL) {
342 pr_err("%s: Invalid param\n", __func__);
343 return -EINVAL;
344 }
345
346 mutex_lock(&trans->lock);
347
348 if (cstream->direction == SND_COMPRESS_PLAYBACK) {
349 if (codec_param->codec.id == SND_AUDIOCODEC_PCM) {
350 trans->sink.codec_format =
351 FORMAT_LINEAR_PCM;
352 switch (codec_param->codec.format) {
353 case SNDRV_PCM_FORMAT_S32_LE:
354 bit_width = 32;
355 break;
356 case SNDRV_PCM_FORMAT_S24_LE:
357 bit_width = 24;
358 break;
359 case SNDRV_PCM_FORMAT_S24_3LE:
360 bit_width = 24;
361 break;
362 case SNDRV_PCM_FORMAT_S16_LE:
363 default:
364 bit_width = 16;
365 break;
366 }
367 } else {
368 pr_debug("%s: unknown sink codec\n", __func__);
369 ret = -EINVAL;
370 goto exit;
371 }
372 trans->sink.start = true;
373 }
374
375 if (cstream->direction == SND_COMPRESS_CAPTURE) {
376 switch (codec_param->codec.id) {
377 case SND_AUDIOCODEC_PCM:
378 pr_debug("Source SND_AUDIOCODEC_PCM\n");
379 trans->source.codec_format =
380 FORMAT_LINEAR_PCM;
381 break;
382 case SND_AUDIOCODEC_AC3:
383 pr_debug("Source SND_AUDIOCODEC_AC3\n");
384 trans->source.codec_format =
385 FORMAT_AC3;
386 break;
387 case SND_AUDIOCODEC_EAC3:
388 pr_debug("Source SND_AUDIOCODEC_EAC3\n");
389 trans->source.codec_format =
390 FORMAT_EAC3;
391 break;
392 default:
393 pr_debug("%s: unknown source codec\n", __func__);
394 ret = -EINVAL;
395 goto exit;
396 }
397 trans->source.start = true;
398 }
399
400 pr_debug("%s: trans->source.start %d trans->sink.start %d trans->source.cstream %pK trans->sink.cstream %pK trans->session_state %d\n",
401 __func__, trans->source.start, trans->sink.start,
402 trans->source.cstream, trans->sink.cstream,
403 trans->session_state);
404
405 if ((trans->session_state == LOOPBACK_SESSION_READY) &&
406 trans->source.start && trans->sink.start) {
407 pr_debug("%s: Moving loopback session to start state\n",
408 __func__);
409 trans->session_state = LOOPBACK_SESSION_START;
410 }
411
412 if (trans->session_state == LOOPBACK_SESSION_START) {
413 if (trans->audio_client != NULL) {
414 pr_debug("%s: ASM client already opened, closing\n",
415 __func__);
416 stop_transcoding(trans);
417 }
418
419 trans->audio_client = q6asm_audio_client_alloc(
420 (app_cb)loopback_event_handler, trans);
421 if (!trans->audio_client) {
422 pr_err("%s: Could not allocate memory\n", __func__);
423 ret = -EINVAL;
424 goto exit;
425 }
426 pr_debug("%s: ASM client allocated, callback %pK\n", __func__,
427 loopback_event_handler);
428 trans->session_id = trans->audio_client->session;
429 trans->audio_client->perf_mode = false;
430 ret = q6asm_open_transcode_loopback(trans->audio_client,
431 bit_width,
432 trans->source.codec_format,
433 trans->sink.codec_format);
434 if (ret < 0) {
435 pr_err("%s: Session transcode loopback open failed\n",
436 __func__);
437 q6asm_audio_client_free(trans->audio_client);
438 trans->audio_client = NULL;
439 goto exit;
440 }
441
442 pr_debug("%s: Starting ADM open for loopback\n", __func__);
443 soc_pcm_rx = trans->sink.cstream->private_data;
444 soc_pcm_tx = trans->source.cstream->private_data;
445 if (trans->source.codec_format != FORMAT_LINEAR_PCM)
446 msm_pcm_routing_reg_phy_compr_stream(
447 soc_pcm_tx->dai_link->id,
448 trans->audio_client->perf_mode,
449 trans->session_id,
450 SNDRV_PCM_STREAM_CAPTURE,
451 true);
452 else
453 msm_pcm_routing_reg_phy_stream(
454 soc_pcm_tx->dai_link->id,
455 trans->audio_client->perf_mode,
456 trans->session_id,
457 SNDRV_PCM_STREAM_CAPTURE);
458
459 msm_pcm_routing_reg_phy_stream(
460 soc_pcm_rx->dai_link->id,
461 trans->audio_client->perf_mode,
462 trans->session_id,
463 SNDRV_PCM_STREAM_PLAYBACK);
464 pr_debug("%s: Successfully opened ADM sessions\n", __func__);
465 }
466exit:
467 mutex_unlock(&trans->lock);
468 return ret;
469}
470
471static int msm_transcode_loopback_get_caps(struct snd_compr_stream *cstream,
472 struct snd_compr_caps *arg)
473{
474 struct snd_compr_runtime *runtime;
475 struct msm_transcode_loopback *trans;
476
477 if (!arg || !cstream) {
478 pr_err("%s: Invalid arguments\n", __func__);
479 return -EINVAL;
480 }
481
482 runtime = cstream->runtime;
483 trans = runtime->private_data;
484 pr_debug("%s\n", __func__);
485 if (cstream->direction == SND_COMPRESS_CAPTURE)
486 memcpy(arg, &trans->source_compr_cap,
487 sizeof(struct snd_compr_caps));
488 else
489 memcpy(arg, &trans->sink_compr_cap,
490 sizeof(struct snd_compr_caps));
491 return 0;
492}
493
494static int msm_transcode_stream_cmd_put(struct snd_kcontrol *kcontrol,
495 struct snd_ctl_elem_value *ucontrol)
496{
497 struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
498 unsigned long fe_id = kcontrol->private_value;
499 struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *)
500 snd_soc_component_get_drvdata(comp);
501 struct snd_compr_stream *cstream = NULL;
502 struct msm_transcode_loopback *prtd;
503 int ret = 0;
504 struct msm_adsp_event_data *event_data = NULL;
Aditya Bavanari2e3341d2018-02-23 12:58:57 +0530505 uint64_t actual_payload_len = 0;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530506
507 if (fe_id >= MSM_FRONTEND_DAI_MAX) {
508 pr_err("%s Received invalid fe_id %lu\n",
509 __func__, fe_id);
510 ret = -EINVAL;
511 goto done;
512 }
513
514 cstream = pdata->cstream[fe_id];
515 if (cstream == NULL) {
516 pr_err("%s cstream is null.\n", __func__);
517 ret = -EINVAL;
518 goto done;
519 }
520
521 prtd = cstream->runtime->private_data;
522 if (!prtd) {
523 pr_err("%s: prtd is null.\n", __func__);
524 ret = -EINVAL;
525 goto done;
526 }
527
528 if (prtd->audio_client == NULL) {
529 pr_err("%s: audio_client is null.\n", __func__);
530 ret = -EINVAL;
531 goto done;
532 }
533
534 event_data = (struct msm_adsp_event_data *)ucontrol->value.bytes.data;
535 if ((event_data->event_type < ADSP_STREAM_PP_EVENT) ||
536 (event_data->event_type >= ADSP_STREAM_EVENT_MAX)) {
537 pr_err("%s: invalid event_type=%d",
538 __func__, event_data->event_type);
539 ret = -EINVAL;
540 goto done;
541 }
542
Aditya Bavanari2e3341d2018-02-23 12:58:57 +0530543 actual_payload_len = sizeof(struct msm_adsp_event_data) +
544 event_data->payload_len;
545 if (actual_payload_len >= U32_MAX) {
546 pr_err("%s payload length 0x%X exceeds limit",
547 __func__, event_data->payload_len);
548 ret = -EINVAL;
549 goto done;
550 }
551
Xiaojun Sangae3d8862018-03-23 08:57:33 +0800552 if (event_data->payload_len > sizeof(ucontrol->value.bytes.data)
553 - sizeof(struct msm_adsp_event_data)) {
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530554 pr_err("%s param length=%d exceeds limit",
555 __func__, event_data->payload_len);
556 ret = -EINVAL;
557 goto done;
558 }
559
560 ret = q6asm_send_stream_cmd(prtd->audio_client, event_data);
561 if (ret < 0)
562 pr_err("%s: failed to send stream event cmd, err = %d\n",
563 __func__, ret);
564done:
565 return ret;
566}
567
568static int msm_transcode_ion_fd_map_put(struct snd_kcontrol *kcontrol,
569 struct snd_ctl_elem_value *ucontrol)
570{
571 struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
572 unsigned long fe_id = kcontrol->private_value;
573 struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *)
574 snd_soc_component_get_drvdata(comp);
575 struct snd_compr_stream *cstream = NULL;
576 struct msm_transcode_loopback *prtd;
577 int fd;
578 int ret = 0;
579
580 if (fe_id >= MSM_FRONTEND_DAI_MAX) {
581 pr_err("%s Received out of bounds invalid fe_id %lu\n",
582 __func__, fe_id);
583 ret = -EINVAL;
584 goto done;
585 }
586
587 cstream = pdata->cstream[fe_id];
588 if (cstream == NULL) {
589 pr_err("%s cstream is null\n", __func__);
590 ret = -EINVAL;
591 goto done;
592 }
593
594 prtd = cstream->runtime->private_data;
595 if (!prtd) {
596 pr_err("%s: prtd is null\n", __func__);
597 ret = -EINVAL;
598 goto done;
599 }
600
601 if (prtd->audio_client == NULL) {
602 pr_err("%s: audio_client is null\n", __func__);
603 ret = -EINVAL;
604 goto done;
605 }
606
607 memcpy(&fd, ucontrol->value.bytes.data, sizeof(fd));
608 ret = q6asm_send_ion_fd(prtd->audio_client, fd);
609 if (ret < 0)
610 pr_err("%s: failed to register ion fd\n", __func__);
611done:
612 return ret;
613}
614
615static int msm_transcode_rtic_event_ack_put(struct snd_kcontrol *kcontrol,
616 struct snd_ctl_elem_value *ucontrol)
617{
618 struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
619 unsigned long fe_id = kcontrol->private_value;
620 struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *)
621 snd_soc_component_get_drvdata(comp);
622 struct snd_compr_stream *cstream = NULL;
623 struct msm_transcode_loopback *prtd;
624 int ret = 0;
625 int param_length = 0;
626
627 if (fe_id >= MSM_FRONTEND_DAI_MAX) {
628 pr_err("%s Received invalid fe_id %lu\n",
629 __func__, fe_id);
630 ret = -EINVAL;
631 goto done;
632 }
633
634 cstream = pdata->cstream[fe_id];
635 if (cstream == NULL) {
636 pr_err("%s cstream is null\n", __func__);
637 ret = -EINVAL;
638 goto done;
639 }
640
641 prtd = cstream->runtime->private_data;
642 if (!prtd) {
643 pr_err("%s: prtd is null\n", __func__);
644 ret = -EINVAL;
645 goto done;
646 }
647
648 if (prtd->audio_client == NULL) {
649 pr_err("%s: audio_client is null\n", __func__);
650 ret = -EINVAL;
651 goto done;
652 }
653
654 memcpy(&param_length, ucontrol->value.bytes.data,
655 sizeof(param_length));
656 if ((param_length + sizeof(param_length))
657 >= sizeof(ucontrol->value.bytes.data)) {
658 pr_err("%s param length=%d exceeds limit",
659 __func__, param_length);
660 ret = -EINVAL;
661 goto done;
662 }
663
664 ret = q6asm_send_rtic_event_ack(prtd->audio_client,
665 ucontrol->value.bytes.data + sizeof(param_length),
666 param_length);
667 if (ret < 0)
668 pr_err("%s: failed to send rtic event ack, err = %d\n",
669 __func__, ret);
670done:
671 return ret;
672}
673
674static int msm_transcode_stream_cmd_control(
675 struct snd_soc_pcm_runtime *rtd)
676{
677 const char *mixer_ctl_name = DSP_STREAM_CMD;
678 const char *deviceNo = "NN";
679 char *mixer_str = NULL;
680 int ctl_len = 0, ret = 0;
681 struct snd_kcontrol_new fe_loopback_stream_cmd_config_control[1] = {
682 {
683 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
684 .name = "?",
685 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
686 .info = msm_adsp_stream_cmd_info,
687 .put = msm_transcode_stream_cmd_put,
688 .private_value = 0,
689 }
690 };
691
692 if (!rtd) {
693 pr_err("%s NULL rtd\n", __func__);
694 ret = -EINVAL;
695 goto done;
696 }
697
698 ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
699 mixer_str = kzalloc(ctl_len, GFP_KERNEL);
700 if (!mixer_str) {
701 ret = -ENOMEM;
702 goto done;
703 }
704
705 snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
706 fe_loopback_stream_cmd_config_control[0].name = mixer_str;
707 fe_loopback_stream_cmd_config_control[0].private_value =
708 rtd->dai_link->id;
709 pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
710 ret = snd_soc_add_platform_controls(rtd->platform,
711 fe_loopback_stream_cmd_config_control,
712 ARRAY_SIZE(fe_loopback_stream_cmd_config_control));
713 if (ret < 0)
714 pr_err("%s: failed to add ctl %s. err = %d\n",
715 __func__, mixer_str, ret);
716
717 kfree(mixer_str);
718done:
719 return ret;
720}
721
722static int msm_transcode_stream_callback_control(
723 struct snd_soc_pcm_runtime *rtd)
724{
725 const char *mixer_ctl_name = DSP_STREAM_CALLBACK;
726 const char *deviceNo = "NN";
727 char *mixer_str = NULL;
728 int ctl_len = 0, ret = 0;
729 struct snd_kcontrol *kctl;
730
731 struct snd_kcontrol_new fe_loopback_callback_config_control[1] = {
732 {
733 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
734 .name = "?",
735 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
736 .info = msm_adsp_stream_callback_info,
737 .get = msm_adsp_stream_callback_get,
738 .private_value = 0,
739 }
740 };
741
742 if (!rtd) {
743 pr_err("%s: rtd is NULL\n", __func__);
744 ret = -EINVAL;
745 goto done;
746 }
747
748 ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
749 mixer_str = kzalloc(ctl_len, GFP_KERNEL);
750 if (!mixer_str) {
751 ret = -ENOMEM;
752 goto done;
753 }
754
755 snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
756 fe_loopback_callback_config_control[0].name = mixer_str;
757 fe_loopback_callback_config_control[0].private_value =
758 rtd->dai_link->id;
759 pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
760 ret = snd_soc_add_platform_controls(rtd->platform,
761 fe_loopback_callback_config_control,
762 ARRAY_SIZE(fe_loopback_callback_config_control));
763 if (ret < 0) {
764 pr_err("%s: failed to add ctl %s. err = %d\n",
765 __func__, mixer_str, ret);
766 ret = -EINVAL;
767 goto free_mixer_str;
768 }
769
770 kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str);
771 if (!kctl) {
772 pr_err("%s: failed to get kctl %s.\n", __func__, mixer_str);
773 ret = -EINVAL;
774 goto free_mixer_str;
775 }
776
777 kctl->private_data = NULL;
778free_mixer_str:
779 kfree(mixer_str);
780done:
781 return ret;
782}
783
784static int msm_transcode_add_ion_fd_cmd_control(struct snd_soc_pcm_runtime *rtd)
785{
786 const char *mixer_ctl_name = "Playback ION FD";
787 const char *deviceNo = "NN";
788 char *mixer_str = NULL;
789 int ctl_len = 0, ret = 0;
790 struct snd_kcontrol_new fe_ion_fd_config_control[1] = {
791 {
792 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
793 .name = "?",
794 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
795 .info = msm_adsp_stream_cmd_info,
796 .put = msm_transcode_ion_fd_map_put,
797 .private_value = 0,
798 }
799 };
800
801 if (!rtd) {
802 pr_err("%s NULL rtd\n", __func__);
803 ret = -EINVAL;
804 goto done;
805 }
806
807 ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
808 mixer_str = kzalloc(ctl_len, GFP_KERNEL);
809 if (!mixer_str) {
810 ret = -ENOMEM;
811 goto done;
812 }
813
814 snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
815 fe_ion_fd_config_control[0].name = mixer_str;
816 fe_ion_fd_config_control[0].private_value = rtd->dai_link->id;
817 pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
818 ret = snd_soc_add_platform_controls(rtd->platform,
819 fe_ion_fd_config_control,
820 ARRAY_SIZE(fe_ion_fd_config_control));
821 if (ret < 0)
822 pr_err("%s: failed to add ctl %s\n", __func__, mixer_str);
823
824 kfree(mixer_str);
825done:
826 return ret;
827}
828
829static int msm_transcode_add_event_ack_cmd_control(
830 struct snd_soc_pcm_runtime *rtd)
831{
832 const char *mixer_ctl_name = "Playback Event Ack";
833 const char *deviceNo = "NN";
834 char *mixer_str = NULL;
835 int ctl_len = 0, ret = 0;
836 struct snd_kcontrol_new fe_event_ack_config_control[1] = {
837 {
838 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
839 .name = "?",
840 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
841 .info = msm_adsp_stream_cmd_info,
842 .put = msm_transcode_rtic_event_ack_put,
843 .private_value = 0,
844 }
845 };
846
847 if (!rtd) {
848 pr_err("%s NULL rtd\n", __func__);
849 ret = -EINVAL;
850 goto done;
851 }
852
853 ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
854 mixer_str = kzalloc(ctl_len, GFP_KERNEL);
855 if (!mixer_str) {
856 ret = -ENOMEM;
857 goto done;
858 }
859
860 snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
861 fe_event_ack_config_control[0].name = mixer_str;
862 fe_event_ack_config_control[0].private_value = rtd->dai_link->id;
863 pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
864 ret = snd_soc_add_platform_controls(rtd->platform,
865 fe_event_ack_config_control,
866 ARRAY_SIZE(fe_event_ack_config_control));
867 if (ret < 0)
868 pr_err("%s: failed to add ctl %s\n", __func__, mixer_str);
869
870 kfree(mixer_str);
871done:
872 return ret;
873}
874
875static int msm_transcode_loopback_new(struct snd_soc_pcm_runtime *rtd)
876{
877 int rc;
878
879 rc = msm_transcode_stream_cmd_control(rtd);
880 if (rc)
881 pr_err("%s: ADSP Stream Cmd Control open failed\n", __func__);
882
883 rc = msm_transcode_stream_callback_control(rtd);
884 if (rc)
885 pr_err("%s: ADSP Stream callback Control open failed\n",
886 __func__);
887
888 rc = msm_transcode_add_ion_fd_cmd_control(rtd);
889 if (rc)
890 pr_err("%s: Could not add transcode ion fd Control\n",
891 __func__);
892
893 rc = msm_transcode_add_event_ack_cmd_control(rtd);
894 if (rc)
895 pr_err("%s: Could not add transcode event ack Control\n",
896 __func__);
897
898 return 0;
899}
900
901static struct snd_compr_ops msm_transcode_loopback_ops = {
902 .open = msm_transcode_loopback_open,
903 .free = msm_transcode_loopback_free,
904 .trigger = msm_transcode_loopback_trigger,
905 .set_params = msm_transcode_loopback_set_params,
906 .get_caps = msm_transcode_loopback_get_caps,
907};
908
909
910static int msm_transcode_loopback_probe(struct snd_soc_platform *platform)
911{
912 struct trans_loopback_pdata *pdata = NULL;
913
914 pr_debug("%s\n", __func__);
915 pdata = (struct trans_loopback_pdata *)
916 kzalloc(sizeof(struct trans_loopback_pdata),
917 GFP_KERNEL);
918 if (!pdata)
919 return -ENOMEM;
920
921 snd_soc_platform_set_drvdata(platform, pdata);
922 return 0;
923}
924
925static struct snd_soc_platform_driver msm_soc_platform = {
926 .probe = msm_transcode_loopback_probe,
927 .compr_ops = &msm_transcode_loopback_ops,
928 .pcm_new = msm_transcode_loopback_new,
929};
930
931static int msm_transcode_dev_probe(struct platform_device *pdev)
932{
933
934 pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
935 if (pdev->dev.of_node)
936 dev_set_name(&pdev->dev, "%s", "msm-transcode-loopback");
937
938 return snd_soc_register_platform(&pdev->dev,
939 &msm_soc_platform);
940}
941
942static int msm_transcode_remove(struct platform_device *pdev)
943{
944 snd_soc_unregister_platform(&pdev->dev);
945 return 0;
946}
947
948static const struct of_device_id msm_transcode_loopback_dt_match[] = {
949 {.compatible = "qcom,msm-transcode-loopback"},
950 {}
951};
952MODULE_DEVICE_TABLE(of, msm_transcode_loopback_dt_match);
953
954static struct platform_driver msm_transcode_loopback_driver = {
955 .driver = {
956 .name = "msm-transcode-loopback",
957 .owner = THIS_MODULE,
958 .of_match_table = msm_transcode_loopback_dt_match,
959 },
960 .probe = msm_transcode_dev_probe,
961 .remove = msm_transcode_remove,
962};
963
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530964int __init msm_transcode_loopback_init(void)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530965{
966 memset(&transcode_info, 0, sizeof(struct msm_transcode_loopback));
967 mutex_init(&transcode_info.lock);
968 return platform_driver_register(&msm_transcode_loopback_driver);
969}
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530970
Asish Bhattacharya5faacb32017-12-04 17:23:15 +0530971void msm_transcode_loopback_exit(void)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530972{
973 mutex_destroy(&transcode_info.lock);
974 platform_driver_unregister(&msm_transcode_loopback_driver);
975}
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530976
977MODULE_DESCRIPTION("Transcode loopback platform driver");
978MODULE_LICENSE("GPL v2");