device.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. //
  3. // Copyright(c) 2020 Intel Corporation. All rights reserved.
  4. //
  5. // Author: Cezary Rojewski <[email protected]>
  6. //
  7. // Special thanks to:
  8. // Marcin Barlik <[email protected]>
  9. // Piotr Papierkowski <[email protected]>
  10. //
  11. // for sharing LPT-LP and WTP-LP AudioDSP architecture expertise and
  12. // helping backtrack its historical background
  13. //
  14. #include <linux/acpi.h>
  15. #include <linux/dma-mapping.h>
  16. #include <linux/interrupt.h>
  17. #include <linux/module.h>
  18. #include <linux/pci.h>
  19. #include <linux/platform_device.h>
  20. #include <linux/pm_runtime.h>
  21. #include <sound/intel-dsp-config.h>
  22. #include <sound/soc.h>
  23. #include <sound/soc-acpi.h>
  24. #include "core.h"
  25. #include "registers.h"
  26. #define CREATE_TRACE_POINTS
  27. #include "trace.h"
  28. static int __maybe_unused catpt_suspend(struct device *dev)
  29. {
  30. struct catpt_dev *cdev = dev_get_drvdata(dev);
  31. struct dma_chan *chan;
  32. int ret;
  33. chan = catpt_dma_request_config_chan(cdev);
  34. if (IS_ERR(chan))
  35. return PTR_ERR(chan);
  36. memset(&cdev->dx_ctx, 0, sizeof(cdev->dx_ctx));
  37. ret = catpt_ipc_enter_dxstate(cdev, CATPT_DX_STATE_D3, &cdev->dx_ctx);
  38. if (ret) {
  39. ret = CATPT_IPC_ERROR(ret);
  40. goto release_dma_chan;
  41. }
  42. ret = catpt_dsp_stall(cdev, true);
  43. if (ret)
  44. goto release_dma_chan;
  45. ret = catpt_store_memdumps(cdev, chan);
  46. if (ret) {
  47. dev_err(cdev->dev, "store memdumps failed: %d\n", ret);
  48. goto release_dma_chan;
  49. }
  50. ret = catpt_store_module_states(cdev, chan);
  51. if (ret) {
  52. dev_err(cdev->dev, "store module states failed: %d\n", ret);
  53. goto release_dma_chan;
  54. }
  55. ret = catpt_store_streams_context(cdev, chan);
  56. if (ret)
  57. dev_err(cdev->dev, "store streams ctx failed: %d\n", ret);
  58. release_dma_chan:
  59. dma_release_channel(chan);
  60. if (ret)
  61. return ret;
  62. return catpt_dsp_power_down(cdev);
  63. }
  64. static int __maybe_unused catpt_resume(struct device *dev)
  65. {
  66. struct catpt_dev *cdev = dev_get_drvdata(dev);
  67. int ret, i;
  68. ret = catpt_dsp_power_up(cdev);
  69. if (ret)
  70. return ret;
  71. if (!try_module_get(dev->driver->owner)) {
  72. dev_info(dev, "module unloading, skipping fw boot\n");
  73. return 0;
  74. }
  75. module_put(dev->driver->owner);
  76. ret = catpt_boot_firmware(cdev, true);
  77. if (ret) {
  78. dev_err(cdev->dev, "boot firmware failed: %d\n", ret);
  79. return ret;
  80. }
  81. /* reconfigure SSP devices after Dx transition */
  82. for (i = 0; i < CATPT_SSP_COUNT; i++) {
  83. if (cdev->devfmt[i].iface == UINT_MAX)
  84. continue;
  85. ret = catpt_ipc_set_device_format(cdev, &cdev->devfmt[i]);
  86. if (ret)
  87. return CATPT_IPC_ERROR(ret);
  88. }
  89. return 0;
  90. }
  91. static int __maybe_unused catpt_runtime_suspend(struct device *dev)
  92. {
  93. if (!try_module_get(dev->driver->owner)) {
  94. dev_info(dev, "module unloading, skipping suspend\n");
  95. return 0;
  96. }
  97. module_put(dev->driver->owner);
  98. return catpt_suspend(dev);
  99. }
  100. static int __maybe_unused catpt_runtime_resume(struct device *dev)
  101. {
  102. return catpt_resume(dev);
  103. }
  104. static const struct dev_pm_ops catpt_dev_pm = {
  105. SET_SYSTEM_SLEEP_PM_OPS(catpt_suspend, catpt_resume)
  106. SET_RUNTIME_PM_OPS(catpt_runtime_suspend, catpt_runtime_resume, NULL)
  107. };
  108. /* machine board owned by CATPT is removed with this hook */
  109. static void board_pdev_unregister(void *data)
  110. {
  111. platform_device_unregister(data);
  112. }
  113. static int catpt_register_board(struct catpt_dev *cdev)
  114. {
  115. const struct catpt_spec *spec = cdev->spec;
  116. struct snd_soc_acpi_mach *mach;
  117. struct platform_device *board;
  118. mach = snd_soc_acpi_find_machine(spec->machines);
  119. if (!mach) {
  120. dev_info(cdev->dev, "no machines present\n");
  121. return 0;
  122. }
  123. mach->mach_params.platform = "catpt-platform";
  124. board = platform_device_register_data(NULL, mach->drv_name,
  125. PLATFORM_DEVID_NONE,
  126. (const void *)mach, sizeof(*mach));
  127. if (IS_ERR(board)) {
  128. dev_err(cdev->dev, "board register failed\n");
  129. return PTR_ERR(board);
  130. }
  131. return devm_add_action_or_reset(cdev->dev, board_pdev_unregister,
  132. board);
  133. }
  134. static int catpt_probe_components(struct catpt_dev *cdev)
  135. {
  136. int ret;
  137. ret = catpt_dsp_power_up(cdev);
  138. if (ret)
  139. return ret;
  140. ret = catpt_dmac_probe(cdev);
  141. if (ret) {
  142. dev_err(cdev->dev, "DMAC probe failed: %d\n", ret);
  143. goto err_dmac_probe;
  144. }
  145. ret = catpt_first_boot_firmware(cdev);
  146. if (ret) {
  147. dev_err(cdev->dev, "first fw boot failed: %d\n", ret);
  148. goto err_boot_fw;
  149. }
  150. ret = catpt_register_plat_component(cdev);
  151. if (ret) {
  152. dev_err(cdev->dev, "register plat comp failed: %d\n", ret);
  153. goto err_boot_fw;
  154. }
  155. ret = catpt_register_board(cdev);
  156. if (ret) {
  157. dev_err(cdev->dev, "register board failed: %d\n", ret);
  158. goto err_reg_board;
  159. }
  160. /* reflect actual ADSP state in pm_runtime */
  161. pm_runtime_set_active(cdev->dev);
  162. pm_runtime_set_autosuspend_delay(cdev->dev, 2000);
  163. pm_runtime_use_autosuspend(cdev->dev);
  164. pm_runtime_mark_last_busy(cdev->dev);
  165. pm_runtime_enable(cdev->dev);
  166. return 0;
  167. err_reg_board:
  168. snd_soc_unregister_component(cdev->dev);
  169. err_boot_fw:
  170. catpt_dmac_remove(cdev);
  171. err_dmac_probe:
  172. catpt_dsp_power_down(cdev);
  173. return ret;
  174. }
  175. static void catpt_dev_init(struct catpt_dev *cdev, struct device *dev,
  176. const struct catpt_spec *spec)
  177. {
  178. cdev->dev = dev;
  179. cdev->spec = spec;
  180. init_completion(&cdev->fw_ready);
  181. INIT_LIST_HEAD(&cdev->stream_list);
  182. spin_lock_init(&cdev->list_lock);
  183. mutex_init(&cdev->clk_mutex);
  184. /*
  185. * Mark both device formats as uninitialized. Once corresponding
  186. * cpu_dai's pcm is created, proper values are assigned.
  187. */
  188. cdev->devfmt[CATPT_SSP_IFACE_0].iface = UINT_MAX;
  189. cdev->devfmt[CATPT_SSP_IFACE_1].iface = UINT_MAX;
  190. catpt_ipc_init(&cdev->ipc, dev);
  191. catpt_sram_init(&cdev->dram, spec->host_dram_offset,
  192. catpt_dram_size(cdev));
  193. catpt_sram_init(&cdev->iram, spec->host_iram_offset,
  194. catpt_iram_size(cdev));
  195. }
  196. static int catpt_acpi_probe(struct platform_device *pdev)
  197. {
  198. const struct catpt_spec *spec;
  199. struct catpt_dev *cdev;
  200. struct device *dev = &pdev->dev;
  201. const struct acpi_device_id *id;
  202. struct resource *res;
  203. int ret;
  204. id = acpi_match_device(dev->driver->acpi_match_table, dev);
  205. if (!id)
  206. return -ENODEV;
  207. ret = snd_intel_acpi_dsp_driver_probe(dev, id->id);
  208. if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_SST) {
  209. dev_dbg(dev, "CATPT ACPI driver not selected, aborting probe\n");
  210. return -ENODEV;
  211. }
  212. cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL);
  213. if (!cdev)
  214. return -ENOMEM;
  215. spec = (const struct catpt_spec *)id->driver_data;
  216. catpt_dev_init(cdev, dev, spec);
  217. /* map DSP bar address */
  218. cdev->lpe_ba = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
  219. if (IS_ERR(cdev->lpe_ba))
  220. return PTR_ERR(cdev->lpe_ba);
  221. cdev->lpe_base = res->start;
  222. /* map PCI bar address */
  223. cdev->pci_ba = devm_platform_ioremap_resource(pdev, 1);
  224. if (IS_ERR(cdev->pci_ba))
  225. return PTR_ERR(cdev->pci_ba);
  226. /* alloc buffer for storing DRAM context during dx transitions */
  227. cdev->dxbuf_vaddr = dmam_alloc_coherent(dev, catpt_dram_size(cdev),
  228. &cdev->dxbuf_paddr, GFP_KERNEL);
  229. if (!cdev->dxbuf_vaddr)
  230. return -ENOMEM;
  231. ret = platform_get_irq(pdev, 0);
  232. if (ret < 0)
  233. return ret;
  234. cdev->irq = ret;
  235. platform_set_drvdata(pdev, cdev);
  236. ret = devm_request_threaded_irq(dev, cdev->irq, catpt_dsp_irq_handler,
  237. catpt_dsp_irq_thread,
  238. IRQF_SHARED, "AudioDSP", cdev);
  239. if (ret)
  240. return ret;
  241. return catpt_probe_components(cdev);
  242. }
  243. static int catpt_acpi_remove(struct platform_device *pdev)
  244. {
  245. struct catpt_dev *cdev = platform_get_drvdata(pdev);
  246. pm_runtime_disable(cdev->dev);
  247. snd_soc_unregister_component(cdev->dev);
  248. catpt_dmac_remove(cdev);
  249. catpt_dsp_power_down(cdev);
  250. catpt_sram_free(&cdev->iram);
  251. catpt_sram_free(&cdev->dram);
  252. return 0;
  253. }
  254. static struct snd_soc_acpi_mach lpt_machines[] = {
  255. {
  256. .id = "INT33CA",
  257. .drv_name = "hsw_rt5640",
  258. },
  259. {}
  260. };
  261. static struct snd_soc_acpi_mach wpt_machines[] = {
  262. {
  263. .id = "INT33CA",
  264. .drv_name = "hsw_rt5640",
  265. },
  266. {
  267. .id = "INT343A",
  268. .drv_name = "bdw_rt286",
  269. },
  270. {
  271. .id = "10EC5650",
  272. .drv_name = "bdw-rt5650",
  273. },
  274. {
  275. .id = "RT5677CE",
  276. .drv_name = "bdw-rt5677",
  277. },
  278. {}
  279. };
  280. static struct catpt_spec lpt_desc = {
  281. .machines = lpt_machines,
  282. .core_id = 0x01,
  283. .host_dram_offset = 0x000000,
  284. .host_iram_offset = 0x080000,
  285. .host_shim_offset = 0x0E7000,
  286. .host_dma_offset = { 0x0F0000, 0x0F8000 },
  287. .host_ssp_offset = { 0x0E8000, 0x0E9000 },
  288. .dram_mask = LPT_VDRTCTL0_DSRAMPGE_MASK,
  289. .iram_mask = LPT_VDRTCTL0_ISRAMPGE_MASK,
  290. .d3srampgd_bit = LPT_VDRTCTL0_D3SRAMPGD,
  291. .d3pgd_bit = LPT_VDRTCTL0_D3PGD,
  292. .pll_shutdown = lpt_dsp_pll_shutdown,
  293. };
  294. static struct catpt_spec wpt_desc = {
  295. .machines = wpt_machines,
  296. .core_id = 0x02,
  297. .host_dram_offset = 0x000000,
  298. .host_iram_offset = 0x0A0000,
  299. .host_shim_offset = 0x0FB000,
  300. .host_dma_offset = { 0x0FE000, 0x0FF000 },
  301. .host_ssp_offset = { 0x0FC000, 0x0FD000 },
  302. .dram_mask = WPT_VDRTCTL0_DSRAMPGE_MASK,
  303. .iram_mask = WPT_VDRTCTL0_ISRAMPGE_MASK,
  304. .d3srampgd_bit = WPT_VDRTCTL0_D3SRAMPGD,
  305. .d3pgd_bit = WPT_VDRTCTL0_D3PGD,
  306. .pll_shutdown = wpt_dsp_pll_shutdown,
  307. };
  308. static const struct acpi_device_id catpt_ids[] = {
  309. { "INT33C8", (unsigned long)&lpt_desc },
  310. { "INT3438", (unsigned long)&wpt_desc },
  311. { }
  312. };
  313. MODULE_DEVICE_TABLE(acpi, catpt_ids);
  314. static struct platform_driver catpt_acpi_driver = {
  315. .probe = catpt_acpi_probe,
  316. .remove = catpt_acpi_remove,
  317. .driver = {
  318. .name = "intel_catpt",
  319. .acpi_match_table = catpt_ids,
  320. .pm = &catpt_dev_pm,
  321. .dev_groups = catpt_attr_groups,
  322. },
  323. };
  324. module_platform_driver(catpt_acpi_driver);
  325. MODULE_AUTHOR("Cezary Rojewski <[email protected]>");
  326. MODULE_DESCRIPTION("Intel LPT/WPT AudioDSP driver");
  327. MODULE_LICENSE("GPL v2");