blob: 1c97d95b5bbcb4956285f6d2c0295bd36dd4c3e3 [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/astro/post-init/post-init.h"
#include <fidl/fuchsia.hardware.gpio/cpp/fidl.h>
#include <lib/driver/component/cpp/driver_export.h>
#include <cinttypes>
namespace astro {
namespace {
constexpr std::array<const char*, 3> kBoardBuildNodeNames = {
"hw-id-0",
"hw-id-1",
"hw-id-2",
};
constexpr std::array<const char*, 1> kDisplayPanelVendorNodeNames = {
"disp-soc-vid",
};
enum class PanelVendor : uint8_t {
kBoe = 0,
kInnolux = 1,
};
} // namespace
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));
zx::result composite_manager =
incoming()->Connect<fuchsia_driver_framework::CompositeNodeManager>();
if (composite_manager.is_error()) {
FDF_LOG(ERROR, "Failed to connect to CompositeNodeManager: %s",
composite_manager.status_string());
return completer(composite_manager.take_error());
}
composite_manager_ = fidl::SyncClient(*std::move(composite_manager));
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 = IdentifyPanel(); 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 = InitBacklight(); 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<AstroBoardBuild>(*board_build);
} else {
return board_build.take_error();
}
if (board_build_ != BOARD_REV_DVT && board_build_ != BOARD_REV_PVT) {
FDF_LOG(ERROR, "Unsupported board revision %u", board_build_);
} else {
FDF_LOG(INFO, "Detected board rev 0x%x", board_build_);
}
return zx::ok();
}
zx::result<> PostInit::IdentifyPanel() {
zx::result<uint8_t> panel_vendor_result = ReadGpios(kDisplayPanelVendorNodeNames);
if (panel_vendor_result.is_error()) {
FDF_LOG(ERROR, "Failed to read display panel vendor: %s", panel_vendor_result.status_string());
return panel_vendor_result.take_error();
}
PanelVendor panel_vendor = static_cast<PanelVendor>(std::move(panel_vendor_result).value());
switch (panel_vendor) {
case PanelVendor::kBoe:
panel_type_ = display::PanelType::kBoeTv070wsmFitipowerJd9364Astro;
return zx::ok();
case PanelVendor::kInnolux:
panel_type_ = display::PanelType::kInnoluxP070acbFitipowerJd9364;
return zx::ok();
}
FDF_LOG(ERROR, "Invalid panel vendor: %" PRIu8, static_cast<uint8_t>(panel_vendor));
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
zx::result<> PostInit::SetBoardInfo() {
fdf::Arena arena('PBUS');
auto board_info = fuchsia_hardware_platform_bus::wire::BoardInfo::Builder(arena)
.board_revision(board_build_)
.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) {
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::SetBufferMode> result =
gpio_client->SetBufferMode(fuchsia_hardware_gpio::BufferMode::kInput);
if (result.is_error()) {
if (result.error_value().is_framework_error()) {
FDF_LOG(ERROR, "Call to SetBufferMode 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, "SetBufferMode 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 SetBufferMode");
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);
}
} // namespace astro
FUCHSIA_DRIVER_EXPORT(astro::PostInit);