[light][amlogic] Fix LED PWM frequency for Nelson

The LED frequency should be 2 kHz (for a period of 500 us). Add logic to
select this period on Nelson, and leave other boards with the original
period.

Bug: 103305
Test: fx test aml-light-test
Test: Used lights-cli to change LED brightness on Astro, Nelson, and
      Sherlock
Change-Id: I97960d52e61d3ec4d4c8e4513a141148afd49694
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/693836
Reviewed-by: Ruby Zhuang <rdzhuang@google.com>
Commit-Queue: Braden Kell <bradenkell@google.com>
diff --git a/src/ui/light/drivers/aml-light/BUILD.gn b/src/ui/light/drivers/aml-light/BUILD.gn
index c25caff..58940cf 100644
--- a/src/ui/light/drivers/aml-light/BUILD.gn
+++ b/src/ui/light/drivers/aml-light/BUILD.gn
@@ -30,6 +30,7 @@
     "//sdk/banjo/fuchsia.hardware.gpio:fuchsia.hardware.gpio_banjo_cpp",
     "//sdk/banjo/fuchsia.hardware.pwm:fuchsia.hardware.pwm_banjo_cpp",
     "//sdk/fidl/fuchsia.hardware.light:fuchsia.hardware.light_llcpp",
+    "//src/devices/bus/lib/device-protocol-pdev",
     "//src/devices/lib/amlogic",
     "//src/devices/lib/driver",
     "//src/lib/ddk",
@@ -62,6 +63,7 @@
     "//sdk/banjo/fuchsia.hardware.pwm:fuchsia.hardware.pwm_banjo_cpp",
     "//sdk/banjo/fuchsia.hardware.pwm:fuchsia.hardware.pwm_banjo_cpp_mock",
     "//sdk/fidl/fuchsia.hardware.light:fuchsia.hardware.light_llcpp",
+    "//src/devices/bus/lib/device-protocol-pdev",
     "//src/devices/lib/amlogic",
     "//src/devices/testing/no_ddk",
     "//src/lib/ddk",
diff --git a/src/ui/light/drivers/aml-light/aml-light-test.cc b/src/ui/light/drivers/aml-light/aml-light-test.cc
index ea48ea3..9b038cc 100644
--- a/src/ui/light/drivers/aml-light/aml-light-test.cc
+++ b/src/ui/light/drivers/aml-light/aml-light-test.cc
@@ -26,7 +26,8 @@
 class FakeAmlLight : public AmlLight {
  public:
   static std::unique_ptr<FakeAmlLight> Create(const gpio_protocol_t* gpio,
-                                              std::optional<const pwm_protocol_t*> pwm) {
+                                              std::optional<const pwm_protocol_t*> pwm,
+                                              zx_duration_t pwm_period = 170'625) {
     fbl::AllocChecker ac;
     auto device = fbl::make_unique_checked<FakeAmlLight>(&ac);
     if (!ac.check()) {
@@ -35,7 +36,8 @@
     }
     device->lights_.emplace_back(
         "test", ddk::GpioProtocolClient(gpio),
-        pwm.has_value() ? std::optional<ddk::PwmProtocolClient>(*pwm) : std::nullopt);
+        pwm.has_value() ? std::optional<ddk::PwmProtocolClient>(*pwm) : std::nullopt,
+        zx::duration(pwm_period));
     EXPECT_OK(device->lights_.back().Init(true));
     return device;
   }
@@ -279,4 +281,42 @@
   }
 }
 
+TEST_F(AmlLightTest, SetValueTestNelson) {
+  pwm_.ExpectEnable(ZX_OK);
+  aml_pwm::mode_config regular = {aml_pwm::ON, {}};
+  pwm_config_t config = {false, 500'000, 100.0, reinterpret_cast<uint8_t*>(&regular),
+                         sizeof(regular)};
+  pwm_.ExpectSetConfig(ZX_OK, config);
+
+  auto gpio = gpio_.GetProto();
+  auto pwm = pwm_.GetProto();
+  light_ = FakeAmlLight::Create(gpio, pwm, 500'000);
+  ASSERT_NOT_NULL(light_);
+  Init();
+
+  fidl::WireSyncClient<fuchsia_hardware_light::Light> client(std::move(client_));
+
+  {
+    config.duty_cycle = 0;
+    pwm_.ExpectSetConfig(ZX_OK, config);
+    auto set_result = client->SetBrightnessValue(0, 0.0);
+    EXPECT_OK(set_result.status());
+    EXPECT_FALSE(set_result->is_error());
+  }
+  {
+    config.duty_cycle = 20.0;
+    pwm_.ExpectSetConfig(ZX_OK, config);
+    auto set_result = client->SetBrightnessValue(0, 0.2);
+    EXPECT_OK(set_result.status());
+    EXPECT_FALSE(set_result->is_error());
+  }
+  {
+    config.duty_cycle = 100.0;
+    pwm_.ExpectSetConfig(ZX_OK, config);
+    auto set_result = client->SetBrightnessValue(0, 1.0);
+    EXPECT_OK(set_result.status());
+    EXPECT_FALSE(set_result->is_error());
+  }
+}
+
 }  // namespace aml_light
diff --git a/src/ui/light/drivers/aml-light/aml-light.cc b/src/ui/light/drivers/aml-light/aml-light.cc
index 8103775..5ee4ead 100644
--- a/src/ui/light/drivers/aml-light/aml-light.cc
+++ b/src/ui/light/drivers/aml-light/aml-light.cc
@@ -6,6 +6,7 @@
 
 #include <lib/ddk/metadata.h>
 #include <lib/ddk/platform-defs.h>
+#include <lib/device-protocol/pdev.h>
 #include <string.h>
 
 #include <cmath>
@@ -22,7 +23,10 @@
 
 constexpr double kMaxBrightness = 1.0;
 constexpr double kMinBrightness = 0.0;
-constexpr uint32_t kPwmPeriodNs = 170625;
+constexpr zx::duration kPwmPeriod = zx::nsec(170'625);
+constexpr zx::duration kNelsonPwmPeriod = zx::nsec(500'000);
+static_assert(kPwmPeriod.to_nsecs() <= UINT32_MAX);
+static_assert(kNelsonPwmPeriod.to_nsecs() <= UINT32_MAX);
 
 }  // namespace
 
@@ -62,7 +66,7 @@
   aml_pwm::mode_config regular = {aml_pwm::ON, {}};
   pwm_config_t config = {
       .polarity = false,
-      .period_ns = kPwmPeriodNs,
+      .period_ns = static_cast<uint32_t>(pwm_period_.to_nsecs()),
       .duty_cycle = static_cast<float>(value * 100.0 / (kMaxBrightness * 1.0)),
       .mode_config_buffer = reinterpret_cast<uint8_t*>(&regular),
       .mode_config_size = sizeof(regular),
@@ -208,6 +212,17 @@
     return ZX_ERR_INTERNAL;
   }
 
+  ddk::PDev pdev(parent(), "pdev");
+  pdev_board_info_t board_info = {};
+  status = ZX_OK;
+  if (!pdev.is_valid() || (status = pdev.GetBoardInfo(&board_info)) != ZX_OK) {
+    board_info.pid = PDEV_PID_GENERIC;
+  }
+
+  const zx::duration pwm_period = board_info.pid == PDEV_PID_NELSON ? kNelsonPwmPeriod : kPwmPeriod;
+
+  zxlogf(INFO, "PWM period: %ld ns", pwm_period.to_nsecs());
+
   uint32_t count = 1;
   for (uint32_t i = 0; i < configs->size(); i++) {
     auto* config = &configs.value()[i];
@@ -227,9 +242,9 @@
         return status;
       }
       count++;
-      lights_.emplace_back(name, gpio, pwm);
+      lights_.emplace_back(name, gpio, pwm, pwm_period);
     } else {
-      lights_.emplace_back(name, gpio, std::nullopt);
+      lights_.emplace_back(name, gpio, std::nullopt, pwm_period);
     }
 
     if ((status = lights_.back().Init(config->init_on)) != ZX_OK) {
diff --git a/src/ui/light/drivers/aml-light/aml-light.h b/src/ui/light/drivers/aml-light/aml-light.h
index 6e28b19..08861ba 100644
--- a/src/ui/light/drivers/aml-light/aml-light.h
+++ b/src/ui/light/drivers/aml-light/aml-light.h
@@ -33,8 +33,8 @@
 class LightDevice {
  public:
   LightDevice(std::string name, ddk::GpioProtocolClient gpio,
-              std::optional<ddk::PwmProtocolClient> pwm)
-      : name_(std::move(name)), gpio_(gpio), pwm_(pwm) {}
+              std::optional<ddk::PwmProtocolClient> pwm, zx::duration pwm_period)
+      : name_(std::move(name)), gpio_(gpio), pwm_(pwm), pwm_period_(pwm_period) {}
 
   zx_status_t Init(bool init_on);
 
@@ -53,6 +53,7 @@
   std::optional<ddk::PwmProtocolClient> pwm_;
 
   double value_ = 0;
+  const zx::duration pwm_period_;
 };
 
 class AmlLight : public AmlLightType, public ddk::EmptyProtocol<ZX_PROTOCOL_LIGHT> {