atom.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
  2. //
  3. // This file is provided under a dual BSD/GPLv2 license. When using or
  4. // redistributing this file, you may do so under either license.
  5. //
  6. // Copyright(c) 2018-2021 Intel Corporation. All rights reserved.
  7. //
  8. // Author: Liam Girdwood <[email protected]>
  9. //
  10. /*
  11. * Hardware interface for audio DSP on Atom devices
  12. */
  13. #include <linux/module.h>
  14. #include <sound/sof.h>
  15. #include <sound/sof/xtensa.h>
  16. #include <sound/soc-acpi.h>
  17. #include <sound/soc-acpi-intel-match.h>
  18. #include <sound/intel-dsp-config.h>
  19. #include "../ops.h"
  20. #include "shim.h"
  21. #include "atom.h"
  22. #include "../sof-acpi-dev.h"
  23. #include "../sof-audio.h"
  24. #include "../../intel/common/soc-intel-quirks.h"
  25. static void atom_host_done(struct snd_sof_dev *sdev);
  26. static void atom_dsp_done(struct snd_sof_dev *sdev);
  27. /*
  28. * Debug
  29. */
  30. static void atom_get_registers(struct snd_sof_dev *sdev,
  31. struct sof_ipc_dsp_oops_xtensa *xoops,
  32. struct sof_ipc_panic_info *panic_info,
  33. u32 *stack, size_t stack_words)
  34. {
  35. u32 offset = sdev->dsp_oops_offset;
  36. /* first read regsisters */
  37. sof_mailbox_read(sdev, offset, xoops, sizeof(*xoops));
  38. /* note: variable AR register array is not read */
  39. /* then get panic info */
  40. if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) {
  41. dev_err(sdev->dev, "invalid header size 0x%x. FW oops is bogus\n",
  42. xoops->arch_hdr.totalsize);
  43. return;
  44. }
  45. offset += xoops->arch_hdr.totalsize;
  46. sof_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info));
  47. /* then get the stack */
  48. offset += sizeof(*panic_info);
  49. sof_mailbox_read(sdev, offset, stack, stack_words * sizeof(u32));
  50. }
  51. void atom_dump(struct snd_sof_dev *sdev, u32 flags)
  52. {
  53. struct sof_ipc_dsp_oops_xtensa xoops;
  54. struct sof_ipc_panic_info panic_info;
  55. u32 stack[STACK_DUMP_SIZE];
  56. u64 status, panic, imrd, imrx;
  57. /* now try generic SOF status messages */
  58. status = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCD);
  59. panic = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX);
  60. atom_get_registers(sdev, &xoops, &panic_info, stack,
  61. STACK_DUMP_SIZE);
  62. sof_print_oops_and_stack(sdev, KERN_ERR, status, panic, &xoops,
  63. &panic_info, stack, STACK_DUMP_SIZE);
  64. /* provide some context for firmware debug */
  65. imrx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IMRX);
  66. imrd = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IMRD);
  67. dev_err(sdev->dev,
  68. "error: ipc host -> DSP: pending %s complete %s raw 0x%llx\n",
  69. (panic & SHIM_IPCX_BUSY) ? "yes" : "no",
  70. (panic & SHIM_IPCX_DONE) ? "yes" : "no", panic);
  71. dev_err(sdev->dev,
  72. "error: mask host: pending %s complete %s raw 0x%llx\n",
  73. (imrx & SHIM_IMRX_BUSY) ? "yes" : "no",
  74. (imrx & SHIM_IMRX_DONE) ? "yes" : "no", imrx);
  75. dev_err(sdev->dev,
  76. "error: ipc DSP -> host: pending %s complete %s raw 0x%llx\n",
  77. (status & SHIM_IPCD_BUSY) ? "yes" : "no",
  78. (status & SHIM_IPCD_DONE) ? "yes" : "no", status);
  79. dev_err(sdev->dev,
  80. "error: mask DSP: pending %s complete %s raw 0x%llx\n",
  81. (imrd & SHIM_IMRD_BUSY) ? "yes" : "no",
  82. (imrd & SHIM_IMRD_DONE) ? "yes" : "no", imrd);
  83. }
  84. EXPORT_SYMBOL_NS(atom_dump, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
  85. /*
  86. * IPC Doorbell IRQ handler and thread.
  87. */
  88. irqreturn_t atom_irq_handler(int irq, void *context)
  89. {
  90. struct snd_sof_dev *sdev = context;
  91. u64 ipcx, ipcd;
  92. int ret = IRQ_NONE;
  93. ipcx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX);
  94. ipcd = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCD);
  95. if (ipcx & SHIM_BYT_IPCX_DONE) {
  96. /* reply message from DSP, Mask Done interrupt first */
  97. snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR,
  98. SHIM_IMRX,
  99. SHIM_IMRX_DONE,
  100. SHIM_IMRX_DONE);
  101. ret = IRQ_WAKE_THREAD;
  102. }
  103. if (ipcd & SHIM_BYT_IPCD_BUSY) {
  104. /* new message from DSP, Mask Busy interrupt first */
  105. snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR,
  106. SHIM_IMRX,
  107. SHIM_IMRX_BUSY,
  108. SHIM_IMRX_BUSY);
  109. ret = IRQ_WAKE_THREAD;
  110. }
  111. return ret;
  112. }
  113. EXPORT_SYMBOL_NS(atom_irq_handler, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
  114. irqreturn_t atom_irq_thread(int irq, void *context)
  115. {
  116. struct snd_sof_dev *sdev = context;
  117. u64 ipcx, ipcd;
  118. ipcx = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCX);
  119. ipcd = snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_IPCD);
  120. /* reply message from DSP */
  121. if (ipcx & SHIM_BYT_IPCX_DONE) {
  122. spin_lock_irq(&sdev->ipc_lock);
  123. /*
  124. * handle immediate reply from DSP core. If the msg is
  125. * found, set done bit in cmd_done which is called at the
  126. * end of message processing function, else set it here
  127. * because the done bit can't be set in cmd_done function
  128. * which is triggered by msg
  129. */
  130. snd_sof_ipc_process_reply(sdev, ipcx);
  131. atom_dsp_done(sdev);
  132. spin_unlock_irq(&sdev->ipc_lock);
  133. }
  134. /* new message from DSP */
  135. if (ipcd & SHIM_BYT_IPCD_BUSY) {
  136. /* Handle messages from DSP Core */
  137. if ((ipcd & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
  138. snd_sof_dsp_panic(sdev, PANIC_OFFSET(ipcd) + MBOX_OFFSET,
  139. true);
  140. } else {
  141. snd_sof_ipc_msgs_rx(sdev);
  142. }
  143. atom_host_done(sdev);
  144. }
  145. return IRQ_HANDLED;
  146. }
  147. EXPORT_SYMBOL_NS(atom_irq_thread, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
  148. int atom_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
  149. {
  150. /* unmask and prepare to receive Done interrupt */
  151. snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IMRX,
  152. SHIM_IMRX_DONE, 0);
  153. /* send the message */
  154. sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
  155. msg->msg_size);
  156. snd_sof_dsp_write64(sdev, DSP_BAR, SHIM_IPCX, SHIM_BYT_IPCX_BUSY);
  157. return 0;
  158. }
  159. EXPORT_SYMBOL_NS(atom_send_msg, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
  160. int atom_get_mailbox_offset(struct snd_sof_dev *sdev)
  161. {
  162. return MBOX_OFFSET;
  163. }
  164. EXPORT_SYMBOL_NS(atom_get_mailbox_offset, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
  165. int atom_get_window_offset(struct snd_sof_dev *sdev, u32 id)
  166. {
  167. return MBOX_OFFSET;
  168. }
  169. EXPORT_SYMBOL_NS(atom_get_window_offset, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
  170. static void atom_host_done(struct snd_sof_dev *sdev)
  171. {
  172. /* clear BUSY bit and set DONE bit - accept new messages */
  173. snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IPCD,
  174. SHIM_BYT_IPCD_BUSY |
  175. SHIM_BYT_IPCD_DONE,
  176. SHIM_BYT_IPCD_DONE);
  177. /* unmask and prepare to receive next new message */
  178. snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IMRX,
  179. SHIM_IMRX_BUSY, 0);
  180. }
  181. static void atom_dsp_done(struct snd_sof_dev *sdev)
  182. {
  183. /* clear DONE bit - tell DSP we have completed */
  184. snd_sof_dsp_update_bits64_unlocked(sdev, DSP_BAR, SHIM_IPCX,
  185. SHIM_BYT_IPCX_DONE, 0);
  186. }
  187. /*
  188. * DSP control.
  189. */
  190. int atom_run(struct snd_sof_dev *sdev)
  191. {
  192. int tries = 10;
  193. /* release stall and wait to unstall */
  194. snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,
  195. SHIM_BYT_CSR_STALL, 0x0);
  196. while (tries--) {
  197. if (!(snd_sof_dsp_read64(sdev, DSP_BAR, SHIM_CSR) &
  198. SHIM_BYT_CSR_PWAITMODE))
  199. break;
  200. msleep(100);
  201. }
  202. if (tries < 0)
  203. return -ENODEV;
  204. /* return init core mask */
  205. return 1;
  206. }
  207. EXPORT_SYMBOL_NS(atom_run, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
  208. int atom_reset(struct snd_sof_dev *sdev)
  209. {
  210. /* put DSP into reset, set reset vector and stall */
  211. snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,
  212. SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL |
  213. SHIM_BYT_CSR_STALL,
  214. SHIM_BYT_CSR_RST | SHIM_BYT_CSR_VECTOR_SEL |
  215. SHIM_BYT_CSR_STALL);
  216. usleep_range(10, 15);
  217. /* take DSP out of reset and keep stalled for FW loading */
  218. snd_sof_dsp_update_bits64(sdev, DSP_BAR, SHIM_CSR,
  219. SHIM_BYT_CSR_RST, 0);
  220. return 0;
  221. }
  222. EXPORT_SYMBOL_NS(atom_reset, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
  223. static const char *fixup_tplg_name(struct snd_sof_dev *sdev,
  224. const char *sof_tplg_filename,
  225. const char *ssp_str)
  226. {
  227. const char *tplg_filename = NULL;
  228. const char *split_ext;
  229. char *filename, *tmp;
  230. filename = kstrdup(sof_tplg_filename, GFP_KERNEL);
  231. if (!filename)
  232. return NULL;
  233. /* this assumes a .tplg extension */
  234. tmp = filename;
  235. split_ext = strsep(&tmp, ".");
  236. if (split_ext)
  237. tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
  238. "%s-%s.tplg",
  239. split_ext, ssp_str);
  240. kfree(filename);
  241. return tplg_filename;
  242. }
  243. struct snd_soc_acpi_mach *atom_machine_select(struct snd_sof_dev *sdev)
  244. {
  245. struct snd_sof_pdata *sof_pdata = sdev->pdata;
  246. const struct sof_dev_desc *desc = sof_pdata->desc;
  247. struct snd_soc_acpi_mach *mach;
  248. struct platform_device *pdev;
  249. const char *tplg_filename;
  250. mach = snd_soc_acpi_find_machine(desc->machines);
  251. if (!mach) {
  252. dev_warn(sdev->dev, "warning: No matching ASoC machine driver found\n");
  253. return NULL;
  254. }
  255. pdev = to_platform_device(sdev->dev);
  256. if (soc_intel_is_byt_cr(pdev)) {
  257. dev_dbg(sdev->dev,
  258. "BYT-CR detected, SSP0 used instead of SSP2\n");
  259. tplg_filename = fixup_tplg_name(sdev,
  260. mach->sof_tplg_filename,
  261. "ssp0");
  262. } else {
  263. tplg_filename = mach->sof_tplg_filename;
  264. }
  265. if (!tplg_filename) {
  266. dev_dbg(sdev->dev,
  267. "error: no topology filename\n");
  268. return NULL;
  269. }
  270. sof_pdata->tplg_filename = tplg_filename;
  271. mach->mach_params.acpi_ipc_irq_index = desc->irqindex_host_ipc;
  272. return mach;
  273. }
  274. EXPORT_SYMBOL_NS(atom_machine_select, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
  275. /* Atom DAIs */
  276. struct snd_soc_dai_driver atom_dai[] = {
  277. {
  278. .name = "ssp0-port",
  279. .playback = {
  280. .channels_min = 1,
  281. .channels_max = 8,
  282. },
  283. .capture = {
  284. .channels_min = 1,
  285. .channels_max = 8,
  286. },
  287. },
  288. {
  289. .name = "ssp1-port",
  290. .playback = {
  291. .channels_min = 1,
  292. .channels_max = 8,
  293. },
  294. .capture = {
  295. .channels_min = 1,
  296. .channels_max = 8,
  297. },
  298. },
  299. {
  300. .name = "ssp2-port",
  301. .playback = {
  302. .channels_min = 1,
  303. .channels_max = 8,
  304. },
  305. .capture = {
  306. .channels_min = 1,
  307. .channels_max = 8,
  308. }
  309. },
  310. {
  311. .name = "ssp3-port",
  312. .playback = {
  313. .channels_min = 1,
  314. .channels_max = 8,
  315. },
  316. .capture = {
  317. .channels_min = 1,
  318. .channels_max = 8,
  319. },
  320. },
  321. {
  322. .name = "ssp4-port",
  323. .playback = {
  324. .channels_min = 1,
  325. .channels_max = 8,
  326. },
  327. .capture = {
  328. .channels_min = 1,
  329. .channels_max = 8,
  330. },
  331. },
  332. {
  333. .name = "ssp5-port",
  334. .playback = {
  335. .channels_min = 1,
  336. .channels_max = 8,
  337. },
  338. .capture = {
  339. .channels_min = 1,
  340. .channels_max = 8,
  341. },
  342. },
  343. };
  344. EXPORT_SYMBOL_NS(atom_dai, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
  345. void atom_set_mach_params(struct snd_soc_acpi_mach *mach,
  346. struct snd_sof_dev *sdev)
  347. {
  348. struct snd_sof_pdata *pdata = sdev->pdata;
  349. const struct sof_dev_desc *desc = pdata->desc;
  350. struct snd_soc_acpi_mach_params *mach_params;
  351. mach_params = &mach->mach_params;
  352. mach_params->platform = dev_name(sdev->dev);
  353. mach_params->num_dai_drivers = desc->ops->num_drv;
  354. mach_params->dai_drivers = desc->ops->drv;
  355. }
  356. EXPORT_SYMBOL_NS(atom_set_mach_params, SND_SOC_SOF_INTEL_ATOM_HIFI_EP);
  357. MODULE_LICENSE("Dual BSD/GPL");