xref: /openbmc/linux/sound/soc/sof/amd/acp-ipc.c (revision d88e0493)
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 
66 	acp_mailbox_write(sdev, offset, msg->msg_data, msg->msg_size);
67 	acp_ipc_host_msg_set(sdev);
68 
69 	/* Trigger host to dsp interrupt for the msg */
70 	acpbus_trigger_host_to_dsp_swintr(adata);
71 	return 0;
72 }
73 EXPORT_SYMBOL_NS(acp_sof_ipc_send_msg, SND_SOC_SOF_AMD_COMMON);
74 
75 static void acp_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
76 {
77 	struct snd_sof_ipc_msg *msg = sdev->msg;
78 	struct sof_ipc_reply reply;
79 	struct sof_ipc_cmd_hdr *hdr;
80 	unsigned int offset = offsetof(struct scratch_ipc_conf, sof_in_box);
81 	int ret = 0;
82 
83        /*
84 	* Sometimes, there is unexpected reply ipc arriving. The reply
85 	* ipc belongs to none of the ipcs sent from driver.
86 	* In this case, the driver must ignore the ipc.
87 	*/
88 	if (!msg) {
89 		dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
90 		return;
91 	}
92 	hdr = msg->msg_data;
93 	if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE) ||
94 	    hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) {
95 		/*
96 		 * memory windows are powered off before sending IPC reply,
97 		 * so we can't read the mailbox for CTX_SAVE and PM_GATE
98 		 * replies.
99 		 */
100 		reply.error = 0;
101 		reply.hdr.cmd = SOF_IPC_GLB_REPLY;
102 		reply.hdr.size = sizeof(reply);
103 		memcpy(msg->reply_data, &reply, sizeof(reply));
104 		goto out;
105 	}
106 	/* get IPC reply from DSP in the mailbox */
107 	acp_mailbox_read(sdev, offset, &reply, sizeof(reply));
108 	if (reply.error < 0) {
109 		memcpy(msg->reply_data, &reply, sizeof(reply));
110 		ret = reply.error;
111 	} else {
112 		/* reply correct size ? */
113 		if (reply.hdr.size != msg->reply_size &&
114 		    !(reply.hdr.cmd & SOF_IPC_GLB_PROBE)) {
115 			dev_err(sdev->dev, "reply expected %zu got %u bytes\n",
116 				msg->reply_size, reply.hdr.size);
117 			ret = -EINVAL;
118 		}
119 		/* read the message */
120 		if (msg->reply_size > 0)
121 			acp_mailbox_read(sdev, offset, msg->reply_data, msg->reply_size);
122 	}
123 out:
124 	msg->reply_error = ret;
125 }
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 = offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);
131 	unsigned int dsp_ack_write = offsetof(struct scratch_ipc_conf, sof_dsp_ack_write);
132 	bool ipc_irq = false;
133 	int dsp_msg, dsp_ack;
134 
135 	dsp_msg = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg_write);
136 	if (dsp_msg) {
137 		snd_sof_ipc_msgs_rx(sdev);
138 		acp_dsp_ipc_host_done(sdev);
139 		ipc_irq = true;
140 	}
141 
142 	dsp_ack = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write);
143 	if (dsp_ack) {
144 		spin_lock_irq(&sdev->ipc_lock);
145 		/* handle immediate reply from DSP core */
146 		acp_dsp_ipc_get_reply(sdev);
147 		snd_sof_ipc_reply(sdev, 0);
148 		/* set the done bit */
149 		acp_dsp_ipc_dsp_done(sdev);
150 		spin_unlock_irq(&sdev->ipc_lock);
151 		ipc_irq = true;
152 	}
153 
154 	if (!ipc_irq)
155 		dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n");
156 
157 	return IRQ_HANDLED;
158 }
159 EXPORT_SYMBOL_NS(acp_sof_ipc_irq_thread, SND_SOC_SOF_AMD_COMMON);
160 
161 int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
162 			 void *p, size_t sz)
163 {
164 	unsigned int offset = offsetof(struct scratch_ipc_conf, sof_out_box);
165 
166 	if (!substream || !sdev->stream_box.size)
167 		acp_mailbox_read(sdev, offset, p, sz);
168 
169 	return 0;
170 }
171 EXPORT_SYMBOL_NS(acp_sof_ipc_msg_data, SND_SOC_SOF_AMD_COMMON);
172 
173 int acp_sof_ipc_pcm_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
174 			   const struct sof_ipc_pcm_params_reply *reply)
175 {
176 	/* TODO: Implement stream hw params to validate stream offset */
177 	return 0;
178 }
179 EXPORT_SYMBOL_NS(acp_sof_ipc_pcm_params, SND_SOC_SOF_AMD_COMMON);
180 
181 int acp_sof_ipc_get_mailbox_offset(struct snd_sof_dev *sdev)
182 {
183 	return ACP_SCRATCH_MEMORY_ADDRESS;
184 }
185 EXPORT_SYMBOL_NS(acp_sof_ipc_get_mailbox_offset, SND_SOC_SOF_AMD_COMMON);
186 
187 MODULE_DESCRIPTION("AMD ACP sof-ipc driver");
188