diff options
Diffstat (limited to 'sound/soc/blackfin/bf5xx-i2s.c')
-rw-r--r-- | sound/soc/blackfin/bf5xx-i2s.c | 197 |
1 files changed, 94 insertions, 103 deletions
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c index 3e6ada0dd1c4..00cc3e00b2fe 100644 --- a/sound/soc/blackfin/bf5xx-i2s.c +++ b/sound/soc/blackfin/bf5xx-i2s.c | |||
@@ -42,7 +42,6 @@ | |||
42 | #include <linux/gpio.h> | 42 | #include <linux/gpio.h> |
43 | 43 | ||
44 | #include "bf5xx-sport.h" | 44 | #include "bf5xx-sport.h" |
45 | #include "bf5xx-i2s.h" | ||
46 | 45 | ||
47 | struct bf5xx_i2s_port { | 46 | struct bf5xx_i2s_port { |
48 | u16 tcr1; | 47 | u16 tcr1; |
@@ -52,59 +51,24 @@ struct bf5xx_i2s_port { | |||
52 | int configured; | 51 | int configured; |
53 | }; | 52 | }; |
54 | 53 | ||
55 | static struct bf5xx_i2s_port bf5xx_i2s; | ||
56 | static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM; | ||
57 | |||
58 | static struct sport_param sport_params[2] = { | ||
59 | { | ||
60 | .dma_rx_chan = CH_SPORT0_RX, | ||
61 | .dma_tx_chan = CH_SPORT0_TX, | ||
62 | .err_irq = IRQ_SPORT0_ERROR, | ||
63 | .regs = (struct sport_register *)SPORT0_TCR1, | ||
64 | }, | ||
65 | { | ||
66 | .dma_rx_chan = CH_SPORT1_RX, | ||
67 | .dma_tx_chan = CH_SPORT1_TX, | ||
68 | .err_irq = IRQ_SPORT1_ERROR, | ||
69 | .regs = (struct sport_register *)SPORT1_TCR1, | ||
70 | } | ||
71 | }; | ||
72 | |||
73 | /* | ||
74 | * Setting the TFS pin selector for SPORT 0 based on whether the selected | ||
75 | * port id F or G. If the port is F then no conflict should exist for the | ||
76 | * TFS. When Port G is selected and EMAC then there is a conflict between | ||
77 | * the PHY interrupt line and TFS. Current settings prevent the conflict | ||
78 | * by ignoring the TFS pin when Port G is selected. This allows both | ||
79 | * codecs and EMAC using Port G concurrently. | ||
80 | */ | ||
81 | #ifdef CONFIG_BF527_SPORT0_PORTG | ||
82 | #define LOCAL_SPORT0_TFS (0) | ||
83 | #else | ||
84 | #define LOCAL_SPORT0_TFS (P_SPORT0_TFS) | ||
85 | #endif | ||
86 | |||
87 | static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, | ||
88 | P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0}, | ||
89 | {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI, | ||
90 | P_SPORT1_RSCLK, P_SPORT1_TFS, 0} }; | ||
91 | |||
92 | static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, | 54 | static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, |
93 | unsigned int fmt) | 55 | unsigned int fmt) |
94 | { | 56 | { |
57 | struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai); | ||
58 | struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; | ||
95 | int ret = 0; | 59 | int ret = 0; |
96 | 60 | ||
97 | /* interface format:support I2S,slave mode */ | 61 | /* interface format:support I2S,slave mode */ |
98 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | 62 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
99 | case SND_SOC_DAIFMT_I2S: | 63 | case SND_SOC_DAIFMT_I2S: |
100 | bf5xx_i2s.tcr1 |= TFSR | TCKFE; | 64 | bf5xx_i2s->tcr1 |= TFSR | TCKFE; |
101 | bf5xx_i2s.rcr1 |= RFSR | RCKFE; | 65 | bf5xx_i2s->rcr1 |= RFSR | RCKFE; |
102 | bf5xx_i2s.tcr2 |= TSFSE; | 66 | bf5xx_i2s->tcr2 |= TSFSE; |
103 | bf5xx_i2s.rcr2 |= RSFSE; | 67 | bf5xx_i2s->rcr2 |= RSFSE; |
104 | break; | 68 | break; |
105 | case SND_SOC_DAIFMT_DSP_A: | 69 | case SND_SOC_DAIFMT_DSP_A: |
106 | bf5xx_i2s.tcr1 |= TFSR; | 70 | bf5xx_i2s->tcr1 |= TFSR; |
107 | bf5xx_i2s.rcr1 |= RFSR; | 71 | bf5xx_i2s->rcr1 |= RFSR; |
108 | break; | 72 | break; |
109 | case SND_SOC_DAIFMT_LEFT_J: | 73 | case SND_SOC_DAIFMT_LEFT_J: |
110 | ret = -EINVAL; | 74 | ret = -EINVAL; |
@@ -136,29 +100,35 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream, | |||
136 | struct snd_pcm_hw_params *params, | 100 | struct snd_pcm_hw_params *params, |
137 | struct snd_soc_dai *dai) | 101 | struct snd_soc_dai *dai) |
138 | { | 102 | { |
103 | struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); | ||
104 | struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; | ||
139 | int ret = 0; | 105 | int ret = 0; |
140 | 106 | ||
141 | bf5xx_i2s.tcr2 &= ~0x1f; | 107 | bf5xx_i2s->tcr2 &= ~0x1f; |
142 | bf5xx_i2s.rcr2 &= ~0x1f; | 108 | bf5xx_i2s->rcr2 &= ~0x1f; |
143 | switch (params_format(params)) { | 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; | ||
144 | case SNDRV_PCM_FORMAT_S16_LE: | 114 | case SNDRV_PCM_FORMAT_S16_LE: |
145 | bf5xx_i2s.tcr2 |= 15; | 115 | bf5xx_i2s->tcr2 |= 15; |
146 | bf5xx_i2s.rcr2 |= 15; | 116 | bf5xx_i2s->rcr2 |= 15; |
147 | sport_handle->wdsize = 2; | 117 | sport_handle->wdsize = 2; |
148 | break; | 118 | break; |
149 | case SNDRV_PCM_FORMAT_S24_LE: | 119 | case SNDRV_PCM_FORMAT_S24_LE: |
150 | bf5xx_i2s.tcr2 |= 23; | 120 | bf5xx_i2s->tcr2 |= 23; |
151 | bf5xx_i2s.rcr2 |= 23; | 121 | bf5xx_i2s->rcr2 |= 23; |
152 | sport_handle->wdsize = 3; | 122 | sport_handle->wdsize = 3; |
153 | break; | 123 | break; |
154 | case SNDRV_PCM_FORMAT_S32_LE: | 124 | case SNDRV_PCM_FORMAT_S32_LE: |
155 | bf5xx_i2s.tcr2 |= 31; | 125 | bf5xx_i2s->tcr2 |= 31; |
156 | bf5xx_i2s.rcr2 |= 31; | 126 | bf5xx_i2s->rcr2 |= 31; |
157 | sport_handle->wdsize = 4; | 127 | sport_handle->wdsize = 4; |
158 | break; | 128 | break; |
159 | } | 129 | } |
160 | 130 | ||
161 | if (!bf5xx_i2s.configured) { | 131 | if (!bf5xx_i2s->configured) { |
162 | /* | 132 | /* |
163 | * TX and RX are not independent,they are enabled at the | 133 | * TX and RX are not independent,they are enabled at the |
164 | * same time, even if only one side is running. So, we | 134 | * same time, even if only one side is running. So, we |
@@ -167,16 +137,16 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream, | |||
167 | * | 137 | * |
168 | * CPU DAI:slave mode. | 138 | * CPU DAI:slave mode. |
169 | */ | 139 | */ |
170 | bf5xx_i2s.configured = 1; | 140 | bf5xx_i2s->configured = 1; |
171 | ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1, | 141 | ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1, |
172 | bf5xx_i2s.rcr2, 0, 0); | 142 | bf5xx_i2s->rcr2, 0, 0); |
173 | if (ret) { | 143 | if (ret) { |
174 | pr_err("SPORT is busy!\n"); | 144 | pr_err("SPORT is busy!\n"); |
175 | return -EBUSY; | 145 | return -EBUSY; |
176 | } | 146 | } |
177 | 147 | ||
178 | ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1, | 148 | ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1, |
179 | bf5xx_i2s.tcr2, 0, 0); | 149 | bf5xx_i2s->tcr2, 0, 0); |
180 | if (ret) { | 150 | if (ret) { |
181 | pr_err("SPORT is busy!\n"); | 151 | pr_err("SPORT is busy!\n"); |
182 | return -EBUSY; | 152 | return -EBUSY; |
@@ -189,67 +159,46 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream, | |||
189 | static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream, | 159 | static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream, |
190 | struct snd_soc_dai *dai) | 160 | struct snd_soc_dai *dai) |
191 | { | 161 | { |
162 | struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); | ||
163 | struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; | ||
164 | |||
192 | pr_debug("%s enter\n", __func__); | 165 | pr_debug("%s enter\n", __func__); |
193 | /* No active stream, SPORT is allowed to be configured again. */ | 166 | /* No active stream, SPORT is allowed to be configured again. */ |
194 | if (!dai->active) | 167 | if (!dai->active) |
195 | bf5xx_i2s.configured = 0; | 168 | bf5xx_i2s->configured = 0; |
196 | } | ||
197 | |||
198 | static int bf5xx_i2s_probe(struct platform_device *pdev, | ||
199 | struct snd_soc_dai *dai) | ||
200 | { | ||
201 | pr_debug("%s enter\n", __func__); | ||
202 | if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) { | ||
203 | pr_err("Requesting Peripherals failed\n"); | ||
204 | return -EFAULT; | ||
205 | } | ||
206 | |||
207 | /* request DMA for SPORT */ | ||
208 | sport_handle = sport_init(&sport_params[sport_num], 4, \ | ||
209 | 2 * sizeof(u32), NULL); | ||
210 | if (!sport_handle) { | ||
211 | peripheral_free_list(&sport_req[sport_num][0]); | ||
212 | return -ENODEV; | ||
213 | } | ||
214 | |||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static void bf5xx_i2s_remove(struct platform_device *pdev, | ||
219 | struct snd_soc_dai *dai) | ||
220 | { | ||
221 | pr_debug("%s enter\n", __func__); | ||
222 | peripheral_free_list(&sport_req[sport_num][0]); | ||
223 | } | 169 | } |
224 | 170 | ||
225 | #ifdef CONFIG_PM | 171 | #ifdef CONFIG_PM |
226 | static int bf5xx_i2s_suspend(struct snd_soc_dai *dai) | 172 | static int bf5xx_i2s_suspend(struct snd_soc_dai *dai) |
227 | { | 173 | { |
174 | struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); | ||
228 | 175 | ||
229 | pr_debug("%s : sport %d\n", __func__, dai->id); | 176 | pr_debug("%s : sport %d\n", __func__, dai->id); |
230 | 177 | ||
231 | if (dai->capture.active) | 178 | if (dai->capture_active) |
232 | sport_rx_stop(sport_handle); | 179 | sport_rx_stop(sport_handle); |
233 | if (dai->playback.active) | 180 | if (dai->playback_active) |
234 | sport_tx_stop(sport_handle); | 181 | sport_tx_stop(sport_handle); |
235 | return 0; | 182 | return 0; |
236 | } | 183 | } |
237 | 184 | ||
238 | static int bf5xx_i2s_resume(struct snd_soc_dai *dai) | 185 | static int bf5xx_i2s_resume(struct snd_soc_dai *dai) |
239 | { | 186 | { |
187 | struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); | ||
188 | struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; | ||
240 | int ret; | 189 | int ret; |
241 | 190 | ||
242 | pr_debug("%s : sport %d\n", __func__, dai->id); | 191 | pr_debug("%s : sport %d\n", __func__, dai->id); |
243 | 192 | ||
244 | ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1, | 193 | ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1, |
245 | bf5xx_i2s.rcr2, 0, 0); | 194 | bf5xx_i2s->rcr2, 0, 0); |
246 | if (ret) { | 195 | if (ret) { |
247 | pr_err("SPORT is busy!\n"); | 196 | pr_err("SPORT is busy!\n"); |
248 | return -EBUSY; | 197 | return -EBUSY; |
249 | } | 198 | } |
250 | 199 | ||
251 | ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1, | 200 | ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1, |
252 | bf5xx_i2s.tcr2, 0, 0); | 201 | bf5xx_i2s->tcr2, 0, 0); |
253 | if (ret) { | 202 | if (ret) { |
254 | pr_err("SPORT is busy!\n"); | 203 | pr_err("SPORT is busy!\n"); |
255 | return -EBUSY; | 204 | return -EBUSY; |
@@ -268,8 +217,11 @@ static int bf5xx_i2s_resume(struct snd_soc_dai *dai) | |||
268 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ | 217 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ |
269 | SNDRV_PCM_RATE_96000) | 218 | SNDRV_PCM_RATE_96000) |
270 | 219 | ||
271 | #define BF5XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\ | 220 | #define BF5XX_I2S_FORMATS \ |
272 | SNDRV_PCM_FMTBIT_S32_LE) | 221 | (SNDRV_PCM_FMTBIT_S8 | \ |
222 | SNDRV_PCM_FMTBIT_S16_LE | \ | ||
223 | SNDRV_PCM_FMTBIT_S24_LE | \ | ||
224 | SNDRV_PCM_FMTBIT_S32_LE) | ||
273 | 225 | ||
274 | static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = { | 226 | static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = { |
275 | .shutdown = bf5xx_i2s_shutdown, | 227 | .shutdown = bf5xx_i2s_shutdown, |
@@ -277,11 +229,7 @@ static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = { | |||
277 | .set_fmt = bf5xx_i2s_set_dai_fmt, | 229 | .set_fmt = bf5xx_i2s_set_dai_fmt, |
278 | }; | 230 | }; |
279 | 231 | ||
280 | struct snd_soc_dai bf5xx_i2s_dai = { | 232 | static struct snd_soc_dai_driver bf5xx_i2s_dai = { |
281 | .name = "bf5xx-i2s", | ||
282 | .id = 0, | ||
283 | .probe = bf5xx_i2s_probe, | ||
284 | .remove = bf5xx_i2s_remove, | ||
285 | .suspend = bf5xx_i2s_suspend, | 233 | .suspend = bf5xx_i2s_suspend, |
286 | .resume = bf5xx_i2s_resume, | 234 | .resume = bf5xx_i2s_resume, |
287 | .playback = { | 235 | .playback = { |
@@ -296,18 +244,61 @@ struct snd_soc_dai bf5xx_i2s_dai = { | |||
296 | .formats = BF5XX_I2S_FORMATS,}, | 244 | .formats = BF5XX_I2S_FORMATS,}, |
297 | .ops = &bf5xx_i2s_dai_ops, | 245 | .ops = &bf5xx_i2s_dai_ops, |
298 | }; | 246 | }; |
299 | EXPORT_SYMBOL_GPL(bf5xx_i2s_dai); | 247 | |
248 | static int __devinit bf5xx_i2s_probe(struct platform_device *pdev) | ||
249 | { | ||
250 | struct sport_device *sport_handle; | ||
251 | int ret; | ||
252 | |||
253 | /* configure SPORT for I2S */ | ||
254 | sport_handle = sport_init(pdev, 4, 2 * sizeof(u32), | ||
255 | sizeof(struct bf5xx_i2s_port)); | ||
256 | if (!sport_handle) | ||
257 | return -ENODEV; | ||
258 | |||
259 | /* register with the ASoC layers */ | ||
260 | ret = snd_soc_register_dai(&pdev->dev, &bf5xx_i2s_dai); | ||
261 | if (ret) { | ||
262 | pr_err("Failed to register DAI: %d\n", ret); | ||
263 | sport_done(sport_handle); | ||
264 | return ret; | ||
265 | } | ||
266 | |||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | static int __devexit bf5xx_i2s_remove(struct platform_device *pdev) | ||
271 | { | ||
272 | struct sport_device *sport_handle = platform_get_drvdata(pdev); | ||
273 | |||
274 | pr_debug("%s enter\n", __func__); | ||
275 | |||
276 | snd_soc_unregister_dai(&pdev->dev); | ||
277 | sport_done(sport_handle); | ||
278 | |||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | static struct platform_driver bfin_i2s_driver = { | ||
283 | .probe = bf5xx_i2s_probe, | ||
284 | .remove = __devexit_p(bf5xx_i2s_remove), | ||
285 | .driver = { | ||
286 | .name = "bfin-i2s", | ||
287 | .owner = THIS_MODULE, | ||
288 | }, | ||
289 | }; | ||
300 | 290 | ||
301 | static int __init bfin_i2s_init(void) | 291 | static int __init bfin_i2s_init(void) |
302 | { | 292 | { |
303 | return snd_soc_register_dai(&bf5xx_i2s_dai); | 293 | return platform_driver_register(&bfin_i2s_driver); |
304 | } | 294 | } |
305 | module_init(bfin_i2s_init); | ||
306 | 295 | ||
307 | static void __exit bfin_i2s_exit(void) | 296 | static void __exit bfin_i2s_exit(void) |
308 | { | 297 | { |
309 | snd_soc_unregister_dai(&bf5xx_i2s_dai); | 298 | platform_driver_unregister(&bfin_i2s_driver); |
310 | } | 299 | } |
300 | |||
301 | module_init(bfin_i2s_init); | ||
311 | module_exit(bfin_i2s_exit); | 302 | module_exit(bfin_i2s_exit); |
312 | 303 | ||
313 | /* Module information */ | 304 | /* Module information */ |