xref: /openbmc/linux/sound/soc/meson/aiu-fifo-i2s.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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/bitfield.h>
76ae9ca9cSJerome Brunet #include <linux/clk.h>
86ae9ca9cSJerome Brunet #include <sound/pcm_params.h>
96ae9ca9cSJerome Brunet #include <sound/soc.h>
106ae9ca9cSJerome Brunet #include <sound/soc-dai.h>
116ae9ca9cSJerome Brunet 
126ae9ca9cSJerome Brunet #include "aiu.h"
136ae9ca9cSJerome Brunet #include "aiu-fifo.h"
146ae9ca9cSJerome Brunet 
156ae9ca9cSJerome Brunet #define AIU_I2S_SOURCE_DESC_MODE_8CH	BIT(0)
166ae9ca9cSJerome Brunet #define AIU_I2S_SOURCE_DESC_MODE_24BIT	BIT(5)
176ae9ca9cSJerome Brunet #define AIU_I2S_SOURCE_DESC_MODE_32BIT	BIT(9)
186ae9ca9cSJerome Brunet #define AIU_I2S_SOURCE_DESC_MODE_SPLIT	BIT(11)
196ae9ca9cSJerome Brunet #define AIU_MEM_I2S_MASKS_IRQ_BLOCK	GENMASK(31, 16)
206ae9ca9cSJerome Brunet #define AIU_MEM_I2S_CONTROL_MODE_16BIT	BIT(6)
216ae9ca9cSJerome Brunet #define AIU_MEM_I2S_BUF_CNTL_INIT	BIT(0)
226ae9ca9cSJerome Brunet #define AIU_RST_SOFT_I2S_FAST		BIT(0)
23ee907afbSMartin Blumenstingl #define AIU_I2S_MISC_HOLD_EN		BIT(2)
24ee907afbSMartin Blumenstingl #define AIU_I2S_MISC_FORCE_LEFT_RIGHT	BIT(4)
256ae9ca9cSJerome Brunet 
266ae9ca9cSJerome Brunet #define AIU_FIFO_I2S_BLOCK		256
276ae9ca9cSJerome Brunet 
286ae9ca9cSJerome Brunet static struct snd_pcm_hardware fifo_i2s_pcm = {
296ae9ca9cSJerome Brunet 	.info = (SNDRV_PCM_INFO_INTERLEAVED |
306ae9ca9cSJerome Brunet 		 SNDRV_PCM_INFO_MMAP |
316ae9ca9cSJerome Brunet 		 SNDRV_PCM_INFO_MMAP_VALID |
326ae9ca9cSJerome Brunet 		 SNDRV_PCM_INFO_PAUSE),
336ae9ca9cSJerome Brunet 	.formats = AIU_FORMATS,
346ae9ca9cSJerome Brunet 	.rate_min = 5512,
356ae9ca9cSJerome Brunet 	.rate_max = 192000,
366ae9ca9cSJerome Brunet 	.channels_min = 2,
376ae9ca9cSJerome Brunet 	.channels_max = 8,
386ae9ca9cSJerome Brunet 	.period_bytes_min = AIU_FIFO_I2S_BLOCK,
396ae9ca9cSJerome Brunet 	.period_bytes_max = AIU_FIFO_I2S_BLOCK * USHRT_MAX,
406ae9ca9cSJerome Brunet 	.periods_min = 2,
416ae9ca9cSJerome Brunet 	.periods_max = UINT_MAX,
426ae9ca9cSJerome Brunet 
436ae9ca9cSJerome Brunet 	/* No real justification for this */
446ae9ca9cSJerome Brunet 	.buffer_bytes_max = 1 * 1024 * 1024,
456ae9ca9cSJerome Brunet };
466ae9ca9cSJerome Brunet 
aiu_fifo_i2s_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)476ae9ca9cSJerome Brunet static int aiu_fifo_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
486ae9ca9cSJerome Brunet 				struct snd_soc_dai *dai)
496ae9ca9cSJerome Brunet {
506ae9ca9cSJerome Brunet 	struct snd_soc_component *component = dai->component;
516ae9ca9cSJerome Brunet 
526ae9ca9cSJerome Brunet 	switch (cmd) {
536ae9ca9cSJerome Brunet 	case SNDRV_PCM_TRIGGER_START:
546ae9ca9cSJerome Brunet 	case SNDRV_PCM_TRIGGER_RESUME:
556ae9ca9cSJerome Brunet 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
566ae9ca9cSJerome Brunet 		snd_soc_component_write(component, AIU_RST_SOFT,
576ae9ca9cSJerome Brunet 					AIU_RST_SOFT_I2S_FAST);
58cf6e26c7SKuninori Morimoto 		snd_soc_component_read(component, AIU_I2S_SYNC);
596ae9ca9cSJerome Brunet 		break;
606ae9ca9cSJerome Brunet 	}
616ae9ca9cSJerome Brunet 
626ae9ca9cSJerome Brunet 	return aiu_fifo_trigger(substream, cmd, dai);
636ae9ca9cSJerome Brunet }
646ae9ca9cSJerome Brunet 
aiu_fifo_i2s_prepare(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)656ae9ca9cSJerome Brunet static int aiu_fifo_i2s_prepare(struct snd_pcm_substream *substream,
666ae9ca9cSJerome Brunet 				struct snd_soc_dai *dai)
676ae9ca9cSJerome Brunet {
686ae9ca9cSJerome Brunet 	struct snd_soc_component *component = dai->component;
696ae9ca9cSJerome Brunet 	int ret;
706ae9ca9cSJerome Brunet 
716ae9ca9cSJerome Brunet 	ret = aiu_fifo_prepare(substream, dai);
726ae9ca9cSJerome Brunet 	if (ret)
736ae9ca9cSJerome Brunet 		return ret;
746ae9ca9cSJerome Brunet 
756ae9ca9cSJerome Brunet 	snd_soc_component_update_bits(component,
766ae9ca9cSJerome Brunet 				      AIU_MEM_I2S_BUF_CNTL,
776ae9ca9cSJerome Brunet 				      AIU_MEM_I2S_BUF_CNTL_INIT,
786ae9ca9cSJerome Brunet 				      AIU_MEM_I2S_BUF_CNTL_INIT);
796ae9ca9cSJerome Brunet 	snd_soc_component_update_bits(component,
806ae9ca9cSJerome Brunet 				      AIU_MEM_I2S_BUF_CNTL,
816ae9ca9cSJerome Brunet 				      AIU_MEM_I2S_BUF_CNTL_INIT, 0);
826ae9ca9cSJerome Brunet 
836ae9ca9cSJerome Brunet 	return 0;
846ae9ca9cSJerome Brunet }
856ae9ca9cSJerome Brunet 
aiu_fifo_i2s_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)866ae9ca9cSJerome Brunet static int aiu_fifo_i2s_hw_params(struct snd_pcm_substream *substream,
876ae9ca9cSJerome Brunet 				  struct snd_pcm_hw_params *params,
886ae9ca9cSJerome Brunet 				  struct snd_soc_dai *dai)
896ae9ca9cSJerome Brunet {
906ae9ca9cSJerome Brunet 	struct snd_soc_component *component = dai->component;
91c765cedaSKuninori Morimoto 	struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
926ae9ca9cSJerome Brunet 	unsigned int val;
936ae9ca9cSJerome Brunet 	int ret;
946ae9ca9cSJerome Brunet 
95ee907afbSMartin Blumenstingl 	snd_soc_component_update_bits(component, AIU_I2S_MISC,
96ee907afbSMartin Blumenstingl 				      AIU_I2S_MISC_HOLD_EN,
97ee907afbSMartin Blumenstingl 				      AIU_I2S_MISC_HOLD_EN);
98ee907afbSMartin Blumenstingl 
996ae9ca9cSJerome Brunet 	ret = aiu_fifo_hw_params(substream, params, dai);
1006ae9ca9cSJerome Brunet 	if (ret)
1016ae9ca9cSJerome Brunet 		return ret;
1026ae9ca9cSJerome Brunet 
1036ae9ca9cSJerome Brunet 	switch (params_physical_width(params)) {
1046ae9ca9cSJerome Brunet 	case 16:
1056ae9ca9cSJerome Brunet 		val = AIU_MEM_I2S_CONTROL_MODE_16BIT;
1066ae9ca9cSJerome Brunet 		break;
1076ae9ca9cSJerome Brunet 	case 32:
1086ae9ca9cSJerome Brunet 		val = 0;
1096ae9ca9cSJerome Brunet 		break;
1106ae9ca9cSJerome Brunet 	default:
1116ae9ca9cSJerome Brunet 		dev_err(dai->dev, "Unsupported physical width %u\n",
1126ae9ca9cSJerome Brunet 			params_physical_width(params));
1136ae9ca9cSJerome Brunet 		return -EINVAL;
1146ae9ca9cSJerome Brunet 	}
1156ae9ca9cSJerome Brunet 
1166ae9ca9cSJerome Brunet 	snd_soc_component_update_bits(component, AIU_MEM_I2S_CONTROL,
1176ae9ca9cSJerome Brunet 				      AIU_MEM_I2S_CONTROL_MODE_16BIT,
1186ae9ca9cSJerome Brunet 				      val);
1196ae9ca9cSJerome Brunet 
1206ae9ca9cSJerome Brunet 	/* Setup the irq periodicity */
1216ae9ca9cSJerome Brunet 	val = params_period_bytes(params) / fifo->fifo_block;
1226ae9ca9cSJerome Brunet 	val = FIELD_PREP(AIU_MEM_I2S_MASKS_IRQ_BLOCK, val);
1236ae9ca9cSJerome Brunet 	snd_soc_component_update_bits(component, AIU_MEM_I2S_MASKS,
1246ae9ca9cSJerome Brunet 				      AIU_MEM_I2S_MASKS_IRQ_BLOCK, val);
1256ae9ca9cSJerome Brunet 
126ee907afbSMartin Blumenstingl 	/*
127ee907afbSMartin Blumenstingl 	 * Most (all?) supported SoCs have this bit set by default. The vendor
128ee907afbSMartin Blumenstingl 	 * driver however sets it manually (depending on the version either
129ee907afbSMartin Blumenstingl 	 * while un-setting AIU_I2S_MISC_HOLD_EN or right before that). Follow
130ee907afbSMartin Blumenstingl 	 * the same approach for consistency with the vendor driver.
131ee907afbSMartin Blumenstingl 	 */
132ee907afbSMartin Blumenstingl 	snd_soc_component_update_bits(component, AIU_I2S_MISC,
133ee907afbSMartin Blumenstingl 				      AIU_I2S_MISC_FORCE_LEFT_RIGHT,
134ee907afbSMartin Blumenstingl 				      AIU_I2S_MISC_FORCE_LEFT_RIGHT);
135ee907afbSMartin Blumenstingl 
136ee907afbSMartin Blumenstingl 	snd_soc_component_update_bits(component, AIU_I2S_MISC,
137ee907afbSMartin Blumenstingl 				      AIU_I2S_MISC_HOLD_EN, 0);
138ee907afbSMartin Blumenstingl 
1396ae9ca9cSJerome Brunet 	return 0;
1406ae9ca9cSJerome Brunet }
1416ae9ca9cSJerome Brunet 
1426ae9ca9cSJerome Brunet const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops = {
143*2d3155a9SKuninori Morimoto 	.pcm_new	= aiu_fifo_pcm_new,
144*2d3155a9SKuninori Morimoto 	.probe		= aiu_fifo_i2s_dai_probe,
145*2d3155a9SKuninori Morimoto 	.remove		= aiu_fifo_dai_remove,
1466ae9ca9cSJerome Brunet 	.trigger	= aiu_fifo_i2s_trigger,
1476ae9ca9cSJerome Brunet 	.prepare	= aiu_fifo_i2s_prepare,
1486ae9ca9cSJerome Brunet 	.hw_params	= aiu_fifo_i2s_hw_params,
1496ae9ca9cSJerome Brunet 	.startup	= aiu_fifo_startup,
1506ae9ca9cSJerome Brunet 	.shutdown	= aiu_fifo_shutdown,
1516ae9ca9cSJerome Brunet };
1526ae9ca9cSJerome Brunet 
aiu_fifo_i2s_dai_probe(struct snd_soc_dai * dai)1536ae9ca9cSJerome Brunet int aiu_fifo_i2s_dai_probe(struct snd_soc_dai *dai)
1546ae9ca9cSJerome Brunet {
1556ae9ca9cSJerome Brunet 	struct snd_soc_component *component = dai->component;
1566ae9ca9cSJerome Brunet 	struct aiu *aiu = snd_soc_component_get_drvdata(component);
1576ae9ca9cSJerome Brunet 	struct aiu_fifo *fifo;
1586ae9ca9cSJerome Brunet 	int ret;
1596ae9ca9cSJerome Brunet 
1606ae9ca9cSJerome Brunet 	ret = aiu_fifo_dai_probe(dai);
1616ae9ca9cSJerome Brunet 	if (ret)
1626ae9ca9cSJerome Brunet 		return ret;
1636ae9ca9cSJerome Brunet 
164c765cedaSKuninori Morimoto 	fifo = snd_soc_dai_dma_data_get_playback(dai);
1656ae9ca9cSJerome Brunet 
1666ae9ca9cSJerome Brunet 	fifo->pcm = &fifo_i2s_pcm;
1676ae9ca9cSJerome Brunet 	fifo->mem_offset = AIU_MEM_I2S_START;
1686ae9ca9cSJerome Brunet 	fifo->fifo_block = AIU_FIFO_I2S_BLOCK;
1696ae9ca9cSJerome Brunet 	fifo->pclk = aiu->i2s.clks[PCLK].clk;
1706ae9ca9cSJerome Brunet 	fifo->irq = aiu->i2s.irq;
1716ae9ca9cSJerome Brunet 
1726ae9ca9cSJerome Brunet 	return 0;
1736ae9ca9cSJerome Brunet }
174