2022-02-10 17:05:25 +02:00
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2019-2022 Intel Corporation. All rights reserved.
//
// Author: Cezary Rojewski <cezary.rojewski@intel.com>
//
// SOF client support:
// Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
// Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
//
# include <linux/debugfs.h>
# include <linux/module.h>
# include <linux/pm_runtime.h>
# include <sound/soc.h>
# include <sound/sof/header.h>
# include "sof-client.h"
# include "sof-client-probes.h"
# define SOF_PROBES_SUSPEND_DELAY_MS 3000
/* only extraction supported for now */
# define SOF_PROBES_NUM_DAI_LINKS 1
# define SOF_PROBES_INVALID_NODE_ID UINT_MAX
static bool __read_mostly sof_probes_enabled ;
module_param_named ( enable , sof_probes_enabled , bool , 0444 ) ;
MODULE_PARM_DESC ( enable , " Enable SOF probes support " ) ;
struct sof_probes_priv {
struct dentry * dfs_points ;
struct dentry * dfs_points_remove ;
u32 extractor_stream_tag ;
struct snd_soc_card card ;
const struct sof_probes_host_ops * host_ops ;
} ;
struct sof_probe_point_desc {
unsigned int buffer_id ;
unsigned int purpose ;
unsigned int stream_tag ;
} __packed ;
struct sof_probe_dma {
unsigned int stream_tag ;
unsigned int dma_buffer_size ;
} __packed ;
struct sof_ipc_probe_dma_add_params {
struct sof_ipc_cmd_hdr hdr ;
unsigned int num_elems ;
struct sof_probe_dma dma [ ] ;
} __packed ;
struct sof_ipc_probe_info_params {
struct sof_ipc_reply rhdr ;
unsigned int num_elems ;
union {
struct sof_probe_dma dma [ 0 ] ;
struct sof_probe_point_desc desc [ 0 ] ;
} ;
} __packed ;
struct sof_ipc_probe_point_add_params {
struct sof_ipc_cmd_hdr hdr ;
unsigned int num_elems ;
struct sof_probe_point_desc desc [ ] ;
} __packed ;
struct sof_ipc_probe_point_remove_params {
struct sof_ipc_cmd_hdr hdr ;
unsigned int num_elems ;
unsigned int buffer_id [ ] ;
} __packed ;
/**
* sof_probes_init - initialize data probing
* @ cdev : SOF client device
* @ stream_tag : Extractor stream tag
* @ buffer_size : DMA buffer size to set for extractor
*
* Host chooses whether extraction is supported or not by providing
* valid stream tag to DSP . Once specified , stream described by that
* tag will be tied to DSP for extraction for the entire lifetime of
* probe .
*
* Probing is initialized only once and each INIT request must be
* matched by DEINIT call .
*/
static int sof_probes_init ( struct sof_client_dev * cdev , u32 stream_tag ,
size_t buffer_size )
{
struct sof_ipc_probe_dma_add_params * msg ;
size_t size = struct_size ( msg , dma , 1 ) ;
struct sof_ipc_reply reply ;
int ret ;
msg = kmalloc ( size , GFP_KERNEL ) ;
if ( ! msg )
return - ENOMEM ;
msg - > hdr . size = size ;
msg - > hdr . cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_INIT ;
msg - > num_elems = 1 ;
msg - > dma [ 0 ] . stream_tag = stream_tag ;
msg - > dma [ 0 ] . dma_buffer_size = buffer_size ;
ret = sof_client_ipc_tx_message ( cdev , msg , & reply , sizeof ( reply ) ) ;
kfree ( msg ) ;
return ret ;
}
/**
* sof_probes_deinit - cleanup after data probing
* @ cdev : SOF client device
*
* Host sends DEINIT request to free previously initialized probe
* on DSP side once it is no longer needed . DEINIT only when there
* are no probes connected and with all injectors detached .
*/
static int sof_probes_deinit ( struct sof_client_dev * cdev )
{
struct sof_ipc_cmd_hdr msg ;
struct sof_ipc_reply reply ;
msg . size = sizeof ( msg ) ;
msg . cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT ;
return sof_client_ipc_tx_message ( cdev , & msg , & reply , sizeof ( reply ) ) ;
}
static int sof_probes_info ( struct sof_client_dev * cdev , unsigned int cmd ,
void * * params , size_t * num_params )
{
2022-05-06 16:26:42 +03:00
size_t max_msg_size = sof_client_get_ipc_max_payload_size ( cdev ) ;
2022-02-10 17:05:25 +02:00
struct sof_ipc_probe_info_params msg = { { { 0 } } } ;
struct sof_ipc_probe_info_params * reply ;
size_t bytes ;
int ret ;
* params = NULL ;
* num_params = 0 ;
2022-05-06 16:26:42 +03:00
reply = kzalloc ( max_msg_size , GFP_KERNEL ) ;
2022-02-10 17:05:25 +02:00
if ( ! reply )
return - ENOMEM ;
msg . rhdr . hdr . size = sizeof ( msg ) ;
msg . rhdr . hdr . cmd = SOF_IPC_GLB_PROBE | cmd ;
2022-05-06 16:26:42 +03:00
ret = sof_client_ipc_tx_message ( cdev , & msg , reply , max_msg_size ) ;
2022-02-10 17:05:25 +02:00
if ( ret < 0 | | reply - > rhdr . error < 0 )
goto exit ;
if ( ! reply - > num_elems )
goto exit ;
if ( cmd = = SOF_IPC_PROBE_DMA_INFO )
bytes = sizeof ( reply - > dma [ 0 ] ) ;
else
bytes = sizeof ( reply - > desc [ 0 ] ) ;
bytes * = reply - > num_elems ;
* params = kmemdup ( & reply - > dma [ 0 ] , bytes , GFP_KERNEL ) ;
if ( ! * params ) {
ret = - ENOMEM ;
goto exit ;
}
* num_params = reply - > num_elems ;
exit :
kfree ( reply ) ;
return ret ;
}
/**
* sof_probes_points_info - retrieve list of active probe points
* @ cdev : SOF client device
* @ desc : Returned list of active probes
* @ num_desc : Returned count of active probes
*
* Host sends PROBE_POINT_INFO request to obtain list of active probe
* points , valid for disconnection when given probe is no longer
* required .
*/
static int sof_probes_points_info ( struct sof_client_dev * cdev ,
struct sof_probe_point_desc * * desc ,
size_t * num_desc )
{
return sof_probes_info ( cdev , SOF_IPC_PROBE_POINT_INFO ,
( void * * ) desc , num_desc ) ;
}
/**
* sof_probes_points_add - connect specified probes
* @ cdev : SOF client device
* @ desc : List of probe points to connect
* @ num_desc : Number of elements in @ desc
*
* Dynamically connects to provided set of endpoints . Immediately
* after connection is established , host must be prepared to
* transfer data from or to target stream given the probing purpose .
*
* Each probe point should be removed using PROBE_POINT_REMOVE
* request when no longer needed .
*/
static int sof_probes_points_add ( struct sof_client_dev * cdev ,
struct sof_probe_point_desc * desc ,
size_t num_desc )
{
struct sof_ipc_probe_point_add_params * msg ;
size_t size = struct_size ( msg , desc , num_desc ) ;
struct sof_ipc_reply reply ;
int ret ;
msg = kmalloc ( size , GFP_KERNEL ) ;
if ( ! msg )
return - ENOMEM ;
msg - > hdr . size = size ;
msg - > num_elems = num_desc ;
msg - > hdr . cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD ;
memcpy ( & msg - > desc [ 0 ] , desc , size - sizeof ( * msg ) ) ;
ret = sof_client_ipc_tx_message ( cdev , msg , & reply , sizeof ( reply ) ) ;
kfree ( msg ) ;
return ret ;
}
/**
* sof_probes_points_remove - disconnect specified probes
* @ cdev : SOF client device
* @ buffer_id : List of probe points to disconnect
* @ num_buffer_id : Number of elements in @ desc
*
* Removes previously connected probes from list of active probe
* points and frees all resources on DSP side .
*/
static int sof_probes_points_remove ( struct sof_client_dev * cdev ,
unsigned int * buffer_id , size_t num_buffer_id )
{
struct sof_ipc_probe_point_remove_params * msg ;
size_t size = struct_size ( msg , buffer_id , num_buffer_id ) ;
struct sof_ipc_reply reply ;
int ret ;
msg = kmalloc ( size , GFP_KERNEL ) ;
if ( ! msg )
return - ENOMEM ;
msg - > hdr . size = size ;
msg - > num_elems = num_buffer_id ;
msg - > hdr . cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE ;
memcpy ( & msg - > buffer_id [ 0 ] , buffer_id , size - sizeof ( * msg ) ) ;
ret = sof_client_ipc_tx_message ( cdev , msg , & reply , sizeof ( reply ) ) ;
kfree ( msg ) ;
return ret ;
}
static int sof_probes_compr_startup ( struct snd_compr_stream * cstream ,
struct snd_soc_dai * dai )
{
struct snd_soc_card * card = snd_soc_component_get_drvdata ( dai - > component ) ;
struct sof_client_dev * cdev = snd_soc_card_get_drvdata ( card ) ;
struct sof_probes_priv * priv = cdev - > data ;
const struct sof_probes_host_ops * ops = priv - > host_ops ;
int ret ;
if ( sof_client_get_fw_state ( cdev ) = = SOF_FW_CRASHED )
return - ENODEV ;
ret = sof_client_core_module_get ( cdev ) ;
if ( ret )
return ret ;
ret = ops - > assign ( cdev , cstream , dai , & priv - > extractor_stream_tag ) ;
if ( ret ) {
dev_err ( dai - > dev , " Failed to assign probe stream: %d \n " , ret ) ;
priv - > extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID ;
sof_client_core_module_put ( cdev ) ;
}
return ret ;
}
static int sof_probes_compr_shutdown ( struct snd_compr_stream * cstream ,
struct snd_soc_dai * dai )
{
struct snd_soc_card * card = snd_soc_component_get_drvdata ( dai - > component ) ;
struct sof_client_dev * cdev = snd_soc_card_get_drvdata ( card ) ;
struct sof_probes_priv * priv = cdev - > data ;
const struct sof_probes_host_ops * ops = priv - > host_ops ;
struct sof_probe_point_desc * desc ;
size_t num_desc ;
int i , ret ;
/* disconnect all probe points */
ret = sof_probes_points_info ( cdev , & desc , & num_desc ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev , " Failed to get probe points: %d \n " , ret ) ;
goto exit ;
}
for ( i = 0 ; i < num_desc ; i + + )
sof_probes_points_remove ( cdev , & desc [ i ] . buffer_id , 1 ) ;
kfree ( desc ) ;
exit :
ret = sof_probes_deinit ( cdev ) ;
if ( ret < 0 )
dev_err ( dai - > dev , " Failed to deinit probe: %d \n " , ret ) ;
priv - > extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID ;
snd_compr_free_pages ( cstream ) ;
ret = ops - > free ( cdev , cstream , dai ) ;
sof_client_core_module_put ( cdev ) ;
return ret ;
}
static int sof_probes_compr_set_params ( struct snd_compr_stream * cstream ,
struct snd_compr_params * params ,
struct snd_soc_dai * dai )
{
struct snd_soc_card * card = snd_soc_component_get_drvdata ( dai - > component ) ;
struct sof_client_dev * cdev = snd_soc_card_get_drvdata ( card ) ;
struct snd_compr_runtime * rtd = cstream - > runtime ;
struct sof_probes_priv * priv = cdev - > data ;
const struct sof_probes_host_ops * ops = priv - > host_ops ;
int ret ;
cstream - > dma_buffer . dev . type = SNDRV_DMA_TYPE_DEV_SG ;
cstream - > dma_buffer . dev . dev = sof_client_get_dma_dev ( cdev ) ;
ret = snd_compr_malloc_pages ( cstream , rtd - > buffer_size ) ;
if ( ret < 0 )
return ret ;
ret = ops - > set_params ( cdev , cstream , params , dai ) ;
if ( ret )
return ret ;
ret = sof_probes_init ( cdev , priv - > extractor_stream_tag , rtd - > dma_bytes ) ;
if ( ret < 0 ) {
dev_err ( dai - > dev , " Failed to init probe: %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
static int sof_probes_compr_trigger ( struct snd_compr_stream * cstream , int cmd ,
struct snd_soc_dai * dai )
{
struct snd_soc_card * card = snd_soc_component_get_drvdata ( dai - > component ) ;
struct sof_client_dev * cdev = snd_soc_card_get_drvdata ( card ) ;
struct sof_probes_priv * priv = cdev - > data ;
const struct sof_probes_host_ops * ops = priv - > host_ops ;
return ops - > trigger ( cdev , cstream , cmd , dai ) ;
}
static int sof_probes_compr_pointer ( struct snd_compr_stream * cstream ,
struct snd_compr_tstamp * tstamp ,
struct snd_soc_dai * dai )
{
struct snd_soc_card * card = snd_soc_component_get_drvdata ( dai - > component ) ;
struct sof_client_dev * cdev = snd_soc_card_get_drvdata ( card ) ;
struct sof_probes_priv * priv = cdev - > data ;
const struct sof_probes_host_ops * ops = priv - > host_ops ;
return ops - > pointer ( cdev , cstream , tstamp , dai ) ;
}
static const struct snd_soc_cdai_ops sof_probes_compr_ops = {
. startup = sof_probes_compr_startup ,
. shutdown = sof_probes_compr_shutdown ,
. set_params = sof_probes_compr_set_params ,
. trigger = sof_probes_compr_trigger ,
. pointer = sof_probes_compr_pointer ,
} ;
static int sof_probes_compr_copy ( struct snd_soc_component * component ,
struct snd_compr_stream * cstream ,
char __user * buf , size_t count )
{
struct snd_compr_runtime * rtd = cstream - > runtime ;
unsigned int offset , n ;
void * ptr ;
int ret ;
if ( count > rtd - > buffer_size )
count = rtd - > buffer_size ;
div_u64_rem ( rtd - > total_bytes_transferred , rtd - > buffer_size , & offset ) ;
ptr = rtd - > dma_area + offset ;
n = rtd - > buffer_size - offset ;
if ( count < n ) {
ret = copy_to_user ( buf , ptr , count ) ;
} else {
ret = copy_to_user ( buf , ptr , n ) ;
ret + = copy_to_user ( buf + n , rtd - > dma_area , count - n ) ;
}
if ( ret )
return count - ret ;
return count ;
}
static const struct snd_compress_ops sof_probes_compressed_ops = {
. copy = sof_probes_compr_copy ,
} ;
/**
* strsplit_u32 - Split string into sequence of u32 tokens
* @ buf : String to split into tokens .
* @ delim : String containing delimiter characters .
* @ tkns : Returned u32 sequence pointer .
* @ num_tkns : Returned number of tokens obtained .
*/
static int strsplit_u32 ( char * buf , const char * delim , u32 * * tkns , size_t * num_tkns )
{
char * s ;
u32 * data , * tmp ;
size_t count = 0 ;
size_t cap = 32 ;
int ret = 0 ;
* tkns = NULL ;
* num_tkns = 0 ;
data = kcalloc ( cap , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
while ( ( s = strsep ( & buf , delim ) ) ! = NULL ) {
ret = kstrtouint ( s , 0 , data + count ) ;
if ( ret )
goto exit ;
if ( + + count > = cap ) {
cap * = 2 ;
tmp = krealloc ( data , cap * sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! tmp ) {
ret = - ENOMEM ;
goto exit ;
}
data = tmp ;
}
}
if ( ! count )
goto exit ;
* tkns = kmemdup ( data , count * sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! ( * tkns ) ) {
ret = - ENOMEM ;
goto exit ;
}
* num_tkns = count ;
exit :
kfree ( data ) ;
return ret ;
}
static int tokenize_input ( const char __user * from , size_t count ,
loff_t * ppos , u32 * * tkns , size_t * num_tkns )
{
char * buf ;
int ret ;
buf = kmalloc ( count + 1 , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
ret = simple_write_to_buffer ( buf , count , ppos , from , count ) ;
if ( ret ! = count ) {
ret = ret > = 0 ? - EIO : ret ;
goto exit ;
}
buf [ count ] = ' \0 ' ;
ret = strsplit_u32 ( buf , " , " , tkns , num_tkns ) ;
exit :
kfree ( buf ) ;
return ret ;
}
static ssize_t sof_probes_dfs_points_read ( struct file * file , char __user * to ,
size_t count , loff_t * ppos )
{
struct sof_client_dev * cdev = file - > private_data ;
struct sof_probes_priv * priv = cdev - > data ;
struct device * dev = & cdev - > auxdev . dev ;
struct sof_probe_point_desc * desc ;
int remaining , offset ;
size_t num_desc ;
char * buf ;
int i , ret , err ;
if ( priv - > extractor_stream_tag = = SOF_PROBES_INVALID_NODE_ID ) {
dev_warn ( dev , " no extractor stream running \n " ) ;
return - ENOENT ;
}
buf = kzalloc ( PAGE_SIZE , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
2022-04-20 03:03:15 +00:00
ret = pm_runtime_resume_and_get ( dev ) ;
2022-02-10 17:05:25 +02:00
if ( ret < 0 & & ret ! = - EACCES ) {
dev_err_ratelimited ( dev , " debugfs read failed to resume %d \n " , ret ) ;
goto exit ;
}
ret = sof_probes_points_info ( cdev , & desc , & num_desc ) ;
if ( ret < 0 )
goto exit ;
pm_runtime_mark_last_busy ( dev ) ;
err = pm_runtime_put_autosuspend ( dev ) ;
if ( err < 0 )
dev_err_ratelimited ( dev , " debugfs read failed to idle %d \n " , err ) ;
for ( i = 0 ; i < num_desc ; i + + ) {
offset = strlen ( buf ) ;
remaining = PAGE_SIZE - offset ;
ret = snprintf ( buf + offset , remaining ,
" Id: %#010x Purpose: %u Node id: %#x \n " ,
desc [ i ] . buffer_id , desc [ i ] . purpose , desc [ i ] . stream_tag ) ;
if ( ret < 0 | | ret > = remaining ) {
/* truncate the output buffer at the last full line */
buf [ offset ] = ' \0 ' ;
break ;
}
}
ret = simple_read_from_buffer ( to , count , ppos , buf , strlen ( buf ) ) ;
kfree ( desc ) ;
exit :
kfree ( buf ) ;
return ret ;
}
static ssize_t
sof_probes_dfs_points_write ( struct file * file , const char __user * from ,
size_t count , loff_t * ppos )
{
struct sof_client_dev * cdev = file - > private_data ;
struct sof_probes_priv * priv = cdev - > data ;
struct device * dev = & cdev - > auxdev . dev ;
struct sof_probe_point_desc * desc ;
size_t num_tkns , bytes ;
u32 * tkns ;
int ret , err ;
if ( priv - > extractor_stream_tag = = SOF_PROBES_INVALID_NODE_ID ) {
dev_warn ( dev , " no extractor stream running \n " ) ;
return - ENOENT ;
}
ret = tokenize_input ( from , count , ppos , & tkns , & num_tkns ) ;
if ( ret < 0 )
return ret ;
bytes = sizeof ( * tkns ) * num_tkns ;
if ( ! num_tkns | | ( bytes % sizeof ( * desc ) ) ) {
ret = - EINVAL ;
goto exit ;
}
desc = ( struct sof_probe_point_desc * ) tkns ;
2022-04-20 03:03:15 +00:00
ret = pm_runtime_resume_and_get ( dev ) ;
2022-02-10 17:05:25 +02:00
if ( ret < 0 & & ret ! = - EACCES ) {
dev_err_ratelimited ( dev , " debugfs write failed to resume %d \n " , ret ) ;
goto exit ;
}
ret = sof_probes_points_add ( cdev , desc , bytes / sizeof ( * desc ) ) ;
if ( ! ret )
ret = count ;
pm_runtime_mark_last_busy ( dev ) ;
err = pm_runtime_put_autosuspend ( dev ) ;
if ( err < 0 )
dev_err_ratelimited ( dev , " debugfs write failed to idle %d \n " , err ) ;
exit :
kfree ( tkns ) ;
return ret ;
}
static const struct file_operations sof_probes_points_fops = {
. open = simple_open ,
. read = sof_probes_dfs_points_read ,
. write = sof_probes_dfs_points_write ,
. llseek = default_llseek ,
. owner = THIS_MODULE ,
} ;
static ssize_t
sof_probes_dfs_points_remove_write ( struct file * file , const char __user * from ,
size_t count , loff_t * ppos )
{
struct sof_client_dev * cdev = file - > private_data ;
struct sof_probes_priv * priv = cdev - > data ;
struct device * dev = & cdev - > auxdev . dev ;
size_t num_tkns ;
u32 * tkns ;
int ret , err ;
if ( priv - > extractor_stream_tag = = SOF_PROBES_INVALID_NODE_ID ) {
dev_warn ( dev , " no extractor stream running \n " ) ;
return - ENOENT ;
}
ret = tokenize_input ( from , count , ppos , & tkns , & num_tkns ) ;
if ( ret < 0 )
return ret ;
if ( ! num_tkns ) {
ret = - EINVAL ;
goto exit ;
}
2022-04-20 03:03:15 +00:00
ret = pm_runtime_resume_and_get ( dev ) ;
2022-02-10 17:05:25 +02:00
if ( ret < 0 ) {
dev_err_ratelimited ( dev , " debugfs write failed to resume %d \n " , ret ) ;
goto exit ;
}
ret = sof_probes_points_remove ( cdev , tkns , num_tkns ) ;
if ( ! ret )
ret = count ;
pm_runtime_mark_last_busy ( dev ) ;
err = pm_runtime_put_autosuspend ( dev ) ;
if ( err < 0 )
dev_err_ratelimited ( dev , " debugfs write failed to idle %d \n " , err ) ;
exit :
kfree ( tkns ) ;
return ret ;
}
static const struct file_operations sof_probes_points_remove_fops = {
. open = simple_open ,
. write = sof_probes_dfs_points_remove_write ,
. llseek = default_llseek ,
. owner = THIS_MODULE ,
} ;
static struct snd_soc_dai_driver sof_probes_dai_drv [ ] = {
{
. name = " Probe Extraction CPU DAI " ,
. compress_new = snd_soc_new_compress ,
. cops = & sof_probes_compr_ops ,
. capture = {
. stream_name = " Probe Extraction " ,
. channels_min = 1 ,
. channels_max = 8 ,
. rates = SNDRV_PCM_RATE_48000 ,
. rate_min = 48000 ,
. rate_max = 48000 ,
} ,
} ,
} ;
static const struct snd_soc_component_driver sof_probes_component = {
. name = " sof-probes-component " ,
. compress_ops = & sof_probes_compressed_ops ,
. module_get_upon_open = 1 ,
2022-06-23 13:51:43 +01:00
. legacy_dai_naming = 1 ,
2022-02-10 17:05:25 +02:00
} ;
SND_SOC_DAILINK_DEF ( dummy , DAILINK_COMP_ARRAY ( COMP_DUMMY ( ) ) ) ;
static int sof_probes_client_probe ( struct auxiliary_device * auxdev ,
const struct auxiliary_device_id * id )
{
struct sof_client_dev * cdev = auxiliary_dev_to_sof_client_dev ( auxdev ) ;
struct dentry * dfsroot = sof_client_get_debugfs_root ( cdev ) ;
struct device * dev = & auxdev - > dev ;
struct snd_soc_dai_link_component platform_component [ ] = {
{
. name = dev_name ( dev ) ,
}
} ;
struct snd_soc_card * card ;
struct sof_probes_priv * priv ;
struct snd_soc_dai_link_component * cpus ;
struct sof_probes_host_ops * ops ;
struct snd_soc_dai_link * links ;
int ret ;
/* do not set up the probes support if it is not enabled */
if ( ! sof_probes_enabled )
return - ENXIO ;
2022-07-12 16:10:22 +03:00
/* only ipc3 is supported */
if ( sof_client_get_ipc_type ( cdev ) ! = SOF_IPC )
return - ENXIO ;
2022-02-10 17:05:25 +02:00
if ( ! dev - > platform_data ) {
dev_err ( dev , " missing platform data \n " ) ;
return - ENODEV ;
}
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
ops = dev - > platform_data ;
if ( ! ops - > assign | | ! ops - > free | | ! ops - > set_params | | ! ops - > trigger | |
! ops - > pointer ) {
dev_err ( dev , " missing platform callback(s) \n " ) ;
return - ENODEV ;
}
priv - > host_ops = ops ;
cdev - > data = priv ;
/* register probes component driver and dai */
ret = devm_snd_soc_register_component ( dev , & sof_probes_component ,
sof_probes_dai_drv ,
ARRAY_SIZE ( sof_probes_dai_drv ) ) ;
if ( ret < 0 ) {
dev_err ( dev , " failed to register SOF probes DAI driver %d \n " , ret ) ;
return ret ;
}
/* set client data */
priv - > extractor_stream_tag = SOF_PROBES_INVALID_NODE_ID ;
/* create read-write probes_points debugfs entry */
priv - > dfs_points = debugfs_create_file ( " probe_points " , 0644 , dfsroot ,
cdev , & sof_probes_points_fops ) ;
/* create read-write probe_points_remove debugfs entry */
priv - > dfs_points_remove = debugfs_create_file ( " probe_points_remove " , 0644 ,
dfsroot , cdev ,
& sof_probes_points_remove_fops ) ;
links = devm_kcalloc ( dev , SOF_PROBES_NUM_DAI_LINKS , sizeof ( * links ) , GFP_KERNEL ) ;
cpus = devm_kcalloc ( dev , SOF_PROBES_NUM_DAI_LINKS , sizeof ( * cpus ) , GFP_KERNEL ) ;
if ( ! links | | ! cpus ) {
debugfs_remove ( priv - > dfs_points ) ;
debugfs_remove ( priv - > dfs_points_remove ) ;
return - ENOMEM ;
}
/* extraction DAI link */
links [ 0 ] . name = " Compress Probe Capture " ;
links [ 0 ] . id = 0 ;
links [ 0 ] . cpus = & cpus [ 0 ] ;
links [ 0 ] . num_cpus = 1 ;
links [ 0 ] . cpus - > dai_name = " Probe Extraction CPU DAI " ;
links [ 0 ] . codecs = dummy ;
links [ 0 ] . num_codecs = 1 ;
links [ 0 ] . platforms = platform_component ;
links [ 0 ] . num_platforms = ARRAY_SIZE ( platform_component ) ;
links [ 0 ] . nonatomic = 1 ;
card = & priv - > card ;
card - > dev = dev ;
card - > name = " sof-probes " ;
card - > owner = THIS_MODULE ;
card - > num_links = SOF_PROBES_NUM_DAI_LINKS ;
card - > dai_link = links ;
/* set idle_bias_off to prevent the core from resuming the card->dev */
card - > dapm . idle_bias_off = true ;
snd_soc_card_set_drvdata ( card , cdev ) ;
ret = devm_snd_soc_register_card ( dev , card ) ;
if ( ret < 0 ) {
debugfs_remove ( priv - > dfs_points ) ;
debugfs_remove ( priv - > dfs_points_remove ) ;
dev_err ( dev , " Probes card register failed %d \n " , ret ) ;
return ret ;
}
/* enable runtime PM */
pm_runtime_set_autosuspend_delay ( dev , SOF_PROBES_SUSPEND_DELAY_MS ) ;
pm_runtime_use_autosuspend ( dev ) ;
pm_runtime_enable ( dev ) ;
pm_runtime_mark_last_busy ( dev ) ;
pm_runtime_idle ( dev ) ;
return 0 ;
}
static void sof_probes_client_remove ( struct auxiliary_device * auxdev )
{
struct sof_client_dev * cdev = auxiliary_dev_to_sof_client_dev ( auxdev ) ;
struct sof_probes_priv * priv = cdev - > data ;
if ( ! sof_probes_enabled )
return ;
pm_runtime_disable ( & auxdev - > dev ) ;
debugfs_remove ( priv - > dfs_points ) ;
debugfs_remove ( priv - > dfs_points_remove ) ;
}
static const struct auxiliary_device_id sof_probes_client_id_table [ ] = {
{ . name = " snd_sof.hda-probes " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( auxiliary , sof_probes_client_id_table ) ;
/* driver name will be set based on KBUILD_MODNAME */
static struct auxiliary_driver sof_probes_client_drv = {
. probe = sof_probes_client_probe ,
. remove = sof_probes_client_remove ,
. id_table = sof_probes_client_id_table ,
} ;
module_auxiliary_driver ( sof_probes_client_drv ) ;
MODULE_DESCRIPTION ( " SOF Probes Client Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_IMPORT_NS ( SND_SOC_SOF_CLIENT ) ;