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/debug.h>
6#include <zircon/device/audio-codec.h>
7#include <zircon/device/i2c.h>
8#include <zircon/assert.h>
9
10#include <fbl/alloc_checker.h>
11
12#include "max98927.h"
13#include "max98927-registers.h"
14
15namespace audio {
16namespace max98927 {
17
18uint8_t Max98927Device::ReadReg(uint16_t addr) {
19    uint16_t buf = htobe16(addr);
20    uint8_t val = 0;
21    zx_status_t status = i2c_write_read_sync(&i2c_, &buf, sizeof(buf), &val, sizeof(val));
22    if (status != ZX_OK) {
23        zxlogf(ERROR, "max98927: could not read reg addr: 0x%04X  status: %d\n", addr, status);
24        return -1;
25    }
26
27    zxlogf(SPEW, "max98927: register 0x%04x read 0x%02x\n", addr, val);
28    return val;
29}
30
31void Max98927Device::WriteReg(uint16_t addr, uint8_t val) {
32    uint8_t buf[3];
33    uint16_t* p = reinterpret_cast<uint16_t*>(buf);
34    *p = htobe16(addr);
35    buf[2] = val;
36    zx_status_t status = i2c_write_sync(&i2c_, buf, sizeof(buf));
37    if (status != ZX_OK) {
38        zxlogf(ERROR, "alc5514: could not write reg addr/val: 0x%04x/0x%02x  status: %d\n", addr,
39               val, status);
40    }
41    zxlogf(SPEW, "max98927: register 0x%04x write 0x%02x\n", addr, val);
42}
43
44void Max98927Device::DumpRegs() {
45    constexpr uint16_t first = INTERRUPT_RAW_1;
46    constexpr uint16_t last = GLOBAL_ENABLE;
47
48    // read all registers
49    // segments followed by write data (first register)
50    // the address pointer is automatically incremented after each byte read
51    uint16_t buf = htobe16(first);
52    uint8_t out[last];
53    zx_status_t status = i2c_write_read_sync(&i2c_, &buf, sizeof(buf), out, sizeof(out));
54    if (status != ZX_OK) {
55        zxlogf(ERROR, "max98927: could not read regs status: %d\n", status);
56    }
57
58    zxlogf(INFO, "max98927: register dump\n");
59    for (uint16_t i = 0; i < last; i++) {
60        zxlogf(INFO, "    [%04x]: 0x%02x\n", i + 1, out[i]);
61    }
62}
63
64zx_status_t Max98927Device::DdkIoctl(uint32_t op, const void* in_buf, size_t in_len,
65                                     void* out_buf, size_t out_len, size_t* actual) {
66    if (op != IOCTL_AUDIO_CODEC_ENABLE) {
67        return ZX_ERR_NOT_SUPPORTED;
68    }
69    if (in_len < sizeof(bool)) {
70        return ZX_ERR_INVALID_ARGS;
71    }
72    const bool* enable = static_cast<const bool*>(in_buf);
73    if (*enable) {
74        Enable();
75    } else {
76        Disable();
77    }
78    return ZX_OK;
79}
80
81void Max98927Device::DdkUnbind() {
82}
83
84void Max98927Device::DdkRelease() {
85    delete this;
86}
87
88void Max98927Device::Test() {
89    // PCM config - slave mode
90    WriteReg(PCM_MASTER_MODE, 0);
91
92    // PCM config - 48kHz 16-bits
93    WriteReg(PCM_SAMPLE_RATE_SETUP_1, PCM_SAMPLE_RATE_SETUP_1_DIG_IF_SR(0x8));
94    WriteReg(PCM_SAMPLE_RATE_SETUP_2, PCM_SAMPLE_RATE_SETUP_2_SPK_SR(0x8) |
95                                      PCM_SAMPLE_RATE_SETUP_2_IVADC_SR(0x8));
96    WriteReg(PCM_MODE_CFG, PCM_MODE_CFG_CHANSZ_16BITS | 0x3);
97    WriteReg(PCM_CLOCK_SETUP, 0x2);
98
99    // Enable TX channels
100    WriteReg(PCM_RX_EN_A, 0x3);
101
102    // Set speaker source to tone generator
103    WriteReg(SPK_SRC_SEL, SPK_SRC_SEL_TONE_GEN);
104
105    // Generate a tone. Must do before AMP_ENABLE.AMP_ENABLE_EN and BROWNOUT_EN.AMP_DSP_EN.
106    WriteReg(TONE_GEN_DC_CFG, 0x6); // fs/64 @ 48kHz = 750Hz
107
108    zxlogf(INFO, "max98927: playing test tone...\n");
109
110    // Enable for 2 secs. The datasheet recommends GLOBAL_ENABLE then AMP_ENABLE, but
111    // the part errors when the bits are toggled in that order.
112    WriteReg(AMP_ENABLE, AMP_ENABLE_EN);
113    WriteReg(GLOBAL_ENABLE, GLOBAL_ENABLE_EN);
114
115    zx_nanosleep(zx_deadline_after(ZX_SEC(2)));
116
117    WriteReg(GLOBAL_ENABLE, 0);
118    WriteReg(AMP_ENABLE, 0);
119
120    // Disable tone generator and rx paths.
121    WriteReg(TONE_GEN_DC_CFG, 0);
122    WriteReg(PCM_RX_EN_A, 0);
123
124    zxlogf(INFO, "max98927: test tone done\n");
125}
126
127void Max98927Device::Enable() {
128    // PCM config - slave mode
129    WriteReg(PCM_MASTER_MODE, 0);
130
131    // PCM config - 48kHz 16-bits TDM0
132    WriteReg(PCM_SAMPLE_RATE_SETUP_1, PCM_SAMPLE_RATE_SETUP_1_DIG_IF_SR(0x8));
133    WriteReg(PCM_SAMPLE_RATE_SETUP_2, PCM_SAMPLE_RATE_SETUP_2_SPK_SR(0x8) |
134                                      PCM_SAMPLE_RATE_SETUP_2_IVADC_SR(0x8));
135    WriteReg(PCM_MODE_CFG, PCM_MODE_CFG_CHANSZ_16BITS | PCM_MODE_CFG_FORMAT_TDM0);
136    WriteReg(PCM_CLOCK_SETUP, 0x6);
137
138    // Enable TX channels
139    WriteReg(PCM_RX_EN_A, 0x3);
140
141    // Set speaker source to DAI
142    WriteReg(SPK_SRC_SEL, 0);
143
144    // The datasheet recommends GLOBAL_ENABLE then AMP_ENABLE, but
145    // the part errors when the bits are toggled in that order.
146    WriteReg(AMP_ENABLE, AMP_ENABLE_EN);
147    WriteReg(GLOBAL_ENABLE, GLOBAL_ENABLE_EN);
148}
149
150void Max98927Device::Disable() {
151    // Disable TX channels
152    WriteReg(PCM_RX_EN_A, 0);
153
154    WriteReg(GLOBAL_ENABLE, 0);
155    WriteReg(AMP_ENABLE, 0);
156}
157
158zx_status_t Max98927Device::Initialize() {
159    // Reset device
160    WriteReg(SOFTWARE_RESET, SOFTWARE_RESET_RST);
161
162    // Set outputs to HiZ
163    WriteReg(PCM_TX_HIZ_CTRL_A, 0xFF);
164    WriteReg(PCM_TX_HIZ_CTRL_B, 0xFF);
165
166    // Default monomix output is (channel 0 + channel 1) / 2
167    // Default monomix input channel 0 is PCM RX channel 0
168    WriteReg(PCM_SPK_MONOMIX_A, PCM_SPK_MONOMIX_A_CFG_OUTPUT_0_1 |
169                                PCM_SPK_MONOMIX_B_CFG_CH0_SRC(0));
170    // Default monomix input channel 1 is PCM RX channel 1
171    WriteReg(PCM_SPK_MONOMIX_B, PCM_SPK_MONOMIX_B_CFG_CH1_SRC(1));
172
173    // Default volume (+6dB dB)
174    WriteReg(AMP_VOL_CTRL, 0x34 + 24);
175    WriteReg(SPK_GAIN, SPK_GAIN_PCM(SPK_GAIN_3DB));
176
177    // Enable DC blocking filter
178    WriteReg(AMP_DSP_CFG, AMP_DSP_CFG_DCBLK_EN);
179
180    // Enable IMON/VMON DC blocker
181    WriteReg(MEAS_DSP_CFG, MEAS_DSP_CFG_I_DCBLK(MEAS_DSP_CFG_FREQ_3_7HZ) |
182                           MEAS_DSP_CFG_V_DCBLK(MEAS_DSP_CFG_FREQ_3_7HZ) |
183                           MEAS_DSP_CFG_DITH_EN |
184                           MEAS_DSP_CFG_I_DCBLK_EN |
185                           MEAS_DSP_CFG_V_DCBLK_EN);
186
187    // Boost output voltage & current limit
188    WriteReg(BOOST_CTRL_0, 0x1C); // 10.00V
189    WriteReg(BOOST_CTRL_1, 0x3E); // 4.00A
190
191    // Measurement ADC config
192    WriteReg(MEAS_ADC_CFG, MEAS_ADC_CFG_CH2_EN);
193    WriteReg(MEAS_ADC_BASE_DIV_MSB, 0);
194    WriteReg(MEAS_ADC_BASE_DIV_LSB, 0x24);
195
196    // Brownout level
197    WriteReg(BROWNOUT_LVL4_AMP1_CTRL1, 0x06); // -6dBFS
198
199    // Envelope tracker configuration
200    WriteReg(ENV_TRACKER_VOUT_HEADROOM, 0x08); // 1.000V
201    WriteReg(ENV_TRACKER_CTRL, ENV_TRACKER_CTRL_EN);
202    WriteReg(ENV_TRACKER_BOOST_VOUT_RB, 0x10); // 8.500V
203
204    // TODO: figure out vmon-slot-no and imon-slot-no
205
206    // Set interleave mode
207    WriteReg(PCM_TX_CH_SRC_B, PCM_TX_CH_SRC_B_INTERLEAVE);
208
209    return ZX_OK;
210}
211
212zx_status_t Max98927Device::Bind() {
213    zx_status_t st = device_get_protocol(parent(), ZX_PROTOCOL_I2C, &i2c_);
214    if (st != ZX_OK) {
215        zxlogf(ERROR, "max98927: could not get I2C protocol: %d\n", st);
216        return st;
217    }
218
219    st = Initialize();
220    if (st != ZX_OK) {
221        return st;
222    }
223
224    // Power on by default...
225    Enable();
226
227    return DdkAdd("max98927");
228}
229
230fbl::unique_ptr<Max98927Device> Max98927Device::Create(zx_device_t* parent) {
231    fbl::AllocChecker ac;
232    fbl::unique_ptr<Max98927Device> ret(new (&ac) Max98927Device(parent));
233    if (!ac.check()) {
234        zxlogf(ERROR, "max98927: out of memory attempting to allocate device\n");
235        return nullptr;
236    }
237    return ret;
238}
239
240}  // namespace max98927
241}  // namespace audio
242
243extern "C" {
244zx_status_t max98927_bind_hook(void* ctx, zx_device_t* parent) {
245    auto dev = audio::max98927::Max98927Device::Create(parent);
246    if (dev == nullptr) {
247        return ZX_ERR_NO_MEMORY;
248    }
249
250    zx_status_t st = dev->Bind();
251    if (st == ZX_OK) {
252        // devmgr is now in charge of the memory for dev
253        __UNUSED auto ptr = dev.release();
254        return st;
255    }
256
257    return ZX_OK;
258}
259}  // extern "C"
260