162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci// ff-protocol-latter.c - a part of driver for RME Fireface series
362306a36Sopenharmony_ci//
462306a36Sopenharmony_ci// Copyright (c) 2019 Takashi Sakamoto
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/delay.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "ff.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define LATTER_STF		0xffff00000004ULL
1162306a36Sopenharmony_ci#define LATTER_ISOC_CHANNELS	0xffff00000008ULL
1262306a36Sopenharmony_ci#define LATTER_ISOC_START	0xffff0000000cULL
1362306a36Sopenharmony_ci#define LATTER_FETCH_MODE	0xffff00000010ULL
1462306a36Sopenharmony_ci#define LATTER_SYNC_STATUS	0x0000801c0000ULL
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci// The content of sync status register differs between models.
1762306a36Sopenharmony_ci//
1862306a36Sopenharmony_ci// Fireface UCX:
1962306a36Sopenharmony_ci//  0xf0000000: (unidentified)
2062306a36Sopenharmony_ci//  0x0f000000: effective rate of sampling clock
2162306a36Sopenharmony_ci//  0x00f00000: detected rate of word clock on BNC interface
2262306a36Sopenharmony_ci//  0x000f0000: detected rate of ADAT or S/PDIF on optical interface
2362306a36Sopenharmony_ci//  0x0000f000: detected rate of S/PDIF on coaxial interface
2462306a36Sopenharmony_ci//  0x00000e00: effective source of sampling clock
2562306a36Sopenharmony_ci//    0x00000e00: Internal
2662306a36Sopenharmony_ci//    0x00000800: (unidentified)
2762306a36Sopenharmony_ci//    0x00000600: Word clock on BNC interface
2862306a36Sopenharmony_ci//    0x00000400: ADAT on optical interface
2962306a36Sopenharmony_ci//    0x00000200: S/PDIF on coaxial or optical interface
3062306a36Sopenharmony_ci//  0x00000100: Optical interface is used for ADAT signal
3162306a36Sopenharmony_ci//  0x00000080: (unidentified)
3262306a36Sopenharmony_ci//  0x00000040: Synchronized to word clock on BNC interface
3362306a36Sopenharmony_ci//  0x00000020: Synchronized to ADAT or S/PDIF on optical interface
3462306a36Sopenharmony_ci//  0x00000010: Synchronized to S/PDIF on coaxial interface
3562306a36Sopenharmony_ci//  0x00000008: (unidentified)
3662306a36Sopenharmony_ci//  0x00000004: Lock word clock on BNC interface
3762306a36Sopenharmony_ci//  0x00000002: Lock ADAT or S/PDIF on optical interface
3862306a36Sopenharmony_ci//  0x00000001: Lock S/PDIF on coaxial interface
3962306a36Sopenharmony_ci//
4062306a36Sopenharmony_ci// Fireface 802 (and perhaps UFX):
4162306a36Sopenharmony_ci//   0xf0000000: effective rate of sampling clock
4262306a36Sopenharmony_ci//   0x0f000000: detected rate of ADAT-B on 2nd optical interface
4362306a36Sopenharmony_ci//   0x00f00000: detected rate of ADAT-A on 1st optical interface
4462306a36Sopenharmony_ci//   0x000f0000: detected rate of AES/EBU on XLR or coaxial interface
4562306a36Sopenharmony_ci//   0x0000f000: detected rate of word clock on BNC interface
4662306a36Sopenharmony_ci//   0x00000e00: effective source of sampling clock
4762306a36Sopenharmony_ci//     0x00000e00: internal
4862306a36Sopenharmony_ci//     0x00000800: ADAT-B
4962306a36Sopenharmony_ci//     0x00000600: ADAT-A
5062306a36Sopenharmony_ci//     0x00000400: AES/EBU
5162306a36Sopenharmony_ci//     0x00000200: Word clock
5262306a36Sopenharmony_ci//   0x00000080: Synchronized to ADAT-B on 2nd optical interface
5362306a36Sopenharmony_ci//   0x00000040: Synchronized to ADAT-A on 1st optical interface
5462306a36Sopenharmony_ci//   0x00000020: Synchronized to AES/EBU on XLR or 2nd optical interface
5562306a36Sopenharmony_ci//   0x00000010: Synchronized to word clock on BNC interface
5662306a36Sopenharmony_ci//   0x00000008: Lock ADAT-B on 2nd optical interface
5762306a36Sopenharmony_ci//   0x00000004: Lock ADAT-A on 1st optical interface
5862306a36Sopenharmony_ci//   0x00000002: Lock AES/EBU on XLR or 2nd optical interface
5962306a36Sopenharmony_ci//   0x00000001: Lock word clock on BNC interface
6062306a36Sopenharmony_ci//
6162306a36Sopenharmony_ci// The pattern for rate bits:
6262306a36Sopenharmony_ci//   0x00: 32.0 kHz
6362306a36Sopenharmony_ci//   0x01: 44.1 kHz
6462306a36Sopenharmony_ci//   0x02: 48.0 kHz
6562306a36Sopenharmony_ci//   0x04: 64.0 kHz
6662306a36Sopenharmony_ci//   0x05: 88.2 kHz
6762306a36Sopenharmony_ci//   0x06: 96.0 kHz
6862306a36Sopenharmony_ci//   0x08: 128.0 kHz
6962306a36Sopenharmony_ci//   0x09: 176.4 kHz
7062306a36Sopenharmony_ci//   0x0a: 192.0 kHz
7162306a36Sopenharmony_cistatic int parse_clock_bits(u32 data, unsigned int *rate,
7262306a36Sopenharmony_ci			    enum snd_ff_clock_src *src,
7362306a36Sopenharmony_ci			    enum snd_ff_unit_version unit_version)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	static const struct {
7662306a36Sopenharmony_ci		unsigned int rate;
7762306a36Sopenharmony_ci		u32 flag;
7862306a36Sopenharmony_ci	} *rate_entry, rate_entries[] = {
7962306a36Sopenharmony_ci		{ 32000,	0x00, },
8062306a36Sopenharmony_ci		{ 44100,	0x01, },
8162306a36Sopenharmony_ci		{ 48000,	0x02, },
8262306a36Sopenharmony_ci		{ 64000,	0x04, },
8362306a36Sopenharmony_ci		{ 88200,	0x05, },
8462306a36Sopenharmony_ci		{ 96000,	0x06, },
8562306a36Sopenharmony_ci		{ 128000,	0x08, },
8662306a36Sopenharmony_ci		{ 176400,	0x09, },
8762306a36Sopenharmony_ci		{ 192000,	0x0a, },
8862306a36Sopenharmony_ci	};
8962306a36Sopenharmony_ci	static const struct {
9062306a36Sopenharmony_ci		enum snd_ff_clock_src src;
9162306a36Sopenharmony_ci		u32 flag;
9262306a36Sopenharmony_ci	} *clk_entry, *clk_entries, ucx_clk_entries[] = {
9362306a36Sopenharmony_ci		{ SND_FF_CLOCK_SRC_SPDIF,	0x00000200, },
9462306a36Sopenharmony_ci		{ SND_FF_CLOCK_SRC_ADAT1,	0x00000400, },
9562306a36Sopenharmony_ci		{ SND_FF_CLOCK_SRC_WORD,	0x00000600, },
9662306a36Sopenharmony_ci		{ SND_FF_CLOCK_SRC_INTERNAL,	0x00000e00, },
9762306a36Sopenharmony_ci	}, ufx_ff802_clk_entries[] = {
9862306a36Sopenharmony_ci		{ SND_FF_CLOCK_SRC_WORD,	0x00000200, },
9962306a36Sopenharmony_ci		{ SND_FF_CLOCK_SRC_SPDIF,	0x00000400, },
10062306a36Sopenharmony_ci		{ SND_FF_CLOCK_SRC_ADAT1,	0x00000600, },
10162306a36Sopenharmony_ci		{ SND_FF_CLOCK_SRC_ADAT2,	0x00000800, },
10262306a36Sopenharmony_ci		{ SND_FF_CLOCK_SRC_INTERNAL,	0x00000e00, },
10362306a36Sopenharmony_ci	};
10462306a36Sopenharmony_ci	u32 rate_bits;
10562306a36Sopenharmony_ci	unsigned int clk_entry_count;
10662306a36Sopenharmony_ci	int i;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if (unit_version == SND_FF_UNIT_VERSION_UCX) {
10962306a36Sopenharmony_ci		rate_bits = (data & 0x0f000000) >> 24;
11062306a36Sopenharmony_ci		clk_entries = ucx_clk_entries;
11162306a36Sopenharmony_ci		clk_entry_count = ARRAY_SIZE(ucx_clk_entries);
11262306a36Sopenharmony_ci	} else {
11362306a36Sopenharmony_ci		rate_bits = (data & 0xf0000000) >> 28;
11462306a36Sopenharmony_ci		clk_entries = ufx_ff802_clk_entries;
11562306a36Sopenharmony_ci		clk_entry_count = ARRAY_SIZE(ufx_ff802_clk_entries);
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) {
11962306a36Sopenharmony_ci		rate_entry = rate_entries + i;
12062306a36Sopenharmony_ci		if (rate_bits == rate_entry->flag) {
12162306a36Sopenharmony_ci			*rate = rate_entry->rate;
12262306a36Sopenharmony_ci			break;
12362306a36Sopenharmony_ci		}
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci	if (i == ARRAY_SIZE(rate_entries))
12662306a36Sopenharmony_ci		return -EIO;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	for (i = 0; i < clk_entry_count; ++i) {
12962306a36Sopenharmony_ci		clk_entry = clk_entries + i;
13062306a36Sopenharmony_ci		if ((data & 0x000e00) == clk_entry->flag) {
13162306a36Sopenharmony_ci			*src = clk_entry->src;
13262306a36Sopenharmony_ci			break;
13362306a36Sopenharmony_ci		}
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci	if (i == clk_entry_count)
13662306a36Sopenharmony_ci		return -EIO;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic int latter_get_clock(struct snd_ff *ff, unsigned int *rate,
14262306a36Sopenharmony_ci			   enum snd_ff_clock_src *src)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	__le32 reg;
14562306a36Sopenharmony_ci	u32 data;
14662306a36Sopenharmony_ci	int err;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
14962306a36Sopenharmony_ci				 LATTER_SYNC_STATUS, &reg, sizeof(reg), 0);
15062306a36Sopenharmony_ci	if (err < 0)
15162306a36Sopenharmony_ci		return err;
15262306a36Sopenharmony_ci	data = le32_to_cpu(reg);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return parse_clock_bits(data, rate, src, ff->unit_version);
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic int latter_switch_fetching_mode(struct snd_ff *ff, bool enable)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	u32 data;
16062306a36Sopenharmony_ci	__le32 reg;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (enable)
16362306a36Sopenharmony_ci		data = 0x00000000;
16462306a36Sopenharmony_ci	else
16562306a36Sopenharmony_ci		data = 0xffffffff;
16662306a36Sopenharmony_ci	reg = cpu_to_le32(data);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
16962306a36Sopenharmony_ci				  LATTER_FETCH_MODE, &reg, sizeof(reg), 0);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic int latter_allocate_resources(struct snd_ff *ff, unsigned int rate)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	enum snd_ff_stream_mode mode;
17562306a36Sopenharmony_ci	unsigned int code;
17662306a36Sopenharmony_ci	__le32 reg;
17762306a36Sopenharmony_ci	unsigned int count;
17862306a36Sopenharmony_ci	int i;
17962306a36Sopenharmony_ci	int err;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	// Set the number of data blocks transferred in a second.
18262306a36Sopenharmony_ci	if (rate % 48000 == 0)
18362306a36Sopenharmony_ci		code = 0x04;
18462306a36Sopenharmony_ci	else if (rate % 44100 == 0)
18562306a36Sopenharmony_ci		code = 0x02;
18662306a36Sopenharmony_ci	else if (rate % 32000 == 0)
18762306a36Sopenharmony_ci		code = 0x00;
18862306a36Sopenharmony_ci	else
18962306a36Sopenharmony_ci		return -EINVAL;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (rate >= 64000 && rate < 128000)
19262306a36Sopenharmony_ci		code |= 0x08;
19362306a36Sopenharmony_ci	else if (rate >= 128000)
19462306a36Sopenharmony_ci		code |= 0x10;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	reg = cpu_to_le32(code);
19762306a36Sopenharmony_ci	err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
19862306a36Sopenharmony_ci				 LATTER_STF, &reg, sizeof(reg), 0);
19962306a36Sopenharmony_ci	if (err < 0)
20062306a36Sopenharmony_ci		return err;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	// Confirm to shift transmission clock.
20362306a36Sopenharmony_ci	count = 0;
20462306a36Sopenharmony_ci	while (count++ < 10) {
20562306a36Sopenharmony_ci		unsigned int curr_rate;
20662306a36Sopenharmony_ci		enum snd_ff_clock_src src;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci		err = latter_get_clock(ff, &curr_rate, &src);
20962306a36Sopenharmony_ci		if (err < 0)
21062306a36Sopenharmony_ci			return err;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci		if (curr_rate == rate)
21362306a36Sopenharmony_ci			break;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci	if (count > 10)
21662306a36Sopenharmony_ci		return -ETIMEDOUT;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); ++i) {
21962306a36Sopenharmony_ci		if (rate == amdtp_rate_table[i])
22062306a36Sopenharmony_ci			break;
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci	if (i == ARRAY_SIZE(amdtp_rate_table))
22362306a36Sopenharmony_ci		return -EINVAL;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	err = snd_ff_stream_get_multiplier_mode(i, &mode);
22662306a36Sopenharmony_ci	if (err < 0)
22762306a36Sopenharmony_ci		return err;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	// Keep resources for in-stream.
23062306a36Sopenharmony_ci	ff->tx_resources.channels_mask = 0x00000000000000ffuLL;
23162306a36Sopenharmony_ci	err = fw_iso_resources_allocate(&ff->tx_resources,
23262306a36Sopenharmony_ci			amdtp_stream_get_max_payload(&ff->tx_stream),
23362306a36Sopenharmony_ci			fw_parent_device(ff->unit)->max_speed);
23462306a36Sopenharmony_ci	if (err < 0)
23562306a36Sopenharmony_ci		return err;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	// Keep resources for out-stream.
23862306a36Sopenharmony_ci	ff->rx_resources.channels_mask = 0x00000000000000ffuLL;
23962306a36Sopenharmony_ci	err = fw_iso_resources_allocate(&ff->rx_resources,
24062306a36Sopenharmony_ci			amdtp_stream_get_max_payload(&ff->rx_stream),
24162306a36Sopenharmony_ci			fw_parent_device(ff->unit)->max_speed);
24262306a36Sopenharmony_ci	if (err < 0)
24362306a36Sopenharmony_ci		fw_iso_resources_free(&ff->tx_resources);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	return err;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic int latter_begin_session(struct snd_ff *ff, unsigned int rate)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	unsigned int generation = ff->rx_resources.generation;
25162306a36Sopenharmony_ci	unsigned int flag;
25262306a36Sopenharmony_ci	u32 data;
25362306a36Sopenharmony_ci	__le32 reg;
25462306a36Sopenharmony_ci	int err;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (ff->unit_version == SND_FF_UNIT_VERSION_UCX) {
25762306a36Sopenharmony_ci		// For Fireface UCX. Always use the maximum number of data
25862306a36Sopenharmony_ci		// channels in data block of packet.
25962306a36Sopenharmony_ci		if (rate >= 32000 && rate <= 48000)
26062306a36Sopenharmony_ci			flag = 0x92;
26162306a36Sopenharmony_ci		else if (rate >= 64000 && rate <= 96000)
26262306a36Sopenharmony_ci			flag = 0x8e;
26362306a36Sopenharmony_ci		else if (rate >= 128000 && rate <= 192000)
26462306a36Sopenharmony_ci			flag = 0x8c;
26562306a36Sopenharmony_ci		else
26662306a36Sopenharmony_ci			return -EINVAL;
26762306a36Sopenharmony_ci	} else {
26862306a36Sopenharmony_ci		// For Fireface UFX and 802. Due to bandwidth limitation on
26962306a36Sopenharmony_ci		// IEEE 1394a (400 Mbps), Analog 1-12 and AES are available
27062306a36Sopenharmony_ci		// without any ADAT at quadruple speed.
27162306a36Sopenharmony_ci		if (rate >= 32000 && rate <= 48000)
27262306a36Sopenharmony_ci			flag = 0x9e;
27362306a36Sopenharmony_ci		else if (rate >= 64000 && rate <= 96000)
27462306a36Sopenharmony_ci			flag = 0x96;
27562306a36Sopenharmony_ci		else if (rate >= 128000 && rate <= 192000)
27662306a36Sopenharmony_ci			flag = 0x8e;
27762306a36Sopenharmony_ci		else
27862306a36Sopenharmony_ci			return -EINVAL;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (generation != fw_parent_device(ff->unit)->card->generation) {
28262306a36Sopenharmony_ci		err = fw_iso_resources_update(&ff->tx_resources);
28362306a36Sopenharmony_ci		if (err < 0)
28462306a36Sopenharmony_ci			return err;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci		err = fw_iso_resources_update(&ff->rx_resources);
28762306a36Sopenharmony_ci		if (err < 0)
28862306a36Sopenharmony_ci			return err;
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	data = (ff->tx_resources.channel << 8) | ff->rx_resources.channel;
29262306a36Sopenharmony_ci	reg = cpu_to_le32(data);
29362306a36Sopenharmony_ci	err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
29462306a36Sopenharmony_ci				 LATTER_ISOC_CHANNELS, &reg, sizeof(reg), 0);
29562306a36Sopenharmony_ci	if (err < 0)
29662306a36Sopenharmony_ci		return err;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	reg = cpu_to_le32(flag);
29962306a36Sopenharmony_ci	return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
30062306a36Sopenharmony_ci				  LATTER_ISOC_START, &reg, sizeof(reg), 0);
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic void latter_finish_session(struct snd_ff *ff)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	__le32 reg;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	reg = cpu_to_le32(0x00000000);
30862306a36Sopenharmony_ci	snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
30962306a36Sopenharmony_ci			   LATTER_ISOC_START, &reg, sizeof(reg), 0);
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	static const struct {
31562306a36Sopenharmony_ci		char *const label;
31662306a36Sopenharmony_ci		u32 locked_mask;
31762306a36Sopenharmony_ci		u32 synced_mask;
31862306a36Sopenharmony_ci	} *clk_entry, *clk_entries, ucx_clk_entries[] = {
31962306a36Sopenharmony_ci		{ "S/PDIF",	0x00000001, 0x00000010, },
32062306a36Sopenharmony_ci		{ "ADAT",	0x00000002, 0x00000020, },
32162306a36Sopenharmony_ci		{ "WDClk",	0x00000004, 0x00000040, },
32262306a36Sopenharmony_ci	}, ufx_ff802_clk_entries[] = {
32362306a36Sopenharmony_ci		{ "WDClk",	0x00000001, 0x00000010, },
32462306a36Sopenharmony_ci		{ "AES/EBU",	0x00000002, 0x00000020, },
32562306a36Sopenharmony_ci		{ "ADAT-A",	0x00000004, 0x00000040, },
32662306a36Sopenharmony_ci		{ "ADAT-B",	0x00000008, 0x00000080, },
32762306a36Sopenharmony_ci	};
32862306a36Sopenharmony_ci	__le32 reg;
32962306a36Sopenharmony_ci	u32 data;
33062306a36Sopenharmony_ci	unsigned int rate;
33162306a36Sopenharmony_ci	enum snd_ff_clock_src src;
33262306a36Sopenharmony_ci	const char *label;
33362306a36Sopenharmony_ci	unsigned int clk_entry_count;
33462306a36Sopenharmony_ci	int i;
33562306a36Sopenharmony_ci	int err;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
33862306a36Sopenharmony_ci				 LATTER_SYNC_STATUS, &reg, sizeof(reg), 0);
33962306a36Sopenharmony_ci	if (err < 0)
34062306a36Sopenharmony_ci		return;
34162306a36Sopenharmony_ci	data = le32_to_cpu(reg);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	snd_iprintf(buffer, "External source detection:\n");
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	if (ff->unit_version == SND_FF_UNIT_VERSION_UCX) {
34662306a36Sopenharmony_ci		clk_entries = ucx_clk_entries;
34762306a36Sopenharmony_ci		clk_entry_count = ARRAY_SIZE(ucx_clk_entries);
34862306a36Sopenharmony_ci	} else {
34962306a36Sopenharmony_ci		clk_entries = ufx_ff802_clk_entries;
35062306a36Sopenharmony_ci		clk_entry_count = ARRAY_SIZE(ufx_ff802_clk_entries);
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	for (i = 0; i < clk_entry_count; ++i) {
35462306a36Sopenharmony_ci		clk_entry = clk_entries + i;
35562306a36Sopenharmony_ci		snd_iprintf(buffer, "%s: ", clk_entry->label);
35662306a36Sopenharmony_ci		if (data & clk_entry->locked_mask) {
35762306a36Sopenharmony_ci			if (data & clk_entry->synced_mask)
35862306a36Sopenharmony_ci				snd_iprintf(buffer, "sync\n");
35962306a36Sopenharmony_ci			else
36062306a36Sopenharmony_ci				snd_iprintf(buffer, "lock\n");
36162306a36Sopenharmony_ci		} else {
36262306a36Sopenharmony_ci			snd_iprintf(buffer, "none\n");
36362306a36Sopenharmony_ci		}
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	err = parse_clock_bits(data, &rate, &src, ff->unit_version);
36762306a36Sopenharmony_ci	if (err < 0)
36862306a36Sopenharmony_ci		return;
36962306a36Sopenharmony_ci	label = snd_ff_proc_get_clk_label(src);
37062306a36Sopenharmony_ci	if (!label)
37162306a36Sopenharmony_ci		return;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	snd_iprintf(buffer, "Referred clock: %s %d\n", label, rate);
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci// NOTE: transactions are transferred within 0x00-0x7f in allocated range of
37762306a36Sopenharmony_ci// address. This seems to be for check of discontinuity in receiver side.
37862306a36Sopenharmony_ci//
37962306a36Sopenharmony_ci// Like Fireface 400, drivers can select one of 4 options for lower 4 bytes of
38062306a36Sopenharmony_ci// destination address by bit flags in quadlet register (little endian) at
38162306a36Sopenharmony_ci// 0x'ffff'0000'0014:
38262306a36Sopenharmony_ci//
38362306a36Sopenharmony_ci// bit flags: offset of destination address
38462306a36Sopenharmony_ci// - 0x00002000: 0x'....'....'0000'0000
38562306a36Sopenharmony_ci// - 0x00004000: 0x'....'....'0000'0080
38662306a36Sopenharmony_ci// - 0x00008000: 0x'....'....'0000'0100
38762306a36Sopenharmony_ci// - 0x00010000: 0x'....'....'0000'0180
38862306a36Sopenharmony_ci//
38962306a36Sopenharmony_ci// Drivers can suppress the device to transfer asynchronous transactions by
39062306a36Sopenharmony_ci// clear these bit flags.
39162306a36Sopenharmony_ci//
39262306a36Sopenharmony_ci// Actually, the register is write-only and includes the other settings such as
39362306a36Sopenharmony_ci// input attenuation. This driver allocates for the first option
39462306a36Sopenharmony_ci// (0x'....'....'0000'0000) and expects userspace application to configure the
39562306a36Sopenharmony_ci// register for it.
39662306a36Sopenharmony_cistatic void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
39762306a36Sopenharmony_ci				   size_t length, u32 tstamp)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	u32 data = le32_to_cpu(*buf);
40062306a36Sopenharmony_ci	unsigned int index = (data & 0x000000f0) >> 4;
40162306a36Sopenharmony_ci	u8 byte[3];
40262306a36Sopenharmony_ci	struct snd_rawmidi_substream *substream;
40362306a36Sopenharmony_ci	unsigned int len;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (index >= ff->spec->midi_in_ports)
40662306a36Sopenharmony_ci		return;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	switch (data & 0x0000000f) {
40962306a36Sopenharmony_ci	case 0x00000008:
41062306a36Sopenharmony_ci	case 0x00000009:
41162306a36Sopenharmony_ci	case 0x0000000a:
41262306a36Sopenharmony_ci	case 0x0000000b:
41362306a36Sopenharmony_ci	case 0x0000000e:
41462306a36Sopenharmony_ci		len = 3;
41562306a36Sopenharmony_ci		break;
41662306a36Sopenharmony_ci	case 0x0000000c:
41762306a36Sopenharmony_ci	case 0x0000000d:
41862306a36Sopenharmony_ci		len = 2;
41962306a36Sopenharmony_ci		break;
42062306a36Sopenharmony_ci	default:
42162306a36Sopenharmony_ci		len = data & 0x00000003;
42262306a36Sopenharmony_ci		if (len == 0)
42362306a36Sopenharmony_ci			len = 3;
42462306a36Sopenharmony_ci		break;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	byte[0] = (data & 0x0000ff00) >> 8;
42862306a36Sopenharmony_ci	byte[1] = (data & 0x00ff0000) >> 16;
42962306a36Sopenharmony_ci	byte[2] = (data & 0xff000000) >> 24;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	substream = READ_ONCE(ff->tx_midi_substreams[index]);
43262306a36Sopenharmony_ci	if (substream)
43362306a36Sopenharmony_ci		snd_rawmidi_receive(substream, byte, len);
43462306a36Sopenharmony_ci}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci/*
43762306a36Sopenharmony_ci * When return minus value, given argument is not MIDI status.
43862306a36Sopenharmony_ci * When return 0, given argument is a beginning of system exclusive.
43962306a36Sopenharmony_ci * When return the others, given argument is MIDI data.
44062306a36Sopenharmony_ci */
44162306a36Sopenharmony_cistatic inline int calculate_message_bytes(u8 status)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	switch (status) {
44462306a36Sopenharmony_ci	case 0xf6:	/* Tune request. */
44562306a36Sopenharmony_ci	case 0xf8:	/* Timing clock. */
44662306a36Sopenharmony_ci	case 0xfa:	/* Start. */
44762306a36Sopenharmony_ci	case 0xfb:	/* Continue. */
44862306a36Sopenharmony_ci	case 0xfc:	/* Stop. */
44962306a36Sopenharmony_ci	case 0xfe:	/* Active sensing. */
45062306a36Sopenharmony_ci	case 0xff:	/* System reset. */
45162306a36Sopenharmony_ci		return 1;
45262306a36Sopenharmony_ci	case 0xf1:	/* MIDI time code quarter frame. */
45362306a36Sopenharmony_ci	case 0xf3:	/* Song select. */
45462306a36Sopenharmony_ci		return 2;
45562306a36Sopenharmony_ci	case 0xf2:	/* Song position pointer. */
45662306a36Sopenharmony_ci		return 3;
45762306a36Sopenharmony_ci	case 0xf0:	/* Exclusive. */
45862306a36Sopenharmony_ci		return 0;
45962306a36Sopenharmony_ci	case 0xf7:	/* End of exclusive. */
46062306a36Sopenharmony_ci		break;
46162306a36Sopenharmony_ci	case 0xf4:	/* Undefined. */
46262306a36Sopenharmony_ci	case 0xf5:	/* Undefined. */
46362306a36Sopenharmony_ci	case 0xf9:	/* Undefined. */
46462306a36Sopenharmony_ci	case 0xfd:	/* Undefined. */
46562306a36Sopenharmony_ci		break;
46662306a36Sopenharmony_ci	default:
46762306a36Sopenharmony_ci		switch (status & 0xf0) {
46862306a36Sopenharmony_ci		case 0x80:	/* Note on. */
46962306a36Sopenharmony_ci		case 0x90:	/* Note off. */
47062306a36Sopenharmony_ci		case 0xa0:	/* Polyphonic key pressure. */
47162306a36Sopenharmony_ci		case 0xb0:	/* Control change and Mode change. */
47262306a36Sopenharmony_ci		case 0xe0:	/* Pitch bend change. */
47362306a36Sopenharmony_ci			return 3;
47462306a36Sopenharmony_ci		case 0xc0:	/* Program change. */
47562306a36Sopenharmony_ci		case 0xd0:	/* Channel pressure. */
47662306a36Sopenharmony_ci			return 2;
47762306a36Sopenharmony_ci		default:
47862306a36Sopenharmony_ci		break;
47962306a36Sopenharmony_ci		}
48062306a36Sopenharmony_ci	break;
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	return -EINVAL;
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_cistatic int latter_fill_midi_msg(struct snd_ff *ff,
48762306a36Sopenharmony_ci				struct snd_rawmidi_substream *substream,
48862306a36Sopenharmony_ci				unsigned int port)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	u32 data = {0};
49162306a36Sopenharmony_ci	u8 *buf = (u8 *)&data;
49262306a36Sopenharmony_ci	int consumed;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	buf[0] = port << 4;
49562306a36Sopenharmony_ci	consumed = snd_rawmidi_transmit_peek(substream, buf + 1, 3);
49662306a36Sopenharmony_ci	if (consumed <= 0)
49762306a36Sopenharmony_ci		return consumed;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (!ff->on_sysex[port]) {
50062306a36Sopenharmony_ci		if (buf[1] != 0xf0) {
50162306a36Sopenharmony_ci			if (consumed < calculate_message_bytes(buf[1]))
50262306a36Sopenharmony_ci				return 0;
50362306a36Sopenharmony_ci		} else {
50462306a36Sopenharmony_ci			// The beginning of exclusives.
50562306a36Sopenharmony_ci			ff->on_sysex[port] = true;
50662306a36Sopenharmony_ci		}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci		buf[0] |= consumed;
50962306a36Sopenharmony_ci	} else {
51062306a36Sopenharmony_ci		if (buf[1] != 0xf7) {
51162306a36Sopenharmony_ci			if (buf[2] == 0xf7 || buf[3] == 0xf7) {
51262306a36Sopenharmony_ci				// Transfer end code at next time.
51362306a36Sopenharmony_ci				consumed -= 1;
51462306a36Sopenharmony_ci			}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci			buf[0] |= consumed;
51762306a36Sopenharmony_ci		} else {
51862306a36Sopenharmony_ci			// The end of exclusives.
51962306a36Sopenharmony_ci			ff->on_sysex[port] = false;
52062306a36Sopenharmony_ci			consumed = 1;
52162306a36Sopenharmony_ci			buf[0] |= 0x0f;
52262306a36Sopenharmony_ci		}
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	ff->msg_buf[port][0] = cpu_to_le32(data);
52662306a36Sopenharmony_ci	ff->rx_bytes[port] = consumed;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	return 1;
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ciconst struct snd_ff_protocol snd_ff_protocol_latter = {
53262306a36Sopenharmony_ci	.handle_msg		= latter_handle_midi_msg,
53362306a36Sopenharmony_ci	.fill_midi_msg		= latter_fill_midi_msg,
53462306a36Sopenharmony_ci	.get_clock		= latter_get_clock,
53562306a36Sopenharmony_ci	.switch_fetching_mode	= latter_switch_fetching_mode,
53662306a36Sopenharmony_ci	.allocate_resources	= latter_allocate_resources,
53762306a36Sopenharmony_ci	.begin_session		= latter_begin_session,
53862306a36Sopenharmony_ci	.finish_session		= latter_finish_session,
53962306a36Sopenharmony_ci	.dump_status		= latter_dump_status,
54062306a36Sopenharmony_ci};
541