History

ALSA: add DICE driver

As a start point for further development, this is an incomplete driver for DICE devices: - only playback (so no clock source except the bus clock) - only 44.1 kHz - no MIDI - recovery after bus reset is slow - hwdep device is created, but not actually implemented Contains compilation fixes by Stefan Richter.

Clemens Ladisch committed 15 years ago

Commit 82fbb4f1 parent 6e466457 files · +1078 −1Browse files

Unified · Split

7 files
  1. Documentation/ioctl/ioctl-number.txt +1 −0
  2. include/uapi/sound/Kbuild +1 −0
  3. include/uapi/sound/asound.h +2 −1
  4. include/uapi/sound/firewire.h +51 −0
  5. sound/firewire/Kconfig +13 −0
  6. sound/firewire/Makefile +2 −0
  7. sound/firewire/dice.c +1008 −0
Documentation/ioctl/ioctl-number.txt+1−0
@@ -138,6 +138,7 @@ Code Seq#(hex) Include File Comments · 137 unmodified lines
138138 'H' C0-DF net/bluetooth/cmtp/cmtp.h conflict!
139139 'H' C0-DF net/bluetooth/bnep/bnep.h conflict!
140140 'H' F1 linux/hid-roccat.h <mailto:erazor_de@users.sourceforge.net>
141+'H' F8-FA sound/firewire.h
141142 'I' all linux/isdn.h conflict!
142143 'I' 00-0F drivers/isdn/divert/isdn_divert.h conflict!
143144 'I' 40-4F linux/mISDNif.h conflict!
include/uapi/sound/Kbuild+1−0
@@ -5,6 +5,7 @@ header-y += asound_fm.h · 4 unmodified lines
55 header-y += compress_offload.h
66 header-y += compress_params.h
77 header-y += emu10k1.h
8+header-y += firewire.h
89 header-y += hdsp.h
910 header-y += hdspm.h
1011 header-y += sb16_csp.h
include/uapi/sound/asound.h+2−1
@@ -93,9 +93,10 @@ enum { · 92 unmodified lines
9393 SNDRV_HWDEP_IFACE_SB_RC, /* SB Extigy/Audigy2NX remote control */
9494 SNDRV_HWDEP_IFACE_HDA, /* HD-audio */
9595 SNDRV_HWDEP_IFACE_USB_STREAM, /* direct access to usb stream */
96+ SNDRV_HWDEP_IFACE_FW_DICE, /* TC DICE FireWire device */
9697
9798 /* Don't forget to change the following: */
98− SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_USB_STREAM
99+ SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_DICE
99100 };
100101
101102 struct snd_hwdep_info {
include/uapi/sound/firewire.h+51−0
@@ -0,0 +1,51 @@
1+#ifndef UAPI_SOUND_FIREWIRE_H_INCLUDED
2+#define UAPI_SOUND_FIREWIRE_H_INCLUDED
3+
4+#include <linux/ioctl.h>
5+
6+/* events can be read() from the hwdep device */
7+
8+#define SNDRV_FIREWIRE_EVENT_LOCK_STATUS 0x000010cc
9+#define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION 0xd1ce004e
10+
11+struct snd_firewire_event_common {
12+ unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
13+};
14+
15+struct snd_firewire_event_lock_status {
16+ unsigned int type;
17+ unsigned int status; /* 0/1 = unlocked/locked */
18+};
19+
20+struct snd_firewire_event_dice_notification {
21+ unsigned int type;
22+ unsigned int notification; /* DICE-specific bits */
23+};
24+
25+union snd_firewire_event {
26+ struct snd_firewire_event_common common;
27+ struct snd_firewire_event_lock_status lock_status;
28+ struct snd_firewire_event_dice_notification dice_notification;
29+};
30+
31+
32+#define SNDRV_FIREWIRE_IOCTL_GET_INFO _IOR('H', 0xf8, struct snd_firewire_get_info)
33+#define SNDRV_FIREWIRE_IOCTL_LOCK _IO('H', 0xf9)
34+#define SNDRV_FIREWIRE_IOCTL_UNLOCK _IO('H', 0xfa)
35+
36+#define SNDRV_FIREWIRE_TYPE_DICE 1
37+/* Fireworks, AV/C, RME, MOTU, ... */
38+
39+struct snd_firewire_get_info {
40+ unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */
41+ unsigned int card; /* same as fw_cdev_get_info.card */
42+ unsigned char guid[8];
43+ char device_name[16]; /* device node in /dev */
44+};
45+
46+/*
47+ * SNDRV_FIREWIRE_IOCTL_LOCK prevents the driver from streaming.
48+ * Returns -EBUSY if the driver is already streaming.
49+ */
50+
51+#endif
sound/firewire/Kconfig+13−0
@@ -11,6 +11,19 @@ config SND_FIREWIRE_LIB · 10 unmodified lines
1111 tristate
1212 depends on SND_PCM
1313
14+config SND_DICE
15+ tristate "DICE devices (EXPERIMENTAL)"
16+ select SND_HWDEP
17+ select SND_PCM
18+ select SND_FIREWIRE_LIB
19+ help
20+ Say Y here to include support for many FireWire audio interfaces
21+ based on the DICE chip family (DICE-II/Jr/Mini) from TC Applied
22+ Technologies.
23+
24+ To compile this driver as a module, choose M here: the module
25+ will be called snd-dice.
26+
1427 config SND_FIREWIRE_SPEAKERS
1528 tristate "FireWire speakers"
1629 select SND_PCM
sound/firewire/Makefile+2−0
@@ -1,10 +1,12 @@
11 snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
22 fcp.o cmp.o amdtp.o
3+snd-dice-objs := dice.o
34 snd-firewire-speakers-objs := speakers.o
45 snd-isight-objs := isight.o
56 snd-scs1x-objs := scs1x.o
67
78 obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o
9+obj-$(CONFIG_SND_DICE) += snd-dice.o
810 obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
911 obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
1012 obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
sound/firewire/dice.c+1008−0
@@ -0,0 +1,1008 @@
1+/*
2+ * TC Applied Technologies Digital Interface Communications Engine 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/delay.h>
9+#include <linux/device.h>
10+#include <linux/firewire.h>
11+#include <linux/firewire-constants.h>
12+#include <linux/module.h>
13+#include <linux/mod_devicetable.h>
14+#include <linux/mutex.h>
15+#include <linux/slab.h>
16+#include <sound/control.h>
17+#include <sound/core.h>
18+#include <sound/hwdep.h>
19+#include <sound/initval.h>
20+#include <sound/pcm.h>
21+#include <sound/pcm_params.h>
22+#include "amdtp.h"
23+#include "iso-resources.h"
24+#include "lib.h"
25+
26+#define DICE_PRIVATE_SPACE 0xffffe0000000uLL
27+
28+/* offset from DICE_PRIVATE_SPACE; offsets and sizes in quadlets */
29+#define DICE_GLOBAL_OFFSET 0x00
30+#define DICE_GLOBAL_SIZE 0x04
31+#define DICE_TX_OFFSET 0x08
32+#define DICE_TX_SIZE 0x0c
33+#define DICE_RX_OFFSET 0x10
34+#define DICE_RX_SIZE 0x14
35+
36+/* pointed to by DICE_GLOBAL_OFFSET */
37+#define GLOBAL_OWNER 0x000
38+#define OWNER_NO_OWNER 0xffff000000000000uLL
39+#define OWNER_NODE_SHIFT 48
40+#define GLOBAL_NOTIFICATION 0x008
41+#define NOTIFY_RX_CFG_CHG 0x00000001
42+#define NOTIFY_TX_CFG_CHG 0x00000002
43+#define NOTIFY_DUP_ISOC 0x00000004
44+#define NOTIFY_BW_ERR 0x00000008
45+#define NOTIFY_LOCK_CHG 0x00000010
46+#define NOTIFY_CLOCK_ACCEPTED 0x00000020
47+#define NOTIFY_INTERFACE_CHG 0x00000040
48+#define NOTIFY_MESSAGE 0x00100000
49+#define GLOBAL_NICK_NAME 0x00c
50+#define NICK_NAME_SIZE 64
51+#define GLOBAL_CLOCK_SELECT 0x04c
52+#define CLOCK_SOURCE_MASK 0x000000ff
53+#define CLOCK_SOURCE_AES1 0x00000000
54+#define CLOCK_SOURCE_AES2 0x00000001
55+#define CLOCK_SOURCE_AES3 0x00000002
56+#define CLOCK_SOURCE_AES4 0x00000003
57+#define CLOCK_SOURCE_AES_ANY 0x00000004
58+#define CLOCK_SOURCE_ADAT 0x00000005
59+#define CLOCK_SOURCE_TDIF 0x00000006
60+#define CLOCK_SOURCE_WC 0x00000007
61+#define CLOCK_SOURCE_ARX1 0x00000008
62+#define CLOCK_SOURCE_ARX2 0x00000009
63+#define CLOCK_SOURCE_ARX3 0x0000000a
64+#define CLOCK_SOURCE_ARX4 0x0000000b
65+#define CLOCK_SOURCE_INTERNAL 0x0000000c
66+#define CLOCK_RATE_MASK 0x0000ff00
67+#define CLOCK_RATE_32000 0x00000000
68+#define CLOCK_RATE_44100 0x00000100
69+#define CLOCK_RATE_48000 0x00000200
70+#define CLOCK_RATE_88200 0x00000300
71+#define CLOCK_RATE_96000 0x00000400
72+#define CLOCK_RATE_176400 0x00000500
73+#define CLOCK_RATE_192000 0x00000600
74+#define CLOCK_RATE_ANY_LOW 0x00000700
75+#define CLOCK_RATE_ANY_MID 0x00000800
76+#define CLOCK_RATE_ANY_HIGH 0x00000900
77+#define CLOCK_RATE_NONE 0x00000a00
78+#define GLOBAL_ENABLE 0x050
79+#define ENABLE 0x00000001
80+#define GLOBAL_STATUS 0x054
81+#define STATUS_SOURCE_LOCKED 0x00000001
82+#define STATUS_RATE_CONFLICT 0x00000002
83+#define STATUS_NOMINAL_RATE_MASK 0x0000ff00
84+#define GLOBAL_EXTENDED_STATUS 0x058
85+#define EXT_STATUS_AES1_LOCKED 0x00000001
86+#define EXT_STATUS_AES2_LOCKED 0x00000002
87+#define EXT_STATUS_AES3_LOCKED 0x00000004
88+#define EXT_STATUS_AES4_LOCKED 0x00000008
89+#define EXT_STATUS_ADAT_LOCKED 0x00000010
90+#define EXT_STATUS_TDIF_LOCKED 0x00000020
91+#define EXT_STATUS_ARX1_LOCKED 0x00000040
92+#define EXT_STATUS_ARX2_LOCKED 0x00000080
93+#define EXT_STATUS_ARX3_LOCKED 0x00000100
94+#define EXT_STATUS_ARX4_LOCKED 0x00000200
95+#define EXT_STATUS_WC_LOCKED 0x00000400
96+#define EXT_STATUS_AES1_SLIP 0x00010000
97+#define EXT_STATUS_AES2_SLIP 0x00020000
98+#define EXT_STATUS_AES3_SLIP 0x00040000
99+#define EXT_STATUS_AES4_SLIP 0x00080000
100+#define EXT_STATUS_ADAT_SLIP 0x00100000
101+#define EXT_STATUS_TDIF_SLIP 0x00200000
102+#define EXT_STATUS_ARX1_SLIP 0x00400000
103+#define EXT_STATUS_ARX2_SLIP 0x00800000
104+#define EXT_STATUS_ARX3_SLIP 0x01000000
105+#define EXT_STATUS_ARX4_SLIP 0x02000000
106+#define EXT_STATUS_WC_SLIP 0x04000000
107+#define GLOBAL_SAMPLE_RATE 0x05c
108+#define GLOBAL_VERSION 0x060
109+#define GLOBAL_CLOCK_CAPABILITIES 0x064
110+#define CLOCK_CAP_RATE_32000 0x00000001
111+#define CLOCK_CAP_RATE_44100 0x00000002
112+#define CLOCK_CAP_RATE_48000 0x00000004
113+#define CLOCK_CAP_RATE_88200 0x00000008
114+#define CLOCK_CAP_RATE_96000 0x00000010
115+#define CLOCK_CAP_RATE_176400 0x00000020
116+#define CLOCK_CAP_RATE_192000 0x00000040
117+#define CLOCK_CAP_SOURCE_AES1 0x00010000
118+#define CLOCK_CAP_SOURCE_AES2 0x00020000
119+#define CLOCK_CAP_SOURCE_AES3 0x00040000
120+#define CLOCK_CAP_SOURCE_AES4 0x00080000
121+#define CLOCK_CAP_SOURCE_AES_ANY 0x00100000
122+#define CLOCK_CAP_SOURCE_ADAT 0x00200000
123+#define CLOCK_CAP_SOURCE_TDIF 0x00400000
124+#define CLOCK_CAP_SOURCE_WC 0x00800000
125+#define CLOCK_CAP_SOURCE_ARX1 0x01000000
126+#define CLOCK_CAP_SOURCE_ARX2 0x02000000
127+#define CLOCK_CAP_SOURCE_ARX3 0x04000000
128+#define CLOCK_CAP_SOURCE_ARX4 0x08000000
129+#define CLOCK_CAP_SOURCE_INTERNAL 0x10000000
130+#define GLOBAL_CLOCK_SOURCE_NAMES 0x068
131+#define CLOCK_SOURCE_NAMES_SIZE 256
132+
133+/* pointed to by DICE_TX_OFFSET */
134+#define TX_NUMBER 0x000
135+#define TX_SIZE 0x004
136+/* repeated TX_NUMBER times, offset by TX_SIZE quadlets */
137+#define TX_ISOCHRONOUS 0x008
138+#define TX_NUMBER_AUDIO 0x00c
139+#define TX_NUMBER_MIDI 0x010
140+#define TX_SPEED 0x014
141+#define TX_NAMES 0x018
142+#define TX_NAMES_SIZE 256
143+#define TX_AC3_CAPABILITIES 0x118
144+#define TX_AC3_ENABLE 0x11c
145+
146+/* pointed to by DICE_RX_OFFSET */
147+#define RX_NUMBER 0x000
148+#define RX_SIZE 0x004
149+/* repeated RX_NUMBER times, offset by RX_SIZE quadlets */
150+#define RX_ISOCHRONOUS 0x008
151+#define RX_SEQ_START 0x00c
152+#define RX_NUMBER_AUDIO 0x010
153+#define RX_NUMBER_MIDI 0x014
154+#define RX_NAMES 0x018
155+#define RX_NAMES_SIZE 256
156+#define RX_AC3_CAPABILITIES 0x118
157+#define RX_AC3_ENABLE 0x11c
158+
159+
160+#define FIRMWARE_LOAD_SPACE 0xffffe0100000uLL
161+
162+/* offset from FIRMWARE_LOAD_SPACE */
163+#define FIRMWARE_VERSION 0x000
164+#define FIRMWARE_OPCODE 0x004
165+#define OPCODE_MASK 0x00000fff
166+#define OPCODE_GET_IMAGE_DESC 0x00000000
167+#define OPCODE_DELETE_IMAGE 0x00000001
168+#define OPCODE_CREATE_IMAGE 0x00000002
169+#define OPCODE_UPLOAD 0x00000003
170+#define OPCODE_UPLOAD_STAT 0x00000004
171+#define OPCODE_RESET_IMAGE 0x00000005
172+#define OPCODE_TEST_ACTION 0x00000006
173+#define OPCODE_GET_RUNNING_IMAGE_VINFO 0x0000000a
174+#define OPCODE_EXECUTE 0x80000000
175+#define FIRMWARE_RETURN_STATUS 0x008
176+#define FIRMWARE_PROGRESS 0x00c
177+#define PROGRESS_CURR_MASK 0x00000fff
178+#define PROGRESS_MAX_MASK 0x00fff000
179+#define PROGRESS_TOUT_MASK 0x0f000000
180+#define PROGRESS_FLAG 0x80000000
181+#define FIRMWARE_CAPABILITIES 0x010
182+#define FL_CAP_AUTOERASE 0x00000001
183+#define FL_CAP_PROGRESS 0x00000002
184+#define FIRMWARE_DATA 0x02c
185+#define TEST_CMD_POKE 0x00000001
186+#define TEST_CMD_PEEK 0x00000002
187+#define CMD_GET_AVS_CNT 0x00000003
188+#define CMD_CLR_AVS_CNT 0x00000004
189+#define CMD_SET_MODE 0x00000005
190+#define CMD_SET_MIDIBP 0x00000006
191+#define CMD_GET_AVSPHASE 0x00000007
192+#define CMD_ENABLE_BNC_SYNC 0x00000008
193+#define CMD_PULSE_BNC_SYNC 0x00000009
194+#define CMD_EMUL_SLOW_CMD 0x0000000a
195+#define FIRMWARE_TEST_DELAY 0xfd8
196+#define FIRMWARE_TEST_BUF 0xfdc
197+
198+
199+/* EAP */
200+#define EAP_PRIVATE_SPACE 0xffffe0200000uLL
201+
202+#define EAP_CAPABILITY_OFFSET 0x000
203+#define EAP_CAPABILITY_SIZE 0x004
204+/* ... */
205+
206+#define EAP_ROUTER_CAPS 0x000
207+#define ROUTER_EXPOSED 0x00000001
208+#define ROUTER_READ_ONLY 0x00000002
209+#define ROUTER_FLASH 0x00000004
210+#define MAX_ROUTES_MASK 0xffff0000
211+#define EAP_MIXER_CAPS 0x004
212+#define MIXER_EXPOSED 0x00000001
213+#define MIXER_READ_ONLY 0x00000002
214+#define MIXER_FLASH 0x00000004
215+#define MIXER_IN_DEV_MASK 0x000000f0
216+#define MIXER_OUT_DEV_MASK 0x00000f00
217+#define MIXER_INPUTS_MASK 0x00ff0000
218+#define MIXER_OUTPUTS_MASK 0xff000000
219+#define EAP_GENERAL_CAPS 0x008
220+#define GENERAL_STREAM_CONFIG 0x00000001
221+#define GENERAL_FLASH 0x00000002
222+#define GENERAL_PEAK 0x00000004
223+#define GENERAL_MAX_TX_STREAMS_MASK 0x000000f0
224+#define GENERAL_MAX_RX_STREAMS_MASK 0x00000f00
225+#define GENERAL_STREAM_CONFIG_FLASH 0x00001000
226+#define GENERAL_CHIP_MASK 0x00ff0000
227+#define GENERAL_CHIP_DICE_II 0x00000000
228+#define GENERAL_CHIP_DICE_MINI 0x00010000
229+#define GENERAL_CHIP_DICE_JR 0x00020000
230+
231+
232+struct dice {
233+ struct snd_card *card;
234+ struct fw_unit *unit;
235+ struct mutex mutex;
236+ unsigned int global_offset;
237+ unsigned int rx_offset;
238+ struct fw_address_handler notification_handler;
239+ int owner_generation;
240+ bool global_enabled;
241+ bool stream_running;
242+ struct snd_pcm_substream *pcm;
243+ struct fw_iso_resources resources;
244+ struct amdtp_out_stream stream;
245+};
246+
247+MODULE_DESCRIPTION("DICE driver");
248+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
249+MODULE_LICENSE("GPL v2");
250+
251+static inline u64 global_address(struct dice *dice, unsigned int offset)
252+{
253+ return DICE_PRIVATE_SPACE + dice->global_offset + offset;
254+}
255+
256+// TODO: rx index
257+static inline u64 rx_address(struct dice *dice, unsigned int offset)
258+{
259+ return DICE_PRIVATE_SPACE + dice->rx_offset + offset;
260+}
261+
262+static int dice_owner_set(struct dice *dice)
263+{
264+ struct fw_device *device = fw_parent_device(dice->unit);
265+ __be64 *buffer;
266+ int rcode, err, errors = 0;
267+
268+ buffer = kmalloc(2 * 8, GFP_KERNEL);
269+ if (!buffer)
270+ return -ENOMEM;
271+
272+ for (;;) {
273+ buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
274+ buffer[1] = cpu_to_be64(
275+ ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
276+ dice->notification_handler.offset);
277+
278+ dice->owner_generation = device->generation;
279+ smp_rmb(); /* node_id vs. generation */
280+ rcode = fw_run_transaction(device->card,
281+ TCODE_LOCK_COMPARE_SWAP,
282+ device->node_id,
283+ dice->owner_generation,
284+ device->max_speed,
285+ global_address(dice, GLOBAL_OWNER),
286+ buffer, 2 * 8);
287+
288+ if (rcode == RCODE_COMPLETE) {
289+ if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER)) {
290+ err = 0;
291+ } else {
292+ dev_err(&dice->unit->device,
293+ "device is already in use\n");
294+ err = -EBUSY;
295+ }
296+ break;
297+ }
298+ if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
299+ dev_err(&dice->unit->device,
300+ "setting device owner failed: %s\n",
301+ fw_rcode_string(rcode));
302+ err = -EIO;
303+ break;
304+ }
305+ msleep(20);
306+ }
307+
308+ kfree(buffer);
309+
310+ return err;
311+}
312+
313+static int dice_owner_update(struct dice *dice)
314+{
315+ struct fw_device *device = fw_parent_device(dice->unit);
316+ __be64 *buffer;
317+ int rcode, err, errors = 0;
318+
319+ if (dice->owner_generation == -1)
320+ return 0;
321+
322+ buffer = kmalloc(2 * 8, GFP_KERNEL);
323+ if (!buffer)
324+ return -ENOMEM;
325+
326+ for (;;) {
327+ buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
328+ buffer[1] = cpu_to_be64(
329+ ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
330+ dice->notification_handler.offset);
331+
332+ dice->owner_generation = device->generation;
333+ smp_rmb(); /* node_id vs. generation */
334+ rcode = fw_run_transaction(device->card,
335+ TCODE_LOCK_COMPARE_SWAP,
336+ device->node_id,
337+ dice->owner_generation,
338+ device->max_speed,
339+ global_address(dice, GLOBAL_OWNER),
340+ buffer, 2 * 8);
341+
342+ if (rcode == RCODE_COMPLETE) {
343+ if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER)) {
344+ err = 0;
345+ } else {
346+ dev_err(&dice->unit->device,
347+ "device is already in use\n");
348+ err = -EBUSY;
349+ }
350+ break;
351+ }
352+ if (rcode == RCODE_GENERATION) {
353+ err = 0; /* try again later */
354+ break;
355+ }
356+ if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
357+ dev_err(&dice->unit->device,
358+ "setting device owner failed: %s\n",
359+ fw_rcode_string(rcode));
360+ err = -EIO;
361+ break;
362+ }
363+ msleep(20);
364+ }
365+
366+ kfree(buffer);
367+
368+ if (err < 0)
369+ dice->owner_generation = -1;
370+
371+ return err;
372+}
373+
374+static void dice_owner_clear(struct dice *dice)
375+{
376+ struct fw_device *device = fw_parent_device(dice->unit);
377+ __be64 *buffer;
378+ int rcode, errors = 0;
379+
380+ buffer = kmalloc(2 * 8, GFP_KERNEL);
381+ if (!buffer)
382+ return;
383+
384+ for (;;) {
385+ buffer[0] = cpu_to_be64(
386+ ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
387+ dice->notification_handler.offset);
388+ buffer[1] = cpu_to_be64(OWNER_NO_OWNER);
389+
390+ rcode = fw_run_transaction(device->card,
391+ TCODE_LOCK_COMPARE_SWAP,
392+ device->node_id,
393+ dice->owner_generation,
394+ device->max_speed,
395+ global_address(dice, GLOBAL_OWNER),
396+ buffer, 2 * 8);
397+
398+ if (rcode == RCODE_COMPLETE)
399+ break;
400+ if (rcode == RCODE_GENERATION)
401+ break;
402+ if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
403+ dev_err(&dice->unit->device,
404+ "clearing device owner failed: %s\n",
405+ fw_rcode_string(rcode));
406+ break;
407+ }
408+ msleep(20);
409+ }
410+
411+ kfree(buffer);
412+
413+ dice->owner_generation = -1;
414+}
415+
416+static int dice_enable_set(struct dice *dice)
417+{
418+ struct fw_device *device = fw_parent_device(dice->unit);
419+ __be32 value;
420+ int rcode, err, errors = 0;
421+
422+ value = cpu_to_be32(ENABLE);
423+ for (;;) {
424+ rcode = fw_run_transaction(device->card,
425+ TCODE_WRITE_QUADLET_REQUEST,
426+ device->node_id,
427+ dice->owner_generation,
428+ device->max_speed,
429+ global_address(dice, GLOBAL_ENABLE),
430+ &value, 4);
431+ if (rcode == RCODE_COMPLETE) {
432+ dice->global_enabled = true;
433+ err = 0;
434+ break;
435+ }
436+ if (rcode == RCODE_GENERATION) {
437+ err = -EAGAIN;
438+ break;
439+ }
440+ if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
441+ dev_err(&dice->unit->device,
442+ "device enabling failed: %s\n",
443+ fw_rcode_string(rcode));
444+ err = -EIO;
445+ break;
446+ }
447+ msleep(20);
448+ }
449+
450+ return err;
451+}
452+
453+static void dice_enable_clear(struct dice *dice)
454+{
455+ struct fw_device *device = fw_parent_device(dice->unit);
456+ __be32 value;
457+ int rcode, errors = 0;
458+
459+ value = 0;
460+ for (;;) {
461+ rcode = fw_run_transaction(device->card,
462+ TCODE_WRITE_QUADLET_REQUEST,
463+ device->node_id,
464+ dice->owner_generation,
465+ device->max_speed,
466+ global_address(dice, GLOBAL_ENABLE),
467+ &value, 4);
468+ if (rcode == RCODE_COMPLETE ||
469+ rcode == RCODE_GENERATION)
470+ break;
471+ if (rcode_is_permanent_error(rcode) || ++errors >= 3) {
472+ dev_err(&dice->unit->device,
473+ "device disabling failed: %s\n",
474+ fw_rcode_string(rcode));
475+ break;
476+ }
477+ msleep(20);
478+ }
479+ dice->global_enabled = false;
480+}
481+
482+static void dice_notification(struct fw_card *card, struct fw_request *request,
483+ int tcode, int destination, int source,
484+ int generation, unsigned long long offset,
485+ void *data, size_t length, void *callback_data)
486+{
487+ struct dice *dice = callback_data;
488+
489+ if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
490+ fw_send_response(card, request, RCODE_TYPE_ERROR);
491+ return;
492+ }
493+ if ((offset & 3) != 0) {
494+ fw_send_response(card, request, RCODE_ADDRESS_ERROR);
495+ return;
496+ }
497+ dev_info(&dice->unit->device,
498+ "notification: %08x\n", be32_to_cpup(data));
499+ fw_send_response(card, request, RCODE_COMPLETE);
500+}
501+
502+static int dice_open(struct snd_pcm_substream *substream)
503+{
504+ static const struct snd_pcm_hardware hardware = {
505+ .info = SNDRV_PCM_INFO_MMAP |
506+ SNDRV_PCM_INFO_MMAP_VALID |
507+ SNDRV_PCM_INFO_BATCH |
508+ SNDRV_PCM_INFO_INTERLEAVED |
509+ SNDRV_PCM_INFO_BLOCK_TRANSFER,
510+ .formats = AMDTP_OUT_PCM_FORMAT_BITS,
511+ .rates = SNDRV_PCM_RATE_44100,
512+ .rate_min = 44100,
513+ .rate_max = 44100,
514+ .buffer_bytes_max = 16 * 1024 * 1024,
515+ .period_bytes_min = 1,
516+ .period_bytes_max = UINT_MAX,
517+ .periods_min = 1,
518+ .periods_max = UINT_MAX,
519+ };
520+ struct dice *dice = substream->private_data;
521+ struct snd_pcm_runtime *runtime = substream->runtime;
522+ __be32 number_audio, number_midi;
523+ int err;
524+
525+ err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
526+ rx_address(dice, RX_NUMBER_AUDIO),
527+ &number_audio, 4);
528+ if (err < 0)
529+ return err;
530+ err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
531+ rx_address(dice, RX_NUMBER_MIDI),
532+ &number_midi, 4);
533+ if (err < 0)
534+ return err;
535+
536+ runtime->hw = hardware;
537+ runtime->hw.channels_min = be32_to_cpu(number_audio);
538+ runtime->hw.channels_max = be32_to_cpu(number_audio);
539+
540+ amdtp_out_stream_set_rate(&dice->stream, 44100);
541+ amdtp_out_stream_set_pcm(&dice->stream, be32_to_cpu(number_audio));
542+ amdtp_out_stream_set_midi(&dice->stream, be32_to_cpu(number_midi));
543+
544+ err = snd_pcm_hw_constraint_minmax(runtime,
545+ SNDRV_PCM_HW_PARAM_PERIOD_TIME,
546+ 5000, 8192000);
547+ if (err < 0)
548+ return err;
549+
550+ err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
551+ if (err < 0)
552+ return err;
553+
554+ return 0;
555+}
556+
557+static int dice_close(struct snd_pcm_substream *substream)
558+{
559+ return 0;
560+}
561+
562+static void dice_stop_stream(struct dice *dice)
563+{
564+ __be32 channel;
565+
566+ if (dice->stream_running) {
567+ dice_enable_clear(dice);
568+
569+ amdtp_out_stream_stop(&dice->stream);
570+
571+ channel = cpu_to_be32((u32)-1);
572+ snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
573+ rx_address(dice, RX_ISOCHRONOUS),
574+ &channel, 4);
575+
576+ fw_iso_resources_free(&dice->resources);
577+
578+ dice->stream_running = false;
579+ }
580+}
581+
582+static int dice_hw_params(struct snd_pcm_substream *substream,
583+ struct snd_pcm_hw_params *hw_params)
584+{
585+ struct dice *dice = substream->private_data;
586+ int err;
587+
588+ mutex_lock(&dice->mutex);
589+ dice_stop_stream(dice);
590+ mutex_unlock(&dice->mutex);
591+
592+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
593+ params_buffer_bytes(hw_params));
594+ if (err < 0)
595+ goto error;
596+
597+ amdtp_out_stream_set_pcm_format(&dice->stream,
598+ params_format(hw_params));
599+
600+ return 0;
601+
602+error:
603+ return err;
604+}
605+
606+static int dice_hw_free(struct snd_pcm_substream *substream)
607+{
608+ struct dice *dice = substream->private_data;
609+
610+ mutex_lock(&dice->mutex);
611+ dice_stop_stream(dice);
612+ mutex_unlock(&dice->mutex);
613+
614+ return snd_pcm_lib_free_vmalloc_buffer(substream);
615+}
616+
617+static int dice_prepare(struct snd_pcm_substream *substream)
618+{
619+ struct dice *dice = substream->private_data;
620+ struct fw_device *device = fw_parent_device(dice->unit);
621+ __be32 channel;
622+ int err;
623+
624+ mutex_lock(&dice->mutex);
625+
626+ if (amdtp_out_streaming_error(&dice->stream))
627+ dice_stop_stream(dice);
628+
629+ if (!dice->stream_running) {
630+ err = fw_iso_resources_allocate(&dice->resources,
631+ amdtp_out_stream_get_max_payload(&dice->stream),
632+ device->max_speed);
633+ if (err < 0)
634+ goto error;
635+
636+ //TODO: RX_SEQ_START
637+ channel = cpu_to_be32(dice->resources.channel);
638+ err = snd_fw_transaction(dice->unit,
639+ TCODE_WRITE_QUADLET_REQUEST,
640+ rx_address(dice, RX_ISOCHRONOUS),
641+ &channel, 4);
642+ if (err < 0)
643+ goto err_resources;
644+
645+ err = amdtp_out_stream_start(&dice->stream,
646+ dice->resources.channel,
647+ device->max_speed);
648+ if (err < 0)
649+ goto err_resources;
650+
651+ err = dice_enable_set(dice);
652+ if (err < 0)
653+ goto err_stream;
654+
655+ dice->stream_running = true;
656+ }
657+
658+ mutex_unlock(&dice->mutex);
659+
660+ amdtp_out_stream_pcm_prepare(&dice->stream);
661+
662+ return 0;
663+
664+err_stream:
665+ amdtp_out_stream_stop(&dice->stream);
666+err_resources:
667+ fw_iso_resources_free(&dice->resources);
668+error:
669+ mutex_unlock(&dice->mutex);
670+
671+ return err;
672+}
673+
674+static int dice_trigger(struct snd_pcm_substream *substream, int cmd)
675+{
676+ struct dice *dice = substream->private_data;
677+ struct snd_pcm_substream *pcm;
678+
679+ switch (cmd) {
680+ case SNDRV_PCM_TRIGGER_START:
681+ pcm = substream;
682+ break;
683+ case SNDRV_PCM_TRIGGER_STOP:
684+ pcm = NULL;
685+ break;
686+ default:
687+ return -EINVAL;
688+ }
689+ amdtp_out_stream_pcm_trigger(&dice->stream, pcm);
690+
691+ return 0;
692+}
693+
694+static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream)
695+{
696+ struct dice *dice = substream->private_data;
697+
698+ return amdtp_out_stream_pcm_pointer(&dice->stream);
699+}
700+
701+static int dice_create_pcm(struct dice *dice)
702+{
703+ static struct snd_pcm_ops ops = {
704+ .open = dice_open,
705+ .close = dice_close,
706+ .ioctl = snd_pcm_lib_ioctl,
707+ .hw_params = dice_hw_params,
708+ .hw_free = dice_hw_free,
709+ .prepare = dice_prepare,
710+ .trigger = dice_trigger,
711+ .pointer = dice_pointer,
712+ .page = snd_pcm_lib_get_vmalloc_page,
713+ .mmap = snd_pcm_lib_mmap_vmalloc,
714+ };
715+ __be32 clock;
716+ struct snd_pcm *pcm;
717+ int err;
718+
719+ clock = cpu_to_be32(CLOCK_SOURCE_ARX1 | CLOCK_RATE_44100);
720+ err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
721+ global_address(dice, GLOBAL_CLOCK_SELECT),
722+ &clock, 4);
723+ if (err < 0)
724+ return err;
725+
726+ err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm);
727+ if (err < 0)
728+ return err;
729+ pcm->private_data = dice;
730+ strcpy(pcm->name, dice->card->shortname);
731+ dice->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
732+ dice->pcm->ops = &ops;
733+
734+ return 0;
735+}
736+
737+// TODO: implement these
738+
739+static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf,
740+ long count, loff_t *offset)
741+{
742+ return -EIO;
743+}
744+
745+static int dice_hwdep_open(struct snd_hwdep *hwdep, struct file *file)
746+{
747+ return -EIO;
748+}
749+
750+static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file)
751+{
752+ return 0;
753+}
754+
755+static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
756+ poll_table *wait)
757+{
758+ return POLLERR | POLLHUP;
759+}
760+
761+static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
762+ unsigned int cmd, unsigned long arg)
763+{
764+ return -EIO;
765+}
766+
767+static int dice_create_hwdep(struct dice *dice)
768+{
769+ static const struct snd_hwdep_ops ops = {
770+ .read = dice_hwdep_read,
771+ .open = dice_hwdep_open,
772+ .release = dice_hwdep_release,
773+ .poll = dice_hwdep_poll,
774+ .ioctl = dice_hwdep_ioctl,
775+ .ioctl_compat = dice_hwdep_ioctl,
776+ };
777+ struct snd_hwdep *hwdep;
778+ int err;
779+
780+ err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep);
781+ if (err < 0)
782+ return err;
783+ strcpy(hwdep->name, "DICE");
784+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE;
785+ hwdep->ops = ops;
786+ hwdep->private_data = dice;
787+ hwdep->exclusive = true;
788+
789+ return 0;
790+}
791+
792+static void dice_card_free(struct snd_card *card)
793+{
794+ struct dice *dice = card->private_data;
795+
796+ amdtp_out_stream_destroy(&dice->stream);
797+ fw_core_remove_address_handler(&dice->notification_handler);
798+ mutex_destroy(&dice->mutex);
799+}
800+
801+static int dice_init_offsets(struct dice *dice)
802+{
803+ __be32 pointers[6];
804+ unsigned int global_size, rx_size;
805+ int err;
806+
807+ err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
808+ DICE_PRIVATE_SPACE, &pointers, 6 * 4);
809+ if (err < 0)
810+ return err;
811+
812+ dice->global_offset = be32_to_cpu(pointers[0]) * 4;
813+ global_size = be32_to_cpu(pointers[1]);
814+ dice->rx_offset = be32_to_cpu(pointers[4]) * 4;
815+ rx_size = be32_to_cpu(pointers[5]);
816+
817+ /* some sanity checks to ensure that we actually have a DICE */
818+ if (dice->global_offset < 10 * 4 || global_size < 0x168 / 4 ||
819+ dice->rx_offset < 10 * 4 || rx_size < 0x120 / 4) {
820+ dev_err(&dice->unit->device, "invalid register pointers\n");
821+ return -ENXIO;
822+ }
823+
824+ return 0;
825+}
826+
827+static void dice_card_strings(struct dice *dice)
828+{
829+ struct snd_card *card = dice->card;
830+ struct fw_device *dev = fw_parent_device(dice->unit);
831+ char vendor[32], model[32];
832+ unsigned int i;
833+ int err;
834+
835+ strcpy(card->driver, "DICE");
836+
837+ strcpy(card->shortname, "DICE");
838+ BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname));
839+ err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
840+ global_address(dice, GLOBAL_NICK_NAME),
841+ card->shortname, sizeof(card->shortname));
842+ if (err >= 0) {
843+ /* DICE strings are returned in "always-wrong" endianness */
844+ BUILD_BUG_ON(sizeof(card->shortname) % 4 != 0);
845+ for (i = 0; i < sizeof(card->shortname); i += 4)
846+ swab32s((u32 *)&card->shortname[i]);
847+ card->shortname[sizeof(card->shortname) - 1] = '\0';
848+ }
849+
850+ strcpy(vendor, "?");
851+ fw_csr_string(dev->config_rom + 5, CSR_VENDOR, vendor, sizeof(vendor));
852+ strcpy(model, "?");
853+ fw_csr_string(dice->unit->directory, CSR_MODEL, model, sizeof(model));
854+ snprintf(card->longname, sizeof(card->longname),
855+ "%s %s, GUID %08x%08x at %s, S%d",
856+ vendor, model, dev->config_rom[3], dev->config_rom[4],
857+ dev_name(&dice->unit->device), 100 << dev->max_speed);
858+
859+ strcpy(card->mixername, "DICE");
860+}
861+
862+static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
863+{
864+ struct snd_card *card;
865+ struct dice *dice;
866+ int err;
867+
868+ err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*dice), &card);
869+ if (err < 0)
870+ return err;
871+ snd_card_set_dev(card, &unit->device);
872+
873+ dice = card->private_data;
874+ dice->card = card;
875+ mutex_init(&dice->mutex);
876+ dice->unit = unit;
877+
878+ err = dice_init_offsets(dice);
879+ if (err < 0)
880+ goto err_mutex;
881+
882+ dice->notification_handler.length = 4;
883+ dice->notification_handler.address_callback = dice_notification;
884+ dice->notification_handler.callback_data = dice;
885+ err = fw_core_add_address_handler(&dice->notification_handler,
886+ &fw_high_memory_region);
887+ if (err < 0)
888+ goto err_mutex;
889+
890+ err = fw_iso_resources_init(&dice->resources, unit);
891+ if (err < 0)
892+ goto err_notification_handler;
893+ dice->resources.channels_mask = 0x00000000ffffffffuLL;
894+
895+ err = amdtp_out_stream_init(&dice->stream, unit, CIP_NONBLOCKING);
896+ if (err < 0)
897+ goto err_resources;
898+
899+ err = dice_owner_set(dice);
900+ if (err < 0)
901+ goto err_stream;
902+
903+ card->private_free = dice_card_free;
904+
905+ dice_card_strings(dice);
906+
907+ err = dice_create_pcm(dice);
908+ if (err < 0)
909+ goto error;
910+
911+ err = dice_create_hwdep(dice);
912+ if (err < 0)
913+ goto error;
914+
915+ err = snd_card_register(card);
916+ if (err < 0)
917+ goto error;
918+
919+ dev_set_drvdata(&unit->device, dice);
920+
921+ return 0;
922+
923+err_stream:
924+ amdtp_out_stream_destroy(&dice->stream);
925+err_resources:
926+ fw_iso_resources_destroy(&dice->resources);
927+err_notification_handler:
928+ fw_core_remove_address_handler(&dice->notification_handler);
929+err_mutex:
930+ mutex_destroy(&dice->mutex);
931+error:
932+ snd_card_free(card);
933+ return err;
934+}
935+
936+static void dice_remove(struct fw_unit *unit)
937+{
938+ struct dice *dice = dev_get_drvdata(&unit->device);
939+
940+ snd_card_disconnect(dice->card);
941+
942+ mutex_lock(&dice->mutex);
943+ amdtp_out_stream_pcm_abort(&dice->stream);
944+ dice_stop_stream(dice);
945+ dice_owner_clear(dice);
946+ mutex_unlock(&dice->mutex);
947+
948+ snd_card_free_when_closed(dice->card);
949+}
950+
951+static void dice_bus_reset(struct fw_unit *unit)
952+{
953+ struct dice *dice = dev_get_drvdata(&unit->device);
954+
955+ mutex_lock(&dice->mutex);
956+ /*
957+ * XXX is the following true?
958+ * On a bus reset, the DICE firmware disables streaming and then goes
959+ * off contemplating its own navel for hundreds of milliseconds before
960+ * it can react to any of our attempts to reenable streaming. This
961+ * means that we lose synchronization anyway, so we force our streams
962+ * to stop so that the application can restart them in an orderly
963+ * manner.
964+ */
965+ dice_owner_update(dice);
966+ amdtp_out_stream_pcm_abort(&dice->stream);
967+ dice_stop_stream(dice);
968+ mutex_unlock(&dice->mutex);
969+}
970+
971+#define TC_OUI 0x000166
972+#define DICE_INTERFACE 0x000001
973+
974+static const struct ieee1394_device_id dice_id_table[] = {
975+ {
976+ .match_flags = IEEE1394_MATCH_SPECIFIER_ID |
977+ IEEE1394_MATCH_VERSION,
978+ .specifier_id = TC_OUI,
979+ .version = DICE_INTERFACE,
980+ },
981+ { }
982+};
983+MODULE_DEVICE_TABLE(ieee1394, dice_id_table);
984+
985+static struct fw_driver dice_driver = {
986+ .driver = {
987+ .owner = THIS_MODULE,
988+ .name = KBUILD_MODNAME,
989+ .bus = &fw_bus_type,
990+ },
991+ .probe = dice_probe,
992+ .update = dice_bus_reset,
993+ .remove = dice_remove,
994+ .id_table = dice_id_table,
995+};
996+
997+static int __init alsa_dice_init(void)
998+{
999+ return driver_register(&dice_driver.driver);
1000+}
1001+
1002+static void __exit alsa_dice_exit(void)
1003+{
1004+ driver_unregister(&dice_driver.driver);
1005+}
1006+
1007+module_init(alsa_dice_init);
1008+module_exit(alsa_dice_exit);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t