18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 48c2ecf20Sopenharmony_ci// redistributing this file, you may do so under either license. 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci// Copyright(c) 2019 Intel Corporation. All rights reserved. 78c2ecf20Sopenharmony_ci// 88c2ecf20Sopenharmony_ci// Author: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> 98c2ecf20Sopenharmony_ci// 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "sof-audio.h" 128c2ecf20Sopenharmony_ci#include "ops.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* 158c2ecf20Sopenharmony_ci * helper to determine if there are only D0i3 compatible 168c2ecf20Sopenharmony_ci * streams active 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_cibool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 218c2ecf20Sopenharmony_ci struct snd_sof_pcm *spcm; 228c2ecf20Sopenharmony_ci bool d0i3_compatible_active = false; 238c2ecf20Sopenharmony_ci int dir; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci list_for_each_entry(spcm, &sdev->pcm_list, list) { 268c2ecf20Sopenharmony_ci for_each_pcm_streams(dir) { 278c2ecf20Sopenharmony_ci substream = spcm->stream[dir].substream; 288c2ecf20Sopenharmony_ci if (!substream || !substream->runtime) 298c2ecf20Sopenharmony_ci continue; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci /* 328c2ecf20Sopenharmony_ci * substream->runtime being not NULL indicates 338c2ecf20Sopenharmony_ci * that the stream is open. No need to check the 348c2ecf20Sopenharmony_ci * stream state. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci if (!spcm->stream[dir].d0i3_compatible) 378c2ecf20Sopenharmony_ci return false; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci d0i3_compatible_active = true; 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci return d0i3_compatible_active; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sof_dsp_only_d0i3_compatible_stream_active); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cibool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct snd_sof_pcm *spcm; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci list_for_each_entry(spcm, &sdev->pcm_list, list) { 528c2ecf20Sopenharmony_ci if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].suspend_ignored || 538c2ecf20Sopenharmony_ci spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored) 548c2ecf20Sopenharmony_ci return true; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci return false; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ciint sof_set_hw_params_upon_resume(struct device *dev) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct snd_sof_dev *sdev = dev_get_drvdata(dev); 638c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 648c2ecf20Sopenharmony_ci struct snd_sof_pcm *spcm; 658c2ecf20Sopenharmony_ci snd_pcm_state_t state; 668c2ecf20Sopenharmony_ci int dir; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* 698c2ecf20Sopenharmony_ci * SOF requires hw_params to be set-up internally upon resume. 708c2ecf20Sopenharmony_ci * So, set the flag to indicate this for those streams that 718c2ecf20Sopenharmony_ci * have been suspended. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci list_for_each_entry(spcm, &sdev->pcm_list, list) { 748c2ecf20Sopenharmony_ci for_each_pcm_streams(dir) { 758c2ecf20Sopenharmony_ci /* 768c2ecf20Sopenharmony_ci * do not reset hw_params upon resume for streams that 778c2ecf20Sopenharmony_ci * were kept running during suspend 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_ci if (spcm->stream[dir].suspend_ignored) 808c2ecf20Sopenharmony_ci continue; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci substream = spcm->stream[dir].substream; 838c2ecf20Sopenharmony_ci if (!substream || !substream->runtime) 848c2ecf20Sopenharmony_ci continue; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci state = substream->runtime->status->state; 878c2ecf20Sopenharmony_ci if (state == SNDRV_PCM_STATE_SUSPENDED) 888c2ecf20Sopenharmony_ci spcm->prepared[dir] = false; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* set internal flag for BE */ 938c2ecf20Sopenharmony_ci return snd_sof_dsp_hw_params_upon_resume(sdev); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic int sof_restore_kcontrols(struct device *dev) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct snd_sof_dev *sdev = dev_get_drvdata(dev); 998c2ecf20Sopenharmony_ci struct snd_sof_control *scontrol; 1008c2ecf20Sopenharmony_ci int ipc_cmd, ctrl_type; 1018c2ecf20Sopenharmony_ci int ret = 0; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* restore kcontrol values */ 1048c2ecf20Sopenharmony_ci list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { 1058c2ecf20Sopenharmony_ci /* reset readback offset for scontrol after resuming */ 1068c2ecf20Sopenharmony_ci scontrol->readback_offset = 0; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* notify DSP of kcontrol values */ 1098c2ecf20Sopenharmony_ci switch (scontrol->cmd) { 1108c2ecf20Sopenharmony_ci case SOF_CTRL_CMD_VOLUME: 1118c2ecf20Sopenharmony_ci case SOF_CTRL_CMD_ENUM: 1128c2ecf20Sopenharmony_ci case SOF_CTRL_CMD_SWITCH: 1138c2ecf20Sopenharmony_ci ipc_cmd = SOF_IPC_COMP_SET_VALUE; 1148c2ecf20Sopenharmony_ci ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET; 1158c2ecf20Sopenharmony_ci ret = snd_sof_ipc_set_get_comp_data(scontrol, 1168c2ecf20Sopenharmony_ci ipc_cmd, ctrl_type, 1178c2ecf20Sopenharmony_ci scontrol->cmd, 1188c2ecf20Sopenharmony_ci true); 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci case SOF_CTRL_CMD_BINARY: 1218c2ecf20Sopenharmony_ci ipc_cmd = SOF_IPC_COMP_SET_DATA; 1228c2ecf20Sopenharmony_ci ctrl_type = SOF_CTRL_TYPE_DATA_SET; 1238c2ecf20Sopenharmony_ci ret = snd_sof_ipc_set_get_comp_data(scontrol, 1248c2ecf20Sopenharmony_ci ipc_cmd, ctrl_type, 1258c2ecf20Sopenharmony_ci scontrol->cmd, 1268c2ecf20Sopenharmony_ci true); 1278c2ecf20Sopenharmony_ci break; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci default: 1308c2ecf20Sopenharmony_ci break; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (ret < 0) { 1348c2ecf20Sopenharmony_ci dev_err(dev, 1358c2ecf20Sopenharmony_ci "error: failed kcontrol value set for widget: %d\n", 1368c2ecf20Sopenharmony_ci scontrol->comp_id); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return ret; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return 0; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ciconst struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev, 1468c2ecf20Sopenharmony_ci int pipeline_id) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci const struct snd_sof_widget *swidget; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci list_for_each_entry(swidget, &sdev->widget_list, list) 1518c2ecf20Sopenharmony_ci if (swidget->id == snd_soc_dapm_scheduler) { 1528c2ecf20Sopenharmony_ci const struct sof_ipc_pipe_new *pipeline = 1538c2ecf20Sopenharmony_ci swidget->private; 1548c2ecf20Sopenharmony_ci if (pipeline->pipeline_id == pipeline_id) 1558c2ecf20Sopenharmony_ci return pipeline; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return NULL; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ciint sof_restore_pipelines(struct device *dev) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct snd_sof_dev *sdev = dev_get_drvdata(dev); 1648c2ecf20Sopenharmony_ci struct snd_sof_widget *swidget; 1658c2ecf20Sopenharmony_ci struct snd_sof_route *sroute; 1668c2ecf20Sopenharmony_ci struct sof_ipc_pipe_new *pipeline; 1678c2ecf20Sopenharmony_ci struct snd_sof_dai *dai; 1688c2ecf20Sopenharmony_ci struct sof_ipc_cmd_hdr *hdr; 1698c2ecf20Sopenharmony_ci struct sof_ipc_comp *comp; 1708c2ecf20Sopenharmony_ci size_t ipc_size; 1718c2ecf20Sopenharmony_ci int ret; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* restore pipeline components */ 1748c2ecf20Sopenharmony_ci list_for_each_entry_reverse(swidget, &sdev->widget_list, list) { 1758c2ecf20Sopenharmony_ci struct sof_ipc_comp_reply r; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* skip if there is no private data */ 1788c2ecf20Sopenharmony_ci if (!swidget->private) 1798c2ecf20Sopenharmony_ci continue; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci ret = sof_pipeline_core_enable(sdev, swidget); 1828c2ecf20Sopenharmony_ci if (ret < 0) { 1838c2ecf20Sopenharmony_ci dev_err(dev, 1848c2ecf20Sopenharmony_ci "error: failed to enable target core: %d\n", 1858c2ecf20Sopenharmony_ci ret); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return ret; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci switch (swidget->id) { 1918c2ecf20Sopenharmony_ci case snd_soc_dapm_dai_in: 1928c2ecf20Sopenharmony_ci case snd_soc_dapm_dai_out: 1938c2ecf20Sopenharmony_ci ipc_size = sizeof(struct sof_ipc_comp_dai) + 1948c2ecf20Sopenharmony_ci sizeof(struct sof_ipc_comp_ext); 1958c2ecf20Sopenharmony_ci comp = kzalloc(ipc_size, GFP_KERNEL); 1968c2ecf20Sopenharmony_ci if (!comp) 1978c2ecf20Sopenharmony_ci return -ENOMEM; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci dai = swidget->private; 2008c2ecf20Sopenharmony_ci memcpy(comp, &dai->comp_dai, 2018c2ecf20Sopenharmony_ci sizeof(struct sof_ipc_comp_dai)); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* append extended data to the end of the component */ 2048c2ecf20Sopenharmony_ci memcpy((u8 *)comp + sizeof(struct sof_ipc_comp_dai), 2058c2ecf20Sopenharmony_ci &swidget->comp_ext, sizeof(swidget->comp_ext)); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, 2088c2ecf20Sopenharmony_ci comp, ipc_size, 2098c2ecf20Sopenharmony_ci &r, sizeof(r)); 2108c2ecf20Sopenharmony_ci kfree(comp); 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci case snd_soc_dapm_scheduler: 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* 2158c2ecf20Sopenharmony_ci * During suspend, all DSP cores are powered off. 2168c2ecf20Sopenharmony_ci * Therefore upon resume, create the pipeline comp 2178c2ecf20Sopenharmony_ci * and power up the core that the pipeline is 2188c2ecf20Sopenharmony_ci * scheduled on. 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_ci pipeline = swidget->private; 2218c2ecf20Sopenharmony_ci ret = sof_load_pipeline_ipc(dev, pipeline, &r); 2228c2ecf20Sopenharmony_ci break; 2238c2ecf20Sopenharmony_ci default: 2248c2ecf20Sopenharmony_ci hdr = swidget->private; 2258c2ecf20Sopenharmony_ci ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, 2268c2ecf20Sopenharmony_ci swidget->private, hdr->size, 2278c2ecf20Sopenharmony_ci &r, sizeof(r)); 2288c2ecf20Sopenharmony_ci break; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci if (ret < 0) { 2318c2ecf20Sopenharmony_ci dev_err(dev, 2328c2ecf20Sopenharmony_ci "error: failed to load widget type %d with ID: %d\n", 2338c2ecf20Sopenharmony_ci swidget->widget->id, swidget->comp_id); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return ret; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* restore pipeline connections */ 2408c2ecf20Sopenharmony_ci list_for_each_entry_reverse(sroute, &sdev->route_list, list) { 2418c2ecf20Sopenharmony_ci struct sof_ipc_pipe_comp_connect *connect; 2428c2ecf20Sopenharmony_ci struct sof_ipc_reply reply; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* skip if there's no private data */ 2458c2ecf20Sopenharmony_ci if (!sroute->private) 2468c2ecf20Sopenharmony_ci continue; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci connect = sroute->private; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* send ipc */ 2518c2ecf20Sopenharmony_ci ret = sof_ipc_tx_message(sdev->ipc, 2528c2ecf20Sopenharmony_ci connect->hdr.cmd, 2538c2ecf20Sopenharmony_ci connect, sizeof(*connect), 2548c2ecf20Sopenharmony_ci &reply, sizeof(reply)); 2558c2ecf20Sopenharmony_ci if (ret < 0) { 2568c2ecf20Sopenharmony_ci dev_err(dev, 2578c2ecf20Sopenharmony_ci "error: failed to load route sink %s control %s source %s\n", 2588c2ecf20Sopenharmony_ci sroute->route->sink, 2598c2ecf20Sopenharmony_ci sroute->route->control ? sroute->route->control 2608c2ecf20Sopenharmony_ci : "none", 2618c2ecf20Sopenharmony_ci sroute->route->source); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return ret; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* restore dai links */ 2688c2ecf20Sopenharmony_ci list_for_each_entry_reverse(dai, &sdev->dai_list, list) { 2698c2ecf20Sopenharmony_ci struct sof_ipc_reply reply; 2708c2ecf20Sopenharmony_ci struct sof_ipc_dai_config *config = dai->dai_config; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (!config) { 2738c2ecf20Sopenharmony_ci dev_err(dev, "error: no config for DAI %s\n", 2748c2ecf20Sopenharmony_ci dai->name); 2758c2ecf20Sopenharmony_ci continue; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* 2798c2ecf20Sopenharmony_ci * The link DMA channel would be invalidated for running 2808c2ecf20Sopenharmony_ci * streams but not for streams that were in the PAUSED 2818c2ecf20Sopenharmony_ci * state during suspend. So invalidate it here before setting 2828c2ecf20Sopenharmony_ci * the dai config in the DSP. 2838c2ecf20Sopenharmony_ci */ 2848c2ecf20Sopenharmony_ci if (config->type == SOF_DAI_INTEL_HDA) 2858c2ecf20Sopenharmony_ci config->hda.link_dma_ch = DMA_CHAN_INVALID; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci ret = sof_ipc_tx_message(sdev->ipc, 2888c2ecf20Sopenharmony_ci config->hdr.cmd, config, 2898c2ecf20Sopenharmony_ci config->hdr.size, 2908c2ecf20Sopenharmony_ci &reply, sizeof(reply)); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (ret < 0) { 2938c2ecf20Sopenharmony_ci dev_err(dev, 2948c2ecf20Sopenharmony_ci "error: failed to set dai config for %s\n", 2958c2ecf20Sopenharmony_ci dai->name); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return ret; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* complete pipeline */ 3028c2ecf20Sopenharmony_ci list_for_each_entry(swidget, &sdev->widget_list, list) { 3038c2ecf20Sopenharmony_ci switch (swidget->id) { 3048c2ecf20Sopenharmony_ci case snd_soc_dapm_scheduler: 3058c2ecf20Sopenharmony_ci swidget->complete = 3068c2ecf20Sopenharmony_ci snd_sof_complete_pipeline(dev, swidget); 3078c2ecf20Sopenharmony_ci break; 3088c2ecf20Sopenharmony_ci default: 3098c2ecf20Sopenharmony_ci break; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* restore pipeline kcontrols */ 3148c2ecf20Sopenharmony_ci ret = sof_restore_kcontrols(dev); 3158c2ecf20Sopenharmony_ci if (ret < 0) 3168c2ecf20Sopenharmony_ci dev_err(dev, 3178c2ecf20Sopenharmony_ci "error: restoring kcontrols after resume\n"); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci return ret; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci/* 3238c2ecf20Sopenharmony_ci * Generic object lookup APIs. 3248c2ecf20Sopenharmony_ci */ 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistruct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp, 3278c2ecf20Sopenharmony_ci const char *name) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 3308c2ecf20Sopenharmony_ci struct snd_sof_pcm *spcm; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci list_for_each_entry(spcm, &sdev->pcm_list, list) { 3338c2ecf20Sopenharmony_ci /* match with PCM dai name */ 3348c2ecf20Sopenharmony_ci if (strcmp(spcm->pcm.dai_name, name) == 0) 3358c2ecf20Sopenharmony_ci return spcm; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* match with playback caps name if set */ 3388c2ecf20Sopenharmony_ci if (*spcm->pcm.caps[0].name && 3398c2ecf20Sopenharmony_ci !strcmp(spcm->pcm.caps[0].name, name)) 3408c2ecf20Sopenharmony_ci return spcm; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* match with capture caps name if set */ 3438c2ecf20Sopenharmony_ci if (*spcm->pcm.caps[1].name && 3448c2ecf20Sopenharmony_ci !strcmp(spcm->pcm.caps[1].name, name)) 3458c2ecf20Sopenharmony_ci return spcm; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return NULL; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistruct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp, 3528c2ecf20Sopenharmony_ci unsigned int comp_id, 3538c2ecf20Sopenharmony_ci int *direction) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 3568c2ecf20Sopenharmony_ci struct snd_sof_pcm *spcm; 3578c2ecf20Sopenharmony_ci int dir; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci list_for_each_entry(spcm, &sdev->pcm_list, list) { 3608c2ecf20Sopenharmony_ci for_each_pcm_streams(dir) { 3618c2ecf20Sopenharmony_ci if (spcm->stream[dir].comp_id == comp_id) { 3628c2ecf20Sopenharmony_ci *direction = dir; 3638c2ecf20Sopenharmony_ci return spcm; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci return NULL; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistruct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_soc_component *scomp, 3728c2ecf20Sopenharmony_ci unsigned int pcm_id) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 3758c2ecf20Sopenharmony_ci struct snd_sof_pcm *spcm; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci list_for_each_entry(spcm, &sdev->pcm_list, list) { 3788c2ecf20Sopenharmony_ci if (le32_to_cpu(spcm->pcm.pcm_id) == pcm_id) 3798c2ecf20Sopenharmony_ci return spcm; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci return NULL; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistruct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp, 3868c2ecf20Sopenharmony_ci const char *name) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 3898c2ecf20Sopenharmony_ci struct snd_sof_widget *swidget; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci list_for_each_entry(swidget, &sdev->widget_list, list) { 3928c2ecf20Sopenharmony_ci if (strcmp(name, swidget->widget->name) == 0) 3938c2ecf20Sopenharmony_ci return swidget; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return NULL; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci/* find widget by stream name and direction */ 4008c2ecf20Sopenharmony_cistruct snd_sof_widget * 4018c2ecf20Sopenharmony_cisnd_sof_find_swidget_sname(struct snd_soc_component *scomp, 4028c2ecf20Sopenharmony_ci const char *pcm_name, int dir) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 4058c2ecf20Sopenharmony_ci struct snd_sof_widget *swidget; 4068c2ecf20Sopenharmony_ci enum snd_soc_dapm_type type; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (dir == SNDRV_PCM_STREAM_PLAYBACK) 4098c2ecf20Sopenharmony_ci type = snd_soc_dapm_aif_in; 4108c2ecf20Sopenharmony_ci else 4118c2ecf20Sopenharmony_ci type = snd_soc_dapm_aif_out; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci list_for_each_entry(swidget, &sdev->widget_list, list) { 4148c2ecf20Sopenharmony_ci if (!strcmp(pcm_name, swidget->widget->sname) && 4158c2ecf20Sopenharmony_ci swidget->id == type) 4168c2ecf20Sopenharmony_ci return swidget; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci return NULL; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistruct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp, 4238c2ecf20Sopenharmony_ci const char *name) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 4268c2ecf20Sopenharmony_ci struct snd_sof_dai *dai; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci list_for_each_entry(dai, &sdev->dai_list, list) { 4298c2ecf20Sopenharmony_ci if (dai->name && (strcmp(name, dai->name) == 0)) 4308c2ecf20Sopenharmony_ci return dai; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci return NULL; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci/* 4378c2ecf20Sopenharmony_ci * SOF Driver enumeration. 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_ciint sof_machine_check(struct snd_sof_dev *sdev) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci struct snd_sof_pdata *sof_pdata = sdev->pdata; 4428c2ecf20Sopenharmony_ci const struct sof_dev_desc *desc = sof_pdata->desc; 4438c2ecf20Sopenharmony_ci struct snd_soc_acpi_mach *mach; 4448c2ecf20Sopenharmony_ci int ret; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* force nocodec mode */ 4478c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE) 4488c2ecf20Sopenharmony_ci dev_warn(sdev->dev, "Force to use nocodec mode\n"); 4498c2ecf20Sopenharmony_ci goto nocodec; 4508c2ecf20Sopenharmony_ci#endif 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* find machine */ 4538c2ecf20Sopenharmony_ci snd_sof_machine_select(sdev); 4548c2ecf20Sopenharmony_ci if (sof_pdata->machine) { 4558c2ecf20Sopenharmony_ci snd_sof_set_mach_params(sof_pdata->machine, sdev->dev); 4568c2ecf20Sopenharmony_ci return 0; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci#if !IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC) 4608c2ecf20Sopenharmony_ci dev_err(sdev->dev, "error: no matching ASoC machine driver found - aborting probe\n"); 4618c2ecf20Sopenharmony_ci return -ENODEV; 4628c2ecf20Sopenharmony_ci#endif 4638c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_FORCE_NOCODEC_MODE) 4648c2ecf20Sopenharmony_cinocodec: 4658c2ecf20Sopenharmony_ci#endif 4668c2ecf20Sopenharmony_ci /* select nocodec mode */ 4678c2ecf20Sopenharmony_ci dev_warn(sdev->dev, "Using nocodec machine driver\n"); 4688c2ecf20Sopenharmony_ci mach = devm_kzalloc(sdev->dev, sizeof(*mach), GFP_KERNEL); 4698c2ecf20Sopenharmony_ci if (!mach) 4708c2ecf20Sopenharmony_ci return -ENOMEM; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci mach->drv_name = "sof-nocodec"; 4738c2ecf20Sopenharmony_ci sof_pdata->tplg_filename = desc->nocodec_tplg_filename; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci ret = sof_nocodec_setup(sdev->dev, desc->ops); 4768c2ecf20Sopenharmony_ci if (ret < 0) 4778c2ecf20Sopenharmony_ci return ret; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci sof_pdata->machine = mach; 4808c2ecf20Sopenharmony_ci snd_sof_set_mach_params(sof_pdata->machine, sdev->dev); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci return 0; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sof_machine_check); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ciint sof_machine_register(struct snd_sof_dev *sdev, void *pdata) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci struct snd_sof_pdata *plat_data = pdata; 4898c2ecf20Sopenharmony_ci const char *drv_name; 4908c2ecf20Sopenharmony_ci const void *mach; 4918c2ecf20Sopenharmony_ci int size; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci drv_name = plat_data->machine->drv_name; 4948c2ecf20Sopenharmony_ci mach = plat_data->machine; 4958c2ecf20Sopenharmony_ci size = sizeof(*plat_data->machine); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* register machine driver, pass machine info as pdata */ 4988c2ecf20Sopenharmony_ci plat_data->pdev_mach = 4998c2ecf20Sopenharmony_ci platform_device_register_data(sdev->dev, drv_name, 5008c2ecf20Sopenharmony_ci PLATFORM_DEVID_NONE, mach, size); 5018c2ecf20Sopenharmony_ci if (IS_ERR(plat_data->pdev_mach)) 5028c2ecf20Sopenharmony_ci return PTR_ERR(plat_data->pdev_mach); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "created machine %s\n", 5058c2ecf20Sopenharmony_ci dev_name(&plat_data->pdev_mach->dev)); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci return 0; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sof_machine_register); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_civoid sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci struct snd_sof_pdata *plat_data = pdata; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(plat_data->pdev_mach)) 5168c2ecf20Sopenharmony_ci platform_device_unregister(plat_data->pdev_mach); 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sof_machine_unregister); 519