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_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
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
332