ALSA: aloop: Add support for the non-interleaved access mode
[linux-2.6-microblaze.git] / sound / drivers / aloop.c
index a38e602..ab116b1 100644 (file)
@@ -158,6 +158,9 @@ struct loopback_pcm {
        unsigned long last_jiffies;
        /* If jiffies timer is used */
        struct timer_list timer;
+
+       /* size of per channel buffer in case of non-interleaved access */
+       unsigned int channel_buf_n;
 };
 
 static struct platform_device *devices[SNDRV_CARDS];
@@ -335,7 +338,8 @@ static int loopback_check_format(struct loopback_cable *cable, int stream)
                                                        substream->runtime;
        check = runtime->format != cruntime->format ||
                runtime->rate != cruntime->rate ||
-               runtime->channels != cruntime->channels;
+               runtime->channels != cruntime->channels ||
+               runtime->access != cruntime->access;
        if (!check)
                return 0;
        if (stream == SNDRV_PCM_STREAM_CAPTURE) {
@@ -472,6 +476,7 @@ static int loopback_prepare(struct snd_pcm_substream *substream)
 
        dpcm->buf_pos = 0;
        dpcm->pcm_buffer_size = frames_to_bytes(runtime, runtime->buffer_size);
+       dpcm->channel_buf_n = dpcm->pcm_buffer_size / runtime->channels;
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
                /* clear capture buffer */
                dpcm->silent_size = dpcm->pcm_buffer_size;
@@ -522,6 +527,22 @@ static void clear_capture_buf(struct loopback_pcm *dpcm, unsigned int bytes)
        }
 }
 
+static void copy_play_buf_part_n(struct loopback_pcm *play, struct loopback_pcm *capt,
+                                unsigned int size, unsigned int src_off, unsigned int dst_off)
+{
+       unsigned int channels = capt->substream->runtime->channels;
+       unsigned int size_p_ch = size / channels;
+       unsigned int src_off_ch = src_off / channels;
+       unsigned int dst_off_ch = dst_off / channels;
+       int i;
+
+       for (i = 0; i < channels; i++) {
+               memcpy(capt->substream->runtime->dma_area + capt->channel_buf_n * i + dst_off_ch,
+                      play->substream->runtime->dma_area + play->channel_buf_n * i + src_off_ch,
+                      size_p_ch);
+       }
+}
+
 static void copy_play_buf(struct loopback_pcm *play,
                          struct loopback_pcm *capt,
                          unsigned int bytes)
@@ -556,7 +577,11 @@ static void copy_play_buf(struct loopback_pcm *play,
                        size = play->pcm_buffer_size - src_off;
                if (dst_off + size > capt->pcm_buffer_size)
                        size = capt->pcm_buffer_size - dst_off;
-               memcpy(dst + dst_off, src + src_off, size);
+               if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED ||
+                   runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED)
+                       copy_play_buf_part_n(play, capt, size, src_off, dst_off);
+               else
+                       memcpy(dst + dst_off, src + src_off, size);
                capt->silent_size = 0;
                bytes -= size;
                if (!bytes)
@@ -878,7 +903,7 @@ static const struct snd_pcm_hardware loopback_pcm_hardware =
 {
        .info =         (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
                         SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE |
-                        SNDRV_PCM_INFO_RESUME),
+                        SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_NONINTERLEAVED),
        .formats =      (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
                         SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |
                         SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |