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, ¶m_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(¶ms, 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, ¶ms);
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