File Index
Files and directories
Sign in
FILE

ipc3.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/sof/ipc3.c 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 or
   4// redistributing this file, you may do so under either license.
   5//
   6// Copyright(c) 2021 Intel Corporation
   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
  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		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
 219static 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
 226static void sof_ipc3_dump_payload(struct snd_sof_dev *sdev,
 227				  void *ipc_data, size_t size)
 228{
 229	dev_dbg(sdev->dev, "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		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, 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			 str_enabled_disabled(ready->flags & SOF_IPC_INFO_GDB),
 812			 str_enabled_disabled(ready->flags & SOF_IPC_INFO_LOCKS),
 813			 str_enabled_disabled(ready->flags & SOF_IPC_INFO_LOCKSV));
 814
 815	/* copy the fw_version into debugfs at first boot */
 816	memcpy(&sdev->fw_version, v, sizeof(*v));
 817
 818	return 0;
 819}
 820
 821static int ipc3_fw_ready(struct snd_sof_dev *sdev, u32 cmd)
 822{
 823	struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready;
 824	int offset;
 825	int ret;
 826
 827	/* mailbox must be on 4k boundary */
 828	offset = snd_sof_dsp_get_mailbox_offset(sdev);
 829	if (offset < 0) {
 830		dev_err(sdev->dev, "%s: no mailbox offset\n", __func__);
 831		return offset;
 832	}
 833
 834	dev_dbg(sdev->dev, "DSP is ready 0x%8.8x offset 0x%x\n", cmd, offset);
 835
 836	/* no need to re-check version/ABI for subsequent boots */
 837	if (!sdev->first_boot)
 838		return 0;
 839
 840	/*
 841	 * copy data from the DSP FW ready offset
 842	 * Subsequent error handling is not needed for BLK_TYPE_SRAM
 843	 */
 844	ret = snd_sof_dsp_block_read(sdev, SOF_FW_BLK_TYPE_SRAM, offset, fw_ready,
 845				     sizeof(*fw_ready));
 846	if (ret) {
 847		dev_err(sdev->dev,
 848			"Unable to read fw_ready, read from TYPE_SRAM failed\n");
 849		return ret;
 850	}
 851
 852	/* make sure ABI version is compatible */
 853	ret = sof_ipc3_validate_fw_version(sdev);
 854	if (ret < 0)
 855		return ret;
 856
 857	/* now check for extended data */
 858	ipc3_fw_parse_ext_data(sdev, offset + sizeof(struct sof_ipc_fw_ready));
 859
 860	ipc3_get_windows(sdev);
 861
 862	return ipc3_init_reply_data_buffer(sdev);
 863}
 864
 865/* IPC stream position. */
 866static void ipc3_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id)
 867{
 868	struct snd_soc_component *scomp = sdev->component;
 869	struct snd_sof_pcm_stream *stream;
 870	struct sof_ipc_stream_posn posn;
 871	struct snd_sof_pcm *spcm;
 872	int direction, ret;
 873
 874	spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction);
 875	if (!spcm) {
 876		dev_err(sdev->dev, "period elapsed for unknown stream, msg_id %d\n",
 877			msg_id);
 878		return;
 879	}
 880
 881	stream = &spcm->stream[direction];
 882	ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn));
 883	if (ret < 0) {
 884		dev_warn(sdev->dev, "failed to read stream position: %d\n", ret);
 885		return;
 886	}
 887
 888	trace_sof_ipc3_period_elapsed_position(sdev, &posn);
 889
 890	memcpy(&stream->posn, &posn, sizeof(posn));
 891
 892	if (spcm->pcm.compress)
 893		snd_sof_compr_fragment_elapsed(stream->cstream);
 894	else if (stream->substream->runtime &&
 895		 !stream->substream->runtime->no_period_wakeup)
 896		/* only inform ALSA for period_wakeup mode */
 897		snd_sof_pcm_period_elapsed(stream->substream);
 898}
 899
 900/* DSP notifies host of an XRUN within FW */
 901static void ipc3_xrun(struct snd_sof_dev *sdev, u32 msg_id)
 902{
 903	struct snd_soc_component *scomp = sdev->component;
 904	struct snd_sof_pcm_stream *stream;
 905	struct sof_ipc_stream_posn posn;
 906	struct snd_sof_pcm *spcm;
 907	int direction, ret;
 908
 909	spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction);
 910	if (!spcm) {
 911		dev_err(sdev->dev, "XRUN for unknown stream, msg_id %d\n",
 912			msg_id);
 913		return;
 914	}
 915
 916	stream = &spcm->stream[direction];
 917	ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn));
 918	if (ret < 0) {
 919		dev_warn(sdev->dev, "failed to read overrun position: %d\n", ret);
 920		return;
 921	}
 922
 923	dev_dbg(sdev->dev,  "posn XRUN: host %llx comp %d size %d\n",
 924		posn.host_posn, posn.xrun_comp_id, posn.xrun_size);
 925
 926#if defined(CONFIG_SND_SOC_SOF_DEBUG_XRUN_STOP)
 927	/* stop PCM on XRUN - used for pipeline debug */
 928	memcpy(&stream->posn, &posn, sizeof(posn));
 929	snd_pcm_stop_xrun(stream->substream);
 930#endif
 931}
 932
 933/* stream notifications from firmware */
 934static void ipc3_stream_message(struct snd_sof_dev *sdev, void *msg_buf)
 935{
 936	struct sof_ipc_cmd_hdr *hdr = msg_buf;
 937	u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
 938	u32 msg_id = SOF_IPC_MESSAGE_ID(hdr->cmd);
 939
 940	switch (msg_type) {
 941	case SOF_IPC_STREAM_POSITION:
 942		ipc3_period_elapsed(sdev, msg_id);
 943		break;
 944	case SOF_IPC_STREAM_TRIG_XRUN:
 945		ipc3_xrun(sdev, msg_id);
 946		break;
 947	default:
 948		dev_err(sdev->dev, "unhandled stream message %#x\n",
 949			msg_id);
 950		break;
 951	}
 952}
 953
 954/* component notifications from firmware */
 955static void ipc3_comp_notification(struct snd_sof_dev *sdev, void *msg_buf)
 956{
 957	const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
 958	struct sof_ipc_cmd_hdr *hdr = msg_buf;
 959	u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
 960
 961	switch (msg_type) {
 962	case SOF_IPC_COMP_GET_VALUE:
 963	case SOF_IPC_COMP_GET_DATA:
 964		break;
 965	default:
 966		dev_err(sdev->dev, "unhandled component message %#x\n", msg_type);
 967		return;
 968	}
 969
 970	if (tplg_ops->control->update)
 971		tplg_ops->control->update(sdev, msg_buf);
 972}
 973
 974static void ipc3_trace_message(struct snd_sof_dev *sdev, void *msg_buf)
 975{
 976	struct sof_ipc_cmd_hdr *hdr = msg_buf;
 977	u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
 978
 979	switch (msg_type) {
 980	case SOF_IPC_TRACE_DMA_POSITION:
 981		ipc3_dtrace_posn_update(sdev, msg_buf);
 982		break;
 983	default:
 984		dev_err(sdev->dev, "unhandled trace message %#x\n", msg_type);
 985		break;
 986	}
 987}
 988
 989void sof_ipc3_do_rx_work(struct snd_sof_dev *sdev, struct sof_ipc_cmd_hdr *hdr, void *msg_buf)
 990{
 991	ipc3_rx_callback rx_callback = NULL;
 992	u32 cmd;
 993	int err;
 994
 995	ipc3_log_header(sdev->dev, "ipc rx", hdr->cmd);
 996
 997	if (hdr->size < sizeof(*hdr) || hdr->size > SOF_IPC_MSG_MAX_SIZE) {
 998		dev_err(sdev->dev, "The received message size is invalid: %u\n",
 999			hdr->size);
1000		return;
1001	}
1002
1003	cmd = hdr->cmd & SOF_GLB_TYPE_MASK;
1004
1005	/* check message type */
1006	switch (cmd) {
1007	case SOF_IPC_GLB_REPLY:
1008		dev_err(sdev->dev, "ipc reply unknown\n");
1009		break;
1010	case SOF_IPC_FW_READY:
1011		/* check for FW boot completion */
1012		if (sdev->fw_state == SOF_FW_BOOT_IN_PROGRESS) {
1013			err = ipc3_fw_ready(sdev, cmd);
1014			if (err < 0)
1015				sof_set_fw_state(sdev, SOF_FW_BOOT_READY_FAILED);
1016			else
1017				sof_set_fw_state(sdev, SOF_FW_BOOT_READY_OK);
1018
1019			/* wake up firmware loader */
1020			wake_up(&sdev->boot_wait);
1021		}
1022		break;
1023	case SOF_IPC_GLB_COMPOUND:
1024	case SOF_IPC_GLB_TPLG_MSG:
1025	case SOF_IPC_GLB_PM_MSG:
1026		break;
1027	case SOF_IPC_GLB_COMP_MSG:
1028		rx_callback = ipc3_comp_notification;
1029		break;
1030	case SOF_IPC_GLB_STREAM_MSG:
1031		rx_callback = ipc3_stream_message;
1032		break;
1033	case SOF_IPC_GLB_TRACE_MSG:
1034		rx_callback = ipc3_trace_message;
1035		break;
1036	default:
1037		dev_err(sdev->dev, "%s: Unknown DSP message: 0x%x\n", __func__, cmd);
1038		break;
1039	}
1040
1041	/* Call local handler for the message */
1042	if (rx_callback)
1043		rx_callback(sdev, msg_buf);
1044
1045	/* Notify registered clients */
1046	sof_client_ipc_rx_dispatcher(sdev, msg_buf);
1047
1048	ipc3_log_header(sdev->dev, "ipc rx done", hdr->cmd);
1049}
1050EXPORT_SYMBOL(sof_ipc3_do_rx_work);
1051
1052/* DSP firmware has sent host a message  */
1053static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev)
1054{
1055	struct sof_ipc_cmd_hdr hdr;
1056	void *msg_buf;
1057	int err;
1058
1059	/* read back header */
1060	err = snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr));
1061	if (err < 0) {
1062		dev_warn(sdev->dev, "failed to read IPC header: %d\n", err);
1063		return;
1064	}
1065
1066	if (hdr.size < sizeof(hdr) || hdr.size > SOF_IPC_MSG_MAX_SIZE) {
1067		dev_err(sdev->dev, "The received message size is invalid\n");
1068		return;
1069	}
1070
1071	/* read the full message */
1072	msg_buf = kmalloc(hdr.size, GFP_KERNEL);
1073	if (!msg_buf)
1074		return;
1075
1076	err = snd_sof_ipc_msg_data(sdev, NULL, msg_buf, hdr.size);
1077	if (err < 0) {
1078		dev_err(sdev->dev, "%s: Failed to read message: %d\n", __func__, err);
1079		kfree(msg_buf);
1080		return;
1081	}
1082
1083	sof_ipc3_do_rx_work(sdev, &hdr, msg_buf);
1084
1085	kfree(msg_buf);
1086}
1087
1088static int sof_ipc3_set_core_state(struct snd_sof_dev *sdev, int core_idx, bool on)
1089{
1090	struct sof_ipc_pm_core_config core_cfg = {
1091		.hdr.size = sizeof(core_cfg),
1092		.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE,
1093	};
1094
1095	if (on)
1096		core_cfg.enable_mask = sdev->enabled_cores_mask | BIT(core_idx);
1097	else
1098		core_cfg.enable_mask = sdev->enabled_cores_mask & ~BIT(core_idx);
1099
1100	return sof_ipc3_tx_msg(sdev, &core_cfg, sizeof(core_cfg), NULL, 0, false);
1101}
1102
1103static int sof_ipc3_ctx_ipc(struct snd_sof_dev *sdev, int cmd)
1104{
1105	struct sof_ipc_pm_ctx pm_ctx = {
1106		.hdr.size = sizeof(pm_ctx),
1107		.hdr.cmd = SOF_IPC_GLB_PM_MSG | cmd,
1108	};
1109
1110	/* send ctx save ipc to dsp */
1111	return sof_ipc3_tx_msg(sdev, &pm_ctx, sizeof(pm_ctx), NULL, 0, false);
1112}
1113
1114static int sof_ipc3_ctx_save(struct snd_sof_dev *sdev)
1115{
1116	return sof_ipc3_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE);
1117}
1118
1119static int sof_ipc3_ctx_restore(struct snd_sof_dev *sdev)
1120{
1121	return sof_ipc3_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE);
1122}
1123
1124static int sof_ipc3_set_pm_gate(struct snd_sof_dev *sdev, u32 flags)
1125{
1126	struct sof_ipc_pm_gate pm_gate;
1127
1128	memset(&pm_gate, 0, sizeof(pm_gate));
1129
1130	/* configure pm_gate ipc message */
1131	pm_gate.hdr.size = sizeof(pm_gate);
1132	pm_gate.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE;
1133	pm_gate.flags = flags;
1134
1135	/* send pm_gate ipc to dsp */
1136	return sof_ipc_tx_message_no_pm_no_reply(sdev->ipc, &pm_gate, sizeof(pm_gate));
1137}
1138
1139static const struct sof_ipc_pm_ops ipc3_pm_ops = {
1140	.ctx_save = sof_ipc3_ctx_save,
1141	.ctx_restore = sof_ipc3_ctx_restore,
1142	.set_core_state = sof_ipc3_set_core_state,
1143	.set_pm_gate = sof_ipc3_set_pm_gate,
1144};
1145
1146const struct sof_ipc_ops ipc3_ops = {
1147	.tplg = &ipc3_tplg_ops,
1148	.pm = &ipc3_pm_ops,
1149	.pcm = &ipc3_pcm_ops,
1150	.fw_loader = &ipc3_loader_ops,
1151	.fw_tracing = &ipc3_dtrace_ops,
1152
1153	.tx_msg = sof_ipc3_tx_msg,
1154	.rx_msg = sof_ipc3_rx_msg,
1155	.set_get_data = sof_ipc3_set_get_data,
1156	.get_reply = sof_ipc3_get_reply,
1157};
~/ Read-only file preview.