msm-pcm-loopback-v2.c 22 KB

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