blob: 94ebbf748a92b5548649ded9ef70a506a6ce04ab [file] [log] [blame]
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
2 *
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;
505
506 if (fe_id >= MSM_FRONTEND_DAI_MAX) {
507 pr_err("%s Received invalid fe_id %lu\n",
508 __func__, fe_id);
509 ret = -EINVAL;
510 goto done;
511 }
512
513 cstream = pdata->cstream[fe_id];
514 if (cstream == NULL) {
515 pr_err("%s cstream is null.\n", __func__);
516 ret = -EINVAL;
517 goto done;
518 }
519
520 prtd = cstream->runtime->private_data;
521 if (!prtd) {
522 pr_err("%s: prtd is null.\n", __func__);
523 ret = -EINVAL;
524 goto done;
525 }
526
527 if (prtd->audio_client == NULL) {
528 pr_err("%s: audio_client is null.\n", __func__);
529 ret = -EINVAL;
530 goto done;
531 }
532
533 event_data = (struct msm_adsp_event_data *)ucontrol->value.bytes.data;
534 if ((event_data->event_type < ADSP_STREAM_PP_EVENT) ||
535 (event_data->event_type >= ADSP_STREAM_EVENT_MAX)) {
536 pr_err("%s: invalid event_type=%d",
537 __func__, event_data->event_type);
538 ret = -EINVAL;
539 goto done;
540 }
541
542 if ((sizeof(struct msm_adsp_event_data) + event_data->payload_len) >=
543 sizeof(ucontrol->value.bytes.data)) {
544 pr_err("%s param length=%d exceeds limit",
545 __func__, event_data->payload_len);
546 ret = -EINVAL;
547 goto done;
548 }
549
550 ret = q6asm_send_stream_cmd(prtd->audio_client, event_data);
551 if (ret < 0)
552 pr_err("%s: failed to send stream event cmd, err = %d\n",
553 __func__, ret);
554done:
555 return ret;
556}
557
558static int msm_transcode_ion_fd_map_put(struct snd_kcontrol *kcontrol,
559 struct snd_ctl_elem_value *ucontrol)
560{
561 struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
562 unsigned long fe_id = kcontrol->private_value;
563 struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *)
564 snd_soc_component_get_drvdata(comp);
565 struct snd_compr_stream *cstream = NULL;
566 struct msm_transcode_loopback *prtd;
567 int fd;
568 int ret = 0;
569
570 if (fe_id >= MSM_FRONTEND_DAI_MAX) {
571 pr_err("%s Received out of bounds invalid fe_id %lu\n",
572 __func__, fe_id);
573 ret = -EINVAL;
574 goto done;
575 }
576
577 cstream = pdata->cstream[fe_id];
578 if (cstream == NULL) {
579 pr_err("%s cstream is null\n", __func__);
580 ret = -EINVAL;
581 goto done;
582 }
583
584 prtd = cstream->runtime->private_data;
585 if (!prtd) {
586 pr_err("%s: prtd is null\n", __func__);
587 ret = -EINVAL;
588 goto done;
589 }
590
591 if (prtd->audio_client == NULL) {
592 pr_err("%s: audio_client is null\n", __func__);
593 ret = -EINVAL;
594 goto done;
595 }
596
597 memcpy(&fd, ucontrol->value.bytes.data, sizeof(fd));
598 ret = q6asm_send_ion_fd(prtd->audio_client, fd);
599 if (ret < 0)
600 pr_err("%s: failed to register ion fd\n", __func__);
601done:
602 return ret;
603}
604
605static int msm_transcode_rtic_event_ack_put(struct snd_kcontrol *kcontrol,
606 struct snd_ctl_elem_value *ucontrol)
607{
608 struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
609 unsigned long fe_id = kcontrol->private_value;
610 struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *)
611 snd_soc_component_get_drvdata(comp);
612 struct snd_compr_stream *cstream = NULL;
613 struct msm_transcode_loopback *prtd;
614 int ret = 0;
615 int param_length = 0;
616
617 if (fe_id >= MSM_FRONTEND_DAI_MAX) {
618 pr_err("%s Received invalid fe_id %lu\n",
619 __func__, fe_id);
620 ret = -EINVAL;
621 goto done;
622 }
623
624 cstream = pdata->cstream[fe_id];
625 if (cstream == NULL) {
626 pr_err("%s cstream is null\n", __func__);
627 ret = -EINVAL;
628 goto done;
629 }
630
631 prtd = cstream->runtime->private_data;
632 if (!prtd) {
633 pr_err("%s: prtd is null\n", __func__);
634 ret = -EINVAL;
635 goto done;
636 }
637
638 if (prtd->audio_client == NULL) {
639 pr_err("%s: audio_client is null\n", __func__);
640 ret = -EINVAL;
641 goto done;
642 }
643
644 memcpy(&param_length, ucontrol->value.bytes.data,
645 sizeof(param_length));
646 if ((param_length + sizeof(param_length))
647 >= sizeof(ucontrol->value.bytes.data)) {
648 pr_err("%s param length=%d exceeds limit",
649 __func__, param_length);
650 ret = -EINVAL;
651 goto done;
652 }
653
654 ret = q6asm_send_rtic_event_ack(prtd->audio_client,
655 ucontrol->value.bytes.data + sizeof(param_length),
656 param_length);
657 if (ret < 0)
658 pr_err("%s: failed to send rtic event ack, err = %d\n",
659 __func__, ret);
660done:
661 return ret;
662}
663
664static int msm_transcode_stream_cmd_control(
665 struct snd_soc_pcm_runtime *rtd)
666{
667 const char *mixer_ctl_name = DSP_STREAM_CMD;
668 const char *deviceNo = "NN";
669 char *mixer_str = NULL;
670 int ctl_len = 0, ret = 0;
671 struct snd_kcontrol_new fe_loopback_stream_cmd_config_control[1] = {
672 {
673 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
674 .name = "?",
675 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
676 .info = msm_adsp_stream_cmd_info,
677 .put = msm_transcode_stream_cmd_put,
678 .private_value = 0,
679 }
680 };
681
682 if (!rtd) {
683 pr_err("%s NULL rtd\n", __func__);
684 ret = -EINVAL;
685 goto done;
686 }
687
688 ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
689 mixer_str = kzalloc(ctl_len, GFP_KERNEL);
690 if (!mixer_str) {
691 ret = -ENOMEM;
692 goto done;
693 }
694
695 snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
696 fe_loopback_stream_cmd_config_control[0].name = mixer_str;
697 fe_loopback_stream_cmd_config_control[0].private_value =
698 rtd->dai_link->id;
699 pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
700 ret = snd_soc_add_platform_controls(rtd->platform,
701 fe_loopback_stream_cmd_config_control,
702 ARRAY_SIZE(fe_loopback_stream_cmd_config_control));
703 if (ret < 0)
704 pr_err("%s: failed to add ctl %s. err = %d\n",
705 __func__, mixer_str, ret);
706
707 kfree(mixer_str);
708done:
709 return ret;
710}
711
712static int msm_transcode_stream_callback_control(
713 struct snd_soc_pcm_runtime *rtd)
714{
715 const char *mixer_ctl_name = DSP_STREAM_CALLBACK;
716 const char *deviceNo = "NN";
717 char *mixer_str = NULL;
718 int ctl_len = 0, ret = 0;
719 struct snd_kcontrol *kctl;
720
721 struct snd_kcontrol_new fe_loopback_callback_config_control[1] = {
722 {
723 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
724 .name = "?",
725 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
726 .info = msm_adsp_stream_callback_info,
727 .get = msm_adsp_stream_callback_get,
728 .private_value = 0,
729 }
730 };
731
732 if (!rtd) {
733 pr_err("%s: rtd is NULL\n", __func__);
734 ret = -EINVAL;
735 goto done;
736 }
737
738 ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
739 mixer_str = kzalloc(ctl_len, GFP_KERNEL);
740 if (!mixer_str) {
741 ret = -ENOMEM;
742 goto done;
743 }
744
745 snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
746 fe_loopback_callback_config_control[0].name = mixer_str;
747 fe_loopback_callback_config_control[0].private_value =
748 rtd->dai_link->id;
749 pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
750 ret = snd_soc_add_platform_controls(rtd->platform,
751 fe_loopback_callback_config_control,
752 ARRAY_SIZE(fe_loopback_callback_config_control));
753 if (ret < 0) {
754 pr_err("%s: failed to add ctl %s. err = %d\n",
755 __func__, mixer_str, ret);
756 ret = -EINVAL;
757 goto free_mixer_str;
758 }
759
760 kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str);
761 if (!kctl) {
762 pr_err("%s: failed to get kctl %s.\n", __func__, mixer_str);
763 ret = -EINVAL;
764 goto free_mixer_str;
765 }
766
767 kctl->private_data = NULL;
768free_mixer_str:
769 kfree(mixer_str);
770done:
771 return ret;
772}
773
774static int msm_transcode_add_ion_fd_cmd_control(struct snd_soc_pcm_runtime *rtd)
775{
776 const char *mixer_ctl_name = "Playback ION FD";
777 const char *deviceNo = "NN";
778 char *mixer_str = NULL;
779 int ctl_len = 0, ret = 0;
780 struct snd_kcontrol_new fe_ion_fd_config_control[1] = {
781 {
782 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
783 .name = "?",
784 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
785 .info = msm_adsp_stream_cmd_info,
786 .put = msm_transcode_ion_fd_map_put,
787 .private_value = 0,
788 }
789 };
790
791 if (!rtd) {
792 pr_err("%s NULL rtd\n", __func__);
793 ret = -EINVAL;
794 goto done;
795 }
796
797 ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
798 mixer_str = kzalloc(ctl_len, GFP_KERNEL);
799 if (!mixer_str) {
800 ret = -ENOMEM;
801 goto done;
802 }
803
804 snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
805 fe_ion_fd_config_control[0].name = mixer_str;
806 fe_ion_fd_config_control[0].private_value = rtd->dai_link->id;
807 pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
808 ret = snd_soc_add_platform_controls(rtd->platform,
809 fe_ion_fd_config_control,
810 ARRAY_SIZE(fe_ion_fd_config_control));
811 if (ret < 0)
812 pr_err("%s: failed to add ctl %s\n", __func__, mixer_str);
813
814 kfree(mixer_str);
815done:
816 return ret;
817}
818
819static int msm_transcode_add_event_ack_cmd_control(
820 struct snd_soc_pcm_runtime *rtd)
821{
822 const char *mixer_ctl_name = "Playback Event Ack";
823 const char *deviceNo = "NN";
824 char *mixer_str = NULL;
825 int ctl_len = 0, ret = 0;
826 struct snd_kcontrol_new fe_event_ack_config_control[1] = {
827 {
828 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
829 .name = "?",
830 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
831 .info = msm_adsp_stream_cmd_info,
832 .put = msm_transcode_rtic_event_ack_put,
833 .private_value = 0,
834 }
835 };
836
837 if (!rtd) {
838 pr_err("%s NULL rtd\n", __func__);
839 ret = -EINVAL;
840 goto done;
841 }
842
843 ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
844 mixer_str = kzalloc(ctl_len, GFP_KERNEL);
845 if (!mixer_str) {
846 ret = -ENOMEM;
847 goto done;
848 }
849
850 snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
851 fe_event_ack_config_control[0].name = mixer_str;
852 fe_event_ack_config_control[0].private_value = rtd->dai_link->id;
853 pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
854 ret = snd_soc_add_platform_controls(rtd->platform,
855 fe_event_ack_config_control,
856 ARRAY_SIZE(fe_event_ack_config_control));
857 if (ret < 0)
858 pr_err("%s: failed to add ctl %s\n", __func__, mixer_str);
859
860 kfree(mixer_str);
861done:
862 return ret;
863}
864
865static int msm_transcode_loopback_new(struct snd_soc_pcm_runtime *rtd)
866{
867 int rc;
868
869 rc = msm_transcode_stream_cmd_control(rtd);
870 if (rc)
871 pr_err("%s: ADSP Stream Cmd Control open failed\n", __func__);
872
873 rc = msm_transcode_stream_callback_control(rtd);
874 if (rc)
875 pr_err("%s: ADSP Stream callback Control open failed\n",
876 __func__);
877
878 rc = msm_transcode_add_ion_fd_cmd_control(rtd);
879 if (rc)
880 pr_err("%s: Could not add transcode ion fd Control\n",
881 __func__);
882
883 rc = msm_transcode_add_event_ack_cmd_control(rtd);
884 if (rc)
885 pr_err("%s: Could not add transcode event ack Control\n",
886 __func__);
887
888 return 0;
889}
890
891static struct snd_compr_ops msm_transcode_loopback_ops = {
892 .open = msm_transcode_loopback_open,
893 .free = msm_transcode_loopback_free,
894 .trigger = msm_transcode_loopback_trigger,
895 .set_params = msm_transcode_loopback_set_params,
896 .get_caps = msm_transcode_loopback_get_caps,
897};
898
899
900static int msm_transcode_loopback_probe(struct snd_soc_platform *platform)
901{
902 struct trans_loopback_pdata *pdata = NULL;
903
904 pr_debug("%s\n", __func__);
905 pdata = (struct trans_loopback_pdata *)
906 kzalloc(sizeof(struct trans_loopback_pdata),
907 GFP_KERNEL);
908 if (!pdata)
909 return -ENOMEM;
910
911 snd_soc_platform_set_drvdata(platform, pdata);
912 return 0;
913}
914
915static struct snd_soc_platform_driver msm_soc_platform = {
916 .probe = msm_transcode_loopback_probe,
917 .compr_ops = &msm_transcode_loopback_ops,
918 .pcm_new = msm_transcode_loopback_new,
919};
920
921static int msm_transcode_dev_probe(struct platform_device *pdev)
922{
923
924 pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
925 if (pdev->dev.of_node)
926 dev_set_name(&pdev->dev, "%s", "msm-transcode-loopback");
927
928 return snd_soc_register_platform(&pdev->dev,
929 &msm_soc_platform);
930}
931
932static int msm_transcode_remove(struct platform_device *pdev)
933{
934 snd_soc_unregister_platform(&pdev->dev);
935 return 0;
936}
937
938static const struct of_device_id msm_transcode_loopback_dt_match[] = {
939 {.compatible = "qcom,msm-transcode-loopback"},
940 {}
941};
942MODULE_DEVICE_TABLE(of, msm_transcode_loopback_dt_match);
943
944static struct platform_driver msm_transcode_loopback_driver = {
945 .driver = {
946 .name = "msm-transcode-loopback",
947 .owner = THIS_MODULE,
948 .of_match_table = msm_transcode_loopback_dt_match,
949 },
950 .probe = msm_transcode_dev_probe,
951 .remove = msm_transcode_remove,
952};
953
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530954int __init msm_transcode_loopback_init(void)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530955{
956 memset(&transcode_info, 0, sizeof(struct msm_transcode_loopback));
957 mutex_init(&transcode_info.lock);
958 return platform_driver_register(&msm_transcode_loopback_driver);
959}
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530960
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530961void __exit msm_transcode_loopback_exit(void)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530962{
963 mutex_destroy(&transcode_info.lock);
964 platform_driver_unregister(&msm_transcode_loopback_driver);
965}
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530966
967MODULE_DESCRIPTION("Transcode loopback platform driver");
968MODULE_LICENSE("GPL v2");