summaryrefslogtreecommitdiff
path: root/sound/firewire/motu/motu-protocol-v1.c
blob: 65209e617e1004eb6f996b439869d36b1480b571 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
// SPDX-License-Identifier: GPL-2.0-only

// motu-protocol-v1.c - a part of driver for MOTU FireWire series
//
// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp>
//
// Licensed under the terms of the GNU General Public License, version 2.

#include "motu.h"

#include <linux/delay.h>

// Status register for MOTU 828 (0x'ffff'f000'0b00).
//
// 0xffff0000: ISOC_COMM_CONTROL_MASK in motu-stream.c.
// 0x00008000: mode of optical input interface.
//   0x00008000: for S/PDIF signal.
//   0x00000000: disabled or for ADAT signal.
// 0x00004000: mode of optical output interface.
//   0x00004000: for S/PDIF signal.
//   0x00000000: disabled or for ADAT signal.
// 0x00003f40: monitor input mode.
//   0x00000800: analog-1/2
//   0x00001a00: analog-3/4
//   0x00002c00: analog-5/6
//   0x00003e00: analog-7/8
//   0x00000000: analog-1
//   0x00000900: analog-2
//   0x00001200: analog-3
//   0x00001b00: analog-4
//   0x00002400: analog-5
//   0x00002d00: analog-6
//   0x00003600: analog-7
//   0x00003f00: analog-8
//   0x00000040: disabled
// 0x00000004: rate of sampling clock.
//   0x00000004: 48.0 kHz
//   0x00000000: 44.1 kHz
// 0x00000023: source of sampling clock.
//   0x00000002: S/PDIF on optical/coaxial interface.
//   0x00000021: ADAT on optical interface
//   0x00000001: ADAT on Dsub 9pin
//   0x00000000: internal or SMPTE

#define CLK_828_STATUS_OFFSET				0x0b00
#define  CLK_828_STATUS_MASK				0x0000ffff
#define  CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF	0x00008000
#define  CLK_828_STATUS_FLAG_OPT_OUT_IFACE_IS_SPDIF	0x00004000
#define  CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES		0x00000080
#define  CLK_828_STATUS_FLAG_SRC_IS_NOT_FROM_ADAT_DSUB	0x00000020
#define  CLK_828_STATUS_FLAG_OUTPUT_MUTE		0x00000008
#define  CLK_828_STATUS_FLAG_RATE_48000			0x00000004
#define  CLK_828_STATUS_FLAG_SRC_SPDIF_ON_OPT_OR_COAX	0x00000002
#define  CLK_828_STATUS_FLAG_SRC_ADAT_ON_OPT_OR_DSUB	0x00000001

// Status register for MOTU 896 (0x'ffff'f000'0b14).
//
// 0x20000000: fetch PCM frames from communication IC to DAC.
// 0x08000000: speed of word clock signal output on BNC interface.
//   0x00000000: follow to system clock.
//   0x08000000: half of system clock.
// 0x01000000: Route main output to headphone output.
// 0x00ffff00: input to monitor.
//   0x00000000: none
//   0x00004800: analog-1/2
//   0x00005a00: analog-3/4
//   0x00006c00: analog-5/6
//   0x00007e00: analog-7/8
//   0x00104800: AES/EBU-1/2
//   0x00004000: analog-1
//   0x00004900: analog-2
//   0x00005200: analog-3
//   0x00005b00: analog-4
//   0x00006400: analog-5
//   0x00006d00: analog-6
//   0x00007600: analog-7
//   0x00007f00: analog-8
//   0x00104000: AES/EBU-1
//   0x00104900: AES/EBU-2
// 0x00000060: sample rate conversin for AES/EBU input/output.
//   0x00000000: None
//   0x00000020: input signal is converted to system rate
//   0x00000040: output is slave to input, ignoring system rate
//   0x00000060: output is double rate than system rate
// 0x00000018: nominal rate of sampling clock.
//   0x00000000: 44.1 kHz
//   0x00000008: 48.0 kHz
//   0x00000010: 88.2 kHz
//   0x00000018: 96.0 kHz
// 0x00000007: source of sampling clock.
//   0x00000000: internal
//   0x00000001: ADAT on optical interface
//   0x00000002: AES/EBU on XLR
//   0x00000004: word clock on BNC
//   0x00000005: ADAT on Dsub 9pin

#define CLK_896_STATUS_OFFSET			0x0b14
#define  CLK_896_STATUS_FLAG_FETCH_ENABLE	0x20000000
#define  CLK_896_STATUS_FLAG_MAIN_TO_HP		0x01000000
#define  CLK_896_STATUS_MASK_SRC		0x00000007
#define   CLK_896_STATUS_FLAG_SRC_INTERNAL	0x00000000
#define   CLK_896_STATUS_FLAG_SRC_ADAT_ON_OPT	0x00000001
#define   CLK_896_STATUS_FLAG_SRC_AESEBU	0x00000002
#define   CLK_896_STATUS_FLAG_SRC_WORD		0x00000004
#define   CLK_896_STATUS_FLAG_SRC_ADAT_ON_DSUB	0x00000005
#define  CLK_896_STATUS_MASK_RATE		0x00000018
#define   CLK_896_STATUS_FLAG_RATE_44100	0x00000000
#define   CLK_896_STATUS_FLAG_RATE_48000	0x00000008
#define   CLK_896_STATUS_FLAG_RATE_88200	0x00000010
#define   CLK_896_STATUS_FLAG_RATE_96000	0x00000018

static void parse_clock_rate_828(u32 data, unsigned int *rate)
{
	if (data & CLK_828_STATUS_FLAG_RATE_48000)
		*rate = 48000;
	else
		*rate = 44100;
}

static int get_clock_rate_828(struct snd_motu *motu, unsigned int *rate)
{
	__be32 reg;
	int err;

	err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
	if (err < 0)
		return err;
	parse_clock_rate_828(be32_to_cpu(reg), rate);

	return 0;
}

static int parse_clock_rate_896(u32 data, unsigned int *rate)
{
	switch (data & CLK_896_STATUS_MASK_RATE) {
	case CLK_896_STATUS_FLAG_RATE_44100:
		*rate = 44100;
		break;
	case CLK_896_STATUS_FLAG_RATE_48000:
		*rate = 48000;
		break;
	case CLK_896_STATUS_FLAG_RATE_88200:
		*rate = 88200;
		break;
	case CLK_896_STATUS_FLAG_RATE_96000:
		*rate = 96000;
		break;
	default:
		return -ENXIO;
	}

	return 0;
}

static int get_clock_rate_896(struct snd_motu *motu, unsigned int *rate)
{
	__be32 reg;
	int err;

	err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
	if (err < 0)
		return err;
	return parse_clock_rate_896(be32_to_cpu(reg), rate);
}

int snd_motu_protocol_v1_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
{
	if (motu->spec == &snd_motu_spec_828)
		return get_clock_rate_828(motu, rate);
	else if (motu->spec == &snd_motu_spec_896)
		return get_clock_rate_896(motu, rate);
	else
		return -ENXIO;
}

static int set_clock_rate_828(struct snd_motu *motu, unsigned int rate)
{
	__be32 reg;
	u32 data;
	int err;

	err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
	if (err < 0)
		return err;
	data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;

	data &= ~CLK_828_STATUS_FLAG_RATE_48000;
	if (rate == 48000)
		data |= CLK_828_STATUS_FLAG_RATE_48000;

	reg = cpu_to_be32(data);
	return snd_motu_transaction_write(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
}

static int set_clock_rate_896(struct snd_motu *motu, unsigned int rate)
{
	unsigned int flag;
	__be32 reg;
	u32 data;
	int err;

	err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
	if (err < 0)
		return err;
	data = be32_to_cpu(reg);

	switch (rate) {
	case 44100:
		flag = CLK_896_STATUS_FLAG_RATE_44100;
		break;
	case 48000:
		flag = CLK_896_STATUS_FLAG_RATE_48000;
		break;
	case 88200:
		flag = CLK_896_STATUS_FLAG_RATE_88200;
		break;
	case 96000:
		flag = CLK_896_STATUS_FLAG_RATE_96000;
		break;
	default:
		return -EINVAL;
	}

	data &= ~CLK_896_STATUS_MASK_RATE;
	data |= flag;

	reg = cpu_to_be32(data);
	return snd_motu_transaction_write(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
}

int snd_motu_protocol_v1_set_clock_rate(struct snd_motu *motu, unsigned int rate)
{
	if (motu->spec == &snd_motu_spec_828)
		return set_clock_rate_828(motu, rate);
	else if (motu->spec == &snd_motu_spec_896)
		return set_clock_rate_896(motu, rate);
	else
		return -ENXIO;
}

static int get_clock_source_828(struct snd_motu *motu, enum snd_motu_clock_source *src)
{
	__be32 reg;
	u32 data;
	int err;

	err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
	if (err < 0)
		return err;
	data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;

	if (data & CLK_828_STATUS_FLAG_SRC_ADAT_ON_OPT_OR_DSUB) {
		if (data & CLK_828_STATUS_FLAG_SRC_IS_NOT_FROM_ADAT_DSUB)
			*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
		else
			*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
	} else if (data & CLK_828_STATUS_FLAG_SRC_SPDIF_ON_OPT_OR_COAX) {
		if (data & CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF)
			*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
		else
			*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
	} else {
		*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
	}

	return 0;
}

static int get_clock_source_896(struct snd_motu *motu, enum snd_motu_clock_source *src)
{
	__be32 reg;
	u32 data;
	int err;

	err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
	if (err < 0)
		return err;
	data = be32_to_cpu(reg);

	switch (data & CLK_896_STATUS_MASK_SRC) {
	case CLK_896_STATUS_FLAG_SRC_INTERNAL:
		*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
		break;
	case CLK_896_STATUS_FLAG_SRC_ADAT_ON_OPT:
		*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
		break;
	case CLK_896_STATUS_FLAG_SRC_AESEBU:
		*src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
		break;
	case CLK_896_STATUS_FLAG_SRC_WORD:
		*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
		break;
	case CLK_896_STATUS_FLAG_SRC_ADAT_ON_DSUB:
		*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
		break;
	default:
		return -ENXIO;
	}

	return 0;
}

int snd_motu_protocol_v1_get_clock_source(struct snd_motu *motu, enum snd_motu_clock_source *src)
{
	if (motu->spec == &snd_motu_spec_828)
		return get_clock_source_828(motu, src);
	else if (motu->spec == &snd_motu_spec_896)
		return get_clock_source_896(motu, src);
	else
		return -ENXIO;
}

static int switch_fetching_mode_828(struct snd_motu *motu, bool enable)
{
	__be32 reg;
	u32 data;
	int err;

	err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
	if (err < 0)
		return err;
	data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;

	data &= ~(CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES | CLK_828_STATUS_FLAG_OUTPUT_MUTE);
	if (enable) {
		// This transaction should be initiated after the device receives batch of packets
		// since the device voluntarily mutes outputs. As a workaround, yield processor over
		// 100 msec.
		msleep(100);
		data |= CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES | CLK_828_STATUS_FLAG_OUTPUT_MUTE;
	}

	reg = cpu_to_be32(data);
	return snd_motu_transaction_write(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
}

static int switch_fetching_mode_896(struct snd_motu *motu, bool enable)
{
	__be32 reg;
	u32 data;
	int err;

	err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
	if (err < 0)
		return err;
	data = be32_to_cpu(reg);

	data &= ~(CLK_896_STATUS_FLAG_FETCH_ENABLE | CLK_896_STATUS_FLAG_MAIN_TO_HP);
	if (enable)
		data |= (CLK_896_STATUS_FLAG_FETCH_ENABLE | CLK_896_STATUS_FLAG_MAIN_TO_HP);

	reg = cpu_to_be32(data);
	return snd_motu_transaction_write(motu, CLK_896_STATUS_OFFSET, &reg, sizeof(reg));
}

int snd_motu_protocol_v1_switch_fetching_mode(struct snd_motu *motu, bool enable)
{
	if (motu->spec == &snd_motu_spec_828)
		return switch_fetching_mode_828(motu, enable);
	else if (motu->spec == &snd_motu_spec_896)
		return switch_fetching_mode_896(motu, enable);
	else
		return -ENXIO;
}

static int detect_packet_formats_828(struct snd_motu *motu)
{
	__be32 reg;
	u32 data;
	int err;

	motu->tx_packet_formats.pcm_byte_offset = 4;
	motu->tx_packet_formats.msg_chunks = 2;

	motu->rx_packet_formats.pcm_byte_offset = 4;
	motu->rx_packet_formats.msg_chunks = 0;

	err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, &reg, sizeof(reg));
	if (err < 0)
		return err;
	data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;

	// The number of chunks is just reduced when SPDIF is activated.
	if (!(data & CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF))
		motu->tx_packet_formats.pcm_chunks[0] += 8;

	if (!(data & CLK_828_STATUS_FLAG_OPT_OUT_IFACE_IS_SPDIF))
		motu->rx_packet_formats.pcm_chunks[0] += 8;

	return 0;
}

static int detect_packet_formats_896(struct snd_motu *motu)
{
	// 24bit PCM frames follow to source packet header without message chunk.
	motu->tx_packet_formats.pcm_byte_offset = 4;
	motu->rx_packet_formats.pcm_byte_offset = 4;

	// No message chunk in data block.
	motu->tx_packet_formats.msg_chunks = 0;
	motu->rx_packet_formats.msg_chunks = 0;

	// Always enable optical interface for ADAT signal since the device have no registers
	// to refer to current configuration.
	motu->tx_packet_formats.pcm_chunks[0] += 8;
	motu->tx_packet_formats.pcm_chunks[1] += 8;

	motu->rx_packet_formats.pcm_chunks[0] += 8;
	motu->rx_packet_formats.pcm_chunks[1] += 8;

	return 0;
}

int snd_motu_protocol_v1_cache_packet_formats(struct snd_motu *motu)
{
	memcpy(motu->tx_packet_formats.pcm_chunks, motu->spec->tx_fixed_pcm_chunks,
	       sizeof(motu->tx_packet_formats.pcm_chunks));
	memcpy(motu->rx_packet_formats.pcm_chunks, motu->spec->rx_fixed_pcm_chunks,
	       sizeof(motu->rx_packet_formats.pcm_chunks));

	if (motu->spec == &snd_motu_spec_828)
		return detect_packet_formats_828(motu);
	else if (motu->spec == &snd_motu_spec_896)
		return detect_packet_formats_896(motu);
	else
		return 0;
}

const struct snd_motu_spec snd_motu_spec_828 = {
	.name = "828",
	.protocol_version = SND_MOTU_PROTOCOL_V1,
	.tx_fixed_pcm_chunks = {10, 0, 0},
	.rx_fixed_pcm_chunks = {10, 0, 0},
};

const struct snd_motu_spec snd_motu_spec_896 = {
	.name = "896",
	.tx_fixed_pcm_chunks = {10, 10, 0},
	.rx_fixed_pcm_chunks = {10, 10, 0},
};