Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/meson/aiu-encoder-i2s.c
29266 views
1
// SPDX-License-Identifier: GPL-2.0
2
//
3
// Copyright (c) 2020 BayLibre, SAS.
4
// Author: Jerome Brunet <[email protected]>
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
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
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
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
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
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
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
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
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
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
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
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
333