blob: 4f76b5df0ce43bfb33adf6f700df3469b10b6a71 [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>
17#include <linux/device.h>
18#include <linux/delay.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090019#include <linux/slab.h>
Shawn Guodfa1a102012-03-16 16:56:42 +080020#include <linux/of_address.h>
21#include <linux/of_irq.h>
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +000022#include <linux/of_platform.h>
Timur Tabi17467f22008-01-11 18:15:26 +010023
Timur Tabi17467f22008-01-11 18:15:26 +010024#include <sound/core.h>
25#include <sound/pcm.h>
26#include <sound/pcm_params.h>
27#include <sound/initval.h>
28#include <sound/soc.h>
29
Timur Tabi17467f22008-01-11 18:15:26 +010030#include "fsl_ssi.h"
Shawn Guo09ce1112012-03-16 16:56:43 +080031#include "imx-pcm.h"
Timur Tabi17467f22008-01-11 18:15:26 +010032
Shawn Guodfa1a102012-03-16 16:56:42 +080033#ifdef PPC
34#define read_ssi(addr) in_be32(addr)
35#define write_ssi(val, addr) out_be32(addr, val)
36#define write_ssi_mask(addr, clear, set) clrsetbits_be32(addr, clear, set)
37#elif defined ARM
38#define read_ssi(addr) readl(addr)
39#define write_ssi(val, addr) writel(val, addr)
40/*
41 * FIXME: Proper locking should be added at write_ssi_mask caller level
42 * to ensure this register read/modify/write sequence is race free.
43 */
44static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set)
45{
46 u32 val = readl(addr);
47 val = (val & ~clear) | set;
48 writel(val, addr);
49}
50#endif
51
Timur Tabi17467f22008-01-11 18:15:26 +010052/**
53 * FSLSSI_I2S_RATES: sample rates supported by the I2S
54 *
55 * This driver currently only supports the SSI running in I2S slave mode,
56 * which means the codec determines the sample rate. Therefore, we tell
57 * ALSA that we support all rates and let the codec driver decide what rates
58 * are really supported.
59 */
60#define FSLSSI_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
61 SNDRV_PCM_RATE_CONTINUOUS)
62
63/**
64 * FSLSSI_I2S_FORMATS: audio formats supported by the SSI
65 *
66 * This driver currently only supports the SSI running in I2S slave mode.
67 *
68 * The SSI has a limitation in that the samples must be in the same byte
69 * order as the host CPU. This is because when multiple bytes are written
70 * to the STX register, the bytes and bits must be written in the same
71 * order. The STX is a shift register, so all the bits need to be aligned
72 * (bit-endianness must match byte-endianness). Processors typically write
73 * the bits within a byte in the same order that the bytes of a word are
74 * written in. So if the host CPU is big-endian, then only big-endian
75 * samples will be written to STX properly.
76 */
77#ifdef __BIG_ENDIAN
78#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
79 SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_S20_3BE | \
80 SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_S24_BE)
81#else
82#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
83 SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \
84 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
85#endif
86
Timur Tabid5a908b2009-03-26 11:42:38 -050087/* SIER bitflag of interrupts to enable */
88#define SIER_FLAGS (CCSR_SSI_SIER_TFRC_EN | CCSR_SSI_SIER_TDMAE | \
89 CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TUE0_EN | \
90 CCSR_SSI_SIER_TUE1_EN | CCSR_SSI_SIER_RFRC_EN | \
91 CCSR_SSI_SIER_RDMAE | CCSR_SSI_SIER_RIE | \
92 CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_ROE1_EN)
93
Timur Tabi17467f22008-01-11 18:15:26 +010094/**
95 * fsl_ssi_private: per-SSI private data
96 *
Timur Tabi17467f22008-01-11 18:15:26 +010097 * @ssi: pointer to the SSI's registers
98 * @ssi_phys: physical address of the SSI registers
99 * @irq: IRQ of this SSI
Timur Tabibe41e942008-07-28 17:04:39 -0500100 * @first_stream: pointer to the stream that was opened first
101 * @second_stream: pointer to second stream
Timur Tabi17467f22008-01-11 18:15:26 +0100102 * @playback: the number of playback streams opened
103 * @capture: the number of capture streams opened
104 * @cpu_dai: the CPU DAI for this device
105 * @dev_attr: the sysfs device attribute structure
106 * @stats: SSI statistics
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000107 * @name: name for this device
Timur Tabi17467f22008-01-11 18:15:26 +0100108 */
109struct fsl_ssi_private {
Timur Tabi17467f22008-01-11 18:15:26 +0100110 struct ccsr_ssi __iomem *ssi;
111 dma_addr_t ssi_phys;
112 unsigned int irq;
Timur Tabibe41e942008-07-28 17:04:39 -0500113 struct snd_pcm_substream *first_stream;
114 struct snd_pcm_substream *second_stream;
Timur Tabi8e9d8692010-08-06 12:16:12 -0500115 unsigned int fifo_depth;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000116 struct snd_soc_dai_driver cpu_dai_drv;
Timur Tabi17467f22008-01-11 18:15:26 +0100117 struct device_attribute dev_attr;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000118 struct platform_device *pdev;
Timur Tabi17467f22008-01-11 18:15:26 +0100119
Shawn Guo09ce1112012-03-16 16:56:43 +0800120 bool new_binding;
121 bool ssi_on_imx;
122 struct platform_device *imx_pcm_pdev;
123 struct imx_pcm_dma_params dma_params_tx;
124 struct imx_pcm_dma_params dma_params_rx;
125
Timur Tabi17467f22008-01-11 18:15:26 +0100126 struct {
127 unsigned int rfrc;
128 unsigned int tfrc;
129 unsigned int cmdau;
130 unsigned int cmddu;
131 unsigned int rxt;
132 unsigned int rdr1;
133 unsigned int rdr0;
134 unsigned int tde1;
135 unsigned int tde0;
136 unsigned int roe1;
137 unsigned int roe0;
138 unsigned int tue1;
139 unsigned int tue0;
140 unsigned int tfs;
141 unsigned int rfs;
142 unsigned int tls;
143 unsigned int rls;
144 unsigned int rff1;
145 unsigned int rff0;
146 unsigned int tfe1;
147 unsigned int tfe0;
148 } stats;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000149
150 char name[1];
Timur Tabi17467f22008-01-11 18:15:26 +0100151};
152
153/**
154 * fsl_ssi_isr: SSI interrupt handler
155 *
156 * Although it's possible to use the interrupt handler to send and receive
157 * data to/from the SSI, we use the DMA instead. Programming is more
158 * complicated, but the performance is much better.
159 *
160 * This interrupt handler is used only to gather statistics.
161 *
162 * @irq: IRQ of the SSI device
163 * @dev_id: pointer to the ssi_private structure for this SSI device
164 */
165static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
166{
167 struct fsl_ssi_private *ssi_private = dev_id;
168 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
169 irqreturn_t ret = IRQ_NONE;
170 __be32 sisr;
171 __be32 sisr2 = 0;
172
173 /* We got an interrupt, so read the status register to see what we
174 were interrupted for. We mask it with the Interrupt Enable register
175 so that we only check for events that we're interested in.
176 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800177 sisr = read_ssi(&ssi->sisr) & SIER_FLAGS;
Timur Tabi17467f22008-01-11 18:15:26 +0100178
179 if (sisr & CCSR_SSI_SISR_RFRC) {
180 ssi_private->stats.rfrc++;
181 sisr2 |= CCSR_SSI_SISR_RFRC;
182 ret = IRQ_HANDLED;
183 }
184
185 if (sisr & CCSR_SSI_SISR_TFRC) {
186 ssi_private->stats.tfrc++;
187 sisr2 |= CCSR_SSI_SISR_TFRC;
188 ret = IRQ_HANDLED;
189 }
190
191 if (sisr & CCSR_SSI_SISR_CMDAU) {
192 ssi_private->stats.cmdau++;
193 ret = IRQ_HANDLED;
194 }
195
196 if (sisr & CCSR_SSI_SISR_CMDDU) {
197 ssi_private->stats.cmddu++;
198 ret = IRQ_HANDLED;
199 }
200
201 if (sisr & CCSR_SSI_SISR_RXT) {
202 ssi_private->stats.rxt++;
203 ret = IRQ_HANDLED;
204 }
205
206 if (sisr & CCSR_SSI_SISR_RDR1) {
207 ssi_private->stats.rdr1++;
208 ret = IRQ_HANDLED;
209 }
210
211 if (sisr & CCSR_SSI_SISR_RDR0) {
212 ssi_private->stats.rdr0++;
213 ret = IRQ_HANDLED;
214 }
215
216 if (sisr & CCSR_SSI_SISR_TDE1) {
217 ssi_private->stats.tde1++;
218 ret = IRQ_HANDLED;
219 }
220
221 if (sisr & CCSR_SSI_SISR_TDE0) {
222 ssi_private->stats.tde0++;
223 ret = IRQ_HANDLED;
224 }
225
226 if (sisr & CCSR_SSI_SISR_ROE1) {
227 ssi_private->stats.roe1++;
228 sisr2 |= CCSR_SSI_SISR_ROE1;
229 ret = IRQ_HANDLED;
230 }
231
232 if (sisr & CCSR_SSI_SISR_ROE0) {
233 ssi_private->stats.roe0++;
234 sisr2 |= CCSR_SSI_SISR_ROE0;
235 ret = IRQ_HANDLED;
236 }
237
238 if (sisr & CCSR_SSI_SISR_TUE1) {
239 ssi_private->stats.tue1++;
240 sisr2 |= CCSR_SSI_SISR_TUE1;
241 ret = IRQ_HANDLED;
242 }
243
244 if (sisr & CCSR_SSI_SISR_TUE0) {
245 ssi_private->stats.tue0++;
246 sisr2 |= CCSR_SSI_SISR_TUE0;
247 ret = IRQ_HANDLED;
248 }
249
250 if (sisr & CCSR_SSI_SISR_TFS) {
251 ssi_private->stats.tfs++;
252 ret = IRQ_HANDLED;
253 }
254
255 if (sisr & CCSR_SSI_SISR_RFS) {
256 ssi_private->stats.rfs++;
257 ret = IRQ_HANDLED;
258 }
259
260 if (sisr & CCSR_SSI_SISR_TLS) {
261 ssi_private->stats.tls++;
262 ret = IRQ_HANDLED;
263 }
264
265 if (sisr & CCSR_SSI_SISR_RLS) {
266 ssi_private->stats.rls++;
267 ret = IRQ_HANDLED;
268 }
269
270 if (sisr & CCSR_SSI_SISR_RFF1) {
271 ssi_private->stats.rff1++;
272 ret = IRQ_HANDLED;
273 }
274
275 if (sisr & CCSR_SSI_SISR_RFF0) {
276 ssi_private->stats.rff0++;
277 ret = IRQ_HANDLED;
278 }
279
280 if (sisr & CCSR_SSI_SISR_TFE1) {
281 ssi_private->stats.tfe1++;
282 ret = IRQ_HANDLED;
283 }
284
285 if (sisr & CCSR_SSI_SISR_TFE0) {
286 ssi_private->stats.tfe0++;
287 ret = IRQ_HANDLED;
288 }
289
290 /* Clear the bits that we set */
291 if (sisr2)
Shawn Guodfa1a102012-03-16 16:56:42 +0800292 write_ssi(sisr2, &ssi->sisr);
Timur Tabi17467f22008-01-11 18:15:26 +0100293
294 return ret;
295}
296
297/**
298 * fsl_ssi_startup: create a new substream
299 *
300 * This is the first function called when a stream is opened.
301 *
302 * If this is the first stream open, then grab the IRQ and program most of
303 * the SSI registers.
304 */
Mark Browndee89c42008-11-18 22:11:38 +0000305static int fsl_ssi_startup(struct snd_pcm_substream *substream,
306 struct snd_soc_dai *dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100307{
308 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Timur Tabi5e538ec2011-09-13 12:59:37 -0500309 struct fsl_ssi_private *ssi_private =
310 snd_soc_dai_get_drvdata(rtd->cpu_dai);
311 int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
Timur Tabi17467f22008-01-11 18:15:26 +0100312
313 /*
314 * If this is the first stream opened, then request the IRQ
315 * and initialize the SSI registers.
316 */
Timur Tabi5e538ec2011-09-13 12:59:37 -0500317 if (!ssi_private->first_stream) {
Timur Tabi17467f22008-01-11 18:15:26 +0100318 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
Timur Tabi17467f22008-01-11 18:15:26 +0100319
Timur Tabi5e538ec2011-09-13 12:59:37 -0500320 ssi_private->first_stream = substream;
321
Timur Tabi17467f22008-01-11 18:15:26 +0100322 /*
323 * Section 16.5 of the MPC8610 reference manual says that the
324 * SSI needs to be disabled before updating the registers we set
325 * here.
326 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800327 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
Timur Tabi17467f22008-01-11 18:15:26 +0100328
329 /*
330 * Program the SSI into I2S Slave Non-Network Synchronous mode.
331 * Also enable the transmit and receive FIFO.
332 *
333 * FIXME: Little-endian samples require a different shift dir
334 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800335 write_ssi_mask(&ssi->scr,
Timur Tabia454dad2009-03-05 17:23:37 -0600336 CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
337 CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE
Timur Tabi5e538ec2011-09-13 12:59:37 -0500338 | (synchronous ? CCSR_SSI_SCR_SYN : 0));
Timur Tabi17467f22008-01-11 18:15:26 +0100339
Shawn Guodfa1a102012-03-16 16:56:42 +0800340 write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
Timur Tabi17467f22008-01-11 18:15:26 +0100341 CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
Shawn Guodfa1a102012-03-16 16:56:42 +0800342 CCSR_SSI_STCR_TSCKP, &ssi->stcr);
Timur Tabi17467f22008-01-11 18:15:26 +0100343
Shawn Guodfa1a102012-03-16 16:56:42 +0800344 write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
Timur Tabi17467f22008-01-11 18:15:26 +0100345 CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
Shawn Guodfa1a102012-03-16 16:56:42 +0800346 CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
Timur Tabi17467f22008-01-11 18:15:26 +0100347
348 /*
349 * The DC and PM bits are only used if the SSI is the clock
350 * master.
351 */
352
Timur Tabi5e538ec2011-09-13 12:59:37 -0500353 /* Enable the interrupts and DMA requests */
Shawn Guodfa1a102012-03-16 16:56:42 +0800354 write_ssi(SIER_FLAGS, &ssi->sier);
Timur Tabi17467f22008-01-11 18:15:26 +0100355
356 /*
357 * Set the watermark for transmit FIFI 0 and receive FIFO 0. We
Timur Tabi8e9d8692010-08-06 12:16:12 -0500358 * don't use FIFO 1. We program the transmit water to signal a
359 * DMA transfer if there are only two (or fewer) elements left
360 * in the FIFO. Two elements equals one frame (left channel,
361 * right channel). This value, however, depends on the depth of
362 * the transmit buffer.
363 *
364 * We program the receive FIFO to notify us if at least two
365 * elements (one frame) have been written to the FIFO. We could
366 * make this value larger (and maybe we should), but this way
367 * data will be written to memory as soon as it's available.
Timur Tabi17467f22008-01-11 18:15:26 +0100368 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800369 write_ssi(CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) |
370 CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2),
371 &ssi->sfcsr);
Timur Tabi17467f22008-01-11 18:15:26 +0100372
373 /*
374 * We keep the SSI disabled because if we enable it, then the
375 * DMA controller will start. It's not supposed to start until
376 * the SCR.TE (or SCR.RE) bit is set, but it does anyway. The
377 * DMA controller will transfer one "BWC" of data (i.e. the
378 * amount of data that the MR.BWC bits are set to). The reason
379 * this is bad is because at this point, the PCM driver has not
380 * finished initializing the DMA controller.
381 */
Timur Tabi5e538ec2011-09-13 12:59:37 -0500382 } else {
383 if (synchronous) {
384 struct snd_pcm_runtime *first_runtime =
385 ssi_private->first_stream->runtime;
386 /*
387 * This is the second stream open, and we're in
388 * synchronous mode, so we need to impose sample
389 * sample size constraints. This is because STCCR is
390 * used for playback and capture in synchronous mode,
391 * so there's no way to specify different word
392 * lengths.
393 *
394 * Note that this can cause a race condition if the
395 * second stream is opened before the first stream is
396 * fully initialized. We provide some protection by
397 * checking to make sure the first stream is
398 * initialized, but it's not perfect. ALSA sometimes
399 * re-initializes the driver with a different sample
400 * rate or size. If the second stream is opened
401 * before the first stream has received its final
402 * parameters, then the second stream may be
403 * constrained to the wrong sample rate or size.
404 */
405 if (!first_runtime->sample_bits) {
406 dev_err(substream->pcm->card->dev,
407 "set sample size in %s stream first\n",
408 substream->stream ==
409 SNDRV_PCM_STREAM_PLAYBACK
410 ? "capture" : "playback");
411 return -EAGAIN;
412 }
Timur Tabi17467f22008-01-11 18:15:26 +0100413
Timur Tabia454dad2009-03-05 17:23:37 -0600414 snd_pcm_hw_constraint_minmax(substream->runtime,
415 SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
416 first_runtime->sample_bits,
417 first_runtime->sample_bits);
Timur Tabi5e538ec2011-09-13 12:59:37 -0500418 }
Timur Tabibe41e942008-07-28 17:04:39 -0500419
420 ssi_private->second_stream = substream;
421 }
422
Shawn Guo09ce1112012-03-16 16:56:43 +0800423 if (ssi_private->ssi_on_imx)
424 snd_soc_dai_set_dma_data(dai, substream,
425 (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
426 &ssi_private->dma_params_tx :
427 &ssi_private->dma_params_rx);
428
Timur Tabi17467f22008-01-11 18:15:26 +0100429 return 0;
430}
431
432/**
Timur Tabi85ef2372009-02-05 17:56:02 -0600433 * fsl_ssi_hw_params - program the sample size
Timur Tabi17467f22008-01-11 18:15:26 +0100434 *
435 * Most of the SSI registers have been programmed in the startup function,
436 * but the word length must be programmed here. Unfortunately, programming
437 * the SxCCR.WL bits requires the SSI to be temporarily disabled. This can
438 * cause a problem with supporting simultaneous playback and capture. If
439 * the SSI is already playing a stream, then that stream may be temporarily
440 * stopped when you start capture.
441 *
442 * Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the
443 * clock master.
444 */
Timur Tabi85ef2372009-02-05 17:56:02 -0600445static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
446 struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100447{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000448 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
Timur Tabi5e538ec2011-09-13 12:59:37 -0500449 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
450 unsigned int sample_size =
451 snd_pcm_format_width(params_format(hw_params));
452 u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
Shawn Guodfa1a102012-03-16 16:56:42 +0800453 int enabled = read_ssi(&ssi->scr) & CCSR_SSI_SCR_SSIEN;
Timur Tabi17467f22008-01-11 18:15:26 +0100454
Timur Tabi5e538ec2011-09-13 12:59:37 -0500455 /*
456 * If we're in synchronous mode, and the SSI is already enabled,
457 * then STCCR is already set properly.
458 */
459 if (enabled && ssi_private->cpu_dai_drv.symmetric_rates)
460 return 0;
Timur Tabi17467f22008-01-11 18:15:26 +0100461
Timur Tabi5e538ec2011-09-13 12:59:37 -0500462 /*
463 * FIXME: The documentation says that SxCCR[WL] should not be
464 * modified while the SSI is enabled. The only time this can
465 * happen is if we're trying to do simultaneous playback and
466 * capture in asynchronous mode. Unfortunately, I have been enable
467 * to get that to work at all on the P1022DS. Therefore, we don't
468 * bother to disable/enable the SSI when setting SxCCR[WL], because
469 * the SSI will stop anyway. Maybe one day, this will get fixed.
470 */
Timur Tabi17467f22008-01-11 18:15:26 +0100471
Timur Tabi5e538ec2011-09-13 12:59:37 -0500472 /* In synchronous mode, the SSI uses STCCR for capture */
473 if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
474 ssi_private->cpu_dai_drv.symmetric_rates)
Shawn Guodfa1a102012-03-16 16:56:42 +0800475 write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
Timur Tabi5e538ec2011-09-13 12:59:37 -0500476 else
Shawn Guodfa1a102012-03-16 16:56:42 +0800477 write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
Timur Tabi17467f22008-01-11 18:15:26 +0100478
479 return 0;
480}
481
482/**
483 * fsl_ssi_trigger: start and stop the DMA transfer.
484 *
485 * This function is called by ALSA to start, stop, pause, and resume the DMA
486 * transfer of data.
487 *
488 * The DMA channel is in external master start and pause mode, which
489 * means the SSI completely controls the flow of data.
490 */
Mark Browndee89c42008-11-18 22:11:38 +0000491static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
492 struct snd_soc_dai *dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100493{
494 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000495 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
Timur Tabi17467f22008-01-11 18:15:26 +0100496 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
497
498 switch (cmd) {
499 case SNDRV_PCM_TRIGGER_START:
Timur Tabi17467f22008-01-11 18:15:26 +0100500 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
Timur Tabia4d11fe2009-03-25 18:20:37 -0500501 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
Shawn Guodfa1a102012-03-16 16:56:42 +0800502 write_ssi_mask(&ssi->scr, 0,
Timur Tabibe41e942008-07-28 17:04:39 -0500503 CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE);
Timur Tabia4d11fe2009-03-25 18:20:37 -0500504 else
Shawn Guodfa1a102012-03-16 16:56:42 +0800505 write_ssi_mask(&ssi->scr, 0,
Timur Tabibe41e942008-07-28 17:04:39 -0500506 CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE);
Timur Tabi17467f22008-01-11 18:15:26 +0100507 break;
508
509 case SNDRV_PCM_TRIGGER_STOP:
Timur Tabi17467f22008-01-11 18:15:26 +0100510 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
511 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
Shawn Guodfa1a102012-03-16 16:56:42 +0800512 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TE, 0);
Timur Tabi17467f22008-01-11 18:15:26 +0100513 else
Shawn Guodfa1a102012-03-16 16:56:42 +0800514 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0);
Timur Tabi17467f22008-01-11 18:15:26 +0100515 break;
516
517 default:
518 return -EINVAL;
519 }
520
521 return 0;
522}
523
524/**
525 * fsl_ssi_shutdown: shutdown the SSI
526 *
527 * Shutdown the SSI if there are no other substreams open.
528 */
Mark Browndee89c42008-11-18 22:11:38 +0000529static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
530 struct snd_soc_dai *dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100531{
532 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000533 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
Timur Tabi17467f22008-01-11 18:15:26 +0100534
Timur Tabibe41e942008-07-28 17:04:39 -0500535 if (ssi_private->first_stream == substream)
536 ssi_private->first_stream = ssi_private->second_stream;
537
538 ssi_private->second_stream = NULL;
539
Timur Tabi17467f22008-01-11 18:15:26 +0100540 /*
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400541 * If this is the last active substream, disable the SSI.
Timur Tabi17467f22008-01-11 18:15:26 +0100542 */
Timur Tabi5e538ec2011-09-13 12:59:37 -0500543 if (!ssi_private->first_stream) {
Timur Tabi17467f22008-01-11 18:15:26 +0100544 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
545
Shawn Guodfa1a102012-03-16 16:56:42 +0800546 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
Timur Tabi17467f22008-01-11 18:15:26 +0100547 }
548}
549
Lars-Peter Clausen85e76522011-11-23 11:40:40 +0100550static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
Eric Miao6335d052009-03-03 09:41:00 +0800551 .startup = fsl_ssi_startup,
552 .hw_params = fsl_ssi_hw_params,
553 .shutdown = fsl_ssi_shutdown,
554 .trigger = fsl_ssi_trigger,
Eric Miao6335d052009-03-03 09:41:00 +0800555};
556
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000557/* Template for the CPU dai driver structure */
558static struct snd_soc_dai_driver fsl_ssi_dai_template = {
Timur Tabi17467f22008-01-11 18:15:26 +0100559 .playback = {
560 /* The SSI does not support monaural audio. */
561 .channels_min = 2,
562 .channels_max = 2,
563 .rates = FSLSSI_I2S_RATES,
564 .formats = FSLSSI_I2S_FORMATS,
565 },
566 .capture = {
567 .channels_min = 2,
568 .channels_max = 2,
569 .rates = FSLSSI_I2S_RATES,
570 .formats = FSLSSI_I2S_FORMATS,
571 },
Eric Miao6335d052009-03-03 09:41:00 +0800572 .ops = &fsl_ssi_dai_ops,
Timur Tabi17467f22008-01-11 18:15:26 +0100573};
574
Timur Tabid5a908b2009-03-26 11:42:38 -0500575/* Show the statistics of a flag only if its interrupt is enabled. The
576 * compiler will optimze this code to a no-op if the interrupt is not
577 * enabled.
578 */
579#define SIER_SHOW(flag, name) \
580 do { \
581 if (SIER_FLAGS & CCSR_SSI_SIER_##flag) \
582 length += sprintf(buf + length, #name "=%u\n", \
583 ssi_private->stats.name); \
584 } while (0)
585
586
Timur Tabi17467f22008-01-11 18:15:26 +0100587/**
588 * fsl_sysfs_ssi_show: display SSI statistics
589 *
Timur Tabid5a908b2009-03-26 11:42:38 -0500590 * Display the statistics for the current SSI device. To avoid confusion,
591 * we only show those counts that are enabled.
Timur Tabi17467f22008-01-11 18:15:26 +0100592 */
593static ssize_t fsl_sysfs_ssi_show(struct device *dev,
594 struct device_attribute *attr, char *buf)
595{
596 struct fsl_ssi_private *ssi_private =
Timur Tabid5a908b2009-03-26 11:42:38 -0500597 container_of(attr, struct fsl_ssi_private, dev_attr);
598 ssize_t length = 0;
Timur Tabi17467f22008-01-11 18:15:26 +0100599
Timur Tabid5a908b2009-03-26 11:42:38 -0500600 SIER_SHOW(RFRC_EN, rfrc);
601 SIER_SHOW(TFRC_EN, tfrc);
602 SIER_SHOW(CMDAU_EN, cmdau);
603 SIER_SHOW(CMDDU_EN, cmddu);
604 SIER_SHOW(RXT_EN, rxt);
605 SIER_SHOW(RDR1_EN, rdr1);
606 SIER_SHOW(RDR0_EN, rdr0);
607 SIER_SHOW(TDE1_EN, tde1);
608 SIER_SHOW(TDE0_EN, tde0);
609 SIER_SHOW(ROE1_EN, roe1);
610 SIER_SHOW(ROE0_EN, roe0);
611 SIER_SHOW(TUE1_EN, tue1);
612 SIER_SHOW(TUE0_EN, tue0);
613 SIER_SHOW(TFS_EN, tfs);
614 SIER_SHOW(RFS_EN, rfs);
615 SIER_SHOW(TLS_EN, tls);
616 SIER_SHOW(RLS_EN, rls);
617 SIER_SHOW(RFF1_EN, rff1);
618 SIER_SHOW(RFF0_EN, rff0);
619 SIER_SHOW(TFE1_EN, tfe1);
620 SIER_SHOW(TFE0_EN, tfe0);
Timur Tabi17467f22008-01-11 18:15:26 +0100621
622 return length;
623}
624
625/**
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000626 * Make every character in a string lower-case
Timur Tabi17467f22008-01-11 18:15:26 +0100627 */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000628static void make_lowercase(char *s)
Timur Tabi17467f22008-01-11 18:15:26 +0100629{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000630 char *p = s;
631 char c;
632
633 while ((c = *p)) {
634 if ((c >= 'A') && (c <= 'Z'))
635 *p = c + ('a' - 'A');
636 p++;
637 }
638}
639
Grant Likelyf07eb222011-02-22 21:05:04 -0700640static int __devinit fsl_ssi_probe(struct platform_device *pdev)
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000641{
Timur Tabi17467f22008-01-11 18:15:26 +0100642 struct fsl_ssi_private *ssi_private;
643 int ret = 0;
Timur Tabi87a06322010-08-03 17:55:28 -0500644 struct device_attribute *dev_attr = NULL;
Timur Tabi38fec722010-08-19 15:26:58 -0500645 struct device_node *np = pdev->dev.of_node;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000646 const char *p, *sprop;
Timur Tabi8e9d8692010-08-06 12:16:12 -0500647 const uint32_t *iprop;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000648 struct resource res;
649 char name[64];
Timur Tabi17467f22008-01-11 18:15:26 +0100650
Timur Tabiff713342010-08-04 17:51:08 -0500651 /* SSIs that are not connected on the board should have a
652 * status = "disabled"
653 * property in their device tree nodes.
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000654 */
Timur Tabiff713342010-08-04 17:51:08 -0500655 if (!of_device_is_available(np))
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000656 return -ENODEV;
657
658 /* We only support the SSI in "I2S Slave" mode */
659 sprop = of_get_property(np, "fsl,mode", NULL);
660 if (!sprop || strcmp(sprop, "i2s-slave")) {
Timur Tabi38fec722010-08-19 15:26:58 -0500661 dev_notice(&pdev->dev, "mode %s is unsupported\n", sprop);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000662 return -ENODEV;
Timur Tabi17467f22008-01-11 18:15:26 +0100663 }
Timur Tabi17467f22008-01-11 18:15:26 +0100664
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000665 /* The DAI name is the last part of the full name of the node. */
666 p = strrchr(np->full_name, '/') + 1;
667 ssi_private = kzalloc(sizeof(struct fsl_ssi_private) + strlen(p),
668 GFP_KERNEL);
669 if (!ssi_private) {
Timur Tabi38fec722010-08-19 15:26:58 -0500670 dev_err(&pdev->dev, "could not allocate DAI object\n");
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000671 return -ENOMEM;
672 }
Timur Tabi17467f22008-01-11 18:15:26 +0100673
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000674 strcpy(ssi_private->name, p);
Timur Tabi17467f22008-01-11 18:15:26 +0100675
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000676 /* Initialize this copy of the CPU DAI driver structure */
677 memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
678 sizeof(fsl_ssi_dai_template));
679 ssi_private->cpu_dai_drv.name = ssi_private->name;
680
681 /* Get the addresses and IRQ */
682 ret = of_address_to_resource(np, 0, &res);
683 if (ret) {
Timur Tabi38fec722010-08-19 15:26:58 -0500684 dev_err(&pdev->dev, "could not determine device resources\n");
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400685 goto error_kmalloc;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000686 }
Timur Tabi147dfe92011-06-08 15:02:55 -0500687 ssi_private->ssi = of_iomap(np, 0);
688 if (!ssi_private->ssi) {
689 dev_err(&pdev->dev, "could not map device resources\n");
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400690 ret = -ENOMEM;
691 goto error_kmalloc;
Timur Tabi147dfe92011-06-08 15:02:55 -0500692 }
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000693 ssi_private->ssi_phys = res.start;
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400694
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000695 ssi_private->irq = irq_of_parse_and_map(np, 0);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400696 if (ssi_private->irq == NO_IRQ) {
697 dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
698 ret = -ENXIO;
699 goto error_iomap;
700 }
701
702 /* The 'name' should not have any slashes in it. */
703 ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0, ssi_private->name,
704 ssi_private);
705 if (ret < 0) {
706 dev_err(&pdev->dev, "could not claim irq %u\n", ssi_private->irq);
707 goto error_irqmap;
708 }
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000709
710 /* Are the RX and the TX clocks locked? */
Timur Tabi5e538ec2011-09-13 12:59:37 -0500711 if (!of_find_property(np, "fsl,ssi-asynchronous", NULL))
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000712 ssi_private->cpu_dai_drv.symmetric_rates = 1;
Timur Tabi17467f22008-01-11 18:15:26 +0100713
Timur Tabi8e9d8692010-08-06 12:16:12 -0500714 /* Determine the FIFO depth. */
715 iprop = of_get_property(np, "fsl,fifo-depth", NULL);
716 if (iprop)
Timur Tabi147dfe92011-06-08 15:02:55 -0500717 ssi_private->fifo_depth = be32_to_cpup(iprop);
Timur Tabi8e9d8692010-08-06 12:16:12 -0500718 else
719 /* Older 8610 DTs didn't have the fifo-depth property */
720 ssi_private->fifo_depth = 8;
721
Shawn Guo09ce1112012-03-16 16:56:43 +0800722 if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx21-ssi")) {
723 u32 dma_events[2];
724 ssi_private->ssi_on_imx = true;
725 /*
726 * We have burstsize be "fifo_depth - 2" to match the SSI
727 * watermark setting in fsl_ssi_startup().
728 */
729 ssi_private->dma_params_tx.burstsize =
730 ssi_private->fifo_depth - 2;
731 ssi_private->dma_params_rx.burstsize =
732 ssi_private->fifo_depth - 2;
733 ssi_private->dma_params_tx.dma_addr =
734 ssi_private->ssi_phys + offsetof(struct ccsr_ssi, stx0);
735 ssi_private->dma_params_rx.dma_addr =
736 ssi_private->ssi_phys + offsetof(struct ccsr_ssi, srx0);
737 /*
738 * TODO: This is a temporary solution and should be changed
739 * to use generic DMA binding later when the helplers get in.
740 */
741 ret = of_property_read_u32_array(pdev->dev.of_node,
742 "fsl,ssi-dma-events", dma_events, 2);
743 if (ret) {
744 dev_err(&pdev->dev, "could not get dma events\n");
745 goto error_irq;
746 }
747 ssi_private->dma_params_tx.dma = dma_events[0];
748 ssi_private->dma_params_rx.dma = dma_events[1];
Shawn Guob46b3732012-03-28 15:34:56 +0800749
750 ssi_private->dma_params_tx.shared_peripheral =
751 of_device_is_compatible(of_get_parent(np),
752 "fsl,spba-bus");
753 ssi_private->dma_params_rx.shared_peripheral =
754 ssi_private->dma_params_tx.shared_peripheral;
Shawn Guo09ce1112012-03-16 16:56:43 +0800755 }
756
Timur Tabi17467f22008-01-11 18:15:26 +0100757 /* Initialize the the device_attribute structure */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000758 dev_attr = &ssi_private->dev_attr;
Timur Tabi0f768a72011-11-14 16:35:26 -0600759 sysfs_attr_init(&dev_attr->attr);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000760 dev_attr->attr.name = "statistics";
Timur Tabi17467f22008-01-11 18:15:26 +0100761 dev_attr->attr.mode = S_IRUGO;
762 dev_attr->show = fsl_sysfs_ssi_show;
763
Timur Tabi38fec722010-08-19 15:26:58 -0500764 ret = device_create_file(&pdev->dev, dev_attr);
Timur Tabi17467f22008-01-11 18:15:26 +0100765 if (ret) {
Timur Tabi38fec722010-08-19 15:26:58 -0500766 dev_err(&pdev->dev, "could not create sysfs %s file\n",
Timur Tabi17467f22008-01-11 18:15:26 +0100767 ssi_private->dev_attr.attr.name);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400768 goto error_irq;
Timur Tabi17467f22008-01-11 18:15:26 +0100769 }
770
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000771 /* Register with ASoC */
Timur Tabi38fec722010-08-19 15:26:58 -0500772 dev_set_drvdata(&pdev->dev, ssi_private);
Mark Brown3f4b7832008-12-03 19:26:35 +0000773
Timur Tabi38fec722010-08-19 15:26:58 -0500774 ret = snd_soc_register_dai(&pdev->dev, &ssi_private->cpu_dai_drv);
Timur Tabi87a06322010-08-03 17:55:28 -0500775 if (ret) {
Timur Tabi38fec722010-08-19 15:26:58 -0500776 dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400777 goto error_dev;
Mark Brown3f4b7832008-12-03 19:26:35 +0000778 }
Timur Tabi17467f22008-01-11 18:15:26 +0100779
Shawn Guo09ce1112012-03-16 16:56:43 +0800780 if (ssi_private->ssi_on_imx) {
781 ssi_private->imx_pcm_pdev =
782 platform_device_register_simple("imx-pcm-audio",
783 -1, NULL, 0);
784 if (IS_ERR(ssi_private->imx_pcm_pdev)) {
785 ret = PTR_ERR(ssi_private->imx_pcm_pdev);
786 goto error_dev;
787 }
788 }
789
790 /*
791 * If codec-handle property is missing from SSI node, we assume
792 * that the machine driver uses new binding which does not require
793 * SSI driver to trigger machine driver's probe.
794 */
795 if (!of_get_property(np, "codec-handle", NULL)) {
796 ssi_private->new_binding = true;
797 goto done;
798 }
799
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000800 /* Trigger the machine driver's probe function. The platform driver
Shawn Guo2b81ec62012-03-09 00:59:46 +0800801 * name of the machine driver is taken from /compatible property of the
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000802 * device tree. We also pass the address of the CPU DAI driver
803 * structure.
804 */
Shawn Guo2b81ec62012-03-09 00:59:46 +0800805 sprop = of_get_property(of_find_node_by_path("/"), "compatible", NULL);
806 /* Sometimes the compatible name has a "fsl," prefix, so we strip it. */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000807 p = strrchr(sprop, ',');
808 if (p)
809 sprop = p + 1;
810 snprintf(name, sizeof(name), "snd-soc-%s", sprop);
811 make_lowercase(name);
812
813 ssi_private->pdev =
Timur Tabi38fec722010-08-19 15:26:58 -0500814 platform_device_register_data(&pdev->dev, name, 0, NULL, 0);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000815 if (IS_ERR(ssi_private->pdev)) {
816 ret = PTR_ERR(ssi_private->pdev);
Timur Tabi38fec722010-08-19 15:26:58 -0500817 dev_err(&pdev->dev, "failed to register platform: %d\n", ret);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400818 goto error_dai;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000819 }
820
Shawn Guo09ce1112012-03-16 16:56:43 +0800821done:
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000822 return 0;
Timur Tabi87a06322010-08-03 17:55:28 -0500823
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400824error_dai:
Shawn Guo09ce1112012-03-16 16:56:43 +0800825 if (ssi_private->ssi_on_imx)
826 platform_device_unregister(ssi_private->imx_pcm_pdev);
Timur Tabi38fec722010-08-19 15:26:58 -0500827 snd_soc_unregister_dai(&pdev->dev);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400828
829error_dev:
Timur Tabi38fec722010-08-19 15:26:58 -0500830 dev_set_drvdata(&pdev->dev, NULL);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400831 device_remove_file(&pdev->dev, dev_attr);
832
833error_irq:
834 free_irq(ssi_private->irq, ssi_private);
835
836error_irqmap:
Timur Tabi87a06322010-08-03 17:55:28 -0500837 irq_dispose_mapping(ssi_private->irq);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400838
839error_iomap:
Timur Tabi87a06322010-08-03 17:55:28 -0500840 iounmap(ssi_private->ssi);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400841
842error_kmalloc:
Timur Tabi87a06322010-08-03 17:55:28 -0500843 kfree(ssi_private);
844
845 return ret;
Timur Tabi17467f22008-01-11 18:15:26 +0100846}
Timur Tabi17467f22008-01-11 18:15:26 +0100847
Timur Tabi38fec722010-08-19 15:26:58 -0500848static int fsl_ssi_remove(struct platform_device *pdev)
Timur Tabi17467f22008-01-11 18:15:26 +0100849{
Timur Tabi38fec722010-08-19 15:26:58 -0500850 struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev);
Timur Tabi17467f22008-01-11 18:15:26 +0100851
Shawn Guo09ce1112012-03-16 16:56:43 +0800852 if (!ssi_private->new_binding)
853 platform_device_unregister(ssi_private->pdev);
854 if (ssi_private->ssi_on_imx)
855 platform_device_unregister(ssi_private->imx_pcm_pdev);
Timur Tabi38fec722010-08-19 15:26:58 -0500856 snd_soc_unregister_dai(&pdev->dev);
857 device_remove_file(&pdev->dev, &ssi_private->dev_attr);
Mark Brown3f4b7832008-12-03 19:26:35 +0000858
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400859 free_irq(ssi_private->irq, ssi_private);
860 irq_dispose_mapping(ssi_private->irq);
861
Timur Tabi17467f22008-01-11 18:15:26 +0100862 kfree(ssi_private);
Timur Tabi38fec722010-08-19 15:26:58 -0500863 dev_set_drvdata(&pdev->dev, NULL);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000864
865 return 0;
Timur Tabi17467f22008-01-11 18:15:26 +0100866}
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000867
868static const struct of_device_id fsl_ssi_ids[] = {
869 { .compatible = "fsl,mpc8610-ssi", },
Shawn Guo09ce1112012-03-16 16:56:43 +0800870 { .compatible = "fsl,imx21-ssi", },
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000871 {}
872};
873MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
874
Grant Likelyf07eb222011-02-22 21:05:04 -0700875static struct platform_driver fsl_ssi_driver = {
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000876 .driver = {
877 .name = "fsl-ssi-dai",
878 .owner = THIS_MODULE,
879 .of_match_table = fsl_ssi_ids,
880 },
881 .probe = fsl_ssi_probe,
882 .remove = fsl_ssi_remove,
883};
Timur Tabi17467f22008-01-11 18:15:26 +0100884
Axel Linba0a7e02011-11-25 10:10:55 +0800885module_platform_driver(fsl_ssi_driver);
Timur Tabia454dad2009-03-05 17:23:37 -0600886
Timur Tabi17467f22008-01-11 18:15:26 +0100887MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
888MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver");
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000889MODULE_LICENSE("GPL v2");