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