18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * amdtp-tascam.c - a part of driver for TASCAM FireWire series
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2015 Takashi Sakamoto
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <sound/pcm.h>
98c2ecf20Sopenharmony_ci#include "tascam.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define AMDTP_FMT_TSCM_TX	0x1e
128c2ecf20Sopenharmony_ci#define AMDTP_FMT_TSCM_RX	0x3e
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistruct amdtp_tscm {
158c2ecf20Sopenharmony_ci	unsigned int pcm_channels;
168c2ecf20Sopenharmony_ci};
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ciint amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	struct amdtp_tscm *p = s->protocol;
218c2ecf20Sopenharmony_ci	unsigned int data_channels;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	if (amdtp_stream_running(s))
248c2ecf20Sopenharmony_ci		return -EBUSY;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	data_channels = p->pcm_channels;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	/* Packets in in-stream have extra 2 data channels. */
298c2ecf20Sopenharmony_ci	if (s->direction == AMDTP_IN_STREAM)
308c2ecf20Sopenharmony_ci		data_channels += 2;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	return amdtp_stream_set_parameters(s, rate, data_channels);
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
368c2ecf20Sopenharmony_ci			  __be32 *buffer, unsigned int frames,
378c2ecf20Sopenharmony_ci			  unsigned int pcm_frames)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	struct amdtp_tscm *p = s->protocol;
408c2ecf20Sopenharmony_ci	unsigned int channels = p->pcm_channels;
418c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = pcm->runtime;
428c2ecf20Sopenharmony_ci	unsigned int pcm_buffer_pointer;
438c2ecf20Sopenharmony_ci	int remaining_frames;
448c2ecf20Sopenharmony_ci	const u32 *src;
458c2ecf20Sopenharmony_ci	int i, c;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
488c2ecf20Sopenharmony_ci	pcm_buffer_pointer %= runtime->buffer_size;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	src = (void *)runtime->dma_area +
518c2ecf20Sopenharmony_ci				frames_to_bytes(runtime, pcm_buffer_pointer);
528c2ecf20Sopenharmony_ci	remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	for (i = 0; i < frames; ++i) {
558c2ecf20Sopenharmony_ci		for (c = 0; c < channels; ++c) {
568c2ecf20Sopenharmony_ci			buffer[c] = cpu_to_be32(*src);
578c2ecf20Sopenharmony_ci			src++;
588c2ecf20Sopenharmony_ci		}
598c2ecf20Sopenharmony_ci		buffer += s->data_block_quadlets;
608c2ecf20Sopenharmony_ci		if (--remaining_frames == 0)
618c2ecf20Sopenharmony_ci			src = (void *)runtime->dma_area;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
668c2ecf20Sopenharmony_ci			 __be32 *buffer, unsigned int frames,
678c2ecf20Sopenharmony_ci			 unsigned int pcm_frames)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct amdtp_tscm *p = s->protocol;
708c2ecf20Sopenharmony_ci	unsigned int channels = p->pcm_channels;
718c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = pcm->runtime;
728c2ecf20Sopenharmony_ci	unsigned int pcm_buffer_pointer;
738c2ecf20Sopenharmony_ci	int remaining_frames;
748c2ecf20Sopenharmony_ci	u32 *dst;
758c2ecf20Sopenharmony_ci	int i, c;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
788c2ecf20Sopenharmony_ci	pcm_buffer_pointer %= runtime->buffer_size;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	dst  = (void *)runtime->dma_area +
818c2ecf20Sopenharmony_ci				frames_to_bytes(runtime, pcm_buffer_pointer);
828c2ecf20Sopenharmony_ci	remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/* The first data channel is for event counter. */
858c2ecf20Sopenharmony_ci	buffer += 1;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	for (i = 0; i < frames; ++i) {
888c2ecf20Sopenharmony_ci		for (c = 0; c < channels; ++c) {
898c2ecf20Sopenharmony_ci			*dst = be32_to_cpu(buffer[c]);
908c2ecf20Sopenharmony_ci			dst++;
918c2ecf20Sopenharmony_ci		}
928c2ecf20Sopenharmony_ci		buffer += s->data_block_quadlets;
938c2ecf20Sopenharmony_ci		if (--remaining_frames == 0)
948c2ecf20Sopenharmony_ci			dst = (void *)runtime->dma_area;
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
998c2ecf20Sopenharmony_ci			      unsigned int data_blocks)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	struct amdtp_tscm *p = s->protocol;
1028c2ecf20Sopenharmony_ci	unsigned int channels, i, c;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	channels = p->pcm_channels;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	for (i = 0; i < data_blocks; ++i) {
1078c2ecf20Sopenharmony_ci		for (c = 0; c < channels; ++c)
1088c2ecf20Sopenharmony_ci			buffer[c] = 0x00000000;
1098c2ecf20Sopenharmony_ci		buffer += s->data_block_quadlets;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ciint amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,
1148c2ecf20Sopenharmony_ci				      struct snd_pcm_runtime *runtime)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	int err;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/*
1198c2ecf20Sopenharmony_ci	 * Our implementation allows this protocol to deliver 24 bit sample in
1208c2ecf20Sopenharmony_ci	 * 32bit data channel.
1218c2ecf20Sopenharmony_ci	 */
1228c2ecf20Sopenharmony_ci	err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
1238c2ecf20Sopenharmony_ci	if (err < 0)
1248c2ecf20Sopenharmony_ci		return err;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	return amdtp_stream_add_pcm_hw_constraints(s, runtime);
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic void read_status_messages(struct amdtp_stream *s,
1308c2ecf20Sopenharmony_ci				 __be32 *buffer, unsigned int data_blocks)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct snd_tscm *tscm = container_of(s, struct snd_tscm, tx_stream);
1338c2ecf20Sopenharmony_ci	bool used = READ_ONCE(tscm->hwdep->used);
1348c2ecf20Sopenharmony_ci	int i;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	for (i = 0; i < data_blocks; i++) {
1378c2ecf20Sopenharmony_ci		unsigned int index;
1388c2ecf20Sopenharmony_ci		__be32 before;
1398c2ecf20Sopenharmony_ci		__be32 after;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci		index = be32_to_cpu(buffer[0]) % SNDRV_FIREWIRE_TASCAM_STATE_COUNT;
1428c2ecf20Sopenharmony_ci		before = tscm->state[index];
1438c2ecf20Sopenharmony_ci		after = buffer[s->data_block_quadlets - 1];
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci		if (used && index > 4 && index < 16) {
1468c2ecf20Sopenharmony_ci			__be32 mask;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci			if (index == 5)
1498c2ecf20Sopenharmony_ci				mask = cpu_to_be32(~0x0000ffff);
1508c2ecf20Sopenharmony_ci			else if (index == 6)
1518c2ecf20Sopenharmony_ci				mask = cpu_to_be32(~0x0000ffff);
1528c2ecf20Sopenharmony_ci			else if (index == 8)
1538c2ecf20Sopenharmony_ci				mask = cpu_to_be32(~0x000f0f00);
1548c2ecf20Sopenharmony_ci			else
1558c2ecf20Sopenharmony_ci				mask = cpu_to_be32(~0x00000000);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci			if ((before ^ after) & mask) {
1588c2ecf20Sopenharmony_ci				struct snd_firewire_tascam_change *entry =
1598c2ecf20Sopenharmony_ci						&tscm->queue[tscm->push_pos];
1608c2ecf20Sopenharmony_ci				unsigned long flag;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci				spin_lock_irqsave(&tscm->lock, flag);
1638c2ecf20Sopenharmony_ci				entry->index = index;
1648c2ecf20Sopenharmony_ci				entry->before = before;
1658c2ecf20Sopenharmony_ci				entry->after = after;
1668c2ecf20Sopenharmony_ci				if (++tscm->push_pos >= SND_TSCM_QUEUE_COUNT)
1678c2ecf20Sopenharmony_ci					tscm->push_pos = 0;
1688c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&tscm->lock, flag);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci				wake_up(&tscm->hwdep_wait);
1718c2ecf20Sopenharmony_ci			}
1728c2ecf20Sopenharmony_ci		}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci		tscm->state[index] = after;
1758c2ecf20Sopenharmony_ci		buffer += s->data_block_quadlets;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
1808c2ecf20Sopenharmony_ci					    const struct pkt_desc *descs,
1818c2ecf20Sopenharmony_ci					    unsigned int packets,
1828c2ecf20Sopenharmony_ci					    struct snd_pcm_substream *pcm)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	unsigned int pcm_frames = 0;
1858c2ecf20Sopenharmony_ci	int i;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	for (i = 0; i < packets; ++i) {
1888c2ecf20Sopenharmony_ci		const struct pkt_desc *desc = descs + i;
1898c2ecf20Sopenharmony_ci		__be32 *buf = desc->ctx_payload;
1908c2ecf20Sopenharmony_ci		unsigned int data_blocks = desc->data_blocks;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		if (pcm) {
1938c2ecf20Sopenharmony_ci			read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
1948c2ecf20Sopenharmony_ci			pcm_frames += data_blocks;
1958c2ecf20Sopenharmony_ci		}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci		read_status_messages(s, buf, data_blocks);
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	return pcm_frames;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
2048c2ecf20Sopenharmony_ci					    const struct pkt_desc *descs,
2058c2ecf20Sopenharmony_ci					    unsigned int packets,
2068c2ecf20Sopenharmony_ci					    struct snd_pcm_substream *pcm)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	unsigned int pcm_frames = 0;
2098c2ecf20Sopenharmony_ci	int i;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	for (i = 0; i < packets; ++i) {
2128c2ecf20Sopenharmony_ci		const struct pkt_desc *desc = descs + i;
2138c2ecf20Sopenharmony_ci		__be32 *buf = desc->ctx_payload;
2148c2ecf20Sopenharmony_ci		unsigned int data_blocks = desc->data_blocks;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci		if (pcm) {
2178c2ecf20Sopenharmony_ci			write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
2188c2ecf20Sopenharmony_ci			pcm_frames += data_blocks;
2198c2ecf20Sopenharmony_ci		} else {
2208c2ecf20Sopenharmony_ci			write_pcm_silence(s, buf, data_blocks);
2218c2ecf20Sopenharmony_ci		}
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return pcm_frames;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ciint amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
2288c2ecf20Sopenharmony_ci		    enum amdtp_stream_direction dir, unsigned int pcm_channels)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
2318c2ecf20Sopenharmony_ci	struct amdtp_tscm *p;
2328c2ecf20Sopenharmony_ci	unsigned int fmt;
2338c2ecf20Sopenharmony_ci	int err;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (dir == AMDTP_IN_STREAM) {
2368c2ecf20Sopenharmony_ci		fmt = AMDTP_FMT_TSCM_TX;
2378c2ecf20Sopenharmony_ci		process_ctx_payloads = process_ir_ctx_payloads;
2388c2ecf20Sopenharmony_ci	} else {
2398c2ecf20Sopenharmony_ci		fmt = AMDTP_FMT_TSCM_RX;
2408c2ecf20Sopenharmony_ci		process_ctx_payloads = process_it_ctx_payloads;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	err = amdtp_stream_init(s, unit, dir,
2448c2ecf20Sopenharmony_ci			CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt,
2458c2ecf20Sopenharmony_ci			process_ctx_payloads, sizeof(struct amdtp_tscm));
2468c2ecf20Sopenharmony_ci	if (err < 0)
2478c2ecf20Sopenharmony_ci		return 0;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	if (dir == AMDTP_OUT_STREAM) {
2508c2ecf20Sopenharmony_ci		// Use fixed value for FDF field.
2518c2ecf20Sopenharmony_ci		s->ctx_data.rx.fdf = 0x00;
2528c2ecf20Sopenharmony_ci		// Not used.
2538c2ecf20Sopenharmony_ci		s->ctx_data.rx.syt_override = 0x0000;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	/* This protocol uses fixed number of data channels for PCM samples. */
2578c2ecf20Sopenharmony_ci	p = s->protocol;
2588c2ecf20Sopenharmony_ci	p->pcm_channels = pcm_channels;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	return 0;
2618c2ecf20Sopenharmony_ci}
262