From 10b2b6dc1a6bb287411045c788f76d53f96c11bc Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:48 +0900 Subject: ALSA: firewire-lib: arrange structure for AMDTP stream In later commit, some members related to AM824 data format will be moved from AMDTP stream structure to data block structure. This commit is a preparation for it. Additionally, current layout of AMDTP stream structure is a bit mess by several extensions. This commit also arranges the layout. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp.h | 58 +++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 27 deletions(-) (limited to 'sound/firewire') diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index b2cf9e75693b..4640d2b35fb8 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -117,52 +117,56 @@ struct amdtp_stream { struct fw_unit *unit; enum cip_flags flags; enum amdtp_stream_direction direction; - struct fw_iso_context *context; struct mutex mutex; - enum cip_sfc sfc; - unsigned int data_block_quadlets; - unsigned int pcm_channels; - unsigned int midi_ports; - void (*transfer_samples)(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); - u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM]; - u8 midi_position; - - unsigned int syt_interval; - unsigned int transfer_delay; - unsigned int source_node_id_field; + /* For packet processing. */ + struct fw_iso_context *context; struct iso_packets_buffer buffer; - - struct snd_pcm_substream *pcm; - struct tasklet_struct period_tasklet; - int packet_index; + + /* For CIP headers. */ + unsigned int source_node_id_field; + unsigned int data_block_quadlets; unsigned int data_block_counter; + /* quirk: fixed interval of dbc between previos/current packets. */ + unsigned int tx_dbc_interval; + /* quirk: indicate the value of dbc field in a first packet. */ + unsigned int tx_first_dbc; + /* Internal flags. */ + enum cip_sfc sfc; + unsigned int syt_interval; + unsigned int transfer_delay; unsigned int data_block_state; - unsigned int last_syt_offset; unsigned int syt_offset_state; + /* For a PCM substream processing. */ + struct snd_pcm_substream *pcm; + struct tasklet_struct period_tasklet; unsigned int pcm_buffer_pointer; unsigned int pcm_period_pointer; bool pointer_flush; - bool double_pcm_frames; + /* To wait for first packet. */ + bool callbacked; + wait_queue_head_t callback_wait; + struct amdtp_stream *sync_slave; + + /* For AM824 processing. */ struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; int midi_fifo_limit; int midi_fifo_used[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; + unsigned int pcm_channels; + unsigned int midi_ports; - /* quirk: fixed interval of dbc between previos/current packets. */ - unsigned int tx_dbc_interval; - /* quirk: indicate the value of dbc field in a first packet. */ - unsigned int tx_first_dbc; + u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM]; + u8 midi_position; + bool double_pcm_frames; - bool callbacked; - wait_queue_head_t callback_wait; - struct amdtp_stream *sync_slave; + void (*transfer_samples)(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); }; int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, -- cgit v1.2.3-55-g7522 From 547e631ce3886175a33b5ccf67729bdd18e9b7e0 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:49 +0900 Subject: ALSA: firewire-lib: return error code when amdtp_stream_set_parameters() detects error Currently, amdtp_stream_set_parameters() returns no error even if wrong arguments are given. This is not good for streaming layer because drivers can continue processing ignoring capability of streaming layer. This commit changes this function to return error code. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp.c | 22 ++++++++++++---------- sound/firewire/amdtp.h | 8 ++++---- sound/firewire/bebob/bebob_stream.c | 13 +++++++++---- sound/firewire/dice/dice-stream.c | 5 ++++- sound/firewire/fireworks/fireworks_stream.c | 6 ++++-- sound/firewire/oxfw/oxfw-stream.c | 5 ++++- 6 files changed, 37 insertions(+), 22 deletions(-) (limited to 'sound/firewire') diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 2a153d260836..2bacb5173ff8 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -197,10 +197,10 @@ EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints); * The parameters must be set before the stream is started, and must not be * changed while the stream is running. */ -void amdtp_stream_set_parameters(struct amdtp_stream *s, - unsigned int rate, - unsigned int pcm_channels, - unsigned int midi_ports) +int amdtp_stream_set_parameters(struct amdtp_stream *s, + unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports) { unsigned int i, sfc, midi_channels; @@ -209,15 +209,15 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s, if (WARN_ON(amdtp_stream_running(s)) | WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) | WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI)) - return; + return -EINVAL; - for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) + for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) { if (amdtp_rate_table[sfc] == rate) - goto sfc_found; - WARN_ON(1); - return; + break; + } + if (sfc == ARRAY_SIZE(amdtp_rate_table)) + return -EINVAL; -sfc_found: s->pcm_channels = pcm_channels; s->sfc = sfc; s->data_block_quadlets = s->pcm_channels + midi_channels; @@ -243,6 +243,8 @@ sfc_found: * (The value here is adjusted for midi_ratelimit_per_packet().) */ s->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1; + + return 0; } EXPORT_SYMBOL(amdtp_stream_set_parameters); diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 4640d2b35fb8..3fb8db7ecd68 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -174,10 +174,10 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, enum cip_flags flags); void amdtp_stream_destroy(struct amdtp_stream *s); -void amdtp_stream_set_parameters(struct amdtp_stream *s, - unsigned int rate, - unsigned int pcm_channels, - unsigned int midi_ports); +int amdtp_stream_set_parameters(struct amdtp_stream *s, + unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports); unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s); int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed); diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 5be5242e1ed8..c642b79e7ed4 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -427,12 +427,17 @@ make_both_connections(struct snd_bebob *bebob, unsigned int rate) index = get_formation_index(rate); pcm_channels = bebob->tx_stream_formations[index].pcm; midi_channels = bebob->tx_stream_formations[index].midi; - amdtp_stream_set_parameters(&bebob->tx_stream, - rate, pcm_channels, midi_channels * 8); + err = amdtp_stream_set_parameters(&bebob->tx_stream, rate, + pcm_channels, midi_channels * 8); + if (err < 0) + goto end; + pcm_channels = bebob->rx_stream_formations[index].pcm; midi_channels = bebob->rx_stream_formations[index].midi; - amdtp_stream_set_parameters(&bebob->rx_stream, - rate, pcm_channels, midi_channels * 8); + err = amdtp_stream_set_parameters(&bebob->rx_stream, rate, + pcm_channels, midi_channels * 8); + if (err < 0) + goto end; /* establish connections for both streams */ err = cmp_connection_establish(&bebob->out_conn, diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 07dbd01d7a6b..c96306adcae0 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -133,7 +133,10 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, stream->double_pcm_frames = false; } - amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports); + err = amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports); + if (err < 0) + goto end; + if (mode > 1) { pcm_chs /= 2; diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index 7e353f1f7bff..dfefccff3c55 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -73,8 +73,10 @@ start_stream(struct snd_efw *efw, struct amdtp_stream *stream, midi_ports = efw->midi_in_ports; } - amdtp_stream_set_parameters(stream, sampling_rate, - pcm_channels, midi_ports); + err = amdtp_stream_set_parameters(stream, sampling_rate, + pcm_channels, midi_ports); + if (err < 0) + goto end; /* establish connection via CMP */ err = cmp_connection_establish(conn, diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 77ad5b98e806..d119468fedf4 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -155,7 +155,10 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream, err = -EINVAL; goto end; } - amdtp_stream_set_parameters(stream, rate, pcm_channels, midi_ports); + err = amdtp_stream_set_parameters(stream, rate, + pcm_channels, midi_ports); + if (err < 0) + goto end; err = cmp_connection_establish(conn, amdtp_stream_get_max_payload(stream)); -- cgit v1.2.3-55-g7522 From 27ec83b5c6ac08599240ec9a95286e79d6ea9e51 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:50 +0900 Subject: ALSA: firewire-lib: add an argument for Dice's dual wire mode In IEC 61883-6, one data block represents one event. In ALSA, the event is one PCM frame. Therefore, when processing one data block, current implementation counts one PCM frame. On the other hand, Dice platform has a quirk called as 'dual wire' at higher sampling rate. In detail, see comment of commit 6eb6c81eee2a ("ALSA: dice: Split stream functionality into a file"). Currently, to handle this quirk, AMDTP stream structure has a 'double_pcm_frames' member. When this is enabled, two PCM frames are counted. Each driver set this flag by accessing the structure member directly. In future commit, some members related to AM824 data block will be moved to specific structure, to separate packet streaming layer and data block processing layer. The access will be limited by opaque pointer. For this reason, this commit adds an argument into amdtp_stream_set_parameter() to set the flag. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp.c | 4 +++- sound/firewire/amdtp.h | 3 ++- sound/firewire/bebob/bebob_stream.c | 6 ++++-- sound/firewire/dice/dice-stream.c | 12 ++++++------ sound/firewire/fireworks/fireworks_stream.c | 2 +- sound/firewire/oxfw/oxfw-stream.c | 2 +- 6 files changed, 17 insertions(+), 12 deletions(-) (limited to 'sound/firewire') diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 2bacb5173ff8..094982f7c0fd 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -193,6 +193,7 @@ EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints); * @pcm_channels: the number of PCM samples in each data block, to be encoded * as AM824 multi-bit linear audio * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) + * @double_pcm_frames: one data block transfers two PCM frames * * The parameters must be set before the stream is started, and must not be * changed while the stream is running. @@ -200,7 +201,8 @@ EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints); int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, unsigned int pcm_channels, - unsigned int midi_ports) + unsigned int midi_ports, + bool double_pcm_frames) { unsigned int i, sfc, midi_channels; diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 3fb8db7ecd68..2c9130695780 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -177,7 +177,8 @@ void amdtp_stream_destroy(struct amdtp_stream *s); int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, unsigned int pcm_channels, - unsigned int midi_ports); + unsigned int midi_ports, + bool double_pcm_frames); unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s); int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed); diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index c642b79e7ed4..920a3b8844ee 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -428,14 +428,16 @@ make_both_connections(struct snd_bebob *bebob, unsigned int rate) pcm_channels = bebob->tx_stream_formations[index].pcm; midi_channels = bebob->tx_stream_formations[index].midi; err = amdtp_stream_set_parameters(&bebob->tx_stream, rate, - pcm_channels, midi_channels * 8); + pcm_channels, midi_channels * 8, + false); if (err < 0) goto end; pcm_channels = bebob->rx_stream_formations[index].pcm; midi_channels = bebob->rx_stream_formations[index].midi; err = amdtp_stream_set_parameters(&bebob->rx_stream, rate, - pcm_channels, midi_channels * 8); + pcm_channels, midi_channels * 8, + false); if (err < 0) goto end; diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index c96306adcae0..e4c6c20d70a0 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -100,6 +100,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, { struct fw_iso_resources *resources; unsigned int i, mode, pcm_chs, midi_ports; + bool double_pcm_frames; int err; err = snd_dice_stream_get_rate_mode(dice, rate, &mode); @@ -125,19 +126,18 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, * For this quirk, blocking mode is required and PCM buffer size should * be aligned to SYT_INTERVAL. */ - if (mode > 1) { + double_pcm_frames = mode > 1; + if (double_pcm_frames) { rate /= 2; pcm_chs *= 2; - stream->double_pcm_frames = true; - } else { - stream->double_pcm_frames = false; } - err = amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports); + err = amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports, + false); if (err < 0) goto end; - if (mode > 1) { + if (double_pcm_frames) { pcm_chs /= 2; for (i = 0; i < pcm_chs; i++) { diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index dfefccff3c55..85a72e63913d 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -74,7 +74,7 @@ start_stream(struct snd_efw *efw, struct amdtp_stream *stream, } err = amdtp_stream_set_parameters(stream, sampling_rate, - pcm_channels, midi_ports); + pcm_channels, midi_ports, false); if (err < 0) goto end; diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index d119468fedf4..318f78e1a313 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -156,7 +156,7 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream, goto end; } err = amdtp_stream_set_parameters(stream, rate, - pcm_channels, midi_ports); + pcm_channels, midi_ports, false); if (err < 0) goto end; -- cgit v1.2.3-55-g7522 From 6a4e89ff0f4e46c1a9d0c28e786952e70516a5f4 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:51 +0900 Subject: ALSA: firewire-lib: add a member of frame_multiplier instead of double_pcm_frames In future commit, interface between data block processing layer and packet stream processing layer is defined. These two layers communicate the number of data blocks and the number of PCM frames. The data block processing layer has a responsibility for calculating the number of PCM frames. Therefore, 'dual wire' of Dice quirk should be handled in data block processing layer. This commit adds a member of 'frame_multiplier'. This member represents the ratio of the number of PCM frames against the number of data blocks. Usually, the value of this member is 1, while it's 2 in Dice's 'dual wire'. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp.c | 22 ++++++++++++---------- sound/firewire/amdtp.h | 3 ++- 2 files changed, 14 insertions(+), 11 deletions(-) (limited to 'sound/firewire') diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 094982f7c0fd..0bb5e14b6627 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -225,6 +225,16 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, s->data_block_quadlets = s->pcm_channels + midi_channels; s->midi_ports = midi_ports; + /* + * In IEC 61883-6, one data block represents one event. In ALSA, one + * event equals to one PCM frame. But Dice has a quirk at higher + * sampling rate to transfer two PCM frames in one data block. + */ + if (double_pcm_frames) + s->frame_multiplier = 2; + else + s->frame_multiplier = 1; + s->syt_interval = amdtp_syt_intervals[sfc]; /* default buffering in the device */ @@ -584,14 +594,6 @@ static void update_pcm_pointers(struct amdtp_stream *s, { unsigned int ptr; - /* - * In IEC 61883-6, one data block represents one event. In ALSA, one - * event equals to one PCM frame. But Dice has a quirk to transfer - * two PCM frames in one data block. - */ - if (s->double_pcm_frames) - frames *= 2; - ptr = s->pcm_buffer_pointer + frames; if (ptr >= pcm->runtime->buffer_size) ptr -= pcm->runtime->buffer_size; @@ -685,7 +687,7 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, return -EIO; if (pcm) - update_pcm_pointers(s, pcm, data_blocks); + update_pcm_pointers(s, pcm, data_blocks * s->frame_multiplier); /* No need to return the number of handled data blocks. */ return 0; @@ -788,7 +790,7 @@ end: return -EIO; if (pcm) - update_pcm_pointers(s, pcm, *data_blocks); + update_pcm_pointers(s, pcm, *data_blocks * s->frame_multiplier); return 0; } diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 2c9130695780..65229258bd24 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -162,11 +162,12 @@ struct amdtp_stream { u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM]; u8 midi_position; - bool double_pcm_frames; void (*transfer_samples)(struct amdtp_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames); + + unsigned int frame_multiplier; }; int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, -- cgit v1.2.3-55-g7522 From 20e445771ea109bc0ce9e717cbf2475e56a141a8 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:52 +0900 Subject: ALSA: firewire-lib: add helper functions as interfaces between packet streaming layer and data block processing layer ALSA PCM framework uses PCM buffer with a concept of 'period' to synchronize userspace operations to hardware for nearly-realtime processing. Each driver implements snd_pcm_period_elapsed() to tell across of the period boundary to ALSA PCM middleware. To call the function, some drivers utilize hardware interrupt handlers, the others count handled PCM frames. Drivers for sound units on IEEE 1394 bus are the latter. They use two buffers; PCM buffer and DMA buffer for IEEE 1394 isochronous packet. PCM frames are copied between these two buffers and 'amdtp_stream' structure counts the handled PCM frames. Then, snd_pcm_period_elapsed() is called if required. Essentially, packet streaming layer should not be responsible for PCM frame processing. The PCM frame processing should be handled in each data block processing layer as a result of handling data blocks. Although, PCM frame counting is a common work for all of protocols which ALSA firewire stack is going to support. This commit adds two new helper functions as interfaces between packet streaming layer to data block processing layer. In future, each data block processing layer implements these functions. The packet streaming layer calls data block processing layer per packet by calling the functions. The data block processing layer processes data blocks and PCM frames, and returns the number of processed PCM frames. Then the packet streaming layer calculates handled PCM frames and calls snd_pcm_period_elapsed(). Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp.c | 82 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 27 deletions(-) (limited to 'sound/firewire') diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 0bb5e14b6627..5a3a4529194a 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -657,28 +657,42 @@ static inline int queue_in_packet(struct amdtp_stream *s) amdtp_stream_get_max_payload(s), false); } +unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks, unsigned int *syt) +{ + struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); + unsigned int pcm_frames; + + if (pcm) { + s->transfer_samples(s, pcm, buffer, data_blocks); + pcm_frames = data_blocks * s->frame_multiplier; + } else { + write_pcm_silence(s, buffer, data_blocks); + pcm_frames = 0; + } + + if (s->midi_ports) + write_midi_messages(s, buffer, data_blocks); + + return pcm_frames; +} + static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, unsigned int syt) { __be32 *buffer; unsigned int payload_length; + unsigned int pcm_frames; struct snd_pcm_substream *pcm; buffer = s->buffer.packets[s->packet_index].buffer; + pcm_frames = process_rx_data_blocks(s, buffer + 2, data_blocks, &syt); + buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | (s->data_block_quadlets << CIP_DBS_SHIFT) | s->data_block_counter); buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 | (s->sfc << CIP_FDF_SHIFT) | syt); - buffer += 2; - - pcm = ACCESS_ONCE(s->pcm); - if (pcm) - s->transfer_samples(s, pcm, buffer, data_blocks); - else - write_pcm_silence(s, buffer, data_blocks); - if (s->midi_ports) - write_midi_messages(s, buffer, data_blocks); s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; @@ -686,20 +700,41 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, if (queue_out_packet(s, payload_length, false) < 0) return -EIO; - if (pcm) - update_pcm_pointers(s, pcm, data_blocks * s->frame_multiplier); + pcm = ACCESS_ONCE(s->pcm); + if (pcm && pcm_frames > 0) + update_pcm_pointers(s, pcm, pcm_frames); /* No need to return the number of handled data blocks. */ return 0; } +unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks, unsigned int *syt) +{ + struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); + unsigned int pcm_frames; + + if (pcm) { + s->transfer_samples(s, pcm, buffer, data_blocks); + pcm_frames = data_blocks * s->frame_multiplier; + } else { + pcm_frames = 0; + } + + if (s->midi_ports) + read_midi_messages(s, buffer, data_blocks); + + return pcm_frames; +} + static int handle_in_packet(struct amdtp_stream *s, unsigned int payload_quadlets, __be32 *buffer, - unsigned int *data_blocks) + unsigned int *data_blocks, unsigned int syt) { u32 cip_header[2]; unsigned int data_block_quadlets, data_block_counter, dbc_interval; - struct snd_pcm_substream *pcm = NULL; + struct snd_pcm_substream *pcm; + unsigned int pcm_frames; bool lost; cip_header[0] = be32_to_cpu(buffer[0]); @@ -716,6 +751,7 @@ static int handle_in_packet(struct amdtp_stream *s, "Invalid CIP header for AMDTP: %08X:%08X\n", cip_header[0], cip_header[1]); *data_blocks = 0; + pcm_frames = 0; goto end; } @@ -769,16 +805,7 @@ static int handle_in_packet(struct amdtp_stream *s, return -EIO; } - if (*data_blocks > 0) { - buffer += 2; - - pcm = ACCESS_ONCE(s->pcm); - if (pcm) - s->transfer_samples(s, pcm, buffer, *data_blocks); - - if (s->midi_ports) - read_midi_messages(s, buffer, *data_blocks); - } + pcm_frames = process_tx_data_blocks(s, buffer + 2, *data_blocks, &syt); if (s->flags & CIP_DBC_IS_END_EVENT) s->data_block_counter = data_block_counter; @@ -789,8 +816,9 @@ end: if (queue_in_packet(s) < 0) return -EIO; - if (pcm) - update_pcm_pointers(s, pcm, *data_blocks * s->frame_multiplier); + pcm = ACCESS_ONCE(s->pcm); + if (pcm && pcm_frames > 0) + update_pcm_pointers(s, pcm, pcm_frames); return 0; } @@ -860,15 +888,15 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle, break; } + syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; if (handle_in_packet(s, payload_quadlets, buffer, - &data_blocks) < 0) { + &data_blocks, syt) < 0) { s->packet_index = -1; break; } /* Process sync slave stream */ if (s->sync_slave && s->sync_slave->callbacked) { - syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; if (handle_out_packet(s->sync_slave, data_blocks, syt) < 0) { s->packet_index = -1; -- cgit v1.2.3-55-g7522 From 414ba022a5287e9473c7fa2b4aa1b1025e9ca8be Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:53 +0900 Subject: ALSA: firewire-lib: add support arbitrary value for fmt/fdf fields in CIP header Some vendor specific protocol uses its own value for fmt/fdf fields in CIP header. This commit support to set arbitrary values for the fields. In IEC 61883-6, NO-DATA code is defined for FDF field. A packet with this code includes no data even if it includes some data blocks. This commit still leaves a condition to handle this special packet. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp.c | 31 +++++++++++++++++++++++-------- sound/firewire/amdtp.h | 2 ++ 2 files changed, 25 insertions(+), 8 deletions(-) (limited to 'sound/firewire') diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 5a3a4529194a..b251f4b2b51c 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -59,8 +59,8 @@ * Audio and Music transfer protocol specific parameters * only "Clock-based rate control mode" is supported */ -#define CIP_FMT_AM (0x10 << CIP_FMT_SHIFT) -#define AMDTP_FDF_AM824 (0 << (CIP_FDF_SHIFT + 3)) +#define CIP_FMT_AM 0x10 +#define AMDTP_FDF_AM824 0x00 #define AMDTP_FDF_NO_DATA 0xff /* TODO: make these configurable */ @@ -94,6 +94,8 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, s->callbacked = false; s->sync_slave = NULL; + s->fmt = CIP_FMT_AM; + return 0; } EXPORT_SYMBOL(amdtp_stream_init); @@ -225,6 +227,8 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, s->data_block_quadlets = s->pcm_channels + midi_channels; s->midi_ports = midi_ports; + s->fdf = AMDTP_FDF_AM824 | s->sfc; + /* * In IEC 61883-6, one data block represents one event. In ALSA, one * event equals to one PCM frame. But Dice has a quirk at higher @@ -691,8 +695,10 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | (s->data_block_quadlets << CIP_DBS_SHIFT) | s->data_block_counter); - buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 | - (s->sfc << CIP_FDF_SHIFT) | syt); + buffer[1] = cpu_to_be32(CIP_EOH | + ((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) | + ((s->fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) | + (syt & CIP_SYT_MASK)); s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; @@ -732,6 +738,7 @@ static int handle_in_packet(struct amdtp_stream *s, unsigned int *data_blocks, unsigned int syt) { u32 cip_header[2]; + unsigned int fmt, fdf; unsigned int data_block_quadlets, data_block_counter, dbc_interval; struct snd_pcm_substream *pcm; unsigned int pcm_frames; @@ -745,8 +752,7 @@ static int handle_in_packet(struct amdtp_stream *s, * For convenience, also check FMT field is AM824 or not. */ if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) || - ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH) || - ((cip_header[1] & CIP_FMT_MASK) != CIP_FMT_AM)) { + ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) { dev_info_ratelimited(&s->unit->device, "Invalid CIP header for AMDTP: %08X:%08X\n", cip_header[0], cip_header[1]); @@ -755,10 +761,19 @@ static int handle_in_packet(struct amdtp_stream *s, goto end; } + /* Check valid protocol or not. */ + fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT; + if (fmt != s->fmt) { + dev_err(&s->unit->device, + "Detect unexpected protocol: %08x %08x\n", + cip_header[0], cip_header[1]); + return -EIO; + } + /* Calculate data blocks */ + fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT; if (payload_quadlets < 3 || - ((cip_header[1] & CIP_FDF_MASK) == - (AMDTP_FDF_NO_DATA << CIP_FDF_SHIFT))) { + (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) { *data_blocks = 0; } else { data_block_quadlets = diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 65229258bd24..883bb1a7e25f 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -128,6 +128,8 @@ struct amdtp_stream { unsigned int source_node_id_field; unsigned int data_block_quadlets; unsigned int data_block_counter; + unsigned int fmt; + unsigned int fdf; /* quirk: fixed interval of dbc between previos/current packets. */ unsigned int tx_dbc_interval; /* quirk: indicate the value of dbc field in a first packet. */ -- cgit v1.2.3-55-g7522 From d67c46b953749aef223496ec95b7bf93e40887dc Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:54 +0900 Subject: ALSA: firewire-lib: rename 'amdtp' to 'amdtp-stream' to prepare for functional separation In later commit, data block processing layer will be newly added. This layer will be named as 'amdtp-am824'. This commit renames current amdtp file to amdtp-stream, to distinguish it from the new layer. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/Makefile | 2 +- sound/firewire/amdtp-stream.c | 1157 ++++++++++++++++++++++++++++++++++ sound/firewire/amdtp-stream.h | 306 +++++++++ sound/firewire/amdtp.c | 1157 ---------------------------------- sound/firewire/amdtp.h | 306 --------- sound/firewire/bebob/bebob.h | 2 +- sound/firewire/dice/dice.h | 2 +- sound/firewire/fcp.c | 2 +- sound/firewire/fireworks/fireworks.h | 2 +- sound/firewire/oxfw/oxfw.h | 2 +- 10 files changed, 1469 insertions(+), 1469 deletions(-) create mode 100644 sound/firewire/amdtp-stream.c create mode 100644 sound/firewire/amdtp-stream.h delete mode 100644 sound/firewire/amdtp.c delete mode 100644 sound/firewire/amdtp.h (limited to 'sound/firewire') diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index 8b37f084b2ab..102e342ddc38 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -1,5 +1,5 @@ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ - fcp.o cmp.o amdtp.o + fcp.o cmp.o amdtp-stream.o snd-oxfw-objs := oxfw.o snd-isight-objs := isight.o snd-scs1x-objs := scs1x.o diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c new file mode 100644 index 000000000000..ec65ebf8f04a --- /dev/null +++ b/sound/firewire/amdtp-stream.c @@ -0,0 +1,1157 @@ +/* + * Audio and Music Data Transmission Protocol (IEC 61883-6) streams + * with Common Isochronous Packet (IEC 61883-1) headers + * + * Copyright (c) Clemens Ladisch + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "amdtp-stream.h" + +#define TICKS_PER_CYCLE 3072 +#define CYCLES_PER_SECOND 8000 +#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND) + +/* + * 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 + +#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */ + +/* isochronous header parameters */ +#define ISO_DATA_LENGTH_SHIFT 16 +#define TAG_CIP 1 + +/* common isochronous packet header parameters */ +#define CIP_EOH_SHIFT 31 +#define CIP_EOH (1u << CIP_EOH_SHIFT) +#define CIP_EOH_MASK 0x80000000 +#define CIP_SID_SHIFT 24 +#define CIP_SID_MASK 0x3f000000 +#define CIP_DBS_MASK 0x00ff0000 +#define CIP_DBS_SHIFT 16 +#define CIP_DBC_MASK 0x000000ff +#define CIP_FMT_SHIFT 24 +#define CIP_FMT_MASK 0x3f000000 +#define CIP_FDF_MASK 0x00ff0000 +#define CIP_FDF_SHIFT 16 +#define CIP_SYT_MASK 0x0000ffff +#define CIP_SYT_NO_INFO 0xffff + +/* + * Audio and Music transfer protocol specific parameters + * only "Clock-based rate control mode" is supported + */ +#define CIP_FMT_AM 0x10 +#define AMDTP_FDF_AM824 0x00 +#define AMDTP_FDF_NO_DATA 0xff + +/* TODO: make these configurable */ +#define INTERRUPT_INTERVAL 16 +#define QUEUE_LENGTH 48 + +#define IN_PACKET_HEADER_SIZE 4 +#define OUT_PACKET_HEADER_SIZE 0 + +static void pcm_period_tasklet(unsigned long data); + +/** + * amdtp_stream_init - initialize an AMDTP stream structure + * @s: the AMDTP stream to initialize + * @unit: the target of the stream + * @dir: the direction of stream + * @flags: the packet transmission method to use + */ +int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, + enum amdtp_stream_direction dir, enum cip_flags flags) +{ + s->unit = unit; + s->direction = dir; + s->flags = flags; + s->context = ERR_PTR(-1); + mutex_init(&s->mutex); + tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s); + s->packet_index = 0; + + init_waitqueue_head(&s->callback_wait); + s->callbacked = false; + s->sync_slave = NULL; + + s->fmt = CIP_FMT_AM; + + return 0; +} +EXPORT_SYMBOL(amdtp_stream_init); + +/** + * amdtp_stream_destroy - free stream resources + * @s: the AMDTP stream to destroy + */ +void amdtp_stream_destroy(struct amdtp_stream *s) +{ + WARN_ON(amdtp_stream_running(s)); + mutex_destroy(&s->mutex); +} +EXPORT_SYMBOL(amdtp_stream_destroy); + +const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = { + [CIP_SFC_32000] = 8, + [CIP_SFC_44100] = 8, + [CIP_SFC_48000] = 8, + [CIP_SFC_88200] = 16, + [CIP_SFC_96000] = 16, + [CIP_SFC_176400] = 32, + [CIP_SFC_192000] = 32, +}; +EXPORT_SYMBOL(amdtp_syt_intervals); + +const unsigned int amdtp_rate_table[CIP_SFC_COUNT] = { + [CIP_SFC_32000] = 32000, + [CIP_SFC_44100] = 44100, + [CIP_SFC_48000] = 48000, + [CIP_SFC_88200] = 88200, + [CIP_SFC_96000] = 96000, + [CIP_SFC_176400] = 176400, + [CIP_SFC_192000] = 192000, +}; +EXPORT_SYMBOL(amdtp_rate_table); + +/** + * amdtp_stream_add_pcm_hw_constraints - add hw constraints for PCM substream + * @s: the AMDTP stream, which must be initialized. + * @runtime: the PCM substream runtime + */ +int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, + struct snd_pcm_runtime *runtime) +{ + int err; + + /* AM824 in IEC 61883-6 can deliver 24bit data */ + err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + if (err < 0) + goto end; + + /* + * Currently firewire-lib processes 16 packets in one software + * interrupt callback. This equals to 2msec but actually the + * interval of the interrupts has a jitter. + * Additionally, even if adding a constraint to fit period size to + * 2msec, actual calculated frames per period doesn't equal to 2msec, + * depending on sampling rate. + * Anyway, the interval to call snd_pcm_period_elapsed() cannot 2msec. + * Here let us use 5msec for safe period interrupt. + */ + err = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, + 5000, UINT_MAX); + if (err < 0) + goto end; + + /* Non-Blocking stream has no more constraints */ + if (!(s->flags & CIP_BLOCKING)) + goto end; + + /* + * One AMDTP packet can include some frames. In blocking mode, the + * number equals to SYT_INTERVAL. So the number is 8, 16 or 32, + * depending on its sampling rate. For accurate period interrupt, it's + * preferrable to align period/buffer sizes to current SYT_INTERVAL. + * + * TODO: These constraints can be improved with proper rules. + * Currently apply LCM of SYT_INTERVALs. + */ + err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32); + if (err < 0) + goto end; + err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32); +end: + return err; +} +EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints); + +/** + * amdtp_stream_set_parameters - set stream parameters + * @s: the AMDTP stream to configure + * @rate: the sample rate + * @pcm_channels: the number of PCM samples in each data block, to be encoded + * as AM824 multi-bit linear audio + * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) + * @double_pcm_frames: one data block transfers two PCM frames + * + * The parameters must be set before the stream is started, and must not be + * changed while the stream is running. + */ +int amdtp_stream_set_parameters(struct amdtp_stream *s, + unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports, + bool double_pcm_frames) +{ + unsigned int i, sfc, midi_channels; + + midi_channels = DIV_ROUND_UP(midi_ports, 8); + + if (WARN_ON(amdtp_stream_running(s)) || + WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) || + WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI)) + return -EINVAL; + + for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) { + if (amdtp_rate_table[sfc] == rate) + break; + } + if (sfc == ARRAY_SIZE(amdtp_rate_table)) + return -EINVAL; + + s->pcm_channels = pcm_channels; + s->sfc = sfc; + s->data_block_quadlets = s->pcm_channels + midi_channels; + s->midi_ports = midi_ports; + + s->fdf = AMDTP_FDF_AM824 | s->sfc; + + /* + * In IEC 61883-6, one data block represents one event. In ALSA, one + * event equals to one PCM frame. But Dice has a quirk at higher + * sampling rate to transfer two PCM frames in one data block. + */ + if (double_pcm_frames) + s->frame_multiplier = 2; + else + s->frame_multiplier = 1; + + s->syt_interval = amdtp_syt_intervals[sfc]; + + /* default buffering in the device */ + s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; + if (s->flags & CIP_BLOCKING) + /* additional buffering needed to adjust for no-data packets */ + s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate; + + /* init the position map for PCM and MIDI channels */ + for (i = 0; i < pcm_channels; i++) + s->pcm_positions[i] = i; + s->midi_position = s->pcm_channels; + + /* + * 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().) + */ + s->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1; + + return 0; +} +EXPORT_SYMBOL(amdtp_stream_set_parameters); + +/** + * amdtp_stream_get_max_payload - get the stream's packet size + * @s: the AMDTP stream + * + * This function must not be called before the stream has been configured + * with amdtp_stream_set_parameters(). + */ +unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s) +{ + unsigned int multiplier = 1; + + if (s->flags & CIP_JUMBO_PAYLOAD) + multiplier = 5; + + return 8 + s->syt_interval * s->data_block_quadlets * 4 * multiplier; +} +EXPORT_SYMBOL(amdtp_stream_get_max_payload); + +static void write_pcm_s16(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); +static void write_pcm_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); +static void read_pcm_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); + +/** + * amdtp_stream_set_pcm_format - set the PCM format + * @s: the AMDTP stream to configure + * @format: the format of the ALSA PCM device + * + * The sample format must be set after the other parameters (rate/PCM channels/ + * MIDI) and before the stream is started, and must not be changed while the + * stream is running. + */ +void amdtp_stream_set_pcm_format(struct amdtp_stream *s, + snd_pcm_format_t format) +{ + if (WARN_ON(amdtp_stream_pcm_running(s))) + return; + + switch (format) { + default: + WARN_ON(1); + /* fall through */ + case SNDRV_PCM_FORMAT_S16: + if (s->direction == AMDTP_OUT_STREAM) { + s->transfer_samples = write_pcm_s16; + break; + } + WARN_ON(1); + /* fall through */ + case SNDRV_PCM_FORMAT_S32: + if (s->direction == AMDTP_OUT_STREAM) + s->transfer_samples = write_pcm_s32; + else + s->transfer_samples = read_pcm_s32; + break; + } +} +EXPORT_SYMBOL(amdtp_stream_set_pcm_format); + +/** + * amdtp_stream_pcm_prepare - prepare PCM device for running + * @s: the AMDTP stream + * + * This function should be called from the PCM device's .prepare callback. + */ +void amdtp_stream_pcm_prepare(struct amdtp_stream *s) +{ + tasklet_kill(&s->period_tasklet); + s->pcm_buffer_pointer = 0; + s->pcm_period_pointer = 0; + s->pointer_flush = true; +} +EXPORT_SYMBOL(amdtp_stream_pcm_prepare); + +static unsigned int calculate_data_blocks(struct amdtp_stream *s, + unsigned int syt) +{ + unsigned int phase, data_blocks; + + /* Blocking mode. */ + if (s->flags & CIP_BLOCKING) { + /* This module generate empty packet for 'no data'. */ + if (syt == CIP_SYT_NO_INFO) + data_blocks = 0; + else + data_blocks = s->syt_interval; + /* Non-blocking mode. */ + } else { + if (!cip_sfc_is_base_44100(s->sfc)) { + /* Sample_rate / 8000 is an integer, and precomputed. */ + data_blocks = s->data_block_state; + } else { + phase = s->data_block_state; + + /* + * This calculates the number of data blocks per packet so that + * 1) the overall rate is correct and exactly synchronized to + * the bus clock, and + * 2) packets with a rounded-up number of blocks occur as early + * as possible in the sequence (to prevent underruns of the + * device's buffer). + */ + if (s->sfc == CIP_SFC_44100) + /* 6 6 5 6 5 6 5 ... */ + data_blocks = 5 + ((phase & 1) ^ + (phase == 0 || phase >= 40)); + else + /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */ + data_blocks = 11 * (s->sfc >> 1) + (phase == 0); + if (++phase >= (80 >> (s->sfc >> 1))) + phase = 0; + s->data_block_state = phase; + } + } + + return data_blocks; +} + +static unsigned int calculate_syt(struct amdtp_stream *s, + unsigned int cycle) +{ + unsigned int syt_offset, phase, index, syt; + + if (s->last_syt_offset < TICKS_PER_CYCLE) { + if (!cip_sfc_is_base_44100(s->sfc)) + syt_offset = s->last_syt_offset + s->syt_offset_state; + else { + /* + * The time, in ticks, of the n'th SYT_INTERVAL sample is: + * n * SYT_INTERVAL * 24576000 / sample_rate + * Modulo TICKS_PER_CYCLE, the difference between successive + * elements is about 1386.23. Rounding the results of this + * formula to the SYT precision results in a sequence of + * differences that begins with: + * 1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ... + * This code generates _exactly_ the same sequence. + */ + phase = s->syt_offset_state; + index = phase % 13; + syt_offset = s->last_syt_offset; + syt_offset += 1386 + ((index && !(index & 3)) || + phase == 146); + if (++phase >= 147) + phase = 0; + s->syt_offset_state = phase; + } + } else + syt_offset = s->last_syt_offset - TICKS_PER_CYCLE; + s->last_syt_offset = syt_offset; + + if (syt_offset < TICKS_PER_CYCLE) { + syt_offset += s->transfer_delay; + syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; + syt += syt_offset % TICKS_PER_CYCLE; + + return syt & CIP_SYT_MASK; + } else { + return CIP_SYT_NO_INFO; + } +} + +static void write_pcm_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + const u32 *src; + + channels = s->pcm_channels; + src = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + buffer[s->pcm_positions[c]] = + cpu_to_be32((*src >> 8) | 0x40000000); + src++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; + } +} + +static void write_pcm_s16(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + const u16 *src; + + channels = s->pcm_channels; + src = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + buffer[s->pcm_positions[c]] = + cpu_to_be32((*src << 8) | 0x42000000); + src++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; + } +} + +static void read_pcm_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + u32 *dst; + + channels = s->pcm_channels; + dst = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + *dst = be32_to_cpu(buffer[s->pcm_positions[c]]) << 8; + dst++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + dst = (void *)runtime->dma_area; + } +} + +static void write_pcm_silence(struct amdtp_stream *s, + __be32 *buffer, unsigned int frames) +{ + unsigned int i, c; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < s->pcm_channels; ++c) + buffer[s->pcm_positions[c]] = cpu_to_be32(0x40000000); + buffer += s->data_block_quadlets; + } +} + +/* + * To avoid sending MIDI bytes at too high a rate, assume that the receiving + * device has a FIFO, and track how much it is filled. This values increases + * by one whenever we send one byte in a packet, but the FIFO empties at + * a constant rate independent of our packet rate. One packet has syt_interval + * samples, so the number of bytes that empty out of the FIFO, per packet(!), + * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate. To avoid storing + * fractional values, the values in midi_fifo_used[] are measured in bytes + * multiplied by the sample rate. + */ +static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port) +{ + int used; + + used = s->midi_fifo_used[port]; + if (used == 0) /* common shortcut */ + return true; + + used -= MIDI_BYTES_PER_SECOND * s->syt_interval; + used = max(used, 0); + s->midi_fifo_used[port] = used; + + return used < s->midi_fifo_limit; +} + +static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port) +{ + s->midi_fifo_used[port] += amdtp_rate_table[s->sfc]; +} + +static void write_midi_messages(struct amdtp_stream *s, + __be32 *buffer, unsigned int frames) +{ + unsigned int f, port; + u8 *b; + + for (f = 0; f < frames; f++) { + b = (u8 *)&buffer[s->midi_position]; + + port = (s->data_block_counter + f) % 8; + if (f < MAX_MIDI_RX_BLOCKS && + midi_ratelimit_per_packet(s, port) && + s->midi[port] != NULL && + snd_rawmidi_transmit(s->midi[port], &b[1], 1) == 1) { + midi_rate_use_one_byte(s, port); + b[0] = 0x81; + } else { + b[0] = 0x80; + b[1] = 0; + } + b[2] = 0; + b[3] = 0; + + buffer += s->data_block_quadlets; + } +} + +static void read_midi_messages(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->midi_position]; + + 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, + struct snd_pcm_substream *pcm, + unsigned int frames) +{ + unsigned int ptr; + + ptr = s->pcm_buffer_pointer + frames; + if (ptr >= pcm->runtime->buffer_size) + ptr -= pcm->runtime->buffer_size; + ACCESS_ONCE(s->pcm_buffer_pointer) = ptr; + + s->pcm_period_pointer += frames; + if (s->pcm_period_pointer >= pcm->runtime->period_size) { + s->pcm_period_pointer -= pcm->runtime->period_size; + s->pointer_flush = false; + tasklet_hi_schedule(&s->period_tasklet); + } +} + +static void pcm_period_tasklet(unsigned long data) +{ + struct amdtp_stream *s = (void *)data; + struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); + + if (pcm) + snd_pcm_period_elapsed(pcm); +} + +static int queue_packet(struct amdtp_stream *s, + unsigned int header_length, + unsigned int payload_length, bool skip) +{ + struct fw_iso_packet p = {0}; + int err = 0; + + if (IS_ERR(s->context)) + goto end; + + p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL); + p.tag = TAG_CIP; + p.header_length = header_length; + p.payload_length = (!skip) ? payload_length : 0; + p.skip = skip; + err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer, + s->buffer.packets[s->packet_index].offset); + if (err < 0) { + dev_err(&s->unit->device, "queueing error: %d\n", err); + goto end; + } + + if (++s->packet_index >= QUEUE_LENGTH) + s->packet_index = 0; +end: + return err; +} + +static inline int queue_out_packet(struct amdtp_stream *s, + unsigned int payload_length, bool skip) +{ + return queue_packet(s, OUT_PACKET_HEADER_SIZE, + payload_length, skip); +} + +static inline int queue_in_packet(struct amdtp_stream *s) +{ + return queue_packet(s, IN_PACKET_HEADER_SIZE, + amdtp_stream_get_max_payload(s), false); +} + +unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks, unsigned int *syt) +{ + struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); + unsigned int pcm_frames; + + if (pcm) { + s->transfer_samples(s, pcm, buffer, data_blocks); + pcm_frames = data_blocks * s->frame_multiplier; + } else { + write_pcm_silence(s, buffer, data_blocks); + pcm_frames = 0; + } + + if (s->midi_ports) + write_midi_messages(s, buffer, data_blocks); + + return pcm_frames; +} + +static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, + unsigned int syt) +{ + __be32 *buffer; + unsigned int payload_length; + unsigned int pcm_frames; + struct snd_pcm_substream *pcm; + + buffer = s->buffer.packets[s->packet_index].buffer; + pcm_frames = process_rx_data_blocks(s, buffer + 2, data_blocks, &syt); + + buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | + (s->data_block_quadlets << CIP_DBS_SHIFT) | + s->data_block_counter); + buffer[1] = cpu_to_be32(CIP_EOH | + ((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) | + ((s->fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) | + (syt & CIP_SYT_MASK)); + + s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; + + payload_length = 8 + data_blocks * 4 * s->data_block_quadlets; + if (queue_out_packet(s, payload_length, false) < 0) + return -EIO; + + pcm = ACCESS_ONCE(s->pcm); + if (pcm && pcm_frames > 0) + update_pcm_pointers(s, pcm, pcm_frames); + + /* No need to return the number of handled data blocks. */ + return 0; +} + +unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks, unsigned int *syt) +{ + struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); + unsigned int pcm_frames; + + if (pcm) { + s->transfer_samples(s, pcm, buffer, data_blocks); + pcm_frames = data_blocks * s->frame_multiplier; + } else { + pcm_frames = 0; + } + + if (s->midi_ports) + read_midi_messages(s, buffer, data_blocks); + + return pcm_frames; +} + +static int handle_in_packet(struct amdtp_stream *s, + unsigned int payload_quadlets, __be32 *buffer, + unsigned int *data_blocks, unsigned int syt) +{ + u32 cip_header[2]; + unsigned int fmt, fdf; + unsigned int data_block_quadlets, data_block_counter, dbc_interval; + struct snd_pcm_substream *pcm; + unsigned int pcm_frames; + bool lost; + + cip_header[0] = be32_to_cpu(buffer[0]); + cip_header[1] = be32_to_cpu(buffer[1]); + + /* + * This module supports 'Two-quadlet CIP header with SYT field'. + * For convenience, also check FMT field is AM824 or not. + */ + if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) || + ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) { + dev_info_ratelimited(&s->unit->device, + "Invalid CIP header for AMDTP: %08X:%08X\n", + cip_header[0], cip_header[1]); + *data_blocks = 0; + pcm_frames = 0; + goto end; + } + + /* Check valid protocol or not. */ + fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT; + if (fmt != s->fmt) { + dev_err(&s->unit->device, + "Detect unexpected protocol: %08x %08x\n", + cip_header[0], cip_header[1]); + return -EIO; + } + + /* Calculate data blocks */ + fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT; + if (payload_quadlets < 3 || + (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) { + *data_blocks = 0; + } else { + data_block_quadlets = + (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT; + /* avoid division by zero */ + if (data_block_quadlets == 0) { + dev_err(&s->unit->device, + "Detect invalid value in dbs field: %08X\n", + cip_header[0]); + return -EPROTO; + } + if (s->flags & CIP_WRONG_DBS) + data_block_quadlets = s->data_block_quadlets; + + *data_blocks = (payload_quadlets - 2) / data_block_quadlets; + } + + /* Check data block counter continuity */ + data_block_counter = cip_header[0] & CIP_DBC_MASK; + if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) && + s->data_block_counter != UINT_MAX) + data_block_counter = s->data_block_counter; + + if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) && + data_block_counter == s->tx_first_dbc) || + s->data_block_counter == UINT_MAX) { + lost = false; + } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) { + lost = data_block_counter != s->data_block_counter; + } else { + if ((*data_blocks > 0) && (s->tx_dbc_interval > 0)) + dbc_interval = s->tx_dbc_interval; + else + dbc_interval = *data_blocks; + + lost = data_block_counter != + ((s->data_block_counter + dbc_interval) & 0xff); + } + + if (lost) { + dev_err(&s->unit->device, + "Detect discontinuity of CIP: %02X %02X\n", + s->data_block_counter, data_block_counter); + return -EIO; + } + + pcm_frames = process_tx_data_blocks(s, buffer + 2, *data_blocks, &syt); + + if (s->flags & CIP_DBC_IS_END_EVENT) + s->data_block_counter = data_block_counter; + else + s->data_block_counter = + (data_block_counter + *data_blocks) & 0xff; +end: + if (queue_in_packet(s) < 0) + return -EIO; + + pcm = ACCESS_ONCE(s->pcm); + if (pcm && pcm_frames > 0) + update_pcm_pointers(s, pcm, pcm_frames); + + return 0; +} + +static void out_stream_callback(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, + void *private_data) +{ + struct amdtp_stream *s = private_data; + unsigned int i, syt, packets = header_length / 4; + unsigned int data_blocks; + + if (s->packet_index < 0) + return; + + /* + * Compute the cycle of the last queued packet. + * (We need only the four lowest bits for the SYT, so we can ignore + * that bits 0-11 must wrap around at 3072.) + */ + cycle += QUEUE_LENGTH - packets; + + for (i = 0; i < packets; ++i) { + syt = calculate_syt(s, ++cycle); + data_blocks = calculate_data_blocks(s, syt); + + if (handle_out_packet(s, data_blocks, syt) < 0) { + s->packet_index = -1; + amdtp_stream_pcm_abort(s); + return; + } + } + + fw_iso_context_queue_flush(s->context); +} + +static void in_stream_callback(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, + void *private_data) +{ + struct amdtp_stream *s = private_data; + unsigned int p, syt, packets; + unsigned int payload_quadlets, max_payload_quadlets; + unsigned int data_blocks; + __be32 *buffer, *headers = header; + + if (s->packet_index < 0) + return; + + /* The number of packets in buffer */ + packets = header_length / IN_PACKET_HEADER_SIZE; + + /* For buffer-over-run prevention. */ + max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4; + + for (p = 0; p < packets; p++) { + buffer = s->buffer.packets[s->packet_index].buffer; + + /* The number of quadlets in this packet */ + payload_quadlets = + (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4; + if (payload_quadlets > max_payload_quadlets) { + dev_err(&s->unit->device, + "Detect jumbo payload: %02x %02x\n", + payload_quadlets, max_payload_quadlets); + s->packet_index = -1; + break; + } + + syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; + if (handle_in_packet(s, payload_quadlets, buffer, + &data_blocks, syt) < 0) { + s->packet_index = -1; + break; + } + + /* Process sync slave stream */ + if (s->sync_slave && s->sync_slave->callbacked) { + if (handle_out_packet(s->sync_slave, + data_blocks, syt) < 0) { + s->packet_index = -1; + break; + } + } + } + + /* Queueing error or detecting discontinuity */ + if (s->packet_index < 0) { + amdtp_stream_pcm_abort(s); + + /* Abort sync slave. */ + if (s->sync_slave) { + s->sync_slave->packet_index = -1; + amdtp_stream_pcm_abort(s->sync_slave); + } + return; + } + + /* when sync to device, flush the packets for slave stream */ + if (s->sync_slave && s->sync_slave->callbacked) + fw_iso_context_queue_flush(s->sync_slave->context); + + fw_iso_context_queue_flush(s->context); +} + +/* processing is done by master callback */ +static void slave_stream_callback(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, + void *private_data) +{ + return; +} + +/* this is executed one time */ +static void amdtp_stream_first_callback(struct fw_iso_context *context, + u32 cycle, size_t header_length, + void *header, void *private_data) +{ + struct amdtp_stream *s = private_data; + + /* + * For in-stream, first packet has come. + * For out-stream, prepared to transmit first packet + */ + s->callbacked = true; + wake_up(&s->callback_wait); + + if (s->direction == AMDTP_IN_STREAM) + context->callback.sc = in_stream_callback; + else if (s->flags & CIP_SYNC_TO_DEVICE) + context->callback.sc = slave_stream_callback; + else + context->callback.sc = out_stream_callback; + + context->callback.sc(context, cycle, header_length, header, s); +} + +/** + * amdtp_stream_start - start transferring packets + * @s: the AMDTP stream to start + * @channel: the isochronous channel on the bus + * @speed: firewire speed code + * + * The stream cannot be started until it has been configured with + * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI + * device can be started. + */ +int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) +{ + static const struct { + unsigned int data_block; + unsigned int syt_offset; + } initial_state[] = { + [CIP_SFC_32000] = { 4, 3072 }, + [CIP_SFC_48000] = { 6, 1024 }, + [CIP_SFC_96000] = { 12, 1024 }, + [CIP_SFC_192000] = { 24, 1024 }, + [CIP_SFC_44100] = { 0, 67 }, + [CIP_SFC_88200] = { 0, 67 }, + [CIP_SFC_176400] = { 0, 67 }, + }; + unsigned int header_size; + enum dma_data_direction dir; + int type, tag, err; + + mutex_lock(&s->mutex); + + if (WARN_ON(amdtp_stream_running(s) || + (s->data_block_quadlets < 1))) { + err = -EBADFD; + goto err_unlock; + } + + if (s->direction == AMDTP_IN_STREAM && + s->flags & CIP_SKIP_INIT_DBC_CHECK) + s->data_block_counter = UINT_MAX; + else + s->data_block_counter = 0; + s->data_block_state = initial_state[s->sfc].data_block; + s->syt_offset_state = initial_state[s->sfc].syt_offset; + s->last_syt_offset = TICKS_PER_CYCLE; + + /* initialize packet buffer */ + if (s->direction == AMDTP_IN_STREAM) { + dir = DMA_FROM_DEVICE; + type = FW_ISO_CONTEXT_RECEIVE; + header_size = IN_PACKET_HEADER_SIZE; + } else { + dir = DMA_TO_DEVICE; + type = FW_ISO_CONTEXT_TRANSMIT; + header_size = OUT_PACKET_HEADER_SIZE; + } + err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, + amdtp_stream_get_max_payload(s), dir); + if (err < 0) + goto err_unlock; + + s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, + type, channel, speed, header_size, + amdtp_stream_first_callback, s); + if (IS_ERR(s->context)) { + err = PTR_ERR(s->context); + if (err == -EBUSY) + dev_err(&s->unit->device, + "no free stream on this controller\n"); + goto err_buffer; + } + + amdtp_stream_update(s); + + s->packet_index = 0; + do { + if (s->direction == AMDTP_IN_STREAM) + err = queue_in_packet(s); + else + err = queue_out_packet(s, 0, true); + if (err < 0) + goto err_context; + } while (s->packet_index > 0); + + /* NOTE: TAG1 matches CIP. This just affects in stream. */ + tag = FW_ISO_CONTEXT_MATCH_TAG1; + if (s->flags & CIP_EMPTY_WITH_TAG0) + tag |= FW_ISO_CONTEXT_MATCH_TAG0; + + s->callbacked = false; + err = fw_iso_context_start(s->context, -1, 0, tag); + if (err < 0) + goto err_context; + + mutex_unlock(&s->mutex); + + return 0; + +err_context: + fw_iso_context_destroy(s->context); + s->context = ERR_PTR(-1); +err_buffer: + iso_packets_buffer_destroy(&s->buffer, s->unit); +err_unlock: + mutex_unlock(&s->mutex); + + return err; +} +EXPORT_SYMBOL(amdtp_stream_start); + +/** + * amdtp_stream_pcm_pointer - get the PCM buffer position + * @s: the AMDTP stream that transports the PCM data + * + * Returns the current buffer position, in frames. + */ +unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s) +{ + /* this optimization is allowed to be racy */ + if (s->pointer_flush && amdtp_stream_running(s)) + fw_iso_context_flush_completions(s->context); + else + s->pointer_flush = true; + + return ACCESS_ONCE(s->pcm_buffer_pointer); +} +EXPORT_SYMBOL(amdtp_stream_pcm_pointer); + +/** + * amdtp_stream_update - update the stream after a bus reset + * @s: the AMDTP stream + */ +void amdtp_stream_update(struct amdtp_stream *s) +{ + /* Precomputing. */ + ACCESS_ONCE(s->source_node_id_field) = + (fw_parent_device(s->unit)->card->node_id << CIP_SID_SHIFT) & + CIP_SID_MASK; +} +EXPORT_SYMBOL(amdtp_stream_update); + +/** + * amdtp_stream_stop - stop sending packets + * @s: the AMDTP stream to stop + * + * All PCM and MIDI devices of the stream must be stopped before the stream + * itself can be stopped. + */ +void amdtp_stream_stop(struct amdtp_stream *s) +{ + mutex_lock(&s->mutex); + + if (!amdtp_stream_running(s)) { + mutex_unlock(&s->mutex); + return; + } + + tasklet_kill(&s->period_tasklet); + fw_iso_context_stop(s->context); + fw_iso_context_destroy(s->context); + s->context = ERR_PTR(-1); + iso_packets_buffer_destroy(&s->buffer, s->unit); + + s->callbacked = false; + + mutex_unlock(&s->mutex); +} +EXPORT_SYMBOL(amdtp_stream_stop); + +/** + * amdtp_stream_pcm_abort - abort the running PCM device + * @s: the AMDTP stream about to be stopped + * + * If the isochronous stream needs to be stopped asynchronously, call this + * function first to stop the PCM device. + */ +void amdtp_stream_pcm_abort(struct amdtp_stream *s) +{ + struct snd_pcm_substream *pcm; + + pcm = ACCESS_ONCE(s->pcm); + if (pcm) + snd_pcm_stop_xrun(pcm); +} +EXPORT_SYMBOL(amdtp_stream_pcm_abort); diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h new file mode 100644 index 000000000000..883bb1a7e25f --- /dev/null +++ b/sound/firewire/amdtp-stream.h @@ -0,0 +1,306 @@ +#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED +#define SOUND_FIREWIRE_AMDTP_H_INCLUDED + +#include +#include +#include +#include +#include "packets-buffer.h" + +/** + * enum cip_flags - describes details of the streaming protocol + * @CIP_NONBLOCKING: In non-blocking mode, each packet contains + * sample_rate/8000 samples, with rounding up or down to adjust + * for clock skew and left-over fractional samples. This should + * be used if supported by the device. + * @CIP_BLOCKING: In blocking mode, each packet contains either zero or + * SYT_INTERVAL samples, with these two types alternating so that + * the overall sample rate comes out right. + * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is + * generated by in packets. Defaultly this driver generates timestamp. + * @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0. + * @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet + * corresponds to the end of event in the packet. Out of IEC 61883. + * @CIP_WRONG_DBS: Only for in-stream. The value of dbs is wrong in in-packets. + * The value of data_block_quadlets is used instead of reported value. + * @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is + * skipped for detecting discontinuity. + * @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first + * packet is not continuous from an initial value. + * @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty + * packet is wrong but the others are correct. + * @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an + * packet is larger than IEC 61883-6 defines. Current implementation + * allows 5 times as large as IEC 61883-6 defines. + */ +enum cip_flags { + CIP_NONBLOCKING = 0x00, + CIP_BLOCKING = 0x01, + CIP_SYNC_TO_DEVICE = 0x02, + CIP_EMPTY_WITH_TAG0 = 0x04, + CIP_DBC_IS_END_EVENT = 0x08, + CIP_WRONG_DBS = 0x10, + CIP_SKIP_DBC_ZERO_CHECK = 0x20, + CIP_SKIP_INIT_DBC_CHECK = 0x40, + CIP_EMPTY_HAS_WRONG_DBC = 0x80, + CIP_JUMBO_PAYLOAD = 0x100, +}; + +/** + * enum cip_sfc - supported Sampling Frequency Codes (SFCs) + * @CIP_SFC_32000: 32,000 data blocks + * @CIP_SFC_44100: 44,100 data blocks + * @CIP_SFC_48000: 48,000 data blocks + * @CIP_SFC_88200: 88,200 data blocks + * @CIP_SFC_96000: 96,000 data blocks + * @CIP_SFC_176400: 176,400 data blocks + * @CIP_SFC_192000: 192,000 data blocks + * @CIP_SFC_COUNT: the number of supported SFCs + * + * These values are used to show nominal Sampling Frequency Code in + * Format Dependent Field (FDF) of AMDTP packet header. In IEC 61883-6:2002, + * this code means the number of events per second. Actually the code + * represents the number of data blocks transferred per second in an AMDTP + * stream. + * + * In IEC 61883-6:2005, some extensions were added to support more types of + * data such as 'One Bit LInear Audio', therefore the meaning of SFC became + * different depending on the types. + * + * Currently our implementation is compatible with IEC 61883-6:2002. + */ +enum cip_sfc { + CIP_SFC_32000 = 0, + CIP_SFC_44100 = 1, + CIP_SFC_48000 = 2, + CIP_SFC_88200 = 3, + CIP_SFC_96000 = 4, + CIP_SFC_176400 = 5, + CIP_SFC_192000 = 6, + CIP_SFC_COUNT +}; + +#define AMDTP_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32 + +#define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ + SNDRV_PCM_FMTBIT_S32) + + +/* + * This module supports maximum 64 PCM channels for one PCM stream + * This is for our convenience. + */ +#define AMDTP_MAX_CHANNELS_FOR_PCM 64 + +/* + * 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_pcm_runtime; +struct snd_rawmidi_substream; + +enum amdtp_stream_direction { + AMDTP_OUT_STREAM = 0, + AMDTP_IN_STREAM +}; + +struct amdtp_stream { + struct fw_unit *unit; + enum cip_flags flags; + enum amdtp_stream_direction direction; + struct mutex mutex; + + /* For packet processing. */ + struct fw_iso_context *context; + struct iso_packets_buffer buffer; + int packet_index; + + /* For CIP headers. */ + unsigned int source_node_id_field; + unsigned int data_block_quadlets; + unsigned int data_block_counter; + unsigned int fmt; + unsigned int fdf; + /* quirk: fixed interval of dbc between previos/current packets. */ + unsigned int tx_dbc_interval; + /* quirk: indicate the value of dbc field in a first packet. */ + unsigned int tx_first_dbc; + + /* Internal flags. */ + enum cip_sfc sfc; + unsigned int syt_interval; + unsigned int transfer_delay; + unsigned int data_block_state; + unsigned int last_syt_offset; + unsigned int syt_offset_state; + + /* For a PCM substream processing. */ + struct snd_pcm_substream *pcm; + struct tasklet_struct period_tasklet; + unsigned int pcm_buffer_pointer; + unsigned int pcm_period_pointer; + bool pointer_flush; + + /* To wait for first packet. */ + bool callbacked; + wait_queue_head_t callback_wait; + struct amdtp_stream *sync_slave; + + /* For AM824 processing. */ + struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; + int midi_fifo_limit; + int midi_fifo_used[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; + unsigned int pcm_channels; + unsigned int midi_ports; + + u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM]; + u8 midi_position; + + void (*transfer_samples)(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); + + unsigned int frame_multiplier; +}; + +int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, + enum amdtp_stream_direction dir, + enum cip_flags flags); +void amdtp_stream_destroy(struct amdtp_stream *s); + +int amdtp_stream_set_parameters(struct amdtp_stream *s, + unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports, + bool double_pcm_frames); +unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s); + +int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed); +void amdtp_stream_update(struct amdtp_stream *s); +void amdtp_stream_stop(struct amdtp_stream *s); + +int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, + struct snd_pcm_runtime *runtime); +void amdtp_stream_set_pcm_format(struct amdtp_stream *s, + snd_pcm_format_t format); +void amdtp_stream_pcm_prepare(struct amdtp_stream *s); +unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s); +void amdtp_stream_pcm_abort(struct amdtp_stream *s); + +extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT]; +extern const unsigned int amdtp_rate_table[CIP_SFC_COUNT]; + +/** + * amdtp_stream_running - check stream is running or not + * @s: the AMDTP stream + * + * If this function returns true, the stream is running. + */ +static inline bool amdtp_stream_running(struct amdtp_stream *s) +{ + return !IS_ERR(s->context); +} + +/** + * amdtp_streaming_error - check for streaming error + * @s: the AMDTP stream + * + * If this function returns true, the stream's packet queue has stopped due to + * an asynchronous error. + */ +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 + * @pcm: the PCM 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 PCM data. This function should be called from the PCM + * device's .trigger callback. + */ +static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s, + struct snd_pcm_substream *pcm) +{ + 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; +} + +static inline void amdtp_stream_set_sync(enum cip_flags sync_mode, + struct amdtp_stream *master, + struct amdtp_stream *slave) +{ + if (sync_mode == CIP_SYNC_TO_DEVICE) { + master->flags |= CIP_SYNC_TO_DEVICE; + slave->flags |= CIP_SYNC_TO_DEVICE; + master->sync_slave = slave; + } else { + master->flags &= ~CIP_SYNC_TO_DEVICE; + slave->flags &= ~CIP_SYNC_TO_DEVICE; + master->sync_slave = NULL; + } + + slave->sync_slave = NULL; +} + +/** + * amdtp_stream_wait_callback - sleep till callbacked or timeout + * @s: the AMDTP stream + * @timeout: msec till timeout + * + * If this function return false, the AMDTP stream should be stopped. + */ +static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s, + unsigned int timeout) +{ + return wait_event_timeout(s->callback_wait, + s->callbacked == true, + msecs_to_jiffies(timeout)) > 0; +} + +#endif diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c deleted file mode 100644 index b251f4b2b51c..000000000000 --- a/sound/firewire/amdtp.c +++ /dev/null @@ -1,1157 +0,0 @@ -/* - * Audio and Music Data Transmission Protocol (IEC 61883-6) streams - * with Common Isochronous Packet (IEC 61883-1) headers - * - * Copyright (c) Clemens Ladisch - * Licensed under the terms of the GNU General Public License, version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "amdtp.h" - -#define TICKS_PER_CYCLE 3072 -#define CYCLES_PER_SECOND 8000 -#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND) - -/* - * 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 - -#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */ - -/* isochronous header parameters */ -#define ISO_DATA_LENGTH_SHIFT 16 -#define TAG_CIP 1 - -/* common isochronous packet header parameters */ -#define CIP_EOH_SHIFT 31 -#define CIP_EOH (1u << CIP_EOH_SHIFT) -#define CIP_EOH_MASK 0x80000000 -#define CIP_SID_SHIFT 24 -#define CIP_SID_MASK 0x3f000000 -#define CIP_DBS_MASK 0x00ff0000 -#define CIP_DBS_SHIFT 16 -#define CIP_DBC_MASK 0x000000ff -#define CIP_FMT_SHIFT 24 -#define CIP_FMT_MASK 0x3f000000 -#define CIP_FDF_MASK 0x00ff0000 -#define CIP_FDF_SHIFT 16 -#define CIP_SYT_MASK 0x0000ffff -#define CIP_SYT_NO_INFO 0xffff - -/* - * Audio and Music transfer protocol specific parameters - * only "Clock-based rate control mode" is supported - */ -#define CIP_FMT_AM 0x10 -#define AMDTP_FDF_AM824 0x00 -#define AMDTP_FDF_NO_DATA 0xff - -/* TODO: make these configurable */ -#define INTERRUPT_INTERVAL 16 -#define QUEUE_LENGTH 48 - -#define IN_PACKET_HEADER_SIZE 4 -#define OUT_PACKET_HEADER_SIZE 0 - -static void pcm_period_tasklet(unsigned long data); - -/** - * amdtp_stream_init - initialize an AMDTP stream structure - * @s: the AMDTP stream to initialize - * @unit: the target of the stream - * @dir: the direction of stream - * @flags: the packet transmission method to use - */ -int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, - enum amdtp_stream_direction dir, enum cip_flags flags) -{ - s->unit = unit; - s->direction = dir; - s->flags = flags; - s->context = ERR_PTR(-1); - mutex_init(&s->mutex); - tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s); - s->packet_index = 0; - - init_waitqueue_head(&s->callback_wait); - s->callbacked = false; - s->sync_slave = NULL; - - s->fmt = CIP_FMT_AM; - - return 0; -} -EXPORT_SYMBOL(amdtp_stream_init); - -/** - * amdtp_stream_destroy - free stream resources - * @s: the AMDTP stream to destroy - */ -void amdtp_stream_destroy(struct amdtp_stream *s) -{ - WARN_ON(amdtp_stream_running(s)); - mutex_destroy(&s->mutex); -} -EXPORT_SYMBOL(amdtp_stream_destroy); - -const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = { - [CIP_SFC_32000] = 8, - [CIP_SFC_44100] = 8, - [CIP_SFC_48000] = 8, - [CIP_SFC_88200] = 16, - [CIP_SFC_96000] = 16, - [CIP_SFC_176400] = 32, - [CIP_SFC_192000] = 32, -}; -EXPORT_SYMBOL(amdtp_syt_intervals); - -const unsigned int amdtp_rate_table[CIP_SFC_COUNT] = { - [CIP_SFC_32000] = 32000, - [CIP_SFC_44100] = 44100, - [CIP_SFC_48000] = 48000, - [CIP_SFC_88200] = 88200, - [CIP_SFC_96000] = 96000, - [CIP_SFC_176400] = 176400, - [CIP_SFC_192000] = 192000, -}; -EXPORT_SYMBOL(amdtp_rate_table); - -/** - * amdtp_stream_add_pcm_hw_constraints - add hw constraints for PCM substream - * @s: the AMDTP stream, which must be initialized. - * @runtime: the PCM substream runtime - */ -int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, - struct snd_pcm_runtime *runtime) -{ - int err; - - /* AM824 in IEC 61883-6 can deliver 24bit data */ - err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); - if (err < 0) - goto end; - - /* - * Currently firewire-lib processes 16 packets in one software - * interrupt callback. This equals to 2msec but actually the - * interval of the interrupts has a jitter. - * Additionally, even if adding a constraint to fit period size to - * 2msec, actual calculated frames per period doesn't equal to 2msec, - * depending on sampling rate. - * Anyway, the interval to call snd_pcm_period_elapsed() cannot 2msec. - * Here let us use 5msec for safe period interrupt. - */ - err = snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_PERIOD_TIME, - 5000, UINT_MAX); - if (err < 0) - goto end; - - /* Non-Blocking stream has no more constraints */ - if (!(s->flags & CIP_BLOCKING)) - goto end; - - /* - * One AMDTP packet can include some frames. In blocking mode, the - * number equals to SYT_INTERVAL. So the number is 8, 16 or 32, - * depending on its sampling rate. For accurate period interrupt, it's - * preferrable to align period/buffer sizes to current SYT_INTERVAL. - * - * TODO: These constraints can be improved with proper rules. - * Currently apply LCM of SYT_INTERVALs. - */ - err = snd_pcm_hw_constraint_step(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32); - if (err < 0) - goto end; - err = snd_pcm_hw_constraint_step(runtime, 0, - SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32); -end: - return err; -} -EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints); - -/** - * amdtp_stream_set_parameters - set stream parameters - * @s: the AMDTP stream to configure - * @rate: the sample rate - * @pcm_channels: the number of PCM samples in each data block, to be encoded - * as AM824 multi-bit linear audio - * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) - * @double_pcm_frames: one data block transfers two PCM frames - * - * The parameters must be set before the stream is started, and must not be - * changed while the stream is running. - */ -int amdtp_stream_set_parameters(struct amdtp_stream *s, - unsigned int rate, - unsigned int pcm_channels, - unsigned int midi_ports, - bool double_pcm_frames) -{ - unsigned int i, sfc, midi_channels; - - midi_channels = DIV_ROUND_UP(midi_ports, 8); - - if (WARN_ON(amdtp_stream_running(s)) | - WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) | - WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI)) - return -EINVAL; - - for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) { - if (amdtp_rate_table[sfc] == rate) - break; - } - if (sfc == ARRAY_SIZE(amdtp_rate_table)) - return -EINVAL; - - s->pcm_channels = pcm_channels; - s->sfc = sfc; - s->data_block_quadlets = s->pcm_channels + midi_channels; - s->midi_ports = midi_ports; - - s->fdf = AMDTP_FDF_AM824 | s->sfc; - - /* - * In IEC 61883-6, one data block represents one event. In ALSA, one - * event equals to one PCM frame. But Dice has a quirk at higher - * sampling rate to transfer two PCM frames in one data block. - */ - if (double_pcm_frames) - s->frame_multiplier = 2; - else - s->frame_multiplier = 1; - - s->syt_interval = amdtp_syt_intervals[sfc]; - - /* default buffering in the device */ - s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; - if (s->flags & CIP_BLOCKING) - /* additional buffering needed to adjust for no-data packets */ - s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate; - - /* init the position map for PCM and MIDI channels */ - for (i = 0; i < pcm_channels; i++) - s->pcm_positions[i] = i; - s->midi_position = s->pcm_channels; - - /* - * 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().) - */ - s->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1; - - return 0; -} -EXPORT_SYMBOL(amdtp_stream_set_parameters); - -/** - * amdtp_stream_get_max_payload - get the stream's packet size - * @s: the AMDTP stream - * - * This function must not be called before the stream has been configured - * with amdtp_stream_set_parameters(). - */ -unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s) -{ - unsigned int multiplier = 1; - - if (s->flags & CIP_JUMBO_PAYLOAD) - multiplier = 5; - - return 8 + s->syt_interval * s->data_block_quadlets * 4 * multiplier; -} -EXPORT_SYMBOL(amdtp_stream_get_max_payload); - -static void write_pcm_s16(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); -static void write_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); -static void read_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); - -/** - * amdtp_stream_set_pcm_format - set the PCM format - * @s: the AMDTP stream to configure - * @format: the format of the ALSA PCM device - * - * The sample format must be set after the other parameters (rate/PCM channels/ - * MIDI) and before the stream is started, and must not be changed while the - * stream is running. - */ -void amdtp_stream_set_pcm_format(struct amdtp_stream *s, - snd_pcm_format_t format) -{ - if (WARN_ON(amdtp_stream_pcm_running(s))) - return; - - switch (format) { - default: - WARN_ON(1); - /* fall through */ - case SNDRV_PCM_FORMAT_S16: - if (s->direction == AMDTP_OUT_STREAM) { - s->transfer_samples = write_pcm_s16; - break; - } - WARN_ON(1); - /* fall through */ - case SNDRV_PCM_FORMAT_S32: - if (s->direction == AMDTP_OUT_STREAM) - s->transfer_samples = write_pcm_s32; - else - s->transfer_samples = read_pcm_s32; - break; - } -} -EXPORT_SYMBOL(amdtp_stream_set_pcm_format); - -/** - * amdtp_stream_pcm_prepare - prepare PCM device for running - * @s: the AMDTP stream - * - * This function should be called from the PCM device's .prepare callback. - */ -void amdtp_stream_pcm_prepare(struct amdtp_stream *s) -{ - tasklet_kill(&s->period_tasklet); - s->pcm_buffer_pointer = 0; - s->pcm_period_pointer = 0; - s->pointer_flush = true; -} -EXPORT_SYMBOL(amdtp_stream_pcm_prepare); - -static unsigned int calculate_data_blocks(struct amdtp_stream *s, - unsigned int syt) -{ - unsigned int phase, data_blocks; - - /* Blocking mode. */ - if (s->flags & CIP_BLOCKING) { - /* This module generate empty packet for 'no data'. */ - if (syt == CIP_SYT_NO_INFO) - data_blocks = 0; - else - data_blocks = s->syt_interval; - /* Non-blocking mode. */ - } else { - if (!cip_sfc_is_base_44100(s->sfc)) { - /* Sample_rate / 8000 is an integer, and precomputed. */ - data_blocks = s->data_block_state; - } else { - phase = s->data_block_state; - - /* - * This calculates the number of data blocks per packet so that - * 1) the overall rate is correct and exactly synchronized to - * the bus clock, and - * 2) packets with a rounded-up number of blocks occur as early - * as possible in the sequence (to prevent underruns of the - * device's buffer). - */ - if (s->sfc == CIP_SFC_44100) - /* 6 6 5 6 5 6 5 ... */ - data_blocks = 5 + ((phase & 1) ^ - (phase == 0 || phase >= 40)); - else - /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */ - data_blocks = 11 * (s->sfc >> 1) + (phase == 0); - if (++phase >= (80 >> (s->sfc >> 1))) - phase = 0; - s->data_block_state = phase; - } - } - - return data_blocks; -} - -static unsigned int calculate_syt(struct amdtp_stream *s, - unsigned int cycle) -{ - unsigned int syt_offset, phase, index, syt; - - if (s->last_syt_offset < TICKS_PER_CYCLE) { - if (!cip_sfc_is_base_44100(s->sfc)) - syt_offset = s->last_syt_offset + s->syt_offset_state; - else { - /* - * The time, in ticks, of the n'th SYT_INTERVAL sample is: - * n * SYT_INTERVAL * 24576000 / sample_rate - * Modulo TICKS_PER_CYCLE, the difference between successive - * elements is about 1386.23. Rounding the results of this - * formula to the SYT precision results in a sequence of - * differences that begins with: - * 1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ... - * This code generates _exactly_ the same sequence. - */ - phase = s->syt_offset_state; - index = phase % 13; - syt_offset = s->last_syt_offset; - syt_offset += 1386 + ((index && !(index & 3)) || - phase == 146); - if (++phase >= 147) - phase = 0; - s->syt_offset_state = phase; - } - } else - syt_offset = s->last_syt_offset - TICKS_PER_CYCLE; - s->last_syt_offset = syt_offset; - - if (syt_offset < TICKS_PER_CYCLE) { - syt_offset += s->transfer_delay; - syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; - syt += syt_offset % TICKS_PER_CYCLE; - - return syt & CIP_SYT_MASK; - } else { - return CIP_SYT_NO_INFO; - } -} - -static void write_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) -{ - struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; - const u32 *src; - - channels = s->pcm_channels; - src = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; - - for (i = 0; i < frames; ++i) { - for (c = 0; c < channels; ++c) { - buffer[s->pcm_positions[c]] = - cpu_to_be32((*src >> 8) | 0x40000000); - src++; - } - buffer += s->data_block_quadlets; - if (--remaining_frames == 0) - src = (void *)runtime->dma_area; - } -} - -static void write_pcm_s16(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) -{ - struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; - const u16 *src; - - channels = s->pcm_channels; - src = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; - - for (i = 0; i < frames; ++i) { - for (c = 0; c < channels; ++c) { - buffer[s->pcm_positions[c]] = - cpu_to_be32((*src << 8) | 0x42000000); - src++; - } - buffer += s->data_block_quadlets; - if (--remaining_frames == 0) - src = (void *)runtime->dma_area; - } -} - -static void read_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) -{ - struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; - u32 *dst; - - channels = s->pcm_channels; - dst = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; - - for (i = 0; i < frames; ++i) { - for (c = 0; c < channels; ++c) { - *dst = be32_to_cpu(buffer[s->pcm_positions[c]]) << 8; - dst++; - } - buffer += s->data_block_quadlets; - if (--remaining_frames == 0) - dst = (void *)runtime->dma_area; - } -} - -static void write_pcm_silence(struct amdtp_stream *s, - __be32 *buffer, unsigned int frames) -{ - unsigned int i, c; - - for (i = 0; i < frames; ++i) { - for (c = 0; c < s->pcm_channels; ++c) - buffer[s->pcm_positions[c]] = cpu_to_be32(0x40000000); - buffer += s->data_block_quadlets; - } -} - -/* - * To avoid sending MIDI bytes at too high a rate, assume that the receiving - * device has a FIFO, and track how much it is filled. This values increases - * by one whenever we send one byte in a packet, but the FIFO empties at - * a constant rate independent of our packet rate. One packet has syt_interval - * samples, so the number of bytes that empty out of the FIFO, per packet(!), - * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate. To avoid storing - * fractional values, the values in midi_fifo_used[] are measured in bytes - * multiplied by the sample rate. - */ -static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port) -{ - int used; - - used = s->midi_fifo_used[port]; - if (used == 0) /* common shortcut */ - return true; - - used -= MIDI_BYTES_PER_SECOND * s->syt_interval; - used = max(used, 0); - s->midi_fifo_used[port] = used; - - return used < s->midi_fifo_limit; -} - -static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port) -{ - s->midi_fifo_used[port] += amdtp_rate_table[s->sfc]; -} - -static void write_midi_messages(struct amdtp_stream *s, - __be32 *buffer, unsigned int frames) -{ - unsigned int f, port; - u8 *b; - - for (f = 0; f < frames; f++) { - b = (u8 *)&buffer[s->midi_position]; - - port = (s->data_block_counter + f) % 8; - if (f < MAX_MIDI_RX_BLOCKS && - midi_ratelimit_per_packet(s, port) && - s->midi[port] != NULL && - snd_rawmidi_transmit(s->midi[port], &b[1], 1) == 1) { - midi_rate_use_one_byte(s, port); - b[0] = 0x81; - } else { - b[0] = 0x80; - b[1] = 0; - } - b[2] = 0; - b[3] = 0; - - buffer += s->data_block_quadlets; - } -} - -static void read_midi_messages(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->midi_position]; - - 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, - struct snd_pcm_substream *pcm, - unsigned int frames) -{ - unsigned int ptr; - - ptr = s->pcm_buffer_pointer + frames; - if (ptr >= pcm->runtime->buffer_size) - ptr -= pcm->runtime->buffer_size; - ACCESS_ONCE(s->pcm_buffer_pointer) = ptr; - - s->pcm_period_pointer += frames; - if (s->pcm_period_pointer >= pcm->runtime->period_size) { - s->pcm_period_pointer -= pcm->runtime->period_size; - s->pointer_flush = false; - tasklet_hi_schedule(&s->period_tasklet); - } -} - -static void pcm_period_tasklet(unsigned long data) -{ - struct amdtp_stream *s = (void *)data; - struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); - - if (pcm) - snd_pcm_period_elapsed(pcm); -} - -static int queue_packet(struct amdtp_stream *s, - unsigned int header_length, - unsigned int payload_length, bool skip) -{ - struct fw_iso_packet p = {0}; - int err = 0; - - if (IS_ERR(s->context)) - goto end; - - p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL); - p.tag = TAG_CIP; - p.header_length = header_length; - p.payload_length = (!skip) ? payload_length : 0; - p.skip = skip; - err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer, - s->buffer.packets[s->packet_index].offset); - if (err < 0) { - dev_err(&s->unit->device, "queueing error: %d\n", err); - goto end; - } - - if (++s->packet_index >= QUEUE_LENGTH) - s->packet_index = 0; -end: - return err; -} - -static inline int queue_out_packet(struct amdtp_stream *s, - unsigned int payload_length, bool skip) -{ - return queue_packet(s, OUT_PACKET_HEADER_SIZE, - payload_length, skip); -} - -static inline int queue_in_packet(struct amdtp_stream *s) -{ - return queue_packet(s, IN_PACKET_HEADER_SIZE, - amdtp_stream_get_max_payload(s), false); -} - -unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer, - unsigned int data_blocks, unsigned int *syt) -{ - struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); - unsigned int pcm_frames; - - if (pcm) { - s->transfer_samples(s, pcm, buffer, data_blocks); - pcm_frames = data_blocks * s->frame_multiplier; - } else { - write_pcm_silence(s, buffer, data_blocks); - pcm_frames = 0; - } - - if (s->midi_ports) - write_midi_messages(s, buffer, data_blocks); - - return pcm_frames; -} - -static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, - unsigned int syt) -{ - __be32 *buffer; - unsigned int payload_length; - unsigned int pcm_frames; - struct snd_pcm_substream *pcm; - - buffer = s->buffer.packets[s->packet_index].buffer; - pcm_frames = process_rx_data_blocks(s, buffer + 2, data_blocks, &syt); - - buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | - (s->data_block_quadlets << CIP_DBS_SHIFT) | - s->data_block_counter); - buffer[1] = cpu_to_be32(CIP_EOH | - ((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) | - ((s->fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) | - (syt & CIP_SYT_MASK)); - - s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; - - payload_length = 8 + data_blocks * 4 * s->data_block_quadlets; - if (queue_out_packet(s, payload_length, false) < 0) - return -EIO; - - pcm = ACCESS_ONCE(s->pcm); - if (pcm && pcm_frames > 0) - update_pcm_pointers(s, pcm, pcm_frames); - - /* No need to return the number of handled data blocks. */ - return 0; -} - -unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, - unsigned int data_blocks, unsigned int *syt) -{ - struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); - unsigned int pcm_frames; - - if (pcm) { - s->transfer_samples(s, pcm, buffer, data_blocks); - pcm_frames = data_blocks * s->frame_multiplier; - } else { - pcm_frames = 0; - } - - if (s->midi_ports) - read_midi_messages(s, buffer, data_blocks); - - return pcm_frames; -} - -static int handle_in_packet(struct amdtp_stream *s, - unsigned int payload_quadlets, __be32 *buffer, - unsigned int *data_blocks, unsigned int syt) -{ - u32 cip_header[2]; - unsigned int fmt, fdf; - unsigned int data_block_quadlets, data_block_counter, dbc_interval; - struct snd_pcm_substream *pcm; - unsigned int pcm_frames; - bool lost; - - cip_header[0] = be32_to_cpu(buffer[0]); - cip_header[1] = be32_to_cpu(buffer[1]); - - /* - * This module supports 'Two-quadlet CIP header with SYT field'. - * For convenience, also check FMT field is AM824 or not. - */ - if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) || - ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) { - dev_info_ratelimited(&s->unit->device, - "Invalid CIP header for AMDTP: %08X:%08X\n", - cip_header[0], cip_header[1]); - *data_blocks = 0; - pcm_frames = 0; - goto end; - } - - /* Check valid protocol or not. */ - fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT; - if (fmt != s->fmt) { - dev_err(&s->unit->device, - "Detect unexpected protocol: %08x %08x\n", - cip_header[0], cip_header[1]); - return -EIO; - } - - /* Calculate data blocks */ - fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT; - if (payload_quadlets < 3 || - (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) { - *data_blocks = 0; - } else { - data_block_quadlets = - (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT; - /* avoid division by zero */ - if (data_block_quadlets == 0) { - dev_err(&s->unit->device, - "Detect invalid value in dbs field: %08X\n", - cip_header[0]); - return -EPROTO; - } - if (s->flags & CIP_WRONG_DBS) - data_block_quadlets = s->data_block_quadlets; - - *data_blocks = (payload_quadlets - 2) / data_block_quadlets; - } - - /* Check data block counter continuity */ - data_block_counter = cip_header[0] & CIP_DBC_MASK; - if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) && - s->data_block_counter != UINT_MAX) - data_block_counter = s->data_block_counter; - - if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) && - data_block_counter == s->tx_first_dbc) || - s->data_block_counter == UINT_MAX) { - lost = false; - } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) { - lost = data_block_counter != s->data_block_counter; - } else { - if ((*data_blocks > 0) && (s->tx_dbc_interval > 0)) - dbc_interval = s->tx_dbc_interval; - else - dbc_interval = *data_blocks; - - lost = data_block_counter != - ((s->data_block_counter + dbc_interval) & 0xff); - } - - if (lost) { - dev_err(&s->unit->device, - "Detect discontinuity of CIP: %02X %02X\n", - s->data_block_counter, data_block_counter); - return -EIO; - } - - pcm_frames = process_tx_data_blocks(s, buffer + 2, *data_blocks, &syt); - - if (s->flags & CIP_DBC_IS_END_EVENT) - s->data_block_counter = data_block_counter; - else - s->data_block_counter = - (data_block_counter + *data_blocks) & 0xff; -end: - if (queue_in_packet(s) < 0) - return -EIO; - - pcm = ACCESS_ONCE(s->pcm); - if (pcm && pcm_frames > 0) - update_pcm_pointers(s, pcm, pcm_frames); - - return 0; -} - -static void out_stream_callback(struct fw_iso_context *context, u32 cycle, - size_t header_length, void *header, - void *private_data) -{ - struct amdtp_stream *s = private_data; - unsigned int i, syt, packets = header_length / 4; - unsigned int data_blocks; - - if (s->packet_index < 0) - return; - - /* - * Compute the cycle of the last queued packet. - * (We need only the four lowest bits for the SYT, so we can ignore - * that bits 0-11 must wrap around at 3072.) - */ - cycle += QUEUE_LENGTH - packets; - - for (i = 0; i < packets; ++i) { - syt = calculate_syt(s, ++cycle); - data_blocks = calculate_data_blocks(s, syt); - - if (handle_out_packet(s, data_blocks, syt) < 0) { - s->packet_index = -1; - amdtp_stream_pcm_abort(s); - return; - } - } - - fw_iso_context_queue_flush(s->context); -} - -static void in_stream_callback(struct fw_iso_context *context, u32 cycle, - size_t header_length, void *header, - void *private_data) -{ - struct amdtp_stream *s = private_data; - unsigned int p, syt, packets; - unsigned int payload_quadlets, max_payload_quadlets; - unsigned int data_blocks; - __be32 *buffer, *headers = header; - - if (s->packet_index < 0) - return; - - /* The number of packets in buffer */ - packets = header_length / IN_PACKET_HEADER_SIZE; - - /* For buffer-over-run prevention. */ - max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4; - - for (p = 0; p < packets; p++) { - buffer = s->buffer.packets[s->packet_index].buffer; - - /* The number of quadlets in this packet */ - payload_quadlets = - (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4; - if (payload_quadlets > max_payload_quadlets) { - dev_err(&s->unit->device, - "Detect jumbo payload: %02x %02x\n", - payload_quadlets, max_payload_quadlets); - s->packet_index = -1; - break; - } - - syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK; - if (handle_in_packet(s, payload_quadlets, buffer, - &data_blocks, syt) < 0) { - s->packet_index = -1; - break; - } - - /* Process sync slave stream */ - if (s->sync_slave && s->sync_slave->callbacked) { - if (handle_out_packet(s->sync_slave, - data_blocks, syt) < 0) { - s->packet_index = -1; - break; - } - } - } - - /* Queueing error or detecting discontinuity */ - if (s->packet_index < 0) { - amdtp_stream_pcm_abort(s); - - /* Abort sync slave. */ - if (s->sync_slave) { - s->sync_slave->packet_index = -1; - amdtp_stream_pcm_abort(s->sync_slave); - } - return; - } - - /* when sync to device, flush the packets for slave stream */ - if (s->sync_slave && s->sync_slave->callbacked) - fw_iso_context_queue_flush(s->sync_slave->context); - - fw_iso_context_queue_flush(s->context); -} - -/* processing is done by master callback */ -static void slave_stream_callback(struct fw_iso_context *context, u32 cycle, - size_t header_length, void *header, - void *private_data) -{ - return; -} - -/* this is executed one time */ -static void amdtp_stream_first_callback(struct fw_iso_context *context, - u32 cycle, size_t header_length, - void *header, void *private_data) -{ - struct amdtp_stream *s = private_data; - - /* - * For in-stream, first packet has come. - * For out-stream, prepared to transmit first packet - */ - s->callbacked = true; - wake_up(&s->callback_wait); - - if (s->direction == AMDTP_IN_STREAM) - context->callback.sc = in_stream_callback; - else if (s->flags & CIP_SYNC_TO_DEVICE) - context->callback.sc = slave_stream_callback; - else - context->callback.sc = out_stream_callback; - - context->callback.sc(context, cycle, header_length, header, s); -} - -/** - * amdtp_stream_start - start transferring packets - * @s: the AMDTP stream to start - * @channel: the isochronous channel on the bus - * @speed: firewire speed code - * - * The stream cannot be started until it has been configured with - * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI - * device can be started. - */ -int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) -{ - static const struct { - unsigned int data_block; - unsigned int syt_offset; - } initial_state[] = { - [CIP_SFC_32000] = { 4, 3072 }, - [CIP_SFC_48000] = { 6, 1024 }, - [CIP_SFC_96000] = { 12, 1024 }, - [CIP_SFC_192000] = { 24, 1024 }, - [CIP_SFC_44100] = { 0, 67 }, - [CIP_SFC_88200] = { 0, 67 }, - [CIP_SFC_176400] = { 0, 67 }, - }; - unsigned int header_size; - enum dma_data_direction dir; - int type, tag, err; - - mutex_lock(&s->mutex); - - if (WARN_ON(amdtp_stream_running(s) || - (s->data_block_quadlets < 1))) { - err = -EBADFD; - goto err_unlock; - } - - if (s->direction == AMDTP_IN_STREAM && - s->flags & CIP_SKIP_INIT_DBC_CHECK) - s->data_block_counter = UINT_MAX; - else - s->data_block_counter = 0; - s->data_block_state = initial_state[s->sfc].data_block; - s->syt_offset_state = initial_state[s->sfc].syt_offset; - s->last_syt_offset = TICKS_PER_CYCLE; - - /* initialize packet buffer */ - if (s->direction == AMDTP_IN_STREAM) { - dir = DMA_FROM_DEVICE; - type = FW_ISO_CONTEXT_RECEIVE; - header_size = IN_PACKET_HEADER_SIZE; - } else { - dir = DMA_TO_DEVICE; - type = FW_ISO_CONTEXT_TRANSMIT; - header_size = OUT_PACKET_HEADER_SIZE; - } - err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, - amdtp_stream_get_max_payload(s), dir); - if (err < 0) - goto err_unlock; - - s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, - type, channel, speed, header_size, - amdtp_stream_first_callback, s); - if (IS_ERR(s->context)) { - err = PTR_ERR(s->context); - if (err == -EBUSY) - dev_err(&s->unit->device, - "no free stream on this controller\n"); - goto err_buffer; - } - - amdtp_stream_update(s); - - s->packet_index = 0; - do { - if (s->direction == AMDTP_IN_STREAM) - err = queue_in_packet(s); - else - err = queue_out_packet(s, 0, true); - if (err < 0) - goto err_context; - } while (s->packet_index > 0); - - /* NOTE: TAG1 matches CIP. This just affects in stream. */ - tag = FW_ISO_CONTEXT_MATCH_TAG1; - if (s->flags & CIP_EMPTY_WITH_TAG0) - tag |= FW_ISO_CONTEXT_MATCH_TAG0; - - s->callbacked = false; - err = fw_iso_context_start(s->context, -1, 0, tag); - if (err < 0) - goto err_context; - - mutex_unlock(&s->mutex); - - return 0; - -err_context: - fw_iso_context_destroy(s->context); - s->context = ERR_PTR(-1); -err_buffer: - iso_packets_buffer_destroy(&s->buffer, s->unit); -err_unlock: - mutex_unlock(&s->mutex); - - return err; -} -EXPORT_SYMBOL(amdtp_stream_start); - -/** - * amdtp_stream_pcm_pointer - get the PCM buffer position - * @s: the AMDTP stream that transports the PCM data - * - * Returns the current buffer position, in frames. - */ -unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s) -{ - /* this optimization is allowed to be racy */ - if (s->pointer_flush && amdtp_stream_running(s)) - fw_iso_context_flush_completions(s->context); - else - s->pointer_flush = true; - - return ACCESS_ONCE(s->pcm_buffer_pointer); -} -EXPORT_SYMBOL(amdtp_stream_pcm_pointer); - -/** - * amdtp_stream_update - update the stream after a bus reset - * @s: the AMDTP stream - */ -void amdtp_stream_update(struct amdtp_stream *s) -{ - /* Precomputing. */ - ACCESS_ONCE(s->source_node_id_field) = - (fw_parent_device(s->unit)->card->node_id << CIP_SID_SHIFT) & - CIP_SID_MASK; -} -EXPORT_SYMBOL(amdtp_stream_update); - -/** - * amdtp_stream_stop - stop sending packets - * @s: the AMDTP stream to stop - * - * All PCM and MIDI devices of the stream must be stopped before the stream - * itself can be stopped. - */ -void amdtp_stream_stop(struct amdtp_stream *s) -{ - mutex_lock(&s->mutex); - - if (!amdtp_stream_running(s)) { - mutex_unlock(&s->mutex); - return; - } - - tasklet_kill(&s->period_tasklet); - fw_iso_context_stop(s->context); - fw_iso_context_destroy(s->context); - s->context = ERR_PTR(-1); - iso_packets_buffer_destroy(&s->buffer, s->unit); - - s->callbacked = false; - - mutex_unlock(&s->mutex); -} -EXPORT_SYMBOL(amdtp_stream_stop); - -/** - * amdtp_stream_pcm_abort - abort the running PCM device - * @s: the AMDTP stream about to be stopped - * - * If the isochronous stream needs to be stopped asynchronously, call this - * function first to stop the PCM device. - */ -void amdtp_stream_pcm_abort(struct amdtp_stream *s) -{ - struct snd_pcm_substream *pcm; - - pcm = ACCESS_ONCE(s->pcm); - if (pcm) - snd_pcm_stop_xrun(pcm); -} -EXPORT_SYMBOL(amdtp_stream_pcm_abort); diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h deleted file mode 100644 index 883bb1a7e25f..000000000000 --- a/sound/firewire/amdtp.h +++ /dev/null @@ -1,306 +0,0 @@ -#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED -#define SOUND_FIREWIRE_AMDTP_H_INCLUDED - -#include -#include -#include -#include -#include "packets-buffer.h" - -/** - * enum cip_flags - describes details of the streaming protocol - * @CIP_NONBLOCKING: In non-blocking mode, each packet contains - * sample_rate/8000 samples, with rounding up or down to adjust - * for clock skew and left-over fractional samples. This should - * be used if supported by the device. - * @CIP_BLOCKING: In blocking mode, each packet contains either zero or - * SYT_INTERVAL samples, with these two types alternating so that - * the overall sample rate comes out right. - * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is - * generated by in packets. Defaultly this driver generates timestamp. - * @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0. - * @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet - * corresponds to the end of event in the packet. Out of IEC 61883. - * @CIP_WRONG_DBS: Only for in-stream. The value of dbs is wrong in in-packets. - * The value of data_block_quadlets is used instead of reported value. - * @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is - * skipped for detecting discontinuity. - * @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first - * packet is not continuous from an initial value. - * @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty - * packet is wrong but the others are correct. - * @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an - * packet is larger than IEC 61883-6 defines. Current implementation - * allows 5 times as large as IEC 61883-6 defines. - */ -enum cip_flags { - CIP_NONBLOCKING = 0x00, - CIP_BLOCKING = 0x01, - CIP_SYNC_TO_DEVICE = 0x02, - CIP_EMPTY_WITH_TAG0 = 0x04, - CIP_DBC_IS_END_EVENT = 0x08, - CIP_WRONG_DBS = 0x10, - CIP_SKIP_DBC_ZERO_CHECK = 0x20, - CIP_SKIP_INIT_DBC_CHECK = 0x40, - CIP_EMPTY_HAS_WRONG_DBC = 0x80, - CIP_JUMBO_PAYLOAD = 0x100, -}; - -/** - * enum cip_sfc - supported Sampling Frequency Codes (SFCs) - * @CIP_SFC_32000: 32,000 data blocks - * @CIP_SFC_44100: 44,100 data blocks - * @CIP_SFC_48000: 48,000 data blocks - * @CIP_SFC_88200: 88,200 data blocks - * @CIP_SFC_96000: 96,000 data blocks - * @CIP_SFC_176400: 176,400 data blocks - * @CIP_SFC_192000: 192,000 data blocks - * @CIP_SFC_COUNT: the number of supported SFCs - * - * These values are used to show nominal Sampling Frequency Code in - * Format Dependent Field (FDF) of AMDTP packet header. In IEC 61883-6:2002, - * this code means the number of events per second. Actually the code - * represents the number of data blocks transferred per second in an AMDTP - * stream. - * - * In IEC 61883-6:2005, some extensions were added to support more types of - * data such as 'One Bit LInear Audio', therefore the meaning of SFC became - * different depending on the types. - * - * Currently our implementation is compatible with IEC 61883-6:2002. - */ -enum cip_sfc { - CIP_SFC_32000 = 0, - CIP_SFC_44100 = 1, - CIP_SFC_48000 = 2, - CIP_SFC_88200 = 3, - CIP_SFC_96000 = 4, - CIP_SFC_176400 = 5, - CIP_SFC_192000 = 6, - CIP_SFC_COUNT -}; - -#define AMDTP_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32 - -#define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ - SNDRV_PCM_FMTBIT_S32) - - -/* - * This module supports maximum 64 PCM channels for one PCM stream - * This is for our convenience. - */ -#define AMDTP_MAX_CHANNELS_FOR_PCM 64 - -/* - * 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_pcm_runtime; -struct snd_rawmidi_substream; - -enum amdtp_stream_direction { - AMDTP_OUT_STREAM = 0, - AMDTP_IN_STREAM -}; - -struct amdtp_stream { - struct fw_unit *unit; - enum cip_flags flags; - enum amdtp_stream_direction direction; - struct mutex mutex; - - /* For packet processing. */ - struct fw_iso_context *context; - struct iso_packets_buffer buffer; - int packet_index; - - /* For CIP headers. */ - unsigned int source_node_id_field; - unsigned int data_block_quadlets; - unsigned int data_block_counter; - unsigned int fmt; - unsigned int fdf; - /* quirk: fixed interval of dbc between previos/current packets. */ - unsigned int tx_dbc_interval; - /* quirk: indicate the value of dbc field in a first packet. */ - unsigned int tx_first_dbc; - - /* Internal flags. */ - enum cip_sfc sfc; - unsigned int syt_interval; - unsigned int transfer_delay; - unsigned int data_block_state; - unsigned int last_syt_offset; - unsigned int syt_offset_state; - - /* For a PCM substream processing. */ - struct snd_pcm_substream *pcm; - struct tasklet_struct period_tasklet; - unsigned int pcm_buffer_pointer; - unsigned int pcm_period_pointer; - bool pointer_flush; - - /* To wait for first packet. */ - bool callbacked; - wait_queue_head_t callback_wait; - struct amdtp_stream *sync_slave; - - /* For AM824 processing. */ - struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; - int midi_fifo_limit; - int midi_fifo_used[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; - unsigned int pcm_channels; - unsigned int midi_ports; - - u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM]; - u8 midi_position; - - void (*transfer_samples)(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); - - unsigned int frame_multiplier; -}; - -int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, - enum amdtp_stream_direction dir, - enum cip_flags flags); -void amdtp_stream_destroy(struct amdtp_stream *s); - -int amdtp_stream_set_parameters(struct amdtp_stream *s, - unsigned int rate, - unsigned int pcm_channels, - unsigned int midi_ports, - bool double_pcm_frames); -unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s); - -int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed); -void amdtp_stream_update(struct amdtp_stream *s); -void amdtp_stream_stop(struct amdtp_stream *s); - -int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, - struct snd_pcm_runtime *runtime); -void amdtp_stream_set_pcm_format(struct amdtp_stream *s, - snd_pcm_format_t format); -void amdtp_stream_pcm_prepare(struct amdtp_stream *s); -unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s); -void amdtp_stream_pcm_abort(struct amdtp_stream *s); - -extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT]; -extern const unsigned int amdtp_rate_table[CIP_SFC_COUNT]; - -/** - * amdtp_stream_running - check stream is running or not - * @s: the AMDTP stream - * - * If this function returns true, the stream is running. - */ -static inline bool amdtp_stream_running(struct amdtp_stream *s) -{ - return !IS_ERR(s->context); -} - -/** - * amdtp_streaming_error - check for streaming error - * @s: the AMDTP stream - * - * If this function returns true, the stream's packet queue has stopped due to - * an asynchronous error. - */ -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 - * @pcm: the PCM 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 PCM data. This function should be called from the PCM - * device's .trigger callback. - */ -static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s, - struct snd_pcm_substream *pcm) -{ - 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; -} - -static inline void amdtp_stream_set_sync(enum cip_flags sync_mode, - struct amdtp_stream *master, - struct amdtp_stream *slave) -{ - if (sync_mode == CIP_SYNC_TO_DEVICE) { - master->flags |= CIP_SYNC_TO_DEVICE; - slave->flags |= CIP_SYNC_TO_DEVICE; - master->sync_slave = slave; - } else { - master->flags &= ~CIP_SYNC_TO_DEVICE; - slave->flags &= ~CIP_SYNC_TO_DEVICE; - master->sync_slave = NULL; - } - - slave->sync_slave = NULL; -} - -/** - * amdtp_stream_wait_callback - sleep till callbacked or timeout - * @s: the AMDTP stream - * @timeout: msec till timeout - * - * If this function return false, the AMDTP stream should be stopped. - */ -static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s, - unsigned int timeout) -{ - return wait_event_timeout(s->callback_wait, - s->callbacked == true, - msecs_to_jiffies(timeout)) > 0; -} - -#endif diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index d23caca7f369..72a1c5e404fd 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -31,7 +31,7 @@ #include "../fcp.h" #include "../packets-buffer.h" #include "../iso-resources.h" -#include "../amdtp.h" +#include "../amdtp-stream.h" #include "../cmp.h" /* basic register addresses on DM1000/DM1100/DM1500 */ diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index ecf5dc862235..29578c19e977 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -34,7 +34,7 @@ #include #include -#include "../amdtp.h" +#include "../amdtp-stream.h" #include "../iso-resources.h" #include "../lib.h" #include "dice-interface.h" diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c index 0619597e3a3f..cce19768f43d 100644 --- a/sound/firewire/fcp.c +++ b/sound/firewire/fcp.c @@ -17,7 +17,7 @@ #include #include "fcp.h" #include "lib.h" -#include "amdtp.h" +#include "amdtp-stream.h" #define CTS_AVC 0x00 diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h index 084d414b228c..d54f17134495 100644 --- a/sound/firewire/fireworks/fireworks.h +++ b/sound/firewire/fireworks/fireworks.h @@ -29,7 +29,7 @@ #include "../packets-buffer.h" #include "../iso-resources.h" -#include "../amdtp.h" +#include "../amdtp-stream.h" #include "../cmp.h" #include "../lib.h" diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index cace5ad4fe76..2c3d20b61cbe 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -28,7 +28,7 @@ #include "../fcp.h" #include "../packets-buffer.h" #include "../iso-resources.h" -#include "../amdtp.h" +#include "../amdtp-stream.h" #include "../cmp.h" struct device_info { -- cgit v1.2.3-55-g7522 From 5955815e71ff9c773b156680c781c87728e37bea Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:55 +0900 Subject: ALSA: firewire-lib: add data block processing layer for AM824 format This commit adds data block processing layer for AM824 format. The new layer initializes streaming layer with its value for fmt field. Currently, most implementation of data block processing still remains streaming layer. In later commits, these codes will be moved to the layer. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/Makefile | 2 +- sound/firewire/amdtp-am824.c | 26 ++++++++++++++++++++++++++ sound/firewire/amdtp-am824.h | 8 ++++++++ sound/firewire/amdtp-stream.c | 7 ++++--- sound/firewire/amdtp-stream.h | 3 ++- sound/firewire/bebob/bebob.h | 2 +- sound/firewire/bebob/bebob_stream.c | 8 ++++---- sound/firewire/dice/dice-stream.c | 2 +- sound/firewire/dice/dice.h | 2 +- sound/firewire/fireworks/fireworks.h | 2 +- sound/firewire/fireworks/fireworks_stream.c | 2 +- sound/firewire/oxfw/oxfw-stream.c | 2 +- sound/firewire/oxfw/oxfw.h | 2 +- 13 files changed, 52 insertions(+), 16 deletions(-) create mode 100644 sound/firewire/amdtp-am824.c create mode 100644 sound/firewire/amdtp-am824.h (limited to 'sound/firewire') diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index 102e342ddc38..6a8a71371efd 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -1,5 +1,5 @@ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ - fcp.o cmp.o amdtp-stream.o + fcp.o cmp.o amdtp-stream.o amdtp-am824.o snd-oxfw-objs := oxfw.o snd-isight-objs := isight.o snd-scs1x-objs := scs1x.o diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c new file mode 100644 index 000000000000..da4b643eff40 --- /dev/null +++ b/sound/firewire/amdtp-am824.c @@ -0,0 +1,26 @@ +/* + * AM824 format in Audio and Music Data Transmission Protocol (IEC 61883-6) + * + * Copyright (c) 2015 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "amdtp-am824.h" + +#define CIP_FMT_AM 0x10 + +/** + * amdtp_am824_init - initialize an AMDTP stream structure to handle AM824 + * data block + * @s: the AMDTP stream to initialize + * @unit: the target of the stream + * @dir: the direction of stream + * @flags: the packet transmission method to use + */ +int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit, + enum amdtp_stream_direction dir, enum cip_flags flags) +{ + return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM); +} +EXPORT_SYMBOL_GPL(amdtp_am824_init); diff --git a/sound/firewire/amdtp-am824.h b/sound/firewire/amdtp-am824.h new file mode 100644 index 000000000000..ed96ac56628f --- /dev/null +++ b/sound/firewire/amdtp-am824.h @@ -0,0 +1,8 @@ +#ifndef SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED +#define SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED + +#include "amdtp-stream.h" + +int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit, + enum amdtp_stream_direction dir, enum cip_flags flags); +#endif diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index ec65ebf8f04a..2254eec4521b 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -78,9 +77,11 @@ static void pcm_period_tasklet(unsigned long data); * @unit: the target of the stream * @dir: the direction of stream * @flags: the packet transmission method to use + * @fmt: the value of fmt field in CIP header */ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, - enum amdtp_stream_direction dir, enum cip_flags flags) + enum amdtp_stream_direction dir, enum cip_flags flags, + unsigned int fmt) { s->unit = unit; s->direction = dir; @@ -94,7 +95,7 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, s->callbacked = false; s->sync_slave = NULL; - s->fmt = CIP_FMT_AM; + s->fmt = fmt; return 0; } diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 883bb1a7e25f..5f96affe37b7 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "packets-buffer.h" @@ -174,7 +175,7 @@ struct amdtp_stream { int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, - enum cip_flags flags); + enum cip_flags flags, unsigned int fmt); void amdtp_stream_destroy(struct amdtp_stream *s); int amdtp_stream_set_parameters(struct amdtp_stream *s, diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index 72a1c5e404fd..d3c9d8de289b 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -31,7 +31,7 @@ #include "../fcp.h" #include "../packets-buffer.h" #include "../iso-resources.h" -#include "../amdtp-stream.h" +#include "../amdtp-am824.h" #include "../cmp.h" /* basic register addresses on DM1000/DM1100/DM1500 */ diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 920a3b8844ee..34bc3a419993 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -537,8 +537,8 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob) if (err < 0) goto end; - err = amdtp_stream_init(&bebob->tx_stream, bebob->unit, - AMDTP_IN_STREAM, CIP_BLOCKING); + err = amdtp_am824_init(&bebob->tx_stream, bebob->unit, + AMDTP_IN_STREAM, CIP_BLOCKING); if (err < 0) { amdtp_stream_destroy(&bebob->tx_stream); destroy_both_connections(bebob); @@ -566,8 +566,8 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob) if (bebob->maudio_special_quirk) bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC; - err = amdtp_stream_init(&bebob->rx_stream, bebob->unit, - AMDTP_OUT_STREAM, CIP_BLOCKING); + err = amdtp_am824_init(&bebob->rx_stream, bebob->unit, + AMDTP_OUT_STREAM, CIP_BLOCKING); if (err < 0) { amdtp_stream_destroy(&bebob->tx_stream); amdtp_stream_destroy(&bebob->rx_stream); diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index e4c6c20d70a0..917860e88b66 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -305,7 +305,7 @@ static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream) goto end; resources->channels_mask = 0x00000000ffffffffuLL; - err = amdtp_stream_init(stream, dice->unit, dir, CIP_BLOCKING); + err = amdtp_am824_init(stream, dice->unit, dir, CIP_BLOCKING); if (err < 0) { amdtp_stream_destroy(stream); fw_iso_resources_destroy(resources); diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index 29578c19e977..101550ac1a24 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -34,7 +34,7 @@ #include #include -#include "../amdtp-stream.h" +#include "../amdtp-am824.h" #include "../iso-resources.h" #include "../lib.h" #include "dice-interface.h" diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h index d54f17134495..c7cb7deafe48 100644 --- a/sound/firewire/fireworks/fireworks.h +++ b/sound/firewire/fireworks/fireworks.h @@ -29,7 +29,7 @@ #include "../packets-buffer.h" #include "../iso-resources.h" -#include "../amdtp-stream.h" +#include "../amdtp-am824.h" #include "../cmp.h" #include "../lib.h" diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index 85a72e63913d..8cac5b987cc1 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -31,7 +31,7 @@ init_stream(struct snd_efw *efw, struct amdtp_stream *stream) if (err < 0) goto end; - err = amdtp_stream_init(stream, efw->unit, s_dir, CIP_BLOCKING); + err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING); if (err < 0) { amdtp_stream_destroy(stream); cmp_connection_destroy(conn); diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 318f78e1a313..83683414793f 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -228,7 +228,7 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw, if (err < 0) goto end; - err = amdtp_stream_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING); + err = amdtp_am824_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING); if (err < 0) { amdtp_stream_destroy(stream); cmp_connection_destroy(conn); diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index 2c3d20b61cbe..2441459d2e58 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -28,7 +28,7 @@ #include "../fcp.h" #include "../packets-buffer.h" #include "../iso-resources.h" -#include "../amdtp-stream.h" +#include "../amdtp-am824.h" #include "../cmp.h" struct device_info { -- cgit v1.2.3-55-g7522 From 51c29fd21389d9995a8a18a91eeb8dd1220a2119 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:56 +0900 Subject: ALSA: firewire-lib: rename parameter setting function for AM824 with FDF field The value of FDF field in CIP header is protocol-dependent. Thus, it's better to allow data block processing layer to decide the value in any timing. In AM824 data format, the value of FDF field in CIP header indicates N-flag and Nominal Sampling Frequency Code (sfc). The N-flag is for switching 'Clock-based rate control mode' and 'Command-based rate control mode'. In our implementation, 'Clock-based rate control mode' is just supported. Therefore, When sampling transfer frequency is decided, then the FDF can be set. This commit replaces 'amdtp_stream_set_parameters' with 'amdtp_am824_set_parameters' to set the FDF. This is the same timing to decide the ration between the number of data blocks and the number of PCM frames. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp-am824.c | 42 +++++++++++++++++++++++++++++ sound/firewire/amdtp-am824.h | 5 ++++ sound/firewire/amdtp-stream.c | 21 ++------------- sound/firewire/amdtp-stream.h | 3 +-- sound/firewire/bebob/bebob_stream.c | 12 ++++----- sound/firewire/dice/dice-stream.c | 4 +-- sound/firewire/fireworks/fireworks_stream.c | 4 +-- sound/firewire/oxfw/oxfw-stream.c | 4 +-- 8 files changed, 62 insertions(+), 33 deletions(-) (limited to 'sound/firewire') diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c index da4b643eff40..f5edcbfa3157 100644 --- a/sound/firewire/amdtp-am824.c +++ b/sound/firewire/amdtp-am824.c @@ -10,6 +10,48 @@ #define CIP_FMT_AM 0x10 +/* "Clock-based rate control mode" is just supported. */ +#define AMDTP_FDF_AM824 0x00 + +/** + * amdtp_am824_set_parameters - set stream parameters + * @s: the AMDTP stream to configure + * @rate: the sample rate + * @pcm_channels: the number of PCM samples in each data block, to be encoded + * as AM824 multi-bit linear audio + * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) + * @double_pcm_frames: one data block transfers two PCM frames + * + * The parameters must be set before the stream is started, and must not be + * changed while the stream is running. + */ +int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports, + bool double_pcm_frames) +{ + int err; + + err = amdtp_stream_set_parameters(s, rate, pcm_channels, midi_ports); + if (err < 0) + return err; + + s->fdf = AMDTP_FDF_AM824 | s->sfc; + + /* + * In IEC 61883-6, one data block represents one event. In ALSA, one + * event equals to one PCM frame. But Dice has a quirk at higher + * sampling rate to transfer two PCM frames in one data block. + */ + if (double_pcm_frames) + s->frame_multiplier = 2; + else + s->frame_multiplier = 1; + + return 0; +} +EXPORT_SYMBOL_GPL(amdtp_am824_set_parameters); + /** * amdtp_am824_init - initialize an AMDTP stream structure to handle AM824 * data block diff --git a/sound/firewire/amdtp-am824.h b/sound/firewire/amdtp-am824.h index ed96ac56628f..cfc0d61e0db4 100644 --- a/sound/firewire/amdtp-am824.h +++ b/sound/firewire/amdtp-am824.h @@ -3,6 +3,11 @@ #include "amdtp-stream.h" +int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports, + bool double_pcm_frames); + int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, enum cip_flags flags); #endif diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 2254eec4521b..53d84e9bf241 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -54,12 +54,8 @@ #define CIP_SYT_MASK 0x0000ffff #define CIP_SYT_NO_INFO 0xffff -/* - * Audio and Music transfer protocol specific parameters - * only "Clock-based rate control mode" is supported - */ +/* Audio and Music transfer protocol specific parameters */ #define CIP_FMT_AM 0x10 -#define AMDTP_FDF_AM824 0x00 #define AMDTP_FDF_NO_DATA 0xff /* TODO: make these configurable */ @@ -204,8 +200,7 @@ EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints); int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, unsigned int pcm_channels, - unsigned int midi_ports, - bool double_pcm_frames) + unsigned int midi_ports) { unsigned int i, sfc, midi_channels; @@ -228,18 +223,6 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, s->data_block_quadlets = s->pcm_channels + midi_channels; s->midi_ports = midi_ports; - s->fdf = AMDTP_FDF_AM824 | s->sfc; - - /* - * In IEC 61883-6, one data block represents one event. In ALSA, one - * event equals to one PCM frame. But Dice has a quirk at higher - * sampling rate to transfer two PCM frames in one data block. - */ - if (double_pcm_frames) - s->frame_multiplier = 2; - else - s->frame_multiplier = 1; - s->syt_interval = amdtp_syt_intervals[sfc]; /* default buffering in the device */ diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 5f96affe37b7..d915940a14c9 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -181,8 +181,7 @@ void amdtp_stream_destroy(struct amdtp_stream *s); int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, unsigned int pcm_channels, - unsigned int midi_ports, - bool double_pcm_frames); + unsigned int midi_ports); unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s); int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed); diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 34bc3a419993..fa510f30cf8f 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -427,17 +427,17 @@ make_both_connections(struct snd_bebob *bebob, unsigned int rate) index = get_formation_index(rate); pcm_channels = bebob->tx_stream_formations[index].pcm; midi_channels = bebob->tx_stream_formations[index].midi; - err = amdtp_stream_set_parameters(&bebob->tx_stream, rate, - pcm_channels, midi_channels * 8, - false); + err = amdtp_am824_set_parameters(&bebob->tx_stream, rate, + pcm_channels, midi_channels * 8, + false); if (err < 0) goto end; pcm_channels = bebob->rx_stream_formations[index].pcm; midi_channels = bebob->rx_stream_formations[index].midi; - err = amdtp_stream_set_parameters(&bebob->rx_stream, rate, - pcm_channels, midi_channels * 8, - false); + err = amdtp_am824_set_parameters(&bebob->rx_stream, rate, + pcm_channels, midi_channels * 8, + false); if (err < 0) goto end; diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 917860e88b66..89483818785e 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -132,8 +132,8 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, pcm_chs *= 2; } - err = amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports, - false); + err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports, + double_pcm_frames); if (err < 0) goto end; diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index 8cac5b987cc1..759f6e3ed44a 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -73,8 +73,8 @@ start_stream(struct snd_efw *efw, struct amdtp_stream *stream, midi_ports = efw->midi_in_ports; } - err = amdtp_stream_set_parameters(stream, sampling_rate, - pcm_channels, midi_ports, false); + err = amdtp_am824_set_parameters(stream, sampling_rate, + pcm_channels, midi_ports, false); if (err < 0) goto end; diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 83683414793f..4982befc9c25 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -155,8 +155,8 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream, err = -EINVAL; goto end; } - err = amdtp_stream_set_parameters(stream, rate, - pcm_channels, midi_ports, false); + err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports, + false); if (err < 0) goto end; -- cgit v1.2.3-55-g7522 From bc8500da3e62cd6a896407a1b330a2eb21817c22 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:57 +0900 Subject: ALSA: firewire-lib: move PCM substream constraint to AM824 layer In IEC 61883-6, PCM frames are transferred in Multi Bit Linear Audio data channel. The data channel transfers 16/20/24 bit PCM samples. Thus, PCM substream has a constrain about it. This commit moves codes related to the constraint from packet streaming layer to AM824 data block processing layer. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp-am824.c | 20 ++++++++++++++++++++ sound/firewire/amdtp-am824.h | 5 +++++ sound/firewire/amdtp-stream.c | 5 ----- sound/firewire/bebob/bebob_pcm.c | 2 +- sound/firewire/dice/dice-pcm.c | 2 +- sound/firewire/fireworks/fireworks_pcm.c | 2 +- sound/firewire/oxfw/oxfw-pcm.c | 2 +- 7 files changed, 29 insertions(+), 9 deletions(-) (limited to 'sound/firewire') diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c index f5edcbfa3157..7adaba356579 100644 --- a/sound/firewire/amdtp-am824.c +++ b/sound/firewire/amdtp-am824.c @@ -52,6 +52,26 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, } EXPORT_SYMBOL_GPL(amdtp_am824_set_parameters); +/** + * amdtp_am824_add_pcm_hw_constraints - add hw constraints for PCM substream + * @s: the AMDTP stream for AM824 data block, must be initialized. + * @runtime: the PCM substream runtime + * + */ +int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s, + struct snd_pcm_runtime *runtime) +{ + int err; + + err = amdtp_stream_add_pcm_hw_constraints(s, runtime); + if (err < 0) + return err; + + /* AM824 in IEC 61883-6 can deliver 24bit data. */ + return snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); +} +EXPORT_SYMBOL_GPL(amdtp_am824_add_pcm_hw_constraints); + /** * amdtp_am824_init - initialize an AMDTP stream structure to handle AM824 * data block diff --git a/sound/firewire/amdtp-am824.h b/sound/firewire/amdtp-am824.h index cfc0d61e0db4..4aaed9048d82 100644 --- a/sound/firewire/amdtp-am824.h +++ b/sound/firewire/amdtp-am824.h @@ -1,6 +1,8 @@ #ifndef SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED #define SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED +#include + #include "amdtp-stream.h" int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, @@ -8,6 +10,9 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, unsigned int midi_ports, bool double_pcm_frames); +int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s, + struct snd_pcm_runtime *runtime); + int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, enum cip_flags flags); #endif diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 53d84e9bf241..def4fb4b1cce 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -140,11 +140,6 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, { int err; - /* AM824 in IEC 61883-6 can deliver 24bit data */ - err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); - if (err < 0) - goto end; - /* * Currently firewire-lib processes 16 packets in one software * interrupt callback. This equals to 2msec but actually the diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index c0f018a61fdc..50610ca4b480 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -146,7 +146,7 @@ pcm_init_hw_params(struct snd_bebob *bebob, if (err < 0) goto end; - err = amdtp_stream_add_pcm_hw_constraints(s, runtime); + err = amdtp_am824_add_pcm_hw_constraints(s, runtime); end: return err; } diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 4e67b1da0fe6..f5f30079311f 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -156,7 +156,7 @@ static int init_hw_info(struct snd_dice *dice, if (err < 0) goto end; - err = amdtp_stream_add_pcm_hw_constraints(stream, runtime); + err = amdtp_am824_add_pcm_hw_constraints(stream, runtime); end: return err; } diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c index c30b2ffa8dfb..f186ab70f429 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -187,7 +187,7 @@ pcm_init_hw_params(struct snd_efw *efw, if (err < 0) goto end; - err = amdtp_stream_add_pcm_hw_constraints(s, runtime); + err = amdtp_am824_add_pcm_hw_constraints(s, runtime); end: return err; } diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c index 9c73930d0278..bddd28743a65 100644 --- a/sound/firewire/oxfw/oxfw-pcm.c +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -158,7 +158,7 @@ static int init_hw_params(struct snd_oxfw *oxfw, if (err < 0) goto end; - err = amdtp_stream_add_pcm_hw_constraints(stream, runtime); + err = amdtp_am824_add_pcm_hw_constraints(stream, runtime); end: return err; } -- cgit v1.2.3-55-g7522 From f65be911c0e13fab28919e17c9fa00c38dc4ff2f Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:58 +0900 Subject: ALSA: firewire-lib: add helper functions to set positions of data channels In IEC 61883-6, several types of data are available in AM824 format. The data is transferred in each data channel. The position of data channel in data block differs depending on model. Current implementation has an array to map the index of data channel in an data block to the position of actual data channel. The implementation allows each driver to access the mapping directly. In later commit, the mapping is in specific structure pushed into an opaque pointer. Helper functions are required. This commit adds the helper functions for this purpose. In IEC 61883-6, AM824 format supports many data types, while this specification easily causes over-engineering. Current AM824 implementation is allowed to handle two types of data, Multi Bit Linear Audio data (=PCM samples) and MIDI conformant data (=MIDI messages). Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp-am824.c | 28 ++++++++++++++++++++++++++++ sound/firewire/amdtp-am824.h | 6 ++++++ sound/firewire/bebob/bebob_stream.c | 5 +++-- sound/firewire/dice/dice-stream.c | 5 +++-- 4 files changed, 40 insertions(+), 4 deletions(-) (limited to 'sound/firewire') diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c index 7adaba356579..5d99a9921d50 100644 --- a/sound/firewire/amdtp-am824.c +++ b/sound/firewire/amdtp-am824.c @@ -52,6 +52,34 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, } EXPORT_SYMBOL_GPL(amdtp_am824_set_parameters); +/** + * amdtp_am824_set_pcm_position - set an index of data channel for a channel + * of PCM frame + * @s: the AMDTP stream + * @index: the index of data channel in an data block + * @position: the channel of PCM frame + */ +void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index, + unsigned int position) +{ + if (index < s->pcm_channels) + s->pcm_positions[index] = position; +} +EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_position); + +/** + * amdtp_am824_set_midi_position - set a index of data channel for MIDI + * conformant data channel + * @s: the AMDTP stream + * @position: the index of data channel in an data block + */ +void amdtp_am824_set_midi_position(struct amdtp_stream *s, + unsigned int position) +{ + s->midi_position = position; +} +EXPORT_SYMBOL_GPL(amdtp_am824_set_midi_position); + /** * amdtp_am824_add_pcm_hw_constraints - add hw constraints for PCM substream * @s: the AMDTP stream for AM824 data block, must be initialized. diff --git a/sound/firewire/amdtp-am824.h b/sound/firewire/amdtp-am824.h index 4aaed9048d82..7c21e9972fad 100644 --- a/sound/firewire/amdtp-am824.h +++ b/sound/firewire/amdtp-am824.h @@ -10,6 +10,12 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, unsigned int midi_ports, bool double_pcm_frames); +void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index, + unsigned int position); + +void amdtp_am824_set_midi_position(struct amdtp_stream *s, + unsigned int position); + int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index fa510f30cf8f..cf6402d371e7 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -338,7 +338,7 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s) err = -ENOSYS; goto end; } - s->midi_position = stm_pos; + amdtp_am824_set_midi_position(s, stm_pos); midi = stm_pos; break; /* for PCM data channel */ @@ -358,7 +358,8 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s) err = -ENOSYS; goto end; } - s->pcm_positions[location] = stm_pos; + amdtp_am824_set_pcm_position(s, location, + stm_pos); break; } } diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 89483818785e..2108f7f1a764 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -141,8 +141,9 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, pcm_chs /= 2; for (i = 0; i < pcm_chs; i++) { - stream->pcm_positions[i] = i * 2; - stream->pcm_positions[i + pcm_chs] = i * 2 + 1; + amdtp_am824_set_pcm_position(stream, i, i * 2); + amdtp_am824_set_pcm_position(stream, i + pcm_chs, + i * 2 + 1); } } -- cgit v1.2.3-55-g7522 From 03e2a67eed7bf2e4c701587080bc8d60dd45209c Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:21:59 +0900 Subject: ALSA: firewire-lib: move MIDI trigger helper function to AM824 layer In IEC 61883-6, MIDI messages are transferred in MIDI conformant data channel. Essentially, packet streaming layer is not responsible for MIDI functionality. This commit moves MIDI trigger helper function from the layer to AM824 layer. The rest of codes related to MIDI functionality will be moved in later commits. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp-am824.c | 18 ++++++++++++++++++ sound/firewire/amdtp-am824.h | 4 ++++ sound/firewire/amdtp-stream.h | 18 ------------------ sound/firewire/bebob/bebob_midi.c | 16 ++++++++-------- sound/firewire/dice/dice-midi.c | 12 ++++++------ sound/firewire/fireworks/fireworks_midi.c | 12 ++++++------ sound/firewire/oxfw/oxfw-midi.c | 16 ++++++++-------- 7 files changed, 50 insertions(+), 46 deletions(-) (limited to 'sound/firewire') diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c index 5d99a9921d50..540a101661e2 100644 --- a/sound/firewire/amdtp-am824.c +++ b/sound/firewire/amdtp-am824.c @@ -100,6 +100,24 @@ int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s, } EXPORT_SYMBOL_GPL(amdtp_am824_add_pcm_hw_constraints); +/** + * amdtp_am824_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. + */ +void amdtp_am824_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; +} +EXPORT_SYMBOL_GPL(amdtp_am824_midi_trigger); + /** * amdtp_am824_init - initialize an AMDTP stream structure to handle AM824 * data block diff --git a/sound/firewire/amdtp-am824.h b/sound/firewire/amdtp-am824.h index 7c21e9972fad..65e6093a4460 100644 --- a/sound/firewire/amdtp-am824.h +++ b/sound/firewire/amdtp-am824.h @@ -2,6 +2,7 @@ #define SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED #include +#include #include "amdtp-stream.h" @@ -19,6 +20,9 @@ void amdtp_am824_set_midi_position(struct amdtp_stream *s, int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); +void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port, + struct snd_rawmidi_substream *midi); + int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, enum cip_flags flags); #endif diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index d915940a14c9..984f02ac2aed 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -248,24 +248,6 @@ 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; diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c index 5681143925cd..90d95be499b0 100644 --- a/sound/firewire/bebob/bebob_midi.c +++ b/sound/firewire/bebob/bebob_midi.c @@ -72,11 +72,11 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&bebob->lock, flags); if (up) - amdtp_stream_midi_trigger(&bebob->tx_stream, - substrm->number, substrm); + amdtp_am824_midi_trigger(&bebob->tx_stream, + substrm->number, substrm); else - amdtp_stream_midi_trigger(&bebob->tx_stream, - substrm->number, NULL); + amdtp_am824_midi_trigger(&bebob->tx_stream, + substrm->number, NULL); spin_unlock_irqrestore(&bebob->lock, flags); } @@ -89,11 +89,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&bebob->lock, flags); if (up) - amdtp_stream_midi_trigger(&bebob->rx_stream, - substrm->number, substrm); + amdtp_am824_midi_trigger(&bebob->rx_stream, + substrm->number, substrm); else - amdtp_stream_midi_trigger(&bebob->rx_stream, - substrm->number, NULL); + amdtp_am824_midi_trigger(&bebob->rx_stream, + substrm->number, NULL); spin_unlock_irqrestore(&bebob->lock, flags); } diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c index fe43ce791f84..151b09f240f2 100644 --- a/sound/firewire/dice/dice-midi.c +++ b/sound/firewire/dice/dice-midi.c @@ -52,10 +52,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&dice->lock, flags); if (up) - amdtp_stream_midi_trigger(&dice->tx_stream, + amdtp_am824_midi_trigger(&dice->tx_stream, substrm->number, substrm); else - amdtp_stream_midi_trigger(&dice->tx_stream, + amdtp_am824_midi_trigger(&dice->tx_stream, substrm->number, NULL); spin_unlock_irqrestore(&dice->lock, flags); @@ -69,11 +69,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&dice->lock, flags); if (up) - amdtp_stream_midi_trigger(&dice->rx_stream, - substrm->number, substrm); + amdtp_am824_midi_trigger(&dice->rx_stream, + substrm->number, substrm); else - amdtp_stream_midi_trigger(&dice->rx_stream, - substrm->number, NULL); + amdtp_am824_midi_trigger(&dice->rx_stream, + substrm->number, NULL); spin_unlock_irqrestore(&dice->lock, flags); } diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c index cf9c65260439..fba01bbba456 100644 --- a/sound/firewire/fireworks/fireworks_midi.c +++ b/sound/firewire/fireworks/fireworks_midi.c @@ -73,10 +73,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&efw->lock, flags); if (up) - amdtp_stream_midi_trigger(&efw->tx_stream, + amdtp_am824_midi_trigger(&efw->tx_stream, substrm->number, substrm); else - amdtp_stream_midi_trigger(&efw->tx_stream, + amdtp_am824_midi_trigger(&efw->tx_stream, substrm->number, NULL); spin_unlock_irqrestore(&efw->lock, flags); @@ -90,11 +90,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&efw->lock, flags); if (up) - amdtp_stream_midi_trigger(&efw->rx_stream, - substrm->number, substrm); + amdtp_am824_midi_trigger(&efw->rx_stream, + substrm->number, substrm); else - amdtp_stream_midi_trigger(&efw->rx_stream, - substrm->number, NULL); + amdtp_am824_midi_trigger(&efw->rx_stream, + substrm->number, NULL); spin_unlock_irqrestore(&efw->lock, flags); } diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c index 540a30338516..37a86cf69cbf 100644 --- a/sound/firewire/oxfw/oxfw-midi.c +++ b/sound/firewire/oxfw/oxfw-midi.c @@ -90,11 +90,11 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&oxfw->lock, flags); if (up) - amdtp_stream_midi_trigger(&oxfw->tx_stream, - substrm->number, substrm); + amdtp_am824_midi_trigger(&oxfw->tx_stream, + substrm->number, substrm); else - amdtp_stream_midi_trigger(&oxfw->tx_stream, - substrm->number, NULL); + amdtp_am824_midi_trigger(&oxfw->tx_stream, + substrm->number, NULL); spin_unlock_irqrestore(&oxfw->lock, flags); } @@ -107,11 +107,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_lock_irqsave(&oxfw->lock, flags); if (up) - amdtp_stream_midi_trigger(&oxfw->rx_stream, - substrm->number, substrm); + amdtp_am824_midi_trigger(&oxfw->rx_stream, + substrm->number, substrm); else - amdtp_stream_midi_trigger(&oxfw->rx_stream, - substrm->number, NULL); + amdtp_am824_midi_trigger(&oxfw->rx_stream, + substrm->number, NULL); spin_unlock_irqrestore(&oxfw->lock, flags); } -- cgit v1.2.3-55-g7522 From 85130cb43e78a7bdb2ade10131563d89fbbddf9d Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:22:00 +0900 Subject: ALSA: firewire-lib: rename PCM format helper function Setting the format of PCM substream to AMDTP stream structure is important to set a handler to copy actual PCM samples between buffers. The processing should be in data block processing layer because essentially it has no relationship to packet streaming. This commit renames PCM format setting function to prepare for integrating AM824 layer. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp-stream.c | 7 +++---- sound/firewire/amdtp-stream.h | 4 ++-- sound/firewire/bebob/bebob_pcm.c | 8 ++++---- sound/firewire/dice/dice-pcm.c | 6 ++---- sound/firewire/fireworks/fireworks_pcm.c | 6 ++++-- sound/firewire/oxfw/oxfw-pcm.c | 4 ++-- 6 files changed, 17 insertions(+), 18 deletions(-) (limited to 'sound/firewire') diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index def4fb4b1cce..245002e9b0cb 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -272,7 +272,7 @@ static void read_pcm_s32(struct amdtp_stream *s, __be32 *buffer, unsigned int frames); /** - * amdtp_stream_set_pcm_format - set the PCM format + * amdtp_am824_set_pcm_format - set the PCM format * @s: the AMDTP stream to configure * @format: the format of the ALSA PCM device * @@ -280,8 +280,7 @@ static void read_pcm_s32(struct amdtp_stream *s, * MIDI) and before the stream is started, and must not be changed while the * stream is running. */ -void amdtp_stream_set_pcm_format(struct amdtp_stream *s, - snd_pcm_format_t format) +void amdtp_am824_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format) { if (WARN_ON(amdtp_stream_pcm_running(s))) return; @@ -305,7 +304,7 @@ void amdtp_stream_set_pcm_format(struct amdtp_stream *s, break; } } -EXPORT_SYMBOL(amdtp_stream_set_pcm_format); +EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_format); /** * amdtp_stream_pcm_prepare - prepare PCM device for running diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 984f02ac2aed..3fb82c753f7b 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -190,8 +190,8 @@ void amdtp_stream_stop(struct amdtp_stream *s); int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); -void amdtp_stream_set_pcm_format(struct amdtp_stream *s, - snd_pcm_format_t format); +void amdtp_am824_set_pcm_format(struct amdtp_stream *s, + snd_pcm_format_t format); void amdtp_stream_pcm_prepare(struct amdtp_stream *s); unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s); void amdtp_stream_pcm_abort(struct amdtp_stream *s); diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index 50610ca4b480..8d8de4c3bfb5 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -220,8 +220,8 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) atomic_inc(&bebob->substreams_counter); - amdtp_stream_set_pcm_format(&bebob->tx_stream, - params_format(hw_params)); + + amdtp_am824_set_pcm_format(&bebob->tx_stream, params_format(hw_params)); return 0; } @@ -239,8 +239,8 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) atomic_inc(&bebob->substreams_counter); - amdtp_stream_set_pcm_format(&bebob->rx_stream, - params_format(hw_params)); + + amdtp_am824_set_pcm_format(&bebob->rx_stream, params_format(hw_params)); return 0; } diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index f5f30079311f..8cbcd5d59b45 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -243,8 +243,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&dice->mutex); } - amdtp_stream_set_pcm_format(&dice->tx_stream, - params_format(hw_params)); + amdtp_am824_set_pcm_format(&dice->tx_stream, params_format(hw_params)); return 0; } @@ -265,8 +264,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&dice->mutex); } - amdtp_stream_set_pcm_format(&dice->rx_stream, - params_format(hw_params)); + amdtp_am824_set_pcm_format(&dice->rx_stream, params_format(hw_params)); return 0; } diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c index f186ab70f429..109fb1bc8d9d 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -253,7 +253,8 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) atomic_inc(&efw->capture_substreams); - amdtp_stream_set_pcm_format(&efw->tx_stream, params_format(hw_params)); + + amdtp_am824_set_pcm_format(&efw->tx_stream, params_format(hw_params)); return 0; } @@ -270,7 +271,8 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) atomic_inc(&efw->playback_substreams); - amdtp_stream_set_pcm_format(&efw->rx_stream, params_format(hw_params)); + + amdtp_am824_set_pcm_format(&efw->rx_stream, params_format(hw_params)); return 0; } diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c index bddd28743a65..217fe376b766 100644 --- a/sound/firewire/oxfw/oxfw-pcm.c +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -244,7 +244,7 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&oxfw->mutex); } - amdtp_stream_set_pcm_format(&oxfw->tx_stream, params_format(hw_params)); + amdtp_am824_set_pcm_format(&oxfw->tx_stream, params_format(hw_params)); return 0; } @@ -265,7 +265,7 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream, mutex_unlock(&oxfw->mutex); } - amdtp_stream_set_pcm_format(&oxfw->rx_stream, params_format(hw_params)); + amdtp_am824_set_pcm_format(&oxfw->rx_stream, params_format(hw_params)); return 0; } -- cgit v1.2.3-55-g7522 From 49c7b3fcd9f0a0125e8cd8212d5576382198eeb2 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:22:01 +0900 Subject: ALSA: firewire-lib: rename macros with AM824 prefix This commit renames some macros just related to AM824 format. In later commit, they're moved to AM824 layer. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp-stream.c | 4 ++-- sound/firewire/amdtp-stream.h | 14 +++++++------- sound/firewire/bebob/bebob_pcm.c | 4 ++-- sound/firewire/bebob/bebob_stream.c | 6 +++--- sound/firewire/dice/dice-pcm.c | 4 ++-- sound/firewire/fireworks/fireworks.c | 12 ++++++------ sound/firewire/fireworks/fireworks_pcm.c | 4 ++-- sound/firewire/oxfw/oxfw-pcm.c | 4 ++-- sound/firewire/oxfw/oxfw-stream.c | 4 ++-- 9 files changed, 28 insertions(+), 28 deletions(-) (limited to 'sound/firewire') diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 245002e9b0cb..c61e0ec5c0f5 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -202,8 +202,8 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, midi_channels = DIV_ROUND_UP(midi_ports, 8); if (WARN_ON(amdtp_stream_running(s)) || - WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) || - WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI)) + WARN_ON(pcm_channels > AM824_MAX_CHANNELS_FOR_PCM) || + WARN_ON(midi_channels > AM824_MAX_CHANNELS_FOR_MIDI)) return -EINVAL; for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) { diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 3fb82c753f7b..71f4f751fabc 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -81,9 +81,9 @@ enum cip_sfc { CIP_SFC_COUNT }; -#define AMDTP_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32 +#define AM824_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32 -#define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ +#define AM824_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ SNDRV_PCM_FMTBIT_S32) @@ -91,7 +91,7 @@ enum cip_sfc { * This module supports maximum 64 PCM channels for one PCM stream * This is for our convenience. */ -#define AMDTP_MAX_CHANNELS_FOR_PCM 64 +#define AM824_MAX_CHANNELS_FOR_PCM 64 /* * AMDTP packet can include channels for MIDI conformant data. @@ -101,7 +101,7 @@ enum cip_sfc { * 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 +#define AM824_MAX_CHANNELS_FOR_MIDI 1 struct fw_unit; struct fw_iso_context; @@ -157,13 +157,13 @@ struct amdtp_stream { struct amdtp_stream *sync_slave; /* For AM824 processing. */ - struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; + struct snd_rawmidi_substream *midi[AM824_MAX_CHANNELS_FOR_MIDI * 8]; int midi_fifo_limit; - int midi_fifo_used[AMDTP_MAX_CHANNELS_FOR_MIDI * 8]; + int midi_fifo_used[AM824_MAX_CHANNELS_FOR_MIDI * 8]; unsigned int pcm_channels; unsigned int midi_ports; - u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM]; + u8 pcm_positions[AM824_MAX_CHANNELS_FOR_PCM]; u8 midi_position; void (*transfer_samples)(struct amdtp_stream *s, diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index 8d8de4c3bfb5..2fdc1f10a2c7 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -122,11 +122,11 @@ pcm_init_hw_params(struct snd_bebob *bebob, SNDRV_PCM_INFO_MMAP_VALID; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS; + runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS; s = &bebob->tx_stream; formations = bebob->tx_stream_formations; } else { - runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS; + runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS; s = &bebob->rx_stream; formations = bebob->rx_stream_formations; } diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index cf6402d371e7..a2baa478d4ba 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -354,7 +354,7 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s) case 0x09: /* Digital */ default: location = pcm + sec_loc; - if (location >= AMDTP_MAX_CHANNELS_FOR_PCM) { + if (location >= AM824_MAX_CHANNELS_FOR_PCM) { err = -ENOSYS; goto end; } @@ -872,8 +872,8 @@ parse_stream_formation(u8 *buf, unsigned int len, } } - if (formation[i].pcm > AMDTP_MAX_CHANNELS_FOR_PCM || - formation[i].midi > AMDTP_MAX_CHANNELS_FOR_MIDI) + if (formation[i].pcm > AM824_MAX_CHANNELS_FOR_PCM || + formation[i].midi > AM824_MAX_CHANNELS_FOR_MIDI) return -ENOSYS; return 0; diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 8cbcd5d59b45..9b3431999fc8 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -133,11 +133,11 @@ static int init_hw_info(struct snd_dice *dice, SNDRV_PCM_INFO_BLOCK_TRANSFER; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - hw->formats = AMDTP_IN_PCM_FORMAT_BITS; + hw->formats = AM824_IN_PCM_FORMAT_BITS; stream = &dice->tx_stream; pcm_channels = dice->tx_channels; } else { - hw->formats = AMDTP_OUT_PCM_FORMAT_BITS; + hw->formats = AM824_OUT_PCM_FORMAT_BITS; stream = &dice->rx_stream; pcm_channels = dice->rx_channels; } diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c index c94a432f7cc6..d5b19bc11e59 100644 --- a/sound/firewire/fireworks/fireworks.c +++ b/sound/firewire/fireworks/fireworks.c @@ -138,12 +138,12 @@ get_hardware_info(struct snd_efw *efw) efw->midi_out_ports = hwinfo->midi_out_ports; efw->midi_in_ports = hwinfo->midi_in_ports; - if (hwinfo->amdtp_tx_pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM || - hwinfo->amdtp_tx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM || - hwinfo->amdtp_tx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM || - hwinfo->amdtp_rx_pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM || - hwinfo->amdtp_rx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM || - hwinfo->amdtp_rx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM) { + if (hwinfo->amdtp_tx_pcm_channels > AM824_MAX_CHANNELS_FOR_PCM || + hwinfo->amdtp_tx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM || + hwinfo->amdtp_tx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM || + hwinfo->amdtp_rx_pcm_channels > AM824_MAX_CHANNELS_FOR_PCM || + hwinfo->amdtp_rx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM || + hwinfo->amdtp_rx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM) { err = -ENOSYS; goto end; } diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c index 109fb1bc8d9d..d27135bac513 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -159,11 +159,11 @@ pcm_init_hw_params(struct snd_efw *efw, SNDRV_PCM_INFO_MMAP_VALID; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS; + runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS; s = &efw->tx_stream; pcm_channels = efw->pcm_capture_channels; } else { - runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS; + runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS; s = &efw->rx_stream; pcm_channels = efw->pcm_playback_channels; } diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c index 217fe376b766..8d233417695d 100644 --- a/sound/firewire/oxfw/oxfw-pcm.c +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -134,11 +134,11 @@ static int init_hw_params(struct snd_oxfw *oxfw, SNDRV_PCM_INFO_MMAP_VALID; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS; + runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS; stream = &oxfw->tx_stream; formats = oxfw->tx_stream_formats; } else { - runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS; + runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS; stream = &oxfw->rx_stream; formats = oxfw->rx_stream_formats; } diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 4982befc9c25..0c40c00daf12 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -483,8 +483,8 @@ int snd_oxfw_stream_parse_format(u8 *format, } } - if (formation->pcm > AMDTP_MAX_CHANNELS_FOR_PCM || - formation->midi > AMDTP_MAX_CHANNELS_FOR_MIDI) + if (formation->pcm > AM824_MAX_CHANNELS_FOR_PCM || + formation->midi > AM824_MAX_CHANNELS_FOR_MIDI) return -ENOSYS; return 0; -- cgit v1.2.3-55-g7522 From df075feefbd347f13fba5198294cda619532c237 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sat, 19 Sep 2015 11:22:02 +0900 Subject: ALSA: firewire-lib: complete AM824 data block processing layer This commit moves the codes related to data block processing from packet streaming layer to AM824 layer. Each driver initializes amdtp stream structure for AM824 data block by calling amdtp_am824_init(). Then, a memory block is allocated for AM824 specific structure. This memory block is released by calling amdtp_stream_destroy(). When setting streaming parameters, it calls amdtp_am824_set_parameters(). When starting packet streaming, it calls amdtp_stream_start(). When stopping packet streaming, it calls amdtp_stream_stop(). Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/amdtp-am824.c | 349 ++++++++++++++++++++++++++++++++++++++++-- sound/firewire/amdtp-am824.h | 24 +++ sound/firewire/amdtp-stream.c | 316 +++----------------------------------- sound/firewire/amdtp-stream.h | 62 ++------ 4 files changed, 401 insertions(+), 350 deletions(-) (limited to 'sound/firewire') diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c index 540a101661e2..fe4b83f65831 100644 --- a/sound/firewire/amdtp-am824.c +++ b/sound/firewire/amdtp-am824.c @@ -1,11 +1,14 @@ /* * AM824 format in Audio and Music Data Transmission Protocol (IEC 61883-6) * + * Copyright (c) Clemens Ladisch * Copyright (c) 2015 Takashi Sakamoto * * Licensed under the terms of the GNU General Public License, version 2. */ +#include + #include "amdtp-am824.h" #define CIP_FMT_AM 0x10 @@ -13,6 +16,35 @@ /* "Clock-based rate control mode" is just supported. */ #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 + +struct amdtp_am824 { + struct snd_rawmidi_substream *midi[AM824_MAX_CHANNELS_FOR_MIDI * 8]; + int midi_fifo_limit; + int midi_fifo_used[AM824_MAX_CHANNELS_FOR_MIDI * 8]; + unsigned int pcm_channels; + unsigned int midi_ports; + + u8 pcm_positions[AM824_MAX_CHANNELS_FOR_PCM]; + u8 midi_position; + + void (*transfer_samples)(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); + + unsigned int frame_multiplier; +}; + /** * amdtp_am824_set_parameters - set stream parameters * @s: the AMDTP stream to configure @@ -30,23 +62,58 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, unsigned int midi_ports, bool double_pcm_frames) { + struct amdtp_am824 *p = s->protocol; + unsigned int midi_channels; + unsigned int i; int err; - err = amdtp_stream_set_parameters(s, rate, pcm_channels, midi_ports); + if (amdtp_stream_running(s)) + return -EINVAL; + + if (pcm_channels > AM824_MAX_CHANNELS_FOR_PCM) + return -EINVAL; + + midi_channels = DIV_ROUND_UP(midi_ports, 8); + if (midi_channels > AM824_MAX_CHANNELS_FOR_MIDI) + return -EINVAL; + + if (WARN_ON(amdtp_stream_running(s)) || + WARN_ON(pcm_channels > AM824_MAX_CHANNELS_FOR_PCM) || + WARN_ON(midi_channels > AM824_MAX_CHANNELS_FOR_MIDI)) + return -EINVAL; + + err = amdtp_stream_set_parameters(s, rate, + pcm_channels + midi_channels); if (err < 0) return err; s->fdf = AMDTP_FDF_AM824 | s->sfc; + p->pcm_channels = pcm_channels; + p->midi_ports = midi_ports; + /* * In IEC 61883-6, one data block represents one event. In ALSA, one * event equals to one PCM frame. But Dice has a quirk at higher * sampling rate to transfer two PCM frames in one data block. */ if (double_pcm_frames) - s->frame_multiplier = 2; + p->frame_multiplier = 2; else - s->frame_multiplier = 1; + p->frame_multiplier = 1; + + /* init the position map for PCM and MIDI channels */ + for (i = 0; i < pcm_channels; i++) + p->pcm_positions[i] = i; + p->midi_position = p->pcm_channels; + + /* + * 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; } @@ -62,8 +129,10 @@ EXPORT_SYMBOL_GPL(amdtp_am824_set_parameters); void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index, unsigned int position) { - if (index < s->pcm_channels) - s->pcm_positions[index] = position; + struct amdtp_am824 *p = s->protocol; + + if (index < p->pcm_channels) + p->pcm_positions[index] = position; } EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_position); @@ -76,10 +145,139 @@ EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_position); void amdtp_am824_set_midi_position(struct amdtp_stream *s, unsigned int position) { - s->midi_position = position; + struct amdtp_am824 *p = s->protocol; + + p->midi_position = position; } EXPORT_SYMBOL_GPL(amdtp_am824_set_midi_position); +static void write_pcm_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct amdtp_am824 *p = s->protocol; + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + const u32 *src; + + channels = p->pcm_channels; + src = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + buffer[p->pcm_positions[c]] = + cpu_to_be32((*src >> 8) | 0x40000000); + src++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; + } +} + +static void write_pcm_s16(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct amdtp_am824 *p = s->protocol; + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + const u16 *src; + + channels = p->pcm_channels; + src = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + buffer[p->pcm_positions[c]] = + cpu_to_be32((*src << 8) | 0x42000000); + src++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + src = (void *)runtime->dma_area; + } +} + +static void read_pcm_s32(struct amdtp_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct amdtp_am824 *p = s->protocol; + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, remaining_frames, i, c; + u32 *dst; + + channels = p->pcm_channels; + dst = (void *)runtime->dma_area + + frames_to_bytes(runtime, s->pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + *dst = be32_to_cpu(buffer[p->pcm_positions[c]]) << 8; + dst++; + } + buffer += s->data_block_quadlets; + if (--remaining_frames == 0) + dst = (void *)runtime->dma_area; + } +} + +static void write_pcm_silence(struct amdtp_stream *s, + __be32 *buffer, unsigned int frames) +{ + struct amdtp_am824 *p = s->protocol; + unsigned int i, c, channels = p->pcm_channels; + + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) + buffer[p->pcm_positions[c]] = cpu_to_be32(0x40000000); + buffer += s->data_block_quadlets; + } +} + +/** + * amdtp_am824_set_pcm_format - set the PCM format + * @s: the AMDTP stream to configure + * @format: the format of the ALSA PCM device + * + * The sample format must be set after the other parameters (rate/PCM channels/ + * MIDI) and before the stream is started, and must not be changed while the + * stream is running. + */ +void amdtp_am824_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format) +{ + struct amdtp_am824 *p = s->protocol; + + if (WARN_ON(amdtp_stream_pcm_running(s))) + return; + + switch (format) { + default: + WARN_ON(1); + /* fall through */ + case SNDRV_PCM_FORMAT_S16: + if (s->direction == AMDTP_OUT_STREAM) { + p->transfer_samples = write_pcm_s16; + break; + } + WARN_ON(1); + /* fall through */ + case SNDRV_PCM_FORMAT_S32: + if (s->direction == AMDTP_OUT_STREAM) + p->transfer_samples = write_pcm_s32; + else + p->transfer_samples = read_pcm_s32; + break; + } +} +EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_format); + /** * amdtp_am824_add_pcm_hw_constraints - add hw constraints for PCM substream * @s: the AMDTP stream for AM824 data block, must be initialized. @@ -113,11 +311,135 @@ EXPORT_SYMBOL_GPL(amdtp_am824_add_pcm_hw_constraints); void amdtp_am824_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; + struct amdtp_am824 *p = s->protocol; + + if (port < p->midi_ports) + ACCESS_ONCE(p->midi[port]) = midi; } EXPORT_SYMBOL_GPL(amdtp_am824_midi_trigger); +/* + * To avoid sending MIDI bytes at too high a rate, assume that the receiving + * device has a FIFO, and track how much it is filled. This values increases + * by one whenever we send one byte in a packet, but the FIFO empties at + * a constant rate independent of our packet rate. One packet has syt_interval + * samples, so the number of bytes that empty out of the FIFO, per packet(!), + * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate. To avoid storing + * fractional values, the values in midi_fifo_used[] are measured in bytes + * multiplied by the sample rate. + */ +static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port) +{ + struct amdtp_am824 *p = s->protocol; + int used; + + used = p->midi_fifo_used[port]; + if (used == 0) /* common shortcut */ + 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 void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port) +{ + struct amdtp_am824 *p = s->protocol; + + p->midi_fifo_used[port] += amdtp_rate_table[s->sfc]; +} + +static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer, + unsigned int frames) +{ + struct amdtp_am824 *p = s->protocol; + unsigned int f, port; + u8 *b; + + for (f = 0; f < frames; f++) { + b = (u8 *)&buffer[p->midi_position]; + + port = (s->data_block_counter + f) % 8; + if (f < MAX_MIDI_RX_BLOCKS && + midi_ratelimit_per_packet(s, port) && + p->midi[port] != NULL && + snd_rawmidi_transmit(p->midi[port], &b[1], 1) == 1) { + midi_rate_use_one_byte(s, port); + b[0] = 0x81; + } else { + b[0] = 0x80; + b[1] = 0; + } + b[2] = 0; + b[3] = 0; + + buffer += s->data_block_quadlets; + } +} + +static void read_midi_messages(struct amdtp_stream *s, + __be32 *buffer, unsigned int frames) +{ + struct amdtp_am824 *p = s->protocol; + unsigned int f, port; + int len; + u8 *b; + + for (f = 0; f < frames; f++) { + port = (s->data_block_counter + f) % 8; + b = (u8 *)&buffer[p->midi_position]; + + len = b[0] - 0x80; + if ((1 <= len) && (len <= 3) && (p->midi[port])) + snd_rawmidi_receive(p->midi[port], b + 1, len); + + buffer += s->data_block_quadlets; + } +} + +unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks, unsigned int *syt) +{ + struct amdtp_am824 *p = s->protocol; + struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); + unsigned int pcm_frames; + + if (pcm) { + p->transfer_samples(s, pcm, buffer, data_blocks); + pcm_frames = data_blocks * p->frame_multiplier; + } else { + write_pcm_silence(s, buffer, data_blocks); + pcm_frames = 0; + } + + if (p->midi_ports) + write_midi_messages(s, buffer, data_blocks); + + return pcm_frames; +} + +unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, + unsigned int data_blocks, unsigned int *syt) +{ + struct amdtp_am824 *p = s->protocol; + struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); + unsigned int pcm_frames; + + if (pcm) { + p->transfer_samples(s, pcm, buffer, data_blocks); + pcm_frames = data_blocks * p->frame_multiplier; + } else { + pcm_frames = 0; + } + + if (p->midi_ports) + read_midi_messages(s, buffer, data_blocks); + + return pcm_frames; +} + /** * amdtp_am824_init - initialize an AMDTP stream structure to handle AM824 * data block @@ -129,6 +451,15 @@ EXPORT_SYMBOL_GPL(amdtp_am824_midi_trigger); int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, enum cip_flags flags) { - return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM); + amdtp_stream_process_data_blocks_t process_data_blocks; + + if (dir == AMDTP_IN_STREAM) + process_data_blocks = process_tx_data_blocks; + else + process_data_blocks = process_rx_data_blocks; + + return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM, + process_data_blocks, + sizeof(struct amdtp_am824)); } EXPORT_SYMBOL_GPL(amdtp_am824_init); diff --git a/sound/firewire/amdtp-am824.h b/sound/firewire/amdtp-am824.h index 65e6093a4460..73b07b3109db 100644 --- a/sound/firewire/amdtp-am824.h +++ b/sound/firewire/amdtp-am824.h @@ -6,6 +6,27 @@ #include "amdtp-stream.h" +#define AM824_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32 + +#define AM824_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ + SNDRV_PCM_FMTBIT_S32) + +/* + * This module supports maximum 64 PCM channels for one PCM stream + * This is for our convenience. + */ +#define AM824_MAX_CHANNELS_FOR_PCM 64 + +/* + * 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 AM824_MAX_CHANNELS_FOR_MIDI 1 + int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, unsigned int pcm_channels, unsigned int midi_ports, @@ -20,6 +41,9 @@ void amdtp_am824_set_midi_position(struct amdtp_stream *s, int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); +void amdtp_am824_set_pcm_format(struct amdtp_stream *s, + snd_pcm_format_t format); + void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port, struct snd_rawmidi_substream *midi); diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index c61e0ec5c0f5..fa10b58a0957 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -13,25 +13,12 @@ #include #include #include -#include #include "amdtp-stream.h" #define TICKS_PER_CYCLE 3072 #define CYCLES_PER_SECOND 8000 #define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND) -/* - * 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 - #define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */ /* isochronous header parameters */ @@ -74,11 +61,22 @@ static void pcm_period_tasklet(unsigned long data); * @dir: the direction of stream * @flags: the packet transmission method to use * @fmt: the value of fmt field in CIP header + * @process_data_blocks: callback handler to process data blocks + * @protocol_size: the size to allocate newly for protocol */ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, enum cip_flags flags, - unsigned int fmt) + unsigned int fmt, + amdtp_stream_process_data_blocks_t process_data_blocks, + unsigned int protocol_size) { + if (process_data_blocks == NULL) + return -EINVAL; + + s->protocol = kzalloc(protocol_size, GFP_KERNEL); + if (!s->protocol) + return -ENOMEM; + s->unit = unit; s->direction = dir; s->flags = flags; @@ -92,6 +90,7 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, s->sync_slave = NULL; s->fmt = fmt; + s->process_data_blocks = process_data_blocks; return 0; } @@ -104,6 +103,7 @@ EXPORT_SYMBOL(amdtp_stream_init); void amdtp_stream_destroy(struct amdtp_stream *s) { WARN_ON(amdtp_stream_running(s)); + kfree(s->protocol); mutex_destroy(&s->mutex); } EXPORT_SYMBOL(amdtp_stream_destroy); @@ -184,27 +184,15 @@ EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints); * amdtp_stream_set_parameters - set stream parameters * @s: the AMDTP stream to configure * @rate: the sample rate - * @pcm_channels: the number of PCM samples in each data block, to be encoded - * as AM824 multi-bit linear audio - * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) - * @double_pcm_frames: one data block transfers two PCM frames + * @data_block_quadlets: the size of a data block in quadlet unit * * The parameters must be set before the stream is started, and must not be * changed while the stream is running. */ -int amdtp_stream_set_parameters(struct amdtp_stream *s, - unsigned int rate, - unsigned int pcm_channels, - unsigned int midi_ports) +int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, + unsigned int data_block_quadlets) { - unsigned int i, sfc, midi_channels; - - midi_channels = DIV_ROUND_UP(midi_ports, 8); - - if (WARN_ON(amdtp_stream_running(s)) || - WARN_ON(pcm_channels > AM824_MAX_CHANNELS_FOR_PCM) || - WARN_ON(midi_channels > AM824_MAX_CHANNELS_FOR_MIDI)) - return -EINVAL; + unsigned int sfc; for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) { if (amdtp_rate_table[sfc] == rate) @@ -213,11 +201,8 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, if (sfc == ARRAY_SIZE(amdtp_rate_table)) return -EINVAL; - s->pcm_channels = pcm_channels; s->sfc = sfc; - s->data_block_quadlets = s->pcm_channels + midi_channels; - s->midi_ports = midi_ports; - + s->data_block_quadlets = data_block_quadlets; s->syt_interval = amdtp_syt_intervals[sfc]; /* default buffering in the device */ @@ -226,19 +211,6 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, /* additional buffering needed to adjust for no-data packets */ s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate; - /* init the position map for PCM and MIDI channels */ - for (i = 0; i < pcm_channels; i++) - s->pcm_positions[i] = i; - s->midi_position = s->pcm_channels; - - /* - * 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().) - */ - s->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1; - return 0; } EXPORT_SYMBOL(amdtp_stream_set_parameters); @@ -261,51 +233,6 @@ unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s) } EXPORT_SYMBOL(amdtp_stream_get_max_payload); -static void write_pcm_s16(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); -static void write_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); -static void read_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); - -/** - * amdtp_am824_set_pcm_format - set the PCM format - * @s: the AMDTP stream to configure - * @format: the format of the ALSA PCM device - * - * The sample format must be set after the other parameters (rate/PCM channels/ - * MIDI) and before the stream is started, and must not be changed while the - * stream is running. - */ -void amdtp_am824_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format) -{ - if (WARN_ON(amdtp_stream_pcm_running(s))) - return; - - switch (format) { - default: - WARN_ON(1); - /* fall through */ - case SNDRV_PCM_FORMAT_S16: - if (s->direction == AMDTP_OUT_STREAM) { - s->transfer_samples = write_pcm_s16; - break; - } - WARN_ON(1); - /* fall through */ - case SNDRV_PCM_FORMAT_S32: - if (s->direction == AMDTP_OUT_STREAM) - s->transfer_samples = write_pcm_s32; - else - s->transfer_samples = read_pcm_s32; - break; - } -} -EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_format); - /** * amdtp_stream_pcm_prepare - prepare PCM device for running * @s: the AMDTP stream @@ -408,168 +335,6 @@ static unsigned int calculate_syt(struct amdtp_stream *s, } } -static void write_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) -{ - struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; - const u32 *src; - - channels = s->pcm_channels; - src = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; - - for (i = 0; i < frames; ++i) { - for (c = 0; c < channels; ++c) { - buffer[s->pcm_positions[c]] = - cpu_to_be32((*src >> 8) | 0x40000000); - src++; - } - buffer += s->data_block_quadlets; - if (--remaining_frames == 0) - src = (void *)runtime->dma_area; - } -} - -static void write_pcm_s16(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) -{ - struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; - const u16 *src; - - channels = s->pcm_channels; - src = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; - - for (i = 0; i < frames; ++i) { - for (c = 0; c < channels; ++c) { - buffer[s->pcm_positions[c]] = - cpu_to_be32((*src << 8) | 0x42000000); - src++; - } - buffer += s->data_block_quadlets; - if (--remaining_frames == 0) - src = (void *)runtime->dma_area; - } -} - -static void read_pcm_s32(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames) -{ - struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; - u32 *dst; - - channels = s->pcm_channels; - dst = (void *)runtime->dma_area + - frames_to_bytes(runtime, s->pcm_buffer_pointer); - remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; - - for (i = 0; i < frames; ++i) { - for (c = 0; c < channels; ++c) { - *dst = be32_to_cpu(buffer[s->pcm_positions[c]]) << 8; - dst++; - } - buffer += s->data_block_quadlets; - if (--remaining_frames == 0) - dst = (void *)runtime->dma_area; - } -} - -static void write_pcm_silence(struct amdtp_stream *s, - __be32 *buffer, unsigned int frames) -{ - unsigned int i, c; - - for (i = 0; i < frames; ++i) { - for (c = 0; c < s->pcm_channels; ++c) - buffer[s->pcm_positions[c]] = cpu_to_be32(0x40000000); - buffer += s->data_block_quadlets; - } -} - -/* - * To avoid sending MIDI bytes at too high a rate, assume that the receiving - * device has a FIFO, and track how much it is filled. This values increases - * by one whenever we send one byte in a packet, but the FIFO empties at - * a constant rate independent of our packet rate. One packet has syt_interval - * samples, so the number of bytes that empty out of the FIFO, per packet(!), - * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate. To avoid storing - * fractional values, the values in midi_fifo_used[] are measured in bytes - * multiplied by the sample rate. - */ -static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port) -{ - int used; - - used = s->midi_fifo_used[port]; - if (used == 0) /* common shortcut */ - return true; - - used -= MIDI_BYTES_PER_SECOND * s->syt_interval; - used = max(used, 0); - s->midi_fifo_used[port] = used; - - return used < s->midi_fifo_limit; -} - -static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port) -{ - s->midi_fifo_used[port] += amdtp_rate_table[s->sfc]; -} - -static void write_midi_messages(struct amdtp_stream *s, - __be32 *buffer, unsigned int frames) -{ - unsigned int f, port; - u8 *b; - - for (f = 0; f < frames; f++) { - b = (u8 *)&buffer[s->midi_position]; - - port = (s->data_block_counter + f) % 8; - if (f < MAX_MIDI_RX_BLOCKS && - midi_ratelimit_per_packet(s, port) && - s->midi[port] != NULL && - snd_rawmidi_transmit(s->midi[port], &b[1], 1) == 1) { - midi_rate_use_one_byte(s, port); - b[0] = 0x81; - } else { - b[0] = 0x80; - b[1] = 0; - } - b[2] = 0; - b[3] = 0; - - buffer += s->data_block_quadlets; - } -} - -static void read_midi_messages(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->midi_position]; - - 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, struct snd_pcm_substream *pcm, unsigned int frames) @@ -639,26 +404,6 @@ static inline int queue_in_packet(struct amdtp_stream *s) amdtp_stream_get_max_payload(s), false); } -unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer, - unsigned int data_blocks, unsigned int *syt) -{ - struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); - unsigned int pcm_frames; - - if (pcm) { - s->transfer_samples(s, pcm, buffer, data_blocks); - pcm_frames = data_blocks * s->frame_multiplier; - } else { - write_pcm_silence(s, buffer, data_blocks); - pcm_frames = 0; - } - - if (s->midi_ports) - write_midi_messages(s, buffer, data_blocks); - - return pcm_frames; -} - static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, unsigned int syt) { @@ -668,7 +413,7 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, struct snd_pcm_substream *pcm; buffer = s->buffer.packets[s->packet_index].buffer; - pcm_frames = process_rx_data_blocks(s, buffer + 2, data_blocks, &syt); + pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt); buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | (s->data_block_quadlets << CIP_DBS_SHIFT) | @@ -692,25 +437,6 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks, return 0; } -unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer, - unsigned int data_blocks, unsigned int *syt) -{ - struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm); - unsigned int pcm_frames; - - if (pcm) { - s->transfer_samples(s, pcm, buffer, data_blocks); - pcm_frames = data_blocks * s->frame_multiplier; - } else { - pcm_frames = 0; - } - - if (s->midi_ports) - read_midi_messages(s, buffer, data_blocks); - - return pcm_frames; -} - static int handle_in_packet(struct amdtp_stream *s, unsigned int payload_quadlets, __be32 *buffer, unsigned int *data_blocks, unsigned int syt) @@ -798,7 +524,7 @@ static int handle_in_packet(struct amdtp_stream *s, return -EIO; } - pcm_frames = process_tx_data_blocks(s, buffer + 2, *data_blocks, &syt); + pcm_frames = s->process_data_blocks(s, buffer + 2, *data_blocks, &syt); if (s->flags & CIP_DBC_IS_END_EVENT) s->data_block_counter = data_block_counter; diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 71f4f751fabc..8775704a3665 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -81,39 +81,22 @@ enum cip_sfc { CIP_SFC_COUNT }; -#define AM824_IN_PCM_FORMAT_BITS SNDRV_PCM_FMTBIT_S32 - -#define AM824_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ - SNDRV_PCM_FMTBIT_S32) - - -/* - * This module supports maximum 64 PCM channels for one PCM stream - * This is for our convenience. - */ -#define AM824_MAX_CHANNELS_FOR_PCM 64 - -/* - * 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 AM824_MAX_CHANNELS_FOR_MIDI 1 - struct fw_unit; struct fw_iso_context; struct snd_pcm_substream; struct snd_pcm_runtime; -struct snd_rawmidi_substream; enum amdtp_stream_direction { AMDTP_OUT_STREAM = 0, AMDTP_IN_STREAM }; +struct amdtp_stream; +typedef unsigned int (*amdtp_stream_process_data_blocks_t)( + struct amdtp_stream *s, + __be32 *buffer, + unsigned int data_blocks, + unsigned int *syt); struct amdtp_stream { struct fw_unit *unit; enum cip_flags flags; @@ -156,32 +139,20 @@ struct amdtp_stream { wait_queue_head_t callback_wait; struct amdtp_stream *sync_slave; - /* For AM824 processing. */ - struct snd_rawmidi_substream *midi[AM824_MAX_CHANNELS_FOR_MIDI * 8]; - int midi_fifo_limit; - int midi_fifo_used[AM824_MAX_CHANNELS_FOR_MIDI * 8]; - unsigned int pcm_channels; - unsigned int midi_ports; - - u8 pcm_positions[AM824_MAX_CHANNELS_FOR_PCM]; - u8 midi_position; - - void (*transfer_samples)(struct amdtp_stream *s, - struct snd_pcm_substream *pcm, - __be32 *buffer, unsigned int frames); - - unsigned int frame_multiplier; + /* For backends to process data blocks. */ + void *protocol; + amdtp_stream_process_data_blocks_t process_data_blocks; }; int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, - enum amdtp_stream_direction dir, - enum cip_flags flags, unsigned int fmt); + enum amdtp_stream_direction dir, enum cip_flags flags, + unsigned int fmt, + amdtp_stream_process_data_blocks_t process_data_blocks, + unsigned int protocol_size); void amdtp_stream_destroy(struct amdtp_stream *s); -int amdtp_stream_set_parameters(struct amdtp_stream *s, - unsigned int rate, - unsigned int pcm_channels, - unsigned int midi_ports); +int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate, + unsigned int data_block_quadlets); unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s); int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed); @@ -190,8 +161,7 @@ void amdtp_stream_stop(struct amdtp_stream *s); int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); -void amdtp_am824_set_pcm_format(struct amdtp_stream *s, - snd_pcm_format_t format); + void amdtp_stream_pcm_prepare(struct amdtp_stream *s); unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s); void amdtp_stream_pcm_abort(struct amdtp_stream *s); -- cgit v1.2.3-55-g7522