@ -325,3 +325,451 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
return 0 ;
}
/*
* Mixer module represents a pipeline . So in the Pre - PMU event of mixer we
* need create the pipeline . So we do following :
* - check the resources
* - Create the pipeline
* - Initialize the modules in pipeline
* - finally bind all modules together
*/
static int skl_tplg_mixer_dapm_pre_pmu_event ( struct snd_soc_dapm_widget * w ,
struct skl * skl )
{
int ret ;
struct skl_module_cfg * mconfig = w - > priv ;
struct skl_pipe_module * w_module ;
struct skl_pipe * s_pipe = mconfig - > pipe ;
struct skl_module_cfg * src_module = NULL , * dst_module ;
struct skl_sst * ctx = skl - > skl_sst ;
/* check resource available */
if ( ! skl_tplg_alloc_pipe_mcps ( skl , mconfig ) )
return - EBUSY ;
if ( ! skl_tplg_alloc_pipe_mem ( skl , mconfig ) )
return - ENOMEM ;
/*
* Create a list of modules for pipe .
* This list contains modules from source to sink
*/
ret = skl_create_pipeline ( ctx , mconfig - > pipe ) ;
if ( ret < 0 )
return ret ;
/*
* we create a w_list of all widgets in that pipe . This list is not
* freed on PMD event as widgets within a pipe are static . This
* saves us cycles to get widgets in pipe every time .
*
* So if we have already initialized all the widgets of a pipeline
* we skip , so check for list_empty and create the list if empty
*/
if ( list_empty ( & s_pipe - > w_list ) ) {
ret = skl_tplg_alloc_pipe_widget ( ctx - > dev , w , s_pipe ) ;
if ( ret < 0 )
return ret ;
}
/* Init all pipe modules from source to sink */
ret = skl_tplg_init_pipe_modules ( skl , s_pipe ) ;
if ( ret < 0 )
return ret ;
/* Bind modules from source to sink */
list_for_each_entry ( w_module , & s_pipe - > w_list , node ) {
dst_module = w_module - > w - > priv ;
if ( src_module = = NULL ) {
src_module = dst_module ;
continue ;
}
ret = skl_bind_modules ( ctx , src_module , dst_module ) ;
if ( ret < 0 )
return ret ;
src_module = dst_module ;
}
return 0 ;
}
/*
* A PGA represents a module in a pipeline . So in the Pre - PMU event of PGA
* we need to do following :
* - Bind to sink pipeline
* Since the sink pipes can be running and we don ' t get mixer event on
* connect for already running mixer , we need to find the sink pipes
* here and bind to them . This way dynamic connect works .
* - Start sink pipeline , if not running
* - Then run current pipe
*/
static int skl_tplg_pga_dapm_pre_pmu_event ( struct snd_soc_dapm_widget * w ,
struct skl * skl )
{
struct snd_soc_dapm_path * p ;
struct skl_dapm_path_list * path_list ;
struct snd_soc_dapm_widget * source , * sink ;
struct skl_module_cfg * src_mconfig , * sink_mconfig ;
struct skl_sst * ctx = skl - > skl_sst ;
int ret = 0 ;
source = w ;
src_mconfig = source - > priv ;
/*
* find which sink it is connected to , bind with the sink ,
* if sink is not started , start sink pipe first , then start
* this pipe
*/
snd_soc_dapm_widget_for_each_source_path ( w , p ) {
if ( ! p - > connect )
continue ;
dev_dbg ( ctx - > dev , " %s: src widget=%s \n " , __func__ , w - > name ) ;
dev_dbg ( ctx - > dev , " %s: sink widget=%s \n " , __func__ , p - > sink - > name ) ;
/*
* here we will check widgets in sink pipelines , so that
* can be any widgets type and we are only interested if
* they are ones used for SKL so check that first
*/
if ( ( p - > sink - > priv ! = NULL ) & &
is_skl_dsp_widget_type ( p - > sink ) ) {
sink = p - > sink ;
src_mconfig = source - > priv ;
sink_mconfig = sink - > priv ;
/* Bind source to sink, mixin is always source */
ret = skl_bind_modules ( ctx , src_mconfig , sink_mconfig ) ;
if ( ret )
return ret ;
/* Start sinks pipe first */
if ( sink_mconfig - > pipe - > state ! = SKL_PIPE_STARTED ) {
ret = skl_run_pipe ( ctx , sink_mconfig - > pipe ) ;
if ( ret )
return ret ;
}
path_list = kzalloc (
sizeof ( struct skl_dapm_path_list ) ,
GFP_KERNEL ) ;
if ( path_list = = NULL )
return - ENOMEM ;
/* Add connected path to one global list */
path_list - > dapm_path = p ;
list_add_tail ( & path_list - > node , & skl - > dapm_path_list ) ;
break ;
}
}
/* Start source pipe last after starting all sinks */
ret = skl_run_pipe ( ctx , src_mconfig - > pipe ) ;
if ( ret )
return ret ;
return 0 ;
}
/*
* in the Post - PMU event of mixer we need to do following :
* - Check if this pipe is running
* - if not , then
* - bind this pipeline to its source pipeline
* if source pipe is already running , this means it is a dynamic
* connection and we need to bind only to that pipe
* - start this pipeline
*/
static int skl_tplg_mixer_dapm_post_pmu_event ( struct snd_soc_dapm_widget * w ,
struct skl * skl )
{
int ret = 0 ;
struct snd_soc_dapm_path * p ;
struct snd_soc_dapm_widget * source , * sink ;
struct skl_module_cfg * src_mconfig , * sink_mconfig ;
struct skl_sst * ctx = skl - > skl_sst ;
int src_pipe_started = 0 ;
sink = w ;
sink_mconfig = sink - > priv ;
/*
* If source pipe is already started , that means source is driving
* one more sink before this sink got connected , Since source is
* started , bind this sink to source and start this pipe .
*/
snd_soc_dapm_widget_for_each_sink_path ( w , p ) {
if ( ! p - > connect )
continue ;
dev_dbg ( ctx - > dev , " sink widget=%s \n " , w - > name ) ;
dev_dbg ( ctx - > dev , " src widget=%s \n " , p - > source - > name ) ;
/*
* here we will check widgets in sink pipelines , so that
* can be any widgets type and we are only interested if
* they are ones used for SKL so check that first
*/
if ( ( p - > source - > priv ! = NULL ) & &
is_skl_dsp_widget_type ( p - > source ) ) {
source = p - > source ;
src_mconfig = source - > priv ;
sink_mconfig = sink - > priv ;
src_pipe_started = 1 ;
/*
* check pipe state , then no need to bind or start
* the pipe
*/
if ( src_mconfig - > pipe - > state ! = SKL_PIPE_STARTED )
src_pipe_started = 0 ;
}
}
if ( src_pipe_started ) {
ret = skl_bind_modules ( ctx , src_mconfig , sink_mconfig ) ;
if ( ret )
return ret ;
ret = skl_run_pipe ( ctx , sink_mconfig - > pipe ) ;
}
return ret ;
}
/*
* in the Pre - PMD event of mixer we need to do following :
* - Stop the pipe
* - find the source connections and remove that from dapm_path_list
* - unbind with source pipelines if still connected
*/
static int skl_tplg_mixer_dapm_pre_pmd_event ( struct snd_soc_dapm_widget * w ,
struct skl * skl )
{
struct snd_soc_dapm_widget * source , * sink ;
struct skl_module_cfg * src_mconfig , * sink_mconfig ;
int ret = 0 , path_found = 0 ;
struct skl_dapm_path_list * path_list , * tmp_list ;
struct skl_sst * ctx = skl - > skl_sst ;
sink = w ;
sink_mconfig = sink - > priv ;
/* Stop the pipe */
ret = skl_stop_pipe ( ctx , sink_mconfig - > pipe ) ;
if ( ret )
return ret ;
/*
* This list , dapm_path_list handling here does not need any locks
* as we are under dapm lock while handling widget events .
* List can be manipulated safely only under dapm widgets handler
* routines
*/
list_for_each_entry_safe ( path_list , tmp_list ,
& skl - > dapm_path_list , node ) {
if ( path_list - > dapm_path - > sink = = sink ) {
dev_dbg ( ctx - > dev , " Path found = %s \n " ,
path_list - > dapm_path - > name ) ;
source = path_list - > dapm_path - > source ;
src_mconfig = source - > priv ;
path_found = 1 ;
list_del ( & path_list - > node ) ;
kfree ( path_list ) ;
break ;
}
}
/*
* If path_found = = 1 , that means pmd for source pipe has
* not occurred , source is connected to some other sink .
* so its responsibility of sink to unbind itself from source .
*/
if ( path_found ) {
ret = skl_stop_pipe ( ctx , src_mconfig - > pipe ) ;
if ( ret < 0 )
return ret ;
ret = skl_unbind_modules ( ctx , src_mconfig , sink_mconfig ) ;
}
return ret ;
}
/*
* in the Post - PMD event of mixer we need to do following :
* - Free the mcps used
* - Free the mem used
* - Unbind the modules within the pipeline
* - Delete the pipeline ( modules are not required to be explicitly
* deleted , pipeline delete is enough here
*/
static int skl_tplg_mixer_dapm_post_pmd_event ( struct snd_soc_dapm_widget * w ,
struct skl * skl )
{
struct skl_module_cfg * mconfig = w - > priv ;
struct skl_pipe_module * w_module ;
struct skl_module_cfg * src_module = NULL , * dst_module ;
struct skl_sst * ctx = skl - > skl_sst ;
struct skl_pipe * s_pipe = mconfig - > pipe ;
int ret = 0 ;
skl_tplg_free_pipe_mcps ( skl , mconfig ) ;
list_for_each_entry ( w_module , & s_pipe - > w_list , node ) {
dst_module = w_module - > w - > priv ;
if ( src_module = = NULL ) {
src_module = dst_module ;
continue ;
}
ret = skl_unbind_modules ( ctx , src_module , dst_module ) ;
if ( ret < 0 )
return ret ;
src_module = dst_module ;
}
ret = skl_delete_pipe ( ctx , mconfig - > pipe ) ;
skl_tplg_free_pipe_mem ( skl , mconfig ) ;
return ret ;
}
/*
* in the Post - PMD event of PGA we need to do following :
* - Free the mcps used
* - Stop the pipeline
* - In source pipe is connected , unbind with source pipelines
*/
static int skl_tplg_pga_dapm_post_pmd_event ( struct snd_soc_dapm_widget * w ,
struct skl * skl )
{
struct snd_soc_dapm_widget * source , * sink ;
struct skl_module_cfg * src_mconfig , * sink_mconfig ;
int ret = 0 , path_found = 0 ;
struct skl_dapm_path_list * path_list , * tmp_path_list ;
struct skl_sst * ctx = skl - > skl_sst ;
source = w ;
src_mconfig = source - > priv ;
skl_tplg_free_pipe_mcps ( skl , src_mconfig ) ;
/* Stop the pipe since this is a mixin module */
ret = skl_stop_pipe ( ctx , src_mconfig - > pipe ) ;
if ( ret )
return ret ;
list_for_each_entry_safe ( path_list , tmp_path_list , & skl - > dapm_path_list , node ) {
if ( path_list - > dapm_path - > source = = source ) {
dev_dbg ( ctx - > dev , " Path found = %s \n " ,
path_list - > dapm_path - > name ) ;
sink = path_list - > dapm_path - > sink ;
sink_mconfig = sink - > priv ;
path_found = 1 ;
list_del ( & path_list - > node ) ;
kfree ( path_list ) ;
break ;
}
}
/*
* This is a connector and if path is found that means
* unbind between source and sink has not happened yet
*/
if ( path_found ) {
ret = skl_stop_pipe ( ctx , src_mconfig - > pipe ) ;
if ( ret < 0 )
return ret ;
ret = skl_unbind_modules ( ctx , src_mconfig , sink_mconfig ) ;
}
return ret ;
}
/*
* In modelling , we assume there will be ONLY one mixer in a pipeline . If
* mixer is not required then it is treated as static mixer aka vmixer with
* a hard path to source module
* So we don ' t need to check if source is started or not as hard path puts
* dependency on each other
*/
static int skl_tplg_vmixer_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * k , int event )
{
struct snd_soc_dapm_context * dapm = w - > dapm ;
struct skl * skl = get_skl_ctx ( dapm - > dev ) ;
switch ( event ) {
case SND_SOC_DAPM_PRE_PMU :
return skl_tplg_mixer_dapm_pre_pmu_event ( w , skl ) ;
case SND_SOC_DAPM_POST_PMD :
return skl_tplg_mixer_dapm_post_pmd_event ( w , skl ) ;
}
return 0 ;
}
/*
* In modelling , we assume there will be ONLY one mixer in a pipeline . If a
* second one is required that is created as another pipe entity .
* The mixer is responsible for pipe management and represent a pipeline
* instance
*/
static int skl_tplg_mixer_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * k , int event )
{
struct snd_soc_dapm_context * dapm = w - > dapm ;
struct skl * skl = get_skl_ctx ( dapm - > dev ) ;
switch ( event ) {
case SND_SOC_DAPM_PRE_PMU :
return skl_tplg_mixer_dapm_pre_pmu_event ( w , skl ) ;
case SND_SOC_DAPM_POST_PMU :
return skl_tplg_mixer_dapm_post_pmu_event ( w , skl ) ;
case SND_SOC_DAPM_PRE_PMD :
return skl_tplg_mixer_dapm_pre_pmd_event ( w , skl ) ;
case SND_SOC_DAPM_POST_PMD :
return skl_tplg_mixer_dapm_post_pmd_event ( w , skl ) ;
}
return 0 ;
}
/*
* In modelling , we assumed rest of the modules in pipeline are PGA . But we
* are interested in last PGA ( leaf PGA ) in a pipeline to disconnect with
* the sink when it is running ( two FE to one BE or one FE to two BE )
* scenarios
*/
static int skl_tplg_pga_event ( struct snd_soc_dapm_widget * w ,
struct snd_kcontrol * k , int event )
{
struct snd_soc_dapm_context * dapm = w - > dapm ;
struct skl * skl = get_skl_ctx ( dapm - > dev ) ;
switch ( event ) {
case SND_SOC_DAPM_PRE_PMU :
return skl_tplg_pga_dapm_pre_pmu_event ( w , skl ) ;
case SND_SOC_DAPM_POST_PMD :
return skl_tplg_pga_dapm_post_pmd_event ( w , skl ) ;
}
return 0 ;
}