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