Commit | Line | Data |
---|---|---|
da607e19 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
17c4e5ea TS |
2 | /* |
3 | * ff.c - a part of driver for RME Fireface series | |
4 | * | |
5 | * Copyright (c) 2015-2017 Takashi Sakamoto | |
17c4e5ea TS |
6 | */ |
7 | ||
8 | #include "ff.h" | |
9 | ||
10 | #define OUI_RME 0x000a35 | |
11 | ||
12 | MODULE_DESCRIPTION("RME Fireface series Driver"); | |
13 | MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>"); | |
81c29435 | 14 | MODULE_LICENSE("GPL"); |
17c4e5ea TS |
15 | |
16 | static void name_card(struct snd_ff *ff) | |
17 | { | |
18 | struct fw_device *fw_dev = fw_parent_device(ff->unit); | |
782222ba TS |
19 | const char *const names[] = { |
20 | [SND_FF_UNIT_VERSION_FF800] = "Fireface800", | |
21 | [SND_FF_UNIT_VERSION_FF400] = "Fireface400", | |
1f65e668 | 22 | [SND_FF_UNIT_VERSION_UFX] = "FirefaceUFX", |
782222ba | 23 | [SND_FF_UNIT_VERSION_UCX] = "FirefaceUCX", |
062bb452 | 24 | [SND_FF_UNIT_VERSION_802] = "Fireface802", |
782222ba TS |
25 | }; |
26 | const char *name; | |
27 | ||
28 | name = names[ff->unit_version]; | |
17c4e5ea TS |
29 | |
30 | strcpy(ff->card->driver, "Fireface"); | |
782222ba TS |
31 | strcpy(ff->card->shortname, name); |
32 | strcpy(ff->card->mixername, name); | |
17c4e5ea | 33 | snprintf(ff->card->longname, sizeof(ff->card->longname), |
782222ba | 34 | "RME %s, GUID %08x%08x at %s, S%d", name, |
17c4e5ea TS |
35 | fw_dev->config_rom[3], fw_dev->config_rom[4], |
36 | dev_name(&ff->unit->device), 100 << fw_dev->max_speed); | |
37 | } | |
38 | ||
3babca45 | 39 | static void ff_card_free(struct snd_card *card) |
17c4e5ea | 40 | { |
3babca45 TS |
41 | struct snd_ff *ff = card->private_data; |
42 | ||
fc716397 | 43 | snd_ff_stream_destroy_duplex(ff); |
73f5537f | 44 | snd_ff_transaction_unregister(ff); |
ee5f0b32 | 45 | |
c31909fa TS |
46 | kfree(ff->msg_parser); |
47 | ||
ee5f0b32 TS |
48 | mutex_destroy(&ff->mutex); |
49 | fw_unit_put(ff->unit); | |
324540c4 TS |
50 | } |
51 | ||
ee5f0b32 | 52 | static int snd_ff_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry) |
324540c4 | 53 | { |
ee5f0b32 TS |
54 | struct snd_card *card; |
55 | struct snd_ff *ff; | |
324540c4 TS |
56 | int err; |
57 | ||
ee5f0b32 | 58 | err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*ff), &card); |
324540c4 | 59 | if (err < 0) |
ee5f0b32 TS |
60 | return err; |
61 | card->private_free = ff_card_free; | |
62 | ||
63 | ff = card->private_data; | |
64 | ff->unit = fw_unit_get(unit); | |
65 | dev_set_drvdata(&unit->device, ff); | |
66 | ff->card = card; | |
67 | ||
68 | mutex_init(&ff->mutex); | |
69 | spin_lock_init(&ff->lock); | |
70 | init_waitqueue_head(&ff->hwdep_wait); | |
71 | ||
72 | ff->unit_version = entry->version; | |
73 | ff->spec = (const struct snd_ff_spec *)entry->driver_data; | |
324540c4 | 74 | |
73f5537f TS |
75 | err = snd_ff_transaction_register(ff); |
76 | if (err < 0) | |
77 | goto error; | |
19174295 | 78 | |
324540c4 TS |
79 | name_card(ff); |
80 | ||
fc716397 TS |
81 | err = snd_ff_stream_init_duplex(ff); |
82 | if (err < 0) | |
83 | goto error; | |
75d6d898 | 84 | |
d3fc7aac TS |
85 | snd_ff_proc_init(ff); |
86 | ||
73f5537f TS |
87 | err = snd_ff_create_midi_devices(ff); |
88 | if (err < 0) | |
89 | goto error; | |
ff2c293e | 90 | |
fc716397 TS |
91 | err = snd_ff_create_pcm_devices(ff); |
92 | if (err < 0) | |
93 | goto error; | |
4b316436 | 94 | |
fc716397 TS |
95 | err = snd_ff_create_hwdep_devices(ff); |
96 | if (err < 0) | |
97 | goto error; | |
f656edd5 | 98 | |
c31909fa TS |
99 | if (ff->spec->protocol->msg_parser_size > 0) { |
100 | ff->msg_parser = kzalloc(ff->spec->protocol->msg_parser_size, GFP_KERNEL); | |
101 | if (!ff->msg_parser) { | |
102 | err = -ENOMEM; | |
103 | goto error; | |
104 | } | |
105 | } | |
106 | ||
ee5f0b32 | 107 | err = snd_card_register(card); |
324540c4 TS |
108 | if (err < 0) |
109 | goto error; | |
110 | ||
17c4e5ea | 111 | return 0; |
ee5f0b32 TS |
112 | error: |
113 | snd_card_free(card); | |
114 | return err; | |
17c4e5ea TS |
115 | } |
116 | ||
117 | static void snd_ff_update(struct fw_unit *unit) | |
118 | { | |
324540c4 TS |
119 | struct snd_ff *ff = dev_get_drvdata(&unit->device); |
120 | ||
73f5537f | 121 | snd_ff_transaction_reregister(ff); |
75d6d898 | 122 | |
ee5f0b32 | 123 | snd_ff_stream_update_duplex(ff); |
17c4e5ea TS |
124 | } |
125 | ||
126 | static void snd_ff_remove(struct fw_unit *unit) | |
127 | { | |
128 | struct snd_ff *ff = dev_get_drvdata(&unit->device); | |
129 | ||
ee5f0b32 TS |
130 | // Block till all of ALSA character devices are released. |
131 | snd_card_free(ff->card); | |
17c4e5ea TS |
132 | } |
133 | ||
a91f6760 | 134 | static const struct snd_ff_spec spec_ff800 = { |
fc716397 TS |
135 | .pcm_capture_channels = {28, 20, 12}, |
136 | .pcm_playback_channels = {28, 20, 12}, | |
a91f6760 TS |
137 | .midi_in_ports = 1, |
138 | .midi_out_ports = 1, | |
139 | .protocol = &snd_ff_protocol_ff800, | |
6d1ef1bb | 140 | .midi_high_addr = 0x000200000320ull, |
90089677 | 141 | .midi_addr_range = 12, |
481e09ac | 142 | .midi_rx_addrs = {0x000080180000ull, 0}, |
a91f6760 TS |
143 | }; |
144 | ||
782fbec7 | 145 | static const struct snd_ff_spec spec_ff400 = { |
76fdb3a9 TS |
146 | .pcm_capture_channels = {18, 14, 10}, |
147 | .pcm_playback_channels = {18, 14, 10}, | |
148 | .midi_in_ports = 2, | |
149 | .midi_out_ports = 2, | |
150 | .protocol = &snd_ff_protocol_ff400, | |
6d1ef1bb | 151 | .midi_high_addr = 0x0000801003f4ull, |
90089677 | 152 | .midi_addr_range = SND_FF_MAXIMIM_MIDI_QUADS * 4, |
481e09ac | 153 | .midi_rx_addrs = {0x000080180000ull, 0x000080190000ull}, |
76fdb3a9 TS |
154 | }; |
155 | ||
fd1cc9de | 156 | static const struct snd_ff_spec spec_ucx = { |
fd1cc9de TS |
157 | .pcm_capture_channels = {18, 14, 12}, |
158 | .pcm_playback_channels = {18, 14, 12}, | |
73f5537f | 159 | .midi_in_ports = 2, |
f0f9f497 | 160 | .midi_out_ports = 2, |
fd1cc9de | 161 | .protocol = &snd_ff_protocol_latter, |
73f5537f TS |
162 | .midi_high_addr = 0xffff00000034ull, |
163 | .midi_addr_range = 0x80, | |
481e09ac | 164 | .midi_rx_addrs = {0xffff00000030ull, 0xffff00000030ull}, |
fd1cc9de TS |
165 | }; |
166 | ||
1f65e668 | 167 | static const struct snd_ff_spec spec_ufx_802 = { |
062bb452 TS |
168 | .pcm_capture_channels = {30, 22, 14}, |
169 | .pcm_playback_channels = {30, 22, 14}, | |
170 | .midi_in_ports = 1, | |
171 | .midi_out_ports = 1, | |
172 | .protocol = &snd_ff_protocol_latter, | |
173 | .midi_high_addr = 0xffff00000034ull, | |
174 | .midi_addr_range = 0x80, | |
175 | .midi_rx_addrs = {0xffff00000030ull, 0xffff00000030ull}, | |
176 | }; | |
177 | ||
17c4e5ea | 178 | static const struct ieee1394_device_id snd_ff_id_table[] = { |
a91f6760 TS |
179 | /* Fireface 800 */ |
180 | { | |
181 | .match_flags = IEEE1394_MATCH_VENDOR_ID | | |
182 | IEEE1394_MATCH_SPECIFIER_ID | | |
183 | IEEE1394_MATCH_VERSION | | |
184 | IEEE1394_MATCH_MODEL_ID, | |
185 | .vendor_id = OUI_RME, | |
186 | .specifier_id = OUI_RME, | |
c52f232e | 187 | .version = SND_FF_UNIT_VERSION_FF800, |
a91f6760 TS |
188 | .model_id = 0x101800, |
189 | .driver_data = (kernel_ulong_t)&spec_ff800, | |
190 | }, | |
76fdb3a9 TS |
191 | /* Fireface 400 */ |
192 | { | |
193 | .match_flags = IEEE1394_MATCH_VENDOR_ID | | |
194 | IEEE1394_MATCH_SPECIFIER_ID | | |
195 | IEEE1394_MATCH_VERSION | | |
196 | IEEE1394_MATCH_MODEL_ID, | |
197 | .vendor_id = OUI_RME, | |
a91f6760 | 198 | .specifier_id = OUI_RME, |
c52f232e | 199 | .version = SND_FF_UNIT_VERSION_FF400, |
76fdb3a9 TS |
200 | .model_id = 0x101800, |
201 | .driver_data = (kernel_ulong_t)&spec_ff400, | |
202 | }, | |
1f65e668 TS |
203 | // Fireface UFX. |
204 | { | |
205 | .match_flags = IEEE1394_MATCH_VENDOR_ID | | |
206 | IEEE1394_MATCH_SPECIFIER_ID | | |
207 | IEEE1394_MATCH_VERSION | | |
208 | IEEE1394_MATCH_MODEL_ID, | |
209 | .vendor_id = OUI_RME, | |
210 | .specifier_id = OUI_RME, | |
211 | .version = SND_FF_UNIT_VERSION_UFX, | |
212 | .model_id = 0x101800, | |
213 | .driver_data = (kernel_ulong_t)&spec_ufx_802, | |
214 | }, | |
fd1cc9de TS |
215 | // Fireface UCX. |
216 | { | |
217 | .match_flags = IEEE1394_MATCH_VENDOR_ID | | |
218 | IEEE1394_MATCH_SPECIFIER_ID | | |
219 | IEEE1394_MATCH_VERSION | | |
220 | IEEE1394_MATCH_MODEL_ID, | |
221 | .vendor_id = OUI_RME, | |
222 | .specifier_id = OUI_RME, | |
c52f232e | 223 | .version = SND_FF_UNIT_VERSION_UCX, |
fd1cc9de TS |
224 | .model_id = 0x101800, |
225 | .driver_data = (kernel_ulong_t)&spec_ucx, | |
226 | }, | |
062bb452 TS |
227 | // Fireface 802. |
228 | { | |
229 | .match_flags = IEEE1394_MATCH_VENDOR_ID | | |
230 | IEEE1394_MATCH_SPECIFIER_ID | | |
231 | IEEE1394_MATCH_VERSION | | |
232 | IEEE1394_MATCH_MODEL_ID, | |
233 | .vendor_id = OUI_RME, | |
234 | .specifier_id = OUI_RME, | |
235 | .version = SND_FF_UNIT_VERSION_802, | |
236 | .model_id = 0x101800, | |
1f65e668 | 237 | .driver_data = (kernel_ulong_t)&spec_ufx_802, |
062bb452 | 238 | }, |
17c4e5ea TS |
239 | {} |
240 | }; | |
241 | MODULE_DEVICE_TABLE(ieee1394, snd_ff_id_table); | |
242 | ||
243 | static struct fw_driver ff_driver = { | |
244 | .driver = { | |
245 | .owner = THIS_MODULE, | |
9c0d16ac | 246 | .name = KBUILD_MODNAME, |
17c4e5ea TS |
247 | .bus = &fw_bus_type, |
248 | }, | |
249 | .probe = snd_ff_probe, | |
250 | .update = snd_ff_update, | |
251 | .remove = snd_ff_remove, | |
252 | .id_table = snd_ff_id_table, | |
253 | }; | |
254 | ||
255 | static int __init snd_ff_init(void) | |
256 | { | |
257 | return driver_register(&ff_driver.driver); | |
258 | } | |
259 | ||
260 | static void __exit snd_ff_exit(void) | |
261 | { | |
262 | driver_unregister(&ff_driver.driver); | |
263 | } | |
264 | ||
265 | module_init(snd_ff_init); | |
266 | module_exit(snd_ff_exit); |