1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> |
4 | */ |
5 | |
6 | #include <linux/bitfield.h> |
7 | #include <linux/clk.h> |
8 | #include <linux/delay.h> |
9 | #include <linux/dma-mapping.h> |
10 | #include <linux/init.h> |
11 | #include <linux/io.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/module.h> |
14 | #include <linux/mod_devicetable.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/regmap.h> |
17 | #include <linux/slab.h> |
18 | |
19 | #include <sound/core.h> |
20 | #include <sound/pcm.h> |
21 | #include <sound/pcm_params.h> |
22 | #include <sound/soc.h> |
23 | #include <sound/initval.h> |
24 | #include <sound/dmaengine_pcm.h> |
25 | |
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 | |
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) |
49 | #define JZ_AIC_CTRL_TFLUSH BIT(8) |
50 | #define JZ_AIC_CTRL_RFLUSH BIT(7) |
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) |
65 | |
66 | struct i2s_soc_info { |
67 | struct snd_soc_dai_driver *dai; |
68 | |
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 | |
74 | bool shared_fifo_flush; |
75 | }; |
76 | |
77 | struct jz4740_i2s { |
78 | struct regmap *regmap; |
79 | |
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 | |
85 | struct clk *clk_aic; |
86 | struct clk *clk_i2s; |
87 | |
88 | struct snd_dmaengine_dai_dma_data playback_dma_data; |
89 | struct snd_dmaengine_dai_dma_data capture_dma_data; |
90 | |
91 | const struct i2s_soc_info *soc_info; |
92 | }; |
93 | |
94 | static int jz4740_i2s_startup(struct snd_pcm_substream *substream, |
95 | struct snd_soc_dai *dai) |
96 | { |
97 | struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
98 | int ret; |
99 | |
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) |
107 | regmap_set_bits(map: i2s->regmap, JZ_REG_AIC_CTRL, JZ_AIC_CTRL_TFLUSH); |
108 | else |
109 | regmap_set_bits(map: i2s->regmap, JZ_REG_AIC_CTRL, JZ_AIC_CTRL_RFLUSH); |
110 | } |
111 | |
112 | if (snd_soc_dai_active(dai)) |
113 | return 0; |
114 | |
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) |
122 | regmap_set_bits(map: i2s->regmap, JZ_REG_AIC_CTRL, JZ_AIC_CTRL_TFLUSH); |
123 | |
124 | ret = clk_prepare_enable(clk: i2s->clk_i2s); |
125 | if (ret) |
126 | return ret; |
127 | |
128 | regmap_set_bits(map: i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE); |
129 | return 0; |
130 | } |
131 | |
132 | static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream, |
133 | struct snd_soc_dai *dai) |
134 | { |
135 | struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
136 | |
137 | if (snd_soc_dai_active(dai)) |
138 | return; |
139 | |
140 | regmap_clear_bits(map: i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE); |
141 | |
142 | clk_disable_unprepare(clk: i2s->clk_i2s); |
143 | } |
144 | |
145 | static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd, |
146 | struct snd_soc_dai *dai) |
147 | { |
148 | struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
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 | |
156 | switch (cmd) { |
157 | case SNDRV_PCM_TRIGGER_START: |
158 | case SNDRV_PCM_TRIGGER_RESUME: |
159 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
160 | regmap_set_bits(map: i2s->regmap, JZ_REG_AIC_CTRL, bits: mask); |
161 | break; |
162 | case SNDRV_PCM_TRIGGER_STOP: |
163 | case SNDRV_PCM_TRIGGER_SUSPEND: |
164 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
165 | regmap_clear_bits(map: i2s->regmap, JZ_REG_AIC_CTRL, bits: mask); |
166 | break; |
167 | default: |
168 | return -EINVAL; |
169 | } |
170 | |
171 | return 0; |
172 | } |
173 | |
174 | static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
175 | { |
176 | struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
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; |
180 | |
181 | switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { |
182 | case SND_SOC_DAIFMT_BP_FP: |
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; |
186 | case SND_SOC_DAIFMT_BC_FP: |
187 | conf |= JZ_AIC_CONF_SYNC_CLK_MASTER; |
188 | break; |
189 | case SND_SOC_DAIFMT_BP_FC: |
190 | conf |= JZ_AIC_CONF_BIT_CLK_MASTER; |
191 | break; |
192 | case SND_SOC_DAIFMT_BC_FC: |
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 | |
215 | regmap_update_bits(map: i2s->regmap, JZ_REG_AIC_CONF, mask: conf_mask, val: conf); |
216 | regmap_write(map: i2s->regmap, JZ_REG_AIC_I2S_FMT, val: format); |
217 | |
218 | return 0; |
219 | } |
220 | |
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 | |
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 | { |
254 | struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
255 | struct regmap_field *div_field; |
256 | unsigned long i2sdiv_max; |
257 | unsigned int sample_size; |
258 | uint32_t ctrl, conf; |
259 | int div = 1; |
260 | |
261 | regmap_read(map: i2s->regmap, JZ_REG_AIC_CTRL, val: &ctrl); |
262 | regmap_read(map: i2s->regmap, JZ_REG_AIC_CONF, val: &conf); |
263 | |
264 | switch (params_format(p: params)) { |
265 | case SNDRV_PCM_FORMAT_S8: |
266 | sample_size = 0; |
267 | break; |
268 | case SNDRV_PCM_FORMAT_S16_LE: |
269 | sample_size = 1; |
270 | break; |
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; |
277 | default: |
278 | return -EINVAL; |
279 | } |
280 | |
281 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
282 | ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE; |
283 | ctrl |= FIELD_PREP(JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE, sample_size); |
284 | |
285 | if (params_channels(p: params) == 1) |
286 | ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO; |
287 | else |
288 | ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO; |
289 | |
290 | div_field = i2s->field_i2sdiv_playback; |
291 | i2sdiv_max = GENMASK(i2s->soc_info->field_i2sdiv_playback.msb, |
292 | i2s->soc_info->field_i2sdiv_playback.lsb); |
293 | } else { |
294 | ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE; |
295 | ctrl |= FIELD_PREP(JZ_AIC_CTRL_INPUT_SAMPLE_SIZE, sample_size); |
296 | |
297 | div_field = i2s->field_i2sdiv_capture; |
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(mclk: clk_get_rate(clk: i2s->clk_i2s), |
309 | rate: params_rate(p: params), i2sdiv_max); |
310 | if (div < 0) |
311 | return div; |
312 | } |
313 | |
314 | regmap_write(map: i2s->regmap, JZ_REG_AIC_CTRL, val: ctrl); |
315 | regmap_field_write(field: div_field, val: div - 1); |
316 | |
317 | return 0; |
318 | } |
319 | |
320 | static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai) |
321 | { |
322 | struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
323 | |
324 | snd_soc_dai_init_dma_data(dai, playback: &i2s->playback_dma_data, |
325 | capture: &i2s->capture_dma_data); |
326 | |
327 | return 0; |
328 | } |
329 | |
330 | static const struct snd_soc_dai_ops jz4740_i2s_dai_ops = { |
331 | .probe = jz4740_i2s_dai_probe, |
332 | .startup = jz4740_i2s_startup, |
333 | .shutdown = jz4740_i2s_shutdown, |
334 | .trigger = jz4740_i2s_trigger, |
335 | .hw_params = jz4740_i2s_hw_params, |
336 | .set_fmt = jz4740_i2s_set_fmt, |
337 | }; |
338 | |
339 | #define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \ |
340 | SNDRV_PCM_FMTBIT_S16_LE | \ |
341 | SNDRV_PCM_FMTBIT_S20_LE | \ |
342 | SNDRV_PCM_FMTBIT_S24_LE) |
343 | |
344 | static struct snd_soc_dai_driver jz4740_i2s_dai = { |
345 | .playback = { |
346 | .channels_min = 1, |
347 | .channels_max = 2, |
348 | .rates = SNDRV_PCM_RATE_CONTINUOUS, |
349 | .formats = JZ4740_I2S_FMTS, |
350 | }, |
351 | .capture = { |
352 | .channels_min = 2, |
353 | .channels_max = 2, |
354 | .rates = SNDRV_PCM_RATE_CONTINUOUS, |
355 | .formats = JZ4740_I2S_FMTS, |
356 | }, |
357 | .symmetric_rate = 1, |
358 | .ops = &jz4740_i2s_dai_ops, |
359 | }; |
360 | |
361 | static const struct i2s_soc_info jz4740_i2s_soc_info = { |
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, |
368 | }; |
369 | |
370 | static const struct i2s_soc_info jz4760_i2s_soc_info = { |
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), |
376 | }; |
377 | |
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 | |
386 | static struct snd_soc_dai_driver jz4770_i2s_dai = { |
387 | .playback = { |
388 | .channels_min = 1, |
389 | .channels_max = 2, |
390 | .rates = SNDRV_PCM_RATE_CONTINUOUS, |
391 | .formats = JZ4740_I2S_FMTS, |
392 | }, |
393 | .capture = { |
394 | .channels_min = 2, |
395 | .channels_max = 2, |
396 | .rates = SNDRV_PCM_RATE_CONTINUOUS, |
397 | .formats = JZ4740_I2S_FMTS, |
398 | }, |
399 | .ops = &jz4740_i2s_dai_ops, |
400 | }; |
401 | |
402 | static const struct i2s_soc_info jz4770_i2s_soc_info = { |
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), |
408 | }; |
409 | |
410 | static const struct i2s_soc_info jz4780_i2s_soc_info = { |
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), |
416 | }; |
417 | |
418 | static int jz4740_i2s_suspend(struct snd_soc_component *component) |
419 | { |
420 | struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(c: component); |
421 | |
422 | if (snd_soc_component_active(component)) { |
423 | regmap_clear_bits(map: i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE); |
424 | clk_disable_unprepare(clk: i2s->clk_i2s); |
425 | } |
426 | |
427 | clk_disable_unprepare(clk: 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(c: component); |
435 | int ret; |
436 | |
437 | ret = clk_prepare_enable(clk: i2s->clk_aic); |
438 | if (ret) |
439 | return ret; |
440 | |
441 | if (snd_soc_component_active(component)) { |
442 | ret = clk_prepare_enable(clk: i2s->clk_i2s); |
443 | if (ret) { |
444 | clk_disable_unprepare(clk: i2s->clk_aic); |
445 | return ret; |
446 | } |
447 | |
448 | regmap_set_bits(map: i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE); |
449 | } |
450 | |
451 | return 0; |
452 | } |
453 | |
454 | static int jz4740_i2s_probe(struct snd_soc_component *component) |
455 | { |
456 | struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(c: component); |
457 | int ret; |
458 | |
459 | ret = clk_prepare_enable(clk: i2s->clk_aic); |
460 | if (ret) |
461 | return ret; |
462 | |
463 | regmap_write(map: i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET); |
464 | |
465 | regmap_write(map: 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(field: i2s->field_rx_fifo_thresh, val: 7); |
470 | regmap_field_write(field: i2s->field_tx_fifo_thresh, val: 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(c: component); |
478 | |
479 | clk_disable_unprepare(clk: i2s->clk_aic); |
480 | } |
481 | |
482 | static const struct snd_soc_component_driver jz4740_i2s_component = { |
483 | .name = "jz4740-i2s" , |
484 | .probe = jz4740_i2s_probe, |
485 | .remove = jz4740_i2s_remove, |
486 | .suspend = jz4740_i2s_suspend, |
487 | .resume = jz4740_i2s_resume, |
488 | .legacy_dai_naming = 1, |
489 | }; |
490 | |
491 | static const struct of_device_id jz4740_of_matches[] = { |
492 | { .compatible = "ingenic,jz4740-i2s" , .data = &jz4740_i2s_soc_info }, |
493 | { .compatible = "ingenic,jz4760-i2s" , .data = &jz4760_i2s_soc_info }, |
494 | { .compatible = "ingenic,jz4770-i2s" , .data = &jz4770_i2s_soc_info }, |
495 | { .compatible = "ingenic,jz4780-i2s" , .data = &jz4780_i2s_soc_info }, |
496 | { .compatible = "ingenic,x1000-i2s" , .data = &x1000_i2s_soc_info }, |
497 | { /* sentinel */ } |
498 | }; |
499 | MODULE_DEVICE_TABLE(of, jz4740_of_matches); |
500 | |
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, regmap: i2s->regmap, |
506 | reg_field: i2s->soc_info->field_rx_fifo_thresh); |
507 | if (IS_ERR(ptr: i2s->field_rx_fifo_thresh)) |
508 | return PTR_ERR(ptr: i2s->field_rx_fifo_thresh); |
509 | |
510 | i2s->field_tx_fifo_thresh = |
511 | devm_regmap_field_alloc(dev, regmap: i2s->regmap, |
512 | reg_field: i2s->soc_info->field_tx_fifo_thresh); |
513 | if (IS_ERR(ptr: i2s->field_tx_fifo_thresh)) |
514 | return PTR_ERR(ptr: i2s->field_tx_fifo_thresh); |
515 | |
516 | i2s->field_i2sdiv_capture = |
517 | devm_regmap_field_alloc(dev, regmap: i2s->regmap, |
518 | reg_field: i2s->soc_info->field_i2sdiv_capture); |
519 | if (IS_ERR(ptr: i2s->field_i2sdiv_capture)) |
520 | return PTR_ERR(ptr: i2s->field_i2sdiv_capture); |
521 | |
522 | i2s->field_i2sdiv_playback = |
523 | devm_regmap_field_alloc(dev, regmap: i2s->regmap, |
524 | reg_field: i2s->soc_info->field_i2sdiv_playback); |
525 | if (IS_ERR(ptr: i2s->field_i2sdiv_playback)) |
526 | return PTR_ERR(ptr: i2s->field_i2sdiv_playback); |
527 | |
528 | return 0; |
529 | } |
530 | |
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 | |
538 | static int jz4740_i2s_dev_probe(struct platform_device *pdev) |
539 | { |
540 | struct device *dev = &pdev->dev; |
541 | struct jz4740_i2s *i2s; |
542 | struct resource *mem; |
543 | void __iomem *regs; |
544 | int ret; |
545 | |
546 | i2s = devm_kzalloc(dev, size: sizeof(*i2s), GFP_KERNEL); |
547 | if (!i2s) |
548 | return -ENOMEM; |
549 | |
550 | i2s->soc_info = device_get_match_data(dev); |
551 | |
552 | regs = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &mem); |
553 | if (IS_ERR(ptr: regs)) |
554 | return PTR_ERR(ptr: regs); |
555 | |
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; |
561 | |
562 | i2s->clk_aic = devm_clk_get(dev, id: "aic" ); |
563 | if (IS_ERR(ptr: i2s->clk_aic)) |
564 | return PTR_ERR(ptr: i2s->clk_aic); |
565 | |
566 | i2s->clk_i2s = devm_clk_get(dev, id: "i2s" ); |
567 | if (IS_ERR(ptr: i2s->clk_i2s)) |
568 | return PTR_ERR(ptr: i2s->clk_i2s); |
569 | |
570 | i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs, |
571 | &jz4740_i2s_regmap_config); |
572 | if (IS_ERR(ptr: i2s->regmap)) |
573 | return PTR_ERR(ptr: i2s->regmap); |
574 | |
575 | ret = jz4740_i2s_init_regmap_fields(dev, i2s); |
576 | if (ret) |
577 | return ret; |
578 | |
579 | platform_set_drvdata(pdev, data: i2s); |
580 | |
581 | ret = devm_snd_soc_register_component(dev, component_driver: &jz4740_i2s_component, |
582 | dai_drv: i2s->soc_info->dai, num_dai: 1); |
583 | if (ret) |
584 | return ret; |
585 | |
586 | return devm_snd_dmaengine_pcm_register(dev, NULL, |
587 | SND_DMAENGINE_PCM_FLAG_COMPAT); |
588 | } |
589 | |
590 | static struct platform_driver jz4740_i2s_driver = { |
591 | .probe = jz4740_i2s_dev_probe, |
592 | .driver = { |
593 | .name = "jz4740-i2s" , |
594 | .of_match_table = jz4740_of_matches, |
595 | }, |
596 | }; |
597 | |
598 | module_platform_driver(jz4740_i2s_driver); |
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" ); |
604 | |