162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 462306a36Sopenharmony_ci// redistributing this file, you may do so under either license. 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved. 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> 962306a36Sopenharmony_ci// 1062306a36Sopenharmony_ci// PCM Layer, interface between ALSA and IPC. 1162306a36Sopenharmony_ci// 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1462306a36Sopenharmony_ci#include <sound/pcm_params.h> 1562306a36Sopenharmony_ci#include <sound/sof.h> 1662306a36Sopenharmony_ci#include <trace/events/sof.h> 1762306a36Sopenharmony_ci#include "sof-of-dev.h" 1862306a36Sopenharmony_ci#include "sof-priv.h" 1962306a36Sopenharmony_ci#include "sof-audio.h" 2062306a36Sopenharmony_ci#include "sof-utils.h" 2162306a36Sopenharmony_ci#include "ops.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* Create DMA buffer page table for DSP */ 2462306a36Sopenharmony_cistatic int create_page_table(struct snd_soc_component *component, 2562306a36Sopenharmony_ci struct snd_pcm_substream *substream, 2662306a36Sopenharmony_ci unsigned char *dma_area, size_t size) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 2962306a36Sopenharmony_ci struct snd_sof_pcm *spcm; 3062306a36Sopenharmony_ci struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream); 3162306a36Sopenharmony_ci int stream = substream->stream; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci spcm = snd_sof_find_spcm_dai(component, rtd); 3462306a36Sopenharmony_ci if (!spcm) 3562306a36Sopenharmony_ci return -EINVAL; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci return snd_sof_create_page_table(component->dev, dmab, 3862306a36Sopenharmony_ci spcm->stream[stream].page_table.area, size); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* 4262306a36Sopenharmony_ci * sof pcm period elapse work 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_cistatic void snd_sof_pcm_period_elapsed_work(struct work_struct *work) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct snd_sof_pcm_stream *sps = 4762306a36Sopenharmony_ci container_of(work, struct snd_sof_pcm_stream, 4862306a36Sopenharmony_ci period_elapsed_work); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci snd_pcm_period_elapsed(sps->substream); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_civoid snd_sof_pcm_init_elapsed_work(struct work_struct *work) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci INIT_WORK(work, snd_sof_pcm_period_elapsed_work); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * sof pcm period elapse, this could be called at irq thread context. 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_civoid snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 6462306a36Sopenharmony_ci struct snd_soc_component *component = 6562306a36Sopenharmony_ci snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); 6662306a36Sopenharmony_ci struct snd_sof_pcm *spcm; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci spcm = snd_sof_find_spcm_dai(component, rtd); 6962306a36Sopenharmony_ci if (!spcm) { 7062306a36Sopenharmony_ci dev_err(component->dev, 7162306a36Sopenharmony_ci "error: period elapsed for unknown stream!\n"); 7262306a36Sopenharmony_ci return; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* 7662306a36Sopenharmony_ci * snd_pcm_period_elapsed() can be called in interrupt context 7762306a36Sopenharmony_ci * before IRQ_HANDLED is returned. Inside snd_pcm_period_elapsed(), 7862306a36Sopenharmony_ci * when the PCM is done draining or xrun happened, a STOP IPC will 7962306a36Sopenharmony_ci * then be sent and this IPC will hit IPC timeout. 8062306a36Sopenharmony_ci * To avoid sending IPC before the previous IPC is handled, we 8162306a36Sopenharmony_ci * schedule delayed work here to call the snd_pcm_period_elapsed(). 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci schedule_work(&spcm->stream[substream->stream].period_elapsed_work); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sof_pcm_period_elapsed); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int 8862306a36Sopenharmony_cisof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd, 8962306a36Sopenharmony_ci struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *params, 9062306a36Sopenharmony_ci struct snd_sof_platform_stream_params *platform_params, int dir) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct snd_soc_dai *dai; 9362306a36Sopenharmony_ci int ret, j; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* query DAPM for list of connected widgets and set them up */ 9662306a36Sopenharmony_ci for_each_rtd_cpu_dais(rtd, j, dai) { 9762306a36Sopenharmony_ci struct snd_soc_dapm_widget_list *list; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci ret = snd_soc_dapm_dai_get_connected_widgets(dai, dir, &list, 10062306a36Sopenharmony_ci dpcm_end_walk_at_be); 10162306a36Sopenharmony_ci if (ret < 0) { 10262306a36Sopenharmony_ci dev_err(sdev->dev, "error: dai %s has no valid %s path\n", dai->name, 10362306a36Sopenharmony_ci dir == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture"); 10462306a36Sopenharmony_ci return ret; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci spcm->stream[dir].list = list; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir); 11062306a36Sopenharmony_ci if (ret < 0) { 11162306a36Sopenharmony_ci dev_err(sdev->dev, "error: failed widget list set up for pcm %d dir %d\n", 11262306a36Sopenharmony_ci spcm->pcm.pcm_id, dir); 11362306a36Sopenharmony_ci spcm->stream[dir].list = NULL; 11462306a36Sopenharmony_ci snd_soc_dapm_dai_free_widgets(&list); 11562306a36Sopenharmony_ci return ret; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int sof_pcm_hw_params(struct snd_soc_component *component, 12362306a36Sopenharmony_ci struct snd_pcm_substream *substream, 12462306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 12762306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 12862306a36Sopenharmony_ci const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); 12962306a36Sopenharmony_ci struct snd_sof_platform_stream_params platform_params = { 0 }; 13062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 13162306a36Sopenharmony_ci struct snd_sof_pcm *spcm; 13262306a36Sopenharmony_ci int ret; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* nothing to do for BE */ 13562306a36Sopenharmony_ci if (rtd->dai_link->no_pcm) 13662306a36Sopenharmony_ci return 0; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci spcm = snd_sof_find_spcm_dai(component, rtd); 13962306a36Sopenharmony_ci if (!spcm) 14062306a36Sopenharmony_ci return -EINVAL; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* 14362306a36Sopenharmony_ci * Handle repeated calls to hw_params() without free_pcm() in 14462306a36Sopenharmony_ci * between. At least ALSA OSS emulation depends on this. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_ci if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) { 14762306a36Sopenharmony_ci ret = pcm_ops->hw_free(component, substream); 14862306a36Sopenharmony_ci if (ret < 0) 14962306a36Sopenharmony_ci return ret; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci spcm->prepared[substream->stream] = false; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n", 15562306a36Sopenharmony_ci spcm->pcm.pcm_id, substream->stream); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params); 15862306a36Sopenharmony_ci if (ret < 0) { 15962306a36Sopenharmony_ci dev_err(component->dev, "platform hw params failed\n"); 16062306a36Sopenharmony_ci return ret; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* if this is a repeated hw_params without hw_free, skip setting up widgets */ 16462306a36Sopenharmony_ci if (!spcm->stream[substream->stream].list) { 16562306a36Sopenharmony_ci ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, &platform_params, 16662306a36Sopenharmony_ci substream->stream); 16762306a36Sopenharmony_ci if (ret < 0) 16862306a36Sopenharmony_ci return ret; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* create compressed page table for audio firmware */ 17262306a36Sopenharmony_ci if (runtime->buffer_changed) { 17362306a36Sopenharmony_ci ret = create_page_table(component, substream, runtime->dma_area, 17462306a36Sopenharmony_ci runtime->dma_bytes); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (ret < 0) 17762306a36Sopenharmony_ci return ret; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (pcm_ops && pcm_ops->hw_params) { 18162306a36Sopenharmony_ci ret = pcm_ops->hw_params(component, substream, params, &platform_params); 18262306a36Sopenharmony_ci if (ret < 0) 18362306a36Sopenharmony_ci return ret; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci spcm->prepared[substream->stream] = true; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* save pcm hw_params */ 18962306a36Sopenharmony_ci memcpy(&spcm->params[substream->stream], params, sizeof(*params)); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci return 0; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int sof_pcm_hw_free(struct snd_soc_component *component, 19562306a36Sopenharmony_ci struct snd_pcm_substream *substream) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 19862306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 19962306a36Sopenharmony_ci const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); 20062306a36Sopenharmony_ci struct snd_sof_pcm *spcm; 20162306a36Sopenharmony_ci int ret, err = 0; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* nothing to do for BE */ 20462306a36Sopenharmony_ci if (rtd->dai_link->no_pcm) 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci spcm = snd_sof_find_spcm_dai(component, rtd); 20862306a36Sopenharmony_ci if (!spcm) 20962306a36Sopenharmony_ci return -EINVAL; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci dev_dbg(component->dev, "pcm: free stream %d dir %d\n", 21262306a36Sopenharmony_ci spcm->pcm.pcm_id, substream->stream); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (spcm->prepared[substream->stream]) { 21562306a36Sopenharmony_ci /* stop DMA first if needed */ 21662306a36Sopenharmony_ci if (pcm_ops && pcm_ops->platform_stop_during_hw_free) 21762306a36Sopenharmony_ci snd_sof_pcm_platform_trigger(sdev, substream, SNDRV_PCM_TRIGGER_STOP); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* free PCM in the DSP */ 22062306a36Sopenharmony_ci if (pcm_ops && pcm_ops->hw_free) { 22162306a36Sopenharmony_ci ret = pcm_ops->hw_free(component, substream); 22262306a36Sopenharmony_ci if (ret < 0) 22362306a36Sopenharmony_ci err = ret; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci spcm->prepared[substream->stream] = false; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* reset DMA */ 23062306a36Sopenharmony_ci ret = snd_sof_pcm_platform_hw_free(sdev, substream); 23162306a36Sopenharmony_ci if (ret < 0) { 23262306a36Sopenharmony_ci dev_err(component->dev, "error: platform hw free failed\n"); 23362306a36Sopenharmony_ci err = ret; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* free the DAPM widget list */ 23762306a36Sopenharmony_ci ret = sof_widget_list_free(sdev, spcm, substream->stream); 23862306a36Sopenharmony_ci if (ret < 0) 23962306a36Sopenharmony_ci err = ret; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci return err; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic int sof_pcm_prepare(struct snd_soc_component *component, 24762306a36Sopenharmony_ci struct snd_pcm_substream *substream) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 25062306a36Sopenharmony_ci struct snd_sof_pcm *spcm; 25162306a36Sopenharmony_ci int ret; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* nothing to do for BE */ 25462306a36Sopenharmony_ci if (rtd->dai_link->no_pcm) 25562306a36Sopenharmony_ci return 0; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci spcm = snd_sof_find_spcm_dai(component, rtd); 25862306a36Sopenharmony_ci if (!spcm) 25962306a36Sopenharmony_ci return -EINVAL; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (spcm->prepared[substream->stream]) 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci dev_dbg(component->dev, "pcm: prepare stream %d dir %d\n", 26562306a36Sopenharmony_ci spcm->pcm.pcm_id, substream->stream); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* set hw_params */ 26862306a36Sopenharmony_ci ret = sof_pcm_hw_params(component, 26962306a36Sopenharmony_ci substream, &spcm->params[substream->stream]); 27062306a36Sopenharmony_ci if (ret < 0) { 27162306a36Sopenharmony_ci dev_err(component->dev, 27262306a36Sopenharmony_ci "error: set pcm hw_params after resume\n"); 27362306a36Sopenharmony_ci return ret; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return 0; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/* 28062306a36Sopenharmony_ci * FE dai link trigger actions are always executed in non-atomic context because 28162306a36Sopenharmony_ci * they involve IPC's. 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_cistatic int sof_pcm_trigger(struct snd_soc_component *component, 28462306a36Sopenharmony_ci struct snd_pcm_substream *substream, int cmd) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 28762306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 28862306a36Sopenharmony_ci const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); 28962306a36Sopenharmony_ci struct snd_sof_pcm *spcm; 29062306a36Sopenharmony_ci bool reset_hw_params = false; 29162306a36Sopenharmony_ci bool ipc_first = false; 29262306a36Sopenharmony_ci int ret = 0; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* nothing to do for BE */ 29562306a36Sopenharmony_ci if (rtd->dai_link->no_pcm) 29662306a36Sopenharmony_ci return 0; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci spcm = snd_sof_find_spcm_dai(component, rtd); 29962306a36Sopenharmony_ci if (!spcm) 30062306a36Sopenharmony_ci return -EINVAL; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci dev_dbg(component->dev, "pcm: trigger stream %d dir %d cmd %d\n", 30362306a36Sopenharmony_ci spcm->pcm.pcm_id, substream->stream, cmd); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci switch (cmd) { 30662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 30762306a36Sopenharmony_ci ipc_first = true; 30862306a36Sopenharmony_ci break; 30962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 31062306a36Sopenharmony_ci if (pcm_ops && pcm_ops->ipc_first_on_start) 31162306a36Sopenharmony_ci ipc_first = true; 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 31462306a36Sopenharmony_ci if (spcm->stream[substream->stream].suspend_ignored) { 31562306a36Sopenharmony_ci /* 31662306a36Sopenharmony_ci * This case will be triggered when INFO_RESUME is 31762306a36Sopenharmony_ci * not supported, no need to re-start streams that 31862306a36Sopenharmony_ci * remained enabled in D0ix. 31962306a36Sopenharmony_ci */ 32062306a36Sopenharmony_ci spcm->stream[substream->stream].suspend_ignored = false; 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (pcm_ops && pcm_ops->ipc_first_on_start) 32562306a36Sopenharmony_ci ipc_first = true; 32662306a36Sopenharmony_ci break; 32762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 32862306a36Sopenharmony_ci if (sdev->system_suspend_target == SOF_SUSPEND_S0IX && 32962306a36Sopenharmony_ci spcm->stream[substream->stream].d0i3_compatible) { 33062306a36Sopenharmony_ci /* 33162306a36Sopenharmony_ci * trap the event, not sending trigger stop to 33262306a36Sopenharmony_ci * prevent the FW pipelines from being stopped, 33362306a36Sopenharmony_ci * and mark the flag to ignore the upcoming DAPM 33462306a36Sopenharmony_ci * PM events. 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_ci spcm->stream[substream->stream].suspend_ignored = true; 33762306a36Sopenharmony_ci return 0; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* On suspend the DMA must be stopped in DSPless mode */ 34162306a36Sopenharmony_ci if (sdev->dspless_mode_selected) 34262306a36Sopenharmony_ci reset_hw_params = true; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci fallthrough; 34562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 34662306a36Sopenharmony_ci ipc_first = true; 34762306a36Sopenharmony_ci if (pcm_ops && pcm_ops->reset_hw_params_during_stop) 34862306a36Sopenharmony_ci reset_hw_params = true; 34962306a36Sopenharmony_ci break; 35062306a36Sopenharmony_ci default: 35162306a36Sopenharmony_ci dev_err(component->dev, "Unhandled trigger cmd %d\n", cmd); 35262306a36Sopenharmony_ci return -EINVAL; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (!ipc_first) 35662306a36Sopenharmony_ci snd_sof_pcm_platform_trigger(sdev, substream, cmd); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (pcm_ops && pcm_ops->trigger) 35962306a36Sopenharmony_ci ret = pcm_ops->trigger(component, substream, cmd); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci switch (cmd) { 36262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 36362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 36462306a36Sopenharmony_ci /* invoke platform trigger to start DMA only if pcm_ops is successful */ 36562306a36Sopenharmony_ci if (ipc_first && !ret) 36662306a36Sopenharmony_ci snd_sof_pcm_platform_trigger(sdev, substream, cmd); 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 36962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 37062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 37162306a36Sopenharmony_ci /* invoke platform trigger to stop DMA even if pcm_ops isn't set or if it failed */ 37262306a36Sopenharmony_ci if (!pcm_ops || !pcm_ops->platform_stop_during_hw_free) 37362306a36Sopenharmony_ci snd_sof_pcm_platform_trigger(sdev, substream, cmd); 37462306a36Sopenharmony_ci break; 37562306a36Sopenharmony_ci default: 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* free PCM if reset_hw_params is set and the STOP IPC is successful */ 38062306a36Sopenharmony_ci if (!ret && reset_hw_params) 38162306a36Sopenharmony_ci ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, false); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return ret; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic snd_pcm_uframes_t sof_pcm_pointer(struct snd_soc_component *component, 38762306a36Sopenharmony_ci struct snd_pcm_substream *substream) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 39062306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 39162306a36Sopenharmony_ci struct snd_sof_pcm *spcm; 39262306a36Sopenharmony_ci snd_pcm_uframes_t host, dai; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* nothing to do for BE */ 39562306a36Sopenharmony_ci if (rtd->dai_link->no_pcm) 39662306a36Sopenharmony_ci return 0; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* use dsp ops pointer callback directly if set */ 39962306a36Sopenharmony_ci if (sof_ops(sdev)->pcm_pointer) 40062306a36Sopenharmony_ci return sof_ops(sdev)->pcm_pointer(sdev, substream); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci spcm = snd_sof_find_spcm_dai(component, rtd); 40362306a36Sopenharmony_ci if (!spcm) 40462306a36Sopenharmony_ci return -EINVAL; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* read position from DSP */ 40762306a36Sopenharmony_ci host = bytes_to_frames(substream->runtime, 40862306a36Sopenharmony_ci spcm->stream[substream->stream].posn.host_posn); 40962306a36Sopenharmony_ci dai = bytes_to_frames(substream->runtime, 41062306a36Sopenharmony_ci spcm->stream[substream->stream].posn.dai_posn); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci trace_sof_pcm_pointer_position(sdev, spcm, substream, host, dai); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci return host; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int sof_pcm_open(struct snd_soc_component *component, 41862306a36Sopenharmony_ci struct snd_pcm_substream *substream) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 42162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 42262306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 42362306a36Sopenharmony_ci struct snd_sof_dsp_ops *ops = sof_ops(sdev); 42462306a36Sopenharmony_ci struct snd_sof_pcm *spcm; 42562306a36Sopenharmony_ci struct snd_soc_tplg_stream_caps *caps; 42662306a36Sopenharmony_ci int ret; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* nothing to do for BE */ 42962306a36Sopenharmony_ci if (rtd->dai_link->no_pcm) 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci spcm = snd_sof_find_spcm_dai(component, rtd); 43362306a36Sopenharmony_ci if (!spcm) 43462306a36Sopenharmony_ci return -EINVAL; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci dev_dbg(component->dev, "pcm: open stream %d dir %d\n", 43762306a36Sopenharmony_ci spcm->pcm.pcm_id, substream->stream); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci caps = &spcm->pcm.caps[substream->stream]; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* set runtime config */ 44362306a36Sopenharmony_ci runtime->hw.info = ops->hw_info; /* platform-specific */ 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* set any runtime constraints based on topology */ 44662306a36Sopenharmony_ci runtime->hw.formats = le64_to_cpu(caps->formats); 44762306a36Sopenharmony_ci runtime->hw.period_bytes_min = le32_to_cpu(caps->period_size_min); 44862306a36Sopenharmony_ci runtime->hw.period_bytes_max = le32_to_cpu(caps->period_size_max); 44962306a36Sopenharmony_ci runtime->hw.periods_min = le32_to_cpu(caps->periods_min); 45062306a36Sopenharmony_ci runtime->hw.periods_max = le32_to_cpu(caps->periods_max); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* 45362306a36Sopenharmony_ci * caps->buffer_size_min is not used since the 45462306a36Sopenharmony_ci * snd_pcm_hardware structure only defines buffer_bytes_max 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_ci runtime->hw.buffer_bytes_max = le32_to_cpu(caps->buffer_size_max); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci dev_dbg(component->dev, "period min %zd max %zd bytes\n", 45962306a36Sopenharmony_ci runtime->hw.period_bytes_min, 46062306a36Sopenharmony_ci runtime->hw.period_bytes_max); 46162306a36Sopenharmony_ci dev_dbg(component->dev, "period count %d max %d\n", 46262306a36Sopenharmony_ci runtime->hw.periods_min, 46362306a36Sopenharmony_ci runtime->hw.periods_max); 46462306a36Sopenharmony_ci dev_dbg(component->dev, "buffer max %zd bytes\n", 46562306a36Sopenharmony_ci runtime->hw.buffer_bytes_max); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* set wait time - TODO: come from topology */ 46862306a36Sopenharmony_ci substream->wait_time = 500; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci spcm->stream[substream->stream].posn.host_posn = 0; 47162306a36Sopenharmony_ci spcm->stream[substream->stream].posn.dai_posn = 0; 47262306a36Sopenharmony_ci spcm->stream[substream->stream].substream = substream; 47362306a36Sopenharmony_ci spcm->prepared[substream->stream] = false; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci ret = snd_sof_pcm_platform_open(sdev, substream); 47662306a36Sopenharmony_ci if (ret < 0) 47762306a36Sopenharmony_ci dev_err(component->dev, "error: pcm open failed %d\n", ret); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return ret; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic int sof_pcm_close(struct snd_soc_component *component, 48362306a36Sopenharmony_ci struct snd_pcm_substream *substream) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 48662306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 48762306a36Sopenharmony_ci struct snd_sof_pcm *spcm; 48862306a36Sopenharmony_ci int err; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* nothing to do for BE */ 49162306a36Sopenharmony_ci if (rtd->dai_link->no_pcm) 49262306a36Sopenharmony_ci return 0; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci spcm = snd_sof_find_spcm_dai(component, rtd); 49562306a36Sopenharmony_ci if (!spcm) 49662306a36Sopenharmony_ci return -EINVAL; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci dev_dbg(component->dev, "pcm: close stream %d dir %d\n", 49962306a36Sopenharmony_ci spcm->pcm.pcm_id, substream->stream); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci err = snd_sof_pcm_platform_close(sdev, substream); 50262306a36Sopenharmony_ci if (err < 0) { 50362306a36Sopenharmony_ci dev_err(component->dev, "error: pcm close failed %d\n", 50462306a36Sopenharmony_ci err); 50562306a36Sopenharmony_ci /* 50662306a36Sopenharmony_ci * keep going, no point in preventing the close 50762306a36Sopenharmony_ci * from happening 50862306a36Sopenharmony_ci */ 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return 0; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci/* 51562306a36Sopenharmony_ci * Pre-allocate playback/capture audio buffer pages. 51662306a36Sopenharmony_ci * no need to explicitly release memory preallocated by sof_pcm_new in pcm_free 51762306a36Sopenharmony_ci * snd_pcm_lib_preallocate_free_for_all() is called by the core. 51862306a36Sopenharmony_ci */ 51962306a36Sopenharmony_cistatic int sof_pcm_new(struct snd_soc_component *component, 52062306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 52362306a36Sopenharmony_ci struct snd_sof_pcm *spcm; 52462306a36Sopenharmony_ci struct snd_pcm *pcm = rtd->pcm; 52562306a36Sopenharmony_ci struct snd_soc_tplg_stream_caps *caps; 52662306a36Sopenharmony_ci int stream = SNDRV_PCM_STREAM_PLAYBACK; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* find SOF PCM for this RTD */ 52962306a36Sopenharmony_ci spcm = snd_sof_find_spcm_dai(component, rtd); 53062306a36Sopenharmony_ci if (!spcm) { 53162306a36Sopenharmony_ci dev_warn(component->dev, "warn: can't find PCM with DAI ID %d\n", 53262306a36Sopenharmony_ci rtd->dai_link->id); 53362306a36Sopenharmony_ci return 0; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci dev_dbg(component->dev, "creating new PCM %s\n", spcm->pcm.pcm_name); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* do we need to pre-allocate playback audio buffer pages */ 53962306a36Sopenharmony_ci if (!spcm->pcm.playback) 54062306a36Sopenharmony_ci goto capture; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci caps = &spcm->pcm.caps[stream]; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* pre-allocate playback audio buffer pages */ 54562306a36Sopenharmony_ci dev_dbg(component->dev, 54662306a36Sopenharmony_ci "spcm: allocate %s playback DMA buffer size 0x%x max 0x%x\n", 54762306a36Sopenharmony_ci caps->name, caps->buffer_size_min, caps->buffer_size_max); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (!pcm->streams[stream].substream) { 55062306a36Sopenharmony_ci dev_err(component->dev, "error: NULL playback substream!\n"); 55162306a36Sopenharmony_ci return -EINVAL; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci snd_pcm_set_managed_buffer(pcm->streams[stream].substream, 55562306a36Sopenharmony_ci SNDRV_DMA_TYPE_DEV_SG, sdev->dev, 55662306a36Sopenharmony_ci 0, le32_to_cpu(caps->buffer_size_max)); 55762306a36Sopenharmony_cicapture: 55862306a36Sopenharmony_ci stream = SNDRV_PCM_STREAM_CAPTURE; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* do we need to pre-allocate capture audio buffer pages */ 56162306a36Sopenharmony_ci if (!spcm->pcm.capture) 56262306a36Sopenharmony_ci return 0; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci caps = &spcm->pcm.caps[stream]; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* pre-allocate capture audio buffer pages */ 56762306a36Sopenharmony_ci dev_dbg(component->dev, 56862306a36Sopenharmony_ci "spcm: allocate %s capture DMA buffer size 0x%x max 0x%x\n", 56962306a36Sopenharmony_ci caps->name, caps->buffer_size_min, caps->buffer_size_max); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (!pcm->streams[stream].substream) { 57262306a36Sopenharmony_ci dev_err(component->dev, "error: NULL capture substream!\n"); 57362306a36Sopenharmony_ci return -EINVAL; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci snd_pcm_set_managed_buffer(pcm->streams[stream].substream, 57762306a36Sopenharmony_ci SNDRV_DMA_TYPE_DEV_SG, sdev->dev, 57862306a36Sopenharmony_ci 0, le32_to_cpu(caps->buffer_size_max)); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci return 0; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci/* fixup the BE DAI link to match any values from topology */ 58462306a36Sopenharmony_ciint sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci struct snd_interval *rate = hw_param_interval(params, 58762306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE); 58862306a36Sopenharmony_ci struct snd_interval *channels = hw_param_interval(params, 58962306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS); 59062306a36Sopenharmony_ci struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); 59162306a36Sopenharmony_ci struct snd_soc_component *component = 59262306a36Sopenharmony_ci snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); 59362306a36Sopenharmony_ci struct snd_sof_dai *dai = 59462306a36Sopenharmony_ci snd_sof_find_dai(component, (char *)rtd->dai_link->name); 59562306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 59662306a36Sopenharmony_ci const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* no topology exists for this BE, try a common configuration */ 59962306a36Sopenharmony_ci if (!dai) { 60062306a36Sopenharmony_ci dev_warn(component->dev, 60162306a36Sopenharmony_ci "warning: no topology found for BE DAI %s config\n", 60262306a36Sopenharmony_ci rtd->dai_link->name); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* set 48k, stereo, 16bits by default */ 60562306a36Sopenharmony_ci rate->min = 48000; 60662306a36Sopenharmony_ci rate->max = 48000; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci channels->min = 2; 60962306a36Sopenharmony_ci channels->max = 2; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci snd_mask_none(fmt); 61262306a36Sopenharmony_ci snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (pcm_ops && pcm_ops->dai_link_fixup) 61862306a36Sopenharmony_ci return pcm_ops->dai_link_fixup(rtd, params); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci return 0; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ciEXPORT_SYMBOL(sof_pcm_dai_link_fixup); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic int sof_pcm_probe(struct snd_soc_component *component) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 62762306a36Sopenharmony_ci struct snd_sof_pdata *plat_data = sdev->pdata; 62862306a36Sopenharmony_ci const char *tplg_filename; 62962306a36Sopenharmony_ci int ret; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* 63262306a36Sopenharmony_ci * make sure the device is pm_runtime_active before loading the 63362306a36Sopenharmony_ci * topology and initiating IPC or bus transactions 63462306a36Sopenharmony_ci */ 63562306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(component->dev); 63662306a36Sopenharmony_ci if (ret < 0 && ret != -EACCES) 63762306a36Sopenharmony_ci return ret; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci /* load the default topology */ 64062306a36Sopenharmony_ci sdev->component = component; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL, 64362306a36Sopenharmony_ci "%s/%s", 64462306a36Sopenharmony_ci plat_data->tplg_filename_prefix, 64562306a36Sopenharmony_ci plat_data->tplg_filename); 64662306a36Sopenharmony_ci if (!tplg_filename) { 64762306a36Sopenharmony_ci ret = -ENOMEM; 64862306a36Sopenharmony_ci goto pm_error; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci ret = snd_sof_load_topology(component, tplg_filename); 65262306a36Sopenharmony_ci if (ret < 0) 65362306a36Sopenharmony_ci dev_err(component->dev, "error: failed to load DSP topology %d\n", 65462306a36Sopenharmony_ci ret); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cipm_error: 65762306a36Sopenharmony_ci pm_runtime_mark_last_busy(component->dev); 65862306a36Sopenharmony_ci pm_runtime_put_autosuspend(component->dev); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci return ret; 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic void sof_pcm_remove(struct snd_soc_component *component) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci /* remove topology */ 66662306a36Sopenharmony_ci snd_soc_tplg_component_remove(component); 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic int sof_pcm_ack(struct snd_soc_component *component, 67062306a36Sopenharmony_ci struct snd_pcm_substream *substream) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci return snd_sof_pcm_platform_ack(sdev, substream); 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic snd_pcm_sframes_t sof_pcm_delay(struct snd_soc_component *component, 67862306a36Sopenharmony_ci struct snd_pcm_substream *substream) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 68162306a36Sopenharmony_ci const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (pcm_ops && pcm_ops->delay) 68462306a36Sopenharmony_ci return pcm_ops->delay(component, substream); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci return 0; 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_civoid snd_sof_new_platform_drv(struct snd_sof_dev *sdev) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci struct snd_soc_component_driver *pd = &sdev->plat_drv; 69262306a36Sopenharmony_ci struct snd_sof_pdata *plat_data = sdev->pdata; 69362306a36Sopenharmony_ci const char *drv_name; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci if (plat_data->machine) 69662306a36Sopenharmony_ci drv_name = plat_data->machine->drv_name; 69762306a36Sopenharmony_ci else if (plat_data->of_machine) 69862306a36Sopenharmony_ci drv_name = plat_data->of_machine->drv_name; 69962306a36Sopenharmony_ci else 70062306a36Sopenharmony_ci drv_name = NULL; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci pd->name = "sof-audio-component"; 70362306a36Sopenharmony_ci pd->probe = sof_pcm_probe; 70462306a36Sopenharmony_ci pd->remove = sof_pcm_remove; 70562306a36Sopenharmony_ci pd->open = sof_pcm_open; 70662306a36Sopenharmony_ci pd->close = sof_pcm_close; 70762306a36Sopenharmony_ci pd->hw_params = sof_pcm_hw_params; 70862306a36Sopenharmony_ci pd->prepare = sof_pcm_prepare; 70962306a36Sopenharmony_ci pd->hw_free = sof_pcm_hw_free; 71062306a36Sopenharmony_ci pd->trigger = sof_pcm_trigger; 71162306a36Sopenharmony_ci pd->pointer = sof_pcm_pointer; 71262306a36Sopenharmony_ci pd->ack = sof_pcm_ack; 71362306a36Sopenharmony_ci pd->delay = sof_pcm_delay; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS) 71662306a36Sopenharmony_ci pd->compress_ops = &sof_compressed_ops; 71762306a36Sopenharmony_ci#endif 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci pd->pcm_construct = sof_pcm_new; 72062306a36Sopenharmony_ci pd->ignore_machine = drv_name; 72162306a36Sopenharmony_ci pd->be_pcm_base = SOF_BE_PCM_BASE; 72262306a36Sopenharmony_ci pd->use_dai_pcm_id = true; 72362306a36Sopenharmony_ci pd->topology_name_prefix = "sof"; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci /* increment module refcount when a pcm is opened */ 72662306a36Sopenharmony_ci pd->module_get_upon_open = 1; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci pd->legacy_dai_naming = 1; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* 73162306a36Sopenharmony_ci * The fixup is only needed when the DSP is in use as with the DSPless 73262306a36Sopenharmony_ci * mode we are directly using the audio interface 73362306a36Sopenharmony_ci */ 73462306a36Sopenharmony_ci if (!sdev->dspless_mode_selected) 73562306a36Sopenharmony_ci pd->be_hw_params_fixup = sof_pcm_dai_link_fixup; 73662306a36Sopenharmony_ci} 737