diff options
Diffstat (limited to 'sound/soc/blackfin/bf5xx-tdm.c')
-rw-r--r-- | sound/soc/blackfin/bf5xx-tdm.c | 133 |
1 files changed, 48 insertions, 85 deletions
diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c index 24c14269f4bc..a822d1ee1380 100644 --- a/sound/soc/blackfin/bf5xx-tdm.c +++ b/sound/soc/blackfin/bf5xx-tdm.c | |||
@@ -46,43 +46,6 @@ | |||
46 | #include "bf5xx-sport.h" | 46 | #include "bf5xx-sport.h" |
47 | #include "bf5xx-tdm.h" | 47 | #include "bf5xx-tdm.h" |
48 | 48 | ||
49 | static struct bf5xx_tdm_port bf5xx_tdm; | ||
50 | static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM; | ||
51 | |||
52 | static struct sport_param sport_params[2] = { | ||
53 | { | ||
54 | .dma_rx_chan = CH_SPORT0_RX, | ||
55 | .dma_tx_chan = CH_SPORT0_TX, | ||
56 | .err_irq = IRQ_SPORT0_ERROR, | ||
57 | .regs = (struct sport_register *)SPORT0_TCR1, | ||
58 | }, | ||
59 | { | ||
60 | .dma_rx_chan = CH_SPORT1_RX, | ||
61 | .dma_tx_chan = CH_SPORT1_TX, | ||
62 | .err_irq = IRQ_SPORT1_ERROR, | ||
63 | .regs = (struct sport_register *)SPORT1_TCR1, | ||
64 | } | ||
65 | }; | ||
66 | |||
67 | /* | ||
68 | * Setting the TFS pin selector for SPORT 0 based on whether the selected | ||
69 | * port id F or G. If the port is F then no conflict should exist for the | ||
70 | * TFS. When Port G is selected and EMAC then there is a conflict between | ||
71 | * the PHY interrupt line and TFS. Current settings prevent the conflict | ||
72 | * by ignoring the TFS pin when Port G is selected. This allows both | ||
73 | * codecs and EMAC using Port G concurrently. | ||
74 | */ | ||
75 | #ifdef CONFIG_BF527_SPORT0_PORTG | ||
76 | #define LOCAL_SPORT0_TFS (0) | ||
77 | #else | ||
78 | #define LOCAL_SPORT0_TFS (P_SPORT0_TFS) | ||
79 | #endif | ||
80 | |||
81 | static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, | ||
82 | P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0}, | ||
83 | {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI, | ||
84 | P_SPORT1_RSCLK, P_SPORT1_TFS, 0} }; | ||
85 | |||
86 | static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai, | 49 | static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai, |
87 | unsigned int fmt) | 50 | unsigned int fmt) |
88 | { | 51 | { |
@@ -119,14 +82,16 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream, | |||
119 | struct snd_pcm_hw_params *params, | 82 | struct snd_pcm_hw_params *params, |
120 | struct snd_soc_dai *dai) | 83 | struct snd_soc_dai *dai) |
121 | { | 84 | { |
85 | struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); | ||
86 | struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data; | ||
122 | int ret = 0; | 87 | int ret = 0; |
123 | 88 | ||
124 | bf5xx_tdm.tcr2 &= ~0x1f; | 89 | bf5xx_tdm->tcr2 &= ~0x1f; |
125 | bf5xx_tdm.rcr2 &= ~0x1f; | 90 | bf5xx_tdm->rcr2 &= ~0x1f; |
126 | switch (params_format(params)) { | 91 | switch (params_format(params)) { |
127 | case SNDRV_PCM_FORMAT_S32_LE: | 92 | case SNDRV_PCM_FORMAT_S32_LE: |
128 | bf5xx_tdm.tcr2 |= 31; | 93 | bf5xx_tdm->tcr2 |= 31; |
129 | bf5xx_tdm.rcr2 |= 31; | 94 | bf5xx_tdm->rcr2 |= 31; |
130 | sport_handle->wdsize = 4; | 95 | sport_handle->wdsize = 4; |
131 | break; | 96 | break; |
132 | /* at present, we only support 32bit transfer */ | 97 | /* at present, we only support 32bit transfer */ |
@@ -136,7 +101,7 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream, | |||
136 | break; | 101 | break; |
137 | } | 102 | } |
138 | 103 | ||
139 | if (!bf5xx_tdm.configured) { | 104 | if (!bf5xx_tdm->configured) { |
140 | /* | 105 | /* |
141 | * TX and RX are not independent,they are enabled at the | 106 | * TX and RX are not independent,they are enabled at the |
142 | * same time, even if only one side is running. So, we | 107 | * same time, even if only one side is running. So, we |
@@ -145,21 +110,21 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream, | |||
145 | * | 110 | * |
146 | * CPU DAI:slave mode. | 111 | * CPU DAI:slave mode. |
147 | */ | 112 | */ |
148 | ret = sport_config_rx(sport_handle, bf5xx_tdm.rcr1, | 113 | ret = sport_config_rx(sport_handle, bf5xx_tdm->rcr1, |
149 | bf5xx_tdm.rcr2, 0, 0); | 114 | bf5xx_tdm->rcr2, 0, 0); |
150 | if (ret) { | 115 | if (ret) { |
151 | pr_err("SPORT is busy!\n"); | 116 | pr_err("SPORT is busy!\n"); |
152 | return -EBUSY; | 117 | return -EBUSY; |
153 | } | 118 | } |
154 | 119 | ||
155 | ret = sport_config_tx(sport_handle, bf5xx_tdm.tcr1, | 120 | ret = sport_config_tx(sport_handle, bf5xx_tdm->tcr1, |
156 | bf5xx_tdm.tcr2, 0, 0); | 121 | bf5xx_tdm->tcr2, 0, 0); |
157 | if (ret) { | 122 | if (ret) { |
158 | pr_err("SPORT is busy!\n"); | 123 | pr_err("SPORT is busy!\n"); |
159 | return -EBUSY; | 124 | return -EBUSY; |
160 | } | 125 | } |
161 | 126 | ||
162 | bf5xx_tdm.configured = 1; | 127 | bf5xx_tdm->configured = 1; |
163 | } | 128 | } |
164 | 129 | ||
165 | return 0; | 130 | return 0; |
@@ -168,15 +133,20 @@ static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream, | |||
168 | static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream, | 133 | static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream, |
169 | struct snd_soc_dai *dai) | 134 | struct snd_soc_dai *dai) |
170 | { | 135 | { |
136 | struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); | ||
137 | struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data; | ||
138 | |||
171 | /* No active stream, SPORT is allowed to be configured again. */ | 139 | /* No active stream, SPORT is allowed to be configured again. */ |
172 | if (!dai->active) | 140 | if (!dai->active) |
173 | bf5xx_tdm.configured = 0; | 141 | bf5xx_tdm->configured = 0; |
174 | } | 142 | } |
175 | 143 | ||
176 | static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai, | 144 | static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai, |
177 | unsigned int tx_num, unsigned int *tx_slot, | 145 | unsigned int tx_num, unsigned int *tx_slot, |
178 | unsigned int rx_num, unsigned int *rx_slot) | 146 | unsigned int rx_num, unsigned int *rx_slot) |
179 | { | 147 | { |
148 | struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); | ||
149 | struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data; | ||
180 | int i; | 150 | int i; |
181 | unsigned int slot; | 151 | unsigned int slot; |
182 | unsigned int tx_mapped = 0, rx_mapped = 0; | 152 | unsigned int tx_mapped = 0, rx_mapped = 0; |
@@ -189,7 +159,7 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai, | |||
189 | slot = tx_slot[i]; | 159 | slot = tx_slot[i]; |
190 | if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && | 160 | if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && |
191 | (!(tx_mapped & (1 << slot)))) { | 161 | (!(tx_mapped & (1 << slot)))) { |
192 | bf5xx_tdm.tx_map[i] = slot; | 162 | bf5xx_tdm->tx_map[i] = slot; |
193 | tx_mapped |= 1 << slot; | 163 | tx_mapped |= 1 << slot; |
194 | } else | 164 | } else |
195 | return -EINVAL; | 165 | return -EINVAL; |
@@ -198,7 +168,7 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai, | |||
198 | slot = rx_slot[i]; | 168 | slot = rx_slot[i]; |
199 | if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && | 169 | if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && |
200 | (!(rx_mapped & (1 << slot)))) { | 170 | (!(rx_mapped & (1 << slot)))) { |
201 | bf5xx_tdm.rx_map[i] = slot; | 171 | bf5xx_tdm->rx_map[i] = slot; |
202 | rx_mapped |= 1 << slot; | 172 | rx_mapped |= 1 << slot; |
203 | } else | 173 | } else |
204 | return -EINVAL; | 174 | return -EINVAL; |
@@ -210,24 +180,23 @@ static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai, | |||
210 | #ifdef CONFIG_PM | 180 | #ifdef CONFIG_PM |
211 | static int bf5xx_tdm_suspend(struct snd_soc_dai *dai) | 181 | static int bf5xx_tdm_suspend(struct snd_soc_dai *dai) |
212 | { | 182 | { |
213 | struct sport_device *sport = dai->private_data; | 183 | struct sport_device *sport = snd_soc_dai_get_drvdata(dai); |
214 | 184 | ||
215 | if (!dai->active) | 185 | if (dai->playback_active) |
216 | return 0; | ||
217 | if (dai->capture.active) | ||
218 | sport_rx_stop(sport); | ||
219 | if (dai->playback.active) | ||
220 | sport_tx_stop(sport); | 186 | sport_tx_stop(sport); |
187 | if (dai->capture_active) | ||
188 | sport_rx_stop(sport); | ||
189 | |||
190 | /* isolate sync/clock pins from codec while sports resume */ | ||
191 | peripheral_free_list(sport->pin_req); | ||
192 | |||
221 | return 0; | 193 | return 0; |
222 | } | 194 | } |
223 | 195 | ||
224 | static int bf5xx_tdm_resume(struct snd_soc_dai *dai) | 196 | static int bf5xx_tdm_resume(struct snd_soc_dai *dai) |
225 | { | 197 | { |
226 | int ret; | 198 | int ret; |
227 | struct sport_device *sport = dai->private_data; | 199 | struct sport_device *sport = snd_soc_dai_get_drvdata(dai); |
228 | |||
229 | if (!dai->active) | ||
230 | return 0; | ||
231 | 200 | ||
232 | ret = sport_set_multichannel(sport, 8, 0xFF, 1); | 201 | ret = sport_set_multichannel(sport, 8, 0xFF, 1); |
233 | if (ret) { | 202 | if (ret) { |
@@ -235,18 +204,20 @@ static int bf5xx_tdm_resume(struct snd_soc_dai *dai) | |||
235 | ret = -EBUSY; | 204 | ret = -EBUSY; |
236 | } | 205 | } |
237 | 206 | ||
238 | ret = sport_config_rx(sport, IRFS, 0x1F, 0, 0); | 207 | ret = sport_config_rx(sport, 0, 0x1F, 0, 0); |
239 | if (ret) { | 208 | if (ret) { |
240 | pr_err("SPORT is busy!\n"); | 209 | pr_err("SPORT is busy!\n"); |
241 | ret = -EBUSY; | 210 | ret = -EBUSY; |
242 | } | 211 | } |
243 | 212 | ||
244 | ret = sport_config_tx(sport, ITFS, 0x1F, 0, 0); | 213 | ret = sport_config_tx(sport, 0, 0x1F, 0, 0); |
245 | if (ret) { | 214 | if (ret) { |
246 | pr_err("SPORT is busy!\n"); | 215 | pr_err("SPORT is busy!\n"); |
247 | ret = -EBUSY; | 216 | ret = -EBUSY; |
248 | } | 217 | } |
249 | 218 | ||
219 | peripheral_request_list(sport->pin_req, "soc-audio"); | ||
220 | |||
250 | return 0; | 221 | return 0; |
251 | } | 222 | } |
252 | 223 | ||
@@ -262,9 +233,7 @@ static struct snd_soc_dai_ops bf5xx_tdm_dai_ops = { | |||
262 | .set_channel_map = bf5xx_tdm_set_channel_map, | 233 | .set_channel_map = bf5xx_tdm_set_channel_map, |
263 | }; | 234 | }; |
264 | 235 | ||
265 | struct snd_soc_dai bf5xx_tdm_dai = { | 236 | static struct snd_soc_dai_driver bf5xx_tdm_dai = { |
266 | .name = "bf5xx-tdm", | ||
267 | .id = 0, | ||
268 | .suspend = bf5xx_tdm_suspend, | 237 | .suspend = bf5xx_tdm_suspend, |
269 | .resume = bf5xx_tdm_resume, | 238 | .resume = bf5xx_tdm_resume, |
270 | .playback = { | 239 | .playback = { |
@@ -279,24 +248,17 @@ struct snd_soc_dai bf5xx_tdm_dai = { | |||
279 | .formats = SNDRV_PCM_FMTBIT_S32_LE,}, | 248 | .formats = SNDRV_PCM_FMTBIT_S32_LE,}, |
280 | .ops = &bf5xx_tdm_dai_ops, | 249 | .ops = &bf5xx_tdm_dai_ops, |
281 | }; | 250 | }; |
282 | EXPORT_SYMBOL_GPL(bf5xx_tdm_dai); | ||
283 | 251 | ||
284 | static int __devinit bfin_tdm_probe(struct platform_device *pdev) | 252 | static int __devinit bfin_tdm_probe(struct platform_device *pdev) |
285 | { | 253 | { |
286 | int ret = 0; | 254 | struct sport_device *sport_handle; |
287 | 255 | int ret; | |
288 | if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) { | ||
289 | pr_err("Requesting Peripherals failed\n"); | ||
290 | return -EFAULT; | ||
291 | } | ||
292 | 256 | ||
293 | /* request DMA for SPORT */ | 257 | /* configure SPORT for TDM */ |
294 | sport_handle = sport_init(&sport_params[sport_num], 4, \ | 258 | sport_handle = sport_init(pdev, 4, 8 * sizeof(u32), |
295 | 8 * sizeof(u32), NULL); | 259 | sizeof(struct bf5xx_tdm_port)); |
296 | if (!sport_handle) { | 260 | if (!sport_handle) |
297 | peripheral_free_list(&sport_req[sport_num][0]); | ||
298 | return -ENODEV; | 261 | return -ENODEV; |
299 | } | ||
300 | 262 | ||
301 | /* SPORT works in TDM mode */ | 263 | /* SPORT works in TDM mode */ |
302 | ret = sport_set_multichannel(sport_handle, 8, 0xFF, 1); | 264 | ret = sport_set_multichannel(sport_handle, 8, 0xFF, 1); |
@@ -306,38 +268,39 @@ static int __devinit bfin_tdm_probe(struct platform_device *pdev) | |||
306 | goto sport_config_err; | 268 | goto sport_config_err; |
307 | } | 269 | } |
308 | 270 | ||
309 | ret = sport_config_rx(sport_handle, IRFS, 0x1F, 0, 0); | 271 | ret = sport_config_rx(sport_handle, 0, 0x1F, 0, 0); |
310 | if (ret) { | 272 | if (ret) { |
311 | pr_err("SPORT is busy!\n"); | 273 | pr_err("SPORT is busy!\n"); |
312 | ret = -EBUSY; | 274 | ret = -EBUSY; |
313 | goto sport_config_err; | 275 | goto sport_config_err; |
314 | } | 276 | } |
315 | 277 | ||
316 | ret = sport_config_tx(sport_handle, ITFS, 0x1F, 0, 0); | 278 | ret = sport_config_tx(sport_handle, 0, 0x1F, 0, 0); |
317 | if (ret) { | 279 | if (ret) { |
318 | pr_err("SPORT is busy!\n"); | 280 | pr_err("SPORT is busy!\n"); |
319 | ret = -EBUSY; | 281 | ret = -EBUSY; |
320 | goto sport_config_err; | 282 | goto sport_config_err; |
321 | } | 283 | } |
322 | 284 | ||
323 | ret = snd_soc_register_dai(&bf5xx_tdm_dai); | 285 | ret = snd_soc_register_dai(&pdev->dev, &bf5xx_tdm_dai); |
324 | if (ret) { | 286 | if (ret) { |
325 | pr_err("Failed to register DAI: %d\n", ret); | 287 | pr_err("Failed to register DAI: %d\n", ret); |
326 | goto sport_config_err; | 288 | goto sport_config_err; |
327 | } | 289 | } |
328 | 290 | ||
329 | sport_handle->private_data = &bf5xx_tdm; | ||
330 | return 0; | 291 | return 0; |
331 | 292 | ||
332 | sport_config_err: | 293 | sport_config_err: |
333 | peripheral_free_list(&sport_req[sport_num][0]); | 294 | sport_done(sport_handle); |
334 | return ret; | 295 | return ret; |
335 | } | 296 | } |
336 | 297 | ||
337 | static int __devexit bfin_tdm_remove(struct platform_device *pdev) | 298 | static int __devexit bfin_tdm_remove(struct platform_device *pdev) |
338 | { | 299 | { |
339 | peripheral_free_list(&sport_req[sport_num][0]); | 300 | struct sport_device *sport_handle = platform_get_drvdata(pdev); |
340 | snd_soc_unregister_dai(&bf5xx_tdm_dai); | 301 | |
302 | snd_soc_unregister_dai(&pdev->dev); | ||
303 | sport_done(sport_handle); | ||
341 | 304 | ||
342 | return 0; | 305 | return 0; |
343 | } | 306 | } |