aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/sound/firewire/motu
diff options
context:
space:
mode:
Diffstat (limited to 'sound/firewire/motu')
-rw-r--r--sound/firewire/motu/Makefile4
-rw-r--r--sound/firewire/motu/amdtp-motu.c199
-rw-r--r--sound/firewire/motu/motu-command-dsp-message-parser.c184
-rw-r--r--sound/firewire/motu/motu-hwdep.c125
-rw-r--r--sound/firewire/motu/motu-midi.c4
-rw-r--r--sound/firewire/motu/motu-pcm.c4
-rw-r--r--sound/firewire/motu/motu-protocol-v1.c467
-rw-r--r--sound/firewire/motu/motu-protocol-v2.c209
-rw-r--r--sound/firewire/motu/motu-protocol-v3.c142
-rw-r--r--sound/firewire/motu/motu-register-dsp-message-parser.c423
-rw-r--r--sound/firewire/motu/motu-stream.c44
-rw-r--r--sound/firewire/motu/motu.c108
-rw-r--r--sound/firewire/motu/motu.h74
13 files changed, 1613 insertions, 374 deletions
diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
index 7c502d35103c..3bef2a0b1e2e 100644
--- a/sound/firewire/motu/Makefile
+++ b/sound/firewire/motu/Makefile
@@ -3,5 +3,7 @@ CFLAGS_amdtp-motu.o := -I$(src)
snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \
motu-proc.o motu-pcm.o motu-midi.o motu-hwdep.o \
- motu-protocol-v2.o motu-protocol-v3.o
+ motu-protocol-v2.o motu-protocol-v3.o \
+ motu-protocol-v1.o motu-register-dsp-message-parser.o \
+ motu-command-dsp-message-parser.o
obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c
index edb31ac26868..39ed57d2c5a0 100644
--- a/sound/firewire/motu/amdtp-motu.c
+++ b/sound/firewire/motu/amdtp-motu.c
@@ -16,6 +16,14 @@
#define CIP_FMT_MOTU_TX_V3 0x22
#define MOTU_FDF_AM824 0x22
+#define TICKS_PER_CYCLE 3072
+#define CYCLES_PER_SECOND 8000
+#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
+
+#define CIP_SPH_CYCLE_SHIFT 12
+#define CIP_SPH_CYCLE_MASK 0x01fff000
+#define CIP_SPH_OFFSET_MASK 0x00000fff
+
/*
* Nominally 3125 bytes/second, but the MIDI port's clock might be
* 1% too slow, and the bus clock 100 ppm too fast.
@@ -23,14 +31,6 @@
#define MIDI_BYTES_PER_SECOND 3093
struct amdtp_motu {
- /* For timestamp processing. */
- unsigned int quotient_ticks_per_event;
- unsigned int remainder_ticks_per_event;
- unsigned int next_ticks;
- unsigned int next_accumulated;
- unsigned int next_cycles;
- unsigned int next_seconds;
-
unsigned int pcm_chunks;
unsigned int pcm_byte_offset;
@@ -41,26 +41,16 @@ struct amdtp_motu {
int midi_db_count;
unsigned int midi_db_interval;
+
+ struct amdtp_motu_cache *cache;
};
int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
unsigned int midi_ports,
struct snd_motu_packet_format *formats)
{
- static const struct {
- unsigned int quotient_ticks_per_event;
- unsigned int remainder_ticks_per_event;
- } params[] = {
- [CIP_SFC_44100] = { 557, 123 },
- [CIP_SFC_48000] = { 512, 0 },
- [CIP_SFC_88200] = { 278, 282 },
- [CIP_SFC_96000] = { 256, 0 },
- [CIP_SFC_176400] = { 139, 141 },
- [CIP_SFC_192000] = { 128, 0 },
- };
struct amdtp_motu *p = s->protocol;
unsigned int pcm_chunks, data_chunks, data_block_quadlets;
- unsigned int delay;
unsigned int mode;
int i, err;
@@ -83,7 +73,7 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
data_chunks = formats->msg_chunks + pcm_chunks;
data_block_quadlets = 1 + DIV_ROUND_UP(data_chunks * 3, 4);
- err = amdtp_stream_set_parameters(s, rate, data_block_quadlets);
+ err = amdtp_stream_set_parameters(s, rate, data_block_quadlets, 1);
if (err < 0)
return err;
@@ -97,19 +87,6 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
p->midi_db_count = 0;
p->midi_db_interval = rate / MIDI_BYTES_PER_SECOND;
- /* IEEE 1394 bus requires. */
- delay = 0x2e00;
-
- /* For no-data or empty packets to adjust PCM sampling frequency. */
- delay += 8000 * 3072 * s->syt_interval / rate;
-
- p->next_seconds = 0;
- p->next_cycles = delay / 3072;
- p->quotient_ticks_per_event = params[s->sfc].quotient_ticks_per_event;
- p->remainder_ticks_per_event = params[s->sfc].remainder_ticks_per_event;
- p->next_ticks = delay % 3072;
- p->next_accumulated = 0;
-
return 0;
}
@@ -299,44 +276,77 @@ static void __maybe_unused copy_message(u64 *frames, __be32 *buffer,
/* This is just for v2/v3 protocol. */
for (i = 0; i < data_blocks; ++i) {
- *frames = (be32_to_cpu(buffer[1]) << 16) |
- (be32_to_cpu(buffer[2]) >> 16);
+ *frames = be32_to_cpu(buffer[1]);
+ *frames <<= 16;
+ *frames |= be32_to_cpu(buffer[2]) >> 16;
+ ++frames;
buffer += data_block_quadlets;
- frames++;
}
}
-static void probe_tracepoints_events(struct amdtp_stream *s,
- const struct pkt_desc *descs,
- unsigned int packets)
+static void probe_tracepoints_events(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count)
{
int i;
- for (i = 0; i < packets; ++i) {
- const struct pkt_desc *desc = descs + i;
+ for (i = 0; i < count; ++i) {
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
trace_data_block_sph(s, data_blocks, buf);
trace_data_block_message(s, data_blocks, buf);
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
}
}
-static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
- const struct pkt_desc *descs,
- unsigned int packets,
- struct snd_pcm_substream *pcm)
+static void cache_event_offsets(struct amdtp_motu_cache *cache, const __be32 *buf,
+ unsigned int data_blocks, unsigned int data_block_quadlets)
{
+ unsigned int *event_offsets = cache->event_offsets;
+ const unsigned int cache_size = cache->size;
+ unsigned int cache_tail = cache->tail;
+ unsigned int base_tick = cache->tx_cycle_count * TICKS_PER_CYCLE;
+ int i;
+
+ for (i = 0; i < data_blocks; ++i) {
+ u32 sph = be32_to_cpu(*buf);
+ unsigned int tick;
+
+ tick = ((sph & CIP_SPH_CYCLE_MASK) >> CIP_SPH_CYCLE_SHIFT) * TICKS_PER_CYCLE +
+ (sph & CIP_SPH_OFFSET_MASK);
+
+ if (tick < base_tick)
+ tick += TICKS_PER_SECOND;
+ event_offsets[cache_tail] = tick - base_tick;
+
+ cache_tail = (cache_tail + 1) % cache_size;
+ buf += data_block_quadlets;
+ }
+
+ cache->tail = cache_tail;
+ cache->tx_cycle_count = (cache->tx_cycle_count + 1) % CYCLES_PER_SECOND;
+}
+
+static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
+{
+ struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream);
struct amdtp_motu *p = s->protocol;
+ const struct pkt_desc *cursor = desc;
unsigned int pcm_frames = 0;
int i;
+ if (p->cache->tx_cycle_count == UINT_MAX)
+ p->cache->tx_cycle_count = (s->domain->processing_cycle.tx_start % CYCLES_PER_SECOND);
+
// For data block processing.
- for (i = 0; i < packets; ++i) {
- const struct pkt_desc *desc = descs + i;
+ for (i = 0; i < count; ++i) {
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
+ cache_event_offsets(p->cache, buf, data_blocks, s->data_block_quadlets);
+
if (pcm) {
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks;
@@ -344,70 +354,57 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
if (p->midi_ports)
read_midi_messages(s, buf, data_blocks);
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
}
+ desc = cursor;
+ if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP)
+ snd_motu_register_dsp_message_parser_parse(s, desc, count);
+ else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP)
+ snd_motu_command_dsp_message_parser_parse(s, desc, count);
+
// For tracepoints.
if (trace_data_block_sph_enabled() ||
trace_data_block_message_enabled())
- probe_tracepoints_events(s, descs, packets);
-
- return pcm_frames;
-}
-
-static inline void compute_next_elapse_from_start(struct amdtp_motu *p)
-{
- p->next_accumulated += p->remainder_ticks_per_event;
- if (p->next_accumulated >= 441) {
- p->next_accumulated -= 441;
- p->next_ticks++;
- }
-
- p->next_ticks += p->quotient_ticks_per_event;
- if (p->next_ticks >= 3072) {
- p->next_ticks -= 3072;
- p->next_cycles++;
- }
-
- if (p->next_cycles >= 8000) {
- p->next_cycles -= 8000;
- p->next_seconds++;
- }
-
- if (p->next_seconds >= 128)
- p->next_seconds -= 128;
+ probe_tracepoints_events(s, desc, count);
}
-static void write_sph(struct amdtp_stream *s, __be32 *buffer,
- unsigned int data_blocks)
+static void write_sph(struct amdtp_motu_cache *cache, __be32 *buffer, unsigned int data_blocks,
+ unsigned int data_block_quadlets)
{
- struct amdtp_motu *p = s->protocol;
- unsigned int next_cycles;
- unsigned int i;
- u32 sph;
+ unsigned int *event_offsets = cache->event_offsets;
+ const unsigned int cache_size = cache->size;
+ unsigned int cache_head = cache->head;
+ unsigned int base_tick = cache->rx_cycle_count * TICKS_PER_CYCLE;
+ int i;
for (i = 0; i < data_blocks; i++) {
- next_cycles = (s->start_cycle + p->next_cycles) % 8000;
- sph = ((next_cycles << 12) | p->next_ticks) & 0x01ffffff;
+ unsigned int tick = (base_tick + event_offsets[cache_head]) % TICKS_PER_SECOND;
+ u32 sph = ((tick / TICKS_PER_CYCLE) << CIP_SPH_CYCLE_SHIFT) | (tick % TICKS_PER_CYCLE);
*buffer = cpu_to_be32(sph);
- compute_next_elapse_from_start(p);
-
- buffer += s->data_block_quadlets;
+ cache_head = (cache_head + 1) % cache_size;
+ buffer += data_block_quadlets;
}
+
+ cache->head = cache_head;
+ cache->rx_cycle_count = (cache->rx_cycle_count + 1) % CYCLES_PER_SECOND;
}
-static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
- const struct pkt_desc *descs,
- unsigned int packets,
- struct snd_pcm_substream *pcm)
+static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
{
struct amdtp_motu *p = s->protocol;
+ const struct pkt_desc *cursor = desc;
unsigned int pcm_frames = 0;
int i;
+ if (p->cache->rx_cycle_count == UINT_MAX)
+ p->cache->rx_cycle_count = (s->domain->processing_cycle.rx_start % CYCLES_PER_SECOND);
+
// For data block processing.
- for (i = 0; i < packets; ++i) {
- const struct pkt_desc *desc = descs + i;
+ for (i = 0; i < count; ++i) {
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
@@ -421,26 +418,27 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
if (p->midi_ports)
write_midi_messages(s, buf, data_blocks);
- // TODO: how to interact control messages between userspace?
+ write_sph(p->cache, buf, data_blocks, s->data_block_quadlets);
- write_sph(s, buf, data_blocks);
+ desc = amdtp_stream_next_packet_desc(s, desc);
}
+ desc = cursor;
+
// For tracepoints.
if (trace_data_block_sph_enabled() ||
trace_data_block_message_enabled())
- probe_tracepoints_events(s, descs, packets);
-
- return pcm_frames;
+ probe_tracepoints_events(s, desc, count);
}
int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir,
- const struct snd_motu_spec *spec)
+ const struct snd_motu_spec *spec, struct amdtp_motu_cache *cache)
{
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
int fmt = CIP_FMT_MOTU;
- int flags = CIP_BLOCKING;
+ unsigned int flags = CIP_BLOCKING | CIP_UNAWARE_SYT;
+ struct amdtp_motu *p;
int err;
if (dir == AMDTP_IN_STREAM) {
@@ -478,9 +476,10 @@ int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
if (dir == AMDTP_OUT_STREAM) {
// Use fixed value for FDF field.
s->ctx_data.rx.fdf = MOTU_FDF_AM824;
- // Not used.
- s->ctx_data.rx.syt_override = 0xffff;
}
+ p = s->protocol;
+ p->cache = cache;
+
return 0;
}
diff --git a/sound/firewire/motu/motu-command-dsp-message-parser.c b/sound/firewire/motu/motu-command-dsp-message-parser.c
new file mode 100644
index 000000000000..5d8a86a12f1f
--- /dev/null
+++ b/sound/firewire/motu/motu-command-dsp-message-parser.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// motu-command-dsp-message-parser.c - a part of driver for MOTU FireWire series
+//
+// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+
+// Below models allow software to configure their DSP function by command transferred in
+// asynchronous transaction:
+// * 828 mk3 (FireWire only and Hybrid)
+// * 896 mk3 (FireWire only and Hybrid)
+// * Ultralite mk3 (FireWire only and Hybrid)
+// * Traveler mk3
+// * Track 16
+//
+// Isochronous packets from the above models includes messages to report state of hardware meter.
+
+#include "motu.h"
+
+enum msg_parser_state {
+ INITIALIZED,
+ FRAGMENT_DETECTED,
+ AVAILABLE,
+};
+
+struct msg_parser {
+ spinlock_t lock;
+ enum msg_parser_state state;
+ unsigned int interval;
+ unsigned int message_count;
+ unsigned int fragment_pos;
+ unsigned int value_index;
+ u64 value;
+ struct snd_firewire_motu_command_dsp_meter meter;
+};
+
+int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu)
+{
+ struct msg_parser *parser;
+
+ parser = devm_kzalloc(&motu->card->card_dev, sizeof(*parser), GFP_KERNEL);
+ if (!parser)
+ return -ENOMEM;
+ spin_lock_init(&parser->lock);
+ motu->message_parser = parser;
+
+ return 0;
+}
+
+int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc)
+{
+ struct msg_parser *parser = motu->message_parser;
+
+ parser->state = INITIALIZED;
+
+ // All of data blocks don't have messages with meaningful information.
+ switch (sfc) {
+ case CIP_SFC_176400:
+ case CIP_SFC_192000:
+ parser->interval = 4;
+ break;
+ case CIP_SFC_88200:
+ case CIP_SFC_96000:
+ parser->interval = 2;
+ break;
+ case CIP_SFC_32000:
+ case CIP_SFC_44100:
+ case CIP_SFC_48000:
+ default:
+ parser->interval = 1;
+ break;
+ }
+
+ return 0;
+}
+
+#define FRAGMENT_POS 6
+#define MIDI_BYTE_POS 7
+#define MIDI_FLAG_POS 8
+// One value of hardware meter consists of 4 messages.
+#define FRAGMENTS_PER_VALUE 4
+#define VALUES_AT_IMAGE_END 0xffffffffffffffff
+
+void snd_motu_command_dsp_message_parser_parse(const struct amdtp_stream *s,
+ const struct pkt_desc *desc, unsigned int count)
+{
+ struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream);
+ unsigned int data_block_quadlets = s->data_block_quadlets;
+ struct msg_parser *parser = motu->message_parser;
+ unsigned int interval = parser->interval;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&parser->lock, flags);
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buffer = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+ int j;
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+
+ for (j = 0; j < data_blocks; ++j) {
+ u8 *b = (u8 *)buffer;
+ buffer += data_block_quadlets;
+
+ switch (parser->state) {
+ case INITIALIZED:
+ {
+ u8 fragment = b[FRAGMENT_POS];
+
+ if (fragment > 0) {
+ parser->value = fragment;
+ parser->message_count = 1;
+ parser->state = FRAGMENT_DETECTED;
+ }
+ break;
+ }
+ case FRAGMENT_DETECTED:
+ {
+ if (parser->message_count % interval == 0) {
+ u8 fragment = b[FRAGMENT_POS];
+
+ parser->value >>= 8;
+ parser->value |= (u64)fragment << 56;
+
+ if (parser->value == VALUES_AT_IMAGE_END) {
+ parser->state = AVAILABLE;
+ parser->fragment_pos = 0;
+ parser->value_index = 0;
+ parser->message_count = 0;
+ }
+ }
+ ++parser->message_count;
+ break;
+ }
+ case AVAILABLE:
+ default:
+ {
+ if (parser->message_count % interval == 0) {
+ u8 fragment = b[FRAGMENT_POS];
+
+ parser->value >>= 8;
+ parser->value |= (u64)fragment << 56;
+ ++parser->fragment_pos;
+
+ if (parser->fragment_pos == 4) {
+ // Skip the last two quadlets since they could be
+ // invalid value (0xffffffff) as floating point
+ // number.
+ if (parser->value_index <
+ SNDRV_FIREWIRE_MOTU_COMMAND_DSP_METER_COUNT - 2) {
+ u32 val = (u32)(parser->value >> 32);
+ parser->meter.data[parser->value_index] = val;
+ }
+ ++parser->value_index;
+ parser->fragment_pos = 0;
+ }
+
+ if (parser->value == VALUES_AT_IMAGE_END) {
+ parser->value_index = 0;
+ parser->fragment_pos = 0;
+ parser->message_count = 0;
+ }
+ }
+ ++parser->message_count;
+ break;
+ }
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&parser->lock, flags);
+}
+
+void snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu,
+ struct snd_firewire_motu_command_dsp_meter *meter)
+{
+ struct msg_parser *parser = motu->message_parser;
+ unsigned long flags;
+
+ spin_lock_irqsave(&parser->lock, flags);
+ memcpy(meter, &parser->meter, sizeof(*meter));
+ spin_unlock_irqrestore(&parser->lock, flags);
+}
diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c
index 0764a477052a..88d1f4b56e4b 100644
--- a/sound/firewire/motu/motu-hwdep.c
+++ b/sound/firewire/motu/motu-hwdep.c
@@ -16,6 +16,14 @@
#include "motu.h"
+static bool has_dsp_event(struct snd_motu *motu)
+{
+ if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP)
+ return (snd_motu_register_dsp_message_parser_count_event(motu) > 0);
+ else
+ return false;
+}
+
static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
loff_t *offset)
{
@@ -25,7 +33,7 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
spin_lock_irq(&motu->lock);
- while (!motu->dev_lock_changed && motu->msg == 0) {
+ while (!motu->dev_lock_changed && motu->msg == 0 && !has_dsp_event(motu)) {
prepare_to_wait(&motu->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
spin_unlock_irq(&motu->lock);
schedule();
@@ -40,20 +48,50 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
event.lock_status.status = (motu->dev_lock_count > 0);
motu->dev_lock_changed = false;
+ spin_unlock_irq(&motu->lock);
- count = min_t(long, count, sizeof(event.lock_status));
- } else {
+ count = min_t(long, count, sizeof(event));
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+ } else if (motu->msg > 0) {
event.motu_notification.type = SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION;
event.motu_notification.message = motu->msg;
motu->msg = 0;
+ spin_unlock_irq(&motu->lock);
- count = min_t(long, count, sizeof(event.motu_notification));
- }
+ count = min_t(long, count, sizeof(event));
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+ } else if (has_dsp_event(motu)) {
+ size_t consumed = 0;
+ u32 __user *ptr;
+ u32 ev;
- spin_unlock_irq(&motu->lock);
+ spin_unlock_irq(&motu->lock);
- if (copy_to_user(buf, &event, count))
- return -EFAULT;
+ // Header is filled later.
+ consumed += sizeof(event.motu_register_dsp_change);
+
+ while (consumed < count &&
+ snd_motu_register_dsp_message_parser_copy_event(motu, &ev)) {
+ ptr = (u32 __user *)(buf + consumed);
+ if (put_user(ev, ptr))
+ return -EFAULT;
+ consumed += sizeof(ev);
+ }
+
+ event.motu_register_dsp_change.type = SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE;
+ event.motu_register_dsp_change.count =
+ (consumed - sizeof(event.motu_register_dsp_change)) / 4;
+ if (copy_to_user(buf, &event, sizeof(event.motu_register_dsp_change)))
+ return -EFAULT;
+
+ count = consumed;
+ } else {
+ spin_unlock_irq(&motu->lock);
+
+ count = 0;
+ }
return count;
}
@@ -67,7 +105,7 @@ static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
poll_wait(file, &motu->hwdep_wait, wait);
spin_lock_irq(&motu->lock);
- if (motu->dev_lock_changed || motu->msg)
+ if (motu->dev_lock_changed || motu->msg || has_dsp_event(motu))
events = EPOLLIN | EPOLLRDNORM;
else
events = 0;
@@ -86,7 +124,7 @@ static int hwdep_get_info(struct snd_motu *motu, void __user *arg)
info.card = dev->card->index;
*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
- strlcpy(info.device_name, dev_name(&dev->device),
+ strscpy(info.device_name, dev_name(&dev->device),
sizeof(info.device_name));
if (copy_to_user(arg, &info, sizeof(info)))
@@ -155,6 +193,71 @@ static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
return hwdep_lock(motu);
case SNDRV_FIREWIRE_IOCTL_UNLOCK:
return hwdep_unlock(motu);
+ case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_METER:
+ {
+ struct snd_firewire_motu_register_dsp_meter *meter;
+ int err;
+
+ if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP))
+ return -ENXIO;
+
+ meter = kzalloc(sizeof(*meter), GFP_KERNEL);
+ if (!meter)
+ return -ENOMEM;
+
+ snd_motu_register_dsp_message_parser_copy_meter(motu, meter);
+
+ err = copy_to_user((void __user *)arg, meter, sizeof(*meter));
+ kfree(meter);
+
+ if (err)
+ return -EFAULT;
+
+ return 0;
+ }
+ case SNDRV_FIREWIRE_IOCTL_MOTU_COMMAND_DSP_METER:
+ {
+ struct snd_firewire_motu_command_dsp_meter *meter;
+ int err;
+
+ if (!(motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP))
+ return -ENXIO;
+
+ meter = kzalloc(sizeof(*meter), GFP_KERNEL);
+ if (!meter)
+ return -ENOMEM;
+
+ snd_motu_command_dsp_message_parser_copy_meter(motu, meter);
+
+ err = copy_to_user((void __user *)arg, meter, sizeof(*meter));
+ kfree(meter);
+
+ if (err)
+ return -EFAULT;
+
+ return 0;
+ }
+ case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_PARAMETER:
+ {
+ struct snd_firewire_motu_register_dsp_parameter *param;
+ int err;
+
+ if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP))
+ return -ENXIO;
+
+ param = kzalloc(sizeof(*param), GFP_KERNEL);
+ if (!param)
+ return -ENOMEM;
+
+ snd_motu_register_dsp_message_parser_copy_parameter(motu, param);
+
+ err = copy_to_user((void __user *)arg, param, sizeof(*param));
+ kfree(param);
+ if (err)
+ return -EFAULT;
+
+ return 0;
+ }
default:
return -ENOIOCTLCMD;
}
@@ -193,5 +296,7 @@ int snd_motu_create_hwdep_device(struct snd_motu *motu)
hwdep->private_data = motu;
hwdep->exclusive = true;
+ motu->hwdep = hwdep;
+
return 0;
}
diff --git a/sound/firewire/motu/motu-midi.c b/sound/firewire/motu/motu-midi.c
index 2365f7dfde26..eebc7e790ee2 100644
--- a/sound/firewire/motu/motu-midi.c
+++ b/sound/firewire/motu/motu-midi.c
@@ -88,8 +88,8 @@ static void set_midi_substream_names(struct snd_motu *motu,
struct snd_rawmidi_substream *subs;
list_for_each_entry(subs, &str->substreams, list) {
- snprintf(subs->name, sizeof(subs->name),
- "%s MIDI %d", motu->card->shortname, subs->number + 1);
+ scnprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d", motu->card->shortname, subs->number + 1);
}
}
diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c
index 8e1437371263..d410c2efbde5 100644
--- a/sound/firewire/motu/motu-pcm.c
+++ b/sound/firewire/motu/motu-pcm.c
@@ -210,7 +210,7 @@ static int pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_motu *motu = substream->private_data;
int err = 0;
- if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ if (substream->runtime->state == SNDRV_PCM_STATE_OPEN) {
unsigned int rate = params_rate(hw_params);
unsigned int frames_per_period = params_period_size(hw_params);
unsigned int frames_per_buffer = params_buffer_size(hw_params);
@@ -232,7 +232,7 @@ static int pcm_hw_free(struct snd_pcm_substream *substream)
mutex_lock(&motu->mutex);
- if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ if (substream->runtime->state != SNDRV_PCM_STATE_OPEN)
--motu->substreams_counter;
snd_motu_stream_stop_duplex(motu);
diff --git a/sound/firewire/motu/motu-protocol-v1.c b/sound/firewire/motu/motu-protocol-v1.c
new file mode 100644
index 000000000000..e811629f167b
--- /dev/null
+++ b/sound/firewire/motu/motu-protocol-v1.c
@@ -0,0 +1,467 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// motu-protocol-v1.c - a part of driver for MOTU FireWire series
+//
+// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+
+#include "motu.h"
+
+#include <linux/delay.h>
+
+// Status register for MOTU 828 (0x'ffff'f000'0b00).
+//
+// 0xffff0000: ISOC_COMM_CONTROL_MASK in motu-stream.c.
+// 0x00008000: mode of optical input interface.
+// 0x00008000: for S/PDIF signal.
+// 0x00000000: disabled or for ADAT signal.
+// 0x00004000: mode of optical output interface.
+// 0x00004000: for S/PDIF signal.
+// 0x00000000: disabled or for ADAT signal.
+// 0x00003f00: monitor input mode.
+// 0x00000800: analog-1/2
+// 0x00001a00: analog-3/4
+// 0x00002c00: analog-5/6
+// 0x00003e00: analog-7/8
+// 0x00000000: analog-1
+// 0x00000900: analog-2
+// 0x00001200: analog-3
+// 0x00001b00: analog-4
+// 0x00002400: analog-5
+// 0x00002d00: analog-6
+// 0x00003600: analog-7
+// 0x00003f00: analog-8
+// 0x00000080: enable stream input.
+// 0x00000040: disable monitor input.
+// 0x00000008: enable main out.
+// 0x00000004: rate of sampling clock.
+// 0x00000004: 48.0 kHz
+// 0x00000000: 44.1 kHz
+// 0x00000023: source of sampling clock.
+// 0x00000003: source packet header (SPH)
+// 0x00000002: S/PDIF on optical/coaxial interface.
+// 0x00000021: ADAT on optical interface
+// 0x00000001: ADAT on Dsub 9pin
+// 0x00000000: internal
+
+#define CLK_828_STATUS_OFFSET 0x0b00
+#define CLK_828_STATUS_MASK 0x0000ffff
+#define CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF 0x00008000
+#define CLK_828_STATUS_FLAG_OPT_OUT_IFACE_IS_SPDIF 0x00004000
+#define CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES 0x00000080
+#define CLK_828_STATUS_FLAG_ENABLE_OUTPUT 0x00000008
+#define CLK_828_STATUS_FLAG_RATE_48000 0x00000004
+#define CLK_828_STATUS_MASK_SRC 0x00000023
+#define CLK_828_STATUS_FLAG_SRC_ADAT_ON_OPT 0x00000021
+#define CLK_828_STATUS_FLAG_SRC_SPH 0x00000003
+#define CLK_828_STATUS_FLAG_SRC_SPDIF 0x00000002
+#define CLK_828_STATUS_FLAG_SRC_ADAT_ON_DSUB 0x00000001
+#define CLK_828_STATUS_FLAG_SRC_INTERNAL 0x00000000
+
+// Status register for MOTU 896 (0x'ffff'f000'0b14).
+//
+// 0xf0000000: enable physical and stream input to DAC.
+// 0x80000000: disable
+// 0x40000000: disable
+// 0x20000000: enable (prior to the other bits)
+// 0x10000000: disable
+// 0x00000000: disable
+// 0x08000000: speed of word clock signal output on BNC interface.
+// 0x00000000: force to low rate (44.1/48.0 kHz).
+// 0x08000000: follow to system clock.
+// 0x04000000: something relevant to clock.
+// 0x03000000: enable output.
+// 0x02000000: enabled irreversibly once standing unless the device voluntarily disables it.
+// 0x01000000: enabled irreversibly once standing unless the device voluntarily disables it.
+// 0x00ffff00: monitor input mode.
+// 0x00000000: disabled
+// 0x00004800: analog-1/2
+// 0x00005a00: analog-3/4
+// 0x00006c00: analog-5/6
+// 0x00007e00: analog-7/8
+// 0x00104800: AES/EBU-1/2
+// 0x00004000: analog-1
+// 0x00004900: analog-2
+// 0x00005200: analog-3
+// 0x00005b00: analog-4
+// 0x00006400: analog-5
+// 0x00006d00: analog-6
+// 0x00007600: analog-7
+// 0x00007f00: analog-8
+// 0x00104000: AES/EBU-1
+// 0x00104900: AES/EBU-2
+// 0x00000060: sample rate conversion for AES/EBU input/output.
+// 0x00000000: None
+// 0x00000020: input signal is converted to system rate
+// 0x00000040: output is slave to input, ignoring system rate
+// 0x00000060: output is double rate than system rate
+// 0x00000018: nominal rate of sampling clock.
+// 0x00000000: 44.1 kHz
+// 0x00000008: 48.0 kHz
+// 0x00000010: 88.2 kHz
+// 0x00000018: 96.0 kHz
+// 0x00000007: source of sampling clock.
+// 0x00000000: internal
+// 0x00000001: ADAT on optical interface
+// 0x00000002: AES/EBU on XLR
+// 0x00000003: source packet header (SPH)
+// 0x00000004: word clock on BNC
+// 0x00000005: ADAT on Dsub 9pin
+
+#define CLK_896_STATUS_OFFSET 0x0b14
+#define CLK_896_STATUS_FLAG_FETCH_ENABLE 0x20000000
+#define CLK_896_STATUS_FLAG_OUTPUT_ON 0x03000000
+#define CLK_896_STATUS_MASK_SRC 0x00000007
+#define CLK_896_STATUS_FLAG_SRC_INTERNAL 0x00000000
+#define CLK_896_STATUS_FLAG_SRC_ADAT_ON_OPT 0x00000001
+#define CLK_896_STATUS_FLAG_SRC_AESEBU 0x00000002
+#define CLK_896_STATUS_FLAG_SRC_SPH 0x00000003
+#define CLK_896_STATUS_FLAG_SRC_WORD 0x00000004
+#define CLK_896_STATUS_FLAG_SRC_ADAT_ON_DSUB 0x00000005
+#define CLK_896_STATUS_MASK_RATE 0x00000018
+#define CLK_896_STATUS_FLAG_RATE_44100 0x00000000
+#define CLK_896_STATUS_FLAG_RATE_48000 0x00000008
+#define CLK_896_STATUS_FLAG_RATE_88200 0x00000010
+#define CLK_896_STATUS_FLAG_RATE_96000 0x00000018
+
+static void parse_clock_rate_828(u32 data, unsigned int *rate)
+{
+ if (data & CLK_828_STATUS_FLAG_RATE_48000)
+ *rate = 48000;
+ else
+ *rate = 44100;
+}
+
+static int get_clock_rate_828(struct snd_motu *motu, unsigned int *rate)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ parse_clock_rate_828(be32_to_cpu(reg), rate);
+
+ return 0;
+}
+
+static int parse_clock_rate_896(u32 data, unsigned int *rate)
+{
+ switch (data & CLK_896_STATUS_MASK_RATE) {
+ case CLK_896_STATUS_FLAG_RATE_44100:
+ *rate = 44100;
+ break;
+ case CLK_896_STATUS_FLAG_RATE_48000:
+ *rate = 48000;
+ break;
+ case CLK_896_STATUS_FLAG_RATE_88200:
+ *rate = 88200;
+ break;
+ case CLK_896_STATUS_FLAG_RATE_96000:
+ *rate = 96000;
+ break;
+ default:
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static int get_clock_rate_896(struct snd_motu *motu, unsigned int *rate)
+{
+ __be32 reg;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ return parse_clock_rate_896(be32_to_cpu(reg), rate);
+}
+
+int snd_motu_protocol_v1_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
+{
+ if (motu->spec == &snd_motu_spec_828)
+ return get_clock_rate_828(motu, rate);
+ else if (motu->spec == &snd_motu_spec_896)
+ return get_clock_rate_896(motu, rate);
+ else
+ return -ENXIO;
+}
+
+static int set_clock_rate_828(struct snd_motu *motu, unsigned int rate)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;
+
+ data &= ~CLK_828_STATUS_FLAG_RATE_48000;
+ if (rate == 48000)
+ data |= CLK_828_STATUS_FLAG_RATE_48000;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+}
+
+static int set_clock_rate_896(struct snd_motu *motu, unsigned int rate)
+{
+ unsigned int flag;
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ switch (rate) {
+ case 44100:
+ flag = CLK_896_STATUS_FLAG_RATE_44100;
+ break;
+ case 48000:
+ flag = CLK_896_STATUS_FLAG_RATE_48000;
+ break;
+ case 88200:
+ flag = CLK_896_STATUS_FLAG_RATE_88200;
+ break;
+ case 96000:
+ flag = CLK_896_STATUS_FLAG_RATE_96000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ data &= ~CLK_896_STATUS_MASK_RATE;
+ data |= flag;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
+}
+
+int snd_motu_protocol_v1_set_clock_rate(struct snd_motu *motu, unsigned int rate)
+{
+ if (motu->spec == &snd_motu_spec_828)
+ return set_clock_rate_828(motu, rate);
+ else if (motu->spec == &snd_motu_spec_896)
+ return set_clock_rate_896(motu, rate);
+ else
+ return -ENXIO;
+}
+
+static int get_clock_source_828(struct snd_motu *motu, enum snd_motu_clock_source *src)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;
+
+ switch (data & CLK_828_STATUS_MASK_SRC) {
+ case CLK_828_STATUS_FLAG_SRC_ADAT_ON_OPT:
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
+ break;
+ case CLK_828_STATUS_FLAG_SRC_SPH:
+ *src = SND_MOTU_CLOCK_SOURCE_SPH;
+ break;
+ case CLK_828_STATUS_FLAG_SRC_SPDIF:
+ {
+ if (data & CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF)
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+ else
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
+ break;
+ }
+ case CLK_828_STATUS_FLAG_SRC_ADAT_ON_DSUB:
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
+ break;
+ case CLK_828_STATUS_FLAG_SRC_INTERNAL:
+ *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
+ break;
+ default:
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static int get_clock_source_896(struct snd_motu *motu, enum snd_motu_clock_source *src)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ switch (data & CLK_896_STATUS_MASK_SRC) {
+ case CLK_896_STATUS_FLAG_SRC_INTERNAL:
+ *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
+ break;
+ case CLK_896_STATUS_FLAG_SRC_ADAT_ON_OPT:
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
+ break;
+ case CLK_896_STATUS_FLAG_SRC_AESEBU:
+ *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
+ break;
+ case CLK_896_STATUS_FLAG_SRC_SPH:
+ *src = SND_MOTU_CLOCK_SOURCE_SPH;
+ break;
+ case CLK_896_STATUS_FLAG_SRC_WORD:
+ *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
+ break;
+ case CLK_896_STATUS_FLAG_SRC_ADAT_ON_DSUB:
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
+ break;
+ default:
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+int snd_motu_protocol_v1_get_clock_source(struct snd_motu *motu, enum snd_motu_clock_source *src)
+{
+ if (motu->spec == &snd_motu_spec_828)
+ return get_clock_source_828(motu, src);
+ else if (motu->spec == &snd_motu_spec_896)
+ return get_clock_source_896(motu, src);
+ else
+ return -ENXIO;
+}
+
+static int switch_fetching_mode_828(struct snd_motu *motu, bool enable)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;
+
+ data &= ~(CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES | CLK_828_STATUS_FLAG_ENABLE_OUTPUT);
+ if (enable) {
+ // This transaction should be initiated after the device receives batch of packets
+ // since the device voluntarily mutes outputs. As a workaround, yield processor over
+ // 100 msec.
+ msleep(100);
+ data |= CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES | CLK_828_STATUS_FLAG_ENABLE_OUTPUT;
+ }
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+}
+
+static int switch_fetching_mode_896(struct snd_motu *motu, bool enable)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ data &= ~CLK_896_STATUS_FLAG_FETCH_ENABLE;
+ if (enable)
+ data |= CLK_896_STATUS_FLAG_FETCH_ENABLE | CLK_896_STATUS_FLAG_OUTPUT_ON;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
+}
+
+int snd_motu_protocol_v1_switch_fetching_mode(struct snd_motu *motu, bool enable)
+{
+ if (motu->spec == &snd_motu_spec_828)
+ return switch_fetching_mode_828(motu, enable);
+ else if (motu->spec == &snd_motu_spec_896)
+ return switch_fetching_mode_896(motu, enable);
+ else
+ return -ENXIO;
+}
+
+static int detect_packet_formats_828(struct snd_motu *motu)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ motu->tx_packet_formats.pcm_byte_offset = 4;
+ motu->tx_packet_formats.msg_chunks = 2;
+
+ motu->rx_packet_formats.pcm_byte_offset = 4;
+ motu->rx_packet_formats.msg_chunks = 0;
+
+ err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;
+
+ // The number of chunks is just reduced when SPDIF is activated.
+ if (!(data & CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF))
+ motu->tx_packet_formats.pcm_chunks[0] += 8;
+
+ if (!(data & CLK_828_STATUS_FLAG_OPT_OUT_IFACE_IS_SPDIF))
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+
+ return 0;
+}
+
+static int detect_packet_formats_896(struct snd_motu *motu)
+{
+ // 24bit PCM frames follow to source packet header without message chunk.
+ motu->tx_packet_formats.pcm_byte_offset = 4;
+ motu->rx_packet_formats.pcm_byte_offset = 4;
+
+ // No message chunk in data block.
+ motu->tx_packet_formats.msg_chunks = 0;
+ motu->rx_packet_formats.msg_chunks = 0;
+
+ // Always enable optical interface for ADAT signal since the device have no registers
+ // to refer to current configuration.
+ motu->tx_packet_formats.pcm_chunks[0] += 8;
+ motu->tx_packet_formats.pcm_chunks[1] += 8;
+
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+ motu->rx_packet_formats.pcm_chunks[1] += 8;
+
+ return 0;
+}
+
+int snd_motu_protocol_v1_cache_packet_formats(struct snd_motu *motu)
+{
+ memcpy(motu->tx_packet_formats.pcm_chunks, motu->spec->tx_fixed_pcm_chunks,
+ sizeof(motu->tx_packet_formats.pcm_chunks));
+ memcpy(motu->rx_packet_formats.pcm_chunks, motu->spec->rx_fixed_pcm_chunks,
+ sizeof(motu->rx_packet_formats.pcm_chunks));
+
+ if (motu->spec == &snd_motu_spec_828)
+ return detect_packet_formats_828(motu);
+ else if (motu->spec == &snd_motu_spec_896)
+ return detect_packet_formats_896(motu);
+ else
+ return 0;
+}
+
+const struct snd_motu_spec snd_motu_spec_828 = {
+ .name = "828",
+ .protocol_version = SND_MOTU_PROTOCOL_V1,
+ .tx_fixed_pcm_chunks = {10, 0, 0},
+ .rx_fixed_pcm_chunks = {10, 0, 0},
+};
+
+const struct snd_motu_spec snd_motu_spec_896 = {
+ .name = "896",
+ .tx_fixed_pcm_chunks = {10, 10, 0},
+ .rx_fixed_pcm_chunks = {10, 10, 0},
+};
diff --git a/sound/firewire/motu/motu-protocol-v2.c b/sound/firewire/motu/motu-protocol-v2.c
index e59e69ab1538..a5f70efa2e88 100644
--- a/sound/firewire/motu/motu-protocol-v2.c
+++ b/sound/firewire/motu/motu-protocol-v2.c
@@ -12,6 +12,13 @@
#define V2_CLOCK_RATE_SHIFT 3
#define V2_CLOCK_SRC_MASK 0x00000007
#define V2_CLOCK_SRC_SHIFT 0
+#define V2_CLOCK_SRC_AESEBU_ON_XLR 0x07 // In Traveler.
+#define V2_CLOCK_SRC_ADAT_ON_DSUB 0x05
+#define V2_CLOCK_SRC_WORD_ON_BNC 0x04
+#define V2_CLOCK_SRC_SPH 0x03
+#define V2_CLOCK_SRC_SPDIF 0x02 // on either coaxial or optical. AES/EBU in 896HD.
+#define V2_CLOCK_SRC_ADAT_ON_OPT 0x01
+#define V2_CLOCK_SRC_INTERNAL 0x00
#define V2_CLOCK_FETCH_ENABLE 0x02000000
#define V2_CLOCK_MODEL_SPECIFIC 0x04000000
@@ -78,64 +85,54 @@ int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu,
sizeof(reg));
}
-static int detect_clock_source_optical_model(struct snd_motu *motu, u32 data,
- enum snd_motu_clock_source *src)
+static int get_clock_source(struct snd_motu *motu, u32 data,
+ enum snd_motu_clock_source *src)
{
- switch (data) {
- case 0:
+ switch (data & V2_CLOCK_SRC_MASK) {
+ case V2_CLOCK_SRC_INTERNAL:
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
break;
- case 1:
+ case V2_CLOCK_SRC_ADAT_ON_OPT:
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
+ break;
+ case V2_CLOCK_SRC_SPDIF:
{
- __be32 reg;
-
- // To check the configuration of optical interface.
- int err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET,
- &reg, sizeof(reg));
- if (err < 0)
- return err;
-
- if (be32_to_cpu(reg) & 0x00000200)
- *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
- else
- *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
+ bool support_iec60958_on_opt = (motu->spec == &snd_motu_spec_828mk2 ||
+ motu->spec == &snd_motu_spec_traveler);
+
+ if (motu->spec == &snd_motu_spec_896hd) {
+ *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
+ } else if (!support_iec60958_on_opt) {
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+ } else {
+ __be32 reg;
+
+ // To check the configuration of optical interface.
+ int err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+
+ if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) ==
+ V2_OPT_IFACE_MODE_SPDIF)
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
+ else
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+ }
break;
}
- case 2:
- *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
- break;
- case 3:
+ case V2_CLOCK_SRC_SPH:
*src = SND_MOTU_CLOCK_SOURCE_SPH;
break;
- case 4:
+ case V2_CLOCK_SRC_WORD_ON_BNC:
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
break;
- case 5:
+ case V2_CLOCK_SRC_ADAT_ON_DSUB:
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
break;
- default:
- *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
- break;
- }
-
- return 0;
-}
-
-static int v2_detect_clock_source(struct snd_motu *motu, u32 data,
- enum snd_motu_clock_source *src)
-{
- switch (data) {
- case 0:
- *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
- break;
- case 2:
- *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
- break;
- case 3:
- *src = SND_MOTU_CLOCK_SOURCE_SPH;
- break;
- case 4:
- *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
+ case V2_CLOCK_SRC_AESEBU_ON_XLR:
+ // For Traveler.
+ *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
break;
default:
*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
@@ -145,17 +142,6 @@ static int v2_detect_clock_source(struct snd_motu *motu, u32 data,
return 0;
}
-static int get_clock_source(struct snd_motu *motu, u32 data,
- enum snd_motu_clock_source *src)
-{
- data &= V2_CLOCK_SRC_MASK;
- if (motu->spec == &snd_motu_spec_828mk2 ||
- motu->spec == &snd_motu_spec_traveler)
- return detect_clock_source_optical_model(motu, data, src);
- else
- return v2_detect_clock_source(motu, data, src);
-}
-
int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu,
enum snd_motu_clock_source *src)
{
@@ -170,7 +156,7 @@ int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu,
return get_clock_source(motu, be32_to_cpu(reg), src);
}
-// Expected for Traveler and 896HD, which implements Altera Cyclone EP1C3.
+// Expected for Traveler, which implements Altera Cyclone EP1C3.
static int switch_fetching_mode_cyclone(struct snd_motu *motu, u32 *data,
bool enable)
{
@@ -207,6 +193,9 @@ int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu,
if (motu->spec == &snd_motu_spec_828mk2) {
// 828mkII implements Altera ACEX 1K EP1K30. Nothing to do.
return 0;
+ } else if (motu->spec == &snd_motu_spec_896hd) {
+ // 896HD implements Altera Cyclone EP1C3 but nothing to do.
+ return 0;
} else {
__be32 reg;
u32 data;
@@ -235,59 +224,9 @@ int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu,
}
}
-static int detect_packet_formats_828mk2(struct snd_motu *motu, u32 data)
-{
- if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) ==
- V2_OPT_IFACE_MODE_ADAT) {
- motu->tx_packet_formats.pcm_chunks[0] += 8;
- motu->tx_packet_formats.pcm_chunks[1] += 4;
- }
-
- if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) ==
- V2_OPT_IFACE_MODE_ADAT) {
- motu->rx_packet_formats.pcm_chunks[0] += 8;
- motu->rx_packet_formats.pcm_chunks[1] += 4;
- }
-
- return 0;
-}
-
-static int detect_packet_formats_traveler(struct snd_motu *motu, u32 data)
-{
- if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) ==
- V2_OPT_IFACE_MODE_ADAT) {
- motu->tx_packet_formats.pcm_chunks[0] += 8;
- motu->tx_packet_formats.pcm_chunks[1] += 4;
- }
-
- if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) ==
- V2_OPT_IFACE_MODE_ADAT) {
- motu->rx_packet_formats.pcm_chunks[0] += 8;
- motu->rx_packet_formats.pcm_chunks[1] += 4;
- }
-
- return 0;
-}
-
-static int detect_packet_formats_8pre(struct snd_motu *motu, u32 data)
-{
- if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) ==
- V2_OPT_IFACE_MODE_ADAT) {
- motu->tx_packet_formats.pcm_chunks[0] += 8;
- motu->tx_packet_formats.pcm_chunks[1] += 8;
- }
-
- if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) ==
- V2_OPT_IFACE_MODE_ADAT) {
- motu->rx_packet_formats.pcm_chunks[0] += 8;
- motu->rx_packet_formats.pcm_chunks[1] += 8;
- }
-
- return 0;
-}
-
int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu)
{
+ bool has_two_opt_ifaces = (motu->spec == &snd_motu_spec_8pre);
__be32 reg;
u32 data;
int err;
@@ -311,30 +250,51 @@ int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu)
motu->spec->rx_fixed_pcm_chunks,
sizeof(motu->rx_packet_formats.pcm_chunks));
- if (motu->spec == &snd_motu_spec_828mk2)
- return detect_packet_formats_828mk2(motu, data);
- else if (motu->spec == &snd_motu_spec_traveler)
- return detect_packet_formats_traveler(motu, data);
- else if (motu->spec == &snd_motu_spec_8pre)
- return detect_packet_formats_8pre(motu, data);
- else
- return 0;
+ if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == V2_OPT_IFACE_MODE_ADAT) {
+ motu->tx_packet_formats.pcm_chunks[0] += 8;
+
+ if (!has_two_opt_ifaces)
+ motu->tx_packet_formats.pcm_chunks[1] += 4;
+ else
+ motu->tx_packet_formats.pcm_chunks[1] += 8;
+ }
+
+ if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) == V2_OPT_IFACE_MODE_ADAT) {
+ motu->rx_packet_formats.pcm_chunks[0] += 8;
+
+ if (!has_two_opt_ifaces)
+ motu->rx_packet_formats.pcm_chunks[1] += 4;
+ else
+ motu->rx_packet_formats.pcm_chunks[1] += 8;
+ }
+
+ return 0;
}
const struct snd_motu_spec snd_motu_spec_828mk2 = {
.name = "828mk2",
.protocol_version = SND_MOTU_PROTOCOL_V2,
.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
- SND_MOTU_SPEC_TX_MIDI_2ND_Q,
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
.tx_fixed_pcm_chunks = {14, 14, 0},
.rx_fixed_pcm_chunks = {14, 14, 0},
};
+const struct snd_motu_spec snd_motu_spec_896hd = {
+ .name = "896HD",
+ .protocol_version = SND_MOTU_PROTOCOL_V2,
+ .flags = SND_MOTU_SPEC_REGISTER_DSP,
+ .tx_fixed_pcm_chunks = {14, 14, 8},
+ .rx_fixed_pcm_chunks = {14, 14, 8},
+};
+
const struct snd_motu_spec snd_motu_spec_traveler = {
.name = "Traveler",
.protocol_version = SND_MOTU_PROTOCOL_V2,
.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
- SND_MOTU_SPEC_TX_MIDI_2ND_Q,
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
.tx_fixed_pcm_chunks = {14, 14, 8},
.rx_fixed_pcm_chunks = {14, 14, 8},
};
@@ -343,7 +303,8 @@ const struct snd_motu_spec snd_motu_spec_ultralite = {
.name = "UltraLite",
.protocol_version = SND_MOTU_PROTOCOL_V2,
.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
- SND_MOTU_SPEC_TX_MIDI_2ND_Q,
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
.tx_fixed_pcm_chunks = {14, 14, 0},
.rx_fixed_pcm_chunks = {14, 14, 0},
};
@@ -352,7 +313,9 @@ const struct snd_motu_spec snd_motu_spec_8pre = {
.name = "8pre",
.protocol_version = SND_MOTU_PROTOCOL_V2,
.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
- SND_MOTU_SPEC_TX_MIDI_2ND_Q,
- .tx_fixed_pcm_chunks = {10, 6, 0},
- .rx_fixed_pcm_chunks = {10, 6, 0},
+ SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
+ // Two dummy chunks always in the end of data block.
+ .tx_fixed_pcm_chunks = {10, 10, 0},
+ .rx_fixed_pcm_chunks = {6, 6, 0},
};
diff --git a/sound/firewire/motu/motu-protocol-v3.c b/sound/firewire/motu/motu-protocol-v3.c
index 4e6b0e449ee4..7254fdfe046a 100644
--- a/sound/firewire/motu/motu-protocol-v3.c
+++ b/sound/firewire/motu/motu-protocol-v3.c
@@ -13,6 +13,13 @@
#define V3_CLOCK_RATE_MASK 0x0000ff00
#define V3_CLOCK_RATE_SHIFT 8
#define V3_CLOCK_SOURCE_MASK 0x000000ff
+#define V3_CLOCK_SRC_INTERNAL 0x00
+#define V3_CLOCK_SRC_WORD_ON_BNC 0x01
+#define V3_CLOCK_SRC_SPH 0x02
+#define V3_CLOCK_SRC_AESEBU_ON_XLR 0x08
+#define V3_CLOCK_SRC_SPDIF_ON_COAX 0x10
+#define V3_CLOCK_SRC_OPT_IFACE_A 0x18
+#define V3_CLOCK_SRC_OPT_IFACE_B 0x19
#define V3_OPT_IFACE_MODE_OFFSET 0x0c94
#define V3_ENABLE_OPT_IN_IFACE_A 0x00000001
@@ -97,28 +104,40 @@ int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu,
return 0;
}
-static int detect_clock_source_828mk3(struct snd_motu *motu, u32 data,
- enum snd_motu_clock_source *src)
+int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src)
{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg) & V3_CLOCK_SOURCE_MASK;
+
switch (data) {
- case 0x00:
+ case V3_CLOCK_SRC_INTERNAL:
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
break;
- case 0x01:
+ case V3_CLOCK_SRC_WORD_ON_BNC:
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
break;
- case 0x02:
+ case V3_CLOCK_SRC_SPH:
*src = SND_MOTU_CLOCK_SOURCE_SPH;
break;
- case 0x10:
+ case V3_CLOCK_SRC_AESEBU_ON_XLR:
+ *src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
+ break;
+ case V3_CLOCK_SRC_SPDIF_ON_COAX:
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
break;
- case 0x18:
- case 0x19:
+ case V3_CLOCK_SRC_OPT_IFACE_A:
+ case V3_CLOCK_SRC_OPT_IFACE_B:
{
__be32 reg;
u32 options;
- int err;
err = snd_motu_transaction_read(motu,
V3_OPT_IFACE_MODE_OFFSET, &reg, sizeof(reg));
@@ -126,7 +145,7 @@ static int detect_clock_source_828mk3(struct snd_motu *motu, u32 data,
return err;
options = be32_to_cpu(reg);
- if (data == 0x18) {
+ if (data == V3_CLOCK_SRC_OPT_IFACE_A) {
if (options & V3_NO_ADAT_OPT_IN_IFACE_A)
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A;
else
@@ -137,33 +156,8 @@ static int detect_clock_source_828mk3(struct snd_motu *motu, u32 data,
else
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B;
}
-
- break;
- }
- default:
- *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
break;
}
-
- return 0;
-}
-
-static int v3_detect_clock_source(struct snd_motu *motu, u32 data,
- enum snd_motu_clock_source *src)
-{
- switch (data) {
- case 0x00:
- *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
- break;
- case 0x01:
- *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
- break;
- case 0x02:
- *src = SND_MOTU_CLOCK_SOURCE_SPH;
- break;
- case 0x10:
- *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
- break;
default:
*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
break;
@@ -172,25 +166,6 @@ static int v3_detect_clock_source(struct snd_motu *motu, u32 data,
return 0;
}
-int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu,
- enum snd_motu_clock_source *src)
-{
- __be32 reg;
- u32 data;
- int err;
-
- err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
- sizeof(reg));
- if (err < 0)
- return err;
- data = be32_to_cpu(reg) & V3_CLOCK_SOURCE_MASK;
-
- if (motu->spec == &snd_motu_spec_828mk3)
- return detect_clock_source_828mk3(motu, data, src);
- else
- return v3_detect_clock_source(motu, data, src);
-}
-
int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu,
bool enable)
{
@@ -214,7 +189,7 @@ int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu,
sizeof(reg));
}
-static int detect_packet_formats_828mk3(struct snd_motu *motu, u32 data)
+static int detect_packet_formats_with_opt_ifaces(struct snd_motu *motu, u32 data)
{
if (data & V3_ENABLE_OPT_IN_IFACE_A) {
if (data & V3_NO_ADAT_OPT_IN_IFACE_A) {
@@ -284,19 +259,51 @@ int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu)
motu->spec->rx_fixed_pcm_chunks,
sizeof(motu->rx_packet_formats.pcm_chunks));
- if (motu->spec == &snd_motu_spec_828mk3)
- return detect_packet_formats_828mk3(motu, data);
+ if (motu->spec == &snd_motu_spec_828mk3_fw ||
+ motu->spec == &snd_motu_spec_828mk3_hybrid ||
+ motu->spec == &snd_motu_spec_896mk3 ||
+ motu->spec == &snd_motu_spec_traveler_mk3 ||
+ motu->spec == &snd_motu_spec_track16)
+ return detect_packet_formats_with_opt_ifaces(motu, data);
else
return 0;
}
+const struct snd_motu_spec snd_motu_spec_828mk3_fw = {
+ .name = "828mk3",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_COMMAND_DSP,
+ .tx_fixed_pcm_chunks = {18, 18, 14},
+ .rx_fixed_pcm_chunks = {14, 14, 10},
+};
-const struct snd_motu_spec snd_motu_spec_828mk3 = {
+const struct snd_motu_spec snd_motu_spec_828mk3_hybrid = {
.name = "828mk3",
.protocol_version = SND_MOTU_PROTOCOL_V3,
.flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
- SND_MOTU_SPEC_TX_MIDI_3RD_Q,
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_COMMAND_DSP,
.tx_fixed_pcm_chunks = {18, 18, 14},
+ .rx_fixed_pcm_chunks = {14, 14, 14}, // Additional 4 dummy chunks at higher rate.
+};
+
+const struct snd_motu_spec snd_motu_spec_896mk3 = {
+ .name = "896mk3",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_COMMAND_DSP,
+ .tx_fixed_pcm_chunks = {18, 14, 10},
+ .rx_fixed_pcm_chunks = {18, 14, 10},
+};
+
+const struct snd_motu_spec snd_motu_spec_traveler_mk3 = {
+ .name = "TravelerMk3",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_COMMAND_DSP,
+ .tx_fixed_pcm_chunks = {18, 14, 10},
.rx_fixed_pcm_chunks = {14, 14, 10},
};
@@ -304,7 +311,8 @@ const struct snd_motu_spec snd_motu_spec_ultralite_mk3 = {
.name = "UltraLiteMk3",
.protocol_version = SND_MOTU_PROTOCOL_V3,
.flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
- SND_MOTU_SPEC_TX_MIDI_3RD_Q,
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_COMMAND_DSP,
.tx_fixed_pcm_chunks = {18, 14, 10},
.rx_fixed_pcm_chunks = {14, 14, 14},
};
@@ -313,14 +321,26 @@ const struct snd_motu_spec snd_motu_spec_audio_express = {
.name = "AudioExpress",
.protocol_version = SND_MOTU_PROTOCOL_V3,
.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
- SND_MOTU_SPEC_TX_MIDI_3RD_Q,
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_REGISTER_DSP,
.tx_fixed_pcm_chunks = {10, 10, 0},
.rx_fixed_pcm_chunks = {10, 10, 0},
};
+const struct snd_motu_spec snd_motu_spec_track16 = {
+ .name = "Track16",
+ .protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+ SND_MOTU_SPEC_COMMAND_DSP,
+ .tx_fixed_pcm_chunks = {14, 14, 14},
+ .rx_fixed_pcm_chunks = {6, 6, 6},
+};
+
const struct snd_motu_spec snd_motu_spec_4pre = {
.name = "4pre",
.protocol_version = SND_MOTU_PROTOCOL_V3,
+ .flags = SND_MOTU_SPEC_REGISTER_DSP,
.tx_fixed_pcm_chunks = {10, 10, 0},
.rx_fixed_pcm_chunks = {10, 10, 0},
};
diff --git a/sound/firewire/motu/motu-register-dsp-message-parser.c b/sound/firewire/motu/motu-register-dsp-message-parser.c
new file mode 100644
index 000000000000..ef3b0b0f0dab
--- /dev/null
+++ b/sound/firewire/motu/motu-register-dsp-message-parser.c
@@ -0,0 +1,423 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// motu-register-dsp-message-parser.c - a part of driver for MOTU FireWire series
+//
+// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+
+// Below models allow software to configure their DSP functions by asynchronous transaction
+// to access their internal registers.
+// * 828 mk2
+// * 896hd
+// * Traveler
+// * 8 pre
+// * Ultralite
+// * 4 pre
+// * Audio Express
+//
+// Additionally, isochronous packets from the above models include messages to notify state of
+// DSP. The messages are two set of 3 byte data in 2nd and 3rd quadlet of data block. When user
+// operates hardware components such as dial and switch, corresponding messages are transferred.
+// The messages include Hardware metering and MIDI messages as well.
+
+#include "motu.h"
+
+#define MSG_FLAG_POS 4
+#define MSG_FLAG_TYPE_MASK 0xf8
+#define MSG_FLAG_MIDI_MASK 0x01
+#define MSG_FLAG_MODEL_SPECIFIC_MASK 0x06
+#define MSG_FLAG_8PRE 0x00
+#define MSG_FLAG_ULTRALITE 0x04
+#define MSG_FLAG_TRAVELER 0x04
+#define MSG_FLAG_828MK2 0x04
+#define MSG_FLAG_896HD 0x04
+#define MSG_FLAG_4PRE 0x05 // MIDI mask is in 8th byte.
+#define MSG_FLAG_AUDIOEXPRESS 0x05 // MIDI mask is in 8th byte.
+#define MSG_FLAG_TYPE_SHIFT 3
+#define MSG_VALUE_POS 5
+#define MSG_MIDI_BYTE_POS 6
+#define MSG_METER_IDX_POS 7
+
+// In 4 pre and Audio express, meter index is in 6th byte. MIDI flag is in 8th byte and MIDI byte
+// is in 7th byte.
+#define MSG_METER_IDX_POS_4PRE_AE 6
+#define MSG_MIDI_BYTE_POS_4PRE_AE 7
+#define MSG_FLAG_MIDI_POS_4PRE_AE 8
+
+enum register_dsp_msg_type {
+ // Used for messages with no information.
+ INVALID = 0x00,
+ MIXER_SELECT = 0x01,
+ MIXER_SRC_GAIN = 0x02,
+ MIXER_SRC_PAN = 0x03,
+ MIXER_SRC_FLAG = 0x04,
+ MIXER_OUTPUT_PAIRED_VOLUME = 0x05,
+ MIXER_OUTPUT_PAIRED_FLAG = 0x06,
+ MAIN_OUTPUT_PAIRED_VOLUME = 0x07,
+ HP_OUTPUT_PAIRED_VOLUME = 0x08,
+ HP_OUTPUT_PAIRED_ASSIGNMENT = 0x09,
+ // Transferred by all models but the purpose is still unknown.
+ UNKNOWN_0 = 0x0a,
+ // Specific to 828mk2, 896hd, Traveler.
+ UNKNOWN_2 = 0x0c,
+ // Specific to 828mk2, Traveler, and 896hd (not functional).
+ LINE_INPUT_BOOST = 0x0d,
+ // Specific to 828mk2, Traveler, and 896hd (not functional).
+ LINE_INPUT_NOMINAL_LEVEL = 0x0e,
+ // Specific to Ultralite, 4 pre, Audio express, and 8 pre (not functional).
+ INPUT_GAIN_AND_INVERT = 0x15,
+ // Specific to 4 pre, and Audio express.
+ INPUT_FLAG = 0x16,
+ // Specific to 4 pre, and Audio express.
+ MIXER_SRC_PAIRED_BALANCE = 0x17,
+ // Specific to 4 pre, and Audio express.
+ MIXER_SRC_PAIRED_WIDTH = 0x18,
+ // Transferred by all models. This type of message interposes the series of the other
+ // messages. The message delivers signal level up to 96.0 kHz. In 828mk2, 896hd, and
+ // Traveler, one of physical outputs is selected for the message. The selection is done
+ // by LSB one byte in asynchronous write quadlet transaction to 0x'ffff'f000'0b2c.
+ METER = 0x1f,
+};
+
+#define EVENT_QUEUE_SIZE 16
+
+struct msg_parser {
+ spinlock_t lock;
+ struct snd_firewire_motu_register_dsp_meter meter;
+ bool meter_pos_quirk;
+
+ struct snd_firewire_motu_register_dsp_parameter param;
+ u8 prev_mixer_src_type;
+ u8 mixer_ch;
+ u8 mixer_src_ch;
+
+ u8 input_ch;
+ u8 prev_msg_type;
+
+ u32 event_queue[EVENT_QUEUE_SIZE];
+ unsigned int push_pos;
+ unsigned int pull_pos;
+};
+
+int snd_motu_register_dsp_message_parser_new(struct snd_motu *motu)
+{
+ struct msg_parser *parser;
+ parser = devm_kzalloc(&motu->card->card_dev, sizeof(*parser), GFP_KERNEL);
+ if (!parser)
+ return -ENOMEM;
+ spin_lock_init(&parser->lock);
+ if (motu->spec == &snd_motu_spec_4pre || motu->spec == &snd_motu_spec_audio_express)
+ parser->meter_pos_quirk = true;
+ motu->message_parser = parser;
+ return 0;
+}
+
+int snd_motu_register_dsp_message_parser_init(struct snd_motu *motu)
+{
+ struct msg_parser *parser = motu->message_parser;
+
+ parser->prev_mixer_src_type = INVALID;
+ parser->mixer_ch = 0xff;
+ parser->mixer_src_ch = 0xff;
+ parser->prev_msg_type = INVALID;
+
+ return 0;
+}
+
+// Rough implementaion of queue without overrun check.
+static void queue_event(struct snd_motu *motu, u8 msg_type, u8 identifier0, u8 identifier1, u8 val)
+{
+ struct msg_parser *parser = motu->message_parser;
+ unsigned int pos = parser->push_pos;
+ u32 entry;
+
+ if (!motu->hwdep || motu->hwdep->used == 0)
+ return;
+
+ entry = (msg_type << 24) | (identifier0 << 16) | (identifier1 << 8) | val;
+ parser->event_queue[pos] = entry;
+
+ ++pos;
+ if (pos >= EVENT_QUEUE_SIZE)
+ pos = 0;
+ parser->push_pos = pos;
+}
+
+void snd_motu_register_dsp_message_parser_parse(const struct amdtp_stream *s,
+ const struct pkt_desc *desc, unsigned int count)
+{
+ struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream);
+ unsigned int data_block_quadlets = s->data_block_quadlets;
+ struct msg_parser *parser = motu->message_parser;
+ bool meter_pos_quirk = parser->meter_pos_quirk;
+ unsigned int pos = parser->push_pos;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&parser->lock, flags);
+
+ for (i = 0; i < count; ++i) {
+ __be32 *buffer = desc->ctx_payload;
+ unsigned int data_blocks = desc->data_blocks;
+ int j;
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
+
+ for (j = 0; j < data_blocks; ++j) {
+ u8 *b = (u8 *)buffer;
+ u8 msg_type = (b[MSG_FLAG_POS] & MSG_FLAG_TYPE_MASK) >> MSG_FLAG_TYPE_SHIFT;
+ u8 val = b[MSG_VALUE_POS];
+
+ buffer += data_block_quadlets;
+
+ switch (msg_type) {
+ case MIXER_SELECT:
+ {
+ u8 mixer_ch = val / 0x20;
+ if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT) {
+ parser->mixer_src_ch = 0;
+ parser->mixer_ch = mixer_ch;
+ }
+ break;
+ }
+ case MIXER_SRC_GAIN:
+ case MIXER_SRC_PAN:
+ case MIXER_SRC_FLAG:
+ case MIXER_SRC_PAIRED_BALANCE:
+ case MIXER_SRC_PAIRED_WIDTH:
+ {
+ struct snd_firewire_motu_register_dsp_parameter *param = &parser->param;
+ u8 mixer_ch = parser->mixer_ch;
+ u8 mixer_src_ch = parser->mixer_src_ch;
+
+ if (msg_type != parser->prev_mixer_src_type)
+ mixer_src_ch = 0;
+ else
+ ++mixer_src_ch;
+ parser->prev_mixer_src_type = msg_type;
+
+ if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT &&
+ mixer_src_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_SRC_COUNT) {
+ u8 mixer_ch = parser->mixer_ch;
+
+ switch (msg_type) {
+ case MIXER_SRC_GAIN:
+ if (param->mixer.source[mixer_ch].gain[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].gain[mixer_src_ch] = val;
+ }
+ break;
+ case MIXER_SRC_PAN:
+ if (param->mixer.source[mixer_ch].pan[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].pan[mixer_src_ch] = val;
+ }
+ break;
+ case MIXER_SRC_FLAG:
+ if (param->mixer.source[mixer_ch].flag[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].flag[mixer_src_ch] = val;
+ }
+ break;
+ case MIXER_SRC_PAIRED_BALANCE:
+ if (param->mixer.source[mixer_ch].paired_balance[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].paired_balance[mixer_src_ch] = val;
+ }
+ break;
+ case MIXER_SRC_PAIRED_WIDTH:
+ if (param->mixer.source[mixer_ch].paired_width[mixer_src_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+ param->mixer.source[mixer_ch].paired_width[mixer_src_ch] = val;
+ }
+ break;
+ default:
+ break;
+ }
+
+ parser->mixer_src_ch = mixer_src_ch;
+ }
+ break;
+ }
+ case MIXER_OUTPUT_PAIRED_VOLUME:
+ case MIXER_OUTPUT_PAIRED_FLAG:
+ {
+ struct snd_firewire_motu_register_dsp_parameter *param = &parser->param;
+ u8 mixer_ch = parser->mixer_ch;
+
+ if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT) {
+ switch (msg_type) {
+ case MIXER_OUTPUT_PAIRED_VOLUME:
+ if (param->mixer.output.paired_volume[mixer_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, 0, val);
+ param->mixer.output.paired_volume[mixer_ch] = val;
+ }
+ break;
+ case MIXER_OUTPUT_PAIRED_FLAG:
+ if (param->mixer.output.paired_flag[mixer_ch] != val) {
+ queue_event(motu, msg_type, mixer_ch, 0, val);
+ param->mixer.output.paired_flag[mixer_ch] = val;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ case MAIN_OUTPUT_PAIRED_VOLUME:
+ if (parser->param.output.main_paired_volume != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.output.main_paired_volume = val;
+ }
+ break;
+ case HP_OUTPUT_PAIRED_VOLUME:
+ if (parser->param.output.hp_paired_volume != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.output.hp_paired_volume = val;
+ }
+ break;
+ case HP_OUTPUT_PAIRED_ASSIGNMENT:
+ if (parser->param.output.hp_paired_assignment != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.output.hp_paired_assignment = val;
+ }
+ break;
+ case LINE_INPUT_BOOST:
+ if (parser->param.line_input.boost_flag != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.line_input.boost_flag = val;
+ }
+ break;
+ case LINE_INPUT_NOMINAL_LEVEL:
+ if (parser->param.line_input.nominal_level_flag != val) {
+ queue_event(motu, msg_type, 0, 0, val);
+ parser->param.line_input.nominal_level_flag = val;
+ }
+ break;
+ case INPUT_GAIN_AND_INVERT:
+ case INPUT_FLAG:
+ {
+ struct snd_firewire_motu_register_dsp_parameter *param = &parser->param;
+ u8 input_ch = parser->input_ch;
+
+ if (parser->prev_msg_type != msg_type)
+ input_ch = 0;
+ else
+ ++input_ch;
+
+ if (input_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_INPUT_COUNT) {
+ switch (msg_type) {
+ case INPUT_GAIN_AND_INVERT:
+ if (param->input.gain_and_invert[input_ch] != val) {
+ queue_event(motu, msg_type, input_ch, 0, val);
+ param->input.gain_and_invert[input_ch] = val;
+ }
+ break;
+ case INPUT_FLAG:
+ if (param->input.flag[input_ch] != val) {
+ queue_event(motu, msg_type, input_ch, 0, val);
+ param->input.flag[input_ch] = val;
+ }
+ break;
+ default:
+ break;
+ }
+ parser->input_ch = input_ch;
+ }
+ break;
+ }
+ case UNKNOWN_0:
+ case UNKNOWN_2:
+ break;
+ case METER:
+ {
+ u8 pos;
+
+ if (!meter_pos_quirk)
+ pos = b[MSG_METER_IDX_POS];
+ else
+ pos = b[MSG_METER_IDX_POS_4PRE_AE];
+
+ if (pos < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_INPUT_COUNT) {
+ parser->meter.data[pos] = val;
+ } else if (pos >= 0x80) {
+ pos -= (0x80 - SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_INPUT_COUNT);
+
+ if (pos < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_COUNT)
+ parser->meter.data[pos] = val;
+ }
+
+ // The message for meter is interruptible to the series of other
+ // types of messages. Don't cache it.
+ fallthrough;
+ }
+ case INVALID:
+ default:
+ // Don't cache it.
+ continue;
+ }
+
+ parser->prev_msg_type = msg_type;
+ }
+ }
+
+ if (pos != parser->push_pos)
+ wake_up(&motu->hwdep_wait);
+
+ spin_unlock_irqrestore(&parser->lock, flags);
+}
+
+void snd_motu_register_dsp_message_parser_copy_meter(struct snd_motu *motu,
+ struct snd_firewire_motu_register_dsp_meter *meter)
+{
+ struct msg_parser *parser = motu->message_parser;
+ unsigned long flags;
+
+ spin_lock_irqsave(&parser->lock, flags);
+ memcpy(meter, &parser->meter, sizeof(*meter));
+ spin_unlock_irqrestore(&parser->lock, flags);
+}
+
+void snd_motu_register_dsp_message_parser_copy_parameter(struct snd_motu *motu,
+ struct snd_firewire_motu_register_dsp_parameter *param)
+{
+ struct msg_parser *parser = motu->message_parser;
+ unsigned long flags;
+
+ spin_lock_irqsave(&parser->lock, flags);
+ memcpy(param, &parser->param, sizeof(*param));
+ spin_unlock_irqrestore(&parser->lock, flags);
+}
+
+unsigned int snd_motu_register_dsp_message_parser_count_event(struct snd_motu *motu)
+{
+ struct msg_parser *parser = motu->message_parser;
+
+ if (parser->pull_pos > parser->push_pos)
+ return EVENT_QUEUE_SIZE - parser->pull_pos + parser->push_pos;
+ else
+ return parser->push_pos - parser->pull_pos;
+}
+
+bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32 *event)
+{
+ struct msg_parser *parser = motu->message_parser;
+ unsigned int pos = parser->pull_pos;
+ unsigned long flags;
+
+ if (pos == parser->push_pos)
+ return false;
+
+ spin_lock_irqsave(&parser->lock, flags);
+
+ *event = parser->event_queue[pos];
+
+ ++pos;
+ if (pos >= EVENT_QUEUE_SIZE)
+ pos = 0;
+ parser->pull_pos = pos;
+
+ spin_unlock_irqrestore(&parser->lock, flags);
+
+ return true;
+}
diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c
index 2028c5419f6f..64aec9c3eefd 100644
--- a/sound/firewire/motu/motu-stream.c
+++ b/sound/firewire/motu/motu-stream.c
@@ -7,7 +7,7 @@
#include "motu.h"
-#define CALLBACK_TIMEOUT 200
+#define READY_TIMEOUT_MS 200
#define ISOC_COMM_CONTROL_OFFSET 0x0b00
#define ISOC_COMM_CONTROL_MASK 0xffff0000
@@ -153,6 +153,9 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate,
fw_iso_resources_free(&motu->tx_resources);
fw_iso_resources_free(&motu->rx_resources);
+ kfree(motu->cache.event_offsets);
+ motu->cache.event_offsets = NULL;
+
err = snd_motu_protocol_set_clock_rate(motu, rate);
if (err < 0) {
dev_err(&motu->unit->device,
@@ -181,6 +184,15 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate,
fw_iso_resources_free(&motu->rx_resources);
return err;
}
+
+ motu->cache.size = motu->tx_stream.syt_interval * frames_per_buffer;
+ motu->cache.event_offsets = kcalloc(motu->cache.size, sizeof(*motu->cache.event_offsets),
+ GFP_KERNEL);
+ if (!motu->cache.event_offsets) {
+ fw_iso_resources_free(&motu->tx_resources);
+ fw_iso_resources_free(&motu->rx_resources);
+ return -ENOMEM;
+ }
}
return 0;
@@ -243,6 +255,16 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
if (err < 0)
return err;
+ if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) {
+ err = snd_motu_register_dsp_message_parser_init(motu);
+ if (err < 0)
+ return err;
+ } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) {
+ err = snd_motu_command_dsp_message_parser_init(motu, motu->tx_stream.sfc);
+ if (err < 0)
+ return err;
+ }
+
err = begin_session(motu);
if (err < 0) {
dev_err(&motu->unit->device,
@@ -260,14 +282,19 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
if (err < 0)
goto stop_streams;
- err = amdtp_domain_start(&motu->domain, 0);
+ motu->cache.tail = 0;
+ motu->cache.tx_cycle_count = UINT_MAX;
+ motu->cache.head = 0;
+ motu->cache.rx_cycle_count = UINT_MAX;
+
+ // NOTE: The device requires both of replay; the sequence of the number of data
+ // blocks per packet, and the sequence of source packet header per data block as
+ // presentation time.
+ err = amdtp_domain_start(&motu->domain, 0, true, false);
if (err < 0)
goto stop_streams;
- if (!amdtp_stream_wait_callback(&motu->tx_stream,
- CALLBACK_TIMEOUT) ||
- !amdtp_stream_wait_callback(&motu->rx_stream,
- CALLBACK_TIMEOUT)) {
+ if (!amdtp_domain_wait_ready(&motu->domain, READY_TIMEOUT_MS)) {
err = -ETIMEDOUT;
goto stop_streams;
}
@@ -296,6 +323,9 @@ void snd_motu_stream_stop_duplex(struct snd_motu *motu)
fw_iso_resources_free(&motu->tx_resources);
fw_iso_resources_free(&motu->rx_resources);
+
+ kfree(motu->cache.event_offsets);
+ motu->cache.event_offsets = NULL;
}
}
@@ -317,7 +347,7 @@ static int init_stream(struct snd_motu *motu, struct amdtp_stream *s)
if (err < 0)
return err;
- err = amdtp_motu_init(s, motu->unit, dir, motu->spec);
+ err = amdtp_motu_init(s, motu->unit, dir, motu->spec, &motu->cache);
if (err < 0)
fw_iso_resources_destroy(resources);
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
index a4929c1302dc..d14ab5dd5bea 100644
--- a/sound/firewire/motu/motu.c
+++ b/sound/firewire/motu/motu.c
@@ -11,7 +11,7 @@
MODULE_DESCRIPTION("MOTU FireWire driver");
MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");
const unsigned int snd_motu_clock_rates[SND_MOTU_CLOCK_RATE_COUNT] = {
/* mode 0 */
@@ -57,22 +57,31 @@ static void motu_card_free(struct snd_card *card)
snd_motu_transaction_unregister(motu);
snd_motu_stream_destroy_duplex(motu);
+
+ mutex_destroy(&motu->mutex);
+ fw_unit_put(motu->unit);
}
-static void do_registration(struct work_struct *work)
+static int motu_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
{
- struct snd_motu *motu = container_of(work, struct snd_motu, dwork.work);
+ struct snd_card *card;
+ struct snd_motu *motu;
int err;
- if (motu->registered)
- return;
-
- err = snd_card_new(&motu->unit->device, -1, NULL, THIS_MODULE, 0,
- &motu->card);
+ err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*motu), &card);
if (err < 0)
- return;
- motu->card->private_free = motu_card_free;
- motu->card->private_data = motu;
+ return err;
+ card->private_free = motu_card_free;
+
+ motu = card->private_data;
+ motu->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, motu);
+ motu->card = card;
+
+ motu->spec = (const struct snd_motu_spec *)entry->driver_data;
+ mutex_init(&motu->mutex);
+ spin_lock_init(&motu->lock);
+ init_waitqueue_head(&motu->hwdep_wait);
name_card(motu);
@@ -103,71 +112,38 @@ static void do_registration(struct work_struct *work)
if (err < 0)
goto error;
- err = snd_card_register(motu->card);
+ if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) {
+ err = snd_motu_register_dsp_message_parser_new(motu);
+ if (err < 0)
+ goto error;
+ } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) {
+ err = snd_motu_command_dsp_message_parser_new(motu);
+ if (err < 0)
+ goto error;
+ }
+
+ err = snd_card_register(card);
if (err < 0)
goto error;
- motu->registered = true;
-
- return;
-error:
- snd_card_free(motu->card);
- dev_info(&motu->unit->device,
- "Sound card registration failed: %d\n", err);
-}
-
-static int motu_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
-{
- struct snd_motu *motu;
-
- /* Allocate this independently of sound card instance. */
- motu = devm_kzalloc(&unit->device, sizeof(struct snd_motu), GFP_KERNEL);
- if (!motu)
- return -ENOMEM;
- motu->unit = fw_unit_get(unit);
- dev_set_drvdata(&unit->device, motu);
-
- motu->spec = (const struct snd_motu_spec *)entry->driver_data;
- mutex_init(&motu->mutex);
- spin_lock_init(&motu->lock);
- init_waitqueue_head(&motu->hwdep_wait);
-
- /* Allocate and register this sound card later. */
- INIT_DEFERRABLE_WORK(&motu->dwork, do_registration);
- snd_fw_schedule_registration(unit, &motu->dwork);
-
return 0;
+error:
+ snd_card_free(card);
+ return err;
}
static void motu_remove(struct fw_unit *unit)
{
struct snd_motu *motu = dev_get_drvdata(&unit->device);
- /*
- * Confirm to stop the work for registration before the sound card is
- * going to be released. The work is not scheduled again because bus
- * reset handler is not called anymore.
- */
- cancel_delayed_work_sync(&motu->dwork);
-
- if (motu->registered) {
- // Block till all of ALSA character devices are released.
- snd_card_free(motu->card);
- }
-
- mutex_destroy(&motu->mutex);
- fw_unit_put(motu->unit);
+ // Block till all of ALSA character devices are released.
+ snd_card_free(motu->card);
}
static void motu_bus_update(struct fw_unit *unit)
{
struct snd_motu *motu = dev_get_drvdata(&unit->device);
- /* Postpone a workqueue for deferred registration. */
- if (!motu->registered)
- snd_fw_schedule_registration(unit, &motu->dwork);
-
/* The handler address register becomes initialized. */
snd_motu_transaction_reregister(motu);
}
@@ -184,14 +160,22 @@ static void motu_bus_update(struct fw_unit *unit)
}
static const struct ieee1394_device_id motu_id_table[] = {
+ SND_MOTU_DEV_ENTRY(0x000001, &snd_motu_spec_828),
+ SND_MOTU_DEV_ENTRY(0x000002, &snd_motu_spec_896),
SND_MOTU_DEV_ENTRY(0x000003, &snd_motu_spec_828mk2),
+ SND_MOTU_DEV_ENTRY(0x000005, &snd_motu_spec_896hd),
SND_MOTU_DEV_ENTRY(0x000009, &snd_motu_spec_traveler),
SND_MOTU_DEV_ENTRY(0x00000d, &snd_motu_spec_ultralite),
SND_MOTU_DEV_ENTRY(0x00000f, &snd_motu_spec_8pre),
- SND_MOTU_DEV_ENTRY(0x000015, &snd_motu_spec_828mk3), // FireWire only.
+ SND_MOTU_DEV_ENTRY(0x000015, &snd_motu_spec_828mk3_fw), // FireWire only.
+ SND_MOTU_DEV_ENTRY(0x000017, &snd_motu_spec_896mk3), // FireWire only.
SND_MOTU_DEV_ENTRY(0x000019, &snd_motu_spec_ultralite_mk3), // FireWire only.
- SND_MOTU_DEV_ENTRY(0x000035, &snd_motu_spec_828mk3), // Hybrid.
+ SND_MOTU_DEV_ENTRY(0x00001b, &snd_motu_spec_traveler_mk3),
+ SND_MOTU_DEV_ENTRY(0x000030, &snd_motu_spec_ultralite_mk3), // Hybrid.
+ SND_MOTU_DEV_ENTRY(0x000035, &snd_motu_spec_828mk3_hybrid), // Hybrid.
+ SND_MOTU_DEV_ENTRY(0x000037, &snd_motu_spec_896mk3), // Hybrid.
SND_MOTU_DEV_ENTRY(0x000033, &snd_motu_spec_audio_express),
+ SND_MOTU_DEV_ENTRY(0x000039, &snd_motu_spec_track16),
SND_MOTU_DEV_ENTRY(0x000045, &snd_motu_spec_4pre),
{ }
};
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index 3d0236ee6716..c66be0a89ccf 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -39,15 +39,21 @@ struct snd_motu_packet_format {
unsigned char pcm_chunks[3];
};
+struct amdtp_motu_cache {
+ unsigned int *event_offsets;
+ unsigned int size;
+ unsigned int tail;
+ unsigned int tx_cycle_count;
+ unsigned int head;
+ unsigned int rx_cycle_count;
+};
+
struct snd_motu {
struct snd_card *card;
struct fw_unit *unit;
struct mutex mutex;
spinlock_t lock;
- bool registered;
- struct delayed_work dwork;
-
/* Model dependent information. */
const struct snd_motu_spec *spec;
@@ -68,8 +74,13 @@ struct snd_motu {
int dev_lock_count;
bool dev_lock_changed;
wait_queue_head_t hwdep_wait;
+ struct snd_hwdep *hwdep;
struct amdtp_domain domain;
+
+ struct amdtp_motu_cache cache;
+
+ void *message_parser;
};
enum snd_motu_spec_flags {
@@ -77,6 +88,8 @@ enum snd_motu_spec_flags {
SND_MOTU_SPEC_RX_MIDI_3RD_Q = 0x0002,
SND_MOTU_SPEC_TX_MIDI_2ND_Q = 0x0004,
SND_MOTU_SPEC_TX_MIDI_3RD_Q = 0x0008,
+ SND_MOTU_SPEC_REGISTER_DSP = 0x0010,
+ SND_MOTU_SPEC_COMMAND_DSP = 0x0020,
};
#define SND_MOTU_CLOCK_RATE_COUNT 6
@@ -99,6 +112,7 @@ enum snd_motu_clock_source {
};
enum snd_motu_protocol_version {
+ SND_MOTU_PROTOCOL_V1,
SND_MOTU_PROTOCOL_V2,
SND_MOTU_PROTOCOL_V3,
};
@@ -106,25 +120,35 @@ enum snd_motu_protocol_version {
struct snd_motu_spec {
const char *const name;
enum snd_motu_protocol_version protocol_version;
- enum snd_motu_spec_flags flags;
+ // The combination of snd_motu_spec_flags enumeration-constants.
+ unsigned int flags;
unsigned char tx_fixed_pcm_chunks[3];
unsigned char rx_fixed_pcm_chunks[3];
};
+extern const struct snd_motu_spec snd_motu_spec_828;
+extern const struct snd_motu_spec snd_motu_spec_896;
+
extern const struct snd_motu_spec snd_motu_spec_828mk2;
+extern const struct snd_motu_spec snd_motu_spec_896hd;
extern const struct snd_motu_spec snd_motu_spec_traveler;
extern const struct snd_motu_spec snd_motu_spec_ultralite;
extern const struct snd_motu_spec snd_motu_spec_8pre;
-extern const struct snd_motu_spec snd_motu_spec_828mk3;
+extern const struct snd_motu_spec snd_motu_spec_828mk3_fw;
+extern const struct snd_motu_spec snd_motu_spec_828mk3_hybrid;
+extern const struct snd_motu_spec snd_motu_spec_896mk3;
+extern const struct snd_motu_spec snd_motu_spec_traveler_mk3;
extern const struct snd_motu_spec snd_motu_spec_ultralite_mk3;
extern const struct snd_motu_spec snd_motu_spec_audio_express;
+extern const struct snd_motu_spec snd_motu_spec_track16;
extern const struct snd_motu_spec snd_motu_spec_4pre;
int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir,
- const struct snd_motu_spec *spec);
+ const struct snd_motu_spec *spec,
+ struct amdtp_motu_cache *cache);
int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
unsigned int midi_ports,
struct snd_motu_packet_format *formats);
@@ -160,6 +184,16 @@ int snd_motu_create_midi_devices(struct snd_motu *motu);
int snd_motu_create_hwdep_device(struct snd_motu *motu);
+int snd_motu_protocol_v1_get_clock_rate(struct snd_motu *motu,
+ unsigned int *rate);
+int snd_motu_protocol_v1_set_clock_rate(struct snd_motu *motu,
+ unsigned int rate);
+int snd_motu_protocol_v1_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src);
+int snd_motu_protocol_v1_switch_fetching_mode(struct snd_motu *motu,
+ bool enable);
+int snd_motu_protocol_v1_cache_packet_formats(struct snd_motu *motu);
+
int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu,
unsigned int *rate);
int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu,
@@ -187,6 +221,8 @@ static inline int snd_motu_protocol_get_clock_rate(struct snd_motu *motu,
return snd_motu_protocol_v2_get_clock_rate(motu, rate);
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
return snd_motu_protocol_v3_get_clock_rate(motu, rate);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
+ return snd_motu_protocol_v1_get_clock_rate(motu, rate);
else
return -ENXIO;
}
@@ -198,6 +234,8 @@ static inline int snd_motu_protocol_set_clock_rate(struct snd_motu *motu,
return snd_motu_protocol_v2_set_clock_rate(motu, rate);
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
return snd_motu_protocol_v3_set_clock_rate(motu, rate);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
+ return snd_motu_protocol_v1_set_clock_rate(motu, rate);
else
return -ENXIO;
}
@@ -209,6 +247,8 @@ static inline int snd_motu_protocol_get_clock_source(struct snd_motu *motu,
return snd_motu_protocol_v2_get_clock_source(motu, source);
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
return snd_motu_protocol_v3_get_clock_source(motu, source);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
+ return snd_motu_protocol_v1_get_clock_source(motu, source);
else
return -ENXIO;
}
@@ -220,6 +260,8 @@ static inline int snd_motu_protocol_switch_fetching_mode(struct snd_motu *motu,
return snd_motu_protocol_v2_switch_fetching_mode(motu, enable);
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
return snd_motu_protocol_v3_switch_fetching_mode(motu, enable);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
+ return snd_motu_protocol_v1_switch_fetching_mode(motu, enable);
else
return -ENXIO;
}
@@ -230,8 +272,28 @@ static inline int snd_motu_protocol_cache_packet_formats(struct snd_motu *motu)
return snd_motu_protocol_v2_cache_packet_formats(motu);
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
return snd_motu_protocol_v3_cache_packet_formats(motu);
+ else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
+ return snd_motu_protocol_v1_cache_packet_formats(motu);
else
return -ENXIO;
}
+int snd_motu_register_dsp_message_parser_new(struct snd_motu *motu);
+int snd_motu_register_dsp_message_parser_init(struct snd_motu *motu);
+void snd_motu_register_dsp_message_parser_parse(const struct amdtp_stream *s,
+ const struct pkt_desc *descs, unsigned int count);
+void snd_motu_register_dsp_message_parser_copy_meter(struct snd_motu *motu,
+ struct snd_firewire_motu_register_dsp_meter *meter);
+void snd_motu_register_dsp_message_parser_copy_parameter(struct snd_motu *motu,
+ struct snd_firewire_motu_register_dsp_parameter *params);
+unsigned int snd_motu_register_dsp_message_parser_count_event(struct snd_motu *motu);
+bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32 *event);
+
+int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu);
+int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc);
+void snd_motu_command_dsp_message_parser_parse(const struct amdtp_stream *s,
+ const struct pkt_desc *descs, unsigned int count);
+void snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu,
+ struct snd_firewire_motu_command_dsp_meter *meter);
+
#endif