@@ -2057,6 +2057,201 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
20572057 return ret ;
20582058}
20592059
2060+ static int sof_ipc3_set_up_all_pipelines (struct snd_sof_dev * sdev , bool verify )
2061+ {
2062+ struct sof_ipc_fw_version * v = & sdev -> fw_ready .version ;
2063+ struct snd_sof_widget * swidget ;
2064+ struct snd_sof_route * sroute ;
2065+ int ret ;
2066+
2067+ /* restore pipeline components */
2068+ list_for_each_entry (swidget , & sdev -> widget_list , list ) {
2069+ /* only set up the widgets belonging to static pipelines */
2070+ if (!verify && swidget -> dynamic_pipeline_widget )
2071+ continue ;
2072+
2073+ /*
2074+ * For older firmware, skip scheduler widgets in this loop,
2075+ * sof_widget_setup() will be called in the 'complete pipeline' loop
2076+ */
2077+ if (v -> abi_version < SOF_ABI_VER (3 , 19 , 0 ) &&
2078+ swidget -> id == snd_soc_dapm_scheduler )
2079+ continue ;
2080+
2081+ /* update DAI config. The IPC will be sent in sof_widget_setup() */
2082+ if (WIDGET_IS_DAI (swidget -> id )) {
2083+ struct snd_sof_dai * dai = swidget -> private ;
2084+ struct sof_dai_private_data * private ;
2085+ struct sof_ipc_dai_config * config ;
2086+
2087+ if (!dai || !dai -> private )
2088+ continue ;
2089+ private = dai -> private ;
2090+ if (!private -> dai_config )
2091+ continue ;
2092+
2093+ config = private -> dai_config ;
2094+ /*
2095+ * The link DMA channel would be invalidated for running
2096+ * streams but not for streams that were in the PAUSED
2097+ * state during suspend. So invalidate it here before setting
2098+ * the dai config in the DSP.
2099+ */
2100+ if (config -> type == SOF_DAI_INTEL_HDA )
2101+ config -> hda .link_dma_ch = DMA_CHAN_INVALID ;
2102+ }
2103+
2104+ ret = sof_widget_setup (sdev , swidget );
2105+ if (ret < 0 )
2106+ return ret ;
2107+ }
2108+
2109+ /* restore pipeline connections */
2110+ list_for_each_entry (sroute , & sdev -> route_list , list ) {
2111+ /* only set up routes belonging to static pipelines */
2112+ if (!verify && (sroute -> src_widget -> dynamic_pipeline_widget ||
2113+ sroute -> sink_widget -> dynamic_pipeline_widget ))
2114+ continue ;
2115+
2116+ /*
2117+ * For virtual routes, both sink and source are not buffer. IPC3 only supports
2118+ * connections between a buffer and a component. Ignore the rest.
2119+ */
2120+ if (sroute -> src_widget -> id != snd_soc_dapm_buffer &&
2121+ sroute -> sink_widget -> id != snd_soc_dapm_buffer )
2122+ continue ;
2123+
2124+ ret = sof_route_setup (sdev , sroute -> src_widget -> widget ,
2125+ sroute -> sink_widget -> widget );
2126+ if (ret < 0 ) {
2127+ dev_err (sdev -> dev , "%s: route set up failed\n" , __func__ );
2128+ return ret ;
2129+ }
2130+ }
2131+
2132+ /* complete pipeline */
2133+ list_for_each_entry (swidget , & sdev -> widget_list , list ) {
2134+ switch (swidget -> id ) {
2135+ case snd_soc_dapm_scheduler :
2136+ /* only complete static pipelines */
2137+ if (!verify && swidget -> dynamic_pipeline_widget )
2138+ continue ;
2139+
2140+ if (v -> abi_version < SOF_ABI_VER (3 , 19 , 0 )) {
2141+ ret = sof_widget_setup (sdev , swidget );
2142+ if (ret < 0 )
2143+ return ret ;
2144+ }
2145+
2146+ swidget -> complete = sof_ipc3_complete_pipeline (sdev , swidget );
2147+ if (swidget -> complete < 0 )
2148+ return swidget -> complete ;
2149+ break ;
2150+ default :
2151+ break ;
2152+ }
2153+ }
2154+
2155+ return 0 ;
2156+ }
2157+
2158+ /*
2159+ * Free the PCM, its associated widgets and set the prepared flag to false for all PCMs that
2160+ * did not get suspended(ex: paused streams) so the widgets can be set up again during resume.
2161+ */
2162+ static int sof_tear_down_left_over_pipelines (struct snd_sof_dev * sdev )
2163+ {
2164+ struct snd_sof_widget * swidget ;
2165+ struct snd_sof_pcm * spcm ;
2166+ int dir , ret ;
2167+
2168+ /*
2169+ * free all PCMs and their associated DAPM widgets if their connected DAPM widget
2170+ * list is not NULL. This should only be true for paused streams at this point.
2171+ * This is equivalent to the handling of FE DAI suspend trigger for running streams.
2172+ */
2173+ list_for_each_entry (spcm , & sdev -> pcm_list , list ) {
2174+ for_each_pcm_streams (dir ) {
2175+ struct snd_pcm_substream * substream = spcm -> stream [dir ].substream ;
2176+
2177+ if (!substream || !substream -> runtime )
2178+ continue ;
2179+
2180+ if (spcm -> stream [dir ].list ) {
2181+ ret = sof_pcm_stream_free (sdev , substream , spcm , dir , true);
2182+ if (ret < 0 )
2183+ return ret ;
2184+ }
2185+ }
2186+ }
2187+
2188+ /*
2189+ * free any left over DAI widgets. This is equivalent to the handling of suspend trigger
2190+ * for the BE DAI for running streams.
2191+ */
2192+ list_for_each_entry (swidget , & sdev -> widget_list , list )
2193+ if (WIDGET_IS_DAI (swidget -> id ) && swidget -> use_count == 1 ) {
2194+ ret = sof_widget_free (sdev , swidget );
2195+ if (ret < 0 )
2196+ return ret ;
2197+ }
2198+
2199+ return 0 ;
2200+ }
2201+
2202+ /*
2203+ * For older firmware, this function doesn't free widgets for static pipelines during suspend.
2204+ * It only resets use_count for all widgets.
2205+ */
2206+ static int sof_ipc3_tear_down_all_pipelines (struct snd_sof_dev * sdev , bool verify )
2207+ {
2208+ struct sof_ipc_fw_version * v = & sdev -> fw_ready .version ;
2209+ struct snd_sof_widget * swidget ;
2210+ struct snd_sof_route * sroute ;
2211+ int ret ;
2212+
2213+ /*
2214+ * This function is called during suspend and for one-time topology verification during
2215+ * first boot. In both cases, there is no need to protect swidget->use_count and
2216+ * sroute->setup because during suspend all running streams are suspended and during
2217+ * topology loading the sound card unavailable to open PCMs.
2218+ */
2219+ list_for_each_entry (swidget , & sdev -> widget_list , list ) {
2220+ if (swidget -> dynamic_pipeline_widget )
2221+ continue ;
2222+
2223+ /* Do not free widgets for static pipelines with FW ABI older than 3.19 */
2224+ if (!verify && !swidget -> dynamic_pipeline_widget &&
2225+ v -> abi_version < SOF_ABI_VER (3 , 19 , 0 )) {
2226+ swidget -> use_count = 0 ;
2227+ swidget -> complete = 0 ;
2228+ continue ;
2229+ }
2230+
2231+ ret = sof_widget_free (sdev , swidget );
2232+ if (ret < 0 )
2233+ return ret ;
2234+ }
2235+
2236+ /*
2237+ * Tear down all pipelines associated with PCMs that did not get suspended
2238+ * and unset the prepare flag so that they can be set up again during resume.
2239+ * Skip this step for older firmware.
2240+ */
2241+ if (!verify && v -> abi_version >= SOF_ABI_VER (3 , 19 , 0 )) {
2242+ ret = sof_tear_down_left_over_pipelines (sdev );
2243+ if (ret < 0 ) {
2244+ dev_err (sdev -> dev , "failed to tear down paused pipelines\n" );
2245+ return ret ;
2246+ }
2247+ }
2248+
2249+ list_for_each_entry (sroute , & sdev -> route_list , list )
2250+ sroute -> setup = false;
2251+
2252+ return 0 ;
2253+ }
2254+
20602255/* token list for each topology object */
20612256static enum sof_tokens host_token_list [] = {
20622257 SOF_CORE_TOKENS ,
@@ -2164,4 +2359,6 @@ const struct sof_ipc_tplg_ops ipc3_tplg_ops = {
21642359 .widget_free = sof_ipc3_widget_free ,
21652360 .widget_setup = sof_ipc3_widget_setup ,
21662361 .dai_config = sof_ipc3_dai_config ,
2362+ .set_up_all_pipelines = sof_ipc3_set_up_all_pipelines ,
2363+ .tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines ,
21672364};
0 commit comments