From 83d8d72dffa87a6dcecce229617273be62ec5bc0 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 25 Apr 2014 22:44:47 +0900 Subject: [PATCH] ALSA: firewire-lib: Add support for MIDI capture/playback For capturing/playbacking MIDI messages, this commit adds one MIDI conformant data channel. This data channel has multiplexed 8 MIDI data streams. So this data channel can transfer messages from/to 8 MIDI ports. And this commit allows to set PCM format even if AMDTP streams already start. I suppose the case that PCM substreams are going to be joined into AMDTP streams when AMDTP streams are already started for MIDI substreams. Each driver must count how many PCM/MIDI substreams use AMDTP streams to stop AMDTP streams. There are differences between specifications about MIDI conformant data. About the multiplexing, IEC 61883-6:2002, itself, has no information. It describes labels and bytes for MIDI messages and refers to MMA/AMEI RP-027 for 'successfull implementation'. MMA/AMEI RP-027 describes 8 MPX-MIDI data streams for one MIDI conformant data channel. IEC 61883-6:2005 adds 'sequence multiplexing' and apply this way and describe incompatibility between 2002 and 2005. So this commit applies IEC 61883-6:2005. When we find some devices compliant to IEC 61883-6:2002, then this difference should be handles as device quirk in additional work. About the number of bytes in an MIDI conformant data, IEC 61883-6:2002 describe 0,1,2,3 bytes. MMA/AMEI RP-027 describes 'MIDI1.0-1x-SPEED', 'MIDI1.0-2x-SPEED', 'MIDI1.0-3x-SPEED' modes and the maximum bytes for each mode corresponds to 1, 2, 3 bytes. The 'MIDI1.0-2x/3x-SPEED' modes are accompanied with 'negotiation procedure' and 'encapsulation details' but there is no specifications for them. So this commit implements 'MIDI1.0-1x-SPEED' mode for playback, but allows to pick up 1-3 bytes for capturing. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp.c | 53 +++++++++++++++++++++++++++++++++++------- sound/firewire/amdtp.h | 43 ++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 8 deletions(-) diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index be1aabcda0d2..a4553ec1b63e 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "amdtp.h" #define TICKS_PER_CYCLE 3072 @@ -123,9 +124,12 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s, [CIP_SFC_176400] = 176400, [CIP_SFC_192000] = 192000, }; - unsigned int sfc; + unsigned int sfc, midi_channels; - if (WARN_ON(amdtp_stream_running(s))) + midi_channels = DIV_ROUND_UP(midi_ports, 8); + + if (WARN_ON(amdtp_stream_running(s)) || + WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI)) return; for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc) @@ -142,7 +146,7 @@ sfc_found: pcm_channels *= 2; } s->sfc = sfc; - s->data_block_quadlets = pcm_channels + DIV_ROUND_UP(midi_ports, 8); + s->data_block_quadlets = pcm_channels + midi_channels; s->pcm_channels = pcm_channels; s->midi_ports = midi_ports; @@ -200,7 +204,7 @@ static void amdtp_read_s32_dualwire(struct amdtp_stream *s, void amdtp_stream_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format) { - if (WARN_ON(amdtp_stream_running(s))) + if (WARN_ON(amdtp_stream_pcm_running(s))) return; switch (format) { @@ -507,11 +511,41 @@ static void amdtp_fill_pcm_silence(struct amdtp_stream *s, static void amdtp_fill_midi(struct amdtp_stream *s, __be32 *buffer, unsigned int frames) { - unsigned int i; + unsigned int f, port; + u8 *b; - for (i = 0; i < frames; ++i) - buffer[s->pcm_channels + i * s->data_block_quadlets] = - cpu_to_be32(0x80000000); + for (f = 0; f < frames; f++) { + buffer[s->pcm_channels + 1] = 0; + b = (u8 *)&buffer[s->pcm_channels + 1]; + + port = (s->data_block_counter + f) % 8; + if ((s->midi[port] == NULL) || + (snd_rawmidi_transmit(s->midi[port], b + 1, 1) <= 0)) + b[0] = 0x80; + else + b[0] = 0x81; + + buffer += s->data_block_quadlets; + } +} + +static void amdtp_pull_midi(struct amdtp_stream *s, + __be32 *buffer, unsigned int frames) +{ + unsigned int f, port; + int len; + u8 *b; + + for (f = 0; f < frames; f++) { + port = (s->data_block_counter + f) % 8; + b = (u8 *)&buffer[s->pcm_channels + 1]; + + len = b[0] - 0x80; + if ((1 <= len) && (len <= 3) && (s->midi[port])) + snd_rawmidi_receive(s->midi[port], b + 1, len); + + buffer += s->data_block_quadlets; + } } static void update_pcm_pointers(struct amdtp_stream *s, @@ -688,6 +722,9 @@ static void handle_in_packet(struct amdtp_stream *s, pcm = ACCESS_ONCE(s->pcm); if (pcm) s->transfer_samples(s, pcm, buffer, data_blocks); + + if (s->midi_ports) + amdtp_pull_midi(s, buffer, data_blocks); } s->data_block_counter = (data_block_counter + data_blocks) & 0xff; diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index c831aaa2a811..098187d4b499 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -46,9 +46,21 @@ enum cip_sfc { #define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ SNDRV_PCM_FMTBIT_S32) + +/* + * AMDTP packet can include channels for MIDI conformant data. + * Each MIDI conformant data channel includes 8 MPX-MIDI data stream. + * Each MPX-MIDI data stream includes one data stream from/to MIDI ports. + * + * This module supports maximum 1 MIDI conformant data channels. + * Then this AMDTP packets can transfer maximum 8 MIDI data streams. + */ +#define AMDTP_MAX_CHANNELS_FOR_MIDI 1 + struct fw_unit; struct fw_iso_context; struct snd_pcm_substream; +struct snd_rawmidi_substream; enum amdtp_stream_direction { AMDTP_OUT_STREAM = 0, @@ -90,6 +102,8 @@ struct amdtp_stream { unsigned int pcm_buffer_pointer; unsigned int pcm_period_pointer; bool pointer_flush; + + struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; }; int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, @@ -138,6 +152,17 @@ static inline bool amdtp_streaming_error(struct amdtp_stream *s) return s->packet_index < 0; } +/** + * amdtp_stream_pcm_running - check PCM substream is running or not + * @s: the AMDTP stream + * + * If this function returns true, PCM substream in the AMDTP stream is running. + */ +static inline bool amdtp_stream_pcm_running(struct amdtp_stream *s) +{ + return !!s->pcm; +} + /** * amdtp_stream_pcm_trigger - start/stop playback from a PCM device * @s: the AMDTP stream @@ -153,6 +178,24 @@ static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s, ACCESS_ONCE(s->pcm) = pcm; } +/** + * amdtp_stream_midi_trigger - start/stop playback/capture with a MIDI device + * @s: the AMDTP stream + * @port: index of MIDI port + * @midi: the MIDI device to be started, or %NULL to stop the current device + * + * Call this function on a running isochronous stream to enable the actual + * transmission of MIDI data. This function should be called from the MIDI + * device's .trigger callback. + */ +static inline void amdtp_stream_midi_trigger(struct amdtp_stream *s, + unsigned int port, + struct snd_rawmidi_substream *midi) +{ + if (port < s->midi_ports) + ACCESS_ONCE(s->midi[port]) = midi; +} + static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc) { return sfc & 1;