Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: meson: aiu: add i2s and spdif support

Add support for the i2s and spdif audio outputs (AIU) found in the
amlogic Gx SoC family

Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Link: https://lore.kernel.org/r/20200213155159.3235792-5-jbrunet@baylibre.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Jerome Brunet and committed by
Mark Brown
6ae9ca9c 06b72824

+1589
+7
sound/soc/meson/Kconfig
··· 2 2 menu "ASoC support for Amlogic platforms" 3 3 depends on ARCH_MESON || COMPILE_TEST 4 4 5 + config SND_MESON_AIU 6 + tristate "Amlogic AIU" 7 + select SND_PCM_IEC958 8 + help 9 + Select Y or M to add support for the Audio output subsystem found 10 + in the Amlogic GX SoC family 11 + 5 12 config SND_MESON_AXG_FIFO 6 13 tristate 7 14 select REGMAP_MMIO
+7
sound/soc/meson/Makefile
··· 1 1 # SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 2 3 + snd-soc-meson-aiu-objs := aiu.o 4 + snd-soc-meson-aiu-objs += aiu-encoder-i2s.o 5 + snd-soc-meson-aiu-objs += aiu-encoder-spdif.o 6 + snd-soc-meson-aiu-objs += aiu-fifo.o 7 + snd-soc-meson-aiu-objs += aiu-fifo-i2s.o 8 + snd-soc-meson-aiu-objs += aiu-fifo-spdif.o 3 9 snd-soc-meson-axg-fifo-objs := axg-fifo.o 4 10 snd-soc-meson-axg-frddr-objs := axg-frddr.o 5 11 snd-soc-meson-axg-toddr-objs := axg-toddr.o ··· 20 14 snd-soc-meson-codec-glue-objs := meson-codec-glue.o 21 15 snd-soc-meson-g12a-tohdmitx-objs := g12a-tohdmitx.o 22 16 17 + obj-$(CONFIG_SND_MESON_AIU) += snd-soc-meson-aiu.o 23 18 obj-$(CONFIG_SND_MESON_AXG_FIFO) += snd-soc-meson-axg-fifo.o 24 19 obj-$(CONFIG_SND_MESON_AXG_FRDDR) += snd-soc-meson-axg-frddr.o 25 20 obj-$(CONFIG_SND_MESON_AXG_TODDR) += snd-soc-meson-axg-toddr.o
+324
sound/soc/meson/aiu-encoder-i2s.c
··· 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/bitfield.h> 7 + #include <linux/clk.h> 8 + #include <sound/pcm_params.h> 9 + #include <sound/soc.h> 10 + #include <sound/soc-dai.h> 11 + 12 + #include "aiu.h" 13 + 14 + #define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) 15 + #define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) 16 + #define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) 17 + #define AIU_I2S_SOURCE_DESC_MODE_SPLIT BIT(11) 18 + #define AIU_RST_SOFT_I2S_FAST BIT(0) 19 + 20 + #define AIU_I2S_DAC_CFG_MSB_FIRST BIT(2) 21 + #define AIU_I2S_MISC_HOLD_EN BIT(2) 22 + #define AIU_CLK_CTRL_I2S_DIV_EN BIT(0) 23 + #define AIU_CLK_CTRL_I2S_DIV GENMASK(3, 2) 24 + #define AIU_CLK_CTRL_AOCLK_INVERT BIT(6) 25 + #define AIU_CLK_CTRL_LRCLK_INVERT BIT(7) 26 + #define AIU_CLK_CTRL_LRCLK_SKEW GENMASK(9, 8) 27 + #define AIU_CLK_CTRL_MORE_HDMI_AMCLK BIT(6) 28 + #define AIU_CLK_CTRL_MORE_I2S_DIV GENMASK(5, 0) 29 + #define AIU_CODEC_DAC_LRCLK_CTRL_DIV GENMASK(11, 0) 30 + 31 + struct aiu_encoder_i2s { 32 + struct clk *aoclk; 33 + struct clk *mclk; 34 + struct clk *mixer; 35 + struct clk *pclk; 36 + }; 37 + 38 + static void aiu_encoder_i2s_divider_enable(struct snd_soc_component *component, 39 + bool enable) 40 + { 41 + snd_soc_component_update_bits(component, AIU_CLK_CTRL, 42 + AIU_CLK_CTRL_I2S_DIV_EN, 43 + enable ? AIU_CLK_CTRL_I2S_DIV_EN : 0); 44 + } 45 + 46 + static void aiu_encoder_i2s_hold(struct snd_soc_component *component, 47 + bool enable) 48 + { 49 + snd_soc_component_update_bits(component, AIU_I2S_MISC, 50 + AIU_I2S_MISC_HOLD_EN, 51 + enable ? AIU_I2S_MISC_HOLD_EN : 0); 52 + } 53 + 54 + static int aiu_encoder_i2s_trigger(struct snd_pcm_substream *substream, int cmd, 55 + struct snd_soc_dai *dai) 56 + { 57 + struct snd_soc_component *component = dai->component; 58 + 59 + switch (cmd) { 60 + case SNDRV_PCM_TRIGGER_START: 61 + case SNDRV_PCM_TRIGGER_RESUME: 62 + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 63 + aiu_encoder_i2s_hold(component, false); 64 + return 0; 65 + 66 + case SNDRV_PCM_TRIGGER_STOP: 67 + case SNDRV_PCM_TRIGGER_SUSPEND: 68 + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 69 + aiu_encoder_i2s_hold(component, true); 70 + return 0; 71 + 72 + default: 73 + return -EINVAL; 74 + } 75 + } 76 + 77 + static int aiu_encoder_i2s_setup_desc(struct snd_soc_component *component, 78 + struct snd_pcm_hw_params *params) 79 + { 80 + /* Always operate in split (classic interleaved) mode */ 81 + unsigned int desc = AIU_I2S_SOURCE_DESC_MODE_SPLIT; 82 + unsigned int val; 83 + 84 + /* Reset required to update the pipeline */ 85 + snd_soc_component_write(component, AIU_RST_SOFT, AIU_RST_SOFT_I2S_FAST); 86 + snd_soc_component_read(component, AIU_I2S_SYNC, &val); 87 + 88 + switch (params_physical_width(params)) { 89 + case 16: /* Nothing to do */ 90 + break; 91 + 92 + case 32: 93 + desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT | 94 + AIU_I2S_SOURCE_DESC_MODE_32BIT); 95 + break; 96 + 97 + default: 98 + return -EINVAL; 99 + } 100 + 101 + switch (params_channels(params)) { 102 + case 2: /* Nothing to do */ 103 + break; 104 + case 8: 105 + desc |= AIU_I2S_SOURCE_DESC_MODE_8CH; 106 + break; 107 + default: 108 + return -EINVAL; 109 + } 110 + 111 + snd_soc_component_update_bits(component, AIU_I2S_SOURCE_DESC, 112 + AIU_I2S_SOURCE_DESC_MODE_8CH | 113 + AIU_I2S_SOURCE_DESC_MODE_24BIT | 114 + AIU_I2S_SOURCE_DESC_MODE_32BIT | 115 + AIU_I2S_SOURCE_DESC_MODE_SPLIT, 116 + desc); 117 + 118 + return 0; 119 + } 120 + 121 + static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component, 122 + struct snd_pcm_hw_params *params) 123 + { 124 + struct aiu *aiu = snd_soc_component_get_drvdata(component); 125 + unsigned int srate = params_rate(params); 126 + unsigned int fs, bs; 127 + 128 + /* Get the oversampling factor */ 129 + fs = DIV_ROUND_CLOSEST(clk_get_rate(aiu->i2s.clks[MCLK].clk), srate); 130 + 131 + if (fs % 64) 132 + return -EINVAL; 133 + 134 + /* Send data MSB first */ 135 + snd_soc_component_update_bits(component, AIU_I2S_DAC_CFG, 136 + AIU_I2S_DAC_CFG_MSB_FIRST, 137 + AIU_I2S_DAC_CFG_MSB_FIRST); 138 + 139 + /* Set bclk to lrlck ratio */ 140 + snd_soc_component_update_bits(component, AIU_CODEC_DAC_LRCLK_CTRL, 141 + AIU_CODEC_DAC_LRCLK_CTRL_DIV, 142 + FIELD_PREP(AIU_CODEC_DAC_LRCLK_CTRL_DIV, 143 + 64 - 1)); 144 + 145 + /* Use CLK_MORE for mclk to bclk divider */ 146 + snd_soc_component_update_bits(component, AIU_CLK_CTRL, 147 + AIU_CLK_CTRL_I2S_DIV, 0); 148 + 149 + /* 150 + * NOTE: this HW is odd. 151 + * In most configuration, the i2s divider is 'mclk / blck'. 152 + * However, in 16 bits - 8ch mode, this factor needs to be 153 + * increased by 50% to get the correct output rate. 154 + * No idea why ! 155 + */ 156 + bs = fs / 64; 157 + if (params_width(params) == 16 && params_channels(params) == 8) { 158 + if (bs % 2) { 159 + dev_err(component->dev, 160 + "Cannot increase i2s divider by 50%%\n"); 161 + return -EINVAL; 162 + } 163 + bs += bs / 2; 164 + } 165 + 166 + snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE, 167 + AIU_CLK_CTRL_MORE_I2S_DIV, 168 + FIELD_PREP(AIU_CLK_CTRL_MORE_I2S_DIV, 169 + bs - 1)); 170 + 171 + /* Make sure amclk is used for HDMI i2s as well */ 172 + snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE, 173 + AIU_CLK_CTRL_MORE_HDMI_AMCLK, 174 + AIU_CLK_CTRL_MORE_HDMI_AMCLK); 175 + 176 + return 0; 177 + } 178 + 179 + static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, 180 + struct snd_pcm_hw_params *params, 181 + struct snd_soc_dai *dai) 182 + { 183 + struct snd_soc_component *component = dai->component; 184 + int ret; 185 + 186 + /* Disable the clock while changing the settings */ 187 + aiu_encoder_i2s_divider_enable(component, false); 188 + 189 + ret = aiu_encoder_i2s_setup_desc(component, params); 190 + if (ret) { 191 + dev_err(dai->dev, "setting i2s desc failed\n"); 192 + return ret; 193 + } 194 + 195 + ret = aiu_encoder_i2s_set_clocks(component, params); 196 + if (ret) { 197 + dev_err(dai->dev, "setting i2s clocks failed\n"); 198 + return ret; 199 + } 200 + 201 + aiu_encoder_i2s_divider_enable(component, true); 202 + 203 + return 0; 204 + } 205 + 206 + static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, 207 + struct snd_soc_dai *dai) 208 + { 209 + struct snd_soc_component *component = dai->component; 210 + 211 + aiu_encoder_i2s_divider_enable(component, false); 212 + 213 + return 0; 214 + } 215 + 216 + static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 217 + { 218 + struct snd_soc_component *component = dai->component; 219 + unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK; 220 + unsigned int val = 0; 221 + unsigned int skew; 222 + 223 + /* Only CPU Master / Codec Slave supported ATM */ 224 + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) 225 + return -EINVAL; 226 + 227 + if (inv == SND_SOC_DAIFMT_NB_IF || 228 + inv == SND_SOC_DAIFMT_IB_IF) 229 + val |= AIU_CLK_CTRL_LRCLK_INVERT; 230 + 231 + if (inv == SND_SOC_DAIFMT_IB_NF || 232 + inv == SND_SOC_DAIFMT_IB_IF) 233 + val |= AIU_CLK_CTRL_AOCLK_INVERT; 234 + 235 + /* Signal skew */ 236 + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 237 + case SND_SOC_DAIFMT_I2S: 238 + /* Invert sample clock for i2s */ 239 + val ^= AIU_CLK_CTRL_LRCLK_INVERT; 240 + skew = 1; 241 + break; 242 + case SND_SOC_DAIFMT_LEFT_J: 243 + skew = 0; 244 + break; 245 + default: 246 + return -EINVAL; 247 + } 248 + 249 + val |= FIELD_PREP(AIU_CLK_CTRL_LRCLK_SKEW, skew); 250 + snd_soc_component_update_bits(component, AIU_CLK_CTRL, 251 + AIU_CLK_CTRL_LRCLK_INVERT | 252 + AIU_CLK_CTRL_AOCLK_INVERT | 253 + AIU_CLK_CTRL_LRCLK_SKEW, 254 + val); 255 + 256 + return 0; 257 + } 258 + 259 + static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, 260 + unsigned int freq, int dir) 261 + { 262 + struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); 263 + int ret; 264 + 265 + if (WARN_ON(clk_id != 0)) 266 + return -EINVAL; 267 + 268 + if (dir == SND_SOC_CLOCK_IN) 269 + return 0; 270 + 271 + ret = clk_set_rate(aiu->i2s.clks[MCLK].clk, freq); 272 + if (ret) 273 + dev_err(dai->dev, "Failed to set sysclk to %uHz", freq); 274 + 275 + return ret; 276 + } 277 + 278 + static const unsigned int hw_channels[] = {2, 8}; 279 + static const struct snd_pcm_hw_constraint_list hw_channel_constraints = { 280 + .list = hw_channels, 281 + .count = ARRAY_SIZE(hw_channels), 282 + .mask = 0, 283 + }; 284 + 285 + static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream, 286 + struct snd_soc_dai *dai) 287 + { 288 + struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); 289 + int ret; 290 + 291 + /* Make sure the encoder gets either 2 or 8 channels */ 292 + ret = snd_pcm_hw_constraint_list(substream->runtime, 0, 293 + SNDRV_PCM_HW_PARAM_CHANNELS, 294 + &hw_channel_constraints); 295 + if (ret) { 296 + dev_err(dai->dev, "adding channels constraints failed\n"); 297 + return ret; 298 + } 299 + 300 + ret = clk_bulk_prepare_enable(aiu->i2s.clk_num, aiu->i2s.clks); 301 + if (ret) 302 + dev_err(dai->dev, "failed to enable i2s clocks\n"); 303 + 304 + return ret; 305 + } 306 + 307 + static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream, 308 + struct snd_soc_dai *dai) 309 + { 310 + struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); 311 + 312 + clk_bulk_disable_unprepare(aiu->i2s.clk_num, aiu->i2s.clks); 313 + } 314 + 315 + const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops = { 316 + .trigger = aiu_encoder_i2s_trigger, 317 + .hw_params = aiu_encoder_i2s_hw_params, 318 + .hw_free = aiu_encoder_i2s_hw_free, 319 + .set_fmt = aiu_encoder_i2s_set_fmt, 320 + .set_sysclk = aiu_encoder_i2s_set_sysclk, 321 + .startup = aiu_encoder_i2s_startup, 322 + .shutdown = aiu_encoder_i2s_shutdown, 323 + }; 324 +
+209
sound/soc/meson/aiu-encoder-spdif.c
··· 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/bitfield.h> 7 + #include <linux/clk.h> 8 + #include <sound/pcm_params.h> 9 + #include <sound/pcm_iec958.h> 10 + #include <sound/soc.h> 11 + #include <sound/soc-dai.h> 12 + 13 + #include "aiu.h" 14 + 15 + #define AIU_958_MISC_NON_PCM BIT(0) 16 + #define AIU_958_MISC_MODE_16BITS BIT(1) 17 + #define AIU_958_MISC_16BITS_ALIGN GENMASK(6, 5) 18 + #define AIU_958_MISC_MODE_32BITS BIT(7) 19 + #define AIU_958_MISC_U_FROM_STREAM BIT(12) 20 + #define AIU_958_MISC_FORCE_LR BIT(13) 21 + #define AIU_958_CTRL_HOLD_EN BIT(0) 22 + #define AIU_CLK_CTRL_958_DIV_EN BIT(1) 23 + #define AIU_CLK_CTRL_958_DIV GENMASK(5, 4) 24 + #define AIU_CLK_CTRL_958_DIV_MORE BIT(12) 25 + 26 + #define AIU_CS_WORD_LEN 4 27 + #define AIU_958_INTERNAL_DIV 2 28 + 29 + static void 30 + aiu_encoder_spdif_divider_enable(struct snd_soc_component *component, 31 + bool enable) 32 + { 33 + snd_soc_component_update_bits(component, AIU_CLK_CTRL, 34 + AIU_CLK_CTRL_958_DIV_EN, 35 + enable ? AIU_CLK_CTRL_958_DIV_EN : 0); 36 + } 37 + 38 + static void aiu_encoder_spdif_hold(struct snd_soc_component *component, 39 + bool enable) 40 + { 41 + snd_soc_component_update_bits(component, AIU_958_CTRL, 42 + AIU_958_CTRL_HOLD_EN, 43 + enable ? AIU_958_CTRL_HOLD_EN : 0); 44 + } 45 + 46 + static int 47 + aiu_encoder_spdif_trigger(struct snd_pcm_substream *substream, int cmd, 48 + struct snd_soc_dai *dai) 49 + { 50 + struct snd_soc_component *component = dai->component; 51 + 52 + switch (cmd) { 53 + case SNDRV_PCM_TRIGGER_START: 54 + case SNDRV_PCM_TRIGGER_RESUME: 55 + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 56 + aiu_encoder_spdif_hold(component, false); 57 + return 0; 58 + 59 + case SNDRV_PCM_TRIGGER_STOP: 60 + case SNDRV_PCM_TRIGGER_SUSPEND: 61 + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 62 + aiu_encoder_spdif_hold(component, true); 63 + return 0; 64 + 65 + default: 66 + return -EINVAL; 67 + } 68 + } 69 + 70 + static int aiu_encoder_spdif_setup_cs_word(struct snd_soc_component *component, 71 + struct snd_pcm_hw_params *params) 72 + { 73 + u8 cs[AIU_CS_WORD_LEN]; 74 + unsigned int val; 75 + int ret; 76 + 77 + ret = snd_pcm_create_iec958_consumer_hw_params(params, cs, 78 + AIU_CS_WORD_LEN); 79 + if (ret < 0) 80 + return ret; 81 + 82 + /* Write the 1st half word */ 83 + val = cs[1] | cs[0] << 8; 84 + snd_soc_component_write(component, AIU_958_CHSTAT_L0, val); 85 + snd_soc_component_write(component, AIU_958_CHSTAT_R0, val); 86 + 87 + /* Write the 2nd half word */ 88 + val = cs[3] | cs[2] << 8; 89 + snd_soc_component_write(component, AIU_958_CHSTAT_L1, val); 90 + snd_soc_component_write(component, AIU_958_CHSTAT_R1, val); 91 + 92 + return 0; 93 + } 94 + 95 + static int aiu_encoder_spdif_hw_params(struct snd_pcm_substream *substream, 96 + struct snd_pcm_hw_params *params, 97 + struct snd_soc_dai *dai) 98 + { 99 + struct snd_soc_component *component = dai->component; 100 + struct aiu *aiu = snd_soc_component_get_drvdata(component); 101 + unsigned int val = 0, mrate; 102 + int ret; 103 + 104 + /* Disable the clock while changing the settings */ 105 + aiu_encoder_spdif_divider_enable(component, false); 106 + 107 + switch (params_physical_width(params)) { 108 + case 16: 109 + val |= AIU_958_MISC_MODE_16BITS; 110 + val |= FIELD_PREP(AIU_958_MISC_16BITS_ALIGN, 2); 111 + break; 112 + case 32: 113 + val |= AIU_958_MISC_MODE_32BITS; 114 + break; 115 + default: 116 + dev_err(dai->dev, "Unsupport physical width\n"); 117 + return -EINVAL; 118 + } 119 + 120 + snd_soc_component_update_bits(component, AIU_958_MISC, 121 + AIU_958_MISC_NON_PCM | 122 + AIU_958_MISC_MODE_16BITS | 123 + AIU_958_MISC_16BITS_ALIGN | 124 + AIU_958_MISC_MODE_32BITS | 125 + AIU_958_MISC_FORCE_LR | 126 + AIU_958_MISC_U_FROM_STREAM, 127 + val); 128 + 129 + /* Set the stream channel status word */ 130 + ret = aiu_encoder_spdif_setup_cs_word(component, params); 131 + if (ret) { 132 + dev_err(dai->dev, "failed to set channel status word\n"); 133 + return ret; 134 + } 135 + 136 + snd_soc_component_update_bits(component, AIU_CLK_CTRL, 137 + AIU_CLK_CTRL_958_DIV | 138 + AIU_CLK_CTRL_958_DIV_MORE, 139 + FIELD_PREP(AIU_CLK_CTRL_958_DIV, 140 + __ffs(AIU_958_INTERNAL_DIV))); 141 + 142 + /* 2 * 32bits per subframe * 2 channels = 128 */ 143 + mrate = params_rate(params) * 128 * AIU_958_INTERNAL_DIV; 144 + ret = clk_set_rate(aiu->spdif.clks[MCLK].clk, mrate); 145 + if (ret) { 146 + dev_err(dai->dev, "failed to set mclk rate\n"); 147 + return ret; 148 + } 149 + 150 + aiu_encoder_spdif_divider_enable(component, true); 151 + 152 + return 0; 153 + } 154 + 155 + static int aiu_encoder_spdif_hw_free(struct snd_pcm_substream *substream, 156 + struct snd_soc_dai *dai) 157 + { 158 + struct snd_soc_component *component = dai->component; 159 + 160 + aiu_encoder_spdif_divider_enable(component, false); 161 + 162 + return 0; 163 + } 164 + 165 + static int aiu_encoder_spdif_startup(struct snd_pcm_substream *substream, 166 + struct snd_soc_dai *dai) 167 + { 168 + struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); 169 + int ret; 170 + 171 + /* 172 + * NOTE: Make sure the spdif block is on its own divider. 173 + * 174 + * The spdif can be clocked by the i2s master clock or its own 175 + * clock. We should (in theory) change the source depending on the 176 + * origin of the data. 177 + * 178 + * However, considering the clocking scheme used on these platforms, 179 + * the master clocks will pick the same PLL source when they are 180 + * playing from the same FIFO. The clock should be in sync so, it 181 + * should not be necessary to reparent the spdif master clock. 182 + */ 183 + ret = clk_set_parent(aiu->spdif.clks[MCLK].clk, 184 + aiu->spdif_mclk); 185 + if (ret) 186 + return ret; 187 + 188 + ret = clk_bulk_prepare_enable(aiu->spdif.clk_num, aiu->spdif.clks); 189 + if (ret) 190 + dev_err(dai->dev, "failed to enable spdif clocks\n"); 191 + 192 + return ret; 193 + } 194 + 195 + static void aiu_encoder_spdif_shutdown(struct snd_pcm_substream *substream, 196 + struct snd_soc_dai *dai) 197 + { 198 + struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); 199 + 200 + clk_bulk_disable_unprepare(aiu->spdif.clk_num, aiu->spdif.clks); 201 + } 202 + 203 + const struct snd_soc_dai_ops aiu_encoder_spdif_dai_ops = { 204 + .trigger = aiu_encoder_spdif_trigger, 205 + .hw_params = aiu_encoder_spdif_hw_params, 206 + .hw_free = aiu_encoder_spdif_hw_free, 207 + .startup = aiu_encoder_spdif_startup, 208 + .shutdown = aiu_encoder_spdif_shutdown, 209 + };
+153
sound/soc/meson/aiu-fifo-i2s.c
··· 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/bitfield.h> 7 + #include <linux/clk.h> 8 + #include <sound/pcm_params.h> 9 + #include <sound/soc.h> 10 + #include <sound/soc-dai.h> 11 + 12 + #include "aiu.h" 13 + #include "aiu-fifo.h" 14 + 15 + #define AIU_I2S_SOURCE_DESC_MODE_8CH BIT(0) 16 + #define AIU_I2S_SOURCE_DESC_MODE_24BIT BIT(5) 17 + #define AIU_I2S_SOURCE_DESC_MODE_32BIT BIT(9) 18 + #define AIU_I2S_SOURCE_DESC_MODE_SPLIT BIT(11) 19 + #define AIU_MEM_I2S_MASKS_IRQ_BLOCK GENMASK(31, 16) 20 + #define AIU_MEM_I2S_CONTROL_MODE_16BIT BIT(6) 21 + #define AIU_MEM_I2S_BUF_CNTL_INIT BIT(0) 22 + #define AIU_RST_SOFT_I2S_FAST BIT(0) 23 + 24 + #define AIU_FIFO_I2S_BLOCK 256 25 + 26 + static struct snd_pcm_hardware fifo_i2s_pcm = { 27 + .info = (SNDRV_PCM_INFO_INTERLEAVED | 28 + SNDRV_PCM_INFO_MMAP | 29 + SNDRV_PCM_INFO_MMAP_VALID | 30 + SNDRV_PCM_INFO_PAUSE), 31 + .formats = AIU_FORMATS, 32 + .rate_min = 5512, 33 + .rate_max = 192000, 34 + .channels_min = 2, 35 + .channels_max = 8, 36 + .period_bytes_min = AIU_FIFO_I2S_BLOCK, 37 + .period_bytes_max = AIU_FIFO_I2S_BLOCK * USHRT_MAX, 38 + .periods_min = 2, 39 + .periods_max = UINT_MAX, 40 + 41 + /* No real justification for this */ 42 + .buffer_bytes_max = 1 * 1024 * 1024, 43 + }; 44 + 45 + static int aiu_fifo_i2s_trigger(struct snd_pcm_substream *substream, int cmd, 46 + struct snd_soc_dai *dai) 47 + { 48 + struct snd_soc_component *component = dai->component; 49 + unsigned int val; 50 + 51 + switch (cmd) { 52 + case SNDRV_PCM_TRIGGER_START: 53 + case SNDRV_PCM_TRIGGER_RESUME: 54 + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 55 + snd_soc_component_write(component, AIU_RST_SOFT, 56 + AIU_RST_SOFT_I2S_FAST); 57 + snd_soc_component_read(component, AIU_I2S_SYNC, &val); 58 + break; 59 + } 60 + 61 + return aiu_fifo_trigger(substream, cmd, dai); 62 + } 63 + 64 + static int aiu_fifo_i2s_prepare(struct snd_pcm_substream *substream, 65 + struct snd_soc_dai *dai) 66 + { 67 + struct snd_soc_component *component = dai->component; 68 + int ret; 69 + 70 + ret = aiu_fifo_prepare(substream, dai); 71 + if (ret) 72 + return ret; 73 + 74 + snd_soc_component_update_bits(component, 75 + AIU_MEM_I2S_BUF_CNTL, 76 + AIU_MEM_I2S_BUF_CNTL_INIT, 77 + AIU_MEM_I2S_BUF_CNTL_INIT); 78 + snd_soc_component_update_bits(component, 79 + AIU_MEM_I2S_BUF_CNTL, 80 + AIU_MEM_I2S_BUF_CNTL_INIT, 0); 81 + 82 + return 0; 83 + } 84 + 85 + static int aiu_fifo_i2s_hw_params(struct snd_pcm_substream *substream, 86 + struct snd_pcm_hw_params *params, 87 + struct snd_soc_dai *dai) 88 + { 89 + struct snd_soc_component *component = dai->component; 90 + struct aiu_fifo *fifo = dai->playback_dma_data; 91 + unsigned int val; 92 + int ret; 93 + 94 + ret = aiu_fifo_hw_params(substream, params, dai); 95 + if (ret) 96 + return ret; 97 + 98 + switch (params_physical_width(params)) { 99 + case 16: 100 + val = AIU_MEM_I2S_CONTROL_MODE_16BIT; 101 + break; 102 + case 32: 103 + val = 0; 104 + break; 105 + default: 106 + dev_err(dai->dev, "Unsupported physical width %u\n", 107 + params_physical_width(params)); 108 + return -EINVAL; 109 + } 110 + 111 + snd_soc_component_update_bits(component, AIU_MEM_I2S_CONTROL, 112 + AIU_MEM_I2S_CONTROL_MODE_16BIT, 113 + val); 114 + 115 + /* Setup the irq periodicity */ 116 + val = params_period_bytes(params) / fifo->fifo_block; 117 + val = FIELD_PREP(AIU_MEM_I2S_MASKS_IRQ_BLOCK, val); 118 + snd_soc_component_update_bits(component, AIU_MEM_I2S_MASKS, 119 + AIU_MEM_I2S_MASKS_IRQ_BLOCK, val); 120 + 121 + return 0; 122 + } 123 + 124 + const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops = { 125 + .trigger = aiu_fifo_i2s_trigger, 126 + .prepare = aiu_fifo_i2s_prepare, 127 + .hw_params = aiu_fifo_i2s_hw_params, 128 + .hw_free = aiu_fifo_hw_free, 129 + .startup = aiu_fifo_startup, 130 + .shutdown = aiu_fifo_shutdown, 131 + }; 132 + 133 + int aiu_fifo_i2s_dai_probe(struct snd_soc_dai *dai) 134 + { 135 + struct snd_soc_component *component = dai->component; 136 + struct aiu *aiu = snd_soc_component_get_drvdata(component); 137 + struct aiu_fifo *fifo; 138 + int ret; 139 + 140 + ret = aiu_fifo_dai_probe(dai); 141 + if (ret) 142 + return ret; 143 + 144 + fifo = dai->playback_dma_data; 145 + 146 + fifo->pcm = &fifo_i2s_pcm; 147 + fifo->mem_offset = AIU_MEM_I2S_START; 148 + fifo->fifo_block = AIU_FIFO_I2S_BLOCK; 149 + fifo->pclk = aiu->i2s.clks[PCLK].clk; 150 + fifo->irq = aiu->i2s.irq; 151 + 152 + return 0; 153 + }
+186
sound/soc/meson/aiu-fifo-spdif.c
··· 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 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 + .trigger = fifo_spdif_trigger, 159 + .prepare = fifo_spdif_prepare, 160 + .hw_params = fifo_spdif_hw_params, 161 + .hw_free = aiu_fifo_hw_free, 162 + .startup = aiu_fifo_startup, 163 + .shutdown = aiu_fifo_shutdown, 164 + }; 165 + 166 + int aiu_fifo_spdif_dai_probe(struct snd_soc_dai *dai) 167 + { 168 + struct snd_soc_component *component = dai->component; 169 + struct aiu *aiu = snd_soc_component_get_drvdata(component); 170 + struct aiu_fifo *fifo; 171 + int ret; 172 + 173 + ret = aiu_fifo_dai_probe(dai); 174 + if (ret) 175 + return ret; 176 + 177 + fifo = dai->playback_dma_data; 178 + 179 + fifo->pcm = &fifo_spdif_pcm; 180 + fifo->mem_offset = AIU_MEM_IEC958_START; 181 + fifo->fifo_block = 1; 182 + fifo->pclk = aiu->spdif.clks[PCLK].clk; 183 + fifo->irq = aiu->spdif.irq; 184 + 185 + return 0; 186 + }
+223
sound/soc/meson/aiu-fifo.c
··· 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/bitfield.h> 7 + #include <linux/clk.h> 8 + #include <sound/pcm_params.h> 9 + #include <sound/soc.h> 10 + #include <sound/soc-dai.h> 11 + 12 + #include "aiu-fifo.h" 13 + 14 + #define AIU_MEM_START 0x00 15 + #define AIU_MEM_RD 0x04 16 + #define AIU_MEM_END 0x08 17 + #define AIU_MEM_MASKS 0x0c 18 + #define AIU_MEM_MASK_CH_RD GENMASK(7, 0) 19 + #define AIU_MEM_MASK_CH_MEM GENMASK(15, 8) 20 + #define AIU_MEM_CONTROL 0x10 21 + #define AIU_MEM_CONTROL_INIT BIT(0) 22 + #define AIU_MEM_CONTROL_FILL_EN BIT(1) 23 + #define AIU_MEM_CONTROL_EMPTY_EN BIT(2) 24 + 25 + static struct snd_soc_dai *aiu_fifo_dai(struct snd_pcm_substream *ss) 26 + { 27 + struct snd_soc_pcm_runtime *rtd = ss->private_data; 28 + 29 + return rtd->cpu_dai; 30 + } 31 + 32 + snd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component, 33 + struct snd_pcm_substream *substream) 34 + { 35 + struct snd_soc_dai *dai = aiu_fifo_dai(substream); 36 + struct aiu_fifo *fifo = dai->playback_dma_data; 37 + struct snd_pcm_runtime *runtime = substream->runtime; 38 + unsigned int addr; 39 + 40 + snd_soc_component_read(component, fifo->mem_offset + AIU_MEM_RD, 41 + &addr); 42 + 43 + return bytes_to_frames(runtime, addr - (unsigned int)runtime->dma_addr); 44 + } 45 + 46 + static void aiu_fifo_enable(struct snd_soc_dai *dai, bool enable) 47 + { 48 + struct snd_soc_component *component = dai->component; 49 + struct aiu_fifo *fifo = dai->playback_dma_data; 50 + unsigned int en_mask = (AIU_MEM_CONTROL_FILL_EN | 51 + AIU_MEM_CONTROL_EMPTY_EN); 52 + 53 + snd_soc_component_update_bits(component, 54 + fifo->mem_offset + AIU_MEM_CONTROL, 55 + en_mask, enable ? en_mask : 0); 56 + } 57 + 58 + int aiu_fifo_trigger(struct snd_pcm_substream *substream, int cmd, 59 + struct snd_soc_dai *dai) 60 + { 61 + switch (cmd) { 62 + case SNDRV_PCM_TRIGGER_START: 63 + case SNDRV_PCM_TRIGGER_RESUME: 64 + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 65 + aiu_fifo_enable(dai, true); 66 + break; 67 + case SNDRV_PCM_TRIGGER_SUSPEND: 68 + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 69 + case SNDRV_PCM_TRIGGER_STOP: 70 + aiu_fifo_enable(dai, false); 71 + break; 72 + default: 73 + return -EINVAL; 74 + } 75 + 76 + return 0; 77 + } 78 + 79 + int aiu_fifo_prepare(struct snd_pcm_substream *substream, 80 + struct snd_soc_dai *dai) 81 + { 82 + struct snd_soc_component *component = dai->component; 83 + struct aiu_fifo *fifo = dai->playback_dma_data; 84 + 85 + snd_soc_component_update_bits(component, 86 + fifo->mem_offset + AIU_MEM_CONTROL, 87 + AIU_MEM_CONTROL_INIT, 88 + AIU_MEM_CONTROL_INIT); 89 + snd_soc_component_update_bits(component, 90 + fifo->mem_offset + AIU_MEM_CONTROL, 91 + AIU_MEM_CONTROL_INIT, 0); 92 + return 0; 93 + } 94 + 95 + int aiu_fifo_hw_params(struct snd_pcm_substream *substream, 96 + struct snd_pcm_hw_params *params, 97 + struct snd_soc_dai *dai) 98 + { 99 + struct snd_pcm_runtime *runtime = substream->runtime; 100 + struct snd_soc_component *component = dai->component; 101 + struct aiu_fifo *fifo = dai->playback_dma_data; 102 + dma_addr_t end; 103 + int ret; 104 + 105 + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); 106 + if (ret < 0) 107 + return ret; 108 + 109 + /* Setup the fifo boundaries */ 110 + end = runtime->dma_addr + runtime->dma_bytes - fifo->fifo_block; 111 + snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_START, 112 + runtime->dma_addr); 113 + snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_RD, 114 + runtime->dma_addr); 115 + snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_END, 116 + end); 117 + 118 + /* Setup the fifo to read all the memory - no skip */ 119 + snd_soc_component_update_bits(component, 120 + fifo->mem_offset + AIU_MEM_MASKS, 121 + AIU_MEM_MASK_CH_RD | AIU_MEM_MASK_CH_MEM, 122 + FIELD_PREP(AIU_MEM_MASK_CH_RD, 0xff) | 123 + FIELD_PREP(AIU_MEM_MASK_CH_MEM, 0xff)); 124 + 125 + return 0; 126 + } 127 + 128 + int aiu_fifo_hw_free(struct snd_pcm_substream *substream, 129 + struct snd_soc_dai *dai) 130 + { 131 + return snd_pcm_lib_free_pages(substream); 132 + } 133 + 134 + static irqreturn_t aiu_fifo_isr(int irq, void *dev_id) 135 + { 136 + struct snd_pcm_substream *playback = dev_id; 137 + 138 + snd_pcm_period_elapsed(playback); 139 + 140 + return IRQ_HANDLED; 141 + } 142 + 143 + int aiu_fifo_startup(struct snd_pcm_substream *substream, 144 + struct snd_soc_dai *dai) 145 + { 146 + struct aiu_fifo *fifo = dai->playback_dma_data; 147 + int ret; 148 + 149 + snd_soc_set_runtime_hwparams(substream, fifo->pcm); 150 + 151 + /* 152 + * Make sure the buffer and period size are multiple of the fifo burst 153 + * size 154 + */ 155 + ret = snd_pcm_hw_constraint_step(substream->runtime, 0, 156 + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 157 + fifo->fifo_block); 158 + if (ret) 159 + return ret; 160 + 161 + ret = snd_pcm_hw_constraint_step(substream->runtime, 0, 162 + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 163 + fifo->fifo_block); 164 + if (ret) 165 + return ret; 166 + 167 + ret = clk_prepare_enable(fifo->pclk); 168 + if (ret) 169 + return ret; 170 + 171 + ret = request_irq(fifo->irq, aiu_fifo_isr, 0, dev_name(dai->dev), 172 + substream); 173 + if (ret) 174 + clk_disable_unprepare(fifo->pclk); 175 + 176 + return ret; 177 + } 178 + 179 + void aiu_fifo_shutdown(struct snd_pcm_substream *substream, 180 + struct snd_soc_dai *dai) 181 + { 182 + struct aiu_fifo *fifo = dai->playback_dma_data; 183 + 184 + free_irq(fifo->irq, substream); 185 + clk_disable_unprepare(fifo->pclk); 186 + } 187 + 188 + int aiu_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd, 189 + struct snd_soc_dai *dai) 190 + { 191 + struct snd_pcm_substream *substream = 192 + rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; 193 + struct snd_card *card = rtd->card->snd_card; 194 + struct aiu_fifo *fifo = dai->playback_dma_data; 195 + size_t size = fifo->pcm->buffer_bytes_max; 196 + 197 + snd_pcm_lib_preallocate_pages(substream, 198 + SNDRV_DMA_TYPE_DEV, 199 + card->dev, size, size); 200 + 201 + return 0; 202 + } 203 + 204 + int aiu_fifo_dai_probe(struct snd_soc_dai *dai) 205 + { 206 + struct aiu_fifo *fifo; 207 + 208 + fifo = kzalloc(sizeof(*fifo), GFP_KERNEL); 209 + if (!fifo) 210 + return -ENOMEM; 211 + 212 + dai->playback_dma_data = fifo; 213 + 214 + return 0; 215 + } 216 + 217 + int aiu_fifo_dai_remove(struct snd_soc_dai *dai) 218 + { 219 + kfree(dai->playback_dma_data); 220 + 221 + return 0; 222 + } 223 +
+50
sound/soc/meson/aiu-fifo.h
··· 1 + /* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ 2 + /* 3 + * Copyright (c) 2020 BayLibre, SAS. 4 + * Author: Jerome Brunet <jbrunet@baylibre.com> 5 + */ 6 + 7 + #ifndef _MESON_AIU_FIFO_H 8 + #define _MESON_AIU_FIFO_H 9 + 10 + struct snd_pcm_hardware; 11 + struct snd_soc_component_driver; 12 + struct snd_soc_dai_driver; 13 + struct clk; 14 + struct snd_pcm_ops; 15 + struct snd_pcm_substream; 16 + struct snd_soc_dai; 17 + struct snd_pcm_hw_params; 18 + struct platform_device; 19 + 20 + struct aiu_fifo { 21 + struct snd_pcm_hardware *pcm; 22 + unsigned int mem_offset; 23 + unsigned int fifo_block; 24 + struct clk *pclk; 25 + int irq; 26 + }; 27 + 28 + int aiu_fifo_dai_probe(struct snd_soc_dai *dai); 29 + int aiu_fifo_dai_remove(struct snd_soc_dai *dai); 30 + 31 + snd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component, 32 + struct snd_pcm_substream *substream); 33 + 34 + int aiu_fifo_trigger(struct snd_pcm_substream *substream, int cmd, 35 + struct snd_soc_dai *dai); 36 + int aiu_fifo_prepare(struct snd_pcm_substream *substream, 37 + struct snd_soc_dai *dai); 38 + int aiu_fifo_hw_params(struct snd_pcm_substream *substream, 39 + struct snd_pcm_hw_params *params, 40 + struct snd_soc_dai *dai); 41 + int aiu_fifo_hw_free(struct snd_pcm_substream *substream, 42 + struct snd_soc_dai *dai); 43 + int aiu_fifo_startup(struct snd_pcm_substream *substream, 44 + struct snd_soc_dai *dai); 45 + void aiu_fifo_shutdown(struct snd_pcm_substream *substream, 46 + struct snd_soc_dai *dai); 47 + int aiu_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd, 48 + struct snd_soc_dai *dai); 49 + 50 + #endif /* _MESON_AIU_FIFO_H */
+348
sound/soc/meson/aiu.c
··· 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/bitfield.h> 7 + #include <linux/clk.h> 8 + #include <linux/module.h> 9 + #include <linux/of_platform.h> 10 + #include <linux/regmap.h> 11 + #include <linux/reset.h> 12 + #include <sound/soc.h> 13 + #include <sound/soc-dai.h> 14 + 15 + #include <dt-bindings/sound/meson-aiu.h> 16 + #include "aiu.h" 17 + #include "aiu-fifo.h" 18 + 19 + #define AIU_I2S_MISC_958_SRC_SHIFT 3 20 + 21 + static const char * const aiu_spdif_encode_sel_texts[] = { 22 + "SPDIF", "I2S", 23 + }; 24 + 25 + static SOC_ENUM_SINGLE_DECL(aiu_spdif_encode_sel_enum, AIU_I2S_MISC, 26 + AIU_I2S_MISC_958_SRC_SHIFT, 27 + aiu_spdif_encode_sel_texts); 28 + 29 + static const struct snd_kcontrol_new aiu_spdif_encode_mux = 30 + SOC_DAPM_ENUM("SPDIF Buffer Src", aiu_spdif_encode_sel_enum); 31 + 32 + static const struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = { 33 + SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0, 34 + &aiu_spdif_encode_mux), 35 + }; 36 + 37 + static const struct snd_soc_dapm_route aiu_cpu_dapm_routes[] = { 38 + { "I2S Encoder Playback", NULL, "I2S FIFO Playback" }, 39 + { "SPDIF SRC SEL", "SPDIF", "SPDIF FIFO Playback" }, 40 + { "SPDIF SRC SEL", "I2S", "I2S FIFO Playback" }, 41 + { "SPDIF Encoder Playback", NULL, "SPDIF SRC SEL" }, 42 + }; 43 + 44 + int aiu_of_xlate_dai_name(struct snd_soc_component *component, 45 + struct of_phandle_args *args, 46 + const char **dai_name, 47 + unsigned int component_id) 48 + { 49 + struct snd_soc_dai *dai; 50 + int id; 51 + 52 + if (args->args_count != 2) 53 + return -EINVAL; 54 + 55 + if (args->args[0] != component_id) 56 + return -EINVAL; 57 + 58 + id = args->args[1]; 59 + 60 + if (id < 0 || id >= component->num_dai) 61 + return -EINVAL; 62 + 63 + for_each_component_dais(component, dai) { 64 + if (id == 0) 65 + break; 66 + id--; 67 + } 68 + 69 + *dai_name = dai->driver->name; 70 + 71 + return 0; 72 + } 73 + 74 + static int aiu_cpu_of_xlate_dai_name(struct snd_soc_component *component, 75 + struct of_phandle_args *args, 76 + const char **dai_name) 77 + { 78 + return aiu_of_xlate_dai_name(component, args, dai_name, AIU_CPU); 79 + } 80 + 81 + static int aiu_cpu_component_probe(struct snd_soc_component *component) 82 + { 83 + struct aiu *aiu = snd_soc_component_get_drvdata(component); 84 + 85 + /* Required for the SPDIF Source control operation */ 86 + return clk_prepare_enable(aiu->i2s.clks[PCLK].clk); 87 + } 88 + 89 + static void aiu_cpu_component_remove(struct snd_soc_component *component) 90 + { 91 + struct aiu *aiu = snd_soc_component_get_drvdata(component); 92 + 93 + clk_disable_unprepare(aiu->i2s.clks[PCLK].clk); 94 + } 95 + 96 + static const struct snd_soc_component_driver aiu_cpu_component = { 97 + .name = "AIU CPU", 98 + .dapm_widgets = aiu_cpu_dapm_widgets, 99 + .num_dapm_widgets = ARRAY_SIZE(aiu_cpu_dapm_widgets), 100 + .dapm_routes = aiu_cpu_dapm_routes, 101 + .num_dapm_routes = ARRAY_SIZE(aiu_cpu_dapm_routes), 102 + .of_xlate_dai_name = aiu_cpu_of_xlate_dai_name, 103 + .pointer = aiu_fifo_pointer, 104 + .probe = aiu_cpu_component_probe, 105 + .remove = aiu_cpu_component_remove, 106 + }; 107 + 108 + static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = { 109 + [CPU_I2S_FIFO] = { 110 + .name = "I2S FIFO", 111 + .playback = { 112 + .stream_name = "I2S FIFO Playback", 113 + .channels_min = 2, 114 + .channels_max = 8, 115 + .rates = SNDRV_PCM_RATE_CONTINUOUS, 116 + .rate_min = 5512, 117 + .rate_max = 192000, 118 + .formats = AIU_FORMATS, 119 + }, 120 + .ops = &aiu_fifo_i2s_dai_ops, 121 + .pcm_new = aiu_fifo_pcm_new, 122 + .probe = aiu_fifo_i2s_dai_probe, 123 + .remove = aiu_fifo_dai_remove, 124 + }, 125 + [CPU_SPDIF_FIFO] = { 126 + .name = "SPDIF FIFO", 127 + .playback = { 128 + .stream_name = "SPDIF FIFO Playback", 129 + .channels_min = 2, 130 + .channels_max = 2, 131 + .rates = SNDRV_PCM_RATE_CONTINUOUS, 132 + .rate_min = 5512, 133 + .rate_max = 192000, 134 + .formats = AIU_FORMATS, 135 + }, 136 + .ops = &aiu_fifo_spdif_dai_ops, 137 + .pcm_new = aiu_fifo_pcm_new, 138 + .probe = aiu_fifo_spdif_dai_probe, 139 + .remove = aiu_fifo_dai_remove, 140 + }, 141 + [CPU_I2S_ENCODER] = { 142 + .name = "I2S Encoder", 143 + .playback = { 144 + .stream_name = "I2S Encoder Playback", 145 + .channels_min = 2, 146 + .channels_max = 8, 147 + .rates = SNDRV_PCM_RATE_8000_192000, 148 + .formats = AIU_FORMATS, 149 + }, 150 + .ops = &aiu_encoder_i2s_dai_ops, 151 + }, 152 + [CPU_SPDIF_ENCODER] = { 153 + .name = "SPDIF Encoder", 154 + .playback = { 155 + .stream_name = "SPDIF Encoder Playback", 156 + .channels_min = 2, 157 + .channels_max = 2, 158 + .rates = (SNDRV_PCM_RATE_32000 | 159 + SNDRV_PCM_RATE_44100 | 160 + SNDRV_PCM_RATE_48000 | 161 + SNDRV_PCM_RATE_88200 | 162 + SNDRV_PCM_RATE_96000 | 163 + SNDRV_PCM_RATE_176400 | 164 + SNDRV_PCM_RATE_192000), 165 + .formats = AIU_FORMATS, 166 + }, 167 + .ops = &aiu_encoder_spdif_dai_ops, 168 + } 169 + }; 170 + 171 + static const struct regmap_config aiu_regmap_cfg = { 172 + .reg_bits = 32, 173 + .val_bits = 32, 174 + .reg_stride = 4, 175 + .max_register = 0x2ac, 176 + }; 177 + 178 + static int aiu_clk_bulk_get(struct device *dev, 179 + const char * const *ids, 180 + unsigned int num, 181 + struct aiu_interface *interface) 182 + { 183 + struct clk_bulk_data *clks; 184 + int i, ret; 185 + 186 + clks = devm_kcalloc(dev, num, sizeof(clks), GFP_KERNEL); 187 + if (!clks) 188 + return -ENOMEM; 189 + 190 + for (i = 0; i < num; i++) 191 + clks[i].id = ids[i]; 192 + 193 + ret = devm_clk_bulk_get(dev, num, clks); 194 + if (ret < 0) 195 + return ret; 196 + 197 + interface->clks = clks; 198 + interface->clk_num = num; 199 + return 0; 200 + } 201 + 202 + static const char * const aiu_i2s_ids[] = { 203 + [PCLK] = "i2s_pclk", 204 + [AOCLK] = "i2s_aoclk", 205 + [MCLK] = "i2s_mclk", 206 + [MIXER] = "i2s_mixer", 207 + }; 208 + 209 + static const char * const aiu_spdif_ids[] = { 210 + [PCLK] = "spdif_pclk", 211 + [AOCLK] = "spdif_aoclk", 212 + [MCLK] = "spdif_mclk_sel" 213 + }; 214 + 215 + static int aiu_clk_get(struct device *dev) 216 + { 217 + struct aiu *aiu = dev_get_drvdata(dev); 218 + int ret; 219 + 220 + aiu->pclk = devm_clk_get(dev, "pclk"); 221 + if (IS_ERR(aiu->pclk)) { 222 + if (PTR_ERR(aiu->pclk) != -EPROBE_DEFER) 223 + dev_err(dev, "Can't get the aiu pclk\n"); 224 + return PTR_ERR(aiu->pclk); 225 + } 226 + 227 + aiu->spdif_mclk = devm_clk_get(dev, "spdif_mclk"); 228 + if (IS_ERR(aiu->spdif_mclk)) { 229 + if (PTR_ERR(aiu->spdif_mclk) != -EPROBE_DEFER) 230 + dev_err(dev, "Can't get the aiu spdif master clock\n"); 231 + return PTR_ERR(aiu->spdif_mclk); 232 + } 233 + 234 + ret = aiu_clk_bulk_get(dev, aiu_i2s_ids, ARRAY_SIZE(aiu_i2s_ids), 235 + &aiu->i2s); 236 + if (ret) { 237 + if (ret != -EPROBE_DEFER) 238 + dev_err(dev, "Can't get the i2s clocks\n"); 239 + return ret; 240 + } 241 + 242 + ret = aiu_clk_bulk_get(dev, aiu_spdif_ids, ARRAY_SIZE(aiu_spdif_ids), 243 + &aiu->spdif); 244 + if (ret) { 245 + if (ret != -EPROBE_DEFER) 246 + dev_err(dev, "Can't get the spdif clocks\n"); 247 + return ret; 248 + } 249 + 250 + ret = clk_prepare_enable(aiu->pclk); 251 + if (ret) { 252 + dev_err(dev, "peripheral clock enable failed\n"); 253 + return ret; 254 + } 255 + 256 + ret = devm_add_action_or_reset(dev, 257 + (void(*)(void *))clk_disable_unprepare, 258 + aiu->pclk); 259 + if (ret) 260 + dev_err(dev, "failed to add reset action on pclk"); 261 + 262 + return ret; 263 + } 264 + 265 + static int aiu_probe(struct platform_device *pdev) 266 + { 267 + struct device *dev = &pdev->dev; 268 + void __iomem *regs; 269 + struct regmap *map; 270 + struct aiu *aiu; 271 + int ret; 272 + 273 + aiu = devm_kzalloc(dev, sizeof(*aiu), GFP_KERNEL); 274 + if (!aiu) 275 + return -ENOMEM; 276 + platform_set_drvdata(pdev, aiu); 277 + 278 + ret = device_reset(dev); 279 + if (ret) { 280 + if (ret != -EPROBE_DEFER) 281 + dev_err(dev, "Failed to reset device\n"); 282 + return ret; 283 + } 284 + 285 + regs = devm_platform_ioremap_resource(pdev, 0); 286 + if (IS_ERR(regs)) 287 + return PTR_ERR(regs); 288 + 289 + map = devm_regmap_init_mmio(dev, regs, &aiu_regmap_cfg); 290 + if (IS_ERR(map)) { 291 + dev_err(dev, "failed to init regmap: %ld\n", 292 + PTR_ERR(map)); 293 + return PTR_ERR(map); 294 + } 295 + 296 + aiu->i2s.irq = platform_get_irq_byname(pdev, "i2s"); 297 + if (aiu->i2s.irq < 0) { 298 + dev_err(dev, "Can't get i2s irq\n"); 299 + return aiu->i2s.irq; 300 + } 301 + 302 + aiu->spdif.irq = platform_get_irq_byname(pdev, "spdif"); 303 + if (aiu->spdif.irq < 0) { 304 + dev_err(dev, "Can't get spdif irq\n"); 305 + return aiu->spdif.irq; 306 + } 307 + 308 + ret = aiu_clk_get(dev); 309 + if (ret) 310 + return ret; 311 + 312 + /* Register the cpu component of the aiu */ 313 + ret = snd_soc_register_component(dev, &aiu_cpu_component, 314 + aiu_cpu_dai_drv, 315 + ARRAY_SIZE(aiu_cpu_dai_drv)); 316 + if (ret) 317 + dev_err(dev, "Failed to register cpu component\n"); 318 + 319 + return ret; 320 + } 321 + 322 + static int aiu_remove(struct platform_device *pdev) 323 + { 324 + snd_soc_unregister_component(&pdev->dev); 325 + 326 + return 0; 327 + } 328 + 329 + static const struct of_device_id aiu_of_match[] = { 330 + { .compatible = "amlogic,aiu-gxbb", }, 331 + { .compatible = "amlogic,aiu-gxl", }, 332 + {} 333 + }; 334 + MODULE_DEVICE_TABLE(of, aiu_of_match); 335 + 336 + static struct platform_driver aiu_pdrv = { 337 + .probe = aiu_probe, 338 + .remove = aiu_remove, 339 + .driver = { 340 + .name = "meson-aiu", 341 + .of_match_table = aiu_of_match, 342 + }, 343 + }; 344 + module_platform_driver(aiu_pdrv); 345 + 346 + MODULE_DESCRIPTION("Meson AIU Driver"); 347 + MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 348 + MODULE_LICENSE("GPL v2");
+82
sound/soc/meson/aiu.h
··· 1 + /* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ 2 + /* 3 + * Copyright (c) 2018 BayLibre, SAS. 4 + * Author: Jerome Brunet <jbrunet@baylibre.com> 5 + */ 6 + 7 + #ifndef _MESON_AIU_H 8 + #define _MESON_AIU_H 9 + 10 + struct clk; 11 + struct clk_bulk_data; 12 + struct device; 13 + struct of_phandle_args; 14 + struct snd_soc_component_driver; 15 + struct snd_soc_dai; 16 + struct snd_soc_dai_driver; 17 + struct snd_soc_dai_ops; 18 + 19 + enum aiu_clk_ids { 20 + PCLK = 0, 21 + AOCLK, 22 + MCLK, 23 + MIXER 24 + }; 25 + 26 + struct aiu_interface { 27 + struct clk_bulk_data *clks; 28 + unsigned int clk_num; 29 + unsigned int irq; 30 + }; 31 + 32 + struct aiu { 33 + struct clk *pclk; 34 + struct clk *spdif_mclk; 35 + struct aiu_interface i2s; 36 + struct aiu_interface spdif; 37 + }; 38 + 39 + #define AIU_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ 40 + SNDRV_PCM_FMTBIT_S20_LE | \ 41 + SNDRV_PCM_FMTBIT_S24_LE) 42 + 43 + int aiu_of_xlate_dai_name(struct snd_soc_component *component, 44 + struct of_phandle_args *args, 45 + const char **dai_name, 46 + unsigned int component_id); 47 + 48 + int aiu_fifo_i2s_dai_probe(struct snd_soc_dai *dai); 49 + int aiu_fifo_spdif_dai_probe(struct snd_soc_dai *dai); 50 + 51 + extern const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops; 52 + extern const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops; 53 + extern const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops; 54 + extern const struct snd_soc_dai_ops aiu_encoder_spdif_dai_ops; 55 + 56 + #define AIU_IEC958_BPF 0x000 57 + #define AIU_958_MISC 0x010 58 + #define AIU_IEC958_DCU_FF_CTRL 0x01c 59 + #define AIU_958_CHSTAT_L0 0x020 60 + #define AIU_958_CHSTAT_L1 0x024 61 + #define AIU_958_CTRL 0x028 62 + #define AIU_I2S_SOURCE_DESC 0x034 63 + #define AIU_I2S_DAC_CFG 0x040 64 + #define AIU_I2S_SYNC 0x044 65 + #define AIU_I2S_MISC 0x048 66 + #define AIU_RST_SOFT 0x054 67 + #define AIU_CLK_CTRL 0x058 68 + #define AIU_CLK_CTRL_MORE 0x064 69 + #define AIU_CODEC_DAC_LRCLK_CTRL 0x0a0 70 + #define AIU_HDMI_CLK_DATA_CTRL 0x0a8 71 + #define AIU_ACODEC_CTRL 0x0b0 72 + #define AIU_958_CHSTAT_R0 0x0c0 73 + #define AIU_958_CHSTAT_R1 0x0c4 74 + #define AIU_MEM_I2S_START 0x180 75 + #define AIU_MEM_I2S_MASKS 0x18c 76 + #define AIU_MEM_I2S_CONTROL 0x190 77 + #define AIU_MEM_IEC958_START 0x194 78 + #define AIU_MEM_IEC958_CONTROL 0x1a4 79 + #define AIU_MEM_I2S_BUF_CNTL 0x1d8 80 + #define AIU_MEM_IEC958_BUF_CNTL 0x1fc 81 + 82 + #endif /* _MESON_AIU_H */