2022-03-17 20:50:28 +03:00
// 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) 2021 Intel Corporation. All rights reserved.
//
//
2022-04-05 20:26:58 +03:00
# include <sound/sof/stream.h>
# include <sound/sof/control.h>
2022-09-19 15:21:07 +03:00
# include <trace/events/sof.h>
2022-03-17 20:50:28 +03:00
# include "sof-priv.h"
2022-04-05 20:27:00 +03:00
# include "sof-audio.h"
2022-04-26 01:11:21 +03:00
# include "ipc3-priv.h"
2022-04-05 20:26:56 +03:00
# include "ops.h"
2022-04-05 20:27:00 +03:00
typedef void ( * ipc3_rx_callback ) ( struct snd_sof_dev * sdev , void * msg_buf ) ;
2022-04-05 20:26:56 +03:00
# if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC)
static void ipc3_log_header ( struct device * dev , u8 * text , u32 cmd )
{
u8 * str ;
u8 * str2 = NULL ;
u32 glb ;
u32 type ;
2022-09-19 15:21:07 +03:00
bool is_sof_ipc_stream_position = false ;
2022-04-05 20:26:56 +03:00
glb = cmd & SOF_GLB_TYPE_MASK ;
type = cmd & SOF_CMD_TYPE_MASK ;
switch ( glb ) {
case SOF_IPC_GLB_REPLY :
str = " GLB_REPLY " ; break ;
case SOF_IPC_GLB_COMPOUND :
str = " GLB_COMPOUND " ; break ;
case SOF_IPC_GLB_TPLG_MSG :
str = " GLB_TPLG_MSG " ;
switch ( type ) {
case SOF_IPC_TPLG_COMP_NEW :
str2 = " COMP_NEW " ; break ;
case SOF_IPC_TPLG_COMP_FREE :
str2 = " COMP_FREE " ; break ;
case SOF_IPC_TPLG_COMP_CONNECT :
str2 = " COMP_CONNECT " ; break ;
case SOF_IPC_TPLG_PIPE_NEW :
str2 = " PIPE_NEW " ; break ;
case SOF_IPC_TPLG_PIPE_FREE :
str2 = " PIPE_FREE " ; break ;
case SOF_IPC_TPLG_PIPE_CONNECT :
str2 = " PIPE_CONNECT " ; break ;
case SOF_IPC_TPLG_PIPE_COMPLETE :
str2 = " PIPE_COMPLETE " ; break ;
case SOF_IPC_TPLG_BUFFER_NEW :
str2 = " BUFFER_NEW " ; break ;
case SOF_IPC_TPLG_BUFFER_FREE :
str2 = " BUFFER_FREE " ; break ;
default :
str2 = " unknown type " ; break ;
}
break ;
case SOF_IPC_GLB_PM_MSG :
str = " GLB_PM_MSG " ;
switch ( type ) {
case SOF_IPC_PM_CTX_SAVE :
str2 = " CTX_SAVE " ; break ;
case SOF_IPC_PM_CTX_RESTORE :
str2 = " CTX_RESTORE " ; break ;
case SOF_IPC_PM_CTX_SIZE :
str2 = " CTX_SIZE " ; break ;
case SOF_IPC_PM_CLK_SET :
str2 = " CLK_SET " ; break ;
case SOF_IPC_PM_CLK_GET :
str2 = " CLK_GET " ; break ;
case SOF_IPC_PM_CLK_REQ :
str2 = " CLK_REQ " ; break ;
case SOF_IPC_PM_CORE_ENABLE :
str2 = " CORE_ENABLE " ; break ;
case SOF_IPC_PM_GATE :
str2 = " GATE " ; break ;
default :
str2 = " unknown type " ; break ;
}
break ;
case SOF_IPC_GLB_COMP_MSG :
str = " GLB_COMP_MSG " ;
switch ( type ) {
case SOF_IPC_COMP_SET_VALUE :
str2 = " SET_VALUE " ; break ;
case SOF_IPC_COMP_GET_VALUE :
str2 = " GET_VALUE " ; break ;
case SOF_IPC_COMP_SET_DATA :
str2 = " SET_DATA " ; break ;
case SOF_IPC_COMP_GET_DATA :
str2 = " GET_DATA " ; break ;
default :
str2 = " unknown type " ; break ;
}
break ;
case SOF_IPC_GLB_STREAM_MSG :
str = " GLB_STREAM_MSG " ;
switch ( type ) {
case SOF_IPC_STREAM_PCM_PARAMS :
str2 = " PCM_PARAMS " ; break ;
case SOF_IPC_STREAM_PCM_PARAMS_REPLY :
str2 = " PCM_REPLY " ; break ;
case SOF_IPC_STREAM_PCM_FREE :
str2 = " PCM_FREE " ; break ;
case SOF_IPC_STREAM_TRIG_START :
str2 = " TRIG_START " ; break ;
case SOF_IPC_STREAM_TRIG_STOP :
str2 = " TRIG_STOP " ; break ;
case SOF_IPC_STREAM_TRIG_PAUSE :
str2 = " TRIG_PAUSE " ; break ;
case SOF_IPC_STREAM_TRIG_RELEASE :
str2 = " TRIG_RELEASE " ; break ;
case SOF_IPC_STREAM_TRIG_DRAIN :
str2 = " TRIG_DRAIN " ; break ;
case SOF_IPC_STREAM_TRIG_XRUN :
str2 = " TRIG_XRUN " ; break ;
case SOF_IPC_STREAM_POSITION :
2022-09-19 15:21:07 +03:00
is_sof_ipc_stream_position = true ;
2022-04-05 20:26:56 +03:00
str2 = " POSITION " ; break ;
case SOF_IPC_STREAM_VORBIS_PARAMS :
str2 = " VORBIS_PARAMS " ; break ;
case SOF_IPC_STREAM_VORBIS_FREE :
str2 = " VORBIS_FREE " ; break ;
default :
str2 = " unknown type " ; break ;
}
break ;
case SOF_IPC_FW_READY :
str = " FW_READY " ; break ;
case SOF_IPC_GLB_DAI_MSG :
str = " GLB_DAI_MSG " ;
switch ( type ) {
case SOF_IPC_DAI_CONFIG :
str2 = " CONFIG " ; break ;
case SOF_IPC_DAI_LOOPBACK :
str2 = " LOOPBACK " ; break ;
default :
str2 = " unknown type " ; break ;
}
break ;
case SOF_IPC_GLB_TRACE_MSG :
str = " GLB_TRACE_MSG " ;
switch ( type ) {
case SOF_IPC_TRACE_DMA_PARAMS :
str2 = " DMA_PARAMS " ; break ;
case SOF_IPC_TRACE_DMA_POSITION :
2022-06-11 00:46:01 +03:00
if ( ! sof_debug_check_flag ( SOF_DBG_PRINT_DMA_POSITION_UPDATE_LOGS ) )
return ;
2022-04-05 20:26:56 +03:00
str2 = " DMA_POSITION " ; break ;
case SOF_IPC_TRACE_DMA_PARAMS_EXT :
str2 = " DMA_PARAMS_EXT " ; break ;
case SOF_IPC_TRACE_FILTER_UPDATE :
str2 = " FILTER_UPDATE " ; break ;
case SOF_IPC_TRACE_DMA_FREE :
str2 = " DMA_FREE " ; break ;
default :
str2 = " unknown type " ; break ;
}
break ;
case SOF_IPC_GLB_TEST_MSG :
str = " GLB_TEST_MSG " ;
switch ( type ) {
case SOF_IPC_TEST_IPC_FLOOD :
str2 = " IPC_FLOOD " ; break ;
default :
str2 = " unknown type " ; break ;
}
break ;
case SOF_IPC_GLB_DEBUG :
str = " GLB_DEBUG " ;
switch ( type ) {
case SOF_IPC_DEBUG_MEM_USAGE :
str2 = " MEM_USAGE " ; break ;
default :
str2 = " unknown type " ; break ;
}
break ;
case SOF_IPC_GLB_PROBE :
str = " GLB_PROBE " ;
switch ( type ) {
case SOF_IPC_PROBE_INIT :
str2 = " INIT " ; break ;
case SOF_IPC_PROBE_DEINIT :
str2 = " DEINIT " ; break ;
case SOF_IPC_PROBE_DMA_ADD :
str2 = " DMA_ADD " ; break ;
case SOF_IPC_PROBE_DMA_INFO :
str2 = " DMA_INFO " ; break ;
case SOF_IPC_PROBE_DMA_REMOVE :
str2 = " DMA_REMOVE " ; break ;
case SOF_IPC_PROBE_POINT_ADD :
str2 = " POINT_ADD " ; break ;
case SOF_IPC_PROBE_POINT_INFO :
str2 = " POINT_INFO " ; break ;
case SOF_IPC_PROBE_POINT_REMOVE :
str2 = " POINT_REMOVE " ; break ;
default :
str2 = " unknown type " ; break ;
}
break ;
default :
str = " unknown GLB command " ; break ;
}
if ( str2 ) {
2022-09-19 15:21:07 +03:00
if ( is_sof_ipc_stream_position )
trace_sof_stream_position_ipc_rx ( dev ) ;
2022-04-05 20:26:56 +03:00
else
dev_dbg ( dev , " %s: 0x%x: %s: %s \n " , text , cmd , str , str2 ) ;
} else {
dev_dbg ( dev , " %s: 0x%x: %s \n " , text , cmd , str ) ;
}
}
# else
static inline void ipc3_log_header ( struct device * dev , u8 * text , u32 cmd )
{
if ( ( cmd & SOF_GLB_TYPE_MASK ) ! = SOF_IPC_GLB_TRACE_MSG )
dev_dbg ( dev , " %s: 0x%x \n " , text , cmd ) ;
}
# endif
2023-06-16 13:00:36 +03:00
static void sof_ipc3_dump_payload ( struct snd_sof_dev * sdev ,
void * ipc_data , size_t size )
{
printk ( KERN_DEBUG " Size of payload following the header: %zu \n " , size ) ;
print_hex_dump_debug ( " Message payload: " , DUMP_PREFIX_OFFSET ,
16 , 4 , ipc_data , size , false ) ;
}
2022-04-05 20:26:59 +03:00
static int sof_ipc3_get_reply ( struct snd_sof_dev * sdev )
{
struct snd_sof_ipc_msg * msg = sdev - > msg ;
struct sof_ipc_reply * reply ;
int ret = 0 ;
/* get the generic reply */
reply = msg - > reply_data ;
snd_sof_dsp_mailbox_read ( sdev , sdev - > host_box . offset , reply , sizeof ( * reply ) ) ;
if ( reply - > error < 0 )
return reply - > error ;
if ( ! reply - > hdr . size ) {
/* Reply should always be >= sizeof(struct sof_ipc_reply) */
if ( msg - > reply_size )
dev_err ( sdev - > dev ,
" empty reply received, expected %zu bytes \n " ,
msg - > reply_size ) ;
else
dev_err ( sdev - > dev , " empty reply received \n " ) ;
return - EINVAL ;
}
if ( msg - > reply_size > 0 ) {
if ( reply - > hdr . size = = msg - > reply_size ) {
ret = 0 ;
} else if ( reply - > hdr . size < msg - > reply_size ) {
dev_dbg ( sdev - > dev ,
" reply size (%u) is less than expected (%zu) \n " ,
reply - > hdr . size , msg - > reply_size ) ;
msg - > reply_size = reply - > hdr . size ;
ret = 0 ;
} else {
dev_err ( sdev - > dev ,
" reply size (%u) exceeds the buffer size (%zu) \n " ,
reply - > hdr . size , msg - > reply_size ) ;
ret = - EINVAL ;
}
/*
* get the full message if reply - > hdr . size < = msg - > reply_size
* and the reply - > hdr . size > sizeof ( struct sof_ipc_reply )
*/
if ( ! ret & & msg - > reply_size > sizeof ( * reply ) )
snd_sof_dsp_mailbox_read ( sdev , sdev - > host_box . offset ,
msg - > reply_data , msg - > reply_size ) ;
}
return ret ;
}
2022-04-05 20:26:56 +03:00
/* wait for IPC message reply */
static int ipc3_wait_tx_done ( struct snd_sof_ipc * ipc , void * reply_data )
{
struct snd_sof_ipc_msg * msg = & ipc - > msg ;
struct sof_ipc_cmd_hdr * hdr = 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 tx timed out for %#x (msg/reply size: %d/%zu) \n " ,
hdr - > cmd , hdr - > size , msg - > reply_size ) ;
2022-06-10 11:04:21 +03:00
snd_sof_handle_fw_exception ( ipc - > sdev , " IPC timeout " ) ;
2022-04-05 20:26:56 +03:00
ret = - ETIMEDOUT ;
} else {
ret = msg - > reply_error ;
if ( ret < 0 ) {
dev_err ( sdev - > dev ,
" ipc tx error for %#x (msg/reply size: %d/%zu): %d \n " ,
hdr - > cmd , hdr - > size , msg - > reply_size , ret ) ;
} else {
2022-06-11 00:46:01 +03:00
if ( sof_debug_check_flag ( SOF_DBG_PRINT_IPC_SUCCESS_LOGS ) )
ipc3_log_header ( sdev - > dev , " ipc tx succeeded " , hdr - > cmd ) ;
2023-08-01 00:37:42 +03:00
if ( reply_data & & msg - > reply_size )
2022-04-05 20:26:56 +03:00
/* copy the data returned from DSP */
memcpy ( reply_data , msg - > reply_data ,
msg - > reply_size ) ;
}
/* re-enable dumps after successful IPC tx */
if ( sdev - > ipc_dump_printed ) {
sdev - > dbg_dump_printed = false ;
sdev - > ipc_dump_printed = false ;
}
}
return ret ;
}
/* send IPC message from host to DSP */
static int ipc3_tx_msg_unlocked ( struct snd_sof_ipc * ipc ,
void * msg_data , size_t msg_bytes ,
void * reply_data , size_t reply_bytes )
{
struct sof_ipc_cmd_hdr * hdr = msg_data ;
struct snd_sof_dev * sdev = ipc - > sdev ;
int ret ;
2022-10-18 16:38:43 +03:00
ipc3_log_header ( sdev - > dev , " ipc tx " , hdr - > cmd ) ;
2022-04-05 20:26:56 +03:00
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 failed: %d \n " ,
__func__ , hdr - > cmd , ret ) ;
return ret ;
}
/* now wait for completion */
return ipc3_wait_tx_done ( ipc , reply_data ) ;
}
static int sof_ipc3_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 | | msg_bytes < sizeof ( struct sof_ipc_cmd_hdr ) ) {
dev_err_ratelimited ( sdev - > dev , " No IPC message to send \n " ) ;
return - EINVAL ;
}
if ( ! no_pm ) {
const struct sof_dsp_power_state target_state = {
. state = SOF_DSP_PM_D0 ,
} ;
/* ensure the DSP is in D0 before sending a new IPC */
ret = snd_sof_dsp_set_power_state ( sdev , & target_state ) ;
if ( ret < 0 ) {
dev_err ( sdev - > dev , " %s: resuming DSP failed: %d \n " ,
__func__ , ret ) ;
return ret ;
}
}
/* Serialise IPC TX */
mutex_lock ( & ipc - > tx_mutex ) ;
ret = ipc3_tx_msg_unlocked ( ipc , msg_data , msg_bytes , reply_data , reply_bytes ) ;
2023-06-16 13:00:36 +03:00
if ( sof_debug_check_flag ( SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD ) ) {
size_t payload_bytes , header_bytes ;
char * payload = NULL ;
/* payload is indicated by non zero msg/reply_bytes */
if ( msg_bytes > sizeof ( struct sof_ipc_cmd_hdr ) ) {
payload = msg_data ;
header_bytes = sizeof ( struct sof_ipc_cmd_hdr ) ;
payload_bytes = msg_bytes - header_bytes ;
} else if ( reply_bytes > sizeof ( struct sof_ipc_reply ) ) {
payload = reply_data ;
header_bytes = sizeof ( struct sof_ipc_reply ) ;
payload_bytes = reply_bytes - header_bytes ;
}
if ( payload ) {
payload + = header_bytes ;
sof_ipc3_dump_payload ( sdev , payload , payload_bytes ) ;
}
}
2022-04-05 20:26:56 +03:00
mutex_unlock ( & ipc - > tx_mutex ) ;
return ret ;
}
2022-03-17 20:50:28 +03:00
2022-04-05 20:26:58 +03:00
static int sof_ipc3_set_get_data ( struct snd_sof_dev * sdev , void * data , size_t data_bytes ,
bool set )
{
size_t msg_bytes , hdr_bytes , payload_size , send_bytes ;
struct sof_ipc_ctrl_data * cdata = data ;
struct sof_ipc_ctrl_data * cdata_chunk ;
struct snd_sof_ipc * ipc = sdev - > ipc ;
size_t offset = 0 ;
u8 * src , * dst ;
u32 num_msg ;
int ret = 0 ;
int i ;
if ( ! cdata | | data_bytes < sizeof ( * cdata ) )
return - EINVAL ;
if ( ( cdata - > rhdr . hdr . cmd & SOF_GLB_TYPE_MASK ) ! = SOF_IPC_GLB_COMP_MSG ) {
dev_err ( sdev - > dev , " %s: Not supported message type of %#x \n " ,
__func__ , cdata - > rhdr . hdr . cmd ) ;
return - EINVAL ;
}
/* send normal size ipc in one part */
if ( cdata - > rhdr . hdr . size < = ipc - > max_payload_size )
return sof_ipc3_tx_msg ( sdev , cdata , cdata - > rhdr . hdr . size ,
cdata , cdata - > rhdr . hdr . size , false ) ;
cdata_chunk = kzalloc ( ipc - > max_payload_size , GFP_KERNEL ) ;
if ( ! cdata_chunk )
return - ENOMEM ;
switch ( cdata - > type ) {
case SOF_CTRL_TYPE_VALUE_CHAN_GET :
case SOF_CTRL_TYPE_VALUE_CHAN_SET :
hdr_bytes = sizeof ( struct sof_ipc_ctrl_data ) ;
if ( set ) {
src = ( u8 * ) cdata - > chanv ;
dst = ( u8 * ) cdata_chunk - > chanv ;
} else {
src = ( u8 * ) cdata_chunk - > chanv ;
dst = ( u8 * ) cdata - > chanv ;
}
break ;
case SOF_CTRL_TYPE_DATA_GET :
case SOF_CTRL_TYPE_DATA_SET :
hdr_bytes = sizeof ( struct sof_ipc_ctrl_data ) + sizeof ( struct sof_abi_hdr ) ;
if ( set ) {
src = ( u8 * ) cdata - > data - > data ;
dst = ( u8 * ) cdata_chunk - > data - > data ;
} else {
src = ( u8 * ) cdata_chunk - > data - > data ;
dst = ( u8 * ) cdata - > data - > data ;
}
break ;
default :
kfree ( cdata_chunk ) ;
return - EINVAL ;
}
msg_bytes = cdata - > rhdr . hdr . size - hdr_bytes ;
payload_size = ipc - > max_payload_size - hdr_bytes ;
num_msg = DIV_ROUND_UP ( msg_bytes , payload_size ) ;
/* copy the header data */
memcpy ( cdata_chunk , cdata , hdr_bytes ) ;
/* Serialise IPC TX */
mutex_lock ( & sdev - > ipc - > tx_mutex ) ;
/* copy the payload data in a loop */
for ( i = 0 ; i < num_msg ; i + + ) {
send_bytes = min ( msg_bytes , payload_size ) ;
cdata_chunk - > num_elems = send_bytes ;
cdata_chunk - > rhdr . hdr . size = hdr_bytes + send_bytes ;
cdata_chunk - > msg_index = i ;
msg_bytes - = send_bytes ;
cdata_chunk - > elems_remaining = msg_bytes ;
if ( set )
memcpy ( dst , src + offset , send_bytes ) ;
ret = ipc3_tx_msg_unlocked ( sdev - > ipc ,
cdata_chunk , cdata_chunk - > rhdr . hdr . size ,
cdata_chunk , cdata_chunk - > rhdr . hdr . size ) ;
if ( ret < 0 )
break ;
if ( ! set )
memcpy ( dst + offset , src , send_bytes ) ;
offset + = payload_size ;
}
2023-06-16 13:00:36 +03:00
if ( sof_debug_check_flag ( SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD ) ) {
size_t header_bytes = sizeof ( struct sof_ipc_reply ) ;
char * payload = ( char * ) cdata ;
payload + = header_bytes ;
sof_ipc3_dump_payload ( sdev , payload , data_bytes - header_bytes ) ;
}
2022-04-05 20:26:58 +03:00
mutex_unlock ( & sdev - > ipc - > tx_mutex ) ;
kfree ( cdata_chunk ) ;
return ret ;
}
2022-04-26 01:11:21 +03:00
int sof_ipc3_get_ext_windows ( struct snd_sof_dev * sdev ,
const struct sof_ipc_ext_data_hdr * ext_hdr )
2022-04-21 11:07:30 +03:00
{
const struct sof_ipc_window * w =
container_of ( ext_hdr , struct sof_ipc_window , ext_hdr ) ;
if ( w - > num_windows = = 0 | | w - > num_windows > SOF_IPC_MAX_ELEMS )
return - EINVAL ;
if ( sdev - > info_window ) {
if ( memcmp ( sdev - > info_window , w , ext_hdr - > hdr . size ) ) {
dev_err ( sdev - > dev , " mismatch between window descriptor from extended manifest and mailbox " ) ;
return - EINVAL ;
}
return 0 ;
}
/* keep a local copy of the data */
sdev - > info_window = devm_kmemdup ( sdev - > dev , w , ext_hdr - > hdr . size , GFP_KERNEL ) ;
if ( ! sdev - > info_window )
return - ENOMEM ;
return 0 ;
}
2022-04-26 01:11:21 +03:00
int sof_ipc3_get_cc_info ( struct snd_sof_dev * sdev ,
const struct sof_ipc_ext_data_hdr * ext_hdr )
2022-04-21 11:07:30 +03:00
{
int ret ;
const struct sof_ipc_cc_version * cc =
container_of ( ext_hdr , struct sof_ipc_cc_version , ext_hdr ) ;
if ( sdev - > cc_version ) {
if ( memcmp ( sdev - > cc_version , cc , cc - > ext_hdr . hdr . size ) ) {
dev_err ( sdev - > dev ,
" Receive diverged cc_version descriptions " ) ;
return - EINVAL ;
}
return 0 ;
}
dev_dbg ( sdev - > dev ,
" Firmware info: used compiler %s %d:%d:%d%s used optimization flags %s \n " ,
cc - > name , cc - > major , cc - > minor , cc - > micro , cc - > desc , cc - > optim ) ;
/* create read-only cc_version debugfs to store compiler version info */
/* use local copy of the cc_version to prevent data corruption */
if ( sdev - > first_boot ) {
sdev - > cc_version = devm_kmalloc ( sdev - > dev , cc - > ext_hdr . hdr . size ,
GFP_KERNEL ) ;
if ( ! sdev - > cc_version )
return - ENOMEM ;
memcpy ( sdev - > cc_version , cc , cc - > ext_hdr . hdr . size ) ;
ret = snd_sof_debugfs_buf_item ( sdev , sdev - > cc_version ,
cc - > ext_hdr . hdr . size ,
" cc_version " , 0444 ) ;
/* errors are only due to memory allocation, not debugfs */
if ( ret < 0 ) {
dev_err ( sdev - > dev , " snd_sof_debugfs_buf_item failed \n " ) ;
return ret ;
}
}
return 0 ;
}
/* parse the extended FW boot data structures from FW boot message */
static int ipc3_fw_parse_ext_data ( struct snd_sof_dev * sdev , u32 offset )
{
struct sof_ipc_ext_data_hdr * ext_hdr ;
void * ext_data ;
int ret = 0 ;
ext_data = kzalloc ( PAGE_SIZE , GFP_KERNEL ) ;
if ( ! ext_data )
return - ENOMEM ;
/* get first header */
snd_sof_dsp_block_read ( sdev , SOF_FW_BLK_TYPE_SRAM , offset , ext_data ,
sizeof ( * ext_hdr ) ) ;
ext_hdr = ext_data ;
while ( ext_hdr - > hdr . cmd = = SOF_IPC_FW_READY ) {
/* read in ext structure */
snd_sof_dsp_block_read ( sdev , SOF_FW_BLK_TYPE_SRAM ,
offset + sizeof ( * ext_hdr ) ,
( void * ) ( ( u8 * ) ext_data + sizeof ( * ext_hdr ) ) ,
ext_hdr - > hdr . size - sizeof ( * ext_hdr ) ) ;
dev_dbg ( sdev - > dev , " found ext header type %d size 0x%x \n " ,
ext_hdr - > type , ext_hdr - > hdr . size ) ;
/* process structure data */
switch ( ext_hdr - > type ) {
case SOF_IPC_EXT_WINDOW :
ret = sof_ipc3_get_ext_windows ( sdev , ext_hdr ) ;
break ;
case SOF_IPC_EXT_CC_INFO :
ret = sof_ipc3_get_cc_info ( sdev , ext_hdr ) ;
break ;
case SOF_IPC_EXT_UNUSED :
case SOF_IPC_EXT_PROBE_INFO :
case SOF_IPC_EXT_USER_ABI_INFO :
/* They are supported but we don't do anything here */
break ;
default :
dev_info ( sdev - > dev , " unknown ext header type %d size 0x%x \n " ,
ext_hdr - > type , ext_hdr - > hdr . size ) ;
ret = 0 ;
break ;
}
if ( ret < 0 ) {
dev_err ( sdev - > dev , " Failed to parse ext data type %d \n " ,
ext_hdr - > type ) ;
break ;
}
/* move to next header */
offset + = ext_hdr - > hdr . size ;
snd_sof_dsp_block_read ( sdev , SOF_FW_BLK_TYPE_SRAM , offset , ext_data ,
sizeof ( * ext_hdr ) ) ;
ext_hdr = ext_data ;
}
kfree ( ext_data ) ;
return ret ;
}
static void ipc3_get_windows ( struct snd_sof_dev * sdev )
{
struct sof_ipc_window_elem * elem ;
u32 outbox_offset = 0 ;
u32 stream_offset = 0 ;
u32 inbox_offset = 0 ;
u32 outbox_size = 0 ;
u32 stream_size = 0 ;
u32 inbox_size = 0 ;
u32 debug_size = 0 ;
u32 debug_offset = 0 ;
int window_offset ;
int i ;
if ( ! sdev - > info_window ) {
dev_err ( sdev - > dev , " %s: No window info present \n " , __func__ ) ;
return ;
}
for ( i = 0 ; i < sdev - > info_window - > num_windows ; i + + ) {
elem = & sdev - > info_window - > window [ i ] ;
window_offset = snd_sof_dsp_get_window_offset ( sdev , elem - > id ) ;
if ( window_offset < 0 ) {
dev_warn ( sdev - > dev , " No offset for window %d \n " , elem - > id ) ;
continue ;
}
switch ( elem - > type ) {
case SOF_IPC_REGION_UPBOX :
inbox_offset = window_offset + elem - > offset ;
inbox_size = elem - > size ;
snd_sof_debugfs_add_region_item ( sdev , SOF_FW_BLK_TYPE_SRAM ,
inbox_offset ,
elem - > size , " inbox " ,
SOF_DEBUGFS_ACCESS_D0_ONLY ) ;
break ;
case SOF_IPC_REGION_DOWNBOX :
outbox_offset = window_offset + elem - > offset ;
outbox_size = elem - > size ;
snd_sof_debugfs_add_region_item ( sdev , SOF_FW_BLK_TYPE_SRAM ,
outbox_offset ,
elem - > size , " outbox " ,
SOF_DEBUGFS_ACCESS_D0_ONLY ) ;
break ;
case SOF_IPC_REGION_TRACE :
snd_sof_debugfs_add_region_item ( sdev , SOF_FW_BLK_TYPE_SRAM ,
window_offset + elem - > offset ,
elem - > size , " etrace " ,
SOF_DEBUGFS_ACCESS_D0_ONLY ) ;
break ;
case SOF_IPC_REGION_DEBUG :
debug_offset = window_offset + elem - > offset ;
debug_size = elem - > size ;
snd_sof_debugfs_add_region_item ( sdev , SOF_FW_BLK_TYPE_SRAM ,
window_offset + elem - > offset ,
elem - > size , " debug " ,
SOF_DEBUGFS_ACCESS_D0_ONLY ) ;
break ;
case SOF_IPC_REGION_STREAM :
stream_offset = window_offset + elem - > offset ;
stream_size = elem - > size ;
snd_sof_debugfs_add_region_item ( sdev , SOF_FW_BLK_TYPE_SRAM ,
stream_offset ,
elem - > size , " stream " ,
SOF_DEBUGFS_ACCESS_D0_ONLY ) ;
break ;
case SOF_IPC_REGION_REGS :
snd_sof_debugfs_add_region_item ( sdev , SOF_FW_BLK_TYPE_SRAM ,
window_offset + elem - > offset ,
elem - > size , " regs " ,
SOF_DEBUGFS_ACCESS_D0_ONLY ) ;
break ;
case SOF_IPC_REGION_EXCEPTION :
sdev - > dsp_oops_offset = window_offset + elem - > offset ;
snd_sof_debugfs_add_region_item ( sdev , SOF_FW_BLK_TYPE_SRAM ,
window_offset + elem - > offset ,
elem - > size , " exception " ,
SOF_DEBUGFS_ACCESS_D0_ONLY ) ;
break ;
default :
dev_err ( sdev - > dev , " %s: Illegal window info: %u \n " ,
__func__ , elem - > type ) ;
return ;
}
}
if ( outbox_size = = 0 | | inbox_size = = 0 ) {
dev_err ( sdev - > dev , " %s: Illegal mailbox window \n " , __func__ ) ;
return ;
}
sdev - > dsp_box . offset = inbox_offset ;
sdev - > dsp_box . size = inbox_size ;
sdev - > host_box . offset = outbox_offset ;
sdev - > host_box . size = outbox_size ;
sdev - > stream_box . offset = stream_offset ;
sdev - > stream_box . size = stream_size ;
sdev - > debug_box . offset = debug_offset ;
sdev - > debug_box . size = debug_size ;
dev_dbg ( sdev - > dev , " mailbox upstream 0x%x - size 0x%x \n " ,
inbox_offset , inbox_size ) ;
dev_dbg ( sdev - > dev , " mailbox downstream 0x%x - size 0x%x \n " ,
outbox_offset , outbox_size ) ;
dev_dbg ( sdev - > dev , " stream region 0x%x - size 0x%x \n " ,
stream_offset , stream_size ) ;
dev_dbg ( sdev - > dev , " debug region 0x%x - size 0x%x \n " ,
debug_offset , debug_size ) ;
}
static int ipc3_init_reply_data_buffer ( struct snd_sof_dev * sdev )
{
struct snd_sof_ipc_msg * msg = & sdev - > ipc - > msg ;
msg - > reply_data = devm_kzalloc ( sdev - > dev , SOF_IPC_MSG_MAX_SIZE , GFP_KERNEL ) ;
if ( ! msg - > reply_data )
return - ENOMEM ;
sdev - > ipc - > max_payload_size = SOF_IPC_MSG_MAX_SIZE ;
return 0 ;
}
2022-04-26 01:11:21 +03:00
int sof_ipc3_validate_fw_version ( struct snd_sof_dev * sdev )
{
struct sof_ipc_fw_ready * ready = & sdev - > fw_ready ;
struct sof_ipc_fw_version * v = & ready - > version ;
dev_info ( sdev - > dev ,
" Firmware info: version %d:%d:%d-%s \n " , v - > major , v - > minor ,
v - > micro , v - > tag ) ;
dev_info ( sdev - > dev ,
" Firmware: ABI %d:%d:%d Kernel ABI %d:%d:%d \n " ,
SOF_ABI_VERSION_MAJOR ( v - > abi_version ) ,
SOF_ABI_VERSION_MINOR ( v - > abi_version ) ,
SOF_ABI_VERSION_PATCH ( v - > abi_version ) ,
SOF_ABI_MAJOR , SOF_ABI_MINOR , SOF_ABI_PATCH ) ;
if ( SOF_ABI_VERSION_INCOMPATIBLE ( SOF_ABI_VERSION , v - > abi_version ) ) {
dev_err ( sdev - > dev , " incompatible FW ABI version \n " ) ;
return - EINVAL ;
}
2022-07-08 23:07:19 +03:00
if ( IS_ENABLED ( CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS ) & &
SOF_ABI_VERSION_MINOR ( v - > abi_version ) > SOF_ABI_MINOR ) {
dev_err ( sdev - > dev , " FW ABI is more recent than kernel \n " ) ;
return - EINVAL ;
2022-04-26 01:11:21 +03:00
}
if ( ready - > flags & SOF_IPC_INFO_BUILD ) {
dev_info ( sdev - > dev ,
" Firmware debug build %d on %s-%s - options: \n "
" GDB: %s \n "
" lock debug: %s \n "
" lock vdebug: %s \n " ,
v - > build , v - > date , v - > time ,
( ready - > flags & SOF_IPC_INFO_GDB ) ?
" enabled " : " disabled " ,
( ready - > flags & SOF_IPC_INFO_LOCKS ) ?
" enabled " : " disabled " ,
( ready - > flags & SOF_IPC_INFO_LOCKSV ) ?
" enabled " : " disabled " ) ;
}
/* copy the fw_version into debugfs at first boot */
memcpy ( & sdev - > fw_version , v , sizeof ( * v ) ) ;
return 0 ;
}
2022-04-21 11:07:30 +03:00
static int ipc3_fw_ready ( struct snd_sof_dev * sdev , u32 cmd )
{
struct sof_ipc_fw_ready * fw_ready = & sdev - > fw_ready ;
int offset ;
int ret ;
/* mailbox must be on 4k boundary */
offset = snd_sof_dsp_get_mailbox_offset ( sdev ) ;
if ( offset < 0 ) {
dev_err ( sdev - > dev , " %s: no mailbox offset \n " , __func__ ) ;
return offset ;
}
dev_dbg ( sdev - > dev , " DSP is ready 0x%8.8x offset 0x%x \n " , cmd , offset ) ;
/* no need to re-check version/ABI for subsequent boots */
if ( ! sdev - > first_boot )
return 0 ;
/*
* copy data from the DSP FW ready offset
* Subsequent error handling is not needed for BLK_TYPE_SRAM
*/
ret = snd_sof_dsp_block_read ( sdev , SOF_FW_BLK_TYPE_SRAM , offset , fw_ready ,
sizeof ( * fw_ready ) ) ;
if ( ret ) {
dev_err ( sdev - > dev ,
" Unable to read fw_ready, read from TYPE_SRAM failed \n " ) ;
return ret ;
}
/* make sure ABI version is compatible */
2022-04-26 01:11:21 +03:00
ret = sof_ipc3_validate_fw_version ( sdev ) ;
2022-04-21 11:07:30 +03:00
if ( ret < 0 )
return ret ;
/* now check for extended data */
ipc3_fw_parse_ext_data ( sdev , offset + sizeof ( struct sof_ipc_fw_ready ) ) ;
ipc3_get_windows ( sdev ) ;
return ipc3_init_reply_data_buffer ( sdev ) ;
}
2022-04-05 20:27:00 +03:00
/* IPC stream position. */
static void ipc3_period_elapsed ( struct snd_sof_dev * sdev , u32 msg_id )
{
struct snd_soc_component * scomp = sdev - > component ;
struct snd_sof_pcm_stream * stream ;
struct sof_ipc_stream_posn posn ;
struct snd_sof_pcm * spcm ;
int direction , ret ;
spcm = snd_sof_find_spcm_comp ( scomp , msg_id , & direction ) ;
if ( ! spcm ) {
dev_err ( sdev - > dev , " period elapsed for unknown stream, msg_id %d \n " ,
msg_id ) ;
return ;
}
stream = & spcm - > stream [ direction ] ;
2023-01-17 15:25:30 +03:00
ret = snd_sof_ipc_msg_data ( sdev , stream , & posn , sizeof ( posn ) ) ;
2022-04-05 20:27:00 +03:00
if ( ret < 0 ) {
dev_warn ( sdev - > dev , " failed to read stream position: %d \n " , ret ) ;
return ;
}
2022-09-19 15:21:07 +03:00
trace_sof_ipc3_period_elapsed_position ( sdev , & posn ) ;
2022-04-05 20:27:00 +03:00
memcpy ( & stream - > posn , & posn , sizeof ( posn ) ) ;
if ( spcm - > pcm . compress )
snd_sof_compr_fragment_elapsed ( stream - > cstream ) ;
else if ( stream - > substream - > runtime & &
! stream - > substream - > runtime - > no_period_wakeup )
/* only inform ALSA for period_wakeup mode */
snd_sof_pcm_period_elapsed ( stream - > substream ) ;
}
/* DSP notifies host of an XRUN within FW */
static void ipc3_xrun ( struct snd_sof_dev * sdev , u32 msg_id )
{
struct snd_soc_component * scomp = sdev - > component ;
struct snd_sof_pcm_stream * stream ;
struct sof_ipc_stream_posn posn ;
struct snd_sof_pcm * spcm ;
int direction , ret ;
spcm = snd_sof_find_spcm_comp ( scomp , msg_id , & direction ) ;
if ( ! spcm ) {
dev_err ( sdev - > dev , " XRUN for unknown stream, msg_id %d \n " ,
msg_id ) ;
return ;
}
stream = & spcm - > stream [ direction ] ;
2023-01-17 15:25:30 +03:00
ret = snd_sof_ipc_msg_data ( sdev , stream , & posn , sizeof ( posn ) ) ;
2022-04-05 20:27:00 +03:00
if ( ret < 0 ) {
dev_warn ( sdev - > dev , " failed to read overrun position: %d \n " , ret ) ;
return ;
}
dev_dbg ( sdev - > dev , " posn XRUN: host %llx comp %d size %d \n " ,
posn . host_posn , posn . xrun_comp_id , posn . xrun_size ) ;
# if defined(CONFIG_SND_SOC_SOF_DEBUG_XRUN_STOP)
/* stop PCM on XRUN - used for pipeline debug */
memcpy ( & stream - > posn , & posn , sizeof ( posn ) ) ;
snd_pcm_stop_xrun ( stream - > substream ) ;
# endif
}
/* stream notifications from firmware */
static void ipc3_stream_message ( struct snd_sof_dev * sdev , void * msg_buf )
{
struct sof_ipc_cmd_hdr * hdr = msg_buf ;
u32 msg_type = hdr - > cmd & SOF_CMD_TYPE_MASK ;
u32 msg_id = SOF_IPC_MESSAGE_ID ( hdr - > cmd ) ;
switch ( msg_type ) {
case SOF_IPC_STREAM_POSITION :
ipc3_period_elapsed ( sdev , msg_id ) ;
break ;
case SOF_IPC_STREAM_TRIG_XRUN :
ipc3_xrun ( sdev , msg_id ) ;
break ;
default :
dev_err ( sdev - > dev , " unhandled stream message %#x \n " ,
msg_id ) ;
break ;
}
}
/* component notifications from firmware */
static void ipc3_comp_notification ( struct snd_sof_dev * sdev , void * msg_buf )
{
const struct sof_ipc_tplg_ops * tplg_ops = sdev - > ipc - > ops - > tplg ;
struct sof_ipc_cmd_hdr * hdr = msg_buf ;
u32 msg_type = hdr - > cmd & SOF_CMD_TYPE_MASK ;
switch ( msg_type ) {
case SOF_IPC_COMP_GET_VALUE :
case SOF_IPC_COMP_GET_DATA :
break ;
default :
dev_err ( sdev - > dev , " unhandled component message %#x \n " , msg_type ) ;
return ;
}
if ( tplg_ops - > control - > update )
tplg_ops - > control - > update ( sdev , msg_buf ) ;
}
static void ipc3_trace_message ( struct snd_sof_dev * sdev , void * msg_buf )
{
struct sof_ipc_cmd_hdr * hdr = msg_buf ;
u32 msg_type = hdr - > cmd & SOF_CMD_TYPE_MASK ;
switch ( msg_type ) {
case SOF_IPC_TRACE_DMA_POSITION :
2022-05-16 13:47:07 +03:00
ipc3_dtrace_posn_update ( sdev , msg_buf ) ;
2022-04-05 20:27:00 +03:00
break ;
default :
dev_err ( sdev - > dev , " unhandled trace message %#x \n " , msg_type ) ;
break ;
}
}
2023-06-09 01:18:15 +03:00
void sof_ipc3_do_rx_work ( struct snd_sof_dev * sdev , struct sof_ipc_cmd_hdr * hdr , void * msg_buf )
2022-04-05 20:27:00 +03:00
{
ipc3_rx_callback rx_callback = NULL ;
u32 cmd ;
int err ;
2023-06-09 01:18:15 +03:00
ipc3_log_header ( sdev - > dev , " ipc rx " , hdr - > cmd ) ;
2022-04-05 20:27:00 +03:00
2023-08-07 10:51:18 +03:00
if ( hdr - > size < sizeof ( * hdr ) | | hdr - > size > SOF_IPC_MSG_MAX_SIZE ) {
2023-03-07 14:49:17 +03:00
dev_err ( sdev - > dev , " The received message size is invalid: %u \n " ,
2023-06-09 01:18:15 +03:00
hdr - > size ) ;
2022-04-05 20:27:00 +03:00
return ;
}
2023-06-09 01:18:15 +03:00
cmd = hdr - > cmd & SOF_GLB_TYPE_MASK ;
2022-04-05 20:27:00 +03:00
/* check message type */
switch ( cmd ) {
case SOF_IPC_GLB_REPLY :
dev_err ( sdev - > dev , " ipc reply unknown \n " ) ;
break ;
case SOF_IPC_FW_READY :
/* check for FW boot completion */
if ( sdev - > fw_state = = SOF_FW_BOOT_IN_PROGRESS ) {
2022-04-21 11:07:30 +03:00
err = ipc3_fw_ready ( sdev , cmd ) ;
2022-04-05 20:27:00 +03:00
if ( err < 0 )
sof_set_fw_state ( sdev , SOF_FW_BOOT_READY_FAILED ) ;
else
sof_set_fw_state ( sdev , SOF_FW_BOOT_READY_OK ) ;
/* wake up firmware loader */
wake_up ( & sdev - > boot_wait ) ;
}
break ;
case SOF_IPC_GLB_COMPOUND :
case SOF_IPC_GLB_TPLG_MSG :
case SOF_IPC_GLB_PM_MSG :
break ;
case SOF_IPC_GLB_COMP_MSG :
rx_callback = ipc3_comp_notification ;
break ;
case SOF_IPC_GLB_STREAM_MSG :
rx_callback = ipc3_stream_message ;
break ;
case SOF_IPC_GLB_TRACE_MSG :
rx_callback = ipc3_trace_message ;
break ;
default :
dev_err ( sdev - > dev , " %s: Unknown DSP message: 0x%x \n " , __func__ , cmd ) ;
break ;
}
2023-06-09 01:18:15 +03:00
/* Call local handler for the message */
if ( rx_callback )
rx_callback ( sdev , msg_buf ) ;
/* Notify registered clients */
sof_client_ipc_rx_dispatcher ( sdev , msg_buf ) ;
ipc3_log_header ( sdev - > dev , " ipc rx done " , hdr - > cmd ) ;
}
EXPORT_SYMBOL ( sof_ipc3_do_rx_work ) ;
/* DSP firmware has sent host a message */
static void sof_ipc3_rx_msg ( struct snd_sof_dev * sdev )
{
struct sof_ipc_cmd_hdr hdr ;
void * msg_buf ;
int err ;
/* read back header */
err = snd_sof_ipc_msg_data ( sdev , NULL , & hdr , sizeof ( hdr ) ) ;
if ( err < 0 ) {
dev_warn ( sdev - > dev , " failed to read IPC header: %d \n " , err ) ;
return ;
}
if ( hdr . size < sizeof ( hdr ) ) {
dev_err ( sdev - > dev , " The received message size is invalid \n " ) ;
return ;
}
2022-04-05 20:27:00 +03:00
/* read the full message */
msg_buf = kmalloc ( hdr . size , GFP_KERNEL ) ;
if ( ! msg_buf )
return ;
err = snd_sof_ipc_msg_data ( sdev , NULL , msg_buf , hdr . size ) ;
if ( err < 0 ) {
dev_err ( sdev - > dev , " %s: Failed to read message: %d \n " , __func__ , err ) ;
2023-06-09 01:18:15 +03:00
kfree ( msg_buf ) ;
return ;
2022-04-05 20:27:00 +03:00
}
2023-06-09 01:18:15 +03:00
sof_ipc3_do_rx_work ( sdev , & hdr , msg_buf ) ;
2022-04-05 20:27:00 +03:00
2023-06-09 01:18:15 +03:00
kfree ( msg_buf ) ;
2022-04-05 20:27:00 +03:00
}
2022-06-10 11:35:46 +03:00
static int sof_ipc3_set_core_state ( struct snd_sof_dev * sdev , int core_idx , bool on )
{
struct sof_ipc_pm_core_config core_cfg = {
. hdr . size = sizeof ( core_cfg ) ,
. hdr . cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE ,
} ;
if ( on )
core_cfg . enable_mask = sdev - > enabled_cores_mask | BIT ( core_idx ) ;
else
core_cfg . enable_mask = sdev - > enabled_cores_mask & ~ BIT ( core_idx ) ;
2023-04-19 22:40:57 +03:00
return sof_ipc3_tx_msg ( sdev , & core_cfg , sizeof ( core_cfg ) , NULL , 0 , false ) ;
2022-06-10 11:35:46 +03:00
}
2022-03-17 20:50:28 +03:00
static int sof_ipc3_ctx_ipc ( struct snd_sof_dev * sdev , int cmd )
{
struct sof_ipc_pm_ctx pm_ctx = {
. hdr . size = sizeof ( pm_ctx ) ,
. hdr . cmd = SOF_IPC_GLB_PM_MSG | cmd ,
} ;
/* send ctx save ipc to dsp */
2023-04-19 22:40:57 +03:00
return sof_ipc3_tx_msg ( sdev , & pm_ctx , sizeof ( pm_ctx ) , NULL , 0 , false ) ;
2022-03-17 20:50:28 +03:00
}
static int sof_ipc3_ctx_save ( struct snd_sof_dev * sdev )
{
return sof_ipc3_ctx_ipc ( sdev , SOF_IPC_PM_CTX_SAVE ) ;
}
static int sof_ipc3_ctx_restore ( struct snd_sof_dev * sdev )
{
return sof_ipc3_ctx_ipc ( sdev , SOF_IPC_PM_CTX_RESTORE ) ;
}
2023-02-14 13:33:41 +03:00
static int sof_ipc3_set_pm_gate ( struct snd_sof_dev * sdev , u32 flags )
{
struct sof_ipc_pm_gate pm_gate ;
memset ( & pm_gate , 0 , sizeof ( pm_gate ) ) ;
/* configure pm_gate ipc message */
pm_gate . hdr . size = sizeof ( pm_gate ) ;
pm_gate . hdr . cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE ;
pm_gate . flags = flags ;
/* send pm_gate ipc to dsp */
2023-04-19 22:40:57 +03:00
return sof_ipc_tx_message_no_pm_no_reply ( sdev - > ipc , & pm_gate , sizeof ( pm_gate ) ) ;
2023-02-14 13:33:41 +03:00
}
2022-03-17 20:50:28 +03:00
static const struct sof_ipc_pm_ops ipc3_pm_ops = {
. ctx_save = sof_ipc3_ctx_save ,
. ctx_restore = sof_ipc3_ctx_restore ,
2022-06-10 11:35:46 +03:00
. set_core_state = sof_ipc3_set_core_state ,
2023-02-14 13:33:41 +03:00
. set_pm_gate = sof_ipc3_set_pm_gate ,
2022-03-17 20:50:28 +03:00
} ;
const struct sof_ipc_ops ipc3_ops = {
. tplg = & ipc3_tplg_ops ,
. pm = & ipc3_pm_ops ,
2022-03-17 20:50:38 +03:00
. pcm = & ipc3_pcm_ops ,
2022-04-26 01:11:21 +03:00
. fw_loader = & ipc3_loader_ops ,
2022-05-16 13:47:06 +03:00
. fw_tracing = & ipc3_dtrace_ops ,
2022-04-05 20:26:56 +03:00
. tx_msg = sof_ipc3_tx_msg ,
2022-04-05 20:27:00 +03:00
. rx_msg = sof_ipc3_rx_msg ,
2022-04-05 20:26:58 +03:00
. set_get_data = sof_ipc3_set_get_data ,
2022-04-05 20:26:59 +03:00
. get_reply = sof_ipc3_get_reply ,
2022-03-17 20:50:28 +03:00
} ;