acp-ipc.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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) 2021, 2023 Advanced Micro Devices, Inc.
  7. //
  8. // Authors: Balakishore Pati <Balakishore.pati@amd.com>
  9. // Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
  10. /* ACP-specific SOF IPC code */
  11. #include <linux/module.h>
  12. #include "../ops.h"
  13. #include "acp.h"
  14. #include "acp-dsp-offset.h"
  15. void acp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes)
  16. {
  17. memcpy_to_scratch(sdev, offset, message, bytes);
  18. }
  19. EXPORT_SYMBOL_NS(acp_mailbox_write, "SND_SOC_SOF_AMD_COMMON");
  20. void acp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes)
  21. {
  22. memcpy_from_scratch(sdev, offset, message, bytes);
  23. }
  24. EXPORT_SYMBOL_NS(acp_mailbox_read, "SND_SOC_SOF_AMD_COMMON");
  25. static void acpbus_trigger_host_to_dsp_swintr(struct acp_dev_data *adata)
  26. {
  27. struct snd_sof_dev *sdev = adata->dev;
  28. const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
  29. u32 swintr_trigger;
  30. swintr_trigger = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->dsp_intr_base +
  31. DSP_SW_INTR_TRIG_OFFSET);
  32. swintr_trigger |= 0x01;
  33. snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->dsp_intr_base + DSP_SW_INTR_TRIG_OFFSET,
  34. swintr_trigger);
  35. }
  36. static void acp_ipc_host_msg_set(struct snd_sof_dev *sdev)
  37. {
  38. unsigned int host_msg = sdev->debug_box.offset +
  39. offsetof(struct scratch_ipc_conf, sof_host_msg_write);
  40. snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + host_msg, 1);
  41. }
  42. static void acp_dsp_ipc_host_done(struct snd_sof_dev *sdev)
  43. {
  44. unsigned int dsp_msg = sdev->debug_box.offset +
  45. offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);
  46. snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg, 0);
  47. }
  48. static void acp_dsp_ipc_dsp_done(struct snd_sof_dev *sdev)
  49. {
  50. unsigned int dsp_ack = sdev->debug_box.offset +
  51. offsetof(struct scratch_ipc_conf, sof_dsp_ack_write);
  52. snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack, 0);
  53. }
  54. int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
  55. {
  56. struct acp_dev_data *adata = sdev->pdata->hw_pdata;
  57. const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
  58. unsigned int offset = sdev->host_box.offset;
  59. unsigned int count = ACP_HW_SEM_RETRY_COUNT;
  60. while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset)) {
  61. /* Wait until acquired HW Semaphore Lock or timeout*/
  62. count--;
  63. if (!count) {
  64. dev_err(sdev->dev, "%s: Failed to acquire HW lock\n", __func__);
  65. return -EINVAL;
  66. }
  67. }
  68. acp_mailbox_write(sdev, offset, msg->msg_data, msg->msg_size);
  69. acp_ipc_host_msg_set(sdev);
  70. /* Trigger host to dsp interrupt for the msg */
  71. acpbus_trigger_host_to_dsp_swintr(adata);
  72. /* Unlock or Release HW Semaphore */
  73. snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset, 0x0);
  74. return 0;
  75. }
  76. EXPORT_SYMBOL_NS(acp_sof_ipc_send_msg, "SND_SOC_SOF_AMD_COMMON");
  77. static void acp_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
  78. {
  79. struct snd_sof_ipc_msg *msg = sdev->msg;
  80. struct sof_ipc_reply reply;
  81. struct sof_ipc_cmd_hdr *hdr;
  82. unsigned int offset = sdev->host_box.offset;
  83. int ret = 0;
  84. /*
  85. * Sometimes, there is unexpected reply ipc arriving. The reply
  86. * ipc belongs to none of the ipcs sent from driver.
  87. * In this case, the driver must ignore the ipc.
  88. */
  89. if (!msg) {
  90. dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
  91. return;
  92. }
  93. hdr = msg->msg_data;
  94. if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE) ||
  95. hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) {
  96. /*
  97. * memory windows are powered off before sending IPC reply,
  98. * so we can't read the mailbox for CTX_SAVE and PM_GATE
  99. * replies.
  100. */
  101. reply.error = 0;
  102. reply.hdr.cmd = SOF_IPC_GLB_REPLY;
  103. reply.hdr.size = sizeof(reply);
  104. memcpy(msg->reply_data, &reply, sizeof(reply));
  105. goto out;
  106. }
  107. /* get IPC reply from DSP in the mailbox */
  108. acp_mailbox_read(sdev, offset, &reply, sizeof(reply));
  109. if (reply.error < 0) {
  110. memcpy(msg->reply_data, &reply, sizeof(reply));
  111. ret = reply.error;
  112. } else {
  113. /*
  114. * To support an IPC tx_message with a
  115. * reply_size set to zero.
  116. */
  117. if (!msg->reply_size)
  118. goto out;
  119. /* reply correct size ? */
  120. if (reply.hdr.size != msg->reply_size &&
  121. !(reply.hdr.cmd & SOF_IPC_GLB_PROBE)) {
  122. dev_err(sdev->dev, "reply expected %zu got %u bytes\n",
  123. msg->reply_size, reply.hdr.size);
  124. ret = -EINVAL;
  125. }
  126. /* read the message */
  127. if (msg->reply_size > 0)
  128. acp_mailbox_read(sdev, offset, msg->reply_data, msg->reply_size);
  129. }
  130. out:
  131. msg->reply_error = ret;
  132. }
  133. irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context)
  134. {
  135. struct snd_sof_dev *sdev = context;
  136. const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
  137. struct acp_dev_data *adata = sdev->pdata->hw_pdata;
  138. unsigned int dsp_msg_write = sdev->debug_box.offset +
  139. offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);
  140. unsigned int dsp_ack_write = sdev->debug_box.offset +
  141. offsetof(struct scratch_ipc_conf, sof_dsp_ack_write);
  142. bool ipc_irq = false;
  143. int dsp_msg, dsp_ack;
  144. unsigned int status;
  145. if (unlikely(sdev->first_boot && sdev->fw_state != SOF_FW_BOOT_COMPLETE)) {
  146. acp_mailbox_read(sdev, sdev->dsp_box.offset, &status, sizeof(status));
  147. if ((status & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
  148. snd_sof_dsp_panic(sdev, sdev->dsp_box.offset + sizeof(status),
  149. true);
  150. status = 0;
  151. acp_mailbox_write(sdev, sdev->dsp_box.offset, &status, sizeof(status));
  152. return IRQ_HANDLED;
  153. }
  154. snd_sof_ipc_msgs_rx(sdev);
  155. acp_dsp_ipc_host_done(sdev);
  156. return IRQ_HANDLED;
  157. }
  158. dsp_msg = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg_write);
  159. if (dsp_msg) {
  160. snd_sof_ipc_msgs_rx(sdev);
  161. acp_dsp_ipc_host_done(sdev);
  162. ipc_irq = true;
  163. }
  164. dsp_ack = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write);
  165. if (dsp_ack) {
  166. if (likely(sdev->fw_state == SOF_FW_BOOT_COMPLETE)) {
  167. guard(spinlock_irq)(&sdev->ipc_lock);
  168. /* handle immediate reply from DSP core */
  169. acp_dsp_ipc_get_reply(sdev);
  170. snd_sof_ipc_reply(sdev, 0);
  171. /* set the done bit */
  172. acp_dsp_ipc_dsp_done(sdev);
  173. } else {
  174. dev_dbg_ratelimited(sdev->dev, "IPC reply before FW_BOOT_COMPLETE: %#x\n",
  175. dsp_ack);
  176. }
  177. ipc_irq = true;
  178. }
  179. acp_mailbox_read(sdev, sdev->debug_box.offset, &status, sizeof(u32));
  180. if ((status & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
  181. snd_sof_dsp_panic(sdev, sdev->dsp_oops_offset, true);
  182. status = 0;
  183. acp_mailbox_write(sdev, sdev->debug_box.offset, &status, sizeof(status));
  184. return IRQ_HANDLED;
  185. }
  186. if (desc->probe_reg_offset) {
  187. u32 val;
  188. u32 posn;
  189. /* Probe register consists of two parts
  190. * (0-30) bit has cumulative position value
  191. * 31 bit is a synchronization flag between DSP and CPU
  192. * for the position update
  193. */
  194. val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->probe_reg_offset);
  195. if (val & PROBE_STATUS_BIT) {
  196. posn = val & ~PROBE_STATUS_BIT;
  197. if (adata->probe_stream) {
  198. /* Probe related posn value is of 31 bits limited to 2GB
  199. * once wrapped DSP won't send posn interrupt.
  200. */
  201. adata->probe_stream->cstream_posn = posn;
  202. snd_compr_fragment_elapsed(adata->probe_stream->cstream);
  203. snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->probe_reg_offset, posn);
  204. ipc_irq = true;
  205. }
  206. }
  207. }
  208. if (!ipc_irq)
  209. dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n");
  210. return IRQ_HANDLED;
  211. }
  212. EXPORT_SYMBOL_NS(acp_sof_ipc_irq_thread, "SND_SOC_SOF_AMD_COMMON");
  213. int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps,
  214. void *p, size_t sz)
  215. {
  216. unsigned int offset = sdev->dsp_box.offset;
  217. if (!sps || !sdev->stream_box.size) {
  218. acp_mailbox_read(sdev, offset, p, sz);
  219. } else {
  220. struct snd_pcm_substream *substream = sps->substream;
  221. struct acp_dsp_stream *stream;
  222. if (!substream || !substream->runtime)
  223. return -ESTRPIPE;
  224. stream = substream->runtime->private_data;
  225. if (!stream)
  226. return -ESTRPIPE;
  227. acp_mailbox_read(sdev, stream->posn_offset, p, sz);
  228. }
  229. return 0;
  230. }
  231. EXPORT_SYMBOL_NS(acp_sof_ipc_msg_data, "SND_SOC_SOF_AMD_COMMON");
  232. int acp_set_stream_data_offset(struct snd_sof_dev *sdev,
  233. struct snd_sof_pcm_stream *sps,
  234. size_t posn_offset)
  235. {
  236. struct snd_pcm_substream *substream = sps->substream;
  237. struct acp_dsp_stream *stream = substream->runtime->private_data;
  238. /* check for unaligned offset or overflow */
  239. if (posn_offset > sdev->stream_box.size ||
  240. posn_offset % sizeof(struct sof_ipc_stream_posn) != 0)
  241. return -EINVAL;
  242. stream->posn_offset = sdev->stream_box.offset + posn_offset;
  243. dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu",
  244. substream->stream, stream->posn_offset);
  245. return 0;
  246. }
  247. EXPORT_SYMBOL_NS(acp_set_stream_data_offset, "SND_SOC_SOF_AMD_COMMON");
  248. int acp_sof_ipc_get_mailbox_offset(struct snd_sof_dev *sdev)
  249. {
  250. const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
  251. return desc->sram_pte_offset;
  252. }
  253. EXPORT_SYMBOL_NS(acp_sof_ipc_get_mailbox_offset, "SND_SOC_SOF_AMD_COMMON");
  254. int acp_sof_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id)
  255. {
  256. return 0;
  257. }
  258. EXPORT_SYMBOL_NS(acp_sof_ipc_get_window_offset, "SND_SOC_SOF_AMD_COMMON");
  259. MODULE_DESCRIPTION("AMD ACP sof-ipc driver");