488 lines
13 KiB
C
488 lines
13 KiB
C
/**************************************************************************//**
|
|
*
|
|
* @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2020-12-12 Wayne First version
|
|
*
|
|
******************************************************************************/
|
|
|
|
#include <rtconfig.h>
|
|
|
|
#if defined(NU_PKG_USING_NAU8822)
|
|
|
|
#include <rtthread.h>
|
|
#include <rtdevice.h>
|
|
|
|
#include "acodec_nau8822.h"
|
|
#include "drv_i2s.h"
|
|
|
|
#define DBG_ENABLE
|
|
#define DBG_LEVEL DBG_LOG
|
|
#define DBG_SECTION_NAME "acodec.nau8822"
|
|
#define DBG_COLOR
|
|
#include <rtdbg.h>
|
|
|
|
#define DEF_NAU8822_ADDR 0x1A
|
|
|
|
static struct rt_i2c_bus_device *g_I2cBusDev = NULL;
|
|
S_NU_NAU8822_CONFIG *g_psCodecConfig = NULL;
|
|
|
|
static rt_err_t nau8822_init(void);
|
|
static rt_err_t nau8822_reset(void);
|
|
static rt_err_t nau8822_dsp_control(struct rt_audio_configure *config);
|
|
static rt_err_t nau8822_mixer_control(rt_uint32_t ui32Units, rt_uint32_t ui32Value);
|
|
static rt_err_t nau8822_mixer_query(rt_uint32_t ui32Units, rt_uint32_t *ui32Value);
|
|
|
|
nu_acodec_ops nu_acodec_ops_nau8822 =
|
|
{
|
|
.name = "NAU8822",
|
|
.role = NU_ACODEC_ROLE_MASTER,
|
|
.config = { // Default settings.
|
|
.samplerate = 16000,
|
|
.channels = 2,
|
|
.samplebits = 16
|
|
},
|
|
.nu_acodec_init = nau8822_init,
|
|
.nu_acodec_reset = nau8822_reset,
|
|
|
|
.nu_acodec_dsp_control = nau8822_dsp_control,
|
|
.nu_acodec_mixer_control = nau8822_mixer_control,
|
|
.nu_acodec_mixer_query = nau8822_mixer_query
|
|
};
|
|
|
|
static void nau8822_delay_ms(rt_uint32_t nms)
|
|
{
|
|
rt_thread_mdelay(nms);
|
|
}
|
|
|
|
static int I2C_ReadNAU8822(uint8_t u8addr, uint16_t *pu16data)
|
|
{
|
|
struct rt_i2c_msg msgs[2];
|
|
uint8_t u8TxData = (u8addr << 1);
|
|
|
|
RT_ASSERT(g_I2cBusDev != NULL);
|
|
RT_ASSERT(pu16data != NULL);
|
|
|
|
msgs[0].addr = DEF_NAU8822_ADDR; /* Slave address */
|
|
msgs[0].flags = RT_I2C_WR; /* Write flag */
|
|
msgs[0].buf = (rt_uint8_t *)&u8TxData; /* Number of bytes sent */
|
|
msgs[0].len = sizeof(u8TxData); /* Number of bytes read */
|
|
|
|
msgs[1].addr = DEF_NAU8822_ADDR; /* Slave address */
|
|
msgs[1].flags = RT_I2C_RD; /* Read flag */
|
|
msgs[1].buf = (rt_uint8_t *)pu16data; /* Read data pointer */
|
|
msgs[1].len = 2; /* Number of bytes read */
|
|
|
|
if (rt_i2c_transfer(g_I2cBusDev, &msgs[0], 2) != 2)
|
|
{
|
|
return -RT_ERROR;
|
|
}
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static int I2C_WriteNAU8822(uint8_t u8addr, uint16_t u16data)
|
|
{
|
|
/* Write 9-bit data to 7-bit address register of NAU8822 */
|
|
struct rt_i2c_msg msg;
|
|
uint8_t au8TxData[2];
|
|
|
|
RT_ASSERT(g_I2cBusDev != NULL);
|
|
|
|
au8TxData[0] = (uint8_t)((u8addr << 1) | (u16data >> 8)); //u8addr [7:1] | u16data [8]
|
|
au8TxData[1] = (uint8_t)(u16data & 0x00FF); //data [7:0]
|
|
|
|
msg.addr = DEF_NAU8822_ADDR; /* Slave address */
|
|
msg.flags = RT_I2C_WR; /* Write flag */
|
|
msg.buf = (rt_uint8_t *)&au8TxData[0]; /* Slave register address */
|
|
msg.len = sizeof(au8TxData); /* Number of bytes sent */
|
|
|
|
if (g_I2cBusDev && rt_i2c_transfer(g_I2cBusDev, &msg, 1) != 1)
|
|
{
|
|
rt_kprintf("[Failed] addr=%x, data=%d\n", u8addr, u16data);
|
|
return -RT_ERROR;
|
|
}
|
|
|
|
{
|
|
/* Verify */
|
|
uint8_t au8RxData[2];
|
|
I2C_ReadNAU8822(u8addr, (uint16_t *)&au8RxData[0]);
|
|
rt_kprintf("Wrote addr %02x -> 0x%04x, read back -> 0x%02x%02x\n", u8addr, u16data, au8RxData[0], au8RxData[1]);
|
|
}
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static void nau8822_phonejack_set(S_NU_NAU8822_CONFIG *psCodecConfig, int bEnable)
|
|
{
|
|
rt_pin_mode(psCodecConfig->pin_phonejack_en, PIN_MODE_OUTPUT);
|
|
|
|
if (bEnable)
|
|
{
|
|
rt_pin_write(psCodecConfig->pin_phonejack_en, PIN_LOW);
|
|
}
|
|
else
|
|
{
|
|
rt_pin_write(psCodecConfig->pin_phonejack_en, PIN_HIGH);
|
|
}
|
|
}
|
|
|
|
|
|
static rt_err_t nau8822_probe(void)
|
|
{
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t nau8822_reset(void)
|
|
{
|
|
I2C_WriteNAU8822(0, 0x000); /* Reset all registers */
|
|
nau8822_delay_ms(30);
|
|
|
|
LOG_I("Software Reset.\n");
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t nau8822_dsp_config(rt_uint32_t ui32SamplRate, rt_uint8_t u8ChNum, rt_uint8_t u8SamplBit)
|
|
{
|
|
uint8_t bClkDiv;
|
|
uint8_t mClkDiv;
|
|
uint16_t u16AudIf = 0x010; /* I2S, 16-bit */
|
|
uint16_t u16ClkCtrl;
|
|
uint8_t u8WLEN;
|
|
|
|
if (ui32SamplRate > 48000)
|
|
return -RT_ERROR;
|
|
|
|
if (u8ChNum == 2)
|
|
{
|
|
u16AudIf = (u16AudIf & 0x1FE) | 0x0;
|
|
}
|
|
else
|
|
{
|
|
u16AudIf = (u16AudIf & 0x1FE) | 0x1;
|
|
}
|
|
|
|
/* Force to set Channel number to 2 */
|
|
u8ChNum = 2;
|
|
|
|
switch (u8SamplBit)
|
|
{
|
|
case 16:
|
|
u8WLEN = 0x0;
|
|
break;
|
|
|
|
case 20:
|
|
u8WLEN = 0x1;
|
|
break;
|
|
|
|
case 24:
|
|
u8WLEN = 0x2;
|
|
break;
|
|
|
|
case 32:
|
|
u8WLEN = 0x3;
|
|
break;
|
|
|
|
default:
|
|
LOG_E("sample rate not match!\n");
|
|
return -RT_ERROR;
|
|
}
|
|
u16AudIf = (u16AudIf & 0x19F) | (u8WLEN << 5);
|
|
|
|
if (ui32SamplRate % 11025)
|
|
{
|
|
I2C_WriteNAU8822(36, 0x008); //12.288Mhz
|
|
I2C_WriteNAU8822(37, 0x00C);
|
|
I2C_WriteNAU8822(38, 0x093);
|
|
I2C_WriteNAU8822(39, 0x0E9);
|
|
|
|
/* FIXME */
|
|
if (ui32SamplRate > 48000)
|
|
ui32SamplRate = 8000;
|
|
|
|
mClkDiv = (48000 * 256 * u8ChNum) / (ui32SamplRate * 256);
|
|
bClkDiv = (ui32SamplRate * 256) / (ui32SamplRate * u8ChNum * u8SamplBit);
|
|
}
|
|
else
|
|
{
|
|
I2C_WriteNAU8822(36, 0x007); //11.2896Mhz
|
|
I2C_WriteNAU8822(37, 0x021);
|
|
I2C_WriteNAU8822(38, 0x161);
|
|
I2C_WriteNAU8822(39, 0x026);
|
|
|
|
/* FIXME */
|
|
if (ui32SamplRate > 44100)
|
|
ui32SamplRate = 11025;
|
|
|
|
mClkDiv = (44100 * 256 * u8ChNum) / (ui32SamplRate * 256);
|
|
bClkDiv = (ui32SamplRate * 256) / (ui32SamplRate * u8ChNum * u8SamplBit);
|
|
}
|
|
|
|
switch (mClkDiv)
|
|
{
|
|
case 1:
|
|
mClkDiv = 0;
|
|
break;
|
|
case 2:
|
|
mClkDiv = 2;
|
|
break;
|
|
case 3:
|
|
mClkDiv = 3;
|
|
break;
|
|
case 4:
|
|
mClkDiv = 4;
|
|
break;
|
|
case 6:
|
|
mClkDiv = 5;
|
|
break;
|
|
case 8:
|
|
mClkDiv = 6;
|
|
break;
|
|
case 12:
|
|
mClkDiv = 7;
|
|
break;
|
|
default:
|
|
LOG_E("mclk divider not match!\n");
|
|
mClkDiv = 0;
|
|
return -RT_ERROR;
|
|
}
|
|
|
|
switch (bClkDiv)
|
|
{
|
|
case 1:
|
|
bClkDiv = 0;
|
|
break;
|
|
case 2:
|
|
bClkDiv = 1;
|
|
break;
|
|
case 4:
|
|
bClkDiv = 2;
|
|
break;
|
|
case 8:
|
|
bClkDiv = 3;
|
|
break;
|
|
case 16:
|
|
bClkDiv = 4;
|
|
break;
|
|
case 32:
|
|
bClkDiv = 5;
|
|
break;
|
|
default:
|
|
LOG_E("bclk divider not match!\n");
|
|
bClkDiv = 0;
|
|
return -RT_ERROR;
|
|
}
|
|
|
|
if (nu_acodec_ops_nau8822.role == NU_ACODEC_ROLE_MASTER)
|
|
{
|
|
u16ClkCtrl = (1 << 8) | (1 << 0); //Use internal PLL, FS/BCLK
|
|
}
|
|
|
|
u16ClkCtrl = (u16ClkCtrl & 0x11F) | (mClkDiv << 5);
|
|
u16ClkCtrl = (u16ClkCtrl & 0x1E3) | (bClkDiv << 2);
|
|
|
|
I2C_WriteNAU8822(4, u16AudIf);
|
|
|
|
I2C_WriteNAU8822(6, u16ClkCtrl);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t nau8822_init(void)
|
|
{
|
|
//input source is MIC
|
|
if (nu_acodec_ops_nau8822.role == NU_ACODEC_ROLE_MASTER)
|
|
{
|
|
I2C_WriteNAU8822(1, 0x03F); /* PLLEN, MICBIASEN, ABIASEN, IOBUFEN, REFIMP(3kohm) */
|
|
}
|
|
else
|
|
{
|
|
I2C_WriteNAU8822(1, 0x01F); /* MICBIASEN, ABIASEN, IOBUFEN, REFIMP(3kohm) */
|
|
}
|
|
|
|
I2C_WriteNAU8822(2, 0x1BF); /* Enable L/R Headphone, ADC Mix/Boost, ADC */
|
|
I2C_WriteNAU8822(3, 0x07F); /* Enable L/R main mixer, DAC */
|
|
I2C_WriteNAU8822(4, 0x010); /* 16-bit word length, I2S format, Stereo */
|
|
I2C_WriteNAU8822(5, 0x000); /* Companding control and loop back mode (all disable) */
|
|
nau8822_delay_ms(30);
|
|
|
|
I2C_WriteNAU8822(6, 0x1AD); /* Divide by 6, 16K */
|
|
I2C_WriteNAU8822(7, 0x006); /* 16K for internal filter coefficients */
|
|
I2C_WriteNAU8822(10, 0x008); /* DAC soft mute is disabled, DAC oversampling rate is 128x */
|
|
I2C_WriteNAU8822(14, 0x108); /* ADC HP filter is disabled, ADC oversampling rate is 128x */
|
|
I2C_WriteNAU8822(15, 0x1EF); /* ADC left digital volume control */
|
|
I2C_WriteNAU8822(16, 0x1EF); /* ADC right digital volume control */
|
|
I2C_WriteNAU8822(44, 0x033); /* LMICN/LMICP is connected to PGA */
|
|
I2C_WriteNAU8822(47, 0x100); /* Gain value */
|
|
I2C_WriteNAU8822(48, 0x100); /* Gain value */
|
|
I2C_WriteNAU8822(50, 0x001); /* Left DAC connected to LMIX */
|
|
I2C_WriteNAU8822(51, 0x001); /* Right DAC connected to RMIX */
|
|
|
|
I2C_WriteNAU8822(0x34, 0x13F);
|
|
I2C_WriteNAU8822(0x35, 0x13F);
|
|
|
|
nu_acodec_ops_nau8822.config.samplerate = 16000;
|
|
nu_acodec_ops_nau8822.config.channels = 2;
|
|
nu_acodec_ops_nau8822.config.samplebits = 16;
|
|
|
|
LOG_I("Initialized done.\n");
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t nau8822_dsp_control(struct rt_audio_configure *config)
|
|
{
|
|
rt_err_t result = RT_EOK;
|
|
RT_ASSERT(config != RT_NULL);
|
|
|
|
if (rt_memcmp((void *)config, (void *)&nu_acodec_ops_nau8822.config, sizeof(struct rt_audio_configure)) != 0)
|
|
{
|
|
if ((result = nau8822_dsp_config(config->samplerate, config->channels, config->samplebits)) == RT_EOK)
|
|
rt_memcpy((void *)&nu_acodec_ops_nau8822.config, (void *)config, sizeof(struct rt_audio_configure)) ;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static rt_err_t nau8822_mixer_control(rt_uint32_t ui32Units, rt_uint32_t ui32Value)
|
|
{
|
|
switch (ui32Units)
|
|
{
|
|
case AUDIO_MIXER_MUTE:
|
|
{
|
|
uint16_t u16Data;
|
|
I2C_ReadNAU8822(10, &u16Data);
|
|
if (ui32Value)
|
|
{
|
|
I2C_WriteNAU8822(10, u16Data | (1 << 6));
|
|
nau8822_phonejack_set(g_psCodecConfig, 0);
|
|
}
|
|
else
|
|
{
|
|
I2C_WriteNAU8822(10, u16Data & ~(1 << 6));
|
|
nau8822_phonejack_set(g_psCodecConfig, 1);
|
|
}
|
|
}
|
|
break;
|
|
case AUDIO_MIXER_VOLUME:
|
|
{
|
|
uint8_t u8GAIN = 256 * ui32Value / 100;
|
|
I2C_WriteNAU8822(11, 0x100 | u8GAIN);
|
|
I2C_WriteNAU8822(12, 0x100 | u8GAIN);
|
|
|
|
u8GAIN = 0x3F * ui32Value / 100;
|
|
I2C_WriteNAU8822(54, 0x100 | u8GAIN);
|
|
I2C_WriteNAU8822(55, 0x100 | u8GAIN);
|
|
}
|
|
break;
|
|
case AUDIO_MIXER_QUERY:
|
|
case AUDIO_MIXER_BASS:
|
|
case AUDIO_MIXER_MID:
|
|
case AUDIO_MIXER_TREBLE:
|
|
case AUDIO_MIXER_EQUALIZER:
|
|
case AUDIO_MIXER_LINE:
|
|
case AUDIO_MIXER_DIGITAL:
|
|
case AUDIO_MIXER_MIC:
|
|
case AUDIO_MIXER_VITURAL:
|
|
case AUDIO_MIXER_EXTEND:
|
|
default:
|
|
return -RT_ERROR;
|
|
}
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t nau8822_mixer_query(rt_uint32_t ui32Units, rt_uint32_t *pui32Value)
|
|
{
|
|
RT_ASSERT(pui32Value != RT_NULL);
|
|
rt_uint16_t u16RV = 0;
|
|
|
|
switch (ui32Units)
|
|
{
|
|
case AUDIO_MIXER_QUERY:
|
|
*pui32Value = AUDIO_MIXER_VOLUME | AUDIO_MIXER_MUTE;
|
|
break;
|
|
|
|
case AUDIO_MIXER_MUTE:
|
|
I2C_ReadNAU8822(10, (uint16_t *)&u16RV);
|
|
if (u16RV & (1 << 6))
|
|
*pui32Value = 1;
|
|
else
|
|
*pui32Value = 0;
|
|
break;
|
|
|
|
case AUDIO_MIXER_VOLUME:
|
|
I2C_ReadNAU8822(11, (uint16_t *)&u16RV);
|
|
*pui32Value = u16RV * 100 / 256;
|
|
break;
|
|
|
|
case AUDIO_MIXER_BASS:
|
|
case AUDIO_MIXER_MID:
|
|
case AUDIO_MIXER_TREBLE:
|
|
case AUDIO_MIXER_EQUALIZER:
|
|
case AUDIO_MIXER_LINE:
|
|
case AUDIO_MIXER_DIGITAL:
|
|
case AUDIO_MIXER_MIC:
|
|
case AUDIO_MIXER_VITURAL:
|
|
case AUDIO_MIXER_EXTEND:
|
|
default:
|
|
return -RT_ERROR;
|
|
}
|
|
return RT_EOK;
|
|
}
|
|
|
|
int nu_hw_nau8822_init(S_NU_NAU8822_CONFIG *psCodecConfig)
|
|
{
|
|
RT_ASSERT(psCodecConfig != RT_NULL);
|
|
struct rt_i2c_bus_device *psI2cBusDev;
|
|
struct rt_audio_device *psAudioDev;
|
|
nu_i2s_t psNuI2s;
|
|
|
|
/* Find I2C bus */
|
|
psI2cBusDev = (struct rt_i2c_bus_device *)rt_device_find(psCodecConfig->i2c_bus_name);
|
|
if (psI2cBusDev == RT_NULL)
|
|
{
|
|
LOG_E("Can't found I2C bus - %s..!\n", psCodecConfig->i2c_bus_name);
|
|
goto exit_rt_hw_nau8822_init;
|
|
}
|
|
|
|
/* Find I2S bus */
|
|
psAudioDev = (struct rt_audio_device *)rt_device_find(psCodecConfig->i2s_bus_name);
|
|
if (psAudioDev == RT_NULL)
|
|
{
|
|
LOG_E("Can't found I2S bus - %s ..!\n", psCodecConfig->i2s_bus_name);
|
|
goto exit_rt_hw_nau8822_init;
|
|
}
|
|
|
|
if (nau8822_probe() != RT_EOK)
|
|
{
|
|
LOG_E("Can't found audio codec..!\n");
|
|
goto exit_rt_hw_nau8822_init;
|
|
}
|
|
|
|
/* Store this board setting. */
|
|
g_psCodecConfig = psCodecConfig;
|
|
g_I2cBusDev = psI2cBusDev;
|
|
|
|
/* Get NuI2S device instance. */
|
|
psNuI2s = (nu_i2s_t)psAudioDev;
|
|
|
|
/* Register Acodec Ops */
|
|
psNuI2s->AcodecOps = &nu_acodec_ops_nau8822;
|
|
|
|
/* Use Acodec default settings. */
|
|
rt_memcpy(&psNuI2s->config, &nu_acodec_ops_nau8822.config, sizeof(struct rt_audio_configure));
|
|
|
|
return RT_EOK;
|
|
|
|
exit_rt_hw_nau8822_init:
|
|
|
|
return -RT_ERROR;
|
|
}
|
|
|
|
#endif //#if defined(NU_PKG_USING_NAU8822)
|