blob: 8b075ef5c6b98a784464b550c7e829e9e6e1c88d [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.
Markus Pargmannde623ec2013-07-27 13:31:53 +020011 *
12 *
13 * Some notes why imx-pcm-fiq is used instead of DMA on some boards:
14 *
15 * The i.MX SSI core has some nasty limitations in AC97 mode. While most
16 * sane processor vendors have a FIFO per AC97 slot, the i.MX has only
17 * one FIFO which combines all valid receive slots. We cannot even select
18 * which slots we want to receive. The WM9712 with which this driver
19 * was developed with always sends GPIO status data in slot 12 which
20 * we receive in our (PCM-) data stream. The only chance we have is to
21 * manually skip this data in the FIQ handler. With sampling rates different
22 * from 48000Hz not every frame has valid receive data, so the ratio
23 * between pcm data and GPIO status data changes. Our FIQ handler is not
24 * able to handle this, hence this driver only works with 48000Hz sampling
25 * rate.
26 * Reading and writing AC97 registers is another challenge. The core
27 * provides us status bits when the read register is updated with *another*
28 * value. When we read the same register two times (and the register still
29 * contains the same value) these status bits are not set. We work
30 * around this by not polling these bits but only wait a fixed delay.
Timur Tabi17467f22008-01-11 18:15:26 +010031 */
32
33#include <linux/init.h>
Shawn Guodfa1a102012-03-16 16:56:42 +080034#include <linux/io.h>
Timur Tabi17467f22008-01-11 18:15:26 +010035#include <linux/module.h>
36#include <linux/interrupt.h>
Shawn Guo95cd98f2012-03-29 10:53:41 +080037#include <linux/clk.h>
Timur Tabi17467f22008-01-11 18:15:26 +010038#include <linux/device.h>
39#include <linux/delay.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090040#include <linux/slab.h>
Shawn Guodfa1a102012-03-16 16:56:42 +080041#include <linux/of_address.h>
42#include <linux/of_irq.h>
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +000043#include <linux/of_platform.h>
Timur Tabi17467f22008-01-11 18:15:26 +010044
Timur Tabi17467f22008-01-11 18:15:26 +010045#include <sound/core.h>
46#include <sound/pcm.h>
47#include <sound/pcm_params.h>
48#include <sound/initval.h>
49#include <sound/soc.h>
Lars-Peter Clausena8909c92013-04-03 11:06:04 +020050#include <sound/dmaengine_pcm.h>
Timur Tabi17467f22008-01-11 18:15:26 +010051
Timur Tabi17467f22008-01-11 18:15:26 +010052#include "fsl_ssi.h"
Shawn Guo09ce1112012-03-16 16:56:43 +080053#include "imx-pcm.h"
Timur Tabi17467f22008-01-11 18:15:26 +010054
Shawn Guodfa1a102012-03-16 16:56:42 +080055#ifdef PPC
56#define read_ssi(addr) in_be32(addr)
57#define write_ssi(val, addr) out_be32(addr, val)
58#define write_ssi_mask(addr, clear, set) clrsetbits_be32(addr, clear, set)
Mark Brown0a9eaa32013-07-19 11:40:13 +010059#else
Shawn Guodfa1a102012-03-16 16:56:42 +080060#define read_ssi(addr) readl(addr)
61#define write_ssi(val, addr) writel(val, addr)
62/*
63 * FIXME: Proper locking should be added at write_ssi_mask caller level
64 * to ensure this register read/modify/write sequence is race free.
65 */
66static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set)
67{
68 u32 val = readl(addr);
69 val = (val & ~clear) | set;
70 writel(val, addr);
71}
72#endif
73
Timur Tabi17467f22008-01-11 18:15:26 +010074/**
75 * FSLSSI_I2S_RATES: sample rates supported by the I2S
76 *
77 * This driver currently only supports the SSI running in I2S slave mode,
78 * which means the codec determines the sample rate. Therefore, we tell
79 * ALSA that we support all rates and let the codec driver decide what rates
80 * are really supported.
81 */
82#define FSLSSI_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
83 SNDRV_PCM_RATE_CONTINUOUS)
84
85/**
86 * FSLSSI_I2S_FORMATS: audio formats supported by the SSI
87 *
88 * This driver currently only supports the SSI running in I2S slave mode.
89 *
90 * The SSI has a limitation in that the samples must be in the same byte
91 * order as the host CPU. This is because when multiple bytes are written
92 * to the STX register, the bytes and bits must be written in the same
93 * order. The STX is a shift register, so all the bits need to be aligned
94 * (bit-endianness must match byte-endianness). Processors typically write
95 * the bits within a byte in the same order that the bytes of a word are
96 * written in. So if the host CPU is big-endian, then only big-endian
97 * samples will be written to STX properly.
98 */
99#ifdef __BIG_ENDIAN
100#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
101 SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_S20_3BE | \
102 SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_S24_BE)
103#else
104#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
105 SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \
106 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
107#endif
108
Timur Tabid5a908b2009-03-26 11:42:38 -0500109/* SIER bitflag of interrupts to enable */
110#define SIER_FLAGS (CCSR_SSI_SIER_TFRC_EN | CCSR_SSI_SIER_TDMAE | \
111 CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TUE0_EN | \
112 CCSR_SSI_SIER_TUE1_EN | CCSR_SSI_SIER_RFRC_EN | \
113 CCSR_SSI_SIER_RDMAE | CCSR_SSI_SIER_RIE | \
114 CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_ROE1_EN)
115
Timur Tabi17467f22008-01-11 18:15:26 +0100116/**
117 * fsl_ssi_private: per-SSI private data
118 *
Timur Tabi17467f22008-01-11 18:15:26 +0100119 * @ssi: pointer to the SSI's registers
120 * @ssi_phys: physical address of the SSI registers
121 * @irq: IRQ of this SSI
Timur Tabibe41e942008-07-28 17:04:39 -0500122 * @first_stream: pointer to the stream that was opened first
123 * @second_stream: pointer to second stream
Timur Tabi17467f22008-01-11 18:15:26 +0100124 * @playback: the number of playback streams opened
125 * @capture: the number of capture streams opened
126 * @cpu_dai: the CPU DAI for this device
127 * @dev_attr: the sysfs device attribute structure
128 * @stats: SSI statistics
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000129 * @name: name for this device
Timur Tabi17467f22008-01-11 18:15:26 +0100130 */
131struct fsl_ssi_private {
Timur Tabi17467f22008-01-11 18:15:26 +0100132 struct ccsr_ssi __iomem *ssi;
133 dma_addr_t ssi_phys;
134 unsigned int irq;
Timur Tabibe41e942008-07-28 17:04:39 -0500135 struct snd_pcm_substream *first_stream;
136 struct snd_pcm_substream *second_stream;
Timur Tabi8e9d8692010-08-06 12:16:12 -0500137 unsigned int fifo_depth;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000138 struct snd_soc_dai_driver cpu_dai_drv;
Timur Tabi17467f22008-01-11 18:15:26 +0100139 struct device_attribute dev_attr;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000140 struct platform_device *pdev;
Timur Tabi17467f22008-01-11 18:15:26 +0100141
Shawn Guo09ce1112012-03-16 16:56:43 +0800142 bool new_binding;
143 bool ssi_on_imx;
Markus Pargmannde623ec2013-07-27 13:31:53 +0200144 bool use_dma;
Shawn Guo95cd98f2012-03-29 10:53:41 +0800145 struct clk *clk;
Lars-Peter Clausena8909c92013-04-03 11:06:04 +0200146 struct snd_dmaengine_dai_dma_data dma_params_tx;
147 struct snd_dmaengine_dai_dma_data dma_params_rx;
148 struct imx_dma_data filter_data_tx;
149 struct imx_dma_data filter_data_rx;
Markus Pargmannde623ec2013-07-27 13:31:53 +0200150 struct imx_pcm_fiq_params fiq_params;
Shawn Guo09ce1112012-03-16 16:56:43 +0800151
Timur Tabi17467f22008-01-11 18:15:26 +0100152 struct {
153 unsigned int rfrc;
154 unsigned int tfrc;
155 unsigned int cmdau;
156 unsigned int cmddu;
157 unsigned int rxt;
158 unsigned int rdr1;
159 unsigned int rdr0;
160 unsigned int tde1;
161 unsigned int tde0;
162 unsigned int roe1;
163 unsigned int roe0;
164 unsigned int tue1;
165 unsigned int tue0;
166 unsigned int tfs;
167 unsigned int rfs;
168 unsigned int tls;
169 unsigned int rls;
170 unsigned int rff1;
171 unsigned int rff0;
172 unsigned int tfe1;
173 unsigned int tfe0;
174 } stats;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000175
176 char name[1];
Timur Tabi17467f22008-01-11 18:15:26 +0100177};
178
179/**
180 * fsl_ssi_isr: SSI interrupt handler
181 *
182 * Although it's possible to use the interrupt handler to send and receive
183 * data to/from the SSI, we use the DMA instead. Programming is more
184 * complicated, but the performance is much better.
185 *
186 * This interrupt handler is used only to gather statistics.
187 *
188 * @irq: IRQ of the SSI device
189 * @dev_id: pointer to the ssi_private structure for this SSI device
190 */
191static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
192{
193 struct fsl_ssi_private *ssi_private = dev_id;
194 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
195 irqreturn_t ret = IRQ_NONE;
196 __be32 sisr;
197 __be32 sisr2 = 0;
198
199 /* We got an interrupt, so read the status register to see what we
200 were interrupted for. We mask it with the Interrupt Enable register
201 so that we only check for events that we're interested in.
202 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800203 sisr = read_ssi(&ssi->sisr) & SIER_FLAGS;
Timur Tabi17467f22008-01-11 18:15:26 +0100204
205 if (sisr & CCSR_SSI_SISR_RFRC) {
206 ssi_private->stats.rfrc++;
207 sisr2 |= CCSR_SSI_SISR_RFRC;
208 ret = IRQ_HANDLED;
209 }
210
211 if (sisr & CCSR_SSI_SISR_TFRC) {
212 ssi_private->stats.tfrc++;
213 sisr2 |= CCSR_SSI_SISR_TFRC;
214 ret = IRQ_HANDLED;
215 }
216
217 if (sisr & CCSR_SSI_SISR_CMDAU) {
218 ssi_private->stats.cmdau++;
219 ret = IRQ_HANDLED;
220 }
221
222 if (sisr & CCSR_SSI_SISR_CMDDU) {
223 ssi_private->stats.cmddu++;
224 ret = IRQ_HANDLED;
225 }
226
227 if (sisr & CCSR_SSI_SISR_RXT) {
228 ssi_private->stats.rxt++;
229 ret = IRQ_HANDLED;
230 }
231
232 if (sisr & CCSR_SSI_SISR_RDR1) {
233 ssi_private->stats.rdr1++;
234 ret = IRQ_HANDLED;
235 }
236
237 if (sisr & CCSR_SSI_SISR_RDR0) {
238 ssi_private->stats.rdr0++;
239 ret = IRQ_HANDLED;
240 }
241
242 if (sisr & CCSR_SSI_SISR_TDE1) {
243 ssi_private->stats.tde1++;
244 ret = IRQ_HANDLED;
245 }
246
247 if (sisr & CCSR_SSI_SISR_TDE0) {
248 ssi_private->stats.tde0++;
249 ret = IRQ_HANDLED;
250 }
251
252 if (sisr & CCSR_SSI_SISR_ROE1) {
253 ssi_private->stats.roe1++;
254 sisr2 |= CCSR_SSI_SISR_ROE1;
255 ret = IRQ_HANDLED;
256 }
257
258 if (sisr & CCSR_SSI_SISR_ROE0) {
259 ssi_private->stats.roe0++;
260 sisr2 |= CCSR_SSI_SISR_ROE0;
261 ret = IRQ_HANDLED;
262 }
263
264 if (sisr & CCSR_SSI_SISR_TUE1) {
265 ssi_private->stats.tue1++;
266 sisr2 |= CCSR_SSI_SISR_TUE1;
267 ret = IRQ_HANDLED;
268 }
269
270 if (sisr & CCSR_SSI_SISR_TUE0) {
271 ssi_private->stats.tue0++;
272 sisr2 |= CCSR_SSI_SISR_TUE0;
273 ret = IRQ_HANDLED;
274 }
275
276 if (sisr & CCSR_SSI_SISR_TFS) {
277 ssi_private->stats.tfs++;
278 ret = IRQ_HANDLED;
279 }
280
281 if (sisr & CCSR_SSI_SISR_RFS) {
282 ssi_private->stats.rfs++;
283 ret = IRQ_HANDLED;
284 }
285
286 if (sisr & CCSR_SSI_SISR_TLS) {
287 ssi_private->stats.tls++;
288 ret = IRQ_HANDLED;
289 }
290
291 if (sisr & CCSR_SSI_SISR_RLS) {
292 ssi_private->stats.rls++;
293 ret = IRQ_HANDLED;
294 }
295
296 if (sisr & CCSR_SSI_SISR_RFF1) {
297 ssi_private->stats.rff1++;
298 ret = IRQ_HANDLED;
299 }
300
301 if (sisr & CCSR_SSI_SISR_RFF0) {
302 ssi_private->stats.rff0++;
303 ret = IRQ_HANDLED;
304 }
305
306 if (sisr & CCSR_SSI_SISR_TFE1) {
307 ssi_private->stats.tfe1++;
308 ret = IRQ_HANDLED;
309 }
310
311 if (sisr & CCSR_SSI_SISR_TFE0) {
312 ssi_private->stats.tfe0++;
313 ret = IRQ_HANDLED;
314 }
315
316 /* Clear the bits that we set */
317 if (sisr2)
Shawn Guodfa1a102012-03-16 16:56:42 +0800318 write_ssi(sisr2, &ssi->sisr);
Timur Tabi17467f22008-01-11 18:15:26 +0100319
320 return ret;
321}
322
323/**
324 * fsl_ssi_startup: create a new substream
325 *
326 * This is the first function called when a stream is opened.
327 *
328 * If this is the first stream open, then grab the IRQ and program most of
329 * the SSI registers.
330 */
Mark Browndee89c42008-11-18 22:11:38 +0000331static int fsl_ssi_startup(struct snd_pcm_substream *substream,
332 struct snd_soc_dai *dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100333{
334 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Timur Tabi5e538ec2011-09-13 12:59:37 -0500335 struct fsl_ssi_private *ssi_private =
336 snd_soc_dai_get_drvdata(rtd->cpu_dai);
337 int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
Timur Tabi17467f22008-01-11 18:15:26 +0100338
339 /*
340 * If this is the first stream opened, then request the IRQ
341 * and initialize the SSI registers.
342 */
Timur Tabi5e538ec2011-09-13 12:59:37 -0500343 if (!ssi_private->first_stream) {
Timur Tabi17467f22008-01-11 18:15:26 +0100344 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
Timur Tabi17467f22008-01-11 18:15:26 +0100345
Timur Tabi5e538ec2011-09-13 12:59:37 -0500346 ssi_private->first_stream = substream;
347
Timur Tabi17467f22008-01-11 18:15:26 +0100348 /*
349 * Section 16.5 of the MPC8610 reference manual says that the
350 * SSI needs to be disabled before updating the registers we set
351 * here.
352 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800353 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
Timur Tabi17467f22008-01-11 18:15:26 +0100354
355 /*
356 * Program the SSI into I2S Slave Non-Network Synchronous mode.
357 * Also enable the transmit and receive FIFO.
358 *
359 * FIXME: Little-endian samples require a different shift dir
360 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800361 write_ssi_mask(&ssi->scr,
Timur Tabia454dad2009-03-05 17:23:37 -0600362 CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
363 CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE
Timur Tabi5e538ec2011-09-13 12:59:37 -0500364 | (synchronous ? CCSR_SSI_SCR_SYN : 0));
Timur Tabi17467f22008-01-11 18:15:26 +0100365
Shawn Guodfa1a102012-03-16 16:56:42 +0800366 write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
Timur Tabi17467f22008-01-11 18:15:26 +0100367 CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
Shawn Guodfa1a102012-03-16 16:56:42 +0800368 CCSR_SSI_STCR_TSCKP, &ssi->stcr);
Timur Tabi17467f22008-01-11 18:15:26 +0100369
Shawn Guodfa1a102012-03-16 16:56:42 +0800370 write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
Timur Tabi17467f22008-01-11 18:15:26 +0100371 CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
Shawn Guodfa1a102012-03-16 16:56:42 +0800372 CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
Timur Tabi17467f22008-01-11 18:15:26 +0100373
374 /*
375 * The DC and PM bits are only used if the SSI is the clock
376 * master.
377 */
378
Timur Tabi5e538ec2011-09-13 12:59:37 -0500379 /* Enable the interrupts and DMA requests */
Markus Pargmannde623ec2013-07-27 13:31:53 +0200380 if (ssi_private->use_dma)
381 write_ssi(SIER_FLAGS, &ssi->sier);
382 else
383 write_ssi(CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN |
384 CCSR_SSI_SIER_RIE |
385 CCSR_SSI_SIER_RFF0_EN, &ssi->sier);
Timur Tabi17467f22008-01-11 18:15:26 +0100386
387 /*
388 * Set the watermark for transmit FIFI 0 and receive FIFO 0. We
Timur Tabi8e9d8692010-08-06 12:16:12 -0500389 * don't use FIFO 1. We program the transmit water to signal a
390 * DMA transfer if there are only two (or fewer) elements left
391 * in the FIFO. Two elements equals one frame (left channel,
392 * right channel). This value, however, depends on the depth of
393 * the transmit buffer.
394 *
395 * We program the receive FIFO to notify us if at least two
396 * elements (one frame) have been written to the FIFO. We could
397 * make this value larger (and maybe we should), but this way
398 * data will be written to memory as soon as it's available.
Timur Tabi17467f22008-01-11 18:15:26 +0100399 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800400 write_ssi(CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) |
401 CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2),
402 &ssi->sfcsr);
Timur Tabi17467f22008-01-11 18:15:26 +0100403
404 /*
405 * We keep the SSI disabled because if we enable it, then the
406 * DMA controller will start. It's not supposed to start until
407 * the SCR.TE (or SCR.RE) bit is set, but it does anyway. The
408 * DMA controller will transfer one "BWC" of data (i.e. the
409 * amount of data that the MR.BWC bits are set to). The reason
410 * this is bad is because at this point, the PCM driver has not
411 * finished initializing the DMA controller.
412 */
Timur Tabi5e538ec2011-09-13 12:59:37 -0500413 } else {
414 if (synchronous) {
415 struct snd_pcm_runtime *first_runtime =
416 ssi_private->first_stream->runtime;
417 /*
418 * This is the second stream open, and we're in
419 * synchronous mode, so we need to impose sample
420 * sample size constraints. This is because STCCR is
421 * used for playback and capture in synchronous mode,
422 * so there's no way to specify different word
423 * lengths.
424 *
425 * Note that this can cause a race condition if the
426 * second stream is opened before the first stream is
427 * fully initialized. We provide some protection by
428 * checking to make sure the first stream is
429 * initialized, but it's not perfect. ALSA sometimes
430 * re-initializes the driver with a different sample
431 * rate or size. If the second stream is opened
432 * before the first stream has received its final
433 * parameters, then the second stream may be
434 * constrained to the wrong sample rate or size.
435 */
436 if (!first_runtime->sample_bits) {
437 dev_err(substream->pcm->card->dev,
438 "set sample size in %s stream first\n",
439 substream->stream ==
440 SNDRV_PCM_STREAM_PLAYBACK
441 ? "capture" : "playback");
442 return -EAGAIN;
443 }
Timur Tabi17467f22008-01-11 18:15:26 +0100444
Timur Tabia454dad2009-03-05 17:23:37 -0600445 snd_pcm_hw_constraint_minmax(substream->runtime,
446 SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
447 first_runtime->sample_bits,
448 first_runtime->sample_bits);
Timur Tabi5e538ec2011-09-13 12:59:37 -0500449 }
Timur Tabibe41e942008-07-28 17:04:39 -0500450
451 ssi_private->second_stream = substream;
452 }
453
Timur Tabi17467f22008-01-11 18:15:26 +0100454 return 0;
455}
456
457/**
Timur Tabi85ef2372009-02-05 17:56:02 -0600458 * fsl_ssi_hw_params - program the sample size
Timur Tabi17467f22008-01-11 18:15:26 +0100459 *
460 * Most of the SSI registers have been programmed in the startup function,
461 * but the word length must be programmed here. Unfortunately, programming
462 * the SxCCR.WL bits requires the SSI to be temporarily disabled. This can
463 * cause a problem with supporting simultaneous playback and capture. If
464 * the SSI is already playing a stream, then that stream may be temporarily
465 * stopped when you start capture.
466 *
467 * Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the
468 * clock master.
469 */
Timur Tabi85ef2372009-02-05 17:56:02 -0600470static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
471 struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100472{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000473 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
Timur Tabi5e538ec2011-09-13 12:59:37 -0500474 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
475 unsigned int sample_size =
476 snd_pcm_format_width(params_format(hw_params));
477 u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
Shawn Guodfa1a102012-03-16 16:56:42 +0800478 int enabled = read_ssi(&ssi->scr) & CCSR_SSI_SCR_SSIEN;
Timur Tabi17467f22008-01-11 18:15:26 +0100479
Timur Tabi5e538ec2011-09-13 12:59:37 -0500480 /*
481 * If we're in synchronous mode, and the SSI is already enabled,
482 * then STCCR is already set properly.
483 */
484 if (enabled && ssi_private->cpu_dai_drv.symmetric_rates)
485 return 0;
Timur Tabi17467f22008-01-11 18:15:26 +0100486
Timur Tabi5e538ec2011-09-13 12:59:37 -0500487 /*
488 * FIXME: The documentation says that SxCCR[WL] should not be
489 * modified while the SSI is enabled. The only time this can
490 * happen is if we're trying to do simultaneous playback and
491 * capture in asynchronous mode. Unfortunately, I have been enable
492 * to get that to work at all on the P1022DS. Therefore, we don't
493 * bother to disable/enable the SSI when setting SxCCR[WL], because
494 * the SSI will stop anyway. Maybe one day, this will get fixed.
495 */
Timur Tabi17467f22008-01-11 18:15:26 +0100496
Timur Tabi5e538ec2011-09-13 12:59:37 -0500497 /* In synchronous mode, the SSI uses STCCR for capture */
498 if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
499 ssi_private->cpu_dai_drv.symmetric_rates)
Shawn Guodfa1a102012-03-16 16:56:42 +0800500 write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
Timur Tabi5e538ec2011-09-13 12:59:37 -0500501 else
Shawn Guodfa1a102012-03-16 16:56:42 +0800502 write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
Timur Tabi17467f22008-01-11 18:15:26 +0100503
504 return 0;
505}
506
507/**
508 * fsl_ssi_trigger: start and stop the DMA transfer.
509 *
510 * This function is called by ALSA to start, stop, pause, and resume the DMA
511 * transfer of data.
512 *
513 * The DMA channel is in external master start and pause mode, which
514 * means the SSI completely controls the flow of data.
515 */
Mark Browndee89c42008-11-18 22:11:38 +0000516static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
517 struct snd_soc_dai *dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100518{
519 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000520 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
Timur Tabi17467f22008-01-11 18:15:26 +0100521 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
522
523 switch (cmd) {
524 case SNDRV_PCM_TRIGGER_START:
Timur Tabi17467f22008-01-11 18:15:26 +0100525 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
Timur Tabia4d11fe2009-03-25 18:20:37 -0500526 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
Shawn Guodfa1a102012-03-16 16:56:42 +0800527 write_ssi_mask(&ssi->scr, 0,
Timur Tabibe41e942008-07-28 17:04:39 -0500528 CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE);
Timur Tabia4d11fe2009-03-25 18:20:37 -0500529 else
Shawn Guodfa1a102012-03-16 16:56:42 +0800530 write_ssi_mask(&ssi->scr, 0,
Timur Tabibe41e942008-07-28 17:04:39 -0500531 CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE);
Timur Tabi17467f22008-01-11 18:15:26 +0100532 break;
533
534 case SNDRV_PCM_TRIGGER_STOP:
Timur Tabi17467f22008-01-11 18:15:26 +0100535 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
536 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
Shawn Guodfa1a102012-03-16 16:56:42 +0800537 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TE, 0);
Timur Tabi17467f22008-01-11 18:15:26 +0100538 else
Shawn Guodfa1a102012-03-16 16:56:42 +0800539 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0);
Nicolin Chenb2c119b2013-07-10 18:43:54 +0800540
541 if ((read_ssi(&ssi->scr) & (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0)
542 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
Timur Tabi17467f22008-01-11 18:15:26 +0100543 break;
544
545 default:
546 return -EINVAL;
547 }
548
549 return 0;
550}
551
552/**
553 * fsl_ssi_shutdown: shutdown the SSI
554 *
555 * Shutdown the SSI if there are no other substreams open.
556 */
Mark Browndee89c42008-11-18 22:11:38 +0000557static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
558 struct snd_soc_dai *dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100559{
560 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000561 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
Timur Tabi17467f22008-01-11 18:15:26 +0100562
Timur Tabibe41e942008-07-28 17:04:39 -0500563 if (ssi_private->first_stream == substream)
564 ssi_private->first_stream = ssi_private->second_stream;
565
566 ssi_private->second_stream = NULL;
Timur Tabi17467f22008-01-11 18:15:26 +0100567}
568
Lars-Peter Clausenfc8ba7f2013-04-15 19:19:58 +0200569static int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
570{
571 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai);
572
Markus Pargmannde623ec2013-07-27 13:31:53 +0200573 if (ssi_private->ssi_on_imx && ssi_private->use_dma) {
Lars-Peter Clausenfc8ba7f2013-04-15 19:19:58 +0200574 dai->playback_dma_data = &ssi_private->dma_params_tx;
575 dai->capture_dma_data = &ssi_private->dma_params_rx;
576 }
577
578 return 0;
579}
580
Lars-Peter Clausen85e76522011-11-23 11:40:40 +0100581static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
Eric Miao6335d052009-03-03 09:41:00 +0800582 .startup = fsl_ssi_startup,
583 .hw_params = fsl_ssi_hw_params,
584 .shutdown = fsl_ssi_shutdown,
585 .trigger = fsl_ssi_trigger,
Eric Miao6335d052009-03-03 09:41:00 +0800586};
587
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000588/* Template for the CPU dai driver structure */
589static struct snd_soc_dai_driver fsl_ssi_dai_template = {
Lars-Peter Clausenfc8ba7f2013-04-15 19:19:58 +0200590 .probe = fsl_ssi_dai_probe,
Timur Tabi17467f22008-01-11 18:15:26 +0100591 .playback = {
592 /* The SSI does not support monaural audio. */
593 .channels_min = 2,
594 .channels_max = 2,
595 .rates = FSLSSI_I2S_RATES,
596 .formats = FSLSSI_I2S_FORMATS,
597 },
598 .capture = {
599 .channels_min = 2,
600 .channels_max = 2,
601 .rates = FSLSSI_I2S_RATES,
602 .formats = FSLSSI_I2S_FORMATS,
603 },
Eric Miao6335d052009-03-03 09:41:00 +0800604 .ops = &fsl_ssi_dai_ops,
Timur Tabi17467f22008-01-11 18:15:26 +0100605};
606
Kuninori Morimoto3580aa12013-03-21 03:32:04 -0700607static const struct snd_soc_component_driver fsl_ssi_component = {
608 .name = "fsl-ssi",
609};
610
Timur Tabid5a908b2009-03-26 11:42:38 -0500611/* Show the statistics of a flag only if its interrupt is enabled. The
612 * compiler will optimze this code to a no-op if the interrupt is not
613 * enabled.
614 */
615#define SIER_SHOW(flag, name) \
616 do { \
617 if (SIER_FLAGS & CCSR_SSI_SIER_##flag) \
618 length += sprintf(buf + length, #name "=%u\n", \
619 ssi_private->stats.name); \
620 } while (0)
621
622
Timur Tabi17467f22008-01-11 18:15:26 +0100623/**
624 * fsl_sysfs_ssi_show: display SSI statistics
625 *
Timur Tabid5a908b2009-03-26 11:42:38 -0500626 * Display the statistics for the current SSI device. To avoid confusion,
627 * we only show those counts that are enabled.
Timur Tabi17467f22008-01-11 18:15:26 +0100628 */
629static ssize_t fsl_sysfs_ssi_show(struct device *dev,
630 struct device_attribute *attr, char *buf)
631{
632 struct fsl_ssi_private *ssi_private =
Timur Tabid5a908b2009-03-26 11:42:38 -0500633 container_of(attr, struct fsl_ssi_private, dev_attr);
634 ssize_t length = 0;
Timur Tabi17467f22008-01-11 18:15:26 +0100635
Timur Tabid5a908b2009-03-26 11:42:38 -0500636 SIER_SHOW(RFRC_EN, rfrc);
637 SIER_SHOW(TFRC_EN, tfrc);
638 SIER_SHOW(CMDAU_EN, cmdau);
639 SIER_SHOW(CMDDU_EN, cmddu);
640 SIER_SHOW(RXT_EN, rxt);
641 SIER_SHOW(RDR1_EN, rdr1);
642 SIER_SHOW(RDR0_EN, rdr0);
643 SIER_SHOW(TDE1_EN, tde1);
644 SIER_SHOW(TDE0_EN, tde0);
645 SIER_SHOW(ROE1_EN, roe1);
646 SIER_SHOW(ROE0_EN, roe0);
647 SIER_SHOW(TUE1_EN, tue1);
648 SIER_SHOW(TUE0_EN, tue0);
649 SIER_SHOW(TFS_EN, tfs);
650 SIER_SHOW(RFS_EN, rfs);
651 SIER_SHOW(TLS_EN, tls);
652 SIER_SHOW(RLS_EN, rls);
653 SIER_SHOW(RFF1_EN, rff1);
654 SIER_SHOW(RFF0_EN, rff0);
655 SIER_SHOW(TFE1_EN, tfe1);
656 SIER_SHOW(TFE0_EN, tfe0);
Timur Tabi17467f22008-01-11 18:15:26 +0100657
658 return length;
659}
660
661/**
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000662 * Make every character in a string lower-case
Timur Tabi17467f22008-01-11 18:15:26 +0100663 */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000664static void make_lowercase(char *s)
Timur Tabi17467f22008-01-11 18:15:26 +0100665{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000666 char *p = s;
667 char c;
668
669 while ((c = *p)) {
670 if ((c >= 'A') && (c <= 'Z'))
671 *p = c + ('a' - 'A');
672 p++;
673 }
674}
675
Bill Pembertona0a3d512012-12-07 09:26:16 -0500676static int fsl_ssi_probe(struct platform_device *pdev)
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000677{
Timur Tabi17467f22008-01-11 18:15:26 +0100678 struct fsl_ssi_private *ssi_private;
679 int ret = 0;
Timur Tabi87a06322010-08-03 17:55:28 -0500680 struct device_attribute *dev_attr = NULL;
Timur Tabi38fec722010-08-19 15:26:58 -0500681 struct device_node *np = pdev->dev.of_node;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000682 const char *p, *sprop;
Timur Tabi8e9d8692010-08-06 12:16:12 -0500683 const uint32_t *iprop;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000684 struct resource res;
685 char name[64];
Lars-Peter Clausen312bb4f2013-03-22 14:12:12 +0100686 bool shared;
Timur Tabi17467f22008-01-11 18:15:26 +0100687
Timur Tabiff713342010-08-04 17:51:08 -0500688 /* SSIs that are not connected on the board should have a
689 * status = "disabled"
690 * property in their device tree nodes.
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000691 */
Timur Tabiff713342010-08-04 17:51:08 -0500692 if (!of_device_is_available(np))
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000693 return -ENODEV;
694
695 /* We only support the SSI in "I2S Slave" mode */
696 sprop = of_get_property(np, "fsl,mode", NULL);
697 if (!sprop || strcmp(sprop, "i2s-slave")) {
Timur Tabi38fec722010-08-19 15:26:58 -0500698 dev_notice(&pdev->dev, "mode %s is unsupported\n", sprop);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000699 return -ENODEV;
Timur Tabi17467f22008-01-11 18:15:26 +0100700 }
Timur Tabi17467f22008-01-11 18:15:26 +0100701
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000702 /* The DAI name is the last part of the full name of the node. */
703 p = strrchr(np->full_name, '/') + 1;
Fabio Estevamb0a47472013-07-17 02:00:38 -0300704 ssi_private = devm_kzalloc(&pdev->dev, sizeof(*ssi_private) + strlen(p),
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000705 GFP_KERNEL);
706 if (!ssi_private) {
Timur Tabi38fec722010-08-19 15:26:58 -0500707 dev_err(&pdev->dev, "could not allocate DAI object\n");
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000708 return -ENOMEM;
709 }
Timur Tabi17467f22008-01-11 18:15:26 +0100710
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000711 strcpy(ssi_private->name, p);
Timur Tabi17467f22008-01-11 18:15:26 +0100712
Markus Pargmannde623ec2013-07-27 13:31:53 +0200713 ssi_private->use_dma = !of_property_read_bool(np,
714 "fsl,fiq-stream-filter");
715
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000716 /* Initialize this copy of the CPU DAI driver structure */
717 memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
718 sizeof(fsl_ssi_dai_template));
719 ssi_private->cpu_dai_drv.name = ssi_private->name;
720
721 /* Get the addresses and IRQ */
722 ret = of_address_to_resource(np, 0, &res);
723 if (ret) {
Timur Tabi38fec722010-08-19 15:26:58 -0500724 dev_err(&pdev->dev, "could not determine device resources\n");
Fabio Estevamb0a47472013-07-17 02:00:38 -0300725 return ret;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000726 }
Timur Tabi147dfe92011-06-08 15:02:55 -0500727 ssi_private->ssi = of_iomap(np, 0);
728 if (!ssi_private->ssi) {
729 dev_err(&pdev->dev, "could not map device resources\n");
Fabio Estevamb0a47472013-07-17 02:00:38 -0300730 return -ENOMEM;
Timur Tabi147dfe92011-06-08 15:02:55 -0500731 }
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000732 ssi_private->ssi_phys = res.start;
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400733
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000734 ssi_private->irq = irq_of_parse_and_map(np, 0);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400735 if (ssi_private->irq == NO_IRQ) {
736 dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
Fabio Estevamb0a47472013-07-17 02:00:38 -0300737 return -ENXIO;
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400738 }
739
Markus Pargmannde623ec2013-07-27 13:31:53 +0200740 if (ssi_private->use_dma) {
741 /* The 'name' should not have any slashes in it. */
742 ret = devm_request_irq(&pdev->dev, ssi_private->irq,
743 fsl_ssi_isr, 0, ssi_private->name,
744 ssi_private);
745 if (ret < 0) {
746 dev_err(&pdev->dev, "could not claim irq %u\n",
747 ssi_private->irq);
748 goto error_irqmap;
749 }
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400750 }
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000751
752 /* Are the RX and the TX clocks locked? */
Timur Tabi5e538ec2011-09-13 12:59:37 -0500753 if (!of_find_property(np, "fsl,ssi-asynchronous", NULL))
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000754 ssi_private->cpu_dai_drv.symmetric_rates = 1;
Timur Tabi17467f22008-01-11 18:15:26 +0100755
Timur Tabi8e9d8692010-08-06 12:16:12 -0500756 /* Determine the FIFO depth. */
757 iprop = of_get_property(np, "fsl,fifo-depth", NULL);
758 if (iprop)
Timur Tabi147dfe92011-06-08 15:02:55 -0500759 ssi_private->fifo_depth = be32_to_cpup(iprop);
Timur Tabi8e9d8692010-08-06 12:16:12 -0500760 else
761 /* Older 8610 DTs didn't have the fifo-depth property */
762 ssi_private->fifo_depth = 8;
763
Shawn Guo09ce1112012-03-16 16:56:43 +0800764 if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx21-ssi")) {
765 u32 dma_events[2];
766 ssi_private->ssi_on_imx = true;
Shawn Guo95cd98f2012-03-29 10:53:41 +0800767
Fabio Estevamb0a47472013-07-17 02:00:38 -0300768 ssi_private->clk = devm_clk_get(&pdev->dev, NULL);
Shawn Guo95cd98f2012-03-29 10:53:41 +0800769 if (IS_ERR(ssi_private->clk)) {
770 ret = PTR_ERR(ssi_private->clk);
771 dev_err(&pdev->dev, "could not get clock: %d\n", ret);
Fabio Estevamb0a47472013-07-17 02:00:38 -0300772 goto error_irqmap;
Shawn Guo95cd98f2012-03-29 10:53:41 +0800773 }
Fabio Estevamede32d32013-07-17 02:00:39 -0300774 ret = clk_prepare_enable(ssi_private->clk);
775 if (ret) {
776 dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n",
777 ret);
778 goto error_irqmap;
779 }
Shawn Guo95cd98f2012-03-29 10:53:41 +0800780
Shawn Guo09ce1112012-03-16 16:56:43 +0800781 /*
782 * We have burstsize be "fifo_depth - 2" to match the SSI
783 * watermark setting in fsl_ssi_startup().
784 */
Lars-Peter Clausena8909c92013-04-03 11:06:04 +0200785 ssi_private->dma_params_tx.maxburst =
Shawn Guo09ce1112012-03-16 16:56:43 +0800786 ssi_private->fifo_depth - 2;
Lars-Peter Clausena8909c92013-04-03 11:06:04 +0200787 ssi_private->dma_params_rx.maxburst =
Shawn Guo09ce1112012-03-16 16:56:43 +0800788 ssi_private->fifo_depth - 2;
Lars-Peter Clausena8909c92013-04-03 11:06:04 +0200789 ssi_private->dma_params_tx.addr =
Shawn Guo09ce1112012-03-16 16:56:43 +0800790 ssi_private->ssi_phys + offsetof(struct ccsr_ssi, stx0);
Lars-Peter Clausena8909c92013-04-03 11:06:04 +0200791 ssi_private->dma_params_rx.addr =
Shawn Guo09ce1112012-03-16 16:56:43 +0800792 ssi_private->ssi_phys + offsetof(struct ccsr_ssi, srx0);
Lars-Peter Clausena8909c92013-04-03 11:06:04 +0200793 ssi_private->dma_params_tx.filter_data =
794 &ssi_private->filter_data_tx;
795 ssi_private->dma_params_rx.filter_data =
796 &ssi_private->filter_data_rx;
Shawn Guo09ce1112012-03-16 16:56:43 +0800797 /*
798 * TODO: This is a temporary solution and should be changed
799 * to use generic DMA binding later when the helplers get in.
800 */
801 ret = of_property_read_u32_array(pdev->dev.of_node,
802 "fsl,ssi-dma-events", dma_events, 2);
Markus Pargmannde623ec2013-07-27 13:31:53 +0200803 if (ret && !ssi_private->use_dma) {
Shawn Guo09ce1112012-03-16 16:56:43 +0800804 dev_err(&pdev->dev, "could not get dma events\n");
Shawn Guo95cd98f2012-03-29 10:53:41 +0800805 goto error_clk;
Shawn Guo09ce1112012-03-16 16:56:43 +0800806 }
Shawn Guob46b3732012-03-28 15:34:56 +0800807
Lars-Peter Clausen312bb4f2013-03-22 14:12:12 +0100808 shared = of_device_is_compatible(of_get_parent(np),
809 "fsl,spba-bus");
810
Lars-Peter Clausena8909c92013-04-03 11:06:04 +0200811 imx_pcm_dma_params_init_data(&ssi_private->filter_data_tx,
Nicolin Chen32bd8cd2013-07-25 17:41:41 +0800812 dma_events[0], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI);
Lars-Peter Clausena8909c92013-04-03 11:06:04 +0200813 imx_pcm_dma_params_init_data(&ssi_private->filter_data_rx,
Nicolin Chen32bd8cd2013-07-25 17:41:41 +0800814 dma_events[1], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI);
Shawn Guo09ce1112012-03-16 16:56:43 +0800815 }
816
Timur Tabi17467f22008-01-11 18:15:26 +0100817 /* Initialize the the device_attribute structure */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000818 dev_attr = &ssi_private->dev_attr;
Timur Tabi0f768a72011-11-14 16:35:26 -0600819 sysfs_attr_init(&dev_attr->attr);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000820 dev_attr->attr.name = "statistics";
Timur Tabi17467f22008-01-11 18:15:26 +0100821 dev_attr->attr.mode = S_IRUGO;
822 dev_attr->show = fsl_sysfs_ssi_show;
823
Timur Tabi38fec722010-08-19 15:26:58 -0500824 ret = device_create_file(&pdev->dev, dev_attr);
Timur Tabi17467f22008-01-11 18:15:26 +0100825 if (ret) {
Timur Tabi38fec722010-08-19 15:26:58 -0500826 dev_err(&pdev->dev, "could not create sysfs %s file\n",
Timur Tabi17467f22008-01-11 18:15:26 +0100827 ssi_private->dev_attr.attr.name);
Fabio Estevamb0a47472013-07-17 02:00:38 -0300828 goto error_clk;
Timur Tabi17467f22008-01-11 18:15:26 +0100829 }
830
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000831 /* Register with ASoC */
Timur Tabi38fec722010-08-19 15:26:58 -0500832 dev_set_drvdata(&pdev->dev, ssi_private);
Mark Brown3f4b7832008-12-03 19:26:35 +0000833
Kuninori Morimoto3580aa12013-03-21 03:32:04 -0700834 ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component,
835 &ssi_private->cpu_dai_drv, 1);
Timur Tabi87a06322010-08-03 17:55:28 -0500836 if (ret) {
Timur Tabi38fec722010-08-19 15:26:58 -0500837 dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400838 goto error_dev;
Mark Brown3f4b7832008-12-03 19:26:35 +0000839 }
Timur Tabi17467f22008-01-11 18:15:26 +0100840
Shawn Guo09ce1112012-03-16 16:56:43 +0800841 if (ssi_private->ssi_on_imx) {
Markus Pargmannde623ec2013-07-27 13:31:53 +0200842 if (!ssi_private->use_dma) {
843
844 /*
845 * Some boards use an incompatible codec. To get it
846 * working, we are using imx-fiq-pcm-audio, that
847 * can handle those codecs. DMA is not possible in this
848 * situation.
849 */
850
851 ssi_private->fiq_params.irq = ssi_private->irq;
852 ssi_private->fiq_params.base = ssi_private->ssi;
853 ssi_private->fiq_params.dma_params_rx =
854 &ssi_private->dma_params_rx;
855 ssi_private->fiq_params.dma_params_tx =
856 &ssi_private->dma_params_tx;
857
858 ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params);
859 if (ret)
860 goto error_dev;
861 } else {
862 ret = imx_pcm_dma_init(pdev);
863 if (ret)
864 goto error_dev;
865 }
Shawn Guo09ce1112012-03-16 16:56:43 +0800866 }
867
868 /*
869 * If codec-handle property is missing from SSI node, we assume
870 * that the machine driver uses new binding which does not require
871 * SSI driver to trigger machine driver's probe.
872 */
873 if (!of_get_property(np, "codec-handle", NULL)) {
874 ssi_private->new_binding = true;
875 goto done;
876 }
877
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000878 /* Trigger the machine driver's probe function. The platform driver
Shawn Guo2b81ec62012-03-09 00:59:46 +0800879 * name of the machine driver is taken from /compatible property of the
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000880 * device tree. We also pass the address of the CPU DAI driver
881 * structure.
882 */
Shawn Guo2b81ec62012-03-09 00:59:46 +0800883 sprop = of_get_property(of_find_node_by_path("/"), "compatible", NULL);
884 /* Sometimes the compatible name has a "fsl," prefix, so we strip it. */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000885 p = strrchr(sprop, ',');
886 if (p)
887 sprop = p + 1;
888 snprintf(name, sizeof(name), "snd-soc-%s", sprop);
889 make_lowercase(name);
890
891 ssi_private->pdev =
Timur Tabi38fec722010-08-19 15:26:58 -0500892 platform_device_register_data(&pdev->dev, name, 0, NULL, 0);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000893 if (IS_ERR(ssi_private->pdev)) {
894 ret = PTR_ERR(ssi_private->pdev);
Timur Tabi38fec722010-08-19 15:26:58 -0500895 dev_err(&pdev->dev, "failed to register platform: %d\n", ret);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400896 goto error_dai;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000897 }
898
Shawn Guo09ce1112012-03-16 16:56:43 +0800899done:
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000900 return 0;
Timur Tabi87a06322010-08-03 17:55:28 -0500901
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400902error_dai:
Shawn Guo09ce1112012-03-16 16:56:43 +0800903 if (ssi_private->ssi_on_imx)
Shawn Guobd41bc92013-04-25 11:18:46 +0800904 imx_pcm_dma_exit(pdev);
Kuninori Morimoto3580aa12013-03-21 03:32:04 -0700905 snd_soc_unregister_component(&pdev->dev);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400906
907error_dev:
Timur Tabi38fec722010-08-19 15:26:58 -0500908 dev_set_drvdata(&pdev->dev, NULL);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400909 device_remove_file(&pdev->dev, dev_attr);
910
Shawn Guo95cd98f2012-03-29 10:53:41 +0800911error_clk:
Fabio Estevamb0a47472013-07-17 02:00:38 -0300912 if (ssi_private->ssi_on_imx)
Shawn Guo95cd98f2012-03-29 10:53:41 +0800913 clk_disable_unprepare(ssi_private->clk);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400914
915error_irqmap:
Timur Tabi87a06322010-08-03 17:55:28 -0500916 irq_dispose_mapping(ssi_private->irq);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400917
Timur Tabi87a06322010-08-03 17:55:28 -0500918 return ret;
Timur Tabi17467f22008-01-11 18:15:26 +0100919}
Timur Tabi17467f22008-01-11 18:15:26 +0100920
Timur Tabi38fec722010-08-19 15:26:58 -0500921static int fsl_ssi_remove(struct platform_device *pdev)
Timur Tabi17467f22008-01-11 18:15:26 +0100922{
Timur Tabi38fec722010-08-19 15:26:58 -0500923 struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev);
Timur Tabi17467f22008-01-11 18:15:26 +0100924
Shawn Guo09ce1112012-03-16 16:56:43 +0800925 if (!ssi_private->new_binding)
926 platform_device_unregister(ssi_private->pdev);
Shawn Guo95cd98f2012-03-29 10:53:41 +0800927 if (ssi_private->ssi_on_imx) {
Shawn Guobd41bc92013-04-25 11:18:46 +0800928 imx_pcm_dma_exit(pdev);
Shawn Guo95cd98f2012-03-29 10:53:41 +0800929 clk_disable_unprepare(ssi_private->clk);
Shawn Guo95cd98f2012-03-29 10:53:41 +0800930 }
Kuninori Morimoto3580aa12013-03-21 03:32:04 -0700931 snd_soc_unregister_component(&pdev->dev);
Timur Tabi38fec722010-08-19 15:26:58 -0500932 device_remove_file(&pdev->dev, &ssi_private->dev_attr);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400933 irq_dispose_mapping(ssi_private->irq);
Timur Tabi38fec722010-08-19 15:26:58 -0500934 dev_set_drvdata(&pdev->dev, NULL);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000935
936 return 0;
Timur Tabi17467f22008-01-11 18:15:26 +0100937}
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000938
939static const struct of_device_id fsl_ssi_ids[] = {
940 { .compatible = "fsl,mpc8610-ssi", },
Shawn Guo09ce1112012-03-16 16:56:43 +0800941 { .compatible = "fsl,imx21-ssi", },
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000942 {}
943};
944MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
945
Grant Likelyf07eb222011-02-22 21:05:04 -0700946static struct platform_driver fsl_ssi_driver = {
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000947 .driver = {
948 .name = "fsl-ssi-dai",
949 .owner = THIS_MODULE,
950 .of_match_table = fsl_ssi_ids,
951 },
952 .probe = fsl_ssi_probe,
953 .remove = fsl_ssi_remove,
954};
Timur Tabi17467f22008-01-11 18:15:26 +0100955
Axel Linba0a7e02011-11-25 10:10:55 +0800956module_platform_driver(fsl_ssi_driver);
Timur Tabia454dad2009-03-05 17:23:37 -0600957
Fabio Estevamf3142802013-07-20 16:16:01 -0300958MODULE_ALIAS("platform:fsl-ssi-dai");
Timur Tabi17467f22008-01-11 18:15:26 +0100959MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
960MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver");
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000961MODULE_LICENSE("GPL v2");