brintos

brintos / linux-shallow public Read only

0
0
Text · 6.2 KiB · 079afa4 Raw
254 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * amdtp-tascam.c - a part of driver for TASCAM FireWire series4 *5 * Copyright (c) 2015 Takashi Sakamoto6 */7 8#include <sound/pcm.h>9#include "tascam.h"10 11#define AMDTP_FMT_TSCM_TX	0x1e12#define AMDTP_FMT_TSCM_RX	0x3e13 14struct amdtp_tscm {15	unsigned int pcm_channels;16};17 18int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate)19{20	struct amdtp_tscm *p = s->protocol;21	unsigned int data_channels;22 23	if (amdtp_stream_running(s))24		return -EBUSY;25 26	data_channels = p->pcm_channels;27 28	/* Packets in in-stream have extra 2 data channels. */29	if (s->direction == AMDTP_IN_STREAM)30		data_channels += 2;31 32	return amdtp_stream_set_parameters(s, rate, data_channels, 1);33}34 35static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,36			  __be32 *buffer, unsigned int frames,37			  unsigned int pcm_frames)38{39	struct amdtp_tscm *p = s->protocol;40	unsigned int channels = p->pcm_channels;41	struct snd_pcm_runtime *runtime = pcm->runtime;42	unsigned int pcm_buffer_pointer;43	int remaining_frames;44	const u32 *src;45	int i, c;46 47	pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;48	pcm_buffer_pointer %= runtime->buffer_size;49 50	src = (void *)runtime->dma_area +51				frames_to_bytes(runtime, pcm_buffer_pointer);52	remaining_frames = runtime->buffer_size - pcm_buffer_pointer;53 54	for (i = 0; i < frames; ++i) {55		for (c = 0; c < channels; ++c) {56			buffer[c] = cpu_to_be32(*src);57			src++;58		}59		buffer += s->data_block_quadlets;60		if (--remaining_frames == 0)61			src = (void *)runtime->dma_area;62	}63}64 65static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,66			 __be32 *buffer, unsigned int frames,67			 unsigned int pcm_frames)68{69	struct amdtp_tscm *p = s->protocol;70	unsigned int channels = p->pcm_channels;71	struct snd_pcm_runtime *runtime = pcm->runtime;72	unsigned int pcm_buffer_pointer;73	int remaining_frames;74	u32 *dst;75	int i, c;76 77	pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;78	pcm_buffer_pointer %= runtime->buffer_size;79 80	dst  = (void *)runtime->dma_area +81				frames_to_bytes(runtime, pcm_buffer_pointer);82	remaining_frames = runtime->buffer_size - pcm_buffer_pointer;83 84	/* The first data channel is for event counter. */85	buffer += 1;86 87	for (i = 0; i < frames; ++i) {88		for (c = 0; c < channels; ++c) {89			*dst = be32_to_cpu(buffer[c]);90			dst++;91		}92		buffer += s->data_block_quadlets;93		if (--remaining_frames == 0)94			dst = (void *)runtime->dma_area;95	}96}97 98static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,99			      unsigned int data_blocks)100{101	struct amdtp_tscm *p = s->protocol;102	unsigned int channels, i, c;103 104	channels = p->pcm_channels;105 106	for (i = 0; i < data_blocks; ++i) {107		for (c = 0; c < channels; ++c)108			buffer[c] = 0x00000000;109		buffer += s->data_block_quadlets;110	}111}112 113int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,114				      struct snd_pcm_runtime *runtime)115{116	int err;117 118	/*119	 * Our implementation allows this protocol to deliver 24 bit sample in120	 * 32bit data channel.121	 */122	err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);123	if (err < 0)124		return err;125 126	return amdtp_stream_add_pcm_hw_constraints(s, runtime);127}128 129static void read_status_messages(struct amdtp_stream *s,130				 __be32 *buffer, unsigned int data_blocks)131{132	struct snd_tscm *tscm = container_of(s, struct snd_tscm, tx_stream);133	bool used = READ_ONCE(tscm->hwdep->used);134	int i;135 136	for (i = 0; i < data_blocks; i++) {137		unsigned int index;138		__be32 before;139		__be32 after;140 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];144 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			else155				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	}177}178 179static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,180				    unsigned int count, struct snd_pcm_substream *pcm)181{182	unsigned int pcm_frames = 0;183	int i;184 185	for (i = 0; i < count; ++i) {186		__be32 *buf = desc->ctx_payload;187		unsigned int data_blocks = desc->data_blocks;188 189		if (pcm) {190			read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);191			pcm_frames += data_blocks;192		}193 194		read_status_messages(s, buf, data_blocks);195 196		desc = amdtp_stream_next_packet_desc(s, desc);197	}198}199 200static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,201				    unsigned int count, struct snd_pcm_substream *pcm)202{203	unsigned int pcm_frames = 0;204	int i;205 206	for (i = 0; i < count; ++i) {207		__be32 *buf = desc->ctx_payload;208		unsigned int data_blocks = desc->data_blocks;209 210		if (pcm) {211			write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);212			pcm_frames += data_blocks;213		} else {214			write_pcm_silence(s, buf, data_blocks);215		}216 217		desc = amdtp_stream_next_packet_desc(s, desc);218	}219}220 221int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,222		    enum amdtp_stream_direction dir, unsigned int pcm_channels)223{224	amdtp_stream_process_ctx_payloads_t process_ctx_payloads;225	unsigned int flags = CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK | CIP_UNAWARE_SYT;226	struct amdtp_tscm *p;227	unsigned int fmt;228	int err;229 230	if (dir == AMDTP_IN_STREAM) {231		fmt = AMDTP_FMT_TSCM_TX;232		process_ctx_payloads = process_ir_ctx_payloads;233	} else {234		fmt = AMDTP_FMT_TSCM_RX;235		process_ctx_payloads = process_it_ctx_payloads;236	}237 238	err = amdtp_stream_init(s, unit, dir, flags, fmt,239			process_ctx_payloads, sizeof(struct amdtp_tscm));240	if (err < 0)241		return err;242 243	if (dir == AMDTP_OUT_STREAM) {244		// Use fixed value for FDF field.245		s->ctx_data.rx.fdf = 0x00;246	}247 248	/* This protocol uses fixed number of data channels for PCM samples. */249	p = s->protocol;250	p->pcm_channels = pcm_channels;251 252	return 0;253}254