diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 838f695b20de..7486eec4d958 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -52,10 +52,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 +176,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 +198,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 +451,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,28 +467,28 @@ 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, - struct fw_iso_packet *params) + struct fw_iso_packet *params, bool sched_irq) { // Queue one packet for IR context. 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, sched_irq); } static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2], @@ -669,13 +685,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 +706,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 +747,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); @@ -779,12 +796,17 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, { 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->events_per_period; + unsigned int event_count = s->event_count; + unsigned int packets; 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); @@ -796,6 +818,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,12 +829,20 @@ 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) { + 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; } } + s->event_count = event_count; + fw_iso_context_queue_flush(s->context); } @@ -820,15 +851,17 @@ 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 events_per_period = s->events_per_period; + unsigned int event_count = s->event_count; + 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); @@ -842,14 +875,29 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, } for (i = 0; i < packets; ++i) { + const struct pkt_desc *desc = s->pkt_descs + i; struct fw_iso_packet params = {0}; + bool sched_irq = false; - if (queue_in_packet(s, ¶ms) < 0) { + if (err >= 0) { + event_count += desc->data_blocks; + if (event_count >= events_per_period) { + event_count -= events_per_period; + sched_irq = true; + } + } else { + sched_irq = + !((s->packet_index + 1) % s->idle_irq_interval); + } + + if (queue_in_packet(s, ¶ms, sched_irq) < 0) { cancel_stream(s); return; } } + s->event_count = event_count; + fw_iso_context_queue_flush(s->context); } @@ -874,7 +922,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; } @@ -894,7 +942,8 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, * 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) { static const struct { unsigned int data_block; @@ -908,6 +957,8 @@ 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 ctx_header_size; unsigned int max_ctx_payload_size; enum dma_data_direction dir; @@ -953,7 +1004,23 @@ 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; + + s->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]); + s->events_per_period = events_per_period; + s->event_count = 0; + + err = iso_packets_buffer_init(&s->buffer, s->unit, s->queue_size, max_ctx_payload_size, dir); if (err < 0) goto err_unlock; @@ -981,7 +1048,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 +1058,15 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) s->packet_index = 0; do { struct fw_iso_packet params; + bool sched_irq; + + sched_irq = !((s->packet_index + 1) % s->idle_irq_interval); if (s->direction == AMDTP_IN_STREAM) { - err = queue_in_packet(s, ¶ms); + err = queue_in_packet(s, ¶ms, sched_irq); } else { params.header_length = 0; params.payload_length = 0; - err = queue_out_packet(s, ¶ms); + err = queue_out_packet(s, ¶ms, sched_irq); } if (err < 0) goto err_pkt_descs; @@ -1196,7 +1266,7 @@ int amdtp_domain_start(struct amdtp_domain *d) int err = 0; list_for_each_entry(s, &d->streams, list) { - err = amdtp_stream_start(s, s->channel, s->speed); + err = amdtp_stream_start(s, s->channel, s->speed, d); if (err < 0) break; } diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index d95a4ed15f20..344818e936df 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; @@ -144,6 +145,9 @@ struct amdtp_stream { int syt_override; } rx; } ctx_data; + unsigned int event_count; + unsigned int events_per_period; + unsigned int idle_irq_interval; /* For CIP headers. */ unsigned int source_node_id_field; @@ -274,6 +278,7 @@ struct amdtp_domain { struct list_head streams; unsigned int events_per_period; + unsigned int events_per_buffer; }; int amdtp_domain_init(struct amdtp_domain *d); @@ -286,9 +291,11 @@ int amdtp_domain_start(struct amdtp_domain *d); 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_period, + unsigned int events_per_buffer) { d->events_per_period = events_per_period; + d->events_per_buffer = events_per_buffer; return 0; } diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index 8738c1d8abf7..d1ad9a8451bc 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -218,7 +218,8 @@ int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob, 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, - unsigned int frames_per_period); + 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 e8f9edf50be5..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, 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 516c9874f4a1..8b2e0ceffe82 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -157,6 +157,7 @@ static int pcm_open(struct snd_pcm_substream *substream) if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL || (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); @@ -178,6 +179,14 @@ static int pcm_open(struct snd_pcm_substream *substream) 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; + } } } @@ -213,10 +222,11 @@ 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, - frames_per_period); + frames_per_period, frames_per_buffer); if (err >= 0) ++bebob->substreams_counter; mutex_unlock(&bebob->mutex); diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index f1db3ddc3e00..5e4a61458be2 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -555,7 +555,8 @@ static int keep_resources(struct snd_bebob *bebob, struct amdtp_stream *stream, } int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate, - unsigned int frames_per_period) + unsigned int frames_per_period, + unsigned int frames_per_buffer) { unsigned int curr_rate; int err; @@ -610,7 +611,7 @@ int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate, } err = amdtp_domain_set_events_per_period(&bebob->domain, - frames_per_period); + frames_per_period, frames_per_buffer); if (err < 0) { cmp_connection_release(&bebob->out_conn); cmp_connection_release(&bebob->in_conn); diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c index 69c3c06bd7aa..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, 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 813c9ffbeced..7c0c34c5bd47 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -204,6 +204,7 @@ static int pcm_open(struct snd_pcm_substream *substream) if (!internal || (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); @@ -217,8 +218,10 @@ static int pcm_open(struct snd_pcm_substream *substream) if (frames_per_period > 0) { // For double_pcm_frame quirk. - if (rate > 96000) + 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, @@ -227,6 +230,14 @@ static int pcm_open(struct snd_pcm_substream *substream) 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; + } } } @@ -263,13 +274,16 @@ 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); // For double_pcm_frame quirk. - if (rate > 96000) + if (rate > 96000) { events_per_period /= 2; + events_per_buffer /= 2; + } err = snd_dice_stream_reserve_duplex(dice, rate, - events_per_period); + events_per_period, events_per_buffer); if (err >= 0) ++dice->substreams_counter; mutex_unlock(&dice->mutex); diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index ef36bf588d11..0cff346e8052 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -279,7 +279,8 @@ static void finish_session(struct snd_dice *dice, struct reg_params *tx_params, } int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate, - unsigned int events_per_period) + unsigned int events_per_period, + unsigned int events_per_buffer) { unsigned int curr_rate; int err; @@ -327,7 +328,7 @@ int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate, goto error; err = amdtp_domain_set_events_per_period(&dice->domain, - events_per_period); + events_per_period, events_per_buffer); if (err < 0) goto error; } diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index 1f9e3502974e..16366773e22e 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -211,7 +211,8 @@ 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, - unsigned int events_per_period); + 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 a407e795d8e7..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, 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 8dbfb3ff17f6..c9a833dff20d 100644 --- a/sound/firewire/digi00x/digi00x-pcm.c +++ b/sound/firewire/digi00x/digi00x-pcm.c @@ -135,6 +135,7 @@ static int pcm_open(struct snd_pcm_substream *substream) if ((clock != SND_DG00X_CLOCK_INTERNAL) || (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); @@ -153,6 +154,14 @@ static int pcm_open(struct snd_pcm_substream *substream) 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; + } } } @@ -189,10 +198,11 @@ 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, - frames_per_period); + frames_per_period, frames_per_buffer); if (err >= 0) ++dg00x->substreams_counter; mutex_unlock(&dg00x->mutex); diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c index 96d331e47b07..0c539188ba18 100644 --- a/sound/firewire/digi00x/digi00x-stream.c +++ b/sound/firewire/digi00x/digi00x-stream.c @@ -284,7 +284,8 @@ void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x) } int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate, - unsigned int frames_per_period) + unsigned int frames_per_period, + unsigned int frames_per_buffer) { unsigned int curr_rate; int err; @@ -318,7 +319,7 @@ int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate, } err = amdtp_domain_set_events_per_period(&dg00x->domain, - frames_per_period); + frames_per_period, frames_per_buffer); if (err < 0) { fw_iso_resources_free(&dg00x->rx_resources); fw_iso_resources_free(&dg00x->tx_resources); diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index d93694282568..129de8edd5ea 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -142,7 +142,8 @@ 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, - unsigned int frames_per_period); + 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 415bc9ccd1c7..005d959f8651 100644 --- a/sound/firewire/fireface/ff-pcm.c +++ b/sound/firewire/fireface/ff-pcm.c @@ -180,6 +180,7 @@ static int pcm_open(struct snd_pcm_substream *substream) } else { 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; @@ -192,6 +193,14 @@ static int pcm_open(struct snd_pcm_substream *substream) 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; + } } } @@ -229,9 +238,11 @@ 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, frames_per_period); + err = snd_ff_stream_reserve_duplex(ff, rate, frames_per_period, + frames_per_buffer); if (err >= 0) ++ff->substreams_counter; mutex_unlock(&ff->mutex); diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c index d05e7d3055e1..a13754f914e8 100644 --- a/sound/firewire/fireface/ff-stream.c +++ b/sound/firewire/fireface/ff-stream.c @@ -107,7 +107,8 @@ void snd_ff_stream_destroy_duplex(struct snd_ff *ff) } int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate, - unsigned int frames_per_period) + unsigned int frames_per_period, + unsigned int frames_per_buffer) { unsigned int curr_rate; enum snd_ff_clock_src src; @@ -153,7 +154,7 @@ int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate, return err; err = amdtp_domain_set_events_per_period(&ff->domain, - frames_per_period); + frames_per_period, frames_per_buffer); if (err < 0) { fw_iso_resources_free(&ff->tx_resources); fw_iso_resources_free(&ff->rx_resources); diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 970d4ae571ee..dc7a20f75983 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -140,7 +140,8 @@ int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc, 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, - unsigned int frames_per_period); + 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 fc5f945a49ff..dda797209a27 100644 --- a/sound/firewire/fireworks/fireworks.h +++ b/sound/firewire/fireworks/fireworks.h @@ -208,7 +208,8 @@ 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, - unsigned int frames_per_period); + 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 e593f842ee8f..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, 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 314d1f8b8344..abcc53dac8a5 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -197,6 +197,7 @@ static int pcm_open(struct snd_pcm_substream *substream) if ((clock_source != SND_EFW_CLOCK_SOURCE_INTERNAL) || (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); @@ -215,6 +216,14 @@ static int pcm_open(struct snd_pcm_substream *substream) 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; + } } } @@ -249,10 +258,11 @@ 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, - frames_per_period); + frames_per_period, frames_per_buffer); if (err >= 0) ++efw->substreams_counter; mutex_unlock(&efw->mutex); diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index 0787d5c3b01b..f35a33d4d4e6 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -182,7 +182,8 @@ static int keep_resources(struct snd_efw *efw, struct amdtp_stream *stream, } int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate, - unsigned int frames_per_period) + unsigned int frames_per_period, + unsigned int frames_per_buffer) { unsigned int curr_rate; int err; @@ -231,7 +232,7 @@ int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate, } err = amdtp_domain_set_events_per_period(&efw->domain, - frames_per_period); + frames_per_period, frames_per_buffer); if (err < 0) { cmp_connection_release(&efw->in_conn); cmp_connection_release(&efw->out_conn); diff --git a/sound/firewire/motu/motu-midi.c b/sound/firewire/motu/motu-midi.c index 997dd6c8ec31..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, 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 7bbf8b86a33d..00e693da0cad 100644 --- a/sound/firewire/motu/motu-pcm.c +++ b/sound/firewire/motu/motu-pcm.c @@ -162,6 +162,7 @@ static int pcm_open(struct snd_pcm_substream *substream) if (src != SND_MOTU_CLOCK_SOURCE_INTERNAL || (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); @@ -179,6 +180,14 @@ static int pcm_open(struct snd_pcm_substream *substream) 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; + } } } @@ -216,10 +225,11 @@ 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, - frames_per_period); + frames_per_period, frames_per_buffer); if (err >= 0) ++motu->substreams_counter; mutex_unlock(&motu->mutex); diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c index 52b7c375bb0b..9975770c9b1f 100644 --- a/sound/firewire/motu/motu-stream.c +++ b/sound/firewire/motu/motu-stream.c @@ -134,7 +134,8 @@ int snd_motu_stream_cache_packet_formats(struct snd_motu *motu) } int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate, - unsigned int frames_per_period) + unsigned int frames_per_period, + unsigned int frames_per_buffer) { unsigned int curr_rate; int err; @@ -174,7 +175,7 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate, } err = amdtp_domain_set_events_per_period(&motu->domain, - frames_per_period); + frames_per_period, frames_per_buffer); if (err < 0) { fw_iso_resources_free(&motu->tx_resources); fw_iso_resources_free(&motu->rx_resources); diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h index a419e6e7daed..35ed8b6bb8e8 100644 --- a/sound/firewire/motu/motu.h +++ b/sound/firewire/motu/motu.h @@ -155,7 +155,8 @@ 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, - unsigned int frames_per_period); + 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 72db7a17d0ad..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, 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, 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 f3e25898d270..ba586d1ac91d 100644 --- a/sound/firewire/oxfw/oxfw-pcm.c +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -188,6 +188,7 @@ static int pcm_open(struct snd_pcm_substream *substream) // 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) { @@ -203,6 +204,14 @@ static int pcm_open(struct snd_pcm_substream *substream) 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; + } } } @@ -239,10 +248,12 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, 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, frames_per_period); + rate, channels, frames_per_period, + frames_per_buffer); if (err >= 0) ++oxfw->substreams_count; mutex_unlock(&oxfw->mutex); @@ -265,10 +276,12 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream, 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, frames_per_period); + rate, channels, frames_per_period, + frames_per_buffer); if (err >= 0) ++oxfw->substreams_count; mutex_unlock(&oxfw->mutex); diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 7d2e88c5b73d..995e9c5bd84b 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -245,7 +245,8 @@ 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 frames_per_period) + unsigned int frames_per_period, + unsigned int frames_per_buffer) { struct snd_oxfw_stream_formation formation; enum avc_general_plug_dir dir; @@ -308,7 +309,7 @@ int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw, } err = amdtp_domain_set_events_per_period(&oxfw->domain, - frames_per_period); + frames_per_period, frames_per_buffer); if (err < 0) { cmp_connection_release(&oxfw->in_conn); if (oxfw->has_output) diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index 422746ef2439..c30e537087b0 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -104,7 +104,8 @@ 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 frames_per_period); + 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 6cd3a420fbdf..b18664fdf955 100644 --- a/sound/firewire/tascam/tascam-pcm.c +++ b/sound/firewire/tascam/tascam-pcm.c @@ -66,6 +66,7 @@ static int pcm_open(struct snd_pcm_substream *substream) // 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); @@ -83,6 +84,14 @@ static int pcm_open(struct snd_pcm_substream *substream) 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); @@ -118,10 +127,11 @@ 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, - frames_per_period); + frames_per_period, frames_per_buffer); if (err >= 0) ++tscm->substreams_counter; mutex_unlock(&tscm->mutex); diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c index 8c04a0ad17d9..a9b3b7eb6d21 100644 --- a/sound/firewire/tascam/tascam-stream.c +++ b/sound/firewire/tascam/tascam-stream.c @@ -384,7 +384,8 @@ void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm) } int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate, - unsigned int frames_per_period) + unsigned int frames_per_period, + unsigned int frames_per_buffer) { unsigned int curr_rate; int err; @@ -416,7 +417,7 @@ int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate, } err = amdtp_domain_set_events_per_period(&tscm->domain, - frames_per_period); + frames_per_period, frames_per_buffer); if (err < 0) { fw_iso_resources_free(&tscm->tx_resources); fw_iso_resources_free(&tscm->rx_resources); diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index 32e72a25bf46..78b7a08986a1 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -169,7 +169,8 @@ 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, - unsigned int frames_per_period); + 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);