blob: c5c51e46f13182f9d7b670db08ec378fe0509532 [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/devices/board/drivers/sherlock/post-init/post-init.h"
#include <fidl/fuchsia.hardware.gpio/cpp/fidl.h>
#include <lib/driver/component/cpp/driver_export.h>
namespace {
constexpr std::array<const char*, 3> kBoardBuildNodeNames = {
"hw-id-0",
"hw-id-1",
"hw-id-2",
};
constexpr std::array<const char*, 2> kBoardOptionNodeNames = {
"hw-id-3",
"hw-id-4",
};
constexpr std::array<const char*, 1> kDisplayVendorNodeNames = {
"disp-soc-id1",
};
constexpr std::array<const char*, 1> kDdicVersionNodeNames = {
"disp-soc-id2",
};
} // namespace
namespace sherlock {
void PostInit::Start(fdf::StartCompleter completer) {
parent_.Bind(std::move(node()));
zx::result pbus =
incoming()->Connect<fuchsia_hardware_platform_bus::Service::PlatformBus>("pbus");
if (pbus.is_error()) {
FDF_LOG(ERROR, "Failed to connect to PlatformBus: %s", pbus.status_string());
return completer(pbus.take_error());
}
pbus_.Bind(*std::move(pbus));
auto args = fuchsia_driver_framework::NodeAddArgs({.name = "post-init"});
zx::result controller_endpoints =
fidl::CreateEndpoints<fuchsia_driver_framework::NodeController>();
if (controller_endpoints.is_error()) {
FDF_LOG(ERROR, "Failed to create controller endpoints: %s",
controller_endpoints.status_string());
return completer(controller_endpoints.take_error());
}
controller_.Bind(std::move(controller_endpoints->client));
if (zx::result result = InitBoardInfo(); result.is_error()) {
return completer(result.take_error());
}
if (zx::result result = SetBoardInfo(); result.is_error()) {
return completer(result.take_error());
}
if (zx::result result = InitDisplay(); result.is_error()) {
return completer(result.take_error());
}
if (zx::result result = InitTouch(); result.is_error()) {
return completer(result.take_error());
}
if (zx::result result = SetInspectProperties(); result.is_error()) {
return completer(result.take_error());
}
auto result = parent_->AddChild({std::move(args), std::move(controller_endpoints->server), {}});
if (result.is_error()) {
if (result.error_value().is_framework_error()) {
FDF_LOG(ERROR, "Failed to add child: %s",
result.error_value().framework_error().FormatDescription().c_str());
return completer(zx::error(result.error_value().framework_error().status()));
}
if (result.error_value().is_domain_error()) {
FDF_LOG(ERROR, "Failed to add child");
return completer(zx::error(ZX_ERR_INTERNAL));
}
}
return completer(zx::ok());
}
zx::result<> PostInit::InitBoardInfo() {
if (zx::result<uint8_t> board_build = ReadGpios(kBoardBuildNodeNames); board_build.is_ok()) {
board_build_ = static_cast<SherlockBoardBuild>(*board_build);
} else {
return board_build.take_error();
}
if (zx::result<uint8_t> board_option = ReadGpios(kBoardOptionNodeNames); board_option.is_ok()) {
board_option_ = *board_option;
} else {
return board_option.take_error();
}
if (zx::result<uint8_t> display_vendor = ReadGpios(kDisplayVendorNodeNames);
display_vendor.is_ok()) {
display_vendor_ = *display_vendor;
} else {
return display_vendor.take_error();
}
if (zx::result<uint8_t> ddic_version = ReadGpios(kDdicVersionNodeNames); ddic_version.is_ok()) {
ddic_version_ = *ddic_version;
} else {
return ddic_version.take_error();
}
return zx::ok();
}
zx::result<> PostInit::SetBoardInfo() {
const uint32_t board_revision = board_build_ | (board_option_ << kBoardBuildNodeNames.size());
fdf::Arena arena('PBUS');
auto board_info = fuchsia_hardware_platform_bus::wire::BoardInfo::Builder(arena)
.board_revision(board_revision)
.Build();
auto result = pbus_.buffer(arena)->SetBoardInfo(board_info);
if (!result.ok()) {
FDF_LOG(ERROR, "Call to SetBoardInfo failed: %s", result.FormatDescription().c_str());
return zx::error(result.error().status());
}
if (result->is_error()) {
FDF_LOG(ERROR, "SetBoardInfo failed: %s", zx_status_get_string(result->error_value()));
return result->take_error();
}
return zx::ok();
}
zx::result<uint8_t> PostInit::ReadGpios(cpp20::span<const char* const> node_names) {
constexpr uint64_t kAmlogicAltFunctionGpio = 0;
uint8_t value = 0;
for (size_t i = 0; i < node_names.size(); i++) {
zx::result gpio = incoming()->Connect<fuchsia_hardware_gpio::Service::Device>(node_names[i]);
if (gpio.is_error()) {
FDF_LOG(ERROR, "Failed to connect to GPIO node: %s", gpio.status_string());
return gpio.take_error();
}
fidl::SyncClient<fuchsia_hardware_gpio::Gpio> gpio_client(*std::move(gpio));
{
fidl::Result<fuchsia_hardware_gpio::Gpio::SetAltFunction> result =
gpio_client->SetAltFunction(kAmlogicAltFunctionGpio);
if (result.is_error()) {
if (result.error_value().is_framework_error()) {
FDF_LOG(ERROR, "Call to SetAltFunction failed: %s",
result.error_value().framework_error().FormatDescription().c_str());
return zx::error(result.error_value().framework_error().status());
}
if (result.error_value().is_domain_error()) {
FDF_LOG(ERROR, "SetAltFunction failed: %s",
zx_status_get_string(result.error_value().domain_error()));
return zx::error(result.error_value().domain_error());
}
FDF_LOG(ERROR, "Unknown error from call to SetAltFunction");
return zx::error(ZX_ERR_BAD_STATE);
}
}
{
fidl::Result<fuchsia_hardware_gpio::Gpio::ConfigIn> result =
gpio_client->ConfigIn(fuchsia_hardware_gpio::GpioFlags::kNoPull);
if (result.is_error()) {
if (result.error_value().is_framework_error()) {
FDF_LOG(ERROR, "Call to ConfigIn failed: %s",
result.error_value().framework_error().FormatDescription().c_str());
return zx::error(result.error_value().framework_error().status());
}
if (result.error_value().is_domain_error()) {
FDF_LOG(ERROR, "ConfigIn failed: %s",
zx_status_get_string(result.error_value().domain_error()));
return zx::error(result.error_value().domain_error());
}
FDF_LOG(ERROR, "Unknown error from call to ConfigIn");
return zx::error(ZX_ERR_BAD_STATE);
}
}
{
fidl::Result<fuchsia_hardware_gpio::Gpio::Read> result = gpio_client->Read();
if (result.is_error()) {
if (result.error_value().is_framework_error()) {
FDF_LOG(ERROR, "Call to Read failed: %s",
result.error_value().framework_error().FormatDescription().c_str());
return zx::error(result.error_value().framework_error().status());
}
if (result.error_value().is_domain_error()) {
FDF_LOG(ERROR, "Read failed: %s",
zx_status_get_string(result.error_value().domain_error()));
return zx::error(result.error_value().domain_error());
}
FDF_LOG(ERROR, "Unknown error from call to Read");
return zx::error(ZX_ERR_BAD_STATE);
}
if (result->value()) {
value |= 1 << i;
}
}
}
return zx::ok(value);
}
zx::result<> PostInit::SetInspectProperties() {
auto inspect_sink = incoming()->Connect<fuchsia_inspect::InspectSink>();
if (inspect_sink.is_error() || !inspect_sink->is_valid()) {
FDF_LOG(ERROR, "Failed to connect to InspectSink: %s", inspect_sink.status_string());
return inspect_sink.take_error();
}
component_inspector_ = std::make_unique<inspect::ComponentInspector>(
dispatcher(), inspect::PublishOptions{.inspector = inspector_,
.client_end = std::move(inspect_sink.value())});
root_ = inspector_.GetRoot().CreateChild("sherlock_board_driver");
board_rev_property_ = root_.CreateUint("board_build", board_build_);
board_option_property_ = root_.CreateUint("board_option", board_option_);
// PANEL_DETECT -> DISP_SOC_ID1
// DDIC_DETECT -> DISP_SOC_ID2
display_id_property_ = root_.CreateUint("display_id", display_vendor_ | (ddic_version_ << 1));
return zx::ok();
}
} // namespace sherlock
FUCHSIA_DRIVER_EXPORT(sherlock::PostInit);