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>
86 files changed:
include/sound/hda_codec.h
include/sound/hdaudio.h
include/uapi/linux/usb/audio.h
sound/core/control.c
sound/core/oss/rate.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/amdtp-stream.h
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/cmp.h
sound/firewire/dice/Makefile
sound/firewire/dice/dice-midi.c
sound/firewire/dice/dice-pcm.c
sound/firewire/dice/dice-presonus.c [new file with mode: 0644]
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-protocol-former.c
sound/firewire/fireface/ff-protocol-latter.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/hda/hdac_sysfs.c
sound/pci/asihpi/asihpi.c
sound/pci/cs4281.c
sound/pci/echoaudio/echoaudio_dsp.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/helper.h
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
sound/xen/xen_snd_front_alsa.c

index c4299da22f93db75dfa129179498d2ee6cdd21b8..8f46ff3449d5a25d71954379e9c640b5f4333069 100644 (file)
@@ -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 */
index e8346784cf3f8d1afdf23d3c2e0f7e99ca909538..612a17e375d026b0215572f6d9dec83e04ad0c37 100644 (file)
@@ -120,7 +120,7 @@ void snd_hdac_device_unregister(struct hdac_device *codec);
 int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name);
 int snd_hdac_codec_modalias(struct hdac_device *hdac, char *buf, size_t size);
 
-int snd_hdac_refresh_widgets(struct hdac_device *codec, bool sysfs);
+int snd_hdac_refresh_widgets(struct hdac_device *codec);
 
 unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid,
                               unsigned int verb, unsigned int parm);
@@ -358,6 +358,9 @@ struct hdac_bus {
        bool align_bdle_4k:1;           /* BDLE align 4K boundary */
        bool reverse_assign:1;          /* assign devices in reverse order */
        bool corbrp_self_clear:1;       /* CORBRP clears itself after reset */
+       bool polling_mode:1;
+
+       int poll_count;
 
        int bdl_pos_adj;                /* BDL position adjustment */
 
index ddc5396800aa425c34c3488f436b5bb55393b9c4..76b7c3f6cd0d7b225f8c2e8560943d76ad885930 100644 (file)
@@ -450,6 +450,43 @@ static inline __u8 *uac_processing_unit_specific(struct uac_processing_unit_desc
        }
 }
 
+/*
+ * Extension Unit (XU) has almost compatible layout with Processing Unit, but
+ * on UAC2, it has a different bmControls size (bControlSize); it's 1 byte for
+ * XU while 2 bytes for PU.  The last iExtension field is a one-byte index as
+ * well as iProcessing field of PU.
+ */
+static inline __u8 uac_extension_unit_bControlSize(struct uac_processing_unit_descriptor *desc,
+                                                  int protocol)
+{
+       switch (protocol) {
+       case UAC_VERSION_1:
+               return desc->baSourceID[desc->bNrInPins + 4];
+       case UAC_VERSION_2:
+               return 1; /* in UAC2, this value is constant */
+       case UAC_VERSION_3:
+               return 4; /* in UAC3, this value is constant */
+       default:
+               return 1;
+       }
+}
+
+static inline __u8 uac_extension_unit_iExtension(struct uac_processing_unit_descriptor *desc,
+                                                int protocol)
+{
+       __u8 control_size = uac_extension_unit_bControlSize(desc, protocol);
+
+       switch (protocol) {
+       case UAC_VERSION_1:
+       case UAC_VERSION_2:
+       default:
+               return *(uac_processing_unit_bmControls(desc, protocol)
+                        + control_size);
+       case UAC_VERSION_3:
+               return 0; /* UAC3 does not have this field */
+       }
+}
+
 /* 4.5.2 Class-Specific AS Interface Descriptor */
 struct uac1_as_header_descriptor {
        __u8  bLength;                  /* in bytes: 7 */
index 5be5b9b931bfefee5a2798775b7c33d02f42222e..7a4d8690ce41f4f0644dc007709fd071095439b7 100644 (file)
@@ -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 2fa9299a440dbb99e767c079e664d0a1133e8762..7cd09cef6961db34a381ddf90d8852c0ff194e30 100644 (file)
@@ -323,8 +323,8 @@ int snd_pcm_plugin_build_rate(struct snd_pcm_substream *plug,
 
        err = snd_pcm_plugin_build(plug, "rate conversion",
                                   src_format, dst_format,
-                                  sizeof(struct rate_priv) +
-                                  src_format->channels * sizeof(struct rate_channel),
+                                  struct_size(data, channels,
+                                              src_format->channels),
                                   &plugin);
        if (err < 0)
                return err;
index 96ad01fb668c3dae74799ea8b08326976825d90b..ccf682689ec951ef5234aebbc55b1bbf692371e7 100644 (file)
@@ -49,7 +49,7 @@ static int snd_seq_oss_oob_user(struct seq_oss_devinfo *dp, void __user *arg)
        if (copy_from_user(ev, arg, 8))
                return -EFAULT;
        memset(&tmpev, 0, sizeof(tmpev));
-       snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.port, dp->addr.client);
+       snd_seq_oss_fill_addr(dp, &tmpev, dp->addr.client, dp->addr.port);
        tmpev.time.tick = 0;
        if (! snd_seq_oss_process_event(dp, (union evrec *)ev, &tmpev)) {
                snd_seq_oss_dispatch(dp, &tmpev, 0, 0);
index 79ef430e56e1798d960ee63b7b1adc9bba56bd43..537d5f423e206b075743129cee071cdeee938645 100644 (file)
@@ -161,7 +161,7 @@ insert_queue(struct seq_oss_devinfo *dp, union evrec *rec, struct file *opt)
        memset(&event, 0, sizeof(event));
        /* set dummy -- to be sure */
        event.type = SNDRV_SEQ_EVENT_NOTEOFF;
-       snd_seq_oss_fill_addr(dp, &event, dp->addr.port, dp->addr.client);
+       snd_seq_oss_fill_addr(dp, &event, dp->addr.client, dp->addr.port);
 
        if (snd_seq_oss_process_event(dp, rec, &event))
                return 0; /* invalid event - no need to insert queue */
index cc6eb30f03a2acb29ad994fbca28c5c4768bb37d..fd5d6b8ac557aa57035ddd66b0a83ba7dc7ea8bd 100644 (file)
@@ -82,7 +82,7 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
        if (err < 0)
                return err;
 
-       s->fdf = AMDTP_FDF_AM824 | s->sfc;
+       s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc;
 
        p->pcm_channels = pcm_channels;
        p->midi_ports = midi_ports;
@@ -320,7 +320,7 @@ static void read_midi_messages(struct amdtp_stream *s,
        u8 *b;
 
        for (f = 0; f < frames; f++) {
-               port = (s->data_block_counter + f) % 8;
+               port = (8 - s->ctx_data.tx.first_dbc + s->data_block_counter + f) % 8;
                b = (u8 *)&buffer[p->midi_position];
 
                len = b[0] - 0x80;
index edb5c3afa6f8c7ba5ece70f34ee51efba2657825..4adbbf789cbe80c1fd2040e9685d79dde806f154 100644 (file)
 
 #include <linux/tracepoint.h>
 
-TRACE_EVENT(in_packet,
-       TP_PROTO(const struct amdtp_stream *s, u32 cycles, u32 *cip_header, unsigned int payload_length, unsigned int index),
-       TP_ARGS(s, cycles, cip_header, payload_length, index),
-       TP_STRUCT__entry(
-               __field(unsigned int, second)
-               __field(unsigned int, cycle)
-               __field(int, channel)
-               __field(int, src)
-               __field(int, dest)
-               __field(u32, cip_header0)
-               __field(u32, cip_header1)
-               __field(unsigned int, payload_quadlets)
-               __field(unsigned int, packet_index)
-               __field(unsigned int, irq)
-               __field(unsigned int, index)
-       ),
-       TP_fast_assign(
-               __entry->second = cycles / CYCLES_PER_SECOND;
-               __entry->cycle = cycles % CYCLES_PER_SECOND;
-               __entry->channel = s->context->channel;
-               __entry->src = fw_parent_device(s->unit)->node_id;
-               __entry->dest = fw_parent_device(s->unit)->card->node_id;
-               __entry->cip_header0 = cip_header[0];
-               __entry->cip_header1 = cip_header[1];
-               __entry->payload_quadlets = payload_length / 4;
-               __entry->packet_index = s->packet_index;
-               __entry->irq = !!in_interrupt();
-               __entry->index = index;
-       ),
-       TP_printk(
-               "%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u",
-               __entry->second,
-               __entry->cycle,
-               __entry->src,
-               __entry->dest,
-               __entry->channel,
-               __entry->cip_header0,
-               __entry->cip_header1,
-               __entry->payload_quadlets,
-               __entry->packet_index,
-               __entry->irq,
-               __entry->index)
-);
-
-TRACE_EVENT(out_packet,
-       TP_PROTO(const struct amdtp_stream *s, u32 cycles, __be32 *cip_header, unsigned int payload_length, unsigned int index),
-       TP_ARGS(s, cycles, cip_header, payload_length, index),
-       TP_STRUCT__entry(
-               __field(unsigned int, second)
-               __field(unsigned int, cycle)
-               __field(int, channel)
-               __field(int, src)
-               __field(int, dest)
-               __field(u32, cip_header0)
-               __field(u32, cip_header1)
-               __field(unsigned int, payload_quadlets)
-               __field(unsigned int, packet_index)
-               __field(unsigned int, irq)
-               __field(unsigned int, index)
-       ),
-       TP_fast_assign(
-               __entry->second = cycles / CYCLES_PER_SECOND;
-               __entry->cycle = cycles % CYCLES_PER_SECOND;
-               __entry->channel = s->context->channel;
-               __entry->src = fw_parent_device(s->unit)->card->node_id;
-               __entry->dest = fw_parent_device(s->unit)->node_id;
-               __entry->cip_header0 = be32_to_cpu(cip_header[0]);
-               __entry->cip_header1 = be32_to_cpu(cip_header[1]);
-               __entry->payload_quadlets = payload_length / 4;
-               __entry->packet_index = s->packet_index;
-               __entry->irq = !!in_interrupt();
-               __entry->index = index;
-       ),
-       TP_printk(
-               "%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u",
-               __entry->second,
-               __entry->cycle,
-               __entry->src,
-               __entry->dest,
-               __entry->channel,
-               __entry->cip_header0,
-               __entry->cip_header1,
-               __entry->payload_quadlets,
-               __entry->packet_index,
-               __entry->irq,
-               __entry->index)
-);
-
-TRACE_EVENT(in_packet_without_header,
-       TP_PROTO(const struct amdtp_stream *s, u32 cycles, unsigned int payload_quadlets, unsigned int data_blocks, unsigned int index),
-       TP_ARGS(s, cycles, payload_quadlets, data_blocks, index),
-       TP_STRUCT__entry(
-               __field(unsigned int, second)
-               __field(unsigned int, cycle)
-               __field(int, channel)
-               __field(int, src)
-               __field(int, dest)
-               __field(unsigned int, payload_quadlets)
-               __field(unsigned int, data_blocks)
-               __field(unsigned int, data_block_counter)
-               __field(unsigned int, packet_index)
-               __field(unsigned int, irq)
-               __field(unsigned int, index)
-       ),
-       TP_fast_assign(
-               __entry->second = cycles / CYCLES_PER_SECOND;
-               __entry->cycle = cycles % CYCLES_PER_SECOND;
-               __entry->channel = s->context->channel;
-               __entry->src = fw_parent_device(s->unit)->node_id;
-               __entry->dest = fw_parent_device(s->unit)->card->node_id;
-               __entry->payload_quadlets = payload_quadlets;
-               __entry->data_blocks = data_blocks,
-               __entry->data_block_counter = s->data_block_counter,
-               __entry->packet_index = s->packet_index;
-               __entry->irq = !!in_interrupt();
-               __entry->index = index;
-       ),
-       TP_printk(
-               "%02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u",
-               __entry->second,
-               __entry->cycle,
-               __entry->src,
-               __entry->dest,
-               __entry->channel,
-               __entry->payload_quadlets,
-               __entry->data_blocks,
-               __entry->data_block_counter,
-               __entry->packet_index,
-               __entry->irq,
-               __entry->index)
-);
-
-TRACE_EVENT(out_packet_without_header,
-       TP_PROTO(const struct amdtp_stream *s, u32 cycles, unsigned int payload_length, unsigned int data_blocks, unsigned int index),
-       TP_ARGS(s, cycles, payload_length, data_blocks, index),
+TRACE_EVENT(amdtp_packet,
+       TP_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int index),
+       TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, index),
        TP_STRUCT__entry(
                __field(unsigned int, second)
                __field(unsigned int, cycle)
                __field(int, channel)
                __field(int, src)
                __field(int, dest)
+               __dynamic_array(u8, cip_header, cip_header ? 8 : 0)
                __field(unsigned int, payload_quadlets)
                __field(unsigned int, data_blocks)
                __field(unsigned int, data_block_counter)
@@ -165,17 +34,26 @@ TRACE_EVENT(out_packet_without_header,
                __entry->second = cycles / CYCLES_PER_SECOND;
                __entry->cycle = cycles % CYCLES_PER_SECOND;
                __entry->channel = s->context->channel;
-               __entry->src = fw_parent_device(s->unit)->card->node_id;
-               __entry->dest = fw_parent_device(s->unit)->node_id;
-               __entry->payload_quadlets = payload_length / 4;
-               __entry->data_blocks = data_blocks,
+               if (s->direction == AMDTP_IN_STREAM) {
+                       __entry->src = fw_parent_device(s->unit)->node_id;
+                       __entry->dest = fw_parent_device(s->unit)->card->node_id;
+               } else {
+                       __entry->src = fw_parent_device(s->unit)->card->node_id;
+                       __entry->dest = fw_parent_device(s->unit)->node_id;
+               }
+               if (cip_header) {
+                       memcpy(__get_dynamic_array(cip_header), cip_header,
+                              __get_dynamic_array_len(cip_header));
+               }
+               __entry->payload_quadlets = payload_length / sizeof(__be32);
+               __entry->data_blocks = data_blocks;
                __entry->data_block_counter = s->data_block_counter,
                __entry->packet_index = s->packet_index;
                __entry->irq = !!in_interrupt();
                __entry->index = index;
        ),
        TP_printk(
-               "%02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u",
+               "%02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u %s",
                __entry->second,
                __entry->cycle,
                __entry->src,
@@ -186,7 +64,10 @@ TRACE_EVENT(out_packet_without_header,
                __entry->data_block_counter,
                __entry->packet_index,
                __entry->irq,
-               __entry->index)
+               __entry->index,
+               __print_array(__get_dynamic_array(cip_header),
+                             __get_dynamic_array_len(cip_header),
+                             sizeof(u8)))
 );
 
 #endif
index 68f5fa4b183d64f5838c905666349fba1c632368..87a46bd604962a6bc44df21da681e45ac498d9cc 100644 (file)
 #define INTERRUPT_INTERVAL     16
 #define QUEUE_LENGTH           48
 
-#define IR_HEADER_SIZE         8       // For header and timestamp.
-#define OUT_PACKET_HEADER_SIZE 0
+// For iso header, tstamp and 2 CIP header.
+#define IR_CTX_HEADER_SIZE_CIP         16
+// For iso header and tstamp.
+#define IR_CTX_HEADER_SIZE_NO_CIP      8
 #define HEADER_TSTAMP_MASK     0x0000ffff
 
+#define IT_PKT_HEADER_SIZE_CIP         8 // For 2 CIP header.
+#define IT_PKT_HEADER_SIZE_NO_CIP      0 // Nothing.
+
 static void pcm_period_tasklet(unsigned long data);
 
 /**
@@ -260,11 +265,18 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
        s->data_block_quadlets = data_block_quadlets;
        s->syt_interval = amdtp_syt_intervals[sfc];
 
-       /* default buffering in the device */
-       s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
-       if (s->flags & CIP_BLOCKING)
-               /* additional buffering needed to adjust for no-data packets */
-               s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
+       // default buffering in the device.
+       if (s->direction == AMDTP_OUT_STREAM) {
+               s->ctx_data.rx.transfer_delay =
+                                       TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
+
+               if (s->flags & CIP_BLOCKING) {
+                       // additional buffering needed to adjust for no-data
+                       // packets.
+                       s->ctx_data.rx.transfer_delay +=
+                               TICKS_PER_SECOND * s->syt_interval / rate;
+               }
+       }
 
        return 0;
 }
@@ -280,15 +292,15 @@ EXPORT_SYMBOL(amdtp_stream_set_parameters);
 unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
 {
        unsigned int multiplier = 1;
-       unsigned int header_size = 0;
+       unsigned int cip_header_size = 0;
 
        if (s->flags & CIP_JUMBO_PAYLOAD)
                multiplier = 5;
        if (!(s->flags & CIP_NO_HEADER))
-               header_size = 8;
+               cip_header_size = sizeof(__be32) * 2;
 
-       return header_size +
-               s->syt_interval * s->data_block_quadlets * 4 * multiplier;
+       return cip_header_size +
+               s->syt_interval * s->data_block_quadlets * sizeof(__be32) * multiplier;
 }
 EXPORT_SYMBOL(amdtp_stream_get_max_payload);
 
@@ -321,10 +333,10 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s,
        /* Non-blocking mode. */
        } else {
                if (!cip_sfc_is_base_44100(s->sfc)) {
-                       /* Sample_rate / 8000 is an integer, and precomputed. */
-                       data_blocks = s->data_block_state;
+                       // Sample_rate / 8000 is an integer, and precomputed.
+                       data_blocks = s->ctx_data.rx.data_block_state;
                } else {
-                       phase = s->data_block_state;
+                       phase = s->ctx_data.rx.data_block_state;
 
                /*
                 * This calculates the number of data blocks per packet so that
@@ -343,7 +355,7 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s,
                                data_blocks = 11 * (s->sfc >> 1) + (phase == 0);
                        if (++phase >= (80 >> (s->sfc >> 1)))
                                phase = 0;
-                       s->data_block_state = phase;
+                       s->ctx_data.rx.data_block_state = phase;
                }
        }
 
@@ -355,9 +367,10 @@ static unsigned int calculate_syt(struct amdtp_stream *s,
 {
        unsigned int syt_offset, phase, index, syt;
 
-       if (s->last_syt_offset < TICKS_PER_CYCLE) {
+       if (s->ctx_data.rx.last_syt_offset < TICKS_PER_CYCLE) {
                if (!cip_sfc_is_base_44100(s->sfc))
-                       syt_offset = s->last_syt_offset + s->syt_offset_state;
+                       syt_offset = s->ctx_data.rx.last_syt_offset +
+                                    s->ctx_data.rx.syt_offset_state;
                else {
                /*
                 * The time, in ticks, of the n'th SYT_INTERVAL sample is:
@@ -369,21 +382,21 @@ static unsigned int calculate_syt(struct amdtp_stream *s,
                 *   1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ...
                 * This code generates _exactly_ the same sequence.
                 */
-                       phase = s->syt_offset_state;
+                       phase = s->ctx_data.rx.syt_offset_state;
                        index = phase % 13;
-                       syt_offset = s->last_syt_offset;
+                       syt_offset = s->ctx_data.rx.last_syt_offset;
                        syt_offset += 1386 + ((index && !(index & 3)) ||
                                              phase == 146);
                        if (++phase >= 147)
                                phase = 0;
-                       s->syt_offset_state = phase;
+                       s->ctx_data.rx.syt_offset_state = phase;
                }
        } else
-               syt_offset = s->last_syt_offset - TICKS_PER_CYCLE;
-       s->last_syt_offset = syt_offset;
+               syt_offset = s->ctx_data.rx.last_syt_offset - TICKS_PER_CYCLE;
+       s->ctx_data.rx.last_syt_offset = syt_offset;
 
        if (syt_offset < TICKS_PER_CYCLE) {
-               syt_offset += s->transfer_delay;
+               syt_offset += s->ctx_data.rx.transfer_delay;
                syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12;
                syt += syt_offset % TICKS_PER_CYCLE;
 
@@ -420,23 +433,15 @@ static void pcm_period_tasklet(unsigned long data)
                snd_pcm_period_elapsed(pcm);
 }
 
-static int queue_packet(struct amdtp_stream *s, unsigned int header_length,
-                       unsigned int payload_length)
+static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params)
 {
-       struct fw_iso_packet p = {0};
-       int err = 0;
+       int err;
 
-       if (IS_ERR(s->context))
-               goto end;
+       params->interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
+       params->tag = s->tag;
+       params->sy = 0;
 
-       p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
-       p.tag = s->tag;
-       p.header_length = header_length;
-       if (payload_length > 0)
-               p.payload_length = payload_length;
-       else
-               p.skip = true;
-       err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer,
+       err = fw_iso_context_queue(s->context, params, &s->buffer.iso_buffer,
                                   s->buffer.packets[s->packet_index].offset);
        if (err < 0) {
                dev_err(&s->unit->device, "queueing error: %d\n", err);
@@ -450,112 +455,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'.
@@ -567,9 +541,7 @@ static int handle_in_packet(struct amdtp_stream *s,
                dev_info_ratelimited(&s->unit->device,
                                "Invalid CIP header for AMDTP: %08X:%08X\n",
                                cip_header[0], cip_header[1]);
-               data_blocks = 0;
-               pcm_frames = 0;
-               goto end;
+               return -EAGAIN;
        }
 
        /* Check valid protocol or not. */
@@ -579,19 +551,17 @@ static int handle_in_packet(struct amdtp_stream *s,
                dev_info_ratelimited(&s->unit->device,
                                     "Detect unexpected protocol: %08x %08x\n",
                                     cip_header[0], cip_header[1]);
-               data_blocks = 0;
-               pcm_frames = 0;
-               goto end;
+               return -EAGAIN;
        }
 
        /* Calculate data blocks */
        fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
-       if (payload_length < 12 ||
+       if (payload_length < sizeof(__be32) * 2 ||
            (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
-               data_blocks = 0;
+               *data_blocks = 0;
        } else {
-               data_block_quadlets =
-                       (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
+               unsigned int data_block_quadlets =
+                               (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
                /* avoid division by zero */
                if (data_block_quadlets == 0) {
                        dev_err(&s->unit->device,
@@ -602,95 +572,97 @@ static int handle_in_packet(struct amdtp_stream *s,
                if (s->flags & CIP_WRONG_DBS)
                        data_block_quadlets = s->data_block_quadlets;
 
-               data_blocks = (payload_length / 4 - 2) /
+               *data_blocks = (payload_length / sizeof(__be32) - 2) /
                                                        data_block_quadlets;
        }
 
        /* Check data block counter continuity */
-       data_block_counter = cip_header[0] & CIP_DBC_MASK;
-       if (data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
+       *dbc = cip_header[0] & CIP_DBC_MASK;
+       if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
            s->data_block_counter != UINT_MAX)
-               data_block_counter = s->data_block_counter;
+               *dbc = s->data_block_counter;
 
        if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) &&
-            data_block_counter == s->tx_first_dbc) ||
+            *dbc == s->ctx_data.tx.first_dbc) ||
            s->data_block_counter == UINT_MAX) {
                lost = false;
        } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
-               lost = data_block_counter != s->data_block_counter;
+               lost = *dbc != s->data_block_counter;
        } else {
-               if (data_blocks > 0 && s->tx_dbc_interval > 0)
-                       dbc_interval = s->tx_dbc_interval;
+               unsigned int dbc_interval;
+
+               if (*data_blocks > 0 && s->ctx_data.tx.dbc_interval > 0)
+                       dbc_interval = s->ctx_data.tx.dbc_interval;
                else
-                       dbc_interval = data_blocks;
+                       dbc_interval = *data_blocks;
 
-               lost = data_block_counter !=
-                      ((s->data_block_counter + dbc_interval) & 0xff);
+               lost = *dbc != ((s->data_block_counter + dbc_interval) & 0xff);
        }
 
        if (lost) {
                dev_err(&s->unit->device,
                        "Detect discontinuity of CIP: %02X %02X\n",
-                       s->data_block_counter, data_block_counter);
+                       s->data_block_counter, *dbc);
                return -EIO;
        }
 
-       syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
-       pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
-
-       if (s->flags & CIP_DBC_IS_END_EVENT)
-               s->data_block_counter = data_block_counter;
-       else
-               s->data_block_counter =
-                               (data_block_counter + data_blocks) & 0xff;
-end:
-       if (queue_in_packet(s) < 0)
-               return -EIO;
-
-       pcm = READ_ONCE(s->pcm);
-       if (pcm && pcm_frames > 0)
-               update_pcm_pointers(s, pcm, pcm_frames);
+       *syt = cip_header[1] & CIP_SYT_MASK;
 
        return 0;
 }
 
-static int handle_in_packet_without_header(struct amdtp_stream *s,
-                       unsigned int payload_length, unsigned int cycle,
-                       unsigned int index)
+static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
+                              const __be32 *ctx_header,
+                              unsigned int *payload_length,
+                              unsigned int *data_blocks, unsigned int *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);
 }
 
@@ -702,31 +674,66 @@ static inline u32 increment_cycle_count(u32 cycle, unsigned int addend)
        return cycle;
 }
 
+// Align to actual cycle count for the packet which is going to be scheduled.
+// This module queued the same number of isochronous cycle as QUEUE_LENGTH to
+// skip isochronous cycle, therefore it's OK to just increment the cycle by
+// QUEUE_LENGTH for scheduled cycle.
+static inline u32 compute_it_cycle(const __be32 ctx_header_tstamp)
+{
+       u32 cycle = compute_cycle_count(ctx_header_tstamp);
+       return increment_cycle_count(cycle, QUEUE_LENGTH);
+}
+
+static inline void cancel_stream(struct amdtp_stream *s)
+{
+       s->packet_index = -1;
+       if (in_interrupt())
+               amdtp_stream_pcm_abort(s);
+       WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN);
+}
+
 static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
                                size_t header_length, void *header,
                                void *private_data)
 {
        struct amdtp_stream *s = private_data;
-       unsigned int i, packets = header_length / 4;
-       u32 cycle;
+       const __be32 *ctx_header = header;
+       unsigned int 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);
@@ -738,46 +745,56 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
 {
        struct amdtp_stream *s = private_data;
        unsigned int i, packets;
-       unsigned int payload_length, max_payload_length;
        __be32 *ctx_header = header;
 
        if (s->packet_index < 0)
                return;
 
-       /* The number of packets in buffer */
-       packets = header_length / IR_HEADER_SIZE;
-
-       /* For buffer-over-run prevention. */
-       max_payload_length = s->max_payload_length;
+       // The number of packets in buffer.
+       packets = header_length / s->ctx_data.tx.ctx_header_size;
 
        for (i = 0; i < packets; i++) {
-               u32 iso_header = be32_to_cpu(ctx_header[0]);
-               unsigned int cycle;
+               u32 cycle;
+               unsigned int payload_length;
+               unsigned int data_blocks;
+               unsigned int 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;
        }
 
@@ -790,9 +807,8 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
                                        void *header, void *private_data)
 {
        struct amdtp_stream *s = private_data;
-       __be32 *ctx_header = header;
+       const __be32 *ctx_header = header;
        u32 cycle;
-       unsigned int packets;
 
        /*
         * For in-stream, first packet has come.
@@ -802,23 +818,13 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
        wake_up(&s->callback_wait);
 
        if (s->direction == AMDTP_IN_STREAM) {
-               tstamp = be32_to_cpu(ctx_header[1]) & HEADER_TSTAMP_MASK;
-               cycle = compute_cycle_count(tstamp);
+               cycle = compute_cycle_count(ctx_header[1]);
 
                context->callback.sc = in_stream_callback;
-               if (s->flags & CIP_NO_HEADER)
-                       s->handle_packet = handle_in_packet_without_header;
-               else
-                       s->handle_packet = handle_in_packet;
        } else {
-               packets = header_length / 4;
-               cycle = compute_cycle_count(tstamp);
-               cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets);
+               cycle = compute_it_cycle(*ctx_header);
+
                context->callback.sc = out_stream_callback;
-               if (s->flags & CIP_NO_HEADER)
-                       s->handle_packet = handle_out_packet_without_header;
-               else
-                       s->handle_packet = handle_out_packet;
        }
 
        s->start_cycle = cycle;
@@ -841,7 +847,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
        static const struct {
                unsigned int data_block;
                unsigned int syt_offset;
-       } initial_state[] = {
+       } *entry, initial_state[] = {
                [CIP_SFC_32000]  = {  4, 3072 },
                [CIP_SFC_48000]  = {  6, 1024 },
                [CIP_SFC_96000]  = { 12, 1024 },
@@ -850,7 +856,8 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
                [CIP_SFC_88200]  = {  0,   67 },
                [CIP_SFC_176400] = {  0,   67 },
        };
-       unsigned int header_size;
+       unsigned int ctx_header_size;
+       unsigned int max_ctx_payload_size;
        enum dma_data_direction dir;
        int type, tag, err;
 
@@ -862,32 +869,46 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
                goto err_unlock;
        }
 
-       if (s->direction == AMDTP_IN_STREAM)
+       if (s->direction == AMDTP_IN_STREAM) {
                s->data_block_counter = UINT_MAX;
-       else
+       } else {
+               entry = &initial_state[s->sfc];
+
                s->data_block_counter = 0;
-       s->data_block_state = initial_state[s->sfc].data_block;
-       s->syt_offset_state = initial_state[s->sfc].syt_offset;
-       s->last_syt_offset = TICKS_PER_CYCLE;
+               s->ctx_data.rx.data_block_state = entry->data_block;
+               s->ctx_data.rx.syt_offset_state = entry->syt_offset;
+               s->ctx_data.rx.last_syt_offset = TICKS_PER_CYCLE;
+       }
 
        /* initialize packet buffer */
        if (s->direction == AMDTP_IN_STREAM) {
                dir = DMA_FROM_DEVICE;
                type = FW_ISO_CONTEXT_RECEIVE;
-               header_size = IR_HEADER_SIZE;
+               if (!(s->flags & CIP_NO_HEADER))
+                       ctx_header_size = IR_CTX_HEADER_SIZE_CIP;
+               else
+                       ctx_header_size = IR_CTX_HEADER_SIZE_NO_CIP;
+
+               max_ctx_payload_size = amdtp_stream_get_max_payload(s) -
+                                      ctx_header_size;
        } else {
                dir = DMA_TO_DEVICE;
                type = FW_ISO_CONTEXT_TRANSMIT;
-               header_size = OUT_PACKET_HEADER_SIZE;
+               ctx_header_size = 0;    // No effect for IT context.
+
+               max_ctx_payload_size = amdtp_stream_get_max_payload(s);
+               if (!(s->flags & CIP_NO_HEADER))
+                       max_ctx_payload_size -= IT_PKT_HEADER_SIZE_CIP;
        }
+
        err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH,
-                                     amdtp_stream_get_max_payload(s), dir);
+                                     max_ctx_payload_size, dir);
        if (err < 0)
                goto err_unlock;
 
        s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
-                                          type, channel, speed, header_size,
-                                          amdtp_stream_first_callback, s);
+                                         type, channel, speed, ctx_header_size,
+                                         amdtp_stream_first_callback, s);
        if (IS_ERR(s->context)) {
                err = PTR_ERR(s->context);
                if (err == -EBUSY)
@@ -898,8 +919,10 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
 
        amdtp_stream_update(s);
 
-       if (s->direction == AMDTP_IN_STREAM)
-               s->max_payload_length = amdtp_stream_get_max_payload(s);
+       if (s->direction == AMDTP_IN_STREAM) {
+               s->ctx_data.tx.max_ctx_payload_length = max_ctx_payload_size;
+               s->ctx_data.tx.ctx_header_size = ctx_header_size;
+       }
 
        if (s->flags & CIP_NO_HEADER)
                s->tag = TAG_NO_CIP_HEADER;
@@ -908,10 +931,14 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
 
        s->packet_index = 0;
        do {
-               if (s->direction == AMDTP_IN_STREAM)
-                       err = queue_in_packet(s);
-               else
-                       err = queue_out_packet(s, 0);
+               struct fw_iso_packet params;
+               if (s->direction == AMDTP_IN_STREAM) {
+                       err = queue_in_packet(s, &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 e45de3eecfe39cb9b3b2f928f9a50307b05a3210..3942894c11ac2e319d30b0e86220579f668149b2 100644 (file)
@@ -108,10 +108,31 @@ struct amdtp_stream {
        struct iso_packets_buffer buffer;
        int packet_index;
        int tag;
-       int (*handle_packet)(struct amdtp_stream *s,
-                       unsigned int payload_quadlets, unsigned int cycle,
-                       unsigned int index);
-       unsigned int max_payload_length;
+       union {
+               struct {
+                       unsigned int ctx_header_size;
+
+                       // limit for payload of iso packet.
+                       unsigned int max_ctx_payload_length;
+
+                       // For quirks of CIP headers.
+                       // Fixed interval of dbc between previos/current
+                       // packets.
+                       unsigned int dbc_interval;
+                       // Indicate the value of dbc field in a first packet.
+                       unsigned int first_dbc;
+               } tx;
+               struct {
+                       // To calculate CIP data blocks and tstamp.
+                       unsigned int transfer_delay;
+                       unsigned int data_block_state;
+                       unsigned int last_syt_offset;
+                       unsigned int syt_offset_state;
+
+                       // To generate CIP header.
+                       unsigned int fdf;
+               } rx;
+       } ctx_data;
 
        /* For CIP headers. */
        unsigned int source_node_id_field;
@@ -119,19 +140,10 @@ struct amdtp_stream {
        unsigned int data_block_counter;
        unsigned int sph;
        unsigned int fmt;
-       unsigned int fdf;
-       /* quirk: fixed interval of dbc between previos/current packets. */
-       unsigned int tx_dbc_interval;
-       /* quirk: indicate the value of dbc field in a first packet. */
-       unsigned int tx_first_dbc;
 
        /* Internal flags. */
        enum cip_sfc sfc;
        unsigned int syt_interval;
-       unsigned int transfer_delay;
-       unsigned int data_block_state;
-       unsigned int last_syt_offset;
-       unsigned int syt_offset_state;
 
        /* For a PCM substream processing. */
        struct snd_pcm_substream *pcm;
index af71dac9f084080026598528dbf28f9ad56adca5..9e0b689fe34a3796cfc8573011b2d9f9d8b447e6 100644 (file)
@@ -92,8 +92,6 @@ struct snd_bebob {
        unsigned int midi_input_ports;
        unsigned int midi_output_ports;
 
-       bool connected;
-
        struct amdtp_stream tx_stream;
        struct amdtp_stream rx_stream;
        struct cmp_connection out_conn;
@@ -217,7 +215,8 @@ int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
                                   enum snd_bebob_clock_type *src);
 int snd_bebob_stream_discover(struct snd_bebob *bebob);
 int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
-int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate);
+int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate);
+int snd_bebob_stream_start_duplex(struct snd_bebob *bebob);
 void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob);
 void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob);
 
index c54ac42622ad5deab59621ed27c19d7397add1dc..4d8805fa8a00a1f06ea5115aaaa89802ac685d52 100644 (file)
@@ -7,58 +7,31 @@
 
 #include "bebob.h"
 
-static int midi_capture_open(struct snd_rawmidi_substream *substream)
+static int midi_open(struct snd_rawmidi_substream *substream)
 {
        struct snd_bebob *bebob = substream->rmidi->private_data;
        int err;
 
        err = snd_bebob_stream_lock_try(bebob);
        if (err < 0)
-               goto end;
+               return err;
 
        mutex_lock(&bebob->mutex);
-       bebob->substreams_counter++;
-       err = snd_bebob_stream_start_duplex(bebob, 0);
+       err = snd_bebob_stream_reserve_duplex(bebob, 0);
+       if (err >= 0) {
+               ++bebob->substreams_counter;
+               err = snd_bebob_stream_start_duplex(bebob);
+               if (err < 0)
+                       --bebob->substreams_counter;
+       }
        mutex_unlock(&bebob->mutex);
        if (err < 0)
                snd_bebob_stream_lock_release(bebob);
-end:
-       return err;
-}
-
-static int midi_playback_open(struct snd_rawmidi_substream *substream)
-{
-       struct snd_bebob *bebob = substream->rmidi->private_data;
-       int err;
-
-       err = snd_bebob_stream_lock_try(bebob);
-       if (err < 0)
-               goto end;
 
-       mutex_lock(&bebob->mutex);
-       bebob->substreams_counter++;
-       err = snd_bebob_stream_start_duplex(bebob, 0);
-       mutex_unlock(&bebob->mutex);
-       if (err < 0)
-               snd_bebob_stream_lock_release(bebob);
-end:
        return err;
 }
 
-static int midi_capture_close(struct snd_rawmidi_substream *substream)
-{
-       struct snd_bebob *bebob = substream->rmidi->private_data;
-
-       mutex_lock(&bebob->mutex);
-       bebob->substreams_counter--;
-       snd_bebob_stream_stop_duplex(bebob);
-       mutex_unlock(&bebob->mutex);
-
-       snd_bebob_stream_lock_release(bebob);
-       return 0;
-}
-
-static int midi_playback_close(struct snd_rawmidi_substream *substream)
+static int midi_close(struct snd_rawmidi_substream *substream)
 {
        struct snd_bebob *bebob = substream->rmidi->private_data;
 
@@ -120,13 +93,13 @@ static void set_midi_substream_names(struct snd_bebob *bebob,
 int snd_bebob_create_midi_devices(struct snd_bebob *bebob)
 {
        static const struct snd_rawmidi_ops capture_ops = {
-               .open           = midi_capture_open,
-               .close          = midi_capture_close,
+               .open           = midi_open,
+               .close          = midi_close,
                .trigger        = midi_capture_trigger,
        };
        static const struct snd_rawmidi_ops playback_ops = {
-               .open           = midi_playback_open,
-               .close          = midi_playback_close,
+               .open           = midi_open,
+               .close          = midi_close,
                .trigger        = midi_playback_trigger,
        };
        struct snd_rawmidi *rmidi;
index 2f50ec7b0147e368ca768d058cfba52de0f3730b..0fb9eed468378e53643f4f76425a784cf1281be9 100644 (file)
@@ -184,9 +184,8 @@ pcm_close(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static int
-pcm_capture_hw_params(struct snd_pcm_substream *substream,
-                     struct snd_pcm_hw_params *hw_params)
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+                        struct snd_pcm_hw_params *hw_params)
 {
        struct snd_bebob *bebob = substream->private_data;
        int err;
@@ -197,62 +196,31 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream,
                return err;
 
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
-               mutex_lock(&bebob->mutex);
-               bebob->substreams_counter++;
-               mutex_unlock(&bebob->mutex);
-       }
+               unsigned int rate = params_rate(hw_params);
 
-       return 0;
-}
-static int
-pcm_playback_hw_params(struct snd_pcm_substream *substream,
-                      struct snd_pcm_hw_params *hw_params)
-{
-       struct snd_bebob *bebob = substream->private_data;
-       int err;
-
-       err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
-                                              params_buffer_bytes(hw_params));
-       if (err < 0)
-               return err;
-
-       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
                mutex_lock(&bebob->mutex);
-               bebob->substreams_counter++;
+               err = snd_bebob_stream_reserve_duplex(bebob, rate);
+               if (err >= 0)
+                       ++bebob->substreams_counter;
                mutex_unlock(&bebob->mutex);
        }
 
-       return 0;
+       return err;
 }
 
-static int
-pcm_capture_hw_free(struct snd_pcm_substream *substream)
+static int pcm_hw_free(struct snd_pcm_substream *substream)
 {
        struct snd_bebob *bebob = substream->private_data;
 
-       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
-               mutex_lock(&bebob->mutex);
-               bebob->substreams_counter--;
-               mutex_unlock(&bebob->mutex);
-       }
-
-       snd_bebob_stream_stop_duplex(bebob);
+       mutex_lock(&bebob->mutex);
 
-       return snd_pcm_lib_free_vmalloc_buffer(substream);
-}
-static int
-pcm_playback_hw_free(struct snd_pcm_substream *substream)
-{
-       struct snd_bebob *bebob = substream->private_data;
-
-       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
-               mutex_lock(&bebob->mutex);
+       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
                bebob->substreams_counter--;
-               mutex_unlock(&bebob->mutex);
-       }
 
        snd_bebob_stream_stop_duplex(bebob);
 
+       mutex_unlock(&bebob->mutex);
+
        return snd_pcm_lib_free_vmalloc_buffer(substream);
 }
 
@@ -260,10 +228,9 @@ static int
 pcm_capture_prepare(struct snd_pcm_substream *substream)
 {
        struct snd_bebob *bebob = substream->private_data;
-       struct snd_pcm_runtime *runtime = substream->runtime;
        int err;
 
-       err = snd_bebob_stream_start_duplex(bebob, runtime->rate);
+       err = snd_bebob_stream_start_duplex(bebob);
        if (err >= 0)
                amdtp_stream_pcm_prepare(&bebob->tx_stream);
 
@@ -273,10 +240,9 @@ static int
 pcm_playback_prepare(struct snd_pcm_substream *substream)
 {
        struct snd_bebob *bebob = substream->private_data;
-       struct snd_pcm_runtime *runtime = substream->runtime;
        int err;
 
-       err = snd_bebob_stream_start_duplex(bebob, runtime->rate);
+       err = snd_bebob_stream_start_duplex(bebob);
        if (err >= 0)
                amdtp_stream_pcm_prepare(&bebob->rx_stream);
 
@@ -353,8 +319,8 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
                .open           = pcm_open,
                .close          = pcm_close,
                .ioctl          = snd_pcm_lib_ioctl,
-               .hw_params      = pcm_capture_hw_params,
-               .hw_free        = pcm_capture_hw_free,
+               .hw_params      = pcm_hw_params,
+               .hw_free        = pcm_hw_free,
                .prepare        = pcm_capture_prepare,
                .trigger        = pcm_capture_trigger,
                .pointer        = pcm_capture_pointer,
@@ -365,8 +331,8 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
                .open           = pcm_open,
                .close          = pcm_close,
                .ioctl          = snd_pcm_lib_ioctl,
-               .hw_params      = pcm_playback_hw_params,
-               .hw_free        = pcm_playback_hw_free,
+               .hw_params      = pcm_hw_params,
+               .hw_free        = pcm_hw_free,
                .prepare        = pcm_playback_prepare,
                .trigger        = pcm_playback_trigger,
                .pointer        = pcm_playback_pointer,
index 0c93a825cb989904572c4856a742c3379d253b41..334dc7c96e1d5131b08a05b23cfc52649c97ce15 100644 (file)
@@ -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)
 {
@@ -417,49 +399,21 @@ check_connection_used_by_others(struct snd_bebob *bebob, struct amdtp_stream *s)
        return err;
 }
 
-static int
-make_both_connections(struct snd_bebob *bebob, unsigned int rate)
+static int make_both_connections(struct snd_bebob *bebob)
 {
-       int index, pcm_channels, midi_channels, err = 0;
-
-       if (bebob->connected)
-               goto end;
-
-       /* confirm params for both streams */
-       err = get_formation_index(rate, &index);
-       if (err < 0)
-               goto end;
-       pcm_channels = bebob->tx_stream_formations[index].pcm;
-       midi_channels = bebob->tx_stream_formations[index].midi;
-       err = amdtp_am824_set_parameters(&bebob->tx_stream, rate,
-                                        pcm_channels, midi_channels * 8,
-                                        false);
-       if (err < 0)
-               goto end;
+       int err = 0;
 
-       pcm_channels = bebob->rx_stream_formations[index].pcm;
-       midi_channels = bebob->rx_stream_formations[index].midi;
-       err = amdtp_am824_set_parameters(&bebob->rx_stream, rate,
-                                        pcm_channels, midi_channels * 8,
-                                        false);
+       err = cmp_connection_establish(&bebob->out_conn);
        if (err < 0)
-               goto end;
+               return err;
 
-       /* establish connections for both streams */
-       err = cmp_connection_establish(&bebob->out_conn,
-                       amdtp_stream_get_max_payload(&bebob->tx_stream));
-       if (err < 0)
-               goto end;
-       err = cmp_connection_establish(&bebob->in_conn,
-                       amdtp_stream_get_max_payload(&bebob->rx_stream));
+       err = cmp_connection_establish(&bebob->in_conn);
        if (err < 0) {
                cmp_connection_break(&bebob->out_conn);
-               goto end;
+               return err;
        }
 
-       bebob->connected = true;
-end:
-       return err;
+       return 0;
 }
 
 static void
@@ -468,23 +422,13 @@ break_both_connections(struct snd_bebob *bebob)
        cmp_connection_break(&bebob->in_conn);
        cmp_connection_break(&bebob->out_conn);
 
-       bebob->connected = false;
-
        /* These models seems to be in transition state for a longer time. */
        if (bebob->maudio_special_quirk != NULL)
                msleep(200);
 }
 
-static void
-destroy_both_connections(struct snd_bebob *bebob)
-{
-       cmp_connection_destroy(&bebob->in_conn);
-       cmp_connection_destroy(&bebob->out_conn);
-}
-
 static int
-start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream,
-            unsigned int rate)
+start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
 {
        struct cmp_connection *conn;
        int err = 0;
@@ -509,190 +453,252 @@ end:
        return err;
 }
 
-int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
+static int init_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
 {
+       enum amdtp_stream_direction dir_stream;
+       struct cmp_connection *conn;
+       enum cmp_direction dir_conn;
        int err;
 
-       err = init_both_connections(bebob);
+       if (stream == &bebob->tx_stream) {
+               dir_stream = AMDTP_IN_STREAM;
+               conn = &bebob->out_conn;
+               dir_conn = CMP_OUTPUT;
+       } else {
+               dir_stream = AMDTP_OUT_STREAM;
+               conn = &bebob->in_conn;
+               dir_conn = CMP_INPUT;
+       }
+
+       err = cmp_connection_init(conn, bebob->unit, dir_conn, 0);
        if (err < 0)
-               goto end;
+               return err;
 
-       err = amdtp_am824_init(&bebob->tx_stream, bebob->unit,
-                              AMDTP_IN_STREAM, CIP_BLOCKING);
+       err = amdtp_am824_init(stream, bebob->unit, dir_stream, CIP_BLOCKING);
        if (err < 0) {
-               amdtp_stream_destroy(&bebob->tx_stream);
-               destroy_both_connections(bebob);
-               goto end;
+               cmp_connection_destroy(conn);
+               return err;
        }
 
-       /*
-        * BeBoB v3 transfers packets with these qurks:
-        *  - In the beginning of streaming, the value of dbc is incremented
-        *    even if no data blocks are transferred.
-        *  - The value of dbc is reset suddenly.
-        */
-       if (bebob->version > 2)
-               bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC |
-                                         CIP_SKIP_DBC_ZERO_CHECK;
+       if (stream == &bebob->tx_stream) {
+               // BeBoB v3 transfers packets with these qurks:
+               //  - In the beginning of streaming, the value of dbc is
+               //    incremented even if no data blocks are transferred.
+               //  - The value of dbc is reset suddenly.
+               if (bebob->version > 2)
+                       bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC |
+                                                 CIP_SKIP_DBC_ZERO_CHECK;
+
+               // At high sampling rate, M-Audio special firmware transmits
+               // empty packet with the value of dbc incremented by 8 but the
+               // others are valid to IEC 61883-1.
+               if (bebob->maudio_special_quirk)
+                       bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC;
+       }
 
-       /*
-        * At high sampling rate, M-Audio special firmware transmits empty
-        * packet with the value of dbc incremented by 8 but the others are
-        * valid to IEC 61883-1.
-        */
-       if (bebob->maudio_special_quirk)
-               bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC;
+       return 0;
+}
+
+static void destroy_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
+{
+       amdtp_stream_destroy(stream);
+
+       if (stream == &bebob->tx_stream)
+               cmp_connection_destroy(&bebob->out_conn);
+       else
+               cmp_connection_destroy(&bebob->in_conn);
+}
+
+int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
+{
+       int err;
 
-       err = amdtp_am824_init(&bebob->rx_stream, bebob->unit,
-                              AMDTP_OUT_STREAM, CIP_BLOCKING);
+       err = init_stream(bebob, &bebob->tx_stream);
+       if (err < 0)
+               return err;
+
+       err = init_stream(bebob, &bebob->rx_stream);
        if (err < 0) {
-               amdtp_stream_destroy(&bebob->tx_stream);
-               amdtp_stream_destroy(&bebob->rx_stream);
-               destroy_both_connections(bebob);
+               destroy_stream(bebob, &bebob->tx_stream);
+               return err;
        }
-end:
-       return err;
+
+       return 0;
 }
 
-int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
+static int keep_resources(struct snd_bebob *bebob, struct amdtp_stream *stream,
+                         unsigned int rate, unsigned int index)
 {
-       const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
-       unsigned int curr_rate;
-       int err = 0;
+       struct snd_bebob_stream_formation *formation;
+       struct cmp_connection *conn;
+       int err;
 
-       /* Need no substreams */
-       if (bebob->substreams_counter == 0)
-               goto end;
+       if (stream == &bebob->tx_stream) {
+               formation = bebob->tx_stream_formations + index;
+               conn = &bebob->out_conn;
+       } else {
+               formation = bebob->rx_stream_formations + index;
+               conn = &bebob->in_conn;
+       }
 
-       /*
-        * Considering JACK/FFADO streaming:
-        * TODO: This can be removed hwdep functionality becomes popular.
-        */
+       err = amdtp_am824_set_parameters(stream, rate, formation->pcm,
+                                        formation->midi, false);
+       if (err < 0)
+               return err;
+
+       return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
+}
+
+int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate)
+{
+       unsigned int curr_rate;
+       int err;
+
+       // Considering JACK/FFADO streaming:
+       // TODO: This can be removed hwdep functionality becomes popular.
        err = check_connection_used_by_others(bebob, &bebob->rx_stream);
        if (err < 0)
-               goto end;
+               return err;
 
-       /*
-        * packet queueing error or detecting discontinuity
-        *
-        * At bus reset, connections should not be broken here. So streams need
-        * to be re-started. This is a reason to use SKIP_INIT_DBC_CHECK flag.
-        */
-       if (amdtp_streaming_error(&bebob->rx_stream))
-               amdtp_stream_stop(&bebob->rx_stream);
-       if (amdtp_streaming_error(&bebob->tx_stream))
+       err = bebob->spec->rate->get(bebob, &curr_rate);
+       if (err < 0)
+               return err;
+       if (rate == 0)
+               rate = curr_rate;
+       if (curr_rate != rate) {
                amdtp_stream_stop(&bebob->tx_stream);
-       if (!amdtp_stream_running(&bebob->rx_stream) &&
-           !amdtp_stream_running(&bebob->tx_stream))
+               amdtp_stream_stop(&bebob->rx_stream);
+
                break_both_connections(bebob);
 
-       /* stop streams if rate is different */
-       err = rate_spec->get(bebob, &curr_rate);
-       if (err < 0) {
-               dev_err(&bebob->unit->device,
-                       "fail to get sampling rate: %d\n", err);
-               goto end;
+               cmp_connection_release(&bebob->out_conn);
+               cmp_connection_release(&bebob->in_conn);
        }
-       if (rate == 0)
-               rate = curr_rate;
-       if (rate != curr_rate) {
+
+       if (bebob->substreams_counter == 0 || curr_rate != rate) {
+               unsigned int index;
+
+               // NOTE:
+               // If establishing connections at first, Yamaha GO46
+               // (and maybe Terratec X24) don't generate sound.
+               //
+               // For firmware customized by M-Audio, refer to next NOTE.
+               err = bebob->spec->rate->set(bebob, rate);
+               if (err < 0) {
+                       dev_err(&bebob->unit->device,
+                               "fail to set sampling rate: %d\n",
+                               err);
+                       return err;
+               }
+
+               err = get_formation_index(rate, &index);
+               if (err < 0)
+                       return err;
+
+               err = keep_resources(bebob, &bebob->tx_stream, rate, index);
+               if (err < 0)
+                       return err;
+
+               err = keep_resources(bebob, &bebob->rx_stream, rate, index);
+               if (err < 0) {
+                       cmp_connection_release(&bebob->out_conn);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
+{
+       int err;
+
+       // Need no substreams.
+       if (bebob->substreams_counter == 0)
+               return -EIO;
+
+       // packet queueing error or detecting discontinuity
+       if (amdtp_streaming_error(&bebob->rx_stream) ||
+           amdtp_streaming_error(&bebob->tx_stream)) {
                amdtp_stream_stop(&bebob->rx_stream);
                amdtp_stream_stop(&bebob->tx_stream);
+
                break_both_connections(bebob);
        }
 
-       /* master should be always running */
        if (!amdtp_stream_running(&bebob->rx_stream)) {
-               /*
-                * NOTE:
-                * If establishing connections at first, Yamaha GO46
-                * (and maybe Terratec X24) don't generate sound.
-                *
-                * For firmware customized by M-Audio, refer to next NOTE.
-                */
-               if (bebob->maudio_special_quirk == NULL) {
-                       err = rate_spec->set(bebob, rate);
-                       if (err < 0) {
-                               dev_err(&bebob->unit->device,
-                                       "fail to set sampling rate: %d\n",
-                                       err);
-                               goto end;
-                       }
+               unsigned int curr_rate;
+
+               if (bebob->maudio_special_quirk) {
+                       err = bebob->spec->rate->get(bebob, &curr_rate);
+                       if (err < 0)
+                               return err;
                }
 
-               err = make_both_connections(bebob, rate);
+               err = make_both_connections(bebob);
                if (err < 0)
-                       goto end;
+                       return err;
 
-               err = start_stream(bebob, &bebob->rx_stream, rate);
+               err = start_stream(bebob, &bebob->rx_stream);
                if (err < 0) {
                        dev_err(&bebob->unit->device,
                                "fail to run AMDTP master stream:%d\n", err);
-                       break_both_connections(bebob);
-                       goto end;
+                       goto error;
                }
 
-               /*
-                * NOTE:
-                * The firmware customized by M-Audio uses these commands to
-                * start transmitting stream. This is not usual way.
-                */
-               if (bebob->maudio_special_quirk != NULL) {
-                       err = rate_spec->set(bebob, rate);
+               // NOTE:
+               // The firmware customized by M-Audio uses these commands to
+               // start transmitting stream. This is not usual way.
+               if (bebob->maudio_special_quirk) {
+                       err = bebob->spec->rate->set(bebob, curr_rate);
                        if (err < 0) {
                                dev_err(&bebob->unit->device,
                                        "fail to ensure sampling rate: %d\n",
                                        err);
-                               amdtp_stream_stop(&bebob->rx_stream);
-                               break_both_connections(bebob);
-                               goto end;
+                               goto error;
                        }
                }
 
-               /* wait first callback */
                if (!amdtp_stream_wait_callback(&bebob->rx_stream,
                                                CALLBACK_TIMEOUT)) {
-                       amdtp_stream_stop(&bebob->rx_stream);
-                       break_both_connections(bebob);
                        err = -ETIMEDOUT;
-                       goto end;
+                       goto error;
                }
        }
 
-       /* start slave if needed */
        if (!amdtp_stream_running(&bebob->tx_stream)) {
-               err = start_stream(bebob, &bebob->tx_stream, rate);
+               err = start_stream(bebob, &bebob->tx_stream);
                if (err < 0) {
                        dev_err(&bebob->unit->device,
                                "fail to run AMDTP slave stream:%d\n", err);
-                       amdtp_stream_stop(&bebob->rx_stream);
-                       break_both_connections(bebob);
-                       goto end;
+                       goto error;
                }
 
-               /* wait first callback */
                if (!amdtp_stream_wait_callback(&bebob->tx_stream,
                                                CALLBACK_TIMEOUT)) {
-                       amdtp_stream_stop(&bebob->tx_stream);
-                       amdtp_stream_stop(&bebob->rx_stream);
-                       break_both_connections(bebob);
                        err = -ETIMEDOUT;
+                       goto error;
                }
        }
-end:
+
+       return 0;
+error:
+       amdtp_stream_stop(&bebob->tx_stream);
+       amdtp_stream_stop(&bebob->rx_stream);
+       break_both_connections(bebob);
        return err;
 }
 
 void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
 {
        if (bebob->substreams_counter == 0) {
-               amdtp_stream_pcm_abort(&bebob->rx_stream);
                amdtp_stream_stop(&bebob->rx_stream);
-
-               amdtp_stream_pcm_abort(&bebob->tx_stream);
                amdtp_stream_stop(&bebob->tx_stream);
 
                break_both_connections(bebob);
+
+               cmp_connection_release(&bebob->out_conn);
+               cmp_connection_release(&bebob->in_conn);
        }
 }
 
@@ -702,10 +708,8 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
  */
 void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob)
 {
-       amdtp_stream_destroy(&bebob->rx_stream);
-       amdtp_stream_destroy(&bebob->tx_stream);
-
-       destroy_both_connections(bebob);
+       destroy_stream(bebob, &bebob->tx_stream);
+       destroy_stream(bebob, &bebob->rx_stream);
 }
 
 /*
index 13f8abc19cfb10e51f94b48c428230c56395a127..14abbe7175b69b0400c33c327ae8c04c106ca110 100644 (file)
@@ -185,6 +185,37 @@ void cmp_connection_destroy(struct cmp_connection *c)
 }
 EXPORT_SYMBOL(cmp_connection_destroy);
 
+int cmp_connection_reserve(struct cmp_connection *c,
+                          unsigned int max_payload_bytes)
+{
+       int err;
+
+       mutex_lock(&c->mutex);
+
+       if (WARN_ON(c->resources.allocated)) {
+               err = -EBUSY;
+               goto end;
+       }
+
+       c->speed = min(c->max_speed,
+                      fw_parent_device(c->resources.unit)->max_speed);
+
+       err = fw_iso_resources_allocate(&c->resources, max_payload_bytes,
+                                       c->speed);
+end:
+       mutex_unlock(&c->mutex);
+
+       return err;
+}
+EXPORT_SYMBOL(cmp_connection_reserve);
+
+void cmp_connection_release(struct cmp_connection *c)
+{
+       mutex_lock(&c->mutex);
+       fw_iso_resources_free(&c->resources);
+       mutex_unlock(&c->mutex);
+}
+EXPORT_SYMBOL(cmp_connection_release);
 
 static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
 {
@@ -270,25 +301,18 @@ static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
  * When this function succeeds, the caller is responsible for starting
  * transmitting packets.
  */
-int cmp_connection_establish(struct cmp_connection *c,
-                            unsigned int max_payload_bytes)
+int cmp_connection_establish(struct cmp_connection *c)
 {
        int err;
 
-       if (WARN_ON(c->connected))
-               return -EISCONN;
-
-       c->speed = min(c->max_speed,
-                      fw_parent_device(c->resources.unit)->max_speed);
-
        mutex_lock(&c->mutex);
 
-retry_after_bus_reset:
-       err = fw_iso_resources_allocate(&c->resources,
-                                       max_payload_bytes, c->speed);
-       if (err < 0)
-               goto err_mutex;
+       if (WARN_ON(c->connected)) {
+               mutex_unlock(&c->mutex);
+               return -EISCONN;
+       }
 
+retry_after_bus_reset:
        if (c->direction == CMP_OUTPUT)
                err = pcr_modify(c, opcr_set_modify, pcr_set_check,
                                 ABORT_ON_BUS_RESET);
@@ -297,21 +321,13 @@ retry_after_bus_reset:
                                 ABORT_ON_BUS_RESET);
 
        if (err == -EAGAIN) {
-               fw_iso_resources_free(&c->resources);
-               goto retry_after_bus_reset;
+               err = fw_iso_resources_update(&c->resources);
+               if (err >= 0)
+                       goto retry_after_bus_reset;
        }
-       if (err < 0)
-               goto err_resources;
-
-       c->connected = true;
-
-       mutex_unlock(&c->mutex);
-
-       return 0;
+       if (err >= 0)
+               c->connected = true;
 
-err_resources:
-       fw_iso_resources_free(&c->resources);
-err_mutex:
        mutex_unlock(&c->mutex);
 
        return err;
@@ -351,14 +367,12 @@ int cmp_connection_update(struct cmp_connection *c)
                                 SUCCEED_ON_BUS_RESET);
 
        if (err < 0)
-               goto err_resources;
+               goto err_unconnect;
 
        mutex_unlock(&c->mutex);
 
        return 0;
 
-err_resources:
-       fw_iso_resources_free(&c->resources);
 err_unconnect:
        c->connected = false;
        mutex_unlock(&c->mutex);
@@ -395,8 +409,6 @@ void cmp_connection_break(struct cmp_connection *c)
        if (err < 0)
                cmp_error(c, "plug is still connected\n");
 
-       fw_iso_resources_free(&c->resources);
-
        c->connected = false;
 
        mutex_unlock(&c->mutex);
index b60b415caa8fdd5d6392de4d98100e5b7bb151e4..26ab88000e34b5413dd5293b92dbe1abcda327a5 100644 (file)
@@ -42,8 +42,11 @@ int cmp_connection_init(struct cmp_connection *connection,
 int cmp_connection_check_used(struct cmp_connection *connection, bool *used);
 void cmp_connection_destroy(struct cmp_connection *connection);
 
-int cmp_connection_establish(struct cmp_connection *connection,
-                            unsigned int max_payload);
+int cmp_connection_reserve(struct cmp_connection *connection,
+                          unsigned int max_payload);
+void cmp_connection_release(struct cmp_connection *connection);
+
+int cmp_connection_establish(struct cmp_connection *connection);
 int cmp_connection_update(struct cmp_connection *connection);
 void cmp_connection_break(struct cmp_connection *connection);
 
index 115eadd8d42e7d289280c5f84d24299347fb6735..7a62dafd0f788e59f2e88ec25f1c7e9a25e5b833 100644 (file)
@@ -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 ca7ae427e8927e74d798b7b5906384f58feceaba..c9e19bddfc09e812085a5771b3f3b7db876ceaac 100644 (file)
@@ -17,8 +17,13 @@ static int midi_open(struct snd_rawmidi_substream *substream)
 
        mutex_lock(&dice->mutex);
 
-       dice->substreams_counter++;
-       err = snd_dice_stream_start_duplex(dice, 0);
+       err = snd_dice_stream_reserve_duplex(dice, 0);
+       if (err >= 0) {
+               ++dice->substreams_counter;
+               err = snd_dice_stream_start_duplex(dice);
+               if (err < 0)
+                       --dice->substreams_counter;
+       }
 
        mutex_unlock(&dice->mutex);
 
@@ -34,7 +39,7 @@ static int midi_close(struct snd_rawmidi_substream *substream)
 
        mutex_lock(&dice->mutex);
 
-       dice->substreams_counter--;
+       --dice->substreams_counter;
        snd_dice_stream_stop_duplex(dice);
 
        mutex_unlock(&dice->mutex);
index 8a601befc11e93214f425bf288972a61ec2befe5..94a4dccfc381b9ed5b76a810597843b9cc9c4768 100644 (file)
@@ -230,8 +230,8 @@ static int pcm_close(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static int capture_hw_params(struct snd_pcm_substream *substream,
-                            struct snd_pcm_hw_params *hw_params)
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+                        struct snd_pcm_hw_params *hw_params)
 {
        struct snd_dice *dice = substream->private_data;
        int err;
@@ -242,57 +242,26 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
                return err;
 
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
-               mutex_lock(&dice->mutex);
-               dice->substreams_counter++;
-               mutex_unlock(&dice->mutex);
-       }
-
-       return 0;
-}
-static int playback_hw_params(struct snd_pcm_substream *substream,
-                             struct snd_pcm_hw_params *hw_params)
-{
-       struct snd_dice *dice = substream->private_data;
-       int err;
-
-       err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
-                                              params_buffer_bytes(hw_params));
-       if (err < 0)
-               return err;
+               unsigned int rate = params_rate(hw_params);
 
-       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
                mutex_lock(&dice->mutex);
-               dice->substreams_counter++;
+               err = snd_dice_stream_reserve_duplex(dice, rate);
+               if (err >= 0)
+                       ++dice->substreams_counter;
                mutex_unlock(&dice->mutex);
        }
 
-       return 0;
-}
-
-static int capture_hw_free(struct snd_pcm_substream *substream)
-{
-       struct snd_dice *dice = substream->private_data;
-
-       mutex_lock(&dice->mutex);
-
-       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-               dice->substreams_counter--;
-
-       snd_dice_stream_stop_duplex(dice);
-
-       mutex_unlock(&dice->mutex);
-
-       return snd_pcm_lib_free_vmalloc_buffer(substream);
+       return err;
 }
 
-static int playback_hw_free(struct snd_pcm_substream *substream)
+static int pcm_hw_free(struct snd_pcm_substream *substream)
 {
        struct snd_dice *dice = substream->private_data;
 
        mutex_lock(&dice->mutex);
 
        if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-               dice->substreams_counter--;
+               --dice->substreams_counter;
 
        snd_dice_stream_stop_duplex(dice);
 
@@ -308,7 +277,7 @@ static int capture_prepare(struct snd_pcm_substream *substream)
        int err;
 
        mutex_lock(&dice->mutex);
-       err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
+       err = snd_dice_stream_start_duplex(dice);
        mutex_unlock(&dice->mutex);
        if (err >= 0)
                amdtp_stream_pcm_prepare(stream);
@@ -322,7 +291,7 @@ static int playback_prepare(struct snd_pcm_substream *substream)
        int err;
 
        mutex_lock(&dice->mutex);
-       err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
+       err = snd_dice_stream_start_duplex(dice);
        mutex_unlock(&dice->mutex);
        if (err >= 0)
                amdtp_stream_pcm_prepare(stream);
@@ -404,8 +373,8 @@ int snd_dice_create_pcm(struct snd_dice *dice)
                .open      = pcm_open,
                .close     = pcm_close,
                .ioctl     = snd_pcm_lib_ioctl,
-               .hw_params = capture_hw_params,
-               .hw_free   = capture_hw_free,
+               .hw_params = pcm_hw_params,
+               .hw_free   = pcm_hw_free,
                .prepare   = capture_prepare,
                .trigger   = capture_trigger,
                .pointer   = capture_pointer,
@@ -416,8 +385,8 @@ int snd_dice_create_pcm(struct snd_dice *dice)
                .open      = pcm_open,
                .close     = pcm_close,
                .ioctl     = snd_pcm_lib_ioctl,
-               .hw_params = playback_hw_params,
-               .hw_free   = playback_hw_free,
+               .hw_params = pcm_hw_params,
+               .hw_free   = pcm_hw_free,
                .prepare   = playback_prepare,
                .trigger   = playback_trigger,
                .pointer   = playback_pointer,
diff --git a/sound/firewire/dice/dice-presonus.c b/sound/firewire/dice/dice-presonus.c
new file mode 100644 (file)
index 0000000..503f462
--- /dev/null
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0
+// dice-presonus.c - a part of driver for DICE based devices
+//
+// Copyright (c) 2019 Takashi Sakamoto
+//
+// Licensed under the terms of the GNU General Public License, version 2.
+
+#include "dice.h"
+
+struct dice_presonus_spec {
+       unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+       unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
+       bool has_midi;
+};
+
+static const struct dice_presonus_spec dice_presonus_firesutio = {
+       .tx_pcm_chs = {{16, 16, 0}, {10, 2, 0} },
+       .rx_pcm_chs = {{16, 16, 0}, {10, 2, 0} },
+       .has_midi = true,
+};
+
+int snd_dice_detect_presonus_formats(struct snd_dice *dice)
+{
+       static const struct {
+               u32 model_id;
+               const struct dice_presonus_spec *spec;
+       } *entry, entries[] = {
+               {0x000008, &dice_presonus_firesutio},
+       };
+       struct fw_csr_iterator it;
+       int key, val, model_id;
+       int i;
+
+       model_id = 0;
+       fw_csr_iterator_init(&it, dice->unit->directory);
+       while (fw_csr_iterator_next(&it, &key, &val)) {
+               if (key == CSR_MODEL) {
+                       model_id = val;
+                       break;
+               }
+       }
+
+       for (i = 0; i < ARRAY_SIZE(entries); ++i) {
+               entry = entries + i;
+               if (entry->model_id == model_id)
+                       break;
+       }
+       if (i == ARRAY_SIZE(entries))
+               return -ENODEV;
+
+       memcpy(dice->tx_pcm_chs, entry->spec->tx_pcm_chs,
+              MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
+       memcpy(dice->rx_pcm_chs, entry->spec->rx_pcm_chs,
+              MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int));
+
+       if (entry->spec->has_midi) {
+               dice->tx_midi_ports[0] = 1;
+               dice->rx_midi_ports[0] = 1;
+       }
+
+       return 0;
+}
index 7a93ae3dc58b51ee44cb7a30046cb379d33b78a2..a9f0c77734c35a768aa02ad0dbc2b23c5b0c564e 100644 (file)
@@ -137,18 +137,9 @@ static int get_register_params(struct snd_dice *dice,
 
 static void release_resources(struct snd_dice *dice)
 {
-       unsigned int i;
-
-       for (i = 0; i < MAX_STREAMS; i++) {
-               if (amdtp_stream_running(&dice->tx_stream[i])) {
-                       amdtp_stream_pcm_abort(&dice->tx_stream[i]);
-                       amdtp_stream_stop(&dice->tx_stream[i]);
-               }
-               if (amdtp_stream_running(&dice->rx_stream[i])) {
-                       amdtp_stream_pcm_abort(&dice->rx_stream[i]);
-                       amdtp_stream_stop(&dice->rx_stream[i]);
-               }
+       int i;
 
+       for (i = 0; i < MAX_STREAMS; ++i) {
                fw_iso_resources_free(&dice->tx_resources[i]);
                fw_iso_resources_free(&dice->rx_resources[i]);
        }
@@ -163,10 +154,14 @@ static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
        for (i = 0; i < params->count; i++) {
                reg = cpu_to_be32((u32)-1);
                if (dir == AMDTP_IN_STREAM) {
+                       amdtp_stream_stop(&dice->tx_stream[i]);
+
                        snd_dice_transaction_write_tx(dice,
                                        params->size * i + TX_ISOCHRONOUS,
                                        &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));
@@ -174,35 +169,22 @@ static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
        }
 }
 
-static int keep_resources(struct snd_dice *dice,
-                         enum amdtp_stream_direction dir, unsigned int index,
-                         unsigned int rate, unsigned int pcm_chs,
-                         unsigned int midi_ports)
+static int keep_resources(struct snd_dice *dice, struct amdtp_stream *stream,
+                         struct fw_iso_resources *resources, unsigned int rate,
+                         unsigned int pcm_chs, unsigned int midi_ports)
 {
-       struct amdtp_stream *stream;
-       struct fw_iso_resources *resources;
        bool double_pcm_frames;
        unsigned int i;
        int err;
 
-       if (dir == AMDTP_IN_STREAM) {
-               stream = &dice->tx_stream[index];
-               resources = &dice->tx_resources[index];
-       } else {
-               stream = &dice->rx_stream[index];
-               resources = &dice->rx_resources[index];
-       }
-
-       /*
-        * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
-        * one data block of AMDTP packet. Thus sampling transfer frequency is
-        * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are
-        * transferred on AMDTP packets at 96 kHz. Two successive samples of a
-        * channel are stored consecutively in the packet. This quirk is called
-        * as 'Dual Wire'.
-        * For this quirk, blocking mode is required and PCM buffer size should
-        * be aligned to SYT_INTERVAL.
-        */
+       // At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
+       // one data block of AMDTP packet. Thus sampling transfer frequency is
+       // a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are
+       // transferred on AMDTP packets at 96 kHz. Two successive samples of a
+       // channel are stored consecutively in the packet. This quirk is called
+       // as 'Dual Wire'.
+       // For this quirk, blocking mode is required and PCM buffer size should
+       // be aligned to SYT_INTERVAL.
        double_pcm_frames = rate > 96000;
        if (double_pcm_frames) {
                rate /= 2;
@@ -229,40 +211,40 @@ static int keep_resources(struct snd_dice *dice,
                                fw_parent_device(dice->unit)->max_speed);
 }
 
-static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
-                        unsigned int rate, struct reg_params *params)
+static int keep_dual_resources(struct snd_dice *dice, unsigned int rate,
+                              enum amdtp_stream_direction dir,
+                              struct reg_params *params)
 {
-       __be32 reg[2];
        enum snd_dice_rate_mode mode;
-       unsigned int i, pcm_chs, midi_ports;
-       struct amdtp_stream *streams;
-       struct fw_iso_resources *resources;
-       struct fw_device *fw_dev = fw_parent_device(dice->unit);
-       int err = 0;
-
-       if (dir == AMDTP_IN_STREAM) {
-               streams = dice->tx_stream;
-               resources = dice->tx_resources;
-       } else {
-               streams = dice->rx_stream;
-               resources = dice->rx_resources;
-       }
+       int i;
+       int err;
 
        err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
        if (err < 0)
                return err;
 
-       for (i = 0; i < params->count; i++) {
+       for (i = 0; i < params->count; ++i) {
+               __be32 reg[2];
+               struct amdtp_stream *stream;
+               struct fw_iso_resources *resources;
                unsigned int pcm_cache;
                unsigned int midi_cache;
+               unsigned int pcm_chs;
+               unsigned int midi_ports;
 
                if (dir == AMDTP_IN_STREAM) {
+                       stream = &dice->tx_stream[i];
+                       resources = &dice->tx_resources[i];
+
                        pcm_cache = dice->tx_pcm_chs[i][mode];
                        midi_cache = dice->tx_midi_ports[i];
                        err = snd_dice_transaction_read_tx(dice,
                                        params->size * i + TX_NUMBER_AUDIO,
                                        reg, sizeof(reg));
                } else {
+                       stream = &dice->rx_stream[i];
+                       resources = &dice->rx_resources[i];
+
                        pcm_cache = dice->rx_pcm_chs[i][mode];
                        midi_cache = dice->rx_midi_ports[i];
                        err = snd_dice_transaction_read_rx(dice,
@@ -274,7 +256,7 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
                pcm_chs = be32_to_cpu(reg[0]);
                midi_ports = be32_to_cpu(reg[1]);
 
-               /* These are important for developer of this driver. */
+               // These are important for developer of this driver.
                if (pcm_chs != pcm_cache || midi_ports != midi_cache) {
                        dev_info(&dice->unit->device,
                                 "cache mismatch: pcm: %u:%u, midi: %u:%u\n",
@@ -282,141 +264,170 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
                        return -EPROTO;
                }
 
-               err = keep_resources(dice, dir, i, rate, pcm_chs, midi_ports);
-               if (err < 0)
-                       return err;
-
-               reg[0] = cpu_to_be32(resources[i].channel);
-               if (dir == AMDTP_IN_STREAM) {
-                       err = snd_dice_transaction_write_tx(dice,
-                                       params->size * i + TX_ISOCHRONOUS,
-                                       reg, sizeof(reg[0]));
-               } else {
-                       err = snd_dice_transaction_write_rx(dice,
-                                       params->size * i + RX_ISOCHRONOUS,
-                                       reg, sizeof(reg[0]));
-               }
+               err = keep_resources(dice, stream, resources, rate, pcm_chs,
+                                    midi_ports);
                if (err < 0)
                        return err;
+       }
 
-               if (dir == AMDTP_IN_STREAM) {
-                       reg[0] = cpu_to_be32(fw_dev->max_speed);
-                       err = snd_dice_transaction_write_tx(dice,
-                                       params->size * i + TX_SPEED,
-                                       reg, sizeof(reg[0]));
-                       if (err < 0)
-                               return err;
-               }
+       return 0;
+}
 
-               err = amdtp_stream_start(&streams[i], resources[i].channel,
-                                        fw_dev->max_speed);
-               if (err < 0)
-                       return err;
-       }
+static void finish_session(struct snd_dice *dice, struct reg_params *tx_params,
+                          struct reg_params *rx_params)
+{
+       stop_streams(dice, AMDTP_IN_STREAM, tx_params);
+       stop_streams(dice, AMDTP_OUT_STREAM, rx_params);
 
-       return err;
+       snd_dice_transaction_clear_enable(dice);
 }
 
-static int start_duplex_streams(struct snd_dice *dice, unsigned int rate)
+int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate)
 {
-       struct reg_params tx_params, rx_params;
-       int i;
+       unsigned int curr_rate;
        int err;
 
-       err = get_register_params(dice, &tx_params, &rx_params);
+       // Check sampling transmission frequency.
+       err = snd_dice_transaction_get_rate(dice, &curr_rate);
        if (err < 0)
                return err;
+       if (rate == 0)
+               rate = curr_rate;
 
-       /* Stop transmission. */
-       stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
-       stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
-       snd_dice_transaction_clear_enable(dice);
-       release_resources(dice);
+       if (dice->substreams_counter == 0 || curr_rate != rate) {
+               struct reg_params tx_params, rx_params;
 
-       err = ensure_phase_lock(dice, rate);
-       if (err < 0) {
-               dev_err(&dice->unit->device, "fail to ensure phase lock\n");
-               return err;
-       }
+               err = get_register_params(dice, &tx_params, &rx_params);
+               if (err < 0)
+                       return err;
 
-       /* Likely to have changed stream formats. */
-       err = get_register_params(dice, &tx_params, &rx_params);
-       if (err < 0)
-               return err;
+               finish_session(dice, &tx_params, &rx_params);
 
-       /* Start both streams. */
-       err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params);
-       if (err < 0)
-               goto error;
-       err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params);
-       if (err < 0)
-               goto error;
+               release_resources(dice);
 
-       err = snd_dice_transaction_set_enable(dice);
-       if (err < 0) {
-               dev_err(&dice->unit->device, "fail to enable interface\n");
-               goto error;
-       }
+               // Just after owning the unit (GLOBAL_OWNER), the unit can
+               // return invalid stream formats. Selecting clock parameters
+               // have an effect for the unit to refine it.
+               err = ensure_phase_lock(dice, rate);
+               if (err < 0)
+                       return err;
 
-       for (i = 0; i < MAX_STREAMS; i++) {
-               if ((i < tx_params.count &&
-                   !amdtp_stream_wait_callback(&dice->tx_stream[i],
-                                               CALLBACK_TIMEOUT)) ||
-                   (i < rx_params.count &&
-                    !amdtp_stream_wait_callback(&dice->rx_stream[i],
-                                                CALLBACK_TIMEOUT))) {
-                       err = -ETIMEDOUT;
+               // After changing sampling transfer frequency, the value of
+               // register can be changed.
+               err = get_register_params(dice, &tx_params, &rx_params);
+               if (err < 0)
+                       return err;
+
+               err = keep_dual_resources(dice, rate, AMDTP_IN_STREAM,
+                                         &tx_params);
+               if (err < 0)
+                       goto error;
+
+               err = keep_dual_resources(dice, rate, AMDTP_OUT_STREAM,
+                                         &rx_params);
+               if (err < 0)
                        goto error;
-               }
        }
 
        return 0;
 error:
-       stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
-       stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
-       snd_dice_transaction_clear_enable(dice);
        release_resources(dice);
        return err;
 }
 
+static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
+                        unsigned int rate, struct reg_params *params)
+{
+       unsigned int max_speed = fw_parent_device(dice->unit)->max_speed;
+       int i;
+       int err;
+
+       for (i = 0; i < params->count; i++) {
+               struct amdtp_stream *stream;
+               struct fw_iso_resources *resources;
+               __be32 reg;
+
+               if (dir == AMDTP_IN_STREAM) {
+                       stream = dice->tx_stream + i;
+                       resources = dice->tx_resources + i;
+               } else {
+                       stream = dice->rx_stream + i;
+                       resources = dice->rx_resources + i;
+               }
+
+               reg = cpu_to_be32(resources->channel);
+               if (dir == AMDTP_IN_STREAM) {
+                       err = snd_dice_transaction_write_tx(dice,
+                                       params->size * i + TX_ISOCHRONOUS,
+                                       &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;
@@ -428,12 +439,40 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
                    !amdtp_stream_running(&dice->rx_stream[i]))
                        break;
        }
-       if (i < MAX_STREAMS)
-               goto restart;
+       if (i < MAX_STREAMS) {
+               // Start both streams.
+               err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params);
+               if (err < 0)
+                       goto error;
+
+               err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params);
+               if (err < 0)
+                       goto error;
+
+               err = snd_dice_transaction_set_enable(dice);
+               if (err < 0) {
+                       dev_err(&dice->unit->device,
+                               "fail to enable interface\n");
+                       goto error;
+               }
+
+               for (i = 0; i < MAX_STREAMS; i++) {
+                       if ((i < tx_params.count &&
+                           !amdtp_stream_wait_callback(&dice->tx_stream[i],
+                                                       CALLBACK_TIMEOUT)) ||
+                           (i < rx_params.count &&
+                            !amdtp_stream_wait_callback(&dice->rx_stream[i],
+                                                        CALLBACK_TIMEOUT))) {
+                               err = -ETIMEDOUT;
+                               goto error;
+                       }
+               }
+       }
 
        return 0;
-restart:
-       return start_duplex_streams(dice, rate);
+error:
+       finish_session(dice, &tx_params, &rx_params);
+       return err;
 }
 
 /*
@@ -445,17 +484,12 @@ void snd_dice_stream_stop_duplex(struct snd_dice *dice)
 {
        struct reg_params tx_params, rx_params;
 
-       if (dice->substreams_counter > 0)
-               return;
-
-       snd_dice_transaction_clear_enable(dice);
+       if (dice->substreams_counter == 0) {
+               if (get_register_params(dice, &tx_params, &rx_params) >= 0)
+                       finish_session(dice, &tx_params, &rx_params);
 
-       if (get_register_params(dice, &tx_params, &rx_params) == 0) {
-               stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
-               stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
+               release_resources(dice);
        }
-
-       release_resources(dice);
 }
 
 static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir,
index ea829cee9aafb300736480c0b232514df47d43e7..13eeb3f52bb613785088759fd7c613ba3546efaf 100644 (file)
@@ -19,6 +19,7 @@ MODULE_LICENSE("GPL v2");
 #define OUI_MAUDIO             0x000d6c
 #define OUI_MYTEK              0x001ee8
 #define OUI_SSL                        0x0050c2        // Actually ID reserved by IEEE.
+#define OUI_PRESONUS           0x000a92
 
 #define DICE_CATEGORY_ID       0x04
 #define WEISS_CATEGORY_ID      0x00
@@ -371,6 +372,14 @@ static const struct ieee1394_device_id dice_id_table[] = {
                .vendor_id      = OUI_SSL,
                .model_id       = 0x000070,
        },
+       // Presonus FireStudio.
+       {
+               .match_flags    = IEEE1394_MATCH_VENDOR_ID |
+                                 IEEE1394_MATCH_MODEL_ID,
+               .vendor_id      = OUI_PRESONUS,
+               .model_id       = 0x000008,
+               .driver_data    = (kernel_ulong_t)snd_dice_detect_presonus_formats,
+       },
        {
                .match_flags = IEEE1394_MATCH_VERSION,
                .version     = DICE_INTERFACE,
index eb4fb8bae2ad2807dc73a6780644a1340c9ad8f0..c6304e5e9fc4a3da8aaad1af996f1f7058f924c9 100644 (file)
@@ -204,10 +204,11 @@ extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT];
 
 int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
                                  enum snd_dice_rate_mode *mode);
-int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate);
+int snd_dice_stream_start_duplex(struct snd_dice *dice);
 void snd_dice_stream_stop_duplex(struct snd_dice *dice);
 int snd_dice_stream_init_duplex(struct snd_dice *dice);
 void snd_dice_stream_destroy_duplex(struct snd_dice *dice);
+int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate);
 void snd_dice_stream_update_duplex(struct snd_dice *dice);
 int snd_dice_stream_detect_current_formats(struct snd_dice *dice);
 
@@ -226,5 +227,6 @@ int snd_dice_detect_tcelectronic_formats(struct snd_dice *dice);
 int snd_dice_detect_alesis_formats(struct snd_dice *dice);
 int snd_dice_detect_extension_formats(struct snd_dice *dice);
 int snd_dice_detect_mytek_formats(struct snd_dice *dice);
+int snd_dice_detect_presonus_formats(struct snd_dice *dice);
 
 #endif
index 10c8803d7f19d4f582ca97b0ac3ab7d8254bbdfd..45ff73d16074429d10171091204f63553b930608 100644 (file)
@@ -127,7 +127,7 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
        if (err < 0)
                return err;
 
-       s->fdf = AMDTP_FDF_AM824 | s->sfc;
+       s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc;
 
        p->pcm_channels = pcm_channels;
 
index bf50a168087f4b5e08622ca001ad1f96d88ac4f2..2b57ece891018814e69868396df6f48db0ee1253 100644 (file)
@@ -17,8 +17,13 @@ static int midi_open(struct snd_rawmidi_substream *substream)
                return err;
 
        mutex_lock(&dg00x->mutex);
-       dg00x->substreams_counter++;
-       err = snd_dg00x_stream_start_duplex(dg00x, 0);
+       err = snd_dg00x_stream_reserve_duplex(dg00x, 0);
+       if (err >= 0) {
+               ++dg00x->substreams_counter;
+               err = snd_dg00x_stream_start_duplex(dg00x);
+               if (err < 0)
+                       --dg00x->substreams_counter;
+       }
        mutex_unlock(&dg00x->mutex);
        if (err < 0)
                snd_dg00x_stream_lock_release(dg00x);
@@ -31,7 +36,7 @@ static int midi_close(struct snd_rawmidi_substream *substream)
        struct snd_dg00x *dg00x = substream->rmidi->private_data;
 
        mutex_lock(&dg00x->mutex);
-       dg00x->substreams_counter--;
+       --dg00x->substreams_counter;
        snd_dg00x_stream_stop_duplex(dg00x);
        mutex_unlock(&dg00x->mutex);
 
index 4f637f227513903d08ad72aecbb635a77fb3c011..18e561b26625d63fed15b1a0a2eee555c486fe11 100644 (file)
@@ -154,8 +154,8 @@ static int pcm_close(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
-                                struct snd_pcm_hw_params *hw_params)
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+                        struct snd_pcm_hw_params *hw_params)
 {
        struct snd_dg00x *dg00x = substream->private_data;
        int err;
@@ -166,58 +166,26 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
                return err;
 
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
-               mutex_lock(&dg00x->mutex);
-               dg00x->substreams_counter++;
-               mutex_unlock(&dg00x->mutex);
-       }
+               unsigned int rate = params_rate(hw_params);
 
-       return 0;
-}
-
-static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
-                                 struct snd_pcm_hw_params *hw_params)
-{
-       struct snd_dg00x *dg00x = substream->private_data;
-       int err;
-
-       err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
-                                              params_buffer_bytes(hw_params));
-       if (err < 0)
-               return err;
-
-       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
                mutex_lock(&dg00x->mutex);
-               dg00x->substreams_counter++;
+               err = snd_dg00x_stream_reserve_duplex(dg00x, rate);
+               if (err >= 0)
+                       ++dg00x->substreams_counter;
                mutex_unlock(&dg00x->mutex);
        }
 
-       return 0;
-}
-
-static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
-{
-       struct snd_dg00x *dg00x = substream->private_data;
-
-       mutex_lock(&dg00x->mutex);
-
-       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-               dg00x->substreams_counter--;
-
-       snd_dg00x_stream_stop_duplex(dg00x);
-
-       mutex_unlock(&dg00x->mutex);
-
-       return snd_pcm_lib_free_vmalloc_buffer(substream);
+       return err;
 }
 
-static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+static int pcm_hw_free(struct snd_pcm_substream *substream)
 {
        struct snd_dg00x *dg00x = substream->private_data;
 
        mutex_lock(&dg00x->mutex);
 
        if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-               dg00x->substreams_counter--;
+               --dg00x->substreams_counter;
 
        snd_dg00x_stream_stop_duplex(dg00x);
 
@@ -229,12 +197,11 @@ static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
 static int pcm_capture_prepare(struct snd_pcm_substream *substream)
 {
        struct snd_dg00x *dg00x = substream->private_data;
-       struct snd_pcm_runtime *runtime = substream->runtime;
        int err;
 
        mutex_lock(&dg00x->mutex);
 
-       err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
+       err = snd_dg00x_stream_start_duplex(dg00x);
        if (err >= 0)
                amdtp_stream_pcm_prepare(&dg00x->tx_stream);
 
@@ -246,12 +213,11 @@ static int pcm_capture_prepare(struct snd_pcm_substream *substream)
 static int pcm_playback_prepare(struct snd_pcm_substream *substream)
 {
        struct snd_dg00x *dg00x = substream->private_data;
-       struct snd_pcm_runtime *runtime = substream->runtime;
        int err;
 
        mutex_lock(&dg00x->mutex);
 
-       err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
+       err = snd_dg00x_stream_start_duplex(dg00x);
        if (err >= 0) {
                amdtp_stream_pcm_prepare(&dg00x->rx_stream);
                amdtp_dot_reset(&dg00x->rx_stream);
@@ -332,8 +298,8 @@ int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
                .open           = pcm_open,
                .close          = pcm_close,
                .ioctl          = snd_pcm_lib_ioctl,
-               .hw_params      = pcm_capture_hw_params,
-               .hw_free        = pcm_capture_hw_free,
+               .hw_params      = pcm_hw_params,
+               .hw_free        = pcm_hw_free,
                .prepare        = pcm_capture_prepare,
                .trigger        = pcm_capture_trigger,
                .pointer        = pcm_capture_pointer,
@@ -344,8 +310,8 @@ int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
                .open           = pcm_open,
                .close          = pcm_close,
                .ioctl          = snd_pcm_lib_ioctl,
-               .hw_params      = pcm_playback_hw_params,
-               .hw_free        = pcm_playback_hw_free,
+               .hw_params      = pcm_hw_params,
+               .hw_free        = pcm_hw_free,
                .prepare        = pcm_playback_prepare,
                .trigger        = pcm_playback_trigger,
                .pointer        = pcm_playback_pointer,
index ac8052c66b6f8704724efc184c38c3c674d7e295..3e77dbd3ee2281793d9deb9806f11d305e100644 100644 (file)
@@ -124,11 +124,25 @@ int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
 
 static void finish_session(struct snd_dg00x *dg00x)
 {
-       __be32 data = cpu_to_be32(0x00000003);
+       __be32 data;
+
+       amdtp_stream_stop(&dg00x->tx_stream);
+       amdtp_stream_stop(&dg00x->rx_stream);
 
+       data = cpu_to_be32(0x00000003);
        snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
                           DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
                           &data, sizeof(data), 0);
+
+       // Unregister isochronous channels for both direction.
+       data = 0;
+       snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
+                          &data, sizeof(data), 0);
+
+       // Just after finishing the session, the device may lost transmitting
+       // functionality for a short time.
+       msleep(50);
 }
 
 static int begin_session(struct snd_dg00x *dg00x)
@@ -137,11 +151,20 @@ static int begin_session(struct snd_dg00x *dg00x)
        u32 curr;
        int err;
 
+       // Register isochronous channels for both direction.
+       data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
+                          dg00x->rx_resources.channel);
+       err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
+                                &data, sizeof(data), 0);
+       if (err < 0)
+               return err;
+
        err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
                                 DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,
                                 &data, sizeof(data), 0);
        if (err < 0)
-               goto error;
+               return err;
        curr = be32_to_cpu(data);
 
        if (curr == 0)
@@ -156,39 +179,23 @@ static int begin_session(struct snd_dg00x *dg00x)
                                         DG00X_OFFSET_STREAMING_SET,
                                         &data, sizeof(data), 0);
                if (err < 0)
-                       goto error;
+                       break;
 
                msleep(20);
                curr--;
        }
 
-       return 0;
-error:
-       finish_session(dg00x);
        return err;
 }
 
-static void release_resources(struct snd_dg00x *dg00x)
+static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream,
+                         unsigned int rate)
 {
-       __be32 data = 0;
-
-       /* Unregister isochronous channels for both direction. */
-       snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
-                          DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
-                          &data, sizeof(data), 0);
-
-       /* Release isochronous resources. */
-       fw_iso_resources_free(&dg00x->tx_resources);
-       fw_iso_resources_free(&dg00x->rx_resources);
-}
-
-static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
-{
-       unsigned int i;
-       __be32 data;
+       struct fw_iso_resources *resources;
+       int i;
        int err;
 
-       /* Check sampling rate. */
+       // Check sampling rate.
        for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
                if (snd_dg00x_stream_rates[i] == rate)
                        break;
@@ -196,41 +203,19 @@ static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
        if (i == SND_DG00X_RATE_COUNT)
                return -EINVAL;
 
-       /* Keep resources for out-stream. */
-       err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate,
-                                      snd_dg00x_stream_pcm_channels[i]);
-       if (err < 0)
-               return err;
-       err = fw_iso_resources_allocate(&dg00x->rx_resources,
-                               amdtp_stream_get_max_payload(&dg00x->rx_stream),
-                               fw_parent_device(dg00x->unit)->max_speed);
-       if (err < 0)
-               return err;
+       if (stream == &dg00x->tx_stream)
+               resources = &dg00x->tx_resources;
+       else
+               resources = &dg00x->rx_resources;
 
-       /* Keep resources for in-stream. */
-       err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate,
+       err = amdtp_dot_set_parameters(stream, rate,
                                       snd_dg00x_stream_pcm_channels[i]);
        if (err < 0)
                return err;
-       err = fw_iso_resources_allocate(&dg00x->tx_resources,
-                               amdtp_stream_get_max_payload(&dg00x->tx_stream),
-                               fw_parent_device(dg00x->unit)->max_speed);
-       if (err < 0)
-               goto error;
 
-       /* Register isochronous channels for both direction. */
-       data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
-                          dg00x->rx_resources.channel);
-       err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
-                                DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
-                                &data, sizeof(data), 0);
-       if (err < 0)
-               goto error;
-
-       return 0;
-error:
-       release_resources(dg00x);
-       return err;
+       return fw_iso_resources_allocate(resources,
+                               amdtp_stream_get_max_payload(stream),
+                               fw_parent_device(dg00x->unit)->max_speed);
 }
 
 int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
@@ -272,43 +257,68 @@ void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
        fw_iso_resources_destroy(&dg00x->tx_resources);
 }
 
-int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate)
+int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate)
 {
        unsigned int curr_rate;
-       int err = 0;
-
-       if (dg00x->substreams_counter == 0)
-               goto end;
+       int err;
 
-       /* Check current sampling rate. */
        err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
        if (err < 0)
-               goto error;
+               return err;
        if (rate == 0)
                rate = curr_rate;
-       if (curr_rate != rate ||
-           amdtp_streaming_error(&dg00x->tx_stream) ||
-           amdtp_streaming_error(&dg00x->rx_stream)) {
+
+       if (dg00x->substreams_counter == 0 || curr_rate != rate) {
                finish_session(dg00x);
 
-               amdtp_stream_stop(&dg00x->tx_stream);
-               amdtp_stream_stop(&dg00x->rx_stream);
-               release_resources(dg00x);
-       }
+               fw_iso_resources_free(&dg00x->tx_resources);
+               fw_iso_resources_free(&dg00x->rx_resources);
 
-       /*
-        * No packets are transmitted without receiving packets, reagardless of
-        * which source of clock is used.
-        */
-       if (!amdtp_stream_running(&dg00x->rx_stream)) {
                err = snd_dg00x_stream_set_local_rate(dg00x, rate);
+               if (err < 0)
+                       return err;
+
+               err = keep_resources(dg00x, &dg00x->rx_stream, rate);
+               if (err < 0)
+                       return err;
+
+               err = keep_resources(dg00x, &dg00x->tx_stream, rate);
+               if (err < 0) {
+                       fw_iso_resources_free(&dg00x->rx_resources);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
+{
+       unsigned int generation = dg00x->rx_resources.generation;
+       int err = 0;
+
+       if (dg00x->substreams_counter == 0)
+               return 0;
+
+       if (amdtp_streaming_error(&dg00x->tx_stream) ||
+           amdtp_streaming_error(&dg00x->rx_stream))
+               finish_session(dg00x);
+
+       if (generation != fw_parent_device(dg00x->unit)->card->generation) {
+               err = fw_iso_resources_update(&dg00x->tx_resources);
                if (err < 0)
                        goto error;
 
-               err = keep_resources(dg00x, rate);
+               err = fw_iso_resources_update(&dg00x->rx_resources);
                if (err < 0)
                        goto error;
+       }
 
+       /*
+        * No packets are transmitted without receiving packets, reagardless of
+        * which source of clock is used.
+        */
+       if (!amdtp_stream_running(&dg00x->rx_stream)) {
                err = begin_session(dg00x);
                if (err < 0)
                        goto error;
@@ -343,33 +353,22 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate)
                        goto error;
                }
        }
-end:
-       return err;
+
+       return 0;
 error:
        finish_session(dg00x);
 
-       amdtp_stream_stop(&dg00x->tx_stream);
-       amdtp_stream_stop(&dg00x->rx_stream);
-       release_resources(dg00x);
-
        return err;
 }
 
 void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
 {
-       if (dg00x->substreams_counter > 0)
-               return;
-
-       amdtp_stream_stop(&dg00x->tx_stream);
-       amdtp_stream_stop(&dg00x->rx_stream);
-       finish_session(dg00x);
-       release_resources(dg00x);
+       if (dg00x->substreams_counter == 0) {
+               finish_session(dg00x);
 
-       /*
-        * Just after finishing the session, the device may lost transmitting
-        * functionality for a short time.
-        */
-       msleep(50);
+               fw_iso_resources_free(&dg00x->tx_resources);
+               fw_iso_resources_free(&dg00x->rx_resources);
+       }
 }
 
 void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
index 464e6d3d82a83591686bc34e10d854a852c853b3..0994d191ccda49cf9ccd15a4e4f03a5ac7f4ec02 100644 (file)
@@ -139,7 +139,8 @@ int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
 int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x,
                                          bool *detect);
 int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x);
-int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate);
+int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate);
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x);
 void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x);
 void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x);
 void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x);
index 0d40bb68db50fbcea80f7847a16d87d08f584d4e..9eab3ad283ce4a4ffc7365e99343c11321a105e7 100644 (file)
@@ -198,8 +198,8 @@ static int pcm_close(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
-                                struct snd_pcm_hw_params *hw_params)
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+                        struct snd_pcm_hw_params *hw_params)
 {
        struct snd_ff *ff = substream->private_data;
        int err;
@@ -210,58 +210,26 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
                return err;
 
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
-               mutex_lock(&ff->mutex);
-               ff->substreams_counter++;
-               mutex_unlock(&ff->mutex);
-       }
-
-       return 0;
-}
-
-static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
-                                 struct snd_pcm_hw_params *hw_params)
-{
-       struct snd_ff *ff = substream->private_data;
-       int err;
-
-       err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
-                                              params_buffer_bytes(hw_params));
-       if (err < 0)
-               return err;
+               unsigned int rate = params_rate(hw_params);
 
-       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
                mutex_lock(&ff->mutex);
-               ff->substreams_counter++;
+               err = snd_ff_stream_reserve_duplex(ff, rate);
+               if (err >= 0)
+                       ++ff->substreams_counter;
                mutex_unlock(&ff->mutex);
        }
 
        return 0;
 }
 
-static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
-{
-       struct snd_ff *ff = substream->private_data;
-
-       mutex_lock(&ff->mutex);
-
-       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-               ff->substreams_counter--;
-
-       snd_ff_stream_stop_duplex(ff);
-
-       mutex_unlock(&ff->mutex);
-
-       return snd_pcm_lib_free_vmalloc_buffer(substream);
-}
-
-static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+static int pcm_hw_free(struct snd_pcm_substream *substream)
 {
        struct snd_ff *ff = substream->private_data;
 
        mutex_lock(&ff->mutex);
 
        if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-               ff->substreams_counter--;
+               --ff->substreams_counter;
 
        snd_ff_stream_stop_duplex(ff);
 
@@ -374,8 +342,8 @@ int snd_ff_create_pcm_devices(struct snd_ff *ff)
                .open           = pcm_open,
                .close          = pcm_close,
                .ioctl          = snd_pcm_lib_ioctl,
-               .hw_params      = pcm_capture_hw_params,
-               .hw_free        = pcm_capture_hw_free,
+               .hw_params      = pcm_hw_params,
+               .hw_free        = pcm_hw_free,
                .prepare        = pcm_capture_prepare,
                .trigger        = pcm_capture_trigger,
                .pointer        = pcm_capture_pointer,
@@ -386,8 +354,8 @@ int snd_ff_create_pcm_devices(struct snd_ff *ff)
                .open           = pcm_open,
                .close          = pcm_close,
                .ioctl          = snd_pcm_lib_ioctl,
-               .hw_params      = pcm_playback_hw_params,
-               .hw_free        = pcm_playback_hw_free,
+               .hw_params      = pcm_hw_params,
+               .hw_free        = pcm_hw_free,
                .prepare        = pcm_playback_prepare,
                .trigger        = pcm_playback_trigger,
                .pointer        = pcm_playback_pointer,
index 8d1c2c6e907b0fd8e35f668368ba09046ac6dea7..bf44cad7985e080697563bdf56a0df34eba37196 100644 (file)
@@ -293,27 +293,6 @@ static int former_fill_midi_msg(struct snd_ff *ff,
 
 #define FF800_TX_PACKET_ISOC_CH        0x0000801c0008
 
-static int allocate_rx_resources(struct snd_ff *ff)
-{
-       u32 data;
-       __le32 reg;
-       int err;
-
-       // Controllers should allocate isochronous resources for rx stream.
-       err = fw_iso_resources_allocate(&ff->rx_resources,
-                               amdtp_stream_get_max_payload(&ff->rx_stream),
-                               fw_parent_device(ff->unit)->max_speed);
-       if (err < 0)
-               return err;
-
-       // Set isochronous channel and the number of quadlets of rx packets.
-       data = ff->rx_stream.data_block_quadlets << 3;
-       data = (data << 8) | ff->rx_resources.channel;
-       reg = cpu_to_le32(data);
-       return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
-                               FF800_RX_PACKET_FORMAT, &reg, sizeof(reg), 0);
-}
-
 static int allocate_tx_resources(struct snd_ff *ff)
 {
        __le32 reg;
@@ -355,8 +334,9 @@ static int allocate_tx_resources(struct snd_ff *ff)
        return 0;
 }
 
-static int ff800_begin_session(struct snd_ff *ff, unsigned int rate)
+static int ff800_allocate_resources(struct snd_ff *ff, unsigned int rate)
 {
+       u32 data;
        __le32 reg;
        int err;
 
@@ -371,14 +351,38 @@ static int ff800_begin_session(struct snd_ff *ff, unsigned int rate)
        // Let's sleep for a bit.
        msleep(100);
 
-       err = allocate_rx_resources(ff);
+       // Controllers should allocate isochronous resources for rx stream.
+       err = fw_iso_resources_allocate(&ff->rx_resources,
+                               amdtp_stream_get_max_payload(&ff->rx_stream),
+                               fw_parent_device(ff->unit)->max_speed);
        if (err < 0)
                return err;
 
-       err = allocate_tx_resources(ff);
+       // Set isochronous channel and the number of quadlets of rx packets.
+       // This should be done before the allocation of tx resources to avoid
+       // periodical noise.
+       data = ff->rx_stream.data_block_quadlets << 3;
+       data = (data << 8) | ff->rx_resources.channel;
+       reg = cpu_to_le32(data);
+       err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                FF800_RX_PACKET_FORMAT, &reg, sizeof(reg), 0);
        if (err < 0)
                return err;
 
+       return allocate_tx_resources(ff);
+}
+
+static int ff800_begin_session(struct snd_ff *ff, unsigned int rate)
+{
+       unsigned int generation = ff->rx_resources.generation;
+       __le32 reg;
+
+       if (generation != fw_parent_device(ff->unit)->card->generation) {
+               int err = fw_iso_resources_update(&ff->rx_resources);
+               if (err < 0)
+                       return err;
+       }
+
        reg = cpu_to_le32(0x80000000);
        reg |= cpu_to_le32(ff->tx_stream.data_block_quadlets);
        if (fw_parent_device(ff->unit)->max_speed == SCODE_800)
@@ -420,6 +424,7 @@ const struct snd_ff_protocol snd_ff_protocol_ff800 = {
        .fill_midi_msg          = former_fill_midi_msg,
        .get_clock              = former_get_clock,
        .switch_fetching_mode   = former_switch_fetching_mode,
+       .allocate_resources     = ff800_allocate_resources,
        .begin_session          = ff800_begin_session,
        .finish_session         = ff800_finish_session,
        .dump_status            = former_dump_status,
@@ -431,12 +436,11 @@ const struct snd_ff_protocol snd_ff_protocol_ff800 = {
 #define FF400_TX_PACKET_FORMAT 0x00008010050cull
 #define FF400_ISOC_COMM_STOP   0x000080100510ull
 
-/*
- * Fireface 400 manages isochronous channel number in 3 bit field. Therefore,
- * we can allocate between 0 and 7 channel.
- */
-static int keep_resources(struct snd_ff *ff, unsigned int rate)
+// Fireface 400 manages isochronous channel number in 3 bit field. Therefore,
+// we can allocate between 0 and 7 channel.
+static int ff400_allocate_resources(struct snd_ff *ff, unsigned int rate)
 {
+       __le32 reg;
        enum snd_ff_stream_mode mode;
        int i;
        int err;
@@ -449,11 +453,20 @@ static int keep_resources(struct snd_ff *ff, unsigned int rate)
        if (i >= CIP_SFC_COUNT)
                return -EINVAL;
 
+       // Set the number of data blocks transferred in a second.
+       reg = cpu_to_le32(rate);
+       err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                FF400_STF, &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       msleep(100);
+
        err = snd_ff_stream_get_multiplier_mode(i, &mode);
        if (err < 0)
                return err;
 
-       /* Keep resources for in-stream. */
+       // Keep resources for in-stream.
        ff->tx_resources.channels_mask = 0x00000000000000ffuLL;
        err = fw_iso_resources_allocate(&ff->tx_resources,
                        amdtp_stream_get_max_payload(&ff->tx_stream),
@@ -461,7 +474,7 @@ static int keep_resources(struct snd_ff *ff, unsigned int rate)
        if (err < 0)
                return err;
 
-       /* Keep resources for out-stream. */
+       // Keep resources for out-stream.
        ff->rx_resources.channels_mask = 0x00000000000000ffuLL;
        err = fw_iso_resources_allocate(&ff->rx_resources,
                        amdtp_stream_get_max_payload(&ff->rx_stream),
@@ -474,26 +487,22 @@ static int keep_resources(struct snd_ff *ff, unsigned int rate)
 
 static int ff400_begin_session(struct snd_ff *ff, unsigned int rate)
 {
+       unsigned int generation = ff->rx_resources.generation;
        __le32 reg;
        int err;
 
-       err = keep_resources(ff, rate);
-       if (err < 0)
-               return err;
-
-       /* Set the number of data blocks transferred in a second. */
-       reg = cpu_to_le32(rate);
-       err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
-                                FF400_STF, &reg, sizeof(reg), 0);
-       if (err < 0)
-               return err;
+       if (generation != fw_parent_device(ff->unit)->card->generation) {
+               err = fw_iso_resources_update(&ff->tx_resources);
+               if (err < 0)
+                       return err;
 
-       msleep(100);
+               err = fw_iso_resources_update(&ff->rx_resources);
+               if (err < 0)
+                       return err;
+       }
 
-       /*
-        * Set isochronous channel and the number of quadlets of received
-        * packets.
-        */
+       // Set isochronous channel and the number of quadlets of received
+       // packets.
        reg = cpu_to_le32(((ff->rx_stream.data_block_quadlets << 3) << 8) |
                          ff->rx_resources.channel);
        err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
@@ -501,11 +510,9 @@ static int ff400_begin_session(struct snd_ff *ff, unsigned int rate)
        if (err < 0)
                return err;
 
-       /*
-        * Set isochronous channel and the number of quadlets of transmitted
-        * packet.
-        */
-       /* TODO: investigate the purpose of this 0x80. */
+       // Set isochronous channel and the number of quadlets of transmitted
+       // packet.
+       // TODO: investigate the purpose of this 0x80.
        reg = cpu_to_le32((0x80 << 24) |
                          (ff->tx_resources.channel << 5) |
                          (ff->tx_stream.data_block_quadlets));
@@ -514,7 +521,7 @@ static int ff400_begin_session(struct snd_ff *ff, unsigned int rate)
        if (err < 0)
                return err;
 
-       /* Allow to transmit packets. */
+       // Allow to transmit packets.
        reg = cpu_to_le32(0x00000001);
        return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
                                 FF400_ISOC_COMM_START, &reg, sizeof(reg), 0);
@@ -591,6 +598,7 @@ const struct snd_ff_protocol snd_ff_protocol_ff400 = {
        .fill_midi_msg          = former_fill_midi_msg,
        .get_clock              = former_get_clock,
        .switch_fetching_mode   = former_switch_fetching_mode,
+       .allocate_resources     = ff400_allocate_resources,
        .begin_session          = ff400_begin_session,
        .finish_session         = ff400_finish_session,
        .dump_status            = former_dump_status,
index b30d02d359b1d21b407f05e4f5f594934ebc1efa..0e4c3a9ed5e4914c8ac4cf634dc21dab7e0a456a 100644 (file)
@@ -97,25 +97,64 @@ static int latter_switch_fetching_mode(struct snd_ff *ff, bool enable)
                                  LATTER_FETCH_MODE, &reg, sizeof(reg), 0);
 }
 
-static int keep_resources(struct snd_ff *ff, unsigned int rate)
+static int latter_allocate_resources(struct snd_ff *ff, unsigned int rate)
 {
        enum snd_ff_stream_mode mode;
+       unsigned int code;
+       __le32 reg;
+       unsigned int count;
        int i;
        int err;
 
-       // Check whether the given value is supported or not.
-       for (i = 0; i < CIP_SFC_COUNT; i++) {
-               if (amdtp_rate_table[i] == rate)
+       // Set the number of data blocks transferred in a second.
+       if (rate % 32000 == 0)
+               code = 0x00;
+       else if (rate % 44100 == 0)
+               code = 0x02;
+       else if (rate % 48000 == 0)
+               code = 0x04;
+       else
+               return -EINVAL;
+
+       if (rate >= 64000 && rate < 128000)
+               code |= 0x08;
+       else if (rate >= 128000 && rate < 192000)
+               code |= 0x10;
+
+       reg = cpu_to_le32(code);
+       err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                LATTER_STF, &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       // Confirm to shift transmission clock.
+       count = 0;
+       while (count++ < 10) {
+               unsigned int curr_rate;
+               enum snd_ff_clock_src src;
+
+               err = latter_get_clock(ff, &curr_rate, &src);
+               if (err < 0)
+                       return err;
+
+               if (curr_rate == rate)
                        break;
        }
-       if (i >= CIP_SFC_COUNT)
+       if (count == 10)
+               return -ETIMEDOUT;
+
+       for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); ++i) {
+               if (rate == amdtp_rate_table[i])
+                       break;
+       }
+       if (i == ARRAY_SIZE(amdtp_rate_table))
                return -EINVAL;
 
        err = snd_ff_stream_get_multiplier_mode(i, &mode);
        if (err < 0)
                return err;
 
-       /* Keep resources for in-stream. */
+       // Keep resources for in-stream.
        ff->tx_resources.channels_mask = 0x00000000000000ffuLL;
        err = fw_iso_resources_allocate(&ff->tx_resources,
                        amdtp_stream_get_max_payload(&ff->tx_stream),
@@ -123,7 +162,7 @@ static int keep_resources(struct snd_ff *ff, unsigned int rate)
        if (err < 0)
                return err;
 
-       /* Keep resources for out-stream. */
+       // Keep resources for out-stream.
        ff->rx_resources.channels_mask = 0x00000000000000ffuLL;
        err = fw_iso_resources_allocate(&ff->rx_resources,
                        amdtp_stream_get_max_payload(&ff->rx_stream),
@@ -136,60 +175,30 @@ static int keep_resources(struct snd_ff *ff, unsigned int rate)
 
 static int latter_begin_session(struct snd_ff *ff, unsigned int rate)
 {
-       static const struct {
-               unsigned int stf;
-               unsigned int code;
-               unsigned int flag;
-       } *entry, rate_table[] = {
-               { 32000,  0x00, 0x92, },
-               { 44100,  0x02, 0x92, },
-               { 48000,  0x04, 0x92, },
-               { 64000,  0x08, 0x8e, },
-               { 88200,  0x0a, 0x8e, },
-               { 96000,  0x0c, 0x8e, },
-               { 128000, 0x10, 0x8c, },
-               { 176400, 0x12, 0x8c, },
-               { 192000, 0x14, 0x8c, },
-       };
+       unsigned int generation = ff->rx_resources.generation;
+       unsigned int flag;
        u32 data;
        __le32 reg;
-       unsigned int count;
-       int i;
        int err;
 
-       for (i = 0; i < ARRAY_SIZE(rate_table); ++i) {
-               entry = rate_table + i;
-               if (entry->stf == rate)
-                       break;
-       }
-       if (i == ARRAY_SIZE(rate_table))
+       if (rate >= 32000 && rate <= 48000)
+               flag = 0x92;
+       else if (rate >= 64000 && rate <= 96000)
+               flag = 0x8e;
+       else if (rate >= 128000 && rate <= 192000)
+               flag = 0x8c;
+       else
                return -EINVAL;
 
-       reg = cpu_to_le32(entry->code);
-       err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
-                                LATTER_STF, &reg, sizeof(reg), 0);
-       if (err < 0)
-               return err;
-
-       // Confirm to shift transmission clock.
-       count = 0;
-       while (count++ < 10) {
-               unsigned int curr_rate;
-               enum snd_ff_clock_src src;
-
-               err = latter_get_clock(ff, &curr_rate, &src);
+       if (generation != fw_parent_device(ff->unit)->card->generation) {
+               err = fw_iso_resources_update(&ff->tx_resources);
                if (err < 0)
                        return err;
 
-               if (curr_rate == rate)
-                       break;
+               err = fw_iso_resources_update(&ff->rx_resources);
+               if (err < 0)
+                       return err;
        }
-       if (count == 10)
-               return -ETIMEDOUT;
-
-       err = keep_resources(ff, rate);
-       if (err < 0)
-               return err;
 
        data = (ff->tx_resources.channel << 8) | ff->rx_resources.channel;
        reg = cpu_to_le32(data);
@@ -200,7 +209,7 @@ static int latter_begin_session(struct snd_ff *ff, unsigned int rate)
 
        // Always use the maximum number of data channels in data block of
        // packet.
-       reg = cpu_to_le32(entry->flag);
+       reg = cpu_to_le32(flag);
        return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
                                  LATTER_ISOC_START, &reg, sizeof(reg), 0);
 }
@@ -424,6 +433,7 @@ const struct snd_ff_protocol snd_ff_protocol_latter = {
        .fill_midi_msg          = latter_fill_midi_msg,
        .get_clock              = latter_get_clock,
        .switch_fetching_mode   = latter_switch_fetching_mode,
+       .allocate_resources     = latter_allocate_resources,
        .begin_session          = latter_begin_session,
        .finish_session         = latter_finish_session,
        .dump_status            = latter_dump_status,
index 6dfd2efb6646fd53839aa4e6720e579a2207d5d7..4208b8004d1acd9a36ada24e152f53781af9a0a9 100644 (file)
@@ -30,14 +30,11 @@ int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
        return 0;
 }
 
-static void release_resources(struct snd_ff *ff)
-{
-       fw_iso_resources_free(&ff->tx_resources);
-       fw_iso_resources_free(&ff->rx_resources);
-}
-
 static inline void finish_session(struct snd_ff *ff)
 {
+       amdtp_stream_stop(&ff->tx_stream);
+       amdtp_stream_stop(&ff->rx_stream);
+
        ff->spec->protocol->finish_session(ff);
        ff->spec->protocol->switch_fetching_mode(ff, false);
 }
@@ -103,37 +100,25 @@ void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
        destroy_stream(ff, AMDTP_OUT_STREAM);
 }
 
-int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
+int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate)
 {
        unsigned int curr_rate;
        enum snd_ff_clock_src src;
        int err;
 
-       if (ff->substreams_counter == 0)
-               return 0;
-
        err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
        if (err < 0)
                return err;
-       if (curr_rate != rate ||
-           amdtp_streaming_error(&ff->tx_stream) ||
-           amdtp_streaming_error(&ff->rx_stream)) {
-               finish_session(ff);
-
-               amdtp_stream_stop(&ff->tx_stream);
-               amdtp_stream_stop(&ff->rx_stream);
-
-               release_resources(ff);
-       }
 
-       /*
-        * Regardless of current source of clock signal, drivers transfer some
-        * packets. Then, the device transfers packets.
-        */
-       if (!amdtp_stream_running(&ff->rx_stream)) {
+       if (ff->substreams_counter == 0 || curr_rate != rate) {
                enum snd_ff_stream_mode mode;
                int i;
 
+               finish_session(ff);
+
+               fw_iso_resources_free(&ff->tx_resources);
+               fw_iso_resources_free(&ff->rx_resources);
+
                for (i = 0; i < CIP_SFC_COUNT; ++i) {
                        if (amdtp_rate_table[i] == rate)
                                break;
@@ -155,6 +140,30 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
                if (err < 0)
                        return err;
 
+               err = ff->spec->protocol->allocate_resources(ff, rate);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
+{
+       int err;
+
+       if (ff->substreams_counter == 0)
+               return 0;
+
+       if (amdtp_streaming_error(&ff->tx_stream) ||
+           amdtp_streaming_error(&ff->rx_stream))
+               finish_session(ff);
+
+       /*
+        * Regardless of current source of clock signal, drivers transfer some
+        * packets. Then, the device transfers packets.
+        */
+       if (!amdtp_stream_running(&ff->rx_stream)) {
                err = ff->spec->protocol->begin_session(ff, rate);
                if (err < 0)
                        goto error;
@@ -192,37 +201,29 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
 
        return 0;
 error:
-       amdtp_stream_stop(&ff->tx_stream);
-       amdtp_stream_stop(&ff->rx_stream);
-
        finish_session(ff);
-       release_resources(ff);
 
        return err;
 }
 
 void snd_ff_stream_stop_duplex(struct snd_ff *ff)
 {
-       if (ff->substreams_counter > 0)
-               return;
+       if (ff->substreams_counter == 0) {
+               finish_session(ff);
 
-       amdtp_stream_stop(&ff->tx_stream);
-       amdtp_stream_stop(&ff->rx_stream);
-       finish_session(ff);
-       release_resources(ff);
+               fw_iso_resources_free(&ff->tx_resources);
+               fw_iso_resources_free(&ff->rx_resources);
+       }
 }
 
 void snd_ff_stream_update_duplex(struct snd_ff *ff)
 {
-       /* The device discontinue to transfer packets.  */
+       // The device discontinue to transfer packets.
        amdtp_stream_pcm_abort(&ff->tx_stream);
        amdtp_stream_stop(&ff->tx_stream);
 
        amdtp_stream_pcm_abort(&ff->rx_stream);
        amdtp_stream_stop(&ff->rx_stream);
-
-       fw_iso_resources_update(&ff->tx_resources);
-       fw_iso_resources_update(&ff->rx_resources);
 }
 
 void snd_ff_stream_lock_changed(struct snd_ff *ff)
index 7fac241c2486fcf6de9008bfa0d8625de7d0b496..36dd0c75b9f713c5dace547a8de358240d44dfa9 100644 (file)
@@ -112,6 +112,7 @@ struct snd_ff_protocol {
        int (*get_clock)(struct snd_ff *ff, unsigned int *rate,
                         enum snd_ff_clock_src *src);
        int (*switch_fetching_mode)(struct snd_ff *ff, bool enable);
+       int (*allocate_resources)(struct snd_ff *ff, unsigned int rate);
        int (*begin_session)(struct snd_ff *ff, unsigned int rate);
        void (*finish_session)(struct snd_ff *ff);
        void (*dump_status)(struct snd_ff *ff, struct snd_info_buffer *buffer);
@@ -136,6 +137,7 @@ int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
                                      enum snd_ff_stream_mode *mode);
 int snd_ff_stream_init_duplex(struct snd_ff *ff);
 void snd_ff_stream_destroy_duplex(struct snd_ff *ff);
+int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate);
 int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate);
 void snd_ff_stream_stop_duplex(struct snd_ff *ff);
 void snd_ff_stream_update_duplex(struct snd_ff *ff);
index 28df49c3542a0630534dc78e870e63671921e7eb..31efd4b53b4f52d174c471c410f80b41b1e68b3d 100644 (file)
@@ -88,8 +88,7 @@ struct snd_efw {
        struct amdtp_stream rx_stream;
        struct cmp_connection out_conn;
        struct cmp_connection in_conn;
-       unsigned int capture_substreams;
-       unsigned int playback_substreams;
+       unsigned int substreams_counter;
 
        /* hardware metering parameters */
        unsigned int phys_out;
@@ -206,7 +205,8 @@ int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate);
 int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate);
 
 int snd_efw_stream_init_duplex(struct snd_efw *efw);
-int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate);
+int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate);
+int snd_efw_stream_start_duplex(struct snd_efw *efw);
 void snd_efw_stream_stop_duplex(struct snd_efw *efw);
 void snd_efw_stream_update_duplex(struct snd_efw *efw);
 void snd_efw_stream_destroy_duplex(struct snd_efw *efw);
index 14b985c4f30481f285c358f425c48b2f751f2296..a9f4a9630d15e4d423362329ebf8dce4ac6d1817 100644 (file)
@@ -7,7 +7,7 @@
  */
 #include "fireworks.h"
 
-static int midi_capture_open(struct snd_rawmidi_substream *substream)
+static int midi_open(struct snd_rawmidi_substream *substream)
 {
        struct snd_efw *efw = substream->rmidi->private_data;
        int err;
@@ -17,28 +17,13 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
                goto end;
 
        mutex_lock(&efw->mutex);
-       efw->capture_substreams++;
-       err = snd_efw_stream_start_duplex(efw, 0);
-       mutex_unlock(&efw->mutex);
-       if (err < 0)
-               snd_efw_stream_lock_release(efw);
-
-end:
-       return err;
-}
-
-static int midi_playback_open(struct snd_rawmidi_substream *substream)
-{
-       struct snd_efw *efw = substream->rmidi->private_data;
-       int err;
-
-       err = snd_efw_stream_lock_try(efw);
-       if (err < 0)
-               goto end;
-
-       mutex_lock(&efw->mutex);
-       efw->playback_substreams++;
-       err = snd_efw_stream_start_duplex(efw, 0);
+       err = snd_efw_stream_reserve_duplex(efw, 0);
+       if (err >= 0) {
+               ++efw->substreams_counter;
+               err = snd_efw_stream_start_duplex(efw);
+               if (err < 0)
+                       --efw->substreams_counter;
+       }
        mutex_unlock(&efw->mutex);
        if (err < 0)
                snd_efw_stream_lock_release(efw);
@@ -46,25 +31,12 @@ end:
        return err;
 }
 
-static int midi_capture_close(struct snd_rawmidi_substream *substream)
-{
-       struct snd_efw *efw = substream->rmidi->private_data;
-
-       mutex_lock(&efw->mutex);
-       efw->capture_substreams--;
-       snd_efw_stream_stop_duplex(efw);
-       mutex_unlock(&efw->mutex);
-
-       snd_efw_stream_lock_release(efw);
-       return 0;
-}
-
-static int midi_playback_close(struct snd_rawmidi_substream *substream)
+static int midi_close(struct snd_rawmidi_substream *substream)
 {
        struct snd_efw *efw = substream->rmidi->private_data;
 
        mutex_lock(&efw->mutex);
-       efw->playback_substreams--;
+       --efw->substreams_counter;
        snd_efw_stream_stop_duplex(efw);
        mutex_unlock(&efw->mutex);
 
@@ -120,13 +92,13 @@ static void set_midi_substream_names(struct snd_efw *efw,
 int snd_efw_create_midi_devices(struct snd_efw *efw)
 {
        static const struct snd_rawmidi_ops capture_ops = {
-               .open           = midi_capture_open,
-               .close          = midi_capture_close,
+               .open           = midi_open,
+               .close          = midi_close,
                .trigger        = midi_capture_trigger,
        };
        static const struct snd_rawmidi_ops playback_ops = {
-               .open           = midi_playback_open,
-               .close          = midi_playback_close,
+               .open           = midi_open,
+               .close          = midi_close,
                .trigger        = midi_playback_trigger,
        };
        struct snd_rawmidi *rmidi;
index affc50fe2e8eb93a63711810e73139ac38914b25..a7025dccc75489cefde6340b6b5d124cb7a3ba91 100644 (file)
@@ -218,7 +218,7 @@ static int pcm_close(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+static int pcm_hw_params(struct snd_pcm_substream *substream,
                                 struct snd_pcm_hw_params *hw_params)
 {
        struct snd_efw *efw = substream->private_data;
@@ -230,69 +230,40 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
                return err;
 
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
-               mutex_lock(&efw->mutex);
-               efw->capture_substreams++;
-               mutex_unlock(&efw->mutex);
-       }
-
-       return 0;
-}
-static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
-                                 struct snd_pcm_hw_params *hw_params)
-{
-       struct snd_efw *efw = substream->private_data;
-       int err;
+               unsigned int rate = params_rate(hw_params);
 
-       err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
-                                              params_buffer_bytes(hw_params));
-       if (err < 0)
-               return err;
-
-       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
                mutex_lock(&efw->mutex);
-               efw->playback_substreams++;
+               err = snd_efw_stream_reserve_duplex(efw, rate);
+               if (err >= 0)
+                       ++efw->substreams_counter;
                mutex_unlock(&efw->mutex);
        }
 
-       return 0;
+       return err;
 }
 
-static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+static int pcm_hw_free(struct snd_pcm_substream *substream)
 {
        struct snd_efw *efw = substream->private_data;
 
-       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
-               mutex_lock(&efw->mutex);
-               efw->capture_substreams--;
-               mutex_unlock(&efw->mutex);
-       }
-
-       snd_efw_stream_stop_duplex(efw);
-
-       return snd_pcm_lib_free_vmalloc_buffer(substream);
-}
-static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
-{
-       struct snd_efw *efw = substream->private_data;
+       mutex_lock(&efw->mutex);
 
-       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
-               mutex_lock(&efw->mutex);
-               efw->playback_substreams--;
-               mutex_unlock(&efw->mutex);
-       }
+       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+               --efw->substreams_counter;
 
        snd_efw_stream_stop_duplex(efw);
 
+       mutex_unlock(&efw->mutex);
+
        return snd_pcm_lib_free_vmalloc_buffer(substream);
 }
 
 static int pcm_capture_prepare(struct snd_pcm_substream *substream)
 {
        struct snd_efw *efw = substream->private_data;
-       struct snd_pcm_runtime *runtime = substream->runtime;
        int err;
 
-       err = snd_efw_stream_start_duplex(efw, runtime->rate);
+       err = snd_efw_stream_start_duplex(efw);
        if (err >= 0)
                amdtp_stream_pcm_prepare(&efw->tx_stream);
 
@@ -301,10 +272,9 @@ static int pcm_capture_prepare(struct snd_pcm_substream *substream)
 static int pcm_playback_prepare(struct snd_pcm_substream *substream)
 {
        struct snd_efw *efw = substream->private_data;
-       struct snd_pcm_runtime *runtime = substream->runtime;
        int err;
 
-       err = snd_efw_stream_start_duplex(efw, runtime->rate);
+       err = snd_efw_stream_start_duplex(efw);
        if (err >= 0)
                amdtp_stream_pcm_prepare(&efw->rx_stream);
 
@@ -377,8 +347,8 @@ int snd_efw_create_pcm_devices(struct snd_efw *efw)
                .open           = pcm_open,
                .close          = pcm_close,
                .ioctl          = snd_pcm_lib_ioctl,
-               .hw_params      = pcm_capture_hw_params,
-               .hw_free        = pcm_capture_hw_free,
+               .hw_params      = pcm_hw_params,
+               .hw_free        = pcm_hw_free,
                .prepare        = pcm_capture_prepare,
                .trigger        = pcm_capture_trigger,
                .pointer        = pcm_capture_pointer,
@@ -389,8 +359,8 @@ int snd_efw_create_pcm_devices(struct snd_efw *efw)
                .open           = pcm_open,
                .close          = pcm_close,
                .ioctl          = snd_pcm_lib_ioctl,
-               .hw_params      = pcm_playback_hw_params,
-               .hw_free        = pcm_playback_hw_free,
+               .hw_params      = pcm_hw_params,
+               .hw_free        = pcm_hw_free,
                .prepare        = pcm_playback_prepare,
                .trigger        = pcm_playback_trigger,
                .pointer        = pcm_playback_pointer,
index 2d30954124272c750fc0b9f655a151299adbd094..e659a0b89ba5b0c784185cc65a049b94dd94b610 100644 (file)
@@ -42,7 +42,6 @@ end:
 static void
 stop_stream(struct snd_efw *efw, struct amdtp_stream *stream)
 {
-       amdtp_stream_pcm_abort(stream);
        amdtp_stream_stop(stream);
 
        if (stream == &efw->tx_stream)
@@ -51,54 +50,37 @@ stop_stream(struct snd_efw *efw, struct amdtp_stream *stream)
                cmp_connection_break(&efw->in_conn);
 }
 
-static int
-start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
-            unsigned int sampling_rate)
+static int start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
+                       unsigned int rate)
 {
        struct cmp_connection *conn;
-       unsigned int mode, pcm_channels, midi_ports;
        int err;
 
-       err = snd_efw_get_multiplier_mode(sampling_rate, &mode);
-       if (err < 0)
-               goto end;
-       if (stream == &efw->tx_stream) {
+       if (stream == &efw->tx_stream)
                conn = &efw->out_conn;
-               pcm_channels = efw->pcm_capture_channels[mode];
-               midi_ports = efw->midi_out_ports;
-       } else {
+       else
                conn = &efw->in_conn;
-               pcm_channels = efw->pcm_playback_channels[mode];
-               midi_ports = efw->midi_in_ports;
-       }
-
-       err = amdtp_am824_set_parameters(stream, sampling_rate,
-                                        pcm_channels, midi_ports, false);
-       if (err < 0)
-               goto end;
 
-       /*  establish connection via CMP */
-       err = cmp_connection_establish(conn,
-                               amdtp_stream_get_max_payload(stream));
+       // Establish connection via CMP.
+       err = cmp_connection_establish(conn);
        if (err < 0)
-               goto end;
+               return err;
 
-       /* start amdtp stream */
-       err = amdtp_stream_start(stream,
-                                conn->resources.channel,
-                                conn->speed);
+       // Start amdtp stream.
+       err = amdtp_stream_start(stream, conn->resources.channel, conn->speed);
        if (err < 0) {
-               stop_stream(efw, stream);
-               goto end;
+               cmp_connection_break(conn);
+               return err;
        }
 
-       /* wait first callback */
+       // Wait first callback.
        if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
-               stop_stream(efw, stream);
-               err = -ETIMEDOUT;
+               amdtp_stream_stop(stream);
+               cmp_connection_break(conn);
+               return -ETIMEDOUT;
        }
-end:
-       return err;
+
+       return 0;
 }
 
 /*
@@ -164,13 +146,13 @@ int snd_efw_stream_init_duplex(struct snd_efw *efw)
            (efw->firmware_version == 0x5070000 ||
             efw->firmware_version == 0x5070300 ||
             efw->firmware_version == 0x5080000))
-               efw->tx_stream.tx_first_dbc = 0x02;
+               efw->tx_stream.ctx_data.tx.first_dbc = 0x02;
        /* AudioFire9 always reports wrong dbs. */
        if (efw->is_af9)
                efw->tx_stream.flags |= CIP_WRONG_DBS;
        /* Firmware version 5.5 reports fixed interval for dbc. */
        if (efw->firmware_version == 0x5050000)
-               efw->tx_stream.tx_dbc_interval = 8;
+               efw->tx_stream.ctx_data.tx.dbc_interval = 8;
 
        err = init_stream(efw, &efw->rx_stream);
        if (err < 0) {
@@ -188,75 +170,135 @@ end:
        return err;
 }
 
-int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
+static int keep_resources(struct snd_efw *efw, struct amdtp_stream *stream,
+                         unsigned int rate, unsigned int mode)
 {
-       unsigned int curr_rate;
-       int err = 0;
+       unsigned int pcm_channels;
+       unsigned int midi_ports;
+       struct cmp_connection *conn;
+       int err;
 
-       /* Need no substreams */
-       if (efw->playback_substreams == 0 && efw->capture_substreams  == 0)
-               goto end;
+       if (stream == &efw->tx_stream) {
+               pcm_channels = efw->pcm_capture_channels[mode];
+               midi_ports = efw->midi_out_ports;
+               conn = &efw->out_conn;
+       } else {
+               pcm_channels = efw->pcm_playback_channels[mode];
+               midi_ports = efw->midi_in_ports;
+               conn = &efw->in_conn;
+       }
 
-       /*
-        * Considering JACK/FFADO streaming:
-        * TODO: This can be removed hwdep functionality becomes popular.
-        */
-       err = check_connection_used_by_others(efw, &efw->rx_stream);
+       err = amdtp_am824_set_parameters(stream, rate, pcm_channels,
+                                        midi_ports, false);
        if (err < 0)
-               goto end;
+               return err;
 
-       /* packet queueing error */
-       if (amdtp_streaming_error(&efw->tx_stream))
-               stop_stream(efw, &efw->tx_stream);
-       if (amdtp_streaming_error(&efw->rx_stream))
-               stop_stream(efw, &efw->rx_stream);
+       return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
+}
+
+int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate)
+{
+       unsigned int curr_rate;
+       int err;
 
-       /* stop streams if rate is different */
+       // Considering JACK/FFADO streaming:
+       // TODO: This can be removed hwdep functionality becomes popular.
+       err = check_connection_used_by_others(efw, &efw->rx_stream);
+       if (err < 0)
+               return err;
+
+       // stop streams if rate is different.
        err = snd_efw_command_get_sampling_rate(efw, &curr_rate);
        if (err < 0)
-               goto end;
+               return err;
        if (rate == 0)
                rate = curr_rate;
        if (rate != curr_rate) {
                stop_stream(efw, &efw->tx_stream);
                stop_stream(efw, &efw->rx_stream);
+
+               cmp_connection_release(&efw->out_conn);
+               cmp_connection_release(&efw->in_conn);
        }
 
-       /* master should be always running */
-       if (!amdtp_stream_running(&efw->rx_stream)) {
+       if (efw->substreams_counter == 0 || rate != curr_rate) {
+               unsigned int mode;
+
                err = snd_efw_command_set_sampling_rate(efw, rate);
                if (err < 0)
-                       goto end;
+                       return err;
+
+               err = snd_efw_get_multiplier_mode(rate, &mode);
+               if (err < 0)
+                       return err;
+
+               err = keep_resources(efw, &efw->tx_stream, rate, mode);
+               if (err < 0)
+                       return err;
 
+               err = keep_resources(efw, &efw->rx_stream, rate, mode);
+               if (err < 0) {
+                       cmp_connection_release(&efw->in_conn);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+int snd_efw_stream_start_duplex(struct snd_efw *efw)
+{
+       unsigned int rate;
+       int err = 0;
+
+       // Need no substreams.
+       if (efw->substreams_counter == 0)
+               return -EIO;
+
+       err = snd_efw_command_get_sampling_rate(efw, &rate);
+       if (err < 0)
+               return err;
+
+       if (amdtp_streaming_error(&efw->rx_stream) ||
+           amdtp_streaming_error(&efw->tx_stream)) {
+               stop_stream(efw, &efw->rx_stream);
+               stop_stream(efw, &efw->tx_stream);
+       }
+
+       /* master should be always running */
+       if (!amdtp_stream_running(&efw->rx_stream)) {
                err = start_stream(efw, &efw->rx_stream, rate);
                if (err < 0) {
                        dev_err(&efw->unit->device,
                                "fail to start AMDTP master stream:%d\n", err);
-                       goto end;
+                       goto error;
                }
        }
 
-       /* start slave if needed */
-       if (efw->capture_substreams > 0 &&
-           !amdtp_stream_running(&efw->tx_stream)) {
+       if (!amdtp_stream_running(&efw->tx_stream)) {
                err = start_stream(efw, &efw->tx_stream, rate);
                if (err < 0) {
                        dev_err(&efw->unit->device,
                                "fail to start AMDTP slave stream:%d\n", err);
-                       stop_stream(efw, &efw->rx_stream);
+                       goto error;
                }
        }
-end:
+
+       return 0;
+error:
+       stop_stream(efw, &efw->rx_stream);
+       stop_stream(efw, &efw->tx_stream);
        return err;
 }
 
 void snd_efw_stream_stop_duplex(struct snd_efw *efw)
 {
-       if (efw->capture_substreams == 0) {
+       if (efw->substreams_counter == 0) {
                stop_stream(efw, &efw->tx_stream);
+               stop_stream(efw, &efw->rx_stream);
 
-               if (efw->playback_substreams == 0)
-                       stop_stream(efw, &efw->rx_stream);
+               cmp_connection_release(&efw->out_conn);
+               cmp_connection_release(&efw->in_conn);
        }
 }
 
index 4d2351c0e8a3e2998274fb679e7c91b76f370058..3d36f125cf6a8eafe32a74e9ce04f913a4416f85 100644 (file)
@@ -18,7 +18,7 @@ static void copy_sph(u32 *frame, __be32 *buffer, unsigned int data_blocks,
 static void copy_message(u64 *frames, __be32 *buffer, unsigned int data_blocks,
                         unsigned int data_block_quadlets);
 
-TRACE_EVENT(in_data_block_sph,
+TRACE_EVENT(data_block_sph,
        TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
        TP_ARGS(s, data_blocks, buffer),
        TP_STRUCT__entry(
@@ -28,8 +28,13 @@ TRACE_EVENT(in_data_block_sph,
                __dynamic_array(u32, tstamps, data_blocks)
        ),
        TP_fast_assign(
-               __entry->src = fw_parent_device(s->unit)->node_id;
-               __entry->dst = fw_parent_device(s->unit)->card->node_id;
+               if (s->direction == AMDTP_IN_STREAM) {
+                       __entry->src = fw_parent_device(s->unit)->node_id;
+                       __entry->dst = fw_parent_device(s->unit)->card->node_id;
+               } else {
+                       __entry->src = fw_parent_device(s->unit)->card->node_id;
+                       __entry->dst = fw_parent_device(s->unit)->node_id;
+               }
                __entry->data_blocks = data_blocks;
                copy_sph(__get_dynamic_array(tstamps), buffer, data_blocks, s->data_block_quadlets);
        ),
@@ -42,55 +47,7 @@ TRACE_EVENT(in_data_block_sph,
        )
 );
 
-TRACE_EVENT(out_data_block_sph,
-       TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
-       TP_ARGS(s, data_blocks, buffer),
-       TP_STRUCT__entry(
-               __field(int, src)
-               __field(int, dst)
-               __field(unsigned int, data_blocks)
-               __dynamic_array(u32, tstamps, data_blocks)
-       ),
-       TP_fast_assign(
-               __entry->src = fw_parent_device(s->unit)->card->node_id;
-               __entry->dst = fw_parent_device(s->unit)->node_id;
-               __entry->data_blocks = data_blocks;
-               copy_sph(__get_dynamic_array(tstamps), buffer, data_blocks, s->data_block_quadlets);
-       ),
-       TP_printk(
-               "%04x %04x %u %s",
-               __entry->src,
-               __entry->dst,
-               __entry->data_blocks,
-               __print_array(__get_dynamic_array(tstamps), __entry->data_blocks, 4)
-       )
-);
-
-TRACE_EVENT(in_data_block_message,
-       TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
-       TP_ARGS(s, data_blocks, buffer),
-       TP_STRUCT__entry(
-               __field(int, src)
-               __field(int, dst)
-               __field(unsigned int, data_blocks)
-               __dynamic_array(u64, messages, data_blocks)
-       ),
-       TP_fast_assign(
-               __entry->src = fw_parent_device(s->unit)->node_id;
-               __entry->dst = fw_parent_device(s->unit)->card->node_id;
-               __entry->data_blocks = data_blocks;
-               copy_message(__get_dynamic_array(messages), buffer, data_blocks, s->data_block_quadlets);
-       ),
-       TP_printk(
-               "%04x %04x %u %s",
-               __entry->src,
-               __entry->dst,
-               __entry->data_blocks,
-               __print_array(__get_dynamic_array(messages), __entry->data_blocks, 8)
-       )
-);
-
-TRACE_EVENT(out_data_block_message,
+TRACE_EVENT(data_block_message,
        TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
        TP_ARGS(s, data_blocks, buffer),
        TP_STRUCT__entry(
@@ -100,8 +57,13 @@ TRACE_EVENT(out_data_block_message,
                __dynamic_array(u64, messages, data_blocks)
        ),
        TP_fast_assign(
-               __entry->src = fw_parent_device(s->unit)->card->node_id;
-               __entry->dst = fw_parent_device(s->unit)->node_id;
+               if (s->direction == AMDTP_IN_STREAM) {
+                       __entry->src = fw_parent_device(s->unit)->node_id;
+                       __entry->dst = fw_parent_device(s->unit)->card->node_id;
+               } else {
+                       __entry->src = fw_parent_device(s->unit)->card->node_id;
+                       __entry->dst = fw_parent_device(s->unit)->node_id;
+               }
                __entry->data_blocks = data_blocks;
                copy_message(__get_dynamic_array(messages), buffer, data_blocks, s->data_block_quadlets);
        ),
index 782d1fa024ecc4b9b41f8e0afc9e23b2824f0bd4..7973dedd31ef3006399b2c0c1a38033ce141d9fc 100644 (file)
@@ -305,8 +305,8 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
        struct amdtp_motu *p = s->protocol;
        struct snd_pcm_substream *pcm;
 
-       trace_in_data_block_sph(s, data_blocks, buffer);
-       trace_in_data_block_message(s, data_blocks, buffer);
+       trace_data_block_sph(s, data_blocks, buffer);
+       trace_data_block_message(s, data_blocks, buffer);
 
        if (p->midi_ports)
                read_midi_messages(s, buffer, data_blocks);
@@ -383,8 +383,8 @@ static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
 
        write_sph(s, buffer, data_blocks);
 
-       trace_out_data_block_sph(s, data_blocks, buffer);
-       trace_out_data_block_message(s, data_blocks, buffer);
+       trace_data_block_sph(s, data_blocks, buffer);
+       trace_data_block_message(s, data_blocks, buffer);
 
        return data_blocks;
 }
@@ -428,7 +428,7 @@ int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
                return err;
 
        s->sph = 1;
-       s->fdf = MOTU_FDF_AM824;
+       s->ctx_data.rx.fdf = MOTU_FDF_AM824;
 
        return 0;
 }
index 75f6b2e9ca9e2390ce1404ef1e957c6e95aa040e..46a0035df31e5ea573df1af852bed26c92024fd9 100644 (file)
@@ -6,7 +6,7 @@
  */
 #include "motu.h"
 
-static int midi_capture_open(struct snd_rawmidi_substream *substream)
+static int midi_open(struct snd_rawmidi_substream *substream)
 {
        struct snd_motu *motu = substream->rmidi->private_data;
        int err;
@@ -17,30 +17,13 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
 
        mutex_lock(&motu->mutex);
 
-       motu->capture_substreams++;
-       err = snd_motu_stream_start_duplex(motu, 0);
-
-       mutex_unlock(&motu->mutex);
-
-       if (err < 0)
-               snd_motu_stream_lock_release(motu);
-
-       return err;
-}
-
-static int midi_playback_open(struct snd_rawmidi_substream *substream)
-{
-       struct snd_motu *motu = substream->rmidi->private_data;
-       int err;
-
-       err = snd_motu_stream_lock_try(motu);
-       if (err < 0)
-               return err;
-
-       mutex_lock(&motu->mutex);
-
-       motu->playback_substreams++;
-       err = snd_motu_stream_start_duplex(motu, 0);
+       err = snd_motu_stream_reserve_duplex(motu, 0);
+       if (err >= 0) {
+               ++motu->substreams_counter;
+               err = snd_motu_stream_start_duplex(motu);
+               if (err < 0)
+                       --motu->substreams_counter;
+       }
 
        mutex_unlock(&motu->mutex);
 
@@ -50,28 +33,13 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
        return err;
 }
 
-static int midi_capture_close(struct snd_rawmidi_substream *substream)
-{
-       struct snd_motu *motu = substream->rmidi->private_data;
-
-       mutex_lock(&motu->mutex);
-
-       motu->capture_substreams--;
-       snd_motu_stream_stop_duplex(motu);
-
-       mutex_unlock(&motu->mutex);
-
-       snd_motu_stream_lock_release(motu);
-       return 0;
-}
-
-static int midi_playback_close(struct snd_rawmidi_substream *substream)
+static int midi_close(struct snd_rawmidi_substream *substream)
 {
        struct snd_motu *motu = substream->rmidi->private_data;
 
        mutex_lock(&motu->mutex);
 
-       motu->playback_substreams--;
+       --motu->substreams_counter;
        snd_motu_stream_stop_duplex(motu);
 
        mutex_unlock(&motu->mutex);
@@ -128,13 +96,13 @@ static void set_midi_substream_names(struct snd_motu *motu,
 int snd_motu_create_midi_devices(struct snd_motu *motu)
 {
        static const struct snd_rawmidi_ops capture_ops = {
-               .open           = midi_capture_open,
-               .close          = midi_capture_close,
+               .open           = midi_open,
+               .close          = midi_close,
                .trigger        = midi_capture_trigger,
        };
        static const struct snd_rawmidi_ops playback_ops = {
-               .open           = midi_playback_open,
-               .close          = midi_playback_close,
+               .open           = midi_open,
+               .close          = midi_close,
                .trigger        = midi_playback_trigger,
        };
        struct snd_rawmidi *rmidi;
index 5e7db7aa4f08ea7d48384f766821ba7923258e94..aa2e584da6fe65bdbf37576ea9718807f8066343 100644 (file)
@@ -189,8 +189,8 @@ static int pcm_close(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static int capture_hw_params(struct snd_pcm_substream *substream,
-                            struct snd_pcm_hw_params *hw_params)
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+                        struct snd_pcm_hw_params *hw_params)
 {
        struct snd_motu *motu = substream->private_data;
        int err;
@@ -201,57 +201,26 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
                return err;
 
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
-               mutex_lock(&motu->mutex);
-               motu->capture_substreams++;
-               mutex_unlock(&motu->mutex);
-       }
-
-       return 0;
-}
-static int playback_hw_params(struct snd_pcm_substream *substream,
-                             struct snd_pcm_hw_params *hw_params)
-{
-       struct snd_motu *motu = substream->private_data;
-       int err;
-
-       err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
-                                              params_buffer_bytes(hw_params));
-       if (err < 0)
-               return err;
+               unsigned int rate = params_rate(hw_params);
 
-       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
                mutex_lock(&motu->mutex);
-               motu->playback_substreams++;
+               err = snd_motu_stream_reserve_duplex(motu, rate);
+               if (err >= 0)
+                       ++motu->substreams_counter;
                mutex_unlock(&motu->mutex);
        }
 
-       return 0;
-}
-
-static int capture_hw_free(struct snd_pcm_substream *substream)
-{
-       struct snd_motu *motu = substream->private_data;
-
-       mutex_lock(&motu->mutex);
-
-       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-               motu->capture_substreams--;
-
-       snd_motu_stream_stop_duplex(motu);
-
-       mutex_unlock(&motu->mutex);
-
-       return snd_pcm_lib_free_vmalloc_buffer(substream);
+       return err;
 }
 
-static int playback_hw_free(struct snd_pcm_substream *substream)
+static int pcm_hw_free(struct snd_pcm_substream *substream)
 {
        struct snd_motu *motu = substream->private_data;
 
        mutex_lock(&motu->mutex);
 
        if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-               motu->playback_substreams--;
+               --motu->substreams_counter;
 
        snd_motu_stream_stop_duplex(motu);
 
@@ -266,7 +235,7 @@ static int capture_prepare(struct snd_pcm_substream *substream)
        int err;
 
        mutex_lock(&motu->mutex);
-       err = snd_motu_stream_start_duplex(motu, substream->runtime->rate);
+       err = snd_motu_stream_start_duplex(motu);
        mutex_unlock(&motu->mutex);
        if (err >= 0)
                amdtp_stream_pcm_prepare(&motu->tx_stream);
@@ -279,7 +248,7 @@ static int playback_prepare(struct snd_pcm_substream *substream)
        int err;
 
        mutex_lock(&motu->mutex);
-       err = snd_motu_stream_start_duplex(motu, substream->runtime->rate);
+       err = snd_motu_stream_start_duplex(motu);
        mutex_unlock(&motu->mutex);
        if (err >= 0)
                amdtp_stream_pcm_prepare(&motu->rx_stream);
@@ -355,8 +324,8 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu)
                .open      = pcm_open,
                .close     = pcm_close,
                .ioctl     = snd_pcm_lib_ioctl,
-               .hw_params = capture_hw_params,
-               .hw_free   = capture_hw_free,
+               .hw_params = pcm_hw_params,
+               .hw_free   = pcm_hw_free,
                .prepare   = capture_prepare,
                .trigger   = capture_trigger,
                .pointer   = capture_pointer,
@@ -367,8 +336,8 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu)
                .open      = pcm_open,
                .close     = pcm_close,
                .ioctl     = snd_pcm_lib_ioctl,
-               .hw_params = playback_hw_params,
-               .hw_free   = playback_hw_free,
+               .hw_params = pcm_hw_params,
+               .hw_free   = pcm_hw_free,
                .prepare   = playback_prepare,
                .trigger   = playback_trigger,
                .pointer   = playback_pointer,
index 81f7edc560d0490a5d9ce93d632c0757b535d7bf..2bbb335e8de199f3ba0ba00be859bc6ceb8a07aa 100644 (file)
 #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 +82,7 @@ static int start_both_streams(struct snd_motu *motu, unsigned int rate)
                                          sizeof(reg));
 }
 
-static void stop_both_streams(struct snd_motu *motu)
+static void finish_session(struct snd_motu *motu)
 {
        __be32 reg;
        u32 data;
@@ -93,6 +92,9 @@ static void stop_both_streams(struct snd_motu *motu)
        if (err < 0)
                return;
 
+       amdtp_stream_stop(&motu->tx_stream);
+       amdtp_stream_stop(&motu->rx_stream);
+
        err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
                                        sizeof(reg));
        if (err < 0)
@@ -105,9 +107,6 @@ static void stop_both_streams(struct snd_motu *motu)
        reg = cpu_to_be32(data);
        snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &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)
@@ -125,28 +124,12 @@ static int start_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
        if (err < 0)
                return err;
 
-       if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
-               amdtp_stream_stop(stream);
-               fw_iso_resources_free(resources);
+       if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT))
                return -ETIMEDOUT;
-       }
 
        return 0;
 }
 
-static void stop_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
-{
-       struct fw_iso_resources *resources;
-
-       if (stream == &motu->rx_stream)
-               resources = &motu->rx_resources;
-       else
-               resources = &motu->tx_resources;
-
-       amdtp_stream_stop(stream);
-       fw_iso_resources_free(resources);
-}
-
 int snd_motu_stream_cache_packet_formats(struct snd_motu *motu)
 {
        int err;
@@ -174,6 +157,48 @@ int snd_motu_stream_cache_packet_formats(struct snd_motu *motu)
        return 0;
 }
 
+int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate)
+{
+       unsigned int curr_rate;
+       int err;
+
+       err = motu->spec->protocol->get_clock_rate(motu, &curr_rate);
+       if (err < 0)
+               return err;
+       if (rate == 0)
+               rate = curr_rate;
+
+       if (motu->substreams_counter == 0 || curr_rate != rate) {
+               finish_session(motu);
+
+               fw_iso_resources_free(&motu->tx_resources);
+               fw_iso_resources_free(&motu->rx_resources);
+
+               err = motu->spec->protocol->set_clock_rate(motu, rate);
+               if (err < 0) {
+                       dev_err(&motu->unit->device,
+                               "fail to set sampling rate: %d\n", err);
+                       return err;
+               }
+
+               err = snd_motu_stream_cache_packet_formats(motu);
+               if (err < 0)
+                       return err;
+
+               err = keep_resources(motu, rate, &motu->tx_stream);
+               if (err < 0)
+                       return err;
+
+               err = keep_resources(motu, rate, &motu->rx_stream);
+               if (err < 0) {
+                       fw_iso_resources_free(&motu->tx_resources);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
 static int ensure_packet_formats(struct snd_motu *motu)
 {
        __be32 reg;
@@ -200,55 +225,34 @@ static int ensure_packet_formats(struct snd_motu *motu)
                                          sizeof(reg));
 }
 
-int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
+int snd_motu_stream_start_duplex(struct snd_motu *motu)
 {
-       const struct snd_motu_protocol *protocol = motu->spec->protocol;
-       unsigned int curr_rate;
+       unsigned int generation = motu->rx_resources.generation;
        int err = 0;
 
-       if (motu->capture_substreams == 0 && motu->playback_substreams == 0)
+       if (motu->substreams_counter == 0)
                return 0;
 
-       /* Some packet queueing errors. */
        if (amdtp_streaming_error(&motu->rx_stream) ||
-           amdtp_streaming_error(&motu->tx_stream)) {
-               amdtp_stream_stop(&motu->rx_stream);
-               amdtp_stream_stop(&motu->tx_stream);
-               stop_both_streams(motu);
-       }
+           amdtp_streaming_error(&motu->tx_stream))
+               finish_session(motu);
 
-       err = snd_motu_stream_cache_packet_formats(motu);
-       if (err < 0)
-               return err;
+       if (generation != fw_parent_device(motu->unit)->card->generation) {
+               err = fw_iso_resources_update(&motu->rx_resources);
+               if (err < 0)
+                       return err;
 
-       /* Stop stream if rate is different. */
-       err = protocol->get_clock_rate(motu, &curr_rate);
-       if (err < 0) {
-               dev_err(&motu->unit->device,
-                       "fail to get sampling rate: %d\n", err);
-               return err;
-       }
-       if (rate == 0)
-               rate = curr_rate;
-       if (rate != curr_rate) {
-               amdtp_stream_stop(&motu->rx_stream);
-               amdtp_stream_stop(&motu->tx_stream);
-               stop_both_streams(motu);
+               err = fw_iso_resources_update(&motu->tx_resources);
+               if (err < 0)
+                       return err;
        }
 
        if (!amdtp_stream_running(&motu->rx_stream)) {
-               err = protocol->set_clock_rate(motu, rate);
-               if (err < 0) {
-                       dev_err(&motu->unit->device,
-                               "fail to set sampling rate: %d\n", err);
-                       return err;
-               }
-
                err = ensure_packet_formats(motu);
                if (err < 0)
                        return err;
 
-               err = start_both_streams(motu, rate);
+               err = begin_session(motu);
                if (err < 0) {
                        dev_err(&motu->unit->device,
                                "fail to start isochronous comm: %d\n", err);
@@ -262,7 +266,7 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
                        goto stop_streams;
                }
 
-               err = protocol->switch_fetching_mode(motu, true);
+               err = motu->spec->protocol->switch_fetching_mode(motu, true);
                if (err < 0) {
                        dev_err(&motu->unit->device,
                                "fail to enable frame fetching: %d\n", err);
@@ -270,13 +274,11 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
                }
        }
 
-       if (!amdtp_stream_running(&motu->tx_stream) &&
-           motu->capture_substreams > 0) {
+       if (!amdtp_stream_running(&motu->tx_stream)) {
                err = start_isoc_ctx(motu, &motu->tx_stream);
                if (err < 0) {
                        dev_err(&motu->unit->device,
                                "fail to start IR context: %d", err);
-                       amdtp_stream_stop(&motu->rx_stream);
                        goto stop_streams;
                }
        }
@@ -284,21 +286,17 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
        return 0;
 
 stop_streams:
-       stop_both_streams(motu);
+       finish_session(motu);
        return err;
 }
 
 void snd_motu_stream_stop_duplex(struct snd_motu *motu)
 {
-       if (motu->capture_substreams == 0) {
-               if (amdtp_stream_running(&motu->tx_stream))
-                       stop_isoc_ctx(motu, &motu->tx_stream);
-
-               if (motu->playback_substreams == 0) {
-                       if (amdtp_stream_running(&motu->rx_stream))
-                               stop_isoc_ctx(motu, &motu->rx_stream);
-                       stop_both_streams(motu);
-               }
+       if (motu->substreams_counter == 0) {
+               finish_session(motu);
+
+               fw_iso_resources_free(&motu->tx_resources);
+               fw_iso_resources_free(&motu->rx_resources);
        }
 }
 
@@ -371,8 +369,7 @@ void snd_motu_stream_destroy_duplex(struct snd_motu *motu)
        destroy_stream(motu, AMDTP_IN_STREAM);
        destroy_stream(motu, AMDTP_OUT_STREAM);
 
-       motu->playback_substreams = 0;
-       motu->capture_substreams = 0;
+       motu->substreams_counter = 0;
 }
 
 static void motu_lock_changed(struct snd_motu *motu)
index 7c795294428d17f1561a0d82938f692994de4329..09d1451d7de49eda67a456d1b4f64a466f1407ed 100644 (file)
@@ -59,8 +59,7 @@ struct snd_motu {
        struct amdtp_stream rx_stream;
        struct fw_iso_resources tx_resources;
        struct fw_iso_resources rx_resources;
-       unsigned int capture_substreams;
-       unsigned int playback_substreams;
+       unsigned int substreams_counter;
 
        /* For notification. */
        struct fw_address_handler async_handler;
@@ -153,7 +152,8 @@ void snd_motu_transaction_unregister(struct snd_motu *motu);
 int snd_motu_stream_init_duplex(struct snd_motu *motu);
 void snd_motu_stream_destroy_duplex(struct snd_motu *motu);
 int snd_motu_stream_cache_packet_formats(struct snd_motu *motu);
-int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate);
+int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate);
+int snd_motu_stream_start_duplex(struct snd_motu *motu);
 void snd_motu_stream_stop_duplex(struct snd_motu *motu);
 int snd_motu_stream_lock_try(struct snd_motu *motu);
 void snd_motu_stream_lock_release(struct snd_motu *motu);
index cbce01308bd1c5261e444ef396d18050fb49af08..9bdec08cb8eac0de6605c7832cb067043342c362 100644 (file)
@@ -18,8 +18,13 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
 
        mutex_lock(&oxfw->mutex);
 
-       oxfw->capture_substreams++;
-       err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream, 0, 0);
+       err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, 0, 0);
+       if (err >= 0) {
+               ++oxfw->substreams_count;
+               err = snd_oxfw_stream_start_duplex(oxfw);
+               if (err < 0)
+                       --oxfw->substreams_count;
+       }
 
        mutex_unlock(&oxfw->mutex);
 
@@ -40,8 +45,11 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
 
        mutex_lock(&oxfw->mutex);
 
-       oxfw->playback_substreams++;
-       err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream, 0, 0);
+       err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, 0, 0);
+       if (err >= 0) {
+               ++oxfw->substreams_count;
+               err = snd_oxfw_stream_start_duplex(oxfw);
+       }
 
        mutex_unlock(&oxfw->mutex);
 
@@ -57,8 +65,8 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
 
        mutex_lock(&oxfw->mutex);
 
-       oxfw->capture_substreams--;
-       snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream);
+       --oxfw->substreams_count;
+       snd_oxfw_stream_stop_duplex(oxfw);
 
        mutex_unlock(&oxfw->mutex);
 
@@ -72,8 +80,8 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
 
        mutex_lock(&oxfw->mutex);
 
-       oxfw->playback_substreams--;
-       snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream);
+       --oxfw->substreams_count;
+       snd_oxfw_stream_stop_duplex(oxfw);
 
        mutex_unlock(&oxfw->mutex);
 
index 94f367cdfdf3bdb89da6debf5d846c03c02e2d7b..9ea39348cdf5623db8c1a8a73fd9a26ecadee095 100644 (file)
@@ -219,12 +219,18 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
                return err;
 
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+               unsigned int rate = params_rate(hw_params);
+               unsigned int channels = params_channels(hw_params);
+
                mutex_lock(&oxfw->mutex);
-               oxfw->capture_substreams++;
+               err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream,
+                                                    rate, channels);
+               if (err >= 0)
+                       ++oxfw->substreams_count;
                mutex_unlock(&oxfw->mutex);
        }
 
-       return 0;
+       return err;
 }
 static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
                                  struct snd_pcm_hw_params *hw_params)
@@ -238,8 +244,14 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
                return err;
 
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+               unsigned int rate = params_rate(hw_params);
+               unsigned int channels = params_channels(hw_params);
+
                mutex_lock(&oxfw->mutex);
-               oxfw->playback_substreams++;
+               err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream,
+                                                    rate, channels);
+               if (err >= 0)
+                       ++oxfw->substreams_count;
                mutex_unlock(&oxfw->mutex);
        }
 
@@ -253,9 +265,9 @@ static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
        mutex_lock(&oxfw->mutex);
 
        if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-               oxfw->capture_substreams--;
+               --oxfw->substreams_count;
 
-       snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream);
+       snd_oxfw_stream_stop_duplex(oxfw);
 
        mutex_unlock(&oxfw->mutex);
 
@@ -268,9 +280,9 @@ static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
        mutex_lock(&oxfw->mutex);
 
        if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-               oxfw->playback_substreams--;
+               --oxfw->substreams_count;
 
-       snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream);
+       snd_oxfw_stream_stop_duplex(oxfw);
 
        mutex_unlock(&oxfw->mutex);
 
@@ -280,12 +292,10 @@ static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
 static int pcm_capture_prepare(struct snd_pcm_substream *substream)
 {
        struct snd_oxfw *oxfw = substream->private_data;
-       struct snd_pcm_runtime *runtime = substream->runtime;
        int err;
 
        mutex_lock(&oxfw->mutex);
-       err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream,
-                                           runtime->rate, runtime->channels);
+       err = snd_oxfw_stream_start_duplex(oxfw);
        mutex_unlock(&oxfw->mutex);
        if (err < 0)
                goto end;
@@ -297,12 +307,10 @@ end:
 static int pcm_playback_prepare(struct snd_pcm_substream *substream)
 {
        struct snd_oxfw *oxfw = substream->private_data;
-       struct snd_pcm_runtime *runtime = substream->runtime;
        int err;
 
        mutex_lock(&oxfw->mutex);
-       err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream,
-                                           runtime->rate, runtime->channels);
+       err = snd_oxfw_stream_start_duplex(oxfw);
        mutex_unlock(&oxfw->mutex);
        if (err < 0)
                goto end;
index 5ffedb0ade3f8e93713ff44fc07ab9ced3b21f4d..74c972d25c6642934e7cefa77588ac74be980123 100644 (file)
@@ -100,85 +100,34 @@ static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s,
        return 0;
 }
 
-static void stop_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
+static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
 {
-       amdtp_stream_pcm_abort(stream);
-       amdtp_stream_stop(stream);
-
-       if (stream == &oxfw->tx_stream)
-               cmp_connection_break(&oxfw->out_conn);
-       else
-               cmp_connection_break(&oxfw->in_conn);
-}
-
-static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream,
-                       unsigned int rate, unsigned int pcm_channels)
-{
-       u8 **formats;
        struct cmp_connection *conn;
-       struct snd_oxfw_stream_formation formation;
-       unsigned int i, midi_ports;
        int err;
 
-       if (stream == &oxfw->rx_stream) {
-               formats = oxfw->rx_stream_formats;
+       if (stream == &oxfw->rx_stream)
                conn = &oxfw->in_conn;
-       } else {
-               formats = oxfw->tx_stream_formats;
+       else
                conn = &oxfw->out_conn;
-       }
-
-       /* Get stream format */
-       for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
-               if (formats[i] == NULL)
-                       break;
-
-               err = snd_oxfw_stream_parse_format(formats[i], &formation);
-               if (err < 0)
-                       goto end;
-               if (rate != formation.rate)
-                       continue;
-               if (pcm_channels == 0 ||  pcm_channels == formation.pcm)
-                       break;
-       }
-       if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) {
-               err = -EINVAL;
-               goto end;
-       }
-
-       pcm_channels = formation.pcm;
-       midi_ports = formation.midi * 8;
-
-       /* The stream should have one pcm channels at least */
-       if (pcm_channels == 0) {
-               err = -EINVAL;
-               goto end;
-       }
-       err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports,
-                                        false);
-       if (err < 0)
-               goto end;
 
-       err = cmp_connection_establish(conn,
-                                      amdtp_stream_get_max_payload(stream));
+       err = cmp_connection_establish(conn);
        if (err < 0)
-               goto end;
+               return err;
 
-       err = amdtp_stream_start(stream,
-                                conn->resources.channel,
-                                conn->speed);
+       err = amdtp_stream_start(stream, conn->resources.channel, conn->speed);
        if (err < 0) {
                cmp_connection_break(conn);
-               goto end;
+               return err;
        }
 
-       /* Wait first packet */
+       // Wait first packet.
        if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
-               stop_stream(oxfw, stream);
-               err = -ETIMEDOUT;
+               amdtp_stream_stop(stream);
+               cmp_connection_break(conn);
+               return -ETIMEDOUT;
        }
-end:
-       return err;
+
+       return 0;
 }
 
 static int check_connection_used_by_others(struct snd_oxfw *oxfw,
@@ -205,8 +154,7 @@ static int check_connection_used_by_others(struct snd_oxfw *oxfw,
        return err;
 }
 
-int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
-                                struct amdtp_stream *stream)
+static int init_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
 {
        struct cmp_connection *conn;
        enum cmp_direction c_dir;
@@ -225,13 +173,12 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
 
        err = cmp_connection_init(conn, oxfw->unit, c_dir, 0);
        if (err < 0)
-               goto end;
+               return err;
 
        err = amdtp_am824_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
        if (err < 0) {
-               amdtp_stream_destroy(stream);
                cmp_connection_destroy(conn);
-               goto end;
+               return err;
        }
 
        /*
@@ -245,115 +192,195 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
                if (oxfw->wrong_dbs)
                        oxfw->tx_stream.flags |= CIP_WRONG_DBS;
        }
-end:
-       return err;
+
+       return 0;
 }
 
-int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
-                                 struct amdtp_stream *stream,
-                                 unsigned int rate, unsigned int pcm_channels)
+static int keep_resources(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
 {
-       struct amdtp_stream *opposite;
-       struct snd_oxfw_stream_formation formation;
        enum avc_general_plug_dir dir;
-       unsigned int substreams, opposite_substreams;
-       int err = 0;
+       u8 **formats;
+       struct snd_oxfw_stream_formation formation;
+       struct cmp_connection *conn;
+       int i;
+       int err;
 
-       if (stream == &oxfw->tx_stream) {
-               substreams = oxfw->capture_substreams;
-               opposite = &oxfw->rx_stream;
-               opposite_substreams = oxfw->playback_substreams;
-               dir = AVC_GENERAL_PLUG_DIR_OUT;
+       if (stream == &oxfw->rx_stream) {
+               dir = AVC_GENERAL_PLUG_DIR_IN;
+               formats = oxfw->rx_stream_formats;
+               conn = &oxfw->in_conn;
        } else {
-               substreams = oxfw->playback_substreams;
-               opposite_substreams = oxfw->capture_substreams;
+               dir = AVC_GENERAL_PLUG_DIR_OUT;
+               formats = oxfw->tx_stream_formats;
+               conn = &oxfw->out_conn;
+       }
 
-               if (oxfw->has_output)
-                       opposite = &oxfw->rx_stream;
-               else
-                       opposite = NULL;
+       err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
+       if (err < 0)
+               return err;
 
-               dir = AVC_GENERAL_PLUG_DIR_IN;
+       for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+               struct snd_oxfw_stream_formation fmt;
+
+               if (formats[i] == NULL)
+                       break;
+
+               err = snd_oxfw_stream_parse_format(formats[i], &fmt);
+               if (err < 0)
+                       return err;
+
+               if (fmt.rate == formation.rate && fmt.pcm == formation.pcm &&
+                   fmt.midi == formation.midi)
+                       break;
        }
+       if (i == SND_OXFW_STREAM_FORMAT_ENTRIES)
+               return -EINVAL;
 
-       if (substreams == 0)
-               goto end;
+       // The stream should have one pcm channels at least.
+       if (formation.pcm == 0)
+               return -EINVAL;
 
-       /*
-        * Considering JACK/FFADO streaming:
-        * TODO: This can be removed hwdep functionality becomes popular.
-        */
-       err = check_connection_used_by_others(oxfw, stream);
+       err = amdtp_am824_set_parameters(stream, formation.rate, formation.pcm,
+                                        formation.midi * 8, false);
        if (err < 0)
-               goto end;
+               return err;
 
-       /* packet queueing error */
-       if (amdtp_streaming_error(stream))
-               stop_stream(oxfw, stream);
+       return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream));
+}
+
+int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw,
+                                  struct amdtp_stream *stream,
+                                  unsigned int rate, unsigned int pcm_channels)
+{
+       struct snd_oxfw_stream_formation formation;
+       enum avc_general_plug_dir dir;
+       int err;
+
+       // Considering JACK/FFADO streaming:
+       // TODO: This can be removed hwdep functionality becomes popular.
+       err = check_connection_used_by_others(oxfw, &oxfw->rx_stream);
+       if (err < 0)
+               return err;
+       if (oxfw->has_output) {
+               err = check_connection_used_by_others(oxfw, &oxfw->tx_stream);
+               if (err < 0)
+                       return err;
+       }
+
+       if (stream == &oxfw->tx_stream)
+               dir = AVC_GENERAL_PLUG_DIR_OUT;
+       else
+               dir = AVC_GENERAL_PLUG_DIR_IN;
 
        err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation);
        if (err < 0)
-               goto end;
-       if (rate == 0)
+               return err;
+       if (rate == 0) {
                rate = formation.rate;
-       if (pcm_channels == 0)
                pcm_channels = formation.pcm;
+       }
+       if (formation.rate != rate || formation.pcm != pcm_channels) {
+               amdtp_stream_stop(&oxfw->rx_stream);
+               cmp_connection_break(&oxfw->in_conn);
+               cmp_connection_release(&oxfw->in_conn);
 
-       if ((formation.rate != rate) || (formation.pcm != pcm_channels)) {
-               if (opposite != NULL) {
-                       err = check_connection_used_by_others(oxfw, opposite);
-                       if (err < 0)
-                               goto end;
-                       stop_stream(oxfw, opposite);
+               if (oxfw->has_output) {
+                       amdtp_stream_stop(&oxfw->tx_stream);
+                       cmp_connection_break(&oxfw->out_conn);
+                       cmp_connection_release(&oxfw->out_conn);
                }
-               stop_stream(oxfw, stream);
+       }
 
+       if (oxfw->substreams_count == 0 ||
+           formation.rate != rate || formation.pcm != pcm_channels) {
                err = set_stream_format(oxfw, stream, rate, pcm_channels);
                if (err < 0) {
                        dev_err(&oxfw->unit->device,
                                "fail to set stream format: %d\n", err);
-                       goto end;
+                       return err;
                }
 
-               /* Start opposite stream if needed. */
-               if (opposite && !amdtp_stream_running(opposite) &&
-                   (opposite_substreams > 0)) {
-                       err = start_stream(oxfw, opposite, rate, 0);
+               err = keep_resources(oxfw, &oxfw->rx_stream);
+               if (err < 0)
+                       return err;
+
+               if (oxfw->has_output) {
+                       err = keep_resources(oxfw, &oxfw->tx_stream);
                        if (err < 0) {
-                               dev_err(&oxfw->unit->device,
-                                       "fail to restart stream: %d\n", err);
-                               goto end;
+                               cmp_connection_release(&oxfw->in_conn);
+                               return err;
                        }
                }
        }
 
-       /* Start requested stream. */
-       if (!amdtp_stream_running(stream)) {
-               err = start_stream(oxfw, stream, rate, pcm_channels);
-               if (err < 0)
+       return 0;
+}
+
+int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
+{
+       int err;
+
+       if (oxfw->substreams_count == 0)
+               return -EIO;
+
+       if (amdtp_streaming_error(&oxfw->rx_stream) ||
+           amdtp_streaming_error(&oxfw->tx_stream)) {
+               amdtp_stream_stop(&oxfw->rx_stream);
+               cmp_connection_break(&oxfw->in_conn);
+
+               if (oxfw->has_output) {
+                       amdtp_stream_stop(&oxfw->tx_stream);
+                       cmp_connection_break(&oxfw->out_conn);
+               }
+       }
+
+       if (!amdtp_stream_running(&oxfw->rx_stream)) {
+               err = start_stream(oxfw, &oxfw->rx_stream);
+               if (err < 0) {
                        dev_err(&oxfw->unit->device,
-                               "fail to start stream: %d\n", err);
+                               "fail to start rx stream: %d\n", err);
+                       goto error;
+               }
+       }
+
+       if (oxfw->has_output) {
+               if (!amdtp_stream_running(&oxfw->tx_stream)) {
+                       err = start_stream(oxfw, &oxfw->tx_stream);
+                       if (err < 0) {
+                               dev_err(&oxfw->unit->device,
+                                       "fail to start tx stream: %d\n", err);
+                               goto error;
+                       }
+               }
+       }
+
+       return 0;
+error:
+       amdtp_stream_stop(&oxfw->rx_stream);
+       cmp_connection_break(&oxfw->in_conn);
+       if (oxfw->has_output) {
+               amdtp_stream_stop(&oxfw->tx_stream);
+               cmp_connection_break(&oxfw->out_conn);
        }
-end:
        return err;
 }
 
-void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw,
-                                 struct amdtp_stream *stream)
+void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw)
 {
-       if (((stream == &oxfw->tx_stream) && (oxfw->capture_substreams > 0)) ||
-           ((stream == &oxfw->rx_stream) && (oxfw->playback_substreams > 0)))
-               return;
+       if (oxfw->substreams_count == 0) {
+               amdtp_stream_stop(&oxfw->rx_stream);
+               cmp_connection_break(&oxfw->in_conn);
+               cmp_connection_release(&oxfw->in_conn);
 
-       stop_stream(oxfw, stream);
+               if (oxfw->has_output) {
+                       amdtp_stream_stop(&oxfw->tx_stream);
+                       cmp_connection_break(&oxfw->out_conn);
+                       cmp_connection_release(&oxfw->out_conn);
+               }
+       }
 }
 
-/*
- * This function should be called before starting the stream or after stopping
- * the streams.
- */
-void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
-                                    struct amdtp_stream *stream)
+static void destroy_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
 {
        struct cmp_connection *conn;
 
@@ -366,20 +393,48 @@ void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
        cmp_connection_destroy(conn);
 }
 
-void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw,
-                                   struct amdtp_stream *stream)
+int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw)
 {
-       struct cmp_connection *conn;
+       int err;
 
-       if (stream == &oxfw->tx_stream)
-               conn = &oxfw->out_conn;
-       else
-               conn = &oxfw->in_conn;
+       err = init_stream(oxfw, &oxfw->rx_stream);
+       if (err < 0)
+               return err;
 
-       if (cmp_connection_update(conn) < 0)
-               stop_stream(oxfw, stream);
-       else
-               amdtp_stream_update(stream);
+       if (oxfw->has_output) {
+               err = init_stream(oxfw, &oxfw->tx_stream);
+               if (err < 0) {
+                       destroy_stream(oxfw, &oxfw->rx_stream);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+// This function should be called before starting the stream or after stopping
+// the streams.
+void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw)
+{
+       destroy_stream(oxfw, &oxfw->rx_stream);
+
+       if (oxfw->has_output)
+               destroy_stream(oxfw, &oxfw->tx_stream);
+}
+
+void snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw)
+{
+       amdtp_stream_stop(&oxfw->rx_stream);
+       cmp_connection_break(&oxfw->in_conn);
+
+       amdtp_stream_pcm_abort(&oxfw->rx_stream);
+
+       if (oxfw->has_output) {
+               amdtp_stream_stop(&oxfw->tx_stream);
+               cmp_connection_break(&oxfw->out_conn);
+
+               amdtp_stream_pcm_abort(&oxfw->tx_stream);
+       }
 }
 
 int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw,
index 9fd145cc4b07f00d206197ef11470f0ac48eb64d..fb6df3fc018edc27bf6964c57f67d761d6088f4f 100644 (file)
@@ -118,9 +118,7 @@ static void oxfw_card_free(struct snd_card *card)
 {
        struct snd_oxfw *oxfw = card->private_data;
 
-       snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
-       if (oxfw->has_output)
-               snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
+       snd_oxfw_stream_destroy_duplex(oxfw);
 }
 
 static int detect_quirks(struct snd_oxfw *oxfw)
@@ -208,14 +206,9 @@ static void do_registration(struct work_struct *work)
        if (err < 0)
                goto error;
 
-       err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
+       err = snd_oxfw_stream_init_duplex(oxfw);
        if (err < 0)
                goto error;
-       if (oxfw->has_output) {
-               err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream);
-               if (err < 0)
-                       goto error;
-       }
 
        err = snd_oxfw_create_pcm(oxfw);
        if (err < 0)
@@ -282,11 +275,7 @@ static void oxfw_bus_reset(struct fw_unit *unit)
 
        if (oxfw->registered) {
                mutex_lock(&oxfw->mutex);
-
-               snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
-               if (oxfw->has_output)
-                       snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
-
+               snd_oxfw_stream_update_duplex(oxfw);
                mutex_unlock(&oxfw->mutex);
 
                if (oxfw->entry->vendor_id == OUI_STANTON)
index 36112850ef9216ea283e8b07ec4f383a5b9d0bff..cb69ab87bb14feb9655eab020896f432ea42317b 100644 (file)
@@ -52,8 +52,7 @@ struct snd_oxfw {
        struct cmp_connection in_conn;
        struct amdtp_stream tx_stream;
        struct amdtp_stream rx_stream;
-       unsigned int capture_substreams;
-       unsigned int playback_substreams;
+       unsigned int substreams_count;
 
        unsigned int midi_input_ports;
        unsigned int midi_output_ports;
@@ -99,17 +98,14 @@ int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate,
                                enum avc_general_plug_dir dir,
                                unsigned short pid);
 
-int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
-                                struct amdtp_stream *stream);
-int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw,
-                                 struct amdtp_stream *stream,
-                                 unsigned int rate, unsigned int pcm_channels);
-void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw,
-                                 struct amdtp_stream *stream);
-void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw,
-                                    struct amdtp_stream *stream);
-void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw,
-                                   struct amdtp_stream *stream);
+int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw);
+int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw,
+                                  struct amdtp_stream *stream,
+                                  unsigned int rate, unsigned int pcm_channels);
+int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw);
+void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw);
+void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw);
+void snd_oxfw_stream_update_duplex(struct snd_oxfw *oxfw);
 
 struct snd_oxfw_stream_formation {
        unsigned int rate;
index d9d20ef22f5bcd7031a4f08f0eb0a8b8e4fefe18..95fb10b7a737a9619a330c45c0b846ea12e739fc 100644 (file)
@@ -223,7 +223,7 @@ int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
                return 0;
 
        /* Use fixed value for FDF field. */
-       s->fdf = 0x00;
+       s->ctx_data.rx.fdf = 0x00;
 
        /* This protocol uses fixed number of data channels for PCM samples. */
        p = s->protocol;
index a8cd9b156488f606bfd422bc5f02c9085c7536f6..b5ced5415e40543a050a7755bbef1855df487de3 100644 (file)
@@ -83,8 +83,8 @@ static int pcm_close(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
-                                struct snd_pcm_hw_params *hw_params)
+static int pcm_hw_params(struct snd_pcm_substream *substream,
+                        struct snd_pcm_hw_params *hw_params)
 {
        struct snd_tscm *tscm = substream->private_data;
        int err;
@@ -95,58 +95,26 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
                return err;
 
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
-               mutex_lock(&tscm->mutex);
-               tscm->substreams_counter++;
-               mutex_unlock(&tscm->mutex);
-       }
-
-       return 0;
-}
-
-static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
-                                 struct snd_pcm_hw_params *hw_params)
-{
-       struct snd_tscm *tscm = substream->private_data;
-       int err;
-
-       err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
-                                              params_buffer_bytes(hw_params));
-       if (err < 0)
-               return err;
+               unsigned int rate = params_rate(hw_params);
 
-       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
                mutex_lock(&tscm->mutex);
-               tscm->substreams_counter++;
+               err = snd_tscm_stream_reserve_duplex(tscm, rate);
+               if (err >= 0)
+                       ++tscm->substreams_counter;
                mutex_unlock(&tscm->mutex);
        }
 
-       return 0;
-}
-
-static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
-{
-       struct snd_tscm *tscm = substream->private_data;
-
-       mutex_lock(&tscm->mutex);
-
-       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-               tscm->substreams_counter--;
-
-       snd_tscm_stream_stop_duplex(tscm);
-
-       mutex_unlock(&tscm->mutex);
-
-       return snd_pcm_lib_free_vmalloc_buffer(substream);
+       return err;
 }
 
-static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+static int pcm_hw_free(struct snd_pcm_substream *substream)
 {
        struct snd_tscm *tscm = substream->private_data;
 
        mutex_lock(&tscm->mutex);
 
        if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-               tscm->substreams_counter--;
+               --tscm->substreams_counter;
 
        snd_tscm_stream_stop_duplex(tscm);
 
@@ -259,8 +227,8 @@ int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
                .open           = pcm_open,
                .close          = pcm_close,
                .ioctl          = snd_pcm_lib_ioctl,
-               .hw_params      = pcm_capture_hw_params,
-               .hw_free        = pcm_capture_hw_free,
+               .hw_params      = pcm_hw_params,
+               .hw_free        = pcm_hw_free,
                .prepare        = pcm_capture_prepare,
                .trigger        = pcm_capture_trigger,
                .pointer        = pcm_capture_pointer,
@@ -271,8 +239,8 @@ int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
                .open           = pcm_open,
                .close          = pcm_close,
                .ioctl          = snd_pcm_lib_ioctl,
-               .hw_params      = pcm_playback_hw_params,
-               .hw_free        = pcm_playback_hw_free,
+               .hw_params      = pcm_hw_params,
+               .hw_free        = pcm_hw_free,
                .prepare        = pcm_playback_prepare,
                .trigger        = pcm_playback_trigger,
                .pointer        = pcm_playback_pointer,
index e6fcd9e1996100da86b4c1684ead5c585cdf376e..e852e46ebe6fa6c9bf77ce579f214c997cb67680 100644 (file)
@@ -165,7 +165,7 @@ static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate)
        __be32 reg;
        int err;
 
-       /* Set an option for unknown purpose. */
+       // Set an option for unknown purpose.
        reg = cpu_to_be32(0x00200000);
        err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
                                 TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
@@ -173,17 +173,16 @@ static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate)
        if (err < 0)
                return err;
 
-       err = enable_data_channels(tscm);
-       if (err < 0)
-               return err;
-
-       return set_clock(tscm, rate, INT_MAX);
+       return enable_data_channels(tscm);
 }
 
 static void finish_session(struct snd_tscm *tscm)
 {
        __be32 reg;
 
+       amdtp_stream_stop(&tscm->rx_stream);
+       amdtp_stream_stop(&tscm->tx_stream);
+
        reg = 0;
        snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
                           TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
@@ -194,6 +193,19 @@ static void finish_session(struct snd_tscm *tscm)
                           TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
                           &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)
@@ -201,6 +213,30 @@ static int begin_session(struct snd_tscm *tscm)
        __be32 reg;
        int err;
 
+       // Register the isochronous channel for transmitting stream.
+       reg = cpu_to_be32(tscm->tx_resources.channel);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
+                                &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,
@@ -215,7 +251,7 @@ static int begin_session(struct snd_tscm *tscm)
        if (err < 0)
                return err;
 
-       /* Set an option for unknown purpose. */
+       // Set an option for unknown purpose.
        reg = cpu_to_be32(0x00002000);
        err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
                                 TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
@@ -223,7 +259,7 @@ static int begin_session(struct snd_tscm *tscm)
        if (err < 0)
                return err;
 
-       /* Start multiplexing PCM samples on packets. */
+       // Start multiplexing PCM samples on packets.
        reg = cpu_to_be32(0x00000001);
        return snd_fw_transaction(tscm->unit,
                                  TCODE_WRITE_QUADLET_REQUEST,
@@ -231,82 +267,24 @@ static int begin_session(struct snd_tscm *tscm)
                                  &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)
@@ -345,7 +323,7 @@ int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
        return err;
 }
 
-/* At bus reset, streaming is stopped and some registers are clear. */
+// At bus reset, streaming is stopped and some registers are clear.
 void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)
 {
        amdtp_stream_pcm_abort(&tscm->tx_stream);
@@ -368,33 +346,62 @@ void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
        fw_iso_resources_destroy(&tscm->tx_resources);
 }
 
-int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
+int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate)
 {
        unsigned int curr_rate;
        int err;
 
-       if (tscm->substreams_counter == 0)
-               return 0;
-
        err = snd_tscm_stream_get_rate(tscm, &curr_rate);
        if (err < 0)
                return err;
-       if (curr_rate != rate ||
-           amdtp_streaming_error(&tscm->rx_stream) ||
-           amdtp_streaming_error(&tscm->tx_stream)) {
+
+       if (tscm->substreams_counter == 0 || rate != curr_rate) {
                finish_session(tscm);
 
-               amdtp_stream_stop(&tscm->rx_stream);
-               amdtp_stream_stop(&tscm->tx_stream);
+               fw_iso_resources_free(&tscm->tx_resources);
+               fw_iso_resources_free(&tscm->rx_resources);
 
-               release_resources(tscm);
+               err = set_clock(tscm, rate, INT_MAX);
+               if (err < 0)
+                       return err;
+
+               err = keep_resources(tscm, rate, &tscm->tx_stream);
+               if (err < 0)
+                       return err;
+
+               err = keep_resources(tscm, rate, &tscm->rx_stream);
+               if (err < 0) {
+                       fw_iso_resources_free(&tscm->tx_resources);
+                       return err;
+               }
        }
 
-       if (!amdtp_stream_running(&tscm->rx_stream)) {
-               err = keep_resources(tscm, rate);
+       return 0;
+}
+
+int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
+{
+       unsigned int generation = tscm->rx_resources.generation;
+       int err;
+
+       if (tscm->substreams_counter == 0)
+               return 0;
+
+       if (amdtp_streaming_error(&tscm->rx_stream) ||
+           amdtp_streaming_error(&tscm->tx_stream))
+               finish_session(tscm);
+
+       if (generation != fw_parent_device(tscm->unit)->card->generation) {
+               err = fw_iso_resources_update(&tscm->tx_resources);
+               if (err < 0)
+                       goto error;
+
+               err = fw_iso_resources_update(&tscm->rx_resources);
                if (err < 0)
                        goto error;
+       }
 
+       if (!amdtp_stream_running(&tscm->rx_stream)) {
                err = set_stream_formats(tscm, rate);
                if (err < 0)
                        goto error;
@@ -432,25 +439,19 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
 
        return 0;
 error:
-       amdtp_stream_stop(&tscm->rx_stream);
-       amdtp_stream_stop(&tscm->tx_stream);
-
        finish_session(tscm);
-       release_resources(tscm);
 
        return err;
 }
 
 void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
 {
-       if (tscm->substreams_counter > 0)
-               return;
-
-       amdtp_stream_stop(&tscm->tx_stream);
-       amdtp_stream_stop(&tscm->rx_stream);
+       if (tscm->substreams_counter == 0) {
+               finish_session(tscm);
 
-       finish_session(tscm);
-       release_resources(tscm);
+               fw_iso_resources_free(&tscm->tx_resources);
+               fw_iso_resources_free(&tscm->rx_resources);
+       }
 }
 
 void snd_tscm_stream_lock_changed(struct snd_tscm *tscm)
index 1d003d4cf448ecb7238d70db363c338f94603ab2..734e5bb9c3dafb631b16e0bedacd25316f4c2065 100644 (file)
@@ -146,6 +146,7 @@ int snd_tscm_stream_get_clock(struct snd_tscm *tscm,
 int snd_tscm_stream_init_duplex(struct snd_tscm *tscm);
 void snd_tscm_stream_update_duplex(struct snd_tscm *tscm);
 void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm);
+int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate);
 int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate);
 void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm);
 
index b02f74528b66de3c0dce17ac7add79581ccfeaec..3b0110545070ac1fec24e44aac251490c18203d7 100644 (file)
@@ -79,6 +79,8 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
        snd_hdac_chip_writew(bus, RINTCNT, 1);
        /* enable rirb dma and response irq */
        snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN);
+       /* Accept unsolicited responses */
+       snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
        spin_unlock_irq(&bus->reg_lock);
 }
 EXPORT_SYMBOL_GPL(snd_hdac_bus_init_cmd_io);
@@ -241,6 +243,8 @@ int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
 
        for (loopcounter = 0;; loopcounter++) {
                spin_lock_irq(&bus->reg_lock);
+               if (bus->polling_mode)
+                       snd_hdac_bus_update_rirb(bus);
                if (!bus->rirb.cmds[addr]) {
                        if (res)
                                *res = bus->rirb.res[addr]; /* the last value */
@@ -415,9 +419,6 @@ int snd_hdac_bus_reset_link(struct hdac_bus *bus, bool full_reset)
                return -EBUSY;
        }
 
-       /* Accept unsolicited responses */
-       snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
-
        /* detect codecs */
        if (!bus->codec_mask) {
                bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS);
index 6907dbefd08c17ce72849ab96b307140ecbebbc2..b26cc93e7e1034d1b446b24490c1dd9b6e11eccf 100644 (file)
@@ -90,7 +90,7 @@ int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus,
 
        fg = codec->afg ? codec->afg : codec->mfg;
 
-       err = snd_hdac_refresh_widgets(codec, false);
+       err = snd_hdac_refresh_widgets(codec);
        if (err < 0)
                goto error;
 
@@ -395,32 +395,35 @@ static void setup_fg_nodes(struct hdac_device *codec)
 /**
  * snd_hdac_refresh_widgets - Reset the widget start/end nodes
  * @codec: the codec object
- * @sysfs: re-initialize sysfs tree, too
  */
-int snd_hdac_refresh_widgets(struct hdac_device *codec, bool sysfs)
+int snd_hdac_refresh_widgets(struct hdac_device *codec)
 {
        hda_nid_t start_nid;
-       int nums, err;
+       int nums, err = 0;
 
+       /*
+        * Serialize against multiple threads trying to update the sysfs
+        * widgets array.
+        */
+       mutex_lock(&codec->widget_lock);
        nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid);
        if (!start_nid || nums <= 0 || nums >= 0xff) {
                dev_err(&codec->dev, "cannot read sub nodes for FG 0x%02x\n",
                        codec->afg);
-               return -EINVAL;
+               err = -EINVAL;
+               goto unlock;
        }
 
-       if (sysfs) {
-               mutex_lock(&codec->widget_lock);
-               err = hda_widget_sysfs_reinit(codec, start_nid, nums);
-               mutex_unlock(&codec->widget_lock);
-               if (err < 0)
-                       return err;
-       }
+       err = hda_widget_sysfs_reinit(codec, start_nid, nums);
+       if (err < 0)
+               goto unlock;
 
        codec->num_nodes = nums;
        codec->start_nid = start_nid;
        codec->end_nid = start_nid + nums;
-       return 0;
+unlock:
+       mutex_unlock(&codec->widget_lock);
+       return err;
 }
 EXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets);
 
index 909d5ef1179c92d3d33f5bb60aa5a5378e30db0b..e56e8332590310f8de65efd3701553544d0e1b0b 100644 (file)
@@ -428,7 +428,7 @@ int hda_widget_sysfs_reinit(struct hdac_device *codec,
        int i;
 
        if (!codec->widgets)
-               return hda_widget_sysfs_init(codec);
+               return 0;
 
        tree = kmemdup(codec->widgets, sizeof(*tree), GFP_KERNEL);
        if (!tree)
index e7234f3d99e248b1f30f74ddd28133c7d755ac3b..2a21a3d997196283f3107929fa1f6847e10724f8 100644 (file)
@@ -1519,7 +1519,6 @@ static int snd_asihpi_volume_get(struct snd_kcontrol *kcontrol,
 static int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
-       int change;
        u32 h_control = kcontrol->private_value;
        short an_gain_mB[HPI_MAX_CHANNELS];
 
@@ -1530,9 +1529,8 @@ static int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol,
        /*  change = asihpi->mixer_volume[addr][0] != left ||
           asihpi->mixer_volume[addr][1] != right;
         */
-       change = 1;
        hpi_handle_error(hpi_volume_set_gain(h_control, an_gain_mB));
-       return change;
+       return 1;
 }
 
 static const DECLARE_TLV_DB_SCALE(db_scale_100, -10000, VOL_STEP_mB, 0);
@@ -1555,13 +1553,12 @@ static int snd_asihpi_volume_mute_put(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
        u32 h_control = kcontrol->private_value;
-       int change = 1;
        /* HPI currently only supports all or none muting of multichannel volume
        ALSA Switch element has opposite sense to HPI mute: on==unmuted, off=muted
        */
        int mute =  ucontrol->value.integer.value[0] ? 0 : HPI_BITMASK_ALL_CHANNELS;
        hpi_handle_error(hpi_volume_set_mute(h_control, mute));
-       return change;
+       return 1;
 }
 
 static int snd_asihpi_volume_add(struct snd_card_asihpi *asihpi,
index a2cce3ecda6f07a59f4cebf93529b383935e5bb1..04c712647853d538ca915984c38c33439b093054 100644 (file)
@@ -694,7 +694,7 @@ static int snd_cs4281_trigger(struct snd_pcm_substream *substream, int cmd)
 
 static unsigned int snd_cs4281_rate(unsigned int rate, unsigned int *real_rate)
 {
-       unsigned int val = ~0;
+       unsigned int val;
        
        if (real_rate)
                *real_rate = rate;
@@ -707,9 +707,8 @@ static unsigned int snd_cs4281_rate(unsigned int rate, unsigned int *real_rate)
        case 44100:     return 1;
        case 48000:     return 0;
        default:
-               goto __variable;
+               break;
        }
-      __variable:
        val = 1536000 / rate;
        if (real_rate)
                *real_rate = 1536000 / val;
index b181752b8481cf2df1b2e0da829caf3910ed0d63..50d4a87a6bb34b57ead7cee72a5e2ceb0924b74f 100644 (file)
@@ -1058,7 +1058,6 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe,
 {
        int i;
        u32 channel_mask;
-       char is_cyclic;
 
        dev_dbg(chip->card->dev,
                "allocate_pipes: ch=%d int=%d\n", pipe_index, interleave);
@@ -1066,8 +1065,6 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe,
        if (chip->bad_board)
                return -EIO;
 
-       is_cyclic = 1;  /* This driver uses cyclic buffers only */
-
        for (channel_mask = i = 0; i < interleave; i++)
                channel_mask |= 1 << (pipe_index + i);
        if (chip->pipe_alloc_mask & channel_mask) {
@@ -1078,8 +1075,8 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe,
 
        chip->comm_page->position[pipe_index] = 0;
        chip->pipe_alloc_mask |= channel_mask;
-       if (is_cyclic)
-               chip->pipe_cyclic_mask |= channel_mask;
+       /* This driver uses cyclic buffers only */
+       chip->pipe_cyclic_mask |= channel_mask;
        pipe->index = pipe_index;
        pipe->interleave = interleave;
        pipe->state = PIPE_STATE_STOPPED;
index 6c51b8363f8b73ccea5f8ff65558a298ddde8524..5346631df1eccd325d4ea6c95ed4df77b5fb80d3 100644 (file)
@@ -108,7 +108,7 @@ static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
 {
        struct hda_conn_list *p;
 
-       p = kmalloc(sizeof(*p) + len * sizeof(hda_nid_t), GFP_KERNEL);
+       p = kmalloc(struct_size(p, conns, len), GFP_KERNEL);
        if (!p)
                return -ENOMEM;
        p->len = len;
@@ -1002,7 +1002,7 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec)
        hda_nid_t fg;
        int err;
 
-       err = snd_hdac_refresh_widgets(&codec->core, true);
+       err = snd_hdac_refresh_widgets(&codec->core);
        if (err < 0)
                return err;
 
index 232a1926758aa07a027d9845d8b3ca16efccdc69..c8d1b4316245f3ac02ca0b25bbe6f63b66eceb02 100644 (file)
@@ -795,11 +795,11 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
 
        for (loopcounter = 0;; loopcounter++) {
                spin_lock_irq(&bus->reg_lock);
-               if (chip->polling_mode || do_poll)
+               if (bus->polling_mode || do_poll)
                        snd_hdac_bus_update_rirb(bus);
                if (!bus->rirb.cmds[addr]) {
                        if (!do_poll)
-                               chip->poll_count = 0;
+                               bus->poll_count = 0;
                        if (res)
                                *res = bus->rirb.res[addr]; /* the last value */
                        spin_unlock_irq(&bus->reg_lock);
@@ -819,21 +819,21 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr,
        if (hbus->no_response_fallback)
                return -EIO;
 
-       if (!chip->polling_mode && chip->poll_count < 2) {
+       if (!bus->polling_mode && bus->poll_count < 2) {
                dev_dbg(chip->card->dev,
                        "azx_get_response timeout, polling the codec once: last cmd=0x%08x\n",
                        bus->last_cmd[addr]);
                do_poll = 1;
-               chip->poll_count++;
+               bus->poll_count++;
                goto again;
        }
 
 
-       if (!chip->polling_mode) {
+       if (!bus->polling_mode) {
                dev_warn(chip->card->dev,
                         "azx_get_response timeout, switching to polling mode: last cmd=0x%08x\n",
                         bus->last_cmd[addr]);
-               chip->polling_mode = 1;
+               bus->polling_mode = 1;
                goto again;
        }
 
index 3aa5c957ffbfa771ee5789288899991ed1cffae5..baa15374fbcb4a38301fa7d5a5197a04ecb66e1b 100644 (file)
@@ -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 50f86f458918049b6989b244919d7796f617f63a..cb8b0945547cdf2c0382fdc7e0bdb794401150b8 100644 (file)
@@ -1687,10 +1687,6 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
        else
                chip->bdl_pos_adj = bdl_pos_adj[dev];
 
-       /* Workaround for a communication error on CFL (bko#199007) and CNL */
-       if (IS_CFL(pci) || IS_CNL(pci))
-               chip->polling_mode = 1;
-
        err = azx_bus_init(chip, model[dev], &pci_hda_io_ops);
        if (err < 0) {
                kfree(hda);
@@ -1698,6 +1694,10 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
                return err;
        }
 
+       /* Workaround for a communication error on CFL (bko#199007) and CNL */
+       if (IS_CFL(pci) || IS_CNL(pci))
+               azx_bus(chip)->polling_mode = 1;
+
        if (chip->driver_type == AZX_DRIVER_NVIDIA) {
                dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n");
                chip->bus.needs_damn_long_delay = 1;
@@ -2374,6 +2374,9 @@ static const struct pci_device_id azx_ids[] = {
        /* Icelake */
        { PCI_DEVICE(0x8086, 0x34c8),
          .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+       /* Elkhart Lake */
+       { PCI_DEVICE(0x8086, 0x4b55),
+         .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
        /* Broxton-P(Apollolake) */
        { PCI_DEVICE(0x8086, 0x5a98),
          .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON },
index 6d9acd5c4e4118888fe6f0c5c7940a93e7fe1e8b..1fb7b06457ae9168027a884c55a3b6c43cd3988a 100644 (file)
@@ -559,7 +559,7 @@ static void call_jack_callback(struct hda_codec *codec, unsigned int res,
 void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res)
 {
        struct hda_jack_tbl *event;
-       int tag = (res >> AC_UNSOL_RES_TAG_SHIFT) & 0x7f;
+       int tag = (res & AC_UNSOL_RES_TAG) >> AC_UNSOL_RES_TAG_SHIFT;
 
        event = snd_hda_jack_tbl_get_from_tag(codec, tag);
        if (!event)
index c3096796ee05e4cd0ca27fb486f46e9543b2f7bc..0d51823d7270eaf2629eabb15290e14d4839dec2 100644 (file)
@@ -2718,7 +2718,7 @@ static bool is_last(const struct dsp_image_seg *p)
 
 static size_t dsp_sizeof(const struct dsp_image_seg *p)
 {
-       return sizeof(*p) + p->count*sizeof(u32);
+       return struct_size(p, data, p->count);
 }
 
 static const struct dsp_image_seg *get_next_seg_ptr(
@@ -5980,7 +5980,7 @@ static int ca0132_alt_volume_put(struct snd_kcontrol *kcontrol,
        int ch = get_amp_channels(kcontrol);
        long *valp = ucontrol->value.integer.value;
        hda_nid_t vnid = 0;
-       int changed = 1;
+       int changed;
 
        switch (nid) {
        case 0x02:
index b7bde55b6adf1ab2278fe2d3215a0bfb6903f059..40323d91f9e4f91683ed03e1e006690a569b4252 100644 (file)
@@ -1614,7 +1614,8 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
        if (jack == NULL)
                goto unlock;
        snd_jack_report(jack,
-                       eld->monitor_present ? SND_JACK_AVOUT : 0);
+                       (eld->monitor_present && eld->eld_valid) ?
+                               SND_JACK_AVOUT : 0);
  unlock:
        mutex_unlock(&per_pin->lock);
 }
index 5b3c26991f26b07474a8481b588982432fd7fdbe..f24a757f8239b0ccd2e80d02df51e300a0e44221 100644 (file)
@@ -2448,9 +2448,10 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1558, 0x9501, "Clevo P950HR", ALC1220_FIXUP_CLEVO_P950),
        SND_PCI_QUIRK(0x1558, 0x95e1, "Clevo P95xER", ALC1220_FIXUP_CLEVO_P950),
        SND_PCI_QUIRK(0x1558, 0x95e2, "Clevo P950ER", ALC1220_FIXUP_CLEVO_P950),
-       SND_PCI_QUIRK(0x1558, 0x96e1, "System76 Oryx Pro (oryp5)", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
-       SND_PCI_QUIRK(0x1558, 0x97e1, "System76 Oryx Pro (oryp5)", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
-       SND_PCI_QUIRK(0x1558, 0x65d1, "Tuxedo Book XC1509", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+       SND_PCI_QUIRK(0x1558, 0x96e1, "Clevo P960[ER][CDFN]-K", ALC1220_FIXUP_CLEVO_P950),
+       SND_PCI_QUIRK(0x1558, 0x97e1, "Clevo P970[ER][CDFN]", ALC1220_FIXUP_CLEVO_P950),
+       SND_PCI_QUIRK(0x1558, 0x65d1, "Clevo PB51[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+       SND_PCI_QUIRK(0x1558, 0x67d1, "Clevo PB71[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
        SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD),
        SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD),
        SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530),
@@ -3254,6 +3255,7 @@ static void alc256_init(struct hda_codec *codec)
        alc_update_coefex_idx(codec, 0x57, 0x04, 0x0007, 0x4); /* Hight power */
        alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 1 << 15); /* Clear bit */
        alc_update_coefex_idx(codec, 0x53, 0x02, 0x8000, 0 << 15);
+       alc_update_coef_idx(codec, 0x36, 1 << 13, 1 << 5); /* Switch pcbeep path to Line in path*/
 }
 
 static void alc256_shutup(struct hda_codec *codec)
@@ -7074,6 +7076,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
        SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
        SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
+       SND_PCI_QUIRK(0x17aa, 0x3111, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
        SND_PCI_QUIRK(0x17aa, 0x312a, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
        SND_PCI_QUIRK(0x17aa, 0x312f, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
        SND_PCI_QUIRK(0x17aa, 0x313c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION),
@@ -7823,7 +7826,6 @@ static int patch_alc269(struct hda_codec *codec)
                spec->shutup = alc256_shutup;
                spec->init_hook = alc256_init;
                spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */
-               alc_update_coef_idx(codec, 0x36, 1 << 13, 1 << 5); /* Switch pcbeep path to Line in path*/
                break;
        case 0x10ec0257:
                spec->codec_variant = ALC269_TYPE_ALC257;
index 9236a1a8c49b33df29e965007bb42c9ba1f7b45e..dd3a873777eb5753ada80be2d9a960be8526038c 100644 (file)
@@ -986,8 +986,6 @@ static int lx_interrupt_handle_async_events(struct lx6464es *chip, u32 irqsrc,
         * Stat[8]      LSB overrun
         * */
 
-       u64 orun_mask;
-       u64 urun_mask;
        int eb_pending_out = (irqsrc & MASK_SYS_STATUS_EOBO) ? 1 : 0;
        int eb_pending_in  = (irqsrc & MASK_SYS_STATUS_EOBI) ? 1 : 0;
 
@@ -1010,9 +1008,6 @@ static int lx_interrupt_handle_async_events(struct lx6464es *chip, u32 irqsrc,
                            *r_notified_out_pipe_mask);
        }
 
-       orun_mask = ((u64)stat[7] << 32) + stat[8];
-       urun_mask = ((u64)stat[5] << 32) + stat[6];
-
        /* todo: handle xrun notification */
 
        return err;
index 6f5eaa510bbcee57488a3f7e2618161f6640fba1..81a6f4b2bd3c52c93abc6a35a83c1e24437fa57e 100644 (file)
@@ -23,6 +23,9 @@
  *      Modified 2011-01-14 added S/PDIF input on RayDATs by Adrian Knoth
  *
  *     Modified 2011-01-25 variable period sizes on RayDAT/AIO by Adrian Knoth
+ *
+ *      Modified 2019-05-23 fix AIO single speed ADAT capture and playback
+ *      by Philippe.Bekaert@uhasselt.be
  */
 
 /* *************    Register Documentation   *******************************************************
@@ -1091,9 +1094,9 @@ static int hdspm_autosync_ref(struct hdspm *hdspm);
 static int hdspm_set_toggle_setting(struct hdspm *hdspm, u32 regmask, int out);
 static int snd_hdspm_set_defaults(struct hdspm *hdspm);
 static int hdspm_system_clock_mode(struct hdspm *hdspm);
-static void hdspm_set_sgbuf(struct hdspm *hdspm,
-                           struct snd_pcm_substream *substream,
-                            unsigned int reg, int channels);
+static void hdspm_set_channel_dma_addr(struct hdspm *hdspm,
+                                      struct snd_pcm_substream *substream,
+                                      unsigned int reg, int channels);
 
 static int hdspm_aes_sync_check(struct hdspm *hdspm, int idx);
 static int hdspm_wc_sync_check(struct hdspm *hdspm);
@@ -5574,11 +5577,16 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 
-               hdspm_set_sgbuf(hdspm, substream, HDSPM_pageAddressBufferOut,
-                               params_channels(params));
+               for (i = 0; i < params_channels(params); ++i) {
+                       int c = hdspm->channel_map_out[i];
 
-               for (i = 0; i < params_channels(params); ++i)
-                       snd_hdspm_enable_out(hdspm, i, 1);
+                       if (c < 0)
+                               continue;      /* just make sure */
+                       hdspm_set_channel_dma_addr(hdspm, substream,
+                                                  HDSPM_pageAddressBufferOut,
+                                                  c);
+                       snd_hdspm_enable_out(hdspm, c, 1);
+               }
 
                hdspm->playback_buffer =
                        (unsigned char *) substream->runtime->dma_area;
@@ -5586,11 +5594,16 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
                        "Allocated sample buffer for playback at %p\n",
                                hdspm->playback_buffer);
        } else {
-               hdspm_set_sgbuf(hdspm, substream, HDSPM_pageAddressBufferIn,
-                               params_channels(params));
-
-               for (i = 0; i < params_channels(params); ++i)
-                       snd_hdspm_enable_in(hdspm, i, 1);
+               for (i = 0; i < params_channels(params); ++i) {
+                       int c = hdspm->channel_map_in[i];
+
+                       if (c < 0)
+                               continue;
+                       hdspm_set_channel_dma_addr(hdspm, substream,
+                                                  HDSPM_pageAddressBufferIn,
+                                                  c);
+                       snd_hdspm_enable_in(hdspm, c, 1);
+               }
 
                hdspm->capture_buffer =
                        (unsigned char *) substream->runtime->dma_area;
@@ -5651,19 +5664,17 @@ static int snd_hdspm_hw_free(struct snd_pcm_substream *substream)
        struct hdspm *hdspm = snd_pcm_substream_chip(substream);
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-
-               /* params_channels(params) should be enough,
-                  but to get sure in case of error */
-               for (i = 0; i < hdspm->max_channels_out; ++i)
+               /* Just disable all channels. The saving when disabling a */
+               /* smaller set is not worth the trouble. */
+               for (i = 0; i < HDSPM_MAX_CHANNELS; ++i)
                        snd_hdspm_enable_out(hdspm, i, 0);
 
                hdspm->playback_buffer = NULL;
        } else {
-               for (i = 0; i < hdspm->max_channels_in; ++i)
+               for (i = 0; i < HDSPM_MAX_CHANNELS; ++i)
                        snd_hdspm_enable_in(hdspm, i, 0);
 
                hdspm->capture_buffer = NULL;
-
        }
 
        snd_pcm_lib_free_pages(substream);
@@ -6402,17 +6413,17 @@ static int snd_hdspm_preallocate_memory(struct hdspm *hdspm)
        return 0;
 }
 
-
-static void hdspm_set_sgbuf(struct hdspm *hdspm,
-                           struct snd_pcm_substream *substream,
-                            unsigned int reg, int channels)
+/* Inform the card what DMA addresses to use for the indicated channel. */
+/* Each channel got 16 4K pages allocated for DMA transfers. */
+static void hdspm_set_channel_dma_addr(struct hdspm *hdspm,
+                                      struct snd_pcm_substream *substream,
+                                      unsigned int reg, int channel)
 {
        int i;
 
-       /* continuous memory segment */
-       for (i = 0; i < (channels * 16); i++)
+       for (i = channel * 16; i < channel * 16 + 16; i++)
                hdspm_write(hdspm, reg + 4 * i,
-                               snd_pcm_sgbuf_get_addr(substream, 4096 * i));
+                           snd_pcm_sgbuf_get_addr(substream, 4096 * i));
 }
 
 
index 2d3f58aa01c8a1a7de40090bf0f743f786470384..29918954e74078bd8a14ffbe63017a99ae9e38b6 100644 (file)
@@ -2072,7 +2072,7 @@ static int hdac_hdmi_dev_probe(struct hdac_device *hdev)
                        "Failed in parse and map nid with err: %d\n", ret);
                return ret;
        }
-       snd_hdac_refresh_widgets(hdev, true);
+       snd_hdac_refresh_widgets(hdev);
 
        /* ASoC specific initialization */
        ret = devm_snd_soc_register_component(&hdev->dev, &hdmi_hda_codec,
index 136f98bf5d7ee2a170b9122d6d359eb3de33dc22..7f665392618f6469f95768dec9e116f0f09fe2a3 100644 (file)
@@ -34,6 +34,9 @@
 /* platform specific devices */
 #include "shim.h"
 
+#define IS_CFL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa348)
+#define IS_CNL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x9dc8)
+
 /*
  * Debug
  */
@@ -245,6 +248,11 @@ static int hda_init(struct snd_sof_dev *sdev)
        ext_ops = snd_soc_hdac_hda_get_ops();
 #endif
        sof_hda_bus_init(bus, &pci->dev, ext_ops);
+
+       /* Workaround for a communication error on CFL (bko#199007) and CNL */
+       if (IS_CFL(pci) || IS_CNL(pci))
+               bus->polling_mode = 1;
+
        bus->use_posbuf = 1;
        bus->bdl_pos_adj = 0;
 
index c02b51a82775a2e53e4d6978cefa31019685f177..d79db71305f635829d5fe47f31af42c47c353caa 100644 (file)
@@ -285,6 +285,33 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip,
        return nr_rates;
 }
 
+/* Line6 Helix series don't support the UAC2_CS_RANGE usb function
+ * call. Return a static table of known clock rates.
+ */
+static int line6_parse_audio_format_rates_quirk(struct snd_usb_audio *chip,
+                                               struct audioformat *fp)
+{
+       switch (chip->usb_id) {
+       case USB_ID(0x0E41, 0x4241): /* Line6 Helix */
+       case USB_ID(0x0E41, 0x4242): /* Line6 Helix Rack */
+       case USB_ID(0x0E41, 0x4244): /* Line6 Helix LT */
+       case USB_ID(0x0E41, 0x4246): /* Line6 HX-Stomp */
+               /* supported rates: 48Khz */
+               kfree(fp->rate_table);
+               fp->rate_table = kmalloc(sizeof(int), GFP_KERNEL);
+               if (!fp->rate_table)
+                       return -ENOMEM;
+               fp->nr_rates = 1;
+               fp->rate_min = 48000;
+               fp->rate_max = 48000;
+               fp->rates = SNDRV_PCM_RATE_48000;
+               fp->rate_table[0] = 48000;
+               return 0;
+       }
+
+       return -ENODEV;
+}
+
 /*
  * parse the format descriptor and stores the possible sample rates
  * on the audioformat table (audio class v2 and v3).
@@ -294,7 +321,7 @@ static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip,
 {
        struct usb_device *dev = chip->dev;
        unsigned char tmp[2], *data;
-       int nr_triplets, data_size, ret = 0;
+       int nr_triplets, data_size, ret = 0, ret_l6;
        int clock = snd_usb_clock_find_source(chip, fp->protocol,
                                              fp->clock, false);
 
@@ -313,9 +340,22 @@ static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip,
                              tmp, sizeof(tmp));
 
        if (ret < 0) {
-               dev_err(&dev->dev,
-                       "%s(): unable to retrieve number of sample rates (clock %d)\n",
+               /* line6 helix devices don't support UAC2_CS_CONTROL_SAM_FREQ call */
+               ret_l6 = line6_parse_audio_format_rates_quirk(chip, fp);
+               if (ret_l6 == -ENODEV) {
+                       /* no line6 device found continue showing the error */
+                       dev_err(&dev->dev,
+                               "%s(): unable to retrieve number of sample rates (clock %d)\n",
+                               __func__, clock);
+                       goto err;
+               }
+               if (ret_l6 == 0) {
+                       dev_info(&dev->dev,
+                               "%s(): unable to retrieve number of sample rates: set it to a predefined value (clock %d).\n",
                                __func__, clock);
+                       return 0;
+               }
+               ret = ret_l6;
                goto err;
        }
 
index 84aa265dd802c7690a92d563498b05f3103c05fc..71d5f540334a23ea2904cb41e67d066c588ee932 100644 (file)
@@ -63,6 +63,20 @@ void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype
        return NULL;
 }
 
+/* check the validity of pipe and EP types */
+int snd_usb_pipe_sanity_check(struct usb_device *dev, unsigned int pipe)
+{
+       static const int pipetypes[4] = {
+               PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT
+       };
+       struct usb_host_endpoint *ep;
+
+       ep = usb_pipe_endpoint(dev, pipe);
+       if (usb_pipetype(pipe) != pipetypes[usb_endpoint_type(&ep->desc)])
+               return -EINVAL;
+       return 0;
+}
+
 /*
  * Wrapper for usb_control_msg().
  * Allocates a temp buffer to prevent dmaing from/to the stack.
@@ -75,6 +89,9 @@ int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
        void *buf = NULL;
        int timeout;
 
+       if (snd_usb_pipe_sanity_check(dev, pipe))
+               return -EINVAL;
+
        if (size > 0) {
                buf = kmemdup(data, size, GFP_KERNEL);
                if (!buf)
index d338bd0e0ca609b29cb8b5bccb344bf099e8b0c6..6afb70156ec4fcae1549f83100c50271edf50ca7 100644 (file)
@@ -7,6 +7,7 @@ unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size);
 void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype);
 void *snd_usb_find_csint_desc(void *descstart, int desclen, void *after, u8 dsubtype);
 
+int snd_usb_pipe_sanity_check(struct usb_device *dev, unsigned int pipe);
 int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe,
                    __u8 request, __u8 requesttype, __u16 value, __u16 index,
                    void *data, __u16 size);
index e63a2451c88f4c26d3945adc89372267682ff170..ab2ec896f49c89bf60db609c915b277a42045161 100644 (file)
@@ -191,17 +191,6 @@ static int line6_send_raw_message_async_part(struct message *msg,
        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.
 */
index a9f7b4aa32c42680a861b3d2e702b2e45a45f0f1..e5e572ed5f304a0e5e2951944449ba87e1cbaddc 100644 (file)
 
 #define LINE6_CHANNEL_MASK 0x0f
 
-#define CHECK_STARTUP_PROGRESS(x, n)   \
-do {                                   \
-       if ((x) >= (n))                 \
-               return;                 \
-       x = (n);                        \
-} while (0)
-
 extern const unsigned char line6_midi_id[3];
 
 static const int SYSEX_DATA_OFS = sizeof(line6_midi_id) + 3;
@@ -197,8 +190,6 @@ extern int line6_send_sysex_message(struct usb_line6 *line6,
                                    const char *buffer, int size);
 extern ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr,
                             const char *buf, size_t count);
-extern void line6_start_timer(struct timer_list *timer, unsigned long msecs,
-                             void (*function)(struct timer_list *t));
 extern int line6_version_request_async(struct usb_line6 *line6);
 extern int line6_write_data(struct usb_line6 *line6, unsigned address,
                            void *data, unsigned datalen);
index 21127e4958b2b3338106ddac394a51560d5d1933..2c03e0f6bf72d661226b73ddee90c087db8f4b6f 100644 (file)
@@ -556,6 +556,11 @@ int line6_init_pcm(struct usb_line6 *line6,
        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);
index 200ae53adf2253629f82d3870fe2d9bd98384357..ee4c9d220fdf76c82a8504a6a63330462ea64878 100644 (file)
        Stages of POD startup procedure
 */
 enum {
-       POD_STARTUP_INIT = 1,
        POD_STARTUP_VERSIONREQ,
-       POD_STARTUP_WORKQUEUE,
        POD_STARTUP_SETUP,
-       POD_STARTUP_LAST = POD_STARTUP_SETUP - 1
+       POD_STARTUP_DONE,
 };
 
 enum {
@@ -59,12 +57,6 @@ struct usb_line6_pod {
        /* Instrument monitor level */
        int monitor_level;
 
-       /* Timer for device initialization */
-       struct timer_list startup_timer;
-
-       /* Work handler for device initialization */
-       struct work_struct startup_work;
-
        /* Current progress in startup procedure */
        int startup_progress;
 
@@ -78,6 +70,8 @@ struct usb_line6_pod {
        int device_id;
 };
 
+#define line6_to_pod(x)                container_of(x, struct usb_line6_pod, line6)
+
 #define POD_SYSEX_CODE 3
 
 /* *INDENT-OFF* */
@@ -169,10 +163,6 @@ static const char pod_version_header[] = {
        0xf2, 0x7e, 0x7f, 0x06, 0x02
 };
 
-/* forward declarations: */
-static void pod_startup2(struct timer_list *t);
-static void pod_startup3(struct usb_line6_pod *pod);
-
 static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code,
                                    int size)
 {
@@ -185,14 +175,17 @@ static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code,
 */
 static void line6_pod_process_message(struct usb_line6 *line6)
 {
-       struct usb_line6_pod *pod = (struct usb_line6_pod *) line6;
+       struct usb_line6_pod *pod = line6_to_pod(line6);
        const unsigned char *buf = pod->line6.buffer_message;
 
        if (memcmp(buf, pod_version_header, sizeof(pod_version_header)) == 0) {
                pod->firmware_version = buf[13] * 100 + buf[14] * 10 + buf[15];
                pod->device_id = ((int)buf[8] << 16) | ((int)buf[9] << 8) |
                                 (int) buf[10];
-               pod_startup3(pod);
+               if (pod->startup_progress == POD_STARTUP_VERSIONREQ) {
+                       pod->startup_progress = POD_STARTUP_SETUP;
+                       schedule_delayed_work(&line6->startup_work, 0);
+               }
                return;
        }
 
@@ -277,47 +270,27 @@ static ssize_t device_id_show(struct device *dev,
        context). After the last one has finished, the device is ready to use.
 */
 
-static void pod_startup1(struct usb_line6_pod *pod)
-{
-       CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_INIT);
-
-       /* delay startup procedure: */
-       line6_start_timer(&pod->startup_timer, POD_STARTUP_DELAY, pod_startup2);
-}
-
-static void pod_startup2(struct timer_list *t)
-{
-       struct usb_line6_pod *pod = from_timer(pod, t, startup_timer);
-       struct usb_line6 *line6 = &pod->line6;
-
-       CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_VERSIONREQ);
-
-       /* request firmware version: */
-       line6_version_request_async(line6);
-}
-
-static void pod_startup3(struct usb_line6_pod *pod)
-{
-       CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_WORKQUEUE);
-
-       /* schedule work for global work queue: */
-       schedule_work(&pod->startup_work);
-}
-
-static void pod_startup4(struct work_struct *work)
+static void pod_startup(struct usb_line6 *line6)
 {
-       struct usb_line6_pod *pod =
-           container_of(work, struct usb_line6_pod, startup_work);
-       struct usb_line6 *line6 = &pod->line6;
-
-       CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_SETUP);
-
-       /* serial number: */
-       line6_read_serial_number(&pod->line6, &pod->serial_number);
-
-       /* ALSA audio interface: */
-       if (snd_card_register(line6->card))
-               dev_err(line6->ifcdev, "Failed to register POD card.\n");
+       struct usb_line6_pod *pod = line6_to_pod(line6);
+
+       switch (pod->startup_progress) {
+       case POD_STARTUP_VERSIONREQ:
+               /* request firmware version: */
+               line6_version_request_async(line6);
+               break;
+       case POD_STARTUP_SETUP:
+               /* serial number: */
+               line6_read_serial_number(&pod->line6, &pod->serial_number);
+
+               /* ALSA audio interface: */
+               if (snd_card_register(line6->card))
+                       dev_err(line6->ifcdev, "Failed to register POD card.\n");
+               pod->startup_progress = POD_STARTUP_DONE;
+               break;
+       default:
+               break;
+       }
 }
 
 /* POD special files: */
@@ -353,7 +326,7 @@ static int snd_pod_control_monitor_get(struct snd_kcontrol *kcontrol,
                                       struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
-       struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6;
+       struct usb_line6_pod *pod = line6_to_pod(line6pcm->line6);
 
        ucontrol->value.integer.value[0] = pod->monitor_level;
        return 0;
@@ -364,7 +337,7 @@ static int snd_pod_control_monitor_put(struct snd_kcontrol *kcontrol,
                                       struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
-       struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6;
+       struct usb_line6_pod *pod = line6_to_pod(line6pcm->line6);
 
        if (ucontrol->value.integer.value[0] == pod->monitor_level)
                return 0;
@@ -386,17 +359,6 @@ static const struct snd_kcontrol_new pod_control_monitor = {
        .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.
 */
@@ -404,13 +366,10 @@ static int pod_init(struct usb_line6 *line6,
                    const struct usb_device_id *id)
 {
        int err;
-       struct usb_line6_pod *pod = (struct usb_line6_pod *) line6;
+       struct usb_line6_pod *pod = line6_to_pod(line6);
 
        line6->process_message = line6_pod_process_message;
-       line6->disconnect = line6_pod_disconnect;
-
-       timer_setup(&pod->startup_timer, NULL, 0);
-       INIT_WORK(&pod->startup_work, pod_startup4);
+       line6->startup = pod_startup;
 
        /* create sysfs entries: */
        err = snd_card_add_dev_attr(line6->card, &pod_dev_attr_group);
@@ -443,7 +402,8 @@ static int pod_init(struct usb_line6 *line6,
                pod->monitor_level = POD_SYSTEM_INVALID;
 
                /* initiate startup procedure: */
-               pod_startup1(pod);
+               schedule_delayed_work(&line6->startup_work,
+                                     msecs_to_jiffies(POD_STARTUP_DELAY));
        }
 
        return 0;
index 77a1d55334bbb34a0a1717ac1854046b6cd0c305..f0662bd4e50fbf865d253ac2d516fdd173d10dad 100644 (file)
 
 #define PODHD_STARTUP_DELAY 500
 
-/*
- * Stages of POD startup procedure
- */
-enum {
-       PODHD_STARTUP_INIT = 1,
-       PODHD_STARTUP_SCHEDULE_WORKQUEUE,
-       PODHD_STARTUP_SETUP,
-       PODHD_STARTUP_LAST = PODHD_STARTUP_SETUP - 1
-};
-
 enum {
        LINE6_PODHD300,
        LINE6_PODHD400,
@@ -43,15 +33,6 @@ struct usb_line6_podhd {
        /* Generic Line 6 USB data */
        struct usb_line6 line6;
 
-       /* Timer for device initialization */
-       struct timer_list startup_timer;
-
-       /* Work handler for device initialization */
-       struct work_struct startup_work;
-
-       /* Current progress in startup procedure */
-       int startup_progress;
-
        /* Serial number of device */
        u32 serial_number;
 
@@ -59,6 +40,8 @@ struct usb_line6_podhd {
        int firmware_version;
 };
 
+#define line6_to_podhd(x)      container_of(x, struct usb_line6_podhd, line6)
+
 static struct snd_ratden podhd_ratden = {
        .num_min = 48000,
        .num_max = 48000,
@@ -154,10 +137,6 @@ static struct line6_pcm_properties podx3_pcm_properties = {
 };
 static struct usb_driver podhd_driver;
 
-static void podhd_startup_start_workqueue(struct timer_list *t);
-static void podhd_startup_workqueue(struct work_struct *work);
-static int podhd_startup_finalize(struct usb_line6_podhd *pod);
-
 static ssize_t serial_number_show(struct device *dev,
                                  struct device_attribute *attr, char *buf)
 {
@@ -198,26 +177,6 @@ static const struct attribute_group podhd_dev_attr_group = {
  * audio nor bulk interfaces to work.
  */
 
-static void podhd_startup(struct usb_line6_podhd *pod)
-{
-       CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_INIT);
-
-       /* delay startup procedure: */
-       line6_start_timer(&pod->startup_timer, PODHD_STARTUP_DELAY,
-               podhd_startup_start_workqueue);
-}
-
-static void podhd_startup_start_workqueue(struct timer_list *t)
-{
-       struct usb_line6_podhd *pod = from_timer(pod, t, startup_timer);
-
-       CHECK_STARTUP_PROGRESS(pod->startup_progress,
-               PODHD_STARTUP_SCHEDULE_WORKQUEUE);
-
-       /* schedule work for global work queue: */
-       schedule_work(&pod->startup_work);
-}
-
 static int podhd_dev_start(struct usb_line6_podhd *pod)
 {
        int ret;
@@ -268,37 +227,23 @@ exit:
        return ret;
 }
 
-static void podhd_startup_workqueue(struct work_struct *work)
+static void podhd_startup(struct usb_line6 *line6)
 {
-       struct usb_line6_podhd *pod =
-           container_of(work, struct usb_line6_podhd, startup_work);
-
-       CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_SETUP);
+       struct usb_line6_podhd *pod = line6_to_podhd(line6);
 
        podhd_dev_start(pod);
        line6_read_serial_number(&pod->line6, &pod->serial_number);
-
-       podhd_startup_finalize(pod);
-}
-
-static int podhd_startup_finalize(struct usb_line6_podhd *pod)
-{
-       struct usb_line6 *line6 = &pod->line6;
-
-       /* ALSA audio interface: */
-       return snd_card_register(line6->card);
+       if (snd_card_register(line6->card))
+               dev_err(line6->ifcdev, "Failed to register POD HD card.\n");
 }
 
 static void podhd_disconnect(struct usb_line6 *line6)
 {
-       struct usb_line6_podhd *pod = (struct usb_line6_podhd *)line6;
+       struct usb_line6_podhd *pod = line6_to_podhd(line6);
 
        if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL_INFO) {
                struct usb_interface *intf;
 
-               del_timer_sync(&pod->startup_timer);
-               cancel_work_sync(&pod->startup_work);
-
                intf = usb_ifnum_to_if(line6->usbdev,
                                        pod->line6.properties->ctrl_if);
                if (intf)
@@ -313,13 +258,11 @@ static int podhd_init(struct usb_line6 *line6,
                      const struct usb_device_id *id)
 {
        int err;
-       struct usb_line6_podhd *pod = (struct usb_line6_podhd *) line6;
+       struct usb_line6_podhd *pod = line6_to_podhd(line6);
        struct usb_interface *intf;
 
        line6->disconnect = podhd_disconnect;
-
-       timer_setup(&pod->startup_timer, NULL, 0);
-       INIT_WORK(&pod->startup_work, podhd_startup_workqueue);
+       line6->startup = podhd_startup;
 
        if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
                /* claim the data interface */
@@ -358,11 +301,12 @@ static int podhd_init(struct usb_line6 *line6,
 
        if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL_INFO)) {
                /* register USB audio system directly */
-               return podhd_startup_finalize(pod);
+               return snd_card_register(line6->card);
        }
 
        /* init device and delay registering */
-       podhd_startup(pod);
+       schedule_delayed_work(&line6->startup_work,
+                             msecs_to_jiffies(PODHD_STARTUP_DELAY));
        return 0;
 }
 
index 974ab3e62b68dc9476e2cd7850e047679cd73799..d0a555dbe324f0e8049023e1327fec7a6b58b584 100644 (file)
@@ -57,6 +57,8 @@ struct usb_line6_toneport {
        struct toneport_led leds[2];
 };
 
+#define line6_to_toneport(x) container_of(x, struct usb_line6_toneport, line6)
+
 static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2);
 
 #define TONEPORT_PCM_DELAY 1
@@ -207,8 +209,8 @@ static int snd_toneport_source_get(struct snd_kcontrol *kcontrol,
                                   struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
-       struct usb_line6_toneport *toneport =
-           (struct usb_line6_toneport *)line6pcm->line6;
+       struct usb_line6_toneport *toneport = line6_to_toneport(line6pcm->line6);
+
        ucontrol->value.enumerated.item[0] = toneport->source;
        return 0;
 }
@@ -218,8 +220,7 @@ static int snd_toneport_source_put(struct snd_kcontrol *kcontrol,
                                   struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
-       struct usb_line6_toneport *toneport =
-           (struct usb_line6_toneport *)line6pcm->line6;
+       struct usb_line6_toneport *toneport = line6_to_toneport(line6pcm->line6);
        unsigned int source;
 
        source = ucontrol->value.enumerated.item[0];
@@ -393,8 +394,7 @@ static int toneport_setup(struct usb_line6_toneport *toneport)
 */
 static void line6_toneport_disconnect(struct usb_line6 *line6)
 {
-       struct usb_line6_toneport *toneport =
-               (struct usb_line6_toneport *)line6;
+       struct usb_line6_toneport *toneport = line6_to_toneport(line6);
 
        if (toneport_has_led(toneport))
                toneport_remove_leds(toneport);
@@ -408,7 +408,7 @@ static int toneport_init(struct usb_line6 *line6,
                         const struct usb_device_id *id)
 {
        int err;
-       struct usb_line6_toneport *toneport =  (struct usb_line6_toneport *) line6;
+       struct usb_line6_toneport *toneport = line6_to_toneport(line6);
 
        toneport->type = id->driver_info;
 
index e59b974443992d734bb2d9794dcff04158f8e7ac..0d24c72c155fb36dcd53446aa6977239ad97af36 100644 (file)
        Stages of Variax startup procedure
 */
 enum {
-       VARIAX_STARTUP_INIT = 1,
        VARIAX_STARTUP_VERSIONREQ,
-       VARIAX_STARTUP_WAIT,
        VARIAX_STARTUP_ACTIVATE,
-       VARIAX_STARTUP_WORKQUEUE,
        VARIAX_STARTUP_SETUP,
-       VARIAX_STARTUP_LAST = VARIAX_STARTUP_SETUP - 1
 };
 
 enum {
@@ -43,17 +39,12 @@ struct usb_line6_variax {
        /* Buffer for activation code */
        unsigned char *buffer_activate;
 
-       /* Handler for device initialization */
-       struct work_struct startup_work;
-
-       /* Timers for device initialization */
-       struct timer_list startup_timer1;
-       struct timer_list startup_timer2;
-
        /* Current progress in startup procedure */
        int startup_progress;
 };
 
+#define line6_to_variax(x)     container_of(x, struct usb_line6_variax, line6)
+
 #define VARIAX_OFFSET_ACTIVATE 7
 
 /*
@@ -77,11 +68,6 @@ static const char variax_activate[] = {
        0xf7
 };
 
-/* forward declarations: */
-static void variax_startup2(struct timer_list *t);
-static void variax_startup4(struct timer_list *t);
-static void variax_startup5(struct timer_list *t);
-
 static void variax_activate_async(struct usb_line6_variax *variax, int a)
 {
        variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a;
@@ -96,74 +82,30 @@ static void variax_activate_async(struct usb_line6_variax *variax, int a)
        context). After the last one has finished, the device is ready to use.
 */
 
-static void variax_startup1(struct usb_line6_variax *variax)
-{
-       CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_INIT);
-
-       /* delay startup procedure: */
-       line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1,
-                         variax_startup2);
-}
-
-static void variax_startup2(struct timer_list *t)
-{
-       struct usb_line6_variax *variax = from_timer(variax, t, startup_timer1);
-       struct usb_line6 *line6 = &variax->line6;
-
-       /* schedule another startup procedure until startup is complete: */
-       if (variax->startup_progress >= VARIAX_STARTUP_LAST)
-               return;
-
-       variax->startup_progress = VARIAX_STARTUP_VERSIONREQ;
-       line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1,
-                         variax_startup2);
-
-       /* request firmware version: */
-       line6_version_request_async(line6);
-}
-
-static void variax_startup3(struct usb_line6_variax *variax)
-{
-       CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_WAIT);
-
-       /* delay startup procedure: */
-       line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY3,
-                         variax_startup4);
-}
-
-static void variax_startup4(struct timer_list *t)
+static void variax_startup(struct usb_line6 *line6)
 {
-       struct usb_line6_variax *variax = from_timer(variax, t, startup_timer2);
-
-       CHECK_STARTUP_PROGRESS(variax->startup_progress,
-                              VARIAX_STARTUP_ACTIVATE);
-
-       /* activate device: */
-       variax_activate_async(variax, 1);
-       line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4,
-                         variax_startup5);
-}
-
-static void variax_startup5(struct timer_list *t)
-{
-       struct usb_line6_variax *variax = from_timer(variax, t, startup_timer2);
-
-       CHECK_STARTUP_PROGRESS(variax->startup_progress,
-                              VARIAX_STARTUP_WORKQUEUE);
-
-       /* schedule work for global work queue: */
-       schedule_work(&variax->startup_work);
-}
-
-static void variax_startup6(struct work_struct *work)
-{
-       struct usb_line6_variax *variax =
-           container_of(work, struct usb_line6_variax, startup_work);
-
-       CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_SETUP);
-
-       /* ALSA audio interface: */
-       snd_card_register(variax->line6.card);
+       struct usb_line6_variax *variax = line6_to_variax(line6);
+
+       switch (variax->startup_progress) {
+       case VARIAX_STARTUP_VERSIONREQ:
+               /* repeat request until getting the response */
+               schedule_delayed_work(&line6->startup_work,
+                                     msecs_to_jiffies(VARIAX_STARTUP_DELAY1));
+               /* request firmware version: */
+               line6_version_request_async(line6);
+               break;
+       case VARIAX_STARTUP_ACTIVATE:
+               /* activate device: */
+               variax_activate_async(variax, 1);
+               variax->startup_progress = VARIAX_STARTUP_SETUP;
+               schedule_delayed_work(&line6->startup_work,
+                                     msecs_to_jiffies(VARIAX_STARTUP_DELAY4));
+               break;
+       case VARIAX_STARTUP_SETUP:
+               /* ALSA audio interface: */
+               snd_card_register(variax->line6.card);
+               break;
+       }
 }
 
 /*
@@ -171,7 +113,7 @@ static void variax_startup6(struct work_struct *work)
 */
 static void line6_variax_process_message(struct usb_line6 *line6)
 {
-       struct usb_line6_variax *variax = (struct usb_line6_variax *) line6;
+       struct usb_line6_variax *variax = line6_to_variax(line6);
        const unsigned char *buf = variax->line6.buffer_message;
 
        switch (buf[0]) {
@@ -182,11 +124,19 @@ static void line6_variax_process_message(struct usb_line6 *line6)
        case LINE6_SYSEX_BEGIN:
                if (memcmp(buf + 1, variax_init_version + 1,
                           sizeof(variax_init_version) - 1) == 0) {
-                       variax_startup3(variax);
+                       if (variax->startup_progress >= VARIAX_STARTUP_ACTIVATE)
+                               break;
+                       variax->startup_progress = VARIAX_STARTUP_ACTIVATE;
+                       cancel_delayed_work(&line6->startup_work);
+                       schedule_delayed_work(&line6->startup_work,
+                                             msecs_to_jiffies(VARIAX_STARTUP_DELAY3));
                } else if (memcmp(buf + 1, variax_init_done + 1,
                                  sizeof(variax_init_done) - 1) == 0) {
                        /* notify of complete initialization: */
-                       variax_startup4(&variax->startup_timer2);
+                       if (variax->startup_progress >= VARIAX_STARTUP_SETUP)
+                               break;
+                       cancel_delayed_work(&line6->startup_work);
+                       schedule_delayed_work(&line6->startup_work, 0);
                }
                break;
        }
@@ -197,11 +147,7 @@ static void line6_variax_process_message(struct usb_line6 *line6)
 */
 static void line6_variax_disconnect(struct usb_line6 *line6)
 {
-       struct usb_line6_variax *variax = (struct usb_line6_variax *)line6;
-
-       del_timer(&variax->startup_timer1);
-       del_timer(&variax->startup_timer2);
-       cancel_work_sync(&variax->startup_work);
+       struct usb_line6_variax *variax = line6_to_variax(line6);
 
        kfree(variax->buffer_activate);
 }
@@ -212,15 +158,12 @@ static void line6_variax_disconnect(struct usb_line6 *line6)
 static int variax_init(struct usb_line6 *line6,
                       const struct usb_device_id *id)
 {
-       struct usb_line6_variax *variax = (struct usb_line6_variax *) line6;
+       struct usb_line6_variax *variax = line6_to_variax(line6);
        int err;
 
        line6->process_message = line6_variax_process_message;
        line6->disconnect = line6_variax_disconnect;
-
-       timer_setup(&variax->startup_timer1, NULL, 0);
-       timer_setup(&variax->startup_timer2, NULL, 0);
-       INIT_WORK(&variax->startup_work, variax_startup6);
+       line6->startup = variax_startup;
 
        /* initialize USB buffers: */
        variax->buffer_activate = kmemdup(variax_activate,
@@ -235,7 +178,8 @@ static int variax_init(struct usb_line6 *line6,
                return err;
 
        /* initiate startup procedure: */
-       variax_startup1(variax);
+       schedule_delayed_work(&line6->startup_work,
+                             msecs_to_jiffies(VARIAX_STARTUP_DELAY1));
        return 0;
 }
 
index c703f8534b0735bdcd9395a0ff5112d79a3db9c7..7498b5191b68e4cf0368599998160ec0eb17f29d 100644 (file)
@@ -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;
@@ -2320,6 +2320,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
        static struct procunit_info default_info = {
                0, NULL, default_value_info
        };
+       const char *name = extension_unit ?
+               "Extension Unit" : "Processing Unit";
 
        if (desc->bLength < 13) {
                usb_audio_err(state->chip, "invalid %s descriptor (id %d)\n", name, unitid);
@@ -2433,7 +2435,10 @@ static int build_audio_procunit(struct mixer_build *state, int unitid,
                } else if (info->name) {
                        strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name));
                } else {
-                       nameid = uac_processing_unit_iProcessing(desc, state->mixer->protocol);
+                       if (extension_unit)
+                               nameid = uac_extension_unit_iExtension(desc, state->mixer->protocol);
+                       else
+                               nameid = uac_processing_unit_iProcessing(desc, state->mixer->protocol);
                        len = 0;
                        if (nameid)
                                len = snd_usb_copy_string_desc(state->chip,
@@ -2466,10 +2471,10 @@ static int parse_audio_processing_unit(struct mixer_build *state, int unitid,
        case UAC_VERSION_2:
        default:
                return build_audio_procunit(state, unitid, raw_desc,
-                               procunits, "Processing Unit");
+                                           procunits, false);
        case UAC_VERSION_3:
                return build_audio_procunit(state, unitid, raw_desc,
-                               uac3_procunits, "Processing Unit");
+                                           uac3_procunits, false);
        }
 }
 
@@ -2480,8 +2485,7 @@ static int parse_audio_extension_unit(struct mixer_build *state, int unitid,
         * Note that we parse extension units with processing unit descriptors.
         * That's ok as the layout is the same.
         */
-       return build_audio_procunit(state, unitid, raw_desc,
-                                   extunits, "Extension Unit");
+       return build_audio_procunit(state, unitid, raw_desc, extunits, true);
 }
 
 /*
index 1f6011f36bb063108e1e49dc60ef5872e7938f1d..199fa157a411e4731fa56e477f0953b2c8fd68ea 100644 (file)
@@ -741,7 +741,7 @@ static int snd_ni_control_init_val(struct usb_mixer_interface *mixer,
                return err;
        }
 
-       kctl->private_value |= (value << 24);
+       kctl->private_value |= ((unsigned int)value << 24);
        return 0;
 }
 
@@ -902,7 +902,7 @@ static int snd_ftu_eff_switch_init(struct usb_mixer_interface *mixer,
        if (err < 0)
                return err;
 
-       kctl->private_value |= value[0] << 24;
+       kctl->private_value |= (unsigned int)value[0] << 24;
        return 0;
 }
 
index 9e049f60e80e35a9c02bf4d29e10d4e9ce3d524e..e918ce34602723137b90f0f608b42be213c909c1 100644 (file)
@@ -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) {
index cf5cff10c08e8897fa87a0ba26f7eb62bd434481..78858918cbc103daac493fc8fc8c3cb21bf291a8 100644 (file)
@@ -828,11 +828,13 @@ static int snd_usb_novation_boot_quirk(struct usb_device *dev)
 static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev)
 {
        int err, actual_length;
-
        /* "midi send" enable */
        static const u8 seq[] = { 0x4e, 0x73, 0x52, 0x01 };
+       void *buf;
 
-       void *buf = kmemdup(seq, ARRAY_SIZE(seq), GFP_KERNEL);
+       if (snd_usb_pipe_sanity_check(dev, usb_sndintpipe(dev, 0x05)))
+               return -EINVAL;
+       buf = kmemdup(seq, ARRAY_SIZE(seq), GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
        err = usb_interrupt_msg(dev, usb_sndintpipe(dev, 0x05), buf,
@@ -857,7 +859,11 @@ static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev)
 
 static int snd_usb_nativeinstruments_boot_quirk(struct usb_device *dev)
 {
-       int ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+       int ret;
+
+       if (snd_usb_pipe_sanity_check(dev, usb_sndctrlpipe(dev, 0)))
+               return -EINVAL;
+       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                                  0xaf, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                                  1, 0, NULL, 0, 1000);
 
@@ -964,6 +970,8 @@ static int snd_usb_axefx3_boot_quirk(struct usb_device *dev)
 
        dev_dbg(&dev->dev, "Waiting for Axe-Fx III to boot up...\n");
 
+       if (snd_usb_pipe_sanity_check(dev, usb_sndctrlpipe(dev, 0)))
+               return -EINVAL;
        /* If the Axe-Fx III has not fully booted, it will timeout when trying
         * to enable the audio streaming interface. A more generous timeout is
         * used here to detect when the Axe-Fx III has finished booting as the
@@ -996,6 +1004,8 @@ static int snd_usb_motu_microbookii_communicate(struct usb_device *dev, u8 *buf,
 {
        int err, actual_length;
 
+       if (snd_usb_pipe_sanity_check(dev, usb_sndintpipe(dev, 0x01)))
+               return -EINVAL;
        err = usb_interrupt_msg(dev, usb_sndintpipe(dev, 0x01), buf, *length,
                                &actual_length, 1000);
        if (err < 0)
@@ -1006,6 +1016,8 @@ static int snd_usb_motu_microbookii_communicate(struct usb_device *dev, u8 *buf,
 
        memset(buf, 0, buf_size);
 
+       if (snd_usb_pipe_sanity_check(dev, usb_rcvintpipe(dev, 0x82)))
+               return -EINVAL;
        err = usb_interrupt_msg(dev, usb_rcvintpipe(dev, 0x82), buf, buf_size,
                                &actual_length, 1000);
        if (err < 0)
index b14ab512c2ce0d4ef2aceae5d673e6b528e1120c..e01631959ed8930e5051f8ea91c752488d851ff1 100644 (file)
@@ -196,7 +196,7 @@ static u64 to_sndif_formats_mask(u64 alsa_formats)
        mask = 0;
        for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
                if (pcm_format_to_bits(ALSA_SNDIF_FORMATS[i].alsa) & alsa_formats)
-                       mask |= 1 << ALSA_SNDIF_FORMATS[i].sndif;
+                       mask |= BIT_ULL(ALSA_SNDIF_FORMATS[i].sndif);
 
        return mask;
 }
@@ -208,7 +208,7 @@ static u64 to_alsa_formats_mask(u64 sndif_formats)
 
        mask = 0;
        for (i = 0; i < ARRAY_SIZE(ALSA_SNDIF_FORMATS); i++)
-               if (1 << ALSA_SNDIF_FORMATS[i].sndif & sndif_formats)
+               if (BIT_ULL(ALSA_SNDIF_FORMATS[i].sndif) & sndif_formats)
                        mask |= pcm_format_to_bits(ALSA_SNDIF_FORMATS[i].alsa);
 
        return mask;