2 * File: sound/soc/blackfin/bf5xx-i2s.c
3 * Author: Cliff Cai <Cliff.Cai@analog.com>
5 * Created: Tue June 06 2008
6 * Description: Blackfin I2S CPU DAI driver
9 * Copyright 2008 Analog Devices Inc.
11 * Bugs: Enter bugs at http://blackfin.uclinux.org/
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see the file COPYING, or write
25 * to the Free Software Foundation, Inc.,
26 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29 #include <linux/init.h>
30 #include <linux/module.h>
31 #include <linux/device.h>
32 #include <linux/delay.h>
33 #include <sound/core.h>
34 #include <sound/pcm.h>
35 #include <sound/pcm_params.h>
36 #include <sound/initval.h>
37 #include <sound/soc.h>
40 #include <asm/portmux.h>
41 #include <linux/mutex.h>
42 #include <linux/gpio.h>
44 #include "bf5xx-sport.h"
46 struct bf5xx_i2s_port
{
54 static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai
*cpu_dai
,
57 struct sport_device
*sport_handle
= snd_soc_dai_get_drvdata(cpu_dai
);
58 struct bf5xx_i2s_port
*bf5xx_i2s
= sport_handle
->private_data
;
61 /* interface format:support I2S,slave mode */
62 switch (fmt
& SND_SOC_DAIFMT_FORMAT_MASK
) {
63 case SND_SOC_DAIFMT_I2S
:
64 bf5xx_i2s
->tcr1
|= TFSR
| TCKFE
;
65 bf5xx_i2s
->rcr1
|= RFSR
| RCKFE
;
66 bf5xx_i2s
->tcr2
|= TSFSE
;
67 bf5xx_i2s
->rcr2
|= RSFSE
;
69 case SND_SOC_DAIFMT_DSP_A
:
70 bf5xx_i2s
->tcr1
|= TFSR
;
71 bf5xx_i2s
->rcr1
|= RFSR
;
73 case SND_SOC_DAIFMT_LEFT_J
:
77 printk(KERN_ERR
"%s: Unknown DAI format type\n", __func__
);
82 switch (fmt
& SND_SOC_DAIFMT_MASTER_MASK
) {
83 case SND_SOC_DAIFMT_CBM_CFM
:
85 case SND_SOC_DAIFMT_CBS_CFS
:
86 case SND_SOC_DAIFMT_CBM_CFS
:
87 case SND_SOC_DAIFMT_CBS_CFM
:
91 printk(KERN_ERR
"%s: Unknown DAI master type\n", __func__
);
99 static int bf5xx_i2s_hw_params(struct snd_pcm_substream
*substream
,
100 struct snd_pcm_hw_params
*params
,
101 struct snd_soc_dai
*dai
)
103 struct sport_device
*sport_handle
= snd_soc_dai_get_drvdata(dai
);
104 struct bf5xx_i2s_port
*bf5xx_i2s
= sport_handle
->private_data
;
107 bf5xx_i2s
->tcr2
&= ~0x1f;
108 bf5xx_i2s
->rcr2
&= ~0x1f;
109 switch (params_format(params
)) {
110 case SNDRV_PCM_FORMAT_S8
:
111 bf5xx_i2s
->tcr2
|= 7;
112 bf5xx_i2s
->rcr2
|= 7;
113 sport_handle
->wdsize
= 1;
114 case SNDRV_PCM_FORMAT_S16_LE
:
115 bf5xx_i2s
->tcr2
|= 15;
116 bf5xx_i2s
->rcr2
|= 15;
117 sport_handle
->wdsize
= 2;
119 case SNDRV_PCM_FORMAT_S24_LE
:
120 bf5xx_i2s
->tcr2
|= 23;
121 bf5xx_i2s
->rcr2
|= 23;
122 sport_handle
->wdsize
= 3;
124 case SNDRV_PCM_FORMAT_S32_LE
:
125 bf5xx_i2s
->tcr2
|= 31;
126 bf5xx_i2s
->rcr2
|= 31;
127 sport_handle
->wdsize
= 4;
131 if (!bf5xx_i2s
->configured
) {
133 * TX and RX are not independent,they are enabled at the
134 * same time, even if only one side is running. So, we
135 * need to configure both of them at the time when the first
138 * CPU DAI:slave mode.
140 bf5xx_i2s
->configured
= 1;
141 ret
= sport_config_rx(sport_handle
, bf5xx_i2s
->rcr1
,
142 bf5xx_i2s
->rcr2
, 0, 0);
144 pr_err("SPORT is busy!\n");
148 ret
= sport_config_tx(sport_handle
, bf5xx_i2s
->tcr1
,
149 bf5xx_i2s
->tcr2
, 0, 0);
151 pr_err("SPORT is busy!\n");
159 static void bf5xx_i2s_shutdown(struct snd_pcm_substream
*substream
,
160 struct snd_soc_dai
*dai
)
162 struct sport_device
*sport_handle
= snd_soc_dai_get_drvdata(dai
);
163 struct bf5xx_i2s_port
*bf5xx_i2s
= sport_handle
->private_data
;
165 pr_debug("%s enter\n", __func__
);
166 /* No active stream, SPORT is allowed to be configured again. */
168 bf5xx_i2s
->configured
= 0;
172 static int bf5xx_i2s_suspend(struct snd_soc_dai
*dai
)
174 struct sport_device
*sport_handle
= snd_soc_dai_get_drvdata(dai
);
176 pr_debug("%s : sport %d\n", __func__
, dai
->id
);
178 if (dai
->capture_active
)
179 sport_rx_stop(sport_handle
);
180 if (dai
->playback_active
)
181 sport_tx_stop(sport_handle
);
185 static int bf5xx_i2s_resume(struct snd_soc_dai
*dai
)
187 struct sport_device
*sport_handle
= snd_soc_dai_get_drvdata(dai
);
188 struct bf5xx_i2s_port
*bf5xx_i2s
= sport_handle
->private_data
;
191 pr_debug("%s : sport %d\n", __func__
, dai
->id
);
193 ret
= sport_config_rx(sport_handle
, bf5xx_i2s
->rcr1
,
194 bf5xx_i2s
->rcr2
, 0, 0);
196 pr_err("SPORT is busy!\n");
200 ret
= sport_config_tx(sport_handle
, bf5xx_i2s
->tcr1
,
201 bf5xx_i2s
->tcr2
, 0, 0);
203 pr_err("SPORT is busy!\n");
211 #define bf5xx_i2s_suspend NULL
212 #define bf5xx_i2s_resume NULL
215 #define BF5XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
216 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
217 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
218 SNDRV_PCM_RATE_96000)
220 #define BF5XX_I2S_FORMATS \
221 (SNDRV_PCM_FMTBIT_S8 | \
222 SNDRV_PCM_FMTBIT_S16_LE | \
223 SNDRV_PCM_FMTBIT_S24_LE | \
224 SNDRV_PCM_FMTBIT_S32_LE)
226 static struct snd_soc_dai_ops bf5xx_i2s_dai_ops
= {
227 .shutdown
= bf5xx_i2s_shutdown
,
228 .hw_params
= bf5xx_i2s_hw_params
,
229 .set_fmt
= bf5xx_i2s_set_dai_fmt
,
232 static struct snd_soc_dai_driver bf5xx_i2s_dai
= {
233 .suspend
= bf5xx_i2s_suspend
,
234 .resume
= bf5xx_i2s_resume
,
238 .rates
= BF5XX_I2S_RATES
,
239 .formats
= BF5XX_I2S_FORMATS
,},
243 .rates
= BF5XX_I2S_RATES
,
244 .formats
= BF5XX_I2S_FORMATS
,},
245 .ops
= &bf5xx_i2s_dai_ops
,
248 static int __devinit
bf5xx_i2s_probe(struct platform_device
*pdev
)
250 struct sport_device
*sport_handle
;
253 /* configure SPORT for I2S */
254 sport_handle
= sport_init(pdev
, 4, 2 * sizeof(u32
),
255 sizeof(struct bf5xx_i2s_port
));
259 /* register with the ASoC layers */
260 ret
= snd_soc_register_dai(&pdev
->dev
, &bf5xx_i2s_dai
);
262 pr_err("Failed to register DAI: %d\n", ret
);
263 sport_done(sport_handle
);
270 static int __devexit
bf5xx_i2s_remove(struct platform_device
*pdev
)
272 struct sport_device
*sport_handle
= platform_get_drvdata(pdev
);
274 pr_debug("%s enter\n", __func__
);
276 snd_soc_unregister_dai(&pdev
->dev
);
277 sport_done(sport_handle
);
282 static struct platform_driver bfin_i2s_driver
= {
283 .probe
= bf5xx_i2s_probe
,
284 .remove
= __devexit_p(bf5xx_i2s_remove
),
287 .owner
= THIS_MODULE
,
291 static int __init
bfin_i2s_init(void)
293 return platform_driver_register(&bfin_i2s_driver
);
296 static void __exit
bfin_i2s_exit(void)
298 platform_driver_unregister(&bfin_i2s_driver
);
301 module_init(bfin_i2s_init
);
302 module_exit(bfin_i2s_exit
);
304 /* Module information */
305 MODULE_AUTHOR("Cliff Cai");
306 MODULE_DESCRIPTION("I2S driver for ADI Blackfin");
307 MODULE_LICENSE("GPL");