diff options
Diffstat (limited to 'sound/firewire')
-rw-r--r-- | sound/firewire/Kconfig | 25 | ||||
-rw-r--r-- | sound/firewire/Makefile | 6 | ||||
-rw-r--r-- | sound/firewire/amdtp.c | 549 | ||||
-rw-r--r-- | sound/firewire/amdtp.h | 157 | ||||
-rw-r--r-- | sound/firewire/cmp.c | 305 | ||||
-rw-r--r-- | sound/firewire/cmp.h | 41 | ||||
-rw-r--r-- | sound/firewire/fcp.c | 223 | ||||
-rw-r--r-- | sound/firewire/fcp.h | 12 | ||||
-rw-r--r-- | sound/firewire/iso-resources.c | 224 | ||||
-rw-r--r-- | sound/firewire/iso-resources.h | 39 | ||||
-rw-r--r-- | sound/firewire/lib.c | 85 | ||||
-rw-r--r-- | sound/firewire/lib.h | 19 | ||||
-rw-r--r-- | sound/firewire/packets-buffer.c | 74 | ||||
-rw-r--r-- | sound/firewire/packets-buffer.h | 26 | ||||
-rw-r--r-- | sound/firewire/speakers.c | 855 |
15 files changed, 2640 insertions, 0 deletions
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig new file mode 100644 index 000000000000..e486f48660fb --- /dev/null +++ b/sound/firewire/Kconfig | |||
@@ -0,0 +1,25 @@ | |||
1 | menuconfig SND_FIREWIRE | ||
2 | bool "FireWire sound devices" | ||
3 | depends on FIREWIRE | ||
4 | default y | ||
5 | help | ||
6 | Support for IEEE-1394/FireWire/iLink sound devices. | ||
7 | |||
8 | if SND_FIREWIRE && FIREWIRE | ||
9 | |||
10 | config SND_FIREWIRE_LIB | ||
11 | tristate | ||
12 | depends on SND_PCM | ||
13 | |||
14 | config SND_FIREWIRE_SPEAKERS | ||
15 | tristate "FireWire speakers" | ||
16 | select SND_PCM | ||
17 | select SND_FIREWIRE_LIB | ||
18 | help | ||
19 | Say Y here to include support for the Griffin FireWave Surround | ||
20 | and the LaCie FireWire Speakers. | ||
21 | |||
22 | To compile this driver as a module, choose M here: the module | ||
23 | will be called snd-firewire-speakers. | ||
24 | |||
25 | endif # SND_FIREWIRE | ||
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile new file mode 100644 index 000000000000..e5b1634d9ad4 --- /dev/null +++ b/sound/firewire/Makefile | |||
@@ -0,0 +1,6 @@ | |||
1 | snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ | ||
2 | fcp.o cmp.o amdtp.o | ||
3 | snd-firewire-speakers-objs := speakers.o | ||
4 | |||
5 | obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o | ||
6 | obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o | ||
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c new file mode 100644 index 000000000000..09f70ee4d04a --- /dev/null +++ b/sound/firewire/amdtp.c | |||
@@ -0,0 +1,549 @@ | |||
1 | /* | ||
2 | * Audio and Music Data Transmission Protocol (IEC 61883-6) streams | ||
3 | * with Common Isochronous Packet (IEC 61883-1) headers | ||
4 | * | ||
5 | * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | ||
6 | * Licensed under the terms of the GNU General Public License, version 2. | ||
7 | */ | ||
8 | |||
9 | #include <linux/device.h> | ||
10 | #include <linux/err.h> | ||
11 | #include <linux/firewire.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <sound/pcm.h> | ||
15 | #include "amdtp.h" | ||
16 | |||
17 | #define TICKS_PER_CYCLE 3072 | ||
18 | #define CYCLES_PER_SECOND 8000 | ||
19 | #define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND) | ||
20 | |||
21 | #define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 µs */ | ||
22 | |||
23 | #define TAG_CIP 1 | ||
24 | |||
25 | #define CIP_EOH (1u << 31) | ||
26 | #define CIP_FMT_AM (0x10 << 24) | ||
27 | #define AMDTP_FDF_AM824 (0 << 19) | ||
28 | #define AMDTP_FDF_SFC_SHIFT 16 | ||
29 | |||
30 | /* TODO: make these configurable */ | ||
31 | #define INTERRUPT_INTERVAL 16 | ||
32 | #define QUEUE_LENGTH 48 | ||
33 | |||
34 | /** | ||
35 | * amdtp_out_stream_init - initialize an AMDTP output stream structure | ||
36 | * @s: the AMDTP output stream to initialize | ||
37 | * @unit: the target of the stream | ||
38 | * @flags: the packet transmission method to use | ||
39 | */ | ||
40 | int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, | ||
41 | enum cip_out_flags flags) | ||
42 | { | ||
43 | if (flags != CIP_NONBLOCKING) | ||
44 | return -EINVAL; | ||
45 | |||
46 | s->unit = fw_unit_get(unit); | ||
47 | s->flags = flags; | ||
48 | s->context = ERR_PTR(-1); | ||
49 | mutex_init(&s->mutex); | ||
50 | |||
51 | return 0; | ||
52 | } | ||
53 | EXPORT_SYMBOL(amdtp_out_stream_init); | ||
54 | |||
55 | /** | ||
56 | * amdtp_out_stream_destroy - free stream resources | ||
57 | * @s: the AMDTP output stream to destroy | ||
58 | */ | ||
59 | void amdtp_out_stream_destroy(struct amdtp_out_stream *s) | ||
60 | { | ||
61 | WARN_ON(!IS_ERR(s->context)); | ||
62 | mutex_destroy(&s->mutex); | ||
63 | fw_unit_put(s->unit); | ||
64 | } | ||
65 | EXPORT_SYMBOL(amdtp_out_stream_destroy); | ||
66 | |||
67 | /** | ||
68 | * amdtp_out_stream_set_rate - set the sample rate | ||
69 | * @s: the AMDTP output stream to configure | ||
70 | * @rate: the sample rate | ||
71 | * | ||
72 | * The sample rate must be set before the stream is started, and must not be | ||
73 | * changed while the stream is running. | ||
74 | */ | ||
75 | void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate) | ||
76 | { | ||
77 | static const struct { | ||
78 | unsigned int rate; | ||
79 | unsigned int syt_interval; | ||
80 | } rate_info[] = { | ||
81 | [CIP_SFC_32000] = { 32000, 8, }, | ||
82 | [CIP_SFC_44100] = { 44100, 8, }, | ||
83 | [CIP_SFC_48000] = { 48000, 8, }, | ||
84 | [CIP_SFC_88200] = { 88200, 16, }, | ||
85 | [CIP_SFC_96000] = { 96000, 16, }, | ||
86 | [CIP_SFC_176400] = { 176400, 32, }, | ||
87 | [CIP_SFC_192000] = { 192000, 32, }, | ||
88 | }; | ||
89 | unsigned int sfc; | ||
90 | |||
91 | if (WARN_ON(!IS_ERR(s->context))) | ||
92 | return; | ||
93 | |||
94 | for (sfc = 0; sfc < ARRAY_SIZE(rate_info); ++sfc) | ||
95 | if (rate_info[sfc].rate == rate) { | ||
96 | s->sfc = sfc; | ||
97 | s->syt_interval = rate_info[sfc].syt_interval; | ||
98 | return; | ||
99 | } | ||
100 | WARN_ON(1); | ||
101 | } | ||
102 | EXPORT_SYMBOL(amdtp_out_stream_set_rate); | ||
103 | |||
104 | /** | ||
105 | * amdtp_out_stream_get_max_payload - get the stream's packet size | ||
106 | * @s: the AMDTP output stream | ||
107 | * | ||
108 | * This function must not be called before the stream has been configured | ||
109 | * with amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and | ||
110 | * amdtp_out_stream_set_midi(). | ||
111 | */ | ||
112 | unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s) | ||
113 | { | ||
114 | static const unsigned int max_data_blocks[] = { | ||
115 | [CIP_SFC_32000] = 4, | ||
116 | [CIP_SFC_44100] = 6, | ||
117 | [CIP_SFC_48000] = 6, | ||
118 | [CIP_SFC_88200] = 12, | ||
119 | [CIP_SFC_96000] = 12, | ||
120 | [CIP_SFC_176400] = 23, | ||
121 | [CIP_SFC_192000] = 24, | ||
122 | }; | ||
123 | |||
124 | s->data_block_quadlets = s->pcm_channels; | ||
125 | s->data_block_quadlets += DIV_ROUND_UP(s->midi_ports, 8); | ||
126 | |||
127 | return 8 + max_data_blocks[s->sfc] * 4 * s->data_block_quadlets; | ||
128 | } | ||
129 | EXPORT_SYMBOL(amdtp_out_stream_get_max_payload); | ||
130 | |||
131 | static void amdtp_write_s16(struct amdtp_out_stream *s, | ||
132 | struct snd_pcm_substream *pcm, | ||
133 | __be32 *buffer, unsigned int frames); | ||
134 | static void amdtp_write_s32(struct amdtp_out_stream *s, | ||
135 | struct snd_pcm_substream *pcm, | ||
136 | __be32 *buffer, unsigned int frames); | ||
137 | |||
138 | /** | ||
139 | * amdtp_out_stream_set_pcm_format - set the PCM format | ||
140 | * @s: the AMDTP output stream to configure | ||
141 | * @format: the format of the ALSA PCM device | ||
142 | * | ||
143 | * The sample format must be set before the stream is started, and must not be | ||
144 | * changed while the stream is running. | ||
145 | */ | ||
146 | void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, | ||
147 | snd_pcm_format_t format) | ||
148 | { | ||
149 | if (WARN_ON(!IS_ERR(s->context))) | ||
150 | return; | ||
151 | |||
152 | switch (format) { | ||
153 | default: | ||
154 | WARN_ON(1); | ||
155 | /* fall through */ | ||
156 | case SNDRV_PCM_FORMAT_S16: | ||
157 | s->transfer_samples = amdtp_write_s16; | ||
158 | break; | ||
159 | case SNDRV_PCM_FORMAT_S32: | ||
160 | s->transfer_samples = amdtp_write_s32; | ||
161 | break; | ||
162 | } | ||
163 | } | ||
164 | EXPORT_SYMBOL(amdtp_out_stream_set_pcm_format); | ||
165 | |||
166 | static unsigned int calculate_data_blocks(struct amdtp_out_stream *s) | ||
167 | { | ||
168 | unsigned int phase, data_blocks; | ||
169 | |||
170 | if (!cip_sfc_is_base_44100(s->sfc)) { | ||
171 | /* Sample_rate / 8000 is an integer, and precomputed. */ | ||
172 | data_blocks = s->data_block_state; | ||
173 | } else { | ||
174 | phase = s->data_block_state; | ||
175 | |||
176 | /* | ||
177 | * This calculates the number of data blocks per packet so that | ||
178 | * 1) the overall rate is correct and exactly synchronized to | ||
179 | * the bus clock, and | ||
180 | * 2) packets with a rounded-up number of blocks occur as early | ||
181 | * as possible in the sequence (to prevent underruns of the | ||
182 | * device's buffer). | ||
183 | */ | ||
184 | if (s->sfc == CIP_SFC_44100) | ||
185 | /* 6 6 5 6 5 6 5 ... */ | ||
186 | data_blocks = 5 + ((phase & 1) ^ | ||
187 | (phase == 0 || phase >= 40)); | ||
188 | else | ||
189 | /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */ | ||
190 | data_blocks = 11 * (s->sfc >> 1) + (phase == 0); | ||
191 | if (++phase >= (80 >> (s->sfc >> 1))) | ||
192 | phase = 0; | ||
193 | s->data_block_state = phase; | ||
194 | } | ||
195 | |||
196 | return data_blocks; | ||
197 | } | ||
198 | |||
199 | static unsigned int calculate_syt(struct amdtp_out_stream *s, | ||
200 | unsigned int cycle) | ||
201 | { | ||
202 | unsigned int syt_offset, phase, index, syt; | ||
203 | |||
204 | if (s->last_syt_offset < TICKS_PER_CYCLE) { | ||
205 | if (!cip_sfc_is_base_44100(s->sfc)) | ||
206 | syt_offset = s->last_syt_offset + s->syt_offset_state; | ||
207 | else { | ||
208 | /* | ||
209 | * The time, in ticks, of the n'th SYT_INTERVAL sample is: | ||
210 | * n * SYT_INTERVAL * 24576000 / sample_rate | ||
211 | * Modulo TICKS_PER_CYCLE, the difference between successive | ||
212 | * elements is about 1386.23. Rounding the results of this | ||
213 | * formula to the SYT precision results in a sequence of | ||
214 | * differences that begins with: | ||
215 | * 1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ... | ||
216 | * This code generates _exactly_ the same sequence. | ||
217 | */ | ||
218 | phase = s->syt_offset_state; | ||
219 | index = phase % 13; | ||
220 | syt_offset = s->last_syt_offset; | ||
221 | syt_offset += 1386 + ((index && !(index & 3)) || | ||
222 | phase == 146); | ||
223 | if (++phase >= 147) | ||
224 | phase = 0; | ||
225 | s->syt_offset_state = phase; | ||
226 | } | ||
227 | } else | ||
228 | syt_offset = s->last_syt_offset - TICKS_PER_CYCLE; | ||
229 | s->last_syt_offset = syt_offset; | ||
230 | |||
231 | syt_offset += TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; | ||
232 | syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; | ||
233 | syt += syt_offset % TICKS_PER_CYCLE; | ||
234 | |||
235 | return syt & 0xffff; | ||
236 | } | ||
237 | |||
238 | static void amdtp_write_s32(struct amdtp_out_stream *s, | ||
239 | struct snd_pcm_substream *pcm, | ||
240 | __be32 *buffer, unsigned int frames) | ||
241 | { | ||
242 | struct snd_pcm_runtime *runtime = pcm->runtime; | ||
243 | unsigned int channels, remaining_frames, frame_step, i, c; | ||
244 | const u32 *src; | ||
245 | |||
246 | channels = s->pcm_channels; | ||
247 | src = (void *)runtime->dma_area + | ||
248 | s->pcm_buffer_pointer * (runtime->frame_bits / 8); | ||
249 | remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; | ||
250 | frame_step = s->data_block_quadlets - channels; | ||
251 | |||
252 | for (i = 0; i < frames; ++i) { | ||
253 | for (c = 0; c < channels; ++c) { | ||
254 | *buffer = cpu_to_be32((*src >> 8) | 0x40000000); | ||
255 | src++; | ||
256 | buffer++; | ||
257 | } | ||
258 | buffer += frame_step; | ||
259 | if (--remaining_frames == 0) | ||
260 | src = (void *)runtime->dma_area; | ||
261 | } | ||
262 | } | ||
263 | |||
264 | static void amdtp_write_s16(struct amdtp_out_stream *s, | ||
265 | struct snd_pcm_substream *pcm, | ||
266 | __be32 *buffer, unsigned int frames) | ||
267 | { | ||
268 | struct snd_pcm_runtime *runtime = pcm->runtime; | ||
269 | unsigned int channels, remaining_frames, frame_step, i, c; | ||
270 | const u16 *src; | ||
271 | |||
272 | channels = s->pcm_channels; | ||
273 | src = (void *)runtime->dma_area + | ||
274 | s->pcm_buffer_pointer * (runtime->frame_bits / 8); | ||
275 | remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; | ||
276 | frame_step = s->data_block_quadlets - channels; | ||
277 | |||
278 | for (i = 0; i < frames; ++i) { | ||
279 | for (c = 0; c < channels; ++c) { | ||
280 | *buffer = cpu_to_be32((*src << 8) | 0x40000000); | ||
281 | src++; | ||
282 | buffer++; | ||
283 | } | ||
284 | buffer += frame_step; | ||
285 | if (--remaining_frames == 0) | ||
286 | src = (void *)runtime->dma_area; | ||
287 | } | ||
288 | } | ||
289 | |||
290 | static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s, | ||
291 | __be32 *buffer, unsigned int frames) | ||
292 | { | ||
293 | unsigned int i, c; | ||
294 | |||
295 | for (i = 0; i < frames; ++i) { | ||
296 | for (c = 0; c < s->pcm_channels; ++c) | ||
297 | buffer[c] = cpu_to_be32(0x40000000); | ||
298 | buffer += s->data_block_quadlets; | ||
299 | } | ||
300 | } | ||
301 | |||
302 | static void amdtp_fill_midi(struct amdtp_out_stream *s, | ||
303 | __be32 *buffer, unsigned int frames) | ||
304 | { | ||
305 | unsigned int i; | ||
306 | |||
307 | for (i = 0; i < frames; ++i) | ||
308 | buffer[s->pcm_channels + i * s->data_block_quadlets] = | ||
309 | cpu_to_be32(0x80000000); | ||
310 | } | ||
311 | |||
312 | static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) | ||
313 | { | ||
314 | __be32 *buffer; | ||
315 | unsigned int data_blocks, syt, ptr; | ||
316 | struct snd_pcm_substream *pcm; | ||
317 | struct fw_iso_packet packet; | ||
318 | int err; | ||
319 | |||
320 | data_blocks = calculate_data_blocks(s); | ||
321 | syt = calculate_syt(s, cycle); | ||
322 | |||
323 | buffer = s->buffer.packets[s->packet_counter].buffer; | ||
324 | buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | | ||
325 | (s->data_block_quadlets << 16) | | ||
326 | s->data_block_counter); | ||
327 | buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 | | ||
328 | (s->sfc << AMDTP_FDF_SFC_SHIFT) | syt); | ||
329 | buffer += 2; | ||
330 | |||
331 | pcm = ACCESS_ONCE(s->pcm); | ||
332 | if (pcm) | ||
333 | s->transfer_samples(s, pcm, buffer, data_blocks); | ||
334 | else | ||
335 | amdtp_fill_pcm_silence(s, buffer, data_blocks); | ||
336 | if (s->midi_ports) | ||
337 | amdtp_fill_midi(s, buffer, data_blocks); | ||
338 | |||
339 | s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff; | ||
340 | |||
341 | packet.payload_length = 8 + data_blocks * 4 * s->data_block_quadlets; | ||
342 | packet.interrupt = IS_ALIGNED(s->packet_counter + 1, | ||
343 | INTERRUPT_INTERVAL); | ||
344 | packet.skip = 0; | ||
345 | packet.tag = TAG_CIP; | ||
346 | packet.sy = 0; | ||
347 | packet.header_length = 0; | ||
348 | |||
349 | err = fw_iso_context_queue(s->context, &packet, &s->buffer.iso_buffer, | ||
350 | s->buffer.packets[s->packet_counter].offset); | ||
351 | if (err < 0) | ||
352 | dev_err(&s->unit->device, "queueing error: %d\n", err); | ||
353 | |||
354 | if (++s->packet_counter >= QUEUE_LENGTH) | ||
355 | s->packet_counter = 0; | ||
356 | |||
357 | if (pcm) { | ||
358 | ptr = s->pcm_buffer_pointer + data_blocks; | ||
359 | if (ptr >= pcm->runtime->buffer_size) | ||
360 | ptr -= pcm->runtime->buffer_size; | ||
361 | ACCESS_ONCE(s->pcm_buffer_pointer) = ptr; | ||
362 | |||
363 | s->pcm_period_pointer += data_blocks; | ||
364 | if (s->pcm_period_pointer >= pcm->runtime->period_size) { | ||
365 | s->pcm_period_pointer -= pcm->runtime->period_size; | ||
366 | snd_pcm_period_elapsed(pcm); | ||
367 | } | ||
368 | } | ||
369 | } | ||
370 | |||
371 | static void out_packet_callback(struct fw_iso_context *context, u32 cycle, | ||
372 | size_t header_length, void *header, void *data) | ||
373 | { | ||
374 | struct amdtp_out_stream *s = data; | ||
375 | unsigned int i, packets = header_length / 4; | ||
376 | |||
377 | /* | ||
378 | * Compute the cycle of the last queued packet. | ||
379 | * (We need only the four lowest bits for the SYT, so we can ignore | ||
380 | * that bits 0-11 must wrap around at 3072.) | ||
381 | */ | ||
382 | cycle += QUEUE_LENGTH - packets; | ||
383 | |||
384 | for (i = 0; i < packets; ++i) | ||
385 | queue_out_packet(s, ++cycle); | ||
386 | } | ||
387 | |||
388 | static int queue_initial_skip_packets(struct amdtp_out_stream *s) | ||
389 | { | ||
390 | struct fw_iso_packet skip_packet = { | ||
391 | .skip = 1, | ||
392 | }; | ||
393 | unsigned int i; | ||
394 | int err; | ||
395 | |||
396 | for (i = 0; i < QUEUE_LENGTH; ++i) { | ||
397 | skip_packet.interrupt = IS_ALIGNED(s->packet_counter + 1, | ||
398 | INTERRUPT_INTERVAL); | ||
399 | err = fw_iso_context_queue(s->context, &skip_packet, NULL, 0); | ||
400 | if (err < 0) | ||
401 | return err; | ||
402 | if (++s->packet_counter >= QUEUE_LENGTH) | ||
403 | s->packet_counter = 0; | ||
404 | } | ||
405 | |||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | /** | ||
410 | * amdtp_out_stream_start - start sending packets | ||
411 | * @s: the AMDTP output stream to start | ||
412 | * @channel: the isochronous channel on the bus | ||
413 | * @speed: firewire speed code | ||
414 | * | ||
415 | * The stream cannot be started until it has been configured with | ||
416 | * amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and | ||
417 | * amdtp_out_stream_set_midi(); and it must be started before any | ||
418 | * PCM or MIDI device can be started. | ||
419 | */ | ||
420 | int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) | ||
421 | { | ||
422 | static const struct { | ||
423 | unsigned int data_block; | ||
424 | unsigned int syt_offset; | ||
425 | } initial_state[] = { | ||
426 | [CIP_SFC_32000] = { 4, 3072 }, | ||
427 | [CIP_SFC_48000] = { 6, 1024 }, | ||
428 | [CIP_SFC_96000] = { 12, 1024 }, | ||
429 | [CIP_SFC_192000] = { 24, 1024 }, | ||
430 | [CIP_SFC_44100] = { 0, 67 }, | ||
431 | [CIP_SFC_88200] = { 0, 67 }, | ||
432 | [CIP_SFC_176400] = { 0, 67 }, | ||
433 | }; | ||
434 | int err; | ||
435 | |||
436 | mutex_lock(&s->mutex); | ||
437 | |||
438 | if (WARN_ON(!IS_ERR(s->context) || | ||
439 | (!s->pcm_channels && !s->midi_ports))) { | ||
440 | err = -EBADFD; | ||
441 | goto err_unlock; | ||
442 | } | ||
443 | |||
444 | s->data_block_state = initial_state[s->sfc].data_block; | ||
445 | s->syt_offset_state = initial_state[s->sfc].syt_offset; | ||
446 | s->last_syt_offset = TICKS_PER_CYCLE; | ||
447 | |||
448 | err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, | ||
449 | amdtp_out_stream_get_max_payload(s), | ||
450 | DMA_TO_DEVICE); | ||
451 | if (err < 0) | ||
452 | goto err_unlock; | ||
453 | |||
454 | s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, | ||
455 | FW_ISO_CONTEXT_TRANSMIT, | ||
456 | channel, speed, 0, | ||
457 | out_packet_callback, s); | ||
458 | if (IS_ERR(s->context)) { | ||
459 | err = PTR_ERR(s->context); | ||
460 | if (err == -EBUSY) | ||
461 | dev_err(&s->unit->device, | ||
462 | "no free output stream on this controller\n"); | ||
463 | goto err_buffer; | ||
464 | } | ||
465 | |||
466 | amdtp_out_stream_update(s); | ||
467 | |||
468 | s->packet_counter = 0; | ||
469 | s->data_block_counter = 0; | ||
470 | err = queue_initial_skip_packets(s); | ||
471 | if (err < 0) | ||
472 | goto err_context; | ||
473 | |||
474 | err = fw_iso_context_start(s->context, -1, 0, 0); | ||
475 | if (err < 0) | ||
476 | goto err_context; | ||
477 | |||
478 | mutex_unlock(&s->mutex); | ||
479 | |||
480 | return 0; | ||
481 | |||
482 | err_context: | ||
483 | fw_iso_context_destroy(s->context); | ||
484 | s->context = ERR_PTR(-1); | ||
485 | err_buffer: | ||
486 | iso_packets_buffer_destroy(&s->buffer, s->unit); | ||
487 | err_unlock: | ||
488 | mutex_unlock(&s->mutex); | ||
489 | |||
490 | return err; | ||
491 | } | ||
492 | EXPORT_SYMBOL(amdtp_out_stream_start); | ||
493 | |||
494 | /** | ||
495 | * amdtp_out_stream_update - update the stream after a bus reset | ||
496 | * @s: the AMDTP output stream | ||
497 | */ | ||
498 | void amdtp_out_stream_update(struct amdtp_out_stream *s) | ||
499 | { | ||
500 | ACCESS_ONCE(s->source_node_id_field) = | ||
501 | (fw_parent_device(s->unit)->card->node_id & 0x3f) << 24; | ||
502 | } | ||
503 | EXPORT_SYMBOL(amdtp_out_stream_update); | ||
504 | |||
505 | /** | ||
506 | * amdtp_out_stream_stop - stop sending packets | ||
507 | * @s: the AMDTP output stream to stop | ||
508 | * | ||
509 | * All PCM and MIDI devices of the stream must be stopped before the stream | ||
510 | * itself can be stopped. | ||
511 | */ | ||
512 | void amdtp_out_stream_stop(struct amdtp_out_stream *s) | ||
513 | { | ||
514 | mutex_lock(&s->mutex); | ||
515 | |||
516 | if (IS_ERR(s->context)) { | ||
517 | mutex_unlock(&s->mutex); | ||
518 | return; | ||
519 | } | ||
520 | |||
521 | fw_iso_context_stop(s->context); | ||
522 | fw_iso_context_destroy(s->context); | ||
523 | s->context = ERR_PTR(-1); | ||
524 | iso_packets_buffer_destroy(&s->buffer, s->unit); | ||
525 | |||
526 | mutex_unlock(&s->mutex); | ||
527 | } | ||
528 | EXPORT_SYMBOL(amdtp_out_stream_stop); | ||
529 | |||
530 | /** | ||
531 | * amdtp_out_stream_pcm_abort - abort the running PCM device | ||
532 | * @s: the AMDTP stream about to be stopped | ||
533 | * | ||
534 | * If the isochronous stream needs to be stopped asynchronously, call this | ||
535 | * function first to stop the PCM device. | ||
536 | */ | ||
537 | void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s) | ||
538 | { | ||
539 | struct snd_pcm_substream *pcm; | ||
540 | |||
541 | pcm = ACCESS_ONCE(s->pcm); | ||
542 | if (pcm) { | ||
543 | snd_pcm_stream_lock_irq(pcm); | ||
544 | if (snd_pcm_running(pcm)) | ||
545 | snd_pcm_stop(pcm, SNDRV_PCM_STATE_XRUN); | ||
546 | snd_pcm_stream_unlock_irq(pcm); | ||
547 | } | ||
548 | } | ||
549 | EXPORT_SYMBOL(amdtp_out_stream_pcm_abort); | ||
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h new file mode 100644 index 000000000000..02dc1a664b55 --- /dev/null +++ b/sound/firewire/amdtp.h | |||
@@ -0,0 +1,157 @@ | |||
1 | #ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED | ||
2 | #define SOUND_FIREWIRE_AMDTP_H_INCLUDED | ||
3 | |||
4 | #include <linux/mutex.h> | ||
5 | #include <linux/spinlock.h> | ||
6 | #include "packets-buffer.h" | ||
7 | |||
8 | /** | ||
9 | * enum cip_out_flags - describes details of the streaming protocol | ||
10 | * @CIP_NONBLOCKING: In non-blocking mode, each packet contains | ||
11 | * sample_rate/8000 samples, with rounding up or down to adjust | ||
12 | * for clock skew and left-over fractional samples. This should | ||
13 | * be used if supported by the device. | ||
14 | */ | ||
15 | enum cip_out_flags { | ||
16 | CIP_NONBLOCKING = 0, | ||
17 | }; | ||
18 | |||
19 | /** | ||
20 | * enum cip_sfc - a stream's sample rate | ||
21 | */ | ||
22 | enum cip_sfc { | ||
23 | CIP_SFC_32000 = 0, | ||
24 | CIP_SFC_44100 = 1, | ||
25 | CIP_SFC_48000 = 2, | ||
26 | CIP_SFC_88200 = 3, | ||
27 | CIP_SFC_96000 = 4, | ||
28 | CIP_SFC_176400 = 5, | ||
29 | CIP_SFC_192000 = 6, | ||
30 | }; | ||
31 | |||
32 | #define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ | ||
33 | SNDRV_PCM_FMTBIT_S32) | ||
34 | |||
35 | struct fw_unit; | ||
36 | struct fw_iso_context; | ||
37 | struct snd_pcm_substream; | ||
38 | |||
39 | struct amdtp_out_stream { | ||
40 | struct fw_unit *unit; | ||
41 | enum cip_out_flags flags; | ||
42 | struct fw_iso_context *context; | ||
43 | struct mutex mutex; | ||
44 | |||
45 | enum cip_sfc sfc; | ||
46 | unsigned int data_block_quadlets; | ||
47 | unsigned int pcm_channels; | ||
48 | unsigned int midi_ports; | ||
49 | void (*transfer_samples)(struct amdtp_out_stream *s, | ||
50 | struct snd_pcm_substream *pcm, | ||
51 | __be32 *buffer, unsigned int frames); | ||
52 | |||
53 | unsigned int syt_interval; | ||
54 | unsigned int source_node_id_field; | ||
55 | struct iso_packets_buffer buffer; | ||
56 | |||
57 | struct snd_pcm_substream *pcm; | ||
58 | |||
59 | unsigned int packet_counter; | ||
60 | unsigned int data_block_counter; | ||
61 | |||
62 | unsigned int data_block_state; | ||
63 | |||
64 | unsigned int last_syt_offset; | ||
65 | unsigned int syt_offset_state; | ||
66 | |||
67 | unsigned int pcm_buffer_pointer; | ||
68 | unsigned int pcm_period_pointer; | ||
69 | }; | ||
70 | |||
71 | int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, | ||
72 | enum cip_out_flags flags); | ||
73 | void amdtp_out_stream_destroy(struct amdtp_out_stream *s); | ||
74 | |||
75 | void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate); | ||
76 | unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s); | ||
77 | |||
78 | int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed); | ||
79 | void amdtp_out_stream_update(struct amdtp_out_stream *s); | ||
80 | void amdtp_out_stream_stop(struct amdtp_out_stream *s); | ||
81 | |||
82 | void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, | ||
83 | snd_pcm_format_t format); | ||
84 | void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s); | ||
85 | |||
86 | /** | ||
87 | * amdtp_out_stream_set_pcm - configure format of PCM samples | ||
88 | * @s: the AMDTP output stream to be configured | ||
89 | * @pcm_channels: the number of PCM samples in each data block, to be encoded | ||
90 | * as AM824 multi-bit linear audio | ||
91 | * | ||
92 | * This function must not be called while the stream is running. | ||
93 | */ | ||
94 | static inline void amdtp_out_stream_set_pcm(struct amdtp_out_stream *s, | ||
95 | unsigned int pcm_channels) | ||
96 | { | ||
97 | s->pcm_channels = pcm_channels; | ||
98 | } | ||
99 | |||
100 | /** | ||
101 | * amdtp_out_stream_set_midi - configure format of MIDI data | ||
102 | * @s: the AMDTP output stream to be configured | ||
103 | * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) | ||
104 | * | ||
105 | * This function must not be called while the stream is running. | ||
106 | */ | ||
107 | static inline void amdtp_out_stream_set_midi(struct amdtp_out_stream *s, | ||
108 | unsigned int midi_ports) | ||
109 | { | ||
110 | s->midi_ports = midi_ports; | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * amdtp_out_stream_pcm_prepare - prepare PCM device for running | ||
115 | * @s: the AMDTP output stream | ||
116 | * | ||
117 | * This function should be called from the PCM device's .prepare callback. | ||
118 | */ | ||
119 | static inline void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s) | ||
120 | { | ||
121 | s->pcm_buffer_pointer = 0; | ||
122 | s->pcm_period_pointer = 0; | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device | ||
127 | * @s: the AMDTP output stream | ||
128 | * @pcm: the PCM device to be started, or %NULL to stop the current device | ||
129 | * | ||
130 | * Call this function on a running isochronous stream to enable the actual | ||
131 | * transmission of PCM data. This function should be called from the PCM | ||
132 | * device's .trigger callback. | ||
133 | */ | ||
134 | static inline void amdtp_out_stream_pcm_trigger(struct amdtp_out_stream *s, | ||
135 | struct snd_pcm_substream *pcm) | ||
136 | { | ||
137 | ACCESS_ONCE(s->pcm) = pcm; | ||
138 | } | ||
139 | |||
140 | /** | ||
141 | * amdtp_out_stream_pcm_pointer - get the PCM buffer position | ||
142 | * @s: the AMDTP output stream that transports the PCM data | ||
143 | * | ||
144 | * Returns the current buffer position, in frames. | ||
145 | */ | ||
146 | static inline unsigned long | ||
147 | amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s) | ||
148 | { | ||
149 | return ACCESS_ONCE(s->pcm_buffer_pointer); | ||
150 | } | ||
151 | |||
152 | static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc) | ||
153 | { | ||
154 | return sfc & 1; | ||
155 | } | ||
156 | |||
157 | #endif | ||
diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c new file mode 100644 index 000000000000..c992dab4bb95 --- /dev/null +++ b/sound/firewire/cmp.c | |||
@@ -0,0 +1,305 @@ | |||
1 | /* | ||
2 | * Connection Management Procedures (IEC 61883-1) helper functions | ||
3 | * | ||
4 | * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | ||
5 | * Licensed under the terms of the GNU General Public License, version 2. | ||
6 | */ | ||
7 | |||
8 | #include <linux/device.h> | ||
9 | #include <linux/firewire.h> | ||
10 | #include <linux/firewire-constants.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include "lib.h" | ||
14 | #include "iso-resources.h" | ||
15 | #include "cmp.h" | ||
16 | |||
17 | #define IMPR_SPEED_MASK 0xc0000000 | ||
18 | #define IMPR_SPEED_SHIFT 30 | ||
19 | #define IMPR_XSPEED_MASK 0x00000060 | ||
20 | #define IMPR_XSPEED_SHIFT 5 | ||
21 | #define IMPR_PLUGS_MASK 0x0000001f | ||
22 | |||
23 | #define IPCR_ONLINE 0x80000000 | ||
24 | #define IPCR_BCAST_CONN 0x40000000 | ||
25 | #define IPCR_P2P_CONN_MASK 0x3f000000 | ||
26 | #define IPCR_P2P_CONN_SHIFT 24 | ||
27 | #define IPCR_CHANNEL_MASK 0x003f0000 | ||
28 | #define IPCR_CHANNEL_SHIFT 16 | ||
29 | |||
30 | enum bus_reset_handling { | ||
31 | ABORT_ON_BUS_RESET, | ||
32 | SUCCEED_ON_BUS_RESET, | ||
33 | }; | ||
34 | |||
35 | static __attribute__((format(printf, 2, 3))) | ||
36 | void cmp_error(struct cmp_connection *c, const char *fmt, ...) | ||
37 | { | ||
38 | va_list va; | ||
39 | |||
40 | va_start(va, fmt); | ||
41 | dev_err(&c->resources.unit->device, "%cPCR%u: %pV", | ||
42 | 'i', c->pcr_index, &(struct va_format){ fmt, &va }); | ||
43 | va_end(va); | ||
44 | } | ||
45 | |||
46 | static int pcr_modify(struct cmp_connection *c, | ||
47 | __be32 (*modify)(struct cmp_connection *c, __be32 old), | ||
48 | int (*check)(struct cmp_connection *c, __be32 pcr), | ||
49 | enum bus_reset_handling bus_reset_handling) | ||
50 | { | ||
51 | struct fw_device *device = fw_parent_device(c->resources.unit); | ||
52 | __be32 *buffer = c->resources.buffer; | ||
53 | int generation = c->resources.generation; | ||
54 | int rcode, errors = 0; | ||
55 | __be32 old_arg; | ||
56 | int err; | ||
57 | |||
58 | buffer[0] = c->last_pcr_value; | ||
59 | for (;;) { | ||
60 | old_arg = buffer[0]; | ||
61 | buffer[1] = modify(c, buffer[0]); | ||
62 | |||
63 | rcode = fw_run_transaction( | ||
64 | device->card, TCODE_LOCK_COMPARE_SWAP, | ||
65 | device->node_id, generation, device->max_speed, | ||
66 | CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index), | ||
67 | buffer, 8); | ||
68 | |||
69 | if (rcode == RCODE_COMPLETE) { | ||
70 | if (buffer[0] == old_arg) /* success? */ | ||
71 | break; | ||
72 | |||
73 | if (check) { | ||
74 | err = check(c, buffer[0]); | ||
75 | if (err < 0) | ||
76 | return err; | ||
77 | } | ||
78 | } else if (rcode == RCODE_GENERATION) | ||
79 | goto bus_reset; | ||
80 | else if (rcode_is_permanent_error(rcode) || ++errors >= 3) | ||
81 | goto io_error; | ||
82 | } | ||
83 | c->last_pcr_value = buffer[1]; | ||
84 | |||
85 | return 0; | ||
86 | |||
87 | io_error: | ||
88 | cmp_error(c, "transaction failed: %s\n", rcode_string(rcode)); | ||
89 | return -EIO; | ||
90 | |||
91 | bus_reset: | ||
92 | return bus_reset_handling == ABORT_ON_BUS_RESET ? -EAGAIN : 0; | ||
93 | } | ||
94 | |||
95 | |||
96 | /** | ||
97 | * cmp_connection_init - initializes a connection manager | ||
98 | * @c: the connection manager to initialize | ||
99 | * @unit: a unit of the target device | ||
100 | * @ipcr_index: the index of the iPCR on the target device | ||
101 | */ | ||
102 | int cmp_connection_init(struct cmp_connection *c, | ||
103 | struct fw_unit *unit, | ||
104 | unsigned int ipcr_index) | ||
105 | { | ||
106 | __be32 impr_be; | ||
107 | u32 impr; | ||
108 | int err; | ||
109 | |||
110 | err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, | ||
111 | CSR_REGISTER_BASE + CSR_IMPR, | ||
112 | &impr_be, 4); | ||
113 | if (err < 0) | ||
114 | return err; | ||
115 | impr = be32_to_cpu(impr_be); | ||
116 | |||
117 | if (ipcr_index >= (impr & IMPR_PLUGS_MASK)) | ||
118 | return -EINVAL; | ||
119 | |||
120 | c->connected = false; | ||
121 | mutex_init(&c->mutex); | ||
122 | fw_iso_resources_init(&c->resources, unit); | ||
123 | c->last_pcr_value = cpu_to_be32(0x80000000); | ||
124 | c->pcr_index = ipcr_index; | ||
125 | c->max_speed = (impr & IMPR_SPEED_MASK) >> IMPR_SPEED_SHIFT; | ||
126 | if (c->max_speed == SCODE_BETA) | ||
127 | c->max_speed += (impr & IMPR_XSPEED_MASK) >> IMPR_XSPEED_SHIFT; | ||
128 | |||
129 | return 0; | ||
130 | } | ||
131 | EXPORT_SYMBOL(cmp_connection_init); | ||
132 | |||
133 | /** | ||
134 | * cmp_connection_destroy - free connection manager resources | ||
135 | * @c: the connection manager | ||
136 | */ | ||
137 | void cmp_connection_destroy(struct cmp_connection *c) | ||
138 | { | ||
139 | WARN_ON(c->connected); | ||
140 | mutex_destroy(&c->mutex); | ||
141 | fw_iso_resources_destroy(&c->resources); | ||
142 | } | ||
143 | EXPORT_SYMBOL(cmp_connection_destroy); | ||
144 | |||
145 | |||
146 | static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr) | ||
147 | { | ||
148 | ipcr &= ~cpu_to_be32(IPCR_BCAST_CONN | | ||
149 | IPCR_P2P_CONN_MASK | | ||
150 | IPCR_CHANNEL_MASK); | ||
151 | ipcr |= cpu_to_be32(1 << IPCR_P2P_CONN_SHIFT); | ||
152 | ipcr |= cpu_to_be32(c->resources.channel << IPCR_CHANNEL_SHIFT); | ||
153 | |||
154 | return ipcr; | ||
155 | } | ||
156 | |||
157 | static int ipcr_set_check(struct cmp_connection *c, __be32 ipcr) | ||
158 | { | ||
159 | if (ipcr & cpu_to_be32(IPCR_BCAST_CONN | | ||
160 | IPCR_P2P_CONN_MASK)) { | ||
161 | cmp_error(c, "plug is already in use\n"); | ||
162 | return -EBUSY; | ||
163 | } | ||
164 | if (!(ipcr & cpu_to_be32(IPCR_ONLINE))) { | ||
165 | cmp_error(c, "plug is not on-line\n"); | ||
166 | return -ECONNREFUSED; | ||
167 | } | ||
168 | |||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | /** | ||
173 | * cmp_connection_establish - establish a connection to the target | ||
174 | * @c: the connection manager | ||
175 | * @max_payload_bytes: the amount of data (including CIP headers) per packet | ||
176 | * | ||
177 | * This function establishes a point-to-point connection from the local | ||
178 | * computer to the target by allocating isochronous resources (channel and | ||
179 | * bandwidth) and setting the target's input plug control register. When this | ||
180 | * function succeeds, the caller is responsible for starting transmitting | ||
181 | * packets. | ||
182 | */ | ||
183 | int cmp_connection_establish(struct cmp_connection *c, | ||
184 | unsigned int max_payload_bytes) | ||
185 | { | ||
186 | int err; | ||
187 | |||
188 | if (WARN_ON(c->connected)) | ||
189 | return -EISCONN; | ||
190 | |||
191 | c->speed = min(c->max_speed, | ||
192 | fw_parent_device(c->resources.unit)->max_speed); | ||
193 | |||
194 | mutex_lock(&c->mutex); | ||
195 | |||
196 | retry_after_bus_reset: | ||
197 | err = fw_iso_resources_allocate(&c->resources, | ||
198 | max_payload_bytes, c->speed); | ||
199 | if (err < 0) | ||
200 | goto err_mutex; | ||
201 | |||
202 | err = pcr_modify(c, ipcr_set_modify, ipcr_set_check, | ||
203 | ABORT_ON_BUS_RESET); | ||
204 | if (err == -EAGAIN) { | ||
205 | fw_iso_resources_free(&c->resources); | ||
206 | goto retry_after_bus_reset; | ||
207 | } | ||
208 | if (err < 0) | ||
209 | goto err_resources; | ||
210 | |||
211 | c->connected = true; | ||
212 | |||
213 | mutex_unlock(&c->mutex); | ||
214 | |||
215 | return 0; | ||
216 | |||
217 | err_resources: | ||
218 | fw_iso_resources_free(&c->resources); | ||
219 | err_mutex: | ||
220 | mutex_unlock(&c->mutex); | ||
221 | |||
222 | return err; | ||
223 | } | ||
224 | EXPORT_SYMBOL(cmp_connection_establish); | ||
225 | |||
226 | /** | ||
227 | * cmp_connection_update - update the connection after a bus reset | ||
228 | * @c: the connection manager | ||
229 | * | ||
230 | * This function must be called from the driver's .update handler to reestablish | ||
231 | * any connection that might have been active. | ||
232 | * | ||
233 | * Returns zero on success, or a negative error code. On an error, the | ||
234 | * connection is broken and the caller must stop transmitting iso packets. | ||
235 | */ | ||
236 | int cmp_connection_update(struct cmp_connection *c) | ||
237 | { | ||
238 | int err; | ||
239 | |||
240 | mutex_lock(&c->mutex); | ||
241 | |||
242 | if (!c->connected) { | ||
243 | mutex_unlock(&c->mutex); | ||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | err = fw_iso_resources_update(&c->resources); | ||
248 | if (err < 0) | ||
249 | goto err_unconnect; | ||
250 | |||
251 | err = pcr_modify(c, ipcr_set_modify, ipcr_set_check, | ||
252 | SUCCEED_ON_BUS_RESET); | ||
253 | if (err < 0) | ||
254 | goto err_resources; | ||
255 | |||
256 | mutex_unlock(&c->mutex); | ||
257 | |||
258 | return 0; | ||
259 | |||
260 | err_resources: | ||
261 | fw_iso_resources_free(&c->resources); | ||
262 | err_unconnect: | ||
263 | c->connected = false; | ||
264 | mutex_unlock(&c->mutex); | ||
265 | |||
266 | return err; | ||
267 | } | ||
268 | EXPORT_SYMBOL(cmp_connection_update); | ||
269 | |||
270 | |||
271 | static __be32 ipcr_break_modify(struct cmp_connection *c, __be32 ipcr) | ||
272 | { | ||
273 | return ipcr & ~cpu_to_be32(IPCR_BCAST_CONN | IPCR_P2P_CONN_MASK); | ||
274 | } | ||
275 | |||
276 | /** | ||
277 | * cmp_connection_break - break the connection to the target | ||
278 | * @c: the connection manager | ||
279 | * | ||
280 | * This function deactives the connection in the target's input plug control | ||
281 | * register, and frees the isochronous resources of the connection. Before | ||
282 | * calling this function, the caller should cease transmitting packets. | ||
283 | */ | ||
284 | void cmp_connection_break(struct cmp_connection *c) | ||
285 | { | ||
286 | int err; | ||
287 | |||
288 | mutex_lock(&c->mutex); | ||
289 | |||
290 | if (!c->connected) { | ||
291 | mutex_unlock(&c->mutex); | ||
292 | return; | ||
293 | } | ||
294 | |||
295 | err = pcr_modify(c, ipcr_break_modify, NULL, SUCCEED_ON_BUS_RESET); | ||
296 | if (err < 0) | ||
297 | cmp_error(c, "plug is still connected\n"); | ||
298 | |||
299 | fw_iso_resources_free(&c->resources); | ||
300 | |||
301 | c->connected = false; | ||
302 | |||
303 | mutex_unlock(&c->mutex); | ||
304 | } | ||
305 | EXPORT_SYMBOL(cmp_connection_break); | ||
diff --git a/sound/firewire/cmp.h b/sound/firewire/cmp.h new file mode 100644 index 000000000000..f47de08feb12 --- /dev/null +++ b/sound/firewire/cmp.h | |||
@@ -0,0 +1,41 @@ | |||
1 | #ifndef SOUND_FIREWIRE_CMP_H_INCLUDED | ||
2 | #define SOUND_FIREWIRE_CMP_H_INCLUDED | ||
3 | |||
4 | #include <linux/mutex.h> | ||
5 | #include <linux/types.h> | ||
6 | #include "iso-resources.h" | ||
7 | |||
8 | struct fw_unit; | ||
9 | |||
10 | /** | ||
11 | * struct cmp_connection - manages an isochronous connection to a device | ||
12 | * @speed: the connection's actual speed | ||
13 | * | ||
14 | * This structure manages (using CMP) an isochronous stream from the local | ||
15 | * computer to a device's input plug (iPCR). | ||
16 | * | ||
17 | * There is no corresponding oPCR created on the local computer, so it is not | ||
18 | * possible to overlay connections on top of this one. | ||
19 | */ | ||
20 | struct cmp_connection { | ||
21 | int speed; | ||
22 | /* private: */ | ||
23 | bool connected; | ||
24 | struct mutex mutex; | ||
25 | struct fw_iso_resources resources; | ||
26 | __be32 last_pcr_value; | ||
27 | unsigned int pcr_index; | ||
28 | unsigned int max_speed; | ||
29 | }; | ||
30 | |||
31 | int cmp_connection_init(struct cmp_connection *connection, | ||
32 | struct fw_unit *unit, | ||
33 | unsigned int ipcr_index); | ||
34 | void cmp_connection_destroy(struct cmp_connection *connection); | ||
35 | |||
36 | int cmp_connection_establish(struct cmp_connection *connection, | ||
37 | unsigned int max_payload); | ||
38 | int cmp_connection_update(struct cmp_connection *connection); | ||
39 | void cmp_connection_break(struct cmp_connection *connection); | ||
40 | |||
41 | #endif | ||
diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c new file mode 100644 index 000000000000..c20bd9c8f5ab --- /dev/null +++ b/sound/firewire/fcp.c | |||
@@ -0,0 +1,223 @@ | |||
1 | /* | ||
2 | * Function Control Protocol (IEC 61883-1) helper functions | ||
3 | * | ||
4 | * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | ||
5 | * Licensed under the terms of the GNU General Public License, version 2. | ||
6 | */ | ||
7 | |||
8 | #include <linux/device.h> | ||
9 | #include <linux/firewire.h> | ||
10 | #include <linux/firewire-constants.h> | ||
11 | #include <linux/list.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/spinlock.h> | ||
15 | #include <linux/wait.h> | ||
16 | #include "fcp.h" | ||
17 | #include "lib.h" | ||
18 | |||
19 | #define CTS_AVC 0x00 | ||
20 | |||
21 | #define ERROR_RETRIES 3 | ||
22 | #define ERROR_DELAY_MS 5 | ||
23 | #define FCP_TIMEOUT_MS 125 | ||
24 | |||
25 | static DEFINE_SPINLOCK(transactions_lock); | ||
26 | static LIST_HEAD(transactions); | ||
27 | |||
28 | enum fcp_state { | ||
29 | STATE_PENDING, | ||
30 | STATE_BUS_RESET, | ||
31 | STATE_COMPLETE, | ||
32 | }; | ||
33 | |||
34 | struct fcp_transaction { | ||
35 | struct list_head list; | ||
36 | struct fw_unit *unit; | ||
37 | void *response_buffer; | ||
38 | unsigned int response_size; | ||
39 | unsigned int response_match_bytes; | ||
40 | enum fcp_state state; | ||
41 | wait_queue_head_t wait; | ||
42 | }; | ||
43 | |||
44 | /** | ||
45 | * fcp_avc_transaction - send an AV/C command and wait for its response | ||
46 | * @unit: a unit on the target device | ||
47 | * @command: a buffer containing the command frame; must be DMA-able | ||
48 | * @command_size: the size of @command | ||
49 | * @response: a buffer for the response frame | ||
50 | * @response_size: the maximum size of @response | ||
51 | * @response_match_bytes: a bitmap specifying the bytes used to detect the | ||
52 | * correct response frame | ||
53 | * | ||
54 | * This function sends a FCP command frame to the target and waits for the | ||
55 | * corresponding response frame to be returned. | ||
56 | * | ||
57 | * Because it is possible for multiple FCP transactions to be active at the | ||
58 | * same time, the correct response frame is detected by the value of certain | ||
59 | * bytes. These bytes must be set in @response before calling this function, | ||
60 | * and the corresponding bits must be set in @response_match_bytes. | ||
61 | * | ||
62 | * @command and @response can point to the same buffer. | ||
63 | * | ||
64 | * Asynchronous operation (INTERIM, NOTIFY) is not supported at the moment. | ||
65 | * | ||
66 | * Returns the actual size of the response frame, or a negative error code. | ||
67 | */ | ||
68 | int fcp_avc_transaction(struct fw_unit *unit, | ||
69 | const void *command, unsigned int command_size, | ||
70 | void *response, unsigned int response_size, | ||
71 | unsigned int response_match_bytes) | ||
72 | { | ||
73 | struct fcp_transaction t; | ||
74 | int tcode, ret, tries = 0; | ||
75 | |||
76 | t.unit = unit; | ||
77 | t.response_buffer = response; | ||
78 | t.response_size = response_size; | ||
79 | t.response_match_bytes = response_match_bytes; | ||
80 | t.state = STATE_PENDING; | ||
81 | init_waitqueue_head(&t.wait); | ||
82 | |||
83 | spin_lock_irq(&transactions_lock); | ||
84 | list_add_tail(&t.list, &transactions); | ||
85 | spin_unlock_irq(&transactions_lock); | ||
86 | |||
87 | for (;;) { | ||
88 | tcode = command_size == 4 ? TCODE_WRITE_QUADLET_REQUEST | ||
89 | : TCODE_WRITE_BLOCK_REQUEST; | ||
90 | ret = snd_fw_transaction(t.unit, tcode, | ||
91 | CSR_REGISTER_BASE + CSR_FCP_COMMAND, | ||
92 | (void *)command, command_size); | ||
93 | if (ret < 0) | ||
94 | break; | ||
95 | |||
96 | wait_event_timeout(t.wait, t.state != STATE_PENDING, | ||
97 | msecs_to_jiffies(FCP_TIMEOUT_MS)); | ||
98 | |||
99 | if (t.state == STATE_COMPLETE) { | ||
100 | ret = t.response_size; | ||
101 | break; | ||
102 | } else if (t.state == STATE_BUS_RESET) { | ||
103 | msleep(ERROR_DELAY_MS); | ||
104 | } else if (++tries >= ERROR_RETRIES) { | ||
105 | dev_err(&t.unit->device, "FCP command timed out\n"); | ||
106 | ret = -EIO; | ||
107 | break; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | spin_lock_irq(&transactions_lock); | ||
112 | list_del(&t.list); | ||
113 | spin_unlock_irq(&transactions_lock); | ||
114 | |||
115 | return ret; | ||
116 | } | ||
117 | EXPORT_SYMBOL(fcp_avc_transaction); | ||
118 | |||
119 | /** | ||
120 | * fcp_bus_reset - inform the target handler about a bus reset | ||
121 | * @unit: the unit that might be used by fcp_avc_transaction() | ||
122 | * | ||
123 | * This function must be called from the driver's .update handler to inform | ||
124 | * the FCP transaction handler that a bus reset has happened. Any pending FCP | ||
125 | * transactions are retried. | ||
126 | */ | ||
127 | void fcp_bus_reset(struct fw_unit *unit) | ||
128 | { | ||
129 | struct fcp_transaction *t; | ||
130 | |||
131 | spin_lock_irq(&transactions_lock); | ||
132 | list_for_each_entry(t, &transactions, list) { | ||
133 | if (t->unit == unit && | ||
134 | t->state == STATE_PENDING) { | ||
135 | t->state = STATE_BUS_RESET; | ||
136 | wake_up(&t->wait); | ||
137 | } | ||
138 | } | ||
139 | spin_unlock_irq(&transactions_lock); | ||
140 | } | ||
141 | EXPORT_SYMBOL(fcp_bus_reset); | ||
142 | |||
143 | /* checks whether the response matches the masked bytes in response_buffer */ | ||
144 | static bool is_matching_response(struct fcp_transaction *transaction, | ||
145 | const void *response, size_t length) | ||
146 | { | ||
147 | const u8 *p1, *p2; | ||
148 | unsigned int mask, i; | ||
149 | |||
150 | p1 = response; | ||
151 | p2 = transaction->response_buffer; | ||
152 | mask = transaction->response_match_bytes; | ||
153 | |||
154 | for (i = 0; ; ++i) { | ||
155 | if ((mask & 1) && p1[i] != p2[i]) | ||
156 | return false; | ||
157 | mask >>= 1; | ||
158 | if (!mask) | ||
159 | return true; | ||
160 | if (--length == 0) | ||
161 | return false; | ||
162 | } | ||
163 | } | ||
164 | |||
165 | static void fcp_response(struct fw_card *card, struct fw_request *request, | ||
166 | int tcode, int destination, int source, | ||
167 | int generation, unsigned long long offset, | ||
168 | void *data, size_t length, void *callback_data) | ||
169 | { | ||
170 | struct fcp_transaction *t; | ||
171 | unsigned long flags; | ||
172 | |||
173 | if (length < 1 || (*(const u8 *)data & 0xf0) != CTS_AVC) | ||
174 | return; | ||
175 | |||
176 | spin_lock_irqsave(&transactions_lock, flags); | ||
177 | list_for_each_entry(t, &transactions, list) { | ||
178 | struct fw_device *device = fw_parent_device(t->unit); | ||
179 | if (device->card != card || | ||
180 | device->generation != generation) | ||
181 | continue; | ||
182 | smp_rmb(); /* node_id vs. generation */ | ||
183 | if (device->node_id != source) | ||
184 | continue; | ||
185 | |||
186 | if (t->state == STATE_PENDING && | ||
187 | is_matching_response(t, data, length)) { | ||
188 | t->state = STATE_COMPLETE; | ||
189 | t->response_size = min((unsigned int)length, | ||
190 | t->response_size); | ||
191 | memcpy(t->response_buffer, data, t->response_size); | ||
192 | wake_up(&t->wait); | ||
193 | } | ||
194 | } | ||
195 | spin_unlock_irqrestore(&transactions_lock, flags); | ||
196 | } | ||
197 | |||
198 | static struct fw_address_handler response_register_handler = { | ||
199 | .length = 0x200, | ||
200 | .address_callback = fcp_response, | ||
201 | }; | ||
202 | |||
203 | static int __init fcp_module_init(void) | ||
204 | { | ||
205 | static const struct fw_address_region response_register_region = { | ||
206 | .start = CSR_REGISTER_BASE + CSR_FCP_RESPONSE, | ||
207 | .end = CSR_REGISTER_BASE + CSR_FCP_END, | ||
208 | }; | ||
209 | |||
210 | fw_core_add_address_handler(&response_register_handler, | ||
211 | &response_register_region); | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | static void __exit fcp_module_exit(void) | ||
217 | { | ||
218 | WARN_ON(!list_empty(&transactions)); | ||
219 | fw_core_remove_address_handler(&response_register_handler); | ||
220 | } | ||
221 | |||
222 | module_init(fcp_module_init); | ||
223 | module_exit(fcp_module_exit); | ||
diff --git a/sound/firewire/fcp.h b/sound/firewire/fcp.h new file mode 100644 index 000000000000..86595688bd91 --- /dev/null +++ b/sound/firewire/fcp.h | |||
@@ -0,0 +1,12 @@ | |||
1 | #ifndef SOUND_FIREWIRE_FCP_H_INCLUDED | ||
2 | #define SOUND_FIREWIRE_FCP_H_INCLUDED | ||
3 | |||
4 | struct fw_unit; | ||
5 | |||
6 | int fcp_avc_transaction(struct fw_unit *unit, | ||
7 | const void *command, unsigned int command_size, | ||
8 | void *response, unsigned int response_size, | ||
9 | unsigned int response_match_bytes); | ||
10 | void fcp_bus_reset(struct fw_unit *unit); | ||
11 | |||
12 | #endif | ||
diff --git a/sound/firewire/iso-resources.c b/sound/firewire/iso-resources.c new file mode 100644 index 000000000000..6f2b5f8651fd --- /dev/null +++ b/sound/firewire/iso-resources.c | |||
@@ -0,0 +1,224 @@ | |||
1 | /* | ||
2 | * isochronous resources helper functions | ||
3 | * | ||
4 | * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | ||
5 | * Licensed under the terms of the GNU General Public License, version 2. | ||
6 | */ | ||
7 | |||
8 | #include <linux/device.h> | ||
9 | #include <linux/firewire.h> | ||
10 | #include <linux/firewire-constants.h> | ||
11 | #include <linux/jiffies.h> | ||
12 | #include <linux/mutex.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/spinlock.h> | ||
15 | #include "iso-resources.h" | ||
16 | |||
17 | /** | ||
18 | * fw_iso_resources_init - initializes a &struct fw_iso_resources | ||
19 | * @r: the resource manager to initialize | ||
20 | * @unit: the device unit for which the resources will be needed | ||
21 | * | ||
22 | * If the device does not support all channel numbers, change @r->channels_mask | ||
23 | * after calling this function. | ||
24 | */ | ||
25 | void fw_iso_resources_init(struct fw_iso_resources *r, struct fw_unit *unit) | ||
26 | { | ||
27 | r->channels_mask = ~0uLL; | ||
28 | r->unit = fw_unit_get(unit); | ||
29 | mutex_init(&r->mutex); | ||
30 | r->allocated = false; | ||
31 | } | ||
32 | |||
33 | /** | ||
34 | * fw_iso_resources_destroy - destroy a resource manager | ||
35 | * @r: the resource manager that is no longer needed | ||
36 | */ | ||
37 | void fw_iso_resources_destroy(struct fw_iso_resources *r) | ||
38 | { | ||
39 | WARN_ON(r->allocated); | ||
40 | mutex_destroy(&r->mutex); | ||
41 | fw_unit_put(r->unit); | ||
42 | } | ||
43 | |||
44 | static unsigned int packet_bandwidth(unsigned int max_payload_bytes, int speed) | ||
45 | { | ||
46 | unsigned int bytes, s400_bytes; | ||
47 | |||
48 | /* iso packets have three header quadlets and quadlet-aligned payload */ | ||
49 | bytes = 3 * 4 + ALIGN(max_payload_bytes, 4); | ||
50 | |||
51 | /* convert to bandwidth units (quadlets at S1600 = bytes at S400) */ | ||
52 | if (speed <= SCODE_400) | ||
53 | s400_bytes = bytes * (1 << (SCODE_400 - speed)); | ||
54 | else | ||
55 | s400_bytes = DIV_ROUND_UP(bytes, 1 << (speed - SCODE_400)); | ||
56 | |||
57 | return s400_bytes; | ||
58 | } | ||
59 | |||
60 | static int current_bandwidth_overhead(struct fw_card *card) | ||
61 | { | ||
62 | /* | ||
63 | * Under the usual pessimistic assumption (cable length 4.5 m), the | ||
64 | * isochronous overhead for N cables is 1.797 µs + N * 0.494 µs, or | ||
65 | * 88.3 + N * 24.3 in bandwidth units. | ||
66 | * | ||
67 | * The calculation below tries to deduce N from the current gap count. | ||
68 | * If the gap count has been optimized by measuring the actual packet | ||
69 | * transmission time, this derived overhead should be near the actual | ||
70 | * overhead as well. | ||
71 | */ | ||
72 | return card->gap_count < 63 ? card->gap_count * 97 / 10 + 89 : 512; | ||
73 | } | ||
74 | |||
75 | static int wait_isoch_resource_delay_after_bus_reset(struct fw_card *card) | ||
76 | { | ||
77 | for (;;) { | ||
78 | s64 delay = (card->reset_jiffies + HZ) - get_jiffies_64(); | ||
79 | if (delay <= 0) | ||
80 | return 0; | ||
81 | if (schedule_timeout_interruptible(delay) > 0) | ||
82 | return -ERESTARTSYS; | ||
83 | } | ||
84 | } | ||
85 | |||
86 | /** | ||
87 | * fw_iso_resources_allocate - allocate isochronous channel and bandwidth | ||
88 | * @r: the resource manager | ||
89 | * @max_payload_bytes: the amount of data (including CIP headers) per packet | ||
90 | * @speed: the speed (e.g., SCODE_400) at which the packets will be sent | ||
91 | * | ||
92 | * This function allocates one isochronous channel and enough bandwidth for the | ||
93 | * specified packet size. | ||
94 | * | ||
95 | * Returns the channel number that the caller must use for streaming, or | ||
96 | * a negative error code. Due to potentionally long delays, this function is | ||
97 | * interruptible and can return -ERESTARTSYS. On success, the caller is | ||
98 | * responsible for calling fw_iso_resources_update() on bus resets, and | ||
99 | * fw_iso_resources_free() when the resources are not longer needed. | ||
100 | */ | ||
101 | int fw_iso_resources_allocate(struct fw_iso_resources *r, | ||
102 | unsigned int max_payload_bytes, int speed) | ||
103 | { | ||
104 | struct fw_card *card = fw_parent_device(r->unit)->card; | ||
105 | int bandwidth, channel, err; | ||
106 | |||
107 | if (WARN_ON(r->allocated)) | ||
108 | return -EBADFD; | ||
109 | |||
110 | r->bandwidth = packet_bandwidth(max_payload_bytes, speed); | ||
111 | |||
112 | retry_after_bus_reset: | ||
113 | spin_lock_irq(&card->lock); | ||
114 | r->generation = card->generation; | ||
115 | r->bandwidth_overhead = current_bandwidth_overhead(card); | ||
116 | spin_unlock_irq(&card->lock); | ||
117 | |||
118 | err = wait_isoch_resource_delay_after_bus_reset(card); | ||
119 | if (err < 0) | ||
120 | return err; | ||
121 | |||
122 | mutex_lock(&r->mutex); | ||
123 | |||
124 | bandwidth = r->bandwidth + r->bandwidth_overhead; | ||
125 | fw_iso_resource_manage(card, r->generation, r->channels_mask, | ||
126 | &channel, &bandwidth, true, r->buffer); | ||
127 | if (channel == -EAGAIN) { | ||
128 | mutex_unlock(&r->mutex); | ||
129 | goto retry_after_bus_reset; | ||
130 | } | ||
131 | if (channel >= 0) { | ||
132 | r->channel = channel; | ||
133 | r->allocated = true; | ||
134 | } else { | ||
135 | if (channel == -EBUSY) | ||
136 | dev_err(&r->unit->device, | ||
137 | "isochronous resources exhausted\n"); | ||
138 | else | ||
139 | dev_err(&r->unit->device, | ||
140 | "isochronous resource allocation failed\n"); | ||
141 | } | ||
142 | |||
143 | mutex_unlock(&r->mutex); | ||
144 | |||
145 | return channel; | ||
146 | } | ||
147 | |||
148 | /** | ||
149 | * fw_iso_resources_update - update resource allocations after a bus reset | ||
150 | * @r: the resource manager | ||
151 | * | ||
152 | * This function must be called from the driver's .update handler to reallocate | ||
153 | * any resources that were allocated before the bus reset. It is safe to call | ||
154 | * this function if no resources are currently allocated. | ||
155 | * | ||
156 | * Returns a negative error code on failure. If this happens, the caller must | ||
157 | * stop streaming. | ||
158 | */ | ||
159 | int fw_iso_resources_update(struct fw_iso_resources *r) | ||
160 | { | ||
161 | struct fw_card *card = fw_parent_device(r->unit)->card; | ||
162 | int bandwidth, channel; | ||
163 | |||
164 | mutex_lock(&r->mutex); | ||
165 | |||
166 | if (!r->allocated) { | ||
167 | mutex_unlock(&r->mutex); | ||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | spin_lock_irq(&card->lock); | ||
172 | r->generation = card->generation; | ||
173 | r->bandwidth_overhead = current_bandwidth_overhead(card); | ||
174 | spin_unlock_irq(&card->lock); | ||
175 | |||
176 | bandwidth = r->bandwidth + r->bandwidth_overhead; | ||
177 | |||
178 | fw_iso_resource_manage(card, r->generation, 1uLL << r->channel, | ||
179 | &channel, &bandwidth, true, r->buffer); | ||
180 | /* | ||
181 | * When another bus reset happens, pretend that the allocation | ||
182 | * succeeded; we will try again for the new generation later. | ||
183 | */ | ||
184 | if (channel < 0 && channel != -EAGAIN) { | ||
185 | r->allocated = false; | ||
186 | if (channel == -EBUSY) | ||
187 | dev_err(&r->unit->device, | ||
188 | "isochronous resources exhausted\n"); | ||
189 | else | ||
190 | dev_err(&r->unit->device, | ||
191 | "isochronous resource allocation failed\n"); | ||
192 | } | ||
193 | |||
194 | mutex_unlock(&r->mutex); | ||
195 | |||
196 | return channel; | ||
197 | } | ||
198 | |||
199 | /** | ||
200 | * fw_iso_resources_free - frees allocated resources | ||
201 | * @r: the resource manager | ||
202 | * | ||
203 | * This function deallocates the channel and bandwidth, if allocated. | ||
204 | */ | ||
205 | void fw_iso_resources_free(struct fw_iso_resources *r) | ||
206 | { | ||
207 | struct fw_card *card = fw_parent_device(r->unit)->card; | ||
208 | int bandwidth, channel; | ||
209 | |||
210 | mutex_lock(&r->mutex); | ||
211 | |||
212 | if (r->allocated) { | ||
213 | bandwidth = r->bandwidth + r->bandwidth_overhead; | ||
214 | fw_iso_resource_manage(card, r->generation, 1uLL << r->channel, | ||
215 | &channel, &bandwidth, false, r->buffer); | ||
216 | if (channel < 0) | ||
217 | dev_err(&r->unit->device, | ||
218 | "isochronous resource deallocation failed\n"); | ||
219 | |||
220 | r->allocated = false; | ||
221 | } | ||
222 | |||
223 | mutex_unlock(&r->mutex); | ||
224 | } | ||
diff --git a/sound/firewire/iso-resources.h b/sound/firewire/iso-resources.h new file mode 100644 index 000000000000..9feb9f8d4745 --- /dev/null +++ b/sound/firewire/iso-resources.h | |||
@@ -0,0 +1,39 @@ | |||
1 | #ifndef SOUND_FIREWIRE_ISO_RESOURCES_H_INCLUDED | ||
2 | #define SOUND_FIREWIRE_ISO_RESOURCES_H_INCLUDED | ||
3 | |||
4 | #include <linux/mutex.h> | ||
5 | #include <linux/types.h> | ||
6 | |||
7 | struct fw_unit; | ||
8 | |||
9 | /** | ||
10 | * struct fw_iso_resources - manages channel/bandwidth allocation | ||
11 | * @channels_mask: if the device does not support all channel numbers, set this | ||
12 | * bit mask to something else than the default (all ones) | ||
13 | * | ||
14 | * This structure manages (de)allocation of isochronous resources (channel and | ||
15 | * bandwidth) for one isochronous stream. | ||
16 | */ | ||
17 | struct fw_iso_resources { | ||
18 | u64 channels_mask; | ||
19 | /* private: */ | ||
20 | struct fw_unit *unit; | ||
21 | struct mutex mutex; | ||
22 | unsigned int channel; | ||
23 | unsigned int bandwidth; /* in bandwidth units, without overhead */ | ||
24 | unsigned int bandwidth_overhead; | ||
25 | int generation; /* in which allocation is valid */ | ||
26 | bool allocated; | ||
27 | __be32 buffer[2]; | ||
28 | }; | ||
29 | |||
30 | void fw_iso_resources_init(struct fw_iso_resources *r, | ||
31 | struct fw_unit *unit); | ||
32 | void fw_iso_resources_destroy(struct fw_iso_resources *r); | ||
33 | |||
34 | int fw_iso_resources_allocate(struct fw_iso_resources *r, | ||
35 | unsigned int max_payload_bytes, int speed); | ||
36 | int fw_iso_resources_update(struct fw_iso_resources *r); | ||
37 | void fw_iso_resources_free(struct fw_iso_resources *r); | ||
38 | |||
39 | #endif | ||
diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c new file mode 100644 index 000000000000..4750cea2210e --- /dev/null +++ b/sound/firewire/lib.c | |||
@@ -0,0 +1,85 @@ | |||
1 | /* | ||
2 | * miscellaneous helper functions | ||
3 | * | ||
4 | * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | ||
5 | * Licensed under the terms of the GNU General Public License, version 2. | ||
6 | */ | ||
7 | |||
8 | #include <linux/delay.h> | ||
9 | #include <linux/device.h> | ||
10 | #include <linux/firewire.h> | ||
11 | #include <linux/module.h> | ||
12 | #include "lib.h" | ||
13 | |||
14 | #define ERROR_RETRY_DELAY_MS 5 | ||
15 | |||
16 | /** | ||
17 | * rcode_string - convert a firewire result code to a string | ||
18 | * @rcode: the result | ||
19 | */ | ||
20 | const char *rcode_string(unsigned int rcode) | ||
21 | { | ||
22 | static const char *const names[] = { | ||
23 | [RCODE_COMPLETE] = "complete", | ||
24 | [RCODE_CONFLICT_ERROR] = "conflict error", | ||
25 | [RCODE_DATA_ERROR] = "data error", | ||
26 | [RCODE_TYPE_ERROR] = "type error", | ||
27 | [RCODE_ADDRESS_ERROR] = "address error", | ||
28 | [RCODE_SEND_ERROR] = "send error", | ||
29 | [RCODE_CANCELLED] = "cancelled", | ||
30 | [RCODE_BUSY] = "busy", | ||
31 | [RCODE_GENERATION] = "generation", | ||
32 | [RCODE_NO_ACK] = "no ack", | ||
33 | }; | ||
34 | |||
35 | if (rcode < ARRAY_SIZE(names) && names[rcode]) | ||
36 | return names[rcode]; | ||
37 | else | ||
38 | return "unknown"; | ||
39 | } | ||
40 | EXPORT_SYMBOL(rcode_string); | ||
41 | |||
42 | /** | ||
43 | * snd_fw_transaction - send a request and wait for its completion | ||
44 | * @unit: the driver's unit on the target device | ||
45 | * @tcode: the transaction code | ||
46 | * @offset: the address in the target's address space | ||
47 | * @buffer: input/output data | ||
48 | * @length: length of @buffer | ||
49 | * | ||
50 | * Submits an asynchronous request to the target device, and waits for the | ||
51 | * response. The node ID and the current generation are derived from @unit. | ||
52 | * On a bus reset or an error, the transaction is retried a few times. | ||
53 | * Returns zero on success, or a negative error code. | ||
54 | */ | ||
55 | int snd_fw_transaction(struct fw_unit *unit, int tcode, | ||
56 | u64 offset, void *buffer, size_t length) | ||
57 | { | ||
58 | struct fw_device *device = fw_parent_device(unit); | ||
59 | int generation, rcode, tries = 0; | ||
60 | |||
61 | for (;;) { | ||
62 | generation = device->generation; | ||
63 | smp_rmb(); /* node_id vs. generation */ | ||
64 | rcode = fw_run_transaction(device->card, tcode, | ||
65 | device->node_id, generation, | ||
66 | device->max_speed, offset, | ||
67 | buffer, length); | ||
68 | |||
69 | if (rcode == RCODE_COMPLETE) | ||
70 | return 0; | ||
71 | |||
72 | if (rcode_is_permanent_error(rcode) || ++tries >= 3) { | ||
73 | dev_err(&unit->device, "transaction failed: %s\n", | ||
74 | rcode_string(rcode)); | ||
75 | return -EIO; | ||
76 | } | ||
77 | |||
78 | msleep(ERROR_RETRY_DELAY_MS); | ||
79 | } | ||
80 | } | ||
81 | EXPORT_SYMBOL(snd_fw_transaction); | ||
82 | |||
83 | MODULE_DESCRIPTION("FireWire audio helper functions"); | ||
84 | MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); | ||
85 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h new file mode 100644 index 000000000000..064f3fd9ab06 --- /dev/null +++ b/sound/firewire/lib.h | |||
@@ -0,0 +1,19 @@ | |||
1 | #ifndef SOUND_FIREWIRE_LIB_H_INCLUDED | ||
2 | #define SOUND_FIREWIRE_LIB_H_INCLUDED | ||
3 | |||
4 | #include <linux/firewire-constants.h> | ||
5 | #include <linux/types.h> | ||
6 | |||
7 | struct fw_unit; | ||
8 | |||
9 | int snd_fw_transaction(struct fw_unit *unit, int tcode, | ||
10 | u64 offset, void *buffer, size_t length); | ||
11 | const char *rcode_string(unsigned int rcode); | ||
12 | |||
13 | /* returns true if retrying the transaction would not make sense */ | ||
14 | static inline bool rcode_is_permanent_error(int rcode) | ||
15 | { | ||
16 | return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR; | ||
17 | } | ||
18 | |||
19 | #endif | ||
diff --git a/sound/firewire/packets-buffer.c b/sound/firewire/packets-buffer.c new file mode 100644 index 000000000000..1e20e60ba6a6 --- /dev/null +++ b/sound/firewire/packets-buffer.c | |||
@@ -0,0 +1,74 @@ | |||
1 | /* | ||
2 | * helpers for managing a buffer for many packets | ||
3 | * | ||
4 | * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | ||
5 | * Licensed under the terms of the GNU General Public License, version 2. | ||
6 | */ | ||
7 | |||
8 | #include <linux/firewire.h> | ||
9 | #include <linux/slab.h> | ||
10 | #include "packets-buffer.h" | ||
11 | |||
12 | /** | ||
13 | * iso_packets_buffer_init - allocates the memory for packets | ||
14 | * @b: the buffer structure to initialize | ||
15 | * @unit: the device at the other end of the stream | ||
16 | * @count: the number of packets | ||
17 | * @packet_size: the (maximum) size of a packet, in bytes | ||
18 | * @direction: %DMA_TO_DEVICE or %DMA_FROM_DEVICE | ||
19 | */ | ||
20 | int iso_packets_buffer_init(struct iso_packets_buffer *b, struct fw_unit *unit, | ||
21 | unsigned int count, unsigned int packet_size, | ||
22 | enum dma_data_direction direction) | ||
23 | { | ||
24 | unsigned int packets_per_page, pages; | ||
25 | unsigned int i, page_index, offset_in_page; | ||
26 | void *p; | ||
27 | int err; | ||
28 | |||
29 | b->packets = kmalloc(count * sizeof(*b->packets), GFP_KERNEL); | ||
30 | if (!b->packets) { | ||
31 | err = -ENOMEM; | ||
32 | goto error; | ||
33 | } | ||
34 | |||
35 | packet_size = L1_CACHE_ALIGN(packet_size); | ||
36 | packets_per_page = PAGE_SIZE / packet_size; | ||
37 | if (WARN_ON(!packets_per_page)) { | ||
38 | err = -EINVAL; | ||
39 | goto error; | ||
40 | } | ||
41 | pages = DIV_ROUND_UP(count, packets_per_page); | ||
42 | |||
43 | err = fw_iso_buffer_init(&b->iso_buffer, fw_parent_device(unit)->card, | ||
44 | pages, direction); | ||
45 | if (err < 0) | ||
46 | goto err_packets; | ||
47 | |||
48 | for (i = 0; i < count; ++i) { | ||
49 | page_index = i / packets_per_page; | ||
50 | p = page_address(b->iso_buffer.pages[page_index]); | ||
51 | offset_in_page = (i % packets_per_page) * packet_size; | ||
52 | b->packets[i].buffer = p + offset_in_page; | ||
53 | b->packets[i].offset = page_index * PAGE_SIZE + offset_in_page; | ||
54 | } | ||
55 | |||
56 | return 0; | ||
57 | |||
58 | err_packets: | ||
59 | kfree(b->packets); | ||
60 | error: | ||
61 | return err; | ||
62 | } | ||
63 | |||
64 | /** | ||
65 | * iso_packets_buffer_destroy - frees packet buffer resources | ||
66 | * @b: the buffer structure to free | ||
67 | * @unit: the device at the other end of the stream | ||
68 | */ | ||
69 | void iso_packets_buffer_destroy(struct iso_packets_buffer *b, | ||
70 | struct fw_unit *unit) | ||
71 | { | ||
72 | fw_iso_buffer_destroy(&b->iso_buffer, fw_parent_device(unit)->card); | ||
73 | kfree(b->packets); | ||
74 | } | ||
diff --git a/sound/firewire/packets-buffer.h b/sound/firewire/packets-buffer.h new file mode 100644 index 000000000000..6513c5cb6ea9 --- /dev/null +++ b/sound/firewire/packets-buffer.h | |||
@@ -0,0 +1,26 @@ | |||
1 | #ifndef SOUND_FIREWIRE_PACKETS_BUFFER_H_INCLUDED | ||
2 | #define SOUND_FIREWIRE_PACKETS_BUFFER_H_INCLUDED | ||
3 | |||
4 | #include <linux/dma-mapping.h> | ||
5 | #include <linux/firewire.h> | ||
6 | |||
7 | /** | ||
8 | * struct iso_packets_buffer - manages a buffer for many packets | ||
9 | * @iso_buffer: the memory containing the packets | ||
10 | * @packets: an array, with each element pointing to one packet | ||
11 | */ | ||
12 | struct iso_packets_buffer { | ||
13 | struct fw_iso_buffer iso_buffer; | ||
14 | struct { | ||
15 | void *buffer; | ||
16 | unsigned int offset; | ||
17 | } *packets; | ||
18 | }; | ||
19 | |||
20 | int iso_packets_buffer_init(struct iso_packets_buffer *b, struct fw_unit *unit, | ||
21 | unsigned int count, unsigned int packet_size, | ||
22 | enum dma_data_direction direction); | ||
23 | void iso_packets_buffer_destroy(struct iso_packets_buffer *b, | ||
24 | struct fw_unit *unit); | ||
25 | |||
26 | #endif | ||
diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c new file mode 100644 index 000000000000..f6b095ef075a --- /dev/null +++ b/sound/firewire/speakers.c | |||
@@ -0,0 +1,855 @@ | |||
1 | /* | ||
2 | * OXFW970-based speakers driver | ||
3 | * | ||
4 | * Copyright (c) Clemens Ladisch <clemens@ladisch.de> | ||
5 | * Licensed under the terms of the GNU General Public License, version 2. | ||
6 | */ | ||
7 | |||
8 | #include <linux/device.h> | ||
9 | #include <linux/firewire.h> | ||
10 | #include <linux/firewire-constants.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/mod_devicetable.h> | ||
13 | #include <linux/mutex.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <sound/control.h> | ||
16 | #include <sound/core.h> | ||
17 | #include <sound/initval.h> | ||
18 | #include <sound/pcm.h> | ||
19 | #include <sound/pcm_params.h> | ||
20 | #include "cmp.h" | ||
21 | #include "fcp.h" | ||
22 | #include "amdtp.h" | ||
23 | #include "lib.h" | ||
24 | |||
25 | #define OXFORD_FIRMWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x50000) | ||
26 | /* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */ | ||
27 | |||
28 | #define OXFORD_HARDWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x90020) | ||
29 | #define OXFORD_HARDWARE_ID_OXFW970 0x39443841 | ||
30 | #define OXFORD_HARDWARE_ID_OXFW971 0x39373100 | ||
31 | |||
32 | #define VENDOR_GRIFFIN 0x001292 | ||
33 | #define VENDOR_LACIE 0x00d04b | ||
34 | |||
35 | #define SPECIFIER_1394TA 0x00a02d | ||
36 | #define VERSION_AVC 0x010001 | ||
37 | |||
38 | struct device_info { | ||
39 | const char *driver_name; | ||
40 | const char *short_name; | ||
41 | const char *long_name; | ||
42 | int (*pcm_constraints)(struct snd_pcm_runtime *runtime); | ||
43 | unsigned int mixer_channels; | ||
44 | u8 mute_fb_id; | ||
45 | u8 volume_fb_id; | ||
46 | }; | ||
47 | |||
48 | struct fwspk { | ||
49 | struct snd_card *card; | ||
50 | struct fw_unit *unit; | ||
51 | const struct device_info *device_info; | ||
52 | struct snd_pcm_substream *pcm; | ||
53 | struct mutex mutex; | ||
54 | struct cmp_connection connection; | ||
55 | struct amdtp_out_stream stream; | ||
56 | bool stream_running; | ||
57 | bool mute; | ||
58 | s16 volume[6]; | ||
59 | s16 volume_min; | ||
60 | s16 volume_max; | ||
61 | }; | ||
62 | |||
63 | MODULE_DESCRIPTION("FireWire speakers driver"); | ||
64 | MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); | ||
65 | MODULE_LICENSE("GPL v2"); | ||
66 | |||
67 | static int firewave_rate_constraint(struct snd_pcm_hw_params *params, | ||
68 | struct snd_pcm_hw_rule *rule) | ||
69 | { | ||
70 | static unsigned int stereo_rates[] = { 48000, 96000 }; | ||
71 | struct snd_interval *channels = | ||
72 | hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); | ||
73 | struct snd_interval *rate = | ||
74 | hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); | ||
75 | |||
76 | /* two channels work only at 48/96 kHz */ | ||
77 | if (snd_interval_max(channels) < 6) | ||
78 | return snd_interval_list(rate, 2, stereo_rates, 0); | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | static int firewave_channels_constraint(struct snd_pcm_hw_params *params, | ||
83 | struct snd_pcm_hw_rule *rule) | ||
84 | { | ||
85 | static const struct snd_interval all_channels = { .min = 6, .max = 6 }; | ||
86 | struct snd_interval *rate = | ||
87 | hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); | ||
88 | struct snd_interval *channels = | ||
89 | hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); | ||
90 | |||
91 | /* 32/44.1 kHz work only with all six channels */ | ||
92 | if (snd_interval_max(rate) < 48000) | ||
93 | return snd_interval_refine(channels, &all_channels); | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | static int firewave_constraints(struct snd_pcm_runtime *runtime) | ||
98 | { | ||
99 | static unsigned int channels_list[] = { 2, 6 }; | ||
100 | static struct snd_pcm_hw_constraint_list channels_list_constraint = { | ||
101 | .count = 2, | ||
102 | .list = channels_list, | ||
103 | }; | ||
104 | int err; | ||
105 | |||
106 | runtime->hw.rates = SNDRV_PCM_RATE_32000 | | ||
107 | SNDRV_PCM_RATE_44100 | | ||
108 | SNDRV_PCM_RATE_48000 | | ||
109 | SNDRV_PCM_RATE_96000; | ||
110 | runtime->hw.channels_max = 6; | ||
111 | |||
112 | err = snd_pcm_hw_constraint_list(runtime, 0, | ||
113 | SNDRV_PCM_HW_PARAM_CHANNELS, | ||
114 | &channels_list_constraint); | ||
115 | if (err < 0) | ||
116 | return err; | ||
117 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
118 | firewave_rate_constraint, NULL, | ||
119 | SNDRV_PCM_HW_PARAM_CHANNELS, -1); | ||
120 | if (err < 0) | ||
121 | return err; | ||
122 | err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, | ||
123 | firewave_channels_constraint, NULL, | ||
124 | SNDRV_PCM_HW_PARAM_RATE, -1); | ||
125 | if (err < 0) | ||
126 | return err; | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static int lacie_speakers_constraints(struct snd_pcm_runtime *runtime) | ||
132 | { | ||
133 | runtime->hw.rates = SNDRV_PCM_RATE_32000 | | ||
134 | SNDRV_PCM_RATE_44100 | | ||
135 | SNDRV_PCM_RATE_48000 | | ||
136 | SNDRV_PCM_RATE_88200 | | ||
137 | SNDRV_PCM_RATE_96000; | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static int fwspk_open(struct snd_pcm_substream *substream) | ||
143 | { | ||
144 | static const struct snd_pcm_hardware hardware = { | ||
145 | .info = SNDRV_PCM_INFO_MMAP | | ||
146 | SNDRV_PCM_INFO_MMAP_VALID | | ||
147 | SNDRV_PCM_INFO_BATCH | | ||
148 | SNDRV_PCM_INFO_INTERLEAVED | | ||
149 | SNDRV_PCM_INFO_BLOCK_TRANSFER, | ||
150 | .formats = AMDTP_OUT_PCM_FORMAT_BITS, | ||
151 | .channels_min = 2, | ||
152 | .channels_max = 2, | ||
153 | .buffer_bytes_max = 4 * 1024 * 1024, | ||
154 | .period_bytes_min = 1, | ||
155 | .period_bytes_max = UINT_MAX, | ||
156 | .periods_min = 1, | ||
157 | .periods_max = UINT_MAX, | ||
158 | }; | ||
159 | struct fwspk *fwspk = substream->private_data; | ||
160 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
161 | int err; | ||
162 | |||
163 | runtime->hw = hardware; | ||
164 | |||
165 | err = fwspk->device_info->pcm_constraints(runtime); | ||
166 | if (err < 0) | ||
167 | return err; | ||
168 | err = snd_pcm_limit_hw_rates(runtime); | ||
169 | if (err < 0) | ||
170 | return err; | ||
171 | |||
172 | err = snd_pcm_hw_constraint_minmax(runtime, | ||
173 | SNDRV_PCM_HW_PARAM_PERIOD_TIME, | ||
174 | 5000, 8192000); | ||
175 | if (err < 0) | ||
176 | return err; | ||
177 | |||
178 | err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); | ||
179 | if (err < 0) | ||
180 | return err; | ||
181 | |||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | static int fwspk_close(struct snd_pcm_substream *substream) | ||
186 | { | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static void fwspk_stop_stream(struct fwspk *fwspk) | ||
191 | { | ||
192 | if (fwspk->stream_running) { | ||
193 | amdtp_out_stream_stop(&fwspk->stream); | ||
194 | cmp_connection_break(&fwspk->connection); | ||
195 | fwspk->stream_running = false; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | static int fwspk_set_rate(struct fwspk *fwspk, unsigned int sfc) | ||
200 | { | ||
201 | u8 *buf; | ||
202 | int err; | ||
203 | |||
204 | buf = kmalloc(8, GFP_KERNEL); | ||
205 | if (!buf) | ||
206 | return -ENOMEM; | ||
207 | |||
208 | buf[0] = 0x00; /* AV/C, CONTROL */ | ||
209 | buf[1] = 0xff; /* unit */ | ||
210 | buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */ | ||
211 | buf[3] = 0x00; /* plug 0 */ | ||
212 | buf[4] = 0x90; /* format: audio */ | ||
213 | buf[5] = 0x00 | sfc; /* AM824, frequency */ | ||
214 | buf[6] = 0xff; /* SYT (not used) */ | ||
215 | buf[7] = 0xff; | ||
216 | |||
217 | err = fcp_avc_transaction(fwspk->unit, buf, 8, buf, 8, | ||
218 | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5)); | ||
219 | if (err < 0) | ||
220 | goto error; | ||
221 | if (err < 6 || buf[0] != 0x09 /* ACCEPTED */) { | ||
222 | dev_err(&fwspk->unit->device, "failed to set sample rate\n"); | ||
223 | err = -EIO; | ||
224 | goto error; | ||
225 | } | ||
226 | |||
227 | err = 0; | ||
228 | |||
229 | error: | ||
230 | kfree(buf); | ||
231 | |||
232 | return err; | ||
233 | } | ||
234 | |||
235 | static int fwspk_hw_params(struct snd_pcm_substream *substream, | ||
236 | struct snd_pcm_hw_params *hw_params) | ||
237 | { | ||
238 | struct fwspk *fwspk = substream->private_data; | ||
239 | int err; | ||
240 | |||
241 | mutex_lock(&fwspk->mutex); | ||
242 | fwspk_stop_stream(fwspk); | ||
243 | mutex_unlock(&fwspk->mutex); | ||
244 | |||
245 | err = snd_pcm_lib_alloc_vmalloc_buffer(substream, | ||
246 | params_buffer_bytes(hw_params)); | ||
247 | if (err < 0) | ||
248 | goto error; | ||
249 | |||
250 | amdtp_out_stream_set_rate(&fwspk->stream, params_rate(hw_params)); | ||
251 | amdtp_out_stream_set_pcm(&fwspk->stream, params_channels(hw_params)); | ||
252 | |||
253 | amdtp_out_stream_set_pcm_format(&fwspk->stream, | ||
254 | params_format(hw_params)); | ||
255 | |||
256 | err = fwspk_set_rate(fwspk, fwspk->stream.sfc); | ||
257 | if (err < 0) | ||
258 | goto err_buffer; | ||
259 | |||
260 | return 0; | ||
261 | |||
262 | err_buffer: | ||
263 | snd_pcm_lib_free_vmalloc_buffer(substream); | ||
264 | error: | ||
265 | return err; | ||
266 | } | ||
267 | |||
268 | static int fwspk_hw_free(struct snd_pcm_substream *substream) | ||
269 | { | ||
270 | struct fwspk *fwspk = substream->private_data; | ||
271 | |||
272 | mutex_lock(&fwspk->mutex); | ||
273 | fwspk_stop_stream(fwspk); | ||
274 | mutex_unlock(&fwspk->mutex); | ||
275 | |||
276 | return snd_pcm_lib_free_vmalloc_buffer(substream); | ||
277 | } | ||
278 | |||
279 | static int fwspk_prepare(struct snd_pcm_substream *substream) | ||
280 | { | ||
281 | struct fwspk *fwspk = substream->private_data; | ||
282 | int err; | ||
283 | |||
284 | mutex_lock(&fwspk->mutex); | ||
285 | |||
286 | if (!fwspk->stream_running) { | ||
287 | err = cmp_connection_establish(&fwspk->connection, | ||
288 | amdtp_out_stream_get_max_payload(&fwspk->stream)); | ||
289 | if (err < 0) | ||
290 | goto err_mutex; | ||
291 | |||
292 | err = amdtp_out_stream_start(&fwspk->stream, | ||
293 | fwspk->connection.resources.channel, | ||
294 | fwspk->connection.speed); | ||
295 | if (err < 0) | ||
296 | goto err_connection; | ||
297 | |||
298 | fwspk->stream_running = true; | ||
299 | } | ||
300 | |||
301 | mutex_unlock(&fwspk->mutex); | ||
302 | |||
303 | amdtp_out_stream_pcm_prepare(&fwspk->stream); | ||
304 | |||
305 | return 0; | ||
306 | |||
307 | err_connection: | ||
308 | cmp_connection_break(&fwspk->connection); | ||
309 | err_mutex: | ||
310 | mutex_unlock(&fwspk->mutex); | ||
311 | |||
312 | return err; | ||
313 | } | ||
314 | |||
315 | static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd) | ||
316 | { | ||
317 | struct fwspk *fwspk = substream->private_data; | ||
318 | struct snd_pcm_substream *pcm; | ||
319 | |||
320 | switch (cmd) { | ||
321 | case SNDRV_PCM_TRIGGER_START: | ||
322 | pcm = substream; | ||
323 | break; | ||
324 | case SNDRV_PCM_TRIGGER_STOP: | ||
325 | pcm = NULL; | ||
326 | break; | ||
327 | default: | ||
328 | return -EINVAL; | ||
329 | } | ||
330 | amdtp_out_stream_pcm_trigger(&fwspk->stream, pcm); | ||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream) | ||
335 | { | ||
336 | struct fwspk *fwspk = substream->private_data; | ||
337 | |||
338 | return amdtp_out_stream_pcm_pointer(&fwspk->stream); | ||
339 | } | ||
340 | |||
341 | static int fwspk_create_pcm(struct fwspk *fwspk) | ||
342 | { | ||
343 | static struct snd_pcm_ops ops = { | ||
344 | .open = fwspk_open, | ||
345 | .close = fwspk_close, | ||
346 | .ioctl = snd_pcm_lib_ioctl, | ||
347 | .hw_params = fwspk_hw_params, | ||
348 | .hw_free = fwspk_hw_free, | ||
349 | .prepare = fwspk_prepare, | ||
350 | .trigger = fwspk_trigger, | ||
351 | .pointer = fwspk_pointer, | ||
352 | .page = snd_pcm_lib_get_vmalloc_page, | ||
353 | .mmap = snd_pcm_lib_mmap_vmalloc, | ||
354 | }; | ||
355 | struct snd_pcm *pcm; | ||
356 | int err; | ||
357 | |||
358 | err = snd_pcm_new(fwspk->card, "OXFW970", 0, 1, 0, &pcm); | ||
359 | if (err < 0) | ||
360 | return err; | ||
361 | pcm->private_data = fwspk; | ||
362 | strcpy(pcm->name, fwspk->device_info->short_name); | ||
363 | fwspk->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; | ||
364 | fwspk->pcm->ops = &ops; | ||
365 | return 0; | ||
366 | } | ||
367 | |||
368 | enum control_action { CTL_READ, CTL_WRITE }; | ||
369 | enum control_attribute { | ||
370 | CTL_MIN = 0x02, | ||
371 | CTL_MAX = 0x03, | ||
372 | CTL_CURRENT = 0x10, | ||
373 | }; | ||
374 | |||
375 | static int fwspk_mute_command(struct fwspk *fwspk, bool *value, | ||
376 | enum control_action action) | ||
377 | { | ||
378 | u8 *buf; | ||
379 | u8 response_ok; | ||
380 | int err; | ||
381 | |||
382 | buf = kmalloc(11, GFP_KERNEL); | ||
383 | if (!buf) | ||
384 | return -ENOMEM; | ||
385 | |||
386 | if (action == CTL_READ) { | ||
387 | buf[0] = 0x01; /* AV/C, STATUS */ | ||
388 | response_ok = 0x0c; /* STABLE */ | ||
389 | } else { | ||
390 | buf[0] = 0x00; /* AV/C, CONTROL */ | ||
391 | response_ok = 0x09; /* ACCEPTED */ | ||
392 | } | ||
393 | buf[1] = 0x08; /* audio unit 0 */ | ||
394 | buf[2] = 0xb8; /* FUNCTION BLOCK */ | ||
395 | buf[3] = 0x81; /* function block type: feature */ | ||
396 | buf[4] = fwspk->device_info->mute_fb_id; /* function block ID */ | ||
397 | buf[5] = 0x10; /* control attribute: current */ | ||
398 | buf[6] = 0x02; /* selector length */ | ||
399 | buf[7] = 0x00; /* audio channel number */ | ||
400 | buf[8] = 0x01; /* control selector: mute */ | ||
401 | buf[9] = 0x01; /* control data length */ | ||
402 | if (action == CTL_READ) | ||
403 | buf[10] = 0xff; | ||
404 | else | ||
405 | buf[10] = *value ? 0x70 : 0x60; | ||
406 | |||
407 | err = fcp_avc_transaction(fwspk->unit, buf, 11, buf, 11, 0x3fe); | ||
408 | if (err < 0) | ||
409 | goto error; | ||
410 | if (err < 11) { | ||
411 | dev_err(&fwspk->unit->device, "short FCP response\n"); | ||
412 | err = -EIO; | ||
413 | goto error; | ||
414 | } | ||
415 | if (buf[0] != response_ok) { | ||
416 | dev_err(&fwspk->unit->device, "mute command failed\n"); | ||
417 | err = -EIO; | ||
418 | goto error; | ||
419 | } | ||
420 | if (action == CTL_READ) | ||
421 | *value = buf[10] == 0x70; | ||
422 | |||
423 | err = 0; | ||
424 | |||
425 | error: | ||
426 | kfree(buf); | ||
427 | |||
428 | return err; | ||
429 | } | ||
430 | |||
431 | static int fwspk_volume_command(struct fwspk *fwspk, s16 *value, | ||
432 | unsigned int channel, | ||
433 | enum control_attribute attribute, | ||
434 | enum control_action action) | ||
435 | { | ||
436 | u8 *buf; | ||
437 | u8 response_ok; | ||
438 | int err; | ||
439 | |||
440 | buf = kmalloc(12, GFP_KERNEL); | ||
441 | if (!buf) | ||
442 | return -ENOMEM; | ||
443 | |||
444 | if (action == CTL_READ) { | ||
445 | buf[0] = 0x01; /* AV/C, STATUS */ | ||
446 | response_ok = 0x0c; /* STABLE */ | ||
447 | } else { | ||
448 | buf[0] = 0x00; /* AV/C, CONTROL */ | ||
449 | response_ok = 0x09; /* ACCEPTED */ | ||
450 | } | ||
451 | buf[1] = 0x08; /* audio unit 0 */ | ||
452 | buf[2] = 0xb8; /* FUNCTION BLOCK */ | ||
453 | buf[3] = 0x81; /* function block type: feature */ | ||
454 | buf[4] = fwspk->device_info->volume_fb_id; /* function block ID */ | ||
455 | buf[5] = attribute; /* control attribute */ | ||
456 | buf[6] = 0x02; /* selector length */ | ||
457 | buf[7] = channel; /* audio channel number */ | ||
458 | buf[8] = 0x02; /* control selector: volume */ | ||
459 | buf[9] = 0x02; /* control data length */ | ||
460 | if (action == CTL_READ) { | ||
461 | buf[10] = 0xff; | ||
462 | buf[11] = 0xff; | ||
463 | } else { | ||
464 | buf[10] = *value >> 8; | ||
465 | buf[11] = *value; | ||
466 | } | ||
467 | |||
468 | err = fcp_avc_transaction(fwspk->unit, buf, 12, buf, 12, 0x3fe); | ||
469 | if (err < 0) | ||
470 | goto error; | ||
471 | if (err < 12) { | ||
472 | dev_err(&fwspk->unit->device, "short FCP response\n"); | ||
473 | err = -EIO; | ||
474 | goto error; | ||
475 | } | ||
476 | if (buf[0] != response_ok) { | ||
477 | dev_err(&fwspk->unit->device, "volume command failed\n"); | ||
478 | err = -EIO; | ||
479 | goto error; | ||
480 | } | ||
481 | if (action == CTL_READ) | ||
482 | *value = (buf[10] << 8) | buf[11]; | ||
483 | |||
484 | err = 0; | ||
485 | |||
486 | error: | ||
487 | kfree(buf); | ||
488 | |||
489 | return err; | ||
490 | } | ||
491 | |||
492 | static int fwspk_mute_get(struct snd_kcontrol *control, | ||
493 | struct snd_ctl_elem_value *value) | ||
494 | { | ||
495 | struct fwspk *fwspk = control->private_data; | ||
496 | |||
497 | value->value.integer.value[0] = !fwspk->mute; | ||
498 | |||
499 | return 0; | ||
500 | } | ||
501 | |||
502 | static int fwspk_mute_put(struct snd_kcontrol *control, | ||
503 | struct snd_ctl_elem_value *value) | ||
504 | { | ||
505 | struct fwspk *fwspk = control->private_data; | ||
506 | bool mute; | ||
507 | int err; | ||
508 | |||
509 | mute = !value->value.integer.value[0]; | ||
510 | |||
511 | if (mute == fwspk->mute) | ||
512 | return 0; | ||
513 | |||
514 | err = fwspk_mute_command(fwspk, &mute, CTL_WRITE); | ||
515 | if (err < 0) | ||
516 | return err; | ||
517 | fwspk->mute = mute; | ||
518 | |||
519 | return 1; | ||
520 | } | ||
521 | |||
522 | static int fwspk_volume_info(struct snd_kcontrol *control, | ||
523 | struct snd_ctl_elem_info *info) | ||
524 | { | ||
525 | struct fwspk *fwspk = control->private_data; | ||
526 | |||
527 | info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
528 | info->count = fwspk->device_info->mixer_channels; | ||
529 | info->value.integer.min = fwspk->volume_min; | ||
530 | info->value.integer.max = fwspk->volume_max; | ||
531 | |||
532 | return 0; | ||
533 | } | ||
534 | |||
535 | static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 }; | ||
536 | |||
537 | static int fwspk_volume_get(struct snd_kcontrol *control, | ||
538 | struct snd_ctl_elem_value *value) | ||
539 | { | ||
540 | struct fwspk *fwspk = control->private_data; | ||
541 | unsigned int i; | ||
542 | |||
543 | for (i = 0; i < fwspk->device_info->mixer_channels; ++i) | ||
544 | value->value.integer.value[channel_map[i]] = fwspk->volume[i]; | ||
545 | |||
546 | return 0; | ||
547 | } | ||
548 | |||
549 | static int fwspk_volume_put(struct snd_kcontrol *control, | ||
550 | struct snd_ctl_elem_value *value) | ||
551 | { | ||
552 | struct fwspk *fwspk = control->private_data; | ||
553 | unsigned int i, changed_channels; | ||
554 | bool equal_values = true; | ||
555 | s16 volume; | ||
556 | int err; | ||
557 | |||
558 | for (i = 0; i < fwspk->device_info->mixer_channels; ++i) { | ||
559 | if (value->value.integer.value[i] < fwspk->volume_min || | ||
560 | value->value.integer.value[i] > fwspk->volume_max) | ||
561 | return -EINVAL; | ||
562 | if (value->value.integer.value[i] != | ||
563 | value->value.integer.value[0]) | ||
564 | equal_values = false; | ||
565 | } | ||
566 | |||
567 | changed_channels = 0; | ||
568 | for (i = 0; i < fwspk->device_info->mixer_channels; ++i) | ||
569 | if (value->value.integer.value[channel_map[i]] != | ||
570 | fwspk->volume[i]) | ||
571 | changed_channels |= 1 << (i + 1); | ||
572 | |||
573 | if (equal_values && changed_channels != 0) | ||
574 | changed_channels = 1 << 0; | ||
575 | |||
576 | for (i = 0; i <= fwspk->device_info->mixer_channels; ++i) { | ||
577 | volume = value->value.integer.value[channel_map[i ? i - 1 : 0]]; | ||
578 | if (changed_channels & (1 << i)) { | ||
579 | err = fwspk_volume_command(fwspk, &volume, i, | ||
580 | CTL_CURRENT, CTL_WRITE); | ||
581 | if (err < 0) | ||
582 | return err; | ||
583 | } | ||
584 | if (i > 0) | ||
585 | fwspk->volume[i - 1] = volume; | ||
586 | } | ||
587 | |||
588 | return changed_channels != 0; | ||
589 | } | ||
590 | |||
591 | static int fwspk_create_mixer(struct fwspk *fwspk) | ||
592 | { | ||
593 | static const struct snd_kcontrol_new controls[] = { | ||
594 | { | ||
595 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
596 | .name = "PCM Playback Switch", | ||
597 | .info = snd_ctl_boolean_mono_info, | ||
598 | .get = fwspk_mute_get, | ||
599 | .put = fwspk_mute_put, | ||
600 | }, | ||
601 | { | ||
602 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
603 | .name = "PCM Playback Volume", | ||
604 | .info = fwspk_volume_info, | ||
605 | .get = fwspk_volume_get, | ||
606 | .put = fwspk_volume_put, | ||
607 | }, | ||
608 | }; | ||
609 | unsigned int i, first_ch; | ||
610 | int err; | ||
611 | |||
612 | err = fwspk_volume_command(fwspk, &fwspk->volume_min, | ||
613 | 0, CTL_MIN, CTL_READ); | ||
614 | if (err < 0) | ||
615 | return err; | ||
616 | err = fwspk_volume_command(fwspk, &fwspk->volume_max, | ||
617 | 0, CTL_MAX, CTL_READ); | ||
618 | if (err < 0) | ||
619 | return err; | ||
620 | |||
621 | err = fwspk_mute_command(fwspk, &fwspk->mute, CTL_READ); | ||
622 | if (err < 0) | ||
623 | return err; | ||
624 | |||
625 | first_ch = fwspk->device_info->mixer_channels == 1 ? 0 : 1; | ||
626 | for (i = 0; i < fwspk->device_info->mixer_channels; ++i) { | ||
627 | err = fwspk_volume_command(fwspk, &fwspk->volume[i], | ||
628 | first_ch + i, CTL_CURRENT, CTL_READ); | ||
629 | if (err < 0) | ||
630 | return err; | ||
631 | } | ||
632 | |||
633 | for (i = 0; i < ARRAY_SIZE(controls); ++i) { | ||
634 | err = snd_ctl_add(fwspk->card, | ||
635 | snd_ctl_new1(&controls[i], fwspk)); | ||
636 | if (err < 0) | ||
637 | return err; | ||
638 | } | ||
639 | |||
640 | return 0; | ||
641 | } | ||
642 | |||
643 | static u32 fwspk_read_firmware_version(struct fw_unit *unit) | ||
644 | { | ||
645 | __be32 data; | ||
646 | int err; | ||
647 | |||
648 | err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, | ||
649 | OXFORD_FIRMWARE_ID_ADDRESS, &data, 4); | ||
650 | return err >= 0 ? be32_to_cpu(data) : 0; | ||
651 | } | ||
652 | |||
653 | static void fwspk_card_free(struct snd_card *card) | ||
654 | { | ||
655 | struct fwspk *fwspk = card->private_data; | ||
656 | struct fw_device *dev = fw_parent_device(fwspk->unit); | ||
657 | |||
658 | amdtp_out_stream_destroy(&fwspk->stream); | ||
659 | cmp_connection_destroy(&fwspk->connection); | ||
660 | fw_unit_put(fwspk->unit); | ||
661 | fw_device_put(dev); | ||
662 | mutex_destroy(&fwspk->mutex); | ||
663 | } | ||
664 | |||
665 | static const struct device_info *__devinit fwspk_detect(struct fw_device *dev) | ||
666 | { | ||
667 | static const struct device_info griffin_firewave = { | ||
668 | .driver_name = "FireWave", | ||
669 | .short_name = "FireWave", | ||
670 | .long_name = "Griffin FireWave Surround", | ||
671 | .pcm_constraints = firewave_constraints, | ||
672 | .mixer_channels = 6, | ||
673 | .mute_fb_id = 0x01, | ||
674 | .volume_fb_id = 0x02, | ||
675 | }; | ||
676 | static const struct device_info lacie_speakers = { | ||
677 | .driver_name = "FWSpeakers", | ||
678 | .short_name = "FireWire Speakers", | ||
679 | .long_name = "LaCie FireWire Speakers", | ||
680 | .pcm_constraints = lacie_speakers_constraints, | ||
681 | .mixer_channels = 1, | ||
682 | .mute_fb_id = 0x01, | ||
683 | .volume_fb_id = 0x01, | ||
684 | }; | ||
685 | struct fw_csr_iterator i; | ||
686 | int key, value; | ||
687 | |||
688 | fw_csr_iterator_init(&i, dev->config_rom); | ||
689 | while (fw_csr_iterator_next(&i, &key, &value)) | ||
690 | if (key == CSR_VENDOR) | ||
691 | switch (value) { | ||
692 | case VENDOR_GRIFFIN: | ||
693 | return &griffin_firewave; | ||
694 | case VENDOR_LACIE: | ||
695 | return &lacie_speakers; | ||
696 | } | ||
697 | |||
698 | return NULL; | ||
699 | } | ||
700 | |||
701 | static int __devinit fwspk_probe(struct device *unit_dev) | ||
702 | { | ||
703 | struct fw_unit *unit = fw_unit(unit_dev); | ||
704 | struct fw_device *fw_dev = fw_parent_device(unit); | ||
705 | struct snd_card *card; | ||
706 | struct fwspk *fwspk; | ||
707 | u32 firmware; | ||
708 | int err; | ||
709 | |||
710 | err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*fwspk), &card); | ||
711 | if (err < 0) | ||
712 | return err; | ||
713 | snd_card_set_dev(card, unit_dev); | ||
714 | |||
715 | fwspk = card->private_data; | ||
716 | fwspk->card = card; | ||
717 | mutex_init(&fwspk->mutex); | ||
718 | fw_device_get(fw_dev); | ||
719 | fwspk->unit = fw_unit_get(unit); | ||
720 | fwspk->device_info = fwspk_detect(fw_dev); | ||
721 | if (!fwspk->device_info) { | ||
722 | err = -ENODEV; | ||
723 | goto err_unit; | ||
724 | } | ||
725 | |||
726 | err = cmp_connection_init(&fwspk->connection, unit, 0); | ||
727 | if (err < 0) | ||
728 | goto err_unit; | ||
729 | |||
730 | err = amdtp_out_stream_init(&fwspk->stream, unit, CIP_NONBLOCKING); | ||
731 | if (err < 0) | ||
732 | goto err_connection; | ||
733 | |||
734 | card->private_free = fwspk_card_free; | ||
735 | |||
736 | strcpy(card->driver, fwspk->device_info->driver_name); | ||
737 | strcpy(card->shortname, fwspk->device_info->short_name); | ||
738 | firmware = fwspk_read_firmware_version(unit); | ||
739 | snprintf(card->longname, sizeof(card->longname), | ||
740 | "%s (OXFW%x %04x), GUID %08x%08x at %s, S%d", | ||
741 | fwspk->device_info->long_name, | ||
742 | firmware >> 20, firmware & 0xffff, | ||
743 | fw_dev->config_rom[3], fw_dev->config_rom[4], | ||
744 | dev_name(&unit->device), 100 << fw_dev->max_speed); | ||
745 | strcpy(card->mixername, "OXFW970"); | ||
746 | |||
747 | err = fwspk_create_pcm(fwspk); | ||
748 | if (err < 0) | ||
749 | goto error; | ||
750 | |||
751 | err = fwspk_create_mixer(fwspk); | ||
752 | if (err < 0) | ||
753 | goto error; | ||
754 | |||
755 | err = snd_card_register(card); | ||
756 | if (err < 0) | ||
757 | goto error; | ||
758 | |||
759 | dev_set_drvdata(unit_dev, fwspk); | ||
760 | |||
761 | return 0; | ||
762 | |||
763 | err_connection: | ||
764 | cmp_connection_destroy(&fwspk->connection); | ||
765 | err_unit: | ||
766 | fw_unit_put(fwspk->unit); | ||
767 | fw_device_put(fw_dev); | ||
768 | mutex_destroy(&fwspk->mutex); | ||
769 | error: | ||
770 | snd_card_free(card); | ||
771 | return err; | ||
772 | } | ||
773 | |||
774 | static int __devexit fwspk_remove(struct device *dev) | ||
775 | { | ||
776 | struct fwspk *fwspk = dev_get_drvdata(dev); | ||
777 | |||
778 | snd_card_disconnect(fwspk->card); | ||
779 | |||
780 | mutex_lock(&fwspk->mutex); | ||
781 | amdtp_out_stream_pcm_abort(&fwspk->stream); | ||
782 | fwspk_stop_stream(fwspk); | ||
783 | mutex_unlock(&fwspk->mutex); | ||
784 | |||
785 | snd_card_free_when_closed(fwspk->card); | ||
786 | |||
787 | return 0; | ||
788 | } | ||
789 | |||
790 | static void fwspk_bus_reset(struct fw_unit *unit) | ||
791 | { | ||
792 | struct fwspk *fwspk = dev_get_drvdata(&unit->device); | ||
793 | |||
794 | fcp_bus_reset(fwspk->unit); | ||
795 | |||
796 | if (cmp_connection_update(&fwspk->connection) < 0) { | ||
797 | mutex_lock(&fwspk->mutex); | ||
798 | amdtp_out_stream_pcm_abort(&fwspk->stream); | ||
799 | fwspk_stop_stream(fwspk); | ||
800 | mutex_unlock(&fwspk->mutex); | ||
801 | return; | ||
802 | } | ||
803 | |||
804 | amdtp_out_stream_update(&fwspk->stream); | ||
805 | } | ||
806 | |||
807 | static const struct ieee1394_device_id fwspk_id_table[] = { | ||
808 | { | ||
809 | .match_flags = IEEE1394_MATCH_VENDOR_ID | | ||
810 | IEEE1394_MATCH_MODEL_ID | | ||
811 | IEEE1394_MATCH_SPECIFIER_ID | | ||
812 | IEEE1394_MATCH_VERSION, | ||
813 | .vendor_id = VENDOR_GRIFFIN, | ||
814 | .model_id = 0x00f970, | ||
815 | .specifier_id = SPECIFIER_1394TA, | ||
816 | .version = VERSION_AVC, | ||
817 | }, | ||
818 | { | ||
819 | .match_flags = IEEE1394_MATCH_VENDOR_ID | | ||
820 | IEEE1394_MATCH_MODEL_ID | | ||
821 | IEEE1394_MATCH_SPECIFIER_ID | | ||
822 | IEEE1394_MATCH_VERSION, | ||
823 | .vendor_id = VENDOR_LACIE, | ||
824 | .model_id = 0x00f970, | ||
825 | .specifier_id = SPECIFIER_1394TA, | ||
826 | .version = VERSION_AVC, | ||
827 | }, | ||
828 | { } | ||
829 | }; | ||
830 | MODULE_DEVICE_TABLE(ieee1394, fwspk_id_table); | ||
831 | |||
832 | static struct fw_driver fwspk_driver = { | ||
833 | .driver = { | ||
834 | .owner = THIS_MODULE, | ||
835 | .name = KBUILD_MODNAME, | ||
836 | .bus = &fw_bus_type, | ||
837 | .probe = fwspk_probe, | ||
838 | .remove = __devexit_p(fwspk_remove), | ||
839 | }, | ||
840 | .update = fwspk_bus_reset, | ||
841 | .id_table = fwspk_id_table, | ||
842 | }; | ||
843 | |||
844 | static int __init alsa_fwspk_init(void) | ||
845 | { | ||
846 | return driver_register(&fwspk_driver.driver); | ||
847 | } | ||
848 | |||
849 | static void __exit alsa_fwspk_exit(void) | ||
850 | { | ||
851 | driver_unregister(&fwspk_driver.driver); | ||
852 | } | ||
853 | |||
854 | module_init(alsa_fwspk_init); | ||
855 | module_exit(alsa_fwspk_exit); | ||