blob: 475ef2828b725170b878dd1c7859f1935e186b05 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/media/audio/drivers/aml-g12-tdm/composite.h"
#include <fidl/fuchsia.hardware.clock/cpp/wire_test_base.h>
#include <fidl/fuchsia.hardware.gpio/cpp/wire_test_base.h>
#include <fidl/fuchsia.hardware.platform.device/cpp/wire.h>
#include <lib/async_patterns/testing/cpp/dispatcher_bound.h>
#include <lib/component/incoming/cpp/service.h>
#include <lib/ddk/platform-defs.h>
#include <lib/driver/incoming/cpp/namespace.h>
#include <lib/driver/testing/cpp/driver_lifecycle.h>
#include <lib/driver/testing/cpp/driver_runtime.h>
#include <lib/driver/testing/cpp/test_environment.h>
#include <lib/driver/testing/cpp/test_node.h>
#include <lib/fake-bti/bti.h>
#include <lib/fdio/directory.h>
#include <lib/fzl/vmo-mapper.h>
#include <zircon/errors.h>
#include <algorithm>
#include <zxtest/zxtest.h>
namespace audio::aml_g12 {
// These tests check the audio-composite driver implementation variation of AMLogic SoCs audio
// support. Helper code in AmlTdmConfigDevice and //src/devices/lib/amlogic is tested in other
// versions of this driver (StreamConfig and DAI).
constexpr uint64_t kEngineARingBufferIdOutput = 4;
constexpr uint64_t kEngineBRingBufferIdOutput = 5;
constexpr uint64_t kEngineCRingBufferIdOutput = 6;
constexpr uint64_t kEngineARingBufferIdInput = 7;
constexpr uint64_t kEngineBRingBufferIdInput = 8;
constexpr uint64_t kEngineCRingBufferIdInput = 9;
constexpr uint64_t kInvalidBufferId0 = 3;
constexpr uint64_t kInvalidBufferId1 = 10;
constexpr std::array<uint64_t, 6> kAllValidIds{
kEngineARingBufferIdOutput, kEngineBRingBufferIdOutput, kEngineCRingBufferIdOutput,
kEngineARingBufferIdInput, kEngineBRingBufferIdInput, kEngineCRingBufferIdInput,
};
class FakePlatformDevice : public fidl::WireServer<fuchsia_hardware_platform_device::Device> {
public:
fuchsia_hardware_platform_device::Service::InstanceHandler GetInstanceHandler() {
return fuchsia_hardware_platform_device::Service::InstanceHandler({
.device = bindings_.CreateHandler(this, fdf::Dispatcher::GetCurrent()->async_dispatcher(),
fidl::kIgnoreBindingClosure),
});
}
void InitResources() {
EXPECT_OK(zx::vmo::create(kMmioSize, 0, &mmio_));
fake_bti_create(bti_.reset_and_get_address());
}
cpp20::span<uint32_t> mmio() {
// The test has to wait for the driver to set the MMIO cache policy before mapping.
if (!mapped_mmio_.start()) {
MapMmio();
}
return {reinterpret_cast<uint32_t*>(mapped_mmio_.start()), kMmioSize / sizeof(uint32_t)};
}
private:
static constexpr size_t kMmioSize = 0x1000;
void GetMmioById(fuchsia_hardware_platform_device::wire::DeviceGetMmioByIdRequest* request,
GetMmioByIdCompleter::Sync& completer) override {
if (request->index != 0) {
return completer.ReplyError(ZX_ERR_OUT_OF_RANGE);
}
zx::vmo vmo;
if (zx_status_t status = mmio_.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo); status != ZX_OK) {
return completer.ReplyError(status);
}
fidl::Arena arena;
completer.ReplySuccess(fuchsia_hardware_platform_device::wire::Mmio::Builder(arena)
.offset(0)
.size(kMmioSize)
.vmo(std::move(vmo))
.Build());
}
void GetMmioByName(fuchsia_hardware_platform_device::wire::DeviceGetMmioByNameRequest* request,
GetMmioByNameCompleter::Sync& completer) override {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void GetInterruptById(
fuchsia_hardware_platform_device::wire::DeviceGetInterruptByIdRequest* request,
GetInterruptByIdCompleter::Sync& completer) override {}
void GetInterruptByName(
fuchsia_hardware_platform_device::wire::DeviceGetInterruptByNameRequest* request,
GetInterruptByNameCompleter::Sync& completer) override {}
void GetBtiById(fuchsia_hardware_platform_device::wire::DeviceGetBtiByIdRequest* request,
GetBtiByIdCompleter::Sync& completer) override {
zx::bti bti;
if (zx_status_t status = bti_.duplicate(ZX_RIGHT_SAME_RIGHTS, &bti); status != ZX_OK) {
return completer.ReplyError(status);
}
completer.ReplySuccess(std::move(bti));
}
void GetBtiByName(fuchsia_hardware_platform_device::wire::DeviceGetBtiByNameRequest* request,
GetBtiByNameCompleter::Sync& completer) override {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void GetSmcById(fuchsia_hardware_platform_device::wire::DeviceGetSmcByIdRequest* request,
GetSmcByIdCompleter::Sync& completer) override {}
void GetNodeDeviceInfo(GetNodeDeviceInfoCompleter::Sync& completer) override {
fidl::Arena arena;
auto info = fuchsia_hardware_platform_device::wire::NodeDeviceInfo::Builder(arena);
completer.ReplySuccess(info.vid(PDEV_VID_AMLOGIC).pid(PDEV_PID_AMLOGIC_A311D).Build());
}
void GetSmcByName(fuchsia_hardware_platform_device::wire::DeviceGetSmcByNameRequest* request,
GetSmcByNameCompleter::Sync& completer) override {}
void GetBoardInfo(GetBoardInfoCompleter::Sync& completer) override {}
void handle_unknown_method(
fidl::UnknownMethodMetadata<fuchsia_hardware_platform_device::Device> metadata,
fidl::UnknownMethodCompleter::Sync& completer) override {}
void MapMmio() { ASSERT_OK(mapped_mmio_.Map(mmio_)); }
void GetPowerConfiguration(GetPowerConfigurationCompleter::Sync& completer) override {}
zx::vmo mmio_;
fzl::VmoMapper mapped_mmio_;
zx::bti bti_;
fidl::ServerBindingGroup<fuchsia_hardware_platform_device::Device> bindings_;
};
// Fake clock for power management test.
class FakeClock : public fidl::testing::WireTestBase<fuchsia_hardware_clock::Clock> {
public:
FakeClock() = default;
bool IsFakeClockEnabled() { return enabled_; }
fuchsia_hardware_clock::Service::InstanceHandler GetInstanceHandler() {
return fuchsia_hardware_clock::Service::InstanceHandler({
.clock = bindings_.CreateHandler(this, fdf::Dispatcher::GetCurrent()->async_dispatcher(),
fidl::kIgnoreBindingClosure),
});
}
protected:
void Enable(EnableCompleter::Sync& completer) override {
enabled_ = true;
completer.Reply(zx::ok());
}
void Disable(DisableCompleter::Sync& completer) override {
enabled_ = false;
completer.Reply(zx::ok());
}
void IsEnabled(IsEnabledCompleter::Sync& completer) override {
completer.Reply(zx::error(ZX_ERR_NOT_SUPPORTED));
}
void SetRate(::fuchsia_hardware_clock::wire::ClockSetRateRequest* request,
SetRateCompleter::Sync& completer) override {
completer.Reply(zx::error(ZX_ERR_NOT_SUPPORTED));
}
void QuerySupportedRate(::fuchsia_hardware_clock::wire::ClockQuerySupportedRateRequest* request,
QuerySupportedRateCompleter::Sync& completer) override {
completer.Reply(zx::error(ZX_ERR_NOT_SUPPORTED));
}
void GetRate(GetRateCompleter::Sync& completer) override {
completer.Reply(zx::error(ZX_ERR_NOT_SUPPORTED));
}
void SetInput(::fuchsia_hardware_clock::wire::ClockSetInputRequest* request,
SetInputCompleter::Sync& completer) override {
completer.Reply(zx::error(ZX_ERR_NOT_SUPPORTED));
}
void GetNumInputs(GetNumInputsCompleter::Sync& completer) override {
completer.Reply(zx::error(ZX_ERR_NOT_SUPPORTED));
}
void GetInput(GetInputCompleter::Sync& completer) override {
completer.Reply(zx::error(ZX_ERR_NOT_SUPPORTED));
}
void NotImplemented_(const std::string& name, fidl::CompleterBase& completer) override {
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
private:
bool enabled_ = false;
fidl::ServerBindingGroup<fuchsia_hardware_clock::Clock> bindings_;
};
class FakeGpio : public fidl::testing::WireTestBase<fuchsia_hardware_gpio::Gpio> {
public:
FakeGpio() = default;
fuchsia_hardware_gpio::Service::InstanceHandler GetInstanceHandler() {
return fuchsia_hardware_gpio::Service::InstanceHandler({
.device = bindings_.CreateHandler(this, fdf::Dispatcher::GetCurrent()->async_dispatcher(),
fidl::kIgnoreBindingClosure),
});
}
bool IsFakeGpioSetToSclk() { return set_to_sclk_; }
protected:
void ConfigOut(ConfigOutRequestView request, ConfigOutCompleter::Sync& completer) override {
completer.Reply(zx::ok());
}
void SetAltFunction(SetAltFunctionRequestView request,
SetAltFunctionCompleter::Sync& completer) override {
set_to_sclk_ = request->function == 1; // function is SCLK.
completer.Reply(zx::ok());
}
void GetName(GetNameCompleter::Sync& completer) override { completer.ReplySuccess("Test"); }
void NotImplemented_(const std::string& name, fidl::CompleterBase& completer) override {
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
private:
bool set_to_sclk_ = false; // Even though the board driver may set this, do not assume it is set.
fidl::ServerBindingGroup<fuchsia_hardware_gpio::Gpio> bindings_;
};
struct IncomingNamespace {
fdf_testing::TestNode node_{std::string("root")};
fdf_testing::TestEnvironment env_{fdf::Dispatcher::GetCurrent()->get()};
};
class AmlG12CompositeTest : public zxtest::Test {
public:
AmlG12CompositeTest()
: env_dispatcher_(runtime_.StartBackgroundDispatcher()),
driver_dispatcher_(runtime_.StartBackgroundDispatcher()),
incoming_(env_dispatcher(), std::in_place) {}
void SetUp() override {
fuchsia_driver_framework::DriverStartArgs driver_start_args;
incoming_.SyncCall([&driver_start_args, this](IncomingNamespace* incoming) {
auto start_args_result = incoming->node_.CreateStartArgsAndServe();
ASSERT_TRUE(start_args_result.is_ok());
auto init_result =
incoming->env_.Initialize(std::move(start_args_result->incoming_directory_server));
ASSERT_TRUE(init_result.is_ok());
platform_device_.InitResources();
const zx::result add_platform_result =
incoming->env_.incoming_directory().AddService<fuchsia_hardware_platform_device::Service>(
platform_device_.GetInstanceHandler());
EXPECT_TRUE(add_platform_result.is_ok());
auto add_clock_gate_result =
incoming->env_.incoming_directory().AddService<fuchsia_hardware_clock::Service>(
clock_gate_server_.GetInstanceHandler(), "clock-gate");
ASSERT_TRUE(add_clock_gate_result.is_ok());
auto add_clock_pll_result =
incoming->env_.incoming_directory().AddService<fuchsia_hardware_clock::Service>(
clock_pll_server_.GetInstanceHandler(), "clock-pll");
ASSERT_TRUE(add_clock_pll_result.is_ok());
auto add_sclk_tdm_a_result =
incoming->env_.incoming_directory().AddService<fuchsia_hardware_gpio::Service>(
sclk_tdm_a_server_.GetInstanceHandler(), "gpio-tdm-a-sclk");
ASSERT_TRUE(add_sclk_tdm_a_result.is_ok());
auto add_sclk_tdm_b_result =
incoming->env_.incoming_directory().AddService<fuchsia_hardware_gpio::Service>(
sclk_tdm_b_server_.GetInstanceHandler(), "gpio-tdm-b-sclk");
ASSERT_TRUE(add_sclk_tdm_b_result.is_ok());
auto add_sclk_tdm_c_result =
incoming->env_.incoming_directory().AddService<fuchsia_hardware_gpio::Service>(
sclk_tdm_c_server_.GetInstanceHandler(), "gpio-tdm-c-sclk");
ASSERT_TRUE(add_sclk_tdm_c_result.is_ok());
driver_start_args = std::move(start_args_result->start_args);
});
zx::result result = runtime_.RunToCompletion(
dut_.SyncCall(&fdf_testing::DriverUnderTest<Driver>::Start, std::move(driver_start_args)));
ASSERT_EQ(ZX_OK, result.status_value());
incoming_.SyncCall([this](IncomingNamespace* incoming) {
auto client_channel = incoming->node_.children().at(kDriverName).ConnectToDevice();
client_.Bind(
fidl::ClientEnd<fuchsia_hardware_audio::Composite>(std::move(client_channel.value())));
ASSERT_TRUE(client_.is_valid());
});
}
void TearDown() override {
zx::result result =
runtime_.RunToCompletion(dut_.SyncCall(&fdf_testing::DriverUnderTest<Driver>::PrepareStop));
ASSERT_EQ(ZX_OK, result.status_value());
}
protected:
fidl::SyncClient<fuchsia_hardware_audio::Composite> client_;
FakePlatformDevice platform_device_;
FakeClock clock_gate_server_;
FakeClock clock_pll_server_;
FakeGpio sclk_tdm_a_server_;
FakeGpio sclk_tdm_b_server_;
FakeGpio sclk_tdm_c_server_;
fuchsia_hardware_audio::DaiFormat GetDefaultDaiFormat() {
return fuchsia_hardware_audio::DaiFormat(
2, 3, fuchsia_hardware_audio::DaiSampleFormat::kPcmSigned,
fuchsia_hardware_audio::DaiFrameFormat::WithFrameFormatStandard(
fuchsia_hardware_audio::DaiFrameFormatStandard::kI2S),
48'000, 32, 16);
}
fuchsia_hardware_audio::Format GetDefaultRingBufferFormat() {
fuchsia_hardware_audio::PcmFormat pcm_format{
2, fuchsia_hardware_audio::SampleFormat::kPcmSigned, 2, 16, 48'000};
fuchsia_hardware_audio::Format format;
format.pcm_format(std::move(pcm_format));
return format;
}
bool IsFakeClockGateEnabled() { return clock_gate_server_.IsFakeClockEnabled(); }
bool IsFakeClockPllEnabled() { return clock_pll_server_.IsFakeClockEnabled(); }
bool IsTdmASclkSet() { return sclk_tdm_a_server_.IsFakeGpioSetToSclk(); }
bool IsTdmBSclkSet() { return sclk_tdm_b_server_.IsFakeGpioSetToSclk(); }
bool IsTdmCSclkSet() { return sclk_tdm_c_server_.IsFakeGpioSetToSclk(); }
void StopSocPower() {
dut_.SyncCall([](auto* dut) { return (*dut)->StopSocPower(); });
}
void StartSocPower() {
dut_.SyncCall([](auto* dut) { return (*dut)->StartSocPower(); });
}
void CheckDefaultDaiFormats(uint64_t id) {
auto dai_formats_result = client_->GetDaiFormats(id);
ASSERT_TRUE(dai_formats_result.is_ok());
ASSERT_EQ(1, dai_formats_result->dai_formats().size());
auto& dai_formats = dai_formats_result->dai_formats()[0];
ASSERT_EQ(1, dai_formats.number_of_channels().size());
ASSERT_EQ(2, dai_formats.number_of_channels()[0]);
ASSERT_EQ(1, dai_formats.sample_formats().size());
ASSERT_EQ(fuchsia_hardware_audio::DaiSampleFormat::kPcmSigned, dai_formats.sample_formats()[0]);
ASSERT_EQ(5, dai_formats.frame_formats().size());
ASSERT_EQ(fuchsia_hardware_audio::DaiFrameFormat::WithFrameFormatStandard(
fuchsia_hardware_audio::DaiFrameFormatStandard::kI2S),
dai_formats.frame_formats()[0]);
ASSERT_EQ(fuchsia_hardware_audio::DaiFrameFormat::WithFrameFormatStandard(
fuchsia_hardware_audio::DaiFrameFormatStandard::kTdm1),
dai_formats.frame_formats()[1]);
ASSERT_EQ(fuchsia_hardware_audio::DaiFrameFormat::WithFrameFormatStandard(
fuchsia_hardware_audio::DaiFrameFormatStandard::kTdm2),
dai_formats.frame_formats()[2]);
ASSERT_EQ(fuchsia_hardware_audio::DaiFrameFormat::WithFrameFormatStandard(
fuchsia_hardware_audio::DaiFrameFormatStandard::kTdm3),
dai_formats.frame_formats()[3]);
ASSERT_EQ(fuchsia_hardware_audio::DaiFrameFormat::WithFrameFormatStandard(
fuchsia_hardware_audio::DaiFrameFormatStandard::kStereoLeft),
dai_formats.frame_formats()[4]);
ASSERT_EQ(5, dai_formats.frame_rates().size());
ASSERT_EQ(8'000, dai_formats.frame_rates()[0]);
ASSERT_EQ(16'000, dai_formats.frame_rates()[1]);
ASSERT_EQ(32'000, dai_formats.frame_rates()[2]);
ASSERT_EQ(48'000, dai_formats.frame_rates()[3]);
ASSERT_EQ(96'000, dai_formats.frame_rates()[4]);
ASSERT_EQ(2, dai_formats.bits_per_slot().size());
ASSERT_EQ(16, dai_formats.bits_per_slot()[0]);
ASSERT_EQ(32, dai_formats.bits_per_slot()[1]);
ASSERT_EQ(2, dai_formats.bits_per_sample().size());
ASSERT_EQ(16, dai_formats.bits_per_sample()[0]);
ASSERT_EQ(32, dai_formats.bits_per_sample()[1]);
}
void SetDaiFormatDefault(uint64_t id) {
auto format = GetDefaultDaiFormat();
fuchsia_hardware_audio::CompositeSetDaiFormatRequest request(id, std::move(format));
auto dai_formats_result = client_->SetDaiFormat(std::move(request));
ASSERT_TRUE(dai_formats_result.is_ok());
}
void SetDaiFormatFrameFormat(uint64_t id, fuchsia_hardware_audio::DaiFrameFormat frame_format) {
auto format = GetDefaultDaiFormat();
format.frame_format(std::move(frame_format));
fuchsia_hardware_audio::CompositeSetDaiFormatRequest request(id, std::move(format));
auto dai_formats_result = client_->SetDaiFormat(std::move(request));
ASSERT_TRUE(dai_formats_result.is_ok());
}
void SetDaiFormatFrameRate(uint64_t id, uint32_t rate) {
auto format = GetDefaultDaiFormat();
format.frame_rate(rate);
fuchsia_hardware_audio::CompositeSetDaiFormatRequest request(id, std::move(format));
auto dai_formats_result = client_->SetDaiFormat(std::move(request));
ASSERT_TRUE(dai_formats_result.is_ok());
}
void SetDaiFormatBitePerSampleAndBitsPerSlot(uint64_t id, uint8_t bits_per_sample,
uint8_t bits_per_slot) {
auto format = GetDefaultDaiFormat();
format.bits_per_sample(bits_per_sample);
format.bits_per_slot(bits_per_slot);
fuchsia_hardware_audio::CompositeSetDaiFormatRequest request(id, std::move(format));
auto dai_formats_result = client_->SetDaiFormat(std::move(request));
ASSERT_TRUE(dai_formats_result.is_ok());
}
private:
async_dispatcher_t* driver_dispatcher() { return driver_dispatcher_->async_dispatcher(); }
async_dispatcher_t* env_dispatcher() { return env_dispatcher_->async_dispatcher(); }
fdf_testing::DriverRuntime runtime_;
fdf::UnownedSynchronizedDispatcher env_dispatcher_;
fdf::UnownedSynchronizedDispatcher driver_dispatcher_;
async_patterns::TestDispatcherBound<IncomingNamespace> incoming_;
// Use dut_ instead of driver_ because driver_ is used by zxtest.
async_patterns::TestDispatcherBound<fdf_testing::DriverUnderTest<Driver>> dut_{
driver_dispatcher(), std::in_place};
};
TEST_F(AmlG12CompositeTest, CompositeProperties) {
fidl::Result result = client_->GetProperties();
ASSERT_TRUE(result.is_ok());
ASSERT_EQ(fuchsia_hardware_audio::kClockDomainMonotonic,
result->properties().clock_domain().value());
}
TEST_F(AmlG12CompositeTest, Reset) {
// TODO(https://fxbug.dev/42082341): Add behavior to change state recovered to the configuration
// below.
fidl::Result reset_result = client_->Reset();
ASSERT_TRUE(reset_result.is_ok());
// After reset we check we have configured all engines for TDM output and input.
// Configure TDM OUT for I2S 16 bits per slot and per sample (default).
// TDM OUT CTRL0 config, bitoffset 2, 2 slots, 16 bits per slot.
ASSERT_EQ(0x3001'002F, platform_device_.mmio()[0x500 / 4]); // A output.
ASSERT_EQ(0x3001'002F, platform_device_.mmio()[0x540 / 4]); // B output.
ASSERT_EQ(0x3001'002F, platform_device_.mmio()[0x580 / 4]); // C output.
// Configure TDM IN for I2S 16 bits per slot and per sample (default).
// TDM IN CTRL0 config, PAD_TDMIN A(0), B(1) and C(2).
ASSERT_EQ(0x7003'000F, platform_device_.mmio()[0x300 / 4]); // A input.
ASSERT_EQ(0x7013'000F, platform_device_.mmio()[0x340 / 4]); // B input.
ASSERT_EQ(0x7023'000F, platform_device_.mmio()[0x380 / 4]); // C input.
// Configure clocks.
// SCLK CTRL, clk in/out enabled, 24 sdiv, 16 lrduty, 32 lrdiv.
ASSERT_EQ(0xc180'3c1f, platform_device_.mmio()[0x040 / 4]); // A.
ASSERT_EQ(0xc180'3c1f, platform_device_.mmio()[0x048 / 4]); // B.
ASSERT_EQ(0xc180'3c1f, platform_device_.mmio()[0x050 / 4]); // C.
}
TEST_F(AmlG12CompositeTest, ElementsAndTopology) {
auto endpoints =
fidl::CreateEndpoints<fuchsia_hardware_audio_signalprocessing::SignalProcessing>();
auto connect_result = client_->SignalProcessingConnect(std::move(endpoints->server));
ASSERT_TRUE(connect_result.is_ok());
fidl::SyncClient signal_client{std::move(endpoints->client)};
auto elements_result = signal_client->GetElements();
ASSERT_TRUE(elements_result.is_ok());
// Got ring buffer and DAIs for all engines input and output.
constexpr size_t kNumberOfElements = 9;
ASSERT_EQ(kNumberOfElements, elements_result->processing_elements().size());
for (size_t i = 0; i < kNumberOfElements; ++i) {
auto& element = elements_result->processing_elements()[i];
ASSERT_EQ(fuchsia_hardware_audio_signalprocessing::ElementType::kEndpoint, element.type());
ASSERT_EQ(fuchsia_hardware_audio_signalprocessing::PlugDetectCapabilities::kHardwired,
element.type_specific()->endpoint()->plug_detect_capabilities());
auto watch_element_result = signal_client->WatchElementState(*element.id());
ASSERT_TRUE(watch_element_result.is_ok());
ASSERT_EQ(true,
watch_element_result->state().type_specific()->endpoint()->plug_state()->plugged());
}
auto& element0 = elements_result->processing_elements()[0];
auto& element1 = elements_result->processing_elements()[1];
auto& element2 = elements_result->processing_elements()[2];
auto& element3 = elements_result->processing_elements()[3];
auto& element4 = elements_result->processing_elements()[4];
auto& element5 = elements_result->processing_elements()[5];
auto& element6 = elements_result->processing_elements()[6];
auto& element7 = elements_result->processing_elements()[7];
auto& element8 = elements_result->processing_elements()[8];
ASSERT_EQ(fuchsia_hardware_audio_signalprocessing::EndpointType::kRingBuffer,
element0.type_specific()->endpoint()->type());
ASSERT_EQ(fuchsia_hardware_audio_signalprocessing::EndpointType::kRingBuffer,
element1.type_specific()->endpoint()->type());
ASSERT_EQ(fuchsia_hardware_audio_signalprocessing::EndpointType::kRingBuffer,
element2.type_specific()->endpoint()->type());
ASSERT_EQ(fuchsia_hardware_audio_signalprocessing::EndpointType::kRingBuffer,
element3.type_specific()->endpoint()->type());
ASSERT_EQ(fuchsia_hardware_audio_signalprocessing::EndpointType::kRingBuffer,
element4.type_specific()->endpoint()->type());
ASSERT_EQ(fuchsia_hardware_audio_signalprocessing::EndpointType::kRingBuffer,
element5.type_specific()->endpoint()->type());
ASSERT_EQ(fuchsia_hardware_audio_signalprocessing::EndpointType::kDaiInterconnect,
element6.type_specific()->endpoint()->type());
ASSERT_EQ(fuchsia_hardware_audio_signalprocessing::EndpointType::kDaiInterconnect,
element7.type_specific()->endpoint()->type());
ASSERT_EQ(fuchsia_hardware_audio_signalprocessing::EndpointType::kDaiInterconnect,
element8.type_specific()->endpoint()->type());
constexpr uint64_t kExpectedTopologyId = 1;
constexpr uint64_t kUnrecognizedTopologyId = 2;
auto topology_result = signal_client->GetTopologies();
ASSERT_TRUE(topology_result.is_ok());
ASSERT_EQ(1, topology_result->topologies().size());
auto& topology = topology_result->topologies()[0];
ASSERT_EQ(kExpectedTopologyId, topology.id());
auto set_topology_result = signal_client->SetTopology(kUnrecognizedTopologyId);
ASSERT_TRUE(set_topology_result.is_error());
ASSERT_TRUE(set_topology_result.error_value().is_domain_error());
ASSERT_EQ(set_topology_result.error_value().domain_error(), ZX_ERR_INVALID_ARGS);
set_topology_result = signal_client->SetTopology(kExpectedTopologyId);
ASSERT_TRUE(set_topology_result.is_ok());
// Get edges for ring buffer and DAIs for all engines.
constexpr size_t kNumberOfEdges = 6;
ASSERT_EQ(kNumberOfEdges, topology.processing_elements_edge_pairs()->size());
auto& edge0 = (*topology.processing_elements_edge_pairs())[0];
auto& edge1 = (*topology.processing_elements_edge_pairs())[1];
auto& edge2 = (*topology.processing_elements_edge_pairs())[2];
auto& edge3 = (*topology.processing_elements_edge_pairs())[3];
auto& edge4 = (*topology.processing_elements_edge_pairs())[4];
auto& edge5 = (*topology.processing_elements_edge_pairs())[5];
// Output.
ASSERT_EQ(4, edge0.processing_element_id_from());
ASSERT_EQ(1, edge0.processing_element_id_to());
ASSERT_EQ(5, edge1.processing_element_id_from());
ASSERT_EQ(2, edge1.processing_element_id_to());
ASSERT_EQ(6, edge2.processing_element_id_from());
ASSERT_EQ(3, edge2.processing_element_id_to());
// Input.
ASSERT_EQ(1, edge3.processing_element_id_from());
ASSERT_EQ(7, edge3.processing_element_id_to());
ASSERT_EQ(2, edge4.processing_element_id_from());
ASSERT_EQ(8, edge4.processing_element_id_to());
ASSERT_EQ(3, edge5.processing_element_id_from());
ASSERT_EQ(9, edge5.processing_element_id_to());
auto watch_topology_result = signal_client->WatchTopology();
ASSERT_TRUE(watch_topology_result.is_ok());
ASSERT_EQ(1, watch_topology_result->topology_id());
}
TEST_F(AmlG12CompositeTest, ElementsState) {
auto endpoints =
fidl::CreateEndpoints<fuchsia_hardware_audio_signalprocessing::SignalProcessing>();
auto connect_result = client_->SignalProcessingConnect(std::move(endpoints->server));
ASSERT_TRUE(connect_result.is_ok());
fidl::SyncClient signal_client{std::move(endpoints->client)};
auto elements_result = signal_client->GetElements();
ASSERT_TRUE(elements_result.is_ok());
// Able to set element state for all (endpoint) elements with no parameters.
for (auto element : elements_result->processing_elements()) {
if (element.type() == fuchsia_hardware_audio_signalprocessing::ElementType::kEndpoint) {
fuchsia_hardware_audio_signalprocessing::SignalProcessingSetElementStateRequest request;
request.processing_element_id(*element.id());
auto set_element_result = signal_client->SetElementState(std::move(request));
ASSERT_TRUE(set_element_result.is_ok());
}
}
}
TEST_F(AmlG12CompositeTest, GetDaiFormatsErrors) {
// Only ids 1, 2, and 3 configure HW DAI formats.
{
auto dai_formats_result = client_->GetDaiFormats(0);
ASSERT_TRUE(dai_formats_result.is_error());
ASSERT_TRUE(dai_formats_result.error_value().is_domain_error());
ASSERT_TRUE(dai_formats_result.error_value().domain_error() ==
fuchsia_hardware_audio::DriverError::kInvalidArgs);
}
{
auto dai_formats_result = client_->GetDaiFormats(4);
ASSERT_TRUE(dai_formats_result.is_error());
ASSERT_TRUE(dai_formats_result.error_value().is_domain_error());
ASSERT_TRUE(dai_formats_result.error_value().domain_error() ==
fuchsia_hardware_audio::DriverError::kInvalidArgs);
}
}
TEST_F(AmlG12CompositeTest, GetDaiFormatsValid) {
// Ids 1, 2, and 3 are valid configure HW DAI formats.
CheckDefaultDaiFormats(1);
CheckDefaultDaiFormats(2);
CheckDefaultDaiFormats(3);
}
TEST_F(AmlG12CompositeTest, SetDaiFormatsErrors) {
// Only ids 1, 2, and 3 configure HW DAI formats.
{
fuchsia_hardware_audio::CompositeSetDaiFormatRequest request(0, GetDefaultDaiFormat());
auto dai_formats_result = client_->SetDaiFormat(std::move(request));
ASSERT_TRUE(dai_formats_result.is_error());
ASSERT_TRUE(dai_formats_result.error_value().is_domain_error());
ASSERT_TRUE(dai_formats_result.error_value().domain_error() ==
fuchsia_hardware_audio::DriverError::kInvalidArgs);
}
{
fuchsia_hardware_audio::CompositeSetDaiFormatRequest request(4, GetDefaultDaiFormat());
auto dai_formats_result = client_->SetDaiFormat(std::move(request));
ASSERT_TRUE(dai_formats_result.is_error());
ASSERT_TRUE(dai_formats_result.error_value().is_domain_error());
ASSERT_TRUE(dai_formats_result.error_value().domain_error() ==
fuchsia_hardware_audio::DriverError::kInvalidArgs);
}
{
fuchsia_hardware_audio::CompositeSetDaiFormatRequest request(1, GetDefaultDaiFormat());
auto dai_formats_result = client_->SetDaiFormat(std::move(request));
ASSERT_TRUE(dai_formats_result.is_ok());
// Configure TDM A for 32 bits I2S:
// TDM OUT CTRL0 config, bitoffset 2, 2 slots, 32 bits per slot.
ASSERT_EQ(0x3001'003F, platform_device_.mmio()[0x500 / 4]);
// TDM IN CTRL0 config, PAD_TDMIN A, 32 bits per slot.
ASSERT_EQ(0x7003'001F, platform_device_.mmio()[0x300 / 4]);
}
{
fuchsia_hardware_audio::CompositeSetDaiFormatRequest request(2, GetDefaultDaiFormat());
auto dai_formats_result = client_->SetDaiFormat(std::move(request));
ASSERT_TRUE(dai_formats_result.is_ok());
// Configure TDM B for 32 bits I2S:
// TDM OUT CTRL0 config, bitoffset 2, 2 slots, 32 bits per slot.
ASSERT_EQ(0x3001'003F, platform_device_.mmio()[0x540 / 4]);
// TDM IN CTRL0 config, PAD_TDMIN B, 32 bits per slot.
ASSERT_EQ(0x7013'001F, platform_device_.mmio()[0x340 / 4]);
}
{
fuchsia_hardware_audio::CompositeSetDaiFormatRequest request(3, GetDefaultDaiFormat());
auto dai_formats_result = client_->SetDaiFormat(std::move(request));
ASSERT_TRUE(dai_formats_result.is_ok());
// Configure TDM C for 32 bits I2S:
// TDM OUT CTRL0 config, bitoffset 2, 2 slots, 32 bits per slot.
ASSERT_EQ(0x3001'003F, platform_device_.mmio()[0x580 / 4]);
// TDM IN CTRL0 config, PAD_TDMIN C, 32bits per slot.
ASSERT_EQ(0x7023'001F, platform_device_.mmio()[0x380 / 4]);
}
// Any DAI field not in the supported ones returns an error.
{
auto format = GetDefaultDaiFormat();
format.number_of_channels(123);
fuchsia_hardware_audio::CompositeSetDaiFormatRequest request(3, std::move(format));
auto dai_formats_result = client_->SetDaiFormat(std::move(request));
ASSERT_TRUE(dai_formats_result.is_error());
ASSERT_TRUE(dai_formats_result.error_value().is_domain_error());
ASSERT_TRUE(dai_formats_result.error_value().domain_error() ==
fuchsia_hardware_audio::DriverError::kInvalidArgs);
}
{
auto format = GetDefaultDaiFormat();
format.sample_format(fuchsia_hardware_audio::DaiSampleFormat::kPcmFloat);
fuchsia_hardware_audio::CompositeSetDaiFormatRequest request(3, std::move(format));
auto dai_formats_result = client_->SetDaiFormat(std::move(request));
ASSERT_TRUE(dai_formats_result.is_error());
ASSERT_TRUE(dai_formats_result.error_value().is_domain_error());
ASSERT_TRUE(dai_formats_result.error_value().domain_error() ==
fuchsia_hardware_audio::DriverError::kInvalidArgs);
}
{
auto format = GetDefaultDaiFormat();
format.frame_format(fuchsia_hardware_audio::DaiFrameFormat::WithFrameFormatStandard(
fuchsia_hardware_audio::DaiFrameFormatStandard::kStereoRight));
fuchsia_hardware_audio::CompositeSetDaiFormatRequest request(3, std::move(format));
auto dai_formats_result = client_->SetDaiFormat(std::move(request));
ASSERT_TRUE(dai_formats_result.is_error());
ASSERT_TRUE(dai_formats_result.error_value().is_domain_error());
ASSERT_TRUE(dai_formats_result.error_value().domain_error() ==
fuchsia_hardware_audio::DriverError::kInvalidArgs);
}
{
auto format = GetDefaultDaiFormat();
format.frame_rate(123);
fuchsia_hardware_audio::CompositeSetDaiFormatRequest request(3, std::move(format));
auto dai_formats_result = client_->SetDaiFormat(std::move(request));
ASSERT_TRUE(dai_formats_result.is_error());
ASSERT_TRUE(dai_formats_result.error_value().is_domain_error());
ASSERT_TRUE(dai_formats_result.error_value().domain_error() ==
fuchsia_hardware_audio::DriverError::kInvalidArgs);
}
{
auto format = GetDefaultDaiFormat();
format.bits_per_slot(123);
fuchsia_hardware_audio::CompositeSetDaiFormatRequest request(3, std::move(format));
auto dai_formats_result = client_->SetDaiFormat(std::move(request));
ASSERT_TRUE(dai_formats_result.is_error());
ASSERT_TRUE(dai_formats_result.error_value().is_domain_error());
ASSERT_TRUE(dai_formats_result.error_value().domain_error() ==
fuchsia_hardware_audio::DriverError::kInvalidArgs);
}
{
auto format = GetDefaultDaiFormat();
format.bits_per_sample(123);
fuchsia_hardware_audio::CompositeSetDaiFormatRequest request(3, std::move(format));
auto dai_formats_result = client_->SetDaiFormat(std::move(request));
ASSERT_TRUE(dai_formats_result.is_error());
ASSERT_TRUE(dai_formats_result.error_value().is_domain_error());
ASSERT_TRUE(dai_formats_result.error_value().domain_error() ==
fuchsia_hardware_audio::DriverError::kInvalidArgs);
}
}
TEST_F(AmlG12CompositeTest, SetDaiFormatsValid) {
// Ids 1, 2, and 3 are valid configure HW DAI formats.
SetDaiFormatDefault(1);
// Configure TDM A for 32 bits I2S:
// TDM OUT CTRL0 config, bitoffset 2, 2 slots, 32 bits per slot.
ASSERT_EQ(0x3001'003F, platform_device_.mmio()[0x500 / 4]);
// TDM IN CTRL0 config, PAD_TDMIN A, 32 bits per slot.
ASSERT_EQ(0x7003'001F, platform_device_.mmio()[0x300 / 4]);
SetDaiFormatDefault(2);
// Configure TDM B for 32 bits I2S:
// TDM OUT CTRL0 config, bitoffset 2, 2 slots, 32 bits per slot.
ASSERT_EQ(0x3001'003F, platform_device_.mmio()[0x540 / 4]);
// TDM IN CTRL0 config, PAD_TDMIN B, 32 bits per slot.
ASSERT_EQ(0x7013'001F, platform_device_.mmio()[0x340 / 4]);
SetDaiFormatDefault(3);
// Configure TDM C for 32 bits I2S:
// TDM OUT CTRL0 config, bitoffset 2, 2 slots, 32 bits per slot.
ASSERT_EQ(0x3001'003F, platform_device_.mmio()[0x580 / 4]);
// TDM IN CTRL0 config, PAD_TDMIN C, 32bits per slot.
ASSERT_EQ(0x7023'001F, platform_device_.mmio()[0x380 / 4]);
SetDaiFormatFrameFormat(1, fuchsia_hardware_audio::DaiFrameFormat::WithFrameFormatStandard(
fuchsia_hardware_audio::DaiFrameFormatStandard::kTdm1));
// TDM OUT CTRL0 config, reg_tdm_init_bitnum (bitoffset) 3
ASSERT_EQ(0x3001'803F, platform_device_.mmio()[0x500 / 4]);
// TDM IN CTRL0 config, reg_tdmin_in_bit_skew 4
ASSERT_EQ(0x3004'001F, platform_device_.mmio()[0x300 / 4]);
SetDaiFormatFrameFormat(1, fuchsia_hardware_audio::DaiFrameFormat::WithFrameFormatStandard(
fuchsia_hardware_audio::DaiFrameFormatStandard::kTdm2));
// TDM OUT CTRL0 config, reg_tdm_init_bitnum (bitoffset) 2
ASSERT_EQ(0x3001'003F, platform_device_.mmio()[0x500 / 4]);
// TDM IN CTRL0 config, reg_tdmin_in_bit_skew 3
ASSERT_EQ(0x3003'001F, platform_device_.mmio()[0x300 / 4]);
SetDaiFormatFrameFormat(1, fuchsia_hardware_audio::DaiFrameFormat::WithFrameFormatStandard(
fuchsia_hardware_audio::DaiFrameFormatStandard::kTdm3));
// TDM OUT CTRL0 config, reg_tdm_init_bitnum (bitoffset) 1
ASSERT_EQ(0x3000'803F, platform_device_.mmio()[0x500 / 4]);
// TDM IN CTRL0 config, reg_tdmin_in_bit_skew 2
ASSERT_EQ(0x3002'001F, platform_device_.mmio()[0x300 / 4]);
SetDaiFormatFrameFormat(1, fuchsia_hardware_audio::DaiFrameFormat::WithFrameFormatStandard(
fuchsia_hardware_audio::DaiFrameFormatStandard::kStereoLeft));
// TDM OUT CTRL0 config, reg_tdm_init_bitnum (bitoffset) 3
ASSERT_EQ(0x3001'803F, platform_device_.mmio()[0x500 / 4]);
// TDM IN CTRL0 config, reg_tdmin_in_bit_skew 4
ASSERT_EQ(0x3004'001F, platform_device_.mmio()[0x300 / 4]);
SetDaiFormatFrameRate(1, 8'000);
EXPECT_EQ(0x8400'003b, platform_device_.mmio()[0x001]); // MCLK CTRL, div 60.
SetDaiFormatFrameRate(1, 16'000);
EXPECT_EQ(0x8400'001d, platform_device_.mmio()[0x001]); // MCLK CTRL, div 30.
SetDaiFormatFrameRate(1, 32'000);
EXPECT_EQ(0x8400'000e, platform_device_.mmio()[0x001]); // MCLK CTRL, div 15.
SetDaiFormatFrameRate(1, 48'000);
EXPECT_EQ(0x8400'0009, platform_device_.mmio()[0x001]); // MCLK CTRL, div 10.
SetDaiFormatFrameRate(1, 96'000);
EXPECT_EQ(0x8400'0004, platform_device_.mmio()[0x001]); // MCLK CTRL, div 5.
SetDaiFormatFrameRate(2, 8'000); // A change to id 2 does not affect id 1.
SetDaiFormatFrameRate(3, 48'000); // A change to id 3 does not affect id 1.
EXPECT_EQ(0x8400'0004, platform_device_.mmio()[0x001]); // MCLK CTRL, div 5.
SetDaiFormatBitePerSampleAndBitsPerSlot(1, 32, 32);
// TDM OUT CTRL0 config, bitoffset 2, 2 slots, 32 bits per slot.
ASSERT_EQ(0x3001'003F, platform_device_.mmio()[0x500 / 4]);
// TDM OUT CTRL1 FRDDR with 32 bits per sample.
ASSERT_EQ(0x0000'1f40, platform_device_.mmio()[0x504 / 4]);
// SCLK CTRL, clk in/out enabled, 24 sdiv, 32 lrduty, 32 lrdiv.
ASSERT_EQ(0xc180'7c3f, platform_device_.mmio()[0x040 / 4]);
SetDaiFormatBitePerSampleAndBitsPerSlot(1, 16, 16);
// TDM OUT CTRL0 config, bitoffset 2, 2 slots, 16 bits per slot.
ASSERT_EQ(0x3001'002F, platform_device_.mmio()[0x500 / 4]);
// TDM OUT CTRL1 FRDDR with 16 bits per sample.
ASSERT_EQ(0x0000'0f20, platform_device_.mmio()[0x504 / 4]);
// SCLK CTRL, clk in/out enabled, 24 sdiv, 16 lrduty, 16 lrdiv.
ASSERT_EQ(0xc180'3c1f, platform_device_.mmio()[0x040 / 4]);
SetDaiFormatBitePerSampleAndBitsPerSlot(1, 16, 32);
// TDM OUT CTRL0 config, bitoffset 2, 2 slots, 32 bits per slot.
ASSERT_EQ(0x3001'003F, platform_device_.mmio()[0x500 / 4]);
// TDM OUT CTRL1 FRDDR with 16 bits per sample.
ASSERT_EQ(0x0000'0f20, platform_device_.mmio()[0x504 / 4]);
// SCLK CTRL, clk in/out enabled, 24 sdiv, 32 lrduty, 32 lrdiv.
ASSERT_EQ(0xc180'7c3f, platform_device_.mmio()[0x040 / 4]);
}
TEST_F(AmlG12CompositeTest, GetRingBufferFormats) {
{
auto ring_buffer_formats_result = client_->GetRingBufferFormats(kInvalidBufferId0);
ASSERT_FALSE(ring_buffer_formats_result.is_ok());
}
{
auto ring_buffer_formats_result = client_->GetRingBufferFormats(kInvalidBufferId1);
ASSERT_FALSE(ring_buffer_formats_result.is_ok());
}
{
auto ring_buffer_formats_result = client_->GetRingBufferFormats(kEngineARingBufferIdOutput);
ASSERT_TRUE(ring_buffer_formats_result.is_ok());
// There is at least one entry reported.
ASSERT_GE(1, ring_buffer_formats_result->ring_buffer_formats().size());
}
}
TEST_F(AmlG12CompositeTest, CreateRingBuffer) {
{
auto endpoints = fidl::CreateEndpoints<fuchsia_hardware_audio::RingBuffer>();
fuchsia_hardware_audio::CompositeCreateRingBufferRequest request(
kInvalidBufferId0, GetDefaultRingBufferFormat(), std::move(endpoints->server));
auto ring_buffer_result = client_->CreateRingBuffer(std::move(request));
ASSERT_FALSE(ring_buffer_result.is_ok());
}
{
auto endpoints = fidl::CreateEndpoints<fuchsia_hardware_audio::RingBuffer>();
fuchsia_hardware_audio::CompositeCreateRingBufferRequest request(
kInvalidBufferId1, GetDefaultRingBufferFormat(), std::move(endpoints->server));
auto ring_buffer_result = client_->CreateRingBuffer(std::move(request));
ASSERT_FALSE(ring_buffer_result.is_ok());
}
// Any Ring Buffer field not in the supported ones returns an error.
{
auto format = GetDefaultRingBufferFormat();
format.pcm_format()->number_of_channels(123); // Bad number of channels.
auto endpoints = fidl::CreateEndpoints<fuchsia_hardware_audio::RingBuffer>();
fuchsia_hardware_audio::CompositeCreateRingBufferRequest request(
kEngineARingBufferIdOutput, std::move(format), std::move(endpoints->server));
auto ring_buffer_result = client_->CreateRingBuffer(std::move(request));
ASSERT_FALSE(ring_buffer_result.is_ok());
}
{
auto format = GetDefaultRingBufferFormat();
format.pcm_format()->sample_format(
fuchsia_hardware_audio::SampleFormat::kPcmFloat); // Bad format.
auto endpoints = fidl::CreateEndpoints<fuchsia_hardware_audio::RingBuffer>();
fuchsia_hardware_audio::CompositeCreateRingBufferRequest request(
kEngineARingBufferIdOutput, std::move(format), std::move(endpoints->server));
auto ring_buffer_result = client_->CreateRingBuffer(std::move(request));
ASSERT_FALSE(ring_buffer_result.is_ok());
}
{
auto format = GetDefaultRingBufferFormat();
format.pcm_format()->bytes_per_sample(123); // Bad bytes per sample.
auto endpoints = fidl::CreateEndpoints<fuchsia_hardware_audio::RingBuffer>();
fuchsia_hardware_audio::CompositeCreateRingBufferRequest request(
kEngineARingBufferIdOutput, std::move(format), std::move(endpoints->server));
auto ring_buffer_result = client_->CreateRingBuffer(std::move(request));
ASSERT_FALSE(ring_buffer_result.is_ok());
}
{
auto format = GetDefaultRingBufferFormat();
format.pcm_format()->valid_bits_per_sample(123); // Bad valid bits per sample.
auto endpoints = fidl::CreateEndpoints<fuchsia_hardware_audio::RingBuffer>();
fuchsia_hardware_audio::CompositeCreateRingBufferRequest request(
kEngineARingBufferIdOutput, std::move(format), std::move(endpoints->server));
auto ring_buffer_result = client_->CreateRingBuffer(std::move(request));
ASSERT_FALSE(ring_buffer_result.is_ok());
}
{
auto format = GetDefaultRingBufferFormat();
format.pcm_format()->frame_rate(123); // Bad frame rate.
auto endpoints = fidl::CreateEndpoints<fuchsia_hardware_audio::RingBuffer>();
fuchsia_hardware_audio::CompositeCreateRingBufferRequest request(
kEngineARingBufferIdOutput, std::move(format), std::move(endpoints->server));
auto ring_buffer_result = client_->CreateRingBuffer(std::move(request));
ASSERT_FALSE(ring_buffer_result.is_ok());
}
}
TEST_F(AmlG12CompositeTest, PowerSuspendResume) {
EXPECT_TRUE(IsTdmASclkSet());
EXPECT_TRUE(IsTdmBSclkSet());
EXPECT_TRUE(IsTdmCSclkSet());
EXPECT_TRUE(IsFakeClockGateEnabled());
EXPECT_TRUE(IsFakeClockPllEnabled());
StopSocPower();
EXPECT_FALSE(IsTdmASclkSet());
EXPECT_FALSE(IsTdmBSclkSet());
EXPECT_FALSE(IsTdmCSclkSet());
EXPECT_FALSE(IsFakeClockGateEnabled());
EXPECT_FALSE(IsFakeClockPllEnabled());
StartSocPower();
EXPECT_TRUE(IsTdmASclkSet());
EXPECT_TRUE(IsTdmBSclkSet());
EXPECT_TRUE(IsTdmCSclkSet());
EXPECT_TRUE(IsFakeClockGateEnabled());
EXPECT_TRUE(IsFakeClockPllEnabled());
}
class AmlG12CompositeRingBufferTest : public AmlG12CompositeTest {
protected:
void SetUp() override { AmlG12CompositeTest::SetUp(); }
fidl::SyncClient<fuchsia_hardware_audio::RingBuffer> GetClient(uint64_t id) {
auto endpoints = fidl::CreateEndpoints<fuchsia_hardware_audio::RingBuffer>();
fuchsia_hardware_audio::CompositeCreateRingBufferRequest request(
id, GetDefaultRingBufferFormat(), std::move(endpoints->server));
auto ring_buffer_result = client_->CreateRingBuffer(std::move(request));
ZX_ASSERT(ring_buffer_result.is_ok());
return fidl::SyncClient<fuchsia_hardware_audio::RingBuffer>(std::move(endpoints->client));
}
void TestProperties(uint64_t id) {
auto client = GetClient(id);
fidl::Result result = client->GetProperties();
ASSERT_TRUE(result.is_ok());
ASSERT_TRUE(result->properties().needs_cache_flush_or_invalidate().has_value());
ASSERT_TRUE(*result->properties().needs_cache_flush_or_invalidate());
ASSERT_TRUE(result->properties().driver_transfer_bytes().has_value());
}
void TestGetVmo(uint64_t id) {
auto client = GetClient(id);
constexpr uint32_t kMinFrames = 1;
auto get_vmo_result =
client->GetVmo(fuchsia_hardware_audio::RingBufferGetVmoRequest(kMinFrames, 0));
ASSERT_TRUE(get_vmo_result.is_ok());
ASSERT_TRUE(get_vmo_result->ring_buffer().is_valid());
}
void TestGetVmoMultipleTimes(uint64_t id) {
auto client = GetClient(id);
constexpr uint32_t kMinFrames = 1;
auto get_vmo_result0 =
client->GetVmo(fuchsia_hardware_audio::RingBufferGetVmoRequest(kMinFrames, 0));
ASSERT_TRUE(get_vmo_result0.is_ok());
ASSERT_TRUE(get_vmo_result0->ring_buffer().is_valid());
auto get_vmo_result1 =
client->GetVmo(fuchsia_hardware_audio::RingBufferGetVmoRequest(kMinFrames, 0));
ASSERT_FALSE(get_vmo_result1.is_ok());
}
void TestStartRingBufferNoVmo(uint64_t id) {
auto client = GetClient(id);
auto start_result = client->Start();
ASSERT_FALSE(start_result.is_ok()); // Not ok to start a ring buffer with no VMO.
}
void TestStopRingBufferNoVmo(uint64_t id) {
auto client = GetClient(id);
auto stop_result = client->Stop();
ASSERT_FALSE(stop_result.is_ok()); // Not ok to stop a ring buffer with no VMO.
}
void TestCreationAndStartStop(uint64_t id) {
auto client = GetClient(id);
constexpr uint32_t kMinFrames = 8192;
auto get_vmo_result =
client->GetVmo(fuchsia_hardware_audio::RingBufferGetVmoRequest(kMinFrames, 0));
ASSERT_TRUE(get_vmo_result.is_ok());
auto start_result = client->Start();
ASSERT_TRUE(start_result.is_ok());
auto stop_result = client->Stop();
ASSERT_TRUE(stop_result.is_ok());
}
void TestStartStartedRingBuffer(uint64_t id) {
auto client = GetClient(id);
constexpr uint32_t kMinFrames = 8192;
auto get_vmo_result =
client->GetVmo(fuchsia_hardware_audio::RingBufferGetVmoRequest(kMinFrames, 0));
ASSERT_TRUE(get_vmo_result.is_ok());
auto start_result0 = client->Start();
ASSERT_TRUE(start_result0.is_ok());
auto start_result1 = client->Start();
ASSERT_FALSE(start_result1.is_ok()); // Not ok to start a started ring buffer.
}
void TestStopStoppedRingBuffer(uint64_t id) {
auto client = GetClient(id);
constexpr uint32_t kMinFrames = 8192;
auto get_vmo_result =
client->GetVmo(fuchsia_hardware_audio::RingBufferGetVmoRequest(kMinFrames, 0));
ASSERT_TRUE(get_vmo_result.is_ok());
auto start_result = client->Start();
ASSERT_TRUE(start_result.is_ok());
auto stop_result0 = client->Stop();
ASSERT_TRUE(stop_result0.is_ok());
auto stop_result1 = client->Stop();
ASSERT_TRUE(stop_result1.is_ok()); // Ok to stop a stopped ring buffer.
}
void TestGetDelay(uint64_t id) {
auto client = GetClient(id);
auto delay_result = client->WatchDelayInfo();
ASSERT_TRUE(delay_result.is_ok());
ASSERT_TRUE(delay_result->delay_info().internal_delay().has_value()); // Some delay reported.
}
};
TEST_F(AmlG12CompositeRingBufferTest, RingBufferProperties) {
for (auto& id : kAllValidIds) {
TestProperties(id);
}
}
TEST_F(AmlG12CompositeRingBufferTest, RingBufferGetVmo) {
for (auto& id : kAllValidIds) {
TestGetVmo(id);
}
}
TEST_F(AmlG12CompositeRingBufferTest, RingBufferGetVmoMultipleTimes) {
for (auto& id : kAllValidIds) {
TestGetVmoMultipleTimes(id);
}
}
TEST_F(AmlG12CompositeRingBufferTest, RingBuffersStartStop) {
for (auto& id : kAllValidIds) {
TestCreationAndStartStop(id);
}
}
TEST_F(AmlG12CompositeRingBufferTest, RingBuffersClientCloseChannel) {
for (auto& id : kAllValidIds) {
auto endpoints = fidl::CreateEndpoints<fuchsia_hardware_audio::RingBuffer>();
fuchsia_hardware_audio::CompositeCreateRingBufferRequest request(
id, GetDefaultRingBufferFormat(), std::move(endpoints->server));
auto ring_buffer_result = client_->CreateRingBuffer(std::move(request));
ASSERT_TRUE(ring_buffer_result.is_ok());
endpoints->client.TakeChannel().reset();
}
}
TEST_F(AmlG12CompositeRingBufferTest, RingBufferStartStartedRingBuffer) {
for (auto& id : kAllValidIds) {
TestStartStartedRingBuffer(id);
}
}
TEST_F(AmlG12CompositeRingBufferTest, RingBufferStopStoppedRingBuffer) {
for (auto& id : kAllValidIds) {
TestStopStoppedRingBuffer(id);
}
}
TEST_F(AmlG12CompositeRingBufferTest, RingBufferGetDelay) {
for (auto& id : kAllValidIds) {
TestGetDelay(id);
}
}
} // namespace audio::aml_g12