Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
0002 //
0003 // This file is provided under a dual BSD/GPLv2 license.  When using or
0004 // redistributing this file, you may do so under either license.
0005 //
0006 // Copyright(c) 2019 Intel Corporation. All rights reserved.
0007 //
0008 // Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
0009 //
0010 
0011 #include <linux/bitfield.h>
0012 #include "sof-audio.h"
0013 #include "ops.h"
0014 
0015 static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget)
0016 {
0017     const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
0018     struct snd_sof_route *sroute;
0019 
0020     list_for_each_entry(sroute, &sdev->route_list, list)
0021         if (sroute->src_widget == widget || sroute->sink_widget == widget) {
0022             if (sroute->setup && tplg_ops->route_free)
0023                 tplg_ops->route_free(sdev, sroute);
0024 
0025             sroute->setup = false;
0026         }
0027 }
0028 
0029 int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
0030 {
0031     const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
0032     int err = 0;
0033     int ret;
0034 
0035     if (!swidget->private)
0036         return 0;
0037 
0038     /* only free when use_count is 0 */
0039     if (--swidget->use_count)
0040         return 0;
0041 
0042     /* reset route setup status for all routes that contain this widget */
0043     sof_reset_route_setup_status(sdev, swidget);
0044 
0045     /* continue to disable core even if IPC fails */
0046     if (tplg_ops->widget_free)
0047         err = tplg_ops->widget_free(sdev, swidget);
0048 
0049     /*
0050      * disable widget core. continue to route setup status and complete flag
0051      * even if this fails and return the appropriate error
0052      */
0053     ret = snd_sof_dsp_core_put(sdev, swidget->core);
0054     if (ret < 0) {
0055         dev_err(sdev->dev, "error: failed to disable target core: %d for widget %s\n",
0056             swidget->core, swidget->widget->name);
0057         if (!err)
0058             err = ret;
0059     }
0060 
0061     /*
0062      * free the scheduler widget (same as pipe_widget) associated with the current swidget.
0063      * skip for static pipelines
0064      */
0065     if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {
0066         ret = sof_widget_free(sdev, swidget->pipe_widget);
0067         if (ret < 0 && !err)
0068             err = ret;
0069         swidget->pipe_widget->complete = 0;
0070     }
0071 
0072     if (!err)
0073         dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name);
0074 
0075     return err;
0076 }
0077 EXPORT_SYMBOL(sof_widget_free);
0078 
0079 int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
0080 {
0081     const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
0082     int ret;
0083 
0084     /* skip if there is no private data */
0085     if (!swidget->private)
0086         return 0;
0087 
0088     /* widget already set up */
0089     if (++swidget->use_count > 1)
0090         return 0;
0091 
0092     /*
0093      * The scheduler widget for a pipeline is not part of the connected DAPM
0094      * widget list and it needs to be set up before the widgets in the pipeline
0095      * are set up. The use_count for the scheduler widget is incremented for every
0096      * widget in a given pipeline to ensure that it is freed only after the last
0097      * widget in the pipeline is freed. Skip setting up scheduler widget for static pipelines.
0098      */
0099     if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {
0100         if (!swidget->pipe_widget) {
0101             dev_err(sdev->dev, "No scheduler widget set for %s\n",
0102                 swidget->widget->name);
0103             ret = -EINVAL;
0104             goto use_count_dec;
0105         }
0106 
0107         ret = sof_widget_setup(sdev, swidget->pipe_widget);
0108         if (ret < 0)
0109             goto use_count_dec;
0110     }
0111 
0112     /* enable widget core */
0113     ret = snd_sof_dsp_core_get(sdev, swidget->core);
0114     if (ret < 0) {
0115         dev_err(sdev->dev, "error: failed to enable target core for widget %s\n",
0116             swidget->widget->name);
0117         goto pipe_widget_free;
0118     }
0119 
0120     /* setup widget in the DSP */
0121     if (tplg_ops->widget_setup) {
0122         ret = tplg_ops->widget_setup(sdev, swidget);
0123         if (ret < 0)
0124             goto core_put;
0125     }
0126 
0127     /* send config for DAI components */
0128     if (WIDGET_IS_DAI(swidget->id)) {
0129         unsigned int flags = SOF_DAI_CONFIG_FLAGS_NONE;
0130 
0131         if (tplg_ops->dai_config) {
0132             ret = tplg_ops->dai_config(sdev, swidget, flags, NULL);
0133             if (ret < 0)
0134                 goto widget_free;
0135         }
0136     }
0137 
0138     /* restore kcontrols for widget */
0139     if (tplg_ops->control->widget_kcontrol_setup) {
0140         ret = tplg_ops->control->widget_kcontrol_setup(sdev, swidget);
0141         if (ret < 0)
0142             goto widget_free;
0143     }
0144 
0145     dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name);
0146 
0147     return 0;
0148 
0149 widget_free:
0150     /* widget use_count and core ref_count will both be decremented by sof_widget_free() */
0151     sof_widget_free(sdev, swidget);
0152 core_put:
0153     snd_sof_dsp_core_put(sdev, swidget->core);
0154 pipe_widget_free:
0155     if (swidget->id != snd_soc_dapm_scheduler)
0156         sof_widget_free(sdev, swidget->pipe_widget);
0157 use_count_dec:
0158     swidget->use_count--;
0159     return ret;
0160 }
0161 EXPORT_SYMBOL(sof_widget_setup);
0162 
0163 int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource,
0164             struct snd_soc_dapm_widget *wsink)
0165 {
0166     const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
0167     struct snd_sof_widget *src_widget = wsource->dobj.private;
0168     struct snd_sof_widget *sink_widget = wsink->dobj.private;
0169     struct snd_sof_route *sroute;
0170     bool route_found = false;
0171     int ret;
0172 
0173     /* ignore routes involving virtual widgets in topology */
0174     switch (src_widget->id) {
0175     case snd_soc_dapm_out_drv:
0176     case snd_soc_dapm_output:
0177     case snd_soc_dapm_input:
0178         return 0;
0179     default:
0180         break;
0181     }
0182 
0183     switch (sink_widget->id) {
0184     case snd_soc_dapm_out_drv:
0185     case snd_soc_dapm_output:
0186     case snd_soc_dapm_input:
0187         return 0;
0188     default:
0189         break;
0190     }
0191 
0192     /* find route matching source and sink widgets */
0193     list_for_each_entry(sroute, &sdev->route_list, list)
0194         if (sroute->src_widget == src_widget && sroute->sink_widget == sink_widget) {
0195             route_found = true;
0196             break;
0197         }
0198 
0199     if (!route_found) {
0200         dev_err(sdev->dev, "error: cannot find SOF route for source %s -> %s sink\n",
0201             wsource->name, wsink->name);
0202         return -EINVAL;
0203     }
0204 
0205     /* nothing to do if route is already set up */
0206     if (sroute->setup)
0207         return 0;
0208 
0209     ret = ipc_tplg_ops->route_setup(sdev, sroute);
0210     if (ret < 0)
0211         return ret;
0212 
0213     sroute->setup = true;
0214     return 0;
0215 }
0216 
0217 static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
0218                       struct snd_soc_dapm_widget_list *list, int dir)
0219 {
0220     struct snd_soc_dapm_widget *widget;
0221     struct snd_soc_dapm_path *p;
0222     int ret;
0223     int i;
0224 
0225     /*
0226      * Set up connections between widgets in the sink/source paths based on direction.
0227      * Some non-SOF widgets exist in topology either for compatibility or for the
0228      * purpose of connecting a pipeline from a host to a DAI in order to receive the DAPM
0229      * events. But they are not handled by the firmware. So ignore them.
0230      */
0231     if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
0232         for_each_dapm_widgets(list, i, widget) {
0233             if (!widget->dobj.private)
0234                 continue;
0235 
0236             snd_soc_dapm_widget_for_each_sink_path(widget, p)
0237                 if (p->sink->dobj.private) {
0238                     ret = sof_route_setup(sdev, widget, p->sink);
0239                     if (ret < 0)
0240                         return ret;
0241                 }
0242         }
0243     } else {
0244         for_each_dapm_widgets(list, i, widget) {
0245             if (!widget->dobj.private)
0246                 continue;
0247 
0248             snd_soc_dapm_widget_for_each_source_path(widget, p)
0249                 if (p->source->dobj.private) {
0250                     ret = sof_route_setup(sdev, p->source, widget);
0251                     if (ret < 0)
0252                         return ret;
0253                 }
0254         }
0255     }
0256 
0257     return 0;
0258 }
0259 
0260 static void
0261 sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget)
0262 {
0263     const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
0264     const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
0265     struct snd_sof_widget *swidget = widget->dobj.private;
0266     struct snd_soc_dapm_path *p;
0267 
0268     if (!widget_ops[widget->id].ipc_unprepare || !swidget->prepared)
0269         goto sink_unprepare;
0270 
0271     /* unprepare the source widget */
0272     widget_ops[widget->id].ipc_unprepare(swidget);
0273     swidget->prepared = false;
0274 
0275 sink_unprepare:
0276     /* unprepare all widgets in the sink paths */
0277     snd_soc_dapm_widget_for_each_sink_path(widget, p) {
0278         if (!p->walking && p->sink->dobj.private) {
0279             p->walking = true;
0280             sof_unprepare_widgets_in_path(sdev, p->sink);
0281             p->walking = false;
0282         }
0283     }
0284 }
0285 
0286 static int
0287 sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
0288                 struct snd_pcm_hw_params *fe_params,
0289                 struct snd_sof_platform_stream_params *platform_params,
0290                 struct snd_pcm_hw_params *pipeline_params, int dir)
0291 {
0292     const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
0293     const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
0294     struct snd_sof_widget *swidget = widget->dobj.private;
0295     struct snd_soc_dapm_path *p;
0296     int ret;
0297 
0298     if (!widget_ops[widget->id].ipc_prepare || swidget->prepared)
0299         goto sink_prepare;
0300 
0301     /* prepare the source widget */
0302     ret = widget_ops[widget->id].ipc_prepare(swidget, fe_params, platform_params,
0303                          pipeline_params, dir);
0304     if (ret < 0) {
0305         dev_err(sdev->dev, "failed to prepare widget %s\n", widget->name);
0306         return ret;
0307     }
0308 
0309     swidget->prepared = true;
0310 
0311 sink_prepare:
0312     /* prepare all widgets in the sink paths */
0313     snd_soc_dapm_widget_for_each_sink_path(widget, p) {
0314         if (!p->walking && p->sink->dobj.private) {
0315             p->walking = true;
0316             ret = sof_prepare_widgets_in_path(sdev, p->sink,  fe_params,
0317                               platform_params, pipeline_params, dir);
0318             p->walking = false;
0319             if (ret < 0) {
0320                 /* unprepare the source widget */
0321                 if (widget_ops[widget->id].ipc_unprepare && swidget->prepared) {
0322                     widget_ops[widget->id].ipc_unprepare(swidget);
0323                     swidget->prepared = false;
0324                 }
0325                 return ret;
0326             }
0327         }
0328     }
0329 
0330     return 0;
0331 }
0332 
0333 /*
0334  * free all widgets in the sink path starting from the source widget
0335  * (DAI type for capture, AIF type for playback)
0336  */
0337 static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
0338                     int dir)
0339 {
0340     struct snd_soc_dapm_path *p;
0341     int err;
0342     int ret = 0;
0343 
0344     /* free all widgets even in case of error to keep use counts balanced */
0345     snd_soc_dapm_widget_for_each_sink_path(widget, p) {
0346         if (!p->walking && p->sink->dobj.private && widget->dobj.private) {
0347             p->walking = true;
0348             if (WIDGET_IS_AIF_OR_DAI(widget->id)) {
0349                 err = sof_widget_free(sdev, widget->dobj.private);
0350                 if (err < 0)
0351                     ret = err;
0352             }
0353 
0354             err = sof_widget_free(sdev, p->sink->dobj.private);
0355             if (err < 0)
0356                 ret = err;
0357 
0358             err = sof_free_widgets_in_path(sdev, p->sink, dir);
0359             if (err < 0)
0360                 ret = err;
0361             p->walking = false;
0362         }
0363     }
0364 
0365     return ret;
0366 }
0367 
0368 /*
0369  * set up all widgets in the sink path starting from the source widget
0370  * (DAI type for capture, AIF type for playback).
0371  * The error path in this function ensures that all successfully set up widgets getting freed.
0372  */
0373 static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
0374                       int dir)
0375 {
0376     struct snd_soc_dapm_path *p;
0377     int ret;
0378 
0379     snd_soc_dapm_widget_for_each_sink_path(widget, p) {
0380         if (!p->walking && p->sink->dobj.private && widget->dobj.private) {
0381             p->walking = true;
0382             if (WIDGET_IS_AIF_OR_DAI(widget->id)) {
0383                 ret = sof_widget_setup(sdev, widget->dobj.private);
0384                 if (ret < 0)
0385                     goto out;
0386             }
0387 
0388             ret = sof_widget_setup(sdev, p->sink->dobj.private);
0389             if (ret < 0) {
0390                 if (WIDGET_IS_AIF_OR_DAI(widget->id))
0391                     sof_widget_free(sdev, widget->dobj.private);
0392                 goto out;
0393             }
0394 
0395             ret = sof_set_up_widgets_in_path(sdev, p->sink, dir);
0396             if (ret < 0) {
0397                 if (WIDGET_IS_AIF_OR_DAI(widget->id))
0398                     sof_widget_free(sdev, widget->dobj.private);
0399                 sof_widget_free(sdev, p->sink->dobj.private);
0400             }
0401 out:
0402             p->walking = false;
0403             if (ret < 0)
0404                 return ret;
0405         }
0406     }
0407 
0408     return 0;
0409 }
0410 
0411 static int
0412 sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list,
0413               struct snd_pcm_hw_params *fe_params,
0414               struct snd_sof_platform_stream_params *platform_params, int dir,
0415               enum sof_widget_op op)
0416 {
0417     struct snd_soc_dapm_widget *widget;
0418     char *str;
0419     int ret = 0;
0420     int i;
0421 
0422     for_each_dapm_widgets(list, i, widget) {
0423         /* starting widget for playback is AIF type */
0424         if (dir == SNDRV_PCM_STREAM_PLAYBACK && !WIDGET_IS_AIF(widget->id))
0425             continue;
0426 
0427         /* starting widget for capture is DAI type */
0428         if (dir == SNDRV_PCM_STREAM_CAPTURE && !WIDGET_IS_DAI(widget->id))
0429             continue;
0430 
0431         switch (op) {
0432         case SOF_WIDGET_SETUP:
0433             ret = sof_set_up_widgets_in_path(sdev, widget, dir);
0434             str = "set up";
0435             break;
0436         case SOF_WIDGET_FREE:
0437             ret = sof_free_widgets_in_path(sdev, widget, dir);
0438             str = "free";
0439             break;
0440         case SOF_WIDGET_PREPARE:
0441         {
0442             struct snd_pcm_hw_params pipeline_params;
0443 
0444             str = "prepare";
0445             /*
0446              * When walking the list of connected widgets, the pipeline_params for each
0447              * widget is modified by the source widget in the path. Use a local
0448              * copy of the runtime params as the pipeline_params so that the runtime
0449              * params does not get overwritten.
0450              */
0451             memcpy(&pipeline_params, fe_params, sizeof(*fe_params));
0452 
0453             ret = sof_prepare_widgets_in_path(sdev, widget, fe_params,
0454                               platform_params, &pipeline_params, dir);
0455             break;
0456         }
0457         case SOF_WIDGET_UNPREPARE:
0458             sof_unprepare_widgets_in_path(sdev, widget);
0459             break;
0460         default:
0461             dev_err(sdev->dev, "Invalid widget op %d\n", op);
0462             return -EINVAL;
0463         }
0464         if (ret < 0) {
0465             dev_err(sdev->dev, "Failed to %s connected widgets\n", str);
0466             return ret;
0467         }
0468     }
0469 
0470     return 0;
0471 }
0472 
0473 int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
0474               struct snd_pcm_hw_params *fe_params,
0475               struct snd_sof_platform_stream_params *platform_params,
0476               int dir)
0477 {
0478     const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
0479     struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
0480     struct snd_soc_dapm_widget *widget;
0481     int i, ret;
0482 
0483     /* nothing to set up */
0484     if (!list)
0485         return 0;
0486 
0487     /*
0488      * Prepare widgets for set up. The prepare step is used to allocate memory, assign
0489      * instance ID and pick the widget configuration based on the runtime PCM params.
0490      */
0491     ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
0492                     dir, SOF_WIDGET_PREPARE);
0493     if (ret < 0)
0494         return ret;
0495 
0496     /* Set up is used to send the IPC to the DSP to create the widget */
0497     ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
0498                     dir, SOF_WIDGET_SETUP);
0499     if (ret < 0) {
0500         ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
0501                         dir, SOF_WIDGET_UNPREPARE);
0502         return ret;
0503     }
0504 
0505     /*
0506      * error in setting pipeline connections will result in route status being reset for
0507      * routes that were successfully set up when the widgets are freed.
0508      */
0509     ret = sof_setup_pipeline_connections(sdev, list, dir);
0510     if (ret < 0)
0511         goto widget_free;
0512 
0513     /* complete pipelines */
0514     for_each_dapm_widgets(list, i, widget) {
0515         struct snd_sof_widget *swidget = widget->dobj.private;
0516         struct snd_sof_widget *pipe_widget;
0517 
0518         if (!swidget)
0519             continue;
0520 
0521         pipe_widget = swidget->pipe_widget;
0522         if (!pipe_widget) {
0523             dev_err(sdev->dev, "error: no pipeline widget found for %s\n",
0524                 swidget->widget->name);
0525             ret = -EINVAL;
0526             goto widget_free;
0527         }
0528 
0529         if (pipe_widget->complete)
0530             continue;
0531 
0532         if (ipc_tplg_ops->pipeline_complete) {
0533             pipe_widget->complete = ipc_tplg_ops->pipeline_complete(sdev, pipe_widget);
0534             if (pipe_widget->complete < 0) {
0535                 ret = pipe_widget->complete;
0536                 goto widget_free;
0537             }
0538         }
0539     }
0540 
0541     return 0;
0542 
0543 widget_free:
0544     sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, dir,
0545                   SOF_WIDGET_FREE);
0546     sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
0547 
0548     return ret;
0549 }
0550 
0551 int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
0552 {
0553     struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
0554     int ret;
0555 
0556     /* nothing to free */
0557     if (!list)
0558         return 0;
0559 
0560     /* send IPC to free widget in the DSP */
0561     ret = sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_FREE);
0562 
0563     /* unprepare the widget */
0564     sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
0565 
0566     snd_soc_dapm_dai_free_widgets(&list);
0567     spcm->stream[dir].list = NULL;
0568 
0569     return ret;
0570 }
0571 
0572 /*
0573  * helper to determine if there are only D0i3 compatible
0574  * streams active
0575  */
0576 bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev)
0577 {
0578     struct snd_pcm_substream *substream;
0579     struct snd_sof_pcm *spcm;
0580     bool d0i3_compatible_active = false;
0581     int dir;
0582 
0583     list_for_each_entry(spcm, &sdev->pcm_list, list) {
0584         for_each_pcm_streams(dir) {
0585             substream = spcm->stream[dir].substream;
0586             if (!substream || !substream->runtime)
0587                 continue;
0588 
0589             /*
0590              * substream->runtime being not NULL indicates
0591              * that the stream is open. No need to check the
0592              * stream state.
0593              */
0594             if (!spcm->stream[dir].d0i3_compatible)
0595                 return false;
0596 
0597             d0i3_compatible_active = true;
0598         }
0599     }
0600 
0601     return d0i3_compatible_active;
0602 }
0603 EXPORT_SYMBOL(snd_sof_dsp_only_d0i3_compatible_stream_active);
0604 
0605 bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev)
0606 {
0607     struct snd_sof_pcm *spcm;
0608 
0609     list_for_each_entry(spcm, &sdev->pcm_list, list) {
0610         if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].suspend_ignored ||
0611             spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored)
0612             return true;
0613     }
0614 
0615     return false;
0616 }
0617 
0618 int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
0619             struct snd_sof_pcm *spcm, int dir, bool free_widget_list)
0620 {
0621     const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
0622     int ret;
0623 
0624     /* Send PCM_FREE IPC to reset pipeline */
0625     if (pcm_ops->hw_free && spcm->prepared[substream->stream]) {
0626         ret = pcm_ops->hw_free(sdev->component, substream);
0627         if (ret < 0)
0628             return ret;
0629     }
0630 
0631     spcm->prepared[substream->stream] = false;
0632 
0633     /* stop the DMA */
0634     ret = snd_sof_pcm_platform_hw_free(sdev, substream);
0635     if (ret < 0)
0636         return ret;
0637 
0638     /* free widget list */
0639     if (free_widget_list) {
0640         ret = sof_widget_list_free(sdev, spcm, dir);
0641         if (ret < 0)
0642             dev_err(sdev->dev, "failed to free widgets during suspend\n");
0643     }
0644 
0645     return ret;
0646 }
0647 
0648 /*
0649  * Generic object lookup APIs.
0650  */
0651 
0652 struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp,
0653                        const char *name)
0654 {
0655     struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
0656     struct snd_sof_pcm *spcm;
0657 
0658     list_for_each_entry(spcm, &sdev->pcm_list, list) {
0659         /* match with PCM dai name */
0660         if (strcmp(spcm->pcm.dai_name, name) == 0)
0661             return spcm;
0662 
0663         /* match with playback caps name if set */
0664         if (*spcm->pcm.caps[0].name &&
0665             !strcmp(spcm->pcm.caps[0].name, name))
0666             return spcm;
0667 
0668         /* match with capture caps name if set */
0669         if (*spcm->pcm.caps[1].name &&
0670             !strcmp(spcm->pcm.caps[1].name, name))
0671             return spcm;
0672     }
0673 
0674     return NULL;
0675 }
0676 
0677 struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp,
0678                        unsigned int comp_id,
0679                        int *direction)
0680 {
0681     struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
0682     struct snd_sof_pcm *spcm;
0683     int dir;
0684 
0685     list_for_each_entry(spcm, &sdev->pcm_list, list) {
0686         for_each_pcm_streams(dir) {
0687             if (spcm->stream[dir].comp_id == comp_id) {
0688                 *direction = dir;
0689                 return spcm;
0690             }
0691         }
0692     }
0693 
0694     return NULL;
0695 }
0696 
0697 struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp,
0698                         const char *name)
0699 {
0700     struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
0701     struct snd_sof_widget *swidget;
0702 
0703     list_for_each_entry(swidget, &sdev->widget_list, list) {
0704         if (strcmp(name, swidget->widget->name) == 0)
0705             return swidget;
0706     }
0707 
0708     return NULL;
0709 }
0710 
0711 /* find widget by stream name and direction */
0712 struct snd_sof_widget *
0713 snd_sof_find_swidget_sname(struct snd_soc_component *scomp,
0714                const char *pcm_name, int dir)
0715 {
0716     struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
0717     struct snd_sof_widget *swidget;
0718     enum snd_soc_dapm_type type;
0719 
0720     if (dir == SNDRV_PCM_STREAM_PLAYBACK)
0721         type = snd_soc_dapm_aif_in;
0722     else
0723         type = snd_soc_dapm_aif_out;
0724 
0725     list_for_each_entry(swidget, &sdev->widget_list, list) {
0726         if (!strcmp(pcm_name, swidget->widget->sname) &&
0727             swidget->id == type)
0728             return swidget;
0729     }
0730 
0731     return NULL;
0732 }
0733 
0734 struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp,
0735                      const char *name)
0736 {
0737     struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
0738     struct snd_sof_dai *dai;
0739 
0740     list_for_each_entry(dai, &sdev->dai_list, list) {
0741         if (dai->name && (strcmp(name, dai->name) == 0))
0742             return dai;
0743     }
0744 
0745     return NULL;
0746 }
0747 
0748 static int sof_dai_get_clk(struct snd_soc_pcm_runtime *rtd, int clk_type)
0749 {
0750     struct snd_soc_component *component =
0751         snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
0752     struct snd_sof_dai *dai =
0753         snd_sof_find_dai(component, (char *)rtd->dai_link->name);
0754     struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
0755     const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
0756 
0757     /* use the tplg configured mclk if existed */
0758     if (!dai)
0759         return 0;
0760 
0761     if (tplg_ops->dai_get_clk)
0762         return tplg_ops->dai_get_clk(sdev, dai, clk_type);
0763 
0764     return 0;
0765 }
0766 
0767 /*
0768  * Helper to get SSP MCLK from a pcm_runtime.
0769  * Return 0 if not exist.
0770  */
0771 int sof_dai_get_mclk(struct snd_soc_pcm_runtime *rtd)
0772 {
0773     return sof_dai_get_clk(rtd, SOF_DAI_CLK_INTEL_SSP_MCLK);
0774 }
0775 EXPORT_SYMBOL(sof_dai_get_mclk);
0776 
0777 /*
0778  * Helper to get SSP BCLK from a pcm_runtime.
0779  * Return 0 if not exist.
0780  */
0781 int sof_dai_get_bclk(struct snd_soc_pcm_runtime *rtd)
0782 {
0783     return sof_dai_get_clk(rtd, SOF_DAI_CLK_INTEL_SSP_BCLK);
0784 }
0785 EXPORT_SYMBOL(sof_dai_get_bclk);
0786 
0787 /*
0788  * SOF Driver enumeration.
0789  */
0790 int sof_machine_check(struct snd_sof_dev *sdev)
0791 {
0792     struct snd_sof_pdata *sof_pdata = sdev->pdata;
0793     const struct sof_dev_desc *desc = sof_pdata->desc;
0794     struct snd_soc_acpi_mach *mach;
0795 
0796     if (!IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE)) {
0797 
0798         /* find machine */
0799         mach = snd_sof_machine_select(sdev);
0800         if (mach) {
0801             sof_pdata->machine = mach;
0802             snd_sof_set_mach_params(mach, sdev);
0803             return 0;
0804         }
0805 
0806         if (!IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC)) {
0807             dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n");
0808             return -ENODEV;
0809         }
0810     } else {
0811         dev_warn(sdev->dev, "Force to use nocodec mode\n");
0812     }
0813 
0814     /* select nocodec mode */
0815     dev_warn(sdev->dev, "Using nocodec machine driver\n");
0816     mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL);
0817     if (!mach)
0818         return -ENOMEM;
0819 
0820     mach->drv_name = "sof-nocodec";
0821     if (!sof_pdata->tplg_filename)
0822         sof_pdata->tplg_filename = desc->nocodec_tplg_filename;
0823 
0824     sof_pdata->machine = mach;
0825     snd_sof_set_mach_params(mach, sdev);
0826 
0827     return 0;
0828 }
0829 EXPORT_SYMBOL(sof_machine_check);
0830 
0831 int sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
0832 {
0833     struct snd_sof_pdata *plat_data = pdata;
0834     const char *drv_name;
0835     const void *mach;
0836     int size;
0837 
0838     drv_name = plat_data->machine->drv_name;
0839     mach = plat_data->machine;
0840     size = sizeof(*plat_data->machine);
0841 
0842     /* register machine driver, pass machine info as pdata */
0843     plat_data->pdev_mach =
0844         platform_device_register_data(sdev->dev, drv_name,
0845                           PLATFORM_DEVID_NONE, mach, size);
0846     if (IS_ERR(plat_data->pdev_mach))
0847         return PTR_ERR(plat_data->pdev_mach);
0848 
0849     dev_dbg(sdev->dev, "created machine %s\n",
0850         dev_name(&plat_data->pdev_mach->dev));
0851 
0852     return 0;
0853 }
0854 EXPORT_SYMBOL(sof_machine_register);
0855 
0856 void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata)
0857 {
0858     struct snd_sof_pdata *plat_data = pdata;
0859 
0860     if (!IS_ERR_OR_NULL(plat_data->pdev_mach))
0861         platform_device_unregister(plat_data->pdev_mach);
0862 }
0863 EXPORT_SYMBOL(sof_machine_unregister);