[audio][aml-g12-pdm] Reduce ring buffer size
This is pretty much the same as http://fxrev.dev/449830 but for
PDM input.
Instead of precalculating the ring buffer size according to the
maximum possible parameters to get 1 second of audio, calculate
the size based on the required format and the min_frames parameter.
Bug: 48587
Test: audio_driver_tests
Test: aml-g12-pdm-test
Change-Id: I97a6f86fa9a861ea809dcbd9955ced41cf243015
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/449999
Reviewed-by: Braden Kell <bradenkell@google.com>
Testability-Review: Braden Kell <bradenkell@google.com>
Commit-Queue: Andres Oportus <andresoportus@google.com>
diff --git a/src/devices/lib/amlogic/include/soc/aml-common/aml-pdm-audio.h b/src/devices/lib/amlogic/include/soc/aml-common/aml-pdm-audio.h
index eb49b1e..be9623b 100644
--- a/src/devices/lib/amlogic/include/soc/aml-common/aml-pdm-audio.h
+++ b/src/devices/lib/amlogic/include/soc/aml-common/aml-pdm-audio.h
@@ -31,6 +31,9 @@
// must resize in lower 32-bits of address space
zx_status_t SetBuffer(zx_paddr_t buf, size_t len);
+ // Get HW alignment required in the buffer in bytes.
+ static uint32_t GetBufferAlignment() { return 8; }
+
/*
Returns offset of dma pointer in the ring buffer
*/
diff --git a/src/media/audio/drivers/aml-g12-pdm/audio-stream-in.cc b/src/media/audio/drivers/aml-g12-pdm/audio-stream-in.cc
index dea6105..348e42b 100644
--- a/src/media/audio/drivers/aml-g12-pdm/audio-stream-in.cc
+++ b/src/media/audio/drivers/aml-g12-pdm/audio-stream-in.cc
@@ -7,6 +7,7 @@
#include <lib/zx/clock.h>
#include <math.h>
+#include <numeric>
#include <optional>
#include <utility>
@@ -120,13 +121,18 @@
return ZX_ERR_NO_MEMORY;
}
- // Calculate ring buffer size for 1 second of 16-bit, 48kHz.
- size_t kRingBufferSize = fbl::round_up<size_t, size_t>(
- kMaxSampleRate * 2 * metadata_.number_of_channels, ZX_PAGE_SIZE);
- // Initialize the ring buffer
- InitBuffer(kRingBufferSize);
-
- lib_->SetBuffer(pinned_ring_buffer_.region(0).phys_addr, pinned_ring_buffer_.region(0).size);
+ // Initial setup of one page of buffer, just to be safe.
+ status = InitBuffer(PAGE_SIZE);
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "%s failed to init buffer %d", __FILE__, status);
+ return status;
+ }
+ status =
+ lib_->SetBuffer(pinned_ring_buffer_.region(0).phys_addr, pinned_ring_buffer_.region(0).size);
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "%s failed to set buffer %d", __FILE__, status);
+ return status;
+ }
InitHw();
@@ -166,23 +172,33 @@
zx_status_t AudioStreamIn::GetBuffer(const audio_proto::RingBufGetBufferReq& req,
uint32_t* out_num_rb_frames, zx::vmo* out_buffer) {
- uint32_t rb_frames = static_cast<uint32_t>(pinned_ring_buffer_.region(0).size) / frame_size_;
-
- if (req.min_ring_buffer_frames > rb_frames) {
- zxlogf(ERROR, "%s out of range min rin buffer frames", __FUNCTION__);
- return ZX_ERR_OUT_OF_RANGE;
+ size_t ring_buffer_size = fbl::round_up<size_t, size_t>(
+ req.min_ring_buffer_frames * frame_size_, std::lcm(frame_size_, lib_->GetBufferAlignment()));
+ size_t out_frames = ring_buffer_size / frame_size_;
+ if (out_frames > std::numeric_limits<uint32_t>::max()) {
+ return ZX_ERR_INVALID_ARGS;
}
- zx_status_t status;
+
+ size_t vmo_size = fbl::round_up<size_t, size_t>(ring_buffer_size, PAGE_SIZE);
+ auto status = InitBuffer(vmo_size);
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "%s failed to init buffer %d", __FILE__, status);
+ return status;
+ }
+
constexpr uint32_t rights = ZX_RIGHT_READ | ZX_RIGHT_WRITE | ZX_RIGHT_MAP | ZX_RIGHT_TRANSFER;
status = ring_buffer_vmo_.duplicate(rights, out_buffer);
if (status != ZX_OK) {
zxlogf(ERROR, "%s failed to duplicate vmo", __FUNCTION__);
return status;
}
-
- *out_num_rb_frames = rb_frames;
-
- lib_->SetBuffer(pinned_ring_buffer_.region(0).phys_addr, rb_frames * frame_size_);
+ status = lib_->SetBuffer(pinned_ring_buffer_.region(0).phys_addr, ring_buffer_size);
+ if (status != ZX_OK) {
+ zxlogf(ERROR, "%s failed to set buffer %d", __FILE__, status);
+ return status;
+ }
+ // This is safe because of the overflow check we made above.
+ *out_num_rb_frames = static_cast<uint32_t>(out_frames);
return status;
}
@@ -251,13 +267,13 @@
}
zx_status_t AudioStreamIn::InitBuffer(size_t size) {
- zx_status_t status;
- status = zx_vmo_create_contiguous(bti_.get(), size, 0, ring_buffer_vmo_.reset_and_get_address());
+ pinned_ring_buffer_.Unpin();
+ auto status =
+ zx_vmo_create_contiguous(bti_.get(), size, 0, ring_buffer_vmo_.reset_and_get_address());
if (status != ZX_OK) {
zxlogf(ERROR, "%s failed to allocate ring buffer vmo - %d", __func__, status);
return status;
}
-
status = pinned_ring_buffer_.Pin(ring_buffer_vmo_, bti_, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE);
if (status != ZX_OK) {
zxlogf(ERROR, "%s failed to pin ring buffer vmo - %d", __func__, status);
diff --git a/src/media/audio/drivers/aml-g12-pdm/test/test.cc b/src/media/audio/drivers/aml-g12-pdm/test/test.cc
index 05a692f..0f2bd6b 100644
--- a/src/media/audio/drivers/aml-g12-pdm/test/test.cc
+++ b/src/media/audio/drivers/aml-g12-pdm/test/test.cc
@@ -9,6 +9,7 @@
#include <ddktl/protocol/composite.h>
#include <fake-mmio-reg/fake-mmio-reg.h>
#include <mock-mmio-reg/mock-mmio-reg.h>
+#include <mock/ddktl/protocol/gpio.h>
#include <soc/aml-s905d2/s905d2-hw.h>
#include <zxtest/zxtest.h>
@@ -138,6 +139,33 @@
EXPECT_EQ(step, 12);
}
+ void TestRingBufferSize(uint8_t number_of_channels, uint32_t frames_req,
+ uint32_t frames_expected) {
+ auto metadata = GetDefaultMetadata();
+ metadata.number_of_channels = number_of_channels;
+ tester_.SetMetadata(&metadata, sizeof(metadata));
+
+ auto stream = audio::SimpleAudioStream::Create<TestAudioStreamIn>();
+ audio_fidl::Device::SyncClient client_wrap(std::move(tester_.FidlClient()));
+ audio_fidl::Device::ResultOf::GetChannel ch = client_wrap.GetChannel();
+ ASSERT_EQ(ch.status(), ZX_OK);
+ audio_fidl::StreamConfig::SyncClient client(std::move(ch->channel));
+ zx::channel local, remote;
+ ASSERT_OK(zx::channel::create(0, &local, &remote));
+ fidl::aligned<audio_fidl::PcmFormat> pcm_format = GetDefaultPcmFormat();
+ pcm_format.value.number_of_channels = number_of_channels;
+ auto builder = audio_fidl::Format::UnownedBuilder();
+ builder.set_pcm_format(fidl::unowned_ptr(&pcm_format));
+ client.CreateRingBuffer(builder.build(), std::move(remote));
+
+ auto vmo = audio_fidl::RingBuffer::Call::GetVmo(zx::unowned_channel(local), frames_req, 0);
+ ASSERT_OK(vmo.status());
+ ASSERT_EQ(vmo.Unwrap()->result.response().num_frames, frames_expected);
+
+ stream->DdkAsyncRemove();
+ EXPECT_TRUE(tester_.Ok());
+ stream->DdkRelease();
+ }
FakePDev pdev_;
fake_ddk::Bind tester_;
};
@@ -155,6 +183,22 @@
TestMasks(/*channels*/ 2, /*channels_to_use_bitmask*/ 0xff, /*channels_mask*/ 3, /*mute_mask*/ 0);
}
+// With 16 bits samples, frame size is 2 x number of channels bytes.
+// Frames returned are rounded to HW buffer alignment (8 bytes) and frame size.
+TEST_F(AudioStreamInTest, RingBufferSize1) {
+ TestRingBufferSize(2, 1, 2);
+} // Rounded to HW buffer.
+TEST_F(AudioStreamInTest, RingBufferSize2) {
+ TestRingBufferSize(2, 3, 4);
+} // Rounded to HW buffer.
+TEST_F(AudioStreamInTest, RingBufferSize3) { TestRingBufferSize(3, 1, 4); } // Rounded to both.
+TEST_F(AudioStreamInTest, RingBufferSize4) { TestRingBufferSize(3, 3, 4); } // Rounded to both.
+TEST_F(AudioStreamInTest, RingBufferSize5) {
+ TestRingBufferSize(8, 1, 1);
+} // Rounded to frame size.
+TEST_F(AudioStreamInTest, RingBufferSize6) {
+ TestRingBufferSize(8, 3, 3);
+} // Rounded to frame size.
} // namespace audio::aml_g12
// Redefine PDevMakeMmioBufferWeak per the recommendation in pdev.h.