File Index
Files and directories
Sign in
FILE

motu-protocol-v2.c

File preview with syntax highlighting.

linux-6.19.14/sound/firewire/motu/motu-protocol-v2.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * motu-protocol-v2.c - a part of driver for MOTU FireWire series
  4 *
  5 * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
  6 */
  7
  8#include "motu.h"
  9
 10#define V2_CLOCK_STATUS_OFFSET			0x0b14
 11#define  V2_CLOCK_RATE_MASK			0x00000038
 12#define  V2_CLOCK_RATE_SHIFT			3
 13#define  V2_CLOCK_SRC_MASK			0x00000007
 14#define  V2_CLOCK_SRC_SHIFT			0
 15#define   V2_CLOCK_SRC_AESEBU_ON_XLR		0x07	// In Traveler.
 16#define   V2_CLOCK_SRC_ADAT_ON_DSUB		0x05
 17#define   V2_CLOCK_SRC_WORD_ON_BNC		0x04
 18#define   V2_CLOCK_SRC_SPH			0x03
 19#define   V2_CLOCK_SRC_SPDIF			0x02	// on either coaxial or optical. AES/EBU in 896HD.
 20#define   V2_CLOCK_SRC_ADAT_ON_OPT		0x01
 21#define   V2_CLOCK_SRC_INTERNAL			0x00
 22#define  V2_CLOCK_FETCH_ENABLE			0x02000000
 23#define  V2_CLOCK_MODEL_SPECIFIC		0x04000000
 24
 25#define V2_IN_OUT_CONF_OFFSET			0x0c04
 26#define  V2_OPT_OUT_IFACE_MASK			0x00000c00
 27#define  V2_OPT_OUT_IFACE_SHIFT			10
 28#define  V2_OPT_IN_IFACE_MASK			0x00000300
 29#define  V2_OPT_IN_IFACE_SHIFT			8
 30#define  V2_OPT_IFACE_MODE_NONE			0
 31#define  V2_OPT_IFACE_MODE_ADAT			1
 32#define  V2_OPT_IFACE_MODE_SPDIF		2
 33
 34static int get_clock_rate(u32 data, unsigned int *rate)
 35{
 36	unsigned int index = (data & V2_CLOCK_RATE_MASK) >> V2_CLOCK_RATE_SHIFT;
 37	if (index >= ARRAY_SIZE(snd_motu_clock_rates))
 38		return -EIO;
 39
 40	*rate = snd_motu_clock_rates[index];
 41
 42	return 0;
 43}
 44
 45int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu,
 46					unsigned int *rate)
 47{
 48	__be32 reg;
 49	int err;
 50
 51	err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
 52					sizeof(reg));
 53	if (err < 0)
 54		return err;
 55
 56	return get_clock_rate(be32_to_cpu(reg), rate);
 57}
 58
 59int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu,
 60					unsigned int rate)
 61{
 62	__be32 reg;
 63	u32 data;
 64	int i;
 65	int err;
 66
 67	for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
 68		if (snd_motu_clock_rates[i] == rate)
 69			break;
 70	}
 71	if (i == ARRAY_SIZE(snd_motu_clock_rates))
 72		return -EINVAL;
 73
 74	err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
 75					sizeof(reg));
 76	if (err < 0)
 77		return err;
 78	data = be32_to_cpu(reg);
 79
 80	data &= ~V2_CLOCK_RATE_MASK;
 81	data |= i << V2_CLOCK_RATE_SHIFT;
 82
 83	reg = cpu_to_be32(data);
 84	return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, &reg,
 85					  sizeof(reg));
 86}
 87
 88static int get_clock_source(struct snd_motu *motu, u32 data,
 89			    enum snd_motu_clock_source *src)
 90{
 91	switch (data & V2_CLOCK_SRC_MASK) {
 92	case V2_CLOCK_SRC_INTERNAL:
 93		*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
 94		break;
 95	case V2_CLOCK_SRC_ADAT_ON_OPT:
 96		*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
 97		break;
 98	case V2_CLOCK_SRC_SPDIF:
 99	{
100		bool support_iec60958_on_opt = (motu->spec == &snd_motu_spec_828mk2 ||
101						motu->spec == &snd_motu_spec_traveler);
102
103		if (motu->spec == &snd_motu_spec_896hd) {
104			*src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
105		} else if (!support_iec60958_on_opt) {
106			*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
107		} else {
108			__be32 reg;
109
110			// To check the configuration of optical interface.
111			int err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
112							    sizeof(reg));
113			if (err < 0)
114				return err;
115
116			if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) ==
117			    V2_OPT_IFACE_MODE_SPDIF)
118				*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
119			else
120				*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
121		}
122		break;
123	}
124	case V2_CLOCK_SRC_SPH:
125		*src = SND_MOTU_CLOCK_SOURCE_SPH;
126		break;
127	case V2_CLOCK_SRC_WORD_ON_BNC:
128		*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
129		break;
130	case V2_CLOCK_SRC_ADAT_ON_DSUB:
131		*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
132		break;
133	case V2_CLOCK_SRC_AESEBU_ON_XLR:
134		// For Traveler.
135		*src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
136		break;
137	default:
138		*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
139		break;
140	}
141
142	return 0;
143}
144
145int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu,
146					  enum snd_motu_clock_source *src)
147{
148	__be32 reg;
149	int err;
150
151	err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
152					sizeof(reg));
153	if (err < 0)
154		return err;
155
156	return get_clock_source(motu, be32_to_cpu(reg), src);
157}
158
159// Expected for Traveler, which implements Altera Cyclone EP1C3.
160static int switch_fetching_mode_cyclone(struct snd_motu *motu, u32 *data,
161					bool enable)
162{
163	*data |= V2_CLOCK_MODEL_SPECIFIC;
164
165	return 0;
166}
167
168// For UltraLite and 8pre, which implements Xilinx Spartan XC3S200.
169static int switch_fetching_mode_spartan(struct snd_motu *motu, u32 *data,
170					bool enable)
171{
172	unsigned int rate;
173	enum snd_motu_clock_source src;
174	int err;
175
176	err = get_clock_source(motu, *data, &src);
177	if (err < 0)
178		return err;
179
180	err = get_clock_rate(*data, &rate);
181	if (err < 0)
182		return err;
183
184	if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000)
185		*data |= V2_CLOCK_MODEL_SPECIFIC;
186
187	return 0;
188}
189
190int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu,
191					      bool enable)
192{
193	if (motu->spec == &snd_motu_spec_828mk2) {
194		// 828mkII implements Altera ACEX 1K EP1K30. Nothing to do.
195		return 0;
196	} else if (motu->spec == &snd_motu_spec_896hd) {
197		// 896HD implements Altera Cyclone EP1C3 but nothing to do.
198		return 0;
199	} else {
200		__be32 reg;
201		u32 data;
202		int err;
203
204		err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET,
205						&reg, sizeof(reg));
206		if (err < 0)
207			return err;
208		data = be32_to_cpu(reg);
209
210		data &= ~(V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC);
211		if (enable)
212			data |= V2_CLOCK_FETCH_ENABLE;
213
214		if (motu->spec == &snd_motu_spec_traveler)
215			err = switch_fetching_mode_cyclone(motu, &data, enable);
216		else
217			err = switch_fetching_mode_spartan(motu, &data, enable);
218		if (err < 0)
219			return err;
220
221		reg = cpu_to_be32(data);
222		return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET,
223						  &reg, sizeof(reg));
224	}
225}
226
227int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu)
228{
229	bool has_two_opt_ifaces = (motu->spec == &snd_motu_spec_8pre);
230	__be32 reg;
231	u32 data;
232	int err;
233
234	motu->tx_packet_formats.pcm_byte_offset = 10;
235	motu->rx_packet_formats.pcm_byte_offset = 10;
236
237	motu->tx_packet_formats.msg_chunks = 2;
238	motu->rx_packet_formats.msg_chunks = 2;
239
240	err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
241					sizeof(reg));
242	if (err < 0)
243		return err;
244	data = be32_to_cpu(reg);
245
246	memcpy(motu->tx_packet_formats.pcm_chunks,
247	       motu->spec->tx_fixed_pcm_chunks,
248	       sizeof(motu->tx_packet_formats.pcm_chunks));
249	memcpy(motu->rx_packet_formats.pcm_chunks,
250	       motu->spec->rx_fixed_pcm_chunks,
251	       sizeof(motu->rx_packet_formats.pcm_chunks));
252
253	if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == V2_OPT_IFACE_MODE_ADAT) {
254		motu->tx_packet_formats.pcm_chunks[0] += 8;
255
256		if (!has_two_opt_ifaces)
257			motu->tx_packet_formats.pcm_chunks[1] += 4;
258		else
259			motu->tx_packet_formats.pcm_chunks[1] += 8;
260	}
261
262	if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) == V2_OPT_IFACE_MODE_ADAT) {
263		motu->rx_packet_formats.pcm_chunks[0] += 8;
264
265		if (!has_two_opt_ifaces)
266			motu->rx_packet_formats.pcm_chunks[1] += 4;
267		else
268			motu->rx_packet_formats.pcm_chunks[1] += 8;
269	}
270
271	return 0;
272}
273
274const struct snd_motu_spec snd_motu_spec_828mk2 = {
275	.name = "828mk2",
276	.protocol_version = SND_MOTU_PROTOCOL_V2,
277	.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
278		 SND_MOTU_SPEC_TX_MIDI_2ND_Q |
279		 SND_MOTU_SPEC_REGISTER_DSP,
280	.tx_fixed_pcm_chunks = {14, 14, 0},
281	.rx_fixed_pcm_chunks = {14, 14, 0},
282};
283
284const struct snd_motu_spec snd_motu_spec_896hd = {
285	.name = "896HD",
286	.protocol_version = SND_MOTU_PROTOCOL_V2,
287	.flags = SND_MOTU_SPEC_REGISTER_DSP,
288	.tx_fixed_pcm_chunks = {14, 14, 8},
289	.rx_fixed_pcm_chunks = {14, 14, 8},
290};
291
292const struct snd_motu_spec snd_motu_spec_traveler = {
293	.name = "Traveler",
294	.protocol_version = SND_MOTU_PROTOCOL_V2,
295	.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
296		 SND_MOTU_SPEC_TX_MIDI_2ND_Q |
297		 SND_MOTU_SPEC_REGISTER_DSP,
298	.tx_fixed_pcm_chunks = {14, 14, 8},
299	.rx_fixed_pcm_chunks = {14, 14, 8},
300};
301
302const struct snd_motu_spec snd_motu_spec_ultralite = {
303	.name = "UltraLite",
304	.protocol_version = SND_MOTU_PROTOCOL_V2,
305	.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
306		 SND_MOTU_SPEC_TX_MIDI_2ND_Q |
307		 SND_MOTU_SPEC_REGISTER_DSP,
308	.tx_fixed_pcm_chunks = {14, 14, 0},
309	.rx_fixed_pcm_chunks = {14, 14, 0},
310};
311
312const struct snd_motu_spec snd_motu_spec_8pre = {
313	.name = "8pre",
314	.protocol_version = SND_MOTU_PROTOCOL_V2,
315	.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
316		 SND_MOTU_SPEC_TX_MIDI_2ND_Q |
317		 SND_MOTU_SPEC_REGISTER_DSP,
318	// Two dummy chunks always in the end of data block.
319	.tx_fixed_pcm_chunks = {10, 10, 0},
320	.rx_fixed_pcm_chunks = {6, 6, 0},
321};
~/ Read-only file preview.