aiu-encoder-i2s.c (8723B)
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_CLK_CTRL_I2S_DIV_EN BIT(0) 22#define AIU_CLK_CTRL_I2S_DIV GENMASK(3, 2) 23#define AIU_CLK_CTRL_AOCLK_INVERT BIT(6) 24#define AIU_CLK_CTRL_LRCLK_INVERT BIT(7) 25#define AIU_CLK_CTRL_LRCLK_SKEW GENMASK(9, 8) 26#define AIU_CLK_CTRL_MORE_HDMI_AMCLK BIT(6) 27#define AIU_CLK_CTRL_MORE_I2S_DIV GENMASK(5, 0) 28#define AIU_CODEC_DAC_LRCLK_CTRL_DIV GENMASK(11, 0) 29 30static void aiu_encoder_i2s_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_I2S_DIV_EN, 35 enable ? AIU_CLK_CTRL_I2S_DIV_EN : 0); 36} 37 38static int aiu_encoder_i2s_setup_desc(struct snd_soc_component *component, 39 struct snd_pcm_hw_params *params) 40{ 41 /* Always operate in split (classic interleaved) mode */ 42 unsigned int desc = AIU_I2S_SOURCE_DESC_MODE_SPLIT; 43 44 /* Reset required to update the pipeline */ 45 snd_soc_component_write(component, AIU_RST_SOFT, AIU_RST_SOFT_I2S_FAST); 46 snd_soc_component_read(component, AIU_I2S_SYNC); 47 48 switch (params_physical_width(params)) { 49 case 16: /* Nothing to do */ 50 break; 51 52 case 32: 53 desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT | 54 AIU_I2S_SOURCE_DESC_MODE_32BIT); 55 break; 56 57 default: 58 return -EINVAL; 59 } 60 61 switch (params_channels(params)) { 62 case 2: /* Nothing to do */ 63 break; 64 case 8: 65 desc |= AIU_I2S_SOURCE_DESC_MODE_8CH; 66 break; 67 default: 68 return -EINVAL; 69 } 70 71 snd_soc_component_update_bits(component, AIU_I2S_SOURCE_DESC, 72 AIU_I2S_SOURCE_DESC_MODE_8CH | 73 AIU_I2S_SOURCE_DESC_MODE_24BIT | 74 AIU_I2S_SOURCE_DESC_MODE_32BIT | 75 AIU_I2S_SOURCE_DESC_MODE_SPLIT, 76 desc); 77 78 return 0; 79} 80 81static int aiu_encoder_i2s_set_legacy_div(struct snd_soc_component *component, 82 struct snd_pcm_hw_params *params, 83 unsigned int bs) 84{ 85 switch (bs) { 86 case 1: 87 case 2: 88 case 4: 89 case 8: 90 /* These are the only valid legacy dividers */ 91 break; 92 93 default: 94 dev_err(component->dev, "Unsupported i2s divider: %u\n", bs); 95 return -EINVAL; 96 } 97 98 snd_soc_component_update_bits(component, AIU_CLK_CTRL, 99 AIU_CLK_CTRL_I2S_DIV, 100 FIELD_PREP(AIU_CLK_CTRL_I2S_DIV, 101 __ffs(bs))); 102 103 snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE, 104 AIU_CLK_CTRL_MORE_I2S_DIV, 105 FIELD_PREP(AIU_CLK_CTRL_MORE_I2S_DIV, 106 0)); 107 108 return 0; 109} 110 111static int aiu_encoder_i2s_set_more_div(struct snd_soc_component *component, 112 struct snd_pcm_hw_params *params, 113 unsigned int bs) 114{ 115 /* 116 * NOTE: this HW is odd. 117 * In most configuration, the i2s divider is 'mclk / blck'. 118 * However, in 16 bits - 8ch mode, this factor needs to be 119 * increased by 50% to get the correct output rate. 120 * No idea why ! 121 */ 122 if (params_width(params) == 16 && params_channels(params) == 8) { 123 if (bs % 2) { 124 dev_err(component->dev, 125 "Cannot increase i2s divider by 50%%\n"); 126 return -EINVAL; 127 } 128 bs += bs / 2; 129 } 130 131 /* Use CLK_MORE for mclk to bclk divider */ 132 snd_soc_component_update_bits(component, AIU_CLK_CTRL, 133 AIU_CLK_CTRL_I2S_DIV, 134 FIELD_PREP(AIU_CLK_CTRL_I2S_DIV, 0)); 135 136 snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE, 137 AIU_CLK_CTRL_MORE_I2S_DIV, 138 FIELD_PREP(AIU_CLK_CTRL_MORE_I2S_DIV, 139 bs - 1)); 140 141 return 0; 142} 143 144static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component, 145 struct snd_pcm_hw_params *params) 146{ 147 struct aiu *aiu = snd_soc_component_get_drvdata(component); 148 unsigned int srate = params_rate(params); 149 unsigned int fs, bs; 150 int ret; 151 152 /* Get the oversampling factor */ 153 fs = DIV_ROUND_CLOSEST(clk_get_rate(aiu->i2s.clks[MCLK].clk), srate); 154 155 if (fs % 64) 156 return -EINVAL; 157 158 /* Send data MSB first */ 159 snd_soc_component_update_bits(component, AIU_I2S_DAC_CFG, 160 AIU_I2S_DAC_CFG_MSB_FIRST, 161 AIU_I2S_DAC_CFG_MSB_FIRST); 162 163 /* Set bclk to lrlck ratio */ 164 snd_soc_component_update_bits(component, AIU_CODEC_DAC_LRCLK_CTRL, 165 AIU_CODEC_DAC_LRCLK_CTRL_DIV, 166 FIELD_PREP(AIU_CODEC_DAC_LRCLK_CTRL_DIV, 167 64 - 1)); 168 169 bs = fs / 64; 170 171 if (aiu->platform->has_clk_ctrl_more_i2s_div) 172 ret = aiu_encoder_i2s_set_more_div(component, params, bs); 173 else 174 ret = aiu_encoder_i2s_set_legacy_div(component, params, bs); 175 176 if (ret) 177 return ret; 178 179 /* Make sure amclk is used for HDMI i2s as well */ 180 snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE, 181 AIU_CLK_CTRL_MORE_HDMI_AMCLK, 182 AIU_CLK_CTRL_MORE_HDMI_AMCLK); 183 184 return 0; 185} 186 187static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream, 188 struct snd_pcm_hw_params *params, 189 struct snd_soc_dai *dai) 190{ 191 struct snd_soc_component *component = dai->component; 192 int ret; 193 194 /* Disable the clock while changing the settings */ 195 aiu_encoder_i2s_divider_enable(component, false); 196 197 ret = aiu_encoder_i2s_setup_desc(component, params); 198 if (ret) { 199 dev_err(dai->dev, "setting i2s desc failed\n"); 200 return ret; 201 } 202 203 ret = aiu_encoder_i2s_set_clocks(component, params); 204 if (ret) { 205 dev_err(dai->dev, "setting i2s clocks failed\n"); 206 return ret; 207 } 208 209 aiu_encoder_i2s_divider_enable(component, true); 210 211 return 0; 212} 213 214static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream, 215 struct snd_soc_dai *dai) 216{ 217 struct snd_soc_component *component = dai->component; 218 219 aiu_encoder_i2s_divider_enable(component, false); 220 221 return 0; 222} 223 224static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 225{ 226 struct snd_soc_component *component = dai->component; 227 unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK; 228 unsigned int val = 0; 229 unsigned int skew; 230 231 /* Only CPU Master / Codec Slave supported ATM */ 232 if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) 233 return -EINVAL; 234 235 if (inv == SND_SOC_DAIFMT_NB_IF || 236 inv == SND_SOC_DAIFMT_IB_IF) 237 val |= AIU_CLK_CTRL_LRCLK_INVERT; 238 239 if (inv == SND_SOC_DAIFMT_IB_NF || 240 inv == SND_SOC_DAIFMT_IB_IF) 241 val |= AIU_CLK_CTRL_AOCLK_INVERT; 242 243 /* Signal skew */ 244 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 245 case SND_SOC_DAIFMT_I2S: 246 /* Invert sample clock for i2s */ 247 val ^= AIU_CLK_CTRL_LRCLK_INVERT; 248 skew = 1; 249 break; 250 case SND_SOC_DAIFMT_LEFT_J: 251 skew = 0; 252 break; 253 default: 254 return -EINVAL; 255 } 256 257 val |= FIELD_PREP(AIU_CLK_CTRL_LRCLK_SKEW, skew); 258 snd_soc_component_update_bits(component, AIU_CLK_CTRL, 259 AIU_CLK_CTRL_LRCLK_INVERT | 260 AIU_CLK_CTRL_AOCLK_INVERT | 261 AIU_CLK_CTRL_LRCLK_SKEW, 262 val); 263 264 return 0; 265} 266 267static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, 268 unsigned int freq, int dir) 269{ 270 struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); 271 int ret; 272 273 if (WARN_ON(clk_id != 0)) 274 return -EINVAL; 275 276 if (dir == SND_SOC_CLOCK_IN) 277 return 0; 278 279 ret = clk_set_rate(aiu->i2s.clks[MCLK].clk, freq); 280 if (ret) 281 dev_err(dai->dev, "Failed to set sysclk to %uHz", freq); 282 283 return ret; 284} 285 286static const unsigned int hw_channels[] = {2, 8}; 287static const struct snd_pcm_hw_constraint_list hw_channel_constraints = { 288 .list = hw_channels, 289 .count = ARRAY_SIZE(hw_channels), 290 .mask = 0, 291}; 292 293static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream, 294 struct snd_soc_dai *dai) 295{ 296 struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); 297 int ret; 298 299 /* Make sure the encoder gets either 2 or 8 channels */ 300 ret = snd_pcm_hw_constraint_list(substream->runtime, 0, 301 SNDRV_PCM_HW_PARAM_CHANNELS, 302 &hw_channel_constraints); 303 if (ret) { 304 dev_err(dai->dev, "adding channels constraints failed\n"); 305 return ret; 306 } 307 308 ret = clk_bulk_prepare_enable(aiu->i2s.clk_num, aiu->i2s.clks); 309 if (ret) 310 dev_err(dai->dev, "failed to enable i2s clocks\n"); 311 312 return ret; 313} 314 315static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream, 316 struct snd_soc_dai *dai) 317{ 318 struct aiu *aiu = snd_soc_component_get_drvdata(dai->component); 319 320 clk_bulk_disable_unprepare(aiu->i2s.clk_num, aiu->i2s.clks); 321} 322 323const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops = { 324 .hw_params = aiu_encoder_i2s_hw_params, 325 .hw_free = aiu_encoder_i2s_hw_free, 326 .set_fmt = aiu_encoder_i2s_set_fmt, 327 .set_sysclk = aiu_encoder_i2s_set_sysclk, 328 .startup = aiu_encoder_i2s_startup, 329 .shutdown = aiu_encoder_i2s_shutdown, 330}; 331