Commit | Line | Data |
---|---|---|
a912e80b | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
11bd3dd1 LPC |
2 | /* |
3 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> | |
11bd3dd1 LPC |
4 | */ |
5 | ||
b355ebeb | 6 | #include <linux/bitfield.h> |
dacc06b8 AM |
7 | #include <linux/clk.h> |
8 | #include <linux/delay.h> | |
9 | #include <linux/dma-mapping.h> | |
11bd3dd1 LPC |
10 | #include <linux/init.h> |
11 | #include <linux/io.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/module.h> | |
98356c89 | 14 | #include <linux/mod_devicetable.h> |
11bd3dd1 | 15 | #include <linux/platform_device.h> |
cf375e69 | 16 | #include <linux/regmap.h> |
11bd3dd1 LPC |
17 | #include <linux/slab.h> |
18 | ||
11bd3dd1 LPC |
19 | #include <sound/core.h> |
20 | #include <sound/pcm.h> | |
21 | #include <sound/pcm_params.h> | |
22 | #include <sound/soc.h> | |
11bd3dd1 | 23 | #include <sound/initval.h> |
0406a40a LPC |
24 | #include <sound/dmaengine_pcm.h> |
25 | ||
11bd3dd1 LPC |
26 | #define JZ_REG_AIC_CONF 0x00 |
27 | #define JZ_REG_AIC_CTRL 0x04 | |
28 | #define JZ_REG_AIC_I2S_FMT 0x10 | |
29 | #define JZ_REG_AIC_FIFO_STATUS 0x14 | |
30 | #define JZ_REG_AIC_I2S_STATUS 0x1c | |
31 | #define JZ_REG_AIC_CLK_DIV 0x30 | |
32 | #define JZ_REG_AIC_FIFO 0x34 | |
33 | ||
dacc06b8 AM |
34 | #define JZ_AIC_CONF_OVERFLOW_PLAY_LAST BIT(6) |
35 | #define JZ_AIC_CONF_INTERNAL_CODEC BIT(5) | |
36 | #define JZ_AIC_CONF_I2S BIT(4) | |
37 | #define JZ_AIC_CONF_RESET BIT(3) | |
38 | #define JZ_AIC_CONF_BIT_CLK_MASTER BIT(2) | |
39 | #define JZ_AIC_CONF_SYNC_CLK_MASTER BIT(1) | |
40 | #define JZ_AIC_CONF_ENABLE BIT(0) | |
41 | ||
42 | #define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE GENMASK(21, 19) | |
43 | #define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE GENMASK(18, 16) | |
44 | #define JZ_AIC_CTRL_ENABLE_RX_DMA BIT(15) | |
45 | #define JZ_AIC_CTRL_ENABLE_TX_DMA BIT(14) | |
46 | #define JZ_AIC_CTRL_MONO_TO_STEREO BIT(11) | |
47 | #define JZ_AIC_CTRL_SWITCH_ENDIANNESS BIT(10) | |
48 | #define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED BIT(9) | |
8b3a9ad8 AM |
49 | #define JZ_AIC_CTRL_TFLUSH BIT(8) |
50 | #define JZ_AIC_CTRL_RFLUSH BIT(7) | |
dacc06b8 AM |
51 | #define JZ_AIC_CTRL_ENABLE_ROR_INT BIT(6) |
52 | #define JZ_AIC_CTRL_ENABLE_TUR_INT BIT(5) | |
53 | #define JZ_AIC_CTRL_ENABLE_RFS_INT BIT(4) | |
54 | #define JZ_AIC_CTRL_ENABLE_TFS_INT BIT(3) | |
55 | #define JZ_AIC_CTRL_ENABLE_LOOPBACK BIT(2) | |
56 | #define JZ_AIC_CTRL_ENABLE_PLAYBACK BIT(1) | |
57 | #define JZ_AIC_CTRL_ENABLE_CAPTURE BIT(0) | |
58 | ||
59 | #define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12) | |
60 | #define JZ_AIC_I2S_FMT_DISABLE_BIT_ICLK BIT(13) | |
61 | #define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4) | |
62 | #define JZ_AIC_I2S_FMT_MSB BIT(0) | |
63 | ||
64 | #define JZ_AIC_I2S_STATUS_BUSY BIT(2) | |
11bd3dd1 | 65 | |
62f9ed5f | 66 | struct i2s_soc_info { |
62f9ed5f | 67 | struct snd_soc_dai_driver *dai; |
8b3a9ad8 | 68 | |
0fddb4bc AM |
69 | struct reg_field field_rx_fifo_thresh; |
70 | struct reg_field field_tx_fifo_thresh; | |
71 | struct reg_field field_i2sdiv_capture; | |
72 | struct reg_field field_i2sdiv_playback; | |
73 | ||
8b3a9ad8 | 74 | bool shared_fifo_flush; |
62f9ed5f PC |
75 | }; |
76 | ||
11bd3dd1 | 77 | struct jz4740_i2s { |
cf375e69 | 78 | struct regmap *regmap; |
11bd3dd1 | 79 | |
0fddb4bc AM |
80 | struct regmap_field *field_rx_fifo_thresh; |
81 | struct regmap_field *field_tx_fifo_thresh; | |
82 | struct regmap_field *field_i2sdiv_capture; | |
83 | struct regmap_field *field_i2sdiv_playback; | |
84 | ||
11bd3dd1 LPC |
85 | struct clk *clk_aic; |
86 | struct clk *clk_i2s; | |
87 | ||
0406a40a LPC |
88 | struct snd_dmaengine_dai_dma_data playback_dma_data; |
89 | struct snd_dmaengine_dai_dma_data capture_dma_data; | |
967beb2e | 90 | |
62f9ed5f | 91 | const struct i2s_soc_info *soc_info; |
11bd3dd1 LPC |
92 | }; |
93 | ||
11bd3dd1 LPC |
94 | static int jz4740_i2s_startup(struct snd_pcm_substream *substream, |
95 | struct snd_soc_dai *dai) | |
96 | { | |
f0fba2ad | 97 | struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
eae37345 | 98 | int ret; |
11bd3dd1 | 99 | |
8b3a9ad8 AM |
100 | /* |
101 | * When we can flush FIFOs independently, only flush the FIFO | |
102 | * that is starting up. We can do this when the DAI is active | |
103 | * because it does not disturb other active substreams. | |
104 | */ | |
105 | if (!i2s->soc_info->shared_fifo_flush) { | |
106 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
cf375e69 | 107 | regmap_set_bits(i2s->regmap, JZ_REG_AIC_CTRL, JZ_AIC_CTRL_TFLUSH); |
8b3a9ad8 | 108 | else |
cf375e69 | 109 | regmap_set_bits(i2s->regmap, JZ_REG_AIC_CTRL, JZ_AIC_CTRL_RFLUSH); |
8b3a9ad8 AM |
110 | } |
111 | ||
48afb287 | 112 | if (snd_soc_dai_active(dai)) |
11bd3dd1 LPC |
113 | return 0; |
114 | ||
8b3a9ad8 AM |
115 | /* |
116 | * When there is a shared flush bit for both FIFOs, the TFLUSH | |
117 | * bit flushes both FIFOs. Flushing while the DAI is active would | |
118 | * cause FIFO underruns in other active substreams so we have to | |
119 | * guard this behind the snd_soc_dai_active() check. | |
120 | */ | |
121 | if (i2s->soc_info->shared_fifo_flush) | |
cf375e69 | 122 | regmap_set_bits(i2s->regmap, JZ_REG_AIC_CTRL, JZ_AIC_CTRL_TFLUSH); |
11bd3dd1 | 123 | |
eae37345 AY |
124 | ret = clk_prepare_enable(i2s->clk_i2s); |
125 | if (ret) | |
126 | return ret; | |
11bd3dd1 | 127 | |
cf375e69 | 128 | regmap_set_bits(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE); |
11bd3dd1 LPC |
129 | return 0; |
130 | } | |
131 | ||
132 | static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream, | |
133 | struct snd_soc_dai *dai) | |
134 | { | |
f0fba2ad | 135 | struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
11bd3dd1 | 136 | |
48afb287 | 137 | if (snd_soc_dai_active(dai)) |
11bd3dd1 LPC |
138 | return; |
139 | ||
cf375e69 | 140 | regmap_clear_bits(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE); |
11bd3dd1 | 141 | |
010187fb | 142 | clk_disable_unprepare(i2s->clk_i2s); |
11bd3dd1 LPC |
143 | } |
144 | ||
145 | static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | |
146 | struct snd_soc_dai *dai) | |
147 | { | |
f0fba2ad | 148 | struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
11bd3dd1 LPC |
149 | uint32_t mask; |
150 | ||
151 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
152 | mask = JZ_AIC_CTRL_ENABLE_PLAYBACK | JZ_AIC_CTRL_ENABLE_TX_DMA; | |
153 | else | |
154 | mask = JZ_AIC_CTRL_ENABLE_CAPTURE | JZ_AIC_CTRL_ENABLE_RX_DMA; | |
155 | ||
11bd3dd1 LPC |
156 | switch (cmd) { |
157 | case SNDRV_PCM_TRIGGER_START: | |
158 | case SNDRV_PCM_TRIGGER_RESUME: | |
159 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
cf375e69 | 160 | regmap_set_bits(i2s->regmap, JZ_REG_AIC_CTRL, mask); |
11bd3dd1 LPC |
161 | break; |
162 | case SNDRV_PCM_TRIGGER_STOP: | |
163 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
164 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
cf375e69 | 165 | regmap_clear_bits(i2s->regmap, JZ_REG_AIC_CTRL, mask); |
11bd3dd1 LPC |
166 | break; |
167 | default: | |
168 | return -EINVAL; | |
169 | } | |
170 | ||
11bd3dd1 LPC |
171 | return 0; |
172 | } | |
173 | ||
174 | static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) | |
175 | { | |
f0fba2ad | 176 | struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
cf375e69 AM |
177 | const unsigned int conf_mask = JZ_AIC_CONF_BIT_CLK_MASTER | |
178 | JZ_AIC_CONF_SYNC_CLK_MASTER; | |
179 | unsigned int conf = 0, format = 0; | |
11bd3dd1 | 180 | |
cbb3a19f CK |
181 | switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { |
182 | case SND_SOC_DAIFMT_BP_FP: | |
11bd3dd1 LPC |
183 | conf |= JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER; |
184 | format |= JZ_AIC_I2S_FMT_ENABLE_SYS_CLK; | |
185 | break; | |
cbb3a19f | 186 | case SND_SOC_DAIFMT_BC_FP: |
11bd3dd1 LPC |
187 | conf |= JZ_AIC_CONF_SYNC_CLK_MASTER; |
188 | break; | |
cbb3a19f | 189 | case SND_SOC_DAIFMT_BP_FC: |
11bd3dd1 LPC |
190 | conf |= JZ_AIC_CONF_BIT_CLK_MASTER; |
191 | break; | |
cbb3a19f | 192 | case SND_SOC_DAIFMT_BC_FC: |
11bd3dd1 LPC |
193 | break; |
194 | default: | |
195 | return -EINVAL; | |
196 | } | |
197 | ||
198 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | |
199 | case SND_SOC_DAIFMT_MSB: | |
200 | format |= JZ_AIC_I2S_FMT_MSB; | |
201 | break; | |
202 | case SND_SOC_DAIFMT_I2S: | |
203 | break; | |
204 | default: | |
205 | return -EINVAL; | |
206 | } | |
207 | ||
208 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | |
209 | case SND_SOC_DAIFMT_NB_NF: | |
210 | break; | |
211 | default: | |
212 | return -EINVAL; | |
213 | } | |
214 | ||
cf375e69 AM |
215 | regmap_update_bits(i2s->regmap, JZ_REG_AIC_CONF, conf_mask, conf); |
216 | regmap_write(i2s->regmap, JZ_REG_AIC_I2S_FMT, format); | |
11bd3dd1 LPC |
217 | |
218 | return 0; | |
219 | } | |
220 | ||
ad721bc9 AM |
221 | static int jz4740_i2s_get_i2sdiv(unsigned long mclk, unsigned long rate, |
222 | unsigned long i2sdiv_max) | |
223 | { | |
224 | unsigned long div, rate1, rate2, err1, err2; | |
225 | ||
226 | div = mclk / (64 * rate); | |
227 | if (div == 0) | |
228 | div = 1; | |
229 | ||
230 | rate1 = mclk / (64 * div); | |
231 | rate2 = mclk / (64 * (div + 1)); | |
232 | ||
233 | err1 = abs(rate1 - rate); | |
234 | err2 = abs(rate2 - rate); | |
235 | ||
236 | /* | |
237 | * Choose the divider that produces the smallest error in the | |
238 | * output rate and reject dividers with a 5% or higher error. | |
239 | * In the event that both dividers are outside the acceptable | |
240 | * error margin, reject the rate to prevent distorted audio. | |
241 | * (The number 5% is arbitrary.) | |
242 | */ | |
243 | if (div <= i2sdiv_max && err1 <= err2 && err1 < rate/20) | |
244 | return div; | |
245 | if (div < i2sdiv_max && err2 < rate/20) | |
246 | return div + 1; | |
247 | ||
248 | return -EINVAL; | |
249 | } | |
250 | ||
11bd3dd1 LPC |
251 | static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream, |
252 | struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) | |
253 | { | |
f0fba2ad | 254 | struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
0fddb4bc | 255 | struct regmap_field *div_field; |
ad721bc9 | 256 | unsigned long i2sdiv_max; |
11bd3dd1 | 257 | unsigned int sample_size; |
ad721bc9 AM |
258 | uint32_t ctrl, conf; |
259 | int div = 1; | |
11bd3dd1 | 260 | |
cf375e69 | 261 | regmap_read(i2s->regmap, JZ_REG_AIC_CTRL, &ctrl); |
ad721bc9 | 262 | regmap_read(i2s->regmap, JZ_REG_AIC_CONF, &conf); |
26b0aad8 | 263 | |
11bd3dd1 LPC |
264 | switch (params_format(params)) { |
265 | case SNDRV_PCM_FORMAT_S8: | |
266 | sample_size = 0; | |
11bd3dd1 | 267 | break; |
7abd01cf | 268 | case SNDRV_PCM_FORMAT_S16_LE: |
11bd3dd1 | 269 | sample_size = 1; |
11bd3dd1 | 270 | break; |
7abd01cf AM |
271 | case SNDRV_PCM_FORMAT_S20_LE: |
272 | sample_size = 3; | |
273 | break; | |
274 | case SNDRV_PCM_FORMAT_S24_LE: | |
275 | sample_size = 4; | |
276 | break; | |
11bd3dd1 LPC |
277 | default: |
278 | return -EINVAL; | |
279 | } | |
280 | ||
281 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | |
b355ebeb AM |
282 | ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE; |
283 | ctrl |= FIELD_PREP(JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE, sample_size); | |
284 | ||
11bd3dd1 LPC |
285 | if (params_channels(params) == 1) |
286 | ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO; | |
287 | else | |
288 | ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO; | |
967beb2e | 289 | |
0fddb4bc | 290 | div_field = i2s->field_i2sdiv_playback; |
ad721bc9 AM |
291 | i2sdiv_max = GENMASK(i2s->soc_info->field_i2sdiv_playback.msb, |
292 | i2s->soc_info->field_i2sdiv_playback.lsb); | |
11bd3dd1 | 293 | } else { |
b355ebeb AM |
294 | ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE; |
295 | ctrl |= FIELD_PREP(JZ_AIC_CTRL_INPUT_SAMPLE_SIZE, sample_size); | |
967beb2e | 296 | |
0fddb4bc | 297 | div_field = i2s->field_i2sdiv_capture; |
ad721bc9 AM |
298 | i2sdiv_max = GENMASK(i2s->soc_info->field_i2sdiv_capture.msb, |
299 | i2s->soc_info->field_i2sdiv_capture.lsb); | |
300 | } | |
301 | ||
302 | /* | |
303 | * Only calculate I2SDIV if we're supplying the bit or frame clock. | |
304 | * If the codec is supplying both clocks then the divider output is | |
305 | * unused, and we don't want it to limit the allowed sample rates. | |
306 | */ | |
307 | if (conf & (JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER)) { | |
308 | div = jz4740_i2s_get_i2sdiv(clk_get_rate(i2s->clk_i2s), | |
309 | params_rate(params), i2sdiv_max); | |
310 | if (div < 0) | |
311 | return div; | |
11bd3dd1 LPC |
312 | } |
313 | ||
cf375e69 | 314 | regmap_write(i2s->regmap, JZ_REG_AIC_CTRL, ctrl); |
0fddb4bc | 315 | regmap_field_write(div_field, div - 1); |
11bd3dd1 | 316 | |
11bd3dd1 LPC |
317 | return 0; |
318 | } | |
319 | ||
f0fba2ad LG |
320 | static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai) |
321 | { | |
322 | struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); | |
f0fba2ad | 323 | |
0406a40a LPC |
324 | snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, |
325 | &i2s->capture_dma_data); | |
f0fba2ad | 326 | |
f0fba2ad LG |
327 | return 0; |
328 | } | |
329 | ||
85e7652d | 330 | static const struct snd_soc_dai_ops jz4740_i2s_dai_ops = { |
cfacc4d8 | 331 | .probe = jz4740_i2s_dai_probe, |
11bd3dd1 LPC |
332 | .startup = jz4740_i2s_startup, |
333 | .shutdown = jz4740_i2s_shutdown, | |
334 | .trigger = jz4740_i2s_trigger, | |
335 | .hw_params = jz4740_i2s_hw_params, | |
1724cc38 | 336 | .set_fmt = jz4740_i2s_set_fmt, |
11bd3dd1 LPC |
337 | }; |
338 | ||
339 | #define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \ | |
7abd01cf AM |
340 | SNDRV_PCM_FMTBIT_S16_LE | \ |
341 | SNDRV_PCM_FMTBIT_S20_LE | \ | |
342 | SNDRV_PCM_FMTBIT_S24_LE) | |
11bd3dd1 | 343 | |
f0fba2ad | 344 | static struct snd_soc_dai_driver jz4740_i2s_dai = { |
11bd3dd1 LPC |
345 | .playback = { |
346 | .channels_min = 1, | |
347 | .channels_max = 2, | |
84a91434 | 348 | .rates = SNDRV_PCM_RATE_CONTINUOUS, |
11bd3dd1 LPC |
349 | .formats = JZ4740_I2S_FMTS, |
350 | }, | |
351 | .capture = { | |
352 | .channels_min = 2, | |
353 | .channels_max = 2, | |
84a91434 | 354 | .rates = SNDRV_PCM_RATE_CONTINUOUS, |
11bd3dd1 LPC |
355 | .formats = JZ4740_I2S_FMTS, |
356 | }, | |
0547dece | 357 | .symmetric_rate = 1, |
11bd3dd1 | 358 | .ops = &jz4740_i2s_dai_ops, |
11bd3dd1 | 359 | }; |
11bd3dd1 | 360 | |
62f9ed5f | 361 | static const struct i2s_soc_info jz4740_i2s_soc_info = { |
0fddb4bc AM |
362 | .dai = &jz4740_i2s_dai, |
363 | .field_rx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 12, 15), | |
364 | .field_tx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 8, 11), | |
365 | .field_i2sdiv_capture = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3), | |
366 | .field_i2sdiv_playback = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3), | |
367 | .shared_fifo_flush = true, | |
62f9ed5f PC |
368 | }; |
369 | ||
bde8ca7c | 370 | static const struct i2s_soc_info jz4760_i2s_soc_info = { |
0fddb4bc AM |
371 | .dai = &jz4740_i2s_dai, |
372 | .field_rx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 24, 27), | |
373 | .field_tx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 16, 20), | |
374 | .field_i2sdiv_capture = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3), | |
375 | .field_i2sdiv_playback = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3), | |
bde8ca7c PC |
376 | }; |
377 | ||
bb1b282d AM |
378 | static const struct i2s_soc_info x1000_i2s_soc_info = { |
379 | .dai = &jz4740_i2s_dai, | |
380 | .field_rx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 24, 27), | |
381 | .field_tx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 16, 20), | |
382 | .field_i2sdiv_capture = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 8), | |
383 | .field_i2sdiv_playback = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 8), | |
384 | }; | |
385 | ||
a3434a49 | 386 | static struct snd_soc_dai_driver jz4770_i2s_dai = { |
967beb2e ZLK |
387 | .playback = { |
388 | .channels_min = 1, | |
389 | .channels_max = 2, | |
84a91434 | 390 | .rates = SNDRV_PCM_RATE_CONTINUOUS, |
967beb2e ZLK |
391 | .formats = JZ4740_I2S_FMTS, |
392 | }, | |
393 | .capture = { | |
394 | .channels_min = 2, | |
395 | .channels_max = 2, | |
84a91434 | 396 | .rates = SNDRV_PCM_RATE_CONTINUOUS, |
967beb2e ZLK |
397 | .formats = JZ4740_I2S_FMTS, |
398 | }, | |
399 | .ops = &jz4740_i2s_dai_ops, | |
967beb2e ZLK |
400 | }; |
401 | ||
a3434a49 | 402 | static const struct i2s_soc_info jz4770_i2s_soc_info = { |
0fddb4bc AM |
403 | .dai = &jz4770_i2s_dai, |
404 | .field_rx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 24, 27), | |
405 | .field_tx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 16, 20), | |
406 | .field_i2sdiv_capture = REG_FIELD(JZ_REG_AIC_CLK_DIV, 8, 11), | |
407 | .field_i2sdiv_playback = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3), | |
a3434a49 PC |
408 | }; |
409 | ||
62f9ed5f | 410 | static const struct i2s_soc_info jz4780_i2s_soc_info = { |
0fddb4bc AM |
411 | .dai = &jz4770_i2s_dai, |
412 | .field_rx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 24, 27), | |
413 | .field_tx_fifo_thresh = REG_FIELD(JZ_REG_AIC_CONF, 16, 20), | |
414 | .field_i2sdiv_capture = REG_FIELD(JZ_REG_AIC_CLK_DIV, 8, 11), | |
415 | .field_i2sdiv_playback = REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3), | |
62f9ed5f PC |
416 | }; |
417 | ||
165afe6b AM |
418 | static int jz4740_i2s_suspend(struct snd_soc_component *component) |
419 | { | |
420 | struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component); | |
421 | ||
422 | if (snd_soc_component_active(component)) { | |
423 | regmap_clear_bits(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE); | |
424 | clk_disable_unprepare(i2s->clk_i2s); | |
425 | } | |
426 | ||
427 | clk_disable_unprepare(i2s->clk_aic); | |
428 | ||
429 | return 0; | |
430 | } | |
431 | ||
432 | static int jz4740_i2s_resume(struct snd_soc_component *component) | |
433 | { | |
434 | struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component); | |
435 | int ret; | |
436 | ||
437 | ret = clk_prepare_enable(i2s->clk_aic); | |
438 | if (ret) | |
439 | return ret; | |
440 | ||
441 | if (snd_soc_component_active(component)) { | |
442 | ret = clk_prepare_enable(i2s->clk_i2s); | |
443 | if (ret) { | |
444 | clk_disable_unprepare(i2s->clk_aic); | |
445 | return ret; | |
446 | } | |
447 | ||
448 | regmap_set_bits(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE); | |
449 | } | |
450 | ||
451 | return 0; | |
452 | } | |
453 | ||
4e02fd62 AM |
454 | static int jz4740_i2s_probe(struct snd_soc_component *component) |
455 | { | |
456 | struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component); | |
457 | int ret; | |
458 | ||
459 | ret = clk_prepare_enable(i2s->clk_aic); | |
460 | if (ret) | |
461 | return ret; | |
462 | ||
463 | regmap_write(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET); | |
464 | ||
465 | regmap_write(i2s->regmap, JZ_REG_AIC_CONF, | |
466 | JZ_AIC_CONF_OVERFLOW_PLAY_LAST | | |
467 | JZ_AIC_CONF_I2S | JZ_AIC_CONF_INTERNAL_CODEC); | |
468 | ||
469 | regmap_field_write(i2s->field_rx_fifo_thresh, 7); | |
470 | regmap_field_write(i2s->field_tx_fifo_thresh, 8); | |
471 | ||
472 | return 0; | |
473 | } | |
474 | ||
475 | static void jz4740_i2s_remove(struct snd_soc_component *component) | |
476 | { | |
477 | struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component); | |
478 | ||
479 | clk_disable_unprepare(i2s->clk_aic); | |
480 | } | |
481 | ||
29cc15cf | 482 | static const struct snd_soc_component_driver jz4740_i2s_component = { |
2bebc3b6 | 483 | .name = "jz4740-i2s", |
4e02fd62 AM |
484 | .probe = jz4740_i2s_probe, |
485 | .remove = jz4740_i2s_remove, | |
2bebc3b6 CK |
486 | .suspend = jz4740_i2s_suspend, |
487 | .resume = jz4740_i2s_resume, | |
488 | .legacy_dai_naming = 1, | |
29cc15cf KM |
489 | }; |
490 | ||
f2610571 | 491 | static const struct of_device_id jz4740_of_matches[] = { |
62f9ed5f | 492 | { .compatible = "ingenic,jz4740-i2s", .data = &jz4740_i2s_soc_info }, |
bde8ca7c | 493 | { .compatible = "ingenic,jz4760-i2s", .data = &jz4760_i2s_soc_info }, |
a3434a49 | 494 | { .compatible = "ingenic,jz4770-i2s", .data = &jz4770_i2s_soc_info }, |
62f9ed5f | 495 | { .compatible = "ingenic,jz4780-i2s", .data = &jz4780_i2s_soc_info }, |
bb1b282d | 496 | { .compatible = "ingenic,x1000-i2s", .data = &x1000_i2s_soc_info }, |
f2610571 ZLK |
497 | { /* sentinel */ } |
498 | }; | |
27fcf913 | 499 | MODULE_DEVICE_TABLE(of, jz4740_of_matches); |
f2610571 | 500 | |
0fddb4bc AM |
501 | static int jz4740_i2s_init_regmap_fields(struct device *dev, |
502 | struct jz4740_i2s *i2s) | |
503 | { | |
504 | i2s->field_rx_fifo_thresh = | |
505 | devm_regmap_field_alloc(dev, i2s->regmap, | |
506 | i2s->soc_info->field_rx_fifo_thresh); | |
507 | if (IS_ERR(i2s->field_rx_fifo_thresh)) | |
508 | return PTR_ERR(i2s->field_rx_fifo_thresh); | |
509 | ||
510 | i2s->field_tx_fifo_thresh = | |
511 | devm_regmap_field_alloc(dev, i2s->regmap, | |
512 | i2s->soc_info->field_tx_fifo_thresh); | |
513 | if (IS_ERR(i2s->field_tx_fifo_thresh)) | |
514 | return PTR_ERR(i2s->field_tx_fifo_thresh); | |
515 | ||
516 | i2s->field_i2sdiv_capture = | |
517 | devm_regmap_field_alloc(dev, i2s->regmap, | |
518 | i2s->soc_info->field_i2sdiv_capture); | |
519 | if (IS_ERR(i2s->field_i2sdiv_capture)) | |
520 | return PTR_ERR(i2s->field_i2sdiv_capture); | |
521 | ||
522 | i2s->field_i2sdiv_playback = | |
523 | devm_regmap_field_alloc(dev, i2s->regmap, | |
524 | i2s->soc_info->field_i2sdiv_playback); | |
525 | if (IS_ERR(i2s->field_i2sdiv_playback)) | |
526 | return PTR_ERR(i2s->field_i2sdiv_playback); | |
527 | ||
528 | return 0; | |
529 | } | |
530 | ||
cf375e69 AM |
531 | static const struct regmap_config jz4740_i2s_regmap_config = { |
532 | .reg_bits = 32, | |
533 | .reg_stride = 4, | |
534 | .val_bits = 32, | |
535 | .max_register = JZ_REG_AIC_FIFO, | |
536 | }; | |
537 | ||
d6a29e3d | 538 | static int jz4740_i2s_dev_probe(struct platform_device *pdev) |
11bd3dd1 | 539 | { |
a42d9ba1 | 540 | struct device *dev = &pdev->dev; |
11bd3dd1 | 541 | struct jz4740_i2s *i2s; |
b84c9ce8 | 542 | struct resource *mem; |
cf375e69 | 543 | void __iomem *regs; |
11bd3dd1 LPC |
544 | int ret; |
545 | ||
a42d9ba1 | 546 | i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL); |
11bd3dd1 LPC |
547 | if (!i2s) |
548 | return -ENOMEM; | |
549 | ||
62f9ed5f | 550 | i2s->soc_info = device_get_match_data(dev); |
967beb2e | 551 | |
cf375e69 AM |
552 | regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); |
553 | if (IS_ERR(regs)) | |
554 | return PTR_ERR(regs); | |
11bd3dd1 | 555 | |
8a769101 AM |
556 | i2s->playback_dma_data.maxburst = 16; |
557 | i2s->playback_dma_data.addr = mem->start + JZ_REG_AIC_FIFO; | |
558 | ||
559 | i2s->capture_dma_data.maxburst = 16; | |
560 | i2s->capture_dma_data.addr = mem->start + JZ_REG_AIC_FIFO; | |
11bd3dd1 | 561 | |
a42d9ba1 | 562 | i2s->clk_aic = devm_clk_get(dev, "aic"); |
b84c9ce8 LPC |
563 | if (IS_ERR(i2s->clk_aic)) |
564 | return PTR_ERR(i2s->clk_aic); | |
11bd3dd1 | 565 | |
a42d9ba1 | 566 | i2s->clk_i2s = devm_clk_get(dev, "i2s"); |
b84c9ce8 LPC |
567 | if (IS_ERR(i2s->clk_i2s)) |
568 | return PTR_ERR(i2s->clk_i2s); | |
11bd3dd1 | 569 | |
cf375e69 AM |
570 | i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs, |
571 | &jz4740_i2s_regmap_config); | |
572 | if (IS_ERR(i2s->regmap)) | |
573 | return PTR_ERR(i2s->regmap); | |
574 | ||
0fddb4bc AM |
575 | ret = jz4740_i2s_init_regmap_fields(dev, i2s); |
576 | if (ret) | |
577 | return ret; | |
578 | ||
f0fba2ad | 579 | platform_set_drvdata(pdev, i2s); |
11bd3dd1 | 580 | |
62f9ed5f PC |
581 | ret = devm_snd_soc_register_component(dev, &jz4740_i2s_component, |
582 | i2s->soc_info->dai, 1); | |
0406a40a LPC |
583 | if (ret) |
584 | return ret; | |
585 | ||
a42d9ba1 | 586 | return devm_snd_dmaengine_pcm_register(dev, NULL, |
0406a40a | 587 | SND_DMAENGINE_PCM_FLAG_COMPAT); |
11bd3dd1 LPC |
588 | } |
589 | ||
590 | static struct platform_driver jz4740_i2s_driver = { | |
591 | .probe = jz4740_i2s_dev_probe, | |
11bd3dd1 LPC |
592 | .driver = { |
593 | .name = "jz4740-i2s", | |
e6825bae | 594 | .of_match_table = jz4740_of_matches, |
11bd3dd1 LPC |
595 | }, |
596 | }; | |
597 | ||
c32986e6 | 598 | module_platform_driver(jz4740_i2s_driver); |
11bd3dd1 LPC |
599 | |
600 | MODULE_AUTHOR("Lars-Peter Clausen, <lars@metafoo.de>"); | |
601 | MODULE_DESCRIPTION("Ingenic JZ4740 SoC I2S driver"); | |
602 | MODULE_LICENSE("GPL"); | |
603 | MODULE_ALIAS("platform:jz4740-i2s"); |