diff options
Diffstat (limited to 'sound/firewire/motu')
-rw-r--r-- | sound/firewire/motu/Makefile | 4 | ||||
-rw-r--r-- | sound/firewire/motu/amdtp-motu.c | 199 | ||||
-rw-r--r-- | sound/firewire/motu/motu-command-dsp-message-parser.c | 184 | ||||
-rw-r--r-- | sound/firewire/motu/motu-hwdep.c | 125 | ||||
-rw-r--r-- | sound/firewire/motu/motu-midi.c | 4 | ||||
-rw-r--r-- | sound/firewire/motu/motu-pcm.c | 4 | ||||
-rw-r--r-- | sound/firewire/motu/motu-protocol-v1.c | 467 | ||||
-rw-r--r-- | sound/firewire/motu/motu-protocol-v2.c | 209 | ||||
-rw-r--r-- | sound/firewire/motu/motu-protocol-v3.c | 142 | ||||
-rw-r--r-- | sound/firewire/motu/motu-register-dsp-message-parser.c | 423 | ||||
-rw-r--r-- | sound/firewire/motu/motu-stream.c | 44 | ||||
-rw-r--r-- | sound/firewire/motu/motu.c | 108 | ||||
-rw-r--r-- | sound/firewire/motu/motu.h | 74 |
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, ®, 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, ®, 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, ®, 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, ®, 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, ®, 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, ®, 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, ®, 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, ®, 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, ®, 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, ®, 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, ®, 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, ®, 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, ®, 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, - ®, 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, ®, + 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, ®, + 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, ®, 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, ®, - 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 |