rt-thread/bsp/allwinner/libraries/sunxi-hal/hal/source/sound/codecs/ac108.c

1784 lines
58 KiB
C
Raw Normal View History

/*
* Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved.
*
* Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in
* the the people's Republic of China and other countries.
* All Allwinner Technology Co.,Ltd. trademarks are used with permission.
*
* DISCLAIMER
* THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT.
* IF YOU NEED TO INTEGRATE THIRD PARTYS TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.)
* IN ALLWINNERSSDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN
* ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES.
* ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS
* COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE.
* YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTYS TECHNOLOGY.
*
*
* THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT
* PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND,
* WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING
* THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE
* OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* IN NO EVENT SHALL ALLWINNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <getopt.h>
#include <sound/snd_core.h>
#include <sound/snd_pcm.h>
#include <sound/pcm_common.h>
#include <aw_common.h>
#include <unistd.h>
#include "ac108.h"
//test config
#define AC108_KCONTROL_TEST_EN
//add to
#undef AC108_IDLE_RESET_EN
//AC108 SDO2/TX2 Enable (SDO1 has be enabled default)
#define AC108_SDO2_EN 0
#define AC108_DMIC_EN 0 //0:ADC 1:DMIC
#undef AC108_IDLE_RESET_EN //reset AC108 when in idle time
#define AC108_POWERON_RESET_EN 1 //AC108 poweron soft reset enable
//add to
#undef AC108_POWERON_RESET_EN
#define AC108_REGULATOR_NAME "regulator_name"
#define AC108_RATES (SNDRV_PCM_RATE_8000_96000 | SNDRV_PCM_RATE_KNOT)
#define AC108_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
struct real_val_to_reg_val {
unsigned int real_val;
unsigned int reg_val;
};
struct pll_div {
unsigned int freq_in;
unsigned int freq_out;
unsigned int m1;
unsigned int m2;
unsigned int n;
unsigned int k1;
unsigned int k2;
};
#if 0
static const unsigned char ac108_reg_addr_list[] = {
CHIP_AUDIO_RST,
//Power Control
PWR_CTRL1,
PWR_CTRL2,
PWR_CTRL3,
PWR_CTRL4,
PWR_CTRL5,
PWR_CTRL6,
PWR_CTRL7,
PWR_CTRL8,
PWR_CTRL9,
//PLL Configure Control
PLL_CTRL1,
PLL_CTRL2,
PLL_CTRL3,
PLL_CTRL4,
PLL_CTRL5,
PLL_CTRL6,
PLL_CTRL7,
PLL_LOCK_CTRL,
//System Clock Control
SYSCLK_CTRL,
MOD_CLK_EN,
MOD_RST_CTRL,
DSM_CLK_CTRL,
//I2S Common Control
I2S_CTRL,
I2S_BCLK_CTRL,
I2S_LRCK_CTRL1,
I2S_LRCK_CTRL2,
I2S_FMT_CTRL1,
I2S_FMT_CTRL2,
I2S_FMT_CTRL3,
//I2S TX1 Control
I2S_TX1_CTRL1,
I2S_TX1_CTRL2,
I2S_TX1_CTRL3,
I2S_TX1_CHMP_CTRL1,
I2S_TX1_CHMP_CTRL2,
I2S_TX1_CHMP_CTRL3,
I2S_TX1_CHMP_CTRL4,
//I2S TX2 Control
I2S_TX2_CTRL1,
I2S_TX2_CTRL2,
I2S_TX2_CTRL3,
I2S_TX2_CHMP_CTRL1,
I2S_TX2_CHMP_CTRL2,
I2S_TX2_CHMP_CTRL3,
I2S_TX2_CHMP_CTRL4,
//I2S RX1 Control
I2S_RX1_CTRL1,
I2S_RX1_CHMP_CTRL1,
I2S_RX1_CHMP_CTRL2,
I2S_RX1_CHMP_CTRL3,
I2S_RX1_CHMP_CTRL4,
//I2S Loopback Debug
I2S_LPB_DEBUG,
//ADC Common Control
ADC_SPRC,
ADC_DIG_EN,
DMIC_EN,
ADC_DSR,
ADC_FIR,
ADC_DDT_CTRL,
//HPF Control
HPF_EN,
HPF_COEF_REGH1,
HPF_COEF_REGH2,
HPF_COEF_REGL1,
HPF_COEF_REGL2,
HPF_GAIN_REGH1,
HPF_GAIN_REGH2,
HPF_GAIN_REGL1,
HPF_GAIN_REGL2,
//ADC Digital Channel Volume Control
ADC1_DVOL_CTRL,
ADC2_DVOL_CTRL,
ADC3_DVOL_CTRL,
ADC4_DVOL_CTRL,
//ADC Digital Mixer Source and Gain Control
ADC1_DMIX_SRC,
ADC2_DMIX_SRC,
ADC3_DMIX_SRC,
ADC4_DMIX_SRC,
//ADC Digital Debug Control
ADC_DIG_DEBUG,
//I2S Pad Drive Control
I2S_DAT_PADDRV_CTRL,
I2S_CLK_PADDRV_CTRL,
//Analog PGA Control
ANA_PGA1_CTRL,
ANA_PGA2_CTRL,
ANA_PGA3_CTRL,
ANA_PGA4_CTRL,
//MIC Offset Control
MIC_OFFSET_CTRL1,
MIC_OFFSET_CTRL2,
MIC1_OFFSET_STATU1,
MIC1_OFFSET_STATU2,
MIC2_OFFSET_STATU1,
MIC2_OFFSET_STATU2,
MIC3_OFFSET_STATU1,
MIC3_OFFSET_STATU2,
MIC4_OFFSET_STATU1,
MIC4_OFFSET_STATU2,
//ADC1 Analog Control
ANA_ADC1_CTRL1,
ANA_ADC1_CTRL2,
ANA_ADC1_CTRL3,
ANA_ADC1_CTRL4,
ANA_ADC1_CTRL5,
ANA_ADC1_CTRL6,
ANA_ADC1_CTRL7,
//ADC2 Analog Control
ANA_ADC2_CTRL1,
ANA_ADC2_CTRL2,
ANA_ADC2_CTRL3,
ANA_ADC2_CTRL4,
ANA_ADC2_CTRL5,
ANA_ADC2_CTRL6,
ANA_ADC2_CTRL7,
//ADC3 Analog Control
ANA_ADC3_CTRL1,
ANA_ADC3_CTRL2,
ANA_ADC3_CTRL3,
ANA_ADC3_CTRL4,
ANA_ADC3_CTRL5,
ANA_ADC3_CTRL6,
ANA_ADC3_CTRL7,
//ADC4 Analog Control
ANA_ADC4_CTRL1,
ANA_ADC4_CTRL2,
ANA_ADC4_CTRL3,
ANA_ADC4_CTRL4,
ANA_ADC4_CTRL5,
ANA_ADC4_CTRL6,
ANA_ADC4_CTRL7,
//GPIO Configure
GPIO_CFG1,
GPIO_CFG2,
GPIO_DAT,
GPIO_DRV,
GPIO_PULL,
GPIO_INT_CFG,
GPIO_INT_EN,
GPIO_INT_STATUS,
//Misc
BGTC_DAT,
BGVC_DAT,
PRNG_CLK_CTRL,
};
#endif
static const struct real_val_to_reg_val ac108_sample_rate[] = {
{8000, 0},
{11025, 1},
{12000, 2},
{16000, 3},
{22050, 4},
{24000, 5},
{32000, 6},
{44100, 7},
{48000, 8},
{96000, 9},
};
static const struct real_val_to_reg_val ac108_sample_resolution[] = {
{8, 1},
{12, 2},
{16, 3},
{20, 4},
{24, 5},
{28, 6},
{32, 7},
};
static const struct real_val_to_reg_val ac108_bclk_div[] = {
{0, 0},
{1, 1},
{2, 2},
{4, 3},
{6, 4},
{8, 5},
{12, 6},
{16, 7},
{24, 8},
{32, 9},
{48, 10},
{64, 11},
{96, 12},
{128, 13},
{176, 14},
{192, 15},
};
//FOUT =(FIN * N) / [(M1+1) * (M2+1)*(K1+1)*(K2+1)] ;
//M1[0,31], M2[0,1], N[0,1023], K1[0,31], K2[0,1]
static const struct pll_div ac108_pll_div[] = {
{400000, 24576000, 0, 0, 983, 7, 1}, //<out: 24.575M>
{512000, 24576000, 0, 0, 960, 9, 1}, //24576000/48
{768000, 24576000, 0, 0, 640, 9, 1}, //24576000/32
{800000, 24576000, 0, 0, 768, 24, 0},
{1024000, 24576000, 0, 0, 480, 9, 1}, //24576000/24
{1600000, 24576000, 0, 0, 384, 24, 0},
{2048000, 24576000, 0, 0, 240, 9, 1}, //24576000/12
{3072000, 24576000, 0, 0, 160, 9, 1}, //24576000/8
{4096000, 24576000, 0, 0, 120, 9, 1}, //24576000/6
{6000000, 24576000, 4, 0, 512, 24, 0},
{12000000, 24576000, 9, 0, 512, 24, 0},
{13000000, 24576000, 12, 0, 639, 12, 1}, //<out: 24.577M>
{15360000, 24576000, 9, 0, 320, 9, 1},
{16000000, 24576000, 9, 0, 384, 24, 0},
{19200000, 24576000, 11, 0, 384, 24, 0},
{19680000, 24576000, 15, 1, 999, 24, 0}, //<out: 24.575M>
{24000000, 24576000, 9, 0, 256, 24, 0},
{400000, 22579200, 0, 0, 1016, 8, 1}, //<out: 22.5778M>
{512000, 22579200, 0, 0, 882, 9, 1},
{768000, 22579200, 0, 0, 588, 9, 1},
{800000, 22579200, 0, 0, 508, 8, 1}, //<out: 22.5778M>
{1024000, 22579200, 0, 0, 441, 9, 1},
{1600000, 22579200, 0, 0, 254, 8, 1}, //<out: 22.5778M>
{2048000, 22579200, 1, 0, 441, 9, 1},
{3072000, 22579200, 2, 0, 441, 9, 1},
{4096000, 22579200, 3, 0, 441, 9, 1},
{6000000, 22579200, 5, 0, 429, 18, 0}, //<out: 22.5789M>
{12000000, 22579200, 11, 0, 429, 18, 0}, //<out: 22.5789M>
{13000000, 22579200, 12, 0, 429, 18, 0}, //<out: 22.5789M>
{15360000, 22579200, 14, 0, 441, 9, 1},
{16000000, 22579200, 24, 0, 882, 24, 0},
{19200000, 22579200, 4, 0, 147, 24, 0},
{19680000, 22579200, 13, 1, 771, 23, 0}, //<out: 22.5793M>
{24000000, 22579200, 24, 0, 588, 24, 0},
{12288000, 24576000, 9, 0, 400, 9, 1}, //24576000/2
{11289600, 22579200, 9, 0, 400, 9, 1}, //22579200/2
{24576000/1, 24576000, 9, 0, 200, 9, 1}, //24576000
{24576000/4, 24576000, 4, 0, 400, 9, 1}, //6144000
{24576000/16, 24576000, 0, 0, 320, 9, 1}, //1536000
{24576000/64, 24576000, 0, 0, 640, 4, 1}, //384000
{24576000/96, 24576000, 0, 0, 960, 4, 1}, //256000
{24576000/128, 24576000, 0, 0, 512, 1, 1}, //192000
{24576000/176, 24576000, 0, 0, 880, 4, 0}, //140000
{24576000/192, 24576000, 0, 0, 960, 4, 0}, //128000
{22579200/1, 22579200, 9, 0, 200, 9, 1}, //22579200
{22579200/4, 22579200, 4, 0, 400, 9, 1}, //5644800
{22579200/16, 22579200, 0, 0, 320, 9, 1}, //1411200
{22579200/64, 22579200, 0, 0, 640, 4, 1}, //352800
{22579200/96, 22579200, 0, 0, 960, 4, 1}, //235200
{22579200/128, 22579200, 0, 0, 512, 1, 1}, //176400
{22579200/176, 22579200, 0, 0, 880, 4, 0}, //128290
{22579200/192, 22579200, 0, 0, 960, 4, 0}, //117600
{22579200/6, 22579200, 2, 0, 360, 9, 1}, //3763200
{22579200/8, 22579200, 0, 0, 160, 9, 1}, //2822400
{22579200/12, 22579200, 0, 0, 240, 9, 1}, //1881600
{22579200/24, 22579200, 0, 0, 480, 9, 1}, //940800
{22579200/32, 22579200, 0, 0, 640, 9, 1}, //705600
{22579200/48, 22579200, 0, 0, 960, 9, 1}, //470400
};
static twi_status_t ac108_init_i2c_device(twi_port_t port)
{
twi_status_t ret = 0;
ret = hal_twi_init(port);
if (ret != TWI_STATUS_OK) {
snd_err("init i2c err ret=%d.\n", ret);
return ret;
}
return TWI_STATUS_OK;
}
static twi_status_t ac108_deinit_i2c_device(twi_port_t port)
{
twi_status_t ret = 0;
ret = hal_twi_uninit(port);
if (ret != TWI_STATUS_OK) {
snd_err("init i2c err ret=%d.\n", ret);
return ret;
}
return TWI_STATUS_OK;
}
static twi_status_t ac108_read(struct twi_device *twi_dev,
unsigned char reg, unsigned char *rt_value)
{
twi_status_t ret;
hal_twi_control(twi_dev->bus, I2C_SLAVE, &twi_dev->addr);
ret = hal_twi_read(twi_dev->bus, reg, rt_value, 1);
if (ret != TWI_STATUS_OK) {
snd_err("error = %d [REG-0x%02x]\n", ret, reg);
return ret;
}
return TWI_STATUS_OK;
}
static int ac108_write(struct twi_device *twi_dev,
unsigned char reg, unsigned char value)
{
twi_status_t ret;
twi_msg_t msg;
unsigned char buf[2] = {reg, value};
msg.flags = 0;
msg.addr = twi_dev->addr;
msg.len = 2;
msg.buf = buf;
ret = hal_twi_control(twi_dev->bus, I2C_RDWR, &msg);
if (ret != TWI_STATUS_OK) {
snd_err("error = %d [REG-0x%02x]\n", ret, reg);
return ret;
}
return TWI_STATUS_OK;
}
static int ac108_update_bits(struct twi_device *twi_dev,
unsigned char reg, unsigned char mask, unsigned char value)
{
unsigned char val_old = 0;
unsigned char val_new = 0;
ac108_read(twi_dev, reg, &val_old);
val_new = (val_old & ~mask) | (value & mask);
if (val_new != val_old)
ac108_write(twi_dev, reg, val_new);
return 0;
}
static int ac108_multi_chips_write(struct ac108_priv *ac108,
unsigned char reg, unsigned char value)
{
unsigned char i;
for (i = 0; i < ac108->param.chip_num; i++)
ac108_write(&(ac108->param.twi_dev[i]), reg, value);
return 0;
}
static int ac108_multi_chips_update_bits(struct ac108_priv *ac108,
unsigned char reg, unsigned char mask, unsigned char value)
{
unsigned char i;
for (i = 0; i < ac108->param.chip_num; i++)
ac108_update_bits(&(ac108->param.twi_dev[i]), reg, mask, value);
return 0;
}
#ifdef AC108_KCONTROL_TEST_EN
/* ac108 mixer controls define */
static const char * const adc_digital_src_mux[] = {
"ADC1_Switch", "ADC2_Switch", "ADC3_Switch", "ADC4_Switch",
};
static const char * const adc12_dmic1_src_mux[] = {
"ADC12_Switch", "DMIC1_Switch",
};
static const char * const adc34_dmic2_src_mux[] = {
"ADC34_Switch", "DMIC2_Switch",
};
static const char * const i2s_tx1ch_map_mux[] = {
"ADC1_Sample_Switch", "ADC2_Sample_Switch",
"ADC3_Sample_Switch", "ADC4_Sample_Switch",
};
static int ac108_ctl_enum_value_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_info *info)
{
unsigned int val = 0;
if (kcontrol->type != SND_CTL_ELEM_TYPE_ENUMERATED) {
snd_err("invalid kcontrol type = %d.\n", kcontrol->type);
return -EINVAL;
}
if (kcontrol->private_data_type == SND_MODULE_CODEC) {
struct snd_codec *codec = kcontrol->private_data;
struct ac108_priv *ac108 = codec->private_data;
struct ac108_param *param = &ac108->param;
ac108_read(&param->twi_dev[0], kcontrol->reg, (unsigned char *)&val);
} else {
snd_err("%s invalid kcontrol data type = %d.\n", __func__,
kcontrol->private_data_type);
}
snd_kcontrol_to_snd_ctl_info(kcontrol, info, val);
return 0;
}
static int ac108_ctl_enum_value_set(struct snd_kcontrol *kcontrol, unsigned long val)
{
if (kcontrol->type != SND_CTL_ELEM_TYPE_ENUMERATED) {
snd_err("invalid kcontrol type = %d.\n", kcontrol->type);
return -EINVAL;
}
if (val >= kcontrol->items) {
snd_err("invalid kcontrol items = %ld.\n", val);
return -EINVAL;
}
if (kcontrol->private_data_type == SND_MODULE_CODEC) {
struct snd_codec *codec = kcontrol->private_data;
struct ac108_priv *ac108 = codec->private_data;
struct ac108_param *param = &ac108->param;
ac108_update_bits(&param->twi_dev[0], kcontrol->reg,
(kcontrol->mask << kcontrol->shift),
((unsigned int)val << kcontrol->shift));
} else {
snd_err("invalid kcontrol data type = %d.\n",
kcontrol->private_data_type);
return -EINVAL;
}
kcontrol->value = val & kcontrol->mask;
snd_info("mask:0x%x, shift:%d, value:0x%x\n",
kcontrol->mask, kcontrol->shift, kcontrol->value);
return 0;
}
static int ac108_ctl_value_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_info *info)
{
unsigned int val = 0;
int mask = 0;
if (kcontrol->type != SND_CTL_ELEM_TYPE_INTEGER) {
snd_err("invalid kcontrol type = %d.\n", kcontrol->type);
return -EINVAL;
}
if (kcontrol->private_data_type == SND_MODULE_CODEC) {
struct snd_codec *codec = kcontrol->private_data;
struct ac108_priv *ac108 = codec->private_data;
struct ac108_param *param = &ac108->param;
ac108_read(&param->twi_dev[0], kcontrol->reg, (unsigned char *)&val);
} else {
snd_err("%s invalid kcontrol data type = %d.\n", __func__,
kcontrol->private_data_type);
}
snd_kcontrol_to_snd_ctl_info(kcontrol, info, val);
return 0;
}
static int ac108_ctl_value_set(struct snd_kcontrol *kcontrol, unsigned long val)
{
if (kcontrol->type != SND_CTL_ELEM_TYPE_INTEGER) {
snd_err("invalid kcontrol type = %d.\n", kcontrol->type);
return -EINVAL;
}
if (kcontrol->private_data_type == SND_MODULE_CODEC) {
struct snd_codec *codec = kcontrol->private_data;
struct ac108_priv *ac108 = codec->private_data;
struct ac108_param *param = &ac108->param;
ac108_update_bits(&param->twi_dev[0], kcontrol->reg,
(kcontrol->mask << kcontrol->shift),
((unsigned int)val << kcontrol->shift));
} else {
snd_err("%s invalid kcontrol data type = %d.\n", __func__,
kcontrol->private_data_type);
}
snd_info("mask:0x%x, shitf:%d, value:0x%x\n",
kcontrol->mask, kcontrol->shift, val);
return 0;
}
static struct snd_kcontrol ac108_codec_controls[] = {
//ADC1 DIG MUX
SND_CTL_ENUM_VALUE_EXT("ADC1 DIG MUX", ARRAY_SIZE(adc_digital_src_mux),
adc_digital_src_mux, ADC_DSR, DIG_ADC1_SRS,
SND_CTL_ENUM_AUTO_MASK,
ac108_ctl_enum_value_get, ac108_ctl_enum_value_set),
//ADC2 DIG MUX
SND_CTL_ENUM_VALUE_EXT("ADC2 DIG MUX", ARRAY_SIZE(adc_digital_src_mux),
adc_digital_src_mux, ADC_DSR, DIG_ADC2_SRS,
SND_CTL_ENUM_AUTO_MASK,
ac108_ctl_enum_value_get, ac108_ctl_enum_value_set),
//ADC3 DIG MUX
SND_CTL_ENUM_VALUE_EXT("ADC3 DIG MUX", ARRAY_SIZE(adc_digital_src_mux),
adc_digital_src_mux, ADC_DSR, DIG_ADC3_SRS,
SND_CTL_ENUM_AUTO_MASK,
ac108_ctl_enum_value_get, ac108_ctl_enum_value_set),
//ADC4 DIG MUX
SND_CTL_ENUM_VALUE_EXT("ADC4 DIG MUX", ARRAY_SIZE(adc_digital_src_mux),
adc_digital_src_mux, ADC_DSR, DIG_ADC4_SRS,
SND_CTL_ENUM_AUTO_MASK,
ac108_ctl_enum_value_get, ac108_ctl_enum_value_set),
//ADC12 DMIC1 MUX
SND_CTL_ENUM_VALUE_EXT("ADC12 DMIC1 MUX", ARRAY_SIZE(adc12_dmic1_src_mux),
adc12_dmic1_src_mux, DMIC_EN, DMIC1_EN,
SND_CTL_ENUM_AUTO_MASK,
ac108_ctl_enum_value_get, ac108_ctl_enum_value_set),
//ADC34 DMIC2 MUX
SND_CTL_ENUM_VALUE_EXT("ADC34 DMIC2 MUX", ARRAY_SIZE(adc34_dmic2_src_mux),
adc34_dmic2_src_mux, DMIC_EN, DMIC2_EN,
SND_CTL_ENUM_AUTO_MASK,
ac108_ctl_enum_value_get, ac108_ctl_enum_value_set),
//ADC1 DIG MIXER
SND_CTL_KCONTROL_VALUE_EXT("ADC1 DIG MIXER ADC1 DAT switch",
ADC1_DMIX_SRC, ADC1_ADC1_DMXL_SRC, 1, 0,
ac108_ctl_value_get, ac108_ctl_value_set),
SND_CTL_KCONTROL_VALUE_EXT("ADC1 DIG MIXER ADC2 DAT switch",
ADC1_DMIX_SRC, ADC1_ADC2_DMXL_SRC, 1, 0,
ac108_ctl_value_get, ac108_ctl_value_set),
SND_CTL_KCONTROL_VALUE_EXT("ADC1 DIG MIXER ADC3 DAT switch",
ADC1_DMIX_SRC, ADC1_ADC3_DMXL_SRC, 1, 0,
ac108_ctl_value_get, ac108_ctl_value_set),
SND_CTL_KCONTROL_VALUE_EXT("ADC1 DIG MIXER ADC4 DAT switch",
ADC1_DMIX_SRC, ADC1_ADC4_DMXL_SRC, 1, 0,
ac108_ctl_value_get, ac108_ctl_value_set),
//ADC2 DIG MIXER
SND_CTL_KCONTROL_VALUE_EXT("ADC2 DIG MIXER ADC1 DAT switch",
ADC2_DMIX_SRC, ADC2_ADC1_DMXL_SRC, 1, 0,
ac108_ctl_value_get, ac108_ctl_value_set),
SND_CTL_KCONTROL_VALUE_EXT("ADC2 DIG MIXER ADC2 DAT switch",
ADC2_DMIX_SRC, ADC2_ADC2_DMXL_SRC, 1, 0,
ac108_ctl_value_get, ac108_ctl_value_set),
SND_CTL_KCONTROL_VALUE_EXT("ADC2 DIG MIXER ADC3 DAT switch",
ADC2_DMIX_SRC, ADC2_ADC3_DMXL_SRC, 1, 0,
ac108_ctl_value_get, ac108_ctl_value_set),
SND_CTL_KCONTROL_VALUE_EXT("ADC2 DIG MIXER ADC4 DAT switch",
ADC2_DMIX_SRC, ADC2_ADC4_DMXL_SRC, 1, 0,
ac108_ctl_value_get, ac108_ctl_value_set),
//ADC3 DIG MIXER
SND_CTL_KCONTROL_VALUE_EXT("ADC3 DIG MIXER ADC1 DAT switch",
ADC3_DMIX_SRC, ADC3_ADC1_DMXL_SRC, 1, 0,
ac108_ctl_value_get, ac108_ctl_value_set),
SND_CTL_KCONTROL_VALUE_EXT("ADC3 DIG MIXER ADC2 DAT switch",
ADC3_DMIX_SRC, ADC3_ADC2_DMXL_SRC, 1, 0,
ac108_ctl_value_get, ac108_ctl_value_set),
SND_CTL_KCONTROL_VALUE_EXT("ADC3 DIG MIXER ADC3 DAT switch",
ADC3_DMIX_SRC, ADC3_ADC3_DMXL_SRC, 1, 0,
ac108_ctl_value_get, ac108_ctl_value_set),
SND_CTL_KCONTROL_VALUE_EXT("ADC3 DIG MIXER ADC4 DAT switch",
ADC3_DMIX_SRC, ADC3_ADC4_DMXL_SRC, 1, 0,
ac108_ctl_value_get, ac108_ctl_value_set),
//ADC4 DIG MIXER
SND_CTL_KCONTROL_VALUE_EXT("ADC4 DIG MIXER ADC1 DAT switch",
ADC4_DMIX_SRC, ADC4_ADC1_DMXL_SRC, 1, 0,
ac108_ctl_value_get, ac108_ctl_value_set),
SND_CTL_KCONTROL_VALUE_EXT("ADC4 DIG MIXER ADC2 DAT switch",
ADC4_DMIX_SRC, ADC4_ADC2_DMXL_SRC, 1, 0,
ac108_ctl_value_get, ac108_ctl_value_set),
SND_CTL_KCONTROL_VALUE_EXT("ADC4 DIG MIXER ADC3 DAT switch",
ADC4_DMIX_SRC, ADC4_ADC3_DMXL_SRC, 1, 0,
ac108_ctl_value_get, ac108_ctl_value_set),
SND_CTL_KCONTROL_VALUE_EXT("ADC4 DIG MIXER ADC4 DAT switch",
ADC4_DMIX_SRC, ADC4_ADC4_DMXL_SRC, 1, 0,
ac108_ctl_value_get, ac108_ctl_value_set),
//I2S TX1 CH1 MUX
SND_CTL_ENUM_VALUE_EXT("I2S TX1 CH1 MUX", ARRAY_SIZE(i2s_tx1ch_map_mux),
i2s_tx1ch_map_mux, I2S_TX1_CHMP_CTRL1, TX1_CH1_MAP,
SND_CTL_ENUM_AUTO_MASK,
ac108_ctl_enum_value_get, ac108_ctl_enum_value_set),
//I2S TX1 CH2 MUX
SND_CTL_ENUM_VALUE_EXT("I2S TX1 CH2 MUX", ARRAY_SIZE(i2s_tx1ch_map_mux),
i2s_tx1ch_map_mux, I2S_TX1_CHMP_CTRL1, TX1_CH2_MAP,
SND_CTL_ENUM_AUTO_MASK,
ac108_ctl_enum_value_get, ac108_ctl_enum_value_set),
//I2S TX1 CH3 MUX
SND_CTL_ENUM_VALUE_EXT("I2S TX1 CH3 MUX", ARRAY_SIZE(i2s_tx1ch_map_mux),
i2s_tx1ch_map_mux, I2S_TX1_CHMP_CTRL1, TX1_CH3_MAP,
SND_CTL_ENUM_AUTO_MASK,
ac108_ctl_enum_value_get, ac108_ctl_enum_value_set),
//I2S TX1 CH4 MUX
SND_CTL_ENUM_VALUE_EXT("I2S TX1 CH4 MUX", ARRAY_SIZE(i2s_tx1ch_map_mux),
i2s_tx1ch_map_mux, I2S_TX1_CHMP_CTRL1, TX1_CH4_MAP,
SND_CTL_ENUM_AUTO_MASK,
ac108_ctl_enum_value_get, ac108_ctl_enum_value_set),
};
#endif
static int ac108_codec_startup(struct snd_pcm_substream *substream,
struct snd_dai *dai)
{
struct snd_codec *codec = dai->component;
struct ac108_priv *ac108 = codec->private_data;
struct ac108_param *param = &ac108->param;
int ret = 0;
snd_print("\n");
return 0;
}
static int ac108_codec_set_pll(struct snd_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out)
{
struct snd_codec *codec = dai->component;
struct ac108_priv *ac108 = codec->private_data;
struct ac108_param *param = &ac108->param;
unsigned int i = 0;
unsigned int m1 = 0;
unsigned int m2 = 0;
unsigned int n = 0;
unsigned int k1 = 0;
unsigned int k2 = 0;
snd_print("\n");
if (freq_out == 0) {
snd_err("freq_out is 0.\n");
return 0;
}
//default: freq_int == freq_out; pll_id = 0; source = 0;
if (freq_in < 128000 || freq_in > 24576000) {
snd_err("AC108 PLLCLK source input freq only support [128K,24M],while now %u\n\n", freq_in);
return -EINVAL;
} else if ((freq_in == 24576000 || freq_in == 22579200) &&
pll_id == SYSCLK_SRC_MCLK) {
//System Clock Source Select MCLK, SYSCLK Enable
snd_print("AC108 don't need to use PLL\n\n");
ac108_multi_chips_update_bits(ac108, SYSCLK_CTRL,
0x1 << SYSCLK_SRC | 0x1 << SYSCLK_EN,
0x0 << SYSCLK_SRC | 0x1 << SYSCLK_EN);
return 0; //Don't need to use PLL
}
//PLL Clock Source Select
switch (pll_id) {
case PLLCLK_SRC_MCLK:
snd_print("AC108 PLLCLK input source select MCLK\n");
ac108_multi_chips_update_bits(ac108, SYSCLK_CTRL, 0x3 << PLLCLK_SRC,
0x0 << PLLCLK_SRC);
break;
case PLLCLK_SRC_BCLK:
snd_print("AC108 PLLCLK input source select BCLK\n");
ac108_multi_chips_update_bits(ac108, SYSCLK_CTRL, 0x3 << PLLCLK_SRC,
0x1 << PLLCLK_SRC);
break;
case PLLCLK_SRC_GPIO2:
snd_print("AC108 PLLCLK input source select GPIO2\n");
ac108_multi_chips_update_bits(ac108, SYSCLK_CTRL, 0x3 << PLLCLK_SRC,
0x2 << PLLCLK_SRC);
break;
case PLLCLK_SRC_GPIO3:
snd_print("AC108 PLLCLK input source select GPIO3\n");
ac108_multi_chips_update_bits(ac108, SYSCLK_CTRL, 0x3 << PLLCLK_SRC,
0x3 << PLLCLK_SRC);
break;
default:
snd_err("AC108 PLLCLK source config error:%d\n", pll_id);
return -EINVAL;
}
//FOUT =(FIN * N) / [(M1+1) * (M2+1)*(K1+1)*(K2+1)] ;
for (i = 0; i < ARRAY_SIZE(ac108_pll_div); i++) {
if (ac108_pll_div[i].freq_in == freq_in &&
ac108_pll_div[i].freq_out == freq_out) {
m1 = ac108_pll_div[i].m1;
m2 = ac108_pll_div[i].m2;
n = ac108_pll_div[i].n;
k1 = ac108_pll_div[i].k1;
k2 = ac108_pll_div[i].k2;
snd_print("AC108 PLL freq_in match:%u, freq_out:%u\n",
freq_in, freq_out);
break;
}
}
if (i == ARRAY_SIZE(ac108_pll_div)) {
snd_err("AC108 don't match PLLCLK freq_in and freq_out table\n");
return -EINVAL;
}
//Config PLL DIV param M1/M2/N/K1/K2
ac108_multi_chips_update_bits(ac108, PLL_CTRL2,
0x1f << PLL_PREDIV1 | 0x1 << PLL_PREDIV2,
m1 << PLL_PREDIV1 | m2 << PLL_PREDIV2);
ac108_multi_chips_update_bits(ac108, PLL_CTRL3, 0x3 << PLL_LOOPDIV_MSB,
(n >> 8) << PLL_LOOPDIV_MSB);
ac108_multi_chips_update_bits(ac108, PLL_CTRL4, 0xff << PLL_LOOPDIV_LSB,
(u8)n << PLL_LOOPDIV_LSB);
ac108_multi_chips_update_bits(ac108, PLL_CTRL5,
(0x1f << PLL_POSTDIV1) | (0x1 << PLL_POSTDIV2),
k1 << PLL_POSTDIV1 | (k2 << PLL_POSTDIV2));
//Config PLL module current
//ac108_multi_chips_update_bits(ac108, PLL_CTRL1, 0x7<<PLL_IBIAS, 0x4<<PLL_IBIAS);
//ac108_multi_chips_update_bits(ac108, PLL_CTRL6, 0x1f<<PLL_CP, 0xf<<PLL_CP);
//PLL module enable
//PLL CLK lock enable
ac108_multi_chips_update_bits(ac108, PLL_LOCK_CTRL, 0x1 << PLL_LOCK_EN,
0x1 << PLL_LOCK_EN);
//PLL Common voltage Enable, PLL Enable
//ac108_multi_chips_update_bits(ac108, PLL_CTRL1,
// 0x1 << PLL_EN | 0x1 << PLL_COM_EN,
// 0x1<<PLL_EN | 0x1<<PLL_COM_EN);
//PLLCLK Enable, SYSCLK Enable
//0x1<<SYSCLK_SRC
ac108_multi_chips_update_bits(ac108, SYSCLK_CTRL,
0x1 << PLLCLK_EN | 0x1 << SYSCLK_EN,
0x1 << PLLCLK_EN | 0x1 << SYSCLK_EN);
return 0;
}
static int ac108_codec_set_clkdiv(struct snd_dai *dai, int div_id, int div)
{
struct snd_codec *codec = dai->component;
struct ac108_priv *ac108 = codec->private_data;
struct ac108_param *param = &ac108->param;
unsigned int i = 0;
unsigned int bclk_div = 0;
unsigned int bclk_div_reg_val = 0;
snd_print("\n");
/* default: div_id = 0; */
if (param->daudio_master == 4) {
/*
*daudio_master(val << 12):
* 1: SND_SOC_DAIFMT_CBM_CFM(codec clk & FRM master)
* 4: SND_SOC_DAIFMT_CBS_CFS(codec clk & FRM slave)
*/
snd_print("AC108 work as Slave mode, don't need to config BCLK_DIV.\n");
return 0;
}
/* SND_SOC_DAIFMT_I2S*/
switch (param->daudio_format) {
case SND_SOC_DAIFMT_DSP_A:
case SND_SOC_DAIFMT_DSP_B:
bclk_div = div / param->lrck_period;
break;
case SND_SOC_DAIFMT_I2S:
case SND_SOC_DAIFMT_RIGHT_J:
case SND_SOC_DAIFMT_LEFT_J:
default:
bclk_div = div / (2 * param->lrck_period);
break;
}
for (i = 0; i < ARRAY_SIZE(ac108_bclk_div); i++) {
if (ac108_bclk_div[i].real_val == bclk_div) {
bclk_div_reg_val = ac108_bclk_div[i].reg_val;
snd_print("AC108 set BCLK_DIV_[%u]\n", bclk_div);
break;
}
}
if (i == ARRAY_SIZE(ac108_bclk_div)) {
snd_err("AC108 don't support BCLK_DIV_[%u]\n", bclk_div);
return -EINVAL;
}
//AC108 set BCLK DIV
ac108_multi_chips_update_bits(ac108, I2S_BCLK_CTRL, 0xf << BCLKDIV,
bclk_div_reg_val << BCLKDIV);
return 0;
}
static int ac108_codec_set_fmt(struct snd_dai *dai, unsigned int fmt)
{
struct snd_codec *codec = dai->component;
struct ac108_priv *ac108 = codec->private_data;
struct ac108_param *param = &ac108->param;
unsigned int i = 0;
unsigned char tx_offset = 0;
unsigned char i2s_mode = 0;
unsigned char lrck_polarity = 0;
unsigned char brck_polarity = 0;
snd_print("\n");
//AC108 config Master/Slave mode
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM: //AC108 Master
snd_print("AC108 set to work as Master\n");
//BCLK & LRCK output
ac108_update_bits(&param->twi_dev[0], I2S_CTRL, 0x3 << LRCK_IOEN,
0x3 << LRCK_IOEN);
break;
case SND_SOC_DAIFMT_CBS_CFS: //AC108 Slave
snd_print("AC108 set to work as Slave\n");
//BCLK & LRCK input
ac108_update_bits(&param->twi_dev[0], I2S_CTRL, 0x3 << LRCK_IOEN,
0x0 << LRCK_IOEN);
break;
default:
snd_err("AC108 Master/Slave mode config error:%u\n\n",
(fmt & SND_SOC_DAIFMT_MASTER_MASK) >> 12);
return -EINVAL;
}
for (i = 0; i < param->chip_num; i++) {
/*
* multi_chips: only one chip set as Master,
* and the others also need to set as Slave
*/
ac108_update_bits(&param->twi_dev[i], I2S_CTRL, 0x3 << LRCK_IOEN,
0x0 << LRCK_IOEN);
}
//AC108 config I2S/LJ/RJ/PCM format
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
snd_print("AC108 config I2S format\n");
i2s_mode = LEFT_JUSTIFIED_FORMAT;
tx_offset = 1;
break;
case SND_SOC_DAIFMT_RIGHT_J:
snd_print("AC108 config RIGHT-JUSTIFIED format\n");
i2s_mode = RIGHT_JUSTIFIED_FORMAT;
tx_offset = 0;
break;
case SND_SOC_DAIFMT_LEFT_J:
snd_print("AC108 config LEFT-JUSTIFIED format\n");
i2s_mode = LEFT_JUSTIFIED_FORMAT;
tx_offset = 0;
break;
case SND_SOC_DAIFMT_DSP_A:
snd_print("AC108 config PCM-A format\n");
i2s_mode = PCM_FORMAT;
tx_offset = 1;
break;
case SND_SOC_DAIFMT_DSP_B:
snd_print("AC108 config PCM-B format\n");
i2s_mode = PCM_FORMAT;
tx_offset = 0;
break;
default:
snd_err("AC108 I2S format config error:%u\n\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL;
}
ac108_multi_chips_update_bits(ac108, I2S_FMT_CTRL1,
0x3 << MODE_SEL | 0x1 << TX2_OFFSET | 0x1 << TX1_OFFSET,
i2s_mode<<MODE_SEL | tx_offset<<TX2_OFFSET | tx_offset << TX1_OFFSET);
//AC108 config BCLK&LRCK polarity
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
snd_print("AC108 config: BCLK_normal,LRCK_normal.\n");
brck_polarity = BCLK_NORMAL_DRIVE_N_SAMPLE_P;
lrck_polarity = LRCK_LEFT_LOW_RIGHT_HIGH;
break;
case SND_SOC_DAIFMT_NB_IF:
snd_print("AC108 config: BCLK_normal,LRCK_invert.\n");
brck_polarity = BCLK_NORMAL_DRIVE_N_SAMPLE_P;
lrck_polarity = LRCK_LEFT_HIGH_RIGHT_LOW;
break;
case SND_SOC_DAIFMT_IB_NF:
snd_print("AC108 config: BCLK_invert,LRCK_normal.\n");
brck_polarity = BCLK_INVERT_DRIVE_P_SAMPLE_N;
lrck_polarity = LRCK_LEFT_LOW_RIGHT_HIGH;
break;
case SND_SOC_DAIFMT_IB_IF:
snd_print("AC108 config: BCLK_invert,LRCK_invert.\n");
brck_polarity = BCLK_INVERT_DRIVE_P_SAMPLE_N;
lrck_polarity = LRCK_LEFT_HIGH_RIGHT_LOW;
break;
default:
snd_err("AC108 config BCLK/LRCLK polarity error:%u\n",
(fmt & SND_SOC_DAIFMT_INV_MASK) >> 8);
return -EINVAL;
}
ac108_multi_chips_update_bits(ac108, I2S_BCLK_CTRL, 0x1<<BCLK_POLARITY,
brck_polarity << BCLK_POLARITY);
ac108_multi_chips_update_bits(ac108, I2S_LRCK_CTRL1, 0x1<<LRCK_POLARITY,
lrck_polarity << LRCK_POLARITY);
return 0;
}
static void ac108_codec_set_pga_gain(struct twi_device *twi_dev, struct ac108_param *param)
{
unsigned int i;
snd_print("\n");
if (twi_dev->ref_chan.ref_channel != 0x0) {
snd_info("ref_cfg.ref_channel:0x%x, set ref_pga_gain:%d\n",
twi_dev->ref_chan.ref_channel, twi_dev->ref_chan.ref_pga);
/* set the gain for referenced channels, always used for aec */
for (i = 0; i < 4; i++) {
if ((twi_dev->ref_chan.ref_channel >> i) & 0x1) {
ac108_update_bits(twi_dev, ANA_PGA1_CTRL + i,
0x1F << ADC1_ANALOG_PGA,
twi_dev->ref_chan.ref_pga << ADC1_ANALOG_PGA);
} else
ac108_update_bits(twi_dev, ANA_PGA1_CTRL + i,
0x1F << ADC1_ANALOG_PGA,
param->pga_gain << ADC1_ANALOG_PGA);
}
} else {
for (i = 0; i < 4; i++)
ac108_update_bits(twi_dev, ANA_PGA1_CTRL + i,
0x1F << ADC1_ANALOG_PGA,
param->pga_gain << ADC1_ANALOG_PGA);
}
}
static void ac108_codec_hw_init(struct twi_device *twi_dev, struct ac108_param *param)
{
/*** Chip reset ***/
/*0x00=0x12: reset all registers to their default state*/
//ac108_write(twi_dev, CHIP_AUDIO_RST, 0x12);
#if !AC108_DMIC_EN
/*** Analog voltage enable ***/
/*0x06=0x01: Enable Analog LDO*/
ac108_write(twi_dev, PWR_CTRL6, 0x01);
/*
* 0x07=0x9b: VREF faststart Enable,
* Enable VREF @ 3.4V (5V) or 3.1V (3.3V)
* (needed for Analog LDO and MICBIAS)
*/
ac108_write(twi_dev, PWR_CTRL7, 0x9b);
/*
* 0x09=0x81: VREFP faststart Enable, Enable VREFP_FASTSTART_ENABLE
* (needed by all audio input channels)
*/
ac108_write(twi_dev, PWR_CTRL9, 0x81);
/*
* DSM low power mode enable, Control bias current for
* DSM integrator opamps
*/
ac108_write(twi_dev, ANA_ADC3_CTRL7, 0x0b);
#endif
/*** SYSCLK Config ***/
/*SYSCLK Enable*/
ac108_update_bits(twi_dev, SYSCLK_CTRL, 0x1 << SYSCLK_EN, 0x1 << SYSCLK_EN);
/*
* 0x21=0x93: Module clock enable<I2S, ADC digital,
* MIC offset Calibration, ADC analog>
*/
ac108_write(twi_dev, MOD_CLK_EN, 0x93);
/*
* 0x22=0x93: Module reset de-asserted
* <I2S, ADC digital, MIC offset Calibration, ADC analog>
*/
ac108_write(twi_dev, MOD_RST_CTRL, 0x93);
/*** I2S Common Config ***/
/*SDO1 enable, SDO2 Enable*/
ac108_update_bits(twi_dev, I2S_CTRL, ((0x1 << SDO1_EN) | (0x1 << SDO2_EN)),
((0x1 << SDO1_EN) | (!!AC108_SDO2_EN << SDO2_EN)));
/*SDO drive data and SDI sample data at the different BCLK edge*/
ac108_update_bits(twi_dev, I2S_BCLK_CTRL, (0x1 << EDGE_TRANSFER),
(0x0 << EDGE_TRANSFER));
ac108_update_bits(twi_dev, I2S_LRCK_CTRL1, 0x3 << LRCK_PERIODH,
(((param->lrck_period - 1) >> 8) << LRCK_PERIODH));
/*
* config LRCK period:
* 16bit * 8ch = 128,
* 32bit * 8ch = 256,
* 32bit *16ch = 512
*/
ac108_write(twi_dev, I2S_LRCK_CTRL2, (param->lrck_period - 1) & 0xFF);
/*
* Encoding mode enable, Turn to hi-z state (TDM)
* when not transferring slot
*/
ac108_update_bits(twi_dev, I2S_FMT_CTRL1,
0x1 << ENCD_SEL | 0x1 << TX_SLOT_HIZ | 0x1 << TX_STATE,
!!AC108_ENCODING_EN << ENCD_SEL | 0x0 << TX_SLOT_HIZ | 0x1 << TX_STATE);
/* 8/12/16/20/24/28/32bit Slot Width */
ac108_update_bits(twi_dev, I2S_FMT_CTRL2, 0x7 << SLOT_WIDTH_SEL,
(param->slot_width / 4 - 1) << SLOT_WIDTH_SEL);
/*
* 0x36=0x70: TX MSB first, TX2 Mute, Transfer 0 after
* each sample in each slot(sample resolution < slot width),
* LRCK = 1 BCLK width (short frame), Linear PCM Data Mode
*/
ac108_write(twi_dev, I2S_FMT_CTRL3, AC108_SDO2_EN ? 0x60 : 0x70);
/*0x3C=0xe4: TX1 CHn Map to CHn adc sample, n=[1,4]*/
ac108_write(twi_dev, I2S_TX1_CHMP_CTRL1, 0xe4);
/*0x3D=0xe4: TX1 CHn Map to CH(n-4) adc sample, n=[5,8]*/
ac108_write(twi_dev, I2S_TX1_CHMP_CTRL2, 0xe4);
/*0x3E=0xe4: TX1 CHn Map to CH(n-8) adc sample, n=[9,12]*/
ac108_write(twi_dev, I2S_TX1_CHMP_CTRL3, 0xe4);
/*0x3F=0xe4: TX1 CHn Map to CH(n-12) adc sample, n=[13,16]*/
ac108_write(twi_dev, I2S_TX1_CHMP_CTRL4, 0xe4);
#if AC108_SDO2_EN
/*
* 0x44=0x4e: TX2 CH1/2 Map to CH3/4 adc sample,
* TX2 CH3/4 Map to CH1/2 adc sample
*/
ac108_write(twi_dev, I2S_TX2_CHMP_CTRL1, 0x4e);
/*0x45=0xe4: TX2 CHn Map to CH(n-4) adc sample, n=[5,8]*/
ac108_write(twi_dev, I2S_TX2_CHMP_CTRL2, 0xe4);
/*0x46=0xe4: TX2 CHn Map to CH(n-8) adc sample, n=[9,12]*/
ac108_write(twi_dev, I2S_TX2_CHMP_CTRL3, 0xe4);
/*0x47=0xe4: TX2 CHn Map to CH(n-12) adc sample, n=[13,16]*/
ac108_write(twi_dev, I2S_TX2_CHMP_CTRL4, 0xe4);
#endif
/*** ADC DIG part Config***/
/*0x60=0x03: ADC Sample Rate 16KHz*/
//ac108_write(ADC_SPRC, 0x03);
/*0x61=0x1f: Digital part globe enable, ADCs digital part enable*/
ac108_write(twi_dev, ADC_DIG_EN, 0x1f);
/*0xBB=0x0f: Gating ADCs CLK de-asserted (ADCs CLK Enable)*/
ac108_write(twi_dev, ANA_ADC4_CTRL7, 0x0f);
if (twi_dev->debug_mode) {
/*0x66=0x00: Digital ADCs channel HPF disable*/
ac108_write(twi_dev, HPF_EN, 0x00);
/*
* 0X7F=0x00: ADC pattern select: 0:ADC normal, 1:0x5A5A5A,
* 2:0x123456, 3:0x00, 4~7:I2S RX data
*/
ac108_write(twi_dev, ADC_DIG_DEBUG, twi_dev->debug_mode & 0x7);
}
#if !AC108_DMIC_EN
/*** ADCs analog PGA gain Config***/
ac108_codec_set_pga_gain(twi_dev, param);
/*** enable AAF/ADC/PGA and UnMute Config ***/
/*
* 0xA0=0x07: ADC1 AAF & ADC enable, ADC1 PGA enable,
* ADC1 MICBIAS enable and UnMute
*/
ac108_write(twi_dev, ANA_ADC1_CTRL1, 0x07);
/*
* 0xA7=0x07: ADC2 AAF & ADC enable, ADC2 PGA enable,
* ADC2 MICBIAS enable and UnMute
*/
ac108_write(twi_dev, ANA_ADC2_CTRL1, 0x07);
/*
* 0xAE=0x07: ADC3 AAF & ADC enable, ADC3 PGA enable,
* ADC3 MICBIAS enable and UnMute
*/
ac108_write(twi_dev, ANA_ADC3_CTRL1, 0x07);
/*
* 0xB5=0x07: ADC4 AAF & ADC enable, ADC4 PGA enable,
* ADC4 MICBIAS enable and UnMute
*/
ac108_write(twi_dev, ANA_ADC4_CTRL1, 0x07);
/*
* delay 50ms to let VREF/VRP faststart powerup stable,
* then disable faststart
*/
usleep(50 * 1000);
/* VREF faststart disable */
ac108_update_bits(twi_dev, PWR_CTRL7, 0x1 << VREF_FASTSTART_ENABLE,
0x0 << VREF_FASTSTART_ENABLE);
/*VREFP faststart disable*/
ac108_update_bits(twi_dev, PWR_CTRL9, 0x1 << VREFP_FASTSTART_ENABLE,
0x0 << VREFP_FASTSTART_ENABLE);
#else
/*** DMIC module Enable ***/
/*DMIC1/2 Enable, while ADC DIG source select DMIC1/2*/
ac108_write(twi_dev, DMIC_EN, 0x03);
/*GPIO1 as DMIC1_DAT, GPIO2 as DMIC_CLK*/
ac108_write(twi_dev, GPIO_CFG1, 0xee);
/*GPIO3 as DMIC2_DAT*/
ac108_write(twi_dev, GPIO_CFG2, 0x7e);
#endif
}
static int ac108_codec_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_dai *dai)
{
struct snd_codec *codec = dai->component;
struct ac108_priv *ac108 = codec->private_data;
struct ac108_param *param = &ac108->param;
int i = 0;
int ret = 0;
unsigned char reg_val = 0;
unsigned short channels = 0;
unsigned short channels_en = 0;
unsigned short sample_resolution = 0;
snd_print("\n");
/* set codec dai fmt */
ret = snd_soc_dai_set_fmt(dai, param->daudio_format
| (param->signal_inversion << SND_SOC_DAIFMT_SIG_SHIFT)
| (param->daudio_master << SND_SOC_DAIFMT_MASTER_SHIFT));
if (ret < 0) {
snd_err("codec_dai set pll failed\n");
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
snd_print("AC108 not need playback.\n");
return 0;
}
//AC108 hw init
for (i = 0; i < param->chip_num; i++)
ac108_codec_hw_init(&param->twi_dev[i], param);
//AC108 set sample rate
for (i = 0; i < ARRAY_SIZE(ac108_sample_rate); i++) {
if (ac108_sample_rate[i].real_val ==
params_rate(params) / (AC108_ENCODING_EN ? AC108_ENCODING_CH_NUMS / 2 : 1)) {
ac108_multi_chips_update_bits(ac108, ADC_SPRC,
0xf << ADC_FS_I2S1,
ac108_sample_rate[i].reg_val << ADC_FS_I2S1);
break;
}
}
//AC108 set channels
#if !AC108_SDO2_EN
channels = params_channels(params) *
(AC108_ENCODING_EN ? AC108_ENCODING_CH_NUMS/2 : 1);
for (i = 0; i < (channels + 3) / 4; i++) {
channels_en = (channels >= 4 * (i + 1)) ? 0x000f << (4 * i) :
((1 << (channels % 4)) - 1) << (4 * i);
ac108_write(&param->twi_dev[i], I2S_TX1_CTRL1, channels - 1);
ac108_write(&param->twi_dev[i], I2S_TX1_CTRL2, (unsigned char )channels_en);
ac108_write(&param->twi_dev[i], I2S_TX1_CTRL3, channels_en >> 8);
}
for (; i < param->chip_num; i++) {
ac108_write(&param->twi_dev[i], I2S_TX1_CTRL1, 0);
ac108_write(&param->twi_dev[i], I2S_TX1_CTRL2, 0);
ac108_write(&param->twi_dev[i], I2S_TX1_CTRL3, 0);
}
#else
channels = params_channels(params);
for (i = 0; i < (channels + 3) / 4; i++) {
//(2 >= 4*(i+1)) ? 0x000f<<(4*i) : ((1<<(2%4))-1)<<(4*i);
channels_en = 0x03;
ac108_write(&param->twi_dev[i], I2S_TX1_CTRL1, 2 - 1);
ac108_write(&param->twi_dev[i], I2S_TX1_CTRL2, (u8)channels_en);
ac108_write(&param->twi_dev[i], I2S_TX1_CTRL3, channels_en >> 8);
}
for (; i < param->chip_num; i++) {
ac108_write(&param->twi_dev[i], I2S_TX1_CTRL1, 0);
ac108_write(&param->twi_dev[i], I2S_TX1_CTRL2, 0);
ac108_write(&param->twi_dev[i], I2S_TX1_CTRL3, 0);
}
for (i = 0; i < (channels + 3) / 4; i++) {
//(2 >= 4*(i+1)) ? 0x000f<<(4*i) : ((1<<(2%4))-1)<<(4*i);
channels_en = 0x03;
ac108_write(&param->twi_dev[i], I2S_TX2_CTRL1, 2 - 1);
ac108_write(&param->twi_dev[i], I2S_TX2_CTRL2, (u8)channels_en);
ac108_write(&param->twi_dev[i], I2S_TX2_CTRL3, channels_en >> 8);
}
for (; i < param->chip_num; i++) {
ac108_write(&param->twi_dev[i], I2S_TX2_CTRL1, 0);
ac108_write(&param->twi_dev[i], I2S_TX2_CTRL2, 0);
ac108_write(&param->twi_dev[i], I2S_TX2_CTRL3, 0);
}
#endif
//AC108 set sample resorution
switch (params_format(params)) {
case SND_PCM_FORMAT_S16_LE:
sample_resolution = 16;
break;
case SND_PCM_FORMAT_S24_LE:
sample_resolution = 24;
break;
case SND_PCM_FORMAT_S32_LE:
sample_resolution = 32;
break;
default:
snd_err("AC108 don't supported the sample resolution: %u\n",
params_format(params));
return -EINVAL;
}
#if 0
//AC108_ENCODING_EN
//TX Encoding mode, SR add 4bits to mark channel number
if ((sample_resolution <= 24) && (sample_resolution != 16))
sample_resolution += 4;
#endif
for (i = 0; i < ARRAY_SIZE(ac108_sample_resolution); i++) {
if (ac108_sample_resolution[i].real_val == sample_resolution) {
ac108_multi_chips_update_bits(ac108, I2S_FMT_CTRL2,
0x7 << SAMPLE_RESOLUTION,
ac108_sample_resolution[i].reg_val << SAMPLE_RESOLUTION);
break;
}
}
ac108_read(&param->twi_dev[0], SYSCLK_CTRL, &reg_val);
if (reg_val & 0x80) {
//PLLCLK Enable
//PLL Common voltage Enable, PLL Enable
ac108_multi_chips_update_bits(ac108, PLL_CTRL1,
0x1 << PLL_EN | 0x1 << PLL_COM_EN,
0x1 << PLL_EN | 0x1 << PLL_COM_EN);
if (!(reg_val & 0x08)) {
//SYSCLK select MCLK
//GPIO4 output Clock 24MHz from DPLL
ac108_multi_chips_update_bits(ac108, GPIO_CFG2,
0xf << GPIO4_SELECT, 0x9 << GPIO4_SELECT);
}
}
return 0;
}
static int ac108_codec_set_sysclk(struct snd_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_codec *codec = dai->component;
struct ac108_priv *ac108 = codec->private_data;
struct ac108_param *param = &ac108->param;
snd_print("\n");
//default: clk_id = 0;
switch (clk_id) {
case SYSCLK_SRC_MCLK:
snd_print("AC108 SYSCLK source select MCLK.\n");
//System Clock Source Select MCLK
ac108_multi_chips_update_bits(ac108,
SYSCLK_CTRL, 0x1 << SYSCLK_SRC,
0x0 << SYSCLK_SRC);
break;
case SYSCLK_SRC_PLL:
snd_print("AC108 SYSCLK source select PLL.\n");
//System Clock Source Select PLL
ac108_multi_chips_update_bits(ac108,
SYSCLK_CTRL, 0x1 << SYSCLK_SRC,
0x1 << SYSCLK_SRC);
break;
default:
snd_err("AC108 SYSCLK source config error:%d\n\n", clk_id);
return -EINVAL;
}
//SYSCLK Enable
ac108_multi_chips_update_bits(ac108,
SYSCLK_CTRL, 0x1 << SYSCLK_EN,
0x1 << SYSCLK_EN);
//AC108 TX enable, Globle enable
ac108_multi_chips_update_bits(ac108, I2S_CTRL,
0x1 << TXEN | 0x1 << GEN,
0x1 << TXEN | 0x1 << GEN);
return 0;
}
static int ac108_codec_prepare(struct snd_pcm_substream *substream,
struct snd_dai *dai)
{
struct snd_codec *codec = dai->component;
struct ac108_priv *ac108 = codec->private_data;
struct ac108_param *param = &ac108->param;
snd_print("\n");
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
snd_print("ac108 playback prepare.\n");
} else {
snd_print("ac108 capture prepare.\n");
}
return 0;
}
void ac108_reg_show(void);
static int ac108_codec_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_dai *dai)
{
struct snd_codec *codec = dai->component;
struct ac108_priv *ac108 = codec->private_data;
struct ac108_param *param = &ac108->param;
snd_print("\n");
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
snd_print("ac108 playback trigger start.\n");
} else {
snd_print("ac108 capture trigger start\n");
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
snd_print("ac108 playback trigger stop.\n");
} else {
snd_print("ac108 capture trigger stop\n");
}
break;
default:
return -EINVAL;
}
return 0;
}
static int ac108_codec_hw_free(struct snd_pcm_substream *substream,
struct snd_dai *dai)
{
struct snd_codec *codec = dai->component;
struct ac108_priv *ac108 = codec->private_data;
struct ac108_param *param = &ac108->param;
snd_print("\n");
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
snd_print("AC108 playback hw_free.\n");
return 0;
}
//AC108 I2S Globle disable
ac108_multi_chips_update_bits(ac108, I2S_CTRL,
0x1 << GEN, 0x0 << GEN);
#ifdef AC108_IDLE_RESET_EN
snd_print("AC108 reset all register to their default value\n");
ac108_multi_chips_write(ac108, CHIP_AUDIO_RST, 0x12);
#else
//repair PLL version no sync problem && Encoding no DAT
ac108_multi_chips_update_bits(ac108, PLL_CTRL1,
0x1 << PLL_EN | 0x1 << PLL_COM_EN,
0x0 << PLL_EN | 0x0 << PLL_COM_EN);
ac108_multi_chips_update_bits(ac108, MOD_CLK_EN,
0x1 << I2S | 0x1 << ADC_DIGITAL,
0x0<<I2S | 0x0<<ADC_DIGITAL);
ac108_multi_chips_update_bits(ac108, MOD_RST_CTRL,
0x1 << I2S | 0x1 << ADC_DIGITAL,
0x0 << I2S | 0x0 << ADC_DIGITAL);
#endif
return 0;
}
static void ac108_codec_shutdown(struct snd_pcm_substream *substream,
struct snd_dai *dai)
{
struct snd_codec *codec = dai->component;
struct ac108_priv *ac108 = codec->private_data;
struct ac108_param *param = &ac108->param;
int ret = 0;
unsigned int i = 0;
snd_print("\n");
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
snd_print("AC108 playback shutdown.\n");
} else {
snd_print("AC108 capture shutdown.\n");
}
return;
}
static struct snd_dai_ops ac108_codec_dai_ops = {
.startup = ac108_codec_startup,
.set_sysclk = ac108_codec_set_sysclk,
.set_pll = ac108_codec_set_pll,
.set_clkdiv = ac108_codec_set_clkdiv,
.set_fmt = ac108_codec_set_fmt,
.hw_params = ac108_codec_hw_params,
.prepare = ac108_codec_prepare,
.trigger = ac108_codec_trigger,
.hw_free = ac108_codec_hw_free,
.shutdown = ac108_codec_shutdown,
};
static struct snd_dai ac108_codec_dai[] = {
{
.name = "ac108-codecdai",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_192000
| SNDRV_PCM_RATE_KNOT,
.formats = SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S24_LE
| SNDRV_PCM_FMTBIT_S32_LE,
.rate_min = 8000,
.rate_max = 48000,
},
.capture = {
.stream_name = "Capture",
.channels_min = 1,
.channels_max = 8,
.rates = SNDRV_PCM_RATE_8000_48000
| SNDRV_PCM_RATE_KNOT,
.formats = SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S24_LE,
.rate_min = 8000,
.rate_max = 48000,
},
.ops = &ac108_codec_dai_ops,
},
};
static int ac108_codec_probe(struct snd_codec *codec)
{
unsigned char read_data = 0x0;
struct ac108_priv *ac108 = NULL;
unsigned int i = 0;
twi_status_t ret = 0;
unsigned char reset_cmd[2] = {0x0, 0x12};
struct ac108_param default_param = {
.chip_num = AC108_CHIP_NUM,
.twi_dev = AC108_CHIP_CFG,
.pga_gain = AC108_PGA_GAIN,
.daudio_master = AC108_DAUDIO_MASTER,
.daudio_format = AC108_DAUDIO_FORMAT,
.signal_inversion = AC108_DAUDIO_SIG_INV,
.lrck_period = AC108_LRCK_PERIOD,
.slot_width = AC108_SLOT_WIDTH,
};
if (!codec->codec_dai) {
snd_err("codec->codec_dai is null.\n");
return -EFAULT;
}
ac108 = snd_malloc(sizeof(struct ac108_priv));
if (!ac108) {
snd_err("no memory\n");
return -ENOMEM;
}
snd_print("codec para init.\n");
codec->private_data = (void *)ac108;
ac108->param = default_param;
ac108->codec = codec;
codec->codec_dai->component = codec;
snd_print("init ac108 i2c port.\n");
ret = ac108_init_i2c_device(ac108->param.twi_dev[i].bus);
if (ret != TWI_STATUS_OK) {
snd_err("init i2c err\n");
ret = -EFAULT;
goto err_twi_init;
}
for (i = 0; i < ac108->param.chip_num; i++) {
hal_twi_control(ac108->param.twi_dev[i].bus, I2C_SLAVE, &default_param.twi_dev[i].addr);
}
snd_print("ac108 codec register finished.\n");
return 0;
err_twi_init:
snd_free(ac108);
return ret;
}
static int ac108_codec_remove(struct snd_codec *codec)
{
struct ac108_priv *ac108 = codec->private_data;
struct ac108_param *param = &ac108->param;
unsigned int i = 0;
int ret = 0;
snd_print("deinit ac108 i2c port.\n");
ret = ac108_deinit_i2c_device(param->twi_dev[i].bus);
if (ret != TWI_STATUS_OK) {
snd_err("i2c deinit port %d failed.\n",
param->twi_dev[i].bus);
}
snd_free(ac108);
codec->private_data = NULL;
return 0;
}
struct snd_codec ac108_codec = {
.name = "ac108-codec",
.codec_dai = ac108_codec_dai,
.codec_dai_num = ARRAY_SIZE(ac108_codec_dai),
.private_data = NULL,
.probe = ac108_codec_probe,
.remove = ac108_codec_remove,
#ifdef AC108_KCONTROL_TEST_EN
.controls = ac108_codec_controls,
.num_controls = ARRAY_SIZE(ac108_codec_controls),
#endif
};
/* for ac108 debug */
void ac108_reg_dump_usage(void)
{
printf("\n\n=========ac108 debug===========\n");
printf("Usage: ac108_reg [option]\n");
printf("\t-l, ac108 dev list\n");
printf("\t-h, tools help\n");
printf("\t-d, ac108 dev addr(hex)\n");
printf("\t-r, ac108 reg addr(hex)\n");
printf("\t-n, ac108 reg read num(hex)\n");
printf("\t-s, ac108 show all regs\n");
printf("\t-w, ac108 write reg val(hex)\n");
printf("\n");
}
void ac108_chip_list(void)
{
unsigned int chip_num = AC108_CHIP_NUM;
struct twi_device twi_dev[] = AC108_CHIP_CFG;
int i = 0;
printf("\n\n=========ac108 show ===========\n");
printf("\tac108 dev num:\t%d\n", chip_num);
for (i = 0; i < chip_num; i++)
printf("\t%d i2c%d-0x%02x\n", i, twi_dev[i].bus, twi_dev[i].addr);
printf("===============================\n");
}
void ac108_reg_show(void)
{
unsigned int chip_num = AC108_CHIP_NUM;
struct twi_device twi_dev[] = AC108_CHIP_CFG;
unsigned char read_command;
unsigned char read_data[1] = {0x0};
twi_status_t ret = 0;
unsigned int i = 0;
unsigned int j = 0;
for (i = 0; i < chip_num; i++) {
printf("\n=========ac108 chip [i2c%d-0x%02x] ===========\n",
twi_dev[i].bus, twi_dev[i].addr);
for (j = 0; j <= AC108_REG_MAX; j++) {
if (j % 8 == 0)
printf("\n");
read_command = 0x0 + j;
ret = ac108_read(&twi_dev[i], read_command, read_data);
if (ret != TWI_STATUS_OK) {
snd_err("[i2c%d-0x%02x] read [REG-0x%02x,val-0x%02x] ret = %d.\n",
twi_dev[i].bus, twi_dev[i].addr,
read_command, read_data[0], ret);
}
printf("[0x%02x]: 0x%02x ", read_command, read_data[0]);
}
printf("\n=========================================\n");
}
}
int cmd_ac108_reg(int argc, char ** argv)
{
twi_status_t ret = -1;
unsigned int i;
const struct option long_option[] = {
{"help", 0, NULL, 'h'},
{"list", 0, NULL, 'l'},
{"addr", 1, NULL, 'd'},
{"reg", 1, NULL, 'r'},
{"num", 1, NULL, 'n'},
{"show", 0, NULL, 's'},
{"write", 1, NULL, 'w'},
{NULL, 0, NULL, 0},
};
unsigned int chip_num = AC108_CHIP_NUM;
struct twi_device twi_dev[] = AC108_CHIP_CFG;
unsigned char reset_cmd[2] = {0x0, 0x12};
unsigned char write_cmd[2] = {0x0, 0x0};
unsigned char read_cmd[1] = {0x0};
unsigned char read_data[1] = {0x0};
unsigned int num = 1;
unsigned int twi_addr = 0x3b;
bool wr_flag = 0;
while (1) {
int c;
if ((c = getopt_long(argc, argv, "hlsd:r:n:w:", long_option, NULL)) < 0)
break;
switch (c) {
case 'h':
ac108_reg_dump_usage();
goto ac108_reg_exit;
case 'l':
ac108_chip_list();
goto ac108_reg_exit;
case 's':
ac108_reg_show();
goto ac108_reg_exit;
case 'd':
if (isdigit(*optarg)) {
sscanf(optarg, "0x%x", &twi_addr);
//twi_addr = atoi(optarg);
printf("\ntwi_addr slave address is 0x%02x.\n", twi_addr);
} else
fprintf(stderr, "twi addr is not a digital value.\n");
break;
case 'r':
if (isdigit(*optarg)) {
sscanf(optarg, "0x%hhx", &read_cmd[0]);
//read_cmd[0] = atoi(optarg);
write_cmd[0] = read_cmd[0];
printf("\nreg is 0x%02x.\n", read_cmd[0]);
} else
fprintf(stderr, "reg is not a digital value.\n");
break;
case 'n':
if (isdigit(*optarg)) {
sscanf(optarg, "0x%x", &num);
//num = atoi(optarg);
printf("\nnum is %d.\n", num);
} else
fprintf(stderr, "num is not a digital value.\n");
break;
case 'w':
if (isdigit(*optarg)) {
wr_flag = 1;
sscanf(optarg, "0x%hhx", &write_cmd[1]);
//write_cmd[1] = atoi(optarg);
printf("\nwrite val is 0x%02x.\n", write_cmd[1]);
} else
fprintf(stderr, "write val is not a digital value.\n");
break;
default:
fprintf(stderr, "Invalid switch or option needs an argument.\n");
break;
}
}
//checkout i2c port and addr.
for (i = 0; i < chip_num; i++) {
if (twi_addr == twi_dev[i].addr)
break;
}
if (i >= chip_num) {
fprintf(stderr, "the addr is error.\n");
goto ac108_reg_exit;
}
if ((read_cmd[0] > AC108_REG_MAX) ||
(write_cmd[0] > AC108_REG_MAX)) {
fprintf(stderr, "the reg is over 0x%02x error.\n", AC108_REG_MAX);
goto ac108_reg_exit;
}
if (wr_flag) {
ret = ac108_write(&twi_dev[i], write_cmd[0], write_cmd[1]);
if (ret != TWI_STATUS_OK) {
snd_err("write error [REG-0x%02x,val-0x%02x] ret = %d.\n",
write_cmd[0], write_cmd[1], ret);
}
ret = ac108_read(&twi_dev[i], read_cmd[0], read_data);
if (ret != TWI_STATUS_OK) {
snd_err("write error [I2C%d-0x%0x] REG=0x%02x, val=0x%02x] ret = %d.\n",
twi_dev[i].bus, twi_dev[i].addr, read_cmd[0], read_data[0], ret);
goto ac108_reg_exit;
}
if (read_data[0] == write_cmd[1]) {
printf("write success, [I2C%d-0x%0x] REG=0x%02x, val=0x%02x] ret = %d.\n",
twi_dev[i].bus, twi_dev[i].addr, read_cmd[0], read_data[0], ret);
} else {
printf("write val:0x%02x failed, [I2C%d-0x%0x] REG=0x%02x, val=0x%02x] ret = %d.\n",
write_cmd[1], twi_dev[i].bus, twi_dev[i].addr, read_cmd[0], read_data[0], ret);
}
} else {
for (i = 0; i < num; i++) {
ret = ac108_read(&twi_dev[i], read_cmd[0], read_data);
if (ret != TWI_STATUS_OK) {
snd_err("read error [I2C%d-0x%0x] REG=0x%02x, val=0x%02x] ret = %d.\n",
twi_dev[i].bus, twi_dev[i].addr, read_cmd[0], read_data[0], ret);
goto ac108_reg_exit;
} else {
printf("read success. [I2C%d-0x%0x] REG=0x%02x, val=0x%02x].\n",
twi_dev[i].bus, twi_dev[i].addr, read_cmd[0], read_data[0]);
}
}
}
ac108_reg_exit:
return 0;
}
FINSH_FUNCTION_EXPORT_CMD(cmd_ac108_reg, ac108_reg, ac108 regs dump);