| // Copyright 2017 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 <string.h> |
| |
| #include <ddk/protocol/i2c-lib.h> |
| |
| #include <fbl/algorithm.h> |
| |
| #include "tas5720.h" |
| |
| namespace audio { |
| |
| constexpr float Tas5720::kMaxGain; |
| constexpr float Tas5720::kMinGain; |
| // clang-format off |
| constexpr uint8_t kRegPowerControl = 0x01; |
| constexpr uint8_t kRegDigitalControl1 = 0x02; |
| constexpr uint8_t kRegDigitalControl2 = 0x03; |
| constexpr uint8_t kRegVolumeControl = 0x04; |
| constexpr uint8_t kRegAnalogControl = 0x06; |
| constexpr uint8_t kRegFaultCfgErrorStatus = 0x08; |
| constexpr uint8_t kRegDigitalClipper2 = 0x10; |
| constexpr uint8_t kRegDigitalClipper1 = 0x11; |
| // clang-format on |
| |
| // static |
| fbl::unique_ptr<Tas5720> Tas5720::Create(ddk::PDev pdev, uint32_t index) { |
| auto i2c = pdev.GetI2c(index); |
| if (i2c.is_valid()) { |
| zxlogf(ERROR, "%s pdev_get_protocol failed\n", __FUNCTION__); |
| return nullptr; |
| } |
| |
| fbl::AllocChecker ac; |
| auto ptr = fbl::make_unique_checked<Tas5720>(&ac, i2c); |
| if (!ac.check()) { |
| return nullptr; |
| } |
| return ptr; |
| } |
| |
| zx_status_t Tas5720::Reset() { |
| return ZX_OK; |
| } |
| |
| zx_status_t Tas5720::SetGain(float gain) { |
| gain = fbl::clamp(gain, kMinGain, kMaxGain); |
| // Datasheet: "DVC [Hex Value] = 0xCF + (DVC [dB] / 0.5 [dB] )". |
| uint8_t gain_reg = static_cast<uint8_t>(0xCF + gain / .5f); |
| zx_status_t status; |
| status = WriteReg(kRegVolumeControl, gain_reg); |
| if (status != ZX_OK) { |
| return status; |
| } |
| current_gain_ = gain; |
| return status; |
| } |
| |
| bool Tas5720::ValidGain(float gain) { |
| return (gain <= kMaxGain) && (gain >= kMinGain); |
| } |
| |
| zx_status_t Tas5720::Init(uint8_t slot) { |
| Standby(); |
| WriteReg(kRegDigitalControl1, 0x45); // Use Slot, Stereo Left Justified. |
| WriteReg(kRegDigitalControl2, slot & 0x07); // Slot. |
| WriteReg(kRegAnalogControl, 0x55); // PWM rate 16 x lrclk, gain 20.7 dBV. |
| WriteReg(kRegDigitalClipper2, 0xFF); // Disabled. |
| WriteReg(kRegDigitalClipper1, 0xFC); // Disabled. |
| ExitStandby(); |
| uint8_t val = 0; |
| ReadReg(kRegFaultCfgErrorStatus, &val); |
| if (val != 0x00) { |
| return ZX_ERR_INTERNAL; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t Tas5720::Standby() { |
| uint8_t r; |
| ReadReg(kRegPowerControl, &r); |
| r = static_cast<uint8_t>(r & ~(0x01)); // SPK_SD. |
| r = static_cast<uint8_t>(r | (0x02)); // SPK_SLEEP. |
| WriteReg(kRegPowerControl, r); |
| return ZX_OK; |
| } |
| |
| zx_status_t Tas5720::ExitStandby() { |
| uint8_t r; |
| ReadReg(kRegPowerControl, &r); |
| r = static_cast<uint8_t>(r | 0x01); // SPK_SD. |
| WriteReg(kRegPowerControl, r); |
| r = static_cast<uint8_t>(r & ~(0x02)); // SPK_SLEEP. |
| WriteReg(kRegPowerControl, r); |
| return ZX_OK; |
| } |
| |
| zx_status_t Tas5720::WriteReg(uint8_t reg, uint8_t value) { |
| uint8_t write_buf[2]; |
| write_buf[0] = reg; |
| write_buf[1] = value; |
| return i2c_.WriteSync(write_buf, 2); |
| } |
| |
| zx_status_t Tas5720::ReadReg(uint8_t reg, uint8_t* value) { |
| return i2c_.WriteReadSync(®, 1, value, 1); |
| } |
| } // audio |