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