blob: b516634fd1b367ca3349c6d07aa0e45d99b8e326 [file] [log] [blame]
Meng Wang43bbb872018-12-10 12:32:05 +08001// SPDX-License-Identifier: GPL-2.0-only
Vignesh Kulothungan2ce67842018-09-25 16:40:29 -07002/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved.
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05303 */
4
5#include <linux/init.h>
6#include <linux/err.h>
7#include <linux/module.h>
8#include <linux/platform_device.h>
9#include <linux/slab.h>
10#include <linux/dma-mapping.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053011#include <sound/core.h>
12#include <sound/soc.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053013#include <sound/pcm.h>
14#include <sound/initval.h>
15#include <sound/control.h>
16#include <sound/tlv.h>
17#include <asm/dma.h>
Laxminath Kasam605b42f2017-08-01 22:02:15 +053018#include <dsp/apr_audio-v2.h>
19#include <dsp/q6audio-v2.h>
20#include <dsp/q6asm-v2.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053021
22#include "msm-pcm-routing-v2.h"
23
Meng Wangee084a02018-09-04 16:11:58 +080024#define DRV_NAME "msm-pcm-loopback-v2"
25
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053026#define LOOPBACK_VOL_MAX_STEPS 0x2000
27#define LOOPBACK_SESSION_MAX 4
28
29static DEFINE_MUTEX(loopback_session_lock);
30static const DECLARE_TLV_DB_LINEAR(loopback_rx_vol_gain, 0,
31 LOOPBACK_VOL_MAX_STEPS);
32
33struct msm_pcm_loopback {
34 struct snd_pcm_substream *playback_substream;
35 struct snd_pcm_substream *capture_substream;
36
37 int instance;
38
39 struct mutex lock;
40
41 uint32_t samp_rate;
42 uint32_t channel_mode;
43
44 int playback_start;
45 int capture_start;
46 int session_id;
47 struct audio_client *audio_client;
48 uint32_t volume;
49};
50
51struct fe_dai_session_map {
52 char stream_name[32];
53 struct msm_pcm_loopback *loopback_priv;
54};
55
56static struct fe_dai_session_map session_map[LOOPBACK_SESSION_MAX] = {
57 { {}, NULL},
58 { {}, NULL},
59 { {}, NULL},
60 { {}, NULL},
61};
62
63static u32 hfp_tx_mute;
64
65struct msm_pcm_pdata {
66 int perf_mode;
67};
68
69static void stop_pcm(struct msm_pcm_loopback *pcm);
70static int msm_pcm_loopback_get_session(struct snd_soc_pcm_runtime *rtd,
71 struct msm_pcm_loopback **pcm);
72
73static void msm_pcm_route_event_handler(enum msm_pcm_routing_event event,
74 void *priv_data)
75{
76 struct msm_pcm_loopback *pcm = priv_data;
77
78 WARN_ON(!pcm);
79
80 pr_debug("%s: event 0x%x\n", __func__, event);
81
82 switch (event) {
83 case MSM_PCM_RT_EVT_DEVSWITCH:
84 q6asm_cmd(pcm->audio_client, CMD_PAUSE);
85 q6asm_cmd(pcm->audio_client, CMD_FLUSH);
86 q6asm_run(pcm->audio_client, 0, 0, 0);
87 /* fallthrough */
88 default:
89 pr_err("%s: default event 0x%x\n", __func__, event);
90 break;
91 }
92}
93
94static void msm_pcm_loopback_event_handler(uint32_t opcode, uint32_t token,
95 uint32_t *payload, void *priv)
96{
97 pr_debug("%s:\n", __func__);
98 switch (opcode) {
99 case APR_BASIC_RSP_RESULT: {
100 switch (payload[0]) {
101 break;
102 default:
103 break;
104 }
105 }
106 break;
107 default:
108 pr_err("%s: Not Supported Event opcode[0x%x]\n",
109 __func__, opcode);
110 break;
111 }
112}
113
114static int msm_loopback_session_mute_get(struct snd_kcontrol *kcontrol,
115 struct snd_ctl_elem_value *ucontrol)
116{
117 ucontrol->value.integer.value[0] = hfp_tx_mute;
118 return 0;
119}
120
121static int msm_loopback_session_mute_put(struct snd_kcontrol *kcontrol,
122 struct snd_ctl_elem_value *ucontrol)
123{
124 int ret = 0, n = 0;
125 int mute = ucontrol->value.integer.value[0];
126 struct msm_pcm_loopback *pcm = NULL;
127
128 if ((mute < 0) || (mute > 1)) {
129 pr_err(" %s Invalid arguments", __func__);
130 ret = -EINVAL;
131 goto done;
132 }
133
134 pr_debug("%s: mute=%d\n", __func__, mute);
135 hfp_tx_mute = mute;
136 for (n = 0; n < LOOPBACK_SESSION_MAX; n++) {
137 if (!strcmp(session_map[n].stream_name, "MultiMedia6"))
138 pcm = session_map[n].loopback_priv;
139 }
140 if (pcm && pcm->audio_client) {
141 ret = q6asm_set_mute(pcm->audio_client, mute);
142 if (ret < 0)
143 pr_err("%s: Send mute command failed rc=%d\n",
144 __func__, ret);
145 }
146done:
147 return ret;
148}
149
150static struct snd_kcontrol_new msm_loopback_controls[] = {
151 SOC_SINGLE_EXT("HFP TX Mute", SND_SOC_NOPM, 0, 1, 0,
152 msm_loopback_session_mute_get,
153 msm_loopback_session_mute_put),
154};
155
Meng Wangee084a02018-09-04 16:11:58 +0800156static int msm_pcm_loopback_probe(struct snd_soc_component *component)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530157{
Meng Wangee084a02018-09-04 16:11:58 +0800158 snd_soc_add_component_controls(component, msm_loopback_controls,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530159 ARRAY_SIZE(msm_loopback_controls));
160
161 return 0;
162}
163static int pcm_loopback_set_volume(struct msm_pcm_loopback *prtd,
164 uint32_t volume)
165{
166 int rc = -EINVAL;
167
168 pr_debug("%s: Setting volume 0x%x\n", __func__, volume);
169
170 if (prtd && prtd->audio_client) {
171 rc = q6asm_set_volume(prtd->audio_client, volume);
172 if (rc < 0) {
173 pr_err("%s: Send Volume command failed rc = %d\n",
174 __func__, rc);
175 return rc;
176 }
177 prtd->volume = volume;
178 }
179 return rc;
180}
181
182static int msm_pcm_loopback_get_session(struct snd_soc_pcm_runtime *rtd,
183 struct msm_pcm_loopback **pcm)
184{
Meng Wangee084a02018-09-04 16:11:58 +0800185 struct snd_soc_component *component =
186 snd_soc_rtdcom_lookup(rtd, DRV_NAME);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530187 int ret = 0;
188 int n, index = -1;
189
Meng Wangee084a02018-09-04 16:11:58 +0800190 if (!component) {
191 pr_err("%s: component is NULL\n", __func__);
192 return -EINVAL;
193 }
194
195 dev_dbg(component->dev, "%s: stream %s\n", __func__,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530196 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) {
Meng Wangee084a02018-09-04 16:11:58 +0800218 dev_err(component->dev, "%s: Max Sessions allocated\n",
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530219 __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));
Meng Wangee084a02018-09-04 16:11:58 +0800234 dev_dbg(component->dev, "%s: stream %s index %d\n",
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530235 __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);
Meng Wangee084a02018-09-04 16:11:58 +0800248 struct snd_soc_component *component =
249 snd_soc_rtdcom_lookup(rtd, DRV_NAME);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530250 struct msm_pcm_loopback *pcm = NULL;
251 int ret = 0;
252 uint16_t bits_per_sample = 16;
253 struct msm_pcm_routing_evt event;
254 struct asm_session_mtmx_strtr_param_window_v2_t asm_mtmx_strtr_window;
255 uint32_t param_id;
256 struct msm_pcm_pdata *pdata;
257
Meng Wangee084a02018-09-04 16:11:58 +0800258 if (!component) {
259 pr_err("%s: component is NULL\n", __func__);
260 return -EINVAL;
261 }
262
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530263 ret = msm_pcm_loopback_get_session(rtd, &pcm);
264 if (ret)
265 return ret;
266
267 mutex_lock(&pcm->lock);
268
269 pcm->volume = 0x2000;
270
271 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
272 pcm->playback_substream = substream;
273 else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
274 pcm->capture_substream = substream;
275
276 pcm->instance++;
Meng Wangee084a02018-09-04 16:11:58 +0800277 dev_dbg(component->dev, "%s: pcm out open: %d,%d\n", __func__,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530278 pcm->instance, substream->stream);
279 if (pcm->instance == 2) {
280 struct snd_soc_pcm_runtime *soc_pcm_rx =
281 pcm->playback_substream->private_data;
282 struct snd_soc_pcm_runtime *soc_pcm_tx =
283 pcm->capture_substream->private_data;
284 if (pcm->audio_client != NULL)
285 stop_pcm(pcm);
286
287 pdata = (struct msm_pcm_pdata *)
Meng Wangee084a02018-09-04 16:11:58 +0800288 dev_get_drvdata(component->dev);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530289 if (!pdata) {
Meng Wangee084a02018-09-04 16:11:58 +0800290 dev_err(component->dev,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530291 "%s: platform data not populated\n", __func__);
292 mutex_unlock(&pcm->lock);
293 return -EINVAL;
294 }
295
296 pcm->audio_client = q6asm_audio_client_alloc(
297 (app_cb)msm_pcm_loopback_event_handler, pcm);
298 if (!pcm->audio_client) {
Meng Wangee084a02018-09-04 16:11:58 +0800299 dev_err(component->dev,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530300 "%s: Could not allocate memory\n", __func__);
301 mutex_unlock(&pcm->lock);
302 return -ENOMEM;
303 }
304 pcm->session_id = pcm->audio_client->session;
305 pcm->audio_client->perf_mode = pdata->perf_mode;
306 ret = q6asm_open_loopback_v2(pcm->audio_client,
307 bits_per_sample);
308 if (ret < 0) {
Meng Wangee084a02018-09-04 16:11:58 +0800309 dev_err(component->dev,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530310 "%s: pcm out open failed\n", __func__);
311 q6asm_audio_client_free(pcm->audio_client);
312 mutex_unlock(&pcm->lock);
313 return -ENOMEM;
314 }
315 event.event_func = msm_pcm_route_event_handler;
316 event.priv_data = (void *) pcm;
317 msm_pcm_routing_reg_phy_stream(soc_pcm_tx->dai_link->id,
318 pcm->audio_client->perf_mode,
319 pcm->session_id, pcm->capture_substream->stream);
320 msm_pcm_routing_reg_phy_stream_v2(soc_pcm_rx->dai_link->id,
321 pcm->audio_client->perf_mode,
322 pcm->session_id, pcm->playback_substream->stream,
323 event);
324 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
325 pcm->playback_substream = substream;
326 ret = pcm_loopback_set_volume(pcm, pcm->volume);
327 if (ret < 0)
Meng Wangee084a02018-09-04 16:11:58 +0800328 dev_err(component->dev,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530329 "Error %d setting volume", ret);
330 }
331 /* Set to largest negative value */
332 asm_mtmx_strtr_window.window_lsw = 0x00000000;
333 asm_mtmx_strtr_window.window_msw = 0x80000000;
334 param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_START_V2;
335 q6asm_send_mtmx_strtr_window(pcm->audio_client,
336 &asm_mtmx_strtr_window,
337 param_id);
338 /* Set to largest positive value */
339 asm_mtmx_strtr_window.window_lsw = 0xffffffff;
340 asm_mtmx_strtr_window.window_msw = 0x7fffffff;
341 param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_END_V2;
342 q6asm_send_mtmx_strtr_window(pcm->audio_client,
343 &asm_mtmx_strtr_window,
344 param_id);
345 }
Meng Wangee084a02018-09-04 16:11:58 +0800346 dev_info(component->dev, "%s: Instance = %d, Stream ID = %s\n",
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530347 __func__, pcm->instance, substream->pcm->id);
348 runtime->private_data = pcm;
349
350 mutex_unlock(&pcm->lock);
351
352 return 0;
353}
354
355static void stop_pcm(struct msm_pcm_loopback *pcm)
356{
357 struct snd_soc_pcm_runtime *soc_pcm_rx;
358 struct snd_soc_pcm_runtime *soc_pcm_tx;
359
360 if (pcm->audio_client == NULL)
361 return;
362 q6asm_cmd(pcm->audio_client, CMD_CLOSE);
363
364 if (pcm->playback_substream != NULL) {
365 soc_pcm_rx = pcm->playback_substream->private_data;
366 msm_pcm_routing_dereg_phy_stream(soc_pcm_rx->dai_link->id,
367 SNDRV_PCM_STREAM_PLAYBACK);
368 }
369 if (pcm->capture_substream != NULL) {
370 soc_pcm_tx = pcm->capture_substream->private_data;
371 msm_pcm_routing_dereg_phy_stream(soc_pcm_tx->dai_link->id,
372 SNDRV_PCM_STREAM_CAPTURE);
373 }
374 q6asm_audio_client_free(pcm->audio_client);
375 pcm->audio_client = NULL;
376}
377
378static int msm_pcm_close(struct snd_pcm_substream *substream)
379{
380 struct snd_pcm_runtime *runtime = substream->runtime;
381 struct msm_pcm_loopback *pcm = runtime->private_data;
382 struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
Meng Wangee084a02018-09-04 16:11:58 +0800383 struct snd_soc_component *component =
384 snd_soc_rtdcom_lookup(rtd, DRV_NAME);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530385 int ret = 0, n;
386 bool found = false;
387
Meng Wangee084a02018-09-04 16:11:58 +0800388 if (!component) {
389 pr_err("%s: component is NULL\n", __func__);
390 return -EINVAL;
391 }
392
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530393 mutex_lock(&pcm->lock);
394
Meng Wangee084a02018-09-04 16:11:58 +0800395 dev_dbg(component->dev, "%s: end pcm call:%d\n",
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530396 __func__, substream->stream);
397 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
398 pcm->playback_start = 0;
399 else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
400 pcm->capture_start = 0;
401
402 pcm->instance--;
403 if (!pcm->playback_start || !pcm->capture_start) {
Meng Wangee084a02018-09-04 16:11:58 +0800404 dev_dbg(component->dev, "%s: end pcm call\n", __func__);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530405 stop_pcm(pcm);
406 }
407
408 if (!pcm->instance) {
409 mutex_lock(&loopback_session_lock);
410 for (n = 0; n < LOOPBACK_SESSION_MAX; n++) {
411 if (!strcmp(rtd->dai_link->stream_name,
412 session_map[n].stream_name)) {
413 found = true;
414 break;
415 }
416 }
417 if (found) {
418 memset(session_map[n].stream_name, 0,
419 sizeof(session_map[n].stream_name));
420 mutex_unlock(&pcm->lock);
421 mutex_destroy(&session_map[n].loopback_priv->lock);
422 session_map[n].loopback_priv = NULL;
423 kfree(pcm);
Meng Wangee084a02018-09-04 16:11:58 +0800424 dev_dbg(component->dev, "%s: stream freed %s\n",
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530425 __func__, rtd->dai_link->stream_name);
426 mutex_unlock(&loopback_session_lock);
427 return 0;
428 }
429 mutex_unlock(&loopback_session_lock);
430 }
431 mutex_unlock(&pcm->lock);
432 return ret;
433}
434
435static int msm_pcm_prepare(struct snd_pcm_substream *substream)
436{
437 int ret = 0;
438 struct snd_pcm_runtime *runtime = substream->runtime;
439 struct msm_pcm_loopback *pcm = runtime->private_data;
440 struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
Meng Wangee084a02018-09-04 16:11:58 +0800441 struct snd_soc_component *component =
442 snd_soc_rtdcom_lookup(rtd, DRV_NAME);
443
444 if (!component) {
445 pr_err("%s: component is NULL\n", __func__);
446 return -EINVAL;
447 }
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530448
449 mutex_lock(&pcm->lock);
450
Meng Wangee084a02018-09-04 16:11:58 +0800451 dev_dbg(component->dev, "%s: ASM loopback stream:%d\n",
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530452 __func__, substream->stream);
453 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
454 if (!pcm->playback_start)
455 pcm->playback_start = 1;
456 } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
457 if (!pcm->capture_start)
458 pcm->capture_start = 1;
459 }
460 mutex_unlock(&pcm->lock);
461
462 return ret;
463}
464
465static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
466{
467 struct snd_pcm_runtime *runtime = substream->runtime;
468 struct msm_pcm_loopback *pcm = runtime->private_data;
469 struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
Meng Wangee084a02018-09-04 16:11:58 +0800470 struct snd_soc_component *component =
471 snd_soc_rtdcom_lookup(rtd, DRV_NAME);
472
473 if (!component) {
474 pr_err("%s: component is NULL\n", __func__);
475 return -EINVAL;
476 }
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530477
478 switch (cmd) {
479 case SNDRV_PCM_TRIGGER_START:
480 case SNDRV_PCM_TRIGGER_RESUME:
481 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
Meng Wangee084a02018-09-04 16:11:58 +0800482 dev_dbg(component->dev,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530483 "%s: playback_start:%d,capture_start:%d\n", __func__,
484 pcm->playback_start, pcm->capture_start);
485 if (pcm->playback_start && pcm->capture_start)
486 q6asm_run_nowait(pcm->audio_client, 0, 0, 0);
487 break;
488 case SNDRV_PCM_TRIGGER_SUSPEND:
489 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
490 case SNDRV_PCM_TRIGGER_STOP:
Meng Wangee084a02018-09-04 16:11:58 +0800491 dev_dbg(component->dev,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530492 "%s:Pause/Stop - playback_start:%d,capture_start:%d\n",
493 __func__, pcm->playback_start, pcm->capture_start);
494 if (pcm->playback_start && pcm->capture_start)
495 q6asm_cmd_nowait(pcm->audio_client, CMD_PAUSE);
496 break;
497 default:
498 pr_err("%s: default cmd %d\n", __func__, cmd);
499 break;
500 }
501
502 return 0;
503}
504
505static const struct snd_pcm_ops msm_pcm_ops = {
506 .open = msm_pcm_open,
507 .close = msm_pcm_close,
508 .prepare = msm_pcm_prepare,
509 .trigger = msm_pcm_trigger,
510};
511
512static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol,
513 struct snd_ctl_elem_value *ucontrol)
514{
515 int rc = 0;
516 struct snd_pcm_volume *vol = kcontrol->private_data;
517 struct snd_pcm_substream *substream = vol->pcm->streams[0].substream;
518 struct msm_pcm_loopback *prtd;
519 int volume = ucontrol->value.integer.value[0];
520
521 pr_debug("%s: volume : 0x%x\n", __func__, volume);
522 if ((!substream) || (!substream->runtime)) {
523 pr_err("%s substream or runtime not found\n", __func__);
524 rc = -ENODEV;
525 goto exit;
526 }
527 prtd = substream->runtime->private_data;
528 if (!prtd) {
529 rc = -ENODEV;
530 goto exit;
531 }
532 rc = pcm_loopback_set_volume(prtd, volume);
533
534exit:
535 return rc;
536}
537
538static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol,
539 struct snd_ctl_elem_value *ucontrol)
540{
541 int rc = 0;
542 struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
543 struct snd_pcm_substream *substream =
544 vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
545 struct msm_pcm_loopback *prtd;
546
547 pr_debug("%s\n", __func__);
548 if ((!substream) || (!substream->runtime)) {
Vignesh Kulothungan2ce67842018-09-25 16:40:29 -0700549 pr_debug("%s substream or runtime not found\n", __func__);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530550 rc = -ENODEV;
551 goto exit;
552 }
553 prtd = substream->runtime->private_data;
554 if (!prtd) {
555 rc = -ENODEV;
556 goto exit;
557 }
558 ucontrol->value.integer.value[0] = prtd->volume;
559
560exit:
561 return rc;
562}
563
564static int msm_pcm_add_volume_controls(struct snd_soc_pcm_runtime *rtd)
565{
566 struct snd_pcm *pcm = rtd->pcm->streams[0].pcm;
567 struct snd_pcm_volume *volume_info;
568 struct snd_kcontrol *kctl;
569 int ret = 0;
570
571 dev_dbg(rtd->dev, "%s, Volume cntrl add\n", __func__);
572 ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
573 NULL, 1,
574 rtd->dai_link->id,
575 &volume_info);
576 if (ret < 0)
577 return ret;
578 kctl = volume_info->kctl;
579 kctl->put = msm_pcm_volume_ctl_put;
580 kctl->get = msm_pcm_volume_ctl_get;
581 kctl->tlv.p = loopback_rx_vol_gain;
582 return 0;
583}
584
585static int msm_pcm_playback_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
586 struct snd_ctl_elem_value *ucontrol)
587{
588 u64 fe_id = kcontrol->private_value;
589 int session_type = SESSION_TYPE_RX;
590 int be_id = ucontrol->value.integer.value[3];
591 struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000};
592 int ret = 0;
593
594 cfg_data.app_type = ucontrol->value.integer.value[0];
595 cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
596 if (ucontrol->value.integer.value[2] != 0)
597 cfg_data.sample_rate = ucontrol->value.integer.value[2];
598 pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n",
599 __func__, fe_id, session_type, be_id,
600 cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
601 ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
602 be_id, &cfg_data);
603 if (ret < 0)
604 pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n",
605 __func__, ret);
606
607 return ret;
608}
609
610static int msm_pcm_playback_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
611 struct snd_ctl_elem_value *ucontrol)
612{
613 u64 fe_id = kcontrol->private_value;
614 int session_type = SESSION_TYPE_RX;
615 int be_id = 0;
616 struct msm_pcm_stream_app_type_cfg cfg_data = {0};
617 int ret = 0;
618
619 ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type,
620 &be_id, &cfg_data);
621 if (ret < 0) {
622 pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n",
623 __func__, ret);
624 goto done;
625 }
626
627 ucontrol->value.integer.value[0] = cfg_data.app_type;
628 ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
629 ucontrol->value.integer.value[2] = cfg_data.sample_rate;
630 ucontrol->value.integer.value[3] = be_id;
631 pr_debug("%s: fedai_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);
634done:
635 return ret;
636}
637
638static int msm_pcm_capture_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
639 struct snd_ctl_elem_value *ucontrol)
640{
641 u64 fe_id = kcontrol->private_value;
642 int session_type = SESSION_TYPE_TX;
643 int be_id = ucontrol->value.integer.value[3];
644 struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000};
645 int ret = 0;
646
647 cfg_data.app_type = ucontrol->value.integer.value[0];
648 cfg_data.acdb_dev_id = ucontrol->value.integer.value[1];
649 if (ucontrol->value.integer.value[2] != 0)
650 cfg_data.sample_rate = ucontrol->value.integer.value[2];
651 pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n",
652 __func__, fe_id, session_type, be_id,
653 cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
654 ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type,
655 be_id, &cfg_data);
656 if (ret < 0)
657 pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n",
658 __func__, ret);
659
660 return ret;
661}
662
663static int msm_pcm_capture_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
664 struct snd_ctl_elem_value *ucontrol)
665{
666 u64 fe_id = kcontrol->private_value;
667 int session_type = SESSION_TYPE_TX;
668 int be_id = 0;
669 struct msm_pcm_stream_app_type_cfg cfg_data = {0};
670 int ret = 0;
671
672 ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type,
673 &be_id, &cfg_data);
674 if (ret < 0) {
675 pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n",
676 __func__, ret);
677 goto done;
678 }
679
680 ucontrol->value.integer.value[0] = cfg_data.app_type;
681 ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id;
682 ucontrol->value.integer.value[2] = cfg_data.sample_rate;
683 ucontrol->value.integer.value[3] = be_id;
684 pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
685 __func__, fe_id, session_type, be_id,
686 cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate);
687done:
688 return ret;
689}
690
691static int msm_pcm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd)
692{
693 struct snd_pcm *pcm = rtd->pcm->streams[0].pcm;
694 struct snd_pcm_usr *app_type_info;
695 struct snd_kcontrol *kctl;
696 const char *playback_mixer_ctl_name = "Audio Stream";
697 const char *capture_mixer_ctl_name = "Audio Stream Capture";
698 const char *deviceNo = "NN";
699 const char *suffix = "App Type Cfg";
700 int ctl_len, ret = 0;
701
702 if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
703 ctl_len = strlen(playback_mixer_ctl_name) + 1 +
704 strlen(deviceNo) + 1 + strlen(suffix) + 1;
705 pr_debug("%s: Playback app type cntrl add\n", __func__);
706 ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
707 NULL, 1, ctl_len, rtd->dai_link->id,
708 &app_type_info);
709 if (ret < 0)
710 return ret;
711 kctl = app_type_info->kctl;
712 snprintf(kctl->id.name, ctl_len, "%s %d %s",
713 playback_mixer_ctl_name, rtd->pcm->device, suffix);
714 kctl->put = msm_pcm_playback_app_type_cfg_ctl_put;
715 kctl->get = msm_pcm_playback_app_type_cfg_ctl_get;
716 }
717
718 if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
719 ctl_len = strlen(capture_mixer_ctl_name) + 1 +
720 strlen(deviceNo) + 1 + strlen(suffix) + 1;
721 pr_debug("%s: Capture app type cntrl add\n", __func__);
722 ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE,
723 NULL, 1, ctl_len, rtd->dai_link->id,
724 &app_type_info);
725 if (ret < 0)
726 return ret;
727 kctl = app_type_info->kctl;
728 snprintf(kctl->id.name, ctl_len, "%s %d %s",
729 capture_mixer_ctl_name, rtd->pcm->device, suffix);
730 kctl->put = msm_pcm_capture_app_type_cfg_ctl_put;
731 kctl->get = msm_pcm_capture_app_type_cfg_ctl_get;
732 }
733
734 return 0;
735}
736
737static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
738{
739 int ret = 0;
740
741 pr_debug("%s\n", __func__);
742 ret = msm_pcm_add_volume_controls(rtd);
743 if (ret)
744 pr_err("%s: pcm add volume controls failed:%d\n",
745 __func__, ret);
746 ret = msm_pcm_add_app_type_controls(rtd);
747 if (ret)
748 pr_err("%s: pcm add app type controls failed:%d\n",
749 __func__, ret);
750 return ret;
751}
752
753static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
754{
755 struct snd_card *card = rtd->card->snd_card;
756 int ret = 0;
757
758 if (!card->dev->coherent_dma_mask)
759 card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
760
761 ret = msm_pcm_add_controls(rtd);
762 if (ret)
763 dev_err(rtd->dev, "%s, kctl add failed\n", __func__);
764 return ret;
765}
766
Meng Wangee084a02018-09-04 16:11:58 +0800767static struct snd_soc_component_driver msm_soc_component = {
768 .name = DRV_NAME,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530769 .ops = &msm_pcm_ops,
770 .pcm_new = msm_asoc_pcm_new,
771 .probe = msm_pcm_loopback_probe,
772};
773
774static int msm_pcm_probe(struct platform_device *pdev)
775{
776 struct msm_pcm_pdata *pdata;
777
778 dev_dbg(&pdev->dev, "%s: dev name %s\n",
779 __func__, dev_name(&pdev->dev));
780
781 pdata = kzalloc(sizeof(struct msm_pcm_pdata), GFP_KERNEL);
782 if (!pdata)
783 return -ENOMEM;
784
785 if (of_property_read_bool(pdev->dev.of_node,
786 "qcom,msm-pcm-loopback-low-latency"))
787 pdata->perf_mode = LOW_LATENCY_PCM_MODE;
788 else
789 pdata->perf_mode = LEGACY_PCM_MODE;
790
791 dev_set_drvdata(&pdev->dev, pdata);
792
Meng Wangee084a02018-09-04 16:11:58 +0800793 return snd_soc_register_component(&pdev->dev,
794 &msm_soc_component,
795 NULL, 0);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530796}
797
798static int msm_pcm_remove(struct platform_device *pdev)
799{
Meng Wangee084a02018-09-04 16:11:58 +0800800 snd_soc_unregister_component(&pdev->dev);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530801 return 0;
802}
803
804static const struct of_device_id msm_pcm_loopback_dt_match[] = {
805 {.compatible = "qcom,msm-pcm-loopback"},
806 {}
807};
808
809static struct platform_driver msm_pcm_driver = {
810 .driver = {
811 .name = "msm-pcm-loopback",
812 .owner = THIS_MODULE,
813 .of_match_table = msm_pcm_loopback_dt_match,
814 },
815 .probe = msm_pcm_probe,
816 .remove = msm_pcm_remove,
817};
818
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530819int __init msm_pcm_loopback_init(void)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530820{
821 return platform_driver_register(&msm_pcm_driver);
822}
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530823
Asish Bhattacharya5faacb32017-12-04 17:23:15 +0530824void msm_pcm_loopback_exit(void)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530825{
826 platform_driver_unregister(&msm_pcm_driver);
827}
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530828
829MODULE_DESCRIPTION("PCM loopback platform driver");
830MODULE_LICENSE("GPL v2");