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 306ae9ca9cSJerome Brunet static 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 = { 1586ae9ca9cSJerome Brunet .trigger = fifo_spdif_trigger, 1596ae9ca9cSJerome Brunet .prepare = fifo_spdif_prepare, 1606ae9ca9cSJerome Brunet .hw_params = fifo_spdif_hw_params, 1616ae9ca9cSJerome Brunet .startup = aiu_fifo_startup, 1626ae9ca9cSJerome Brunet .shutdown = aiu_fifo_shutdown, 1636ae9ca9cSJerome Brunet }; 1646ae9ca9cSJerome Brunet 1656ae9ca9cSJerome Brunet int aiu_fifo_spdif_dai_probe(struct snd_soc_dai *dai) 1666ae9ca9cSJerome Brunet { 1676ae9ca9cSJerome Brunet struct snd_soc_component *component = dai->component; 1686ae9ca9cSJerome Brunet struct aiu *aiu = snd_soc_component_get_drvdata(component); 1696ae9ca9cSJerome Brunet struct aiu_fifo *fifo; 1706ae9ca9cSJerome Brunet int ret; 1716ae9ca9cSJerome Brunet 1726ae9ca9cSJerome Brunet ret = aiu_fifo_dai_probe(dai); 1736ae9ca9cSJerome Brunet if (ret) 1746ae9ca9cSJerome Brunet return ret; 1756ae9ca9cSJerome Brunet 176*c765cedaSKuninori Morimoto fifo = snd_soc_dai_dma_data_get_playback(dai); 1776ae9ca9cSJerome Brunet 1786ae9ca9cSJerome Brunet fifo->pcm = &fifo_spdif_pcm; 1796ae9ca9cSJerome Brunet fifo->mem_offset = AIU_MEM_IEC958_START; 1806ae9ca9cSJerome Brunet fifo->fifo_block = 1; 1816ae9ca9cSJerome Brunet fifo->pclk = aiu->spdif.clks[PCLK].clk; 1826ae9ca9cSJerome Brunet fifo->irq = aiu->spdif.irq; 1836ae9ca9cSJerome Brunet 1846ae9ca9cSJerome Brunet return 0; 1856ae9ca9cSJerome Brunet } 186