1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <string.h>
6 
7 #include <ddk/protocol/i2c-lib.h>
8 
9 #include <fbl/algorithm.h>
10 
11 #include "tas5720.h"
12 
13 namespace audio {
14 
15 constexpr float Tas5720::kMaxGain;
16 constexpr float Tas5720::kMinGain;
17 // clang-format off
18 constexpr uint8_t kRegPowerControl        = 0x01;
19 constexpr uint8_t kRegDigitalControl1     = 0x02;
20 constexpr uint8_t kRegDigitalControl2     = 0x03;
21 constexpr uint8_t kRegVolumeControl       = 0x04;
22 constexpr uint8_t kRegAnalogControl       = 0x06;
23 constexpr uint8_t kRegFaultCfgErrorStatus = 0x08;
24 constexpr uint8_t kRegDigitalClipper2     = 0x10;
25 constexpr uint8_t kRegDigitalClipper1     = 0x11;
26 // clang-format on
27 
28 // static
Create(const pdev_protocol_t & pdev,uint32_t index)29 fbl::unique_ptr<Tas5720> Tas5720::Create(const pdev_protocol_t& pdev, uint32_t index) {
30     size_t actual;
31     i2c_protocol_t i2c;
32     zx_status_t status = pdev_get_protocol(&pdev, ZX_PROTOCOL_I2C, index, &i2c,
33                                            sizeof(i2c_protocol_t), &actual);
34     if (status != ZX_OK) {
35         zxlogf(ERROR, "%s pdev_get_protocol failed %d\n", __FUNCTION__, status);
36         return nullptr;
37     }
38 
39     fbl::AllocChecker ac;
40     auto ptr = fbl::make_unique_checked<Tas5720>(&ac, i2c);
41     if (!ac.check()) {
42         return nullptr;
43     }
44     return ptr;
45 }
46 
Reset()47 zx_status_t Tas5720::Reset() {
48     return ZX_OK;
49 }
50 
SetGain(float gain)51 zx_status_t Tas5720::SetGain(float gain) {
52     gain = fbl::clamp(gain, kMinGain, kMaxGain);
53     // Datasheet: "DVC [Hex Value] = 0xCF + (DVC [dB] / 0.5 [dB] )".
54     uint8_t gain_reg = static_cast<uint8_t>(0xCF + gain / .5f);
55     zx_status_t status;
56     status = WriteReg(kRegVolumeControl, gain_reg);
57     if (status != ZX_OK) {
58         return status;
59     }
60     current_gain_ = gain;
61     return status;
62 }
63 
ValidGain(float gain)64 bool Tas5720::ValidGain(float gain) {
65     return (gain <= kMaxGain) && (gain >= kMinGain);
66 }
67 
Init(uint8_t slot)68 zx_status_t Tas5720::Init(uint8_t slot) {
69     Standby();
70     WriteReg(kRegDigitalControl1, 0x45);        // Use Slot, Stereo Left Justified.
71     WriteReg(kRegDigitalControl2, slot & 0x07); // Slot.
72     WriteReg(kRegAnalogControl, 0x55);          // PWM rate 16 x lrclk, gain 20.7 dBV.
73     WriteReg(kRegDigitalClipper2, 0xFF);        // Disabled.
74     WriteReg(kRegDigitalClipper1, 0xFC);        // Disabled.
75     ExitStandby();
76     uint8_t val = 0;
77     ReadReg(kRegFaultCfgErrorStatus, &val);
78     if (val != 0x00) {
79         return ZX_ERR_INTERNAL;
80     }
81     return ZX_OK;
82 }
83 
Standby()84 zx_status_t Tas5720::Standby() {
85     uint8_t r;
86     ReadReg(kRegPowerControl, &r);
87     r = static_cast<uint8_t>(r & ~(0x01)); // SPK_SD.
88     r = static_cast<uint8_t>(r | (0x02));  // SPK_SLEEP.
89     WriteReg(kRegPowerControl, r);
90     return ZX_OK;
91 }
92 
ExitStandby()93 zx_status_t Tas5720::ExitStandby() {
94     uint8_t r;
95     ReadReg(kRegPowerControl, &r);
96     r = static_cast<uint8_t>(r | 0x01); // SPK_SD.
97     WriteReg(kRegPowerControl, r);
98     r = static_cast<uint8_t>(r & ~(0x02)); // SPK_SLEEP.
99     WriteReg(kRegPowerControl, r);
100     return ZX_OK;
101 }
102 
WriteReg(uint8_t reg,uint8_t value)103 zx_status_t Tas5720::WriteReg(uint8_t reg, uint8_t value) {
104     uint8_t write_buf[2];
105     write_buf[0] = reg;
106     write_buf[1] = value;
107     return i2c_write_sync(&i2c_, write_buf, 2);
108 }
109 
ReadReg(uint8_t reg,uint8_t * value)110 zx_status_t Tas5720::ReadReg(uint8_t reg, uint8_t* value) {
111     return i2c_write_read_sync(&i2c_, &reg, 1, value, 1);
112 }
113 } // audio
114