diff options
Diffstat (limited to 'sound/firewire')
-rw-r--r-- | sound/firewire/dice/Makefile | 3 | ||||
-rw-r--r-- | sound/firewire/dice/dice-alesis.c | 52 | ||||
-rw-r--r-- | sound/firewire/dice/dice-extension.c | 172 | ||||
-rw-r--r-- | sound/firewire/dice/dice-interface.h | 9 | ||||
-rw-r--r-- | sound/firewire/dice/dice-midi.c | 23 | ||||
-rw-r--r-- | sound/firewire/dice/dice-pcm.c | 233 | ||||
-rw-r--r-- | sound/firewire/dice/dice-proc.c | 80 | ||||
-rw-r--r-- | sound/firewire/dice/dice-stream.c | 283 | ||||
-rw-r--r-- | sound/firewire/dice/dice-tcelectronic.c | 100 | ||||
-rw-r--r-- | sound/firewire/dice/dice-transaction.c | 49 | ||||
-rw-r--r-- | sound/firewire/dice/dice.c | 139 | ||||
-rw-r--r-- | sound/firewire/dice/dice.h | 24 | ||||
-rw-r--r-- | sound/firewire/isight.c | 10 |
13 files changed, 937 insertions, 240 deletions
diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile index 55b4be9b0034..7b7997a5754c 100644 --- a/sound/firewire/dice/Makefile +++ b/sound/firewire/dice/Makefile @@ -1,3 +1,4 @@ snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \ - dice-pcm.o dice-hwdep.o dice.o + dice-pcm.o dice-hwdep.o dice.o dice-tcelectronic.o \ + dice-alesis.o dice-extension.o obj-$(CONFIG_SND_DICE) += snd-dice.o diff --git a/sound/firewire/dice/dice-alesis.c b/sound/firewire/dice/dice-alesis.c new file mode 100644 index 000000000000..b2efb1c71a98 --- /dev/null +++ b/sound/firewire/dice/dice-alesis.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dice-alesis.c - a part of driver for DICE based devices + * + * Copyright (c) 2018 Takashi Sakamoto + */ + +#include "dice.h" + +static const unsigned int +alesis_io14_tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT] = { + {6, 6, 4}, /* Tx0 = Analog + S/PDIF. */ + {8, 4, 0}, /* Tx1 = ADAT1. */ +}; + +static const unsigned int +alesis_io26_tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT] = { + {10, 10, 8}, /* Tx0 = Analog + S/PDIF. */ + {16, 8, 0}, /* Tx1 = ADAT1 + ADAT2. */ +}; + +int snd_dice_detect_alesis_formats(struct snd_dice *dice) +{ + __be32 reg; + u32 data; + int i; + int err; + + err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, ®, + sizeof(reg)); + if (err < 0) + return err; + data = be32_to_cpu(reg); + + if (data == 4 || data == 6) { + memcpy(dice->tx_pcm_chs, alesis_io14_tx_pcm_chs, + MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * + sizeof(unsigned int)); + } else { + memcpy(dice->rx_pcm_chs, alesis_io26_tx_pcm_chs, + MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * + sizeof(unsigned int)); + } + + for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i) + dice->rx_pcm_chs[0][i] = 8; + + dice->tx_midi_ports[0] = 1; + dice->rx_midi_ports[0] = 1; + + return 0; +} diff --git a/sound/firewire/dice/dice-extension.c b/sound/firewire/dice/dice-extension.c new file mode 100644 index 000000000000..a63fcbc875ad --- /dev/null +++ b/sound/firewire/dice/dice-extension.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dice-extension.c - a part of driver for DICE based devices + * + * Copyright (c) 2018 Takashi Sakamoto + */ + +#include "dice.h" + +/* For TCD2210/2220, TCAT defines extension of application protocol. */ + +#define DICE_EXT_APP_SPACE 0xffffe0200000uLL + +#define DICE_EXT_APP_CAPS_OFFSET 0x00 +#define DICE_EXT_APP_CAPS_SIZE 0x04 +#define DICE_EXT_APP_CMD_OFFSET 0x08 +#define DICE_EXT_APP_CMD_SIZE 0x0c +#define DICE_EXT_APP_MIXER_OFFSET 0x10 +#define DICE_EXT_APP_MIXER_SIZE 0x14 +#define DICE_EXT_APP_PEAK_OFFSET 0x18 +#define DICE_EXT_APP_PEAK_SIZE 0x1c +#define DICE_EXT_APP_ROUTER_OFFSET 0x20 +#define DICE_EXT_APP_ROUTER_SIZE 0x24 +#define DICE_EXT_APP_STREAM_OFFSET 0x28 +#define DICE_EXT_APP_STREAM_SIZE 0x2c +#define DICE_EXT_APP_CURRENT_OFFSET 0x30 +#define DICE_EXT_APP_CURRENT_SIZE 0x34 +#define DICE_EXT_APP_STANDALONE_OFFSET 0x38 +#define DICE_EXT_APP_STANDALONE_SIZE 0x3c +#define DICE_EXT_APP_APPLICATION_OFFSET 0x40 +#define DICE_EXT_APP_APPLICATION_SIZE 0x44 + +#define EXT_APP_STREAM_TX_NUMBER 0x0000 +#define EXT_APP_STREAM_RX_NUMBER 0x0004 +#define EXT_APP_STREAM_ENTRIES 0x0008 +#define EXT_APP_STREAM_ENTRY_SIZE 0x010c +#define EXT_APP_NUMBER_AUDIO 0x0000 +#define EXT_APP_NUMBER_MIDI 0x0004 +#define EXT_APP_NAMES 0x0008 +#define EXT_APP_NAMES_SIZE 256 +#define EXT_APP_AC3 0x0108 + +#define EXT_APP_CONFIG_LOW_ROUTER 0x0000 +#define EXT_APP_CONFIG_LOW_STREAM 0x1000 +#define EXT_APP_CONFIG_MIDDLE_ROUTER 0x2000 +#define EXT_APP_CONFIG_MIDDLE_STREAM 0x3000 +#define EXT_APP_CONFIG_HIGH_ROUTER 0x4000 +#define EXT_APP_CONFIG_HIGH_STREAM 0x5000 + +static inline int read_transaction(struct snd_dice *dice, u64 section_addr, + u32 offset, void *buf, size_t len) +{ + return snd_fw_transaction(dice->unit, + len == 4 ? TCODE_READ_QUADLET_REQUEST : + TCODE_READ_BLOCK_REQUEST, + section_addr + offset, buf, len, 0); +} + +static int read_stream_entries(struct snd_dice *dice, u64 section_addr, + u32 base_offset, unsigned int stream_count, + unsigned int mode, + unsigned int pcm_channels[MAX_STREAMS][3], + unsigned int midi_ports[MAX_STREAMS]) +{ + u32 entry_offset; + __be32 reg[2]; + int err; + int i; + + for (i = 0; i < stream_count; ++i) { + entry_offset = base_offset + i * EXT_APP_STREAM_ENTRY_SIZE; + err = read_transaction(dice, section_addr, + entry_offset + EXT_APP_NUMBER_AUDIO, + reg, sizeof(reg)); + if (err < 0) + return err; + pcm_channels[i][mode] = be32_to_cpu(reg[0]); + midi_ports[i] = max(midi_ports[i], be32_to_cpu(reg[1])); + } + + return 0; +} + +static int detect_stream_formats(struct snd_dice *dice, u64 section_addr) +{ + u32 base_offset; + __be32 reg[2]; + unsigned int stream_count; + int mode; + int err = 0; + + for (mode = 0; mode < SND_DICE_RATE_MODE_COUNT; ++mode) { + unsigned int cap; + + /* + * Some models report stream formats at highest mode, however + * they don't support the mode. Check clock capabilities. + */ + if (mode == 2) { + cap = CLOCK_CAP_RATE_176400 | CLOCK_CAP_RATE_192000; + } else if (mode == 1) { + cap = CLOCK_CAP_RATE_88200 | CLOCK_CAP_RATE_96000; + } else { + cap = CLOCK_CAP_RATE_32000 | CLOCK_CAP_RATE_44100 | + CLOCK_CAP_RATE_48000; + } + if (!(cap & dice->clock_caps)) + continue; + + base_offset = 0x2000 * mode + 0x1000; + + err = read_transaction(dice, section_addr, + base_offset + EXT_APP_STREAM_TX_NUMBER, + ®, sizeof(reg)); + if (err < 0) + break; + + base_offset += EXT_APP_STREAM_ENTRIES; + stream_count = be32_to_cpu(reg[0]); + err = read_stream_entries(dice, section_addr, base_offset, + stream_count, mode, + dice->tx_pcm_chs, + dice->tx_midi_ports); + if (err < 0) + break; + + base_offset += stream_count * EXT_APP_STREAM_ENTRY_SIZE; + stream_count = be32_to_cpu(reg[1]); + err = read_stream_entries(dice, section_addr, base_offset, + stream_count, + mode, dice->rx_pcm_chs, + dice->rx_midi_ports); + if (err < 0) + break; + } + + return err; +} + +int snd_dice_detect_extension_formats(struct snd_dice *dice) +{ + __be32 *pointers; + unsigned int i; + u64 section_addr; + int err; + + pointers = kmalloc_array(9, sizeof(__be32) * 2, GFP_KERNEL); + if (pointers == NULL) + return -ENOMEM; + + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + DICE_EXT_APP_SPACE, pointers, + 9 * sizeof(__be32) * 2, 0); + if (err < 0) + goto end; + + /* Check two of them for offset have the same value or not. */ + for (i = 0; i < 9; ++i) { + int j; + + for (j = i + 1; j < 9; ++j) { + if (pointers[i * 2] == pointers[j * 2]) + goto end; + } + } + + section_addr = DICE_EXT_APP_SPACE + be32_to_cpu(pointers[12]) * 4; + err = detect_stream_formats(dice, section_addr); +end: + kfree(pointers); + return err; +} diff --git a/sound/firewire/dice/dice-interface.h b/sound/firewire/dice/dice-interface.h index 15a484b05298..9cad3d608229 100644 --- a/sound/firewire/dice/dice-interface.h +++ b/sound/firewire/dice/dice-interface.h @@ -175,13 +175,18 @@ #define GLOBAL_SAMPLE_RATE 0x05c /* + * Some old firmware versions do not have the following global registers. + * Windows drivers produced by TCAT lost backward compatibility in its + * early release because they can handle firmware only which supports the + * following registers. + */ + +/* * The version of the DICE driver specification that this device conforms to; * read-only. */ #define GLOBAL_VERSION 0x060 -/* Some old firmware versions do not have the following global registers: */ - /* * Supported sample rates and clock sources; read-only. */ diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c index 8ff6da3c51f7..84eca8a51a02 100644 --- a/sound/firewire/dice/dice-midi.c +++ b/sound/firewire/dice/dice-midi.c @@ -101,27 +101,18 @@ int snd_dice_create_midi(struct snd_dice *dice) .close = midi_close, .trigger = midi_playback_trigger, }; - __be32 reg; struct snd_rawmidi *rmidi; struct snd_rawmidi_str *str; unsigned int midi_in_ports, midi_out_ports; + int i; int err; - /* - * Use the number of MIDI conformant data channel at current sampling - * transfer frequency. - */ - err = snd_dice_transaction_read_tx(dice, TX_NUMBER_MIDI, - ®, sizeof(reg)); - if (err < 0) - return err; - midi_in_ports = be32_to_cpu(reg); - - err = snd_dice_transaction_read_rx(dice, RX_NUMBER_MIDI, - ®, sizeof(reg)); - if (err < 0) - return err; - midi_out_ports = be32_to_cpu(reg); + midi_in_ports = 0; + midi_out_ports = 0; + for (i = 0; i < MAX_STREAMS; ++i) { + midi_in_ports = max(midi_in_ports, dice->tx_midi_ports[i]); + midi_out_ports = max(midi_out_ports, dice->rx_midi_ports[i]); + } if (midi_in_ports + midi_out_ports == 0) return 0; diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 7cb9e9713ac3..80351b29fe0d 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -9,43 +9,115 @@ #include "dice.h" +static int dice_rate_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_pcm_substream *substream = rule->private; + struct snd_dice *dice = substream->private_data; + unsigned int index = substream->pcm->device; + + 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 *pcm_channels; + enum snd_dice_rate_mode mode; + unsigned int i, rate; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + pcm_channels = dice->tx_pcm_chs[index]; + else + pcm_channels = dice->rx_pcm_chs[index]; + + 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_pcm_substream *substream = rule->private; + struct snd_dice *dice = substream->private_data; + unsigned int index = substream->pcm->device; + + 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 *pcm_channels; + enum snd_dice_rate_mode mode; + unsigned int i, rate; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + pcm_channels = dice->tx_pcm_chs[index]; + else + pcm_channels = dice->rx_pcm_chs[index]; + + 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 limit_channels_and_rates(struct snd_dice *dice, struct snd_pcm_runtime *runtime, enum amdtp_stream_direction dir, - unsigned int index, unsigned int size) + unsigned int index) { struct snd_pcm_hardware *hw = &runtime->hw; - struct amdtp_stream *stream; - unsigned int rate; - __be32 reg; - int err; - - /* - * Retrieve current Multi Bit Linear Audio data channel and limit to - * it. - */ - if (dir == AMDTP_IN_STREAM) { - stream = &dice->tx_stream[index]; - err = snd_dice_transaction_read_tx(dice, - size * index + TX_NUMBER_AUDIO, - ®, sizeof(reg)); - } else { - stream = &dice->rx_stream[index]; - err = snd_dice_transaction_read_rx(dice, - size * index + RX_NUMBER_AUDIO, - ®, sizeof(reg)); + unsigned int *pcm_channels; + unsigned int i; + + if (dir == AMDTP_IN_STREAM) + pcm_channels = dice->tx_pcm_chs[index]; + else + pcm_channels = dice->rx_pcm_chs[index]; + + hw->channels_min = UINT_MAX; + hw->channels_max = 0; + + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { + enum snd_dice_rate_mode mode; + unsigned int rate, channels; + + 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); + + channels = pcm_channels[mode]; + if (channels == 0) + continue; + hw->channels_min = min(hw->channels_min, channels); + hw->channels_max = max(hw->channels_max, channels); } - if (err < 0) - return err; - hw->channels_min = hw->channels_max = be32_to_cpu(reg); - - /* Retrieve current sampling transfer frequency and limit to it. */ - err = snd_dice_transaction_get_rate(dice, &rate); - if (err < 0) - return err; - - hw->rates = snd_pcm_rate_to_rate_bit(rate); snd_pcm_limit_hw_rates(runtime); return 0; @@ -56,36 +128,34 @@ static int init_hw_info(struct snd_dice *dice, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_hardware *hw = &runtime->hw; + unsigned int index = substream->pcm->device; enum amdtp_stream_direction dir; struct amdtp_stream *stream; - __be32 reg[2]; - unsigned int count, size; int err; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { hw->formats = AM824_IN_PCM_FORMAT_BITS; dir = AMDTP_IN_STREAM; - stream = &dice->tx_stream[substream->pcm->device]; - err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, - sizeof(reg)); + stream = &dice->tx_stream[index]; } else { hw->formats = AM824_OUT_PCM_FORMAT_BITS; dir = AMDTP_OUT_STREAM; - stream = &dice->rx_stream[substream->pcm->device]; - err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, - sizeof(reg)); + stream = &dice->rx_stream[index]; } + err = limit_channels_and_rates(dice, substream->runtime, dir, + index); if (err < 0) return err; - count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); - if (substream->pcm->device >= count) - return -ENXIO; - - size = be32_to_cpu(reg[1]) * 4; - err = limit_channels_and_rates(dice, substream->runtime, dir, - substream->pcm->device, size); + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + dice_rate_constraint, substream, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + dice_channels_constraint, substream, + SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) return err; @@ -95,6 +165,8 @@ static int init_hw_info(struct snd_dice *dice, static int pcm_open(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; + unsigned int source; + bool internal; int err; err = snd_dice_stream_lock_try(dice); @@ -105,6 +177,43 @@ static int pcm_open(struct snd_pcm_substream *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[0]) || + amdtp_stream_pcm_running(&dice->tx_stream[1]) || + amdtp_stream_pcm_running(&dice->rx_stream[0]) || + amdtp_stream_pcm_running(&dice->rx_stream[1])) { + unsigned int rate; + + 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; @@ -318,37 +427,19 @@ int snd_dice_create_pcm(struct snd_dice *dice) .page = snd_pcm_lib_get_vmalloc_page, .mmap = snd_pcm_lib_mmap_vmalloc, }; - __be32 reg; struct snd_pcm *pcm; - unsigned int i, max_capture, max_playback, capture, playback; + unsigned int capture, playback; + int i, j; int err; - /* Check whether PCM substreams are required. */ - if (dice->force_two_pcms) { - max_capture = max_playback = 2; - } else { - max_capture = max_playback = 0; - err = snd_dice_transaction_read_tx(dice, TX_NUMBER, ®, - sizeof(reg)); - if (err < 0) - return err; - max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); - - err = snd_dice_transaction_read_rx(dice, RX_NUMBER, ®, - sizeof(reg)); - if (err < 0) - return err; - max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS); - } - for (i = 0; i < MAX_STREAMS; i++) { capture = playback = 0; - if (i < max_capture) - capture = 1; - if (i < max_playback) - playback = 1; - if (capture == 0 && playback == 0) - break; + for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j) { + if (dice->tx_pcm_chs[i][j] > 0) + capture = 1; + if (dice->rx_pcm_chs[i][j] > 0) + playback = 1; + } err = snd_pcm_new(dice->card, "DICE", i, playback, capture, &pcm); diff --git a/sound/firewire/dice/dice-proc.c b/sound/firewire/dice/dice-proc.c index f5c1d1bced59..faf8c854d256 100644 --- a/sound/firewire/dice/dice-proc.c +++ b/sound/firewire/dice/dice-proc.c @@ -148,12 +148,12 @@ static void dice_proc_read(struct snd_info_entry *entry, >> 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, " 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); snd_iprintf(buffer, " clock caps:"); for (i = 0; i <= 6; ++i) if (buf.global.clock_caps & (1 << i)) @@ -243,10 +243,74 @@ static void dice_proc_read(struct snd_info_entry *entry, } } -void snd_dice_create_proc(struct snd_dice *dice) +static void dice_proc_read_formation(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + static const char *const rate_labels[] = { + [SND_DICE_RATE_MODE_LOW] = "low", + [SND_DICE_RATE_MODE_MIDDLE] = "middle", + [SND_DICE_RATE_MODE_HIGH] = "high", + }; + struct snd_dice *dice = entry->private_data; + int i, j; + + snd_iprintf(buffer, "Output stream from unit:\n"); + for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i) + snd_iprintf(buffer, "\t%s", rate_labels[i]); + snd_iprintf(buffer, "\tMIDI\n"); + for (i = 0; i < MAX_STREAMS; ++i) { + snd_iprintf(buffer, "Tx %u:", i); + for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j) + snd_iprintf(buffer, "\t%u", dice->tx_pcm_chs[i][j]); + snd_iprintf(buffer, "\t%u\n", dice->tx_midi_ports[i]); + } + + snd_iprintf(buffer, "Input stream to unit:\n"); + for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i) + snd_iprintf(buffer, "\t%s", rate_labels[i]); + snd_iprintf(buffer, "\n"); + for (i = 0; i < MAX_STREAMS; ++i) { + snd_iprintf(buffer, "Rx %u:", i); + for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j) + snd_iprintf(buffer, "\t%u", dice->rx_pcm_chs[i][j]); + snd_iprintf(buffer, "\t%u\n", dice->rx_midi_ports[i]); + } +} + +static void add_node(struct snd_dice *dice, struct snd_info_entry *root, + const char *name, + void (*op)(struct snd_info_entry *entry, + struct snd_info_buffer *buffer)) { struct snd_info_entry *entry; - if (!snd_card_proc_new(dice->card, "dice", &entry)) - snd_info_set_text_ops(entry, dice, dice_proc_read); + entry = snd_info_create_card_entry(dice->card, name, root); + if (!entry) + return; + + snd_info_set_text_ops(entry, dice, op); + if (snd_info_register(entry) < 0) + snd_info_free_entry(entry); +} + +void snd_dice_create_proc(struct snd_dice *dice) +{ + struct snd_info_entry *root; + + /* + * All nodes are automatically removed at snd_card_disconnect(), + * by following to link list. + */ + root = snd_info_create_card_entry(dice->card, "firewire", + dice->card->proc_root); + if (!root) + return; + root->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(root) < 0) { + snd_info_free_entry(root); + return; + } + + add_node(dice, root, "dice", dice_proc_read); + add_node(dice, root, "formation", dice_proc_read_formation); } diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 928a255bfc35..c3c892c5c7ff 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -30,13 +30,43 @@ const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { [6] = 192000, }; +int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, + enum snd_dice_rate_mode *mode) +{ + /* Corresponding to each entry in snd_dice_rates. */ + static const enum snd_dice_rate_mode modes[] = { + [0] = SND_DICE_RATE_MODE_LOW, + [1] = SND_DICE_RATE_MODE_LOW, + [2] = SND_DICE_RATE_MODE_LOW, + [3] = SND_DICE_RATE_MODE_MIDDLE, + [4] = SND_DICE_RATE_MODE_MIDDLE, + [5] = SND_DICE_RATE_MODE_HIGH, + [6] = SND_DICE_RATE_MODE_HIGH, + }; + 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) + continue; + + *mode = modes[i]; + return 0; + } + + return -EINVAL; +} + /* * This operation has an effect to synchronize GLOBAL_STATUS/GLOBAL_SAMPLE_RATE * to GLOBAL_STATUS. Especially, just after powering on, these are different. */ -static int ensure_phase_lock(struct snd_dice *dice) +static int ensure_phase_lock(struct snd_dice *dice, unsigned int rate) { __be32 reg, nominal; + u32 data; + int i; int err; err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT, @@ -44,9 +74,21 @@ static int ensure_phase_lock(struct snd_dice *dice) if (err < 0) return err; + data = be32_to_cpu(reg); + + data &= ~CLOCK_RATE_MASK; + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { + if (snd_dice_rates[i] == rate) + break; + } + if (i == ARRAY_SIZE(snd_dice_rates)) + return -EINVAL; + data |= i << CLOCK_RATE_SHIFT; + if (completion_done(&dice->clock_accepted)) reinit_completion(&dice->clock_accepted); + reg = cpu_to_be32(data); err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT, ®, sizeof(reg)); if (err < 0) @@ -192,6 +234,7 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, unsigned int rate, struct reg_params *params) { __be32 reg[2]; + enum snd_dice_rate_mode mode; unsigned int i, pcm_chs, midi_ports; struct amdtp_stream *streams; struct fw_iso_resources *resources; @@ -206,12 +249,23 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, resources = dice->rx_resources; } + err = snd_dice_stream_get_rate_mode(dice, rate, &mode); + if (err < 0) + return err; + for (i = 0; i < params->count; i++) { + unsigned int pcm_cache; + unsigned int midi_cache; + if (dir == AMDTP_IN_STREAM) { + pcm_cache = dice->tx_pcm_chs[i][mode]; + midi_cache = dice->tx_midi_ports[i]; err = snd_dice_transaction_read_tx(dice, params->size * i + TX_NUMBER_AUDIO, reg, sizeof(reg)); } else { + pcm_cache = dice->rx_pcm_chs[i][mode]; + midi_cache = dice->rx_midi_ports[i]; err = snd_dice_transaction_read_rx(dice, params->size * i + RX_NUMBER_AUDIO, reg, sizeof(reg)); @@ -221,6 +275,14 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, pcm_chs = be32_to_cpu(reg[0]); midi_ports = be32_to_cpu(reg[1]); + /* These are important for developer of this driver. */ + if (pcm_chs != pcm_cache || midi_ports != midi_cache) { + dev_info(&dice->unit->device, + "cache mismatch: pcm: %u:%u, midi: %u:%u\n", + pcm_chs, pcm_cache, midi_ports, midi_cache); + return -EPROTO; + } + err = keep_resources(dice, dir, i, rate, pcm_chs, midi_ports); if (err < 0) return err; @@ -256,6 +318,68 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, return err; } +static int start_duplex_streams(struct snd_dice *dice, unsigned int rate) +{ + struct reg_params tx_params, rx_params; + int i; + int err; + + err = get_register_params(dice, &tx_params, &rx_params); + if (err < 0) + return err; + + /* Stop transmission. */ + stop_streams(dice, AMDTP_IN_STREAM, &tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); + snd_dice_transaction_clear_enable(dice); + release_resources(dice); + + err = ensure_phase_lock(dice, rate); + if (err < 0) { + dev_err(&dice->unit->device, "fail to ensure phase lock\n"); + return err; + } + + /* Likely to have changed stream formats. */ + err = get_register_params(dice, &tx_params, &rx_params); + if (err < 0) + return err; + + /* Start both streams. */ + err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params); + if (err < 0) + goto error; + err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params); + if (err < 0) + goto error; + + err = snd_dice_transaction_set_enable(dice); + if (err < 0) { + dev_err(&dice->unit->device, "fail to enable interface\n"); + goto error; + } + + for (i = 0; i < MAX_STREAMS; i++) { + if ((i < tx_params.count && + !amdtp_stream_wait_callback(&dice->tx_stream[i], + CALLBACK_TIMEOUT)) || + (i < rx_params.count && + !amdtp_stream_wait_callback(&dice->rx_stream[i], + CALLBACK_TIMEOUT))) { + err = -ETIMEDOUT; + goto error; + } + } + + return 0; +error: + stop_streams(dice, AMDTP_IN_STREAM, &tx_params); + stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); + snd_dice_transaction_clear_enable(dice); + release_resources(dice); + return err; +} + /* * MEMO: After this function, there're two states of streams: * - None streams are running. @@ -265,17 +389,13 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) { unsigned int curr_rate; unsigned int i; - struct reg_params tx_params, rx_params; - bool need_to_start; + enum snd_dice_rate_mode mode; int err; if (dice->substreams_counter == 0) return -EIO; - err = get_register_params(dice, &tx_params, &rx_params); - if (err < 0) - return err; - + /* Check sampling transmission frequency. */ err = snd_dice_transaction_get_rate(dice, &curr_rate); if (err < 0) { dev_err(&dice->unit->device, @@ -285,72 +405,36 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) if (rate == 0) rate = curr_rate; if (rate != curr_rate) - return -EINVAL; + goto restart; - /* Judge to need to restart streams. */ - for (i = 0; i < MAX_STREAMS; i++) { - if (i < tx_params.count) { - if (amdtp_streaming_error(&dice->tx_stream[i]) || - !amdtp_stream_running(&dice->tx_stream[i])) - break; - } - if (i < rx_params.count) { - if (amdtp_streaming_error(&dice->rx_stream[i]) || - !amdtp_stream_running(&dice->rx_stream[i])) - break; - } + /* Check error of packet streaming. */ + for (i = 0; i < MAX_STREAMS; ++i) { + if (amdtp_streaming_error(&dice->tx_stream[i])) + break; + if (amdtp_streaming_error(&dice->rx_stream[i])) + break; } - need_to_start = (i < MAX_STREAMS); - - if (need_to_start) { - /* Stop transmission. */ - snd_dice_transaction_clear_enable(dice); - stop_streams(dice, AMDTP_IN_STREAM, &tx_params); - stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); - release_resources(dice); - - err = ensure_phase_lock(dice); - if (err < 0) { - dev_err(&dice->unit->device, - "fail to ensure phase lock\n"); - return err; - } + if (i < MAX_STREAMS) + goto restart; - /* Start both streams. */ - err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params); - if (err < 0) - goto error; - err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params); - if (err < 0) - goto error; - - err = snd_dice_transaction_set_enable(dice); - if (err < 0) { - dev_err(&dice->unit->device, - "fail to enable interface\n"); - goto error; - } - - for (i = 0; i < MAX_STREAMS; i++) { - if ((i < tx_params.count && - !amdtp_stream_wait_callback(&dice->tx_stream[i], - CALLBACK_TIMEOUT)) || - (i < rx_params.count && - !amdtp_stream_wait_callback(&dice->rx_stream[i], - CALLBACK_TIMEOUT))) { - err = -ETIMEDOUT; - goto error; - } - } + /* Check required streams are running or not. */ + err = snd_dice_stream_get_rate_mode(dice, rate, &mode); + if (err < 0) + return err; + for (i = 0; i < MAX_STREAMS; ++i) { + if (dice->tx_pcm_chs[i][mode] > 0 && + !amdtp_stream_running(&dice->tx_stream[i])) + break; + if (dice->rx_pcm_chs[i][mode] > 0 && + !amdtp_stream_running(&dice->rx_stream[i])) + break; } + if (i < MAX_STREAMS) + goto restart; - return err; -error: - snd_dice_transaction_clear_enable(dice); - stop_streams(dice, AMDTP_IN_STREAM, &tx_params); - stop_streams(dice, AMDTP_OUT_STREAM, &rx_params); - release_resources(dice); - return err; + return 0; +restart: + return start_duplex_streams(dice, rate); } /* @@ -484,6 +568,69 @@ void snd_dice_stream_update_duplex(struct snd_dice *dice) } } +int snd_dice_stream_detect_current_formats(struct snd_dice *dice) +{ + unsigned int rate; + enum snd_dice_rate_mode mode; + __be32 reg[2]; + struct reg_params tx_params, rx_params; + int i; + int err; + + /* If extended protocol is available, detect detail spec. */ + err = snd_dice_detect_extension_formats(dice); + if (err >= 0) + return err; + + /* + * Available stream format is restricted at current mode of sampling + * clock. + */ + err = snd_dice_transaction_get_rate(dice, &rate); + if (err < 0) + return err; + + err = snd_dice_stream_get_rate_mode(dice, rate, &mode); + if (err < 0) + return err; + + /* + * Just after owning the unit (GLOBAL_OWNER), the unit can return + * invalid stream formats. Selecting clock parameters have an effect + * for the unit to refine it. + */ + err = ensure_phase_lock(dice, rate); + if (err < 0) + return err; + + err = get_register_params(dice, &tx_params, &rx_params); + if (err < 0) + return err; + + for (i = 0; i < tx_params.count; ++i) { + err = snd_dice_transaction_read_tx(dice, + tx_params.size * i + TX_NUMBER_AUDIO, + reg, sizeof(reg)); + if (err < 0) + return err; + dice->tx_pcm_chs[i][mode] = be32_to_cpu(reg[0]); + dice->tx_midi_ports[i] = max_t(unsigned int, + be32_to_cpu(reg[1]), dice->tx_midi_ports[i]); + } + for (i = 0; i < rx_params.count; ++i) { + err = snd_dice_transaction_read_rx(dice, + rx_params.size * i + RX_NUMBER_AUDIO, + reg, sizeof(reg)); + if (err < 0) + return err; + dice->rx_pcm_chs[i][mode] = be32_to_cpu(reg[0]); + dice->rx_midi_ports[i] = max_t(unsigned int, + be32_to_cpu(reg[1]), dice->rx_midi_ports[i]); + } + + return 0; +} + static void dice_lock_changed(struct snd_dice *dice) { dice->dev_lock_changed = true; diff --git a/sound/firewire/dice/dice-tcelectronic.c b/sound/firewire/dice/dice-tcelectronic.c new file mode 100644 index 000000000000..af8203b9d1a6 --- /dev/null +++ b/sound/firewire/dice/dice-tcelectronic.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dice-tc_electronic.c - a part of driver for DICE based devices + * + * Copyright (c) 2018 Takashi Sakamoto + */ + +#include "dice.h" + +struct dice_tc_spec { + unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT]; + unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT]; + bool has_midi; +}; + +static const struct dice_tc_spec desktop_konnekt6 = { + .tx_pcm_chs = {{6, 6, 2}, {0, 0, 0} }, + .rx_pcm_chs = {{6, 6, 4}, {0, 0, 0} }, + .has_midi = false, +}; + +static const struct dice_tc_spec impact_twin = { + .tx_pcm_chs = {{14, 10, 6}, {0, 0, 0} }, + .rx_pcm_chs = {{14, 10, 6}, {0, 0, 0} }, + .has_midi = true, +}; + +static const struct dice_tc_spec konnekt_8 = { + .tx_pcm_chs = {{4, 4, 3}, {0, 0, 0} }, + .rx_pcm_chs = {{4, 4, 3}, {0, 0, 0} }, + .has_midi = true, +}; + +static const struct dice_tc_spec konnekt_24d = { + .tx_pcm_chs = {{16, 16, 6}, {0, 0, 0} }, + .rx_pcm_chs = {{16, 16, 6}, {0, 0, 0} }, + .has_midi = true, +}; + +static const struct dice_tc_spec konnekt_live = { + .tx_pcm_chs = {{16, 16, 16}, {0, 0, 0} }, + .rx_pcm_chs = {{16, 16, 16}, {0, 0, 0} }, + .has_midi = true, +}; + +static const struct dice_tc_spec studio_konnekt_48 = { + .tx_pcm_chs = {{16, 16, 16}, {16, 16, 0} }, + .rx_pcm_chs = {{16, 16, 16}, {16, 16, 0} }, + .has_midi = true, +}; + +int snd_dice_detect_tcelectronic_formats(struct snd_dice *dice) +{ + static const struct { + u32 model_id; + const struct dice_tc_spec *spec; + } *entry, entries[] = { + {0x00000020, &konnekt_24d}, + {0x00000021, &konnekt_8}, + {0x00000022, &studio_konnekt_48}, + {0x00000023, &konnekt_live}, + {0x00000024, &desktop_konnekt6}, + {0x00000027, &impact_twin}, + }; + struct fw_csr_iterator it; + int key, val, model_id; + int i; + + model_id = 0; + fw_csr_iterator_init(&it, dice->unit->directory); + while (fw_csr_iterator_next(&it, &key, &val)) { + if (key == CSR_MODEL) { + model_id = val; + break; + } + } + + entry = NULL; + for (i = 0; i < ARRAY_SIZE(entries); ++i) { + entry = entries + i; + if (entry->model_id == model_id) + break; + } + if (!entry) + return -ENODEV; + + memcpy(dice->tx_pcm_chs, entry->spec->tx_pcm_chs, + MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int)); + memcpy(dice->rx_pcm_chs, entry->spec->rx_pcm_chs, + MAX_STREAMS * SND_DICE_RATE_MODE_COUNT * sizeof(unsigned int)); + + for (i = 0; i < MAX_STREAMS; ++i) { + if (entry->spec->has_midi) { + dice->tx_midi_ports[i] = 1; + dice->rx_midi_ports[i] = 1; + } + } + + return 0; +} diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c index 0f0350320ae8..b7e138b5abcf 100644 --- a/sound/firewire/dice/dice-transaction.c +++ b/sound/firewire/dice/dice-transaction.c @@ -265,7 +265,7 @@ int snd_dice_transaction_reinit(struct snd_dice *dice) static int get_subaddrs(struct snd_dice *dice) { static const int min_values[10] = { - 10, 0x64 / 4, + 10, 0x60 / 4, 10, 0x18 / 4, 10, 0x18 / 4, 0, 0, @@ -301,33 +301,40 @@ static int get_subaddrs(struct snd_dice *dice) } } - /* - * Check that the implemented DICE driver specification major version - * number matches. - */ - err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, - DICE_PRIVATE_SPACE + - be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION, - &version, sizeof(version), 0); - if (err < 0) - goto end; + if (be32_to_cpu(pointers[1]) > 0x18) { + /* + * Check that the implemented DICE driver specification major + * version number matches. + */ + err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, + DICE_PRIVATE_SPACE + + be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION, + &version, sizeof(version), 0); + if (err < 0) + goto end; - if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) { - dev_err(&dice->unit->device, - "unknown DICE version: 0x%08x\n", be32_to_cpu(version)); - err = -ENODEV; - goto end; + if ((version & cpu_to_be32(0xff000000)) != + cpu_to_be32(0x01000000)) { + dev_err(&dice->unit->device, + "unknown DICE version: 0x%08x\n", + be32_to_cpu(version)); + err = -ENODEV; + goto end; + } + + /* Set up later. */ + dice->clock_caps = 1; } 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; + /* Old firmware doesn't support these fields. */ + if (pointers[7]) + dice->sync_offset = be32_to_cpu(pointers[6]) * 4; + if (pointers[9]) + dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4; end: kfree(pointers); return err; diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c index 96bb01b6b751..40f7a32e4893 100644 --- a/sound/firewire/dice/dice.c +++ b/sound/firewire/dice/dice.c @@ -15,40 +15,14 @@ MODULE_LICENSE("GPL v2"); #define OUI_LOUD 0x000ff2 #define OUI_FOCUSRITE 0x00130e #define OUI_TCELECTRONIC 0x000166 +#define OUI_ALESIS 0x000595 +#define OUI_MAUDIO 0x000d6c #define DICE_CATEGORY_ID 0x04 #define WEISS_CATEGORY_ID 0x00 #define LOUD_CATEGORY_ID 0x10 -/* - * Some models support several isochronous channels, while these streams are not - * always available. In this case, add the model name to this list. - */ -static bool force_two_pcm_support(struct fw_unit *unit) -{ - static const char *const models[] = { - /* TC Electronic models. */ - "StudioKonnekt48", - /* Focusrite models. */ - "SAFFIRE_PRO_40", - "LIQUID_SAFFIRE_56", - "SAFFIRE_PRO_40_1", - }; - char model[32]; - unsigned int i; - int err; - - err = fw_csr_string(unit->directory, CSR_MODEL, model, sizeof(model)); - if (err < 0) - return false; - - for (i = 0; i < ARRAY_SIZE(models); i++) { - if (strcmp(models[i], model) == 0) - break; - } - - return i < ARRAY_SIZE(models); -} +#define MODEL_ALESIS_IO_BOTH 0x000001 static int check_dice_category(struct fw_unit *unit) { @@ -75,11 +49,6 @@ static int check_dice_category(struct fw_unit *unit) } } - if (vendor == OUI_FOCUSRITE || vendor == OUI_TCELECTRONIC) { - if (force_two_pcm_support(unit)) - return 0; - } - if (vendor == OUI_WEISS) category = WEISS_CATEGORY_ID; else if (vendor == OUI_LOUD) @@ -186,9 +155,6 @@ static void do_registration(struct work_struct *work) if (err < 0) return; - if (force_two_pcm_support(dice->unit)) - dice->force_two_pcms = true; - err = snd_dice_transaction_init(dice); if (err < 0) goto error; @@ -199,6 +165,10 @@ static void do_registration(struct work_struct *work) dice_card_strings(dice); + err = dice->detect_formats(dice); + if (err < 0) + goto error; + err = snd_dice_stream_init_duplex(dice); if (err < 0) goto error; @@ -239,14 +209,17 @@ error: "Sound card registration failed: %d\n", err); } -static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) +static int dice_probe(struct fw_unit *unit, + const struct ieee1394_device_id *entry) { struct snd_dice *dice; int err; - err = check_dice_category(unit); - if (err < 0) - return -ENODEV; + if (!entry->driver_data) { + err = check_dice_category(unit); + if (err < 0) + return -ENODEV; + } /* Allocate this independent of sound card instance. */ dice = kzalloc(sizeof(struct snd_dice), GFP_KERNEL); @@ -256,6 +229,13 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) dice->unit = fw_unit_get(unit); dev_set_drvdata(&unit->device, dice); + if (!entry->driver_data) { + dice->detect_formats = snd_dice_stream_detect_current_formats; + } else { + dice->detect_formats = + (snd_dice_detect_formats_t)entry->driver_data; + } + spin_lock_init(&dice->lock); mutex_init(&dice->mutex); init_completion(&dice->clock_accepted); @@ -313,16 +293,81 @@ static void dice_bus_reset(struct fw_unit *unit) #define DICE_INTERFACE 0x000001 static const struct ieee1394_device_id dice_id_table[] = { + /* M-Audio Profire 2626 has a different value in version field. */ { - .match_flags = IEEE1394_MATCH_VERSION, - .version = DICE_INTERFACE, + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_MAUDIO, + .model_id = 0x000010, + .driver_data = (kernel_ulong_t)snd_dice_detect_extension_formats, + }, + /* M-Audio Profire 610 has a different value in version field. */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_MAUDIO, + .model_id = 0x000011, + .driver_data = (kernel_ulong_t)snd_dice_detect_extension_formats, + }, + /* TC Electronic Konnekt 24D. */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_TCELECTRONIC, + .model_id = 0x000020, + .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + }, + /* TC Electronic Konnekt 8. */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_TCELECTRONIC, + .model_id = 0x000021, + .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + }, + /* TC Electronic Studio Konnekt 48. */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_TCELECTRONIC, + .model_id = 0x000022, + .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, }, - /* M-Audio Profire 610/2626 has a different value in version field. */ + /* TC Electronic Konnekt Live. */ { .match_flags = IEEE1394_MATCH_VENDOR_ID | - IEEE1394_MATCH_SPECIFIER_ID, - .vendor_id = 0x000d6c, - .specifier_id = 0x000d6c, + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_TCELECTRONIC, + .model_id = 0x000023, + .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + }, + /* TC Electronic Desktop Konnekt 6. */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_TCELECTRONIC, + .model_id = 0x000024, + .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + }, + /* TC Electronic Impact Twin. */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_TCELECTRONIC, + .model_id = 0x000027, + .driver_data = (kernel_ulong_t)snd_dice_detect_tcelectronic_formats, + }, + /* Alesis iO14/iO26. */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_ALESIS, + .model_id = MODEL_ALESIS_IO_BOTH, + .driver_data = (kernel_ulong_t)snd_dice_detect_alesis_formats, + }, + { + .match_flags = IEEE1394_MATCH_VERSION, + .version = DICE_INTERFACE, }, { } }; diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index da00e75e09d4..505b79fea6d9 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -63,6 +63,16 @@ */ #define MAX_STREAMS 2 +enum snd_dice_rate_mode { + SND_DICE_RATE_MODE_LOW = 0, + SND_DICE_RATE_MODE_MIDDLE, + SND_DICE_RATE_MODE_HIGH, + SND_DICE_RATE_MODE_COUNT, +}; + +struct snd_dice; +typedef int (*snd_dice_detect_formats_t)(struct snd_dice *dice); + struct snd_dice { struct snd_card *card; struct fw_unit *unit; @@ -80,6 +90,11 @@ struct snd_dice { unsigned int rsrv_offset; unsigned int clock_caps; + unsigned int tx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT]; + unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT]; + unsigned int tx_midi_ports[MAX_STREAMS]; + unsigned int rx_midi_ports[MAX_STREAMS]; + snd_dice_detect_formats_t detect_formats; struct fw_address_handler notification_handler; int owner_generation; @@ -98,8 +113,6 @@ struct snd_dice { bool global_enabled; struct completion clock_accepted; unsigned int substreams_counter; - - bool force_two_pcms; }; enum snd_dice_addr_type { @@ -190,11 +203,14 @@ 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, + enum snd_dice_rate_mode *mode); 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_detect_current_formats(struct snd_dice *dice); int snd_dice_stream_lock_try(struct snd_dice *dice); void snd_dice_stream_lock_release(struct snd_dice *dice); @@ -207,4 +223,8 @@ void snd_dice_create_proc(struct snd_dice *dice); int snd_dice_create_midi(struct snd_dice *dice); +int snd_dice_detect_tcelectronic_formats(struct snd_dice *dice); +int snd_dice_detect_alesis_formats(struct snd_dice *dice); +int snd_dice_detect_extension_formats(struct snd_dice *dice); + #endif diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index 46092fa3ff9b..3919e186a30b 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -569,18 +569,20 @@ static int isight_create_mixer(struct isight *isight) return err; isight->gain_max = be32_to_cpu(value); - isight->gain_tlv[0] = SNDRV_CTL_TLVT_DB_MINMAX; - isight->gain_tlv[1] = 2 * sizeof(unsigned int); + isight->gain_tlv[SNDRV_CTL_TLVO_TYPE] = SNDRV_CTL_TLVT_DB_MINMAX; + isight->gain_tlv[SNDRV_CTL_TLVO_LEN] = 2 * sizeof(unsigned int); err = reg_read(isight, REG_GAIN_DB_START, &value); if (err < 0) return err; - isight->gain_tlv[2] = (s32)be32_to_cpu(value) * 100; + isight->gain_tlv[SNDRV_CTL_TLVO_DB_MINMAX_MIN] = + (s32)be32_to_cpu(value) * 100; err = reg_read(isight, REG_GAIN_DB_END, &value); if (err < 0) return err; - isight->gain_tlv[3] = (s32)be32_to_cpu(value) * 100; + isight->gain_tlv[SNDRV_CTL_TLVO_DB_MINMAX_MAX] = + (s32)be32_to_cpu(value) * 100; ctl = snd_ctl_new1(&gain_control, isight); if (ctl) |