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

ipc3-pcm.c (12274B)


      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) 2021 Intel Corporation. All rights reserved.
      7//
      8//
      9
     10#include <sound/pcm_params.h>
     11#include "ipc3-priv.h"
     12#include "ops.h"
     13#include "sof-priv.h"
     14#include "sof-audio.h"
     15
     16static int sof_ipc3_pcm_hw_free(struct snd_soc_component *component,
     17				struct snd_pcm_substream *substream)
     18{
     19	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
     20	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
     21	struct sof_ipc_stream stream;
     22	struct sof_ipc_reply reply;
     23	struct snd_sof_pcm *spcm;
     24
     25	spcm = snd_sof_find_spcm_dai(component, rtd);
     26	if (!spcm)
     27		return -EINVAL;
     28
     29	if (!spcm->prepared[substream->stream])
     30		return 0;
     31
     32	stream.hdr.size = sizeof(stream);
     33	stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE;
     34	stream.comp_id = spcm->stream[substream->stream].comp_id;
     35
     36	/* send IPC to the DSP */
     37	return sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream), &reply, sizeof(reply));
     38}
     39
     40static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component,
     41				  struct snd_pcm_substream *substream,
     42				  struct snd_pcm_hw_params *params,
     43				  struct snd_sof_platform_stream_params *platform_params)
     44{
     45	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
     46	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
     47	struct sof_ipc_fw_version *v = &sdev->fw_ready.version;
     48	struct snd_pcm_runtime *runtime = substream->runtime;
     49	struct sof_ipc_pcm_params_reply ipc_params_reply;
     50	struct sof_ipc_pcm_params pcm;
     51	struct snd_sof_pcm *spcm;
     52	int ret;
     53
     54	spcm = snd_sof_find_spcm_dai(component, rtd);
     55	if (!spcm)
     56		return -EINVAL;
     57
     58	memset(&pcm, 0, sizeof(pcm));
     59
     60	/* number of pages should be rounded up */
     61	pcm.params.buffer.pages = PFN_UP(runtime->dma_bytes);
     62
     63	/* set IPC PCM parameters */
     64	pcm.hdr.size = sizeof(pcm);
     65	pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS;
     66	pcm.comp_id = spcm->stream[substream->stream].comp_id;
     67	pcm.params.hdr.size = sizeof(pcm.params);
     68	pcm.params.buffer.phy_addr = spcm->stream[substream->stream].page_table.addr;
     69	pcm.params.buffer.size = runtime->dma_bytes;
     70	pcm.params.direction = substream->stream;
     71	pcm.params.sample_valid_bytes = params_width(params) >> 3;
     72	pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED;
     73	pcm.params.rate = params_rate(params);
     74	pcm.params.channels = params_channels(params);
     75	pcm.params.host_period_bytes = params_period_bytes(params);
     76
     77	/* container size */
     78	ret = snd_pcm_format_physical_width(params_format(params));
     79	if (ret < 0)
     80		return ret;
     81	pcm.params.sample_container_bytes = ret >> 3;
     82
     83	/* format */
     84	switch (params_format(params)) {
     85	case SNDRV_PCM_FORMAT_S16:
     86		pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE;
     87		break;
     88	case SNDRV_PCM_FORMAT_S24:
     89		pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE;
     90		break;
     91	case SNDRV_PCM_FORMAT_S32:
     92		pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE;
     93		break;
     94	case SNDRV_PCM_FORMAT_FLOAT:
     95		pcm.params.frame_fmt = SOF_IPC_FRAME_FLOAT;
     96		break;
     97	default:
     98		return -EINVAL;
     99	}
    100
    101	/* Update the IPC message with information from the platform */
    102	pcm.params.stream_tag = platform_params->stream_tag;
    103
    104	if (platform_params->use_phy_address)
    105		pcm.params.buffer.phy_addr = platform_params->phy_addr;
    106
    107	if (platform_params->no_ipc_position) {
    108		/* For older ABIs set host_period_bytes to zero to inform
    109		 * FW we don't want position updates. Newer versions use
    110		 * no_stream_position for this purpose.
    111		 */
    112		if (v->abi_version < SOF_ABI_VER(3, 10, 0))
    113			pcm.params.host_period_bytes = 0;
    114		else
    115			pcm.params.no_stream_position = 1;
    116	}
    117
    118	dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag);
    119
    120	/* send hw_params IPC to the DSP */
    121	ret = sof_ipc_tx_message(sdev->ipc, &pcm, sizeof(pcm),
    122				 &ipc_params_reply, sizeof(ipc_params_reply));
    123	if (ret < 0) {
    124		dev_err(component->dev, "HW params ipc failed for stream %d\n",
    125			pcm.params.stream_tag);
    126		return ret;
    127	}
    128
    129	ret = snd_sof_set_stream_data_offset(sdev, substream, ipc_params_reply.posn_offset);
    130	if (ret < 0) {
    131		dev_err(component->dev, "%s: invalid stream data offset for PCM %d\n",
    132			__func__, spcm->pcm.pcm_id);
    133		return ret;
    134	}
    135
    136	return ret;
    137}
    138
    139static int sof_ipc3_pcm_trigger(struct snd_soc_component *component,
    140				struct snd_pcm_substream *substream, int cmd)
    141{
    142	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
    143	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
    144	struct sof_ipc_stream stream;
    145	struct sof_ipc_reply reply;
    146	struct snd_sof_pcm *spcm;
    147
    148	spcm = snd_sof_find_spcm_dai(component, rtd);
    149	if (!spcm)
    150		return -EINVAL;
    151
    152	stream.hdr.size = sizeof(stream);
    153	stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG;
    154	stream.comp_id = spcm->stream[substream->stream].comp_id;
    155
    156	switch (cmd) {
    157	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    158		stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE;
    159		break;
    160	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
    161		stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE;
    162		break;
    163	case SNDRV_PCM_TRIGGER_START:
    164		stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START;
    165		break;
    166	case SNDRV_PCM_TRIGGER_SUSPEND:
    167		fallthrough;
    168	case SNDRV_PCM_TRIGGER_STOP:
    169		stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP;
    170		break;
    171	default:
    172		dev_err(component->dev, "Unhandled trigger cmd %d\n", cmd);
    173		return -EINVAL;
    174	}
    175
    176	/* send IPC to the DSP */
    177	return sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream), &reply, sizeof(reply));
    178}
    179
    180static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name,
    181					    struct snd_pcm_hw_params *params)
    182{
    183	struct sof_ipc_dai_config *config;
    184	struct snd_sof_dai *dai;
    185	int i;
    186
    187	/*
    188	 * Search for all matching DAIs as we can have both playback and capture DAI
    189	 * associated with the same link.
    190	 */
    191	list_for_each_entry(dai, &sdev->dai_list, list) {
    192		if (!dai->name || strcmp(link_name, dai->name))
    193			continue;
    194		for (i = 0; i < dai->number_configs; i++) {
    195			struct sof_dai_private_data *private = dai->private;
    196
    197			config = &private->dai_config[i];
    198			if (config->ssp.fsync_rate == params_rate(params)) {
    199				dev_dbg(sdev->dev, "DAI config %d matches pcm hw params\n", i);
    200				dai->current_config = i;
    201				break;
    202			}
    203		}
    204	}
    205}
    206
    207static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
    208				       struct snd_pcm_hw_params *params)
    209{
    210	struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
    211	struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
    212	struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name);
    213	struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
    214	struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
    215	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
    216	struct sof_dai_private_data *private;
    217	struct snd_soc_dpcm *dpcm;
    218
    219	if (!dai) {
    220		dev_err(component->dev, "%s: No DAI found with name %s\n", __func__,
    221			rtd->dai_link->name);
    222		return -EINVAL;
    223	}
    224
    225	private = dai->private;
    226	if (!private) {
    227		dev_err(component->dev, "%s: No private data found for DAI %s\n", __func__,
    228			rtd->dai_link->name);
    229		return -EINVAL;
    230	}
    231
    232	/* read format from topology */
    233	snd_mask_none(fmt);
    234
    235	switch (private->comp_dai->config.frame_fmt) {
    236	case SOF_IPC_FRAME_S16_LE:
    237		snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
    238		break;
    239	case SOF_IPC_FRAME_S24_4LE:
    240		snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
    241		break;
    242	case SOF_IPC_FRAME_S32_LE:
    243		snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE);
    244		break;
    245	default:
    246		dev_err(component->dev, "No available DAI format!\n");
    247		return -EINVAL;
    248	}
    249
    250	/* read rate and channels from topology */
    251	switch (private->dai_config->type) {
    252	case SOF_DAI_INTEL_SSP:
    253		/* search for config to pcm params match, if not found use default */
    254		ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params);
    255
    256		rate->min = private->dai_config[dai->current_config].ssp.fsync_rate;
    257		rate->max = private->dai_config[dai->current_config].ssp.fsync_rate;
    258		channels->min = private->dai_config[dai->current_config].ssp.tdm_slots;
    259		channels->max = private->dai_config[dai->current_config].ssp.tdm_slots;
    260
    261		dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max);
    262		dev_dbg(component->dev, "channels_min: %d channels_max: %d\n",
    263			channels->min, channels->max);
    264
    265		break;
    266	case SOF_DAI_INTEL_DMIC:
    267		/* DMIC only supports 16 or 32 bit formats */
    268		if (private->comp_dai->config.frame_fmt == SOF_IPC_FRAME_S24_4LE) {
    269			dev_err(component->dev, "Invalid fmt %d for DAI type %d\n",
    270				private->comp_dai->config.frame_fmt,
    271				private->dai_config->type);
    272		}
    273		break;
    274	case SOF_DAI_INTEL_HDA:
    275		/*
    276		 * HDAudio does not follow the default trigger
    277		 * sequence due to firmware implementation
    278		 */
    279		for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_PLAYBACK, dpcm) {
    280			struct snd_soc_pcm_runtime *fe = dpcm->fe;
    281
    282			fe->dai_link->trigger[SNDRV_PCM_STREAM_PLAYBACK] =
    283				SND_SOC_DPCM_TRIGGER_POST;
    284		}
    285		break;
    286	case SOF_DAI_INTEL_ALH:
    287		/*
    288		 * Dai could run with different channel count compared with
    289		 * front end, so get dai channel count from topology
    290		 */
    291		channels->min = private->dai_config->alh.channels;
    292		channels->max = private->dai_config->alh.channels;
    293		break;
    294	case SOF_DAI_IMX_ESAI:
    295		rate->min = private->dai_config->esai.fsync_rate;
    296		rate->max = private->dai_config->esai.fsync_rate;
    297		channels->min = private->dai_config->esai.tdm_slots;
    298		channels->max = private->dai_config->esai.tdm_slots;
    299
    300		dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max);
    301		dev_dbg(component->dev, "channels_min: %d channels_max: %d\n",
    302			channels->min, channels->max);
    303		break;
    304	case SOF_DAI_MEDIATEK_AFE:
    305		rate->min = private->dai_config->afe.rate;
    306		rate->max = private->dai_config->afe.rate;
    307		channels->min = private->dai_config->afe.channels;
    308		channels->max = private->dai_config->afe.channels;
    309
    310		dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max);
    311		dev_dbg(component->dev, "channels_min: %d channels_max: %d\n",
    312			channels->min, channels->max);
    313		break;
    314	case SOF_DAI_IMX_SAI:
    315		rate->min = private->dai_config->sai.fsync_rate;
    316		rate->max = private->dai_config->sai.fsync_rate;
    317		channels->min = private->dai_config->sai.tdm_slots;
    318		channels->max = private->dai_config->sai.tdm_slots;
    319
    320		dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max);
    321		dev_dbg(component->dev, "channels_min: %d channels_max: %d\n",
    322			channels->min, channels->max);
    323		break;
    324	case SOF_DAI_AMD_BT:
    325		rate->min = private->dai_config->acpbt.fsync_rate;
    326		rate->max = private->dai_config->acpbt.fsync_rate;
    327		channels->min = private->dai_config->acpbt.tdm_slots;
    328		channels->max = private->dai_config->acpbt.tdm_slots;
    329
    330		dev_dbg(component->dev,
    331			"AMD_BT rate_min: %d rate_max: %d\n", rate->min, rate->max);
    332		dev_dbg(component->dev, "AMD_BT channels_min: %d channels_max: %d\n",
    333			channels->min, channels->max);
    334		break;
    335	case SOF_DAI_AMD_SP:
    336		rate->min = private->dai_config->acpsp.fsync_rate;
    337		rate->max = private->dai_config->acpsp.fsync_rate;
    338		channels->min = private->dai_config->acpsp.tdm_slots;
    339		channels->max = private->dai_config->acpsp.tdm_slots;
    340
    341		dev_dbg(component->dev,
    342			"AMD_SP rate_min: %d rate_max: %d\n", rate->min, rate->max);
    343		dev_dbg(component->dev, "AMD_SP channels_min: %d channels_max: %d\n",
    344			channels->min, channels->max);
    345		break;
    346	case SOF_DAI_AMD_DMIC:
    347		rate->min = private->dai_config->acpdmic.fsync_rate;
    348		rate->max = private->dai_config->acpdmic.fsync_rate;
    349		channels->min = private->dai_config->acpdmic.tdm_slots;
    350		channels->max = private->dai_config->acpdmic.tdm_slots;
    351
    352		dev_dbg(component->dev,
    353			"AMD_DMIC rate_min: %d rate_max: %d\n", rate->min, rate->max);
    354		dev_dbg(component->dev, "AMD_DMIC channels_min: %d channels_max: %d\n",
    355			channels->min, channels->max);
    356		break;
    357	default:
    358		dev_err(component->dev, "Invalid DAI type %d\n", private->dai_config->type);
    359		break;
    360	}
    361
    362	return 0;
    363}
    364
    365const struct sof_ipc_pcm_ops ipc3_pcm_ops = {
    366	.hw_params = sof_ipc3_pcm_hw_params,
    367	.hw_free = sof_ipc3_pcm_hw_free,
    368	.trigger = sof_ipc3_pcm_trigger,
    369	.dai_link_fixup = sof_ipc3_pcm_dai_link_fixup,
    370};