diff options
-rw-r--r-- | sound/firewire/digi00x/amdtp-dot.c | 120 | ||||
-rw-r--r-- | sound/firewire/digi00x/digi00x-stream.c | 4 | ||||
-rw-r--r-- | sound/firewire/digi00x/digi00x.h | 9 |
3 files changed, 125 insertions, 8 deletions
diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c index c12ce4d0821a..b02a5e8cad44 100644 --- a/sound/firewire/digi00x/amdtp-dot.c +++ b/sound/firewire/digi00x/amdtp-dot.c @@ -17,6 +17,18 @@ #define AMDTP_FDF_AM824 0x00 /* + * Nominally 3125 bytes/second, but the MIDI port's clock might be + * 1% too slow, and the bus clock 100 ppm too fast. + */ +#define MIDI_BYTES_PER_SECOND 3093 + +/* + * Several devices look only at the first eight data blocks. + * In any case, this is more than enough for the MIDI data rate. + */ +#define MAX_MIDI_RX_BLOCKS 8 + +/* * The double-oh-three algorithm was discovered by Robin Gareus and Damien * Zammit in 2012, with reverse-engineering for Digi 003 Rack. */ @@ -31,6 +43,10 @@ struct amdtp_dot { struct dot_state state; unsigned int midi_ports; + /* 2 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) */ + struct snd_rawmidi_substream *midi[2]; + int midi_fifo_used[2]; + int midi_fifo_limit; void (*transfer_samples)(struct amdtp_stream *s, struct snd_pcm_substream *pcm, @@ -99,7 +115,7 @@ static void dot_encode_step(struct dot_state *state, __be32 *const buffer) } int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate, - unsigned int pcm_channels, unsigned int midi_ports) + unsigned int pcm_channels) { struct amdtp_dot *p = s->protocol; int err; @@ -118,7 +134,19 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate, s->fdf = AMDTP_FDF_AM824 | s->sfc; p->pcm_channels = pcm_channels; - p->midi_ports = midi_ports; + + if (s->direction == AMDTP_IN_STREAM) + p->midi_ports = DOT_MIDI_IN_PORTS; + else + p->midi_ports = DOT_MIDI_OUT_PORTS; + + /* + * We do not know the actual MIDI FIFO size of most devices. Just + * assume two bytes, i.e., one byte can be received over the bus while + * the previous one is transmitted over MIDI. + * (The value here is adjusted for midi_ratelimit_per_packet().) + */ + p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1; return 0; } @@ -216,6 +244,81 @@ static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer, } } +static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port) +{ + struct amdtp_dot *p = s->protocol; + int used; + + used = p->midi_fifo_used[port]; + if (used == 0) + return true; + + used -= MIDI_BYTES_PER_SECOND * s->syt_interval; + used = max(used, 0); + p->midi_fifo_used[port] = used; + + return used < p->midi_fifo_limit; +} + +static inline void midi_use_bytes(struct amdtp_stream *s, + unsigned int port, unsigned int count) +{ + struct amdtp_dot *p = s->protocol; + + p->midi_fifo_used[port] += amdtp_rate_table[s->sfc] * count; +} + +static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks) +{ + struct amdtp_dot *p = s->protocol; + unsigned int f, port; + int len; + u8 *b; + + for (f = 0; f < data_blocks; f++) { + port = (s->data_block_counter + f) % 8; + b = (u8 *)&buffer[0]; + + len = 0; + if (port < p->midi_ports && + midi_ratelimit_per_packet(s, port) && + p->midi[port] != NULL) + len = snd_rawmidi_transmit(p->midi[port], b + 1, 2); + + if (len > 0) { + b[3] = (0x10 << port) | len; + midi_use_bytes(s, port, len); + } else { + b[1] = 0; + b[2] = 0; + b[3] = 0; + } + b[0] = 0x80; + + buffer += s->data_block_quadlets; + } +} + +static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks) +{ + struct amdtp_dot *p = s->protocol; + unsigned int f, port, len; + u8 *b; + + for (f = 0; f < data_blocks; f++) { + b = (u8 *)&buffer[0]; + port = b[3] >> 4; + len = b[3] & 0x0f; + + if (port < p->midi_ports && p->midi[port] && len > 0) + snd_rawmidi_receive(p->midi[port], b + 1, len); + + buffer += s->data_block_quadlets; + } +} + int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime) { @@ -256,6 +359,15 @@ void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format) } } +void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port, + struct snd_rawmidi_substream *midi) +{ + struct amdtp_dot *p = s->protocol; + + if (port < p->midi_ports) + ACCESS_ONCE(p->midi[port]) = midi; +} + static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, unsigned int data_blocks, @@ -273,7 +385,7 @@ static unsigned int process_tx_data_blocks(struct amdtp_stream *s, pcm_frames = 0; } - /* A place holder for MIDI processing. */ + read_midi_messages(s, buffer, data_blocks); return pcm_frames; } @@ -296,7 +408,7 @@ static unsigned int process_rx_data_blocks(struct amdtp_stream *s, pcm_frames = 0; } - /* A place holder for MIDI processing. */ + write_midi_messages(s, buffer, data_blocks); return pcm_frames; } diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c index 8aac31be3132..e9be162fc895 100644 --- a/sound/firewire/digi00x/digi00x-stream.c +++ b/sound/firewire/digi00x/digi00x-stream.c @@ -199,7 +199,7 @@ static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate) /* Keep resources for out-stream. */ err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate, - snd_dg00x_stream_pcm_channels[i], 0); + snd_dg00x_stream_pcm_channels[i]); if (err < 0) return err; err = fw_iso_resources_allocate(&dg00x->rx_resources, @@ -210,7 +210,7 @@ static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate) /* Keep resources for in-stream. */ err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate, - snd_dg00x_stream_pcm_channels[i], 0); + snd_dg00x_stream_pcm_channels[i]); if (err < 0) return err; err = fw_iso_resources_allocate(&dg00x->tx_resources, diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index b960c868b59b..88df67be5477 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -24,6 +24,7 @@ #include <sound/pcm_params.h> #include <sound/firewire.h> #include <sound/hwdep.h> +#include <sound/rawmidi.h> #include "../lib.h" #include "../iso-resources.h" @@ -103,15 +104,19 @@ enum snd_dg00x_optical_mode { SND_DG00X_OPT_IFACE_MODE_COUNT, }; +#define DOT_MIDI_IN_PORTS 1 +#define DOT_MIDI_OUT_PORTS 2 + int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir); int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate, - unsigned int pcm_channels, - unsigned int midi_ports); + unsigned int pcm_channels); void amdtp_dot_reset(struct amdtp_stream *s); int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format); +void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port, + struct snd_rawmidi_substream *midi); int snd_dg00x_transaction_register(struct snd_dg00x *dg00x); int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x); |