[alsa-devel] [PATCH 00/14] ALSA: Dice enhancement
This 14 patchset is to enable ALSA Dice driver to handle more models with some functionalities: - up to 192.0 kHz - PCM capture - MIDI playback/capture - Duplex streams with synchronization (with snd-firewire-lib)
This driver doesn't support: - Synchronization between multiple devices on the same bus For this purpose, please join in user-space driver project (FFADO) - Any control functionality for internal DSP For this purpose, please use ffado-dbus-server/ffado-mixer, or write your own application
Remarks: - When this driver is loaded, user-space driver fails to register notification address, thus cannot handle device for streaming. See: http://mailman.alsa-project.org/pipermail/alsa-devel/2014-September/081731.h...
Newly supported models (as long as I know): Supported devices: TC Electronic Desktop Konnekt 6 Impact Twin Konnekt 8/24D/Live Studio Konnekt 48 PreSonus FireStudio 26x26/Mobile/Project/Tube/LightPipe StudioLive 1602/1642/2442 FireStation Mackie Onyx Blackbird Onyx i-series (latter models) Focusrite Saffire Pro 14/24/24 DSP/26/40/56 M-Audio Profire 610/2626 Alessis IO 24/26 MultiMix 12/16 Weiss ADC2 Vesta Mineva AFI 1 TAG DAC 1 INT 202 DAC 202 Flex 5000 DnR Axus FireWire I/O card 16x16 Lexicon I-ONIX FW 810S Allen and Heath Zed R16 Midas Venice F32
Updates from my previous RFC: http://mailman.alsa-project.org/pipermail/alsa-devel/2014-September/081673.h... - Improve bus reset handling, especially for registering notification address At bus reset, Dice devices initialize a register for notification address, then drivers need to register the address again. Just after powering on, devices generates any bus reset, then drivers often fail to detect stream formation because this operation requires to change sampling rate. My improvement is retries to register notification address when notification wait is timed out. - Remove experimental state - Fix minor bugs
Takashi Sakamoto (14): ALSA: dice: Rename structure and its members ALSA: dice: Move file to its own directory ALSA: dice: Split transaction functionality into a file ALSA: dice: Split stream functionality into a file ALSA: dice: Split PCM functionality into a file ALSA: dice: Split hwdep functionality into a file ALSA: dice: Split proc functionality into a file ALSA: dice: Add new functions for restriction of PCM parameters ALSA: dice: Change the way to start stream ALSA: dice: Add support for duplex streams with synchronization ALSA: dice: Support for non SYT-Match sampling clock source mode ALSA: dice: Add support for PCM capture ALSA: dice: Add support for MIDI capture/playback ALSA: dice: remove experimental state
sound/firewire/Kconfig | 7 +- sound/firewire/Makefile | 3 +- sound/firewire/dice.c | 1511 ---------------------------- sound/firewire/dice/Makefile | 3 + sound/firewire/dice/dice-hwdep.c | 190 ++++ sound/firewire/{ => dice}/dice-interface.h | 0 sound/firewire/dice/dice-midi.c | 176 ++++ sound/firewire/dice/dice-pcm.c | 404 ++++++++ sound/firewire/dice/dice-proc.c | 252 +++++ sound/firewire/dice/dice-stream.c | 424 ++++++++ sound/firewire/dice/dice-transaction.c | 380 +++++++ sound/firewire/dice/dice.c | 359 +++++++ sound/firewire/dice/dice.h | 189 ++++ 13 files changed, 2380 insertions(+), 1518 deletions(-) delete mode 100644 sound/firewire/dice.c create mode 100644 sound/firewire/dice/Makefile create mode 100644 sound/firewire/dice/dice-hwdep.c rename sound/firewire/{ => dice}/dice-interface.h (100%) create mode 100644 sound/firewire/dice/dice-midi.c create mode 100644 sound/firewire/dice/dice-pcm.c create mode 100644 sound/firewire/dice/dice-proc.c create mode 100644 sound/firewire/dice/dice-stream.c create mode 100644 sound/firewire/dice/dice-transaction.c create mode 100644 sound/firewire/dice/dice.c create mode 100644 sound/firewire/dice/dice.h
Currently, dice driver supports AMDTP out-stream. In followed commits, AMDTP in-stream will be supported but current name of members in dice structure are not propper. This commit renames these members to proper name.
Additionally, for easily distinguishing local symbols from structure tag, rename dice tag into snd_dice.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/dice.c | 162 +++++++++++++++++++++++++------------------------- 1 file changed, 81 insertions(+), 81 deletions(-)
diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c index e3a04d6..c7c8495 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice.c @@ -32,7 +32,7 @@ #include "dice-interface.h"
-struct dice { +struct snd_dice { struct snd_card *card; struct fw_unit *unit; spinlock_t lock; @@ -50,8 +50,8 @@ struct dice { struct completion clock_accepted; wait_queue_head_t hwdep_wait; u32 notification_bits; - struct fw_iso_resources resources; - struct amdtp_stream stream; + struct fw_iso_resources rx_resources; + struct amdtp_stream rx_stream; };
MODULE_DESCRIPTION("DICE driver"); @@ -87,13 +87,13 @@ static unsigned int rate_index_to_mode(unsigned int rate_index) return ((int)rate_index - 1) / 2; }
-static void dice_lock_changed(struct dice *dice) +static void dice_lock_changed(struct snd_dice *dice) { dice->dev_lock_changed = true; wake_up(&dice->hwdep_wait); }
-static int dice_try_lock(struct dice *dice) +static int dice_try_lock(struct snd_dice *dice) { int err;
@@ -114,7 +114,7 @@ out: return err; }
-static void dice_unlock(struct dice *dice) +static void dice_unlock(struct snd_dice *dice) { spin_lock_irq(&dice->lock);
@@ -128,18 +128,18 @@ out: spin_unlock_irq(&dice->lock); }
-static inline u64 global_address(struct dice *dice, unsigned int offset) +static inline u64 global_address(struct snd_dice *dice, unsigned int offset) { return DICE_PRIVATE_SPACE + dice->global_offset + offset; }
// TODO: rx index -static inline u64 rx_address(struct dice *dice, unsigned int offset) +static inline u64 rx_address(struct snd_dice *dice, unsigned int offset) { return DICE_PRIVATE_SPACE + dice->rx_offset + offset; }
-static int dice_owner_set(struct dice *dice) +static int dice_owner_set(struct snd_dice *dice) { struct fw_device *device = fw_parent_device(dice->unit); __be64 *buffer; @@ -183,7 +183,7 @@ static int dice_owner_set(struct dice *dice) return err; }
-static int dice_owner_update(struct dice *dice) +static int dice_owner_update(struct snd_dice *dice) { struct fw_device *device = fw_parent_device(dice->unit); __be64 *buffer; @@ -226,7 +226,7 @@ static int dice_owner_update(struct dice *dice) return err; }
-static void dice_owner_clear(struct dice *dice) +static void dice_owner_clear(struct snd_dice *dice) { struct fw_device *device = fw_parent_device(dice->unit); __be64 *buffer; @@ -249,7 +249,7 @@ static void dice_owner_clear(struct dice *dice) dice->owner_generation = -1; }
-static int dice_enable_set(struct dice *dice) +static int dice_enable_set(struct snd_dice *dice) { __be32 value; int err; @@ -267,7 +267,7 @@ static int dice_enable_set(struct dice *dice) return 0; }
-static void dice_enable_clear(struct dice *dice) +static void dice_enable_clear(struct snd_dice *dice) { __be32 value;
@@ -288,7 +288,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request, int generation, unsigned long long offset, void *data, size_t length, void *callback_data) { - struct dice *dice = callback_data; + struct snd_dice *dice = callback_data; u32 bits; unsigned long flags;
@@ -317,7 +317,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request, static int dice_rate_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - struct dice *dice = rule->private; + struct snd_dice *dice = rule->private; const struct snd_interval *channels = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); struct snd_interval *rate = @@ -344,7 +344,7 @@ static int dice_rate_constraint(struct snd_pcm_hw_params *params, static int dice_channels_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - struct dice *dice = rule->private; + struct snd_dice *dice = rule->private; const struct snd_interval *rate = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = @@ -384,7 +384,7 @@ static int dice_open(struct snd_pcm_substream *substream) .periods_min = 1, .periods_max = UINT_MAX, }; - struct dice *dice = substream->private_data; + struct snd_dice *dice = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; unsigned int i; int err; @@ -420,7 +420,7 @@ static int dice_open(struct snd_pcm_substream *substream) if (err < 0) goto err_lock;
- err = amdtp_stream_add_pcm_hw_constraints(&dice->stream, runtime); + err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime); if (err < 0) goto err_lock;
@@ -434,47 +434,47 @@ error:
static int dice_close(struct snd_pcm_substream *substream) { - struct dice *dice = substream->private_data; + struct snd_dice *dice = substream->private_data;
dice_unlock(dice);
return 0; }
-static int dice_stream_start_packets(struct dice *dice) +static int dice_stream_start_packets(struct snd_dice *dice) { int err;
- if (amdtp_stream_running(&dice->stream)) + if (amdtp_stream_running(&dice->rx_stream)) return 0;
- err = amdtp_stream_start(&dice->stream, dice->resources.channel, + err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel, fw_parent_device(dice->unit)->max_speed); if (err < 0) return err;
err = dice_enable_set(dice); if (err < 0) { - amdtp_stream_stop(&dice->stream); + amdtp_stream_stop(&dice->rx_stream); return err; }
return 0; }
-static int dice_stream_start(struct dice *dice) +static int dice_stream_start(struct snd_dice *dice) { __be32 channel; int err;
- if (!dice->resources.allocated) { - err = fw_iso_resources_allocate(&dice->resources, - amdtp_stream_get_max_payload(&dice->stream), + if (!dice->rx_resources.allocated) { + err = fw_iso_resources_allocate(&dice->rx_resources, + amdtp_stream_get_max_payload(&dice->rx_stream), fw_parent_device(dice->unit)->max_speed); if (err < 0) goto error;
- channel = cpu_to_be32(dice->resources.channel); + channel = cpu_to_be32(dice->rx_resources.channel); err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, rx_address(dice, RX_ISOCHRONOUS), @@ -494,36 +494,36 @@ err_rx_channel: snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0); err_resources: - fw_iso_resources_free(&dice->resources); + fw_iso_resources_free(&dice->rx_resources); error: return err; }
-static void dice_stream_stop_packets(struct dice *dice) +static void dice_stream_stop_packets(struct snd_dice *dice) { - if (amdtp_stream_running(&dice->stream)) { + if (amdtp_stream_running(&dice->rx_stream)) { dice_enable_clear(dice); - amdtp_stream_stop(&dice->stream); + amdtp_stream_stop(&dice->rx_stream); } }
-static void dice_stream_stop(struct dice *dice) +static void dice_stream_stop(struct snd_dice *dice) { __be32 channel;
dice_stream_stop_packets(dice);
- if (!dice->resources.allocated) + if (!dice->rx_resources.allocated) return;
channel = cpu_to_be32((u32)-1); snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0);
- fw_iso_resources_free(&dice->resources); + fw_iso_resources_free(&dice->rx_resources); }
-static int dice_change_rate(struct dice *dice, unsigned int clock_rate) +static int dice_change_rate(struct snd_dice *dice, unsigned int clock_rate) { __be32 value; int err; @@ -547,7 +547,7 @@ static int dice_change_rate(struct dice *dice, unsigned int clock_rate) static int dice_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - struct dice *dice = substream->private_data; + struct snd_dice *dice = substream->private_data; unsigned int rate_index, mode, rate, channels, i; int err;
@@ -585,24 +585,24 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
rate /= 2; channels *= 2; - dice->stream.double_pcm_frames = true; + dice->rx_stream.double_pcm_frames = true; } else { - dice->stream.double_pcm_frames = false; + dice->rx_stream.double_pcm_frames = false; }
mode = rate_index_to_mode(rate_index); - amdtp_stream_set_parameters(&dice->stream, rate, channels, + amdtp_stream_set_parameters(&dice->rx_stream, rate, channels, dice->rx_midi_ports[mode]); if (rate_index > 4) { channels /= 2;
for (i = 0; i < channels; i++) { - dice->stream.pcm_positions[i] = i * 2; - dice->stream.pcm_positions[i + channels] = i * 2 + 1; + dice->rx_stream.pcm_positions[i] = i * 2; + dice->rx_stream.pcm_positions[i + channels] = i * 2 + 1; } }
- amdtp_stream_set_pcm_format(&dice->stream, + amdtp_stream_set_pcm_format(&dice->rx_stream, params_format(hw_params));
return 0; @@ -610,7 +610,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
static int dice_hw_free(struct snd_pcm_substream *substream) { - struct dice *dice = substream->private_data; + struct snd_dice *dice = substream->private_data;
mutex_lock(&dice->mutex); dice_stream_stop(dice); @@ -621,12 +621,12 @@ static int dice_hw_free(struct snd_pcm_substream *substream)
static int dice_prepare(struct snd_pcm_substream *substream) { - struct dice *dice = substream->private_data; + struct snd_dice *dice = substream->private_data; int err;
mutex_lock(&dice->mutex);
- if (amdtp_streaming_error(&dice->stream)) + if (amdtp_streaming_error(&dice->rx_stream)) dice_stream_stop_packets(dice);
err = dice_stream_start(dice); @@ -637,14 +637,14 @@ static int dice_prepare(struct snd_pcm_substream *substream)
mutex_unlock(&dice->mutex);
- amdtp_stream_pcm_prepare(&dice->stream); + amdtp_stream_pcm_prepare(&dice->rx_stream);
return 0; }
static int dice_trigger(struct snd_pcm_substream *substream, int cmd) { - struct dice *dice = substream->private_data; + struct snd_dice *dice = substream->private_data; struct snd_pcm_substream *pcm;
switch (cmd) { @@ -657,19 +657,19 @@ static int dice_trigger(struct snd_pcm_substream *substream, int cmd) default: return -EINVAL; } - amdtp_stream_pcm_trigger(&dice->stream, pcm); + amdtp_stream_pcm_trigger(&dice->rx_stream, pcm);
return 0; }
static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream) { - struct dice *dice = substream->private_data; + struct snd_dice *dice = substream->private_data;
- return amdtp_stream_pcm_pointer(&dice->stream); + return amdtp_stream_pcm_pointer(&dice->rx_stream); }
-static int dice_create_pcm(struct dice *dice) +static int dice_create_pcm(struct snd_dice *dice) { static struct snd_pcm_ops ops = { .open = dice_open, @@ -699,7 +699,7 @@ static int dice_create_pcm(struct dice *dice) static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, loff_t *offset) { - struct dice *dice = hwdep->private_data; + struct snd_dice *dice = hwdep->private_data; DEFINE_WAIT(wait); union snd_firewire_event event;
@@ -741,7 +741,7 @@ static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait) { - struct dice *dice = hwdep->private_data; + struct snd_dice *dice = hwdep->private_data; unsigned int events;
poll_wait(file, &dice->hwdep_wait, wait); @@ -756,7 +756,7 @@ static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, return events; }
-static int dice_hwdep_get_info(struct dice *dice, void __user *arg) +static int dice_hwdep_get_info(struct snd_dice *dice, void __user *arg) { struct fw_device *dev = fw_parent_device(dice->unit); struct snd_firewire_get_info info; @@ -775,7 +775,7 @@ static int dice_hwdep_get_info(struct dice *dice, void __user *arg) return 0; }
-static int dice_hwdep_lock(struct dice *dice) +static int dice_hwdep_lock(struct snd_dice *dice) { int err;
@@ -793,7 +793,7 @@ static int dice_hwdep_lock(struct dice *dice) return err; }
-static int dice_hwdep_unlock(struct dice *dice) +static int dice_hwdep_unlock(struct snd_dice *dice) { int err;
@@ -813,7 +813,7 @@ static int dice_hwdep_unlock(struct dice *dice)
static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file) { - struct dice *dice = hwdep->private_data; + struct snd_dice *dice = hwdep->private_data;
spin_lock_irq(&dice->lock); if (dice->dev_lock_count == -1) @@ -826,7 +826,7 @@ static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file) static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, unsigned int cmd, unsigned long arg) { - struct dice *dice = hwdep->private_data; + struct snd_dice *dice = hwdep->private_data;
switch (cmd) { case SNDRV_FIREWIRE_IOCTL_GET_INFO: @@ -851,7 +851,7 @@ static int dice_hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, #define dice_hwdep_compat_ioctl NULL #endif
-static int dice_create_hwdep(struct dice *dice) +static int dice_create_hwdep(struct snd_dice *dice) { static const struct snd_hwdep_ops ops = { .read = dice_hwdep_read, @@ -875,7 +875,7 @@ static int dice_create_hwdep(struct dice *dice) return 0; }
-static int dice_proc_read_mem(struct dice *dice, void *buffer, +static int dice_proc_read_mem(struct snd_dice *dice, void *buffer, unsigned int offset_q, unsigned int quadlets) { unsigned int i; @@ -898,8 +898,8 @@ static const char *str_from_array(const char *const strs[], unsigned int count, { if (i < count) return strs[i]; - else - return "(unknown)"; + + return "(unknown)"; }
static void dice_proc_fixup_string(char *s, unsigned int size) @@ -934,7 +934,7 @@ static void dice_proc_read(struct snd_info_entry *entry, "32000", "44100", "48000", "88200", "96000", "176400", "192000", "any low", "any mid", "any high", "none" }; - struct dice *dice = entry->private_data; + struct snd_dice *dice = entry->private_data; u32 sections[ARRAY_SIZE(section_names) * 2]; struct { u32 number; @@ -1109,7 +1109,7 @@ static void dice_proc_read(struct snd_info_entry *entry, } }
-static void dice_create_proc(struct dice *dice) +static void dice_create_proc(struct snd_dice *dice) { struct snd_info_entry *entry;
@@ -1119,9 +1119,9 @@ static void dice_create_proc(struct dice *dice)
static void dice_card_free(struct snd_card *card) { - struct dice *dice = card->private_data; + struct snd_dice *dice = card->private_data;
- amdtp_stream_destroy(&dice->stream); + amdtp_stream_destroy(&dice->rx_stream); fw_core_remove_address_handler(&dice->notification_handler); mutex_destroy(&dice->mutex); } @@ -1216,7 +1216,7 @@ static int dice_interface_check(struct fw_unit *unit) return 0; }
-static int highest_supported_mode_rate(struct dice *dice, unsigned int mode) +static int highest_supported_mode_rate(struct snd_dice *dice, unsigned int mode) { int i;
@@ -1228,7 +1228,7 @@ static int highest_supported_mode_rate(struct dice *dice, unsigned int mode) return -1; }
-static int dice_read_mode_params(struct dice *dice, unsigned int mode) +static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode) { __be32 values[2]; int rate_index, err; @@ -1256,7 +1256,7 @@ static int dice_read_mode_params(struct dice *dice, unsigned int mode) return 0; }
-static int dice_read_params(struct dice *dice) +static int dice_read_params(struct snd_dice *dice) { __be32 pointers[6]; __be32 value; @@ -1297,7 +1297,7 @@ static int dice_read_params(struct dice *dice) return 0; }
-static void dice_card_strings(struct dice *dice) +static void dice_card_strings(struct snd_dice *dice) { struct snd_card *card = dice->card; struct fw_device *dev = fw_parent_device(dice->unit); @@ -1335,7 +1335,7 @@ static void dice_card_strings(struct dice *dice) static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) { struct snd_card *card; - struct dice *dice; + struct snd_dice *dice; __be32 clock_sel; int err;
@@ -1372,12 +1372,12 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) if (err < 0) goto err_owner;
- err = fw_iso_resources_init(&dice->resources, unit); + err = fw_iso_resources_init(&dice->rx_resources, unit); if (err < 0) goto err_owner; - dice->resources.channels_mask = 0x00000000ffffffffuLL; + dice->rx_resources.channels_mask = 0x00000000ffffffffuLL;
- err = amdtp_stream_init(&dice->stream, unit, AMDTP_OUT_STREAM, + err = amdtp_stream_init(&dice->rx_stream, unit, AMDTP_OUT_STREAM, CIP_BLOCKING); if (err < 0) goto err_resources; @@ -1418,7 +1418,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) return 0;
err_resources: - fw_iso_resources_destroy(&dice->resources); + fw_iso_resources_destroy(&dice->rx_resources); err_owner: dice_owner_clear(dice); err_notification_handler: @@ -1432,9 +1432,9 @@ error:
static void dice_remove(struct fw_unit *unit) { - struct dice *dice = dev_get_drvdata(&unit->device); + struct snd_dice *dice = dev_get_drvdata(&unit->device);
- amdtp_stream_pcm_abort(&dice->stream); + amdtp_stream_pcm_abort(&dice->rx_stream);
snd_card_disconnect(dice->card);
@@ -1450,7 +1450,7 @@ static void dice_remove(struct fw_unit *unit)
static void dice_bus_reset(struct fw_unit *unit) { - struct dice *dice = dev_get_drvdata(&unit->device); + struct snd_dice *dice = dev_get_drvdata(&unit->device);
/* * On a bus reset, the DICE firmware disables streaming and then goes @@ -1460,7 +1460,7 @@ static void dice_bus_reset(struct fw_unit *unit) * to stop so that the application can restart them in an orderly * manner. */ - amdtp_stream_pcm_abort(&dice->stream); + amdtp_stream_pcm_abort(&dice->rx_stream);
mutex_lock(&dice->mutex);
@@ -1469,7 +1469,7 @@ static void dice_bus_reset(struct fw_unit *unit)
dice_owner_update(dice);
- fw_iso_resources_update(&dice->resources); + fw_iso_resources_update(&dice->rx_resources);
mutex_unlock(&dice->mutex); }
In followed commits, dice driver is split into several files. For easily managing these files, this commit adds subdirectory and move file into the directory.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/Makefile | 3 +-- sound/firewire/dice/Makefile | 2 ++ sound/firewire/{ => dice}/dice-interface.h | 0 sound/firewire/{ => dice}/dice.c | 19 ++++++++++--------- 4 files changed, 13 insertions(+), 11 deletions(-) create mode 100644 sound/firewire/dice/Makefile rename sound/firewire/{ => dice}/dice-interface.h (100%) rename sound/firewire/{ => dice}/dice.c (99%)
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index fad8d49..c50761c 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -1,12 +1,11 @@ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ fcp.o cmp.o amdtp.o -snd-dice-objs := dice.o snd-firewire-speakers-objs := speakers.o snd-isight-objs := isight.o snd-scs1x-objs := scs1x.o
obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o -obj-$(CONFIG_SND_DICE) += snd-dice.o +obj-$(CONFIG_SND_DICE) += dice/ obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o obj-$(CONFIG_SND_ISIGHT) += snd-isight.o obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile new file mode 100644 index 0000000..af05d7e --- /dev/null +++ b/sound/firewire/dice/Makefile @@ -0,0 +1,2 @@ +snd-dice-objs := dice.o +obj-m += snd-dice.o diff --git a/sound/firewire/dice-interface.h b/sound/firewire/dice/dice-interface.h similarity index 100% rename from sound/firewire/dice-interface.h rename to sound/firewire/dice/dice-interface.h diff --git a/sound/firewire/dice.c b/sound/firewire/dice/dice.c similarity index 99% rename from sound/firewire/dice.c rename to sound/firewire/dice/dice.c index c7c8495..2c3158f 100644 --- a/sound/firewire/dice.c +++ b/sound/firewire/dice/dice.c @@ -26,9 +26,9 @@ #include <sound/initval.h> #include <sound/pcm.h> #include <sound/pcm_params.h> -#include "amdtp.h" -#include "iso-resources.h" -#include "lib.h" +#include "../amdtp.h" +#include "../iso-resources.h" +#include "../lib.h" #include "dice-interface.h"
@@ -133,7 +133,7 @@ static inline u64 global_address(struct snd_dice *dice, unsigned int offset) return DICE_PRIVATE_SPACE + dice->global_offset + offset; }
-// TODO: rx index +/* TODO: rx index */ static inline u64 rx_address(struct snd_dice *dice, unsigned int offset) { return DICE_PRIVATE_SPACE + dice->rx_offset + offset; @@ -721,13 +721,14 @@ static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, event.lock_status.status = dice->dev_lock_count > 0; dice->dev_lock_changed = false;
- count = min(count, (long)sizeof(event.lock_status)); + count = min_t(long, count, sizeof(event.lock_status)); } else { - event.dice_notification.type = SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION; + event.dice_notification.type = + SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION; event.dice_notification.notification = dice->notification_bits; dice->notification_bits = 0;
- count = min(count, (long)sizeof(event.dice_notification)); + count = min_t(long, count, sizeof(event.dice_notification)); }
spin_unlock_irq(&dice->lock); @@ -1448,7 +1449,7 @@ static void dice_remove(struct fw_unit *unit) snd_card_free_when_closed(dice->card); }
-static void dice_bus_reset(struct fw_unit *unit) +static void dice_update(struct fw_unit *unit) { struct snd_dice *dice = dev_get_drvdata(&unit->device);
@@ -1492,7 +1493,7 @@ static struct fw_driver dice_driver = { .bus = &fw_bus_type, }, .probe = dice_probe, - .update = dice_bus_reset, + .update = dice_update, .remove = dice_remove, .id_table = dice_id_table, };
This commit adds a file with some helper functions for transaction, and move some codes into the file with some arrangements.
For Dice chipset, well-known FCP or AV/C commands are not used to control devices. It's achieved by read/write transactions into specific addresses.
Dice's address area is split into 5 areas. Each area has its own role. The offset for each area can be got by reading head of the address area. By reading these areas, drivers can get to know device status. By writing these areas, drivers can change device status.
Dice has a specific mechanism called as 'notification'. When device status is changed, Dice devices tells the event by sending transaction. This notification is sent to an address which drivers register in advance. But this causes an issue to drivers.
To handle the notification, drivers need to allocate its own callback function to the address region in host controller. This region is exclusive. For the other applications, drivers must give a mechanism to read the received notification. For this purpose, Dice driver already implements hwdep interface.
Dice chipset doesn't allow drivers to register several addresses. In this reason, when this driver is applied to a device, the other drivers should _not_ try to register its own address to the device.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/dice/Makefile | 2 +- sound/firewire/dice/dice-transaction.c | 385 ++++++++++++++++++++++++++ sound/firewire/dice/dice.c | 489 +++++++-------------------------- sound/firewire/dice/dice.h | 160 +++++++++++ 4 files changed, 652 insertions(+), 384 deletions(-) create mode 100644 sound/firewire/dice/dice-transaction.c create mode 100644 sound/firewire/dice/dice.h
diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile index af05d7e..9f473cb 100644 --- a/sound/firewire/dice/Makefile +++ b/sound/firewire/dice/Makefile @@ -1,2 +1,2 @@ -snd-dice-objs := dice.o +snd-dice-objs := dice-transaction.o dice.o obj-m += snd-dice.o diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c new file mode 100644 index 0000000..0f4b52a --- /dev/null +++ b/sound/firewire/dice/dice-transaction.c @@ -0,0 +1,385 @@ +/* + * dice_transaction.c - a part of driver for Dice based devices + * + * Copyright (c) Clemens Ladisch + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "dice.h" + +#define NOTIFICATION_TIMEOUT_MS 100 + +static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type, + u64 offset) +{ + switch (type) { + case SND_DICE_ADDR_TYPE_TX: + offset += dice->tx_offset; + break; + case SND_DICE_ADDR_TYPE_RX: + offset += dice->rx_offset; + break; + case SND_DICE_ADDR_TYPE_SYNC: + offset += dice->sync_offset; + break; + case SND_DICE_ADDR_TYPE_RSRV: + offset += dice->rsrv_offset; + break; + case SND_DICE_ADDR_TYPE_GLOBAL: + default: + offset += dice->global_offset; + break; + }; + offset += DICE_PRIVATE_SPACE; + return offset; +} + +int snd_dice_transaction_write(struct snd_dice *dice, + enum snd_dice_addr_type type, + unsigned int offset, void *buf, unsigned int len) +{ + return snd_fw_transaction(dice->unit, + (len == 4) ? TCODE_WRITE_QUADLET_REQUEST : + TCODE_WRITE_BLOCK_REQUEST, + get_subaddr(dice, type, offset), buf, len, 0); +} + +int snd_dice_transaction_read(struct snd_dice *dice, + enum snd_dice_addr_type type, unsigned int offset, + void *buf, unsigned int len) +{ + return snd_fw_transaction(dice->unit, + (len == 4) ? TCODE_READ_QUADLET_REQUEST : + TCODE_READ_BLOCK_REQUEST, + get_subaddr(dice, type, offset), buf, len, 0); +} + +static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info) +{ + return snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT, + info, 4); +} + +static int set_clock_info(struct snd_dice *dice, + unsigned int rate, unsigned int source) +{ + unsigned int retries = 3; + unsigned int i; + __be32 info; + u32 mask; + u32 clock; + int err; +retry: + err = get_clock_info(dice, &info); + if (err < 0) + goto end; + + clock = be32_to_cpu(info); + if (source != UINT_MAX) { + mask = CLOCK_SOURCE_MASK; + clock &= ~mask; + clock |= source; + } + if (rate != UINT_MAX) { + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) { + if (snd_dice_rates[i] == rate) + break; + } + if (i == ARRAY_SIZE(snd_dice_rates)) { + err = -EINVAL; + goto end; + } + + mask = CLOCK_RATE_MASK; + clock &= ~mask; + clock |= i << CLOCK_RATE_SHIFT; + } + info = cpu_to_be32(clock); + + if (completion_done(&dice->clock_accepted)) + reinit_completion(&dice->clock_accepted); + + err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT, + &info, 4); + if (err < 0) + goto end; + + /* Timeout means it's invalid request, probably bus reset occured. */ + if (wait_for_completion_timeout(&dice->clock_accepted, + msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) { + if (retries-- == 0) { + err = -ETIMEDOUT; + goto end; + } + + err = snd_dice_transaction_reinit(dice); + if (err < 0) + goto end; + + msleep(500); /* arbitrary */ + goto retry; + } +end: + return err; +} + +int snd_dice_transaction_get_clock_source(struct snd_dice *dice, + unsigned int *source) +{ + __be32 info; + int err; + + err = get_clock_info(dice, &info); + if (err >= 0) + *source = be32_to_cpu(info) & CLOCK_SOURCE_MASK; + + return err; +} +int snd_dice_transaction_set_clock_source(struct snd_dice *dice, + unsigned int source) +{ + return set_clock_info(dice, UINT_MAX, source); +} + +int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate) +{ + __be32 info; + unsigned int index; + int err; + + err = get_clock_info(dice, &info); + if (err < 0) + goto end; + + index = (be32_to_cpu(info) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT; + if (index >= SND_DICE_RATES_COUNT) + err = -ENOSYS; + else + *rate = snd_dice_rates[index]; +end: + return err; +} +int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate) +{ + return set_clock_info(dice, rate, UINT_MAX); +} + +int snd_dice_transaction_set_enable(struct snd_dice *dice) +{ + __be32 value; + int err = 0; + + if (dice->global_enabled) + goto end; + + value = cpu_to_be32(1); + err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL, + GLOBAL_ENABLE), + &value, 4, + FW_FIXED_GENERATION | dice->owner_generation); + if (err < 0) + goto end; + + dice->global_enabled = true; +end: + return err; +} + +void snd_dice_transaction_clear_enable(struct snd_dice *dice) +{ + __be32 value; + + value = 0; + snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL, + GLOBAL_ENABLE), + &value, 4, FW_QUIET | + FW_FIXED_GENERATION | dice->owner_generation); + + dice->global_enabled = false; +} + +static void dice_notification(struct fw_card *card, struct fw_request *request, + int tcode, int destination, int source, + int generation, unsigned long long offset, + void *data, size_t length, void *callback_data) +{ + struct snd_dice *dice = callback_data; + u32 bits; + unsigned long flags; + + if (tcode != TCODE_WRITE_QUADLET_REQUEST) { + fw_send_response(card, request, RCODE_TYPE_ERROR); + return; + } + if ((offset & 3) != 0) { + fw_send_response(card, request, RCODE_ADDRESS_ERROR); + return; + } + + bits = be32_to_cpup(data); + + spin_lock_irqsave(&dice->lock, flags); + dice->notification_bits |= bits; + spin_unlock_irqrestore(&dice->lock, flags); + + fw_send_response(card, request, RCODE_COMPLETE); + + if (bits & NOTIFY_CLOCK_ACCEPTED) + complete(&dice->clock_accepted); + wake_up(&dice->hwdep_wait); +} + +static int register_notification_address(struct snd_dice *dice, bool retry) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be64 *buffer; + unsigned int retries; + int err; + + retries = (retry) ? 3 : 0; + + buffer = kmalloc(2 * 8, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + for (;;) { + buffer[0] = cpu_to_be64(OWNER_NO_OWNER); + buffer[1] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); + + dice->owner_generation = device->generation; + smp_rmb(); /* node_id vs. generation */ + err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, + get_subaddr(dice, + SND_DICE_ADDR_TYPE_GLOBAL, + GLOBAL_OWNER), + buffer, 2 * 8, + FW_FIXED_GENERATION | + dice->owner_generation); + if (err == 0) { + /* success */ + if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER)) + break; + /* The address seems to be already registered. */ + if (buffer[0] == buffer[1]) + break; + + dev_err(&dice->unit->device, + "device is already in use\n"); + err = -EBUSY; + } + if (err != -EAGAIN || retries-- > 0) + break; + + msleep(20); + } + + kfree(buffer); + + if (err < 0) + dice->owner_generation = -1; + + return err; +} + +static void unregister_notification_address(struct snd_dice *dice) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be64 *buffer; + + buffer = kmalloc(2 * 8, GFP_KERNEL); + if (buffer == NULL) + return; + + buffer[0] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); + buffer[1] = cpu_to_be64(OWNER_NO_OWNER); + snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, + get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL, + GLOBAL_OWNER), + buffer, 2 * 8, FW_QUIET | + FW_FIXED_GENERATION | dice->owner_generation); + + kfree(buffer); + + dice->owner_generation = -1; +} + +void snd_dice_transaction_destroy(struct snd_dice *dice) +{ + struct fw_address_handler *handler = &dice->notification_handler; + + if (handler->callback_data == NULL) + return; + + unregister_notification_address(dice); + + fw_core_remove_address_handler(handler); + handler->callback_data = NULL; +} + +int snd_dice_transaction_reinit(struct snd_dice *dice) +{ + struct fw_address_handler *handler = &dice->notification_handler; + + if (handler->callback_data == NULL) + return -EINVAL; + + return register_notification_address(dice, false); +} + +int snd_dice_transaction_init(struct snd_dice *dice) +{ + struct fw_address_handler *handler = &dice->notification_handler; + __be32 *pointers; + int err; + + /* Use the same way which dice_interface_check() does. */ + pointers = kmalloc(sizeof(__be32) * 10, GFP_KERNEL); + if (pointers == NULL) + return -ENOMEM; + + /* Get offsets for sub-addresses */ + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE, + pointers, sizeof(__be32) * 10, 0); + if (err < 0) + goto end; + + /* Allocation callback in address space over host controller */ + handler->length = 4; + handler->address_callback = dice_notification; + handler->callback_data = dice; + err = fw_core_add_address_handler(handler, &fw_high_memory_region); + if (err < 0) { + handler->callback_data = NULL; + goto end; + } + + /* Register the address space */ + err = register_notification_address(dice, true); + if (err < 0) { + fw_core_remove_address_handler(handler); + handler->callback_data = NULL; + goto end; + } + + dice->global_offset = be32_to_cpu(pointers[0]) * 4; + dice->tx_offset = be32_to_cpu(pointers[2]) * 4; + dice->rx_offset = be32_to_cpu(pointers[4]) * 4; + dice->sync_offset = be32_to_cpu(pointers[6]) * 4; + dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4; + + /* Set up later. */ + if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4) + dice->clock_caps = 1; +end: + kfree(pointers); + return err; +} diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index 2c3158f..ddf05fd 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -5,60 +5,13 @@ * Licensed under the terms of the GNU General Public License, version 2. */
-#include <linux/compat.h> -#include <linux/completion.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/firewire.h> -#include <linux/firewire-constants.h> -#include <linux/jiffies.h> -#include <linux/module.h> -#include <linux/mod_devicetable.h> -#include <linux/mutex.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/wait.h> -#include <sound/control.h> -#include <sound/core.h> -#include <sound/firewire.h> -#include <sound/hwdep.h> -#include <sound/info.h> -#include <sound/initval.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include "../amdtp.h" -#include "../iso-resources.h" -#include "../lib.h" -#include "dice-interface.h" - - -struct snd_dice { - struct snd_card *card; - struct fw_unit *unit; - spinlock_t lock; - struct mutex mutex; - unsigned int global_offset; - unsigned int rx_offset; - unsigned int clock_caps; - unsigned int rx_channels[3]; - unsigned int rx_midi_ports[3]; - struct fw_address_handler notification_handler; - int owner_generation; - int dev_lock_count; /* > 0 driver, < 0 userspace */ - bool dev_lock_changed; - bool global_enabled; - struct completion clock_accepted; - wait_queue_head_t hwdep_wait; - u32 notification_bits; - struct fw_iso_resources rx_resources; - struct amdtp_stream rx_stream; -}; +#include "dice.h"
MODULE_DESCRIPTION("DICE driver"); MODULE_AUTHOR("Clemens Ladisch clemens@ladisch.de"); MODULE_LICENSE("GPL v2");
-static const unsigned int dice_rates[] = { +const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { /* mode 0 */ [0] = 32000, [1] = 44100, @@ -75,8 +28,8 @@ static unsigned int rate_to_index(unsigned int rate) { unsigned int i;
- for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) - if (dice_rates[i] == rate) + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) + if (snd_dice_rates[i] == rate) return i;
return 0; @@ -128,192 +81,6 @@ out: spin_unlock_irq(&dice->lock); }
-static inline u64 global_address(struct snd_dice *dice, unsigned int offset) -{ - return DICE_PRIVATE_SPACE + dice->global_offset + offset; -} - -/* TODO: rx index */ -static inline u64 rx_address(struct snd_dice *dice, unsigned int offset) -{ - return DICE_PRIVATE_SPACE + dice->rx_offset + offset; -} - -static int dice_owner_set(struct snd_dice *dice) -{ - struct fw_device *device = fw_parent_device(dice->unit); - __be64 *buffer; - int err, errors = 0; - - buffer = kmalloc(2 * 8, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - for (;;) { - buffer[0] = cpu_to_be64(OWNER_NO_OWNER); - buffer[1] = cpu_to_be64( - ((u64)device->card->node_id << OWNER_NODE_SHIFT) | - dice->notification_handler.offset); - - dice->owner_generation = device->generation; - smp_rmb(); /* node_id vs. generation */ - err = snd_fw_transaction(dice->unit, - TCODE_LOCK_COMPARE_SWAP, - global_address(dice, GLOBAL_OWNER), - buffer, 2 * 8, - FW_FIXED_GENERATION | - dice->owner_generation); - - if (err == 0) { - if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) { - dev_err(&dice->unit->device, - "device is already in use\n"); - err = -EBUSY; - } - break; - } - if (err != -EAGAIN || ++errors >= 3) - break; - - msleep(20); - } - - kfree(buffer); - - return err; -} - -static int dice_owner_update(struct snd_dice *dice) -{ - struct fw_device *device = fw_parent_device(dice->unit); - __be64 *buffer; - int err; - - if (dice->owner_generation == -1) - return 0; - - buffer = kmalloc(2 * 8, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - buffer[0] = cpu_to_be64(OWNER_NO_OWNER); - buffer[1] = cpu_to_be64( - ((u64)device->card->node_id << OWNER_NODE_SHIFT) | - dice->notification_handler.offset); - - dice->owner_generation = device->generation; - smp_rmb(); /* node_id vs. generation */ - err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, - global_address(dice, GLOBAL_OWNER), - buffer, 2 * 8, - FW_FIXED_GENERATION | dice->owner_generation); - - if (err == 0) { - if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) { - dev_err(&dice->unit->device, - "device is already in use\n"); - err = -EBUSY; - } - } else if (err == -EAGAIN) { - err = 0; /* try again later */ - } - - kfree(buffer); - - if (err < 0) - dice->owner_generation = -1; - - return err; -} - -static void dice_owner_clear(struct snd_dice *dice) -{ - struct fw_device *device = fw_parent_device(dice->unit); - __be64 *buffer; - - buffer = kmalloc(2 * 8, GFP_KERNEL); - if (!buffer) - return; - - buffer[0] = cpu_to_be64( - ((u64)device->card->node_id << OWNER_NODE_SHIFT) | - dice->notification_handler.offset); - buffer[1] = cpu_to_be64(OWNER_NO_OWNER); - snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, - global_address(dice, GLOBAL_OWNER), - buffer, 2 * 8, FW_QUIET | - FW_FIXED_GENERATION | dice->owner_generation); - - kfree(buffer); - - dice->owner_generation = -1; -} - -static int dice_enable_set(struct snd_dice *dice) -{ - __be32 value; - int err; - - value = cpu_to_be32(1); - err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - global_address(dice, GLOBAL_ENABLE), - &value, 4, - FW_FIXED_GENERATION | dice->owner_generation); - if (err < 0) - return err; - - dice->global_enabled = true; - - return 0; -} - -static void dice_enable_clear(struct snd_dice *dice) -{ - __be32 value; - - if (!dice->global_enabled) - return; - - value = 0; - snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - global_address(dice, GLOBAL_ENABLE), - &value, 4, FW_QUIET | - FW_FIXED_GENERATION | dice->owner_generation); - - dice->global_enabled = false; -} - -static void dice_notification(struct fw_card *card, struct fw_request *request, - int tcode, int destination, int source, - int generation, unsigned long long offset, - void *data, size_t length, void *callback_data) -{ - struct snd_dice *dice = callback_data; - u32 bits; - unsigned long flags; - - if (tcode != TCODE_WRITE_QUADLET_REQUEST) { - fw_send_response(card, request, RCODE_TYPE_ERROR); - return; - } - if ((offset & 3) != 0) { - fw_send_response(card, request, RCODE_ADDRESS_ERROR); - return; - } - - bits = be32_to_cpup(data); - - spin_lock_irqsave(&dice->lock, flags); - dice->notification_bits |= bits; - spin_unlock_irqrestore(&dice->lock, flags); - - fw_send_response(card, request, RCODE_COMPLETE); - - if (bits & NOTIFY_CLOCK_ACCEPTED) - complete(&dice->clock_accepted); - wake_up(&dice->hwdep_wait); -} - static int dice_rate_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { @@ -327,14 +94,14 @@ static int dice_rate_constraint(struct snd_pcm_hw_params *params, }; unsigned int i, mode;
- for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) { + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { mode = rate_index_to_mode(i); if ((dice->clock_caps & (1 << i)) && snd_interval_test(channels, dice->rx_channels[mode])) { allowed_rates.min = min(allowed_rates.min, - dice_rates[i]); + snd_dice_rates[i]); allowed_rates.max = max(allowed_rates.max, - dice_rates[i]); + snd_dice_rates[i]); } }
@@ -354,9 +121,9 @@ static int dice_channels_constraint(struct snd_pcm_hw_params *params, }; unsigned int i, mode;
- for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) if ((dice->clock_caps & (1 << i)) && - snd_interval_test(rate, dice_rates[i])) { + snd_interval_test(rate, snd_dice_rates[i])) { mode = rate_index_to_mode(i); allowed_channels.min = min(allowed_channels.min, dice->rx_channels[mode]); @@ -395,10 +162,10 @@ static int dice_open(struct snd_pcm_substream *substream)
runtime->hw = hardware;
- for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) if (dice->clock_caps & (1 << i)) runtime->hw.rates |= - snd_pcm_rate_to_rate_bit(dice_rates[i]); + snd_pcm_rate_to_rate_bit(snd_dice_rates[i]); snd_pcm_limit_hw_rates(runtime);
for (i = 0; i < 3; ++i) @@ -453,7 +220,7 @@ static int dice_stream_start_packets(struct snd_dice *dice) if (err < 0) return err;
- err = dice_enable_set(dice); + err = snd_dice_transaction_set_enable(dice); if (err < 0) { amdtp_stream_stop(&dice->rx_stream); return err; @@ -475,10 +242,8 @@ static int dice_stream_start(struct snd_dice *dice) goto error;
channel = cpu_to_be32(dice->rx_resources.channel); - err = snd_fw_transaction(dice->unit, - TCODE_WRITE_QUADLET_REQUEST, - rx_address(dice, RX_ISOCHRONOUS), - &channel, 4, 0); + err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, + &channel, 4); if (err < 0) goto err_resources; } @@ -491,8 +256,7 @@ static int dice_stream_start(struct snd_dice *dice)
err_rx_channel: channel = cpu_to_be32((u32)-1); - snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0); + snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4); err_resources: fw_iso_resources_free(&dice->rx_resources); error: @@ -502,7 +266,7 @@ error: static void dice_stream_stop_packets(struct snd_dice *dice) { if (amdtp_stream_running(&dice->rx_stream)) { - dice_enable_clear(dice); + snd_dice_transaction_clear_enable(dice); amdtp_stream_stop(&dice->rx_stream); } } @@ -517,33 +281,11 @@ static void dice_stream_stop(struct snd_dice *dice) return;
channel = cpu_to_be32((u32)-1); - snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0); + snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4);
fw_iso_resources_free(&dice->rx_resources); }
-static int dice_change_rate(struct snd_dice *dice, unsigned int clock_rate) -{ - __be32 value; - int err; - - reinit_completion(&dice->clock_accepted); - - value = cpu_to_be32(clock_rate | CLOCK_SOURCE_ARX1); - err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - global_address(dice, GLOBAL_CLOCK_SELECT), - &value, 4, 0); - if (err < 0) - return err; - - if (!wait_for_completion_timeout(&dice->clock_accepted, - msecs_to_jiffies(100))) - dev_warn(&dice->unit->device, "clock change timed out\n"); - - return 0; -} - static int dice_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { @@ -561,8 +303,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream, return err;
rate = params_rate(hw_params); - rate_index = rate_to_index(rate); - err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT); + err = snd_dice_transaction_set_rate(dice, rate); if (err < 0) return err;
@@ -577,6 +318,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream, * be aligned to SYT_INTERVAL. */ channels = params_channels(hw_params); + rate_index = rate_to_index(rate); if (rate_index > 4) { if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) { err = -ENOSYS; @@ -1118,15 +860,6 @@ static void dice_create_proc(struct snd_dice *dice) snd_info_set_text_ops(entry, dice, dice_proc_read); }
-static void dice_card_free(struct snd_card *card) -{ - struct snd_dice *dice = card->private_data; - - amdtp_stream_destroy(&dice->rx_stream); - fw_core_remove_address_handler(&dice->notification_handler); - mutex_destroy(&dice->mutex); -} - #define OUI_WEISS 0x001c6a
#define DICE_CATEGORY_ID 0x04 @@ -1143,12 +876,17 @@ static int dice_interface_check(struct fw_unit *unit) }; struct fw_device *device = fw_parent_device(unit); struct fw_csr_iterator it; - int key, value, vendor = -1, model = -1, err; + int key, val, vendor = -1, model = -1, err; unsigned int category, i; - __be32 pointers[ARRAY_SIZE(min_values)]; + __be32 *pointers, value; __be32 tx_data[4]; __be32 version;
+ pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32), + GFP_KERNEL); + if (pointers == NULL) + return -ENOMEM; + /* * Check that GUID and unit directory are constructed according to DICE * rules, i.e., that the specifier ID is the GUID's OUI, and that the @@ -1156,13 +894,13 @@ static int dice_interface_check(struct fw_unit *unit) * ID, and a 22-bit serial number. */ fw_csr_iterator_init(&it, unit->directory); - while (fw_csr_iterator_next(&it, &key, &value)) { + while (fw_csr_iterator_next(&it, &key, &val)) { switch (key) { case CSR_SPECIFIER_ID: - vendor = value; + vendor = val; break; case CSR_MODEL: - model = value; + model = val; break; } } @@ -1171,8 +909,10 @@ static int dice_interface_check(struct fw_unit *unit) else category = DICE_CATEGORY_ID; if (device->config_rom[3] != ((vendor << 8) | category) || - device->config_rom[4] >> 22 != model) - return -ENODEV; + device->config_rom[4] >> 22 != model) { + err = -ENODEV; + goto end; + }
/* * Check that the sub address spaces exist and are located inside the @@ -1180,14 +920,18 @@ static int dice_interface_check(struct fw_unit *unit) * minimally required registers are included. */ err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST, - DICE_PRIVATE_SPACE, - pointers, sizeof(pointers), 0); - if (err < 0) - return -ENODEV; - for (i = 0; i < ARRAY_SIZE(pointers); ++i) { + DICE_PRIVATE_SPACE, pointers, + sizeof(__be32) * ARRAY_SIZE(min_values), 0); + if (err < 0) { + err = -ENODEV; + goto end; + } + for (i = 0; i < ARRAY_SIZE(min_values); ++i) { value = be32_to_cpu(pointers[i]); - if (value < min_values[i] || value >= 0x40000) - return -ENODEV; + if (value < min_values[i] || value >= 0x40000) { + err = -ENODEV; + goto end; + } }
/* We support playback only. Let capture devices be handled by FFADO. */ @@ -1195,8 +939,10 @@ static int dice_interface_check(struct fw_unit *unit) DICE_PRIVATE_SPACE + be32_to_cpu(pointers[2]) * 4, tx_data, sizeof(tx_data), 0); - if (err < 0 || (tx_data[0] && tx_data[3])) - return -ENODEV; + if (err < 0 || (tx_data[0] && tx_data[3])) { + err = -ENODEV; + goto end; + }
/* * Check that the implemented DICE driver specification major version @@ -1206,22 +952,25 @@ static int dice_interface_check(struct fw_unit *unit) DICE_PRIVATE_SPACE + be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION, &version, 4, 0); - if (err < 0) - return -ENODEV; + if (err < 0) { + err = -ENODEV; + goto end; + } if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) { dev_err(&unit->device, "unknown DICE version: 0x%08x\n", be32_to_cpu(version)); - return -ENODEV; + err = -ENODEV; + goto end; } - - return 0; +end: + return err; }
static int highest_supported_mode_rate(struct snd_dice *dice, unsigned int mode) { int i;
- for (i = ARRAY_SIZE(dice_rates) - 1; i >= 0; --i) + for (i = ARRAY_SIZE(snd_dice_rates) - 1; i >= 0; --i) if ((dice->clock_caps & (1 << i)) && rate_index_to_mode(i) == mode) return i; @@ -1241,13 +990,12 @@ static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode) return 0; }
- err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT); + err = snd_dice_transaction_set_rate(dice, snd_dice_rates[rate_index]); if (err < 0) return err;
- err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, - rx_address(dice, RX_NUMBER_AUDIO), - values, 2 * 4, 0); + err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, + values, sizeof(values)); if (err < 0) return err;
@@ -1259,25 +1007,14 @@ static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode)
static int dice_read_params(struct snd_dice *dice) { - __be32 pointers[6]; __be32 value; int mode, err;
- err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, - DICE_PRIVATE_SPACE, - pointers, sizeof(pointers), 0); - if (err < 0) - return err; - - dice->global_offset = be32_to_cpu(pointers[0]) * 4; - dice->rx_offset = be32_to_cpu(pointers[4]) * 4; - /* some very old firmwares don't tell about their clock support */ - if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4) { - err = snd_fw_transaction( - dice->unit, TCODE_READ_QUADLET_REQUEST, - global_address(dice, GLOBAL_CLOCK_CAPABILITIES), - &value, 4, 0); + if (dice->clock_caps > 0) { + err = snd_dice_transaction_read_global(dice, + GLOBAL_CLOCK_CAPABILITIES, + &value, 4); if (err < 0) return err; dice->clock_caps = be32_to_cpu(value); @@ -1310,9 +1047,9 @@ static void dice_card_strings(struct snd_dice *dice)
strcpy(card->shortname, "DICE"); BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname)); - err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, - global_address(dice, GLOBAL_NICK_NAME), - card->shortname, sizeof(card->shortname), 0); + err = snd_dice_transaction_read_global(dice, GLOBAL_NICK_NAME, + card->shortname, + sizeof(card->shortname)); if (err >= 0) { /* DICE strings are returned in "always-wrong" endianness */ BUILD_BUG_ON(sizeof(card->shortname) % 4 != 0); @@ -1333,70 +1070,50 @@ static void dice_card_strings(struct snd_dice *dice) strcpy(card->mixername, "DICE"); }
+static void dice_card_free(struct snd_card *card) +{ + struct snd_dice *dice = card->private_data; + + snd_dice_transaction_destroy(dice); + mutex_destroy(&dice->mutex); +} + static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) { struct snd_card *card; struct snd_dice *dice; - __be32 clock_sel; int err;
err = dice_interface_check(unit); if (err < 0) - return err; + goto end;
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*dice), &card); if (err < 0) - return err; + goto end;
dice = card->private_data; dice->card = card; + dice->unit = unit; + card->private_free = dice_card_free; + spin_lock_init(&dice->lock); mutex_init(&dice->mutex); - dice->unit = unit; init_completion(&dice->clock_accepted); init_waitqueue_head(&dice->hwdep_wait);
- dice->notification_handler.length = 4; - dice->notification_handler.address_callback = dice_notification; - dice->notification_handler.callback_data = dice; - err = fw_core_add_address_handler(&dice->notification_handler, - &fw_high_memory_region); - if (err < 0) - goto err_mutex; - - err = dice_owner_set(dice); + err = snd_dice_transaction_init(dice); if (err < 0) - goto err_notification_handler; + goto error;
err = dice_read_params(dice); if (err < 0) - goto err_owner; - - err = fw_iso_resources_init(&dice->rx_resources, unit); - if (err < 0) - goto err_owner; - dice->rx_resources.channels_mask = 0x00000000ffffffffuLL; - - err = amdtp_stream_init(&dice->rx_stream, unit, AMDTP_OUT_STREAM, - CIP_BLOCKING); - if (err < 0) - goto err_resources; - - card->private_free = dice_card_free; + goto error;
dice_card_strings(dice);
- err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, - global_address(dice, GLOBAL_CLOCK_SELECT), - &clock_sel, 4, 0); - if (err < 0) - goto error; - clock_sel &= cpu_to_be32(~CLOCK_SOURCE_MASK); - clock_sel |= cpu_to_be32(CLOCK_SOURCE_ARX1); - err = snd_fw_transaction(unit, TCODE_WRITE_QUADLET_REQUEST, - global_address(dice, GLOBAL_CLOCK_SELECT), - &clock_sel, 4, 0); + err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1); if (err < 0) goto error;
@@ -1410,22 +1127,28 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
dice_create_proc(dice);
- err = snd_card_register(card); + err = fw_iso_resources_init(&dice->rx_resources, unit); if (err < 0) goto error; + dice->rx_resources.channels_mask = 0x00000000ffffffffuLL;
- dev_set_drvdata(&unit->device, dice); + err = amdtp_stream_init(&dice->rx_stream, unit, AMDTP_OUT_STREAM, + CIP_BLOCKING); + if (err < 0) { + fw_iso_resources_destroy(&dice->rx_resources); + goto error; + }
- return 0; + err = snd_card_register(card); + if (err < 0) { + amdtp_stream_destroy(&dice->rx_stream); + fw_iso_resources_destroy(&dice->rx_resources); + goto error; + }
-err_resources: - fw_iso_resources_destroy(&dice->rx_resources); -err_owner: - dice_owner_clear(dice); -err_notification_handler: - fw_core_remove_address_handler(&dice->notification_handler); -err_mutex: - mutex_destroy(&dice->mutex); + dev_set_drvdata(&unit->device, dice); +end: + return err; error: snd_card_free(card); return err; @@ -1442,7 +1165,6 @@ static void dice_remove(struct fw_unit *unit) mutex_lock(&dice->mutex);
dice_stream_stop(dice); - dice_owner_clear(dice);
mutex_unlock(&dice->mutex);
@@ -1453,6 +1175,11 @@ static void dice_update(struct fw_unit *unit) { struct snd_dice *dice = dev_get_drvdata(&unit->device);
+ /* The handler address register becomes initialized. */ + snd_dice_transaction_reinit(dice); + + dice->global_enabled = false; + /* * On a bus reset, the DICE firmware disables streaming and then goes * off contemplating its own navel for hundreds of milliseconds before @@ -1465,11 +1192,7 @@ static void dice_update(struct fw_unit *unit)
mutex_lock(&dice->mutex);
- dice->global_enabled = false; dice_stream_stop_packets(dice); - - dice_owner_update(dice); - fw_iso_resources_update(&dice->rx_resources);
mutex_unlock(&dice->mutex); diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h new file mode 100644 index 0000000..c756e62 --- /dev/null +++ b/sound/firewire/dice/dice.h @@ -0,0 +1,160 @@ +/* + * dice.h - a part of driver for Dice based devices + * + * Copyright (c) Clemens Ladisch + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#ifndef SOUND_DICE_H_INCLUDED +#define SOUND_DICE_H_INCLUDED + +#include <linux/compat.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/firewire.h> +#include <linux/firewire-constants.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/wait.h> + +#include <sound/control.h> +#include <sound/core.h> +#include <sound/firewire.h> +#include <sound/hwdep.h> +#include <sound/info.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> + +#include "../amdtp.h" +#include "../iso-resources.h" +#include "../lib.h" +#include "dice-interface.h" + +struct snd_dice { + struct snd_card *card; + struct fw_unit *unit; + spinlock_t lock; + struct mutex mutex; + + /* Offsets for sub-addresses */ + unsigned int global_offset; + unsigned int rx_offset; + unsigned int tx_offset; + unsigned int sync_offset; + unsigned int rsrv_offset; + + unsigned int clock_caps; + unsigned int rx_channels[3]; + unsigned int rx_midi_ports[3]; + struct fw_address_handler notification_handler; + int owner_generation; + int dev_lock_count; /* > 0 driver, < 0 userspace */ + bool dev_lock_changed; + bool global_enabled; + struct completion clock_accepted; + wait_queue_head_t hwdep_wait; + u32 notification_bits; + struct fw_iso_resources rx_resources; + struct amdtp_stream rx_stream; +}; + +enum snd_dice_addr_type { + SND_DICE_ADDR_TYPE_PRIVATE, + SND_DICE_ADDR_TYPE_GLOBAL, + SND_DICE_ADDR_TYPE_TX, + SND_DICE_ADDR_TYPE_RX, + SND_DICE_ADDR_TYPE_SYNC, + SND_DICE_ADDR_TYPE_RSRV, +}; + +int snd_dice_transaction_write(struct snd_dice *dice, + enum snd_dice_addr_type type, + unsigned int offset, + void *buf, unsigned int len); +int snd_dice_transaction_read(struct snd_dice *dice, + enum snd_dice_addr_type type, unsigned int offset, + void *buf, unsigned int len); + +static inline int snd_dice_transaction_write_global(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_write(dice, + SND_DICE_ADDR_TYPE_GLOBAL, offset, + buf, len); +} +static inline int snd_dice_transaction_read_global(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_read(dice, + SND_DICE_ADDR_TYPE_GLOBAL, offset, + buf, len); +} +static inline int snd_dice_transaction_write_tx(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_TX, offset, + buf, len); +} +static inline int snd_dice_transaction_read_tx(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_TX, offset, + buf, len); +} +static inline int snd_dice_transaction_write_rx(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_RX, offset, + buf, len); +} +static inline int snd_dice_transaction_read_rx(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_RX, offset, + buf, len); +} +static inline int snd_dice_transaction_write_sync(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_SYNC, offset, + buf, len); +} +static inline int snd_dice_transaction_read_sync(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_SYNC, offset, + buf, len); +} + +int snd_dice_transaction_set_clock_source(struct snd_dice *dice, + unsigned int source); +int snd_dice_transaction_get_clock_source(struct snd_dice *dice, + unsigned int *source); +int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate); +int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate); +int snd_dice_transaction_set_enable(struct snd_dice *dice); +void snd_dice_transaction_clear_enable(struct snd_dice *dice); +int snd_dice_transaction_init(struct snd_dice *dice); +int snd_dice_transaction_reinit(struct snd_dice *dice); +void snd_dice_transaction_destroy(struct snd_dice *dice); + +#define SND_DICE_RATES_COUNT 7 +extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT]; + +#endif
This commit adds a file with some helper functions for streaming, and move some codes into the file with some arrangements.
Well-known CMP is not used to start/stop streams for Dice chipset. It's achieved by writing to specific address. We call this way as 'enable'. When devices are 'enabled', streaming starts in registered isochronous channel. Some helper functions are already implemented in previous commit.
Basically, the stream is compliant to IEC 61883-6, so-called as AMDTP. But Dice has a specific quirk, so called-as 'Dual Wire'. This quirk is applied at 176.4/192.0kHz. In this mode, each packet includes double number of events than number in the specification, and stream runs at a half of sampling rate.
There is another quirk at bus reset. Dice chipset handles drivers' request but don't re-enable streaming. So stream should be stopped.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/dice/Makefile | 2 +- sound/firewire/dice/dice-stream.c | 206 ++++++++++++++++++++++++++ sound/firewire/dice/dice.c | 300 +++++++++----------------------------- sound/firewire/dice/dice.h | 14 ++ 4 files changed, 291 insertions(+), 231 deletions(-) create mode 100644 sound/firewire/dice/dice-stream.c
diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile index 9f473cb..867864c 100644 --- a/sound/firewire/dice/Makefile +++ b/sound/firewire/dice/Makefile @@ -1,2 +1,2 @@ -snd-dice-objs := dice-transaction.o dice.o +snd-dice-objs := dice-transaction.o dice-stream.o dice.o obj-m += snd-dice.o diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c new file mode 100644 index 0000000..ba8fe8d --- /dev/null +++ b/sound/firewire/dice/dice-stream.c @@ -0,0 +1,206 @@ +/* + * dice_stream.c - a part of driver for DICE based devices + * + * Copyright (c) Clemens Ladisch clemens@ladisch.de + * Copyright (c) 2014 Takashi Sakamoto o-takashi@sakamocchi.jp + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "dice.h" + +const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { + /* mode 0 */ + [0] = 32000, + [1] = 44100, + [2] = 48000, + /* mode 1 */ + [3] = 88200, + [4] = 96000, + /* mode 2 */ + [5] = 176400, + [6] = 192000, +}; + +int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, + unsigned int *mode) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) { + if (!(dice->clock_caps & BIT(i))) + continue; + if (snd_dice_rates[i] == rate) { + *mode = (i - 1) / 2; + return 0; + } + } + return -EINVAL; +} + +int snd_dice_stream_start_packets(struct snd_dice *dice) +{ + int err; + + if (amdtp_stream_running(&dice->rx_stream)) + return 0; + + err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel, + fw_parent_device(dice->unit)->max_speed); + if (err < 0) + return err; + + err = snd_dice_transaction_set_enable(dice); + if (err < 0) { + amdtp_stream_stop(&dice->rx_stream); + return err; + } + + return 0; +} + +int snd_dice_stream_start(struct snd_dice *dice) +{ + __be32 channel; + int err; + + if (!dice->rx_resources.allocated) { + err = fw_iso_resources_allocate(&dice->rx_resources, + amdtp_stream_get_max_payload(&dice->rx_stream), + fw_parent_device(dice->unit)->max_speed); + if (err < 0) + goto error; + + channel = cpu_to_be32(dice->rx_resources.channel); + err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, + &channel, 4); + if (err < 0) + goto err_resources; + } + + err = snd_dice_stream_start_packets(dice); + if (err < 0) + goto err_rx_channel; + + return 0; + +err_rx_channel: + channel = cpu_to_be32((u32)-1); + snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4); +err_resources: + fw_iso_resources_free(&dice->rx_resources); +error: + return err; +} + +void snd_dice_stream_stop_packets(struct snd_dice *dice) +{ + if (amdtp_stream_running(&dice->rx_stream)) { + snd_dice_transaction_clear_enable(dice); + amdtp_stream_stop(&dice->rx_stream); + } +} + +void snd_dice_stream_stop(struct snd_dice *dice) +{ + __be32 channel; + + snd_dice_stream_stop_packets(dice); + + if (!dice->rx_resources.allocated) + return; + + channel = cpu_to_be32((u32)-1); + snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4); + + fw_iso_resources_free(&dice->rx_resources); +} + +int snd_dice_stream_init(struct snd_dice *dice) +{ + int err; + + err = fw_iso_resources_init(&dice->rx_resources, dice->unit); + if (err < 0) + goto end; + dice->rx_resources.channels_mask = 0x00000000ffffffffuLL; + + err = amdtp_stream_init(&dice->rx_stream, dice->unit, AMDTP_OUT_STREAM, + CIP_BLOCKING); + if (err < 0) + goto error; + + err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1); + if (err < 0) + goto error; +end: + return err; +error: + amdtp_stream_destroy(&dice->rx_stream); + fw_iso_resources_destroy(&dice->rx_resources); + return err; +} + +void snd_dice_stream_destroy(struct snd_dice *dice) +{ + amdtp_stream_pcm_abort(&dice->rx_stream); + snd_dice_stream_stop(dice); + amdtp_stream_destroy(&dice->rx_stream); + fw_iso_resources_destroy(&dice->rx_resources); +} + +void snd_dice_stream_update(struct snd_dice *dice) +{ + /* + * On a bus reset, the DICE firmware disables streaming and then goes + * off contemplating its own navel for hundreds of milliseconds before + * it can react to any of our attempts to reenable streaming. This + * means that we lose synchronization anyway, so we force our streams + * to stop so that the application can restart them in an orderly + * manner. + */ + amdtp_stream_pcm_abort(&dice->rx_stream); + snd_dice_stream_stop_packets(dice); + fw_iso_resources_update(&dice->rx_resources); +} + +static void dice_lock_changed(struct snd_dice *dice) +{ + dice->dev_lock_changed = true; + wake_up(&dice->hwdep_wait); +} + +int snd_dice_stream_lock_try(struct snd_dice *dice) +{ + int err; + + spin_lock_irq(&dice->lock); + + if (dice->dev_lock_count < 0) { + err = -EBUSY; + goto out; + } + + if (dice->dev_lock_count++ == 0) + dice_lock_changed(dice); + err = 0; + +out: + spin_unlock_irq(&dice->lock); + + return err; +} + +void snd_dice_stream_lock_release(struct snd_dice *dice) +{ + spin_lock_irq(&dice->lock); + + if (WARN_ON(dice->dev_lock_count <= 0)) + goto out; + + if (--dice->dev_lock_count == 0) + dice_lock_changed(dice); + +out: + spin_unlock_irq(&dice->lock); +} diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index ddf05fd..588a89e 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -11,127 +11,62 @@ MODULE_DESCRIPTION("DICE driver"); MODULE_AUTHOR("Clemens Ladisch clemens@ladisch.de"); MODULE_LICENSE("GPL v2");
-const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { - /* mode 0 */ - [0] = 32000, - [1] = 44100, - [2] = 48000, - /* mode 1 */ - [3] = 88200, - [4] = 96000, - /* mode 2 */ - [5] = 176400, - [6] = 192000, -}; - -static unsigned int rate_to_index(unsigned int rate) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) - if (snd_dice_rates[i] == rate) - return i; - - return 0; -} - -static unsigned int rate_index_to_mode(unsigned int rate_index) -{ - return ((int)rate_index - 1) / 2; -} - -static void dice_lock_changed(struct snd_dice *dice) -{ - dice->dev_lock_changed = true; - wake_up(&dice->hwdep_wait); -} - -static int dice_try_lock(struct snd_dice *dice) -{ - int err; - - spin_lock_irq(&dice->lock); - - if (dice->dev_lock_count < 0) { - err = -EBUSY; - goto out; - } - - if (dice->dev_lock_count++ == 0) - dice_lock_changed(dice); - err = 0; - -out: - spin_unlock_irq(&dice->lock); - - return err; -} - -static void dice_unlock(struct snd_dice *dice) -{ - spin_lock_irq(&dice->lock); - - if (WARN_ON(dice->dev_lock_count <= 0)) - goto out; - - if (--dice->dev_lock_count == 0) - dice_lock_changed(dice); - -out: - spin_unlock_irq(&dice->lock); -} - static int dice_rate_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_dice *dice = rule->private; - const struct snd_interval *channels = + + const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval *rate = + struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval allowed_rates = { + struct snd_interval rates = { .min = UINT_MAX, .max = 0, .integer = 1 }; - unsigned int i, mode; + unsigned int i, rate, mode, *pcm_channels = dice->rx_channels;
for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { - mode = rate_index_to_mode(i); - if ((dice->clock_caps & (1 << i)) && - snd_interval_test(channels, dice->rx_channels[mode])) { - allowed_rates.min = min(allowed_rates.min, - snd_dice_rates[i]); - allowed_rates.max = max(allowed_rates.max, - snd_dice_rates[i]); - } + rate = snd_dice_rates[i]; + if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) + continue; + + if (!snd_interval_test(c, pcm_channels[mode])) + continue; + + rates.min = min(rates.min, rate); + rates.max = max(rates.max, rate); }
- return snd_interval_refine(rate, &allowed_rates); + return snd_interval_refine(r, &rates); }
static int dice_channels_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_dice *dice = rule->private; - const struct snd_interval *rate = + + const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = + struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval allowed_channels = { + struct snd_interval channels = { .min = UINT_MAX, .max = 0, .integer = 1 }; - unsigned int i, mode; + unsigned int i, rate, mode, *pcm_channels = dice->rx_channels;
- for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) - if ((dice->clock_caps & (1 << i)) && - snd_interval_test(rate, snd_dice_rates[i])) { - mode = rate_index_to_mode(i); - allowed_channels.min = min(allowed_channels.min, - dice->rx_channels[mode]); - allowed_channels.max = max(allowed_channels.max, - dice->rx_channels[mode]); - } + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { + rate = snd_dice_rates[i]; + if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) + continue;
- return snd_interval_refine(channels, &allowed_channels); + if (!snd_interval_test(r, rate)) + continue; + + channels.min = min(channels.min, pcm_channels[mode]); + channels.max = max(channels.max, pcm_channels[mode]); + } + + return snd_interval_refine(c, &channels); }
static int dice_open(struct snd_pcm_substream *substream) @@ -156,7 +91,7 @@ static int dice_open(struct snd_pcm_substream *substream) unsigned int i; int err;
- err = dice_try_lock(dice); + err = snd_dice_stream_lock_try(dice); if (err < 0) goto error;
@@ -194,7 +129,7 @@ static int dice_open(struct snd_pcm_substream *substream) return 0;
err_lock: - dice_unlock(dice); + snd_dice_stream_lock_release(dice); error: return err; } @@ -203,98 +138,20 @@ static int dice_close(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data;
- dice_unlock(dice); - - return 0; -} - -static int dice_stream_start_packets(struct snd_dice *dice) -{ - int err; - - if (amdtp_stream_running(&dice->rx_stream)) - return 0; - - err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel, - fw_parent_device(dice->unit)->max_speed); - if (err < 0) - return err; - - err = snd_dice_transaction_set_enable(dice); - if (err < 0) { - amdtp_stream_stop(&dice->rx_stream); - return err; - } + snd_dice_stream_lock_release(dice);
return 0; }
-static int dice_stream_start(struct snd_dice *dice) -{ - __be32 channel; - int err; - - if (!dice->rx_resources.allocated) { - err = fw_iso_resources_allocate(&dice->rx_resources, - amdtp_stream_get_max_payload(&dice->rx_stream), - fw_parent_device(dice->unit)->max_speed); - if (err < 0) - goto error; - - channel = cpu_to_be32(dice->rx_resources.channel); - err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, - &channel, 4); - if (err < 0) - goto err_resources; - } - - err = dice_stream_start_packets(dice); - if (err < 0) - goto err_rx_channel; - - return 0; - -err_rx_channel: - channel = cpu_to_be32((u32)-1); - snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4); -err_resources: - fw_iso_resources_free(&dice->rx_resources); -error: - return err; -} - -static void dice_stream_stop_packets(struct snd_dice *dice) -{ - if (amdtp_stream_running(&dice->rx_stream)) { - snd_dice_transaction_clear_enable(dice); - amdtp_stream_stop(&dice->rx_stream); - } -} - -static void dice_stream_stop(struct snd_dice *dice) -{ - __be32 channel; - - dice_stream_stop_packets(dice); - - if (!dice->rx_resources.allocated) - return; - - channel = cpu_to_be32((u32)-1); - snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4); - - fw_iso_resources_free(&dice->rx_resources); -} - static int dice_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; - unsigned int rate_index, mode, rate, channels, i; + unsigned int mode, rate, channels, i; int err;
mutex_lock(&dice->mutex); - dice_stream_stop(dice); + snd_dice_stream_stop(dice); mutex_unlock(&dice->mutex);
err = snd_pcm_lib_alloc_vmalloc_buffer(substream, @@ -307,6 +164,10 @@ static int dice_hw_params(struct snd_pcm_substream *substream, if (err < 0) return err;
+ err = snd_dice_stream_get_rate_mode(dice, rate, &mode); + if (err < 0) + return err; + /* * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in * one data block of AMDTP packet. Thus sampling transfer frequency is @@ -318,8 +179,7 @@ static int dice_hw_params(struct snd_pcm_substream *substream, * be aligned to SYT_INTERVAL. */ channels = params_channels(hw_params); - rate_index = rate_to_index(rate); - if (rate_index > 4) { + if (mode > 1) { if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) { err = -ENOSYS; return err; @@ -332,10 +192,9 @@ static int dice_hw_params(struct snd_pcm_substream *substream, dice->rx_stream.double_pcm_frames = false; }
- mode = rate_index_to_mode(rate_index); amdtp_stream_set_parameters(&dice->rx_stream, rate, channels, dice->rx_midi_ports[mode]); - if (rate_index > 4) { + if (mode > 4) { channels /= 2;
for (i = 0; i < channels; i++) { @@ -355,7 +214,7 @@ static int dice_hw_free(struct snd_pcm_substream *substream) struct snd_dice *dice = substream->private_data;
mutex_lock(&dice->mutex); - dice_stream_stop(dice); + snd_dice_stream_stop(dice); mutex_unlock(&dice->mutex);
return snd_pcm_lib_free_vmalloc_buffer(substream); @@ -369,9 +228,9 @@ static int dice_prepare(struct snd_pcm_substream *substream) mutex_lock(&dice->mutex);
if (amdtp_streaming_error(&dice->rx_stream)) - dice_stream_stop_packets(dice); + snd_dice_stream_stop_packets(dice);
- err = dice_stream_start(dice); + err = snd_dice_stream_start(dice); if (err < 0) { mutex_unlock(&dice->mutex); return err; @@ -966,31 +825,37 @@ end: return err; }
-static int highest_supported_mode_rate(struct snd_dice *dice, unsigned int mode) +static int highest_supported_mode_rate(struct snd_dice *dice, + unsigned int mode, unsigned int *rate) { - int i; + unsigned int i, m;
- for (i = ARRAY_SIZE(snd_dice_rates) - 1; i >= 0; --i) - if ((dice->clock_caps & (1 << i)) && - rate_index_to_mode(i) == mode) - return i; + for (i = ARRAY_SIZE(snd_dice_rates); i > 0; i--) { + *rate = snd_dice_rates[i - 1]; + if (snd_dice_stream_get_rate_mode(dice, *rate, &m) < 0) + continue; + if (mode == m) + break; + } + if (i == 0) + return -EINVAL;
- return -1; + return 0; }
static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode) { __be32 values[2]; - int rate_index, err; + unsigned int rate; + int err;
- rate_index = highest_supported_mode_rate(dice, mode); - if (rate_index < 0) { + if (highest_supported_mode_rate(dice, mode, &rate) < 0) { dice->rx_channels[mode] = 0; dice->rx_midi_ports[mode] = 0; return 0; }
- err = snd_dice_transaction_set_rate(dice, snd_dice_rates[rate_index]); + err = snd_dice_transaction_set_rate(dice, rate); if (err < 0) return err;
@@ -1113,10 +978,6 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
dice_card_strings(dice);
- err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1); - if (err < 0) - goto error; - err = dice_create_pcm(dice); if (err < 0) goto error; @@ -1127,22 +988,13 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
dice_create_proc(dice);
- err = fw_iso_resources_init(&dice->rx_resources, unit); + err = snd_dice_stream_init(dice); if (err < 0) goto error; - dice->rx_resources.channels_mask = 0x00000000ffffffffuLL; - - err = amdtp_stream_init(&dice->rx_stream, unit, AMDTP_OUT_STREAM, - CIP_BLOCKING); - if (err < 0) { - fw_iso_resources_destroy(&dice->rx_resources); - goto error; - }
err = snd_card_register(card); if (err < 0) { - amdtp_stream_destroy(&dice->rx_stream); - fw_iso_resources_destroy(&dice->rx_resources); + snd_dice_stream_destroy(dice); goto error; }
@@ -1158,13 +1010,11 @@ static void dice_remove(struct fw_unit *unit) { struct snd_dice *dice = dev_get_drvdata(&unit->device);
- amdtp_stream_pcm_abort(&dice->rx_stream); - snd_card_disconnect(dice->card);
mutex_lock(&dice->mutex);
- dice_stream_stop(dice); + snd_dice_stream_destroy(dice);
mutex_unlock(&dice->mutex);
@@ -1178,22 +1028,12 @@ static void dice_update(struct fw_unit *unit) /* The handler address register becomes initialized. */ snd_dice_transaction_reinit(dice);
- dice->global_enabled = false; - - /* - * On a bus reset, the DICE firmware disables streaming and then goes - * off contemplating its own navel for hundreds of milliseconds before - * it can react to any of our attempts to reenable streaming. This - * means that we lose synchronization anyway, so we force our streams - * to stop so that the application can restart them in an orderly - * manner. - */ - amdtp_stream_pcm_abort(&dice->rx_stream); - mutex_lock(&dice->mutex);
- dice_stream_stop_packets(dice); - fw_iso_resources_update(&dice->rx_resources); + /* The enable register becomes initialized, then streams are stopped. */ + dice->global_enabled = false; + + snd_dice_stream_stop_packets(dice);
mutex_unlock(&dice->mutex); } diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index c756e62..ca4090d 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -157,4 +157,18 @@ void snd_dice_transaction_destroy(struct snd_dice *dice); #define SND_DICE_RATES_COUNT 7 extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT];
+int snd_dice_stream_get_rate_mode(struct snd_dice *dice, + unsigned int rate, unsigned int *mode); + +int snd_dice_stream_start_packets(struct snd_dice *dice); +int snd_dice_stream_start(struct snd_dice *dice); +void snd_dice_stream_stop_packets(struct snd_dice *dice); +void snd_dice_stream_stop(struct snd_dice *dice); +int snd_dice_stream_init(struct snd_dice *dice); +void snd_dice_stream_destroy(struct snd_dice *dice); +void snd_dice_stream_update(struct snd_dice *dice); + +int snd_dice_stream_lock_try(struct snd_dice *dice); +void snd_dice_stream_lock_release(struct snd_dice *dice); + #endif
This commit adds a file and move some codes related to PCM functionality.
Currently PCM playback is supported. PCM capture will be supported in followed commits.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/dice/Makefile | 2 +- sound/firewire/dice/dice-pcm.c | 294 ++++++++++++++++++++++++++++++++++++++ sound/firewire/dice/dice-stream.c | 10 +- sound/firewire/dice/dice.c | 288 +------------------------------------ sound/firewire/dice/dice.h | 2 + 5 files changed, 303 insertions(+), 293 deletions(-) create mode 100644 sound/firewire/dice/dice-pcm.c
diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile index 867864c..1ddaeca 100644 --- a/sound/firewire/dice/Makefile +++ b/sound/firewire/dice/Makefile @@ -1,2 +1,2 @@ -snd-dice-objs := dice-transaction.o dice-stream.o dice.o +snd-dice-objs := dice-transaction.o dice-stream.o dice-pcm.o dice.o obj-m += snd-dice.o diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c new file mode 100644 index 0000000..beff320 --- /dev/null +++ b/sound/firewire/dice/dice-pcm.c @@ -0,0 +1,294 @@ +/* + * dice_pcm.c - a part of driver for DICE based devices + * + * Copyright (c) Clemens Ladisch clemens@ladisch.de + * Copyright (c) 2014 Takashi Sakamoto o-takashi@sakamocchi.jp + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "dice.h" + +static int dice_rate_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_dice *dice = rule->private; + + const struct snd_interval *c = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *r = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval rates = { + .min = UINT_MAX, .max = 0, .integer = 1 + }; + unsigned int i, rate, mode, *pcm_channels = dice->rx_channels; + + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { + rate = snd_dice_rates[i]; + if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) + continue; + + if (!snd_interval_test(c, pcm_channels[mode])) + continue; + + rates.min = min(rates.min, rate); + rates.max = max(rates.max, rate); + } + + return snd_interval_refine(r, &rates); +} + +static int dice_channels_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_dice *dice = rule->private; + + const struct snd_interval *r = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *c = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval channels = { + .min = UINT_MAX, .max = 0, .integer = 1 + }; + unsigned int i, rate, mode, *pcm_channels = dice->rx_channels; + + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { + rate = snd_dice_rates[i]; + if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) + continue; + + if (!snd_interval_test(r, rate)) + continue; + + channels.min = min(channels.min, pcm_channels[mode]); + channels.max = max(channels.max, pcm_channels[mode]); + } + + return snd_interval_refine(c, &channels); +} + + +static int pcm_open(struct snd_pcm_substream *substream) +{ + static const struct snd_pcm_hardware hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + .formats = AMDTP_OUT_PCM_FORMAT_BITS, + .channels_min = UINT_MAX, + .channels_max = 0, + .buffer_bytes_max = 16 * 1024 * 1024, + .period_bytes_min = 1, + .period_bytes_max = UINT_MAX, + .periods_min = 1, + .periods_max = UINT_MAX, + }; + struct snd_dice *dice = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int i; + int err; + + err = snd_dice_stream_lock_try(dice); + if (err < 0) + goto error; + + runtime->hw = hardware; + + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) + if (dice->clock_caps & (1 << i)) + runtime->hw.rates |= + snd_pcm_rate_to_rate_bit(snd_dice_rates[i]); + snd_pcm_limit_hw_rates(runtime); + + for (i = 0; i < 3; ++i) + if (dice->rx_channels[i]) { + runtime->hw.channels_min = min(runtime->hw.channels_min, + dice->rx_channels[i]); + runtime->hw.channels_max = max(runtime->hw.channels_max, + dice->rx_channels[i]); + } + + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + dice_rate_constraint, dice, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + goto err_lock; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + dice_channels_constraint, dice, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + goto err_lock; + + err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime); + if (err < 0) + goto err_lock; + + return 0; + +err_lock: + snd_dice_stream_lock_release(dice); +error: + return err; +} + +static int pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + snd_dice_stream_lock_release(dice); + + return 0; +} + +static int playback_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_dice *dice = substream->private_data; + unsigned int mode, rate, channels, i; + int err; + + mutex_lock(&dice->mutex); + snd_dice_stream_stop(dice); + mutex_unlock(&dice->mutex); + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; + + rate = params_rate(hw_params); + err = snd_dice_transaction_set_rate(dice, rate); + if (err < 0) + return err; + + if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) + return err; + + /* + * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in + * one data block of AMDTP packet. Thus sampling transfer frequency is + * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are + * transferred on AMDTP packets at 96 kHz. Two successive samples of a + * channel are stored consecutively in the packet. This quirk is called + * as 'Dual Wire'. + * For this quirk, blocking mode is required and PCM buffer size should + * be aligned to SYT_INTERVAL. + */ + channels = params_channels(hw_params); + if (mode > 1) { + if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) { + err = -ENOSYS; + return err; + } + + rate /= 2; + channels *= 2; + dice->rx_stream.double_pcm_frames = true; + } else { + dice->rx_stream.double_pcm_frames = false; + } + + amdtp_stream_set_parameters(&dice->rx_stream, rate, channels, + dice->rx_midi_ports[mode]); + if (mode > 1) { + channels /= 2; + + for (i = 0; i < channels; i++) { + dice->rx_stream.pcm_positions[i] = i * 2; + dice->rx_stream.pcm_positions[i + channels] = i * 2 + 1; + } + } + + amdtp_stream_set_pcm_format(&dice->rx_stream, + params_format(hw_params)); + + return 0; +} + +static int playback_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + mutex_lock(&dice->mutex); + snd_dice_stream_stop(dice); + mutex_unlock(&dice->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + int err; + + mutex_lock(&dice->mutex); + + if (amdtp_streaming_error(&dice->rx_stream)) + snd_dice_stream_stop_packets(dice); + + err = snd_dice_stream_start(dice); + if (err < 0) { + mutex_unlock(&dice->mutex); + return err; + } + + mutex_unlock(&dice->mutex); + + amdtp_stream_pcm_prepare(&dice->rx_stream); + + return 0; +} + +static int playback_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_dice *dice = substream->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + amdtp_stream_pcm_trigger(&dice->rx_stream, substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + amdtp_stream_pcm_trigger(&dice->rx_stream, NULL); + break; + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + return amdtp_stream_pcm_pointer(&dice->rx_stream); +} + +int snd_dice_create_pcm(struct snd_dice *dice) +{ + static struct snd_pcm_ops playback_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = playback_hw_params, + .hw_free = playback_hw_free, + .prepare = playback_prepare, + .trigger = playback_trigger, + .pointer = playback_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm); + if (err < 0) + return err; + pcm->private_data = dice; + strcpy(pcm->name, dice->card->shortname); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); + + return 0; +} diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index ba8fe8d..d5251a0 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -95,10 +95,11 @@ error:
void snd_dice_stream_stop_packets(struct snd_dice *dice) { - if (amdtp_stream_running(&dice->rx_stream)) { - snd_dice_transaction_clear_enable(dice); - amdtp_stream_stop(&dice->rx_stream); - } + if (!amdtp_stream_running(&dice->rx_stream)) + return; + + snd_dice_transaction_clear_enable(dice); + amdtp_stream_stop(&dice->rx_stream); }
void snd_dice_stream_stop(struct snd_dice *dice) @@ -184,7 +185,6 @@ int snd_dice_stream_lock_try(struct snd_dice *dice) if (dice->dev_lock_count++ == 0) dice_lock_changed(dice); err = 0; - out: spin_unlock_irq(&dice->lock);
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index 588a89e..4f0356c 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -11,292 +11,6 @@ MODULE_DESCRIPTION("DICE driver"); MODULE_AUTHOR("Clemens Ladisch clemens@ladisch.de"); MODULE_LICENSE("GPL v2");
-static int dice_rate_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_dice *dice = rule->private; - - const struct snd_interval *c = - hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval *r = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval rates = { - .min = UINT_MAX, .max = 0, .integer = 1 - }; - unsigned int i, rate, mode, *pcm_channels = dice->rx_channels; - - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { - rate = snd_dice_rates[i]; - if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) - continue; - - if (!snd_interval_test(c, pcm_channels[mode])) - continue; - - rates.min = min(rates.min, rate); - rates.max = max(rates.max, rate); - } - - return snd_interval_refine(r, &rates); -} - -static int dice_channels_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_dice *dice = rule->private; - - const struct snd_interval *r = - hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *c = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval channels = { - .min = UINT_MAX, .max = 0, .integer = 1 - }; - unsigned int i, rate, mode, *pcm_channels = dice->rx_channels; - - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { - rate = snd_dice_rates[i]; - if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) - continue; - - if (!snd_interval_test(r, rate)) - continue; - - channels.min = min(channels.min, pcm_channels[mode]); - channels.max = max(channels.max, pcm_channels[mode]); - } - - return snd_interval_refine(c, &channels); -} - -static int dice_open(struct snd_pcm_substream *substream) -{ - static const struct snd_pcm_hardware hardware = { - .info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER, - .formats = AMDTP_OUT_PCM_FORMAT_BITS, - .channels_min = UINT_MAX, - .channels_max = 0, - .buffer_bytes_max = 16 * 1024 * 1024, - .period_bytes_min = 1, - .period_bytes_max = UINT_MAX, - .periods_min = 1, - .periods_max = UINT_MAX, - }; - struct snd_dice *dice = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int i; - int err; - - err = snd_dice_stream_lock_try(dice); - if (err < 0) - goto error; - - runtime->hw = hardware; - - for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) - if (dice->clock_caps & (1 << i)) - runtime->hw.rates |= - snd_pcm_rate_to_rate_bit(snd_dice_rates[i]); - snd_pcm_limit_hw_rates(runtime); - - for (i = 0; i < 3; ++i) - if (dice->rx_channels[i]) { - runtime->hw.channels_min = min(runtime->hw.channels_min, - dice->rx_channels[i]); - runtime->hw.channels_max = max(runtime->hw.channels_max, - dice->rx_channels[i]); - } - - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - dice_rate_constraint, dice, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); - if (err < 0) - goto err_lock; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - dice_channels_constraint, dice, - SNDRV_PCM_HW_PARAM_RATE, -1); - if (err < 0) - goto err_lock; - - err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime); - if (err < 0) - goto err_lock; - - return 0; - -err_lock: - snd_dice_stream_lock_release(dice); -error: - return err; -} - -static int dice_close(struct snd_pcm_substream *substream) -{ - struct snd_dice *dice = substream->private_data; - - snd_dice_stream_lock_release(dice); - - return 0; -} - -static int dice_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_dice *dice = substream->private_data; - unsigned int mode, rate, channels, i; - int err; - - mutex_lock(&dice->mutex); - snd_dice_stream_stop(dice); - mutex_unlock(&dice->mutex); - - err = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - return err; - - rate = params_rate(hw_params); - err = snd_dice_transaction_set_rate(dice, rate); - if (err < 0) - return err; - - err = snd_dice_stream_get_rate_mode(dice, rate, &mode); - if (err < 0) - return err; - - /* - * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in - * one data block of AMDTP packet. Thus sampling transfer frequency is - * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are - * transferred on AMDTP packets at 96 kHz. Two successive samples of a - * channel are stored consecutively in the packet. This quirk is called - * as 'Dual Wire'. - * For this quirk, blocking mode is required and PCM buffer size should - * be aligned to SYT_INTERVAL. - */ - channels = params_channels(hw_params); - if (mode > 1) { - if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) { - err = -ENOSYS; - return err; - } - - rate /= 2; - channels *= 2; - dice->rx_stream.double_pcm_frames = true; - } else { - dice->rx_stream.double_pcm_frames = false; - } - - amdtp_stream_set_parameters(&dice->rx_stream, rate, channels, - dice->rx_midi_ports[mode]); - if (mode > 4) { - channels /= 2; - - for (i = 0; i < channels; i++) { - dice->rx_stream.pcm_positions[i] = i * 2; - dice->rx_stream.pcm_positions[i + channels] = i * 2 + 1; - } - } - - amdtp_stream_set_pcm_format(&dice->rx_stream, - params_format(hw_params)); - - return 0; -} - -static int dice_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_dice *dice = substream->private_data; - - mutex_lock(&dice->mutex); - snd_dice_stream_stop(dice); - mutex_unlock(&dice->mutex); - - return snd_pcm_lib_free_vmalloc_buffer(substream); -} - -static int dice_prepare(struct snd_pcm_substream *substream) -{ - struct snd_dice *dice = substream->private_data; - int err; - - mutex_lock(&dice->mutex); - - if (amdtp_streaming_error(&dice->rx_stream)) - snd_dice_stream_stop_packets(dice); - - err = snd_dice_stream_start(dice); - if (err < 0) { - mutex_unlock(&dice->mutex); - return err; - } - - mutex_unlock(&dice->mutex); - - amdtp_stream_pcm_prepare(&dice->rx_stream); - - return 0; -} - -static int dice_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct snd_dice *dice = substream->private_data; - struct snd_pcm_substream *pcm; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - pcm = substream; - break; - case SNDRV_PCM_TRIGGER_STOP: - pcm = NULL; - break; - default: - return -EINVAL; - } - amdtp_stream_pcm_trigger(&dice->rx_stream, pcm); - - return 0; -} - -static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream) -{ - struct snd_dice *dice = substream->private_data; - - return amdtp_stream_pcm_pointer(&dice->rx_stream); -} - -static int dice_create_pcm(struct snd_dice *dice) -{ - static struct snd_pcm_ops ops = { - .open = dice_open, - .close = dice_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = dice_hw_params, - .hw_free = dice_hw_free, - .prepare = dice_prepare, - .trigger = dice_trigger, - .pointer = dice_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, - }; - struct snd_pcm *pcm; - int err; - - err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm); - if (err < 0) - return err; - pcm->private_data = dice; - strcpy(pcm->name, dice->card->shortname); - pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->ops = &ops; - - return 0; -} - static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, loff_t *offset) { @@ -978,7 +692,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
dice_card_strings(dice);
- err = dice_create_pcm(dice); + err = snd_dice_create_pcm(dice); if (err < 0) goto error;
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index ca4090d..4d9e55b 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -171,4 +171,6 @@ void snd_dice_stream_update(struct snd_dice *dice); int snd_dice_stream_lock_try(struct snd_dice *dice); void snd_dice_stream_lock_release(struct snd_dice *dice);
+int snd_dice_create_pcm(struct snd_dice *dice); + #endif
This commit adds a file and move some codes related to hwdep functionality.
This interface is designed for mixer/control application. By using hwdep interface, the application can get information about firewire node, can lock/unlock kernel streaming and can get notification at starting/stopping kernel streaming.
Additionally, this interface give a way to read Dice notification.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/dice/Makefile | 3 +- sound/firewire/dice/dice-hwdep.c | 190 +++++++++++++++++++++++++++++++++++++++ sound/firewire/dice/dice.c | 182 +------------------------------------ sound/firewire/dice/dice.h | 2 + 4 files changed, 195 insertions(+), 182 deletions(-) create mode 100644 sound/firewire/dice/dice-hwdep.c
diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile index 1ddaeca..73b0e38 100644 --- a/sound/firewire/dice/Makefile +++ b/sound/firewire/dice/Makefile @@ -1,2 +1,3 @@ -snd-dice-objs := dice-transaction.o dice-stream.o dice-pcm.o dice.o +snd-dice-objs := dice-transaction.o dice-stream.o dice-pcm.o dice-hwdep.o \ + dice.o obj-m += snd-dice.o diff --git a/sound/firewire/dice/dice-hwdep.c b/sound/firewire/dice/dice-hwdep.c new file mode 100644 index 0000000..a4dc02a --- /dev/null +++ b/sound/firewire/dice/dice-hwdep.c @@ -0,0 +1,190 @@ +/* + * dice_hwdep.c - a part of driver for DICE based devices + * + * Copyright (c) Clemens Ladisch clemens@ladisch.de + * Copyright (c) 2014 Takashi Sakamoto o-takashi@sakamocchi.jp + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "dice.h" + +static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, + long count, loff_t *offset) +{ + struct snd_dice *dice = hwdep->private_data; + DEFINE_WAIT(wait); + union snd_firewire_event event; + + spin_lock_irq(&dice->lock); + + while (!dice->dev_lock_changed && dice->notification_bits == 0) { + prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE); + spin_unlock_irq(&dice->lock); + schedule(); + finish_wait(&dice->hwdep_wait, &wait); + if (signal_pending(current)) + return -ERESTARTSYS; + spin_lock_irq(&dice->lock); + } + + memset(&event, 0, sizeof(event)); + if (dice->dev_lock_changed) { + event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; + event.lock_status.status = dice->dev_lock_count > 0; + dice->dev_lock_changed = false; + + count = min_t(long, count, sizeof(event.lock_status)); + } else { + event.dice_notification.type = + SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION; + event.dice_notification.notification = dice->notification_bits; + dice->notification_bits = 0; + + count = min_t(long, count, sizeof(event.dice_notification)); + } + + spin_unlock_irq(&dice->lock); + + if (copy_to_user(buf, &event, count)) + return -EFAULT; + + return count; +} + +static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file, + poll_table *wait) +{ + struct snd_dice *dice = hwdep->private_data; + unsigned int events; + + poll_wait(file, &dice->hwdep_wait, wait); + + spin_lock_irq(&dice->lock); + if (dice->dev_lock_changed || dice->notification_bits != 0) + events = POLLIN | POLLRDNORM; + else + events = 0; + spin_unlock_irq(&dice->lock); + + return events; +} + +static int hwdep_get_info(struct snd_dice *dice, void __user *arg) +{ + struct fw_device *dev = fw_parent_device(dice->unit); + struct snd_firewire_get_info info; + + memset(&info, 0, sizeof(info)); + info.type = SNDRV_FIREWIRE_TYPE_DICE; + info.card = dev->card->index; + *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); + *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); + strlcpy(info.device_name, dev_name(&dev->device), + sizeof(info.device_name)); + + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +static int hwdep_lock(struct snd_dice *dice) +{ + int err; + + spin_lock_irq(&dice->lock); + + if (dice->dev_lock_count == 0) { + dice->dev_lock_count = -1; + err = 0; + } else { + err = -EBUSY; + } + + spin_unlock_irq(&dice->lock); + + return err; +} + +static int hwdep_unlock(struct snd_dice *dice) +{ + int err; + + spin_lock_irq(&dice->lock); + + if (dice->dev_lock_count == -1) { + dice->dev_lock_count = 0; + err = 0; + } else { + err = -EBADFD; + } + + spin_unlock_irq(&dice->lock); + + return err; +} + +static int hwdep_release(struct snd_hwdep *hwdep, struct file *file) +{ + struct snd_dice *dice = hwdep->private_data; + + spin_lock_irq(&dice->lock); + if (dice->dev_lock_count == -1) + dice->dev_lock_count = 0; + spin_unlock_irq(&dice->lock); + + return 0; +} + +static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct snd_dice *dice = hwdep->private_data; + + switch (cmd) { + case SNDRV_FIREWIRE_IOCTL_GET_INFO: + return hwdep_get_info(dice, (void __user *)arg); + case SNDRV_FIREWIRE_IOCTL_LOCK: + return hwdep_lock(dice); + case SNDRV_FIREWIRE_IOCTL_UNLOCK: + return hwdep_unlock(dice); + default: + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMPAT +static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return hwdep_ioctl(hwdep, file, cmd, + (unsigned long)compat_ptr(arg)); +} +#else +#define hwdep_compat_ioctl NULL +#endif + +int snd_dice_create_hwdep(struct snd_dice *dice) +{ + static const struct snd_hwdep_ops ops = { + .read = hwdep_read, + .release = hwdep_release, + .poll = hwdep_poll, + .ioctl = hwdep_ioctl, + .ioctl_compat = hwdep_compat_ioctl, + }; + struct snd_hwdep *hwdep; + int err; + + err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep); + if (err < 0) + return err; + strcpy(hwdep->name, "DICE"); + hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE; + hwdep->ops = ops; + hwdep->private_data = dice; + hwdep->exclusive = true; + + return 0; +} diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index 4f0356c..e7d665a 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -11,186 +11,6 @@ MODULE_DESCRIPTION("DICE driver"); MODULE_AUTHOR("Clemens Ladisch clemens@ladisch.de"); MODULE_LICENSE("GPL v2");
-static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, - long count, loff_t *offset) -{ - struct snd_dice *dice = hwdep->private_data; - DEFINE_WAIT(wait); - union snd_firewire_event event; - - spin_lock_irq(&dice->lock); - - while (!dice->dev_lock_changed && dice->notification_bits == 0) { - prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE); - spin_unlock_irq(&dice->lock); - schedule(); - finish_wait(&dice->hwdep_wait, &wait); - if (signal_pending(current)) - return -ERESTARTSYS; - spin_lock_irq(&dice->lock); - } - - memset(&event, 0, sizeof(event)); - if (dice->dev_lock_changed) { - event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; - event.lock_status.status = dice->dev_lock_count > 0; - dice->dev_lock_changed = false; - - count = min_t(long, count, sizeof(event.lock_status)); - } else { - event.dice_notification.type = - SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION; - event.dice_notification.notification = dice->notification_bits; - dice->notification_bits = 0; - - count = min_t(long, count, sizeof(event.dice_notification)); - } - - spin_unlock_irq(&dice->lock); - - if (copy_to_user(buf, &event, count)) - return -EFAULT; - - return count; -} - -static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, - poll_table *wait) -{ - struct snd_dice *dice = hwdep->private_data; - unsigned int events; - - poll_wait(file, &dice->hwdep_wait, wait); - - spin_lock_irq(&dice->lock); - if (dice->dev_lock_changed || dice->notification_bits != 0) - events = POLLIN | POLLRDNORM; - else - events = 0; - spin_unlock_irq(&dice->lock); - - return events; -} - -static int dice_hwdep_get_info(struct snd_dice *dice, void __user *arg) -{ - struct fw_device *dev = fw_parent_device(dice->unit); - struct snd_firewire_get_info info; - - memset(&info, 0, sizeof(info)); - info.type = SNDRV_FIREWIRE_TYPE_DICE; - info.card = dev->card->index; - *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); - *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); - strlcpy(info.device_name, dev_name(&dev->device), - sizeof(info.device_name)); - - if (copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; - - return 0; -} - -static int dice_hwdep_lock(struct snd_dice *dice) -{ - int err; - - spin_lock_irq(&dice->lock); - - if (dice->dev_lock_count == 0) { - dice->dev_lock_count = -1; - err = 0; - } else { - err = -EBUSY; - } - - spin_unlock_irq(&dice->lock); - - return err; -} - -static int dice_hwdep_unlock(struct snd_dice *dice) -{ - int err; - - spin_lock_irq(&dice->lock); - - if (dice->dev_lock_count == -1) { - dice->dev_lock_count = 0; - err = 0; - } else { - err = -EBADFD; - } - - spin_unlock_irq(&dice->lock); - - return err; -} - -static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file) -{ - struct snd_dice *dice = hwdep->private_data; - - spin_lock_irq(&dice->lock); - if (dice->dev_lock_count == -1) - dice->dev_lock_count = 0; - spin_unlock_irq(&dice->lock); - - return 0; -} - -static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct snd_dice *dice = hwdep->private_data; - - switch (cmd) { - case SNDRV_FIREWIRE_IOCTL_GET_INFO: - return dice_hwdep_get_info(dice, (void __user *)arg); - case SNDRV_FIREWIRE_IOCTL_LOCK: - return dice_hwdep_lock(dice); - case SNDRV_FIREWIRE_IOCTL_UNLOCK: - return dice_hwdep_unlock(dice); - default: - return -ENOIOCTLCMD; - } -} - -#ifdef CONFIG_COMPAT -static int dice_hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return dice_hwdep_ioctl(hwdep, file, cmd, - (unsigned long)compat_ptr(arg)); -} -#else -#define dice_hwdep_compat_ioctl NULL -#endif - -static int dice_create_hwdep(struct snd_dice *dice) -{ - static const struct snd_hwdep_ops ops = { - .read = dice_hwdep_read, - .release = dice_hwdep_release, - .poll = dice_hwdep_poll, - .ioctl = dice_hwdep_ioctl, - .ioctl_compat = dice_hwdep_compat_ioctl, - }; - struct snd_hwdep *hwdep; - int err; - - err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep); - if (err < 0) - return err; - strcpy(hwdep->name, "DICE"); - hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE; - hwdep->ops = ops; - hwdep->private_data = dice; - hwdep->exclusive = true; - - return 0; -} - static int dice_proc_read_mem(struct snd_dice *dice, void *buffer, unsigned int offset_q, unsigned int quadlets) { @@ -696,7 +516,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) if (err < 0) goto error;
- err = dice_create_hwdep(dice); + err = snd_dice_create_hwdep(dice); if (err < 0) goto error;
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index 4d9e55b..dcc8c78 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -173,4 +173,6 @@ void snd_dice_stream_lock_release(struct snd_dice *dice);
int snd_dice_create_pcm(struct snd_dice *dice);
+int snd_dice_create_hwdep(struct snd_dice *dice); + #endif
This commit adds a file and move some codes related to proc functionality.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/dice/Makefile | 4 +- sound/firewire/dice/dice-proc.c | 252 ++++++++++++++++++++++++++++++++++++++++ sound/firewire/dice/dice.c | 244 +------------------------------------- sound/firewire/dice/dice.h | 2 + 4 files changed, 257 insertions(+), 245 deletions(-) create mode 100644 sound/firewire/dice/dice-proc.c
diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile index 73b0e38..9a48289 100644 --- a/sound/firewire/dice/Makefile +++ b/sound/firewire/dice/Makefile @@ -1,3 +1,3 @@ -snd-dice-objs := dice-transaction.o dice-stream.o dice-pcm.o dice-hwdep.o \ - dice.o +snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-pcm.o \ + dice-hwdep.o dice.o obj-m += snd-dice.o diff --git a/sound/firewire/dice/dice-proc.c b/sound/firewire/dice/dice-proc.c new file mode 100644 index 0000000..f5c1d1b --- /dev/null +++ b/sound/firewire/dice/dice-proc.c @@ -0,0 +1,252 @@ +/* + * dice_proc.c - a part of driver for Dice based devices + * + * Copyright (c) Clemens Ladisch + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "dice.h" + +static int dice_proc_read_mem(struct snd_dice *dice, void *buffer, + unsigned int offset_q, unsigned int quadlets) +{ + unsigned int i; + int err; + + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE + 4 * offset_q, + buffer, 4 * quadlets, 0); + if (err < 0) + return err; + + for (i = 0; i < quadlets; ++i) + be32_to_cpus(&((u32 *)buffer)[i]); + + return 0; +} + +static const char *str_from_array(const char *const strs[], unsigned int count, + unsigned int i) +{ + if (i < count) + return strs[i]; + + return "(unknown)"; +} + +static void dice_proc_fixup_string(char *s, unsigned int size) +{ + unsigned int i; + + for (i = 0; i < size; i += 4) + cpu_to_le32s((u32 *)(s + i)); + + for (i = 0; i < size - 2; ++i) { + if (s[i] == '\0') + return; + if (s[i] == '\' && s[i + 1] == '\') { + s[i + 2] = '\0'; + return; + } + } + s[size - 1] = '\0'; +} + +static void dice_proc_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + static const char *const section_names[5] = { + "global", "tx", "rx", "ext_sync", "unused2" + }; + static const char *const clock_sources[] = { + "aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif", + "wc", "arx1", "arx2", "arx3", "arx4", "internal" + }; + static const char *const rates[] = { + "32000", "44100", "48000", "88200", "96000", "176400", "192000", + "any low", "any mid", "any high", "none" + }; + struct snd_dice *dice = entry->private_data; + u32 sections[ARRAY_SIZE(section_names) * 2]; + struct { + u32 number; + u32 size; + } tx_rx_header; + union { + struct { + u32 owner_hi, owner_lo; + u32 notification; + char nick_name[NICK_NAME_SIZE]; + u32 clock_select; + u32 enable; + u32 status; + u32 extended_status; + u32 sample_rate; + u32 version; + u32 clock_caps; + char clock_source_names[CLOCK_SOURCE_NAMES_SIZE]; + } global; + struct { + u32 iso; + u32 number_audio; + u32 number_midi; + u32 speed; + char names[TX_NAMES_SIZE]; + u32 ac3_caps; + u32 ac3_enable; + } tx; + struct { + u32 iso; + u32 seq_start; + u32 number_audio; + u32 number_midi; + char names[RX_NAMES_SIZE]; + u32 ac3_caps; + u32 ac3_enable; + } rx; + struct { + u32 clock_source; + u32 locked; + u32 rate; + u32 adat_user_data; + } ext_sync; + } buf; + unsigned int quadlets, stream, i; + + if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0) + return; + snd_iprintf(buffer, "sections:\n"); + for (i = 0; i < ARRAY_SIZE(section_names); ++i) + snd_iprintf(buffer, " %s: offset %u, size %u\n", + section_names[i], + sections[i * 2], sections[i * 2 + 1]); + + quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4); + if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0) + return; + snd_iprintf(buffer, "global:\n"); + snd_iprintf(buffer, " owner: %04x:%04x%08x\n", + buf.global.owner_hi >> 16, + buf.global.owner_hi & 0xffff, buf.global.owner_lo); + snd_iprintf(buffer, " notification: %08x\n", buf.global.notification); + dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE); + snd_iprintf(buffer, " nick name: %s\n", buf.global.nick_name); + snd_iprintf(buffer, " clock select: %s %s\n", + str_from_array(clock_sources, ARRAY_SIZE(clock_sources), + buf.global.clock_select & CLOCK_SOURCE_MASK), + str_from_array(rates, ARRAY_SIZE(rates), + (buf.global.clock_select & CLOCK_RATE_MASK) + >> CLOCK_RATE_SHIFT)); + snd_iprintf(buffer, " enable: %u\n", buf.global.enable); + snd_iprintf(buffer, " status: %slocked %s\n", + buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un", + str_from_array(rates, ARRAY_SIZE(rates), + (buf.global.status & + STATUS_NOMINAL_RATE_MASK) + >> CLOCK_RATE_SHIFT)); + snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status); + snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate); + snd_iprintf(buffer, " version: %u.%u.%u.%u\n", + (buf.global.version >> 24) & 0xff, + (buf.global.version >> 16) & 0xff, + (buf.global.version >> 8) & 0xff, + (buf.global.version >> 0) & 0xff); + if (quadlets >= 90) { + snd_iprintf(buffer, " clock caps:"); + for (i = 0; i <= 6; ++i) + if (buf.global.clock_caps & (1 << i)) + snd_iprintf(buffer, " %s", rates[i]); + for (i = 0; i <= 12; ++i) + if (buf.global.clock_caps & (1 << (16 + i))) + snd_iprintf(buffer, " %s", clock_sources[i]); + snd_iprintf(buffer, "\n"); + dice_proc_fixup_string(buf.global.clock_source_names, + CLOCK_SOURCE_NAMES_SIZE); + snd_iprintf(buffer, " clock source names: %s\n", + buf.global.clock_source_names); + } + + if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0) + return; + quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx) / 4); + for (stream = 0; stream < tx_rx_header.number; ++stream) { + if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 + + stream * tx_rx_header.size, + quadlets) < 0) + break; + snd_iprintf(buffer, "tx %u:\n", stream); + snd_iprintf(buffer, " iso channel: %d\n", (int)buf.tx.iso); + snd_iprintf(buffer, " audio channels: %u\n", + buf.tx.number_audio); + snd_iprintf(buffer, " midi ports: %u\n", buf.tx.number_midi); + snd_iprintf(buffer, " speed: S%u\n", 100u << buf.tx.speed); + if (quadlets >= 68) { + dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE); + snd_iprintf(buffer, " names: %s\n", buf.tx.names); + } + if (quadlets >= 70) { + snd_iprintf(buffer, " ac3 caps: %08x\n", + buf.tx.ac3_caps); + snd_iprintf(buffer, " ac3 enable: %08x\n", + buf.tx.ac3_enable); + } + } + + if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0) + return; + quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx) / 4); + for (stream = 0; stream < tx_rx_header.number; ++stream) { + if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 + + stream * tx_rx_header.size, + quadlets) < 0) + break; + snd_iprintf(buffer, "rx %u:\n", stream); + snd_iprintf(buffer, " iso channel: %d\n", (int)buf.rx.iso); + snd_iprintf(buffer, " sequence start: %u\n", buf.rx.seq_start); + snd_iprintf(buffer, " audio channels: %u\n", + buf.rx.number_audio); + snd_iprintf(buffer, " midi ports: %u\n", buf.rx.number_midi); + if (quadlets >= 68) { + dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE); + snd_iprintf(buffer, " names: %s\n", buf.rx.names); + } + if (quadlets >= 70) { + snd_iprintf(buffer, " ac3 caps: %08x\n", + buf.rx.ac3_caps); + snd_iprintf(buffer, " ac3 enable: %08x\n", + buf.rx.ac3_enable); + } + } + + quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4); + if (quadlets >= 4) { + if (dice_proc_read_mem(dice, &buf.ext_sync, + sections[6], 4) < 0) + return; + snd_iprintf(buffer, "ext status:\n"); + snd_iprintf(buffer, " clock source: %s\n", + str_from_array(clock_sources, + ARRAY_SIZE(clock_sources), + buf.ext_sync.clock_source)); + snd_iprintf(buffer, " locked: %u\n", buf.ext_sync.locked); + snd_iprintf(buffer, " rate: %s\n", + str_from_array(rates, ARRAY_SIZE(rates), + buf.ext_sync.rate)); + snd_iprintf(buffer, " adat user data: "); + if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA) + snd_iprintf(buffer, "-\n"); + else + snd_iprintf(buffer, "%x\n", + buf.ext_sync.adat_user_data); + } +} + +void snd_dice_create_proc(struct snd_dice *dice) +{ + struct snd_info_entry *entry; + + if (!snd_card_proc_new(dice->card, "dice", &entry)) + snd_info_set_text_ops(entry, dice, dice_proc_read); +} diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index e7d665a..7e8ecd3 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -11,248 +11,6 @@ MODULE_DESCRIPTION("DICE driver"); MODULE_AUTHOR("Clemens Ladisch clemens@ladisch.de"); MODULE_LICENSE("GPL v2");
-static int dice_proc_read_mem(struct snd_dice *dice, void *buffer, - unsigned int offset_q, unsigned int quadlets) -{ - unsigned int i; - int err; - - err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, - DICE_PRIVATE_SPACE + 4 * offset_q, - buffer, 4 * quadlets, 0); - if (err < 0) - return err; - - for (i = 0; i < quadlets; ++i) - be32_to_cpus(&((u32 *)buffer)[i]); - - return 0; -} - -static const char *str_from_array(const char *const strs[], unsigned int count, - unsigned int i) -{ - if (i < count) - return strs[i]; - - return "(unknown)"; -} - -static void dice_proc_fixup_string(char *s, unsigned int size) -{ - unsigned int i; - - for (i = 0; i < size; i += 4) - cpu_to_le32s((u32 *)(s + i)); - - for (i = 0; i < size - 2; ++i) { - if (s[i] == '\0') - return; - if (s[i] == '\' && s[i + 1] == '\') { - s[i + 2] = '\0'; - return; - } - } - s[size - 1] = '\0'; -} - -static void dice_proc_read(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) -{ - static const char *const section_names[5] = { - "global", "tx", "rx", "ext_sync", "unused2" - }; - static const char *const clock_sources[] = { - "aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif", - "wc", "arx1", "arx2", "arx3", "arx4", "internal" - }; - static const char *const rates[] = { - "32000", "44100", "48000", "88200", "96000", "176400", "192000", - "any low", "any mid", "any high", "none" - }; - struct snd_dice *dice = entry->private_data; - u32 sections[ARRAY_SIZE(section_names) * 2]; - struct { - u32 number; - u32 size; - } tx_rx_header; - union { - struct { - u32 owner_hi, owner_lo; - u32 notification; - char nick_name[NICK_NAME_SIZE]; - u32 clock_select; - u32 enable; - u32 status; - u32 extended_status; - u32 sample_rate; - u32 version; - u32 clock_caps; - char clock_source_names[CLOCK_SOURCE_NAMES_SIZE]; - } global; - struct { - u32 iso; - u32 number_audio; - u32 number_midi; - u32 speed; - char names[TX_NAMES_SIZE]; - u32 ac3_caps; - u32 ac3_enable; - } tx; - struct { - u32 iso; - u32 seq_start; - u32 number_audio; - u32 number_midi; - char names[RX_NAMES_SIZE]; - u32 ac3_caps; - u32 ac3_enable; - } rx; - struct { - u32 clock_source; - u32 locked; - u32 rate; - u32 adat_user_data; - } ext_sync; - } buf; - unsigned int quadlets, stream, i; - - if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0) - return; - snd_iprintf(buffer, "sections:\n"); - for (i = 0; i < ARRAY_SIZE(section_names); ++i) - snd_iprintf(buffer, " %s: offset %u, size %u\n", - section_names[i], - sections[i * 2], sections[i * 2 + 1]); - - quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4); - if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0) - return; - snd_iprintf(buffer, "global:\n"); - snd_iprintf(buffer, " owner: %04x:%04x%08x\n", - buf.global.owner_hi >> 16, - buf.global.owner_hi & 0xffff, buf.global.owner_lo); - snd_iprintf(buffer, " notification: %08x\n", buf.global.notification); - dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE); - snd_iprintf(buffer, " nick name: %s\n", buf.global.nick_name); - snd_iprintf(buffer, " clock select: %s %s\n", - str_from_array(clock_sources, ARRAY_SIZE(clock_sources), - buf.global.clock_select & CLOCK_SOURCE_MASK), - str_from_array(rates, ARRAY_SIZE(rates), - (buf.global.clock_select & CLOCK_RATE_MASK) - >> CLOCK_RATE_SHIFT)); - snd_iprintf(buffer, " enable: %u\n", buf.global.enable); - snd_iprintf(buffer, " status: %slocked %s\n", - buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un", - str_from_array(rates, ARRAY_SIZE(rates), - (buf.global.status & - STATUS_NOMINAL_RATE_MASK) - >> CLOCK_RATE_SHIFT)); - snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status); - snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate); - snd_iprintf(buffer, " version: %u.%u.%u.%u\n", - (buf.global.version >> 24) & 0xff, - (buf.global.version >> 16) & 0xff, - (buf.global.version >> 8) & 0xff, - (buf.global.version >> 0) & 0xff); - if (quadlets >= 90) { - snd_iprintf(buffer, " clock caps:"); - for (i = 0; i <= 6; ++i) - if (buf.global.clock_caps & (1 << i)) - snd_iprintf(buffer, " %s", rates[i]); - for (i = 0; i <= 12; ++i) - if (buf.global.clock_caps & (1 << (16 + i))) - snd_iprintf(buffer, " %s", clock_sources[i]); - snd_iprintf(buffer, "\n"); - dice_proc_fixup_string(buf.global.clock_source_names, - CLOCK_SOURCE_NAMES_SIZE); - snd_iprintf(buffer, " clock source names: %s\n", - buf.global.clock_source_names); - } - - if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0) - return; - quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx) / 4); - for (stream = 0; stream < tx_rx_header.number; ++stream) { - if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 + - stream * tx_rx_header.size, - quadlets) < 0) - break; - snd_iprintf(buffer, "tx %u:\n", stream); - snd_iprintf(buffer, " iso channel: %d\n", (int)buf.tx.iso); - snd_iprintf(buffer, " audio channels: %u\n", - buf.tx.number_audio); - snd_iprintf(buffer, " midi ports: %u\n", buf.tx.number_midi); - snd_iprintf(buffer, " speed: S%u\n", 100u << buf.tx.speed); - if (quadlets >= 68) { - dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE); - snd_iprintf(buffer, " names: %s\n", buf.tx.names); - } - if (quadlets >= 70) { - snd_iprintf(buffer, " ac3 caps: %08x\n", - buf.tx.ac3_caps); - snd_iprintf(buffer, " ac3 enable: %08x\n", - buf.tx.ac3_enable); - } - } - - if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0) - return; - quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx) / 4); - for (stream = 0; stream < tx_rx_header.number; ++stream) { - if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 + - stream * tx_rx_header.size, - quadlets) < 0) - break; - snd_iprintf(buffer, "rx %u:\n", stream); - snd_iprintf(buffer, " iso channel: %d\n", (int)buf.rx.iso); - snd_iprintf(buffer, " sequence start: %u\n", buf.rx.seq_start); - snd_iprintf(buffer, " audio channels: %u\n", - buf.rx.number_audio); - snd_iprintf(buffer, " midi ports: %u\n", buf.rx.number_midi); - if (quadlets >= 68) { - dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE); - snd_iprintf(buffer, " names: %s\n", buf.rx.names); - } - if (quadlets >= 70) { - snd_iprintf(buffer, " ac3 caps: %08x\n", - buf.rx.ac3_caps); - snd_iprintf(buffer, " ac3 enable: %08x\n", - buf.rx.ac3_enable); - } - } - - quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4); - if (quadlets >= 4) { - if (dice_proc_read_mem(dice, &buf.ext_sync, - sections[6], 4) < 0) - return; - snd_iprintf(buffer, "ext status:\n"); - snd_iprintf(buffer, " clock source: %s\n", - str_from_array(clock_sources, - ARRAY_SIZE(clock_sources), - buf.ext_sync.clock_source)); - snd_iprintf(buffer, " locked: %u\n", buf.ext_sync.locked); - snd_iprintf(buffer, " rate: %s\n", - str_from_array(rates, ARRAY_SIZE(rates), - buf.ext_sync.rate)); - snd_iprintf(buffer, " adat user data: "); - if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA) - snd_iprintf(buffer, "-\n"); - else - snd_iprintf(buffer, "%x\n", - buf.ext_sync.adat_user_data); - } -} - -static void dice_create_proc(struct snd_dice *dice) -{ - struct snd_info_entry *entry; - - if (!snd_card_proc_new(dice->card, "dice", &entry)) - snd_info_set_text_ops(entry, dice, dice_proc_read); -} - #define OUI_WEISS 0x001c6a
#define DICE_CATEGORY_ID 0x04 @@ -520,7 +278,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) if (err < 0) goto error;
- dice_create_proc(dice); + snd_dice_create_proc(dice);
err = snd_dice_stream_init(dice); if (err < 0) diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index dcc8c78..969189a 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -175,4 +175,6 @@ int snd_dice_create_pcm(struct snd_dice *dice);
int snd_dice_create_hwdep(struct snd_dice *dice);
+void snd_dice_create_proc(struct snd_dice *dice); + #endif
This commit adds a new functions and some arrangement for PCM restriction. This arrangement is due to the number of channels which each Dice device has.
I note that minimum number for period becomes 2, instead of 1 because its PCM functionality has SNDRV_PCM_INFO_BATCH, this means that the driver uses double (or more) buffering so the minimum number for period should be 2.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/dice/dice-pcm.c | 109 +++++++++++++++++++++++++---------------- 1 file changed, 66 insertions(+), 43 deletions(-)
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index beff320..2e531bd 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -67,69 +67,92 @@ static int dice_channels_constraint(struct snd_pcm_hw_params *params, return snd_interval_refine(c, &channels); }
- -static int pcm_open(struct snd_pcm_substream *substream) +static void limit_channels_and_rates(struct snd_dice *dice, + struct snd_pcm_runtime *runtime, + unsigned int *pcm_channels) { - static const struct snd_pcm_hardware hardware = { - .info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER, - .formats = AMDTP_OUT_PCM_FORMAT_BITS, - .channels_min = UINT_MAX, - .channels_max = 0, - .buffer_bytes_max = 16 * 1024 * 1024, - .period_bytes_min = 1, - .period_bytes_max = UINT_MAX, - .periods_min = 1, - .periods_max = UINT_MAX, - }; - struct snd_dice *dice = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int i; - int err; + struct snd_pcm_hardware *hw = &runtime->hw; + unsigned int i, rate, mode;
- err = snd_dice_stream_lock_try(dice); - if (err < 0) - goto error; + hw->channels_min = UINT_MAX; + hw->channels_max = 0; + + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { + rate = snd_dice_rates[i]; + if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) + continue; + hw->rates |= snd_pcm_rate_to_rate_bit(rate);
- runtime->hw = hardware; + if (pcm_channels[mode] == 0) + continue; + hw->channels_min = min(hw->channels_min, pcm_channels[mode]); + hw->channels_max = max(hw->channels_max, pcm_channels[mode]); + }
- for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) - if (dice->clock_caps & (1 << i)) - runtime->hw.rates |= - snd_pcm_rate_to_rate_bit(snd_dice_rates[i]); snd_pcm_limit_hw_rates(runtime); +}
- for (i = 0; i < 3; ++i) - if (dice->rx_channels[i]) { - runtime->hw.channels_min = min(runtime->hw.channels_min, - dice->rx_channels[i]); - runtime->hw.channels_max = max(runtime->hw.channels_max, - dice->rx_channels[i]); - } +static void limit_period_and_buffer(struct snd_pcm_hardware *hw) +{ + hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */ + hw->periods_max = UINT_MAX; + + hw->period_bytes_min = 4 * hw->channels_max; /* byte for a frame */ + + /* Just to prevent from allocating much pages. */ + hw->period_bytes_max = hw->period_bytes_min * 2048; + hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; +} + +static int init_hw_info(struct snd_dice *dice, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_hardware *hw = &runtime->hw; + int err; + + hw->info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER; + hw->formats = AMDTP_OUT_PCM_FORMAT_BITS; + + limit_channels_and_rates(dice, runtime, dice->rx_channels); + limit_period_and_buffer(hw);
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, dice_rate_constraint, dice, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) - goto err_lock; + goto end; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, dice_channels_constraint, dice, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) - goto err_lock; + goto end;
err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime); - if (err < 0) - goto err_lock; +end: + return err; +}
- return 0; +static int pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + int err;
-err_lock: + err = snd_dice_stream_lock_try(dice); + if (err < 0) + goto end; + + err = init_hw_info(dice, substream); + if (err < 0) + goto err_locked; +end: + return err; +err_locked: snd_dice_stream_lock_release(dice); -error: return err; }
Streaming functionality can start streams when rate is given but currently some codes are in PCM functionality.
This commit changes the way to start stream and add some arrangement to make it easy to understand the way.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/dice/dice-pcm.c | 79 ++------------- sound/firewire/dice/dice-stream.c | 207 ++++++++++++++++++++++++++++---------- sound/firewire/dice/dice.c | 13 +-- sound/firewire/dice/dice.h | 4 +- 4 files changed, 161 insertions(+), 142 deletions(-)
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 2e531bd..904cb80 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -169,74 +169,18 @@ static int playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; - unsigned int mode, rate, channels, i; - int err; - - mutex_lock(&dice->mutex); - snd_dice_stream_stop(dice); - mutex_unlock(&dice->mutex); - - err = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - return err; - - rate = params_rate(hw_params); - err = snd_dice_transaction_set_rate(dice, rate); - if (err < 0) - return err; - - if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) - return err; - - /* - * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in - * one data block of AMDTP packet. Thus sampling transfer frequency is - * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are - * transferred on AMDTP packets at 96 kHz. Two successive samples of a - * channel are stored consecutively in the packet. This quirk is called - * as 'Dual Wire'. - * For this quirk, blocking mode is required and PCM buffer size should - * be aligned to SYT_INTERVAL. - */ - channels = params_channels(hw_params); - if (mode > 1) { - if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) { - err = -ENOSYS; - return err; - } - - rate /= 2; - channels *= 2; - dice->rx_stream.double_pcm_frames = true; - } else { - dice->rx_stream.double_pcm_frames = false; - } - - amdtp_stream_set_parameters(&dice->rx_stream, rate, channels, - dice->rx_midi_ports[mode]); - if (mode > 1) { - channels /= 2; - - for (i = 0; i < channels; i++) { - dice->rx_stream.pcm_positions[i] = i * 2; - dice->rx_stream.pcm_positions[i + channels] = i * 2 + 1; - } - } - amdtp_stream_set_pcm_format(&dice->rx_stream, params_format(hw_params));
- return 0; + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); }
static int playback_hw_free(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data;
- mutex_lock(&dice->mutex); snd_dice_stream_stop(dice); - mutex_unlock(&dice->mutex);
return snd_pcm_lib_free_vmalloc_buffer(substream); } @@ -246,22 +190,11 @@ static int playback_prepare(struct snd_pcm_substream *substream) struct snd_dice *dice = substream->private_data; int err;
- mutex_lock(&dice->mutex); - - if (amdtp_streaming_error(&dice->rx_stream)) - snd_dice_stream_stop_packets(dice); + err = snd_dice_stream_start(dice, substream->runtime->rate); + if (err >= 0) + amdtp_stream_pcm_prepare(&dice->rx_stream);
- err = snd_dice_stream_start(dice); - if (err < 0) { - mutex_unlock(&dice->mutex); - return err; - } - - mutex_unlock(&dice->mutex); - - amdtp_stream_pcm_prepare(&dice->rx_stream); - - return 0; + return err; }
static int playback_trigger(struct snd_pcm_substream *substream, int cmd) diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index d5251a0..2382ffc 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -9,6 +9,8 @@
#include "dice.h"
+#define CALLBACK_TIMEOUT 200 + const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { /* mode 0 */ [0] = 32000, @@ -38,83 +40,169 @@ int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, return -EINVAL; }
-int snd_dice_stream_start_packets(struct snd_dice *dice) +static void release_resources(struct snd_dice *dice) { + unsigned int channel; + + /* Reset channel number */ + channel = cpu_to_be32((u32)-1); + snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4); + + fw_iso_resources_free(&dice->rx_resources); +} + +static int keep_resources(struct snd_dice *dice, unsigned int max_payload_bytes) +{ + unsigned int channel; int err;
- if (amdtp_stream_running(&dice->rx_stream)) - return 0; + err = fw_iso_resources_allocate(&dice->rx_resources, max_payload_bytes, + fw_parent_device(dice->unit)->max_speed); + if (err < 0) + goto end;
- err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel, - fw_parent_device(dice->unit)->max_speed); + /* Set channel number */ + channel = cpu_to_be32(dice->rx_resources.channel); + err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, + &channel, 4); if (err < 0) - return err; + release_resources(dice); +end: + return err; +}
- err = snd_dice_transaction_set_enable(dice); - if (err < 0) { - amdtp_stream_stop(&dice->rx_stream); - return err; - } +static void stop_stream(struct snd_dice *dice) +{ + if (!amdtp_stream_running(&dice->rx_stream)) + return;
- return 0; + amdtp_stream_pcm_abort(&dice->rx_stream); + amdtp_stream_stop(&dice->rx_stream); + release_resources(dice); }
-int snd_dice_stream_start(struct snd_dice *dice) +static int start_stream(struct snd_dice *dice, unsigned int rate) { - __be32 channel; + unsigned int i, mode, pcm_chs, midi_ports; int err;
- if (!dice->rx_resources.allocated) { - err = fw_iso_resources_allocate(&dice->rx_resources, - amdtp_stream_get_max_payload(&dice->rx_stream), - fw_parent_device(dice->unit)->max_speed); - if (err < 0) - goto error; - - channel = cpu_to_be32(dice->rx_resources.channel); - err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, - &channel, 4); - if (err < 0) - goto err_resources; + err = snd_dice_stream_get_rate_mode(dice, rate, &mode); + if (err < 0) + goto end; + + /* + * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in + * one data block of AMDTP packet. Thus sampling transfer frequency is + * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are + * transferred on AMDTP packets at 96 kHz. Two successive samples of a + * channel are stored consecutively in the packet. This quirk is called + * as 'Dual Wire'. + * For this quirk, blocking mode is required and PCM buffer size should + * be aligned to SYT_INTERVAL. + */ + pcm_chs = dice->rx_channels[mode]; + midi_ports = dice->rx_midi_ports[mode]; + if (mode > 1) { + rate /= 2; + pcm_chs *= 2; + dice->rx_stream.double_pcm_frames = true; + } else { + dice->rx_stream.double_pcm_frames = false; }
- err = snd_dice_stream_start_packets(dice); - if (err < 0) - goto err_rx_channel; + amdtp_stream_set_parameters(&dice->rx_stream, rate, + pcm_chs, midi_ports); + if (mode > 1) { + pcm_chs /= 2;
- return 0; + for (i = 0; i < pcm_chs; i++) { + dice->rx_stream.pcm_positions[i] = i * 2; + dice->rx_stream.pcm_positions[i + pcm_chs] = i * 2 + 1; + } + }
-err_rx_channel: - channel = cpu_to_be32((u32)-1); - snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4); -err_resources: - fw_iso_resources_free(&dice->rx_resources); -error: + err = keep_resources(dice, + amdtp_stream_get_max_payload(&dice->rx_stream)); + if (err < 0) { + dev_err(&dice->unit->device, + "fail to keep isochronous resources\n"); + goto end; + } + + err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel, + fw_parent_device(dice->unit)->max_speed); + if (err < 0) + release_resources(dice); +end: return err; }
-void snd_dice_stream_stop_packets(struct snd_dice *dice) +int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate) { - if (!amdtp_stream_running(&dice->rx_stream)) - return; + unsigned int curr_rate; + int err;
- snd_dice_transaction_clear_enable(dice); - amdtp_stream_stop(&dice->rx_stream); + mutex_lock(&dice->mutex); + + /* Some packet queueing errors. */ + if (amdtp_streaming_error(&dice->rx_stream)) + stop_stream(dice); + + /* Stop stream if rate is different. */ + err = snd_dice_transaction_get_rate(dice, &curr_rate); + if (err < 0) { + dev_err(&dice->unit->device, + "fail to get sampling rate\n"); + goto end; + } + if (rate != curr_rate) + stop_stream(dice); + + if (!amdtp_stream_running(&dice->rx_stream)) { + snd_dice_transaction_clear_enable(dice); + + err = snd_dice_transaction_set_rate(dice, rate); + if (err < 0) { + dev_err(&dice->unit->device, + "fail to set sampling rate\n"); + goto end; + } + + /* Start stream. */ + err = start_stream(dice, rate); + if (err < 0) { + dev_err(&dice->unit->device, + "fail to start AMDTP stream\n"); + goto end; + } + err = snd_dice_transaction_set_enable(dice); + if (err < 0) { + dev_err(&dice->unit->device, + "fail to enable interface\n"); + stop_stream(dice); + goto end; + } + + if (!amdtp_stream_wait_callback(&dice->rx_stream, + CALLBACK_TIMEOUT)) { + snd_dice_transaction_clear_enable(dice); + stop_stream(dice); + err = -ETIMEDOUT; + } + } +end: + mutex_unlock(&dice->mutex); + return err; }
void snd_dice_stream_stop(struct snd_dice *dice) { - __be32 channel; - - snd_dice_stream_stop_packets(dice); + mutex_lock(&dice->mutex);
- if (!dice->rx_resources.allocated) - return; - - channel = cpu_to_be32((u32)-1); - snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4); + snd_dice_transaction_clear_enable(dice); + stop_stream(dice);
- fw_iso_resources_free(&dice->rx_resources); + mutex_unlock(&dice->mutex); }
int snd_dice_stream_init(struct snd_dice *dice) @@ -144,10 +232,14 @@ error:
void snd_dice_stream_destroy(struct snd_dice *dice) { - amdtp_stream_pcm_abort(&dice->rx_stream); - snd_dice_stream_stop(dice); + mutex_lock(&dice->mutex); + + snd_dice_transaction_clear_enable(dice); + stop_stream(dice); amdtp_stream_destroy(&dice->rx_stream); fw_iso_resources_destroy(&dice->rx_resources); + + mutex_unlock(&dice->mutex); }
void snd_dice_stream_update(struct snd_dice *dice) @@ -160,9 +252,16 @@ void snd_dice_stream_update(struct snd_dice *dice) * to stop so that the application can restart them in an orderly * manner. */ - amdtp_stream_pcm_abort(&dice->rx_stream); - snd_dice_stream_stop_packets(dice); + mutex_lock(&dice->mutex); + + /* The enable register becomes initialized, then streams are stopped. */ + dice->global_enabled = false; + + stop_stream(dice); + fw_iso_resources_update(&dice->rx_resources); + + mutex_unlock(&dice->mutex); }
static void dice_lock_changed(struct snd_dice *dice) diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index 7e8ecd3..7bddd29 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -304,12 +304,8 @@ static void dice_remove(struct fw_unit *unit)
snd_card_disconnect(dice->card);
- mutex_lock(&dice->mutex); - snd_dice_stream_destroy(dice);
- mutex_unlock(&dice->mutex); - snd_card_free_when_closed(dice->card); }
@@ -320,14 +316,7 @@ static void dice_update(struct fw_unit *unit) /* The handler address register becomes initialized. */ snd_dice_transaction_reinit(dice);
- mutex_lock(&dice->mutex); - - /* The enable register becomes initialized, then streams are stopped. */ - dice->global_enabled = false; - - snd_dice_stream_stop_packets(dice); - - mutex_unlock(&dice->mutex); + snd_dice_stream_update(dice); }
#define DICE_INTERFACE 0x000001 diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index 969189a..8be530f 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -160,9 +160,7 @@ extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT]; int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, unsigned int *mode);
-int snd_dice_stream_start_packets(struct snd_dice *dice); -int snd_dice_stream_start(struct snd_dice *dice); -void snd_dice_stream_stop_packets(struct snd_dice *dice); +int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate); void snd_dice_stream_stop(struct snd_dice *dice); int snd_dice_stream_init(struct snd_dice *dice); void snd_dice_stream_destroy(struct snd_dice *dice);
This commit adds support for AMDTP in-stream. As a result, Dice driver supports full duplex streams with synchronization.
AMDTP can transfer timestamps in its packets. By handling the timestamp, devices can synchronize to the other devices or drivers on the same bus.
When Dice chipset is 'enabled', it starts streams with correct settings. This 'enable' register is global, thus, when a stream is started to run, an opposite stream can't start unless turning off 'enable'. Therefore a pairt of streams must be running. This causes a loss of CPU usage when single stream is needed for neither playbacking or capturing.
This commit assumes that playback-only models also have a functionality to transmit stream for delivering timestamps.
Currently, sampling clock source is restricted to SYT-Match mode. This is improved in followed commit. I note that at SYT-Match mode, Dice can select from 4 streams for synchronization but this driver uses the 1st stream only for simplicity.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/dice/dice-pcm.c | 4 +- sound/firewire/dice/dice-stream.c | 230 ++++++++++++++++++++++++++++---------- sound/firewire/dice/dice.c | 29 +++-- sound/firewire/dice/dice.h | 26 +++-- 4 files changed, 202 insertions(+), 87 deletions(-)
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 904cb80..78b5408 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -180,7 +180,7 @@ static int playback_hw_free(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data;
- snd_dice_stream_stop(dice); + snd_dice_stream_stop_duplex(dice);
return snd_pcm_lib_free_vmalloc_buffer(substream); } @@ -190,7 +190,7 @@ static int playback_prepare(struct snd_pcm_substream *substream) struct snd_dice *dice = substream->private_data; int err;
- err = snd_dice_stream_start(dice, substream->runtime->rate); + err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); if (err >= 0) amdtp_stream_pcm_prepare(&dice->rx_stream);
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 2382ffc..2ca62ce 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -40,55 +40,79 @@ int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, return -EINVAL; }
-static void release_resources(struct snd_dice *dice) +static void release_resources(struct snd_dice *dice, + struct fw_iso_resources *resources) { unsigned int channel;
/* Reset channel number */ channel = cpu_to_be32((u32)-1); - snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, &channel, 4); - - fw_iso_resources_free(&dice->rx_resources); + if (resources == &dice->tx_resources) + snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, + &channel, 4); + else + snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, + &channel, 4); + + fw_iso_resources_free(resources); }
-static int keep_resources(struct snd_dice *dice, unsigned int max_payload_bytes) +static int keep_resources(struct snd_dice *dice, + struct fw_iso_resources *resources, + unsigned int max_payload_bytes) { unsigned int channel; int err;
- err = fw_iso_resources_allocate(&dice->rx_resources, max_payload_bytes, + err = fw_iso_resources_allocate(resources, max_payload_bytes, fw_parent_device(dice->unit)->max_speed); if (err < 0) goto end;
/* Set channel number */ - channel = cpu_to_be32(dice->rx_resources.channel); - err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, - &channel, 4); + channel = cpu_to_be32(resources->channel); + if (resources == &dice->tx_resources) + err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, + &channel, 4); + else + err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, + &channel, 4); if (err < 0) - release_resources(dice); + release_resources(dice, resources); end: return err; }
-static void stop_stream(struct snd_dice *dice) +static void stop_stream(struct snd_dice *dice, struct amdtp_stream *stream) { - if (!amdtp_stream_running(&dice->rx_stream)) - return; + amdtp_stream_pcm_abort(stream); + amdtp_stream_stop(stream);
- amdtp_stream_pcm_abort(&dice->rx_stream); - amdtp_stream_stop(&dice->rx_stream); - release_resources(dice); + if (stream == &dice->tx_stream) + release_resources(dice, &dice->tx_resources); + else + release_resources(dice, &dice->rx_resources); }
-static int start_stream(struct snd_dice *dice, unsigned int rate) +static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, + unsigned int rate) { + struct fw_iso_resources *resources; unsigned int i, mode, pcm_chs, midi_ports; int err;
err = snd_dice_stream_get_rate_mode(dice, rate, &mode); if (err < 0) goto end; + if (stream == &dice->tx_stream) { + resources = &dice->tx_resources; + pcm_chs = dice->tx_channels[mode]; + midi_ports = dice->tx_midi_ports[mode]; + } else { + resources = &dice->rx_resources; + pcm_chs = dice->rx_channels[mode]; + midi_ports = dice->rx_midi_ports[mode]; + }
/* * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in @@ -100,53 +124,73 @@ static int start_stream(struct snd_dice *dice, unsigned int rate) * For this quirk, blocking mode is required and PCM buffer size should * be aligned to SYT_INTERVAL. */ - pcm_chs = dice->rx_channels[mode]; - midi_ports = dice->rx_midi_ports[mode]; if (mode > 1) { rate /= 2; pcm_chs *= 2; - dice->rx_stream.double_pcm_frames = true; + stream->double_pcm_frames = true; } else { - dice->rx_stream.double_pcm_frames = false; + stream->double_pcm_frames = false; }
- amdtp_stream_set_parameters(&dice->rx_stream, rate, - pcm_chs, midi_ports); + amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports); if (mode > 1) { pcm_chs /= 2;
for (i = 0; i < pcm_chs; i++) { - dice->rx_stream.pcm_positions[i] = i * 2; - dice->rx_stream.pcm_positions[i + pcm_chs] = i * 2 + 1; + stream->pcm_positions[i] = i * 2; + stream->pcm_positions[i + pcm_chs] = i * 2 + 1; } }
- err = keep_resources(dice, - amdtp_stream_get_max_payload(&dice->rx_stream)); + err = keep_resources(dice, resources, + amdtp_stream_get_max_payload(stream)); if (err < 0) { dev_err(&dice->unit->device, "fail to keep isochronous resources\n"); goto end; }
- err = amdtp_stream_start(&dice->rx_stream, dice->rx_resources.channel, + err = amdtp_stream_start(stream, resources->channel, fw_parent_device(dice->unit)->max_speed); if (err < 0) - release_resources(dice); + release_resources(dice, resources); end: return err; }
-int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate) +static int get_sync_mode(struct snd_dice *dice, enum cip_flags *sync_mode) +{ + /* Currently, clock source is fixed at SYT-Match mode. */ + *sync_mode = 0; + return 0; +} + +int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) { + struct amdtp_stream *master, *slave; unsigned int curr_rate; - int err; + enum cip_flags sync_mode; + int err = 0; + + if (atomic_read(&dice->substreams_counter) == 0) + goto end;
mutex_lock(&dice->mutex);
+ err = get_sync_mode(dice, &sync_mode); + if (err < 0) + goto end; + if (sync_mode == CIP_SYNC_TO_DEVICE) { + master = &dice->tx_stream; + slave = &dice->rx_stream; + } else { + master = &dice->rx_stream; + slave = &dice->tx_stream; + } + /* Some packet queueing errors. */ - if (amdtp_streaming_error(&dice->rx_stream)) - stop_stream(dice); + if (amdtp_streaming_error(master) || amdtp_streaming_error(slave)) + stop_stream(dice, master);
/* Stop stream if rate is different. */ err = snd_dice_transaction_get_rate(dice, &curr_rate); @@ -156,11 +200,14 @@ int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate) goto end; } if (rate != curr_rate) - stop_stream(dice); + stop_stream(dice, master);
- if (!amdtp_stream_running(&dice->rx_stream)) { + if (!amdtp_stream_running(master)) { + stop_stream(dice, slave); snd_dice_transaction_clear_enable(dice);
+ amdtp_stream_set_sync(sync_mode, master, slave); + err = snd_dice_transaction_set_rate(dice, rate); if (err < 0) { dev_err(&dice->unit->device, @@ -168,25 +215,35 @@ int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate) goto end; }
- /* Start stream. */ - err = start_stream(dice, rate); + /* Start both streams. */ + err = start_stream(dice, master, rate); + if (err < 0) { + dev_err(&dice->unit->device, + "fail to start AMDTP master stream\n"); + goto end; + } + err = start_stream(dice, slave, rate); if (err < 0) { dev_err(&dice->unit->device, - "fail to start AMDTP stream\n"); + "fail to start AMDTP slave stream\n"); + stop_stream(dice, master); goto end; } err = snd_dice_transaction_set_enable(dice); if (err < 0) { dev_err(&dice->unit->device, "fail to enable interface\n"); - stop_stream(dice); + stop_stream(dice, master); + stop_stream(dice, slave); goto end; }
- if (!amdtp_stream_wait_callback(&dice->rx_stream, - CALLBACK_TIMEOUT)) { + /* Wait first callbacks */ + if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT) || + !amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) { snd_dice_transaction_clear_enable(dice); - stop_stream(dice); + stop_stream(dice, master); + stop_stream(dice, slave); err = -ETIMEDOUT; } } @@ -195,54 +252,101 @@ end: return err; }
-void snd_dice_stream_stop(struct snd_dice *dice) +void snd_dice_stream_stop_duplex(struct snd_dice *dice) { + if (atomic_read(&dice->substreams_counter) > 0) + return; + mutex_lock(&dice->mutex);
snd_dice_transaction_clear_enable(dice); - stop_stream(dice); + + stop_stream(dice, &dice->tx_stream); + stop_stream(dice, &dice->rx_stream);
mutex_unlock(&dice->mutex); }
-int snd_dice_stream_init(struct snd_dice *dice) +static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream) { int err; + struct fw_iso_resources *resources; + enum amdtp_stream_direction dir; + + if (stream == &dice->tx_stream) { + resources = &dice->tx_resources; + dir = AMDTP_IN_STREAM; + } else { + resources = &dice->rx_resources; + dir = AMDTP_OUT_STREAM; + }
- err = fw_iso_resources_init(&dice->rx_resources, dice->unit); + err = fw_iso_resources_init(resources, dice->unit); if (err < 0) goto end; - dice->rx_resources.channels_mask = 0x00000000ffffffffuLL; + resources->channels_mask = 0x00000000ffffffffuLL;
- err = amdtp_stream_init(&dice->rx_stream, dice->unit, AMDTP_OUT_STREAM, - CIP_BLOCKING); + err = amdtp_stream_init(stream, dice->unit, dir, CIP_BLOCKING); + if (err < 0) { + amdtp_stream_destroy(stream); + fw_iso_resources_destroy(resources); + } +end: + return err; +} + +static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream) +{ + amdtp_stream_destroy(stream); + + if (stream == &dice->tx_stream) + fw_iso_resources_destroy(&dice->tx_resources); + else + fw_iso_resources_destroy(&dice->rx_resources); +} + +int snd_dice_stream_init_duplex(struct snd_dice *dice) +{ + int err; + + atomic_set(&dice->substreams_counter, 0); + + err = init_stream(dice, &dice->tx_stream); if (err < 0) - goto error; + goto end;
- err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1); + err = init_stream(dice, &dice->rx_stream); if (err < 0) - goto error; + goto end; + + /* Currently, clock source is fixed at SYT-Match mode. */ + err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1); + if (err < 0) { + destroy_stream(dice, &dice->rx_stream); + destroy_stream(dice, &dice->tx_stream); + } end: return err; -error: - amdtp_stream_destroy(&dice->rx_stream); - fw_iso_resources_destroy(&dice->rx_resources); - return err; }
-void snd_dice_stream_destroy(struct snd_dice *dice) +void snd_dice_stream_destroy_duplex(struct snd_dice *dice) { mutex_lock(&dice->mutex);
snd_dice_transaction_clear_enable(dice); - stop_stream(dice); - amdtp_stream_destroy(&dice->rx_stream); - fw_iso_resources_destroy(&dice->rx_resources); + + stop_stream(dice, &dice->tx_stream); + destroy_stream(dice, &dice->tx_stream); + + stop_stream(dice, &dice->rx_stream); + destroy_stream(dice, &dice->rx_stream); + + atomic_set(&dice->substreams_counter, 0);
mutex_unlock(&dice->mutex); }
-void snd_dice_stream_update(struct snd_dice *dice) +void snd_dice_stream_update_duplex(struct snd_dice *dice) { /* * On a bus reset, the DICE firmware disables streaming and then goes @@ -257,9 +361,11 @@ void snd_dice_stream_update(struct snd_dice *dice) /* The enable register becomes initialized, then streams are stopped. */ dice->global_enabled = false;
- stop_stream(dice); + stop_stream(dice, &dice->rx_stream); + stop_stream(dice, &dice->tx_stream);
fw_iso_resources_update(&dice->rx_resources); + fw_iso_resources_update(&dice->tx_resources);
mutex_unlock(&dice->mutex); } diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index 7bddd29..2390cc6 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -30,7 +30,6 @@ static int dice_interface_check(struct fw_unit *unit) int key, val, vendor = -1, model = -1, err; unsigned int category, i; __be32 *pointers, value; - __be32 tx_data[4]; __be32 version;
pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32), @@ -85,16 +84,6 @@ static int dice_interface_check(struct fw_unit *unit) } }
- /* We support playback only. Let capture devices be handled by FFADO. */ - err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST, - DICE_PRIVATE_SPACE + - be32_to_cpu(pointers[2]) * 4, - tx_data, sizeof(tx_data), 0); - if (err < 0 || (tx_data[0] && tx_data[3])) { - err = -ENODEV; - goto end; - } - /* * Check that the implemented DICE driver specification major version * number matches. @@ -142,6 +131,8 @@ static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode) int err;
if (highest_supported_mode_rate(dice, mode, &rate) < 0) { + dice->tx_channels[mode] = 0; + dice->tx_midi_ports[mode] = 0; dice->rx_channels[mode] = 0; dice->rx_midi_ports[mode] = 0; return 0; @@ -151,6 +142,14 @@ static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode) if (err < 0) return err;
+ err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, + values, sizeof(values)); + if (err < 0) + return err; + + dice->tx_channels[mode] = be32_to_cpu(values[0]); + dice->tx_midi_ports[mode] = be32_to_cpu(values[1]); + err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, values, sizeof(values)); if (err < 0) @@ -280,13 +279,13 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
snd_dice_create_proc(dice);
- err = snd_dice_stream_init(dice); + err = snd_dice_stream_init_duplex(dice); if (err < 0) goto error;
err = snd_card_register(card); if (err < 0) { - snd_dice_stream_destroy(dice); + snd_dice_stream_destroy_duplex(dice); goto error; }
@@ -304,7 +303,7 @@ static void dice_remove(struct fw_unit *unit)
snd_card_disconnect(dice->card);
- snd_dice_stream_destroy(dice); + snd_dice_stream_destroy_duplex(dice);
snd_card_free_when_closed(dice->card); } @@ -316,7 +315,7 @@ static void dice_update(struct fw_unit *unit) /* The handler address register becomes initialized. */ snd_dice_transaction_reinit(dice);
- snd_dice_stream_update(dice); + snd_dice_stream_update_duplex(dice); }
#define DICE_INTERFACE 0x000001 diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index 8be530f..a868485 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -52,18 +52,28 @@ struct snd_dice { unsigned int rsrv_offset;
unsigned int clock_caps; + unsigned int tx_channels[3]; unsigned int rx_channels[3]; + unsigned int tx_midi_ports[3]; unsigned int rx_midi_ports[3]; + struct fw_address_handler notification_handler; int owner_generation; + u32 notification_bits; + + /* For uapi */ int dev_lock_count; /* > 0 driver, < 0 userspace */ bool dev_lock_changed; - bool global_enabled; - struct completion clock_accepted; wait_queue_head_t hwdep_wait; - u32 notification_bits; + + /* For streaming */ + struct fw_iso_resources tx_resources; struct fw_iso_resources rx_resources; + struct amdtp_stream tx_stream; struct amdtp_stream rx_stream; + bool global_enabled; + struct completion clock_accepted; + atomic_t substreams_counter; };
enum snd_dice_addr_type { @@ -160,11 +170,11 @@ extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT]; int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, unsigned int *mode);
-int snd_dice_stream_start(struct snd_dice *dice, unsigned int rate); -void snd_dice_stream_stop(struct snd_dice *dice); -int snd_dice_stream_init(struct snd_dice *dice); -void snd_dice_stream_destroy(struct snd_dice *dice); -void snd_dice_stream_update(struct snd_dice *dice); +int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate); +void snd_dice_stream_stop_duplex(struct snd_dice *dice); +int snd_dice_stream_init_duplex(struct snd_dice *dice); +void snd_dice_stream_destroy_duplex(struct snd_dice *dice); +void snd_dice_stream_update_duplex(struct snd_dice *dice);
int snd_dice_stream_lock_try(struct snd_dice *dice); void snd_dice_stream_lock_release(struct snd_dice *dice);
This commit allows another mode.
When sampling clock source is SYT-Match mode, devices handle 'presentation timestamp' in received packets and generates sampling clock according to the information. In this case, driver is synchronization master and must transfer correct value in SYT field of each packets in outgoing stream, then the outgoing stream is a master stream.
On the other hand, non SYT-Match mode, devices do this. So drivers must pick up the value in SYT field of incoming packets and use the value for outgoing stream. Currently firewire-lib module achieve this work.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/dice/dice-pcm.c | 37 ++++++++++++++++++++++++++++++++++ sound/firewire/dice/dice-stream.c | 35 +++++++++++++++++++++----------- sound/firewire/dice/dice-transaction.c | 5 ----- sound/firewire/dice/dice.h | 2 -- 4 files changed, 60 insertions(+), 19 deletions(-)
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 78b5408..a0dbe0c 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -140,6 +140,8 @@ end: static int pcm_open(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; + unsigned int source, rate; + bool internal; int err;
err = snd_dice_stream_lock_try(dice); @@ -149,6 +151,41 @@ static int pcm_open(struct snd_pcm_substream *substream) err = init_hw_info(dice, substream); if (err < 0) goto err_locked; + + err = snd_dice_transaction_get_clock_source(dice, &source); + if (err < 0) + goto err_locked; + switch (source) { + case CLOCK_SOURCE_AES1: + case CLOCK_SOURCE_AES2: + case CLOCK_SOURCE_AES3: + case CLOCK_SOURCE_AES4: + case CLOCK_SOURCE_AES_ANY: + case CLOCK_SOURCE_ADAT: + case CLOCK_SOURCE_TDIF: + case CLOCK_SOURCE_WC: + internal = false; + break; + default: + internal = true; + break; + } + + /* + * When source of clock is not internal or any PCM streams are running, + * available sampling rate is limited at current sampling rate. + */ + if (!internal || + amdtp_stream_pcm_running(&dice->tx_stream) || + amdtp_stream_pcm_running(&dice->rx_stream)) { + err = snd_dice_transaction_get_rate(dice, &rate); + if (err < 0) + goto err_locked; + substream->runtime->hw.rate_min = rate; + substream->runtime->hw.rate_max = rate; + } + + snd_pcm_set_sync(substream); end: return err; err_locked: diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 2ca62ce..c95709e 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -160,9 +160,29 @@ end:
static int get_sync_mode(struct snd_dice *dice, enum cip_flags *sync_mode) { - /* Currently, clock source is fixed at SYT-Match mode. */ - *sync_mode = 0; - return 0; + u32 source; + int err; + + err = snd_dice_transaction_get_clock_source(dice, &source); + if (err < 0) + goto end; + + switch (source) { + /* So-called 'SYT Match' modes, sync_to_syt value of packets received */ + case CLOCK_SOURCE_ARX4: /* in 4th stream */ + case CLOCK_SOURCE_ARX3: /* in 3rd stream */ + case CLOCK_SOURCE_ARX2: /* in 2nd stream */ + err = -ENOSYS; + break; + case CLOCK_SOURCE_ARX1: /* in 1st stream, which this driver uses */ + *sync_mode = 0; + break; + default: + *sync_mode = CIP_SYNC_TO_DEVICE; + break; + } +end: + return err; }
int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) @@ -316,15 +336,6 @@ int snd_dice_stream_init_duplex(struct snd_dice *dice) goto end;
err = init_stream(dice, &dice->rx_stream); - if (err < 0) - goto end; - - /* Currently, clock source is fixed at SYT-Match mode. */ - err = snd_dice_transaction_set_clock_source(dice, CLOCK_SOURCE_ARX1); - if (err < 0) { - destroy_stream(dice, &dice->rx_stream); - destroy_stream(dice, &dice->tx_stream); - } end: return err; } diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c index 0f4b52a..01d25bf 100644 --- a/sound/firewire/dice/dice-transaction.c +++ b/sound/firewire/dice/dice-transaction.c @@ -137,11 +137,6 @@ int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
return err; } -int snd_dice_transaction_set_clock_source(struct snd_dice *dice, - unsigned int source) -{ - return set_clock_info(dice, UINT_MAX, source); -}
int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate) { diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index a868485..8ea5cb4 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -152,8 +152,6 @@ static inline int snd_dice_transaction_read_sync(struct snd_dice *dice, buf, len); }
-int snd_dice_transaction_set_clock_source(struct snd_dice *dice, - unsigned int source); int snd_dice_transaction_get_clock_source(struct snd_dice *dice, unsigned int *source); int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate);
This commit adds a support for PCM capture.
When source of clock is external or opposite PCM substream is already running, available sampling rate is limited at current one.
This driver give no way to change source of clock because it can be implemented in user-land.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/Kconfig | 3 - sound/firewire/dice/dice-pcm.c | 139 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 128 insertions(+), 14 deletions(-)
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 46dff64..c8e704c 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -20,9 +20,6 @@ config SND_DICE Say Y here to include support for many DACs based on the DICE chip family (DICE-II/Jr/Mini) from TC Applied Technologies.
- At the moment, this driver supports playback only. If you - want to use devices that support capturing, use FFADO instead. - To compile this driver as a module, choose M here: the module will be called snd-dice.
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index a0dbe0c..3427a21 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -12,7 +12,8 @@ static int dice_rate_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - struct snd_dice *dice = rule->private; + struct snd_pcm_substream *substream = rule->private; + struct snd_dice *dice = substream->private_data;
const struct snd_interval *c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); @@ -21,7 +22,12 @@ static int dice_rate_constraint(struct snd_pcm_hw_params *params, struct snd_interval rates = { .min = UINT_MAX, .max = 0, .integer = 1 }; - unsigned int i, rate, mode, *pcm_channels = dice->rx_channels; + unsigned int i, rate, mode, *pcm_channels; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + pcm_channels = dice->tx_channels; + else + pcm_channels = dice->rx_channels;
for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { rate = snd_dice_rates[i]; @@ -41,7 +47,8 @@ static int dice_rate_constraint(struct snd_pcm_hw_params *params, static int dice_channels_constraint(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - struct snd_dice *dice = rule->private; + struct snd_pcm_substream *substream = rule->private; + struct snd_dice *dice = substream->private_data;
const struct snd_interval *r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); @@ -50,7 +57,12 @@ static int dice_channels_constraint(struct snd_pcm_hw_params *params, struct snd_interval channels = { .min = UINT_MAX, .max = 0, .integer = 1 }; - unsigned int i, rate, mode, *pcm_channels = dice->rx_channels; + unsigned int i, rate, mode, *pcm_channels; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + pcm_channels = dice->tx_channels; + else + pcm_channels = dice->rx_channels;
for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { rate = snd_dice_rates[i]; @@ -109,30 +121,42 @@ static int init_hw_info(struct snd_dice *dice, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_hardware *hw = &runtime->hw; + struct amdtp_stream *stream; + unsigned int *pcm_channels; int err;
hw->info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_JOINT_DUPLEX | SNDRV_PCM_INFO_BLOCK_TRANSFER; - hw->formats = AMDTP_OUT_PCM_FORMAT_BITS;
- limit_channels_and_rates(dice, runtime, dice->rx_channels); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + hw->formats = AMDTP_IN_PCM_FORMAT_BITS; + stream = &dice->tx_stream; + pcm_channels = dice->tx_channels; + } else { + hw->formats = AMDTP_OUT_PCM_FORMAT_BITS; + stream = &dice->rx_stream; + pcm_channels = dice->rx_channels; + } + + limit_channels_and_rates(dice, runtime, pcm_channels); limit_period_and_buffer(hw);
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - dice_rate_constraint, dice, + dice_rate_constraint, substream, SNDRV_PCM_HW_PARAM_CHANNELS, -1); if (err < 0) goto end; err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - dice_channels_constraint, dice, + dice_channels_constraint, substream, SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) goto end;
- err = amdtp_stream_add_pcm_hw_constraints(&dice->rx_stream, runtime); + err = amdtp_stream_add_pcm_hw_constraints(stream, runtime); end: return err; } @@ -202,10 +226,28 @@ static int pcm_close(struct snd_pcm_substream *substream) return 0; }
+static int capture_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_dice *dice = substream->private_data; + + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + atomic_inc(&dice->substreams_counter); + + amdtp_stream_set_pcm_format(&dice->tx_stream, + params_format(hw_params)); + + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); +} static int playback_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_dice *dice = substream->private_data; + + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) + atomic_inc(&dice->substreams_counter); + amdtp_stream_set_pcm_format(&dice->rx_stream, params_format(hw_params));
@@ -213,15 +255,41 @@ static int playback_hw_params(struct snd_pcm_substream *substream, params_buffer_bytes(hw_params)); }
+static int capture_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) + atomic_dec(&dice->substreams_counter); + + snd_dice_stream_stop_duplex(dice); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + static int playback_hw_free(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data;
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) + atomic_dec(&dice->substreams_counter); + snd_dice_stream_stop_duplex(dice);
return snd_pcm_lib_free_vmalloc_buffer(substream); }
+static int capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + int err; + + err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); + if (err >= 0) + amdtp_stream_pcm_prepare(&dice->tx_stream); + + return 0; +} static int playback_prepare(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; @@ -234,6 +302,23 @@ static int playback_prepare(struct snd_pcm_substream *substream) return err; }
+static int capture_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_dice *dice = substream->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + amdtp_stream_pcm_trigger(&dice->tx_stream, substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + amdtp_stream_pcm_trigger(&dice->tx_stream, NULL); + break; + default: + return -EINVAL; + } + + return 0; +} static int playback_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_dice *dice = substream->private_data; @@ -252,6 +337,12 @@ static int playback_trigger(struct snd_pcm_substream *substream, int cmd) return 0; }
+static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + return amdtp_stream_pcm_pointer(&dice->tx_stream); +} static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; @@ -261,6 +352,18 @@ static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
int snd_dice_create_pcm(struct snd_dice *dice) { + static struct snd_pcm_ops capture_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = capture_hw_params, + .hw_free = capture_hw_free, + .prepare = capture_prepare, + .trigger = capture_trigger, + .pointer = capture_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; static struct snd_pcm_ops playback_ops = { .open = pcm_open, .close = pcm_close, @@ -274,14 +377,28 @@ int snd_dice_create_pcm(struct snd_dice *dice) .mmap = snd_pcm_lib_mmap_vmalloc, }; struct snd_pcm *pcm; + unsigned int i, capture, playback; int err;
- err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm); + capture = playback = 0; + for (i = 0; i < 3; i++) { + if (dice->tx_channels[i] > 0) + capture = 1; + if (dice->rx_channels[i] > 0) + playback = 1; + } + + err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm); if (err < 0) return err; pcm->private_data = dice; strcpy(pcm->name, dice->card->shortname); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); + + if (capture > 0) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); + + if (playback > 0) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
return 0; }
This commit adds a support for MIDI capture/playback
When MIDI substrams already start streaming and PCM substreams are going to join at different sampling rate, streams are stopped once. Then sampling rate is changed and streams are restarted.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/dice/Makefile | 4 +- sound/firewire/dice/dice-midi.c | 176 ++++++++++++++++++++++++++++++++++++++ sound/firewire/dice/dice-stream.c | 2 + sound/firewire/dice/dice.c | 4 + sound/firewire/dice/dice.h | 3 + 5 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 sound/firewire/dice/dice-midi.c
diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile index 9a48289..9ef228e 100644 --- a/sound/firewire/dice/Makefile +++ b/sound/firewire/dice/Makefile @@ -1,3 +1,3 @@ -snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-pcm.o \ - dice-hwdep.o dice.o +snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \ + dice-pcm.o dice-hwdep.o dice.o obj-m += snd-dice.o diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c new file mode 100644 index 0000000..4f4eae7 --- /dev/null +++ b/sound/firewire/dice/dice-midi.c @@ -0,0 +1,176 @@ +/* + * dice_midi.c - a part of driver for Dice based devices + * + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ +#include "dice.h" + +static int capture_open(struct snd_rawmidi_substream *substream) +{ + struct snd_dice *dice = substream->rmidi->private_data; + int err; + + err = snd_dice_stream_lock_try(dice); + if (err < 0) + goto end; + + atomic_inc(&dice->substreams_counter); + err = snd_dice_stream_start_duplex(dice, 0); + if (err < 0) + snd_dice_stream_lock_release(dice); +end: + return err; +} + +static int playback_open(struct snd_rawmidi_substream *substream) +{ + struct snd_dice *dice = substream->rmidi->private_data; + int err; + + err = snd_dice_stream_lock_try(dice); + if (err < 0) + goto end; + + atomic_inc(&dice->substreams_counter); + err = snd_dice_stream_start_duplex(dice, 0); + if (err < 0) + snd_dice_stream_lock_release(dice); +end: + return err; +} + +static int capture_close(struct snd_rawmidi_substream *substream) +{ + struct snd_dice *dice = substream->rmidi->private_data; + + atomic_dec(&dice->substreams_counter); + snd_dice_stream_stop_duplex(dice); + + snd_dice_stream_lock_release(dice); + return 0; +} + +static int playback_close(struct snd_rawmidi_substream *substream) +{ + struct snd_dice *dice = substream->rmidi->private_data; + + atomic_dec(&dice->substreams_counter); + snd_dice_stream_stop_duplex(dice); + + snd_dice_stream_lock_release(dice); + return 0; +} + +static void capture_trigger(struct snd_rawmidi_substream *substrm, int up) +{ + struct snd_dice *dice = substrm->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&dice->lock, flags); + + if (up) + amdtp_stream_midi_trigger(&dice->tx_stream, + substrm->number, substrm); + else + amdtp_stream_midi_trigger(&dice->tx_stream, + substrm->number, NULL); + + spin_unlock_irqrestore(&dice->lock, flags); +} + +static void playback_trigger(struct snd_rawmidi_substream *substrm, int up) +{ + struct snd_dice *dice = substrm->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&dice->lock, flags); + + if (up) + amdtp_stream_midi_trigger(&dice->rx_stream, + substrm->number, substrm); + else + amdtp_stream_midi_trigger(&dice->rx_stream, + substrm->number, NULL); + + spin_unlock_irqrestore(&dice->lock, flags); +} + +static struct snd_rawmidi_ops capture_ops = { + .open = capture_open, + .close = capture_close, + .trigger = capture_trigger, +}; + +static struct snd_rawmidi_ops playback_ops = { + .open = playback_open, + .close = playback_close, + .trigger = playback_trigger, +}; + +static void set_midi_substream_names(struct snd_dice *dice, + struct snd_rawmidi_str *str) +{ + struct snd_rawmidi_substream *subs; + + list_for_each_entry(subs, &str->substreams, list) { + snprintf(subs->name, sizeof(subs->name), + "%s MIDI %d", dice->card->shortname, subs->number + 1); + } +} + +int snd_dice_create_midi(struct snd_dice *dice) +{ + struct snd_rawmidi *rmidi; + struct snd_rawmidi_str *str; + unsigned int i, midi_in_ports, midi_out_ports; + int err; + + midi_in_ports = midi_out_ports = 0; + for (i = 0; i < 3; i++) { + midi_in_ports = max(dice->tx_midi_ports[i], midi_in_ports); + midi_out_ports = max(dice->rx_midi_ports[i], midi_out_ports); + } + + if (midi_in_ports + midi_out_ports == 0) + return 0; + + /* create midi ports */ + err = snd_rawmidi_new(dice->card, dice->card->driver, 0, + midi_out_ports, midi_in_ports, + &rmidi); + if (err < 0) + return err; + + snprintf(rmidi->name, sizeof(rmidi->name), + "%s MIDI", dice->card->shortname); + rmidi->private_data = dice; + + if (midi_in_ports > 0) { + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; + + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, + &capture_ops); + + str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]; + + set_midi_substream_names(dice, str); + } + + if (midi_out_ports > 0) { + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; + + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, + &playback_ops); + + str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; + + set_midi_substream_names(dice, str); + } + + if ((midi_out_ports > 0) && (midi_in_ports > 0)) + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; + + return 0; +} diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index c95709e..84c4868 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -219,6 +219,8 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) "fail to get sampling rate\n"); goto end; } + if (rate == 0) + rate = curr_rate; if (rate != curr_rate) stop_stream(dice, master);
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index 2390cc6..b6b2c92 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -279,6 +279,10 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
snd_dice_create_proc(dice);
+ err = snd_dice_create_midi(dice); + if (err < 0) + goto error; + err = snd_dice_stream_init_duplex(dice); if (err < 0) goto error; diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index 8ea5cb4..dbfb988 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -32,6 +32,7 @@ #include <sound/initval.h> #include <sound/pcm.h> #include <sound/pcm_params.h> +#include <sound/rawmidi.h>
#include "../amdtp.h" #include "../iso-resources.h" @@ -183,4 +184,6 @@ int snd_dice_create_hwdep(struct snd_dice *dice);
void snd_dice_create_proc(struct snd_dice *dice);
+int snd_dice_create_midi(struct snd_dice *dice); + #endif
Some developers test this driver, thus it's better to remove its experimental state.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- sound/firewire/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index c8e704c..1ea249d 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -13,12 +13,12 @@ config SND_FIREWIRE_LIB select SND_RAWMIDI
config SND_DICE - tristate "DICE-based DACs (EXPERIMENTAL)" + tristate "DICE-based DACs support" select SND_HWDEP select SND_FIREWIRE_LIB help Say Y here to include support for many DACs based on the DICE - chip family (DICE-II/Jr/Mini) from TC Applied Technologies. + chip family (DICE-II/Jr/Mini) from TC Applied Technologies:
To compile this driver as a module, choose M here: the module will be called snd-dice.
At Sat, 4 Oct 2014 20:27:10 +0900, Takashi Sakamoto wrote:
This 14 patchset is to enable ALSA Dice driver to handle more models with some functionalities:
- up to 192.0 kHz
- PCM capture
- MIDI playback/capture
- Duplex streams with synchronization (with snd-firewire-lib)
This driver doesn't support:
- Synchronization between multiple devices on the same bus For this purpose, please join in user-space driver project (FFADO)
- Any control functionality for internal DSP For this purpose, please use ffado-dbus-server/ffado-mixer, or write your own application
Remarks:
- When this driver is loaded, user-space driver fails to register notification address, thus cannot handle device for streaming. See:
http://mailman.alsa-project.org/pipermail/alsa-devel/2014-September/081731.h...
Newly supported models (as long as I know): Supported devices: TC Electronic Desktop Konnekt 6 Impact Twin Konnekt 8/24D/Live Studio Konnekt 48 PreSonus FireStudio 26x26/Mobile/Project/Tube/LightPipe StudioLive 1602/1642/2442 FireStation Mackie Onyx Blackbird Onyx i-series (latter models) Focusrite Saffire Pro 14/24/24 DSP/26/40/56 M-Audio Profire 610/2626 Alessis IO 24/26 MultiMix 12/16 Weiss ADC2 Vesta Mineva AFI 1 TAG DAC 1 INT 202 DAC 202 Flex 5000 DnR Axus FireWire I/O card 16x16 Lexicon I-ONIX FW 810S Allen and Heath Zed R16 Midas Venice F32
Updates from my previous RFC: http://mailman.alsa-project.org/pipermail/alsa-devel/2014-September/081673.h...
- Improve bus reset handling, especially for registering notification address At bus reset, Dice devices initialize a register for notification address, then drivers need to register the address again. Just after powering on, devices generates any bus reset, then drivers often fail to detect stream formation because this operation requires to change sampling rate. My improvement is retries to register notification address when notification wait is timed out.
- Remove experimental state
- Fix minor bugs
JFYI,
as the patches are massive and a bit intrusive, they won't fit with 3.18 merge, unfortunately. So we'll plenty of time for review :)
thanks,
Takashi -- "There can be only one!"
I am using TC Electronic's Desktop Konnekt 6 with snd_dice for a few days -- no problems with playback ;) Gentoo x86_64, linux 3.14.14.
On Tue, Oct 7, 2014 at 10:19 PM, Takashi Iwai tiwai@suse.de wrote:
At Sat, 4 Oct 2014 20:27:10 +0900, Takashi Sakamoto wrote:
This 14 patchset is to enable ALSA Dice driver to handle more models with some functionalities:
- up to 192.0 kHz
- PCM capture
- MIDI playback/capture
- Duplex streams with synchronization (with snd-firewire-lib)
This driver doesn't support:
- Synchronization between multiple devices on the same bus For this purpose, please join in user-space driver project (FFADO)
- Any control functionality for internal DSP For this purpose, please use ffado-dbus-server/ffado-mixer, or write your own application
Remarks:
- When this driver is loaded, user-space driver fails to register notification address, thus cannot handle device for streaming. See:
http://mailman.alsa-project.org/pipermail/alsa-devel/2014-September/081731.h...
Newly supported models (as long as I know): Supported devices: TC Electronic Desktop Konnekt 6 Impact Twin Konnekt 8/24D/Live Studio Konnekt 48 PreSonus FireStudio 26x26/Mobile/Project/Tube/LightPipe StudioLive 1602/1642/2442 FireStation Mackie Onyx Blackbird Onyx i-series (latter models) Focusrite Saffire Pro 14/24/24 DSP/26/40/56 M-Audio Profire 610/2626 Alessis IO 24/26 MultiMix 12/16 Weiss ADC2 Vesta Mineva AFI 1 TAG DAC 1 INT 202 DAC 202 Flex 5000 DnR Axus FireWire I/O card 16x16 Lexicon I-ONIX FW 810S Allen and Heath Zed R16 Midas Venice F32
Updates from my previous RFC:
http://mailman.alsa-project.org/pipermail/alsa-devel/2014-September/081673.h...
- Improve bus reset handling, especially for registering notification
address
At bus reset, Dice devices initialize a register for notification
address,
then drivers need to register the address again. Just after powering on, devices generates any bus reset, then drivers
often
fail to detect stream formation because this operation requires to
change
sampling rate. My improvement is retries to register notification address when
notification
wait is timed out.
- Remove experimental state
- Fix minor bugs
JFYI,
as the patches are massive and a bit intrusive, they won't fit with 3.18 merge, unfortunately. So we'll plenty of time for review :)
thanks,
Takashi -- "There can be only one!" _______________________________________________ Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
participants (3)
-
Anthony Labunsky
-
Takashi Iwai
-
Takashi Sakamoto