diff options
Diffstat (limited to 'sound/firewire')
33 files changed, 862 insertions, 238 deletions
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index b0a904cdb932..995c2cefc222 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -77,7 +77,7 @@ config SND_BEBOB tristate "BridgeCo DM1000/DM1100/DM1500 with BeBoB firmware" select SND_FIREWIRE_LIB select SND_HWDEP - help + help Say Y here to include support for FireWire devices based on BridgeCo DM1000/DM1100/DM1500 with BeBoB firmware: * Edirol FA-66/FA-101 @@ -111,8 +111,8 @@ config SND_BEBOB * M-Audio FireWire 1814/ProjectMix IO * Digidesign Mbox 2 Pro - To compile this driver as a module, choose M here: the module - will be called snd-bebob. + To compile this driver as a module, choose M here: the module + will be called snd-bebob. config SND_FIREWIRE_DIGI00X tristate "Digidesign Digi 002/003 family support" diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index e50e28f77e74..37d38efb4c87 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -9,6 +9,7 @@ #include <linux/device.h> #include <linux/err.h> #include <linux/firewire.h> +#include <linux/firewire-constants.h> #include <linux/module.h> #include <linux/slab.h> #include <sound/pcm.h> @@ -52,10 +53,6 @@ #define CIP_FMT_AM 0x10 #define AMDTP_FDF_NO_DATA 0xff -/* TODO: make these configurable */ -#define INTERRUPT_INTERVAL 16 -#define QUEUE_LENGTH 48 - // For iso header, tstamp and 2 CIP header. #define IR_CTX_HEADER_SIZE_CIP 16 // For iso header and tstamp. @@ -180,6 +177,8 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime) { struct snd_pcm_hardware *hw = &runtime->hw; + unsigned int ctx_header_size; + unsigned int maximum_usec_per_period; int err; hw->info = SNDRV_PCM_INFO_BATCH | @@ -200,19 +199,36 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, hw->period_bytes_max = hw->period_bytes_min * 2048; hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; - /* - * Currently firewire-lib processes 16 packets in one software - * interrupt callback. This equals to 2msec but actually the - * interval of the interrupts has a jitter. - * Additionally, even if adding a constraint to fit period size to - * 2msec, actual calculated frames per period doesn't equal to 2msec, - * depending on sampling rate. - * Anyway, the interval to call snd_pcm_period_elapsed() cannot 2msec. - * Here let us use 5msec for safe period interrupt. - */ + // Linux driver for 1394 OHCI controller voluntarily flushes isoc + // context when total size of accumulated context header reaches + // PAGE_SIZE. This kicks tasklet for the isoc context and brings + // callback in the middle of scheduled interrupts. + // Although AMDTP streams in the same domain use the same events per + // IRQ, use the largest size of context header between IT/IR contexts. + // Here, use the value of context header in IR context is for both + // contexts. + if (!(s->flags & CIP_NO_HEADER)) + ctx_header_size = IR_CTX_HEADER_SIZE_CIP; + else + ctx_header_size = IR_CTX_HEADER_SIZE_NO_CIP; + maximum_usec_per_period = USEC_PER_SEC * PAGE_SIZE / + CYCLES_PER_SECOND / ctx_header_size; + + // In IEC 61883-6, one isoc packet can transfer events up to the value + // of syt interval. This comes from the interval of isoc cycle. As 1394 + // OHCI controller can generate hardware IRQ per isoc packet, the + // interval is 125 usec. + // However, there are two ways of transmission in IEC 61883-6; blocking + // and non-blocking modes. In blocking mode, the sequence of isoc packet + // includes 'empty' or 'NODATA' packets which include no event. In + // non-blocking mode, the number of events per packet is variable up to + // the syt interval. + // Due to the above protocol design, the minimum PCM frames per + // interrupt should be double of the value of syt interval, thus it is + // 250 usec. err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, - 5000, UINT_MAX); + 250, maximum_usec_per_period); if (err < 0) goto end; @@ -436,11 +452,12 @@ static void pcm_period_tasklet(unsigned long data) snd_pcm_period_elapsed(pcm); } -static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params) +static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params, + bool sched_irq) { int err; - params->interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL); + params->interrupt = sched_irq; params->tag = s->tag; params->sy = 0; @@ -451,18 +468,18 @@ static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params) goto end; } - if (++s->packet_index >= QUEUE_LENGTH) + if (++s->packet_index >= s->queue_size) s->packet_index = 0; end: return err; } static inline int queue_out_packet(struct amdtp_stream *s, - struct fw_iso_packet *params) + struct fw_iso_packet *params, bool sched_irq) { params->skip = !!(params->header_length == 0 && params->payload_length == 0); - return queue_packet(s, params); + return queue_packet(s, params, sched_irq); } static inline int queue_in_packet(struct amdtp_stream *s, @@ -472,7 +489,7 @@ static inline int queue_in_packet(struct amdtp_stream *s, params->header_length = s->ctx_data.tx.ctx_header_size; params->payload_length = s->ctx_data.tx.max_ctx_payload_length; params->skip = false; - return queue_packet(s, params); + return queue_packet(s, params, false); } static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2], @@ -669,13 +686,14 @@ static inline u32 increment_cycle_count(u32 cycle, unsigned int addend) } // Align to actual cycle count for the packet which is going to be scheduled. -// This module queued the same number of isochronous cycle as QUEUE_LENGTH to -// skip isochronous cycle, therefore it's OK to just increment the cycle by -// QUEUE_LENGTH for scheduled cycle. -static inline u32 compute_it_cycle(const __be32 ctx_header_tstamp) +// This module queued the same number of isochronous cycle as the size of queue +// to kip isochronous cycle, therefore it's OK to just increment the cycle by +// the size of queue for scheduled cycle. +static inline u32 compute_it_cycle(const __be32 ctx_header_tstamp, + unsigned int queue_size) { u32 cycle = compute_cycle_count(ctx_header_tstamp); - return increment_cycle_count(cycle, QUEUE_LENGTH); + return increment_cycle_count(cycle, queue_size); } static int generate_device_pkt_descs(struct amdtp_stream *s, @@ -689,7 +707,7 @@ static int generate_device_pkt_descs(struct amdtp_stream *s, for (i = 0; i < packets; ++i) { struct pkt_desc *desc = descs + i; - unsigned int index = (s->packet_index + i) % QUEUE_LENGTH; + unsigned int index = (s->packet_index + i) % s->queue_size; unsigned int cycle; unsigned int payload_length; unsigned int data_blocks; @@ -730,9 +748,9 @@ static void generate_ideal_pkt_descs(struct amdtp_stream *s, for (i = 0; i < packets; ++i) { struct pkt_desc *desc = descs + i; - unsigned int index = (s->packet_index + i) % QUEUE_LENGTH; + unsigned int index = (s->packet_index + i) % s->queue_size; - desc->cycle = compute_it_cycle(*ctx_header); + desc->cycle = compute_it_cycle(*ctx_header, s->queue_size); desc->syt = calculate_syt(s, desc->cycle); desc->data_blocks = calculate_data_blocks(s, desc->syt); @@ -773,22 +791,40 @@ static void process_ctx_payloads(struct amdtp_stream *s, update_pcm_pointers(s, pcm, pcm_frames); } +static void amdtp_stream_master_callback(struct fw_iso_context *context, + u32 tstamp, size_t header_length, + void *header, void *private_data); + +static void amdtp_stream_master_first_callback(struct fw_iso_context *context, + u32 tstamp, size_t header_length, + void *header, void *private_data); + static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length, void *header, void *private_data) { struct amdtp_stream *s = private_data; const __be32 *ctx_header = header; - unsigned int packets = header_length / sizeof(*ctx_header); + unsigned int events_per_period = s->ctx_data.rx.events_per_period; + unsigned int event_count = s->ctx_data.rx.event_count; + unsigned int packets; + bool is_irq_target; int i; if (s->packet_index < 0) return; + // Calculate the number of packets in buffer and check XRUN. + packets = header_length / sizeof(*ctx_header); + generate_ideal_pkt_descs(s, s->pkt_descs, ctx_header, packets); process_ctx_payloads(s, s->pkt_descs, packets); + is_irq_target = + !!(context->callback.sc == amdtp_stream_master_callback || + context->callback.sc == amdtp_stream_master_first_callback); + for (i = 0; i < packets; ++i) { const struct pkt_desc *desc = s->pkt_descs + i; unsigned int syt; @@ -796,6 +832,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, struct fw_iso_packet params; __be32 header[IT_PKT_HEADER_SIZE_CIP / sizeof(__be32)]; } template = { {0}, {0} }; + bool sched_irq = false; if (s->ctx_data.rx.syt_override < 0) syt = desc->syt; @@ -806,13 +843,21 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, desc->data_blocks, desc->data_block_counter, syt, i); - if (queue_out_packet(s, &template.params) < 0) { + if (is_irq_target) { + event_count += desc->data_blocks; + if (event_count >= events_per_period) { + event_count -= events_per_period; + sched_irq = true; + } + } + + if (queue_out_packet(s, &template.params, sched_irq) < 0) { cancel_stream(s); return; } } - fw_iso_context_queue_flush(s->context); + s->ctx_data.rx.event_count = event_count; } static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, @@ -820,15 +865,15 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, void *private_data) { struct amdtp_stream *s = private_data; - unsigned int packets; __be32 *ctx_header = header; + unsigned int packets; int i; int err; if (s->packet_index < 0) return; - // The number of packets in buffer. + // Calculate the number of packets in buffer and check XRUN. packets = header_length / s->ctx_data.tx.ctx_header_size; err = generate_device_pkt_descs(s, s->pkt_descs, ctx_header, packets); @@ -849,11 +894,40 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, return; } } +} + +static void amdtp_stream_master_callback(struct fw_iso_context *context, + u32 tstamp, size_t header_length, + void *header, void *private_data) +{ + struct amdtp_domain *d = private_data; + struct amdtp_stream *irq_target = d->irq_target; + struct amdtp_stream *s; + + out_stream_callback(context, tstamp, header_length, header, irq_target); + if (amdtp_streaming_error(irq_target)) + goto error; + + list_for_each_entry(s, &d->streams, list) { + if (s != irq_target && amdtp_stream_running(s)) { + fw_iso_context_flush_completions(s->context); + if (amdtp_streaming_error(s)) + goto error; + } + } - fw_iso_context_queue_flush(s->context); + return; +error: + if (amdtp_stream_running(irq_target)) + cancel_stream(irq_target); + + list_for_each_entry(s, &d->streams, list) { + if (amdtp_stream_running(s)) + cancel_stream(s); + } } -/* this is executed one time */ +// this is executed one time. static void amdtp_stream_first_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length, void *header, void *private_data) @@ -874,7 +948,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, context->callback.sc = in_stream_callback; } else { - cycle = compute_it_cycle(*ctx_header); + cycle = compute_it_cycle(*ctx_header, s->queue_size); context->callback.sc = out_stream_callback; } @@ -884,17 +958,42 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, context->callback.sc(context, tstamp, header_length, header, s); } +static void amdtp_stream_master_first_callback(struct fw_iso_context *context, + u32 tstamp, size_t header_length, + void *header, void *private_data) +{ + struct amdtp_domain *d = private_data; + struct amdtp_stream *s = d->irq_target; + const __be32 *ctx_header = header; + + s->callbacked = true; + wake_up(&s->callback_wait); + + s->start_cycle = compute_it_cycle(*ctx_header, s->queue_size); + + context->callback.sc = amdtp_stream_master_callback; + + context->callback.sc(context, tstamp, header_length, header, d); +} + /** * amdtp_stream_start - start transferring packets * @s: the AMDTP stream to start * @channel: the isochronous channel on the bus * @speed: firewire speed code + * @d: the AMDTP domain to which the AMDTP stream belongs + * @is_irq_target: whether isoc context for the AMDTP stream is used to generate + * hardware IRQ. + * @start_cycle: the isochronous cycle to start the context. Start immediately + * if negative value is given. * * The stream cannot be started until it has been configured with * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI * device can be started. */ -static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) +static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, + struct amdtp_domain *d, bool is_irq_target, + int start_cycle) { static const struct { unsigned int data_block; @@ -908,10 +1007,15 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) [CIP_SFC_88200] = { 0, 67 }, [CIP_SFC_176400] = { 0, 67 }, }; + unsigned int events_per_buffer = d->events_per_buffer; + unsigned int events_per_period = d->events_per_period; + unsigned int idle_irq_interval; unsigned int ctx_header_size; unsigned int max_ctx_payload_size; enum dma_data_direction dir; int type, tag, err; + fw_iso_callback_t ctx_cb; + void *ctx_data; mutex_lock(&s->mutex); @@ -922,6 +1026,12 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) } if (s->direction == AMDTP_IN_STREAM) { + // NOTE: IT context should be used for constant IRQ. + if (is_irq_target) { + err = -EINVAL; + goto err_unlock; + } + s->data_block_counter = UINT_MAX; } else { entry = &initial_state[s->sfc]; @@ -953,14 +1063,37 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) max_ctx_payload_size -= IT_PKT_HEADER_SIZE_CIP; } - err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, + // This is a case that AMDTP streams in domain run just for MIDI + // substream. Use the number of events equivalent to 10 msec as + // interval of hardware IRQ. + if (events_per_period == 0) + events_per_period = amdtp_rate_table[s->sfc] / 100; + if (events_per_buffer == 0) + events_per_buffer = events_per_period * 3; + + idle_irq_interval = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period, + amdtp_rate_table[s->sfc]); + s->queue_size = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_buffer, + amdtp_rate_table[s->sfc]); + + err = iso_packets_buffer_init(&s->buffer, s->unit, s->queue_size, max_ctx_payload_size, dir); if (err < 0) goto err_unlock; + if (is_irq_target) { + s->ctx_data.rx.events_per_period = events_per_period; + s->ctx_data.rx.event_count = 0; + ctx_cb = amdtp_stream_master_first_callback; + ctx_data = d; + } else { + ctx_cb = amdtp_stream_first_callback; + ctx_data = s; + } + s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, type, channel, speed, ctx_header_size, - amdtp_stream_first_callback, s); + ctx_cb, ctx_data); if (IS_ERR(s->context)) { err = PTR_ERR(s->context); if (err == -EBUSY) @@ -981,7 +1114,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) else s->tag = TAG_CIP; - s->pkt_descs = kcalloc(INTERRUPT_INTERVAL, sizeof(*s->pkt_descs), + s->pkt_descs = kcalloc(s->queue_size, sizeof(*s->pkt_descs), GFP_KERNEL); if (!s->pkt_descs) { err = -ENOMEM; @@ -991,12 +1124,21 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) s->packet_index = 0; do { struct fw_iso_packet params; + if (s->direction == AMDTP_IN_STREAM) { err = queue_in_packet(s, ¶ms); } else { + bool sched_irq = false; + params.header_length = 0; params.payload_length = 0; - err = queue_out_packet(s, ¶ms); + + if (is_irq_target) { + sched_irq = !((s->packet_index + 1) % + idle_irq_interval); + } + + err = queue_out_packet(s, ¶ms, sched_irq); } if (err < 0) goto err_pkt_descs; @@ -1008,7 +1150,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) tag |= FW_ISO_CONTEXT_MATCH_TAG0; s->callbacked = false; - err = fw_iso_context_start(s->context, -1, 0, tag); + err = fw_iso_context_start(s->context, start_cycle, 0, tag); if (err < 0) goto err_pkt_descs; @@ -1029,54 +1171,69 @@ err_unlock: } /** - * amdtp_stream_pcm_pointer - get the PCM buffer position + * amdtp_domain_stream_pcm_pointer - get the PCM buffer position + * @d: the AMDTP domain. * @s: the AMDTP stream that transports the PCM data * * Returns the current buffer position, in frames. */ -unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s) +unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d, + struct amdtp_stream *s) { - /* - * This function is called in software IRQ context of period_tasklet or - * process context. - * - * When the software IRQ context was scheduled by software IRQ context - * of IR/IT contexts, queued packets were already handled. Therefore, - * no need to flush the queue in buffer anymore. - * - * When the process context reach here, some packets will be already - * queued in the buffer. These packets should be handled immediately - * to keep better granularity of PCM pointer. - * - * Later, the process context will sometimes schedules software IRQ - * context of the period_tasklet. Then, no need to flush the queue by - * the same reason as described for IR/IT contexts. - */ - if (!in_interrupt() && amdtp_stream_running(s)) - fw_iso_context_flush_completions(s->context); + struct amdtp_stream *irq_target = d->irq_target; + + if (irq_target && amdtp_stream_running(irq_target)) { + // This function is called in software IRQ context of + // period_tasklet or process context. + // + // When the software IRQ context was scheduled by software IRQ + // context of IT contexts, queued packets were already handled. + // Therefore, no need to flush the queue in buffer furthermore. + // + // When the process context reach here, some packets will be + // already queued in the buffer. These packets should be handled + // immediately to keep better granularity of PCM pointer. + // + // Later, the process context will sometimes schedules software + // IRQ context of the period_tasklet. Then, no need to flush the + // queue by the same reason as described in the above + if (!in_interrupt()) { + // Queued packet should be processed without any kernel + // preemption to keep latency against bus cycle. + preempt_disable(); + fw_iso_context_flush_completions(irq_target->context); + preempt_enable(); + } + } return READ_ONCE(s->pcm_buffer_pointer); } -EXPORT_SYMBOL(amdtp_stream_pcm_pointer); +EXPORT_SYMBOL_GPL(amdtp_domain_stream_pcm_pointer); /** - * amdtp_stream_pcm_ack - acknowledge queued PCM frames + * amdtp_domain_stream_pcm_ack - acknowledge queued PCM frames + * @d: the AMDTP domain. * @s: the AMDTP stream that transfers the PCM frames * * Returns zero always. */ -int amdtp_stream_pcm_ack(struct amdtp_stream *s) +int amdtp_domain_stream_pcm_ack(struct amdtp_domain *d, struct amdtp_stream *s) { - /* - * Process isochronous packets for recent isochronous cycle to handle - * queued PCM frames. - */ - if (amdtp_stream_running(s)) - fw_iso_context_flush_completions(s->context); + struct amdtp_stream *irq_target = d->irq_target; + + // Process isochronous packets for recent isochronous cycle to handle + // queued PCM frames. + if (irq_target && amdtp_stream_running(irq_target)) { + // Queued packet should be processed without any kernel + // preemption to keep latency against bus cycle. + preempt_disable(); + fw_iso_context_flush_completions(irq_target->context); + preempt_enable(); + } return 0; } -EXPORT_SYMBOL(amdtp_stream_pcm_ack); +EXPORT_SYMBOL_GPL(amdtp_domain_stream_pcm_ack); /** * amdtp_stream_update - update the stream after a bus reset @@ -1143,6 +1300,8 @@ int amdtp_domain_init(struct amdtp_domain *d) { INIT_LIST_HEAD(&d->streams); + d->events_per_period = 0; + return 0; } EXPORT_SYMBOL_GPL(amdtp_domain_init); @@ -1184,26 +1343,105 @@ int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s, } EXPORT_SYMBOL_GPL(amdtp_domain_add_stream); +static int get_current_cycle_time(struct fw_card *fw_card, int *cur_cycle) +{ + int generation; + int rcode; + __be32 reg; + u32 data; + + // This is a request to local 1394 OHCI controller and expected to + // complete without any event waiting. + generation = fw_card->generation; + smp_rmb(); // node_id vs. generation. + rcode = fw_run_transaction(fw_card, TCODE_READ_QUADLET_REQUEST, + fw_card->node_id, generation, SCODE_100, + CSR_REGISTER_BASE + CSR_CYCLE_TIME, + ®, sizeof(reg)); + if (rcode != RCODE_COMPLETE) + return -EIO; + + data = be32_to_cpu(reg); + *cur_cycle = data >> 12; + + return 0; +} + /** * amdtp_domain_start - start sending packets for isoc context in the domain. * @d: the AMDTP domain. + * @ir_delay_cycle: the cycle delay to start all IR contexts. */ -int amdtp_domain_start(struct amdtp_domain *d) +int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) { struct amdtp_stream *s; - int err = 0; + int cycle; + int err; + // Select an IT context as IRQ target. list_for_each_entry(s, &d->streams, list) { - err = amdtp_stream_start(s, s->channel, s->speed); - if (err < 0) + if (s->direction == AMDTP_OUT_STREAM) break; } + if (!s) + return -ENXIO; + d->irq_target = s; - if (err < 0) { - list_for_each_entry(s, &d->streams, list) - amdtp_stream_stop(s); + if (ir_delay_cycle > 0) { + struct fw_card *fw_card = fw_parent_device(s->unit)->card; + + err = get_current_cycle_time(fw_card, &cycle); + if (err < 0) + return err; + + // No need to care overflow in cycle field because of enough + // width. + cycle += ir_delay_cycle; + + // Round up to sec field. + if ((cycle & 0x00001fff) >= CYCLES_PER_SECOND) { + unsigned int sec; + + // The sec field can overflow. + sec = (cycle & 0xffffe000) >> 13; + cycle = (++sec << 13) | + ((cycle & 0x00001fff) / CYCLES_PER_SECOND); + } + + // In OHCI 1394 specification, lower 2 bits are available for + // sec field. + cycle &= 0x00007fff; + } else { + cycle = -1; + } + + list_for_each_entry(s, &d->streams, list) { + int cycle_match; + + if (s->direction == AMDTP_IN_STREAM) { + cycle_match = cycle; + } else { + // IT context starts immediately. + cycle_match = -1; + } + + if (s != d->irq_target) { + err = amdtp_stream_start(s, s->channel, s->speed, d, + false, cycle_match); + if (err < 0) + goto error; + } } + s = d->irq_target; + err = amdtp_stream_start(s, s->channel, s->speed, d, true, -1); + if (err < 0) + goto error; + + return 0; +error: + list_for_each_entry(s, &d->streams, list) + amdtp_stream_stop(s); return err; } EXPORT_SYMBOL_GPL(amdtp_domain_start); @@ -1216,10 +1454,17 @@ void amdtp_domain_stop(struct amdtp_domain *d) { struct amdtp_stream *s, *next; + if (d->irq_target) + amdtp_stream_stop(d->irq_target); + list_for_each_entry_safe(s, next, &d->streams, list) { list_del(&s->list); - amdtp_stream_stop(s); + if (s != d->irq_target) + amdtp_stream_stop(s); } + + d->events_per_period = 0; + d->irq_target = NULL; } EXPORT_SYMBOL_GPL(amdtp_domain_stop); diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index bbbca964b9b4..f2d44e2dc3c8 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -117,6 +117,7 @@ struct amdtp_stream { /* For packet processing. */ struct fw_iso_context *context; struct iso_packets_buffer buffer; + unsigned int queue_size; int packet_index; struct pkt_desc *pkt_descs; int tag; @@ -142,6 +143,10 @@ struct amdtp_stream { // To generate CIP header. unsigned int fdf; int syt_override; + + // To generate constant hardware IRQ. + unsigned int event_count; + unsigned int events_per_period; } rx; } ctx_data; @@ -194,8 +199,6 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); void amdtp_stream_pcm_prepare(struct amdtp_stream *s); -unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s); -int amdtp_stream_pcm_ack(struct amdtp_stream *s); void amdtp_stream_pcm_abort(struct amdtp_stream *s); extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT]; @@ -272,6 +275,11 @@ static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s, struct amdtp_domain { struct list_head streams; + + unsigned int events_per_period; + unsigned int events_per_buffer; + + struct amdtp_stream *irq_target; }; int amdtp_domain_init(struct amdtp_domain *d); @@ -280,7 +288,21 @@ void amdtp_domain_destroy(struct amdtp_domain *d); int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s, int channel, int speed); -int amdtp_domain_start(struct amdtp_domain *d); +int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle); void amdtp_domain_stop(struct amdtp_domain *d); +static inline int amdtp_domain_set_events_per_period(struct amdtp_domain *d, + unsigned int events_per_period, + unsigned int events_per_buffer) +{ + d->events_per_period = events_per_period; + d->events_per_buffer = events_per_buffer; + + return 0; +} + +unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d, + struct amdtp_stream *s); +int amdtp_domain_stream_pcm_ack(struct amdtp_domain *d, struct amdtp_stream *s); + #endif diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index 356d6ba60959..d1ad9a8451bc 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -217,7 +217,9 @@ int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob, enum snd_bebob_clock_type *src); int snd_bebob_stream_discover(struct snd_bebob *bebob); int snd_bebob_stream_init_duplex(struct snd_bebob *bebob); -int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate); +int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer); int snd_bebob_stream_start_duplex(struct snd_bebob *bebob); void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob); void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob); diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c index 4d8805fa8a00..6f597d03e7c1 100644 --- a/sound/firewire/bebob/bebob_midi.c +++ b/sound/firewire/bebob/bebob_midi.c @@ -17,7 +17,7 @@ static int midi_open(struct snd_rawmidi_substream *substream) return err; mutex_lock(&bebob->mutex); - err = snd_bebob_stream_reserve_duplex(bebob, 0); + err = snd_bebob_stream_reserve_duplex(bebob, 0, 0, 0); if (err >= 0) { ++bebob->substreams_counter; err = snd_bebob_stream_start_duplex(bebob); diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index 0fb9eed46837..1b100159f4c5 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -129,18 +129,17 @@ end: return err; } -static int -pcm_open(struct snd_pcm_substream *substream) +static int pcm_open(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; const struct snd_bebob_rate_spec *spec = bebob->spec->rate; - unsigned int sampling_rate; + struct amdtp_domain *d = &bebob->domain; enum snd_bebob_clock_type src; int err; err = snd_bebob_stream_lock_try(bebob); if (err < 0) - goto end; + return err; err = pcm_init_hw_params(bebob, substream); if (err < 0) @@ -150,15 +149,20 @@ pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto err_locked; - /* - * When source of clock is internal or any PCM stream are running, - * the available sampling rate is limited at current sampling rate. - */ + mutex_lock(&bebob->mutex); + + // When source of clock is not internal or any stream is reserved for + // transmission of PCM frames, the available sampling rate is limited + // at current one. if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL || - amdtp_stream_pcm_running(&bebob->tx_stream) || - amdtp_stream_pcm_running(&bebob->rx_stream)) { + (bebob->substreams_counter > 0 && d->events_per_period > 0)) { + unsigned int frames_per_period = d->events_per_period; + unsigned int frames_per_buffer = d->events_per_buffer; + unsigned int sampling_rate; + err = spec->get(bebob, &sampling_rate); if (err < 0) { + mutex_unlock(&bebob->mutex); dev_err(&bebob->unit->device, "fail to get sampling rate: %d\n", err); goto err_locked; @@ -166,11 +170,31 @@ pcm_open(struct snd_pcm_substream *substream) substream->runtime->hw.rate_min = sampling_rate; substream->runtime->hw.rate_max = sampling_rate; + + if (frames_per_period > 0) { + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + frames_per_period, frames_per_period); + if (err < 0) { + mutex_unlock(&bebob->mutex); + goto err_locked; + } + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + frames_per_buffer, frames_per_buffer); + if (err < 0) { + mutex_unlock(&bebob->mutex); + goto err_locked; + } + } } + mutex_unlock(&bebob->mutex); + snd_pcm_set_sync(substream); -end: - return err; + + return 0; err_locked: snd_bebob_stream_lock_release(bebob); return err; @@ -197,9 +221,12 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { unsigned int rate = params_rate(hw_params); + unsigned int frames_per_period = params_period_size(hw_params); + unsigned int frames_per_buffer = params_buffer_size(hw_params); mutex_lock(&bebob->mutex); - err = snd_bebob_stream_reserve_duplex(bebob, rate); + err = snd_bebob_stream_reserve_duplex(bebob, rate, + frames_per_period, frames_per_buffer); if (err >= 0) ++bebob->substreams_counter; mutex_unlock(&bebob->mutex); @@ -286,31 +313,33 @@ pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) return 0; } -static snd_pcm_uframes_t -pcm_capture_pointer(struct snd_pcm_substream *sbstrm) +static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_bebob *bebob = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&bebob->tx_stream); + + return amdtp_domain_stream_pcm_pointer(&bebob->domain, + &bebob->tx_stream); } -static snd_pcm_uframes_t -pcm_playback_pointer(struct snd_pcm_substream *sbstrm) +static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_bebob *bebob = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&bebob->rx_stream); + + return amdtp_domain_stream_pcm_pointer(&bebob->domain, + &bebob->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; - return amdtp_stream_pcm_ack(&bebob->tx_stream); + return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; - return amdtp_stream_pcm_ack(&bebob->rx_stream); + return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->rx_stream); } int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 6c1497d9f52b..f7f0db5aa811 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -553,7 +553,9 @@ static int keep_resources(struct snd_bebob *bebob, struct amdtp_stream *stream, return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream)); } -int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate) +int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer) { unsigned int curr_rate; int err; @@ -606,6 +608,14 @@ int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate) cmp_connection_release(&bebob->out_conn); return err; } + + err = amdtp_domain_set_events_per_period(&bebob->domain, + frames_per_period, frames_per_buffer); + if (err < 0) { + cmp_connection_release(&bebob->out_conn); + cmp_connection_release(&bebob->in_conn); + return err; + } } return 0; @@ -647,7 +657,15 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob) if (err < 0) goto error; - err = amdtp_domain_start(&bebob->domain); + // The device postpones start of transmission mostly for 1 sec + // after receives packets firstly. For safe, IR context starts + // 1.5 sec (=12000 cycles) later. This is within 2.0 sec + // (=CALLBACK_TIMEOUT). + // Furthermore, some devices transfer isoc packets with + // discontinuous counter in the beginning of packet streaming. + // The delay has an effect to avoid detection of this + // discontinuity. + err = amdtp_domain_start(&bebob->domain, 12000); if (err < 0) goto error; diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c index c9e19bddfc09..4c2998034313 100644 --- a/sound/firewire/dice/dice-midi.c +++ b/sound/firewire/dice/dice-midi.c @@ -17,7 +17,7 @@ static int midi_open(struct snd_rawmidi_substream *substream) mutex_lock(&dice->mutex); - err = snd_dice_stream_reserve_duplex(dice, 0); + err = snd_dice_stream_reserve_duplex(dice, 0, 0, 0); if (err >= 0) { ++dice->substreams_counter; err = snd_dice_stream_start_duplex(dice); diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 94a4dccfc381..f1848fb39bd0 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -164,13 +164,14 @@ static int init_hw_info(struct snd_dice *dice, static int pcm_open(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; + struct amdtp_domain *d = &dice->domain; unsigned int source; bool internal; int err; err = snd_dice_stream_lock_try(dice); if (err < 0) - goto end; + return err; err = init_hw_info(dice, substream); if (err < 0) @@ -195,27 +196,56 @@ static int pcm_open(struct snd_pcm_substream *substream) break; } - /* - * When source of clock is not internal or any PCM streams are running, - * available sampling rate is limited at current sampling rate. - */ + mutex_lock(&dice->mutex); + + // When source of clock is not internal or any stream is reserved for + // transmission of PCM frames, the available sampling rate is limited + // at current one. if (!internal || - amdtp_stream_pcm_running(&dice->tx_stream[0]) || - amdtp_stream_pcm_running(&dice->tx_stream[1]) || - amdtp_stream_pcm_running(&dice->rx_stream[0]) || - amdtp_stream_pcm_running(&dice->rx_stream[1])) { + (dice->substreams_counter > 0 && d->events_per_period > 0)) { + unsigned int frames_per_period = d->events_per_period; + unsigned int frames_per_buffer = d->events_per_buffer; unsigned int rate; err = snd_dice_transaction_get_rate(dice, &rate); - if (err < 0) + if (err < 0) { + mutex_unlock(&dice->mutex); goto err_locked; + } + substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; + + if (frames_per_period > 0) { + // For double_pcm_frame quirk. + if (rate > 96000) { + frames_per_period *= 2; + frames_per_buffer *= 2; + } + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + frames_per_period, frames_per_period); + if (err < 0) { + mutex_unlock(&dice->mutex); + goto err_locked; + } + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + frames_per_buffer, frames_per_buffer); + if (err < 0) { + mutex_unlock(&dice->mutex); + goto err_locked; + } + } } + mutex_unlock(&dice->mutex); + snd_pcm_set_sync(substream); -end: - return err; + + return 0; err_locked: snd_dice_stream_lock_release(dice); return err; @@ -243,9 +273,17 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { unsigned int rate = params_rate(hw_params); + unsigned int events_per_period = params_period_size(hw_params); + unsigned int events_per_buffer = params_buffer_size(hw_params); mutex_lock(&dice->mutex); - err = snd_dice_stream_reserve_duplex(dice, rate); + // For double_pcm_frame quirk. + if (rate > 96000) { + events_per_period /= 2; + events_per_buffer /= 2; + } + err = snd_dice_stream_reserve_duplex(dice, rate, + events_per_period, events_per_buffer); if (err >= 0) ++dice->substreams_counter; mutex_unlock(&dice->mutex); @@ -341,14 +379,14 @@ static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream) struct snd_dice *dice = substream->private_data; struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device]; - return amdtp_stream_pcm_pointer(stream); + return amdtp_domain_stream_pcm_pointer(&dice->domain, stream); } static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device]; - return amdtp_stream_pcm_pointer(stream); + return amdtp_domain_stream_pcm_pointer(&dice->domain, stream); } static int capture_ack(struct snd_pcm_substream *substream) @@ -356,7 +394,7 @@ static int capture_ack(struct snd_pcm_substream *substream) struct snd_dice *dice = substream->private_data; struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device]; - return amdtp_stream_pcm_ack(stream); + return amdtp_domain_stream_pcm_ack(&dice->domain, stream); } static int playback_ack(struct snd_pcm_substream *substream) @@ -364,7 +402,7 @@ static int playback_ack(struct snd_pcm_substream *substream) struct snd_dice *dice = substream->private_data; struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device]; - return amdtp_stream_pcm_ack(stream); + return amdtp_domain_stream_pcm_ack(&dice->domain, stream); } int snd_dice_create_pcm(struct snd_dice *dice) diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index f6a8627ae5a2..6a3d60913e10 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -278,7 +278,9 @@ static void finish_session(struct snd_dice *dice, struct reg_params *tx_params, snd_dice_transaction_clear_enable(dice); } -int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate) +int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate, + unsigned int events_per_period, + unsigned int events_per_buffer) { unsigned int curr_rate; int err; @@ -324,6 +326,11 @@ int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate) &rx_params); if (err < 0) goto error; + + err = amdtp_domain_set_events_per_period(&dice->domain, + events_per_period, events_per_buffer); + if (err < 0) + goto error; } return 0; @@ -455,7 +462,7 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice) goto error; } - err = amdtp_domain_start(&dice->domain); + err = amdtp_domain_start(&dice->domain, 0); if (err < 0) goto error; diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index fa6d74303f54..16366773e22e 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -210,7 +210,9 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice); void snd_dice_stream_stop_duplex(struct snd_dice *dice); int snd_dice_stream_init_duplex(struct snd_dice *dice); void snd_dice_stream_destroy_duplex(struct snd_dice *dice); -int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate); +int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate, + unsigned int events_per_period, + unsigned int events_per_buffer); void snd_dice_stream_update_duplex(struct snd_dice *dice); int snd_dice_stream_detect_current_formats(struct snd_dice *dice); diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c index 2b57ece89101..68eb8c39afa6 100644 --- a/sound/firewire/digi00x/digi00x-midi.c +++ b/sound/firewire/digi00x/digi00x-midi.c @@ -17,7 +17,7 @@ static int midi_open(struct snd_rawmidi_substream *substream) return err; mutex_lock(&dg00x->mutex); - err = snd_dg00x_stream_reserve_duplex(dg00x, 0); + err = snd_dg00x_stream_reserve_duplex(dg00x, 0, 0, 0); if (err >= 0) { ++dg00x->substreams_counter; err = snd_dg00x_stream_start_duplex(dg00x); diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c index 18e561b26625..8befc5d2ef22 100644 --- a/sound/firewire/digi00x/digi00x-pcm.c +++ b/sound/firewire/digi00x/digi00x-pcm.c @@ -100,14 +100,14 @@ static int pcm_init_hw_params(struct snd_dg00x *dg00x, static int pcm_open(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; + struct amdtp_domain *d = &dg00x->domain; enum snd_dg00x_clock clock; bool detect; - unsigned int rate; int err; err = snd_dg00x_stream_lock_try(dg00x); if (err < 0) - goto end; + return err; err = pcm_init_hw_params(dg00x, substream); if (err < 0) @@ -127,19 +127,49 @@ static int pcm_open(struct snd_pcm_substream *substream) } } + mutex_lock(&dg00x->mutex); + + // When source of clock is not internal or any stream is reserved for + // transmission of PCM frames, the available sampling rate is limited + // at current one. if ((clock != SND_DG00X_CLOCK_INTERNAL) || - amdtp_stream_pcm_running(&dg00x->rx_stream) || - amdtp_stream_pcm_running(&dg00x->tx_stream)) { + (dg00x->substreams_counter > 0 && d->events_per_period > 0)) { + unsigned int frames_per_period = d->events_per_period; + unsigned int frames_per_buffer = d->events_per_buffer; + unsigned int rate; + err = snd_dg00x_stream_get_external_rate(dg00x, &rate); - if (err < 0) + if (err < 0) { + mutex_unlock(&dg00x->mutex); goto err_locked; + } substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; + + if (frames_per_period > 0) { + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + frames_per_period, frames_per_period); + if (err < 0) { + mutex_unlock(&dg00x->mutex); + goto err_locked; + } + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + frames_per_buffer, frames_per_buffer); + if (err < 0) { + mutex_unlock(&dg00x->mutex); + goto err_locked; + } + } } + mutex_unlock(&dg00x->mutex); + snd_pcm_set_sync(substream); -end: - return err; + + return 0; err_locked: snd_dg00x_stream_lock_release(dg00x); return err; @@ -167,9 +197,12 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { unsigned int rate = params_rate(hw_params); + unsigned int frames_per_period = params_period_size(hw_params); + unsigned int frames_per_buffer = params_buffer_size(hw_params); mutex_lock(&dg00x->mutex); - err = snd_dg00x_stream_reserve_duplex(dg00x, rate); + err = snd_dg00x_stream_reserve_duplex(dg00x, rate, + frames_per_period, frames_per_buffer); if (err >= 0) ++dg00x->substreams_counter; mutex_unlock(&dg00x->mutex); @@ -268,28 +301,28 @@ static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_dg00x *dg00x = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&dg00x->tx_stream); + return amdtp_domain_stream_pcm_pointer(&dg00x->domain, &dg00x->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_dg00x *dg00x = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&dg00x->rx_stream); + return amdtp_domain_stream_pcm_pointer(&dg00x->domain, &dg00x->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; - return amdtp_stream_pcm_ack(&dg00x->tx_stream); + return amdtp_domain_stream_pcm_ack(&dg00x->domain, &dg00x->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; - return amdtp_stream_pcm_ack(&dg00x->rx_stream); + return amdtp_domain_stream_pcm_ack(&dg00x->domain, &dg00x->rx_stream); } int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x) diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c index d6a92460060f..405d6903bfbc 100644 --- a/sound/firewire/digi00x/digi00x-stream.c +++ b/sound/firewire/digi00x/digi00x-stream.c @@ -283,7 +283,9 @@ void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x) destroy_stream(dg00x, &dg00x->tx_stream); } -int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate) +int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer) { unsigned int curr_rate; int err; @@ -315,6 +317,14 @@ int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate) fw_iso_resources_free(&dg00x->rx_resources); return err; } + + err = amdtp_domain_set_events_per_period(&dg00x->domain, + frames_per_period, frames_per_buffer); + if (err < 0) { + fw_iso_resources_free(&dg00x->rx_resources); + fw_iso_resources_free(&dg00x->tx_resources); + return err; + } } return 0; @@ -365,7 +375,7 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x) if (err < 0) goto error; - err = amdtp_domain_start(&dg00x->domain); + err = amdtp_domain_start(&dg00x->domain, 0); if (err < 0) goto error; diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index 8041c65f2736..129de8edd5ea 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -141,7 +141,9 @@ int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x, int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect); int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x); -int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate); +int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer); int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x); void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x); void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x); diff --git a/sound/firewire/fireface/ff-pcm.c b/sound/firewire/fireface/ff-pcm.c index 9eab3ad283ce..c29f87a65c0f 100644 --- a/sound/firewire/fireface/ff-pcm.c +++ b/sound/firewire/fireface/ff-pcm.c @@ -139,6 +139,7 @@ static int pcm_init_hw_params(struct snd_ff *ff, static int pcm_open(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; + struct amdtp_domain *d = &ff->domain; unsigned int rate; enum snd_ff_clock_src src; int i, err; @@ -155,16 +156,21 @@ static int pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto release_lock; + mutex_lock(&ff->mutex); + + // When source of clock is not internal or any stream is reserved for + // transmission of PCM frames, the available sampling rate is limited + // at current one. if (src != SND_FF_CLOCK_SRC_INTERNAL) { for (i = 0; i < CIP_SFC_COUNT; ++i) { if (amdtp_rate_table[i] == rate) break; } - /* - * The unit is configured at sampling frequency which packet - * streaming engine can't support. - */ + + // The unit is configured at sampling frequency which packet + // streaming engine can't support. if (i >= CIP_SFC_COUNT) { + mutex_unlock(&ff->mutex); err = -EIO; goto release_lock; } @@ -172,14 +178,34 @@ static int pcm_open(struct snd_pcm_substream *substream) substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } else { - if (amdtp_stream_pcm_running(&ff->rx_stream) || - amdtp_stream_pcm_running(&ff->tx_stream)) { + if (ff->substreams_counter > 0) { + unsigned int frames_per_period = d->events_per_period; + unsigned int frames_per_buffer = d->events_per_buffer; + rate = amdtp_rate_table[ff->rx_stream.sfc]; substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + frames_per_period, frames_per_period); + if (err < 0) { + mutex_unlock(&ff->mutex); + goto release_lock; + } + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + frames_per_buffer, frames_per_buffer); + if (err < 0) { + mutex_unlock(&ff->mutex); + goto release_lock; + } } } + mutex_unlock(&ff->mutex); + snd_pcm_set_sync(substream); return 0; @@ -211,9 +237,12 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { unsigned int rate = params_rate(hw_params); + unsigned int frames_per_period = params_period_size(hw_params); + unsigned int frames_per_buffer = params_buffer_size(hw_params); mutex_lock(&ff->mutex); - err = snd_ff_stream_reserve_duplex(ff, rate); + err = snd_ff_stream_reserve_duplex(ff, rate, frames_per_period, + frames_per_buffer); if (err >= 0) ++ff->substreams_counter; mutex_unlock(&ff->mutex); @@ -312,28 +341,28 @@ static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_ff *ff = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&ff->tx_stream); + return amdtp_domain_stream_pcm_pointer(&ff->domain, &ff->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_ff *ff = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&ff->rx_stream); + return amdtp_domain_stream_pcm_pointer(&ff->domain, &ff->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; - return amdtp_stream_pcm_ack(&ff->tx_stream); + return amdtp_domain_stream_pcm_ack(&ff->domain, &ff->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; - return amdtp_stream_pcm_ack(&ff->rx_stream); + return amdtp_domain_stream_pcm_ack(&ff->domain, &ff->rx_stream); } int snd_ff_create_pcm_devices(struct snd_ff *ff) diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c index e8e6f9fd6433..63b79c4a5405 100644 --- a/sound/firewire/fireface/ff-stream.c +++ b/sound/firewire/fireface/ff-stream.c @@ -106,7 +106,9 @@ void snd_ff_stream_destroy_duplex(struct snd_ff *ff) destroy_stream(ff, &ff->tx_stream); } -int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate) +int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer) { unsigned int curr_rate; enum snd_ff_clock_src src; @@ -150,6 +152,14 @@ int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate) err = ff->spec->protocol->allocate_resources(ff, rate); if (err < 0) return err; + + err = amdtp_domain_set_events_per_period(&ff->domain, + frames_per_period, frames_per_buffer); + if (err < 0) { + fw_iso_resources_free(&ff->tx_resources); + fw_iso_resources_free(&ff->rx_resources); + return err; + } } return 0; @@ -174,6 +184,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) */ if (!amdtp_stream_running(&ff->rx_stream)) { int spd = fw_parent_device(ff->unit)->max_speed; + unsigned int ir_delay_cycle; err = ff->spec->protocol->begin_session(ff, rate); if (err < 0) @@ -189,7 +200,14 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) if (err < 0) goto error; - err = amdtp_domain_start(&ff->domain); + // The device postpones start of transmission mostly for several + // cycles after receiving packets firstly. + if (ff->spec->protocol == &snd_ff_protocol_ff800) + ir_delay_cycle = 800; // = 100 msec + else + ir_delay_cycle = 16; // = 2 msec + + err = amdtp_domain_start(&ff->domain, ir_delay_cycle); if (err < 0) goto error; diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index b4c22ca6079e..dc7a20f75983 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -139,7 +139,9 @@ int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc, enum snd_ff_stream_mode *mode); int snd_ff_stream_init_duplex(struct snd_ff *ff); void snd_ff_stream_destroy_duplex(struct snd_ff *ff); -int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate); +int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer); int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate); void snd_ff_stream_stop_duplex(struct snd_ff *ff); void snd_ff_stream_update_duplex(struct snd_ff *ff); diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h index 4cda297f8438..dda797209a27 100644 --- a/sound/firewire/fireworks/fireworks.h +++ b/sound/firewire/fireworks/fireworks.h @@ -207,7 +207,9 @@ int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate); int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate); int snd_efw_stream_init_duplex(struct snd_efw *efw); -int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate); +int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer); int snd_efw_stream_start_duplex(struct snd_efw *efw); void snd_efw_stream_stop_duplex(struct snd_efw *efw); void snd_efw_stream_update_duplex(struct snd_efw *efw); diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c index a9f4a9630d15..84621e356848 100644 --- a/sound/firewire/fireworks/fireworks_midi.c +++ b/sound/firewire/fireworks/fireworks_midi.c @@ -17,7 +17,7 @@ static int midi_open(struct snd_rawmidi_substream *substream) goto end; mutex_lock(&efw->mutex); - err = snd_efw_stream_reserve_duplex(efw, 0); + err = snd_efw_stream_reserve_duplex(efw, 0, 0, 0); if (err >= 0) { ++efw->substreams_counter; err = snd_efw_stream_start_duplex(efw); diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c index a7025dccc754..64c1bcf28dfa 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -173,13 +173,13 @@ end: static int pcm_open(struct snd_pcm_substream *substream) { struct snd_efw *efw = substream->private_data; - unsigned int sampling_rate; + struct amdtp_domain *d = &efw->domain; enum snd_efw_clock_source clock_source; int err; err = snd_efw_stream_lock_try(efw); if (err < 0) - goto end; + return err; err = pcm_init_hw_params(efw, substream); if (err < 0) @@ -189,23 +189,49 @@ static int pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto err_locked; - /* - * When source of clock is not internal or any PCM streams are running, - * available sampling rate is limited at current sampling rate. - */ + mutex_lock(&efw->mutex); + + // When source of clock is not internal or any stream is reserved for + // transmission of PCM frames, the available sampling rate is limited + // at current one. if ((clock_source != SND_EFW_CLOCK_SOURCE_INTERNAL) || - amdtp_stream_pcm_running(&efw->tx_stream) || - amdtp_stream_pcm_running(&efw->rx_stream)) { + (efw->substreams_counter > 0 && d->events_per_period > 0)) { + unsigned int frames_per_period = d->events_per_period; + unsigned int frames_per_buffer = d->events_per_buffer; + unsigned int sampling_rate; + err = snd_efw_command_get_sampling_rate(efw, &sampling_rate); - if (err < 0) + if (err < 0) { + mutex_unlock(&efw->mutex); goto err_locked; + } substream->runtime->hw.rate_min = sampling_rate; substream->runtime->hw.rate_max = sampling_rate; + + if (frames_per_period > 0) { + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + frames_per_period, frames_per_period); + if (err < 0) { + mutex_unlock(&efw->mutex); + goto err_locked; + } + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + frames_per_buffer, frames_per_buffer); + if (err < 0) { + mutex_unlock(&efw->mutex); + goto err_locked; + } + } } + mutex_unlock(&efw->mutex); + snd_pcm_set_sync(substream); -end: - return err; + + return 0; err_locked: snd_efw_stream_lock_release(efw); return err; @@ -231,9 +257,12 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { unsigned int rate = params_rate(hw_params); + unsigned int frames_per_period = params_period_size(hw_params); + unsigned int frames_per_buffer = params_buffer_size(hw_params); mutex_lock(&efw->mutex); - err = snd_efw_stream_reserve_duplex(efw, rate); + err = snd_efw_stream_reserve_duplex(efw, rate, + frames_per_period, frames_per_buffer); if (err >= 0) ++efw->substreams_counter; mutex_unlock(&efw->mutex); @@ -319,26 +348,28 @@ static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_efw *efw = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&efw->tx_stream); + + return amdtp_domain_stream_pcm_pointer(&efw->domain, &efw->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_efw *efw = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&efw->rx_stream); + + return amdtp_domain_stream_pcm_pointer(&efw->domain, &efw->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_efw *efw = substream->private_data; - return amdtp_stream_pcm_ack(&efw->tx_stream); + return amdtp_domain_stream_pcm_ack(&efw->domain, &efw->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_efw *efw = substream->private_data; - return amdtp_stream_pcm_ack(&efw->rx_stream); + return amdtp_domain_stream_pcm_ack(&efw->domain, &efw->rx_stream); } int snd_efw_create_pcm_devices(struct snd_efw *efw) diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index f2de304d2f26..2206af0fef42 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -181,7 +181,9 @@ static int keep_resources(struct snd_efw *efw, struct amdtp_stream *stream, return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream)); } -int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate) +int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer) { unsigned int curr_rate; int err; @@ -228,6 +230,14 @@ int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate) cmp_connection_release(&efw->in_conn); return err; } + + err = amdtp_domain_set_events_per_period(&efw->domain, + frames_per_period, frames_per_buffer); + if (err < 0) { + cmp_connection_release(&efw->in_conn); + cmp_connection_release(&efw->out_conn); + return err; + } } return 0; @@ -262,7 +272,7 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw) if (err < 0) goto error; - err = amdtp_domain_start(&efw->domain); + err = amdtp_domain_start(&efw->domain, 0); if (err < 0) goto error; diff --git a/sound/firewire/motu/motu-midi.c b/sound/firewire/motu/motu-midi.c index 46a0035df31e..2365f7dfde26 100644 --- a/sound/firewire/motu/motu-midi.c +++ b/sound/firewire/motu/motu-midi.c @@ -17,7 +17,7 @@ static int midi_open(struct snd_rawmidi_substream *substream) mutex_lock(&motu->mutex); - err = snd_motu_stream_reserve_duplex(motu, 0); + err = snd_motu_stream_reserve_duplex(motu, 0, 0, 0); if (err >= 0) { ++motu->substreams_counter; err = snd_motu_stream_start_duplex(motu); diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c index aa2e584da6fe..55d3d6661731 100644 --- a/sound/firewire/motu/motu-pcm.c +++ b/sound/firewire/motu/motu-pcm.c @@ -134,8 +134,8 @@ static int pcm_open(struct snd_pcm_substream *substream) { struct snd_motu *motu = substream->private_data; const struct snd_motu_protocol *const protocol = motu->spec->protocol; + struct amdtp_domain *d = &motu->domain; enum snd_motu_clock_source src; - unsigned int rate; int err; err = snd_motu_stream_lock_try(motu); @@ -152,28 +152,50 @@ static int pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto err_locked; - /* - * When source of clock is not internal or any PCM streams are running, - * available sampling rate is limited at current sampling rate. - */ err = protocol->get_clock_source(motu, &src); if (err < 0) goto err_locked; + + // When source of clock is not internal or any stream is reserved for + // transmission of PCM frames, the available sampling rate is limited + // at current one. if (src != SND_MOTU_CLOCK_SOURCE_INTERNAL || - amdtp_stream_pcm_running(&motu->tx_stream) || - amdtp_stream_pcm_running(&motu->rx_stream)) { + (motu->substreams_counter > 0 && d->events_per_period > 0)) { + unsigned int frames_per_period = d->events_per_period; + unsigned int frames_per_buffer = d->events_per_buffer; + unsigned int rate; + err = protocol->get_clock_rate(motu, &rate); if (err < 0) goto err_locked; + substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; + + if (frames_per_period > 0) { + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + frames_per_period, frames_per_period); + if (err < 0) { + mutex_unlock(&motu->mutex); + goto err_locked; + } + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + frames_per_buffer, frames_per_buffer); + if (err < 0) { + mutex_unlock(&motu->mutex); + goto err_locked; + } + } } snd_pcm_set_sync(substream); mutex_unlock(&motu->mutex); - return err; + return 0; err_locked: mutex_unlock(&motu->mutex); snd_motu_stream_lock_release(motu); @@ -202,9 +224,12 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { unsigned int rate = params_rate(hw_params); + unsigned int frames_per_period = params_period_size(hw_params); + unsigned int frames_per_buffer = params_buffer_size(hw_params); mutex_lock(&motu->mutex); - err = snd_motu_stream_reserve_duplex(motu, rate); + err = snd_motu_stream_reserve_duplex(motu, rate, + frames_per_period, frames_per_buffer); if (err >= 0) ++motu->substreams_counter; mutex_unlock(&motu->mutex); @@ -295,27 +320,27 @@ static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream) { struct snd_motu *motu = substream->private_data; - return amdtp_stream_pcm_pointer(&motu->tx_stream); + return amdtp_domain_stream_pcm_pointer(&motu->domain, &motu->tx_stream); } static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) { struct snd_motu *motu = substream->private_data; - return amdtp_stream_pcm_pointer(&motu->rx_stream); + return amdtp_domain_stream_pcm_pointer(&motu->domain, &motu->rx_stream); } static int capture_ack(struct snd_pcm_substream *substream) { struct snd_motu *motu = substream->private_data; - return amdtp_stream_pcm_ack(&motu->tx_stream); + return amdtp_domain_stream_pcm_ack(&motu->domain, &motu->tx_stream); } static int playback_ack(struct snd_pcm_substream *substream) { struct snd_motu *motu = substream->private_data; - return amdtp_stream_pcm_ack(&motu->rx_stream); + return amdtp_domain_stream_pcm_ack(&motu->domain, &motu->rx_stream); } int snd_motu_create_pcm_devices(struct snd_motu *motu) diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c index 813e38e6a86e..a17ddceb1bec 100644 --- a/sound/firewire/motu/motu-stream.c +++ b/sound/firewire/motu/motu-stream.c @@ -133,7 +133,9 @@ int snd_motu_stream_cache_packet_formats(struct snd_motu *motu) return 0; } -int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate) +int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer) { unsigned int curr_rate; int err; @@ -171,6 +173,14 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate) fw_iso_resources_free(&motu->tx_resources); return err; } + + err = amdtp_domain_set_events_per_period(&motu->domain, + frames_per_period, frames_per_buffer); + if (err < 0) { + fw_iso_resources_free(&motu->tx_resources); + fw_iso_resources_free(&motu->rx_resources); + return err; + } } return 0; @@ -250,7 +260,7 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu) if (err < 0) goto stop_streams; - err = amdtp_domain_start(&motu->domain); + err = amdtp_domain_start(&motu->domain, 0); if (err < 0) goto stop_streams; diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h index 350ee2c16f4a..35ed8b6bb8e8 100644 --- a/sound/firewire/motu/motu.h +++ b/sound/firewire/motu/motu.h @@ -154,7 +154,9 @@ void snd_motu_transaction_unregister(struct snd_motu *motu); int snd_motu_stream_init_duplex(struct snd_motu *motu); void snd_motu_stream_destroy_duplex(struct snd_motu *motu); int snd_motu_stream_cache_packet_formats(struct snd_motu *motu); -int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate); +int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer); int snd_motu_stream_start_duplex(struct snd_motu *motu); void snd_motu_stream_stop_duplex(struct snd_motu *motu); int snd_motu_stream_lock_try(struct snd_motu *motu); diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c index 9bdec08cb8ea..775cba3f1f02 100644 --- a/sound/firewire/oxfw/oxfw-midi.c +++ b/sound/firewire/oxfw/oxfw-midi.c @@ -18,7 +18,7 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream) mutex_lock(&oxfw->mutex); - err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, 0, 0); + err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, 0, 0, 0, 0); if (err >= 0) { ++oxfw->substreams_count; err = snd_oxfw_stream_start_duplex(oxfw); @@ -45,7 +45,7 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream) mutex_lock(&oxfw->mutex); - err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, 0, 0); + err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, 0, 0, 0, 0); if (err >= 0) { ++oxfw->substreams_count; err = snd_oxfw_stream_start_duplex(oxfw); diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c index 7c6d1c277d4d..74bd1811cec2 100644 --- a/sound/firewire/oxfw/oxfw-pcm.c +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -170,30 +170,56 @@ end: static int pcm_open(struct snd_pcm_substream *substream) { struct snd_oxfw *oxfw = substream->private_data; + struct amdtp_domain *d = &oxfw->domain; int err; err = snd_oxfw_stream_lock_try(oxfw); if (err < 0) - goto end; + return err; err = init_hw_params(oxfw, substream); if (err < 0) goto err_locked; - /* - * When any PCM streams are already running, the available sampling - * rate is limited at current value. - */ - if (amdtp_stream_pcm_running(&oxfw->tx_stream) || - amdtp_stream_pcm_running(&oxfw->rx_stream)) { + mutex_lock(&oxfw->mutex); + + // When source of clock is not internal or any stream is reserved for + // transmission of PCM frames, the available sampling rate is limited + // at current one. + if (oxfw->substreams_count > 0 && d->events_per_period > 0) { + unsigned int frames_per_period = d->events_per_period; + unsigned int frames_per_buffer = d->events_per_buffer; + err = limit_to_current_params(substream); - if (err < 0) - goto end; + if (err < 0) { + mutex_unlock(&oxfw->mutex); + goto err_locked; + } + + if (frames_per_period > 0) { + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + frames_per_period, frames_per_period); + if (err < 0) { + mutex_unlock(&oxfw->mutex); + goto err_locked; + } + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + frames_per_buffer, frames_per_buffer); + if (err < 0) { + mutex_unlock(&oxfw->mutex); + goto err_locked; + } + } } + mutex_unlock(&oxfw->mutex); + snd_pcm_set_sync(substream); -end: - return err; + + return 0; err_locked: snd_oxfw_stream_lock_release(oxfw); return err; @@ -221,10 +247,13 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { unsigned int rate = params_rate(hw_params); unsigned int channels = params_channels(hw_params); + unsigned int frames_per_period = params_period_size(hw_params); + unsigned int frames_per_buffer = params_buffer_size(hw_params); mutex_lock(&oxfw->mutex); err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, - rate, channels); + rate, channels, frames_per_period, + frames_per_buffer); if (err >= 0) ++oxfw->substreams_count; mutex_unlock(&oxfw->mutex); @@ -246,10 +275,13 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { unsigned int rate = params_rate(hw_params); unsigned int channels = params_channels(hw_params); + unsigned int frames_per_period = params_period_size(hw_params); + unsigned int frames_per_buffer = params_buffer_size(hw_params); mutex_lock(&oxfw->mutex); err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, - rate, channels); + rate, channels, frames_per_period, + frames_per_buffer); if (err >= 0) ++oxfw->substreams_count; mutex_unlock(&oxfw->mutex); @@ -361,27 +393,27 @@ static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstm) { struct snd_oxfw *oxfw = sbstm->private_data; - return amdtp_stream_pcm_pointer(&oxfw->tx_stream); + return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm) { struct snd_oxfw *oxfw = sbstm->private_data; - return amdtp_stream_pcm_pointer(&oxfw->rx_stream); + return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_oxfw *oxfw = substream->private_data; - return amdtp_stream_pcm_ack(&oxfw->tx_stream); + return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_oxfw *oxfw = substream->private_data; - return amdtp_stream_pcm_ack(&oxfw->rx_stream); + return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->rx_stream); } int snd_oxfw_create_pcm(struct snd_oxfw *oxfw) diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 3c9a796b6526..501a80094bf7 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -244,7 +244,9 @@ static int keep_resources(struct snd_oxfw *oxfw, struct amdtp_stream *stream) int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw, struct amdtp_stream *stream, - unsigned int rate, unsigned int pcm_channels) + unsigned int rate, unsigned int pcm_channels, + unsigned int frames_per_period, + unsigned int frames_per_buffer) { struct snd_oxfw_stream_formation formation; enum avc_general_plug_dir dir; @@ -305,6 +307,15 @@ int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw, return err; } } + + err = amdtp_domain_set_events_per_period(&oxfw->domain, + frames_per_period, frames_per_buffer); + if (err < 0) { + cmp_connection_release(&oxfw->in_conn); + if (oxfw->has_output) + cmp_connection_release(&oxfw->out_conn); + return err; + } } return 0; @@ -344,7 +355,7 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw) } } - err = amdtp_domain_start(&oxfw->domain); + err = amdtp_domain_start(&oxfw->domain, 0); if (err < 0) goto error; diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index c9627b8c5d6e..c30e537087b0 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -103,7 +103,9 @@ int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate, int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw); int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw, struct amdtp_stream *stream, - unsigned int rate, unsigned int pcm_channels); + unsigned int rate, unsigned int pcm_channels, + unsigned int frames_per_period, + unsigned int frames_per_buffer); int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw); void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw); void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw); diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c index 2377732caa52..cd45f20ba515 100644 --- a/sound/firewire/tascam/tascam-pcm.c +++ b/sound/firewire/tascam/tascam-pcm.c @@ -43,13 +43,13 @@ static int pcm_init_hw_params(struct snd_tscm *tscm, static int pcm_open(struct snd_pcm_substream *substream) { struct snd_tscm *tscm = substream->private_data; + struct amdtp_domain *d = &tscm->domain; enum snd_tscm_clock clock; - unsigned int rate; int err; err = snd_tscm_stream_lock_try(tscm); if (err < 0) - goto end; + return err; err = pcm_init_hw_params(tscm, substream); if (err < 0) @@ -59,19 +59,46 @@ static int pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto err_locked; - if (clock != SND_TSCM_CLOCK_INTERNAL || - amdtp_stream_pcm_running(&tscm->rx_stream) || - amdtp_stream_pcm_running(&tscm->tx_stream)) { + mutex_lock(&tscm->mutex); + + // When source of clock is not internal or any stream is reserved for + // transmission of PCM frames, the available sampling rate is limited + // at current one. + if (clock != SND_TSCM_CLOCK_INTERNAL || tscm->substreams_counter > 0) { + unsigned int frames_per_period = d->events_per_period; + unsigned int frames_per_buffer = d->events_per_buffer; + unsigned int rate; + err = snd_tscm_stream_get_rate(tscm, &rate); - if (err < 0) + if (err < 0) { + mutex_unlock(&tscm->mutex); goto err_locked; + } substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + frames_per_period, frames_per_period); + if (err < 0) { + mutex_unlock(&tscm->mutex); + goto err_locked; + } + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + frames_per_buffer, frames_per_buffer); + if (err < 0) { + mutex_unlock(&tscm->mutex); + goto err_locked; + } } + mutex_unlock(&tscm->mutex); + snd_pcm_set_sync(substream); -end: - return err; + + return 0; err_locked: snd_tscm_stream_lock_release(tscm); return err; @@ -99,9 +126,12 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { unsigned int rate = params_rate(hw_params); + unsigned int frames_per_period = params_period_size(hw_params); + unsigned int frames_per_buffer = params_buffer_size(hw_params); mutex_lock(&tscm->mutex); - err = snd_tscm_stream_reserve_duplex(tscm, rate); + err = snd_tscm_stream_reserve_duplex(tscm, rate, + frames_per_period, frames_per_buffer); if (err >= 0) ++tscm->substreams_counter; mutex_unlock(&tscm->mutex); @@ -200,28 +230,28 @@ static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_tscm *tscm = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&tscm->tx_stream); + return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_tscm *tscm = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&tscm->rx_stream); + return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_tscm *tscm = substream->private_data; - return amdtp_stream_pcm_ack(&tscm->tx_stream); + return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_tscm *tscm = substream->private_data; - return amdtp_stream_pcm_ack(&tscm->rx_stream); + return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->rx_stream); } int snd_tscm_create_pcm_devices(struct snd_tscm *tscm) diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c index adf69a520b80..eb07e1decf9b 100644 --- a/sound/firewire/tascam/tascam-stream.c +++ b/sound/firewire/tascam/tascam-stream.c @@ -383,7 +383,9 @@ void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm) destroy_stream(tscm, &tscm->tx_stream); } -int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate) +int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer) { unsigned int curr_rate; int err; @@ -413,6 +415,14 @@ int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate) fw_iso_resources_free(&tscm->tx_resources); return err; } + + err = amdtp_domain_set_events_per_period(&tscm->domain, + frames_per_period, frames_per_buffer); + if (err < 0) { + fw_iso_resources_free(&tscm->tx_resources); + fw_iso_resources_free(&tscm->rx_resources); + return err; + } } return 0; @@ -463,7 +473,7 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate) if (err < 0) goto error; - err = amdtp_domain_start(&tscm->domain); + err = amdtp_domain_start(&tscm->domain, 0); if (err < 0) return err; diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index 15bd335fa07f..78b7a08986a1 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -168,7 +168,9 @@ int snd_tscm_stream_get_clock(struct snd_tscm *tscm, int snd_tscm_stream_init_duplex(struct snd_tscm *tscm); void snd_tscm_stream_update_duplex(struct snd_tscm *tscm); void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm); -int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate); +int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer); int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate); void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm); |