1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2021 Intel Corporation. All rights reserved.
4 //
5 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
6 // Author: Rander Wang <rander.wang@linux.intel.com>
7 /*
8  * IPC (InterProcessor Communication) provides a method of two way
9  * communication between the host processor and the DSP. The IPC used here
10  * utilises a shared mailbox and door bell between the host and DSP.
11  *
12  */
13 
14 #include <sof/audio/buffer.h>
15 #include <sof/audio/component_ext.h>
16 #include <sof/audio/pipeline.h>
17 #include <sof/common.h>
18 #include <sof/ipc/topology.h>
19 #include <sof/ipc/common.h>
20 #include <sof/ipc/msg.h>
21 #include <sof/ipc/driver.h>
22 #include <sof/lib/mailbox.h>
23 #include <sof/math/numbers.h>
24 #include <sof/trace/trace.h>
25 #include <ipc4/error_status.h>
26 #include <ipc4/header.h>
27 #include <ipc4/module.h>
28 #include <ipc4/pipeline.h>
29 #include <ipc4/notification.h>
30 #include <ipc/trace.h>
31 #include <user/trace.h>
32 
33 #include <errno.h>
34 #include <stdbool.h>
35 #include <stddef.h>
36 #include <stdint.h>
37 
38 struct ipc4_msg_data {
39 	uint32_t msg_in[2]; /* local copy of current message from host header */
40 	uint32_t msg_out[2]; /* local copy of current message to host header */
41 };
42 
43 /*
44  * Global IPC Operations.
45  */
ipc4_create_pipeline(union ipc4_message_header * ipc4)46 static int ipc4_create_pipeline(union ipc4_message_header *ipc4)
47 {
48 	struct ipc *ipc = ipc_get();
49 
50 	return ipc_pipeline_new(ipc, (ipc_pipe_new *)ipc4);
51 }
52 
ipc4_delete_pipeline(union ipc4_message_header * ipc4)53 static int ipc4_delete_pipeline(union ipc4_message_header *ipc4)
54 {
55 	struct ipc4_pipeline_delete *pipe;
56 	struct ipc *ipc = ipc_get();
57 
58 	pipe = (struct ipc4_pipeline_delete *)ipc4;
59 	tr_dbg(&ipc_tr, "ipc4 delete pipeline %x:", (uint32_t)pipe->header.r.instance_id);
60 
61 	return ipc_pipeline_free(ipc, pipe->header.r.instance_id);
62 }
63 
ipc4_comp_params(struct comp_dev * current,struct comp_buffer * calling_buf,struct pipeline_walk_context * ctx,int dir)64 static int ipc4_comp_params(struct comp_dev *current,
65 			    struct comp_buffer *calling_buf,
66 			    struct pipeline_walk_context *ctx, int dir)
67 {
68 	struct pipeline_data *ppl_data = ctx->comp_data;
69 	int err;
70 
71 	/* don't do any params if current is running */
72 	if (current->state == COMP_STATE_ACTIVE)
73 		return 0;
74 
75 	err = comp_params(current, &ppl_data->params->params);
76 	if (err < 0 || err == PPL_STATUS_PATH_STOP)
77 		return err;
78 
79 	return pipeline_for_each_comp(current, ctx, dir);
80 }
81 
ipc4_pipeline_params(struct pipeline * p,struct comp_dev * host,struct sof_ipc_pcm_params * params)82 static int ipc4_pipeline_params(struct pipeline *p, struct comp_dev *host,
83 				struct sof_ipc_pcm_params *params)
84 {
85 	struct sof_ipc_pcm_params hw_params;
86 	struct pipeline_data data = {
87 		.start = host,
88 		.params = &hw_params,
89 	};
90 
91 	struct pipeline_walk_context param_ctx = {
92 		.comp_func = ipc4_comp_params,
93 		.comp_data = &data,
94 		.skip_incomplete = true,
95 	};
96 
97 	return param_ctx.comp_func(host, NULL, &param_ctx, host->direction);
98 }
99 
ipc4_pcm_params(struct ipc_comp_dev * pcm_dev)100 static int ipc4_pcm_params(struct ipc_comp_dev *pcm_dev)
101 {
102 	struct sof_ipc_pcm_params params;
103 	int err, reset_err;
104 
105 	memset(&params, 0, sizeof(params));
106 
107 	/* sanity check comp */
108 	if (!pcm_dev->cd->pipeline) {
109 		tr_err(&ipc_tr, "ipc: comp %d pipeline not found", pcm_dev->id);
110 		return -EINVAL;
111 	}
112 
113 	/* configure pipeline audio params */
114 	err = ipc4_pipeline_params(pcm_dev->cd->pipeline, pcm_dev->cd, &params);
115 	if (err < 0) {
116 		tr_err(&ipc_tr, "ipc: pipe %d comp %d params failed %d",
117 		       pcm_dev->cd->pipeline->pipeline_id,
118 		       pcm_dev->cd->pipeline->comp_id, err);
119 		goto error;
120 	}
121 
122 	/* prepare pipeline audio params */
123 	err = pipeline_prepare(pcm_dev->cd->pipeline, pcm_dev->cd);
124 	if (err < 0) {
125 		tr_err(&ipc_tr, "ipc: pipe %d comp %d prepare failed %d",
126 		       pcm_dev->cd->pipeline->pipeline_id,
127 		       pcm_dev->cd->pipeline->comp_id, err);
128 		goto error;
129 	}
130 
131 	return 0;
132 
133 error:
134 	reset_err = pipeline_reset(pcm_dev->cd->pipeline, pcm_dev->cd);
135 	if (reset_err < 0)
136 		tr_err(&ipc_tr, "ipc: pipe %d comp %d reset failed %d",
137 		       pcm_dev->cd->pipeline->pipeline_id,
138 		       pcm_dev->cd->pipeline->comp_id, reset_err);
139 
140 	return err;
141 }
142 
143 /* Ipc4 pipeline message <------> ipc3 pipeline message
144  * RUNNING     <-------> TRIGGER START
145  * INIT + PAUSED  <-------> PIPELINE COMPLETE
146  * PAUSED      <-------> TRIGER_PAUSE
147  * RESET       <-------> TRIGER_STOP + RESET
148  * EOS         <-------> TRIGER_RELEASE
149  */
ipc4_set_pipeline_state(union ipc4_message_header * ipc4)150 static int ipc4_set_pipeline_state(union ipc4_message_header *ipc4)
151 {
152 	struct ipc4_pipeline_set_state state;
153 	struct ipc_comp_dev *pcm_dev;
154 	struct ipc_comp_dev *host;
155 	struct ipc *ipc = ipc_get();
156 	uint32_t cmd, id;
157 	int ret;
158 
159 	state.header.dat = ipc4->dat;
160 	id = state.header.r.ppl_id;
161 	cmd = state.header.r.ppl_state;
162 
163 	tr_dbg(&ipc_tr, "ipc4 set pipeline state %x:", cmd);
164 
165 	/* get the pcm_dev */
166 	pcm_dev = ipc_get_comp_by_ppl_id(ipc, COMP_TYPE_PIPELINE, id);
167 	if (!pcm_dev) {
168 		tr_err(&ipc_tr, "ipc: comp %d not found", id);
169 		return IPC4_INVALID_RESOURCE_ID;
170 	}
171 
172 	if (pcm_dev->pipeline->source_comp->direction == SOF_IPC_STREAM_PLAYBACK)
173 		host = ipc_get_comp_by_id(ipc, pcm_dev->pipeline->source_comp->ipc_config.id);
174 	else
175 		host = ipc_get_comp_by_id(ipc, pcm_dev->pipeline->sink_comp->ipc_config.id);
176 
177 	if (!host) {
178 		tr_err(&ipc_tr, "ipc: comp host not found",
179 		       pcm_dev->pipeline->source_comp->ipc_config.id);
180 		return IPC4_INVALID_RESOURCE_ID;
181 	}
182 
183 	/* check core */
184 	if (!cpu_is_me(host->core))
185 		return ipc_process_on_core(host->core, false);
186 
187 	switch (cmd) {
188 	case SOF_IPC4_PIPELINE_STATE_RUNNING:
189 		cmd = COMP_TRIGGER_START;
190 
191 		ret = ipc4_pcm_params(host);
192 		if (ret < 0)
193 			return IPC4_INVALID_REQUEST;
194 		break;
195 	case SOF_IPC4_PIPELINE_STATE_RESET:
196 		ret = pipeline_trigger(host->cd->pipeline, host->cd, COMP_TRIGGER_STOP);
197 		if (ret < 0) {
198 			tr_err(&ipc_tr, "ipc: comp %d trigger 0x%x failed %d", id, cmd, ret);
199 			return IPC4_PIPELINE_STATE_NOT_SET;
200 		}
201 
202 		/* resource is not released by triggering reset which is used by current FW */
203 		return pipeline_reset(host->cd->pipeline, host->cd);
204 	case SOF_IPC4_PIPELINE_STATE_PAUSED:
205 		if (pcm_dev->pipeline->status == COMP_STATE_INIT)
206 			return ipc_pipeline_complete(ipc, id);
207 
208 		cmd = COMP_TRIGGER_PAUSE;
209 		break;
210 	case SOF_IPC4_PIPELINE_STATE_EOS:
211 		cmd = COMP_TRIGGER_RELEASE;
212 		break;
213 	/* special case- TODO */
214 	case SOF_IPC4_PIPELINE_STATE_SAVED:
215 	case SOF_IPC4_PIPELINE_STATE_ERROR_STOP:
216 		return 0;
217 	default:
218 		tr_err(&ipc_tr, "ipc: invalid trigger cmd 0x%x", cmd);
219 		return IPC4_INVALID_REQUEST;
220 	}
221 
222 	/* trigger the component */
223 	ret = pipeline_trigger(host->cd->pipeline, host->cd, cmd);
224 	if (ret < 0) {
225 		tr_err(&ipc_tr, "ipc: comp %d trigger 0x%x failed %d", id, cmd, ret);
226 		ret = IPC4_PIPELINE_STATE_NOT_SET;
227 	}
228 
229 	return ret;
230 }
231 
ipc4_process_glb_message(union ipc4_message_header * ipc4)232 static int ipc4_process_glb_message(union ipc4_message_header *ipc4)
233 {
234 	uint32_t type;
235 	int ret;
236 
237 	type = ipc4->r.type;
238 
239 	switch (type) {
240 	case SOF_IPC4_GLB_BOOT_CONFIG:
241 	case SOF_IPC4_GLB_ROM_CONTROL:
242 	case SOF_IPC4_GLB_IPCGATEWAY_CMD:
243 	case SOF_IPC4_GLB_PERF_MEASUREMENTS_CMD:
244 	case SOF_IPC4_GLB_CHAIN_DMA:
245 	case SOF_IPC4_GLB_LOAD_MULTIPLE_MODULES:
246 	case SOF_IPC4_GLB_UNLOAD_MULTIPLE_MODULES:
247 		tr_err(&ipc_tr, "not implemented ipc message type %d", type);
248 		ret = IPC4_UNAVAILABLE;
249 		break;
250 
251 	/* pipeline settings */
252 	case SOF_IPC4_GLB_CREATE_PIPELINE:
253 		ret = ipc4_create_pipeline(ipc4);
254 		break;
255 	case SOF_IPC4_GLB_DELETE_PIPELINE:
256 		ret = ipc4_delete_pipeline(ipc4);
257 		break;
258 	case SOF_IPC4_GLB_SET_PIPELINE_STATE:
259 		ret = ipc4_set_pipeline_state(ipc4);
260 		break;
261 
262 	case SOF_IPC4_GLB_GET_PIPELINE_STATE:
263 	case SOF_IPC4_GLB_GET_PIPELINE_CONTEXT_SIZE:
264 	case SOF_IPC4_GLB_SAVE_PIPELINE:
265 	case SOF_IPC4_GLB_RESTORE_PIPELINE:
266 		tr_err(&ipc_tr, "not implemented ipc message type %d", type);
267 		break;
268 
269 	/* Loads library (using Code Load or HD/A Host Output DMA) */
270 	case SOF_IPC4_GLB_LOAD_LIBRARY:
271 	case SOF_IPC4_GLB_INTERNAL_MESSAGE:
272 		tr_err(&ipc_tr, "not implemented ipc message type %d", type);
273 		ret = IPC4_UNAVAILABLE;
274 		break;
275 
276 	/* Notification (FW to SW driver) */
277 	case SOF_IPC4_GLB_NOTIFICATION:
278 		tr_err(&ipc_tr, "not implemented ipc message type %d", type);
279 		ret = IPC4_UNAVAILABLE;
280 		break;
281 
282 	default:
283 		tr_err(&ipc_tr, "unsupported ipc message type %d", type);
284 		ret = IPC4_UNAVAILABLE;
285 		break;
286 	}
287 
288 	return ret;
289 }
290 
291 /*
292  * Ipc4 Module message <------> ipc3 module message
293  * init module <-------> create component
294  * bind modules <-------> connect components
295  * module set_large_config <-------> component cmd
296  * delete module <-------> free component
297  */
298 
ipc4_init_module_instance(union ipc4_message_header * ipc4)299 static int ipc4_init_module_instance(union ipc4_message_header *ipc4)
300 {
301 	struct ipc4_module_init_instance module;
302 	struct sof_ipc_comp comp;
303 	struct comp_dev *dev;
304 
305 	memcpy_s(&module, sizeof(module), ipc4, sizeof(module));
306 	tr_dbg(&ipc_tr, "ipc4_init_module_instance %x : %x", (uint32_t)module.header.r.module_id,
307 	       (uint32_t)module.header.r.instance_id);
308 
309 	memset(&comp, 0, sizeof(comp));
310 	comp.id = IPC4_COMP_ID(module.header.r.module_id, module.header.r.instance_id);
311 	comp.pipeline_id = module.data.r.ppl_instance_id;
312 	comp.core = module.data.r.core_id;
313 	dev = comp_new(&comp);
314 	if (!dev) {
315 		tr_err(&ipc_tr, "error: failed to init module %x : %x",
316 		       (uint32_t)module.header.r.module_id,
317 		       (uint32_t)module.header.r.instance_id);
318 		return IPC4_MOD_NOT_INITIALIZED;
319 	}
320 
321 	return 0;
322 }
323 
ipc4_bind_module_instance(union ipc4_message_header * ipc4)324 static int ipc4_bind_module_instance(union ipc4_message_header *ipc4)
325 {
326 	struct ipc4_module_bind_unbind bu;
327 	struct ipc *ipc = ipc_get();
328 
329 	memcpy_s(&bu, sizeof(bu), ipc4, sizeof(bu));
330 	tr_dbg(&ipc_tr, "ipc4_bind_module_instance %x : %x with %x : %x",
331 	       (uint32_t)bu.header.r.module_id, (uint32_t)bu.header.r.instance_id,
332 	       (uint32_t)bu.data.r.dst_module_id, (uint32_t)bu.data.r.dst_instance_id);
333 
334 	return ipc_comp_connect(ipc, (ipc_pipe_comp_connect *)&bu);
335 }
336 
ipc4_unbind_module_instance(union ipc4_message_header * ipc4)337 static int ipc4_unbind_module_instance(union ipc4_message_header *ipc4)
338 {
339 	struct ipc4_module_bind_unbind bu;
340 	struct ipc *ipc = ipc_get();
341 
342 	memcpy_s(&bu, sizeof(bu), ipc4, sizeof(bu));
343 	tr_dbg(&ipc_tr, "ipc4_unbind_module_instance %x : %x with %x : %x",
344 	       (uint32_t)bu.header.r.module_id, (uint32_t)bu.header.r.instance_id,
345 	       (uint32_t)bu.data.r.dst_module_id, (uint32_t)bu.data.r.dst_instance_id);
346 
347 	return ipc_comp_disconnect(ipc, (ipc_pipe_comp_connect *)&bu);
348 }
349 
ipc4_set_large_config_module_instance(union ipc4_message_header * ipc4)350 static int ipc4_set_large_config_module_instance(union ipc4_message_header *ipc4)
351 {
352 	struct ipc4_module_large_config config;
353 	struct comp_dev *dev;
354 	int comp_id;
355 	int ret;
356 
357 	memcpy_s(&config, sizeof(config), ipc4, sizeof(config));
358 	tr_dbg(&ipc_tr, "ipc4_set_large_config_module_instance %x : %x with %x : %x",
359 	       (uint32_t)config.header.r.module_id, (uint32_t)config.header.r.instance_id);
360 
361 	comp_id = IPC4_COMP_ID(config.header.r.module_id, config.header.r.instance_id);
362 	dev = ipc4_get_comp_dev(comp_id);
363 	if (!dev)
364 		return IPC4_MOD_INVALID_ID;
365 
366 	ret = comp_cmd(dev, config.data.r.large_param_id, (void *)MAILBOX_HOSTBOX_BASE,
367 		       config.data.dat);
368 	if (ret < 0) {
369 		tr_dbg(&ipc_tr, "failed to set large_config_module_instance %x : %x with %x : %x",
370 		       (uint32_t)config.header.r.module_id, (uint32_t)config.header.r.instance_id);
371 		ret = IPC4_INVALID_RESOURCE_ID;
372 	}
373 
374 	return ret;
375 }
376 
ipc4_delete_module_instance(union ipc4_message_header * ipc4)377 static int ipc4_delete_module_instance(union ipc4_message_header *ipc4)
378 {
379 	struct ipc4_module_delete_instance module;
380 	struct ipc *ipc = ipc_get();
381 	uint32_t comp_id;
382 	int ret;
383 
384 	memcpy_s(&module, sizeof(module), ipc4, sizeof(module));
385 	tr_dbg(&ipc_tr, "ipc4_delete_module_instance %x : %x", (uint32_t)module.header.r.module_id,
386 	       (uint32_t)module.header.r.instance_id);
387 
388 	comp_id = IPC4_COMP_ID(module.header.r.module_id, module.header.r.instance_id);
389 	ret = ipc_comp_free(ipc, comp_id);
390 	if (ret < 0) {
391 		tr_err(&ipc_tr, "failed to delete module instance %x : %x",
392 		       (uint32_t)module.header.r.module_id,
393 		       (uint32_t)module.header.r.instance_id);
394 		ret = IPC4_INVALID_RESOURCE_ID;
395 	}
396 
397 	return ret;
398 }
399 
ipc4_process_module_message(union ipc4_message_header * ipc4)400 static int ipc4_process_module_message(union ipc4_message_header *ipc4)
401 {
402 	uint32_t type;
403 	int ret;
404 
405 	type = ipc4->r.type;
406 
407 	switch (type) {
408 	case SOF_IPC4_MOD_INIT_INSTANCE:
409 		ret = ipc4_init_module_instance(ipc4);
410 		break;
411 	case SOF_IPC4_MOD_CONFIG_GET:
412 	case SOF_IPC4_MOD_CONFIG_SET:
413 	case SOF_IPC4_MOD_LARGE_CONFIG_GET:
414 		ret = IPC4_UNAVAILABLE;
415 		break;
416 	case SOF_IPC4_MOD_LARGE_CONFIG_SET:
417 		ret = ipc4_set_large_config_module_instance(ipc4);
418 		break;
419 	case SOF_IPC4_MOD_BIND:
420 		ret = ipc4_bind_module_instance(ipc4);
421 		break;
422 	case SOF_IPC4_MOD_UNBIND:
423 		ret = ipc4_unbind_module_instance(ipc4);
424 		break;
425 	case SOF_IPC4_MOD_DELETE_INSTANCE:
426 		ret = ipc4_delete_module_instance(ipc4);
427 		break;
428 	case SOF_IPC4_MOD_SET_DX:
429 	case SOF_IPC4_MOD_SET_D0IX:
430 	case SOF_IPC4_MOD_ENTER_MODULE_RESTORE:
431 	case SOF_IPC4_MOD_EXIT_MODULE_RESTORE:
432 		ret = IPC4_UNAVAILABLE;
433 		break;
434 	default:
435 		ret = IPC4_UNAVAILABLE;
436 		break;
437 	}
438 
439 	return ret;
440 }
441 
mailbox_validate(void)442 ipc_cmd_hdr *mailbox_validate(void)
443 {
444 	ipc_cmd_hdr *hdr = ipc_get()->comp_data;
445 	return hdr;
446 }
447 
448 static struct ipc4_msg_data msg_data;
449 
450 /* fw sends a fw ipc message to send the status of the last host ipc message */
451 static struct ipc_msg msg_reply;
452 
ipc_compact_read_msg(void)453 ipc_cmd_hdr *ipc_compact_read_msg(void)
454 {
455 	ipc_cmd_hdr *hdr = (ipc_cmd_hdr *)msg_data.msg_in;
456 	int words;
457 
458 	words = ipc_platform_compact_read_msg(hdr, 2);
459 	if (!words)
460 		return mailbox_validate();
461 
462 	return ipc_to_hdr(msg_data.msg_in);
463 }
464 
ipc_prepare_to_send(struct ipc_msg * msg)465 ipc_cmd_hdr *ipc_prepare_to_send(struct ipc_msg *msg)
466 {
467 	msg_data.msg_out[0] = msg->header;
468 	msg_data.msg_out[1] = *(uint32_t *)msg->tx_data;
469 
470 	/* the first uint of msg data is sent by ipc data register for ipc4 */
471 	msg->tx_size -= sizeof(uint32_t);
472 	if (msg->tx_size)
473 		mailbox_dspbox_write(0, (uint32_t *)msg->tx_data + 1, msg->tx_size);
474 
475 	return ipc_to_hdr(msg_data.msg_out);
476 }
477 
ipc_boot_complete_msg(ipc_cmd_hdr * header,uint32_t * data)478 void ipc_boot_complete_msg(ipc_cmd_hdr *header, uint32_t *data)
479 {
480 	*header = SOF_IPC4_FW_READY;
481 	*data = 0;
482 }
483 
ipc_cmd(ipc_cmd_hdr * _hdr)484 void ipc_cmd(ipc_cmd_hdr *_hdr)
485 {
486 	union ipc4_message_header *in = ipc_from_hdr(_hdr);
487 	enum ipc4_message_target target;
488 	int err = -EINVAL;
489 
490 	if (!in)
491 		return;
492 
493 	target = in->r.msg_tgt;
494 
495 	switch (target) {
496 	case SOF_IPC4_MESSAGE_TARGET_FW_GEN_MSG:
497 		err = ipc4_process_glb_message(in);
498 		break;
499 	case SOF_IPC4_MESSAGE_TARGET_MODULE_MSG:
500 		err = ipc4_process_module_message(in);
501 		break;
502 	default:
503 		/* should not reach here as we only have 2 message types */
504 		tr_err(&ipc_tr, "ipc4: invalid target %d", target);
505 		err = IPC4_UNKNOWN_MESSAGE_TYPE;
506 	}
507 
508 	if (err)
509 		tr_err(&ipc_tr, "ipc4: %d failed ....", target);
510 
511 	/* FW sends a ipc message to host if request bit is set*/
512 	if (in->r.rsp == SOF_IPC4_MESSAGE_DIR_MSG_REQUEST) {
513 		struct ipc4_message_reply reply;
514 		uint32_t msg_reply_data;
515 
516 		/* copy contents of message received */
517 		reply.header.r.rsp = SOF_IPC4_MESSAGE_DIR_MSG_REPLY;
518 		reply.header.r.status = err;
519 		reply.header.r.msg_tgt = in->r.msg_tgt;
520 		reply.header.r.type = in->r.type;
521 		reply.data.dat = 0;
522 
523 		msg_reply_data = 0;
524 
525 		msg_reply.header = reply.header.dat;
526 		msg_reply.tx_data = &msg_reply_data;
527 		msg_reply.tx_size = sizeof(msg_reply_data);
528 		ipc_msg_send(&msg_reply, &reply.data.dat, true);
529 	}
530 }
531