xref: /linux/sound/soc/sof/amd/acp-ipc.c (revision 24bce201d79807b668bf9d9e0aca801c5c0d5f78)
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 <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 	u32 swintr_trigger;
34 
35 	swintr_trigger = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW_INTR_TRIG);
36 	swintr_trigger |= 0x01;
37 	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SW_INTR_TRIG, swintr_trigger);
38 }
39 
40 static void acp_ipc_host_msg_set(struct snd_sof_dev *sdev)
41 {
42 	unsigned int host_msg = offsetof(struct scratch_ipc_conf, sof_host_msg_write);
43 
44 	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + host_msg, 1);
45 }
46 
47 static void acp_dsp_ipc_host_done(struct snd_sof_dev *sdev)
48 {
49 	unsigned int dsp_msg = offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);
50 
51 	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg, 0);
52 }
53 
54 static void acp_dsp_ipc_dsp_done(struct snd_sof_dev *sdev)
55 {
56 	unsigned int dsp_ack = offsetof(struct scratch_ipc_conf, sof_dsp_ack_write);
57 
58 	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack, 0);
59 }
60 
61 int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
62 {
63 	struct acp_dev_data *adata = sdev->pdata->hw_pdata;
64 	unsigned int offset = offsetof(struct scratch_ipc_conf, sof_in_box);
65 	unsigned int count = ACP_HW_SEM_RETRY_COUNT;
66 
67 	while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_AXI2DAGB_SEM_0)) {
68 		/* Wait until acquired HW Semaphore Lock or timeout*/
69 		count--;
70 		if (!count) {
71 			dev_err(sdev->dev, "%s: Failed to acquire HW lock\n", __func__);
72 			return -EINVAL;
73 		}
74 	}
75 
76 	acp_mailbox_write(sdev, offset, msg->msg_data, msg->msg_size);
77 	acp_ipc_host_msg_set(sdev);
78 
79 	/* Trigger host to dsp interrupt for the msg */
80 	acpbus_trigger_host_to_dsp_swintr(adata);
81 
82 	/* Unlock or Release HW Semaphore */
83 	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_AXI2DAGB_SEM_0, 0x0);
84 
85 	return 0;
86 }
87 EXPORT_SYMBOL_NS(acp_sof_ipc_send_msg, SND_SOC_SOF_AMD_COMMON);
88 
89 static void acp_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
90 {
91 	struct snd_sof_ipc_msg *msg = sdev->msg;
92 	struct sof_ipc_reply reply;
93 	struct sof_ipc_cmd_hdr *hdr;
94 	unsigned int offset = offsetof(struct scratch_ipc_conf, sof_in_box);
95 	int ret = 0;
96 
97        /*
98 	* Sometimes, there is unexpected reply ipc arriving. The reply
99 	* ipc belongs to none of the ipcs sent from driver.
100 	* In this case, the driver must ignore the ipc.
101 	*/
102 	if (!msg) {
103 		dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
104 		return;
105 	}
106 	hdr = msg->msg_data;
107 	if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE) ||
108 	    hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) {
109 		/*
110 		 * memory windows are powered off before sending IPC reply,
111 		 * so we can't read the mailbox for CTX_SAVE and PM_GATE
112 		 * replies.
113 		 */
114 		reply.error = 0;
115 		reply.hdr.cmd = SOF_IPC_GLB_REPLY;
116 		reply.hdr.size = sizeof(reply);
117 		memcpy(msg->reply_data, &reply, sizeof(reply));
118 		goto out;
119 	}
120 	/* get IPC reply from DSP in the mailbox */
121 	acp_mailbox_read(sdev, offset, &reply, sizeof(reply));
122 	if (reply.error < 0) {
123 		memcpy(msg->reply_data, &reply, sizeof(reply));
124 		ret = reply.error;
125 	} else {
126 		/* reply correct size ? */
127 		if (reply.hdr.size != msg->reply_size &&
128 		    !(reply.hdr.cmd & SOF_IPC_GLB_PROBE)) {
129 			dev_err(sdev->dev, "reply expected %zu got %u bytes\n",
130 				msg->reply_size, reply.hdr.size);
131 			ret = -EINVAL;
132 		}
133 		/* read the message */
134 		if (msg->reply_size > 0)
135 			acp_mailbox_read(sdev, offset, msg->reply_data, msg->reply_size);
136 	}
137 out:
138 	msg->reply_error = ret;
139 }
140 
141 irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context)
142 {
143 	struct snd_sof_dev *sdev = context;
144 	unsigned int dsp_msg_write = offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);
145 	unsigned int dsp_ack_write = offsetof(struct scratch_ipc_conf, sof_dsp_ack_write);
146 	bool ipc_irq = false;
147 	int dsp_msg, dsp_ack;
148 
149 	dsp_msg = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg_write);
150 	if (dsp_msg) {
151 		snd_sof_ipc_msgs_rx(sdev);
152 		acp_dsp_ipc_host_done(sdev);
153 		ipc_irq = true;
154 	}
155 
156 	dsp_ack = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write);
157 	if (dsp_ack) {
158 		spin_lock_irq(&sdev->ipc_lock);
159 		/* handle immediate reply from DSP core */
160 		acp_dsp_ipc_get_reply(sdev);
161 		snd_sof_ipc_reply(sdev, 0);
162 		/* set the done bit */
163 		acp_dsp_ipc_dsp_done(sdev);
164 		spin_unlock_irq(&sdev->ipc_lock);
165 		ipc_irq = true;
166 	}
167 
168 	if (!ipc_irq)
169 		dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n");
170 
171 	return IRQ_HANDLED;
172 }
173 EXPORT_SYMBOL_NS(acp_sof_ipc_irq_thread, SND_SOC_SOF_AMD_COMMON);
174 
175 int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
176 			 void *p, size_t sz)
177 {
178 	unsigned int offset = offsetof(struct scratch_ipc_conf, sof_out_box);
179 
180 	if (!substream || !sdev->stream_box.size)
181 		acp_mailbox_read(sdev, offset, p, sz);
182 
183 	return 0;
184 }
185 EXPORT_SYMBOL_NS(acp_sof_ipc_msg_data, SND_SOC_SOF_AMD_COMMON);
186 
187 int acp_sof_ipc_get_mailbox_offset(struct snd_sof_dev *sdev)
188 {
189 	return ACP_SCRATCH_MEMORY_ADDRESS;
190 }
191 EXPORT_SYMBOL_NS(acp_sof_ipc_get_mailbox_offset, SND_SOC_SOF_AMD_COMMON);
192 
193 MODULE_DESCRIPTION("AMD ACP sof-ipc driver");
194