From b60152f750ca22ddee20954228d1bcbf45c936f7 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 2 May 2018 19:16:42 +0900 Subject: ALSA: dice: cache stream formats at current mode of sampling transmission frequency In former commits, proxy structure get members for cache of stream formats. This commit fills the cache with stream formats at current mode of sampling transmission frequency. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-stream.c | 86 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) (limited to 'sound/firewire/dice/dice-stream.c') diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 8573289c381e..6c859d2b9084 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -30,6 +30,34 @@ 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. @@ -484,6 +512,64 @@ 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; + + /* + * 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); + 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; -- cgit v1.2.2 From 58579c056c1c9510ae6695ed8e01ee05bbdcfb23 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 2 May 2018 19:16:45 +0900 Subject: ALSA: dice: use extended protocol to detect available stream formats TC Applied Technologies (TCAT) have added extension to DICE protocol. This protocol extension is called as Extended Application Protocol, a.k.a. EAP. In this protocol extension, units get additional 9 address spaces. One of it is for current configuration. In this address space, a pair of router and stream formats are exposed per mode of three sampling transmission frequencies. This commit adds support the protocol extension for address space of the current configuration to generate cache of stream formats. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-stream.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound/firewire/dice/dice-stream.c') diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index 6c859d2b9084..d3fb460bb86c 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -521,6 +521,11 @@ int snd_dice_stream_detect_current_formats(struct snd_dice *dice) 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. -- cgit v1.2.2 From ec592fd32e102ec91d7c61c901093ce17878ccb7 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 2 May 2018 19:16:46 +0900 Subject: ALSA: dice: use cache of stream format to check running stream At present, to check running stream, available stream formats are used at current sampling transmission frequency (stf). But when changing stf, it's convenient to use cache of stream formats. This commit applies this idea. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-stream.c | 50 ++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 19 deletions(-) (limited to 'sound/firewire/dice/dice-stream.c') diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index d3fb460bb86c..b792e7bb6b14 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -294,16 +294,14 @@ 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; + bool need_to_start = false; + 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, @@ -315,22 +313,36 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) if (rate != curr_rate) return -EINVAL; - /* 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; + } + if (i < MAX_STREAMS) + need_to_start = true; + + /* 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; } - need_to_start = (i < MAX_STREAMS); + if (i < MAX_STREAMS) + need_to_start = true; if (need_to_start) { + err = get_register_params(dice, &tx_params, &rx_params); + if (err < 0) + return err; + /* Stop transmission. */ snd_dice_transaction_clear_enable(dice); stop_streams(dice, AMDTP_IN_STREAM, &tx_params); @@ -341,7 +353,7 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) if (err < 0) { dev_err(&dice->unit->device, "fail to ensure phase lock\n"); - return err; + goto error; } /* Start both streams. */ -- cgit v1.2.2 From 20b94544792d2eb4e03b17c50300b8b7ae441826 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 2 May 2018 19:16:47 +0900 Subject: ALSA: dice: add a helper function to restart all of available streams This commit is a small refactoring for better readability. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-stream.c | 119 ++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 57 deletions(-) (limited to 'sound/firewire/dice/dice-stream.c') diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index b792e7bb6b14..e93ceff8ce39 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -284,6 +284,63 @@ 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); + if (err < 0) { + dev_err(&dice->unit->device, "fail to ensure phase lock\n"); + 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. @@ -293,8 +350,6 @@ 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 = false; enum snd_dice_rate_mode mode; int err; @@ -321,7 +376,7 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) break; } if (i < MAX_STREAMS) - need_to_start = true; + goto restart; /* Check required streams are running or not. */ err = snd_dice_stream_get_rate_mode(dice, rate, &mode); @@ -336,61 +391,11 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) break; } if (i < MAX_STREAMS) - need_to_start = true; - - if (need_to_start) { - err = get_register_params(dice, &tx_params, &rx_params); - if (err < 0) - return err; - - /* 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); + goto restart; - err = ensure_phase_lock(dice); - if (err < 0) { - dev_err(&dice->unit->device, - "fail to ensure phase lock\n"); - goto error; - } - - /* 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 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); } /* -- cgit v1.2.2 From afa617f219761473881ace59f31bd6713fed1833 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 2 May 2018 19:16:48 +0900 Subject: ALSA: dice: enable to change current sampling transmission frequency This is a preparation for userspace applications to change current sampling transmission frequency via ALSA PCM interface. Signed-off-by: Takashi Sakamoto Signed-off-by: Takashi Iwai --- sound/firewire/dice/dice-stream.c | 47 +++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) (limited to 'sound/firewire/dice/dice-stream.c') diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index e93ceff8ce39..422a1ab1ddf5 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -62,9 +62,11 @@ int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, * 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, @@ -72,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) @@ -220,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; @@ -234,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)); @@ -249,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; @@ -300,12 +334,17 @@ static int start_duplex_streams(struct snd_dice *dice, unsigned int rate) snd_dice_transaction_clear_enable(dice); release_resources(dice); - err = ensure_phase_lock(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) @@ -366,7 +405,7 @@ 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; /* Check error of packet streaming. */ for (i = 0; i < MAX_STREAMS; ++i) { @@ -560,7 +599,7 @@ int snd_dice_stream_detect_current_formats(struct snd_dice *dice) * invalid stream formats. Selecting clock parameters have an effect * for the unit to refine it. */ - err = ensure_phase_lock(dice); + err = ensure_phase_lock(dice, rate); if (err < 0) return err; -- cgit v1.2.2