blob: db9a734043857591c9652de63aa2084bfd7ea7ac [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"
31
Shawn Guodfa1a102012-03-16 16:56:42 +080032#ifdef PPC
33#define read_ssi(addr) in_be32(addr)
34#define write_ssi(val, addr) out_be32(addr, val)
35#define write_ssi_mask(addr, clear, set) clrsetbits_be32(addr, clear, set)
36#elif defined ARM
37#define read_ssi(addr) readl(addr)
38#define write_ssi(val, addr) writel(val, addr)
39/*
40 * FIXME: Proper locking should be added at write_ssi_mask caller level
41 * to ensure this register read/modify/write sequence is race free.
42 */
43static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set)
44{
45 u32 val = readl(addr);
46 val = (val & ~clear) | set;
47 writel(val, addr);
48}
49#endif
50
Timur Tabi17467f22008-01-11 18:15:26 +010051/**
52 * FSLSSI_I2S_RATES: sample rates supported by the I2S
53 *
54 * This driver currently only supports the SSI running in I2S slave mode,
55 * which means the codec determines the sample rate. Therefore, we tell
56 * ALSA that we support all rates and let the codec driver decide what rates
57 * are really supported.
58 */
59#define FSLSSI_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \
60 SNDRV_PCM_RATE_CONTINUOUS)
61
62/**
63 * FSLSSI_I2S_FORMATS: audio formats supported by the SSI
64 *
65 * This driver currently only supports the SSI running in I2S slave mode.
66 *
67 * The SSI has a limitation in that the samples must be in the same byte
68 * order as the host CPU. This is because when multiple bytes are written
69 * to the STX register, the bytes and bits must be written in the same
70 * order. The STX is a shift register, so all the bits need to be aligned
71 * (bit-endianness must match byte-endianness). Processors typically write
72 * the bits within a byte in the same order that the bytes of a word are
73 * written in. So if the host CPU is big-endian, then only big-endian
74 * samples will be written to STX properly.
75 */
76#ifdef __BIG_ENDIAN
77#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
78 SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_S20_3BE | \
79 SNDRV_PCM_FMTBIT_S24_3BE | SNDRV_PCM_FMTBIT_S24_BE)
80#else
81#define FSLSSI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
82 SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE | \
83 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE)
84#endif
85
Timur Tabid5a908b2009-03-26 11:42:38 -050086/* SIER bitflag of interrupts to enable */
87#define SIER_FLAGS (CCSR_SSI_SIER_TFRC_EN | CCSR_SSI_SIER_TDMAE | \
88 CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TUE0_EN | \
89 CCSR_SSI_SIER_TUE1_EN | CCSR_SSI_SIER_RFRC_EN | \
90 CCSR_SSI_SIER_RDMAE | CCSR_SSI_SIER_RIE | \
91 CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_ROE1_EN)
92
Timur Tabi17467f22008-01-11 18:15:26 +010093/**
94 * fsl_ssi_private: per-SSI private data
95 *
Timur Tabi17467f22008-01-11 18:15:26 +010096 * @ssi: pointer to the SSI's registers
97 * @ssi_phys: physical address of the SSI registers
98 * @irq: IRQ of this SSI
Timur Tabibe41e942008-07-28 17:04:39 -050099 * @first_stream: pointer to the stream that was opened first
100 * @second_stream: pointer to second stream
Timur Tabi17467f22008-01-11 18:15:26 +0100101 * @playback: the number of playback streams opened
102 * @capture: the number of capture streams opened
103 * @cpu_dai: the CPU DAI for this device
104 * @dev_attr: the sysfs device attribute structure
105 * @stats: SSI statistics
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000106 * @name: name for this device
Timur Tabi17467f22008-01-11 18:15:26 +0100107 */
108struct fsl_ssi_private {
Timur Tabi17467f22008-01-11 18:15:26 +0100109 struct ccsr_ssi __iomem *ssi;
110 dma_addr_t ssi_phys;
111 unsigned int irq;
Timur Tabibe41e942008-07-28 17:04:39 -0500112 struct snd_pcm_substream *first_stream;
113 struct snd_pcm_substream *second_stream;
Timur Tabi8e9d8692010-08-06 12:16:12 -0500114 unsigned int fifo_depth;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000115 struct snd_soc_dai_driver cpu_dai_drv;
Timur Tabi17467f22008-01-11 18:15:26 +0100116 struct device_attribute dev_attr;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000117 struct platform_device *pdev;
Timur Tabi17467f22008-01-11 18:15:26 +0100118
119 struct {
120 unsigned int rfrc;
121 unsigned int tfrc;
122 unsigned int cmdau;
123 unsigned int cmddu;
124 unsigned int rxt;
125 unsigned int rdr1;
126 unsigned int rdr0;
127 unsigned int tde1;
128 unsigned int tde0;
129 unsigned int roe1;
130 unsigned int roe0;
131 unsigned int tue1;
132 unsigned int tue0;
133 unsigned int tfs;
134 unsigned int rfs;
135 unsigned int tls;
136 unsigned int rls;
137 unsigned int rff1;
138 unsigned int rff0;
139 unsigned int tfe1;
140 unsigned int tfe0;
141 } stats;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000142
143 char name[1];
Timur Tabi17467f22008-01-11 18:15:26 +0100144};
145
146/**
147 * fsl_ssi_isr: SSI interrupt handler
148 *
149 * Although it's possible to use the interrupt handler to send and receive
150 * data to/from the SSI, we use the DMA instead. Programming is more
151 * complicated, but the performance is much better.
152 *
153 * This interrupt handler is used only to gather statistics.
154 *
155 * @irq: IRQ of the SSI device
156 * @dev_id: pointer to the ssi_private structure for this SSI device
157 */
158static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
159{
160 struct fsl_ssi_private *ssi_private = dev_id;
161 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
162 irqreturn_t ret = IRQ_NONE;
163 __be32 sisr;
164 __be32 sisr2 = 0;
165
166 /* We got an interrupt, so read the status register to see what we
167 were interrupted for. We mask it with the Interrupt Enable register
168 so that we only check for events that we're interested in.
169 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800170 sisr = read_ssi(&ssi->sisr) & SIER_FLAGS;
Timur Tabi17467f22008-01-11 18:15:26 +0100171
172 if (sisr & CCSR_SSI_SISR_RFRC) {
173 ssi_private->stats.rfrc++;
174 sisr2 |= CCSR_SSI_SISR_RFRC;
175 ret = IRQ_HANDLED;
176 }
177
178 if (sisr & CCSR_SSI_SISR_TFRC) {
179 ssi_private->stats.tfrc++;
180 sisr2 |= CCSR_SSI_SISR_TFRC;
181 ret = IRQ_HANDLED;
182 }
183
184 if (sisr & CCSR_SSI_SISR_CMDAU) {
185 ssi_private->stats.cmdau++;
186 ret = IRQ_HANDLED;
187 }
188
189 if (sisr & CCSR_SSI_SISR_CMDDU) {
190 ssi_private->stats.cmddu++;
191 ret = IRQ_HANDLED;
192 }
193
194 if (sisr & CCSR_SSI_SISR_RXT) {
195 ssi_private->stats.rxt++;
196 ret = IRQ_HANDLED;
197 }
198
199 if (sisr & CCSR_SSI_SISR_RDR1) {
200 ssi_private->stats.rdr1++;
201 ret = IRQ_HANDLED;
202 }
203
204 if (sisr & CCSR_SSI_SISR_RDR0) {
205 ssi_private->stats.rdr0++;
206 ret = IRQ_HANDLED;
207 }
208
209 if (sisr & CCSR_SSI_SISR_TDE1) {
210 ssi_private->stats.tde1++;
211 ret = IRQ_HANDLED;
212 }
213
214 if (sisr & CCSR_SSI_SISR_TDE0) {
215 ssi_private->stats.tde0++;
216 ret = IRQ_HANDLED;
217 }
218
219 if (sisr & CCSR_SSI_SISR_ROE1) {
220 ssi_private->stats.roe1++;
221 sisr2 |= CCSR_SSI_SISR_ROE1;
222 ret = IRQ_HANDLED;
223 }
224
225 if (sisr & CCSR_SSI_SISR_ROE0) {
226 ssi_private->stats.roe0++;
227 sisr2 |= CCSR_SSI_SISR_ROE0;
228 ret = IRQ_HANDLED;
229 }
230
231 if (sisr & CCSR_SSI_SISR_TUE1) {
232 ssi_private->stats.tue1++;
233 sisr2 |= CCSR_SSI_SISR_TUE1;
234 ret = IRQ_HANDLED;
235 }
236
237 if (sisr & CCSR_SSI_SISR_TUE0) {
238 ssi_private->stats.tue0++;
239 sisr2 |= CCSR_SSI_SISR_TUE0;
240 ret = IRQ_HANDLED;
241 }
242
243 if (sisr & CCSR_SSI_SISR_TFS) {
244 ssi_private->stats.tfs++;
245 ret = IRQ_HANDLED;
246 }
247
248 if (sisr & CCSR_SSI_SISR_RFS) {
249 ssi_private->stats.rfs++;
250 ret = IRQ_HANDLED;
251 }
252
253 if (sisr & CCSR_SSI_SISR_TLS) {
254 ssi_private->stats.tls++;
255 ret = IRQ_HANDLED;
256 }
257
258 if (sisr & CCSR_SSI_SISR_RLS) {
259 ssi_private->stats.rls++;
260 ret = IRQ_HANDLED;
261 }
262
263 if (sisr & CCSR_SSI_SISR_RFF1) {
264 ssi_private->stats.rff1++;
265 ret = IRQ_HANDLED;
266 }
267
268 if (sisr & CCSR_SSI_SISR_RFF0) {
269 ssi_private->stats.rff0++;
270 ret = IRQ_HANDLED;
271 }
272
273 if (sisr & CCSR_SSI_SISR_TFE1) {
274 ssi_private->stats.tfe1++;
275 ret = IRQ_HANDLED;
276 }
277
278 if (sisr & CCSR_SSI_SISR_TFE0) {
279 ssi_private->stats.tfe0++;
280 ret = IRQ_HANDLED;
281 }
282
283 /* Clear the bits that we set */
284 if (sisr2)
Shawn Guodfa1a102012-03-16 16:56:42 +0800285 write_ssi(sisr2, &ssi->sisr);
Timur Tabi17467f22008-01-11 18:15:26 +0100286
287 return ret;
288}
289
290/**
291 * fsl_ssi_startup: create a new substream
292 *
293 * This is the first function called when a stream is opened.
294 *
295 * If this is the first stream open, then grab the IRQ and program most of
296 * the SSI registers.
297 */
Mark Browndee89c42008-11-18 22:11:38 +0000298static int fsl_ssi_startup(struct snd_pcm_substream *substream,
299 struct snd_soc_dai *dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100300{
301 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Timur Tabi5e538ec2011-09-13 12:59:37 -0500302 struct fsl_ssi_private *ssi_private =
303 snd_soc_dai_get_drvdata(rtd->cpu_dai);
304 int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
Timur Tabi17467f22008-01-11 18:15:26 +0100305
306 /*
307 * If this is the first stream opened, then request the IRQ
308 * and initialize the SSI registers.
309 */
Timur Tabi5e538ec2011-09-13 12:59:37 -0500310 if (!ssi_private->first_stream) {
Timur Tabi17467f22008-01-11 18:15:26 +0100311 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
Timur Tabi17467f22008-01-11 18:15:26 +0100312
Timur Tabi5e538ec2011-09-13 12:59:37 -0500313 ssi_private->first_stream = substream;
314
Timur Tabi17467f22008-01-11 18:15:26 +0100315 /*
316 * Section 16.5 of the MPC8610 reference manual says that the
317 * SSI needs to be disabled before updating the registers we set
318 * here.
319 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800320 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
Timur Tabi17467f22008-01-11 18:15:26 +0100321
322 /*
323 * Program the SSI into I2S Slave Non-Network Synchronous mode.
324 * Also enable the transmit and receive FIFO.
325 *
326 * FIXME: Little-endian samples require a different shift dir
327 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800328 write_ssi_mask(&ssi->scr,
Timur Tabia454dad2009-03-05 17:23:37 -0600329 CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
330 CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE
Timur Tabi5e538ec2011-09-13 12:59:37 -0500331 | (synchronous ? CCSR_SSI_SCR_SYN : 0));
Timur Tabi17467f22008-01-11 18:15:26 +0100332
Shawn Guodfa1a102012-03-16 16:56:42 +0800333 write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
Timur Tabi17467f22008-01-11 18:15:26 +0100334 CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
Shawn Guodfa1a102012-03-16 16:56:42 +0800335 CCSR_SSI_STCR_TSCKP, &ssi->stcr);
Timur Tabi17467f22008-01-11 18:15:26 +0100336
Shawn Guodfa1a102012-03-16 16:56:42 +0800337 write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
Timur Tabi17467f22008-01-11 18:15:26 +0100338 CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
Shawn Guodfa1a102012-03-16 16:56:42 +0800339 CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
Timur Tabi17467f22008-01-11 18:15:26 +0100340
341 /*
342 * The DC and PM bits are only used if the SSI is the clock
343 * master.
344 */
345
Timur Tabi5e538ec2011-09-13 12:59:37 -0500346 /* Enable the interrupts and DMA requests */
Shawn Guodfa1a102012-03-16 16:56:42 +0800347 write_ssi(SIER_FLAGS, &ssi->sier);
Timur Tabi17467f22008-01-11 18:15:26 +0100348
349 /*
350 * Set the watermark for transmit FIFI 0 and receive FIFO 0. We
Timur Tabi8e9d8692010-08-06 12:16:12 -0500351 * don't use FIFO 1. We program the transmit water to signal a
352 * DMA transfer if there are only two (or fewer) elements left
353 * in the FIFO. Two elements equals one frame (left channel,
354 * right channel). This value, however, depends on the depth of
355 * the transmit buffer.
356 *
357 * We program the receive FIFO to notify us if at least two
358 * elements (one frame) have been written to the FIFO. We could
359 * make this value larger (and maybe we should), but this way
360 * data will be written to memory as soon as it's available.
Timur Tabi17467f22008-01-11 18:15:26 +0100361 */
Shawn Guodfa1a102012-03-16 16:56:42 +0800362 write_ssi(CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) |
363 CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2),
364 &ssi->sfcsr);
Timur Tabi17467f22008-01-11 18:15:26 +0100365
366 /*
367 * We keep the SSI disabled because if we enable it, then the
368 * DMA controller will start. It's not supposed to start until
369 * the SCR.TE (or SCR.RE) bit is set, but it does anyway. The
370 * DMA controller will transfer one "BWC" of data (i.e. the
371 * amount of data that the MR.BWC bits are set to). The reason
372 * this is bad is because at this point, the PCM driver has not
373 * finished initializing the DMA controller.
374 */
Timur Tabi5e538ec2011-09-13 12:59:37 -0500375 } else {
376 if (synchronous) {
377 struct snd_pcm_runtime *first_runtime =
378 ssi_private->first_stream->runtime;
379 /*
380 * This is the second stream open, and we're in
381 * synchronous mode, so we need to impose sample
382 * sample size constraints. This is because STCCR is
383 * used for playback and capture in synchronous mode,
384 * so there's no way to specify different word
385 * lengths.
386 *
387 * Note that this can cause a race condition if the
388 * second stream is opened before the first stream is
389 * fully initialized. We provide some protection by
390 * checking to make sure the first stream is
391 * initialized, but it's not perfect. ALSA sometimes
392 * re-initializes the driver with a different sample
393 * rate or size. If the second stream is opened
394 * before the first stream has received its final
395 * parameters, then the second stream may be
396 * constrained to the wrong sample rate or size.
397 */
398 if (!first_runtime->sample_bits) {
399 dev_err(substream->pcm->card->dev,
400 "set sample size in %s stream first\n",
401 substream->stream ==
402 SNDRV_PCM_STREAM_PLAYBACK
403 ? "capture" : "playback");
404 return -EAGAIN;
405 }
Timur Tabi17467f22008-01-11 18:15:26 +0100406
Timur Tabia454dad2009-03-05 17:23:37 -0600407 snd_pcm_hw_constraint_minmax(substream->runtime,
408 SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
409 first_runtime->sample_bits,
410 first_runtime->sample_bits);
Timur Tabi5e538ec2011-09-13 12:59:37 -0500411 }
Timur Tabibe41e942008-07-28 17:04:39 -0500412
413 ssi_private->second_stream = substream;
414 }
415
Timur Tabi17467f22008-01-11 18:15:26 +0100416 return 0;
417}
418
419/**
Timur Tabi85ef2372009-02-05 17:56:02 -0600420 * fsl_ssi_hw_params - program the sample size
Timur Tabi17467f22008-01-11 18:15:26 +0100421 *
422 * Most of the SSI registers have been programmed in the startup function,
423 * but the word length must be programmed here. Unfortunately, programming
424 * the SxCCR.WL bits requires the SSI to be temporarily disabled. This can
425 * cause a problem with supporting simultaneous playback and capture. If
426 * the SSI is already playing a stream, then that stream may be temporarily
427 * stopped when you start capture.
428 *
429 * Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the
430 * clock master.
431 */
Timur Tabi85ef2372009-02-05 17:56:02 -0600432static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
433 struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100434{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000435 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
Timur Tabi5e538ec2011-09-13 12:59:37 -0500436 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
437 unsigned int sample_size =
438 snd_pcm_format_width(params_format(hw_params));
439 u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
Shawn Guodfa1a102012-03-16 16:56:42 +0800440 int enabled = read_ssi(&ssi->scr) & CCSR_SSI_SCR_SSIEN;
Timur Tabi17467f22008-01-11 18:15:26 +0100441
Timur Tabi5e538ec2011-09-13 12:59:37 -0500442 /*
443 * If we're in synchronous mode, and the SSI is already enabled,
444 * then STCCR is already set properly.
445 */
446 if (enabled && ssi_private->cpu_dai_drv.symmetric_rates)
447 return 0;
Timur Tabi17467f22008-01-11 18:15:26 +0100448
Timur Tabi5e538ec2011-09-13 12:59:37 -0500449 /*
450 * FIXME: The documentation says that SxCCR[WL] should not be
451 * modified while the SSI is enabled. The only time this can
452 * happen is if we're trying to do simultaneous playback and
453 * capture in asynchronous mode. Unfortunately, I have been enable
454 * to get that to work at all on the P1022DS. Therefore, we don't
455 * bother to disable/enable the SSI when setting SxCCR[WL], because
456 * the SSI will stop anyway. Maybe one day, this will get fixed.
457 */
Timur Tabi17467f22008-01-11 18:15:26 +0100458
Timur Tabi5e538ec2011-09-13 12:59:37 -0500459 /* In synchronous mode, the SSI uses STCCR for capture */
460 if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
461 ssi_private->cpu_dai_drv.symmetric_rates)
Shawn Guodfa1a102012-03-16 16:56:42 +0800462 write_ssi_mask(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
Timur Tabi5e538ec2011-09-13 12:59:37 -0500463 else
Shawn Guodfa1a102012-03-16 16:56:42 +0800464 write_ssi_mask(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
Timur Tabi17467f22008-01-11 18:15:26 +0100465
466 return 0;
467}
468
469/**
470 * fsl_ssi_trigger: start and stop the DMA transfer.
471 *
472 * This function is called by ALSA to start, stop, pause, and resume the DMA
473 * transfer of data.
474 *
475 * The DMA channel is in external master start and pause mode, which
476 * means the SSI completely controls the flow of data.
477 */
Mark Browndee89c42008-11-18 22:11:38 +0000478static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
479 struct snd_soc_dai *dai)
Timur Tabi17467f22008-01-11 18:15:26 +0100480{
481 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000482 struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
Timur Tabi17467f22008-01-11 18:15:26 +0100483 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
484
485 switch (cmd) {
486 case SNDRV_PCM_TRIGGER_START:
Timur Tabi17467f22008-01-11 18:15:26 +0100487 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
Timur Tabia4d11fe2009-03-25 18:20:37 -0500488 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
Shawn Guodfa1a102012-03-16 16:56:42 +0800489 write_ssi_mask(&ssi->scr, 0,
Timur Tabibe41e942008-07-28 17:04:39 -0500490 CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE);
Timur Tabia4d11fe2009-03-25 18:20:37 -0500491 else
Shawn Guodfa1a102012-03-16 16:56:42 +0800492 write_ssi_mask(&ssi->scr, 0,
Timur Tabibe41e942008-07-28 17:04:39 -0500493 CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE);
Timur Tabi17467f22008-01-11 18:15:26 +0100494 break;
495
496 case SNDRV_PCM_TRIGGER_STOP:
Timur Tabi17467f22008-01-11 18:15:26 +0100497 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
498 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
Shawn Guodfa1a102012-03-16 16:56:42 +0800499 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TE, 0);
Timur Tabi17467f22008-01-11 18:15:26 +0100500 else
Shawn Guodfa1a102012-03-16 16:56:42 +0800501 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0);
Timur Tabi17467f22008-01-11 18:15:26 +0100502 break;
503
504 default:
505 return -EINVAL;
506 }
507
508 return 0;
509}
510
511/**
512 * fsl_ssi_shutdown: shutdown the SSI
513 *
514 * Shutdown the SSI if there are no other substreams open.
515 */
Mark Browndee89c42008-11-18 22:11:38 +0000516static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
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
Timur Tabibe41e942008-07-28 17:04:39 -0500522 if (ssi_private->first_stream == substream)
523 ssi_private->first_stream = ssi_private->second_stream;
524
525 ssi_private->second_stream = NULL;
526
Timur Tabi17467f22008-01-11 18:15:26 +0100527 /*
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400528 * If this is the last active substream, disable the SSI.
Timur Tabi17467f22008-01-11 18:15:26 +0100529 */
Timur Tabi5e538ec2011-09-13 12:59:37 -0500530 if (!ssi_private->first_stream) {
Timur Tabi17467f22008-01-11 18:15:26 +0100531 struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
532
Shawn Guodfa1a102012-03-16 16:56:42 +0800533 write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
Timur Tabi17467f22008-01-11 18:15:26 +0100534 }
535}
536
Lars-Peter Clausen85e76522011-11-23 11:40:40 +0100537static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
Eric Miao6335d052009-03-03 09:41:00 +0800538 .startup = fsl_ssi_startup,
539 .hw_params = fsl_ssi_hw_params,
540 .shutdown = fsl_ssi_shutdown,
541 .trigger = fsl_ssi_trigger,
Eric Miao6335d052009-03-03 09:41:00 +0800542};
543
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000544/* Template for the CPU dai driver structure */
545static struct snd_soc_dai_driver fsl_ssi_dai_template = {
Timur Tabi17467f22008-01-11 18:15:26 +0100546 .playback = {
547 /* The SSI does not support monaural audio. */
548 .channels_min = 2,
549 .channels_max = 2,
550 .rates = FSLSSI_I2S_RATES,
551 .formats = FSLSSI_I2S_FORMATS,
552 },
553 .capture = {
554 .channels_min = 2,
555 .channels_max = 2,
556 .rates = FSLSSI_I2S_RATES,
557 .formats = FSLSSI_I2S_FORMATS,
558 },
Eric Miao6335d052009-03-03 09:41:00 +0800559 .ops = &fsl_ssi_dai_ops,
Timur Tabi17467f22008-01-11 18:15:26 +0100560};
561
Timur Tabid5a908b2009-03-26 11:42:38 -0500562/* Show the statistics of a flag only if its interrupt is enabled. The
563 * compiler will optimze this code to a no-op if the interrupt is not
564 * enabled.
565 */
566#define SIER_SHOW(flag, name) \
567 do { \
568 if (SIER_FLAGS & CCSR_SSI_SIER_##flag) \
569 length += sprintf(buf + length, #name "=%u\n", \
570 ssi_private->stats.name); \
571 } while (0)
572
573
Timur Tabi17467f22008-01-11 18:15:26 +0100574/**
575 * fsl_sysfs_ssi_show: display SSI statistics
576 *
Timur Tabid5a908b2009-03-26 11:42:38 -0500577 * Display the statistics for the current SSI device. To avoid confusion,
578 * we only show those counts that are enabled.
Timur Tabi17467f22008-01-11 18:15:26 +0100579 */
580static ssize_t fsl_sysfs_ssi_show(struct device *dev,
581 struct device_attribute *attr, char *buf)
582{
583 struct fsl_ssi_private *ssi_private =
Timur Tabid5a908b2009-03-26 11:42:38 -0500584 container_of(attr, struct fsl_ssi_private, dev_attr);
585 ssize_t length = 0;
Timur Tabi17467f22008-01-11 18:15:26 +0100586
Timur Tabid5a908b2009-03-26 11:42:38 -0500587 SIER_SHOW(RFRC_EN, rfrc);
588 SIER_SHOW(TFRC_EN, tfrc);
589 SIER_SHOW(CMDAU_EN, cmdau);
590 SIER_SHOW(CMDDU_EN, cmddu);
591 SIER_SHOW(RXT_EN, rxt);
592 SIER_SHOW(RDR1_EN, rdr1);
593 SIER_SHOW(RDR0_EN, rdr0);
594 SIER_SHOW(TDE1_EN, tde1);
595 SIER_SHOW(TDE0_EN, tde0);
596 SIER_SHOW(ROE1_EN, roe1);
597 SIER_SHOW(ROE0_EN, roe0);
598 SIER_SHOW(TUE1_EN, tue1);
599 SIER_SHOW(TUE0_EN, tue0);
600 SIER_SHOW(TFS_EN, tfs);
601 SIER_SHOW(RFS_EN, rfs);
602 SIER_SHOW(TLS_EN, tls);
603 SIER_SHOW(RLS_EN, rls);
604 SIER_SHOW(RFF1_EN, rff1);
605 SIER_SHOW(RFF0_EN, rff0);
606 SIER_SHOW(TFE1_EN, tfe1);
607 SIER_SHOW(TFE0_EN, tfe0);
Timur Tabi17467f22008-01-11 18:15:26 +0100608
609 return length;
610}
611
612/**
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000613 * Make every character in a string lower-case
Timur Tabi17467f22008-01-11 18:15:26 +0100614 */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000615static void make_lowercase(char *s)
Timur Tabi17467f22008-01-11 18:15:26 +0100616{
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000617 char *p = s;
618 char c;
619
620 while ((c = *p)) {
621 if ((c >= 'A') && (c <= 'Z'))
622 *p = c + ('a' - 'A');
623 p++;
624 }
625}
626
Grant Likelyf07eb222011-02-22 21:05:04 -0700627static int __devinit fsl_ssi_probe(struct platform_device *pdev)
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000628{
Timur Tabi17467f22008-01-11 18:15:26 +0100629 struct fsl_ssi_private *ssi_private;
630 int ret = 0;
Timur Tabi87a06322010-08-03 17:55:28 -0500631 struct device_attribute *dev_attr = NULL;
Timur Tabi38fec722010-08-19 15:26:58 -0500632 struct device_node *np = pdev->dev.of_node;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000633 const char *p, *sprop;
Timur Tabi8e9d8692010-08-06 12:16:12 -0500634 const uint32_t *iprop;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000635 struct resource res;
636 char name[64];
Timur Tabi17467f22008-01-11 18:15:26 +0100637
Timur Tabiff713342010-08-04 17:51:08 -0500638 /* SSIs that are not connected on the board should have a
639 * status = "disabled"
640 * property in their device tree nodes.
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000641 */
Timur Tabiff713342010-08-04 17:51:08 -0500642 if (!of_device_is_available(np))
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000643 return -ENODEV;
644
Timur Tabiff713342010-08-04 17:51:08 -0500645 /* Check for a codec-handle property. */
646 if (!of_get_property(np, "codec-handle", NULL)) {
Timur Tabi38fec722010-08-19 15:26:58 -0500647 dev_err(&pdev->dev, "missing codec-handle property\n");
Timur Tabiff713342010-08-04 17:51:08 -0500648 return -ENODEV;
649 }
650
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000651 /* We only support the SSI in "I2S Slave" mode */
652 sprop = of_get_property(np, "fsl,mode", NULL);
653 if (!sprop || strcmp(sprop, "i2s-slave")) {
Timur Tabi38fec722010-08-19 15:26:58 -0500654 dev_notice(&pdev->dev, "mode %s is unsupported\n", sprop);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000655 return -ENODEV;
Timur Tabi17467f22008-01-11 18:15:26 +0100656 }
Timur Tabi17467f22008-01-11 18:15:26 +0100657
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000658 /* The DAI name is the last part of the full name of the node. */
659 p = strrchr(np->full_name, '/') + 1;
660 ssi_private = kzalloc(sizeof(struct fsl_ssi_private) + strlen(p),
661 GFP_KERNEL);
662 if (!ssi_private) {
Timur Tabi38fec722010-08-19 15:26:58 -0500663 dev_err(&pdev->dev, "could not allocate DAI object\n");
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000664 return -ENOMEM;
665 }
Timur Tabi17467f22008-01-11 18:15:26 +0100666
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000667 strcpy(ssi_private->name, p);
Timur Tabi17467f22008-01-11 18:15:26 +0100668
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000669 /* Initialize this copy of the CPU DAI driver structure */
670 memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
671 sizeof(fsl_ssi_dai_template));
672 ssi_private->cpu_dai_drv.name = ssi_private->name;
673
674 /* Get the addresses and IRQ */
675 ret = of_address_to_resource(np, 0, &res);
676 if (ret) {
Timur Tabi38fec722010-08-19 15:26:58 -0500677 dev_err(&pdev->dev, "could not determine device resources\n");
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400678 goto error_kmalloc;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000679 }
Timur Tabi147dfe92011-06-08 15:02:55 -0500680 ssi_private->ssi = of_iomap(np, 0);
681 if (!ssi_private->ssi) {
682 dev_err(&pdev->dev, "could not map device resources\n");
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400683 ret = -ENOMEM;
684 goto error_kmalloc;
Timur Tabi147dfe92011-06-08 15:02:55 -0500685 }
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000686 ssi_private->ssi_phys = res.start;
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400687
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000688 ssi_private->irq = irq_of_parse_and_map(np, 0);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400689 if (ssi_private->irq == NO_IRQ) {
690 dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
691 ret = -ENXIO;
692 goto error_iomap;
693 }
694
695 /* The 'name' should not have any slashes in it. */
696 ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0, ssi_private->name,
697 ssi_private);
698 if (ret < 0) {
699 dev_err(&pdev->dev, "could not claim irq %u\n", ssi_private->irq);
700 goto error_irqmap;
701 }
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000702
703 /* Are the RX and the TX clocks locked? */
Timur Tabi5e538ec2011-09-13 12:59:37 -0500704 if (!of_find_property(np, "fsl,ssi-asynchronous", NULL))
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000705 ssi_private->cpu_dai_drv.symmetric_rates = 1;
Timur Tabi17467f22008-01-11 18:15:26 +0100706
Timur Tabi8e9d8692010-08-06 12:16:12 -0500707 /* Determine the FIFO depth. */
708 iprop = of_get_property(np, "fsl,fifo-depth", NULL);
709 if (iprop)
Timur Tabi147dfe92011-06-08 15:02:55 -0500710 ssi_private->fifo_depth = be32_to_cpup(iprop);
Timur Tabi8e9d8692010-08-06 12:16:12 -0500711 else
712 /* Older 8610 DTs didn't have the fifo-depth property */
713 ssi_private->fifo_depth = 8;
714
Timur Tabi17467f22008-01-11 18:15:26 +0100715 /* Initialize the the device_attribute structure */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000716 dev_attr = &ssi_private->dev_attr;
Timur Tabi0f768a72011-11-14 16:35:26 -0600717 sysfs_attr_init(&dev_attr->attr);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000718 dev_attr->attr.name = "statistics";
Timur Tabi17467f22008-01-11 18:15:26 +0100719 dev_attr->attr.mode = S_IRUGO;
720 dev_attr->show = fsl_sysfs_ssi_show;
721
Timur Tabi38fec722010-08-19 15:26:58 -0500722 ret = device_create_file(&pdev->dev, dev_attr);
Timur Tabi17467f22008-01-11 18:15:26 +0100723 if (ret) {
Timur Tabi38fec722010-08-19 15:26:58 -0500724 dev_err(&pdev->dev, "could not create sysfs %s file\n",
Timur Tabi17467f22008-01-11 18:15:26 +0100725 ssi_private->dev_attr.attr.name);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400726 goto error_irq;
Timur Tabi17467f22008-01-11 18:15:26 +0100727 }
728
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000729 /* Register with ASoC */
Timur Tabi38fec722010-08-19 15:26:58 -0500730 dev_set_drvdata(&pdev->dev, ssi_private);
Mark Brown3f4b7832008-12-03 19:26:35 +0000731
Timur Tabi38fec722010-08-19 15:26:58 -0500732 ret = snd_soc_register_dai(&pdev->dev, &ssi_private->cpu_dai_drv);
Timur Tabi87a06322010-08-03 17:55:28 -0500733 if (ret) {
Timur Tabi38fec722010-08-19 15:26:58 -0500734 dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400735 goto error_dev;
Mark Brown3f4b7832008-12-03 19:26:35 +0000736 }
Timur Tabi17467f22008-01-11 18:15:26 +0100737
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000738 /* Trigger the machine driver's probe function. The platform driver
Shawn Guo2b81ec62012-03-09 00:59:46 +0800739 * name of the machine driver is taken from /compatible property of the
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000740 * device tree. We also pass the address of the CPU DAI driver
741 * structure.
742 */
Shawn Guo2b81ec62012-03-09 00:59:46 +0800743 sprop = of_get_property(of_find_node_by_path("/"), "compatible", NULL);
744 /* Sometimes the compatible name has a "fsl," prefix, so we strip it. */
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000745 p = strrchr(sprop, ',');
746 if (p)
747 sprop = p + 1;
748 snprintf(name, sizeof(name), "snd-soc-%s", sprop);
749 make_lowercase(name);
750
751 ssi_private->pdev =
Timur Tabi38fec722010-08-19 15:26:58 -0500752 platform_device_register_data(&pdev->dev, name, 0, NULL, 0);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000753 if (IS_ERR(ssi_private->pdev)) {
754 ret = PTR_ERR(ssi_private->pdev);
Timur Tabi38fec722010-08-19 15:26:58 -0500755 dev_err(&pdev->dev, "failed to register platform: %d\n", ret);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400756 goto error_dai;
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000757 }
758
759 return 0;
Timur Tabi87a06322010-08-03 17:55:28 -0500760
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400761error_dai:
Timur Tabi38fec722010-08-19 15:26:58 -0500762 snd_soc_unregister_dai(&pdev->dev);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400763
764error_dev:
Timur Tabi38fec722010-08-19 15:26:58 -0500765 dev_set_drvdata(&pdev->dev, NULL);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400766 device_remove_file(&pdev->dev, dev_attr);
767
768error_irq:
769 free_irq(ssi_private->irq, ssi_private);
770
771error_irqmap:
Timur Tabi87a06322010-08-03 17:55:28 -0500772 irq_dispose_mapping(ssi_private->irq);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400773
774error_iomap:
Timur Tabi87a06322010-08-03 17:55:28 -0500775 iounmap(ssi_private->ssi);
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400776
777error_kmalloc:
Timur Tabi87a06322010-08-03 17:55:28 -0500778 kfree(ssi_private);
779
780 return ret;
Timur Tabi17467f22008-01-11 18:15:26 +0100781}
Timur Tabi17467f22008-01-11 18:15:26 +0100782
Timur Tabi38fec722010-08-19 15:26:58 -0500783static int fsl_ssi_remove(struct platform_device *pdev)
Timur Tabi17467f22008-01-11 18:15:26 +0100784{
Timur Tabi38fec722010-08-19 15:26:58 -0500785 struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev);
Timur Tabi17467f22008-01-11 18:15:26 +0100786
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000787 platform_device_unregister(ssi_private->pdev);
Timur Tabi38fec722010-08-19 15:26:58 -0500788 snd_soc_unregister_dai(&pdev->dev);
789 device_remove_file(&pdev->dev, &ssi_private->dev_attr);
Mark Brown3f4b7832008-12-03 19:26:35 +0000790
Timur Tabi1fab6ca2011-08-16 18:47:45 -0400791 free_irq(ssi_private->irq, ssi_private);
792 irq_dispose_mapping(ssi_private->irq);
793
Timur Tabi17467f22008-01-11 18:15:26 +0100794 kfree(ssi_private);
Timur Tabi38fec722010-08-19 15:26:58 -0500795 dev_set_drvdata(&pdev->dev, NULL);
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000796
797 return 0;
Timur Tabi17467f22008-01-11 18:15:26 +0100798}
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000799
800static const struct of_device_id fsl_ssi_ids[] = {
801 { .compatible = "fsl,mpc8610-ssi", },
802 {}
803};
804MODULE_DEVICE_TABLE(of, fsl_ssi_ids);
805
Grant Likelyf07eb222011-02-22 21:05:04 -0700806static struct platform_driver fsl_ssi_driver = {
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000807 .driver = {
808 .name = "fsl-ssi-dai",
809 .owner = THIS_MODULE,
810 .of_match_table = fsl_ssi_ids,
811 },
812 .probe = fsl_ssi_probe,
813 .remove = fsl_ssi_remove,
814};
Timur Tabi17467f22008-01-11 18:15:26 +0100815
Axel Linba0a7e02011-11-25 10:10:55 +0800816module_platform_driver(fsl_ssi_driver);
Timur Tabia454dad2009-03-05 17:23:37 -0600817
Timur Tabi17467f22008-01-11 18:15:26 +0100818MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
819MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver");
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000820MODULE_LICENSE("GPL v2");