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

compress.c (8923B)


      1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
      2//
      3// Copyright 2021 NXP
      4//
      5// Author: Daniel Baluta <daniel.baluta@nxp.com>
      6
      7#include <sound/soc.h>
      8#include <sound/sof.h>
      9#include <sound/compress_driver.h>
     10#include "sof-audio.h"
     11#include "sof-priv.h"
     12#include "sof-utils.h"
     13
     14static void sof_set_transferred_bytes(struct snd_compr_tstamp *tstamp,
     15				      u64 host_pos, u64 buffer_size)
     16{
     17	u64 prev_pos;
     18	unsigned int copied;
     19
     20	div64_u64_rem(tstamp->copied_total, buffer_size, &prev_pos);
     21
     22	if (host_pos < prev_pos)
     23		copied = (buffer_size - prev_pos) + host_pos;
     24	else
     25		copied = host_pos - prev_pos;
     26
     27	tstamp->copied_total += copied;
     28}
     29
     30static void snd_sof_compr_fragment_elapsed_work(struct work_struct *work)
     31{
     32	struct snd_sof_pcm_stream *sps =
     33		container_of(work, struct snd_sof_pcm_stream,
     34			     period_elapsed_work);
     35
     36	snd_compr_fragment_elapsed(sps->cstream);
     37}
     38
     39void snd_sof_compr_init_elapsed_work(struct work_struct *work)
     40{
     41	INIT_WORK(work, snd_sof_compr_fragment_elapsed_work);
     42}
     43
     44/*
     45 * sof compr fragment elapse, this could be called in irq thread context
     46 */
     47void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream)
     48{
     49	struct snd_soc_pcm_runtime *rtd;
     50	struct snd_compr_runtime *crtd;
     51	struct snd_soc_component *component;
     52	struct snd_compr_tstamp *tstamp;
     53	struct snd_sof_pcm *spcm;
     54
     55	if (!cstream)
     56		return;
     57
     58	rtd = cstream->private_data;
     59	crtd = cstream->runtime;
     60	tstamp = crtd->private_data;
     61	component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
     62
     63	spcm = snd_sof_find_spcm_dai(component, rtd);
     64	if (!spcm) {
     65		dev_err(component->dev,
     66			"fragment elapsed called for unknown stream!\n");
     67		return;
     68	}
     69
     70	sof_set_transferred_bytes(tstamp, spcm->stream[cstream->direction].posn.host_posn,
     71				  crtd->buffer_size);
     72
     73	/* use the same workqueue-based solution as for PCM, cf. snd_sof_pcm_elapsed */
     74	schedule_work(&spcm->stream[cstream->direction].period_elapsed_work);
     75}
     76
     77static int create_page_table(struct snd_soc_component *component,
     78			     struct snd_compr_stream *cstream,
     79			     unsigned char *dma_area, size_t size)
     80{
     81	struct snd_dma_buffer *dmab = cstream->runtime->dma_buffer_p;
     82	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
     83	int dir = cstream->direction;
     84	struct snd_sof_pcm *spcm;
     85
     86	spcm = snd_sof_find_spcm_dai(component, rtd);
     87	if (!spcm)
     88		return -EINVAL;
     89
     90	return snd_sof_create_page_table(component->dev, dmab,
     91					 spcm->stream[dir].page_table.area, size);
     92}
     93
     94static int sof_compr_open(struct snd_soc_component *component,
     95			  struct snd_compr_stream *cstream)
     96{
     97	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
     98	struct snd_compr_runtime *crtd = cstream->runtime;
     99	struct snd_compr_tstamp *tstamp;
    100	struct snd_sof_pcm *spcm;
    101	int dir;
    102
    103	tstamp = kzalloc(sizeof(*tstamp), GFP_KERNEL);
    104	if (!tstamp)
    105		return -ENOMEM;
    106
    107	spcm = snd_sof_find_spcm_dai(component, rtd);
    108	if (!spcm) {
    109		kfree(tstamp);
    110		return -EINVAL;
    111	}
    112
    113	dir = cstream->direction;
    114
    115	if (spcm->stream[dir].cstream) {
    116		kfree(tstamp);
    117		return -EBUSY;
    118	}
    119
    120	spcm->stream[dir].cstream = cstream;
    121	spcm->stream[dir].posn.host_posn = 0;
    122	spcm->stream[dir].posn.dai_posn = 0;
    123	spcm->prepared[dir] = false;
    124
    125	crtd->private_data = tstamp;
    126
    127	return 0;
    128}
    129
    130static int sof_compr_free(struct snd_soc_component *component,
    131			  struct snd_compr_stream *cstream)
    132{
    133	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
    134	struct snd_compr_tstamp *tstamp = cstream->runtime->private_data;
    135	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
    136	struct sof_ipc_stream stream;
    137	struct sof_ipc_reply reply;
    138	struct snd_sof_pcm *spcm;
    139	int ret = 0;
    140
    141	spcm = snd_sof_find_spcm_dai(component, rtd);
    142	if (!spcm)
    143		return -EINVAL;
    144
    145	stream.hdr.size = sizeof(stream);
    146	stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE;
    147	stream.comp_id = spcm->stream[cstream->direction].comp_id;
    148
    149	if (spcm->prepared[cstream->direction]) {
    150		ret = sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream),
    151					 &reply, sizeof(reply));
    152		if (!ret)
    153			spcm->prepared[cstream->direction] = false;
    154	}
    155
    156	cancel_work_sync(&spcm->stream[cstream->direction].period_elapsed_work);
    157	spcm->stream[cstream->direction].cstream = NULL;
    158	kfree(tstamp);
    159
    160	return ret;
    161}
    162
    163static int sof_compr_set_params(struct snd_soc_component *component,
    164				struct snd_compr_stream *cstream, struct snd_compr_params *params)
    165{
    166	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
    167	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
    168	struct snd_compr_runtime *crtd = cstream->runtime;
    169	struct sof_ipc_pcm_params_reply ipc_params_reply;
    170	struct snd_compr_tstamp *tstamp;
    171	struct sof_ipc_pcm_params pcm;
    172	struct snd_sof_pcm *spcm;
    173	int ret;
    174
    175	tstamp = crtd->private_data;
    176
    177	spcm = snd_sof_find_spcm_dai(component, rtd);
    178
    179	if (!spcm)
    180		return -EINVAL;
    181
    182	cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG;
    183	cstream->dma_buffer.dev.dev = sdev->dev;
    184	ret = snd_compr_malloc_pages(cstream, crtd->buffer_size);
    185	if (ret < 0)
    186		return ret;
    187
    188	ret = create_page_table(component, cstream, crtd->dma_area, crtd->dma_bytes);
    189	if (ret < 0)
    190		return ret;
    191
    192	memset(&pcm, 0, sizeof(pcm));
    193
    194	pcm.params.buffer.pages = PFN_UP(crtd->dma_bytes);
    195	pcm.hdr.size = sizeof(pcm);
    196	pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS;
    197
    198	pcm.comp_id = spcm->stream[cstream->direction].comp_id;
    199	pcm.params.hdr.size = sizeof(pcm.params);
    200	pcm.params.buffer.phy_addr = spcm->stream[cstream->direction].page_table.addr;
    201	pcm.params.buffer.size = crtd->dma_bytes;
    202	pcm.params.direction = cstream->direction;
    203	pcm.params.channels = params->codec.ch_out;
    204	pcm.params.rate = params->codec.sample_rate;
    205	pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED;
    206	pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE;
    207	pcm.params.sample_container_bytes =
    208		snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32) >> 3;
    209	pcm.params.host_period_bytes = params->buffer.fragment_size;
    210
    211	ret = sof_ipc_tx_message(sdev->ipc, &pcm, sizeof(pcm),
    212				 &ipc_params_reply, sizeof(ipc_params_reply));
    213	if (ret < 0) {
    214		dev_err(component->dev, "error ipc failed\n");
    215		return ret;
    216	}
    217
    218	tstamp->byte_offset = sdev->stream_box.offset + ipc_params_reply.posn_offset;
    219	tstamp->sampling_rate = params->codec.sample_rate;
    220
    221	spcm->prepared[cstream->direction] = true;
    222
    223	return 0;
    224}
    225
    226static int sof_compr_get_params(struct snd_soc_component *component,
    227				struct snd_compr_stream *cstream, struct snd_codec *params)
    228{
    229	/* TODO: we don't query the supported codecs for now, if the
    230	 * application asks for an unsupported codec the set_params() will fail.
    231	 */
    232	return 0;
    233}
    234
    235static int sof_compr_trigger(struct snd_soc_component *component,
    236			     struct snd_compr_stream *cstream, int cmd)
    237{
    238	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
    239	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
    240	struct sof_ipc_stream stream;
    241	struct sof_ipc_reply reply;
    242	struct snd_sof_pcm *spcm;
    243
    244	spcm = snd_sof_find_spcm_dai(component, rtd);
    245	if (!spcm)
    246		return -EINVAL;
    247
    248	stream.hdr.size = sizeof(stream);
    249	stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG;
    250	stream.comp_id = spcm->stream[cstream->direction].comp_id;
    251
    252	switch (cmd) {
    253	case SNDRV_PCM_TRIGGER_START:
    254		stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START;
    255		break;
    256	case SNDRV_PCM_TRIGGER_STOP:
    257		stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP;
    258		break;
    259	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
    260		stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE;
    261		break;
    262	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
    263		stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE;
    264		break;
    265	default:
    266		dev_err(component->dev, "error: unhandled trigger cmd %d\n", cmd);
    267		break;
    268	}
    269
    270	return sof_ipc_tx_message(sdev->ipc, &stream, sizeof(stream),
    271				  &reply, sizeof(reply));
    272}
    273
    274static int sof_compr_copy(struct snd_soc_component *component,
    275			  struct snd_compr_stream *cstream,
    276			  char __user *buf, size_t count)
    277{
    278	struct snd_compr_runtime *rtd = cstream->runtime;
    279	unsigned int offset, n;
    280	void *ptr;
    281	int ret;
    282
    283	if (count > rtd->buffer_size)
    284		count = rtd->buffer_size;
    285
    286	div_u64_rem(rtd->total_bytes_available, rtd->buffer_size, &offset);
    287	ptr = rtd->dma_area + offset;
    288	n = rtd->buffer_size - offset;
    289
    290	if (count < n) {
    291		ret = copy_from_user(ptr, buf, count);
    292	} else {
    293		ret = copy_from_user(ptr, buf, n);
    294		ret += copy_from_user(rtd->dma_area, buf + n, count - n);
    295	}
    296
    297	return count - ret;
    298}
    299
    300static int sof_compr_pointer(struct snd_soc_component *component,
    301			     struct snd_compr_stream *cstream,
    302			     struct snd_compr_tstamp *tstamp)
    303{
    304	struct snd_compr_tstamp *pstamp = cstream->runtime->private_data;
    305
    306	tstamp->sampling_rate = pstamp->sampling_rate;
    307	tstamp->copied_total = pstamp->copied_total;
    308
    309	return 0;
    310}
    311
    312struct snd_compress_ops sof_compressed_ops = {
    313	.open		= sof_compr_open,
    314	.free		= sof_compr_free,
    315	.set_params	= sof_compr_set_params,
    316	.get_params	= sof_compr_get_params,
    317	.trigger	= sof_compr_trigger,
    318	.pointer	= sof_compr_pointer,
    319	.copy		= sof_compr_copy,
    320};
    321EXPORT_SYMBOL(sof_compressed_ops);