407 lines
12 KiB
C
Executable File
407 lines
12 KiB
C
Executable File
/*
|
|
Emaltion External audio codec driver
|
|
|
|
This file is the driver for Emaltion extended audio codec.
|
|
|
|
@file aud_ac108.c
|
|
@ingroup mISYSAud
|
|
@note Nothing.
|
|
|
|
Copyright Novatek Microelectronics Corp. 2016. All rights reserved.
|
|
*/
|
|
|
|
/** \addtogroup mISYSAud */
|
|
//@{
|
|
|
|
#ifdef __KERNEL__
|
|
#include <linux/delay.h>
|
|
#include <mach/rcw_macro.h>
|
|
#include "kwrap/type.h"
|
|
#include "kwrap/semaphore.h"
|
|
#include "kwrap/flag.h"
|
|
|
|
#include "aud_ac108_dbg.h"
|
|
#include "aud_ac108.h"
|
|
#else
|
|
#include "kwrap/type.h"
|
|
#include "kwrap/semaphore.h"
|
|
#include "kwrap/flag.h"
|
|
|
|
#include "aud_ac108_dbg.h"
|
|
#include "aud_ac108.h"
|
|
#endif
|
|
|
|
unsigned int aud_ac108_debug_level = NVT_DBG_WRN;
|
|
|
|
static BOOL ac108_gain_balance = TRUE; //balance gain of each mic
|
|
static UINT32 digital_gain = 0xA0;
|
|
static BOOL ac108_opened = FALSE;
|
|
|
|
static ER aud_open_ac108(CTL_AUD_ID id); ///< initial sensor flow
|
|
static ER aud_close_ac108(CTL_AUD_ID id); ///< un-initial sensor flow
|
|
static ER aud_start_ac108(CTL_AUD_ID id); ///< enter sensor sleep mode
|
|
static ER aud_stop_ac108(CTL_AUD_ID id); ///< exit sensor sleep mode
|
|
static ER aud_set_cfg_ac108(CTL_AUD_ID id, CTL_AUDDRV_CFGID drv_cfg_id, void *data); ///< set sensor information (if sensor driver not support feature, pls return E_NOSPT)
|
|
static ER aud_get_cfg_ac108(CTL_AUD_ID id, CTL_AUDDRV_CFGID drv_cfg_id, void *data); ///< get sensor information (if sensor driver not support feature, pls return E_NOSPT)
|
|
|
|
|
|
static CTL_AUD_DRV_TAB ac108_aud_drv_tab = {
|
|
aud_open_ac108,
|
|
aud_close_ac108,
|
|
aud_start_ac108,
|
|
aud_stop_ac108,
|
|
aud_set_cfg_ac108,
|
|
aud_get_cfg_ac108,
|
|
};
|
|
|
|
CTL_AUD_DRV_TAB *aud_get_drv_tab_ac108(void)
|
|
{
|
|
return &ac108_aud_drv_tab;
|
|
}
|
|
|
|
static ER aud_open_ac108(CTL_AUD_ID id)
|
|
{
|
|
UINT32 temp, vec;
|
|
UINT32 m1, m2, n, k1, k2;
|
|
|
|
if (ac108_opened) {
|
|
return E_OK;
|
|
}
|
|
|
|
//reset
|
|
//aud_i2c_write(CHIP_RST, CHIP_RST_VAL);
|
|
//Delay_DelayMs(3);
|
|
|
|
|
|
#if 1
|
|
// SYSCLK_SRC_PLL
|
|
aud_i2c_update(SYSCLK_CTRL, 0x1 << SYSCLK_SRC, SYSCLK_SRC_PLL << SYSCLK_SRC);
|
|
#else
|
|
// SYSCLK_SRC_MCLK
|
|
aud_i2c_update(SYSCLK_CTRL, 0x1 << SYSCLK_SRC, SYSCLK_SRC_MCLK << SYSCLK_SRC);
|
|
#endif
|
|
|
|
|
|
/*
|
|
ac108_set_fmt
|
|
*/
|
|
|
|
#if 1
|
|
// slave mode
|
|
// 0x30:chip is slave mode, BCLK & LRCK input,enable SDO1_EN and
|
|
// SDO2_EN, Transmitter Block Enable, Globe Enable
|
|
aud_i2c_update(I2S_CTRL, 0x03 << LRCK_IOEN | 0x03 << SDO1_EN | 0x1 << TXEN | 0x1 << GEN,
|
|
0x00 << LRCK_IOEN | 0x03 << SDO1_EN | 0x1 << TXEN | 0x1 << GEN);
|
|
#else
|
|
// master mode
|
|
aud_i2c_update(I2S_CTRL, 0x03 << LRCK_IOEN | 0x03 << SDO1_EN | 0x1 << TXEN | 0x1 << GEN,
|
|
0x00 << LRCK_IOEN | 0x03 << SDO1_EN | 0x1 << TXEN | 0x1 << GEN);
|
|
/* multi_chips: only one chip set as Master, and the others also need to set as Slave */
|
|
aud_i2c_update(I2S_CTRL, 0x3 << LRCK_IOEN, 0x3 << LRCK_IOEN);
|
|
#endif
|
|
|
|
|
|
/* ac108_configure_power */
|
|
//0x06:Enable Analog LDO
|
|
aud_i2c_update(PWR_CTRL6, 0x01 << LDO33ANA_ENABLE, 0x01 << LDO33ANA_ENABLE);
|
|
// 0x07:
|
|
// Control VREF output and micbias voltage ?
|
|
// REF faststart disable, enable Enable VREF (needed for Analog
|
|
// LDO and MICBIAS)
|
|
aud_i2c_update(PWR_CTRL7, 0x1f << VREF_SEL | 0x01 << VREF_FASTSTART_ENABLE | 0x01 << VREF_ENABLE,
|
|
0x13 << VREF_SEL | 0x00 << VREF_FASTSTART_ENABLE | 0x01 << VREF_ENABLE);
|
|
// 0x09:
|
|
// Disable fast-start circuit on VREFP
|
|
// VREFP_RESCTRL=00=1 MOhm
|
|
// IGEN_TRIM=100=+25%
|
|
// Enable VREFP (needed by all audio input channels)
|
|
aud_i2c_update(PWR_CTRL9, 0x01 << VREFP_FASTSTART_ENABLE | 0x03 << VREFP_RESCTRL | 0x07 << IGEN_TRIM | 0x01 << VREFP_ENABLE,
|
|
0x00 << VREFP_FASTSTART_ENABLE | 0x00 << VREFP_RESCTRL | 0x04 << IGEN_TRIM | 0x01 << VREFP_ENABLE);
|
|
|
|
|
|
//0x31: 0: normal mode, negative edge drive and positive edge sample
|
|
//1: invert mode, positive edge drive and negative edge sample
|
|
aud_i2c_update(I2S_BCLK_CTRL, 0x01 << BCLK_POLARITY, BCLK_NORMAL_DRIVE_N_SAMPLE_P << BCLK_POLARITY);
|
|
// 0x32: same as 0x31
|
|
aud_i2c_update(I2S_LRCK_CTRL1, 0x01 << LRCK_POLARITY, LRCK_LEFT_HIGH_RIGHT_LOW << LRCK_POLARITY);
|
|
|
|
// 0x34:Encoding Mode Selection,Mode
|
|
// Selection,data is offset by 1 BCLKs to LRCK
|
|
// normal mode for the last half cycle of BCLK in the slot ?
|
|
// turn to hi-z state (TDM) when not transferring slot ?
|
|
aud_i2c_update(I2S_FMT_CTRL1, 0x01 << ENCD_SEL | 0x03 << MODE_SEL | 0x01 << TX2_OFFSET |
|
|
0x01 << TX1_OFFSET | 0x01 << TX_SLOT_HIZ | 0x01 << TX_STATE,
|
|
0 << ENCD_SEL |
|
|
LEFT_JUSTIFIED_FORMAT << MODE_SEL |
|
|
1 << TX2_OFFSET |
|
|
1 << TX1_OFFSET |
|
|
0x00 << TX_SLOT_HIZ |
|
|
0x01 << TX_STATE);
|
|
|
|
// 0x60:
|
|
// MSB / LSB First Select: This driver only support MSB First Select .
|
|
// OUT2_MUTE,OUT1_MUTE shoule be set in widget.
|
|
// LRCK = 1 BCLK width
|
|
// Linear PCM
|
|
// TODO:pcm mode, bit[0:1] and bit[2] is special
|
|
aud_i2c_update(I2S_FMT_CTRL3, 0x01 << TX_MLS | 0x03 << SEXT | 0x01 << LRCK_WIDTH | 0x03 << TX_PDM,
|
|
0x00 << TX_MLS | 0x03 << SEXT | 0x00 << LRCK_WIDTH | 0x00 << TX_PDM);
|
|
aud_i2c_write(HPF_EN, 0x00);
|
|
|
|
m1 = 4;
|
|
m2 = 0;
|
|
n = 128;
|
|
k1 = 24;
|
|
k2 = 0;
|
|
|
|
/*
|
|
ac108_hw_params
|
|
*/
|
|
|
|
// TDM mode or normal mode
|
|
aud_i2c_write(I2S_LRCK_CTRL2, 128 - 1);
|
|
aud_i2c_update(I2S_LRCK_CTRL1, 0x03 << 0, 0x00);
|
|
|
|
/**
|
|
* 0x35:
|
|
* TX Encoding mode will add 4bits to mark channel number
|
|
* TODO: need a chat to explain this
|
|
*/
|
|
aud_i2c_update(I2S_FMT_CTRL2, 0x07 << SAMPLE_RESOLUTION | 0x07 << SLOT_WIDTH_SEL,
|
|
7 << SAMPLE_RESOLUTION
|
|
| 7 << SLOT_WIDTH_SEL);
|
|
|
|
/**
|
|
* 0x60:
|
|
* ADC Sample Rate synchronised with I2S1 clock zone
|
|
*/
|
|
aud_i2c_update(ADC_SPRC, 0x0f << ADC_FS_I2S1, 8 << ADC_FS_I2S1);
|
|
aud_i2c_write(HPF_EN, 0x0F);
|
|
|
|
/*ac108_config_pll(ac10x, ac108_sample_rate[rate].real_val, 0); */
|
|
/* 0x11,0x12,0x13,0x14: Config PLL DIV param M1/M2/N/K1/K2 */
|
|
aud_i2c_update(PLL_CTRL5, 0x1f << PLL_POSTDIV1 | 0x01 << PLL_POSTDIV2,
|
|
k1 << PLL_POSTDIV1 | k2 << PLL_POSTDIV2);
|
|
aud_i2c_update(PLL_CTRL4, 0xff << PLL_LOOPDIV_LSB, (unsigned char)n << PLL_LOOPDIV_LSB);
|
|
aud_i2c_update(PLL_CTRL3, 0x03 << PLL_LOOPDIV_MSB, (n >> 8) << PLL_LOOPDIV_MSB);
|
|
aud_i2c_update(PLL_CTRL2, 0x1f << PLL_PREDIV1 | 0x01 << PLL_PREDIV2,
|
|
m1 << PLL_PREDIV1 | m2 << PLL_PREDIV2);
|
|
|
|
/*0x18: PLL clk lock enable*/
|
|
aud_i2c_update(PLL_LOCK_CTRL, 0x1 << PLL_LOCK_EN, 0x1 << PLL_LOCK_EN);
|
|
|
|
/*0x10: PLL Common voltage Enable, PLL Enable,PLL loop divider factor detection enable*/
|
|
aud_i2c_update(PLL_CTRL1, 0x01 << PLL_EN | 0x01 << PLL_COM_EN | 0x01 << PLL_NDET,
|
|
0x01 << PLL_EN | 0x01 << PLL_COM_EN | 0x01 << PLL_NDET);
|
|
|
|
/**
|
|
* 0x20: enable pll, pll source from mclk/bclk, sysclk source from pll, enable sysclk
|
|
*/
|
|
aud_i2c_update(SYSCLK_CTRL, 0x01 << PLLCLK_EN | 0x03 << PLLCLK_SRC | 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN,
|
|
0x01 << PLLCLK_EN |0<< PLLCLK_SRC | 0x01 << SYSCLK_SRC | 0x01 << SYSCLK_EN);
|
|
|
|
aud_i2c_update(I2S_BCLK_CTRL, 0x0F << BCLKDIV, 2 << BCLKDIV);
|
|
|
|
/*
|
|
ac108_multi_chips_slots
|
|
*/
|
|
vec = 0xFUL;
|
|
//aud_i2c_write(I2S_TX1_CTRL1, 8 - 1);
|
|
aud_i2c_write(I2S_TX1_CTRL1, 4 - 1);
|
|
aud_i2c_write(I2S_TX1_CTRL2, (vec >> 0) & 0xFF);
|
|
aud_i2c_write(I2S_TX1_CTRL3, (vec >> 8) & 0xFF);
|
|
|
|
#if 1
|
|
vec = (0x2 << 0 | 0x3 << 2 | 0x0 << 4 | 0x1 << 6);
|
|
#else
|
|
//DBG_DUMP("AC108 Set to 2CH Mode\r\n");
|
|
//2CH TEST
|
|
vec = (0x2 << 0 | 0x0 << 2 | 0x3 << 4 | 0x1 << 6);
|
|
#endif
|
|
aud_i2c_write(I2S_TX1_CHMP_CTRL1, (vec >> 0) & 0xFF);
|
|
aud_i2c_write(I2S_TX1_CHMP_CTRL2, (vec >> 8) & 0xFF);
|
|
aud_i2c_write(I2S_TX1_CHMP_CTRL3, (vec >> 16) & 0xFF);
|
|
aud_i2c_write(I2S_TX1_CHMP_CTRL4, (vec >> 24) & 0xFF);
|
|
|
|
/* Digital gain Default value */
|
|
aud_i2c_write(ADC1_DVOL_CTRL, 0xA7);//CH3
|
|
aud_i2c_write(ADC2_DVOL_CTRL, 0xA7);//CH2
|
|
aud_i2c_write(ADC3_DVOL_CTRL, 0xA7);//CH1
|
|
aud_i2c_write(ADC4_DVOL_CTRL, 0x9C);//CH0
|
|
//aud_i2c_write(ADC1_DVOL_CTRL, 0x98);//CH3
|
|
//aud_i2c_write(ADC2_DVOL_CTRL, 0x98);//CH2
|
|
//aud_i2c_write(ADC3_DVOL_CTRL, 0x98);//CH1
|
|
//aud_i2c_write(ADC4_DVOL_CTRL, 0x98);//CH0
|
|
|
|
|
|
/* PGA gain Default value */
|
|
aud_i2c_write(ANA_PGA1_CTRL, 0x00<<1);
|
|
aud_i2c_write(ANA_PGA2_CTRL, 0x00<<1);
|
|
aud_i2c_write(ANA_PGA3_CTRL, 0x00<<1);
|
|
aud_i2c_write(ANA_PGA4_CTRL, 0x00<<1);
|
|
|
|
// MIC-BIAS ON
|
|
aud_i2c_update(ANA_ADC1_CTRL1, 0x07 << ADC1_MICBIAS_EN, 0x07 << ADC1_MICBIAS_EN);
|
|
aud_i2c_update(ANA_ADC2_CTRL1, 0x07 << ADC2_MICBIAS_EN, 0x07 << ADC2_MICBIAS_EN);
|
|
aud_i2c_update(ANA_ADC3_CTRL1, 0x07 << ADC3_MICBIAS_EN, 0x07 << ADC3_MICBIAS_EN);
|
|
aud_i2c_update(ANA_ADC4_CTRL1, 0x07 << ADC4_MICBIAS_EN, 0x07 << ADC4_MICBIAS_EN);
|
|
|
|
aud_i2c_write(ADC_DIG_EN, 0x1F);
|
|
aud_i2c_write(ANA_ADC4_CTRL7, 0x0F);
|
|
aud_i2c_write(ANA_ADC4_CTRL6, 0x20);
|
|
|
|
/*
|
|
ac108_trigger
|
|
*/
|
|
temp = aud_i2c_read(I2S_CTRL);
|
|
if ((temp & (0x02 << LRCK_IOEN)) && (temp & (0x01 << LRCK_IOEN)) == 0) {
|
|
/* disable global clock */
|
|
aud_i2c_update(I2S_CTRL, 0x1 << TXEN | 0x1 << GEN, 0x1 << TXEN | 0x0 << GEN);
|
|
}
|
|
/*0x21: Module clock enable<I2S, ADC digital, MIC offset Calibration, ADC analog>*/
|
|
aud_i2c_write(MOD_CLK_EN, 1 << _I2S | 1 << ADC_DIGITAL | 1 << MIC_OFFSET_CALIBRATION | 1 << ADC_ANALOG);
|
|
/*0x22: Module reset de-asserted<I2S, ADC digital, MIC offset Calibration, ADC analog>*/
|
|
aud_i2c_write(MOD_RST_CTRL, 1 << _I2S | 1 << ADC_DIGITAL | 1 << MIC_OFFSET_CALIBRATION | 1 << ADC_ANALOG);
|
|
|
|
// AC108 as Master!!
|
|
aud_i2c_write(I2S_CTRL, aud_i2c_read(I2S_CTRL)|0xC0);
|
|
|
|
ac108_opened = TRUE;
|
|
|
|
return E_OK;
|
|
}
|
|
|
|
static ER aud_close_ac108(CTL_AUD_ID id)
|
|
{
|
|
if (!ac108_opened) {
|
|
return E_OK;
|
|
}
|
|
|
|
ac108_opened = FALSE;
|
|
//aud_i2c_remove_driver(0);
|
|
|
|
return E_OK;
|
|
}
|
|
static ER aud_start_ac108(CTL_AUD_ID id)
|
|
{
|
|
return E_OK;
|
|
}
|
|
static ER aud_stop_ac108(CTL_AUD_ID id)
|
|
{
|
|
return E_OK;
|
|
}
|
|
|
|
static ER aud_set_cfg_ac108(CTL_AUD_ID id, CTL_AUDDRV_CFGID drv_cfg_id, void *data)
|
|
{
|
|
switch (drv_cfg_id) {
|
|
case CTL_AUDDRV_CFGID_SET_SAMPLE_RATE:
|
|
{
|
|
UINT32 sample_rate = *(UINT32*)data;
|
|
if(sample_rate == 48000) {
|
|
aud_i2c_update(I2S_BCLK_CTRL, 0x0F << BCLKDIV, 2 << BCLKDIV);
|
|
} else if (sample_rate == 16000) {
|
|
aud_i2c_update(I2S_BCLK_CTRL, 0x0F << BCLKDIV, 4 << BCLKDIV);
|
|
} else if (sample_rate == 8000) {
|
|
aud_i2c_update(I2S_BCLK_CTRL, 0x0F << BCLKDIV, 6 << BCLKDIV);
|
|
} else if (sample_rate == 24000) {
|
|
aud_i2c_update(I2S_BCLK_CTRL, 0x0F << BCLKDIV, 3 << BCLKDIV);
|
|
}
|
|
break;
|
|
}
|
|
case CTL_AUDDRV_CFGID_SET_VOLUME:
|
|
{
|
|
if (id == CTL_AUD_ID_CAP) {
|
|
UINT32 value;
|
|
UINT32 gain = *(UINT32*)data;
|
|
|
|
DBG_DUMP("gain = %d\r\n", gain);
|
|
|
|
if (gain == 0) {
|
|
value = 0;
|
|
|
|
aud_i2c_write(ADC1_DVOL_CTRL, 0x00);
|
|
aud_i2c_write(ADC2_DVOL_CTRL, 0x00);
|
|
aud_i2c_write(ADC3_DVOL_CTRL, 0x00);
|
|
aud_i2c_write(ADC4_DVOL_CTRL, 0x00);
|
|
|
|
aud_i2c_write(ANA_PGA1_CTRL, value<<1);
|
|
aud_i2c_write(ANA_PGA2_CTRL, value<<1);
|
|
aud_i2c_write(ANA_PGA3_CTRL, value<<1);
|
|
aud_i2c_write(ANA_PGA4_CTRL, value<<1);
|
|
|
|
} else {
|
|
gain = gain/12;
|
|
|
|
value = ((gain - 1)<<2)+3;
|
|
|
|
DBG_DUMP("gain = %d, value = %d\r\n", gain, value);
|
|
|
|
aud_i2c_write(ADC1_DVOL_CTRL, digital_gain);
|
|
aud_i2c_write(ADC2_DVOL_CTRL, digital_gain);
|
|
aud_i2c_write(ADC3_DVOL_CTRL, digital_gain);
|
|
aud_i2c_write(ADC4_DVOL_CTRL, digital_gain);
|
|
|
|
/* PGA gain Max 0x1F */
|
|
aud_i2c_write(ANA_PGA1_CTRL, value<<1); // Our Ch3
|
|
|
|
aud_i2c_write(ANA_PGA3_CTRL, value<<1); // Our CH1
|
|
|
|
if (ac108_gain_balance) {
|
|
//making volume balance
|
|
if (value > 6)
|
|
value -= 6;
|
|
else
|
|
value = 0;
|
|
}
|
|
aud_i2c_write(ANA_PGA4_CTRL, value<<1); // Our CH0
|
|
aud_i2c_write(ANA_PGA2_CTRL, value<<1); // Our CH2
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return E_OK;
|
|
}
|
|
|
|
static ER aud_get_cfg_ac108(CTL_AUD_ID id, CTL_AUDDRV_CFGID drv_cfg_id, void *data)
|
|
{
|
|
return E_OK;
|
|
}
|
|
|
|
int aud_init_ac108(void)
|
|
{
|
|
INT32 ret;
|
|
CTL_AUD_DRV_TAB *drv_tab = NULL;
|
|
|
|
drv_tab = aud_get_drv_tab_ac108();
|
|
|
|
ret = ctl_aud_reg_auddrv("nvt_aud_ac108", drv_tab);
|
|
if (ret != E_OK) {
|
|
DBG_ERR("register audio driver fail \r\n");
|
|
}
|
|
|
|
ret = aud_i2c_init_driver();
|
|
|
|
if (ret != E_OK) {
|
|
DBG_ERR("init. i2c driver fail\r\n");
|
|
return -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int aud_uninit_ac108(void)
|
|
{
|
|
INT32 ret = 0;
|
|
|
|
aud_i2c_remove_driver(0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
//@}
|