1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) |
---|---|
2 | // |
3 | // This file is provided under a dual BSD/GPLv2 license. When using or |
4 | // redistributing this file, you may do so under either license. |
5 | // |
6 | // Copyright(c) 2021 Intel Corporation. All rights reserved. |
7 | // |
8 | // |
9 | |
10 | #include <sound/sof/stream.h> |
11 | #include <sound/sof/control.h> |
12 | #include <trace/events/sof.h> |
13 | #include "sof-priv.h" |
14 | #include "sof-audio.h" |
15 | #include "ipc3-priv.h" |
16 | #include "ops.h" |
17 | |
18 | typedef void (*ipc3_rx_callback)(struct snd_sof_dev *sdev, void *msg_buf); |
19 | |
20 | #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC) |
21 | static void ipc3_log_header(struct device *dev, u8 *text, u32 cmd) |
22 | { |
23 | u8 *str; |
24 | u8 *str2 = NULL; |
25 | u32 glb; |
26 | u32 type; |
27 | bool is_sof_ipc_stream_position = false; |
28 | |
29 | glb = cmd & SOF_GLB_TYPE_MASK; |
30 | type = cmd & SOF_CMD_TYPE_MASK; |
31 | |
32 | switch (glb) { |
33 | case SOF_IPC_GLB_REPLY: |
34 | str = "GLB_REPLY"; break; |
35 | case SOF_IPC_GLB_COMPOUND: |
36 | str = "GLB_COMPOUND"; break; |
37 | case SOF_IPC_GLB_TPLG_MSG: |
38 | str = "GLB_TPLG_MSG"; |
39 | switch (type) { |
40 | case SOF_IPC_TPLG_COMP_NEW: |
41 | str2 = "COMP_NEW"; break; |
42 | case SOF_IPC_TPLG_COMP_FREE: |
43 | str2 = "COMP_FREE"; break; |
44 | case SOF_IPC_TPLG_COMP_CONNECT: |
45 | str2 = "COMP_CONNECT"; break; |
46 | case SOF_IPC_TPLG_PIPE_NEW: |
47 | str2 = "PIPE_NEW"; break; |
48 | case SOF_IPC_TPLG_PIPE_FREE: |
49 | str2 = "PIPE_FREE"; break; |
50 | case SOF_IPC_TPLG_PIPE_CONNECT: |
51 | str2 = "PIPE_CONNECT"; break; |
52 | case SOF_IPC_TPLG_PIPE_COMPLETE: |
53 | str2 = "PIPE_COMPLETE"; break; |
54 | case SOF_IPC_TPLG_BUFFER_NEW: |
55 | str2 = "BUFFER_NEW"; break; |
56 | case SOF_IPC_TPLG_BUFFER_FREE: |
57 | str2 = "BUFFER_FREE"; break; |
58 | default: |
59 | str2 = "unknown type"; break; |
60 | } |
61 | break; |
62 | case SOF_IPC_GLB_PM_MSG: |
63 | str = "GLB_PM_MSG"; |
64 | switch (type) { |
65 | case SOF_IPC_PM_CTX_SAVE: |
66 | str2 = "CTX_SAVE"; break; |
67 | case SOF_IPC_PM_CTX_RESTORE: |
68 | str2 = "CTX_RESTORE"; break; |
69 | case SOF_IPC_PM_CTX_SIZE: |
70 | str2 = "CTX_SIZE"; break; |
71 | case SOF_IPC_PM_CLK_SET: |
72 | str2 = "CLK_SET"; break; |
73 | case SOF_IPC_PM_CLK_GET: |
74 | str2 = "CLK_GET"; break; |
75 | case SOF_IPC_PM_CLK_REQ: |
76 | str2 = "CLK_REQ"; break; |
77 | case SOF_IPC_PM_CORE_ENABLE: |
78 | str2 = "CORE_ENABLE"; break; |
79 | case SOF_IPC_PM_GATE: |
80 | str2 = "GATE"; break; |
81 | default: |
82 | str2 = "unknown type"; break; |
83 | } |
84 | break; |
85 | case SOF_IPC_GLB_COMP_MSG: |
86 | str = "GLB_COMP_MSG"; |
87 | switch (type) { |
88 | case SOF_IPC_COMP_SET_VALUE: |
89 | str2 = "SET_VALUE"; break; |
90 | case SOF_IPC_COMP_GET_VALUE: |
91 | str2 = "GET_VALUE"; break; |
92 | case SOF_IPC_COMP_SET_DATA: |
93 | str2 = "SET_DATA"; break; |
94 | case SOF_IPC_COMP_GET_DATA: |
95 | str2 = "GET_DATA"; break; |
96 | default: |
97 | str2 = "unknown type"; break; |
98 | } |
99 | break; |
100 | case SOF_IPC_GLB_STREAM_MSG: |
101 | str = "GLB_STREAM_MSG"; |
102 | switch (type) { |
103 | case SOF_IPC_STREAM_PCM_PARAMS: |
104 | str2 = "PCM_PARAMS"; break; |
105 | case SOF_IPC_STREAM_PCM_PARAMS_REPLY: |
106 | str2 = "PCM_REPLY"; break; |
107 | case SOF_IPC_STREAM_PCM_FREE: |
108 | str2 = "PCM_FREE"; break; |
109 | case SOF_IPC_STREAM_TRIG_START: |
110 | str2 = "TRIG_START"; break; |
111 | case SOF_IPC_STREAM_TRIG_STOP: |
112 | str2 = "TRIG_STOP"; break; |
113 | case SOF_IPC_STREAM_TRIG_PAUSE: |
114 | str2 = "TRIG_PAUSE"; break; |
115 | case SOF_IPC_STREAM_TRIG_RELEASE: |
116 | str2 = "TRIG_RELEASE"; break; |
117 | case SOF_IPC_STREAM_TRIG_DRAIN: |
118 | str2 = "TRIG_DRAIN"; break; |
119 | case SOF_IPC_STREAM_TRIG_XRUN: |
120 | str2 = "TRIG_XRUN"; break; |
121 | case SOF_IPC_STREAM_POSITION: |
122 | is_sof_ipc_stream_position = true; |
123 | str2 = "POSITION"; break; |
124 | case SOF_IPC_STREAM_VORBIS_PARAMS: |
125 | str2 = "VORBIS_PARAMS"; break; |
126 | case SOF_IPC_STREAM_VORBIS_FREE: |
127 | str2 = "VORBIS_FREE"; break; |
128 | default: |
129 | str2 = "unknown type"; break; |
130 | } |
131 | break; |
132 | case SOF_IPC_FW_READY: |
133 | str = "FW_READY"; break; |
134 | case SOF_IPC_GLB_DAI_MSG: |
135 | str = "GLB_DAI_MSG"; |
136 | switch (type) { |
137 | case SOF_IPC_DAI_CONFIG: |
138 | str2 = "CONFIG"; break; |
139 | case SOF_IPC_DAI_LOOPBACK: |
140 | str2 = "LOOPBACK"; break; |
141 | default: |
142 | str2 = "unknown type"; break; |
143 | } |
144 | break; |
145 | case SOF_IPC_GLB_TRACE_MSG: |
146 | str = "GLB_TRACE_MSG"; |
147 | switch (type) { |
148 | case SOF_IPC_TRACE_DMA_PARAMS: |
149 | str2 = "DMA_PARAMS"; break; |
150 | case SOF_IPC_TRACE_DMA_POSITION: |
151 | if (!sof_debug_check_flag(SOF_DBG_PRINT_DMA_POSITION_UPDATE_LOGS)) |
152 | return; |
153 | str2 = "DMA_POSITION"; break; |
154 | case SOF_IPC_TRACE_DMA_PARAMS_EXT: |
155 | str2 = "DMA_PARAMS_EXT"; break; |
156 | case SOF_IPC_TRACE_FILTER_UPDATE: |
157 | str2 = "FILTER_UPDATE"; break; |
158 | case SOF_IPC_TRACE_DMA_FREE: |
159 | str2 = "DMA_FREE"; break; |
160 | default: |
161 | str2 = "unknown type"; break; |
162 | } |
163 | break; |
164 | case SOF_IPC_GLB_TEST_MSG: |
165 | str = "GLB_TEST_MSG"; |
166 | switch (type) { |
167 | case SOF_IPC_TEST_IPC_FLOOD: |
168 | str2 = "IPC_FLOOD"; break; |
169 | default: |
170 | str2 = "unknown type"; break; |
171 | } |
172 | break; |
173 | case SOF_IPC_GLB_DEBUG: |
174 | str = "GLB_DEBUG"; |
175 | switch (type) { |
176 | case SOF_IPC_DEBUG_MEM_USAGE: |
177 | str2 = "MEM_USAGE"; break; |
178 | default: |
179 | str2 = "unknown type"; break; |
180 | } |
181 | break; |
182 | case SOF_IPC_GLB_PROBE: |
183 | str = "GLB_PROBE"; |
184 | switch (type) { |
185 | case SOF_IPC_PROBE_INIT: |
186 | str2 = "INIT"; break; |
187 | case SOF_IPC_PROBE_DEINIT: |
188 | str2 = "DEINIT"; break; |
189 | case SOF_IPC_PROBE_DMA_ADD: |
190 | str2 = "DMA_ADD"; break; |
191 | case SOF_IPC_PROBE_DMA_INFO: |
192 | str2 = "DMA_INFO"; break; |
193 | case SOF_IPC_PROBE_DMA_REMOVE: |
194 | str2 = "DMA_REMOVE"; break; |
195 | case SOF_IPC_PROBE_POINT_ADD: |
196 | str2 = "POINT_ADD"; break; |
197 | case SOF_IPC_PROBE_POINT_INFO: |
198 | str2 = "POINT_INFO"; break; |
199 | case SOF_IPC_PROBE_POINT_REMOVE: |
200 | str2 = "POINT_REMOVE"; break; |
201 | default: |
202 | str2 = "unknown type"; break; |
203 | } |
204 | break; |
205 | default: |
206 | str = "unknown GLB command"; break; |
207 | } |
208 | |
209 | if (str2) { |
210 | if (is_sof_ipc_stream_position) |
211 | trace_sof_stream_position_ipc_rx(dev); |
212 | else |
213 | dev_dbg(dev, "%s: 0x%x: %s: %s\n", text, cmd, str, str2); |
214 | } else { |
215 | dev_dbg(dev, "%s: 0x%x: %s\n", text, cmd, str); |
216 | } |
217 | } |
218 | #else |
219 | static inline void ipc3_log_header(struct device *dev, u8 *text, u32 cmd) |
220 | { |
221 | if ((cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_TRACE_MSG) |
222 | dev_dbg(dev, "%s: 0x%x\n", text, cmd); |
223 | } |
224 | #endif |
225 | |
226 | static void sof_ipc3_dump_payload(struct snd_sof_dev *sdev, |
227 | void *ipc_data, size_t size) |
228 | { |
229 | printk(KERN_DEBUG "Size of payload following the header: %zu\n", size); |
230 | print_hex_dump_debug("Message payload: ", DUMP_PREFIX_OFFSET, |
231 | 16, 4, ipc_data, size, false); |
232 | } |
233 | |
234 | static int sof_ipc3_get_reply(struct snd_sof_dev *sdev) |
235 | { |
236 | struct snd_sof_ipc_msg *msg = sdev->msg; |
237 | struct sof_ipc_reply *reply; |
238 | int ret = 0; |
239 | |
240 | /* get the generic reply */ |
241 | reply = msg->reply_data; |
242 | snd_sof_dsp_mailbox_read(sdev, offset: sdev->host_box.offset, dest: reply, bytes: sizeof(*reply)); |
243 | |
244 | if (reply->error < 0) |
245 | return reply->error; |
246 | |
247 | if (!reply->hdr.size) { |
248 | /* Reply should always be >= sizeof(struct sof_ipc_reply) */ |
249 | if (msg->reply_size) |
250 | dev_err(sdev->dev, |
251 | "empty reply received, expected %zu bytes\n", |
252 | msg->reply_size); |
253 | else |
254 | dev_err(sdev->dev, "empty reply received\n"); |
255 | |
256 | return -EINVAL; |
257 | } |
258 | |
259 | if (msg->reply_size > 0) { |
260 | if (reply->hdr.size == msg->reply_size) { |
261 | ret = 0; |
262 | } else if (reply->hdr.size < msg->reply_size) { |
263 | dev_dbg(sdev->dev, |
264 | "reply size (%u) is less than expected (%zu)\n", |
265 | reply->hdr.size, msg->reply_size); |
266 | |
267 | msg->reply_size = reply->hdr.size; |
268 | ret = 0; |
269 | } else { |
270 | dev_err(sdev->dev, |
271 | "reply size (%u) exceeds the buffer size (%zu)\n", |
272 | reply->hdr.size, msg->reply_size); |
273 | ret = -EINVAL; |
274 | } |
275 | |
276 | /* |
277 | * get the full message if reply->hdr.size <= msg->reply_size |
278 | * and the reply->hdr.size > sizeof(struct sof_ipc_reply) |
279 | */ |
280 | if (!ret && msg->reply_size > sizeof(*reply)) |
281 | snd_sof_dsp_mailbox_read(sdev, offset: sdev->host_box.offset, |
282 | dest: msg->reply_data, bytes: msg->reply_size); |
283 | } |
284 | |
285 | return ret; |
286 | } |
287 | |
288 | /* wait for IPC message reply */ |
289 | static int ipc3_wait_tx_done(struct snd_sof_ipc *ipc, void *reply_data) |
290 | { |
291 | struct snd_sof_ipc_msg *msg = &ipc->msg; |
292 | struct sof_ipc_cmd_hdr *hdr = msg->msg_data; |
293 | struct snd_sof_dev *sdev = ipc->sdev; |
294 | int ret; |
295 | |
296 | /* wait for DSP IPC completion */ |
297 | ret = wait_event_timeout(msg->waitq, msg->ipc_complete, |
298 | msecs_to_jiffies(sdev->ipc_timeout)); |
299 | |
300 | if (ret == 0) { |
301 | dev_err(sdev->dev, |
302 | "ipc tx timed out for %#x (msg/reply size: %d/%zu)\n", |
303 | hdr->cmd, hdr->size, msg->reply_size); |
304 | snd_sof_handle_fw_exception(sdev: ipc->sdev, msg: "IPC timeout"); |
305 | ret = -ETIMEDOUT; |
306 | } else { |
307 | ret = msg->reply_error; |
308 | if (ret < 0) { |
309 | dev_err(sdev->dev, |
310 | "ipc tx error for %#x (msg/reply size: %d/%zu): %d\n", |
311 | hdr->cmd, hdr->size, msg->reply_size, ret); |
312 | } else { |
313 | if (sof_debug_check_flag(SOF_DBG_PRINT_IPC_SUCCESS_LOGS)) |
314 | ipc3_log_header(dev: sdev->dev, text: "ipc tx succeeded", cmd: hdr->cmd); |
315 | if (reply_data && msg->reply_size) |
316 | /* copy the data returned from DSP */ |
317 | memcpy(reply_data, msg->reply_data, |
318 | msg->reply_size); |
319 | } |
320 | |
321 | /* re-enable dumps after successful IPC tx */ |
322 | if (sdev->ipc_dump_printed) { |
323 | sdev->dbg_dump_printed = false; |
324 | sdev->ipc_dump_printed = false; |
325 | } |
326 | } |
327 | |
328 | return ret; |
329 | } |
330 | |
331 | /* send IPC message from host to DSP */ |
332 | static int ipc3_tx_msg_unlocked(struct snd_sof_ipc *ipc, |
333 | void *msg_data, size_t msg_bytes, |
334 | void *reply_data, size_t reply_bytes) |
335 | { |
336 | struct sof_ipc_cmd_hdr *hdr = msg_data; |
337 | struct snd_sof_dev *sdev = ipc->sdev; |
338 | int ret; |
339 | |
340 | ipc3_log_header(dev: sdev->dev, text: "ipc tx", cmd: hdr->cmd); |
341 | |
342 | ret = sof_ipc_send_msg(sdev, msg_data, msg_bytes, reply_bytes); |
343 | |
344 | if (ret) { |
345 | dev_err_ratelimited(sdev->dev, |
346 | "%s: ipc message send for %#x failed: %d\n", |
347 | __func__, hdr->cmd, ret); |
348 | return ret; |
349 | } |
350 | |
351 | /* now wait for completion */ |
352 | return ipc3_wait_tx_done(ipc, reply_data); |
353 | } |
354 | |
355 | static int sof_ipc3_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes, |
356 | void *reply_data, size_t reply_bytes, bool no_pm) |
357 | { |
358 | struct snd_sof_ipc *ipc = sdev->ipc; |
359 | int ret; |
360 | |
361 | if (!msg_data || msg_bytes < sizeof(struct sof_ipc_cmd_hdr)) { |
362 | dev_err_ratelimited(sdev->dev, "No IPC message to send\n"); |
363 | return -EINVAL; |
364 | } |
365 | |
366 | if (!no_pm) { |
367 | const struct sof_dsp_power_state target_state = { |
368 | .state = SOF_DSP_PM_D0, |
369 | }; |
370 | |
371 | /* ensure the DSP is in D0 before sending a new IPC */ |
372 | ret = snd_sof_dsp_set_power_state(sdev, target_state: &target_state); |
373 | if (ret < 0) { |
374 | dev_err(sdev->dev, "%s: resuming DSP failed: %d\n", |
375 | __func__, ret); |
376 | return ret; |
377 | } |
378 | } |
379 | |
380 | /* Serialise IPC TX */ |
381 | mutex_lock(&ipc->tx_mutex); |
382 | |
383 | ret = ipc3_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes); |
384 | |
385 | if (sof_debug_check_flag(SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD)) { |
386 | size_t payload_bytes, header_bytes; |
387 | char *payload = NULL; |
388 | |
389 | /* payload is indicated by non zero msg/reply_bytes */ |
390 | if (msg_bytes > sizeof(struct sof_ipc_cmd_hdr)) { |
391 | payload = msg_data; |
392 | |
393 | header_bytes = sizeof(struct sof_ipc_cmd_hdr); |
394 | payload_bytes = msg_bytes - header_bytes; |
395 | } else if (reply_bytes > sizeof(struct sof_ipc_reply)) { |
396 | payload = reply_data; |
397 | |
398 | header_bytes = sizeof(struct sof_ipc_reply); |
399 | payload_bytes = reply_bytes - header_bytes; |
400 | } |
401 | |
402 | if (payload) { |
403 | payload += header_bytes; |
404 | sof_ipc3_dump_payload(sdev, ipc_data: payload, size: payload_bytes); |
405 | } |
406 | } |
407 | |
408 | mutex_unlock(lock: &ipc->tx_mutex); |
409 | |
410 | return ret; |
411 | } |
412 | |
413 | static int sof_ipc3_set_get_data(struct snd_sof_dev *sdev, void *data, size_t data_bytes, |
414 | bool set) |
415 | { |
416 | size_t msg_bytes, hdr_bytes, payload_size, send_bytes; |
417 | struct sof_ipc_ctrl_data *cdata = data; |
418 | struct sof_ipc_ctrl_data *cdata_chunk; |
419 | struct snd_sof_ipc *ipc = sdev->ipc; |
420 | size_t offset = 0; |
421 | u8 *src, *dst; |
422 | u32 num_msg; |
423 | int ret = 0; |
424 | int i; |
425 | |
426 | if (!cdata || data_bytes < sizeof(*cdata)) |
427 | return -EINVAL; |
428 | |
429 | if ((cdata->rhdr.hdr.cmd & SOF_GLB_TYPE_MASK) != SOF_IPC_GLB_COMP_MSG) { |
430 | dev_err(sdev->dev, "%s: Not supported message type of %#x\n", |
431 | __func__, cdata->rhdr.hdr.cmd); |
432 | return -EINVAL; |
433 | } |
434 | |
435 | /* send normal size ipc in one part */ |
436 | if (cdata->rhdr.hdr.size <= ipc->max_payload_size) |
437 | return sof_ipc3_tx_msg(sdev, msg_data: cdata, msg_bytes: cdata->rhdr.hdr.size, |
438 | reply_data: cdata, reply_bytes: cdata->rhdr.hdr.size, no_pm: false); |
439 | |
440 | cdata_chunk = kzalloc(size: ipc->max_payload_size, GFP_KERNEL); |
441 | if (!cdata_chunk) |
442 | return -ENOMEM; |
443 | |
444 | switch (cdata->type) { |
445 | case SOF_CTRL_TYPE_VALUE_CHAN_GET: |
446 | case SOF_CTRL_TYPE_VALUE_CHAN_SET: |
447 | hdr_bytes = sizeof(struct sof_ipc_ctrl_data); |
448 | if (set) { |
449 | src = (u8 *)cdata->chanv; |
450 | dst = (u8 *)cdata_chunk->chanv; |
451 | } else { |
452 | src = (u8 *)cdata_chunk->chanv; |
453 | dst = (u8 *)cdata->chanv; |
454 | } |
455 | break; |
456 | case SOF_CTRL_TYPE_DATA_GET: |
457 | case SOF_CTRL_TYPE_DATA_SET: |
458 | hdr_bytes = sizeof(struct sof_ipc_ctrl_data) + sizeof(struct sof_abi_hdr); |
459 | if (set) { |
460 | src = (u8 *)cdata->data->data; |
461 | dst = (u8 *)cdata_chunk->data->data; |
462 | } else { |
463 | src = (u8 *)cdata_chunk->data->data; |
464 | dst = (u8 *)cdata->data->data; |
465 | } |
466 | break; |
467 | default: |
468 | kfree(objp: cdata_chunk); |
469 | return -EINVAL; |
470 | } |
471 | |
472 | msg_bytes = cdata->rhdr.hdr.size - hdr_bytes; |
473 | payload_size = ipc->max_payload_size - hdr_bytes; |
474 | num_msg = DIV_ROUND_UP(msg_bytes, payload_size); |
475 | |
476 | /* copy the header data */ |
477 | memcpy(cdata_chunk, cdata, hdr_bytes); |
478 | |
479 | /* Serialise IPC TX */ |
480 | mutex_lock(&sdev->ipc->tx_mutex); |
481 | |
482 | /* copy the payload data in a loop */ |
483 | for (i = 0; i < num_msg; i++) { |
484 | send_bytes = min(msg_bytes, payload_size); |
485 | cdata_chunk->num_elems = send_bytes; |
486 | cdata_chunk->rhdr.hdr.size = hdr_bytes + send_bytes; |
487 | cdata_chunk->msg_index = i; |
488 | msg_bytes -= send_bytes; |
489 | cdata_chunk->elems_remaining = msg_bytes; |
490 | |
491 | if (set) |
492 | memcpy(dst, src + offset, send_bytes); |
493 | |
494 | ret = ipc3_tx_msg_unlocked(ipc: sdev->ipc, |
495 | msg_data: cdata_chunk, msg_bytes: cdata_chunk->rhdr.hdr.size, |
496 | reply_data: cdata_chunk, reply_bytes: cdata_chunk->rhdr.hdr.size); |
497 | if (ret < 0) |
498 | break; |
499 | |
500 | if (!set) |
501 | memcpy(dst + offset, src, send_bytes); |
502 | |
503 | offset += payload_size; |
504 | } |
505 | |
506 | if (sof_debug_check_flag(SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD)) { |
507 | size_t header_bytes = sizeof(struct sof_ipc_reply); |
508 | char *payload = (char *)cdata; |
509 | |
510 | payload += header_bytes; |
511 | sof_ipc3_dump_payload(sdev, ipc_data: payload, size: data_bytes - header_bytes); |
512 | } |
513 | |
514 | mutex_unlock(lock: &sdev->ipc->tx_mutex); |
515 | |
516 | kfree(objp: cdata_chunk); |
517 | |
518 | return ret; |
519 | } |
520 | |
521 | int sof_ipc3_get_ext_windows(struct snd_sof_dev *sdev, |
522 | const struct sof_ipc_ext_data_hdr *ext_hdr) |
523 | { |
524 | const struct sof_ipc_window *w = |
525 | container_of(ext_hdr, struct sof_ipc_window, ext_hdr); |
526 | |
527 | if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS) |
528 | return -EINVAL; |
529 | |
530 | if (sdev->info_window) { |
531 | if (memcmp(p: sdev->info_window, q: w, size: ext_hdr->hdr.size)) { |
532 | dev_err(sdev->dev, "mismatch between window descriptor from extended manifest and mailbox"); |
533 | return -EINVAL; |
534 | } |
535 | return 0; |
536 | } |
537 | |
538 | /* keep a local copy of the data */ |
539 | sdev->info_window = devm_kmemdup(dev: sdev->dev, src: w, len: ext_hdr->hdr.size, GFP_KERNEL); |
540 | if (!sdev->info_window) |
541 | return -ENOMEM; |
542 | |
543 | return 0; |
544 | } |
545 | |
546 | int sof_ipc3_get_cc_info(struct snd_sof_dev *sdev, |
547 | const struct sof_ipc_ext_data_hdr *ext_hdr) |
548 | { |
549 | int ret; |
550 | |
551 | const struct sof_ipc_cc_version *cc = |
552 | container_of(ext_hdr, struct sof_ipc_cc_version, ext_hdr); |
553 | |
554 | if (sdev->cc_version) { |
555 | if (memcmp(p: sdev->cc_version, q: cc, size: cc->ext_hdr.hdr.size)) { |
556 | dev_err(sdev->dev, |
557 | "Receive diverged cc_version descriptions"); |
558 | return -EINVAL; |
559 | } |
560 | return 0; |
561 | } |
562 | |
563 | dev_dbg(sdev->dev, |
564 | "Firmware info: used compiler %s %d:%d:%d%s used optimization flags %s\n", |
565 | cc->name, cc->major, cc->minor, cc->micro, cc->desc, cc->optim); |
566 | |
567 | /* create read-only cc_version debugfs to store compiler version info */ |
568 | /* use local copy of the cc_version to prevent data corruption */ |
569 | if (sdev->first_boot) { |
570 | sdev->cc_version = devm_kmemdup(dev: sdev->dev, src: cc, len: cc->ext_hdr.hdr.size, GFP_KERNEL); |
571 | if (!sdev->cc_version) |
572 | return -ENOMEM; |
573 | |
574 | ret = snd_sof_debugfs_buf_item(sdev, base: sdev->cc_version, |
575 | size: cc->ext_hdr.hdr.size, |
576 | name: "cc_version", mode: 0444); |
577 | |
578 | /* errors are only due to memory allocation, not debugfs */ |
579 | if (ret < 0) { |
580 | dev_err(sdev->dev, "snd_sof_debugfs_buf_item failed\n"); |
581 | return ret; |
582 | } |
583 | } |
584 | |
585 | return 0; |
586 | } |
587 | |
588 | /* parse the extended FW boot data structures from FW boot message */ |
589 | static int ipc3_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 offset) |
590 | { |
591 | struct sof_ipc_ext_data_hdr *ext_hdr; |
592 | void *ext_data; |
593 | int ret = 0; |
594 | |
595 | ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL); |
596 | if (!ext_data) |
597 | return -ENOMEM; |
598 | |
599 | /* get first header */ |
600 | snd_sof_dsp_block_read(sdev, blk_type: SOF_FW_BLK_TYPE_SRAM, offset, dest: ext_data, |
601 | bytes: sizeof(*ext_hdr)); |
602 | ext_hdr = ext_data; |
603 | |
604 | while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) { |
605 | /* read in ext structure */ |
606 | snd_sof_dsp_block_read(sdev, blk_type: SOF_FW_BLK_TYPE_SRAM, |
607 | offset: offset + sizeof(*ext_hdr), |
608 | dest: (void *)((u8 *)ext_data + sizeof(*ext_hdr)), |
609 | bytes: ext_hdr->hdr.size - sizeof(*ext_hdr)); |
610 | |
611 | dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n", |
612 | ext_hdr->type, ext_hdr->hdr.size); |
613 | |
614 | /* process structure data */ |
615 | switch (ext_hdr->type) { |
616 | case SOF_IPC_EXT_WINDOW: |
617 | ret = sof_ipc3_get_ext_windows(sdev, ext_hdr); |
618 | break; |
619 | case SOF_IPC_EXT_CC_INFO: |
620 | ret = sof_ipc3_get_cc_info(sdev, ext_hdr); |
621 | break; |
622 | case SOF_IPC_EXT_UNUSED: |
623 | case SOF_IPC_EXT_PROBE_INFO: |
624 | case SOF_IPC_EXT_USER_ABI_INFO: |
625 | /* They are supported but we don't do anything here */ |
626 | break; |
627 | default: |
628 | dev_info(sdev->dev, "unknown ext header type %d size 0x%x\n", |
629 | ext_hdr->type, ext_hdr->hdr.size); |
630 | ret = 0; |
631 | break; |
632 | } |
633 | |
634 | if (ret < 0) { |
635 | dev_err(sdev->dev, "Failed to parse ext data type %d\n", |
636 | ext_hdr->type); |
637 | break; |
638 | } |
639 | |
640 | /* move to next header */ |
641 | offset += ext_hdr->hdr.size; |
642 | snd_sof_dsp_block_read(sdev, blk_type: SOF_FW_BLK_TYPE_SRAM, offset, dest: ext_data, |
643 | bytes: sizeof(*ext_hdr)); |
644 | ext_hdr = ext_data; |
645 | } |
646 | |
647 | kfree(objp: ext_data); |
648 | return ret; |
649 | } |
650 | |
651 | static void ipc3_get_windows(struct snd_sof_dev *sdev) |
652 | { |
653 | struct sof_ipc_window_elem *elem; |
654 | u32 outbox_offset = 0; |
655 | u32 stream_offset = 0; |
656 | u32 inbox_offset = 0; |
657 | u32 outbox_size = 0; |
658 | u32 stream_size = 0; |
659 | u32 inbox_size = 0; |
660 | u32 debug_size = 0; |
661 | u32 debug_offset = 0; |
662 | int window_offset; |
663 | int i; |
664 | |
665 | if (!sdev->info_window) { |
666 | dev_err(sdev->dev, "%s: No window info present\n", __func__); |
667 | return; |
668 | } |
669 | |
670 | for (i = 0; i < sdev->info_window->num_windows; i++) { |
671 | elem = &sdev->info_window->window[i]; |
672 | |
673 | window_offset = snd_sof_dsp_get_window_offset(sdev, id: elem->id); |
674 | if (window_offset < 0) { |
675 | dev_warn(sdev->dev, "No offset for window %d\n", elem->id); |
676 | continue; |
677 | } |
678 | |
679 | switch (elem->type) { |
680 | case SOF_IPC_REGION_UPBOX: |
681 | inbox_offset = window_offset + elem->offset; |
682 | inbox_size = elem->size; |
683 | snd_sof_debugfs_add_region_item(sdev, blk_type: SOF_FW_BLK_TYPE_SRAM, |
684 | offset: inbox_offset, |
685 | size: elem->size, name: "inbox", |
686 | access_type: SOF_DEBUGFS_ACCESS_D0_ONLY); |
687 | break; |
688 | case SOF_IPC_REGION_DOWNBOX: |
689 | outbox_offset = window_offset + elem->offset; |
690 | outbox_size = elem->size; |
691 | snd_sof_debugfs_add_region_item(sdev, blk_type: SOF_FW_BLK_TYPE_SRAM, |
692 | offset: outbox_offset, |
693 | size: elem->size, name: "outbox", |
694 | access_type: SOF_DEBUGFS_ACCESS_D0_ONLY); |
695 | break; |
696 | case SOF_IPC_REGION_TRACE: |
697 | snd_sof_debugfs_add_region_item(sdev, blk_type: SOF_FW_BLK_TYPE_SRAM, |
698 | offset: window_offset + elem->offset, |
699 | size: elem->size, name: "etrace", |
700 | access_type: SOF_DEBUGFS_ACCESS_D0_ONLY); |
701 | break; |
702 | case SOF_IPC_REGION_DEBUG: |
703 | debug_offset = window_offset + elem->offset; |
704 | debug_size = elem->size; |
705 | snd_sof_debugfs_add_region_item(sdev, blk_type: SOF_FW_BLK_TYPE_SRAM, |
706 | offset: window_offset + elem->offset, |
707 | size: elem->size, name: "debug", |
708 | access_type: SOF_DEBUGFS_ACCESS_D0_ONLY); |
709 | break; |
710 | case SOF_IPC_REGION_STREAM: |
711 | stream_offset = window_offset + elem->offset; |
712 | stream_size = elem->size; |
713 | snd_sof_debugfs_add_region_item(sdev, blk_type: SOF_FW_BLK_TYPE_SRAM, |
714 | offset: stream_offset, |
715 | size: elem->size, name: "stream", |
716 | access_type: SOF_DEBUGFS_ACCESS_D0_ONLY); |
717 | break; |
718 | case SOF_IPC_REGION_REGS: |
719 | snd_sof_debugfs_add_region_item(sdev, blk_type: SOF_FW_BLK_TYPE_SRAM, |
720 | offset: window_offset + elem->offset, |
721 | size: elem->size, name: "regs", |
722 | access_type: SOF_DEBUGFS_ACCESS_D0_ONLY); |
723 | break; |
724 | case SOF_IPC_REGION_EXCEPTION: |
725 | sdev->dsp_oops_offset = window_offset + elem->offset; |
726 | snd_sof_debugfs_add_region_item(sdev, blk_type: SOF_FW_BLK_TYPE_SRAM, |
727 | offset: window_offset + elem->offset, |
728 | size: elem->size, name: "exception", |
729 | access_type: SOF_DEBUGFS_ACCESS_D0_ONLY); |
730 | break; |
731 | default: |
732 | dev_err(sdev->dev, "%s: Illegal window info: %u\n", |
733 | __func__, elem->type); |
734 | return; |
735 | } |
736 | } |
737 | |
738 | if (outbox_size == 0 || inbox_size == 0) { |
739 | dev_err(sdev->dev, "%s: Illegal mailbox window\n", __func__); |
740 | return; |
741 | } |
742 | |
743 | sdev->dsp_box.offset = inbox_offset; |
744 | sdev->dsp_box.size = inbox_size; |
745 | |
746 | sdev->host_box.offset = outbox_offset; |
747 | sdev->host_box.size = outbox_size; |
748 | |
749 | sdev->stream_box.offset = stream_offset; |
750 | sdev->stream_box.size = stream_size; |
751 | |
752 | sdev->debug_box.offset = debug_offset; |
753 | sdev->debug_box.size = debug_size; |
754 | |
755 | dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", |
756 | inbox_offset, inbox_size); |
757 | dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", |
758 | outbox_offset, outbox_size); |
759 | dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", |
760 | stream_offset, stream_size); |
761 | dev_dbg(sdev->dev, " debug region 0x%x - size 0x%x\n", |
762 | debug_offset, debug_size); |
763 | } |
764 | |
765 | static int ipc3_init_reply_data_buffer(struct snd_sof_dev *sdev) |
766 | { |
767 | struct snd_sof_ipc_msg *msg = &sdev->ipc->msg; |
768 | |
769 | msg->reply_data = devm_kzalloc(dev: sdev->dev, SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); |
770 | if (!msg->reply_data) |
771 | return -ENOMEM; |
772 | |
773 | sdev->ipc->max_payload_size = SOF_IPC_MSG_MAX_SIZE; |
774 | |
775 | return 0; |
776 | } |
777 | |
778 | int sof_ipc3_validate_fw_version(struct snd_sof_dev *sdev) |
779 | { |
780 | struct sof_ipc_fw_ready *ready = &sdev->fw_ready; |
781 | struct sof_ipc_fw_version *v = &ready->version; |
782 | |
783 | dev_info(sdev->dev, |
784 | "Firmware info: version %d:%d:%d-%s\n", v->major, v->minor, |
785 | v->micro, v->tag); |
786 | dev_info(sdev->dev, |
787 | "Firmware: ABI %d:%d:%d Kernel ABI %d:%d:%d\n", |
788 | SOF_ABI_VERSION_MAJOR(v->abi_version), |
789 | SOF_ABI_VERSION_MINOR(v->abi_version), |
790 | SOF_ABI_VERSION_PATCH(v->abi_version), |
791 | SOF_ABI_MAJOR, SOF_ABI_MINOR, SOF_ABI_PATCH); |
792 | |
793 | if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, v->abi_version)) { |
794 | dev_err(sdev->dev, "incompatible FW ABI version\n"); |
795 | return -EINVAL; |
796 | } |
797 | |
798 | if (IS_ENABLED(CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS) && |
799 | SOF_ABI_VERSION_MINOR(v->abi_version) > SOF_ABI_MINOR) { |
800 | dev_err(sdev->dev, "FW ABI is more recent than kernel\n"); |
801 | return -EINVAL; |
802 | } |
803 | |
804 | if (ready->flags & SOF_IPC_INFO_BUILD) { |
805 | dev_info(sdev->dev, |
806 | "Firmware debug build %d on %s-%s - options:\n" |
807 | " GDB: %s\n" |
808 | " lock debug: %s\n" |
809 | " lock vdebug: %s\n", |
810 | v->build, v->date, v->time, |
811 | (ready->flags & SOF_IPC_INFO_GDB) ? |
812 | "enabled": "disabled", |
813 | (ready->flags & SOF_IPC_INFO_LOCKS) ? |
814 | "enabled": "disabled", |
815 | (ready->flags & SOF_IPC_INFO_LOCKSV) ? |
816 | "enabled": "disabled"); |
817 | } |
818 | |
819 | /* copy the fw_version into debugfs at first boot */ |
820 | memcpy(&sdev->fw_version, v, sizeof(*v)); |
821 | |
822 | return 0; |
823 | } |
824 | |
825 | static int ipc3_fw_ready(struct snd_sof_dev *sdev, u32 cmd) |
826 | { |
827 | struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; |
828 | int offset; |
829 | int ret; |
830 | |
831 | /* mailbox must be on 4k boundary */ |
832 | offset = snd_sof_dsp_get_mailbox_offset(sdev); |
833 | if (offset < 0) { |
834 | dev_err(sdev->dev, "%s: no mailbox offset\n", __func__); |
835 | return offset; |
836 | } |
837 | |
838 | dev_dbg(sdev->dev, "DSP is ready 0x%8.8x offset 0x%x\n", cmd, offset); |
839 | |
840 | /* no need to re-check version/ABI for subsequent boots */ |
841 | if (!sdev->first_boot) |
842 | return 0; |
843 | |
844 | /* |
845 | * copy data from the DSP FW ready offset |
846 | * Subsequent error handling is not needed for BLK_TYPE_SRAM |
847 | */ |
848 | ret = snd_sof_dsp_block_read(sdev, blk_type: SOF_FW_BLK_TYPE_SRAM, offset, dest: fw_ready, |
849 | bytes: sizeof(*fw_ready)); |
850 | if (ret) { |
851 | dev_err(sdev->dev, |
852 | "Unable to read fw_ready, read from TYPE_SRAM failed\n"); |
853 | return ret; |
854 | } |
855 | |
856 | /* make sure ABI version is compatible */ |
857 | ret = sof_ipc3_validate_fw_version(sdev); |
858 | if (ret < 0) |
859 | return ret; |
860 | |
861 | /* now check for extended data */ |
862 | ipc3_fw_parse_ext_data(sdev, offset: offset + sizeof(struct sof_ipc_fw_ready)); |
863 | |
864 | ipc3_get_windows(sdev); |
865 | |
866 | return ipc3_init_reply_data_buffer(sdev); |
867 | } |
868 | |
869 | /* IPC stream position. */ |
870 | static void ipc3_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) |
871 | { |
872 | struct snd_soc_component *scomp = sdev->component; |
873 | struct snd_sof_pcm_stream *stream; |
874 | struct sof_ipc_stream_posn posn; |
875 | struct snd_sof_pcm *spcm; |
876 | int direction, ret; |
877 | |
878 | spcm = snd_sof_find_spcm_comp(scomp, comp_id: msg_id, direction: &direction); |
879 | if (!spcm) { |
880 | dev_err(sdev->dev, "period elapsed for unknown stream, msg_id %d\n", |
881 | msg_id); |
882 | return; |
883 | } |
884 | |
885 | stream = &spcm->stream[direction]; |
886 | ret = snd_sof_ipc_msg_data(sdev, sps: stream, p: &posn, sz: sizeof(posn)); |
887 | if (ret < 0) { |
888 | dev_warn(sdev->dev, "failed to read stream position: %d\n", ret); |
889 | return; |
890 | } |
891 | |
892 | trace_sof_ipc3_period_elapsed_position(sdev, posn: &posn); |
893 | |
894 | memcpy(&stream->posn, &posn, sizeof(posn)); |
895 | |
896 | if (spcm->pcm.compress) |
897 | snd_sof_compr_fragment_elapsed(cstream: stream->cstream); |
898 | else if (stream->substream->runtime && |
899 | !stream->substream->runtime->no_period_wakeup) |
900 | /* only inform ALSA for period_wakeup mode */ |
901 | snd_sof_pcm_period_elapsed(substream: stream->substream); |
902 | } |
903 | |
904 | /* DSP notifies host of an XRUN within FW */ |
905 | static void ipc3_xrun(struct snd_sof_dev *sdev, u32 msg_id) |
906 | { |
907 | struct snd_soc_component *scomp = sdev->component; |
908 | struct snd_sof_pcm_stream *stream; |
909 | struct sof_ipc_stream_posn posn; |
910 | struct snd_sof_pcm *spcm; |
911 | int direction, ret; |
912 | |
913 | spcm = snd_sof_find_spcm_comp(scomp, comp_id: msg_id, direction: &direction); |
914 | if (!spcm) { |
915 | dev_err(sdev->dev, "XRUN for unknown stream, msg_id %d\n", |
916 | msg_id); |
917 | return; |
918 | } |
919 | |
920 | stream = &spcm->stream[direction]; |
921 | ret = snd_sof_ipc_msg_data(sdev, sps: stream, p: &posn, sz: sizeof(posn)); |
922 | if (ret < 0) { |
923 | dev_warn(sdev->dev, "failed to read overrun position: %d\n", ret); |
924 | return; |
925 | } |
926 | |
927 | dev_dbg(sdev->dev, "posn XRUN: host %llx comp %d size %d\n", |
928 | posn.host_posn, posn.xrun_comp_id, posn.xrun_size); |
929 | |
930 | #if defined(CONFIG_SND_SOC_SOF_DEBUG_XRUN_STOP) |
931 | /* stop PCM on XRUN - used for pipeline debug */ |
932 | memcpy(&stream->posn, &posn, sizeof(posn)); |
933 | snd_pcm_stop_xrun(substream: stream->substream); |
934 | #endif |
935 | } |
936 | |
937 | /* stream notifications from firmware */ |
938 | static void ipc3_stream_message(struct snd_sof_dev *sdev, void *msg_buf) |
939 | { |
940 | struct sof_ipc_cmd_hdr *hdr = msg_buf; |
941 | u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; |
942 | u32 msg_id = SOF_IPC_MESSAGE_ID(hdr->cmd); |
943 | |
944 | switch (msg_type) { |
945 | case SOF_IPC_STREAM_POSITION: |
946 | ipc3_period_elapsed(sdev, msg_id); |
947 | break; |
948 | case SOF_IPC_STREAM_TRIG_XRUN: |
949 | ipc3_xrun(sdev, msg_id); |
950 | break; |
951 | default: |
952 | dev_err(sdev->dev, "unhandled stream message %#x\n", |
953 | msg_id); |
954 | break; |
955 | } |
956 | } |
957 | |
958 | /* component notifications from firmware */ |
959 | static void ipc3_comp_notification(struct snd_sof_dev *sdev, void *msg_buf) |
960 | { |
961 | const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; |
962 | struct sof_ipc_cmd_hdr *hdr = msg_buf; |
963 | u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; |
964 | |
965 | switch (msg_type) { |
966 | case SOF_IPC_COMP_GET_VALUE: |
967 | case SOF_IPC_COMP_GET_DATA: |
968 | break; |
969 | default: |
970 | dev_err(sdev->dev, "unhandled component message %#x\n", msg_type); |
971 | return; |
972 | } |
973 | |
974 | if (tplg_ops->control->update) |
975 | tplg_ops->control->update(sdev, msg_buf); |
976 | } |
977 | |
978 | static void ipc3_trace_message(struct snd_sof_dev *sdev, void *msg_buf) |
979 | { |
980 | struct sof_ipc_cmd_hdr *hdr = msg_buf; |
981 | u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; |
982 | |
983 | switch (msg_type) { |
984 | case SOF_IPC_TRACE_DMA_POSITION: |
985 | ipc3_dtrace_posn_update(sdev, posn: msg_buf); |
986 | break; |
987 | default: |
988 | dev_err(sdev->dev, "unhandled trace message %#x\n", msg_type); |
989 | break; |
990 | } |
991 | } |
992 | |
993 | void sof_ipc3_do_rx_work(struct snd_sof_dev *sdev, struct sof_ipc_cmd_hdr *hdr, void *msg_buf) |
994 | { |
995 | ipc3_rx_callback rx_callback = NULL; |
996 | u32 cmd; |
997 | int err; |
998 | |
999 | ipc3_log_header(dev: sdev->dev, text: "ipc rx", cmd: hdr->cmd); |
1000 | |
1001 | if (hdr->size < sizeof(*hdr) || hdr->size > SOF_IPC_MSG_MAX_SIZE) { |
1002 | dev_err(sdev->dev, "The received message size is invalid: %u\n", |
1003 | hdr->size); |
1004 | return; |
1005 | } |
1006 | |
1007 | cmd = hdr->cmd & SOF_GLB_TYPE_MASK; |
1008 | |
1009 | /* check message type */ |
1010 | switch (cmd) { |
1011 | case SOF_IPC_GLB_REPLY: |
1012 | dev_err(sdev->dev, "ipc reply unknown\n"); |
1013 | break; |
1014 | case SOF_IPC_FW_READY: |
1015 | /* check for FW boot completion */ |
1016 | if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) { |
1017 | err = ipc3_fw_ready(sdev, cmd); |
1018 | if (err < 0) |
1019 | sof_set_fw_state(sdev, new_state: SOF_FW_BOOT_READY_FAILED); |
1020 | else |
1021 | sof_set_fw_state(sdev, new_state: SOF_FW_BOOT_READY_OK); |
1022 | |
1023 | /* wake up firmware loader */ |
1024 | wake_up(&sdev->boot_wait); |
1025 | } |
1026 | break; |
1027 | case SOF_IPC_GLB_COMPOUND: |
1028 | case SOF_IPC_GLB_TPLG_MSG: |
1029 | case SOF_IPC_GLB_PM_MSG: |
1030 | break; |
1031 | case SOF_IPC_GLB_COMP_MSG: |
1032 | rx_callback = ipc3_comp_notification; |
1033 | break; |
1034 | case SOF_IPC_GLB_STREAM_MSG: |
1035 | rx_callback = ipc3_stream_message; |
1036 | break; |
1037 | case SOF_IPC_GLB_TRACE_MSG: |
1038 | rx_callback = ipc3_trace_message; |
1039 | break; |
1040 | default: |
1041 | dev_err(sdev->dev, "%s: Unknown DSP message: 0x%x\n", __func__, cmd); |
1042 | break; |
1043 | } |
1044 | |
1045 | /* Call local handler for the message */ |
1046 | if (rx_callback) |
1047 | rx_callback(sdev, msg_buf); |
1048 | |
1049 | /* Notify registered clients */ |
1050 | sof_client_ipc_rx_dispatcher(sdev, msg_buf); |
1051 | |
1052 | ipc3_log_header(dev: sdev->dev, text: "ipc rx done", cmd: hdr->cmd); |
1053 | } |
1054 | EXPORT_SYMBOL(sof_ipc3_do_rx_work); |
1055 | |
1056 | /* DSP firmware has sent host a message */ |
1057 | static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev) |
1058 | { |
1059 | struct sof_ipc_cmd_hdr hdr; |
1060 | void *msg_buf; |
1061 | int err; |
1062 | |
1063 | /* read back header */ |
1064 | err = snd_sof_ipc_msg_data(sdev, NULL, p: &hdr, sz: sizeof(hdr)); |
1065 | if (err < 0) { |
1066 | dev_warn(sdev->dev, "failed to read IPC header: %d\n", err); |
1067 | return; |
1068 | } |
1069 | |
1070 | if (hdr.size < sizeof(hdr) || hdr.size > SOF_IPC_MSG_MAX_SIZE) { |
1071 | dev_err(sdev->dev, "The received message size is invalid\n"); |
1072 | return; |
1073 | } |
1074 | |
1075 | /* read the full message */ |
1076 | msg_buf = kmalloc(size: hdr.size, GFP_KERNEL); |
1077 | if (!msg_buf) |
1078 | return; |
1079 | |
1080 | err = snd_sof_ipc_msg_data(sdev, NULL, p: msg_buf, sz: hdr.size); |
1081 | if (err < 0) { |
1082 | dev_err(sdev->dev, "%s: Failed to read message: %d\n", __func__, err); |
1083 | kfree(objp: msg_buf); |
1084 | return; |
1085 | } |
1086 | |
1087 | sof_ipc3_do_rx_work(sdev, &hdr, msg_buf); |
1088 | |
1089 | kfree(objp: msg_buf); |
1090 | } |
1091 | |
1092 | static int sof_ipc3_set_core_state(struct snd_sof_dev *sdev, int core_idx, bool on) |
1093 | { |
1094 | struct sof_ipc_pm_core_config core_cfg = { |
1095 | .hdr.size = sizeof(core_cfg), |
1096 | .hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE, |
1097 | }; |
1098 | |
1099 | if (on) |
1100 | core_cfg.enable_mask = sdev->enabled_cores_mask | BIT(core_idx); |
1101 | else |
1102 | core_cfg.enable_mask = sdev->enabled_cores_mask & ~BIT(core_idx); |
1103 | |
1104 | return sof_ipc3_tx_msg(sdev, msg_data: &core_cfg, msg_bytes: sizeof(core_cfg), NULL, reply_bytes: 0, no_pm: false); |
1105 | } |
1106 | |
1107 | static int sof_ipc3_ctx_ipc(struct snd_sof_dev *sdev, int cmd) |
1108 | { |
1109 | struct sof_ipc_pm_ctx pm_ctx = { |
1110 | .hdr.size = sizeof(pm_ctx), |
1111 | .hdr.cmd = SOF_IPC_GLB_PM_MSG | cmd, |
1112 | }; |
1113 | |
1114 | /* send ctx save ipc to dsp */ |
1115 | return sof_ipc3_tx_msg(sdev, msg_data: &pm_ctx, msg_bytes: sizeof(pm_ctx), NULL, reply_bytes: 0, no_pm: false); |
1116 | } |
1117 | |
1118 | static int sof_ipc3_ctx_save(struct snd_sof_dev *sdev) |
1119 | { |
1120 | return sof_ipc3_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE); |
1121 | } |
1122 | |
1123 | static int sof_ipc3_ctx_restore(struct snd_sof_dev *sdev) |
1124 | { |
1125 | return sof_ipc3_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE); |
1126 | } |
1127 | |
1128 | static int sof_ipc3_set_pm_gate(struct snd_sof_dev *sdev, u32 flags) |
1129 | { |
1130 | struct sof_ipc_pm_gate pm_gate; |
1131 | |
1132 | memset(&pm_gate, 0, sizeof(pm_gate)); |
1133 | |
1134 | /* configure pm_gate ipc message */ |
1135 | pm_gate.hdr.size = sizeof(pm_gate); |
1136 | pm_gate.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE; |
1137 | pm_gate.flags = flags; |
1138 | |
1139 | /* send pm_gate ipc to dsp */ |
1140 | return sof_ipc_tx_message_no_pm_no_reply(ipc: sdev->ipc, msg_data: &pm_gate, msg_bytes: sizeof(pm_gate)); |
1141 | } |
1142 | |
1143 | static const struct sof_ipc_pm_ops ipc3_pm_ops = { |
1144 | .ctx_save = sof_ipc3_ctx_save, |
1145 | .ctx_restore = sof_ipc3_ctx_restore, |
1146 | .set_core_state = sof_ipc3_set_core_state, |
1147 | .set_pm_gate = sof_ipc3_set_pm_gate, |
1148 | }; |
1149 | |
1150 | const struct sof_ipc_ops ipc3_ops = { |
1151 | .tplg = &ipc3_tplg_ops, |
1152 | .pm = &ipc3_pm_ops, |
1153 | .pcm = &ipc3_pcm_ops, |
1154 | .fw_loader = &ipc3_loader_ops, |
1155 | .fw_tracing = &ipc3_dtrace_ops, |
1156 | |
1157 | .tx_msg = sof_ipc3_tx_msg, |
1158 | .rx_msg = sof_ipc3_rx_msg, |
1159 | .set_get_data = sof_ipc3_set_get_data, |
1160 | .get_reply = sof_ipc3_get_reply, |
1161 | }; |
1162 |
Definitions
- ipc3_log_header
- sof_ipc3_dump_payload
- sof_ipc3_get_reply
- ipc3_wait_tx_done
- ipc3_tx_msg_unlocked
- sof_ipc3_tx_msg
- sof_ipc3_set_get_data
- sof_ipc3_get_ext_windows
- sof_ipc3_get_cc_info
- ipc3_fw_parse_ext_data
- ipc3_get_windows
- ipc3_init_reply_data_buffer
- sof_ipc3_validate_fw_version
- ipc3_fw_ready
- ipc3_period_elapsed
- ipc3_xrun
- ipc3_stream_message
- ipc3_comp_notification
- ipc3_trace_message
- sof_ipc3_do_rx_work
- sof_ipc3_rx_msg
- sof_ipc3_set_core_state
- sof_ipc3_ctx_ipc
- sof_ipc3_ctx_save
- sof_ipc3_ctx_restore
- sof_ipc3_set_pm_gate
- ipc3_pm_ops
Improve your Profiling and Debugging skills
Find out more