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