xref: /linux/sound/soc/qcom/qdsp6/q6apm-dai.c (revision 58136d93d4e2c1207a5e4f3044815cd40b1d95fd)
19b4fe0f1SSrinivas Kandagatla // SPDX-License-Identifier: GPL-2.0
29b4fe0f1SSrinivas Kandagatla // Copyright (c) 2021, Linaro Limited
39b4fe0f1SSrinivas Kandagatla 
49b4fe0f1SSrinivas Kandagatla #include <linux/init.h>
59b4fe0f1SSrinivas Kandagatla #include <linux/err.h>
69b4fe0f1SSrinivas Kandagatla #include <linux/module.h>
79b4fe0f1SSrinivas Kandagatla #include <linux/platform_device.h>
89b4fe0f1SSrinivas Kandagatla #include <linux/slab.h>
99b4fe0f1SSrinivas Kandagatla #include <sound/soc.h>
109b4fe0f1SSrinivas Kandagatla #include <sound/soc-dapm.h>
119b4fe0f1SSrinivas Kandagatla #include <sound/pcm.h>
129b4fe0f1SSrinivas Kandagatla #include <asm/dma.h>
139b4fe0f1SSrinivas Kandagatla #include <linux/dma-mapping.h>
149b4fe0f1SSrinivas Kandagatla #include <linux/of_device.h>
159b4fe0f1SSrinivas Kandagatla #include <sound/pcm_params.h>
169b4fe0f1SSrinivas Kandagatla #include "q6apm.h"
179b4fe0f1SSrinivas Kandagatla 
189b4fe0f1SSrinivas Kandagatla #define DRV_NAME "q6apm-dai"
199b4fe0f1SSrinivas Kandagatla 
209b4fe0f1SSrinivas Kandagatla #define PLAYBACK_MIN_NUM_PERIODS	2
219b4fe0f1SSrinivas Kandagatla #define PLAYBACK_MAX_NUM_PERIODS	8
229b4fe0f1SSrinivas Kandagatla #define PLAYBACK_MAX_PERIOD_SIZE	65536
239b4fe0f1SSrinivas Kandagatla #define PLAYBACK_MIN_PERIOD_SIZE	128
249b4fe0f1SSrinivas Kandagatla #define CAPTURE_MIN_NUM_PERIODS		2
259b4fe0f1SSrinivas Kandagatla #define CAPTURE_MAX_NUM_PERIODS		8
269b4fe0f1SSrinivas Kandagatla #define CAPTURE_MAX_PERIOD_SIZE		4096
279b4fe0f1SSrinivas Kandagatla #define CAPTURE_MIN_PERIOD_SIZE		320
289b4fe0f1SSrinivas Kandagatla #define BUFFER_BYTES_MAX (PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE)
299b4fe0f1SSrinivas Kandagatla #define BUFFER_BYTES_MIN (PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE)
309b4fe0f1SSrinivas Kandagatla #define SID_MASK_DEFAULT	0xF
319b4fe0f1SSrinivas Kandagatla 
329b4fe0f1SSrinivas Kandagatla enum stream_state {
339b4fe0f1SSrinivas Kandagatla 	Q6APM_STREAM_IDLE = 0,
349b4fe0f1SSrinivas Kandagatla 	Q6APM_STREAM_STOPPED,
359b4fe0f1SSrinivas Kandagatla 	Q6APM_STREAM_RUNNING,
369b4fe0f1SSrinivas Kandagatla };
379b4fe0f1SSrinivas Kandagatla 
389b4fe0f1SSrinivas Kandagatla struct q6apm_dai_rtd {
399b4fe0f1SSrinivas Kandagatla 	struct snd_pcm_substream *substream;
409b4fe0f1SSrinivas Kandagatla 	struct snd_compr_stream *cstream;
419b4fe0f1SSrinivas Kandagatla 	struct snd_compr_params codec_param;
429b4fe0f1SSrinivas Kandagatla 	struct snd_dma_buffer dma_buffer;
439b4fe0f1SSrinivas Kandagatla 	phys_addr_t phys;
449b4fe0f1SSrinivas Kandagatla 	unsigned int pcm_size;
459b4fe0f1SSrinivas Kandagatla 	unsigned int pcm_count;
469b4fe0f1SSrinivas Kandagatla 	unsigned int pos;       /* Buffer position */
479b4fe0f1SSrinivas Kandagatla 	unsigned int periods;
489b4fe0f1SSrinivas Kandagatla 	unsigned int bytes_sent;
499b4fe0f1SSrinivas Kandagatla 	unsigned int bytes_received;
509b4fe0f1SSrinivas Kandagatla 	unsigned int copied_total;
519b4fe0f1SSrinivas Kandagatla 	uint16_t bits_per_sample;
529b4fe0f1SSrinivas Kandagatla 	uint16_t source; /* Encoding source bit mask */
539b4fe0f1SSrinivas Kandagatla 	uint16_t session_id;
549b4fe0f1SSrinivas Kandagatla 	enum stream_state state;
559b4fe0f1SSrinivas Kandagatla 	struct q6apm_graph *graph;
569b4fe0f1SSrinivas Kandagatla };
579b4fe0f1SSrinivas Kandagatla 
589b4fe0f1SSrinivas Kandagatla struct q6apm_dai_data {
599b4fe0f1SSrinivas Kandagatla 	long long sid;
609b4fe0f1SSrinivas Kandagatla };
619b4fe0f1SSrinivas Kandagatla 
629b4fe0f1SSrinivas Kandagatla static struct snd_pcm_hardware q6apm_dai_hardware_capture = {
639b4fe0f1SSrinivas Kandagatla 	.info =                 (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER |
649b4fe0f1SSrinivas Kandagatla 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED |
659b4fe0f1SSrinivas Kandagatla 				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
669b4fe0f1SSrinivas Kandagatla 	.formats =              (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE),
679b4fe0f1SSrinivas Kandagatla 	.rates =                SNDRV_PCM_RATE_8000_48000,
689b4fe0f1SSrinivas Kandagatla 	.rate_min =             8000,
699b4fe0f1SSrinivas Kandagatla 	.rate_max =             48000,
709b4fe0f1SSrinivas Kandagatla 	.channels_min =         2,
719b4fe0f1SSrinivas Kandagatla 	.channels_max =         4,
729b4fe0f1SSrinivas Kandagatla 	.buffer_bytes_max =     CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE,
739b4fe0f1SSrinivas Kandagatla 	.period_bytes_min =	CAPTURE_MIN_PERIOD_SIZE,
749b4fe0f1SSrinivas Kandagatla 	.period_bytes_max =     CAPTURE_MAX_PERIOD_SIZE,
759b4fe0f1SSrinivas Kandagatla 	.periods_min =          CAPTURE_MIN_NUM_PERIODS,
769b4fe0f1SSrinivas Kandagatla 	.periods_max =          CAPTURE_MAX_NUM_PERIODS,
779b4fe0f1SSrinivas Kandagatla 	.fifo_size =            0,
789b4fe0f1SSrinivas Kandagatla };
799b4fe0f1SSrinivas Kandagatla 
809b4fe0f1SSrinivas Kandagatla static struct snd_pcm_hardware q6apm_dai_hardware_playback = {
819b4fe0f1SSrinivas Kandagatla 	.info =                 (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER |
829b4fe0f1SSrinivas Kandagatla 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED |
839b4fe0f1SSrinivas Kandagatla 				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
849b4fe0f1SSrinivas Kandagatla 	.formats =              (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE),
859b4fe0f1SSrinivas Kandagatla 	.rates =                SNDRV_PCM_RATE_8000_192000,
869b4fe0f1SSrinivas Kandagatla 	.rate_min =             8000,
879b4fe0f1SSrinivas Kandagatla 	.rate_max =             192000,
889b4fe0f1SSrinivas Kandagatla 	.channels_min =         2,
899b4fe0f1SSrinivas Kandagatla 	.channels_max =         8,
909b4fe0f1SSrinivas Kandagatla 	.buffer_bytes_max =     (PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE),
919b4fe0f1SSrinivas Kandagatla 	.period_bytes_min =	PLAYBACK_MIN_PERIOD_SIZE,
929b4fe0f1SSrinivas Kandagatla 	.period_bytes_max =     PLAYBACK_MAX_PERIOD_SIZE,
939b4fe0f1SSrinivas Kandagatla 	.periods_min =          PLAYBACK_MIN_NUM_PERIODS,
949b4fe0f1SSrinivas Kandagatla 	.periods_max =          PLAYBACK_MAX_NUM_PERIODS,
959b4fe0f1SSrinivas Kandagatla 	.fifo_size =            0,
969b4fe0f1SSrinivas Kandagatla };
979b4fe0f1SSrinivas Kandagatla 
989b4fe0f1SSrinivas Kandagatla static void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, void *priv)
999b4fe0f1SSrinivas Kandagatla {
1009b4fe0f1SSrinivas Kandagatla 	struct q6apm_dai_rtd *prtd = priv;
1019b4fe0f1SSrinivas Kandagatla 	struct snd_pcm_substream *substream = prtd->substream;
1029b4fe0f1SSrinivas Kandagatla 
1039b4fe0f1SSrinivas Kandagatla 	switch (opcode) {
1049b4fe0f1SSrinivas Kandagatla 	case APM_CLIENT_EVENT_CMD_EOS_DONE:
1059b4fe0f1SSrinivas Kandagatla 		prtd->state = Q6APM_STREAM_STOPPED;
1069b4fe0f1SSrinivas Kandagatla 		break;
1079b4fe0f1SSrinivas Kandagatla 	case APM_CLIENT_EVENT_DATA_WRITE_DONE:
1089b4fe0f1SSrinivas Kandagatla 		prtd->pos += prtd->pcm_count;
1099b4fe0f1SSrinivas Kandagatla 		snd_pcm_period_elapsed(substream);
1109b4fe0f1SSrinivas Kandagatla 		if (prtd->state == Q6APM_STREAM_RUNNING)
1119b4fe0f1SSrinivas Kandagatla 			q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, 0);
1129b4fe0f1SSrinivas Kandagatla 
1139b4fe0f1SSrinivas Kandagatla 		break;
1149b4fe0f1SSrinivas Kandagatla 	case APM_CLIENT_EVENT_DATA_READ_DONE:
1159b4fe0f1SSrinivas Kandagatla 		prtd->pos += prtd->pcm_count;
1169b4fe0f1SSrinivas Kandagatla 		snd_pcm_period_elapsed(substream);
1179b4fe0f1SSrinivas Kandagatla 		if (prtd->state == Q6APM_STREAM_RUNNING)
1189b4fe0f1SSrinivas Kandagatla 			q6apm_read(prtd->graph);
1199b4fe0f1SSrinivas Kandagatla 
1209b4fe0f1SSrinivas Kandagatla 		break;
1219b4fe0f1SSrinivas Kandagatla 	default:
1229b4fe0f1SSrinivas Kandagatla 		break;
1239b4fe0f1SSrinivas Kandagatla 	}
1249b4fe0f1SSrinivas Kandagatla }
1259b4fe0f1SSrinivas Kandagatla 
1269b4fe0f1SSrinivas Kandagatla static int q6apm_dai_prepare(struct snd_soc_component *component,
1279b4fe0f1SSrinivas Kandagatla 			     struct snd_pcm_substream *substream)
1289b4fe0f1SSrinivas Kandagatla {
1299b4fe0f1SSrinivas Kandagatla 	struct snd_pcm_runtime *runtime = substream->runtime;
1309b4fe0f1SSrinivas Kandagatla 	struct q6apm_dai_rtd *prtd = runtime->private_data;
1319b4fe0f1SSrinivas Kandagatla 	struct audioreach_module_config cfg;
1329b4fe0f1SSrinivas Kandagatla 	struct device *dev = component->dev;
1339b4fe0f1SSrinivas Kandagatla 	struct q6apm_dai_data *pdata;
1349b4fe0f1SSrinivas Kandagatla 	int ret;
1359b4fe0f1SSrinivas Kandagatla 
1369b4fe0f1SSrinivas Kandagatla 	pdata = snd_soc_component_get_drvdata(component);
1379b4fe0f1SSrinivas Kandagatla 	if (!pdata)
1389b4fe0f1SSrinivas Kandagatla 		return -EINVAL;
1399b4fe0f1SSrinivas Kandagatla 
1409b4fe0f1SSrinivas Kandagatla 	if (!prtd || !prtd->graph) {
1419b4fe0f1SSrinivas Kandagatla 		dev_err(dev, "%s: private data null or audio client freed\n", __func__);
1429b4fe0f1SSrinivas Kandagatla 		return -EINVAL;
1439b4fe0f1SSrinivas Kandagatla 	}
1449b4fe0f1SSrinivas Kandagatla 
1459b4fe0f1SSrinivas Kandagatla 	cfg.direction = substream->stream;
1469b4fe0f1SSrinivas Kandagatla 	cfg.sample_rate = runtime->rate;
1479b4fe0f1SSrinivas Kandagatla 	cfg.num_channels = runtime->channels;
1489b4fe0f1SSrinivas Kandagatla 	cfg.bit_width = prtd->bits_per_sample;
1499b4fe0f1SSrinivas Kandagatla 
150*58136d93SSrinivas Kandagatla 	if (prtd->state) {
151*58136d93SSrinivas Kandagatla 		/* clear the previous setup if any  */
152*58136d93SSrinivas Kandagatla 		q6apm_graph_stop(prtd->graph);
153*58136d93SSrinivas Kandagatla 		q6apm_unmap_memory_regions(prtd->graph, substream->stream);
154*58136d93SSrinivas Kandagatla 	}
155*58136d93SSrinivas Kandagatla 
1569b4fe0f1SSrinivas Kandagatla 	prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
1579b4fe0f1SSrinivas Kandagatla 	prtd->pos = 0;
1589b4fe0f1SSrinivas Kandagatla 	/* rate and channels are sent to audio driver */
1599b4fe0f1SSrinivas Kandagatla 	ret = q6apm_graph_media_format_shmem(prtd->graph, &cfg);
1609b4fe0f1SSrinivas Kandagatla 	if (ret < 0) {
1619b4fe0f1SSrinivas Kandagatla 		dev_err(dev, "%s: q6apm_open_write failed\n", __func__);
1629b4fe0f1SSrinivas Kandagatla 		return ret;
1639b4fe0f1SSrinivas Kandagatla 	}
1649b4fe0f1SSrinivas Kandagatla 
1659b4fe0f1SSrinivas Kandagatla 	ret = q6apm_graph_media_format_pcm(prtd->graph, &cfg);
1669b4fe0f1SSrinivas Kandagatla 	if (ret < 0)
1679b4fe0f1SSrinivas Kandagatla 		dev_err(dev, "%s: CMD Format block failed\n", __func__);
1689b4fe0f1SSrinivas Kandagatla 
1699b4fe0f1SSrinivas Kandagatla 	ret = q6apm_map_memory_regions(prtd->graph, substream->stream, prtd->phys,
1709b4fe0f1SSrinivas Kandagatla 				       (prtd->pcm_size / prtd->periods), prtd->periods);
1719b4fe0f1SSrinivas Kandagatla 
1729b4fe0f1SSrinivas Kandagatla 	if (ret < 0) {
1739b4fe0f1SSrinivas Kandagatla 		dev_err(dev, "Audio Start: Buffer Allocation failed rc = %d\n",	ret);
1749b4fe0f1SSrinivas Kandagatla 		return -ENOMEM;
1759b4fe0f1SSrinivas Kandagatla 	}
1769b4fe0f1SSrinivas Kandagatla 
1779b4fe0f1SSrinivas Kandagatla 	ret = q6apm_graph_prepare(prtd->graph);
1789b4fe0f1SSrinivas Kandagatla 	if (ret) {
1799b4fe0f1SSrinivas Kandagatla 		dev_err(dev, "Failed to prepare Graph %d\n", ret);
1809b4fe0f1SSrinivas Kandagatla 		return ret;
1819b4fe0f1SSrinivas Kandagatla 	}
1829b4fe0f1SSrinivas Kandagatla 
1839b4fe0f1SSrinivas Kandagatla 	ret = q6apm_graph_start(prtd->graph);
1849b4fe0f1SSrinivas Kandagatla 	if (ret) {
1859b4fe0f1SSrinivas Kandagatla 		dev_err(dev, "Failed to Start Graph %d\n", ret);
1869b4fe0f1SSrinivas Kandagatla 		return ret;
1879b4fe0f1SSrinivas Kandagatla 	}
1889b4fe0f1SSrinivas Kandagatla 
1899b4fe0f1SSrinivas Kandagatla 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
1909b4fe0f1SSrinivas Kandagatla 		int i;
1919b4fe0f1SSrinivas Kandagatla 		/* Queue the buffers for Capture ONLY after graph is started */
1929b4fe0f1SSrinivas Kandagatla 		for (i = 0; i < runtime->periods; i++)
1939b4fe0f1SSrinivas Kandagatla 			q6apm_read(prtd->graph);
1949b4fe0f1SSrinivas Kandagatla 
1959b4fe0f1SSrinivas Kandagatla 	}
1969b4fe0f1SSrinivas Kandagatla 
1979b4fe0f1SSrinivas Kandagatla 	/* Now that graph as been prepared and started update the internal state accordingly */
1989b4fe0f1SSrinivas Kandagatla 	prtd->state = Q6APM_STREAM_RUNNING;
1999b4fe0f1SSrinivas Kandagatla 
2009b4fe0f1SSrinivas Kandagatla 	return 0;
2019b4fe0f1SSrinivas Kandagatla }
2029b4fe0f1SSrinivas Kandagatla 
2039b4fe0f1SSrinivas Kandagatla static int q6apm_dai_trigger(struct snd_soc_component *component,
2049b4fe0f1SSrinivas Kandagatla 			     struct snd_pcm_substream *substream, int cmd)
2059b4fe0f1SSrinivas Kandagatla {
2069b4fe0f1SSrinivas Kandagatla 	struct snd_pcm_runtime *runtime = substream->runtime;
2079b4fe0f1SSrinivas Kandagatla 	struct q6apm_dai_rtd *prtd = runtime->private_data;
2089b4fe0f1SSrinivas Kandagatla 	int ret = 0;
2099b4fe0f1SSrinivas Kandagatla 
2109b4fe0f1SSrinivas Kandagatla 	switch (cmd) {
2119b4fe0f1SSrinivas Kandagatla 	case SNDRV_PCM_TRIGGER_START:
2129b4fe0f1SSrinivas Kandagatla 	case SNDRV_PCM_TRIGGER_RESUME:
2139b4fe0f1SSrinivas Kandagatla 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
2149b4fe0f1SSrinivas Kandagatla 		 /* start writing buffers for playback only as we already queued capture buffers */
2159b4fe0f1SSrinivas Kandagatla 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
2169b4fe0f1SSrinivas Kandagatla 			ret = q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, 0);
2179b4fe0f1SSrinivas Kandagatla 		break;
2189b4fe0f1SSrinivas Kandagatla 	case SNDRV_PCM_TRIGGER_STOP:
2199b4fe0f1SSrinivas Kandagatla 		/* TODO support be handled via SoftPause Module */
2209b4fe0f1SSrinivas Kandagatla 		prtd->state = Q6APM_STREAM_STOPPED;
2219b4fe0f1SSrinivas Kandagatla 		break;
2229b4fe0f1SSrinivas Kandagatla 	case SNDRV_PCM_TRIGGER_SUSPEND:
2239b4fe0f1SSrinivas Kandagatla 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
2249b4fe0f1SSrinivas Kandagatla 		break;
2259b4fe0f1SSrinivas Kandagatla 	default:
2269b4fe0f1SSrinivas Kandagatla 		ret = -EINVAL;
2279b4fe0f1SSrinivas Kandagatla 		break;
2289b4fe0f1SSrinivas Kandagatla 	}
2299b4fe0f1SSrinivas Kandagatla 
2309b4fe0f1SSrinivas Kandagatla 	return ret;
2319b4fe0f1SSrinivas Kandagatla }
2329b4fe0f1SSrinivas Kandagatla 
2339b4fe0f1SSrinivas Kandagatla static int q6apm_dai_open(struct snd_soc_component *component,
2349b4fe0f1SSrinivas Kandagatla 			  struct snd_pcm_substream *substream)
2359b4fe0f1SSrinivas Kandagatla {
2369b4fe0f1SSrinivas Kandagatla 	struct snd_pcm_runtime *runtime = substream->runtime;
2379b4fe0f1SSrinivas Kandagatla 	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
2389b4fe0f1SSrinivas Kandagatla 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_prtd, 0);
2399b4fe0f1SSrinivas Kandagatla 	struct device *dev = component->dev;
2409b4fe0f1SSrinivas Kandagatla 	struct q6apm_dai_data *pdata;
2419b4fe0f1SSrinivas Kandagatla 	struct q6apm_dai_rtd *prtd;
2429b4fe0f1SSrinivas Kandagatla 	int graph_id, ret;
2439b4fe0f1SSrinivas Kandagatla 
2449b4fe0f1SSrinivas Kandagatla 	graph_id = cpu_dai->driver->id;
2459b4fe0f1SSrinivas Kandagatla 
2469b4fe0f1SSrinivas Kandagatla 	pdata = snd_soc_component_get_drvdata(component);
2479b4fe0f1SSrinivas Kandagatla 	if (!pdata) {
2489b4fe0f1SSrinivas Kandagatla 		dev_err(dev, "Drv data not found ..\n");
2499b4fe0f1SSrinivas Kandagatla 		return -EINVAL;
2509b4fe0f1SSrinivas Kandagatla 	}
2519b4fe0f1SSrinivas Kandagatla 
2529b4fe0f1SSrinivas Kandagatla 	prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
2539b4fe0f1SSrinivas Kandagatla 	if (prtd == NULL)
2549b4fe0f1SSrinivas Kandagatla 		return -ENOMEM;
2559b4fe0f1SSrinivas Kandagatla 
2569b4fe0f1SSrinivas Kandagatla 	prtd->substream = substream;
2579b4fe0f1SSrinivas Kandagatla 	prtd->graph = q6apm_graph_open(dev, (q6apm_cb)event_handler, prtd, graph_id);
2589b4fe0f1SSrinivas Kandagatla 	if (IS_ERR(prtd->graph)) {
2599b4fe0f1SSrinivas Kandagatla 		dev_err(dev, "%s: Could not allocate memory\n", __func__);
2609b4fe0f1SSrinivas Kandagatla 		ret = PTR_ERR(prtd->graph);
2619b4fe0f1SSrinivas Kandagatla 		goto err;
2629b4fe0f1SSrinivas Kandagatla 	}
2639b4fe0f1SSrinivas Kandagatla 
2649b4fe0f1SSrinivas Kandagatla 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
2659b4fe0f1SSrinivas Kandagatla 		runtime->hw = q6apm_dai_hardware_playback;
2669b4fe0f1SSrinivas Kandagatla 	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
2679b4fe0f1SSrinivas Kandagatla 		runtime->hw = q6apm_dai_hardware_capture;
2689b4fe0f1SSrinivas Kandagatla 
2699b4fe0f1SSrinivas Kandagatla 	/* Ensure that buffer size is a multiple of period size */
2709b4fe0f1SSrinivas Kandagatla 	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
2719b4fe0f1SSrinivas Kandagatla 	if (ret < 0) {
2729b4fe0f1SSrinivas Kandagatla 		dev_err(dev, "snd_pcm_hw_constraint_integer failed\n");
2739b4fe0f1SSrinivas Kandagatla 		goto err;
2749b4fe0f1SSrinivas Kandagatla 	}
2759b4fe0f1SSrinivas Kandagatla 
2769b4fe0f1SSrinivas Kandagatla 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
2779b4fe0f1SSrinivas Kandagatla 		ret = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
2789b4fe0f1SSrinivas Kandagatla 						   BUFFER_BYTES_MIN, BUFFER_BYTES_MAX);
2799b4fe0f1SSrinivas Kandagatla 		if (ret < 0) {
2809b4fe0f1SSrinivas Kandagatla 			dev_err(dev, "constraint for buffer bytes min max ret = %d\n", ret);
2819b4fe0f1SSrinivas Kandagatla 			goto err;
2829b4fe0f1SSrinivas Kandagatla 		}
2839b4fe0f1SSrinivas Kandagatla 	}
2849b4fe0f1SSrinivas Kandagatla 
2859b4fe0f1SSrinivas Kandagatla 	ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
2869b4fe0f1SSrinivas Kandagatla 	if (ret < 0) {
2879b4fe0f1SSrinivas Kandagatla 		dev_err(dev, "constraint for period bytes step ret = %d\n", ret);
2889b4fe0f1SSrinivas Kandagatla 		goto err;
2899b4fe0f1SSrinivas Kandagatla 	}
2909b4fe0f1SSrinivas Kandagatla 
2919b4fe0f1SSrinivas Kandagatla 	ret = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
2929b4fe0f1SSrinivas Kandagatla 	if (ret < 0) {
2939b4fe0f1SSrinivas Kandagatla 		dev_err(dev, "constraint for buffer bytes step ret = %d\n", ret);
2949b4fe0f1SSrinivas Kandagatla 		goto err;
2959b4fe0f1SSrinivas Kandagatla 	}
2969b4fe0f1SSrinivas Kandagatla 
2979b4fe0f1SSrinivas Kandagatla 	runtime->private_data = prtd;
2989b4fe0f1SSrinivas Kandagatla 	runtime->dma_bytes = BUFFER_BYTES_MAX;
2999b4fe0f1SSrinivas Kandagatla 	if (pdata->sid < 0)
3009b4fe0f1SSrinivas Kandagatla 		prtd->phys = substream->dma_buffer.addr;
3019b4fe0f1SSrinivas Kandagatla 	else
3029b4fe0f1SSrinivas Kandagatla 		prtd->phys = substream->dma_buffer.addr | (pdata->sid << 32);
3039b4fe0f1SSrinivas Kandagatla 
3049b4fe0f1SSrinivas Kandagatla 	return 0;
3059b4fe0f1SSrinivas Kandagatla err:
3069b4fe0f1SSrinivas Kandagatla 	kfree(prtd);
3079b4fe0f1SSrinivas Kandagatla 
3089b4fe0f1SSrinivas Kandagatla 	return ret;
3099b4fe0f1SSrinivas Kandagatla }
3109b4fe0f1SSrinivas Kandagatla 
3119b4fe0f1SSrinivas Kandagatla static int q6apm_dai_close(struct snd_soc_component *component,
3129b4fe0f1SSrinivas Kandagatla 			   struct snd_pcm_substream *substream)
3139b4fe0f1SSrinivas Kandagatla {
3149b4fe0f1SSrinivas Kandagatla 	struct snd_pcm_runtime *runtime = substream->runtime;
3159b4fe0f1SSrinivas Kandagatla 	struct q6apm_dai_rtd *prtd = runtime->private_data;
3169b4fe0f1SSrinivas Kandagatla 
3178f2e5c65SSrinivas Kandagatla 	if (prtd->state) { /* only stop graph that is started */
3189b4fe0f1SSrinivas Kandagatla 		q6apm_graph_stop(prtd->graph);
3199b4fe0f1SSrinivas Kandagatla 		q6apm_unmap_memory_regions(prtd->graph, substream->stream);
3208f2e5c65SSrinivas Kandagatla 	}
3218f2e5c65SSrinivas Kandagatla 
3229b4fe0f1SSrinivas Kandagatla 	q6apm_graph_close(prtd->graph);
3239b4fe0f1SSrinivas Kandagatla 	prtd->graph = NULL;
3249b4fe0f1SSrinivas Kandagatla 	kfree(prtd);
3259b4fe0f1SSrinivas Kandagatla 	runtime->private_data = NULL;
3269b4fe0f1SSrinivas Kandagatla 
3279b4fe0f1SSrinivas Kandagatla 	return 0;
3289b4fe0f1SSrinivas Kandagatla }
3299b4fe0f1SSrinivas Kandagatla 
3309b4fe0f1SSrinivas Kandagatla static snd_pcm_uframes_t q6apm_dai_pointer(struct snd_soc_component *component,
3319b4fe0f1SSrinivas Kandagatla 					   struct snd_pcm_substream *substream)
3329b4fe0f1SSrinivas Kandagatla {
3339b4fe0f1SSrinivas Kandagatla 	struct snd_pcm_runtime *runtime = substream->runtime;
3349b4fe0f1SSrinivas Kandagatla 	struct q6apm_dai_rtd *prtd = runtime->private_data;
3359b4fe0f1SSrinivas Kandagatla 
3369b4fe0f1SSrinivas Kandagatla 	if (prtd->pos == prtd->pcm_size)
3379b4fe0f1SSrinivas Kandagatla 		prtd->pos = 0;
3389b4fe0f1SSrinivas Kandagatla 
3399b4fe0f1SSrinivas Kandagatla 	return bytes_to_frames(runtime, prtd->pos);
3409b4fe0f1SSrinivas Kandagatla }
3419b4fe0f1SSrinivas Kandagatla 
3429b4fe0f1SSrinivas Kandagatla static int q6apm_dai_hw_params(struct snd_soc_component *component,
3439b4fe0f1SSrinivas Kandagatla 			       struct snd_pcm_substream *substream,
3449b4fe0f1SSrinivas Kandagatla 			       struct snd_pcm_hw_params *params)
3459b4fe0f1SSrinivas Kandagatla {
3469b4fe0f1SSrinivas Kandagatla 	struct snd_pcm_runtime *runtime = substream->runtime;
3479b4fe0f1SSrinivas Kandagatla 	struct q6apm_dai_rtd *prtd = runtime->private_data;
3489b4fe0f1SSrinivas Kandagatla 
3499b4fe0f1SSrinivas Kandagatla 	prtd->pcm_size = params_buffer_bytes(params);
3509b4fe0f1SSrinivas Kandagatla 	prtd->periods = params_periods(params);
3519b4fe0f1SSrinivas Kandagatla 
3529b4fe0f1SSrinivas Kandagatla 	switch (params_format(params)) {
3539b4fe0f1SSrinivas Kandagatla 	case SNDRV_PCM_FORMAT_S16_LE:
3549b4fe0f1SSrinivas Kandagatla 		prtd->bits_per_sample = 16;
3559b4fe0f1SSrinivas Kandagatla 		break;
3569b4fe0f1SSrinivas Kandagatla 	case SNDRV_PCM_FORMAT_S24_LE:
3579b4fe0f1SSrinivas Kandagatla 		prtd->bits_per_sample = 24;
3589b4fe0f1SSrinivas Kandagatla 		break;
3599b4fe0f1SSrinivas Kandagatla 	default:
3609b4fe0f1SSrinivas Kandagatla 		return -EINVAL;
3619b4fe0f1SSrinivas Kandagatla 	}
3629b4fe0f1SSrinivas Kandagatla 
3639b4fe0f1SSrinivas Kandagatla 	return 0;
3649b4fe0f1SSrinivas Kandagatla }
3659b4fe0f1SSrinivas Kandagatla 
3669b4fe0f1SSrinivas Kandagatla static int q6apm_dai_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd)
3679b4fe0f1SSrinivas Kandagatla {
3689b4fe0f1SSrinivas Kandagatla 	int size = BUFFER_BYTES_MAX;
3699b4fe0f1SSrinivas Kandagatla 
3709b4fe0f1SSrinivas Kandagatla 	return snd_pcm_set_fixed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, component->dev, size);
3719b4fe0f1SSrinivas Kandagatla }
3729b4fe0f1SSrinivas Kandagatla 
3739b4fe0f1SSrinivas Kandagatla static const struct snd_soc_component_driver q6apm_fe_dai_component = {
3749b4fe0f1SSrinivas Kandagatla 	.name		= DRV_NAME,
3759b4fe0f1SSrinivas Kandagatla 	.open		= q6apm_dai_open,
3769b4fe0f1SSrinivas Kandagatla 	.close		= q6apm_dai_close,
3779b4fe0f1SSrinivas Kandagatla 	.prepare	= q6apm_dai_prepare,
3789b4fe0f1SSrinivas Kandagatla 	.pcm_construct	= q6apm_dai_pcm_new,
3799b4fe0f1SSrinivas Kandagatla 	.hw_params	= q6apm_dai_hw_params,
3809b4fe0f1SSrinivas Kandagatla 	.pointer	= q6apm_dai_pointer,
3819b4fe0f1SSrinivas Kandagatla 	.trigger	= q6apm_dai_trigger,
3829b4fe0f1SSrinivas Kandagatla };
3839b4fe0f1SSrinivas Kandagatla 
3849b4fe0f1SSrinivas Kandagatla static int q6apm_dai_probe(struct platform_device *pdev)
3859b4fe0f1SSrinivas Kandagatla {
3869b4fe0f1SSrinivas Kandagatla 	struct device *dev = &pdev->dev;
3879b4fe0f1SSrinivas Kandagatla 	struct device_node *node = dev->of_node;
3889b4fe0f1SSrinivas Kandagatla 	struct q6apm_dai_data *pdata;
3899b4fe0f1SSrinivas Kandagatla 	struct of_phandle_args args;
3909b4fe0f1SSrinivas Kandagatla 	int rc;
3919b4fe0f1SSrinivas Kandagatla 
3929b4fe0f1SSrinivas Kandagatla 	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
3939b4fe0f1SSrinivas Kandagatla 	if (!pdata)
3949b4fe0f1SSrinivas Kandagatla 		return -ENOMEM;
3959b4fe0f1SSrinivas Kandagatla 
3969b4fe0f1SSrinivas Kandagatla 	rc = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args);
3979b4fe0f1SSrinivas Kandagatla 	if (rc < 0)
3989b4fe0f1SSrinivas Kandagatla 		pdata->sid = -1;
3999b4fe0f1SSrinivas Kandagatla 	else
4009b4fe0f1SSrinivas Kandagatla 		pdata->sid = args.args[0] & SID_MASK_DEFAULT;
4019b4fe0f1SSrinivas Kandagatla 
4029b4fe0f1SSrinivas Kandagatla 	dev_set_drvdata(dev, pdata);
4039b4fe0f1SSrinivas Kandagatla 
4049b4fe0f1SSrinivas Kandagatla 	return devm_snd_soc_register_component(dev, &q6apm_fe_dai_component, NULL, 0);
4059b4fe0f1SSrinivas Kandagatla }
4069b4fe0f1SSrinivas Kandagatla 
4079b4fe0f1SSrinivas Kandagatla #ifdef CONFIG_OF
4089b4fe0f1SSrinivas Kandagatla static const struct of_device_id q6apm_dai_device_id[] = {
4099b4fe0f1SSrinivas Kandagatla 	{ .compatible = "qcom,q6apm-dais" },
4109b4fe0f1SSrinivas Kandagatla 	{},
4119b4fe0f1SSrinivas Kandagatla };
4129b4fe0f1SSrinivas Kandagatla MODULE_DEVICE_TABLE(of, q6apm_dai_device_id);
4139b4fe0f1SSrinivas Kandagatla #endif
4149b4fe0f1SSrinivas Kandagatla 
4159b4fe0f1SSrinivas Kandagatla static struct platform_driver q6apm_dai_platform_driver = {
4169b4fe0f1SSrinivas Kandagatla 	.driver = {
4179b4fe0f1SSrinivas Kandagatla 		.name = "q6apm-dai",
4189b4fe0f1SSrinivas Kandagatla 		.of_match_table = of_match_ptr(q6apm_dai_device_id),
4199b4fe0f1SSrinivas Kandagatla 	},
4209b4fe0f1SSrinivas Kandagatla 	.probe = q6apm_dai_probe,
4219b4fe0f1SSrinivas Kandagatla };
4229b4fe0f1SSrinivas Kandagatla module_platform_driver(q6apm_dai_platform_driver);
4239b4fe0f1SSrinivas Kandagatla 
4249b4fe0f1SSrinivas Kandagatla MODULE_DESCRIPTION("Q6APM dai driver");
4259b4fe0f1SSrinivas Kandagatla MODULE_LICENSE("GPL");
426