brintos

brintos / linux-shallow public Read only

0
0
Text · 27.1 KiB · 9a52781 Raw
1029 lines · c
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)2//3// This file is provided under a dual BSD/GPLv2 license.  When using or4// redistributing this file, you may do so under either license.5//6// Copyright(c) 2019 Intel Corporation7//8// Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>9//10 11#include <linux/bitfield.h>12#include <trace/events/sof.h>13#include "sof-audio.h"14#include "ops.h"15 16static bool is_virtual_widget(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,17			      const char *func)18{19	switch (widget->id) {20	case snd_soc_dapm_out_drv:21	case snd_soc_dapm_output:22	case snd_soc_dapm_input:23		dev_dbg(sdev->dev, "%s: %s is a virtual widget\n", func, widget->name);24		return true;25	default:26		return false;27	}28}29 30static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget)31{32	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);33	struct snd_sof_route *sroute;34 35	list_for_each_entry(sroute, &sdev->route_list, list)36		if (sroute->src_widget == widget || sroute->sink_widget == widget) {37			if (sroute->setup && tplg_ops && tplg_ops->route_free)38				tplg_ops->route_free(sdev, sroute);39 40			sroute->setup = false;41		}42}43 44static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,45				    struct snd_sof_widget *swidget)46{47	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);48	struct snd_sof_pipeline *spipe = swidget->spipe;49	int err = 0;50	int ret;51 52	if (!swidget->private)53		return 0;54 55	trace_sof_widget_free(swidget);56 57	/* only free when use_count is 0 */58	if (--swidget->use_count)59		return 0;60 61	/* reset route setup status for all routes that contain this widget */62	sof_reset_route_setup_status(sdev, swidget);63 64	/* free DAI config and continue to free widget even if it fails */65	if (WIDGET_IS_DAI(swidget->id)) {66		struct snd_sof_dai_config_data data;67		unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_FREE;68 69		data.dai_data = DMA_CHAN_INVALID;70 71		if (tplg_ops && tplg_ops->dai_config) {72			err = tplg_ops->dai_config(sdev, swidget, flags, &data);73			if (err < 0)74				dev_err(sdev->dev, "failed to free config for widget %s\n",75					swidget->widget->name);76		}77	}78 79	/* continue to disable core even if IPC fails */80	if (tplg_ops && tplg_ops->widget_free) {81		ret = tplg_ops->widget_free(sdev, swidget);82		if (ret < 0 && !err)83			err = ret;84	}85 86	/*87	 * decrement ref count for cores associated with all modules in the pipeline and clear88	 * the complete flag89	 */90	if (swidget->id == snd_soc_dapm_scheduler) {91		int i;92 93		for_each_set_bit(i, &spipe->core_mask, sdev->num_cores) {94			ret = snd_sof_dsp_core_put(sdev, i);95			if (ret < 0) {96				dev_err(sdev->dev, "failed to disable target core: %d for pipeline %s\n",97					i, swidget->widget->name);98				if (!err)99					err = ret;100			}101		}102		swidget->spipe->complete = 0;103	}104 105	/*106	 * free the scheduler widget (same as pipe_widget) associated with the current swidget.107	 * skip for static pipelines108	 */109	if (swidget->spipe && swidget->dynamic_pipeline_widget &&110	    swidget->id != snd_soc_dapm_scheduler) {111		ret = sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget);112		if (ret < 0 && !err)113			err = ret;114	}115 116	if (!err)117		dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name);118 119	return err;120}121 122int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)123{124	int ret;125 126	mutex_lock(&swidget->setup_mutex);127	ret = sof_widget_free_unlocked(sdev, swidget);128	mutex_unlock(&swidget->setup_mutex);129 130	return ret;131}132EXPORT_SYMBOL(sof_widget_free);133 134static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev,135				     struct snd_sof_widget *swidget)136{137	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);138	struct snd_sof_pipeline *spipe = swidget->spipe;139	bool use_count_decremented = false;140	int ret;141	int i;142 143	/* skip if there is no private data */144	if (!swidget->private)145		return 0;146 147	trace_sof_widget_setup(swidget);148 149	/* widget already set up */150	if (++swidget->use_count > 1)151		return 0;152 153	/*154	 * The scheduler widget for a pipeline is not part of the connected DAPM155	 * widget list and it needs to be set up before the widgets in the pipeline156	 * are set up. The use_count for the scheduler widget is incremented for every157	 * widget in a given pipeline to ensure that it is freed only after the last158	 * widget in the pipeline is freed. Skip setting up scheduler widget for static pipelines.159	 */160	if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {161		if (!swidget->spipe || !swidget->spipe->pipe_widget) {162			dev_err(sdev->dev, "No pipeline set for %s\n", swidget->widget->name);163			ret = -EINVAL;164			goto use_count_dec;165		}166 167		ret = sof_widget_setup_unlocked(sdev, swidget->spipe->pipe_widget);168		if (ret < 0)169			goto use_count_dec;170	}171 172	/* update ref count for cores associated with all modules in the pipeline */173	if (swidget->id == snd_soc_dapm_scheduler) {174		for_each_set_bit(i, &spipe->core_mask, sdev->num_cores) {175			ret = snd_sof_dsp_core_get(sdev, i);176			if (ret < 0) {177				dev_err(sdev->dev, "failed to enable target core %d for pipeline %s\n",178					i, swidget->widget->name);179				goto pipe_widget_free;180			}181		}182	}183 184	/* setup widget in the DSP */185	if (tplg_ops && tplg_ops->widget_setup) {186		ret = tplg_ops->widget_setup(sdev, swidget);187		if (ret < 0)188			goto pipe_widget_free;189	}190 191	/* send config for DAI components */192	if (WIDGET_IS_DAI(swidget->id)) {193		unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;194 195		/*196		 * The config flags saved during BE DAI hw_params will be used for IPC3. IPC4 does197		 * not use the flags argument.198		 */199		if (tplg_ops && tplg_ops->dai_config) {200			ret = tplg_ops->dai_config(sdev, swidget, flags, NULL);201			if (ret < 0)202				goto widget_free;203		}204	}205 206	/* restore kcontrols for widget */207	if (tplg_ops && tplg_ops->control && tplg_ops->control->widget_kcontrol_setup) {208		ret = tplg_ops->control->widget_kcontrol_setup(sdev, swidget);209		if (ret < 0)210			goto widget_free;211	}212 213	dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name);214 215	return 0;216 217widget_free:218	/* widget use_count will be decremented by sof_widget_free() */219	sof_widget_free_unlocked(sdev, swidget);220	use_count_decremented = true;221pipe_widget_free:222	if (swidget->id != snd_soc_dapm_scheduler) {223		sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget);224	} else {225		int j;226 227		/* decrement ref count for all cores that were updated previously */228		for_each_set_bit(j, &spipe->core_mask, sdev->num_cores) {229			if (j >= i)230				break;231			snd_sof_dsp_core_put(sdev, j);232		}233	}234use_count_dec:235	if (!use_count_decremented)236		swidget->use_count--;237 238	return ret;239}240 241int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)242{243	int ret;244 245	mutex_lock(&swidget->setup_mutex);246	ret = sof_widget_setup_unlocked(sdev, swidget);247	mutex_unlock(&swidget->setup_mutex);248 249	return ret;250}251EXPORT_SYMBOL(sof_widget_setup);252 253int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource,254		    struct snd_soc_dapm_widget *wsink)255{256	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);257	struct snd_sof_widget *src_widget = wsource->dobj.private;258	struct snd_sof_widget *sink_widget = wsink->dobj.private;259	struct snd_sof_route *sroute;260	bool route_found = false;261 262	/* ignore routes involving virtual widgets in topology */263	if (is_virtual_widget(sdev, src_widget->widget, __func__) ||264	    is_virtual_widget(sdev, sink_widget->widget, __func__))265		return 0;266 267	/* find route matching source and sink widgets */268	list_for_each_entry(sroute, &sdev->route_list, list)269		if (sroute->src_widget == src_widget && sroute->sink_widget == sink_widget) {270			route_found = true;271			break;272		}273 274	if (!route_found) {275		dev_err(sdev->dev, "error: cannot find SOF route for source %s -> %s sink\n",276			wsource->name, wsink->name);277		return -EINVAL;278	}279 280	/* nothing to do if route is already set up */281	if (sroute->setup)282		return 0;283 284	if (tplg_ops && tplg_ops->route_setup) {285		int ret = tplg_ops->route_setup(sdev, sroute);286 287		if (ret < 0)288			return ret;289	}290 291	sroute->setup = true;292	return 0;293}294 295static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,296					  struct snd_soc_dapm_widget_list *list, int dir)297{298	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);299	struct snd_soc_dapm_widget *widget;300	struct snd_sof_route *sroute;301	struct snd_soc_dapm_path *p;302	int ret = 0;303	int i;304 305	/*306	 * Set up connections between widgets in the sink/source paths based on direction.307	 * Some non-SOF widgets exist in topology either for compatibility or for the308	 * purpose of connecting a pipeline from a host to a DAI in order to receive the DAPM309	 * events. But they are not handled by the firmware. So ignore them.310	 */311	if (dir == SNDRV_PCM_STREAM_PLAYBACK) {312		for_each_dapm_widgets(list, i, widget) {313			if (!widget->dobj.private)314				continue;315 316			snd_soc_dapm_widget_for_each_sink_path(widget, p) {317				if (!widget_in_list(list, p->sink))318					continue;319 320				if (p->sink->dobj.private) {321					ret = sof_route_setup(sdev, widget, p->sink);322					if (ret < 0)323						return ret;324				}325			}326		}327	} else {328		for_each_dapm_widgets(list, i, widget) {329			if (!widget->dobj.private)330				continue;331 332			snd_soc_dapm_widget_for_each_source_path(widget, p) {333				if (!widget_in_list(list, p->source))334					continue;335 336				if (p->source->dobj.private) {337					ret = sof_route_setup(sdev, p->source, widget);338					if (ret < 0)339						return ret;340				}341			}342		}343	}344 345	/*346	 * The above loop handles connections between widgets that belong to the DAPM widget list.347	 * This is not sufficient to handle loopback cases between pipelines configured with348	 * different directions, e.g. a sidetone or an amplifier feedback connected to a speaker349	 * protection module.350	 */351	list_for_each_entry(sroute, &sdev->route_list, list) {352		bool src_widget_in_dapm_list, sink_widget_in_dapm_list;353		struct snd_sof_widget *swidget;354 355		if (sroute->setup)356			continue;357 358		src_widget_in_dapm_list = widget_in_list(list, sroute->src_widget->widget);359		sink_widget_in_dapm_list = widget_in_list(list, sroute->sink_widget->widget);360 361		/*362		 * if both source and sink are in the DAPM list, the route must already have been363		 * set up above. And if neither are in the DAPM list, the route shouldn't be364		 * handled now.365		 */366		if (src_widget_in_dapm_list == sink_widget_in_dapm_list)367			continue;368 369		/*370		 * At this point either the source widget or the sink widget is in the DAPM list371		 * with a route that might need to be set up. Check the use_count of the widget372		 * that is not in the DAPM list to confirm if it is in use currently before setting373		 * up the route.374		 */375		if (src_widget_in_dapm_list)376			swidget = sroute->sink_widget;377		else378			swidget = sroute->src_widget;379 380		mutex_lock(&swidget->setup_mutex);381		if (!swidget->use_count) {382			mutex_unlock(&swidget->setup_mutex);383			continue;384		}385 386		if (tplg_ops && tplg_ops->route_setup) {387			/*388			 * this route will get freed when either the source widget or the sink389			 * widget is freed during hw_free390			 */391			ret = tplg_ops->route_setup(sdev, sroute);392			if (!ret)393				sroute->setup = true;394		}395 396		mutex_unlock(&swidget->setup_mutex);397 398		if (ret < 0)399			return ret;400	}401 402	return 0;403}404 405static void406sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,407			      struct snd_soc_dapm_widget_list *list)408{409	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);410	struct snd_sof_widget *swidget = widget->dobj.private;411	const struct sof_ipc_tplg_widget_ops *widget_ops;412	struct snd_soc_dapm_path *p;413 414	if (is_virtual_widget(sdev, widget, __func__))415		return;416 417	/* skip if the widget is in use or if it is already unprepared */418	if (!swidget || !swidget->prepared || swidget->use_count > 0)419		goto sink_unprepare;420 421	widget_ops = tplg_ops ? tplg_ops->widget : NULL;422	if (widget_ops && widget_ops[widget->id].ipc_unprepare)423		/* unprepare the source widget */424		widget_ops[widget->id].ipc_unprepare(swidget);425 426	swidget->prepared = false;427 428sink_unprepare:429	/* unprepare all widgets in the sink paths */430	snd_soc_dapm_widget_for_each_sink_path(widget, p) {431		if (!widget_in_list(list, p->sink))432			continue;433		if (!p->walking && p->sink->dobj.private) {434			p->walking = true;435			sof_unprepare_widgets_in_path(sdev, p->sink, list);436			p->walking = false;437		}438	}439}440 441static int442sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,443			    struct snd_pcm_hw_params *fe_params,444			    struct snd_sof_platform_stream_params *platform_params,445			    struct snd_pcm_hw_params *pipeline_params, int dir,446			    struct snd_soc_dapm_widget_list *list)447{448	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);449	struct snd_sof_widget *swidget = widget->dobj.private;450	const struct sof_ipc_tplg_widget_ops *widget_ops;451	struct snd_soc_dapm_path *p;452	int ret;453 454	if (is_virtual_widget(sdev, widget, __func__))455		return 0;456 457	widget_ops = tplg_ops ? tplg_ops->widget : NULL;458	if (!widget_ops)459		return 0;460 461	if (!swidget || !widget_ops[widget->id].ipc_prepare || swidget->prepared)462		goto sink_prepare;463 464	/* prepare the source widget */465	ret = widget_ops[widget->id].ipc_prepare(swidget, fe_params, platform_params,466					     pipeline_params, dir);467	if (ret < 0) {468		dev_err(sdev->dev, "failed to prepare widget %s\n", widget->name);469		return ret;470	}471 472	swidget->prepared = true;473 474sink_prepare:475	/* prepare all widgets in the sink paths */476	snd_soc_dapm_widget_for_each_sink_path(widget, p) {477		if (!widget_in_list(list, p->sink))478			continue;479		if (!p->walking && p->sink->dobj.private) {480			p->walking = true;481			ret = sof_prepare_widgets_in_path(sdev, p->sink,  fe_params,482							  platform_params, pipeline_params, dir,483							  list);484			p->walking = false;485			if (ret < 0) {486				/* unprepare the source widget */487				if (widget_ops[widget->id].ipc_unprepare &&488				    swidget && swidget->prepared && swidget->use_count == 0) {489					widget_ops[widget->id].ipc_unprepare(swidget);490					swidget->prepared = false;491				}492				return ret;493			}494		}495	}496 497	return 0;498}499 500/*501 * free all widgets in the sink path starting from the source widget502 * (DAI type for capture, AIF type for playback)503 */504static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,505				    int dir, struct snd_sof_pcm *spcm)506{507	struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;508	struct snd_soc_dapm_path *p;509	int err;510	int ret = 0;511 512	if (is_virtual_widget(sdev, widget, __func__))513		return 0;514 515	if (widget->dobj.private) {516		err = sof_widget_free(sdev, widget->dobj.private);517		if (err < 0)518			ret = err;519	}520 521	/* free all widgets in the sink paths even in case of error to keep use counts balanced */522	snd_soc_dapm_widget_for_each_sink_path(widget, p) {523		if (!p->walking) {524			if (!widget_in_list(list, p->sink))525				continue;526 527			p->walking = true;528 529			err = sof_free_widgets_in_path(sdev, p->sink, dir, spcm);530			if (err < 0)531				ret = err;532			p->walking = false;533		}534	}535 536	return ret;537}538 539/*540 * set up all widgets in the sink path starting from the source widget541 * (DAI type for capture, AIF type for playback).542 * The error path in this function ensures that all successfully set up widgets getting freed.543 */544static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,545				      int dir, struct snd_sof_pcm *spcm)546{547	struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list;548	struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;549	struct snd_sof_widget *swidget = widget->dobj.private;550	struct snd_sof_pipeline *spipe;551	struct snd_soc_dapm_path *p;552	int ret;553 554	if (is_virtual_widget(sdev, widget, __func__))555		return 0;556 557	if (swidget) {558		int i;559 560		ret = sof_widget_setup(sdev, widget->dobj.private);561		if (ret < 0)562			return ret;563 564		/* skip populating the pipe_widgets array if it is NULL */565		if (!pipeline_list->pipelines)566			goto sink_setup;567 568		/*569		 * Add the widget's pipe_widget to the list of pipelines to be triggered if not570		 * already in the list. This will result in the pipelines getting added in the571		 * order source to sink.572		 */573		for (i = 0; i < pipeline_list->count; i++) {574			spipe = pipeline_list->pipelines[i];575			if (spipe == swidget->spipe)576				break;577		}578 579		if (i == pipeline_list->count) {580			pipeline_list->count++;581			pipeline_list->pipelines[i] = swidget->spipe;582		}583	}584 585sink_setup:586	snd_soc_dapm_widget_for_each_sink_path(widget, p) {587		if (!p->walking) {588			if (!widget_in_list(list, p->sink))589				continue;590 591			p->walking = true;592 593			ret = sof_set_up_widgets_in_path(sdev, p->sink, dir, spcm);594			p->walking = false;595			if (ret < 0) {596				if (swidget)597					sof_widget_free(sdev, swidget);598				return ret;599			}600		}601	}602 603	return 0;604}605 606static int607sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,608			  struct snd_pcm_hw_params *fe_params,609			  struct snd_sof_platform_stream_params *platform_params, int dir,610			  enum sof_widget_op op)611{612	struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;613	struct snd_soc_dapm_widget *widget;614	char *str;615	int ret = 0;616	int i;617 618	if (!list)619		return 0;620 621	for_each_dapm_widgets(list, i, widget) {622		if (is_virtual_widget(sdev, widget, __func__))623			continue;624 625		/* starting widget for playback is AIF type */626		if (dir == SNDRV_PCM_STREAM_PLAYBACK && widget->id != snd_soc_dapm_aif_in)627			continue;628 629		/* starting widget for capture is DAI type */630		if (dir == SNDRV_PCM_STREAM_CAPTURE && widget->id != snd_soc_dapm_dai_out)631			continue;632 633		switch (op) {634		case SOF_WIDGET_SETUP:635			ret = sof_set_up_widgets_in_path(sdev, widget, dir, spcm);636			str = "set up";637			break;638		case SOF_WIDGET_FREE:639			ret = sof_free_widgets_in_path(sdev, widget, dir, spcm);640			str = "free";641			break;642		case SOF_WIDGET_PREPARE:643		{644			struct snd_pcm_hw_params pipeline_params;645 646			str = "prepare";647			/*648			 * When walking the list of connected widgets, the pipeline_params for each649			 * widget is modified by the source widget in the path. Use a local650			 * copy of the runtime params as the pipeline_params so that the runtime651			 * params does not get overwritten.652			 */653			memcpy(&pipeline_params, fe_params, sizeof(*fe_params));654 655			ret = sof_prepare_widgets_in_path(sdev, widget, fe_params, platform_params,656							  &pipeline_params, dir, list);657			break;658		}659		case SOF_WIDGET_UNPREPARE:660			sof_unprepare_widgets_in_path(sdev, widget, list);661			break;662		default:663			dev_err(sdev->dev, "Invalid widget op %d\n", op);664			return -EINVAL;665		}666		if (ret < 0) {667			dev_err(sdev->dev, "Failed to %s connected widgets\n", str);668			return ret;669		}670	}671 672	return 0;673}674 675int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,676			  struct snd_pcm_hw_params *fe_params,677			  struct snd_sof_platform_stream_params *platform_params,678			  int dir)679{680	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);681	struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;682	struct snd_soc_dapm_widget *widget;683	int i, ret;684 685	/* nothing to set up */686	if (!list)687		return 0;688 689	/*690	 * Prepare widgets for set up. The prepare step is used to allocate memory, assign691	 * instance ID and pick the widget configuration based on the runtime PCM params.692	 */693	ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,694					dir, SOF_WIDGET_PREPARE);695	if (ret < 0)696		return ret;697 698	/* Set up is used to send the IPC to the DSP to create the widget */699	ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,700					dir, SOF_WIDGET_SETUP);701	if (ret < 0) {702		sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,703					  dir, SOF_WIDGET_UNPREPARE);704		return ret;705	}706 707	/*708	 * error in setting pipeline connections will result in route status being reset for709	 * routes that were successfully set up when the widgets are freed.710	 */711	ret = sof_setup_pipeline_connections(sdev, list, dir);712	if (ret < 0)713		goto widget_free;714 715	/* complete pipelines */716	for_each_dapm_widgets(list, i, widget) {717		struct snd_sof_widget *swidget = widget->dobj.private;718		struct snd_sof_widget *pipe_widget;719		struct snd_sof_pipeline *spipe;720 721		if (!swidget || sdev->dspless_mode_selected)722			continue;723 724		spipe = swidget->spipe;725		if (!spipe) {726			dev_err(sdev->dev, "no pipeline found for %s\n",727				swidget->widget->name);728			ret = -EINVAL;729			goto widget_free;730		}731 732		pipe_widget = spipe->pipe_widget;733		if (!pipe_widget) {734			dev_err(sdev->dev, "error: no pipeline widget found for %s\n",735				swidget->widget->name);736			ret = -EINVAL;737			goto widget_free;738		}739 740		if (spipe->complete)741			continue;742 743		if (tplg_ops && tplg_ops->pipeline_complete) {744			spipe->complete = tplg_ops->pipeline_complete(sdev, pipe_widget);745			if (spipe->complete < 0) {746				ret = spipe->complete;747				goto widget_free;748			}749		}750	}751 752	return 0;753 754widget_free:755	sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, dir,756				  SOF_WIDGET_FREE);757	sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);758 759	return ret;760}761 762int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)763{764	struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list;765	struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;766	int ret;767 768	/* nothing to free */769	if (!list)770		return 0;771 772	/* send IPC to free widget in the DSP */773	ret = sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_FREE);774 775	/* unprepare the widget */776	sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);777 778	snd_soc_dapm_dai_free_widgets(&list);779	spcm->stream[dir].list = NULL;780 781	pipeline_list->count = 0;782 783	return ret;784}785 786/*787 * helper to determine if there are only D0i3 compatible788 * streams active789 */790bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev)791{792	struct snd_pcm_substream *substream;793	struct snd_sof_pcm *spcm;794	bool d0i3_compatible_active = false;795	int dir;796 797	list_for_each_entry(spcm, &sdev->pcm_list, list) {798		for_each_pcm_streams(dir) {799			substream = spcm->stream[dir].substream;800			if (!substream || !substream->runtime)801				continue;802 803			/*804			 * substream->runtime being not NULL indicates805			 * that the stream is open. No need to check the806			 * stream state.807			 */808			if (!spcm->stream[dir].d0i3_compatible)809				return false;810 811			d0i3_compatible_active = true;812		}813	}814 815	return d0i3_compatible_active;816}817EXPORT_SYMBOL(snd_sof_dsp_only_d0i3_compatible_stream_active);818 819bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev)820{821	struct snd_sof_pcm *spcm;822 823	list_for_each_entry(spcm, &sdev->pcm_list, list) {824		if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].suspend_ignored ||825		    spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored)826			return true;827	}828 829	return false;830}831 832int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,833			struct snd_sof_pcm *spcm, int dir, bool free_widget_list)834{835	const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);836	int ret;837	int err = 0;838 839	if (spcm->prepared[substream->stream]) {840		/* stop DMA first if needed */841		if (pcm_ops && pcm_ops->platform_stop_during_hw_free)842			snd_sof_pcm_platform_trigger(sdev, substream, SNDRV_PCM_TRIGGER_STOP);843 844		/* free PCM in the DSP */845		if (pcm_ops && pcm_ops->hw_free) {846			ret = pcm_ops->hw_free(sdev->component, substream);847			if (ret < 0) {848				dev_err(sdev->dev, "%s: pcm_ops hw_free failed %d\n",849					__func__, ret);850				err = ret;851			}852		}853 854		spcm->prepared[substream->stream] = false;855		spcm->pending_stop[substream->stream] = false;856	}857 858	/* reset the DMA */859	ret = snd_sof_pcm_platform_hw_free(sdev, substream);860	if (ret < 0) {861		dev_err(sdev->dev, "%s: platform hw free failed %d\n",862			__func__, ret);863		if (!err)864			err = ret;865	}866 867	/* free widget list */868	if (free_widget_list) {869		ret = sof_widget_list_free(sdev, spcm, dir);870		if (ret < 0) {871			dev_err(sdev->dev, "%s: sof_widget_list_free failed %d\n",872				__func__, ret);873			if (!err)874				err = ret;875		}876	}877 878	return err;879}880 881/*882 * Generic object lookup APIs.883 */884 885struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp,886					   const char *name)887{888	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);889	struct snd_sof_pcm *spcm;890 891	list_for_each_entry(spcm, &sdev->pcm_list, list) {892		/* match with PCM dai name */893		if (strcmp(spcm->pcm.dai_name, name) == 0)894			return spcm;895 896		/* match with playback caps name if set */897		if (*spcm->pcm.caps[0].name &&898		    !strcmp(spcm->pcm.caps[0].name, name))899			return spcm;900 901		/* match with capture caps name if set */902		if (*spcm->pcm.caps[1].name &&903		    !strcmp(spcm->pcm.caps[1].name, name))904			return spcm;905	}906 907	return NULL;908}909 910struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp,911					   unsigned int comp_id,912					   int *direction)913{914	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);915	struct snd_sof_pcm *spcm;916	int dir;917 918	list_for_each_entry(spcm, &sdev->pcm_list, list) {919		for_each_pcm_streams(dir) {920			if (spcm->stream[dir].comp_id == comp_id) {921				*direction = dir;922				return spcm;923			}924		}925	}926 927	return NULL;928}929 930struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp,931					    const char *name)932{933	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);934	struct snd_sof_widget *swidget;935 936	list_for_each_entry(swidget, &sdev->widget_list, list) {937		if (strcmp(name, swidget->widget->name) == 0)938			return swidget;939	}940 941	return NULL;942}943 944/* find widget by stream name and direction */945struct snd_sof_widget *946snd_sof_find_swidget_sname(struct snd_soc_component *scomp,947			   const char *pcm_name, int dir)948{949	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);950	struct snd_sof_widget *swidget;951	enum snd_soc_dapm_type type;952 953	if (dir == SNDRV_PCM_STREAM_PLAYBACK)954		type = snd_soc_dapm_aif_in;955	else956		type = snd_soc_dapm_aif_out;957 958	list_for_each_entry(swidget, &sdev->widget_list, list) {959		if (!strcmp(pcm_name, swidget->widget->sname) &&960		    swidget->id == type)961			return swidget;962	}963 964	return NULL;965}966 967struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp,968				     const char *name)969{970	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);971	struct snd_sof_dai *dai;972 973	list_for_each_entry(dai, &sdev->dai_list, list) {974		if (dai->name && (strcmp(name, dai->name) == 0))975			return dai;976	}977 978	return NULL;979}980 981static int sof_dai_get_param(struct snd_soc_pcm_runtime *rtd, int param_type)982{983	struct snd_soc_component *component =984		snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);985	struct snd_sof_dai *dai =986		snd_sof_find_dai(component, (char *)rtd->dai_link->name);987	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);988	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);989 990	/* use the tplg configured mclk if existed */991	if (!dai)992		return 0;993 994	if (tplg_ops && tplg_ops->dai_get_param)995		return tplg_ops->dai_get_param(sdev, dai, param_type);996 997	return 0;998}999 1000/*1001 * Helper to get SSP MCLK from a pcm_runtime.1002 * Return 0 if not exist.1003 */1004int sof_dai_get_mclk(struct snd_soc_pcm_runtime *rtd)1005{1006	return sof_dai_get_param(rtd, SOF_DAI_PARAM_INTEL_SSP_MCLK);1007}1008EXPORT_SYMBOL(sof_dai_get_mclk);1009 1010/*1011 * Helper to get SSP BCLK from a pcm_runtime.1012 * Return 0 if not exist.1013 */1014int sof_dai_get_bclk(struct snd_soc_pcm_runtime *rtd)1015{1016	return sof_dai_get_param(rtd, SOF_DAI_PARAM_INTEL_SSP_BCLK);1017}1018EXPORT_SYMBOL(sof_dai_get_bclk);1019 1020/*1021 * Helper to get SSP TDM slot number from a pcm_runtime.1022 * Return 0 if not exist.1023 */1024int sof_dai_get_tdm_slots(struct snd_soc_pcm_runtime *rtd)1025{1026	return sof_dai_get_param(rtd, SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS);1027}1028EXPORT_SYMBOL(sof_dai_get_tdm_slots);1029