diff options
Diffstat (limited to 'sound/soc/blackfin/bf5xx-ac97.c')
-rw-r--r-- | sound/soc/blackfin/bf5xx-ac97.c | 178 |
1 files changed, 110 insertions, 68 deletions
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c index 5e5aafb6485f..3be2be60576d 100644 --- a/sound/soc/blackfin/bf5xx-ac97.c +++ b/sound/soc/blackfin/bf5xx-ac97.c | |||
@@ -54,71 +54,103 @@ | |||
54 | static int *cmd_count; | 54 | static int *cmd_count; |
55 | static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM; | 55 | static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM; |
56 | 56 | ||
57 | #if defined(CONFIG_BF54x) | 57 | static u16 sport_req[][7] = { |
58 | PIN_REQ_SPORT_0, | ||
59 | #ifdef PIN_REQ_SPORT_1 | ||
60 | PIN_REQ_SPORT_1, | ||
61 | #endif | ||
62 | #ifdef PIN_REQ_SPORT_2 | ||
63 | PIN_REQ_SPORT_2, | ||
64 | #endif | ||
65 | #ifdef PIN_REQ_SPORT_3 | ||
66 | PIN_REQ_SPORT_3, | ||
67 | #endif | ||
68 | }; | ||
69 | |||
58 | static struct sport_param sport_params[4] = { | 70 | static struct sport_param sport_params[4] = { |
59 | { | 71 | { |
60 | .dma_rx_chan = CH_SPORT0_RX, | 72 | .dma_rx_chan = CH_SPORT0_RX, |
61 | .dma_tx_chan = CH_SPORT0_TX, | 73 | .dma_tx_chan = CH_SPORT0_TX, |
62 | .err_irq = IRQ_SPORT0_ERR, | 74 | .err_irq = IRQ_SPORT0_ERROR, |
63 | .regs = (struct sport_register *)SPORT0_TCR1, | 75 | .regs = (struct sport_register *)SPORT0_TCR1, |
64 | }, | 76 | }, |
77 | #ifdef PIN_REQ_SPORT_1 | ||
65 | { | 78 | { |
66 | .dma_rx_chan = CH_SPORT1_RX, | 79 | .dma_rx_chan = CH_SPORT1_RX, |
67 | .dma_tx_chan = CH_SPORT1_TX, | 80 | .dma_tx_chan = CH_SPORT1_TX, |
68 | .err_irq = IRQ_SPORT1_ERR, | 81 | .err_irq = IRQ_SPORT1_ERROR, |
69 | .regs = (struct sport_register *)SPORT1_TCR1, | 82 | .regs = (struct sport_register *)SPORT1_TCR1, |
70 | }, | 83 | }, |
84 | #endif | ||
85 | #ifdef PIN_REQ_SPORT_2 | ||
71 | { | 86 | { |
72 | .dma_rx_chan = CH_SPORT2_RX, | 87 | .dma_rx_chan = CH_SPORT2_RX, |
73 | .dma_tx_chan = CH_SPORT2_TX, | 88 | .dma_tx_chan = CH_SPORT2_TX, |
74 | .err_irq = IRQ_SPORT2_ERR, | 89 | .err_irq = IRQ_SPORT2_ERROR, |
75 | .regs = (struct sport_register *)SPORT2_TCR1, | 90 | .regs = (struct sport_register *)SPORT2_TCR1, |
76 | }, | 91 | }, |
92 | #endif | ||
93 | #ifdef PIN_REQ_SPORT_3 | ||
77 | { | 94 | { |
78 | .dma_rx_chan = CH_SPORT3_RX, | 95 | .dma_rx_chan = CH_SPORT3_RX, |
79 | .dma_tx_chan = CH_SPORT3_TX, | 96 | .dma_tx_chan = CH_SPORT3_TX, |
80 | .err_irq = IRQ_SPORT3_ERR, | 97 | .err_irq = IRQ_SPORT3_ERROR, |
81 | .regs = (struct sport_register *)SPORT3_TCR1, | 98 | .regs = (struct sport_register *)SPORT3_TCR1, |
82 | } | 99 | } |
83 | }; | ||
84 | #else | ||
85 | static struct sport_param sport_params[2] = { | ||
86 | { | ||
87 | .dma_rx_chan = CH_SPORT0_RX, | ||
88 | .dma_tx_chan = CH_SPORT0_TX, | ||
89 | .err_irq = IRQ_SPORT0_ERROR, | ||
90 | .regs = (struct sport_register *)SPORT0_TCR1, | ||
91 | }, | ||
92 | { | ||
93 | .dma_rx_chan = CH_SPORT1_RX, | ||
94 | .dma_tx_chan = CH_SPORT1_TX, | ||
95 | .err_irq = IRQ_SPORT1_ERROR, | ||
96 | .regs = (struct sport_register *)SPORT1_TCR1, | ||
97 | } | ||
98 | }; | ||
99 | #endif | 100 | #endif |
101 | }; | ||
100 | 102 | ||
101 | void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \ | 103 | void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u16 *src, |
102 | size_t count) | 104 | size_t count, unsigned int chan_mask) |
103 | { | 105 | { |
104 | while (count--) { | 106 | while (count--) { |
105 | dst->ac97_tag = TAG_VALID | TAG_PCM; | 107 | dst->ac97_tag = TAG_VALID; |
106 | (dst++)->ac97_pcm = *src++; | 108 | if (chan_mask & SP_FL) { |
109 | dst->ac97_pcm_r = *src++; | ||
110 | dst->ac97_tag |= TAG_PCM_RIGHT; | ||
111 | } | ||
112 | if (chan_mask & SP_FR) { | ||
113 | dst->ac97_pcm_l = *src++; | ||
114 | dst->ac97_tag |= TAG_PCM_LEFT; | ||
115 | |||
116 | } | ||
117 | #if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT) | ||
118 | if (chan_mask & SP_SR) { | ||
119 | dst->ac97_sl = *src++; | ||
120 | dst->ac97_tag |= TAG_PCM_SL; | ||
121 | } | ||
122 | if (chan_mask & SP_SL) { | ||
123 | dst->ac97_sr = *src++; | ||
124 | dst->ac97_tag |= TAG_PCM_SR; | ||
125 | } | ||
126 | if (chan_mask & SP_LFE) { | ||
127 | dst->ac97_lfe = *src++; | ||
128 | dst->ac97_tag |= TAG_PCM_LFE; | ||
129 | } | ||
130 | if (chan_mask & SP_FC) { | ||
131 | dst->ac97_center = *src++; | ||
132 | dst->ac97_tag |= TAG_PCM_CENTER; | ||
133 | } | ||
134 | #endif | ||
135 | dst++; | ||
107 | } | 136 | } |
108 | } | 137 | } |
109 | EXPORT_SYMBOL(bf5xx_pcm_to_ac97); | 138 | EXPORT_SYMBOL(bf5xx_pcm_to_ac97); |
110 | 139 | ||
111 | void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \ | 140 | void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u16 *dst, |
112 | size_t count) | 141 | size_t count) |
113 | { | 142 | { |
114 | while (count--) | 143 | while (count--) { |
115 | *(dst++) = (src++)->ac97_pcm; | 144 | *(dst++) = src->ac97_pcm_l; |
145 | *(dst++) = src->ac97_pcm_r; | ||
146 | src++; | ||
147 | } | ||
116 | } | 148 | } |
117 | EXPORT_SYMBOL(bf5xx_ac97_to_pcm); | 149 | EXPORT_SYMBOL(bf5xx_ac97_to_pcm); |
118 | 150 | ||
119 | static unsigned int sport_tx_curr_frag(struct sport_device *sport) | 151 | static unsigned int sport_tx_curr_frag(struct sport_device *sport) |
120 | { | 152 | { |
121 | return sport->tx_curr_frag = sport_curr_offset_tx(sport) / \ | 153 | return sport->tx_curr_frag = sport_curr_offset_tx(sport) / |
122 | sport->tx_fragsize; | 154 | sport->tx_fragsize; |
123 | } | 155 | } |
124 | 156 | ||
@@ -130,7 +162,7 @@ static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data) | |||
130 | 162 | ||
131 | sport_incfrag(sport, &nextfrag, 1); | 163 | sport_incfrag(sport, &nextfrag, 1); |
132 | 164 | ||
133 | nextwrite = (struct ac97_frame *)(sport->tx_buf + \ | 165 | nextwrite = (struct ac97_frame *)(sport->tx_buf + |
134 | nextfrag * sport->tx_fragsize); | 166 | nextfrag * sport->tx_fragsize); |
135 | pr_debug("sport->tx_buf:%p, nextfrag:0x%x nextwrite:%p, cmd_count:%d\n", | 167 | pr_debug("sport->tx_buf:%p, nextfrag:0x%x nextwrite:%p, cmd_count:%d\n", |
136 | sport->tx_buf, nextfrag, nextwrite, cmd_count[nextfrag]); | 168 | sport->tx_buf, nextfrag, nextwrite, cmd_count[nextfrag]); |
@@ -237,8 +269,7 @@ struct snd_ac97_bus_ops soc_ac97_ops = { | |||
237 | EXPORT_SYMBOL_GPL(soc_ac97_ops); | 269 | EXPORT_SYMBOL_GPL(soc_ac97_ops); |
238 | 270 | ||
239 | #ifdef CONFIG_PM | 271 | #ifdef CONFIG_PM |
240 | static int bf5xx_ac97_suspend(struct platform_device *pdev, | 272 | static int bf5xx_ac97_suspend(struct snd_soc_dai *dai) |
241 | struct snd_soc_dai *dai) | ||
242 | { | 273 | { |
243 | struct sport_device *sport = | 274 | struct sport_device *sport = |
244 | (struct sport_device *)dai->private_data; | 275 | (struct sport_device *)dai->private_data; |
@@ -253,8 +284,7 @@ static int bf5xx_ac97_suspend(struct platform_device *pdev, | |||
253 | return 0; | 284 | return 0; |
254 | } | 285 | } |
255 | 286 | ||
256 | static int bf5xx_ac97_resume(struct platform_device *pdev, | 287 | static int bf5xx_ac97_resume(struct snd_soc_dai *dai) |
257 | struct snd_soc_dai *dai) | ||
258 | { | 288 | { |
259 | int ret; | 289 | int ret; |
260 | struct sport_device *sport = | 290 | struct sport_device *sport = |
@@ -297,20 +327,15 @@ static int bf5xx_ac97_resume(struct platform_device *pdev, | |||
297 | static int bf5xx_ac97_probe(struct platform_device *pdev, | 327 | static int bf5xx_ac97_probe(struct platform_device *pdev, |
298 | struct snd_soc_dai *dai) | 328 | struct snd_soc_dai *dai) |
299 | { | 329 | { |
300 | int ret; | 330 | int ret = 0; |
301 | #if defined(CONFIG_BF54x) | ||
302 | u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1, | ||
303 | PIN_REQ_SPORT_2, PIN_REQ_SPORT_3}; | ||
304 | #else | ||
305 | u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1}; | ||
306 | #endif | ||
307 | cmd_count = (int *)get_zeroed_page(GFP_KERNEL); | 331 | cmd_count = (int *)get_zeroed_page(GFP_KERNEL); |
308 | if (cmd_count == NULL) | 332 | if (cmd_count == NULL) |
309 | return -ENOMEM; | 333 | return -ENOMEM; |
310 | 334 | ||
311 | if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) { | 335 | if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) { |
312 | pr_err("Requesting Peripherals failed\n"); | 336 | pr_err("Requesting Peripherals failed\n"); |
313 | return -EFAULT; | 337 | ret = -EFAULT; |
338 | goto peripheral_err; | ||
314 | } | 339 | } |
315 | 340 | ||
316 | #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET | 341 | #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET |
@@ -318,54 +343,54 @@ static int bf5xx_ac97_probe(struct platform_device *pdev, | |||
318 | if (gpio_request(CONFIG_SND_BF5XX_RESET_GPIO_NUM, "SND_AD198x RESET")) { | 343 | if (gpio_request(CONFIG_SND_BF5XX_RESET_GPIO_NUM, "SND_AD198x RESET")) { |
319 | pr_err("Failed to request GPIO_%d for reset\n", | 344 | pr_err("Failed to request GPIO_%d for reset\n", |
320 | CONFIG_SND_BF5XX_RESET_GPIO_NUM); | 345 | CONFIG_SND_BF5XX_RESET_GPIO_NUM); |
321 | peripheral_free_list(&sport_req[sport_num][0]); | 346 | ret = -1; |
322 | return -1; | 347 | goto gpio_err; |
323 | } | 348 | } |
324 | gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1); | 349 | gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1); |
325 | #endif | 350 | #endif |
326 | sport_handle = sport_init(&sport_params[sport_num], 2, \ | 351 | sport_handle = sport_init(&sport_params[sport_num], 2, \ |
327 | sizeof(struct ac97_frame), NULL); | 352 | sizeof(struct ac97_frame), NULL); |
328 | if (!sport_handle) { | 353 | if (!sport_handle) { |
329 | peripheral_free_list(&sport_req[sport_num][0]); | 354 | ret = -ENODEV; |
330 | #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET | 355 | goto sport_err; |
331 | gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); | ||
332 | #endif | ||
333 | return -ENODEV; | ||
334 | } | 356 | } |
335 | /*SPORT works in TDM mode to simulate AC97 transfers*/ | 357 | /*SPORT works in TDM mode to simulate AC97 transfers*/ |
336 | ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1); | 358 | ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1); |
337 | if (ret) { | 359 | if (ret) { |
338 | pr_err("SPORT is busy!\n"); | 360 | pr_err("SPORT is busy!\n"); |
339 | kfree(sport_handle); | 361 | ret = -EBUSY; |
340 | peripheral_free_list(&sport_req[sport_num][0]); | 362 | goto sport_config_err; |
341 | #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET | ||
342 | gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); | ||
343 | #endif | ||
344 | return -EBUSY; | ||
345 | } | 363 | } |
346 | 364 | ||
347 | ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1)); | 365 | ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1)); |
348 | if (ret) { | 366 | if (ret) { |
349 | pr_err("SPORT is busy!\n"); | 367 | pr_err("SPORT is busy!\n"); |
350 | kfree(sport_handle); | 368 | ret = -EBUSY; |
351 | peripheral_free_list(&sport_req[sport_num][0]); | 369 | goto sport_config_err; |
352 | #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET | ||
353 | gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); | ||
354 | #endif | ||
355 | return -EBUSY; | ||
356 | } | 370 | } |
357 | 371 | ||
358 | ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1)); | 372 | ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1)); |
359 | if (ret) { | 373 | if (ret) { |
360 | pr_err("SPORT is busy!\n"); | 374 | pr_err("SPORT is busy!\n"); |
361 | kfree(sport_handle); | 375 | ret = -EBUSY; |
362 | peripheral_free_list(&sport_req[sport_num][0]); | 376 | goto sport_config_err; |
363 | #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET | ||
364 | gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); | ||
365 | #endif | ||
366 | return -EBUSY; | ||
367 | } | 377 | } |
378 | |||
368 | return 0; | 379 | return 0; |
380 | |||
381 | sport_config_err: | ||
382 | kfree(sport_handle); | ||
383 | sport_err: | ||
384 | #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET | ||
385 | gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); | ||
386 | #endif | ||
387 | gpio_err: | ||
388 | peripheral_free_list(&sport_req[sport_num][0]); | ||
389 | peripheral_err: | ||
390 | free_page((unsigned long)cmd_count); | ||
391 | cmd_count = NULL; | ||
392 | |||
393 | return ret; | ||
369 | } | 394 | } |
370 | 395 | ||
371 | static void bf5xx_ac97_remove(struct platform_device *pdev, | 396 | static void bf5xx_ac97_remove(struct platform_device *pdev, |
@@ -373,6 +398,7 @@ static void bf5xx_ac97_remove(struct platform_device *pdev, | |||
373 | { | 398 | { |
374 | free_page((unsigned long)cmd_count); | 399 | free_page((unsigned long)cmd_count); |
375 | cmd_count = NULL; | 400 | cmd_count = NULL; |
401 | peripheral_free_list(&sport_req[sport_num][0]); | ||
376 | #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET | 402 | #ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET |
377 | gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); | 403 | gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); |
378 | #endif | 404 | #endif |
@@ -381,7 +407,7 @@ static void bf5xx_ac97_remove(struct platform_device *pdev, | |||
381 | struct snd_soc_dai bfin_ac97_dai = { | 407 | struct snd_soc_dai bfin_ac97_dai = { |
382 | .name = "bf5xx-ac97", | 408 | .name = "bf5xx-ac97", |
383 | .id = 0, | 409 | .id = 0, |
384 | .type = SND_SOC_DAI_AC97, | 410 | .ac97_control = 1, |
385 | .probe = bf5xx_ac97_probe, | 411 | .probe = bf5xx_ac97_probe, |
386 | .remove = bf5xx_ac97_remove, | 412 | .remove = bf5xx_ac97_remove, |
387 | .suspend = bf5xx_ac97_suspend, | 413 | .suspend = bf5xx_ac97_suspend, |
@@ -389,7 +415,11 @@ struct snd_soc_dai bfin_ac97_dai = { | |||
389 | .playback = { | 415 | .playback = { |
390 | .stream_name = "AC97 Playback", | 416 | .stream_name = "AC97 Playback", |
391 | .channels_min = 2, | 417 | .channels_min = 2, |
418 | #if defined(CONFIG_SND_BF5XX_MULTICHAN_SUPPORT) | ||
419 | .channels_max = 6, | ||
420 | #else | ||
392 | .channels_max = 2, | 421 | .channels_max = 2, |
422 | #endif | ||
393 | .rates = SNDRV_PCM_RATE_48000, | 423 | .rates = SNDRV_PCM_RATE_48000, |
394 | .formats = SNDRV_PCM_FMTBIT_S16_LE, }, | 424 | .formats = SNDRV_PCM_FMTBIT_S16_LE, }, |
395 | .capture = { | 425 | .capture = { |
@@ -401,6 +431,18 @@ struct snd_soc_dai bfin_ac97_dai = { | |||
401 | }; | 431 | }; |
402 | EXPORT_SYMBOL_GPL(bfin_ac97_dai); | 432 | EXPORT_SYMBOL_GPL(bfin_ac97_dai); |
403 | 433 | ||
434 | static int __init bfin_ac97_init(void) | ||
435 | { | ||
436 | return snd_soc_register_dai(&bfin_ac97_dai); | ||
437 | } | ||
438 | module_init(bfin_ac97_init); | ||
439 | |||
440 | static void __exit bfin_ac97_exit(void) | ||
441 | { | ||
442 | snd_soc_unregister_dai(&bfin_ac97_dai); | ||
443 | } | ||
444 | module_exit(bfin_ac97_exit); | ||
445 | |||
404 | MODULE_AUTHOR("Roy Huang"); | 446 | MODULE_AUTHOR("Roy Huang"); |
405 | MODULE_DESCRIPTION("AC97 driver for ADI Blackfin"); | 447 | MODULE_DESCRIPTION("AC97 driver for ADI Blackfin"); |
406 | MODULE_LICENSE("GPL"); | 448 | MODULE_LICENSE("GPL"); |