18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * amdtp-dot.c - a part of driver for Digidesign Digi 002/003 family
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2014-2015 Takashi Sakamoto
68c2ecf20Sopenharmony_ci * Copyright (C) 2012 Robin Gareus <robin@gareus.org>
78c2ecf20Sopenharmony_ci * Copyright (C) 2012 Damien Zammit <damien@zamaudio.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <sound/pcm.h>
118c2ecf20Sopenharmony_ci#include "digi00x.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define CIP_FMT_AM		0x10
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/* 'Clock-based rate control mode' is just supported. */
168c2ecf20Sopenharmony_ci#define AMDTP_FDF_AM824		0x00
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/*
198c2ecf20Sopenharmony_ci * Nominally 3125 bytes/second, but the MIDI port's clock might be
208c2ecf20Sopenharmony_ci * 1% too slow, and the bus clock 100 ppm too fast.
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci#define MIDI_BYTES_PER_SECOND	3093
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/*
258c2ecf20Sopenharmony_ci * Several devices look only at the first eight data blocks.
268c2ecf20Sopenharmony_ci * In any case, this is more than enough for the MIDI data rate.
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_ci#define MAX_MIDI_RX_BLOCKS	8
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/* 3 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) + 1. */
318c2ecf20Sopenharmony_ci#define MAX_MIDI_PORTS		3
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/*
348c2ecf20Sopenharmony_ci * The double-oh-three algorithm was discovered by Robin Gareus and Damien
358c2ecf20Sopenharmony_ci * Zammit in 2012, with reverse-engineering for Digi 003 Rack.
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_cistruct dot_state {
388c2ecf20Sopenharmony_ci	u8 carry;
398c2ecf20Sopenharmony_ci	u8 idx;
408c2ecf20Sopenharmony_ci	unsigned int off;
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistruct amdtp_dot {
448c2ecf20Sopenharmony_ci	unsigned int pcm_channels;
458c2ecf20Sopenharmony_ci	struct dot_state state;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	struct snd_rawmidi_substream *midi[MAX_MIDI_PORTS];
488c2ecf20Sopenharmony_ci	int midi_fifo_used[MAX_MIDI_PORTS];
498c2ecf20Sopenharmony_ci	int midi_fifo_limit;
508c2ecf20Sopenharmony_ci};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/*
538c2ecf20Sopenharmony_ci * double-oh-three look up table
548c2ecf20Sopenharmony_ci *
558c2ecf20Sopenharmony_ci * @param idx index byte (audio-sample data) 0x00..0xff
568c2ecf20Sopenharmony_ci * @param off channel offset shift
578c2ecf20Sopenharmony_ci * @return salt to XOR with given data
588c2ecf20Sopenharmony_ci */
598c2ecf20Sopenharmony_ci#define BYTE_PER_SAMPLE (4)
608c2ecf20Sopenharmony_ci#define MAGIC_DOT_BYTE (2)
618c2ecf20Sopenharmony_ci#define MAGIC_BYTE_OFF(x) (((x) * BYTE_PER_SAMPLE) + MAGIC_DOT_BYTE)
628c2ecf20Sopenharmony_cistatic u8 dot_scrt(const u8 idx, const unsigned int off)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	/*
658c2ecf20Sopenharmony_ci	 * the length of the added pattern only depends on the lower nibble
668c2ecf20Sopenharmony_ci	 * of the last non-zero data
678c2ecf20Sopenharmony_ci	 */
688c2ecf20Sopenharmony_ci	static const u8 len[16] = {0, 1, 3, 5, 7, 9, 11, 13, 14,
698c2ecf20Sopenharmony_ci				   12, 10, 8, 6, 4, 2, 0};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	/*
728c2ecf20Sopenharmony_ci	 * the lower nibble of the salt. Interleaved sequence.
738c2ecf20Sopenharmony_ci	 * this is walked backwards according to len[]
748c2ecf20Sopenharmony_ci	 */
758c2ecf20Sopenharmony_ci	static const u8 nib[15] = {0x8, 0x7, 0x9, 0x6, 0xa, 0x5, 0xb, 0x4,
768c2ecf20Sopenharmony_ci				   0xc, 0x3, 0xd, 0x2, 0xe, 0x1, 0xf};
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/* circular list for the salt's hi nibble. */
798c2ecf20Sopenharmony_ci	static const u8 hir[15] = {0x0, 0x6, 0xf, 0x8, 0x7, 0x5, 0x3, 0x4,
808c2ecf20Sopenharmony_ci				   0xc, 0xd, 0xe, 0x1, 0x2, 0xb, 0xa};
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	/*
838c2ecf20Sopenharmony_ci	 * start offset for upper nibble mapping.
848c2ecf20Sopenharmony_ci	 * note: 9 is /special/. In the case where the high nibble == 0x9,
858c2ecf20Sopenharmony_ci	 * hir[] is not used and - coincidentally - the salt's hi nibble is
868c2ecf20Sopenharmony_ci	 * 0x09 regardless of the offset.
878c2ecf20Sopenharmony_ci	 */
888c2ecf20Sopenharmony_ci	static const u8 hio[16] = {0, 11, 12, 6, 7, 5, 1, 4,
898c2ecf20Sopenharmony_ci				   3, 0x00, 14, 13, 8, 9, 10, 2};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	const u8 ln = idx & 0xf;
928c2ecf20Sopenharmony_ci	const u8 hn = (idx >> 4) & 0xf;
938c2ecf20Sopenharmony_ci	const u8 hr = (hn == 0x9) ? 0x9 : hir[(hio[hn] + off) % 15];
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (len[ln] < off)
968c2ecf20Sopenharmony_ci		return 0x00;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return ((nib[14 + off - len[ln]]) | (hr << 4));
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic void dot_encode_step(struct dot_state *state, __be32 *const buffer)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	u8 * const data = (u8 *) buffer;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (data[MAGIC_DOT_BYTE] != 0x00) {
1068c2ecf20Sopenharmony_ci		state->off = 0;
1078c2ecf20Sopenharmony_ci		state->idx = data[MAGIC_DOT_BYTE] ^ state->carry;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci	data[MAGIC_DOT_BYTE] ^= state->carry;
1108c2ecf20Sopenharmony_ci	state->carry = dot_scrt(state->idx, ++(state->off));
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ciint amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
1148c2ecf20Sopenharmony_ci			     unsigned int pcm_channels)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	struct amdtp_dot *p = s->protocol;
1178c2ecf20Sopenharmony_ci	int err;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	if (amdtp_stream_running(s))
1208c2ecf20Sopenharmony_ci		return -EBUSY;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	/*
1238c2ecf20Sopenharmony_ci	 * A first data channel is for MIDI messages, the rest is Multi Bit
1248c2ecf20Sopenharmony_ci	 * Linear Audio data channel.
1258c2ecf20Sopenharmony_ci	 */
1268c2ecf20Sopenharmony_ci	err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1);
1278c2ecf20Sopenharmony_ci	if (err < 0)
1288c2ecf20Sopenharmony_ci		return err;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	p->pcm_channels = pcm_channels;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	/*
1358c2ecf20Sopenharmony_ci	 * We do not know the actual MIDI FIFO size of most devices.  Just
1368c2ecf20Sopenharmony_ci	 * assume two bytes, i.e., one byte can be received over the bus while
1378c2ecf20Sopenharmony_ci	 * the previous one is transmitted over MIDI.
1388c2ecf20Sopenharmony_ci	 * (The value here is adjusted for midi_ratelimit_per_packet().)
1398c2ecf20Sopenharmony_ci	 */
1408c2ecf20Sopenharmony_ci	p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	return 0;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
1468c2ecf20Sopenharmony_ci			  __be32 *buffer, unsigned int frames,
1478c2ecf20Sopenharmony_ci			  unsigned int pcm_frames)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	struct amdtp_dot *p = s->protocol;
1508c2ecf20Sopenharmony_ci	unsigned int channels = p->pcm_channels;
1518c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = pcm->runtime;
1528c2ecf20Sopenharmony_ci	unsigned int pcm_buffer_pointer;
1538c2ecf20Sopenharmony_ci	int remaining_frames;
1548c2ecf20Sopenharmony_ci	const u32 *src;
1558c2ecf20Sopenharmony_ci	int i, c;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
1588c2ecf20Sopenharmony_ci	pcm_buffer_pointer %= runtime->buffer_size;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	src = (void *)runtime->dma_area +
1618c2ecf20Sopenharmony_ci				frames_to_bytes(runtime, pcm_buffer_pointer);
1628c2ecf20Sopenharmony_ci	remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	buffer++;
1658c2ecf20Sopenharmony_ci	for (i = 0; i < frames; ++i) {
1668c2ecf20Sopenharmony_ci		for (c = 0; c < channels; ++c) {
1678c2ecf20Sopenharmony_ci			buffer[c] = cpu_to_be32((*src >> 8) | 0x40000000);
1688c2ecf20Sopenharmony_ci			dot_encode_step(&p->state, &buffer[c]);
1698c2ecf20Sopenharmony_ci			src++;
1708c2ecf20Sopenharmony_ci		}
1718c2ecf20Sopenharmony_ci		buffer += s->data_block_quadlets;
1728c2ecf20Sopenharmony_ci		if (--remaining_frames == 0)
1738c2ecf20Sopenharmony_ci			src = (void *)runtime->dma_area;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
1788c2ecf20Sopenharmony_ci			 __be32 *buffer, unsigned int frames,
1798c2ecf20Sopenharmony_ci			 unsigned int pcm_frames)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct amdtp_dot *p = s->protocol;
1828c2ecf20Sopenharmony_ci	unsigned int channels = p->pcm_channels;
1838c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = pcm->runtime;
1848c2ecf20Sopenharmony_ci	unsigned int pcm_buffer_pointer;
1858c2ecf20Sopenharmony_ci	int remaining_frames;
1868c2ecf20Sopenharmony_ci	u32 *dst;
1878c2ecf20Sopenharmony_ci	int i, c;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	pcm_buffer_pointer = s->pcm_buffer_pointer + pcm_frames;
1908c2ecf20Sopenharmony_ci	pcm_buffer_pointer %= runtime->buffer_size;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	dst  = (void *)runtime->dma_area +
1938c2ecf20Sopenharmony_ci				frames_to_bytes(runtime, pcm_buffer_pointer);
1948c2ecf20Sopenharmony_ci	remaining_frames = runtime->buffer_size - pcm_buffer_pointer;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	buffer++;
1978c2ecf20Sopenharmony_ci	for (i = 0; i < frames; ++i) {
1988c2ecf20Sopenharmony_ci		for (c = 0; c < channels; ++c) {
1998c2ecf20Sopenharmony_ci			*dst = be32_to_cpu(buffer[c]) << 8;
2008c2ecf20Sopenharmony_ci			dst++;
2018c2ecf20Sopenharmony_ci		}
2028c2ecf20Sopenharmony_ci		buffer += s->data_block_quadlets;
2038c2ecf20Sopenharmony_ci		if (--remaining_frames == 0)
2048c2ecf20Sopenharmony_ci			dst = (void *)runtime->dma_area;
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
2098c2ecf20Sopenharmony_ci			      unsigned int data_blocks)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct amdtp_dot *p = s->protocol;
2128c2ecf20Sopenharmony_ci	unsigned int channels, i, c;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	channels = p->pcm_channels;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	buffer++;
2178c2ecf20Sopenharmony_ci	for (i = 0; i < data_blocks; ++i) {
2188c2ecf20Sopenharmony_ci		for (c = 0; c < channels; ++c)
2198c2ecf20Sopenharmony_ci			buffer[c] = cpu_to_be32(0x40000000);
2208c2ecf20Sopenharmony_ci		buffer += s->data_block_quadlets;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	struct amdtp_dot *p = s->protocol;
2278c2ecf20Sopenharmony_ci	int used;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	used = p->midi_fifo_used[port];
2308c2ecf20Sopenharmony_ci	if (used == 0)
2318c2ecf20Sopenharmony_ci		return true;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
2348c2ecf20Sopenharmony_ci	used = max(used, 0);
2358c2ecf20Sopenharmony_ci	p->midi_fifo_used[port] = used;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	return used < p->midi_fifo_limit;
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic inline void midi_use_bytes(struct amdtp_stream *s,
2418c2ecf20Sopenharmony_ci				  unsigned int port, unsigned int count)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	struct amdtp_dot *p = s->protocol;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	p->midi_fifo_used[port] += amdtp_rate_table[s->sfc] * count;
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
2498c2ecf20Sopenharmony_ci		unsigned int data_blocks, unsigned int data_block_counter)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct amdtp_dot *p = s->protocol;
2528c2ecf20Sopenharmony_ci	unsigned int f, port;
2538c2ecf20Sopenharmony_ci	int len;
2548c2ecf20Sopenharmony_ci	u8 *b;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	for (f = 0; f < data_blocks; f++) {
2578c2ecf20Sopenharmony_ci		port = (data_block_counter + f) % 8;
2588c2ecf20Sopenharmony_ci		b = (u8 *)&buffer[0];
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		len = 0;
2618c2ecf20Sopenharmony_ci		if (port < MAX_MIDI_PORTS &&
2628c2ecf20Sopenharmony_ci		    midi_ratelimit_per_packet(s, port) &&
2638c2ecf20Sopenharmony_ci		    p->midi[port] != NULL)
2648c2ecf20Sopenharmony_ci			len = snd_rawmidi_transmit(p->midi[port], b + 1, 2);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci		if (len > 0) {
2678c2ecf20Sopenharmony_ci			/*
2688c2ecf20Sopenharmony_ci			 * Upper 4 bits of LSB represent port number.
2698c2ecf20Sopenharmony_ci			 * - 0000b: physical MIDI port 1.
2708c2ecf20Sopenharmony_ci			 * - 0010b: physical MIDI port 2.
2718c2ecf20Sopenharmony_ci			 * - 1110b: console MIDI port.
2728c2ecf20Sopenharmony_ci			 */
2738c2ecf20Sopenharmony_ci			if (port == 2)
2748c2ecf20Sopenharmony_ci				b[3] = 0xe0;
2758c2ecf20Sopenharmony_ci			else if (port == 1)
2768c2ecf20Sopenharmony_ci				b[3] = 0x20;
2778c2ecf20Sopenharmony_ci			else
2788c2ecf20Sopenharmony_ci				b[3] = 0x00;
2798c2ecf20Sopenharmony_ci			b[3] |= len;
2808c2ecf20Sopenharmony_ci			midi_use_bytes(s, port, len);
2818c2ecf20Sopenharmony_ci		} else {
2828c2ecf20Sopenharmony_ci			b[1] = 0;
2838c2ecf20Sopenharmony_ci			b[2] = 0;
2848c2ecf20Sopenharmony_ci			b[3] = 0;
2858c2ecf20Sopenharmony_ci		}
2868c2ecf20Sopenharmony_ci		b[0] = 0x80;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		buffer += s->data_block_quadlets;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
2938c2ecf20Sopenharmony_ci			       unsigned int data_blocks)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	struct amdtp_dot *p = s->protocol;
2968c2ecf20Sopenharmony_ci	unsigned int f, port, len;
2978c2ecf20Sopenharmony_ci	u8 *b;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	for (f = 0; f < data_blocks; f++) {
3008c2ecf20Sopenharmony_ci		b = (u8 *)&buffer[0];
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci		len = b[3] & 0x0f;
3038c2ecf20Sopenharmony_ci		if (len > 0) {
3048c2ecf20Sopenharmony_ci			/*
3058c2ecf20Sopenharmony_ci			 * Upper 4 bits of LSB represent port number.
3068c2ecf20Sopenharmony_ci			 * - 0000b: physical MIDI port 1. Use port 0.
3078c2ecf20Sopenharmony_ci			 * - 1110b: console MIDI port. Use port 2.
3088c2ecf20Sopenharmony_ci			 */
3098c2ecf20Sopenharmony_ci			if (b[3] >> 4 > 0)
3108c2ecf20Sopenharmony_ci				port = 2;
3118c2ecf20Sopenharmony_ci			else
3128c2ecf20Sopenharmony_ci				port = 0;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci			if (port < MAX_MIDI_PORTS && p->midi[port])
3158c2ecf20Sopenharmony_ci				snd_rawmidi_receive(p->midi[port], b + 1, len);
3168c2ecf20Sopenharmony_ci		}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci		buffer += s->data_block_quadlets;
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ciint amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
3238c2ecf20Sopenharmony_ci				     struct snd_pcm_runtime *runtime)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	int err;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	/* This protocol delivers 24 bit data in 32bit data channel. */
3288c2ecf20Sopenharmony_ci	err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
3298c2ecf20Sopenharmony_ci	if (err < 0)
3308c2ecf20Sopenharmony_ci		return err;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return amdtp_stream_add_pcm_hw_constraints(s, runtime);
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_civoid amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
3368c2ecf20Sopenharmony_ci			  struct snd_rawmidi_substream *midi)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct amdtp_dot *p = s->protocol;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	if (port < MAX_MIDI_PORTS)
3418c2ecf20Sopenharmony_ci		WRITE_ONCE(p->midi[port], midi);
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
3458c2ecf20Sopenharmony_ci					    const struct pkt_desc *descs,
3468c2ecf20Sopenharmony_ci					    unsigned int packets,
3478c2ecf20Sopenharmony_ci					    struct snd_pcm_substream *pcm)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	unsigned int pcm_frames = 0;
3508c2ecf20Sopenharmony_ci	int i;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	for (i = 0; i < packets; ++i) {
3538c2ecf20Sopenharmony_ci		const struct pkt_desc *desc = descs + i;
3548c2ecf20Sopenharmony_ci		__be32 *buf = desc->ctx_payload;
3558c2ecf20Sopenharmony_ci		unsigned int data_blocks = desc->data_blocks;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci		if (pcm) {
3588c2ecf20Sopenharmony_ci			read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
3598c2ecf20Sopenharmony_ci			pcm_frames += data_blocks;
3608c2ecf20Sopenharmony_ci		}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci		read_midi_messages(s, buf, data_blocks);
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	return pcm_frames;
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
3698c2ecf20Sopenharmony_ci					    const struct pkt_desc *descs,
3708c2ecf20Sopenharmony_ci					    unsigned int packets,
3718c2ecf20Sopenharmony_ci					    struct snd_pcm_substream *pcm)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	unsigned int pcm_frames = 0;
3748c2ecf20Sopenharmony_ci	int i;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	for (i = 0; i < packets; ++i) {
3778c2ecf20Sopenharmony_ci		const struct pkt_desc *desc = descs + i;
3788c2ecf20Sopenharmony_ci		__be32 *buf = desc->ctx_payload;
3798c2ecf20Sopenharmony_ci		unsigned int data_blocks = desc->data_blocks;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci		if (pcm) {
3828c2ecf20Sopenharmony_ci			write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
3838c2ecf20Sopenharmony_ci			pcm_frames += data_blocks;
3848c2ecf20Sopenharmony_ci		} else {
3858c2ecf20Sopenharmony_ci			write_pcm_silence(s, buf, data_blocks);
3868c2ecf20Sopenharmony_ci		}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci		write_midi_messages(s, buf, data_blocks,
3898c2ecf20Sopenharmony_ci				    desc->data_block_counter);
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	return pcm_frames;
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ciint amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
3968c2ecf20Sopenharmony_ci		 enum amdtp_stream_direction dir)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
3998c2ecf20Sopenharmony_ci	enum cip_flags flags;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	// Use different mode between incoming/outgoing.
4028c2ecf20Sopenharmony_ci	if (dir == AMDTP_IN_STREAM) {
4038c2ecf20Sopenharmony_ci		flags = CIP_NONBLOCKING;
4048c2ecf20Sopenharmony_ci		process_ctx_payloads = process_ir_ctx_payloads;
4058c2ecf20Sopenharmony_ci	} else {
4068c2ecf20Sopenharmony_ci		flags = CIP_BLOCKING;
4078c2ecf20Sopenharmony_ci		process_ctx_payloads = process_it_ctx_payloads;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
4118c2ecf20Sopenharmony_ci				process_ctx_payloads, sizeof(struct amdtp_dot));
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_civoid amdtp_dot_reset(struct amdtp_stream *s)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	struct amdtp_dot *p = s->protocol;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	p->state.carry = 0x00;
4198c2ecf20Sopenharmony_ci	p->state.idx = 0x00;
4208c2ecf20Sopenharmony_ci	p->state.off = 0;
4218c2ecf20Sopenharmony_ci}
422