2020-05-01 09:58:50 -05:00
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2019-12-04 15:15:51 -06:00
//
// 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) 2019 Intel Corporation. All rights reserved.
//
// Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
//
2021-10-04 12:14:28 -05:00
# include <linux/bitfield.h>
2019-12-04 15:15:51 -06:00
# include "sof-audio.h"
# include "ops.h"
2021-09-27 15:05:09 +03:00
static int sof_kcontrol_setup ( struct snd_sof_dev * sdev , struct snd_sof_control * scontrol )
{
int ret ;
/* reset readback offset for scontrol */
scontrol - > readback_offset = 0 ;
2021-12-15 10:04:03 -08:00
ret = snd_sof_ipc_set_get_comp_data ( scontrol , true ) ;
2021-09-27 15:05:09 +03:00
if ( ret < 0 )
dev_err ( sdev - > dev , " error: failed kcontrol value set for widget: %d \n " ,
scontrol - > comp_id ) ;
return ret ;
}
static int sof_dai_config_setup ( struct snd_sof_dev * sdev , struct snd_sof_dai * dai )
{
2022-03-08 08:43:43 -08:00
struct sof_dai_private_data * private = dai - > private ;
2021-09-27 15:05:09 +03:00
struct sof_ipc_dai_config * config ;
struct sof_ipc_reply reply ;
int ret ;
2022-03-08 08:43:43 -08:00
config = & private - > dai_config [ dai - > current_config ] ;
2021-09-27 15:05:09 +03:00
if ( ! config ) {
dev_err ( sdev - > dev , " error: no config for DAI %s \n " , dai - > name ) ;
return - EINVAL ;
}
2021-10-04 12:14:28 -05:00
/* set NONE flag to clear all previous settings */
2021-11-25 12:15:19 +02:00
config - > flags = SOF_DAI_CONFIG_FLAGS_NONE ;
2021-10-04 12:14:28 -05:00
2021-09-27 15:05:09 +03:00
ret = sof_ipc_tx_message ( sdev - > ipc , config - > hdr . cmd , config , config - > hdr . size ,
& reply , sizeof ( reply ) ) ;
if ( ret < 0 )
dev_err ( sdev - > dev , " error: failed to set dai config for %s \n " , dai - > name ) ;
return ret ;
}
2021-09-27 15:05:12 +03:00
static int sof_widget_kcontrol_setup ( struct snd_sof_dev * sdev , struct snd_sof_widget * swidget )
{
struct snd_sof_control * scontrol ;
int ret ;
/* set up all controls for the widget */
list_for_each_entry ( scontrol , & sdev - > kcontrol_list , list )
if ( scontrol - > comp_id = = swidget - > comp_id ) {
2021-12-15 10:04:04 -08:00
/* set kcontrol data in DSP */
2021-09-27 15:05:12 +03:00
ret = sof_kcontrol_setup ( sdev , scontrol ) ;
if ( ret < 0 ) {
dev_err ( sdev - > dev , " error: fail to set up kcontrols for widget %s \n " ,
swidget - > widget - > name ) ;
return ret ;
}
2021-12-15 10:04:04 -08:00
/*
* Read back the data from the DSP for static widgets . This is particularly
* useful for binary kcontrols associated with static pipeline widgets to
* initialize the data size to match that in the DSP .
*/
if ( swidget - > dynamic_pipeline_widget )
continue ;
ret = snd_sof_ipc_set_get_comp_data ( scontrol , false ) ;
if ( ret < 0 )
dev_warn ( sdev - > dev , " Failed kcontrol get for control in widget %s \n " ,
swidget - > widget - > name ) ;
2021-09-27 15:05:12 +03:00
}
return 0 ;
}
2021-09-27 15:05:16 +03:00
static void sof_reset_route_setup_status ( struct snd_sof_dev * sdev , struct snd_sof_widget * widget )
{
struct snd_sof_route * sroute ;
list_for_each_entry ( sroute , & sdev - > route_list , list )
if ( sroute - > src_widget = = widget | | sroute - > sink_widget = = widget )
sroute - > setup = false ;
}
2021-09-27 15:05:14 +03:00
int sof_widget_free ( struct snd_sof_dev * sdev , struct snd_sof_widget * swidget )
{
struct sof_ipc_free ipc_free = {
. hdr = {
. size = sizeof ( ipc_free ) ,
. cmd = SOF_IPC_GLB_TPLG_MSG ,
} ,
. id = swidget - > comp_id ,
} ;
struct sof_ipc_reply reply ;
2022-03-07 10:10:56 -08:00
int ret , ret1 ;
2021-09-27 15:05:14 +03:00
if ( ! swidget - > private )
return 0 ;
/* only free when use_count is 0 */
if ( - - swidget - > use_count )
return 0 ;
switch ( swidget - > id ) {
case snd_soc_dapm_scheduler :
2021-11-19 21:26:20 +02:00
{
2021-09-27 15:05:14 +03:00
ipc_free . hdr . cmd | = SOF_IPC_TPLG_PIPE_FREE ;
break ;
2021-11-19 21:26:20 +02:00
}
2021-09-27 15:05:14 +03:00
case snd_soc_dapm_buffer :
ipc_free . hdr . cmd | = SOF_IPC_TPLG_BUFFER_FREE ;
break ;
2021-11-23 19:16:06 +02:00
case snd_soc_dapm_dai_in :
case snd_soc_dapm_dai_out :
{
struct snd_sof_dai * dai = swidget - > private ;
dai - > configured = false ;
fallthrough ;
}
2021-09-27 15:05:14 +03:00
default :
ipc_free . hdr . cmd | = SOF_IPC_TPLG_COMP_FREE ;
break ;
}
2021-11-19 21:26:20 +02:00
/* continue to disable core even if IPC fails */
2021-09-27 15:05:14 +03:00
ret = sof_ipc_tx_message ( sdev - > ipc , ipc_free . hdr . cmd , & ipc_free , sizeof ( ipc_free ) ,
& reply , sizeof ( reply ) ) ;
2021-11-19 21:26:20 +02:00
if ( ret < 0 )
2021-09-27 15:05:14 +03:00
dev_err ( sdev - > dev , " error: failed to free widget %s \n " , swidget - > widget - > name ) ;
2021-11-19 21:26:20 +02:00
/*
* disable widget core . continue to route setup status and complete flag
* even if this fails and return the appropriate error
*/
2022-03-07 10:10:56 -08:00
ret1 = snd_sof_dsp_core_put ( sdev , swidget - > core ) ;
2021-11-19 21:26:20 +02:00
if ( ret1 < 0 ) {
dev_err ( sdev - > dev , " error: failed to disable target core: %d for widget %s \n " ,
2022-03-07 10:10:56 -08:00
swidget - > core , swidget - > widget - > name ) ;
2021-11-19 21:26:20 +02:00
if ( ! ret )
ret = ret1 ;
2021-09-27 15:05:14 +03:00
}
2021-09-27 15:05:16 +03:00
/* reset route setup status for all routes that contain this widget */
sof_reset_route_setup_status ( sdev , swidget ) ;
2021-09-27 15:05:14 +03:00
swidget - > complete = 0 ;
2021-11-19 21:26:20 +02:00
if ( ! ret )
dev_dbg ( sdev - > dev , " widget %s freed \n " , swidget - > widget - > name ) ;
return ret ;
2021-09-27 15:05:14 +03:00
}
EXPORT_SYMBOL ( sof_widget_free ) ;
int sof_widget_setup ( struct snd_sof_dev * sdev , struct snd_sof_widget * swidget )
2021-09-27 15:05:09 +03:00
{
struct sof_ipc_pipe_new * pipeline ;
struct sof_ipc_comp_reply r ;
struct sof_ipc_cmd_hdr * hdr ;
struct sof_ipc_comp * comp ;
struct snd_sof_dai * dai ;
int ret ;
/* skip if there is no private data */
if ( ! swidget - > private )
return 0 ;
2021-09-27 15:05:14 +03:00
/* widget already set up */
if ( + + swidget - > use_count > 1 )
return 0 ;
2021-11-19 21:26:20 +02:00
/* enable widget core */
2022-03-07 10:10:56 -08:00
ret = snd_sof_dsp_core_get ( sdev , swidget - > core ) ;
2021-09-27 15:05:09 +03:00
if ( ret < 0 ) {
2021-11-19 21:26:20 +02:00
dev_err ( sdev - > dev , " error: failed to enable target core for widget %s \n " ,
swidget - > widget - > name ) ;
2021-09-27 15:05:14 +03:00
goto use_count_dec ;
2021-09-27 15:05:09 +03:00
}
switch ( swidget - > id ) {
case snd_soc_dapm_dai_in :
case snd_soc_dapm_dai_out :
2022-03-08 08:43:43 -08:00
{
struct sof_dai_private_data * dai_data ;
2021-09-27 15:05:09 +03:00
dai = swidget - > private ;
2022-03-08 08:43:43 -08:00
dai_data = dai - > private ;
comp = & dai_data - > comp_dai - > comp ;
2021-09-27 15:05:15 +03:00
dai - > configured = false ;
2021-09-27 15:05:09 +03:00
2022-03-08 08:43:43 -08:00
ret = sof_ipc_tx_message ( sdev - > ipc , comp - > hdr . cmd , dai_data - > comp_dai ,
comp - > hdr . size , & r , sizeof ( r ) ) ;
2021-09-27 15:05:16 +03:00
if ( ret < 0 ) {
dev_err ( sdev - > dev , " error: failed to load widget %s \n " ,
swidget - > widget - > name ) ;
2021-11-19 21:26:20 +02:00
goto core_put ;
2021-09-27 15:05:16 +03:00
}
ret = sof_dai_config_setup ( sdev , dai ) ;
if ( ret < 0 ) {
dev_err ( sdev - > dev , " error: failed to load dai config for DAI %s \n " ,
swidget - > widget - > name ) ;
2021-11-19 21:26:20 +02:00
/*
* widget use_count and core ref_count will both be decremented by
* sof_widget_free ( )
*/
2021-09-27 15:05:16 +03:00
sof_widget_free ( sdev , swidget ) ;
return ret ;
}
2021-09-27 15:05:09 +03:00
break ;
2022-03-08 08:43:43 -08:00
}
2021-09-27 15:05:09 +03:00
case snd_soc_dapm_scheduler :
pipeline = swidget - > private ;
2021-11-19 21:26:17 +02:00
ret = sof_ipc_tx_message ( sdev - > ipc , pipeline - > hdr . cmd , pipeline ,
sizeof ( * pipeline ) , & r , sizeof ( r ) ) ;
2021-09-27 15:05:09 +03:00
break ;
default :
hdr = swidget - > private ;
ret = sof_ipc_tx_message ( sdev - > ipc , hdr - > cmd , swidget - > private , hdr - > size ,
& r , sizeof ( r ) ) ;
break ;
}
2021-09-27 15:05:12 +03:00
if ( ret < 0 ) {
2021-09-27 15:05:09 +03:00
dev_err ( sdev - > dev , " error: failed to load widget %s \n " , swidget - > widget - > name ) ;
2021-11-19 21:26:20 +02:00
goto core_put ;
2021-09-27 15:05:12 +03:00
}
/* restore kcontrols for widget */
ret = sof_widget_kcontrol_setup ( sdev , swidget ) ;
if ( ret < 0 ) {
dev_err ( sdev - > dev , " error: failed to restore kcontrols for widget %s \n " ,
swidget - > widget - > name ) ;
2021-11-19 21:26:20 +02:00
/*
* widget use_count and core ref_count will both be decremented by
* sof_widget_free ( )
*/
2021-09-27 15:05:16 +03:00
sof_widget_free ( sdev , swidget ) ;
2021-09-27 15:05:12 +03:00
return ret ;
}
dev_dbg ( sdev - > dev , " widget %s setup complete \n " , swidget - > widget - > name ) ;
2021-09-27 15:05:09 +03:00
2021-09-27 15:05:14 +03:00
return 0 ;
2021-11-19 21:26:20 +02:00
core_put :
2022-03-07 10:10:56 -08:00
snd_sof_dsp_core_put ( sdev , swidget - > core ) ;
2021-09-27 15:05:14 +03:00
use_count_dec :
swidget - > use_count - - ;
2021-09-27 15:05:09 +03:00
return ret ;
}
2021-09-27 15:05:14 +03:00
EXPORT_SYMBOL ( sof_widget_setup ) ;
2021-09-27 15:05:09 +03:00
2021-09-27 15:05:16 +03:00
static int sof_route_setup_ipc ( struct snd_sof_dev * sdev , struct snd_sof_route * sroute )
{
2022-03-08 08:43:38 -08:00
struct sof_ipc_pipe_comp_connect connect ;
2021-09-27 15:05:16 +03:00
struct sof_ipc_reply reply ;
int ret ;
/* nothing to do if route is already set up */
if ( sroute - > setup )
return 0 ;
2022-03-08 08:43:38 -08:00
connect . hdr . size = sizeof ( connect ) ;
connect . hdr . cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_CONNECT ;
connect . source_id = sroute - > src_widget - > comp_id ;
connect . sink_id = sroute - > sink_widget - > comp_id ;
2021-09-27 15:05:16 +03:00
dev_dbg ( sdev - > dev , " setting up route %s -> %s \n " ,
sroute - > src_widget - > widget - > name ,
sroute - > sink_widget - > widget - > name ) ;
/* send ipc */
2022-03-08 08:43:38 -08:00
ret = sof_ipc_tx_message ( sdev - > ipc , connect . hdr . cmd , & connect , sizeof ( connect ) ,
2021-09-27 15:05:16 +03:00
& reply , sizeof ( reply ) ) ;
if ( ret < 0 ) {
dev_err ( sdev - > dev , " %s: route setup failed %d \n " , __func__ , ret ) ;
return ret ;
}
sroute - > setup = true ;
return 0 ;
}
static int sof_route_setup ( struct snd_sof_dev * sdev , struct snd_soc_dapm_widget * wsource ,
struct snd_soc_dapm_widget * wsink )
{
struct snd_sof_widget * src_widget = wsource - > dobj . private ;
struct snd_sof_widget * sink_widget = wsink - > dobj . private ;
struct snd_sof_route * sroute ;
bool route_found = false ;
/* ignore routes involving virtual widgets in topology */
switch ( src_widget - > id ) {
case snd_soc_dapm_out_drv :
case snd_soc_dapm_output :
case snd_soc_dapm_input :
return 0 ;
default :
break ;
}
switch ( sink_widget - > id ) {
case snd_soc_dapm_out_drv :
case snd_soc_dapm_output :
case snd_soc_dapm_input :
return 0 ;
default :
break ;
}
/* find route matching source and sink widgets */
list_for_each_entry ( sroute , & sdev - > route_list , list )
if ( sroute - > src_widget = = src_widget & & sroute - > sink_widget = = sink_widget ) {
route_found = true ;
break ;
}
if ( ! route_found ) {
dev_err ( sdev - > dev , " error: cannot find SOF route for source %s -> %s sink \n " ,
wsource - > name , wsink - > name ) ;
return - EINVAL ;
}
return sof_route_setup_ipc ( sdev , sroute ) ;
}
static int sof_setup_pipeline_connections ( struct snd_sof_dev * sdev ,
struct snd_soc_dapm_widget_list * list , int dir )
{
struct snd_soc_dapm_widget * widget ;
struct snd_soc_dapm_path * p ;
int ret ;
int i ;
/*
* Set up connections between widgets in the sink / source paths based on direction .
* Some non - SOF widgets exist in topology either for compatibility or for the
* purpose of connecting a pipeline from a host to a DAI in order to receive the DAPM
* events . But they are not handled by the firmware . So ignore them .
*/
if ( dir = = SNDRV_PCM_STREAM_PLAYBACK ) {
for_each_dapm_widgets ( list , i , widget ) {
if ( ! widget - > dobj . private )
continue ;
snd_soc_dapm_widget_for_each_sink_path ( widget , p )
if ( p - > sink - > dobj . private ) {
ret = sof_route_setup ( sdev , widget , p - > sink ) ;
if ( ret < 0 )
return ret ;
}
}
} else {
for_each_dapm_widgets ( list , i , widget ) {
if ( ! widget - > dobj . private )
continue ;
snd_soc_dapm_widget_for_each_source_path ( widget , p )
if ( p - > source - > dobj . private ) {
ret = sof_route_setup ( sdev , p - > source , widget ) ;
if ( ret < 0 )
return ret ;
}
}
}
return 0 ;
}
int sof_widget_list_setup ( struct snd_sof_dev * sdev , struct snd_sof_pcm * spcm , int dir )
{
struct snd_soc_dapm_widget_list * list = spcm - > stream [ dir ] . list ;
struct snd_soc_dapm_widget * widget ;
int i , ret , num_widgets ;
/* nothing to set up */
if ( ! list )
return 0 ;
/* set up widgets in the list */
for_each_dapm_widgets ( list , num_widgets , widget ) {
struct snd_sof_widget * swidget = widget - > dobj . private ;
struct snd_sof_widget * pipe_widget ;
if ( ! swidget )
continue ;
/*
* The scheduler widget for a pipeline is not part of the connected DAPM
* widget list and it needs to be set up before the widgets in the pipeline
* are set up . The use_count for the scheduler widget is incremented for every
* widget in a given pipeline to ensure that it is freed only after the last
* widget in the pipeline is freed .
*/
pipe_widget = swidget - > pipe_widget ;
if ( ! pipe_widget ) {
dev_err ( sdev - > dev , " error: no pipeline widget found for %s \n " ,
swidget - > widget - > name ) ;
ret = - EINVAL ;
goto widget_free ;
}
ret = sof_widget_setup ( sdev , pipe_widget ) ;
if ( ret < 0 )
goto widget_free ;
/* set up the widget */
ret = sof_widget_setup ( sdev , swidget ) ;
if ( ret < 0 ) {
sof_widget_free ( sdev , pipe_widget ) ;
goto widget_free ;
}
}
/*
* error in setting pipeline connections will result in route status being reset for
* routes that were successfully set up when the widgets are freed .
*/
ret = sof_setup_pipeline_connections ( sdev , list , dir ) ;
if ( ret < 0 )
goto widget_free ;
/* complete pipelines */
for_each_dapm_widgets ( list , i , widget ) {
struct snd_sof_widget * swidget = widget - > dobj . private ;
struct snd_sof_widget * pipe_widget ;
if ( ! swidget )
continue ;
pipe_widget = swidget - > pipe_widget ;
if ( ! pipe_widget ) {
dev_err ( sdev - > dev , " error: no pipeline widget found for %s \n " ,
swidget - > widget - > name ) ;
ret = - EINVAL ;
goto widget_free ;
}
if ( pipe_widget - > complete )
continue ;
2021-10-06 14:16:51 +03:00
pipe_widget - > complete = snd_sof_complete_pipeline ( sdev , pipe_widget ) ;
2021-09-27 15:05:16 +03:00
if ( pipe_widget - > complete < 0 ) {
ret = pipe_widget - > complete ;
goto widget_free ;
}
}
return 0 ;
widget_free :
/* free all widgets that have been set up successfully */
for_each_dapm_widgets ( list , i , widget ) {
struct snd_sof_widget * swidget = widget - > dobj . private ;
if ( ! swidget )
continue ;
if ( ! num_widgets - - )
break ;
sof_widget_free ( sdev , swidget ) ;
sof_widget_free ( sdev , swidget - > pipe_widget ) ;
}
return ret ;
}
int sof_widget_list_free ( struct snd_sof_dev * sdev , struct snd_sof_pcm * spcm , int dir )
{
struct snd_soc_dapm_widget_list * list = spcm - > stream [ dir ] . list ;
struct snd_soc_dapm_widget * widget ;
int i , ret ;
int ret1 = 0 ;
/* nothing to free */
if ( ! list )
return 0 ;
/*
* Free widgets in the list . This can fail but continue freeing other widgets to keep
* use_counts balanced .
*/
for_each_dapm_widgets ( list , i , widget ) {
struct snd_sof_widget * swidget = widget - > dobj . private ;
if ( ! swidget )
continue ;
/*
* free widget and its pipe_widget . Either of these can fail , but free as many as
* possible before freeing the list and returning the error .
*/
ret = sof_widget_free ( sdev , swidget ) ;
if ( ret < 0 )
ret1 = ret ;
ret = sof_widget_free ( sdev , swidget - > pipe_widget ) ;
if ( ret < 0 )
ret1 = ret ;
}
snd_soc_dapm_dai_free_widgets ( & list ) ;
spcm - > stream [ dir ] . list = NULL ;
return ret1 ;
}
2020-01-29 16:07:23 -06:00
/*
* helper to determine if there are only D0i3 compatible
* streams active
*/
bool snd_sof_dsp_only_d0i3_compatible_stream_active ( struct snd_sof_dev * sdev )
{
struct snd_pcm_substream * substream ;
struct snd_sof_pcm * spcm ;
bool d0i3_compatible_active = false ;
int dir ;
list_for_each_entry ( spcm , & sdev - > pcm_list , list ) {
2020-02-17 17:28:47 +09:00
for_each_pcm_streams ( dir ) {
2020-01-29 16:07:23 -06:00
substream = spcm - > stream [ dir ] . substream ;
if ( ! substream | | ! substream - > runtime )
continue ;
/*
2020-08-07 18:21:56 -07:00
* substream - > runtime being not NULL indicates
2020-01-29 16:07:23 -06:00
* that the stream is open . No need to check the
* stream state .
*/
if ( ! spcm - > stream [ dir ] . d0i3_compatible )
return false ;
d0i3_compatible_active = true ;
}
}
return d0i3_compatible_active ;
}
EXPORT_SYMBOL ( snd_sof_dsp_only_d0i3_compatible_stream_active ) ;
2020-01-29 16:07:21 -06:00
bool snd_sof_stream_suspend_ignored ( struct snd_sof_dev * sdev )
2019-12-04 15:15:51 -06:00
{
struct snd_sof_pcm * spcm ;
list_for_each_entry ( spcm , & sdev - > pcm_list , list ) {
if ( spcm - > stream [ SNDRV_PCM_STREAM_PLAYBACK ] . suspend_ignored | |
spcm - > stream [ SNDRV_PCM_STREAM_CAPTURE ] . suspend_ignored )
return true ;
}
return false ;
}
int sof_set_hw_params_upon_resume ( struct device * dev )
{
struct snd_sof_dev * sdev = dev_get_drvdata ( dev ) ;
struct snd_pcm_substream * substream ;
struct snd_sof_pcm * spcm ;
snd_pcm_state_t state ;
int dir ;
/*
* SOF requires hw_params to be set - up internally upon resume .
* So , set the flag to indicate this for those streams that
* have been suspended .
*/
list_for_each_entry ( spcm , & sdev - > pcm_list , list ) {
2020-02-17 17:28:47 +09:00
for_each_pcm_streams ( dir ) {
2020-01-29 16:07:18 -06:00
/*
* do not reset hw_params upon resume for streams that
* were kept running during suspend
*/
if ( spcm - > stream [ dir ] . suspend_ignored )
continue ;
2019-12-04 15:15:51 -06:00
substream = spcm - > stream [ dir ] . substream ;
if ( ! substream | | ! substream - > runtime )
continue ;
state = substream - > runtime - > status - > state ;
if ( state = = SNDRV_PCM_STATE_SUSPENDED )
spcm - > prepared [ dir ] = false ;
}
}
/* set internal flag for BE */
return snd_sof_dsp_hw_params_upon_resume ( sdev ) ;
}
2021-10-06 14:16:51 +03:00
int sof_set_up_pipelines ( struct snd_sof_dev * sdev , bool verify )
2019-12-04 15:15:51 -06:00
{
2021-11-23 19:16:04 +02:00
struct sof_ipc_fw_version * v = & sdev - > fw_ready . version ;
2019-12-04 15:15:51 -06:00
struct snd_sof_widget * swidget ;
struct snd_sof_route * sroute ;
int ret ;
/* restore pipeline components */
2021-11-23 19:16:04 +02:00
list_for_each_entry ( swidget , & sdev - > widget_list , list ) {
2021-09-27 15:05:16 +03:00
/* only set up the widgets belonging to static pipelines */
2021-09-27 15:05:17 +03:00
if ( ! verify & & swidget - > dynamic_pipeline_widget )
2021-09-27 15:05:16 +03:00
continue ;
2021-11-23 19:16:04 +02:00
/*
* For older firmware , skip scheduler widgets in this loop ,
* sof_widget_setup ( ) will be called in the ' complete pipeline ' loop
*/
if ( v - > abi_version < SOF_ABI_VER ( 3 , 19 , 0 ) & &
swidget - > id = = snd_soc_dapm_scheduler )
continue ;
2021-09-27 15:05:16 +03:00
/* update DAI config. The IPC will be sent in sof_widget_setup() */
if ( WIDGET_IS_DAI ( swidget - > id ) ) {
struct snd_sof_dai * dai = swidget - > private ;
2022-03-08 08:43:43 -08:00
struct sof_dai_private_data * private = dai - > private ;
2021-09-27 15:05:16 +03:00
struct sof_ipc_dai_config * config ;
2022-03-08 08:43:43 -08:00
if ( ! dai | | ! private | | ! private - > dai_config )
2021-09-27 15:05:16 +03:00
continue ;
2022-03-08 08:43:43 -08:00
config = private - > dai_config ;
2021-09-27 15:05:16 +03:00
/*
* The link DMA channel would be invalidated for running
* streams but not for streams that were in the PAUSED
* state during suspend . So invalidate it here before setting
* the dai config in the DSP .
*/
if ( config - > type = = SOF_DAI_INTEL_HDA )
config - > hda . link_dma_ch = DMA_CHAN_INVALID ;
}
2021-09-27 15:05:14 +03:00
2021-09-27 15:05:09 +03:00
ret = sof_widget_setup ( sdev , swidget ) ;
if ( ret < 0 )
2020-09-02 17:07:55 +03:00
return ret ;
2019-12-04 15:15:51 -06:00
}
/* restore pipeline connections */
2021-09-27 15:05:16 +03:00
list_for_each_entry ( sroute , & sdev - > route_list , list ) {
2019-12-04 15:15:51 -06:00
2021-09-27 15:05:16 +03:00
/* only set up routes belonging to static pipelines */
2021-09-27 15:05:17 +03:00
if ( ! verify & & ( sroute - > src_widget - > dynamic_pipeline_widget | |
sroute - > sink_widget - > dynamic_pipeline_widget ) )
2019-12-04 15:15:51 -06:00
continue ;
2021-09-27 15:05:16 +03:00
ret = sof_route_setup_ipc ( sdev , sroute ) ;
2019-12-04 15:15:51 -06:00
if ( ret < 0 ) {
2021-09-27 15:05:16 +03:00
dev_err ( sdev - > dev , " %s: restore pipeline connections failed \n " , __func__ ) ;
2019-12-04 15:15:51 -06:00
return ret ;
}
}
/* complete pipeline */
list_for_each_entry ( swidget , & sdev - > widget_list , list ) {
switch ( swidget - > id ) {
case snd_soc_dapm_scheduler :
2021-09-27 15:05:16 +03:00
/* only complete static pipelines */
2021-09-27 15:05:17 +03:00
if ( ! verify & & swidget - > dynamic_pipeline_widget )
2021-09-27 15:05:16 +03:00
continue ;
2021-11-23 19:16:04 +02:00
if ( v - > abi_version < SOF_ABI_VER ( 3 , 19 , 0 ) ) {
ret = sof_widget_setup ( sdev , swidget ) ;
if ( ret < 0 )
return ret ;
}
2019-12-04 15:15:51 -06:00
swidget - > complete =
2021-10-06 14:16:51 +03:00
snd_sof_complete_pipeline ( sdev , swidget ) ;
2019-12-04 15:15:51 -06:00
break ;
default :
break ;
}
}
2021-09-27 15:05:12 +03:00
return 0 ;
2019-12-04 15:15:51 -06:00
}
2021-11-25 12:15:16 +02:00
int sof_pcm_stream_free ( struct snd_sof_dev * sdev , struct snd_pcm_substream * substream ,
struct snd_sof_pcm * spcm , int dir , bool free_widget_list )
{
int ret ;
/* Send PCM_FREE IPC to reset pipeline */
ret = sof_pcm_dsp_pcm_free ( substream , sdev , spcm ) ;
if ( ret < 0 )
return ret ;
/* stop the DMA */
ret = snd_sof_pcm_platform_hw_free ( sdev , substream ) ;
if ( ret < 0 )
return ret ;
/* free widget list */
if ( free_widget_list ) {
ret = sof_widget_list_free ( sdev , spcm , dir ) ;
if ( ret < 0 )
dev_err ( sdev - > dev , " failed to free widgets during suspend \n " ) ;
}
return ret ;
}
2021-11-23 19:16:06 +02:00
/*
* Free the PCM , its associated widgets and set the prepared flag to false for all PCMs that
* did not get suspended ( ex : paused streams ) so the widgets can be set up again during resume .
*/
static int sof_tear_down_left_over_pipelines ( struct snd_sof_dev * sdev )
{
struct snd_sof_widget * swidget ;
struct snd_sof_pcm * spcm ;
int dir , ret ;
/*
* free all PCMs and their associated DAPM widgets if their connected DAPM widget
* list is not NULL . This should only be true for paused streams at this point .
* This is equivalent to the handling of FE DAI suspend trigger for running streams .
*/
list_for_each_entry ( spcm , & sdev - > pcm_list , list )
for_each_pcm_streams ( dir ) {
struct snd_pcm_substream * substream = spcm - > stream [ dir ] . substream ;
if ( ! substream | | ! substream - > runtime )
continue ;
if ( spcm - > stream [ dir ] . list ) {
2021-11-25 12:15:16 +02:00
ret = sof_pcm_stream_free ( sdev , substream , spcm , dir , true ) ;
2021-11-25 12:15:15 +02:00
if ( ret < 0 )
return ret ;
2021-11-23 19:16:06 +02:00
}
}
/*
* free any left over DAI widgets . This is equivalent to the handling of suspend trigger
* for the BE DAI for running streams .
*/
list_for_each_entry ( swidget , & sdev - > widget_list , list )
if ( WIDGET_IS_DAI ( swidget - > id ) & & swidget - > use_count = = 1 ) {
ret = sof_widget_free ( sdev , swidget ) ;
if ( ret < 0 )
return ret ;
}
return 0 ;
}
2021-09-27 15:05:14 +03:00
/*
2021-11-19 21:26:18 +02:00
* For older firmware , this function doesn ' t free widgets for static pipelines during suspend .
* It only resets use_count for all widgets .
2021-09-27 15:05:14 +03:00
*/
2021-10-06 14:16:51 +03:00
int sof_tear_down_pipelines ( struct snd_sof_dev * sdev , bool verify )
2021-09-27 15:05:11 +03:00
{
2021-11-19 21:26:18 +02:00
struct sof_ipc_fw_version * v = & sdev - > fw_ready . version ;
2021-09-27 15:05:14 +03:00
struct snd_sof_widget * swidget ;
2021-09-27 15:05:11 +03:00
struct snd_sof_route * sroute ;
2021-09-27 15:05:17 +03:00
int ret ;
2021-09-27 15:05:11 +03:00
/*
2021-09-27 15:05:17 +03:00
* This function is called during suspend and for one - time topology verification during
* first boot . In both cases , there is no need to protect swidget - > use_count and
2021-11-23 19:16:06 +02:00
* sroute - > setup because during suspend all running streams are suspended and during
* topology loading the sound card unavailable to open PCMs .
2021-09-27 15:05:11 +03:00
*/
2021-11-23 19:16:04 +02:00
list_for_each_entry ( swidget , & sdev - > widget_list , list ) {
2021-11-19 21:26:18 +02:00
if ( swidget - > dynamic_pipeline_widget )
continue ;
/* Do not free widgets for static pipelines with FW ABI older than 3.19 */
if ( ! verify & & ! swidget - > dynamic_pipeline_widget & &
v - > abi_version < SOF_ABI_VER ( 3 , 19 , 0 ) ) {
2021-09-27 15:05:17 +03:00
swidget - > use_count = 0 ;
2021-11-19 21:26:18 +02:00
swidget - > complete = 0 ;
2021-09-27 15:05:17 +03:00
continue ;
}
ret = sof_widget_free ( sdev , swidget ) ;
if ( ret < 0 )
return ret ;
}
2021-09-27 15:05:14 +03:00
2021-11-23 19:16:06 +02:00
/*
* Tear down all pipelines associated with PCMs that did not get suspended
* and unset the prepare flag so that they can be set up again during resume .
* Skip this step for older firmware .
*/
if ( ! verify & & v - > abi_version > = SOF_ABI_VER ( 3 , 19 , 0 ) ) {
ret = sof_tear_down_left_over_pipelines ( sdev ) ;
if ( ret < 0 ) {
dev_err ( sdev - > dev , " failed to tear down paused pipelines \n " ) ;
return ret ;
}
}
2021-09-27 15:05:11 +03:00
list_for_each_entry ( sroute , & sdev - > route_list , list )
sroute - > setup = false ;
2021-09-27 15:05:17 +03:00
return 0 ;
2021-09-27 15:05:11 +03:00
}
2019-12-04 15:15:51 -06:00
/*
* Generic object lookup APIs .
*/
struct snd_sof_pcm * snd_sof_find_spcm_name ( struct snd_soc_component * scomp ,
const char * name )
{
struct snd_sof_dev * sdev = snd_soc_component_get_drvdata ( scomp ) ;
struct snd_sof_pcm * spcm ;
list_for_each_entry ( spcm , & sdev - > pcm_list , list ) {
/* match with PCM dai name */
if ( strcmp ( spcm - > pcm . dai_name , name ) = = 0 )
return spcm ;
/* match with playback caps name if set */
if ( * spcm - > pcm . caps [ 0 ] . name & &
! strcmp ( spcm - > pcm . caps [ 0 ] . name , name ) )
return spcm ;
/* match with capture caps name if set */
if ( * spcm - > pcm . caps [ 1 ] . name & &
! strcmp ( spcm - > pcm . caps [ 1 ] . name , name ) )
return spcm ;
}
return NULL ;
}
struct snd_sof_pcm * snd_sof_find_spcm_comp ( struct snd_soc_component * scomp ,
unsigned int comp_id ,
int * direction )
{
struct snd_sof_dev * sdev = snd_soc_component_get_drvdata ( scomp ) ;
struct snd_sof_pcm * spcm ;
int dir ;
list_for_each_entry ( spcm , & sdev - > pcm_list , list ) {
2020-02-17 17:28:47 +09:00
for_each_pcm_streams ( dir ) {
if ( spcm - > stream [ dir ] . comp_id = = comp_id ) {
* direction = dir ;
return spcm ;
}
2019-12-04 15:15:51 -06:00
}
}
return NULL ;
}
struct snd_sof_widget * snd_sof_find_swidget ( struct snd_soc_component * scomp ,
const char * name )
{
struct snd_sof_dev * sdev = snd_soc_component_get_drvdata ( scomp ) ;
struct snd_sof_widget * swidget ;
list_for_each_entry ( swidget , & sdev - > widget_list , list ) {
if ( strcmp ( name , swidget - > widget - > name ) = = 0 )
return swidget ;
}
return NULL ;
}
/* find widget by stream name and direction */
struct snd_sof_widget *
snd_sof_find_swidget_sname ( struct snd_soc_component * scomp ,
const char * pcm_name , int dir )
{
struct snd_sof_dev * sdev = snd_soc_component_get_drvdata ( scomp ) ;
struct snd_sof_widget * swidget ;
enum snd_soc_dapm_type type ;
if ( dir = = SNDRV_PCM_STREAM_PLAYBACK )
type = snd_soc_dapm_aif_in ;
else
type = snd_soc_dapm_aif_out ;
list_for_each_entry ( swidget , & sdev - > widget_list , list ) {
if ( ! strcmp ( pcm_name , swidget - > widget - > sname ) & &
swidget - > id = = type )
return swidget ;
}
return NULL ;
}
struct snd_sof_dai * snd_sof_find_dai ( struct snd_soc_component * scomp ,
const char * name )
{
struct snd_sof_dev * sdev = snd_soc_component_get_drvdata ( scomp ) ;
struct snd_sof_dai * dai ;
list_for_each_entry ( dai , & sdev - > dai_list , list ) {
if ( dai - > name & & ( strcmp ( name , dai - > name ) = = 0 ) )
return dai ;
}
return NULL ;
}
2021-06-25 15:50:41 -05:00
# define SOF_DAI_CLK_INTEL_SSP_MCLK 0
# define SOF_DAI_CLK_INTEL_SSP_BCLK 1
static int sof_dai_get_clk ( struct snd_soc_pcm_runtime * rtd , int clk_type )
2021-03-19 14:49:49 +02:00
{
struct snd_soc_component * component =
snd_soc_rtdcom_lookup ( rtd , SOF_AUDIO_PCM_DRV_NAME ) ;
struct snd_sof_dai * dai =
snd_sof_find_dai ( component , ( char * ) rtd - > dai_link - > name ) ;
2022-03-08 08:43:43 -08:00
struct sof_dai_private_data * private = dai - > private ;
2021-03-19 14:49:49 +02:00
/* use the tplg configured mclk if existed */
2022-03-08 08:43:43 -08:00
if ( ! dai | | ! private | | ! private - > dai_config )
2021-03-19 14:49:49 +02:00
return 0 ;
2022-03-08 08:43:43 -08:00
switch ( private - > dai_config - > type ) {
2021-03-19 14:49:49 +02:00
case SOF_DAI_INTEL_SSP :
2021-06-25 15:50:41 -05:00
switch ( clk_type ) {
case SOF_DAI_CLK_INTEL_SSP_MCLK :
2022-03-08 08:43:43 -08:00
return private - > dai_config - > ssp . mclk_rate ;
2021-06-25 15:50:41 -05:00
case SOF_DAI_CLK_INTEL_SSP_BCLK :
2022-03-08 08:43:43 -08:00
return private - > dai_config - > ssp . bclk_rate ;
2021-06-25 15:50:41 -05:00
default :
dev_err ( rtd - > dev , " fail to get SSP clk %d rate \n " ,
clk_type ) ;
return - EINVAL ;
}
break ;
2021-03-19 14:49:49 +02:00
default :
/* not yet implemented for platforms other than the above */
2021-06-25 15:50:41 -05:00
dev_err ( rtd - > dev , " DAI type %d not supported yet! \n " ,
2022-03-08 08:43:43 -08:00
private - > dai_config - > type ) ;
2021-03-19 14:49:49 +02:00
return - EINVAL ;
}
}
2021-06-25 15:50:41 -05:00
/*
* Helper to get SSP MCLK from a pcm_runtime .
* Return 0 if not exist .
*/
int sof_dai_get_mclk ( struct snd_soc_pcm_runtime * rtd )
{
return sof_dai_get_clk ( rtd , SOF_DAI_CLK_INTEL_SSP_MCLK ) ;
}
2021-03-19 14:49:49 +02:00
EXPORT_SYMBOL ( sof_dai_get_mclk ) ;
2021-06-25 15:50:41 -05:00
/*
* Helper to get SSP BCLK from a pcm_runtime .
* Return 0 if not exist .
*/
int sof_dai_get_bclk ( struct snd_soc_pcm_runtime * rtd )
{
return sof_dai_get_clk ( rtd , SOF_DAI_CLK_INTEL_SSP_BCLK ) ;
}
EXPORT_SYMBOL ( sof_dai_get_bclk ) ;
2019-12-04 15:15:53 -06:00
/*
* SOF Driver enumeration .
*/
int sof_machine_check ( struct snd_sof_dev * sdev )
{
struct snd_sof_pdata * sof_pdata = sdev - > pdata ;
const struct sof_dev_desc * desc = sof_pdata - > desc ;
struct snd_soc_acpi_mach * mach ;
2021-04-09 15:01:21 -07:00
if ( ! IS_ENABLED ( CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE ) ) {
2019-12-04 15:15:53 -06:00
2021-04-09 15:01:21 -07:00
/* find machine */
2021-12-16 17:24:20 -06:00
mach = snd_sof_machine_select ( sdev ) ;
if ( mach ) {
sof_pdata - > machine = mach ;
snd_sof_set_mach_params ( mach , sdev ) ;
2021-04-09 15:01:21 -07:00
return 0 ;
}
if ( ! IS_ENABLED ( CONFIG_SND_SOC_SOF_NOCODEC ) ) {
dev_err ( sdev - > dev , " error: no matching ASoC machine driver found - aborting probe \n " ) ;
return - ENODEV ;
}
} else {
dev_warn ( sdev - > dev , " Force to use nocodec mode \n " ) ;
2019-12-04 15:15:53 -06:00
}
/* select nocodec mode */
dev_warn ( sdev - > dev , " Using nocodec machine driver \n " ) ;
mach = devm_kzalloc ( sdev - > dev , sizeof ( * mach ) , GFP_KERNEL ) ;
if ( ! mach )
return - ENOMEM ;
2019-12-04 15:15:56 -06:00
mach - > drv_name = " sof-nocodec " ;
sof_pdata - > tplg_filename = desc - > nocodec_tplg_filename ;
2019-12-04 15:15:53 -06:00
sof_pdata - > machine = mach ;
2021-12-16 17:24:20 -06:00
snd_sof_set_mach_params ( mach , sdev ) ;
2019-12-04 15:15:53 -06:00
return 0 ;
}
EXPORT_SYMBOL ( sof_machine_check ) ;
int sof_machine_register ( struct snd_sof_dev * sdev , void * pdata )
{
2020-09-17 13:56:31 +03:00
struct snd_sof_pdata * plat_data = pdata ;
2019-12-04 15:15:53 -06:00
const char * drv_name ;
const void * mach ;
int size ;
drv_name = plat_data - > machine - > drv_name ;
2020-09-17 13:56:31 +03:00
mach = plat_data - > machine ;
2019-12-04 15:15:53 -06:00
size = sizeof ( * plat_data - > machine ) ;
/* register machine driver, pass machine info as pdata */
plat_data - > pdev_mach =
platform_device_register_data ( sdev - > dev , drv_name ,
PLATFORM_DEVID_NONE , mach , size ) ;
if ( IS_ERR ( plat_data - > pdev_mach ) )
return PTR_ERR ( plat_data - > pdev_mach ) ;
dev_dbg ( sdev - > dev , " created machine %s \n " ,
dev_name ( & plat_data - > pdev_mach - > dev ) ) ;
return 0 ;
}
EXPORT_SYMBOL ( sof_machine_register ) ;
void sof_machine_unregister ( struct snd_sof_dev * sdev , void * pdata )
{
2020-09-17 13:56:31 +03:00
struct snd_sof_pdata * plat_data = pdata ;
2019-12-04 15:15:53 -06:00
if ( ! IS_ERR_OR_NULL ( plat_data - > pdev_mach ) )
platform_device_unregister ( plat_data - > pdev_mach ) ;
}
EXPORT_SYMBOL ( sof_machine_unregister ) ;