blob: e50a00575d95acf86ae15a8b405b50ce91703595 [file] [log] [blame]
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301/* Copyright (c) 2013-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/platform_device.h>
17#include <linux/slab.h>
18#include <linux/dma-mapping.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053019#include <sound/core.h>
20#include <sound/soc.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053021#include <sound/pcm.h>
22#include <sound/initval.h>
23#include <sound/control.h>
24#include <sound/tlv.h>
25#include <asm/dma.h>
Laxminath Kasam605b42f2017-08-01 22:02:15 +053026#include <dsp/apr_audio-v2.h>
27#include <dsp/q6audio-v2.h>
28#include <dsp/q6asm-v2.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053029
30#include "msm-pcm-routing-v2.h"
31
32#define LOOPBACK_VOL_MAX_STEPS 0x2000
33#define LOOPBACK_SESSION_MAX 4
34
35static DEFINE_MUTEX(loopback_session_lock);
36static const DECLARE_TLV_DB_LINEAR(loopback_rx_vol_gain, 0,
37 LOOPBACK_VOL_MAX_STEPS);
38
39struct msm_pcm_loopback {
40 struct snd_pcm_substream *playback_substream;
41 struct snd_pcm_substream *capture_substream;
42
43 int instance;
44
45 struct mutex lock;
46
47 uint32_t samp_rate;
48 uint32_t channel_mode;
49
50 int playback_start;
51 int capture_start;
52 int session_id;
53 struct audio_client *audio_client;
54 uint32_t volume;
55};
56
57struct fe_dai_session_map {
58 char stream_name[32];
59 struct msm_pcm_loopback *loopback_priv;
60};
61
62static struct fe_dai_session_map session_map[LOOPBACK_SESSION_MAX] = {
63 { {}, NULL},
64 { {}, NULL},
65 { {}, NULL},
66 { {}, NULL},
67};
68
69static u32 hfp_tx_mute;
70
71struct msm_pcm_pdata {
72 int perf_mode;
73};
74
75static void stop_pcm(struct msm_pcm_loopback *pcm);
76static int msm_pcm_loopback_get_session(struct snd_soc_pcm_runtime *rtd,
77 struct msm_pcm_loopback **pcm);
78
79static void msm_pcm_route_event_handler(enum msm_pcm_routing_event event,
80 void *priv_data)
81{
82 struct msm_pcm_loopback *pcm = priv_data;
83
84 WARN_ON(!pcm);
85
86 pr_debug("%s: event 0x%x\n", __func__, event);
87
88 switch (event) {
89 case MSM_PCM_RT_EVT_DEVSWITCH:
90 q6asm_cmd(pcm->audio_client, CMD_PAUSE);
91 q6asm_cmd(pcm->audio_client, CMD_FLUSH);
92 q6asm_run(pcm->audio_client, 0, 0, 0);
93 /* fallthrough */
94 default:
95 pr_err("%s: default event 0x%x\n", __func__, event);
96 break;
97 }
98}
99
100static void msm_pcm_loopback_event_handler(uint32_t opcode, uint32_t token,
101 uint32_t *payload, void *priv)
102{
103 pr_debug("%s:\n", __func__);
104 switch (opcode) {
105 case APR_BASIC_RSP_RESULT: {
106 switch (payload[0]) {
107 break;
108 default:
109 break;
110 }
111 }
112 break;
113 default:
114 pr_err("%s: Not Supported Event opcode[0x%x]\n",
115 __func__, opcode);
116 break;
117 }
118}
119
120static int msm_loopback_session_mute_get(struct snd_kcontrol *kcontrol,
121 struct snd_ctl_elem_value *ucontrol)
122{
123 ucontrol->value.integer.value[0] = hfp_tx_mute;
124 return 0;
125}
126
127static int msm_loopback_session_mute_put(struct snd_kcontrol *kcontrol,
128 struct snd_ctl_elem_value *ucontrol)
129{
130 int ret = 0, n = 0;
131 int mute = ucontrol->value.integer.value[0];
132 struct msm_pcm_loopback *pcm = NULL;
133
134 if ((mute < 0) || (mute > 1)) {
135 pr_err(" %s Invalid arguments", __func__);
136 ret = -EINVAL;
137 goto done;
138 }
139
140 pr_debug("%s: mute=%d\n", __func__, mute);
141 hfp_tx_mute = mute;
142 for (n = 0; n < LOOPBACK_SESSION_MAX; n++) {
143 if (!strcmp(session_map[n].stream_name, "MultiMedia6"))
144 pcm = session_map[n].loopback_priv;
145 }
146 if (pcm && pcm->audio_client) {
147 ret = q6asm_set_mute(pcm->audio_client, mute);
148 if (ret < 0)
149 pr_err("%s: Send mute command failed rc=%d\n",
150 __func__, ret);
151 }
152done:
153 return ret;
154}
155
156static struct snd_kcontrol_new msm_loopback_controls[] = {
157 SOC_SINGLE_EXT("HFP TX Mute", SND_SOC_NOPM, 0, 1, 0,
158 msm_loopback_session_mute_get,
159 msm_loopback_session_mute_put),
160};
161
162static int msm_pcm_loopback_probe(struct snd_soc_platform *platform)
163{
164 snd_soc_add_platform_controls(platform, msm_loopback_controls,
165 ARRAY_SIZE(msm_loopback_controls));
166
167 return 0;
168}
169static int pcm_loopback_set_volume(struct msm_pcm_loopback *prtd,
170 uint32_t volume)
171{
172 int rc = -EINVAL;
173
174 pr_debug("%s: Setting volume 0x%x\n", __func__, volume);
175
176 if (prtd && prtd->audio_client) {
177 rc = q6asm_set_volume(prtd->audio_client, volume);
178 if (rc < 0) {
179 pr_err("%s: Send Volume command failed rc = %d\n",
180 __func__, rc);
181 return rc;
182 }
183 prtd->volume = volume;
184 }
185 return rc;
186}
187
188static int msm_pcm_loopback_get_session(struct snd_soc_pcm_runtime *rtd,
189 struct msm_pcm_loopback **pcm)
190{
191 int ret = 0;
192 int n, index = -1;
193
194 dev_dbg(rtd->platform->dev, "%s: stream %s\n", __func__,
195 rtd->dai_link->stream_name);
196
197 mutex_lock(&loopback_session_lock);
198 for (n = 0; n < LOOPBACK_SESSION_MAX; n++) {
199 if (!strcmp(rtd->dai_link->stream_name,
200 session_map[n].stream_name)) {
201 *pcm = session_map[n].loopback_priv;
202 goto exit;
203 }
204 /*
205 * Store the min index value for allocating a new session.
206 * Here, if session stream name is not found in the
207 * existing entries after the loop iteration, then this
208 * index will be used to allocate the new session.
209 * This index variable is expected to point to the topmost
210 * available free session.
211 */
212 if (!(session_map[n].stream_name[0]) && (index < 0))
213 index = n;
214 }
215
216 if (index < 0) {
217 dev_err(rtd->platform->dev, "%s: Max Sessions allocated\n",
218 __func__);
219 ret = -EAGAIN;
220 goto exit;
221 }
222
223 session_map[index].loopback_priv = kzalloc(
224 sizeof(struct msm_pcm_loopback), GFP_KERNEL);
225 if (!session_map[index].loopback_priv) {
226 ret = -ENOMEM;
227 goto exit;
228 }
229
230 strlcpy(session_map[index].stream_name,
231 rtd->dai_link->stream_name,
232 sizeof(session_map[index].stream_name));
233 dev_dbg(rtd->platform->dev, "%s: stream %s index %d\n",
234 __func__, session_map[index].stream_name, index);
235
236 mutex_init(&session_map[index].loopback_priv->lock);
237 *pcm = session_map[index].loopback_priv;
238exit:
239 mutex_unlock(&loopback_session_lock);
240 return ret;
241}
242
243static int msm_pcm_open(struct snd_pcm_substream *substream)
244{
245 struct snd_pcm_runtime *runtime = substream->runtime;
246 struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
247 struct msm_pcm_loopback *pcm = NULL;
248 int ret = 0;
249 uint16_t bits_per_sample = 16;
250 struct msm_pcm_routing_evt event;
251 struct asm_session_mtmx_strtr_param_window_v2_t asm_mtmx_strtr_window;
252 uint32_t param_id;
253 struct msm_pcm_pdata *pdata;
254
255 ret = msm_pcm_loopback_get_session(rtd, &pcm);
256 if (ret)
257 return ret;
258
259 mutex_lock(&pcm->lock);
260
261 pcm->volume = 0x2000;
262
263 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
264 pcm->playback_substream = substream;
265 else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
266 pcm->capture_substream = substream;
267
268 pcm->instance++;
269 dev_dbg(rtd->platform->dev, "%s: pcm out open: %d,%d\n", __func__,
270 pcm->instance, substream->stream);
271 if (pcm->instance == 2) {
272 struct snd_soc_pcm_runtime *soc_pcm_rx =
273 pcm->playback_substream->private_data;
274 struct snd_soc_pcm_runtime *soc_pcm_tx =
275 pcm->capture_substream->private_data;
276 if (pcm->audio_client != NULL)
277 stop_pcm(pcm);
278
279 pdata = (struct msm_pcm_pdata *)
280 dev_get_drvdata(rtd->platform->dev);
281 if (!pdata) {
282 dev_err(rtd->platform->dev,
283 "%s: platform data not populated\n", __func__);
284 mutex_unlock(&pcm->lock);
285 return -EINVAL;
286 }
287
288 pcm->audio_client = q6asm_audio_client_alloc(
289 (app_cb)msm_pcm_loopback_event_handler, pcm);
290 if (!pcm->audio_client) {
291 dev_err(rtd->platform->dev,
292 "%s: Could not allocate memory\n", __func__);
293 mutex_unlock(&pcm->lock);
294 return -ENOMEM;
295 }
296 pcm->session_id = pcm->audio_client->session;
297 pcm->audio_client->perf_mode = pdata->perf_mode;
298 ret = q6asm_open_loopback_v2(pcm->audio_client,
299 bits_per_sample);
300 if (ret < 0) {
301 dev_err(rtd->platform->dev,
302 "%s: pcm out open failed\n", __func__);
303 q6asm_audio_client_free(pcm->audio_client);
304 mutex_unlock(&pcm->lock);
305 return -ENOMEM;
306 }
307 event.event_func = msm_pcm_route_event_handler;
308 event.priv_data = (void *) pcm;
309 msm_pcm_routing_reg_phy_stream(soc_pcm_tx->dai_link->id,
310 pcm->audio_client->perf_mode,
311 pcm->session_id, pcm->capture_substream->stream);
312 msm_pcm_routing_reg_phy_stream_v2(soc_pcm_rx->dai_link->id,
313 pcm->audio_client->perf_mode,
314 pcm->session_id, pcm->playback_substream->stream,
315 event);
316 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
317 pcm->playback_substream = substream;
318 ret = pcm_loopback_set_volume(pcm, pcm->volume);
319 if (ret < 0)
320 dev_err(rtd->platform->dev,
321 "Error %d setting volume", ret);
322 }
323 /* Set to largest negative value */
324 asm_mtmx_strtr_window.window_lsw = 0x00000000;
325 asm_mtmx_strtr_window.window_msw = 0x80000000;
326 param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_START_V2;
327 q6asm_send_mtmx_strtr_window(pcm->audio_client,
328 &asm_mtmx_strtr_window,
329 param_id);
330 /* Set to largest positive value */
331 asm_mtmx_strtr_window.window_lsw = 0xffffffff;
332 asm_mtmx_strtr_window.window_msw = 0x7fffffff;
333 param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_END_V2;
334 q6asm_send_mtmx_strtr_window(pcm->audio_client,
335 &asm_mtmx_strtr_window,
336 param_id);
337 }
338 dev_info(rtd->platform->dev, "%s: Instance = %d, Stream ID = %s\n",
339 __func__, pcm->instance, substream->pcm->id);
340 runtime->private_data = pcm;
341
342 mutex_unlock(&pcm->lock);
343
344 return 0;
345}
346
347static void stop_pcm(struct msm_pcm_loopback *pcm)
348{
349 struct snd_soc_pcm_runtime *soc_pcm_rx;
350 struct snd_soc_pcm_runtime *soc_pcm_tx;
351
352 if (pcm->audio_client == NULL)
353 return;
354 q6asm_cmd(pcm->audio_client, CMD_CLOSE);
355
356 if (pcm->playback_substream != NULL) {
357 soc_pcm_rx = pcm->playback_substream->private_data;
358 msm_pcm_routing_dereg_phy_stream(soc_pcm_rx->dai_link->id,
359 SNDRV_PCM_STREAM_PLAYBACK);
360 }
361 if (pcm->capture_substream != NULL) {
362 soc_pcm_tx = pcm->capture_substream->private_data;
363 msm_pcm_routing_dereg_phy_stream(soc_pcm_tx->dai_link->id,
364 SNDRV_PCM_STREAM_CAPTURE);
365 }
366 q6asm_audio_client_free(pcm->audio_client);
367 pcm->audio_client = NULL;
368}
369
370static int msm_pcm_close(struct snd_pcm_substream *substream)
371{
372 struct snd_pcm_runtime *runtime = substream->runtime;
373 struct msm_pcm_loopback *pcm = runtime->private_data;
374 struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
375 int ret = 0, n;
376 bool found = false;
377
378 mutex_lock(&pcm->lock);
379
380 dev_dbg(rtd->platform->dev, "%s: end pcm call:%d\n",
381 __func__, substream->stream);
382 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
383 pcm->playback_start = 0;
384 else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
385 pcm->capture_start = 0;
386
387 pcm->instance--;
388 if (!pcm->playback_start || !pcm->capture_start) {
389 dev_dbg(rtd->platform->dev, "%s: end pcm call\n", __func__);
390 stop_pcm(pcm);
391 }
392
393 if (!pcm->instance) {
394 mutex_lock(&loopback_session_lock);
395 for (n = 0; n < LOOPBACK_SESSION_MAX; n++) {
396 if (!strcmp(rtd->dai_link->stream_name,
397 session_map[n].stream_name)) {
398 found = true;
399 break;
400 }
401 }
402 if (found) {
403 memset(session_map[n].stream_name, 0,
404 sizeof(session_map[n].stream_name));
405 mutex_unlock(&pcm->lock);
406 mutex_destroy(&session_map[n].loopback_priv->lock);
407 session_map[n].loopback_priv = NULL;
408 kfree(pcm);
409 dev_dbg(rtd->platform->dev, "%s: stream freed %s\n",
410 __func__, rtd->dai_link->stream_name);
411 mutex_unlock(&loopback_session_lock);
412 return 0;
413 }
414 mutex_unlock(&loopback_session_lock);
415 }
416 mutex_unlock(&pcm->lock);
417 return ret;
418}
419
420static int msm_pcm_prepare(struct snd_pcm_substream *substream)
421{
422 int ret = 0;
423 struct snd_pcm_runtime *runtime = substream->runtime;
424 struct msm_pcm_loopback *pcm = runtime->private_data;
425 struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
426
427 mutex_lock(&pcm->lock);
428
429 dev_dbg(rtd->platform->dev, "%s: ASM loopback stream:%d\n",
430 __func__, substream->stream);
431 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
432 if (!pcm->playback_start)
433 pcm->playback_start = 1;
434 } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
435 if (!pcm->capture_start)
436 pcm->capture_start = 1;
437 }
438 mutex_unlock(&pcm->lock);
439
440 return ret;
441}
442
443static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
444{
445 struct snd_pcm_runtime *runtime = substream->runtime;
446 struct msm_pcm_loopback *pcm = runtime->private_data;
447 struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
448
449 switch (cmd) {
450 case SNDRV_PCM_TRIGGER_START:
451 case SNDRV_PCM_TRIGGER_RESUME:
452 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
453 dev_dbg(rtd->platform->dev,
454 "%s: playback_start:%d,capture_start:%d\n", __func__,
455 pcm->playback_start, pcm->capture_start);
456 if (pcm->playback_start && pcm->capture_start)
457 q6asm_run_nowait(pcm->audio_client, 0, 0, 0);
458 break;
459 case SNDRV_PCM_TRIGGER_SUSPEND:
460 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
461 case SNDRV_PCM_TRIGGER_STOP:
462 dev_dbg(rtd->platform->dev,
463 "%s:Pause/Stop - playback_start:%d,capture_start:%d\n",
464 __func__, pcm->playback_start, pcm->capture_start);
465 if (pcm->playback_start && pcm->capture_start)
466 q6asm_cmd_nowait(pcm->audio_client, CMD_PAUSE);
467 break;
468 default:
469 pr_err("%s: default cmd %d\n", __func__, cmd);
470 break;
471 }
472
473 return 0;
474}
475
476static const struct snd_pcm_ops msm_pcm_ops = {
477 .open = msm_pcm_open,
478 .close = msm_pcm_close,
479 .prepare = msm_pcm_prepare,
480 .trigger = msm_pcm_trigger,
481};
482
483static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol,
484 struct snd_ctl_elem_value *ucontrol)
485{
486 int rc = 0;
487 struct snd_pcm_volume *vol = kcontrol->private_data;
488 struct snd_pcm_substream *substream = vol->pcm->streams[0].substream;
489 struct msm_pcm_loopback *prtd;
490 int volume = ucontrol->value.integer.value[0];
491
492 pr_debug("%s: volume : 0x%x\n", __func__, volume);
493 if ((!substream) || (!substream->runtime)) {
494 pr_err("%s substream or runtime not found\n", __func__);
495 rc = -ENODEV;
496 goto exit;
497 }
498 prtd = substream->runtime->private_data;
499 if (!prtd) {
500 rc = -ENODEV;
501 goto exit;
502 }
503 rc = pcm_loopback_set_volume(prtd, volume);
504
505exit:
506 return rc;
507}
508
509static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol,
510 struct snd_ctl_elem_value *ucontrol)
511{
512 int rc = 0;
513 struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
514 struct snd_pcm_substream *substream =
515 vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
516 struct msm_pcm_loopback *prtd;
517
518 pr_debug("%s\n", __func__);
519 if ((!substream) || (!substream->runtime)) {
520 pr_err("%s substream or runtime not found\n", __func__);
521 rc = -ENODEV;
522 goto exit;
523 }
524 prtd = substream->runtime->private_data;
525 if (!prtd) {
526 rc = -ENODEV;
527 goto exit;
528 }
529 ucontrol->value.integer.value[0] = prtd->volume;
530
531exit:
532 return rc;
533}
534
535static int msm_pcm_add_volume_controls(struct snd_soc_pcm_runtime *rtd)
536{
537 struct snd_pcm *pcm = rtd->pcm->streams[0].pcm;
538 struct snd_pcm_volume *volume_info;
539 struct snd_kcontrol *kctl;
540 int ret = 0;
541
542 dev_dbg(rtd->dev, "%s, Volume cntrl add\n", __func__);
543 ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
544 NULL, 1,
545 rtd->dai_link->id,
546 &volume_info);
547 if (ret < 0)
548 return ret;
549 kctl = volume_info->kctl;
550 kctl->put = msm_pcm_volume_ctl_put;
551 kctl->get = msm_pcm_volume_ctl_get;
552 kctl->tlv.p = loopback_rx_vol_gain;
553 return 0;
554}
555
556static int msm_pcm_playback_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
557 struct snd_ctl_elem_value *ucontrol)
558{
559 u64 fe_id = kcontrol->private_value;
560 int session_type = SESSION_TYPE_RX;
561 int be_id = ucontrol->value.integer.value[3];
562 struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000};
563 int ret = 0;
564
565 cfg_data.app_type = ucontrol->value.integer.value[0];
566 cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
567 if (ucontrol->value.integer.value[2] != 0)
568 cfg_data.sample_rate = ucontrol->value.integer.value[2];
569 pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n",
570 __func__, fe_id, session_type, be_id,
571 cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
572 ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
573 be_id, &cfg_data);
574 if (ret < 0)
575 pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n",
576 __func__, ret);
577
578 return ret;
579}
580
581static int msm_pcm_playback_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
582 struct snd_ctl_elem_value *ucontrol)
583{
584 u64 fe_id = kcontrol->private_value;
585 int session_type = SESSION_TYPE_RX;
586 int be_id = 0;
587 struct msm_pcm_stream_app_type_cfg cfg_data = {0};
588 int ret = 0;
589
590 ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type,
591 &be_id, &cfg_data);
592 if (ret < 0) {
593 pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n",
594 __func__, ret);
595 goto done;
596 }
597
598 ucontrol->value.integer.value[0] = cfg_data.app_type;
599 ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
600 ucontrol->value.integer.value[2] = cfg_data.sample_rate;
601 ucontrol->value.integer.value[3] = be_id;
602 pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
603 __func__, fe_id, session_type, be_id,
604 cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
605done:
606 return ret;
607}
608
609static int msm_pcm_capture_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
610 struct snd_ctl_elem_value *ucontrol)
611{
612 u64 fe_id = kcontrol->private_value;
613 int session_type = SESSION_TYPE_TX;
614 int be_id = ucontrol->value.integer.value[3];
615 struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000};
616 int ret = 0;
617
618 cfg_data.app_type = ucontrol->value.integer.value[0];
619 cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
620 if (ucontrol->value.integer.value[2] != 0)
621 cfg_data.sample_rate = ucontrol->value.integer.value[2];
622 pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n",
623 __func__, fe_id, session_type, be_id,
624 cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
625 ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
626 be_id, &cfg_data);
627 if (ret < 0)
628 pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n",
629 __func__, ret);
630
631 return ret;
632}
633
634static int msm_pcm_capture_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
635 struct snd_ctl_elem_value *ucontrol)
636{
637 u64 fe_id = kcontrol->private_value;
638 int session_type = SESSION_TYPE_TX;
639 int be_id = 0;
640 struct msm_pcm_stream_app_type_cfg cfg_data = {0};
641 int ret = 0;
642
643 ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type,
644 &be_id, &cfg_data);
645 if (ret < 0) {
646 pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n",
647 __func__, ret);
648 goto done;
649 }
650
651 ucontrol->value.integer.value[0] = cfg_data.app_type;
652 ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
653 ucontrol->value.integer.value[2] = cfg_data.sample_rate;
654 ucontrol->value.integer.value[3] = be_id;
655 pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
656 __func__, fe_id, session_type, be_id,
657 cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
658done:
659 return ret;
660}
661
662static int msm_pcm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd)
663{
664 struct snd_pcm *pcm = rtd->pcm->streams[0].pcm;
665 struct snd_pcm_usr *app_type_info;
666 struct snd_kcontrol *kctl;
667 const char *playback_mixer_ctl_name = "Audio Stream";
668 const char *capture_mixer_ctl_name = "Audio Stream Capture";
669 const char *deviceNo = "NN";
670 const char *suffix = "App Type Cfg";
671 int ctl_len, ret = 0;
672
673 if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
674 ctl_len = strlen(playback_mixer_ctl_name) + 1 +
675 strlen(deviceNo) + 1 + strlen(suffix) + 1;
676 pr_debug("%s: Playback app type cntrl add\n", __func__);
677 ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
678 NULL, 1, ctl_len, rtd->dai_link->id,
679 &app_type_info);
680 if (ret < 0)
681 return ret;
682 kctl = app_type_info->kctl;
683 snprintf(kctl->id.name, ctl_len, "%s %d %s",
684 playback_mixer_ctl_name, rtd->pcm->device, suffix);
685 kctl->put = msm_pcm_playback_app_type_cfg_ctl_put;
686 kctl->get = msm_pcm_playback_app_type_cfg_ctl_get;
687 }
688
689 if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
690 ctl_len = strlen(capture_mixer_ctl_name) + 1 +
691 strlen(deviceNo) + 1 + strlen(suffix) + 1;
692 pr_debug("%s: Capture app type cntrl add\n", __func__);
693 ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE,
694 NULL, 1, ctl_len, rtd->dai_link->id,
695 &app_type_info);
696 if (ret < 0)
697 return ret;
698 kctl = app_type_info->kctl;
699 snprintf(kctl->id.name, ctl_len, "%s %d %s",
700 capture_mixer_ctl_name, rtd->pcm->device, suffix);
701 kctl->put = msm_pcm_capture_app_type_cfg_ctl_put;
702 kctl->get = msm_pcm_capture_app_type_cfg_ctl_get;
703 }
704
705 return 0;
706}
707
708static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
709{
710 int ret = 0;
711
712 pr_debug("%s\n", __func__);
713 ret = msm_pcm_add_volume_controls(rtd);
714 if (ret)
715 pr_err("%s: pcm add volume controls failed:%d\n",
716 __func__, ret);
717 ret = msm_pcm_add_app_type_controls(rtd);
718 if (ret)
719 pr_err("%s: pcm add app type controls failed:%d\n",
720 __func__, ret);
721 return ret;
722}
723
724static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
725{
726 struct snd_card *card = rtd->card->snd_card;
727 int ret = 0;
728
729 if (!card->dev->coherent_dma_mask)
730 card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
731
732 ret = msm_pcm_add_controls(rtd);
733 if (ret)
734 dev_err(rtd->dev, "%s, kctl add failed\n", __func__);
735 return ret;
736}
737
738static struct snd_soc_platform_driver msm_soc_platform = {
739 .ops = &msm_pcm_ops,
740 .pcm_new = msm_asoc_pcm_new,
741 .probe = msm_pcm_loopback_probe,
742};
743
744static int msm_pcm_probe(struct platform_device *pdev)
745{
746 struct msm_pcm_pdata *pdata;
747
748 dev_dbg(&pdev->dev, "%s: dev name %s\n",
749 __func__, dev_name(&pdev->dev));
750
751 pdata = kzalloc(sizeof(struct msm_pcm_pdata), GFP_KERNEL);
752 if (!pdata)
753 return -ENOMEM;
754
755 if (of_property_read_bool(pdev->dev.of_node,
756 "qcom,msm-pcm-loopback-low-latency"))
757 pdata->perf_mode = LOW_LATENCY_PCM_MODE;
758 else
759 pdata->perf_mode = LEGACY_PCM_MODE;
760
761 dev_set_drvdata(&pdev->dev, pdata);
762
763 return snd_soc_register_platform(&pdev->dev,
764 &msm_soc_platform);
765}
766
767static int msm_pcm_remove(struct platform_device *pdev)
768{
769 snd_soc_unregister_platform(&pdev->dev);
770 return 0;
771}
772
773static const struct of_device_id msm_pcm_loopback_dt_match[] = {
774 {.compatible = "qcom,msm-pcm-loopback"},
775 {}
776};
777
778static struct platform_driver msm_pcm_driver = {
779 .driver = {
780 .name = "msm-pcm-loopback",
781 .owner = THIS_MODULE,
782 .of_match_table = msm_pcm_loopback_dt_match,
783 },
784 .probe = msm_pcm_probe,
785 .remove = msm_pcm_remove,
786};
787
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530788int __init msm_pcm_loopback_init(void)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530789{
790 return platform_driver_register(&msm_pcm_driver);
791}
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530792
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530793void __exit msm_pcm_loopback_exit(void)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530794{
795 platform_driver_unregister(&msm_pcm_driver);
796}
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530797
798MODULE_DESCRIPTION("PCM loopback platform driver");
799MODULE_LICENSE("GPL v2");