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