// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include "AW8898driver.hpp" #include "board/BoardDefinitions.hpp" #include extern "C" { #include "FreeRTOS.h" #include "task.h" } #include namespace { constexpr auto softResetCommand = 0x55aa; constexpr auto readRetries = 5; constexpr auto expectedReadSize = sizeof(std::uint16_t); } // namespace namespace bsp::audio::AW8898 { std::shared_ptr i2c; std::shared_ptr gpio; drivers::I2CAddress i2cAddr; inline void FlipBytes(uint16_t *val) { std::uint16_t tmp = *val << 8; *val = (*val >> 8) | tmp; } // internal typedefs & variables struct aw8898_reg_cfg { std::uint8_t addr; std::uint16_t data; }; enum class aw8898_init { AW8898_INIT_ST = 0, AW8898_INIT_OK = 1, AW8898_INIT_NG = 2, }; struct aw8898 { aw8898_init init; aw_sel_mode mode; }; struct aw8898 g_aw8898 = { .init = aw8898_init::AW8898_INIT_ST, .mode = aw_sel_mode::OFF_MODE, }; namespace { constexpr std::size_t configurationSize = 16; constexpr std::array initialConfiguration = {{ {0x06, 0x0330}, {0x08, 0xa00e}, {0x09, 0x424a}, {0x0a, 0x03c2}, {0x0b, 0x03c2}, {0x0c, 0x3007}, {0x0d, 0x011b}, {0x0e, 0x0329}, {0x20, 0x0001}, {0x60, 0x1cbc}, {0x61, 0x0f0e}, {0x62, 0xf5b6}, {0x63, 0x307f}, {0x67, 0x007c}, {0x69, 0x0245}, {0x04, 0x0044}, }}; constexpr std::uint8_t availableRegisters[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0f, 0x21, 0x60, 0x61, 0x62}; } // namespace /******************************************************************************* * Code ******************************************************************************/ status_t WriteReg(std::uint8_t reg, std::uint16_t val) { i2cAddr.subAddress = reg; std::uint16_t tval = val; FlipBytes(reinterpret_cast(&tval)); auto sent = i2c->Write(i2cAddr, reinterpret_cast(&tval), expectedReadSize); if (sent == expectedReadSize) return kStatus_Success; else return kStatus_Fail; } status_t ReadReg(std::uint8_t reg, std::uint16_t *val) { std::uint16_t tval; i2cAddr.subAddress = reg; auto received = i2c->Read(i2cAddr, reinterpret_cast(&tval), expectedReadSize); if (received != expectedReadSize) return kStatus_Fail; FlipBytes(reinterpret_cast(&tval)); *val = tval; return kStatus_Success; } status_t ModifyReg(std::uint8_t reg, std::uint16_t mask, std::uint16_t val) { status_t retval = 0; std::uint16_t reg_val = 0; retval = ReadReg(reg, ®_val); if (retval != kStatus_Success) { return kStatus_Fail; } reg_val &= mask; reg_val |= val; retval = WriteReg(reg, reg_val); if (retval != kStatus_Success) { return kStatus_Fail; } return kStatus_Success; } status_t HwReset(void) { gpio->WritePin(static_cast(BoardDefinitions::BELL_AUDIOCODEC_RSTN_PA_PIN), LogicLow); // reset chip HAL_Delay(OperationWaitTimeMS); gpio->WritePin(static_cast(BoardDefinitions::BELL_AUDIOCODEC_RSTN_PA_PIN), LogicHigh); // clear reset HAL_Delay(OperationWaitTimeMS); g_aw8898.init = aw8898_init::AW8898_INIT_ST; return kStatus_Success; } void SoftReset(void) { WriteReg(AW8898_REG_ID, softResetCommand); LOG_DEBUG("enter %s end", __func__); } status_t ReadChipid(void) { std::uint8_t cnt = 0; std::uint16_t reg_val = 0; while (cnt < readRetries) { ReadReg(AW8898_REG_ID, ®_val); if (reg_val == AW8898_CHIP_ID) { LOG_DEBUG("this chip is Aw8898 chipid=0x%x", reg_val); return kStatus_Success; } LOG_DEBUG("%s: aw8898 chipid=0x%x error\n", __func__, reg_val); cnt++; HAL_Delay(OperationWaitTimeMS); } return kStatus_Fail; } status_t Init(const aw_i2s_channel chsel, aw_i2s_frequency rate) { status_t ret = kStatus_Success; HwReset(); HAL_Delay(OperationWaitTimeMS); ret = ReadChipid(); if (ret == kStatus_Fail) { LOG_DEBUG("please check hardware ad_pin && i2c config!!!"); return ret; } RunPwd(false); HAL_Delay(OperationWaitTimeMS); SetMode(aw_sel_mode::SPK_MODE); HwParams(chsel, rate, aw_i2s_width::WIDTH_16BITS, aw_i2s_fs::I2SBCK_32FS); HAL_Delay(OperationWaitTimeMS); CtrlState(aw_codec_mode::AW_DECODE, aw_ctrl::START); Start(); ReadAllReg(); return ret; } void setChannelSelection(aw_i2s_channel chsel) { std::uint16_t reg_value; switch (chsel) { case aw_i2s_channel::CHSEL_LEFT: reg_value = AW8898_BIT_I2SCTRL_CHS_LEFT; break; case aw_i2s_channel::CHSEL_RIGHT: reg_value = AW8898_BIT_I2SCTRL_CHS_RIGHT; break; case aw_i2s_channel::CHSEL_MONO: reg_value = AW8898_BIT_I2SCTRL_CHS_MONO; break; default: reg_value = AW8898_BIT_I2SCTRL_CHS_MONO; LOG_DEBUG("%s: chsel can not support ", __func__); break; } // set chsel ModifyReg(AW8898_REG_I2SCTRL, AW8898_BIT_I2SCTRL_CHS_MASK, reg_value); } void setSampleRate(aw_i2s_frequency rate) { std::uint16_t reg_value; switch (rate) { case aw_i2s_frequency::FREQUENCY_08K: reg_value = AW8898_BIT_I2SCTRL_SR_8K; break; case aw_i2s_frequency::FREQUENCY_11K: reg_value = AW8898_BIT_I2SCTRL_SR_11K; break; case aw_i2s_frequency::FREQUENCY_16K: reg_value = AW8898_BIT_I2SCTRL_SR_16K; break; case aw_i2s_frequency::FREQUENCY_22K: reg_value = AW8898_BIT_I2SCTRL_SR_22K; break; case aw_i2s_frequency::FREQUENCY_24K: reg_value = AW8898_BIT_I2SCTRL_SR_24K; break; case aw_i2s_frequency::FREQUENCY_32K: reg_value = AW8898_BIT_I2SCTRL_SR_32K; break; case aw_i2s_frequency::FREQUENCY_44K: reg_value = AW8898_BIT_I2SCTRL_SR_44P1K; break; case aw_i2s_frequency::FREQUENCY_48K: reg_value = AW8898_BIT_I2SCTRL_SR_48K; break; default: reg_value = AW8898_BIT_I2SCTRL_SR_48K; LOG_DEBUG("%s: rate can not support", __func__); break; } // set rate ModifyReg(AW8898_REG_I2SCTRL, AW8898_BIT_I2SCTRL_SR_MASK, reg_value); } void setBitWidth(aw_i2s_width width) { std::uint16_t reg_value; switch (width) { case aw_i2s_width::WIDTH_16BITS: reg_value = AW8898_BIT_I2SCTRL_FMS_16BIT; break; case aw_i2s_width::WIDTH_24BITS: reg_value = AW8898_BIT_I2SCTRL_FMS_24BIT; break; case aw_i2s_width::WIDTH_32BITS: reg_value = AW8898_BIT_I2SCTRL_FMS_32BIT; break; default: reg_value = AW8898_BIT_I2SCTRL_FMS_16BIT; LOG_DEBUG("%s: width can not support ", __func__); break; } // set width ModifyReg(AW8898_REG_I2SCTRL, AW8898_BIT_I2SCTRL_FMS_MASK, reg_value); } void setFs(aw_i2s_fs fs) { std::uint16_t reg_value; switch (fs) { case aw_i2s_fs::I2SBCK_32FS: reg_value = AW8898_BIT_I2SCTRL_BCK_32FS; break; case aw_i2s_fs::I2SBCK_48FS: reg_value = AW8898_BIT_I2SCTRL_BCK_48FS; break; case aw_i2s_fs::I2SBCK_64FS: reg_value = AW8898_BIT_I2SCTRL_BCK_64FS; break; default: reg_value = AW8898_BIT_I2SCTRL_BCK_64FS; LOG_DEBUG("%s: fs can not support ", __func__); break; } // set fs ModifyReg(AW8898_REG_I2SCTRL, AW8898_BIT_I2SCTRL_BCK_MASK, reg_value); } status_t HwParams(aw_i2s_channel chsel, aw_i2s_frequency rate, aw_i2s_width width, aw_i2s_fs fs) { LOG_DEBUG("[chsel]=%i, [rate]=%i", static_cast(chsel), static_cast(rate)); setChannelSelection(chsel); setSampleRate(rate); setBitWidth(width); setFs(fs); return kStatus_Success; } void LoadRegCfg(void) { for (unsigned int i = 0; i < configurationSize; i++) { WriteReg(initialConfiguration[i].addr, initialConfiguration[i].data); } } status_t RunPwd(bool pwd) { if (pwd) { ModifyReg(AW8898_REG_SYSCTRL, AW8898_BIT_SYSCTRL_PW_MASK, AW8898_BIT_SYSCTRL_PW_PDN); } else { ModifyReg(AW8898_REG_SYSCTRL, AW8898_BIT_SYSCTRL_PW_MASK, AW8898_BIT_SYSCTRL_PW_ACTIVE); ModifyReg(AW8898_REG_SYSCTRL, AW8898_BIT_SYSCTRL_I2SEN_MASK, AW8898_BIT_SYSCTRL_I2S_ENABLE); } return kStatus_Success; } status_t RunMute(bool mute) { if (mute) { ModifyReg(AW8898_REG_PWMCTRL, AW8898_BIT_PWMCTRL_HMUTE_MASK, AW8898_BIT_PWMCTRL_HMUTE_ENABLE); } else { ModifyReg(AW8898_REG_PWMCTRL, AW8898_BIT_PWMCTRL_HMUTE_MASK, AW8898_BIT_PWMCTRL_HMUTE_DISABLE); } return kStatus_Success; } void ColdStart(void) { LoadRegCfg(); g_aw8898.init = aw8898_init::AW8898_INIT_OK; if ((g_aw8898.mode == aw_sel_mode::SPK_MODE) || (g_aw8898.mode == aw_sel_mode::MUSIC_MODE)) { LOG_DEBUG("SPK MODE"); ModifyReg(AW8898_REG_SYSCTRL, AW8898_BIT_SYSCTRL_MODE_MASK, AW8898_BIT_SYSCTRL_SPK_MODE); } else { LOG_DEBUG("RCV MODE"); ModifyReg(AW8898_REG_SYSCTRL, AW8898_BIT_SYSCTRL_MODE_MASK, AW8898_BIT_SYSCTRL_RCV_MODE); } } status_t Start(void) { std::uint16_t reg_val = 0; RunPwd(false); HAL_Delay(OperationWaitTimeMS); for (unsigned int i = 0; i < readRetries; i++) { ReadReg(AW8898_REG_SYSST, ®_val); if ((reg_val & AW8898_BIT_SYSST_PLLS)) { RunMute(false); LOG_DEBUG("%s iis signal check pass!", __func__); return kStatus_Success; } HAL_Delay(OperationWaitTimeMS); } RunPwd(true); LOG_DEBUG("%s: iis signal check error[0x%04X]", __func__, reg_val); return kStatus_Fail; } status_t Stop(void) { RunMute(true); RunPwd(true); return kStatus_Success; } status_t SmartpaCfg(bool play_flag) { int ret = kStatus_Fail; LOG_DEBUG("%s: flag = %d", __func__, play_flag); if (play_flag == true && g_aw8898.mode != aw_sel_mode::OFF_MODE) { if ((g_aw8898.init == aw8898_init::AW8898_INIT_ST) || (g_aw8898.init == aw8898_init::AW8898_INIT_NG)) { LOG_DEBUG("%s: init = %d", __func__, static_cast(g_aw8898.init)); ColdStart(); } else { ret = Start(); LOG_DEBUG("%s: init = %d", __func__, static_cast(g_aw8898.init)); if (ret < 0) { LOG_DEBUG("%s: start fail, ret=%d\n", __func__, ret); } else { LOG_DEBUG("%s: start success", __func__); } } } else { Stop(); } return kStatus_Success; } status_t CtrlState(aw_codec_mode mode, aw_ctrl aw_ctrl) { switch (mode) { case aw_codec_mode::AW_ENCODE: break; case aw_codec_mode::AW_DECODE: SmartpaCfg(aw_ctrl != aw_ctrl::STOP); break; case aw_codec_mode::AW_MODE_BOTH: break; case aw_codec_mode::AW_MODE_LINE_IN: break; default: break; } return kStatus_Success; } status_t SetVolume(std::uint8_t gain) { status_t res = kStatus_Success; std::uint16_t reg = 0, reg_val = 0; res = ReadReg(AW8898_REG_HAGCCFG7, ®); if (res != 0) { LOG_DEBUG("reg read err(%ld)", res); return res; } reg_val = (gain << 8) | (reg & 0x00ff); res = WriteReg(AW8898_REG_HAGCCFG7, reg_val); LOG_DEBUG("Vol 0x%04X", reg_val); return res; } status_t GetVolume(std::uint8_t *gian) { status_t res = kStatus_Success; std::uint16_t reg = 0; res = ReadReg(AW8898_REG_HAGCCFG7, ®); if (res != kStatus_Success) { LOG_DEBUG("reg read err(%ld)", res); return res; } *gian = reg >> 8; return res; } int SetMode(aw_sel_mode mode) { if ((mode == aw_sel_mode::SPK_MODE) && (g_aw8898.mode != mode)) { g_aw8898.mode = aw_sel_mode::SPK_MODE; g_aw8898.init = aw8898_init::AW8898_INIT_ST; } else if ((mode == aw_sel_mode::VOICE_MODE) && (g_aw8898.mode != mode)) { g_aw8898.mode = aw_sel_mode::VOICE_MODE; g_aw8898.init = aw8898_init::AW8898_INIT_ST; } else if ((mode == aw_sel_mode::MUSIC_MODE) && (g_aw8898.mode != mode)) { g_aw8898.mode = aw_sel_mode::MUSIC_MODE; g_aw8898.init = aw8898_init::AW8898_INIT_ST; } else if (mode == aw_sel_mode::OFF_MODE) { g_aw8898.mode = aw_sel_mode::OFF_MODE; } else { LOG_DEBUG("Mode not changed!"); } return 0; } bool CheckPllStatus(void) { std::uint16_t reg_val = 0; ReadReg(AW8898_REG_SYSST, ®_val); return (reg_val & AW8898_BIT_SYSST_PLLS) != 0; } void HAL_Delay(std::uint32_t count) { extern std::uint32_t SystemCoreClock; vTaskDelay(pdMS_TO_TICKS(count)); } void ReadAllReg(void) { std::uint16_t val = 0; for (std::uint8_t i = 0; i < sizeof(availableRegisters); i++) { ReadReg(availableRegisters[i], &val); LOG_DEBUG("reg[%x] = 0x%x", availableRegisters[i], val); } } }; // namespace bsp::audio::AW8898