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_, ®, 1, value, 1);
112 }
113 } // audio
114