msm-pcm-loopback-v2.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. /* Copyright (c) 2013, The Linux Foundation. All rights reserved.
  2. * This program is free software; you can redistribute it and/or modify
  3. * it under the terms of the GNU General Public License version 2 and
  4. * only version 2 as published by the Free Software Foundation.
  5. * This program is distributed in the hope that it will be useful,
  6. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  7. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8. * GNU General Public License for more details.
  9. */
  10. #include <linux/init.h>
  11. #include <linux/err.h>
  12. #include <linux/module.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/slab.h>
  15. #include <linux/dma-mapping.h>
  16. #include <sound/apr_audio-v2.h>
  17. #include <sound/core.h>
  18. #include <sound/soc.h>
  19. #include <sound/q6asm-v2.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 "msm-pcm-routing-v2.h"
  26. #define LOOPBACK_VOL_MAX_STEPS 0x2000
  27. static const DECLARE_TLV_DB_LINEAR(loopback_rx_vol_gain, 0,
  28. LOOPBACK_VOL_MAX_STEPS);
  29. struct msm_pcm_loopback {
  30. struct snd_pcm_substream *playback_substream;
  31. struct snd_pcm_substream *capture_substream;
  32. int instance;
  33. struct mutex lock;
  34. uint32_t samp_rate;
  35. uint32_t channel_mode;
  36. int playback_start;
  37. int capture_start;
  38. int session_id;
  39. struct audio_client *audio_client;
  40. int volume;
  41. };
  42. static void stop_pcm(struct msm_pcm_loopback *pcm);
  43. static const struct snd_pcm_hardware dummy_pcm_hardware = {
  44. .formats = 0xffffffff,
  45. .channels_min = 1,
  46. .channels_max = UINT_MAX,
  47. /* Random values to keep userspace happy when checking constraints */
  48. .info = SNDRV_PCM_INFO_INTERLEAVED |
  49. SNDRV_PCM_INFO_BLOCK_TRANSFER,
  50. .buffer_bytes_max = 128*1024,
  51. .period_bytes_min = 1024,
  52. .period_bytes_max = 1024*2,
  53. .periods_min = 2,
  54. .periods_max = 128,
  55. };
  56. static void msm_pcm_route_event_handler(enum msm_pcm_routing_event event,
  57. void *priv_data)
  58. {
  59. struct msm_pcm_loopback *pcm = priv_data;
  60. BUG_ON(!pcm);
  61. pr_debug("%s: event %x\n", __func__, event);
  62. switch (event) {
  63. case MSM_PCM_RT_EVT_DEVSWITCH:
  64. q6asm_cmd(pcm->audio_client, CMD_PAUSE);
  65. q6asm_cmd(pcm->audio_client, CMD_FLUSH);
  66. q6asm_run(pcm->audio_client, 0, 0, 0);
  67. default:
  68. break;
  69. }
  70. }
  71. static void msm_pcm_loopback_event_handler(uint32_t opcode, uint32_t token,
  72. uint32_t *payload, void *priv)
  73. {
  74. pr_debug("%s\n", __func__);
  75. switch (opcode) {
  76. case APR_BASIC_RSP_RESULT: {
  77. switch (payload[0]) {
  78. break;
  79. default:
  80. break;
  81. }
  82. }
  83. break;
  84. default:
  85. pr_err("Not Supported Event opcode[0x%x]\n", opcode);
  86. break;
  87. }
  88. }
  89. static int pcm_loopback_set_volume(struct msm_pcm_loopback *prtd, int volume)
  90. {
  91. int rc = -EINVAL;
  92. pr_debug("%s Setting volume 0x%x\n", __func__, volume);
  93. if (prtd && prtd->audio_client) {
  94. rc = q6asm_set_volume(prtd->audio_client, volume);
  95. if (rc < 0) {
  96. pr_err("%s: Send Volume command failed rc = %d\n",
  97. __func__, rc);
  98. return rc;
  99. }
  100. prtd->volume = volume;
  101. }
  102. return rc;
  103. }
  104. static int msm_pcm_open(struct snd_pcm_substream *substream)
  105. {
  106. struct snd_pcm_runtime *runtime = substream->runtime;
  107. struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
  108. struct msm_pcm_loopback *pcm;
  109. int ret = 0;
  110. uint16_t bits_per_sample = 16;
  111. struct msm_pcm_routing_evt event;
  112. pcm = dev_get_drvdata(rtd->platform->dev);
  113. mutex_lock(&pcm->lock);
  114. snd_soc_set_runtime_hwparams(substream, &dummy_pcm_hardware);
  115. pcm->volume = 0x2000;
  116. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  117. pcm->playback_substream = substream;
  118. else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
  119. pcm->capture_substream = substream;
  120. pcm->instance++;
  121. dev_dbg(rtd->platform->dev, "%s: pcm out open: %d,%d\n", __func__,
  122. pcm->instance, substream->stream);
  123. if (pcm->instance == 2) {
  124. struct snd_soc_pcm_runtime *soc_pcm_rx =
  125. pcm->playback_substream->private_data;
  126. struct snd_soc_pcm_runtime *soc_pcm_tx =
  127. pcm->capture_substream->private_data;
  128. if (pcm->audio_client != NULL)
  129. stop_pcm(pcm);
  130. pcm->audio_client = q6asm_audio_client_alloc(
  131. (app_cb)msm_pcm_loopback_event_handler, pcm);
  132. if (!pcm->audio_client) {
  133. dev_err(rtd->platform->dev,
  134. "%s: Could not allocate memory\n", __func__);
  135. mutex_unlock(&pcm->lock);
  136. return -ENOMEM;
  137. }
  138. pcm->session_id = pcm->audio_client->session;
  139. pcm->audio_client->perf_mode = false;
  140. ret = q6asm_open_loopback_v2(pcm->audio_client,
  141. bits_per_sample);
  142. if (ret < 0) {
  143. dev_err(rtd->platform->dev,
  144. "%s: pcm out open failed\n", __func__);
  145. q6asm_audio_client_free(pcm->audio_client);
  146. mutex_unlock(&pcm->lock);
  147. return -ENOMEM;
  148. }
  149. event.event_func = msm_pcm_route_event_handler;
  150. event.priv_data = (void *) pcm;
  151. msm_pcm_routing_reg_phy_stream(soc_pcm_tx->dai_link->be_id,
  152. pcm->audio_client->perf_mode,
  153. pcm->session_id, pcm->capture_substream->stream);
  154. msm_pcm_routing_reg_phy_stream_v2(soc_pcm_rx->dai_link->be_id,
  155. pcm->audio_client->perf_mode,
  156. pcm->session_id, pcm->playback_substream->stream,
  157. event);
  158. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  159. pcm->playback_substream = substream;
  160. ret = pcm_loopback_set_volume(pcm, pcm->volume);
  161. if (ret < 0)
  162. dev_err(rtd->platform->dev,
  163. "Error %d setting volume", ret);
  164. }
  165. }
  166. dev_info(rtd->platform->dev, "%s: Instance = %d, Stream ID = %s\n",
  167. __func__ , pcm->instance, substream->pcm->id);
  168. runtime->private_data = pcm;
  169. mutex_unlock(&pcm->lock);
  170. return 0;
  171. }
  172. static void stop_pcm(struct msm_pcm_loopback *pcm)
  173. {
  174. struct snd_soc_pcm_runtime *soc_pcm_rx;
  175. struct snd_soc_pcm_runtime *soc_pcm_tx;
  176. if (pcm->audio_client == NULL)
  177. return;
  178. q6asm_cmd(pcm->audio_client, CMD_CLOSE);
  179. if (pcm->playback_substream != NULL) {
  180. soc_pcm_rx = pcm->playback_substream->private_data;
  181. msm_pcm_routing_dereg_phy_stream(soc_pcm_rx->dai_link->be_id,
  182. SNDRV_PCM_STREAM_PLAYBACK);
  183. }
  184. if (pcm->capture_substream != NULL) {
  185. soc_pcm_tx = pcm->capture_substream->private_data;
  186. msm_pcm_routing_dereg_phy_stream(soc_pcm_tx->dai_link->be_id,
  187. SNDRV_PCM_STREAM_CAPTURE);
  188. }
  189. q6asm_audio_client_free(pcm->audio_client);
  190. pcm->audio_client = NULL;
  191. }
  192. static int msm_pcm_close(struct snd_pcm_substream *substream)
  193. {
  194. struct snd_pcm_runtime *runtime = substream->runtime;
  195. struct msm_pcm_loopback *pcm = runtime->private_data;
  196. struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
  197. int ret = 0;
  198. mutex_lock(&pcm->lock);
  199. dev_dbg(rtd->platform->dev, "%s: end pcm call:%d\n",
  200. __func__, substream->stream);
  201. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  202. pcm->playback_start = 0;
  203. else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
  204. pcm->capture_start = 0;
  205. pcm->instance--;
  206. if (!pcm->playback_start || !pcm->capture_start) {
  207. dev_dbg(rtd->platform->dev, "%s: end pcm call\n", __func__);
  208. stop_pcm(pcm);
  209. }
  210. mutex_unlock(&pcm->lock);
  211. return ret;
  212. }
  213. static int msm_pcm_prepare(struct snd_pcm_substream *substream)
  214. {
  215. int ret = 0;
  216. struct snd_pcm_runtime *runtime = substream->runtime;
  217. struct msm_pcm_loopback *pcm = runtime->private_data;
  218. struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
  219. mutex_lock(&pcm->lock);
  220. dev_dbg(rtd->platform->dev, "%s: ASM loopback stream:%d\n",
  221. __func__, substream->stream);
  222. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  223. if (!pcm->playback_start)
  224. pcm->playback_start = 1;
  225. } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
  226. if (!pcm->capture_start)
  227. pcm->capture_start = 1;
  228. }
  229. mutex_unlock(&pcm->lock);
  230. return ret;
  231. }
  232. static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
  233. {
  234. struct snd_pcm_runtime *runtime = substream->runtime;
  235. struct msm_pcm_loopback *pcm = runtime->private_data;
  236. struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
  237. switch (cmd) {
  238. case SNDRV_PCM_TRIGGER_START:
  239. case SNDRV_PCM_TRIGGER_RESUME:
  240. case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  241. dev_dbg(rtd->platform->dev,
  242. "%s: playback_start:%d,capture_start:%d\n", __func__,
  243. pcm->playback_start, pcm->capture_start);
  244. if (pcm->playback_start && pcm->capture_start)
  245. q6asm_run_nowait(pcm->audio_client, 0, 0, 0);
  246. break;
  247. case SNDRV_PCM_TRIGGER_SUSPEND:
  248. case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  249. case SNDRV_PCM_TRIGGER_STOP:
  250. dev_dbg(rtd->platform->dev,
  251. "%s:Pause/Stop - playback_start:%d,capture_start:%d\n",
  252. __func__, pcm->playback_start, pcm->capture_start);
  253. if (pcm->playback_start && pcm->capture_start)
  254. q6asm_cmd_nowait(pcm->audio_client, CMD_PAUSE);
  255. break;
  256. default:
  257. break;
  258. }
  259. return 0;
  260. }
  261. static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
  262. struct snd_pcm_hw_params *params)
  263. {
  264. struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
  265. dev_dbg(rtd->platform->dev, "%s: ASM loopback\n", __func__);
  266. return snd_pcm_lib_alloc_vmalloc_buffer(substream,
  267. params_buffer_bytes(params));
  268. }
  269. static int msm_pcm_hw_free(struct snd_pcm_substream *substream)
  270. {
  271. return snd_pcm_lib_free_vmalloc_buffer(substream);
  272. }
  273. static struct snd_pcm_ops msm_pcm_ops = {
  274. .open = msm_pcm_open,
  275. .hw_params = msm_pcm_hw_params,
  276. .hw_free = msm_pcm_hw_free,
  277. .close = msm_pcm_close,
  278. .prepare = msm_pcm_prepare,
  279. .trigger = msm_pcm_trigger,
  280. };
  281. static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol,
  282. struct snd_ctl_elem_value *ucontrol)
  283. {
  284. int rc = 0;
  285. struct snd_pcm_volume *vol = kcontrol->private_data;
  286. struct snd_pcm_substream *substream = vol->pcm->streams[0].substream;
  287. struct msm_pcm_loopback *prtd = substream->runtime->private_data;
  288. int volume = ucontrol->value.integer.value[0];
  289. rc = pcm_loopback_set_volume(prtd, volume);
  290. return rc;
  291. }
  292. static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
  293. {
  294. struct snd_pcm *pcm = rtd->pcm->streams[0].pcm;
  295. struct snd_pcm_volume *volume_info;
  296. struct snd_kcontrol *kctl;
  297. int ret = 0;
  298. dev_dbg(rtd->dev, "%s, Volume cntrl add\n", __func__);
  299. ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
  300. NULL, 1,
  301. rtd->dai_link->be_id,
  302. &volume_info);
  303. if (ret < 0)
  304. return ret;
  305. kctl = volume_info->kctl;
  306. kctl->put = msm_pcm_volume_ctl_put;
  307. kctl->tlv.p = loopback_rx_vol_gain;
  308. return 0;
  309. }
  310. static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
  311. {
  312. struct snd_card *card = rtd->card->snd_card;
  313. int ret = 0;
  314. if (!card->dev->coherent_dma_mask)
  315. card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
  316. ret = msm_pcm_add_controls(rtd);
  317. if (ret)
  318. dev_err(rtd->dev, "%s, kctl add failed\n", __func__);
  319. return ret;
  320. }
  321. static struct snd_soc_platform_driver msm_soc_platform = {
  322. .ops = &msm_pcm_ops,
  323. .pcm_new = msm_asoc_pcm_new,
  324. };
  325. static __devinit int msm_pcm_probe(struct platform_device *pdev)
  326. {
  327. struct msm_pcm_loopback *pcm;
  328. dev_set_name(&pdev->dev, "%s", "msm-pcm-loopback");
  329. dev_dbg(&pdev->dev, "%s: dev name %s\n",
  330. __func__, dev_name(&pdev->dev));
  331. pcm = devm_kzalloc(&pdev->dev, sizeof(struct msm_pcm_loopback), GFP_KERNEL);
  332. if (!pcm) {
  333. dev_err(&pdev->dev, "%s Failed to allocate memory for pcm\n",
  334. __func__);
  335. return -ENOMEM;
  336. } else {
  337. mutex_init(&pcm->lock);
  338. dev_set_drvdata(&pdev->dev, pcm);
  339. }
  340. return snd_soc_register_platform(&pdev->dev,
  341. &msm_soc_platform);
  342. }
  343. static int msm_pcm_remove(struct platform_device *pdev)
  344. {
  345. struct msm_pcm_loopback *pcm;
  346. pcm = dev_get_drvdata(&pdev->dev);
  347. mutex_destroy(&pcm->lock);
  348. kfree(pcm);
  349. snd_soc_unregister_platform(&pdev->dev);
  350. return 0;
  351. }
  352. static const struct of_device_id msm_pcm_loopback_dt_match[] = {
  353. {.compatible = "qti,msm-pcm-loopback"},
  354. {}
  355. };
  356. static struct platform_driver msm_pcm_driver = {
  357. .driver = {
  358. .name = "msm-pcm-loopback",
  359. .owner = THIS_MODULE,
  360. .of_match_table = msm_pcm_loopback_dt_match,
  361. },
  362. .probe = msm_pcm_probe,
  363. .remove = __devexit_p(msm_pcm_remove),
  364. };
  365. static int __init msm_soc_platform_init(void)
  366. {
  367. return platform_driver_register(&msm_pcm_driver);
  368. }
  369. module_init(msm_soc_platform_init);
  370. static void __exit msm_soc_platform_exit(void)
  371. {
  372. platform_driver_unregister(&msm_pcm_driver);
  373. }
  374. module_exit(msm_soc_platform_exit);
  375. MODULE_DESCRIPTION("PCM loopback platform driver");
  376. MODULE_LICENSE("GPL v2");