| // Copyright 2018 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 "ti-lp8556.h" |
| |
| #include <ddk/binding.h> |
| #include <ddk/debug.h> |
| #include <ddk/platform-defs.h> |
| #include <ddk/protocol/i2c-lib.h> |
| #include <ddk/protocol/platform/bus.h> |
| |
| #include <fbl/alloc_checker.h> |
| #include <fbl/unique_ptr.h> |
| |
| namespace ti { |
| |
| namespace { |
| constexpr uint8_t kBacklightControlReg = 0x0; |
| constexpr uint8_t kDeviceControlReg = 0x1; |
| constexpr uint8_t kCfg2Reg = 0xA2; |
| |
| constexpr uint8_t kBacklightOn = 0x05; |
| constexpr uint8_t kBacklightOff = 0x04; |
| constexpr uint8_t kCfg2Default = 0x20; |
| } // namespace |
| |
| zx_status_t Lp8556Device::Bind() { |
| zx_status_t status = device_get_protocol(parent_, ZX_PROTOCOL_PDEV, &pdev_); |
| if (status != ZX_OK) { |
| LOG_ERROR("Could not get parent protocol\n"); |
| return status; |
| } |
| |
| // Obtain I2C protocol needed to control backlight |
| status = device_get_protocol(parent_, ZX_PROTOCOL_I2C, &i2c_); |
| if (status != ZX_OK) { |
| LOG_ERROR("Could not obtain I2C protocol\n"); |
| return status; |
| } |
| |
| status = DdkAdd("ti-lp8556"); |
| if (status != ZX_OK) { |
| LOG_ERROR("Could not add device\n"); |
| return status; |
| } |
| return ZX_OK; |
| } |
| |
| |
| void Lp8556Device::DdkUnbind() { |
| DdkRemove(); |
| } |
| |
| void Lp8556Device::DdkRelease() { |
| delete this; |
| } |
| |
| zx_status_t Lp8556Device::GetBacklightState(bool* power, uint8_t* brightness) { |
| *power = power_; |
| *brightness = brightness_; |
| return ZX_OK; |
| } |
| |
| zx_status_t Lp8556Device::SetBacklightState(bool power, uint8_t brightness) { |
| if (brightness != brightness_) { |
| uint8_t buf[2]; |
| buf[0] = kBacklightControlReg; |
| buf[1] = brightness; |
| i2c_write_sync(&i2c_, buf, 2); |
| } |
| |
| if (power != power_) { |
| uint8_t buf[2]; |
| buf[0] = kDeviceControlReg; |
| buf[1] = power? kBacklightOn : kBacklightOff; |
| i2c_write_sync(&i2c_, buf, 2); |
| if (power) { |
| buf[0] = kCfg2Reg; |
| buf[1] = kCfg2Default; |
| i2c_write_sync(&i2c_, buf, 2); |
| } |
| } |
| |
| // update internal values |
| power_ = power; |
| brightness_ = brightness; |
| return ZX_OK; |
| } |
| |
| static zx_status_t GetState(void* ctx, fidl_txn_t* txn) { |
| zircon_backlight_State state; |
| auto& self = *static_cast<ti::Lp8556Device*>(ctx); |
| self.GetBacklightState(&state.on, &state.brightness); |
| return zircon_backlight_DeviceGetState_reply(txn, &state); |
| } |
| |
| static zx_status_t SetState(void* ctx, const zircon_backlight_State* state) { |
| auto& self = *static_cast<ti::Lp8556Device*>(ctx); |
| self.SetBacklightState(state->on, state->brightness); |
| return ZX_OK; |
| } |
| |
| static zircon_backlight_Device_ops_t fidl_ops = { |
| .GetState = GetState, |
| .SetState = SetState, |
| }; |
| |
| zx_status_t Lp8556Device::DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn) { |
| return zircon_backlight_Device_dispatch(this, txn, msg, &fidl_ops); |
| } |
| |
| zx_status_t ti_lp8556_bind(void* ctx, zx_device_t* parent) { |
| fbl::AllocChecker ac; |
| auto dev = fbl::make_unique_checked<ti::Lp8556Device>(&ac, parent); |
| if (!ac.check()) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| auto status = dev->Bind(); |
| if (status != ZX_OK) { |
| // devmgr is now in charge of memory for dev |
| __UNUSED auto ptr = dev.release(); |
| } |
| return status; |
| } |
| |
| static zx_driver_ops_t ti_lp8556_driver_ops = [](){ |
| zx_driver_ops_t ops; |
| ops.version = DRIVER_OPS_VERSION; |
| ops.bind = ti_lp8556_bind; |
| return ops; |
| }(); |
| |
| } // namespace ti |
| |
| // clang-format off |
| ZIRCON_DRIVER_BEGIN(ti_lp8556, ti::ti_lp8556_driver_ops, "TI-LP8556", "0.1", 3) |
| BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_TI), |
| BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_TI_LP8556), |
| BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_TI_BACKLIGHT), |
| ZIRCON_DRIVER_END(ti_lp8556) |
| // clang-format on |