aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2012-03-18 14:06:39 -0400
committerStefan Richter <stefanr@s5r6.in-berlin.de>2012-03-18 17:15:39 -0400
commitd1bbd20972936b9b178fda3eb1ec417cb27fdc01 (patch)
tree5d2e29adbc6f0e8ad0dd07b7624e2ad07181303e /drivers/firewire
parent18d627113b830cda80792e96b28341bcd41cf40c (diff)
firewire: allow explicit flushing of iso packet completions
Extend the kernel and userspace APIs to allow reporting all currently completed isochronous packets, even if the next interrupt packet has not yet been reached. This is required to determine the status of the packets at the end of a paused or stopped stream, and useful for more precise synchronization of audio streams. Signed-off-by: Clemens Ladisch <clemens@ladisch.de> Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire')
-rw-r--r--drivers/firewire/core-card.c6
-rw-r--r--drivers/firewire/core-cdev.c12
-rw-r--r--drivers/firewire/core-iso.c6
-rw-r--r--drivers/firewire/core.h2
-rw-r--r--drivers/firewire/ohci.c78
5 files changed, 96 insertions, 8 deletions
diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c
index b19db0f6a254..cc595eba7ba9 100644
--- a/drivers/firewire/core-card.c
+++ b/drivers/firewire/core-card.c
@@ -650,6 +650,11 @@ static void dummy_flush_queue_iso(struct fw_iso_context *ctx)
650{ 650{
651} 651}
652 652
653static int dummy_flush_iso_completions(struct fw_iso_context *ctx)
654{
655 return -ENODEV;
656}
657
653static const struct fw_card_driver dummy_driver_template = { 658static const struct fw_card_driver dummy_driver_template = {
654 .read_phy_reg = dummy_read_phy_reg, 659 .read_phy_reg = dummy_read_phy_reg,
655 .update_phy_reg = dummy_update_phy_reg, 660 .update_phy_reg = dummy_update_phy_reg,
@@ -662,6 +667,7 @@ static const struct fw_card_driver dummy_driver_template = {
662 .set_iso_channels = dummy_set_iso_channels, 667 .set_iso_channels = dummy_set_iso_channels,
663 .queue_iso = dummy_queue_iso, 668 .queue_iso = dummy_queue_iso,
664 .flush_queue_iso = dummy_flush_queue_iso, 669 .flush_queue_iso = dummy_flush_queue_iso,
670 .flush_iso_completions = dummy_flush_iso_completions,
665}; 671};
666 672
667void fw_card_release(struct kref *kref) 673void fw_card_release(struct kref *kref)
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index 4cb27dc542ce..22c6df5f136d 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -438,6 +438,7 @@ union ioctl_arg {
438 struct fw_cdev_send_phy_packet send_phy_packet; 438 struct fw_cdev_send_phy_packet send_phy_packet;
439 struct fw_cdev_receive_phy_packets receive_phy_packets; 439 struct fw_cdev_receive_phy_packets receive_phy_packets;
440 struct fw_cdev_set_iso_channels set_iso_channels; 440 struct fw_cdev_set_iso_channels set_iso_channels;
441 struct fw_cdev_flush_iso flush_iso;
441}; 442};
442 443
443static int ioctl_get_info(struct client *client, union ioctl_arg *arg) 444static int ioctl_get_info(struct client *client, union ioctl_arg *arg)
@@ -1168,6 +1169,16 @@ static int ioctl_stop_iso(struct client *client, union ioctl_arg *arg)
1168 return fw_iso_context_stop(client->iso_context); 1169 return fw_iso_context_stop(client->iso_context);
1169} 1170}
1170 1171
1172static int ioctl_flush_iso(struct client *client, union ioctl_arg *arg)
1173{
1174 struct fw_cdev_flush_iso *a = &arg->flush_iso;
1175
1176 if (client->iso_context == NULL || a->handle != 0)
1177 return -EINVAL;
1178
1179 return fw_iso_context_flush_completions(client->iso_context);
1180}
1181
1171static int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg) 1182static int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg)
1172{ 1183{
1173 struct fw_cdev_get_cycle_timer2 *a = &arg->get_cycle_timer2; 1184 struct fw_cdev_get_cycle_timer2 *a = &arg->get_cycle_timer2;
@@ -1589,6 +1600,7 @@ static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = {
1589 [0x15] = ioctl_send_phy_packet, 1600 [0x15] = ioctl_send_phy_packet,
1590 [0x16] = ioctl_receive_phy_packets, 1601 [0x16] = ioctl_receive_phy_packets,
1591 [0x17] = ioctl_set_iso_channels, 1602 [0x17] = ioctl_set_iso_channels,
1603 [0x18] = ioctl_flush_iso,
1592}; 1604};
1593 1605
1594static int dispatch_ioctl(struct client *client, 1606static int dispatch_ioctl(struct client *client,
diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c
index 57c3973093ad..2f432a20ce7e 100644
--- a/drivers/firewire/core-iso.c
+++ b/drivers/firewire/core-iso.c
@@ -191,6 +191,12 @@ void fw_iso_context_queue_flush(struct fw_iso_context *ctx)
191} 191}
192EXPORT_SYMBOL(fw_iso_context_queue_flush); 192EXPORT_SYMBOL(fw_iso_context_queue_flush);
193 193
194int fw_iso_context_flush_completions(struct fw_iso_context *ctx)
195{
196 return ctx->card->driver->flush_iso_completions(ctx);
197}
198EXPORT_SYMBOL(fw_iso_context_flush_completions);
199
194int fw_iso_context_stop(struct fw_iso_context *ctx) 200int fw_iso_context_stop(struct fw_iso_context *ctx)
195{ 201{
196 return ctx->card->driver->stop_iso(ctx); 202 return ctx->card->driver->stop_iso(ctx);
diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h
index 62f57a4331e3..9047f5547d98 100644
--- a/drivers/firewire/core.h
+++ b/drivers/firewire/core.h
@@ -106,6 +106,8 @@ struct fw_card_driver {
106 106
107 void (*flush_queue_iso)(struct fw_iso_context *ctx); 107 void (*flush_queue_iso)(struct fw_iso_context *ctx);
108 108
109 int (*flush_iso_completions)(struct fw_iso_context *ctx);
110
109 int (*stop_iso)(struct fw_iso_context *ctx); 111 int (*stop_iso)(struct fw_iso_context *ctx);
110}; 112};
111 113
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index 632562667a01..59e7894ae3b8 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -172,6 +172,9 @@ struct iso_context {
172 struct context context; 172 struct context context;
173 void *header; 173 void *header;
174 size_t header_length; 174 size_t header_length;
175 unsigned long flushing_completions;
176 u32 mc_buffer_bus;
177 u16 mc_completed;
175 u16 last_timestamp; 178 u16 last_timestamp;
176 u8 sync; 179 u8 sync;
177 u8 tags; 180 u8 tags;
@@ -2749,28 +2752,51 @@ static int handle_ir_buffer_fill(struct context *context,
2749{ 2752{
2750 struct iso_context *ctx = 2753 struct iso_context *ctx =
2751 container_of(context, struct iso_context, context); 2754 container_of(context, struct iso_context, context);
2755 unsigned int req_count, res_count, completed;
2752 u32 buffer_dma; 2756 u32 buffer_dma;
2753 2757
2754 if (last->res_count != 0) 2758 req_count = le16_to_cpu(last->req_count);
2759 res_count = le16_to_cpu(ACCESS_ONCE(last->res_count));
2760 completed = req_count - res_count;
2761 buffer_dma = le32_to_cpu(last->data_address);
2762
2763 if (completed > 0) {
2764 ctx->mc_buffer_bus = buffer_dma;
2765 ctx->mc_completed = completed;
2766 }
2767
2768 if (res_count != 0)
2755 /* Descriptor(s) not done yet, stop iteration */ 2769 /* Descriptor(s) not done yet, stop iteration */
2756 return 0; 2770 return 0;
2757 2771
2758 buffer_dma = le32_to_cpu(last->data_address);
2759 dma_sync_single_range_for_cpu(context->ohci->card.device, 2772 dma_sync_single_range_for_cpu(context->ohci->card.device,
2760 buffer_dma & PAGE_MASK, 2773 buffer_dma & PAGE_MASK,
2761 buffer_dma & ~PAGE_MASK, 2774 buffer_dma & ~PAGE_MASK,
2762 le16_to_cpu(last->req_count), 2775 completed, DMA_FROM_DEVICE);
2763 DMA_FROM_DEVICE);
2764 2776
2765 if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS)) 2777 if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS)) {
2766 ctx->base.callback.mc(&ctx->base, 2778 ctx->base.callback.mc(&ctx->base,
2767 le32_to_cpu(last->data_address) + 2779 buffer_dma + completed,
2768 le16_to_cpu(last->req_count),
2769 ctx->base.callback_data); 2780 ctx->base.callback_data);
2781 ctx->mc_completed = 0;
2782 }
2770 2783
2771 return 1; 2784 return 1;
2772} 2785}
2773 2786
2787static void flush_ir_buffer_fill(struct iso_context *ctx)
2788{
2789 dma_sync_single_range_for_cpu(ctx->context.ohci->card.device,
2790 ctx->mc_buffer_bus & PAGE_MASK,
2791 ctx->mc_buffer_bus & ~PAGE_MASK,
2792 ctx->mc_completed, DMA_FROM_DEVICE);
2793
2794 ctx->base.callback.mc(&ctx->base,
2795 ctx->mc_buffer_bus + ctx->mc_completed,
2796 ctx->base.callback_data);
2797 ctx->mc_completed = 0;
2798}
2799
2774static inline void sync_it_packet_for_cpu(struct context *context, 2800static inline void sync_it_packet_for_cpu(struct context *context,
2775 struct descriptor *pd) 2801 struct descriptor *pd)
2776{ 2802{
@@ -2925,8 +2951,10 @@ static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card,
2925 if (ret < 0) 2951 if (ret < 0)
2926 goto out_with_header; 2952 goto out_with_header;
2927 2953
2928 if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) 2954 if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) {
2929 set_multichannel_mask(ohci, 0); 2955 set_multichannel_mask(ohci, 0);
2956 ctx->mc_completed = 0;
2957 }
2930 2958
2931 return &ctx->base; 2959 return &ctx->base;
2932 2960
@@ -3388,6 +3416,39 @@ static void ohci_flush_queue_iso(struct fw_iso_context *base)
3388 reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE); 3416 reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE);
3389} 3417}
3390 3418
3419static int ohci_flush_iso_completions(struct fw_iso_context *base)
3420{
3421 struct iso_context *ctx = container_of(base, struct iso_context, base);
3422 int ret = 0;
3423
3424 tasklet_disable(&ctx->context.tasklet);
3425
3426 if (!test_and_set_bit_lock(0, &ctx->flushing_completions)) {
3427 context_tasklet((unsigned long)&ctx->context);
3428
3429 switch (base->type) {
3430 case FW_ISO_CONTEXT_TRANSMIT:
3431 case FW_ISO_CONTEXT_RECEIVE:
3432 if (ctx->header_length != 0)
3433 flush_iso_completions(ctx);
3434 break;
3435 case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL:
3436 if (ctx->mc_completed != 0)
3437 flush_ir_buffer_fill(ctx);
3438 break;
3439 default:
3440 ret = -ENOSYS;
3441 }
3442
3443 clear_bit_unlock(0, &ctx->flushing_completions);
3444 smp_mb__after_clear_bit();
3445 }
3446
3447 tasklet_enable(&ctx->context.tasklet);
3448
3449 return ret;
3450}
3451
3391static const struct fw_card_driver ohci_driver = { 3452static const struct fw_card_driver ohci_driver = {
3392 .enable = ohci_enable, 3453 .enable = ohci_enable,
3393 .read_phy_reg = ohci_read_phy_reg, 3454 .read_phy_reg = ohci_read_phy_reg,
@@ -3405,6 +3466,7 @@ static const struct fw_card_driver ohci_driver = {
3405 .set_iso_channels = ohci_set_iso_channels, 3466 .set_iso_channels = ohci_set_iso_channels,
3406 .queue_iso = ohci_queue_iso, 3467 .queue_iso = ohci_queue_iso,
3407 .flush_queue_iso = ohci_flush_queue_iso, 3468 .flush_queue_iso = ohci_flush_queue_iso,
3469 .flush_iso_completions = ohci_flush_iso_completions,
3408 .start_iso = ohci_start_iso, 3470 .start_iso = ohci_start_iso,
3409 .stop_iso = ohci_stop_iso, 3471 .stop_iso = ohci_stop_iso,
3410}; 3472};