xref: /linux/sound/soc/meson/aiu-fifo-spdif.c (revision 7b5ce9f0c52a5885d34d46bba62e9eaedc3dd459)
16ae9ca9cSJerome Brunet // SPDX-License-Identifier: GPL-2.0
26ae9ca9cSJerome Brunet //
36ae9ca9cSJerome Brunet // Copyright (c) 2020 BayLibre, SAS.
46ae9ca9cSJerome Brunet // Author: Jerome Brunet <jbrunet@baylibre.com>
56ae9ca9cSJerome Brunet 
66ae9ca9cSJerome Brunet #include <linux/clk.h>
76ae9ca9cSJerome Brunet #include <sound/pcm_params.h>
86ae9ca9cSJerome Brunet #include <sound/soc.h>
96ae9ca9cSJerome Brunet #include <sound/soc-dai.h>
106ae9ca9cSJerome Brunet 
116ae9ca9cSJerome Brunet #include "aiu.h"
126ae9ca9cSJerome Brunet #include "aiu-fifo.h"
136ae9ca9cSJerome Brunet 
146ae9ca9cSJerome Brunet #define AIU_IEC958_DCU_FF_CTRL_EN		BIT(0)
156ae9ca9cSJerome Brunet #define AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE	BIT(1)
166ae9ca9cSJerome Brunet #define AIU_IEC958_DCU_FF_CTRL_IRQ_MODE		GENMASK(3, 2)
176ae9ca9cSJerome Brunet #define AIU_IEC958_DCU_FF_CTRL_IRQ_OUT_THD	BIT(2)
186ae9ca9cSJerome Brunet #define AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ	BIT(3)
196ae9ca9cSJerome Brunet #define AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN	BIT(4)
206ae9ca9cSJerome Brunet #define AIU_IEC958_DCU_FF_CTRL_BYTE_SEEK	BIT(5)
216ae9ca9cSJerome Brunet #define AIU_IEC958_DCU_FF_CTRL_CONTINUE		BIT(6)
226ae9ca9cSJerome Brunet #define AIU_MEM_IEC958_CONTROL_ENDIAN		GENMASK(5, 3)
236ae9ca9cSJerome Brunet #define AIU_MEM_IEC958_CONTROL_RD_DDR		BIT(6)
246ae9ca9cSJerome Brunet #define AIU_MEM_IEC958_CONTROL_MODE_16BIT	BIT(7)
256ae9ca9cSJerome Brunet #define AIU_MEM_IEC958_CONTROL_MODE_LINEAR	BIT(8)
266ae9ca9cSJerome Brunet #define AIU_MEM_IEC958_BUF_CNTL_INIT		BIT(0)
276ae9ca9cSJerome Brunet 
286ae9ca9cSJerome Brunet #define AIU_FIFO_SPDIF_BLOCK			8
296ae9ca9cSJerome Brunet 
30*7b5ce9f0SKrzysztof Kozlowski static const struct snd_pcm_hardware fifo_spdif_pcm = {
316ae9ca9cSJerome Brunet 	.info = (SNDRV_PCM_INFO_INTERLEAVED |
326ae9ca9cSJerome Brunet 		 SNDRV_PCM_INFO_MMAP |
336ae9ca9cSJerome Brunet 		 SNDRV_PCM_INFO_MMAP_VALID |
346ae9ca9cSJerome Brunet 		 SNDRV_PCM_INFO_PAUSE),
356ae9ca9cSJerome Brunet 	.formats = AIU_FORMATS,
366ae9ca9cSJerome Brunet 	.rate_min = 5512,
376ae9ca9cSJerome Brunet 	.rate_max = 192000,
386ae9ca9cSJerome Brunet 	.channels_min = 2,
396ae9ca9cSJerome Brunet 	.channels_max = 2,
406ae9ca9cSJerome Brunet 	.period_bytes_min = AIU_FIFO_SPDIF_BLOCK,
416ae9ca9cSJerome Brunet 	.period_bytes_max = AIU_FIFO_SPDIF_BLOCK * USHRT_MAX,
426ae9ca9cSJerome Brunet 	.periods_min = 2,
436ae9ca9cSJerome Brunet 	.periods_max = UINT_MAX,
446ae9ca9cSJerome Brunet 
456ae9ca9cSJerome Brunet 	/* No real justification for this */
466ae9ca9cSJerome Brunet 	.buffer_bytes_max = 1 * 1024 * 1024,
476ae9ca9cSJerome Brunet };
486ae9ca9cSJerome Brunet 
496ae9ca9cSJerome Brunet static void fifo_spdif_dcu_enable(struct snd_soc_component *component,
506ae9ca9cSJerome Brunet 				  bool enable)
516ae9ca9cSJerome Brunet {
526ae9ca9cSJerome Brunet 	snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL,
536ae9ca9cSJerome Brunet 				      AIU_IEC958_DCU_FF_CTRL_EN,
546ae9ca9cSJerome Brunet 				      enable ? AIU_IEC958_DCU_FF_CTRL_EN : 0);
556ae9ca9cSJerome Brunet }
566ae9ca9cSJerome Brunet 
576ae9ca9cSJerome Brunet static int fifo_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
586ae9ca9cSJerome Brunet 			      struct snd_soc_dai *dai)
596ae9ca9cSJerome Brunet {
606ae9ca9cSJerome Brunet 	struct snd_soc_component *component = dai->component;
616ae9ca9cSJerome Brunet 	int ret;
626ae9ca9cSJerome Brunet 
636ae9ca9cSJerome Brunet 	ret = aiu_fifo_trigger(substream, cmd, dai);
646ae9ca9cSJerome Brunet 	if (ret)
656ae9ca9cSJerome Brunet 		return ret;
666ae9ca9cSJerome Brunet 
676ae9ca9cSJerome Brunet 	switch (cmd) {
686ae9ca9cSJerome Brunet 	case SNDRV_PCM_TRIGGER_START:
696ae9ca9cSJerome Brunet 	case SNDRV_PCM_TRIGGER_RESUME:
706ae9ca9cSJerome Brunet 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
716ae9ca9cSJerome Brunet 		fifo_spdif_dcu_enable(component, true);
726ae9ca9cSJerome Brunet 		break;
736ae9ca9cSJerome Brunet 	case SNDRV_PCM_TRIGGER_SUSPEND:
746ae9ca9cSJerome Brunet 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
756ae9ca9cSJerome Brunet 	case SNDRV_PCM_TRIGGER_STOP:
766ae9ca9cSJerome Brunet 		fifo_spdif_dcu_enable(component, false);
776ae9ca9cSJerome Brunet 		break;
786ae9ca9cSJerome Brunet 	default:
796ae9ca9cSJerome Brunet 		return -EINVAL;
806ae9ca9cSJerome Brunet 	}
816ae9ca9cSJerome Brunet 
826ae9ca9cSJerome Brunet 	return 0;
836ae9ca9cSJerome Brunet }
846ae9ca9cSJerome Brunet 
856ae9ca9cSJerome Brunet static int fifo_spdif_prepare(struct snd_pcm_substream *substream,
866ae9ca9cSJerome Brunet 			      struct snd_soc_dai *dai)
876ae9ca9cSJerome Brunet {
886ae9ca9cSJerome Brunet 	struct snd_soc_component *component = dai->component;
896ae9ca9cSJerome Brunet 	int ret;
906ae9ca9cSJerome Brunet 
916ae9ca9cSJerome Brunet 	ret = aiu_fifo_prepare(substream, dai);
926ae9ca9cSJerome Brunet 	if (ret)
936ae9ca9cSJerome Brunet 		return ret;
946ae9ca9cSJerome Brunet 
956ae9ca9cSJerome Brunet 	snd_soc_component_update_bits(component,
966ae9ca9cSJerome Brunet 				      AIU_MEM_IEC958_BUF_CNTL,
976ae9ca9cSJerome Brunet 				      AIU_MEM_IEC958_BUF_CNTL_INIT,
986ae9ca9cSJerome Brunet 				      AIU_MEM_IEC958_BUF_CNTL_INIT);
996ae9ca9cSJerome Brunet 	snd_soc_component_update_bits(component,
1006ae9ca9cSJerome Brunet 				      AIU_MEM_IEC958_BUF_CNTL,
1016ae9ca9cSJerome Brunet 				      AIU_MEM_IEC958_BUF_CNTL_INIT, 0);
1026ae9ca9cSJerome Brunet 
1036ae9ca9cSJerome Brunet 	return 0;
1046ae9ca9cSJerome Brunet }
1056ae9ca9cSJerome Brunet 
1066ae9ca9cSJerome Brunet static int fifo_spdif_hw_params(struct snd_pcm_substream *substream,
1076ae9ca9cSJerome Brunet 				struct snd_pcm_hw_params *params,
1086ae9ca9cSJerome Brunet 				struct snd_soc_dai *dai)
1096ae9ca9cSJerome Brunet {
1106ae9ca9cSJerome Brunet 	struct snd_soc_component *component = dai->component;
1116ae9ca9cSJerome Brunet 	unsigned int val;
1126ae9ca9cSJerome Brunet 	int ret;
1136ae9ca9cSJerome Brunet 
1146ae9ca9cSJerome Brunet 	ret = aiu_fifo_hw_params(substream, params, dai);
1156ae9ca9cSJerome Brunet 	if (ret)
1166ae9ca9cSJerome Brunet 		return ret;
1176ae9ca9cSJerome Brunet 
1186ae9ca9cSJerome Brunet 	val = AIU_MEM_IEC958_CONTROL_RD_DDR |
1196ae9ca9cSJerome Brunet 	      AIU_MEM_IEC958_CONTROL_MODE_LINEAR;
1206ae9ca9cSJerome Brunet 
1216ae9ca9cSJerome Brunet 	switch (params_physical_width(params)) {
1226ae9ca9cSJerome Brunet 	case 16:
1236ae9ca9cSJerome Brunet 		val |= AIU_MEM_IEC958_CONTROL_MODE_16BIT;
1246ae9ca9cSJerome Brunet 		break;
1256ae9ca9cSJerome Brunet 	case 32:
1266ae9ca9cSJerome Brunet 		break;
1276ae9ca9cSJerome Brunet 	default:
1286ae9ca9cSJerome Brunet 		dev_err(dai->dev, "Unsupported physical width %u\n",
1296ae9ca9cSJerome Brunet 			params_physical_width(params));
1306ae9ca9cSJerome Brunet 		return -EINVAL;
1316ae9ca9cSJerome Brunet 	}
1326ae9ca9cSJerome Brunet 
1336ae9ca9cSJerome Brunet 	snd_soc_component_update_bits(component, AIU_MEM_IEC958_CONTROL,
1346ae9ca9cSJerome Brunet 				      AIU_MEM_IEC958_CONTROL_ENDIAN |
1356ae9ca9cSJerome Brunet 				      AIU_MEM_IEC958_CONTROL_RD_DDR |
1366ae9ca9cSJerome Brunet 				      AIU_MEM_IEC958_CONTROL_MODE_LINEAR |
1376ae9ca9cSJerome Brunet 				      AIU_MEM_IEC958_CONTROL_MODE_16BIT,
1386ae9ca9cSJerome Brunet 				      val);
1396ae9ca9cSJerome Brunet 
1406ae9ca9cSJerome Brunet 	/* Number bytes read by the FIFO between each IRQ */
1416ae9ca9cSJerome Brunet 	snd_soc_component_write(component, AIU_IEC958_BPF,
1426ae9ca9cSJerome Brunet 				params_period_bytes(params));
1436ae9ca9cSJerome Brunet 
1446ae9ca9cSJerome Brunet 	/*
1456ae9ca9cSJerome Brunet 	 * AUTO_DISABLE and SYNC_HEAD are enabled by default but
1466ae9ca9cSJerome Brunet 	 * this should be disabled in PCM (uncompressed) mode
1476ae9ca9cSJerome Brunet 	 */
1486ae9ca9cSJerome Brunet 	snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL,
1496ae9ca9cSJerome Brunet 				      AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE |
1506ae9ca9cSJerome Brunet 				      AIU_IEC958_DCU_FF_CTRL_IRQ_MODE |
1516ae9ca9cSJerome Brunet 				      AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN,
1526ae9ca9cSJerome Brunet 				      AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ);
1536ae9ca9cSJerome Brunet 
1546ae9ca9cSJerome Brunet 	return 0;
1556ae9ca9cSJerome Brunet }
1566ae9ca9cSJerome Brunet 
1576ae9ca9cSJerome Brunet const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops = {
1582d3155a9SKuninori Morimoto 	.pcm_new	= aiu_fifo_pcm_new,
1592d3155a9SKuninori Morimoto 	.probe		= aiu_fifo_spdif_dai_probe,
1602d3155a9SKuninori Morimoto 	.remove		= aiu_fifo_dai_remove,
1616ae9ca9cSJerome Brunet 	.trigger	= fifo_spdif_trigger,
1626ae9ca9cSJerome Brunet 	.prepare	= fifo_spdif_prepare,
1636ae9ca9cSJerome Brunet 	.hw_params	= fifo_spdif_hw_params,
1646ae9ca9cSJerome Brunet 	.startup	= aiu_fifo_startup,
1656ae9ca9cSJerome Brunet 	.shutdown	= aiu_fifo_shutdown,
1666ae9ca9cSJerome Brunet };
1676ae9ca9cSJerome Brunet 
1686ae9ca9cSJerome Brunet int aiu_fifo_spdif_dai_probe(struct snd_soc_dai *dai)
1696ae9ca9cSJerome Brunet {
1706ae9ca9cSJerome Brunet 	struct snd_soc_component *component = dai->component;
1716ae9ca9cSJerome Brunet 	struct aiu *aiu = snd_soc_component_get_drvdata(component);
1726ae9ca9cSJerome Brunet 	struct aiu_fifo *fifo;
1736ae9ca9cSJerome Brunet 	int ret;
1746ae9ca9cSJerome Brunet 
1756ae9ca9cSJerome Brunet 	ret = aiu_fifo_dai_probe(dai);
1766ae9ca9cSJerome Brunet 	if (ret)
1776ae9ca9cSJerome Brunet 		return ret;
1786ae9ca9cSJerome Brunet 
179c765cedaSKuninori Morimoto 	fifo = snd_soc_dai_dma_data_get_playback(dai);
1806ae9ca9cSJerome Brunet 
1816ae9ca9cSJerome Brunet 	fifo->pcm = &fifo_spdif_pcm;
1826ae9ca9cSJerome Brunet 	fifo->mem_offset = AIU_MEM_IEC958_START;
1836ae9ca9cSJerome Brunet 	fifo->fifo_block = 1;
1846ae9ca9cSJerome Brunet 	fifo->pclk = aiu->spdif.clks[PCLK].clk;
1856ae9ca9cSJerome Brunet 	fifo->irq = aiu->spdif.irq;
1866ae9ca9cSJerome Brunet 
1876ae9ca9cSJerome Brunet 	return 0;
1886ae9ca9cSJerome Brunet }
189