1*6ae9ca9cSJerome Brunet // SPDX-License-Identifier: GPL-2.0 2*6ae9ca9cSJerome Brunet // 3*6ae9ca9cSJerome Brunet // Copyright (c) 2020 BayLibre, SAS. 4*6ae9ca9cSJerome Brunet // Author: Jerome Brunet <jbrunet@baylibre.com> 5*6ae9ca9cSJerome Brunet 6*6ae9ca9cSJerome Brunet #include <linux/clk.h> 7*6ae9ca9cSJerome Brunet #include <sound/pcm_params.h> 8*6ae9ca9cSJerome Brunet #include <sound/soc.h> 9*6ae9ca9cSJerome Brunet #include <sound/soc-dai.h> 10*6ae9ca9cSJerome Brunet 11*6ae9ca9cSJerome Brunet #include "aiu.h" 12*6ae9ca9cSJerome Brunet #include "aiu-fifo.h" 13*6ae9ca9cSJerome Brunet 14*6ae9ca9cSJerome Brunet #define AIU_IEC958_DCU_FF_CTRL_EN BIT(0) 15*6ae9ca9cSJerome Brunet #define AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE BIT(1) 16*6ae9ca9cSJerome Brunet #define AIU_IEC958_DCU_FF_CTRL_IRQ_MODE GENMASK(3, 2) 17*6ae9ca9cSJerome Brunet #define AIU_IEC958_DCU_FF_CTRL_IRQ_OUT_THD BIT(2) 18*6ae9ca9cSJerome Brunet #define AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ BIT(3) 19*6ae9ca9cSJerome Brunet #define AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN BIT(4) 20*6ae9ca9cSJerome Brunet #define AIU_IEC958_DCU_FF_CTRL_BYTE_SEEK BIT(5) 21*6ae9ca9cSJerome Brunet #define AIU_IEC958_DCU_FF_CTRL_CONTINUE BIT(6) 22*6ae9ca9cSJerome Brunet #define AIU_MEM_IEC958_CONTROL_ENDIAN GENMASK(5, 3) 23*6ae9ca9cSJerome Brunet #define AIU_MEM_IEC958_CONTROL_RD_DDR BIT(6) 24*6ae9ca9cSJerome Brunet #define AIU_MEM_IEC958_CONTROL_MODE_16BIT BIT(7) 25*6ae9ca9cSJerome Brunet #define AIU_MEM_IEC958_CONTROL_MODE_LINEAR BIT(8) 26*6ae9ca9cSJerome Brunet #define AIU_MEM_IEC958_BUF_CNTL_INIT BIT(0) 27*6ae9ca9cSJerome Brunet 28*6ae9ca9cSJerome Brunet #define AIU_FIFO_SPDIF_BLOCK 8 29*6ae9ca9cSJerome Brunet 30*6ae9ca9cSJerome Brunet static struct snd_pcm_hardware fifo_spdif_pcm = { 31*6ae9ca9cSJerome Brunet .info = (SNDRV_PCM_INFO_INTERLEAVED | 32*6ae9ca9cSJerome Brunet SNDRV_PCM_INFO_MMAP | 33*6ae9ca9cSJerome Brunet SNDRV_PCM_INFO_MMAP_VALID | 34*6ae9ca9cSJerome Brunet SNDRV_PCM_INFO_PAUSE), 35*6ae9ca9cSJerome Brunet .formats = AIU_FORMATS, 36*6ae9ca9cSJerome Brunet .rate_min = 5512, 37*6ae9ca9cSJerome Brunet .rate_max = 192000, 38*6ae9ca9cSJerome Brunet .channels_min = 2, 39*6ae9ca9cSJerome Brunet .channels_max = 2, 40*6ae9ca9cSJerome Brunet .period_bytes_min = AIU_FIFO_SPDIF_BLOCK, 41*6ae9ca9cSJerome Brunet .period_bytes_max = AIU_FIFO_SPDIF_BLOCK * USHRT_MAX, 42*6ae9ca9cSJerome Brunet .periods_min = 2, 43*6ae9ca9cSJerome Brunet .periods_max = UINT_MAX, 44*6ae9ca9cSJerome Brunet 45*6ae9ca9cSJerome Brunet /* No real justification for this */ 46*6ae9ca9cSJerome Brunet .buffer_bytes_max = 1 * 1024 * 1024, 47*6ae9ca9cSJerome Brunet }; 48*6ae9ca9cSJerome Brunet 49*6ae9ca9cSJerome Brunet static void fifo_spdif_dcu_enable(struct snd_soc_component *component, 50*6ae9ca9cSJerome Brunet bool enable) 51*6ae9ca9cSJerome Brunet { 52*6ae9ca9cSJerome Brunet snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL, 53*6ae9ca9cSJerome Brunet AIU_IEC958_DCU_FF_CTRL_EN, 54*6ae9ca9cSJerome Brunet enable ? AIU_IEC958_DCU_FF_CTRL_EN : 0); 55*6ae9ca9cSJerome Brunet } 56*6ae9ca9cSJerome Brunet 57*6ae9ca9cSJerome Brunet static int fifo_spdif_trigger(struct snd_pcm_substream *substream, int cmd, 58*6ae9ca9cSJerome Brunet struct snd_soc_dai *dai) 59*6ae9ca9cSJerome Brunet { 60*6ae9ca9cSJerome Brunet struct snd_soc_component *component = dai->component; 61*6ae9ca9cSJerome Brunet int ret; 62*6ae9ca9cSJerome Brunet 63*6ae9ca9cSJerome Brunet ret = aiu_fifo_trigger(substream, cmd, dai); 64*6ae9ca9cSJerome Brunet if (ret) 65*6ae9ca9cSJerome Brunet return ret; 66*6ae9ca9cSJerome Brunet 67*6ae9ca9cSJerome Brunet switch (cmd) { 68*6ae9ca9cSJerome Brunet case SNDRV_PCM_TRIGGER_START: 69*6ae9ca9cSJerome Brunet case SNDRV_PCM_TRIGGER_RESUME: 70*6ae9ca9cSJerome Brunet case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 71*6ae9ca9cSJerome Brunet fifo_spdif_dcu_enable(component, true); 72*6ae9ca9cSJerome Brunet break; 73*6ae9ca9cSJerome Brunet case SNDRV_PCM_TRIGGER_SUSPEND: 74*6ae9ca9cSJerome Brunet case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 75*6ae9ca9cSJerome Brunet case SNDRV_PCM_TRIGGER_STOP: 76*6ae9ca9cSJerome Brunet fifo_spdif_dcu_enable(component, false); 77*6ae9ca9cSJerome Brunet break; 78*6ae9ca9cSJerome Brunet default: 79*6ae9ca9cSJerome Brunet return -EINVAL; 80*6ae9ca9cSJerome Brunet } 81*6ae9ca9cSJerome Brunet 82*6ae9ca9cSJerome Brunet return 0; 83*6ae9ca9cSJerome Brunet } 84*6ae9ca9cSJerome Brunet 85*6ae9ca9cSJerome Brunet static int fifo_spdif_prepare(struct snd_pcm_substream *substream, 86*6ae9ca9cSJerome Brunet struct snd_soc_dai *dai) 87*6ae9ca9cSJerome Brunet { 88*6ae9ca9cSJerome Brunet struct snd_soc_component *component = dai->component; 89*6ae9ca9cSJerome Brunet int ret; 90*6ae9ca9cSJerome Brunet 91*6ae9ca9cSJerome Brunet ret = aiu_fifo_prepare(substream, dai); 92*6ae9ca9cSJerome Brunet if (ret) 93*6ae9ca9cSJerome Brunet return ret; 94*6ae9ca9cSJerome Brunet 95*6ae9ca9cSJerome Brunet snd_soc_component_update_bits(component, 96*6ae9ca9cSJerome Brunet AIU_MEM_IEC958_BUF_CNTL, 97*6ae9ca9cSJerome Brunet AIU_MEM_IEC958_BUF_CNTL_INIT, 98*6ae9ca9cSJerome Brunet AIU_MEM_IEC958_BUF_CNTL_INIT); 99*6ae9ca9cSJerome Brunet snd_soc_component_update_bits(component, 100*6ae9ca9cSJerome Brunet AIU_MEM_IEC958_BUF_CNTL, 101*6ae9ca9cSJerome Brunet AIU_MEM_IEC958_BUF_CNTL_INIT, 0); 102*6ae9ca9cSJerome Brunet 103*6ae9ca9cSJerome Brunet return 0; 104*6ae9ca9cSJerome Brunet } 105*6ae9ca9cSJerome Brunet 106*6ae9ca9cSJerome Brunet static int fifo_spdif_hw_params(struct snd_pcm_substream *substream, 107*6ae9ca9cSJerome Brunet struct snd_pcm_hw_params *params, 108*6ae9ca9cSJerome Brunet struct snd_soc_dai *dai) 109*6ae9ca9cSJerome Brunet { 110*6ae9ca9cSJerome Brunet struct snd_soc_component *component = dai->component; 111*6ae9ca9cSJerome Brunet unsigned int val; 112*6ae9ca9cSJerome Brunet int ret; 113*6ae9ca9cSJerome Brunet 114*6ae9ca9cSJerome Brunet ret = aiu_fifo_hw_params(substream, params, dai); 115*6ae9ca9cSJerome Brunet if (ret) 116*6ae9ca9cSJerome Brunet return ret; 117*6ae9ca9cSJerome Brunet 118*6ae9ca9cSJerome Brunet val = AIU_MEM_IEC958_CONTROL_RD_DDR | 119*6ae9ca9cSJerome Brunet AIU_MEM_IEC958_CONTROL_MODE_LINEAR; 120*6ae9ca9cSJerome Brunet 121*6ae9ca9cSJerome Brunet switch (params_physical_width(params)) { 122*6ae9ca9cSJerome Brunet case 16: 123*6ae9ca9cSJerome Brunet val |= AIU_MEM_IEC958_CONTROL_MODE_16BIT; 124*6ae9ca9cSJerome Brunet break; 125*6ae9ca9cSJerome Brunet case 32: 126*6ae9ca9cSJerome Brunet break; 127*6ae9ca9cSJerome Brunet default: 128*6ae9ca9cSJerome Brunet dev_err(dai->dev, "Unsupported physical width %u\n", 129*6ae9ca9cSJerome Brunet params_physical_width(params)); 130*6ae9ca9cSJerome Brunet return -EINVAL; 131*6ae9ca9cSJerome Brunet } 132*6ae9ca9cSJerome Brunet 133*6ae9ca9cSJerome Brunet snd_soc_component_update_bits(component, AIU_MEM_IEC958_CONTROL, 134*6ae9ca9cSJerome Brunet AIU_MEM_IEC958_CONTROL_ENDIAN | 135*6ae9ca9cSJerome Brunet AIU_MEM_IEC958_CONTROL_RD_DDR | 136*6ae9ca9cSJerome Brunet AIU_MEM_IEC958_CONTROL_MODE_LINEAR | 137*6ae9ca9cSJerome Brunet AIU_MEM_IEC958_CONTROL_MODE_16BIT, 138*6ae9ca9cSJerome Brunet val); 139*6ae9ca9cSJerome Brunet 140*6ae9ca9cSJerome Brunet /* Number bytes read by the FIFO between each IRQ */ 141*6ae9ca9cSJerome Brunet snd_soc_component_write(component, AIU_IEC958_BPF, 142*6ae9ca9cSJerome Brunet params_period_bytes(params)); 143*6ae9ca9cSJerome Brunet 144*6ae9ca9cSJerome Brunet /* 145*6ae9ca9cSJerome Brunet * AUTO_DISABLE and SYNC_HEAD are enabled by default but 146*6ae9ca9cSJerome Brunet * this should be disabled in PCM (uncompressed) mode 147*6ae9ca9cSJerome Brunet */ 148*6ae9ca9cSJerome Brunet snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL, 149*6ae9ca9cSJerome Brunet AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE | 150*6ae9ca9cSJerome Brunet AIU_IEC958_DCU_FF_CTRL_IRQ_MODE | 151*6ae9ca9cSJerome Brunet AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN, 152*6ae9ca9cSJerome Brunet AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ); 153*6ae9ca9cSJerome Brunet 154*6ae9ca9cSJerome Brunet return 0; 155*6ae9ca9cSJerome Brunet } 156*6ae9ca9cSJerome Brunet 157*6ae9ca9cSJerome Brunet const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops = { 158*6ae9ca9cSJerome Brunet .trigger = fifo_spdif_trigger, 159*6ae9ca9cSJerome Brunet .prepare = fifo_spdif_prepare, 160*6ae9ca9cSJerome Brunet .hw_params = fifo_spdif_hw_params, 161*6ae9ca9cSJerome Brunet .hw_free = aiu_fifo_hw_free, 162*6ae9ca9cSJerome Brunet .startup = aiu_fifo_startup, 163*6ae9ca9cSJerome Brunet .shutdown = aiu_fifo_shutdown, 164*6ae9ca9cSJerome Brunet }; 165*6ae9ca9cSJerome Brunet 166*6ae9ca9cSJerome Brunet int aiu_fifo_spdif_dai_probe(struct snd_soc_dai *dai) 167*6ae9ca9cSJerome Brunet { 168*6ae9ca9cSJerome Brunet struct snd_soc_component *component = dai->component; 169*6ae9ca9cSJerome Brunet struct aiu *aiu = snd_soc_component_get_drvdata(component); 170*6ae9ca9cSJerome Brunet struct aiu_fifo *fifo; 171*6ae9ca9cSJerome Brunet int ret; 172*6ae9ca9cSJerome Brunet 173*6ae9ca9cSJerome Brunet ret = aiu_fifo_dai_probe(dai); 174*6ae9ca9cSJerome Brunet if (ret) 175*6ae9ca9cSJerome Brunet return ret; 176*6ae9ca9cSJerome Brunet 177*6ae9ca9cSJerome Brunet fifo = dai->playback_dma_data; 178*6ae9ca9cSJerome Brunet 179*6ae9ca9cSJerome Brunet fifo->pcm = &fifo_spdif_pcm; 180*6ae9ca9cSJerome Brunet fifo->mem_offset = AIU_MEM_IEC958_START; 181*6ae9ca9cSJerome Brunet fifo->fifo_block = 1; 182*6ae9ca9cSJerome Brunet fifo->pclk = aiu->spdif.clks[PCLK].clk; 183*6ae9ca9cSJerome Brunet fifo->irq = aiu->spdif.irq; 184*6ae9ca9cSJerome Brunet 185*6ae9ca9cSJerome Brunet return 0; 186*6ae9ca9cSJerome Brunet } 187