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
fifo_spdif_dcu_enable(struct snd_soc_component * component,bool enable)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
fifo_spdif_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * dai)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
fifo_spdif_prepare(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)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
fifo_spdif_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)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
aiu_fifo_spdif_dai_probe(struct snd_soc_dai * dai)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