xref: /linux/sound/soc/meson/aiu-encoder-i2s.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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 
aiu_encoder_i2s_divider_enable(struct snd_soc_component * component,bool enable)30 static 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 
aiu_encoder_i2s_setup_desc(struct snd_soc_component * component,struct snd_pcm_hw_params * params)38 static 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 
aiu_encoder_i2s_set_legacy_div(struct snd_soc_component * component,struct snd_pcm_hw_params * params,unsigned int bs)81 static 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 
aiu_encoder_i2s_set_more_div(struct snd_soc_component * component,struct snd_pcm_hw_params * params,unsigned int bs)111 static 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 
aiu_encoder_i2s_set_clocks(struct snd_soc_component * component,struct snd_pcm_hw_params * params)144 static 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 
aiu_encoder_i2s_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)187 static 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 
aiu_encoder_i2s_hw_free(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)214 static 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 
aiu_encoder_i2s_set_fmt(struct snd_soc_dai * dai,unsigned int fmt)224 static 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_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP)
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 
aiu_encoder_i2s_set_sysclk(struct snd_soc_dai * dai,int clk_id,unsigned int freq,int dir)267 static 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 
286 static const unsigned int hw_channels[] = {2, 8};
287 static 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 
aiu_encoder_i2s_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)293 static 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 
aiu_encoder_i2s_shutdown(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)315 static 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 
323 const 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 
332