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