blob: 4ed2afd47782cba1667cf109d3c63c6d501ba2a1 [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>
30
Timur Tabi17467f22008-01-11 18:15:26 +010031#include "fsl_ssi.h"
Shawn Guo09ce1112012-03-16 16:56:43 +080032#include "imx-pcm.h"
Timur Tabi17467f22008-01-11 18:15:26 +010033
Shawn Guodfa1a102012-03-16 16:56:42 +080034#ifdef PPC
35#define read_ssi(addr) in_be32(addr)
36#define write_ssi(val, addr) out_be32(addr, val)
37#define write_ssi_mask(addr, clear, set) clrsetbits_be32(addr, clear, set)
38#elif defined ARM
39#define read_ssi(addr) readl(addr)
40#define write_ssi(val, addr) writel(val, addr)
41/*
42 * FIXME: Proper locking should be added at write_ssi_mask caller level
43 * to ensure this register read/modify/write sequence is race free.
44 */
45static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set)
46{
47 u32 val = readl(addr);
48 val = (val & ~clear) | set;
49 writel(val, addr);
50}
51#endif
52
Timur Tabi17467f22008-01-11 18:15:26 +010053/**
54 * FSLSSI_I2S_RATES: sample rates supported by the I2S
55 *
56 * This driver currently only supports the SSI running in I2S slave mode,
57 * which means the codec determines the sample rate. Therefore, we tell
58 * ALSA that we support all rates and let the codec driver decide what rates
59 * are really supported.
60 */
61#define FSLSSI_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
62 SNDRV_PCM_RATE_CONTINUOUS)
63
64/**
65 * FSLSSI_I2S_FORMATS: audio formats supported by the SSI
66 *
67 * This driver currently only supports the SSI running in I2S slave mode.
68 *
69 * The SSI has a limitation in that the samples must be in the same byte
70 * order as the host CPU. This is because when multiple bytes are written
71 * to the STX register, the bytes and bits must be written in the same
72 * order. The STX is a shift register, so all the bits need to be aligned
73 * (bit-endianness must match byte-endianness). Processors typically write
74 * the bits within a byte in the same order that the bytes of a word are
75 * written in. So if the host CPU is big-endian, then only big-endian
76 * samples will be written to STX properly.
77 */
78#ifdef __BIG_ENDIAN
79#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
80 SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_S20_3BE | \
81 SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_S24_BE)
82#else
83#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
84 SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \
85 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
86#endif
87
Timur Tabid5a908b2009-03-26 11:42:38 -050088/* SIER bitflag of interrupts to enable */
89#define SIER_FLAGS (CCSR_SSI_SIER_TFRC_EN | CCSR_SSI_SIER_TDMAE | \
90 CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TUE0_EN | \
91 CCSR_SSI_SIER_TUE1_EN | CCSR_SSI_SIER_RFRC_EN | \
92 CCSR_SSI_SIER_RDMAE | CCSR_SSI_SIER_RIE | \
93 CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_ROE1_EN)
94
Timur Tabi17467f22008-01-11 18:15:26 +010095/**
96 * fsl_ssi_private: per-SSI private data
97 *
Timur Tabi17467f22008-01-11 18:15:26 +010098 * @ssi: pointer to the SSI's registers
99 * @ssi_phys: physical address of the SSI registers
100 * @irq: IRQ of this SSI
Timur Tabibe41e942008-07-28 17:04:39 -0500101 * @first_stream: pointer to the stream that was opened first
102 * @second_stream: pointer to second stream
Timur Tabi17467f22008-01-11 18:15:26 +0100103 * @playback: the number of playback streams opened
104 * @capture: the number of capture streams opened
105 * @cpu_dai: the CPU DAI for this device
106 * @dev_attr: the sysfs device attribute structure
107 * @stats: SSI statistics
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000108 * @name: name for this device
Timur Tabi17467f22008-01-11 18:15:26 +0100109 */
110struct fsl_ssi_private {
Timur Tabi17467f22008-01-11 18:15:26 +0100111 struct ccsr_ssi __iomem *ssi;
112 dma_addr_t ssi_phys;
113 unsigned int irq;
Timur Tabibe41e942008-07-28 17:04:39 -0500114 struct snd_pcm_substream *first_stream;
115 struct snd_pcm_substream *second_stream;
Timur Tabi8e9d8692010-08-06 12:16:12 -0500116 unsigned int fifo_depth;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000117 struct snd_soc_dai_driver cpu_dai_drv;
Timur Tabi17467f22008-01-11 18:15:26 +0100118 struct device_attribute dev_attr;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000119 struct platform_device *pdev;
Timur Tabi17467f22008-01-11 18:15:26 +0100120
Shawn Guo09ce1112012-03-16 16:56:43 +0800121 bool new_binding;
122 bool ssi_on_imx;
Shawn Guo95cd98f2012-03-29 10:53:41 +0800123 struct clk *clk;
Shawn Guo09ce1112012-03-16 16:56:43 +0800124 struct platform_device *imx_pcm_pdev;
125 struct imx_pcm_dma_params dma_params_tx;
126 struct imx_pcm_dma_params dma_params_rx;
127
Timur Tabi17467f22008-01-11 18:15:26 +0100128 struct {
129 unsigned int rfrc;
130 unsigned int tfrc;
131 unsigned int cmdau;
132 unsigned int cmddu;
133 unsigned int rxt;
134 unsigned int rdr1;
135 unsigned int rdr0;
136 unsigned int tde1;
137 unsigned int tde0;
138 unsigned int roe1;
139 unsigned int roe0;
140 unsigned int tue1;
141 unsigned int tue0;
142 unsigned int tfs;
143 unsigned int rfs;
144 unsigned int tls;
145 unsigned int rls;
146 unsigned int rff1;
147 unsigned int rff0;
148 unsigned int tfe1;
149 unsigned int tfe0;
150 } stats;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000151
152 char name[1];
Timur Tabi17467f22008-01-11 18:15:26 +0100153};
154
155/**
156 * fsl_ssi_isr: SSI interrupt handler
157 *
158 * Although it's possible to use the interrupt handler to send and receive
159 * data to/from the SSI, we use the DMA instead. Programming is more
160 * complicated, but the performance is much better.
161 *
162 * This interrupt handler is used only to gather statistics.
163 *
164 * @irq: IRQ of the SSI device
165 * @dev_id: pointer to the ssi_private structure for this SSI device
166 */
167static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
168{
169 struct fsl_ssi_private *ssi_private = dev_id;
170 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
171 irqreturn_t ret = IRQ_NONE;
172 __be32 sisr;
173 __be32 sisr2 = 0;
174
175 /* We got an interrupt, so read the status register to see what we
176 were interrupted for. We mask it with the Interrupt Enable register
177 so that we only check for events that we're interested in.
178 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800179 sisr = read_ssi(&ssi->sisr) & SIER_FLAGS;
Timur Tabi17467f22008-01-11 18:15:26 +0100180
181 if (sisr & CCSR_SSI_SISR_RFRC) {
182 ssi_private->stats.rfrc++;
183 sisr2 |= CCSR_SSI_SISR_RFRC;
184 ret = IRQ_HANDLED;
185 }
186
187 if (sisr & CCSR_SSI_SISR_TFRC) {
188 ssi_private->stats.tfrc++;
189 sisr2 |= CCSR_SSI_SISR_TFRC;
190 ret = IRQ_HANDLED;
191 }
192
193 if (sisr & CCSR_SSI_SISR_CMDAU) {
194 ssi_private->stats.cmdau++;
195 ret = IRQ_HANDLED;
196 }
197
198 if (sisr & CCSR_SSI_SISR_CMDDU) {
199 ssi_private->stats.cmddu++;
200 ret = IRQ_HANDLED;
201 }
202
203 if (sisr & CCSR_SSI_SISR_RXT) {
204 ssi_private->stats.rxt++;
205 ret = IRQ_HANDLED;
206 }
207
208 if (sisr & CCSR_SSI_SISR_RDR1) {
209 ssi_private->stats.rdr1++;
210 ret = IRQ_HANDLED;
211 }
212
213 if (sisr & CCSR_SSI_SISR_RDR0) {
214 ssi_private->stats.rdr0++;
215 ret = IRQ_HANDLED;
216 }
217
218 if (sisr & CCSR_SSI_SISR_TDE1) {
219 ssi_private->stats.tde1++;
220 ret = IRQ_HANDLED;
221 }
222
223 if (sisr & CCSR_SSI_SISR_TDE0) {
224 ssi_private->stats.tde0++;
225 ret = IRQ_HANDLED;
226 }
227
228 if (sisr & CCSR_SSI_SISR_ROE1) {
229 ssi_private->stats.roe1++;
230 sisr2 |= CCSR_SSI_SISR_ROE1;
231 ret = IRQ_HANDLED;
232 }
233
234 if (sisr & CCSR_SSI_SISR_ROE0) {
235 ssi_private->stats.roe0++;
236 sisr2 |= CCSR_SSI_SISR_ROE0;
237 ret = IRQ_HANDLED;
238 }
239
240 if (sisr & CCSR_SSI_SISR_TUE1) {
241 ssi_private->stats.tue1++;
242 sisr2 |= CCSR_SSI_SISR_TUE1;
243 ret = IRQ_HANDLED;
244 }
245
246 if (sisr & CCSR_SSI_SISR_TUE0) {
247 ssi_private->stats.tue0++;
248 sisr2 |= CCSR_SSI_SISR_TUE0;
249 ret = IRQ_HANDLED;
250 }
251
252 if (sisr & CCSR_SSI_SISR_TFS) {
253 ssi_private->stats.tfs++;
254 ret = IRQ_HANDLED;
255 }
256
257 if (sisr & CCSR_SSI_SISR_RFS) {
258 ssi_private->stats.rfs++;
259 ret = IRQ_HANDLED;
260 }
261
262 if (sisr & CCSR_SSI_SISR_TLS) {
263 ssi_private->stats.tls++;
264 ret = IRQ_HANDLED;
265 }
266
267 if (sisr & CCSR_SSI_SISR_RLS) {
268 ssi_private->stats.rls++;
269 ret = IRQ_HANDLED;
270 }
271
272 if (sisr & CCSR_SSI_SISR_RFF1) {
273 ssi_private->stats.rff1++;
274 ret = IRQ_HANDLED;
275 }
276
277 if (sisr & CCSR_SSI_SISR_RFF0) {
278 ssi_private->stats.rff0++;
279 ret = IRQ_HANDLED;
280 }
281
282 if (sisr & CCSR_SSI_SISR_TFE1) {
283 ssi_private->stats.tfe1++;
284 ret = IRQ_HANDLED;
285 }
286
287 if (sisr & CCSR_SSI_SISR_TFE0) {
288 ssi_private->stats.tfe0++;
289 ret = IRQ_HANDLED;
290 }
291
292 /* Clear the bits that we set */
293 if (sisr2)
Shawn Guodfa1a102012-03-16 16:56:42 +0800294 write_ssi(sisr2, &ssi->sisr);
Timur Tabi17467f22008-01-11 18:15:26 +0100295
296 return ret;
297}
298
299/**
300 * fsl_ssi_startup: create a new substream
301 *
302 * This is the first function called when a stream is opened.
303 *
304 * If this is the first stream open, then grab the IRQ and program most of
305 * the SSI registers.
306 */
Mark Browndee89c42008-11-18 22:11:38 +0000307static int fsl_ssi_startup(struct snd_pcm_substream *substream,
308 struct snd_soc_dai *dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100309{
310 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Timur Tabi5e538ec2011-09-13 12:59:37 -0500311 struct fsl_ssi_private *ssi_private =
312 snd_soc_dai_get_drvdata(rtd->cpu_dai);
313 int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
Timur Tabi17467f22008-01-11 18:15:26 +0100314
315 /*
316 * If this is the first stream opened, then request the IRQ
317 * and initialize the SSI registers.
318 */
Timur Tabi5e538ec2011-09-13 12:59:37 -0500319 if (!ssi_private->first_stream) {
Timur Tabi17467f22008-01-11 18:15:26 +0100320 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
Timur Tabi17467f22008-01-11 18:15:26 +0100321
Timur Tabi5e538ec2011-09-13 12:59:37 -0500322 ssi_private->first_stream = substream;
323
Timur Tabi17467f22008-01-11 18:15:26 +0100324 /*
325 * Section 16.5 of the MPC8610 reference manual says that the
326 * SSI needs to be disabled before updating the registers we set
327 * here.
328 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800329 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
Timur Tabi17467f22008-01-11 18:15:26 +0100330
331 /*
332 * Program the SSI into I2S Slave Non-Network Synchronous mode.
333 * Also enable the transmit and receive FIFO.
334 *
335 * FIXME: Little-endian samples require a different shift dir
336 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800337 write_ssi_mask(&ssi->scr,
Timur Tabia454dad2009-03-05 17:23:37 -0600338 CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
339 CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE
Timur Tabi5e538ec2011-09-13 12:59:37 -0500340 | (synchronous ? CCSR_SSI_SCR_SYN : 0));
Timur Tabi17467f22008-01-11 18:15:26 +0100341
Shawn Guodfa1a102012-03-16 16:56:42 +0800342 write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
Timur Tabi17467f22008-01-11 18:15:26 +0100343 CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
Shawn Guodfa1a102012-03-16 16:56:42 +0800344 CCSR_SSI_STCR_TSCKP, &ssi->stcr);
Timur Tabi17467f22008-01-11 18:15:26 +0100345
Shawn Guodfa1a102012-03-16 16:56:42 +0800346 write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
Timur Tabi17467f22008-01-11 18:15:26 +0100347 CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
Shawn Guodfa1a102012-03-16 16:56:42 +0800348 CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
Timur Tabi17467f22008-01-11 18:15:26 +0100349
350 /*
351 * The DC and PM bits are only used if the SSI is the clock
352 * master.
353 */
354
Timur Tabi5e538ec2011-09-13 12:59:37 -0500355 /* Enable the interrupts and DMA requests */
Shawn Guodfa1a102012-03-16 16:56:42 +0800356 write_ssi(SIER_FLAGS, &ssi->sier);
Timur Tabi17467f22008-01-11 18:15:26 +0100357
358 /*
359 * Set the watermark for transmit FIFI 0 and receive FIFO 0. We
Timur Tabi8e9d8692010-08-06 12:16:12 -0500360 * don't use FIFO 1. We program the transmit water to signal a
361 * DMA transfer if there are only two (or fewer) elements left
362 * in the FIFO. Two elements equals one frame (left channel,
363 * right channel). This value, however, depends on the depth of
364 * the transmit buffer.
365 *
366 * We program the receive FIFO to notify us if at least two
367 * elements (one frame) have been written to the FIFO. We could
368 * make this value larger (and maybe we should), but this way
369 * data will be written to memory as soon as it's available.
Timur Tabi17467f22008-01-11 18:15:26 +0100370 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800371 write_ssi(CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) |
372 CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2),
373 &ssi->sfcsr);
Timur Tabi17467f22008-01-11 18:15:26 +0100374
375 /*
376 * We keep the SSI disabled because if we enable it, then the
377 * DMA controller will start. It's not supposed to start until
378 * the SCR.TE (or SCR.RE) bit is set, but it does anyway. The
379 * DMA controller will transfer one "BWC" of data (i.e. the
380 * amount of data that the MR.BWC bits are set to). The reason
381 * this is bad is because at this point, the PCM driver has not
382 * finished initializing the DMA controller.
383 */
Timur Tabi5e538ec2011-09-13 12:59:37 -0500384 } else {
385 if (synchronous) {
386 struct snd_pcm_runtime *first_runtime =
387 ssi_private->first_stream->runtime;
388 /*
389 * This is the second stream open, and we're in
390 * synchronous mode, so we need to impose sample
391 * sample size constraints. This is because STCCR is
392 * used for playback and capture in synchronous mode,
393 * so there's no way to specify different word
394 * lengths.
395 *
396 * Note that this can cause a race condition if the
397 * second stream is opened before the first stream is
398 * fully initialized. We provide some protection by
399 * checking to make sure the first stream is
400 * initialized, but it's not perfect. ALSA sometimes
401 * re-initializes the driver with a different sample
402 * rate or size. If the second stream is opened
403 * before the first stream has received its final
404 * parameters, then the second stream may be
405 * constrained to the wrong sample rate or size.
406 */
407 if (!first_runtime->sample_bits) {
408 dev_err(substream->pcm->card->dev,
409 "set sample size in %s stream first\n",
410 substream->stream ==
411 SNDRV_PCM_STREAM_PLAYBACK
412 ? "capture" : "playback");
413 return -EAGAIN;
414 }
Timur Tabi17467f22008-01-11 18:15:26 +0100415
Timur Tabia454dad2009-03-05 17:23:37 -0600416 snd_pcm_hw_constraint_minmax(substream->runtime,
417 SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
418 first_runtime->sample_bits,
419 first_runtime->sample_bits);
Timur Tabi5e538ec2011-09-13 12:59:37 -0500420 }
Timur Tabibe41e942008-07-28 17:04:39 -0500421
422 ssi_private->second_stream = substream;
423 }
424
Shawn Guo09ce1112012-03-16 16:56:43 +0800425 if (ssi_private->ssi_on_imx)
426 snd_soc_dai_set_dma_data(dai, substream,
427 (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
428 &ssi_private->dma_params_tx :
429 &ssi_private->dma_params_rx);
430
Timur Tabi17467f22008-01-11 18:15:26 +0100431 return 0;
432}
433
434/**
Timur Tabi85ef2372009-02-05 17:56:02 -0600435 * fsl_ssi_hw_params - program the sample size
Timur Tabi17467f22008-01-11 18:15:26 +0100436 *
437 * Most of the SSI registers have been programmed in the startup function,
438 * but the word length must be programmed here. Unfortunately, programming
439 * the SxCCR.WL bits requires the SSI to be temporarily disabled. This can
440 * cause a problem with supporting simultaneous playback and capture. If
441 * the SSI is already playing a stream, then that stream may be temporarily
442 * stopped when you start capture.
443 *
444 * Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the
445 * clock master.
446 */
Timur Tabi85ef2372009-02-05 17:56:02 -0600447static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
448 struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100449{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000450 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
Timur Tabi5e538ec2011-09-13 12:59:37 -0500451 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
452 unsigned int sample_size =
453 snd_pcm_format_width(params_format(hw_params));
454 u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
Shawn Guodfa1a102012-03-16 16:56:42 +0800455 int enabled = read_ssi(&ssi->scr) & CCSR_SSI_SCR_SSIEN;
Timur Tabi17467f22008-01-11 18:15:26 +0100456
Timur Tabi5e538ec2011-09-13 12:59:37 -0500457 /*
458 * If we're in synchronous mode, and the SSI is already enabled,
459 * then STCCR is already set properly.
460 */
461 if (enabled && ssi_private->cpu_dai_drv.symmetric_rates)
462 return 0;
Timur Tabi17467f22008-01-11 18:15:26 +0100463
Timur Tabi5e538ec2011-09-13 12:59:37 -0500464 /*
465 * FIXME: The documentation says that SxCCR[WL] should not be
466 * modified while the SSI is enabled. The only time this can
467 * happen is if we're trying to do simultaneous playback and
468 * capture in asynchronous mode. Unfortunately, I have been enable
469 * to get that to work at all on the P1022DS. Therefore, we don't
470 * bother to disable/enable the SSI when setting SxCCR[WL], because
471 * the SSI will stop anyway. Maybe one day, this will get fixed.
472 */
Timur Tabi17467f22008-01-11 18:15:26 +0100473
Timur Tabi5e538ec2011-09-13 12:59:37 -0500474 /* In synchronous mode, the SSI uses STCCR for capture */
475 if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
476 ssi_private->cpu_dai_drv.symmetric_rates)
Shawn Guodfa1a102012-03-16 16:56:42 +0800477 write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
Timur Tabi5e538ec2011-09-13 12:59:37 -0500478 else
Shawn Guodfa1a102012-03-16 16:56:42 +0800479 write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
Timur Tabi17467f22008-01-11 18:15:26 +0100480
481 return 0;
482}
483
484/**
485 * fsl_ssi_trigger: start and stop the DMA transfer.
486 *
487 * This function is called by ALSA to start, stop, pause, and resume the DMA
488 * transfer of data.
489 *
490 * The DMA channel is in external master start and pause mode, which
491 * means the SSI completely controls the flow of data.
492 */
Mark Browndee89c42008-11-18 22:11:38 +0000493static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
494 struct snd_soc_dai *dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100495{
496 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000497 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
Timur Tabi17467f22008-01-11 18:15:26 +0100498 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
499
500 switch (cmd) {
501 case SNDRV_PCM_TRIGGER_START:
Timur Tabi17467f22008-01-11 18:15:26 +0100502 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
Timur Tabia4d11fe2009-03-25 18:20:37 -0500503 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
Shawn Guodfa1a102012-03-16 16:56:42 +0800504 write_ssi_mask(&ssi->scr, 0,
Timur Tabibe41e942008-07-28 17:04:39 -0500505 CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE);
Timur Tabia4d11fe2009-03-25 18:20:37 -0500506 else
Shawn Guodfa1a102012-03-16 16:56:42 +0800507 write_ssi_mask(&ssi->scr, 0,
Timur Tabibe41e942008-07-28 17:04:39 -0500508 CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE);
Timur Tabi17467f22008-01-11 18:15:26 +0100509 break;
510
511 case SNDRV_PCM_TRIGGER_STOP:
Timur Tabi17467f22008-01-11 18:15:26 +0100512 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
513 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
Shawn Guodfa1a102012-03-16 16:56:42 +0800514 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TE, 0);
Timur Tabi17467f22008-01-11 18:15:26 +0100515 else
Shawn Guodfa1a102012-03-16 16:56:42 +0800516 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0);
Timur Tabi17467f22008-01-11 18:15:26 +0100517 break;
518
519 default:
520 return -EINVAL;
521 }
522
523 return 0;
524}
525
526/**
527 * fsl_ssi_shutdown: shutdown the SSI
528 *
529 * Shutdown the SSI if there are no other substreams open.
530 */
Mark Browndee89c42008-11-18 22:11:38 +0000531static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
532 struct snd_soc_dai *dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100533{
534 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000535 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
Timur Tabi17467f22008-01-11 18:15:26 +0100536
Timur Tabibe41e942008-07-28 17:04:39 -0500537 if (ssi_private->first_stream == substream)
538 ssi_private->first_stream = ssi_private->second_stream;
539
540 ssi_private->second_stream = NULL;
541
Timur Tabi17467f22008-01-11 18:15:26 +0100542 /*
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400543 * If this is the last active substream, disable the SSI.
Timur Tabi17467f22008-01-11 18:15:26 +0100544 */
Timur Tabi5e538ec2011-09-13 12:59:37 -0500545 if (!ssi_private->first_stream) {
Timur Tabi17467f22008-01-11 18:15:26 +0100546 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
547
Shawn Guodfa1a102012-03-16 16:56:42 +0800548 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
Timur Tabi17467f22008-01-11 18:15:26 +0100549 }
550}
551
Lars-Peter Clausen85e76522011-11-23 11:40:40 +0100552static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
Eric Miao6335d052009-03-03 09:41:00 +0800553 .startup = fsl_ssi_startup,
554 .hw_params = fsl_ssi_hw_params,
555 .shutdown = fsl_ssi_shutdown,
556 .trigger = fsl_ssi_trigger,
Eric Miao6335d052009-03-03 09:41:00 +0800557};
558
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000559/* Template for the CPU dai driver structure */
560static struct snd_soc_dai_driver fsl_ssi_dai_template = {
Timur Tabi17467f22008-01-11 18:15:26 +0100561 .playback = {
562 /* The SSI does not support monaural audio. */
563 .channels_min = 2,
564 .channels_max = 2,
565 .rates = FSLSSI_I2S_RATES,
566 .formats = FSLSSI_I2S_FORMATS,
567 },
568 .capture = {
569 .channels_min = 2,
570 .channels_max = 2,
571 .rates = FSLSSI_I2S_RATES,
572 .formats = FSLSSI_I2S_FORMATS,
573 },
Eric Miao6335d052009-03-03 09:41:00 +0800574 .ops = &fsl_ssi_dai_ops,
Timur Tabi17467f22008-01-11 18:15:26 +0100575};
576
Timur Tabid5a908b2009-03-26 11:42:38 -0500577/* Show the statistics of a flag only if its interrupt is enabled. The
578 * compiler will optimze this code to a no-op if the interrupt is not
579 * enabled.
580 */
581#define SIER_SHOW(flag, name) \
582 do { \
583 if (SIER_FLAGS & CCSR_SSI_SIER_##flag) \
584 length += sprintf(buf + length, #name "=%u\n", \
585 ssi_private->stats.name); \
586 } while (0)
587
588
Timur Tabi17467f22008-01-11 18:15:26 +0100589/**
590 * fsl_sysfs_ssi_show: display SSI statistics
591 *
Timur Tabid5a908b2009-03-26 11:42:38 -0500592 * Display the statistics for the current SSI device. To avoid confusion,
593 * we only show those counts that are enabled.
Timur Tabi17467f22008-01-11 18:15:26 +0100594 */
595static ssize_t fsl_sysfs_ssi_show(struct device *dev,
596 struct device_attribute *attr, char *buf)
597{
598 struct fsl_ssi_private *ssi_private =
Timur Tabid5a908b2009-03-26 11:42:38 -0500599 container_of(attr, struct fsl_ssi_private, dev_attr);
600 ssize_t length = 0;
Timur Tabi17467f22008-01-11 18:15:26 +0100601
Timur Tabid5a908b2009-03-26 11:42:38 -0500602 SIER_SHOW(RFRC_EN, rfrc);
603 SIER_SHOW(TFRC_EN, tfrc);
604 SIER_SHOW(CMDAU_EN, cmdau);
605 SIER_SHOW(CMDDU_EN, cmddu);
606 SIER_SHOW(RXT_EN, rxt);
607 SIER_SHOW(RDR1_EN, rdr1);
608 SIER_SHOW(RDR0_EN, rdr0);
609 SIER_SHOW(TDE1_EN, tde1);
610 SIER_SHOW(TDE0_EN, tde0);
611 SIER_SHOW(ROE1_EN, roe1);
612 SIER_SHOW(ROE0_EN, roe0);
613 SIER_SHOW(TUE1_EN, tue1);
614 SIER_SHOW(TUE0_EN, tue0);
615 SIER_SHOW(TFS_EN, tfs);
616 SIER_SHOW(RFS_EN, rfs);
617 SIER_SHOW(TLS_EN, tls);
618 SIER_SHOW(RLS_EN, rls);
619 SIER_SHOW(RFF1_EN, rff1);
620 SIER_SHOW(RFF0_EN, rff0);
621 SIER_SHOW(TFE1_EN, tfe1);
622 SIER_SHOW(TFE0_EN, tfe0);
Timur Tabi17467f22008-01-11 18:15:26 +0100623
624 return length;
625}
626
627/**
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000628 * Make every character in a string lower-case
Timur Tabi17467f22008-01-11 18:15:26 +0100629 */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000630static void make_lowercase(char *s)
Timur Tabi17467f22008-01-11 18:15:26 +0100631{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000632 char *p = s;
633 char c;
634
635 while ((c = *p)) {
636 if ((c >= 'A') && (c <= 'Z'))
637 *p = c + ('a' - 'A');
638 p++;
639 }
640}
641
Grant Likelyf07eb222011-02-22 21:05:04 -0700642static int __devinit fsl_ssi_probe(struct platform_device *pdev)
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000643{
Timur Tabi17467f22008-01-11 18:15:26 +0100644 struct fsl_ssi_private *ssi_private;
645 int ret = 0;
Timur Tabi87a06322010-08-03 17:55:28 -0500646 struct device_attribute *dev_attr = NULL;
Timur Tabi38fec722010-08-19 15:26:58 -0500647 struct device_node *np = pdev->dev.of_node;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000648 const char *p, *sprop;
Timur Tabi8e9d8692010-08-06 12:16:12 -0500649 const uint32_t *iprop;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000650 struct resource res;
651 char name[64];
Timur Tabi17467f22008-01-11 18:15:26 +0100652
Timur Tabiff713342010-08-04 17:51:08 -0500653 /* SSIs that are not connected on the board should have a
654 * status = "disabled"
655 * property in their device tree nodes.
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000656 */
Timur Tabiff713342010-08-04 17:51:08 -0500657 if (!of_device_is_available(np))
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000658 return -ENODEV;
659
660 /* We only support the SSI in "I2S Slave" mode */
661 sprop = of_get_property(np, "fsl,mode", NULL);
662 if (!sprop || strcmp(sprop, "i2s-slave")) {
Timur Tabi38fec722010-08-19 15:26:58 -0500663 dev_notice(&pdev->dev, "mode %s is unsupported\n", sprop);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000664 return -ENODEV;
Timur Tabi17467f22008-01-11 18:15:26 +0100665 }
Timur Tabi17467f22008-01-11 18:15:26 +0100666
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000667 /* The DAI name is the last part of the full name of the node. */
668 p = strrchr(np->full_name, '/') + 1;
669 ssi_private = kzalloc(sizeof(struct fsl_ssi_private) + strlen(p),
670 GFP_KERNEL);
671 if (!ssi_private) {
Timur Tabi38fec722010-08-19 15:26:58 -0500672 dev_err(&pdev->dev, "could not allocate DAI object\n");
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000673 return -ENOMEM;
674 }
Timur Tabi17467f22008-01-11 18:15:26 +0100675
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000676 strcpy(ssi_private->name, p);
Timur Tabi17467f22008-01-11 18:15:26 +0100677
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000678 /* Initialize this copy of the CPU DAI driver structure */
679 memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
680 sizeof(fsl_ssi_dai_template));
681 ssi_private->cpu_dai_drv.name = ssi_private->name;
682
683 /* Get the addresses and IRQ */
684 ret = of_address_to_resource(np, 0, &res);
685 if (ret) {
Timur Tabi38fec722010-08-19 15:26:58 -0500686 dev_err(&pdev->dev, "could not determine device resources\n");
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400687 goto error_kmalloc;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000688 }
Timur Tabi147dfe92011-06-08 15:02:55 -0500689 ssi_private->ssi = of_iomap(np, 0);
690 if (!ssi_private->ssi) {
691 dev_err(&pdev->dev, "could not map device resources\n");
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400692 ret = -ENOMEM;
693 goto error_kmalloc;
Timur Tabi147dfe92011-06-08 15:02:55 -0500694 }
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000695 ssi_private->ssi_phys = res.start;
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400696
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000697 ssi_private->irq = irq_of_parse_and_map(np, 0);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400698 if (ssi_private->irq == NO_IRQ) {
699 dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
700 ret = -ENXIO;
701 goto error_iomap;
702 }
703
704 /* The 'name' should not have any slashes in it. */
705 ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0, ssi_private->name,
706 ssi_private);
707 if (ret < 0) {
708 dev_err(&pdev->dev, "could not claim irq %u\n", ssi_private->irq);
709 goto error_irqmap;
710 }
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000711
712 /* Are the RX and the TX clocks locked? */
Timur Tabi5e538ec2011-09-13 12:59:37 -0500713 if (!of_find_property(np, "fsl,ssi-asynchronous", NULL))
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000714 ssi_private->cpu_dai_drv.symmetric_rates = 1;
Timur Tabi17467f22008-01-11 18:15:26 +0100715
Timur Tabi8e9d8692010-08-06 12:16:12 -0500716 /* Determine the FIFO depth. */
717 iprop = of_get_property(np, "fsl,fifo-depth", NULL);
718 if (iprop)
Timur Tabi147dfe92011-06-08 15:02:55 -0500719 ssi_private->fifo_depth = be32_to_cpup(iprop);
Timur Tabi8e9d8692010-08-06 12:16:12 -0500720 else
721 /* Older 8610 DTs didn't have the fifo-depth property */
722 ssi_private->fifo_depth = 8;
723
Shawn Guo09ce1112012-03-16 16:56:43 +0800724 if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx21-ssi")) {
725 u32 dma_events[2];
726 ssi_private->ssi_on_imx = true;
Shawn Guo95cd98f2012-03-29 10:53:41 +0800727
728 ssi_private->clk = clk_get(&pdev->dev, NULL);
729 if (IS_ERR(ssi_private->clk)) {
730 ret = PTR_ERR(ssi_private->clk);
731 dev_err(&pdev->dev, "could not get clock: %d\n", ret);
732 goto error_irq;
733 }
734 clk_prepare_enable(ssi_private->clk);
735
Shawn Guo09ce1112012-03-16 16:56:43 +0800736 /*
737 * We have burstsize be "fifo_depth - 2" to match the SSI
738 * watermark setting in fsl_ssi_startup().
739 */
740 ssi_private->dma_params_tx.burstsize =
741 ssi_private->fifo_depth - 2;
742 ssi_private->dma_params_rx.burstsize =
743 ssi_private->fifo_depth - 2;
744 ssi_private->dma_params_tx.dma_addr =
745 ssi_private->ssi_phys + offsetof(struct ccsr_ssi, stx0);
746 ssi_private->dma_params_rx.dma_addr =
747 ssi_private->ssi_phys + offsetof(struct ccsr_ssi, srx0);
748 /*
749 * TODO: This is a temporary solution and should be changed
750 * to use generic DMA binding later when the helplers get in.
751 */
752 ret = of_property_read_u32_array(pdev->dev.of_node,
753 "fsl,ssi-dma-events", dma_events, 2);
754 if (ret) {
755 dev_err(&pdev->dev, "could not get dma events\n");
Shawn Guo95cd98f2012-03-29 10:53:41 +0800756 goto error_clk;
Shawn Guo09ce1112012-03-16 16:56:43 +0800757 }
758 ssi_private->dma_params_tx.dma = dma_events[0];
759 ssi_private->dma_params_rx.dma = dma_events[1];
Shawn Guob46b3732012-03-28 15:34:56 +0800760
761 ssi_private->dma_params_tx.shared_peripheral =
762 of_device_is_compatible(of_get_parent(np),
763 "fsl,spba-bus");
764 ssi_private->dma_params_rx.shared_peripheral =
765 ssi_private->dma_params_tx.shared_peripheral;
Shawn Guo09ce1112012-03-16 16:56:43 +0800766 }
767
Timur Tabi17467f22008-01-11 18:15:26 +0100768 /* Initialize the the device_attribute structure */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000769 dev_attr = &ssi_private->dev_attr;
Timur Tabi0f768a72011-11-14 16:35:26 -0600770 sysfs_attr_init(&dev_attr->attr);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000771 dev_attr->attr.name = "statistics";
Timur Tabi17467f22008-01-11 18:15:26 +0100772 dev_attr->attr.mode = S_IRUGO;
773 dev_attr->show = fsl_sysfs_ssi_show;
774
Timur Tabi38fec722010-08-19 15:26:58 -0500775 ret = device_create_file(&pdev->dev, dev_attr);
Timur Tabi17467f22008-01-11 18:15:26 +0100776 if (ret) {
Timur Tabi38fec722010-08-19 15:26:58 -0500777 dev_err(&pdev->dev, "could not create sysfs %s file\n",
Timur Tabi17467f22008-01-11 18:15:26 +0100778 ssi_private->dev_attr.attr.name);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400779 goto error_irq;
Timur Tabi17467f22008-01-11 18:15:26 +0100780 }
781
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000782 /* Register with ASoC */
Timur Tabi38fec722010-08-19 15:26:58 -0500783 dev_set_drvdata(&pdev->dev, ssi_private);
Mark Brown3f4b7832008-12-03 19:26:35 +0000784
Timur Tabi38fec722010-08-19 15:26:58 -0500785 ret = snd_soc_register_dai(&pdev->dev, &ssi_private->cpu_dai_drv);
Timur Tabi87a06322010-08-03 17:55:28 -0500786 if (ret) {
Timur Tabi38fec722010-08-19 15:26:58 -0500787 dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400788 goto error_dev;
Mark Brown3f4b7832008-12-03 19:26:35 +0000789 }
Timur Tabi17467f22008-01-11 18:15:26 +0100790
Shawn Guo09ce1112012-03-16 16:56:43 +0800791 if (ssi_private->ssi_on_imx) {
792 ssi_private->imx_pcm_pdev =
793 platform_device_register_simple("imx-pcm-audio",
794 -1, NULL, 0);
795 if (IS_ERR(ssi_private->imx_pcm_pdev)) {
796 ret = PTR_ERR(ssi_private->imx_pcm_pdev);
797 goto error_dev;
798 }
799 }
800
801 /*
802 * If codec-handle property is missing from SSI node, we assume
803 * that the machine driver uses new binding which does not require
804 * SSI driver to trigger machine driver's probe.
805 */
806 if (!of_get_property(np, "codec-handle", NULL)) {
807 ssi_private->new_binding = true;
808 goto done;
809 }
810
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000811 /* Trigger the machine driver's probe function. The platform driver
Shawn Guo2b81ec62012-03-09 00:59:46 +0800812 * name of the machine driver is taken from /compatible property of the
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000813 * device tree. We also pass the address of the CPU DAI driver
814 * structure.
815 */
Shawn Guo2b81ec62012-03-09 00:59:46 +0800816 sprop = of_get_property(of_find_node_by_path("/"), "compatible", NULL);
817 /* Sometimes the compatible name has a "fsl," prefix, so we strip it. */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000818 p = strrchr(sprop, ',');
819 if (p)
820 sprop = p + 1;
821 snprintf(name, sizeof(name), "snd-soc-%s", sprop);
822 make_lowercase(name);
823
824 ssi_private->pdev =
Timur Tabi38fec722010-08-19 15:26:58 -0500825 platform_device_register_data(&pdev->dev, name, 0, NULL, 0);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000826 if (IS_ERR(ssi_private->pdev)) {
827 ret = PTR_ERR(ssi_private->pdev);
Timur Tabi38fec722010-08-19 15:26:58 -0500828 dev_err(&pdev->dev, "failed to register platform: %d\n", ret);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400829 goto error_dai;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000830 }
831
Shawn Guo09ce1112012-03-16 16:56:43 +0800832done:
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000833 return 0;
Timur Tabi87a06322010-08-03 17:55:28 -0500834
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400835error_dai:
Shawn Guo09ce1112012-03-16 16:56:43 +0800836 if (ssi_private->ssi_on_imx)
837 platform_device_unregister(ssi_private->imx_pcm_pdev);
Timur Tabi38fec722010-08-19 15:26:58 -0500838 snd_soc_unregister_dai(&pdev->dev);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400839
840error_dev:
Timur Tabi38fec722010-08-19 15:26:58 -0500841 dev_set_drvdata(&pdev->dev, NULL);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400842 device_remove_file(&pdev->dev, dev_attr);
843
Shawn Guo95cd98f2012-03-29 10:53:41 +0800844error_clk:
845 if (ssi_private->ssi_on_imx) {
846 clk_disable_unprepare(ssi_private->clk);
847 clk_put(ssi_private->clk);
848 }
849
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400850error_irq:
851 free_irq(ssi_private->irq, ssi_private);
852
853error_irqmap:
Timur Tabi87a06322010-08-03 17:55:28 -0500854 irq_dispose_mapping(ssi_private->irq);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400855
856error_iomap:
Timur Tabi87a06322010-08-03 17:55:28 -0500857 iounmap(ssi_private->ssi);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400858
859error_kmalloc:
Timur Tabi87a06322010-08-03 17:55:28 -0500860 kfree(ssi_private);
861
862 return ret;
Timur Tabi17467f22008-01-11 18:15:26 +0100863}
Timur Tabi17467f22008-01-11 18:15:26 +0100864
Timur Tabi38fec722010-08-19 15:26:58 -0500865static int fsl_ssi_remove(struct platform_device *pdev)
Timur Tabi17467f22008-01-11 18:15:26 +0100866{
Timur Tabi38fec722010-08-19 15:26:58 -0500867 struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev);
Timur Tabi17467f22008-01-11 18:15:26 +0100868
Shawn Guo09ce1112012-03-16 16:56:43 +0800869 if (!ssi_private->new_binding)
870 platform_device_unregister(ssi_private->pdev);
Shawn Guo95cd98f2012-03-29 10:53:41 +0800871 if (ssi_private->ssi_on_imx) {
Shawn Guo09ce1112012-03-16 16:56:43 +0800872 platform_device_unregister(ssi_private->imx_pcm_pdev);
Shawn Guo95cd98f2012-03-29 10:53:41 +0800873 clk_disable_unprepare(ssi_private->clk);
874 clk_put(ssi_private->clk);
875 }
Timur Tabi38fec722010-08-19 15:26:58 -0500876 snd_soc_unregister_dai(&pdev->dev);
877 device_remove_file(&pdev->dev, &ssi_private->dev_attr);
Mark Brown3f4b7832008-12-03 19:26:35 +0000878
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400879 free_irq(ssi_private->irq, ssi_private);
880 irq_dispose_mapping(ssi_private->irq);
881
Timur Tabi17467f22008-01-11 18:15:26 +0100882 kfree(ssi_private);
Timur Tabi38fec722010-08-19 15:26:58 -0500883 dev_set_drvdata(&pdev->dev, NULL);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000884
885 return 0;
Timur Tabi17467f22008-01-11 18:15:26 +0100886}
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000887
888static const struct of_device_id fsl_ssi_ids[] = {
889 { .compatible = "fsl,mpc8610-ssi", },
Shawn Guo09ce1112012-03-16 16:56:43 +0800890 { .compatible = "fsl,imx21-ssi", },
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000891 {}
892};
893MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
894
Grant Likelyf07eb222011-02-22 21:05:04 -0700895static struct platform_driver fsl_ssi_driver = {
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000896 .driver = {
897 .name = "fsl-ssi-dai",
898 .owner = THIS_MODULE,
899 .of_match_table = fsl_ssi_ids,
900 },
901 .probe = fsl_ssi_probe,
902 .remove = fsl_ssi_remove,
903};
Timur Tabi17467f22008-01-11 18:15:26 +0100904
Axel Linba0a7e02011-11-25 10:10:55 +0800905module_platform_driver(fsl_ssi_driver);
Timur Tabia454dad2009-03-05 17:23:37 -0600906
Timur Tabi17467f22008-01-11 18:15:26 +0100907MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
908MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver");
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000909MODULE_LICENSE("GPL v2");