From e8c1c9d6bff1de4fc4cbb0b82ebc458914a7564e Mon Sep 17 00:00:00 2001 From: Andrea Bondavalli Date: Tue, 7 Jul 2020 22:21:12 +0200 Subject: [PATCH 1/8] Fix wrong file extension of ravenna-alsa-lkm-independent-playback-capture patch file --- ...path => ravenna-alsa-lkm-independent-playback-capture.patch} | 0 build.sh | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename 3rdparty/patches/{ravenna-alsa-lkm-independent-playback-capture.path => ravenna-alsa-lkm-independent-playback-capture.patch} (100%) diff --git a/3rdparty/patches/ravenna-alsa-lkm-independent-playback-capture.path b/3rdparty/patches/ravenna-alsa-lkm-independent-playback-capture.patch similarity index 100% rename from 3rdparty/patches/ravenna-alsa-lkm-independent-playback-capture.path rename to 3rdparty/patches/ravenna-alsa-lkm-independent-playback-capture.patch diff --git a/build.sh b/build.sh index 0df7101..e04f25c 100755 --- a/build.sh +++ b/build.sh @@ -19,7 +19,7 @@ if [ ! -d ravenna-alsa-lkm.git ]; then git apply ../patches/ravenna-alsa-lkm-arm-32bit.patch git apply ../patches/ravenna-alsa-lkm-add-codec-am824.patch git apply ../patches/ravenna-alsa-lkm-disable-ptp-checksum.patch - git apply ../patches/ravenna-alsa-lkm-independent-playback-capture.path + git apply ../patches/ravenna-alsa-lkm-independent-playback-capture.patch echo "Building ravenna-alsa-lkm kernel module ..." cd driver make From af01d7d2f809792296b19d184a5aa0382ed2d94a Mon Sep 17 00:00:00 2001 From: Andrea Bondavalli Date: Thu, 9 Jul 2020 22:03:56 +0200 Subject: [PATCH 2/8] Minor changes to devices documentation --- DEVICES.md | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/DEVICES.md b/DEVICES.md index a8c5d64..5e0dd55 100644 --- a/DEVICES.md +++ b/DEVICES.md @@ -6,8 +6,8 @@ Before starting make sure that the [AES67 daemon basic setup](#daemon_setup) is The following devices have been tested: -* AVIOUSB Dante receiver, see [Dante receivers](#dante_avio_receiver) -* AVIOUSB and AVIOAI2 Dante trasmitters, see [Dante transmitters](#dante_avio_transmitter) +* Audinate AVIOUSB Dante receiver, see [Dante receivers](#dante_avio_receiver) +* Audinate AVIOUSB and AVIOAI2 Dante trasmitters, see [Dante transmitters](#dante_avio_transmitter) * [Hasseb audio over Ethernet receiver](#hasseb_receiver) @@ -16,8 +16,9 @@ The following devices have been tested: Before running any interoperability test configure the AES67 daemon with the following instructions: * open the daemon configuration file *daemon.conf* and change the following parameters: - * set network interface name to your Ethernet card, e.g.: *"interface\_name": "eth0"* + * set network interface name to your Ethernet card: *"interface\_name": "eth0"* * set default sample rate to 48Khz: *"sample\_rate": 48000* + * set TIC frame size @1FS (samples) to 48 samples: *"tic\_frame\_size\_at\_1fs": 48* * verify that PulseAdio is not running. See [PulseAudio](README.md#notes). * install the ALSA RAVENNA/AES67 module with: @@ -27,26 +28,29 @@ Before running any interoperability test configure the AES67 daemon with the fol aes67-daemon -c daemon.conf -* open the Daemon WebUi *http://[address:8080]* and do the following: - * go to Config tab and verify that the sample rate is set to 48KHz - * go to Sources tab and add a new Source using the plus button, set Codec to L24 and press the Submit button +* open the Daemon WebUI *http://[address:8080]* and do the following: + * go to Config tab and verify that the sample rate is set to 48KHz and the TIC frame size @1FS is set to 48 samples + * go to Sources tab and add a new Source using the plus button + * set Codec to L24 + * set Max samples per packet to 48 samples + * press the Submit button ## Dante receivers ## -To run interoperability tests using the [Dante Controller](https://www.audinate.com/products/software/dante-controller) and a Dante receiver use the following steps. Tests were done using a Dante AVIOUBS device. +To run interoperability tests with a Dante receiver follow these steps. Tests were done using a Dante AVIOUBS device. * make sure [AES67 daemon basic setup](#daemon_setup) is done -* download and install the Dante controller -* connect the Dante AVIO receiver to the network -* open the Dante Controller application, select the Routing dialog and wait for the AVIO receiver to show up. Make sure this device or another on the network is working as PTP clock master +* download and install the [Dante Controller](https://www.audinate.com/products/software/dante-controller) +* connect the Dante receiver to the network +* open the Dante Controller application, select the Routing dialog and wait for the Dante receiver to show up. Make sure this device or another on the network is acting as PTP clock master * wait for a daemon Source to show up in the Routing dialog -* using the Routing Dialog connect the Daemon channels to the AVIO receiver channels with the desired configuration, prohibition icons should show up +* using the Routing Dialog connect the Daemon channels to the Dante receiver channels with the desired configuration, prohibition icons should show up * go to the daemon WebUI, click on the PTP tab and wait for the "PTP Status" to report "locked" * open a shell on the Linux host and start the playback on the RAVENNA ALSA device. For example to playback a test sound use: speaker-test -D plughw:RAVENNA -r 48000 -c 2 -t sine -* alternatively start a playback with a file in S24_3LE format: +* alternatively start the playback of a file: aplay -D plughw:RAVENNA -r 48000 -c 2 -f S24_3LE test.wav @@ -54,16 +58,16 @@ To run interoperability tests using the [Dante Controller](https://www.audinate. ## Dante transmitters ## -To run interoperability tests using the [Dante Controller](https://www.audinate.com/products/software/dante-controller) and a Dante transmitter use the followings steps. Tests were done using Dante AVIOUBS and AVIOAI2 devices. +To run interoperability tests using a Dante transmitter follow these steps. Tests were done using Dante AVIOUBS and AVIOAI2 devices. * make sure [AES67 daemon basic setup](#daemon_setup) is done -* download and install the Dante controller -* connect the Dante AVIO transmitter to the network -* open the Dante Controller application, select the Routing dialog and wait for the AVIO transmitter to show up. Make sure this device or another on the network is working as PTP clock master +* download and install the [Dante Controller](https://www.audinate.com/products/software/dante-controller) +* connect the Dante transmitter to the network +* open the Dante Controller application, select the Routing dialog and wait for the Dante transmitter to show up. Make sure this device or another on the network is acting as PTP clock master * in the Dante Controller wait for a daemon Source to show up in the Routing dialog -* in the Dante Controller go to Device view and select AVIO transmitter, go to the AES67 Config tab and select "AES67 Mode" to Enabled. Reboot the device if required -* go to the daemon WebUI, click on the Browser tab and wait for the AVIO transmitter to show up as remote SAP source -* on the daemon WebUI select the Sinks tab, click on the plus icon to add a new Sink, mark the "Use SDP" flag and select the AVIO transmitter SAP source +* in the Dante Controller go to Device view and select Dante transmitter, go to the AES67 Config tab and select "AES67 Mode" to Enabled. This may require a reboot of the Dante device +* go to the daemon WebUI, click on the Browser tab and wait for the Dante transmitter to show up as remote SAP source +* on the daemon WebUI select the Sinks tab, click on the plus icon to add a new Sink, mark the "Use SDP" flag and select the Dante transmitter SAP source * open a shell on the Linux host and start the recording on the RAVENA ALSA device. For example: arecord -D plughw:RAVENNA -c 2 -f S24_3LE -r 48000 -t wav sink.wav @@ -73,18 +77,18 @@ To run interoperability tests using the [Dante Controller](https://www.audinate. To run interoperability tests using the [Hasseb audio over Ethernet receiver](http://hasseb.fi/shop2/index.php?route=product/product&product_id=62) follow these steps: * make sure [AES67 daemon basic setup](#daemon_setup) is done +* make sure that the daemon mDNS support is enabled * open the Hasseb WebUI and do the following: * deselect the "PTP slave only" checkbox to enable PTP master on Hasseb device - * wait for the daemon source to show up in the "Stream name" drop down list and select it. In this case mDNS support must be enabled on the daemon + * wait for the daemon source to show up in the "Stream name" drop down list and select it * press the Submit button * go to the daemon WebUI, click on the PTP tab and wait for the "PTP Status" to report "locked" * open a shell on the Linux host and start the playback on the RAVENNA ALSA device. For example to playback a test sound use: speaker-test -D plughw:RAVENNA -r 48000 -c 2 -t sine -* alternatively start a playback with a file in S24_3LE format: +* alternatively start the playback of a file: aplay -D plughw:RAVENNA -r 48000 -c 2 -f S24_3LE test.wav - From fe08f3c3bd550c0d9b89ee2cbb6cec4d9ca918e0 Mon Sep 17 00:00:00 2001 From: Andrea Bondavalli Date: Mon, 20 Jul 2020 16:51:30 +0200 Subject: [PATCH 3/8] Fix to independent playback and capture patch to stop triggering PCM capture interrupts when a capture is not running and PCM playback interrupts when a playback is not running. This was causing a possible kernel hang while stopping a capture with an ongoing playback or vice versa. --- ...enna-alsa-lkm-independent-playback-capture.patch | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/3rdparty/patches/ravenna-alsa-lkm-independent-playback-capture.patch b/3rdparty/patches/ravenna-alsa-lkm-independent-playback-capture.patch index dc8f094..09d212c 100644 --- a/3rdparty/patches/ravenna-alsa-lkm-independent-playback-capture.patch +++ b/3rdparty/patches/ravenna-alsa-lkm-independent-playback-capture.patch @@ -169,6 +169,19 @@ index 25b77dc..ec62624 100644 break; } case MT_ALSA_Msg_SetSampleRate: +@@ -1468,8 +1502,10 @@ void AudioFrameTIC(void* user) + frame_process_begin(&self->m_RTP_streams_manager); + if(self->m_pALSAChip && self->m_alsa_driver_frontend) + { +- self->m_alsa_driver_frontend->pcm_interrupt(self->m_pALSAChip, 1); +- self->m_alsa_driver_frontend->pcm_interrupt(self->m_pALSAChip, 0); ++ if (self->m_bIsRecordingIO) ++ self->m_alsa_driver_frontend->pcm_interrupt(self->m_pALSAChip, 1); ++ if (self->m_bIsPlaybackIO) ++ self->m_alsa_driver_frontend->pcm_interrupt(self->m_pALSAChip, 0); + } + frame_process_end(&self->m_RTP_streams_manager); + #endif @@ -1680,20 +1714,25 @@ int get_interrupts_frame_size(void* user, uint32_t *framesize) return -EINVAL; } From 7d6a8289d5de4e929d7481f648a9bfc8dfb26a0a Mon Sep 17 00:00:00 2001 From: Andrea Bondavalli Date: Wed, 29 Jul 2020 21:21:58 +0200 Subject: [PATCH 4/8] Fix to RtspClient::process() method to avoid ending up into an infinite loop in certain cases when the client gets stopped via RtspClient::stop() or RtspClient::stop_all() methods. The bug was also preventing the daemon from terminating. It was possible to reproduce the problem using the add_remove_check_mdns_browser_update_all regression test. --- daemon/rtsp_client.cpp | 82 ++++++++++++++++++++++++------------ daemon/rtsp_client.hpp | 2 +- daemon/tests/daemon_test.cpp | 4 -- 3 files changed, 56 insertions(+), 32 deletions(-) diff --git a/daemon/rtsp_client.cpp b/daemon/rtsp_client.cpp index 9777563..d08b302 100644 --- a/daemon/rtsp_client.cpp +++ b/daemon/rtsp_client.cpp @@ -87,6 +87,39 @@ RtspResponse read_response(tcp::iostream& s, uint16_t max_length) { return res; } +struct RtspActiveClientRemover { + RtspActiveClientRemover() = delete; + RtspActiveClientRemover(ip::tcp::iostream* s, + const std::string& name, + const std::string& domain, + bool wait_for_updates) + : stream_(s), + name_(name), + domain_(domain), + wait_for_updates_(wait_for_updates) { + if (stream_ != nullptr && wait_for_updates_) { + RtspClient::g_mutex.lock(); + RtspClient::g_active_clients[{name_, domain_}] = stream_; + RtspClient::g_mutex.unlock(); + } + } + ~RtspActiveClientRemover() { + if (stream_ != nullptr && wait_for_updates_) { + std::lock_guard lock(RtspClient::g_mutex); + auto it = RtspClient::g_active_clients.find({name_, domain_}); + if (it != RtspClient::g_active_clients.end() && it->second == stream_) { + RtspClient::g_active_clients.erase(it); + } + } + } + + private: + ip::tcp::iostream* stream_{nullptr}; + const std::string& name_; + const std::string& domain_; + bool wait_for_updates_{false}; +}; + std::pair RtspClient::process(RtspClient::Observer callback, const std::string& name, const std::string& domain, @@ -96,11 +129,12 @@ std::pair RtspClient::process(RtspClient::Observer callback, bool wait_for_updates) { RtspSource rtsp_source; ip::tcp::iostream s; + RtspActiveClientRemover clientRemover(&s, name, domain, wait_for_updates); try { BOOST_LOG_TRIVIAL(debug) << "rtsp_client:: connecting to " << "rtsp://" << address << ":" << port << path; s.connect(address, port.length() ? port : dft_port); - if (!s) { + if (!s || s.error()) { BOOST_LOG_TRIVIAL(warning) << "rtsp_client:: unable to connect to " << address << ":" << port; return {false, rtsp_source}; @@ -125,7 +159,7 @@ std::pair RtspClient::process(RtspClient::Observer callback, std::string request; std::getline(s, request); - if (!s || rtsp_version.substr(0, 5) != "RTSP/") { + if (!s || s.error() || rtsp_version.substr(0, 5) != "RTSP/") { BOOST_LOG_TRIVIAL(error) << "rtsp_client:: invalid response from " << "rtsp://" << address << ":" << port << path; return {false, rtsp_source}; @@ -169,6 +203,17 @@ std::pair RtspClient::process(RtspClient::Observer callback, rtsp_source.source = "mDNS"; rtsp_source.address = address; rtsp_source.sdp = std::move(res.body); + + if (is_announce) { + s << "RTSP/1.0 200 OK\r\n"; + s << "CSeq: " << res.cseq << "\r\n"; + s << "\r\n"; + } else if (!is_describe) { + s << "RTSP/1.0 405 Method Not Allowed\r\n"; + s << "CSeq: " << res.cseq << "\r\n"; + s << "\r\n"; + } + BOOST_LOG_TRIVIAL(info) << "rtsp_client:: completed " << "rtsp://" << address << ":" << port << path; @@ -180,28 +225,14 @@ std::pair RtspClient::process(RtspClient::Observer callback, callback(announced_name.empty() ? name : announced_name, domain, rtsp_source); } - - if (is_announce) { - s << "RTSP/1.0 200 OK\r\n"; - s << "CSeq: " << res.cseq << "\r\n"; - s << "\r\n"; - } else if (!is_describe) { - s << "RTSP/1.0 405 Method Not Allowed\r\n"; - s << "CSeq: " << res.cseq << "\r\n"; - s << "\r\n"; - } } if (wait_for_updates) { - g_mutex.lock(); - g_active_clients[{name, domain}] = &s; - g_mutex.unlock(); - /* we start waiting for updates */ do { std::getline(s, request); - } while (request.empty() && !s.error()); - if (s.error()) { + } while (request.empty() && !s.error() && is_active(name, domain)); + if (s.error() || !is_active(name, domain)) { BOOST_LOG_TRIVIAL(info) << "rtsp_client:: end: " << s.error().message(); break; @@ -227,21 +258,13 @@ std::pair RtspClient::process(RtspClient::Observer callback, is_announce = true; } } - } while (wait_for_updates); + } while (wait_for_updates && is_active(name, domain)); } catch (std::exception& e) { BOOST_LOG_TRIVIAL(warning) << "rtsp_client:: error with " << "rtsp://" << address << ":" << port << path << ": " << e.what(); } - if (wait_for_updates) { - std::lock_guard lock(g_mutex); - auto it = g_active_clients.find({name, domain}); - if (it != g_active_clients.end() && it->second == &s) { - g_active_clients.erase(it); - } - } - return {true, rtsp_source}; } @@ -260,6 +283,11 @@ void RtspClient::stop(const std::string& name, const std::string& domain) { } } +bool RtspClient::is_active(const std::string& name, const std::string& domain) { + std::lock_guard lock(g_mutex); + return g_active_clients.find({name, domain}) != g_active_clients.end(); +} + void RtspClient::stop_all() { std::lock_guard lock(g_mutex); auto it = g_active_clients.begin(); diff --git a/daemon/rtsp_client.hpp b/daemon/rtsp_client.hpp index ff36d67..66dd4a1 100644 --- a/daemon/rtsp_client.hpp +++ b/daemon/rtsp_client.hpp @@ -48,9 +48,9 @@ class RtspClient { const std::string& port = dft_port, bool wait_for_updates = true); + static bool is_active(const std::string& name, const std::string& domain); static void stop(const std::string& name, const std::string& domain); static void stop_all(); - static std::pair describe( const std::string& path, const std::string& address, diff --git a/daemon/tests/daemon_test.cpp b/daemon/tests/daemon_test.cpp index accb133..9dd8c18 100644 --- a/daemon/tests/daemon_test.cpp +++ b/daemon/tests/daemon_test.cpp @@ -854,10 +854,6 @@ BOOST_AUTO_TEST_CASE(add_remove_check_mdns_browser_update_all) { BOOST_REQUIRE_MESSAGE(cli.add_source(id), std::string("added source ") + std::to_string(id)); } - for (int id = 0; id < g_stream_num_max; id++) { - BOOST_REQUIRE_MESSAGE(cli.update_source(id), - std::string("updated source ") + std::to_string(id)); - } std::vector sdps{g_stream_num_max}; for (int id = 0; id < g_stream_num_max; id++) { auto sdp = cli.get_source_sdp(id); From b24ba6ef6b15145872e4b19078103fffa45e0fb1 Mon Sep 17 00:00:00 2001 From: Andrea Bondavalli Date: Sun, 2 Aug 2020 15:42:47 +0200 Subject: [PATCH 5/8] Added patch to the RAVENNA ALSA driver to implement direct PCM transfer for the memory map access mode as replacement to indirect PCM transfer. PCM samples are transferred by the PCM interrupt routine from the memory of the ALSA playback device to the RAVENNA buffer and from the RAVENNA buffer to the memory of the ALSA capture device. This patch will enabled the use of the ALSA plugins and it has been testes with the ALSA Dmix plugin. This patch applies to issues #7 and #15. --- ...ravenna-alsa-lkm-direct-pcm-transfer.patch | 435 ++++++++++++++++++ build.sh | 1 + 2 files changed, 436 insertions(+) create mode 100644 3rdparty/patches/ravenna-alsa-lkm-direct-pcm-transfer.patch diff --git a/3rdparty/patches/ravenna-alsa-lkm-direct-pcm-transfer.patch b/3rdparty/patches/ravenna-alsa-lkm-direct-pcm-transfer.patch new file mode 100644 index 0000000..0f3cdf1 --- /dev/null +++ b/3rdparty/patches/ravenna-alsa-lkm-direct-pcm-transfer.patch @@ -0,0 +1,435 @@ +diff --git a/driver/audio_driver.c b/driver/audio_driver.c +index 3d9debd..910ed5b 100644 +--- a/driver/audio_driver.c ++++ b/driver/audio_driver.c +@@ -43,7 +43,7 @@ + #include + #include + #include +-#include // for mmap ++//#include // for mmap + #include + #include + +@@ -121,9 +121,6 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s + int channel, uint32_t pos, + void __user *src, + snd_pcm_uframes_t count); +-static int mr_alsa_audio_pcm_playback_silence( struct snd_pcm_substream *substream, +- int channel, snd_pcm_uframes_t pos, +- snd_pcm_uframes_t count); + + /// "chip" : the main private structure + struct mr_alsa_audio_chip +@@ -177,9 +174,7 @@ struct mr_alsa_audio_chip + struct snd_card *card; /* one card */ + struct snd_pcm *pcm; /* has one pcm */ + +- struct snd_pcm_indirect pcm_playback_indirect; + atomic_t dma_playback_offset; // to be used with atomic_read, atomic_set +- struct snd_pcm_indirect pcm_capture_indirect; + atomic_t dma_capture_offset; // to be used with atomic_read, atomic_set + }; + +@@ -594,7 +589,7 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) + uint32_t ring_buffer_size = MR_ALSA_RINGBUFFER_NB_FRAMES; // init to the max size possible + uint32_t ptp_frame_size; + struct mr_alsa_audio_chip *chip = (struct mr_alsa_audio_chip*)rawchip; +- spin_lock_irq(&chip->lock); ++ spin_lock(&chip->lock); + chip->mr_alsa_audio_ops->get_interrupts_frame_size(chip->ravenna_peer, &ptp_frame_size); + if(direction == 1 && chip->capture_substream != NULL) + { +@@ -614,21 +609,30 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) + unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; + unsigned int pcm_buffer_size = snd_pcm_lib_buffer_bytes(chip->capture_substream); + unsigned int pos; +- uint32_t offset = 0; ++ //uint32_t offset = 0; + // char jitter_buffer_byte_len = 3; + // chip->mr_alsa_audio_ops->get_jitter_buffer_sample_bytelength(chip->ravenna_peer, &jitter_buffer_byte_len); +- + pos = atomic_read(&chip->dma_capture_offset); ++ + pos += ptp_frame_size * bytes_to_frame_factor; + if (pos >= pcm_buffer_size) + { + pos -= pcm_buffer_size; + } + atomic_set(&chip->dma_capture_offset, pos); ++ ++ //printk(KERN_DEBUG "capture copy pos=%u, dma_pos=%u, count=%u, channels=%d pcm_size=%u\n", chip->capture_buffer_pos, pos, ptp_frame_size, runtime->channels, pcm_buffer_size); ++ mr_alsa_audio_pcm_capture_copy_internal(chip->capture_substream, ++ runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ? -1 : runtime->channels/*channel*/, ++ chip->capture_buffer_pos, runtime->dma_area + pos/**src*/, ptp_frame_size, false); + +- chip->mr_alsa_audio_ops->get_input_jitter_buffer_offset(chip->ravenna_peer, &offset); ++ //chip->mr_alsa_audio_ops->get_input_jitter_buffer_offset(chip->ravenna_peer, &offset); + //printk(KERN_DEBUG "Interrupt Capture pos = %u \n", offset); + } ++ ++ chip->capture_buffer_pos += ptp_frame_size; ++ if(chip->capture_buffer_pos >= ring_buffer_size) ++ chip->capture_buffer_pos -= ring_buffer_size; + + /// Ravenna DSD always uses a rate of 352k with eventual zero padding to maintain a 32 bit alignment + /// while DSD in ALSA uses a continuous 8, 16 or 32 bit aligned stream with at 352k, 176k or 88k +@@ -636,7 +640,9 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) + if(++chip->current_capture_interrupt_idx >= chip->nb_capture_interrupts_per_period) + { + chip->current_capture_interrupt_idx = 0; ++ spin_unlock(&chip->lock); + snd_pcm_period_elapsed(chip->capture_substream); ++ spin_lock(&chip->lock); + } + } + else if(direction == 0 && chip->playback_substream != NULL) +@@ -659,6 +665,12 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) + unsigned int pos; + + pos = atomic_read(&chip->dma_playback_offset); ++ ++ //printk(KERN_DEBUG "playback copy pos=%u, dma_pos=%u, count=%u, channels=%d pcm_size=%u\n", chip->playback_buffer_pos, pos, ptp_frame_size, runtime->channels, pcm_buffer_size); ++ mr_alsa_audio_pcm_playback_copy_internal(chip->playback_substream, ++ runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ? -1 : runtime->channels/*channel*/, ++ chip->playback_buffer_pos/*pos*/, runtime->dma_area + pos/*src*/, ptp_frame_size/*count*/); ++ + pos += ptp_frame_size * bytes_to_frame_factor; + if (pos >= pcm_buffer_size) + { +@@ -678,10 +690,12 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) + { + chip->playback_buffer_rav_sac += ptp_frame_size; + chip->current_playback_interrupt_idx = 0; ++ spin_unlock(&chip->lock); + snd_pcm_period_elapsed(chip->playback_substream); ++ spin_lock(&chip->lock); + } + } +- spin_unlock_irq(&chip->lock); ++ spin_unlock(&chip->lock); + return 0; + } + return -1; +@@ -917,15 +931,12 @@ static int mr_alsa_audio_pcm_prepare(struct snd_pcm_substream *substream) + // TODO: snd_pcm_format_set_silence(SNDRV_PCM_FORMAT_S24_3LE, chip->mr_alsa_audio_ops->, ) + + atomic_set(&chip->dma_playback_offset, 0); +- memset(&chip->pcm_playback_indirect, 0, sizeof(chip->pcm_playback_indirect)); +- chip->pcm_playback_indirect.hw_buffer_size = chip->pcm_playback_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); + } + else if(substream->stream == SNDRV_PCM_STREAM_CAPTURE) + { + uint32_t offset = 0; + chip->mr_alsa_audio_ops->get_input_jitter_buffer_offset(chip->ravenna_peer, &offset); + +- + printk(KERN_DEBUG "mr_alsa_audio_pcm_prepare for capture stream\n"); + if(chip->ravenna_peer) + { +@@ -946,9 +957,6 @@ static int mr_alsa_audio_pcm_prepare(struct snd_pcm_substream *substream) + // TODO: snd_pcm_format_set_silence + + atomic_set(&chip->dma_capture_offset, 0); +- memset(&chip->pcm_capture_indirect, 0, sizeof(chip->pcm_capture_indirect)); +- chip->pcm_capture_indirect.hw_buffer_size = snd_pcm_lib_buffer_bytes(substream); +- chip->pcm_capture_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); + } + } + else +@@ -970,6 +978,7 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als + uint32_t offset = 0; + //printk("entering mr_alsa_audio_pcm_pointer (substream name=%s #%d) ...\n", alsa_sub->name, alsa_sub->number); + ++ spin_lock(&chip->lock); + if(alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK) + { + /// DMA case +@@ -977,7 +986,9 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als + alsa_sub->runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED || + alsa_sub->runtime->access == SNDRV_PCM_ACCESS_MMAP_COMPLEX) + { +- offset = snd_pcm_indirect_playback_pointer(alsa_sub, &chip->pcm_playback_indirect, atomic_read(&chip->dma_playback_offset)); ++ struct snd_pcm_runtime *runtime = alsa_sub->runtime; ++ unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; ++ offset = atomic_read(&chip->dma_playback_offset) / bytes_to_frame_factor; + } + else + { +@@ -1010,7 +1021,9 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als + alsa_sub->runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED || + alsa_sub->runtime->access == SNDRV_PCM_ACCESS_MMAP_COMPLEX) + { +- offset = snd_pcm_indirect_capture_pointer(alsa_sub, &chip->pcm_capture_indirect, atomic_read(&chip->dma_capture_offset)); ++ struct snd_pcm_runtime *runtime = alsa_sub->runtime; ++ unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; ++ offset = atomic_read(&chip->dma_capture_offset) / bytes_to_frame_factor; + } + else + { +@@ -1036,6 +1049,7 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als + } + //printk("mr_alsa_audio_pcm_pointer capture offset = %u\n", offset); + } ++ spin_unlock(&chip->lock); + return offset; + } + +@@ -1555,230 +1607,6 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s + return count; + } + +- +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) +-static int mr_alsa_audio_pcm_playback_fill_silence( struct snd_pcm_substream *substream, +- int channel, unsigned long pos, +- unsigned long count) +-{ +- struct snd_pcm_runtime *runtime = substream->runtime; +- bool interleaved = runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ? 1 : 0; +- unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; +- return mr_alsa_audio_pcm_playback_silence(substream, interleaved ? -1 : channel, pos / bytes_to_frame_factor, count / bytes_to_frame_factor); +-} +-#endif +- +-static int mr_alsa_audio_pcm_playback_silence( struct snd_pcm_substream *substream, +- int channel, snd_pcm_uframes_t pos, +- snd_pcm_uframes_t count) +-{ +- struct mr_alsa_audio_chip *chip = snd_pcm_substream_chip(substream); +- struct snd_pcm_runtime *runtime = substream->runtime; +- unsigned char *out; +- int interleaved = ((channel == -1 && runtime->channels > 1)? 1 : 0); +- //unsigned int strideIn = snd_pcm_format_physical_width(runtime->format) >> 3; +- unsigned int strideOut = snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32_LE) >> 3; +- size_t ravBuffer_csize = MR_ALSA_RINGBUFFER_NB_FRAMES * strideOut; +- const unsigned char def_sil_pat[8] = {0,0,0,0,0,0,0,0}; +- const unsigned char *sil_pat = snd_pcm_format_silence_64(runtime->format); +- const uint32_t dsd_pattern = 0x55555555; +- uint32_t dsdrate = mr_alsa_audio_get_dsd_sample_rate(runtime->format, runtime->rate); +- uint32_t dsdmode = (dsdrate > 0? mr_alsa_audio_get_dsd_mode(dsdrate) : 0); +- +- /// Ravenna DSD always uses a rate of 352k with eventual zero padding to maintain a 32 bit alignment +- /// while DSD in ALSA uses a continuous 8, 16 or 32 bit aligned stream with at 352k, 176k or 88k +- /// so respective ring buffers might have different scale and size +- pos *= chip->nb_playback_interrupts_per_period; +- +- printk(KERN_DEBUG "mr_alsa_audio_pcm_playback_silence called for %lu frames at pos %lu\n", count, pos); +- +- if(sil_pat == NULL) +- sil_pat = &def_sil_pat[0]; +- +- if(interleaved) +- { +- /// mute all channels directly in the Ravenna Ring Buffer +- unsigned int samples = count; +- int chn = 0; +- for(chn = 0; chn < runtime->channels; ++chn) +- { +- out = chip->playback_buffer + chn * ravBuffer_csize + pos * strideOut; +- if(dsdmode == 0) +- { +- switch (strideOut) +- { +- case 2: +- while (samples--) { +- memcpy(out, sil_pat, 2); +- out += 2; +- } +- break; +- case 3: +- while (samples--) { +- memcpy(out, sil_pat, 3); +- out += 3; +- } +- break; +- case 4: +- while (samples--) { +- memcpy(out, sil_pat, 4); +- out += 4; +- } +- break; +- } +- } +- else +- { +- uint32_t dsdmute = dsd_pattern; +- switch(dsdmode) +- { +- case 1: ///DSD64 +- dsdmute = (dsd_pattern & 0xFF); +- break; +- case 2: ///DSD128 +- dsdmute = (dsd_pattern & 0xFFFF); +- break; +- } +- while (samples--) +- { +- memcpy(out, &dsdmute, strideOut); +- out += strideOut; +- } +- } +- } +- } +- else +- { +- /// mute the specified channel in the Ravenna Ring Buffer +- unsigned int samples = count; +- out = chip->playback_buffer + channel * ravBuffer_csize + pos * strideOut; +- if(dsdmode == 0) +- { +- switch (strideOut) +- { +- case 2: +- while (samples--) { +- memcpy(out, sil_pat, 2); +- out += 2; +- } +- break; +- case 3: +- while (samples--) { +- memcpy(out, sil_pat, 3); +- out += 3; +- } +- break; +- case 4: +- while (samples--) { +- memcpy(out, sil_pat, 4); +- out += 4; +- } +- break; +- } +- } +- else +- { +- uint32_t dsdmute = dsd_pattern; +- switch(dsdmode) +- { +- case 1: ///DSD64 +- dsdmute = (dsd_pattern & 0xFF); +- break; +- case 2: ///DSD128 +- dsdmute = (dsd_pattern & 0xFFFF); +- break; +- } +- while (samples--) +- { +- memcpy(out, &dsdmute, strideOut); +- out += strideOut; +- } +- } +- } +- return count; +-} +- +-static void mr_alsa_audio_pcm_capture_ack_transfer(struct snd_pcm_substream *substream, struct snd_pcm_indirect *rec, size_t bytes) +-{ +- struct snd_pcm_runtime *runtime = substream->runtime; +- struct mr_alsa_audio_chip *chip = snd_pcm_substream_chip(substream); +- unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; +- uint32_t ring_buffer_size = MR_ALSA_RINGBUFFER_NB_FRAMES; // init to the max size possible +- uint32_t pos = chip->capture_buffer_pos; +- +- char jitter_buffer_byte_len = 3; +- chip->mr_alsa_audio_ops->get_jitter_buffer_sample_bytelength(chip->ravenna_peer, &jitter_buffer_byte_len); +- +- ring_buffer_size = chip->current_dsd ? MR_ALSA_RINGBUFFER_NB_FRAMES : runtime->period_size * runtime->periods; +- +- //printk(KERN_DEBUG "Transfer Capture pos = %u, size = %zu (ring_buffer_size = %u, bytes_to_frame_factor = %zu, jitter_buffer_byte_len = %d)\n", pos, bytes, ring_buffer_size, bytes_to_frame_factor, jitter_buffer_byte_len); +- +- chip->capture_buffer_pos += bytes / bytes_to_frame_factor; +- if (chip->capture_buffer_pos >= ring_buffer_size) +- { +- // unsigned long end_bytes = ring_buffer_size - pos; +- // unsigned long start_bytes = bytes - end_bytes; +- +- // mr_alsa_audio_pcm_capture_copy_internal(chip->capture_substream, +- // runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ? -1 : runtime->channels/*channel*/, +- // pos, runtime->dma_area + rec->sw_data/**src*/, (end_bytes * jitter_buffer_byte_len) / bytes_to_frame_factor); +- +- // mr_alsa_audio_pcm_capture_copy_internal(chip->capture_substream, +- // runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ? -1 : runtime->channels/*channel*/, +- // 0, runtime->dma_area + rec->sw_data + end_bytes, (start_bytes * jitter_buffer_byte_len) / bytes_to_frame_factor); +- +- // memset(runtime->dma_area + rec->sw_data, 0x00, bytes); +- +- chip->capture_buffer_pos -= ring_buffer_size; +- if (chip->capture_buffer_pos != 0) +- printk(KERN_WARNING "Capture tranfer buffer wrapping to implement"); +- } +- //else +- { +- mr_alsa_audio_pcm_capture_copy_internal(chip->capture_substream, +- runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ? -1 : runtime->channels/*channel*/, +- pos, runtime->dma_area + rec->sw_data/**src*/, bytes / bytes_to_frame_factor, false); +- } +-} +- +-static void mr_alsa_audio_pcm_playback_ack_transfer(struct snd_pcm_substream *substream, struct snd_pcm_indirect *rec, size_t bytes) +-{ +- struct snd_pcm_runtime *runtime = substream->runtime; +- struct mr_alsa_audio_chip *chip = snd_pcm_substream_chip(substream); +- unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; +- +- mr_alsa_audio_pcm_playback_copy_internal(chip->playback_substream, +- runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ? -1 : runtime->channels/*channel*/, +- chip->playback_buffer_pos/*pos*/, runtime->dma_area + rec->sw_data/**src*/, bytes / bytes_to_frame_factor/*count*/); +-} +- +-static int mr_alsa_audio_pcm_ack(struct snd_pcm_substream *substream) +-{ +- struct mr_alsa_audio_chip *chip = snd_pcm_substream_chip(substream); +- +- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) +- { +- struct snd_pcm_indirect *pcm_indirect = &chip->pcm_playback_indirect; +- #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0) +- return snd_pcm_indirect_playback_transfer(substream, pcm_indirect, mr_alsa_audio_pcm_playback_ack_transfer); +- #else +- snd_pcm_indirect_playback_transfer(substream, pcm_indirect, mr_alsa_audio_pcm_playback_ack_transfer); +- return 0; +- #endif +- } +- else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) +- { +- struct snd_pcm_indirect *pcm_indirect = &chip->pcm_capture_indirect; +- #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,2,0) +- return snd_pcm_indirect_capture_transfer(substream, pcm_indirect, mr_alsa_audio_pcm_capture_ack_transfer); +- #else +- snd_pcm_indirect_capture_transfer(substream, pcm_indirect, mr_alsa_audio_pcm_capture_ack_transfer); +- return 0; +- #endif +- } +- return 0; +-} +- + /// hw_params callback + /// This is called when the hardware parameter (hw_params) is set up by the application, that is, once when + /// the buffer size, the period size, the format, etc. are defined for the pcm substream. +@@ -2340,13 +2168,13 @@ static struct snd_pcm_ops mr_alsa_audio_pcm_playback_ops = { + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) + .copy_user = mr_alsa_audio_pcm_playback_copy_user, + //.copy_kernel = mr_alsa_audio_pcm_playback_copy, +- .fill_silence = mr_alsa_audio_pcm_playback_fill_silence, ++ //.fill_silence = mr_alsa_audio_pcm_playback_fill_silence, + #else + .copy = mr_alsa_audio_pcm_playback_copy, +- .silence = mr_alsa_audio_pcm_playback_silence, ++ //.silence = mr_alsa_audio_pcm_playback_silence, + #endif + .page = snd_pcm_lib_get_vmalloc_page, +- .ack = mr_alsa_audio_pcm_ack, ++ //.ack = mr_alsa_audio_pcm_ack, + }; + + ///////////////////////////////////////////////////////////////////////////////////// +@@ -2368,7 +2196,7 @@ static struct snd_pcm_ops mr_alsa_audio_pcm_capture_ops = { + .silence = NULL, //mr_alsa_audio_pcm_silence, + #endif + .page = snd_pcm_lib_get_vmalloc_page, +- .ack = mr_alsa_audio_pcm_ack, ++ //.ack = mr_alsa_audio_pcm_ack, + }; + + + diff --git a/build.sh b/build.sh index e04f25c..2bbdc11 100755 --- a/build.sh +++ b/build.sh @@ -20,6 +20,7 @@ if [ ! -d ravenna-alsa-lkm.git ]; then git apply ../patches/ravenna-alsa-lkm-add-codec-am824.patch git apply ../patches/ravenna-alsa-lkm-disable-ptp-checksum.patch git apply ../patches/ravenna-alsa-lkm-independent-playback-capture.patch + git apply ../patches/ravenna-alsa-lkm-direct-pcm-transfer.patch echo "Building ravenna-alsa-lkm kernel module ..." cd driver make From 2a2d53628405ba9ec2084dcbe823200cf643f8f5 Mon Sep 17 00:00:00 2001 From: Andrea Bondavalli Date: Sun, 2 Aug 2020 15:54:02 +0200 Subject: [PATCH 6/8] Change in RAVENNA ALSA driver to avoid channels consistency check when operating in mmap interleaved mode --- 3rdparty/patches/ravenna-alsa-lkm-fixes.patch | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/3rdparty/patches/ravenna-alsa-lkm-fixes.patch b/3rdparty/patches/ravenna-alsa-lkm-fixes.patch index 6f50158..0b482cd 100644 --- a/3rdparty/patches/ravenna-alsa-lkm-fixes.patch +++ b/3rdparty/patches/ravenna-alsa-lkm-fixes.patch @@ -84,16 +84,7 @@ diff --git a/driver/audio_driver.c b/driver/audio_driver.c index 3d9debd..0ff2dfc 100644 --- a/driver/audio_driver.c +++ b/driver/audio_driver.c -@@ -1312,7 +1312,7 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s - - if (channel > 0 && channel >= runtime->channels) - { -- printk(KERN_WARNING "Channel %d copy ignored because it does not fit the available runtime channels (%d)", channel, runtime->channels); -+ //printk(KERN_WARNING "Channel %d copy ignored because it does not fit the available runtime channels (%d)", channel, runtime->channels); - return 0; - } - -@@ -1292,8 +1290,7 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s +@@ -1292,8 +1292,7 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s struct mr_alsa_audio_chip *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; int chn = 0; @@ -103,4 +94,12 @@ index 3d9debd..0ff2dfc 100644 unsigned int nb_logical_bits = snd_pcm_format_width(runtime->format); unsigned int strideIn = snd_pcm_format_physical_width(runtime->format) >> 3; unsigned int strideOut = snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32_LE) >> 3; - +@@ -1310,7 +1309,7 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s + memset(testblock, 0, sizeof(testblock)); + #endif + +- if (channel > 0 && channel >= runtime->channels) ++ if (!interleaved && channel >= runtime->channels) + { + printk(KERN_WARNING "Channel %d copy ignored because it does not fit the available runtime channels (%d)", channel, runtime->channels); + return 0; From 7c42e52dcaef7c7c2dd7d086e95d2fcf581afd15 Mon Sep 17 00:00:00 2001 From: Andrea Bondavalli Date: Thu, 27 Aug 2020 21:11:41 +0200 Subject: [PATCH 7/8] Set of chnages to ALSA Ravenna driver to fix the playback with memory mapped access mode on ARM platform. Issue is in the mr_alsa_audio_pcm_playback_copy_internal() function of audio_driver.c that transfers the PCM samples from the ALSA buffer to the Ravenna buffer using the get_user() function. In case of memory access mode the ALSA buffer can be directly accessed by the kernel and the samples must be copied. mr_alsa_audio_pcm_playback_copy_internal() has been modified can to handle the transfer of the ALSA buffer from user space (used with read/write access mode) and from kernel space (used by memory access mode). The issue is common to all the platform but impacting ARM only. Additional changes: - changed type for dma_playback_offset and dma_capture_offset variables to uint32_t instead of atomic_t since the pointer callback mr_alsa_audio_pcm_pointer() is now atomic - added variables to the mr_alsa_audio_chip scructure to decrease the number of calls to snd_pcm_lib_xxx() functions. These get assigned in the prepare callback mr_alsa_audio_pcm_prepare() - changed mr_alsa_audio_get_playback_buffer_size_in_frames() to return configured size of Ravenna playback buffer (same as mr_alsa_audio_get_capture_buffer_size_in_frames()) - some rework of the existing patches This fixes issue #20wq --- .../patches/ravenna-alsa-lkm-arm-32bit.patch | 22 - ...ravenna-alsa-lkm-direct-pcm-transfer.patch | 683 ++++++++++++++++-- 3rdparty/patches/ravenna-alsa-lkm-fixes.patch | 23 - 3 files changed, 615 insertions(+), 113 deletions(-) diff --git a/3rdparty/patches/ravenna-alsa-lkm-arm-32bit.patch b/3rdparty/patches/ravenna-alsa-lkm-arm-32bit.patch index 699b5a5..8a5fd00 100644 --- a/3rdparty/patches/ravenna-alsa-lkm-arm-32bit.patch +++ b/3rdparty/patches/ravenna-alsa-lkm-arm-32bit.patch @@ -37,28 +37,6 @@ index 5a160e4..a5c84e4 100644 i64DeltaSAC = ui64UsedSAC - ui64GlobalSAC; //MTAL_DP("i64DeltaSAC %I64u playout delay %u, frame size: %u\n", i64DeltaSAC, pRTP_stream_info->m_ui32PlayOutDelay, pManager->get_frame_size(pManager->user)); -diff --git a/driver/audio_driver.c b/driver/audio_driver.c -index a04ec21..cb8b056 100644 ---- a/driver/audio_driver.c -+++ b/driver/audio_driver.c -@@ -1205,8 +1205,8 @@ static int mr_alsa_audio_pcm_capture_copy_internal( struct snd_pcm_substream *s - { - int ret_pu; - char val = 0xf1; -- __put_user_x(1, val, (unsigned long __user *)src, ret_pu); -- ret_pu = put_user(val, (unsigned long __user *)src); -+ //__put_user_x(1, val, (unsigned long __user *)src, ret_pu); -+ ret_pu = put_user(val, (unsigned char __user *)src); - //put_user(val, (unsigned long __user *)src); - switch(nb_logical_bits) - { -@@ -2758,4 +2758,4 @@ void mr_alsa_audio_card_exit(void) - g_ravenna_peer = NULL; - g_mr_alsa_audio_ops = NULL; - printk(KERN_INFO "leaving mr_alsa_audio_card_exit..\n"); --} -\ No newline at end of file -+} diff --git a/driver/manager.c b/driver/manager.c index 5a90eca..8023708 100644 --- a/driver/manager.c diff --git a/3rdparty/patches/ravenna-alsa-lkm-direct-pcm-transfer.patch b/3rdparty/patches/ravenna-alsa-lkm-direct-pcm-transfer.patch index 0f3cdf1..6c694ab 100644 --- a/3rdparty/patches/ravenna-alsa-lkm-direct-pcm-transfer.patch +++ b/3rdparty/patches/ravenna-alsa-lkm-direct-pcm-transfer.patch @@ -1,71 +1,143 @@ diff --git a/driver/audio_driver.c b/driver/audio_driver.c -index 3d9debd..910ed5b 100644 +index 3d9debd..3c3183e 100644 --- a/driver/audio_driver.c +++ b/driver/audio_driver.c -@@ -43,7 +43,7 @@ +@@ -43,7 +43,6 @@ #include #include #include -#include // for mmap -+//#include // for mmap #include #include -@@ -121,9 +121,6 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s +@@ -77,11 +76,6 @@ static int index = SNDRV_DEFAULT_IDX1; /* Index 0-max */ + static char *id = SNDRV_DEFAULT_STR1; /* Id for card */ + static bool enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */ + static int pcm_devs = 1; +-//static int pcm_substreams = 8; // todo +-//#define MUTE_CHECK +-#ifdef MUTE_CHECK +-static bool playback_mute_detected = false; +-#endif + + module_param(index, int, 0444); + MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); +@@ -112,7 +106,8 @@ static int mr_alsa_audio_pcm_capture_copy( struct snd_pcm_substream *substream, + static int mr_alsa_audio_pcm_capture_copy_internal( struct snd_pcm_substream *substream, int channel, uint32_t pos, void __user *src, - snd_pcm_uframes_t count); +- snd_pcm_uframes_t count, bool to_user_space); ++ snd_pcm_uframes_t count, ++ bool to_user_space); + static int mr_alsa_audio_pcm_playback_copy( struct snd_pcm_substream *substream, + int channel, snd_pcm_uframes_t pos, + void __user *src, +@@ -120,10 +115,8 @@ static int mr_alsa_audio_pcm_playback_copy( struct snd_pcm_substream *substream, + static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *substream, + int channel, uint32_t pos, + void __user *src, +- snd_pcm_uframes_t count); -static int mr_alsa_audio_pcm_playback_silence( struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, - snd_pcm_uframes_t count); ++ snd_pcm_uframes_t count, ++ bool from_user_space); /// "chip" : the main private structure struct mr_alsa_audio_chip -@@ -177,9 +174,7 @@ struct mr_alsa_audio_chip +@@ -177,10 +170,14 @@ struct mr_alsa_audio_chip struct snd_card *card; /* one card */ struct snd_pcm *pcm; /* has one pcm */ - struct snd_pcm_indirect pcm_playback_indirect; - atomic_t dma_playback_offset; // to be used with atomic_read, atomic_set +- atomic_t dma_playback_offset; // to be used with atomic_read, atomic_set - struct snd_pcm_indirect pcm_capture_indirect; - atomic_t dma_capture_offset; // to be used with atomic_read, atomic_set +- atomic_t dma_capture_offset; // to be used with atomic_read, atomic_set ++ uint32_t dma_playback_offset; ++ uint32_t dma_capture_offset; ++ ++ unsigned int pcm_playback_buffer_size; ++ unsigned int pcm_capture_buffer_size; ++ ++ uint8_t *dma_playback_buffer; ++ uint8_t *dma_capture_buffer; }; -@@ -594,7 +589,7 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) + +@@ -507,13 +504,28 @@ static void* mr_alsa_audio_get_playback_buffer(void *rawchip) + } + static uint32_t mr_alsa_audio_get_playback_buffer_size_in_frames(void *rawchip) + { +- if(rawchip) ++ uint32_t res = 0; ++ if (rawchip) + { +- struct mr_alsa_audio_chip *chip = (struct mr_alsa_audio_chip*)rawchip; +- if(chip->playback_buffer) +- return MR_ALSA_RINGBUFFER_NB_FRAMES; ++ struct mr_alsa_audio_chip* chip = (struct mr_alsa_audio_chip*)rawchip; ++ //spin_lock_irq(&chip->lock); ++ { ++ struct snd_pcm_runtime* runtime = chip->playback_substream ? chip->playback_substream->runtime : NULL; ++ if (chip->playback_buffer) ++ { ++ if (runtime && runtime->period_size != 0 && runtime->periods != 0) ++ { ++ res = chip->current_dsd ? MR_ALSA_RINGBUFFER_NB_FRAMES : runtime->period_size * runtime->periods; ++ } ++ else ++ { ++ res = MR_ALSA_RINGBUFFER_NB_FRAMES; ++ } ++ } ++ } ++ //spin_unlock_irq(&chip->lock); + } +- return 0; ++ return res; + + } + static void* mr_alsa_audio_get_capture_buffer(void *rawchip) +@@ -594,7 +606,8 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) uint32_t ring_buffer_size = MR_ALSA_RINGBUFFER_NB_FRAMES; // init to the max size possible uint32_t ptp_frame_size; struct mr_alsa_audio_chip *chip = (struct mr_alsa_audio_chip*)rawchip; - spin_lock_irq(&chip->lock); ++ + spin_lock(&chip->lock); chip->mr_alsa_audio_ops->get_interrupts_frame_size(chip->ravenna_peer, &ptp_frame_size); if(direction == 1 && chip->capture_substream != NULL) { -@@ -614,21 +609,30 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) - unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; - unsigned int pcm_buffer_size = snd_pcm_lib_buffer_bytes(chip->capture_substream); - unsigned int pos; +@@ -611,24 +624,22 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) + runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED || + runtime->access == SNDRV_PCM_ACCESS_MMAP_COMPLEX) + { +- unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; +- unsigned int pcm_buffer_size = snd_pcm_lib_buffer_bytes(chip->capture_substream); +- unsigned int pos; - uint32_t offset = 0; -+ //uint32_t offset = 0; - // char jitter_buffer_byte_len = 3; - // chip->mr_alsa_audio_ops->get_jitter_buffer_sample_bytelength(chip->ravenna_peer, &jitter_buffer_byte_len); +- // char jitter_buffer_byte_len = 3; +- // chip->mr_alsa_audio_ops->get_jitter_buffer_sample_bytelength(chip->ravenna_peer, &jitter_buffer_byte_len); - - pos = atomic_read(&chip->dma_capture_offset); -+ - pos += ptp_frame_size * bytes_to_frame_factor; - if (pos >= pcm_buffer_size) - { - pos -= pcm_buffer_size; - } - atomic_set(&chip->dma_capture_offset, pos); +- pos = atomic_read(&chip->dma_capture_offset); +- pos += ptp_frame_size * bytes_to_frame_factor; +- if (pos >= pcm_buffer_size) ++ unsigned long bytes_to_frame_factor = runtime->channels * chip->current_alsa_capture_stride; + + //printk(KERN_DEBUG "capture copy pos=%u, dma_pos=%u, count=%u, channels=%d pcm_size=%u\n", chip->capture_buffer_pos, pos, ptp_frame_size, runtime->channels, pcm_buffer_size); -+ mr_alsa_audio_pcm_capture_copy_internal(chip->capture_substream, -+ runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ? -1 : runtime->channels/*channel*/, -+ chip->capture_buffer_pos, runtime->dma_area + pos/**src*/, ptp_frame_size, false); - ++ mr_alsa_audio_pcm_capture_copy_internal(chip->capture_substream, runtime->channels/*channel*/, ++ chip->capture_buffer_pos, chip->dma_capture_buffer + chip->dma_capture_offset/**src*/, ptp_frame_size, false); ++ ++ chip->dma_capture_offset += ptp_frame_size * bytes_to_frame_factor; ++ if (chip->dma_capture_offset >= chip->pcm_capture_buffer_size) + { +- pos -= pcm_buffer_size; ++ chip->dma_capture_offset -= chip->pcm_capture_buffer_size; + } +- atomic_set(&chip->dma_capture_offset, pos); +- - chip->mr_alsa_audio_ops->get_input_jitter_buffer_offset(chip->ravenna_peer, &offset); -+ //chip->mr_alsa_audio_ops->get_input_jitter_buffer_offset(chip->ravenna_peer, &offset); - //printk(KERN_DEBUG "Interrupt Capture pos = %u \n", offset); +- //printk(KERN_DEBUG "Interrupt Capture pos = %u \n", offset); } + + chip->capture_buffer_pos += ptp_frame_size; @@ -74,7 +146,7 @@ index 3d9debd..910ed5b 100644 /// Ravenna DSD always uses a rate of 352k with eventual zero padding to maintain a 32 bit alignment /// while DSD in ALSA uses a continuous 8, 16 or 32 bit aligned stream with at 352k, 176k or 88k -@@ -636,7 +640,9 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) +@@ -636,7 +647,9 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) if(++chip->current_capture_interrupt_idx >= chip->nb_capture_interrupts_per_period) { chip->current_capture_interrupt_idx = 0; @@ -84,20 +156,47 @@ index 3d9debd..910ed5b 100644 } } else if(direction == 0 && chip->playback_substream != NULL) -@@ -659,6 +665,12 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) - unsigned int pos; - - pos = atomic_read(&chip->dma_playback_offset); +@@ -648,27 +661,27 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) + printk(KERN_ERR "mr_alsa_audio_pcm_interrupt playback period_size*periods > MR_ALSA_RINGBUFFER_NB_FRAMES\n"); + return -2; + } +- ++ + /// DMA case + if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED || + runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED || + runtime->access == SNDRV_PCM_ACCESS_MMAP_COMPLEX) + { +- unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; +- unsigned int pcm_buffer_size = snd_pcm_lib_buffer_bytes(chip->playback_substream); +- unsigned int pos; +- +- pos = atomic_read(&chip->dma_playback_offset); +- pos += ptp_frame_size * bytes_to_frame_factor; +- if (pos >= pcm_buffer_size) ++ unsigned long bytes_to_frame_factor = runtime->channels * chip->current_alsa_playback_stride; + + //printk(KERN_DEBUG "playback copy pos=%u, dma_pos=%u, count=%u, channels=%d pcm_size=%u\n", chip->playback_buffer_pos, pos, ptp_frame_size, runtime->channels, pcm_buffer_size); -+ mr_alsa_audio_pcm_playback_copy_internal(chip->playback_substream, -+ runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ? -1 : runtime->channels/*channel*/, -+ chip->playback_buffer_pos/*pos*/, runtime->dma_area + pos/*src*/, ptp_frame_size/*count*/); ++ mr_alsa_audio_pcm_playback_copy_internal(chip->playback_substream, runtime->channels/*channel*/, ++ chip->playback_buffer_pos/*pos*/, chip->dma_playback_buffer + chip->dma_playback_offset/*src*/, ptp_frame_size/*count*/, false); + - pos += ptp_frame_size * bytes_to_frame_factor; - if (pos >= pcm_buffer_size) ++ chip->dma_playback_offset += ptp_frame_size * bytes_to_frame_factor; ++ if (chip->dma_playback_offset >= chip->pcm_playback_buffer_size) { -@@ -678,10 +690,12 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) +- pos -= pcm_buffer_size; ++ chip->dma_playback_offset -= chip->pcm_playback_buffer_size; + } +- atomic_set(&chip->dma_playback_offset, pos); + } +- ++ + chip->playback_buffer_pos += ptp_frame_size; +- if(chip->playback_buffer_pos >= ring_buffer_size) ++ if (chip->playback_buffer_pos >= ring_buffer_size) + chip->playback_buffer_pos -= ring_buffer_size; + + /// Ravenna DSD always uses a rate of 352k with eventual zero padding to maintain a 32 bit alignment +@@ -678,10 +691,12 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) { chip->playback_buffer_rav_sac += ptp_frame_size; chip->current_playback_interrupt_idx = 0; @@ -111,12 +210,18 @@ index 3d9debd..910ed5b 100644 return 0; } return -1; -@@ -917,15 +931,12 @@ static int mr_alsa_audio_pcm_prepare(struct snd_pcm_substream *substream) - // TODO: snd_pcm_format_set_silence(SNDRV_PCM_FORMAT_S24_3LE, chip->mr_alsa_audio_ops->, ) +@@ -914,18 +929,15 @@ static int mr_alsa_audio_pcm_prepare(struct snd_pcm_substream *substream) + /// Fill the additional delay between the packet output and the sound eared + chip->mr_alsa_audio_ops->get_playout_delay(chip->ravenna_peer, &runtime->delay); - atomic_set(&chip->dma_playback_offset, 0); +- // TODO: snd_pcm_format_set_silence(SNDRV_PCM_FORMAT_S24_3LE, chip->mr_alsa_audio_ops->, ) +- +- atomic_set(&chip->dma_playback_offset, 0); - memset(&chip->pcm_playback_indirect, 0, sizeof(chip->pcm_playback_indirect)); - chip->pcm_playback_indirect.hw_buffer_size = chip->pcm_playback_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); ++ chip->dma_playback_offset = 0; ++ chip->dma_playback_buffer = runtime->dma_area; ++ chip->pcm_playback_buffer_size = snd_pcm_lib_buffer_bytes(chip->playback_substream); } else if(substream->stream == SNDRV_PCM_STREAM_CAPTURE) { @@ -127,17 +232,23 @@ index 3d9debd..910ed5b 100644 printk(KERN_DEBUG "mr_alsa_audio_pcm_prepare for capture stream\n"); if(chip->ravenna_peer) { -@@ -946,9 +957,6 @@ static int mr_alsa_audio_pcm_prepare(struct snd_pcm_substream *substream) - // TODO: snd_pcm_format_set_silence +@@ -943,12 +955,10 @@ static int mr_alsa_audio_pcm_prepare(struct snd_pcm_substream *substream) + chip->capture_buffer_pos = offset; + chip->current_capture_interrupt_idx = 0; + chip->nb_capture_interrupts_per_period = ((runtime_dsd_mode != 0)? (MR_ALSA_PTP_FRAME_RATE_FOR_DSD / runtime->rate) : 1); +- // TODO: snd_pcm_format_set_silence - atomic_set(&chip->dma_capture_offset, 0); +- atomic_set(&chip->dma_capture_offset, 0); - memset(&chip->pcm_capture_indirect, 0, sizeof(chip->pcm_capture_indirect)); - chip->pcm_capture_indirect.hw_buffer_size = snd_pcm_lib_buffer_bytes(substream); - chip->pcm_capture_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); ++ chip->dma_capture_offset = 0; ++ chip->dma_capture_buffer = runtime->dma_area; ++ chip->pcm_capture_buffer_size = snd_pcm_lib_buffer_bytes(chip->capture_substream); } } else -@@ -970,6 +978,7 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als +@@ -970,6 +980,7 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als uint32_t offset = 0; //printk("entering mr_alsa_audio_pcm_pointer (substream name=%s #%d) ...\n", alsa_sub->name, alsa_sub->number); @@ -145,29 +256,29 @@ index 3d9debd..910ed5b 100644 if(alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK) { /// DMA case -@@ -977,7 +986,9 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als +@@ -977,7 +988,9 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als alsa_sub->runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED || alsa_sub->runtime->access == SNDRV_PCM_ACCESS_MMAP_COMPLEX) { - offset = snd_pcm_indirect_playback_pointer(alsa_sub, &chip->pcm_playback_indirect, atomic_read(&chip->dma_playback_offset)); + struct snd_pcm_runtime *runtime = alsa_sub->runtime; -+ unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; -+ offset = atomic_read(&chip->dma_playback_offset) / bytes_to_frame_factor; ++ unsigned long bytes_to_frame_factor = runtime->channels * chip->current_alsa_playback_stride; ++ offset = chip->dma_playback_offset / bytes_to_frame_factor; } else { -@@ -1010,7 +1021,9 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als +@@ -1010,7 +1023,9 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als alsa_sub->runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED || alsa_sub->runtime->access == SNDRV_PCM_ACCESS_MMAP_COMPLEX) { - offset = snd_pcm_indirect_capture_pointer(alsa_sub, &chip->pcm_capture_indirect, atomic_read(&chip->dma_capture_offset)); + struct snd_pcm_runtime *runtime = alsa_sub->runtime; -+ unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; -+ offset = atomic_read(&chip->dma_capture_offset) / bytes_to_frame_factor; ++ unsigned long bytes_to_frame_factor = runtime->channels * chip->current_alsa_capture_stride; ++ offset = chip->dma_capture_offset / bytes_to_frame_factor; } else { -@@ -1036,6 +1049,7 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als +@@ -1036,6 +1051,7 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als } //printk("mr_alsa_audio_pcm_pointer capture offset = %u\n", offset); } @@ -175,11 +286,438 @@ index 3d9debd..910ed5b 100644 return offset; } -@@ -1555,230 +1607,6 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s +@@ -1152,9 +1168,10 @@ static int mr_alsa_audio_pcm_capture_copy_user( struct snd_pcm_substream *subst + void __user *src, + unsigned long count) + { ++ struct mr_alsa_audio_chip* chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + bool interleaved = runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ? 1 : 0; +- unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; ++ unsigned long bytes_to_frame_factor = runtime->channels * chip->current_alsa_capture_stride; + return mr_alsa_audio_pcm_capture_copy(substream, interleaved ? -1 : channel, pos / bytes_to_frame_factor, src, count / bytes_to_frame_factor); + } + #endif +@@ -1173,13 +1190,14 @@ static int mr_alsa_audio_pcm_capture_copy( struct snd_pcm_substream *substream, + static int mr_alsa_audio_pcm_capture_copy_internal( struct snd_pcm_substream *substream, + int channel, uint32_t pos, + void __user *src, +- snd_pcm_uframes_t count, bool to_user_space) ++ snd_pcm_uframes_t count, ++ bool to_user_space) + { + struct mr_alsa_audio_chip *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; +- int interleaved = ((channel == -1 && runtime->channels > 1)? 1 : 0); ++ bool interleaved = (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); + unsigned int nb_logical_bits = snd_pcm_format_width(runtime->format); +- unsigned int strideIn = snd_pcm_format_physical_width(runtime->format) >> 3; ++ unsigned int strideIn = chip->current_alsa_capture_stride; + uint32_t ravenna_buffer_pos = pos; + + // todo DSD capture +@@ -1204,12 +1222,7 @@ static int mr_alsa_audio_pcm_capture_copy_internal( struct snd_pcm_substream *s + + + if(interleaved) +- { +- int ret_pu; +- char val = 0xf1; +- __put_user_x(1, val, (unsigned long __user *)src, ret_pu); +- ret_pu = put_user(val, (unsigned long __user *)src); +- //put_user(val, (unsigned long __user *)src); ++ { + switch(nb_logical_bits) + { + case 16: +@@ -1244,8 +1257,8 @@ static int mr_alsa_audio_pcm_capture_copy_internal( struct snd_pcm_substream *s + return -EINVAL; + } + return count; +- + } ++ + /// This callback is called whenever the alsa application wants to write data + /// We use it here to do all the de-interleaving, format conversion and DSD re-packing + /// The intermediate buffer is actually the alsa (dma) buffer, allocated in hw_params() +@@ -1256,10 +1269,10 @@ static int mr_alsa_audio_pcm_playback_copy_user( struct snd_pcm_substream *subs + void __user *src, + unsigned long count) + { ++ struct mr_alsa_audio_chip* chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; +- bool interleaved = runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ? 1 : 0; +- unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; +- return mr_alsa_audio_pcm_playback_copy(substream, interleaved ? -1 : channel, pos / bytes_to_frame_factor, src, count / bytes_to_frame_factor); ++ unsigned long bytes_to_frame_factor = runtime->channels * chip->current_alsa_playback_stride; ++ return mr_alsa_audio_pcm_playback_copy(substream, channel, pos / bytes_to_frame_factor, src, count / bytes_to_frame_factor); + } + #endif + +@@ -1278,126 +1291,68 @@ static int mr_alsa_audio_pcm_playback_copy( struct snd_pcm_substream *substream, + /// so respective ring buffers might have different scale and size + uint32_t ravenna_buffer_pos = pos * chip->nb_playback_interrupts_per_period; + +- if(snd_BUG_ON(ravenna_buffer_pos >= MR_ALSA_RINGBUFFER_NB_FRAMES)) +- ravenna_buffer_pos -= MR_ALSA_RINGBUFFER_NB_FRAMES; +- +- return mr_alsa_audio_pcm_playback_copy_internal(substream, channel, ravenna_buffer_pos, src, count); ++ return mr_alsa_audio_pcm_playback_copy_internal(substream, channel, ravenna_buffer_pos, src, count, true); + } +-/// ++ ++ + static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *substream, + int channel, uint32_t pos, + void __user *src, +- snd_pcm_uframes_t count) ++ snd_pcm_uframes_t count, ++ bool from_user_space) + { + struct mr_alsa_audio_chip *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int chn = 0; +- //int interleaved = ((channel == -1 && runtime->channels > 1)? 1 : 0); +- int interleaved = runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ? 1 : 0; ++ bool interleaved = (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); + unsigned int nb_logical_bits = snd_pcm_format_width(runtime->format); +- unsigned int strideIn = snd_pcm_format_physical_width(runtime->format) >> 3; ++ unsigned int strideIn = chip->current_alsa_playback_stride; + unsigned int strideOut = snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32_LE) >> 3; + uint32_t dsdrate = mr_alsa_audio_get_dsd_sample_rate(runtime->format, runtime->rate); + uint32_t dsdmode = (dsdrate > 0? mr_alsa_audio_get_dsd_mode(dsdrate) : 0); + uint32_t ravenna_buffer_pos = pos; +- //uint32_t alsa_ring_buffer_nb_frames = MR_ALSA_RINGBUFFER_NB_FRAMES / chip->nb_playback_interrupts_per_period; +- +- #ifdef MUTE_CHECK +- // mute check +- unsigned char *buffer_to_check = chip->playback_buffer + ravenna_buffer_pos * strideOut; // output buffer channel 0 +- bool mute_detected = false; +- char testblock [256]; +- memset(testblock, 0, sizeof(testblock)); +- #endif +- +- if (channel > 0 && channel >= runtime->channels) +- { +- printk(KERN_WARNING "Channel %d copy ignored because it does not fit the available runtime channels (%d)", channel, runtime->channels); +- return 0; +- } +- +- //printk(KERN_DEBUG "entering mr_alsa_audio_pcm_playback_copy (substream name=%s #%d) (runtime channels %d) access %d...\n", substream->name, substream->number, runtime->channels, runtime->access)); +- +- /*if(snd_BUG_ON(chip->playback_buffer_rav_sac > chip->playback_buffer_alsa_sac)) +- { +- printk(KERN_WARNING "mr_alsa_audio_pcm_playback_copy: Playback stall. Missing playback data from the player application."); +- return -EINVAL; +- } +- +- //printk("playback_copy: initial count = %u, alsa_ring_buffer_nb_frames = %u \n", count, alsa_ring_buffer_nb_frames); +- if(alsa_ring_buffer_nb_frames < chip->playback_buffer_alsa_sac - chip->playback_buffer_rav_sac) +- { +- count = 0; /// no room for more playback at the moment +- printk(KERN_WARNING "playback_copy: no room at the moment (count =%lu) \n", count); +- } +- +- if(count > alsa_ring_buffer_nb_frames - (chip->playback_buffer_alsa_sac - chip->playback_buffer_rav_sac)) +- { +- snd_pcm_uframes_t new_count = (snd_pcm_uframes_t)(alsa_ring_buffer_nb_frames - (chip->playback_buffer_alsa_sac - chip->playback_buffer_rav_sac)); +- printk(KERN_WARNING "playback_copy count overflow 1: change count from %lu to %lu\n", count, new_count); +- count = new_count; +- } +- if(count * chip->nb_playback_interrupts_per_period + ravenna_buffer_pos > MR_ALSA_RINGBUFFER_NB_FRAMES) +- { +- snd_pcm_uframes_t new_count = (MR_ALSA_RINGBUFFER_NB_FRAMES - ravenna_buffer_pos) / chip->nb_playback_interrupts_per_period; +- printk(KERN_WARNING "playback_copy count overflow 2: change count from %lu to %lu\n", count, new_count); +- count = new_count; +- }*/ + + //printk("playback_copy: rate = %u, dsdmode = %u, #IRQ per period = %u, count = %u, pos = %lu, ravenna_buffer_pos = %u, alsa_pb_sac = %llu, ravenna_pb_sac = %llu\n", (dsdrate > 0? dsdrate : runtime->rate), dsdmode, chip->nb_playback_interrupts_per_period, count, pos, ravenna_buffer_pos, chip->playback_buffer_alsa_sac, chip->playback_buffer_rav_sac); +- if(count == 0) +- return 0; + + if(interleaved) + { ++ /// de-interleaving ++ unsigned char *in, *out; ++ unsigned int stepIn = runtime->channels * strideIn; ++ unsigned int stepOut = strideOut * chip->nb_playback_interrupts_per_period; ++ uint32_t ring_buffer_size = MR_ALSA_RINGBUFFER_NB_FRAMES * strideOut; ++ //printk("playback_copy: de-interleaving %u frames, pos = %llu, ravenna_buffer_pos = %u, with strideIn = %u, strideOut = %u, stepIn = %u, stepOut = %u, ravBuffer_csize = %u \n", count, pos, ravenna_buffer_pos, strideIn, strideOut, stepIn, stepOut, (unsigned int)ravBuffer_csize); ++ ++ if (from_user_space) + { +- /// de-interleaving +- unsigned char *in, *out; +- unsigned int stepIn = runtime->channels * strideIn; +- unsigned int stepOut = strideOut * chip->nb_playback_interrupts_per_period; +- size_t ravBuffer_csize = MR_ALSA_RINGBUFFER_NB_FRAMES * strideOut; +- //printk("playback_copy: de-interleaving %u frames, pos = %llu, ravenna_buffer_pos = %u, with strideIn = %u, strideOut = %u, stepIn = %u, stepOut = %u, ravBuffer_csize = %u \n", count, pos, ravenna_buffer_pos, strideIn, strideOut, stepIn, stepOut, (unsigned int)ravBuffer_csize); +- for(chn = 0; chn < runtime->channels; ++chn) ++ for (chn = 0; chn < runtime->channels; ++chn) + { + uint32_t currentOutPos = ravenna_buffer_pos * strideOut; + snd_pcm_uframes_t frmCnt = 0; + in = (unsigned char*)src + chn * strideIn; +- out = chip->playback_buffer + chn * ravBuffer_csize + currentOutPos; +- ++ out = chip->playback_buffer + chn * ring_buffer_size + currentOutPos; ++ // + ///Conversion to Signed integer 32 bit LE +- for(frmCnt = 0; frmCnt < count; ++frmCnt) ++ for (frmCnt = 0; frmCnt < count; ++frmCnt) + { + /// assumes Little Endian + int32_t val = 0; +- if(dsdmode == 0) ++ if (dsdmode == 0) + { +- switch(nb_logical_bits) ++ switch (nb_logical_bits) + { +- case 16: +- //val = (((int32_t)(in[1]) << 8) | ((int32_t)(in[0]))) << 16; +- // OR +- //((unsigned char*)&val)[3] = in[1]; +- //((unsigned char*)&val)[2] = in[0]; +- // OR without intermediate copy_from_user buffer +- __get_user(((unsigned char*)&val)[3], &in[1]); +- __get_user(((unsigned char*)&val)[2], &in[0]); +- break; +- case 24: +- //val = (((int32_t)(in[2]) << 16) | ((int32_t)(in[1]) << 8) | ((int32_t)(in[0]))) << 8; +- // OR +- // ((unsigned char*)&val)[3] = in[2]; +- // ((unsigned char*)&val)[2] = in[1]; +- // ((unsigned char*)&val)[1] = in[0]; +- // OR without intermediate copy_from_user buffer +- __get_user(((unsigned char*)&val)[3], &in[2]); +- __get_user(((unsigned char*)&val)[2], &in[1]); +- __get_user(((unsigned char*)&val)[1], &in[0]); +- break; +- case 32: +- //val = *(int32_t*)(in); +- // OR without intermediate copy_from_user buffer +- __get_user(val, (int32_t*)in); +- break; ++ case 16: ++ __get_user(((unsigned char*)&val)[3], &in[1]); ++ __get_user(((unsigned char*)&val)[2], &in[0]); ++ break; ++ case 24: ++ __get_user(((unsigned char*)&val)[3], &in[2]); ++ __get_user(((unsigned char*)&val)[2], &in[1]); ++ __get_user(((unsigned char*)&val)[1], &in[0]); ++ break; ++ case 32: ++ __get_user(val, (int32_t*)in); ++ break; + } + *((int32_t*)out) = val; + } +@@ -1405,34 +1360,30 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s + { + /// interleaved DSD stream to non interleaved 32 bit aligned blocks with 1/2/4 DSD bytes per 32 bit + uint32_t out_cnt; +- for(out_cnt = 0; out_cnt < chip->nb_playback_interrupts_per_period; ++out_cnt) ++ for (out_cnt = 0; out_cnt < chip->nb_playback_interrupts_per_period; ++out_cnt) + { +- switch(dsdmode) ++ switch (dsdmode) + { +- case 1: ///DSD64 +- //val = *(int32_t*)(in + out_cnt) & 0xFF; +- __get_user(((unsigned char*)&val)[0], &in[out_cnt]); +- break; +- case 2: ///DSD128 +- //val = (((int32_t)(in[2 * out_cnt + 1]) << 8) | ((int32_t)(in[2 * out_cnt]))) & 0xFFFF; +- __get_user(((unsigned char*)&val)[1], &in[2 * out_cnt + 1]); +- __get_user(((unsigned char*)&val)[0], &in[2 * out_cnt]); +- break; +- case 4: ///DSD256 +- //val = *(int32_t*)(in); +- // OR without intermediate copy_from_user buffer +- __get_user(val, (int32_t*)in); +- break; ++ case 1: ///DSD64 ++ __get_user(((unsigned char*)&val)[0], &in[out_cnt]); ++ break; ++ case 2: ///DSD128 ++ __get_user(((unsigned char*)&val)[1], &in[2 * out_cnt + 1]); ++ __get_user(((unsigned char*)&val)[0], &in[2 * out_cnt]); ++ break; ++ case 4: ///DSD256 ++ __get_user(val, (int32_t*)in); ++ break; + } + ((int32_t*)out)[out_cnt] = val; + } + } + + in += stepIn; +- if(currentOutPos + stepOut >= ravBuffer_csize) ++ if (currentOutPos + stepOut >= ring_buffer_size) + { + currentOutPos = 0; +- out = chip->playback_buffer + chn * ravBuffer_csize; ++ out = chip->playback_buffer + chn * ring_buffer_size; + } + else + { +@@ -1442,56 +1393,37 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s + } + } + } +- } +- else +- { ++ else + { +- //printk("mr_alsa_audio_pcm_playback_copy: no de-interleaving, converting %u frames with strideIn = %u\n", count, strideIn); +- /// do the format conversion to the Ravenna Ring buffer ++ for (chn = 0; chn < runtime->channels; ++chn) + { +- unsigned char *in, *out; +- unsigned int stepIn = strideIn; +- unsigned int stepOut = strideOut * chip->nb_playback_interrupts_per_period; +- size_t ravBuffer_csize = MR_ALSA_RINGBUFFER_NB_FRAMES * strideOut; + uint32_t currentOutPos = ravenna_buffer_pos * strideOut; + snd_pcm_uframes_t frmCnt = 0; +- +- in = (unsigned char*)src; +- out = chip->playback_buffer + channel * ravBuffer_csize + currentOutPos; +- for(frmCnt = 0; frmCnt < count; ++frmCnt) ++ in = (unsigned char*)src + chn * strideIn; ++ out = chip->playback_buffer + chn * ring_buffer_size + currentOutPos; ++ // ++ ///Conversion to Signed integer 32 bit LE ++ for (frmCnt = 0; frmCnt < count; ++frmCnt) + { +- /// conversion to signed 32 bit integer LE + /// assumes Little Endian +- int32_t val = 0; +- if(dsdmode == 0) ++ int32_t val = 0; ++ if (dsdmode == 0) + { +- switch(nb_logical_bits) ++ switch (nb_logical_bits) + { +- case 16: +- //val = (((int32_t)(in[1]) << 8) | ((int32_t)(in[0]))) << 16; +- // OR +- //((unsigned char*)&val)[3] = in[1]; +- //((unsigned char*)&val)[2] = in[0]; +- // OR without intermediate copy_from_user buffer +- __get_user(((unsigned char*)&val)[3], &in[1]); +- __get_user(((unsigned char*)&val)[2], &in[0]); +- break; +- case 24: +- //val = (((int32_t)(in[2]) << 16) | ((int32_t)(in[1]) << 8) | ((int32_t)(in[0]))) << 8; +- // OR +- // ((unsigned char*)&val)[3] = in[2]; +- // ((unsigned char*)&val)[2] = in[1]; +- // ((unsigned char*)&val)[1] = in[0]; +- // OR without intermediate copy_from_user buffer +- __get_user(((unsigned char*)&val)[3], &in[2]); +- __get_user(((unsigned char*)&val)[2], &in[1]); +- __get_user(((unsigned char*)&val)[1], &in[0]); +- break; +- case 32: +- //val = *(int32_t*)(in); +- // OR without intermediate copy_from_user buffer +- __get_user(val, (int32_t*)in); +- break; ++ case 16: ++ ++ ((unsigned char*)&val)[3] = in[1]; ++ ((unsigned char*)&val)[2] = in[0]; ++ break; ++ case 24: ++ ((unsigned char*)&val)[3] = in[2]; ++ ((unsigned char*)&val)[2] = in[1]; ++ ((unsigned char*)&val)[1] = in[0]; ++ break; ++ case 32: ++ val = *(int32_t*)(in); ++ break; + } + *((int32_t*)out) = val; + } +@@ -1499,33 +1431,29 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s + { + /// interleaved DSD stream to non interleaved 32 bit aligned blocks with 1/2/4 DSD bytes per 32 bit + uint32_t out_cnt; +- for(out_cnt = 0; out_cnt < chip->nb_playback_interrupts_per_period; ++out_cnt) ++ for (out_cnt = 0; out_cnt < chip->nb_playback_interrupts_per_period; ++out_cnt) + { +- switch(dsdmode) ++ switch (dsdmode) + { +- case 1: ///DSD64 +- //val = *(int32_t*)(in + out_cnt) & 0xFF; +- __get_user(((unsigned char*)&val)[0], &in[out_cnt]); +- break; +- case 2: ///DSD128 +- //val = (((int32_t)(in[2 * out_cnt + 1]) << 8) | ((int32_t)(in[2 * out_cnt]))) & 0xFFFF; +- __get_user(((unsigned char*)&val)[1], &in[2 * out_cnt + 1]); +- __get_user(((unsigned char*)&val)[0], &in[2 * out_cnt]); +- break; +- case 4: ///DSD256 +- //val = *(int32_t*)(in); +- // OR without intermediate copy_from_user buffer +- __get_user(val, (int32_t*)in); +- break; ++ case 1: ///DSD64 ++ val = *(int32_t*)(in + out_cnt) & 0xFF; ++ break; ++ case 2: ///DSD128 ++ val = (((int32_t)(in[2 * out_cnt + 1]) << 8) | ((int32_t)(in[2 * out_cnt]))) & 0xFFFF; ++ break; ++ case 4: ///DSD256 ++ val = *(int32_t*)(in); ++ break; + } + ((int32_t*)out)[out_cnt] = val; + } + } ++ + in += stepIn; +- if(currentOutPos + stepOut >= ravBuffer_csize) ++ if (currentOutPos + stepOut >= ring_buffer_size) + { + currentOutPos = 0; +- out = chip->playback_buffer + channel * ravBuffer_csize; ++ out = chip->playback_buffer + chn * ring_buffer_size; + } + else + { +@@ -1536,248 +1464,17 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s + } + } + } +- +- +- #ifdef MUTE_CHECK +- // First channel check +- mute_detected = !memcmp(testblock, buffer_to_check, min((ssize_t )256, frames_to_bytes(runtime, count))); +- if (mute_detected != playback_mute_detected) ++ else + { +- if (mute_detected) +- printk(">>>>Playback buffer mute detected\n"); +- else +- printk(">>>>Playback buffer signal detected\n"); +- playback_mute_detected = mute_detected; ++ printk(KERN_WARNING "Uninterleaved Playback is not supported\n"); ++ return -EINVAL; + } +- #endif + + chip->playback_buffer_alsa_sac += count; return count; } -- + -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) -static int mr_alsa_audio_pcm_playback_fill_silence( struct snd_pcm_substream *substream, - int channel, unsigned long pos, @@ -402,34 +940,43 @@ index 3d9debd..910ed5b 100644 - } - return 0; -} -- + /// hw_params callback /// This is called when the hardware parameter (hw_params) is set up by the application, that is, once when - /// the buffer size, the period size, the format, etc. are defined for the pcm substream. -@@ -2340,13 +2168,13 @@ static struct snd_pcm_ops mr_alsa_audio_pcm_playback_ops = { +@@ -2339,14 +2032,10 @@ static struct snd_pcm_ops mr_alsa_audio_pcm_playback_ops = { + .pointer = mr_alsa_audio_pcm_pointer, #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) .copy_user = mr_alsa_audio_pcm_playback_copy_user, - //.copy_kernel = mr_alsa_audio_pcm_playback_copy, +- //.copy_kernel = mr_alsa_audio_pcm_playback_copy, - .fill_silence = mr_alsa_audio_pcm_playback_fill_silence, -+ //.fill_silence = mr_alsa_audio_pcm_playback_fill_silence, #else .copy = mr_alsa_audio_pcm_playback_copy, - .silence = mr_alsa_audio_pcm_playback_silence, -+ //.silence = mr_alsa_audio_pcm_playback_silence, #endif .page = snd_pcm_lib_get_vmalloc_page, - .ack = mr_alsa_audio_pcm_ack, -+ //.ack = mr_alsa_audio_pcm_ack, }; ///////////////////////////////////////////////////////////////////////////////////// -@@ -2368,7 +2196,7 @@ static struct snd_pcm_ops mr_alsa_audio_pcm_capture_ops = { +@@ -2361,14 +2050,12 @@ static struct snd_pcm_ops mr_alsa_audio_pcm_capture_ops = { + .pointer = mr_alsa_audio_pcm_pointer, + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) + .copy_user = mr_alsa_audio_pcm_capture_copy_user, +- //.copy_kernel = mr_alsa_audio_pcm_capture_copy, + .fill_silence = NULL, + #else + .copy = mr_alsa_audio_pcm_capture_copy, .silence = NULL, //mr_alsa_audio_pcm_silence, #endif .page = snd_pcm_lib_get_vmalloc_page, - .ack = mr_alsa_audio_pcm_ack, -+ //.ack = mr_alsa_audio_pcm_ack, }; - - + +@@ -2786,4 +2473,4 @@ void mr_alsa_audio_card_exit(void) + g_ravenna_peer = NULL; + g_mr_alsa_audio_ops = NULL; + printk(KERN_INFO "leaving mr_alsa_audio_card_exit..\n"); +-} +\ No newline at end of file ++} diff --git a/3rdparty/patches/ravenna-alsa-lkm-fixes.patch b/3rdparty/patches/ravenna-alsa-lkm-fixes.patch index 0b482cd..ba0b646 100644 --- a/3rdparty/patches/ravenna-alsa-lkm-fixes.patch +++ b/3rdparty/patches/ravenna-alsa-lkm-fixes.patch @@ -80,26 +80,3 @@ index ac65bbb..a65994f 100644 #else #include #endif -diff --git a/driver/audio_driver.c b/driver/audio_driver.c -index 3d9debd..0ff2dfc 100644 ---- a/driver/audio_driver.c -+++ b/driver/audio_driver.c -@@ -1292,8 +1292,7 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s - struct mr_alsa_audio_chip *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - int chn = 0; -- //int interleaved = ((channel == -1 && runtime->channels > 1)? 1 : 0); -- int interleaved = runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ? 1 : 0; -+ int interleaved = ((channel == -1 && runtime->channels > 1)? 1 : 0); - unsigned int nb_logical_bits = snd_pcm_format_width(runtime->format); - unsigned int strideIn = snd_pcm_format_physical_width(runtime->format) >> 3; - unsigned int strideOut = snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32_LE) >> 3; -@@ -1310,7 +1309,7 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s - memset(testblock, 0, sizeof(testblock)); - #endif - -- if (channel > 0 && channel >= runtime->channels) -+ if (!interleaved && channel >= runtime->channels) - { - printk(KERN_WARNING "Channel %d copy ignored because it does not fit the available runtime channels (%d)", channel, runtime->channels); - return 0; From 3d3209bfc90717f2c31554aee8356c4eaf5c2f2f Mon Sep 17 00:00:00 2001 From: Andrea Bondavalli Date: Thu, 27 Aug 2020 21:11:41 +0200 Subject: [PATCH 8/8] Set of chnages to ALSA Ravenna driver to fix the playback with memory mapped access mode on ARM platform. Issue is in the mr_alsa_audio_pcm_playback_copy_internal() function of audio_driver.c that transfers the PCM samples from the ALSA buffer to the Ravenna buffer using the get_user() function. In case of memory access mode the ALSA buffer can be directly accessed by the kernel and the samples must be copied. mr_alsa_audio_pcm_playback_copy_internal() has been modified to handle the transfer of the ALSA buffer from user space (used with read/write access mode) and from kernel space (used by memory access mode). The issue is common to all the platform but impacting ARM only. Additional changes: - changed type for dma_playback_offset and dma_capture_offset variables to uint32_t instead of atomic_t since the pointer callback mr_alsa_audio_pcm_pointer() is now atomic - added variables to the mr_alsa_audio_chip scructure to decrease the number of calls to snd_pcm_lib_xxx() functions. These get assigned in the prepare callback mr_alsa_audio_pcm_prepare() - changed mr_alsa_audio_get_playback_buffer_size_in_frames() to return configured size of Ravenna playback buffer (same as mr_alsa_audio_get_capture_buffer_size_in_frames()) - some rework of the existing patches This fixes issue #20 --- .../patches/ravenna-alsa-lkm-arm-32bit.patch | 22 - ...ravenna-alsa-lkm-direct-pcm-transfer.patch | 683 ++++++++++++++++-- 3rdparty/patches/ravenna-alsa-lkm-fixes.patch | 23 - 3 files changed, 615 insertions(+), 113 deletions(-) diff --git a/3rdparty/patches/ravenna-alsa-lkm-arm-32bit.patch b/3rdparty/patches/ravenna-alsa-lkm-arm-32bit.patch index 699b5a5..8a5fd00 100644 --- a/3rdparty/patches/ravenna-alsa-lkm-arm-32bit.patch +++ b/3rdparty/patches/ravenna-alsa-lkm-arm-32bit.patch @@ -37,28 +37,6 @@ index 5a160e4..a5c84e4 100644 i64DeltaSAC = ui64UsedSAC - ui64GlobalSAC; //MTAL_DP("i64DeltaSAC %I64u playout delay %u, frame size: %u\n", i64DeltaSAC, pRTP_stream_info->m_ui32PlayOutDelay, pManager->get_frame_size(pManager->user)); -diff --git a/driver/audio_driver.c b/driver/audio_driver.c -index a04ec21..cb8b056 100644 ---- a/driver/audio_driver.c -+++ b/driver/audio_driver.c -@@ -1205,8 +1205,8 @@ static int mr_alsa_audio_pcm_capture_copy_internal( struct snd_pcm_substream *s - { - int ret_pu; - char val = 0xf1; -- __put_user_x(1, val, (unsigned long __user *)src, ret_pu); -- ret_pu = put_user(val, (unsigned long __user *)src); -+ //__put_user_x(1, val, (unsigned long __user *)src, ret_pu); -+ ret_pu = put_user(val, (unsigned char __user *)src); - //put_user(val, (unsigned long __user *)src); - switch(nb_logical_bits) - { -@@ -2758,4 +2758,4 @@ void mr_alsa_audio_card_exit(void) - g_ravenna_peer = NULL; - g_mr_alsa_audio_ops = NULL; - printk(KERN_INFO "leaving mr_alsa_audio_card_exit..\n"); --} -\ No newline at end of file -+} diff --git a/driver/manager.c b/driver/manager.c index 5a90eca..8023708 100644 --- a/driver/manager.c diff --git a/3rdparty/patches/ravenna-alsa-lkm-direct-pcm-transfer.patch b/3rdparty/patches/ravenna-alsa-lkm-direct-pcm-transfer.patch index 0f3cdf1..6c694ab 100644 --- a/3rdparty/patches/ravenna-alsa-lkm-direct-pcm-transfer.patch +++ b/3rdparty/patches/ravenna-alsa-lkm-direct-pcm-transfer.patch @@ -1,71 +1,143 @@ diff --git a/driver/audio_driver.c b/driver/audio_driver.c -index 3d9debd..910ed5b 100644 +index 3d9debd..3c3183e 100644 --- a/driver/audio_driver.c +++ b/driver/audio_driver.c -@@ -43,7 +43,7 @@ +@@ -43,7 +43,6 @@ #include #include #include -#include // for mmap -+//#include // for mmap #include #include -@@ -121,9 +121,6 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s +@@ -77,11 +76,6 @@ static int index = SNDRV_DEFAULT_IDX1; /* Index 0-max */ + static char *id = SNDRV_DEFAULT_STR1; /* Id for card */ + static bool enable = SNDRV_DEFAULT_ENABLE1; /* Enable this card */ + static int pcm_devs = 1; +-//static int pcm_substreams = 8; // todo +-//#define MUTE_CHECK +-#ifdef MUTE_CHECK +-static bool playback_mute_detected = false; +-#endif + + module_param(index, int, 0444); + MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); +@@ -112,7 +106,8 @@ static int mr_alsa_audio_pcm_capture_copy( struct snd_pcm_substream *substream, + static int mr_alsa_audio_pcm_capture_copy_internal( struct snd_pcm_substream *substream, int channel, uint32_t pos, void __user *src, - snd_pcm_uframes_t count); +- snd_pcm_uframes_t count, bool to_user_space); ++ snd_pcm_uframes_t count, ++ bool to_user_space); + static int mr_alsa_audio_pcm_playback_copy( struct snd_pcm_substream *substream, + int channel, snd_pcm_uframes_t pos, + void __user *src, +@@ -120,10 +115,8 @@ static int mr_alsa_audio_pcm_playback_copy( struct snd_pcm_substream *substream, + static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *substream, + int channel, uint32_t pos, + void __user *src, +- snd_pcm_uframes_t count); -static int mr_alsa_audio_pcm_playback_silence( struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, - snd_pcm_uframes_t count); ++ snd_pcm_uframes_t count, ++ bool from_user_space); /// "chip" : the main private structure struct mr_alsa_audio_chip -@@ -177,9 +174,7 @@ struct mr_alsa_audio_chip +@@ -177,10 +170,14 @@ struct mr_alsa_audio_chip struct snd_card *card; /* one card */ struct snd_pcm *pcm; /* has one pcm */ - struct snd_pcm_indirect pcm_playback_indirect; - atomic_t dma_playback_offset; // to be used with atomic_read, atomic_set +- atomic_t dma_playback_offset; // to be used with atomic_read, atomic_set - struct snd_pcm_indirect pcm_capture_indirect; - atomic_t dma_capture_offset; // to be used with atomic_read, atomic_set +- atomic_t dma_capture_offset; // to be used with atomic_read, atomic_set ++ uint32_t dma_playback_offset; ++ uint32_t dma_capture_offset; ++ ++ unsigned int pcm_playback_buffer_size; ++ unsigned int pcm_capture_buffer_size; ++ ++ uint8_t *dma_playback_buffer; ++ uint8_t *dma_capture_buffer; }; -@@ -594,7 +589,7 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) + +@@ -507,13 +504,28 @@ static void* mr_alsa_audio_get_playback_buffer(void *rawchip) + } + static uint32_t mr_alsa_audio_get_playback_buffer_size_in_frames(void *rawchip) + { +- if(rawchip) ++ uint32_t res = 0; ++ if (rawchip) + { +- struct mr_alsa_audio_chip *chip = (struct mr_alsa_audio_chip*)rawchip; +- if(chip->playback_buffer) +- return MR_ALSA_RINGBUFFER_NB_FRAMES; ++ struct mr_alsa_audio_chip* chip = (struct mr_alsa_audio_chip*)rawchip; ++ //spin_lock_irq(&chip->lock); ++ { ++ struct snd_pcm_runtime* runtime = chip->playback_substream ? chip->playback_substream->runtime : NULL; ++ if (chip->playback_buffer) ++ { ++ if (runtime && runtime->period_size != 0 && runtime->periods != 0) ++ { ++ res = chip->current_dsd ? MR_ALSA_RINGBUFFER_NB_FRAMES : runtime->period_size * runtime->periods; ++ } ++ else ++ { ++ res = MR_ALSA_RINGBUFFER_NB_FRAMES; ++ } ++ } ++ } ++ //spin_unlock_irq(&chip->lock); + } +- return 0; ++ return res; + + } + static void* mr_alsa_audio_get_capture_buffer(void *rawchip) +@@ -594,7 +606,8 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) uint32_t ring_buffer_size = MR_ALSA_RINGBUFFER_NB_FRAMES; // init to the max size possible uint32_t ptp_frame_size; struct mr_alsa_audio_chip *chip = (struct mr_alsa_audio_chip*)rawchip; - spin_lock_irq(&chip->lock); ++ + spin_lock(&chip->lock); chip->mr_alsa_audio_ops->get_interrupts_frame_size(chip->ravenna_peer, &ptp_frame_size); if(direction == 1 && chip->capture_substream != NULL) { -@@ -614,21 +609,30 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) - unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; - unsigned int pcm_buffer_size = snd_pcm_lib_buffer_bytes(chip->capture_substream); - unsigned int pos; +@@ -611,24 +624,22 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) + runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED || + runtime->access == SNDRV_PCM_ACCESS_MMAP_COMPLEX) + { +- unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; +- unsigned int pcm_buffer_size = snd_pcm_lib_buffer_bytes(chip->capture_substream); +- unsigned int pos; - uint32_t offset = 0; -+ //uint32_t offset = 0; - // char jitter_buffer_byte_len = 3; - // chip->mr_alsa_audio_ops->get_jitter_buffer_sample_bytelength(chip->ravenna_peer, &jitter_buffer_byte_len); +- // char jitter_buffer_byte_len = 3; +- // chip->mr_alsa_audio_ops->get_jitter_buffer_sample_bytelength(chip->ravenna_peer, &jitter_buffer_byte_len); - - pos = atomic_read(&chip->dma_capture_offset); -+ - pos += ptp_frame_size * bytes_to_frame_factor; - if (pos >= pcm_buffer_size) - { - pos -= pcm_buffer_size; - } - atomic_set(&chip->dma_capture_offset, pos); +- pos = atomic_read(&chip->dma_capture_offset); +- pos += ptp_frame_size * bytes_to_frame_factor; +- if (pos >= pcm_buffer_size) ++ unsigned long bytes_to_frame_factor = runtime->channels * chip->current_alsa_capture_stride; + + //printk(KERN_DEBUG "capture copy pos=%u, dma_pos=%u, count=%u, channels=%d pcm_size=%u\n", chip->capture_buffer_pos, pos, ptp_frame_size, runtime->channels, pcm_buffer_size); -+ mr_alsa_audio_pcm_capture_copy_internal(chip->capture_substream, -+ runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ? -1 : runtime->channels/*channel*/, -+ chip->capture_buffer_pos, runtime->dma_area + pos/**src*/, ptp_frame_size, false); - ++ mr_alsa_audio_pcm_capture_copy_internal(chip->capture_substream, runtime->channels/*channel*/, ++ chip->capture_buffer_pos, chip->dma_capture_buffer + chip->dma_capture_offset/**src*/, ptp_frame_size, false); ++ ++ chip->dma_capture_offset += ptp_frame_size * bytes_to_frame_factor; ++ if (chip->dma_capture_offset >= chip->pcm_capture_buffer_size) + { +- pos -= pcm_buffer_size; ++ chip->dma_capture_offset -= chip->pcm_capture_buffer_size; + } +- atomic_set(&chip->dma_capture_offset, pos); +- - chip->mr_alsa_audio_ops->get_input_jitter_buffer_offset(chip->ravenna_peer, &offset); -+ //chip->mr_alsa_audio_ops->get_input_jitter_buffer_offset(chip->ravenna_peer, &offset); - //printk(KERN_DEBUG "Interrupt Capture pos = %u \n", offset); +- //printk(KERN_DEBUG "Interrupt Capture pos = %u \n", offset); } + + chip->capture_buffer_pos += ptp_frame_size; @@ -74,7 +146,7 @@ index 3d9debd..910ed5b 100644 /// Ravenna DSD always uses a rate of 352k with eventual zero padding to maintain a 32 bit alignment /// while DSD in ALSA uses a continuous 8, 16 or 32 bit aligned stream with at 352k, 176k or 88k -@@ -636,7 +640,9 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) +@@ -636,7 +647,9 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) if(++chip->current_capture_interrupt_idx >= chip->nb_capture_interrupts_per_period) { chip->current_capture_interrupt_idx = 0; @@ -84,20 +156,47 @@ index 3d9debd..910ed5b 100644 } } else if(direction == 0 && chip->playback_substream != NULL) -@@ -659,6 +665,12 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) - unsigned int pos; - - pos = atomic_read(&chip->dma_playback_offset); +@@ -648,27 +661,27 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) + printk(KERN_ERR "mr_alsa_audio_pcm_interrupt playback period_size*periods > MR_ALSA_RINGBUFFER_NB_FRAMES\n"); + return -2; + } +- ++ + /// DMA case + if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED || + runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED || + runtime->access == SNDRV_PCM_ACCESS_MMAP_COMPLEX) + { +- unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; +- unsigned int pcm_buffer_size = snd_pcm_lib_buffer_bytes(chip->playback_substream); +- unsigned int pos; +- +- pos = atomic_read(&chip->dma_playback_offset); +- pos += ptp_frame_size * bytes_to_frame_factor; +- if (pos >= pcm_buffer_size) ++ unsigned long bytes_to_frame_factor = runtime->channels * chip->current_alsa_playback_stride; + + //printk(KERN_DEBUG "playback copy pos=%u, dma_pos=%u, count=%u, channels=%d pcm_size=%u\n", chip->playback_buffer_pos, pos, ptp_frame_size, runtime->channels, pcm_buffer_size); -+ mr_alsa_audio_pcm_playback_copy_internal(chip->playback_substream, -+ runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ? -1 : runtime->channels/*channel*/, -+ chip->playback_buffer_pos/*pos*/, runtime->dma_area + pos/*src*/, ptp_frame_size/*count*/); ++ mr_alsa_audio_pcm_playback_copy_internal(chip->playback_substream, runtime->channels/*channel*/, ++ chip->playback_buffer_pos/*pos*/, chip->dma_playback_buffer + chip->dma_playback_offset/*src*/, ptp_frame_size/*count*/, false); + - pos += ptp_frame_size * bytes_to_frame_factor; - if (pos >= pcm_buffer_size) ++ chip->dma_playback_offset += ptp_frame_size * bytes_to_frame_factor; ++ if (chip->dma_playback_offset >= chip->pcm_playback_buffer_size) { -@@ -678,10 +690,12 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) +- pos -= pcm_buffer_size; ++ chip->dma_playback_offset -= chip->pcm_playback_buffer_size; + } +- atomic_set(&chip->dma_playback_offset, pos); + } +- ++ + chip->playback_buffer_pos += ptp_frame_size; +- if(chip->playback_buffer_pos >= ring_buffer_size) ++ if (chip->playback_buffer_pos >= ring_buffer_size) + chip->playback_buffer_pos -= ring_buffer_size; + + /// Ravenna DSD always uses a rate of 352k with eventual zero padding to maintain a 32 bit alignment +@@ -678,10 +691,12 @@ static int mr_alsa_audio_pcm_interrupt(void *rawchip, int direction) { chip->playback_buffer_rav_sac += ptp_frame_size; chip->current_playback_interrupt_idx = 0; @@ -111,12 +210,18 @@ index 3d9debd..910ed5b 100644 return 0; } return -1; -@@ -917,15 +931,12 @@ static int mr_alsa_audio_pcm_prepare(struct snd_pcm_substream *substream) - // TODO: snd_pcm_format_set_silence(SNDRV_PCM_FORMAT_S24_3LE, chip->mr_alsa_audio_ops->, ) +@@ -914,18 +929,15 @@ static int mr_alsa_audio_pcm_prepare(struct snd_pcm_substream *substream) + /// Fill the additional delay between the packet output and the sound eared + chip->mr_alsa_audio_ops->get_playout_delay(chip->ravenna_peer, &runtime->delay); - atomic_set(&chip->dma_playback_offset, 0); +- // TODO: snd_pcm_format_set_silence(SNDRV_PCM_FORMAT_S24_3LE, chip->mr_alsa_audio_ops->, ) +- +- atomic_set(&chip->dma_playback_offset, 0); - memset(&chip->pcm_playback_indirect, 0, sizeof(chip->pcm_playback_indirect)); - chip->pcm_playback_indirect.hw_buffer_size = chip->pcm_playback_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); ++ chip->dma_playback_offset = 0; ++ chip->dma_playback_buffer = runtime->dma_area; ++ chip->pcm_playback_buffer_size = snd_pcm_lib_buffer_bytes(chip->playback_substream); } else if(substream->stream == SNDRV_PCM_STREAM_CAPTURE) { @@ -127,17 +232,23 @@ index 3d9debd..910ed5b 100644 printk(KERN_DEBUG "mr_alsa_audio_pcm_prepare for capture stream\n"); if(chip->ravenna_peer) { -@@ -946,9 +957,6 @@ static int mr_alsa_audio_pcm_prepare(struct snd_pcm_substream *substream) - // TODO: snd_pcm_format_set_silence +@@ -943,12 +955,10 @@ static int mr_alsa_audio_pcm_prepare(struct snd_pcm_substream *substream) + chip->capture_buffer_pos = offset; + chip->current_capture_interrupt_idx = 0; + chip->nb_capture_interrupts_per_period = ((runtime_dsd_mode != 0)? (MR_ALSA_PTP_FRAME_RATE_FOR_DSD / runtime->rate) : 1); +- // TODO: snd_pcm_format_set_silence - atomic_set(&chip->dma_capture_offset, 0); +- atomic_set(&chip->dma_capture_offset, 0); - memset(&chip->pcm_capture_indirect, 0, sizeof(chip->pcm_capture_indirect)); - chip->pcm_capture_indirect.hw_buffer_size = snd_pcm_lib_buffer_bytes(substream); - chip->pcm_capture_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); ++ chip->dma_capture_offset = 0; ++ chip->dma_capture_buffer = runtime->dma_area; ++ chip->pcm_capture_buffer_size = snd_pcm_lib_buffer_bytes(chip->capture_substream); } } else -@@ -970,6 +978,7 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als +@@ -970,6 +980,7 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als uint32_t offset = 0; //printk("entering mr_alsa_audio_pcm_pointer (substream name=%s #%d) ...\n", alsa_sub->name, alsa_sub->number); @@ -145,29 +256,29 @@ index 3d9debd..910ed5b 100644 if(alsa_sub->stream == SNDRV_PCM_STREAM_PLAYBACK) { /// DMA case -@@ -977,7 +986,9 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als +@@ -977,7 +988,9 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als alsa_sub->runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED || alsa_sub->runtime->access == SNDRV_PCM_ACCESS_MMAP_COMPLEX) { - offset = snd_pcm_indirect_playback_pointer(alsa_sub, &chip->pcm_playback_indirect, atomic_read(&chip->dma_playback_offset)); + struct snd_pcm_runtime *runtime = alsa_sub->runtime; -+ unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; -+ offset = atomic_read(&chip->dma_playback_offset) / bytes_to_frame_factor; ++ unsigned long bytes_to_frame_factor = runtime->channels * chip->current_alsa_playback_stride; ++ offset = chip->dma_playback_offset / bytes_to_frame_factor; } else { -@@ -1010,7 +1021,9 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als +@@ -1010,7 +1023,9 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als alsa_sub->runtime->access == SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED || alsa_sub->runtime->access == SNDRV_PCM_ACCESS_MMAP_COMPLEX) { - offset = snd_pcm_indirect_capture_pointer(alsa_sub, &chip->pcm_capture_indirect, atomic_read(&chip->dma_capture_offset)); + struct snd_pcm_runtime *runtime = alsa_sub->runtime; -+ unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; -+ offset = atomic_read(&chip->dma_capture_offset) / bytes_to_frame_factor; ++ unsigned long bytes_to_frame_factor = runtime->channels * chip->current_alsa_capture_stride; ++ offset = chip->dma_capture_offset / bytes_to_frame_factor; } else { -@@ -1036,6 +1049,7 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als +@@ -1036,6 +1051,7 @@ static snd_pcm_uframes_t mr_alsa_audio_pcm_pointer(struct snd_pcm_substream *als } //printk("mr_alsa_audio_pcm_pointer capture offset = %u\n", offset); } @@ -175,11 +286,438 @@ index 3d9debd..910ed5b 100644 return offset; } -@@ -1555,230 +1607,6 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s +@@ -1152,9 +1168,10 @@ static int mr_alsa_audio_pcm_capture_copy_user( struct snd_pcm_substream *subst + void __user *src, + unsigned long count) + { ++ struct mr_alsa_audio_chip* chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + bool interleaved = runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ? 1 : 0; +- unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; ++ unsigned long bytes_to_frame_factor = runtime->channels * chip->current_alsa_capture_stride; + return mr_alsa_audio_pcm_capture_copy(substream, interleaved ? -1 : channel, pos / bytes_to_frame_factor, src, count / bytes_to_frame_factor); + } + #endif +@@ -1173,13 +1190,14 @@ static int mr_alsa_audio_pcm_capture_copy( struct snd_pcm_substream *substream, + static int mr_alsa_audio_pcm_capture_copy_internal( struct snd_pcm_substream *substream, + int channel, uint32_t pos, + void __user *src, +- snd_pcm_uframes_t count, bool to_user_space) ++ snd_pcm_uframes_t count, ++ bool to_user_space) + { + struct mr_alsa_audio_chip *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; +- int interleaved = ((channel == -1 && runtime->channels > 1)? 1 : 0); ++ bool interleaved = (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); + unsigned int nb_logical_bits = snd_pcm_format_width(runtime->format); +- unsigned int strideIn = snd_pcm_format_physical_width(runtime->format) >> 3; ++ unsigned int strideIn = chip->current_alsa_capture_stride; + uint32_t ravenna_buffer_pos = pos; + + // todo DSD capture +@@ -1204,12 +1222,7 @@ static int mr_alsa_audio_pcm_capture_copy_internal( struct snd_pcm_substream *s + + + if(interleaved) +- { +- int ret_pu; +- char val = 0xf1; +- __put_user_x(1, val, (unsigned long __user *)src, ret_pu); +- ret_pu = put_user(val, (unsigned long __user *)src); +- //put_user(val, (unsigned long __user *)src); ++ { + switch(nb_logical_bits) + { + case 16: +@@ -1244,8 +1257,8 @@ static int mr_alsa_audio_pcm_capture_copy_internal( struct snd_pcm_substream *s + return -EINVAL; + } + return count; +- + } ++ + /// This callback is called whenever the alsa application wants to write data + /// We use it here to do all the de-interleaving, format conversion and DSD re-packing + /// The intermediate buffer is actually the alsa (dma) buffer, allocated in hw_params() +@@ -1256,10 +1269,10 @@ static int mr_alsa_audio_pcm_playback_copy_user( struct snd_pcm_substream *subs + void __user *src, + unsigned long count) + { ++ struct mr_alsa_audio_chip* chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; +- bool interleaved = runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ? 1 : 0; +- unsigned long bytes_to_frame_factor = runtime->channels * snd_pcm_format_physical_width(runtime->format) >> 3; +- return mr_alsa_audio_pcm_playback_copy(substream, interleaved ? -1 : channel, pos / bytes_to_frame_factor, src, count / bytes_to_frame_factor); ++ unsigned long bytes_to_frame_factor = runtime->channels * chip->current_alsa_playback_stride; ++ return mr_alsa_audio_pcm_playback_copy(substream, channel, pos / bytes_to_frame_factor, src, count / bytes_to_frame_factor); + } + #endif + +@@ -1278,126 +1291,68 @@ static int mr_alsa_audio_pcm_playback_copy( struct snd_pcm_substream *substream, + /// so respective ring buffers might have different scale and size + uint32_t ravenna_buffer_pos = pos * chip->nb_playback_interrupts_per_period; + +- if(snd_BUG_ON(ravenna_buffer_pos >= MR_ALSA_RINGBUFFER_NB_FRAMES)) +- ravenna_buffer_pos -= MR_ALSA_RINGBUFFER_NB_FRAMES; +- +- return mr_alsa_audio_pcm_playback_copy_internal(substream, channel, ravenna_buffer_pos, src, count); ++ return mr_alsa_audio_pcm_playback_copy_internal(substream, channel, ravenna_buffer_pos, src, count, true); + } +-/// ++ ++ + static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *substream, + int channel, uint32_t pos, + void __user *src, +- snd_pcm_uframes_t count) ++ snd_pcm_uframes_t count, ++ bool from_user_space) + { + struct mr_alsa_audio_chip *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int chn = 0; +- //int interleaved = ((channel == -1 && runtime->channels > 1)? 1 : 0); +- int interleaved = runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ? 1 : 0; ++ bool interleaved = (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); + unsigned int nb_logical_bits = snd_pcm_format_width(runtime->format); +- unsigned int strideIn = snd_pcm_format_physical_width(runtime->format) >> 3; ++ unsigned int strideIn = chip->current_alsa_playback_stride; + unsigned int strideOut = snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32_LE) >> 3; + uint32_t dsdrate = mr_alsa_audio_get_dsd_sample_rate(runtime->format, runtime->rate); + uint32_t dsdmode = (dsdrate > 0? mr_alsa_audio_get_dsd_mode(dsdrate) : 0); + uint32_t ravenna_buffer_pos = pos; +- //uint32_t alsa_ring_buffer_nb_frames = MR_ALSA_RINGBUFFER_NB_FRAMES / chip->nb_playback_interrupts_per_period; +- +- #ifdef MUTE_CHECK +- // mute check +- unsigned char *buffer_to_check = chip->playback_buffer + ravenna_buffer_pos * strideOut; // output buffer channel 0 +- bool mute_detected = false; +- char testblock [256]; +- memset(testblock, 0, sizeof(testblock)); +- #endif +- +- if (channel > 0 && channel >= runtime->channels) +- { +- printk(KERN_WARNING "Channel %d copy ignored because it does not fit the available runtime channels (%d)", channel, runtime->channels); +- return 0; +- } +- +- //printk(KERN_DEBUG "entering mr_alsa_audio_pcm_playback_copy (substream name=%s #%d) (runtime channels %d) access %d...\n", substream->name, substream->number, runtime->channels, runtime->access)); +- +- /*if(snd_BUG_ON(chip->playback_buffer_rav_sac > chip->playback_buffer_alsa_sac)) +- { +- printk(KERN_WARNING "mr_alsa_audio_pcm_playback_copy: Playback stall. Missing playback data from the player application."); +- return -EINVAL; +- } +- +- //printk("playback_copy: initial count = %u, alsa_ring_buffer_nb_frames = %u \n", count, alsa_ring_buffer_nb_frames); +- if(alsa_ring_buffer_nb_frames < chip->playback_buffer_alsa_sac - chip->playback_buffer_rav_sac) +- { +- count = 0; /// no room for more playback at the moment +- printk(KERN_WARNING "playback_copy: no room at the moment (count =%lu) \n", count); +- } +- +- if(count > alsa_ring_buffer_nb_frames - (chip->playback_buffer_alsa_sac - chip->playback_buffer_rav_sac)) +- { +- snd_pcm_uframes_t new_count = (snd_pcm_uframes_t)(alsa_ring_buffer_nb_frames - (chip->playback_buffer_alsa_sac - chip->playback_buffer_rav_sac)); +- printk(KERN_WARNING "playback_copy count overflow 1: change count from %lu to %lu\n", count, new_count); +- count = new_count; +- } +- if(count * chip->nb_playback_interrupts_per_period + ravenna_buffer_pos > MR_ALSA_RINGBUFFER_NB_FRAMES) +- { +- snd_pcm_uframes_t new_count = (MR_ALSA_RINGBUFFER_NB_FRAMES - ravenna_buffer_pos) / chip->nb_playback_interrupts_per_period; +- printk(KERN_WARNING "playback_copy count overflow 2: change count from %lu to %lu\n", count, new_count); +- count = new_count; +- }*/ + + //printk("playback_copy: rate = %u, dsdmode = %u, #IRQ per period = %u, count = %u, pos = %lu, ravenna_buffer_pos = %u, alsa_pb_sac = %llu, ravenna_pb_sac = %llu\n", (dsdrate > 0? dsdrate : runtime->rate), dsdmode, chip->nb_playback_interrupts_per_period, count, pos, ravenna_buffer_pos, chip->playback_buffer_alsa_sac, chip->playback_buffer_rav_sac); +- if(count == 0) +- return 0; + + if(interleaved) + { ++ /// de-interleaving ++ unsigned char *in, *out; ++ unsigned int stepIn = runtime->channels * strideIn; ++ unsigned int stepOut = strideOut * chip->nb_playback_interrupts_per_period; ++ uint32_t ring_buffer_size = MR_ALSA_RINGBUFFER_NB_FRAMES * strideOut; ++ //printk("playback_copy: de-interleaving %u frames, pos = %llu, ravenna_buffer_pos = %u, with strideIn = %u, strideOut = %u, stepIn = %u, stepOut = %u, ravBuffer_csize = %u \n", count, pos, ravenna_buffer_pos, strideIn, strideOut, stepIn, stepOut, (unsigned int)ravBuffer_csize); ++ ++ if (from_user_space) + { +- /// de-interleaving +- unsigned char *in, *out; +- unsigned int stepIn = runtime->channels * strideIn; +- unsigned int stepOut = strideOut * chip->nb_playback_interrupts_per_period; +- size_t ravBuffer_csize = MR_ALSA_RINGBUFFER_NB_FRAMES * strideOut; +- //printk("playback_copy: de-interleaving %u frames, pos = %llu, ravenna_buffer_pos = %u, with strideIn = %u, strideOut = %u, stepIn = %u, stepOut = %u, ravBuffer_csize = %u \n", count, pos, ravenna_buffer_pos, strideIn, strideOut, stepIn, stepOut, (unsigned int)ravBuffer_csize); +- for(chn = 0; chn < runtime->channels; ++chn) ++ for (chn = 0; chn < runtime->channels; ++chn) + { + uint32_t currentOutPos = ravenna_buffer_pos * strideOut; + snd_pcm_uframes_t frmCnt = 0; + in = (unsigned char*)src + chn * strideIn; +- out = chip->playback_buffer + chn * ravBuffer_csize + currentOutPos; +- ++ out = chip->playback_buffer + chn * ring_buffer_size + currentOutPos; ++ // + ///Conversion to Signed integer 32 bit LE +- for(frmCnt = 0; frmCnt < count; ++frmCnt) ++ for (frmCnt = 0; frmCnt < count; ++frmCnt) + { + /// assumes Little Endian + int32_t val = 0; +- if(dsdmode == 0) ++ if (dsdmode == 0) + { +- switch(nb_logical_bits) ++ switch (nb_logical_bits) + { +- case 16: +- //val = (((int32_t)(in[1]) << 8) | ((int32_t)(in[0]))) << 16; +- // OR +- //((unsigned char*)&val)[3] = in[1]; +- //((unsigned char*)&val)[2] = in[0]; +- // OR without intermediate copy_from_user buffer +- __get_user(((unsigned char*)&val)[3], &in[1]); +- __get_user(((unsigned char*)&val)[2], &in[0]); +- break; +- case 24: +- //val = (((int32_t)(in[2]) << 16) | ((int32_t)(in[1]) << 8) | ((int32_t)(in[0]))) << 8; +- // OR +- // ((unsigned char*)&val)[3] = in[2]; +- // ((unsigned char*)&val)[2] = in[1]; +- // ((unsigned char*)&val)[1] = in[0]; +- // OR without intermediate copy_from_user buffer +- __get_user(((unsigned char*)&val)[3], &in[2]); +- __get_user(((unsigned char*)&val)[2], &in[1]); +- __get_user(((unsigned char*)&val)[1], &in[0]); +- break; +- case 32: +- //val = *(int32_t*)(in); +- // OR without intermediate copy_from_user buffer +- __get_user(val, (int32_t*)in); +- break; ++ case 16: ++ __get_user(((unsigned char*)&val)[3], &in[1]); ++ __get_user(((unsigned char*)&val)[2], &in[0]); ++ break; ++ case 24: ++ __get_user(((unsigned char*)&val)[3], &in[2]); ++ __get_user(((unsigned char*)&val)[2], &in[1]); ++ __get_user(((unsigned char*)&val)[1], &in[0]); ++ break; ++ case 32: ++ __get_user(val, (int32_t*)in); ++ break; + } + *((int32_t*)out) = val; + } +@@ -1405,34 +1360,30 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s + { + /// interleaved DSD stream to non interleaved 32 bit aligned blocks with 1/2/4 DSD bytes per 32 bit + uint32_t out_cnt; +- for(out_cnt = 0; out_cnt < chip->nb_playback_interrupts_per_period; ++out_cnt) ++ for (out_cnt = 0; out_cnt < chip->nb_playback_interrupts_per_period; ++out_cnt) + { +- switch(dsdmode) ++ switch (dsdmode) + { +- case 1: ///DSD64 +- //val = *(int32_t*)(in + out_cnt) & 0xFF; +- __get_user(((unsigned char*)&val)[0], &in[out_cnt]); +- break; +- case 2: ///DSD128 +- //val = (((int32_t)(in[2 * out_cnt + 1]) << 8) | ((int32_t)(in[2 * out_cnt]))) & 0xFFFF; +- __get_user(((unsigned char*)&val)[1], &in[2 * out_cnt + 1]); +- __get_user(((unsigned char*)&val)[0], &in[2 * out_cnt]); +- break; +- case 4: ///DSD256 +- //val = *(int32_t*)(in); +- // OR without intermediate copy_from_user buffer +- __get_user(val, (int32_t*)in); +- break; ++ case 1: ///DSD64 ++ __get_user(((unsigned char*)&val)[0], &in[out_cnt]); ++ break; ++ case 2: ///DSD128 ++ __get_user(((unsigned char*)&val)[1], &in[2 * out_cnt + 1]); ++ __get_user(((unsigned char*)&val)[0], &in[2 * out_cnt]); ++ break; ++ case 4: ///DSD256 ++ __get_user(val, (int32_t*)in); ++ break; + } + ((int32_t*)out)[out_cnt] = val; + } + } + + in += stepIn; +- if(currentOutPos + stepOut >= ravBuffer_csize) ++ if (currentOutPos + stepOut >= ring_buffer_size) + { + currentOutPos = 0; +- out = chip->playback_buffer + chn * ravBuffer_csize; ++ out = chip->playback_buffer + chn * ring_buffer_size; + } + else + { +@@ -1442,56 +1393,37 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s + } + } + } +- } +- else +- { ++ else + { +- //printk("mr_alsa_audio_pcm_playback_copy: no de-interleaving, converting %u frames with strideIn = %u\n", count, strideIn); +- /// do the format conversion to the Ravenna Ring buffer ++ for (chn = 0; chn < runtime->channels; ++chn) + { +- unsigned char *in, *out; +- unsigned int stepIn = strideIn; +- unsigned int stepOut = strideOut * chip->nb_playback_interrupts_per_period; +- size_t ravBuffer_csize = MR_ALSA_RINGBUFFER_NB_FRAMES * strideOut; + uint32_t currentOutPos = ravenna_buffer_pos * strideOut; + snd_pcm_uframes_t frmCnt = 0; +- +- in = (unsigned char*)src; +- out = chip->playback_buffer + channel * ravBuffer_csize + currentOutPos; +- for(frmCnt = 0; frmCnt < count; ++frmCnt) ++ in = (unsigned char*)src + chn * strideIn; ++ out = chip->playback_buffer + chn * ring_buffer_size + currentOutPos; ++ // ++ ///Conversion to Signed integer 32 bit LE ++ for (frmCnt = 0; frmCnt < count; ++frmCnt) + { +- /// conversion to signed 32 bit integer LE + /// assumes Little Endian +- int32_t val = 0; +- if(dsdmode == 0) ++ int32_t val = 0; ++ if (dsdmode == 0) + { +- switch(nb_logical_bits) ++ switch (nb_logical_bits) + { +- case 16: +- //val = (((int32_t)(in[1]) << 8) | ((int32_t)(in[0]))) << 16; +- // OR +- //((unsigned char*)&val)[3] = in[1]; +- //((unsigned char*)&val)[2] = in[0]; +- // OR without intermediate copy_from_user buffer +- __get_user(((unsigned char*)&val)[3], &in[1]); +- __get_user(((unsigned char*)&val)[2], &in[0]); +- break; +- case 24: +- //val = (((int32_t)(in[2]) << 16) | ((int32_t)(in[1]) << 8) | ((int32_t)(in[0]))) << 8; +- // OR +- // ((unsigned char*)&val)[3] = in[2]; +- // ((unsigned char*)&val)[2] = in[1]; +- // ((unsigned char*)&val)[1] = in[0]; +- // OR without intermediate copy_from_user buffer +- __get_user(((unsigned char*)&val)[3], &in[2]); +- __get_user(((unsigned char*)&val)[2], &in[1]); +- __get_user(((unsigned char*)&val)[1], &in[0]); +- break; +- case 32: +- //val = *(int32_t*)(in); +- // OR without intermediate copy_from_user buffer +- __get_user(val, (int32_t*)in); +- break; ++ case 16: ++ ++ ((unsigned char*)&val)[3] = in[1]; ++ ((unsigned char*)&val)[2] = in[0]; ++ break; ++ case 24: ++ ((unsigned char*)&val)[3] = in[2]; ++ ((unsigned char*)&val)[2] = in[1]; ++ ((unsigned char*)&val)[1] = in[0]; ++ break; ++ case 32: ++ val = *(int32_t*)(in); ++ break; + } + *((int32_t*)out) = val; + } +@@ -1499,33 +1431,29 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s + { + /// interleaved DSD stream to non interleaved 32 bit aligned blocks with 1/2/4 DSD bytes per 32 bit + uint32_t out_cnt; +- for(out_cnt = 0; out_cnt < chip->nb_playback_interrupts_per_period; ++out_cnt) ++ for (out_cnt = 0; out_cnt < chip->nb_playback_interrupts_per_period; ++out_cnt) + { +- switch(dsdmode) ++ switch (dsdmode) + { +- case 1: ///DSD64 +- //val = *(int32_t*)(in + out_cnt) & 0xFF; +- __get_user(((unsigned char*)&val)[0], &in[out_cnt]); +- break; +- case 2: ///DSD128 +- //val = (((int32_t)(in[2 * out_cnt + 1]) << 8) | ((int32_t)(in[2 * out_cnt]))) & 0xFFFF; +- __get_user(((unsigned char*)&val)[1], &in[2 * out_cnt + 1]); +- __get_user(((unsigned char*)&val)[0], &in[2 * out_cnt]); +- break; +- case 4: ///DSD256 +- //val = *(int32_t*)(in); +- // OR without intermediate copy_from_user buffer +- __get_user(val, (int32_t*)in); +- break; ++ case 1: ///DSD64 ++ val = *(int32_t*)(in + out_cnt) & 0xFF; ++ break; ++ case 2: ///DSD128 ++ val = (((int32_t)(in[2 * out_cnt + 1]) << 8) | ((int32_t)(in[2 * out_cnt]))) & 0xFFFF; ++ break; ++ case 4: ///DSD256 ++ val = *(int32_t*)(in); ++ break; + } + ((int32_t*)out)[out_cnt] = val; + } + } ++ + in += stepIn; +- if(currentOutPos + stepOut >= ravBuffer_csize) ++ if (currentOutPos + stepOut >= ring_buffer_size) + { + currentOutPos = 0; +- out = chip->playback_buffer + channel * ravBuffer_csize; ++ out = chip->playback_buffer + chn * ring_buffer_size; + } + else + { +@@ -1536,248 +1464,17 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s + } + } + } +- +- +- #ifdef MUTE_CHECK +- // First channel check +- mute_detected = !memcmp(testblock, buffer_to_check, min((ssize_t )256, frames_to_bytes(runtime, count))); +- if (mute_detected != playback_mute_detected) ++ else + { +- if (mute_detected) +- printk(">>>>Playback buffer mute detected\n"); +- else +- printk(">>>>Playback buffer signal detected\n"); +- playback_mute_detected = mute_detected; ++ printk(KERN_WARNING "Uninterleaved Playback is not supported\n"); ++ return -EINVAL; + } +- #endif + + chip->playback_buffer_alsa_sac += count; return count; } -- + -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) -static int mr_alsa_audio_pcm_playback_fill_silence( struct snd_pcm_substream *substream, - int channel, unsigned long pos, @@ -402,34 +940,43 @@ index 3d9debd..910ed5b 100644 - } - return 0; -} -- + /// hw_params callback /// This is called when the hardware parameter (hw_params) is set up by the application, that is, once when - /// the buffer size, the period size, the format, etc. are defined for the pcm substream. -@@ -2340,13 +2168,13 @@ static struct snd_pcm_ops mr_alsa_audio_pcm_playback_ops = { +@@ -2339,14 +2032,10 @@ static struct snd_pcm_ops mr_alsa_audio_pcm_playback_ops = { + .pointer = mr_alsa_audio_pcm_pointer, #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) .copy_user = mr_alsa_audio_pcm_playback_copy_user, - //.copy_kernel = mr_alsa_audio_pcm_playback_copy, +- //.copy_kernel = mr_alsa_audio_pcm_playback_copy, - .fill_silence = mr_alsa_audio_pcm_playback_fill_silence, -+ //.fill_silence = mr_alsa_audio_pcm_playback_fill_silence, #else .copy = mr_alsa_audio_pcm_playback_copy, - .silence = mr_alsa_audio_pcm_playback_silence, -+ //.silence = mr_alsa_audio_pcm_playback_silence, #endif .page = snd_pcm_lib_get_vmalloc_page, - .ack = mr_alsa_audio_pcm_ack, -+ //.ack = mr_alsa_audio_pcm_ack, }; ///////////////////////////////////////////////////////////////////////////////////// -@@ -2368,7 +2196,7 @@ static struct snd_pcm_ops mr_alsa_audio_pcm_capture_ops = { +@@ -2361,14 +2050,12 @@ static struct snd_pcm_ops mr_alsa_audio_pcm_capture_ops = { + .pointer = mr_alsa_audio_pcm_pointer, + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0) + .copy_user = mr_alsa_audio_pcm_capture_copy_user, +- //.copy_kernel = mr_alsa_audio_pcm_capture_copy, + .fill_silence = NULL, + #else + .copy = mr_alsa_audio_pcm_capture_copy, .silence = NULL, //mr_alsa_audio_pcm_silence, #endif .page = snd_pcm_lib_get_vmalloc_page, - .ack = mr_alsa_audio_pcm_ack, -+ //.ack = mr_alsa_audio_pcm_ack, }; - - + +@@ -2786,4 +2473,4 @@ void mr_alsa_audio_card_exit(void) + g_ravenna_peer = NULL; + g_mr_alsa_audio_ops = NULL; + printk(KERN_INFO "leaving mr_alsa_audio_card_exit..\n"); +-} +\ No newline at end of file ++} diff --git a/3rdparty/patches/ravenna-alsa-lkm-fixes.patch b/3rdparty/patches/ravenna-alsa-lkm-fixes.patch index 0b482cd..ba0b646 100644 --- a/3rdparty/patches/ravenna-alsa-lkm-fixes.patch +++ b/3rdparty/patches/ravenna-alsa-lkm-fixes.patch @@ -80,26 +80,3 @@ index ac65bbb..a65994f 100644 #else #include #endif -diff --git a/driver/audio_driver.c b/driver/audio_driver.c -index 3d9debd..0ff2dfc 100644 ---- a/driver/audio_driver.c -+++ b/driver/audio_driver.c -@@ -1292,8 +1292,7 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s - struct mr_alsa_audio_chip *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - int chn = 0; -- //int interleaved = ((channel == -1 && runtime->channels > 1)? 1 : 0); -- int interleaved = runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || SNDRV_PCM_ACCESS_MMAP_INTERLEAVED ? 1 : 0; -+ int interleaved = ((channel == -1 && runtime->channels > 1)? 1 : 0); - unsigned int nb_logical_bits = snd_pcm_format_width(runtime->format); - unsigned int strideIn = snd_pcm_format_physical_width(runtime->format) >> 3; - unsigned int strideOut = snd_pcm_format_physical_width(SNDRV_PCM_FORMAT_S32_LE) >> 3; -@@ -1310,7 +1309,7 @@ static int mr_alsa_audio_pcm_playback_copy_internal( struct snd_pcm_substream *s - memset(testblock, 0, sizeof(testblock)); - #endif - -- if (channel > 0 && channel >= runtime->channels) -+ if (!interleaved && channel >= runtime->channels) - { - printk(KERN_WARNING "Channel %d copy ignored because it does not fit the available runtime channels (%d)", channel, runtime->channels); - return 0;