diff options
Diffstat (limited to 'sound/firewire')
31 files changed, 192 insertions, 113 deletions
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 22b6c779682a..5973c25c2add 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -175,6 +175,8 @@ config SND_FIREWIRE_MOTU * 8pre * 828mk3 (FireWire only) * 828mk3 (Hybrid) + * 896mk3 (FireWire only) + * 896mk3 (Hybrid) * Ultralite mk3 (FireWire only) * Ultralite mk3 (Hybrid) * Traveler mk3 diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index 44a7b510b75b..45018a5c224f 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -2,9 +2,9 @@ # To find a header included by define_trace.h. CFLAGS_amdtp-stream.o := -I$(src) -snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ +snd-firewire-lib-y := lib.o iso-resources.o packets-buffer.o \ fcp.o cmp.o amdtp-stream.o amdtp-am824.o -snd-isight-objs := isight.o +snd-isight-y := isight.o obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o obj-$(CONFIG_SND_DICE) += dice/ diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index a13c0b408aad..7fc51f829ecc 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -77,6 +77,8 @@ // overrun. Actual device can skip more, then this module stops the packet streaming. #define IR_JUMBO_PAYLOAD_MAX_SKIP_CYCLES 5 +static void pcm_period_work(struct work_struct *work); + /** * amdtp_stream_init - initialize an AMDTP stream structure * @s: the AMDTP stream to initialize @@ -105,6 +107,7 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, s->flags = flags; s->context = ERR_PTR(-1); mutex_init(&s->mutex); + INIT_WORK(&s->period_work, pcm_period_work); s->packet_index = 0; init_waitqueue_head(&s->ready_wait); @@ -169,6 +172,9 @@ static int apply_constraint_to_size(struct snd_pcm_hw_params *params, step = max(step, amdtp_syt_intervals[i]); } + if (step == 0) + return -EINVAL; + t.min = roundup(s->min, step); t.max = rounddown(s->max, step); t.integer = 1; @@ -347,6 +353,7 @@ EXPORT_SYMBOL(amdtp_stream_get_max_payload); */ void amdtp_stream_pcm_prepare(struct amdtp_stream *s) { + cancel_work_sync(&s->period_work); s->pcm_buffer_pointer = 0; s->pcm_period_pointer = 0; } @@ -611,19 +618,37 @@ static void update_pcm_pointers(struct amdtp_stream *s, // The program in user process should periodically check the status of intermediate // buffer associated to PCM substream to process PCM frames in the buffer, instead // of receiving notification of period elapsed by poll wait. - if (!pcm->runtime->no_period_wakeup) { - if (in_softirq()) { - // In software IRQ context for 1394 OHCI. - snd_pcm_period_elapsed(pcm); - } else { - // In process context of ALSA PCM application under acquired lock of - // PCM substream. - snd_pcm_period_elapsed_under_stream_lock(pcm); - } - } + // + // Use another work item for period elapsed event to prevent the following AB/BA + // deadlock: + // + // thread 1 thread 2 + // ================================= ================================= + // A.work item (process) pcm ioctl (process) + // v v + // process_rx_packets() B.PCM stream lock + // process_tx_packets() v + // v callbacks in snd_pcm_ops + // update_pcm_pointers() v + // snd_pcm_elapsed() fw_iso_context_flush_completions() + // snd_pcm_stream_lock_irqsave() disable_work_sync() + // v v + // wait until release of B wait until A exits + if (!pcm->runtime->no_period_wakeup) + queue_work(system_highpri_wq, &s->period_work); } } +static void pcm_period_work(struct work_struct *work) +{ + struct amdtp_stream *s = container_of(work, struct amdtp_stream, + period_work); + struct snd_pcm_substream *pcm = READ_ONCE(s->pcm); + + if (pcm) + snd_pcm_period_elapsed(pcm); +} + static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params, bool sched_irq) { @@ -773,10 +798,14 @@ static int check_cip_header(struct amdtp_stream *s, const __be32 *buf, } else { 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; + if (!(s->flags & CIP_DBC_IS_PAYLOAD_QUADLETS)) { + if (*data_blocks > 0 && s->ctx_data.tx.dbc_interval > 0) + dbc_interval = s->ctx_data.tx.dbc_interval; + else + dbc_interval = *data_blocks; + } else { + dbc_interval = payload_length / sizeof(__be32); + } lost = dbc != ((*data_block_counter + dbc_interval) & 0xff); } @@ -951,7 +980,7 @@ static int generate_tx_packet_descs(struct amdtp_stream *s, struct pkt_desc *des // to the reason. unsigned int safe_cycle = increment_ohci_cycle_count(next_cycle, IR_JUMBO_PAYLOAD_MAX_SKIP_CYCLES); - lost = (compare_ohci_cycle_count(safe_cycle, cycle) > 0); + lost = (compare_ohci_cycle_count(safe_cycle, cycle) < 0); } if (lost) { dev_err(&s->unit->device, "Detect discontinuity of cycle: %d %d\n", @@ -1045,8 +1074,15 @@ static void generate_rx_packet_descs(struct amdtp_stream *s, struct pkt_desc *de static inline void cancel_stream(struct amdtp_stream *s) { + struct work_struct *work = current_work(); + s->packet_index = -1; - if (in_softirq()) + + // Detect work items for any isochronous context. The work item for pcm_period_work() + // should be avoided since the call of snd_pcm_period_elapsed() can reach via + // snd_pcm_ops.pointer() under acquiring PCM stream(group) lock and causes dead lock at + // snd_pcm_stop_xrun(). + if (work && work != &s->period_work) amdtp_stream_pcm_abort(s); WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN); } @@ -1176,13 +1212,10 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_ (void)fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &curr_cycle_time); for (i = 0; i < packets; ++i) { - struct { - struct fw_iso_packet params; - __be32 header[CIP_HEADER_QUADLETS]; - } template = { {0}, {0} }; + DEFINE_RAW_FLEX(struct fw_iso_packet, template, header, CIP_HEADER_QUADLETS); bool sched_irq = false; - build_it_pkt_header(s, desc->cycle, &template.params, pkt_header_length, + build_it_pkt_header(s, desc->cycle, template, pkt_header_length, desc->data_blocks, desc->data_block_counter, desc->syt, i, curr_cycle_time); @@ -1194,7 +1227,7 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_ } } - if (queue_out_packet(s, &template.params, sched_irq) < 0) { + if (queue_out_packet(s, template, sched_irq) < 0) { cancel_stream(s); return; } @@ -1848,11 +1881,11 @@ unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d, { struct amdtp_stream *irq_target = d->irq_target; - // Process isochronous packets queued till recent isochronous cycle to handle PCM frames. if (irq_target && amdtp_stream_running(irq_target)) { - // In software IRQ context, the call causes dead-lock to disable the tasklet - // synchronously. - if (!in_softirq()) + // The work item to call snd_pcm_period_elapsed() can reach here by the call of + // snd_pcm_ops.pointer(), however less packets would be available then. Therefore + // the following call is just for user process contexts. + if (current_work() != &s->period_work) fw_iso_context_flush_completions(irq_target->context); } @@ -1908,6 +1941,7 @@ static void amdtp_stream_stop(struct amdtp_stream *s) return; } + cancel_work_sync(&s->period_work); fw_iso_context_stop(s->context); fw_iso_context_destroy(s->context); s->context = ERR_PTR(-1); diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index b7ff44751ab9..775db3fc4959 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -37,6 +37,9 @@ * the value of current SYT_INTERVAL; e.g. initial value is not zero. * @CIP_UNAWARE_SYT: For outgoing packet, the value in SYT field of CIP is 0xffff. * For incoming packet, the value in SYT field of CIP is not handled. + * @CIP_DBC_IS_PAYLOAD_QUADLETS: Available for incoming packet, and only effective with + * CIP_DBC_IS_END_EVENT flag. The value of dbc field is the number of accumulated quadlets + * in CIP payload, instead of the number of accumulated data blocks. */ enum cip_flags { CIP_NONBLOCKING = 0x00, @@ -51,6 +54,7 @@ enum cip_flags { CIP_NO_HEADER = 0x100, CIP_UNALIGHED_DBC = 0x200, CIP_UNAWARE_SYT = 0x400, + CIP_DBC_IS_PAYLOAD_QUADLETS = 0x800, }; /** @@ -187,6 +191,7 @@ struct amdtp_stream { /* For a PCM substream processing. */ struct snd_pcm_substream *pcm; + struct work_struct period_work; snd_pcm_uframes_t pcm_buffer_pointer; unsigned int pcm_period_pointer; unsigned int pcm_frame_multiplier; diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile index 14bc84c51ef5..b913e805bd7a 100644 --- a/sound/firewire/bebob/Makefile +++ b/sound/firewire/bebob/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \ +snd-bebob-y := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \ bebob_pcm.o bebob_hwdep.o bebob_terratec.o \ bebob_yamaha_terratec.o bebob_focusrite.o bebob_maudio.o \ bebob.o diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index ce49eef0fcba..360ebf3c4ca2 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -367,6 +367,7 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) goto end; pcm->private_data = bebob; + pcm->nonatomic = true; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", bebob->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c index b596bec19774..f5028a061a91 100644 --- a/sound/firewire/cmp.c +++ b/sound/firewire/cmp.c @@ -333,53 +333,6 @@ retry_after_bus_reset: } EXPORT_SYMBOL(cmp_connection_establish); -/** - * cmp_connection_update - update the connection after a bus reset - * @c: the connection manager - * - * This function must be called from the driver's .update handler to - * reestablish any connection that might have been active. - * - * Returns zero on success, or a negative error code. On an error, the - * connection is broken and the caller must stop transmitting iso packets. - */ -int cmp_connection_update(struct cmp_connection *c) -{ - int err; - - mutex_lock(&c->mutex); - - if (!c->connected) { - mutex_unlock(&c->mutex); - return 0; - } - - err = fw_iso_resources_update(&c->resources); - if (err < 0) - goto err_unconnect; - - if (c->direction == CMP_OUTPUT) - err = pcr_modify(c, opcr_set_modify, pcr_set_check, - SUCCEED_ON_BUS_RESET); - else - err = pcr_modify(c, ipcr_set_modify, pcr_set_check, - SUCCEED_ON_BUS_RESET); - - if (err < 0) - goto err_unconnect; - - mutex_unlock(&c->mutex); - - return 0; - -err_unconnect: - c->connected = false; - mutex_unlock(&c->mutex); - - return err; -} -EXPORT_SYMBOL(cmp_connection_update); - static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr) { return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK); diff --git a/sound/firewire/cmp.h b/sound/firewire/cmp.h index 26ab88000e34..66fc08b742d2 100644 --- a/sound/firewire/cmp.h +++ b/sound/firewire/cmp.h @@ -47,7 +47,6 @@ int cmp_connection_reserve(struct cmp_connection *connection, 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); #endif diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile index bac8712f9014..36e25a3cf3c6 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 \ +snd-dice-y := 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-presonus.o \ dice-harman.o dice-focusrite.o dice-weiss.o diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index d64366217d57..2cf2adb48f2a 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -441,6 +441,7 @@ int snd_dice_create_pcm(struct snd_dice *dice) if (err < 0) return err; pcm->private_data = dice; + pcm->nonatomic = true; strcpy(pcm->name, dice->card->shortname); if (capture > 0) diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile index 8add0cd9af3a..6dc18bd2e186 100644 --- a/sound/firewire/digi00x/Makefile +++ b/sound/firewire/digi00x/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \ +snd-firewire-digi00x-y := amdtp-dot.o digi00x-stream.o digi00x-proc.o \ digi00x-pcm.o digi00x-hwdep.o \ digi00x-transaction.o digi00x-midi.o digi00x.o obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c index 3bd1575c9d9c..85e65cbc00c4 100644 --- a/sound/firewire/digi00x/digi00x-pcm.c +++ b/sound/firewire/digi00x/digi00x-pcm.c @@ -350,6 +350,7 @@ int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x) return err; pcm->private_data = dg00x; + pcm->nonatomic = true; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", dg00x->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); diff --git a/sound/firewire/fireface/Makefile b/sound/firewire/fireface/Makefile index 3aef221ce4b0..b397d95877a0 100644 --- a/sound/firewire/fireface/Makefile +++ b/sound/firewire/fireface/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-fireface-objs := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o \ +snd-fireface-y := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o \ ff-stream.o ff-pcm.o ff-hwdep.o ff-protocol-former.o \ ff-protocol-latter.o obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o diff --git a/sound/firewire/fireface/ff-pcm.c b/sound/firewire/fireface/ff-pcm.c index ec915671a79b..63457d24a288 100644 --- a/sound/firewire/fireface/ff-pcm.c +++ b/sound/firewire/fireface/ff-pcm.c @@ -390,6 +390,7 @@ int snd_ff_create_pcm_devices(struct snd_ff *ff) return err; pcm->private_data = ff; + pcm->nonatomic = true; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", ff->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c index efd59e9d9935..0907d0a2296f 100644 --- a/sound/firewire/fireface/ff-protocol-former.c +++ b/sound/firewire/fireface/ff-protocol-former.c @@ -135,13 +135,13 @@ static void dump_clock_config(struct snd_ff *ff, struct snd_info_buffer *buffer) snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n", (data & 0x00000020) ? "Professional" : "Consumer", - (data & 0x00000040) ? "on" : "off"); + str_on_off(data & 0x00000040)); snd_iprintf(buffer, "Optical output interface format: %s\n", (data & 0x00000100) ? "S/PDIF" : "ADAT"); snd_iprintf(buffer, "Word output single speed: %s\n", - (data & 0x00002000) ? "on" : "off"); + str_on_off(data & 0x00002000)); snd_iprintf(buffer, "S/PDIF input interface: %s\n", (data & 0x00000200) ? "Optical" : "Coaxial"); diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile index 3386121b2a04..baaf3066c9b1 100644 --- a/sound/firewire/fireworks/Makefile +++ b/sound/firewire/fireworks/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \ +snd-fireworks-y := fireworks_transaction.o fireworks_command.o \ fireworks_stream.o fireworks_proc.o fireworks_midi.o \ fireworks_pcm.o fireworks_hwdep.o fireworks.o obj-$(CONFIG_SND_FIREWORKS) += snd-fireworks.o diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c index c3c21860b245..eaf7778211de 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -397,6 +397,7 @@ int snd_efw_create_pcm_devices(struct snd_efw *efw) goto end; pcm->private_data = efw; + pcm->nonatomic = true; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", efw->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index 806f82c9ceee..b1e059f0d473 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -454,6 +454,7 @@ static int isight_create_pcm(struct isight *isight) if (err < 0) return err; pcm->private_data = isight; + pcm->nonatomic = true; strcpy(pcm->name, "iSight"); isight->pcm = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; isight->pcm->ops = &ops; diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile index 3bef2a0b1e2e..df0fe886dbc0 100644 --- a/sound/firewire/motu/Makefile +++ b/sound/firewire/motu/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 CFLAGS_amdtp-motu.o := -I$(src) -snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \ +snd-firewire-motu-y := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \ motu-proc.o motu-pcm.o motu-midi.o motu-hwdep.o \ motu-protocol-v2.o motu-protocol-v3.o \ motu-protocol-v1.o motu-register-dsp-message-parser.o \ diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c index d410c2efbde5..f3b48495acae 100644 --- a/sound/firewire/motu/motu-pcm.c +++ b/sound/firewire/motu/motu-pcm.c @@ -360,6 +360,7 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu) if (err < 0) return err; pcm->private_data = motu; + pcm->nonatomic = true; strcpy(pcm->name, motu->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); diff --git a/sound/firewire/motu/motu-protocol-v3.c b/sound/firewire/motu/motu-protocol-v3.c index 8a0426920a76..7254fdfe046a 100644 --- a/sound/firewire/motu/motu-protocol-v3.c +++ b/sound/firewire/motu/motu-protocol-v3.c @@ -261,6 +261,7 @@ int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu) if (motu->spec == &snd_motu_spec_828mk3_fw || motu->spec == &snd_motu_spec_828mk3_hybrid || + motu->spec == &snd_motu_spec_896mk3 || motu->spec == &snd_motu_spec_traveler_mk3 || motu->spec == &snd_motu_spec_track16) return detect_packet_formats_with_opt_ifaces(motu, data); @@ -288,6 +289,14 @@ const struct snd_motu_spec snd_motu_spec_828mk3_hybrid = { .rx_fixed_pcm_chunks = {14, 14, 14}, // Additional 4 dummy chunks at higher rate. }; +const struct snd_motu_spec snd_motu_spec_896mk3 = { + .name = "896mk3", + .protocol_version = SND_MOTU_PROTOCOL_V3, + .flags = SND_MOTU_SPEC_COMMAND_DSP, + .tx_fixed_pcm_chunks = {18, 14, 10}, + .rx_fixed_pcm_chunks = {18, 14, 10}, +}; + const struct snd_motu_spec snd_motu_spec_traveler_mk3 = { .name = "TravelerMk3", .protocol_version = SND_MOTU_PROTOCOL_V3, diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c index d73599eb7d5a..d14ab5dd5bea 100644 --- a/sound/firewire/motu/motu.c +++ b/sound/firewire/motu/motu.c @@ -168,10 +168,12 @@ static const struct ieee1394_device_id motu_id_table[] = { SND_MOTU_DEV_ENTRY(0x00000d, &snd_motu_spec_ultralite), SND_MOTU_DEV_ENTRY(0x00000f, &snd_motu_spec_8pre), SND_MOTU_DEV_ENTRY(0x000015, &snd_motu_spec_828mk3_fw), // FireWire only. + SND_MOTU_DEV_ENTRY(0x000017, &snd_motu_spec_896mk3), // FireWire only. SND_MOTU_DEV_ENTRY(0x000019, &snd_motu_spec_ultralite_mk3), // FireWire only. SND_MOTU_DEV_ENTRY(0x00001b, &snd_motu_spec_traveler_mk3), SND_MOTU_DEV_ENTRY(0x000030, &snd_motu_spec_ultralite_mk3), // Hybrid. SND_MOTU_DEV_ENTRY(0x000035, &snd_motu_spec_828mk3_hybrid), // Hybrid. + SND_MOTU_DEV_ENTRY(0x000037, &snd_motu_spec_896mk3), // Hybrid. SND_MOTU_DEV_ENTRY(0x000033, &snd_motu_spec_audio_express), SND_MOTU_DEV_ENTRY(0x000039, &snd_motu_spec_track16), SND_MOTU_DEV_ENTRY(0x000045, &snd_motu_spec_4pre), diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h index 3b1dc98a7be0..c66be0a89ccf 100644 --- a/sound/firewire/motu/motu.h +++ b/sound/firewire/motu/motu.h @@ -138,6 +138,7 @@ extern const struct snd_motu_spec snd_motu_spec_8pre; extern const struct snd_motu_spec snd_motu_spec_828mk3_fw; extern const struct snd_motu_spec snd_motu_spec_828mk3_hybrid; +extern const struct snd_motu_spec snd_motu_spec_896mk3; extern const struct snd_motu_spec snd_motu_spec_traveler_mk3; extern const struct snd_motu_spec snd_motu_spec_ultralite_mk3; extern const struct snd_motu_spec snd_motu_spec_audio_express; diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile index 669d1e8238df..9ac8893a926f 100644 --- a/sound/firewire/oxfw/Makefile +++ b/sound/firewire/oxfw/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-pcm.o oxfw-proc.o \ +snd-oxfw-y := oxfw-command.o oxfw-stream.o oxfw-pcm.o oxfw-proc.o \ oxfw-midi.o oxfw-hwdep.o oxfw-spkr.o oxfw-scs1x.o oxfw.o obj-$(CONFIG_SND_OXFW) += snd-oxfw.o diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c index 5f43a0b826d2..8ca9dde54ec6 100644 --- a/sound/firewire/oxfw/oxfw-pcm.c +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -440,6 +440,7 @@ int snd_oxfw_create_pcm(struct snd_oxfw *oxfw) return err; pcm->private_data = oxfw; + pcm->nonatomic = true; strcpy(pcm->name, oxfw->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); if (cap > 0) diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index f4a702def397..00f7feb91f92 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -177,6 +177,8 @@ static int init_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream) flags |= CIP_JUMBO_PAYLOAD; if (oxfw->quirks & SND_OXFW_QUIRK_WRONG_DBS) flags |= CIP_WRONG_DBS; + if (oxfw->quirks & SND_OXFW_QUIRK_DBC_IS_TOTAL_PAYLOAD_QUADLETS) + flags |= CIP_DBC_IS_END_EVENT | CIP_DBC_IS_PAYLOAD_QUADLETS; } else { conn = &oxfw->in_conn; c_dir = CMP_INPUT; @@ -486,26 +488,57 @@ int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir, struct snd_oxfw_stream_formation *formation) { - u8 *format; - unsigned int len; int err; - len = AVC_GENERIC_FRAME_MAXIMUM_BYTES; - format = kmalloc(len, GFP_KERNEL); - if (format == NULL) - return -ENOMEM; + if (!(oxfw->quirks & SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED)) { + u8 *format; + unsigned int len; - err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len); - if (err < 0) - goto end; - if (len < 3) { - err = -EIO; - goto end; + len = AVC_GENERIC_FRAME_MAXIMUM_BYTES; + format = kmalloc(len, GFP_KERNEL); + if (format == NULL) + return -ENOMEM; + + err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len); + if (err >= 0) { + if (len < 3) + err = -EIO; + else + err = snd_oxfw_stream_parse_format(format, formation); + } + + kfree(format); + } else { + // Miglia Harmony Audio does not support Extended Stream Format Information + // command. Use the duplicated hard-coded format, instead. + unsigned int rate; + u8 *const *formats; + int i; + + err = avc_general_get_sig_fmt(oxfw->unit, &rate, dir, 0); + if (err < 0) + return err; + + if (dir == AVC_GENERAL_PLUG_DIR_IN) + formats = oxfw->rx_stream_formats; + else + formats = oxfw->tx_stream_formats; + + for (i = 0; (i < SND_OXFW_STREAM_FORMAT_ENTRIES); ++i) { + if (!formats[i]) + continue; + + err = snd_oxfw_stream_parse_format(formats[i], formation); + if (err < 0) + continue; + + if (formation->rate == rate) + break; + } + if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) + return -EIO; } - err = snd_oxfw_stream_parse_format(format, formation); -end: - kfree(format); return err; } @@ -515,7 +548,7 @@ end: * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA) * Also 'Clause 12 AM824 sequence adaption layers' in IEC 61883-6:2005 */ -int snd_oxfw_stream_parse_format(u8 *format, +int snd_oxfw_stream_parse_format(const u8 *format, struct snd_oxfw_stream_formation *formation) { unsigned int i, e, channels, type; @@ -600,14 +633,33 @@ assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir, unsigned int i, eid; int err; - /* get format at current sampling rate */ - err = avc_stream_get_format_single(oxfw->unit, dir, pid, buf, len); - if (err < 0) { - dev_err(&oxfw->unit->device, - "fail to get current stream format for isoc %s plug %d:%d\n", - (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out", - pid, err); - goto end; + // get format at current sampling rate. + if (!(oxfw->quirks & SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED)) { + err = avc_stream_get_format_single(oxfw->unit, dir, pid, buf, len); + if (err < 0) { + dev_err(&oxfw->unit->device, + "fail to get current stream format for isoc %s plug %d:%d\n", + (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out", + pid, err); + goto end; + } + } else { + // Miglia Harmony Audio does not support Extended Stream Format Information + // command. Use the hard-coded format, instead. + buf[0] = 0x90; + buf[1] = 0x40; + buf[2] = avc_stream_rate_table[0]; + buf[3] = 0x00; + buf[4] = 0x01; + + if (dir == AVC_GENERAL_PLUG_DIR_IN) + buf[5] = 0x08; + else + buf[5] = 0x02; + + buf[6] = 0x06; + + *len = 7; } /* parse and set stream format */ diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index 241a697ce26b..98ae0e8cba87 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -21,6 +21,7 @@ #define VENDOR_TASCAM 0x00022e #define OUI_STANTON 0x001260 #define OUI_APOGEE 0x0003db +#define OUI_OXFORD 0x0030e0 #define MODEL_SATELLITE 0x00200f #define MODEL_SCS1M 0x001000 @@ -232,6 +233,11 @@ static int oxfw_probe(struct fw_unit *unit, const struct ieee1394_device_id *ent if (err < 0) goto error; + if (entry->vendor_id == OUI_OXFORD && entry->model_id == 0x00f970) { + oxfw->quirks |= SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED | + SND_OXFW_QUIRK_DBC_IS_TOTAL_PAYLOAD_QUADLETS; + } + err = snd_oxfw_stream_discover(oxfw); if (err < 0) goto error; @@ -330,6 +336,9 @@ static const struct ieee1394_device_id oxfw_id_table[] = { // OXFW_DEV_ENTRY(VENDOR_GRIFFIN, 0x00f970, &griffin_firewave), OXFW_DEV_ENTRY(VENDOR_LACIE, 0x00f970, &lacie_speakers), + // Miglia HarmonyAudio (HA02). The numeric vendor ID is ASIC vendor and the model ID is the + // default value of ASIC. + OXFW_DEV_ENTRY(OUI_OXFORD, 0x00f970, NULL), // Behringer,F-Control Audio 202. The value of SYT field is not reliable at all. OXFW_DEV_ENTRY(VENDOR_BEHRINGER, 0x00fc22, NULL), // Loud Technologies, Tapco Link.FireWire 4x6. The value of SYT field is always 0xffff. @@ -337,7 +346,6 @@ static const struct ieee1394_device_id oxfw_id_table[] = { // Loud Technologies, Mackie Onyx Satellite. Although revised version of firmware is // installed to avoid the postpone, the value of SYT field is always 0xffff. OXFW_DEV_ENTRY(VENDOR_LOUD, MODEL_SATELLITE, NULL), - // Miglia HarmonyAudio. Not yet identified. // // OXFW971 devices: diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index d728e451a25c..39ea9a6dde33 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -52,6 +52,11 @@ enum snd_oxfw_quirk { // performs media clock recovery voluntarily. In the recovery, the packets with NO_INFO // are ignored, thus driver should transfer packets with timestamp. SND_OXFW_QUIRK_VOLUNTARY_RECOVERY = 0x20, + // Miglia Harmony Audio does not support AV/C Stream Format Information command. + SND_OXFW_QUIRK_STREAM_FORMAT_INFO_UNSUPPORTED = 0x40, + // Miglia Harmony Audio transmits CIP in which the value of dbc field expresses the number + // of accumulated payload quadlets including the packet. + SND_OXFW_QUIRK_DBC_IS_TOTAL_PAYLOAD_QUADLETS = 0x80, }; /* This is an arbitrary number for convinience. */ @@ -136,7 +141,7 @@ struct snd_oxfw_stream_formation { unsigned int pcm; unsigned int midi; }; -int snd_oxfw_stream_parse_format(u8 *format, +int snd_oxfw_stream_parse_format(const u8 *format, struct snd_oxfw_stream_formation *formation); int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir, diff --git a/sound/firewire/tascam/Makefile b/sound/firewire/tascam/Makefile index a1d21f244d64..43fed14cf172 100644 --- a/sound/firewire/tascam/Makefile +++ b/sound/firewire/tascam/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -snd-firewire-tascam-objs := tascam-proc.o amdtp-tascam.o tascam-stream.o \ +snd-firewire-tascam-y := tascam-proc.o amdtp-tascam.o tascam-stream.o \ tascam-pcm.o tascam-hwdep.o tascam-transaction.o \ tascam-midi.o tascam.o obj-$(CONFIG_SND_FIREWIRE_TASCAM) += snd-firewire-tascam.o diff --git a/sound/firewire/tascam/amdtp-tascam.c b/sound/firewire/tascam/amdtp-tascam.c index 0b42d6559008..079afa4bd381 100644 --- a/sound/firewire/tascam/amdtp-tascam.c +++ b/sound/firewire/tascam/amdtp-tascam.c @@ -238,7 +238,7 @@ int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit, err = amdtp_stream_init(s, unit, dir, flags, fmt, process_ctx_payloads, sizeof(struct amdtp_tscm)); if (err < 0) - return 0; + return err; if (dir == AMDTP_OUT_STREAM) { // Use fixed value for FDF field. diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c index f6da571707ac..a73003ac11e6 100644 --- a/sound/firewire/tascam/tascam-pcm.c +++ b/sound/firewire/tascam/tascam-pcm.c @@ -279,6 +279,7 @@ int snd_tscm_create_pcm_devices(struct snd_tscm *tscm) return err; pcm->private_data = tscm; + pcm->nonatomic = true; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", tscm->card->shortname); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); |