acp-ipc.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  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 Advanced Micro Devices, Inc.
  7. //
  8. // Authors: Balakishore Pati <[email protected]>
  9. // Ajit Kumar Pandey <[email protected]>
  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. /* reply correct size ? */
  114. if (reply.hdr.size != msg->reply_size &&
  115. !(reply.hdr.cmd & SOF_IPC_GLB_PROBE)) {
  116. dev_err(sdev->dev, "reply expected %zu got %u bytes\n",
  117. msg->reply_size, reply.hdr.size);
  118. ret = -EINVAL;
  119. }
  120. /* read the message */
  121. if (msg->reply_size > 0)
  122. acp_mailbox_read(sdev, offset, msg->reply_data, msg->reply_size);
  123. }
  124. out:
  125. msg->reply_error = ret;
  126. }
  127. irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context)
  128. {
  129. struct snd_sof_dev *sdev = context;
  130. unsigned int dsp_msg_write = sdev->debug_box.offset +
  131. offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);
  132. unsigned int dsp_ack_write = sdev->debug_box.offset +
  133. offsetof(struct scratch_ipc_conf, sof_dsp_ack_write);
  134. bool ipc_irq = false;
  135. int dsp_msg, dsp_ack;
  136. if (sdev->first_boot && sdev->fw_state != SOF_FW_BOOT_COMPLETE) {
  137. snd_sof_ipc_msgs_rx(sdev);
  138. acp_dsp_ipc_host_done(sdev);
  139. return IRQ_HANDLED;
  140. }
  141. dsp_msg = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg_write);
  142. if (dsp_msg) {
  143. snd_sof_ipc_msgs_rx(sdev);
  144. acp_dsp_ipc_host_done(sdev);
  145. ipc_irq = true;
  146. }
  147. dsp_ack = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write);
  148. if (dsp_ack) {
  149. spin_lock_irq(&sdev->ipc_lock);
  150. /* handle immediate reply from DSP core */
  151. acp_dsp_ipc_get_reply(sdev);
  152. snd_sof_ipc_reply(sdev, 0);
  153. /* set the done bit */
  154. acp_dsp_ipc_dsp_done(sdev);
  155. spin_unlock_irq(&sdev->ipc_lock);
  156. ipc_irq = true;
  157. }
  158. if (!ipc_irq)
  159. dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n");
  160. return IRQ_HANDLED;
  161. }
  162. EXPORT_SYMBOL_NS(acp_sof_ipc_irq_thread, SND_SOC_SOF_AMD_COMMON);
  163. int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
  164. void *p, size_t sz)
  165. {
  166. unsigned int offset = sdev->dsp_box.offset;
  167. if (!substream || !sdev->stream_box.size)
  168. acp_mailbox_read(sdev, offset, p, sz);
  169. return 0;
  170. }
  171. EXPORT_SYMBOL_NS(acp_sof_ipc_msg_data, SND_SOC_SOF_AMD_COMMON);
  172. int acp_sof_ipc_get_mailbox_offset(struct snd_sof_dev *sdev)
  173. {
  174. const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
  175. return desc->sram_pte_offset;
  176. }
  177. EXPORT_SYMBOL_NS(acp_sof_ipc_get_mailbox_offset, SND_SOC_SOF_AMD_COMMON);
  178. int acp_sof_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id)
  179. {
  180. return 0;
  181. }
  182. EXPORT_SYMBOL_NS(acp_sof_ipc_get_window_offset, SND_SOC_SOF_AMD_COMMON);
  183. MODULE_DESCRIPTION("AMD ACP sof-ipc driver");