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