brintos

brintos / linux-shallow public Read only

0
0
Text · 24.3 KiB · c9404b5 Raw
885 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2021, Linaro Limited3 4#include <linux/init.h>5#include <linux/err.h>6#include <linux/module.h>7#include <linux/of.h>8#include <linux/platform_device.h>9#include <linux/slab.h>10#include <sound/soc.h>11#include <sound/soc-dapm.h>12#include <linux/spinlock.h>13#include <sound/pcm.h>14#include <asm/dma.h>15#include <linux/dma-mapping.h>16#include <sound/pcm_params.h>17#include "q6apm.h"18 19#define DRV_NAME "q6apm-dai"20 21#define PLAYBACK_MIN_NUM_PERIODS	222#define PLAYBACK_MAX_NUM_PERIODS	823#define PLAYBACK_MAX_PERIOD_SIZE	6553624#define PLAYBACK_MIN_PERIOD_SIZE	12825#define CAPTURE_MIN_NUM_PERIODS		226#define CAPTURE_MAX_NUM_PERIODS		827#define CAPTURE_MAX_PERIOD_SIZE		409628#define CAPTURE_MIN_PERIOD_SIZE		32029#define BUFFER_BYTES_MAX (PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE)30#define BUFFER_BYTES_MIN (PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE)31#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024)32#define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4)33#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024)34#define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4)35#define SID_MASK_DEFAULT	0xF36 37static const struct snd_compr_codec_caps q6apm_compr_caps = {38	.num_descriptors = 1,39	.descriptor[0].max_ch = 2,40	.descriptor[0].sample_rates = {	8000, 11025, 12000, 16000, 22050,41					24000, 32000, 44100, 48000, 88200,42					96000, 176400, 192000 },43	.descriptor[0].num_sample_rates = 13,44	.descriptor[0].bit_rate[0] = 320,45	.descriptor[0].bit_rate[1] = 128,46	.descriptor[0].num_bitrates = 2,47	.descriptor[0].profiles = 0,48	.descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO,49	.descriptor[0].formats = 0,50};51 52enum stream_state {53	Q6APM_STREAM_IDLE = 0,54	Q6APM_STREAM_STOPPED,55	Q6APM_STREAM_RUNNING,56};57 58struct q6apm_dai_rtd {59	struct snd_pcm_substream *substream;60	struct snd_compr_stream *cstream;61	struct snd_codec codec;62	struct snd_compr_params codec_param;63	struct snd_dma_buffer dma_buffer;64	phys_addr_t phys;65	unsigned int pcm_size;66	unsigned int pcm_count;67	unsigned int pos;       /* Buffer position */68	unsigned int periods;69	unsigned int bytes_sent;70	unsigned int bytes_received;71	unsigned int copied_total;72	uint16_t bits_per_sample;73	bool next_track;74	enum stream_state state;75	struct q6apm_graph *graph;76	spinlock_t lock;77	bool notify_on_drain;78};79 80struct q6apm_dai_data {81	long long sid;82};83 84static const struct snd_pcm_hardware q6apm_dai_hardware_capture = {85	.info =                 (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER |86				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED |87				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |88				 SNDRV_PCM_INFO_BATCH),89	.formats =              (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE),90	.rates =                SNDRV_PCM_RATE_8000_48000,91	.rate_min =             8000,92	.rate_max =             48000,93	.channels_min =         2,94	.channels_max =         4,95	.buffer_bytes_max =     CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,96	.period_bytes_min =	CAPTURE_MIN_PERIOD_SIZE,97	.period_bytes_max =     CAPTURE_MAX_PERIOD_SIZE,98	.periods_min =          CAPTURE_MIN_NUM_PERIODS,99	.periods_max =          CAPTURE_MAX_NUM_PERIODS,100	.fifo_size =            0,101};102 103static const struct snd_pcm_hardware q6apm_dai_hardware_playback = {104	.info =                 (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER |105				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED |106				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |107				 SNDRV_PCM_INFO_BATCH),108	.formats =              (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE),109	.rates =                SNDRV_PCM_RATE_8000_192000,110	.rate_min =             8000,111	.rate_max =             192000,112	.channels_min =         2,113	.channels_max =         8,114	.buffer_bytes_max =     (PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE),115	.period_bytes_min =	PLAYBACK_MIN_PERIOD_SIZE,116	.period_bytes_max =     PLAYBACK_MAX_PERIOD_SIZE,117	.periods_min =          PLAYBACK_MIN_NUM_PERIODS,118	.periods_max =          PLAYBACK_MAX_NUM_PERIODS,119	.fifo_size =            0,120};121 122static void event_handler(uint32_t opcode, uint32_t token, void *payload, void *priv)123{124	struct q6apm_dai_rtd *prtd = priv;125	struct snd_pcm_substream *substream = prtd->substream;126	unsigned long flags;127 128	switch (opcode) {129	case APM_CLIENT_EVENT_CMD_EOS_DONE:130		prtd->state = Q6APM_STREAM_STOPPED;131		break;132	case APM_CLIENT_EVENT_DATA_WRITE_DONE:133		spin_lock_irqsave(&prtd->lock, flags);134		prtd->pos += prtd->pcm_count;135		spin_unlock_irqrestore(&prtd->lock, flags);136		snd_pcm_period_elapsed(substream);137		if (prtd->state == Q6APM_STREAM_RUNNING)138			q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, 0);139 140		break;141	case APM_CLIENT_EVENT_DATA_READ_DONE:142		spin_lock_irqsave(&prtd->lock, flags);143		prtd->pos += prtd->pcm_count;144		spin_unlock_irqrestore(&prtd->lock, flags);145		snd_pcm_period_elapsed(substream);146		if (prtd->state == Q6APM_STREAM_RUNNING)147			q6apm_read(prtd->graph);148 149		break;150	default:151		break;152	}153}154 155static void event_handler_compr(uint32_t opcode, uint32_t token,156				void *payload, void *priv)157{158	struct q6apm_dai_rtd *prtd = priv;159	struct snd_compr_stream *substream = prtd->cstream;160	unsigned long flags;161	uint32_t wflags = 0;162	uint64_t avail;163	uint32_t bytes_written, bytes_to_write;164	bool is_last_buffer = false;165 166	switch (opcode) {167	case APM_CLIENT_EVENT_CMD_EOS_DONE:168		spin_lock_irqsave(&prtd->lock, flags);169		if (prtd->notify_on_drain) {170			snd_compr_drain_notify(prtd->cstream);171			prtd->notify_on_drain = false;172		} else {173			prtd->state = Q6APM_STREAM_STOPPED;174		}175		spin_unlock_irqrestore(&prtd->lock, flags);176		break;177	case APM_CLIENT_EVENT_DATA_WRITE_DONE:178		spin_lock_irqsave(&prtd->lock, flags);179		bytes_written = token >> APM_WRITE_TOKEN_LEN_SHIFT;180		prtd->copied_total += bytes_written;181		snd_compr_fragment_elapsed(substream);182 183		if (prtd->state != Q6APM_STREAM_RUNNING) {184			spin_unlock_irqrestore(&prtd->lock, flags);185			break;186		}187 188		avail = prtd->bytes_received - prtd->bytes_sent;189 190		if (avail > prtd->pcm_count) {191			bytes_to_write = prtd->pcm_count;192		} else {193			if (substream->partial_drain || prtd->notify_on_drain)194				is_last_buffer = true;195			bytes_to_write = avail;196		}197 198		if (bytes_to_write) {199			if (substream->partial_drain && is_last_buffer)200				wflags |= APM_LAST_BUFFER_FLAG;201 202			q6apm_write_async(prtd->graph,203						bytes_to_write, 0, 0, wflags);204 205			prtd->bytes_sent += bytes_to_write;206 207			if (prtd->notify_on_drain && is_last_buffer)208				audioreach_shared_memory_send_eos(prtd->graph);209		}210 211		spin_unlock_irqrestore(&prtd->lock, flags);212		break;213	default:214		break;215	}216}217 218static int q6apm_dai_prepare(struct snd_soc_component *component,219			     struct snd_pcm_substream *substream)220{221	struct snd_pcm_runtime *runtime = substream->runtime;222	struct q6apm_dai_rtd *prtd = runtime->private_data;223	struct audioreach_module_config cfg;224	struct device *dev = component->dev;225	struct q6apm_dai_data *pdata;226	int ret;227 228	pdata = snd_soc_component_get_drvdata(component);229	if (!pdata)230		return -EINVAL;231 232	if (!prtd || !prtd->graph) {233		dev_err(dev, "%s: private data null or audio client freed\n", __func__);234		return -EINVAL;235	}236 237	cfg.direction = substream->stream;238	cfg.sample_rate = runtime->rate;239	cfg.num_channels = runtime->channels;240	cfg.bit_width = prtd->bits_per_sample;241	cfg.fmt = SND_AUDIOCODEC_PCM;242	audioreach_set_default_channel_mapping(cfg.channel_map, runtime->channels);243 244	if (prtd->state) {245		/* clear the previous setup if any  */246		q6apm_graph_stop(prtd->graph);247		q6apm_unmap_memory_regions(prtd->graph, substream->stream);248	}249 250	prtd->pcm_count = snd_pcm_lib_period_bytes(substream);251	prtd->pos = 0;252	/* rate and channels are sent to audio driver */253	ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg);254	if (ret < 0) {255		dev_err(dev, "%s: q6apm_open_write failed\n", __func__);256		return ret;257	}258 259	ret = q6apm_graph_media_format_pcm(prtd->graph, &cfg);260	if (ret < 0)261		dev_err(dev, "%s: CMD Format block failed\n", __func__);262 263	ret = q6apm_map_memory_regions(prtd->graph, substream->stream, prtd->phys,264				       (prtd->pcm_size / prtd->periods), prtd->periods);265 266	if (ret < 0) {267		dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n",	ret);268		return -ENOMEM;269	}270 271	ret = q6apm_graph_prepare(prtd->graph);272	if (ret) {273		dev_err(dev, "Failed to prepare Graph %d\n", ret);274		return ret;275	}276 277	ret = q6apm_graph_start(prtd->graph);278	if (ret) {279		dev_err(dev, "Failed to Start Graph %d\n", ret);280		return ret;281	}282 283	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {284		int i;285		/* Queue the buffers for Capture ONLY after graph is started */286		for (i = 0; i < runtime->periods; i++)287			q6apm_read(prtd->graph);288 289	}290 291	/* Now that graph as been prepared and started update the internal state accordingly */292	prtd->state = Q6APM_STREAM_RUNNING;293 294	return 0;295}296 297static int q6apm_dai_trigger(struct snd_soc_component *component,298			     struct snd_pcm_substream *substream, int cmd)299{300	struct snd_pcm_runtime *runtime = substream->runtime;301	struct q6apm_dai_rtd *prtd = runtime->private_data;302	int ret = 0;303 304	switch (cmd) {305	case SNDRV_PCM_TRIGGER_START:306	case SNDRV_PCM_TRIGGER_RESUME:307	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:308		 /* start writing buffers for playback only as we already queued capture buffers */309		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)310			ret = q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, 0);311		break;312	case SNDRV_PCM_TRIGGER_STOP:313		/* TODO support be handled via SoftPause Module */314		prtd->state = Q6APM_STREAM_STOPPED;315		break;316	case SNDRV_PCM_TRIGGER_SUSPEND:317	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:318		break;319	default:320		ret = -EINVAL;321		break;322	}323 324	return ret;325}326 327static int q6apm_dai_open(struct snd_soc_component *component,328			  struct snd_pcm_substream *substream)329{330	struct snd_pcm_runtime *runtime = substream->runtime;331	struct snd_soc_pcm_runtime *soc_prtd = snd_soc_substream_to_rtd(substream);332	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_prtd, 0);333	struct device *dev = component->dev;334	struct q6apm_dai_data *pdata;335	struct q6apm_dai_rtd *prtd;336	int graph_id, ret;337 338	graph_id = cpu_dai->driver->id;339 340	pdata = snd_soc_component_get_drvdata(component);341	if (!pdata) {342		dev_err(dev, "Drv data not found ..\n");343		return -EINVAL;344	}345 346	prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);347	if (prtd == NULL)348		return -ENOMEM;349 350	spin_lock_init(&prtd->lock);351	prtd->substream = substream;352	prtd->graph = q6apm_graph_open(dev, event_handler, prtd, graph_id);353	if (IS_ERR(prtd->graph)) {354		dev_err(dev, "%s: Could not allocate memory\n", __func__);355		ret = PTR_ERR(prtd->graph);356		goto err;357	}358 359	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)360		runtime->hw = q6apm_dai_hardware_playback;361	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)362		runtime->hw = q6apm_dai_hardware_capture;363 364	/* Ensure that buffer size is a multiple of period size */365	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);366	if (ret < 0) {367		dev_err(dev, "snd_pcm_hw_constraint_integer failed\n");368		goto err;369	}370 371	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {372		ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,373						   BUFFER_BYTES_MIN, BUFFER_BYTES_MAX);374		if (ret < 0) {375			dev_err(dev, "constraint for buffer bytes min max ret = %d\n", ret);376			goto err;377		}378	}379 380	ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);381	if (ret < 0) {382		dev_err(dev, "constraint for period bytes step ret = %d\n", ret);383		goto err;384	}385 386	ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);387	if (ret < 0) {388		dev_err(dev, "constraint for buffer bytes step ret = %d\n", ret);389		goto err;390	}391 392	runtime->private_data = prtd;393	runtime->dma_bytes = BUFFER_BYTES_MAX;394	if (pdata->sid < 0)395		prtd->phys = substream->dma_buffer.addr;396	else397		prtd->phys = substream->dma_buffer.addr | (pdata->sid << 32);398 399	return 0;400err:401	kfree(prtd);402 403	return ret;404}405 406static int q6apm_dai_close(struct snd_soc_component *component,407			   struct snd_pcm_substream *substream)408{409	struct snd_pcm_runtime *runtime = substream->runtime;410	struct q6apm_dai_rtd *prtd = runtime->private_data;411 412	if (prtd->state) { /* only stop graph that is started */413		q6apm_graph_stop(prtd->graph);414		q6apm_unmap_memory_regions(prtd->graph, substream->stream);415	}416 417	q6apm_graph_close(prtd->graph);418	prtd->graph = NULL;419	kfree(prtd);420	runtime->private_data = NULL;421 422	return 0;423}424 425static snd_pcm_uframes_t q6apm_dai_pointer(struct snd_soc_component *component,426					   struct snd_pcm_substream *substream)427{428	struct snd_pcm_runtime *runtime = substream->runtime;429	struct q6apm_dai_rtd *prtd = runtime->private_data;430	snd_pcm_uframes_t ptr;431	unsigned long flags;432 433	spin_lock_irqsave(&prtd->lock, flags);434	if (prtd->pos == prtd->pcm_size)435		prtd->pos = 0;436 437	ptr =  bytes_to_frames(runtime, prtd->pos);438	spin_unlock_irqrestore(&prtd->lock, flags);439 440	return ptr;441}442 443static int q6apm_dai_hw_params(struct snd_soc_component *component,444			       struct snd_pcm_substream *substream,445			       struct snd_pcm_hw_params *params)446{447	struct snd_pcm_runtime *runtime = substream->runtime;448	struct q6apm_dai_rtd *prtd = runtime->private_data;449 450	prtd->pcm_size = params_buffer_bytes(params);451	prtd->periods = params_periods(params);452 453	switch (params_format(params)) {454	case SNDRV_PCM_FORMAT_S16_LE:455		prtd->bits_per_sample = 16;456		break;457	case SNDRV_PCM_FORMAT_S24_LE:458		prtd->bits_per_sample = 24;459		break;460	default:461		return -EINVAL;462	}463 464	return 0;465}466 467static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd)468{469	int size = BUFFER_BYTES_MAX;470 471	return snd_pcm_set_fixed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, component->dev, size);472}473 474static int q6apm_dai_compr_open(struct snd_soc_component *component,475				struct snd_compr_stream *stream)476{477	struct snd_soc_pcm_runtime *rtd = stream->private_data;478	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);479	struct snd_compr_runtime *runtime = stream->runtime;480	struct q6apm_dai_rtd *prtd;481	struct q6apm_dai_data *pdata;482	struct device *dev = component->dev;483	int ret, size;484	int graph_id;485 486	graph_id = cpu_dai->driver->id;487	pdata = snd_soc_component_get_drvdata(component);488	if (!pdata)489		return -EINVAL;490 491	prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);492	if (prtd == NULL)493		return -ENOMEM;494 495	prtd->cstream = stream;496	prtd->graph = q6apm_graph_open(dev, event_handler_compr, prtd, graph_id);497	if (IS_ERR(prtd->graph)) {498		ret = PTR_ERR(prtd->graph);499		kfree(prtd);500		return ret;501	}502 503	runtime->private_data = prtd;504	runtime->dma_bytes = BUFFER_BYTES_MAX;505	size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE * COMPR_PLAYBACK_MAX_NUM_FRAGMENTS;506	ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &prtd->dma_buffer);507	if (ret)508		return ret;509 510	if (pdata->sid < 0)511		prtd->phys = prtd->dma_buffer.addr;512	else513		prtd->phys = prtd->dma_buffer.addr | (pdata->sid << 32);514 515	snd_compr_set_runtime_buffer(stream, &prtd->dma_buffer);516	spin_lock_init(&prtd->lock);517 518	q6apm_enable_compress_module(dev, prtd->graph, true);519	return 0;520}521 522static int q6apm_dai_compr_free(struct snd_soc_component *component,523				struct snd_compr_stream *stream)524{525	struct snd_compr_runtime *runtime = stream->runtime;526	struct q6apm_dai_rtd *prtd = runtime->private_data;527 528	q6apm_graph_stop(prtd->graph);529	q6apm_unmap_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK);530	q6apm_graph_close(prtd->graph);531	snd_dma_free_pages(&prtd->dma_buffer);532	prtd->graph = NULL;533	kfree(prtd);534	runtime->private_data = NULL;535 536	return 0;537}538 539static int q6apm_dai_compr_get_caps(struct snd_soc_component *component,540				    struct snd_compr_stream *stream,541				    struct snd_compr_caps *caps)542{543	caps->direction = SND_COMPRESS_PLAYBACK;544	caps->min_fragment_size = COMPR_PLAYBACK_MIN_FRAGMENT_SIZE;545	caps->max_fragment_size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE;546	caps->min_fragments = COMPR_PLAYBACK_MIN_NUM_FRAGMENTS;547	caps->max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS;548	caps->num_codecs = 3;549	caps->codecs[0] = SND_AUDIOCODEC_MP3;550	caps->codecs[1] = SND_AUDIOCODEC_AAC;551	caps->codecs[2] = SND_AUDIOCODEC_FLAC;552 553	return 0;554}555 556static int q6apm_dai_compr_get_codec_caps(struct snd_soc_component *component,557					  struct snd_compr_stream *stream,558					  struct snd_compr_codec_caps *codec)559{560	switch (codec->codec) {561	case SND_AUDIOCODEC_MP3:562		*codec = q6apm_compr_caps;563		break;564	default:565		break;566	}567 568	return 0;569}570 571static int q6apm_dai_compr_pointer(struct snd_soc_component *component,572				   struct snd_compr_stream *stream,573				   struct snd_compr_tstamp *tstamp)574{575	struct snd_compr_runtime *runtime = stream->runtime;576	struct q6apm_dai_rtd *prtd = runtime->private_data;577	unsigned long flags;578 579	spin_lock_irqsave(&prtd->lock, flags);580	tstamp->copied_total = prtd->copied_total;581	tstamp->byte_offset = prtd->copied_total % prtd->pcm_size;582	spin_unlock_irqrestore(&prtd->lock, flags);583 584	return 0;585}586 587static int q6apm_dai_compr_trigger(struct snd_soc_component *component,588			    struct snd_compr_stream *stream, int cmd)589{590	struct snd_compr_runtime *runtime = stream->runtime;591	struct q6apm_dai_rtd *prtd = runtime->private_data;592	int ret = 0;593 594	switch (cmd) {595	case SNDRV_PCM_TRIGGER_START:596	case SNDRV_PCM_TRIGGER_RESUME:597	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:598		ret = q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, NO_TIMESTAMP);599		break;600	case SNDRV_PCM_TRIGGER_STOP:601		break;602	case SNDRV_PCM_TRIGGER_SUSPEND:603	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:604		break;605	case SND_COMPR_TRIGGER_NEXT_TRACK:606		prtd->next_track = true;607		break;608	case SND_COMPR_TRIGGER_DRAIN:609	case SND_COMPR_TRIGGER_PARTIAL_DRAIN:610		prtd->notify_on_drain = true;611		break;612	default:613		ret = -EINVAL;614		break;615	}616 617	return ret;618}619 620static int q6apm_dai_compr_ack(struct snd_soc_component *component, struct snd_compr_stream *stream,621			size_t count)622{623	struct snd_compr_runtime *runtime = stream->runtime;624	struct q6apm_dai_rtd *prtd = runtime->private_data;625	unsigned long flags;626 627	spin_lock_irqsave(&prtd->lock, flags);628	prtd->bytes_received += count;629	spin_unlock_irqrestore(&prtd->lock, flags);630 631	return count;632}633 634static int q6apm_dai_compr_set_params(struct snd_soc_component *component,635				      struct snd_compr_stream *stream,636				      struct snd_compr_params *params)637{638	struct snd_compr_runtime *runtime = stream->runtime;639	struct q6apm_dai_rtd *prtd = runtime->private_data;640	struct q6apm_dai_data *pdata;641	struct audioreach_module_config cfg;642	struct snd_codec *codec = &params->codec;643	int dir = stream->direction;644	int ret;645 646	pdata = snd_soc_component_get_drvdata(component);647	if (!pdata)648		return -EINVAL;649 650	prtd->periods = runtime->fragments;651	prtd->pcm_count = runtime->fragment_size;652	prtd->pcm_size = runtime->fragments * runtime->fragment_size;653	prtd->bits_per_sample = 16;654 655	prtd->pos = 0;656 657	if (prtd->next_track != true) {658		memcpy(&prtd->codec, codec, sizeof(*codec));659 660		ret = q6apm_set_real_module_id(component->dev, prtd->graph, codec->id);661		if (ret)662			return ret;663 664		cfg.direction = dir;665		cfg.sample_rate = codec->sample_rate;666		cfg.num_channels = 2;667		cfg.bit_width = prtd->bits_per_sample;668		cfg.fmt = codec->id;669		audioreach_set_default_channel_mapping(cfg.channel_map,670						       cfg.num_channels);671		memcpy(&cfg.codec, codec, sizeof(*codec));672 673		ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg);674		if (ret < 0)675			return ret;676 677		ret = q6apm_graph_media_format_pcm(prtd->graph, &cfg);678		if (ret)679			return ret;680 681		ret = q6apm_map_memory_regions(prtd->graph, SNDRV_PCM_STREAM_PLAYBACK,682					       prtd->phys, (prtd->pcm_size / prtd->periods),683					       prtd->periods);684		if (ret < 0)685			return -ENOMEM;686 687		ret = q6apm_graph_prepare(prtd->graph);688		if (ret)689			return ret;690 691		ret = q6apm_graph_start(prtd->graph);692		if (ret)693			return ret;694 695	} else {696		cfg.direction = dir;697		cfg.sample_rate = codec->sample_rate;698		cfg.num_channels = 2;699		cfg.bit_width = prtd->bits_per_sample;700		cfg.fmt = codec->id;701		memcpy(&cfg.codec, codec, sizeof(*codec));702 703		ret = audioreach_compr_set_param(prtd->graph,  &cfg);704		if (ret < 0)705			return ret;706	}707	prtd->state = Q6APM_STREAM_RUNNING;708 709	return 0;710}711 712static int q6apm_dai_compr_set_metadata(struct snd_soc_component *component,713					struct snd_compr_stream *stream,714					struct snd_compr_metadata *metadata)715{716	struct snd_compr_runtime *runtime = stream->runtime;717	struct q6apm_dai_rtd *prtd = runtime->private_data;718	int ret = 0;719 720	switch (metadata->key) {721	case SNDRV_COMPRESS_ENCODER_PADDING:722		q6apm_remove_trailing_silence(component->dev, prtd->graph,723					      metadata->value[0]);724		break;725	case SNDRV_COMPRESS_ENCODER_DELAY:726		q6apm_remove_initial_silence(component->dev, prtd->graph,727					     metadata->value[0]);728		break;729	default:730		ret = -EINVAL;731		break;732	}733 734	return ret;735}736 737static int q6apm_dai_compr_mmap(struct snd_soc_component *component,738				struct snd_compr_stream *stream,739				struct vm_area_struct *vma)740{741	struct snd_compr_runtime *runtime = stream->runtime;742	struct q6apm_dai_rtd *prtd = runtime->private_data;743	struct device *dev = component->dev;744 745	return dma_mmap_coherent(dev, vma, prtd->dma_buffer.area, prtd->dma_buffer.addr,746				 prtd->dma_buffer.bytes);747}748 749static int q6apm_compr_copy(struct snd_soc_component *component,750			    struct snd_compr_stream *stream, char __user *buf,751			    size_t count)752{753	struct snd_compr_runtime *runtime = stream->runtime;754	struct q6apm_dai_rtd *prtd = runtime->private_data;755	void *dstn;756	unsigned long flags;757	size_t copy;758	u32 wflags = 0;759	u32 app_pointer;760	u32 bytes_received;761	uint32_t bytes_to_write;762	int avail, bytes_in_flight = 0;763 764	bytes_received = prtd->bytes_received;765 766	/**767	 * Make sure that next track data pointer is aligned at 32 bit boundary768	 * This is a Mandatory requirement from DSP data buffers alignment769	 */770	if (prtd->next_track)771		bytes_received = ALIGN(prtd->bytes_received, prtd->pcm_count);772 773	app_pointer = bytes_received/prtd->pcm_size;774	app_pointer = bytes_received -  (app_pointer * prtd->pcm_size);775	dstn = prtd->dma_buffer.area + app_pointer;776 777	if (count < prtd->pcm_size - app_pointer) {778		if (copy_from_user(dstn, buf, count))779			return -EFAULT;780	} else {781		copy = prtd->pcm_size - app_pointer;782		if (copy_from_user(dstn, buf, copy))783			return -EFAULT;784		if (copy_from_user(prtd->dma_buffer.area, buf + copy, count - copy))785			return -EFAULT;786	}787 788	spin_lock_irqsave(&prtd->lock, flags);789	bytes_in_flight = prtd->bytes_received - prtd->copied_total;790 791	if (prtd->next_track) {792		prtd->next_track = false;793		prtd->copied_total = ALIGN(prtd->copied_total, prtd->pcm_count);794		prtd->bytes_sent = ALIGN(prtd->bytes_sent, prtd->pcm_count);795	}796 797	prtd->bytes_received = bytes_received + count;798 799	/* Kick off the data to dsp if its starving!! */800	if (prtd->state == Q6APM_STREAM_RUNNING && (bytes_in_flight == 0)) {801		bytes_to_write = prtd->pcm_count;802		avail = prtd->bytes_received - prtd->bytes_sent;803 804		if (avail < prtd->pcm_count)805			bytes_to_write = avail;806 807		q6apm_write_async(prtd->graph, bytes_to_write, 0, 0, wflags);808		prtd->bytes_sent += bytes_to_write;809	}810 811	spin_unlock_irqrestore(&prtd->lock, flags);812 813	return count;814}815 816static const struct snd_compress_ops q6apm_dai_compress_ops = {817	.open		= q6apm_dai_compr_open,818	.free		= q6apm_dai_compr_free,819	.get_caps	= q6apm_dai_compr_get_caps,820	.get_codec_caps	= q6apm_dai_compr_get_codec_caps,821	.pointer	= q6apm_dai_compr_pointer,822	.trigger	= q6apm_dai_compr_trigger,823	.ack		= q6apm_dai_compr_ack,824	.set_params	= q6apm_dai_compr_set_params,825	.set_metadata	= q6apm_dai_compr_set_metadata,826	.mmap		= q6apm_dai_compr_mmap,827	.copy		= q6apm_compr_copy,828};829 830static const struct snd_soc_component_driver q6apm_fe_dai_component = {831	.name		= DRV_NAME,832	.open		= q6apm_dai_open,833	.close		= q6apm_dai_close,834	.prepare	= q6apm_dai_prepare,835	.pcm_construct	= q6apm_dai_pcm_new,836	.hw_params	= q6apm_dai_hw_params,837	.pointer	= q6apm_dai_pointer,838	.trigger	= q6apm_dai_trigger,839	.compress_ops	= &q6apm_dai_compress_ops,840	.use_dai_pcm_id = true,841};842 843static int q6apm_dai_probe(struct platform_device *pdev)844{845	struct device *dev = &pdev->dev;846	struct device_node *node = dev->of_node;847	struct q6apm_dai_data *pdata;848	struct of_phandle_args args;849	int rc;850 851	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);852	if (!pdata)853		return -ENOMEM;854 855	rc = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args);856	if (rc < 0)857		pdata->sid = -1;858	else859		pdata->sid = args.args[0] & SID_MASK_DEFAULT;860 861	dev_set_drvdata(dev, pdata);862 863	return devm_snd_soc_register_component(dev, &q6apm_fe_dai_component, NULL, 0);864}865 866#ifdef CONFIG_OF867static const struct of_device_id q6apm_dai_device_id[] = {868	{ .compatible = "qcom,q6apm-dais" },869	{},870};871MODULE_DEVICE_TABLE(of, q6apm_dai_device_id);872#endif873 874static struct platform_driver q6apm_dai_platform_driver = {875	.driver = {876		.name = "q6apm-dai",877		.of_match_table = of_match_ptr(q6apm_dai_device_id),878	},879	.probe = q6apm_dai_probe,880};881module_platform_driver(q6apm_dai_platform_driver);882 883MODULE_DESCRIPTION("Q6APM dai driver");884MODULE_LICENSE("GPL");885