diff options
Diffstat (limited to 'sound/firewire/fireface/ff-protocol-latter.c')
| -rw-r--r-- | sound/firewire/fireface/ff-protocol-latter.c | 128 | 
1 files changed, 104 insertions, 24 deletions
diff --git a/sound/firewire/fireface/ff-protocol-latter.c b/sound/firewire/fireface/ff-protocol-latter.c index 8d3b23778eb2..9947e0c2e0aa 100644 --- a/sound/firewire/fireface/ff-protocol-latter.c +++ b/sound/firewire/fireface/ff-protocol-latter.c @@ -1,9 +1,7 @@  // SPDX-License-Identifier: GPL-2.0 -// ff-protocol-latter - a part of driver for RME Fireface series +// ff-protocol-latter.c - a part of driver for RME Fireface series  //  // Copyright (c) 2019 Takashi Sakamoto -// -// Licensed under the terms of the GNU General Public License, version 2.  #include <linux/delay.h> @@ -15,6 +13,61 @@  #define LATTER_FETCH_MODE	0xffff00000010ULL  #define LATTER_SYNC_STATUS	0x0000801c0000ULL +// The content of sync status register differs between models. +// +// Fireface UCX: +//  0xf0000000: (unidentified) +//  0x0f000000: effective rate of sampling clock +//  0x00f00000: detected rate of word clock on BNC interface +//  0x000f0000: detected rate of ADAT or S/PDIF on optical interface +//  0x0000f000: detected rate of S/PDIF on coaxial interface +//  0x00000e00: effective source of sampling clock +//    0x00000e00: Internal +//    0x00000800: (unidentified) +//    0x00000600: Word clock on BNC interface +//    0x00000400: ADAT on optical interface +//    0x00000200: S/PDIF on coaxial or optical interface +//  0x00000100: Optical interface is used for ADAT signal +//  0x00000080: (unidentified) +//  0x00000040: Synchronized to word clock on BNC interface +//  0x00000020: Synchronized to ADAT or S/PDIF on optical interface +//  0x00000010: Synchronized to S/PDIF on coaxial interface +//  0x00000008: (unidentified) +//  0x00000004: Lock word clock on BNC interface +//  0x00000002: Lock ADAT or S/PDIF on optical interface +//  0x00000001: Lock S/PDIF on coaxial interface +// +// Fireface 802 (and perhaps UFX): +//   0xf0000000: effective rate of sampling clock +//   0x0f000000: detected rate of ADAT-B on 2nd optical interface +//   0x00f00000: detected rate of ADAT-A on 1st optical interface +//   0x000f0000: detected rate of AES/EBU on XLR or coaxial interface +//   0x0000f000: detected rate of word clock on BNC interface +//   0x00000e00: effective source of sampling clock +//     0x00000e00: internal +//     0x00000800: ADAT-B +//     0x00000600: ADAT-A +//     0x00000400: AES/EBU +//     0x00000200: Word clock +//   0x00000080: Synchronized to ADAT-B on 2nd optical interface +//   0x00000040: Synchronized to ADAT-A on 1st optical interface +//   0x00000020: Synchronized to AES/EBU on XLR or 2nd optical interface +//   0x00000010: Synchronized to word clock on BNC interface +//   0x00000008: Lock ADAT-B on 2nd optical interface +//   0x00000004: Lock ADAT-A on 1st optical interface +//   0x00000002: Lock AES/EBU on XLR or 2nd optical interface +//   0x00000001: Lock word clock on BNC interface +// +// The pattern for rate bits: +//   0x00: 32.0 kHz +//   0x01: 44.1 kHz +//   0x02: 48.0 kHz +//   0x04: 64.0 kHz +//   0x05: 88.2 kHz +//   0x06: 96.0 kHz +//   0x08: 128.0 kHz +//   0x09: 176.4 kHz +//   0x0a: 192.0 kHz  static int parse_clock_bits(u32 data, unsigned int *rate,  			    enum snd_ff_clock_src *src,  			    enum snd_ff_unit_version unit_version) @@ -23,35 +76,48 @@ static int parse_clock_bits(u32 data, unsigned int *rate,  		unsigned int rate;  		u32 flag;  	} *rate_entry, rate_entries[] = { -		{ 32000,	0x00000000, }, -		{ 44100,	0x01000000, }, -		{ 48000,	0x02000000, }, -		{ 64000,	0x04000000, }, -		{ 88200,	0x05000000, }, -		{ 96000,	0x06000000, }, -		{ 128000,	0x08000000, }, -		{ 176400,	0x09000000, }, -		{ 192000,	0x0a000000, }, +		{ 32000,	0x00, }, +		{ 44100,	0x01, }, +		{ 48000,	0x02, }, +		{ 64000,	0x04, }, +		{ 88200,	0x05, }, +		{ 96000,	0x06, }, +		{ 128000,	0x08, }, +		{ 176400,	0x09, }, +		{ 192000,	0x0a, },  	};  	static const struct {  		enum snd_ff_clock_src src;  		u32 flag; -	} *clk_entry, clk_entries[] = { +	} *clk_entry, *clk_entries, ucx_clk_entries[] = {  		{ SND_FF_CLOCK_SRC_SPDIF,	0x00000200, },  		{ SND_FF_CLOCK_SRC_ADAT1,	0x00000400, },  		{ SND_FF_CLOCK_SRC_WORD,	0x00000600, },  		{ SND_FF_CLOCK_SRC_INTERNAL,	0x00000e00, }, +	}, ufx_ff802_clk_entries[] = { +		{ SND_FF_CLOCK_SRC_WORD,	0x00000200, }, +		{ SND_FF_CLOCK_SRC_SPDIF,	0x00000400, }, +		{ SND_FF_CLOCK_SRC_ADAT1,	0x00000600, }, +		{ SND_FF_CLOCK_SRC_ADAT2,	0x00000800, }, +		{ SND_FF_CLOCK_SRC_INTERNAL,	0x00000e00, },  	}; +	u32 rate_bits; +	unsigned int clk_entry_count;  	int i; -	if (unit_version != SND_FF_UNIT_VERSION_UCX) { -		// e.g. 0x00fe0f20 but expected 0x00eff002. -		data = ((data & 0xf0f0f0f0) >> 4) | ((data & 0x0f0f0f0f) << 4); +	if (unit_version == SND_FF_UNIT_VERSION_UCX) { +		rate_bits = (data & 0x0f000000) >> 24; +		clk_entries = ucx_clk_entries; +		clk_entry_count = ARRAY_SIZE(ucx_clk_entries); +	} else { +		rate_bits = (data & 0xf0000000) >> 28; +		clk_entries = ufx_ff802_clk_entries; +		clk_entry_count = ARRAY_SIZE(ufx_ff802_clk_entries);  	}  	for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) {  		rate_entry = rate_entries + i; -		if ((data & 0x0f000000) == rate_entry->flag) { +		if (rate_bits == rate_entry->flag) {  			*rate = rate_entry->rate;  			break;  		} @@ -59,14 +125,14 @@ static int parse_clock_bits(u32 data, unsigned int *rate,  	if (i == ARRAY_SIZE(rate_entries))  		return -EIO; -	for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) { +	for (i = 0; i < clk_entry_count; ++i) {  		clk_entry = clk_entries + i;  		if ((data & 0x000e00) == clk_entry->flag) {  			*src = clk_entry->src;  			break;  		}  	} -	if (i == ARRAY_SIZE(clk_entries)) +	if (i == clk_entry_count)  		return -EIO;  	return 0; @@ -249,16 +315,22 @@ static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer  		char *const label;  		u32 locked_mask;  		u32 synced_mask; -	} *clk_entry, clk_entries[] = { +	} *clk_entry, *clk_entries, ucx_clk_entries[] = {  		{ "S/PDIF",	0x00000001, 0x00000010, },  		{ "ADAT",	0x00000002, 0x00000020, },  		{ "WDClk",	0x00000004, 0x00000040, }, +	}, ufx_ff802_clk_entries[] = { +		{ "WDClk",	0x00000001, 0x00000010, }, +		{ "AES/EBU",	0x00000002, 0x00000020, }, +		{ "ADAT-A",	0x00000004, 0x00000040, }, +		{ "ADAT-B",	0x00000008, 0x00000080, },  	};  	__le32 reg;  	u32 data;  	unsigned int rate;  	enum snd_ff_clock_src src;  	const char *label; +	unsigned int clk_entry_count;  	int i;  	int err; @@ -270,7 +342,15 @@ static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer  	snd_iprintf(buffer, "External source detection:\n"); -	for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) { +	if (ff->unit_version == SND_FF_UNIT_VERSION_UCX) { +		clk_entries = ucx_clk_entries; +		clk_entry_count = ARRAY_SIZE(ucx_clk_entries); +	} else { +		clk_entries = ufx_ff802_clk_entries; +		clk_entry_count = ARRAY_SIZE(ufx_ff802_clk_entries); +	} + +	for (i = 0; i < clk_entry_count; ++i) {  		clk_entry = clk_entries + i;  		snd_iprintf(buffer, "%s: ", clk_entry->label);  		if (data & clk_entry->locked_mask) { @@ -313,8 +393,8 @@ static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer  // input attenuation. This driver allocates for the first option  // (0x'....'....'0000'0000) and expects userspace application to configure the  // register for it. -static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset, -				   __le32 *buf, size_t length) +static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf, +				   size_t length, u32 tstamp)  {  	u32 data = le32_to_cpu(*buf);  	unsigned int index = (data & 0x000000f0) >> 4; @@ -449,7 +529,7 @@ static int latter_fill_midi_msg(struct snd_ff *ff,  }  const struct snd_ff_protocol snd_ff_protocol_latter = { -	.handle_midi_msg	= latter_handle_midi_msg, +	.handle_msg		= latter_handle_midi_msg,  	.fill_midi_msg		= latter_fill_midi_msg,  	.get_clock		= latter_get_clock,  	.switch_fetching_mode	= latter_switch_fetching_mode,  | 
