1162 lines · c
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 or4// redistributing this file, you may do so under either license.5//6// Copyright(c) 2021 Intel Corporation7//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 18typedef void (*ipc3_rx_callback)(struct snd_sof_dev *sdev, void *msg_buf);19 20#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC)21static 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 else213 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#else219static 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#endif225 226static 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 234static 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, sdev->host_box.offset, reply, 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 else254 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_size278 * 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, sdev->host_box.offset,282 msg->reply_data, msg->reply_size);283 }284 285 return ret;286}287 288/* wait for IPC message reply */289static 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(ipc->sdev, "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(sdev->dev, "ipc tx succeeded", 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 */332static 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(sdev->dev, "ipc tx", 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 355static 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);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, payload, payload_bytes);405 }406 }407 408 mutex_unlock(&ipc->tx_mutex);409 410 return ret;411}412 413static 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, cdata, cdata->rhdr.hdr.size,438 cdata, cdata->rhdr.hdr.size, false);439 440 cdata_chunk = kzalloc(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(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(sdev->ipc,495 cdata_chunk, cdata_chunk->rhdr.hdr.size,496 cdata_chunk, 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, payload, data_bytes - header_bytes);512 }513 514 mutex_unlock(&sdev->ipc->tx_mutex);515 516 kfree(cdata_chunk);517 518 return ret;519}520 521int 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(sdev->info_window, w, 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(sdev->dev, w, ext_hdr->hdr.size, GFP_KERNEL);540 if (!sdev->info_window)541 return -ENOMEM;542 543 return 0;544}545 546int 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(sdev->cc_version, cc, 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(sdev->dev, cc, cc->ext_hdr.hdr.size, GFP_KERNEL);571 if (!sdev->cc_version)572 return -ENOMEM;573 574 ret = snd_sof_debugfs_buf_item(sdev, sdev->cc_version,575 cc->ext_hdr.hdr.size,576 "cc_version", 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 */589static 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, SOF_FW_BLK_TYPE_SRAM, offset, ext_data,601 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, SOF_FW_BLK_TYPE_SRAM,607 offset + sizeof(*ext_hdr),608 (void *)((u8 *)ext_data + sizeof(*ext_hdr)),609 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, SOF_FW_BLK_TYPE_SRAM, offset, ext_data,643 sizeof(*ext_hdr));644 ext_hdr = ext_data;645 }646 647 kfree(ext_data);648 return ret;649}650 651static 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, 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, SOF_FW_BLK_TYPE_SRAM,684 inbox_offset,685 elem->size, "inbox",686 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, SOF_FW_BLK_TYPE_SRAM,692 outbox_offset,693 elem->size, "outbox",694 SOF_DEBUGFS_ACCESS_D0_ONLY);695 break;696 case SOF_IPC_REGION_TRACE:697 snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,698 window_offset + elem->offset,699 elem->size, "etrace",700 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, SOF_FW_BLK_TYPE_SRAM,706 window_offset + elem->offset,707 elem->size, "debug",708 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, SOF_FW_BLK_TYPE_SRAM,714 stream_offset,715 elem->size, "stream",716 SOF_DEBUGFS_ACCESS_D0_ONLY);717 break;718 case SOF_IPC_REGION_REGS:719 snd_sof_debugfs_add_region_item(sdev, SOF_FW_BLK_TYPE_SRAM,720 window_offset + elem->offset,721 elem->size, "regs",722 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, SOF_FW_BLK_TYPE_SRAM,727 window_offset + elem->offset,728 elem->size, "exception",729 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 765static 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(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 778int 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 825static 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 offset846 * Subsequent error handling is not needed for BLK_TYPE_SRAM847 */848 ret = snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, fw_ready,849 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 + 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. */870static 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, msg_id, &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, stream, &posn, 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);893 894 memcpy(&stream->posn, &posn, sizeof(posn));895 896 if (spcm->pcm.compress)897 snd_sof_compr_fragment_elapsed(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(stream->substream);902}903 904/* DSP notifies host of an XRUN within FW */905static 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, msg_id, &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, stream, &posn, 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(stream->substream);934#endif935}936 937/* stream notifications from firmware */938static 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 */959static 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 978static 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, msg_buf);986 break;987 default:988 dev_err(sdev->dev, "unhandled trace message %#x\n", msg_type);989 break;990 }991}992 993void 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(sdev->dev, "ipc rx", 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, SOF_FW_BOOT_READY_FAILED);1020 else1021 sof_set_fw_state(sdev, 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(sdev->dev, "ipc rx done", hdr->cmd);1053}1054EXPORT_SYMBOL(sof_ipc3_do_rx_work);1055 1056/* DSP firmware has sent host a message */1057static 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, &hdr, 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(hdr.size, GFP_KERNEL);1077 if (!msg_buf)1078 return;1079 1080 err = snd_sof_ipc_msg_data(sdev, NULL, msg_buf, hdr.size);1081 if (err < 0) {1082 dev_err(sdev->dev, "%s: Failed to read message: %d\n", __func__, err);1083 kfree(msg_buf);1084 return;1085 }1086 1087 sof_ipc3_do_rx_work(sdev, &hdr, msg_buf);1088 1089 kfree(msg_buf);1090}1091 1092static 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 else1102 core_cfg.enable_mask = sdev->enabled_cores_mask & ~BIT(core_idx);1103 1104 return sof_ipc3_tx_msg(sdev, &core_cfg, sizeof(core_cfg), NULL, 0, false);1105}1106 1107static 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, &pm_ctx, sizeof(pm_ctx), NULL, 0, false);1116}1117 1118static 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 1123static 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 1128static 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(sdev->ipc, &pm_gate, sizeof(pm_gate));1141}1142 1143static 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 1150const 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