msm-pcm-dtmf-v2.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /* Copyright (c) 2013-2014, 2017-2020 The Linux Foundation. All rights reserved.
  3. */
  4. #include <linux/init.h>
  5. #include <linux/module.h>
  6. #include <linux/time.h>
  7. #include <linux/wait.h>
  8. #include <linux/platform_device.h>
  9. #include <linux/slab.h>
  10. #include <linux/dma-mapping.h>
  11. #include <sound/core.h>
  12. #include <sound/soc.h>
  13. #include <sound/pcm.h>
  14. #include <dsp/q6afe-v2.h>
  15. #include <dsp/q6voice.h>
  16. #include "msm-pcm-q6-v2.h"
  17. #include "msm-pcm-routing-v2.h"
  18. #define DRV_NAME "msm-pcm-dtmf-v2"
  19. #define DTMF_MAX_DURATION 65535
  20. enum {
  21. DTMF_IN_RX,
  22. DTMF_IN_TX,
  23. };
  24. enum format {
  25. FORMAT_S16_LE = 2
  26. };
  27. struct dtmf_det_info {
  28. char session[MAX_SESSION_NAME_LEN];
  29. uint8_t dir;
  30. uint16_t high_freq;
  31. uint16_t low_freq;
  32. };
  33. struct dtmf_buf_node {
  34. struct list_head list;
  35. struct dtmf_det_info dtmf_det_pkt;
  36. };
  37. enum dtmf_state {
  38. DTMF_GEN_RX_STOPPED,
  39. DTMF_GEN_RX_STARTED,
  40. };
  41. #define DTMF_MAX_Q_LEN 10
  42. #define DTMF_PKT_SIZE sizeof(struct dtmf_det_info)
  43. struct dtmf_drv_info {
  44. enum dtmf_state state;
  45. struct snd_pcm_substream *capture_substream;
  46. struct list_head out_queue;
  47. struct list_head free_out_queue;
  48. wait_queue_head_t out_wait;
  49. struct mutex lock;
  50. spinlock_t dsp_lock;
  51. uint8_t capture_start;
  52. uint8_t capture_instance;
  53. unsigned int pcm_capture_size;
  54. unsigned int pcm_capture_count;
  55. unsigned int pcm_capture_irq_pos;
  56. unsigned int pcm_capture_buf_pos;
  57. };
  58. static struct snd_pcm_hardware msm_pcm_hardware = {
  59. .info = (SNDRV_PCM_INFO_MMAP |
  60. SNDRV_PCM_INFO_BLOCK_TRANSFER |
  61. SNDRV_PCM_INFO_MMAP_VALID |
  62. SNDRV_PCM_INFO_INTERLEAVED),
  63. .formats = SNDRV_PCM_FMTBIT_S16_LE,
  64. .channels_min = 1,
  65. .channels_max = 1,
  66. .buffer_bytes_max = (sizeof(struct dtmf_buf_node) * DTMF_MAX_Q_LEN),
  67. .period_bytes_min = DTMF_PKT_SIZE,
  68. .period_bytes_max = DTMF_PKT_SIZE,
  69. .periods_min = DTMF_MAX_Q_LEN,
  70. .periods_max = DTMF_MAX_Q_LEN,
  71. .fifo_size = 0,
  72. };
  73. static int msm_dtmf_rx_generate_put(struct snd_kcontrol *kcontrol,
  74. struct snd_ctl_elem_value *ucontrol)
  75. {
  76. uint16_t low_freq = ucontrol->value.integer.value[0];
  77. uint16_t high_freq = ucontrol->value.integer.value[1];
  78. int64_t duration = ucontrol->value.integer.value[2];
  79. uint16_t gain = ucontrol->value.integer.value[3];
  80. pr_debug("%s: low_freq=%d high_freq=%d duration=%lld gain=%d\n",
  81. __func__, low_freq, high_freq, (int)duration, gain);
  82. if (duration == DTMF_MAX_DURATION)
  83. duration = -1;
  84. afe_dtmf_generate_rx(duration, high_freq, low_freq, gain);
  85. return 0;
  86. }
  87. static int msm_dtmf_rx_generate_get(struct snd_kcontrol *kcontrol,
  88. struct snd_ctl_elem_value *ucontrol)
  89. {
  90. pr_debug("%s:\n", __func__);
  91. ucontrol->value.integer.value[0] = 0;
  92. return 0;
  93. }
  94. static int msm_dtmf_detect_voice_rx_put(struct snd_kcontrol *kcontrol,
  95. struct snd_ctl_elem_value *ucontrol)
  96. {
  97. int enable = ucontrol->value.integer.value[0];
  98. pr_debug("%s: enable=%d\n", __func__, enable);
  99. voc_enable_dtmf_rx_detection(voc_get_session_id(VOICE_SESSION_NAME),
  100. enable);
  101. return 0;
  102. }
  103. static int msm_dtmf_detect_voice_rx_get(struct snd_kcontrol *kcontrol,
  104. struct snd_ctl_elem_value *ucontrol)
  105. {
  106. ucontrol->value.integer.value[0] = 0;
  107. return 0;
  108. }
  109. static int msm_dtmf_detect_volte_rx_put(struct snd_kcontrol *kcontrol,
  110. struct snd_ctl_elem_value *ucontrol)
  111. {
  112. int enable = ucontrol->value.integer.value[0];
  113. pr_debug("%s: enable=%d\n", __func__, enable);
  114. voc_enable_dtmf_rx_detection(voc_get_session_id(VOLTE_SESSION_NAME),
  115. enable);
  116. return 0;
  117. }
  118. static int msm_dtmf_detect_volte_rx_get(struct snd_kcontrol *kcontrol,
  119. struct snd_ctl_elem_value *ucontrol)
  120. {
  121. ucontrol->value.integer.value[0] = 0;
  122. return 0;
  123. }
  124. static struct snd_kcontrol_new msm_dtmf_controls[] = {
  125. SOC_SINGLE_MULTI_EXT("DTMF_Generate Rx Low High Duration Gain",
  126. SND_SOC_NOPM, 0, 5000, 0, 4,
  127. msm_dtmf_rx_generate_get,
  128. msm_dtmf_rx_generate_put),
  129. SOC_SINGLE_EXT("DTMF_Detect Rx Voice enable", SND_SOC_NOPM, 0, 1, 0,
  130. msm_dtmf_detect_voice_rx_get,
  131. msm_dtmf_detect_voice_rx_put),
  132. SOC_SINGLE_EXT("DTMF_Detect Rx VoLTE enable", SND_SOC_NOPM, 0, 1, 0,
  133. msm_dtmf_detect_volte_rx_get,
  134. msm_dtmf_detect_volte_rx_put),
  135. };
  136. static int msm_pcm_dtmf_probe(struct snd_soc_component *component)
  137. {
  138. snd_soc_add_component_controls(component, msm_dtmf_controls,
  139. ARRAY_SIZE(msm_dtmf_controls));
  140. return 0;
  141. }
  142. static void dtmf_rx_detected_cb(uint8_t *pkt,
  143. char *session,
  144. void *private_data)
  145. {
  146. struct dtmf_buf_node *buf_node = NULL;
  147. struct vss_istream_evt_rx_dtmf_detected *dtmf_det_pkt =
  148. (struct vss_istream_evt_rx_dtmf_detected *)pkt;
  149. struct dtmf_drv_info *prtd = private_data;
  150. unsigned long dsp_flags;
  151. pr_debug("%s\n", __func__);
  152. if (prtd->capture_substream == NULL)
  153. return;
  154. /* Copy dtmf detected info into out_queue. */
  155. spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
  156. /* discarding dtmf detection info till start is received */
  157. if (!list_empty(&prtd->free_out_queue) && prtd->capture_start) {
  158. buf_node = list_first_entry(&prtd->free_out_queue,
  159. struct dtmf_buf_node, list);
  160. list_del(&buf_node->list);
  161. buf_node->dtmf_det_pkt.high_freq = dtmf_det_pkt->high_freq;
  162. buf_node->dtmf_det_pkt.low_freq = dtmf_det_pkt->low_freq;
  163. if (session != NULL)
  164. strlcpy(buf_node->dtmf_det_pkt.session,
  165. session, MAX_SESSION_NAME_LEN);
  166. buf_node->dtmf_det_pkt.dir = DTMF_IN_RX;
  167. pr_debug("high =%d, low=%d session=%s\n",
  168. buf_node->dtmf_det_pkt.high_freq,
  169. buf_node->dtmf_det_pkt.low_freq,
  170. buf_node->dtmf_det_pkt.session);
  171. list_add_tail(&buf_node->list, &prtd->out_queue);
  172. prtd->pcm_capture_irq_pos += prtd->pcm_capture_count;
  173. spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
  174. snd_pcm_period_elapsed(prtd->capture_substream);
  175. } else {
  176. spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
  177. pr_err("DTMF detection pkt in Rx dropped, no free node available\n");
  178. }
  179. wake_up(&prtd->out_wait);
  180. }
  181. static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
  182. int channel, unsigned long hwoff,
  183. void __user *buf, unsigned long fbytes)
  184. {
  185. int ret = 0;
  186. struct dtmf_buf_node *buf_node = NULL;
  187. struct snd_pcm_runtime *runtime = substream->runtime;
  188. struct dtmf_drv_info *prtd = runtime->private_data;
  189. unsigned long dsp_flags = 0;
  190. ret = wait_event_interruptible_timeout(prtd->out_wait,
  191. (!list_empty(&prtd->out_queue)),
  192. 1 * HZ);
  193. if (ret > 0) {
  194. if (fbytes <= DTMF_PKT_SIZE) {
  195. spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
  196. buf_node = list_first_entry(&prtd->out_queue,
  197. struct dtmf_buf_node, list);
  198. list_del(&buf_node->list);
  199. spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
  200. ret = copy_to_user(buf,
  201. &buf_node->dtmf_det_pkt,
  202. fbytes);
  203. if (ret) {
  204. pr_err("%s: Copy to user returned %d\n",
  205. __func__, ret);
  206. ret = -EFAULT;
  207. }
  208. spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
  209. list_add_tail(&buf_node->list,
  210. &prtd->free_out_queue);
  211. spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
  212. } else {
  213. pr_err("%s: Read count %lu > DTMF_PKT_SIZE\n",
  214. __func__, fbytes);
  215. ret = -ENOMEM;
  216. }
  217. } else if (ret == 0) {
  218. pr_err("%s: No UL data available\n", __func__);
  219. ret = -ETIMEDOUT;
  220. } else {
  221. pr_err("%s: Read was interrupted\n", __func__);
  222. ret = -ERESTARTSYS;
  223. }
  224. return ret;
  225. }
  226. static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
  227. unsigned long hwoff, void __user *buf, unsigned long fbytes)
  228. {
  229. int ret = 0;
  230. pr_debug("%s() DTMF\n", __func__);
  231. if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
  232. ret = msm_pcm_capture_copy(substream, a, hwoff, buf, fbytes);
  233. return ret;
  234. }
  235. static int msm_pcm_open(struct snd_pcm_substream *substream)
  236. {
  237. struct snd_pcm_runtime *runtime = substream->runtime;
  238. struct dtmf_drv_info *prtd = NULL;
  239. int ret = 0;
  240. if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
  241. prtd = kzalloc(sizeof(struct dtmf_drv_info), GFP_KERNEL);
  242. if (prtd == NULL) {
  243. ret = -ENOMEM;
  244. goto done;
  245. }
  246. mutex_init(&prtd->lock);
  247. spin_lock_init(&prtd->dsp_lock);
  248. init_waitqueue_head(&prtd->out_wait);
  249. INIT_LIST_HEAD(&prtd->out_queue);
  250. INIT_LIST_HEAD(&prtd->free_out_queue);
  251. runtime->hw = msm_pcm_hardware;
  252. ret = snd_pcm_hw_constraint_integer(runtime,
  253. SNDRV_PCM_HW_PARAM_PERIODS);
  254. if (ret < 0)
  255. pr_info("snd_pcm_hw_constraint_integer failed\n");
  256. prtd->capture_substream = substream;
  257. prtd->capture_instance++;
  258. runtime->private_data = prtd;
  259. }
  260. done:
  261. return ret;
  262. }
  263. static int msm_pcm_close(struct snd_pcm_substream *substream)
  264. {
  265. int ret = 0;
  266. struct list_head *ptr = NULL;
  267. struct list_head *next = NULL;
  268. struct dtmf_buf_node *buf_node = NULL;
  269. struct snd_dma_buffer *c_dma_buf;
  270. struct snd_pcm_substream *c_substream;
  271. struct snd_pcm_runtime *runtime = substream->runtime;
  272. struct dtmf_drv_info *prtd = runtime->private_data;
  273. unsigned long dsp_flags = 0;
  274. pr_debug("%s() DTMF\n", __func__);
  275. if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
  276. mutex_lock(&prtd->lock);
  277. wake_up(&prtd->out_wait);
  278. if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
  279. prtd->capture_instance--;
  280. if (!prtd->capture_instance) {
  281. if (prtd->state == DTMF_GEN_RX_STARTED) {
  282. prtd->state = DTMF_GEN_RX_STOPPED;
  283. voc_disable_dtmf_det_on_active_sessions();
  284. voc_register_dtmf_rx_detection_cb(NULL, NULL);
  285. }
  286. /* release all buffer */
  287. /* release out_queue and free_out_queue */
  288. pr_debug("release all buffer\n");
  289. c_substream = prtd->capture_substream;
  290. if (c_substream == NULL) {
  291. pr_debug("c_substream is NULL\n");
  292. mutex_unlock(&prtd->lock);
  293. return -EINVAL;
  294. }
  295. c_dma_buf = &c_substream->dma_buffer;
  296. if (c_dma_buf == NULL) {
  297. pr_debug("c_dma_buf is NULL.\n");
  298. mutex_unlock(&prtd->lock);
  299. return -EINVAL;
  300. }
  301. if (c_dma_buf->area != NULL) {
  302. spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
  303. list_for_each_safe(ptr, next,
  304. &prtd->out_queue) {
  305. buf_node = list_entry(ptr,
  306. struct dtmf_buf_node, list);
  307. list_del(&buf_node->list);
  308. }
  309. list_for_each_safe(ptr, next,
  310. &prtd->free_out_queue) {
  311. buf_node = list_entry(ptr,
  312. struct dtmf_buf_node, list);
  313. list_del(&buf_node->list);
  314. }
  315. spin_unlock_irqrestore(&prtd->dsp_lock,
  316. dsp_flags);
  317. dma_free_coherent(c_substream->pcm->card->dev,
  318. runtime->hw.buffer_bytes_max,
  319. c_dma_buf->area,
  320. c_dma_buf->addr);
  321. c_dma_buf->area = NULL;
  322. }
  323. }
  324. prtd->capture_substream = NULL;
  325. mutex_unlock(&prtd->lock);
  326. }
  327. return ret;
  328. }
  329. static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
  330. struct snd_pcm_hw_params *params)
  331. {
  332. struct snd_pcm_runtime *runtime = substream->runtime;
  333. struct dtmf_drv_info *prtd = runtime->private_data;
  334. struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
  335. struct dtmf_buf_node *buf_node = NULL;
  336. int i = 0, offset = 0;
  337. int ret = 0;
  338. pr_debug("%s: DTMF\n", __func__);
  339. if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
  340. mutex_lock(&prtd->lock);
  341. dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
  342. dma_buf->dev.dev = substream->pcm->card->dev;
  343. dma_buf->private_data = NULL;
  344. dma_buf->area = dma_alloc_coherent(substream->pcm->card->dev,
  345. runtime->hw.buffer_bytes_max,
  346. &dma_buf->addr, GFP_KERNEL);
  347. if (!dma_buf->area) {
  348. pr_err("%s:MSM DTMF dma_alloc failed\n", __func__);
  349. mutex_unlock(&prtd->lock);
  350. return -ENOMEM;
  351. }
  352. dma_buf->bytes = runtime->hw.buffer_bytes_max;
  353. memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max);
  354. for (i = 0; i < DTMF_MAX_Q_LEN; i++) {
  355. pr_debug("node =%d\n", i);
  356. buf_node = (void *) dma_buf->area + offset;
  357. list_add_tail(&buf_node->list,
  358. &prtd->free_out_queue);
  359. offset = offset + sizeof(struct dtmf_buf_node);
  360. }
  361. snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
  362. mutex_unlock(&prtd->lock);
  363. }
  364. return ret;
  365. }
  366. static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
  367. {
  368. struct snd_pcm_runtime *runtime = substream->runtime;
  369. struct dtmf_drv_info *prtd = runtime->private_data;
  370. pr_debug("%s: DTMF\n", __func__);
  371. prtd->pcm_capture_size = snd_pcm_lib_buffer_bytes(substream);
  372. prtd->pcm_capture_count = snd_pcm_lib_period_bytes(substream);
  373. prtd->pcm_capture_irq_pos = 0;
  374. prtd->pcm_capture_buf_pos = 0;
  375. return 0;
  376. }
  377. static int msm_pcm_prepare(struct snd_pcm_substream *substream)
  378. {
  379. struct snd_pcm_runtime *runtime = substream->runtime;
  380. struct dtmf_drv_info *prtd = runtime->private_data;
  381. pr_debug("%s: DTMF\n", __func__);
  382. if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
  383. mutex_lock(&prtd->lock);
  384. msm_pcm_capture_prepare(substream);
  385. if (runtime->format != FORMAT_S16_LE) {
  386. pr_err("format:%u doesn't match %d\n",
  387. (uint32_t)runtime->format, FORMAT_S16_LE);
  388. mutex_unlock(&prtd->lock);
  389. return -EINVAL;
  390. }
  391. if (prtd->capture_instance &&
  392. (prtd->state != DTMF_GEN_RX_STARTED)) {
  393. voc_register_dtmf_rx_detection_cb(dtmf_rx_detected_cb,
  394. prtd);
  395. prtd->state = DTMF_GEN_RX_STARTED;
  396. }
  397. mutex_unlock(&prtd->lock);
  398. }
  399. return 0;
  400. }
  401. static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
  402. {
  403. int ret = 0;
  404. struct snd_pcm_runtime *runtime = substream->runtime;
  405. struct dtmf_drv_info *prtd = runtime->private_data;
  406. switch (cmd) {
  407. case SNDRV_PCM_TRIGGER_START:
  408. case SNDRV_PCM_TRIGGER_RESUME:
  409. pr_debug("%s: Trigger start\n", __func__);
  410. if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
  411. prtd->capture_start = 1;
  412. break;
  413. case SNDRV_PCM_TRIGGER_STOP:
  414. pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
  415. if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
  416. prtd->capture_start = 0;
  417. break;
  418. default:
  419. ret = -EINVAL;
  420. break;
  421. }
  422. return ret;
  423. }
  424. static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
  425. {
  426. snd_pcm_uframes_t ret = 0;
  427. struct snd_pcm_runtime *runtime = substream->runtime;
  428. struct dtmf_drv_info *prtd = runtime->private_data;
  429. if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
  430. if (prtd->pcm_capture_irq_pos >= prtd->pcm_capture_size)
  431. prtd->pcm_capture_irq_pos = 0;
  432. ret = bytes_to_frames(runtime, (prtd->pcm_capture_irq_pos));
  433. }
  434. return ret;
  435. }
  436. static const struct snd_pcm_ops msm_pcm_ops = {
  437. .open = msm_pcm_open,
  438. .copy_user = msm_pcm_copy,
  439. .hw_params = msm_pcm_hw_params,
  440. .close = msm_pcm_close,
  441. .prepare = msm_pcm_prepare,
  442. .trigger = msm_pcm_trigger,
  443. .pointer = msm_pcm_pointer,
  444. };
  445. static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
  446. {
  447. struct snd_card *card = rtd->card->snd_card;
  448. int ret = 0;
  449. if (!card->dev->coherent_dma_mask)
  450. card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
  451. return ret;
  452. }
  453. static struct snd_soc_component_driver msm_soc_component = {
  454. .name = DRV_NAME,
  455. .ops = &msm_pcm_ops,
  456. .pcm_new = msm_asoc_pcm_new,
  457. .probe = msm_pcm_dtmf_probe,
  458. };
  459. static int msm_pcm_probe(struct platform_device *pdev)
  460. {
  461. pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
  462. return snd_soc_register_component(&pdev->dev,
  463. &msm_soc_component, NULL, 0);
  464. }
  465. static int msm_pcm_remove(struct platform_device *pdev)
  466. {
  467. snd_soc_unregister_component(&pdev->dev);
  468. return 0;
  469. }
  470. static const struct of_device_id msm_pcm_dtmf_dt_match[] = {
  471. {.compatible = "qcom,msm-pcm-dtmf"},
  472. {}
  473. };
  474. MODULE_DEVICE_TABLE(of, msm_pcm_dtmf_dt_match);
  475. static struct platform_driver msm_pcm_driver = {
  476. .driver = {
  477. .name = "msm-pcm-dtmf",
  478. .owner = THIS_MODULE,
  479. .of_match_table = msm_pcm_dtmf_dt_match,
  480. .suppress_bind_attrs = true,
  481. },
  482. .probe = msm_pcm_probe,
  483. .remove = msm_pcm_remove,
  484. };
  485. int __init msm_pcm_dtmf_init(void)
  486. {
  487. return platform_driver_register(&msm_pcm_driver);
  488. }
  489. void msm_pcm_dtmf_exit(void)
  490. {
  491. platform_driver_unregister(&msm_pcm_driver);
  492. }
  493. MODULE_DESCRIPTION("DTMF platform driver");
  494. MODULE_LICENSE("GPL v2");