Merge tag 'asoc-v5.3' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorTakashi Iwai <tiwai@suse.de>
Mon, 8 Jul 2019 12:45:20 +0000 (14:45 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 8 Jul 2019 12:45:34 +0000 (14:45 +0200)
ASoC: Updates for v5.3

This is a very big update, mainly thanks to Morimoto-san's refactoring
work and some fairly large new drivers.

 - Lots more work on moving towards a component based framework from
   Morimoto-san.
 - Support for force disconnecting muxes from Jerome Brunet.
 - New drivers for Cirrus Logic CS47L35, CS47L85 and CS47L90, Conexant
   CX2072X, Realtek RT1011 and RT1308.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
74 files changed:
1  2 
include/sound/hda_codec.h
sound/core/control.c
sound/core/seq/oss/seq_oss_ioctl.c
sound/core/seq/oss/seq_oss_rw.c
sound/firewire/amdtp-am824.c
sound/firewire/amdtp-stream-trace.h
sound/firewire/amdtp-stream.c
sound/firewire/bebob/bebob.h
sound/firewire/bebob/bebob_midi.c
sound/firewire/bebob/bebob_pcm.c
sound/firewire/bebob/bebob_stream.c
sound/firewire/cmp.c
sound/firewire/dice/Makefile
sound/firewire/dice/dice-midi.c
sound/firewire/dice/dice-pcm.c
sound/firewire/dice/dice-stream.c
sound/firewire/dice/dice.c
sound/firewire/dice/dice.h
sound/firewire/digi00x/amdtp-dot.c
sound/firewire/digi00x/digi00x-midi.c
sound/firewire/digi00x/digi00x-pcm.c
sound/firewire/digi00x/digi00x-stream.c
sound/firewire/digi00x/digi00x.h
sound/firewire/fireface/ff-pcm.c
sound/firewire/fireface/ff-stream.c
sound/firewire/fireface/ff.h
sound/firewire/fireworks/fireworks.h
sound/firewire/fireworks/fireworks_midi.c
sound/firewire/fireworks/fireworks_pcm.c
sound/firewire/fireworks/fireworks_stream.c
sound/firewire/motu/amdtp-motu-trace.h
sound/firewire/motu/amdtp-motu.c
sound/firewire/motu/motu-midi.c
sound/firewire/motu/motu-pcm.c
sound/firewire/motu/motu-stream.c
sound/firewire/motu/motu.h
sound/firewire/oxfw/oxfw-midi.c
sound/firewire/oxfw/oxfw-pcm.c
sound/firewire/oxfw/oxfw-stream.c
sound/firewire/oxfw/oxfw.c
sound/firewire/oxfw/oxfw.h
sound/firewire/tascam/amdtp-tascam.c
sound/firewire/tascam/tascam-pcm.c
sound/firewire/tascam/tascam-stream.c
sound/firewire/tascam/tascam.h
sound/hda/hdac_controller.c
sound/hda/hdac_device.c
sound/pci/asihpi/asihpi.c
sound/pci/cs4281.c
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_controller.c
sound/pci/hda/hda_controller.h
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_jack.c
sound/pci/hda/patch_ca0132.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/lx6464es/lx_core.c
sound/pci/rme9652/hdspm.c
sound/soc/codecs/hdac_hdmi.c
sound/soc/sof/intel/hda.c
sound/usb/format.c
sound/usb/helper.c
sound/usb/line6/driver.c
sound/usb/line6/driver.h
sound/usb/line6/pcm.c
sound/usb/line6/pod.c
sound/usb/line6/podhd.c
sound/usb/line6/toneport.c
sound/usb/line6/variax.c
sound/usb/mixer.c
sound/usb/mixer_quirks.c
sound/usb/quirks-table.h
sound/usb/quirks.c

index 2a1d1ad44d661ac8381971b6c7780d58eecbd3aa,c4299da22f93db75dfa129179498d2ee6cdd21b8..8f46ff3449d5a25d71954379e9c640b5f4333069
@@@ -1,21 -1,8 +1,8 @@@
+ /* SPDX-License-Identifier: GPL-2.0-or-later */
  /*
   * Universal Interface for Intel High Definition Audio Codec
   *
   * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
-  *
-  *  This program is free software; you can redistribute it and/or modify it
-  *  under the terms of the GNU General Public License as published by the Free
-  *  Software Foundation; either version 2 of the License, or (at your option)
-  *  any later version.
-  *
-  *  This program is distributed in the hope that it will be useful, but WITHOUT
-  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
-  *  more details.
-  *
-  *  You should have received a copy of the GNU General Public License along with
-  *  this program; if not, write to the Free Software Foundation, Inc., 59
-  *  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   */
  
  #ifndef __SOUND_HDA_CODEC_H
@@@ -31,6 -18,9 +18,9 @@@
  #include <sound/hda_verbs.h>
  #include <sound/hda_regmap.h>
  
+ #define IS_BXT(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x5a98)
+ #define IS_CFL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa348)
  /*
   * Structures
   */
@@@ -281,6 -271,9 +271,6 @@@ struct hda_codec 
        unsigned long jackpoll_interval; /* In jiffies. Zero means no poll, rely on unsol events */
        struct delayed_work jackpoll_work;
  
 -      /* jack detection */
 -      struct snd_array jacks;
 -
        int depop_delay; /* depop delay in ms, -1 for default delay time */
  
        /* fix-up list */
diff --combined sound/core/control.c
index 04eb1a15ffb401bc83f172939dcd60977389cee2,5be5b9b931bfefee5a2798775b7c33d02f42222e..7a4d8690ce41f4f0644dc007709fd071095439b7
@@@ -1,22 -1,7 +1,7 @@@
+ // SPDX-License-Identifier: GPL-2.0-or-later
  /*
   *  Routines for driver control interface
   *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
-  *
-  *
-  *   This program is free software; you can redistribute it and/or modify
-  *   it under the terms of the GNU General Public License as published by
-  *   the Free Software Foundation; either version 2 of the License, or
-  *   (at your option) any later version.
-  *
-  *   This program is distributed in the hope that it will be useful,
-  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
-  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  *   GNU General Public License for more details.
-  *
-  *   You should have received a copy of the GNU General Public License
-  *   along with this program; if not, write to the Free Software
-  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-  *
   */
  
  #include <linux/threads.h>
@@@ -211,12 -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;
  
index 7d72e3d48ad5e50f79403c16ed4d193e88199f86,96ad01fb668c3dae74799ea8b08326976825d90b..ccf682689ec951ef5234aebbc55b1bbf692371e7
@@@ -1,23 -1,10 +1,10 @@@
+ // SPDX-License-Identifier: GPL-2.0-or-later
  /*
   * OSS compatible sequencer driver
   *
   * OSS compatible i/o control
   *
   * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
-  *
-  * This program is free software; you can redistribute it and/or modify
-  * it under the terms of the GNU General Public License as published by
-  * the Free Software Foundation; either version 2 of the License, or
-  * (at your option) any later version.
-  *
-  * This program is distributed in the hope that it will be useful,
-  * but WITHOUT ANY WARRANTY; without even the implied warranty of
-  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  * GNU General Public License for more details.
-  *
-  * You should have received a copy of the GNU General Public License
-  * along with this program; if not, write to the Free Software
-  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   */
  
  #include "seq_oss_device.h"
@@@ -62,7 -49,7 +49,7 @@@ static int snd_seq_oss_oob_user(struct 
        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);
index 1063e1b16ea0f1f54119b96ca1d330edcc693c76,79ef430e56e1798d960ee63b7b1adc9bba56bd43..537d5f423e206b075743129cee071cdeee938645
@@@ -1,23 -1,10 +1,10 @@@
+ // SPDX-License-Identifier: GPL-2.0-or-later
  /*
   * OSS compatible sequencer driver
   *
   * read/write/select interface to device file
   *
   * Copyright (C) 1998,99 Takashi Iwai <tiwai@suse.de>
-  *
-  * This program is free software; you can redistribute it and/or modify
-  * it under the terms of the GNU General Public License as published by
-  * the Free Software Foundation; either version 2 of the License, or
-  * (at your option) any later version.
-  *
-  * This program is distributed in the hope that it will be useful,
-  * but WITHOUT ANY WARRANTY; without even the implied warranty of
-  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  * GNU General Public License for more details.
-  *
-  * You should have received a copy of the GNU General Public License
-  * along with this program; if not, write to the Free Software
-  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   */
  
  #include "seq_oss_device.h"
@@@ -174,7 -161,7 +161,7 @@@ insert_queue(struct seq_oss_devinfo *dp
        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 */
index 623d014c0e7ebb6c4aa36d9d137e22742b9a87fc,cc6eb30f03a2acb29ad994fbca28c5c4768bb37d..fd5d6b8ac557aa57035ddd66b0a83ba7dc7ea8bd
@@@ -1,10 -1,9 +1,9 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * AM824 format in Audio and Music Data Transmission Protocol (IEC 61883-6)
   *
   * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
   * Copyright (c) 2015 Takashi Sakamoto <o-takashi@sakamocchi.jp>
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include <linux/slab.h>
@@@ -83,7 -82,7 +82,7 @@@ int amdtp_am824_set_parameters(struct a
        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;
@@@ -321,7 -320,7 +320,7 @@@ static void read_midi_messages(struct a
        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;
index ab708857979fc7a618f81fc20c45bb6d2ff41ee1,edb5c3afa6f8c7ba5ece70f34ee51efba2657825..4adbbf789cbe80c1fd2040e9685d79dde806f154
@@@ -1,8 -1,8 +1,8 @@@
+ /* SPDX-License-Identifier: GPL-2.0-only */
  /*
   * amdtp-stream-trace.h - tracepoint definitions to dump a part of packet data
   *
   * Copyright (c) 2016 Takashi Sakamoto
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #undef TRACE_SYSTEM
  
  #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)
                __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,
                __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
index 91b8902418400f886afcd6a7bf797cb6f765af99,68f5fa4b183d64f5838c905666349fba1c632368..87a46bd604962a6bc44df21da681e45ac498d9cc
@@@ -1,9 -1,9 +1,9 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * Audio and Music Data Transmission Protocol (IEC 61883-6) streams
   * with Common Isochronous Packet (IEC 61883-1) headers
   *
   * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include <linux/device.h>
  #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);
  
  /**
@@@ -265,18 -260,11 +265,18 @@@ int amdtp_stream_set_parameters(struct 
        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;
  }
@@@ -292,15 -280,15 +292,15 @@@ EXPORT_SYMBOL(amdtp_stream_set_paramete
  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);
  
@@@ -333,10 -321,10 +333,10 @@@ static unsigned int calculate_data_bloc
        /* 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
                                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;
                }
        }
  
@@@ -367,10 -355,9 +367,10 @@@ static unsigned int calculate_syt(struc
  {
        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:
                 *   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;
  
@@@ -433,15 -420,23 +433,15 @@@ static void pcm_period_tasklet(unsigne
                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);
@@@ -455,81 -450,112 +455,81 @@@ 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;
 +      __be32 *cip_header;
  
 -      payload_length = data_blocks * 4 * s->data_block_quadlets;
 +      if (s->flags & CIP_DBC_IS_END_EVENT) {
 +              s->data_block_counter =
 +                              (s->data_block_counter + data_blocks) & 0xff;
 +      }
  
 -      trace_out_packet_without_header(s, cycle, payload_length, data_blocks,
 -                                      index);
 +      if (!(s->flags & CIP_NO_HEADER)) {
 +              cip_header = (__be32 *)params->header;
 +              generate_cip_header(s, cip_header, syt);
 +              params->header_length = 2 * sizeof(__be32);
 +      } else {
 +              cip_header = NULL;
 +      }
  
 -      if (queue_out_packet(s, payload_length) < 0)
 -              return -EIO;
 +      if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
 +              s->data_block_counter =
 +                              (s->data_block_counter + data_blocks) & 0xff;
 +      }
  
 -      pcm = READ_ONCE(s->pcm);
 -      if (pcm && pcm_frames > 0)
 -              update_pcm_pointers(s, pcm, pcm_frames);
 +      params->payload_length =
 +                      data_blocks * sizeof(__be32) * s->data_block_quadlets;
  
 -      /* No need to return the number of handled data blocks. */
 -      return 0;
 +      trace_amdtp_packet(s, cycle, cip_header, params->payload_length,
 +                         data_blocks, index);
  }
  
 -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'.
                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. */
                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,
                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 *dbc,
 +                             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;
 +      const __be32 *cip_header;
 +      int err;
  
 -      buffer = s->buffer.packets[s->packet_index].buffer;
 -      payload_quadlets = payload_length / 4;
 -      data_blocks = payload_quadlets / s->data_block_quadlets;
 +      *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;
 +      }
  
 -      trace_in_packet_without_header(s, cycle, payload_quadlets, data_blocks,
 -                                     index);
 +      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) {
 +                      if (err != -EAGAIN)
 +                              return err;
  
 -      pcm_frames = s->process_data_blocks(s, buffer, data_blocks, NULL);
 -      s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
 +                      *data_blocks = 0;
 +              }
 +      } else {
 +              cip_header = NULL;
 +              err = 0;
 +              *data_blocks = *payload_length / sizeof(__be32) /
 +                             s->data_block_quadlets;
 +              *dbc = s->data_block_counter;
 +              *syt = 0;
 +      }
  
 -      if (queue_in_packet(s) < 0)
 -              return -EIO;
 +      if (err >= 0 && s->flags & CIP_DBC_IS_END_EVENT)
 +              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);
  }
  
@@@ -674,66 -702,31 +674,66 @@@ static inline u32 increment_cycle_count
        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 i, packets = header_length / sizeof(*ctx_header);
  
        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_block;
 +              __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_block = calculate_data_blocks(s, syt);
 +              buffer = s->buffer.packets[s->packet_index].buffer;
 +              pcm_frames = s->process_data_blocks(s, buffer, data_block, &syt);
 +
 +              build_it_pkt_header(s, cycle, &template.params, data_block, 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);
@@@ -745,56 -738,46 +745,56 @@@ static void in_stream_callback(struct f
  {
        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 dbc;
 +              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, &dbc, &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 =
 +                                              (dbc + data_blocks) & 0xff;
 +                      }
                }
  
 -              if (s->handle_packet(s, payload_length, cycle, i) < 0)
 +              if (queue_in_packet(s, &params) < 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;
        }
  
@@@ -807,8 -790,9 +807,8 @@@ static void amdtp_stream_first_callback
                                        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.
        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;
@@@ -847,7 -841,7 +847,7 @@@ int amdtp_stream_start(struct amdtp_str
        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 },
                [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;
  
                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)
  
        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;
  
        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, &params);
 +              } else {
 +                      params.header_length = 0;
 +                      params.payload_length = 0;
 +                      err = queue_out_packet(s, &params);
 +              }
                if (err < 0)
                        goto err_context;
        } while (s->packet_index > 0);
index 20ed2dbd8d6121f0c515d3fee1d71173a9999285,af71dac9f084080026598528dbf28f9ad56adca5..9e0b689fe34a3796cfc8573011b2d9f9d8b447e6
@@@ -1,9 -1,8 +1,8 @@@
+ /* SPDX-License-Identifier: GPL-2.0-only */
  /*
   * bebob.h - a part of driver for BeBoB based devices
   *
   * Copyright (c) 2013-2014 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #ifndef SOUND_BEBOB_H_INCLUDED
@@@ -93,6 -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;
@@@ -216,8 -217,7 +215,8 @@@ int snd_bebob_stream_get_clock_src(stru
                                   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);
  
index f903271e93d0602c3ac5027f772bb0b3eea8e444,c54ac42622ad5deab59621ed27c19d7397add1dc..4d8805fa8a00a1f06ea5115aaaa89802ac685d52
@@@ -1,38 -1,64 +1,37 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * bebob_midi.c - a part of driver for BeBoB based devices
   *
   * Copyright (c) 2013-2014 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #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;
  
@@@ -94,13 -120,13 +93,13 @@@ static void set_midi_substream_names(st
  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;
index e21de44deaa91682015c814e72551c6b58fd5b48,2f50ec7b0147e368ca768d058cfba52de0f3730b..0fb9eed468378e53643f4f76425a784cf1281be9
@@@ -1,9 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * bebob_pcm.c - a part of driver for BeBoB based devices
   *
   * Copyright (c) 2013-2014 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include "./bebob.h"
@@@ -185,8 -184,9 +184,8 @@@ pcm_close(struct snd_pcm_substream *sub
        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;
                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);
  }
  
@@@ -229,9 -260,10 +228,9 @@@ static in
  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);
  
@@@ -241,9 -273,10 +240,9 @@@ static in
  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);
  
@@@ -320,8 -353,8 +319,8 @@@ int snd_bebob_create_pcm_devices(struc
                .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,
                .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,
index 63e78fc8711de81e41d40a0237bae6242a497a8c,0c93a825cb989904572c4856a742c3379d253b41..334dc7c96e1d5131b08a05b23cfc52649c97ce15
@@@ -1,9 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * bebob_stream.c - a part of driver for BeBoB based devices
   *
   * Copyright (c) 2013-2014 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include "./bebob.h"
@@@ -376,6 -375,24 +375,6 @@@ end
        return err;
  }
  
 -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)
  {
        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
@@@ -423,13 -468,23 +422,13 @@@ break_both_connections(struct snd_bebo
        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;
        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);
 +}
  
 -      err = amdtp_am824_init(&bebob->rx_stream, bebob->unit,
 -                             AMDTP_OUT_STREAM, CIP_BLOCKING);
 +int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
 +{
 +      int err;
 +
 +      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);
        }
  }
  
   */
  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 --combined sound/firewire/cmp.c
index 5dedc4f31842d3902bb7dc2745ef31459d2af2d1,13f8abc19cfb10e51f94b48c428230c56395a127..14abbe7175b69b0400c33c327ae8c04c106ca110
@@@ -1,8 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * Connection Management Procedures (IEC 61883-1) helper functions
   *
   * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include <linux/device.h>
@@@ -185,37 -185,6 +185,37 @@@ void cmp_connection_destroy(struct cmp_
  }
  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)
  {
@@@ -301,18 -270,25 +301,18 @@@ static int pcr_set_check(struct cmp_con
   * 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);
                                 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;
@@@ -367,12 -351,14 +367,12 @@@ int cmp_connection_update(struct cmp_co
                                 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);
@@@ -409,6 -395,8 +409,6 @@@ void cmp_connection_break(struct cmp_co
        if (err < 0)
                cmp_error(c, "plug is still connected\n");
  
 -      fw_iso_resources_free(&c->resources);
 -
        c->connected = false;
  
        mutex_unlock(&c->mutex);
index 7efca3648c6afc4a7285907edf43b0d228d65a5a,115eadd8d42e7d289280c5f84d24299347fb6735..7a62dafd0f788e59f2e88ec25f1c7e9a25e5b833
@@@ -1,4 -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
index 30a0b8200160e94f6a470dc0d3970277c1a82595,ca7ae427e8927e74d798b7b5906384f58feceaba..c9e19bddfc09e812085a5771b3f3b7db876ceaac
@@@ -1,9 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * dice_midi.c - a part of driver for Dice based devices
   *
   * Copyright (c) 2014 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  #include "dice.h"
  
@@@ -18,13 -17,8 +17,13 @@@ static int midi_open(struct snd_rawmidi
  
        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);
  
@@@ -40,7 -34,7 +39,7 @@@ static int midi_close(struct snd_rawmid
  
        mutex_lock(&dice->mutex);
  
 -      dice->substreams_counter--;
 +      --dice->substreams_counter;
        snd_dice_stream_stop_duplex(dice);
  
        mutex_unlock(&dice->mutex);
index 8368073f7fa0a483d82bed4a4a2a53a24f7275b5,8a601befc11e93214f425bf288972a61ec2befe5..94a4dccfc381b9ed5b76a810597843b9cc9c4768
@@@ -1,10 -1,9 +1,9 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * dice_pcm.c - a part of driver for DICE based devices
   *
   * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
   * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include "dice.h"
@@@ -231,8 -230,8 +230,8 @@@ static int pcm_close(struct snd_pcm_sub
        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;
                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);
  
@@@ -278,7 -308,7 +277,7 @@@ static int capture_prepare(struct snd_p
        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);
@@@ -292,7 -322,7 +291,7 @@@ static int playback_prepare(struct snd_
        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);
@@@ -374,8 -404,8 +373,8 @@@ int snd_dice_create_pcm(struct snd_dic
                .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,
                .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,
index 433714a117a0b77f020d8640d717e2d0067433e0,7a93ae3dc58b51ee44cb7a30046cb379d33b78a2..a9f0c77734c35a768aa02ad0dbc2b23c5b0c564e
@@@ -1,10 -1,9 +1,9 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * dice_stream.c - a part of driver for DICE based devices
   *
   * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
   * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include "dice.h"
@@@ -138,9 -137,18 +137,9 @@@ static int get_register_params(struct s
  
  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]);
        }
@@@ -155,14 -163,10 +154,14 @@@ static void stop_streams(struct snd_dic
        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,
                                        &reg, sizeof(reg));
                } else {
 +                      amdtp_stream_stop(&dice->rx_stream[i]);
 +
                        snd_dice_transaction_write_rx(dice,
                                        params->size * i + RX_ISOCHRONOUS,
                                        &reg, sizeof(reg));
        }
  }
  
 -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;
                                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,
                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",
                        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;
 +
 +              // 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;
  
 -      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;
 +              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,
 +                                      &reg, sizeof(reg));
 +              } else {
 +                      err = snd_dice_transaction_write_rx(dice,
 +                                      params->size * i + RX_ISOCHRONOUS,
 +                                      &reg, 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,
 +                                      &reg, 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;
                    !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;
  }
  
  /*
@@@ -485,12 -445,17 +484,12 @@@ void snd_dice_stream_stop_duplex(struc
  {
        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,
index ac600e061d7bd60f3e77c3b007e7019e78b7b0a5,ea829cee9aafb300736480c0b232514df47d43e7..13eeb3f52bb613785088759fd7c613ba3546efaf
@@@ -1,8 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * TC Applied Technologies Digital Interface Communications Engine driver
   *
   * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include "dice.h"
@@@ -19,7 -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
@@@ -372,14 -371,6 +372,14 @@@ static const struct ieee1394_device_id 
                .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,
index fd3f483283d5018817bf0649218c62be4155857b,eb4fb8bae2ad2807dc73a6780644a1340c9ad8f0..c6304e5e9fc4a3da8aaad1af996f1f7058f924c9
@@@ -1,10 -1,9 +1,9 @@@
+ /* SPDX-License-Identifier: GPL-2.0-only */
  /*
   * dice.h - a part of driver for Dice based devices
   *
   * Copyright (c) Clemens Ladisch
   * Copyright (c) 2014 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #ifndef SOUND_DICE_H_INCLUDED
@@@ -205,11 -204,10 +204,11 @@@ extern const unsigned int snd_dice_rate
  
  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);
  
@@@ -228,6 -226,5 +227,6 @@@ int snd_dice_detect_tcelectronic_format
  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
index 3fb1997dca30d6a160baaf13fad2215d4e03d317,10c8803d7f19d4f582ca97b0ac3ab7d8254bbdfd..45ff73d16074429d10171091204f63553b930608
@@@ -1,11 -1,10 +1,10 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * amdtp-dot.c - a part of driver for Digidesign Digi 002/003 family
   *
   * Copyright (c) 2014-2015 Takashi Sakamoto
   * Copyright (C) 2012 Robin Gareus <robin@gareus.org>
   * Copyright (C) 2012 Damien Zammit <damien@zamaudio.com>
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include <sound/pcm.h>
@@@ -128,7 -127,7 +127,7 @@@ int amdtp_dot_set_parameters(struct amd
        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;
  
index 0563701141b821fe4df86ed2153443cb93fe91dc,bf50a168087f4b5e08622ca001ad1f96d88ac4f2..2b57ece891018814e69868396df6f48db0ee1253
@@@ -1,9 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * digi00x-midi.h - a part of driver for Digidesign Digi 002/003 family
   *
   * Copyright (c) 2014-2015 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include "digi00x.h"
@@@ -18,13 -17,8 +17,13 @@@ static int midi_open(struct snd_rawmidi
                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);
@@@ -37,7 -31,7 +36,7 @@@ static int midi_close(struct snd_rawmid
        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);
  
index c38fbd6ded9f0ff31891b6061b2b894dce1e3b41,4f637f227513903d08ad72aecbb635a77fb3c011..18e561b26625d63fed15b1a0a2eee555c486fe11
@@@ -1,9 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * digi00x-pcm.c - a part of driver for Digidesign Digi 002/003 family
   *
   * Copyright (c) 2014-2015 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include "digi00x.h"
@@@ -155,8 -154,8 +154,8 @@@ static int pcm_close(struct snd_pcm_sub
        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;
                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);
  
  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);
  
  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);
@@@ -299,8 -332,8 +298,8 @@@ int snd_dg00x_create_pcm_devices(struc
                .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,
                .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,
index 7c8e7ad48d03cf5dcc939d79498cd2ba795c06f8,ac8052c66b6f8704724efc184c38c3c674d7e295..3e77dbd3ee2281793d9deb9806f11d305e100644
@@@ -1,9 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family
   *
   * Copyright (c) 2014-2015 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include "digi00x.h"
@@@ -125,25 -124,11 +124,25 @@@ int snd_dg00x_stream_get_external_rate(
  
  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)
        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)
                                         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;
        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)
@@@ -258,68 -272,43 +257,68 @@@ void snd_dg00x_stream_destroy_duplex(st
        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;
                        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)
index 2d026b5b00797c0a6c9b43760da6775dc26b9342,464e6d3d82a83591686bc34e10d854a852c853b3..0994d191ccda49cf9ccd15a4e4f03a5ac7f4ec02
@@@ -1,9 -1,8 +1,8 @@@
+ /* SPDX-License-Identifier: GPL-2.0-only */
  /*
   * digi00x.h - a part of driver for Digidesign Digi 002/003 family
   *
   * Copyright (c) 2014-2015 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #ifndef SOUND_DIGI00X_H_INCLUDED
@@@ -140,8 -139,7 +139,8 @@@ int snd_dg00x_stream_get_clock(struct s
  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);
index 75228e769687a9b0be1586bbedd1d73a18a20c4b,0d40bb68db50fbcea80f7847a16d87d08f584d4e..9eab3ad283ce4a4ffc7365e99343c11321a105e7
@@@ -1,9 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * ff-pcm.c - a part of driver for RME Fireface series
   *
   * Copyright (c) 2015-2017 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include "ff.h"
@@@ -199,8 -198,8 +198,8 @@@ static int pcm_close(struct snd_pcm_sub
        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;
                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);
  
@@@ -343,8 -374,8 +342,8 @@@ int snd_ff_create_pcm_devices(struct sn
                .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,
                .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,
index 049920a6d43ed631b8596b7e90ac78fc8ca163e4,6dfd2efb6646fd53839aa4e6720e579a2207d5d7..4208b8004d1acd9a36ada24e152f53781af9a0a9
@@@ -1,9 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * ff-stream.c - a part of driver for RME Fireface series
   *
   * Copyright (c) 2015-2017 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include "ff.h"
@@@ -31,11 -30,14 +30,11 @@@ int snd_ff_stream_get_multiplier_mode(e
        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);
  }
@@@ -101,25 -103,37 +100,25 @@@ void snd_ff_stream_destroy_duplex(struc
        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;
                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;
  
        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)
index 9b52c368f6de4037cf3c0a0ae73d2fc468e26039,7fac241c2486fcf6de9008bfa0d8625de7d0b496..36dd0c75b9f713c5dace547a8de358240d44dfa9
@@@ -1,9 -1,8 +1,8 @@@
+ /* SPDX-License-Identifier: GPL-2.0-only */
  /*
   * ff.h - a part of driver for RME Fireface series
   *
   * Copyright (c) 2015-2017 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #ifndef SOUND_FIREFACE_H_INCLUDED
@@@ -113,7 -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);
@@@ -138,7 -136,6 +137,7 @@@ int snd_ff_stream_get_multiplier_mode(e
                                      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);
index 0c1802aa79232f704057ca468b833827dfaed979,28df49c3542a0630534dc78e870e63671921e7eb..31efd4b53b4f52d174c471c410f80b41b1e68b3d
@@@ -1,10 -1,9 +1,9 @@@
+ /* SPDX-License-Identifier: GPL-2.0-only */
  /*
   * fireworks.h - a part of driver for Fireworks based devices
   *
   * Copyright (c) 2009-2010 Clemens Ladisch
   * Copyright (c) 2013-2014 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  #ifndef SOUND_FIREWORKS_H_INCLUDED
  #define SOUND_FIREWORKS_H_INCLUDED
@@@ -89,7 -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,8 -206,7 +205,8 @@@ int snd_efw_command_get_sampling_rate(s
  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);
index fd17777981e41207044705cadc03c806d2f8f4e6,14b985c4f30481f285c358f425c48b2f751f2296..a9f4a9630d15e4d423362329ebf8dce4ac6d1817
@@@ -1,14 -1,13 +1,13 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * fireworks_midi.c - a part of driver for Fireworks based devices
   *
   * Copyright (c) 2009-2010 Clemens Ladisch
   * Copyright (c) 2013-2014 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  #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;
                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);
@@@ -32,12 -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);
  
@@@ -93,13 -120,13 +92,13 @@@ static void set_midi_substream_names(st
  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;
index 8dc34249a1b0481c43583c399039de7babdd69b7,affc50fe2e8eb93a63711810e73139ac38914b25..a7025dccc75489cefde6340b6b5d124cb7a3ba91
@@@ -1,10 -1,9 +1,9 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * fireworks_pcm.c - a part of driver for Fireworks based devices
   *
   * Copyright (c) 2009-2010 Clemens Ladisch
   * Copyright (c) 2013-2014 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  #include "./fireworks.h"
  
@@@ -219,7 -218,7 +218,7 @@@ static int pcm_close(struct snd_pcm_sub
        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;
                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);
  
  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);
  
@@@ -348,8 -377,8 +347,8 @@@ int snd_efw_create_pcm_devices(struct s
                .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,
                .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,
index 16cf635a6f5767d23759720d7e61e7164748ce5c,2d30954124272c750fc0b9f655a151299adbd094..e659a0b89ba5b0c784185cc65a049b94dd94b610
@@@ -1,9 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * fireworks_stream.c - a part of driver for Fireworks based devices
   *
   * Copyright (c) 2013-2014 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  #include "./fireworks.h"
  
@@@ -43,6 -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)
                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;
  }
  
  /*
@@@ -147,13 -164,13 +146,13 @@@ int snd_efw_stream_init_duplex(struct s
            (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) {
        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);
        }
  }
  
index edc551d4ca500666b902c178b9823ab43577c994,4d2351c0e8a3e2998274fb679e7c91b76f370058..3d36f125cf6a8eafe32a74e9ce04f913a4416f85
@@@ -1,8 -1,8 +1,8 @@@
+ /* SPDX-License-Identifier: GPL-2.0-only */
  /*
   * amdtp-motu-trace.h - tracepoint definitions to dump a part of packet data
   *
   * Copyright (c) 2017 Takashi Sakamoto
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #undef TRACE_SYSTEM
@@@ -18,7 -18,7 +18,7 @@@ static void copy_sph(u32 *frame, __be3
  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(
                __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);
        ),
        )
  );
  
 -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(
                __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);
        ),
index 1c9ce04a2e899f61ae37c1d868d94650107058fc,782d1fa024ecc4b9b41f8e0afc9e23b2824f0bd4..7973dedd31ef3006399b2c0c1a38033ce141d9fc
@@@ -1,9 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * amdtp-motu.c - a part of driver for MOTU FireWire series
   *
   * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include <linux/slab.h>
@@@ -306,8 -305,8 +305,8 @@@ static unsigned int process_tx_data_blo
        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);
@@@ -384,8 -383,8 +383,8 @@@ static unsigned int process_rx_data_blo
  
        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;
  }
@@@ -429,7 -428,7 +428,7 @@@ int amdtp_motu_init(struct amdtp_strea
                return err;
  
        s->sph = 1;
 -      s->fdf = MOTU_FDF_AM824;
 +      s->ctx_data.rx.fdf = MOTU_FDF_AM824;
  
        return 0;
  }
index 5c77e417c627e737318e0d183dcaa9dec9693da7,75f6b2e9ca9e2390ce1404ef1e957c6e95aa040e..46a0035df31e5ea573df1af852bed26c92024fd9
@@@ -1,13 -1,12 +1,12 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * motu-midi.h - a part of driver for MOTU FireWire series
   *
   * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  #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;
  
        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);
  
        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);
@@@ -97,13 -128,13 +96,13 @@@ static void set_midi_substream_names(st
  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;
index 60c691d189524b7ec6bd389d78d803a12ede65c4,5e7db7aa4f08ea7d48384f766821ba7923258e94..aa2e584da6fe65bdbf37576ea9718807f8066343
@@@ -1,9 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * motu-pcm.c - a part of driver for MOTU FireWire series
   *
   * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include <sound/pcm_params.h>
@@@ -190,8 -189,8 +189,8 @@@ static int pcm_close(struct snd_pcm_sub
        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;
                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);
  
@@@ -236,7 -266,7 +235,7 @@@ static int capture_prepare(struct snd_p
        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);
@@@ -249,7 -279,7 +248,7 @@@ static int playback_prepare(struct snd_
        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);
@@@ -325,8 -355,8 +324,8 @@@ int snd_motu_create_pcm_devices(struct 
                .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,
                .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,
index 6744d62623158490fb8fa346ca9034d4458f745b,81f7edc560d0490a5d9ce93d632c0757b535d7bf..2bbb335e8de199f3ba0ba00be859bc6ceb8a07aa
@@@ -1,9 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * motu-stream.c - a part of driver for MOTU FireWire series
   *
   * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include "motu.h"
  #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, &reg,
                                        sizeof(reg));
        if (err < 0)
@@@ -83,7 -83,7 +82,7 @@@
                                          sizeof(reg));
  }
  
 -static void stop_both_streams(struct snd_motu *motu)
 +static void finish_session(struct snd_motu *motu)
  {
        __be32 reg;
        u32 data;
@@@ -93,9 -93,6 +92,9 @@@
        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, &reg,
                                        sizeof(reg));
        if (err < 0)
        reg = cpu_to_be32(data);
        snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
                                   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)
        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;
        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;
                                          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);
                        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);
                }
        }
  
 -      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;
                }
        }
        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);
        }
  }
  
@@@ -370,7 -371,8 +369,7 @@@ void snd_motu_stream_destroy_duplex(str
        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)
index a4ac320f2c9ef976e71689bcece8d323906def36,7c795294428d17f1561a0d82938f692994de4329..09d1451d7de49eda67a456d1b4f64a466f1407ed
@@@ -1,9 -1,8 +1,8 @@@
+ /* SPDX-License-Identifier: GPL-2.0-only */
  /*
   * motu.h - a part of driver for MOTU FireWire series
   *
   * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #ifndef SOUND_FIREWIRE_MOTU_H_INCLUDED
@@@ -60,7 -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,8 -153,7 +152,8 @@@ void snd_motu_transaction_unregister(st
  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);
index 92538ab06932e8c042f3b208f7c816b1717e41e0,cbce01308bd1c5261e444ef396d18050fb49af08..9bdec08cb8eac0de6605c7832cb067043342c362
@@@ -1,9 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * oxfw_midi.c - a part of driver for OXFW970/971 based devices
   *
   * Copyright (c) 2014 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include "oxfw.h"
@@@ -19,13 -18,8 +18,13 @@@ static int midi_capture_open(struct snd
  
        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);
  
@@@ -46,11 -40,8 +45,11 @@@ static int midi_playback_open(struct sn
  
        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);
  
@@@ -66,8 -57,8 +65,8 @@@ static int midi_capture_close(struct sn
  
        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);
  
@@@ -81,8 -72,8 +80,8 @@@ static int midi_playback_close(struct s
  
        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);
  
index b08b850d53ea7aa276f2b66c7a1d993ca61ac6a5,94f367cdfdf3bdb89da6debf5d846c03c02e2d7b..9ea39348cdf5623db8c1a8a73fd9a26ecadee095
@@@ -1,8 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * oxfw_pcm.c - a part of driver for OXFW970/971 based devices
   *
   * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include "oxfw.h"
@@@ -219,18 -219,12 +219,18 @@@ static int pcm_capture_hw_params(struc
                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)
                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->tx_stream,
 +                                                   rate, channels);
 +              if (err >= 0)
 +                      ++oxfw->substreams_count;
                mutex_unlock(&oxfw->mutex);
        }
  
@@@ -265,9 -253,9 +265,9 @@@ static int pcm_capture_hw_free(struct s
        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);
  
@@@ -280,9 -268,9 +280,9 @@@ static int pcm_playback_hw_free(struct 
        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);
  
  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;
@@@ -307,10 -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;
index a7502810a3ad8563df0665b6ee88cd11e62217bd,5ffedb0ade3f8e93713ff44fc07ab9ced3b21f4d..74c972d25c6642934e7cefa77588ac74be980123
@@@ -1,9 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * oxfw_stream.c - a part of driver for OXFW970/971 based devices
   *
   * Copyright (c) 2014 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include "oxfw.h"
@@@ -101,34 -100,85 +100,34 @@@ static int set_stream_format(struct snd
        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,
        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;
  
        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;
        }
  
        /*
                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;
  
        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,
index 11a4e1581381a16ea82a4b9786eacee10416374a,9fd145cc4b07f00d206197ef11470f0ac48eb64d..fb6df3fc018edc27bf6964c57f67d761d6088f4f
@@@ -1,8 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * oxfw.c - a part of driver for OXFW970/971 based devices
   *
   * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include "oxfw.h"
@@@ -118,7 -118,9 +118,7 @@@ static void oxfw_card_free(struct snd_c
  {
        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)
@@@ -206,9 -208,14 +206,9 @@@ static void do_registration(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)
@@@ -275,7 -282,11 +275,7 @@@ static void oxfw_bus_reset(struct fw_un
  
        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)
index d4d4926c28cfe96f0077e36e7fe3826b732048b9,36112850ef9216ea283e8b07ec4f383a5b9d0bff..cb69ab87bb14feb9655eab020896f432ea42317b
@@@ -1,8 -1,8 +1,8 @@@
+ /* SPDX-License-Identifier: GPL-2.0-only */
  /*
   * oxfw.h - a part of driver for OXFW970/971 based devices
   *
   * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include <linux/device.h>
@@@ -52,7 -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;
@@@ -98,14 -99,17 +98,14 @@@ int avc_general_inquiry_sig_fmt(struct 
                                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;
index 1cf0f9470449d014510e4b28d604b129e987e9eb,d9d20ef22f5bcd7031a4f08f0eb0a8b8e4fefe18..95fb10b7a737a9619a330c45c0b846ea12e739fc
@@@ -1,9 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * amdtp-tascam.c - a part of driver for TASCAM FireWire series
   *
   * Copyright (c) 2015 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include <sound/pcm.h>
@@@ -224,7 -223,7 +223,7 @@@ int amdtp_tscm_init(struct amdtp_strea
                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;
index 300683a82c68849f13c442f7f61fb4017f3cac07,a8cd9b156488f606bfd422bc5f02c9085c7536f6..b5ced5415e40543a050a7755bbef1855df487de3
@@@ -1,9 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * tascam-pcm.c - a part of driver for TASCAM FireWire series
   *
   * Copyright (c) 2015 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include "tascam.h"
@@@ -84,8 -83,8 +83,8 @@@ static int pcm_close(struct snd_pcm_sub
        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;
                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);
  
@@@ -228,8 -259,8 +227,8 @@@ int snd_tscm_create_pcm_devices(struct 
                .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,
                .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,
index 0e515b7be2766c8bc6f1d884de8151157604aaf4,e6fcd9e1996100da86b4c1684ead5c585cdf376e..e852e46ebe6fa6c9bf77ce579f214c997cb67680
@@@ -1,9 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * tascam-stream.c - a part of driver for TASCAM FireWire series
   *
   * Copyright (c) 2015 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #include <linux/delay.h>
@@@ -166,7 -165,7 +165,7 @@@ static int set_stream_formats(struct sn
        __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,
        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,
                           TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
                           &reg, 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,
 +                         &reg, sizeof(reg), 0);
 +      reg = cpu_to_be32(0x00000000);
 +      snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
 +                         TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
 +                         &reg, 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,
 +                         &reg, sizeof(reg), 0);
  }
  
  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,
 +                               &reg, 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,
 +                               &reg, 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,
 +                               &reg, 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,
        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,
        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,
                                  &reg, 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,
 -                         &reg, sizeof(reg), 0);
 -      reg = cpu_to_be32(0x00000000);
 -      snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
 -                         TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
 -                         &reg, 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,
 -                         &reg, 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,
 -                               &reg, 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,
 -                               &reg, 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,
 -                               &reg, 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)
        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);
@@@ -347,62 -368,33 +346,62 @@@ void snd_tscm_stream_destroy_duplex(str
        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;
  
        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)
index 4ed88cceaedbe525282d4e3eae71fc49faa70137,1d003d4cf448ecb7238d70db363c338f94603ab2..734e5bb9c3dafb631b16e0bedacd25316f4c2065
@@@ -1,9 -1,8 +1,8 @@@
+ /* SPDX-License-Identifier: GPL-2.0-only */
  /*
   * tascam.h - a part of driver for TASCAM FireWire series
   *
   * Copyright (c) 2015 Takashi Sakamoto
-  *
-  * Licensed under the terms of the GNU General Public License, version 2.
   */
  
  #ifndef SOUND_TASCAM_H_INCLUDED
@@@ -147,7 -146,6 +146,7 @@@ int snd_tscm_stream_get_clock(struct sn
  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);
  
index c24fc8d266a91d4cd3cb0a8d7395157ed522f67a,b02f74528b66de3c0dce17ac7add79581ccfeaec..3b0110545070ac1fec24e44aac251490c18203d7
@@@ -1,3 -1,4 +1,4 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * HD-audio controller helpers
   */
@@@ -78,8 -79,6 +79,8 @@@ void snd_hdac_bus_init_cmd_io(struct hd
        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);
@@@ -242,8 -241,6 +243,8 @@@ int snd_hdac_bus_get_response(struct hd
  
        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 */
@@@ -418,6 -415,9 +419,6 @@@ int snd_hdac_bus_reset_link(struct hdac
                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 --combined sound/hda/hdac_device.c
index a265c1d688766882a046dda5622ace56f05e02f5,6907dbefd08c17ce72849ab96b307140ecbebbc2..b26cc93e7e1034d1b446b24490c1dd9b6e11eccf
@@@ -1,3 -1,4 +1,4 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * HD-audio codec core device
   */
@@@ -89,7 -90,7 +90,7 @@@ int snd_hdac_device_init(struct hdac_de
  
        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;
  
@@@ -394,35 -395,32 +395,35 @@@ static void setup_fg_nodes(struct hdac_
  /**
   * 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);
  
index 0b35fb7e7a785fddd41cf5eda24827324240d06b,e7234f3d99e248b1f30f74ddd28133c7d755ac3b..2a21a3d997196283f3107929fa1f6847e10724f8
@@@ -1,21 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   *  Asihpi soundcard
   *  Copyright (c) by AudioScience Inc <support@audioscience.com>
   *
-  *   This program is free software; you can redistribute it and/or modify
-  *   it under the terms of version 2 of the GNU General Public License as
-  *   published by the Free Software Foundation;
-  *
-  *   This program is distributed in the hope that it will be useful,
-  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
-  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  *   GNU General Public License for more details.
-  *
-  *   You should have received a copy of the GNU General Public License
-  *   along with this program; if not, write to the Free Software
-  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-  *
-  *
   *  The following is not a condition of use, merely a request:
   *  If you modify this program, particularly if you fix errors, AudioScience Inc
   *  would appreciate it if you grant us the right to use those modifications
@@@ -1532,6 -1519,7 +1519,6 @@@ static int snd_asihpi_volume_get(struc
  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];
  
        /*  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);
@@@ -1566,12 -1555,13 +1553,12 @@@ static int snd_asihpi_volume_mute_put(s
                                 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 --combined sound/pci/cs4281.c
index d271cb88194820e271c23a52defd1397e4cc9332,a2cce3ecda6f07a59f4cebf93529b383935e5bb1..04c712647853d538ca915984c38c33439b093054
@@@ -1,22 -1,7 +1,7 @@@
+ // SPDX-License-Identifier: GPL-2.0-or-later
  /*
   *  Driver for Cirrus Logic CS4281 based PCI soundcard
   *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
-  *
-  *
-  *   This program is free software; you can redistribute it and/or modify
-  *   it under the terms of the GNU General Public License as published by
-  *   the Free Software Foundation; either version 2 of the License, or
-  *   (at your option) any later version.
-  *
-  *   This program is distributed in the hope that it will be useful,
-  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
-  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  *   GNU General Public License for more details.
-  *
-  *   You should have received a copy of the GNU General Public License
-  *   along with this program; if not, write to the Free Software
-  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-  *
   */
  
  #include <linux/io.h>
@@@ -709,7 -694,7 +694,7 @@@ static int snd_cs4281_trigger(struct sn
  
  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;
        case 44100:     return 1;
        case 48000:     return 0;
        default:
 -              goto __variable;
 +              break;
        }
 -      __variable:
        val = 1536000 / rate;
        if (real_rate)
                *real_rate = 1536000 / val;
index 7d8834907f1fd6775d4303a1a850b0967133b5e2,6c51b8363f8b73ccea5f8ff65558a298ddde8524..5346631df1eccd325d4ea6c95ed4df77b5fb80d3
@@@ -1,22 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-or-later
  /*
   * Universal Interface for Intel High Definition Audio Codec
   *
   * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
-  *
-  *
-  *  This driver is free software; you can redistribute it and/or modify
-  *  it under the terms of the GNU General Public License as published by
-  *  the Free Software Foundation; either version 2 of the License, or
-  *  (at your option) any later version.
-  *
-  *  This driver is distributed in the hope that it will be useful,
-  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  *  GNU General Public License for more details.
-  *
-  *  You should have received a copy of the GNU General Public License
-  *  along with this program; if not, write to the Free Software
-  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   */
  
  #include <linux/init.h>
@@@ -122,7 -108,7 +108,7 @@@ static int add_conn_list(struct hda_cod
  {
        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;
@@@ -1016,7 -1002,7 +1002,7 @@@ int snd_hda_codec_update_widgets(struc
        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;
  
index 53feaeef15531419649752f5fa3135071d5d6bc6,232a1926758aa07a027d9845d8b3ca16efccdc69..c8d1b4316245f3ac02ca0b25bbe6f63b66eceb02
@@@ -1,3 -1,4 +1,4 @@@
+ // SPDX-License-Identifier: GPL-2.0-or-later
  /*
   *
   *  Implementation of primary alsa driver code base for Intel HD Audio.
@@@ -6,18 -7,6 +7,6 @@@
   *
   *  Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
   *                     PeiSen Hou <pshou@realtek.com.tw>
-  *
-  *  This program is free software; you can redistribute it and/or modify it
-  *  under the terms of the GNU General Public License as published by the Free
-  *  Software Foundation; either version 2 of the License, or (at your option)
-  *  any later version.
-  *
-  *  This program is distributed in the hope that it will be useful, but WITHOUT
-  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
-  *  more details.
-  *
-  *
   */
  
  #include <linux/clocksource.h>
@@@ -806,11 -795,11 +795,11 @@@ static int azx_rirb_get_response(struc
  
        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);
        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;
        }
  
index 8d886791cf0fc1508b7a20c22fbc2403cf440a34,3aa5c957ffbfa771ee5789288899991ed1cffae5..baa15374fbcb4a38301fa7d5a5197a04ecb66e1b
@@@ -1,15 -1,6 +1,6 @@@
+ /* SPDX-License-Identifier: GPL-2.0-or-later */
  /*
   *  Common functionality for the alsa driver code base for HD Audio.
-  *
-  *  This program is free software; you can redistribute it and/or modify it
-  *  under the terms of the GNU General Public License as published by the Free
-  *  Software Foundation; either version 2 of the License, or (at your option)
-  *  any later version.
-  *
-  *  This program is distributed in the hope that it will be useful, but WITHOUT
-  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
-  *  more details.
   */
  
  #ifndef __SOUND_HDA_CONTROLLER_H
@@@ -142,9 -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;
index 922c9b9301244da9cb03b9aa86c10bb26996ec64,50f86f458918049b6989b244919d7796f617f63a..cb8b0945547cdf2c0382fdc7e0bdb794401150b8
@@@ -1,3 -1,4 +1,4 @@@
+ // SPDX-License-Identifier: GPL-2.0-or-later
  /*
   *
   *  hda_intel.c - Implementation of primary alsa driver code base
@@@ -8,20 -9,6 +9,6 @@@
   *  Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
   *                     PeiSen Hou <pshou@realtek.com.tw>
   *
-  *  This program is free software; you can redistribute it and/or modify it
-  *  under the terms of the GNU General Public License as published by the Free
-  *  Software Foundation; either version 2 of the License, or (at your option)
-  *  any later version.
-  *
-  *  This program is distributed in the hope that it will be useful, but WITHOUT
-  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
-  *  more details.
-  *
-  *  You should have received a copy of the GNU General Public License along with
-  *  this program; if not, write to the Free Software Foundation, Inc., 59
-  *  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-  *
   *  CONTACTS:
   *
   *  Matt Jared                matt.jared@intel.com
@@@ -31,7 -18,6 +18,6 @@@
   *  CHANGES:
   *
   *  2004.12.01        Major rewrite by tiwai, merged the work of pshou
-  * 
   */
  
  #include <linux/delay.h>
@@@ -1701,6 -1687,10 +1687,6 @@@ static int azx_create(struct snd_card *
        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);
                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;
@@@ -2388,9 -2374,6 +2374,9 @@@ static const struct pci_device_id azx_i
        /* 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 },
diff --combined sound/pci/hda/hda_jack.c
index 60a548513c9db191de26e387a56b8a1961826bdc,6d9acd5c4e4118888fe6f0c5c7940a93e7fe1e8b..1fb7b06457ae9168027a884c55a3b6c43cd3988a
@@@ -1,12 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-or-later
  /*
   * Jack-detection handling for HD-audio
   *
   * Copyright (c) 2011 Takashi Iwai <tiwai@suse.de>
-  *
-  * This driver is free software; you can redistribute it and/or modify
-  * it under the terms of the GNU General Public License as published by
-  * the Free Software Foundation; either version 2 of the License, or
-  * (at your option) any later version.
   */
  
  #include <linux/init.h>
@@@ -563,7 -559,7 +559,7 @@@ static void call_jack_callback(struct h
  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)
index 49adfc4ef5de5cd65653772e8bba5e889e9b6030,c3096796ee05e4cd0ca27fb486f46e9543b2f7bc..0d51823d7270eaf2629eabb15290e14d4839dec2
@@@ -1,3 -1,4 +1,4 @@@
+ // SPDX-License-Identifier: GPL-2.0-or-later
  /*
   * HD audio interface patch for Creative CA0132 chip
   *
@@@ -5,20 -6,6 +6,6 @@@
   *
   * Based on patch_ca0110.c
   * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de>
-  *
-  *  This driver is free software; you can redistribute it and/or modify
-  *  it under the terms of the GNU General Public License as published by
-  *  the Free Software Foundation; either version 2 of the License, or
-  *  (at your option) any later version.
-  *
-  *  This driver is distributed in the hope that it will be useful,
-  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  *  GNU General Public License for more details.
-  *
-  *  You should have received a copy of the GNU General Public License
-  *  along with this program; if not, write to the Free Software
-  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   */
  
  #include <linux/init.h>
@@@ -2731,7 -2718,7 +2718,7 @@@ static bool is_last(const struct dsp_im
  
  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(
@@@ -5993,7 -5980,7 +5980,7 @@@ static int ca0132_alt_volume_put(struc
        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:
index b522314ec5bed158d3a02b59fb24272885db885a,b7bde55b6adf1ab2278fe2d3215a0bfb6903f059..40323d91f9e4f91683ed03e1e006690a569b4252
@@@ -1,3 -1,4 +1,4 @@@
+ // SPDX-License-Identifier: GPL-2.0-or-later
  /*
   *
   *  patch_hdmi.c - routines for HDMI/DisplayPort codecs
   *
   *  Maintained by:
   *                    Wu Fengguang <wfg@linux.intel.com>
-  *
-  *  This program is free software; you can redistribute it and/or modify it
-  *  under the terms of the GNU General Public License as published by the Free
-  *  Software Foundation; either version 2 of the License, or (at your option)
-  *  any later version.
-  *
-  *  This program is distributed in the hope that it will be useful, but
-  *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-  *  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-  *  for more details.
-  *
-  *  You should have received a copy of the GNU General Public License
-  *  along with this program; if not, write to the Free Software Foundation,
-  *  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   */
  
  #include <linux/init.h>
@@@ -1627,8 -1614,7 +1614,8 @@@ static void sync_eld_via_acomp(struct h
        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);
  }
index 0f776444ab869189e1bc262d457fa7f87416f4d6,5b3c26991f26b07474a8481b588982432fd7fdbe..f24a757f8239b0ccd2e80d02df51e300a0e44221
@@@ -1,3 -1,4 +1,4 @@@
+ // SPDX-License-Identifier: GPL-2.0-or-later
  /*
   * Universal Interface for Intel High Definition Audio Codec
   *
@@@ -7,20 -8,6 +8,6 @@@
   *                    PeiSen Hou <pshou@realtek.com.tw>
   *                    Takashi Iwai <tiwai@suse.de>
   *                    Jonathan Woithe <jwoithe@just42.net>
-  *
-  *  This driver is free software; you can redistribute it and/or modify
-  *  it under the terms of the GNU General Public License as published by
-  *  the Free Software Foundation; either version 2 of the License, or
-  *  (at your option) any later version.
-  *
-  *  This driver is distributed in the hope that it will be useful,
-  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  *  GNU General Public License for more details.
-  *
-  *  You should have received a copy of the GNU General Public License
-  *  along with this program; if not, write to the Free Software
-  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   */
  
  #include <linux/init.h>
@@@ -2461,10 -2448,9 +2448,10 @@@ static const struct snd_pci_quirk alc88
        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),
@@@ -3268,7 -3254,6 +3255,7 @@@ static void alc256_init(struct hda_code
        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)
@@@ -7089,7 -7074,6 +7076,7 @@@ static const struct snd_pci_quirk alc26
        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),
@@@ -7839,6 -7823,7 +7826,6 @@@ static int patch_alc269(struct hda_code
                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;
index 36116881cf52571628c1ab50a44cadb447f1afb1,9236a1a8c49b33df29e965007bb42c9ba1f7b45e..dd3a873777eb5753ada80be2d9a960be8526038c
@@@ -1,25 -1,10 +1,10 @@@
+ // SPDX-License-Identifier: GPL-2.0-or-later
  /* -*- linux-c -*- *
   *
   * ALSA driver for the digigram lx6464es interface
   * low-level interface
   *
   * Copyright (c) 2009 Tim Blechmann <tim@klingt.org>
-  *
-  * This program is free software; you can redistribute it and/or modify
-  * it under the terms of the GNU General Public License as published by
-  * the Free Software Foundation; either version 2 of the License, or
-  * (at your option) any later version.
-  *
-  * This program is distributed in the hope that it will be useful,
-  * but WITHOUT ANY WARRANTY; without even the implied warranty of
-  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  * GNU General Public License for more details.
-  *
-  * You should have received a copy of the GNU General Public License
-  * along with this program; see the file COPYING.  If not, write to
-  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-  * Boston, MA 02111-1307, USA.
-  *
   */
  
  /* #define RMH_DEBUG 1 */
@@@ -1001,6 -986,8 +986,6 @@@ static int lx_interrupt_handle_async_ev
         * 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;
  
                            *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;
index 982b297b3d0a8ec77aed4ae3e1c04b83938150e8,6f5eaa510bbcee57488a3f7e2618161f6640fba1..81a6f4b2bd3c52c93abc6a35a83c1e24437fa57e
@@@ -1,3 -1,4 +1,4 @@@
+ // SPDX-License-Identifier: GPL-2.0-or-later
  /*
   *   ALSA driver for RME Hammerfall DSP MADI audio interface(s)
   *
   *      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
-  *
-  *   This program is free software; you can redistribute it and/or modify
-  *   it under the terms of the GNU General Public License as published by
-  *   the Free Software Foundation; either version 2 of the License, or
-  *   (at your option) any later version.
-  *
-  *   This program is distributed in the hope that it will be useful,
-  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
-  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  *   GNU General Public License for more details.
-  *
-  *   You should have received a copy of the GNU General Public License
-  *   along with this program; if not, write to the Free Software
-  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-  *
 + *
 + *      Modified 2019-05-23 fix AIO single speed ADAT capture and playback
 + *      by Philippe.Bekaert@uhasselt.be
   */
  
  /* *************    Register Documentation   *******************************************************
@@@ -1108,9 -1091,9 +1094,9 @@@ static int hdspm_autosync_ref(struct hd
  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);
@@@ -5591,16 -5574,11 +5577,16 @@@ static int snd_hdspm_hw_params(struct s
  
        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;
                        "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;
@@@ -5678,17 -5651,19 +5664,17 @@@ static int snd_hdspm_hw_free(struct snd
        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);
@@@ -6427,17 -6402,17 +6413,17 @@@ static int snd_hdspm_preallocate_memory
        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));
  }
  
  
index 6302ad5b712807dffe305402956ebfaa521e02ad,2d3f58aa01c8a1a7de40090bf0f743f786470384..29918954e74078bd8a14ffbe63017a99ae9e38b6
@@@ -1,3 -1,4 +1,4 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   *  hdac_hdmi.c - ASoc HDA-HDMI codec driver for Intel platforms
   *
@@@ -6,15 -7,6 +7,6 @@@
   *        Subhransu S. Prusty <subhransu.s.prusty@intel.com>
   *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   *
-  *  This program is free software; you can redistribute it and/or modify
-  *  it under the terms of the GNU General Public License as published by
-  *  the Free Software Foundation; version 2 of the License.
-  *
-  *  This program is distributed in the hope that it will be useful, but
-  *  WITHOUT ANY WARRANTY; without even the implied warranty of
-  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-  *  General Public License for more details.
-  *
   * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   */
  #include <linux/init.h>
@@@ -546,6 -538,29 +538,29 @@@ static struct hdac_hdmi_port *hdac_hdmi
        return NULL;
  }
  
+ /*
+  * 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
@@@ -806,6 -821,14 +821,14 @@@ static int hdac_hdmi_cvt_output_widget_
                                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:
@@@ -1867,6 -1890,12 +1890,12 @@@ static void hdmi_codec_remove(struct sn
  {
        struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
        struct hdac_device *hdev = hdmi->hdev;
+       int ret;
+       ret = snd_hdac_acomp_register_notifier(hdev->bus, NULL);
+       if (ret < 0)
+               dev_err(&hdev->dev, "notifier unregister failed: err: %d\n",
+                               ret);
  
        pm_runtime_disable(&hdev->dev);
  }
@@@ -2043,7 -2072,7 +2072,7 @@@ static int hdac_hdmi_dev_probe(struct h
                        "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,
@@@ -2090,6 -2119,7 +2119,7 @@@ static int hdac_hdmi_runtime_suspend(st
                return -EIO;
        }
  
+       snd_hdac_codec_link_down(hdev);
        snd_hdac_ext_bus_link_put(bus, hlink);
  
        snd_hdac_display_power(bus, hdev->addr, false);
@@@ -2116,6 -2146,7 +2146,7 @@@ static int hdac_hdmi_runtime_resume(str
        }
  
        snd_hdac_ext_bus_link_get(bus, hlink);
+       snd_hdac_codec_link_up(hdev);
  
        snd_hdac_display_power(bus, hdev->addr, true);
  
index 6811a477d0fbddaa30fd4c14dbbb7562a0ce4783,136f98bf5d7ee2a170b9122d6d359eb3de33dc22..7f665392618f6469f95768dec9e116f0f09fe2a3
   * Hardware interface for generic Intel audio DSP HDA IP
   */
  
- #include <linux/module.h>
  #include <sound/hdaudio_ext.h>
+ #include <sound/hda_register.h>
+ #include <linux/module.h>
  #include <sound/sof.h>
  #include <sound/sof/xtensa.h>
  #include "../ops.h"
@@@ -32,9 -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
   */
@@@ -186,12 -185,38 +188,38 @@@ void hda_dsp_dump(struct snd_sof_dev *s
        }
  }
  
+ void hda_ipc_irq_dump(struct snd_sof_dev *sdev)
+ {
+       struct hdac_bus *bus = sof_to_bus(sdev);
+       u32 adspis;
+       u32 intsts;
+       u32 intctl;
+       u32 ppsts;
+       u8 rirbsts;
+       /* read key IRQ stats and config registers */
+       adspis = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS);
+       intsts = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS);
+       intctl = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL);
+       ppsts = snd_sof_dsp_read(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPSTS);
+       rirbsts = snd_hdac_chip_readb(bus, RIRBSTS);
+       dev_err(sdev->dev,
+               "error: hda irq intsts 0x%8.8x intlctl 0x%8.8x rirb %2.2x\n",
+               intsts, intctl, rirbsts);
+       dev_err(sdev->dev,
+               "error: dsp irq ppsts 0x%8.8x adspis 0x%8.8x\n",
+               ppsts, adspis);
+ }
  void hda_ipc_dump(struct snd_sof_dev *sdev)
  {
        u32 hipcie;
        u32 hipct;
        u32 hipcctl;
  
+       hda_ipc_irq_dump(sdev);
        /* read IPC status */
        hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE);
        hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT);
@@@ -220,11 -245,6 +248,11 @@@ static int hda_init(struct snd_sof_dev 
        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 --combined sound/usb/format.c
index c391ae03a07bed8b656f9562f37068c28a609b56,c02b51a82775a2e53e4d6978cefa31019685f177..d79db71305f635829d5fe47f31af42c47c353caa
@@@ -1,18 -1,5 +1,5 @@@
+ // SPDX-License-Identifier: GPL-2.0-or-later
  /*
-  *   This program is free software; you can redistribute it and/or modify
-  *   it under the terms of the GNU General Public License as published by
-  *   the Free Software Foundation; either version 2 of the License, or
-  *   (at your option) any later version.
-  *
-  *   This program is distributed in the hope that it will be useful,
-  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
-  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  *   GNU General Public License for more details.
-  *
-  *   You should have received a copy of the GNU General Public License
-  *   along with this program; if not, write to the Free Software
-  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-  *
   */
  
  #include <linux/init.h>
@@@ -298,33 -285,6 +285,33 @@@ static int parse_uac2_sample_rate_range
        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).
@@@ -334,7 -294,7 +321,7 @@@ static int parse_audio_format_rates_v2v
  {
        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);
  
                              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 --combined sound/usb/helper.c
index b1cc9499c57e1c59b88212a81c99f1a1e6905200,84aa265dd802c7690a92d563498b05f3103c05fc..71d5f540334a23ea2904cb41e67d066c588ee932
@@@ -1,18 -1,5 +1,5 @@@
+ // SPDX-License-Identifier: GPL-2.0-or-later
  /*
-  *   This program is free software; you can redistribute it and/or modify
-  *   it under the terms of the GNU General Public License as published by
-  *   the Free Software Foundation; either version 2 of the License, or
-  *   (at your option) any later version.
-  *
-  *   This program is distributed in the hope that it will be useful,
-  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
-  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  *   GNU General Public License for more details.
-  *
-  *   You should have received a copy of the GNU General Public License
-  *   along with this program; if not, write to the Free Software
-  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-  *
   */
  
  #include <linux/init.h>
@@@ -76,20 -63,6 +63,20 @@@ void *snd_usb_find_csint_desc(void *buf
        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 (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.
@@@ -102,9 -75,6 +89,9 @@@ int snd_usb_ctl_msg(struct usb_device *
        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 --combined sound/usb/line6/driver.c
index 79e96b269411dcc6474dc087156e504b38fc64b0,e63a2451c88f4c26d3945adc89372267682ff170..ab2ec896f49c89bf60db609c915b277a42045161
@@@ -1,12 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * Line 6 Linux USB driver
   *
   * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
-  *
-  *    This program is free software; you can redistribute it and/or
-  *    modify it under the terms of the GNU General Public License as
-  *    published by the Free Software Foundation, version 2.
-  *
   */
  
  #include <linux/kernel.h>
@@@ -195,6 -191,17 +191,6 @@@ static int line6_send_raw_message_async
        return retval;
  }
  
 -/*
 -      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.
  */
diff --combined sound/usb/line6/driver.h
index 4eb66cdf1ece8de75af1dac49b70f05596fafb01,a9f7b4aa32c42680a861b3d2e702b2e45a45f0f1..e5e572ed5f304a0e5e2951944449ba87e1cbaddc
@@@ -1,12 -1,8 +1,8 @@@
+ /* SPDX-License-Identifier: GPL-2.0-only */
  /*
   * Line 6 Linux USB driver
   *
   * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
-  *
-  *    This program is free software; you can redistribute it and/or
-  *    modify it under the terms of the GNU General Public License as
-  *    published by the Free Software Foundation, version 2.
-  *
   */
  
  #ifndef DRIVER_H
  
  #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;
@@@ -194,6 -197,8 +190,6 @@@ extern int line6_send_sysex_message(str
                                    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 --combined sound/usb/line6/pcm.c
index 78c2d6cab3b52f7341e7f02ee1e7e0d502f8679b,21127e4958b2b3338106ddac394a51560d5d1933..2c03e0f6bf72d661226b73ddee90c087db8f4b6f
@@@ -1,12 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * Line 6 Linux USB driver
   *
   * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
-  *
-  *    This program is free software; you can redistribute it and/or
-  *    modify it under the terms of the GNU General Public License as
-  *    published by the Free Software Foundation, version 2.
-  *
   */
  
  #include <linux/slab.h>
@@@ -560,11 -556,6 +556,11 @@@ int line6_init_pcm(struct usb_line6 *li
        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;
 +      }
  
        spin_lock_init(&line6pcm->out.lock);
        spin_lock_init(&line6pcm->in.lock);
diff --combined sound/usb/line6/pod.c
index 9ea720b4b2ab4743af166f825c081cf6fe70d2e9,200ae53adf2253629f82d3870fe2d9bd98384357..ee4c9d220fdf76c82a8504a6a63330462ea64878
@@@ -1,12 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * Line 6 Linux USB driver
   *
   * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
-  *
-  *    This program is free software; you can redistribute it and/or
-  *    modify it under the terms of the GNU General Public License as
-  *    published by the Free Software Foundation, version 2.
-  *
   */
  
  #include <linux/slab.h>
        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 {
@@@ -61,6 -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;
  
@@@ -74,8 -78,6 +70,8 @@@
        int device_id;
  };
  
 +#define line6_to_pod(x)               container_of(x, struct usb_line6_pod, line6)
 +
  #define POD_SYSEX_CODE 3
  
  /* *INDENT-OFF* */
@@@ -167,6 -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)
  {
  */
  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;
        }
  
@@@ -274,27 -277,47 +270,27 @@@ static ssize_t device_id_show(struct de
        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: */
@@@ -330,7 -353,7 +326,7 @@@ static int snd_pod_control_monitor_get(
                                       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;
@@@ -341,7 -364,7 +337,7 @@@ static int snd_pod_control_monitor_put(
                                       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;
@@@ -363,6 -386,17 +359,6 @@@ static const struct snd_kcontrol_new po
        .put = snd_pod_control_monitor_put
  };
  
 -/*
 -      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.
  */
@@@ -370,10 -404,13 +366,10 @@@ static int pod_init(struct usb_line6 *l
                    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);
                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 --combined sound/usb/line6/podhd.c
index 395ae1692f4573411ce552c7efd6dd957a06b78e,77a1d55334bbb34a0a1717ac1854046b6cd0c305..f0662bd4e50fbf865d253ac2d516fdd173d10dad
@@@ -1,14 -1,10 +1,10 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * Line 6 Pod HD
   *
   * Copyright (C) 2011 Stefan Hajnoczi <stefanha@gmail.com>
   * Copyright (C) 2015 Andrej Krutak <dev@andree.sk>
   * Copyright (C) 2017 Hans P. Moller <hmoller@uc.cl>
-  *
-  *    This program is free software; you can redistribute it and/or
-  *    modify it under the terms of the GNU General Public License as
-  *    published by the Free Software Foundation, version 2.
-  *
   */
  
  #include <linux/usb.h>
  
  #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,
@@@ -37,6 -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;
  
@@@ -44,8 -59,6 +40,8 @@@
        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,
@@@ -141,6 -154,10 +137,6 @@@ static struct line6_pcm_properties podx
  };
  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)
  {
@@@ -181,6 -198,26 +177,6 @@@ static const struct attribute_group pod
   * 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;
@@@ -231,23 -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)
@@@ -262,11 -313,13 +258,11 @@@ static int podhd_init(struct usb_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 */
  
        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;
  }
  
index 94a9764110d36dbd989b033fbdf96a6a4fffd056,974ab3e62b68dc9476e2cd7850e047679cd73799..d0a555dbe324f0e8049023e1327fec7a6b58b584
@@@ -1,13 -1,9 +1,9 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * Line 6 Linux USB driver
   *
   * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
   *                         Emil Myhrman (emil.myhrman@gmail.com)
-  *
-  *    This program is free software; you can redistribute it and/or
-  *    modify it under the terms of the GNU General Public License as
-  *    published by the Free Software Foundation, version 2.
-  *
   */
  
  #include <linux/wait.h>
@@@ -61,8 -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
@@@ -213,8 -207,8 +209,8 @@@ static int snd_toneport_source_get(stru
                                   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;
  }
@@@ -224,7 -218,8 +220,7 @@@ static int snd_toneport_source_put(stru
                                   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];
@@@ -398,7 -393,8 +394,7 @@@ static int toneport_setup(struct usb_li
  */
  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);
@@@ -412,7 -408,7 +408,7 @@@ static int toneport_init(struct usb_lin
                         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 --combined sound/usb/line6/variax.c
index 0d0de907d497a25174d88ada5d1fed0da6668d11,e59b974443992d734bb2d9794dcff04158f8e7ac..0d24c72c155fb36dcd53446aa6977239ad97af36
@@@ -1,12 -1,8 +1,8 @@@
+ // SPDX-License-Identifier: GPL-2.0-only
  /*
   * Line 6 Linux USB driver
   *
   * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
-  *
-  *    This program is free software; you can redistribute it and/or
-  *    modify it under the terms of the GNU General Public License as
-  *    published by the Free Software Foundation, version 2.
-  *
   */
  
  #include <linux/slab.h>
        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,12 -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
  
  /*
@@@ -72,6 -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;
        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;
 +      }
  }
  
  /*
  */
  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]) {
        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;
        }
  */
  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);
  }
  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,
                return err;
  
        /* initiate startup procedure: */
 -      variax_startup1(variax);
 +      schedule_delayed_work(&line6->startup_work,
 +                            msecs_to_jiffies(VARIAX_STARTUP_DELAY1));
        return 0;
  }
  
diff --combined sound/usb/mixer.c
index ac121b10c51c5a06b2b53514a360992db2581913,c703f8534b0735bdcd9395a0ff5112d79a3db9c7..7498b5191b68e4cf0368599998160ec0eb17f29d
@@@ -1,3 -1,4 +1,4 @@@
+ // SPDX-License-Identifier: GPL-2.0-or-later
  /*
   *   (Tentative) USB Audio Driver for ALSA
   *
@@@ -8,22 -9,6 +9,6 @@@
   *   Many codes borrowed from audio.c by
   *        Alan Cox (alan@lxorguk.ukuu.org.uk)
   *        Thomas Sailer (sailer@ife.ee.ethz.ch)
-  *
-  *
-  *   This program is free software; you can redistribute it and/or modify
-  *   it under the terms of the GNU General Public License as published by
-  *   the Free Software Foundation; either version 2 of the License, or
-  *   (at your option) any later version.
-  *
-  *   This program is distributed in the hope that it will be useful,
-  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
-  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  *   GNU General Public License for more details.
-  *
-  *   You should have received a copy of the GNU General Public License
-  *   along with this program; if not, write to the Free Software
-  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-  *
   */
  
  /*
@@@ -2318,7 -2303,7 +2303,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;
        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);
                } 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,
@@@ -2486,10 -2466,10 +2471,10 @@@ static int parse_audio_processing_unit(
        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);
        }
  }
  
@@@ -2500,7 -2480,8 +2485,7 @@@ static int parse_audio_extension_unit(s
         * 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 --combined sound/usb/mixer_quirks.c
index 5783329a3237fbd906a70c6c09657844129533a7,1f6011f36bb063108e1e49dc60ef5872e7938f1d..199fa157a411e4731fa56e477f0953b2c8fd68ea
@@@ -1,3 -1,4 +1,4 @@@
+ // SPDX-License-Identifier: GPL-2.0-or-later
  /*
   *   USB Audio Driver for ALSA
   *
   *
   *   Audio Advantage Micro II support added by:
   *        Przemek Rudy (prudy1@o2.pl)
-  *
-  *   This program is free software; you can redistribute it and/or modify
-  *   it under the terms of the GNU General Public License as published by
-  *   the Free Software Foundation; either version 2 of the License, or
-  *   (at your option) any later version.
-  *
-  *   This program is distributed in the hope that it will be useful,
-  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
-  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  *   GNU General Public License for more details.
-  *
-  *   You should have received a copy of the GNU General Public License
-  *   along with this program; if not, write to the Free Software
-  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   */
  
  #include <linux/hid.h>
@@@ -754,7 -741,7 +741,7 @@@ static int snd_ni_control_init_val(stru
                return err;
        }
  
 -      kctl->private_value |= (value << 24);
 +      kctl->private_value |= ((unsigned int)value << 24);
        return 0;
  }
  
@@@ -915,7 -902,7 +902,7 @@@ static int snd_ftu_eff_switch_init(stru
        if (err < 0)
                return err;
  
 -      kctl->private_value |= value[0] << 24;
 +      kctl->private_value |= (unsigned int)value[0] << 24;
        return 0;
  }
  
diff --combined sound/usb/quirks-table.h
index 5fd748c4eb30e6d5992fc2f46f8c01cd9b859e1a,9e049f60e80e35a9c02bf4d29e10d4e9ce3d524e..e918ce34602723137b90f0f608b42be213c909c1
@@@ -1,23 -1,9 +1,9 @@@
+ /* SPDX-License-Identifier: GPL-2.0-or-later */
  /*
   * ALSA USB Audio Driver
   *
   * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>,
   *                       Clemens Ladisch <clemens@ladisch.de>
-  *
-  *
-  *  This program is free software; you can redistribute it and/or modify
-  *  it under the terms of the GNU General Public License as published by
-  *  the Free Software Foundation; either version 2 of the License, or
-  *  (at your option) any later version.
-  *
-  *  This program is distributed in the hope that it will be useful,
-  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  *  GNU General Public License for more details.
-  *
-  *  You should have received a copy of the GNU General Public License
-  *  along with this program; if not, write to the Free Software
-  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   */
  
  /*
@@@ -2422,7 -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 --combined sound/usb/quirks.c
index 057143330a281002bf9a2fc8bbb62e39b07daf21,cf5cff10c08e8897fa87a0ba26f7eb62bd434481..78858918cbc103daac493fc8fc8c3cb21bf291a8
@@@ -1,17 -1,5 +1,5 @@@
+ // SPDX-License-Identifier: GPL-2.0-or-later
  /*
-  *   This program is free software; you can redistribute it and/or modify
-  *   it under the terms of the GNU General Public License as published by
-  *   the Free Software Foundation; either version 2 of the License, or
-  *   (at your option) any later version.
-  *
-  *   This program is distributed in the hope that it will be useful,
-  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
-  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  *   GNU General Public License for more details.
-  *
-  *   You should have received a copy of the GNU General Public License
-  *   along with this program; if not, write to the Free Software
-  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   */
  
  #include <linux/init.h>
@@@ -840,13 -828,11 +828,13 @@@ static int snd_usb_novation_boot_quirk(
  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,
  
  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);
  
@@@ -982,8 -964,6 +970,8 @@@ static int snd_usb_axefx3_boot_quirk(st
  
        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
@@@ -1016,8 -996,6 +1004,8 @@@ static int snd_usb_motu_microbookii_com
  {
        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)
  
        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)