summaryrefslogtreecommitdiffstats
path: root/sound/firewire/motu/motu.h
blob: d071b2342f113558e0d6327a1e8e08591f2cc671 (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
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * motu.h - a part of driver for MOTU FireWire series
 *
 * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
 */

#ifndef SOUND_FIREWIRE_MOTU_H_INCLUDED
#define SOUND_FIREWIRE_MOTU_H_INCLUDED

#include <linux/device.h>
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/sched/signal.h>

#include <sound/control.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/info.h>
#include <sound/rawmidi.h>
#include <sound/firewire.h>
#include <sound/hwdep.h>

#include "../lib.h"
#include "../amdtp-stream.h"
#include "../iso-resources.h"

struct snd_motu_packet_format {
	unsigned char midi_flag_offset;
	unsigned char midi_byte_offset;
	unsigned char pcm_byte_offset;

	unsigned char msg_chunks;
	unsigned char pcm_chunks[3];
	unsigned char fixed_part_pcm_chunks[3];
	unsigned char differed_part_pcm_chunks[3];
};

struct snd_motu {
	struct snd_card *card;
	struct fw_unit *unit;
	struct mutex mutex;
	spinlock_t lock;

	bool registered;
	struct delayed_work dwork;

	/* Model dependent information. */
	const struct snd_motu_spec *spec;

	/* For packet streaming */
	struct snd_motu_packet_format tx_packet_formats;
	struct snd_motu_packet_format rx_packet_formats;
	struct amdtp_stream tx_stream;
	struct amdtp_stream rx_stream;
	struct fw_iso_resources tx_resources;
	struct fw_iso_resources rx_resources;
	unsigned int substreams_counter;

	/* For notification. */
	struct fw_address_handler async_handler;
	u32 msg;

	/* For uapi */
	int dev_lock_count;
	bool dev_lock_changed;
	wait_queue_head_t hwdep_wait;

	struct amdtp_domain domain;
};

enum snd_motu_spec_flags {
	SND_MOTU_SPEC_SUPPORT_CLOCK_X2	= 0x0001,
	SND_MOTU_SPEC_SUPPORT_CLOCK_X4	= 0x0002,
	SND_MOTU_SPEC_TX_MICINST_CHUNK	= 0x0004,
	SND_MOTU_SPEC_TX_RETURN_CHUNK	= 0x0008,
	SND_MOTU_SPEC_TX_REVERB_CHUNK	= 0x0010,
	SND_MOTU_SPEC_HAS_AESEBU_IFACE	= 0x0020,
	SND_MOTU_SPEC_HAS_OPT_IFACE_A	= 0x0040,
	SND_MOTU_SPEC_HAS_OPT_IFACE_B	= 0x0080,
	SND_MOTU_SPEC_RX_MIDI_2ND_Q	= 0x0100,
	SND_MOTU_SPEC_RX_MIDI_3RD_Q	= 0x0200,
	SND_MOTU_SPEC_TX_MIDI_2ND_Q	= 0x0400,
	SND_MOTU_SPEC_TX_MIDI_3RD_Q	= 0x0800,
	SND_MOTU_SPEC_RX_SEPARATED_MAIN	= 0x1000,
};

#define SND_MOTU_CLOCK_RATE_COUNT	6
extern const unsigned int snd_motu_clock_rates[SND_MOTU_CLOCK_RATE_COUNT];

enum snd_motu_clock_source {
	SND_MOTU_CLOCK_SOURCE_INTERNAL,
	SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB,
	SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT,
	SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A,
	SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B,
	SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT,
	SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A,
	SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B,
	SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX,
	SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR,
	SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC,
	SND_MOTU_CLOCK_SOURCE_SPH,
	SND_MOTU_CLOCK_SOURCE_UNKNOWN,
};

enum snd_motu_protocol_version {
	SND_MOTU_PROTOCOL_V2,
	SND_MOTU_PROTOCOL_V3,
};

struct snd_motu_spec {
	const char *const name;
	enum snd_motu_protocol_version protocol_version;
	enum snd_motu_spec_flags flags;

	unsigned char tx_fixed_pcm_chunks[3];
	unsigned char rx_fixed_pcm_chunks[3];

	unsigned char analog_in_ports;
	unsigned char analog_out_ports;
};

extern const struct snd_motu_spec snd_motu_spec_828mk2;
extern const struct snd_motu_spec snd_motu_spec_traveler;
extern const struct snd_motu_spec snd_motu_spec_ultralite;
extern const struct snd_motu_spec snd_motu_spec_8pre;

extern const struct snd_motu_spec snd_motu_spec_828mk3;
extern const struct snd_motu_spec snd_motu_spec_audio_express;
extern const struct snd_motu_spec snd_motu_spec_4pre;

int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
		    enum amdtp_stream_direction dir,
		    const struct snd_motu_spec *spec);
int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
			      unsigned int midi_ports,
			      struct snd_motu_packet_format *formats);
int amdtp_motu_add_pcm_hw_constraints(struct amdtp_stream *s,
				      struct snd_pcm_runtime *runtime);
void amdtp_motu_midi_trigger(struct amdtp_stream *s, unsigned int port,
			     struct snd_rawmidi_substream *midi);

int snd_motu_transaction_read(struct snd_motu *motu, u32 offset, __be32 *reg,
			      size_t size);
int snd_motu_transaction_write(struct snd_motu *motu, u32 offset, __be32 *reg,
			       size_t size);
int snd_motu_transaction_register(struct snd_motu *motu);
int snd_motu_transaction_reregister(struct snd_motu *motu);
void snd_motu_transaction_unregister(struct snd_motu *motu);

int snd_motu_stream_init_duplex(struct snd_motu *motu);
void snd_motu_stream_destroy_duplex(struct snd_motu *motu);
int snd_motu_stream_cache_packet_formats(struct snd_motu *motu);
int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate,
				   unsigned int frames_per_period,
				   unsigned int frames_per_buffer);
int snd_motu_stream_start_duplex(struct snd_motu *motu);
void snd_motu_stream_stop_duplex(struct snd_motu *motu);
int snd_motu_stream_lock_try(struct snd_motu *motu);
void snd_motu_stream_lock_release(struct snd_motu *motu);

void snd_motu_proc_init(struct snd_motu *motu);

int snd_motu_create_pcm_devices(struct snd_motu *motu);

int snd_motu_create_midi_devices(struct snd_motu *motu);

int snd_motu_create_hwdep_device(struct snd_motu *motu);

int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu,
					unsigned int *rate);
int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu,
					unsigned int rate);
int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu,
					  enum snd_motu_clock_source *src);
int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu,
					      bool enable);
int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu);

int snd_motu_protocol_v3_get_clock_rate(struct snd_motu *motu,
					unsigned int *rate);
int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu,
					unsigned int rate);
int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu,
					  enum snd_motu_clock_source *src);
int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu,
					      bool enable);
int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu);

static inline int snd_motu_protocol_get_clock_rate(struct snd_motu *motu,
						   unsigned int *rate)
{
	if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
		return snd_motu_protocol_v2_get_clock_rate(motu, rate);
	else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
		return snd_motu_protocol_v3_get_clock_rate(motu, rate);
	else
		return -ENXIO;
}

static inline int snd_motu_protocol_set_clock_rate(struct snd_motu *motu,
						   unsigned int rate)
{
	if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
		return snd_motu_protocol_v2_set_clock_rate(motu, rate);
	else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
		return snd_motu_protocol_v3_set_clock_rate(motu, rate);
	else
		return -ENXIO;
}

static inline int snd_motu_protocol_get_clock_source(struct snd_motu *motu,
					enum snd_motu_clock_source *source)
{
	if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
		return snd_motu_protocol_v2_get_clock_source(motu, source);
	else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
		return snd_motu_protocol_v3_get_clock_source(motu, source);
	else
		return -ENXIO;
}

static inline int snd_motu_protocol_switch_fetching_mode(struct snd_motu *motu,
							 bool enable)
{
	if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
		return snd_motu_protocol_v2_switch_fetching_mode(motu, enable);
	else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
		return snd_motu_protocol_v3_switch_fetching_mode(motu, enable);
	else
		return -ENXIO;
}

static inline int snd_motu_protocol_cache_packet_formats(struct snd_motu *motu)
{
	if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2)
		return snd_motu_protocol_v2_cache_packet_formats(motu);
	else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
		return snd_motu_protocol_v3_cache_packet_formats(motu);
	else
		return -ENXIO;
}

#endif