1040 lines · 30.7 KB · C
RawDownload
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)//// This file is provided under a dual BSD/GPLv2 license.  When using or// redistributing this file, you may do so under either license.//// Copyright(c) 2022 Intel Corporation//// Authors: Rander Wang <rander.wang@linux.intel.com>//	    Peter Ujfalusi <peter.ujfalusi@linux.intel.com>//#include <linux/firmware.h>#include <sound/sof/header.h>#include <sound/sof/ipc4/header.h>#include "sof-priv.h"#include "sof-audio.h"#include "ipc4-fw-reg.h"#include "ipc4-priv.h"#include "ipc4-topology.h"#include "ipc4-telemetry.h"#include "ops.h" static const struct sof_ipc4_fw_status {	int status;	char *msg;} ipc4_status[] = {	{0, "The operation was successful"},	{1, "Invalid parameter specified"},	{2, "Unknown message type specified"},	{3, "Not enough space in the IPC reply buffer to complete the request"},	{4, "The system or resource is busy"},	{5, "Replaced ADSP IPC PENDING (unused)"},	{6, "Unknown error while processing the request"},	{7, "Unsupported operation requested"},	{8, "Reserved (ADSP_STAGE_UNINITIALIZED removed)"},	{9, "Specified resource not found"},	{10, "A resource's ID requested to be created is already assigned"},	{11, "Reserved (ADSP_IPC_OUT_OF_MIPS removed)"},	{12, "Required resource is in invalid state"},	{13, "Requested power transition failed to complete"},	{14, "Manifest of the library being loaded is invalid"},	{15, "Requested service or data is unavailable on the target platform"},	{42, "Library target address is out of storage memory range"},	{43, "Reserved"},	{44, "Image verification by CSE failed"},	{100, "General module management error"},	{101, "Module loading failed"},	{102, "Integrity check of the loaded module content failed"},	{103, "Attempt to unload code of the module in use"},	{104, "Other failure of module instance initialization request"},	{105, "Reserved (ADSP_IPC_OUT_OF_MIPS removed)"},	{106, "Reserved (ADSP_IPC_CONFIG_GET_ERROR removed)"},	{107, "Reserved (ADSP_IPC_CONFIG_SET_ERROR removed)"},	{108, "Reserved (ADSP_IPC_LARGE_CONFIG_GET_ERROR removed)"},	{109, "Reserved (ADSP_IPC_LARGE_CONFIG_SET_ERROR removed)"},	{110, "Invalid (out of range) module ID provided"},	{111, "Invalid module instance ID provided"},	{112, "Invalid queue (pin) ID provided"},	{113, "Invalid destination queue (pin) ID provided"},	{114, "Reserved (ADSP_IPC_BIND_UNBIND_DST_SINK_UNSUPPORTED removed)"},	{115, "Reserved (ADSP_IPC_UNLOAD_INST_EXISTS removed)"},	{116, "Invalid target code ID provided"},	{117, "Injection DMA buffer is too small for probing the input pin"},	{118, "Extraction DMA buffer is too small for probing the output pin"},	{120, "Invalid ID of configuration item provided in TLV list"},	{121, "Invalid length of configuration item provided in TLV list"},	{122, "Invalid structure of configuration item provided"},	{140, "Initialization of DMA Gateway failed"},	{141, "Invalid ID of gateway provided"},	{142, "Setting state of DMA Gateway failed"},	{143, "DMA_CONTROL message targeting gateway not allocated yet"},	{150, "Attempt to configure SCLK while I2S port is running"},	{151, "Attempt to configure MCLK while I2S port is running"},	{152, "Attempt to stop SCLK that is not running"},	{153, "Attempt to stop MCLK that is not running"},	{160, "Reserved (ADSP_IPC_PIPELINE_NOT_INITIALIZED removed)"},	{161, "Reserved (ADSP_IPC_PIPELINE_NOT_EXIST removed)"},	{162, "Reserved (ADSP_IPC_PIPELINE_SAVE_FAILED removed)"},	{163, "Reserved (ADSP_IPC_PIPELINE_RESTORE_FAILED removed)"},	{165, "Reserved (ADSP_IPC_PIPELINE_ALREADY_EXISTS removed)"},}; typedef void (*ipc4_notification_handler)(struct snd_sof_dev *sdev,					  struct sof_ipc4_msg *msg); static int sof_ipc4_check_reply_status(struct snd_sof_dev *sdev, u32 status){	int i, ret; 	status &= SOF_IPC4_REPLY_STATUS; 	if (!status)		return 0; 	for (i = 0; i < ARRAY_SIZE(ipc4_status); i++) {		if (ipc4_status[i].status == status) {			dev_err(sdev->dev, "FW reported error: %u - %s\n",				status, ipc4_status[i].msg);			goto to_errno;		}	} 	if (i == ARRAY_SIZE(ipc4_status))		dev_err(sdev->dev, "FW reported error: %u - Unknown\n", status); to_errno:	switch (status) {	case 2:	case 15:		ret = -EOPNOTSUPP;		break;	case 8:	case 11:	case 105 ... 109:	case 114 ... 115:	case 160 ... 163:	case 165:		ret = -ENOENT;		break;	case 4:	case 150:	case 151:		ret = -EBUSY;		break;	default:		ret = -EINVAL;		break;	} 	return ret;} #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC)#define DBG_IPC4_MSG_TYPE_ENTRY(type)	[SOF_IPC4_##type] = #typestatic const char * const ipc4_dbg_mod_msg_type[] = {	DBG_IPC4_MSG_TYPE_ENTRY(MOD_INIT_INSTANCE),	DBG_IPC4_MSG_TYPE_ENTRY(MOD_CONFIG_GET),	DBG_IPC4_MSG_TYPE_ENTRY(MOD_CONFIG_SET),	DBG_IPC4_MSG_TYPE_ENTRY(MOD_LARGE_CONFIG_GET),	DBG_IPC4_MSG_TYPE_ENTRY(MOD_LARGE_CONFIG_SET),	DBG_IPC4_MSG_TYPE_ENTRY(MOD_BIND),	DBG_IPC4_MSG_TYPE_ENTRY(MOD_UNBIND),	DBG_IPC4_MSG_TYPE_ENTRY(MOD_SET_DX),	DBG_IPC4_MSG_TYPE_ENTRY(MOD_SET_D0IX),	DBG_IPC4_MSG_TYPE_ENTRY(MOD_ENTER_MODULE_RESTORE),	DBG_IPC4_MSG_TYPE_ENTRY(MOD_EXIT_MODULE_RESTORE),	DBG_IPC4_MSG_TYPE_ENTRY(MOD_DELETE_INSTANCE),}; static const char * const ipc4_dbg_glb_msg_type[] = {	DBG_IPC4_MSG_TYPE_ENTRY(GLB_BOOT_CONFIG),	DBG_IPC4_MSG_TYPE_ENTRY(GLB_ROM_CONTROL),	DBG_IPC4_MSG_TYPE_ENTRY(GLB_IPCGATEWAY_CMD),	DBG_IPC4_MSG_TYPE_ENTRY(GLB_PERF_MEASUREMENTS_CMD),	DBG_IPC4_MSG_TYPE_ENTRY(GLB_CHAIN_DMA),	DBG_IPC4_MSG_TYPE_ENTRY(GLB_LOAD_MULTIPLE_MODULES),	DBG_IPC4_MSG_TYPE_ENTRY(GLB_UNLOAD_MULTIPLE_MODULES),	DBG_IPC4_MSG_TYPE_ENTRY(GLB_CREATE_PIPELINE),	DBG_IPC4_MSG_TYPE_ENTRY(GLB_DELETE_PIPELINE),	DBG_IPC4_MSG_TYPE_ENTRY(GLB_SET_PIPELINE_STATE),	DBG_IPC4_MSG_TYPE_ENTRY(GLB_GET_PIPELINE_STATE),	DBG_IPC4_MSG_TYPE_ENTRY(GLB_GET_PIPELINE_CONTEXT_SIZE),	DBG_IPC4_MSG_TYPE_ENTRY(GLB_SAVE_PIPELINE),	DBG_IPC4_MSG_TYPE_ENTRY(GLB_RESTORE_PIPELINE),	DBG_IPC4_MSG_TYPE_ENTRY(GLB_LOAD_LIBRARY),	DBG_IPC4_MSG_TYPE_ENTRY(GLB_LOAD_LIBRARY_PREPARE),	DBG_IPC4_MSG_TYPE_ENTRY(GLB_INTERNAL_MESSAGE),	DBG_IPC4_MSG_TYPE_ENTRY(GLB_NOTIFICATION),}; #define DBG_IPC4_NOTIFICATION_TYPE_ENTRY(type)	[SOF_IPC4_NOTIFY_##type] = #typestatic const char * const ipc4_dbg_notification_type[] = {	DBG_IPC4_NOTIFICATION_TYPE_ENTRY(PHRASE_DETECTED),	DBG_IPC4_NOTIFICATION_TYPE_ENTRY(RESOURCE_EVENT),	DBG_IPC4_NOTIFICATION_TYPE_ENTRY(LOG_BUFFER_STATUS),	DBG_IPC4_NOTIFICATION_TYPE_ENTRY(TIMESTAMP_CAPTURED),	DBG_IPC4_NOTIFICATION_TYPE_ENTRY(FW_READY),	DBG_IPC4_NOTIFICATION_TYPE_ENTRY(FW_AUD_CLASS_RESULT),	DBG_IPC4_NOTIFICATION_TYPE_ENTRY(EXCEPTION_CAUGHT),	DBG_IPC4_NOTIFICATION_TYPE_ENTRY(MODULE_NOTIFICATION),	DBG_IPC4_NOTIFICATION_TYPE_ENTRY(PROBE_DATA_AVAILABLE),	DBG_IPC4_NOTIFICATION_TYPE_ENTRY(ASYNC_MSG_SRVC_MESSAGE),}; static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_msg *msg,				bool data_size_valid){	u32 val, type;	const u8 *str2 = NULL;	const u8 *str = NULL; 	val = msg->primary & SOF_IPC4_MSG_TARGET_MASK;	type = SOF_IPC4_MSG_TYPE_GET(msg->primary); 	if (val == SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG)) {		/* Module message */		if (type < SOF_IPC4_MOD_TYPE_LAST)			str = ipc4_dbg_mod_msg_type[type];		if (!str)			str = "Unknown Module message type";	} else {		/* Global FW message */		if (type < SOF_IPC4_GLB_TYPE_LAST)			str = ipc4_dbg_glb_msg_type[type];		if (!str)			str = "Unknown Global message type"; 		if (type == SOF_IPC4_GLB_NOTIFICATION) {			/* Notification message */			u32 notif = SOF_IPC4_NOTIFICATION_TYPE_GET(msg->primary); 			/* Do not print log buffer notification if not desired */			if (notif == SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS &&			    !sof_debug_check_flag(SOF_DBG_PRINT_DMA_POSITION_UPDATE_LOGS))				return; 			if (notif < SOF_IPC4_NOTIFY_TYPE_LAST)				str2 = ipc4_dbg_notification_type[notif];			if (!str2)				str2 = "Unknown Global notification";		}	} 	if (str2) {		if (data_size_valid && msg->data_size)			dev_dbg(dev, "%s: %#x|%#x: %s|%s [data size: %zu]\n",				text, msg->primary, msg->extension, str, str2,				msg->data_size);		else			dev_dbg(dev, "%s: %#x|%#x: %s|%s\n", text, msg->primary,				msg->extension, str, str2);	} else {		if (data_size_valid && msg->data_size)			dev_dbg(dev, "%s: %#x|%#x: %s [data size: %zu]\n",				text, msg->primary, msg->extension, str,				msg->data_size);		else			dev_dbg(dev, "%s: %#x|%#x: %s\n", text, msg->primary,				msg->extension, str);	}} const char *sof_ipc4_pipeline_state_str(enum sof_ipc4_pipeline_state state){	switch (state) {	case SOF_IPC4_PIPE_INVALID_STATE:		return " (INVALID_STATE)";	case SOF_IPC4_PIPE_UNINITIALIZED:		return " (UNINITIALIZED)";	case SOF_IPC4_PIPE_RESET:		return " (RESET)";	case SOF_IPC4_PIPE_PAUSED:		return " (PAUSED)";	case SOF_IPC4_PIPE_RUNNING:		return " (RUNNING)";	case SOF_IPC4_PIPE_EOS:		return " (EOS)";	default:		return " (<unknown>)";	}}#else /* CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC */static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_msg *msg,				bool data_size_valid){	/* Do not print log buffer notification if not desired */	if (!sof_debug_check_flag(SOF_DBG_PRINT_DMA_POSITION_UPDATE_LOGS) &&	    !SOF_IPC4_MSG_IS_MODULE_MSG(msg->primary) &&	    SOF_IPC4_MSG_TYPE_GET(msg->primary) == SOF_IPC4_GLB_NOTIFICATION &&	    SOF_IPC4_NOTIFICATION_TYPE_GET(msg->primary) == SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS)		return; 	if (data_size_valid && msg->data_size)		dev_dbg(dev, "%s: %#x|%#x [data size: %zu]\n", text,			msg->primary, msg->extension, msg->data_size);	else		dev_dbg(dev, "%s: %#x|%#x\n", text, msg->primary, msg->extension);} const char *sof_ipc4_pipeline_state_str(enum sof_ipc4_pipeline_state state){	return "";}#endif static void sof_ipc4_dump_payload(struct snd_sof_dev *sdev,				  void *ipc_data, size_t size){	print_hex_dump_debug("Message payload: ", DUMP_PREFIX_OFFSET,			     16, 4, ipc_data, size, false);} static const char *sof_ipc4_resource_type_str(u32 type){	switch (type) {	case SOF_IPC4_MODULE_INSTANCE:		return "resource: MODULE_INSTANCE";	case SOF_IPC4_PIPELINE:		return "resource: PIPELINE";	case SOF_IPC4_GATEWAY:		return "resource: GATEWAY";	case SOF_IPC4_EDF_TASK:		return "resource: EDF_TASK";	case SOF_IPC4_INVALID_RESOURCE_TYPE:		return "Resource is invalid";	default:		return "Unknown resource type";	}} static const char *sof_ipc4_resource_event_type_str(u32 event_type){	switch (event_type) {	case SOF_IPC4_MIXER_UNDERRUN_DETECTED:		return "event:    MIXER_UNDERRUN_DETECTED";	case SOF_IPC4_PROCESS_DATA_ERROR:		return "event:    PROCESS_DATA_ERROR";	case SOF_IPC4_GATEWAY_UNDERRUN_DETECTED:		return "event:    GATEWAY_UNDERRUN_DETECTED";	case SOF_IPC4_GATEWAY_OVERRUN_DETECTED:		return "event:    GATEWAY_OVERRUN_DETECTED";	default:		return "Unknown event type";	}} static void sof_ipc4_resource_event_handler(struct snd_sof_dev *sdev,					    struct sof_ipc4_msg *ipc4_msg){	struct sof_ipc4_notify_resource_data *data = ipc4_msg->data_ptr; 	/* Print event details */	switch (data->event_type) {	case SOF_IPC4_MIXER_UNDERRUN_DETECTED:		dev_dbg(sdev->dev, "%s (%u): eos %u, mixed %u, expected %u\n",			sof_ipc4_resource_event_type_str(data->event_type),			data->event_type, data->data.mixer_underrun.eos_flag,			data->data.mixer_underrun.data_mixed,			data->data.mixer_underrun.expected_data_mixed);		break;	case SOF_IPC4_PROCESS_DATA_ERROR:		dev_dbg(sdev->dev, "%s (%u): error_code %#x\n",			sof_ipc4_resource_event_type_str(data->event_type),			data->event_type, data->data.process_data_error.error_code);		break;	case SOF_IPC4_GATEWAY_UNDERRUN_DETECTED:	case SOF_IPC4_GATEWAY_OVERRUN_DETECTED:		dev_dbg(sdev->dev, "%s (%u)\n",			sof_ipc4_resource_event_type_str(data->event_type),			data->event_type);		break;	default:		dev_dbg(sdev->dev, "%s (%u): raw dws %#x %#x %#x %#x %#x %#x\n",			sof_ipc4_resource_event_type_str(data->event_type),			data->event_type,			data->data.dws[0], data->data.dws[1], data->data.dws[2],			data->data.dws[3], data->data.dws[4], data->data.dws[5]);		break;	} 	/* Print resource details */	if (data->resource_type == SOF_IPC4_MODULE_INSTANCE) {		u32 module_id = SOF_IPC4_MOD_ID_GET(data->resource_id);		u32 instance_id = SOF_IPC4_MOD_INSTANCE_GET(data->resource_id); 		dev_dbg(sdev->dev, "%s (%u), module_id %u, instance_id %u\n",			sof_ipc4_resource_type_str(data->resource_type),			data->resource_type, module_id, instance_id);	} else if (data->resource_type != SOF_IPC4_INVALID_RESOURCE_TYPE) {		dev_dbg(sdev->dev, "%s (%u), id %u\n",			sof_ipc4_resource_type_str(data->resource_type),			data->resource_type, data->resource_id);	}} static int sof_ipc4_get_reply(struct snd_sof_dev *sdev){	struct snd_sof_ipc_msg *msg = sdev->msg;	struct sof_ipc4_msg *ipc4_reply;	int ret; 	/* get the generic reply */	ipc4_reply = msg->reply_data; 	sof_ipc4_log_header(sdev->dev, "ipc tx reply", ipc4_reply, false); 	ret = sof_ipc4_check_reply_status(sdev, ipc4_reply->primary);	if (ret)		return ret; 	/* No other information is expected for non large config get replies */	if (!msg->reply_size || !SOF_IPC4_MSG_IS_MODULE_MSG(ipc4_reply->primary) ||	    (SOF_IPC4_MSG_TYPE_GET(ipc4_reply->primary) != SOF_IPC4_MOD_LARGE_CONFIG_GET))		return 0; 	/* Read the requested payload */	snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, ipc4_reply->data_ptr,				 msg->reply_size); 	return 0;} /* wait for IPC message reply */static int ipc4_wait_tx_done(struct snd_sof_ipc *ipc, void *reply_data){	struct snd_sof_ipc_msg *msg = &ipc->msg;	struct sof_ipc4_msg *ipc4_msg = msg->msg_data;	struct snd_sof_dev *sdev = ipc->sdev;	int ret; 	/* wait for DSP IPC completion */	ret = wait_event_timeout(msg->waitq, msg->ipc_complete,				 msecs_to_jiffies(sdev->ipc_timeout));	if (ret == 0) {		dev_err(sdev->dev, "ipc timed out for %#x|%#x\n",			ipc4_msg->primary, ipc4_msg->extension);		snd_sof_handle_fw_exception(ipc->sdev, "IPC timeout");		return -ETIMEDOUT;	} 	if (msg->reply_error) {		dev_err(sdev->dev, "ipc error for msg %#x|%#x\n",			ipc4_msg->primary, ipc4_msg->extension);		ret =  msg->reply_error;	} else {		if (reply_data) {			struct sof_ipc4_msg *ipc4_reply = msg->reply_data;			struct sof_ipc4_msg *ipc4_reply_data = reply_data; 			/* Copy the header */			ipc4_reply_data->header_u64 = ipc4_reply->header_u64;			if (msg->reply_size && ipc4_reply_data->data_ptr) {				/* copy the payload returned from DSP */				memcpy(ipc4_reply_data->data_ptr, ipc4_reply->data_ptr,				       msg->reply_size);				ipc4_reply_data->data_size = msg->reply_size;			}		} 		ret = 0;		sof_ipc4_log_header(sdev->dev, "ipc tx done ", ipc4_msg, true);	} 	/* re-enable dumps after successful IPC tx */	if (sdev->ipc_dump_printed) {		sdev->dbg_dump_printed = false;		sdev->ipc_dump_printed = false;	} 	return ret;} static int ipc4_tx_msg_unlocked(struct snd_sof_ipc *ipc,				void *msg_data, size_t msg_bytes,				void *reply_data, size_t reply_bytes){	struct sof_ipc4_msg *ipc4_msg = msg_data;	struct snd_sof_dev *sdev = ipc->sdev;	int ret; 	if (msg_bytes > ipc->max_payload_size || reply_bytes > ipc->max_payload_size)		return -EINVAL; 	sof_ipc4_log_header(sdev->dev, "ipc tx      ", msg_data, true); 	ret = sof_ipc_send_msg(sdev, msg_data, msg_bytes, reply_bytes);	if (ret) {		dev_err_ratelimited(sdev->dev,				    "%s: ipc message send for %#x|%#x failed: %d\n",				    __func__, ipc4_msg->primary, ipc4_msg->extension, ret);		return ret;	} 	/* now wait for completion */	return ipc4_wait_tx_done(ipc, reply_data);} static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,			   void *reply_data, size_t reply_bytes, bool no_pm){	struct snd_sof_ipc *ipc = sdev->ipc;	int ret; 	if (!msg_data)		return -EINVAL; 	if (!no_pm) {		const struct sof_dsp_power_state target_state = {			.state = SOF_DSP_PM_D0,		}; 		/* ensure the DSP is in D0i0 before sending a new IPC */		ret = snd_sof_dsp_set_power_state(sdev, &target_state);		if (ret < 0)			return ret;	} 	/* Serialise IPC TX */	guard(mutex)(&ipc->tx_mutex); 	ret = ipc4_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes);

Lines 1–500 of 1040.