]>
Commit | Line | Data |
---|---|---|
3e086edf | 1 | /* |
2 | * STM32 ALSA SoC Digital Audio Interface (SAI) driver. | |
3 | * | |
4 | * Copyright (C) 2016, STMicroelectronics - All Rights Reserved | |
5 | * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics. | |
6 | * | |
7 | * License terms: GPL V2.0. | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify it | |
10 | * under the terms of the GNU General Public License version 2 as published by | |
11 | * the Free Software Foundation. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, but | |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | |
16 | * details. | |
17 | */ | |
18 | ||
19 | #include <linux/clk.h> | |
20 | #include <linux/kernel.h> | |
21 | #include <linux/module.h> | |
22 | #include <linux/of_irq.h> | |
23 | #include <linux/of_platform.h> | |
24 | #include <linux/regmap.h> | |
25 | ||
26 | #include <sound/core.h> | |
27 | #include <sound/dmaengine_pcm.h> | |
28 | #include <sound/pcm_params.h> | |
29 | ||
30 | #include "stm32_sai.h" | |
31 | ||
32 | #define SAI_FREE_PROTOCOL 0x0 | |
33 | ||
34 | #define SAI_SLOT_SIZE_AUTO 0x0 | |
35 | #define SAI_SLOT_SIZE_16 0x1 | |
36 | #define SAI_SLOT_SIZE_32 0x2 | |
37 | ||
38 | #define SAI_DATASIZE_8 0x2 | |
39 | #define SAI_DATASIZE_10 0x3 | |
40 | #define SAI_DATASIZE_16 0x4 | |
41 | #define SAI_DATASIZE_20 0x5 | |
42 | #define SAI_DATASIZE_24 0x6 | |
43 | #define SAI_DATASIZE_32 0x7 | |
44 | ||
45 | #define STM_SAI_FIFO_SIZE 8 | |
46 | #define STM_SAI_DAI_NAME_SIZE 15 | |
47 | ||
48 | #define STM_SAI_IS_PLAYBACK(ip) ((ip)->dir == SNDRV_PCM_STREAM_PLAYBACK) | |
49 | #define STM_SAI_IS_CAPTURE(ip) ((ip)->dir == SNDRV_PCM_STREAM_CAPTURE) | |
50 | ||
51 | #define STM_SAI_A_ID 0x0 | |
52 | #define STM_SAI_B_ID 0x1 | |
53 | ||
03e78a24 | 54 | #define STM_SAI_IS_SUB_A(x) ((x)->id == STM_SAI_A_ID) |
55 | #define STM_SAI_IS_SUB_B(x) ((x)->id == STM_SAI_B_ID) | |
3e086edf | 56 | #define STM_SAI_BLOCK_NAME(x) (((x)->id == STM_SAI_A_ID) ? "A" : "B") |
57 | ||
58 | /** | |
59 | * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) | |
60 | * @pdev: device data pointer | |
61 | * @regmap: SAI register map pointer | |
03e78a24 | 62 | * @regmap_config: SAI sub block register map configuration pointer |
3e086edf | 63 | * @dma_params: dma configuration data for rx or tx channel |
64 | * @cpu_dai_drv: DAI driver data pointer | |
65 | * @cpu_dai: DAI runtime data pointer | |
66 | * @substream: PCM substream data pointer | |
67 | * @pdata: SAI block parent data pointer | |
68 | * @sai_ck: kernel clock feeding the SAI clock generator | |
69 | * @phys_addr: SAI registers physical base address | |
70 | * @mclk_rate: SAI block master clock frequency (Hz). set at init | |
71 | * @id: SAI sub block id corresponding to sub-block A or B | |
72 | * @dir: SAI block direction (playback or capture). set at init | |
73 | * @master: SAI block mode flag. (true=master, false=slave) set at init | |
74 | * @fmt: SAI block format. relevant only for custom protocols. set at init | |
75 | * @sync: SAI block synchronization mode. (none, internal or external) | |
76 | * @fs_length: frame synchronization length. depends on protocol settings | |
77 | * @slots: rx or tx slot number | |
78 | * @slot_width: rx or tx slot width in bits | |
79 | * @slot_mask: rx or tx active slots mask. set at init or at runtime | |
80 | * @data_size: PCM data width. corresponds to PCM substream width. | |
81 | */ | |
82 | struct stm32_sai_sub_data { | |
83 | struct platform_device *pdev; | |
84 | struct regmap *regmap; | |
03e78a24 | 85 | const struct regmap_config *regmap_config; |
3e086edf | 86 | struct snd_dmaengine_dai_dma_data dma_params; |
87 | struct snd_soc_dai_driver *cpu_dai_drv; | |
88 | struct snd_soc_dai *cpu_dai; | |
89 | struct snd_pcm_substream *substream; | |
90 | struct stm32_sai_data *pdata; | |
91 | struct clk *sai_ck; | |
92 | dma_addr_t phys_addr; | |
93 | unsigned int mclk_rate; | |
94 | unsigned int id; | |
95 | int dir; | |
96 | bool master; | |
97 | int fmt; | |
98 | int sync; | |
99 | int fs_length; | |
100 | int slots; | |
101 | int slot_width; | |
102 | int slot_mask; | |
103 | int data_size; | |
104 | }; | |
105 | ||
106 | enum stm32_sai_fifo_th { | |
107 | STM_SAI_FIFO_TH_EMPTY, | |
108 | STM_SAI_FIFO_TH_QUARTER, | |
109 | STM_SAI_FIFO_TH_HALF, | |
110 | STM_SAI_FIFO_TH_3_QUARTER, | |
111 | STM_SAI_FIFO_TH_FULL, | |
112 | }; | |
113 | ||
114 | static bool stm32_sai_sub_readable_reg(struct device *dev, unsigned int reg) | |
115 | { | |
116 | switch (reg) { | |
117 | case STM_SAI_CR1_REGX: | |
118 | case STM_SAI_CR2_REGX: | |
119 | case STM_SAI_FRCR_REGX: | |
120 | case STM_SAI_SLOTR_REGX: | |
121 | case STM_SAI_IMR_REGX: | |
122 | case STM_SAI_SR_REGX: | |
123 | case STM_SAI_CLRFR_REGX: | |
124 | case STM_SAI_DR_REGX: | |
03e78a24 | 125 | case STM_SAI_PDMCR_REGX: |
126 | case STM_SAI_PDMLY_REGX: | |
3e086edf | 127 | return true; |
128 | default: | |
129 | return false; | |
130 | } | |
131 | } | |
132 | ||
133 | static bool stm32_sai_sub_volatile_reg(struct device *dev, unsigned int reg) | |
134 | { | |
135 | switch (reg) { | |
136 | case STM_SAI_DR_REGX: | |
137 | return true; | |
138 | default: | |
139 | return false; | |
140 | } | |
141 | } | |
142 | ||
143 | static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg) | |
144 | { | |
145 | switch (reg) { | |
146 | case STM_SAI_CR1_REGX: | |
147 | case STM_SAI_CR2_REGX: | |
148 | case STM_SAI_FRCR_REGX: | |
149 | case STM_SAI_SLOTR_REGX: | |
150 | case STM_SAI_IMR_REGX: | |
151 | case STM_SAI_SR_REGX: | |
152 | case STM_SAI_CLRFR_REGX: | |
153 | case STM_SAI_DR_REGX: | |
03e78a24 | 154 | case STM_SAI_PDMCR_REGX: |
155 | case STM_SAI_PDMLY_REGX: | |
3e086edf | 156 | return true; |
157 | default: | |
158 | return false; | |
159 | } | |
160 | } | |
161 | ||
03e78a24 | 162 | static const struct regmap_config stm32_sai_sub_regmap_config_f4 = { |
3e086edf | 163 | .reg_bits = 32, |
164 | .reg_stride = 4, | |
165 | .val_bits = 32, | |
166 | .max_register = STM_SAI_DR_REGX, | |
167 | .readable_reg = stm32_sai_sub_readable_reg, | |
168 | .volatile_reg = stm32_sai_sub_volatile_reg, | |
169 | .writeable_reg = stm32_sai_sub_writeable_reg, | |
170 | .fast_io = true, | |
171 | }; | |
172 | ||
03e78a24 | 173 | static const struct regmap_config stm32_sai_sub_regmap_config_h7 = { |
174 | .reg_bits = 32, | |
175 | .reg_stride = 4, | |
176 | .val_bits = 32, | |
177 | .max_register = STM_SAI_PDMLY_REGX, | |
178 | .readable_reg = stm32_sai_sub_readable_reg, | |
179 | .volatile_reg = stm32_sai_sub_volatile_reg, | |
180 | .writeable_reg = stm32_sai_sub_writeable_reg, | |
181 | .fast_io = true, | |
182 | }; | |
183 | ||
3e086edf | 184 | static irqreturn_t stm32_sai_isr(int irq, void *devid) |
185 | { | |
186 | struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid; | |
3e086edf | 187 | struct platform_device *pdev = sai->pdev; |
188 | unsigned int sr, imr, flags; | |
189 | snd_pcm_state_t status = SNDRV_PCM_STATE_RUNNING; | |
190 | ||
191 | regmap_read(sai->regmap, STM_SAI_IMR_REGX, &imr); | |
192 | regmap_read(sai->regmap, STM_SAI_SR_REGX, &sr); | |
193 | ||
194 | flags = sr & imr; | |
195 | if (!flags) | |
196 | return IRQ_NONE; | |
197 | ||
198 | regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK, | |
199 | SAI_XCLRFR_MASK); | |
200 | ||
d807cdfb OM |
201 | if (!sai->substream) { |
202 | dev_err(&pdev->dev, "Device stopped. Spurious IRQ 0x%x\n", sr); | |
203 | return IRQ_NONE; | |
204 | } | |
205 | ||
3e086edf | 206 | if (flags & SAI_XIMR_OVRUDRIE) { |
602fdadc | 207 | dev_err(&pdev->dev, "IRQ %s\n", |
3e086edf | 208 | STM_SAI_IS_PLAYBACK(sai) ? "underrun" : "overrun"); |
209 | status = SNDRV_PCM_STATE_XRUN; | |
210 | } | |
211 | ||
212 | if (flags & SAI_XIMR_MUTEDETIE) | |
602fdadc | 213 | dev_dbg(&pdev->dev, "IRQ mute detected\n"); |
3e086edf | 214 | |
215 | if (flags & SAI_XIMR_WCKCFGIE) { | |
602fdadc | 216 | dev_err(&pdev->dev, "IRQ wrong clock configuration\n"); |
3e086edf | 217 | status = SNDRV_PCM_STATE_DISCONNECTED; |
218 | } | |
219 | ||
220 | if (flags & SAI_XIMR_CNRDYIE) | |
602fdadc | 221 | dev_err(&pdev->dev, "IRQ Codec not ready\n"); |
3e086edf | 222 | |
223 | if (flags & SAI_XIMR_AFSDETIE) { | |
602fdadc | 224 | dev_err(&pdev->dev, "IRQ Anticipated frame synchro\n"); |
3e086edf | 225 | status = SNDRV_PCM_STATE_XRUN; |
226 | } | |
227 | ||
228 | if (flags & SAI_XIMR_LFSDETIE) { | |
602fdadc | 229 | dev_err(&pdev->dev, "IRQ Late frame synchro\n"); |
3e086edf | 230 | status = SNDRV_PCM_STATE_XRUN; |
231 | } | |
232 | ||
233 | if (status != SNDRV_PCM_STATE_RUNNING) { | |
d807cdfb OM |
234 | snd_pcm_stream_lock(sai->substream); |
235 | snd_pcm_stop(sai->substream, SNDRV_PCM_STATE_XRUN); | |
236 | snd_pcm_stream_unlock(sai->substream); | |
3e086edf | 237 | } |
238 | ||
239 | return IRQ_HANDLED; | |
240 | } | |
241 | ||
242 | static int stm32_sai_set_sysclk(struct snd_soc_dai *cpu_dai, | |
243 | int clk_id, unsigned int freq, int dir) | |
244 | { | |
245 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | |
701a6ec3 | 246 | int ret; |
3e086edf | 247 | |
248 | if ((dir == SND_SOC_CLOCK_OUT) && sai->master) { | |
701a6ec3 | 249 | ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, |
250 | SAI_XCR1_NODIV, | |
251 | (unsigned int)~SAI_XCR1_NODIV); | |
252 | if (ret < 0) | |
253 | return ret; | |
254 | ||
3e086edf | 255 | sai->mclk_rate = freq; |
256 | dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq); | |
257 | } | |
258 | ||
259 | return 0; | |
260 | } | |
261 | ||
262 | static int stm32_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, | |
263 | u32 rx_mask, int slots, int slot_width) | |
264 | { | |
265 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | |
266 | int slotr, slotr_mask, slot_size; | |
267 | ||
602fdadc | 268 | dev_dbg(cpu_dai->dev, "Masks tx/rx:%#x/%#x, slots:%d, width:%d\n", |
3e086edf | 269 | tx_mask, rx_mask, slots, slot_width); |
270 | ||
271 | switch (slot_width) { | |
272 | case 16: | |
273 | slot_size = SAI_SLOT_SIZE_16; | |
274 | break; | |
275 | case 32: | |
276 | slot_size = SAI_SLOT_SIZE_32; | |
277 | break; | |
278 | default: | |
279 | slot_size = SAI_SLOT_SIZE_AUTO; | |
280 | break; | |
281 | } | |
282 | ||
283 | slotr = SAI_XSLOTR_SLOTSZ_SET(slot_size) | | |
284 | SAI_XSLOTR_NBSLOT_SET(slots - 1); | |
285 | slotr_mask = SAI_XSLOTR_SLOTSZ_MASK | SAI_XSLOTR_NBSLOT_MASK; | |
286 | ||
287 | /* tx/rx mask set in machine init, if slot number defined in DT */ | |
288 | if (STM_SAI_IS_PLAYBACK(sai)) { | |
289 | sai->slot_mask = tx_mask; | |
290 | slotr |= SAI_XSLOTR_SLOTEN_SET(tx_mask); | |
291 | } | |
292 | ||
293 | if (STM_SAI_IS_CAPTURE(sai)) { | |
294 | sai->slot_mask = rx_mask; | |
295 | slotr |= SAI_XSLOTR_SLOTEN_SET(rx_mask); | |
296 | } | |
297 | ||
298 | slotr_mask |= SAI_XSLOTR_SLOTEN_MASK; | |
299 | ||
300 | regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX, slotr_mask, slotr); | |
301 | ||
302 | sai->slot_width = slot_width; | |
303 | sai->slots = slots; | |
304 | ||
305 | return 0; | |
306 | } | |
307 | ||
308 | static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) | |
309 | { | |
310 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | |
311 | int cr1 = 0, frcr = 0; | |
312 | int cr1_mask = 0, frcr_mask = 0; | |
313 | int ret; | |
314 | ||
315 | dev_dbg(cpu_dai->dev, "fmt %x\n", fmt); | |
316 | ||
317 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | |
318 | /* SCK active high for all protocols */ | |
319 | case SND_SOC_DAIFMT_I2S: | |
320 | cr1 |= SAI_XCR1_CKSTR; | |
321 | frcr |= SAI_XFRCR_FSOFF | SAI_XFRCR_FSDEF; | |
322 | break; | |
323 | /* Left justified */ | |
324 | case SND_SOC_DAIFMT_MSB: | |
325 | frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF; | |
326 | break; | |
327 | /* Right justified */ | |
328 | case SND_SOC_DAIFMT_LSB: | |
329 | frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF; | |
330 | break; | |
331 | case SND_SOC_DAIFMT_DSP_A: | |
332 | frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF; | |
333 | break; | |
334 | case SND_SOC_DAIFMT_DSP_B: | |
335 | frcr |= SAI_XFRCR_FSPOL; | |
336 | break; | |
337 | default: | |
338 | dev_err(cpu_dai->dev, "Unsupported protocol %#x\n", | |
339 | fmt & SND_SOC_DAIFMT_FORMAT_MASK); | |
340 | return -EINVAL; | |
341 | } | |
342 | ||
343 | cr1_mask |= SAI_XCR1_PRTCFG_MASK | SAI_XCR1_CKSTR; | |
344 | frcr_mask |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF | | |
345 | SAI_XFRCR_FSDEF; | |
346 | ||
347 | /* DAI clock strobing. Invert setting previously set */ | |
348 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | |
349 | case SND_SOC_DAIFMT_NB_NF: | |
350 | break; | |
351 | case SND_SOC_DAIFMT_IB_NF: | |
352 | cr1 ^= SAI_XCR1_CKSTR; | |
353 | break; | |
354 | case SND_SOC_DAIFMT_NB_IF: | |
355 | frcr ^= SAI_XFRCR_FSPOL; | |
356 | break; | |
357 | case SND_SOC_DAIFMT_IB_IF: | |
358 | /* Invert fs & sck */ | |
359 | cr1 ^= SAI_XCR1_CKSTR; | |
360 | frcr ^= SAI_XFRCR_FSPOL; | |
361 | break; | |
362 | default: | |
363 | dev_err(cpu_dai->dev, "Unsupported strobing %#x\n", | |
364 | fmt & SND_SOC_DAIFMT_INV_MASK); | |
365 | return -EINVAL; | |
366 | } | |
367 | cr1_mask |= SAI_XCR1_CKSTR; | |
368 | frcr_mask |= SAI_XFRCR_FSPOL; | |
369 | ||
370 | regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr); | |
371 | ||
372 | /* DAI clock master masks */ | |
373 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | |
374 | case SND_SOC_DAIFMT_CBM_CFM: | |
375 | /* codec is master */ | |
376 | cr1 |= SAI_XCR1_SLAVE; | |
377 | sai->master = false; | |
378 | break; | |
379 | case SND_SOC_DAIFMT_CBS_CFS: | |
380 | sai->master = true; | |
381 | break; | |
382 | default: | |
383 | dev_err(cpu_dai->dev, "Unsupported mode %#x\n", | |
384 | fmt & SND_SOC_DAIFMT_MASTER_MASK); | |
385 | return -EINVAL; | |
386 | } | |
387 | cr1_mask |= SAI_XCR1_SLAVE; | |
388 | ||
701a6ec3 | 389 | /* do not generate master by default */ |
390 | cr1 |= SAI_XCR1_NODIV; | |
391 | cr1_mask |= SAI_XCR1_NODIV; | |
392 | ||
3e086edf | 393 | ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1); |
394 | if (ret < 0) { | |
395 | dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); | |
396 | return ret; | |
397 | } | |
398 | ||
399 | sai->fmt = fmt; | |
400 | ||
401 | return 0; | |
402 | } | |
403 | ||
404 | static int stm32_sai_startup(struct snd_pcm_substream *substream, | |
405 | struct snd_soc_dai *cpu_dai) | |
406 | { | |
407 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | |
408 | int imr, cr2, ret; | |
409 | ||
410 | sai->substream = substream; | |
411 | ||
412 | ret = clk_prepare_enable(sai->sai_ck); | |
413 | if (ret < 0) { | |
602fdadc | 414 | dev_err(cpu_dai->dev, "Failed to enable clock: %d\n", ret); |
3e086edf | 415 | return ret; |
416 | } | |
417 | ||
418 | /* Enable ITs */ | |
419 | regmap_update_bits(sai->regmap, STM_SAI_SR_REGX, | |
420 | SAI_XSR_MASK, (unsigned int)~SAI_XSR_MASK); | |
421 | ||
422 | regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, | |
423 | SAI_XCLRFR_MASK, SAI_XCLRFR_MASK); | |
424 | ||
425 | imr = SAI_XIMR_OVRUDRIE; | |
426 | if (STM_SAI_IS_CAPTURE(sai)) { | |
427 | regmap_read(sai->regmap, STM_SAI_CR2_REGX, &cr2); | |
428 | if (cr2 & SAI_XCR2_MUTECNT_MASK) | |
429 | imr |= SAI_XIMR_MUTEDETIE; | |
430 | } | |
431 | ||
432 | if (sai->master) | |
433 | imr |= SAI_XIMR_WCKCFGIE; | |
434 | else | |
435 | imr |= SAI_XIMR_AFSDETIE | SAI_XIMR_LFSDETIE; | |
436 | ||
437 | regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, | |
438 | SAI_XIMR_MASK, imr); | |
439 | ||
440 | return 0; | |
441 | } | |
442 | ||
443 | static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai, | |
444 | struct snd_pcm_substream *substream, | |
445 | struct snd_pcm_hw_params *params) | |
446 | { | |
447 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | |
448 | int cr1, cr1_mask, ret; | |
449 | int fth = STM_SAI_FIFO_TH_HALF; | |
450 | ||
451 | /* FIFO config */ | |
452 | regmap_update_bits(sai->regmap, STM_SAI_CR2_REGX, | |
453 | SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK, | |
454 | SAI_XCR2_FFLUSH | SAI_XCR2_FTH_SET(fth)); | |
455 | ||
456 | /* Mode, data format and channel config */ | |
457 | cr1 = SAI_XCR1_PRTCFG_SET(SAI_FREE_PROTOCOL); | |
458 | switch (params_format(params)) { | |
459 | case SNDRV_PCM_FORMAT_S8: | |
460 | cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_8); | |
461 | break; | |
462 | case SNDRV_PCM_FORMAT_S16_LE: | |
463 | cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_16); | |
464 | break; | |
465 | case SNDRV_PCM_FORMAT_S32_LE: | |
466 | cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_32); | |
467 | break; | |
468 | default: | |
469 | dev_err(cpu_dai->dev, "Data format not supported"); | |
470 | return -EINVAL; | |
471 | } | |
472 | cr1_mask = SAI_XCR1_DS_MASK | SAI_XCR1_PRTCFG_MASK; | |
473 | ||
474 | cr1_mask |= SAI_XCR1_RX_TX; | |
475 | if (STM_SAI_IS_CAPTURE(sai)) | |
476 | cr1 |= SAI_XCR1_RX_TX; | |
477 | ||
478 | cr1_mask |= SAI_XCR1_MONO; | |
479 | if ((sai->slots == 2) && (params_channels(params) == 1)) | |
480 | cr1 |= SAI_XCR1_MONO; | |
481 | ||
482 | ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1); | |
483 | if (ret < 0) { | |
484 | dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); | |
485 | return ret; | |
486 | } | |
487 | ||
488 | /* DMA config */ | |
489 | sai->dma_params.maxburst = STM_SAI_FIFO_SIZE * fth / sizeof(u32); | |
490 | snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&sai->dma_params); | |
491 | ||
492 | return 0; | |
493 | } | |
494 | ||
495 | static int stm32_sai_set_slots(struct snd_soc_dai *cpu_dai) | |
496 | { | |
497 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | |
498 | int slotr, slot_sz; | |
499 | ||
500 | regmap_read(sai->regmap, STM_SAI_SLOTR_REGX, &slotr); | |
501 | ||
502 | /* | |
503 | * If SLOTSZ is set to auto in SLOTR, align slot width on data size | |
504 | * By default slot width = data size, if not forced from DT | |
505 | */ | |
506 | slot_sz = slotr & SAI_XSLOTR_SLOTSZ_MASK; | |
507 | if (slot_sz == SAI_XSLOTR_SLOTSZ_SET(SAI_SLOT_SIZE_AUTO)) | |
508 | sai->slot_width = sai->data_size; | |
509 | ||
510 | if (sai->slot_width < sai->data_size) { | |
511 | dev_err(cpu_dai->dev, | |
512 | "Data size %d larger than slot width\n", | |
513 | sai->data_size); | |
514 | return -EINVAL; | |
515 | } | |
516 | ||
517 | /* Slot number is set to 2, if not specified in DT */ | |
518 | if (!sai->slots) | |
519 | sai->slots = 2; | |
520 | ||
521 | /* The number of slots in the audio frame is equal to NBSLOT[3:0] + 1*/ | |
522 | regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX, | |
523 | SAI_XSLOTR_NBSLOT_MASK, | |
524 | SAI_XSLOTR_NBSLOT_SET((sai->slots - 1))); | |
525 | ||
526 | /* Set default slots mask if not already set from DT */ | |
527 | if (!(slotr & SAI_XSLOTR_SLOTEN_MASK)) { | |
528 | sai->slot_mask = (1 << sai->slots) - 1; | |
529 | regmap_update_bits(sai->regmap, | |
530 | STM_SAI_SLOTR_REGX, SAI_XSLOTR_SLOTEN_MASK, | |
531 | SAI_XSLOTR_SLOTEN_SET(sai->slot_mask)); | |
532 | } | |
533 | ||
602fdadc | 534 | dev_dbg(cpu_dai->dev, "Slots %d, slot width %d\n", |
3e086edf | 535 | sai->slots, sai->slot_width); |
536 | ||
537 | return 0; | |
538 | } | |
539 | ||
540 | static void stm32_sai_set_frame(struct snd_soc_dai *cpu_dai) | |
541 | { | |
542 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | |
543 | int fs_active, offset, format; | |
544 | int frcr, frcr_mask; | |
545 | ||
546 | format = sai->fmt & SND_SOC_DAIFMT_FORMAT_MASK; | |
547 | sai->fs_length = sai->slot_width * sai->slots; | |
548 | ||
549 | fs_active = sai->fs_length / 2; | |
550 | if ((format == SND_SOC_DAIFMT_DSP_A) || | |
551 | (format == SND_SOC_DAIFMT_DSP_B)) | |
552 | fs_active = 1; | |
553 | ||
554 | frcr = SAI_XFRCR_FRL_SET((sai->fs_length - 1)); | |
555 | frcr |= SAI_XFRCR_FSALL_SET((fs_active - 1)); | |
556 | frcr_mask = SAI_XFRCR_FRL_MASK | SAI_XFRCR_FSALL_MASK; | |
557 | ||
602fdadc | 558 | dev_dbg(cpu_dai->dev, "Frame length %d, frame active %d\n", |
3e086edf | 559 | sai->fs_length, fs_active); |
560 | ||
561 | regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr); | |
562 | ||
563 | if ((sai->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_LSB) { | |
564 | offset = sai->slot_width - sai->data_size; | |
565 | ||
566 | regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX, | |
567 | SAI_XSLOTR_FBOFF_MASK, | |
568 | SAI_XSLOTR_FBOFF_SET(offset)); | |
569 | } | |
570 | } | |
571 | ||
572 | static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, | |
573 | struct snd_pcm_hw_params *params) | |
574 | { | |
575 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | |
576 | int cr1, mask, div = 0; | |
03e78a24 | 577 | int sai_clk_rate, mclk_ratio, den, ret; |
578 | int version = sai->pdata->conf->version; | |
3e086edf | 579 | |
580 | if (!sai->mclk_rate) { | |
581 | dev_err(cpu_dai->dev, "Mclk rate is null\n"); | |
582 | return -EINVAL; | |
583 | } | |
584 | ||
585 | if (!(params_rate(params) % 11025)) | |
586 | clk_set_parent(sai->sai_ck, sai->pdata->clk_x11k); | |
587 | else | |
588 | clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k); | |
589 | sai_clk_rate = clk_get_rate(sai->sai_ck); | |
590 | ||
03e78a24 | 591 | if (STM_SAI_IS_F4(sai->pdata)) { |
592 | /* | |
593 | * mclk_rate = 256 * fs | |
594 | * MCKDIV = 0 if sai_ck < 3/2 * mclk_rate | |
595 | * MCKDIV = sai_ck / (2 * mclk_rate) otherwise | |
596 | */ | |
597 | if (2 * sai_clk_rate >= 3 * sai->mclk_rate) | |
598 | div = DIV_ROUND_CLOSEST(sai_clk_rate, | |
599 | 2 * sai->mclk_rate); | |
600 | } else { | |
601 | /* | |
602 | * TDM mode : | |
603 | * mclk on | |
604 | * MCKDIV = sai_ck / (ws x 256) (NOMCK=0. OSR=0) | |
605 | * MCKDIV = sai_ck / (ws x 512) (NOMCK=0. OSR=1) | |
606 | * mclk off | |
607 | * MCKDIV = sai_ck / (frl x ws) (NOMCK=1) | |
608 | * Note: NOMCK/NODIV correspond to same bit. | |
609 | */ | |
610 | if (sai->mclk_rate) { | |
611 | mclk_ratio = sai->mclk_rate / params_rate(params); | |
612 | if (mclk_ratio != 256) { | |
613 | if (mclk_ratio == 512) { | |
614 | mask = SAI_XCR1_OSR; | |
615 | cr1 = SAI_XCR1_OSR; | |
616 | } else { | |
617 | dev_err(cpu_dai->dev, | |
618 | "Wrong mclk ratio %d\n", | |
619 | mclk_ratio); | |
620 | return -EINVAL; | |
621 | } | |
622 | } | |
623 | div = DIV_ROUND_CLOSEST(sai_clk_rate, sai->mclk_rate); | |
624 | } else { | |
625 | /* mclk-fs not set, master clock not active. NOMCK=1 */ | |
626 | den = sai->fs_length * params_rate(params); | |
627 | div = DIV_ROUND_CLOSEST(sai_clk_rate, den); | |
628 | } | |
629 | } | |
3e086edf | 630 | |
03e78a24 | 631 | if (div > SAI_XCR1_MCKDIV_MAX(version)) { |
3e086edf | 632 | dev_err(cpu_dai->dev, "Divider %d out of range\n", div); |
633 | return -EINVAL; | |
634 | } | |
635 | dev_dbg(cpu_dai->dev, "SAI clock %d, divider %d\n", sai_clk_rate, div); | |
636 | ||
03e78a24 | 637 | mask = SAI_XCR1_MCKDIV_MASK(SAI_XCR1_MCKDIV_WIDTH(version)); |
81321fe9 | 638 | cr1 = SAI_XCR1_MCKDIV_SET(div); |
3e086edf | 639 | ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1); |
640 | if (ret < 0) { | |
641 | dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); | |
642 | return ret; | |
643 | } | |
644 | ||
645 | return 0; | |
646 | } | |
647 | ||
648 | static int stm32_sai_hw_params(struct snd_pcm_substream *substream, | |
649 | struct snd_pcm_hw_params *params, | |
650 | struct snd_soc_dai *cpu_dai) | |
651 | { | |
652 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | |
653 | int ret; | |
654 | ||
655 | sai->data_size = params_width(params); | |
656 | ||
657 | ret = stm32_sai_set_slots(cpu_dai); | |
658 | if (ret < 0) | |
659 | return ret; | |
660 | stm32_sai_set_frame(cpu_dai); | |
661 | ||
662 | ret = stm32_sai_set_config(cpu_dai, substream, params); | |
663 | if (ret) | |
664 | return ret; | |
665 | ||
666 | if (sai->master) | |
667 | ret = stm32_sai_configure_clock(cpu_dai, params); | |
668 | ||
669 | return ret; | |
670 | } | |
671 | ||
672 | static int stm32_sai_trigger(struct snd_pcm_substream *substream, int cmd, | |
673 | struct snd_soc_dai *cpu_dai) | |
674 | { | |
675 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | |
676 | int ret; | |
677 | ||
678 | switch (cmd) { | |
679 | case SNDRV_PCM_TRIGGER_START: | |
680 | case SNDRV_PCM_TRIGGER_RESUME: | |
681 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | |
682 | dev_dbg(cpu_dai->dev, "Enable DMA and SAI\n"); | |
683 | ||
684 | regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, | |
685 | SAI_XCR1_DMAEN, SAI_XCR1_DMAEN); | |
686 | ||
687 | /* Enable SAI */ | |
688 | ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, | |
689 | SAI_XCR1_SAIEN, SAI_XCR1_SAIEN); | |
690 | if (ret < 0) | |
691 | dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); | |
692 | break; | |
693 | case SNDRV_PCM_TRIGGER_SUSPEND: | |
694 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | |
695 | case SNDRV_PCM_TRIGGER_STOP: | |
696 | dev_dbg(cpu_dai->dev, "Disable DMA and SAI\n"); | |
697 | ||
698 | regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, | |
4fa17938 | 699 | SAI_XCR1_SAIEN, |
700 | (unsigned int)~SAI_XCR1_SAIEN); | |
3e086edf | 701 | |
702 | ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, | |
4fa17938 | 703 | SAI_XCR1_DMAEN, |
704 | (unsigned int)~SAI_XCR1_DMAEN); | |
3e086edf | 705 | if (ret < 0) |
706 | dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); | |
707 | break; | |
708 | default: | |
709 | return -EINVAL; | |
710 | } | |
711 | ||
712 | return ret; | |
713 | } | |
714 | ||
715 | static void stm32_sai_shutdown(struct snd_pcm_substream *substream, | |
716 | struct snd_soc_dai *cpu_dai) | |
717 | { | |
718 | struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); | |
719 | ||
720 | regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, SAI_XIMR_MASK, 0); | |
721 | ||
701a6ec3 | 722 | regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_NODIV, |
723 | SAI_XCR1_NODIV); | |
724 | ||
3e086edf | 725 | clk_disable_unprepare(sai->sai_ck); |
726 | sai->substream = NULL; | |
727 | } | |
728 | ||
729 | static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) | |
730 | { | |
731 | struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); | |
732 | ||
733 | sai->dma_params.addr = (dma_addr_t)(sai->phys_addr + STM_SAI_DR_REGX); | |
734 | sai->dma_params.maxburst = 1; | |
735 | /* Buswidth will be set by framework at runtime */ | |
736 | sai->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; | |
737 | ||
738 | if (STM_SAI_IS_PLAYBACK(sai)) | |
739 | snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params, NULL); | |
740 | else | |
741 | snd_soc_dai_init_dma_data(cpu_dai, NULL, &sai->dma_params); | |
742 | ||
743 | return 0; | |
744 | } | |
745 | ||
746 | static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = { | |
747 | .set_sysclk = stm32_sai_set_sysclk, | |
748 | .set_fmt = stm32_sai_set_dai_fmt, | |
749 | .set_tdm_slot = stm32_sai_set_dai_tdm_slot, | |
750 | .startup = stm32_sai_startup, | |
751 | .hw_params = stm32_sai_hw_params, | |
752 | .trigger = stm32_sai_trigger, | |
753 | .shutdown = stm32_sai_shutdown, | |
754 | }; | |
755 | ||
756 | static const struct snd_pcm_hardware stm32_sai_pcm_hw = { | |
757 | .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP, | |
758 | .buffer_bytes_max = 8 * PAGE_SIZE, | |
759 | .period_bytes_min = 1024, /* 5ms at 48kHz */ | |
760 | .period_bytes_max = PAGE_SIZE, | |
761 | .periods_min = 2, | |
762 | .periods_max = 8, | |
763 | }; | |
764 | ||
765 | static struct snd_soc_dai_driver stm32_sai_playback_dai[] = { | |
766 | { | |
767 | .probe = stm32_sai_dai_probe, | |
768 | .id = 1, /* avoid call to fmt_single_name() */ | |
769 | .playback = { | |
770 | .channels_min = 1, | |
771 | .channels_max = 2, | |
772 | .rate_min = 8000, | |
773 | .rate_max = 192000, | |
774 | .rates = SNDRV_PCM_RATE_CONTINUOUS, | |
775 | /* DMA does not support 24 bits transfers */ | |
776 | .formats = | |
777 | SNDRV_PCM_FMTBIT_S8 | | |
778 | SNDRV_PCM_FMTBIT_S16_LE | | |
779 | SNDRV_PCM_FMTBIT_S32_LE, | |
780 | }, | |
781 | .ops = &stm32_sai_pcm_dai_ops, | |
782 | } | |
783 | }; | |
784 | ||
785 | static struct snd_soc_dai_driver stm32_sai_capture_dai[] = { | |
786 | { | |
787 | .probe = stm32_sai_dai_probe, | |
788 | .id = 1, /* avoid call to fmt_single_name() */ | |
789 | .capture = { | |
790 | .channels_min = 1, | |
791 | .channels_max = 2, | |
792 | .rate_min = 8000, | |
793 | .rate_max = 192000, | |
794 | .rates = SNDRV_PCM_RATE_CONTINUOUS, | |
795 | /* DMA does not support 24 bits transfers */ | |
796 | .formats = | |
797 | SNDRV_PCM_FMTBIT_S8 | | |
798 | SNDRV_PCM_FMTBIT_S16_LE | | |
799 | SNDRV_PCM_FMTBIT_S32_LE, | |
800 | }, | |
801 | .ops = &stm32_sai_pcm_dai_ops, | |
802 | } | |
803 | }; | |
804 | ||
805 | static const struct snd_dmaengine_pcm_config stm32_sai_pcm_config = { | |
806 | .pcm_hardware = &stm32_sai_pcm_hw, | |
807 | .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, | |
808 | }; | |
809 | ||
810 | static const struct snd_soc_component_driver stm32_component = { | |
811 | .name = "stm32-sai", | |
812 | }; | |
813 | ||
814 | static const struct of_device_id stm32_sai_sub_ids[] = { | |
815 | { .compatible = "st,stm32-sai-sub-a", | |
816 | .data = (void *)STM_SAI_A_ID}, | |
817 | { .compatible = "st,stm32-sai-sub-b", | |
818 | .data = (void *)STM_SAI_B_ID}, | |
819 | {} | |
820 | }; | |
821 | MODULE_DEVICE_TABLE(of, stm32_sai_sub_ids); | |
822 | ||
823 | static int stm32_sai_sub_parse_of(struct platform_device *pdev, | |
824 | struct stm32_sai_sub_data *sai) | |
825 | { | |
826 | struct device_node *np = pdev->dev.of_node; | |
827 | struct resource *res; | |
828 | void __iomem *base; | |
829 | ||
830 | if (!np) | |
831 | return -ENODEV; | |
832 | ||
833 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
3e086edf | 834 | base = devm_ioremap_resource(&pdev->dev, res); |
835 | if (IS_ERR(base)) | |
836 | return PTR_ERR(base); | |
837 | ||
838 | sai->phys_addr = res->start; | |
03e78a24 | 839 | |
840 | sai->regmap_config = &stm32_sai_sub_regmap_config_f4; | |
841 | /* Note: PDM registers not available for H7 sub-block B */ | |
842 | if (STM_SAI_IS_H7(sai->pdata) && STM_SAI_IS_SUB_A(sai)) | |
843 | sai->regmap_config = &stm32_sai_sub_regmap_config_h7; | |
844 | ||
845 | sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "sai_ck", | |
846 | base, sai->regmap_config); | |
847 | if (IS_ERR(sai->regmap)) { | |
848 | dev_err(&pdev->dev, "Failed to initialize MMIO\n"); | |
849 | return PTR_ERR(sai->regmap); | |
850 | } | |
3e086edf | 851 | |
852 | /* Get direction property */ | |
853 | if (of_property_match_string(np, "dma-names", "tx") >= 0) { | |
854 | sai->dir = SNDRV_PCM_STREAM_PLAYBACK; | |
855 | } else if (of_property_match_string(np, "dma-names", "rx") >= 0) { | |
856 | sai->dir = SNDRV_PCM_STREAM_CAPTURE; | |
857 | } else { | |
858 | dev_err(&pdev->dev, "Unsupported direction\n"); | |
859 | return -EINVAL; | |
860 | } | |
861 | ||
862 | sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck"); | |
863 | if (IS_ERR(sai->sai_ck)) { | |
602fdadc | 864 | dev_err(&pdev->dev, "Missing kernel clock sai_ck\n"); |
3e086edf | 865 | return PTR_ERR(sai->sai_ck); |
866 | } | |
867 | ||
868 | return 0; | |
869 | } | |
870 | ||
871 | static int stm32_sai_sub_dais_init(struct platform_device *pdev, | |
872 | struct stm32_sai_sub_data *sai) | |
873 | { | |
874 | sai->cpu_dai_drv = devm_kzalloc(&pdev->dev, | |
875 | sizeof(struct snd_soc_dai_driver), | |
876 | GFP_KERNEL); | |
877 | if (!sai->cpu_dai_drv) | |
878 | return -ENOMEM; | |
879 | ||
880 | sai->cpu_dai_drv->name = dev_name(&pdev->dev); | |
881 | if (STM_SAI_IS_PLAYBACK(sai)) { | |
882 | memcpy(sai->cpu_dai_drv, &stm32_sai_playback_dai, | |
883 | sizeof(stm32_sai_playback_dai)); | |
884 | sai->cpu_dai_drv->playback.stream_name = sai->cpu_dai_drv->name; | |
885 | } else { | |
886 | memcpy(sai->cpu_dai_drv, &stm32_sai_capture_dai, | |
887 | sizeof(stm32_sai_capture_dai)); | |
888 | sai->cpu_dai_drv->capture.stream_name = sai->cpu_dai_drv->name; | |
889 | } | |
890 | ||
891 | return 0; | |
892 | } | |
893 | ||
894 | static int stm32_sai_sub_probe(struct platform_device *pdev) | |
895 | { | |
896 | struct stm32_sai_sub_data *sai; | |
897 | const struct of_device_id *of_id; | |
898 | int ret; | |
899 | ||
900 | sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); | |
901 | if (!sai) | |
902 | return -ENOMEM; | |
903 | ||
904 | of_id = of_match_device(stm32_sai_sub_ids, &pdev->dev); | |
905 | if (!of_id) | |
906 | return -EINVAL; | |
907 | sai->id = (uintptr_t)of_id->data; | |
908 | ||
909 | sai->pdev = pdev; | |
910 | platform_set_drvdata(pdev, sai); | |
911 | ||
912 | sai->pdata = dev_get_drvdata(pdev->dev.parent); | |
913 | if (!sai->pdata) { | |
914 | dev_err(&pdev->dev, "Parent device data not available\n"); | |
915 | return -EINVAL; | |
916 | } | |
917 | ||
918 | ret = stm32_sai_sub_parse_of(pdev, sai); | |
919 | if (ret) | |
920 | return ret; | |
921 | ||
922 | ret = stm32_sai_sub_dais_init(pdev, sai); | |
923 | if (ret) | |
924 | return ret; | |
925 | ||
926 | ret = devm_request_irq(&pdev->dev, sai->pdata->irq, stm32_sai_isr, | |
927 | IRQF_SHARED, dev_name(&pdev->dev), sai); | |
928 | if (ret) { | |
602fdadc | 929 | dev_err(&pdev->dev, "IRQ request returned %d\n", ret); |
3e086edf | 930 | return ret; |
931 | } | |
932 | ||
933 | ret = devm_snd_soc_register_component(&pdev->dev, &stm32_component, | |
934 | sai->cpu_dai_drv, 1); | |
935 | if (ret) | |
936 | return ret; | |
937 | ||
938 | ret = devm_snd_dmaengine_pcm_register(&pdev->dev, | |
939 | &stm32_sai_pcm_config, 0); | |
940 | if (ret) { | |
602fdadc | 941 | dev_err(&pdev->dev, "Could not register pcm dma\n"); |
3e086edf | 942 | return ret; |
943 | } | |
944 | ||
945 | return 0; | |
946 | } | |
947 | ||
948 | static struct platform_driver stm32_sai_sub_driver = { | |
949 | .driver = { | |
950 | .name = "st,stm32-sai-sub", | |
951 | .of_match_table = stm32_sai_sub_ids, | |
952 | }, | |
953 | .probe = stm32_sai_sub_probe, | |
954 | }; | |
955 | ||
956 | module_platform_driver(stm32_sai_sub_driver); | |
957 | ||
958 | MODULE_DESCRIPTION("STM32 Soc SAI sub-block Interface"); | |
602fdadc | 959 | MODULE_AUTHOR("Olivier Moysan <olivier.moysan@st.com>"); |
3e086edf | 960 | MODULE_ALIAS("platform:st,stm32-sai-sub"); |
961 | MODULE_LICENSE("GPL v2"); |