diff options
Diffstat (limited to 'sound/firewire/tascam/amdtp-tascam.c')
| -rw-r--r-- | sound/firewire/tascam/amdtp-tascam.c | 169 |
1 files changed, 118 insertions, 51 deletions
diff --git a/sound/firewire/tascam/amdtp-tascam.c b/sound/firewire/tascam/amdtp-tascam.c index 6aff1fc1c72d..59c339d9b5fb 100644 --- a/sound/firewire/tascam/amdtp-tascam.c +++ b/sound/firewire/tascam/amdtp-tascam.c @@ -1,9 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * amdtp-tascam.c - a part of driver for TASCAM FireWire series * * Copyright (c) 2015 Takashi Sakamoto - * - * Licensed under the terms of the GNU General Public License, version 2. */ #include <sound/pcm.h> @@ -30,22 +29,27 @@ int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate) if (s->direction == AMDTP_IN_STREAM) data_channels += 2; - return amdtp_stream_set_parameters(s, rate, data_channels); + return amdtp_stream_set_parameters(s, rate, data_channels, 1); } -static void write_pcm_s32(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, + unsigned int pcm_frames) { struct amdtp_tscm *p = s->protocol; + unsigned int channels = p->pcm_channels; struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; + unsigned int pcm_buffer_pointer; + int remaining_frames; const u32 *src; + int i, c; + + pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames; + pcm_buffer_pointer %= runtime->buffer_size; - 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; + frames_to_bytes(runtime, pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - pcm_buffer_pointer; for (i = 0; i < frames; ++i) { for (c = 0; c < channels; ++c) { @@ -58,19 +62,24 @@ static void write_pcm_s32(struct amdtp_stream *s, } } -static void read_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, + unsigned int pcm_frames) { struct amdtp_tscm *p = s->protocol; + unsigned int channels = p->pcm_channels; struct snd_pcm_runtime *runtime = pcm->runtime; - unsigned int channels, remaining_frames, i, c; + unsigned int pcm_buffer_pointer; + int remaining_frames; u32 *dst; + int i, c; + + pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames; + pcm_buffer_pointer %= runtime->buffer_size; - 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; + frames_to_bytes(runtime, pcm_buffer_pointer); + remaining_frames = runtime->buffer_size - pcm_buffer_pointer; /* The first data channel is for event counter. */ buffer += 1; @@ -117,65 +126,123 @@ int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s, return amdtp_stream_add_pcm_hw_constraints(s, runtime); } -static unsigned int process_tx_data_blocks(struct amdtp_stream *s, - __be32 *buffer, - unsigned int data_blocks, - unsigned int *syt) +static void read_status_messages(struct amdtp_stream *s, + __be32 *buffer, unsigned int data_blocks) { - struct snd_pcm_substream *pcm; - - pcm = ACCESS_ONCE(s->pcm); - if (data_blocks > 0 && pcm) - read_pcm_s32(s, pcm, buffer, data_blocks); - - /* A place holder for control messages. */ + struct snd_tscm *tscm = container_of(s, struct snd_tscm, tx_stream); + bool used = READ_ONCE(tscm->hwdep->used); + int i; + + for (i = 0; i < data_blocks; i++) { + unsigned int index; + __be32 before; + __be32 after; + + index = be32_to_cpu(buffer[0]) % SNDRV_FIREWIRE_TASCAM_STATE_COUNT; + before = tscm->state[index]; + after = buffer[s->data_block_quadlets - 1]; + + if (used && index > 4 && index < 16) { + __be32 mask; + + if (index == 5) + mask = cpu_to_be32(~0x0000ffff); + else if (index == 6) + mask = cpu_to_be32(~0x0000ffff); + else if (index == 8) + mask = cpu_to_be32(~0x000f0f00); + else + mask = cpu_to_be32(~0x00000000); + + if ((before ^ after) & mask) { + struct snd_firewire_tascam_change *entry = + &tscm->queue[tscm->push_pos]; + + scoped_guard(spinlock_irqsave, &tscm->lock) { + entry->index = index; + entry->before = before; + entry->after = after; + if (++tscm->push_pos >= SND_TSCM_QUEUE_COUNT) + tscm->push_pos = 0; + } + + wake_up(&tscm->hwdep_wait); + } + } - return data_blocks; + tscm->state[index] = after; + buffer += s->data_block_quadlets; + } } -static unsigned int process_rx_data_blocks(struct amdtp_stream *s, - __be32 *buffer, - unsigned int data_blocks, - unsigned int *syt) +static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) { - struct snd_pcm_substream *pcm; + unsigned int pcm_frames = 0; + int i; - /* This field is not used. */ - *syt = 0x0000; + for (i = 0; i < count; ++i) { + __be32 *buf = desc->ctx_payload; + unsigned int data_blocks = desc->data_blocks; - pcm = ACCESS_ONCE(s->pcm); - if (pcm) - write_pcm_s32(s, pcm, buffer, data_blocks); - else - write_pcm_silence(s, buffer, data_blocks); + if (pcm) { + read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); + pcm_frames += data_blocks; + } - return data_blocks; + read_status_messages(s, buf, data_blocks); + + desc = amdtp_stream_next_packet_desc(s, desc); + } +} + +static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc, + unsigned int count, struct snd_pcm_substream *pcm) +{ + unsigned int pcm_frames = 0; + int i; + + for (i = 0; i < count; ++i) { + __be32 *buf = desc->ctx_payload; + unsigned int data_blocks = desc->data_blocks; + + if (pcm) { + write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); + pcm_frames += data_blocks; + } else { + write_pcm_silence(s, buf, data_blocks); + } + + desc = amdtp_stream_next_packet_desc(s, desc); + } } int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, unsigned int pcm_channels) { - amdtp_stream_process_data_blocks_t process_data_blocks; + amdtp_stream_process_ctx_payloads_t process_ctx_payloads; + unsigned int flags = CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK | CIP_UNAWARE_SYT; struct amdtp_tscm *p; unsigned int fmt; int err; if (dir == AMDTP_IN_STREAM) { fmt = AMDTP_FMT_TSCM_TX; - process_data_blocks = process_tx_data_blocks; + process_ctx_payloads = process_ir_ctx_payloads; } else { fmt = AMDTP_FMT_TSCM_RX; - process_data_blocks = process_rx_data_blocks; + process_ctx_payloads = process_it_ctx_payloads; } - err = amdtp_stream_init(s, unit, dir, - CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt, - process_data_blocks, sizeof(struct amdtp_tscm)); + err = amdtp_stream_init(s, unit, dir, flags, fmt, + process_ctx_payloads, sizeof(struct amdtp_tscm)); if (err < 0) - return 0; + return err; - /* Use fixed value for FDF field. */ - s->fdf = 0x00; + if (dir == AMDTP_OUT_STREAM) { + // Use fixed value for FDF field. + s->ctx_data.rx.fdf = 0x00; + } /* This protocol uses fixed number of data channels for PCM samples. */ p = s->protocol; |
