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 "tas5760.h"
12
13 namespace audio {
14
15 constexpr float Tas5760::kMaxGain;
16 constexpr float Tas5760::kMinGain;
17 // clang-format off
18 constexpr uint8_t kRegPowerControl = 0x01;
19 constexpr uint8_t kRegDigitalControl = 0x02;
20 constexpr uint8_t kRegVolumeControlCnf = 0x03;
21 constexpr uint8_t kRegLeftControl = 0x04;
22 constexpr uint8_t kRegRightControl = 0x05;
23 constexpr uint8_t kRegAnalogControl = 0x06;
24 constexpr uint8_t kRegFaultCfgErrorStatus = 0x08;
25 constexpr uint8_t kRegDigitalClipper2 = 0x10;
26 constexpr uint8_t kRegDigitalClipper1 = 0x11;
27 // clang-format on
28
29 // static
Create(const pdev_protocol_t & pdev,uint32_t index)30 fbl::unique_ptr<Tas5760> Tas5760::Create(const pdev_protocol_t& pdev, uint32_t index) {
31 size_t actual;
32 i2c_protocol_t i2c;
33 zx_status_t status = pdev_get_protocol(&pdev, ZX_PROTOCOL_I2C, index, &i2c,
34 sizeof(i2c_protocol_t), &actual);
35 if (status != ZX_OK) {
36 zxlogf(ERROR, "%s pdev_get_protocol failed %d\n", __FUNCTION__, status);
37 return nullptr;
38 }
39
40 fbl::AllocChecker ac;
41 auto ptr = fbl::make_unique_checked<Tas5760>(&ac, i2c);
42 if (!ac.check()) {
43 return nullptr;
44 }
45 return ptr;
46 }
47
Reset()48 zx_status_t Tas5760::Reset() {
49 return ZX_OK;
50 }
51
SetGain(float gain)52 zx_status_t Tas5760::SetGain(float gain) {
53 gain = fbl::clamp(gain, kMinGain, kMaxGain);
54 // Datasheet: "DVC [Hex Value] = 0xCF + (DVC [dB] / 0.5 [dB] )".
55 uint8_t gain_reg = static_cast<uint8_t>(0xCF + gain / .5f);
56 zx_status_t status = WriteReg(kRegLeftControl, gain_reg);
57 if (status != ZX_OK) {
58 return status;
59 }
60 status = WriteReg(kRegRightControl, gain_reg);
61 if (status != ZX_OK) {
62 return status;
63 }
64 current_gain_ = gain;
65 return status;
66 }
67
ValidGain(float gain)68 bool Tas5760::ValidGain(float gain) {
69 return (gain <= kMaxGain) && (gain >= kMinGain);
70 }
71
Init()72 zx_status_t Tas5760::Init() {
73 Standby();
74 WriteReg(kRegDigitalControl, 0x05); // no HPF, no boost, Single Speed, Stereo Left Justified.
75 WriteReg(kRegVolumeControlCnf, 0x80); // Fade enabled.
76 WriteReg(kRegAnalogControl, 0x51); // PWM rate 16 x lrclk.
77 WriteReg(kRegDigitalClipper2, 0xFF); // Disabled.
78 WriteReg(kRegDigitalClipper1, 0xFC); // Disabled.
79 ExitStandby();
80 uint8_t val = 0;
81 ReadReg(kRegFaultCfgErrorStatus, &val);
82 if (val != 0x00) {
83 return ZX_ERR_INTERNAL;
84 }
85 return ZX_OK;
86 }
87
Standby()88 zx_status_t Tas5760::Standby() {
89 uint8_t r;
90 ReadReg(kRegPowerControl, &r);
91 r = static_cast<uint8_t>(r & ~(0x01)); // SPK_SD.
92 r = static_cast<uint8_t>(r | (0x02)); // SPK_SLEEP.
93 WriteReg(kRegPowerControl, r);
94 return ZX_OK;
95 }
96
ExitStandby()97 zx_status_t Tas5760::ExitStandby() {
98 uint8_t r;
99 ReadReg(kRegPowerControl, &r);
100 r = static_cast<uint8_t>(r | 0x01); // SPK_SD.
101 WriteReg(kRegPowerControl, r);
102 r = static_cast<uint8_t>(r & ~(0x02)); // SPK_SLEEP.
103 WriteReg(kRegPowerControl, r);
104 return ZX_OK;
105 }
106
WriteReg(uint8_t reg,uint8_t value)107 zx_status_t Tas5760::WriteReg(uint8_t reg, uint8_t value) {
108 uint8_t write_buf[2];
109 write_buf[0] = reg;
110 write_buf[1] = value;
111 return i2c_write_sync(&i2c_, write_buf, 2);
112 }
113
ReadReg(uint8_t reg,uint8_t * value)114 zx_status_t Tas5760::ReadReg(uint8_t reg, uint8_t* value) {
115 return i2c_write_read_sync(&i2c_, ®, 1, value, 1);
116 }
117 } // audio
118