diff options
Diffstat (limited to 'sound')
124 files changed, 2598 insertions, 2565 deletions
diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c index 7b977b753a03..7985dd8198b6 100644 --- a/sound/ac97/bus.c +++ b/sound/ac97/bus.c @@ -122,17 +122,12 @@ static int ac97_codec_add(struct ac97_controller *ac97_ctrl, int idx, vendor_id); ret = device_add(&codec->dev); - if (ret) - goto err_free_codec; + if (ret) { + put_device(&codec->dev); + return ret; + } return 0; -err_free_codec: - of_node_put(codec->dev.of_node); - put_device(&codec->dev); - kfree(codec); - ac97_ctrl->codecs[idx] = NULL; - - return ret; } unsigned int snd_ac97_bus_scan_one(struct ac97_controller *adrv, diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 99b882158705..41905afada63 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -574,10 +574,7 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg) stream->metadata_set = false; stream->next_track = false; - if (stream->direction == SND_COMPRESS_PLAYBACK) - stream->runtime->state = SNDRV_PCM_STATE_SETUP; - else - stream->runtime->state = SNDRV_PCM_STATE_PREPARED; + stream->runtime->state = SNDRV_PCM_STATE_SETUP; } else { return -EPERM; } @@ -693,8 +690,17 @@ static int snd_compr_start(struct snd_compr_stream *stream) { int retval; - if (stream->runtime->state != SNDRV_PCM_STATE_PREPARED) + switch (stream->runtime->state) { + case SNDRV_PCM_STATE_SETUP: + if (stream->direction != SND_COMPRESS_CAPTURE) + return -EPERM; + break; + case SNDRV_PCM_STATE_PREPARED: + break; + default: return -EPERM; + } + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_START); if (!retval) stream->runtime->state = SNDRV_PCM_STATE_RUNNING; @@ -705,9 +711,15 @@ static int snd_compr_stop(struct snd_compr_stream *stream) { int retval; - if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || - stream->runtime->state == SNDRV_PCM_STATE_SETUP) + switch (stream->runtime->state) { + case SNDRV_PCM_STATE_OPEN: + case SNDRV_PCM_STATE_SETUP: + case SNDRV_PCM_STATE_PREPARED: return -EPERM; + default: + break; + } + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); if (!retval) { snd_compr_drain_notify(stream); @@ -795,9 +807,17 @@ static int snd_compr_drain(struct snd_compr_stream *stream) { int retval; - if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || - stream->runtime->state == SNDRV_PCM_STATE_SETUP) + switch (stream->runtime->state) { + case SNDRV_PCM_STATE_OPEN: + case SNDRV_PCM_STATE_SETUP: + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_PAUSED: return -EPERM; + case SNDRV_PCM_STATE_XRUN: + return -EPIPE; + default: + break; + } retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); if (retval) { @@ -817,6 +837,10 @@ static int snd_compr_next_track(struct snd_compr_stream *stream) if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) return -EPERM; + /* next track doesn't have any meaning for capture streams */ + if (stream->direction == SND_COMPRESS_CAPTURE) + return -EPERM; + /* you can signal next track if this is intended to be a gapless stream * and current track metadata is set */ @@ -834,9 +858,23 @@ static int snd_compr_next_track(struct snd_compr_stream *stream) static int snd_compr_partial_drain(struct snd_compr_stream *stream) { int retval; - if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || - stream->runtime->state == SNDRV_PCM_STATE_SETUP) + + switch (stream->runtime->state) { + case SNDRV_PCM_STATE_OPEN: + case SNDRV_PCM_STATE_SETUP: + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_PAUSED: + return -EPERM; + case SNDRV_PCM_STATE_XRUN: + return -EPIPE; + default: + break; + } + + /* partial drain doesn't have any meaning for capture streams */ + if (stream->direction == SND_COMPRESS_CAPTURE) return -EPERM; + /* stream can be drained only when next track has been signalled */ if (stream->next_track == false) return -EPERM; diff --git a/sound/core/control.c b/sound/core/control.c index 5be5b9b931bf..7a4d8690ce41 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -196,16 +196,12 @@ EXPORT_SYMBOL(snd_ctl_notify); static int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count, unsigned int access, struct snd_ctl_file *file) { - unsigned int size; unsigned int idx; if (count == 0 || count > MAX_CONTROL_COUNT) return -EINVAL; - size = sizeof(struct snd_kcontrol); - size += sizeof(struct snd_kcontrol_volatile) * count; - - *kctl = kzalloc(size, GFP_KERNEL); + *kctl = kzalloc(struct_size(*kctl, vd, count), GFP_KERNEL); if (!*kctl) return -ENOMEM; diff --git a/sound/core/oss/rate.c b/sound/core/oss/rate.c index 2fa9299a440d..7cd09cef6961 100644 --- a/sound/core/oss/rate.c +++ b/sound/core/oss/rate.c @@ -323,8 +323,8 @@ int snd_pcm_plugin_build_rate(struct snd_pcm_substream *plug, err = snd_pcm_plugin_build(plug, "rate conversion", src_format, dst_format, - sizeof(struct rate_priv) + - src_format->channels * sizeof(struct rate_channel), + struct_size(data, channels, + src_format->channels), &plugin); if (err < 0) return err; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 860543a4c840..703857aab00f 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -77,7 +77,7 @@ void snd_pcm_group_init(struct snd_pcm_group *group) spin_lock_init(&group->lock); mutex_init(&group->mutex); INIT_LIST_HEAD(&group->substreams); - refcount_set(&group->refs, 0); + refcount_set(&group->refs, 1); } /* define group lock helpers */ @@ -1096,8 +1096,7 @@ static void snd_pcm_group_unref(struct snd_pcm_group *group, if (!group) return; - do_free = refcount_dec_and_test(&group->refs) && - list_empty(&group->substreams); + do_free = refcount_dec_and_test(&group->refs); snd_pcm_group_unlock(group, substream->pcm->nonatomic); if (do_free) kfree(group); @@ -1874,6 +1873,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream, if (!to_check) break; /* all drained */ init_waitqueue_entry(&wait, current); + set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&to_check->sleep, &wait); snd_pcm_stream_unlock_irq(substream); if (runtime->no_period_wakeup) @@ -1886,7 +1886,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream, } tout = msecs_to_jiffies(tout * 1000); } - tout = schedule_timeout_interruptible(tout); + tout = schedule_timeout(tout); snd_pcm_stream_lock_irq(substream); group = snd_pcm_stream_group_ref(substream); @@ -2020,6 +2020,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) snd_pcm_group_lock_irq(target_group, nonatomic); snd_pcm_stream_lock(substream1); snd_pcm_group_assign(substream1, target_group); + refcount_inc(&target_group->refs); snd_pcm_stream_unlock(substream1); snd_pcm_group_unlock_irq(target_group, nonatomic); _end: @@ -2056,13 +2057,14 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream) snd_pcm_group_lock_irq(group, nonatomic); relink_to_local(substream); + refcount_dec(&group->refs); /* detach the last stream, too */ if (list_is_singular(&group->substreams)) { relink_to_local(list_first_entry(&group->substreams, struct snd_pcm_substream, link_list)); - do_free = !refcount_read(&group->refs); + do_free = refcount_dec_and_test(&group->refs); } snd_pcm_group_unlock_irq(group, nonatomic); diff --git a/sound/core/seq/oss/seq_oss_ioctl.c b/sound/core/seq/oss/seq_oss_ioctl.c index 96ad01fb668c..ccf682689ec9 100644 --- a/sound/core/seq/oss/seq_oss_ioctl.c +++ b/sound/core/seq/oss/seq_oss_ioctl.c @@ -49,7 +49,7 @@ static int snd_seq_oss_oob_user(struct seq_oss_devinfo *dp, void __user *arg) if (copy_from_user(ev, arg, 8)) return -EFAULT; memset(&tmpev, 0, sizeof(tmpev)); - snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.port, dp->addr.client); + snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.client, dp->addr.port); tmpev.time.tick = 0; if (! snd_seq_oss_process_event(dp, (union evrec *)ev, &tmpev)) { snd_seq_oss_dispatch(dp, &tmpev, 0, 0); diff --git a/sound/core/seq/oss/seq_oss_rw.c b/sound/core/seq/oss/seq_oss_rw.c index 79ef430e56e1..537d5f423e20 100644 --- a/sound/core/seq/oss/seq_oss_rw.c +++ b/sound/core/seq/oss/seq_oss_rw.c @@ -161,7 +161,7 @@ insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt) memset(&event, 0, sizeof(event)); /* set dummy -- to be sure */ event.type = SNDRV_SEQ_EVENT_NOTEOFF; - snd_seq_oss_fill_addr(dp, &event, dp->addr.port, dp->addr.client); + snd_seq_oss_fill_addr(dp, &event, dp->addr.client, dp->addr.port); if (snd_seq_oss_process_event(dp, rec, &event)) return 0; /* invalid event - no need to insert queue */ diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index a60e7a17f0b8..6d9592f0ae1d 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -1021,7 +1021,7 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf, { struct snd_seq_client *client = file->private_data; int written = 0, len; - int err; + int err, handled; struct snd_seq_event event; if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT)) @@ -1034,6 +1034,8 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf, if (!client->accept_output || client->pool == NULL) return -ENXIO; + repeat: + handled = 0; /* allocate the pool now if the pool is not allocated yet */ mutex_lock(&client->ioctl_mutex); if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) { @@ -1093,12 +1095,19 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf, 0, 0, &client->ioctl_mutex); if (err < 0) break; + handled++; __skip_event: /* Update pointers and counts */ count -= len; buf += len; written += len; + + /* let's have a coffee break if too many events are queued */ + if (++handled >= 200) { + mutex_unlock(&client->ioctl_mutex); + goto repeat; + } } out: @@ -1826,8 +1835,7 @@ static int snd_seq_ioctl_get_client_pool(struct snd_seq_client *client, if (cptr->type == USER_CLIENT) { info->input_pool = cptr->data.user.fifo_pool_size; info->input_free = info->input_pool; - if (cptr->data.user.fifo) - info->input_free = snd_seq_unused_cells(cptr->data.user.fifo->pool); + info->input_free = snd_seq_fifo_unused_cells(cptr->data.user.fifo); } else { info->input_pool = 0; info->input_free = 0; diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c index ea69261f269a..eaaa8b5830bb 100644 --- a/sound/core/seq/seq_fifo.c +++ b/sound/core/seq/seq_fifo.c @@ -263,3 +263,20 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize) return 0; } + +/* get the number of unused cells safely */ +int snd_seq_fifo_unused_cells(struct snd_seq_fifo *f) +{ + unsigned long flags; + int cells; + + if (!f) + return 0; + + snd_use_lock_use(&f->use_lock); + spin_lock_irqsave(&f->lock, flags); + cells = snd_seq_unused_cells(f->pool); + spin_unlock_irqrestore(&f->lock, flags); + snd_use_lock_free(&f->use_lock); + return cells; +} diff --git a/sound/core/seq/seq_fifo.h b/sound/core/seq/seq_fifo.h index edc68743943d..b56a7b897c9c 100644 --- a/sound/core/seq/seq_fifo.h +++ b/sound/core/seq/seq_fifo.h @@ -53,5 +53,7 @@ int snd_seq_fifo_poll_wait(struct snd_seq_fifo *f, struct file *file, poll_table /* resize pool in fifo */ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize); +/* get the number of unused cells safely */ +int snd_seq_fifo_unused_cells(struct snd_seq_fifo *f); #endif diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c index cc6eb30f03a2..fd5d6b8ac557 100644 --- a/sound/firewire/amdtp-am824.c +++ b/sound/firewire/amdtp-am824.c @@ -82,7 +82,7 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, if (err < 0) return err; - s->fdf = AMDTP_FDF_AM824 | s->sfc; + s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc; p->pcm_channels = pcm_channels; p->midi_ports = midi_ports; @@ -320,7 +320,7 @@ static void read_midi_messages(struct amdtp_stream *s, u8 *b; for (f = 0; f < frames; f++) { - port = (s->data_block_counter + f) % 8; + port = (8 - s->ctx_data.tx.first_dbc + s->data_block_counter + f) % 8; b = (u8 *)&buffer[p->midi_position]; len = b[0] - 0x80; diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h index edb5c3afa6f8..4adbbf789cbe 100644 --- a/sound/firewire/amdtp-stream-trace.h +++ b/sound/firewire/amdtp-stream-trace.h @@ -13,147 +13,16 @@ #include <linux/tracepoint.h> -TRACE_EVENT(in_packet, - TP_PROTO(const struct amdtp_stream *s, u32 cycles, u32 *cip_header, unsigned int payload_length, unsigned int index), - TP_ARGS(s, cycles, cip_header, payload_length, index), - TP_STRUCT__entry( - __field(unsigned int, second) - __field(unsigned int, cycle) - __field(int, channel) - __field(int, src) - __field(int, dest) - __field(u32, cip_header0) - __field(u32, cip_header1) - __field(unsigned int, payload_quadlets) - __field(unsigned int, packet_index) - __field(unsigned int, irq) - __field(unsigned int, index) - ), - TP_fast_assign( - __entry->second = cycles / CYCLES_PER_SECOND; - __entry->cycle = cycles % CYCLES_PER_SECOND; - __entry->channel = s->context->channel; - __entry->src = fw_parent_device(s->unit)->node_id; - __entry->dest = fw_parent_device(s->unit)->card->node_id; - __entry->cip_header0 = cip_header[0]; - __entry->cip_header1 = cip_header[1]; - __entry->payload_quadlets = payload_length / 4; - __entry->packet_index = s->packet_index; - __entry->irq = !!in_interrupt(); - __entry->index = index; - ), - TP_printk( - "%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u", - __entry->second, - __entry->cycle, - __entry->src, - __entry->dest, - __entry->channel, - __entry->cip_header0, - __entry->cip_header1, - __entry->payload_quadlets, - __entry->packet_index, - __entry->irq, - __entry->index) -); - -TRACE_EVENT(out_packet, - TP_PROTO(const struct amdtp_stream *s, u32 cycles, __be32 *cip_header, unsigned int payload_length, unsigned int index), - TP_ARGS(s, cycles, cip_header, payload_length, index), - TP_STRUCT__entry( - __field(unsigned int, second) - __field(unsigned int, cycle) - __field(int, channel) - __field(int, src) - __field(int, dest) - __field(u32, cip_header0) - __field(u32, cip_header1) - __field(unsigned int, payload_quadlets) - __field(unsigned int, packet_index) - __field(unsigned int, irq) - __field(unsigned int, index) - ), - TP_fast_assign( - __entry->second = cycles / CYCLES_PER_SECOND; - __entry->cycle = cycles % CYCLES_PER_SECOND; - __entry->channel = s->context->channel; - __entry->src = fw_parent_device(s->unit)->card->node_id; - __entry->dest = fw_parent_device(s->unit)->node_id; - __entry->cip_header0 = be32_to_cpu(cip_header[0]); - __entry->cip_header1 = be32_to_cpu(cip_header[1]); - __entry->payload_quadlets = payload_length / 4; - __entry->packet_index = s->packet_index; - __entry->irq = !!in_interrupt(); - __entry->index = index; - ), - TP_printk( - "%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u", - __entry->second, - __entry->cycle, - __entry->src, - __entry->dest, - __entry->channel, - __entry->cip_header0, - __entry->cip_header1, - __entry->payload_quadlets, - __entry->packet_index, - __entry->irq, - __entry->index) -); - -TRACE_EVENT(in_packet_without_header, - TP_PROTO(const struct amdtp_stream *s, u32 cycles, unsigned int payload_quadlets, unsigned int data_blocks, unsigned int index), - TP_ARGS(s, cycles, payload_quadlets, data_blocks, index), - TP_STRUCT__entry( - __field(unsigned int, second) - __field(unsigned int, cycle) - __field(int, channel) - __field(int, src) - __field(int, dest) - __field(unsigned int, payload_quadlets) - __field(unsigned int, data_blocks) - __field(unsigned int, data_block_counter) - __field(unsigned int, packet_index) - __field(unsigned int, irq) - __field(unsigned int, index) - ), - TP_fast_assign( - __entry->second = cycles / CYCLES_PER_SECOND; - __entry->cycle = cycles % CYCLES_PER_SECOND; - __entry->channel = s->context->channel; - __entry->src = fw_parent_device(s->unit)->node_id; - __entry->dest = fw_parent_device(s->unit)->card->node_id; - __entry->payload_quadlets = payload_quadlets; - __entry->data_blocks = data_blocks, - __entry->data_block_counter = s->data_block_counter, - __entry->packet_index = s->packet_index; - __entry->irq = !!in_interrupt(); - __entry->index = index; - ), - TP_printk( - "%02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u", - __entry->second, - __entry->cycle, - __entry->src, - __entry->dest, - __entry->channel, - __entry->payload_quadlets, - __entry->data_blocks, - __entry->data_block_counter, - __entry->packet_index, - __entry->irq, - __entry->index) -); - -TRACE_EVENT(out_packet_without_header, - TP_PROTO(const struct amdtp_stream *s, u32 cycles, unsigned int payload_length, unsigned int data_blocks, unsigned int index), - TP_ARGS(s, cycles, payload_length, data_blocks, index), +TRACE_EVENT(amdtp_packet, + TP_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int index), + TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, index), TP_STRUCT__entry( __field(unsigned int, second) __field(unsigned int, cycle) __field(int, channel) __field(int, src) __field(int, dest) + __dynamic_array(u8, cip_header, cip_header ? 8 : 0) __field(unsigned int, payload_quadlets) __field(unsigned int, data_blocks) __field(unsigned int, data_block_counter) @@ -165,17 +34,26 @@ TRACE_EVENT(out_packet_without_header, __entry->second = cycles / CYCLES_PER_SECOND; __entry->cycle = cycles % CYCLES_PER_SECOND; __entry->channel = s->context->channel; - __entry->src = fw_parent_device(s->unit)->card->node_id; - __entry->dest = fw_parent_device(s->unit)->node_id; - __entry->payload_quadlets = payload_length / 4; - __entry->data_blocks = data_blocks, + if (s->direction == AMDTP_IN_STREAM) { + __entry->src = fw_parent_device(s->unit)->node_id; + __entry->dest = fw_parent_device(s->unit)->card->node_id; + } else { + __entry->src = fw_parent_device(s->unit)->card->node_id; + __entry->dest = fw_parent_device(s->unit)->node_id; + } + if (cip_header) { + memcpy(__get_dynamic_array(cip_header), cip_header, + __get_dynamic_array_len(cip_header)); + } + __entry->payload_quadlets = payload_length / sizeof(__be32); + __entry->data_blocks = data_blocks; __entry->data_block_counter = s->data_block_counter, __entry->packet_index = s->packet_index; __entry->irq = !!in_interrupt(); __entry->index = index; ), TP_printk( - "%02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u", + "%02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u %s", __entry->second, __entry->cycle, __entry->src, @@ -186,7 +64,10 @@ TRACE_EVENT(out_packet_without_header, __entry->data_block_counter, __entry->packet_index, __entry->irq, - __entry->index) + __entry->index, + __print_array(__get_dynamic_array(cip_header), + __get_dynamic_array_len(cip_header), + sizeof(u8))) ); #endif diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 68f5fa4b183d..4d71d74707cf 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -56,10 +56,15 @@ #define INTERRUPT_INTERVAL 16 #define QUEUE_LENGTH 48 -#define IR_HEADER_SIZE 8 // For header and timestamp. -#define OUT_PACKET_HEADER_SIZE 0 +// For iso header, tstamp and 2 CIP header. +#define IR_CTX_HEADER_SIZE_CIP 16 +// For iso header and tstamp. +#define IR_CTX_HEADER_SIZE_NO_CIP 8 #define HEADER_TSTAMP_MASK 0x0000ffff +#define IT_PKT_HEADER_SIZE_CIP 8 // For 2 CIP header. +#define IT_PKT_HEADER_SIZE_NO_CIP 0 // Nothing. + static void pcm_period_tasklet(unsigned long data); /** @@ -260,11 +265,18 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, s->data_block_quadlets = data_block_quadlets; s->syt_interval = amdtp_syt_intervals[sfc]; - /* default buffering in the device */ - s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; - if (s->flags & CIP_BLOCKING) - /* additional buffering needed to adjust for no-data packets */ - s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate; + // default buffering in the device. + if (s->direction == AMDTP_OUT_STREAM) { + s->ctx_data.rx.transfer_delay = + TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; + + if (s->flags & CIP_BLOCKING) { + // additional buffering needed to adjust for no-data + // packets. + s->ctx_data.rx.transfer_delay += + TICKS_PER_SECOND * s->syt_interval / rate; + } + } return 0; } @@ -280,15 +292,15 @@ EXPORT_SYMBOL(amdtp_stream_set_parameters); unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s) { unsigned int multiplier = 1; - unsigned int header_size = 0; + unsigned int cip_header_size = 0; if (s->flags & CIP_JUMBO_PAYLOAD) multiplier = 5; if (!(s->flags & CIP_NO_HEADER)) - header_size = 8; + cip_header_size = sizeof(__be32) * 2; - return header_size + - s->syt_interval * s->data_block_quadlets * 4 * multiplier; + return cip_header_size + + s->syt_interval * s->data_block_quadlets * sizeof(__be32) * multiplier; } EXPORT_SYMBOL(amdtp_stream_get_max_payload); @@ -321,10 +333,10 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s, /* Non-blocking mode. */ } else { if (!cip_sfc_is_base_44100(s->sfc)) { - /* Sample_rate / 8000 is an integer, and precomputed. */ - data_blocks = s->data_block_state; + // Sample_rate / 8000 is an integer, and precomputed. + data_blocks = s->ctx_data.rx.data_block_state; } else { - phase = s->data_block_state; + phase = s->ctx_data.rx.data_block_state; /* * This calculates the number of data blocks per packet so that @@ -343,7 +355,7 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s, data_blocks = 11 * (s->sfc >> 1) + (phase == 0); if (++phase >= (80 >> (s->sfc >> 1))) phase = 0; - s->data_block_state = phase; + s->ctx_data.rx.data_block_state = phase; } } @@ -355,9 +367,10 @@ static unsigned int calculate_syt(struct amdtp_stream *s, { unsigned int syt_offset, phase, index, syt; - if (s->last_syt_offset < TICKS_PER_CYCLE) { + if (s->ctx_data.rx.last_syt_offset < TICKS_PER_CYCLE) { if (!cip_sfc_is_base_44100(s->sfc)) - syt_offset = s->last_syt_offset + s->syt_offset_state; + syt_offset = s->ctx_data.rx.last_syt_offset + + s->ctx_data.rx.syt_offset_state; else { /* * The time, in ticks, of the n'th SYT_INTERVAL sample is: @@ -369,21 +382,21 @@ static unsigned int calculate_syt(struct amdtp_stream *s, * 1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ... * This code generates _exactly_ the same sequence. */ - phase = s->syt_offset_state; + phase = s->ctx_data.rx.syt_offset_state; index = phase % 13; - syt_offset = s->last_syt_offset; + syt_offset = s->ctx_data.rx.last_syt_offset; syt_offset += 1386 + ((index && !(index & 3)) || phase == 146); if (++phase >= 147) phase = 0; - s->syt_offset_state = phase; + s->ctx_data.rx.syt_offset_state = phase; } } else - syt_offset = s->last_syt_offset - TICKS_PER_CYCLE; - s->last_syt_offset = syt_offset; + syt_offset = s->ctx_data.rx.last_syt_offset - TICKS_PER_CYCLE; + s->ctx_data.rx.last_syt_offset = syt_offset; if (syt_offset < TICKS_PER_CYCLE) { - syt_offset += s->transfer_delay; + syt_offset += s->ctx_data.rx.transfer_delay; syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; syt += syt_offset % TICKS_PER_CYCLE; @@ -420,23 +433,15 @@ static void pcm_period_tasklet(unsigned long data) snd_pcm_period_elapsed(pcm); } -static int queue_packet(struct amdtp_stream *s, unsigned int header_length, - unsigned int payload_length) +static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params) { - struct fw_iso_packet p = {0}; - int err = 0; + int err; - if (IS_ERR(s->context)) - goto end; + params->interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL); + params->tag = s->tag; + params->sy = 0; - p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL); - p.tag = s->tag; - p.header_length = header_length; - if (payload_length > 0) - p.payload_length = payload_length; - else - p.skip = true; - err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer, + err = fw_iso_context_queue(s->context, params, &s->buffer.iso_buffer, s->buffer.packets[s->packet_index].offset); if (err < 0) { dev_err(&s->unit->device, "queueing error: %d\n", err); @@ -450,112 +455,83 @@ end: } static inline int queue_out_packet(struct amdtp_stream *s, - unsigned int payload_length) + struct fw_iso_packet *params) { - return queue_packet(s, OUT_PACKET_HEADER_SIZE, payload_length); + params->skip = + !!(params->header_length == 0 && params->payload_length == 0); + return queue_packet(s, params); } -static inline int queue_in_packet(struct amdtp_stream *s) +static inline int queue_in_packet(struct amdtp_stream *s, + struct fw_iso_packet *params) { - return queue_packet(s, IR_HEADER_SIZE, s->max_payload_length); + // 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); } -static int handle_out_packet(struct amdtp_stream *s, - unsigned int payload_length, unsigned int cycle, - unsigned int index) +static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2], + unsigned int syt) { - __be32 *buffer; - unsigned int syt; - unsigned int data_blocks; - unsigned int pcm_frames; - struct snd_pcm_substream *pcm; - - buffer = s->buffer.packets[s->packet_index].buffer; - syt = calculate_syt(s, cycle); - data_blocks = calculate_data_blocks(s, syt); - pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt); - - if (s->flags & CIP_DBC_IS_END_EVENT) - s->data_block_counter = - (s->data_block_counter + data_blocks) & 0xff; - - buffer[0] = cpu_to_be32(READ_ONCE(s->source_node_id_field) | + cip_header[0] = cpu_to_be32(READ_ONCE(s->source_node_id_field) | (s->data_block_quadlets << CIP_DBS_SHIFT) | ((s->sph << CIP_SPH_SHIFT) & CIP_SPH_MASK) | s->data_block_counter); - buffer[1] = cpu_to_be32(CIP_EOH | - ((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) | - ((s->fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) | - (syt & CIP_SYT_MASK)); - - if (!(s->flags & CIP_DBC_IS_END_EVENT)) - s->data_block_counter = - (s->data_block_counter + data_blocks) & 0xff; - payload_length = 8 + data_blocks * 4 * s->data_block_quadlets; - - trace_out_packet(s, cycle, buffer, payload_length, index); - - if (queue_out_packet(s, payload_length) < 0) - return -EIO; - - pcm = READ_ONCE(s->pcm); - if (pcm && pcm_frames > 0) - update_pcm_pointers(s, pcm, pcm_frames); - - /* No need to return the number of handled data blocks. */ - return 0; + cip_header[1] = cpu_to_be32(CIP_EOH | + ((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) | + ((s->ctx_data.rx.fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) | + (syt & CIP_SYT_MASK)); } -static int handle_out_packet_without_header(struct amdtp_stream *s, - unsigned int payload_length, unsigned int cycle, - unsigned int index) +static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle, + struct fw_iso_packet *params, + unsigned int data_blocks, unsigned int syt, + unsigned int index) { - __be32 *buffer; - unsigned int syt; - unsigned int data_blocks; - unsigned int pcm_frames; - struct snd_pcm_substream *pcm; - - buffer = s->buffer.packets[s->packet_index].buffer; - syt = calculate_syt(s, cycle); - data_blocks = calculate_data_blocks(s, syt); - pcm_frames = s->process_data_blocks(s, buffer, data_blocks, &syt); - s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; + unsigned int payload_length; + __be32 *cip_header; - payload_length = data_blocks * 4 * s->data_block_quadlets; + payload_length = data_blocks * sizeof(__be32) * s->data_block_quadlets; + params->payload_length = payload_length; - trace_out_packet_without_header(s, cycle, payload_length, data_blocks, - index); + if (s->flags & CIP_DBC_IS_END_EVENT) { + s->data_block_counter = + (s->data_block_counter + data_blocks) & 0xff; + } - if (queue_out_packet(s, payload_length) < 0) - return -EIO; + if (!(s->flags & CIP_NO_HEADER)) { + cip_header = (__be32 *)params->header; + generate_cip_header(s, cip_header, syt); + params->header_length = 2 * sizeof(__be32); + payload_length += params->header_length; + } else { + cip_header = NULL; + } - pcm = READ_ONCE(s->pcm); - if (pcm && pcm_frames > 0) - update_pcm_pointers(s, pcm, pcm_frames); + trace_amdtp_packet(s, cycle, cip_header, payload_length, data_blocks, + index); - /* No need to return the number of handled data blocks. */ - return 0; + if (!(s->flags & CIP_DBC_IS_END_EVENT)) { + s->data_block_counter = + (s->data_block_counter + data_blocks) & 0xff; + } } -static int handle_in_packet(struct amdtp_stream *s, - unsigned int payload_length, unsigned int cycle, - unsigned int index) +static int check_cip_header(struct amdtp_stream *s, const __be32 *buf, + unsigned int payload_length, + unsigned int *data_blocks, unsigned int *dbc, + unsigned int *syt) { - __be32 *buffer; u32 cip_header[2]; - unsigned int sph, fmt, fdf, syt; - unsigned int data_block_quadlets, data_block_counter, dbc_interval; - unsigned int data_blocks; - struct snd_pcm_substream *pcm; - unsigned int pcm_frames; + unsigned int sph; + unsigned int fmt; + unsigned int fdf; bool lost; - buffer = s->buffer.packets[s->packet_index].buffer; - cip_header[0] = be32_to_cpu(buffer[0]); - cip_header[1] = be32_to_cpu(buffer[1]); - - trace_in_packet(s, cycle, cip_header, payload_length, index); + cip_header[0] = be32_to_cpu(buf[0]); + cip_header[1] = be32_to_cpu(buf[1]); /* * This module supports 'Two-quadlet CIP header with SYT field'. @@ -567,9 +543,7 @@ static int handle_in_packet(struct amdtp_stream *s, dev_info_ratelimited(&s->unit->device, "Invalid CIP header for AMDTP: %08X:%08X\n", cip_header[0], cip_header[1]); - data_blocks = 0; - pcm_frames = 0; - goto end; + return -EAGAIN; } /* Check valid protocol or not. */ @@ -579,19 +553,17 @@ static int handle_in_packet(struct amdtp_stream *s, dev_info_ratelimited(&s->unit->device, "Detect unexpected protocol: %08x %08x\n", cip_header[0], cip_header[1]); - data_blocks = 0; - pcm_frames = 0; - goto end; + return -EAGAIN; } /* Calculate data blocks */ fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT; - if (payload_length < 12 || + if (payload_length < sizeof(__be32) * 2 || (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) { - data_blocks = 0; + *data_blocks = 0; } else { - data_block_quadlets = - (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT; + unsigned int data_block_quadlets = + (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT; /* avoid division by zero */ if (data_block_quadlets == 0) { dev_err(&s->unit->device, @@ -602,95 +574,97 @@ static int handle_in_packet(struct amdtp_stream *s, if (s->flags & CIP_WRONG_DBS) data_block_quadlets = s->data_block_quadlets; - data_blocks = (payload_length / 4 - 2) / + *data_blocks = (payload_length / sizeof(__be32) - 2) / data_block_quadlets; } /* Check data block counter continuity */ - data_block_counter = cip_header[0] & CIP_DBC_MASK; - if (data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) && + *dbc = cip_header[0] & CIP_DBC_MASK; + if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) && s->data_block_counter != UINT_MAX) - data_block_counter = s->data_block_counter; + *dbc = s->data_block_counter; if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) && - data_block_counter == s->tx_first_dbc) || + *dbc == s->ctx_data.tx.first_dbc) || s->data_block_counter == UINT_MAX) { lost = false; } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) { - lost = data_block_counter != s->data_block_counter; + lost = *dbc != s->data_block_counter; } else { - if (data_blocks > 0 && s->tx_dbc_interval > 0) - dbc_interval = s->tx_dbc_interval; + unsigned int dbc_interval; + + if (*data_blocks > 0 && s->ctx_data.tx.dbc_interval > 0) + dbc_interval = s->ctx_data.tx.dbc_interval; else - dbc_interval = data_blocks; + dbc_interval = *data_blocks; - lost = data_block_counter != - ((s->data_block_counter + dbc_interval) & 0xff); + lost = *dbc != ((s->data_block_counter + dbc_interval) & 0xff); } if (lost) { dev_err(&s->unit->device, "Detect discontinuity of CIP: %02X %02X\n", - s->data_block_counter, data_block_counter); + s->data_block_counter, *dbc); return -EIO; } - syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; - pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt); - - if (s->flags & CIP_DBC_IS_END_EVENT) - s->data_block_counter = data_block_counter; - else - s->data_block_counter = - (data_block_counter + data_blocks) & 0xff; -end: - if (queue_in_packet(s) < 0) - return -EIO; - - pcm = READ_ONCE(s->pcm); - if (pcm && pcm_frames > 0) - update_pcm_pointers(s, pcm, pcm_frames); + *syt = cip_header[1] & CIP_SYT_MASK; return 0; } -static int handle_in_packet_without_header(struct amdtp_stream *s, - unsigned int payload_length, unsigned int cycle, - unsigned int index) +static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle, + const __be32 *ctx_header, + unsigned int *payload_length, + unsigned int *data_blocks, unsigned int *syt, + unsigned int index) { - __be32 *buffer; - unsigned int payload_quadlets; - unsigned int data_blocks; - struct snd_pcm_substream *pcm; - unsigned int pcm_frames; - - buffer = s->buffer.packets[s->packet_index].buffer; - payload_quadlets = payload_length / 4; - data_blocks = payload_quadlets / s->data_block_quadlets; + unsigned int dbc; + const __be32 *cip_header; + int err; - trace_in_packet_without_header(s, cycle, payload_quadlets, data_blocks, - index); + *payload_length = be32_to_cpu(ctx_header[0]) >> ISO_DATA_LENGTH_SHIFT; + if (*payload_length > s->ctx_data.tx.ctx_header_size + + s->ctx_data.tx.max_ctx_payload_length) { + dev_err(&s->unit->device, + "Detect jumbo payload: %04x %04x\n", + *payload_length, s->ctx_data.tx.max_ctx_payload_length); + return -EIO; + } - pcm_frames = s->process_data_blocks(s, buffer, data_blocks, NULL); - s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; + if (!(s->flags & CIP_NO_HEADER)) { + cip_header = ctx_header + 2; + err = check_cip_header(s, cip_header, *payload_length, + data_blocks, &dbc, syt); + if (err < 0) + return err; + } else { + cip_header = NULL; + err = 0; + *data_blocks = *payload_length / sizeof(__be32) / + s->data_block_quadlets; + *syt = 0; + + if (s->data_block_counter != UINT_MAX) + dbc = s->data_block_counter; + else + dbc = 0; + } - if (queue_in_packet(s) < 0) - return -EIO; + s->data_block_counter = dbc; - pcm = READ_ONCE(s->pcm); - if (pcm && pcm_frames > 0) - update_pcm_pointers(s, pcm, pcm_frames); + trace_amdtp_packet(s, cycle, cip_header, *payload_length, *data_blocks, + index); - return 0; + return err; } -/* - * In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On - * the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent - * it. Thus, via Linux firewire subsystem, we can get the 3 bits for second. - */ -static inline u32 compute_cycle_count(u32 tstamp) +// In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On +// the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent +// it. Thus, via Linux firewire subsystem, we can get the 3 bits for second. +static inline u32 compute_cycle_count(__be32 ctx_header_tstamp) { + u32 tstamp = be32_to_cpu(ctx_header_tstamp) & HEADER_TSTAMP_MASK; return (((tstamp >> 13) & 0x07) * 8000) + (tstamp & 0x1fff); } @@ -702,31 +676,68 @@ static inline u32 increment_cycle_count(u32 cycle, unsigned int addend) return cycle; } +// 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) +{ + u32 cycle = compute_cycle_count(ctx_header_tstamp); + return increment_cycle_count(cycle, QUEUE_LENGTH); +} + +static inline void cancel_stream(struct amdtp_stream *s) +{ + s->packet_index = -1; + if (in_interrupt()) + amdtp_stream_pcm_abort(s); + WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN); +} + 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; - unsigned int i, packets = header_length / 4; - u32 cycle; + const __be32 *ctx_header = header; + unsigned int packets = header_length / sizeof(*ctx_header); + int i; if (s->packet_index < 0) return; - cycle = compute_cycle_count(tstamp); - - /* Align to actual cycle count for the last packet. */ - cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets); - for (i = 0; i < packets; ++i) { - cycle = increment_cycle_count(cycle, 1); - if (s->handle_packet(s, 0, cycle, i) < 0) { - s->packet_index = -1; - if (in_interrupt()) - amdtp_stream_pcm_abort(s); - WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN); + u32 cycle; + unsigned int syt; + unsigned int data_blocks; + __be32 *buffer; + unsigned int pcm_frames; + struct { + struct fw_iso_packet params; + __be32 header[IT_PKT_HEADER_SIZE_CIP / sizeof(__be32)]; + } template = { {0}, {0} }; + struct snd_pcm_substream *pcm; + + cycle = compute_it_cycle(*ctx_header); + syt = calculate_syt(s, cycle); + data_blocks = calculate_data_blocks(s, syt); + buffer = s->buffer.packets[s->packet_index].buffer; + pcm_frames = s->process_data_blocks(s, buffer, data_blocks, + &syt); + + build_it_pkt_header(s, cycle, &template.params, data_blocks, + syt, i); + + if (queue_out_packet(s, &template.params) < 0) { + cancel_stream(s); return; } + + pcm = READ_ONCE(s->pcm); + if (pcm && pcm_frames > 0) + update_pcm_pointers(s, pcm, pcm_frames); + + ++ctx_header; } fw_iso_context_queue_flush(s->context); @@ -738,46 +749,55 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, { struct amdtp_stream *s = private_data; unsigned int i, packets; - unsigned int payload_length, max_payload_length; __be32 *ctx_header = header; if (s->packet_index < 0) return; - /* The number of packets in buffer */ - packets = header_length / IR_HEADER_SIZE; - - /* For buffer-over-run prevention. */ - max_payload_length = s->max_payload_length; + // The number of packets in buffer. + packets = header_length / s->ctx_data.tx.ctx_header_size; for (i = 0; i < packets; i++) { - u32 iso_header = be32_to_cpu(ctx_header[0]); - unsigned int cycle; + u32 cycle; + unsigned int payload_length; + unsigned int data_blocks; + unsigned int syt; + __be32 *buffer; + unsigned int pcm_frames = 0; + struct fw_iso_packet params = {0}; + struct snd_pcm_substream *pcm; + int err; + + cycle = compute_cycle_count(ctx_header[1]); + err = parse_ir_ctx_header(s, cycle, ctx_header, &payload_length, + &data_blocks, &syt, i); + if (err < 0 && err != -EAGAIN) + break; - tstamp = be32_to_cpu(ctx_header[1]) & HEADER_TSTAMP_MASK; - cycle = compute_cycle_count(tstamp); + if (err >= 0) { + buffer = s->buffer.packets[s->packet_index].buffer; + pcm_frames = s->process_data_blocks(s, buffer, + data_blocks, &syt); - /* The number of bytes in this packet */ - payload_length = iso_header >> ISO_DATA_LENGTH_SHIFT; - if (payload_length > max_payload_length) { - dev_err(&s->unit->device, - "Detect jumbo payload: %04x %04x\n", - payload_length, max_payload_length); - break; + if (!(s->flags & CIP_DBC_IS_END_EVENT)) { + s->data_block_counter += data_blocks; + s->data_block_counter &= 0xff; + } } - if (s->handle_packet(s, payload_length, cycle, i) < 0) + if (queue_in_packet(s, ¶ms) < 0) break; - ctx_header += IR_HEADER_SIZE / sizeof(__be32); + pcm = READ_ONCE(s->pcm); + if (pcm && pcm_frames > 0) + update_pcm_pointers(s, pcm, pcm_frames); + + ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header); } /* Queueing error or detecting invalid payload. */ if (i < packets) { - s->packet_index = -1; - if (in_interrupt()) - amdtp_stream_pcm_abort(s); - WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN); + cancel_stream(s); return; } @@ -790,9 +810,8 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, void *header, void *private_data) { struct amdtp_stream *s = private_data; - __be32 *ctx_header = header; + const __be32 *ctx_header = header; u32 cycle; - unsigned int packets; /* * For in-stream, first packet has come. @@ -802,23 +821,13 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, wake_up(&s->callback_wait); if (s->direction == AMDTP_IN_STREAM) { - tstamp = be32_to_cpu(ctx_header[1]) & HEADER_TSTAMP_MASK; - cycle = compute_cycle_count(tstamp); + cycle = compute_cycle_count(ctx_header[1]); context->callback.sc = in_stream_callback; - if (s->flags & CIP_NO_HEADER) - s->handle_packet = handle_in_packet_without_header; - else - s->handle_packet = handle_in_packet; } else { - packets = header_length / 4; - cycle = compute_cycle_count(tstamp); - cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets); + cycle = compute_it_cycle(*ctx_header); + context->callback.sc = out_stream_callback; - if (s->flags & CIP_NO_HEADER) - s->handle_packet = handle_out_packet_without_header; - else - s->handle_packet = handle_out_packet; } s->start_cycle = cycle; @@ -841,7 +850,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) static const struct { unsigned int data_block; unsigned int syt_offset; - } initial_state[] = { + } *entry, initial_state[] = { [CIP_SFC_32000] = { 4, 3072 }, [CIP_SFC_48000] = { 6, 1024 }, [CIP_SFC_96000] = { 12, 1024 }, @@ -850,7 +859,8 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) [CIP_SFC_88200] = { 0, 67 }, [CIP_SFC_176400] = { 0, 67 }, }; - unsigned int header_size; + unsigned int ctx_header_size; + unsigned int max_ctx_payload_size; enum dma_data_direction dir; int type, tag, err; @@ -862,32 +872,46 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) goto err_unlock; } - if (s->direction == AMDTP_IN_STREAM) + if (s->direction == AMDTP_IN_STREAM) { s->data_block_counter = UINT_MAX; - else + } else { + entry = &initial_state[s->sfc]; + s->data_block_counter = 0; - s->data_block_state = initial_state[s->sfc].data_block; - s->syt_offset_state = initial_state[s->sfc].syt_offset; - s->last_syt_offset = TICKS_PER_CYCLE; + s->ctx_data.rx.data_block_state = entry->data_block; + s->ctx_data.rx.syt_offset_state = entry->syt_offset; + s->ctx_data.rx.last_syt_offset = TICKS_PER_CYCLE; + } /* initialize packet buffer */ if (s->direction == AMDTP_IN_STREAM) { dir = DMA_FROM_DEVICE; type = FW_ISO_CONTEXT_RECEIVE; - header_size = IR_HEADER_SIZE; + if (!(s->flags & CIP_NO_HEADER)) + ctx_header_size = IR_CTX_HEADER_SIZE_CIP; + else + ctx_header_size = IR_CTX_HEADER_SIZE_NO_CIP; + + max_ctx_payload_size = amdtp_stream_get_max_payload(s) - + ctx_header_size; } else { dir = DMA_TO_DEVICE; type = FW_ISO_CONTEXT_TRANSMIT; - header_size = OUT_PACKET_HEADER_SIZE; + ctx_header_size = 0; // No effect for IT context. + + max_ctx_payload_size = amdtp_stream_get_max_payload(s); + if (!(s->flags & CIP_NO_HEADER)) + max_ctx_payload_size -= IT_PKT_HEADER_SIZE_CIP; } + err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, - amdtp_stream_get_max_payload(s), dir); + max_ctx_payload_size, dir); if (err < 0) goto err_unlock; s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, - type, channel, speed, header_size, - amdtp_stream_first_callback, s); + type, channel, speed, ctx_header_size, + amdtp_stream_first_callback, s); if (IS_ERR(s->context)) { err = PTR_ERR(s->context); if (err == -EBUSY) @@ -898,8 +922,10 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) amdtp_stream_update(s); - if (s->direction == AMDTP_IN_STREAM) - s->max_payload_length = amdtp_stream_get_max_payload(s); + if (s->direction == AMDTP_IN_STREAM) { + s->ctx_data.tx.max_ctx_payload_length = max_ctx_payload_size; + s->ctx_data.tx.ctx_header_size = ctx_header_size; + } if (s->flags & CIP_NO_HEADER) s->tag = TAG_NO_CIP_HEADER; @@ -908,10 +934,14 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) s->packet_index = 0; do { - if (s->direction == AMDTP_IN_STREAM) - err = queue_in_packet(s); - else - err = queue_out_packet(s, 0); + struct fw_iso_packet params; + if (s->direction == AMDTP_IN_STREAM) { + err = queue_in_packet(s, ¶ms); + } else { + params.header_length = 0; + params.payload_length = 0; + err = queue_out_packet(s, ¶ms); + } if (err < 0) goto err_context; } while (s->packet_index > 0); diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index e45de3eecfe3..3942894c11ac 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -108,10 +108,31 @@ struct amdtp_stream { struct iso_packets_buffer buffer; int packet_index; int tag; - int (*handle_packet)(struct amdtp_stream *s, - unsigned int payload_quadlets, unsigned int cycle, - unsigned int index); - unsigned int max_payload_length; + union { + struct { + unsigned int ctx_header_size; + + // limit for payload of iso packet. + unsigned int max_ctx_payload_length; + + // For quirks of CIP headers. + // Fixed interval of dbc between previos/current + // packets. + unsigned int dbc_interval; + // Indicate the value of dbc field in a first packet. + unsigned int first_dbc; + } tx; + struct { + // To calculate CIP data blocks and tstamp. + unsigned int transfer_delay; + unsigned int data_block_state; + unsigned int last_syt_offset; + unsigned int syt_offset_state; + + // To generate CIP header. + unsigned int fdf; + } rx; + } ctx_data; /* For CIP headers. */ unsigned int source_node_id_field; @@ -119,19 +140,10 @@ struct amdtp_stream { unsigned int data_block_counter; unsigned int sph; unsigned int fmt; - unsigned int fdf; - /* quirk: fixed interval of dbc between previos/current packets. */ - unsigned int tx_dbc_interval; - /* quirk: indicate the value of dbc field in a first packet. */ - unsigned int tx_first_dbc; /* Internal flags. */ enum cip_sfc sfc; unsigned int syt_interval; - unsigned int transfer_delay; - unsigned int data_block_state; - unsigned int last_syt_offset; - unsigned int syt_offset_state; /* For a PCM substream processing. */ struct snd_pcm_substream *pcm; diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index af71dac9f084..9e0b689fe34a 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -92,8 +92,6 @@ struct snd_bebob { unsigned int midi_input_ports; unsigned int midi_output_ports; - bool connected; - struct amdtp_stream tx_stream; struct amdtp_stream rx_stream; struct cmp_connection out_conn; @@ -217,7 +215,8 @@ 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_start_duplex(struct snd_bebob *bebob, unsigned int rate); +int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate); +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 c54ac42622ad..4d8805fa8a00 100644 --- a/sound/firewire/bebob/bebob_midi.c +++ b/sound/firewire/bebob/bebob_midi.c @@ -7,58 +7,31 @@ #include "bebob.h" -static int midi_capture_open(struct snd_rawmidi_substream *substream) +static int midi_open(struct snd_rawmidi_substream *substream) { struct snd_bebob *bebob = substream->rmidi->private_data; int err; err = snd_bebob_stream_lock_try(bebob); if (err < 0) - goto end; + return err; mutex_lock(&bebob->mutex); - bebob->substreams_counter++; - err = snd_bebob_stream_start_duplex(bebob, 0); + err = snd_bebob_stream_reserve_duplex(bebob, 0); + if (err >= 0) { + ++bebob->substreams_counter; + err = snd_bebob_stream_start_duplex(bebob); + if (err < 0) + --bebob->substreams_counter; + } mutex_unlock(&bebob->mutex); if (err < 0) snd_bebob_stream_lock_release(bebob); -end: - return err; -} - -static int midi_playback_open(struct snd_rawmidi_substream *substream) -{ - struct snd_bebob *bebob = substream->rmidi->private_data; - int err; - - err = snd_bebob_stream_lock_try(bebob); - if (err < 0) - goto end; - mutex_lock(&bebob->mutex); - bebob->substreams_counter++; - err = snd_bebob_stream_start_duplex(bebob, 0); - mutex_unlock(&bebob->mutex); - if (err < 0) - snd_bebob_stream_lock_release(bebob); -end: return err; } -static int midi_capture_close(struct snd_rawmidi_substream *substream) -{ - struct snd_bebob *bebob = substream->rmidi->private_data; - - mutex_lock(&bebob->mutex); - bebob->substreams_counter--; - snd_bebob_stream_stop_duplex(bebob); - mutex_unlock(&bebob->mutex); - - snd_bebob_stream_lock_release(bebob); - return 0; -} - -static int midi_playback_close(struct snd_rawmidi_substream *substream) +static int midi_close(struct snd_rawmidi_substream *substream) { struct snd_bebob *bebob = substream->rmidi->private_data; @@ -120,13 +93,13 @@ static void set_midi_substream_names(struct snd_bebob *bebob, int snd_bebob_create_midi_devices(struct snd_bebob *bebob) { static const struct snd_rawmidi_ops capture_ops = { - .open = midi_capture_open, - .close = midi_capture_close, + .open = midi_open, + .close = midi_close, .trigger = midi_capture_trigger, }; static const struct snd_rawmidi_ops playback_ops = { - .open = midi_playback_open, - .close = midi_playback_close, + .open = midi_open, + .close = midi_close, .trigger = midi_playback_trigger, }; struct snd_rawmidi *rmidi; diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index 2f50ec7b0147..0fb9eed46837 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -184,9 +184,8 @@ pcm_close(struct snd_pcm_substream *substream) return 0; } -static int -pcm_capture_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) +static int pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) { struct snd_bebob *bebob = substream->private_data; int err; @@ -197,62 +196,31 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream, return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { - mutex_lock(&bebob->mutex); - bebob->substreams_counter++; - mutex_unlock(&bebob->mutex); - } + unsigned int rate = params_rate(hw_params); - return 0; -} -static int -pcm_playback_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_bebob *bebob = substream->private_data; - int err; - - err = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - return err; - - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&bebob->mutex); - bebob->substreams_counter++; + err = snd_bebob_stream_reserve_duplex(bebob, rate); + if (err >= 0) + ++bebob->substreams_counter; mutex_unlock(&bebob->mutex); } - return 0; + return err; } -static int -pcm_capture_hw_free(struct snd_pcm_substream *substream) +static int pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { - mutex_lock(&bebob->mutex); - bebob->substreams_counter--; - mutex_unlock(&bebob->mutex); - } - - snd_bebob_stream_stop_duplex(bebob); + mutex_lock(&bebob->mutex); - return snd_pcm_lib_free_vmalloc_buffer(substream); -} -static int -pcm_playback_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_bebob *bebob = substream->private_data; - - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { - mutex_lock(&bebob->mutex); + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) bebob->substreams_counter--; - mutex_unlock(&bebob->mutex); - } snd_bebob_stream_stop_duplex(bebob); + mutex_unlock(&bebob->mutex); + return snd_pcm_lib_free_vmalloc_buffer(substream); } @@ -260,10 +228,9 @@ static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; int err; - err = snd_bebob_stream_start_duplex(bebob, runtime->rate); + err = snd_bebob_stream_start_duplex(bebob); if (err >= 0) amdtp_stream_pcm_prepare(&bebob->tx_stream); @@ -273,10 +240,9 @@ static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; int err; - err = snd_bebob_stream_start_duplex(bebob, runtime->rate); + err = snd_bebob_stream_start_duplex(bebob); if (err >= 0) amdtp_stream_pcm_prepare(&bebob->rx_stream); @@ -353,8 +319,8 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_capture_hw_params, - .hw_free = pcm_capture_hw_free, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, @@ -365,8 +331,8 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_playback_hw_params, - .hw_free = pcm_playback_hw_free, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 0c93a825cb98..334dc7c96e1d 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -376,24 +376,6 @@ end: } static int -init_both_connections(struct snd_bebob *bebob) -{ - int err; - - err = cmp_connection_init(&bebob->in_conn, - bebob->unit, CMP_INPUT, 0); - if (err < 0) - goto end; - - err = cmp_connection_init(&bebob->out_conn, - bebob->unit, CMP_OUTPUT, 0); - if (err < 0) - cmp_connection_destroy(&bebob->in_conn); -end: - return err; -} - -static int check_connection_used_by_others(struct snd_bebob *bebob, struct amdtp_stream *s) { struct cmp_connection *conn; @@ -417,49 +399,21 @@ check_connection_used_by_others(struct snd_bebob *bebob, struct amdtp_stream *s) return err; } -static int -make_both_connections(struct snd_bebob *bebob, unsigned int rate) +static int make_both_connections(struct snd_bebob *bebob) { - int index, pcm_channels, midi_channels, err = 0; - - if (bebob->connected) - goto end; - - /* confirm params for both streams */ - err = get_formation_index(rate, &index); - if (err < 0) - goto end; - pcm_channels = bebob->tx_stream_formations[index].pcm; - midi_channels = bebob->tx_stream_formations[index].midi; - err = amdtp_am824_set_parameters(&bebob->tx_stream, rate, - pcm_channels, midi_channels * 8, - false); - if (err < 0) - goto end; + int err = 0; - pcm_channels = bebob->rx_stream_formations[index].pcm; - midi_channels = bebob->rx_stream_formations[index].midi; - err = amdtp_am824_set_parameters(&bebob->rx_stream, rate, - pcm_channels, midi_channels * 8, - false); + err = cmp_connection_establish(&bebob->out_conn); if (err < 0) - goto end; + return err; - /* establish connections for both streams */ - err = cmp_connection_establish(&bebob->out_conn, - amdtp_stream_get_max_payload(&bebob->tx_stream)); - if (err < 0) - goto end; - err = cmp_connection_establish(&bebob->in_conn, - amdtp_stream_get_max_payload(&bebob->rx_stream)); + err = cmp_connection_establish(&bebob->in_conn); if (err < 0) { cmp_connection_break(&bebob->out_conn); - goto end; + return err; } - bebob->connected = true; -end: - return err; + return 0; } static void @@ -468,23 +422,13 @@ break_both_connections(struct snd_bebob *bebob) cmp_connection_break(&bebob->in_conn); cmp_connection_break(&bebob->out_conn); - bebob->connected = false; - /* These models seems to be in transition state for a longer time. */ if (bebob->maudio_special_quirk != NULL) msleep(200); } -static void -destroy_both_connections(struct snd_bebob *bebob) -{ - cmp_connection_destroy(&bebob->in_conn); - cmp_connection_destroy(&bebob->out_conn); -} - static int -start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream, - unsigned int rate) +start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream) { struct cmp_connection *conn; int err = 0; @@ -509,190 +453,252 @@ end: return err; } -int snd_bebob_stream_init_duplex(struct snd_bebob *bebob) +static int init_stream(struct snd_bebob *bebob, struct amdtp_stream *stream) { + enum amdtp_stream_direction dir_stream; + struct cmp_connection *conn; + enum cmp_direction dir_conn; int err; - err = init_both_connections(bebob); + if (stream == &bebob->tx_stream) { + dir_stream = AMDTP_IN_STREAM; + conn = &bebob->out_conn; + dir_conn = CMP_OUTPUT; + } else { + dir_stream = AMDTP_OUT_STREAM; + conn = &bebob->in_conn; + dir_conn = CMP_INPUT; + } + + err = cmp_connection_init(conn, bebob->unit, dir_conn, 0); if (err < 0) - goto end; + return err; - err = amdtp_am824_init(&bebob->tx_stream, bebob->unit, - AMDTP_IN_STREAM, CIP_BLOCKING); + err = amdtp_am824_init(stream, bebob->unit, dir_stream, CIP_BLOCKING); if (err < 0) { - amdtp_stream_destroy(&bebob->tx_stream); - destroy_both_connections(bebob); - goto end; + cmp_connection_destroy(conn); + return err; } - /* - * BeBoB v3 transfers packets with these qurks: - * - In the beginning of streaming, the value of dbc is incremented - * even if no data blocks are transferred. - * - The value of dbc is reset suddenly. - */ - if (bebob->version > 2) - bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC | - CIP_SKIP_DBC_ZERO_CHECK; + if (stream == &bebob->tx_stream) { + // BeBoB v3 transfers packets with these qurks: + // - In the beginning of streaming, the value of dbc is + // incremented even if no data blocks are transferred. + // - The value of dbc is reset suddenly. + if (bebob->version > 2) + bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC | + CIP_SKIP_DBC_ZERO_CHECK; + + // At high sampling rate, M-Audio special firmware transmits + // empty packet with the value of dbc incremented by 8 but the + // others are valid to IEC 61883-1. + if (bebob->maudio_special_quirk) + bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC; + } - /* - * At high sampling rate, M-Audio special firmware transmits empty - * packet with the value of dbc incremented by 8 but the others are - * valid to IEC 61883-1. - */ - if (bebob->maudio_special_quirk) - bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC; + return 0; +} + +static void destroy_stream(struct snd_bebob *bebob, struct amdtp_stream *stream) +{ + amdtp_stream_destroy(stream); + + if (stream == &bebob->tx_stream) + cmp_connection_destroy(&bebob->out_conn); + else + cmp_connection_destroy(&bebob->in_conn); +} + +int snd_bebob_stream_init_duplex(struct snd_bebob *bebob) +{ + int err; - err = amdtp_am824_init(&bebob->rx_stream, bebob->unit, - AMDTP_OUT_STREAM, CIP_BLOCKING); + err = init_stream(bebob, &bebob->tx_stream); + if (err < 0) + return err; + + err = init_stream(bebob, &bebob->rx_stream); if (err < 0) { - amdtp_stream_destroy(&bebob->tx_stream); - amdtp_stream_destroy(&bebob->rx_stream); - destroy_both_connections(bebob); + destroy_stream(bebob, &bebob->tx_stream); + return err; } -end: - return err; + + return 0; } -int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate) +static int keep_resources(struct snd_bebob *bebob, struct amdtp_stream *stream, + unsigned int rate, unsigned int index) { - const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate; - unsigned int curr_rate; - int err = 0; + struct snd_bebob_stream_formation *formation; + struct cmp_connection *conn; + int err; - /* Need no substreams */ - if (bebob->substreams_counter == 0) - goto end; + if (stream == &bebob->tx_stream) { + formation = bebob->tx_stream_formations + index; + conn = &bebob->out_conn; + } else { + formation = bebob->rx_stream_formations + index; + conn = &bebob->in_conn; + } - /* - * Considering JACK/FFADO streaming: - * TODO: This can be removed hwdep functionality becomes popular. - */ + err = amdtp_am824_set_parameters(stream, rate, formation->pcm, + formation->midi, false); + if (err < 0) + return err; + + return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream)); +} + +int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate) +{ + unsigned int curr_rate; + int err; + + // Considering JACK/FFADO streaming: + // TODO: This can be removed hwdep functionality becomes popular. err = check_connection_used_by_others(bebob, &bebob->rx_stream); if (err < 0) - goto end; + return err; - /* - * packet queueing error or detecting discontinuity - * - * At bus reset, connections should not be broken here. So streams need - * to be re-started. This is a reason to use SKIP_INIT_DBC_CHECK flag. - */ - if (amdtp_streaming_error(&bebob->rx_stream)) - amdtp_stream_stop(&bebob->rx_stream); - if (amdtp_streaming_error(&bebob->tx_stream)) + err = bebob->spec->rate->get(bebob, &curr_rate); + if (err < 0) + return err; + if (rate == 0) + rate = curr_rate; + if (curr_rate != rate) { amdtp_stream_stop(&bebob->tx_stream); - if (!amdtp_stream_running(&bebob->rx_stream) && - !amdtp_stream_running(&bebob->tx_stream)) + amdtp_stream_stop(&bebob->rx_stream); + break_both_connections(bebob); - /* stop streams if rate is different */ - err = rate_spec->get(bebob, &curr_rate); - if (err < 0) { - dev_err(&bebob->unit->device, - "fail to get sampling rate: %d\n", err); - goto end; + cmp_connection_release(&bebob->out_conn); + cmp_connection_release(&bebob->in_conn); } - if (rate == 0) - rate = curr_rate; - if (rate != curr_rate) { + + if (bebob->substreams_counter == 0 || curr_rate != rate) { + unsigned int index; + + // NOTE: + // If establishing connections at first, Yamaha GO46 + // (and maybe Terratec X24) don't generate sound. + // + // For firmware customized by M-Audio, refer to next NOTE. + err = bebob->spec->rate->set(bebob, rate); + if (err < 0) { + dev_err(&bebob->unit->device, + "fail to set sampling rate: %d\n", + err); + return err; + } + + err = get_formation_index(rate, &index); + if (err < 0) + return err; + + err = keep_resources(bebob, &bebob->tx_stream, rate, index); + if (err < 0) + return err; + + err = keep_resources(bebob, &bebob->rx_stream, rate, index); + if (err < 0) { + cmp_connection_release(&bebob->out_conn); + return err; + } + } + + return 0; +} + +int snd_bebob_stream_start_duplex(struct snd_bebob *bebob) +{ + int err; + + // Need no substreams. + if (bebob->substreams_counter == 0) + return -EIO; + + // packet queueing error or detecting discontinuity + if (amdtp_streaming_error(&bebob->rx_stream) || + amdtp_streaming_error(&bebob->tx_stream)) { amdtp_stream_stop(&bebob->rx_stream); amdtp_stream_stop(&bebob->tx_stream); + break_both_connections(bebob); } - /* master should be always running */ if (!amdtp_stream_running(&bebob->rx_stream)) { - /* - * NOTE: - * If establishing connections at first, Yamaha GO46 - * (and maybe Terratec X24) don't generate sound. - * - * For firmware customized by M-Audio, refer to next NOTE. - */ - if (bebob->maudio_special_quirk == NULL) { - err = rate_spec->set(bebob, rate); - if (err < 0) { - dev_err(&bebob->unit->device, - "fail to set sampling rate: %d\n", - err); - goto end; - } + unsigned int curr_rate; + + if (bebob->maudio_special_quirk) { + err = bebob->spec->rate->get(bebob, &curr_rate); + if (err < 0) + return err; } - err = make_both_connections(bebob, rate); + err = make_both_connections(bebob); if (err < 0) - goto end; + return err; - err = start_stream(bebob, &bebob->rx_stream, rate); + err = start_stream(bebob, &bebob->rx_stream); if (err < 0) { dev_err(&bebob->unit->device, "fail to run AMDTP master stream:%d\n", err); - break_both_connections(bebob); - goto end; + goto error; } - /* - * NOTE: - * The firmware customized by M-Audio uses these commands to - * start transmitting stream. This is not usual way. - */ - if (bebob->maudio_special_quirk != NULL) { - err = rate_spec->set(bebob, rate); + // NOTE: + // The firmware customized by M-Audio uses these commands to + // start transmitting stream. This is not usual way. + if (bebob->maudio_special_quirk) { + err = bebob->spec->rate->set(bebob, curr_rate); if (err < 0) { dev_err(&bebob->unit->device, "fail to ensure sampling rate: %d\n", err); - amdtp_stream_stop(&bebob->rx_stream); - break_both_connections(bebob); - goto end; + goto error; } } - /* wait first callback */ if (!amdtp_stream_wait_callback(&bebob->rx_stream, CALLBACK_TIMEOUT)) { - amdtp_stream_stop(&bebob->rx_stream); - break_both_connections(bebob); err = -ETIMEDOUT; - goto end; + goto error; } } - /* start slave if needed */ if (!amdtp_stream_running(&bebob->tx_stream)) { - err = start_stream(bebob, &bebob->tx_stream, rate); + err = start_stream(bebob, &bebob->tx_stream); if (err < 0) { dev_err(&bebob->unit->device, "fail to run AMDTP slave stream:%d\n", err); - amdtp_stream_stop(&bebob->rx_stream); - break_both_connections(bebob); - goto end; + goto error; } - /* wait first callback */ if (!amdtp_stream_wait_callback(&bebob->tx_stream, CALLBACK_TIMEOUT)) { - amdtp_stream_stop(&bebob->tx_stream); - amdtp_stream_stop(&bebob->rx_stream); - break_both_connections(bebob); err = -ETIMEDOUT; + goto error; } } -end: + + return 0; +error: + amdtp_stream_stop(&bebob->tx_stream); + amdtp_stream_stop(&bebob->rx_stream); + break_both_connections(bebob); return err; } void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) { if (bebob->substreams_counter == 0) { - amdtp_stream_pcm_abort(&bebob->rx_stream); amdtp_stream_stop(&bebob->rx_stream); - - amdtp_stream_pcm_abort(&bebob->tx_stream); amdtp_stream_stop(&bebob->tx_stream); break_both_connections(bebob); + + cmp_connection_release(&bebob->out_conn); + cmp_connection_release(&bebob->in_conn); } } @@ -702,10 +708,8 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob) */ void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob) { - amdtp_stream_destroy(&bebob->rx_stream); - amdtp_stream_destroy(&bebob->tx_stream); - - destroy_both_connections(bebob); + destroy_stream(bebob, &bebob->tx_stream); + destroy_stream(bebob, &bebob->rx_stream); } /* diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c index 13f8abc19cfb..14abbe7175b6 100644 --- a/sound/firewire/cmp.c +++ b/sound/firewire/cmp.c @@ -185,6 +185,37 @@ void cmp_connection_destroy(struct cmp_connection *c) } EXPORT_SYMBOL(cmp_connection_destroy); +int cmp_connection_reserve(struct cmp_connection *c, + unsigned int max_payload_bytes) +{ + int err; + + mutex_lock(&c->mutex); + + if (WARN_ON(c->resources.allocated)) { + err = -EBUSY; + goto end; + } + + c->speed = min(c->max_speed, + fw_parent_device(c->resources.unit)->max_speed); + + err = fw_iso_resources_allocate(&c->resources, max_payload_bytes, + c->speed); +end: + mutex_unlock(&c->mutex); + + return err; +} +EXPORT_SYMBOL(cmp_connection_reserve); + +void cmp_connection_release(struct cmp_connection *c) +{ + mutex_lock(&c->mutex); + fw_iso_resources_free(&c->resources); + mutex_unlock(&c->mutex); +} +EXPORT_SYMBOL(cmp_connection_release); static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr) { @@ -270,25 +301,18 @@ static int pcr_set_check(struct cmp_connection *c, __be32 pcr) * When this function succeeds, the caller is responsible for starting * transmitting packets. */ -int cmp_connection_establish(struct cmp_connection *c, - unsigned int max_payload_bytes) +int cmp_connection_establish(struct cmp_connection *c) { int err; - if (WARN_ON(c->connected)) - return -EISCONN; - - c->speed = min(c->max_speed, - fw_parent_device(c->resources.unit)->max_speed); - mutex_lock(&c->mutex); -retry_after_bus_reset: - err = fw_iso_resources_allocate(&c->resources, - max_payload_bytes, c->speed); - if (err < 0) - goto err_mutex; + if (WARN_ON(c->connected)) { + mutex_unlock(&c->mutex); + return -EISCONN; + } +retry_after_bus_reset: if (c->direction == CMP_OUTPUT) err = pcr_modify(c, opcr_set_modify, pcr_set_check, ABORT_ON_BUS_RESET); @@ -297,21 +321,13 @@ retry_after_bus_reset: ABORT_ON_BUS_RESET); if (err == -EAGAIN) { - fw_iso_resources_free(&c->resources); - goto retry_after_bus_reset; + err = fw_iso_resources_update(&c->resources); + if (err >= 0) + goto retry_after_bus_reset; } - if (err < 0) - goto err_resources; - - c->connected = true; - - mutex_unlock(&c->mutex); - - return 0; + if (err >= 0) + c->connected = true; -err_resources: - fw_iso_resources_free(&c->resources); -err_mutex: mutex_unlock(&c->mutex); return err; @@ -351,14 +367,12 @@ int cmp_connection_update(struct cmp_connection *c) SUCCEED_ON_BUS_RESET); if (err < 0) - goto err_resources; + goto err_unconnect; mutex_unlock(&c->mutex); return 0; -err_resources: - fw_iso_resources_free(&c->resources); err_unconnect: c->connected = false; mutex_unlock(&c->mutex); @@ -395,8 +409,6 @@ void cmp_connection_break(struct cmp_connection *c) if (err < 0) cmp_error(c, "plug is still connected\n"); - fw_iso_resources_free(&c->resources); - c->connected = false; mutex_unlock(&c->mutex); diff --git a/sound/firewire/cmp.h b/sound/firewire/cmp.h index b60b415caa8f..26ab88000e34 100644 --- a/sound/firewire/cmp.h +++ b/sound/firewire/cmp.h @@ -42,8 +42,11 @@ int cmp_connection_init(struct cmp_connection *connection, int cmp_connection_check_used(struct cmp_connection *connection, bool *used); void cmp_connection_destroy(struct cmp_connection *connection); -int cmp_connection_establish(struct cmp_connection *connection, - unsigned int max_payload); +int cmp_connection_reserve(struct cmp_connection *connection, + unsigned int max_payload); +void cmp_connection_release(struct cmp_connection *connection); + +int cmp_connection_establish(struct cmp_connection *connection); int cmp_connection_update(struct cmp_connection *connection); void cmp_connection_break(struct cmp_connection *connection); diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile index 115eadd8d42e..7a62dafd0f78 100644 --- a/sound/firewire/dice/Makefile +++ b/sound/firewire/dice/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \ dice-pcm.o dice-hwdep.o dice.o dice-tcelectronic.o \ - dice-alesis.o dice-extension.o dice-mytek.o + dice-alesis.o dice-extension.o dice-mytek.o dice-presonus.o obj-$(CONFIG_SND_DICE) += snd-dice.o diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c index ca7ae427e892..c9e19bddfc09 100644 --- a/sound/firewire/dice/dice-midi.c +++ b/sound/firewire/dice/dice-midi.c @@ -17,8 +17,13 @@ static int midi_open(struct snd_rawmidi_substream *substream) mutex_lock(&dice->mutex); - dice->substreams_counter++; - err = snd_dice_stream_start_duplex(dice, 0); + err = snd_dice_stream_reserve_duplex(dice, 0); + if (err >= 0) { + ++dice->substreams_counter; + err = snd_dice_stream_start_duplex(dice); + if (err < 0) + --dice->substreams_counter; + } mutex_unlock(&dice->mutex); @@ -34,7 +39,7 @@ static int midi_close(struct snd_rawmidi_substream *substream) mutex_lock(&dice->mutex); - dice->substreams_counter--; + --dice->substreams_counter; snd_dice_stream_stop_duplex(dice); mutex_unlock(&dice->mutex); diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 8a601befc11e..94a4dccfc381 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -230,8 +230,8 @@ static int pcm_close(struct snd_pcm_substream *substream) return 0; } -static int capture_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) +static int pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; int err; @@ -242,57 +242,26 @@ static int capture_hw_params(struct snd_pcm_substream *substream, return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { - mutex_lock(&dice->mutex); - dice->substreams_counter++; - mutex_unlock(&dice->mutex); - } - - return 0; -} -static int playback_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_dice *dice = substream->private_data; - int err; - - err = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - return err; + unsigned int rate = params_rate(hw_params); - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&dice->mutex); - dice->substreams_counter++; + err = snd_dice_stream_reserve_duplex(dice, rate); + if (err >= 0) + ++dice->substreams_counter; mutex_unlock(&dice->mutex); } - return 0; -} - -static int capture_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_dice *dice = substream->private_data; - - mutex_lock(&dice->mutex); - - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - dice->substreams_counter--; - - snd_dice_stream_stop_duplex(dice); - - mutex_unlock(&dice->mutex); - - return snd_pcm_lib_free_vmalloc_buffer(substream); + return err; } -static int playback_hw_free(struct snd_pcm_substream *substream) +static int pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; mutex_lock(&dice->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - dice->substreams_counter--; + --dice->substreams_counter; snd_dice_stream_stop_duplex(dice); @@ -308,7 +277,7 @@ static int capture_prepare(struct snd_pcm_substream *substream) int err; mutex_lock(&dice->mutex); - err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); + err = snd_dice_stream_start_duplex(dice); mutex_unlock(&dice->mutex); if (err >= 0) amdtp_stream_pcm_prepare(stream); @@ -322,7 +291,7 @@ static int playback_prepare(struct snd_pcm_substream *substream) int err; mutex_lock(&dice->mutex); - err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); + err = snd_dice_stream_start_duplex(dice); mutex_unlock(&dice->mutex); if (err >= 0) amdtp_stream_pcm_prepare(stream); @@ -404,8 +373,8 @@ int snd_dice_create_pcm(struct snd_dice *dice) .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = capture_hw_params, - .hw_free = capture_hw_free, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, .prepare = capture_prepare, .trigger = capture_trigger, .pointer = capture_pointer, @@ -416,8 +385,8 @@ int snd_dice_create_pcm(struct snd_dice *dice) .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = playback_hw_params, - .hw_free = playback_hw_free, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, .prepare = playback_prepare, .trigger = playback_trigger, .pointer = playback_pointer, diff --git a/sound/firewire/dice/dice-presonus.c b/sound/firewire/dice/dice-presonus.c new file mode 100644 index 000000000000..503f462a83f4 --- /dev/null +++ b/sound/firewire/dice/dice-presonus.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0 +// dice-presonus.c - a part of driver for DICE based devices +// +// Copyright (c) 2019 Takashi Sakamoto +// +// Licensed under the terms of the GNU General Public License, version 2. + +#include "dice.h" + +struct dice_presonus_spec { + unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT]; + unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT]; + bool has_midi; +}; + +static const struct dice_presonus_spec dice_presonus_firesutio = { + .tx_pcm_chs = {{16, 16, 0}, {10, 2, 0} }, + .rx_pcm_chs = {{16, 16, 0}, {10, 2, 0} }, + .has_midi = true, +}; + +int snd_dice_detect_presonus_formats(struct snd_dice *dice) +{ + static const struct { + u32 model_id; + const struct dice_presonus_spec *spec; + } *entry, entries[] = { + {0x000008, &dice_presonus_firesutio}, + }; + struct fw_csr_iterator it; + int key, val, model_id; + int i; + + model_id = 0; + fw_csr_iterator_init(&it, dice->unit->directory); + while (fw_csr_iterator_next(&it, &key, &val)) { + if (key == CSR_MODEL) { + model_id = val; + break; + } + } + + for (i = 0; i < ARRAY_SIZE(entries); ++i) { + entry = entries + i; + if (entry->model_id == model_id) + break; + } + if (i == ARRAY_SIZE(entries)) + return -ENODEV; + + memcpy(dice->tx_pcm_chs, entry->spec->tx_pcm_chs, + MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int)); + memcpy(dice->rx_pcm_chs, entry->spec->rx_pcm_chs, + MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int)); + + if (entry->spec->has_midi) { + dice->tx_midi_ports[0] = 1; + dice->rx_midi_ports[0] = 1; + } + + return 0; +} diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 7a93ae3dc58b..a9f0c77734c3 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -137,18 +137,9 @@ static int get_register_params(struct snd_dice *dice, static void release_resources(struct snd_dice *dice) { - unsigned int i; - - for (i = 0; i < MAX_STREAMS; i++) { - if (amdtp_stream_running(&dice->tx_stream[i])) { - amdtp_stream_pcm_abort(&dice->tx_stream[i]); - amdtp_stream_stop(&dice->tx_stream[i]); - } - if (amdtp_stream_running(&dice->rx_stream[i])) { - amdtp_stream_pcm_abort(&dice->rx_stream[i]); - amdtp_stream_stop(&dice->rx_stream[i]); - } + int i; + for (i = 0; i < MAX_STREAMS; ++i) { fw_iso_resources_free(&dice->tx_resources[i]); fw_iso_resources_free(&dice->rx_resources[i]); } @@ -163,10 +154,14 @@ static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, for (i = 0; i < params->count; i++) { reg = cpu_to_be32((u32)-1); if (dir == AMDTP_IN_STREAM) { + amdtp_stream_stop(&dice->tx_stream[i]); + snd_dice_transaction_write_tx(dice, params->size * i + TX_ISOCHRONOUS, ®, sizeof(reg)); } else { + amdtp_stream_stop(&dice->rx_stream[i]); + snd_dice_transaction_write_rx(dice, params->size * i + RX_ISOCHRONOUS, ®, sizeof(reg)); @@ -174,35 +169,22 @@ static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, } } -static int keep_resources(struct snd_dice *dice, - enum amdtp_stream_direction dir, unsigned int index, - unsigned int rate, unsigned int pcm_chs, - unsigned int midi_ports) +static int keep_resources(struct snd_dice *dice, struct amdtp_stream *stream, + struct fw_iso_resources *resources, unsigned int rate, + unsigned int pcm_chs, unsigned int midi_ports) { - struct amdtp_stream *stream; - struct fw_iso_resources *resources; bool double_pcm_frames; unsigned int i; int err; - if (dir == AMDTP_IN_STREAM) { - stream = &dice->tx_stream[index]; - resources = &dice->tx_resources[index]; - } else { - stream = &dice->rx_stream[index]; - resources = &dice->rx_resources[index]; - } - - /* - * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in - * one data block of AMDTP packet. Thus sampling transfer frequency is - * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are - * transferred on AMDTP packets at 96 kHz. Two successive samples of a - * channel are stored consecutively in the packet. This quirk is called - * as 'Dual Wire'. - * For this quirk, blocking mode is required and PCM buffer size should - * be aligned to SYT_INTERVAL. - */ + // At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in + // one data block of AMDTP packet. Thus sampling transfer frequency is + // a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are + // transferred on AMDTP packets at 96 kHz. Two successive samples of a + // channel are stored consecutively in the packet. This quirk is called + // as 'Dual Wire'. + // For this quirk, blocking mode is required and PCM buffer size should + // be aligned to SYT_INTERVAL. double_pcm_frames = rate > 96000; if (double_pcm_frames) { rate /= 2; @@ -229,40 +211,40 @@ static int keep_resources(struct snd_dice *dice, fw_parent_device(dice->unit)->max_speed); } -static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, - unsigned int rate, struct reg_params *params) +static int keep_dual_resources(struct snd_dice *dice, unsigned int rate, + enum amdtp_stream_direction dir, + struct reg_params *params) { - __be32 reg[2]; enum snd_dice_rate_mode mode; - unsigned int i, pcm_chs, midi_ports; - struct amdtp_stream *streams; - struct fw_iso_resources *resources; - struct fw_device *fw_dev = fw_parent_device(dice->unit); - int err = 0; - - if (dir == AMDTP_IN_STREAM) { - streams = dice->tx_stream; - resources = dice->tx_resources; - } else { - streams = dice->rx_stream; - resources = dice->rx_resources; - } + int i; + int err; err = snd_dice_stream_get_rate_mode(dice, rate, &mode); if (err < 0) return err; - for (i = 0; i < params->count; i++) { + for (i = 0; i < params->count; ++i) { + __be32 reg[2]; + struct amdtp_stream *stream; + struct fw_iso_resources *resources; unsigned int pcm_cache; unsigned int midi_cache; + unsigned int pcm_chs; + unsigned int midi_ports; if (dir == AMDTP_IN_STREAM) { + stream = &dice->tx_stream[i]; + resources = &dice->tx_resources[i]; + pcm_cache = dice->tx_pcm_chs[i][mode]; midi_cache = dice->tx_midi_ports[i]; err = snd_dice_transaction_read_tx(dice, params->size * i + TX_NUMBER_AUDIO, reg, sizeof(reg)); } else { + stream = &dice->rx_stream[i]; + resources = &dice->rx_resources[i]; + pcm_cache = dice->rx_pcm_chs[i][mode]; midi_cache = dice->rx_midi_ports[i]; err = snd_dice_transaction_read_rx(dice, @@ -274,7 +256,7 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, pcm_chs = be32_to_cpu(reg[0]); midi_ports = be32_to_cpu(reg[1]); - /* These are important for developer of this driver. */ + // These are important for developer of this driver. if (pcm_chs != pcm_cache || midi_ports != midi_cache) { dev_info(&dice->unit->device, "cache mismatch: pcm: %u:%u, midi: %u:%u\n", @@ -282,141 +264,170 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, return -EPROTO; } - err = keep_resources(dice, dir, i, rate, pcm_chs, midi_ports); - if (err < 0) - return err; - - reg[0] = cpu_to_be32(resources[i].channel); - if (dir == AMDTP_IN_STREAM) { - err = snd_dice_transaction_write_tx(dice, - params->size * i + TX_ISOCHRONOUS, - reg, sizeof(reg[0])); - } else { - err = snd_dice_transaction_write_rx(dice, - params->size * i + RX_ISOCHRONOUS, - reg, sizeof(reg[0])); - } + err = keep_resources(dice, stream, resources, rate, pcm_chs, + midi_ports); if (err < 0) return err; + } - if (dir == AMDTP_IN_STREAM) { - reg[0] = cpu_to_be32(fw_dev->max_speed); - err = snd_dice_transaction_write_tx(dice, - params->size * i + TX_SPEED, - reg, sizeof(reg[0])); - if (err < 0) - return err; - } + return 0; +} - err = amdtp_stream_start(&streams[i], resources[i].channel, - fw_dev->max_speed); - if (err < 0) - return err; - } +static void finish_session(struct snd_dice *dice, struct reg_params *tx_params, + struct reg_params *rx_params) +{ + stop_streams(dice, AMDTP_IN_STREAM, tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, rx_params); - return err; + snd_dice_transaction_clear_enable(dice); } -static int start_duplex_streams(struct snd_dice *dice, unsigned int rate) +int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate) { - struct reg_params tx_params, rx_params; - int i; + unsigned int curr_rate; int err; - err = get_register_params(dice, &tx_params, &rx_params); + // Check sampling transmission frequency. + err = snd_dice_transaction_get_rate(dice, &curr_rate); if (err < 0) return err; + if (rate == 0) + rate = curr_rate; - /* Stop transmission. */ - stop_streams(dice, AMDTP_IN_STREAM, &tx_params); - stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); - snd_dice_transaction_clear_enable(dice); - release_resources(dice); + if (dice->substreams_counter == 0 || curr_rate != rate) { + struct reg_params tx_params, rx_params; - err = ensure_phase_lock(dice, rate); - if (err < 0) { - dev_err(&dice->unit->device, "fail to ensure phase lock\n"); - return err; - } + err = get_register_params(dice, &tx_params, &rx_params); + if (err < 0) + return err; - /* Likely to have changed stream formats. */ - err = get_register_params(dice, &tx_params, &rx_params); - if (err < 0) - return err; + finish_session(dice, &tx_params, &rx_params); - /* Start both streams. */ - err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params); - if (err < 0) - goto error; - err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params); - if (err < 0) - goto error; + release_resources(dice); - err = snd_dice_transaction_set_enable(dice); - if (err < 0) { - dev_err(&dice->unit->device, "fail to enable interface\n"); - goto error; - } + // Just after owning the unit (GLOBAL_OWNER), the unit can + // return invalid stream formats. Selecting clock parameters + // have an effect for the unit to refine it. + err = ensure_phase_lock(dice, rate); + if (err < 0) + return err; - for (i = 0; i < MAX_STREAMS; i++) { - if ((i < tx_params.count && - !amdtp_stream_wait_callback(&dice->tx_stream[i], - CALLBACK_TIMEOUT)) || - (i < rx_params.count && - !amdtp_stream_wait_callback(&dice->rx_stream[i], - CALLBACK_TIMEOUT))) { - err = -ETIMEDOUT; + // After changing sampling transfer frequency, the value of + // register can be changed. + err = get_register_params(dice, &tx_params, &rx_params); + if (err < 0) + return err; + + err = keep_dual_resources(dice, rate, AMDTP_IN_STREAM, + &tx_params); + if (err < 0) + goto error; + + err = keep_dual_resources(dice, rate, AMDTP_OUT_STREAM, + &rx_params); + if (err < 0) goto error; - } } return 0; error: - stop_streams(dice, AMDTP_IN_STREAM, &tx_params); - stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); - snd_dice_transaction_clear_enable(dice); release_resources(dice); return err; } +static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, + unsigned int rate, struct reg_params *params) +{ + unsigned int max_speed = fw_parent_device(dice->unit)->max_speed; + int i; + int err; + + for (i = 0; i < params->count; i++) { + struct amdtp_stream *stream; + struct fw_iso_resources *resources; + __be32 reg; + + if (dir == AMDTP_IN_STREAM) { + stream = dice->tx_stream + i; + resources = dice->tx_resources + i; + } else { + stream = dice->rx_stream + i; + resources = dice->rx_resources + i; + } + + reg = cpu_to_be32(resources->channel); + if (dir == AMDTP_IN_STREAM) { + err = snd_dice_transaction_write_tx(dice, + params->size * i + TX_ISOCHRONOUS, + ®, sizeof(reg)); + } else { + err = snd_dice_transaction_write_rx(dice, + params->size * i + RX_ISOCHRONOUS, + ®, sizeof(reg)); + } + if (err < 0) + return err; + + if (dir == AMDTP_IN_STREAM) { + reg = cpu_to_be32(max_speed); + err = snd_dice_transaction_write_tx(dice, + params->size * i + TX_SPEED, + ®, sizeof(reg)); + if (err < 0) + return err; + } + + err = amdtp_stream_start(stream, resources->channel, max_speed); + if (err < 0) + return err; + } + + return 0; +} + /* * MEMO: After this function, there're two states of streams: * - None streams are running. * - All streams are running. */ -int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) +int snd_dice_stream_start_duplex(struct snd_dice *dice) { - unsigned int curr_rate; + unsigned int generation = dice->rx_resources[0].generation; + struct reg_params tx_params, rx_params; unsigned int i; + unsigned int rate; enum snd_dice_rate_mode mode; int err; if (dice->substreams_counter == 0) return -EIO; - /* Check sampling transmission frequency. */ - err = snd_dice_transaction_get_rate(dice, &curr_rate); - if (err < 0) { - dev_err(&dice->unit->device, - "fail to get sampling rate\n"); + err = get_register_params(dice, &tx_params, &rx_params); + if (err < 0) return err; - } - if (rate == 0) - rate = curr_rate; - if (rate != curr_rate) - goto restart; - /* Check error of packet streaming. */ + // Check error of packet streaming. for (i = 0; i < MAX_STREAMS; ++i) { - if (amdtp_streaming_error(&dice->tx_stream[i])) - break; - if (amdtp_streaming_error(&dice->rx_stream[i])) + if (amdtp_streaming_error(&dice->tx_stream[i]) || + amdtp_streaming_error(&dice->rx_stream[i])) { + finish_session(dice, &tx_params, &rx_params); break; + } } - if (i < MAX_STREAMS) - goto restart; - /* Check required streams are running or not. */ + if (generation != fw_parent_device(dice->unit)->card->generation) { + for (i = 0; i < MAX_STREAMS; ++i) { + if (i < tx_params.count) + fw_iso_resources_update(dice->tx_resources + i); + if (i < rx_params.count) + fw_iso_resources_update(dice->rx_resources + i); + } + } + + // Check required streams are running or not. + err = snd_dice_transaction_get_rate(dice, &rate); + if (err < 0) + return err; err = snd_dice_stream_get_rate_mode(dice, rate, &mode); if (err < 0) return err; @@ -428,12 +439,40 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) !amdtp_stream_running(&dice->rx_stream[i])) break; } - if (i < MAX_STREAMS) - goto restart; + if (i < MAX_STREAMS) { + // Start both streams. + err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params); + if (err < 0) + goto error; + + err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params); + if (err < 0) + goto error; + + err = snd_dice_transaction_set_enable(dice); + if (err < 0) { + dev_err(&dice->unit->device, + "fail to enable interface\n"); + goto error; + } + + for (i = 0; i < MAX_STREAMS; i++) { + if ((i < tx_params.count && + !amdtp_stream_wait_callback(&dice->tx_stream[i], + CALLBACK_TIMEOUT)) || + (i < rx_params.count && + !amdtp_stream_wait_callback(&dice->rx_stream[i], + CALLBACK_TIMEOUT))) { + err = -ETIMEDOUT; + goto error; + } + } + } return 0; -restart: - return start_duplex_streams(dice, rate); +error: + finish_session(dice, &tx_params, &rx_params); + return err; } /* @@ -445,17 +484,12 @@ void snd_dice_stream_stop_duplex(struct snd_dice *dice) { struct reg_params tx_params, rx_params; - if (dice->substreams_counter > 0) - return; - - snd_dice_transaction_clear_enable(dice); + if (dice->substreams_counter == 0) { + if (get_register_params(dice, &tx_params, &rx_params) >= 0) + finish_session(dice, &tx_params, &rx_params); - if (get_register_params(dice, &tx_params, &rx_params) == 0) { - stop_streams(dice, AMDTP_IN_STREAM, &tx_params); - stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); + release_resources(dice); } - - release_resources(dice); } static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir, diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index ea829cee9aaf..13eeb3f52bb6 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -19,6 +19,7 @@ MODULE_LICENSE("GPL v2"); #define OUI_MAUDIO 0x000d6c #define OUI_MYTEK 0x001ee8 #define OUI_SSL 0x0050c2 // Actually ID reserved by IEEE. +#define OUI_PRESONUS 0x000a92 #define DICE_CATEGORY_ID 0x04 #define WEISS_CATEGORY_ID 0x00 @@ -371,6 +372,14 @@ static const struct ieee1394_device_id dice_id_table[] = { .vendor_id = OUI_SSL, .model_id = 0x000070, }, + // Presonus FireStudio. + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_PRESONUS, + .model_id = 0x000008, + .driver_data = (kernel_ulong_t)snd_dice_detect_presonus_formats, + }, { .match_flags = IEEE1394_MATCH_VERSION, .version = DICE_INTERFACE, diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index eb4fb8bae2ad..c6304e5e9fc4 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -204,10 +204,11 @@ extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT]; int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, enum snd_dice_rate_mode *mode); -int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate); +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); void snd_dice_stream_update_duplex(struct snd_dice *dice); int snd_dice_stream_detect_current_formats(struct snd_dice *dice); @@ -226,5 +227,6 @@ int snd_dice_detect_tcelectronic_formats(struct snd_dice *dice); int snd_dice_detect_alesis_formats(struct snd_dice *dice); int snd_dice_detect_extension_formats(struct snd_dice *dice); int snd_dice_detect_mytek_formats(struct snd_dice *dice); +int snd_dice_detect_presonus_formats(struct snd_dice *dice); #endif diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c index 10c8803d7f19..45ff73d16074 100644 --- a/sound/firewire/digi00x/amdtp-dot.c +++ b/sound/firewire/digi00x/amdtp-dot.c @@ -127,7 +127,7 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate, if (err < 0) return err; - s->fdf = AMDTP_FDF_AM824 | s->sfc; + s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc; p->pcm_channels = pcm_channels; diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c index bf50a168087f..2b57ece89101 100644 --- a/sound/firewire/digi00x/digi00x-midi.c +++ b/sound/firewire/digi00x/digi00x-midi.c @@ -17,8 +17,13 @@ static int midi_open(struct snd_rawmidi_substream *substream) return err; mutex_lock(&dg00x->mutex); - dg00x->substreams_counter++; - err = snd_dg00x_stream_start_duplex(dg00x, 0); + err = snd_dg00x_stream_reserve_duplex(dg00x, 0); + if (err >= 0) { + ++dg00x->substreams_counter; + err = snd_dg00x_stream_start_duplex(dg00x); + if (err < 0) + --dg00x->substreams_counter; + } mutex_unlock(&dg00x->mutex); if (err < 0) snd_dg00x_stream_lock_release(dg00x); @@ -31,7 +36,7 @@ static int midi_close(struct snd_rawmidi_substream *substream) struct snd_dg00x *dg00x = substream->rmidi->private_data; mutex_lock(&dg00x->mutex); - dg00x->substreams_counter--; + --dg00x->substreams_counter; snd_dg00x_stream_stop_duplex(dg00x); mutex_unlock(&dg00x->mutex); diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c index 4f637f227513..18e561b26625 100644 --- a/sound/firewire/digi00x/digi00x-pcm.c +++ b/sound/firewire/digi00x/digi00x-pcm.c @@ -154,8 +154,8 @@ static int pcm_close(struct snd_pcm_substream *substream) return 0; } -static int pcm_capture_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) +static int pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) { struct snd_dg00x *dg00x = substream->private_data; int err; @@ -166,58 +166,26 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { - mutex_lock(&dg00x->mutex); - dg00x->substreams_counter++; - mutex_unlock(&dg00x->mutex); - } + unsigned int rate = params_rate(hw_params); - return 0; -} - -static int pcm_playback_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_dg00x *dg00x = substream->private_data; - int err; - - err = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - return err; - - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&dg00x->mutex); - dg00x->substreams_counter++; + err = snd_dg00x_stream_reserve_duplex(dg00x, rate); + if (err >= 0) + ++dg00x->substreams_counter; mutex_unlock(&dg00x->mutex); } - return 0; -} - -static int pcm_capture_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_dg00x *dg00x = substream->private_data; - - mutex_lock(&dg00x->mutex); - - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - dg00x->substreams_counter--; - - snd_dg00x_stream_stop_duplex(dg00x); - - mutex_unlock(&dg00x->mutex); - - return snd_pcm_lib_free_vmalloc_buffer(substream); + return err; } -static int pcm_playback_hw_free(struct snd_pcm_substream *substream) +static int pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; mutex_lock(&dg00x->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - dg00x->substreams_counter--; + --dg00x->substreams_counter; snd_dg00x_stream_stop_duplex(dg00x); @@ -229,12 +197,11 @@ static int pcm_playback_hw_free(struct snd_pcm_substream *substream) static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&dg00x->mutex); - err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate); + err = snd_dg00x_stream_start_duplex(dg00x); if (err >= 0) amdtp_stream_pcm_prepare(&dg00x->tx_stream); @@ -246,12 +213,11 @@ static int pcm_capture_prepare(struct snd_pcm_substream *substream) static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&dg00x->mutex); - err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate); + err = snd_dg00x_stream_start_duplex(dg00x); if (err >= 0) { amdtp_stream_pcm_prepare(&dg00x->rx_stream); amdtp_dot_reset(&dg00x->rx_stream); @@ -332,8 +298,8 @@ int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x) .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_capture_hw_params, - .hw_free = pcm_capture_hw_free, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, @@ -344,8 +310,8 @@ int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x) .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_playback_hw_params, - .hw_free = pcm_playback_hw_free, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c index ac8052c66b6f..3e77dbd3ee22 100644 --- a/sound/firewire/digi00x/digi00x-stream.c +++ b/sound/firewire/digi00x/digi00x-stream.c @@ -124,11 +124,25 @@ int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x, static void finish_session(struct snd_dg00x *dg00x) { - __be32 data = cpu_to_be32(0x00000003); + __be32 data; + + amdtp_stream_stop(&dg00x->tx_stream); + amdtp_stream_stop(&dg00x->rx_stream); + data = cpu_to_be32(0x00000003); snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET, &data, sizeof(data), 0); + + // Unregister isochronous channels for both direction. + data = 0; + snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, + DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS, + &data, sizeof(data), 0); + + // Just after finishing the session, the device may lost transmitting + // functionality for a short time. + msleep(50); } static int begin_session(struct snd_dg00x *dg00x) @@ -137,11 +151,20 @@ static int begin_session(struct snd_dg00x *dg00x) u32 curr; int err; + // Register isochronous channels for both direction. + data = cpu_to_be32((dg00x->tx_resources.channel << 16) | + dg00x->rx_resources.channel); + err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, + DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS, + &data, sizeof(data), 0); + if (err < 0) + return err; + err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST, DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE, &data, sizeof(data), 0); if (err < 0) - goto error; + return err; curr = be32_to_cpu(data); if (curr == 0) @@ -156,39 +179,23 @@ static int begin_session(struct snd_dg00x *dg00x) DG00X_OFFSET_STREAMING_SET, &data, sizeof(data), 0); if (err < 0) - goto error; + break; msleep(20); curr--; } - return 0; -error: - finish_session(dg00x); return err; } -static void release_resources(struct snd_dg00x *dg00x) +static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream, + unsigned int rate) { - __be32 data = 0; - - /* Unregister isochronous channels for both direction. */ - snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, - DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS, - &data, sizeof(data), 0); - - /* Release isochronous resources. */ - fw_iso_resources_free(&dg00x->tx_resources); - fw_iso_resources_free(&dg00x->rx_resources); -} - -static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate) -{ - unsigned int i; - __be32 data; + struct fw_iso_resources *resources; + int i; int err; - /* Check sampling rate. */ + // Check sampling rate. for (i = 0; i < SND_DG00X_RATE_COUNT; i++) { if (snd_dg00x_stream_rates[i] == rate) break; @@ -196,41 +203,19 @@ static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate) if (i == SND_DG00X_RATE_COUNT) return -EINVAL; - /* Keep resources for out-stream. */ - err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate, - snd_dg00x_stream_pcm_channels[i]); - if (err < 0) - return err; - err = fw_iso_resources_allocate(&dg00x->rx_resources, - amdtp_stream_get_max_payload(&dg00x->rx_stream), - fw_parent_device(dg00x->unit)->max_speed); - if (err < 0) - return err; + if (stream == &dg00x->tx_stream) + resources = &dg00x->tx_resources; + else + resources = &dg00x->rx_resources; - /* Keep resources for in-stream. */ - err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate, + err = amdtp_dot_set_parameters(stream, rate, snd_dg00x_stream_pcm_channels[i]); if (err < 0) return err; - err = fw_iso_resources_allocate(&dg00x->tx_resources, - amdtp_stream_get_max_payload(&dg00x->tx_stream), - fw_parent_device(dg00x->unit)->max_speed); - if (err < 0) - goto error; - /* Register isochronous channels for both direction. */ - data = cpu_to_be32((dg00x->tx_resources.channel << 16) | - dg00x->rx_resources.channel); - err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, - DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS, - &data, sizeof(data), 0); - if (err < 0) - goto error; - - return 0; -error: - release_resources(dg00x); - return err; + return fw_iso_resources_allocate(resources, + amdtp_stream_get_max_payload(stream), + fw_parent_device(dg00x->unit)->max_speed); } int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x) @@ -272,43 +257,68 @@ void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x) fw_iso_resources_destroy(&dg00x->tx_resources); } -int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate) +int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate) { unsigned int curr_rate; - int err = 0; - - if (dg00x->substreams_counter == 0) - goto end; + int err; - /* Check current sampling rate. */ err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate); if (err < 0) - goto error; + return err; if (rate == 0) rate = curr_rate; - if (curr_rate != rate || - amdtp_streaming_error(&dg00x->tx_stream) || - amdtp_streaming_error(&dg00x->rx_stream)) { + + if (dg00x->substreams_counter == 0 || curr_rate != rate) { finish_session(dg00x); - amdtp_stream_stop(&dg00x->tx_stream); - amdtp_stream_stop(&dg00x->rx_stream); - release_resources(dg00x); - } + fw_iso_resources_free(&dg00x->tx_resources); + fw_iso_resources_free(&dg00x->rx_resources); - /* - * No packets are transmitted without receiving packets, reagardless of - * which source of clock is used. - */ - if (!amdtp_stream_running(&dg00x->rx_stream)) { err = snd_dg00x_stream_set_local_rate(dg00x, rate); if (err < 0) + return err; + + err = keep_resources(dg00x, &dg00x->rx_stream, rate); + if (err < 0) + return err; + + err = keep_resources(dg00x, &dg00x->tx_stream, rate); + if (err < 0) { + fw_iso_resources_free(&dg00x->rx_resources); + return err; + } + } + + return 0; +} + +int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x) +{ + unsigned int generation = dg00x->rx_resources.generation; + int err = 0; + + if (dg00x->substreams_counter == 0) + return 0; + + if (amdtp_streaming_error(&dg00x->tx_stream) || + amdtp_streaming_error(&dg00x->rx_stream)) + finish_session(dg00x); + + if (generation != fw_parent_device(dg00x->unit)->card->generation) { + err = fw_iso_resources_update(&dg00x->tx_resources); + if (err < 0) goto error; - err = keep_resources(dg00x, rate); + err = fw_iso_resources_update(&dg00x->rx_resources); if (err < 0) goto error; + } + /* + * No packets are transmitted without receiving packets, reagardless of + * which source of clock is used. + */ + if (!amdtp_stream_running(&dg00x->rx_stream)) { err = begin_session(dg00x); if (err < 0) goto error; @@ -343,33 +353,22 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate) goto error; } } -end: - return err; + + return 0; error: finish_session(dg00x); - amdtp_stream_stop(&dg00x->tx_stream); - amdtp_stream_stop(&dg00x->rx_stream); - release_resources(dg00x); - return err; } void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x) { - if (dg00x->substreams_counter > 0) - return; - - amdtp_stream_stop(&dg00x->tx_stream); - amdtp_stream_stop(&dg00x->rx_stream); - finish_session(dg00x); - release_resources(dg00x); + if (dg00x->substreams_counter == 0) { + finish_session(dg00x); - /* - * Just after finishing the session, the device may lost transmitting - * functionality for a short time. - */ - msleep(50); + fw_iso_resources_free(&dg00x->tx_resources); + fw_iso_resources_free(&dg00x->rx_resources); + } } void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x) diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index 464e6d3d82a8..0994d191ccda 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -139,7 +139,8 @@ 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_start_duplex(struct snd_dg00x *dg00x, unsigned int rate); +int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate); +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); void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x); diff --git a/sound/firewire/fireface/ff-pcm.c b/sound/firewire/fireface/ff-pcm.c index 0d40bb68db50..9eab3ad283ce 100644 --- a/sound/firewire/fireface/ff-pcm.c +++ b/sound/firewire/fireface/ff-pcm.c @@ -198,8 +198,8 @@ static int pcm_close(struct snd_pcm_substream *substream) return 0; } -static int pcm_capture_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) +static int pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) { struct snd_ff *ff = substream->private_data; int err; @@ -210,58 +210,26 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { - mutex_lock(&ff->mutex); - ff->substreams_counter++; - mutex_unlock(&ff->mutex); - } - - return 0; -} - -static int pcm_playback_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_ff *ff = substream->private_data; - int err; - - err = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - return err; + unsigned int rate = params_rate(hw_params); - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&ff->mutex); - ff->substreams_counter++; + err = snd_ff_stream_reserve_duplex(ff, rate); + if (err >= 0) + ++ff->substreams_counter; mutex_unlock(&ff->mutex); } return 0; } -static int pcm_capture_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_ff *ff = substream->private_data; - - mutex_lock(&ff->mutex); - - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - ff->substreams_counter--; - - snd_ff_stream_stop_duplex(ff); - - mutex_unlock(&ff->mutex); - - return snd_pcm_lib_free_vmalloc_buffer(substream); -} - -static int pcm_playback_hw_free(struct snd_pcm_substream *substream) +static int pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; mutex_lock(&ff->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - ff->substreams_counter--; + --ff->substreams_counter; snd_ff_stream_stop_duplex(ff); @@ -374,8 +342,8 @@ int snd_ff_create_pcm_devices(struct snd_ff *ff) .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_capture_hw_params, - .hw_free = pcm_capture_hw_free, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, @@ -386,8 +354,8 @@ int snd_ff_create_pcm_devices(struct snd_ff *ff) .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_playback_hw_params, - .hw_free = pcm_playback_hw_free, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c index 8d1c2c6e907b..bf44cad7985e 100644 --- a/sound/firewire/fireface/ff-protocol-former.c +++ b/sound/firewire/fireface/ff-protocol-former.c @@ -293,27 +293,6 @@ static int former_fill_midi_msg(struct snd_ff *ff, #define FF800_TX_PACKET_ISOC_CH 0x0000801c0008 -static int allocate_rx_resources(struct snd_ff *ff) -{ - u32 data; - __le32 reg; - int err; - - // Controllers should allocate isochronous resources for rx stream. - err = fw_iso_resources_allocate(&ff->rx_resources, - amdtp_stream_get_max_payload(&ff->rx_stream), - fw_parent_device(ff->unit)->max_speed); - if (err < 0) - return err; - - // Set isochronous channel and the number of quadlets of rx packets. - data = ff->rx_stream.data_block_quadlets << 3; - data = (data << 8) | ff->rx_resources.channel; - reg = cpu_to_le32(data); - return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - FF800_RX_PACKET_FORMAT, ®, sizeof(reg), 0); -} - static int allocate_tx_resources(struct snd_ff *ff) { __le32 reg; @@ -355,8 +334,9 @@ static int allocate_tx_resources(struct snd_ff *ff) return 0; } -static int ff800_begin_session(struct snd_ff *ff, unsigned int rate) +static int ff800_allocate_resources(struct snd_ff *ff, unsigned int rate) { + u32 data; __le32 reg; int err; @@ -371,14 +351,38 @@ static int ff800_begin_session(struct snd_ff *ff, unsigned int rate) // Let's sleep for a bit. msleep(100); - err = allocate_rx_resources(ff); + // Controllers should allocate isochronous resources for rx stream. + err = fw_iso_resources_allocate(&ff->rx_resources, + amdtp_stream_get_max_payload(&ff->rx_stream), + fw_parent_device(ff->unit)->max_speed); if (err < 0) return err; - err = allocate_tx_resources(ff); + // Set isochronous channel and the number of quadlets of rx packets. + // This should be done before the allocation of tx resources to avoid + // periodical noise. + data = ff->rx_stream.data_block_quadlets << 3; + data = (data << 8) | ff->rx_resources.channel; + reg = cpu_to_le32(data); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF800_RX_PACKET_FORMAT, ®, sizeof(reg), 0); if (err < 0) return err; + return allocate_tx_resources(ff); +} + +static int ff800_begin_session(struct snd_ff *ff, unsigned int rate) +{ + unsigned int generation = ff->rx_resources.generation; + __le32 reg; + + if (generation != fw_parent_device(ff->unit)->card->generation) { + int err = fw_iso_resources_update(&ff->rx_resources); + if (err < 0) + return err; + } + reg = cpu_to_le32(0x80000000); reg |= cpu_to_le32(ff->tx_stream.data_block_quadlets); if (fw_parent_device(ff->unit)->max_speed == SCODE_800) @@ -420,6 +424,7 @@ const struct snd_ff_protocol snd_ff_protocol_ff800 = { .fill_midi_msg = former_fill_midi_msg, .get_clock = former_get_clock, .switch_fetching_mode = former_switch_fetching_mode, + .allocate_resources = ff800_allocate_resources, .begin_session = ff800_begin_session, .finish_session = ff800_finish_session, .dump_status = former_dump_status, @@ -431,12 +436,11 @@ const struct snd_ff_protocol snd_ff_protocol_ff800 = { #define FF400_TX_PACKET_FORMAT 0x00008010050cull #define FF400_ISOC_COMM_STOP 0x000080100510ull -/* - * Fireface 400 manages isochronous channel number in 3 bit field. Therefore, - * we can allocate between 0 and 7 channel. - */ -static int keep_resources(struct snd_ff *ff, unsigned int rate) +// Fireface 400 manages isochronous channel number in 3 bit field. Therefore, +// we can allocate between 0 and 7 channel. +static int ff400_allocate_resources(struct snd_ff *ff, unsigned int rate) { + __le32 reg; enum snd_ff_stream_mode mode; int i; int err; @@ -449,11 +453,20 @@ static int keep_resources(struct snd_ff *ff, unsigned int rate) if (i >= CIP_SFC_COUNT) return -EINVAL; + // Set the number of data blocks transferred in a second. + reg = cpu_to_le32(rate); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + FF400_STF, ®, sizeof(reg), 0); + if (err < 0) + return err; + + msleep(100); + err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) return err; - /* Keep resources for in-stream. */ + // Keep resources for in-stream. ff->tx_resources.channels_mask = 0x00000000000000ffuLL; err = fw_iso_resources_allocate(&ff->tx_resources, amdtp_stream_get_max_payload(&ff->tx_stream), @@ -461,7 +474,7 @@ static int keep_resources(struct snd_ff *ff, unsigned int rate) if (err < 0) return err; - /* Keep resources for out-stream. */ + // Keep resources for out-stream. ff->rx_resources.channels_mask = 0x00000000000000ffuLL; err = fw_iso_resources_allocate(&ff->rx_resources, amdtp_stream_get_max_payload(&ff->rx_stream), @@ -474,26 +487,22 @@ static int keep_resources(struct snd_ff *ff, unsigned int rate) static int ff400_begin_session(struct snd_ff *ff, unsigned int rate) { + unsigned int generation = ff->rx_resources.generation; __le32 reg; int err; - err = keep_resources(ff, rate); - if (err < 0) - return err; - - /* Set the number of data blocks transferred in a second. */ - reg = cpu_to_le32(rate); - err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - FF400_STF, ®, sizeof(reg), 0); - if (err < 0) - return err; + if (generation != fw_parent_device(ff->unit)->card->generation) { + err = fw_iso_resources_update(&ff->tx_resources); + if (err < 0) + return err; - msleep(100); + err = fw_iso_resources_update(&ff->rx_resources); + if (err < 0) + return err; + } - /* - * Set isochronous channel and the number of quadlets of received - * packets. - */ + // Set isochronous channel and the number of quadlets of received + // packets. reg = cpu_to_le32(((ff->rx_stream.data_block_quadlets << 3) << 8) | ff->rx_resources.channel); err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, @@ -501,11 +510,9 @@ static int ff400_begin_session(struct snd_ff *ff, unsigned int rate) if (err < 0) return err; - /* - * Set isochronous channel and the number of quadlets of transmitted - * packet. - */ - /* TODO: investigate the purpose of this 0x80. */ + // Set isochronous channel and the number of quadlets of transmitted + // packet. + // TODO: investigate the purpose of this 0x80. reg = cpu_to_le32((0x80 << 24) | (ff->tx_resources.channel << 5) | (ff->tx_stream.data_block_quadlets)); @@ -514,7 +521,7 @@ static int ff400_begin_session(struct snd_ff *ff, unsigned int rate) if (err < 0) return err; - /* Allow to transmit packets. */ + // Allow to transmit packets. reg = cpu_to_le32(0x00000001); return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, FF400_ISOC_COMM_START, ®, sizeof(reg), 0); @@ -591,6 +598,7 @@ const struct snd_ff_protocol snd_ff_protocol_ff400 = { .fill_midi_msg = former_fill_midi_msg, .get_clock = former_get_clock, .switch_fetching_mode = former_switch_fetching_mode, + .allocate_resources = ff400_allocate_resources, .begin_session = ff400_begin_session, .finish_session = ff400_finish_session, .dump_status = former_dump_status, diff --git a/sound/firewire/fireface/ff-protocol-latter.c b/sound/firewire/fireface/ff-protocol-latter.c index b30d02d359b1..0e4c3a9ed5e4 100644 --- a/sound/firewire/fireface/ff-protocol-latter.c +++ b/sound/firewire/fireface/ff-protocol-latter.c @@ -97,25 +97,64 @@ static int latter_switch_fetching_mode(struct snd_ff *ff, bool enable) LATTER_FETCH_MODE, ®, sizeof(reg), 0); } -static int keep_resources(struct snd_ff *ff, unsigned int rate) +static int latter_allocate_resources(struct snd_ff *ff, unsigned int rate) { enum snd_ff_stream_mode mode; + unsigned int code; + __le32 reg; + unsigned int count; int i; int err; - // Check whether the given value is supported or not. - for (i = 0; i < CIP_SFC_COUNT; i++) { - if (amdtp_rate_table[i] == rate) + // Set the number of data blocks transferred in a second. + if (rate % 32000 == 0) + code = 0x00; + else if (rate % 44100 == 0) + code = 0x02; + else if (rate % 48000 == 0) + code = 0x04; + else + return -EINVAL; + + if (rate >= 64000 && rate < 128000) + code |= 0x08; + else if (rate >= 128000 && rate < 192000) + code |= 0x10; + + reg = cpu_to_le32(code); + err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, + LATTER_STF, ®, sizeof(reg), 0); + if (err < 0) + return err; + + // Confirm to shift transmission clock. + count = 0; + while (count++ < 10) { + unsigned int curr_rate; + enum snd_ff_clock_src src; + + err = latter_get_clock(ff, &curr_rate, &src); + if (err < 0) + return err; + + if (curr_rate == rate) break; } - if (i >= CIP_SFC_COUNT) + if (count == 10) + return -ETIMEDOUT; + + for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); ++i) { + if (rate == amdtp_rate_table[i]) + break; + } + if (i == ARRAY_SIZE(amdtp_rate_table)) return -EINVAL; err = snd_ff_stream_get_multiplier_mode(i, &mode); if (err < 0) return err; - /* Keep resources for in-stream. */ + // Keep resources for in-stream. ff->tx_resources.channels_mask = 0x00000000000000ffuLL; err = fw_iso_resources_allocate(&ff->tx_resources, amdtp_stream_get_max_payload(&ff->tx_stream), @@ -123,7 +162,7 @@ static int keep_resources(struct snd_ff *ff, unsigned int rate) if (err < 0) return err; - /* Keep resources for out-stream. */ + // Keep resources for out-stream. ff->rx_resources.channels_mask = 0x00000000000000ffuLL; err = fw_iso_resources_allocate(&ff->rx_resources, amdtp_stream_get_max_payload(&ff->rx_stream), @@ -136,60 +175,30 @@ static int keep_resources(struct snd_ff *ff, unsigned int rate) static int latter_begin_session(struct snd_ff *ff, unsigned int rate) { - static const struct { - unsigned int stf; - unsigned int code; - unsigned int flag; - } *entry, rate_table[] = { - { 32000, 0x00, 0x92, }, - { 44100, 0x02, 0x92, }, - { 48000, 0x04, 0x92, }, - { 64000, 0x08, 0x8e, }, - { 88200, 0x0a, 0x8e, }, - { 96000, 0x0c, 0x8e, }, - { 128000, 0x10, 0x8c, }, - { 176400, 0x12, 0x8c, }, - { 192000, 0x14, 0x8c, }, - }; + unsigned int generation = ff->rx_resources.generation; + unsigned int flag; u32 data; __le32 reg; - unsigned int count; - int i; int err; - for (i = 0; i < ARRAY_SIZE(rate_table); ++i) { - entry = rate_table + i; - if (entry->stf == rate) - break; - } - if (i == ARRAY_SIZE(rate_table)) + if (rate >= 32000 && rate <= 48000) + flag = 0x92; + else if (rate >= 64000 && rate <= 96000) + flag = 0x8e; + else if (rate >= 128000 && rate <= 192000) + flag = 0x8c; + else return -EINVAL; - reg = cpu_to_le32(entry->code); - err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, - LATTER_STF, ®, sizeof(reg), 0); - if (err < 0) - return err; - - // Confirm to shift transmission clock. - count = 0; - while (count++ < 10) { - unsigned int curr_rate; - enum snd_ff_clock_src src; - - err = latter_get_clock(ff, &curr_rate, &src); + if (generation != fw_parent_device(ff->unit)->card->generation) { + err = fw_iso_resources_update(&ff->tx_resources); if (err < 0) return err; - if (curr_rate == rate) - break; + err = fw_iso_resources_update(&ff->rx_resources); + if (err < 0) + return err; } - if (count == 10) - return -ETIMEDOUT; - - err = keep_resources(ff, rate); - if (err < 0) - return err; data = (ff->tx_resources.channel << 8) | ff->rx_resources.channel; reg = cpu_to_le32(data); @@ -200,7 +209,7 @@ static int latter_begin_session(struct snd_ff *ff, unsigned int rate) // Always use the maximum number of data channels in data block of // packet. - reg = cpu_to_le32(entry->flag); + reg = cpu_to_le32(flag); return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, LATTER_ISOC_START, ®, sizeof(reg), 0); } @@ -424,6 +433,7 @@ const struct snd_ff_protocol snd_ff_protocol_latter = { .fill_midi_msg = latter_fill_midi_msg, .get_clock = latter_get_clock, .switch_fetching_mode = latter_switch_fetching_mode, + .allocate_resources = latter_allocate_resources, .begin_session = latter_begin_session, .finish_session = latter_finish_session, .dump_status = latter_dump_status, diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c index 6dfd2efb6646..4208b8004d1a 100644 --- a/sound/firewire/fireface/ff-stream.c +++ b/sound/firewire/fireface/ff-stream.c @@ -30,14 +30,11 @@ int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc, return 0; } -static void release_resources(struct snd_ff *ff) -{ - fw_iso_resources_free(&ff->tx_resources); - fw_iso_resources_free(&ff->rx_resources); -} - static inline void finish_session(struct snd_ff *ff) { + amdtp_stream_stop(&ff->tx_stream); + amdtp_stream_stop(&ff->rx_stream); + ff->spec->protocol->finish_session(ff); ff->spec->protocol->switch_fetching_mode(ff, false); } @@ -103,37 +100,25 @@ void snd_ff_stream_destroy_duplex(struct snd_ff *ff) destroy_stream(ff, AMDTP_OUT_STREAM); } -int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) +int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate) { unsigned int curr_rate; enum snd_ff_clock_src src; int err; - if (ff->substreams_counter == 0) - return 0; - err = ff->spec->protocol->get_clock(ff, &curr_rate, &src); if (err < 0) return err; - if (curr_rate != rate || - amdtp_streaming_error(&ff->tx_stream) || - amdtp_streaming_error(&ff->rx_stream)) { - finish_session(ff); - - amdtp_stream_stop(&ff->tx_stream); - amdtp_stream_stop(&ff->rx_stream); - - release_resources(ff); - } - /* - * Regardless of current source of clock signal, drivers transfer some - * packets. Then, the device transfers packets. - */ - if (!amdtp_stream_running(&ff->rx_stream)) { + if (ff->substreams_counter == 0 || curr_rate != rate) { enum snd_ff_stream_mode mode; int i; + finish_session(ff); + + fw_iso_resources_free(&ff->tx_resources); + fw_iso_resources_free(&ff->rx_resources); + for (i = 0; i < CIP_SFC_COUNT; ++i) { if (amdtp_rate_table[i] == rate) break; @@ -155,6 +140,30 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) if (err < 0) return err; + err = ff->spec->protocol->allocate_resources(ff, rate); + if (err < 0) + return err; + } + + return 0; +} + +int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) +{ + int err; + + if (ff->substreams_counter == 0) + return 0; + + if (amdtp_streaming_error(&ff->tx_stream) || + amdtp_streaming_error(&ff->rx_stream)) + finish_session(ff); + + /* + * Regardless of current source of clock signal, drivers transfer some + * packets. Then, the device transfers packets. + */ + if (!amdtp_stream_running(&ff->rx_stream)) { err = ff->spec->protocol->begin_session(ff, rate); if (err < 0) goto error; @@ -192,37 +201,29 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) return 0; error: - amdtp_stream_stop(&ff->tx_stream); - amdtp_stream_stop(&ff->rx_stream); - finish_session(ff); - release_resources(ff); return err; } void snd_ff_stream_stop_duplex(struct snd_ff *ff) { - if (ff->substreams_counter > 0) - return; + if (ff->substreams_counter == 0) { + finish_session(ff); - amdtp_stream_stop(&ff->tx_stream); - amdtp_stream_stop(&ff->rx_stream); - finish_session(ff); - release_resources(ff); + fw_iso_resources_free(&ff->tx_resources); + fw_iso_resources_free(&ff->rx_resources); + } } void snd_ff_stream_update_duplex(struct snd_ff *ff) { - /* The device discontinue to transfer packets. */ + // The device discontinue to transfer packets. amdtp_stream_pcm_abort(&ff->tx_stream); amdtp_stream_stop(&ff->tx_stream); amdtp_stream_pcm_abort(&ff->rx_stream); amdtp_stream_stop(&ff->rx_stream); - - fw_iso_resources_update(&ff->tx_resources); - fw_iso_resources_update(&ff->rx_resources); } void snd_ff_stream_lock_changed(struct snd_ff *ff) diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index 7fac241c2486..36dd0c75b9f7 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -112,6 +112,7 @@ struct snd_ff_protocol { int (*get_clock)(struct snd_ff *ff, unsigned int *rate, enum snd_ff_clock_src *src); int (*switch_fetching_mode)(struct snd_ff *ff, bool enable); + int (*allocate_resources)(struct snd_ff *ff, unsigned int rate); int (*begin_session)(struct snd_ff *ff, unsigned int rate); void (*finish_session)(struct snd_ff *ff); void (*dump_status)(struct snd_ff *ff, struct snd_info_buffer *buffer); @@ -136,6 +137,7 @@ 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_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 28df49c3542a..31efd4b53b4f 100644 --- a/sound/firewire/fireworks/fireworks.h +++ b/sound/firewire/fireworks/fireworks.h @@ -88,8 +88,7 @@ struct snd_efw { struct amdtp_stream rx_stream; struct cmp_connection out_conn; struct cmp_connection in_conn; - unsigned int capture_substreams; - unsigned int playback_substreams; + unsigned int substreams_counter; /* hardware metering parameters */ unsigned int phys_out; @@ -206,7 +205,8 @@ 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_start_duplex(struct snd_efw *efw, unsigned int rate); +int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate); +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); void snd_efw_stream_destroy_duplex(struct snd_efw *efw); diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c index 14b985c4f304..a9f4a9630d15 100644 --- a/sound/firewire/fireworks/fireworks_midi.c +++ b/sound/firewire/fireworks/fireworks_midi.c @@ -7,7 +7,7 @@ */ #include "fireworks.h" -static int midi_capture_open(struct snd_rawmidi_substream *substream) +static int midi_open(struct snd_rawmidi_substream *substream) { struct snd_efw *efw = substream->rmidi->private_data; int err; @@ -17,28 +17,13 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream) goto end; mutex_lock(&efw->mutex); - efw->capture_substreams++; - err = snd_efw_stream_start_duplex(efw, 0); - mutex_unlock(&efw->mutex); - if (err < 0) - snd_efw_stream_lock_release(efw); - -end: - return err; -} - -static int midi_playback_open(struct snd_rawmidi_substream *substream) -{ - struct snd_efw *efw = substream->rmidi->private_data; - int err; - - err = snd_efw_stream_lock_try(efw); - if (err < 0) - goto end; - - mutex_lock(&efw->mutex); - efw->playback_substreams++; - err = snd_efw_stream_start_duplex(efw, 0); + err = snd_efw_stream_reserve_duplex(efw, 0); + if (err >= 0) { + ++efw->substreams_counter; + err = snd_efw_stream_start_duplex(efw); + if (err < 0) + --efw->substreams_counter; + } mutex_unlock(&efw->mutex); if (err < 0) snd_efw_stream_lock_release(efw); @@ -46,25 +31,12 @@ end: return err; } -static int midi_capture_close(struct snd_rawmidi_substream *substream) -{ - struct snd_efw *efw = substream->rmidi->private_data; - - mutex_lock(&efw->mutex); - efw->capture_substreams--; - snd_efw_stream_stop_duplex(efw); - mutex_unlock(&efw->mutex); - - snd_efw_stream_lock_release(efw); - return 0; -} - -static int midi_playback_close(struct snd_rawmidi_substream *substream) +static int midi_close(struct snd_rawmidi_substream *substream) { struct snd_efw *efw = substream->rmidi->private_data; mutex_lock(&efw->mutex); - efw->playback_substreams--; + --efw->substreams_counter; snd_efw_stream_stop_duplex(efw); mutex_unlock(&efw->mutex); @@ -120,13 +92,13 @@ static void set_midi_substream_names(struct snd_efw *efw, int snd_efw_create_midi_devices(struct snd_efw *efw) { static const struct snd_rawmidi_ops capture_ops = { - .open = midi_capture_open, - .close = midi_capture_close, + .open = midi_open, + .close = midi_close, .trigger = midi_capture_trigger, }; static const struct snd_rawmidi_ops playback_ops = { - .open = midi_playback_open, - .close = midi_playback_close, + .open = midi_open, + .close = midi_close, .trigger = midi_playback_trigger, }; struct snd_rawmidi *rmidi; diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c index affc50fe2e8e..a7025dccc754 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -218,7 +218,7 @@ static int pcm_close(struct snd_pcm_substream *substream) return 0; } -static int pcm_capture_hw_params(struct snd_pcm_substream *substream, +static int pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_efw *efw = substream->private_data; @@ -230,69 +230,40 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { - mutex_lock(&efw->mutex); - efw->capture_substreams++; - mutex_unlock(&efw->mutex); - } - - return 0; -} -static int pcm_playback_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_efw *efw = substream->private_data; - int err; + unsigned int rate = params_rate(hw_params); - err = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - return err; - - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&efw->mutex); - efw->playback_substreams++; + err = snd_efw_stream_reserve_duplex(efw, rate); + if (err >= 0) + ++efw->substreams_counter; mutex_unlock(&efw->mutex); } - return 0; + return err; } -static int pcm_capture_hw_free(struct snd_pcm_substream *substream) +static int pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_efw *efw = substream->private_data; - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { - mutex_lock(&efw->mutex); - efw->capture_substreams--; - mutex_unlock(&efw->mutex); - } - - snd_efw_stream_stop_duplex(efw); - - return snd_pcm_lib_free_vmalloc_buffer(substream); -} -static int pcm_playback_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_efw *efw = substream->private_data; + mutex_lock(&efw->mutex); - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) { - mutex_lock(&efw->mutex); - efw->playback_substreams--; - mutex_unlock(&efw->mutex); - } + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) + --efw->substreams_counter; snd_efw_stream_stop_duplex(efw); + mutex_unlock(&efw->mutex); + return snd_pcm_lib_free_vmalloc_buffer(substream); } static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_efw *efw = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; int err; - err = snd_efw_stream_start_duplex(efw, runtime->rate); + err = snd_efw_stream_start_duplex(efw); if (err >= 0) amdtp_stream_pcm_prepare(&efw->tx_stream); @@ -301,10 +272,9 @@ static int pcm_capture_prepare(struct snd_pcm_substream *substream) static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_efw *efw = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; int err; - err = snd_efw_stream_start_duplex(efw, runtime->rate); + err = snd_efw_stream_start_duplex(efw); if (err >= 0) amdtp_stream_pcm_prepare(&efw->rx_stream); @@ -377,8 +347,8 @@ int snd_efw_create_pcm_devices(struct snd_efw *efw) .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_capture_hw_params, - .hw_free = pcm_capture_hw_free, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, @@ -389,8 +359,8 @@ int snd_efw_create_pcm_devices(struct snd_efw *efw) .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_playback_hw_params, - .hw_free = pcm_playback_hw_free, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index 2d3095412427..e659a0b89ba5 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -42,7 +42,6 @@ end: static void stop_stream(struct snd_efw *efw, struct amdtp_stream *stream) { - amdtp_stream_pcm_abort(stream); amdtp_stream_stop(stream); if (stream == &efw->tx_stream) @@ -51,54 +50,37 @@ stop_stream(struct snd_efw *efw, struct amdtp_stream *stream) cmp_connection_break(&efw->in_conn); } -static int -start_stream(struct snd_efw *efw, struct amdtp_stream *stream, - unsigned int sampling_rate) +static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream, + unsigned int rate) { struct cmp_connection *conn; - unsigned int mode, pcm_channels, midi_ports; int err; - err = snd_efw_get_multiplier_mode(sampling_rate, &mode); - if (err < 0) - goto end; - if (stream == &efw->tx_stream) { + if (stream == &efw->tx_stream) conn = &efw->out_conn; - pcm_channels = efw->pcm_capture_channels[mode]; - midi_ports = efw->midi_out_ports; - } else { + else conn = &efw->in_conn; - pcm_channels = efw->pcm_playback_channels[mode]; - midi_ports = efw->midi_in_ports; - } - - err = amdtp_am824_set_parameters(stream, sampling_rate, - pcm_channels, midi_ports, false); - if (err < 0) - goto end; - /* establish connection via CMP */ - err = cmp_connection_establish(conn, - amdtp_stream_get_max_payload(stream)); + // Establish connection via CMP. + err = cmp_connection_establish(conn); if (err < 0) - goto end; + return err; - /* start amdtp stream */ - err = amdtp_stream_start(stream, - conn->resources.channel, - conn->speed); + // Start amdtp stream. + err = amdtp_stream_start(stream, conn->resources.channel, conn->speed); if (err < 0) { - stop_stream(efw, stream); - goto end; + cmp_connection_break(conn); + return err; } - /* wait first callback */ + // Wait first callback. if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) { - stop_stream(efw, stream); - err = -ETIMEDOUT; + amdtp_stream_stop(stream); + cmp_connection_break(conn); + return -ETIMEDOUT; } -end: - return err; + + return 0; } /* @@ -164,13 +146,13 @@ int snd_efw_stream_init_duplex(struct snd_efw *efw) (efw->firmware_version == 0x5070000 || efw->firmware_version == 0x5070300 || efw->firmware_version == 0x5080000)) - efw->tx_stream.tx_first_dbc = 0x02; + efw->tx_stream.ctx_data.tx.first_dbc = 0x02; /* AudioFire9 always reports wrong dbs. */ if (efw->is_af9) efw->tx_stream.flags |= CIP_WRONG_DBS; /* Firmware version 5.5 reports fixed interval for dbc. */ if (efw->firmware_version == 0x5050000) - efw->tx_stream.tx_dbc_interval = 8; + efw->tx_stream.ctx_data.tx.dbc_interval = 8; err = init_stream(efw, &efw->rx_stream); if (err < 0) { @@ -188,75 +170,135 @@ end: return err; } -int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate) +static int keep_resources(struct snd_efw *efw, struct amdtp_stream *stream, + unsigned int rate, unsigned int mode) { - unsigned int curr_rate; - int err = 0; + unsigned int pcm_channels; + unsigned int midi_ports; + struct cmp_connection *conn; + int err; - /* Need no substreams */ - if (efw->playback_substreams == 0 && efw->capture_substreams == 0) - goto end; + if (stream == &efw->tx_stream) { + pcm_channels = efw->pcm_capture_channels[mode]; + midi_ports = efw->midi_out_ports; + conn = &efw->out_conn; + } else { + pcm_channels = efw->pcm_playback_channels[mode]; + midi_ports = efw->midi_in_ports; + conn = &efw->in_conn; + } - /* - * Considering JACK/FFADO streaming: - * TODO: This can be removed hwdep functionality becomes popular. - */ - err = check_connection_used_by_others(efw, &efw->rx_stream); + err = amdtp_am824_set_parameters(stream, rate, pcm_channels, + midi_ports, false); if (err < 0) - goto end; + return err; - /* packet queueing error */ - if (amdtp_streaming_error(&efw->tx_stream)) - stop_stream(efw, &efw->tx_stream); - if (amdtp_streaming_error(&efw->rx_stream)) - stop_stream(efw, &efw->rx_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) +{ + unsigned int curr_rate; + int err; - /* stop streams if rate is different */ + // Considering JACK/FFADO streaming: + // TODO: This can be removed hwdep functionality becomes popular. + err = check_connection_used_by_others(efw, &efw->rx_stream); + if (err < 0) + return err; + + // stop streams if rate is different. err = snd_efw_command_get_sampling_rate(efw, &curr_rate); if (err < 0) - goto end; + return err; if (rate == 0) rate = curr_rate; if (rate != curr_rate) { stop_stream(efw, &efw->tx_stream); stop_stream(efw, &efw->rx_stream); + + cmp_connection_release(&efw->out_conn); + cmp_connection_release(&efw->in_conn); } - /* master should be always running */ - if (!amdtp_stream_running(&efw->rx_stream)) { + if (efw->substreams_counter == 0 || rate != curr_rate) { + unsigned int mode; + err = snd_efw_command_set_sampling_rate(efw, rate); if (err < 0) - goto end; + return err; + + err = snd_efw_get_multiplier_mode(rate, &mode); + if (err < 0) + return err; + + err = keep_resources(efw, &efw->tx_stream, rate, mode); + if (err < 0) + return err; + err = keep_resources(efw, &efw->rx_stream, rate, mode); + if (err < 0) { + cmp_connection_release(&efw->in_conn); + return err; + } + } + + return 0; +} + +int snd_efw_stream_start_duplex(struct snd_efw *efw) +{ + unsigned int rate; + int err = 0; + + // Need no substreams. + if (efw->substreams_counter == 0) + return -EIO; + + err = snd_efw_command_get_sampling_rate(efw, &rate); + if (err < 0) + return err; + + if (amdtp_streaming_error(&efw->rx_stream) || + amdtp_streaming_error(&efw->tx_stream)) { + stop_stream(efw, &efw->rx_stream); + stop_stream(efw, &efw->tx_stream); + } + + /* master should be always running */ + if (!amdtp_stream_running(&efw->rx_stream)) { err = start_stream(efw, &efw->rx_stream, rate); if (err < 0) { dev_err(&efw->unit->device, "fail to start AMDTP master stream:%d\n", err); - goto end; + goto error; } } - /* start slave if needed */ - if (efw->capture_substreams > 0 && - !amdtp_stream_running(&efw->tx_stream)) { + if (!amdtp_stream_running(&efw->tx_stream)) { err = start_stream(efw, &efw->tx_stream, rate); if (err < 0) { dev_err(&efw->unit->device, "fail to start AMDTP slave stream:%d\n", err); - stop_stream(efw, &efw->rx_stream); + goto error; } } -end: + + return 0; +error: + stop_stream(efw, &efw->rx_stream); + stop_stream(efw, &efw->tx_stream); return err; } void snd_efw_stream_stop_duplex(struct snd_efw *efw) { - if (efw->capture_substreams == 0) { + if (efw->substreams_counter == 0) { stop_stream(efw, &efw->tx_stream); + stop_stream(efw, &efw->rx_stream); - if (efw->playback_substreams == 0) - stop_stream(efw, &efw->rx_stream); + cmp_connection_release(&efw->out_conn); + cmp_connection_release(&efw->in_conn); } } diff --git a/sound/firewire/motu/amdtp-motu-trace.h b/sound/firewire/motu/amdtp-motu-trace.h index 4d2351c0e8a3..3d36f125cf6a 100644 --- a/sound/firewire/motu/amdtp-motu-trace.h +++ b/sound/firewire/motu/amdtp-motu-trace.h @@ -18,7 +18,7 @@ static void copy_sph(u32 *frame, __be32 *buffer, unsigned int data_blocks, static void copy_message(u64 *frames, __be32 *buffer, unsigned int data_blocks, unsigned int data_block_quadlets); -TRACE_EVENT(in_data_block_sph, +TRACE_EVENT(data_block_sph, TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer), TP_ARGS(s, data_blocks, buffer), TP_STRUCT__entry( @@ -28,8 +28,13 @@ TRACE_EVENT(in_data_block_sph, __dynamic_array(u32, tstamps, data_blocks) ), TP_fast_assign( - __entry->src = fw_parent_device(s->unit)->node_id; - __entry->dst = fw_parent_device(s->unit)->card->node_id; + if (s->direction == AMDTP_IN_STREAM) { + __entry->src = fw_parent_device(s->unit)->node_id; + __entry->dst = fw_parent_device(s->unit)->card->node_id; + } else { + __entry->src = fw_parent_device(s->unit)->card->node_id; + __entry->dst = fw_parent_device(s->unit)->node_id; + } __entry->data_blocks = data_blocks; copy_sph(__get_dynamic_array(tstamps), buffer, data_blocks, s->data_block_quadlets); ), @@ -42,55 +47,7 @@ TRACE_EVENT(in_data_block_sph, ) ); -TRACE_EVENT(out_data_block_sph, - TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer), - TP_ARGS(s, data_blocks, buffer), - TP_STRUCT__entry( - __field(int, src) - __field(int, dst) - __field(unsigned int, data_blocks) - __dynamic_array(u32, tstamps, data_blocks) - ), - TP_fast_assign( - __entry->src = fw_parent_device(s->unit)->card->node_id; - __entry->dst = fw_parent_device(s->unit)->node_id; - __entry->data_blocks = data_blocks; - copy_sph(__get_dynamic_array(tstamps), buffer, data_blocks, s->data_block_quadlets); - ), - TP_printk( - "%04x %04x %u %s", - __entry->src, - __entry->dst, - __entry->data_blocks, - __print_array(__get_dynamic_array(tstamps), __entry->data_blocks, 4) - ) -); - -TRACE_EVENT(in_data_block_message, - TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer), - TP_ARGS(s, data_blocks, buffer), - TP_STRUCT__entry( - __field(int, src) - __field(int, dst) - __field(unsigned int, data_blocks) - __dynamic_array(u64, messages, data_blocks) - ), - TP_fast_assign( - __entry->src = fw_parent_device(s->unit)->node_id; - __entry->dst = fw_parent_device(s->unit)->card->node_id; - __entry->data_blocks = data_blocks; - copy_message(__get_dynamic_array(messages), buffer, data_blocks, s->data_block_quadlets); - ), - TP_printk( - "%04x %04x %u %s", - __entry->src, - __entry->dst, - __entry->data_blocks, - __print_array(__get_dynamic_array(messages), __entry->data_blocks, 8) - ) -); - -TRACE_EVENT(out_data_block_message, +TRACE_EVENT(data_block_message, TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer), TP_ARGS(s, data_blocks, buffer), TP_STRUCT__entry( @@ -100,8 +57,13 @@ TRACE_EVENT(out_data_block_message, __dynamic_array(u64, messages, data_blocks) ), TP_fast_assign( - __entry->src = fw_parent_device(s->unit)->card->node_id; - __entry->dst = fw_parent_device(s->unit)->node_id; + if (s->direction == AMDTP_IN_STREAM) { + __entry->src = fw_parent_device(s->unit)->node_id; + __entry->dst = fw_parent_device(s->unit)->card->node_id; + } else { + __entry->src = fw_parent_device(s->unit)->card->node_id; + __entry->dst = fw_parent_device(s->unit)->node_id; + } __entry->data_blocks = data_blocks; copy_message(__get_dynamic_array(messages), buffer, data_blocks, s->data_block_quadlets); ), diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c index 782d1fa024ec..7973dedd31ef 100644 --- a/sound/firewire/motu/amdtp-motu.c +++ b/sound/firewire/motu/amdtp-motu.c @@ -305,8 +305,8 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s, struct amdtp_motu *p = s->protocol; struct snd_pcm_substream *pcm; - trace_in_data_block_sph(s, data_blocks, buffer); - trace_in_data_block_message(s, data_blocks, buffer); + trace_data_block_sph(s, data_blocks, buffer); + trace_data_block_message(s, data_blocks, buffer); if (p->midi_ports) read_midi_messages(s, buffer, data_blocks); @@ -383,8 +383,8 @@ static unsigned int process_rx_data_blocks(struct amdtp_stream *s, write_sph(s, buffer, data_blocks); - trace_out_data_block_sph(s, data_blocks, buffer); - trace_out_data_block_message(s, data_blocks, buffer); + trace_data_block_sph(s, data_blocks, buffer); + trace_data_block_message(s, data_blocks, buffer); return data_blocks; } @@ -428,7 +428,7 @@ int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit, return err; s->sph = 1; - s->fdf = MOTU_FDF_AM824; + s->ctx_data.rx.fdf = MOTU_FDF_AM824; return 0; } diff --git a/sound/firewire/motu/motu-midi.c b/sound/firewire/motu/motu-midi.c index 75f6b2e9ca9e..46a0035df31e 100644 --- a/sound/firewire/motu/motu-midi.c +++ b/sound/firewire/motu/motu-midi.c @@ -6,7 +6,7 @@ */ #include "motu.h" -static int midi_capture_open(struct snd_rawmidi_substream *substream) +static int midi_open(struct snd_rawmidi_substream *substream) { struct snd_motu *motu = substream->rmidi->private_data; int err; @@ -17,30 +17,13 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream) mutex_lock(&motu->mutex); - motu->capture_substreams++; - err = snd_motu_stream_start_duplex(motu, 0); - - mutex_unlock(&motu->mutex); - - if (err < 0) - snd_motu_stream_lock_release(motu); - - return err; -} - -static int midi_playback_open(struct snd_rawmidi_substream *substream) -{ - struct snd_motu *motu = substream->rmidi->private_data; - int err; - - err = snd_motu_stream_lock_try(motu); - if (err < 0) - return err; - - mutex_lock(&motu->mutex); - - motu->playback_substreams++; - err = snd_motu_stream_start_duplex(motu, 0); + err = snd_motu_stream_reserve_duplex(motu, 0); + if (err >= 0) { + ++motu->substreams_counter; + err = snd_motu_stream_start_duplex(motu); + if (err < 0) + --motu->substreams_counter; + } mutex_unlock(&motu->mutex); @@ -50,28 +33,13 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream) return err; } -static int midi_capture_close(struct snd_rawmidi_substream *substream) -{ - struct snd_motu *motu = substream->rmidi->private_data; - - mutex_lock(&motu->mutex); - - motu->capture_substreams--; - snd_motu_stream_stop_duplex(motu); - - mutex_unlock(&motu->mutex); - - snd_motu_stream_lock_release(motu); - return 0; -} - -static int midi_playback_close(struct snd_rawmidi_substream *substream) +static int midi_close(struct snd_rawmidi_substream *substream) { struct snd_motu *motu = substream->rmidi->private_data; mutex_lock(&motu->mutex); - motu->playback_substreams--; + --motu->substreams_counter; snd_motu_stream_stop_duplex(motu); mutex_unlock(&motu->mutex); @@ -128,13 +96,13 @@ static void set_midi_substream_names(struct snd_motu *motu, int snd_motu_create_midi_devices(struct snd_motu *motu) { static const struct snd_rawmidi_ops capture_ops = { - .open = midi_capture_open, - .close = midi_capture_close, + .open = midi_open, + .close = midi_close, .trigger = midi_capture_trigger, }; static const struct snd_rawmidi_ops playback_ops = { - .open = midi_playback_open, - .close = midi_playback_close, + .open = midi_open, + .close = midi_close, .trigger = midi_playback_trigger, }; struct snd_rawmidi *rmidi; diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c index 5e7db7aa4f08..aa2e584da6fe 100644 --- a/sound/firewire/motu/motu-pcm.c +++ b/sound/firewire/motu/motu-pcm.c @@ -189,8 +189,8 @@ static int pcm_close(struct snd_pcm_substream *substream) return 0; } -static int capture_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) +static int pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) { struct snd_motu *motu = substream->private_data; int err; @@ -201,57 +201,26 @@ static int capture_hw_params(struct snd_pcm_substream *substream, return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { - mutex_lock(&motu->mutex); - motu->capture_substreams++; - mutex_unlock(&motu->mutex); - } - - return 0; -} -static int playback_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_motu *motu = substream->private_data; - int err; - - err = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - return err; + unsigned int rate = params_rate(hw_params); - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&motu->mutex); - motu->playback_substreams++; + err = snd_motu_stream_reserve_duplex(motu, rate); + if (err >= 0) + ++motu->substreams_counter; mutex_unlock(&motu->mutex); } - return 0; -} - -static int capture_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_motu *motu = substream->private_data; - - mutex_lock(&motu->mutex); - - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - motu->capture_substreams--; - - snd_motu_stream_stop_duplex(motu); - - mutex_unlock(&motu->mutex); - - return snd_pcm_lib_free_vmalloc_buffer(substream); + return err; } -static int playback_hw_free(struct snd_pcm_substream *substream) +static int pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_motu *motu = substream->private_data; mutex_lock(&motu->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - motu->playback_substreams--; + --motu->substreams_counter; snd_motu_stream_stop_duplex(motu); @@ -266,7 +235,7 @@ static int capture_prepare(struct snd_pcm_substream *substream) int err; mutex_lock(&motu->mutex); - err = snd_motu_stream_start_duplex(motu, substream->runtime->rate); + err = snd_motu_stream_start_duplex(motu); mutex_unlock(&motu->mutex); if (err >= 0) amdtp_stream_pcm_prepare(&motu->tx_stream); @@ -279,7 +248,7 @@ static int playback_prepare(struct snd_pcm_substream *substream) int err; mutex_lock(&motu->mutex); - err = snd_motu_stream_start_duplex(motu, substream->runtime->rate); + err = snd_motu_stream_start_duplex(motu); mutex_unlock(&motu->mutex); if (err >= 0) amdtp_stream_pcm_prepare(&motu->rx_stream); @@ -355,8 +324,8 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu) .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = capture_hw_params, - .hw_free = capture_hw_free, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, .prepare = capture_prepare, .trigger = capture_trigger, .pointer = capture_pointer, @@ -367,8 +336,8 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu) .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = playback_hw_params, - .hw_free = playback_hw_free, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, .prepare = playback_prepare, .trigger = playback_trigger, .pointer = playback_pointer, diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c index 81f7edc560d0..2bbb335e8de1 100644 --- a/sound/firewire/motu/motu-stream.c +++ b/sound/firewire/motu/motu-stream.c @@ -25,48 +25,47 @@ #define RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS 0x00000040 #define TX_PACKET_TRANSMISSION_SPEED_MASK 0x0000000f -static int start_both_streams(struct snd_motu *motu, unsigned int rate) +static int keep_resources(struct snd_motu *motu, unsigned int rate, + struct amdtp_stream *stream) { + struct fw_iso_resources *resources; + struct snd_motu_packet_format *packet_format; unsigned int midi_ports = 0; - __be32 reg; - u32 data; int err; - if ((motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) || - (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q)) - midi_ports = 1; + if (stream == &motu->rx_stream) { + resources = &motu->rx_resources; + packet_format = &motu->rx_packet_formats; - /* Set packet formation to our packet streaming engine. */ - err = amdtp_motu_set_parameters(&motu->rx_stream, rate, midi_ports, - &motu->rx_packet_formats); - if (err < 0) - return err; + if ((motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_2ND_Q) || + (motu->spec->flags & SND_MOTU_SPEC_RX_MIDI_3RD_Q)) + midi_ports = 1; + } else { + resources = &motu->tx_resources; + packet_format = &motu->tx_packet_formats; - if ((motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q) || - (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q)) - midi_ports = 1; - else - midi_ports = 0; + if ((motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_2ND_Q) || + (motu->spec->flags & SND_MOTU_SPEC_TX_MIDI_3RD_Q)) + midi_ports = 1; + } - err = amdtp_motu_set_parameters(&motu->tx_stream, rate, midi_ports, - &motu->tx_packet_formats); + err = amdtp_motu_set_parameters(stream, rate, midi_ports, + packet_format); if (err < 0) return err; - /* Get isochronous resources on the bus. */ - err = fw_iso_resources_allocate(&motu->rx_resources, - amdtp_stream_get_max_payload(&motu->rx_stream), + return fw_iso_resources_allocate(resources, + amdtp_stream_get_max_payload(stream), fw_parent_device(motu->unit)->max_speed); - if (err < 0) - return err; +} - err = fw_iso_resources_allocate(&motu->tx_resources, - amdtp_stream_get_max_payload(&motu->tx_stream), - fw_parent_device(motu->unit)->max_speed); - if (err < 0) - return err; +static int begin_session(struct snd_motu *motu) +{ + __be32 reg; + u32 data; + int err; - /* Configure the unit to start isochronous communication. */ + // Configure the unit to start isochronous communication. err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, ®, sizeof(reg)); if (err < 0) @@ -83,7 +82,7 @@ static int start_both_streams(struct snd_motu *motu, unsigned int rate) sizeof(reg)); } -static void stop_both_streams(struct snd_motu *motu) +static void finish_session(struct snd_motu *motu) { __be32 reg; u32 data; @@ -93,6 +92,9 @@ static void stop_both_streams(struct snd_motu *motu) if (err < 0) return; + amdtp_stream_stop(&motu->tx_stream); + amdtp_stream_stop(&motu->rx_stream); + err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, ®, sizeof(reg)); if (err < 0) @@ -105,9 +107,6 @@ static void stop_both_streams(struct snd_motu *motu) reg = cpu_to_be32(data); snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, ®, sizeof(reg)); - - fw_iso_resources_free(&motu->tx_resources); - fw_iso_resources_free(&motu->rx_resources); } static int start_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream) @@ -125,28 +124,12 @@ static int start_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream) if (err < 0) return err; - if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) { - amdtp_stream_stop(stream); - fw_iso_resources_free(resources); + if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) return -ETIMEDOUT; - } return 0; } -static void stop_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream) -{ - struct fw_iso_resources *resources; - - if (stream == &motu->rx_stream) - resources = &motu->rx_resources; - else - resources = &motu->tx_resources; - - amdtp_stream_stop(stream); - fw_iso_resources_free(resources); -} - int snd_motu_stream_cache_packet_formats(struct snd_motu *motu) { int err; @@ -174,6 +157,48 @@ 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) +{ + unsigned int curr_rate; + int err; + + err = motu->spec->protocol->get_clock_rate(motu, &curr_rate); + if (err < 0) + return err; + if (rate == 0) + rate = curr_rate; + + if (motu->substreams_counter == 0 || curr_rate != rate) { + finish_session(motu); + + fw_iso_resources_free(&motu->tx_resources); + fw_iso_resources_free(&motu->rx_resources); + + err = motu->spec->protocol->set_clock_rate(motu, rate); + if (err < 0) { + dev_err(&motu->unit->device, + "fail to set sampling rate: %d\n", err); + return err; + } + + err = snd_motu_stream_cache_packet_formats(motu); + if (err < 0) + return err; + + err = keep_resources(motu, rate, &motu->tx_stream); + if (err < 0) + return err; + + err = keep_resources(motu, rate, &motu->rx_stream); + if (err < 0) { + fw_iso_resources_free(&motu->tx_resources); + return err; + } + } + + return 0; +} + static int ensure_packet_formats(struct snd_motu *motu) { __be32 reg; @@ -200,55 +225,34 @@ static int ensure_packet_formats(struct snd_motu *motu) sizeof(reg)); } -int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate) +int snd_motu_stream_start_duplex(struct snd_motu *motu) { - const struct snd_motu_protocol *protocol = motu->spec->protocol; - unsigned int curr_rate; + unsigned int generation = motu->rx_resources.generation; int err = 0; - if (motu->capture_substreams == 0 && motu->playback_substreams == 0) + if (motu->substreams_counter == 0) return 0; - /* Some packet queueing errors. */ if (amdtp_streaming_error(&motu->rx_stream) || - amdtp_streaming_error(&motu->tx_stream)) { - amdtp_stream_stop(&motu->rx_stream); - amdtp_stream_stop(&motu->tx_stream); - stop_both_streams(motu); - } + amdtp_streaming_error(&motu->tx_stream)) + finish_session(motu); - err = snd_motu_stream_cache_packet_formats(motu); - if (err < 0) - return err; + if (generation != fw_parent_device(motu->unit)->card->generation) { + err = fw_iso_resources_update(&motu->rx_resources); + if (err < 0) + return err; - /* Stop stream if rate is different. */ - err = protocol->get_clock_rate(motu, &curr_rate); - if (err < 0) { - dev_err(&motu->unit->device, - "fail to get sampling rate: %d\n", err); - return err; - } - if (rate == 0) - rate = curr_rate; - if (rate != curr_rate) { - amdtp_stream_stop(&motu->rx_stream); - amdtp_stream_stop(&motu->tx_stream); - stop_both_streams(motu); + err = fw_iso_resources_update(&motu->tx_resources); + if (err < 0) + return err; } if (!amdtp_stream_running(&motu->rx_stream)) { - err = protocol->set_clock_rate(motu, rate); - if (err < 0) { - dev_err(&motu->unit->device, - "fail to set sampling rate: %d\n", err); - return err; - } - err = ensure_packet_formats(motu); if (err < 0) return err; - err = start_both_streams(motu, rate); + err = begin_session(motu); if (err < 0) { dev_err(&motu->unit->device, "fail to start isochronous comm: %d\n", err); @@ -262,7 +266,7 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate) goto stop_streams; } - err = protocol->switch_fetching_mode(motu, true); + err = motu->spec->protocol->switch_fetching_mode(motu, true); if (err < 0) { dev_err(&motu->unit->device, "fail to enable frame fetching: %d\n", err); @@ -270,13 +274,11 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate) } } - if (!amdtp_stream_running(&motu->tx_stream) && - motu->capture_substreams > 0) { + if (!amdtp_stream_running(&motu->tx_stream)) { err = start_isoc_ctx(motu, &motu->tx_stream); if (err < 0) { dev_err(&motu->unit->device, "fail to start IR context: %d", err); - amdtp_stream_stop(&motu->rx_stream); goto stop_streams; } } @@ -284,21 +286,17 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate) return 0; stop_streams: - stop_both_streams(motu); + finish_session(motu); return err; } void snd_motu_stream_stop_duplex(struct snd_motu *motu) { - if (motu->capture_substreams == 0) { - if (amdtp_stream_running(&motu->tx_stream)) - stop_isoc_ctx(motu, &motu->tx_stream); - - if (motu->playback_substreams == 0) { - if (amdtp_stream_running(&motu->rx_stream)) - stop_isoc_ctx(motu, &motu->rx_stream); - stop_both_streams(motu); - } + if (motu->substreams_counter == 0) { + finish_session(motu); + + fw_iso_resources_free(&motu->tx_resources); + fw_iso_resources_free(&motu->rx_resources); } } @@ -371,8 +369,7 @@ void snd_motu_stream_destroy_duplex(struct snd_motu *motu) destroy_stream(motu, AMDTP_IN_STREAM); destroy_stream(motu, AMDTP_OUT_STREAM); - motu->playback_substreams = 0; - motu->capture_substreams = 0; + motu->substreams_counter = 0; } static void motu_lock_changed(struct snd_motu *motu) diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h index 7c795294428d..09d1451d7de4 100644 --- a/sound/firewire/motu/motu.h +++ b/sound/firewire/motu/motu.h @@ -59,8 +59,7 @@ struct snd_motu { struct amdtp_stream rx_stream; struct fw_iso_resources tx_resources; struct fw_iso_resources rx_resources; - unsigned int capture_substreams; - unsigned int playback_substreams; + unsigned int substreams_counter; /* For notification. */ struct fw_address_handler async_handler; @@ -153,7 +152,8 @@ 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_start_duplex(struct snd_motu *motu, unsigned int rate); +int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate); +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); void snd_motu_stream_lock_release(struct snd_motu *motu); diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c index cbce01308bd1..9bdec08cb8ea 100644 --- a/sound/firewire/oxfw/oxfw-midi.c +++ b/sound/firewire/oxfw/oxfw-midi.c @@ -18,8 +18,13 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream) mutex_lock(&oxfw->mutex); - oxfw->capture_substreams++; - err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream, 0, 0); + err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, 0, 0); + if (err >= 0) { + ++oxfw->substreams_count; + err = snd_oxfw_stream_start_duplex(oxfw); + if (err < 0) + --oxfw->substreams_count; + } mutex_unlock(&oxfw->mutex); @@ -40,8 +45,11 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream) mutex_lock(&oxfw->mutex); - oxfw->playback_substreams++; - err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream, 0, 0); + err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, 0, 0); + if (err >= 0) { + ++oxfw->substreams_count; + err = snd_oxfw_stream_start_duplex(oxfw); + } mutex_unlock(&oxfw->mutex); @@ -57,8 +65,8 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream) mutex_lock(&oxfw->mutex); - oxfw->capture_substreams--; - snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream); + --oxfw->substreams_count; + snd_oxfw_stream_stop_duplex(oxfw); mutex_unlock(&oxfw->mutex); @@ -72,8 +80,8 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream) mutex_lock(&oxfw->mutex); - oxfw->playback_substreams--; - snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream); + --oxfw->substreams_count; + snd_oxfw_stream_stop_duplex(oxfw); mutex_unlock(&oxfw->mutex); diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c index 94f367cdfdf3..7c6d1c277d4d 100644 --- a/sound/firewire/oxfw/oxfw-pcm.c +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -219,12 +219,18 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + unsigned int rate = params_rate(hw_params); + unsigned int channels = params_channels(hw_params); + mutex_lock(&oxfw->mutex); - oxfw->capture_substreams++; + err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, + rate, channels); + if (err >= 0) + ++oxfw->substreams_count; mutex_unlock(&oxfw->mutex); } - return 0; + return err; } static int pcm_playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) @@ -238,8 +244,14 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream, return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + unsigned int rate = params_rate(hw_params); + unsigned int channels = params_channels(hw_params); + mutex_lock(&oxfw->mutex); - oxfw->playback_substreams++; + err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, + rate, channels); + if (err >= 0) + ++oxfw->substreams_count; mutex_unlock(&oxfw->mutex); } @@ -253,9 +265,9 @@ static int pcm_capture_hw_free(struct snd_pcm_substream *substream) mutex_lock(&oxfw->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - oxfw->capture_substreams--; + --oxfw->substreams_count; - snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream); + snd_oxfw_stream_stop_duplex(oxfw); mutex_unlock(&oxfw->mutex); @@ -268,9 +280,9 @@ static int pcm_playback_hw_free(struct snd_pcm_substream *substream) mutex_lock(&oxfw->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - oxfw->playback_substreams--; + --oxfw->substreams_count; - snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream); + snd_oxfw_stream_stop_duplex(oxfw); mutex_unlock(&oxfw->mutex); @@ -280,12 +292,10 @@ static int pcm_playback_hw_free(struct snd_pcm_substream *substream) static int pcm_capture_prepare(struct snd_pcm_substream *substream) { struct snd_oxfw *oxfw = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&oxfw->mutex); - err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream, - runtime->rate, runtime->channels); + err = snd_oxfw_stream_start_duplex(oxfw); mutex_unlock(&oxfw->mutex); if (err < 0) goto end; @@ -297,12 +307,10 @@ end: static int pcm_playback_prepare(struct snd_pcm_substream *substream) { struct snd_oxfw *oxfw = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; int err; mutex_lock(&oxfw->mutex); - err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream, - runtime->rate, runtime->channels); + err = snd_oxfw_stream_start_duplex(oxfw); mutex_unlock(&oxfw->mutex); if (err < 0) goto end; diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 5ffedb0ade3f..74c972d25c66 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -100,85 +100,34 @@ static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s, return 0; } -static void stop_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream) +static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream) { - amdtp_stream_pcm_abort(stream); - amdtp_stream_stop(stream); - - if (stream == &oxfw->tx_stream) - cmp_connection_break(&oxfw->out_conn); - else - cmp_connection_break(&oxfw->in_conn); -} - -static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream, - unsigned int rate, unsigned int pcm_channels) -{ - u8 **formats; struct cmp_connection *conn; - struct snd_oxfw_stream_formation formation; - unsigned int i, midi_ports; int err; - if (stream == &oxfw->rx_stream) { - formats = oxfw->rx_stream_formats; + if (stream == &oxfw->rx_stream) conn = &oxfw->in_conn; - } else { - formats = oxfw->tx_stream_formats; + else conn = &oxfw->out_conn; - } - - /* Get stream format */ - for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { - if (formats[i] == NULL) - break; - - err = snd_oxfw_stream_parse_format(formats[i], &formation); - if (err < 0) - goto end; - if (rate != formation.rate) - continue; - if (pcm_channels == 0 || pcm_channels == formation.pcm) - break; - } - if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) { - err = -EINVAL; - goto end; - } - - pcm_channels = formation.pcm; - midi_ports = formation.midi * 8; - - /* The stream should have one pcm channels at least */ - if (pcm_channels == 0) { - err = -EINVAL; - goto end; - } - err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports, - false); - if (err < 0) - goto end; - err = cmp_connection_establish(conn, - amdtp_stream_get_max_payload(stream)); + err = cmp_connection_establish(conn); if (err < 0) - goto end; + return err; - err = amdtp_stream_start(stream, - conn->resources.channel, - conn->speed); + err = amdtp_stream_start(stream, conn->resources.channel, conn->speed); if (err < 0) { cmp_connection_break(conn); - goto end; + return err; } - /* Wait first packet */ + // Wait first packet. if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) { - stop_stream(oxfw, stream); - err = -ETIMEDOUT; + amdtp_stream_stop(stream); + cmp_connection_break(conn); + return -ETIMEDOUT; } -end: - return err; + + return 0; } static int check_connection_used_by_others(struct snd_oxfw *oxfw, @@ -205,8 +154,7 @@ static int check_connection_used_by_others(struct snd_oxfw *oxfw, return err; } -int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream) +static int init_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream) { struct cmp_connection *conn; enum cmp_direction c_dir; @@ -225,13 +173,12 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw, err = cmp_connection_init(conn, oxfw->unit, c_dir, 0); if (err < 0) - goto end; + return err; err = amdtp_am824_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING); if (err < 0) { - amdtp_stream_destroy(stream); cmp_connection_destroy(conn); - goto end; + return err; } /* @@ -245,115 +192,195 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw, if (oxfw->wrong_dbs) oxfw->tx_stream.flags |= CIP_WRONG_DBS; } -end: - return err; + + return 0; } -int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream, - unsigned int rate, unsigned int pcm_channels) +static int keep_resources(struct snd_oxfw *oxfw, struct amdtp_stream *stream) { - struct amdtp_stream *opposite; - struct snd_oxfw_stream_formation formation; enum avc_general_plug_dir dir; - unsigned int substreams, opposite_substreams; - int err = 0; + u8 **formats; + struct snd_oxfw_stream_formation formation; + struct cmp_connection *conn; + int i; + int err; - if (stream == &oxfw->tx_stream) { - substreams = oxfw->capture_substreams; - opposite = &oxfw->rx_stream; - opposite_substreams = oxfw->playback_substreams; - dir = AVC_GENERAL_PLUG_DIR_OUT; + if (stream == &oxfw->rx_stream) { + dir = AVC_GENERAL_PLUG_DIR_IN; + formats = oxfw->rx_stream_formats; + conn = &oxfw->in_conn; } else { - substreams = oxfw->playback_substreams; - opposite_substreams = oxfw->capture_substreams; + dir = AVC_GENERAL_PLUG_DIR_OUT; + formats = oxfw->tx_stream_formats; + conn = &oxfw->out_conn; + } - if (oxfw->has_output) - opposite = &oxfw->rx_stream; - else - opposite = NULL; + err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation); + if (err < 0) + return err; - dir = AVC_GENERAL_PLUG_DIR_IN; + for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { + struct snd_oxfw_stream_formation fmt; + + if (formats[i] == NULL) + break; + + err = snd_oxfw_stream_parse_format(formats[i], &fmt); + if (err < 0) + return err; + + if (fmt.rate == formation.rate && fmt.pcm == formation.pcm && + fmt.midi == formation.midi) + break; } + if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) + return -EINVAL; - if (substreams == 0) - goto end; + // The stream should have one pcm channels at least. + if (formation.pcm == 0) + return -EINVAL; - /* - * Considering JACK/FFADO streaming: - * TODO: This can be removed hwdep functionality becomes popular. - */ - err = check_connection_used_by_others(oxfw, stream); + err = amdtp_am824_set_parameters(stream, formation.rate, formation.pcm, + formation.midi * 8, false); if (err < 0) - goto end; + return err; - /* packet queueing error */ - if (amdtp_streaming_error(stream)) - stop_stream(oxfw, stream); + return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream)); +} + +int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw, + struct amdtp_stream *stream, + unsigned int rate, unsigned int pcm_channels) +{ + struct snd_oxfw_stream_formation formation; + enum avc_general_plug_dir dir; + int err; + + // Considering JACK/FFADO streaming: + // TODO: This can be removed hwdep functionality becomes popular. + err = check_connection_used_by_others(oxfw, &oxfw->rx_stream); + if (err < 0) + return err; + if (oxfw->has_output) { + err = check_connection_used_by_others(oxfw, &oxfw->tx_stream); + if (err < 0) + return err; + } + + if (stream == &oxfw->tx_stream) + dir = AVC_GENERAL_PLUG_DIR_OUT; + else + dir = AVC_GENERAL_PLUG_DIR_IN; err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation); if (err < 0) - goto end; - if (rate == 0) + return err; + if (rate == 0) { rate = formation.rate; - if (pcm_channels == 0) pcm_channels = formation.pcm; + } + if (formation.rate != rate || formation.pcm != pcm_channels) { + amdtp_stream_stop(&oxfw->rx_stream); + cmp_connection_break(&oxfw->in_conn); + cmp_connection_release(&oxfw->in_conn); - if ((formation.rate != rate) || (formation.pcm != pcm_channels)) { - if (opposite != NULL) { - err = check_connection_used_by_others(oxfw, opposite); - if (err < 0) - goto end; - stop_stream(oxfw, opposite); + if (oxfw->has_output) { + amdtp_stream_stop(&oxfw->tx_stream); + cmp_connection_break(&oxfw->out_conn); + cmp_connection_release(&oxfw->out_conn); } - stop_stream(oxfw, stream); + } + if (oxfw->substreams_count == 0 || + formation.rate != rate || formation.pcm != pcm_channels) { err = set_stream_format(oxfw, stream, rate, pcm_channels); if (err < 0) { dev_err(&oxfw->unit->device, "fail to set stream format: %d\n", err); - goto end; + return err; } - /* Start opposite stream if needed. */ - if (opposite && !amdtp_stream_running(opposite) && - (opposite_substreams > 0)) { - err = start_stream(oxfw, opposite, rate, 0); + err = keep_resources(oxfw, &oxfw->rx_stream); + if (err < 0) + return err; + + if (oxfw->has_output) { + err = keep_resources(oxfw, &oxfw->tx_stream); if (err < 0) { - dev_err(&oxfw->unit->device, - "fail to restart stream: %d\n", err); - goto end; + cmp_connection_release(&oxfw->in_conn); + return err; } } } - /* Start requested stream. */ - if (!amdtp_stream_running(stream)) { - err = start_stream(oxfw, stream, rate, pcm_channels); - if (err < 0) + return 0; +} + +int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw) +{ + int err; + + if (oxfw->substreams_count == 0) + return -EIO; + + if (amdtp_streaming_error(&oxfw->rx_stream) || + amdtp_streaming_error(&oxfw->tx_stream)) { + amdtp_stream_stop(&oxfw->rx_stream); + cmp_connection_break(&oxfw->in_conn); + + if (oxfw->has_output) { + amdtp_stream_stop(&oxfw->tx_stream); + cmp_connection_break(&oxfw->out_conn); + } + } + + if (!amdtp_stream_running(&oxfw->rx_stream)) { + err = start_stream(oxfw, &oxfw->rx_stream); + if (err < 0) { dev_err(&oxfw->unit->device, - "fail to start stream: %d\n", err); + "fail to start rx stream: %d\n", err); + goto error; + } + } + + if (oxfw->has_output) { + if (!amdtp_stream_running(&oxfw->tx_stream)) { + err = start_stream(oxfw, &oxfw->tx_stream); + if (err < 0) { + dev_err(&oxfw->unit->device, + "fail to start tx stream: %d\n", err); + goto error; + } + } + } + + return 0; +error: + amdtp_stream_stop(&oxfw->rx_stream); + cmp_connection_break(&oxfw->in_conn); + if (oxfw->has_output) { + amdtp_stream_stop(&oxfw->tx_stream); + cmp_connection_break(&oxfw->out_conn); } -end: return err; } -void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream) +void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw) { - if (((stream == &oxfw->tx_stream) && (oxfw->capture_substreams > 0)) || - ((stream == &oxfw->rx_stream) && (oxfw->playback_substreams > 0))) - return; + if (oxfw->substreams_count == 0) { + amdtp_stream_stop(&oxfw->rx_stream); + cmp_connection_break(&oxfw->in_conn); + cmp_connection_release(&oxfw->in_conn); - stop_stream(oxfw, stream); + if (oxfw->has_output) { + amdtp_stream_stop(&oxfw->tx_stream); + cmp_connection_break(&oxfw->out_conn); + cmp_connection_release(&oxfw->out_conn); + } + } } -/* - * This function should be called before starting the stream or after stopping - * the streams. - */ -void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream) +static void destroy_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream) { struct cmp_connection *conn; @@ -366,20 +393,48 @@ void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw, cmp_connection_destroy(conn); } -void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream) +int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw) { - struct cmp_connection *conn; + int err; - if (stream == &oxfw->tx_stream) - conn = &oxfw->out_conn; - else - conn = &oxfw->in_conn; + err = init_stream(oxfw, &oxfw->rx_stream); + if (err < 0) + return err; - if (cmp_connection_update(conn) < 0) - stop_stream(oxfw, stream); - else - amdtp_stream_update(stream); + if (oxfw->has_output) { + err = init_stream(oxfw, &oxfw->tx_stream); + if (err < 0) { + destroy_stream(oxfw, &oxfw->rx_stream); + return err; + } + } + + return 0; +} + +// This function should be called before starting the stream or after stopping +// the streams. +void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw) +{ + destroy_stream(oxfw, &oxfw->rx_stream); + + if (oxfw->has_output) + destroy_stream(oxfw, &oxfw->tx_stream); +} + +void snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw) +{ + amdtp_stream_stop(&oxfw->rx_stream); + cmp_connection_break(&oxfw->in_conn); + + amdtp_stream_pcm_abort(&oxfw->rx_stream); + + if (oxfw->has_output) { + amdtp_stream_stop(&oxfw->tx_stream); + cmp_connection_break(&oxfw->out_conn); + + amdtp_stream_pcm_abort(&oxfw->tx_stream); + } } int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw, diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index 9fd145cc4b07..fb6df3fc018e 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -118,9 +118,7 @@ static void oxfw_card_free(struct snd_card *card) { struct snd_oxfw *oxfw = card->private_data; - snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream); - if (oxfw->has_output) - snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream); + snd_oxfw_stream_destroy_duplex(oxfw); } static int detect_quirks(struct snd_oxfw *oxfw) @@ -208,14 +206,9 @@ static void do_registration(struct work_struct *work) if (err < 0) goto error; - err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream); + err = snd_oxfw_stream_init_duplex(oxfw); if (err < 0) goto error; - if (oxfw->has_output) { - err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream); - if (err < 0) - goto error; - } err = snd_oxfw_create_pcm(oxfw); if (err < 0) @@ -282,11 +275,7 @@ static void oxfw_bus_reset(struct fw_unit *unit) if (oxfw->registered) { mutex_lock(&oxfw->mutex); - - snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream); - if (oxfw->has_output) - snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream); - + snd_oxfw_stream_update_duplex(oxfw); mutex_unlock(&oxfw->mutex); if (oxfw->entry->vendor_id == OUI_STANTON) diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index 36112850ef92..cb69ab87bb14 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -52,8 +52,7 @@ struct snd_oxfw { struct cmp_connection in_conn; struct amdtp_stream tx_stream; struct amdtp_stream rx_stream; - unsigned int capture_substreams; - unsigned int playback_substreams; + unsigned int substreams_count; unsigned int midi_input_ports; unsigned int midi_output_ports; @@ -99,17 +98,14 @@ int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate, enum avc_general_plug_dir dir, unsigned short pid); -int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream); -int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream, - unsigned int rate, unsigned int pcm_channels); -void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream); -void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream); -void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw, - struct amdtp_stream *stream); +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); +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); +void snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw); struct snd_oxfw_stream_formation { unsigned int rate; diff --git a/sound/firewire/packets-buffer.c b/sound/firewire/packets-buffer.c index 0d35359d25cd..0ecafd0c6722 100644 --- a/sound/firewire/packets-buffer.c +++ b/sound/firewire/packets-buffer.c @@ -37,7 +37,7 @@ int iso_packets_buffer_init(struct iso_packets_buffer *b, struct fw_unit *unit, packets_per_page = PAGE_SIZE / packet_size; if (WARN_ON(!packets_per_page)) { err = -EINVAL; - goto error; + goto err_packets; } pages = DIV_ROUND_UP(count, packets_per_page); diff --git a/sound/firewire/tascam/amdtp-tascam.c b/sound/firewire/tascam/amdtp-tascam.c index d9d20ef22f5b..95fb10b7a737 100644 --- a/sound/firewire/tascam/amdtp-tascam.c +++ b/sound/firewire/tascam/amdtp-tascam.c @@ -223,7 +223,7 @@ int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit, return 0; /* Use fixed value for FDF field. */ - s->fdf = 0x00; + s->ctx_data.rx.fdf = 0x00; /* This protocol uses fixed number of data channels for PCM samples. */ p = s->protocol; diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c index a8cd9b156488..b5ced5415e40 100644 --- a/sound/firewire/tascam/tascam-pcm.c +++ b/sound/firewire/tascam/tascam-pcm.c @@ -83,8 +83,8 @@ static int pcm_close(struct snd_pcm_substream *substream) return 0; } -static int pcm_capture_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) +static int pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) { struct snd_tscm *tscm = substream->private_data; int err; @@ -95,58 +95,26 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, return err; if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { - mutex_lock(&tscm->mutex); - tscm->substreams_counter++; - mutex_unlock(&tscm->mutex); - } - - return 0; -} - -static int pcm_playback_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_tscm *tscm = substream->private_data; - int err; - - err = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - return err; + unsigned int rate = params_rate(hw_params); - if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { mutex_lock(&tscm->mutex); - tscm->substreams_counter++; + err = snd_tscm_stream_reserve_duplex(tscm, rate); + if (err >= 0) + ++tscm->substreams_counter; mutex_unlock(&tscm->mutex); } - return 0; -} - -static int pcm_capture_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_tscm *tscm = substream->private_data; - - mutex_lock(&tscm->mutex); - - if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - tscm->substreams_counter--; - - snd_tscm_stream_stop_duplex(tscm); - - mutex_unlock(&tscm->mutex); - - return snd_pcm_lib_free_vmalloc_buffer(substream); + return err; } -static int pcm_playback_hw_free(struct snd_pcm_substream *substream) +static int pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_tscm *tscm = substream->private_data; mutex_lock(&tscm->mutex); if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) - tscm->substreams_counter--; + --tscm->substreams_counter; snd_tscm_stream_stop_duplex(tscm); @@ -259,8 +227,8 @@ int snd_tscm_create_pcm_devices(struct snd_tscm *tscm) .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_capture_hw_params, - .hw_free = pcm_capture_hw_free, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, .prepare = pcm_capture_prepare, .trigger = pcm_capture_trigger, .pointer = pcm_capture_pointer, @@ -271,8 +239,8 @@ int snd_tscm_create_pcm_devices(struct snd_tscm *tscm) .open = pcm_open, .close = pcm_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_playback_hw_params, - .hw_free = pcm_playback_hw_free, + .hw_params = pcm_hw_params, + .hw_free = pcm_hw_free, .prepare = pcm_playback_prepare, .trigger = pcm_playback_trigger, .pointer = pcm_playback_pointer, diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c index e6fcd9e19961..e852e46ebe6f 100644 --- a/sound/firewire/tascam/tascam-stream.c +++ b/sound/firewire/tascam/tascam-stream.c @@ -165,7 +165,7 @@ static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate) __be32 reg; int err; - /* Set an option for unknown purpose. */ + // Set an option for unknown purpose. reg = cpu_to_be32(0x00200000); err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION, @@ -173,17 +173,16 @@ static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate) if (err < 0) return err; - err = enable_data_channels(tscm); - if (err < 0) - return err; - - return set_clock(tscm, rate, INT_MAX); + return enable_data_channels(tscm); } static void finish_session(struct snd_tscm *tscm) { __be32 reg; + amdtp_stream_stop(&tscm->rx_stream); + amdtp_stream_stop(&tscm->tx_stream); + reg = 0; snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING, @@ -194,6 +193,19 @@ static void finish_session(struct snd_tscm *tscm) TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON, ®, sizeof(reg), 0); + // Unregister channels. + reg = cpu_to_be32(0x00000000); + snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH, + ®, sizeof(reg), 0); + reg = cpu_to_be32(0x00000000); + snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN, + ®, sizeof(reg), 0); + reg = cpu_to_be32(0x00000000); + snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH, + ®, sizeof(reg), 0); } static int begin_session(struct snd_tscm *tscm) @@ -201,6 +213,30 @@ static int begin_session(struct snd_tscm *tscm) __be32 reg; int err; + // Register the isochronous channel for transmitting stream. + reg = cpu_to_be32(tscm->tx_resources.channel); + err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH, + ®, sizeof(reg), 0); + if (err < 0) + return err; + + // Unknown. + reg = cpu_to_be32(0x00000002); + err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN, + ®, sizeof(reg), 0); + if (err < 0) + return err; + + // Register the isochronous channel for receiving stream. + reg = cpu_to_be32(tscm->rx_resources.channel); + err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, + TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH, + ®, sizeof(reg), 0); + if (err < 0) + return err; + reg = cpu_to_be32(0x00000001); err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING, @@ -215,7 +251,7 @@ static int begin_session(struct snd_tscm *tscm) if (err < 0) return err; - /* Set an option for unknown purpose. */ + // Set an option for unknown purpose. reg = cpu_to_be32(0x00002000); err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION, @@ -223,7 +259,7 @@ static int begin_session(struct snd_tscm *tscm) if (err < 0) return err; - /* Start multiplexing PCM samples on packets. */ + // Start multiplexing PCM samples on packets. reg = cpu_to_be32(0x00000001); return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, @@ -231,82 +267,24 @@ static int begin_session(struct snd_tscm *tscm) ®, sizeof(reg), 0); } -static void release_resources(struct snd_tscm *tscm) +static int keep_resources(struct snd_tscm *tscm, unsigned int rate, + struct amdtp_stream *stream) { - __be32 reg; - - /* Unregister channels. */ - reg = cpu_to_be32(0x00000000); - snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, - TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH, - ®, sizeof(reg), 0); - reg = cpu_to_be32(0x00000000); - snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, - TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN, - ®, sizeof(reg), 0); - reg = cpu_to_be32(0x00000000); - snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, - TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH, - ®, sizeof(reg), 0); - - /* Release isochronous resources. */ - fw_iso_resources_free(&tscm->tx_resources); - fw_iso_resources_free(&tscm->rx_resources); -} - -static int keep_resources(struct snd_tscm *tscm, unsigned int rate) -{ - __be32 reg; + struct fw_iso_resources *resources; int err; - /* Keep resources for in-stream. */ - err = amdtp_tscm_set_parameters(&tscm->tx_stream, rate); - if (err < 0) - return err; - err = fw_iso_resources_allocate(&tscm->tx_resources, - amdtp_stream_get_max_payload(&tscm->tx_stream), - fw_parent_device(tscm->unit)->max_speed); - if (err < 0) - goto error; + if (stream == &tscm->tx_stream) + resources = &tscm->tx_resources; + else + resources = &tscm->rx_resources; - /* Keep resources for out-stream. */ - err = amdtp_tscm_set_parameters(&tscm->rx_stream, rate); - if (err < 0) - return err; - err = fw_iso_resources_allocate(&tscm->rx_resources, - amdtp_stream_get_max_payload(&tscm->rx_stream), - fw_parent_device(tscm->unit)->max_speed); + err = amdtp_tscm_set_parameters(stream, rate); if (err < 0) return err; - /* Register the isochronous channel for transmitting stream. */ - reg = cpu_to_be32(tscm->tx_resources.channel); - err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, - TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH, - ®, sizeof(reg), 0); - if (err < 0) - goto error; - - /* Unknown */ - reg = cpu_to_be32(0x00000002); - err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, - TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN, - ®, sizeof(reg), 0); - if (err < 0) - goto error; - - /* Register the isochronous channel for receiving stream. */ - reg = cpu_to_be32(tscm->rx_resources.channel); - err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST, - TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH, - ®, sizeof(reg), 0); - if (err < 0) - goto error; - - return 0; -error: - release_resources(tscm); - return err; + return fw_iso_resources_allocate(resources, + amdtp_stream_get_max_payload(stream), + fw_parent_device(tscm->unit)->max_speed); } int snd_tscm_stream_init_duplex(struct snd_tscm *tscm) @@ -345,7 +323,7 @@ int snd_tscm_stream_init_duplex(struct snd_tscm *tscm) return err; } -/* At bus reset, streaming is stopped and some registers are clear. */ +// At bus reset, streaming is stopped and some registers are clear. void snd_tscm_stream_update_duplex(struct snd_tscm *tscm) { amdtp_stream_pcm_abort(&tscm->tx_stream); @@ -368,33 +346,62 @@ void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm) fw_iso_resources_destroy(&tscm->tx_resources); } -int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate) +int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate) { unsigned int curr_rate; int err; - if (tscm->substreams_counter == 0) - return 0; - err = snd_tscm_stream_get_rate(tscm, &curr_rate); if (err < 0) return err; - if (curr_rate != rate || - amdtp_streaming_error(&tscm->rx_stream) || - amdtp_streaming_error(&tscm->tx_stream)) { + + if (tscm->substreams_counter == 0 || rate != curr_rate) { finish_session(tscm); - amdtp_stream_stop(&tscm->rx_stream); - amdtp_stream_stop(&tscm->tx_stream); + fw_iso_resources_free(&tscm->tx_resources); + fw_iso_resources_free(&tscm->rx_resources); - release_resources(tscm); + err = set_clock(tscm, rate, INT_MAX); + if (err < 0) + return err; + + err = keep_resources(tscm, rate, &tscm->tx_stream); + if (err < 0) + return err; + + err = keep_resources(tscm, rate, &tscm->rx_stream); + if (err < 0) { + fw_iso_resources_free(&tscm->tx_resources); + return err; + } } - if (!amdtp_stream_running(&tscm->rx_stream)) { - err = keep_resources(tscm, rate); + return 0; +} + +int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate) +{ + unsigned int generation = tscm->rx_resources.generation; + int err; + + if (tscm->substreams_counter == 0) + return 0; + + if (amdtp_streaming_error(&tscm->rx_stream) || + amdtp_streaming_error(&tscm->tx_stream)) + finish_session(tscm); + + if (generation != fw_parent_device(tscm->unit)->card->generation) { + err = fw_iso_resources_update(&tscm->tx_resources); + if (err < 0) + goto error; + + err = fw_iso_resources_update(&tscm->rx_resources); if (err < 0) goto error; + } + if (!amdtp_stream_running(&tscm->rx_stream)) { err = set_stream_formats(tscm, rate); if (err < 0) goto error; @@ -432,25 +439,19 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate) return 0; error: - amdtp_stream_stop(&tscm->rx_stream); - amdtp_stream_stop(&tscm->tx_stream); - finish_session(tscm); - release_resources(tscm); return err; } void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm) { - if (tscm->substreams_counter > 0) - return; - - amdtp_stream_stop(&tscm->tx_stream); - amdtp_stream_stop(&tscm->rx_stream); + if (tscm->substreams_counter == 0) { + finish_session(tscm); - finish_session(tscm); - release_resources(tscm); + fw_iso_resources_free(&tscm->tx_resources); + fw_iso_resources_free(&tscm->rx_resources); + } } void snd_tscm_stream_lock_changed(struct snd_tscm *tscm) diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index 1d003d4cf448..734e5bb9c3da 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -146,6 +146,7 @@ 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_start_duplex(struct snd_tscm *tscm, unsigned int rate); void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm); diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index b02f74528b66..3b0110545070 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -79,6 +79,8 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) snd_hdac_chip_writew(bus, RINTCNT, 1); /* enable rirb dma and response irq */ snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN); + /* Accept unsolicited responses */ + snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL); spin_unlock_irq(&bus->reg_lock); } EXPORT_SYMBOL_GPL(snd_hdac_bus_init_cmd_io); @@ -241,6 +243,8 @@ int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr, for (loopcounter = 0;; loopcounter++) { spin_lock_irq(&bus->reg_lock); + if (bus->polling_mode) + snd_hdac_bus_update_rirb(bus); if (!bus->rirb.cmds[addr]) { if (res) *res = bus->rirb.res[addr]; /* the last value */ @@ -415,9 +419,6 @@ int snd_hdac_bus_reset_link(struct hdac_bus *bus, bool full_reset) return -EBUSY; } - /* Accept unsolicited responses */ - snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL); - /* detect codecs */ if (!bus->codec_mask) { bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS); diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index 6907dbefd08c..b26cc93e7e10 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -90,7 +90,7 @@ int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus, fg = codec->afg ? codec->afg : codec->mfg; - err = snd_hdac_refresh_widgets(codec, false); + err = snd_hdac_refresh_widgets(codec); if (err < 0) goto error; @@ -395,32 +395,35 @@ static void setup_fg_nodes(struct hdac_device *codec) /** * snd_hdac_refresh_widgets - Reset the widget start/end nodes * @codec: the codec object - * @sysfs: re-initialize sysfs tree, too */ -int snd_hdac_refresh_widgets(struct hdac_device *codec, bool sysfs) +int snd_hdac_refresh_widgets(struct hdac_device *codec) { hda_nid_t start_nid; - int nums, err; + int nums, err = 0; + /* + * Serialize against multiple threads trying to update the sysfs + * widgets array. + */ + mutex_lock(&codec->widget_lock); nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid); if (!start_nid || nums <= 0 || nums >= 0xff) { dev_err(&codec->dev, "cannot read sub nodes for FG 0x%02x\n", codec->afg); - return -EINVAL; + err = -EINVAL; + goto unlock; } - if (sysfs) { - mutex_lock(&codec->widget_lock); - err = hda_widget_sysfs_reinit(codec, start_nid, nums); - mutex_unlock(&codec->widget_lock); - if (err < 0) - return err; - } + err = hda_widget_sysfs_reinit(codec, start_nid, nums); + if (err < 0) + goto unlock; codec->num_nodes = nums; codec->start_nid = start_nid; codec->end_nid = start_nid + nums; - return 0; +unlock: + mutex_unlock(&codec->widget_lock); + return err; } EXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets); diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index 1192c7561d62..3c2db3816029 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -136,10 +136,12 @@ int snd_hdac_i915_init(struct hdac_bus *bus) if (!acomp) return -ENODEV; if (!acomp->ops) { - request_module("i915"); - /* 60s timeout */ - wait_for_completion_timeout(&bind_complete, - msecs_to_jiffies(60 * 1000)); + if (!IS_ENABLED(CONFIG_MODULES) || + !request_module("i915")) { + /* 60s timeout */ + wait_for_completion_timeout(&bind_complete, + msecs_to_jiffies(60 * 1000)); + } } if (!acomp->ops) { dev_info(bus->dev, "couldn't bind with audio component\n"); diff --git a/sound/hda/hdac_sysfs.c b/sound/hda/hdac_sysfs.c index 909d5ef1179c..e56e83325903 100644 --- a/sound/hda/hdac_sysfs.c +++ b/sound/hda/hdac_sysfs.c @@ -428,7 +428,7 @@ int hda_widget_sysfs_reinit(struct hdac_device *codec, int i; if (!codec->widgets) - return hda_widget_sysfs_init(codec); + return 0; tree = kmemdup(codec->widgets, sizeof(*tree), GFP_KERNEL); if (!tree) diff --git a/sound/oss/dmasound/Kconfig b/sound/oss/dmasound/Kconfig index 12e42165b4a5..1a3339859840 100644 --- a/sound/oss/dmasound/Kconfig +++ b/sound/oss/dmasound/Kconfig @@ -11,7 +11,7 @@ config DMASOUND_ATARI This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - <file:Documentation/kbuild/modules.txt>. + <file:Documentation/kbuild/modules.rst>. config DMASOUND_PAULA tristate "Amiga DMA sound support" @@ -25,7 +25,7 @@ config DMASOUND_PAULA This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - <file:Documentation/kbuild/modules.txt>. + <file:Documentation/kbuild/modules.rst>. config DMASOUND_Q40 tristate "Q40 sound support" @@ -39,7 +39,7 @@ config DMASOUND_Q40 This driver is also available as a module ( = code which can be inserted in and removed from the running kernel whenever you want). If you want to compile it as a module, say M here and read - <file:Documentation/kbuild/modules.txt>. + <file:Documentation/kbuild/modules.rst>. config DMASOUND tristate diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index e7234f3d99e2..2a21a3d99719 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -1519,7 +1519,6 @@ static int snd_asihpi_volume_get(struct snd_kcontrol *kcontrol, static int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - int change; u32 h_control = kcontrol->private_value; short an_gain_mB[HPI_MAX_CHANNELS]; @@ -1530,9 +1529,8 @@ static int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol, /* change = asihpi->mixer_volume[addr][0] != left || asihpi->mixer_volume[addr][1] != right; */ - change = 1; hpi_handle_error(hpi_volume_set_gain(h_control, an_gain_mB)); - return change; + return 1; } static const DECLARE_TLV_DB_SCALE(db_scale_100, -10000, VOL_STEP_mB, 0); @@ -1555,13 +1553,12 @@ static int snd_asihpi_volume_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { u32 h_control = kcontrol->private_value; - int change = 1; /* HPI currently only supports all or none muting of multichannel volume ALSA Switch element has opposite sense to HPI mute: on==unmuted, off=muted */ int mute = ucontrol->value.integer.value[0] ? 0 : HPI_BITMASK_ALL_CHANNELS; hpi_handle_error(hpi_volume_set_mute(h_control, mute)); - return change; + return 1; } static int snd_asihpi_volume_add(struct snd_card_asihpi *asihpi, diff --git a/sound/pci/au88x0/au88x0_a3d.c b/sound/pci/au88x0/au88x0_a3d.c index 73471037d59f..2db183f8826a 100644 --- a/sound/pci/au88x0/au88x0_a3d.c +++ b/sound/pci/au88x0/au88x0_a3d.c @@ -765,7 +765,7 @@ snd_vortex_a3d_hrtf_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { a3dsrc_t *a = kcontrol->private_data; - int changed = 1, i; + int i; int coord[6]; for (i = 0; i < 6; i++) coord[i] = ucontrol->value.integer.value[i]; @@ -774,7 +774,7 @@ snd_vortex_a3d_hrtf_put(struct snd_kcontrol *kcontrol, vortex_a3d_coord2hrtf(a->hrtf[1], coord); a3dsrc_SetHrtfTarget(a, a->hrtf[0], a->hrtf[1]); a3dsrc_SetHrtfCurrent(a, a->hrtf[0], a->hrtf[1]); - return changed; + return 1; } static int @@ -783,7 +783,7 @@ snd_vortex_a3d_itd_put(struct snd_kcontrol *kcontrol, { a3dsrc_t *a = kcontrol->private_data; int coord[6]; - int i, changed = 1; + int i; for (i = 0; i < 6; i++) coord[i] = ucontrol->value.integer.value[i]; /* Translate orientation coordinates to a3d params. */ @@ -793,7 +793,7 @@ snd_vortex_a3d_itd_put(struct snd_kcontrol *kcontrol, a3dsrc_SetItdTarget(a, a->itd[0], a->itd[1]); a3dsrc_SetItdCurrent(a, a->itd[0], a->itd[1]); a3dsrc_SetItdDline(a, a->dline); - return changed; + return 1; } static int @@ -801,7 +801,6 @@ snd_vortex_a3d_ild_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { a3dsrc_t *a = kcontrol->private_data; - int changed = 1; int l, r; /* There may be some scale tranlation needed here. */ l = ucontrol->value.integer.value[0]; @@ -810,7 +809,7 @@ snd_vortex_a3d_ild_put(struct snd_kcontrol *kcontrol, /* Left Right panning. */ a3dsrc_SetGainTarget(a, l, r); a3dsrc_SetGainCurrent(a, l, r); - return changed; + return 1; } static int @@ -818,7 +817,7 @@ snd_vortex_a3d_filter_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { a3dsrc_t *a = kcontrol->private_data; - int i, changed = 1; + int i; int params[6]; for (i = 0; i < 6; i++) params[i] = ucontrol->value.integer.value[i]; @@ -831,7 +830,7 @@ snd_vortex_a3d_filter_put(struct snd_kcontrol *kcontrol, a3dsrc_SetAtmosCurrent(a, a->filter[0], a->filter[1], a->filter[2], a->filter[3], a->filter[4]); - return changed; + return 1; } static const struct snd_kcontrol_new vortex_a3d_kcontrol = { diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index a2cce3ecda6f..04c712647853 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -694,7 +694,7 @@ static int snd_cs4281_trigger(struct snd_pcm_substream *substream, int cmd) static unsigned int snd_cs4281_rate(unsigned int rate, unsigned int *real_rate) { - unsigned int val = ~0; + unsigned int val; if (real_rate) *real_rate = rate; @@ -707,9 +707,8 @@ static unsigned int snd_cs4281_rate(unsigned int rate, unsigned int *real_rate) case 44100: return 1; case 48000: return 0; default: - goto __variable; + break; } - __variable: val = 1536000 / rate; if (real_rate) *real_rate = 1536000 / val; diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c index b181752b8481..50d4a87a6bb3 100644 --- a/sound/pci/echoaudio/echoaudio_dsp.c +++ b/sound/pci/echoaudio/echoaudio_dsp.c @@ -1058,7 +1058,6 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe, { int i; u32 channel_mask; - char is_cyclic; dev_dbg(chip->card->dev, "allocate_pipes: ch=%d int=%d\n", pipe_index, interleave); @@ -1066,8 +1065,6 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe, if (chip->bad_board) return -EIO; - is_cyclic = 1; /* This driver uses cyclic buffers only */ - for (channel_mask = i = 0; i < interleave; i++) channel_mask |= 1 << (pipe_index + i); if (chip->pipe_alloc_mask & channel_mask) { @@ -1078,8 +1075,8 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe, chip->comm_page->position[pipe_index] = 0; chip->pipe_alloc_mask |= channel_mask; - if (is_cyclic) - chip->pipe_cyclic_mask |= channel_mask; + /* This driver uses cyclic buffers only */ + chip->pipe_cyclic_mask |= channel_mask; pipe->index = pipe_index; pipe->interleave = interleave; pipe->state = PIPE_STATE_STOPPED; diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 67d6473ab0cd..9cf81832259c 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -1074,7 +1074,6 @@ static int snd_emu10k1x_shared_spdif_put(struct snd_kcontrol *kcontrol, { struct emu10k1x *emu = snd_kcontrol_chip(kcontrol); unsigned int val; - int change = 0; val = ucontrol->value.integer.value[0] ; @@ -1089,7 +1088,7 @@ static int snd_emu10k1x_shared_spdif_put(struct snd_kcontrol *kcontrol, snd_emu10k1x_ptr_write(emu, ROUTING, 0, 0x1003F); snd_emu10k1x_gpio_write(emu, 0x1080); } - return change; + return 0; } static const struct snd_kcontrol_new snd_emu10k1x_shared_spdif = diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 92390d457567..18e6546b4467 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -824,6 +824,8 @@ static void apply_fixup(struct hda_codec *codec, int id, int action, int depth) while (id >= 0) { const struct hda_fixup *fix = codec->fixup_list + id; + if (++depth > 10) + break; if (fix->chained_before) apply_fixup(codec, fix->chain_id, action, depth + 1); @@ -863,8 +865,6 @@ static void apply_fixup(struct hda_codec *codec, int id, int action, int depth) } if (!fix->chained || fix->chained_before) break; - if (++depth > 10) - break; id = fix->chain_id; } } diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 6c51b8363f8b..51f10ed9bc43 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -108,7 +108,7 @@ static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len, { struct hda_conn_list *p; - p = kmalloc(sizeof(*p) + len * sizeof(hda_nid_t), GFP_KERNEL); + p = kmalloc(struct_size(p, conns, len), GFP_KERNEL); if (!p) return -ENOMEM; p->len = len; @@ -1002,7 +1002,7 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec) hda_nid_t fg; int err; - err = snd_hdac_refresh_widgets(&codec->core, true); + err = snd_hdac_refresh_widgets(&codec->core); if (err < 0) return err; @@ -2941,15 +2941,19 @@ static int hda_codec_runtime_resume(struct device *dev) #ifdef CONFIG_PM_SLEEP static int hda_codec_force_resume(struct device *dev) { + struct hda_codec *codec = dev_to_hda_codec(dev); + bool forced_resume = !codec->relaxed_resume && codec->jacktbl.used; int ret; /* The get/put pair below enforces the runtime resume even if the * device hasn't been used at suspend time. This trick is needed to * update the jack state change during the sleep. */ - pm_runtime_get_noresume(dev); + if (forced_resume) + pm_runtime_get_noresume(dev); ret = pm_runtime_force_resume(dev); - pm_runtime_put(dev); + if (forced_resume) + pm_runtime_put(dev); return ret; } diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 232a1926758a..48d863736b3c 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -598,11 +598,9 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) } runtime->private_data = azx_dev; - if (chip->gts_present) - azx_pcm_hw.info = azx_pcm_hw.info | - SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME; - runtime->hw = azx_pcm_hw; + if (chip->gts_present) + runtime->hw.info |= SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME; runtime->hw.channels_min = hinfo->channels_min; runtime->hw.channels_max = hinfo->channels_max; runtime->hw.formats = hinfo->formats; @@ -615,6 +613,13 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) 20, 178000000); + /* by some reason, the playback stream stalls on PulseAudio with + * tsched=1 when a capture stream triggers. Until we figure out the + * real cause, disable tsched mode by telling the PCM info flag. + */ + if (chip->driver_caps & AZX_DCAPS_AMD_WORKAROUND) + runtime->hw.info |= SNDRV_PCM_INFO_BATCH; + if (chip->align_buffer_size) /* constrain buffer sizes to be multiple of 128 bytes. This is more efficient in terms of memory @@ -795,11 +800,11 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr, for (loopcounter = 0;; loopcounter++) { spin_lock_irq(&bus->reg_lock); - if (chip->polling_mode || do_poll) + if (bus->polling_mode || do_poll) snd_hdac_bus_update_rirb(bus); if (!bus->rirb.cmds[addr]) { if (!do_poll) - chip->poll_count = 0; + bus->poll_count = 0; if (res) *res = bus->rirb.res[addr]; /* the last value */ spin_unlock_irq(&bus->reg_lock); @@ -819,21 +824,21 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr, if (hbus->no_response_fallback) return -EIO; - if (!chip->polling_mode && chip->poll_count < 2) { + if (!bus->polling_mode && bus->poll_count < 2) { dev_dbg(chip->card->dev, "azx_get_response timeout, polling the codec once: last cmd=0x%08x\n", bus->last_cmd[addr]); do_poll = 1; - chip->poll_count++; + bus->poll_count++; goto again; } - if (!chip->polling_mode) { + if (!bus->polling_mode) { dev_warn(chip->card->dev, "azx_get_response timeout, switching to polling mode: last cmd=0x%08x\n", bus->last_cmd[addr]); - chip->polling_mode = 1; + bus->polling_mode = 1; goto again; } diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index 3aa5c957ffbf..f2a6df5e6bcb 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -31,7 +31,7 @@ /* 14 unused */ #define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */ #define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */ -/* 17 unused */ +#define AZX_DCAPS_AMD_WORKAROUND (1 << 17) /* AMD-specific workaround */ #define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */ #define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */ #define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */ @@ -133,11 +133,9 @@ struct azx { /* flags */ int bdl_pos_adj; - int poll_count; unsigned int running:1; unsigned int fallback_to_single_cmd:1; unsigned int single_cmd:1; - unsigned int polling_mode:1; unsigned int msi:1; unsigned int probing:1; /* codec probing phase */ unsigned int snoop:1; diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 485edaba0037..10d502328b76 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -6009,7 +6009,8 @@ int snd_hda_gen_init(struct hda_codec *codec) if (spec->init_hook) spec->init_hook(codec); - snd_hda_apply_verbs(codec); + if (!spec->skip_verbs) + snd_hda_apply_verbs(codec); init_multi_out(codec); init_extra_out(codec); @@ -6051,6 +6052,24 @@ void snd_hda_gen_free(struct hda_codec *codec) } EXPORT_SYMBOL_GPL(snd_hda_gen_free); +/** + * snd_hda_gen_reboot_notify - Make codec enter D3 before rebooting + * @codec: the HDA codec + * + * This can be put as patch_ops reboot_notify function. + */ +void snd_hda_gen_reboot_notify(struct hda_codec *codec) +{ + /* Make the codec enter D3 to avoid spurious noises from the internal + * speaker during (and after) reboot + */ + snd_hda_codec_set_power_to_all(codec, codec->core.afg, AC_PWRST_D3); + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); + msleep(10); +} +EXPORT_SYMBOL_GPL(snd_hda_gen_reboot_notify); + #ifdef CONFIG_PM /** * snd_hda_gen_check_power_status - check the loopback power save state @@ -6078,6 +6097,7 @@ static const struct hda_codec_ops generic_patch_ops = { .init = snd_hda_gen_init, .free = snd_hda_gen_free, .unsol_event = snd_hda_jack_unsol_event, + .reboot_notify = snd_hda_gen_reboot_notify, #ifdef CONFIG_PM .check_power_status = snd_hda_gen_check_power_status, #endif @@ -6100,7 +6120,7 @@ static int snd_hda_parse_generic_codec(struct hda_codec *codec) err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0); if (err < 0) - return err; + goto error; err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg); if (err < 0) diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 35a670a71c42..fb9f1a90238b 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -243,6 +243,7 @@ struct hda_gen_spec { unsigned int indep_hp_enabled:1; /* independent HP enabled */ unsigned int have_aamix_ctl:1; unsigned int hp_mic_jack_modes:1; + unsigned int skip_verbs:1; /* don't apply verbs at snd_hda_gen_init() */ /* additional mute flags (only effective with auto_mute_via_amp=1) */ u64 mute_bits; @@ -332,6 +333,7 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, struct auto_pin_cfg *cfg); int snd_hda_gen_build_controls(struct hda_codec *codec); int snd_hda_gen_build_pcms(struct hda_codec *codec); +void snd_hda_gen_reboot_notify(struct hda_codec *codec); /* standard jack event callbacks */ void snd_hda_gen_hp_automute(struct hda_codec *codec, diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 50f86f458918..99fc0917339b 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -64,6 +64,7 @@ enum { POS_FIX_VIACOMBO, POS_FIX_COMBO, POS_FIX_SKL, + POS_FIX_FIFO, }; /* Defines for ATI HD Audio support in SB450 south bridge */ @@ -135,7 +136,7 @@ module_param_array(model, charp, NULL, 0444); MODULE_PARM_DESC(model, "Use the given board model."); module_param_array(position_fix, int, NULL, 0444); MODULE_PARM_DESC(position_fix, "DMA pointer read method." - "(-1 = system default, 0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO, 5 = SKL+)."); + "(-1 = system default, 0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO, 5 = SKL+, 6 = FIFO)."); module_param_array(bdl_pos_adj, int, NULL, 0644); MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset."); module_param_array(probe_mask, int, NULL, 0444); @@ -313,11 +314,10 @@ enum { #define AZX_DCAPS_INTEL_SKYLAKE \ (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME |\ + AZX_DCAPS_SYNC_WRITE |\ AZX_DCAPS_SEPARATE_STREAM_TAG | AZX_DCAPS_I915_COMPONENT) -#define AZX_DCAPS_INTEL_BROXTON \ - (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME |\ - AZX_DCAPS_SEPARATE_STREAM_TAG | AZX_DCAPS_I915_COMPONENT) +#define AZX_DCAPS_INTEL_BROXTON AZX_DCAPS_INTEL_SKYLAKE /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ @@ -333,6 +333,11 @@ enum { #define AZX_DCAPS_PRESET_ATI_HDMI_NS \ (AZX_DCAPS_PRESET_ATI_HDMI | AZX_DCAPS_SNOOP_OFF) +/* quirks for AMD SB */ +#define AZX_DCAPS_PRESET_AMD_SB \ + (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_AMD_WORKAROUND |\ + AZX_DCAPS_SNOOP_TYPE(ATI) | AZX_DCAPS_PM_RUNTIME) + /* quirks for Nvidia */ #define AZX_DCAPS_PRESET_NVIDIA \ (AZX_DCAPS_NO_MSI | AZX_DCAPS_CORBRP_SELF_CLEAR |\ @@ -842,6 +847,49 @@ static unsigned int azx_via_get_position(struct azx *chip, return bound_pos + mod_dma_pos; } +#define AMD_FIFO_SIZE 32 + +/* get the current DMA position with FIFO size correction */ +static unsigned int azx_get_pos_fifo(struct azx *chip, struct azx_dev *azx_dev) +{ + struct snd_pcm_substream *substream = azx_dev->core.substream; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int pos, delay; + + pos = snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev)); + if (!runtime) + return pos; + + runtime->delay = AMD_FIFO_SIZE; + delay = frames_to_bytes(runtime, AMD_FIFO_SIZE); + if (azx_dev->insufficient) { + if (pos < delay) { + delay = pos; + runtime->delay = bytes_to_frames(runtime, pos); + } else { + azx_dev->insufficient = 0; + } + } + + /* correct the DMA position for capture stream */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + if (pos < delay) + pos += azx_dev->core.bufsize; + pos -= delay; + } + + return pos; +} + +static int azx_get_delay_from_fifo(struct azx *chip, struct azx_dev *azx_dev, + unsigned int pos) +{ + struct snd_pcm_substream *substream = azx_dev->core.substream; + + /* just read back the calculated value in the above */ + return substream->runtime->delay; +} + static unsigned int azx_skl_get_dpib_pos(struct azx *chip, struct azx_dev *azx_dev) { @@ -1418,6 +1466,7 @@ static int check_position_fix(struct azx *chip, int fix) case POS_FIX_VIACOMBO: case POS_FIX_COMBO: case POS_FIX_SKL: + case POS_FIX_FIFO: return fix; } @@ -1434,6 +1483,10 @@ static int check_position_fix(struct azx *chip, int fix) dev_dbg(chip->card->dev, "Using VIACOMBO position fix\n"); return POS_FIX_VIACOMBO; } + if (chip->driver_caps & AZX_DCAPS_AMD_WORKAROUND) { + dev_dbg(chip->card->dev, "Using FIFO position fix\n"); + return POS_FIX_FIFO; + } if (chip->driver_caps & AZX_DCAPS_POSFIX_LPIB) { dev_dbg(chip->card->dev, "Using LPIB position fix\n"); return POS_FIX_LPIB; @@ -1454,6 +1507,7 @@ static void assign_position_fix(struct azx *chip, int fix) [POS_FIX_VIACOMBO] = azx_via_get_position, [POS_FIX_COMBO] = azx_get_pos_lpib, [POS_FIX_SKL] = azx_get_pos_skl, + [POS_FIX_FIFO] = azx_get_pos_fifo, }; chip->get_position[0] = chip->get_position[1] = callbacks[fix]; @@ -1468,6 +1522,9 @@ static void assign_position_fix(struct azx *chip, int fix) azx_get_delay_from_lpib; } + if (fix == POS_FIX_FIFO) + chip->get_delay[0] = chip->get_delay[1] = + azx_get_delay_from_fifo; } /* @@ -1687,10 +1744,6 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, else chip->bdl_pos_adj = bdl_pos_adj[dev]; - /* Workaround for a communication error on CFL (bko#199007) and CNL */ - if (IS_CFL(pci) || IS_CNL(pci)) - chip->polling_mode = 1; - err = azx_bus_init(chip, model[dev], &pci_hda_io_ops); if (err < 0) { kfree(hda); @@ -1698,6 +1751,10 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, return err; } + /* Workaround for a communication error on CFL (bko#199007) and CNL */ + if (IS_CFL(pci) || IS_CNL(pci)) + azx_bus(chip)->polling_mode = 1; + if (chip->driver_type == AZX_DRIVER_NVIDIA) { dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n"); chip->bus.needs_damn_long_delay = 1; @@ -2374,6 +2431,9 @@ static const struct pci_device_id azx_ids[] = { /* Icelake */ { PCI_DEVICE(0x8086, 0x34c8), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + /* Elkhart Lake */ + { PCI_DEVICE(0x8086, 0x4b55), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, /* Broxton-P(Apollolake) */ { PCI_DEVICE(0x8086, 0x5a98), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON }, @@ -2445,6 +2505,12 @@ static const struct pci_device_id azx_ids[] = { /* AMD Hudson */ { PCI_DEVICE(0x1022, 0x780d), .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB }, + /* AMD, X370 & co */ + { PCI_DEVICE(0x1022, 0x1457), + .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB }, + /* AMD, X570 & co */ + { PCI_DEVICE(0x1022, 0x1487), + .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB }, /* AMD Stoney */ { PCI_DEVICE(0x1022, 0x157a), .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB | diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 6d9acd5c4e41..1fb7b06457ae 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -559,7 +559,7 @@ static void call_jack_callback(struct hda_codec *codec, unsigned int res, void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) { struct hda_jack_tbl *event; - int tag = (res >> AC_UNSOL_RES_TAG_SHIFT) & 0x7f; + int tag = (res & AC_UNSOL_RES_TAG) >> AC_UNSOL_RES_TAG_SHIFT; event = snd_hda_jack_tbl_get_from_tag(codec, tag); if (!event) diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index c3096796ee05..6d1fb7c11f17 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1175,6 +1175,7 @@ static const struct snd_pci_quirk ca0132_quirks[] = { SND_PCI_QUIRK(0x1028, 0x0708, "Alienware 15 R2 2016", QUIRK_ALIENWARE), SND_PCI_QUIRK(0x1102, 0x0010, "Sound Blaster Z", QUIRK_SBZ), SND_PCI_QUIRK(0x1102, 0x0023, "Sound Blaster Z", QUIRK_SBZ), + SND_PCI_QUIRK(0x1102, 0x0027, "Sound Blaster Z", QUIRK_SBZ), SND_PCI_QUIRK(0x1102, 0x0033, "Sound Blaster ZxR", QUIRK_SBZ), SND_PCI_QUIRK(0x1458, 0xA016, "Recon3Di", QUIRK_R3DI), SND_PCI_QUIRK(0x1458, 0xA026, "Gigabyte G1.Sniper Z97", QUIRK_R3DI), @@ -2718,7 +2719,7 @@ static bool is_last(const struct dsp_image_seg *p) static size_t dsp_sizeof(const struct dsp_image_seg *p) { - return sizeof(*p) + p->count*sizeof(u32); + return struct_size(p, data, p->count); } static const struct dsp_image_seg *get_next_seg_ptr( @@ -5980,7 +5981,7 @@ static int ca0132_alt_volume_put(struct snd_kcontrol *kcontrol, int ch = get_amp_channels(kcontrol); long *valp = ucontrol->value.integer.value; hda_nid_t vnid = 0; - int changed = 1; + int changed; switch (nid) { case 0x02: diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 4f8d0845ee1e..968d3caab6ac 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -163,23 +163,10 @@ static void cx_auto_reboot_notify(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - switch (codec->core.vendor_id) { - case 0x14f12008: /* CX8200 */ - case 0x14f150f2: /* CX20722 */ - case 0x14f150f4: /* CX20724 */ - break; - default: - return; - } - /* Turn the problematic codec into D3 to avoid spurious noises from the internal speaker during (and after) reboot */ cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false); - - snd_hda_codec_set_power_to_all(codec, codec->core.afg, AC_PWRST_D3); - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - msleep(10); + snd_hda_gen_reboot_notify(codec); } static void cx_auto_free(struct hda_codec *codec) @@ -624,18 +611,20 @@ static void cxt_fixup_hp_gate_mic_jack(struct hda_codec *codec, /* update LED status via GPIO */ static void cxt_update_gpio_led(struct hda_codec *codec, unsigned int mask, - bool enabled) + bool led_on) { struct conexant_spec *spec = codec->spec; unsigned int oldval = spec->gpio_led; if (spec->mute_led_polarity) - enabled = !enabled; + led_on = !led_on; - if (enabled) - spec->gpio_led &= ~mask; - else + if (led_on) spec->gpio_led |= mask; + else + spec->gpio_led &= ~mask; + codec_dbg(codec, "mask:%d enabled:%d gpio_led:%d\n", + mask, led_on, spec->gpio_led); if (spec->gpio_led != oldval) snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_led); @@ -646,8 +635,8 @@ static void cxt_fixup_gpio_mute_hook(void *private_data, int enabled) { struct hda_codec *codec = private_data; struct conexant_spec *spec = codec->spec; - - cxt_update_gpio_led(codec, spec->gpio_mute_led_mask, enabled); + /* muted -> LED on */ + cxt_update_gpio_led(codec, spec->gpio_mute_led_mask, !enabled); } /* turn on/off mic-mute LED via GPIO per capture hook */ @@ -669,7 +658,6 @@ static void cxt_fixup_mute_led_gpio(struct hda_codec *codec, { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03 }, {} }; - codec_info(codec, "action: %d gpio_led: %d\n", action, spec->gpio_led); if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->gen.vmaster_mute.hook = cxt_fixup_gpio_mute_hook; @@ -1083,6 +1071,7 @@ static int patch_conexant_auto(struct hda_codec *codec) */ static const struct hda_device_id snd_hda_id_conexant[] = { + HDA_CODEC_ENTRY(0x14f11f86, "CX8070", patch_conexant_auto), HDA_CODEC_ENTRY(0x14f12008, "CX8200", patch_conexant_auto), HDA_CODEC_ENTRY(0x14f15045, "CX20549 (Venice)", patch_conexant_auto), HDA_CODEC_ENTRY(0x14f15047, "CX20551 (Waikiki)", patch_conexant_auto), diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index b7bde55b6adf..bea7b0961080 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1614,7 +1614,8 @@ static void sync_eld_via_acomp(struct hda_codec *codec, if (jack == NULL) goto unlock; snd_jack_report(jack, - eld->monitor_present ? SND_JACK_AVOUT : 0); + (eld->monitor_present && eld->eld_valid) ? + SND_JACK_AVOUT : 0); unlock: mutex_unlock(&per_pin->lock); } @@ -2291,8 +2292,10 @@ static void generic_hdmi_free(struct hda_codec *codec) struct hdmi_spec *spec = codec->spec; int pin_idx, pcm_idx; - if (codec_has_acomp(codec)) + if (codec_has_acomp(codec)) { snd_hdac_acomp_register_notifier(&codec->bus->core, NULL); + codec->relaxed_resume = 0; + } for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); @@ -2416,7 +2419,6 @@ static void intel_haswell_fixup_connect_list(struct hda_codec *codec, } #define INTEL_GET_VENDOR_VERB 0xf81 -#define INTEL_GET_VENDOR_VERB 0xf81 #define INTEL_SET_VENDOR_VERB 0x781 #define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ #define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */ @@ -2524,18 +2526,32 @@ static int intel_pin2port(void *audio_ptr, int pin_nid) return -1; } +static int intel_port2pin(struct hda_codec *codec, int port) +{ + struct hdmi_spec *spec = codec->spec; + + if (!spec->port_num) { + /* we assume only from port-B to port-D */ + if (port < 1 || port > 3) + return 0; + /* intel port is 1-based */ + return port + intel_base_nid(codec) - 1; + } + + if (port < 1 || port > spec->port_num) + return 0; + return spec->port_map[port - 1]; +} + static void intel_pin_eld_notify(void *audio_ptr, int port, int pipe) { struct hda_codec *codec = audio_ptr; int pin_nid; int dev_id = pipe; - /* we assume only from port-B to port-D */ - if (port < 1 || port > 3) + pin_nid = intel_port2pin(codec, port); + if (!pin_nid) return; - - pin_nid = port + intel_base_nid(codec) - 1; /* intel port is 1-based */ - /* skip notification during system suspend (but not in runtime PM); * the state will be updated at resume */ @@ -2565,6 +2581,8 @@ static void register_i915_notifier(struct hda_codec *codec) spec->drm_audio_ops.pin_eld_notify = intel_pin_eld_notify; snd_hdac_acomp_register_notifier(&codec->bus->core, &spec->drm_audio_ops); + /* no need for forcible resume for jack check thanks to notifier */ + codec->relaxed_resume = 1; } /* setup_stream ops override for HSW+ */ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 5b3c26991f26..c1ddfd2fac52 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -837,9 +837,11 @@ static int alc_init(struct hda_codec *codec) if (spec->init_hook) spec->init_hook(codec); + spec->gen.skip_verbs = 1; /* applied in below */ snd_hda_gen_init(codec); alc_fix_pll(codec); alc_auto_init_amp(codec, spec->init_amp); + snd_hda_apply_verbs(codec); /* apply verbs here after own init */ snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); @@ -869,15 +871,6 @@ static void alc_reboot_notify(struct hda_codec *codec) alc_shutup(codec); } -/* power down codec to D3 at reboot/shutdown; set as reboot_notify ops */ -static void alc_d3_at_reboot(struct hda_codec *codec) -{ - snd_hda_codec_set_power_to_all(codec, codec->core.afg, AC_PWRST_D3); - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_POWER_STATE, AC_PWRST_D3); - msleep(10); -} - #define alc_free snd_hda_gen_free #ifdef CONFIG_PM @@ -2448,9 +2441,10 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x9501, "Clevo P950HR", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1558, 0x95e1, "Clevo P95xER", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1558, 0x95e2, "Clevo P950ER", ALC1220_FIXUP_CLEVO_P950), - SND_PCI_QUIRK(0x1558, 0x96e1, "System76 Oryx Pro (oryp5)", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x97e1, "System76 Oryx Pro (oryp5)", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x65d1, "Tuxedo Book XC1509", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x96e1, "Clevo P960[ER][CDFN]-K", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x97e1, "Clevo P970[ER][CDFN]", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x65d1, "Clevo PB51[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x67d1, "Clevo PB71[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD), SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530), @@ -3254,6 +3248,7 @@ static void alc256_init(struct hda_codec *codec) alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */ alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 1 << 15); /* Clear bit */ alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 0 << 15); + alc_update_coef_idx(codec, 0x36, 1 << 13, 1 << 5); /* Switch pcbeep path to Line in path*/ } static void alc256_shutup(struct hda_codec *codec) @@ -5150,7 +5145,7 @@ static void alc_fixup_tpt440_dock(struct hda_codec *codec, struct alc_spec *spec = codec->spec; if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->reboot_notify = alc_d3_at_reboot; /* reduce noise */ + spec->reboot_notify = snd_hda_gen_reboot_notify; /* reduce noise */ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; codec->power_save_node = 0; /* avoid click noises */ snd_hda_apply_pincfgs(codec, pincfgs); @@ -5804,6 +5799,7 @@ enum { ALC286_FIXUP_ACER_AIO_HEADSET_MIC, ALC256_FIXUP_ASUS_MIC_NO_PRESENCE, ALC299_FIXUP_PREDATOR_SPK, + ALC294_FIXUP_ASUS_INTSPK_HEADSET_MIC, }; static const struct hda_fixup alc269_fixups[] = { @@ -6844,6 +6840,16 @@ static const struct hda_fixup alc269_fixups[] = { { } } }, + [ALC294_FIXUP_ASUS_INTSPK_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x411111f0 }, /* disable confusing internal speaker */ + { 0x19, 0x04a11150 }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -6985,6 +6991,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x82bf, "HP G3 mini", ALC221_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x82c0, "HP G3 mini premium", ALC221_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x83b9, "HP Spectre x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), + SND_PCI_QUIRK(0x103c, 0x8497, "HP Envy x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), + SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), @@ -7001,6 +7009,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK), SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A), SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x1043, 0x17d1, "ASUS UX431FL", ALC294_FIXUP_ASUS_INTSPK_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), SND_PCI_QUIRK(0x1043, 0x1a30, "ASUS X705UD", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC), @@ -7074,9 +7083,11 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), + SND_PCI_QUIRK(0x17aa, 0x3111, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), SND_PCI_QUIRK(0x17aa, 0x312a, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), SND_PCI_QUIRK(0x17aa, 0x312f, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), SND_PCI_QUIRK(0x17aa, 0x313c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), + SND_PCI_QUIRK(0x17aa, 0x3151, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI), @@ -7654,9 +7665,12 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x12, 0x90a60130}, {0x17, 0x90170110}, {0x21, 0x03211020}), - SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, {0x14, 0x90170110}, {0x21, 0x04211020}), + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, + {0x14, 0x90170110}, + {0x21, 0x04211030}), SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, ALC295_STANDARD_PINS, {0x17, 0x21014020}, @@ -7823,7 +7837,6 @@ static int patch_alc269(struct hda_codec *codec) spec->shutup = alc256_shutup; spec->init_hook = alc256_init; spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */ - alc_update_coef_idx(codec, 0x36, 1 << 13, 1 << 5); /* Switch pcbeep path to Line in path*/ break; case 0x10ec0257: spec->codec_variant = ALC269_TYPE_ALC257; @@ -8798,6 +8811,11 @@ static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = { {0x18, 0x01a19030}, {0x1a, 0x01813040}, {0x21, 0x01014020}), + SND_HDA_PIN_QUIRK(0x10ec0867, 0x1028, "Dell", ALC891_FIXUP_DELL_MIC_NO_PRESENCE, + {0x16, 0x01813030}, + {0x17, 0x02211010}, + {0x18, 0x01a19040}, + {0x21, 0x01014020}), SND_HDA_PIN_QUIRK(0x10ec0662, 0x1028, "Dell", ALC662_FIXUP_DELL_MIC_NO_PRESENCE, {0x14, 0x01014010}, {0x18, 0x01a19020}, @@ -8944,6 +8962,7 @@ static int patch_alc680(struct hda_codec *codec) static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0215, "ALC215", patch_alc269), HDA_CODEC_ENTRY(0x10ec0221, "ALC221", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0222, "ALC222", patch_alc269), HDA_CODEC_ENTRY(0x10ec0225, "ALC225", patch_alc269), HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269), HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269), diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index 1771a6dcbe18..583ca7384d83 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -253,9 +253,8 @@ exit: static int lx_pcm_close(struct snd_pcm_substream *substream) { - int err = 0; dev_dbg(substream->pcm->card->dev, "->lx_pcm_close\n"); - return err; + return 0; } static snd_pcm_uframes_t lx_pcm_stream_pointer(struct snd_pcm_substream diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c index 9236a1a8c49b..dd3a873777eb 100644 --- a/sound/pci/lx6464es/lx_core.c +++ b/sound/pci/lx6464es/lx_core.c @@ -986,8 +986,6 @@ static int lx_interrupt_handle_async_events(struct lx6464es *chip, u32 irqsrc, * Stat[8] LSB overrun * */ - u64 orun_mask; - u64 urun_mask; int eb_pending_out = (irqsrc & MASK_SYS_STATUS_EOBO) ? 1 : 0; int eb_pending_in = (irqsrc & MASK_SYS_STATUS_EOBI) ? 1 : 0; @@ -1010,9 +1008,6 @@ static int lx_interrupt_handle_async_events(struct lx6464es *chip, u32 irqsrc, *r_notified_out_pipe_mask); } - orun_mask = ((u64)stat[7] << 32) + stat[8]; - urun_mask = ((u64)stat[5] << 32) + stat[6]; - /* todo: handle xrun notification */ return err; diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 6f5eaa510bbc..81a6f4b2bd3c 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -23,6 +23,9 @@ * Modified 2011-01-14 added S/PDIF input on RayDATs by Adrian Knoth * * Modified 2011-01-25 variable period sizes on RayDAT/AIO by Adrian Knoth + * + * Modified 2019-05-23 fix AIO single speed ADAT capture and playback + * by Philippe.Bekaert@uhasselt.be */ /* ************* Register Documentation ******************************************************* @@ -1091,9 +1094,9 @@ static int hdspm_autosync_ref(struct hdspm *hdspm); static int hdspm_set_toggle_setting(struct hdspm *hdspm, u32 regmask, int out); static int snd_hdspm_set_defaults(struct hdspm *hdspm); static int hdspm_system_clock_mode(struct hdspm *hdspm); -static void hdspm_set_sgbuf(struct hdspm *hdspm, - struct snd_pcm_substream *substream, - unsigned int reg, int channels); +static void hdspm_set_channel_dma_addr(struct hdspm *hdspm, + struct snd_pcm_substream *substream, + unsigned int reg, int channels); static int hdspm_aes_sync_check(struct hdspm *hdspm, int idx); static int hdspm_wc_sync_check(struct hdspm *hdspm); @@ -5574,11 +5577,16 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - hdspm_set_sgbuf(hdspm, substream, HDSPM_pageAddressBufferOut, - params_channels(params)); + for (i = 0; i < params_channels(params); ++i) { + int c = hdspm->channel_map_out[i]; - for (i = 0; i < params_channels(params); ++i) - snd_hdspm_enable_out(hdspm, i, 1); + if (c < 0) + continue; /* just make sure */ + hdspm_set_channel_dma_addr(hdspm, substream, + HDSPM_pageAddressBufferOut, + c); + snd_hdspm_enable_out(hdspm, c, 1); + } hdspm->playback_buffer = (unsigned char *) substream->runtime->dma_area; @@ -5586,11 +5594,16 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream, "Allocated sample buffer for playback at %p\n", hdspm->playback_buffer); } else { - hdspm_set_sgbuf(hdspm, substream, HDSPM_pageAddressBufferIn, - params_channels(params)); - - for (i = 0; i < params_channels(params); ++i) - snd_hdspm_enable_in(hdspm, i, 1); + for (i = 0; i < params_channels(params); ++i) { + int c = hdspm->channel_map_in[i]; + + if (c < 0) + continue; + hdspm_set_channel_dma_addr(hdspm, substream, + HDSPM_pageAddressBufferIn, + c); + snd_hdspm_enable_in(hdspm, c, 1); + } hdspm->capture_buffer = (unsigned char *) substream->runtime->dma_area; @@ -5651,19 +5664,17 @@ static int snd_hdspm_hw_free(struct snd_pcm_substream *substream) struct hdspm *hdspm = snd_pcm_substream_chip(substream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - - /* params_channels(params) should be enough, - but to get sure in case of error */ - for (i = 0; i < hdspm->max_channels_out; ++i) + /* Just disable all channels. The saving when disabling a */ + /* smaller set is not worth the trouble. */ + for (i = 0; i < HDSPM_MAX_CHANNELS; ++i) snd_hdspm_enable_out(hdspm, i, 0); hdspm->playback_buffer = NULL; } else { - for (i = 0; i < hdspm->max_channels_in; ++i) + for (i = 0; i < HDSPM_MAX_CHANNELS; ++i) snd_hdspm_enable_in(hdspm, i, 0); hdspm->capture_buffer = NULL; - } snd_pcm_lib_free_pages(substream); @@ -6402,17 +6413,17 @@ static int snd_hdspm_preallocate_memory(struct hdspm *hdspm) return 0; } - -static void hdspm_set_sgbuf(struct hdspm *hdspm, - struct snd_pcm_substream *substream, - unsigned int reg, int channels) +/* Inform the card what DMA addresses to use for the indicated channel. */ +/* Each channel got 16 4K pages allocated for DMA transfers. */ +static void hdspm_set_channel_dma_addr(struct hdspm *hdspm, + struct snd_pcm_substream *substream, + unsigned int reg, int channel) { int i; - /* continuous memory segment */ - for (i = 0; i < (channels * 16); i++) + for (i = channel * 16; i < channel * 16 + 16; i++) hdspm_write(hdspm, reg + 4 * i, - snd_pcm_sgbuf_get_addr(substream, 4096 * i)); + snd_pcm_sgbuf_get_addr(substream, 4096 * i)); } diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index cb9818af5b41..4c851f8dcaf8 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -2158,13 +2158,12 @@ static int snd_rme9652_prepare(struct snd_pcm_substream *substream) { struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream); unsigned long flags; - int result = 0; spin_lock_irqsave(&rme9652->lock, flags); if (!rme9652->running) rme9652_reset_hw_pointer(rme9652); spin_unlock_irqrestore(&rme9652->lock, flags); - return result; + return 0; } static const struct snd_pcm_hardware snd_rme9652_playback_subinfo = diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c index 71b7fd344c58..c213eb7ca23c 100644 --- a/sound/ppc/snd_ps3.c +++ b/sound/ppc/snd_ps3.c @@ -628,7 +628,6 @@ static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); - int ret = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -665,7 +664,7 @@ static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream, } - return ret; + return 0; }; /* diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c index 9615e786d049..80dab5df9633 100644 --- a/sound/soc/codecs/ad193x.c +++ b/sound/soc/codecs/ad193x.c @@ -240,10 +240,8 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, } /* For DSP_*, LRCLK's polarity must be inverted */ - if (fmt & SND_SOC_DAIFMT_DSP_A) { - change_bit(ffs(AD193X_DAC_LEFT_HIGH) - 1, - (unsigned long *)&dac_fmt); - } + if (fmt & SND_SOC_DAIFMT_DSP_A) + dac_fmt ^= AD193X_DAC_LEFT_HIGH; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */ diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index 0ac3e520653f..85beef265cc8 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -38,21 +38,21 @@ static const DECLARE_TLV_DB_SCALE(ec_mic_gain_tlv, 0, 100, 0); static int ec_command_get_gain(struct snd_soc_component *component, struct ec_param_codec_i2s *param, - struct ec_response_codec_gain *resp) + struct ec_codec_i2s_gain *resp) { struct cros_ec_codec_data *codec_data = snd_soc_component_get_drvdata(component); struct cros_ec_device *ec_device = codec_data->ec_device; u8 buffer[sizeof(struct cros_ec_command) + max(sizeof(struct ec_param_codec_i2s), - sizeof(struct ec_response_codec_gain))]; + sizeof(struct ec_codec_i2s_gain))]; struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; int ret; msg->version = 0; msg->command = EC_CMD_CODEC_I2S; msg->outsize = sizeof(struct ec_param_codec_i2s); - msg->insize = sizeof(struct ec_response_codec_gain); + msg->insize = sizeof(struct ec_codec_i2s_gain); memcpy(msg->data, param, msg->outsize); @@ -226,7 +226,7 @@ static int get_ec_mic_gain(struct snd_soc_component *component, u8 *left, u8 *right) { struct ec_param_codec_i2s param; - struct ec_response_codec_gain resp; + struct ec_codec_i2s_gain resp; int ret; param.cmd = EC_CODEC_GET_GAIN; diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 11ec031ad749..18c173e6a13b 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -559,6 +559,29 @@ static struct hdac_hdmi_port *hdac_hdmi_get_port_from_cvt( } /* + * Go through all converters and ensure connection is set to + * the correct pin as set via kcontrols. + */ +static void hdac_hdmi_verify_connect_sel_all_pins(struct hdac_device *hdev) +{ + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + struct hdac_hdmi_port *port; + struct hdac_hdmi_cvt *cvt; + int cvt_idx = 0; + + list_for_each_entry(cvt, &hdmi->cvt_list, head) { + port = hdac_hdmi_get_port_from_cvt(hdev, hdmi, cvt); + if (port && port->pin) { + snd_hdac_codec_write(hdev, port->pin->nid, 0, + AC_VERB_SET_CONNECT_SEL, cvt_idx); + dev_dbg(&hdev->dev, "%s: %s set connect %d -> %d\n", + __func__, cvt->name, port->pin->nid, cvt_idx); + } + ++cvt_idx; + } +} + +/* * This tries to get a valid pin and set the HW constraints based on the * ELD. Even if a valid pin is not found return success so that device open * doesn't fail. @@ -818,6 +841,14 @@ static int hdac_hdmi_cvt_output_widget_event(struct snd_soc_dapm_widget *w, AC_VERB_SET_CHANNEL_STREAMID, pcm->stream_tag); snd_hdac_codec_write(hdev, cvt->nid, 0, AC_VERB_SET_STREAM_FORMAT, pcm->format); + + /* + * The connection indices are shared by all converters and + * may interfere with each other. Ensure correct + * routing for all converters at stream start. + */ + hdac_hdmi_verify_connect_sel_all_pins(hdev); + break; case SND_SOC_DAPM_POST_PMD: @@ -2066,7 +2097,7 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev) "Failed in parse and map nid with err: %d\n", ret); return ret; } - snd_hdac_refresh_widgets(hdev, true); + snd_hdac_refresh_widgets(hdev); /* ASoC specific initialization */ ret = devm_snd_soc_register_component(&hdev->dev, &hdmi_hda_codec, diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index e5dd05c94f62..9f5aee7de686 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -1880,6 +1880,10 @@ static void nau8825_init_regs(struct nau8825 *nau8825) NAU8825_JACK_EJECT_DEBOUNCE_MASK, nau8825->jack_eject_debounce << NAU8825_JACK_EJECT_DEBOUNCE_SFT); + /* Pull up IRQ pin */ + regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, + NAU8825_IRQ_PIN_PULLUP | NAU8825_IRQ_PIN_PULL_EN, + NAU8825_IRQ_PIN_PULLUP | NAU8825_IRQ_PIN_PULL_EN); /* Mask unneeded IRQs: 1 - disable, 0 - enable */ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, 0x7ff, 0x7ff); diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h index 5e60696460de..887bbff03ec6 100644 --- a/sound/soc/codecs/nau8825.h +++ b/sound/soc/codecs/nau8825.h @@ -168,6 +168,8 @@ #define NAU8825_JACK_POLARITY (1 << 1) /* 0 - active low, 1 - active high */ /* INTERRUPT_MASK (0xf) */ +#define NAU8825_IRQ_PIN_PULLUP (1 << 14) +#define NAU8825_IRQ_PIN_PULL_EN (1 << 13) #define NAU8825_IRQ_OUTPUT_EN (1 << 11) #define NAU8825_IRQ_HEADSET_COMPLETE_EN (1 << 10) #define NAU8825_IRQ_RMS_EN (1 << 8) diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index ebf2ca3249cb..288df245b2f0 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -423,9 +423,6 @@ static int graph_for_each_link(struct asoc_simple_priv *priv, codec_ep = of_graph_get_remote_endpoint(cpu_ep); codec_port = of_get_parent(codec_ep); - of_node_put(codec_ep); - of_node_put(codec_port); - /* get convert-xxx property */ memset(&adata, 0, sizeof(adata)); graph_parse_convert(dev, codec_ep, &adata); @@ -445,6 +442,9 @@ static int graph_for_each_link(struct asoc_simple_priv *priv, else ret = func_noml(priv, cpu_ep, codec_ep, li); + of_node_put(codec_ep); + of_node_put(codec_port); + if (ret < 0) return ret; diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 7285474f7d65..0b12d61cf1e7 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -555,7 +555,7 @@ static int sof_audio_probe(struct platform_device *pdev) int dmic_be_num, hdmi_num; int ret, ssp_amp, ssp_codec; - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 7e093fb13c92..3362e71b4563 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -184,6 +184,25 @@ void skl_update_d0i3c(struct device *dev, bool enable) snd_hdac_chip_readb(bus, VS_D0I3C)); } +/** + * skl_dum_set - set DUM bit in EM2 register + * @bus: HD-audio core bus + * + * Addresses incorrect position reporting for capture streams. + * Used on device power up. + */ +static void skl_dum_set(struct hdac_bus *bus) +{ + /* For the DUM bit to be set, CRST needs to be out of reset state */ + if (!(snd_hdac_chip_readb(bus, GCTL) & AZX_GCTL_RESET)) { + skl_enable_miscbdcge(bus->dev, false); + snd_hdac_bus_exit_link_reset(bus); + skl_enable_miscbdcge(bus->dev, true); + } + + snd_hdac_chip_updatel(bus, VS_EM2, AZX_VS_EM2_DUM, AZX_VS_EM2_DUM); +} + /* called from IRQ */ static void skl_stream_update(struct hdac_bus *bus, struct hdac_stream *hstr) { @@ -291,6 +310,7 @@ static int _skl_resume(struct hdac_bus *bus) struct skl *skl = bus_to_skl(bus); skl_init_pci(skl); + skl_dum_set(bus); skl_init_chip(bus, true); return skl_resume_dsp(skl); @@ -950,6 +970,7 @@ static int skl_first_init(struct hdac_bus *bus) /* initialize chip */ skl_init_pci(skl); + skl_dum_set(bus); return skl_init_chip(bus, true); } diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 6fab1a2e133a..6070666a6392 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -37,6 +37,7 @@ #define DMA_TRANSMITION_START 2 #define DMA_TRANSMITION_STOP 3 +#define AZX_VS_EM2_DUM BIT(23) #define AZX_REG_VS_EM2_L1SEN BIT(13) struct skl_dsp_resource { diff --git a/sound/soc/meson/axg-tdm.h b/sound/soc/meson/axg-tdm.h index e578b6f40a07..5774ce0916d4 100644 --- a/sound/soc/meson/axg-tdm.h +++ b/sound/soc/meson/axg-tdm.h @@ -40,7 +40,7 @@ struct axg_tdm_iface { static inline bool axg_tdm_lrclk_invert(unsigned int fmt) { - return (fmt & SND_SOC_DAIFMT_I2S) ^ + return ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) ^ !!(fmt & (SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_NB_IF)); } diff --git a/sound/soc/meson/axg-tdmin.c b/sound/soc/meson/axg-tdmin.c index a790f925a4ef..cb87f17f3e95 100644 --- a/sound/soc/meson/axg-tdmin.c +++ b/sound/soc/meson/axg-tdmin.c @@ -121,7 +121,6 @@ static int axg_tdmin_prepare(struct regmap *map, break; case SND_SOC_DAIFMT_LEFT_J: - case SND_SOC_DAIFMT_RIGHT_J: case SND_SOC_DAIFMT_DSP_B: break; diff --git a/sound/soc/meson/axg-tdmout.c b/sound/soc/meson/axg-tdmout.c index 527bfc4487e0..86537fc0ecb5 100644 --- a/sound/soc/meson/axg-tdmout.c +++ b/sound/soc/meson/axg-tdmout.c @@ -137,7 +137,6 @@ static int axg_tdmout_prepare(struct regmap *map, break; case SND_SOC_DAIFMT_LEFT_J: - case SND_SOC_DAIFMT_RIGHT_J: case SND_SOC_DAIFMT_DSP_B: skew += 1; break; diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index 97488b5cc515..2c7348ddbbb3 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -116,6 +116,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) goto err; } + link->nonatomic = 1; link->dpcm_playback = 1; link->dpcm_capture = 1; link->stream_name = link->name; diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index 4f85cb19a309..e8141a33a55e 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -1194,7 +1194,7 @@ EXPORT_SYMBOL_GPL(q6asm_open_read); * q6asm_write_async() - non blocking write * * @ac: audio client pointer - * @len: lenght in bytes + * @len: length in bytes * @msw_ts: timestamp msw * @lsw_ts: timestamp lsw * @wflags: flags associated with write diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c index 7a3e138594c1..c16b0ffe8cfc 100644 --- a/sound/soc/rockchip/rk3399_gru_sound.c +++ b/sound/soc/rockchip/rk3399_gru_sound.c @@ -422,7 +422,7 @@ static const struct dailink_match_data dailink_match[] = { }, }; -static int of_dev_node_match(struct device *dev, void *data) +static int of_dev_node_match(struct device *dev, const void *data) { return dev->of_node == data; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 1486fb2eb921..44f899b970c2 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -165,9 +165,10 @@ static void soc_init_component_debugfs(struct snd_soc_component *component) component->card->debugfs_card_root); } - if (!component->debugfs_root) { + if (IS_ERR(component->debugfs_root)) { dev_warn(component->dev, - "ASoC: Failed to create component debugfs directory\n"); + "ASoC: Failed to create component debugfs directory: %ld\n", + PTR_ERR(component->debugfs_root)); return; } @@ -219,18 +220,21 @@ static void soc_init_card_debugfs(struct snd_soc_card *card) card->debugfs_card_root = debugfs_create_dir(card->name, snd_soc_debugfs_root); - if (!card->debugfs_card_root) { + if (IS_ERR(card->debugfs_card_root)) { dev_warn(card->dev, - "ASoC: Failed to create card debugfs directory\n"); + "ASoC: Failed to create card debugfs directory: %ld\n", + PTR_ERR(card->debugfs_card_root)); + card->debugfs_card_root = NULL; return; } card->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0644, card->debugfs_card_root, &card->pop_time); - if (!card->debugfs_pop_time) + if (IS_ERR(card->debugfs_pop_time)) dev_warn(card->dev, - "ASoC: Failed to create pop time debugfs file\n"); + "ASoC: Failed to create pop time debugfs file: %ld\n", + PTR_ERR(card->debugfs_pop_time)); } static void soc_cleanup_card_debugfs(struct snd_soc_card *card) @@ -2748,14 +2752,12 @@ static void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister) snd_soc_dapm_shutdown(card); snd_soc_flush_all_delayed_work(card); - mutex_lock(&client_mutex); /* remove all components used by DAI links on this card */ for_each_comp_order(order) { for_each_card_rtds(card, rtd) { soc_remove_link_components(card, rtd, order); } } - mutex_unlock(&client_mutex); soc_cleanup_card_resources(card); if (!unregister) @@ -2774,7 +2776,9 @@ static void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister) */ int snd_soc_unregister_card(struct snd_soc_card *card) { + mutex_lock(&client_mutex); snd_soc_unbind_card(card, true); + mutex_unlock(&client_mutex); dev_dbg(card->dev, "ASoC: Unregistered card '%s'\n", card->name); return 0; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 656cb5cd9cd8..2790c00735f3 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2156,23 +2156,25 @@ void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, { struct dentry *d; - if (!parent) + if (!parent || IS_ERR(parent)) return; dapm->debugfs_dapm = debugfs_create_dir("dapm", parent); - if (!dapm->debugfs_dapm) { + if (IS_ERR(dapm->debugfs_dapm)) { dev_warn(dapm->dev, - "ASoC: Failed to create DAPM debugfs directory\n"); + "ASoC: Failed to create DAPM debugfs directory %ld\n", + PTR_ERR(dapm->debugfs_dapm)); return; } d = debugfs_create_file("bias_level", 0444, dapm->debugfs_dapm, dapm, &dapm_bias_fops); - if (!d) + if (IS_ERR(d)) dev_warn(dapm->dev, - "ASoC: Failed to create bias level debugfs file\n"); + "ASoC: Failed to create bias level debugfs file: %ld\n", + PTR_ERR(d)); } static void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w) @@ -2186,10 +2188,10 @@ static void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w) d = debugfs_create_file(w->name, 0444, dapm->debugfs_dapm, w, &dapm_widget_power_fops); - if (!d) + if (IS_ERR(d)) dev_warn(w->dapm->dev, - "ASoC: Failed to create %s debugfs file\n", - w->name); + "ASoC: Failed to create %s debugfs file: %ld\n", + w->name, PTR_ERR(d)); } static void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 82f61c4e74bd..ae50839fddfe 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -34,6 +34,9 @@ /* platform specific devices */ #include "shim.h" +#define IS_CFL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa348) +#define IS_CNL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x9dc8) + /* * Debug */ @@ -245,6 +248,11 @@ static int hda_init(struct snd_sof_dev *sdev) ext_ops = snd_soc_hdac_hda_get_ops(); #endif sof_hda_bus_init(bus, &pci->dev, ext_ops); + + /* Workaround for a communication error on CFL (bko#199007) and CNL */ + if (IS_CFL(pci) || IS_CNL(pci)) + bus->polling_mode = 1; + bus->use_posbuf = 1; bus->bdl_pos_adj = 0; diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index b9bdf45889da..b1c27615b805 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -369,7 +369,7 @@ static inline const struct snd_sof_dsp_ops * @cond: Break condition (usually involving @val) * @sleep_us: Maximum time to sleep between reads in us (0 * tight-loops). Should be less than ~20ms since usleep_range - * is used (see Documentation/timers/timers-howto.txt). + * is used (see Documentation/timers/timers-howto.rst). * @timeout_us: Timeout in us, 0 means never timeout * * Returns 0 on success and -ETIMEDOUT upon a timeout. In either diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 44708c8f90d6..bc7bf15ed7a4 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -2297,7 +2297,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) ret = edma_pcm_platform_register(&pdev->dev); break; case PCM_SDMA: - ret = sdma_pcm_platform_register(&pdev->dev, NULL, NULL); + ret = sdma_pcm_platform_register(&pdev->dev, "tx", "rx"); break; default: dev_err(&pdev->dev, "No DMA controller found (%d)\n", ret); diff --git a/sound/soc/ti/omap-mcbsp.c b/sound/soc/ti/omap-mcbsp.c index 1ab3c7df4f8b..26b503bbdb5f 100644 --- a/sound/soc/ti/omap-mcbsp.c +++ b/sound/soc/ti/omap-mcbsp.c @@ -1424,7 +1424,7 @@ static int asoc_mcbsp_probe(struct platform_device *pdev) if (ret) return ret; - return sdma_pcm_platform_register(&pdev->dev, NULL, NULL); + return sdma_pcm_platform_register(&pdev->dev, "tx", "rx"); } static int asoc_mcbsp_remove(struct platform_device *pdev) diff --git a/sound/sound_core.c b/sound/sound_core.c index b730d97c4de6..90d118cd9164 100644 --- a/sound/sound_core.c +++ b/sound/sound_core.c @@ -275,7 +275,8 @@ retry: goto retry; } spin_unlock(&sound_loader_lock); - return -EBUSY; + r = -EBUSY; + goto fail; } } diff --git a/sound/usb/format.c b/sound/usb/format.c index c02b51a82775..d79db71305f6 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -285,6 +285,33 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip, return nr_rates; } +/* Line6 Helix series don't support the UAC2_CS_RANGE usb function + * call. Return a static table of known clock rates. + */ +static int line6_parse_audio_format_rates_quirk(struct snd_usb_audio *chip, + struct audioformat *fp) +{ + switch (chip->usb_id) { + case USB_ID(0x0E41, 0x4241): /* Line6 Helix */ + case USB_ID(0x0E41, 0x4242): /* Line6 Helix Rack */ + case USB_ID(0x0E41, 0x4244): /* Line6 Helix LT */ + case USB_ID(0x0E41, 0x4246): /* Line6 HX-Stomp */ + /* supported rates: 48Khz */ + kfree(fp->rate_table); + fp->rate_table = kmalloc(sizeof(int), GFP_KERNEL); + if (!fp->rate_table) + return -ENOMEM; + fp->nr_rates = 1; + fp->rate_min = 48000; + fp->rate_max = 48000; + fp->rates = SNDRV_PCM_RATE_48000; + fp->rate_table[0] = 48000; + return 0; + } + + return -ENODEV; +} + /* * parse the format descriptor and stores the possible sample rates * on the audioformat table (audio class v2 and v3). @@ -294,7 +321,7 @@ static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip, { struct usb_device *dev = chip->dev; unsigned char tmp[2], *data; - int nr_triplets, data_size, ret = 0; + int nr_triplets, data_size, ret = 0, ret_l6; int clock = snd_usb_clock_find_source(chip, fp->protocol, fp->clock, false); @@ -313,9 +340,22 @@ static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip, tmp, sizeof(tmp)); if (ret < 0) { - dev_err(&dev->dev, - "%s(): unable to retrieve number of sample rates (clock %d)\n", + /* line6 helix devices don't support UAC2_CS_CONTROL_SAM_FREQ call */ + ret_l6 = line6_parse_audio_format_rates_quirk(chip, fp); + if (ret_l6 == -ENODEV) { + /* no line6 device found continue showing the error */ + dev_err(&dev->dev, + "%s(): unable to retrieve number of sample rates (clock %d)\n", + __func__, clock); + goto err; + } + if (ret_l6 == 0) { + dev_info(&dev->dev, + "%s(): unable to retrieve number of sample rates: set it to a predefined value (clock %d).\n", __func__, clock); + return 0; + } + ret = ret_l6; goto err; } diff --git a/sound/usb/helper.c b/sound/usb/helper.c index 84aa265dd802..4c12cc5b53fd 100644 --- a/sound/usb/helper.c +++ b/sound/usb/helper.c @@ -63,6 +63,20 @@ void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype return NULL; } +/* check the validity of pipe and EP types */ +int snd_usb_pipe_sanity_check(struct usb_device *dev, unsigned int pipe) +{ + static const int pipetypes[4] = { + PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT + }; + struct usb_host_endpoint *ep; + + ep = usb_pipe_endpoint(dev, pipe); + if (!ep || usb_pipetype(pipe) != pipetypes[usb_endpoint_type(&ep->desc)]) + return -EINVAL; + return 0; +} + /* * Wrapper for usb_control_msg(). * Allocates a temp buffer to prevent dmaing from/to the stack. @@ -75,6 +89,9 @@ int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request, void *buf = NULL; int timeout; + if (snd_usb_pipe_sanity_check(dev, pipe)) + return -EINVAL; + if (size > 0) { buf = kmemdup(data, size, GFP_KERNEL); if (!buf) diff --git a/sound/usb/helper.h b/sound/usb/helper.h index d338bd0e0ca6..6afb70156ec4 100644 --- a/sound/usb/helper.h +++ b/sound/usb/helper.h @@ -7,6 +7,7 @@ unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size); void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype); void *snd_usb_find_csint_desc(void *descstart, int desclen, void *after, u8 dsubtype); +int snd_usb_pipe_sanity_check(struct usb_device *dev, unsigned int pipe); int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size); diff --git a/sound/usb/hiface/pcm.c b/sound/usb/hiface/pcm.c index 14fc1e1d5d13..c406497c5919 100644 --- a/sound/usb/hiface/pcm.c +++ b/sound/usb/hiface/pcm.c @@ -600,14 +600,13 @@ int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq) ret = hiface_pcm_init_urb(&rt->out_urbs[i], chip, OUT_EP, hiface_pcm_out_urb_handler); if (ret < 0) - return ret; + goto error; } ret = snd_pcm_new(chip->card, "USB-SPDIF Audio", 0, 1, 0, &pcm); if (ret < 0) { - kfree(rt); dev_err(&chip->dev->dev, "Cannot create pcm instance\n"); - return ret; + goto error; } pcm->private_data = rt; @@ -620,4 +619,10 @@ int hiface_pcm_init(struct hiface_chip *chip, u8 extra_freq) chip->pcm = rt; return 0; + +error: + for (i = 0; i < PCM_N_URBS; i++) + kfree(rt->out_urbs[i].buffer); + kfree(rt); + return ret; } diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c index e63a2451c88f..ab2ec896f49c 100644 --- a/sound/usb/line6/driver.c +++ b/sound/usb/line6/driver.c @@ -192,17 +192,6 @@ static int line6_send_raw_message_async_part(struct message *msg, } /* - Setup and start timer. -*/ -void line6_start_timer(struct timer_list *timer, unsigned long msecs, - void (*function)(struct timer_list *t)) -{ - timer->function = function; - mod_timer(timer, jiffies + msecs_to_jiffies(msecs)); -} -EXPORT_SYMBOL_GPL(line6_start_timer); - -/* Asynchronously send raw message. */ int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer, diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h index a9f7b4aa32c4..e5e572ed5f30 100644 --- a/sound/usb/line6/driver.h +++ b/sound/usb/line6/driver.h @@ -64,13 +64,6 @@ #define LINE6_CHANNEL_MASK 0x0f -#define CHECK_STARTUP_PROGRESS(x, n) \ -do { \ - if ((x) >= (n)) \ - return; \ - x = (n); \ -} while (0) - extern const unsigned char line6_midi_id[3]; static const int SYSEX_DATA_OFS = sizeof(line6_midi_id) + 3; @@ -197,8 +190,6 @@ extern int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer, int size); extern ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); -extern void line6_start_timer(struct timer_list *timer, unsigned long msecs, - void (*function)(struct timer_list *t)); extern int line6_version_request_async(struct usb_line6 *line6); extern int line6_write_data(struct usb_line6 *line6, unsigned address, void *data, unsigned datalen); diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c index 21127e4958b2..f70211e6b174 100644 --- a/sound/usb/line6/pcm.c +++ b/sound/usb/line6/pcm.c @@ -550,13 +550,6 @@ int line6_init_pcm(struct usb_line6 *line6, line6pcm->volume_monitor = 255; line6pcm->line6 = line6; - line6pcm->max_packet_size_in = - usb_maxpacket(line6->usbdev, - usb_rcvisocpipe(line6->usbdev, ep_read), 0); - line6pcm->max_packet_size_out = - usb_maxpacket(line6->usbdev, - usb_sndisocpipe(line6->usbdev, ep_write), 1); - spin_lock_init(&line6pcm->out.lock); spin_lock_init(&line6pcm->in.lock); line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD; @@ -566,6 +559,18 @@ int line6_init_pcm(struct usb_line6 *line6, pcm->private_data = line6pcm; pcm->private_free = line6_cleanup_pcm; + line6pcm->max_packet_size_in = + usb_maxpacket(line6->usbdev, + usb_rcvisocpipe(line6->usbdev, ep_read), 0); + line6pcm->max_packet_size_out = + usb_maxpacket(line6->usbdev, + usb_sndisocpipe(line6->usbdev, ep_write), 1); + if (!line6pcm->max_packet_size_in || !line6pcm->max_packet_size_out) { + dev_err(line6pcm->line6->ifcdev, + "cannot get proper max packet size\n"); + return -EINVAL; + } + err = line6_create_audio_out_urbs(line6pcm); if (err < 0) return err; diff --git a/sound/usb/line6/pod.c b/sound/usb/line6/pod.c index 200ae53adf22..ee4c9d220fdf 100644 --- a/sound/usb/line6/pod.c +++ b/sound/usb/line6/pod.c @@ -35,11 +35,9 @@ Stages of POD startup procedure */ enum { - POD_STARTUP_INIT = 1, POD_STARTUP_VERSIONREQ, - POD_STARTUP_WORKQUEUE, POD_STARTUP_SETUP, - POD_STARTUP_LAST = POD_STARTUP_SETUP - 1 + POD_STARTUP_DONE, }; enum { @@ -59,12 +57,6 @@ struct usb_line6_pod { /* Instrument monitor level */ int monitor_level; - /* Timer for device initialization */ - struct timer_list startup_timer; - - /* Work handler for device initialization */ - struct work_struct startup_work; - /* Current progress in startup procedure */ int startup_progress; @@ -78,6 +70,8 @@ struct usb_line6_pod { int device_id; }; +#define line6_to_pod(x) container_of(x, struct usb_line6_pod, line6) + #define POD_SYSEX_CODE 3 /* *INDENT-OFF* */ @@ -169,10 +163,6 @@ static const char pod_version_header[] = { 0xf2, 0x7e, 0x7f, 0x06, 0x02 }; -/* forward declarations: */ -static void pod_startup2(struct timer_list *t); -static void pod_startup3(struct usb_line6_pod *pod); - static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code, int size) { @@ -185,14 +175,17 @@ static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code, */ static void line6_pod_process_message(struct usb_line6 *line6) { - struct usb_line6_pod *pod = (struct usb_line6_pod *) line6; + struct usb_line6_pod *pod = line6_to_pod(line6); const unsigned char *buf = pod->line6.buffer_message; if (memcmp(buf, pod_version_header, sizeof(pod_version_header)) == 0) { pod->firmware_version = buf[13] * 100 + buf[14] * 10 + buf[15]; pod->device_id = ((int)buf[8] << 16) | ((int)buf[9] << 8) | (int) buf[10]; - pod_startup3(pod); + if (pod->startup_progress == POD_STARTUP_VERSIONREQ) { + pod->startup_progress = POD_STARTUP_SETUP; + schedule_delayed_work(&line6->startup_work, 0); + } return; } @@ -277,47 +270,27 @@ static ssize_t device_id_show(struct device *dev, context). After the last one has finished, the device is ready to use. */ -static void pod_startup1(struct usb_line6_pod *pod) -{ - CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_INIT); - - /* delay startup procedure: */ - line6_start_timer(&pod->startup_timer, POD_STARTUP_DELAY, pod_startup2); -} - -static void pod_startup2(struct timer_list *t) -{ - struct usb_line6_pod *pod = from_timer(pod, t, startup_timer); - struct usb_line6 *line6 = &pod->line6; - - CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_VERSIONREQ); - - /* request firmware version: */ - line6_version_request_async(line6); -} - -static void pod_startup3(struct usb_line6_pod *pod) -{ - CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_WORKQUEUE); - - /* schedule work for global work queue: */ - schedule_work(&pod->startup_work); -} - -static void pod_startup4(struct work_struct *work) +static void pod_startup(struct usb_line6 *line6) { - struct usb_line6_pod *pod = - container_of(work, struct usb_line6_pod, startup_work); - struct usb_line6 *line6 = &pod->line6; - - CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_SETUP); - - /* serial number: */ - line6_read_serial_number(&pod->line6, &pod->serial_number); - - /* ALSA audio interface: */ - if (snd_card_register(line6->card)) - dev_err(line6->ifcdev, "Failed to register POD card.\n"); + struct usb_line6_pod *pod = line6_to_pod(line6); + + switch (pod->startup_progress) { + case POD_STARTUP_VERSIONREQ: + /* request firmware version: */ + line6_version_request_async(line6); + break; + case POD_STARTUP_SETUP: + /* serial number: */ + line6_read_serial_number(&pod->line6, &pod->serial_number); + + /* ALSA audio interface: */ + if (snd_card_register(line6->card)) + dev_err(line6->ifcdev, "Failed to register POD card.\n"); + pod->startup_progress = POD_STARTUP_DONE; + break; + default: + break; + } } /* POD special files: */ @@ -353,7 +326,7 @@ static int snd_pod_control_monitor_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; + struct usb_line6_pod *pod = line6_to_pod(line6pcm->line6); ucontrol->value.integer.value[0] = pod->monitor_level; return 0; @@ -364,7 +337,7 @@ static int snd_pod_control_monitor_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; + struct usb_line6_pod *pod = line6_to_pod(line6pcm->line6); if (ucontrol->value.integer.value[0] == pod->monitor_level) return 0; @@ -387,30 +360,16 @@ static const struct snd_kcontrol_new pod_control_monitor = { }; /* - POD device disconnected. -*/ -static void line6_pod_disconnect(struct usb_line6 *line6) -{ - struct usb_line6_pod *pod = (struct usb_line6_pod *)line6; - - del_timer_sync(&pod->startup_timer); - cancel_work_sync(&pod->startup_work); -} - -/* Try to init POD device. */ static int pod_init(struct usb_line6 *line6, const struct usb_device_id *id) { int err; - struct usb_line6_pod *pod = (struct usb_line6_pod *) line6; + struct usb_line6_pod *pod = line6_to_pod(line6); line6->process_message = line6_pod_process_message; - line6->disconnect = line6_pod_disconnect; - - timer_setup(&pod->startup_timer, NULL, 0); - INIT_WORK(&pod->startup_work, pod_startup4); + line6->startup = pod_startup; /* create sysfs entries: */ err = snd_card_add_dev_attr(line6->card, &pod_dev_attr_group); @@ -443,7 +402,8 @@ static int pod_init(struct usb_line6 *line6, pod->monitor_level = POD_SYSTEM_INVALID; /* initiate startup procedure: */ - pod_startup1(pod); + schedule_delayed_work(&line6->startup_work, + msecs_to_jiffies(POD_STARTUP_DELAY)); } return 0; diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c index 77a1d55334bb..27bf61c177c0 100644 --- a/sound/usb/line6/podhd.c +++ b/sound/usb/line6/podhd.c @@ -18,16 +18,6 @@ #define PODHD_STARTUP_DELAY 500 -/* - * Stages of POD startup procedure - */ -enum { - PODHD_STARTUP_INIT = 1, - PODHD_STARTUP_SCHEDULE_WORKQUEUE, - PODHD_STARTUP_SETUP, - PODHD_STARTUP_LAST = PODHD_STARTUP_SETUP - 1 -}; - enum { LINE6_PODHD300, LINE6_PODHD400, @@ -43,15 +33,6 @@ struct usb_line6_podhd { /* Generic Line 6 USB data */ struct usb_line6 line6; - /* Timer for device initialization */ - struct timer_list startup_timer; - - /* Work handler for device initialization */ - struct work_struct startup_work; - - /* Current progress in startup procedure */ - int startup_progress; - /* Serial number of device */ u32 serial_number; @@ -59,6 +40,8 @@ struct usb_line6_podhd { int firmware_version; }; +#define line6_to_podhd(x) container_of(x, struct usb_line6_podhd, line6) + static struct snd_ratden podhd_ratden = { .num_min = 48000, .num_max = 48000, @@ -154,10 +137,6 @@ static struct line6_pcm_properties podx3_pcm_properties = { }; static struct usb_driver podhd_driver; -static void podhd_startup_start_workqueue(struct timer_list *t); -static void podhd_startup_workqueue(struct work_struct *work); -static int podhd_startup_finalize(struct usb_line6_podhd *pod); - static ssize_t serial_number_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -198,26 +177,6 @@ static const struct attribute_group podhd_dev_attr_group = { * audio nor bulk interfaces to work. */ -static void podhd_startup(struct usb_line6_podhd *pod) -{ - CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_INIT); - - /* delay startup procedure: */ - line6_start_timer(&pod->startup_timer, PODHD_STARTUP_DELAY, - podhd_startup_start_workqueue); -} - -static void podhd_startup_start_workqueue(struct timer_list *t) -{ - struct usb_line6_podhd *pod = from_timer(pod, t, startup_timer); - - CHECK_STARTUP_PROGRESS(pod->startup_progress, - PODHD_STARTUP_SCHEDULE_WORKQUEUE); - - /* schedule work for global work queue: */ - schedule_work(&pod->startup_work); -} - static int podhd_dev_start(struct usb_line6_podhd *pod) { int ret; @@ -268,37 +227,23 @@ exit: return ret; } -static void podhd_startup_workqueue(struct work_struct *work) +static void podhd_startup(struct usb_line6 *line6) { - struct usb_line6_podhd *pod = - container_of(work, struct usb_line6_podhd, startup_work); - - CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_SETUP); + struct usb_line6_podhd *pod = line6_to_podhd(line6); podhd_dev_start(pod); line6_read_serial_number(&pod->line6, &pod->serial_number); - - podhd_startup_finalize(pod); -} - -static int podhd_startup_finalize(struct usb_line6_podhd *pod) -{ - struct usb_line6 *line6 = &pod->line6; - - /* ALSA audio interface: */ - return snd_card_register(line6->card); + if (snd_card_register(line6->card)) + dev_err(line6->ifcdev, "Failed to register POD HD card.\n"); } static void podhd_disconnect(struct usb_line6 *line6) { - struct usb_line6_podhd *pod = (struct usb_line6_podhd *)line6; + struct usb_line6_podhd *pod = line6_to_podhd(line6); if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL_INFO) { struct usb_interface *intf; - del_timer_sync(&pod->startup_timer); - cancel_work_sync(&pod->startup_work); - intf = usb_ifnum_to_if(line6->usbdev, pod->line6.properties->ctrl_if); if (intf) @@ -313,13 +258,11 @@ static int podhd_init(struct usb_line6 *line6, const struct usb_device_id *id) { int err; - struct usb_line6_podhd *pod = (struct usb_line6_podhd *) line6; + struct usb_line6_podhd *pod = line6_to_podhd(line6); struct usb_interface *intf; line6->disconnect = podhd_disconnect; - - timer_setup(&pod->startup_timer, NULL, 0); - INIT_WORK(&pod->startup_work, podhd_startup_workqueue); + line6->startup = podhd_startup; if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) { /* claim the data interface */ @@ -358,11 +301,12 @@ static int podhd_init(struct usb_line6 *line6, if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL_INFO)) { /* register USB audio system directly */ - return podhd_startup_finalize(pod); + return snd_card_register(line6->card); } /* init device and delay registering */ - podhd_startup(pod); + schedule_delayed_work(&line6->startup_work, + msecs_to_jiffies(PODHD_STARTUP_DELAY)); return 0; } @@ -424,7 +368,7 @@ static const struct line6_properties podhd_properties_table[] = { .name = "POD HD500", .capabilities = LINE6_CAP_PCM | LINE6_CAP_HWMON, - .altsetting = 1, + .altsetting = 0, .ep_ctrl_r = 0x81, .ep_ctrl_w = 0x01, .ep_audio_r = 0x86, diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c index 974ab3e62b68..d0a555dbe324 100644 --- a/sound/usb/line6/toneport.c +++ b/sound/usb/line6/toneport.c @@ -57,6 +57,8 @@ struct usb_line6_toneport { struct toneport_led leds[2]; }; +#define line6_to_toneport(x) container_of(x, struct usb_line6_toneport, line6) + static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2); #define TONEPORT_PCM_DELAY 1 @@ -207,8 +209,8 @@ static int snd_toneport_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - struct usb_line6_toneport *toneport = - (struct usb_line6_toneport *)line6pcm->line6; + struct usb_line6_toneport *toneport = line6_to_toneport(line6pcm->line6); + ucontrol->value.enumerated.item[0] = toneport->source; return 0; } @@ -218,8 +220,7 @@ static int snd_toneport_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - struct usb_line6_toneport *toneport = - (struct usb_line6_toneport *)line6pcm->line6; + struct usb_line6_toneport *toneport = line6_to_toneport(line6pcm->line6); unsigned int source; source = ucontrol->value.enumerated.item[0]; @@ -393,8 +394,7 @@ static int toneport_setup(struct usb_line6_toneport *toneport) */ static void line6_toneport_disconnect(struct usb_line6 *line6) { - struct usb_line6_toneport *toneport = - (struct usb_line6_toneport *)line6; + struct usb_line6_toneport *toneport = line6_to_toneport(line6); if (toneport_has_led(toneport)) toneport_remove_leds(toneport); @@ -408,7 +408,7 @@ static int toneport_init(struct usb_line6 *line6, const struct usb_device_id *id) { int err; - struct usb_line6_toneport *toneport = (struct usb_line6_toneport *) line6; + struct usb_line6_toneport *toneport = line6_to_toneport(line6); toneport->type = id->driver_info; diff --git a/sound/usb/line6/variax.c b/sound/usb/line6/variax.c index e59b97444399..ed158f04de80 100644 --- a/sound/usb/line6/variax.c +++ b/sound/usb/line6/variax.c @@ -22,13 +22,9 @@ Stages of Variax startup procedure */ enum { - VARIAX_STARTUP_INIT = 1, VARIAX_STARTUP_VERSIONREQ, - VARIAX_STARTUP_WAIT, VARIAX_STARTUP_ACTIVATE, - VARIAX_STARTUP_WORKQUEUE, VARIAX_STARTUP_SETUP, - VARIAX_STARTUP_LAST = VARIAX_STARTUP_SETUP - 1 }; enum { @@ -43,17 +39,12 @@ struct usb_line6_variax { /* Buffer for activation code */ unsigned char *buffer_activate; - /* Handler for device initialization */ - struct work_struct startup_work; - - /* Timers for device initialization */ - struct timer_list startup_timer1; - struct timer_list startup_timer2; - /* Current progress in startup procedure */ int startup_progress; }; +#define line6_to_variax(x) container_of(x, struct usb_line6_variax, line6) + #define VARIAX_OFFSET_ACTIVATE 7 /* @@ -77,11 +68,6 @@ static const char variax_activate[] = { 0xf7 }; -/* forward declarations: */ -static void variax_startup2(struct timer_list *t); -static void variax_startup4(struct timer_list *t); -static void variax_startup5(struct timer_list *t); - static void variax_activate_async(struct usb_line6_variax *variax, int a) { variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a; @@ -96,74 +82,30 @@ static void variax_activate_async(struct usb_line6_variax *variax, int a) context). After the last one has finished, the device is ready to use. */ -static void variax_startup1(struct usb_line6_variax *variax) -{ - CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_INIT); - - /* delay startup procedure: */ - line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, - variax_startup2); -} - -static void variax_startup2(struct timer_list *t) -{ - struct usb_line6_variax *variax = from_timer(variax, t, startup_timer1); - struct usb_line6 *line6 = &variax->line6; - - /* schedule another startup procedure until startup is complete: */ - if (variax->startup_progress >= VARIAX_STARTUP_LAST) - return; - - variax->startup_progress = VARIAX_STARTUP_VERSIONREQ; - line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, - variax_startup2); - - /* request firmware version: */ - line6_version_request_async(line6); -} - -static void variax_startup3(struct usb_line6_variax *variax) -{ - CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_WAIT); - - /* delay startup procedure: */ - line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY3, - variax_startup4); -} - -static void variax_startup4(struct timer_list *t) +static void variax_startup(struct usb_line6 *line6) { - struct usb_line6_variax *variax = from_timer(variax, t, startup_timer2); - - CHECK_STARTUP_PROGRESS(variax->startup_progress, - VARIAX_STARTUP_ACTIVATE); - - /* activate device: */ - variax_activate_async(variax, 1); - line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4, - variax_startup5); -} - -static void variax_startup5(struct timer_list *t) -{ - struct usb_line6_variax *variax = from_timer(variax, t, startup_timer2); - - CHECK_STARTUP_PROGRESS(variax->startup_progress, - VARIAX_STARTUP_WORKQUEUE); - - /* schedule work for global work queue: */ - schedule_work(&variax->startup_work); -} - -static void variax_startup6(struct work_struct *work) -{ - struct usb_line6_variax *variax = - container_of(work, struct usb_line6_variax, startup_work); - - CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_SETUP); - - /* ALSA audio interface: */ - snd_card_register(variax->line6.card); + struct usb_line6_variax *variax = line6_to_variax(line6); + + switch (variax->startup_progress) { + case VARIAX_STARTUP_VERSIONREQ: + /* repeat request until getting the response */ + schedule_delayed_work(&line6->startup_work, + msecs_to_jiffies(VARIAX_STARTUP_DELAY1)); + /* request firmware version: */ + line6_version_request_async(line6); + break; + case VARIAX_STARTUP_ACTIVATE: + /* activate device: */ + variax_activate_async(variax, 1); + variax->startup_progress = VARIAX_STARTUP_SETUP; + schedule_delayed_work(&line6->startup_work, + msecs_to_jiffies(VARIAX_STARTUP_DELAY4)); + break; + case VARIAX_STARTUP_SETUP: + /* ALSA audio interface: */ + snd_card_register(variax->line6.card); + break; + } } /* @@ -171,7 +113,7 @@ static void variax_startup6(struct work_struct *work) */ static void line6_variax_process_message(struct usb_line6 *line6) { - struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; + struct usb_line6_variax *variax = line6_to_variax(line6); const unsigned char *buf = variax->line6.buffer_message; switch (buf[0]) { @@ -182,11 +124,19 @@ static void line6_variax_process_message(struct usb_line6 *line6) case LINE6_SYSEX_BEGIN: if (memcmp(buf + 1, variax_init_version + 1, sizeof(variax_init_version) - 1) == 0) { - variax_startup3(variax); + if (variax->startup_progress >= VARIAX_STARTUP_ACTIVATE) + break; + variax->startup_progress = VARIAX_STARTUP_ACTIVATE; + cancel_delayed_work(&line6->startup_work); + schedule_delayed_work(&line6->startup_work, + msecs_to_jiffies(VARIAX_STARTUP_DELAY3)); } else if (memcmp(buf + 1, variax_init_done + 1, sizeof(variax_init_done) - 1) == 0) { /* notify of complete initialization: */ - variax_startup4(&variax->startup_timer2); + if (variax->startup_progress >= VARIAX_STARTUP_SETUP) + break; + cancel_delayed_work(&line6->startup_work); + schedule_delayed_work(&line6->startup_work, 0); } break; } @@ -197,11 +147,7 @@ static void line6_variax_process_message(struct usb_line6 *line6) */ static void line6_variax_disconnect(struct usb_line6 *line6) { - struct usb_line6_variax *variax = (struct usb_line6_variax *)line6; - - del_timer(&variax->startup_timer1); - del_timer(&variax->startup_timer2); - cancel_work_sync(&variax->startup_work); + struct usb_line6_variax *variax = line6_to_variax(line6); kfree(variax->buffer_activate); } @@ -212,15 +158,12 @@ static void line6_variax_disconnect(struct usb_line6 *line6) static int variax_init(struct usb_line6 *line6, const struct usb_device_id *id) { - struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; + struct usb_line6_variax *variax = line6_to_variax(line6); int err; line6->process_message = line6_variax_process_message; line6->disconnect = line6_variax_disconnect; - - timer_setup(&variax->startup_timer1, NULL, 0); - timer_setup(&variax->startup_timer2, NULL, 0); - INIT_WORK(&variax->startup_work, variax_startup6); + line6->startup = variax_startup; /* initialize USB buffers: */ variax->buffer_activate = kmemdup(variax_activate, @@ -235,7 +178,8 @@ static int variax_init(struct usb_line6 *line6, return err; /* initiate startup procedure: */ - variax_startup1(variax); + schedule_delayed_work(&line6->startup_work, + msecs_to_jiffies(VARIAX_STARTUP_DELAY1)); return 0; } @@ -300,5 +244,5 @@ static struct usb_driver variax_driver = { module_usb_driver(variax_driver); -MODULE_DESCRIPTION("Vairax Workbench USB driver"); +MODULE_DESCRIPTION("Variax Workbench USB driver"); MODULE_LICENSE("GPL"); diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index c703f8534b07..eceab19766db 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -68,6 +68,7 @@ struct mixer_build { unsigned char *buffer; unsigned int buflen; DECLARE_BITMAP(unitbitmap, MAX_ID_ELEMS); + DECLARE_BITMAP(termbitmap, MAX_ID_ELEMS); struct usb_audio_term oterm; const struct usbmix_name_map *map; const struct usbmix_selector_map *selector_map; @@ -738,12 +739,13 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state, struct uac_mixer_unit_descriptor *desc) { int mu_channels; - void *c; if (desc->bLength < sizeof(*desc)) return -EINVAL; if (!desc->bNrInPins) return -EINVAL; + if (desc->bLength < sizeof(*desc) + desc->bNrInPins) + return -EINVAL; switch (state->mixer->protocol) { case UAC_VERSION_1: @@ -759,13 +761,6 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state, break; } - if (!mu_channels) - return 0; - - c = uac_mixer_unit_bmControls(desc, state->mixer->protocol); - if (c - (void *)desc + (mu_channels - 1) / 8 >= desc->bLength) - return 0; /* no bmControls -> skip */ - return mu_channels; } @@ -773,16 +768,25 @@ static int uac_mixer_unit_get_channels(struct mixer_build *state, * parse the source unit recursively until it reaches to a terminal * or a branched unit. */ -static int check_input_term(struct mixer_build *state, int id, +static int __check_input_term(struct mixer_build *state, int id, struct usb_audio_term *term) { int protocol = state->mixer->protocol; int err; void *p1; + unsigned char *hdr; memset(term, 0, sizeof(*term)); - while ((p1 = find_audio_control_unit(state, id)) != NULL) { - unsigned char *hdr = p1; + for (;;) { + /* a loop in the terminal chain? */ + if (test_and_set_bit(id, state->termbitmap)) + return -EINVAL; + + p1 = find_audio_control_unit(state, id); + if (!p1) + break; + + hdr = p1; term->id = id; if (protocol == UAC_VERSION_1 || protocol == UAC_VERSION_2) { @@ -800,7 +804,7 @@ static int check_input_term(struct mixer_build *state, int id, /* call recursively to verify that the * referenced clock entity is valid */ - err = check_input_term(state, d->bCSourceID, term); + err = __check_input_term(state, d->bCSourceID, term); if (err < 0) return err; @@ -834,7 +838,7 @@ static int check_input_term(struct mixer_build *state, int id, case UAC2_CLOCK_SELECTOR: { struct uac_selector_unit_descriptor *d = p1; /* call recursively to retrieve the channel info */ - err = check_input_term(state, d->baSourceID[0], term); + err = __check_input_term(state, d->baSourceID[0], term); if (err < 0) return err; term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */ @@ -897,7 +901,7 @@ static int check_input_term(struct mixer_build *state, int id, /* call recursively to verify that the * referenced clock entity is valid */ - err = check_input_term(state, d->bCSourceID, term); + err = __check_input_term(state, d->bCSourceID, term); if (err < 0) return err; @@ -948,7 +952,7 @@ static int check_input_term(struct mixer_build *state, int id, case UAC3_CLOCK_SELECTOR: { struct uac_selector_unit_descriptor *d = p1; /* call recursively to retrieve the channel info */ - err = check_input_term(state, d->baSourceID[0], term); + err = __check_input_term(state, d->baSourceID[0], term); if (err < 0) return err; term->type = UAC3_SELECTOR_UNIT << 16; /* virtual type */ @@ -964,7 +968,7 @@ static int check_input_term(struct mixer_build *state, int id, return -EINVAL; /* call recursively to retrieve the channel info */ - err = check_input_term(state, d->baSourceID[0], term); + err = __check_input_term(state, d->baSourceID[0], term); if (err < 0) return err; @@ -982,6 +986,15 @@ static int check_input_term(struct mixer_build *state, int id, return -ENODEV; } + +static int check_input_term(struct mixer_build *state, int id, + struct usb_audio_term *term) +{ + memset(term, 0, sizeof(*term)); + memset(state->termbitmap, 0, sizeof(state->termbitmap)); + return __check_input_term(state, id, term); +} + /* * Feature Unit */ @@ -1988,6 +2001,31 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, * Mixer Unit */ +/* check whether the given in/out overflows bmMixerControls matrix */ +static bool mixer_bitmap_overflow(struct uac_mixer_unit_descriptor *desc, + int protocol, int num_ins, int num_outs) +{ + u8 *hdr = (u8 *)desc; + u8 *c = uac_mixer_unit_bmControls(desc, protocol); + size_t rest; /* remaining bytes after bmMixerControls */ + + switch (protocol) { + case UAC_VERSION_1: + default: + rest = 1; /* iMixer */ + break; + case UAC_VERSION_2: + rest = 2; /* bmControls + iMixer */ + break; + case UAC_VERSION_3: + rest = 6; /* bmControls + wMixerDescrStr */ + break; + } + + /* overflow? */ + return c + (num_ins * num_outs + 7) / 8 + rest > hdr + hdr[0]; +} + /* * build a mixer unit control * @@ -2116,6 +2154,9 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, if (err < 0) return err; num_ins += iterm.channels; + if (mixer_bitmap_overflow(desc, state->mixer->protocol, + num_ins, num_outs)) + break; for (; ich < num_ins; ich++) { int och, ich_has_controls = 0; @@ -2303,7 +2344,7 @@ static struct procunit_info extunits[] = { */ static int build_audio_procunit(struct mixer_build *state, int unitid, void *raw_desc, struct procunit_info *list, - char *name) + bool extension_unit) { struct uac_processing_unit_descriptor *desc = raw_desc; int num_ins; @@ -2320,6 +2361,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, static struct procunit_info default_info = { 0, NULL, default_value_info }; + const char *name = extension_unit ? + "Extension Unit" : "Processing Unit"; if (desc->bLength < 13) { usb_audio_err(state->chip, "invalid %s descriptor (id %d)\n", name, unitid); @@ -2433,7 +2476,10 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, } else if (info->name) { strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name)); } else { - nameid = uac_processing_unit_iProcessing(desc, state->mixer->protocol); + if (extension_unit) + nameid = uac_extension_unit_iExtension(desc, state->mixer->protocol); + else + nameid = uac_processing_unit_iProcessing(desc, state->mixer->protocol); len = 0; if (nameid) len = snd_usb_copy_string_desc(state->chip, @@ -2466,10 +2512,10 @@ static int parse_audio_processing_unit(struct mixer_build *state, int unitid, case UAC_VERSION_2: default: return build_audio_procunit(state, unitid, raw_desc, - procunits, "Processing Unit"); + procunits, false); case UAC_VERSION_3: return build_audio_procunit(state, unitid, raw_desc, - uac3_procunits, "Processing Unit"); + uac3_procunits, false); } } @@ -2480,8 +2526,7 @@ static int parse_audio_extension_unit(struct mixer_build *state, int unitid, * Note that we parse extension units with processing unit descriptors. * That's ok as the layout is the same. */ - return build_audio_procunit(state, unitid, raw_desc, - extunits, "Extension Unit"); + return build_audio_procunit(state, unitid, raw_desc, extunits, true); } /* diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 1f6011f36bb0..27dcb3743690 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -741,7 +741,7 @@ static int snd_ni_control_init_val(struct usb_mixer_interface *mixer, return err; } - kctl->private_value |= (value << 24); + kctl->private_value |= ((unsigned int)value << 24); return 0; } @@ -902,7 +902,7 @@ static int snd_ftu_eff_switch_init(struct usb_mixer_interface *mixer, if (err < 0) return err; - kctl->private_value |= value[0] << 24; + kctl->private_value |= (unsigned int)value[0] << 24; return 0; } @@ -1155,17 +1155,17 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, { struct usb_mixer_interface *mixer; struct usb_mixer_elem_info *cval; - int unitid = 12; /* SamleRate ExtensionUnit ID */ + int unitid = 12; /* SampleRate ExtensionUnit ID */ list_for_each_entry(mixer, &chip->mixer_list, list) { - cval = mixer_elem_list_to_info(mixer->id_elems[unitid]); - if (cval) { + if (mixer->id_elems[unitid]) { + cval = mixer_elem_list_to_info(mixer->id_elems[unitid]); snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, cval->control << 8, samplerate_id); snd_usb_mixer_notify_id(mixer, unitid); + break; } - break; } } diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 75b96929f76c..e4bbf79de956 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -339,6 +339,7 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, ep = 0x81; ifnum = 2; goto add_sync_ep_from_ifnum; + case USB_ID(0x1397, 0x0001): /* Behringer UFX1604 */ case USB_ID(0x1397, 0x0002): /* Behringer UFX1204 */ ep = 0x81; ifnum = 1; diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 9e049f60e80e..e918ce346027 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2408,7 +2408,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), USB_DEVICE(0x086a, 0x0001), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { .vendor_name = "Emagic", - /* .product_name = "Unitor8", */ + .product_name = "Unitor8", .ifnum = 2, .type = QUIRK_MIDI_EMAGIC, .data = & (const struct snd_usb_midi_endpoint_info) { diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index cf5cff10c08e..78858918cbc1 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -828,11 +828,13 @@ static int snd_usb_novation_boot_quirk(struct usb_device *dev) static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev) { int err, actual_length; - /* "midi send" enable */ static const u8 seq[] = { 0x4e, 0x73, 0x52, 0x01 }; + void *buf; - void *buf = kmemdup(seq, ARRAY_SIZE(seq), GFP_KERNEL); + if (snd_usb_pipe_sanity_check(dev, usb_sndintpipe(dev, 0x05))) + return -EINVAL; + buf = kmemdup(seq, ARRAY_SIZE(seq), GFP_KERNEL); if (!buf) return -ENOMEM; err = usb_interrupt_msg(dev, usb_sndintpipe(dev, 0x05), buf, @@ -857,7 +859,11 @@ static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev) static int snd_usb_nativeinstruments_boot_quirk(struct usb_device *dev) { - int ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + int ret; + + if (snd_usb_pipe_sanity_check(dev, usb_sndctrlpipe(dev, 0))) + return -EINVAL; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0xaf, USB_TYPE_VENDOR | USB_RECIP_DEVICE, 1, 0, NULL, 0, 1000); @@ -964,6 +970,8 @@ static int snd_usb_axefx3_boot_quirk(struct usb_device *dev) dev_dbg(&dev->dev, "Waiting for Axe-Fx III to boot up...\n"); + if (snd_usb_pipe_sanity_check(dev, usb_sndctrlpipe(dev, 0))) + return -EINVAL; /* If the Axe-Fx III has not fully booted, it will timeout when trying * to enable the audio streaming interface. A more generous timeout is * used here to detect when the Axe-Fx III has finished booting as the @@ -996,6 +1004,8 @@ static int snd_usb_motu_microbookii_communicate(struct usb_device *dev, u8 *buf, { int err, actual_length; + if (snd_usb_pipe_sanity_check(dev, usb_sndintpipe(dev, 0x01))) + return -EINVAL; err = usb_interrupt_msg(dev, usb_sndintpipe(dev, 0x01), buf, *length, &actual_length, 1000); if (err < 0) @@ -1006,6 +1016,8 @@ static int snd_usb_motu_microbookii_communicate(struct usb_device *dev, u8 *buf, memset(buf, 0, buf_size); + if (snd_usb_pipe_sanity_check(dev, usb_rcvintpipe(dev, 0x82))) + return -EINVAL; err = usb_interrupt_msg(dev, usb_rcvintpipe(dev, 0x82), buf, buf_size, &actual_length, 1000); if (err < 0) diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 7ee9d17d0143..e852c7fd6109 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -1043,6 +1043,7 @@ found_clock: pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (!pd) { + kfree(fp->chmap); kfree(fp->rate_table); kfree(fp); return NULL; diff --git a/sound/xen/xen_snd_front_alsa.c b/sound/xen/xen_snd_front_alsa.c index b14ab512c2ce..e01631959ed8 100644 --- a/sound/xen/xen_snd_front_alsa.c +++ b/sound/xen/xen_snd_front_alsa.c @@ -196,7 +196,7 @@ static u64 to_sndif_formats_mask(u64 alsa_formats) mask = 0; for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++) if (pcm_format_to_bits(ALSA_SNDIF_FORMATS[i].alsa) & alsa_formats) - mask |= 1 << ALSA_SNDIF_FORMATS[i].sndif; + mask |= BIT_ULL(ALSA_SNDIF_FORMATS[i].sndif); return mask; } @@ -208,7 +208,7 @@ static u64 to_alsa_formats_mask(u64 sndif_formats) mask = 0; for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++) - if (1 << ALSA_SNDIF_FORMATS[i].sndif & sndif_formats) + if (BIT_ULL(ALSA_SNDIF_FORMATS[i].sndif) & sndif_formats) mask |= pcm_format_to_bits(ALSA_SNDIF_FORMATS[i].alsa); return mask; |