static void vchan_complete(unsigned long arg)
 {
        struct virt_dma_chan *vc = (struct virt_dma_chan *)arg;
+       struct virt_dma_desc *vd;
+       dma_async_tx_callback cb = NULL;
+       void *cb_data = NULL;
        LIST_HEAD(head);
 
        spin_lock_irq(&vc->lock);
        list_splice_tail_init(&vc->desc_completed, &head);
+       vd = vc->cyclic;
+       if (vd) {
+               vc->cyclic = NULL;
+               cb = vd->tx.callback;
+               cb_data = vd->tx.callback_param;
+       }
        spin_unlock_irq(&vc->lock);
 
+       if (cb)
+               cb(cb_data);
+
        while (!list_empty(&head)) {
-               struct virt_dma_desc *vd = list_first_entry(&head,
-                               struct virt_dma_desc, node);
-               dma_async_tx_callback cb = vd->tx.callback;
-               void *cb_data = vd->tx.callback_param;
+               vd = list_first_entry(&head, struct virt_dma_desc, node);
+               cb = vd->tx.callback;
+               cb_data = vd->tx.callback_param;
 
                list_del(&vd->node);
 
 
        struct list_head desc_submitted;
        struct list_head desc_issued;
        struct list_head desc_completed;
+
+       struct virt_dma_desc *cyclic;
 };
 
 static inline struct virt_dma_chan *to_virt_chan(struct dma_chan *chan)
        tasklet_schedule(&vc->task);
 }
 
+/**
+ * vchan_cyclic_callback - report the completion of a period
+ * vd: virtual descriptor
+ */
+static inline void vchan_cyclic_callback(struct virt_dma_desc *vd)
+{
+       struct virt_dma_chan *vc = to_virt_chan(vd->tx.chan);
+
+       vc->cyclic = vd;
+       tasklet_schedule(&vc->task);
+}
+
 /**
  * vchan_next_desc - peek at the next descriptor to be processed
  * vc: virtual channel to obtain descriptor from