From 1f93a7dfd1f8d5ff7a5c53246c7534fe2332d6f4 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 11 Dec 2023 02:46:07 +0000 Subject: [PATCH] add audio --- kernel/sound/firewire/digi00x/digi00x-stream.c | 292 ++++++++++++++++++++++++++++++++------------------------- 1 files changed, 163 insertions(+), 129 deletions(-) diff --git a/kernel/sound/firewire/digi00x/digi00x-stream.c b/kernel/sound/firewire/digi00x/digi00x-stream.c index 4d3b4eb..405d690 100644 --- a/kernel/sound/firewire/digi00x/digi00x-stream.c +++ b/kernel/sound/firewire/digi00x/digi00x-stream.c @@ -1,9 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family * * Copyright (c) 2014-2015 Takashi Sakamoto - * - * Licensed under the terms of the GNU General Public License, version 2. */ #include "digi00x.h" @@ -125,11 +124,22 @@ static void finish_session(struct snd_dg00x *dg00x) { - __be32 data = cpu_to_be32(0x00000003); + __be32 data; + data = cpu_to_be32(0x00000003); snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET, &data, sizeof(data), 0); + + // Unregister isochronous channels for both direction. + data = 0; + snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, + DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS, + &data, sizeof(data), 0); + + // Just after finishing the session, the device may lost transmitting + // functionality for a short time. + msleep(50); } static int begin_session(struct snd_dg00x *dg00x) @@ -138,11 +148,20 @@ u32 curr; int err; + // Register isochronous channels for both direction. + data = cpu_to_be32((dg00x->tx_resources.channel << 16) | + dg00x->rx_resources.channel); + err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, + DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS, + &data, sizeof(data), 0); + if (err < 0) + return err; + err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST, DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE, &data, sizeof(data), 0); if (err < 0) - goto error; + return err; curr = be32_to_cpu(data); if (curr == 0) @@ -157,39 +176,23 @@ DG00X_OFFSET_STREAMING_SET, &data, sizeof(data), 0); if (err < 0) - goto error; + break; msleep(20); curr--; } - return 0; -error: - finish_session(dg00x); return err; } -static void release_resources(struct snd_dg00x *dg00x) +static int keep_resources(struct snd_dg00x *dg00x, struct amdtp_stream *stream, + unsigned int rate) { - __be32 data = 0; - - /* Unregister isochronous channels for both direction. */ - snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, - DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS, - &data, sizeof(data), 0); - - /* Release isochronous resources. */ - fw_iso_resources_free(&dg00x->tx_resources); - fw_iso_resources_free(&dg00x->rx_resources); -} - -static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate) -{ - unsigned int i; - __be32 data; + struct fw_iso_resources *resources; + int i; int err; - /* Check sampling rate. */ + // Check sampling rate. for (i = 0; i < SND_DG00X_RATE_COUNT; i++) { if (snd_dg00x_stream_rates[i] == rate) break; @@ -197,66 +200,74 @@ if (i == SND_DG00X_RATE_COUNT) return -EINVAL; - /* Keep resources for out-stream. */ - err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate, + if (stream == &dg00x->tx_stream) + resources = &dg00x->tx_resources; + else + resources = &dg00x->rx_resources; + + err = amdtp_dot_set_parameters(stream, rate, snd_dg00x_stream_pcm_channels[i]); if (err < 0) return err; - err = fw_iso_resources_allocate(&dg00x->rx_resources, - amdtp_stream_get_max_payload(&dg00x->rx_stream), + + return fw_iso_resources_allocate(resources, + amdtp_stream_get_max_payload(stream), fw_parent_device(dg00x->unit)->max_speed); +} + +static int init_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s) +{ + struct fw_iso_resources *resources; + enum amdtp_stream_direction dir; + int err; + + if (s == &dg00x->tx_stream) { + resources = &dg00x->tx_resources; + dir = AMDTP_IN_STREAM; + } else { + resources = &dg00x->rx_resources; + dir = AMDTP_OUT_STREAM; + } + + err = fw_iso_resources_init(resources, dg00x->unit); if (err < 0) return err; - /* Keep resources for in-stream. */ - err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate, - snd_dg00x_stream_pcm_channels[i]); + err = amdtp_dot_init(s, dg00x->unit, dir); if (err < 0) - return err; - err = fw_iso_resources_allocate(&dg00x->tx_resources, - amdtp_stream_get_max_payload(&dg00x->tx_stream), - fw_parent_device(dg00x->unit)->max_speed); - if (err < 0) - goto error; + fw_iso_resources_destroy(resources); - /* Register isochronous channels for both direction. */ - data = cpu_to_be32((dg00x->tx_resources.channel << 16) | - dg00x->rx_resources.channel); - err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST, - DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS, - &data, sizeof(data), 0); - if (err < 0) - goto error; - - return 0; -error: - release_resources(dg00x); return err; +} + +static void destroy_stream(struct snd_dg00x *dg00x, struct amdtp_stream *s) +{ + amdtp_stream_destroy(s); + + if (s == &dg00x->tx_stream) + fw_iso_resources_destroy(&dg00x->tx_resources); + else + fw_iso_resources_destroy(&dg00x->rx_resources); } int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x) { int err; - /* For out-stream. */ - err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit); + err = init_stream(dg00x, &dg00x->rx_stream); if (err < 0) - goto error; - err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM); - if (err < 0) - goto error; + return err; - /* For in-stream. */ - err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit); + err = init_stream(dg00x, &dg00x->tx_stream); if (err < 0) - goto error; - err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM); - if (err < 0) - goto error; + destroy_stream(dg00x, &dg00x->rx_stream); - return 0; -error: - snd_dg00x_stream_destroy_duplex(dg00x); + err = amdtp_domain_init(&dg00x->domain); + if (err < 0) { + destroy_stream(dg00x, &dg00x->rx_stream); + destroy_stream(dg00x, &dg00x->tx_stream); + } + return err; } @@ -266,35 +277,81 @@ */ void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x) { - amdtp_stream_destroy(&dg00x->rx_stream); - fw_iso_resources_destroy(&dg00x->rx_resources); + amdtp_domain_destroy(&dg00x->domain); - amdtp_stream_destroy(&dg00x->tx_stream); - fw_iso_resources_destroy(&dg00x->tx_resources); + destroy_stream(dg00x, &dg00x->rx_stream); + destroy_stream(dg00x, &dg00x->tx_stream); } -int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate) +int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer) { unsigned int curr_rate; + int err; + + err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate); + if (err < 0) + return err; + if (rate == 0) + rate = curr_rate; + + if (dg00x->substreams_counter == 0 || curr_rate != rate) { + amdtp_domain_stop(&dg00x->domain); + + finish_session(dg00x); + + fw_iso_resources_free(&dg00x->tx_resources); + fw_iso_resources_free(&dg00x->rx_resources); + + err = snd_dg00x_stream_set_local_rate(dg00x, rate); + if (err < 0) + return err; + + err = keep_resources(dg00x, &dg00x->rx_stream, rate); + if (err < 0) + return err; + + err = keep_resources(dg00x, &dg00x->tx_stream, rate); + if (err < 0) { + fw_iso_resources_free(&dg00x->rx_resources); + return err; + } + + err = amdtp_domain_set_events_per_period(&dg00x->domain, + frames_per_period, frames_per_buffer); + if (err < 0) { + fw_iso_resources_free(&dg00x->rx_resources); + fw_iso_resources_free(&dg00x->tx_resources); + return err; + } + } + + return 0; +} + +int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x) +{ + unsigned int generation = dg00x->rx_resources.generation; int err = 0; if (dg00x->substreams_counter == 0) - goto end; + return 0; - /* Check current sampling rate. */ - err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate); - if (err < 0) - goto error; - if (rate == 0) - rate = curr_rate; - if (curr_rate != rate || - amdtp_streaming_error(&dg00x->tx_stream) || + if (amdtp_streaming_error(&dg00x->tx_stream) || amdtp_streaming_error(&dg00x->rx_stream)) { + amdtp_domain_stop(&dg00x->domain); finish_session(dg00x); + } - amdtp_stream_stop(&dg00x->tx_stream); - amdtp_stream_stop(&dg00x->rx_stream); - release_resources(dg00x); + if (generation != fw_parent_device(dg00x->unit)->card->generation) { + err = fw_iso_resources_update(&dg00x->tx_resources); + if (err < 0) + goto error; + + err = fw_iso_resources_update(&dg00x->rx_resources); + if (err < 0) + goto error; } /* @@ -302,75 +359,52 @@ * which source of clock is used. */ if (!amdtp_stream_running(&dg00x->rx_stream)) { - err = snd_dg00x_stream_set_local_rate(dg00x, rate); - if (err < 0) - goto error; - - err = keep_resources(dg00x, rate); - if (err < 0) - goto error; + int spd = fw_parent_device(dg00x->unit)->max_speed; err = begin_session(dg00x); if (err < 0) goto error; - err = amdtp_stream_start(&dg00x->rx_stream, - dg00x->rx_resources.channel, - fw_parent_device(dg00x->unit)->max_speed); + err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->rx_stream, + dg00x->rx_resources.channel, spd); + if (err < 0) + goto error; + + err = amdtp_domain_add_stream(&dg00x->domain, &dg00x->tx_stream, + dg00x->tx_resources.channel, spd); + if (err < 0) + goto error; + + err = amdtp_domain_start(&dg00x->domain, 0); if (err < 0) goto error; if (!amdtp_stream_wait_callback(&dg00x->rx_stream, - CALLBACK_TIMEOUT)) { + CALLBACK_TIMEOUT) || + !amdtp_stream_wait_callback(&dg00x->tx_stream, + CALLBACK_TIMEOUT)) { err = -ETIMEDOUT; goto error; } } - /* - * The value of SYT field in transmitted packets is always 0x0000. Thus, - * duplex streams with timestamp synchronization cannot be built. - */ - if (!amdtp_stream_running(&dg00x->tx_stream)) { - err = amdtp_stream_start(&dg00x->tx_stream, - dg00x->tx_resources.channel, - fw_parent_device(dg00x->unit)->max_speed); - if (err < 0) - goto error; - - if (!amdtp_stream_wait_callback(&dg00x->tx_stream, - CALLBACK_TIMEOUT)) { - err = -ETIMEDOUT; - goto error; - } - } -end: - return err; + return 0; error: + amdtp_domain_stop(&dg00x->domain); finish_session(dg00x); - - amdtp_stream_stop(&dg00x->tx_stream); - amdtp_stream_stop(&dg00x->rx_stream); - release_resources(dg00x); return err; } void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x) { - if (dg00x->substreams_counter > 0) - return; + if (dg00x->substreams_counter == 0) { + amdtp_domain_stop(&dg00x->domain); + finish_session(dg00x); - amdtp_stream_stop(&dg00x->tx_stream); - amdtp_stream_stop(&dg00x->rx_stream); - finish_session(dg00x); - release_resources(dg00x); - - /* - * Just after finishing the session, the device may lost transmitting - * functionality for a short time. - */ - msleep(50); + fw_iso_resources_free(&dg00x->tx_resources); + fw_iso_resources_free(&dg00x->rx_resources); + } } void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x) -- Gitblit v1.6.2