diff options
Diffstat (limited to 'sound/soc/stm/stm32_i2s.c')
-rw-r--r-- | sound/soc/stm/stm32_i2s.c | 946 |
1 files changed, 946 insertions, 0 deletions
diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c new file mode 100644 index 000000000000..8052629a89df --- /dev/null +++ b/sound/soc/stm/stm32_i2s.c | |||
@@ -0,0 +1,946 @@ | |||
1 | /* | ||
2 | * STM32 ALSA SoC Digital Audio Interface (I2S) driver. | ||
3 | * | ||
4 | * Copyright (C) 2017, 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/delay.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/of_irq.h> | ||
23 | #include <linux/of_platform.h> | ||
24 | #include <linux/regmap.h> | ||
25 | #include <linux/reset.h> | ||
26 | #include <linux/spinlock.h> | ||
27 | |||
28 | #include <sound/dmaengine_pcm.h> | ||
29 | #include <sound/pcm_params.h> | ||
30 | |||
31 | #define STM32_I2S_CR1_REG 0x0 | ||
32 | #define STM32_I2S_CFG1_REG 0x08 | ||
33 | #define STM32_I2S_CFG2_REG 0x0C | ||
34 | #define STM32_I2S_IER_REG 0x10 | ||
35 | #define STM32_I2S_SR_REG 0x14 | ||
36 | #define STM32_I2S_IFCR_REG 0x18 | ||
37 | #define STM32_I2S_TXDR_REG 0X20 | ||
38 | #define STM32_I2S_RXDR_REG 0x30 | ||
39 | #define STM32_I2S_CGFR_REG 0X50 | ||
40 | |||
41 | /* Bit definition for SPI2S_CR1 register */ | ||
42 | #define I2S_CR1_SPE BIT(0) | ||
43 | #define I2S_CR1_CSTART BIT(9) | ||
44 | #define I2S_CR1_CSUSP BIT(10) | ||
45 | #define I2S_CR1_HDDIR BIT(11) | ||
46 | #define I2S_CR1_SSI BIT(12) | ||
47 | #define I2S_CR1_CRC33_17 BIT(13) | ||
48 | #define I2S_CR1_RCRCI BIT(14) | ||
49 | #define I2S_CR1_TCRCI BIT(15) | ||
50 | |||
51 | /* Bit definition for SPI_CFG2 register */ | ||
52 | #define I2S_CFG2_IOSWP_SHIFT 15 | ||
53 | #define I2S_CFG2_IOSWP BIT(I2S_CFG2_IOSWP_SHIFT) | ||
54 | #define I2S_CFG2_LSBFRST BIT(23) | ||
55 | #define I2S_CFG2_AFCNTR BIT(31) | ||
56 | |||
57 | /* Bit definition for SPI_CFG1 register */ | ||
58 | #define I2S_CFG1_FTHVL_SHIFT 5 | ||
59 | #define I2S_CFG1_FTHVL_MASK GENMASK(8, I2S_CFG1_FTHVL_SHIFT) | ||
60 | #define I2S_CFG1_FTHVL_SET(x) ((x) << I2S_CFG1_FTHVL_SHIFT) | ||
61 | |||
62 | #define I2S_CFG1_TXDMAEN BIT(15) | ||
63 | #define I2S_CFG1_RXDMAEN BIT(14) | ||
64 | |||
65 | /* Bit definition for SPI2S_IER register */ | ||
66 | #define I2S_IER_RXPIE BIT(0) | ||
67 | #define I2S_IER_TXPIE BIT(1) | ||
68 | #define I2S_IER_DPXPIE BIT(2) | ||
69 | #define I2S_IER_EOTIE BIT(3) | ||
70 | #define I2S_IER_TXTFIE BIT(4) | ||
71 | #define I2S_IER_UDRIE BIT(5) | ||
72 | #define I2S_IER_OVRIE BIT(6) | ||
73 | #define I2S_IER_CRCEIE BIT(7) | ||
74 | #define I2S_IER_TIFREIE BIT(8) | ||
75 | #define I2S_IER_MODFIE BIT(9) | ||
76 | #define I2S_IER_TSERFIE BIT(10) | ||
77 | |||
78 | /* Bit definition for SPI2S_SR register */ | ||
79 | #define I2S_SR_RXP BIT(0) | ||
80 | #define I2S_SR_TXP BIT(1) | ||
81 | #define I2S_SR_DPXP BIT(2) | ||
82 | #define I2S_SR_EOT BIT(3) | ||
83 | #define I2S_SR_TXTF BIT(4) | ||
84 | #define I2S_SR_UDR BIT(5) | ||
85 | #define I2S_SR_OVR BIT(6) | ||
86 | #define I2S_SR_CRCERR BIT(7) | ||
87 | #define I2S_SR_TIFRE BIT(8) | ||
88 | #define I2S_SR_MODF BIT(9) | ||
89 | #define I2S_SR_TSERF BIT(10) | ||
90 | #define I2S_SR_SUSP BIT(11) | ||
91 | #define I2S_SR_TXC BIT(12) | ||
92 | #define I2S_SR_RXPLVL GENMASK(14, 13) | ||
93 | #define I2S_SR_RXWNE BIT(15) | ||
94 | |||
95 | #define I2S_SR_MASK GENMASK(15, 0) | ||
96 | |||
97 | /* Bit definition for SPI_IFCR register */ | ||
98 | #define I2S_IFCR_EOTC BIT(3) | ||
99 | #define I2S_IFCR_TXTFC BIT(4) | ||
100 | #define I2S_IFCR_UDRC BIT(5) | ||
101 | #define I2S_IFCR_OVRC BIT(6) | ||
102 | #define I2S_IFCR_CRCEC BIT(7) | ||
103 | #define I2S_IFCR_TIFREC BIT(8) | ||
104 | #define I2S_IFCR_MODFC BIT(9) | ||
105 | #define I2S_IFCR_TSERFC BIT(10) | ||
106 | #define I2S_IFCR_SUSPC BIT(11) | ||
107 | |||
108 | #define I2S_IFCR_MASK GENMASK(11, 3) | ||
109 | |||
110 | /* Bit definition for SPI_I2SCGFR register */ | ||
111 | #define I2S_CGFR_I2SMOD BIT(0) | ||
112 | |||
113 | #define I2S_CGFR_I2SCFG_SHIFT 1 | ||
114 | #define I2S_CGFR_I2SCFG_MASK GENMASK(3, I2S_CGFR_I2SCFG_SHIFT) | ||
115 | #define I2S_CGFR_I2SCFG_SET(x) ((x) << I2S_CGFR_I2SCFG_SHIFT) | ||
116 | |||
117 | #define I2S_CGFR_I2SSTD_SHIFT 4 | ||
118 | #define I2S_CGFR_I2SSTD_MASK GENMASK(5, I2S_CGFR_I2SSTD_SHIFT) | ||
119 | #define I2S_CGFR_I2SSTD_SET(x) ((x) << I2S_CGFR_I2SSTD_SHIFT) | ||
120 | |||
121 | #define I2S_CGFR_PCMSYNC BIT(7) | ||
122 | |||
123 | #define I2S_CGFR_DATLEN_SHIFT 8 | ||
124 | #define I2S_CGFR_DATLEN_MASK GENMASK(9, I2S_CGFR_DATLEN_SHIFT) | ||
125 | #define I2S_CGFR_DATLEN_SET(x) ((x) << I2S_CGFR_DATLEN_SHIFT) | ||
126 | |||
127 | #define I2S_CGFR_CHLEN_SHIFT 10 | ||
128 | #define I2S_CGFR_CHLEN BIT(I2S_CGFR_CHLEN_SHIFT) | ||
129 | #define I2S_CGFR_CKPOL BIT(11) | ||
130 | #define I2S_CGFR_FIXCH BIT(12) | ||
131 | #define I2S_CGFR_WSINV BIT(13) | ||
132 | #define I2S_CGFR_DATFMT BIT(14) | ||
133 | |||
134 | #define I2S_CGFR_I2SDIV_SHIFT 16 | ||
135 | #define I2S_CGFR_I2SDIV_BIT_H 23 | ||
136 | #define I2S_CGFR_I2SDIV_MASK GENMASK(I2S_CGFR_I2SDIV_BIT_H,\ | ||
137 | I2S_CGFR_I2SDIV_SHIFT) | ||
138 | #define I2S_CGFR_I2SDIV_SET(x) ((x) << I2S_CGFR_I2SDIV_SHIFT) | ||
139 | #define I2S_CGFR_I2SDIV_MAX ((1 << (I2S_CGFR_I2SDIV_BIT_H -\ | ||
140 | I2S_CGFR_I2SDIV_SHIFT)) - 1) | ||
141 | |||
142 | #define I2S_CGFR_ODD_SHIFT 24 | ||
143 | #define I2S_CGFR_ODD BIT(I2S_CGFR_ODD_SHIFT) | ||
144 | #define I2S_CGFR_MCKOE BIT(25) | ||
145 | |||
146 | enum i2s_master_mode { | ||
147 | I2S_MS_NOT_SET, | ||
148 | I2S_MS_MASTER, | ||
149 | I2S_MS_SLAVE, | ||
150 | }; | ||
151 | |||
152 | enum i2s_mode { | ||
153 | I2S_I2SMOD_TX_SLAVE, | ||
154 | I2S_I2SMOD_RX_SLAVE, | ||
155 | I2S_I2SMOD_TX_MASTER, | ||
156 | I2S_I2SMOD_RX_MASTER, | ||
157 | I2S_I2SMOD_FD_SLAVE, | ||
158 | I2S_I2SMOD_FD_MASTER, | ||
159 | }; | ||
160 | |||
161 | enum i2s_fifo_th { | ||
162 | I2S_FIFO_TH_NONE, | ||
163 | I2S_FIFO_TH_ONE_QUARTER, | ||
164 | I2S_FIFO_TH_HALF, | ||
165 | I2S_FIFO_TH_THREE_QUARTER, | ||
166 | I2S_FIFO_TH_FULL, | ||
167 | }; | ||
168 | |||
169 | enum i2s_std { | ||
170 | I2S_STD_I2S, | ||
171 | I2S_STD_LEFT_J, | ||
172 | I2S_STD_RIGHT_J, | ||
173 | I2S_STD_DSP, | ||
174 | }; | ||
175 | |||
176 | enum i2s_datlen { | ||
177 | I2S_I2SMOD_DATLEN_16, | ||
178 | I2S_I2SMOD_DATLEN_24, | ||
179 | I2S_I2SMOD_DATLEN_32, | ||
180 | }; | ||
181 | |||
182 | #define STM32_I2S_DAI_NAME_SIZE 20 | ||
183 | #define STM32_I2S_FIFO_SIZE 16 | ||
184 | |||
185 | #define STM32_I2S_IS_MASTER(x) ((x)->ms_flg == I2S_MS_MASTER) | ||
186 | #define STM32_I2S_IS_SLAVE(x) ((x)->ms_flg == I2S_MS_SLAVE) | ||
187 | |||
188 | /** | ||
189 | * @regmap_conf: I2S register map configuration pointer | ||
190 | * @egmap: I2S register map pointer | ||
191 | * @pdev: device data pointer | ||
192 | * @dai_drv: DAI driver pointer | ||
193 | * @dma_data_tx: dma configuration data for tx channel | ||
194 | * @dma_data_rx: dma configuration data for tx channel | ||
195 | * @substream: PCM substream data pointer | ||
196 | * @i2sclk: kernel clock feeding the I2S clock generator | ||
197 | * @pclk: peripheral clock driving bus interface | ||
198 | * @x8kclk: I2S parent clock for sampling frequencies multiple of 8kHz | ||
199 | * @x11kclk: I2S parent clock for sampling frequencies multiple of 11kHz | ||
200 | * @base: mmio register base virtual address | ||
201 | * @phys_addr: I2S registers physical base address | ||
202 | * @lock_fd: lock to manage race conditions in full duplex mode | ||
203 | * @dais_name: DAI name | ||
204 | * @mclk_rate: master clock frequency (Hz) | ||
205 | * @fmt: DAI protocol | ||
206 | * @refcount: keep count of opened streams on I2S | ||
207 | * @ms_flg: master mode flag. | ||
208 | */ | ||
209 | struct stm32_i2s_data { | ||
210 | const struct regmap_config *regmap_conf; | ||
211 | struct regmap *regmap; | ||
212 | struct platform_device *pdev; | ||
213 | struct snd_soc_dai_driver *dai_drv; | ||
214 | struct snd_dmaengine_dai_dma_data dma_data_tx; | ||
215 | struct snd_dmaengine_dai_dma_data dma_data_rx; | ||
216 | struct snd_pcm_substream *substream; | ||
217 | struct clk *i2sclk; | ||
218 | struct clk *pclk; | ||
219 | struct clk *x8kclk; | ||
220 | struct clk *x11kclk; | ||
221 | void __iomem *base; | ||
222 | dma_addr_t phys_addr; | ||
223 | spinlock_t lock_fd; /* Manage race conditions for full duplex */ | ||
224 | char dais_name[STM32_I2S_DAI_NAME_SIZE]; | ||
225 | unsigned int mclk_rate; | ||
226 | unsigned int fmt; | ||
227 | int refcount; | ||
228 | int ms_flg; | ||
229 | }; | ||
230 | |||
231 | static irqreturn_t stm32_i2s_isr(int irq, void *devid) | ||
232 | { | ||
233 | struct stm32_i2s_data *i2s = (struct stm32_i2s_data *)devid; | ||
234 | struct platform_device *pdev = i2s->pdev; | ||
235 | u32 sr, ier; | ||
236 | unsigned long flags; | ||
237 | int err = 0; | ||
238 | |||
239 | regmap_read(i2s->regmap, STM32_I2S_SR_REG, &sr); | ||
240 | regmap_read(i2s->regmap, STM32_I2S_IER_REG, &ier); | ||
241 | |||
242 | flags = sr & ier; | ||
243 | if (!flags) { | ||
244 | dev_dbg(&pdev->dev, "Spurious IRQ sr=0x%08x, ier=0x%08x\n", | ||
245 | sr, ier); | ||
246 | return IRQ_NONE; | ||
247 | } | ||
248 | |||
249 | regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, | ||
250 | I2S_IFCR_MASK, flags); | ||
251 | |||
252 | if (flags & I2S_SR_OVR) { | ||
253 | dev_dbg(&pdev->dev, "Overrun\n"); | ||
254 | err = 1; | ||
255 | } | ||
256 | |||
257 | if (flags & I2S_SR_UDR) { | ||
258 | dev_dbg(&pdev->dev, "Underrun\n"); | ||
259 | err = 1; | ||
260 | } | ||
261 | |||
262 | if (flags & I2S_SR_TIFRE) | ||
263 | dev_dbg(&pdev->dev, "Frame error\n"); | ||
264 | |||
265 | if (err) | ||
266 | snd_pcm_stop_xrun(i2s->substream); | ||
267 | |||
268 | return IRQ_HANDLED; | ||
269 | } | ||
270 | |||
271 | static bool stm32_i2s_readable_reg(struct device *dev, unsigned int reg) | ||
272 | { | ||
273 | switch (reg) { | ||
274 | case STM32_I2S_CR1_REG: | ||
275 | case STM32_I2S_CFG1_REG: | ||
276 | case STM32_I2S_CFG2_REG: | ||
277 | case STM32_I2S_IER_REG: | ||
278 | case STM32_I2S_SR_REG: | ||
279 | case STM32_I2S_IFCR_REG: | ||
280 | case STM32_I2S_TXDR_REG: | ||
281 | case STM32_I2S_RXDR_REG: | ||
282 | case STM32_I2S_CGFR_REG: | ||
283 | return true; | ||
284 | default: | ||
285 | return false; | ||
286 | } | ||
287 | } | ||
288 | |||
289 | static bool stm32_i2s_volatile_reg(struct device *dev, unsigned int reg) | ||
290 | { | ||
291 | switch (reg) { | ||
292 | case STM32_I2S_TXDR_REG: | ||
293 | case STM32_I2S_RXDR_REG: | ||
294 | return true; | ||
295 | default: | ||
296 | return false; | ||
297 | } | ||
298 | } | ||
299 | |||
300 | static bool stm32_i2s_writeable_reg(struct device *dev, unsigned int reg) | ||
301 | { | ||
302 | switch (reg) { | ||
303 | case STM32_I2S_CR1_REG: | ||
304 | case STM32_I2S_CFG1_REG: | ||
305 | case STM32_I2S_CFG2_REG: | ||
306 | case STM32_I2S_IER_REG: | ||
307 | case STM32_I2S_IFCR_REG: | ||
308 | case STM32_I2S_TXDR_REG: | ||
309 | case STM32_I2S_CGFR_REG: | ||
310 | return true; | ||
311 | default: | ||
312 | return false; | ||
313 | } | ||
314 | } | ||
315 | |||
316 | static int stm32_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) | ||
317 | { | ||
318 | struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); | ||
319 | u32 cgfr; | ||
320 | u32 cgfr_mask = I2S_CGFR_I2SSTD_MASK | I2S_CGFR_CKPOL | | ||
321 | I2S_CGFR_WSINV | I2S_CGFR_I2SCFG_MASK; | ||
322 | |||
323 | dev_dbg(cpu_dai->dev, "fmt %x\n", fmt); | ||
324 | |||
325 | /* | ||
326 | * winv = 0 : default behavior (high/low) for all standards | ||
327 | * ckpol = 0 for all standards. | ||
328 | */ | ||
329 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
330 | case SND_SOC_DAIFMT_I2S: | ||
331 | cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_I2S); | ||
332 | break; | ||
333 | case SND_SOC_DAIFMT_MSB: | ||
334 | cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_LEFT_J); | ||
335 | break; | ||
336 | case SND_SOC_DAIFMT_LSB: | ||
337 | cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_RIGHT_J); | ||
338 | break; | ||
339 | case SND_SOC_DAIFMT_DSP_A: | ||
340 | cgfr = I2S_CGFR_I2SSTD_SET(I2S_STD_DSP); | ||
341 | break; | ||
342 | /* DSP_B not mapped on I2S PCM long format. 1 bit offset does not fit */ | ||
343 | default: | ||
344 | dev_err(cpu_dai->dev, "Unsupported protocol %#x\n", | ||
345 | fmt & SND_SOC_DAIFMT_FORMAT_MASK); | ||
346 | return -EINVAL; | ||
347 | } | ||
348 | |||
349 | /* DAI clock strobing */ | ||
350 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
351 | case SND_SOC_DAIFMT_NB_NF: | ||
352 | break; | ||
353 | case SND_SOC_DAIFMT_IB_NF: | ||
354 | cgfr |= I2S_CGFR_CKPOL; | ||
355 | break; | ||
356 | case SND_SOC_DAIFMT_NB_IF: | ||
357 | cgfr |= I2S_CGFR_WSINV; | ||
358 | break; | ||
359 | case SND_SOC_DAIFMT_IB_IF: | ||
360 | cgfr |= I2S_CGFR_CKPOL; | ||
361 | cgfr |= I2S_CGFR_WSINV; | ||
362 | break; | ||
363 | default: | ||
364 | dev_err(cpu_dai->dev, "Unsupported strobing %#x\n", | ||
365 | fmt & SND_SOC_DAIFMT_INV_MASK); | ||
366 | return -EINVAL; | ||
367 | } | ||
368 | |||
369 | /* DAI clock master masks */ | ||
370 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
371 | case SND_SOC_DAIFMT_CBM_CFM: | ||
372 | i2s->ms_flg = I2S_MS_SLAVE; | ||
373 | break; | ||
374 | case SND_SOC_DAIFMT_CBS_CFS: | ||
375 | i2s->ms_flg = I2S_MS_MASTER; | ||
376 | break; | ||
377 | default: | ||
378 | dev_err(cpu_dai->dev, "Unsupported mode %#x\n", | ||
379 | fmt & SND_SOC_DAIFMT_MASTER_MASK); | ||
380 | return -EINVAL; | ||
381 | } | ||
382 | |||
383 | i2s->fmt = fmt; | ||
384 | return regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, | ||
385 | cgfr_mask, cgfr); | ||
386 | } | ||
387 | |||
388 | static int stm32_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, | ||
389 | int clk_id, unsigned int freq, int dir) | ||
390 | { | ||
391 | struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); | ||
392 | |||
393 | dev_dbg(cpu_dai->dev, "I2S MCLK frequency is %uHz\n", freq); | ||
394 | |||
395 | if ((dir == SND_SOC_CLOCK_OUT) && STM32_I2S_IS_MASTER(i2s)) { | ||
396 | i2s->mclk_rate = freq; | ||
397 | |||
398 | /* Enable master clock if master mode and mclk-fs are set */ | ||
399 | return regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, | ||
400 | I2S_CGFR_MCKOE, I2S_CGFR_MCKOE); | ||
401 | } | ||
402 | |||
403 | return 0; | ||
404 | } | ||
405 | |||
406 | static int stm32_i2s_configure_clock(struct snd_soc_dai *cpu_dai, | ||
407 | struct snd_pcm_hw_params *params) | ||
408 | { | ||
409 | struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); | ||
410 | unsigned long i2s_clock_rate; | ||
411 | unsigned int tmp, div, real_div, nb_bits, frame_len; | ||
412 | unsigned int rate = params_rate(params); | ||
413 | int ret; | ||
414 | u32 cgfr, cgfr_mask; | ||
415 | bool odd; | ||
416 | |||
417 | if (!(rate % 11025)) | ||
418 | clk_set_parent(i2s->i2sclk, i2s->x11kclk); | ||
419 | else | ||
420 | clk_set_parent(i2s->i2sclk, i2s->x8kclk); | ||
421 | i2s_clock_rate = clk_get_rate(i2s->i2sclk); | ||
422 | |||
423 | /* | ||
424 | * mckl = mclk_ratio x ws | ||
425 | * i2s mode : mclk_ratio = 256 | ||
426 | * dsp mode : mclk_ratio = 128 | ||
427 | * | ||
428 | * mclk on | ||
429 | * i2s mode : div = i2s_clk / (mclk_ratio * ws) | ||
430 | * dsp mode : div = i2s_clk / (mclk_ratio * ws) | ||
431 | * mclk off | ||
432 | * i2s mode : div = i2s_clk / (nb_bits x ws) | ||
433 | * dsp mode : div = i2s_clk / (nb_bits x ws) | ||
434 | */ | ||
435 | if (i2s->mclk_rate) { | ||
436 | tmp = DIV_ROUND_CLOSEST(i2s_clock_rate, i2s->mclk_rate); | ||
437 | } else { | ||
438 | frame_len = 32; | ||
439 | if ((i2s->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == | ||
440 | SND_SOC_DAIFMT_DSP_A) | ||
441 | frame_len = 16; | ||
442 | |||
443 | /* master clock not enabled */ | ||
444 | ret = regmap_read(i2s->regmap, STM32_I2S_CGFR_REG, &cgfr); | ||
445 | if (ret < 0) | ||
446 | return ret; | ||
447 | |||
448 | nb_bits = frame_len * ((cgfr & I2S_CGFR_CHLEN) + 1); | ||
449 | tmp = DIV_ROUND_CLOSEST(i2s_clock_rate, (nb_bits * rate)); | ||
450 | } | ||
451 | |||
452 | /* Check the parity of the divider */ | ||
453 | odd = tmp & 0x1; | ||
454 | |||
455 | /* Compute the div prescaler */ | ||
456 | div = tmp >> 1; | ||
457 | |||
458 | cgfr = I2S_CGFR_I2SDIV_SET(div) | (odd << I2S_CGFR_ODD_SHIFT); | ||
459 | cgfr_mask = I2S_CGFR_I2SDIV_MASK | I2S_CGFR_ODD; | ||
460 | |||
461 | real_div = ((2 * div) + odd); | ||
462 | dev_dbg(cpu_dai->dev, "I2S clk: %ld, SCLK: %d\n", | ||
463 | i2s_clock_rate, rate); | ||
464 | dev_dbg(cpu_dai->dev, "Divider: 2*%d(div)+%d(odd) = %d\n", | ||
465 | div, odd, real_div); | ||
466 | |||
467 | if (((div == 1) && odd) || (div > I2S_CGFR_I2SDIV_MAX)) { | ||
468 | dev_err(cpu_dai->dev, "Wrong divider setting\n"); | ||
469 | return -EINVAL; | ||
470 | } | ||
471 | |||
472 | if (!div && !odd) | ||
473 | dev_warn(cpu_dai->dev, "real divider forced to 1\n"); | ||
474 | |||
475 | ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, | ||
476 | cgfr_mask, cgfr); | ||
477 | if (ret < 0) | ||
478 | return ret; | ||
479 | |||
480 | /* Set bitclock and frameclock to their inactive state */ | ||
481 | return regmap_update_bits(i2s->regmap, STM32_I2S_CFG2_REG, | ||
482 | I2S_CFG2_AFCNTR, I2S_CFG2_AFCNTR); | ||
483 | } | ||
484 | |||
485 | static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai, | ||
486 | struct snd_pcm_hw_params *params, | ||
487 | struct snd_pcm_substream *substream) | ||
488 | { | ||
489 | struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); | ||
490 | int format = params_width(params); | ||
491 | u32 cfgr, cfgr_mask, cfg1, cfg1_mask; | ||
492 | unsigned int fthlv; | ||
493 | int ret; | ||
494 | |||
495 | if ((params_channels(params) == 1) && | ||
496 | ((i2s->fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_DSP_A)) { | ||
497 | dev_err(cpu_dai->dev, "Mono mode supported only by DSP_A\n"); | ||
498 | return -EINVAL; | ||
499 | } | ||
500 | |||
501 | switch (format) { | ||
502 | case 16: | ||
503 | cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_16); | ||
504 | cfgr_mask = I2S_CGFR_DATLEN_MASK; | ||
505 | break; | ||
506 | case 32: | ||
507 | cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_32) | | ||
508 | I2S_CGFR_CHLEN; | ||
509 | cfgr_mask = I2S_CGFR_DATLEN_MASK | I2S_CGFR_CHLEN; | ||
510 | break; | ||
511 | default: | ||
512 | dev_err(cpu_dai->dev, "Unexpected format %d", format); | ||
513 | return -EINVAL; | ||
514 | } | ||
515 | |||
516 | if (STM32_I2S_IS_SLAVE(i2s)) { | ||
517 | cfgr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_FD_SLAVE); | ||
518 | |||
519 | /* As data length is either 16 or 32 bits, fixch always set */ | ||
520 | cfgr |= I2S_CGFR_FIXCH; | ||
521 | cfgr_mask |= I2S_CGFR_FIXCH; | ||
522 | } else { | ||
523 | cfgr |= I2S_CGFR_I2SCFG_SET(I2S_I2SMOD_FD_MASTER); | ||
524 | } | ||
525 | cfgr_mask |= I2S_CGFR_I2SCFG_MASK; | ||
526 | |||
527 | ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, | ||
528 | cfgr_mask, cfgr); | ||
529 | if (ret < 0) | ||
530 | return ret; | ||
531 | |||
532 | cfg1 = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; | ||
533 | cfg1_mask = cfg1; | ||
534 | |||
535 | fthlv = STM32_I2S_FIFO_SIZE * I2S_FIFO_TH_ONE_QUARTER / 4; | ||
536 | cfg1 |= I2S_CFG1_FTHVL_SET(fthlv - 1); | ||
537 | cfg1_mask |= I2S_CFG1_FTHVL_MASK; | ||
538 | |||
539 | return regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, | ||
540 | cfg1_mask, cfg1); | ||
541 | } | ||
542 | |||
543 | static int stm32_i2s_startup(struct snd_pcm_substream *substream, | ||
544 | struct snd_soc_dai *cpu_dai) | ||
545 | { | ||
546 | struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); | ||
547 | |||
548 | i2s->substream = substream; | ||
549 | |||
550 | spin_lock(&i2s->lock_fd); | ||
551 | i2s->refcount++; | ||
552 | spin_unlock(&i2s->lock_fd); | ||
553 | |||
554 | return regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, | ||
555 | I2S_IFCR_MASK, I2S_IFCR_MASK); | ||
556 | } | ||
557 | |||
558 | static int stm32_i2s_hw_params(struct snd_pcm_substream *substream, | ||
559 | struct snd_pcm_hw_params *params, | ||
560 | struct snd_soc_dai *cpu_dai) | ||
561 | { | ||
562 | struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); | ||
563 | int ret; | ||
564 | |||
565 | ret = stm32_i2s_configure(cpu_dai, params, substream); | ||
566 | if (ret < 0) { | ||
567 | dev_err(cpu_dai->dev, "Configuration returned error %d\n", ret); | ||
568 | return ret; | ||
569 | } | ||
570 | |||
571 | if (STM32_I2S_IS_MASTER(i2s)) | ||
572 | ret = stm32_i2s_configure_clock(cpu_dai, params); | ||
573 | |||
574 | return ret; | ||
575 | } | ||
576 | |||
577 | static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, | ||
578 | struct snd_soc_dai *cpu_dai) | ||
579 | { | ||
580 | struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); | ||
581 | bool playback_flg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); | ||
582 | u32 cfg1_mask, ier; | ||
583 | int ret; | ||
584 | |||
585 | switch (cmd) { | ||
586 | case SNDRV_PCM_TRIGGER_START: | ||
587 | case SNDRV_PCM_TRIGGER_RESUME: | ||
588 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
589 | /* Enable i2s */ | ||
590 | dev_dbg(cpu_dai->dev, "start I2S\n"); | ||
591 | |||
592 | ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG, | ||
593 | I2S_CR1_SPE, I2S_CR1_SPE); | ||
594 | if (ret < 0) { | ||
595 | dev_err(cpu_dai->dev, "Error %d enabling I2S\n", ret); | ||
596 | return ret; | ||
597 | } | ||
598 | |||
599 | ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG, | ||
600 | I2S_CR1_CSTART, I2S_CR1_CSTART); | ||
601 | if (ret < 0) { | ||
602 | dev_err(cpu_dai->dev, "Error %d starting I2S\n", ret); | ||
603 | return ret; | ||
604 | } | ||
605 | |||
606 | regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, | ||
607 | I2S_IFCR_MASK, I2S_IFCR_MASK); | ||
608 | |||
609 | if (playback_flg) { | ||
610 | ier = I2S_IER_UDRIE; | ||
611 | } else { | ||
612 | ier = I2S_IER_OVRIE; | ||
613 | |||
614 | spin_lock(&i2s->lock_fd); | ||
615 | if (i2s->refcount == 1) | ||
616 | /* dummy write to trigger capture */ | ||
617 | regmap_write(i2s->regmap, | ||
618 | STM32_I2S_TXDR_REG, 0); | ||
619 | spin_unlock(&i2s->lock_fd); | ||
620 | } | ||
621 | |||
622 | if (STM32_I2S_IS_SLAVE(i2s)) | ||
623 | ier |= I2S_IER_TIFREIE; | ||
624 | |||
625 | regmap_update_bits(i2s->regmap, STM32_I2S_IER_REG, ier, ier); | ||
626 | break; | ||
627 | case SNDRV_PCM_TRIGGER_STOP: | ||
628 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
629 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
630 | if (playback_flg) | ||
631 | regmap_update_bits(i2s->regmap, STM32_I2S_IER_REG, | ||
632 | I2S_IER_UDRIE, | ||
633 | (unsigned int)~I2S_IER_UDRIE); | ||
634 | else | ||
635 | regmap_update_bits(i2s->regmap, STM32_I2S_IER_REG, | ||
636 | I2S_IER_OVRIE, | ||
637 | (unsigned int)~I2S_IER_OVRIE); | ||
638 | |||
639 | spin_lock(&i2s->lock_fd); | ||
640 | i2s->refcount--; | ||
641 | if (i2s->refcount) { | ||
642 | spin_unlock(&i2s->lock_fd); | ||
643 | break; | ||
644 | } | ||
645 | spin_unlock(&i2s->lock_fd); | ||
646 | |||
647 | dev_dbg(cpu_dai->dev, "stop I2S\n"); | ||
648 | |||
649 | ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG, | ||
650 | I2S_CR1_SPE, 0); | ||
651 | if (ret < 0) { | ||
652 | dev_err(cpu_dai->dev, "Error %d disabling I2S\n", ret); | ||
653 | return ret; | ||
654 | } | ||
655 | |||
656 | cfg1_mask = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; | ||
657 | regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, | ||
658 | cfg1_mask, 0); | ||
659 | break; | ||
660 | default: | ||
661 | return -EINVAL; | ||
662 | } | ||
663 | |||
664 | return 0; | ||
665 | } | ||
666 | |||
667 | static void stm32_i2s_shutdown(struct snd_pcm_substream *substream, | ||
668 | struct snd_soc_dai *cpu_dai) | ||
669 | { | ||
670 | struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); | ||
671 | |||
672 | i2s->substream = NULL; | ||
673 | |||
674 | regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, | ||
675 | I2S_CGFR_MCKOE, (unsigned int)~I2S_CGFR_MCKOE); | ||
676 | } | ||
677 | |||
678 | static int stm32_i2s_dai_probe(struct snd_soc_dai *cpu_dai) | ||
679 | { | ||
680 | struct stm32_i2s_data *i2s = dev_get_drvdata(cpu_dai->dev); | ||
681 | struct snd_dmaengine_dai_dma_data *dma_data_tx = &i2s->dma_data_tx; | ||
682 | struct snd_dmaengine_dai_dma_data *dma_data_rx = &i2s->dma_data_rx; | ||
683 | |||
684 | /* Buswidth will be set by framework */ | ||
685 | dma_data_tx->addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; | ||
686 | dma_data_tx->addr = (dma_addr_t)(i2s->phys_addr) + STM32_I2S_TXDR_REG; | ||
687 | dma_data_tx->maxburst = 1; | ||
688 | dma_data_rx->addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; | ||
689 | dma_data_rx->addr = (dma_addr_t)(i2s->phys_addr) + STM32_I2S_RXDR_REG; | ||
690 | dma_data_rx->maxburst = 1; | ||
691 | |||
692 | snd_soc_dai_init_dma_data(cpu_dai, dma_data_tx, dma_data_rx); | ||
693 | |||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | static const struct regmap_config stm32_h7_i2s_regmap_conf = { | ||
698 | .reg_bits = 32, | ||
699 | .reg_stride = 4, | ||
700 | .val_bits = 32, | ||
701 | .max_register = STM32_I2S_CGFR_REG, | ||
702 | .readable_reg = stm32_i2s_readable_reg, | ||
703 | .volatile_reg = stm32_i2s_volatile_reg, | ||
704 | .writeable_reg = stm32_i2s_writeable_reg, | ||
705 | .fast_io = true, | ||
706 | }; | ||
707 | |||
708 | static const struct snd_soc_dai_ops stm32_i2s_pcm_dai_ops = { | ||
709 | .set_sysclk = stm32_i2s_set_sysclk, | ||
710 | .set_fmt = stm32_i2s_set_dai_fmt, | ||
711 | .startup = stm32_i2s_startup, | ||
712 | .hw_params = stm32_i2s_hw_params, | ||
713 | .trigger = stm32_i2s_trigger, | ||
714 | .shutdown = stm32_i2s_shutdown, | ||
715 | }; | ||
716 | |||
717 | static const struct snd_pcm_hardware stm32_i2s_pcm_hw = { | ||
718 | .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP, | ||
719 | .buffer_bytes_max = 8 * PAGE_SIZE, | ||
720 | .period_bytes_max = 2048, | ||
721 | .periods_min = 2, | ||
722 | .periods_max = 8, | ||
723 | }; | ||
724 | |||
725 | static const struct snd_dmaengine_pcm_config stm32_i2s_pcm_config = { | ||
726 | .pcm_hardware = &stm32_i2s_pcm_hw, | ||
727 | .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, | ||
728 | .prealloc_buffer_size = PAGE_SIZE * 8, | ||
729 | }; | ||
730 | |||
731 | static const struct snd_soc_component_driver stm32_i2s_component = { | ||
732 | .name = "stm32-i2s", | ||
733 | }; | ||
734 | |||
735 | static void stm32_i2s_dai_init(struct snd_soc_pcm_stream *stream, | ||
736 | char *stream_name) | ||
737 | { | ||
738 | stream->stream_name = stream_name; | ||
739 | stream->channels_min = 1; | ||
740 | stream->channels_max = 2; | ||
741 | stream->rates = SNDRV_PCM_RATE_8000_192000; | ||
742 | stream->formats = SNDRV_PCM_FMTBIT_S16_LE | | ||
743 | SNDRV_PCM_FMTBIT_S32_LE; | ||
744 | } | ||
745 | |||
746 | static int stm32_i2s_dais_init(struct platform_device *pdev, | ||
747 | struct stm32_i2s_data *i2s) | ||
748 | { | ||
749 | struct snd_soc_dai_driver *dai_ptr; | ||
750 | |||
751 | dai_ptr = devm_kzalloc(&pdev->dev, sizeof(struct snd_soc_dai_driver), | ||
752 | GFP_KERNEL); | ||
753 | if (!dai_ptr) | ||
754 | return -ENOMEM; | ||
755 | |||
756 | snprintf(i2s->dais_name, STM32_I2S_DAI_NAME_SIZE, | ||
757 | "%s", dev_name(&pdev->dev)); | ||
758 | |||
759 | dai_ptr->probe = stm32_i2s_dai_probe; | ||
760 | dai_ptr->ops = &stm32_i2s_pcm_dai_ops; | ||
761 | dai_ptr->name = i2s->dais_name; | ||
762 | dai_ptr->id = 1; | ||
763 | stm32_i2s_dai_init(&dai_ptr->playback, "playback"); | ||
764 | stm32_i2s_dai_init(&dai_ptr->capture, "capture"); | ||
765 | i2s->dai_drv = dai_ptr; | ||
766 | |||
767 | return 0; | ||
768 | } | ||
769 | |||
770 | static const struct of_device_id stm32_i2s_ids[] = { | ||
771 | { | ||
772 | .compatible = "st,stm32h7-i2s", | ||
773 | .data = &stm32_h7_i2s_regmap_conf | ||
774 | }, | ||
775 | {}, | ||
776 | }; | ||
777 | |||
778 | static int stm32_i2s_parse_dt(struct platform_device *pdev, | ||
779 | struct stm32_i2s_data *i2s) | ||
780 | { | ||
781 | struct device_node *np = pdev->dev.of_node; | ||
782 | const struct of_device_id *of_id; | ||
783 | struct reset_control *rst; | ||
784 | struct resource *res; | ||
785 | int irq, ret; | ||
786 | |||
787 | if (!np) | ||
788 | return -ENODEV; | ||
789 | |||
790 | of_id = of_match_device(stm32_i2s_ids, &pdev->dev); | ||
791 | if (of_id) | ||
792 | i2s->regmap_conf = (const struct regmap_config *)of_id->data; | ||
793 | else | ||
794 | return -EINVAL; | ||
795 | |||
796 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
797 | i2s->base = devm_ioremap_resource(&pdev->dev, res); | ||
798 | if (IS_ERR(i2s->base)) | ||
799 | return PTR_ERR(i2s->base); | ||
800 | |||
801 | i2s->phys_addr = res->start; | ||
802 | |||
803 | /* Get clocks */ | ||
804 | i2s->pclk = devm_clk_get(&pdev->dev, "pclk"); | ||
805 | if (IS_ERR(i2s->pclk)) { | ||
806 | dev_err(&pdev->dev, "Could not get pclk\n"); | ||
807 | return PTR_ERR(i2s->pclk); | ||
808 | } | ||
809 | |||
810 | i2s->i2sclk = devm_clk_get(&pdev->dev, "i2sclk"); | ||
811 | if (IS_ERR(i2s->i2sclk)) { | ||
812 | dev_err(&pdev->dev, "Could not get i2sclk\n"); | ||
813 | return PTR_ERR(i2s->i2sclk); | ||
814 | } | ||
815 | |||
816 | i2s->x8kclk = devm_clk_get(&pdev->dev, "x8k"); | ||
817 | if (IS_ERR(i2s->x8kclk)) { | ||
818 | dev_err(&pdev->dev, "missing x8k parent clock\n"); | ||
819 | return PTR_ERR(i2s->x8kclk); | ||
820 | } | ||
821 | |||
822 | i2s->x11kclk = devm_clk_get(&pdev->dev, "x11k"); | ||
823 | if (IS_ERR(i2s->x11kclk)) { | ||
824 | dev_err(&pdev->dev, "missing x11k parent clock\n"); | ||
825 | return PTR_ERR(i2s->x11kclk); | ||
826 | } | ||
827 | |||
828 | /* Get irqs */ | ||
829 | irq = platform_get_irq(pdev, 0); | ||
830 | if (irq < 0) { | ||
831 | dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); | ||
832 | return -ENOENT; | ||
833 | } | ||
834 | |||
835 | ret = devm_request_irq(&pdev->dev, irq, stm32_i2s_isr, IRQF_ONESHOT, | ||
836 | dev_name(&pdev->dev), i2s); | ||
837 | if (ret) { | ||
838 | dev_err(&pdev->dev, "irq request returned %d\n", ret); | ||
839 | return ret; | ||
840 | } | ||
841 | |||
842 | /* Reset */ | ||
843 | rst = devm_reset_control_get(&pdev->dev, NULL); | ||
844 | if (!IS_ERR(rst)) { | ||
845 | reset_control_assert(rst); | ||
846 | udelay(2); | ||
847 | reset_control_deassert(rst); | ||
848 | } | ||
849 | |||
850 | return 0; | ||
851 | } | ||
852 | |||
853 | static int stm32_i2s_probe(struct platform_device *pdev) | ||
854 | { | ||
855 | struct stm32_i2s_data *i2s; | ||
856 | int ret; | ||
857 | |||
858 | i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); | ||
859 | if (!i2s) | ||
860 | return -ENOMEM; | ||
861 | |||
862 | ret = stm32_i2s_parse_dt(pdev, i2s); | ||
863 | if (ret) | ||
864 | return ret; | ||
865 | |||
866 | i2s->pdev = pdev; | ||
867 | i2s->ms_flg = I2S_MS_NOT_SET; | ||
868 | spin_lock_init(&i2s->lock_fd); | ||
869 | platform_set_drvdata(pdev, i2s); | ||
870 | |||
871 | ret = stm32_i2s_dais_init(pdev, i2s); | ||
872 | if (ret) | ||
873 | return ret; | ||
874 | |||
875 | i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->base, | ||
876 | i2s->regmap_conf); | ||
877 | if (IS_ERR(i2s->regmap)) { | ||
878 | dev_err(&pdev->dev, "regmap init failed\n"); | ||
879 | return PTR_ERR(i2s->regmap); | ||
880 | } | ||
881 | |||
882 | ret = clk_prepare_enable(i2s->pclk); | ||
883 | if (ret) { | ||
884 | dev_err(&pdev->dev, "Enable pclk failed: %d\n", ret); | ||
885 | return ret; | ||
886 | } | ||
887 | |||
888 | ret = clk_prepare_enable(i2s->i2sclk); | ||
889 | if (ret) { | ||
890 | dev_err(&pdev->dev, "Enable i2sclk failed: %d\n", ret); | ||
891 | goto err_pclk_disable; | ||
892 | } | ||
893 | |||
894 | ret = devm_snd_soc_register_component(&pdev->dev, &stm32_i2s_component, | ||
895 | i2s->dai_drv, 1); | ||
896 | if (ret) | ||
897 | goto err_clocks_disable; | ||
898 | |||
899 | ret = devm_snd_dmaengine_pcm_register(&pdev->dev, | ||
900 | &stm32_i2s_pcm_config, 0); | ||
901 | if (ret) | ||
902 | goto err_clocks_disable; | ||
903 | |||
904 | /* Set SPI/I2S in i2s mode */ | ||
905 | ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, | ||
906 | I2S_CGFR_I2SMOD, I2S_CGFR_I2SMOD); | ||
907 | if (ret) | ||
908 | goto err_clocks_disable; | ||
909 | |||
910 | return ret; | ||
911 | |||
912 | err_clocks_disable: | ||
913 | clk_disable_unprepare(i2s->i2sclk); | ||
914 | err_pclk_disable: | ||
915 | clk_disable_unprepare(i2s->pclk); | ||
916 | |||
917 | return ret; | ||
918 | } | ||
919 | |||
920 | static int stm32_i2s_remove(struct platform_device *pdev) | ||
921 | { | ||
922 | struct stm32_i2s_data *i2s = platform_get_drvdata(pdev); | ||
923 | |||
924 | clk_disable_unprepare(i2s->i2sclk); | ||
925 | clk_disable_unprepare(i2s->pclk); | ||
926 | |||
927 | return 0; | ||
928 | } | ||
929 | |||
930 | MODULE_DEVICE_TABLE(of, stm32_i2s_ids); | ||
931 | |||
932 | static struct platform_driver stm32_i2s_driver = { | ||
933 | .driver = { | ||
934 | .name = "st,stm32-i2s", | ||
935 | .of_match_table = stm32_i2s_ids, | ||
936 | }, | ||
937 | .probe = stm32_i2s_probe, | ||
938 | .remove = stm32_i2s_remove, | ||
939 | }; | ||
940 | |||
941 | module_platform_driver(stm32_i2s_driver); | ||
942 | |||
943 | MODULE_DESCRIPTION("STM32 Soc i2s Interface"); | ||
944 | MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>"); | ||
945 | MODULE_ALIAS("platform:stm32-i2s"); | ||
946 | MODULE_LICENSE("GPL v2"); | ||