imx8ulp.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
  2. //
  3. // Copyright 2021-2022 NXP
  4. //
  5. // Author: Peng Zhang <peng.zhang_8@nxp.com>
  6. //
  7. // Hardware interface for audio DSP on i.MX8ULP
  8. #include <linux/arm-smccc.h>
  9. #include <linux/clk.h>
  10. #include <linux/firmware.h>
  11. #include <linux/firmware/imx/dsp.h>
  12. #include <linux/firmware/imx/ipc.h>
  13. #include <linux/firmware/imx/svc/misc.h>
  14. #include <linux/mfd/syscon.h>
  15. #include <linux/module.h>
  16. #include <linux/of_address.h>
  17. #include <linux/of_irq.h>
  18. #include <linux/of_platform.h>
  19. #include <linux/of_reserved_mem.h>
  20. #include <sound/sof.h>
  21. #include <sound/sof/xtensa.h>
  22. #include "../ops.h"
  23. #include "../sof-of-dev.h"
  24. #include "imx-common.h"
  25. #define FSL_SIP_HIFI_XRDC 0xc200000e
  26. /* SIM Domain register */
  27. #define SYSCTRL0 0x8
  28. #define EXECUTE_BIT BIT(13)
  29. #define RESET_BIT BIT(16)
  30. #define HIFI4_CLK_BIT BIT(17)
  31. #define PB_CLK_BIT BIT(18)
  32. #define PLAT_CLK_BIT BIT(19)
  33. #define DEBUG_LOGIC_BIT BIT(25)
  34. #define MBOX_OFFSET 0x800000
  35. #define MBOX_SIZE 0x1000
  36. struct imx8ulp_priv {
  37. struct device *dev;
  38. struct snd_sof_dev *sdev;
  39. /* DSP IPC handler */
  40. struct imx_dsp_ipc *dsp_ipc;
  41. struct platform_device *ipc_dev;
  42. struct regmap *regmap;
  43. struct clk_bulk_data *clks;
  44. int clk_num;
  45. };
  46. static void imx8ulp_sim_lpav_start(struct imx8ulp_priv *priv)
  47. {
  48. /* Controls the HiFi4 DSP Reset: 1 in reset, 0 out of reset */
  49. regmap_update_bits(priv->regmap, SYSCTRL0, RESET_BIT, 0);
  50. /* Reset HiFi4 DSP Debug logic: 1 debug reset, 0 out of reset*/
  51. regmap_update_bits(priv->regmap, SYSCTRL0, DEBUG_LOGIC_BIT, 0);
  52. /* Stall HIFI4 DSP Execution: 1 stall, 0 run */
  53. regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, 0);
  54. }
  55. static int imx8ulp_get_mailbox_offset(struct snd_sof_dev *sdev)
  56. {
  57. return MBOX_OFFSET;
  58. }
  59. static int imx8ulp_get_window_offset(struct snd_sof_dev *sdev, u32 id)
  60. {
  61. return MBOX_OFFSET;
  62. }
  63. static void imx8ulp_dsp_handle_reply(struct imx_dsp_ipc *ipc)
  64. {
  65. struct imx8ulp_priv *priv = imx_dsp_get_data(ipc);
  66. unsigned long flags;
  67. spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
  68. snd_sof_ipc_process_reply(priv->sdev, 0);
  69. spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
  70. }
  71. static void imx8ulp_dsp_handle_request(struct imx_dsp_ipc *ipc)
  72. {
  73. struct imx8ulp_priv *priv = imx_dsp_get_data(ipc);
  74. u32 p; /* panic code */
  75. /* Read the message from the debug box. */
  76. sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, &p, sizeof(p));
  77. /* Check to see if the message is a panic code (0x0dead***) */
  78. if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC)
  79. snd_sof_dsp_panic(priv->sdev, p, true);
  80. else
  81. snd_sof_ipc_msgs_rx(priv->sdev);
  82. }
  83. static struct imx_dsp_ops dsp_ops = {
  84. .handle_reply = imx8ulp_dsp_handle_reply,
  85. .handle_request = imx8ulp_dsp_handle_request,
  86. };
  87. static int imx8ulp_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
  88. {
  89. struct imx8ulp_priv *priv = sdev->pdata->hw_pdata;
  90. sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
  91. msg->msg_size);
  92. imx_dsp_ring_doorbell(priv->dsp_ipc, 0);
  93. return 0;
  94. }
  95. static int imx8ulp_run(struct snd_sof_dev *sdev)
  96. {
  97. struct imx8ulp_priv *priv = sdev->pdata->hw_pdata;
  98. imx8ulp_sim_lpav_start(priv);
  99. return 0;
  100. }
  101. static int imx8ulp_reset(struct snd_sof_dev *sdev)
  102. {
  103. struct imx8ulp_priv *priv = sdev->pdata->hw_pdata;
  104. struct arm_smccc_res smc_resource;
  105. /* HiFi4 Platform Clock Enable: 1 enabled, 0 disabled */
  106. regmap_update_bits(priv->regmap, SYSCTRL0, PLAT_CLK_BIT, PLAT_CLK_BIT);
  107. /* HiFi4 PBCLK clock enable: 1 enabled, 0 disabled */
  108. regmap_update_bits(priv->regmap, SYSCTRL0, PB_CLK_BIT, PB_CLK_BIT);
  109. /* HiFi4 Clock Enable: 1 enabled, 0 disabled */
  110. regmap_update_bits(priv->regmap, SYSCTRL0, HIFI4_CLK_BIT, HIFI4_CLK_BIT);
  111. regmap_update_bits(priv->regmap, SYSCTRL0, RESET_BIT, RESET_BIT);
  112. usleep_range(1, 2);
  113. /* Stall HIFI4 DSP Execution: 1 stall, 0 not stall */
  114. regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT);
  115. usleep_range(1, 2);
  116. arm_smccc_smc(FSL_SIP_HIFI_XRDC, 0, 0, 0, 0, 0, 0, 0, &smc_resource);
  117. return 0;
  118. }
  119. static int imx8ulp_probe(struct snd_sof_dev *sdev)
  120. {
  121. struct platform_device *pdev =
  122. container_of(sdev->dev, struct platform_device, dev);
  123. struct device_node *np = pdev->dev.of_node;
  124. struct device_node *res_node;
  125. struct resource *mmio;
  126. struct imx8ulp_priv *priv;
  127. struct resource res;
  128. u32 base, size;
  129. int ret = 0;
  130. priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
  131. if (!priv)
  132. return -ENOMEM;
  133. sdev->num_cores = 1;
  134. sdev->pdata->hw_pdata = priv;
  135. priv->dev = sdev->dev;
  136. priv->sdev = sdev;
  137. /* System integration module(SIM) control dsp configuration */
  138. priv->regmap = syscon_regmap_lookup_by_phandle(np, "fsl,dsp-ctrl");
  139. if (IS_ERR(priv->regmap))
  140. return PTR_ERR(priv->regmap);
  141. priv->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp",
  142. PLATFORM_DEVID_NONE,
  143. pdev, sizeof(*pdev));
  144. if (IS_ERR(priv->ipc_dev))
  145. return PTR_ERR(priv->ipc_dev);
  146. priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev);
  147. if (!priv->dsp_ipc) {
  148. /* DSP IPC driver not probed yet, try later */
  149. ret = -EPROBE_DEFER;
  150. dev_err(sdev->dev, "Failed to get drvdata\n");
  151. goto exit_pdev_unregister;
  152. }
  153. imx_dsp_set_data(priv->dsp_ipc, priv);
  154. priv->dsp_ipc->ops = &dsp_ops;
  155. /* DSP base */
  156. mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  157. if (mmio) {
  158. base = mmio->start;
  159. size = resource_size(mmio);
  160. } else {
  161. dev_err(sdev->dev, "error: failed to get DSP base at idx 0\n");
  162. ret = -EINVAL;
  163. goto exit_pdev_unregister;
  164. }
  165. sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size);
  166. if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) {
  167. dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n",
  168. base, size);
  169. ret = -ENODEV;
  170. goto exit_pdev_unregister;
  171. }
  172. sdev->mmio_bar = SOF_FW_BLK_TYPE_IRAM;
  173. res_node = of_parse_phandle(np, "memory-reserved", 0);
  174. if (!res_node) {
  175. dev_err(&pdev->dev, "failed to get memory region node\n");
  176. ret = -ENODEV;
  177. goto exit_pdev_unregister;
  178. }
  179. ret = of_address_to_resource(res_node, 0, &res);
  180. of_node_put(res_node);
  181. if (ret) {
  182. dev_err(&pdev->dev, "failed to get reserved region address\n");
  183. goto exit_pdev_unregister;
  184. }
  185. sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, res.start,
  186. resource_size(&res));
  187. if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) {
  188. dev_err(sdev->dev, "failed to ioremap mem 0x%x size 0x%x\n",
  189. base, size);
  190. ret = -ENOMEM;
  191. goto exit_pdev_unregister;
  192. }
  193. sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM;
  194. /* set default mailbox offset for FW ready message */
  195. sdev->dsp_box.offset = MBOX_OFFSET;
  196. ret = of_reserved_mem_device_init(sdev->dev);
  197. if (ret) {
  198. dev_err(&pdev->dev, "failed to init reserved memory region %d\n", ret);
  199. goto exit_pdev_unregister;
  200. }
  201. ret = devm_clk_bulk_get_all(sdev->dev, &priv->clks);
  202. if (ret < 0) {
  203. dev_err(sdev->dev, "failed to fetch clocks: %d\n", ret);
  204. goto exit_pdev_unregister;
  205. }
  206. priv->clk_num = ret;
  207. ret = clk_bulk_prepare_enable(priv->clk_num, priv->clks);
  208. if (ret < 0) {
  209. dev_err(sdev->dev, "failed to enable clocks: %d\n", ret);
  210. goto exit_pdev_unregister;
  211. }
  212. return 0;
  213. exit_pdev_unregister:
  214. platform_device_unregister(priv->ipc_dev);
  215. return ret;
  216. }
  217. static void imx8ulp_remove(struct snd_sof_dev *sdev)
  218. {
  219. struct imx8ulp_priv *priv = sdev->pdata->hw_pdata;
  220. clk_bulk_disable_unprepare(priv->clk_num, priv->clks);
  221. platform_device_unregister(priv->ipc_dev);
  222. }
  223. /* on i.MX8 there is 1 to 1 match between type and BAR idx */
  224. static int imx8ulp_get_bar_index(struct snd_sof_dev *sdev, u32 type)
  225. {
  226. return type;
  227. }
  228. static int imx8ulp_suspend(struct snd_sof_dev *sdev)
  229. {
  230. int i;
  231. struct imx8ulp_priv *priv = (struct imx8ulp_priv *)sdev->pdata->hw_pdata;
  232. /*Stall DSP, release in .run() */
  233. regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT);
  234. for (i = 0; i < DSP_MU_CHAN_NUM; i++)
  235. imx_dsp_free_channel(priv->dsp_ipc, i);
  236. clk_bulk_disable_unprepare(priv->clk_num, priv->clks);
  237. return 0;
  238. }
  239. static int imx8ulp_resume(struct snd_sof_dev *sdev)
  240. {
  241. struct imx8ulp_priv *priv = (struct imx8ulp_priv *)sdev->pdata->hw_pdata;
  242. int i, ret;
  243. ret = clk_bulk_prepare_enable(priv->clk_num, priv->clks);
  244. if (ret < 0) {
  245. dev_err(sdev->dev, "failed to enable clocks: %d\n", ret);
  246. return ret;
  247. }
  248. for (i = 0; i < DSP_MU_CHAN_NUM; i++)
  249. imx_dsp_request_channel(priv->dsp_ipc, i);
  250. return 0;
  251. }
  252. static int imx8ulp_dsp_runtime_resume(struct snd_sof_dev *sdev)
  253. {
  254. const struct sof_dsp_power_state target_dsp_state = {
  255. .state = SOF_DSP_PM_D0,
  256. .substate = 0,
  257. };
  258. imx8ulp_resume(sdev);
  259. return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
  260. }
  261. static int imx8ulp_dsp_runtime_suspend(struct snd_sof_dev *sdev)
  262. {
  263. const struct sof_dsp_power_state target_dsp_state = {
  264. .state = SOF_DSP_PM_D3,
  265. .substate = 0,
  266. };
  267. imx8ulp_suspend(sdev);
  268. return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
  269. }
  270. static int imx8ulp_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state)
  271. {
  272. const struct sof_dsp_power_state target_dsp_state = {
  273. .state = target_state,
  274. .substate = 0,
  275. };
  276. if (!pm_runtime_suspended(sdev->dev))
  277. imx8ulp_suspend(sdev);
  278. return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
  279. }
  280. static int imx8ulp_dsp_resume(struct snd_sof_dev *sdev)
  281. {
  282. const struct sof_dsp_power_state target_dsp_state = {
  283. .state = SOF_DSP_PM_D0,
  284. .substate = 0,
  285. };
  286. imx8ulp_resume(sdev);
  287. if (pm_runtime_suspended(sdev->dev)) {
  288. pm_runtime_disable(sdev->dev);
  289. pm_runtime_set_active(sdev->dev);
  290. pm_runtime_mark_last_busy(sdev->dev);
  291. pm_runtime_enable(sdev->dev);
  292. pm_runtime_idle(sdev->dev);
  293. }
  294. return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
  295. }
  296. static struct snd_soc_dai_driver imx8ulp_dai[] = {
  297. {
  298. .name = "sai5",
  299. .playback = {
  300. .channels_min = 1,
  301. .channels_max = 32,
  302. },
  303. .capture = {
  304. .channels_min = 1,
  305. .channels_max = 32,
  306. },
  307. },
  308. {
  309. .name = "sai6",
  310. .playback = {
  311. .channels_min = 1,
  312. .channels_max = 32,
  313. },
  314. .capture = {
  315. .channels_min = 1,
  316. .channels_max = 32,
  317. },
  318. },
  319. };
  320. static int imx8ulp_dsp_set_power_state(struct snd_sof_dev *sdev,
  321. const struct sof_dsp_power_state *target_state)
  322. {
  323. sdev->dsp_power_state = *target_state;
  324. return 0;
  325. }
  326. /* i.MX8 ops */
  327. static const struct snd_sof_dsp_ops sof_imx8ulp_ops = {
  328. /* probe and remove */
  329. .probe = imx8ulp_probe,
  330. .remove = imx8ulp_remove,
  331. /* DSP core boot */
  332. .run = imx8ulp_run,
  333. .reset = imx8ulp_reset,
  334. /* Block IO */
  335. .block_read = sof_block_read,
  336. .block_write = sof_block_write,
  337. /* Module IO */
  338. .read64 = sof_io_read64,
  339. /* Mailbox IO */
  340. .mailbox_read = sof_mailbox_read,
  341. .mailbox_write = sof_mailbox_write,
  342. /* ipc */
  343. .send_msg = imx8ulp_send_msg,
  344. .get_mailbox_offset = imx8ulp_get_mailbox_offset,
  345. .get_window_offset = imx8ulp_get_window_offset,
  346. .ipc_msg_data = sof_ipc_msg_data,
  347. .set_stream_data_offset = sof_set_stream_data_offset,
  348. /* stream callbacks */
  349. .pcm_open = sof_stream_pcm_open,
  350. .pcm_close = sof_stream_pcm_close,
  351. /* module loading */
  352. .get_bar_index = imx8ulp_get_bar_index,
  353. /* firmware loading */
  354. .load_firmware = snd_sof_load_firmware_memcpy,
  355. /* Debug information */
  356. .dbg_dump = imx8_dump,
  357. /* Firmware ops */
  358. .dsp_arch_ops = &sof_xtensa_arch_ops,
  359. /* DAI drivers */
  360. .drv = imx8ulp_dai,
  361. .num_drv = ARRAY_SIZE(imx8ulp_dai),
  362. /* ALSA HW info flags */
  363. .hw_info = SNDRV_PCM_INFO_MMAP |
  364. SNDRV_PCM_INFO_MMAP_VALID |
  365. SNDRV_PCM_INFO_INTERLEAVED |
  366. SNDRV_PCM_INFO_PAUSE |
  367. SNDRV_PCM_INFO_BATCH |
  368. SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
  369. /* PM */
  370. .runtime_suspend = imx8ulp_dsp_runtime_suspend,
  371. .runtime_resume = imx8ulp_dsp_runtime_resume,
  372. .suspend = imx8ulp_dsp_suspend,
  373. .resume = imx8ulp_dsp_resume,
  374. .set_power_state = imx8ulp_dsp_set_power_state,
  375. };
  376. static struct snd_sof_of_mach sof_imx8ulp_machs[] = {
  377. {
  378. .compatible = "fsl,imx8ulp-evk",
  379. .sof_tplg_filename = "sof-imx8ulp-btsco.tplg",
  380. .drv_name = "asoc-audio-graph-card2",
  381. },
  382. {}
  383. };
  384. static struct sof_dev_desc sof_of_imx8ulp_desc = {
  385. .of_machines = sof_imx8ulp_machs,
  386. .ipc_supported_mask = BIT(SOF_IPC_TYPE_3),
  387. .ipc_default = SOF_IPC_TYPE_3,
  388. .default_fw_path = {
  389. [SOF_IPC_TYPE_3] = "imx/sof",
  390. },
  391. .default_tplg_path = {
  392. [SOF_IPC_TYPE_3] = "imx/sof-tplg",
  393. },
  394. .default_fw_filename = {
  395. [SOF_IPC_TYPE_3] = "sof-imx8ulp.ri",
  396. },
  397. .nocodec_tplg_filename = "sof-imx8ulp-nocodec.tplg",
  398. .ops = &sof_imx8ulp_ops,
  399. };
  400. static const struct of_device_id sof_of_imx8ulp_ids[] = {
  401. { .compatible = "fsl,imx8ulp-dsp", .data = &sof_of_imx8ulp_desc},
  402. { }
  403. };
  404. MODULE_DEVICE_TABLE(of, sof_of_imx8ulp_ids);
  405. /* DT driver definition */
  406. static struct platform_driver snd_sof_of_imx8ulp_driver = {
  407. .probe = sof_of_probe,
  408. .remove = sof_of_remove,
  409. .driver = {
  410. .name = "sof-audio-of-imx8ulp",
  411. .pm = &sof_of_pm,
  412. .of_match_table = sof_of_imx8ulp_ids,
  413. },
  414. };
  415. module_platform_driver(snd_sof_of_imx8ulp_driver);
  416. MODULE_LICENSE("Dual BSD/GPL");
  417. MODULE_DESCRIPTION("SOF support for IMX8ULP platforms");
  418. MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA);