diff options
Diffstat (limited to 'sound/soc/blackfin/bf5xx-ac97-pcm.c')
| -rw-r--r-- | sound/soc/blackfin/bf5xx-ac97-pcm.c | 113 |
1 files changed, 62 insertions, 51 deletions
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c index 25e50d2ea1ec..8067cfafa3a7 100644 --- a/sound/soc/blackfin/bf5xx-ac97-pcm.c +++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c | |||
| @@ -43,24 +43,34 @@ | |||
| 43 | #include "bf5xx-ac97.h" | 43 | #include "bf5xx-ac97.h" |
| 44 | #include "bf5xx-sport.h" | 44 | #include "bf5xx-sport.h" |
| 45 | 45 | ||
| 46 | #if defined(CONFIG_SND_MMAP_SUPPORT) | 46 | static unsigned int ac97_chan_mask[] = { |
| 47 | SP_FL, /* Mono */ | ||
| 48 | SP_STEREO, /* Stereo */ | ||
| 49 | SP_2DOT1, /* 2.1*/ | ||
| 50 | SP_QUAD,/*Quadraquic*/ | ||
| 51 | SP_FL | SP_FR | SP_FC | SP_SL | SP_SR,/*5 channels */ | ||
| 52 | SP_5DOT1, /* 5.1 */ | ||
| 53 | }; | ||
| 54 | |||
| 55 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) | ||
| 47 | static void bf5xx_mmap_copy(struct snd_pcm_substream *substream, | 56 | static void bf5xx_mmap_copy(struct snd_pcm_substream *substream, |
| 48 | snd_pcm_uframes_t count) | 57 | snd_pcm_uframes_t count) |
| 49 | { | 58 | { |
| 50 | struct snd_pcm_runtime *runtime = substream->runtime; | 59 | struct snd_pcm_runtime *runtime = substream->runtime; |
| 51 | struct sport_device *sport = runtime->private_data; | 60 | struct sport_device *sport = runtime->private_data; |
| 61 | unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1]; | ||
| 52 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 62 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
| 53 | bf5xx_pcm_to_ac97( | 63 | bf5xx_pcm_to_ac97((struct ac97_frame *)sport->tx_dma_buf + |
| 54 | (struct ac97_frame *)sport->tx_dma_buf + sport->tx_pos, | 64 | sport->tx_pos, (__u16 *)runtime->dma_area + sport->tx_pos * |
| 55 | (__u32 *)runtime->dma_area + sport->tx_pos, count); | 65 | runtime->channels, count, chan_mask); |
| 56 | sport->tx_pos += runtime->period_size; | 66 | sport->tx_pos += runtime->period_size; |
| 57 | if (sport->tx_pos >= runtime->buffer_size) | 67 | if (sport->tx_pos >= runtime->buffer_size) |
| 58 | sport->tx_pos %= runtime->buffer_size; | 68 | sport->tx_pos %= runtime->buffer_size; |
| 59 | sport->tx_delay_pos = sport->tx_pos; | 69 | sport->tx_delay_pos = sport->tx_pos; |
| 60 | } else { | 70 | } else { |
| 61 | bf5xx_ac97_to_pcm( | 71 | bf5xx_ac97_to_pcm((struct ac97_frame *)sport->rx_dma_buf + |
| 62 | (struct ac97_frame *)sport->rx_dma_buf + sport->rx_pos, | 72 | sport->rx_pos, (__u16 *)runtime->dma_area + sport->rx_pos * |
| 63 | (__u32 *)runtime->dma_area + sport->rx_pos, count); | 73 | runtime->channels, count); |
| 64 | sport->rx_pos += runtime->period_size; | 74 | sport->rx_pos += runtime->period_size; |
| 65 | if (sport->rx_pos >= runtime->buffer_size) | 75 | if (sport->rx_pos >= runtime->buffer_size) |
| 66 | sport->rx_pos %= runtime->buffer_size; | 76 | sport->rx_pos %= runtime->buffer_size; |
| @@ -71,7 +81,7 @@ static void bf5xx_mmap_copy(struct snd_pcm_substream *substream, | |||
| 71 | static void bf5xx_dma_irq(void *data) | 81 | static void bf5xx_dma_irq(void *data) |
| 72 | { | 82 | { |
| 73 | struct snd_pcm_substream *pcm = data; | 83 | struct snd_pcm_substream *pcm = data; |
| 74 | #if defined(CONFIG_SND_MMAP_SUPPORT) | 84 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
| 75 | struct snd_pcm_runtime *runtime = pcm->runtime; | 85 | struct snd_pcm_runtime *runtime = pcm->runtime; |
| 76 | struct sport_device *sport = runtime->private_data; | 86 | struct sport_device *sport = runtime->private_data; |
| 77 | bf5xx_mmap_copy(pcm, runtime->period_size); | 87 | bf5xx_mmap_copy(pcm, runtime->period_size); |
| @@ -90,17 +100,14 @@ static void bf5xx_dma_irq(void *data) | |||
| 90 | * The total rx/tx buffer is for ac97 frame to hold all pcm data | 100 | * The total rx/tx buffer is for ac97 frame to hold all pcm data |
| 91 | * is 0x20000 * sizeof(struct ac97_frame) / 4. | 101 | * is 0x20000 * sizeof(struct ac97_frame) / 4. |
| 92 | */ | 102 | */ |
| 93 | #ifdef CONFIG_SND_MMAP_SUPPORT | ||
| 94 | static const struct snd_pcm_hardware bf5xx_pcm_hardware = { | 103 | static const struct snd_pcm_hardware bf5xx_pcm_hardware = { |
| 95 | .info = SNDRV_PCM_INFO_INTERLEAVED | | 104 | .info = SNDRV_PCM_INFO_INTERLEAVED | |
| 105 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) | ||
| 96 | SNDRV_PCM_INFO_MMAP | | 106 | SNDRV_PCM_INFO_MMAP | |
| 97 | SNDRV_PCM_INFO_MMAP_VALID | | 107 | SNDRV_PCM_INFO_MMAP_VALID | |
| 98 | SNDRV_PCM_INFO_BLOCK_TRANSFER, | ||
| 99 | #else | ||
| 100 | static const struct snd_pcm_hardware bf5xx_pcm_hardware = { | ||
| 101 | .info = SNDRV_PCM_INFO_INTERLEAVED | | ||
| 102 | SNDRV_PCM_INFO_BLOCK_TRANSFER, | ||
| 103 | #endif | 108 | #endif |
| 109 | SNDRV_PCM_INFO_BLOCK_TRANSFER, | ||
| 110 | |||
| 104 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | 111 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
| 105 | .period_bytes_min = 32, | 112 | .period_bytes_min = 32, |
| 106 | .period_bytes_max = 0x10000, | 113 | .period_bytes_max = 0x10000, |
| @@ -123,10 +130,20 @@ static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, | |||
| 123 | 130 | ||
| 124 | static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) | 131 | static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) |
| 125 | { | 132 | { |
| 133 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) | ||
| 126 | struct snd_pcm_runtime *runtime = substream->runtime; | 134 | struct snd_pcm_runtime *runtime = substream->runtime; |
| 135 | struct sport_device *sport = runtime->private_data; | ||
| 127 | 136 | ||
| 128 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 137 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
| 129 | memset(runtime->dma_area, 0, runtime->buffer_size); | 138 | sport->once = 0; |
| 139 | if (runtime->dma_area) | ||
| 140 | memset(runtime->dma_area, 0, runtime->buffer_size); | ||
| 141 | memset(sport->tx_dma_buf, 0, runtime->buffer_size * | ||
| 142 | sizeof(struct ac97_frame)); | ||
| 143 | } else | ||
| 144 | memset(sport->rx_dma_buf, 0, runtime->buffer_size * | ||
| 145 | sizeof(struct ac97_frame)); | ||
| 146 | #endif | ||
| 130 | snd_pcm_lib_free_pages(substream); | 147 | snd_pcm_lib_free_pages(substream); |
| 131 | return 0; | 148 | return 0; |
| 132 | } | 149 | } |
| @@ -139,7 +156,7 @@ static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream) | |||
| 139 | /* An intermediate buffer is introduced for implementing mmap for | 156 | /* An intermediate buffer is introduced for implementing mmap for |
| 140 | * SPORT working in TMD mode(include AC97). | 157 | * SPORT working in TMD mode(include AC97). |
| 141 | */ | 158 | */ |
| 142 | #if defined(CONFIG_SND_MMAP_SUPPORT) | 159 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
| 143 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 160 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
| 144 | sport_set_tx_callback(sport, bf5xx_dma_irq, substream); | 161 | sport_set_tx_callback(sport, bf5xx_dma_irq, substream); |
| 145 | sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods, | 162 | sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods, |
| @@ -173,24 +190,24 @@ static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | |||
| 173 | switch (cmd) { | 190 | switch (cmd) { |
| 174 | case SNDRV_PCM_TRIGGER_START: | 191 | case SNDRV_PCM_TRIGGER_START: |
| 175 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 192 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
| 193 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) | ||
| 176 | bf5xx_mmap_copy(substream, runtime->period_size); | 194 | bf5xx_mmap_copy(substream, runtime->period_size); |
| 177 | snd_pcm_period_elapsed(substream); | ||
| 178 | sport->tx_delay_pos = 0; | 195 | sport->tx_delay_pos = 0; |
| 196 | #endif | ||
| 179 | sport_tx_start(sport); | 197 | sport_tx_start(sport); |
| 180 | } | 198 | } else |
| 181 | else | ||
| 182 | sport_rx_start(sport); | 199 | sport_rx_start(sport); |
| 183 | break; | 200 | break; |
| 184 | case SNDRV_PCM_TRIGGER_STOP: | 201 | case SNDRV_PCM_TRIGGER_STOP: |
| 185 | case SNDRV_PCM_TRIGGER_SUSPEND: | 202 | case SNDRV_PCM_TRIGGER_SUSPEND: |
| 186 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 203 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
| 187 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 204 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
| 188 | #if defined(CONFIG_SND_MMAP_SUPPORT) | 205 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
| 189 | sport->tx_pos = 0; | 206 | sport->tx_pos = 0; |
| 190 | #endif | 207 | #endif |
| 191 | sport_tx_stop(sport); | 208 | sport_tx_stop(sport); |
| 192 | } else { | 209 | } else { |
| 193 | #if defined(CONFIG_SND_MMAP_SUPPORT) | 210 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
| 194 | sport->rx_pos = 0; | 211 | sport->rx_pos = 0; |
| 195 | #endif | 212 | #endif |
| 196 | sport_rx_stop(sport); | 213 | sport_rx_stop(sport); |
| @@ -208,7 +225,7 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) | |||
| 208 | struct sport_device *sport = runtime->private_data; | 225 | struct sport_device *sport = runtime->private_data; |
| 209 | unsigned int curr; | 226 | unsigned int curr; |
| 210 | 227 | ||
| 211 | #if defined(CONFIG_SND_MMAP_SUPPORT) | 228 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
| 212 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 229 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
| 213 | curr = sport->tx_delay_pos; | 230 | curr = sport->tx_delay_pos; |
| 214 | else | 231 | else |
| @@ -249,22 +266,7 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream) | |||
| 249 | return ret; | 266 | return ret; |
| 250 | } | 267 | } |
| 251 | 268 | ||
| 252 | static int bf5xx_pcm_close(struct snd_pcm_substream *substream) | 269 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
| 253 | { | ||
| 254 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
| 255 | struct sport_device *sport = runtime->private_data; | ||
| 256 | |||
| 257 | pr_debug("%s enter\n", __func__); | ||
| 258 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
| 259 | sport->once = 0; | ||
| 260 | memset(sport->tx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame)); | ||
| 261 | } else | ||
| 262 | memset(sport->rx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame)); | ||
| 263 | |||
| 264 | return 0; | ||
| 265 | } | ||
| 266 | |||
| 267 | #ifdef CONFIG_SND_MMAP_SUPPORT | ||
| 268 | static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream, | 270 | static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream, |
| 269 | struct vm_area_struct *vma) | 271 | struct vm_area_struct *vma) |
| 270 | { | 272 | { |
| @@ -281,32 +283,29 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, | |||
| 281 | void __user *buf, snd_pcm_uframes_t count) | 283 | void __user *buf, snd_pcm_uframes_t count) |
| 282 | { | 284 | { |
| 283 | struct snd_pcm_runtime *runtime = substream->runtime; | 285 | struct snd_pcm_runtime *runtime = substream->runtime; |
| 284 | 286 | unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1]; | |
| 285 | pr_debug("%s copy pos:0x%lx count:0x%lx\n", | 287 | pr_debug("%s copy pos:0x%lx count:0x%lx\n", |
| 286 | substream->stream ? "Capture" : "Playback", pos, count); | 288 | substream->stream ? "Capture" : "Playback", pos, count); |
| 287 | 289 | ||
| 288 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 290 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
| 289 | bf5xx_pcm_to_ac97( | 291 | bf5xx_pcm_to_ac97((struct ac97_frame *)runtime->dma_area + pos, |
| 290 | (struct ac97_frame *)runtime->dma_area + pos, | 292 | (__u16 *)buf, count, chan_mask); |
| 291 | buf, count); | ||
| 292 | else | 293 | else |
| 293 | bf5xx_ac97_to_pcm( | 294 | bf5xx_ac97_to_pcm((struct ac97_frame *)runtime->dma_area + pos, |
| 294 | (struct ac97_frame *)runtime->dma_area + pos, | 295 | (__u16 *)buf, count); |
| 295 | buf, count); | ||
| 296 | return 0; | 296 | return 0; |
| 297 | } | 297 | } |
| 298 | #endif | 298 | #endif |
| 299 | 299 | ||
| 300 | struct snd_pcm_ops bf5xx_pcm_ac97_ops = { | 300 | struct snd_pcm_ops bf5xx_pcm_ac97_ops = { |
| 301 | .open = bf5xx_pcm_open, | 301 | .open = bf5xx_pcm_open, |
| 302 | .close = bf5xx_pcm_close, | ||
| 303 | .ioctl = snd_pcm_lib_ioctl, | 302 | .ioctl = snd_pcm_lib_ioctl, |
| 304 | .hw_params = bf5xx_pcm_hw_params, | 303 | .hw_params = bf5xx_pcm_hw_params, |
| 305 | .hw_free = bf5xx_pcm_hw_free, | 304 | .hw_free = bf5xx_pcm_hw_free, |
| 306 | .prepare = bf5xx_pcm_prepare, | 305 | .prepare = bf5xx_pcm_prepare, |
| 307 | .trigger = bf5xx_pcm_trigger, | 306 | .trigger = bf5xx_pcm_trigger, |
| 308 | .pointer = bf5xx_pcm_pointer, | 307 | .pointer = bf5xx_pcm_pointer, |
| 309 | #ifdef CONFIG_SND_MMAP_SUPPORT | 308 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
| 310 | .mmap = bf5xx_pcm_mmap, | 309 | .mmap = bf5xx_pcm_mmap, |
| 311 | #else | 310 | #else |
| 312 | .copy = bf5xx_pcm_copy, | 311 | .copy = bf5xx_pcm_copy, |
| @@ -344,7 +343,7 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) | |||
| 344 | * Need to allocate local buffer when enable | 343 | * Need to allocate local buffer when enable |
| 345 | * MMAP for SPORT working in TMD mode (include AC97). | 344 | * MMAP for SPORT working in TMD mode (include AC97). |
| 346 | */ | 345 | */ |
| 347 | #if defined(CONFIG_SND_MMAP_SUPPORT) | 346 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
| 348 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 347 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
| 349 | if (!sport_handle->tx_dma_buf) { | 348 | if (!sport_handle->tx_dma_buf) { |
| 350 | sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \ | 349 | sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \ |
| @@ -381,7 +380,7 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) | |||
| 381 | struct snd_pcm_substream *substream; | 380 | struct snd_pcm_substream *substream; |
| 382 | struct snd_dma_buffer *buf; | 381 | struct snd_dma_buffer *buf; |
| 383 | int stream; | 382 | int stream; |
| 384 | #if defined(CONFIG_SND_MMAP_SUPPORT) | 383 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
| 385 | size_t size = bf5xx_pcm_hardware.buffer_bytes_max * | 384 | size_t size = bf5xx_pcm_hardware.buffer_bytes_max * |
| 386 | sizeof(struct ac97_frame) / 4; | 385 | sizeof(struct ac97_frame) / 4; |
| 387 | #endif | 386 | #endif |
| @@ -395,7 +394,7 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) | |||
| 395 | continue; | 394 | continue; |
| 396 | dma_free_coherent(NULL, buf->bytes, buf->area, 0); | 395 | dma_free_coherent(NULL, buf->bytes, buf->area, 0); |
| 397 | buf->area = NULL; | 396 | buf->area = NULL; |
| 398 | #if defined(CONFIG_SND_MMAP_SUPPORT) | 397 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
| 399 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 398 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
| 400 | if (sport_handle->tx_dma_buf) | 399 | if (sport_handle->tx_dma_buf) |
| 401 | dma_free_coherent(NULL, size, \ | 400 | dma_free_coherent(NULL, size, \ |
| @@ -452,6 +451,18 @@ struct snd_soc_platform bf5xx_ac97_soc_platform = { | |||
| 452 | }; | 451 | }; |
| 453 | EXPORT_SYMBOL_GPL(bf5xx_ac97_soc_platform); | 452 | EXPORT_SYMBOL_GPL(bf5xx_ac97_soc_platform); |
| 454 | 453 | ||
| 454 | static int __init bfin_ac97_init(void) | ||
| 455 | { | ||
| 456 | return snd_soc_register_platform(&bf5xx_ac97_soc_platform); | ||
| 457 | } | ||
| 458 | module_init(bfin_ac97_init); | ||
| 459 | |||
| 460 | static void __exit bfin_ac97_exit(void) | ||
| 461 | { | ||
| 462 | snd_soc_unregister_platform(&bf5xx_ac97_soc_platform); | ||
| 463 | } | ||
| 464 | module_exit(bfin_ac97_exit); | ||
| 465 | |||
| 455 | MODULE_AUTHOR("Cliff Cai"); | 466 | MODULE_AUTHOR("Cliff Cai"); |
| 456 | MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module"); | 467 | MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module"); |
| 457 | MODULE_LICENSE("GPL"); | 468 | MODULE_LICENSE("GPL"); |
