cachepc-linux

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

pcm.c (19208B)


      1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
      2//
      3// This file is provided under a dual BSD/GPLv2 license.  When using or
      4// redistributing this file, you may do so under either license.
      5//
      6// Copyright(c) 2018 Intel Corporation. All rights reserved.
      7//
      8// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
      9//
     10// PCM Layer, interface between ALSA and IPC.
     11//
     12
     13#include <linux/pm_runtime.h>
     14#include <sound/pcm_params.h>
     15#include <sound/sof.h>
     16#include "sof-priv.h"
     17#include "sof-audio.h"
     18#include "sof-utils.h"
     19#include "ops.h"
     20
     21/* Create DMA buffer page table for DSP */
     22static int create_page_table(struct snd_soc_component *component,
     23			     struct snd_pcm_substream *substream,
     24			     unsigned char *dma_area, size_t size)
     25{
     26	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
     27	struct snd_sof_pcm *spcm;
     28	struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream);
     29	int stream = substream->stream;
     30
     31	spcm = snd_sof_find_spcm_dai(component, rtd);
     32	if (!spcm)
     33		return -EINVAL;
     34
     35	return snd_sof_create_page_table(component->dev, dmab,
     36		spcm->stream[stream].page_table.area, size);
     37}
     38
     39/*
     40 * sof pcm period elapse work
     41 */
     42static void snd_sof_pcm_period_elapsed_work(struct work_struct *work)
     43{
     44	struct snd_sof_pcm_stream *sps =
     45		container_of(work, struct snd_sof_pcm_stream,
     46			     period_elapsed_work);
     47
     48	snd_pcm_period_elapsed(sps->substream);
     49}
     50
     51void snd_sof_pcm_init_elapsed_work(struct work_struct *work)
     52{
     53	 INIT_WORK(work, snd_sof_pcm_period_elapsed_work);
     54}
     55
     56/*
     57 * sof pcm period elapse, this could be called at irq thread context.
     58 */
     59void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream)
     60{
     61	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
     62	struct snd_soc_component *component =
     63		snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
     64	struct snd_sof_pcm *spcm;
     65
     66	spcm = snd_sof_find_spcm_dai(component, rtd);
     67	if (!spcm) {
     68		dev_err(component->dev,
     69			"error: period elapsed for unknown stream!\n");
     70		return;
     71	}
     72
     73	/*
     74	 * snd_pcm_period_elapsed() can be called in interrupt context
     75	 * before IRQ_HANDLED is returned. Inside snd_pcm_period_elapsed(),
     76	 * when the PCM is done draining or xrun happened, a STOP IPC will
     77	 * then be sent and this IPC will hit IPC timeout.
     78	 * To avoid sending IPC before the previous IPC is handled, we
     79	 * schedule delayed work here to call the snd_pcm_period_elapsed().
     80	 */
     81	schedule_work(&spcm->stream[substream->stream].period_elapsed_work);
     82}
     83EXPORT_SYMBOL(snd_sof_pcm_period_elapsed);
     84
     85static int
     86sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd,
     87				struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *params,
     88				struct snd_sof_platform_stream_params *platform_params, int dir)
     89{
     90	struct snd_soc_dai *dai;
     91	int ret, j;
     92
     93	/* query DAPM for list of connected widgets and set them up */
     94	for_each_rtd_cpu_dais(rtd, j, dai) {
     95		struct snd_soc_dapm_widget_list *list;
     96
     97		ret = snd_soc_dapm_dai_get_connected_widgets(dai, dir, &list,
     98							     dpcm_end_walk_at_be);
     99		if (ret < 0) {
    100			dev_err(sdev->dev, "error: dai %s has no valid %s path\n", dai->name,
    101				dir == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture");
    102			return ret;
    103		}
    104
    105		spcm->stream[dir].list = list;
    106
    107		ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir);
    108		if (ret < 0) {
    109			dev_err(sdev->dev, "error: failed widget list set up for pcm %d dir %d\n",
    110				spcm->pcm.pcm_id, dir);
    111			spcm->stream[dir].list = NULL;
    112			snd_soc_dapm_dai_free_widgets(&list);
    113			return ret;
    114		}
    115	}
    116
    117	return 0;
    118}
    119
    120static int sof_pcm_hw_params(struct snd_soc_component *component,
    121			     struct snd_pcm_substream *substream,
    122			     struct snd_pcm_hw_params *params)
    123{
    124	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
    125	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    126	struct snd_sof_platform_stream_params platform_params = { 0 };
    127	const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
    128	struct snd_pcm_runtime *runtime = substream->runtime;
    129	struct snd_sof_pcm *spcm;
    130	int ret;
    131
    132	/* nothing to do for BE */
    133	if (rtd->dai_link->no_pcm)
    134		return 0;
    135
    136	spcm = snd_sof_find_spcm_dai(component, rtd);
    137	if (!spcm)
    138		return -EINVAL;
    139
    140	/*
    141	 * Handle repeated calls to hw_params() without free_pcm() in
    142	 * between. At least ALSA OSS emulation depends on this.
    143	 */
    144	if (pcm_ops->hw_free && spcm->prepared[substream->stream]) {
    145		ret = pcm_ops->hw_free(component, substream);
    146		if (ret < 0)
    147			return ret;
    148
    149		spcm->prepared[substream->stream] = false;
    150	}
    151
    152	dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n",
    153		spcm->pcm.pcm_id, substream->stream);
    154
    155	ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params);
    156	if (ret < 0) {
    157		dev_err(component->dev, "platform hw params failed\n");
    158		return ret;
    159	}
    160
    161	/* if this is a repeated hw_params without hw_free, skip setting up widgets */
    162	if (!spcm->stream[substream->stream].list) {
    163		ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, &platform_params,
    164						      substream->stream);
    165		if (ret < 0)
    166			return ret;
    167	}
    168
    169	/* create compressed page table for audio firmware */
    170	if (runtime->buffer_changed) {
    171		ret = create_page_table(component, substream, runtime->dma_area,
    172					runtime->dma_bytes);
    173
    174		if (ret < 0)
    175			return ret;
    176	}
    177
    178	if (pcm_ops->hw_params) {
    179		ret = pcm_ops->hw_params(component, substream, params, &platform_params);
    180		if (ret < 0)
    181			return ret;
    182	}
    183
    184	spcm->prepared[substream->stream] = true;
    185
    186	/* save pcm hw_params */
    187	memcpy(&spcm->params[substream->stream], params, sizeof(*params));
    188
    189	return 0;
    190}
    191
    192static int sof_pcm_hw_free(struct snd_soc_component *component,
    193			   struct snd_pcm_substream *substream)
    194{
    195	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    196	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
    197	const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
    198	struct snd_sof_pcm *spcm;
    199	int ret, err = 0;
    200
    201	/* nothing to do for BE */
    202	if (rtd->dai_link->no_pcm)
    203		return 0;
    204
    205	spcm = snd_sof_find_spcm_dai(component, rtd);
    206	if (!spcm)
    207		return -EINVAL;
    208
    209	dev_dbg(component->dev, "pcm: free stream %d dir %d\n",
    210		spcm->pcm.pcm_id, substream->stream);
    211
    212	/* free PCM in the DSP */
    213	if (pcm_ops->hw_free && spcm->prepared[substream->stream]) {
    214		ret = pcm_ops->hw_free(component, substream);
    215		if (ret < 0)
    216			err = ret;
    217
    218		spcm->prepared[substream->stream] = false;
    219	}
    220
    221	/* stop DMA */
    222	ret = snd_sof_pcm_platform_hw_free(sdev, substream);
    223	if (ret < 0) {
    224		dev_err(component->dev, "error: platform hw free failed\n");
    225		err = ret;
    226	}
    227
    228	/* free the DAPM widget list */
    229	ret = sof_widget_list_free(sdev, spcm, substream->stream);
    230	if (ret < 0)
    231		err = ret;
    232
    233	cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work);
    234
    235	return err;
    236}
    237
    238static int sof_pcm_prepare(struct snd_soc_component *component,
    239			   struct snd_pcm_substream *substream)
    240{
    241	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    242	struct snd_sof_pcm *spcm;
    243	int ret;
    244
    245	/* nothing to do for BE */
    246	if (rtd->dai_link->no_pcm)
    247		return 0;
    248
    249	spcm = snd_sof_find_spcm_dai(component, rtd);
    250	if (!spcm)
    251		return -EINVAL;
    252
    253	if (spcm->prepared[substream->stream])
    254		return 0;
    255
    256	dev_dbg(component->dev, "pcm: prepare stream %d dir %d\n",
    257		spcm->pcm.pcm_id, substream->stream);
    258
    259	/* set hw_params */
    260	ret = sof_pcm_hw_params(component,
    261				substream, &spcm->params[substream->stream]);
    262	if (ret < 0) {
    263		dev_err(component->dev,
    264			"error: set pcm hw_params after resume\n");
    265		return ret;
    266	}
    267
    268	return 0;
    269}
    270
    271/*
    272 * FE dai link trigger actions are always executed in non-atomic context because
    273 * they involve IPC's.
    274 */
    275static int sof_pcm_trigger(struct snd_soc_component *component,
    276			   struct snd_pcm_substream *substream, int cmd)
    277{
    278	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    279	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
    280	const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
    281	struct snd_sof_pcm *spcm;
    282	bool reset_hw_params = false;
    283	bool free_widget_list = false;
    284	bool ipc_first = false;
    285	int ret = 0;
    286
    287	/* nothing to do for BE */
    288	if (rtd->dai_link->no_pcm)
    289		return 0;
    290
    291	spcm = snd_sof_find_spcm_dai(component, rtd);
    292	if (!spcm)
    293		return -EINVAL;
    294
    295	dev_dbg(component->dev, "pcm: trigger stream %d dir %d cmd %d\n",
    296		spcm->pcm.pcm_id, substream->stream, cmd);
    297
    298	switch (cmd) {
    299	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    300		ipc_first = true;
    301		break;
    302	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
    303		break;
    304	case SNDRV_PCM_TRIGGER_START:
    305		if (spcm->stream[substream->stream].suspend_ignored) {
    306			/*
    307			 * This case will be triggered when INFO_RESUME is
    308			 * not supported, no need to re-start streams that
    309			 * remained enabled in D0ix.
    310			 */
    311			spcm->stream[substream->stream].suspend_ignored = false;
    312			return 0;
    313		}
    314		break;
    315	case SNDRV_PCM_TRIGGER_SUSPEND:
    316		if (sdev->system_suspend_target == SOF_SUSPEND_S0IX &&
    317		    spcm->stream[substream->stream].d0i3_compatible) {
    318			/*
    319			 * trap the event, not sending trigger stop to
    320			 * prevent the FW pipelines from being stopped,
    321			 * and mark the flag to ignore the upcoming DAPM
    322			 * PM events.
    323			 */
    324			spcm->stream[substream->stream].suspend_ignored = true;
    325			return 0;
    326		}
    327		free_widget_list = true;
    328		fallthrough;
    329	case SNDRV_PCM_TRIGGER_STOP:
    330		ipc_first = true;
    331		reset_hw_params = true;
    332		break;
    333	default:
    334		dev_err(component->dev, "Unhandled trigger cmd %d\n", cmd);
    335		return -EINVAL;
    336	}
    337
    338	/*
    339	 * DMA and IPC sequence is different for start and stop. Need to send
    340	 * STOP IPC before stop DMA
    341	 */
    342	if (!ipc_first)
    343		snd_sof_pcm_platform_trigger(sdev, substream, cmd);
    344
    345	if (pcm_ops->trigger)
    346		ret = pcm_ops->trigger(component, substream, cmd);
    347
    348	/* need to STOP DMA even if trigger IPC failed */
    349	if (ipc_first)
    350		snd_sof_pcm_platform_trigger(sdev, substream, cmd);
    351
    352	/* free PCM if reset_hw_params is set and the STOP IPC is successful */
    353	if (!ret && reset_hw_params)
    354		ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream,
    355					  free_widget_list);
    356
    357	return ret;
    358}
    359
    360static snd_pcm_uframes_t sof_pcm_pointer(struct snd_soc_component *component,
    361					 struct snd_pcm_substream *substream)
    362{
    363	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    364	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
    365	struct snd_sof_pcm *spcm;
    366	snd_pcm_uframes_t host, dai;
    367
    368	/* nothing to do for BE */
    369	if (rtd->dai_link->no_pcm)
    370		return 0;
    371
    372	/* use dsp ops pointer callback directly if set */
    373	if (sof_ops(sdev)->pcm_pointer)
    374		return sof_ops(sdev)->pcm_pointer(sdev, substream);
    375
    376	spcm = snd_sof_find_spcm_dai(component, rtd);
    377	if (!spcm)
    378		return -EINVAL;
    379
    380	/* read position from DSP */
    381	host = bytes_to_frames(substream->runtime,
    382			       spcm->stream[substream->stream].posn.host_posn);
    383	dai = bytes_to_frames(substream->runtime,
    384			      spcm->stream[substream->stream].posn.dai_posn);
    385
    386	dev_vdbg(component->dev,
    387		 "PCM: stream %d dir %d DMA position %lu DAI position %lu\n",
    388		 spcm->pcm.pcm_id, substream->stream, host, dai);
    389
    390	return host;
    391}
    392
    393static int sof_pcm_open(struct snd_soc_component *component,
    394			struct snd_pcm_substream *substream)
    395{
    396	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    397	struct snd_pcm_runtime *runtime = substream->runtime;
    398	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
    399	struct snd_sof_dsp_ops *ops = sof_ops(sdev);
    400	struct snd_sof_pcm *spcm;
    401	struct snd_soc_tplg_stream_caps *caps;
    402	int ret;
    403
    404	/* nothing to do for BE */
    405	if (rtd->dai_link->no_pcm)
    406		return 0;
    407
    408	spcm = snd_sof_find_spcm_dai(component, rtd);
    409	if (!spcm)
    410		return -EINVAL;
    411
    412	dev_dbg(component->dev, "pcm: open stream %d dir %d\n",
    413		spcm->pcm.pcm_id, substream->stream);
    414
    415
    416	caps = &spcm->pcm.caps[substream->stream];
    417
    418	/* set runtime config */
    419	runtime->hw.info = ops->hw_info; /* platform-specific */
    420
    421	/* set any runtime constraints based on topology */
    422	runtime->hw.formats = le64_to_cpu(caps->formats);
    423	runtime->hw.period_bytes_min = le32_to_cpu(caps->period_size_min);
    424	runtime->hw.period_bytes_max = le32_to_cpu(caps->period_size_max);
    425	runtime->hw.periods_min = le32_to_cpu(caps->periods_min);
    426	runtime->hw.periods_max = le32_to_cpu(caps->periods_max);
    427
    428	/*
    429	 * caps->buffer_size_min is not used since the
    430	 * snd_pcm_hardware structure only defines buffer_bytes_max
    431	 */
    432	runtime->hw.buffer_bytes_max = le32_to_cpu(caps->buffer_size_max);
    433
    434	dev_dbg(component->dev, "period min %zd max %zd bytes\n",
    435		runtime->hw.period_bytes_min,
    436		runtime->hw.period_bytes_max);
    437	dev_dbg(component->dev, "period count %d max %d\n",
    438		runtime->hw.periods_min,
    439		runtime->hw.periods_max);
    440	dev_dbg(component->dev, "buffer max %zd bytes\n",
    441		runtime->hw.buffer_bytes_max);
    442
    443	/* set wait time - TODO: come from topology */
    444	substream->wait_time = 500;
    445
    446	spcm->stream[substream->stream].posn.host_posn = 0;
    447	spcm->stream[substream->stream].posn.dai_posn = 0;
    448	spcm->stream[substream->stream].substream = substream;
    449	spcm->prepared[substream->stream] = false;
    450
    451	ret = snd_sof_pcm_platform_open(sdev, substream);
    452	if (ret < 0)
    453		dev_err(component->dev, "error: pcm open failed %d\n", ret);
    454
    455	return ret;
    456}
    457
    458static int sof_pcm_close(struct snd_soc_component *component,
    459			 struct snd_pcm_substream *substream)
    460{
    461	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    462	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
    463	struct snd_sof_pcm *spcm;
    464	int err;
    465
    466	/* nothing to do for BE */
    467	if (rtd->dai_link->no_pcm)
    468		return 0;
    469
    470	spcm = snd_sof_find_spcm_dai(component, rtd);
    471	if (!spcm)
    472		return -EINVAL;
    473
    474	dev_dbg(component->dev, "pcm: close stream %d dir %d\n",
    475		spcm->pcm.pcm_id, substream->stream);
    476
    477	err = snd_sof_pcm_platform_close(sdev, substream);
    478	if (err < 0) {
    479		dev_err(component->dev, "error: pcm close failed %d\n",
    480			err);
    481		/*
    482		 * keep going, no point in preventing the close
    483		 * from happening
    484		 */
    485	}
    486
    487	return 0;
    488}
    489
    490/*
    491 * Pre-allocate playback/capture audio buffer pages.
    492 * no need to explicitly release memory preallocated by sof_pcm_new in pcm_free
    493 * snd_pcm_lib_preallocate_free_for_all() is called by the core.
    494 */
    495static int sof_pcm_new(struct snd_soc_component *component,
    496		       struct snd_soc_pcm_runtime *rtd)
    497{
    498	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
    499	struct snd_sof_pcm *spcm;
    500	struct snd_pcm *pcm = rtd->pcm;
    501	struct snd_soc_tplg_stream_caps *caps;
    502	int stream = SNDRV_PCM_STREAM_PLAYBACK;
    503
    504	/* find SOF PCM for this RTD */
    505	spcm = snd_sof_find_spcm_dai(component, rtd);
    506	if (!spcm) {
    507		dev_warn(component->dev, "warn: can't find PCM with DAI ID %d\n",
    508			 rtd->dai_link->id);
    509		return 0;
    510	}
    511
    512	dev_dbg(component->dev, "creating new PCM %s\n", spcm->pcm.pcm_name);
    513
    514	/* do we need to pre-allocate playback audio buffer pages */
    515	if (!spcm->pcm.playback)
    516		goto capture;
    517
    518	caps = &spcm->pcm.caps[stream];
    519
    520	/* pre-allocate playback audio buffer pages */
    521	dev_dbg(component->dev,
    522		"spcm: allocate %s playback DMA buffer size 0x%x max 0x%x\n",
    523		caps->name, caps->buffer_size_min, caps->buffer_size_max);
    524
    525	if (!pcm->streams[stream].substream) {
    526		dev_err(component->dev, "error: NULL playback substream!\n");
    527		return -EINVAL;
    528	}
    529
    530	snd_pcm_set_managed_buffer(pcm->streams[stream].substream,
    531				   SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
    532				   0, le32_to_cpu(caps->buffer_size_max));
    533capture:
    534	stream = SNDRV_PCM_STREAM_CAPTURE;
    535
    536	/* do we need to pre-allocate capture audio buffer pages */
    537	if (!spcm->pcm.capture)
    538		return 0;
    539
    540	caps = &spcm->pcm.caps[stream];
    541
    542	/* pre-allocate capture audio buffer pages */
    543	dev_dbg(component->dev,
    544		"spcm: allocate %s capture DMA buffer size 0x%x max 0x%x\n",
    545		caps->name, caps->buffer_size_min, caps->buffer_size_max);
    546
    547	if (!pcm->streams[stream].substream) {
    548		dev_err(component->dev, "error: NULL capture substream!\n");
    549		return -EINVAL;
    550	}
    551
    552	snd_pcm_set_managed_buffer(pcm->streams[stream].substream,
    553				   SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
    554				   0, le32_to_cpu(caps->buffer_size_max));
    555
    556	return 0;
    557}
    558
    559/* fixup the BE DAI link to match any values from topology */
    560int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params)
    561{
    562	struct snd_interval *rate = hw_param_interval(params,
    563			SNDRV_PCM_HW_PARAM_RATE);
    564	struct snd_interval *channels = hw_param_interval(params,
    565						SNDRV_PCM_HW_PARAM_CHANNELS);
    566	struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
    567	struct snd_soc_component *component =
    568		snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
    569	struct snd_sof_dai *dai =
    570		snd_sof_find_dai(component, (char *)rtd->dai_link->name);
    571	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
    572	const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
    573
    574	/* no topology exists for this BE, try a common configuration */
    575	if (!dai) {
    576		dev_warn(component->dev,
    577			 "warning: no topology found for BE DAI %s config\n",
    578			 rtd->dai_link->name);
    579
    580		/*  set 48k, stereo, 16bits by default */
    581		rate->min = 48000;
    582		rate->max = 48000;
    583
    584		channels->min = 2;
    585		channels->max = 2;
    586
    587		snd_mask_none(fmt);
    588		snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
    589
    590		return 0;
    591	}
    592
    593	if (pcm_ops->dai_link_fixup)
    594		return pcm_ops->dai_link_fixup(rtd, params);
    595
    596	return 0;
    597}
    598EXPORT_SYMBOL(sof_pcm_dai_link_fixup);
    599
    600static int sof_pcm_probe(struct snd_soc_component *component)
    601{
    602	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
    603	struct snd_sof_pdata *plat_data = sdev->pdata;
    604	const char *tplg_filename;
    605	int ret;
    606
    607	/* load the default topology */
    608	sdev->component = component;
    609
    610	tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL,
    611				       "%s/%s",
    612				       plat_data->tplg_filename_prefix,
    613				       plat_data->tplg_filename);
    614	if (!tplg_filename)
    615		return -ENOMEM;
    616
    617	ret = snd_sof_load_topology(component, tplg_filename);
    618	if (ret < 0) {
    619		dev_err(component->dev, "error: failed to load DSP topology %d\n",
    620			ret);
    621		return ret;
    622	}
    623
    624	return ret;
    625}
    626
    627static void sof_pcm_remove(struct snd_soc_component *component)
    628{
    629	/* remove topology */
    630	snd_soc_tplg_component_remove(component);
    631}
    632
    633static int sof_pcm_ack(struct snd_soc_component *component,
    634		       struct snd_pcm_substream *substream)
    635{
    636	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
    637
    638	return snd_sof_pcm_platform_ack(sdev, substream);
    639}
    640
    641void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
    642{
    643	struct snd_soc_component_driver *pd = &sdev->plat_drv;
    644	struct snd_sof_pdata *plat_data = sdev->pdata;
    645	const char *drv_name;
    646
    647	drv_name = plat_data->machine->drv_name;
    648
    649	pd->name = "sof-audio-component";
    650	pd->probe = sof_pcm_probe;
    651	pd->remove = sof_pcm_remove;
    652	pd->open = sof_pcm_open;
    653	pd->close = sof_pcm_close;
    654	pd->hw_params = sof_pcm_hw_params;
    655	pd->prepare = sof_pcm_prepare;
    656	pd->hw_free = sof_pcm_hw_free;
    657	pd->trigger = sof_pcm_trigger;
    658	pd->pointer = sof_pcm_pointer;
    659	pd->ack = sof_pcm_ack;
    660
    661#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
    662	pd->compress_ops = &sof_compressed_ops;
    663#endif
    664
    665	pd->pcm_construct = sof_pcm_new;
    666	pd->ignore_machine = drv_name;
    667	pd->be_hw_params_fixup = sof_pcm_dai_link_fixup;
    668	pd->be_pcm_base = SOF_BE_PCM_BASE;
    669	pd->use_dai_pcm_id = true;
    670	pd->topology_name_prefix = "sof";
    671
    672	 /* increment module refcount when a pcm is opened */
    673	pd->module_get_upon_open = 1;
    674}