2 * mxc-ssi.c -- SSI driver for Freescale IMX
4 * Copyright 2006 Wolfson Microelectronics PLC.
5 * Author: Liam Girdwood
6 * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
8 * Based on mxc-alsa-mc13783 (C) 2006 Freescale.
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version.
16 * Need to rework SSI register defs when new defs go into mainline.
17 * Add support for TDM and FIFO 1.
18 * Add support for i.mx3x DMA interface.
23 #include <linux/module.h>
24 #include <linux/init.h>
25 #include <linux/platform_device.h>
26 #include <linux/slab.h>
27 #include <linux/dma-mapping.h>
28 #include <linux/clk.h>
29 #include <sound/core.h>
30 #include <sound/pcm.h>
31 #include <sound/pcm_params.h>
32 #include <sound/soc.h>
33 #include <mach/dma-mx1-mx2.h>
34 #include <asm/mach-types.h>
37 #include "mx1_mx2-pcm.h"
42 static int ssi_active
[2] = {0, 0};
44 /* DMA information for mx1_mx2 platforms */
45 static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_out0
= {
46 .name
= "SSI1 PCM Stereo out 0",
47 .transfer_type
= DMA_MODE_WRITE
,
48 .per_address
= SSI1_BASE_ADDR
+ STX0
,
49 .event_id
= DMA_REQ_SSI1_TX0
,
50 .watermark_level
= TXFIFO_WATERMARK
,
51 .per_config
= IMX_DMA_MEMSIZE_16
| IMX_DMA_TYPE_FIFO
,
52 .mem_config
= IMX_DMA_MEMSIZE_32
| IMX_DMA_TYPE_LINEAR
,
55 static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_out1
= {
56 .name
= "SSI1 PCM Stereo out 1",
57 .transfer_type
= DMA_MODE_WRITE
,
58 .per_address
= SSI1_BASE_ADDR
+ STX1
,
59 .event_id
= DMA_REQ_SSI1_TX1
,
60 .watermark_level
= TXFIFO_WATERMARK
,
61 .per_config
= IMX_DMA_MEMSIZE_16
| IMX_DMA_TYPE_FIFO
,
62 .mem_config
= IMX_DMA_MEMSIZE_32
| IMX_DMA_TYPE_LINEAR
,
65 static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_in0
= {
66 .name
= "SSI1 PCM Stereo in 0",
67 .transfer_type
= DMA_MODE_READ
,
68 .per_address
= SSI1_BASE_ADDR
+ SRX0
,
69 .event_id
= DMA_REQ_SSI1_RX0
,
70 .watermark_level
= RXFIFO_WATERMARK
,
71 .per_config
= IMX_DMA_MEMSIZE_16
| IMX_DMA_TYPE_FIFO
,
72 .mem_config
= IMX_DMA_MEMSIZE_32
| IMX_DMA_TYPE_LINEAR
,
75 static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_in1
= {
76 .name
= "SSI1 PCM Stereo in 1",
77 .transfer_type
= DMA_MODE_READ
,
78 .per_address
= SSI1_BASE_ADDR
+ SRX1
,
79 .event_id
= DMA_REQ_SSI1_RX1
,
80 .watermark_level
= RXFIFO_WATERMARK
,
81 .per_config
= IMX_DMA_MEMSIZE_16
| IMX_DMA_TYPE_FIFO
,
82 .mem_config
= IMX_DMA_MEMSIZE_32
| IMX_DMA_TYPE_LINEAR
,
85 static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_out0
= {
86 .name
= "SSI2 PCM Stereo out 0",
87 .transfer_type
= DMA_MODE_WRITE
,
88 .per_address
= SSI2_BASE_ADDR
+ STX0
,
89 .event_id
= DMA_REQ_SSI2_TX0
,
90 .watermark_level
= TXFIFO_WATERMARK
,
91 .per_config
= IMX_DMA_MEMSIZE_16
| IMX_DMA_TYPE_FIFO
,
92 .mem_config
= IMX_DMA_MEMSIZE_32
| IMX_DMA_TYPE_LINEAR
,
95 static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_out1
= {
96 .name
= "SSI2 PCM Stereo out 1",
97 .transfer_type
= DMA_MODE_WRITE
,
98 .per_address
= SSI2_BASE_ADDR
+ STX1
,
99 .event_id
= DMA_REQ_SSI2_TX1
,
100 .watermark_level
= TXFIFO_WATERMARK
,
101 .per_config
= IMX_DMA_MEMSIZE_16
| IMX_DMA_TYPE_FIFO
,
102 .mem_config
= IMX_DMA_MEMSIZE_32
| IMX_DMA_TYPE_LINEAR
,
105 static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_in0
= {
106 .name
= "SSI2 PCM Stereo in 0",
107 .transfer_type
= DMA_MODE_READ
,
108 .per_address
= SSI2_BASE_ADDR
+ SRX0
,
109 .event_id
= DMA_REQ_SSI2_RX0
,
110 .watermark_level
= RXFIFO_WATERMARK
,
111 .per_config
= IMX_DMA_MEMSIZE_16
| IMX_DMA_TYPE_FIFO
,
112 .mem_config
= IMX_DMA_MEMSIZE_32
| IMX_DMA_TYPE_LINEAR
,
115 static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_in1
= {
116 .name
= "SSI2 PCM Stereo in 1",
117 .transfer_type
= DMA_MODE_READ
,
118 .per_address
= SSI2_BASE_ADDR
+ SRX1
,
119 .event_id
= DMA_REQ_SSI2_RX1
,
120 .watermark_level
= RXFIFO_WATERMARK
,
121 .per_config
= IMX_DMA_MEMSIZE_16
| IMX_DMA_TYPE_FIFO
,
122 .mem_config
= IMX_DMA_MEMSIZE_32
| IMX_DMA_TYPE_LINEAR
,
125 static struct clk
*ssi_clk0
, *ssi_clk1
;
127 int get_ssi_clk(int ssi
, struct device
*dev
)
131 ssi_clk0
= clk_get(dev
, "ssi1");
132 if (IS_ERR(ssi_clk0
))
133 return PTR_ERR(ssi_clk0
);
136 ssi_clk1
= clk_get(dev
, "ssi2");
137 if (IS_ERR(ssi_clk1
))
138 return PTR_ERR(ssi_clk1
);
144 EXPORT_SYMBOL(get_ssi_clk
);
146 void put_ssi_clk(int ssi
)
159 EXPORT_SYMBOL(put_ssi_clk
);
162 * SSI system clock configuration.
163 * Should only be called when port is inactive (i.e. SSIEN = 0).
165 static int imx_ssi_set_dai_sysclk(struct snd_soc_dai
*cpu_dai
,
166 int clk_id
, unsigned int freq
, int dir
)
170 if (cpu_dai
->id
== IMX_DAI_SSI0
|| cpu_dai
->id
== IMX_DAI_SSI2
) {
172 pr_debug("%s: SCR for SSI1 is %x\n", __func__
, scr
);
175 pr_debug("%s: SCR for SSI2 is %x\n", __func__
, scr
);
178 if (scr
& SSI_SCR_SSIEN
) {
179 printk(KERN_WARNING
"Warning ssi already enabled\n");
184 case IMX_SSP_SYS_CLK
:
185 if (dir
== SND_SOC_CLOCK_OUT
) {
186 scr
|= SSI_SCR_SYS_CLK_EN
;
187 pr_debug("%s: clk of is output\n", __func__
);
189 scr
&= ~SSI_SCR_SYS_CLK_EN
;
190 pr_debug("%s: clk of is input\n", __func__
);
197 if (cpu_dai
->id
== IMX_DAI_SSI0
|| cpu_dai
->id
== IMX_DAI_SSI2
) {
198 pr_debug("%s: writeback of SSI1_SCR\n", __func__
);
201 pr_debug("%s: writeback of SSI2_SCR\n", __func__
);
210 * Should only be called when port is inactive (i.e. SSIEN = 0).
212 static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai
*cpu_dai
,
217 pr_debug("%s\n", __func__
);
218 if (cpu_dai
->id
== IMX_DAI_SSI0
|| cpu_dai
->id
== IMX_DAI_SSI2
) {
219 if (SSI1_SCR
& SSI_SCR_SSIEN
)
224 if (SSI2_SCR
& SSI_SCR_SSIEN
)
231 case IMX_SSI_TX_DIV_2
:
232 stccr
&= ~SSI_STCCR_DIV2
;
235 case IMX_SSI_TX_DIV_PSR
:
236 stccr
&= ~SSI_STCCR_PSR
;
239 case IMX_SSI_TX_DIV_PM
:
241 stccr
|= SSI_STCCR_PM(div
);
243 case IMX_SSI_RX_DIV_2
:
244 stccr
&= ~SSI_STCCR_DIV2
;
247 case IMX_SSI_RX_DIV_PSR
:
248 stccr
&= ~SSI_STCCR_PSR
;
251 case IMX_SSI_RX_DIV_PM
:
253 stccr
|= SSI_STCCR_PM(div
);
259 if (cpu_dai
->id
== IMX_DAI_SSI0
|| cpu_dai
->id
== IMX_DAI_SSI2
) {
270 * SSI Network Mode or TDM slots configuration.
271 * Should only be called when port is inactive (i.e. SSIEN = 0).
273 static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai
*cpu_dai
,
274 unsigned int mask
, int slots
)
276 u32 stmsk
, srmsk
, stccr
;
278 if (cpu_dai
->id
== IMX_DAI_SSI0
|| cpu_dai
->id
== IMX_DAI_SSI2
) {
279 if (SSI1_SCR
& SSI_SCR_SSIEN
) {
280 printk(KERN_WARNING
"Warning ssi already enabled\n");
285 if (SSI2_SCR
& SSI_SCR_SSIEN
) {
286 printk(KERN_WARNING
"Warning ssi already enabled\n");
292 stmsk
= srmsk
= mask
;
293 stccr
&= ~SSI_STCCR_DC_MASK
;
294 stccr
|= SSI_STCCR_DC(slots
- 1);
296 if (cpu_dai
->id
== IMX_DAI_SSI0
|| cpu_dai
->id
== IMX_DAI_SSI2
) {
299 SSI1_SRCCR
= SSI1_STCCR
= stccr
;
303 SSI2_SRCCR
= SSI2_STCCR
= stccr
;
310 * SSI DAI format configuration.
311 * Should only be called when port is inactive (i.e. SSIEN = 0).
312 * Note: We don't use the I2S modes but instead manually configure the
315 static int imx_ssi_set_dai_fmt(struct snd_soc_dai
*cpu_dai
,
318 u32 stcr
= 0, srcr
= 0, scr
;
321 * This is done to avoid this function to modify
322 * previous set values in stcr
326 if (cpu_dai
->id
== IMX_DAI_SSI0
|| cpu_dai
->id
== IMX_DAI_SSI2
)
327 scr
= SSI1_SCR
& ~(SSI_SCR_SYN
| SSI_SCR_NET
);
329 scr
= SSI2_SCR
& ~(SSI_SCR_SYN
| SSI_SCR_NET
);
331 if (scr
& SSI_SCR_SSIEN
) {
332 printk(KERN_WARNING
"Warning ssi already enabled\n");
337 switch (fmt
& SND_SOC_DAIFMT_FORMAT_MASK
) {
338 case SND_SOC_DAIFMT_I2S
:
339 /* data on rising edge of bclk, frame low 1clk before data */
340 stcr
|= SSI_STCR_TFSI
| SSI_STCR_TEFS
| SSI_STCR_TXBIT0
;
341 srcr
|= SSI_SRCR_RFSI
| SSI_SRCR_REFS
| SSI_SRCR_RXBIT0
;
343 case SND_SOC_DAIFMT_LEFT_J
:
344 /* data on rising edge of bclk, frame high with data */
345 stcr
|= SSI_STCR_TXBIT0
;
346 srcr
|= SSI_SRCR_RXBIT0
;
348 case SND_SOC_DAIFMT_DSP_B
:
349 /* data on rising edge of bclk, frame high with data */
350 stcr
|= SSI_STCR_TFSL
;
351 srcr
|= SSI_SRCR_RFSL
;
353 case SND_SOC_DAIFMT_DSP_A
:
354 /* data on rising edge of bclk, frame high 1clk before data */
355 stcr
|= SSI_STCR_TFSL
| SSI_STCR_TEFS
;
356 srcr
|= SSI_SRCR_RFSL
| SSI_SRCR_REFS
;
360 /* DAI clock inversion */
361 switch (fmt
& SND_SOC_DAIFMT_INV_MASK
) {
362 case SND_SOC_DAIFMT_IB_IF
:
363 stcr
|= SSI_STCR_TFSI
;
364 stcr
&= ~SSI_STCR_TSCKP
;
365 srcr
|= SSI_SRCR_RFSI
;
366 srcr
&= ~SSI_SRCR_RSCKP
;
368 case SND_SOC_DAIFMT_IB_NF
:
369 stcr
&= ~(SSI_STCR_TSCKP
| SSI_STCR_TFSI
);
370 srcr
&= ~(SSI_SRCR_RSCKP
| SSI_SRCR_RFSI
);
372 case SND_SOC_DAIFMT_NB_IF
:
373 stcr
|= SSI_STCR_TFSI
| SSI_STCR_TSCKP
;
374 srcr
|= SSI_SRCR_RFSI
| SSI_SRCR_RSCKP
;
376 case SND_SOC_DAIFMT_NB_NF
:
377 stcr
&= ~SSI_STCR_TFSI
;
378 stcr
|= SSI_STCR_TSCKP
;
379 srcr
&= ~SSI_SRCR_RFSI
;
380 srcr
|= SSI_SRCR_RSCKP
;
384 /* DAI clock master masks */
385 switch (fmt
& SND_SOC_DAIFMT_MASTER_MASK
) {
386 case SND_SOC_DAIFMT_CBS_CFS
:
387 stcr
|= SSI_STCR_TFDIR
| SSI_STCR_TXDIR
;
388 srcr
|= SSI_SRCR_RFDIR
| SSI_SRCR_RXDIR
;
390 case SND_SOC_DAIFMT_CBM_CFS
:
391 stcr
|= SSI_STCR_TFDIR
;
392 srcr
|= SSI_SRCR_RFDIR
;
394 case SND_SOC_DAIFMT_CBS_CFM
:
395 stcr
|= SSI_STCR_TXDIR
;
396 srcr
|= SSI_SRCR_RXDIR
;
400 if (cpu_dai
->id
== IMX_DAI_SSI0
|| cpu_dai
->id
== IMX_DAI_SSI2
) {
413 static int imx_ssi_startup(struct snd_pcm_substream
*substream
,
414 struct snd_soc_dai
*dai
)
416 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
417 struct snd_soc_dai
*cpu_dai
= rtd
->dai
->cpu_dai
;
419 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
) {
420 /* set up TX DMA params */
421 switch (cpu_dai
->id
) {
423 cpu_dai
->dma_data
= &imx_ssi1_pcm_stereo_out0
;
426 cpu_dai
->dma_data
= &imx_ssi1_pcm_stereo_out1
;
429 cpu_dai
->dma_data
= &imx_ssi2_pcm_stereo_out0
;
432 cpu_dai
->dma_data
= &imx_ssi2_pcm_stereo_out1
;
434 pr_debug("%s: (playback)\n", __func__
);
436 /* set up RX DMA params */
437 switch (cpu_dai
->id
) {
439 cpu_dai
->dma_data
= &imx_ssi1_pcm_stereo_in0
;
442 cpu_dai
->dma_data
= &imx_ssi1_pcm_stereo_in1
;
445 cpu_dai
->dma_data
= &imx_ssi2_pcm_stereo_in0
;
448 cpu_dai
->dma_data
= &imx_ssi2_pcm_stereo_in1
;
450 pr_debug("%s: (capture)\n", __func__
);
454 * we cant really change any SSI values after SSI is enabled
455 * need to fix in software for max flexibility - lrg
457 if (cpu_dai
->active
) {
458 printk(KERN_WARNING
"Warning ssi already enabled\n");
462 /* reset the SSI port - Sect 45.4.4 */
463 if (cpu_dai
->id
== IMX_DAI_SSI0
|| cpu_dai
->id
== IMX_DAI_SSI2
) {
468 if (ssi_active
[SSI1_PORT
]++) {
469 pr_debug("%s: exit before reset\n", __func__
);
476 SSI1_SFCSR
= SSI_SFCSR_RFWM1(RXFIFO_WATERMARK
) |
477 SSI_SFCSR_RFWM0(RXFIFO_WATERMARK
) |
478 SSI_SFCSR_TFWM1(TXFIFO_WATERMARK
) |
479 SSI_SFCSR_TFWM0(TXFIFO_WATERMARK
);
485 if (ssi_active
[SSI2_PORT
]++) {
486 pr_debug("%s: exit before reset\n", __func__
);
493 SSI2_SFCSR
= SSI_SFCSR_RFWM1(RXFIFO_WATERMARK
) |
494 SSI_SFCSR_RFWM0(RXFIFO_WATERMARK
) |
495 SSI_SFCSR_TFWM1(TXFIFO_WATERMARK
) |
496 SSI_SFCSR_TFWM0(TXFIFO_WATERMARK
);
502 int imx_ssi_hw_tx_params(struct snd_pcm_substream
*substream
,
503 struct snd_pcm_hw_params
*params
)
505 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
506 struct snd_soc_dai
*cpu_dai
= rtd
->dai
->cpu_dai
;
507 u32 stccr
, stcr
, sier
;
509 pr_debug("%s\n", __func__
);
511 if (cpu_dai
->id
== IMX_DAI_SSI0
|| cpu_dai
->id
== IMX_DAI_SSI2
) {
512 stccr
= SSI1_STCCR
& ~SSI_STCCR_WL_MASK
;
516 stccr
= SSI2_STCCR
& ~SSI_STCCR_WL_MASK
;
521 /* DAI data (word) size */
522 switch (params_format(params
)) {
523 case SNDRV_PCM_FORMAT_S16_LE
:
524 stccr
|= SSI_STCCR_WL(16);
526 case SNDRV_PCM_FORMAT_S20_3LE
:
527 stccr
|= SSI_STCCR_WL(20);
529 case SNDRV_PCM_FORMAT_S24_LE
:
530 stccr
|= SSI_STCCR_WL(24);
534 /* enable interrupts */
535 if (cpu_dai
->id
== IMX_DAI_SSI0
|| cpu_dai
->id
== IMX_DAI_SSI2
)
536 stcr
|= SSI_STCR_TFEN0
;
538 stcr
|= SSI_STCR_TFEN1
;
539 sier
|= SSI_SIER_TDMAE
;
541 if (cpu_dai
->id
== IMX_DAI_SSI0
|| cpu_dai
->id
== IMX_DAI_SSI2
) {
554 int imx_ssi_hw_rx_params(struct snd_pcm_substream
*substream
,
555 struct snd_pcm_hw_params
*params
)
557 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
558 struct snd_soc_dai
*cpu_dai
= rtd
->dai
->cpu_dai
;
559 u32 srccr
, srcr
, sier
;
561 pr_debug("%s\n", __func__
);
563 if (cpu_dai
->id
== IMX_DAI_SSI0
|| cpu_dai
->id
== IMX_DAI_SSI2
) {
564 srccr
= SSI1_SRCCR
& ~SSI_SRCCR_WL_MASK
;
568 srccr
= SSI2_SRCCR
& ~SSI_SRCCR_WL_MASK
;
573 /* DAI data (word) size */
574 switch (params_format(params
)) {
575 case SNDRV_PCM_FORMAT_S16_LE
:
576 srccr
|= SSI_SRCCR_WL(16);
578 case SNDRV_PCM_FORMAT_S20_3LE
:
579 srccr
|= SSI_SRCCR_WL(20);
581 case SNDRV_PCM_FORMAT_S24_LE
:
582 srccr
|= SSI_SRCCR_WL(24);
586 /* enable interrupts */
587 if (cpu_dai
->id
== IMX_DAI_SSI0
|| cpu_dai
->id
== IMX_DAI_SSI2
)
588 srcr
|= SSI_SRCR_RFEN0
;
590 srcr
|= SSI_SRCR_RFEN1
;
591 sier
|= SSI_SIER_RDMAE
;
593 if (cpu_dai
->id
== IMX_DAI_SSI0
|| cpu_dai
->id
== IMX_DAI_SSI2
) {
607 * Should only be called when port is inactive (i.e. SSIEN = 0),
608 * although can be called multiple times by upper layers.
610 int imx_ssi_hw_params(struct snd_pcm_substream
*substream
,
611 struct snd_pcm_hw_params
*params
,
612 struct snd_soc_dai
*dai
)
614 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
615 struct snd_soc_dai
*cpu_dai
= rtd
->dai
->cpu_dai
;
619 /* cant change any parameters when SSI is running */
620 if (cpu_dai
->id
== IMX_DAI_SSI0
|| cpu_dai
->id
== IMX_DAI_SSI2
) {
621 if (SSI1_SCR
& SSI_SCR_SSIEN
) {
622 printk(KERN_WARNING
"Warning ssi already enabled\n");
626 if (SSI2_SCR
& SSI_SCR_SSIEN
) {
627 printk(KERN_WARNING
"Warning ssi already enabled\n");
633 * Configure both tx and rx params with the same settings. This is
634 * really a harware restriction because SSI must be disabled until
635 * we can change those values. If there is an active audio stream in
636 * one direction, enabling the other direction with different
637 * settings would mean disturbing the running one.
639 ret
= imx_ssi_hw_tx_params(substream
, params
);
642 return imx_ssi_hw_rx_params(substream
, params
);
645 int imx_ssi_prepare(struct snd_pcm_substream
*substream
,
646 struct snd_soc_dai
*dai
)
648 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
649 struct snd_soc_dai
*cpu_dai
= rtd
->dai
->cpu_dai
;
652 pr_debug("%s\n", __func__
);
654 /* Enable clks here to follow SSI recommended init sequence */
655 if (cpu_dai
->id
== IMX_DAI_SSI0
|| cpu_dai
->id
== IMX_DAI_SSI2
) {
656 ret
= clk_enable(ssi_clk0
);
658 printk(KERN_ERR
"Unable to enable ssi_clk0\n");
660 ret
= clk_enable(ssi_clk1
);
662 printk(KERN_ERR
"Unable to enable ssi_clk1\n");
668 static int imx_ssi_trigger(struct snd_pcm_substream
*substream
, int cmd
,
669 struct snd_soc_dai
*dai
)
671 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
672 struct snd_soc_dai
*cpu_dai
= rtd
->dai
->cpu_dai
;
675 if (cpu_dai
->id
== IMX_DAI_SSI0
|| cpu_dai
->id
== IMX_DAI_SSI2
)
681 case SNDRV_PCM_TRIGGER_START
:
682 case SNDRV_PCM_TRIGGER_RESUME
:
683 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
684 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
685 scr
|= SSI_SCR_TE
| SSI_SCR_SSIEN
;
687 scr
|= SSI_SCR_RE
| SSI_SCR_SSIEN
;
689 case SNDRV_PCM_TRIGGER_SUSPEND
:
690 case SNDRV_PCM_TRIGGER_STOP
:
691 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
692 if (substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
)
701 if (cpu_dai
->id
== IMX_DAI_SSI0
|| cpu_dai
->id
== IMX_DAI_SSI2
)
709 static void imx_ssi_shutdown(struct snd_pcm_substream
*substream
,
710 struct snd_soc_dai
*dai
)
712 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
713 struct snd_soc_dai
*cpu_dai
= rtd
->dai
->cpu_dai
;
715 /* shutdown SSI if neither Tx or Rx is active */
716 if (!cpu_dai
->active
) {
718 if (cpu_dai
->id
== IMX_DAI_SSI0
||
719 cpu_dai
->id
== IMX_DAI_SSI2
) {
721 if (--ssi_active
[SSI1_PORT
] > 1)
725 clk_disable(ssi_clk0
);
727 if (--ssi_active
[SSI2_PORT
])
730 clk_disable(ssi_clk1
);
736 static int imx_ssi_suspend(struct platform_device
*dev
,
737 struct snd_soc_dai
*dai
)
742 static int imx_ssi_resume(struct platform_device
*pdev
,
743 struct snd_soc_dai
*dai
)
749 #define imx_ssi_suspend NULL
750 #define imx_ssi_resume NULL
753 #define IMX_SSI_RATES \
754 (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
755 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
756 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
757 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
758 SNDRV_PCM_RATE_96000)
760 #define IMX_SSI_BITS \
761 (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
762 SNDRV_PCM_FMTBIT_S24_LE)
764 static struct snd_soc_dai_ops imx_ssi_pcm_dai_ops
= {
765 .startup
= imx_ssi_startup
,
766 .shutdown
= imx_ssi_shutdown
,
767 .trigger
= imx_ssi_trigger
,
768 .prepare
= imx_ssi_prepare
,
769 .hw_params
= imx_ssi_hw_params
,
770 .set_sysclk
= imx_ssi_set_dai_sysclk
,
771 .set_clkdiv
= imx_ssi_set_dai_clkdiv
,
772 .set_fmt
= imx_ssi_set_dai_fmt
,
773 .set_tdm_slot
= imx_ssi_set_dai_tdm_slot
,
776 struct snd_soc_dai imx_ssi_pcm_dai
[] = {
778 .name
= "imx-i2s-1-0",
780 .suspend
= imx_ssi_suspend
,
781 .resume
= imx_ssi_resume
,
785 .formats
= IMX_SSI_BITS
,
786 .rates
= IMX_SSI_RATES
,},
790 .formats
= IMX_SSI_BITS
,
791 .rates
= IMX_SSI_RATES
,},
792 .ops
= &imx_ssi_pcm_dai_ops
,
795 .name
= "imx-i2s-2-0",
800 .formats
= IMX_SSI_BITS
,
801 .rates
= IMX_SSI_RATES
,},
805 .formats
= IMX_SSI_BITS
,
806 .rates
= IMX_SSI_RATES
,},
807 .ops
= &imx_ssi_pcm_dai_ops
,
810 .name
= "imx-i2s-1-1",
812 .suspend
= imx_ssi_suspend
,
813 .resume
= imx_ssi_resume
,
817 .formats
= IMX_SSI_BITS
,
818 .rates
= IMX_SSI_RATES
,},
822 .formats
= IMX_SSI_BITS
,
823 .rates
= IMX_SSI_RATES
,},
824 .ops
= &imx_ssi_pcm_dai_ops
,
827 .name
= "imx-i2s-2-1",
832 .formats
= IMX_SSI_BITS
,
833 .rates
= IMX_SSI_RATES
,},
837 .formats
= IMX_SSI_BITS
,
838 .rates
= IMX_SSI_RATES
,},
839 .ops
= &imx_ssi_pcm_dai_ops
,
842 EXPORT_SYMBOL_GPL(imx_ssi_pcm_dai
);
844 static int __init
imx_ssi_init(void)
846 return snd_soc_register_dais(imx_ssi_pcm_dai
,
847 ARRAY_SIZE(imx_ssi_pcm_dai
));
850 static void __exit
imx_ssi_exit(void)
852 snd_soc_unregister_dais(imx_ssi_pcm_dai
,
853 ARRAY_SIZE(imx_ssi_pcm_dai
));
856 module_init(imx_ssi_init
);
857 module_exit(imx_ssi_exit
);
858 MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com");
859 MODULE_DESCRIPTION("i.MX ASoC I2S driver");
860 MODULE_LICENSE("GPL");