Commit | Line | Data |
---|---|---|
17467f23 TT |
1 | /* |
2 | * Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver | |
3 | * | |
4 | * Author: Timur Tabi <timur@freescale.com> | |
5 | * | |
f0fba2ad LG |
6 | * Copyright 2007-2010 Freescale Semiconductor, Inc. |
7 | * | |
8 | * This file is licensed under the terms of the GNU General Public License | |
9 | * version 2. This program is licensed "as is" without any warranty of any | |
10 | * kind, whether express or implied. | |
de623ece MP |
11 | * |
12 | * | |
13 | * Some notes why imx-pcm-fiq is used instead of DMA on some boards: | |
14 | * | |
15 | * The i.MX SSI core has some nasty limitations in AC97 mode. While most | |
16 | * sane processor vendors have a FIFO per AC97 slot, the i.MX has only | |
17 | * one FIFO which combines all valid receive slots. We cannot even select | |
18 | * which slots we want to receive. The WM9712 with which this driver | |
19 | * was developed with always sends GPIO status data in slot 12 which | |
20 | * we receive in our (PCM-) data stream. The only chance we have is to | |
21 | * manually skip this data in the FIQ handler. With sampling rates different | |
22 | * from 48000Hz not every frame has valid receive data, so the ratio | |
23 | * between pcm data and GPIO status data changes. Our FIQ handler is not | |
24 | * able to handle this, hence this driver only works with 48000Hz sampling | |
25 | * rate. | |
26 | * Reading and writing AC97 registers is another challenge. The core | |
27 | * provides us status bits when the read register is updated with *another* | |
28 | * value. When we read the same register two times (and the register still | |
29 | * contains the same value) these status bits are not set. We work | |
30 | * around this by not polling these bits but only wait a fixed delay. | |
17467f23 TT |
31 | */ |
32 | ||
33 | #include <linux/init.h> | |
dfa1a107 | 34 | #include <linux/io.h> |
17467f23 TT |
35 | #include <linux/module.h> |
36 | #include <linux/interrupt.h> | |
95cd98f9 | 37 | #include <linux/clk.h> |
c6682fed | 38 | #include <linux/ctype.h> |
17467f23 TT |
39 | #include <linux/device.h> |
40 | #include <linux/delay.h> | |
b880b805 | 41 | #include <linux/mutex.h> |
5a0e3ad6 | 42 | #include <linux/slab.h> |
aafa85e7 | 43 | #include <linux/spinlock.h> |
9c72a04c | 44 | #include <linux/of.h> |
dfa1a107 SG |
45 | #include <linux/of_address.h> |
46 | #include <linux/of_irq.h> | |
f0fba2ad | 47 | #include <linux/of_platform.h> |
17467f23 | 48 | |
17467f23 TT |
49 | #include <sound/core.h> |
50 | #include <sound/pcm.h> | |
51 | #include <sound/pcm_params.h> | |
52 | #include <sound/initval.h> | |
53 | #include <sound/soc.h> | |
a8909c9b | 54 | #include <sound/dmaengine_pcm.h> |
17467f23 | 55 | |
17467f23 | 56 | #include "fsl_ssi.h" |
09ce1111 | 57 | #include "imx-pcm.h" |
17467f23 | 58 | |
17467f23 TT |
59 | /** |
60 | * FSLSSI_I2S_FORMATS: audio formats supported by the SSI | |
61 | * | |
17467f23 TT |
62 | * The SSI has a limitation in that the samples must be in the same byte |
63 | * order as the host CPU. This is because when multiple bytes are written | |
64 | * to the STX register, the bytes and bits must be written in the same | |
65 | * order. The STX is a shift register, so all the bits need to be aligned | |
66 | * (bit-endianness must match byte-endianness). Processors typically write | |
67 | * the bits within a byte in the same order that the bytes of a word are | |
68 | * written in. So if the host CPU is big-endian, then only big-endian | |
69 | * samples will be written to STX properly. | |
70 | */ | |
71 | #ifdef __BIG_ENDIAN | |
af4f7f38 NC |
72 | #define FSLSSI_I2S_FORMATS \ |
73 | (SNDRV_PCM_FMTBIT_S8 | \ | |
74 | SNDRV_PCM_FMTBIT_S16_BE | \ | |
75 | SNDRV_PCM_FMTBIT_S18_3BE | \ | |
76 | SNDRV_PCM_FMTBIT_S20_3BE | \ | |
77 | SNDRV_PCM_FMTBIT_S24_3BE | \ | |
78 | SNDRV_PCM_FMTBIT_S24_BE) | |
17467f23 | 79 | #else |
af4f7f38 NC |
80 | #define FSLSSI_I2S_FORMATS \ |
81 | (SNDRV_PCM_FMTBIT_S8 | \ | |
82 | SNDRV_PCM_FMTBIT_S16_LE | \ | |
83 | SNDRV_PCM_FMTBIT_S18_3LE | \ | |
84 | SNDRV_PCM_FMTBIT_S20_3LE | \ | |
85 | SNDRV_PCM_FMTBIT_S24_3LE | \ | |
86 | SNDRV_PCM_FMTBIT_S24_LE) | |
17467f23 TT |
87 | #endif |
88 | ||
af4f7f38 NC |
89 | #define FSLSSI_SIER_DBG_RX_FLAGS \ |
90 | (SSI_SIER_RFF0_EN | \ | |
91 | SSI_SIER_RLS_EN | \ | |
92 | SSI_SIER_RFS_EN | \ | |
93 | SSI_SIER_ROE0_EN | \ | |
94 | SSI_SIER_RFRC_EN) | |
95 | #define FSLSSI_SIER_DBG_TX_FLAGS \ | |
96 | (SSI_SIER_TFE0_EN | \ | |
97 | SSI_SIER_TLS_EN | \ | |
98 | SSI_SIER_TFS_EN | \ | |
99 | SSI_SIER_TUE0_EN | \ | |
100 | SSI_SIER_TFRC_EN) | |
c1953bfe MP |
101 | |
102 | enum fsl_ssi_type { | |
103 | FSL_SSI_MCP8610, | |
104 | FSL_SSI_MX21, | |
0888efd1 | 105 | FSL_SSI_MX35, |
c1953bfe MP |
106 | FSL_SSI_MX51, |
107 | }; | |
108 | ||
2474e403 | 109 | struct fsl_ssi_regvals { |
4e6ec0d9 MP |
110 | u32 sier; |
111 | u32 srcr; | |
112 | u32 stcr; | |
113 | u32 scr; | |
114 | }; | |
115 | ||
05cf2379 ZW |
116 | static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg) |
117 | { | |
118 | switch (reg) { | |
a818aa5f NC |
119 | case REG_SSI_SACCEN: |
120 | case REG_SSI_SACCDIS: | |
05cf2379 ZW |
121 | return false; |
122 | default: | |
123 | return true; | |
124 | } | |
125 | } | |
126 | ||
127 | static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg) | |
128 | { | |
129 | switch (reg) { | |
a818aa5f NC |
130 | case REG_SSI_STX0: |
131 | case REG_SSI_STX1: | |
132 | case REG_SSI_SRX0: | |
133 | case REG_SSI_SRX1: | |
134 | case REG_SSI_SISR: | |
135 | case REG_SSI_SFCSR: | |
136 | case REG_SSI_SACNT: | |
137 | case REG_SSI_SACADD: | |
138 | case REG_SSI_SACDAT: | |
139 | case REG_SSI_SATAG: | |
140 | case REG_SSI_SACCST: | |
141 | case REG_SSI_SOR: | |
05cf2379 ZW |
142 | return true; |
143 | default: | |
144 | return false; | |
145 | } | |
146 | } | |
147 | ||
f51e3d53 MS |
148 | static bool fsl_ssi_precious_reg(struct device *dev, unsigned int reg) |
149 | { | |
150 | switch (reg) { | |
a818aa5f NC |
151 | case REG_SSI_SRX0: |
152 | case REG_SSI_SRX1: | |
153 | case REG_SSI_SISR: | |
154 | case REG_SSI_SACADD: | |
155 | case REG_SSI_SACDAT: | |
156 | case REG_SSI_SATAG: | |
f51e3d53 MS |
157 | return true; |
158 | default: | |
159 | return false; | |
160 | } | |
161 | } | |
162 | ||
05cf2379 ZW |
163 | static bool fsl_ssi_writeable_reg(struct device *dev, unsigned int reg) |
164 | { | |
165 | switch (reg) { | |
a818aa5f NC |
166 | case REG_SSI_SRX0: |
167 | case REG_SSI_SRX1: | |
168 | case REG_SSI_SACCST: | |
05cf2379 ZW |
169 | return false; |
170 | default: | |
171 | return true; | |
172 | } | |
173 | } | |
174 | ||
43248122 | 175 | static const struct regmap_config fsl_ssi_regconfig = { |
a818aa5f | 176 | .max_register = REG_SSI_SACCDIS, |
43248122 MP |
177 | .reg_bits = 32, |
178 | .val_bits = 32, | |
179 | .reg_stride = 4, | |
180 | .val_format_endian = REGMAP_ENDIAN_NATIVE, | |
a818aa5f | 181 | .num_reg_defaults_raw = REG_SSI_SACCDIS / sizeof(uint32_t) + 1, |
05cf2379 ZW |
182 | .readable_reg = fsl_ssi_readable_reg, |
183 | .volatile_reg = fsl_ssi_volatile_reg, | |
f51e3d53 | 184 | .precious_reg = fsl_ssi_precious_reg, |
05cf2379 | 185 | .writeable_reg = fsl_ssi_writeable_reg, |
bfcf928d | 186 | .cache_type = REGCACHE_FLAT, |
43248122 | 187 | }; |
d5a908b2 | 188 | |
fcdbadef SH |
189 | struct fsl_ssi_soc_data { |
190 | bool imx; | |
6139b1b1 | 191 | bool imx21regs; /* imx21-class SSI - no SACC{ST,EN,DIS} regs */ |
fcdbadef SH |
192 | bool offline_config; |
193 | u32 sisr_write_mask; | |
194 | }; | |
195 | ||
17467f23 | 196 | /** |
f3176834 | 197 | * fsl_ssi: per-SSI private data |
17467f23 | 198 | * |
7a8fceb7 | 199 | * @regs: Pointer to the regmap registers |
17467f23 | 200 | * @irq: IRQ of this SSI |
737a6b41 MP |
201 | * @cpu_dai_drv: CPU DAI driver for this device |
202 | * | |
203 | * @dai_fmt: DAI configuration this device is currently used with | |
8bc84a33 | 204 | * @i2s_net: I2S and Network mode configurations of SCR register |
737a6b41 | 205 | * @use_dma: DMA is used or FIQ with stream filter |
7a8fceb7 NC |
206 | * @use_dual_fifo: DMA with support for dual FIFO mode |
207 | * @has_ipg_clk_name: If "ipg" is in the clock name list of device tree | |
208 | * @fifo_depth: Depth of the SSI FIFOs | |
209 | * @slot_width: Width of each DAI slot | |
210 | * @slots: Number of slots | |
2474e403 | 211 | * @regvals: Specific RX/TX register settings |
737a6b41 | 212 | * |
7a8fceb7 NC |
213 | * @clk: Clock source to access register |
214 | * @baudclk: Clock source to generate bit and frame-sync clocks | |
737a6b41 | 215 | * @baudclk_streams: Active streams that are using baudclk |
737a6b41 | 216 | * |
7a8fceb7 NC |
217 | * @regcache_sfcsr: Cache sfcsr register value during suspend and resume |
218 | * @regcache_sacnt: Cache sacnt register value during suspend and resume | |
219 | * | |
737a6b41 MP |
220 | * @dma_params_tx: DMA transmit parameters |
221 | * @dma_params_rx: DMA receive parameters | |
222 | * @ssi_phys: physical address of the SSI registers | |
223 | * | |
224 | * @fiq_params: FIQ stream filtering parameters | |
225 | * | |
7a8fceb7 NC |
226 | * @pdev: Pointer to pdev when using fsl-ssi as sound card (ppc only) |
227 | * TODO: Should be replaced with simple-sound-card | |
737a6b41 MP |
228 | * |
229 | * @dbg_stats: Debugging statistics | |
230 | * | |
dcfcf2c2 | 231 | * @soc: SoC specific data |
7a8fceb7 NC |
232 | * @dev: Pointer to &pdev->dev |
233 | * | |
234 | * @fifo_watermark: The FIFO watermark setting. Notifies DMA when there are | |
235 | * @fifo_watermark or fewer words in TX fifo or | |
236 | * @fifo_watermark or more empty words in RX fifo. | |
237 | * @dma_maxburst: Max number of words to transfer in one go. So far, | |
238 | * this is always the same as fifo_watermark. | |
4ee437fb | 239 | * |
7a8fceb7 | 240 | * @ac97_reg_lock: Mutex lock to serialize AC97 register access operations |
17467f23 | 241 | */ |
f3176834 | 242 | struct fsl_ssi { |
43248122 | 243 | struct regmap *regs; |
9e446ad5 | 244 | int irq; |
f0fba2ad | 245 | struct snd_soc_dai_driver cpu_dai_drv; |
17467f23 | 246 | |
737a6b41 | 247 | unsigned int dai_fmt; |
8bc84a33 | 248 | u8 i2s_net; |
de623ece | 249 | bool use_dma; |
0da9e55e | 250 | bool use_dual_fifo; |
f4a43cab | 251 | bool has_ipg_clk_name; |
737a6b41 | 252 | unsigned int fifo_depth; |
b0a7043d NC |
253 | unsigned int slot_width; |
254 | unsigned int slots; | |
2474e403 | 255 | struct fsl_ssi_regvals regvals[2]; |
737a6b41 | 256 | |
95cd98f9 | 257 | struct clk *clk; |
737a6b41 | 258 | struct clk *baudclk; |
d429d8e3 | 259 | unsigned int baudclk_streams; |
737a6b41 | 260 | |
05cf2379 | 261 | u32 regcache_sfcsr; |
3f1c241f | 262 | u32 regcache_sacnt; |
05cf2379 | 263 | |
a8909c9b LPC |
264 | struct snd_dmaengine_dai_dma_data dma_params_tx; |
265 | struct snd_dmaengine_dai_dma_data dma_params_rx; | |
737a6b41 MP |
266 | dma_addr_t ssi_phys; |
267 | ||
de623ece | 268 | struct imx_pcm_fiq_params fiq_params; |
737a6b41 | 269 | |
737a6b41 | 270 | struct platform_device *pdev; |
09ce1111 | 271 | |
f138e621 | 272 | struct fsl_ssi_dbg dbg_stats; |
17467f23 | 273 | |
fcdbadef | 274 | const struct fsl_ssi_soc_data *soc; |
0096b693 | 275 | struct device *dev; |
4ee437fb CC |
276 | |
277 | u32 fifo_watermark; | |
278 | u32 dma_maxburst; | |
b880b805 MS |
279 | |
280 | struct mutex ac97_reg_lock; | |
c1953bfe | 281 | }; |
171d683d MP |
282 | |
283 | /* | |
7a8fceb7 | 284 | * SoC specific data |
171d683d | 285 | * |
7a8fceb7 NC |
286 | * Notes: |
287 | * 1) SSI in earlier SoCS has critical bits in control registers that | |
288 | * cannot be changed after SSI starts running -- a software reset | |
289 | * (set SSIEN to 0) is required to change their values. So adding | |
290 | * an offline_config flag for these SoCs. | |
291 | * 2) SDMA is available since imx35. However, imx35 does not support | |
292 | * DMA bits changing when SSI is running, so set offline_config. | |
293 | * 3) imx51 and later versions support register configurations when | |
294 | * SSI is running (SSIEN); For these versions, DMA needs to be | |
295 | * configured before SSI sends DMA request to avoid an undefined | |
296 | * DMA request on the SDMA side. | |
171d683d | 297 | */ |
171d683d | 298 | |
fcdbadef SH |
299 | static struct fsl_ssi_soc_data fsl_ssi_mpc8610 = { |
300 | .imx = false, | |
301 | .offline_config = true, | |
a818aa5f | 302 | .sisr_write_mask = SSI_SISR_RFRC | SSI_SISR_TFRC | |
af4f7f38 NC |
303 | SSI_SISR_ROE0 | SSI_SISR_ROE1 | |
304 | SSI_SISR_TUE0 | SSI_SISR_TUE1, | |
fcdbadef SH |
305 | }; |
306 | ||
307 | static struct fsl_ssi_soc_data fsl_ssi_imx21 = { | |
308 | .imx = true, | |
6139b1b1 | 309 | .imx21regs = true, |
fcdbadef SH |
310 | .offline_config = true, |
311 | .sisr_write_mask = 0, | |
312 | }; | |
313 | ||
314 | static struct fsl_ssi_soc_data fsl_ssi_imx35 = { | |
315 | .imx = true, | |
316 | .offline_config = true, | |
a818aa5f | 317 | .sisr_write_mask = SSI_SISR_RFRC | SSI_SISR_TFRC | |
af4f7f38 NC |
318 | SSI_SISR_ROE0 | SSI_SISR_ROE1 | |
319 | SSI_SISR_TUE0 | SSI_SISR_TUE1, | |
fcdbadef SH |
320 | }; |
321 | ||
322 | static struct fsl_ssi_soc_data fsl_ssi_imx51 = { | |
323 | .imx = true, | |
324 | .offline_config = false, | |
a818aa5f | 325 | .sisr_write_mask = SSI_SISR_ROE0 | SSI_SISR_ROE1 | |
af4f7f38 | 326 | SSI_SISR_TUE0 | SSI_SISR_TUE1, |
fcdbadef SH |
327 | }; |
328 | ||
329 | static const struct of_device_id fsl_ssi_ids[] = { | |
330 | { .compatible = "fsl,mpc8610-ssi", .data = &fsl_ssi_mpc8610 }, | |
331 | { .compatible = "fsl,imx51-ssi", .data = &fsl_ssi_imx51 }, | |
332 | { .compatible = "fsl,imx35-ssi", .data = &fsl_ssi_imx35 }, | |
333 | { .compatible = "fsl,imx21-ssi", .data = &fsl_ssi_imx21 }, | |
334 | {} | |
335 | }; | |
336 | MODULE_DEVICE_TABLE(of, fsl_ssi_ids); | |
337 | ||
f3176834 | 338 | static bool fsl_ssi_is_ac97(struct fsl_ssi *ssi) |
fcdbadef | 339 | { |
f3176834 | 340 | return (ssi->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) == |
5b64c173 | 341 | SND_SOC_DAIFMT_AC97; |
171d683d MP |
342 | } |
343 | ||
f3176834 | 344 | static bool fsl_ssi_is_i2s_master(struct fsl_ssi *ssi) |
8dd51e23 | 345 | { |
f3176834 | 346 | return (ssi->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) == |
8dd51e23 SH |
347 | SND_SOC_DAIFMT_CBS_CFS; |
348 | } | |
349 | ||
f3176834 | 350 | static bool fsl_ssi_is_i2s_cbm_cfs(struct fsl_ssi *ssi) |
cf4f7fc3 | 351 | { |
f3176834 | 352 | return (ssi->dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) == |
cf4f7fc3 FF |
353 | SND_SOC_DAIFMT_CBM_CFS; |
354 | } | |
7a8fceb7 | 355 | |
17467f23 | 356 | /** |
7a8fceb7 | 357 | * Interrupt handler to gather states |
17467f23 TT |
358 | */ |
359 | static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) | |
360 | { | |
f3176834 NC |
361 | struct fsl_ssi *ssi = dev_id; |
362 | struct regmap *regs = ssi->regs; | |
17467f23 | 363 | __be32 sisr; |
0888efd1 | 364 | __be32 sisr2; |
17467f23 | 365 | |
a818aa5f | 366 | regmap_read(regs, REG_SSI_SISR, &sisr); |
17467f23 | 367 | |
f3176834 | 368 | sisr2 = sisr & ssi->soc->sisr_write_mask; |
17467f23 TT |
369 | /* Clear the bits that we set */ |
370 | if (sisr2) | |
a818aa5f | 371 | regmap_write(regs, REG_SSI_SISR, sisr2); |
17467f23 | 372 | |
f3176834 | 373 | fsl_ssi_dbg_isr(&ssi->dbg_stats, sisr); |
9368acc4 | 374 | |
f138e621 | 375 | return IRQ_HANDLED; |
9368acc4 MP |
376 | } |
377 | ||
7a8fceb7 NC |
378 | /** |
379 | * Enable or disable all rx/tx config flags at once | |
4e6ec0d9 | 380 | */ |
f3176834 | 381 | static void fsl_ssi_rxtx_config(struct fsl_ssi *ssi, bool enable) |
4e6ec0d9 | 382 | { |
f3176834 | 383 | struct regmap *regs = ssi->regs; |
2474e403 | 384 | struct fsl_ssi_regvals *vals = ssi->regvals; |
4e6ec0d9 MP |
385 | |
386 | if (enable) { | |
a818aa5f | 387 | regmap_update_bits(regs, REG_SSI_SIER, |
2474e403 NC |
388 | vals[RX].sier | vals[TX].sier, |
389 | vals[RX].sier | vals[TX].sier); | |
a818aa5f | 390 | regmap_update_bits(regs, REG_SSI_SRCR, |
2474e403 NC |
391 | vals[RX].srcr | vals[TX].srcr, |
392 | vals[RX].srcr | vals[TX].srcr); | |
a818aa5f | 393 | regmap_update_bits(regs, REG_SSI_STCR, |
2474e403 NC |
394 | vals[RX].stcr | vals[TX].stcr, |
395 | vals[RX].stcr | vals[TX].stcr); | |
4e6ec0d9 | 396 | } else { |
a818aa5f | 397 | regmap_update_bits(regs, REG_SSI_SRCR, |
2474e403 | 398 | vals[RX].srcr | vals[TX].srcr, 0); |
a818aa5f | 399 | regmap_update_bits(regs, REG_SSI_STCR, |
2474e403 | 400 | vals[RX].stcr | vals[TX].stcr, 0); |
a818aa5f | 401 | regmap_update_bits(regs, REG_SSI_SIER, |
2474e403 | 402 | vals[RX].sier | vals[TX].sier, 0); |
4e6ec0d9 MP |
403 | } |
404 | } | |
405 | ||
7a8fceb7 NC |
406 | /** |
407 | * Clear remaining data in the FIFO to avoid dirty data or channel slipping | |
027db2e1 | 408 | */ |
f3176834 | 409 | static void fsl_ssi_fifo_clear(struct fsl_ssi *ssi, bool is_rx) |
027db2e1 | 410 | { |
52eee84e NC |
411 | bool tx = !is_rx; |
412 | ||
413 | regmap_update_bits(ssi->regs, REG_SSI_SOR, | |
414 | SSI_SOR_xX_CLR(tx), SSI_SOR_xX_CLR(tx)); | |
027db2e1 AM |
415 | } |
416 | ||
7a8fceb7 | 417 | /** |
65c961cc MP |
418 | * Calculate the bits that have to be disabled for the current stream that is |
419 | * getting disabled. This keeps the bits enabled that are necessary for the | |
420 | * second stream to work if 'stream_active' is true. | |
421 | * | |
422 | * Detailed calculation: | |
423 | * These are the values that need to be active after disabling. For non-active | |
424 | * second stream, this is 0: | |
425 | * vals_stream * !!stream_active | |
426 | * | |
427 | * The following computes the overall differences between the setup for the | |
428 | * to-disable stream and the active stream, a simple XOR: | |
429 | * vals_disable ^ (vals_stream * !!(stream_active)) | |
430 | * | |
431 | * The full expression adds a mask on all values we care about | |
432 | */ | |
433 | #define fsl_ssi_disable_val(vals_disable, vals_stream, stream_active) \ | |
434 | ((vals_disable) & \ | |
435 | ((vals_disable) ^ ((vals_stream) * (u32)!!(stream_active)))) | |
436 | ||
7a8fceb7 NC |
437 | /** |
438 | * Enable or disable SSI configuration. | |
4e6ec0d9 | 439 | */ |
f3176834 | 440 | static void fsl_ssi_config(struct fsl_ssi *ssi, bool enable, |
2474e403 | 441 | struct fsl_ssi_regvals *vals) |
4e6ec0d9 | 442 | { |
f3176834 | 443 | struct regmap *regs = ssi->regs; |
2474e403 | 444 | struct fsl_ssi_regvals *avals; |
43248122 | 445 | int nr_active_streams; |
ff4adb09 | 446 | u32 scr; |
65c961cc MP |
447 | int keep_active; |
448 | ||
ff4adb09 | 449 | regmap_read(regs, REG_SSI_SCR, &scr); |
43248122 | 450 | |
ff4adb09 | 451 | nr_active_streams = !!(scr & SSI_SCR_TE) + !!(scr & SSI_SCR_RE); |
43248122 | 452 | |
65c961cc MP |
453 | if (nr_active_streams - 1 > 0) |
454 | keep_active = 1; | |
455 | else | |
456 | keep_active = 0; | |
4e6ec0d9 | 457 | |
7a8fceb7 | 458 | /* Get the opposite direction to keep its values untouched */ |
2474e403 NC |
459 | if (&ssi->regvals[RX] == vals) |
460 | avals = &ssi->regvals[TX]; | |
4e6ec0d9 | 461 | else |
2474e403 | 462 | avals = &ssi->regvals[RX]; |
4e6ec0d9 | 463 | |
4e6ec0d9 | 464 | if (!enable) { |
7a8fceb7 NC |
465 | /* |
466 | * To keep the other stream safe, exclude shared bits between | |
467 | * both streams, and get safe bits to disable current stream | |
468 | */ | |
65c961cc | 469 | u32 scr = fsl_ssi_disable_val(vals->scr, avals->scr, |
af4f7f38 | 470 | keep_active); |
7a8fceb7 | 471 | /* Safely disable SCR register for the stream */ |
a818aa5f | 472 | regmap_update_bits(regs, REG_SSI_SCR, scr, 0); |
4e6ec0d9 MP |
473 | } |
474 | ||
475 | /* | |
7a8fceb7 NC |
476 | * For cases where online configuration is not supported, |
477 | * 1) Enable all necessary bits of both streams when 1st stream starts | |
478 | * even if the opposite stream will not start | |
479 | * 2) Disable all remaining bits of both streams when last stream ends | |
4e6ec0d9 | 480 | */ |
f3176834 | 481 | if (ssi->soc->offline_config) { |
af4f7f38 | 482 | if ((enable && !nr_active_streams) || (!enable && !keep_active)) |
f3176834 | 483 | fsl_ssi_rxtx_config(ssi, enable); |
4e6ec0d9 MP |
484 | |
485 | goto config_done; | |
486 | } | |
487 | ||
7a8fceb7 | 488 | /* Online configure single direction while SSI is running */ |
4e6ec0d9 | 489 | if (enable) { |
a818aa5f | 490 | fsl_ssi_fifo_clear(ssi, vals->scr & SSI_SCR_RE); |
027db2e1 | 491 | |
a818aa5f NC |
492 | regmap_update_bits(regs, REG_SSI_SRCR, vals->srcr, vals->srcr); |
493 | regmap_update_bits(regs, REG_SSI_STCR, vals->stcr, vals->stcr); | |
494 | regmap_update_bits(regs, REG_SSI_SIER, vals->sier, vals->sier); | |
4e6ec0d9 MP |
495 | } else { |
496 | u32 sier; | |
497 | u32 srcr; | |
498 | u32 stcr; | |
499 | ||
500 | /* | |
7a8fceb7 NC |
501 | * To keep the other stream safe, exclude shared bits between |
502 | * both streams, and get safe bits to disable current stream | |
4e6ec0d9 | 503 | */ |
65c961cc | 504 | sier = fsl_ssi_disable_val(vals->sier, avals->sier, |
af4f7f38 | 505 | keep_active); |
65c961cc | 506 | srcr = fsl_ssi_disable_val(vals->srcr, avals->srcr, |
af4f7f38 | 507 | keep_active); |
65c961cc | 508 | stcr = fsl_ssi_disable_val(vals->stcr, avals->stcr, |
af4f7f38 | 509 | keep_active); |
4e6ec0d9 | 510 | |
7a8fceb7 | 511 | /* Safely disable other control registers for the stream */ |
a818aa5f NC |
512 | regmap_update_bits(regs, REG_SSI_SRCR, srcr, 0); |
513 | regmap_update_bits(regs, REG_SSI_STCR, stcr, 0); | |
514 | regmap_update_bits(regs, REG_SSI_SIER, sier, 0); | |
4e6ec0d9 MP |
515 | } |
516 | ||
517 | config_done: | |
518 | /* Enabling of subunits is done after configuration */ | |
61fcf10a | 519 | if (enable) { |
7a8fceb7 NC |
520 | /* |
521 | * Start DMA before setting TE to avoid FIFO underrun | |
522 | * which may cause a channel slip or a channel swap | |
523 | * | |
524 | * TODO: FIQ cases might also need this upon testing | |
525 | */ | |
a818aa5f | 526 | if (ssi->use_dma && (vals->scr & SSI_SCR_TE)) { |
61fcf10a AM |
527 | int i; |
528 | int max_loop = 100; | |
7a8fceb7 NC |
529 | |
530 | /* Enable SSI first to send TX DMA request */ | |
a818aa5f | 531 | regmap_update_bits(regs, REG_SSI_SCR, |
af4f7f38 | 532 | SSI_SCR_SSIEN, SSI_SCR_SSIEN); |
7a8fceb7 NC |
533 | |
534 | /* Busy wait until TX FIFO not empty -- DMA working */ | |
61fcf10a AM |
535 | for (i = 0; i < max_loop; i++) { |
536 | u32 sfcsr; | |
a818aa5f NC |
537 | regmap_read(regs, REG_SSI_SFCSR, &sfcsr); |
538 | if (SSI_SFCSR_TFCNT0(sfcsr)) | |
61fcf10a AM |
539 | break; |
540 | } | |
541 | if (i == max_loop) { | |
f3176834 | 542 | dev_err(ssi->dev, |
61fcf10a AM |
543 | "Timeout waiting TX FIFO filling\n"); |
544 | } | |
545 | } | |
7a8fceb7 | 546 | /* Enable all remaining bits */ |
a818aa5f | 547 | regmap_update_bits(regs, REG_SSI_SCR, vals->scr, vals->scr); |
61fcf10a | 548 | } |
4e6ec0d9 MP |
549 | } |
550 | ||
f3176834 | 551 | static void fsl_ssi_rx_config(struct fsl_ssi *ssi, bool enable) |
4e6ec0d9 | 552 | { |
2474e403 | 553 | fsl_ssi_config(ssi, enable, &ssi->regvals[RX]); |
4e6ec0d9 MP |
554 | } |
555 | ||
f3176834 | 556 | static void fsl_ssi_tx_ac97_saccst_setup(struct fsl_ssi *ssi) |
01ca4851 | 557 | { |
f3176834 | 558 | struct regmap *regs = ssi->regs; |
01ca4851 MS |
559 | |
560 | /* no SACC{ST,EN,DIS} regs on imx21-class SSI */ | |
f3176834 | 561 | if (!ssi->soc->imx21regs) { |
7a8fceb7 | 562 | /* Disable all channel slots */ |
a818aa5f | 563 | regmap_write(regs, REG_SSI_SACCDIS, 0xff); |
7a8fceb7 | 564 | /* Enable slots 3 & 4 -- PCM Playback Left & Right channels */ |
a818aa5f | 565 | regmap_write(regs, REG_SSI_SACCEN, 0x300); |
01ca4851 MS |
566 | } |
567 | } | |
568 | ||
f3176834 | 569 | static void fsl_ssi_tx_config(struct fsl_ssi *ssi, bool enable) |
4e6ec0d9 | 570 | { |
01ca4851 | 571 | /* |
7a8fceb7 NC |
572 | * SACCST might be modified via AC Link by a CODEC if it sends |
573 | * extra bits in their SLOTREQ requests, which'll accidentally | |
574 | * send valid data to slots other than normal playback slots. | |
01ca4851 | 575 | * |
7a8fceb7 | 576 | * To be safe, configure SACCST right before TX starts. |
01ca4851 | 577 | */ |
f3176834 NC |
578 | if (enable && fsl_ssi_is_ac97(ssi)) |
579 | fsl_ssi_tx_ac97_saccst_setup(ssi); | |
01ca4851 | 580 | |
2474e403 | 581 | fsl_ssi_config(ssi, enable, &ssi->regvals[TX]); |
4e6ec0d9 MP |
582 | } |
583 | ||
7a8fceb7 NC |
584 | /** |
585 | * Cache critical bits of SIER, SRCR, STCR and SCR to later set them safely | |
6de83879 | 586 | */ |
2474e403 | 587 | static void fsl_ssi_setup_regvals(struct fsl_ssi *ssi) |
6de83879 | 588 | { |
2474e403 | 589 | struct fsl_ssi_regvals *vals = ssi->regvals; |
6de83879 | 590 | |
2474e403 NC |
591 | vals[RX].sier = SSI_SIER_RFF0_EN; |
592 | vals[RX].srcr = SSI_SRCR_RFEN0; | |
593 | vals[RX].scr = 0; | |
594 | vals[TX].sier = SSI_SIER_TFE0_EN; | |
595 | vals[TX].stcr = SSI_STCR_TFEN0; | |
596 | vals[TX].scr = 0; | |
6de83879 | 597 | |
7a8fceb7 | 598 | /* AC97 has already enabled SSIEN, RE and TE, so ignore them */ |
f3176834 | 599 | if (!fsl_ssi_is_ac97(ssi)) { |
2474e403 NC |
600 | vals[RX].scr = SSI_SCR_SSIEN | SSI_SCR_RE; |
601 | vals[TX].scr = SSI_SCR_SSIEN | SSI_SCR_TE; | |
6de83879 MP |
602 | } |
603 | ||
f3176834 | 604 | if (ssi->use_dma) { |
2474e403 NC |
605 | vals[RX].sier |= SSI_SIER_RDMAE; |
606 | vals[TX].sier |= SSI_SIER_TDMAE; | |
6de83879 | 607 | } else { |
2474e403 NC |
608 | vals[RX].sier |= SSI_SIER_RIE; |
609 | vals[TX].sier |= SSI_SIER_TIE; | |
6de83879 MP |
610 | } |
611 | ||
2474e403 NC |
612 | vals[RX].sier |= FSLSSI_SIER_DBG_RX_FLAGS; |
613 | vals[TX].sier |= FSLSSI_SIER_DBG_TX_FLAGS; | |
6de83879 MP |
614 | } |
615 | ||
f3176834 | 616 | static void fsl_ssi_setup_ac97(struct fsl_ssi *ssi) |
d8764646 | 617 | { |
f3176834 | 618 | struct regmap *regs = ssi->regs; |
d8764646 | 619 | |
7a8fceb7 | 620 | /* Setup the clock control register */ |
af4f7f38 NC |
621 | regmap_write(regs, REG_SSI_STCCR, SSI_SxCCR_WL(17) | SSI_SxCCR_DC(13)); |
622 | regmap_write(regs, REG_SSI_SRCCR, SSI_SxCCR_WL(17) | SSI_SxCCR_DC(13)); | |
d8764646 | 623 | |
7a8fceb7 | 624 | /* Enable AC97 mode and startup the SSI */ |
af4f7f38 | 625 | regmap_write(regs, REG_SSI_SACNT, SSI_SACNT_AC97EN | SSI_SACNT_FV); |
6139b1b1 | 626 | |
7a8fceb7 | 627 | /* AC97 has to communicate with codec before starting a stream */ |
a818aa5f | 628 | regmap_update_bits(regs, REG_SSI_SCR, |
af4f7f38 NC |
629 | SSI_SCR_SSIEN | SSI_SCR_TE | SSI_SCR_RE, |
630 | SSI_SCR_SSIEN | SSI_SCR_TE | SSI_SCR_RE); | |
d8764646 | 631 | |
a818aa5f | 632 | regmap_write(regs, REG_SSI_SOR, SSI_SOR_WAIT(3)); |
d8764646 MP |
633 | } |
634 | ||
dee89c4d MB |
635 | static int fsl_ssi_startup(struct snd_pcm_substream *substream, |
636 | struct snd_soc_dai *dai) | |
17467f23 TT |
637 | { |
638 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
f3176834 | 639 | struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai); |
f4a43cab SW |
640 | int ret; |
641 | ||
f3176834 | 642 | ret = clk_prepare_enable(ssi->clk); |
f4a43cab SW |
643 | if (ret) |
644 | return ret; | |
17467f23 | 645 | |
7a8fceb7 NC |
646 | /* |
647 | * When using dual fifo mode, it is safer to ensure an even period | |
0da9e55e NC |
648 | * size. If appearing to an odd number while DMA always starts its |
649 | * task from fifo0, fifo1 would be neglected at the end of each | |
650 | * period. But SSI would still access fifo1 with an invalid data. | |
651 | */ | |
f3176834 | 652 | if (ssi->use_dual_fifo) |
0da9e55e | 653 | snd_pcm_hw_constraint_step(substream->runtime, 0, |
af4f7f38 | 654 | SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2); |
0da9e55e | 655 | |
17467f23 TT |
656 | return 0; |
657 | } | |
658 | ||
f4a43cab | 659 | static void fsl_ssi_shutdown(struct snd_pcm_substream *substream, |
af4f7f38 | 660 | struct snd_soc_dai *dai) |
f4a43cab SW |
661 | { |
662 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
f3176834 | 663 | struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai); |
f4a43cab | 664 | |
f3176834 | 665 | clk_disable_unprepare(ssi->clk); |
f4a43cab SW |
666 | } |
667 | ||
ee9daad4 | 668 | /** |
7a8fceb7 | 669 | * Configure Digital Audio Interface bit clock |
ee9daad4 SH |
670 | * |
671 | * Note: This function can be only called when using SSI as DAI master | |
672 | * | |
673 | * Quick instruction for parameters: | |
b0a7043d NC |
674 | * freq: Output BCLK frequency = samplerate * slots * slot_width |
675 | * (In 2-channel I2S Master mode, slot_width is fixed 32) | |
ee9daad4 | 676 | */ |
8dd51e23 | 677 | static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream, |
0c884bed | 678 | struct snd_soc_dai *dai, |
af4f7f38 | 679 | struct snd_pcm_hw_params *hw_params) |
ee9daad4 | 680 | { |
52eee84e | 681 | bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; |
0c884bed | 682 | struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); |
f3176834 NC |
683 | struct regmap *regs = ssi->regs; |
684 | int synchronous = ssi->cpu_dai_drv.symmetric_rates, ret; | |
ee9daad4 | 685 | u32 pm = 999, div2, psr, stccr, mask, afreq, factor, i; |
d8ced479 | 686 | unsigned long clkrate, baudrate, tmprate; |
b0a7043d NC |
687 | unsigned int slots = params_channels(hw_params); |
688 | unsigned int slot_width = 32; | |
ee9daad4 | 689 | u64 sub, savesub = 100000; |
8dd51e23 | 690 | unsigned int freq; |
d429d8e3 | 691 | bool baudclk_is_used; |
8dd51e23 | 692 | |
b0a7043d | 693 | /* Override slots and slot_width if being specifically set... */ |
f3176834 NC |
694 | if (ssi->slots) |
695 | slots = ssi->slots; | |
b0a7043d | 696 | /* ...but keep 32 bits if slots is 2 -- I2S Master mode */ |
f3176834 NC |
697 | if (ssi->slot_width && slots != 2) |
698 | slot_width = ssi->slot_width; | |
b0a7043d NC |
699 | |
700 | /* Generate bit clock based on the slot number and slot width */ | |
701 | freq = slots * slot_width * params_rate(hw_params); | |
ee9daad4 SH |
702 | |
703 | /* Don't apply it to any non-baudclk circumstance */ | |
f3176834 | 704 | if (IS_ERR(ssi->baudclk)) |
ee9daad4 SH |
705 | return -EINVAL; |
706 | ||
e09745f2 AM |
707 | /* |
708 | * Hardware limitation: The bclk rate must be | |
709 | * never greater than 1/5 IPG clock rate | |
710 | */ | |
f3176834 | 711 | if (freq * 5 > clk_get_rate(ssi->clk)) { |
0c884bed | 712 | dev_err(dai->dev, "bitclk > ipgclk / 5\n"); |
e09745f2 AM |
713 | return -EINVAL; |
714 | } | |
715 | ||
f3176834 | 716 | baudclk_is_used = ssi->baudclk_streams & ~(BIT(substream->stream)); |
d429d8e3 | 717 | |
ee9daad4 SH |
718 | /* It should be already enough to divide clock by setting pm alone */ |
719 | psr = 0; | |
720 | div2 = 0; | |
721 | ||
722 | factor = (div2 + 1) * (7 * psr + 1) * 2; | |
723 | ||
724 | for (i = 0; i < 255; i++) { | |
6c8ca30e | 725 | tmprate = freq * factor * (i + 1); |
d429d8e3 MP |
726 | |
727 | if (baudclk_is_used) | |
f3176834 | 728 | clkrate = clk_get_rate(ssi->baudclk); |
d429d8e3 | 729 | else |
f3176834 | 730 | clkrate = clk_round_rate(ssi->baudclk, tmprate); |
ee9daad4 | 731 | |
acf2c60a TT |
732 | clkrate /= factor; |
733 | afreq = clkrate / (i + 1); | |
ee9daad4 SH |
734 | |
735 | if (freq == afreq) | |
736 | sub = 0; | |
737 | else if (freq / afreq == 1) | |
738 | sub = freq - afreq; | |
739 | else if (afreq / freq == 1) | |
740 | sub = afreq - freq; | |
741 | else | |
742 | continue; | |
743 | ||
744 | /* Calculate the fraction */ | |
745 | sub *= 100000; | |
746 | do_div(sub, freq); | |
747 | ||
ebac95a9 | 748 | if (sub < savesub && !(i == 0 && psr == 0 && div2 == 0)) { |
ee9daad4 SH |
749 | baudrate = tmprate; |
750 | savesub = sub; | |
751 | pm = i; | |
752 | } | |
753 | ||
754 | /* We are lucky */ | |
755 | if (savesub == 0) | |
756 | break; | |
757 | } | |
758 | ||
759 | /* No proper pm found if it is still remaining the initial value */ | |
760 | if (pm == 999) { | |
0c884bed | 761 | dev_err(dai->dev, "failed to handle the required sysclk\n"); |
ee9daad4 SH |
762 | return -EINVAL; |
763 | } | |
764 | ||
a818aa5f NC |
765 | stccr = SSI_SxCCR_PM(pm + 1) | (div2 ? SSI_SxCCR_DIV2 : 0) | |
766 | (psr ? SSI_SxCCR_PSR : 0); | |
af4f7f38 | 767 | mask = SSI_SxCCR_PM_MASK | SSI_SxCCR_DIV2 | SSI_SxCCR_PSR; |
ee9daad4 | 768 | |
52eee84e NC |
769 | /* STCCR is used for RX in synchronous mode */ |
770 | tx2 = tx || synchronous; | |
771 | regmap_update_bits(regs, REG_SSI_SxCCR(tx2), mask, stccr); | |
ee9daad4 | 772 | |
d429d8e3 | 773 | if (!baudclk_is_used) { |
f3176834 | 774 | ret = clk_set_rate(ssi->baudclk, baudrate); |
ee9daad4 | 775 | if (ret) { |
0c884bed | 776 | dev_err(dai->dev, "failed to set baudclk rate\n"); |
ee9daad4 SH |
777 | return -EINVAL; |
778 | } | |
ee9daad4 | 779 | } |
ee9daad4 SH |
780 | |
781 | return 0; | |
782 | } | |
783 | ||
17467f23 | 784 | /** |
7a8fceb7 | 785 | * Configure SSI based on PCM hardware parameters |
17467f23 | 786 | * |
7a8fceb7 NC |
787 | * Notes: |
788 | * 1) SxCCR.WL bits are critical bits that require SSI to be temporarily | |
789 | * disabled on offline_config SoCs. Even for online configurable SoCs | |
790 | * running in synchronous mode (both TX and RX use STCCR), it is not | |
791 | * safe to re-configure them when both two streams start running. | |
792 | * 2) SxCCR.PM, SxCCR.DIV2 and SxCCR.PSR bits will be configured in the | |
793 | * fsl_ssi_set_bclk() if SSI is the DAI clock master. | |
17467f23 | 794 | */ |
85ef2375 | 795 | static int fsl_ssi_hw_params(struct snd_pcm_substream *substream, |
af4f7f38 | 796 | struct snd_pcm_hw_params *hw_params, |
0c884bed | 797 | struct snd_soc_dai *dai) |
17467f23 | 798 | { |
52eee84e | 799 | bool tx2, tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; |
0c884bed | 800 | struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); |
f3176834 | 801 | struct regmap *regs = ssi->regs; |
2924a998 | 802 | unsigned int channels = params_channels(hw_params); |
4ca73043 | 803 | unsigned int sample_size = params_width(hw_params); |
a818aa5f | 804 | u32 wl = SSI_SxCCR_WL(sample_size); |
8dd51e23 | 805 | int ret; |
ff4adb09 | 806 | u32 scr; |
43248122 MP |
807 | int enabled; |
808 | ||
ff4adb09 NC |
809 | regmap_read(regs, REG_SSI_SCR, &scr); |
810 | enabled = scr & SSI_SCR_SSIEN; | |
17467f23 | 811 | |
5e538eca | 812 | /* |
7a8fceb7 NC |
813 | * SSI is properly configured if it is enabled and running in |
814 | * the synchronous mode; Note that AC97 mode is an exception | |
815 | * that should set separate configurations for STCCR and SRCCR | |
816 | * despite running in the synchronous mode. | |
5e538eca | 817 | */ |
f3176834 | 818 | if (enabled && ssi->cpu_dai_drv.symmetric_rates) |
5e538eca | 819 | return 0; |
17467f23 | 820 | |
f3176834 | 821 | if (fsl_ssi_is_i2s_master(ssi)) { |
0c884bed | 822 | ret = fsl_ssi_set_bclk(substream, dai, hw_params); |
8dd51e23 SH |
823 | if (ret) |
824 | return ret; | |
d429d8e3 MP |
825 | |
826 | /* Do not enable the clock if it is already enabled */ | |
f3176834 NC |
827 | if (!(ssi->baudclk_streams & BIT(substream->stream))) { |
828 | ret = clk_prepare_enable(ssi->baudclk); | |
d429d8e3 MP |
829 | if (ret) |
830 | return ret; | |
831 | ||
f3176834 | 832 | ssi->baudclk_streams |= BIT(substream->stream); |
d429d8e3 | 833 | } |
8dd51e23 SH |
834 | } |
835 | ||
f3176834 | 836 | if (!fsl_ssi_is_ac97(ssi)) { |
8bc84a33 | 837 | u8 i2s_net; |
7a8fceb7 | 838 | /* Normal + Network mode to send 16-bit data in 32-bit frames */ |
f3176834 | 839 | if (fsl_ssi_is_i2s_cbm_cfs(ssi) && sample_size == 16) |
8bc84a33 | 840 | i2s_net = SSI_SCR_I2S_MODE_NORMAL | SSI_SCR_NET; |
cf4f7fc3 | 841 | else |
8bc84a33 | 842 | i2s_net = ssi->i2s_net; |
cf4f7fc3 | 843 | |
a818aa5f | 844 | regmap_update_bits(regs, REG_SSI_SCR, |
8bc84a33 NC |
845 | SSI_SCR_I2S_NET_MASK, |
846 | channels == 1 ? 0 : i2s_net); | |
cf4f7fc3 FF |
847 | } |
848 | ||
5e538eca | 849 | /* In synchronous mode, the SSI uses STCCR for capture */ |
52eee84e NC |
850 | tx2 = tx || ssi->cpu_dai_drv.symmetric_rates; |
851 | regmap_update_bits(regs, REG_SSI_SxCCR(tx2), SSI_SxCCR_WL_MASK, wl); | |
17467f23 TT |
852 | |
853 | return 0; | |
854 | } | |
855 | ||
d429d8e3 | 856 | static int fsl_ssi_hw_free(struct snd_pcm_substream *substream, |
0c884bed | 857 | struct snd_soc_dai *dai) |
d429d8e3 MP |
858 | { |
859 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
f3176834 | 860 | struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai); |
d429d8e3 | 861 | |
f3176834 | 862 | if (fsl_ssi_is_i2s_master(ssi) && |
af4f7f38 | 863 | ssi->baudclk_streams & BIT(substream->stream)) { |
f3176834 NC |
864 | clk_disable_unprepare(ssi->baudclk); |
865 | ssi->baudclk_streams &= ~BIT(substream->stream); | |
d429d8e3 MP |
866 | } |
867 | ||
868 | return 0; | |
869 | } | |
870 | ||
85151461 | 871 | static int _fsl_ssi_set_dai_fmt(struct device *dev, |
f3176834 | 872 | struct fsl_ssi *ssi, unsigned int fmt) |
aafa85e7 | 873 | { |
f3176834 | 874 | struct regmap *regs = ssi->regs; |
aafa85e7 | 875 | u32 strcr = 0, stcr, srcr, scr, mask; |
2b0db996 MP |
876 | u8 wm; |
877 | ||
f3176834 | 878 | ssi->dai_fmt = fmt; |
171d683d | 879 | |
f3176834 | 880 | if (fsl_ssi_is_i2s_master(ssi) && IS_ERR(ssi->baudclk)) { |
2c225036 | 881 | dev_err(dev, "missing baudclk for master mode\n"); |
d429d8e3 MP |
882 | return -EINVAL; |
883 | } | |
884 | ||
2474e403 | 885 | fsl_ssi_setup_regvals(ssi); |
aafa85e7 | 886 | |
a818aa5f NC |
887 | regmap_read(regs, REG_SSI_SCR, &scr); |
888 | scr &= ~(SSI_SCR_SYN | SSI_SCR_I2S_MODE_MASK); | |
7a8fceb7 | 889 | /* Synchronize frame sync clock for TE to avoid data slipping */ |
a818aa5f | 890 | scr |= SSI_SCR_SYNC_TX_FS; |
aafa85e7 | 891 | |
a818aa5f | 892 | mask = SSI_STCR_TXBIT0 | SSI_STCR_TFDIR | SSI_STCR_TXDIR | |
af4f7f38 | 893 | SSI_STCR_TSCKP | SSI_STCR_TFSI | SSI_STCR_TFSL | SSI_STCR_TEFS; |
a818aa5f NC |
894 | regmap_read(regs, REG_SSI_STCR, &stcr); |
895 | regmap_read(regs, REG_SSI_SRCR, &srcr); | |
43248122 MP |
896 | stcr &= ~mask; |
897 | srcr &= ~mask; | |
aafa85e7 | 898 | |
7a8fceb7 | 899 | /* Use Network mode as default */ |
8bc84a33 | 900 | ssi->i2s_net = SSI_SCR_NET; |
aafa85e7 NC |
901 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
902 | case SND_SOC_DAIFMT_I2S: | |
a818aa5f | 903 | regmap_update_bits(regs, REG_SSI_STCCR, |
af4f7f38 | 904 | SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(2)); |
a818aa5f | 905 | regmap_update_bits(regs, REG_SSI_SRCCR, |
af4f7f38 | 906 | SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(2)); |
aafa85e7 | 907 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
cf4f7fc3 | 908 | case SND_SOC_DAIFMT_CBM_CFS: |
aafa85e7 | 909 | case SND_SOC_DAIFMT_CBS_CFS: |
8bc84a33 | 910 | ssi->i2s_net |= SSI_SCR_I2S_MODE_MASTER; |
aafa85e7 NC |
911 | break; |
912 | case SND_SOC_DAIFMT_CBM_CFM: | |
8bc84a33 | 913 | ssi->i2s_net |= SSI_SCR_I2S_MODE_SLAVE; |
aafa85e7 NC |
914 | break; |
915 | default: | |
916 | return -EINVAL; | |
917 | } | |
aafa85e7 NC |
918 | |
919 | /* Data on rising edge of bclk, frame low, 1clk before data */ | |
a818aa5f | 920 | strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP | |
af4f7f38 | 921 | SSI_STCR_TXBIT0 | SSI_STCR_TEFS; |
aafa85e7 NC |
922 | break; |
923 | case SND_SOC_DAIFMT_LEFT_J: | |
924 | /* Data on rising edge of bclk, frame high */ | |
a818aa5f | 925 | strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP; |
aafa85e7 NC |
926 | break; |
927 | case SND_SOC_DAIFMT_DSP_A: | |
928 | /* Data on rising edge of bclk, frame high, 1clk before data */ | |
a818aa5f | 929 | strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP | |
af4f7f38 | 930 | SSI_STCR_TXBIT0 | SSI_STCR_TEFS; |
aafa85e7 NC |
931 | break; |
932 | case SND_SOC_DAIFMT_DSP_B: | |
933 | /* Data on rising edge of bclk, frame high */ | |
af4f7f38 | 934 | strcr |= SSI_STCR_TFSL | SSI_STCR_TSCKP | SSI_STCR_TXBIT0; |
aafa85e7 | 935 | break; |
2b0db996 | 936 | case SND_SOC_DAIFMT_AC97: |
7a8fceb7 | 937 | /* Data on falling edge of bclk, frame high, 1clk before data */ |
8bc84a33 | 938 | ssi->i2s_net |= SSI_SCR_I2S_MODE_NORMAL; |
2b0db996 | 939 | break; |
aafa85e7 NC |
940 | default: |
941 | return -EINVAL; | |
942 | } | |
8bc84a33 | 943 | scr |= ssi->i2s_net; |
aafa85e7 NC |
944 | |
945 | /* DAI clock inversion */ | |
946 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | |
947 | case SND_SOC_DAIFMT_NB_NF: | |
948 | /* Nothing to do for both normal cases */ | |
949 | break; | |
950 | case SND_SOC_DAIFMT_IB_NF: | |
951 | /* Invert bit clock */ | |
a818aa5f | 952 | strcr ^= SSI_STCR_TSCKP; |
aafa85e7 NC |
953 | break; |
954 | case SND_SOC_DAIFMT_NB_IF: | |
955 | /* Invert frame clock */ | |
a818aa5f | 956 | strcr ^= SSI_STCR_TFSI; |
aafa85e7 NC |
957 | break; |
958 | case SND_SOC_DAIFMT_IB_IF: | |
959 | /* Invert both clocks */ | |
a818aa5f NC |
960 | strcr ^= SSI_STCR_TSCKP; |
961 | strcr ^= SSI_STCR_TFSI; | |
aafa85e7 NC |
962 | break; |
963 | default: | |
964 | return -EINVAL; | |
965 | } | |
966 | ||
967 | /* DAI clock master masks */ | |
968 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | |
969 | case SND_SOC_DAIFMT_CBS_CFS: | |
7a8fceb7 | 970 | /* Output bit and frame sync clocks */ |
a818aa5f NC |
971 | strcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR; |
972 | scr |= SSI_SCR_SYS_CLK_EN; | |
aafa85e7 NC |
973 | break; |
974 | case SND_SOC_DAIFMT_CBM_CFM: | |
7a8fceb7 | 975 | /* Input bit or frame sync clocks */ |
a818aa5f | 976 | scr &= ~SSI_SCR_SYS_CLK_EN; |
aafa85e7 | 977 | break; |
cf4f7fc3 | 978 | case SND_SOC_DAIFMT_CBM_CFS: |
7a8fceb7 | 979 | /* Input bit clock but output frame sync clock */ |
a818aa5f NC |
980 | strcr &= ~SSI_STCR_TXDIR; |
981 | strcr |= SSI_STCR_TFDIR; | |
982 | scr &= ~SSI_SCR_SYS_CLK_EN; | |
cf4f7fc3 | 983 | break; |
aafa85e7 | 984 | default: |
f3176834 | 985 | if (!fsl_ssi_is_ac97(ssi)) |
dce0332c | 986 | return -EINVAL; |
aafa85e7 NC |
987 | } |
988 | ||
989 | stcr |= strcr; | |
990 | srcr |= strcr; | |
991 | ||
7a8fceb7 | 992 | /* Set SYN mode and clear RXDIR bit when using SYN or AC97 mode */ |
f3176834 | 993 | if (ssi->cpu_dai_drv.symmetric_rates || fsl_ssi_is_ac97(ssi)) { |
a818aa5f NC |
994 | srcr &= ~SSI_SRCR_RXDIR; |
995 | scr |= SSI_SCR_SYN; | |
aafa85e7 NC |
996 | } |
997 | ||
a818aa5f NC |
998 | regmap_write(regs, REG_SSI_STCR, stcr); |
999 | regmap_write(regs, REG_SSI_SRCR, srcr); | |
1000 | regmap_write(regs, REG_SSI_SCR, scr); | |
aafa85e7 | 1001 | |
f3176834 | 1002 | wm = ssi->fifo_watermark; |
2b0db996 | 1003 | |
a818aa5f | 1004 | regmap_write(regs, REG_SSI_SFCSR, |
af4f7f38 NC |
1005 | SSI_SFCSR_TFWM0(wm) | SSI_SFCSR_RFWM0(wm) | |
1006 | SSI_SFCSR_TFWM1(wm) | SSI_SFCSR_RFWM1(wm)); | |
2b0db996 | 1007 | |
f3176834 | 1008 | if (ssi->use_dual_fifo) { |
af4f7f38 NC |
1009 | regmap_update_bits(regs, REG_SSI_SRCR, |
1010 | SSI_SRCR_RFEN1, SSI_SRCR_RFEN1); | |
1011 | regmap_update_bits(regs, REG_SSI_STCR, | |
1012 | SSI_STCR_TFEN1, SSI_STCR_TFEN1); | |
1013 | regmap_update_bits(regs, REG_SSI_SCR, | |
1014 | SSI_SCR_TCH_EN, SSI_SCR_TCH_EN); | |
2b0db996 MP |
1015 | } |
1016 | ||
5b64c173 | 1017 | if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_AC97) |
f3176834 | 1018 | fsl_ssi_setup_ac97(ssi); |
2b0db996 | 1019 | |
aafa85e7 | 1020 | return 0; |
85e59af2 MP |
1021 | } |
1022 | ||
1023 | /** | |
7a8fceb7 | 1024 | * Configure Digital Audio Interface (DAI) Format |
85e59af2 | 1025 | */ |
0c884bed | 1026 | static int fsl_ssi_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
85e59af2 | 1027 | { |
0c884bed | 1028 | struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); |
85e59af2 | 1029 | |
7a8fceb7 | 1030 | /* AC97 configured DAIFMT earlier in the probe() */ |
f3176834 | 1031 | if (fsl_ssi_is_ac97(ssi)) |
c997a92a MS |
1032 | return 0; |
1033 | ||
0c884bed | 1034 | return _fsl_ssi_set_dai_fmt(dai->dev, ssi, fmt); |
aafa85e7 NC |
1035 | } |
1036 | ||
aafa85e7 | 1037 | /** |
7a8fceb7 | 1038 | * Set TDM slot number and slot width |
aafa85e7 | 1039 | */ |
0c884bed | 1040 | static int fsl_ssi_set_dai_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, |
af4f7f38 | 1041 | u32 rx_mask, int slots, int slot_width) |
aafa85e7 | 1042 | { |
0c884bed | 1043 | struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); |
f3176834 | 1044 | struct regmap *regs = ssi->regs; |
aafa85e7 NC |
1045 | u32 val; |
1046 | ||
b0a7043d NC |
1047 | /* The word length should be 8, 10, 12, 16, 18, 20, 22 or 24 */ |
1048 | if (slot_width & 1 || slot_width < 8 || slot_width > 24) { | |
0c884bed | 1049 | dev_err(dai->dev, "invalid slot width: %d\n", slot_width); |
b0a7043d NC |
1050 | return -EINVAL; |
1051 | } | |
1052 | ||
aafa85e7 | 1053 | /* The slot number should be >= 2 if using Network mode or I2S mode */ |
a818aa5f NC |
1054 | regmap_read(regs, REG_SSI_SCR, &val); |
1055 | val &= SSI_SCR_I2S_MODE_MASK | SSI_SCR_NET; | |
aafa85e7 | 1056 | if (val && slots < 2) { |
0c884bed | 1057 | dev_err(dai->dev, "slot number should be >= 2 in I2S or NET\n"); |
aafa85e7 NC |
1058 | return -EINVAL; |
1059 | } | |
1060 | ||
af4f7f38 NC |
1061 | regmap_update_bits(regs, REG_SSI_STCCR, |
1062 | SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots)); | |
1063 | regmap_update_bits(regs, REG_SSI_SRCCR, | |
1064 | SSI_SxCCR_DC_MASK, SSI_SxCCR_DC(slots)); | |
aafa85e7 | 1065 | |
7a8fceb7 | 1066 | /* Save SSIEN bit of the SCR register */ |
a818aa5f NC |
1067 | regmap_read(regs, REG_SSI_SCR, &val); |
1068 | val &= SSI_SCR_SSIEN; | |
7a8fceb7 | 1069 | /* Temporarily enable SSI to allow SxMSKs to be configurable */ |
af4f7f38 | 1070 | regmap_update_bits(regs, REG_SSI_SCR, SSI_SCR_SSIEN, SSI_SCR_SSIEN); |
aafa85e7 | 1071 | |
a818aa5f NC |
1072 | regmap_write(regs, REG_SSI_STMSK, ~tx_mask); |
1073 | regmap_write(regs, REG_SSI_SRMSK, ~rx_mask); | |
aafa85e7 | 1074 | |
7a8fceb7 | 1075 | /* Restore the value of SSIEN bit */ |
a818aa5f | 1076 | regmap_update_bits(regs, REG_SSI_SCR, SSI_SCR_SSIEN, val); |
aafa85e7 | 1077 | |
f3176834 NC |
1078 | ssi->slot_width = slot_width; |
1079 | ssi->slots = slots; | |
b0a7043d | 1080 | |
aafa85e7 NC |
1081 | return 0; |
1082 | } | |
1083 | ||
17467f23 | 1084 | /** |
7a8fceb7 | 1085 | * Start or stop SSI and corresponding DMA transaction. |
17467f23 TT |
1086 | * |
1087 | * The DMA channel is in external master start and pause mode, which | |
1088 | * means the SSI completely controls the flow of data. | |
1089 | */ | |
dee89c4d MB |
1090 | static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd, |
1091 | struct snd_soc_dai *dai) | |
17467f23 TT |
1092 | { |
1093 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
f3176834 NC |
1094 | struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(rtd->cpu_dai); |
1095 | struct regmap *regs = ssi->regs; | |
9b443e3d | 1096 | |
17467f23 TT |
1097 | switch (cmd) { |
1098 | case SNDRV_PCM_TRIGGER_START: | |
b20e53a8 | 1099 | case SNDRV_PCM_TRIGGER_RESUME: |
17467f23 | 1100 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
a4d11fe5 | 1101 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
f3176834 | 1102 | fsl_ssi_tx_config(ssi, true); |
a4d11fe5 | 1103 | else |
f3176834 | 1104 | fsl_ssi_rx_config(ssi, true); |
17467f23 TT |
1105 | break; |
1106 | ||
1107 | case SNDRV_PCM_TRIGGER_STOP: | |
b20e53a8 | 1108 | case SNDRV_PCM_TRIGGER_SUSPEND: |
17467f23 TT |
1109 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
1110 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | |
f3176834 | 1111 | fsl_ssi_tx_config(ssi, false); |
17467f23 | 1112 | else |
f3176834 | 1113 | fsl_ssi_rx_config(ssi, false); |
17467f23 TT |
1114 | break; |
1115 | ||
1116 | default: | |
1117 | return -EINVAL; | |
1118 | } | |
1119 | ||
7a8fceb7 | 1120 | /* Clear corresponding FIFO */ |
f3176834 | 1121 | if (fsl_ssi_is_ac97(ssi)) { |
a5a7ee7c | 1122 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
a818aa5f | 1123 | regmap_write(regs, REG_SSI_SOR, SSI_SOR_TX_CLR); |
a5a7ee7c | 1124 | else |
a818aa5f | 1125 | regmap_write(regs, REG_SSI_SOR, SSI_SOR_RX_CLR); |
a5a7ee7c | 1126 | } |
9b443e3d | 1127 | |
17467f23 TT |
1128 | return 0; |
1129 | } | |
1130 | ||
fc8ba7f9 LPC |
1131 | static int fsl_ssi_dai_probe(struct snd_soc_dai *dai) |
1132 | { | |
f3176834 | 1133 | struct fsl_ssi *ssi = snd_soc_dai_get_drvdata(dai); |
fc8ba7f9 | 1134 | |
f3176834 NC |
1135 | if (ssi->soc->imx && ssi->use_dma) { |
1136 | dai->playback_dma_data = &ssi->dma_params_tx; | |
1137 | dai->capture_dma_data = &ssi->dma_params_rx; | |
fc8ba7f9 LPC |
1138 | } |
1139 | ||
1140 | return 0; | |
1141 | } | |
1142 | ||
85e7652d | 1143 | static const struct snd_soc_dai_ops fsl_ssi_dai_ops = { |
af4f7f38 NC |
1144 | .startup = fsl_ssi_startup, |
1145 | .shutdown = fsl_ssi_shutdown, | |
1146 | .hw_params = fsl_ssi_hw_params, | |
1147 | .hw_free = fsl_ssi_hw_free, | |
1148 | .set_fmt = fsl_ssi_set_dai_fmt, | |
1149 | .set_tdm_slot = fsl_ssi_set_dai_tdm_slot, | |
1150 | .trigger = fsl_ssi_trigger, | |
6335d055 EM |
1151 | }; |
1152 | ||
f0fba2ad | 1153 | static struct snd_soc_dai_driver fsl_ssi_dai_template = { |
fc8ba7f9 | 1154 | .probe = fsl_ssi_dai_probe, |
17467f23 | 1155 | .playback = { |
e3655004 | 1156 | .stream_name = "CPU-Playback", |
2924a998 | 1157 | .channels_min = 1, |
48a260ee | 1158 | .channels_max = 32, |
58055677 | 1159 | .rates = SNDRV_PCM_RATE_CONTINUOUS, |
17467f23 TT |
1160 | .formats = FSLSSI_I2S_FORMATS, |
1161 | }, | |
1162 | .capture = { | |
e3655004 | 1163 | .stream_name = "CPU-Capture", |
2924a998 | 1164 | .channels_min = 1, |
48a260ee | 1165 | .channels_max = 32, |
58055677 | 1166 | .rates = SNDRV_PCM_RATE_CONTINUOUS, |
17467f23 TT |
1167 | .formats = FSLSSI_I2S_FORMATS, |
1168 | }, | |
6335d055 | 1169 | .ops = &fsl_ssi_dai_ops, |
17467f23 TT |
1170 | }; |
1171 | ||
3580aa10 | 1172 | static const struct snd_soc_component_driver fsl_ssi_component = { |
af4f7f38 | 1173 | .name = "fsl-ssi", |
3580aa10 KM |
1174 | }; |
1175 | ||
cd7f0295 | 1176 | static struct snd_soc_dai_driver fsl_ssi_ac97_dai = { |
bc263214 | 1177 | .bus_control = true, |
793e3e9e | 1178 | .probe = fsl_ssi_dai_probe, |
cd7f0295 MP |
1179 | .playback = { |
1180 | .stream_name = "AC97 Playback", | |
1181 | .channels_min = 2, | |
1182 | .channels_max = 2, | |
1183 | .rates = SNDRV_PCM_RATE_8000_48000, | |
10582635 | 1184 | .formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S20, |
cd7f0295 MP |
1185 | }, |
1186 | .capture = { | |
1187 | .stream_name = "AC97 Capture", | |
1188 | .channels_min = 2, | |
1189 | .channels_max = 2, | |
1190 | .rates = SNDRV_PCM_RATE_48000, | |
10582635 MS |
1191 | /* 16-bit capture is broken (errata ERR003778) */ |
1192 | .formats = SNDRV_PCM_FMTBIT_S20, | |
cd7f0295 | 1193 | }, |
a5a7ee7c | 1194 | .ops = &fsl_ssi_dai_ops, |
cd7f0295 MP |
1195 | }; |
1196 | ||
f3176834 | 1197 | static struct fsl_ssi *fsl_ac97_data; |
cd7f0295 | 1198 | |
a851a2bb | 1199 | static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, |
af4f7f38 | 1200 | unsigned short val) |
cd7f0295 | 1201 | { |
43248122 | 1202 | struct regmap *regs = fsl_ac97_data->regs; |
cd7f0295 MP |
1203 | unsigned int lreg; |
1204 | unsigned int lval; | |
8277df3c | 1205 | int ret; |
cd7f0295 MP |
1206 | |
1207 | if (reg > 0x7f) | |
1208 | return; | |
1209 | ||
b880b805 MS |
1210 | mutex_lock(&fsl_ac97_data->ac97_reg_lock); |
1211 | ||
8277df3c MS |
1212 | ret = clk_prepare_enable(fsl_ac97_data->clk); |
1213 | if (ret) { | |
1214 | pr_err("ac97 write clk_prepare_enable failed: %d\n", | |
1215 | ret); | |
b880b805 | 1216 | goto ret_unlock; |
8277df3c | 1217 | } |
cd7f0295 MP |
1218 | |
1219 | lreg = reg << 12; | |
a818aa5f | 1220 | regmap_write(regs, REG_SSI_SACADD, lreg); |
cd7f0295 MP |
1221 | |
1222 | lval = val << 4; | |
a818aa5f | 1223 | regmap_write(regs, REG_SSI_SACDAT, lval); |
cd7f0295 | 1224 | |
af4f7f38 NC |
1225 | regmap_update_bits(regs, REG_SSI_SACNT, |
1226 | SSI_SACNT_RDWR_MASK, SSI_SACNT_WR); | |
cd7f0295 | 1227 | udelay(100); |
8277df3c MS |
1228 | |
1229 | clk_disable_unprepare(fsl_ac97_data->clk); | |
b880b805 MS |
1230 | |
1231 | ret_unlock: | |
1232 | mutex_unlock(&fsl_ac97_data->ac97_reg_lock); | |
cd7f0295 MP |
1233 | } |
1234 | ||
a851a2bb | 1235 | static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97, |
af4f7f38 | 1236 | unsigned short reg) |
cd7f0295 | 1237 | { |
43248122 | 1238 | struct regmap *regs = fsl_ac97_data->regs; |
b880b805 | 1239 | unsigned short val = 0; |
43248122 | 1240 | u32 reg_val; |
cd7f0295 | 1241 | unsigned int lreg; |
8277df3c MS |
1242 | int ret; |
1243 | ||
b880b805 MS |
1244 | mutex_lock(&fsl_ac97_data->ac97_reg_lock); |
1245 | ||
8277df3c MS |
1246 | ret = clk_prepare_enable(fsl_ac97_data->clk); |
1247 | if (ret) { | |
af4f7f38 | 1248 | pr_err("ac97 read clk_prepare_enable failed: %d\n", ret); |
b880b805 | 1249 | goto ret_unlock; |
8277df3c | 1250 | } |
cd7f0295 MP |
1251 | |
1252 | lreg = (reg & 0x7f) << 12; | |
a818aa5f | 1253 | regmap_write(regs, REG_SSI_SACADD, lreg); |
af4f7f38 NC |
1254 | regmap_update_bits(regs, REG_SSI_SACNT, |
1255 | SSI_SACNT_RDWR_MASK, SSI_SACNT_RD); | |
cd7f0295 MP |
1256 | |
1257 | udelay(100); | |
1258 | ||
a818aa5f | 1259 | regmap_read(regs, REG_SSI_SACDAT, ®_val); |
43248122 | 1260 | val = (reg_val >> 4) & 0xffff; |
cd7f0295 | 1261 | |
8277df3c MS |
1262 | clk_disable_unprepare(fsl_ac97_data->clk); |
1263 | ||
b880b805 MS |
1264 | ret_unlock: |
1265 | mutex_unlock(&fsl_ac97_data->ac97_reg_lock); | |
cd7f0295 MP |
1266 | return val; |
1267 | } | |
1268 | ||
1269 | static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = { | |
af4f7f38 NC |
1270 | .read = fsl_ssi_ac97_read, |
1271 | .write = fsl_ssi_ac97_write, | |
cd7f0295 MP |
1272 | }; |
1273 | ||
17467f23 | 1274 | /** |
f0fba2ad | 1275 | * Make every character in a string lower-case |
17467f23 | 1276 | */ |
f0fba2ad LG |
1277 | static void make_lowercase(char *s) |
1278 | { | |
c6682fed FE |
1279 | if (!s) |
1280 | return; | |
1281 | for (; *s; s++) | |
1282 | *s = tolower(*s); | |
f0fba2ad LG |
1283 | } |
1284 | ||
49da09e2 | 1285 | static int fsl_ssi_imx_probe(struct platform_device *pdev, |
af4f7f38 | 1286 | struct fsl_ssi *ssi, void __iomem *iomem) |
49da09e2 MP |
1287 | { |
1288 | struct device_node *np = pdev->dev.of_node; | |
8483c067 | 1289 | struct device *dev = &pdev->dev; |
ed0f1604 | 1290 | u32 dmas[4]; |
49da09e2 MP |
1291 | int ret; |
1292 | ||
7a8fceb7 | 1293 | /* Backward compatible for a DT without ipg clock name assigned */ |
f3176834 | 1294 | if (ssi->has_ipg_clk_name) |
8483c067 | 1295 | ssi->clk = devm_clk_get(dev, "ipg"); |
f4a43cab | 1296 | else |
8483c067 | 1297 | ssi->clk = devm_clk_get(dev, NULL); |
f3176834 NC |
1298 | if (IS_ERR(ssi->clk)) { |
1299 | ret = PTR_ERR(ssi->clk); | |
2c225036 | 1300 | dev_err(dev, "failed to get clock: %d\n", ret); |
49da09e2 MP |
1301 | return ret; |
1302 | } | |
1303 | ||
7a8fceb7 | 1304 | /* Enable the clock since regmap will not handle it in this case */ |
f3176834 NC |
1305 | if (!ssi->has_ipg_clk_name) { |
1306 | ret = clk_prepare_enable(ssi->clk); | |
f4a43cab | 1307 | if (ret) { |
8483c067 | 1308 | dev_err(dev, "clk_prepare_enable failed: %d\n", ret); |
f4a43cab SW |
1309 | return ret; |
1310 | } | |
49da09e2 MP |
1311 | } |
1312 | ||
7a8fceb7 | 1313 | /* Do not error out for slave cases that live without a baud clock */ |
8483c067 | 1314 | ssi->baudclk = devm_clk_get(dev, "baud"); |
f3176834 | 1315 | if (IS_ERR(ssi->baudclk)) |
2c225036 | 1316 | dev_dbg(dev, "failed to get baud clock: %ld\n", |
f3176834 | 1317 | PTR_ERR(ssi->baudclk)); |
49da09e2 | 1318 | |
f3176834 NC |
1319 | ssi->dma_params_tx.maxburst = ssi->dma_maxburst; |
1320 | ssi->dma_params_rx.maxburst = ssi->dma_maxburst; | |
a818aa5f NC |
1321 | ssi->dma_params_tx.addr = ssi->ssi_phys + REG_SSI_STX0; |
1322 | ssi->dma_params_rx.addr = ssi->ssi_phys + REG_SSI_SRX0; | |
49da09e2 | 1323 | |
7a8fceb7 | 1324 | /* Set to dual FIFO mode according to the SDMA sciprt */ |
90aff15b | 1325 | ret = of_property_read_u32_array(np, "dmas", dmas, 4); |
f3176834 NC |
1326 | if (ssi->use_dma && !ret && dmas[2] == IMX_DMATYPE_SSI_DUAL) { |
1327 | ssi->use_dual_fifo = true; | |
7a8fceb7 NC |
1328 | /* |
1329 | * Use even numbers to avoid channel swap due to SDMA | |
1330 | * script design | |
49da09e2 | 1331 | */ |
f3176834 NC |
1332 | ssi->dma_params_tx.maxburst &= ~0x1; |
1333 | ssi->dma_params_rx.maxburst &= ~0x1; | |
49da09e2 MP |
1334 | } |
1335 | ||
f3176834 | 1336 | if (!ssi->use_dma) { |
4d9b7926 | 1337 | /* |
7a8fceb7 NC |
1338 | * Some boards use an incompatible codec. Use imx-fiq-pcm-audio |
1339 | * to get it working, as DMA is not possible in this situation. | |
4d9b7926 | 1340 | */ |
f3176834 NC |
1341 | ssi->fiq_params.irq = ssi->irq; |
1342 | ssi->fiq_params.base = iomem; | |
1343 | ssi->fiq_params.dma_params_rx = &ssi->dma_params_rx; | |
1344 | ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx; | |
4d9b7926 | 1345 | |
f3176834 | 1346 | ret = imx_pcm_fiq_init(pdev, &ssi->fiq_params); |
4d9b7926 MP |
1347 | if (ret) |
1348 | goto error_pcm; | |
1349 | } else { | |
0d69e0dd | 1350 | ret = imx_pcm_dma_init(pdev, IMX_SSI_DMABUF_SIZE); |
4d9b7926 MP |
1351 | if (ret) |
1352 | goto error_pcm; | |
1353 | } | |
1354 | ||
49da09e2 | 1355 | return 0; |
4d9b7926 MP |
1356 | |
1357 | error_pcm: | |
f3176834 NC |
1358 | if (!ssi->has_ipg_clk_name) |
1359 | clk_disable_unprepare(ssi->clk); | |
af4f7f38 | 1360 | |
4d9b7926 | 1361 | return ret; |
49da09e2 MP |
1362 | } |
1363 | ||
af4f7f38 | 1364 | static void fsl_ssi_imx_clean(struct platform_device *pdev, struct fsl_ssi *ssi) |
49da09e2 | 1365 | { |
f3176834 | 1366 | if (!ssi->use_dma) |
4d9b7926 | 1367 | imx_pcm_fiq_exit(pdev); |
f3176834 NC |
1368 | if (!ssi->has_ipg_clk_name) |
1369 | clk_disable_unprepare(ssi->clk); | |
49da09e2 MP |
1370 | } |
1371 | ||
a0a3d518 | 1372 | static int fsl_ssi_probe(struct platform_device *pdev) |
17467f23 | 1373 | { |
f3176834 | 1374 | struct fsl_ssi *ssi; |
17467f23 | 1375 | int ret = 0; |
38fec727 | 1376 | struct device_node *np = pdev->dev.of_node; |
8483c067 | 1377 | struct device *dev = &pdev->dev; |
c1953bfe | 1378 | const struct of_device_id *of_id; |
f0fba2ad | 1379 | const char *p, *sprop; |
8e9d8690 | 1380 | const uint32_t *iprop; |
ca264189 | 1381 | struct resource *res; |
43248122 | 1382 | void __iomem *iomem; |
f0fba2ad | 1383 | char name[64]; |
6139b1b1 | 1384 | struct regmap_config regconfig = fsl_ssi_regconfig; |
17467f23 | 1385 | |
8483c067 | 1386 | of_id = of_match_device(fsl_ssi_ids, dev); |
fcdbadef | 1387 | if (!of_id || !of_id->data) |
c1953bfe | 1388 | return -EINVAL; |
c1953bfe | 1389 | |
8483c067 | 1390 | ssi = devm_kzalloc(dev, sizeof(*ssi), GFP_KERNEL); |
f3176834 | 1391 | if (!ssi) |
f0fba2ad | 1392 | return -ENOMEM; |
17467f23 | 1393 | |
f3176834 | 1394 | ssi->soc = of_id->data; |
8483c067 | 1395 | ssi->dev = dev; |
fcdbadef | 1396 | |
7a8fceb7 | 1397 | /* Check if being used in AC97 mode */ |
85e59af2 MP |
1398 | sprop = of_get_property(np, "fsl,mode", NULL); |
1399 | if (sprop) { | |
1400 | if (!strcmp(sprop, "ac97-slave")) | |
f3176834 | 1401 | ssi->dai_fmt = SND_SOC_DAIFMT_AC97; |
85e59af2 MP |
1402 | } |
1403 | ||
7a8fceb7 | 1404 | /* Select DMA or FIQ */ |
f3176834 | 1405 | ssi->use_dma = !of_property_read_bool(np, "fsl,fiq-stream-filter"); |
de623ece | 1406 | |
f3176834 NC |
1407 | if (fsl_ssi_is_ac97(ssi)) { |
1408 | memcpy(&ssi->cpu_dai_drv, &fsl_ssi_ac97_dai, | |
af4f7f38 | 1409 | sizeof(fsl_ssi_ac97_dai)); |
f3176834 | 1410 | fsl_ac97_data = ssi; |
cd7f0295 | 1411 | } else { |
f3176834 | 1412 | memcpy(&ssi->cpu_dai_drv, &fsl_ssi_dai_template, |
cd7f0295 MP |
1413 | sizeof(fsl_ssi_dai_template)); |
1414 | } | |
8483c067 | 1415 | ssi->cpu_dai_drv.name = dev_name(dev); |
f0fba2ad | 1416 | |
ca264189 | 1417 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
8483c067 | 1418 | iomem = devm_ioremap_resource(dev, res); |
ca264189 FE |
1419 | if (IS_ERR(iomem)) |
1420 | return PTR_ERR(iomem); | |
f3176834 | 1421 | ssi->ssi_phys = res->start; |
43248122 | 1422 | |
f3176834 | 1423 | if (ssi->soc->imx21regs) { |
7a8fceb7 | 1424 | /* No SACC{ST,EN,DIS} regs in imx21-class SSI */ |
a818aa5f | 1425 | regconfig.max_register = REG_SSI_SRMSK; |
f26b3b2a | 1426 | regconfig.num_reg_defaults_raw = |
a818aa5f | 1427 | REG_SSI_SRMSK / sizeof(uint32_t) + 1; |
6139b1b1 MS |
1428 | } |
1429 | ||
f4a43cab SW |
1430 | ret = of_property_match_string(np, "clock-names", "ipg"); |
1431 | if (ret < 0) { | |
f3176834 | 1432 | ssi->has_ipg_clk_name = false; |
8483c067 | 1433 | ssi->regs = devm_regmap_init_mmio(dev, iomem, ®config); |
f4a43cab | 1434 | } else { |
f3176834 | 1435 | ssi->has_ipg_clk_name = true; |
8483c067 NC |
1436 | ssi->regs = devm_regmap_init_mmio_clk(dev, "ipg", iomem, |
1437 | ®config); | |
f4a43cab | 1438 | } |
f3176834 | 1439 | if (IS_ERR(ssi->regs)) { |
2c225036 | 1440 | dev_err(dev, "failed to init register map\n"); |
f3176834 | 1441 | return PTR_ERR(ssi->regs); |
43248122 | 1442 | } |
1fab6caf | 1443 | |
f3176834 NC |
1444 | ssi->irq = platform_get_irq(pdev, 0); |
1445 | if (ssi->irq < 0) { | |
8483c067 | 1446 | dev_err(dev, "no irq for node %s\n", pdev->name); |
f3176834 | 1447 | return ssi->irq; |
1fab6caf TT |
1448 | } |
1449 | ||
7a8fceb7 | 1450 | /* Set software limitations for synchronous mode */ |
07a9483a | 1451 | if (!of_find_property(np, "fsl,ssi-asynchronous", NULL)) { |
f3176834 NC |
1452 | if (!fsl_ssi_is_ac97(ssi)) { |
1453 | ssi->cpu_dai_drv.symmetric_rates = 1; | |
1454 | ssi->cpu_dai_drv.symmetric_samplebits = 1; | |
10582635 | 1455 | } |
06cb3736 | 1456 | |
f3176834 | 1457 | ssi->cpu_dai_drv.symmetric_channels = 1; |
07a9483a | 1458 | } |
17467f23 | 1459 | |
7a8fceb7 | 1460 | /* Fetch FIFO depth; Set to 8 for older DT without this property */ |
8e9d8690 TT |
1461 | iprop = of_get_property(np, "fsl,fifo-depth", NULL); |
1462 | if (iprop) | |
f3176834 | 1463 | ssi->fifo_depth = be32_to_cpup(iprop); |
8e9d8690 | 1464 | else |
f3176834 | 1465 | ssi->fifo_depth = 8; |
8e9d8690 | 1466 | |
4ee437fb | 1467 | /* |
7a8fceb7 | 1468 | * Configure TX and RX DMA watermarks -- when to send a DMA request |
4ee437fb | 1469 | * |
7a8fceb7 NC |
1470 | * Values should be tested to avoid FIFO under/over run. Set maxburst |
1471 | * to fifo_watermark to maxiumize DMA transaction to reduce overhead. | |
4ee437fb | 1472 | */ |
f3176834 | 1473 | switch (ssi->fifo_depth) { |
4ee437fb CC |
1474 | case 15: |
1475 | /* | |
7a8fceb7 NC |
1476 | * Set to 8 as a balanced configuration -- When TX FIFO has 8 |
1477 | * empty slots, send a DMA request to fill these 8 slots. The | |
1478 | * remaining 7 slots should be able to allow DMA to finish the | |
1479 | * transaction before TX FIFO underruns; Same applies to RX. | |
1480 | * | |
1481 | * Tested with cases running at 48kHz @ 16 bits x 16 channels | |
4ee437fb | 1482 | */ |
f3176834 NC |
1483 | ssi->fifo_watermark = 8; |
1484 | ssi->dma_maxburst = 8; | |
4ee437fb CC |
1485 | break; |
1486 | case 8: | |
1487 | default: | |
7a8fceb7 | 1488 | /* Safely use old watermark configurations for older chips */ |
f3176834 NC |
1489 | ssi->fifo_watermark = ssi->fifo_depth - 2; |
1490 | ssi->dma_maxburst = ssi->fifo_depth - 2; | |
4ee437fb CC |
1491 | break; |
1492 | } | |
1493 | ||
8483c067 | 1494 | dev_set_drvdata(dev, ssi); |
4d9b7926 | 1495 | |
f3176834 NC |
1496 | if (ssi->soc->imx) { |
1497 | ret = fsl_ssi_imx_probe(pdev, ssi, iomem); | |
49da09e2 | 1498 | if (ret) |
2ffa5310 | 1499 | return ret; |
0888efd1 MP |
1500 | } |
1501 | ||
f3176834 NC |
1502 | if (fsl_ssi_is_ac97(ssi)) { |
1503 | mutex_init(&ssi->ac97_reg_lock); | |
695b78b5 MS |
1504 | ret = snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev); |
1505 | if (ret) { | |
2c225036 | 1506 | dev_err(dev, "failed to set AC'97 ops\n"); |
695b78b5 MS |
1507 | goto error_ac97_ops; |
1508 | } | |
1509 | } | |
1510 | ||
8483c067 | 1511 | ret = devm_snd_soc_register_component(dev, &fsl_ssi_component, |
f3176834 | 1512 | &ssi->cpu_dai_drv, 1); |
4d9b7926 | 1513 | if (ret) { |
8483c067 | 1514 | dev_err(dev, "failed to register DAI: %d\n", ret); |
4d9b7926 MP |
1515 | goto error_asoc_register; |
1516 | } | |
1517 | ||
f3176834 | 1518 | if (ssi->use_dma) { |
8483c067 NC |
1519 | ret = devm_request_irq(dev, ssi->irq, fsl_ssi_isr, 0, |
1520 | dev_name(dev), ssi); | |
f0377086 | 1521 | if (ret < 0) { |
2c225036 | 1522 | dev_err(dev, "failed to claim irq %u\n", ssi->irq); |
299e7e97 | 1523 | goto error_asoc_register; |
f0377086 | 1524 | } |
09ce1111 SG |
1525 | } |
1526 | ||
8483c067 | 1527 | ret = fsl_ssi_debugfs_create(&ssi->dbg_stats, dev); |
9368acc4 | 1528 | if (ret) |
299e7e97 | 1529 | goto error_asoc_register; |
09ce1111 | 1530 | |
7a8fceb7 | 1531 | /* Bypass it if using newer DT bindings of ASoC machine drivers */ |
171d683d | 1532 | if (!of_get_property(np, "codec-handle", NULL)) |
09ce1111 | 1533 | goto done; |
09ce1111 | 1534 | |
7a8fceb7 NC |
1535 | /* |
1536 | * Backward compatible for older bindings by manually triggering the | |
1537 | * machine driver's probe(). Use /compatible property, including the | |
1538 | * address of CPU DAI driver structure, as the name of machine driver. | |
f0fba2ad | 1539 | */ |
2b81ec69 SG |
1540 | sprop = of_get_property(of_find_node_by_path("/"), "compatible", NULL); |
1541 | /* Sometimes the compatible name has a "fsl," prefix, so we strip it. */ | |
f0fba2ad LG |
1542 | p = strrchr(sprop, ','); |
1543 | if (p) | |
1544 | sprop = p + 1; | |
1545 | snprintf(name, sizeof(name), "snd-soc-%s", sprop); | |
1546 | make_lowercase(name); | |
1547 | ||
8483c067 | 1548 | ssi->pdev = platform_device_register_data(dev, name, 0, NULL, 0); |
f3176834 NC |
1549 | if (IS_ERR(ssi->pdev)) { |
1550 | ret = PTR_ERR(ssi->pdev); | |
8483c067 | 1551 | dev_err(dev, "failed to register platform: %d\n", ret); |
4d9b7926 | 1552 | goto error_sound_card; |
3f4b783c | 1553 | } |
17467f23 | 1554 | |
09ce1111 | 1555 | done: |
f3176834 | 1556 | if (ssi->dai_fmt) |
8483c067 | 1557 | _fsl_ssi_set_dai_fmt(dev, ssi, ssi->dai_fmt); |
85e59af2 | 1558 | |
f3176834 | 1559 | if (fsl_ssi_is_ac97(ssi)) { |
8ed0c842 MS |
1560 | u32 ssi_idx; |
1561 | ||
1562 | ret = of_property_read_u32(np, "cell-index", &ssi_idx); | |
1563 | if (ret) { | |
2c225036 | 1564 | dev_err(dev, "failed to get SSI index property\n"); |
8ed0c842 MS |
1565 | goto error_sound_card; |
1566 | } | |
1567 | ||
af4f7f38 NC |
1568 | ssi->pdev = platform_device_register_data(NULL, "ac97-codec", |
1569 | ssi_idx, NULL, 0); | |
f3176834 NC |
1570 | if (IS_ERR(ssi->pdev)) { |
1571 | ret = PTR_ERR(ssi->pdev); | |
8483c067 | 1572 | dev_err(dev, |
8ed0c842 MS |
1573 | "failed to register AC97 codec platform: %d\n", |
1574 | ret); | |
1575 | goto error_sound_card; | |
1576 | } | |
1577 | } | |
1578 | ||
f0fba2ad | 1579 | return 0; |
87a0632b | 1580 | |
4d9b7926 | 1581 | error_sound_card: |
f3176834 | 1582 | fsl_ssi_debugfs_remove(&ssi->dbg_stats); |
4d9b7926 | 1583 | error_asoc_register: |
f3176834 | 1584 | if (fsl_ssi_is_ac97(ssi)) |
695b78b5 | 1585 | snd_soc_set_ac97_ops(NULL); |
695b78b5 | 1586 | error_ac97_ops: |
f3176834 NC |
1587 | if (fsl_ssi_is_ac97(ssi)) |
1588 | mutex_destroy(&ssi->ac97_reg_lock); | |
b880b805 | 1589 | |
f3176834 NC |
1590 | if (ssi->soc->imx) |
1591 | fsl_ssi_imx_clean(pdev, ssi); | |
1fab6caf | 1592 | |
87a0632b | 1593 | return ret; |
17467f23 | 1594 | } |
17467f23 | 1595 | |
38fec727 | 1596 | static int fsl_ssi_remove(struct platform_device *pdev) |
17467f23 | 1597 | { |
f3176834 | 1598 | struct fsl_ssi *ssi = dev_get_drvdata(&pdev->dev); |
17467f23 | 1599 | |
f3176834 | 1600 | fsl_ssi_debugfs_remove(&ssi->dbg_stats); |
9368acc4 | 1601 | |
f3176834 NC |
1602 | if (ssi->pdev) |
1603 | platform_device_unregister(ssi->pdev); | |
49da09e2 | 1604 | |
f3176834 NC |
1605 | if (ssi->soc->imx) |
1606 | fsl_ssi_imx_clean(pdev, ssi); | |
49da09e2 | 1607 | |
f3176834 | 1608 | if (fsl_ssi_is_ac97(ssi)) { |
04143d61 | 1609 | snd_soc_set_ac97_ops(NULL); |
f3176834 | 1610 | mutex_destroy(&ssi->ac97_reg_lock); |
b880b805 | 1611 | } |
04143d61 | 1612 | |
f0fba2ad | 1613 | return 0; |
17467f23 | 1614 | } |
f0fba2ad | 1615 | |
05cf2379 ZW |
1616 | #ifdef CONFIG_PM_SLEEP |
1617 | static int fsl_ssi_suspend(struct device *dev) | |
1618 | { | |
f3176834 NC |
1619 | struct fsl_ssi *ssi = dev_get_drvdata(dev); |
1620 | struct regmap *regs = ssi->regs; | |
05cf2379 | 1621 | |
a818aa5f NC |
1622 | regmap_read(regs, REG_SSI_SFCSR, &ssi->regcache_sfcsr); |
1623 | regmap_read(regs, REG_SSI_SACNT, &ssi->regcache_sacnt); | |
05cf2379 ZW |
1624 | |
1625 | regcache_cache_only(regs, true); | |
1626 | regcache_mark_dirty(regs); | |
1627 | ||
1628 | return 0; | |
1629 | } | |
1630 | ||
1631 | static int fsl_ssi_resume(struct device *dev) | |
1632 | { | |
f3176834 NC |
1633 | struct fsl_ssi *ssi = dev_get_drvdata(dev); |
1634 | struct regmap *regs = ssi->regs; | |
05cf2379 ZW |
1635 | |
1636 | regcache_cache_only(regs, false); | |
1637 | ||
a818aa5f | 1638 | regmap_update_bits(regs, REG_SSI_SFCSR, |
af4f7f38 NC |
1639 | SSI_SFCSR_RFWM1_MASK | SSI_SFCSR_TFWM1_MASK | |
1640 | SSI_SFCSR_RFWM0_MASK | SSI_SFCSR_TFWM0_MASK, | |
1641 | ssi->regcache_sfcsr); | |
a818aa5f | 1642 | regmap_write(regs, REG_SSI_SACNT, ssi->regcache_sacnt); |
05cf2379 ZW |
1643 | |
1644 | return regcache_sync(regs); | |
1645 | } | |
1646 | #endif /* CONFIG_PM_SLEEP */ | |
1647 | ||
1648 | static const struct dev_pm_ops fsl_ssi_pm = { | |
1649 | SET_SYSTEM_SLEEP_PM_OPS(fsl_ssi_suspend, fsl_ssi_resume) | |
1650 | }; | |
1651 | ||
f07eb223 | 1652 | static struct platform_driver fsl_ssi_driver = { |
f0fba2ad LG |
1653 | .driver = { |
1654 | .name = "fsl-ssi-dai", | |
f0fba2ad | 1655 | .of_match_table = fsl_ssi_ids, |
05cf2379 | 1656 | .pm = &fsl_ssi_pm, |
f0fba2ad LG |
1657 | }, |
1658 | .probe = fsl_ssi_probe, | |
1659 | .remove = fsl_ssi_remove, | |
1660 | }; | |
17467f23 | 1661 | |
ba0a7e02 | 1662 | module_platform_driver(fsl_ssi_driver); |
a454dad1 | 1663 | |
f3142807 | 1664 | MODULE_ALIAS("platform:fsl-ssi-dai"); |
17467f23 TT |
1665 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); |
1666 | MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver"); | |
f0fba2ad | 1667 | MODULE_LICENSE("GPL v2"); |