1 // Copyright 2018 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 <ddk/protocol/i2c.h>
6 #include <fbl/algorithm.h>
7 #include <fbl/alloc_checker.h>
8 
9 #include <utility>
10 
11 #include "tas27xx.h"
12 
13 namespace audio {
14 namespace astro {
15 
16 // static
Create(ddk::I2cChannel && i2c)17 fbl::unique_ptr<Tas27xx> Tas27xx::Create(ddk::I2cChannel&& i2c) {
18     fbl::AllocChecker ac;
19 
20     auto ptr = fbl::unique_ptr<Tas27xx>(new (&ac) Tas27xx(std::move(i2c)));
21     if (!ac.check()) {
22         return nullptr;
23     }
24 
25     return ptr;
26 }
27 
Reset()28 zx_status_t Tas27xx::Reset() {
29     return WriteReg(SW_RESET, 0x01);
30 }
31 
SetGain(float gain)32 zx_status_t Tas27xx::SetGain(float gain) {
33     gain = fbl::clamp(gain, GetMinGain(), GetMaxGain());
34     uint8_t gain_reg = static_cast<uint8_t>(-gain / kGainStep);
35 
36     zx_status_t status;
37     status = WriteReg(PB_CFG2, gain_reg);
38     if (status == ZX_OK) {
39         current_gain_ = gain;
40     }
41     return status;
42 }
43 
ValidGain(float gain)44 bool Tas27xx::ValidGain(float gain) {
45     return (gain <= kMaxGain) && (gain >= kMinGain);
46 }
47 
Init()48 zx_status_t Tas27xx::Init() {
49     zx_status_t status;
50 
51     //Put part in active, but muted state
52     Standby();
53 
54     // 128 clocks per frame, manually configure dividers
55     status = WriteReg(CLOCK_CFG, (0x06 << 2) | 1);
56     if (status != ZX_OK)
57         return status;
58 
59     // 48kHz, FSYNC on high to low transition
60     // Disable autorate detection
61     status = WriteReg(TDM_CFG0, (1 << 4) | (0x03 << 1) | 1);
62     if (status != ZX_OK)
63         return status;
64 
65     // Left justified, offset 0 bclk, clock on falling edge of sclk
66     //  our fsync is on falling edge, so first bit after falling edge is valid
67     status = WriteReg(TDM_CFG1, (0 << 1) | 1);
68     if (status != ZX_OK)
69         return status;
70 
71     // Mono (L+R)/2, 32bit sample, 32bit slot
72     status = WriteReg(TDM_CFG2, (0x03 << 4) | (0x00 << 2) | 0x03);
73     if (status != ZX_OK)
74         return status;
75 
76     // Left channel slot 0, Right channel slot 1
77     status = WriteReg(TDM_CFG3, (1 << 4) | 0);
78     if (status != ZX_OK)
79         return status;
80 
81     // Initial gain -20db
82     SetGain(-20);
83 
84     // Disable v and i sense, enter active mode
85     status = WriteReg(PWR_CTL, (0x03 << 2));
86     if (status != ZX_OK)
87         return status;
88 
89     return ZX_OK;
90 }
91 
92 //Standby puts the part in active, but muted state
Standby()93 zx_status_t Tas27xx::Standby() {
94     return WriteReg(PWR_CTL, (0x03 << 2) | 0x01);
95 }
96 
ExitStandby()97 zx_status_t Tas27xx::ExitStandby() {
98     return WriteReg(PWR_CTL, (0x03 << 2));
99 }
100 
ReadReg(uint8_t reg)101 uint8_t Tas27xx::ReadReg(uint8_t reg) {
102     uint8_t val;
103     i2c_.ReadSync(reg, &val, 1);
104     return val;
105 }
106 
WriteReg(uint8_t reg,uint8_t value)107 zx_status_t Tas27xx::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_.WriteSync(write_buf, 2);
112 }
113 } //namespace astro
114 } //namespace audio
115