xref: /openbmc/linux/sound/soc/sof/ipc.c (revision 53e0c72d98ba3eae314c32476103eac47612aa58)
1*53e0c72dSLiam Girdwood // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
2*53e0c72dSLiam Girdwood //
3*53e0c72dSLiam Girdwood // This file is provided under a dual BSD/GPLv2 license.  When using or
4*53e0c72dSLiam Girdwood // redistributing this file, you may do so under either license.
5*53e0c72dSLiam Girdwood //
6*53e0c72dSLiam Girdwood // Copyright(c) 2018 Intel Corporation. All rights reserved.
7*53e0c72dSLiam Girdwood //
8*53e0c72dSLiam Girdwood // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
9*53e0c72dSLiam Girdwood //
10*53e0c72dSLiam Girdwood // Generic IPC layer that can work over MMIO and SPI/I2C. PHY layer provided
11*53e0c72dSLiam Girdwood // by platform driver code.
12*53e0c72dSLiam Girdwood //
13*53e0c72dSLiam Girdwood 
14*53e0c72dSLiam Girdwood #include <linux/mutex.h>
15*53e0c72dSLiam Girdwood #include <linux/types.h>
16*53e0c72dSLiam Girdwood 
17*53e0c72dSLiam Girdwood #include "sof-priv.h"
18*53e0c72dSLiam Girdwood #include "ops.h"
19*53e0c72dSLiam Girdwood 
20*53e0c72dSLiam Girdwood /*
21*53e0c72dSLiam Girdwood  * IPC message default size and timeout (ms).
22*53e0c72dSLiam Girdwood  * TODO: allow platforms to set size and timeout.
23*53e0c72dSLiam Girdwood  */
24*53e0c72dSLiam Girdwood #define IPC_TIMEOUT_MS		300
25*53e0c72dSLiam Girdwood 
26*53e0c72dSLiam Girdwood static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id);
27*53e0c72dSLiam Girdwood static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd);
28*53e0c72dSLiam Girdwood 
29*53e0c72dSLiam Girdwood /*
30*53e0c72dSLiam Girdwood  * IPC message Tx/Rx message handling.
31*53e0c72dSLiam Girdwood  */
32*53e0c72dSLiam Girdwood 
33*53e0c72dSLiam Girdwood /* SOF generic IPC data */
34*53e0c72dSLiam Girdwood struct snd_sof_ipc {
35*53e0c72dSLiam Girdwood 	struct snd_sof_dev *sdev;
36*53e0c72dSLiam Girdwood 
37*53e0c72dSLiam Girdwood 	/* protects messages and the disable flag */
38*53e0c72dSLiam Girdwood 	struct mutex tx_mutex;
39*53e0c72dSLiam Girdwood 	/* disables further sending of ipc's */
40*53e0c72dSLiam Girdwood 	bool disable_ipc_tx;
41*53e0c72dSLiam Girdwood 
42*53e0c72dSLiam Girdwood 	struct snd_sof_ipc_msg msg;
43*53e0c72dSLiam Girdwood };
44*53e0c72dSLiam Girdwood 
45*53e0c72dSLiam Girdwood struct sof_ipc_ctrl_data_params {
46*53e0c72dSLiam Girdwood 	size_t msg_bytes;
47*53e0c72dSLiam Girdwood 	size_t hdr_bytes;
48*53e0c72dSLiam Girdwood 	size_t pl_size;
49*53e0c72dSLiam Girdwood 	size_t elems;
50*53e0c72dSLiam Girdwood 	u32 num_msg;
51*53e0c72dSLiam Girdwood 	u8 *src;
52*53e0c72dSLiam Girdwood 	u8 *dst;
53*53e0c72dSLiam Girdwood };
54*53e0c72dSLiam Girdwood 
55*53e0c72dSLiam Girdwood #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC)
56*53e0c72dSLiam Girdwood static void ipc_log_header(struct device *dev, u8 *text, u32 cmd)
57*53e0c72dSLiam Girdwood {
58*53e0c72dSLiam Girdwood 	u8 *str;
59*53e0c72dSLiam Girdwood 	u8 *str2 = NULL;
60*53e0c72dSLiam Girdwood 	u32 glb;
61*53e0c72dSLiam Girdwood 	u32 type;
62*53e0c72dSLiam Girdwood 
63*53e0c72dSLiam Girdwood 	glb = cmd & SOF_GLB_TYPE_MASK;
64*53e0c72dSLiam Girdwood 	type = cmd & SOF_CMD_TYPE_MASK;
65*53e0c72dSLiam Girdwood 
66*53e0c72dSLiam Girdwood 	switch (glb) {
67*53e0c72dSLiam Girdwood 	case SOF_IPC_GLB_REPLY:
68*53e0c72dSLiam Girdwood 		str = "GLB_REPLY"; break;
69*53e0c72dSLiam Girdwood 	case SOF_IPC_GLB_COMPOUND:
70*53e0c72dSLiam Girdwood 		str = "GLB_COMPOUND"; break;
71*53e0c72dSLiam Girdwood 	case SOF_IPC_GLB_TPLG_MSG:
72*53e0c72dSLiam Girdwood 		str = "GLB_TPLG_MSG";
73*53e0c72dSLiam Girdwood 		switch (type) {
74*53e0c72dSLiam Girdwood 		case SOF_IPC_TPLG_COMP_NEW:
75*53e0c72dSLiam Girdwood 			str2 = "COMP_NEW"; break;
76*53e0c72dSLiam Girdwood 		case SOF_IPC_TPLG_COMP_FREE:
77*53e0c72dSLiam Girdwood 			str2 = "COMP_FREE"; break;
78*53e0c72dSLiam Girdwood 		case SOF_IPC_TPLG_COMP_CONNECT:
79*53e0c72dSLiam Girdwood 			str2 = "COMP_CONNECT"; break;
80*53e0c72dSLiam Girdwood 		case SOF_IPC_TPLG_PIPE_NEW:
81*53e0c72dSLiam Girdwood 			str2 = "PIPE_NEW"; break;
82*53e0c72dSLiam Girdwood 		case SOF_IPC_TPLG_PIPE_FREE:
83*53e0c72dSLiam Girdwood 			str2 = "PIPE_FREE"; break;
84*53e0c72dSLiam Girdwood 		case SOF_IPC_TPLG_PIPE_CONNECT:
85*53e0c72dSLiam Girdwood 			str2 = "PIPE_CONNECT"; break;
86*53e0c72dSLiam Girdwood 		case SOF_IPC_TPLG_PIPE_COMPLETE:
87*53e0c72dSLiam Girdwood 			str2 = "PIPE_COMPLETE"; break;
88*53e0c72dSLiam Girdwood 		case SOF_IPC_TPLG_BUFFER_NEW:
89*53e0c72dSLiam Girdwood 			str2 = "BUFFER_NEW"; break;
90*53e0c72dSLiam Girdwood 		case SOF_IPC_TPLG_BUFFER_FREE:
91*53e0c72dSLiam Girdwood 			str2 = "BUFFER_FREE"; break;
92*53e0c72dSLiam Girdwood 		default:
93*53e0c72dSLiam Girdwood 			str2 = "unknown type"; break;
94*53e0c72dSLiam Girdwood 		}
95*53e0c72dSLiam Girdwood 		break;
96*53e0c72dSLiam Girdwood 	case SOF_IPC_GLB_PM_MSG:
97*53e0c72dSLiam Girdwood 		str = "GLB_PM_MSG";
98*53e0c72dSLiam Girdwood 		switch (type) {
99*53e0c72dSLiam Girdwood 		case SOF_IPC_PM_CTX_SAVE:
100*53e0c72dSLiam Girdwood 			str2 = "CTX_SAVE"; break;
101*53e0c72dSLiam Girdwood 		case SOF_IPC_PM_CTX_RESTORE:
102*53e0c72dSLiam Girdwood 			str2 = "CTX_RESTORE"; break;
103*53e0c72dSLiam Girdwood 		case SOF_IPC_PM_CTX_SIZE:
104*53e0c72dSLiam Girdwood 			str2 = "CTX_SIZE"; break;
105*53e0c72dSLiam Girdwood 		case SOF_IPC_PM_CLK_SET:
106*53e0c72dSLiam Girdwood 			str2 = "CLK_SET"; break;
107*53e0c72dSLiam Girdwood 		case SOF_IPC_PM_CLK_GET:
108*53e0c72dSLiam Girdwood 			str2 = "CLK_GET"; break;
109*53e0c72dSLiam Girdwood 		case SOF_IPC_PM_CLK_REQ:
110*53e0c72dSLiam Girdwood 			str2 = "CLK_REQ"; break;
111*53e0c72dSLiam Girdwood 		case SOF_IPC_PM_CORE_ENABLE:
112*53e0c72dSLiam Girdwood 			str2 = "CORE_ENABLE"; break;
113*53e0c72dSLiam Girdwood 		default:
114*53e0c72dSLiam Girdwood 			str2 = "unknown type"; break;
115*53e0c72dSLiam Girdwood 		}
116*53e0c72dSLiam Girdwood 		break;
117*53e0c72dSLiam Girdwood 	case SOF_IPC_GLB_COMP_MSG:
118*53e0c72dSLiam Girdwood 		str = "GLB_COMP_MSG: SET_VALUE";
119*53e0c72dSLiam Girdwood 		switch (type) {
120*53e0c72dSLiam Girdwood 		case SOF_IPC_COMP_SET_VALUE:
121*53e0c72dSLiam Girdwood 			str2 = "SET_VALUE"; break;
122*53e0c72dSLiam Girdwood 		case SOF_IPC_COMP_GET_VALUE:
123*53e0c72dSLiam Girdwood 			str2 = "GET_VALUE"; break;
124*53e0c72dSLiam Girdwood 		case SOF_IPC_COMP_SET_DATA:
125*53e0c72dSLiam Girdwood 			str2 = "SET_DATA"; break;
126*53e0c72dSLiam Girdwood 		case SOF_IPC_COMP_GET_DATA:
127*53e0c72dSLiam Girdwood 			str2 = "GET_DATA"; break;
128*53e0c72dSLiam Girdwood 		default:
129*53e0c72dSLiam Girdwood 			str2 = "unknown type"; break;
130*53e0c72dSLiam Girdwood 		}
131*53e0c72dSLiam Girdwood 		break;
132*53e0c72dSLiam Girdwood 	case SOF_IPC_GLB_STREAM_MSG:
133*53e0c72dSLiam Girdwood 		str = "GLB_STREAM_MSG";
134*53e0c72dSLiam Girdwood 		switch (type) {
135*53e0c72dSLiam Girdwood 		case SOF_IPC_STREAM_PCM_PARAMS:
136*53e0c72dSLiam Girdwood 			str2 = "PCM_PARAMS"; break;
137*53e0c72dSLiam Girdwood 		case SOF_IPC_STREAM_PCM_PARAMS_REPLY:
138*53e0c72dSLiam Girdwood 			str2 = "PCM_REPLY"; break;
139*53e0c72dSLiam Girdwood 		case SOF_IPC_STREAM_PCM_FREE:
140*53e0c72dSLiam Girdwood 			str2 = "PCM_FREE"; break;
141*53e0c72dSLiam Girdwood 		case SOF_IPC_STREAM_TRIG_START:
142*53e0c72dSLiam Girdwood 			str2 = "TRIG_START"; break;
143*53e0c72dSLiam Girdwood 		case SOF_IPC_STREAM_TRIG_STOP:
144*53e0c72dSLiam Girdwood 			str2 = "TRIG_STOP"; break;
145*53e0c72dSLiam Girdwood 		case SOF_IPC_STREAM_TRIG_PAUSE:
146*53e0c72dSLiam Girdwood 			str2 = "TRIG_PAUSE"; break;
147*53e0c72dSLiam Girdwood 		case SOF_IPC_STREAM_TRIG_RELEASE:
148*53e0c72dSLiam Girdwood 			str2 = "TRIG_RELEASE"; break;
149*53e0c72dSLiam Girdwood 		case SOF_IPC_STREAM_TRIG_DRAIN:
150*53e0c72dSLiam Girdwood 			str2 = "TRIG_DRAIN"; break;
151*53e0c72dSLiam Girdwood 		case SOF_IPC_STREAM_TRIG_XRUN:
152*53e0c72dSLiam Girdwood 			str2 = "TRIG_XRUN"; break;
153*53e0c72dSLiam Girdwood 		case SOF_IPC_STREAM_POSITION:
154*53e0c72dSLiam Girdwood 			str2 = "POSITION"; break;
155*53e0c72dSLiam Girdwood 		case SOF_IPC_STREAM_VORBIS_PARAMS:
156*53e0c72dSLiam Girdwood 			str2 = "VORBIS_PARAMS"; break;
157*53e0c72dSLiam Girdwood 		case SOF_IPC_STREAM_VORBIS_FREE:
158*53e0c72dSLiam Girdwood 			str2 = "VORBIS_FREE"; break;
159*53e0c72dSLiam Girdwood 		default:
160*53e0c72dSLiam Girdwood 			str2 = "unknown type"; break;
161*53e0c72dSLiam Girdwood 		}
162*53e0c72dSLiam Girdwood 		break;
163*53e0c72dSLiam Girdwood 	case SOF_IPC_FW_READY:
164*53e0c72dSLiam Girdwood 		str = "FW_READY"; break;
165*53e0c72dSLiam Girdwood 	case SOF_IPC_GLB_DAI_MSG:
166*53e0c72dSLiam Girdwood 		str = "GLB_DAI_MSG";
167*53e0c72dSLiam Girdwood 		switch (type) {
168*53e0c72dSLiam Girdwood 		case SOF_IPC_DAI_CONFIG:
169*53e0c72dSLiam Girdwood 			str2 = "CONFIG"; break;
170*53e0c72dSLiam Girdwood 		case SOF_IPC_DAI_LOOPBACK:
171*53e0c72dSLiam Girdwood 			str2 = "LOOPBACK"; break;
172*53e0c72dSLiam Girdwood 		default:
173*53e0c72dSLiam Girdwood 			str2 = "unknown type"; break;
174*53e0c72dSLiam Girdwood 		}
175*53e0c72dSLiam Girdwood 		break;
176*53e0c72dSLiam Girdwood 	case SOF_IPC_GLB_TRACE_MSG:
177*53e0c72dSLiam Girdwood 		str = "GLB_TRACE_MSG"; break;
178*53e0c72dSLiam Girdwood 	default:
179*53e0c72dSLiam Girdwood 		str = "unknown GLB command"; break;
180*53e0c72dSLiam Girdwood 	}
181*53e0c72dSLiam Girdwood 
182*53e0c72dSLiam Girdwood 	if (str2)
183*53e0c72dSLiam Girdwood 		dev_dbg(dev, "%s: 0x%x: %s: %s\n", text, cmd, str, str2);
184*53e0c72dSLiam Girdwood 	else
185*53e0c72dSLiam Girdwood 		dev_dbg(dev, "%s: 0x%x: %s\n", text, cmd, str);
186*53e0c72dSLiam Girdwood }
187*53e0c72dSLiam Girdwood #else
188*53e0c72dSLiam Girdwood static inline void ipc_log_header(struct device *dev, u8 *text, u32 cmd)
189*53e0c72dSLiam Girdwood {
190*53e0c72dSLiam Girdwood 	dev_dbg(dev, "%s: 0x%x\n", text, cmd);
191*53e0c72dSLiam Girdwood }
192*53e0c72dSLiam Girdwood #endif
193*53e0c72dSLiam Girdwood 
194*53e0c72dSLiam Girdwood /* wait for IPC message reply */
195*53e0c72dSLiam Girdwood static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg,
196*53e0c72dSLiam Girdwood 			void *reply_data)
197*53e0c72dSLiam Girdwood {
198*53e0c72dSLiam Girdwood 	struct snd_sof_dev *sdev = ipc->sdev;
199*53e0c72dSLiam Girdwood 	struct sof_ipc_cmd_hdr *hdr = msg->msg_data;
200*53e0c72dSLiam Girdwood 	int ret;
201*53e0c72dSLiam Girdwood 
202*53e0c72dSLiam Girdwood 	/* wait for DSP IPC completion */
203*53e0c72dSLiam Girdwood 	ret = wait_event_timeout(msg->waitq, msg->ipc_complete,
204*53e0c72dSLiam Girdwood 				 msecs_to_jiffies(IPC_TIMEOUT_MS));
205*53e0c72dSLiam Girdwood 
206*53e0c72dSLiam Girdwood 	if (ret == 0) {
207*53e0c72dSLiam Girdwood 		dev_err(sdev->dev, "error: ipc timed out for 0x%x size %d\n",
208*53e0c72dSLiam Girdwood 			hdr->cmd, hdr->size);
209*53e0c72dSLiam Girdwood 		snd_sof_dsp_dbg_dump(ipc->sdev, SOF_DBG_REGS | SOF_DBG_MBOX);
210*53e0c72dSLiam Girdwood 		snd_sof_trace_notify_for_error(ipc->sdev);
211*53e0c72dSLiam Girdwood 		ret = -ETIMEDOUT;
212*53e0c72dSLiam Girdwood 	} else {
213*53e0c72dSLiam Girdwood 		/* copy the data returned from DSP */
214*53e0c72dSLiam Girdwood 		ret = msg->reply_error;
215*53e0c72dSLiam Girdwood 		if (msg->reply_size)
216*53e0c72dSLiam Girdwood 			memcpy(reply_data, msg->reply_data, msg->reply_size);
217*53e0c72dSLiam Girdwood 		if (ret < 0)
218*53e0c72dSLiam Girdwood 			dev_err(sdev->dev, "error: ipc error for 0x%x size %zu\n",
219*53e0c72dSLiam Girdwood 				hdr->cmd, msg->reply_size);
220*53e0c72dSLiam Girdwood 		else
221*53e0c72dSLiam Girdwood 			ipc_log_header(sdev->dev, "ipc tx succeeded", hdr->cmd);
222*53e0c72dSLiam Girdwood 	}
223*53e0c72dSLiam Girdwood 
224*53e0c72dSLiam Girdwood 	return ret;
225*53e0c72dSLiam Girdwood }
226*53e0c72dSLiam Girdwood 
227*53e0c72dSLiam Girdwood /* send IPC message from host to DSP */
228*53e0c72dSLiam Girdwood static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header,
229*53e0c72dSLiam Girdwood 				       void *msg_data, size_t msg_bytes,
230*53e0c72dSLiam Girdwood 				       void *reply_data, size_t reply_bytes)
231*53e0c72dSLiam Girdwood {
232*53e0c72dSLiam Girdwood 	struct snd_sof_dev *sdev = ipc->sdev;
233*53e0c72dSLiam Girdwood 	struct snd_sof_ipc_msg *msg;
234*53e0c72dSLiam Girdwood 	int ret;
235*53e0c72dSLiam Girdwood 
236*53e0c72dSLiam Girdwood 	if (ipc->disable_ipc_tx)
237*53e0c72dSLiam Girdwood 		return -ENODEV;
238*53e0c72dSLiam Girdwood 
239*53e0c72dSLiam Girdwood 	/*
240*53e0c72dSLiam Girdwood 	 * The spin-lock is also still needed to protect message objects against
241*53e0c72dSLiam Girdwood 	 * other atomic contexts.
242*53e0c72dSLiam Girdwood 	 */
243*53e0c72dSLiam Girdwood 	spin_lock_irq(&sdev->ipc_lock);
244*53e0c72dSLiam Girdwood 
245*53e0c72dSLiam Girdwood 	/* initialise the message */
246*53e0c72dSLiam Girdwood 	msg = &ipc->msg;
247*53e0c72dSLiam Girdwood 
248*53e0c72dSLiam Girdwood 	msg->header = header;
249*53e0c72dSLiam Girdwood 	msg->msg_size = msg_bytes;
250*53e0c72dSLiam Girdwood 	msg->reply_size = reply_bytes;
251*53e0c72dSLiam Girdwood 	msg->reply_error = 0;
252*53e0c72dSLiam Girdwood 
253*53e0c72dSLiam Girdwood 	/* attach any data */
254*53e0c72dSLiam Girdwood 	if (msg_bytes)
255*53e0c72dSLiam Girdwood 		memcpy(msg->msg_data, msg_data, msg_bytes);
256*53e0c72dSLiam Girdwood 
257*53e0c72dSLiam Girdwood 	sdev->msg = msg;
258*53e0c72dSLiam Girdwood 
259*53e0c72dSLiam Girdwood 	ret = snd_sof_dsp_send_msg(sdev, msg);
260*53e0c72dSLiam Girdwood 	/* Next reply that we receive will be related to this message */
261*53e0c72dSLiam Girdwood 	if (!ret)
262*53e0c72dSLiam Girdwood 		msg->ipc_complete = false;
263*53e0c72dSLiam Girdwood 
264*53e0c72dSLiam Girdwood 	spin_unlock_irq(&sdev->ipc_lock);
265*53e0c72dSLiam Girdwood 
266*53e0c72dSLiam Girdwood 	if (ret < 0) {
267*53e0c72dSLiam Girdwood 		/* So far IPC TX never fails, consider making the above void */
268*53e0c72dSLiam Girdwood 		dev_err_ratelimited(sdev->dev,
269*53e0c72dSLiam Girdwood 				    "error: ipc tx failed with error %d\n",
270*53e0c72dSLiam Girdwood 				    ret);
271*53e0c72dSLiam Girdwood 		return ret;
272*53e0c72dSLiam Girdwood 	}
273*53e0c72dSLiam Girdwood 
274*53e0c72dSLiam Girdwood 	ipc_log_header(sdev->dev, "ipc tx", msg->header);
275*53e0c72dSLiam Girdwood 
276*53e0c72dSLiam Girdwood 	/* now wait for completion */
277*53e0c72dSLiam Girdwood 	if (!ret)
278*53e0c72dSLiam Girdwood 		ret = tx_wait_done(ipc, msg, reply_data);
279*53e0c72dSLiam Girdwood 
280*53e0c72dSLiam Girdwood 	return ret;
281*53e0c72dSLiam Girdwood }
282*53e0c72dSLiam Girdwood 
283*53e0c72dSLiam Girdwood /* send IPC message from host to DSP */
284*53e0c72dSLiam Girdwood int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header,
285*53e0c72dSLiam Girdwood 		       void *msg_data, size_t msg_bytes, void *reply_data,
286*53e0c72dSLiam Girdwood 		       size_t reply_bytes)
287*53e0c72dSLiam Girdwood {
288*53e0c72dSLiam Girdwood 	int ret;
289*53e0c72dSLiam Girdwood 
290*53e0c72dSLiam Girdwood 	if (msg_bytes > SOF_IPC_MSG_MAX_SIZE ||
291*53e0c72dSLiam Girdwood 	    reply_bytes > SOF_IPC_MSG_MAX_SIZE)
292*53e0c72dSLiam Girdwood 		return -ENOBUFS;
293*53e0c72dSLiam Girdwood 
294*53e0c72dSLiam Girdwood 	/* Serialise IPC TX */
295*53e0c72dSLiam Girdwood 	mutex_lock(&ipc->tx_mutex);
296*53e0c72dSLiam Girdwood 
297*53e0c72dSLiam Girdwood 	ret = sof_ipc_tx_message_unlocked(ipc, header, msg_data, msg_bytes,
298*53e0c72dSLiam Girdwood 					  reply_data, reply_bytes);
299*53e0c72dSLiam Girdwood 
300*53e0c72dSLiam Girdwood 	mutex_unlock(&ipc->tx_mutex);
301*53e0c72dSLiam Girdwood 
302*53e0c72dSLiam Girdwood 	return ret;
303*53e0c72dSLiam Girdwood }
304*53e0c72dSLiam Girdwood EXPORT_SYMBOL(sof_ipc_tx_message);
305*53e0c72dSLiam Girdwood 
306*53e0c72dSLiam Girdwood /* handle reply message from DSP */
307*53e0c72dSLiam Girdwood int snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id)
308*53e0c72dSLiam Girdwood {
309*53e0c72dSLiam Girdwood 	struct snd_sof_ipc_msg *msg = &sdev->ipc->msg;
310*53e0c72dSLiam Girdwood 	unsigned long flags;
311*53e0c72dSLiam Girdwood 
312*53e0c72dSLiam Girdwood 	/*
313*53e0c72dSLiam Girdwood 	 * Protect against a theoretical race with sof_ipc_tx_message(): if the
314*53e0c72dSLiam Girdwood 	 * DSP is fast enough to receive an IPC message, reply to it, and the
315*53e0c72dSLiam Girdwood 	 * host interrupt processing calls this function on a different core
316*53e0c72dSLiam Girdwood 	 * from the one, where the sending is taking place, the message might
317*53e0c72dSLiam Girdwood 	 * not yet be marked as expecting a reply.
318*53e0c72dSLiam Girdwood 	 */
319*53e0c72dSLiam Girdwood 	spin_lock_irqsave(&sdev->ipc_lock, flags);
320*53e0c72dSLiam Girdwood 
321*53e0c72dSLiam Girdwood 	if (msg->ipc_complete) {
322*53e0c72dSLiam Girdwood 		spin_unlock_irqrestore(&sdev->ipc_lock, flags);
323*53e0c72dSLiam Girdwood 		dev_err(sdev->dev, "error: no reply expected, received 0x%x",
324*53e0c72dSLiam Girdwood 			msg_id);
325*53e0c72dSLiam Girdwood 		return -EINVAL;
326*53e0c72dSLiam Girdwood 	}
327*53e0c72dSLiam Girdwood 
328*53e0c72dSLiam Girdwood 	/* wake up and return the error if we have waiters on this message ? */
329*53e0c72dSLiam Girdwood 	msg->ipc_complete = true;
330*53e0c72dSLiam Girdwood 	wake_up(&msg->waitq);
331*53e0c72dSLiam Girdwood 
332*53e0c72dSLiam Girdwood 	spin_unlock_irqrestore(&sdev->ipc_lock, flags);
333*53e0c72dSLiam Girdwood 
334*53e0c72dSLiam Girdwood 	return 0;
335*53e0c72dSLiam Girdwood }
336*53e0c72dSLiam Girdwood EXPORT_SYMBOL(snd_sof_ipc_reply);
337*53e0c72dSLiam Girdwood 
338*53e0c72dSLiam Girdwood /* DSP firmware has sent host a message  */
339*53e0c72dSLiam Girdwood void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
340*53e0c72dSLiam Girdwood {
341*53e0c72dSLiam Girdwood 	struct sof_ipc_cmd_hdr hdr;
342*53e0c72dSLiam Girdwood 	u32 cmd, type;
343*53e0c72dSLiam Girdwood 	int err = 0;
344*53e0c72dSLiam Girdwood 
345*53e0c72dSLiam Girdwood 	/* read back header */
346*53e0c72dSLiam Girdwood 	snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr));
347*53e0c72dSLiam Girdwood 	ipc_log_header(sdev->dev, "ipc rx", hdr.cmd);
348*53e0c72dSLiam Girdwood 
349*53e0c72dSLiam Girdwood 	cmd = hdr.cmd & SOF_GLB_TYPE_MASK;
350*53e0c72dSLiam Girdwood 	type = hdr.cmd & SOF_CMD_TYPE_MASK;
351*53e0c72dSLiam Girdwood 
352*53e0c72dSLiam Girdwood 	/* check message type */
353*53e0c72dSLiam Girdwood 	switch (cmd) {
354*53e0c72dSLiam Girdwood 	case SOF_IPC_GLB_REPLY:
355*53e0c72dSLiam Girdwood 		dev_err(sdev->dev, "error: ipc reply unknown\n");
356*53e0c72dSLiam Girdwood 		break;
357*53e0c72dSLiam Girdwood 	case SOF_IPC_FW_READY:
358*53e0c72dSLiam Girdwood 		/* check for FW boot completion */
359*53e0c72dSLiam Girdwood 		if (!sdev->boot_complete) {
360*53e0c72dSLiam Girdwood 			err = sof_ops(sdev)->fw_ready(sdev, cmd);
361*53e0c72dSLiam Girdwood 			if (err < 0) {
362*53e0c72dSLiam Girdwood 				/*
363*53e0c72dSLiam Girdwood 				 * this indicates a mismatch in ABI
364*53e0c72dSLiam Girdwood 				 * between the driver and fw
365*53e0c72dSLiam Girdwood 				 */
366*53e0c72dSLiam Girdwood 				dev_err(sdev->dev, "error: ABI mismatch %d\n",
367*53e0c72dSLiam Girdwood 					err);
368*53e0c72dSLiam Girdwood 			} else {
369*53e0c72dSLiam Girdwood 				/* firmware boot completed OK */
370*53e0c72dSLiam Girdwood 				sdev->boot_complete = true;
371*53e0c72dSLiam Girdwood 			}
372*53e0c72dSLiam Girdwood 
373*53e0c72dSLiam Girdwood 			/* wake up firmware loader */
374*53e0c72dSLiam Girdwood 			wake_up(&sdev->boot_wait);
375*53e0c72dSLiam Girdwood 		}
376*53e0c72dSLiam Girdwood 		break;
377*53e0c72dSLiam Girdwood 	case SOF_IPC_GLB_COMPOUND:
378*53e0c72dSLiam Girdwood 	case SOF_IPC_GLB_TPLG_MSG:
379*53e0c72dSLiam Girdwood 	case SOF_IPC_GLB_PM_MSG:
380*53e0c72dSLiam Girdwood 	case SOF_IPC_GLB_COMP_MSG:
381*53e0c72dSLiam Girdwood 		break;
382*53e0c72dSLiam Girdwood 	case SOF_IPC_GLB_STREAM_MSG:
383*53e0c72dSLiam Girdwood 		/* need to pass msg id into the function */
384*53e0c72dSLiam Girdwood 		ipc_stream_message(sdev, hdr.cmd);
385*53e0c72dSLiam Girdwood 		break;
386*53e0c72dSLiam Girdwood 	case SOF_IPC_GLB_TRACE_MSG:
387*53e0c72dSLiam Girdwood 		ipc_trace_message(sdev, type);
388*53e0c72dSLiam Girdwood 		break;
389*53e0c72dSLiam Girdwood 	default:
390*53e0c72dSLiam Girdwood 		dev_err(sdev->dev, "error: unknown DSP message 0x%x\n", cmd);
391*53e0c72dSLiam Girdwood 		break;
392*53e0c72dSLiam Girdwood 	}
393*53e0c72dSLiam Girdwood 
394*53e0c72dSLiam Girdwood 	ipc_log_header(sdev->dev, "ipc rx done", hdr.cmd);
395*53e0c72dSLiam Girdwood }
396*53e0c72dSLiam Girdwood EXPORT_SYMBOL(snd_sof_ipc_msgs_rx);
397*53e0c72dSLiam Girdwood 
398*53e0c72dSLiam Girdwood /*
399*53e0c72dSLiam Girdwood  * IPC trace mechanism.
400*53e0c72dSLiam Girdwood  */
401*53e0c72dSLiam Girdwood 
402*53e0c72dSLiam Girdwood static void ipc_trace_message(struct snd_sof_dev *sdev, u32 msg_id)
403*53e0c72dSLiam Girdwood {
404*53e0c72dSLiam Girdwood 	struct sof_ipc_dma_trace_posn posn;
405*53e0c72dSLiam Girdwood 
406*53e0c72dSLiam Girdwood 	switch (msg_id) {
407*53e0c72dSLiam Girdwood 	case SOF_IPC_TRACE_DMA_POSITION:
408*53e0c72dSLiam Girdwood 		/* read back full message */
409*53e0c72dSLiam Girdwood 		snd_sof_ipc_msg_data(sdev, NULL, &posn, sizeof(posn));
410*53e0c72dSLiam Girdwood 		snd_sof_trace_update_pos(sdev, &posn);
411*53e0c72dSLiam Girdwood 		break;
412*53e0c72dSLiam Girdwood 	default:
413*53e0c72dSLiam Girdwood 		dev_err(sdev->dev, "error: unhandled trace message %x\n",
414*53e0c72dSLiam Girdwood 			msg_id);
415*53e0c72dSLiam Girdwood 		break;
416*53e0c72dSLiam Girdwood 	}
417*53e0c72dSLiam Girdwood }
418*53e0c72dSLiam Girdwood 
419*53e0c72dSLiam Girdwood /*
420*53e0c72dSLiam Girdwood  * IPC stream position.
421*53e0c72dSLiam Girdwood  */
422*53e0c72dSLiam Girdwood 
423*53e0c72dSLiam Girdwood static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id)
424*53e0c72dSLiam Girdwood {
425*53e0c72dSLiam Girdwood 	struct snd_sof_pcm_stream *stream;
426*53e0c72dSLiam Girdwood 	struct sof_ipc_stream_posn posn;
427*53e0c72dSLiam Girdwood 	struct snd_sof_pcm *spcm;
428*53e0c72dSLiam Girdwood 	int direction;
429*53e0c72dSLiam Girdwood 
430*53e0c72dSLiam Girdwood 	spcm = snd_sof_find_spcm_comp(sdev, msg_id, &direction);
431*53e0c72dSLiam Girdwood 	if (!spcm) {
432*53e0c72dSLiam Girdwood 		dev_err(sdev->dev,
433*53e0c72dSLiam Girdwood 			"error: period elapsed for unknown stream, msg_id %d\n",
434*53e0c72dSLiam Girdwood 			msg_id);
435*53e0c72dSLiam Girdwood 		return;
436*53e0c72dSLiam Girdwood 	}
437*53e0c72dSLiam Girdwood 
438*53e0c72dSLiam Girdwood 	stream = &spcm->stream[direction];
439*53e0c72dSLiam Girdwood 	snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
440*53e0c72dSLiam Girdwood 
441*53e0c72dSLiam Girdwood 	dev_dbg(sdev->dev, "posn : host 0x%llx dai 0x%llx wall 0x%llx\n",
442*53e0c72dSLiam Girdwood 		posn.host_posn, posn.dai_posn, posn.wallclock);
443*53e0c72dSLiam Girdwood 
444*53e0c72dSLiam Girdwood 	memcpy(&stream->posn, &posn, sizeof(posn));
445*53e0c72dSLiam Girdwood 
446*53e0c72dSLiam Girdwood 	/* only inform ALSA for period_wakeup mode */
447*53e0c72dSLiam Girdwood 	if (!stream->substream->runtime->no_period_wakeup)
448*53e0c72dSLiam Girdwood 		snd_pcm_period_elapsed(stream->substream);
449*53e0c72dSLiam Girdwood }
450*53e0c72dSLiam Girdwood 
451*53e0c72dSLiam Girdwood /* DSP notifies host of an XRUN within FW */
452*53e0c72dSLiam Girdwood static void ipc_xrun(struct snd_sof_dev *sdev, u32 msg_id)
453*53e0c72dSLiam Girdwood {
454*53e0c72dSLiam Girdwood 	struct snd_sof_pcm_stream *stream;
455*53e0c72dSLiam Girdwood 	struct sof_ipc_stream_posn posn;
456*53e0c72dSLiam Girdwood 	struct snd_sof_pcm *spcm;
457*53e0c72dSLiam Girdwood 	int direction;
458*53e0c72dSLiam Girdwood 
459*53e0c72dSLiam Girdwood 	spcm = snd_sof_find_spcm_comp(sdev, msg_id, &direction);
460*53e0c72dSLiam Girdwood 	if (!spcm) {
461*53e0c72dSLiam Girdwood 		dev_err(sdev->dev, "error: XRUN for unknown stream, msg_id %d\n",
462*53e0c72dSLiam Girdwood 			msg_id);
463*53e0c72dSLiam Girdwood 		return;
464*53e0c72dSLiam Girdwood 	}
465*53e0c72dSLiam Girdwood 
466*53e0c72dSLiam Girdwood 	stream = &spcm->stream[direction];
467*53e0c72dSLiam Girdwood 	snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
468*53e0c72dSLiam Girdwood 
469*53e0c72dSLiam Girdwood 	dev_dbg(sdev->dev,  "posn XRUN: host %llx comp %d size %d\n",
470*53e0c72dSLiam Girdwood 		posn.host_posn, posn.xrun_comp_id, posn.xrun_size);
471*53e0c72dSLiam Girdwood 
472*53e0c72dSLiam Girdwood #if defined(CONFIG_SND_SOC_SOF_DEBUG_XRUN_STOP)
473*53e0c72dSLiam Girdwood 	/* stop PCM on XRUN - used for pipeline debug */
474*53e0c72dSLiam Girdwood 	memcpy(&stream->posn, &posn, sizeof(posn));
475*53e0c72dSLiam Girdwood 	snd_pcm_stop_xrun(stream->substream);
476*53e0c72dSLiam Girdwood #endif
477*53e0c72dSLiam Girdwood }
478*53e0c72dSLiam Girdwood 
479*53e0c72dSLiam Girdwood /* stream notifications from DSP FW */
480*53e0c72dSLiam Girdwood static void ipc_stream_message(struct snd_sof_dev *sdev, u32 msg_cmd)
481*53e0c72dSLiam Girdwood {
482*53e0c72dSLiam Girdwood 	/* get msg cmd type and msd id */
483*53e0c72dSLiam Girdwood 	u32 msg_type = msg_cmd & SOF_CMD_TYPE_MASK;
484*53e0c72dSLiam Girdwood 	u32 msg_id = SOF_IPC_MESSAGE_ID(msg_cmd);
485*53e0c72dSLiam Girdwood 
486*53e0c72dSLiam Girdwood 	switch (msg_type) {
487*53e0c72dSLiam Girdwood 	case SOF_IPC_STREAM_POSITION:
488*53e0c72dSLiam Girdwood 		ipc_period_elapsed(sdev, msg_id);
489*53e0c72dSLiam Girdwood 		break;
490*53e0c72dSLiam Girdwood 	case SOF_IPC_STREAM_TRIG_XRUN:
491*53e0c72dSLiam Girdwood 		ipc_xrun(sdev, msg_id);
492*53e0c72dSLiam Girdwood 		break;
493*53e0c72dSLiam Girdwood 	default:
494*53e0c72dSLiam Girdwood 		dev_err(sdev->dev, "error: unhandled stream message %x\n",
495*53e0c72dSLiam Girdwood 			msg_id);
496*53e0c72dSLiam Girdwood 		break;
497*53e0c72dSLiam Girdwood 	}
498*53e0c72dSLiam Girdwood }
499*53e0c72dSLiam Girdwood 
500*53e0c72dSLiam Girdwood /* get stream position IPC - use faster MMIO method if available on platform */
501*53e0c72dSLiam Girdwood int snd_sof_ipc_stream_posn(struct snd_sof_dev *sdev,
502*53e0c72dSLiam Girdwood 			    struct snd_sof_pcm *spcm, int direction,
503*53e0c72dSLiam Girdwood 			    struct sof_ipc_stream_posn *posn)
504*53e0c72dSLiam Girdwood {
505*53e0c72dSLiam Girdwood 	struct sof_ipc_stream stream;
506*53e0c72dSLiam Girdwood 	int err;
507*53e0c72dSLiam Girdwood 
508*53e0c72dSLiam Girdwood 	/* read position via slower IPC */
509*53e0c72dSLiam Girdwood 	stream.hdr.size = sizeof(stream);
510*53e0c72dSLiam Girdwood 	stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_POSITION;
511*53e0c72dSLiam Girdwood 	stream.comp_id = spcm->stream[direction].comp_id;
512*53e0c72dSLiam Girdwood 
513*53e0c72dSLiam Girdwood 	/* send IPC to the DSP */
514*53e0c72dSLiam Girdwood 	err = sof_ipc_tx_message(sdev->ipc,
515*53e0c72dSLiam Girdwood 				 stream.hdr.cmd, &stream, sizeof(stream), &posn,
516*53e0c72dSLiam Girdwood 				 sizeof(*posn));
517*53e0c72dSLiam Girdwood 	if (err < 0) {
518*53e0c72dSLiam Girdwood 		dev_err(sdev->dev, "error: failed to get stream %d position\n",
519*53e0c72dSLiam Girdwood 			stream.comp_id);
520*53e0c72dSLiam Girdwood 		return err;
521*53e0c72dSLiam Girdwood 	}
522*53e0c72dSLiam Girdwood 
523*53e0c72dSLiam Girdwood 	return 0;
524*53e0c72dSLiam Girdwood }
525*53e0c72dSLiam Girdwood EXPORT_SYMBOL(snd_sof_ipc_stream_posn);
526*53e0c72dSLiam Girdwood 
527*53e0c72dSLiam Girdwood static int sof_get_ctrl_copy_params(enum sof_ipc_ctrl_type ctrl_type,
528*53e0c72dSLiam Girdwood 				    struct sof_ipc_ctrl_data *src,
529*53e0c72dSLiam Girdwood 				    struct sof_ipc_ctrl_data *dst,
530*53e0c72dSLiam Girdwood 				    struct sof_ipc_ctrl_data_params *sparams)
531*53e0c72dSLiam Girdwood {
532*53e0c72dSLiam Girdwood 	switch (ctrl_type) {
533*53e0c72dSLiam Girdwood 	case SOF_CTRL_TYPE_VALUE_CHAN_GET:
534*53e0c72dSLiam Girdwood 	case SOF_CTRL_TYPE_VALUE_CHAN_SET:
535*53e0c72dSLiam Girdwood 		sparams->src = (u8 *)src->chanv;
536*53e0c72dSLiam Girdwood 		sparams->dst = (u8 *)dst->chanv;
537*53e0c72dSLiam Girdwood 		break;
538*53e0c72dSLiam Girdwood 	case SOF_CTRL_TYPE_VALUE_COMP_GET:
539*53e0c72dSLiam Girdwood 	case SOF_CTRL_TYPE_VALUE_COMP_SET:
540*53e0c72dSLiam Girdwood 		sparams->src = (u8 *)src->compv;
541*53e0c72dSLiam Girdwood 		sparams->dst = (u8 *)dst->compv;
542*53e0c72dSLiam Girdwood 		break;
543*53e0c72dSLiam Girdwood 	case SOF_CTRL_TYPE_DATA_GET:
544*53e0c72dSLiam Girdwood 	case SOF_CTRL_TYPE_DATA_SET:
545*53e0c72dSLiam Girdwood 		sparams->src = (u8 *)src->data->data;
546*53e0c72dSLiam Girdwood 		sparams->dst = (u8 *)dst->data->data;
547*53e0c72dSLiam Girdwood 		break;
548*53e0c72dSLiam Girdwood 	default:
549*53e0c72dSLiam Girdwood 		return -EINVAL;
550*53e0c72dSLiam Girdwood 	}
551*53e0c72dSLiam Girdwood 
552*53e0c72dSLiam Girdwood 	/* calculate payload size and number of messages */
553*53e0c72dSLiam Girdwood 	sparams->pl_size = SOF_IPC_MSG_MAX_SIZE - sparams->hdr_bytes;
554*53e0c72dSLiam Girdwood 	sparams->num_msg = DIV_ROUND_UP(sparams->msg_bytes, sparams->pl_size);
555*53e0c72dSLiam Girdwood 
556*53e0c72dSLiam Girdwood 	return 0;
557*53e0c72dSLiam Girdwood }
558*53e0c72dSLiam Girdwood 
559*53e0c72dSLiam Girdwood static int sof_set_get_large_ctrl_data(struct snd_sof_dev *sdev,
560*53e0c72dSLiam Girdwood 				       struct sof_ipc_ctrl_data *cdata,
561*53e0c72dSLiam Girdwood 				       struct sof_ipc_ctrl_data_params *sparams,
562*53e0c72dSLiam Girdwood 				       bool send)
563*53e0c72dSLiam Girdwood {
564*53e0c72dSLiam Girdwood 	struct sof_ipc_ctrl_data *partdata;
565*53e0c72dSLiam Girdwood 	size_t send_bytes;
566*53e0c72dSLiam Girdwood 	size_t offset = 0;
567*53e0c72dSLiam Girdwood 	size_t msg_bytes;
568*53e0c72dSLiam Girdwood 	size_t pl_size;
569*53e0c72dSLiam Girdwood 	int err = 0;
570*53e0c72dSLiam Girdwood 	int i;
571*53e0c72dSLiam Girdwood 
572*53e0c72dSLiam Girdwood 	/* allocate max ipc size because we have at least one */
573*53e0c72dSLiam Girdwood 	partdata = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
574*53e0c72dSLiam Girdwood 	if (!partdata)
575*53e0c72dSLiam Girdwood 		return -ENOMEM;
576*53e0c72dSLiam Girdwood 
577*53e0c72dSLiam Girdwood 	if (send)
578*53e0c72dSLiam Girdwood 		sof_get_ctrl_copy_params(cdata->type, cdata, partdata, sparams);
579*53e0c72dSLiam Girdwood 	else
580*53e0c72dSLiam Girdwood 		sof_get_ctrl_copy_params(cdata->type, partdata, cdata, sparams);
581*53e0c72dSLiam Girdwood 
582*53e0c72dSLiam Girdwood 	msg_bytes = sparams->msg_bytes;
583*53e0c72dSLiam Girdwood 	pl_size = sparams->pl_size;
584*53e0c72dSLiam Girdwood 
585*53e0c72dSLiam Girdwood 	/* copy the header data */
586*53e0c72dSLiam Girdwood 	memcpy(partdata, cdata, sparams->hdr_bytes);
587*53e0c72dSLiam Girdwood 
588*53e0c72dSLiam Girdwood 	/* Serialise IPC TX */
589*53e0c72dSLiam Girdwood 	mutex_lock(&sdev->ipc->tx_mutex);
590*53e0c72dSLiam Girdwood 
591*53e0c72dSLiam Girdwood 	/* copy the payload data in a loop */
592*53e0c72dSLiam Girdwood 	for (i = 0; i < sparams->num_msg; i++) {
593*53e0c72dSLiam Girdwood 		send_bytes = min(msg_bytes, pl_size);
594*53e0c72dSLiam Girdwood 		partdata->num_elems = send_bytes;
595*53e0c72dSLiam Girdwood 		partdata->rhdr.hdr.size = sparams->hdr_bytes + send_bytes;
596*53e0c72dSLiam Girdwood 		partdata->msg_index = i;
597*53e0c72dSLiam Girdwood 		msg_bytes -= send_bytes;
598*53e0c72dSLiam Girdwood 		partdata->elems_remaining = msg_bytes;
599*53e0c72dSLiam Girdwood 
600*53e0c72dSLiam Girdwood 		if (send)
601*53e0c72dSLiam Girdwood 			memcpy(sparams->dst, sparams->src + offset, send_bytes);
602*53e0c72dSLiam Girdwood 
603*53e0c72dSLiam Girdwood 		err = sof_ipc_tx_message_unlocked(sdev->ipc,
604*53e0c72dSLiam Girdwood 						  partdata->rhdr.hdr.cmd,
605*53e0c72dSLiam Girdwood 						  partdata,
606*53e0c72dSLiam Girdwood 						  partdata->rhdr.hdr.size,
607*53e0c72dSLiam Girdwood 						  partdata,
608*53e0c72dSLiam Girdwood 						  partdata->rhdr.hdr.size);
609*53e0c72dSLiam Girdwood 		if (err < 0)
610*53e0c72dSLiam Girdwood 			break;
611*53e0c72dSLiam Girdwood 
612*53e0c72dSLiam Girdwood 		if (!send)
613*53e0c72dSLiam Girdwood 			memcpy(sparams->dst + offset, sparams->src, send_bytes);
614*53e0c72dSLiam Girdwood 
615*53e0c72dSLiam Girdwood 		offset += pl_size;
616*53e0c72dSLiam Girdwood 	}
617*53e0c72dSLiam Girdwood 
618*53e0c72dSLiam Girdwood 	mutex_unlock(&sdev->ipc->tx_mutex);
619*53e0c72dSLiam Girdwood 
620*53e0c72dSLiam Girdwood 	kfree(partdata);
621*53e0c72dSLiam Girdwood 	return err;
622*53e0c72dSLiam Girdwood }
623*53e0c72dSLiam Girdwood 
624*53e0c72dSLiam Girdwood /*
625*53e0c72dSLiam Girdwood  * IPC get()/set() for kcontrols.
626*53e0c72dSLiam Girdwood  */
627*53e0c72dSLiam Girdwood int snd_sof_ipc_set_get_comp_data(struct snd_sof_ipc *ipc,
628*53e0c72dSLiam Girdwood 				  struct snd_sof_control *scontrol,
629*53e0c72dSLiam Girdwood 				  u32 ipc_cmd,
630*53e0c72dSLiam Girdwood 				  enum sof_ipc_ctrl_type ctrl_type,
631*53e0c72dSLiam Girdwood 				  enum sof_ipc_ctrl_cmd ctrl_cmd,
632*53e0c72dSLiam Girdwood 				  bool send)
633*53e0c72dSLiam Girdwood {
634*53e0c72dSLiam Girdwood 	struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
635*53e0c72dSLiam Girdwood 	struct snd_sof_dev *sdev = ipc->sdev;
636*53e0c72dSLiam Girdwood 	struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
637*53e0c72dSLiam Girdwood 	struct sof_ipc_fw_version *v = &ready->version;
638*53e0c72dSLiam Girdwood 	struct sof_ipc_ctrl_data_params sparams;
639*53e0c72dSLiam Girdwood 	size_t send_bytes;
640*53e0c72dSLiam Girdwood 	int err;
641*53e0c72dSLiam Girdwood 
642*53e0c72dSLiam Girdwood 	/* read or write firmware volume */
643*53e0c72dSLiam Girdwood 	if (scontrol->readback_offset != 0) {
644*53e0c72dSLiam Girdwood 		/* write/read value header via mmaped region */
645*53e0c72dSLiam Girdwood 		send_bytes = sizeof(struct sof_ipc_ctrl_value_chan) *
646*53e0c72dSLiam Girdwood 		cdata->num_elems;
647*53e0c72dSLiam Girdwood 		if (send)
648*53e0c72dSLiam Girdwood 			snd_sof_dsp_block_write(sdev, sdev->mmio_bar,
649*53e0c72dSLiam Girdwood 						scontrol->readback_offset,
650*53e0c72dSLiam Girdwood 						cdata->chanv, send_bytes);
651*53e0c72dSLiam Girdwood 
652*53e0c72dSLiam Girdwood 		else
653*53e0c72dSLiam Girdwood 			snd_sof_dsp_block_read(sdev, sdev->mmio_bar,
654*53e0c72dSLiam Girdwood 					       scontrol->readback_offset,
655*53e0c72dSLiam Girdwood 					       cdata->chanv, send_bytes);
656*53e0c72dSLiam Girdwood 		return 0;
657*53e0c72dSLiam Girdwood 	}
658*53e0c72dSLiam Girdwood 
659*53e0c72dSLiam Girdwood 	cdata->rhdr.hdr.cmd = SOF_IPC_GLB_COMP_MSG | ipc_cmd;
660*53e0c72dSLiam Girdwood 	cdata->cmd = ctrl_cmd;
661*53e0c72dSLiam Girdwood 	cdata->type = ctrl_type;
662*53e0c72dSLiam Girdwood 	cdata->comp_id = scontrol->comp_id;
663*53e0c72dSLiam Girdwood 	cdata->msg_index = 0;
664*53e0c72dSLiam Girdwood 
665*53e0c72dSLiam Girdwood 	/* calculate header and data size */
666*53e0c72dSLiam Girdwood 	switch (cdata->type) {
667*53e0c72dSLiam Girdwood 	case SOF_CTRL_TYPE_VALUE_CHAN_GET:
668*53e0c72dSLiam Girdwood 	case SOF_CTRL_TYPE_VALUE_CHAN_SET:
669*53e0c72dSLiam Girdwood 		sparams.msg_bytes = scontrol->num_channels *
670*53e0c72dSLiam Girdwood 			sizeof(struct sof_ipc_ctrl_value_chan);
671*53e0c72dSLiam Girdwood 		sparams.hdr_bytes = sizeof(struct sof_ipc_ctrl_data);
672*53e0c72dSLiam Girdwood 		sparams.elems = scontrol->num_channels;
673*53e0c72dSLiam Girdwood 		break;
674*53e0c72dSLiam Girdwood 	case SOF_CTRL_TYPE_VALUE_COMP_GET:
675*53e0c72dSLiam Girdwood 	case SOF_CTRL_TYPE_VALUE_COMP_SET:
676*53e0c72dSLiam Girdwood 		sparams.msg_bytes = scontrol->num_channels *
677*53e0c72dSLiam Girdwood 			sizeof(struct sof_ipc_ctrl_value_comp);
678*53e0c72dSLiam Girdwood 		sparams.hdr_bytes = sizeof(struct sof_ipc_ctrl_data);
679*53e0c72dSLiam Girdwood 		sparams.elems = scontrol->num_channels;
680*53e0c72dSLiam Girdwood 		break;
681*53e0c72dSLiam Girdwood 	case SOF_CTRL_TYPE_DATA_GET:
682*53e0c72dSLiam Girdwood 	case SOF_CTRL_TYPE_DATA_SET:
683*53e0c72dSLiam Girdwood 		sparams.msg_bytes = cdata->data->size;
684*53e0c72dSLiam Girdwood 		sparams.hdr_bytes = sizeof(struct sof_ipc_ctrl_data) +
685*53e0c72dSLiam Girdwood 			sizeof(struct sof_abi_hdr);
686*53e0c72dSLiam Girdwood 		sparams.elems = cdata->data->size;
687*53e0c72dSLiam Girdwood 		break;
688*53e0c72dSLiam Girdwood 	default:
689*53e0c72dSLiam Girdwood 		return -EINVAL;
690*53e0c72dSLiam Girdwood 	}
691*53e0c72dSLiam Girdwood 
692*53e0c72dSLiam Girdwood 	cdata->rhdr.hdr.size = sparams.msg_bytes + sparams.hdr_bytes;
693*53e0c72dSLiam Girdwood 	cdata->num_elems = sparams.elems;
694*53e0c72dSLiam Girdwood 	cdata->elems_remaining = 0;
695*53e0c72dSLiam Girdwood 
696*53e0c72dSLiam Girdwood 	/* send normal size ipc in one part */
697*53e0c72dSLiam Girdwood 	if (cdata->rhdr.hdr.size <= SOF_IPC_MSG_MAX_SIZE) {
698*53e0c72dSLiam Girdwood 		err = sof_ipc_tx_message(sdev->ipc, cdata->rhdr.hdr.cmd, cdata,
699*53e0c72dSLiam Girdwood 					 cdata->rhdr.hdr.size, cdata,
700*53e0c72dSLiam Girdwood 					 cdata->rhdr.hdr.size);
701*53e0c72dSLiam Girdwood 
702*53e0c72dSLiam Girdwood 		if (err < 0)
703*53e0c72dSLiam Girdwood 			dev_err(sdev->dev, "error: set/get ctrl ipc comp %d\n",
704*53e0c72dSLiam Girdwood 				cdata->comp_id);
705*53e0c72dSLiam Girdwood 
706*53e0c72dSLiam Girdwood 		return err;
707*53e0c72dSLiam Girdwood 	}
708*53e0c72dSLiam Girdwood 
709*53e0c72dSLiam Girdwood 	/* data is bigger than max ipc size, chop into smaller pieces */
710*53e0c72dSLiam Girdwood 	dev_dbg(sdev->dev, "large ipc size %u, control size %u\n",
711*53e0c72dSLiam Girdwood 		cdata->rhdr.hdr.size, scontrol->size);
712*53e0c72dSLiam Girdwood 
713*53e0c72dSLiam Girdwood 	/* large messages is only supported from ABI 3.3.0 onwards */
714*53e0c72dSLiam Girdwood 	if (v->abi_version < SOF_ABI_VER(3, 3, 0)) {
715*53e0c72dSLiam Girdwood 		dev_err(sdev->dev, "error: incompatible FW ABI version\n");
716*53e0c72dSLiam Girdwood 		return -EINVAL;
717*53e0c72dSLiam Girdwood 	}
718*53e0c72dSLiam Girdwood 
719*53e0c72dSLiam Girdwood 	err = sof_set_get_large_ctrl_data(sdev, cdata, &sparams, send);
720*53e0c72dSLiam Girdwood 
721*53e0c72dSLiam Girdwood 	if (err < 0)
722*53e0c72dSLiam Girdwood 		dev_err(sdev->dev, "error: set/get large ctrl ipc comp %d\n",
723*53e0c72dSLiam Girdwood 			cdata->comp_id);
724*53e0c72dSLiam Girdwood 
725*53e0c72dSLiam Girdwood 	return err;
726*53e0c72dSLiam Girdwood }
727*53e0c72dSLiam Girdwood EXPORT_SYMBOL(snd_sof_ipc_set_get_comp_data);
728*53e0c72dSLiam Girdwood 
729*53e0c72dSLiam Girdwood /*
730*53e0c72dSLiam Girdwood  * IPC layer enumeration.
731*53e0c72dSLiam Girdwood  */
732*53e0c72dSLiam Girdwood 
733*53e0c72dSLiam Girdwood int snd_sof_dsp_mailbox_init(struct snd_sof_dev *sdev, u32 dspbox,
734*53e0c72dSLiam Girdwood 			     size_t dspbox_size, u32 hostbox,
735*53e0c72dSLiam Girdwood 			     size_t hostbox_size)
736*53e0c72dSLiam Girdwood {
737*53e0c72dSLiam Girdwood 	sdev->dsp_box.offset = dspbox;
738*53e0c72dSLiam Girdwood 	sdev->dsp_box.size = dspbox_size;
739*53e0c72dSLiam Girdwood 	sdev->host_box.offset = hostbox;
740*53e0c72dSLiam Girdwood 	sdev->host_box.size = hostbox_size;
741*53e0c72dSLiam Girdwood 	return 0;
742*53e0c72dSLiam Girdwood }
743*53e0c72dSLiam Girdwood EXPORT_SYMBOL(snd_sof_dsp_mailbox_init);
744*53e0c72dSLiam Girdwood 
745*53e0c72dSLiam Girdwood int snd_sof_ipc_valid(struct snd_sof_dev *sdev)
746*53e0c72dSLiam Girdwood {
747*53e0c72dSLiam Girdwood 	struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
748*53e0c72dSLiam Girdwood 	struct sof_ipc_fw_version *v = &ready->version;
749*53e0c72dSLiam Girdwood 
750*53e0c72dSLiam Girdwood 	dev_info(sdev->dev,
751*53e0c72dSLiam Girdwood 		 "Firmware info: version %d:%d:%d-%s\n",  v->major, v->minor,
752*53e0c72dSLiam Girdwood 		 v->micro, v->tag);
753*53e0c72dSLiam Girdwood 	dev_info(sdev->dev,
754*53e0c72dSLiam Girdwood 		 "Firmware: ABI %d:%d:%d Kernel ABI %d:%d:%d\n",
755*53e0c72dSLiam Girdwood 		 SOF_ABI_VERSION_MAJOR(v->abi_version),
756*53e0c72dSLiam Girdwood 		 SOF_ABI_VERSION_MINOR(v->abi_version),
757*53e0c72dSLiam Girdwood 		 SOF_ABI_VERSION_PATCH(v->abi_version),
758*53e0c72dSLiam Girdwood 		 SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH);
759*53e0c72dSLiam Girdwood 
760*53e0c72dSLiam Girdwood 	if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, v->abi_version)) {
761*53e0c72dSLiam Girdwood 		dev_err(sdev->dev, "error: incompatible FW ABI version\n");
762*53e0c72dSLiam Girdwood 		return -EINVAL;
763*53e0c72dSLiam Girdwood 	}
764*53e0c72dSLiam Girdwood 
765*53e0c72dSLiam Girdwood 	if (ready->debug.bits.build) {
766*53e0c72dSLiam Girdwood 		dev_info(sdev->dev,
767*53e0c72dSLiam Girdwood 			 "Firmware debug build %d on %s-%s - options:\n"
768*53e0c72dSLiam Girdwood 			 " GDB: %s\n"
769*53e0c72dSLiam Girdwood 			 " lock debug: %s\n"
770*53e0c72dSLiam Girdwood 			 " lock vdebug: %s\n",
771*53e0c72dSLiam Girdwood 			 v->build, v->date, v->time,
772*53e0c72dSLiam Girdwood 			 ready->debug.bits.gdb ? "enabled" : "disabled",
773*53e0c72dSLiam Girdwood 			 ready->debug.bits.locks ? "enabled" : "disabled",
774*53e0c72dSLiam Girdwood 			 ready->debug.bits.locks_verbose ? "enabled" : "disabled");
775*53e0c72dSLiam Girdwood 	}
776*53e0c72dSLiam Girdwood 
777*53e0c72dSLiam Girdwood 	/* copy the fw_version into debugfs at first boot */
778*53e0c72dSLiam Girdwood 	memcpy(&sdev->fw_version, v, sizeof(*v));
779*53e0c72dSLiam Girdwood 
780*53e0c72dSLiam Girdwood 	return 0;
781*53e0c72dSLiam Girdwood }
782*53e0c72dSLiam Girdwood EXPORT_SYMBOL(snd_sof_ipc_valid);
783*53e0c72dSLiam Girdwood 
784*53e0c72dSLiam Girdwood struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev)
785*53e0c72dSLiam Girdwood {
786*53e0c72dSLiam Girdwood 	struct snd_sof_ipc *ipc;
787*53e0c72dSLiam Girdwood 	struct snd_sof_ipc_msg *msg;
788*53e0c72dSLiam Girdwood 
789*53e0c72dSLiam Girdwood 	/* check if mandatory ops required for ipc are defined */
790*53e0c72dSLiam Girdwood 	if (!sof_ops(sdev)->fw_ready) {
791*53e0c72dSLiam Girdwood 		dev_err(sdev->dev, "error: ipc mandatory ops not defined\n");
792*53e0c72dSLiam Girdwood 		return NULL;
793*53e0c72dSLiam Girdwood 	}
794*53e0c72dSLiam Girdwood 
795*53e0c72dSLiam Girdwood 	ipc = devm_kzalloc(sdev->dev, sizeof(*ipc), GFP_KERNEL);
796*53e0c72dSLiam Girdwood 	if (!ipc)
797*53e0c72dSLiam Girdwood 		return NULL;
798*53e0c72dSLiam Girdwood 
799*53e0c72dSLiam Girdwood 	mutex_init(&ipc->tx_mutex);
800*53e0c72dSLiam Girdwood 	ipc->sdev = sdev;
801*53e0c72dSLiam Girdwood 	msg = &ipc->msg;
802*53e0c72dSLiam Girdwood 
803*53e0c72dSLiam Girdwood 	/* indicate that we aren't sending a message ATM */
804*53e0c72dSLiam Girdwood 	msg->ipc_complete = true;
805*53e0c72dSLiam Girdwood 
806*53e0c72dSLiam Girdwood 	/* pre-allocate message data */
807*53e0c72dSLiam Girdwood 	msg->msg_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE,
808*53e0c72dSLiam Girdwood 				     GFP_KERNEL);
809*53e0c72dSLiam Girdwood 	if (!msg->msg_data)
810*53e0c72dSLiam Girdwood 		return NULL;
811*53e0c72dSLiam Girdwood 
812*53e0c72dSLiam Girdwood 	msg->reply_data = devm_kzalloc(sdev->dev, SOF_IPC_MSG_MAX_SIZE,
813*53e0c72dSLiam Girdwood 				       GFP_KERNEL);
814*53e0c72dSLiam Girdwood 	if (!msg->reply_data)
815*53e0c72dSLiam Girdwood 		return NULL;
816*53e0c72dSLiam Girdwood 
817*53e0c72dSLiam Girdwood 	init_waitqueue_head(&msg->waitq);
818*53e0c72dSLiam Girdwood 
819*53e0c72dSLiam Girdwood 	return ipc;
820*53e0c72dSLiam Girdwood }
821*53e0c72dSLiam Girdwood EXPORT_SYMBOL(snd_sof_ipc_init);
822*53e0c72dSLiam Girdwood 
823*53e0c72dSLiam Girdwood void snd_sof_ipc_free(struct snd_sof_dev *sdev)
824*53e0c72dSLiam Girdwood {
825*53e0c72dSLiam Girdwood 	struct snd_sof_ipc *ipc = sdev->ipc;
826*53e0c72dSLiam Girdwood 
827*53e0c72dSLiam Girdwood 	/* disable sending of ipc's */
828*53e0c72dSLiam Girdwood 	mutex_lock(&ipc->tx_mutex);
829*53e0c72dSLiam Girdwood 	ipc->disable_ipc_tx = true;
830*53e0c72dSLiam Girdwood 	mutex_unlock(&ipc->tx_mutex);
831*53e0c72dSLiam Girdwood }
832*53e0c72dSLiam Girdwood EXPORT_SYMBOL(snd_sof_ipc_free);
833