voice_mhi.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2019, The Linux Foundation. All rights reserved.
  4. */
  5. #include <linux/dma-mapping.h>
  6. #include <linux/platform_device.h>
  7. #include <linux/of.h>
  8. #include <linux/of_address.h>
  9. #include <linux/list.h>
  10. #include <linux/slab.h>
  11. #include <linux/module.h>
  12. #include <linux/mhi.h>
  13. #include <linux/mutex.h>
  14. #include <dsp/voice_mhi.h>
  15. #include <dsp/msm_audio_ion.h>
  16. #include <dsp/audio_notifier.h>
  17. #include <dsp/q6core.h>
  18. #include <dsp/audio_notifier.h>
  19. #include <ipc/apr.h>
  20. #include "adsp_err.h"
  21. #define VSS_IPKTEXG_CMD_SET_MAILBOX_MEMORY_CONFIG 0x0001333B
  22. #define VOICE_MHI_STATE_SET(a, b) ((a) |= (1UL<<(b)))
  23. #define VOICE_MHI_STATE_RESET(a, b) ((a) &= ~(1UL<<(b)))
  24. #define VOICE_MHI_STATE_CHECK(a, b) (1UL & (a >> b))
  25. #define CMD_STATUS_SUCCESS 0
  26. #define CMD_STATUS_FAIL 1
  27. #define TIMEOUT_MS 500
  28. #define PORT_NUM 0x01
  29. #define PORT_MASK 0x03
  30. #define CONVERT_PORT_APR(x, y) (x << 8 | y)
  31. enum voice_states {
  32. VOICE_MHI_INIT = 0,
  33. VOICE_MHI_PROBED = VOICE_MHI_INIT,
  34. VOICE_MHI_ADSP_UP,
  35. VOICE_MHI_SDX_UP,
  36. VOICE_MHI_INCALL
  37. };
  38. struct voice_mhi_addr {
  39. dma_addr_t base;
  40. uint32_t size;
  41. };
  42. struct voice_mhi_dev_info {
  43. struct platform_device *pdev;
  44. struct voice_mhi_addr phys_addr;
  45. struct voice_mhi_addr iova_pcie;
  46. struct voice_mhi_addr iova_adsp;
  47. };
  48. struct voice_mhi {
  49. struct voice_mhi_dev_info dev_info;
  50. struct mhi_device *mhi_dev;
  51. uint32_t vote_count;
  52. struct mutex mutex;
  53. enum voice_states voice_mhi_state;
  54. bool vote_enable;
  55. bool pcie_enabled;
  56. void *apr_mvm_handle;
  57. struct work_struct voice_mhi_work_pcie;
  58. struct work_struct voice_mhi_work_adsp;
  59. wait_queue_head_t voice_mhi_wait;
  60. u32 mvm_state;
  61. u32 async_err;
  62. };
  63. struct vss_ipktexg_cmd_set_mailbox_memory_config_t {
  64. struct apr_hdr hdr;
  65. uint64_t mailbox_mem_address_adsp;
  66. /*
  67. * IOVA of mailbox memory for ADSP access
  68. */
  69. uint64_t mailbox_mem_address_pcie;
  70. /*
  71. * IOVA of mailbox memory for PCIe access
  72. */
  73. uint32_t mem_size;
  74. /*
  75. * Size of mailbox memory allocated
  76. */
  77. } __packed;
  78. static struct voice_mhi voice_mhi_lcl;
  79. static int voice_mhi_pcie_up_callback(struct mhi_device *,
  80. const struct mhi_device_id *);
  81. static void voice_mhi_pcie_down_callback(struct mhi_device *);
  82. static void voice_mhi_pcie_status_callback(struct mhi_device *, enum MHI_CB);
  83. static int32_t voice_mhi_apr_callback(struct apr_client_data *data, void *priv);
  84. static int voice_mhi_notifier_service_cb(struct notifier_block *nb,
  85. unsigned long opcode, void *ptr);
  86. static int voice_mhi_apr_register(void);
  87. static struct notifier_block voice_mhi_service_nb = {
  88. .notifier_call = voice_mhi_notifier_service_cb,
  89. .priority = -INT_MAX,
  90. };
  91. static const struct mhi_device_id voice_mhi_match_table[] = {
  92. { .chan = "AUDIO_VOICE_0", .driver_data = 0 },
  93. {},
  94. };
  95. static struct mhi_driver voice_mhi_driver = {
  96. .id_table = voice_mhi_match_table,
  97. .probe = voice_mhi_pcie_up_callback,
  98. .remove = voice_mhi_pcie_down_callback,
  99. .status_cb = voice_mhi_pcie_status_callback,
  100. .driver = {
  101. .name = "voice_mhi_audio",
  102. .owner = THIS_MODULE,
  103. },
  104. };
  105. static int voice_mhi_notifier_service_cb(struct notifier_block *nb,
  106. unsigned long opcode, void *ptr)
  107. {
  108. pr_debug("%s: opcode 0x%lx\n", __func__, opcode);
  109. switch (opcode) {
  110. case AUDIO_NOTIFIER_SERVICE_DOWN:
  111. if (voice_mhi_lcl.apr_mvm_handle) {
  112. apr_reset(voice_mhi_lcl.apr_mvm_handle);
  113. voice_mhi_lcl.apr_mvm_handle = NULL;
  114. VOICE_MHI_STATE_RESET(voice_mhi_lcl.voice_mhi_state,
  115. VOICE_MHI_ADSP_UP);
  116. }
  117. break;
  118. case AUDIO_NOTIFIER_SERVICE_UP:
  119. if (!VOICE_MHI_STATE_CHECK(voice_mhi_lcl.voice_mhi_state,
  120. VOICE_MHI_ADSP_UP)) {
  121. VOICE_MHI_STATE_SET(voice_mhi_lcl.voice_mhi_state,
  122. VOICE_MHI_ADSP_UP);
  123. schedule_work(&voice_mhi_lcl.voice_mhi_work_adsp);
  124. }
  125. break;
  126. default:
  127. break;
  128. }
  129. return NOTIFY_OK;
  130. }
  131. static int32_t voice_mhi_apr_callback(struct apr_client_data *data, void *priv)
  132. {
  133. uint32_t *ptr1;
  134. if (data == NULL) {
  135. pr_err("%s: data is NULL\n", __func__);
  136. return -EINVAL;
  137. }
  138. pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__,
  139. data->payload_size, data->opcode);
  140. switch (data->opcode) {
  141. case APR_BASIC_RSP_RESULT:
  142. if (data->payload_size < 2 * sizeof(uint32_t)) {
  143. pr_err("%s: APR_BASIC_RSP_RESULT payload less than expected\n",
  144. __func__);
  145. return 0;
  146. }
  147. ptr1 = data->payload;
  148. switch (ptr1[0]) {
  149. case VSS_IPKTEXG_CMD_SET_MAILBOX_MEMORY_CONFIG:
  150. pr_debug("%s: cmd VSS_IPKTEXG_CMD_SET_MAILBOX_MEMORY_CONFIG\n",
  151. __func__);
  152. voice_mhi_lcl.mvm_state = CMD_STATUS_SUCCESS;
  153. voice_mhi_lcl.async_err = ptr1[1];
  154. wake_up(&voice_mhi_lcl.voice_mhi_wait);
  155. break;
  156. default:
  157. pr_err("%s: Invalid cmd response 0x%x 0x%x\n", __func__,
  158. ptr1[0], ptr1[1]);
  159. break;
  160. }
  161. break;
  162. case APR_RSP_ACCEPTED:
  163. if (data->payload_size < sizeof(uint32_t)) {
  164. pr_err("%s: APR_RSP_ACCEPTED payload less than expected\n",
  165. __func__);
  166. return 0;
  167. }
  168. ptr1 = data->payload;
  169. if (ptr1[0])
  170. pr_debug("%s: APR_RSP_ACCEPTED for 0x%x:\n",
  171. __func__, ptr1[0]);
  172. break;
  173. case RESET_EVENTS:
  174. /* Should we handle here or audio notifier down? */
  175. if (voice_mhi_lcl.apr_mvm_handle) {
  176. apr_reset(voice_mhi_lcl.apr_mvm_handle);
  177. voice_mhi_lcl.apr_mvm_handle = NULL;
  178. VOICE_MHI_STATE_RESET(voice_mhi_lcl.voice_mhi_state,
  179. VOICE_MHI_ADSP_UP);
  180. }
  181. break;
  182. default:
  183. pr_err("%s: Invalid opcode %d\n", __func__,
  184. data->opcode);
  185. break;
  186. }
  187. return 0;
  188. }
  189. /**
  190. * voice_mhi_start -
  191. * Start vote for MHI/PCIe clock
  192. *
  193. * Returns 0 on success or error on failure
  194. */
  195. int voice_mhi_start(void)
  196. {
  197. int ret = 0;
  198. mutex_lock(&voice_mhi_lcl.mutex);
  199. if (voice_mhi_lcl.pcie_enabled) {
  200. if (!voice_mhi_lcl.mhi_dev) {
  201. pr_err("%s: NULL device found\n", __func__);
  202. ret = -EINVAL;
  203. goto done;
  204. }
  205. if (voice_mhi_lcl.vote_count == 0) {
  206. ret = mhi_device_get_sync(voice_mhi_lcl.mhi_dev,
  207. MHI_VOTE_DEVICE);
  208. if (ret) {
  209. pr_err("%s: mhi_device_get_sync failed\n",
  210. __func__);
  211. ret = -EINVAL;
  212. goto done;
  213. }
  214. pr_debug("%s: mhi_device_get_sync success\n", __func__);
  215. } else {
  216. /* For DSDA, no additional voting is needed */
  217. pr_debug("%s: mhi is already voted\n", __func__);
  218. }
  219. voice_mhi_lcl.vote_count++;
  220. } else {
  221. /* PCIe not supported - return success*/
  222. goto done;
  223. }
  224. done:
  225. mutex_unlock(&voice_mhi_lcl.mutex);
  226. return ret;
  227. }
  228. EXPORT_SYMBOL(voice_mhi_start);
  229. /**
  230. * voice_mhi_end -
  231. * End vote for MHI/PCIe clock
  232. *
  233. * Returns 0 on success or error on failure
  234. */
  235. int voice_mhi_end(void)
  236. {
  237. mutex_lock(&voice_mhi_lcl.mutex);
  238. if (voice_mhi_lcl.pcie_enabled) {
  239. if (!voice_mhi_lcl.mhi_dev || voice_mhi_lcl.vote_count == 0) {
  240. pr_err("%s: NULL device found\n", __func__);
  241. mutex_unlock(&voice_mhi_lcl.mutex);
  242. return -EINVAL;
  243. }
  244. if (voice_mhi_lcl.vote_count == 1)
  245. mhi_device_put(voice_mhi_lcl.mhi_dev, MHI_VOTE_DEVICE);
  246. voice_mhi_lcl.vote_count--;
  247. }
  248. mutex_unlock(&voice_mhi_lcl.mutex);
  249. return 0;
  250. }
  251. EXPORT_SYMBOL(voice_mhi_end);
  252. static int voice_mhi_set_mailbox_memory_config(void)
  253. {
  254. struct vss_ipktexg_cmd_set_mailbox_memory_config_t mb_memory_config;
  255. int ret = 0;
  256. void *apr_mvm;
  257. if (!voice_mhi_lcl.apr_mvm_handle) {
  258. pr_err("%s: APR handle is NULL\n", __func__);
  259. return -EINVAL;
  260. }
  261. memset(&mb_memory_config, 0, sizeof(mb_memory_config));
  262. mb_memory_config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
  263. APR_HDR_LEN(APR_HDR_SIZE),
  264. APR_PKT_VER);
  265. mb_memory_config.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
  266. sizeof(mb_memory_config) - APR_HDR_SIZE);
  267. pr_debug("%s: pkt size = %d\n", __func__,
  268. mb_memory_config.hdr.pkt_size);
  269. mutex_lock(&voice_mhi_lcl.mutex);
  270. apr_mvm = voice_mhi_lcl.apr_mvm_handle;
  271. /*
  272. * Handle can be NULL as it is not tied to any session
  273. */
  274. mb_memory_config.hdr.src_port = CONVERT_PORT_APR(PORT_NUM, PORT_MASK);
  275. mb_memory_config.hdr.dest_port = 0;
  276. mb_memory_config.hdr.token = 0;
  277. mb_memory_config.hdr.opcode = VSS_IPKTEXG_CMD_SET_MAILBOX_MEMORY_CONFIG;
  278. mb_memory_config.mailbox_mem_address_pcie =
  279. voice_mhi_lcl.dev_info.iova_pcie.base;
  280. mb_memory_config.mailbox_mem_address_adsp =
  281. voice_mhi_lcl.dev_info.iova_adsp.base;
  282. mb_memory_config.mem_size = voice_mhi_lcl.dev_info.iova_adsp.size;
  283. voice_mhi_lcl.mvm_state = CMD_STATUS_FAIL;
  284. voice_mhi_lcl.async_err = 0;
  285. ret = apr_send_pkt(apr_mvm, (uint32_t *) &mb_memory_config);
  286. if (ret < 0) {
  287. pr_err("%s: Set mailbox memory config failed ret = %d\n",
  288. __func__, ret);
  289. goto unlock;
  290. }
  291. ret = wait_event_timeout(voice_mhi_lcl.voice_mhi_wait,
  292. (voice_mhi_lcl.mvm_state ==
  293. CMD_STATUS_SUCCESS),
  294. msecs_to_jiffies(TIMEOUT_MS));
  295. if (!ret) {
  296. pr_err("%s: wait_event timeout\n", __func__);
  297. ret = -ETIME;
  298. goto unlock;
  299. }
  300. if (voice_mhi_lcl.async_err > 0) {
  301. pr_err("%s: DSP returned error[%d]\n",
  302. __func__, voice_mhi_lcl.async_err);
  303. ret = voice_mhi_lcl.async_err;
  304. goto unlock;
  305. }
  306. ret = 0;
  307. unlock:
  308. mutex_unlock(&voice_mhi_lcl.mutex);
  309. return ret;
  310. }
  311. static void voice_mhi_map_pcie_and_send(struct work_struct *work)
  312. {
  313. dma_addr_t iova, phys_addr;
  314. uint32_t mem_size;
  315. struct device *md;
  316. mutex_lock(&voice_mhi_lcl.mutex);
  317. if (voice_mhi_lcl.mhi_dev) {
  318. md = &voice_mhi_lcl.mhi_dev->dev;
  319. } else {
  320. pr_err("%s: MHI device handle is NULL\n", __func__);
  321. goto err;
  322. }
  323. phys_addr = voice_mhi_lcl.dev_info.phys_addr.base;
  324. mem_size = voice_mhi_lcl.dev_info.iova_pcie.size;
  325. if (md) {
  326. iova = dma_map_resource(md->parent, phys_addr, mem_size,
  327. DMA_BIDIRECTIONAL, 0);
  328. if (dma_mapping_error(md->parent, iova)) {
  329. pr_err("%s: dma_mapping_error\n", __func__);
  330. goto err;
  331. }
  332. pr_debug("%s: dma_mapping_success iova:0x%lx\n",
  333. __func__, (unsigned long)iova);
  334. voice_mhi_lcl.dev_info.iova_pcie.base = iova;
  335. if (q6core_is_adsp_ready()) {
  336. if (VOICE_MHI_STATE_CHECK(voice_mhi_lcl.voice_mhi_state,
  337. VOICE_MHI_SDX_UP)) {
  338. mutex_unlock(&voice_mhi_lcl.mutex);
  339. voice_mhi_set_mailbox_memory_config();
  340. return;
  341. }
  342. }
  343. }
  344. err:
  345. mutex_unlock(&voice_mhi_lcl.mutex);
  346. }
  347. static void voice_mhi_register_apr_and_send(struct work_struct *work)
  348. {
  349. int ret = 0;
  350. ret = voice_mhi_apr_register();
  351. if (ret) {
  352. pr_err("%s: APR registration failed %d\n", __func__, ret);
  353. return;
  354. }
  355. mutex_lock(&voice_mhi_lcl.mutex);
  356. if (q6core_is_adsp_ready()) {
  357. if (VOICE_MHI_STATE_CHECK(voice_mhi_lcl.voice_mhi_state,
  358. VOICE_MHI_SDX_UP)) {
  359. mutex_unlock(&voice_mhi_lcl.mutex);
  360. voice_mhi_set_mailbox_memory_config();
  361. return;
  362. }
  363. }
  364. mutex_unlock(&voice_mhi_lcl.mutex);
  365. }
  366. static int voice_mhi_pcie_up_callback(struct mhi_device *voice_mhi_dev,
  367. const struct mhi_device_id *id)
  368. {
  369. if ((!voice_mhi_dev) || (id != &voice_mhi_match_table[0])) {
  370. pr_err("%s: Invalid device or table received\n", __func__);
  371. return -EINVAL;
  372. }
  373. pr_debug("%s: MHI PCIe UP callback\n", __func__);
  374. mutex_lock(&voice_mhi_lcl.mutex);
  375. voice_mhi_lcl.mhi_dev = voice_mhi_dev;
  376. VOICE_MHI_STATE_SET(voice_mhi_lcl.voice_mhi_state, VOICE_MHI_SDX_UP);
  377. mutex_unlock(&voice_mhi_lcl.mutex);
  378. schedule_work(&voice_mhi_lcl.voice_mhi_work_pcie);
  379. return 0;
  380. }
  381. static void voice_mhi_pcie_down_callback(struct mhi_device *voice_mhi_dev)
  382. {
  383. dma_addr_t iova;
  384. struct device *md = NULL;
  385. mutex_lock(&voice_mhi_lcl.mutex);
  386. if (voice_mhi_lcl.mhi_dev)
  387. md = &voice_mhi_lcl.mhi_dev->dev;
  388. VOICE_MHI_STATE_RESET(voice_mhi_lcl.voice_mhi_state, VOICE_MHI_SDX_UP);
  389. iova = voice_mhi_lcl.dev_info.iova_pcie.base;
  390. if (md)
  391. dma_unmap_resource(md->parent, iova, PAGE_SIZE,
  392. DMA_BIDIRECTIONAL, 0);
  393. voice_mhi_lcl.mhi_dev = NULL;
  394. voice_mhi_lcl.vote_count = 0;
  395. mutex_unlock(&voice_mhi_lcl.mutex);
  396. }
  397. static void voice_mhi_pcie_status_callback(struct mhi_device *voice_mhi_dev,
  398. enum MHI_CB mhi_cb)
  399. {
  400. }
  401. static int voice_mhi_apr_register(void)
  402. {
  403. int ret = 0;
  404. mutex_lock(&voice_mhi_lcl.mutex);
  405. voice_mhi_lcl.apr_mvm_handle = apr_register("ADSP", "MVM",
  406. (apr_fn)voice_mhi_apr_callback,
  407. CONVERT_PORT_APR(PORT_NUM,
  408. PORT_MASK),
  409. &voice_mhi_lcl);
  410. if (voice_mhi_lcl.apr_mvm_handle == NULL) {
  411. pr_err("%s: error in APR register\n", __func__);
  412. ret = -ENODEV;
  413. }
  414. mutex_unlock(&voice_mhi_lcl.mutex);
  415. return ret;
  416. }
  417. static int voice_mhi_probe(struct platform_device *pdev)
  418. {
  419. int ret = 0;
  420. struct device_node *node;
  421. uint32_t mem_size = 0;
  422. void *ptr;
  423. dma_addr_t phys_addr, iova;
  424. const __be32 *cell;
  425. pr_debug("%s:\n", __func__);
  426. INIT_WORK(&voice_mhi_lcl.voice_mhi_work_pcie,
  427. voice_mhi_map_pcie_and_send);
  428. INIT_WORK(&voice_mhi_lcl.voice_mhi_work_adsp,
  429. voice_mhi_register_apr_and_send);
  430. init_waitqueue_head(&voice_mhi_lcl.voice_mhi_wait);
  431. node = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
  432. if (node) {
  433. cell = of_get_property(node, "size", NULL);
  434. if (cell)
  435. mem_size = of_read_number(cell, 2);
  436. else {
  437. pr_err("%s: cell not found\n", __func__);
  438. ret = -EINVAL;
  439. goto done;
  440. }
  441. } else {
  442. pr_err("%s: Node read failed\n", __func__);
  443. ret = -EINVAL;
  444. goto done;
  445. }
  446. pr_debug("%s: mem_size = %d\n", __func__, mem_size);
  447. if (mem_size) {
  448. ptr = dma_alloc_attrs(&pdev->dev, mem_size, &phys_addr,
  449. GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING);
  450. if (IS_ERR_OR_NULL(ptr)) {
  451. pr_err("%s: Memory alloc failed\n", __func__);
  452. ret = -ENOMEM;
  453. goto done;
  454. } else {
  455. pr_debug("%s: Memory alloc success phys_addr:0x%lx\n",
  456. __func__, (unsigned long)phys_addr);
  457. }
  458. ret = msm_audio_ion_dma_map(&phys_addr, &iova, mem_size,
  459. DMA_BIDIRECTIONAL);
  460. if (ret) {
  461. pr_err("%s: dma mapping failed %d\n", __func__, ret);
  462. goto err_free;
  463. }
  464. pr_debug("%s: dma_mapping_success iova:0x%lx\n",
  465. __func__, (unsigned long)iova);
  466. voice_mhi_lcl.dev_info.phys_addr.base = phys_addr;
  467. voice_mhi_lcl.dev_info.iova_adsp.base = iova;
  468. voice_mhi_lcl.dev_info.iova_adsp.size = mem_size;
  469. voice_mhi_lcl.dev_info.iova_pcie.size = mem_size;
  470. VOICE_MHI_STATE_SET(voice_mhi_lcl.voice_mhi_state,
  471. VOICE_MHI_ADSP_UP);
  472. ret = voice_mhi_apr_register();
  473. /* If fails register during audio notifier UP event */
  474. if (ret)
  475. pr_err("%s: APR register failed %d\n", __func__, ret);
  476. ret = mhi_driver_register(&voice_mhi_driver);
  477. if (ret) {
  478. pr_err("%s: mhi register failed %d\n", __func__, ret);
  479. goto done;
  480. }
  481. ret = audio_notifier_register("voice_mhi",
  482. AUDIO_NOTIFIER_ADSP_DOMAIN,
  483. &voice_mhi_service_nb);
  484. if (ret < 0)
  485. pr_err("%s: Audio notifier register failed ret = %d\n",
  486. __func__, ret);
  487. mutex_lock(&voice_mhi_lcl.mutex);
  488. voice_mhi_lcl.dev_info.pdev = pdev;
  489. voice_mhi_lcl.pcie_enabled = true;
  490. VOICE_MHI_STATE_SET(voice_mhi_lcl.voice_mhi_state,
  491. VOICE_MHI_PROBED);
  492. mutex_unlock(&voice_mhi_lcl.mutex);
  493. } else {
  494. pr_err("%s: Memory size can't be zero\n", __func__);
  495. ret = -ENOMEM;
  496. goto done;
  497. }
  498. done:
  499. return ret;
  500. err_free:
  501. dma_free_attrs(&pdev->dev, mem_size, ptr, phys_addr,
  502. DMA_ATTR_NO_KERNEL_MAPPING);
  503. return ret;
  504. }
  505. static int voice_mhi_remove(struct platform_device *pdev)
  506. {
  507. if (voice_mhi_lcl.apr_mvm_handle)
  508. apr_reset(voice_mhi_lcl.apr_mvm_handle);
  509. mhi_driver_unregister(&voice_mhi_driver);
  510. memset(&voice_mhi_lcl, 0, sizeof(voice_mhi_lcl));
  511. return 0;
  512. }
  513. static const struct of_device_id voice_mhi_of_match[] = {
  514. { .compatible = "qcom,voice-mhi-audio", },
  515. {},
  516. };
  517. static struct platform_driver voice_mhi_platform_driver = {
  518. .probe = voice_mhi_probe,
  519. .remove = voice_mhi_remove,
  520. .driver = {
  521. .name = "voice_mhi_audio",
  522. .owner = THIS_MODULE,
  523. .of_match_table = voice_mhi_of_match,
  524. }
  525. };
  526. int __init voice_mhi_init(void)
  527. {
  528. int ret = 0;
  529. memset(&voice_mhi_lcl, 0, sizeof(voice_mhi_lcl));
  530. mutex_init(&voice_mhi_lcl.mutex);
  531. /* Add remaining init here */
  532. voice_mhi_lcl.pcie_enabled = false;
  533. voice_mhi_lcl.voice_mhi_state = VOICE_MHI_INIT;
  534. voice_mhi_lcl.vote_count = 0;
  535. voice_mhi_lcl.apr_mvm_handle = NULL;
  536. ret = platform_driver_register(&voice_mhi_platform_driver);
  537. return ret;
  538. }
  539. void __exit voice_mhi_exit(void)
  540. {
  541. mutex_destroy(&voice_mhi_lcl.mutex);
  542. platform_driver_unregister(&voice_mhi_platform_driver);
  543. }
  544. MODULE_DESCRIPTION("Voice MHI module");
  545. MODULE_LICENSE("GPL v2");