brintos

brintos / linux-shallow public Read only

0
0
Text · 10.7 KiB · d7b044f Raw
392 lines · c
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)2//3// Copyright 2021 NXP4//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#include "ops.h"14 15static void sof_set_transferred_bytes(struct sof_compr_stream *sstream,16				      u64 host_pos, u64 buffer_size)17{18	u64 prev_pos;19	unsigned int copied;20 21	div64_u64_rem(sstream->copied_total, buffer_size, &prev_pos);22 23	if (host_pos < prev_pos)24		copied = (buffer_size - prev_pos) + host_pos;25	else26		copied = host_pos - prev_pos;27 28	sstream->copied_total += copied;29}30 31static void snd_sof_compr_fragment_elapsed_work(struct work_struct *work)32{33	struct snd_sof_pcm_stream *sps =34		container_of(work, struct snd_sof_pcm_stream,35			     period_elapsed_work);36 37	snd_compr_fragment_elapsed(sps->cstream);38}39 40void snd_sof_compr_init_elapsed_work(struct work_struct *work)41{42	INIT_WORK(work, snd_sof_compr_fragment_elapsed_work);43}44 45/*46 * sof compr fragment elapse, this could be called in irq thread context47 */48void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream)49{50	struct snd_soc_pcm_runtime *rtd;51	struct snd_compr_runtime *crtd;52	struct snd_soc_component *component;53	struct sof_compr_stream *sstream;54	struct snd_sof_pcm *spcm;55 56	if (!cstream)57		return;58 59	rtd = cstream->private_data;60	crtd = cstream->runtime;61	sstream = crtd->private_data;62	component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);63 64	spcm = snd_sof_find_spcm_dai(component, rtd);65	if (!spcm) {66		dev_err(component->dev,67			"fragment elapsed called for unknown stream!\n");68		return;69	}70 71	sof_set_transferred_bytes(sstream, spcm->stream[cstream->direction].posn.host_posn,72				  crtd->buffer_size);73 74	/* use the same workqueue-based solution as for PCM, cf. snd_sof_pcm_elapsed */75	schedule_work(&spcm->stream[cstream->direction].period_elapsed_work);76}77 78static int create_page_table(struct snd_soc_component *component,79			     struct snd_compr_stream *cstream,80			     unsigned char *dma_area, size_t size)81{82	struct snd_dma_buffer *dmab = cstream->runtime->dma_buffer_p;83	struct snd_soc_pcm_runtime *rtd = cstream->private_data;84	int dir = cstream->direction;85	struct snd_sof_pcm *spcm;86 87	spcm = snd_sof_find_spcm_dai(component, rtd);88	if (!spcm)89		return -EINVAL;90 91	return snd_sof_create_page_table(component->dev, dmab,92					 spcm->stream[dir].page_table.area, size);93}94 95static int sof_compr_open(struct snd_soc_component *component,96			  struct snd_compr_stream *cstream)97{98	struct snd_soc_pcm_runtime *rtd = cstream->private_data;99	struct snd_compr_runtime *crtd = cstream->runtime;100	struct sof_compr_stream *sstream;101	struct snd_sof_pcm *spcm;102	int dir;103 104	sstream = kzalloc(sizeof(*sstream), GFP_KERNEL);105	if (!sstream)106		return -ENOMEM;107 108	spcm = snd_sof_find_spcm_dai(component, rtd);109	if (!spcm) {110		kfree(sstream);111		return -EINVAL;112	}113 114	dir = cstream->direction;115 116	if (spcm->stream[dir].cstream) {117		kfree(sstream);118		return -EBUSY;119	}120 121	spcm->stream[dir].cstream = cstream;122	spcm->stream[dir].posn.host_posn = 0;123	spcm->stream[dir].posn.dai_posn = 0;124	spcm->prepared[dir] = false;125 126	crtd->private_data = sstream;127 128	return 0;129}130 131static int sof_compr_free(struct snd_soc_component *component,132			  struct snd_compr_stream *cstream)133{134	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);135	struct sof_compr_stream *sstream = cstream->runtime->private_data;136	struct snd_soc_pcm_runtime *rtd = cstream->private_data;137	struct sof_ipc_stream stream;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_no_reply(sdev->ipc, &stream, sizeof(stream));151		if (!ret)152			spcm->prepared[cstream->direction] = false;153	}154 155	cancel_work_sync(&spcm->stream[cstream->direction].period_elapsed_work);156	spcm->stream[cstream->direction].cstream = NULL;157	kfree(sstream);158 159	return ret;160}161 162static int sof_compr_set_params(struct snd_soc_component *component,163				struct snd_compr_stream *cstream, struct snd_compr_params *params)164{165	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);166	struct snd_soc_pcm_runtime *rtd = cstream->private_data;167	struct snd_compr_runtime *crtd = cstream->runtime;168	struct sof_ipc_pcm_params_reply ipc_params_reply;169	struct sof_ipc_fw_ready *ready = &sdev->fw_ready;170	struct sof_ipc_fw_version *v = &ready->version;171	struct sof_compr_stream *sstream;172	struct sof_ipc_pcm_params *pcm;173	struct snd_sof_pcm *spcm;174	size_t ext_data_size;175	int ret;176 177	if (v->abi_version < SOF_ABI_VER(3, 22, 0)) {178		dev_err(component->dev,179			"Compress params not supported with FW ABI version %d:%d:%d\n",180			SOF_ABI_VERSION_MAJOR(v->abi_version),181			SOF_ABI_VERSION_MINOR(v->abi_version),182			SOF_ABI_VERSION_PATCH(v->abi_version));183		return -EINVAL;184	}185 186	sstream = crtd->private_data;187 188	spcm = snd_sof_find_spcm_dai(component, rtd);189 190	if (!spcm)191		return -EINVAL;192 193	ext_data_size = sizeof(params->codec);194 195	if (sizeof(*pcm) + ext_data_size > sdev->ipc->max_payload_size)196		return -EINVAL;197 198	pcm = kzalloc(sizeof(*pcm) + ext_data_size, GFP_KERNEL);199	if (!pcm)200		return -ENOMEM;201 202	cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG;203	cstream->dma_buffer.dev.dev = sdev->dev;204	ret = snd_compr_malloc_pages(cstream, crtd->buffer_size);205	if (ret < 0)206		goto out;207 208	ret = create_page_table(component, cstream, crtd->dma_area, crtd->dma_bytes);209	if (ret < 0)210		goto out;211 212	pcm->params.buffer.pages = PFN_UP(crtd->dma_bytes);213	pcm->hdr.size = sizeof(*pcm) + ext_data_size;214	pcm->hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS;215 216	pcm->comp_id = spcm->stream[cstream->direction].comp_id;217	pcm->params.hdr.size = sizeof(pcm->params) + ext_data_size;218	pcm->params.buffer.phy_addr = spcm->stream[cstream->direction].page_table.addr;219	pcm->params.buffer.size = crtd->dma_bytes;220	pcm->params.direction = cstream->direction;221	pcm->params.channels = params->codec.ch_out;222	pcm->params.rate = params->codec.sample_rate;223	pcm->params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED;224	pcm->params.frame_fmt = SOF_IPC_FRAME_S32_LE;225	pcm->params.sample_container_bytes =226		snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32) >> 3;227	pcm->params.host_period_bytes = params->buffer.fragment_size;228	pcm->params.ext_data_length = ext_data_size;229 230	memcpy((u8 *)pcm->params.ext_data, &params->codec, ext_data_size);231 232	ret = sof_ipc_tx_message(sdev->ipc, pcm, sizeof(*pcm) + ext_data_size,233				 &ipc_params_reply, sizeof(ipc_params_reply));234	if (ret < 0) {235		dev_err(component->dev, "error ipc failed\n");236		goto out;237	}238 239	ret = snd_sof_set_stream_data_offset(sdev, &spcm->stream[cstream->direction],240					     ipc_params_reply.posn_offset);241	if (ret < 0) {242		dev_err(component->dev, "Invalid stream data offset for Compr %d\n",243			spcm->pcm.pcm_id);244		goto out;245	}246 247	sstream->sampling_rate = params->codec.sample_rate;248	sstream->channels = params->codec.ch_out;249	sstream->sample_container_bytes = pcm->params.sample_container_bytes;250 251	spcm->prepared[cstream->direction] = true;252 253out:254	kfree(pcm);255 256	return ret;257}258 259static int sof_compr_get_params(struct snd_soc_component *component,260				struct snd_compr_stream *cstream, struct snd_codec *params)261{262	/* TODO: we don't query the supported codecs for now, if the263	 * application asks for an unsupported codec the set_params() will fail.264	 */265	return 0;266}267 268static int sof_compr_trigger(struct snd_soc_component *component,269			     struct snd_compr_stream *cstream, int cmd)270{271	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);272	struct snd_soc_pcm_runtime *rtd = cstream->private_data;273	struct sof_ipc_stream stream;274	struct snd_sof_pcm *spcm;275 276	spcm = snd_sof_find_spcm_dai(component, rtd);277	if (!spcm)278		return -EINVAL;279 280	stream.hdr.size = sizeof(stream);281	stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG;282	stream.comp_id = spcm->stream[cstream->direction].comp_id;283 284	switch (cmd) {285	case SNDRV_PCM_TRIGGER_START:286		stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START;287		break;288	case SNDRV_PCM_TRIGGER_STOP:289		stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP;290		break;291	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:292		stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE;293		break;294	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:295		stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE;296		break;297	default:298		dev_err(component->dev, "error: unhandled trigger cmd %d\n", cmd);299		break;300	}301 302	return sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream));303}304 305static int sof_compr_copy_playback(struct snd_compr_runtime *rtd,306				   char __user *buf, size_t count)307{308	void *ptr;309	unsigned int offset, n;310	int ret;311 312	div_u64_rem(rtd->total_bytes_available, rtd->buffer_size, &offset);313	ptr = rtd->dma_area + offset;314	n = rtd->buffer_size - offset;315 316	if (count < n) {317		ret = copy_from_user(ptr, buf, count);318	} else {319		ret = copy_from_user(ptr, buf, n);320		ret += copy_from_user(rtd->dma_area, buf + n, count - n);321	}322 323	return count - ret;324}325 326static int sof_compr_copy_capture(struct snd_compr_runtime *rtd,327				  char __user *buf, size_t count)328{329	void *ptr;330	unsigned int offset, n;331	int ret;332 333	div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset);334	ptr = rtd->dma_area + offset;335	n = rtd->buffer_size - offset;336 337	if (count < n) {338		ret = copy_to_user(buf, ptr, count);339	} else {340		ret = copy_to_user(buf, ptr, n);341		ret += copy_to_user(buf + n, rtd->dma_area, count - n);342	}343 344	return count - ret;345}346 347static int sof_compr_copy(struct snd_soc_component *component,348			  struct snd_compr_stream *cstream,349			  char __user *buf, size_t count)350{351	struct snd_compr_runtime *rtd = cstream->runtime;352 353	if (count > rtd->buffer_size)354		count = rtd->buffer_size;355 356	if (cstream->direction == SND_COMPRESS_PLAYBACK)357		return sof_compr_copy_playback(rtd, buf, count);358	else359		return sof_compr_copy_capture(rtd, buf, count);360}361 362static int sof_compr_pointer(struct snd_soc_component *component,363			     struct snd_compr_stream *cstream,364			     struct snd_compr_tstamp *tstamp)365{366	struct snd_sof_pcm *spcm;367	struct snd_soc_pcm_runtime *rtd = cstream->private_data;368	struct sof_compr_stream *sstream = cstream->runtime->private_data;369 370	spcm = snd_sof_find_spcm_dai(component, rtd);371	if (!spcm)372		return -EINVAL;373 374	tstamp->sampling_rate = sstream->sampling_rate;375	tstamp->copied_total = sstream->copied_total;376	tstamp->pcm_io_frames = div_u64(spcm->stream[cstream->direction].posn.dai_posn,377					sstream->channels * sstream->sample_container_bytes);378 379	return 0;380}381 382struct snd_compress_ops sof_compressed_ops = {383	.open		= sof_compr_open,384	.free		= sof_compr_free,385	.set_params	= sof_compr_set_params,386	.get_params	= sof_compr_get_params,387	.trigger	= sof_compr_trigger,388	.pointer	= sof_compr_pointer,389	.copy		= sof_compr_copy,390};391EXPORT_SYMBOL(sof_compressed_ops);392