Merge branch 'for-linus' into for-next
authorTakashi Iwai <tiwai@suse.de>
Mon, 1 Jul 2019 15:01:00 +0000 (17:01 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 1 Jul 2019 15:01:55 +0000 (17:01 +0200)
This back-merge is necessary for adjusting the latest FireWire fix
with the recent refactoring in 5.3 development branch.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
73 files changed:
include/sound/hda_codec.h
include/sound/hdaudio.h
sound/core/control.c
sound/core/oss/rate.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/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/lx6464es/lx_core.c
sound/pci/rme9652/hdspm.c
sound/soc/sof/intel/hda.c
sound/usb/helper.c
sound/usb/helper.h
sound/usb/line6/driver.c
sound/usb/line6/driver.h
sound/usb/line6/pod.c
sound/usb/line6/podhd.c
sound/usb/line6/toneport.c
sound/usb/line6/variax.c
sound/usb/quirks-table.h
sound/usb/quirks.c
sound/xen/xen_snd_front_alsa.c

index cc7c8d42d4fd9a8a494f6b0cf59d48a8fbf2f902..2a1d1ad44d661ac8381971b6c7780d58eecbd3aa 100644 (file)
@@ -281,9 +281,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..f49af557bdb0e8df308085901d12ffbb48171c95 100644 (file)
@@ -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 a5cc9a874062c4a235e1f6909e05a9729c78f166..04eb1a15ffb401bc83f172939dcd60977389cee2 100644 (file)
@@ -211,16 +211,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 d09da9dbf235fef572be31beeb772ded6675129f..623d014c0e7ebb6c4aa36d9d137e22742b9a87fc 100644 (file)
@@ -83,7 +83,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;
@@ -321,7 +321,7 @@ static void read_midi_messages(struct amdtp_stream *s,
        u8 *b;
 
        for (f = 0; f < frames; f++) {
-               port = (8 - s->tx_first_dbc + 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 ac20acf48fc6912190df52dcecaa3fca801f5fc5..ab708857979fc7a618f81fc20c45bb6d2ff41ee1 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 43f28b813386e7b9f90273fb82da0b4b6ad012d0..91b8902418400f886afcd6a7bf797cb6f765af99 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 df1b1e94c43c94b82044a55801a0846efac6c1b3..20ed2dbd8d6121f0c515d3fee1d71173a9999285 100644 (file)
@@ -93,8 +93,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;
@@ -218,7 +216,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 3befa3eca6ef8068c4b27ede8257d293d259332f..f7db1f7bce861ec0296f61574f70b79446a840b5 100644 (file)
@@ -8,58 +8,29 @@
 
 #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);
+       }
        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;
 
@@ -121,13 +92,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 ea9b8645058072c03e6f8f654088282146f9e893..e21de44deaa91682015c814e72551c6b58fd5b48 100644 (file)
@@ -185,9 +185,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;
@@ -198,62 +197,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);
 }
 
@@ -261,10 +229,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);
 
@@ -274,10 +241,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);
 
@@ -354,8 +320,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,
@@ -366,8 +332,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 4d3034a68bdfbd587163566484eea57804260eb7..63e78fc8711de81e41d40a0237bae6242a497a8c 100644 (file)
@@ -376,24 +376,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)
 {
@@ -418,49 +400,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
@@ -469,23 +423,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;
@@ -510,190 +454,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);
        }
 }
 
@@ -703,10 +709,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 ae3bc1940efa6811b3bb5ec28d7a5999e1263b89..5dedc4f31842d3902bb7dc2745ef31459d2af2d1 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 37062a233f6a461835b60614902fba2de92bd724..7efca3648c6afc4a7285907edf43b0d228d65a5a 100644 (file)
@@ -1,4 +1,4 @@
 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 84eca8a51a02fa9057a2c3153ad4d9f3c29b3a29..436f0c3c0fbba75ee5cedac70bece1bcb56dff3d 100644 (file)
@@ -18,8 +18,11 @@ 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);
+       }
 
        mutex_unlock(&dice->mutex);
 
@@ -35,7 +38,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 bb3ef5ff3488c83082206aaf497eff4cffc44722..8368073f7fa0a483d82bed4a4a2a53a24f7275b5 100644 (file)
@@ -231,8 +231,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;
@@ -243,57 +243,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);
 
@@ -309,7 +278,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);
@@ -323,7 +292,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);
@@ -405,8 +374,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,
@@ -417,8 +386,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 c3c892c5c7ff1644a5161a2739b79dd1404cefe7..433714a117a0b77f020d8640d717e2d0067433e0 100644 (file)
@@ -138,18 +138,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]);
        }
@@ -164,10 +155,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));
@@ -175,35 +170,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;
@@ -230,40 +212,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,
@@ -275,7 +257,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",
@@ -283,141 +265,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;
@@ -429,12 +440,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;
 }
 
 /*
@@ -446,17 +485,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 eee184b05d937f093a641137bce9ad2b1f6eec69..ac600e061d7bd60f3e77c3b007e7019e78b7b0a5 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 83353a3559e89f4b8da80e9995ff49604eba78b2..fd3f483283d5018817bf0649218c62be4155857b 100644 (file)
@@ -205,10 +205,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);
 
@@ -227,5 +228,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 4a884a33524809a23ba6a07e8319711f76771081..3fb1997dca30d6a160baaf13fad2215d4e03d317 100644 (file)
@@ -128,7 +128,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 7ab3d0810f6b4356ce8280da158241ba74f6f89a..ca06ad318ed6d334b5f32d1634d9e3fc1498fbbe 100644 (file)
@@ -18,8 +18,11 @@ 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);
+       }
        mutex_unlock(&dg00x->mutex);
        if (err < 0)
                snd_dg00x_stream_lock_release(dg00x);
@@ -32,7 +35,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 fdcff0460c5304bae838dadc85f3a6f1eddf2611..c38fbd6ded9f0ff31891b6061b2b894dce1e3b41 100644 (file)
@@ -155,8 +155,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;
@@ -167,58 +167,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);
 
@@ -230,12 +198,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);
 
@@ -247,12 +214,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);
@@ -333,8 +299,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,
@@ -345,8 +311,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 4d3b4ebbdd49141291394485bb1821ca3320e422..7c8e7ad48d03cf5dcc939d79498cd2ba795c06f8 100644 (file)
@@ -125,11 +125,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)
@@ -138,11 +152,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)
@@ -157,39 +180,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;
@@ -197,41 +204,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)
@@ -273,43 +258,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;
@@ -344,33 +354,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 4dd1bbf2ed3c58ecef5d28fb48d4ad3cda0b0620..2d026b5b00797c0a6c9b43760da6775dc26b9342 100644 (file)
@@ -140,7 +140,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 5adf04b95c04cf27091edbe200ac85781f314dca..75228e769687a9b0be1586bbedd1d73a18a20c4b 100644 (file)
@@ -199,8 +199,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;
@@ -211,58 +211,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);
 
@@ -375,8 +343,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,
@@ -387,8 +355,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 a8a90f1ae09e218ac13a976dd259b66342a01ce3..049920a6d43ed631b8596b7e90ac78fc8ca163e4 100644 (file)
@@ -31,14 +31,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);
 }
@@ -104,37 +101,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;
@@ -156,6 +141,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;
@@ -193,37 +202,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 ed8fea0ff5e182ed109ba65ec9c536cecbffd445..9b52c368f6de4037cf3c0a0ae73d2fc468e26039 100644 (file)
@@ -113,6 +113,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);
@@ -137,6 +138,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 9b19c7f05d57916d399c9e6bc49b2120a9d0ee7f..0c1802aa79232f704057ca468b833827dfaed979 100644 (file)
@@ -89,8 +89,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;
@@ -207,7 +206,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 f5da2cd4ce4219ab7a78519600cdc6dca168f0ff..6d3d942e2dced14b0c6384aec2d844aa49486732 100644 (file)
@@ -8,7 +8,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;
@@ -18,28 +18,11 @@ 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);
+       }
        mutex_unlock(&efw->mutex);
        if (err < 0)
                snd_efw_stream_lock_release(efw);
@@ -47,25 +30,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);
 
@@ -121,13 +91,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 aed566d827266602707ef397a1cfb4d089ec9bff..8dc34249a1b0481c43583c399039de7babdd69b7 100644 (file)
@@ -219,7 +219,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;
@@ -231,69 +231,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);
 
@@ -302,10 +273,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);
 
@@ -378,8 +348,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,
@@ -390,8 +360,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 827161bc269cfa1414972692bcdb45d0a42ad9ed..16cf635a6f5767d23759720d7e61e7164748ce5c 100644 (file)
@@ -43,7 +43,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)
@@ -52,54 +51,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;
 }
 
 /*
@@ -165,13 +147,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) {
@@ -189,75 +171,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 cd0cbfa9f96f3d25c96a82cc27c069fecf21fabb..edc551d4ca500666b902c178b9823ab43577c994 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 cb0c967dea6318e1a246a60f56f7fac993304bd6..1c9ce04a2e899f61ae37c1d868d94650107058fc 100644 (file)
@@ -306,8 +306,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);
@@ -384,8 +384,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;
 }
@@ -429,7 +429,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 e55cab6d79c7df91d72b6837def3a1bdb1b6bdca..4520c0060b8374726c60429c716bb6b869c76dd7 100644 (file)
@@ -7,7 +7,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;
@@ -18,30 +18,11 @@ 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);
+       }
 
        mutex_unlock(&motu->mutex);
 
@@ -51,28 +32,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);
@@ -129,13 +95,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 ab69d7e6ac0560a4bc7f4b0c557945541cbbd083..60c691d189524b7ec6bd389d78d803a12ede65c4 100644 (file)
@@ -190,8 +190,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;
@@ -202,57 +202,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);
 
@@ -267,7 +236,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);
@@ -280,7 +249,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);
@@ -356,8 +325,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,
@@ -368,8 +337,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 483a8771d5020abe68a5ea688446782ce37685a8..6744d62623158490fb8fa346ca9034d4458f745b 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)
@@ -84,7 +83,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;
@@ -94,6 +93,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)
@@ -106,9 +108,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)
@@ -126,28 +125,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;
@@ -175,6 +158,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;
@@ -201,55 +226,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);
@@ -263,7 +267,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);
@@ -271,13 +275,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;
                }
        }
@@ -285,21 +287,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);
        }
 }
 
@@ -372,8 +370,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 1cd112be7dadd6b12989c04f1d3a25267b0eca62..a4ac320f2c9ef976e71689bcece8d323906def36 100644 (file)
@@ -60,8 +60,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;
@@ -154,7 +153,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 b7bbd77dfff179581120489a2d85a2127fd7773c..9ba62778add29b1d91fd6b0ff663bf397ceb3c46 100644 (file)
@@ -19,8 +19,11 @@ 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);
+       }
 
        mutex_unlock(&oxfw->mutex);
 
@@ -41,8 +44,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);
 
@@ -58,8 +64,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);
 
@@ -73,8 +79,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 b3f6503dd34dd04ee090a435ae6c117600a71285..b08b850d53ea7aa276f2b66c7a1d993ca61ac6a5 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 f230a9e44c3c0f65f5ece0c73ae774a7ac5cfe77..a7502810a3ad8563df0665b6ee88cd11e62217bd 100644 (file)
@@ -101,85 +101,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,
@@ -206,8 +155,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;
@@ -226,13 +174,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;
        }
 
        /*
@@ -246,115 +193,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;
 
@@ -367,20 +394,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 b4bef574929d82df32b86183f5161819e2c67b08..11a4e1581381a16ea82a4b9786eacee10416374a 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 d54d4a9ac4a159192494c7f957517cf9099e7004..d4d4926c28cfe96f0077e36e7fe3826b732048b9 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 a52d1f76c610fbed8003e8195d60a61251e8dc45..1cf0f9470449d014510e4b28d604b129e987e9eb 100644 (file)
@@ -224,7 +224,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 e4cc8990e1953c2c550ebd591a2cff434097adea..300683a82c68849f13c442f7f61fb4017f3cac07 100644 (file)
@@ -84,8 +84,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;
@@ -96,58 +96,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);
 
@@ -260,8 +228,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,
@@ -272,8 +240,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 f1657a4e0621ef4999349477ce6e71fdbcd7f411..0e515b7be2766c8bc6f1d884de8151157604aaf4 100644 (file)
@@ -166,7 +166,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,
@@ -174,17 +174,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,
@@ -195,6 +194,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)
@@ -202,6 +214,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,
@@ -216,7 +252,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,
@@ -224,7 +260,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,
@@ -232,82 +268,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)
@@ -346,7 +324,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);
@@ -369,33 +347,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;
@@ -433,25 +440,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 6a411ee0dcf1ab60f6e701e025ed0b7bfda19ded..4ed88cceaedbe525282d4e3eae71fc49faa70137 100644 (file)
@@ -147,6 +147,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 b2e9454f5816a042c8218e0d76b770277e6eeedb..c24fc8d266a91d4cd3cb0a8d7395157ed522f67a 100644 (file)
@@ -78,6 +78,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);
@@ -240,6 +242,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 */
@@ -414,9 +418,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 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 fcdf2cd3783b5c19d1c4ce6c426bc794c6bc0b28..ccb53f1c1e1a3553f9ab96fa2bf8a12d80362363 100644 (file)
@@ -122,7 +122,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;
index 532e081f8b8a243d3fcb1199227ef1883ab76bf6..53feaeef15531419649752f5fa3135071d5d6bc6 100644 (file)
@@ -806,11 +806,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);
@@ -830,21 +830,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 7185ed574b412fc8786a2ca15316347925e3c1d7..8d886791cf0fc1508b7a20c22fbc2403cf440a34 100644 (file)
@@ -142,11 +142,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 a4b0414dda3bd914482a96554ae4e385be42c622..922c9b9301244da9cb03b9aa86c10bb26996ec64 100644 (file)
@@ -1701,10 +1701,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);
@@ -1712,6 +1708,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;
@@ -2388,6 +2388,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 74b46952fc987441b59f86ba06efce8d1b507ead..60a548513c9db191de26e387a56b8a1961826bdc 100644 (file)
@@ -563,7 +563,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 e1ebc6d5f38226b10f689b2bc04fd0504331ced2..de61179c91e2cdcc95de01fba569ea1c692f9488 100644 (file)
@@ -2731,7 +2731,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(
index 0c61c05503f5ee61c551c38b80556582ec7dca6e..b522314ec5bed158d3a02b59fb24272885db885a 100644 (file)
@@ -1627,7 +1627,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 a80684bdc30d6ee3cdad09e6e99be4cef902b7c3..36116881cf52571628c1ab50a44cadb447f1afb1 100644 (file)
@@ -1001,8 +1001,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;
 
@@ -1025,9 +1023,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 1209cf0b05e00a677b66694e57c770a68d1b973b..982b297b3d0a8ec77aed4ae3e1c04b83938150e8 100644 (file)
@@ -23,6 +23,9 @@
  *
  *     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
+ *
  *   This program is free software; you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
  *   the Free Software Foundation; either version 2 of the License, or
@@ -1105,9 +1108,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);
@@ -5588,11 +5591,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;
@@ -5600,11 +5608,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;
@@ -5665,19 +5678,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);
@@ -6416,17 +6427,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 faf1a8ada091fddc7352a9b7ac107ab7a80a8c9d..6811a477d0fbddaa30fd4c14dbbb7562a0ce4783 100644 (file)
@@ -32,6 +32,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
  */
@@ -217,6 +220,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 7712e2b84183891336469d65b861cba0beb145e3..b1cc9499c57e1c59b88212a81c99f1a1e6905200 100644 (file)
@@ -76,6 +76,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.
@@ -88,6 +102,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 2b57854335b3ba93c4c1e9ae551f291b97ab2002..79e96b269411dcc6474dc087156e504b38fc64b0 100644 (file)
@@ -195,17 +195,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 650d909c9c4ff86a41a4a61f4cd7528e1748514d..4eb66cdf1ece8de75af1dac49b70f05596fafb01 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;
@@ -201,8 +194,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 ce45b6dab651718b6da3ee24f169063d2cadae85..9ea720b4b2ab4743af166f825c081cf6fe70d2e9 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 {
@@ -63,12 +61,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;
 
@@ -82,6 +74,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* */
@@ -173,10 +167,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)
 {
@@ -189,14 +179,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;
        }
 
@@ -281,47 +274,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: */
@@ -357,7 +330,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;
@@ -368,7 +341,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;
@@ -390,17 +363,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.
 */
@@ -408,13 +370,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);
@@ -447,7 +406,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 5f3c87264e66776049f436b45ce7e44f368db4e8..395ae1692f4573411ce552c7efd6dd957a06b78e 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,
@@ -47,15 +37,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;
 
@@ -63,6 +44,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,
@@ -158,10 +141,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)
 {
@@ -202,26 +181,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;
@@ -272,37 +231,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)
@@ -317,13 +262,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 */
@@ -362,11 +305,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 55865f7e437dc4b6646b463550446e0fa13ccbc6..94a9764110d36dbd989b033fbdf96a6a4fffd056 100644 (file)
@@ -61,6 +61,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
@@ -211,8 +213,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;
 }
@@ -222,8 +224,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];
@@ -397,8 +398,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);
@@ -412,7 +412,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 e8c852b2ce3502b8c24ba83ae9eacfb6519c84f8..0d0de907d497a25174d88ada5d1fed0da6668d11 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 {
@@ -47,17 +43,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
 
 /*
@@ -81,11 +72,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;
@@ -100,74 +86,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;
+       }
 }
 
 /*
@@ -175,7 +117,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]) {
@@ -186,11 +128,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;
        }
@@ -201,11 +151,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);
 }
@@ -216,15 +162,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,
@@ -239,7 +182,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 5600143ff66020dc1d4f24e2b14dafb965b928f3..5fd748c4eb30e6d5992fc2f46f8c01cd9b859e1a 100644 (file)
@@ -2422,7 +2422,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 e6ce1bbe6ca6ef1af81a1c135a10a1a0ce95fd93..057143330a281002bf9a2fc8bbb62e39b07daf21 100644 (file)
@@ -840,11 +840,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,
@@ -869,7 +871,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);
 
@@ -976,6 +982,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
@@ -1008,6 +1016,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)
@@ -1018,6 +1028,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;