cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sof-audio.c (22970B)


      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 or
      4// redistributing this file, you may do so under either license.
      5//
      6// Copyright(c) 2019 Intel Corporation. All rights reserved.
      7//
      8// Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
      9//
     10
     11#include <linux/bitfield.h>
     12#include "sof-audio.h"
     13#include "ops.h"
     14
     15static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget)
     16{
     17	const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
     18	struct snd_sof_route *sroute;
     19
     20	list_for_each_entry(sroute, &sdev->route_list, list)
     21		if (sroute->src_widget == widget || sroute->sink_widget == widget) {
     22			if (sroute->setup && tplg_ops->route_free)
     23				tplg_ops->route_free(sdev, sroute);
     24
     25			sroute->setup = false;
     26		}
     27}
     28
     29int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
     30{
     31	const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
     32	int err = 0;
     33	int ret;
     34
     35	if (!swidget->private)
     36		return 0;
     37
     38	/* only free when use_count is 0 */
     39	if (--swidget->use_count)
     40		return 0;
     41
     42	/* reset route setup status for all routes that contain this widget */
     43	sof_reset_route_setup_status(sdev, swidget);
     44
     45	/* continue to disable core even if IPC fails */
     46	if (tplg_ops->widget_free)
     47		err = tplg_ops->widget_free(sdev, swidget);
     48
     49	/*
     50	 * disable widget core. continue to route setup status and complete flag
     51	 * even if this fails and return the appropriate error
     52	 */
     53	ret = snd_sof_dsp_core_put(sdev, swidget->core);
     54	if (ret < 0) {
     55		dev_err(sdev->dev, "error: failed to disable target core: %d for widget %s\n",
     56			swidget->core, swidget->widget->name);
     57		if (!err)
     58			err = ret;
     59	}
     60
     61	/*
     62	 * free the scheduler widget (same as pipe_widget) associated with the current swidget.
     63	 * skip for static pipelines
     64	 */
     65	if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {
     66		ret = sof_widget_free(sdev, swidget->pipe_widget);
     67		if (ret < 0 && !err)
     68			err = ret;
     69		swidget->pipe_widget->complete = 0;
     70	}
     71
     72	if (!err)
     73		dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name);
     74
     75	return err;
     76}
     77EXPORT_SYMBOL(sof_widget_free);
     78
     79int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
     80{
     81	const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
     82	int ret;
     83
     84	/* skip if there is no private data */
     85	if (!swidget->private)
     86		return 0;
     87
     88	/* widget already set up */
     89	if (++swidget->use_count > 1)
     90		return 0;
     91
     92	/*
     93	 * The scheduler widget for a pipeline is not part of the connected DAPM
     94	 * widget list and it needs to be set up before the widgets in the pipeline
     95	 * are set up. The use_count for the scheduler widget is incremented for every
     96	 * widget in a given pipeline to ensure that it is freed only after the last
     97	 * widget in the pipeline is freed. Skip setting up scheduler widget for static pipelines.
     98	 */
     99	if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {
    100		if (!swidget->pipe_widget) {
    101			dev_err(sdev->dev, "No scheduler widget set for %s\n",
    102				swidget->widget->name);
    103			ret = -EINVAL;
    104			goto use_count_dec;
    105		}
    106
    107		ret = sof_widget_setup(sdev, swidget->pipe_widget);
    108		if (ret < 0)
    109			goto use_count_dec;
    110	}
    111
    112	/* enable widget core */
    113	ret = snd_sof_dsp_core_get(sdev, swidget->core);
    114	if (ret < 0) {
    115		dev_err(sdev->dev, "error: failed to enable target core for widget %s\n",
    116			swidget->widget->name);
    117		goto pipe_widget_free;
    118	}
    119
    120	/* setup widget in the DSP */
    121	if (tplg_ops->widget_setup) {
    122		ret = tplg_ops->widget_setup(sdev, swidget);
    123		if (ret < 0)
    124			goto core_put;
    125	}
    126
    127	/* send config for DAI components */
    128	if (WIDGET_IS_DAI(swidget->id)) {
    129		unsigned int flags = SOF_DAI_CONFIG_FLAGS_NONE;
    130
    131		if (tplg_ops->dai_config) {
    132			ret = tplg_ops->dai_config(sdev, swidget, flags, NULL);
    133			if (ret < 0)
    134				goto widget_free;
    135		}
    136	}
    137
    138	/* restore kcontrols for widget */
    139	if (tplg_ops->control->widget_kcontrol_setup) {
    140		ret = tplg_ops->control->widget_kcontrol_setup(sdev, swidget);
    141		if (ret < 0)
    142			goto widget_free;
    143	}
    144
    145	dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name);
    146
    147	return 0;
    148
    149widget_free:
    150	/* widget use_count and core ref_count will both be decremented by sof_widget_free() */
    151	sof_widget_free(sdev, swidget);
    152core_put:
    153	snd_sof_dsp_core_put(sdev, swidget->core);
    154pipe_widget_free:
    155	if (swidget->id != snd_soc_dapm_scheduler)
    156		sof_widget_free(sdev, swidget->pipe_widget);
    157use_count_dec:
    158	swidget->use_count--;
    159	return ret;
    160}
    161EXPORT_SYMBOL(sof_widget_setup);
    162
    163int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource,
    164		    struct snd_soc_dapm_widget *wsink)
    165{
    166	const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
    167	struct snd_sof_widget *src_widget = wsource->dobj.private;
    168	struct snd_sof_widget *sink_widget = wsink->dobj.private;
    169	struct snd_sof_route *sroute;
    170	bool route_found = false;
    171	int ret;
    172
    173	/* ignore routes involving virtual widgets in topology */
    174	switch (src_widget->id) {
    175	case snd_soc_dapm_out_drv:
    176	case snd_soc_dapm_output:
    177	case snd_soc_dapm_input:
    178		return 0;
    179	default:
    180		break;
    181	}
    182
    183	switch (sink_widget->id) {
    184	case snd_soc_dapm_out_drv:
    185	case snd_soc_dapm_output:
    186	case snd_soc_dapm_input:
    187		return 0;
    188	default:
    189		break;
    190	}
    191
    192	/* find route matching source and sink widgets */
    193	list_for_each_entry(sroute, &sdev->route_list, list)
    194		if (sroute->src_widget == src_widget && sroute->sink_widget == sink_widget) {
    195			route_found = true;
    196			break;
    197		}
    198
    199	if (!route_found) {
    200		dev_err(sdev->dev, "error: cannot find SOF route for source %s -> %s sink\n",
    201			wsource->name, wsink->name);
    202		return -EINVAL;
    203	}
    204
    205	/* nothing to do if route is already set up */
    206	if (sroute->setup)
    207		return 0;
    208
    209	ret = ipc_tplg_ops->route_setup(sdev, sroute);
    210	if (ret < 0)
    211		return ret;
    212
    213	sroute->setup = true;
    214	return 0;
    215}
    216
    217static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
    218					  struct snd_soc_dapm_widget_list *list, int dir)
    219{
    220	struct snd_soc_dapm_widget *widget;
    221	struct snd_soc_dapm_path *p;
    222	int ret;
    223	int i;
    224
    225	/*
    226	 * Set up connections between widgets in the sink/source paths based on direction.
    227	 * Some non-SOF widgets exist in topology either for compatibility or for the
    228	 * purpose of connecting a pipeline from a host to a DAI in order to receive the DAPM
    229	 * events. But they are not handled by the firmware. So ignore them.
    230	 */
    231	if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
    232		for_each_dapm_widgets(list, i, widget) {
    233			if (!widget->dobj.private)
    234				continue;
    235
    236			snd_soc_dapm_widget_for_each_sink_path(widget, p)
    237				if (p->sink->dobj.private) {
    238					ret = sof_route_setup(sdev, widget, p->sink);
    239					if (ret < 0)
    240						return ret;
    241				}
    242		}
    243	} else {
    244		for_each_dapm_widgets(list, i, widget) {
    245			if (!widget->dobj.private)
    246				continue;
    247
    248			snd_soc_dapm_widget_for_each_source_path(widget, p)
    249				if (p->source->dobj.private) {
    250					ret = sof_route_setup(sdev, p->source, widget);
    251					if (ret < 0)
    252						return ret;
    253				}
    254		}
    255	}
    256
    257	return 0;
    258}
    259
    260static void
    261sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget)
    262{
    263	const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
    264	const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
    265	struct snd_sof_widget *swidget = widget->dobj.private;
    266	struct snd_soc_dapm_path *p;
    267
    268	if (!widget_ops[widget->id].ipc_unprepare || !swidget->prepared)
    269		goto sink_unprepare;
    270
    271	/* unprepare the source widget */
    272	widget_ops[widget->id].ipc_unprepare(swidget);
    273	swidget->prepared = false;
    274
    275sink_unprepare:
    276	/* unprepare all widgets in the sink paths */
    277	snd_soc_dapm_widget_for_each_sink_path(widget, p) {
    278		if (!p->walking && p->sink->dobj.private) {
    279			p->walking = true;
    280			sof_unprepare_widgets_in_path(sdev, p->sink);
    281			p->walking = false;
    282		}
    283	}
    284}
    285
    286static int
    287sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
    288			    struct snd_pcm_hw_params *fe_params,
    289			    struct snd_sof_platform_stream_params *platform_params,
    290			    struct snd_pcm_hw_params *pipeline_params, int dir)
    291{
    292	const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
    293	const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
    294	struct snd_sof_widget *swidget = widget->dobj.private;
    295	struct snd_soc_dapm_path *p;
    296	int ret;
    297
    298	if (!widget_ops[widget->id].ipc_prepare || swidget->prepared)
    299		goto sink_prepare;
    300
    301	/* prepare the source widget */
    302	ret = widget_ops[widget->id].ipc_prepare(swidget, fe_params, platform_params,
    303					     pipeline_params, dir);
    304	if (ret < 0) {
    305		dev_err(sdev->dev, "failed to prepare widget %s\n", widget->name);
    306		return ret;
    307	}
    308
    309	swidget->prepared = true;
    310
    311sink_prepare:
    312	/* prepare all widgets in the sink paths */
    313	snd_soc_dapm_widget_for_each_sink_path(widget, p) {
    314		if (!p->walking && p->sink->dobj.private) {
    315			p->walking = true;
    316			ret = sof_prepare_widgets_in_path(sdev, p->sink,  fe_params,
    317							  platform_params, pipeline_params, dir);
    318			p->walking = false;
    319			if (ret < 0) {
    320				/* unprepare the source widget */
    321				if (widget_ops[widget->id].ipc_unprepare && swidget->prepared) {
    322					widget_ops[widget->id].ipc_unprepare(swidget);
    323					swidget->prepared = false;
    324				}
    325				return ret;
    326			}
    327		}
    328	}
    329
    330	return 0;
    331}
    332
    333/*
    334 * free all widgets in the sink path starting from the source widget
    335 * (DAI type for capture, AIF type for playback)
    336 */
    337static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
    338				    int dir)
    339{
    340	struct snd_soc_dapm_path *p;
    341	int err;
    342	int ret = 0;
    343
    344	/* free all widgets even in case of error to keep use counts balanced */
    345	snd_soc_dapm_widget_for_each_sink_path(widget, p) {
    346		if (!p->walking && p->sink->dobj.private && widget->dobj.private) {
    347			p->walking = true;
    348			if (WIDGET_IS_AIF_OR_DAI(widget->id)) {
    349				err = sof_widget_free(sdev, widget->dobj.private);
    350				if (err < 0)
    351					ret = err;
    352			}
    353
    354			err = sof_widget_free(sdev, p->sink->dobj.private);
    355			if (err < 0)
    356				ret = err;
    357
    358			err = sof_free_widgets_in_path(sdev, p->sink, dir);
    359			if (err < 0)
    360				ret = err;
    361			p->walking = false;
    362		}
    363	}
    364
    365	return ret;
    366}
    367
    368/*
    369 * set up all widgets in the sink path starting from the source widget
    370 * (DAI type for capture, AIF type for playback).
    371 * The error path in this function ensures that all successfully set up widgets getting freed.
    372 */
    373static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
    374				      int dir)
    375{
    376	struct snd_soc_dapm_path *p;
    377	int ret;
    378
    379	snd_soc_dapm_widget_for_each_sink_path(widget, p) {
    380		if (!p->walking && p->sink->dobj.private && widget->dobj.private) {
    381			p->walking = true;
    382			if (WIDGET_IS_AIF_OR_DAI(widget->id)) {
    383				ret = sof_widget_setup(sdev, widget->dobj.private);
    384				if (ret < 0)
    385					goto out;
    386			}
    387
    388			ret = sof_widget_setup(sdev, p->sink->dobj.private);
    389			if (ret < 0) {
    390				if (WIDGET_IS_AIF_OR_DAI(widget->id))
    391					sof_widget_free(sdev, widget->dobj.private);
    392				goto out;
    393			}
    394
    395			ret = sof_set_up_widgets_in_path(sdev, p->sink, dir);
    396			if (ret < 0) {
    397				if (WIDGET_IS_AIF_OR_DAI(widget->id))
    398					sof_widget_free(sdev, widget->dobj.private);
    399				sof_widget_free(sdev, p->sink->dobj.private);
    400			}
    401out:
    402			p->walking = false;
    403			if (ret < 0)
    404				return ret;
    405		}
    406	}
    407
    408	return 0;
    409}
    410
    411static int
    412sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list,
    413			  struct snd_pcm_hw_params *fe_params,
    414			  struct snd_sof_platform_stream_params *platform_params, int dir,
    415			  enum sof_widget_op op)
    416{
    417	struct snd_soc_dapm_widget *widget;
    418	char *str;
    419	int ret = 0;
    420	int i;
    421
    422	for_each_dapm_widgets(list, i, widget) {
    423		/* starting widget for playback is AIF type */
    424		if (dir == SNDRV_PCM_STREAM_PLAYBACK && !WIDGET_IS_AIF(widget->id))
    425			continue;
    426
    427		/* starting widget for capture is DAI type */
    428		if (dir == SNDRV_PCM_STREAM_CAPTURE && !WIDGET_IS_DAI(widget->id))
    429			continue;
    430
    431		switch (op) {
    432		case SOF_WIDGET_SETUP:
    433			ret = sof_set_up_widgets_in_path(sdev, widget, dir);
    434			str = "set up";
    435			break;
    436		case SOF_WIDGET_FREE:
    437			ret = sof_free_widgets_in_path(sdev, widget, dir);
    438			str = "free";
    439			break;
    440		case SOF_WIDGET_PREPARE:
    441		{
    442			struct snd_pcm_hw_params pipeline_params;
    443
    444			str = "prepare";
    445			/*
    446			 * When walking the list of connected widgets, the pipeline_params for each
    447			 * widget is modified by the source widget in the path. Use a local
    448			 * copy of the runtime params as the pipeline_params so that the runtime
    449			 * params does not get overwritten.
    450			 */
    451			memcpy(&pipeline_params, fe_params, sizeof(*fe_params));
    452
    453			ret = sof_prepare_widgets_in_path(sdev, widget, fe_params,
    454							  platform_params, &pipeline_params, dir);
    455			break;
    456		}
    457		case SOF_WIDGET_UNPREPARE:
    458			sof_unprepare_widgets_in_path(sdev, widget);
    459			break;
    460		default:
    461			dev_err(sdev->dev, "Invalid widget op %d\n", op);
    462			return -EINVAL;
    463		}
    464		if (ret < 0) {
    465			dev_err(sdev->dev, "Failed to %s connected widgets\n", str);
    466			return ret;
    467		}
    468	}
    469
    470	return 0;
    471}
    472
    473int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
    474			  struct snd_pcm_hw_params *fe_params,
    475			  struct snd_sof_platform_stream_params *platform_params,
    476			  int dir)
    477{
    478	const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
    479	struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
    480	struct snd_soc_dapm_widget *widget;
    481	int i, ret;
    482
    483	/* nothing to set up */
    484	if (!list)
    485		return 0;
    486
    487	/*
    488	 * Prepare widgets for set up. The prepare step is used to allocate memory, assign
    489	 * instance ID and pick the widget configuration based on the runtime PCM params.
    490	 */
    491	ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
    492					dir, SOF_WIDGET_PREPARE);
    493	if (ret < 0)
    494		return ret;
    495
    496	/* Set up is used to send the IPC to the DSP to create the widget */
    497	ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
    498					dir, SOF_WIDGET_SETUP);
    499	if (ret < 0) {
    500		ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
    501						dir, SOF_WIDGET_UNPREPARE);
    502		return ret;
    503	}
    504
    505	/*
    506	 * error in setting pipeline connections will result in route status being reset for
    507	 * routes that were successfully set up when the widgets are freed.
    508	 */
    509	ret = sof_setup_pipeline_connections(sdev, list, dir);
    510	if (ret < 0)
    511		goto widget_free;
    512
    513	/* complete pipelines */
    514	for_each_dapm_widgets(list, i, widget) {
    515		struct snd_sof_widget *swidget = widget->dobj.private;
    516		struct snd_sof_widget *pipe_widget;
    517
    518		if (!swidget)
    519			continue;
    520
    521		pipe_widget = swidget->pipe_widget;
    522		if (!pipe_widget) {
    523			dev_err(sdev->dev, "error: no pipeline widget found for %s\n",
    524				swidget->widget->name);
    525			ret = -EINVAL;
    526			goto widget_free;
    527		}
    528
    529		if (pipe_widget->complete)
    530			continue;
    531
    532		if (ipc_tplg_ops->pipeline_complete) {
    533			pipe_widget->complete = ipc_tplg_ops->pipeline_complete(sdev, pipe_widget);
    534			if (pipe_widget->complete < 0) {
    535				ret = pipe_widget->complete;
    536				goto widget_free;
    537			}
    538		}
    539	}
    540
    541	return 0;
    542
    543widget_free:
    544	sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, dir,
    545				  SOF_WIDGET_FREE);
    546	sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
    547
    548	return ret;
    549}
    550
    551int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
    552{
    553	struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
    554	int ret;
    555
    556	/* nothing to free */
    557	if (!list)
    558		return 0;
    559
    560	/* send IPC to free widget in the DSP */
    561	ret = sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_FREE);
    562
    563	/* unprepare the widget */
    564	sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
    565
    566	snd_soc_dapm_dai_free_widgets(&list);
    567	spcm->stream[dir].list = NULL;
    568
    569	return ret;
    570}
    571
    572/*
    573 * helper to determine if there are only D0i3 compatible
    574 * streams active
    575 */
    576bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev)
    577{
    578	struct snd_pcm_substream *substream;
    579	struct snd_sof_pcm *spcm;
    580	bool d0i3_compatible_active = false;
    581	int dir;
    582
    583	list_for_each_entry(spcm, &sdev->pcm_list, list) {
    584		for_each_pcm_streams(dir) {
    585			substream = spcm->stream[dir].substream;
    586			if (!substream || !substream->runtime)
    587				continue;
    588
    589			/*
    590			 * substream->runtime being not NULL indicates
    591			 * that the stream is open. No need to check the
    592			 * stream state.
    593			 */
    594			if (!spcm->stream[dir].d0i3_compatible)
    595				return false;
    596
    597			d0i3_compatible_active = true;
    598		}
    599	}
    600
    601	return d0i3_compatible_active;
    602}
    603EXPORT_SYMBOL(snd_sof_dsp_only_d0i3_compatible_stream_active);
    604
    605bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev)
    606{
    607	struct snd_sof_pcm *spcm;
    608
    609	list_for_each_entry(spcm, &sdev->pcm_list, list) {
    610		if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].suspend_ignored ||
    611		    spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored)
    612			return true;
    613	}
    614
    615	return false;
    616}
    617
    618int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
    619			struct snd_sof_pcm *spcm, int dir, bool free_widget_list)
    620{
    621	const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
    622	int ret;
    623
    624	/* Send PCM_FREE IPC to reset pipeline */
    625	if (pcm_ops->hw_free && spcm->prepared[substream->stream]) {
    626		ret = pcm_ops->hw_free(sdev->component, substream);
    627		if (ret < 0)
    628			return ret;
    629	}
    630
    631	spcm->prepared[substream->stream] = false;
    632
    633	/* stop the DMA */
    634	ret = snd_sof_pcm_platform_hw_free(sdev, substream);
    635	if (ret < 0)
    636		return ret;
    637
    638	/* free widget list */
    639	if (free_widget_list) {
    640		ret = sof_widget_list_free(sdev, spcm, dir);
    641		if (ret < 0)
    642			dev_err(sdev->dev, "failed to free widgets during suspend\n");
    643	}
    644
    645	return ret;
    646}
    647
    648/*
    649 * Generic object lookup APIs.
    650 */
    651
    652struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp,
    653					   const char *name)
    654{
    655	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
    656	struct snd_sof_pcm *spcm;
    657
    658	list_for_each_entry(spcm, &sdev->pcm_list, list) {
    659		/* match with PCM dai name */
    660		if (strcmp(spcm->pcm.dai_name, name) == 0)
    661			return spcm;
    662
    663		/* match with playback caps name if set */
    664		if (*spcm->pcm.caps[0].name &&
    665		    !strcmp(spcm->pcm.caps[0].name, name))
    666			return spcm;
    667
    668		/* match with capture caps name if set */
    669		if (*spcm->pcm.caps[1].name &&
    670		    !strcmp(spcm->pcm.caps[1].name, name))
    671			return spcm;
    672	}
    673
    674	return NULL;
    675}
    676
    677struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp,
    678					   unsigned int comp_id,
    679					   int *direction)
    680{
    681	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
    682	struct snd_sof_pcm *spcm;
    683	int dir;
    684
    685	list_for_each_entry(spcm, &sdev->pcm_list, list) {
    686		for_each_pcm_streams(dir) {
    687			if (spcm->stream[dir].comp_id == comp_id) {
    688				*direction = dir;
    689				return spcm;
    690			}
    691		}
    692	}
    693
    694	return NULL;
    695}
    696
    697struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp,
    698					    const char *name)
    699{
    700	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
    701	struct snd_sof_widget *swidget;
    702
    703	list_for_each_entry(swidget, &sdev->widget_list, list) {
    704		if (strcmp(name, swidget->widget->name) == 0)
    705			return swidget;
    706	}
    707
    708	return NULL;
    709}
    710
    711/* find widget by stream name and direction */
    712struct snd_sof_widget *
    713snd_sof_find_swidget_sname(struct snd_soc_component *scomp,
    714			   const char *pcm_name, int dir)
    715{
    716	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
    717	struct snd_sof_widget *swidget;
    718	enum snd_soc_dapm_type type;
    719
    720	if (dir == SNDRV_PCM_STREAM_PLAYBACK)
    721		type = snd_soc_dapm_aif_in;
    722	else
    723		type = snd_soc_dapm_aif_out;
    724
    725	list_for_each_entry(swidget, &sdev->widget_list, list) {
    726		if (!strcmp(pcm_name, swidget->widget->sname) &&
    727		    swidget->id == type)
    728			return swidget;
    729	}
    730
    731	return NULL;
    732}
    733
    734struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp,
    735				     const char *name)
    736{
    737	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
    738	struct snd_sof_dai *dai;
    739
    740	list_for_each_entry(dai, &sdev->dai_list, list) {
    741		if (dai->name && (strcmp(name, dai->name) == 0))
    742			return dai;
    743	}
    744
    745	return NULL;
    746}
    747
    748static int sof_dai_get_clk(struct snd_soc_pcm_runtime *rtd, int clk_type)
    749{
    750	struct snd_soc_component *component =
    751		snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
    752	struct snd_sof_dai *dai =
    753		snd_sof_find_dai(component, (char *)rtd->dai_link->name);
    754	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
    755	const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
    756
    757	/* use the tplg configured mclk if existed */
    758	if (!dai)
    759		return 0;
    760
    761	if (tplg_ops->dai_get_clk)
    762		return tplg_ops->dai_get_clk(sdev, dai, clk_type);
    763
    764	return 0;
    765}
    766
    767/*
    768 * Helper to get SSP MCLK from a pcm_runtime.
    769 * Return 0 if not exist.
    770 */
    771int sof_dai_get_mclk(struct snd_soc_pcm_runtime *rtd)
    772{
    773	return sof_dai_get_clk(rtd, SOF_DAI_CLK_INTEL_SSP_MCLK);
    774}
    775EXPORT_SYMBOL(sof_dai_get_mclk);
    776
    777/*
    778 * Helper to get SSP BCLK from a pcm_runtime.
    779 * Return 0 if not exist.
    780 */
    781int sof_dai_get_bclk(struct snd_soc_pcm_runtime *rtd)
    782{
    783	return sof_dai_get_clk(rtd, SOF_DAI_CLK_INTEL_SSP_BCLK);
    784}
    785EXPORT_SYMBOL(sof_dai_get_bclk);
    786
    787/*
    788 * SOF Driver enumeration.
    789 */
    790int sof_machine_check(struct snd_sof_dev *sdev)
    791{
    792	struct snd_sof_pdata *sof_pdata = sdev->pdata;
    793	const struct sof_dev_desc *desc = sof_pdata->desc;
    794	struct snd_soc_acpi_mach *mach;
    795
    796	if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) {
    797
    798		/* find machine */
    799		mach = snd_sof_machine_select(sdev);
    800		if (mach) {
    801			sof_pdata->machine = mach;
    802			snd_sof_set_mach_params(mach, sdev);
    803			return 0;
    804		}
    805
    806		if (!IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)) {
    807			dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n");
    808			return -ENODEV;
    809		}
    810	} else {
    811		dev_warn(sdev->dev, "Force to use nocodec mode\n");
    812	}
    813
    814	/* select nocodec mode */
    815	dev_warn(sdev->dev, "Using nocodec machine driver\n");
    816	mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL);
    817	if (!mach)
    818		return -ENOMEM;
    819
    820	mach->drv_name = "sof-nocodec";
    821	if (!sof_pdata->tplg_filename)
    822		sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
    823
    824	sof_pdata->machine = mach;
    825	snd_sof_set_mach_params(mach, sdev);
    826
    827	return 0;
    828}
    829EXPORT_SYMBOL(sof_machine_check);
    830
    831int sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
    832{
    833	struct snd_sof_pdata *plat_data = pdata;
    834	const char *drv_name;
    835	const void *mach;
    836	int size;
    837
    838	drv_name = plat_data->machine->drv_name;
    839	mach = plat_data->machine;
    840	size = sizeof(*plat_data->machine);
    841
    842	/* register machine driver, pass machine info as pdata */
    843	plat_data->pdev_mach =
    844		platform_device_register_data(sdev->dev, drv_name,
    845					      PLATFORM_DEVID_NONE, mach, size);
    846	if (IS_ERR(plat_data->pdev_mach))
    847		return PTR_ERR(plat_data->pdev_mach);
    848
    849	dev_dbg(sdev->dev, "created machine %s\n",
    850		dev_name(&plat_data->pdev_mach->dev));
    851
    852	return 0;
    853}
    854EXPORT_SYMBOL(sof_machine_register);
    855
    856void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata)
    857{
    858	struct snd_sof_pdata *plat_data = pdata;
    859
    860	if (!IS_ERR_OR_NULL(plat_data->pdev_mach))
    861		platform_device_unregister(plat_data->pdev_mach);
    862}
    863EXPORT_SYMBOL(sof_machine_unregister);