| .. | .. | 
|---|
|  | 1 | +// SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 1 | 2 | /* | 
|---|
| 2 | 3 | * amdtp-tascam.c - a part of driver for TASCAM FireWire series | 
|---|
| 3 | 4 | * | 
|---|
| 4 | 5 | * Copyright (c) 2015 Takashi Sakamoto | 
|---|
| 5 |  | - * | 
|---|
| 6 |  | - * Licensed under the terms of the GNU General Public License, version 2. | 
|---|
| 7 | 6 | */ | 
|---|
| 8 | 7 |  | 
|---|
| 9 | 8 | #include <sound/pcm.h> | 
|---|
| .. | .. | 
|---|
| 33 | 32 | return amdtp_stream_set_parameters(s, rate, data_channels); | 
|---|
| 34 | 33 | } | 
|---|
| 35 | 34 |  | 
|---|
| 36 |  | -static void write_pcm_s32(struct amdtp_stream *s, | 
|---|
| 37 |  | -			  struct snd_pcm_substream *pcm, | 
|---|
| 38 |  | -			  __be32 *buffer, unsigned int frames) | 
|---|
|  | 35 | +static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, | 
|---|
|  | 36 | +			  __be32 *buffer, unsigned int frames, | 
|---|
|  | 37 | +			  unsigned int pcm_frames) | 
|---|
| 39 | 38 | { | 
|---|
| 40 | 39 | struct amdtp_tscm *p = s->protocol; | 
|---|
|  | 40 | +	unsigned int channels = p->pcm_channels; | 
|---|
| 41 | 41 | struct snd_pcm_runtime *runtime = pcm->runtime; | 
|---|
| 42 |  | -	unsigned int channels, remaining_frames, i, c; | 
|---|
|  | 42 | +	unsigned int pcm_buffer_pointer; | 
|---|
|  | 43 | +	int remaining_frames; | 
|---|
| 43 | 44 | const u32 *src; | 
|---|
|  | 45 | +	int i, c; | 
|---|
| 44 | 46 |  | 
|---|
| 45 |  | -	channels = p->pcm_channels; | 
|---|
|  | 47 | +	pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames; | 
|---|
|  | 48 | +	pcm_buffer_pointer %= runtime->buffer_size; | 
|---|
|  | 49 | + | 
|---|
| 46 | 50 | src = (void *)runtime->dma_area + | 
|---|
| 47 |  | -			frames_to_bytes(runtime, s->pcm_buffer_pointer); | 
|---|
| 48 |  | -	remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; | 
|---|
|  | 51 | +				frames_to_bytes(runtime, pcm_buffer_pointer); | 
|---|
|  | 52 | +	remaining_frames = runtime->buffer_size - pcm_buffer_pointer; | 
|---|
| 49 | 53 |  | 
|---|
| 50 | 54 | for (i = 0; i < frames; ++i) { | 
|---|
| 51 | 55 | for (c = 0; c < channels; ++c) { | 
|---|
| .. | .. | 
|---|
| 58 | 62 | } | 
|---|
| 59 | 63 | } | 
|---|
| 60 | 64 |  | 
|---|
| 61 |  | -static void read_pcm_s32(struct amdtp_stream *s, | 
|---|
| 62 |  | -			 struct snd_pcm_substream *pcm, | 
|---|
| 63 |  | -			 __be32 *buffer, unsigned int frames) | 
|---|
|  | 65 | +static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm, | 
|---|
|  | 66 | +			 __be32 *buffer, unsigned int frames, | 
|---|
|  | 67 | +			 unsigned int pcm_frames) | 
|---|
| 64 | 68 | { | 
|---|
| 65 | 69 | struct amdtp_tscm *p = s->protocol; | 
|---|
|  | 70 | +	unsigned int channels = p->pcm_channels; | 
|---|
| 66 | 71 | struct snd_pcm_runtime *runtime = pcm->runtime; | 
|---|
| 67 |  | -	unsigned int channels, remaining_frames, i, c; | 
|---|
|  | 72 | +	unsigned int pcm_buffer_pointer; | 
|---|
|  | 73 | +	int remaining_frames; | 
|---|
| 68 | 74 | u32 *dst; | 
|---|
|  | 75 | +	int i, c; | 
|---|
| 69 | 76 |  | 
|---|
| 70 |  | -	channels = p->pcm_channels; | 
|---|
|  | 77 | +	pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames; | 
|---|
|  | 78 | +	pcm_buffer_pointer %= runtime->buffer_size; | 
|---|
|  | 79 | + | 
|---|
| 71 | 80 | dst  = (void *)runtime->dma_area + | 
|---|
| 72 |  | -			frames_to_bytes(runtime, s->pcm_buffer_pointer); | 
|---|
| 73 |  | -	remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; | 
|---|
|  | 81 | +				frames_to_bytes(runtime, pcm_buffer_pointer); | 
|---|
|  | 82 | +	remaining_frames = runtime->buffer_size - pcm_buffer_pointer; | 
|---|
| 74 | 83 |  | 
|---|
| 75 | 84 | /* The first data channel is for event counter. */ | 
|---|
| 76 | 85 | buffer += 1; | 
|---|
| .. | .. | 
|---|
| 117 | 126 | return amdtp_stream_add_pcm_hw_constraints(s, runtime); | 
|---|
| 118 | 127 | } | 
|---|
| 119 | 128 |  | 
|---|
| 120 |  | -static unsigned int process_tx_data_blocks(struct amdtp_stream *s, | 
|---|
| 121 |  | -					   __be32 *buffer, | 
|---|
| 122 |  | -					   unsigned int data_blocks, | 
|---|
| 123 |  | -					   unsigned int *syt) | 
|---|
|  | 129 | +static void read_status_messages(struct amdtp_stream *s, | 
|---|
|  | 130 | +				 __be32 *buffer, unsigned int data_blocks) | 
|---|
| 124 | 131 | { | 
|---|
| 125 |  | -	struct snd_pcm_substream *pcm; | 
|---|
|  | 132 | +	struct snd_tscm *tscm = container_of(s, struct snd_tscm, tx_stream); | 
|---|
|  | 133 | +	bool used = READ_ONCE(tscm->hwdep->used); | 
|---|
|  | 134 | +	int i; | 
|---|
| 126 | 135 |  | 
|---|
| 127 |  | -	pcm = READ_ONCE(s->pcm); | 
|---|
| 128 |  | -	if (data_blocks > 0 && pcm) | 
|---|
| 129 |  | -		read_pcm_s32(s, pcm, buffer, data_blocks); | 
|---|
|  | 136 | +	for (i = 0; i < data_blocks; i++) { | 
|---|
|  | 137 | +		unsigned int index; | 
|---|
|  | 138 | +		__be32 before; | 
|---|
|  | 139 | +		__be32 after; | 
|---|
| 130 | 140 |  | 
|---|
| 131 |  | -	/* A place holder for control messages. */ | 
|---|
|  | 141 | +		index = be32_to_cpu(buffer[0]) % SNDRV_FIREWIRE_TASCAM_STATE_COUNT; | 
|---|
|  | 142 | +		before = tscm->state[index]; | 
|---|
|  | 143 | +		after = buffer[s->data_block_quadlets - 1]; | 
|---|
| 132 | 144 |  | 
|---|
| 133 |  | -	return data_blocks; | 
|---|
|  | 145 | +		if (used && index > 4 && index < 16) { | 
|---|
|  | 146 | +			__be32 mask; | 
|---|
|  | 147 | + | 
|---|
|  | 148 | +			if (index == 5) | 
|---|
|  | 149 | +				mask = cpu_to_be32(~0x0000ffff); | 
|---|
|  | 150 | +			else if (index == 6) | 
|---|
|  | 151 | +				mask = cpu_to_be32(~0x0000ffff); | 
|---|
|  | 152 | +			else if (index == 8) | 
|---|
|  | 153 | +				mask = cpu_to_be32(~0x000f0f00); | 
|---|
|  | 154 | +			else | 
|---|
|  | 155 | +				mask = cpu_to_be32(~0x00000000); | 
|---|
|  | 156 | + | 
|---|
|  | 157 | +			if ((before ^ after) & mask) { | 
|---|
|  | 158 | +				struct snd_firewire_tascam_change *entry = | 
|---|
|  | 159 | +						&tscm->queue[tscm->push_pos]; | 
|---|
|  | 160 | +				unsigned long flag; | 
|---|
|  | 161 | + | 
|---|
|  | 162 | +				spin_lock_irqsave(&tscm->lock, flag); | 
|---|
|  | 163 | +				entry->index = index; | 
|---|
|  | 164 | +				entry->before = before; | 
|---|
|  | 165 | +				entry->after = after; | 
|---|
|  | 166 | +				if (++tscm->push_pos >= SND_TSCM_QUEUE_COUNT) | 
|---|
|  | 167 | +					tscm->push_pos = 0; | 
|---|
|  | 168 | +				spin_unlock_irqrestore(&tscm->lock, flag); | 
|---|
|  | 169 | + | 
|---|
|  | 170 | +				wake_up(&tscm->hwdep_wait); | 
|---|
|  | 171 | +			} | 
|---|
|  | 172 | +		} | 
|---|
|  | 173 | + | 
|---|
|  | 174 | +		tscm->state[index] = after; | 
|---|
|  | 175 | +		buffer += s->data_block_quadlets; | 
|---|
|  | 176 | +	} | 
|---|
| 134 | 177 | } | 
|---|
| 135 | 178 |  | 
|---|
| 136 |  | -static unsigned int process_rx_data_blocks(struct amdtp_stream *s, | 
|---|
| 137 |  | -					   __be32 *buffer, | 
|---|
| 138 |  | -					   unsigned int data_blocks, | 
|---|
| 139 |  | -					   unsigned int *syt) | 
|---|
|  | 179 | +static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, | 
|---|
|  | 180 | +					    const struct pkt_desc *descs, | 
|---|
|  | 181 | +					    unsigned int packets, | 
|---|
|  | 182 | +					    struct snd_pcm_substream *pcm) | 
|---|
| 140 | 183 | { | 
|---|
| 141 |  | -	struct snd_pcm_substream *pcm; | 
|---|
|  | 184 | +	unsigned int pcm_frames = 0; | 
|---|
|  | 185 | +	int i; | 
|---|
| 142 | 186 |  | 
|---|
| 143 |  | -	/* This field is not used. */ | 
|---|
| 144 |  | -	*syt = 0x0000; | 
|---|
|  | 187 | +	for (i = 0; i < packets; ++i) { | 
|---|
|  | 188 | +		const struct pkt_desc *desc = descs + i; | 
|---|
|  | 189 | +		__be32 *buf = desc->ctx_payload; | 
|---|
|  | 190 | +		unsigned int data_blocks = desc->data_blocks; | 
|---|
| 145 | 191 |  | 
|---|
| 146 |  | -	pcm = READ_ONCE(s->pcm); | 
|---|
| 147 |  | -	if (pcm) | 
|---|
| 148 |  | -		write_pcm_s32(s, pcm, buffer, data_blocks); | 
|---|
| 149 |  | -	else | 
|---|
| 150 |  | -		write_pcm_silence(s, buffer, data_blocks); | 
|---|
|  | 192 | +		if (pcm) { | 
|---|
|  | 193 | +			read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); | 
|---|
|  | 194 | +			pcm_frames += data_blocks; | 
|---|
|  | 195 | +		} | 
|---|
| 151 | 196 |  | 
|---|
| 152 |  | -	return data_blocks; | 
|---|
|  | 197 | +		read_status_messages(s, buf, data_blocks); | 
|---|
|  | 198 | +	} | 
|---|
|  | 199 | + | 
|---|
|  | 200 | +	return pcm_frames; | 
|---|
|  | 201 | +} | 
|---|
|  | 202 | + | 
|---|
|  | 203 | +static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, | 
|---|
|  | 204 | +					    const struct pkt_desc *descs, | 
|---|
|  | 205 | +					    unsigned int packets, | 
|---|
|  | 206 | +					    struct snd_pcm_substream *pcm) | 
|---|
|  | 207 | +{ | 
|---|
|  | 208 | +	unsigned int pcm_frames = 0; | 
|---|
|  | 209 | +	int i; | 
|---|
|  | 210 | + | 
|---|
|  | 211 | +	for (i = 0; i < packets; ++i) { | 
|---|
|  | 212 | +		const struct pkt_desc *desc = descs + i; | 
|---|
|  | 213 | +		__be32 *buf = desc->ctx_payload; | 
|---|
|  | 214 | +		unsigned int data_blocks = desc->data_blocks; | 
|---|
|  | 215 | + | 
|---|
|  | 216 | +		if (pcm) { | 
|---|
|  | 217 | +			write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames); | 
|---|
|  | 218 | +			pcm_frames += data_blocks; | 
|---|
|  | 219 | +		} else { | 
|---|
|  | 220 | +			write_pcm_silence(s, buf, data_blocks); | 
|---|
|  | 221 | +		} | 
|---|
|  | 222 | +	} | 
|---|
|  | 223 | + | 
|---|
|  | 224 | +	return pcm_frames; | 
|---|
| 153 | 225 | } | 
|---|
| 154 | 226 |  | 
|---|
| 155 | 227 | int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit, | 
|---|
| 156 | 228 | enum amdtp_stream_direction dir, unsigned int pcm_channels) | 
|---|
| 157 | 229 | { | 
|---|
| 158 |  | -	amdtp_stream_process_data_blocks_t process_data_blocks; | 
|---|
|  | 230 | +	amdtp_stream_process_ctx_payloads_t process_ctx_payloads; | 
|---|
| 159 | 231 | struct amdtp_tscm *p; | 
|---|
| 160 | 232 | unsigned int fmt; | 
|---|
| 161 | 233 | int err; | 
|---|
| 162 | 234 |  | 
|---|
| 163 | 235 | if (dir == AMDTP_IN_STREAM) { | 
|---|
| 164 | 236 | fmt = AMDTP_FMT_TSCM_TX; | 
|---|
| 165 |  | -		process_data_blocks = process_tx_data_blocks; | 
|---|
|  | 237 | +		process_ctx_payloads = process_ir_ctx_payloads; | 
|---|
| 166 | 238 | } else { | 
|---|
| 167 | 239 | fmt = AMDTP_FMT_TSCM_RX; | 
|---|
| 168 |  | -		process_data_blocks = process_rx_data_blocks; | 
|---|
|  | 240 | +		process_ctx_payloads = process_it_ctx_payloads; | 
|---|
| 169 | 241 | } | 
|---|
| 170 | 242 |  | 
|---|
| 171 | 243 | err = amdtp_stream_init(s, unit, dir, | 
|---|
| 172 |  | -				CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt, | 
|---|
| 173 |  | -				process_data_blocks, sizeof(struct amdtp_tscm)); | 
|---|
|  | 244 | +			CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt, | 
|---|
|  | 245 | +			process_ctx_payloads, sizeof(struct amdtp_tscm)); | 
|---|
| 174 | 246 | if (err < 0) | 
|---|
| 175 | 247 | return 0; | 
|---|
| 176 | 248 |  | 
|---|
| 177 |  | -	/* Use fixed value for FDF field. */ | 
|---|
| 178 |  | -	s->fdf = 0x00; | 
|---|
|  | 249 | +	if (dir == AMDTP_OUT_STREAM) { | 
|---|
|  | 250 | +		// Use fixed value for FDF field. | 
|---|
|  | 251 | +		s->ctx_data.rx.fdf = 0x00; | 
|---|
|  | 252 | +		// Not used. | 
|---|
|  | 253 | +		s->ctx_data.rx.syt_override = 0x0000; | 
|---|
|  | 254 | +	} | 
|---|
| 179 | 255 |  | 
|---|
| 180 | 256 | /* This protocol uses fixed number of data channels for PCM samples. */ | 
|---|
| 181 | 257 | p = s->protocol; | 
|---|