blob: 2f2d837df07f078e5b60da38308cbdb8d53b9969 [file] [log] [blame]
Timur Tabi17467f22008-01-11 18:15:26 +01001/*
2 * Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver
3 *
4 * Author: Timur Tabi <timur@freescale.com>
5 *
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +00006 * 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.
Timur Tabi17467f22008-01-11 18:15:26 +010011 */
12
13#include <linux/init.h>
Shawn Guodfa1a102012-03-16 16:56:42 +080014#include <linux/io.h>
Timur Tabi17467f22008-01-11 18:15:26 +010015#include <linux/module.h>
16#include <linux/interrupt.h>
Shawn Guo95cd98f2012-03-29 10:53:41 +080017#include <linux/clk.h>
Timur Tabi17467f22008-01-11 18:15:26 +010018#include <linux/device.h>
19#include <linux/delay.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090020#include <linux/slab.h>
Shawn Guodfa1a102012-03-16 16:56:42 +080021#include <linux/of_address.h>
22#include <linux/of_irq.h>
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +000023#include <linux/of_platform.h>
Timur Tabi17467f22008-01-11 18:15:26 +010024
Timur Tabi17467f22008-01-11 18:15:26 +010025#include <sound/core.h>
26#include <sound/pcm.h>
27#include <sound/pcm_params.h>
28#include <sound/initval.h>
29#include <sound/soc.h>
Lars-Peter Clausena8909c92013-04-03 11:06:04 +020030#include <sound/dmaengine_pcm.h>
Timur Tabi17467f22008-01-11 18:15:26 +010031
Timur Tabi17467f22008-01-11 18:15:26 +010032#include "fsl_ssi.h"
Shawn Guo09ce1112012-03-16 16:56:43 +080033#include "imx-pcm.h"
Timur Tabi17467f22008-01-11 18:15:26 +010034
Shawn Guodfa1a102012-03-16 16:56:42 +080035#ifdef PPC
36#define read_ssi(addr) in_be32(addr)
37#define write_ssi(val, addr) out_be32(addr, val)
38#define write_ssi_mask(addr, clear, set) clrsetbits_be32(addr, clear, set)
39#elif defined ARM
40#define read_ssi(addr) readl(addr)
41#define write_ssi(val, addr) writel(val, addr)
42/*
43 * FIXME: Proper locking should be added at write_ssi_mask caller level
44 * to ensure this register read/modify/write sequence is race free.
45 */
46static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set)
47{
48 u32 val = readl(addr);
49 val = (val & ~clear) | set;
50 writel(val, addr);
51}
52#endif
53
Timur Tabi17467f22008-01-11 18:15:26 +010054/**
55 * FSLSSI_I2S_RATES: sample rates supported by the I2S
56 *
57 * This driver currently only supports the SSI running in I2S slave mode,
58 * which means the codec determines the sample rate. Therefore, we tell
59 * ALSA that we support all rates and let the codec driver decide what rates
60 * are really supported.
61 */
62#define FSLSSI_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
63 SNDRV_PCM_RATE_CONTINUOUS)
64
65/**
66 * FSLSSI_I2S_FORMATS: audio formats supported by the SSI
67 *
68 * This driver currently only supports the SSI running in I2S slave mode.
69 *
70 * The SSI has a limitation in that the samples must be in the same byte
71 * order as the host CPU. This is because when multiple bytes are written
72 * to the STX register, the bytes and bits must be written in the same
73 * order. The STX is a shift register, so all the bits need to be aligned
74 * (bit-endianness must match byte-endianness). Processors typically write
75 * the bits within a byte in the same order that the bytes of a word are
76 * written in. So if the host CPU is big-endian, then only big-endian
77 * samples will be written to STX properly.
78 */
79#ifdef __BIG_ENDIAN
80#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
81 SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_S20_3BE | \
82 SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_S24_BE)
83#else
84#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
85 SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \
86 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
87#endif
88
Timur Tabid5a908b2009-03-26 11:42:38 -050089/* SIER bitflag of interrupts to enable */
90#define SIER_FLAGS (CCSR_SSI_SIER_TFRC_EN | CCSR_SSI_SIER_TDMAE | \
91 CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TUE0_EN | \
92 CCSR_SSI_SIER_TUE1_EN | CCSR_SSI_SIER_RFRC_EN | \
93 CCSR_SSI_SIER_RDMAE | CCSR_SSI_SIER_RIE | \
94 CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_ROE1_EN)
95
Timur Tabi17467f22008-01-11 18:15:26 +010096/**
97 * fsl_ssi_private: per-SSI private data
98 *
Timur Tabi17467f22008-01-11 18:15:26 +010099 * @ssi: pointer to the SSI's registers
100 * @ssi_phys: physical address of the SSI registers
101 * @irq: IRQ of this SSI
Timur Tabibe41e942008-07-28 17:04:39 -0500102 * @first_stream: pointer to the stream that was opened first
103 * @second_stream: pointer to second stream
Timur Tabi17467f22008-01-11 18:15:26 +0100104 * @playback: the number of playback streams opened
105 * @capture: the number of capture streams opened
106 * @cpu_dai: the CPU DAI for this device
107 * @dev_attr: the sysfs device attribute structure
108 * @stats: SSI statistics
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000109 * @name: name for this device
Timur Tabi17467f22008-01-11 18:15:26 +0100110 */
111struct fsl_ssi_private {
Timur Tabi17467f22008-01-11 18:15:26 +0100112 struct ccsr_ssi __iomem *ssi;
113 dma_addr_t ssi_phys;
114 unsigned int irq;
Timur Tabibe41e942008-07-28 17:04:39 -0500115 struct snd_pcm_substream *first_stream;
116 struct snd_pcm_substream *second_stream;
Timur Tabi8e9d8692010-08-06 12:16:12 -0500117 unsigned int fifo_depth;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000118 struct snd_soc_dai_driver cpu_dai_drv;
Timur Tabi17467f22008-01-11 18:15:26 +0100119 struct device_attribute dev_attr;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000120 struct platform_device *pdev;
Timur Tabi17467f22008-01-11 18:15:26 +0100121
Shawn Guo09ce1112012-03-16 16:56:43 +0800122 bool new_binding;
123 bool ssi_on_imx;
Shawn Guo95cd98f2012-03-29 10:53:41 +0800124 struct clk *clk;
Lars-Peter Clausena8909c92013-04-03 11:06:04 +0200125 struct snd_dmaengine_dai_dma_data dma_params_tx;
126 struct snd_dmaengine_dai_dma_data dma_params_rx;
127 struct imx_dma_data filter_data_tx;
128 struct imx_dma_data filter_data_rx;
Shawn Guo09ce1112012-03-16 16:56:43 +0800129
Timur Tabi17467f22008-01-11 18:15:26 +0100130 struct {
131 unsigned int rfrc;
132 unsigned int tfrc;
133 unsigned int cmdau;
134 unsigned int cmddu;
135 unsigned int rxt;
136 unsigned int rdr1;
137 unsigned int rdr0;
138 unsigned int tde1;
139 unsigned int tde0;
140 unsigned int roe1;
141 unsigned int roe0;
142 unsigned int tue1;
143 unsigned int tue0;
144 unsigned int tfs;
145 unsigned int rfs;
146 unsigned int tls;
147 unsigned int rls;
148 unsigned int rff1;
149 unsigned int rff0;
150 unsigned int tfe1;
151 unsigned int tfe0;
152 } stats;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000153
154 char name[1];
Timur Tabi17467f22008-01-11 18:15:26 +0100155};
156
157/**
158 * fsl_ssi_isr: SSI interrupt handler
159 *
160 * Although it's possible to use the interrupt handler to send and receive
161 * data to/from the SSI, we use the DMA instead. Programming is more
162 * complicated, but the performance is much better.
163 *
164 * This interrupt handler is used only to gather statistics.
165 *
166 * @irq: IRQ of the SSI device
167 * @dev_id: pointer to the ssi_private structure for this SSI device
168 */
169static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
170{
171 struct fsl_ssi_private *ssi_private = dev_id;
172 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
173 irqreturn_t ret = IRQ_NONE;
174 __be32 sisr;
175 __be32 sisr2 = 0;
176
177 /* We got an interrupt, so read the status register to see what we
178 were interrupted for. We mask it with the Interrupt Enable register
179 so that we only check for events that we're interested in.
180 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800181 sisr = read_ssi(&ssi->sisr) & SIER_FLAGS;
Timur Tabi17467f22008-01-11 18:15:26 +0100182
183 if (sisr & CCSR_SSI_SISR_RFRC) {
184 ssi_private->stats.rfrc++;
185 sisr2 |= CCSR_SSI_SISR_RFRC;
186 ret = IRQ_HANDLED;
187 }
188
189 if (sisr & CCSR_SSI_SISR_TFRC) {
190 ssi_private->stats.tfrc++;
191 sisr2 |= CCSR_SSI_SISR_TFRC;
192 ret = IRQ_HANDLED;
193 }
194
195 if (sisr & CCSR_SSI_SISR_CMDAU) {
196 ssi_private->stats.cmdau++;
197 ret = IRQ_HANDLED;
198 }
199
200 if (sisr & CCSR_SSI_SISR_CMDDU) {
201 ssi_private->stats.cmddu++;
202 ret = IRQ_HANDLED;
203 }
204
205 if (sisr & CCSR_SSI_SISR_RXT) {
206 ssi_private->stats.rxt++;
207 ret = IRQ_HANDLED;
208 }
209
210 if (sisr & CCSR_SSI_SISR_RDR1) {
211 ssi_private->stats.rdr1++;
212 ret = IRQ_HANDLED;
213 }
214
215 if (sisr & CCSR_SSI_SISR_RDR0) {
216 ssi_private->stats.rdr0++;
217 ret = IRQ_HANDLED;
218 }
219
220 if (sisr & CCSR_SSI_SISR_TDE1) {
221 ssi_private->stats.tde1++;
222 ret = IRQ_HANDLED;
223 }
224
225 if (sisr & CCSR_SSI_SISR_TDE0) {
226 ssi_private->stats.tde0++;
227 ret = IRQ_HANDLED;
228 }
229
230 if (sisr & CCSR_SSI_SISR_ROE1) {
231 ssi_private->stats.roe1++;
232 sisr2 |= CCSR_SSI_SISR_ROE1;
233 ret = IRQ_HANDLED;
234 }
235
236 if (sisr & CCSR_SSI_SISR_ROE0) {
237 ssi_private->stats.roe0++;
238 sisr2 |= CCSR_SSI_SISR_ROE0;
239 ret = IRQ_HANDLED;
240 }
241
242 if (sisr & CCSR_SSI_SISR_TUE1) {
243 ssi_private->stats.tue1++;
244 sisr2 |= CCSR_SSI_SISR_TUE1;
245 ret = IRQ_HANDLED;
246 }
247
248 if (sisr & CCSR_SSI_SISR_TUE0) {
249 ssi_private->stats.tue0++;
250 sisr2 |= CCSR_SSI_SISR_TUE0;
251 ret = IRQ_HANDLED;
252 }
253
254 if (sisr & CCSR_SSI_SISR_TFS) {
255 ssi_private->stats.tfs++;
256 ret = IRQ_HANDLED;
257 }
258
259 if (sisr & CCSR_SSI_SISR_RFS) {
260 ssi_private->stats.rfs++;
261 ret = IRQ_HANDLED;
262 }
263
264 if (sisr & CCSR_SSI_SISR_TLS) {
265 ssi_private->stats.tls++;
266 ret = IRQ_HANDLED;
267 }
268
269 if (sisr & CCSR_SSI_SISR_RLS) {
270 ssi_private->stats.rls++;
271 ret = IRQ_HANDLED;
272 }
273
274 if (sisr & CCSR_SSI_SISR_RFF1) {
275 ssi_private->stats.rff1++;
276 ret = IRQ_HANDLED;
277 }
278
279 if (sisr & CCSR_SSI_SISR_RFF0) {
280 ssi_private->stats.rff0++;
281 ret = IRQ_HANDLED;
282 }
283
284 if (sisr & CCSR_SSI_SISR_TFE1) {
285 ssi_private->stats.tfe1++;
286 ret = IRQ_HANDLED;
287 }
288
289 if (sisr & CCSR_SSI_SISR_TFE0) {
290 ssi_private->stats.tfe0++;
291 ret = IRQ_HANDLED;
292 }
293
294 /* Clear the bits that we set */
295 if (sisr2)
Shawn Guodfa1a102012-03-16 16:56:42 +0800296 write_ssi(sisr2, &ssi->sisr);
Timur Tabi17467f22008-01-11 18:15:26 +0100297
298 return ret;
299}
300
301/**
302 * fsl_ssi_startup: create a new substream
303 *
304 * This is the first function called when a stream is opened.
305 *
306 * If this is the first stream open, then grab the IRQ and program most of
307 * the SSI registers.
308 */
Mark Browndee89c42008-11-18 22:11:38 +0000309static int fsl_ssi_startup(struct snd_pcm_substream *substream,
310 struct snd_soc_dai *dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100311{
312 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Timur Tabi5e538ec2011-09-13 12:59:37 -0500313 struct fsl_ssi_private *ssi_private =
314 snd_soc_dai_get_drvdata(rtd->cpu_dai);
315 int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
Timur Tabi17467f22008-01-11 18:15:26 +0100316
317 /*
318 * If this is the first stream opened, then request the IRQ
319 * and initialize the SSI registers.
320 */
Timur Tabi5e538ec2011-09-13 12:59:37 -0500321 if (!ssi_private->first_stream) {
Timur Tabi17467f22008-01-11 18:15:26 +0100322 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
Timur Tabi17467f22008-01-11 18:15:26 +0100323
Timur Tabi5e538ec2011-09-13 12:59:37 -0500324 ssi_private->first_stream = substream;
325
Timur Tabi17467f22008-01-11 18:15:26 +0100326 /*
327 * Section 16.5 of the MPC8610 reference manual says that the
328 * SSI needs to be disabled before updating the registers we set
329 * here.
330 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800331 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
Timur Tabi17467f22008-01-11 18:15:26 +0100332
333 /*
334 * Program the SSI into I2S Slave Non-Network Synchronous mode.
335 * Also enable the transmit and receive FIFO.
336 *
337 * FIXME: Little-endian samples require a different shift dir
338 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800339 write_ssi_mask(&ssi->scr,
Timur Tabia454dad2009-03-05 17:23:37 -0600340 CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
341 CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE
Timur Tabi5e538ec2011-09-13 12:59:37 -0500342 | (synchronous ? CCSR_SSI_SCR_SYN : 0));
Timur Tabi17467f22008-01-11 18:15:26 +0100343
Shawn Guodfa1a102012-03-16 16:56:42 +0800344 write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
Timur Tabi17467f22008-01-11 18:15:26 +0100345 CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
Shawn Guodfa1a102012-03-16 16:56:42 +0800346 CCSR_SSI_STCR_TSCKP, &ssi->stcr);
Timur Tabi17467f22008-01-11 18:15:26 +0100347
Shawn Guodfa1a102012-03-16 16:56:42 +0800348 write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
Timur Tabi17467f22008-01-11 18:15:26 +0100349 CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
Shawn Guodfa1a102012-03-16 16:56:42 +0800350 CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
Timur Tabi17467f22008-01-11 18:15:26 +0100351
352 /*
353 * The DC and PM bits are only used if the SSI is the clock
354 * master.
355 */
356
Timur Tabi5e538ec2011-09-13 12:59:37 -0500357 /* Enable the interrupts and DMA requests */
Shawn Guodfa1a102012-03-16 16:56:42 +0800358 write_ssi(SIER_FLAGS, &ssi->sier);
Timur Tabi17467f22008-01-11 18:15:26 +0100359
360 /*
361 * Set the watermark for transmit FIFI 0 and receive FIFO 0. We
Timur Tabi8e9d8692010-08-06 12:16:12 -0500362 * don't use FIFO 1. We program the transmit water to signal a
363 * DMA transfer if there are only two (or fewer) elements left
364 * in the FIFO. Two elements equals one frame (left channel,
365 * right channel). This value, however, depends on the depth of
366 * the transmit buffer.
367 *
368 * We program the receive FIFO to notify us if at least two
369 * elements (one frame) have been written to the FIFO. We could
370 * make this value larger (and maybe we should), but this way
371 * data will be written to memory as soon as it's available.
Timur Tabi17467f22008-01-11 18:15:26 +0100372 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800373 write_ssi(CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) |
374 CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2),
375 &ssi->sfcsr);
Timur Tabi17467f22008-01-11 18:15:26 +0100376
377 /*
378 * We keep the SSI disabled because if we enable it, then the
379 * DMA controller will start. It's not supposed to start until
380 * the SCR.TE (or SCR.RE) bit is set, but it does anyway. The
381 * DMA controller will transfer one "BWC" of data (i.e. the
382 * amount of data that the MR.BWC bits are set to). The reason
383 * this is bad is because at this point, the PCM driver has not
384 * finished initializing the DMA controller.
385 */
Timur Tabi5e538ec2011-09-13 12:59:37 -0500386 } else {
387 if (synchronous) {
388 struct snd_pcm_runtime *first_runtime =
389 ssi_private->first_stream->runtime;
390 /*
391 * This is the second stream open, and we're in
392 * synchronous mode, so we need to impose sample
393 * sample size constraints. This is because STCCR is
394 * used for playback and capture in synchronous mode,
395 * so there's no way to specify different word
396 * lengths.
397 *
398 * Note that this can cause a race condition if the
399 * second stream is opened before the first stream is
400 * fully initialized. We provide some protection by
401 * checking to make sure the first stream is
402 * initialized, but it's not perfect. ALSA sometimes
403 * re-initializes the driver with a different sample
404 * rate or size. If the second stream is opened
405 * before the first stream has received its final
406 * parameters, then the second stream may be
407 * constrained to the wrong sample rate or size.
408 */
409 if (!first_runtime->sample_bits) {
410 dev_err(substream->pcm->card->dev,
411 "set sample size in %s stream first\n",
412 substream->stream ==
413 SNDRV_PCM_STREAM_PLAYBACK
414 ? "capture" : "playback");
415 return -EAGAIN;
416 }
Timur Tabi17467f22008-01-11 18:15:26 +0100417
Timur Tabia454dad2009-03-05 17:23:37 -0600418 snd_pcm_hw_constraint_minmax(substream->runtime,
419 SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
420 first_runtime->sample_bits,
421 first_runtime->sample_bits);
Timur Tabi5e538ec2011-09-13 12:59:37 -0500422 }
Timur Tabibe41e942008-07-28 17:04:39 -0500423
424 ssi_private->second_stream = substream;
425 }
426
Timur Tabi17467f22008-01-11 18:15:26 +0100427 return 0;
428}
429
430/**
Timur Tabi85ef2372009-02-05 17:56:02 -0600431 * fsl_ssi_hw_params - program the sample size
Timur Tabi17467f22008-01-11 18:15:26 +0100432 *
433 * Most of the SSI registers have been programmed in the startup function,
434 * but the word length must be programmed here. Unfortunately, programming
435 * the SxCCR.WL bits requires the SSI to be temporarily disabled. This can
436 * cause a problem with supporting simultaneous playback and capture. If
437 * the SSI is already playing a stream, then that stream may be temporarily
438 * stopped when you start capture.
439 *
440 * Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the
441 * clock master.
442 */
Timur Tabi85ef2372009-02-05 17:56:02 -0600443static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
444 struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100445{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000446 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
Timur Tabi5e538ec2011-09-13 12:59:37 -0500447 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
448 unsigned int sample_size =
449 snd_pcm_format_width(params_format(hw_params));
450 u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
Shawn Guodfa1a102012-03-16 16:56:42 +0800451 int enabled = read_ssi(&ssi->scr) & CCSR_SSI_SCR_SSIEN;
Timur Tabi17467f22008-01-11 18:15:26 +0100452
Timur Tabi5e538ec2011-09-13 12:59:37 -0500453 /*
454 * If we're in synchronous mode, and the SSI is already enabled,
455 * then STCCR is already set properly.
456 */
457 if (enabled && ssi_private->cpu_dai_drv.symmetric_rates)
458 return 0;
Timur Tabi17467f22008-01-11 18:15:26 +0100459
Timur Tabi5e538ec2011-09-13 12:59:37 -0500460 /*
461 * FIXME: The documentation says that SxCCR[WL] should not be
462 * modified while the SSI is enabled. The only time this can
463 * happen is if we're trying to do simultaneous playback and
464 * capture in asynchronous mode. Unfortunately, I have been enable
465 * to get that to work at all on the P1022DS. Therefore, we don't
466 * bother to disable/enable the SSI when setting SxCCR[WL], because
467 * the SSI will stop anyway. Maybe one day, this will get fixed.
468 */
Timur Tabi17467f22008-01-11 18:15:26 +0100469
Timur Tabi5e538ec2011-09-13 12:59:37 -0500470 /* In synchronous mode, the SSI uses STCCR for capture */
471 if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
472 ssi_private->cpu_dai_drv.symmetric_rates)
Shawn Guodfa1a102012-03-16 16:56:42 +0800473 write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
Timur Tabi5e538ec2011-09-13 12:59:37 -0500474 else
Shawn Guodfa1a102012-03-16 16:56:42 +0800475 write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
Timur Tabi17467f22008-01-11 18:15:26 +0100476
477 return 0;
478}
479
480/**
481 * fsl_ssi_trigger: start and stop the DMA transfer.
482 *
483 * This function is called by ALSA to start, stop, pause, and resume the DMA
484 * transfer of data.
485 *
486 * The DMA channel is in external master start and pause mode, which
487 * means the SSI completely controls the flow of data.
488 */
Mark Browndee89c42008-11-18 22:11:38 +0000489static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
490 struct snd_soc_dai *dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100491{
492 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000493 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
Timur Tabi17467f22008-01-11 18:15:26 +0100494 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
495
496 switch (cmd) {
497 case SNDRV_PCM_TRIGGER_START:
Timur Tabi17467f22008-01-11 18:15:26 +0100498 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
Timur Tabia4d11fe2009-03-25 18:20:37 -0500499 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
Shawn Guodfa1a102012-03-16 16:56:42 +0800500 write_ssi_mask(&ssi->scr, 0,
Timur Tabibe41e942008-07-28 17:04:39 -0500501 CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE);
Timur Tabia4d11fe2009-03-25 18:20:37 -0500502 else
Shawn Guodfa1a102012-03-16 16:56:42 +0800503 write_ssi_mask(&ssi->scr, 0,
Timur Tabibe41e942008-07-28 17:04:39 -0500504 CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE);
Timur Tabi17467f22008-01-11 18:15:26 +0100505 break;
506
507 case SNDRV_PCM_TRIGGER_STOP:
Timur Tabi17467f22008-01-11 18:15:26 +0100508 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
509 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
Shawn Guodfa1a102012-03-16 16:56:42 +0800510 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TE, 0);
Timur Tabi17467f22008-01-11 18:15:26 +0100511 else
Shawn Guodfa1a102012-03-16 16:56:42 +0800512 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0);
Timur Tabi17467f22008-01-11 18:15:26 +0100513 break;
514
515 default:
516 return -EINVAL;
517 }
518
519 return 0;
520}
521
522/**
523 * fsl_ssi_shutdown: shutdown the SSI
524 *
525 * Shutdown the SSI if there are no other substreams open.
526 */
Mark Browndee89c42008-11-18 22:11:38 +0000527static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
528 struct snd_soc_dai *dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100529{
530 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000531 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
Timur Tabi17467f22008-01-11 18:15:26 +0100532
Timur Tabibe41e942008-07-28 17:04:39 -0500533 if (ssi_private->first_stream == substream)
534 ssi_private->first_stream = ssi_private->second_stream;
535
536 ssi_private->second_stream = NULL;
537
Timur Tabi17467f22008-01-11 18:15:26 +0100538 /*
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400539 * If this is the last active substream, disable the SSI.
Timur Tabi17467f22008-01-11 18:15:26 +0100540 */
Timur Tabi5e538ec2011-09-13 12:59:37 -0500541 if (!ssi_private->first_stream) {
Timur Tabi17467f22008-01-11 18:15:26 +0100542 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
543
Shawn Guodfa1a102012-03-16 16:56:42 +0800544 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
Timur Tabi17467f22008-01-11 18:15:26 +0100545 }
546}
547
Lars-Peter Clausenfc8ba7f2013-04-15 19:19:58 +0200548static int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
549{
550 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai);
551
552 if (ssi_private->ssi_on_imx) {
553 dai->playback_dma_data = &ssi_private->dma_params_tx;
554 dai->capture_dma_data = &ssi_private->dma_params_rx;
555 }
556
557 return 0;
558}
559
Lars-Peter Clausen85e76522011-11-23 11:40:40 +0100560static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
Eric Miao6335d052009-03-03 09:41:00 +0800561 .startup = fsl_ssi_startup,
562 .hw_params = fsl_ssi_hw_params,
563 .shutdown = fsl_ssi_shutdown,
564 .trigger = fsl_ssi_trigger,
Eric Miao6335d052009-03-03 09:41:00 +0800565};
566
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000567/* Template for the CPU dai driver structure */
568static struct snd_soc_dai_driver fsl_ssi_dai_template = {
Lars-Peter Clausenfc8ba7f2013-04-15 19:19:58 +0200569 .probe = fsl_ssi_dai_probe,
Timur Tabi17467f22008-01-11 18:15:26 +0100570 .playback = {
571 /* The SSI does not support monaural audio. */
572 .channels_min = 2,
573 .channels_max = 2,
574 .rates = FSLSSI_I2S_RATES,
575 .formats = FSLSSI_I2S_FORMATS,
576 },
577 .capture = {
578 .channels_min = 2,
579 .channels_max = 2,
580 .rates = FSLSSI_I2S_RATES,
581 .formats = FSLSSI_I2S_FORMATS,
582 },
Eric Miao6335d052009-03-03 09:41:00 +0800583 .ops = &fsl_ssi_dai_ops,
Timur Tabi17467f22008-01-11 18:15:26 +0100584};
585
Kuninori Morimoto3580aa12013-03-21 03:32:04 -0700586static const struct snd_soc_component_driver fsl_ssi_component = {
587 .name = "fsl-ssi",
588};
589
Timur Tabid5a908b2009-03-26 11:42:38 -0500590/* Show the statistics of a flag only if its interrupt is enabled. The
591 * compiler will optimze this code to a no-op if the interrupt is not
592 * enabled.
593 */
594#define SIER_SHOW(flag, name) \
595 do { \
596 if (SIER_FLAGS & CCSR_SSI_SIER_##flag) \
597 length += sprintf(buf + length, #name "=%u\n", \
598 ssi_private->stats.name); \
599 } while (0)
600
601
Timur Tabi17467f22008-01-11 18:15:26 +0100602/**
603 * fsl_sysfs_ssi_show: display SSI statistics
604 *
Timur Tabid5a908b2009-03-26 11:42:38 -0500605 * Display the statistics for the current SSI device. To avoid confusion,
606 * we only show those counts that are enabled.
Timur Tabi17467f22008-01-11 18:15:26 +0100607 */
608static ssize_t fsl_sysfs_ssi_show(struct device *dev,
609 struct device_attribute *attr, char *buf)
610{
611 struct fsl_ssi_private *ssi_private =
Timur Tabid5a908b2009-03-26 11:42:38 -0500612 container_of(attr, struct fsl_ssi_private, dev_attr);
613 ssize_t length = 0;
Timur Tabi17467f22008-01-11 18:15:26 +0100614
Timur Tabid5a908b2009-03-26 11:42:38 -0500615 SIER_SHOW(RFRC_EN, rfrc);
616 SIER_SHOW(TFRC_EN, tfrc);
617 SIER_SHOW(CMDAU_EN, cmdau);
618 SIER_SHOW(CMDDU_EN, cmddu);
619 SIER_SHOW(RXT_EN, rxt);
620 SIER_SHOW(RDR1_EN, rdr1);
621 SIER_SHOW(RDR0_EN, rdr0);
622 SIER_SHOW(TDE1_EN, tde1);
623 SIER_SHOW(TDE0_EN, tde0);
624 SIER_SHOW(ROE1_EN, roe1);
625 SIER_SHOW(ROE0_EN, roe0);
626 SIER_SHOW(TUE1_EN, tue1);
627 SIER_SHOW(TUE0_EN, tue0);
628 SIER_SHOW(TFS_EN, tfs);
629 SIER_SHOW(RFS_EN, rfs);
630 SIER_SHOW(TLS_EN, tls);
631 SIER_SHOW(RLS_EN, rls);
632 SIER_SHOW(RFF1_EN, rff1);
633 SIER_SHOW(RFF0_EN, rff0);
634 SIER_SHOW(TFE1_EN, tfe1);
635 SIER_SHOW(TFE0_EN, tfe0);
Timur Tabi17467f22008-01-11 18:15:26 +0100636
637 return length;
638}
639
640/**
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000641 * Make every character in a string lower-case
Timur Tabi17467f22008-01-11 18:15:26 +0100642 */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000643static void make_lowercase(char *s)
Timur Tabi17467f22008-01-11 18:15:26 +0100644{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000645 char *p = s;
646 char c;
647
648 while ((c = *p)) {
649 if ((c >= 'A') && (c <= 'Z'))
650 *p = c + ('a' - 'A');
651 p++;
652 }
653}
654
Bill Pembertona0a3d512012-12-07 09:26:16 -0500655static int fsl_ssi_probe(struct platform_device *pdev)
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000656{
Timur Tabi17467f22008-01-11 18:15:26 +0100657 struct fsl_ssi_private *ssi_private;
658 int ret = 0;
Timur Tabi87a06322010-08-03 17:55:28 -0500659 struct device_attribute *dev_attr = NULL;
Timur Tabi38fec722010-08-19 15:26:58 -0500660 struct device_node *np = pdev->dev.of_node;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000661 const char *p, *sprop;
Timur Tabi8e9d8692010-08-06 12:16:12 -0500662 const uint32_t *iprop;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000663 struct resource res;
664 char name[64];
Lars-Peter Clausen312bb4f2013-03-22 14:12:12 +0100665 bool shared;
Timur Tabi17467f22008-01-11 18:15:26 +0100666
Timur Tabiff713342010-08-04 17:51:08 -0500667 /* SSIs that are not connected on the board should have a
668 * status = "disabled"
669 * property in their device tree nodes.
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000670 */
Timur Tabiff713342010-08-04 17:51:08 -0500671 if (!of_device_is_available(np))
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000672 return -ENODEV;
673
674 /* We only support the SSI in "I2S Slave" mode */
675 sprop = of_get_property(np, "fsl,mode", NULL);
676 if (!sprop || strcmp(sprop, "i2s-slave")) {
Timur Tabi38fec722010-08-19 15:26:58 -0500677 dev_notice(&pdev->dev, "mode %s is unsupported\n", sprop);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000678 return -ENODEV;
Timur Tabi17467f22008-01-11 18:15:26 +0100679 }
Timur Tabi17467f22008-01-11 18:15:26 +0100680
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000681 /* The DAI name is the last part of the full name of the node. */
682 p = strrchr(np->full_name, '/') + 1;
683 ssi_private = kzalloc(sizeof(struct fsl_ssi_private) + strlen(p),
684 GFP_KERNEL);
685 if (!ssi_private) {
Timur Tabi38fec722010-08-19 15:26:58 -0500686 dev_err(&pdev->dev, "could not allocate DAI object\n");
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000687 return -ENOMEM;
688 }
Timur Tabi17467f22008-01-11 18:15:26 +0100689
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000690 strcpy(ssi_private->name, p);
Timur Tabi17467f22008-01-11 18:15:26 +0100691
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000692 /* Initialize this copy of the CPU DAI driver structure */
693 memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
694 sizeof(fsl_ssi_dai_template));
695 ssi_private->cpu_dai_drv.name = ssi_private->name;
696
697 /* Get the addresses and IRQ */
698 ret = of_address_to_resource(np, 0, &res);
699 if (ret) {
Timur Tabi38fec722010-08-19 15:26:58 -0500700 dev_err(&pdev->dev, "could not determine device resources\n");
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400701 goto error_kmalloc;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000702 }
Timur Tabi147dfe92011-06-08 15:02:55 -0500703 ssi_private->ssi = of_iomap(np, 0);
704 if (!ssi_private->ssi) {
705 dev_err(&pdev->dev, "could not map device resources\n");
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400706 ret = -ENOMEM;
707 goto error_kmalloc;
Timur Tabi147dfe92011-06-08 15:02:55 -0500708 }
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000709 ssi_private->ssi_phys = res.start;
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400710
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000711 ssi_private->irq = irq_of_parse_and_map(np, 0);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400712 if (ssi_private->irq == NO_IRQ) {
713 dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
714 ret = -ENXIO;
715 goto error_iomap;
716 }
717
718 /* The 'name' should not have any slashes in it. */
719 ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0, ssi_private->name,
720 ssi_private);
721 if (ret < 0) {
722 dev_err(&pdev->dev, "could not claim irq %u\n", ssi_private->irq);
723 goto error_irqmap;
724 }
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000725
726 /* Are the RX and the TX clocks locked? */
Timur Tabi5e538ec2011-09-13 12:59:37 -0500727 if (!of_find_property(np, "fsl,ssi-asynchronous", NULL))
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000728 ssi_private->cpu_dai_drv.symmetric_rates = 1;
Timur Tabi17467f22008-01-11 18:15:26 +0100729
Timur Tabi8e9d8692010-08-06 12:16:12 -0500730 /* Determine the FIFO depth. */
731 iprop = of_get_property(np, "fsl,fifo-depth", NULL);
732 if (iprop)
Timur Tabi147dfe92011-06-08 15:02:55 -0500733 ssi_private->fifo_depth = be32_to_cpup(iprop);
Timur Tabi8e9d8692010-08-06 12:16:12 -0500734 else
735 /* Older 8610 DTs didn't have the fifo-depth property */
736 ssi_private->fifo_depth = 8;
737
Shawn Guo09ce1112012-03-16 16:56:43 +0800738 if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx21-ssi")) {
739 u32 dma_events[2];
740 ssi_private->ssi_on_imx = true;
Shawn Guo95cd98f2012-03-29 10:53:41 +0800741
742 ssi_private->clk = clk_get(&pdev->dev, NULL);
743 if (IS_ERR(ssi_private->clk)) {
744 ret = PTR_ERR(ssi_private->clk);
745 dev_err(&pdev->dev, "could not get clock: %d\n", ret);
746 goto error_irq;
747 }
748 clk_prepare_enable(ssi_private->clk);
749
Shawn Guo09ce1112012-03-16 16:56:43 +0800750 /*
751 * We have burstsize be "fifo_depth - 2" to match the SSI
752 * watermark setting in fsl_ssi_startup().
753 */
Lars-Peter Clausena8909c92013-04-03 11:06:04 +0200754 ssi_private->dma_params_tx.maxburst =
Shawn Guo09ce1112012-03-16 16:56:43 +0800755 ssi_private->fifo_depth - 2;
Lars-Peter Clausena8909c92013-04-03 11:06:04 +0200756 ssi_private->dma_params_rx.maxburst =
Shawn Guo09ce1112012-03-16 16:56:43 +0800757 ssi_private->fifo_depth - 2;
Lars-Peter Clausena8909c92013-04-03 11:06:04 +0200758 ssi_private->dma_params_tx.addr =
Shawn Guo09ce1112012-03-16 16:56:43 +0800759 ssi_private->ssi_phys + offsetof(struct ccsr_ssi, stx0);
Lars-Peter Clausena8909c92013-04-03 11:06:04 +0200760 ssi_private->dma_params_rx.addr =
Shawn Guo09ce1112012-03-16 16:56:43 +0800761 ssi_private->ssi_phys + offsetof(struct ccsr_ssi, srx0);
Lars-Peter Clausena8909c92013-04-03 11:06:04 +0200762 ssi_private->dma_params_tx.filter_data =
763 &ssi_private->filter_data_tx;
764 ssi_private->dma_params_rx.filter_data =
765 &ssi_private->filter_data_rx;
Shawn Guo09ce1112012-03-16 16:56:43 +0800766 /*
767 * TODO: This is a temporary solution and should be changed
768 * to use generic DMA binding later when the helplers get in.
769 */
770 ret = of_property_read_u32_array(pdev->dev.of_node,
771 "fsl,ssi-dma-events", dma_events, 2);
772 if (ret) {
773 dev_err(&pdev->dev, "could not get dma events\n");
Shawn Guo95cd98f2012-03-29 10:53:41 +0800774 goto error_clk;
Shawn Guo09ce1112012-03-16 16:56:43 +0800775 }
Shawn Guob46b3732012-03-28 15:34:56 +0800776
Lars-Peter Clausen312bb4f2013-03-22 14:12:12 +0100777 shared = of_device_is_compatible(of_get_parent(np),
778 "fsl,spba-bus");
779
Lars-Peter Clausena8909c92013-04-03 11:06:04 +0200780 imx_pcm_dma_params_init_data(&ssi_private->filter_data_tx,
Lars-Peter Clausen312bb4f2013-03-22 14:12:12 +0100781 dma_events[0], shared);
Lars-Peter Clausena8909c92013-04-03 11:06:04 +0200782 imx_pcm_dma_params_init_data(&ssi_private->filter_data_rx,
Lars-Peter Clausen312bb4f2013-03-22 14:12:12 +0100783 dma_events[1], shared);
Shawn Guo09ce1112012-03-16 16:56:43 +0800784 }
785
Timur Tabi17467f22008-01-11 18:15:26 +0100786 /* Initialize the the device_attribute structure */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000787 dev_attr = &ssi_private->dev_attr;
Timur Tabi0f768a72011-11-14 16:35:26 -0600788 sysfs_attr_init(&dev_attr->attr);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000789 dev_attr->attr.name = "statistics";
Timur Tabi17467f22008-01-11 18:15:26 +0100790 dev_attr->attr.mode = S_IRUGO;
791 dev_attr->show = fsl_sysfs_ssi_show;
792
Timur Tabi38fec722010-08-19 15:26:58 -0500793 ret = device_create_file(&pdev->dev, dev_attr);
Timur Tabi17467f22008-01-11 18:15:26 +0100794 if (ret) {
Timur Tabi38fec722010-08-19 15:26:58 -0500795 dev_err(&pdev->dev, "could not create sysfs %s file\n",
Timur Tabi17467f22008-01-11 18:15:26 +0100796 ssi_private->dev_attr.attr.name);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400797 goto error_irq;
Timur Tabi17467f22008-01-11 18:15:26 +0100798 }
799
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000800 /* Register with ASoC */
Timur Tabi38fec722010-08-19 15:26:58 -0500801 dev_set_drvdata(&pdev->dev, ssi_private);
Mark Brown3f4b7832008-12-03 19:26:35 +0000802
Kuninori Morimoto3580aa12013-03-21 03:32:04 -0700803 ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component,
804 &ssi_private->cpu_dai_drv, 1);
Timur Tabi87a06322010-08-03 17:55:28 -0500805 if (ret) {
Timur Tabi38fec722010-08-19 15:26:58 -0500806 dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400807 goto error_dev;
Mark Brown3f4b7832008-12-03 19:26:35 +0000808 }
Timur Tabi17467f22008-01-11 18:15:26 +0100809
Shawn Guo09ce1112012-03-16 16:56:43 +0800810 if (ssi_private->ssi_on_imx) {
Shawn Guobd41bc92013-04-25 11:18:46 +0800811 ret = imx_pcm_dma_init(pdev);
812 if (ret)
Shawn Guo09ce1112012-03-16 16:56:43 +0800813 goto error_dev;
Shawn Guo09ce1112012-03-16 16:56:43 +0800814 }
815
816 /*
817 * If codec-handle property is missing from SSI node, we assume
818 * that the machine driver uses new binding which does not require
819 * SSI driver to trigger machine driver's probe.
820 */
821 if (!of_get_property(np, "codec-handle", NULL)) {
822 ssi_private->new_binding = true;
823 goto done;
824 }
825
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000826 /* Trigger the machine driver's probe function. The platform driver
Shawn Guo2b81ec62012-03-09 00:59:46 +0800827 * name of the machine driver is taken from /compatible property of the
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000828 * device tree. We also pass the address of the CPU DAI driver
829 * structure.
830 */
Shawn Guo2b81ec62012-03-09 00:59:46 +0800831 sprop = of_get_property(of_find_node_by_path("/"), "compatible", NULL);
832 /* Sometimes the compatible name has a "fsl," prefix, so we strip it. */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000833 p = strrchr(sprop, ',');
834 if (p)
835 sprop = p + 1;
836 snprintf(name, sizeof(name), "snd-soc-%s", sprop);
837 make_lowercase(name);
838
839 ssi_private->pdev =
Timur Tabi38fec722010-08-19 15:26:58 -0500840 platform_device_register_data(&pdev->dev, name, 0, NULL, 0);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000841 if (IS_ERR(ssi_private->pdev)) {
842 ret = PTR_ERR(ssi_private->pdev);
Timur Tabi38fec722010-08-19 15:26:58 -0500843 dev_err(&pdev->dev, "failed to register platform: %d\n", ret);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400844 goto error_dai;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000845 }
846
Shawn Guo09ce1112012-03-16 16:56:43 +0800847done:
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000848 return 0;
Timur Tabi87a06322010-08-03 17:55:28 -0500849
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400850error_dai:
Shawn Guo09ce1112012-03-16 16:56:43 +0800851 if (ssi_private->ssi_on_imx)
Shawn Guobd41bc92013-04-25 11:18:46 +0800852 imx_pcm_dma_exit(pdev);
Kuninori Morimoto3580aa12013-03-21 03:32:04 -0700853 snd_soc_unregister_component(&pdev->dev);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400854
855error_dev:
Timur Tabi38fec722010-08-19 15:26:58 -0500856 dev_set_drvdata(&pdev->dev, NULL);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400857 device_remove_file(&pdev->dev, dev_attr);
858
Shawn Guo95cd98f2012-03-29 10:53:41 +0800859error_clk:
860 if (ssi_private->ssi_on_imx) {
861 clk_disable_unprepare(ssi_private->clk);
862 clk_put(ssi_private->clk);
863 }
864
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400865error_irq:
866 free_irq(ssi_private->irq, ssi_private);
867
868error_irqmap:
Timur Tabi87a06322010-08-03 17:55:28 -0500869 irq_dispose_mapping(ssi_private->irq);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400870
871error_iomap:
Timur Tabi87a06322010-08-03 17:55:28 -0500872 iounmap(ssi_private->ssi);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400873
874error_kmalloc:
Timur Tabi87a06322010-08-03 17:55:28 -0500875 kfree(ssi_private);
876
877 return ret;
Timur Tabi17467f22008-01-11 18:15:26 +0100878}
Timur Tabi17467f22008-01-11 18:15:26 +0100879
Timur Tabi38fec722010-08-19 15:26:58 -0500880static int fsl_ssi_remove(struct platform_device *pdev)
Timur Tabi17467f22008-01-11 18:15:26 +0100881{
Timur Tabi38fec722010-08-19 15:26:58 -0500882 struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev);
Timur Tabi17467f22008-01-11 18:15:26 +0100883
Shawn Guo09ce1112012-03-16 16:56:43 +0800884 if (!ssi_private->new_binding)
885 platform_device_unregister(ssi_private->pdev);
Shawn Guo95cd98f2012-03-29 10:53:41 +0800886 if (ssi_private->ssi_on_imx) {
Shawn Guobd41bc92013-04-25 11:18:46 +0800887 imx_pcm_dma_exit(pdev);
Shawn Guo95cd98f2012-03-29 10:53:41 +0800888 clk_disable_unprepare(ssi_private->clk);
889 clk_put(ssi_private->clk);
890 }
Kuninori Morimoto3580aa12013-03-21 03:32:04 -0700891 snd_soc_unregister_component(&pdev->dev);
Timur Tabi38fec722010-08-19 15:26:58 -0500892 device_remove_file(&pdev->dev, &ssi_private->dev_attr);
Mark Brown3f4b7832008-12-03 19:26:35 +0000893
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400894 free_irq(ssi_private->irq, ssi_private);
895 irq_dispose_mapping(ssi_private->irq);
896
Timur Tabi17467f22008-01-11 18:15:26 +0100897 kfree(ssi_private);
Timur Tabi38fec722010-08-19 15:26:58 -0500898 dev_set_drvdata(&pdev->dev, NULL);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000899
900 return 0;
Timur Tabi17467f22008-01-11 18:15:26 +0100901}
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000902
903static const struct of_device_id fsl_ssi_ids[] = {
904 { .compatible = "fsl,mpc8610-ssi", },
Shawn Guo09ce1112012-03-16 16:56:43 +0800905 { .compatible = "fsl,imx21-ssi", },
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000906 {}
907};
908MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
909
Grant Likelyf07eb222011-02-22 21:05:04 -0700910static struct platform_driver fsl_ssi_driver = {
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000911 .driver = {
912 .name = "fsl-ssi-dai",
913 .owner = THIS_MODULE,
914 .of_match_table = fsl_ssi_ids,
915 },
916 .probe = fsl_ssi_probe,
917 .remove = fsl_ssi_remove,
918};
Timur Tabi17467f22008-01-11 18:15:26 +0100919
Axel Linba0a7e02011-11-25 10:10:55 +0800920module_platform_driver(fsl_ssi_driver);
Timur Tabia454dad2009-03-05 17:23:37 -0600921
Timur Tabi17467f22008-01-11 18:15:26 +0100922MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
923MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver");
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000924MODULE_LICENSE("GPL v2");