#include <linux/firewire.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/sched.h>
 #include <sound/pcm.h>
 #include <sound/rawmidi.h>
 #include "amdtp.h"
        tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s);
        s->packet_index = 0;
 
+       init_waitqueue_head(&s->callback_wait);
+       s->callbacked = false;
+       s->sync_slave = NULL;
+
        return 0;
 }
 EXPORT_SYMBOL(amdtp_stream_init);
                        unsigned int payload_length, bool skip)
 {
        struct fw_iso_packet p = {0};
-       int err;
+       int err = 0;
+
+       if (IS_ERR(s->context))
+               goto end;
 
        p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
        p.tag = TAG_CIP;
                               void *private_data)
 {
        struct amdtp_stream *s = private_data;
-       unsigned int p, packets, payload_quadlets;
+       unsigned int p, syt, packets, payload_quadlets;
        __be32 *buffer, *headers = header;
 
        /* The number of packets in buffer */
 
        for (p = 0; p < packets; p++) {
                if (s->packet_index < 0)
-                       return;
+                       break;
+
                buffer = s->buffer.packets[s->packet_index].buffer;
 
+               /* Process sync slave stream */
+               if (s->sync_slave && s->sync_slave->callbacked) {
+                       syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
+                       handle_out_packet(s->sync_slave, syt);
+               }
+
                /* The number of quadlets in this packet */
                payload_quadlets =
                        (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
                handle_in_packet(s, payload_quadlets, buffer);
        }
 
+       /* Queueing error or detecting discontinuity */
+       if (s->packet_index < 0) {
+               /* Abort sync slave. */
+               if (s->sync_slave) {
+                       s->sync_slave->packet_index = -1;
+                       amdtp_stream_pcm_abort(s->sync_slave);
+               }
+               return;
+       }
+
+       /* when sync to device, flush the packets for slave stream */
+       if (s->sync_slave && s->sync_slave->callbacked)
+               fw_iso_context_queue_flush(s->sync_slave->context);
+
        fw_iso_context_queue_flush(s->context);
 }
 
+/* processing is done by master callback */
+static void slave_stream_callback(struct fw_iso_context *context, u32 cycle,
+                                 size_t header_length, void *header,
+                                 void *private_data)
+{
+       return;
+}
+
+/* this is executed one time */
+static void amdtp_stream_first_callback(struct fw_iso_context *context,
+                                       u32 cycle, size_t header_length,
+                                       void *header, void *private_data)
+{
+       struct amdtp_stream *s = private_data;
+
+       /*
+        * For in-stream, first packet has come.
+        * For out-stream, prepared to transmit first packet
+        */
+       s->callbacked = true;
+       wake_up(&s->callback_wait);
+
+       if (s->direction == AMDTP_IN_STREAM)
+               context->callback.sc = in_stream_callback;
+       else if ((s->flags & CIP_BLOCKING) && (s->flags & CIP_SYNC_TO_DEVICE))
+               context->callback.sc = slave_stream_callback;
+       else
+               context->callback.sc = out_stream_callback;
+
+       context->callback.sc(context, cycle, header_length, header, s);
+}
+
 /**
  * amdtp_stream_start - start transferring packets
  * @s: the AMDTP stream to start
        };
        unsigned int header_size;
        enum dma_data_direction dir;
-       fw_iso_callback_t cb;
        int type, err;
 
        mutex_lock(&s->mutex);
                dir = DMA_FROM_DEVICE;
                type = FW_ISO_CONTEXT_RECEIVE;
                header_size = IN_PACKET_HEADER_SIZE;
-               cb = in_stream_callback;
        } else {
                dir = DMA_TO_DEVICE;
                type = FW_ISO_CONTEXT_TRANSMIT;
                header_size = OUT_PACKET_HEADER_SIZE;
-               cb = out_stream_callback;
        }
        err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH,
                                      amdtp_stream_get_max_payload(s), dir);
 
        s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
                                           type, channel, speed, header_size,
-                                          cb, s);
+                                          amdtp_stream_first_callback, s);
        if (IS_ERR(s->context)) {
                err = PTR_ERR(s->context);
                if (err == -EBUSY)
        } while (s->packet_index > 0);
 
        /* NOTE: TAG1 matches CIP. This just affects in stream. */
+       s->callbacked = false;
        err = fw_iso_context_start(s->context, -1, 0,
                                   FW_ISO_CONTEXT_MATCH_TAG1);
        if (err < 0)
        s->context = ERR_PTR(-1);
        iso_packets_buffer_destroy(&s->buffer, s->unit);
 
+       s->callbacked = false;
+
        mutex_unlock(&s->mutex);
 }
 EXPORT_SYMBOL(amdtp_stream_stop);
 
  *     at half the actual sample rate with twice the number of channels;
  *     two samples of a channel are stored consecutively in the packet.
  *     Requires blocking mode and SYT_INTERVAL-aligned PCM buffer size.
+ * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is
+ *     generated by in packets. Defaultly this driver generates timestamp.
  */
 enum cip_flags {
-       CIP_NONBLOCKING = 0x00,
-       CIP_BLOCKING    = 0x01,
-       CIP_HI_DUALWIRE = 0x02,
+       CIP_NONBLOCKING         = 0x00,
+       CIP_BLOCKING            = 0x01,
+       CIP_HI_DUALWIRE         = 0x02,
+       CIP_SYNC_TO_DEVICE      = 0x04,
 };
 
 /**
        bool pointer_flush;
 
        struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
+
+       bool callbacked;
+       wait_queue_head_t callback_wait;
+       struct amdtp_stream *sync_slave;
 };
 
 int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
        return sfc & 1;
 }
 
+static inline void amdtp_stream_set_sync(enum cip_flags sync_mode,
+                                        struct amdtp_stream *master,
+                                        struct amdtp_stream *slave)
+{
+       if (sync_mode == CIP_SYNC_TO_DEVICE) {
+               master->flags |= CIP_SYNC_TO_DEVICE;
+               slave->flags |= CIP_SYNC_TO_DEVICE;
+               master->sync_slave = slave;
+       } else {
+               master->flags &= ~CIP_SYNC_TO_DEVICE;
+               slave->flags &= ~CIP_SYNC_TO_DEVICE;
+               master->sync_slave = NULL;
+       }
+
+       slave->sync_slave = NULL;
+}
+
+/**
+ * amdtp_stream_wait_callback - sleep till callbacked or timeout
+ * @s: the AMDTP stream
+ * @timeout: msec till timeout
+ *
+ * If this function return false, the AMDTP stream should be stopped.
+ */
+static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s,
+                                             unsigned int timeout)
+{
+       return wait_event_timeout(s->callback_wait,
+                                 s->callbacked == true,
+                                 msecs_to_jiffies(timeout)) > 0;
+}
+
 #endif