aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>2014-04-25 09:44:49 -0400
committerTakashi Iwai <tiwai@suse.de>2014-05-26 08:13:59 -0400
commit7b3b0d8583c926547fed8e493344cd8b30aa847d (patch)
tree3689f96fd1c296fdf66c1793838949b359c501bf
parentccccad8646fad5f86f09adb040e02ca1a838585c (diff)
ALSA: firewire-lib: Add support for duplex streams synchronization in blocking mode
Generally, the devices can synchronize to handle 'presentation timestamp' in CIP packets. This commit adds functionality to pick up this timestamp from in-packets transmitted by the device, then use it for out packets. In current implementation, this module generated the timestamp by itself. This is 'SYT Match' mode. Then drivers with this module acts as synchronization master. This commit allows this module to act as synchronization slave. This commit restricts this mechanism is only available in blocking mode because handling the timestamp in non-blocking mode is more complicated than in blocking mode. Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/firewire/amdtp.c75
-rw-r--r--sound/firewire/amdtp.h45
2 files changed, 110 insertions, 10 deletions
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index 24e9a961fe7e..8498155c15a6 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -11,6 +11,7 @@
11#include <linux/firewire.h> 11#include <linux/firewire.h>
12#include <linux/module.h> 12#include <linux/module.h>
13#include <linux/slab.h> 13#include <linux/slab.h>
14#include <linux/sched.h>
14#include <sound/pcm.h> 15#include <sound/pcm.h>
15#include <sound/rawmidi.h> 16#include <sound/rawmidi.h>
16#include "amdtp.h" 17#include "amdtp.h"
@@ -72,6 +73,10 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
72 tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s); 73 tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s);
73 s->packet_index = 0; 74 s->packet_index = 0;
74 75
76 init_waitqueue_head(&s->callback_wait);
77 s->callbacked = false;
78 s->sync_slave = NULL;
79
75 return 0; 80 return 0;
76} 81}
77EXPORT_SYMBOL(amdtp_stream_init); 82EXPORT_SYMBOL(amdtp_stream_init);
@@ -585,7 +590,10 @@ static int queue_packet(struct amdtp_stream *s,
585 unsigned int payload_length, bool skip) 590 unsigned int payload_length, bool skip)
586{ 591{
587 struct fw_iso_packet p = {0}; 592 struct fw_iso_packet p = {0};
588 int err; 593 int err = 0;
594
595 if (IS_ERR(s->context))
596 goto end;
589 597
590 p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL); 598 p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
591 p.tag = TAG_CIP; 599 p.tag = TAG_CIP;
@@ -765,7 +773,7 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
765 void *private_data) 773 void *private_data)
766{ 774{
767 struct amdtp_stream *s = private_data; 775 struct amdtp_stream *s = private_data;
768 unsigned int p, packets, payload_quadlets; 776 unsigned int p, syt, packets, payload_quadlets;
769 __be32 *buffer, *headers = header; 777 __be32 *buffer, *headers = header;
770 778
771 /* The number of packets in buffer */ 779 /* The number of packets in buffer */
@@ -773,18 +781,71 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
773 781
774 for (p = 0; p < packets; p++) { 782 for (p = 0; p < packets; p++) {
775 if (s->packet_index < 0) 783 if (s->packet_index < 0)
776 return; 784 break;
785
777 buffer = s->buffer.packets[s->packet_index].buffer; 786 buffer = s->buffer.packets[s->packet_index].buffer;
778 787
788 /* Process sync slave stream */
789 if (s->sync_slave && s->sync_slave->callbacked) {
790 syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
791 handle_out_packet(s->sync_slave, syt);
792 }
793
779 /* The number of quadlets in this packet */ 794 /* The number of quadlets in this packet */
780 payload_quadlets = 795 payload_quadlets =
781 (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4; 796 (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
782 handle_in_packet(s, payload_quadlets, buffer); 797 handle_in_packet(s, payload_quadlets, buffer);
783 } 798 }
784 799
800 /* Queueing error or detecting discontinuity */
801 if (s->packet_index < 0) {
802 /* Abort sync slave. */
803 if (s->sync_slave) {
804 s->sync_slave->packet_index = -1;
805 amdtp_stream_pcm_abort(s->sync_slave);
806 }
807 return;
808 }
809
810 /* when sync to device, flush the packets for slave stream */
811 if (s->sync_slave && s->sync_slave->callbacked)
812 fw_iso_context_queue_flush(s->sync_slave->context);
813
785 fw_iso_context_queue_flush(s->context); 814 fw_iso_context_queue_flush(s->context);
786} 815}
787 816
817/* processing is done by master callback */
818static void slave_stream_callback(struct fw_iso_context *context, u32 cycle,
819 size_t header_length, void *header,
820 void *private_data)
821{
822 return;
823}
824
825/* this is executed one time */
826static void amdtp_stream_first_callback(struct fw_iso_context *context,
827 u32 cycle, size_t header_length,
828 void *header, void *private_data)
829{
830 struct amdtp_stream *s = private_data;
831
832 /*
833 * For in-stream, first packet has come.
834 * For out-stream, prepared to transmit first packet
835 */
836 s->callbacked = true;
837 wake_up(&s->callback_wait);
838
839 if (s->direction == AMDTP_IN_STREAM)
840 context->callback.sc = in_stream_callback;
841 else if ((s->flags & CIP_BLOCKING) && (s->flags & CIP_SYNC_TO_DEVICE))
842 context->callback.sc = slave_stream_callback;
843 else
844 context->callback.sc = out_stream_callback;
845
846 context->callback.sc(context, cycle, header_length, header, s);
847}
848
788/** 849/**
789 * amdtp_stream_start - start transferring packets 850 * amdtp_stream_start - start transferring packets
790 * @s: the AMDTP stream to start 851 * @s: the AMDTP stream to start
@@ -811,7 +872,6 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
811 }; 872 };
812 unsigned int header_size; 873 unsigned int header_size;
813 enum dma_data_direction dir; 874 enum dma_data_direction dir;
814 fw_iso_callback_t cb;
815 int type, err; 875 int type, err;
816 876
817 mutex_lock(&s->mutex); 877 mutex_lock(&s->mutex);
@@ -832,12 +892,10 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
832 dir = DMA_FROM_DEVICE; 892 dir = DMA_FROM_DEVICE;
833 type = FW_ISO_CONTEXT_RECEIVE; 893 type = FW_ISO_CONTEXT_RECEIVE;
834 header_size = IN_PACKET_HEADER_SIZE; 894 header_size = IN_PACKET_HEADER_SIZE;
835 cb = in_stream_callback;
836 } else { 895 } else {
837 dir = DMA_TO_DEVICE; 896 dir = DMA_TO_DEVICE;
838 type = FW_ISO_CONTEXT_TRANSMIT; 897 type = FW_ISO_CONTEXT_TRANSMIT;
839 header_size = OUT_PACKET_HEADER_SIZE; 898 header_size = OUT_PACKET_HEADER_SIZE;
840 cb = out_stream_callback;
841 } 899 }
842 err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, 900 err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH,
843 amdtp_stream_get_max_payload(s), dir); 901 amdtp_stream_get_max_payload(s), dir);
@@ -846,7 +904,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
846 904
847 s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, 905 s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
848 type, channel, speed, header_size, 906 type, channel, speed, header_size,
849 cb, s); 907 amdtp_stream_first_callback, s);
850 if (IS_ERR(s->context)) { 908 if (IS_ERR(s->context)) {
851 err = PTR_ERR(s->context); 909 err = PTR_ERR(s->context);
852 if (err == -EBUSY) 910 if (err == -EBUSY)
@@ -868,6 +926,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
868 } while (s->packet_index > 0); 926 } while (s->packet_index > 0);
869 927
870 /* NOTE: TAG1 matches CIP. This just affects in stream. */ 928 /* NOTE: TAG1 matches CIP. This just affects in stream. */
929 s->callbacked = false;
871 err = fw_iso_context_start(s->context, -1, 0, 930 err = fw_iso_context_start(s->context, -1, 0,
872 FW_ISO_CONTEXT_MATCH_TAG1); 931 FW_ISO_CONTEXT_MATCH_TAG1);
873 if (err < 0) 932 if (err < 0)
@@ -940,6 +999,8 @@ void amdtp_stream_stop(struct amdtp_stream *s)
940 s->context = ERR_PTR(-1); 999 s->context = ERR_PTR(-1);
941 iso_packets_buffer_destroy(&s->buffer, s->unit); 1000 iso_packets_buffer_destroy(&s->buffer, s->unit);
942 1001
1002 s->callbacked = false;
1003
943 mutex_unlock(&s->mutex); 1004 mutex_unlock(&s->mutex);
944} 1005}
945EXPORT_SYMBOL(amdtp_stream_stop); 1006EXPORT_SYMBOL(amdtp_stream_stop);
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index 098187d4b499..2bd3b27ac938 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -20,11 +20,14 @@
20 * at half the actual sample rate with twice the number of channels; 20 * at half the actual sample rate with twice the number of channels;
21 * two samples of a channel are stored consecutively in the packet. 21 * two samples of a channel are stored consecutively in the packet.
22 * Requires blocking mode and SYT_INTERVAL-aligned PCM buffer size. 22 * Requires blocking mode and SYT_INTERVAL-aligned PCM buffer size.
23 * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is
24 * generated by in packets. Defaultly this driver generates timestamp.
23 */ 25 */
24enum cip_flags { 26enum cip_flags {
25 CIP_NONBLOCKING = 0x00, 27 CIP_NONBLOCKING = 0x00,
26 CIP_BLOCKING = 0x01, 28 CIP_BLOCKING = 0x01,
27 CIP_HI_DUALWIRE = 0x02, 29 CIP_HI_DUALWIRE = 0x02,
30 CIP_SYNC_TO_DEVICE = 0x04,
28}; 31};
29 32
30/** 33/**
@@ -104,6 +107,10 @@ struct amdtp_stream {
104 bool pointer_flush; 107 bool pointer_flush;
105 108
106 struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; 109 struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
110
111 bool callbacked;
112 wait_queue_head_t callback_wait;
113 struct amdtp_stream *sync_slave;
107}; 114};
108 115
109int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, 116int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
@@ -201,4 +208,36 @@ static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
201 return sfc & 1; 208 return sfc & 1;
202} 209}
203 210
211static inline void amdtp_stream_set_sync(enum cip_flags sync_mode,
212 struct amdtp_stream *master,
213 struct amdtp_stream *slave)
214{
215 if (sync_mode == CIP_SYNC_TO_DEVICE) {
216 master->flags |= CIP_SYNC_TO_DEVICE;
217 slave->flags |= CIP_SYNC_TO_DEVICE;
218 master->sync_slave = slave;
219 } else {
220 master->flags &= ~CIP_SYNC_TO_DEVICE;
221 slave->flags &= ~CIP_SYNC_TO_DEVICE;
222 master->sync_slave = NULL;
223 }
224
225 slave->sync_slave = NULL;
226}
227
228/**
229 * amdtp_stream_wait_callback - sleep till callbacked or timeout
230 * @s: the AMDTP stream
231 * @timeout: msec till timeout
232 *
233 * If this function return false, the AMDTP stream should be stopped.
234 */
235static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s,
236 unsigned int timeout)
237{
238 return wait_event_timeout(s->callback_wait,
239 s->callbacked == true,
240 msecs_to_jiffies(timeout)) > 0;
241}
242
204#endif 243#endif