From feb238febfc36b9ed88daf27dd28040e0ecf6589 Mon Sep 17 00:00:00 2001 From: guoyf2 Date: Tue, 25 Aug 2020 15:37:56 +0800 Subject: [PATCH 01/40] Audio bringup: asoc: add aw882xx support Change-Id: Ia5781b73ecaceba7393379da5c38c63112e7f894 Signed-off-by: guoyf2 Reviewed-on: https://gerrit.mot.com/1732545 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Submit-Approved: Jira Key Tested-by: Jira Key Reviewed-by: Yunfei Guo Reviewed-by: Xianhong Li --- asoc/bengal.c | 101 +- asoc/codecs/Android.mk | 9 + asoc/codecs/Kbuild | 9 + asoc/codecs/awinic/aw882xx.c | 3391 ++++++++++++++++++++++++++++++ asoc/codecs/awinic/aw882xx.h | 277 +++ asoc/codecs/awinic/aw882xx_reg.h | 1570 ++++++++++++++ asoc/msm-pcm-routing-v2.c | 55 +- config/bengalauto.conf | 2 +- config/bengalautoconf.h | 3 +- dsp/q6adm.c | 41 + dsp/q6afe.c | 217 ++ 11 files changed, 5668 insertions(+), 7 deletions(-) create mode 100644 asoc/codecs/awinic/aw882xx.c create mode 100644 asoc/codecs/awinic/aw882xx.h create mode 100644 asoc/codecs/awinic/aw882xx_reg.h diff --git a/asoc/bengal.c b/asoc/bengal.c index f3e50ef42c4f..5cfdca61cd55 100644 --- a/asoc/bengal.c +++ b/asoc/bengal.c @@ -378,7 +378,11 @@ static struct dev_config mi2s_rx_cfg[] = { }; static struct dev_config mi2s_tx_cfg[] = { +#ifdef CONFIG_SND_SMARTPA_AW882XX + [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, +#else [PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, +#endif [SEC_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, [TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, [QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, @@ -4389,8 +4393,8 @@ static void *def_wcd_mbhc_cal(void) btn_high = ((void *)&btn_cfg->_v_btn_low) + (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn); - btn_high[0] = 75; - btn_high[1] = 150; + btn_high[0] = 88;//75; + btn_high[1] = 138;//150; btn_high[2] = 237; btn_high[3] = 500; btn_high[4] = 500; @@ -5060,6 +5064,26 @@ static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = { }, }; +#ifdef CONFIG_SND_SMARTPA_AW882XX +static struct snd_soc_dai_link msm_awinic_fe_dai_links[] = { + { + .name = "Primary MI2S TX_Hostless", + .stream_name = "Primary MI2S_TX Hostless Capture", + .cpu_dai_name = "PRI_MI2S_TX_HOSTLESS", + .platform_name = "msm-pcm-hostless", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, +}; +#endif + static struct snd_soc_dai_link msm_common_be_dai_links[] = { /* Backend AFE DAI Links */ { @@ -5376,6 +5400,41 @@ static struct snd_soc_dai_link msm_wcn_btfm_be_dai_links[] = { }, }; +#ifdef CONFIG_SND_SMARTPA_AW882XX +static struct snd_soc_dai_link msm_awinic_be_dai_links[] = { + { + .name = LPASS_BE_PRI_MI2S_RX, + .stream_name = "Primary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "aw882xx_smartpa.1-0034", + .codec_dai_name = "aw882xx-aif", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_MI2S_TX, + .stream_name = "Primary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .codec_name = "aw882xx_smartpa.1-0034", + .codec_dai_name = "aw882xx-aif", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, +}; +#endif + +#if !defined(CONFIG_SND_SMARTPA_AW882XX) static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { { .name = LPASS_BE_PRI_MI2S_RX, @@ -5494,6 +5553,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .ignore_suspend = 1, }, }; +#endif static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { /* Primary AUX PCM Backend DAI Links */ @@ -5780,12 +5840,18 @@ static struct snd_soc_dai_link msm_bengal_dai_links[ ARRAY_SIZE(msm_common_dai_links) + ARRAY_SIZE(msm_common_misc_fe_dai_links) + ARRAY_SIZE(msm_common_be_dai_links) + +#if !defined(CONFIG_SND_SMARTPA_AW882XX) ARRAY_SIZE(msm_mi2s_be_dai_links) + +#endif ARRAY_SIZE(msm_auxpcm_be_dai_links) + ARRAY_SIZE(msm_rx_tx_cdc_dma_be_dai_links) + ARRAY_SIZE(msm_va_cdc_dma_be_dai_links) + ARRAY_SIZE(msm_afe_rxtx_lb_be_dai_link) + ARRAY_SIZE(msm_wcn_btfm_be_dai_links) + +#ifdef CONFIG_SND_SMARTPA_AW882XX + ARRAY_SIZE(msm_awinic_fe_dai_links) + + ARRAY_SIZE(msm_awinic_be_dai_links) + +#endif ARRAY_SIZE(msm_tdm_be_dai_links)]; static int msm_populate_dai_link_component_of_node( @@ -6048,13 +6114,18 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) int len_2 = 0; int total_links = 0; int rc = 0; +#if !defined(CONFIG_SND_SMARTPA_AW882XX) u32 mi2s_audio_intf = 0; +#endif u32 auxpcm_audio_intf = 0; u32 rxtx_bolero_codec = 0; u32 va_bolero_codec = 0; u32 val = 0; u32 wcn_btfm_intf = 0; const struct of_device_id *match; +#ifdef CONFIG_SND_SMARTPA_AW882XX + u32 has_awinic_pa = 0; +#endif match = of_match_node(bengal_asoc_machine_of_match, dev->of_node); if (!match) { @@ -6113,6 +6184,7 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) } } +#if !defined(CONFIG_SND_SMARTPA_AW882XX) rc = of_property_read_u32(dev->of_node, "qcom,mi2s-audio-intf", &mi2s_audio_intf); if (rc) { @@ -6127,6 +6199,7 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) ARRAY_SIZE(msm_mi2s_be_dai_links); } } +#endif rc = of_property_read_u32(dev->of_node, "qcom,auxpcm-audio-intf", @@ -6178,6 +6251,30 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) ARRAY_SIZE(msm_wcn_btfm_be_dai_links); } } + +#ifdef CONFIG_SND_SMARTPA_AW882XX + rc = of_property_read_u32(dev->of_node, "aw,have-882xx", + &has_awinic_pa); + if (rc) { + dev_dbg(dev, "%s: No DT match awinic audio interface\n", + __func__); + } else { + dev_dbg(dev,"%s,aw,has-882xx=%d\n",__func__,has_awinic_pa); + if (has_awinic_pa) { + memcpy(msm_bengal_dai_links + total_links, + msm_awinic_fe_dai_links, + sizeof(msm_awinic_fe_dai_links)); + total_links += + ARRAY_SIZE(msm_awinic_fe_dai_links); + memcpy(msm_bengal_dai_links + total_links, + msm_awinic_be_dai_links, + sizeof(msm_awinic_be_dai_links)); + total_links += + ARRAY_SIZE(msm_awinic_be_dai_links); + } + } +#endif + dailink = msm_bengal_dai_links; } else if (!strcmp(match->data, "stub_codec")) { card = &snd_soc_card_stub_msm; diff --git a/asoc/codecs/Android.mk b/asoc/codecs/Android.mk index 08c555b53a13..95f6c1abfe92 100644 --- a/asoc/codecs/Android.mk +++ b/asoc/codecs/Android.mk @@ -167,6 +167,15 @@ LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) include $(DLKM_DIR)/AndroidKernelModule.mk endif ########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_snd_soc_aw882xx.ko +LOCAL_MODULE_KBUILD_NAME := aw882xx_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### + endif # DLKM check endif # supported target check diff --git a/asoc/codecs/Kbuild b/asoc/codecs/Kbuild index c0213bf00f9c..5051969904cb 100644 --- a/asoc/codecs/Kbuild +++ b/asoc/codecs/Kbuild @@ -251,6 +251,7 @@ ifeq ($(KERNEL_BUILD), 1) obj-y += rouleur/ obj-y += wsa883x/ obj-y += sdm660_cdc/ + obj-y += awinic/ endif # Module information used by KBuild framework obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd_core_dlkm.o @@ -284,5 +285,13 @@ mbhc_dlkm-y := $(MBHC_OBJS) obj-$(CONFIG_SND_SOC_MSM_HDMI_CODEC_RX) += hdmi_dlkm.o hdmi_dlkm-y := $(HDMICODEC_OBJS) +#Add awinic driver begain +ifdef CONFIG_SND_SMARTPA_AW882XX + SND_SOC_AW882XX_OBJS += awinic/aw882xx.o +endif +obj-$(CONFIG_SND_SMARTPA_AW882XX) += aw882xx_dlkm.o +aw882xx_dlkm-y := $(SND_SOC_AW882XX_OBJS) +#Add awinic driver end + # inject some build related information DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/asoc/codecs/awinic/aw882xx.c b/asoc/codecs/awinic/aw882xx.c new file mode 100644 index 000000000000..cd9d25ce6501 --- /dev/null +++ b/asoc/codecs/awinic/aw882xx.c @@ -0,0 +1,3391 @@ +/* + * aw882xx.c aw882xx codec module + * + * Version: v0.1.13 + * + * keep same with AW882XX_VERSION + * + * Copyright (c) 2019 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifdef CONFIG_SND_SMARTPA_AW882XX + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw882xx.h" +#include "aw882xx_reg.h" + +/****************************************************** + * + * Marco + * + ******************************************************/ +#define AW882XX_I2C_NAME "aw882xx_smartpa" + +#define AW882XX_VERSION "v0.1.13" + +#define AW882XX_RATES SNDRV_PCM_RATE_8000_48000 +#define AW882XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + + +#define AW_I2C_RETRIES 5 /* 5 times */ +#define AW_I2C_RETRY_DELAY 5 /* 5 ms */ +#define AW_READ_CHIPID_RETRIES 5 /* 5 times */ +#define AW_READ_CHIPID_RETRY_DELAY 5 /* 5 ms */ + + +#define CALI_BUF_MAX 100 +#define AWINIC_CALI_FILE "/mnt/vendor/persist/factory/audio/aw_cali.bin" + +#ifdef CONFIG_AW882XX_DSP +extern int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id, + uint32_t param_id, void *buf, int cmd_size, bool write); +extern int aw_send_afe_rx_module_enable(uint32_t rx_port_id, void *buf, int cmd_size); +extern int aw_send_afe_tx_module_enable(uint32_t tx_port_id, void *buf, int cmd_size); +extern int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable); +#else +static int aw_send_afe_cal_apr(uint32_t rx_port_id,uint32_t tx_port_id, + uint32_t param_id, void *buf, int cmd_size, bool write) { + return 0; +} + +static int aw_send_afe_rx_module_enable(uint32_t rx_port_id, void *buf, int cmd_size) +{ + return 0; +} + +static int aw_send_afe_tx_module_enable(uint32_t tx_port_id, void *buf, int cmd_size) +{ + return 0; +} + +static int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable) +{ + return 0; +} +#endif + +static int aw882xx_get_cali_re_from_nv(uint32_t *cali_re); +static int aw882xx_set_cali_re(struct aw882xx *aw882xx, uint32_t cali_re); +static int aw882xx_load_profile_params(struct aw882xx *aw882xx); +static int aw882xx_skt_set_dsp(int value); +static int aw882xx_send_profile_params_to_dsp(struct aw882xx *aw882xx, int profile_id, bool is_fade); +static void aw882xx_fade_in_out(struct aw882xx *aw882xx, bool is_fade_in); + +/*monitor voltage and temperature table*/ +static struct aw882xx_low_vol vol_down_table[] = { + {3500, IPEAK_2P50_A, GAIN_NEG_1P5_DB}, + {3700, IPEAK_2P75_A, GAIN_NEG_1P0_DB}, + {3900, IPEAK_3P00_A, GAIN_NEG_0P5_DB}, + }; +static struct aw882xx_low_vol vol_up_table[] = { + {4000, IPEAK_3P50_A, GAIN_NEG_0P0_DB}, + {3800, IPEAK_3P00_A, GAIN_NEG_0P5_DB}, + {3600, IPEAK_2P75_A, GAIN_NEG_1P0_DB}, + }; +static struct aw882xx_low_temp temp_down_table[] = { + {-5, IPEAK_2P50_A, GAIN_NEG_6P0_DB, VMAX_063_PERCENTAGE}, + { 0, IPEAK_2P75_A, GAIN_NEG_4P5_DB, VMAX_075_PERCENTAGE}, + { 5, IPEAK_3P00_A, GAIN_NEG_3P0_DB, VMAX_086_PERCENTAGE}, + }; +static struct aw882xx_low_temp temp_up_table[] = { + { 7, IPEAK_3P50_A, GAIN_NEG_0P0_DB, VMAX_100_PERCENTAGE}, + { 2, IPEAK_3P00_A, GAIN_NEG_3P0_DB, VMAX_086_PERCENTAGE}, + {-2, IPEAK_2P75_A, GAIN_NEG_4P5_DB, VMAX_075_PERCENTAGE}, + }; + +static int aw882xx_monitor_start(struct aw882xx_monitor *monitor); +static int aw882xx_monitor_stop(struct aw882xx_monitor *monitor); + +/****************************************************** + * + * Value + * + ******************************************************/ +static int aw882xx_spk_control; +static int aw882xx_rcv_control; + +#ifdef AW882XX_RUNIN_TEST +static atomic_t g_runin_test; +#endif +static atomic_t g_algo_rx_enable; +static atomic_t g_algo_tx_enable; +static atomic_t g_skt_disable; +static struct aw882xx *g_aw882xx = NULL; +static int8_t g_aw882xx_cali_flag = 0; +static int8_t g_aw882xx_profile_flag = 0; + +#define AW882XX_CFG_NAME_MAX 64 +static char aw882xx_cfg_name[][AW882XX_CFG_NAME_MAX] = { + {"aw882xx_spk_reg.bin"}, + {"aw882xx_rcv_reg.bin"}, +}; + +static unsigned int aw882xx_mode_cfg_shift[AW882XX_MODE_SHIFT_MAX] = { + AW882XX_MODE_SPK_SHIFT, + AW882XX_MODE_RCV_SHIFT, +}; + +/****************************************************** + * + * aw882xx i2c write/read + * + ******************************************************/ +static int aw882xx_i2c_writes(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned char *buf, unsigned int len) +{ + int ret = -1; + unsigned char *data; + + data = kmalloc(len+1, GFP_KERNEL); + if (data == NULL) { + pr_err("%s: can not allocate memory\n", __func__); + return -ENOMEM; + } + + data[0] = reg_addr; + memcpy(&data[1], buf, len); + + ret = i2c_master_send(aw882xx->i2c, data, len+1); + if (ret < 0) + pr_err("%s: i2c master send error\n", __func__); + + kfree(data); + + return ret; +} + +static int aw882xx_i2c_reads(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned char *data_buf, unsigned int data_len) +{ + int ret; + struct i2c_msg msg[] = { + [0] = { + .addr = aw882xx->i2c->addr, + .flags = 0, + .len = sizeof(uint8_t), + .buf = ®_addr, + }, + [1] = { + .addr = aw882xx->i2c->addr, + .flags = I2C_M_RD, + .len = data_len, + .buf = data_buf, + }, + }; + + ret = i2c_transfer(aw882xx->i2c->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) { + pr_err("%s: i2c master send error, ret=%d\n", + __func__, ret); + return ret; + } else if (ret != AW882XX_I2C_READ_MSG_NUM) { + pr_err("%s: couldn't read registers, return %d bytes\n", + __func__, ret); + return -ENXIO; + } + + return 0; +} + +static int aw882xx_i2c_write(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + unsigned char buf[2]; + + buf[0] = (reg_data&0xff00)>>8; + buf[1] = (reg_data&0x00ff)>>0; + while (cnt < AW_I2C_RETRIES) { + ret = aw882xx_i2c_writes(aw882xx, reg_addr, buf, 2); + if (ret < 0) + pr_err("%s: i2c_write cnt=%d error=%d\n", + __func__, cnt, ret); + else + break; + cnt++; + } + + return ret; +} + +static int aw882xx_i2c_read(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int *reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + unsigned char buf[2]; + + while (cnt < AW_I2C_RETRIES) { + ret = aw882xx_i2c_reads(aw882xx, reg_addr, buf, 2); + if (ret < 0) { + pr_err("%s: i2c_read cnt=%d error=%d\n", + __func__, cnt, ret); + } else { + *reg_data = (buf[0]<<8) | (buf[1]<<0); + break; + } + cnt++; + } + + return ret; +} + +static int aw882xx_i2c_write_bits(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int mask, unsigned int reg_data) +{ + int ret = -1; + unsigned int reg_val = 0; + + ret = aw882xx_i2c_read(aw882xx, reg_addr, ®_val); + if (ret < 0) { + pr_err("%s: i2c read error, ret=%d\n", __func__, ret); + return ret; + } + + reg_val &= mask; + reg_val |= reg_data; + ret = aw882xx_i2c_write(aw882xx, reg_addr, reg_val); + if (ret < 0) { + pr_err("%s: i2c read error, ret=%d\n", __func__, ret); + return ret; + } + + return 0; +} + +/****************************************************** + * + * aw882xx control + * + ******************************************************/ +static void aw882xx_run_mute(struct aw882xx *aw882xx, bool mute) +{ + pr_debug("%s: enter\n", __func__); + + if (mute) { + if (aw882xx->need_fade) + aw882xx_fade_in_out(aw882xx, false); + aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL2_REG, + AW882XX_HMUTE_MASK, + AW882XX_HMUTE_ENABLE_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL2_REG, + AW882XX_HMUTE_MASK, + AW882XX_HMUTE_DISABLE_VALUE); + if (aw882xx->need_fade) + aw882xx_fade_in_out(aw882xx, true); + } +} + +#if 0 +static bool aw882xx_get_power_status(struct aw882xx *aw882xx) +{ + unsigned int reg_value = 0; + int ret = 0; + pr_debug("%s: enter\n", __func__); + + ret = aw882xx_i2c_read(aw882xx, AW882XX_SYSCTRL_REG, ®_value); + if (ret < 0) { + pr_err("%s: read reg %d failed \n", __func__, AW882XX_SYSCTRL_REG); + return false; + } + /*bit 0: 1 power off, 0 power on*/ + if (reg_value & 0x01) { + return false; + } else { + return true; + } +} +#endif + +static void aw882xx_run_pwd(struct aw882xx *aw882xx, bool pwd) +{ + pr_debug("%s: enter\n", __func__); + + if (pwd) { + aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL_REG, + AW882XX_PWDN_MASK, + AW882XX_PWDN_POWER_DOWN_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL_REG, + AW882XX_PWDN_MASK, + AW882XX_PWDN_NORMAL_WORKING_VALUE); + } +} + +static int aw882xx_sysst_check(struct aw882xx *aw882xx) +{ + int ret = -1; + unsigned char i; + unsigned int reg_val = 0; + + for (i = 0; i < AW882XX_SYSST_CHECK_MAX; i++) { + aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_val); + if (((reg_val & (~AW882XX_SYSST_CHECK_MASK)) & AW882XX_SYSST_CHECK) == + AW882XX_SYSST_CHECK) { + ret = 0; + break; + } else { + pr_debug("%s: check fail, cnt=%d, reg_val=0x%04x\n", + __func__, i, reg_val); + msleep(2); + } + } + if (ret < 0) + pr_info("%s: check fail\n", __func__); + + return ret; +} + +/* +static int aw882xx_get_sysint(struct aw882xx *aw882xx, unsigned int *sysint) +{ + int ret = -1; + unsigned int reg_val = 0; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_SYSINT_REG, ®_val); + if (ret < 0) + pr_info("%s: read sysint fail, ret=%d\n", __func__, ret); + else + *sysint = reg_val; + + return ret; +} + +static int aw882xx_get_iis_status(struct aw882xx *aw882xx) +{ + int ret = -1; + unsigned int reg_val = 0; + + pr_debug("%s: enter\n", __func__); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_val); + if (reg_val & AW882XX_PLLS_LOCKED_VALUE) + ret = 0; + + return ret; +} +*/ + +static int aw882xx_get_icalk(struct aw882xx *aw882xx, int16_t *icalk) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t reg_icalk = 0; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_EFRM1_REG, ®_val); + reg_icalk = (uint16_t)reg_val & AW882XX_EF_ISN_GESLP_MASK; + + if (reg_icalk & AW882XX_EF_ISN_GESLP_SIGN_MASK) + reg_icalk = reg_icalk | AW882XX_EF_ISN_GESLP_NEG; + + *icalk = (int16_t)reg_icalk; + + return ret; +} + +static int aw882xx_get_vcalk(struct aw882xx *aw882xx, int16_t *vcalk) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t reg_vcalk = 0; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_EFRH_REG, ®_val); + + reg_vcalk = (uint16_t)reg_val & AW882XX_EF_VSN_GESLP_MASK; + + if (reg_vcalk & AW882XX_EF_VSN_GESLP_SIGN_MASK) + reg_vcalk = reg_vcalk | AW882XX_EF_VSN_GESLP_NEG; + + *vcalk = (int16_t)reg_vcalk; + + return ret; +} + +static int aw882xx_set_vcalb(struct aw882xx *aw882xx) +{ + int ret = -1; + unsigned int reg_val; + int vcalb; + int icalk; + int vcalk; + int16_t icalk_val = 0; + int16_t vcalk_val = 0; + + ret = aw882xx_get_icalk(aw882xx, &icalk_val); + ret = aw882xx_get_vcalk(aw882xx, &vcalk_val); + + icalk = AW882XX_CABL_BASE_VALUE + AW882XX_ICABLK_FACTOR * icalk_val; + vcalk = AW882XX_CABL_BASE_VALUE + AW882XX_VCABLK_FACTOR * vcalk_val; + + vcalb = AW882XX_VCAL_FACTOR * icalk / vcalk; + + reg_val = (unsigned int)vcalb; + pr_debug("%s: icalk=%d, vcalk=%d, vcalb=%d, reg_val=%d\n", + __func__, icalk, vcalk, vcalb, reg_val); + + ret = aw882xx_i2c_write(aw882xx, AW882XX_VTMCTRL3_REG, reg_val); + + return ret; +} + +static void aw882xx_send_cali_re_to_dsp(struct aw882xx *aw882xx) +{ + int ret = 0; + if((aw882xx != NULL) && (aw882xx->default_re == 0)) { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_RE_L, + &aw882xx->default_re, sizeof(int32_t), false); + pr_info("aw882xx: default_re:%d\n", aw882xx->default_re); + } + if ((aw882xx != NULL) && (aw882xx->cali_re != ERRO_CALI_VALUE)) { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_RE_L, &aw882xx->cali_re, sizeof(int32_t), true); + } + if (ret) + pr_err("%s : set cali re to dsp failed 0x%x\n", + __func__ , AFE_PARAM_ID_AWDSP_RX_RE_L); +} + +static void aw882xx_start(struct aw882xx *aw882xx) +{ + int ret = -1; + uint32_t cali_re; + pr_debug("%s: enter\n", __func__); + + ret = aw882xx_get_cali_re_from_nv(&cali_re); + if (ret < 0) { + cali_re = ERRO_CALI_VALUE; + pr_err("%s: use default vaule %d", __func__ , ERRO_CALI_VALUE); + } + ret = aw882xx_set_cali_re(aw882xx, cali_re); + if (ret < 0) + pr_err("%s: set cali re failed: %d\n", __func__, ret); + + mutex_lock(&aw882xx->lock); + pr_info("%s: monitor is_enable %d,spk_rcv_mode %d\n", + __func__, aw882xx->monitor.is_enable, aw882xx->spk_rcv_mode); + if (aw882xx->monitor.is_enable && + (aw882xx->spk_rcv_mode == AW882XX_SPEAKER_MODE)) { + aw882xx->monitor.first_entry = AW_FIRST_ENTRY; + aw882xx->monitor.pre_vol = 0; + aw882xx->monitor.vol_count = 0; + schedule_work(&aw882xx->monitor.work); + } + if (aw882xx->afe_profile) { + ret = aw882xx_send_profile_params_to_dsp(aw882xx, aw882xx->profile.cur_profile, false); + if (ret) { + pr_err("%s: set profile %d failed \n", __func__,aw882xx->profile.cur_profile); + aw882xx->profile.cur_profile = 0; + } + } + aw882xx_send_cali_re_to_dsp(aw882xx); + + aw882xx_run_pwd(aw882xx, false); + ret = aw882xx_sysst_check(aw882xx); + if (ret < 0) { + aw882xx_run_mute(aw882xx, true); + aw882xx_run_pwd(aw882xx, true); + aw882xx->init = AW882XX_INIT_NG; + aw882xx->power_flag = AW882XX_POWER_DOWN; + } else { + aw882xx_run_mute(aw882xx, false); + aw882xx->init = AW882XX_INIT_OK; + aw882xx->power_flag = AW882XX_POWER_UP; + } +#ifdef AW882XX_RUNIN_TEST + schedule_delayed_work(&aw882xx->adsp_status, msecs_to_jiffies(50)); +#endif + mutex_unlock(&aw882xx->lock); +} + +static void aw882xx_stop(struct aw882xx *aw882xx) +{ + pr_debug("%s: enter\n", __func__); + + mutex_lock(&aw882xx->lock); + aw882xx->power_flag = AW882XX_POWER_DOWN; + aw882xx_run_mute(aw882xx, true); + aw882xx_run_pwd(aw882xx, true); + if (aw882xx->afe_profile) { + aw882xx->profile.cur_profile = 0; + } + if (aw882xx->monitor.is_enable) + aw882xx_monitor_stop(&aw882xx->monitor); + mutex_unlock(&aw882xx->lock); +} + +/****************************************************** + * + * aw882xx config + * + ******************************************************/ +static int aw882xx_get_cali_re_from_nv(uint32_t *cali_re) +{ + /*custom add, if success return value is 0 , else -1*/ + struct file *fp; + char buf[CALI_BUF_MAX]; + uint32_t read_re; + loff_t pos = 0; + mm_segment_t fs; + + memset(buf, 0, CALI_BUF_MAX); + /*open cali file*/ + fp = filp_open(AWINIC_CALI_FILE, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("%s: open %s failed!", __func__, AWINIC_CALI_FILE); + return -EINVAL; + } + /*set fs kernel*/ + fs = get_fs(); + set_fs(get_ds()); + + /*read file*/ + vfs_read(fp, buf, CALI_BUF_MAX - 1, &pos); + + /*get cali re value*/ + if (sscanf(buf, "%d", &read_re) != 1) { + pr_err("%s: file read error", __func__); + set_fs(fs); + filp_close(fp, NULL); + return -EINVAL; + } + set_fs(fs); + + /*close file*/ + filp_close(fp, NULL); + + *cali_re = read_re; + pr_info("%s: %d", __func__, read_re); + return 0; +} + +static int aw882xx_set_cali_re(struct aw882xx *aw882xx, uint32_t cali_re) +{ + if (aw882xx == NULL) + return -EINVAL; + aw882xx->cali_re = cali_re; + return 0; +} + +static int aw882xx_reg_container_update(struct aw882xx *aw882xx, + struct aw882xx_container *aw882xx_cont) +{ + int i = 0; + int reg_addr = 0; + int reg_val = 0; + int ret = -1; + + pr_debug("%s: enter\n", __func__); + + for (i = 0; i < aw882xx_cont->len; i += 4) { + reg_addr = (aw882xx_cont->data[i+1]<<8) + + aw882xx_cont->data[i+0]; + reg_val = (aw882xx_cont->data[i+3]<<8) + + aw882xx_cont->data[i+2]; + pr_debug("%s: reg=0x%04x, val = 0x%04x\n", + __func__, reg_addr, reg_val); + ret = aw882xx_i2c_write(aw882xx, + (unsigned char)reg_addr, + (unsigned int)reg_val); + if (ret < 0) + break; + } + + pr_debug("%s: exit\n", __func__); + + return ret; +} + +static void aw882xx_reg_loaded(const struct firmware *cont, void *context) +{ + struct aw882xx *aw882xx = context; + struct aw882xx_container *aw882xx_cfg; + int ret = -1; + + if (!cont) { + pr_err("%s: failed to read %s\n", __func__, + aw882xx_cfg_name[aw882xx->cfg_num]); + release_firmware(cont); + return; + } + + pr_info("%s: loaded %s - size: %zu\n", __func__, + aw882xx_cfg_name[aw882xx->cfg_num], cont ? cont->size : 0); + + aw882xx_cfg = kzalloc(cont->size+sizeof(int), GFP_KERNEL); + if (!aw882xx_cfg) { + release_firmware(cont); + pr_err("%s: error allocating memory\n", __func__); + return; + } + aw882xx_cfg->len = cont->size; + memcpy(aw882xx_cfg->data, cont->data, cont->size); + release_firmware(cont); + + mutex_lock(&aw882xx->lock); + ret = aw882xx_reg_container_update(aw882xx, aw882xx_cfg); + if (ret < 0) { + pr_err("%s: reg update fail\n", __func__); + } else { + pr_err("%s: reg update sucess\n", __func__); + aw882xx_run_mute(aw882xx, true); + aw882xx_set_vcalb(aw882xx); + } + mutex_unlock(&aw882xx->lock); + kfree(aw882xx_cfg); + if (aw882xx->afe_profile) { + aw882xx_load_profile_params(aw882xx); + aw882xx->need_fade = 0; + } + aw882xx_start(aw882xx); + + if (aw882xx->afe_profile) { + aw882xx->need_fade = 1; + } +} + +static int aw882xx_load_reg(struct aw882xx *aw882xx) +{ + pr_info("%s: enter\n", __func__); + + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + aw882xx_cfg_name[aw882xx->cfg_num], + aw882xx->dev, GFP_KERNEL, + aw882xx, aw882xx_reg_loaded); +} + +static void aw882xx_get_cfg_shift(struct aw882xx *aw882xx) +{ + aw882xx->cfg_num = aw882xx_mode_cfg_shift[aw882xx->spk_rcv_mode]; + pr_debug("%s: cfg_num=%d\n", __func__, aw882xx->cfg_num); +} + +static void aw882xx_cold_start(struct aw882xx *aw882xx) +{ + int ret = -1; + + pr_info("%s: enter\n", __func__); + + aw882xx_get_cfg_shift(aw882xx); + + ret = aw882xx_load_reg(aw882xx); + if (ret < 0) + pr_err("%s: cfg loading requested failed: %d\n", __func__, ret); + +} + +static void aw882xx_smartpa_cfg(struct aw882xx *aw882xx, bool flag) +{ + pr_info("%s: flag = %d\n", __func__, flag); + + if (flag == true) { + if ((aw882xx->init == AW882XX_INIT_ST) || + (aw882xx->init == AW882XX_INIT_NG)) { + pr_info("%s: init = %d\n", __func__, aw882xx->init); + aw882xx_cold_start(aw882xx); + } else { + aw882xx_start(aw882xx); + } + } else { + aw882xx_stop(aw882xx); + } +} + +/****************************************************** + * + * kcontrol + * + ******************************************************/ +static const char *const switch_status[] = { "Off", "On" }; +static const char *const awinic_algo[] = { "Disable", "Enable" }; +static const char *const awinic_profile[AW_PROFILE_MAX] = { +"Music", "Ringtone", "Notification", "Voice"}; +static const char *const awinic_profile_param_file_name[] = { + "aw882xx_afe_params.bin", + }; + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 50, 0); + +struct soc_mixer_control aw882xx_mixer = { + .reg = AW882XX_HAGCCFG4_REG, + .shift = AW882XX_VOL_START_BIT, + .max = AW882XX_VOLUME_MAX, + .min = AW882XX_VOLUME_MIN, +}; + +static int aw882xx_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + /* set kcontrol info */ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mc->max - mc->min; + return 0; +} + +static int aw882xx_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + unsigned int reg_val = 0; + unsigned int value = 0; + struct soc_mixer_control *mc = + (struct soc_mixer_control *) kcontrol->private_value; + + aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_val); + ucontrol->value.integer.value[0] = (value >> mc->shift) & + (AW882XX_VOL_MASK); + return 0; +} + +static int aw882xx_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *) kcontrol->private_value; + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + unsigned int value = 0; + unsigned int reg_value = 0; + + /* value is right */ + value = ucontrol->value.integer.value[0]; + if (value > (mc->max-mc->min) || value < 0) { + pr_err("%s: value over range\n", __func__); + return -ERANGE; + } + + /* smartpa have clk */ + aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_value); + if (!(reg_value & AW882XX_PLLS_LOCKED_VALUE)) { + pr_err("%s: NO I2S CLK ,cat not write reg\n", __func__); + return 0; + } + + /* cal real value */ + value = (value << mc->shift) & AW882XX_VOL_MASK; + aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_value); + value = value | (reg_value & 0x00ff); + + /* write value */ + aw882xx->cur_gain = value >> AW882XX_BIT_HAGCCFG4_GAIN_SHIFT; + aw882xx_i2c_write(aw882xx, AW882XX_HAGCCFG4_REG, value); + + return 0; +} + +static struct snd_kcontrol_new aw882xx_volume = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "aw882xx_rx_volume", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .tlv.p = (digital_gain), + .info = aw882xx_volume_info, + .get = aw882xx_volume_get, + .put = aw882xx_volume_put, + .private_value = (unsigned long)&aw882xx_mixer, +}; + +static int aw882xx_spk_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw882xx_spk_control=%d\n", + __func__, aw882xx_spk_control); + ucontrol->value.integer.value[0] = aw882xx_spk_control; + return 0; +} + +static int aw882xx_spk_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n", + __func__, ucontrol->value.integer.value[0]); + + if (ucontrol->value.integer.value[0] == aw882xx_spk_control) + return 1; + + aw882xx_spk_control = ucontrol->value.integer.value[0]; + + if (AW882XX_SPEAKER_MODE != aw882xx->spk_rcv_mode) { + aw882xx->spk_rcv_mode = AW882XX_SPEAKER_MODE; + aw882xx->init = AW882XX_INIT_ST; + } + + return 0; +} + +static int aw882xx_rcv_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw882xx_rcv_control=%d\n", __func__, aw882xx_rcv_control); + ucontrol->value.integer.value[0] = aw882xx_rcv_control; + return 0; +} + +static int aw882xx_rcv_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n", + __func__, ucontrol->value.integer.value[0]); + + if (ucontrol->value.integer.value[0] == aw882xx_rcv_control) + return 1; + + aw882xx_rcv_control = ucontrol->value.integer.value[0]; + + if (AW882XX_RECEIVER_MODE != aw882xx->spk_rcv_mode) { + aw882xx->spk_rcv_mode = AW882XX_RECEIVER_MODE; + aw882xx->init = AW882XX_INIT_ST; + } + + return 0; +} + +static int aw882xx_algo_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw882xx_algo enable=%d\n", + __func__, atomic_read(&g_algo_rx_enable)); + + ucontrol->value.integer.value[0] = atomic_read(&g_algo_rx_enable); + + return 0; +} + +static int aw882xx_algo_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n", + __func__, ucontrol->value.integer.value[0]); + + ctrl_value = ucontrol->value.integer.value[0]; + ret = aw_send_afe_rx_module_enable(g_aw882xx->afe_rx_portid, &ctrl_value, sizeof(uint32_t)); + if (ret) + pr_err("%s: set algo %d failed, ret=%d\n", + __func__, ctrl_value, ret); + atomic_set(&g_algo_rx_enable, ctrl_value); + return 0; +} + +static int aw882xx_tx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw882xx_tx_control=%d\n", __func__, + atomic_read(&g_algo_tx_enable)); + + ucontrol->value.integer.value[0] = atomic_read(&g_algo_tx_enable); + + return 0; +} + +static int aw882xx_tx_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n", + __func__, ucontrol->value.integer.value[0]); + + ctrl_value = ucontrol->value.integer.value[0]; + ret = aw_send_afe_tx_module_enable(g_aw882xx->afe_tx_portid, &ctrl_value, sizeof(uint32_t)); + if (ret) + pr_err("%s: set tx enable %d, ret=%d\n", __func__, ctrl_value, ret); + atomic_set(&g_algo_tx_enable, ctrl_value); + return 0; +} + +static int aw882xx_profile_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + pr_debug("%s: profile : %s \n", __func__, + awinic_profile[aw882xx->profile.cur_profile]); + if (aw882xx->afe_profile) { + ucontrol->value.integer.value[0] = aw882xx->profile.cur_profile; + } + return 0; +} +static int aw882xx_profile_firmware_parse(struct aw882xx *aw882xx, const struct firmware *cont) +{ + aw_afe_params_hdr_t *hdr; + char *fw_data = NULL; + uint8_t sum = 0; + uint32_t param_size = 0; + char *param_data = NULL; + int i; + pr_info("%s: loaded %s - size: %zu\n", __func__, + awinic_profile_param_file_name[0], cont ? cont->size : 0); + + if (cont->size == 0) { + goto load_failed; + } + + fw_data = kzalloc(cont->size, GFP_KERNEL); + if (fw_data == NULL) { + pr_err("%s: alloc memory failed \n", __func__); + goto load_failed; + } + memcpy(fw_data, cont->data, cont->size); + + /*output bin info*/ + hdr = (aw_afe_params_hdr_t *)fw_data; + pr_info("%s: fw v_%d.%d.%d \n", __func__, + hdr->fw[0], hdr->fw[1], hdr->fw[2]); + pr_info("%s: param v_%d.%d.%d \n", __func__, + hdr->cfg[0], hdr->cfg[1], hdr->cfg[2]); + pr_info("%s: project name : %s \n", __func__, hdr->project); + pr_info("%s: param len: %d", __func__, hdr->params_len); + + param_data = fw_data + sizeof(aw_afe_params_hdr_t); + param_size = cont->size - sizeof(aw_afe_params_hdr_t); + + for (i = 0; i < param_size; i++) { + sum += param_data[i]; + } + if (sum != hdr->check_sum) { + pr_err("%s:data check sum failed : %d -- %d \n", __func__, + sum, hdr->check_sum); + kfree(fw_data); + goto load_failed; + } + + if (hdr->profile_num < AW_PROFILE_MAX) { + pr_err("%s: profile num is not match %d -- %d ", __func__, + hdr->profile_num, AW_PROFILE_MAX); + kfree(fw_data); + goto load_failed; + } + + if (hdr->params_len != (param_size / hdr->profile_num)) { + pr_err("%s: profile params len is not match %d ", __func__, + hdr->params_len); + kfree(fw_data); + goto load_failed; + } + + mutex_lock(&aw882xx->profile.lock); + param_data = fw_data + sizeof(aw_afe_params_hdr_t); + for (i = 0; i < AW_PROFILE_MAX; i++) { + aw882xx->profile.data[i] = kzalloc(hdr->params_len, GFP_KERNEL); + if (aw882xx->profile.data[i] == NULL) { + pr_err("%s: alloc failed !", __func__); + kfree(fw_data); + mutex_unlock(&aw882xx->profile.lock); + goto load_failed; + } + memcpy(aw882xx->profile.data[i], param_data , hdr->params_len); + param_data += hdr->params_len; + } + aw882xx->profile.len = hdr->params_len; + aw882xx->profile.status = AW882XX_INIT_OK; + mutex_unlock(&aw882xx->profile.lock); + kfree(fw_data); + return 0; + +load_failed: + mutex_lock(&aw882xx->profile.lock); + for (i = 0; i < AW_PROFILE_MAX; i++) { + if (aw882xx->profile.data[i]) { + kfree(aw882xx->profile.data[i]); + aw882xx->profile.data[i] = NULL; + } + } + aw882xx->profile.status = AW882XX_INIT_NG; + aw882xx->profile.len = 0; + mutex_unlock(&aw882xx->profile.lock); + pr_err("%s: load params bin failed !", __func__); + return 0; +} + +static int aw882xx_load_profile_params(struct aw882xx *aw882xx) +{ + const struct firmware *fw = NULL; + int ret; + pr_info("%s: enter\n", __func__); + + ret = request_firmware(&fw, + awinic_profile_param_file_name[0], + aw882xx->dev); + if (ret) { + pr_err("%s: load profile params failed !", __func__); + return -EINVAL; + } + + ret = aw882xx_profile_firmware_parse(aw882xx, fw); + return ret; +} + +static void aw882xx_volume_set(struct aw882xx *aw882xx, unsigned int value) +{ + unsigned int reg_value = 0; + unsigned int real_value = ((value / VOLUME_STEP_DB) << 4) + (value % VOLUME_STEP_DB) * 2; + + if(real_value > aw882xx->cur_gain) { + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_value); + real_value = (real_value << 8) | (reg_value & 0x00ff); + /* write value */ + aw882xx_i2c_write(aw882xx, AW882XX_HAGCCFG4_REG, real_value); + } +} + +static void aw882xx_fade_in_out(struct aw882xx *aw882xx, bool is_fade_in) +{ + int i = 0; + + //volume up + if (is_fade_in) { + i = VOLUME_MIN_NEG_90_DB; + do { + aw882xx_volume_set(aw882xx, i); + i -= FADE_STEP_DB; + usleep_range(1400,1600); + } while (i >= 0); + } else { // volume down + do { + aw882xx_volume_set(aw882xx, i); + i += FADE_STEP_DB; + usleep_range(1400,1600); + } while (i <= VOLUME_MIN_NEG_90_DB); + } +} + +static int aw882xx_send_profile_params_to_dsp(struct aw882xx *aw882xx, int profile_id, bool is_fade) +{ + int ret ; + int32_t params_id = AFE_PARAM_ID_AWDSP_RX_PARAMS; + + mutex_lock(&aw882xx->profile.lock); + if (aw882xx->profile.data[profile_id] == NULL) { + pr_err("%s: profile %s not data is NULL \n", __func__, + awinic_profile[profile_id]); + mutex_unlock(&aw882xx->profile.lock); + return -EINVAL; + } + if (is_fade) { + g_aw882xx_profile_flag = true; + //fade out + //aw882xx_fade_in_out(aw882xx, false); + aw882xx_run_mute(aw882xx, true); + } + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + params_id, + aw882xx->profile.data[profile_id], + aw882xx->profile.len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, params_id); + aw882xx_run_mute(aw882xx, false); + //aw882xx_fade_in_out(aw882xx, true); + g_aw882xx_profile_flag = false; + mutex_unlock(&aw882xx->profile.lock); + return ret; + } + if (is_fade) { + //fade out + aw882xx_run_mute(aw882xx, false); + //aw882xx_fade_in_out(aw882xx, true); + g_aw882xx_profile_flag = false; + } + mutex_unlock(&aw882xx->profile.lock); + + pr_debug("%s: set params done", __func__); + return 0; +} + +static int aw882xx_profile_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + unsigned int ctl_value = 0; + int ret; + ctl_value = ucontrol->value.integer.value[0]; + + if (aw882xx->afe_profile) { + if (ctl_value >= AW_PROFILE_MAX) { + pr_err("%s: unsuport profile %d", __func__, ctl_value); + return -EINVAL; + } + pr_debug("%s: profile switch to %s \n", + __func__, awinic_profile[ctl_value]); + + mutex_lock(&aw882xx->lock); + if (aw882xx->profile.cur_profile != ctl_value) { + /*PA have power on ,set params direct*/ + if (aw882xx->power_flag == AW882XX_POWER_UP) { + ret = aw882xx_send_profile_params_to_dsp(aw882xx, ctl_value, true); + if (ret < 0) { + mutex_unlock(&aw882xx->lock); + return -EINVAL; + } + aw882xx_send_cali_re_to_dsp(aw882xx); + aw882xx->profile.cur_profile = ctl_value; + } else { + /*PA power only set params status*/ + aw882xx->profile.cur_profile = ctl_value; + } + } + mutex_unlock(&aw882xx->lock); + } + return 0; +} + +static int aw882xx_skt_disable_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw882xx_skt_disable %d\n", + __func__, atomic_read(&g_skt_disable)); + + ucontrol->value.integer.value[0] = atomic_read(&g_skt_disable); + + return 0; +} + +static int aw882xx_skt_set_dsp(int value) +{ + int ret; + int port_id = g_aw882xx->afe_rx_portid; + int module_id = AW_MODULE_ID_COPP; + int param_id = AW_MODULE_PARAMS_ID_COPP_ENABLE; + + ret = aw_adm_param_enable(port_id, module_id, param_id, value); + if (ret) { + pr_err("%s: set skt %d failed \n", __func__, value); + return -EINVAL; + } + + pr_info("%s: set skt %s", __func__, value == 1 ? "enable" : "disable"); + return 0; +} + +static int aw882xx_skt_disable_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int value = ucontrol->value.integer.value[0]; + + pr_debug("%s: aw882xx_skt_disable %d \n", __func__, value); + aw882xx_skt_set_dsp(!value); + atomic_set(&g_skt_disable, value); + + return 0; +} + +static int aw882xx_monitor_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + + pr_debug("%s: aw882xx_monitor_get %d\n", + __func__, aw882xx->monitor.is_enable); + + ucontrol->value.integer.value[0] = aw882xx->monitor.is_enable; + + return 0; +} + +static int aw882xx_monitor_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + int enable = ucontrol->value.integer.value[0]; + + pr_debug("%s: aw882xx_monitor_set %d \n", __func__, enable); + + if (enable == aw882xx->monitor.is_enable) + return 1; + + aw882xx->monitor.is_enable = enable; + if (enable) + schedule_work(&aw882xx->monitor.work); + + return 0; +} + +#ifdef AW882XX_RUNIN_TEST +static int aw882xx_runin_test_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw882xx_runin test %d\n", + __func__, atomic_read(&g_runin_test)); + + ucontrol->value.integer.value[0] = atomic_read(&g_runin_test); + + return 0; +} + +static int aw882xx_runin_test_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int value = ucontrol->value.integer.value[0]; + + pr_debug("%s: aw882xx_runin_test %d \n", __func__, value); + + if (value > 1 ) { + value = 0; + } + + atomic_set(&g_runin_test, value); + + return 0; +} + +static void aw882xx_set_adsp_module_status(struct work_struct *work) +{ + int ret = 0; + int32_t set_value; + + /*no test reg set default value*/ + if (!atomic_read(&g_runin_test)) { + return; + } + set_value = 0; + /*set afe rx module*/ + ret = aw_send_afe_rx_module_enable(g_aw882xx->afe_rx_portid, &set_value, sizeof(uint32_t)); + if (ret) { + pr_info("%s: disable afe rx module %d falied , ret=%d\n", + __func__, set_value, ret); + } + + /*set skt module*/ + ret = aw882xx_skt_set_dsp(false); + if (ret) { + pr_err("%s: disable skt failed !\n", __func__); + } + + pr_info("%s: disable skt and afe module \n", __func__); +} +#endif + +static const struct soc_enum aw882xx_snd_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(switch_status), switch_status), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(awinic_algo), awinic_algo), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(awinic_profile), awinic_profile), +}; + +static struct snd_kcontrol_new aw882xx_controls[] = { + SOC_ENUM_EXT("aw882xx_speaker_switch", aw882xx_snd_enum[0], + aw882xx_spk_get, aw882xx_spk_set), + SOC_ENUM_EXT("aw882xx_receiver_switch", aw882xx_snd_enum[0], + aw882xx_rcv_get, aw882xx_rcv_set), + SOC_ENUM_EXT("aw882xx_rx_switch", aw882xx_snd_enum[1], + aw882xx_algo_get, aw882xx_algo_set), + SOC_ENUM_EXT("aw882xx_tx_switch", aw882xx_snd_enum[1], + aw882xx_tx_get, aw882xx_tx_set), + SOC_ENUM_EXT("aw882xx_skt_disable", aw882xx_snd_enum[0], + aw882xx_skt_disable_get, aw882xx_skt_disable_set), + SOC_ENUM_EXT("aw882xx_monitor_switch", aw882xx_snd_enum[1], + aw882xx_monitor_get, aw882xx_monitor_set), +#ifdef AW882XX_RUNIN_TEST + SOC_ENUM_EXT("aw882xx_runin_test", aw882xx_snd_enum[0], + aw882xx_runin_test_get, aw882xx_runin_test_set), +#endif +}; + +static struct snd_kcontrol_new aw882xx_profile_controls[] = { + SOC_ENUM_EXT("aw882xx_profile_switch", aw882xx_snd_enum[2], + aw882xx_profile_get, aw882xx_profile_set), +}; + +static void aw882xx_add_codec_controls(struct aw882xx *aw882xx) +{ + pr_info("%s: enter\n", __func__); + + snd_soc_add_component_controls(aw882xx->component, aw882xx_controls, + ARRAY_SIZE(aw882xx_controls)); + if(aw882xx->afe_profile) { + snd_soc_add_component_controls(aw882xx->component, aw882xx_profile_controls, + ARRAY_SIZE(aw882xx_profile_controls)); + } + snd_soc_add_component_controls(aw882xx->component, &aw882xx_volume, 1); +} + +/****************************************************** + * + * Digital Audio Interface + * + ******************************************************/ + + +static int aw882xx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + pr_info("%s: playback enter\n", __func__); + /*aw882xx_run_pwd(aw882xx, false);*/ + aw882xx->power_flag = AW882XX_POWER_ING; + } else { + pr_info("%s: capture enter\n", __func__); + } + + return 0; +} + +static int aw882xx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + /*struct aw882xx *aw882xx = snd_soc_component_get_drvdata(dai->codec);*/ + struct snd_soc_component *component = dai->component; + + pr_info("%s: fmt=0x%x\n", __func__, fmt); + + /* Supported mode: regular I2S, slave, or PDM */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != + SND_SOC_DAIFMT_CBS_CFS) { +// dev_err(component->dev, "%s: invalid codec master mode\n", +// __func__); + return -EINVAL; + } + break; + default: + dev_err(component->dev, "%s: unsupported DAI format %d\n", + __func__, fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + return 0; +} + +static int aw882xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec_dai->component); + + pr_info("%s: freq=%d\n", __func__, freq); + + aw882xx->sysclk = freq; + return 0; +} + +static int aw882xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + unsigned int rate = 0; + int reg_value = 0; + uint32_t cco_mux_value; + int width = 0; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + pr_debug("%s: requested rate: %d, sample size: %d\n", + __func__, rate, snd_pcm_format_width(params_format(params))); + return 0; + } + /* get rate param */ + aw882xx->rate = rate = params_rate(params); + pr_debug("%s: requested rate: %d, sample size: %d\n", + __func__, rate, snd_pcm_format_width(params_format(params))); + + /* match rate */ + switch (rate) { + case 8000: + reg_value = AW882XX_I2SSR_8KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_8_16_32KHZ_VALUE; + break; + case 16000: + reg_value = AW882XX_I2SSR_16KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_8_16_32KHZ_VALUE; + break; + case 32000: + reg_value = AW882XX_I2SSR_32KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_8_16_32KHZ_VALUE; + break; + case 44100: + reg_value = AW882XX_I2SSR_44P1KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + break; + case 48000: + reg_value = AW882XX_I2SSR_48KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + break; + case 96000: + reg_value = AW882XX_I2SSR_96KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + break; + case 192000: + reg_value = AW882XX_I2SSR_192KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + break; + default: + reg_value = AW882XX_I2SSR_48KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + pr_err("%s: rate can not support\n", __func__); + break; + } + aw882xx_i2c_write_bits(aw882xx, AW882XX_PLLCTRL1_REG, + AW882XX_I2S_CCO_MUX_MASK, cco_mux_value); + + /* set chip rate */ + if (-1 != reg_value) { + aw882xx_i2c_write_bits(aw882xx, AW882XX_I2SCTRL_REG, + AW882XX_I2SSR_MASK, reg_value); + } + + /* get bit width */ + width = params_width(params); + pr_debug("%s: width = %d\n", __func__, width); + switch (width) { + case 16: + reg_value = AW882XX_I2SFS_16_BITS_VALUE; + break; + case 20: + reg_value = AW882XX_I2SFS_20_BITS_VALUE; + break; + case 24: + reg_value = AW882XX_I2SFS_24_BITS_VALUE; + break; + case 32: + reg_value = AW882XX_I2SFS_32_BITS_VALUE; + break; + default: + reg_value = AW882XX_I2SFS_16_BITS_VALUE; + pr_err("%s: width can not support\n", __func__); + break; + } + + /* get width */ + if (-1 != reg_value) { + aw882xx_i2c_write_bits(aw882xx, AW882XX_I2SCTRL_REG, + AW882XX_I2SFS_MASK, reg_value); + } + + return 0; +} + +static int aw882xx_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + + pr_info("%s: mute state=%d\n", __func__, mute); + + if (!(aw882xx->flags & AW882XX_FLAG_START_ON_MUTE)) + return 0; + + if (mute) { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + aw882xx_smartpa_cfg(aw882xx, false); + atomic_set(&g_algo_tx_enable, 0); + atomic_set(&g_algo_rx_enable, 0); + atomic_set(&g_skt_disable, 0); + } else { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + aw882xx_smartpa_cfg(aw882xx, true); + } + + return 0; +} + +static void aw882xx_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aw882xx->rate = 0; + aw882xx_run_pwd(aw882xx, true); + } +} + +static const struct snd_soc_dai_ops aw882xx_dai_ops = { + .startup = aw882xx_startup, + .set_fmt = aw882xx_set_fmt, + .set_sysclk = aw882xx_set_dai_sysclk, + .hw_params = aw882xx_hw_params, + .mute_stream = aw882xx_mute, + .shutdown = aw882xx_shutdown, +}; + +static struct snd_soc_dai_driver aw882xx_dai[] = { + { + .name = "aw882xx-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AW882XX_RATES, + .formats = AW882XX_FORMATS, + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AW882XX_RATES, + .formats = AW882XX_FORMATS, + }, + .ops = &aw882xx_dai_ops, + .symmetric_rates = 1, +#if 0 + .symmetric_channels = 1, + .symmetric_samplebits = 1, +#endif + }, +}; + +/***************************************************** + * + * codec driver + * + *****************************************************/ +static int aw882xx_probe(struct snd_soc_component *component) +{ + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + int ret = -1; + + pr_info("%s: enter\n", __func__); + + aw882xx->component = component; + + aw882xx_add_codec_controls(aw882xx); + + //if (component->dev->of_node) + //dev_set_name(component->dev, "%s", "aw882xx_smartpa"); + + pr_info("%s: exit\n", __func__); + + ret = 0; + return ret; +} + +static void aw882xx_remove(struct snd_soc_component *component) +{ + /*struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec);*/ + pr_info("%s: enter\n", __func__); + return; +} + + +static unsigned int aw882xx_codec_read(struct snd_soc_component *component, + unsigned int reg) +{ + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + unsigned int value = 0; + int ret = -1; + + pr_debug("%s: enter\n", __func__); + + if (aw882xx_reg_access[reg] & REG_RD_ACCESS) { + ret = aw882xx_i2c_read(aw882xx, reg, &value); + if (ret < 0) + pr_debug("%s: read register failed\n", __func__); + } else { + pr_debug("%s: register 0x%x no read access\n", __func__, reg); + } + return ret; +} + +static int aw882xx_codec_write(struct snd_soc_component *component, + unsigned int reg, unsigned int value) +{ + int ret = -1; + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + + pr_debug("%s: enter ,reg is 0x%x value is 0x%x\n", + __func__, reg, value); + + if (aw882xx_reg_access[reg]®_WR_ACCESS) { + ret = aw882xx_i2c_write(aw882xx, reg, value); + } else { + pr_debug("%s: register 0x%x no write access\n", + __func__, reg); + } + + return ret; +} + +static struct snd_soc_component_driver soc_component_dev_aw882xx = { + .probe = aw882xx_probe, + .remove = aw882xx_remove, + .read = aw882xx_codec_read, + .write = aw882xx_codec_write, +// .reg_cache_size = AW882XX_REG_MAX, +// .reg_word_size = 2, +}; + +/****************************************************** + * + * irq + * + ******************************************************/ +static void aw882xx_interrupt_setup(struct aw882xx *aw882xx) +{ + unsigned int reg_val; + + pr_info("%s: enter\n", __func__); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSINTM_REG, ®_val); + reg_val &= (~AW882XX_UVLS_VDD_BELOW_2P8V_VALUE); + reg_val &= (~AW882XX_NOCLKS_TRIG_VALUE); + reg_val &= (~AW882XX_CLKS_TRIG_VALUE); + aw882xx_i2c_write(aw882xx, AW882XX_SYSINTM_REG, reg_val); +} + +static void aw882xx_interrupt_clear(struct aw882xx *aw882xx) +{ + unsigned int reg_val = 0; + + pr_info("%s: enter\n", __func__); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_val); + pr_info("%s: reg SYSST=0x%x\n", __func__, reg_val); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSINT_REG, ®_val); + pr_info("%s: reg SYSINT=0x%x\n", __func__, reg_val); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSINTM_REG, ®_val); + pr_info("%s: reg SYSINTM=0x%x\n", __func__, reg_val); +} + +static irqreturn_t aw882xx_irq(int irq, void *data) +{ + struct aw882xx *aw882xx = data; + + pr_info("%s: enter\n", __func__); + + aw882xx_interrupt_clear(aw882xx); + + pr_info("%s: exit\n", __func__); + + return IRQ_HANDLED; +} + +/***************************************************** + * + * device tree + * + *****************************************************/ +int aw882xx_parse_low_vol_cfg(struct device *dev, struct aw882xx *aw882xx, + struct device_node *np, char *dir, struct aw882xx_low_vol **table, int *num) +{ + int ret; + int i; + uint32_t *cfg_value; + char cfg_name[32] = {0}; + int cfg_len; + + snprintf(cfg_name, sizeof(cfg_name), "low-vol-table-%s", dir); + + cfg_len = of_property_count_u32_elems(np, cfg_name); + if (cfg_len <= 0) { + dev_info(dev, "%s: %s get cfg_len:%d error\n", + __func__, cfg_name, cfg_len); + goto use_default; + } + + cfg_value = devm_kzalloc(dev, sizeof(uint32_t) * cfg_len, GFP_KERNEL); + if (!cfg_value) { + dev_info(dev,"%s: %s aw_cfg kzalloc failed\n", + __func__, cfg_name); + goto use_default; + } + + ret = of_property_read_u32_array(np, cfg_name, cfg_value, cfg_len); + if (ret != 0) { + dev_info(dev, "%s: fail get %s dt\n", + __func__, cfg_name); + devm_kfree(dev, cfg_value); + goto use_default; + } + *num = cfg_len / 3; + *table = devm_kzalloc(dev, sizeof(struct aw882xx_low_vol) * (*num), GFP_KERNEL); + if (!(*table)) { + dev_info(dev, "%s: %s table kzalloc failed\n", + __func__, cfg_name); + devm_kfree(dev, cfg_value); + goto use_default; + } + + for(i = 0; i < (*num); i++) { + (*table)[i].vol = cfg_value[3 * i]; + (*table)[i].ipeak = cfg_value[3 * i + 1]; + (*table)[i].gain = cfg_value[3 * i + 2]; + } + + devm_kfree(dev, cfg_value); + + return 0; + +use_default: + dev_info(dev, "%s: %s low-vol table use default cfg\n", __func__, cfg_name); + if (!strcmp(dir,"up")) { + *table = vol_up_table; + *num = sizeof(vol_up_table) / sizeof(struct aw882xx_low_vol); + } else if (!strcmp(dir,"down")) { + *table = vol_down_table; + *num = sizeof(vol_down_table) / sizeof(struct aw882xx_low_vol); + } else { + dev_err(dev,"%s: unsupport dir %s\n", + __func__, dir); + return -EINVAL; + } + return 0; +} + + +int aw882xx_parse_low_temp_cfg(struct device *dev, struct aw882xx *aw882xx, + struct device_node *np, char *dir, struct aw882xx_low_temp **table, int *num) +{ + int ret; + int i; + uint32_t *cfg_value; + char cfg_name[32] = {0}; + int cfg_len; + + snprintf(cfg_name, sizeof(cfg_name), "low-temp-table-%s", dir); + + cfg_len = of_property_count_u32_elems(np, cfg_name); + if (cfg_len <= 0) { + dev_info(dev, "%s: %s get cfg_len:%d error\n", + __func__, cfg_name, cfg_len); + goto use_default; + } + + cfg_value = devm_kzalloc(dev, sizeof(uint32_t) * cfg_len, GFP_KERNEL); + if (!cfg_value) { + dev_info(dev,"%s: %s aw_cfg kzalloc failed\n", + __func__, cfg_name); + goto use_default; + } + + ret = of_property_read_u32_array(np, cfg_name, cfg_value, cfg_len); + if (ret != 0) { + dev_info(dev, "%s: fail get %s dt\n", + __func__, cfg_name); + devm_kfree(dev, cfg_value); + goto use_default; + } + *num = cfg_len / 4; + *table = devm_kzalloc(dev, sizeof(struct aw882xx_low_temp) * (*num), GFP_KERNEL); + if (!(*table)) { + dev_info(dev, "%s: %s table kzalloc failed\n", + __func__, cfg_name); + devm_kfree(dev, cfg_value); + goto use_default; + } + + for(i = 0; i < (*num); i++) { + (*table)[i].temp = cfg_value[4 * i]; + (*table)[i].ipeak = cfg_value[4 * i + 1]; + (*table)[i].gain = cfg_value[4 * i + 2]; + (*table)[i].vmax = cfg_value[4 * i + 3]; + } + + devm_kfree(dev, cfg_value); + + return 0; + +use_default: + dev_info(dev, "%s: %s temp table use default cfg\n", __func__, cfg_name); + if (!strcmp(dir,"up")) { + *table = temp_up_table; + *num = sizeof(temp_up_table) / sizeof(struct aw882xx_low_temp); + } else if (!strcmp(dir,"down")) { + *table = temp_down_table; + *num = sizeof(temp_down_table) / sizeof(struct aw882xx_low_temp); + } else { + dev_err(dev,"%s: unsupport dir %s\n", + __func__, dir); + return -EINVAL; + } + return 0; +} + +static int aw882xx_parse_dt(struct device *dev, struct aw882xx *aw882xx, + struct device_node *np) +{ + int ret = 0; + int i = 0; + struct aw882xx_monitor *monitor = &aw882xx->monitor; + /* gpio */ + aw882xx->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (aw882xx->reset_gpio < 0) { + dev_err(dev, "%s: no reset gpio provided, will not HW reset device\n", + __func__); + } else { + dev_info(dev, "%s: reset gpio provided ok\n", __func__); + } + aw882xx->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (aw882xx->irq_gpio < 0) + dev_info(dev, "%s: no irq gpio provided.\n", __func__); + else + dev_info(dev, "%s: irq gpio provided ok.\n", __func__); + + ret = of_property_read_u32(np, "monitor-flag", &monitor->is_enable); + if (ret) { + monitor->is_enable = AW882XX_MONITOR_DEFAULT_FLAG; + dev_err(dev, "%s: monitor-flag get failed ,use default value!\n", __func__); + } else { + dev_info(dev, "%s: monitor-flag = %d\n", + __func__, monitor->is_enable); + } + + ret = of_property_read_u32(np, "monitor-timer-val", &monitor->timer_val); + if (ret) { + monitor->timer_val = AW882XX_MONITOR_DEFAULT_TIMER_VAL; + dev_err(dev, "%s: monitor-timer-val get failed,use default value!\n", __func__); + } else { + dev_info(dev, "%s: monitor-timer-val = %d\n", + __func__, monitor->timer_val); + } + + ret = of_property_read_u32(np, "afe-rx-portid", &aw882xx->afe_rx_portid); + if (ret) { + aw882xx->afe_rx_portid = AW882XX_DEFAULT_AFE_RX_PROT_ID; + dev_err(dev, "%s: afe_rx_portid get failed,use default port: 0x%x!\n", + __func__, aw882xx->afe_rx_portid); + } else { + dev_info(dev, "%s: afe_rx_portid = 0x%x\n", + __func__, aw882xx->afe_rx_portid); + } + + ret = of_property_read_u32(np, "afe-tx-portid", &aw882xx->afe_tx_portid); + if (ret) { + aw882xx->afe_tx_portid = AW882XX_DEFAULT_AFE_TX_PROT_ID; + dev_err(dev, "%s: afe_tx_portid get failed,use default port: 0x%x!\n", + __func__, aw882xx->afe_tx_portid); + } else { + dev_info(dev, "%s: afe_tx_portid = 0x%x\n", + __func__, aw882xx->afe_tx_portid); + } + + ret = of_property_read_u32(np, "afe-profile", &aw882xx->afe_profile); + if (ret) { + aw882xx->afe_profile = 0; + dev_err(dev, "%s: afe-profile get failed,use default value!\n", __func__); + } else { + dev_info(dev, "%s: afe-profile = %d\n", + __func__, aw882xx->afe_profile); + } + + /*get low vol table cfg*/ + ret = aw882xx_parse_low_vol_cfg(dev, aw882xx, np, "up", + &monitor->vol_up_table, &monitor->vol_up_num); + if (ret <0) + return ret; + + ret = aw882xx_parse_low_vol_cfg(dev, aw882xx, np, "down", + &monitor->vol_down_table, &monitor->vol_down_num); + if (ret < 0) + return ret; + + for(i = 0; i < monitor->vol_up_num; i++) + dev_info(dev,"%s:vol_up_table vol:%d, %d, %d\n", + __func__, monitor->vol_up_table[i].vol, + monitor->vol_up_table[i].ipeak, + monitor->vol_up_table[i].gain); + + for(i = 0; i < monitor->vol_down_num; i++) + dev_info(dev, "%s:vol_down_table vol:%d, %d, %d\n", + __func__, monitor->vol_down_table[i].vol, + monitor->vol_down_table[i].ipeak, + monitor->vol_down_table[i].gain); + + for(i = 0; i < monitor->vol_down_num; i++) { + if(monitor->vol_down_table[i].vol >= + monitor->vol_up_table[monitor->vol_up_num - 1 -i].vol) { + dev_err(aw882xx->dev,"%s: use default table\n", __func__); + monitor->vol_up_table = vol_up_table; + monitor->vol_up_num= sizeof(vol_up_table) / + sizeof(struct aw882xx_low_vol); + monitor->vol_down_table = vol_down_table; + monitor->vol_down_num = sizeof(vol_down_table) / + sizeof(struct aw882xx_low_vol); + break; + } + } + + for(i = 1; i < monitor->vol_down_num; i++) { + if(monitor->vol_up_table[monitor->vol_up_num - i].vol >= + monitor->vol_down_table[i].vol) { + dev_err(aw882xx->dev,"%s: use default table\n", __func__); + monitor->vol_up_table = vol_up_table; + monitor->vol_up_num= sizeof(vol_up_table) / + sizeof(struct aw882xx_low_vol); + monitor->vol_down_table = vol_down_table; + monitor->vol_down_num = sizeof(vol_down_table) / + sizeof(struct aw882xx_low_vol); + break; + } + } + /*get low temp table cfg*/ + ret = aw882xx_parse_low_temp_cfg(dev, aw882xx, np, "up", + &monitor->temp_up_table, &monitor->temp_up_num); + if (ret <0) + return ret; + + ret = aw882xx_parse_low_temp_cfg(dev, aw882xx, np, "down", + &monitor->temp_down_table, &monitor->temp_down_num); + + for(i = 0; i < monitor->temp_up_num; i++) + dev_info(dev,"%s:temp_up_table temp:%d, 0x%x, 0x%x, 0x%x\n", + __func__, monitor->temp_up_table[i].temp, + monitor->temp_up_table[i].ipeak, + monitor->temp_up_table[i].gain, + monitor->temp_up_table[i].vmax); + + for(i = 0; i < monitor->temp_down_num; i++) + dev_info(dev, "%s:temp_down_table temp:%d, 0x%x, 0x%x, 0x%x\n", + __func__, monitor->temp_down_table[i].temp, + monitor->temp_down_table[i].ipeak, + monitor->temp_down_table[i].gain, + monitor->temp_down_table[i].vmax); + + for(i = 0; i < monitor->temp_down_num; i++) { + if(monitor->temp_down_table[i].temp >= + monitor->temp_up_table[monitor->temp_up_num - 1 -i].temp) { + dev_err(aw882xx->dev,"%s: use default table\n", __func__); + monitor->temp_up_table = temp_up_table; + monitor->temp_up_num= sizeof(temp_up_table) / + sizeof(struct aw882xx_low_temp); + monitor->temp_down_table = temp_down_table; + monitor->temp_down_num = sizeof(temp_down_table) / + sizeof(struct aw882xx_low_temp); + break; + } + } + + for(i = 1; i < monitor->temp_down_num; i++) { + if(monitor->temp_up_table[monitor->temp_up_num - i].temp >= + monitor->temp_down_table[i].temp) { + dev_err(aw882xx->dev,"%s: use default table\n", __func__); + monitor->temp_up_table = temp_up_table; + monitor->temp_up_num= sizeof(temp_up_table) / + sizeof(struct aw882xx_low_temp); + monitor->temp_down_table = temp_down_table; + monitor->temp_down_num = sizeof(temp_down_table) / + sizeof(struct aw882xx_low_temp); + break; + } + } + return 0; +} + +int aw882xx_hw_reset(struct aw882xx *aw882xx) +{ + pr_info("%s: enter\n", __func__); + + if (aw882xx && gpio_is_valid(aw882xx->reset_gpio)) { + gpio_set_value_cansleep(aw882xx->reset_gpio, 0); + msleep(1); + gpio_set_value_cansleep(aw882xx->reset_gpio, 1); + msleep(2); + } else { + dev_err(aw882xx->dev, "%s: failed\n", __func__); + } + return 0; +} + +/***************************************************** + * + * check chip id + * + *****************************************************/ +int aw882xx_read_chipid(struct aw882xx *aw882xx) +{ + int ret = -1; + unsigned int cnt = 0; + unsigned int reg = 0; + + while (cnt < AW_READ_CHIPID_RETRIES) { + ret = aw882xx_i2c_read(aw882xx, AW882XX_ID_REG, ®); + if (ret < 0) { + dev_err(aw882xx->dev, + "%s: failed to read REG_ID: %d\n", + __func__, ret); + return -EIO; + } + switch (reg) { + case AW882XX_ID: + pr_info("%s: aw882xx detected\n", __func__); + aw882xx->flags |= AW882XX_FLAG_SKIP_INTERRUPTS; + aw882xx->flags |= AW882XX_FLAG_START_ON_MUTE; + aw882xx->chipid = AW882XX_ID; + pr_info("%s: aw882xx->flags=0x%x\n", + __func__, aw882xx->flags); + return 0; + default: + pr_info("%s: unsupported device revision (0x%x)\n", + __func__, reg); + break; + } + cnt++; + + msleep(AW_READ_CHIPID_RETRY_DELAY); + } + + return -EINVAL; +} + +/****************************************************** + * + * sys group attribute: monitor + * + ******************************************************/ +static ssize_t aw882xx_monitor_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + uint32_t enable = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &enable); + if (ret < 0) + return ret; + + dev_info(dev, "%s:monitor enable set =%d\n", + __func__, enable); + aw882xx->monitor.is_enable = enable; + if (enable) + aw882xx_monitor_start(&aw882xx->monitor); + + return count; +} + +static ssize_t aw882xx_monitor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + uint32_t local_enable; + + local_enable = aw882xx->monitor.is_enable; + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx monitor enable: %d\n", local_enable); + return len; +} + +/****************************************************** + * + * sys group attribute: reg + * + ******************************************************/ +static ssize_t aw882xx_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + unsigned int databuf[2] = {0}; + + if (2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) + aw882xx_i2c_write(aw882xx, databuf[0], databuf[1]); + + return count; +} + +static ssize_t aw882xx_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned char i = 0; + unsigned int reg_val = 0; + + for (i = 0; i < AW882XX_REG_MAX; i++) { + if (aw882xx_reg_access[i]®_RD_ACCESS) { + aw882xx_i2c_read(aw882xx, i, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, + "reg:0x%02x=0x%04x\n", i, reg_val); + } + } + return len; +} + +static ssize_t aw882xx_rw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0}; + + if (2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) { + aw882xx->reg_addr = (unsigned char)databuf[0]; + aw882xx_i2c_write(aw882xx, databuf[0], databuf[1]); + } else if (1 == sscanf(buf, "%x", &databuf[0])) { + aw882xx->reg_addr = (unsigned char)databuf[0]; + } + + return count; +} + +static ssize_t aw882xx_rw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned int reg_val = 0; + + if (aw882xx_reg_access[aw882xx->reg_addr] & REG_RD_ACCESS) { + aw882xx_i2c_read(aw882xx, aw882xx->reg_addr, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, + "reg:0x%02x=0x%04x\n", aw882xx->reg_addr, reg_val); + } + return len; +} + +static ssize_t aw882xx_spk_rcv_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + int ret = -1; + unsigned int databuf[2] = {0}; + + ret = kstrtouint(buf, 0, &databuf[0]); + if (ret < 0) + return ret; + + aw882xx->spk_rcv_mode = databuf[0]; + + return count; +} + +static ssize_t aw882xx_spk_rcv_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + + if (aw882xx->spk_rcv_mode == AW882XX_SPEAKER_MODE) + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx spk_rcv: %d, speaker mode\n", + aw882xx->spk_rcv_mode); + else if (aw882xx->spk_rcv_mode == AW882XX_RECEIVER_MODE) + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx spk_rcv: %d, receiver mode\n", + aw882xx->spk_rcv_mode); + else + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx spk_rcv: %d, unknown mode\n", + aw882xx->spk_rcv_mode); + + return len; +} + +static ssize_t aw882xx_mec_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + /*struct aw882xx *aw882xx = dev_get_drvdata(dev);*/ + uint32_t mec_ctr = 0; + uint32_t param_id = AFE_PARAM_ID_AWDSP_RX_SET_ENABLE; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &mec_ctr); + if (ret < 0) + return ret; + + pr_info("%s: mec_ctr=%d\n", __func__, mec_ctr); + + ret = aw_send_afe_cal_apr(g_aw882xx->afe_rx_portid, g_aw882xx->afe_tx_portid, + param_id, &mec_ctr, sizeof(uint32_t), true); + if (ret) + pr_err("%s: dsp_msg error, ret=%d\n", __func__, ret); + + mdelay(2); + + return count; +} + +static ssize_t aw882xx_mec_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + /*struct aw882xx *aw882xx = dev_get_drvdata(dev);*/ + ssize_t len = 0; + int ret = -1; + uint8_t *buffer = NULL; + uint32_t mec_ctr = 0; + uint32_t param_id = AFE_PARAM_ID_AWDSP_RX_SET_ENABLE; + + buffer = kmalloc(sizeof(uint32_t), GFP_KERNEL); + if (buffer == NULL) { + pr_err("%s can not allocate memory\n", __func__); + return -ENOMEM; + } + + ret = aw_send_afe_cal_apr(g_aw882xx->afe_rx_portid, g_aw882xx->afe_tx_portid, + param_id, buffer, sizeof(uint32_t), false); + if (ret) { + pr_err("%s: dsp_msg_read error: %d\n", __func__, ret); + kfree(buffer); + return -EFAULT; + } + + memcpy(&mec_ctr, buffer, sizeof(uint32_t)); + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx mec: %d\n", mec_ctr); + + kfree(buffer); + + return len; +} + +static ssize_t aw882xx_default_re_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + int ret = -1; + + if((aw882xx != NULL) && (aw882xx->default_re == 0)) { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_RE_L, + &aw882xx->default_re, sizeof(int32_t), false); + if (ret) { + pr_err("%s: dsp_msg_read error: %d\n", __func__, ret); + return -EFAULT; + } + } + if(aw882xx != NULL) { + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx default_re: %d\n", aw882xx->default_re); + } + return len; +} + +#ifdef AW_DEBUG +static ssize_t aw882xx_vol_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + uint32_t vol = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &vol); + if (ret < 0) + return ret; + + pr_info("%s: vol set =%d\n", __func__, vol); + aw882xx->monitor.test_vol = vol; + + return count; +} + +static ssize_t aw882xx_vol_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + uint32_t local_vol = aw882xx->monitor.test_vol; + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx vol: %d\n", local_vol); + return len; +} +static ssize_t aw882xx_temp_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + int32_t temp = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtoint(buf, 0, &temp); + if (ret < 0) + return ret; + + pr_info("%s: temp set =%d\n", __func__, temp); + aw882xx->monitor.test_temp = temp; + + return count; +} + +static ssize_t aw882xx_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + int32_t local_temp = aw882xx->monitor.test_temp; + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx vol: %d\n", local_temp); + return len; +} +#endif +static int aw882xx_cali_operation(struct aw882xx *aw882xx, + unsigned int cmd, unsigned long arg) +{ + int16_t data_len = _IOC_SIZE(cmd); + int ret = 0; + char *data_ptr = NULL; + struct ptr_params_data *p_params; + int32_t *p_data; + + + data_ptr = kmalloc(data_len, GFP_KERNEL); + if (data_ptr == NULL) { + pr_err("%s : malloc failed !\n", __func__); + return -EFAULT; + } + + pr_info("cmd : %d, data_len%d\n", cmd , data_len); + switch (cmd) { + case AW882XX_IOCTL_ENABLE_CALI: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + g_aw882xx_cali_flag = (int8_t)data_ptr[0]; + pr_info("%s:set cali %s", __func__, + (g_aw882xx_cali_flag == 0) ? ("disable") : ("enable")); + } break; + case AW882XX_IOCTL_SET_CALI_CFG: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L); + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_GET_CALI_CFG: { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_GET_CALI_DATA: { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_REAL_DATA_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_REAL_DATA_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_SET_NOISE: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_NOISE_L, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_NOISE_L); + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_GET_F0: { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_F0_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_F0_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_SET_CALI_RE: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + if(aw882xx->default_re == 0) { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_RE_L, &aw882xx->default_re, sizeof(int32_t), false); + pr_info("aw882xx: default_re:%d\n", aw882xx->default_re); + } + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_RE_L, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_RE_L); + ret = -EFAULT; + goto exit; + } + aw882xx_set_cali_re(aw882xx, *((int32_t *)data_ptr)); + } break; + case AW882XX_IOCTL_GET_CALI_RE: { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_RE_L, + data_ptr, data_len, false); + if(aw882xx->default_re == 0) { + aw882xx->default_re = *((uint32_t *)data_ptr); + pr_info("aw882xx: default_re:%d\n", aw882xx->default_re); + } + if (ret) { + pr_err("%s: dsp_msg_read error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_RE_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_GET_VMAX: { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_VMAX_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error:0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_VMAX_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_SET_VMAX: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_VMAX_L, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_VMAX_L); + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_SET_PARAM: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_PARAMS, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_PARAMS); + ret = -EFAULT; + goto exit; + } + pr_debug("%s: set params done", __func__); + } break; + case AW882XX_IOCTL_SET_PTR_PARAM_NUM: { + if (copy_from_user(data_ptr, (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + p_params = (struct ptr_params_data *)data_ptr; + if (p_params->data == NULL || (!p_params->len)) { + pr_err("%s: p_params error\n", __func__); + ret = -EFAULT; + goto exit; + } + p_data = kzalloc(p_params->len, GFP_KERNEL); + if (!p_data) { + pr_err("%s: error allocating memory\n", __func__); + ret = -ENOMEM; + goto exit; + } + + if (copy_from_user(p_data, (void __user *)p_params->data, + p_params->len)) { + kfree(p_data); + p_data = NULL; + ret = -EFAULT; + goto exit; + } + + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, + aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_PARAMS, + p_data, p_params->len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_PARAMS); + kfree(p_data); + p_data = NULL; + ret =-EFAULT; + goto exit; + } + kfree(p_data); + p_data = NULL; + } break; + default: { + pr_err("%s : cmd %d\n", __func__, cmd); + } break; + } +exit: + kfree(data_ptr); + return ret; +} + +static DEVICE_ATTR(monitor, S_IWUSR | S_IRUGO, + aw882xx_monitor_show, aw882xx_monitor_store); +static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, + aw882xx_reg_show, aw882xx_reg_store); +static DEVICE_ATTR(rw, S_IWUSR | S_IRUGO, + aw882xx_rw_show, aw882xx_rw_store); +static DEVICE_ATTR(spk_rcv, S_IWUSR | S_IRUGO, + aw882xx_spk_rcv_show, aw882xx_spk_rcv_store); +static DEVICE_ATTR(mec, S_IWUSR | S_IRUGO, + aw882xx_mec_show, aw882xx_mec_store); +static DEVICE_ATTR(default_re, S_IWUSR | S_IRUGO, + aw882xx_default_re_show, NULL); +#ifdef AW_DEBUG +static DEVICE_ATTR(vol, S_IWUSR | S_IRUGO, + aw882xx_vol_show, aw882xx_vol_store); +static DEVICE_ATTR(temp, S_IWUSR | S_IRUGO, + aw882xx_temp_show, aw882xx_temp_store); +#endif + +static struct attribute *aw882xx_attributes[] = { + &dev_attr_monitor.attr, + &dev_attr_reg.attr, + &dev_attr_rw.attr, + &dev_attr_spk_rcv.attr, + &dev_attr_mec.attr, + &dev_attr_default_re.attr, +#ifdef AW_DEBUG + &dev_attr_vol.attr, + &dev_attr_temp.attr, +#endif + NULL +}; + +static struct attribute_group aw882xx_attribute_group = { + .attrs = aw882xx_attributes +}; + +#define AW882XX_SMARTPA_NAME "aw882xx_smartpa" +static int aw882xx_file_open(struct inode *inode, struct file *file) +{ + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + file->private_data = (void *)g_aw882xx; + + pr_debug("open success"); + return 0; +} + +static int aw882xx_file_release(struct inode *inode, struct file *file) +{ + file->private_data = (void *)NULL; + + pr_debug("release successi\n"); + return 0; +} + +static long aw882xx_file_unlocked_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct aw882xx *aw882xx = NULL; + + if (((_IOC_TYPE(cmd)) != (AW882XX_IOCTL_MAGIC))) { + pr_err("%s: cmd magic err\n", __func__); + return -EINVAL; + } + aw882xx = (struct aw882xx *)file->private_data; + ret = aw882xx_cali_operation(aw882xx, cmd, arg); + if (ret) + return -EINVAL; + + return 0; +} + +static struct file_operations aw882xx_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = aw882xx_file_unlocked_ioctl, + .open = aw882xx_file_open, + .release = aw882xx_file_release, +}; + +static struct miscdevice aw882xx_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = AW882XX_SMARTPA_NAME, + .fops = &aw882xx_fops, +}; + +void init_aw882xx_misc_driver(struct aw882xx *aw882xx) +{ + int ret; + + ret = misc_register(&aw882xx_misc); + if (ret) { + pr_err("%s: misc register fail: %d\n", __func__, ret); + return; + } + pr_debug("%s: misc register success", __func__); +} + +/*monitor*/ +static int aw882xx_monitor_start(struct aw882xx_monitor *monitor) +{ + pr_debug("%s: enter\n", __func__); + + if (!hrtimer_active(&monitor->timer)) { + pr_info("%s: start monitor\n", __func__); + hrtimer_start(&monitor->timer, + ktime_set(monitor->timer_val/1000, + (monitor->timer_val%1000)*1000000), HRTIMER_MODE_REL); + } + + return 0; +} + +static int aw882xx_monitor_stop(struct aw882xx_monitor *monitor) +{ + pr_info("%s: enter\n", __func__); + + if (hrtimer_active(&monitor->timer)) { + pr_info("%s: stop monitor\n", __func__); + hrtimer_cancel(&monitor->timer); + } + return 0; +} + +static enum hrtimer_restart + aw882xx_monitor_timer_func(struct hrtimer *timer) +{ + struct aw882xx_monitor *monitor = + container_of(timer, struct aw882xx_monitor, timer); + + pr_debug("%s : enter\n", __func__); + + if (monitor->is_enable) + schedule_work(&monitor->work); + + return HRTIMER_NORESTART; +} +static int aw882xx_monitor_get_voltage(struct aw882xx *aw882xx, + unsigned int *vol) +{ + int ret = -1; + uint16_t local_vol = 0; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_VBAT_REG, vol); + if (ret < 0) { + pr_err("%s: read voltage failed !\n", __func__); + return ret; + } + local_vol = (*vol) * AW882XX_MONITOR_VBAT_RANGE + / AW882XX_MONITOR_INT_10BIT; + + if(aw882xx->monitor.vol_count < AW_VOL_COUNT) { + aw882xx->monitor.vol_count++; + if(aw882xx->monitor.pre_vol == 0) { + *vol = local_vol; + } else { + *vol = (aw882xx->monitor.pre_vol * (AW_VOL_COUNT -1 ) + + local_vol) / AW_VOL_COUNT; + } + } else { + *vol = (aw882xx->monitor.pre_vol * (AW_VOL_COUNT -1 ) + + local_vol) / AW_VOL_COUNT; + } + aw882xx->monitor.pre_vol = *vol; + pr_debug("%s: cur voltage is %d, vol is %d\n", __func__, local_vol, *vol); + return 0; +} +static int aw882xx_monitor_voltage(struct aw882xx *aw882xx, + struct aw882xx_low_vol *vol_cfg) +{ + int ret = -1; + int i; + unsigned int voltage = 0; + struct aw882xx_monitor *monitor = NULL; + + if (aw882xx == NULL || vol_cfg == NULL) { + pr_err("%s: pointer is NUL\n", __func__); + return ret; + } + monitor = &aw882xx->monitor; +#ifdef AW_DEBUG + if (monitor->test_vol == 0) { + ret = aw882xx_monitor_get_voltage(aw882xx, &voltage); + if (ret < 0) + return ret; + } else { + voltage = monitor->test_vol; + } +#else + ret = aw882xx_monitor_get_voltage(aw882xx, &voltage); + if (ret < 0) + return ret; +#endif + + if (monitor->first_entry == AW_FIRST_ENTRY) { + for (i = 0; i < monitor->vol_down_num; i++) { + if (voltage < monitor->vol_down_table[i].vol) { + *vol_cfg = monitor->vol_down_table[i]; + return 0; + } + } + + *vol_cfg = monitor->vol_up_table[0]; + return 0; + } + + if (voltage < monitor->vol_down_table[0].vol) { + *vol_cfg = monitor->vol_down_table[0]; + return 0; + } + + for (i = 1; i < monitor->vol_down_num; i++) { + if ((monitor->vol_up_table[monitor->vol_down_num - i].vol < voltage) && + (voltage < monitor->vol_down_table[i].vol)) { + *vol_cfg = monitor->vol_down_table[i]; + return 0; + } + } + + if (voltage > monitor->vol_up_table[0].vol) { + *vol_cfg = monitor->vol_up_table[0]; + return 0; + } + + return 0; +} +static int aw882xx_monitor_get_temperature(struct aw882xx *aw882xx, int *temp) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t local_temp; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_TEMP_REG, ®_val); + if (ret < 0) { + pr_err("%s: get temperature failed !\n", __func__); + return ret; + } + + local_temp = reg_val; + if (local_temp & AW882XX_MONITOR_TEMP_SIGN_MASK) + local_temp = local_temp | AW882XX_MONITOR_TEMP_NEG_MASK; + + *temp = (int)local_temp; + pr_debug("%s: chip temperature = %d\n", __func__, local_temp); + return 0; +} + +static int aw882xx_monitor_temperature(struct aw882xx *aw882xx, + struct aw882xx_low_temp *temp_cfg) +{ + int ret; + int i; + struct aw882xx_monitor *monitor = NULL; + int current_temp = 0; + + monitor = &aw882xx->monitor; +#ifdef AW_DEBUG + if (monitor->test_temp == 0) { + ret = aw882xx_monitor_get_temperature(aw882xx, ¤t_temp); + if (ret) + return ret; + } else { + current_temp = monitor->test_temp; + } +#else + ret = aw882xx_monitor_get_temperature(aw882xx, ¤t_temp); + if (ret < 0) + return ret; +#endif + + if (monitor->first_entry == AW_FIRST_ENTRY) { + monitor->first_entry = AW_NOT_FIRST_ENTRY; + for (i = 0; i < monitor->temp_down_num; i++) { + if (current_temp < monitor->temp_down_table[i].temp) { + temp_cfg->ipeak = monitor->temp_down_table[i].ipeak; + temp_cfg->gain = monitor->temp_down_table[i].gain; + temp_cfg->vmax = monitor->temp_down_table[i].vmax; + return 0; + } + } + + temp_cfg->ipeak = monitor->temp_up_table[0].ipeak; + temp_cfg->gain = monitor->temp_up_table[0].gain; + temp_cfg->vmax = monitor->temp_up_table[0].vmax; + return 0; + } + + if (current_temp < monitor->temp_down_table[0].temp) { + temp_cfg->ipeak = monitor->temp_down_table[0].ipeak; + temp_cfg->gain = monitor->temp_down_table[0].gain; + temp_cfg->vmax = monitor->temp_down_table[0].vmax; + return 0; + } + + for (i = 1; i < monitor->temp_down_num; i++) { + if ((monitor->temp_up_table[monitor->temp_down_num - i].temp < current_temp) + && (current_temp < monitor->temp_down_table[i].temp)) { + temp_cfg->ipeak = monitor->temp_down_table[i].ipeak; + temp_cfg->gain = monitor->temp_down_table[i].gain; + temp_cfg->vmax = monitor->temp_down_table[i].vmax; + return 0; + } + } + + if (current_temp > monitor->temp_up_table[0].temp) { + temp_cfg->ipeak = monitor->temp_up_table[0].ipeak; + temp_cfg->gain = monitor->temp_up_table[0].gain; + temp_cfg->vmax = monitor->temp_up_table[0].vmax; + return 0; + } + + return 0; +} + +static void aw882xx_monitor_get_cfg(struct aw882xx_low_temp *temp, + struct aw882xx_low_vol *vol) +{ + if (vol->ipeak == IPEAK_NONE) + return; + + if (temp->ipeak == IPEAK_NONE) { + temp->ipeak = vol->ipeak; + temp->gain = vol->gain; + return; + } + + /*get min ipeak*/ + if (temp->ipeak > vol->ipeak) + temp->ipeak = vol->ipeak; + + /*get min gain*/ + if (temp->gain < vol->gain) + temp->gain = vol->gain; + +} +static void aw882xx_monitor_set_ipeak(struct aw882xx *aw882xx, uint8_t ipeak) +{ + unsigned int reg_val = 0; + unsigned int read_reg_val; + int ret; + + if (ipeak == IPEAK_NONE) + return; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_SYSCTRL2_REG, ®_val); + if (ret < 0) { + pr_err("%s: read ipeak failed\n", __func__); + return; + } + + read_reg_val = reg_val; + read_reg_val &= AW882XX_BIT_SYSCTRL2_BST_IPEAK_MASK; + + if (read_reg_val == ipeak) { + pr_debug("%s: ipeak = 0x%x, no change\n", + __func__, read_reg_val); + return; + } + reg_val &= (~AW882XX_BIT_SYSCTRL2_BST_IPEAK_MASK); + read_reg_val = ipeak; + reg_val |= read_reg_val; + + ret = aw882xx_i2c_write(aw882xx, AW882XX_SYSCTRL2_REG, reg_val); + if (ret < 0) { + pr_err("%s: write ipeak failed\n", __func__); + return; + } + pr_debug("%s: set reg val = 0x%x, ipeak = 0x%x\n", + __func__, reg_val, ipeak); +} +static void aw882xx_monitor_set_gain(struct aw882xx *aw882xx, uint8_t gain) +{ + unsigned int reg_val = 0; + unsigned int read_reg_val; + int ret; + + if (gain == GAIN_NONE) + return; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_val); + if (ret < 0) { + pr_err("%s: read gain failed\n", __func__); + return; + } + + read_reg_val = reg_val >> AW882XX_BIT_HAGCCFG4_GAIN_SHIFT; + aw882xx->cur_gain = gain; + if (read_reg_val == gain) { + pr_debug("%s: gain = 0x%x, no change\n", + __func__, read_reg_val); + return; + } + reg_val &= AW882XX_BIT_HAGCCFG4_GAIN_MASK; + read_reg_val = gain; + reg_val |= (read_reg_val << AW882XX_BIT_HAGCCFG4_GAIN_SHIFT); + + ret = aw882xx_i2c_write(aw882xx, AW882XX_HAGCCFG4_REG, reg_val); + if (ret < 0) { + pr_err("%s: write gain failed\n", __func__); + return; + } + pr_debug("%s: set reg val = 0x%x, gain = 0x%x\n", + __func__, reg_val, gain); +} +static void aw882xx_monitor_set_vmax(struct aw882xx *aw882xx, uint32_t vmax) +{ + uint32_t local_vmax = vmax; + int ret; + + if (vmax == VMAX_NONE) + return; + + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_VMAX_L, + &local_vmax, sizeof(uint32_t), true); + if (ret) + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_VMAX_L); + + pr_debug("%s: set vmax = 0x%x\n", __func__, vmax); +} +static void aw882xx_monitor_work(struct aw882xx *aw882xx) +{ + struct aw882xx_low_vol *vol_cfg = &aw882xx->monitor.vol_cfg; + struct aw882xx_low_temp *temp_cfg = &aw882xx->monitor.temp_cfg; + struct aw882xx_low_temp set_cfg; + int ret; + + if (aw882xx == NULL) { + pr_err("%s: pointer is NULL\n", __func__); + return; + } + if (g_aw882xx_cali_flag != 0 || g_aw882xx_profile_flag != 0) { + pr_info("%s: done nothing while start cali", __func__); + return; + } + + ret = aw882xx_monitor_voltage(aw882xx, vol_cfg); + if (ret < 0) { + pr_err("%s: monitor voltage failed\n", __func__); + return; + } + + ret = aw882xx_monitor_temperature(aw882xx, temp_cfg); + if (ret < 0) { + pr_err("%s: monitor temperature failed\n", __func__); + return; + } + pr_debug("%s: vol: ipeak = 0x%x, gain = 0x%x\n", + __func__, vol_cfg->ipeak, vol_cfg->gain); + pr_debug("%s: temp: ipeak = 0x%x, gain = 0x%x, vmax = 0x%x\n", + __func__, temp_cfg->ipeak, temp_cfg->gain, temp_cfg->vmax); + + memcpy(&set_cfg, temp_cfg, sizeof(struct aw882xx_low_temp)); + + aw882xx_monitor_get_cfg(&set_cfg, vol_cfg); + + aw882xx_monitor_set_ipeak(aw882xx, set_cfg.ipeak); + + aw882xx_monitor_set_gain(aw882xx, set_cfg.gain); + + aw882xx_monitor_set_vmax(aw882xx, set_cfg.vmax); +} + +static int aw882xx_get_hmute(struct aw882xx *aw882xx) +{ + unsigned int reg_val = 0; + int ret; + + pr_debug("%s: enter\n", __func__); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSCTRL2_REG, ®_val); + if ((~AW882XX_HMUTE_MASK) & reg_val) + ret = 1; + else + ret = 0; + + return ret; +} +static void aw882xx_monitor_work_func(struct work_struct *work) +{ + struct aw882xx_monitor *monitor = container_of(work, + struct aw882xx_monitor, work); + struct aw882xx *aw882xx = container_of(monitor, + struct aw882xx, monitor); + + pr_debug("%s: enter\n", __func__); + mutex_lock(&aw882xx->lock); + if (!aw882xx_get_hmute(aw882xx)) { + aw882xx_monitor_work(aw882xx); + aw882xx_monitor_start(monitor); + } + mutex_unlock(&aw882xx->lock); +} + +void init_aw882xx_monitor(struct aw882xx_monitor *monitor) +{ + pr_info("%s: enter\n", __func__); + hrtimer_init(&monitor->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + monitor->timer.function = aw882xx_monitor_timer_func; + INIT_WORK(&monitor->work, aw882xx_monitor_work_func); + monitor->pre_vol = 0; + monitor->vol_count = 0; + monitor->vol_cfg.ipeak = monitor->vol_up_table[0].ipeak; + monitor->vol_cfg.gain = monitor->vol_up_table[0].gain; + monitor->temp_cfg.ipeak = monitor->temp_up_table[0].ipeak; + monitor->temp_cfg.gain = monitor->temp_up_table[0].gain; + monitor->temp_cfg.vmax = monitor->temp_up_table[0].vmax; + +#ifdef AW_DEBUG + monitor->test_vol = 0; + monitor->test_temp = 0; +#endif +} + +/****************************************************** + * + * i2c driver + * + ******************************************************/ +static int aw882xx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct snd_soc_dai_driver *dai; + struct aw882xx *aw882xx; + struct device_node *np = i2c->dev.of_node; + int irq_flags = 0; + int ret = -1; + int i; + + pr_info("%s: enter\n", __func__); + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed\n"); + return -EIO; + } + + aw882xx = devm_kzalloc(&i2c->dev, sizeof(struct aw882xx), GFP_KERNEL); + if (aw882xx == NULL) + return -ENOMEM; + + aw882xx->dev = &i2c->dev; + aw882xx->i2c = i2c; + + i2c_set_clientdata(i2c, aw882xx); + mutex_init(&aw882xx->lock); + + /* aw882xx rst & int */ + if (np) { + ret = aw882xx_parse_dt(&i2c->dev, aw882xx, np); + if (ret) { + dev_err(&i2c->dev, + "%s: failed to parse device tree node\n", + __func__); + goto err_parse_dt; + } + } else { + aw882xx->reset_gpio = -1; + aw882xx->irq_gpio = -1; + } + + if (gpio_is_valid(aw882xx->reset_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw882xx->reset_gpio, + GPIOF_OUT_INIT_LOW, "aw882xx_rst"); + if (ret) { + dev_err(&i2c->dev, "%s: rst request failed\n", + __func__); + goto err_reset_gpio_request; + } + } + + if (gpio_is_valid(aw882xx->irq_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw882xx->irq_gpio, + GPIOF_DIR_IN, "aw882xx_int"); + if (ret) { + dev_err(&i2c->dev, "%s: int request failed\n", + __func__); + goto err_irq_gpio_request; + } + } + + /* hardware reset */ + aw882xx_hw_reset(aw882xx); + + /* aw882xx chip id */ + ret = aw882xx_read_chipid(aw882xx); + if (ret < 0) { + dev_err(&i2c->dev, "%s: aw882xx_read_chipid failed ret=%d\n", + __func__, ret); + goto err_id; + } + + /* aw882xx device name */ + //if (i2c->dev.of_node) + // dev_set_name(&i2c->dev, "%s", "aw882xx_smartpa"); + //else + // dev_err(&i2c->dev, "%s failed to set device name: %d\n", + // __func__, ret); + + /* register codec */ + dai = devm_kzalloc(&i2c->dev, sizeof(aw882xx_dai), GFP_KERNEL); + if (!dai) + goto err_dai_kzalloc; + + memcpy(dai, aw882xx_dai, sizeof(aw882xx_dai)); + pr_info("%s: dai->name(%s)\n", __func__, dai->name); + + ret = snd_soc_register_component(&i2c->dev, &soc_component_dev_aw882xx, + dai, ARRAY_SIZE(aw882xx_dai)); + if (ret < 0) { + dev_err(&i2c->dev, "%s failed to register aw882xx: %d\n", + __func__, ret); + goto err_register_codec; + } + + /* aw882xx irq */ + if (gpio_is_valid(aw882xx->irq_gpio) && + !(aw882xx->flags & AW882XX_FLAG_SKIP_INTERRUPTS)) { + aw882xx_interrupt_setup(aw882xx); + /* register irq handler */ + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&i2c->dev, + gpio_to_irq(aw882xx->irq_gpio), + NULL, aw882xx_irq, irq_flags, + "aw882xx", aw882xx); + if (ret != 0) { + dev_err(&i2c->dev, "failed to request IRQ %d: %d\n", + gpio_to_irq(aw882xx->irq_gpio), ret); + goto err_irq; + } + } else { + dev_info(&i2c->dev, "%s skipping IRQ registration\n", __func__); + /* disable feature support if gpio was invalid */ + aw882xx->flags |= AW882XX_FLAG_SKIP_INTERRUPTS; + } + + dev_set_drvdata(&i2c->dev, aw882xx); + ret = sysfs_create_group(&i2c->dev.kobj, &aw882xx_attribute_group); + if (ret < 0) { + dev_info(&i2c->dev, + "%s error creating sysfs attr files\n", + __func__); + goto err_sysfs; + } + + init_aw882xx_monitor(&aw882xx->monitor); + + init_aw882xx_misc_driver(aw882xx); + g_aw882xx = aw882xx; + + aw882xx->default_re = 0; + aw882xx->need_fade = 0; + /*init profile*/ + mutex_init(&aw882xx->profile.lock); + aw882xx->profile.cur_profile = 0; + aw882xx->profile.status = AW882XX_INIT_ST; + aw882xx->profile.len = 0; + for (i = 0; i < AW_PROFILE_MAX; i++) { + aw882xx->profile.data[i] = NULL; + } + + /*global init*/ + atomic_set(&g_algo_rx_enable, 0); + atomic_set(&g_algo_tx_enable, 0); + atomic_set(&g_skt_disable, 0); +#ifdef AW882XX_RUNIN_TEST + atomic_set(&g_runin_test, 0); + INIT_DELAYED_WORK(&aw882xx->adsp_status, aw882xx_set_adsp_module_status); +#endif + pr_info("%s: probe completed successfully!\n", __func__); + + return 0; + +err_sysfs: + devm_free_irq(&i2c->dev, gpio_to_irq(aw882xx->irq_gpio), aw882xx); +err_irq: + snd_soc_unregister_component(&i2c->dev); +err_register_codec: + devm_kfree(&i2c->dev, dai); + dai = NULL; +err_dai_kzalloc: +err_id: + if (gpio_is_valid(aw882xx->irq_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->irq_gpio); +err_irq_gpio_request: + if (gpio_is_valid(aw882xx->reset_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->reset_gpio); +err_reset_gpio_request: +err_parse_dt: + devm_kfree(&i2c->dev, aw882xx); + aw882xx = NULL; + g_aw882xx = NULL; + return ret; +} + +static int aw882xx_i2c_remove(struct i2c_client *i2c) +{ + struct aw882xx *aw882xx = i2c_get_clientdata(i2c); + + pr_info("%s: enter\n", __func__); + misc_deregister(&aw882xx_misc); + if (gpio_to_irq(aw882xx->irq_gpio)) + devm_free_irq(&i2c->dev, + gpio_to_irq(aw882xx->irq_gpio), + aw882xx); + + snd_soc_unregister_component(&i2c->dev); + + if (gpio_is_valid(aw882xx->irq_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->irq_gpio); + if (gpio_is_valid(aw882xx->reset_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->reset_gpio); + + devm_kfree(&i2c->dev, aw882xx); + aw882xx = NULL; + g_aw882xx = NULL; + return 0; +} + +static const struct i2c_device_id aw882xx_i2c_id[] = { + { AW882XX_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw882xx_i2c_id); + +static struct of_device_id aw882xx_dt_match[] = { + { .compatible = "awinic,aw882xx_smartpa" }, + { }, +}; + +static struct i2c_driver aw882xx_i2c_driver = { + .driver = { + .name = AW882XX_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw882xx_dt_match), + }, + .probe = aw882xx_i2c_probe, + .remove = aw882xx_i2c_remove, + .id_table = aw882xx_i2c_id, +}; + + +static int __init aw882xx_i2c_init(void) +{ + int ret = -1; + + pr_info("%s: aw882xx driver version %s\n", __func__, AW882XX_VERSION); + + ret = i2c_add_driver(&aw882xx_i2c_driver); + if (ret) + pr_err("%s: fail to add aw882xx device into i2c\n", __func__); + + return ret; +} +module_init(aw882xx_i2c_init); + + +static void __exit aw882xx_i2c_exit(void) +{ + i2c_del_driver(&aw882xx_i2c_driver); +} +module_exit(aw882xx_i2c_exit); + +MODULE_DESCRIPTION("ASoC AW882XX Smart PA Driver"); +MODULE_LICENSE("GPL v2"); + +#endif /* CONFIG_SND_SMARTPA_AW882XX */ diff --git a/asoc/codecs/awinic/aw882xx.h b/asoc/codecs/awinic/aw882xx.h new file mode 100644 index 000000000000..82c8decfe390 --- /dev/null +++ b/asoc/codecs/awinic/aw882xx.h @@ -0,0 +1,277 @@ +#ifndef _AW882XX_H_ +#define _AW882XX_H_ + +//#define AW_DEBUG +/* + * i2c transaction on Linux limited to 64k + * (See Linux kernel documentation: Documentation/i2c/writing-clients) +*/ +#define MAX_I2C_BUFFER_SIZE 65536 +#define AW882XX_I2C_READ_MSG_NUM 2 + +#define AW_VOL_COUNT (3) +#define AW882XX_FLAG_START_ON_MUTE (1 << 0) +#define AW882XX_FLAG_SKIP_INTERRUPTS (1 << 1) +#define AW882XX_FLAG_SAAM_AVAILABLE (1 << 2) +#define AW882XX_FLAG_STEREO_DEVICE (1 << 3) +#define AW882XX_FLAG_MULTI_MIC_INPUTS (1 << 4) + +#define AW882XX_NUM_RATES 9 +#define AW882XX_SYSST_CHECK_MAX 10 + +#define AW882XX_MODE_SHIFT_MAX 2 + +#define AW882XX_DEFAULT_AFE_RX_PROT_ID 0x1000 /*AFE_PORT_ID_PRIMARY_MI2S_RX*/ +#define AW882XX_DEFAULT_AFE_TX_PROT_ID 0x1001 /*AFE_PORT_ID_PRIMARY_MI2S_TX*/ +#define AW_MODULE_ID_COPP (0X10013D02) /*SKT module id*/ +#define AW_MODULE_PARAMS_ID_COPP_ENABLE (0X10013D14) /*SKT enable param id*/ + +#define DEFAULT_CALI_VALUE (7) +#define ERRO_CALI_VALUE (0) +#define AFE_PARAM_ID_AWDSP_RX_SET_ENABLE (0x10013D11) +#define AFE_PARAM_ID_AWDSP_RX_PARAMS (0x10013D12) +#define AFE_PARAM_ID_AWDSP_TX_SET_ENABLE (0x10013D13) +#define AFE_PARAM_ID_AWDSP_RX_VMAX_L (0X10013D17) +#define AFE_PARAM_ID_AWDSP_RX_VMAX_R (0X10013D18) +#define AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L (0X10013D19) +#define AFE_PARAM_ID_AWDSP_RX_CALI_CFG_R (0x10013d1A) +#define AFE_PARAM_ID_AWDSP_RX_RE_L (0x10013d1B) +#define AFE_PARAM_ID_AWDSP_RX_RE_R (0X10013D1C) +#define AFE_PARAM_ID_AWDSP_RX_NOISE_L (0X10013D1D) +#define AFE_PARAM_ID_AWDSP_RX_NOISE_R (0X10013D1E) +#define AFE_PARAM_ID_AWDSP_RX_F0_L (0X10013D1F) +#define AFE_PARAM_ID_AWDSP_RX_F0_R (0X10013D20) +#define AFE_PARAM_ID_AWDSP_RX_REAL_DATA_L (0X10013D21) +#define AFE_PARAM_ID_AWDSP_RX_REAL_DATA_R (0X10013D22) + +enum { + AW_FIRST_ENTRY = 0, + AW_NOT_FIRST_ENTRY = 1, +}; + +enum AWINIC_CALI_CMD{ + AW_CALI_CMD_NONE = 0, + AW_CALI_CMD_START, + AW_CALI_CMD_END, + AW_CALI_CMD_SET_CALI_CFG, + AW_CALI_CMD_GET_CALI_CFG, + AW_CALI_CMD_GET_CALI_DATA, + AW_CALI_CMD_SET_NOISE, + AW_CALI_CMD_GET_F0, + AW_CALI_CMD_SET_CALI_RE, + AW_CALI_CMD_GET_CALI_RE, + AW_CALI_CMD_SET_VMAX, + AW_CALI_CMD_GET_VMAX, + AW_CALI_CMD_MAX, +}; + +#define AW882XX_CALI_CFG_NUM 3 +#define AW882XX_CALI_DATA_NUM 6 +#define AW882XX_PARAMS_NUM 400 +struct cali_cfg{ + int32_t data[AW882XX_CALI_CFG_NUM]; +}; +struct cali_data{ + int32_t data[AW882XX_CALI_DATA_NUM]; +}; +struct params_data{ + int32_t data[AW882XX_PARAMS_NUM]; +}; +struct ptr_params_data { + int len; + int32_t *data; +}; + +#define AW882XX_IOCTL_MAGIC 'a' +#define AW882XX_IOCTL_SET_CALI_CFG _IOWR(AW882XX_IOCTL_MAGIC, 1, struct cali_cfg) +#define AW882XX_IOCTL_GET_CALI_CFG _IOWR(AW882XX_IOCTL_MAGIC, 2, struct cali_cfg) +#define AW882XX_IOCTL_GET_CALI_DATA _IOWR(AW882XX_IOCTL_MAGIC, 3, struct cali_data) +#define AW882XX_IOCTL_SET_NOISE _IOWR(AW882XX_IOCTL_MAGIC, 4, int32_t) +#define AW882XX_IOCTL_GET_F0 _IOWR(AW882XX_IOCTL_MAGIC, 5, int32_t) +#define AW882XX_IOCTL_SET_CALI_RE _IOWR(AW882XX_IOCTL_MAGIC, 6, int32_t) +#define AW882XX_IOCTL_GET_CALI_RE _IOWR(AW882XX_IOCTL_MAGIC, 7, int32_t) +#define AW882XX_IOCTL_SET_VMAX _IOWR(AW882XX_IOCTL_MAGIC, 8, int32_t) +#define AW882XX_IOCTL_GET_VMAX _IOWR(AW882XX_IOCTL_MAGIC, 9, int32_t) +#define AW882XX_IOCTL_SET_PARAM _IOWR(AW882XX_IOCTL_MAGIC, 10,struct params_data) +#define AW882XX_IOCTL_ENABLE_CALI _IOWR(AW882XX_IOCTL_MAGIC, 11,int8_t) +#define AW882XX_IOCTL_SET_PTR_PARAM_NUM _IOWR(AW882XX_IOCTL_MAGIC, 12, struct ptr_params_data) + +enum aw882xx_init { + AW882XX_INIT_ST = 0, + AW882XX_INIT_OK = 1, + AW882XX_INIT_NG = 2, +}; + +enum aw882xx_power_status { + AW882XX_POWER_DOWN = 0, + AW882XX_POWER_ING = 1, + AW882XX_POWER_UP = 2, +}; + +enum aw882xx_chipid { + AW882XX_ID = 0x1852, +}; + +enum aw882xx_modeshift { + AW882XX_MODE_SPK_SHIFT = 0, + AW882XX_MODE_RCV_SHIFT = 1, +}; + +enum aw882xx_mode_spk_rcv { + AW882XX_SPEAKER_MODE = 0, + AW882XX_RECEIVER_MODE = 1, +}; +//smartpa monitor +enum aw882xx_ipeak { + IPEAK_3P50_A = 0x08, + IPEAK_3P00_A = 0x06, + IPEAK_2P75_A = 0x05, + IPEAK_2P50_A = 0x04, + IPEAK_NONE = 0xFF, +}; + +enum aw882xx_gain { + GAIN_NEG_0P0_DB = 0x00, + GAIN_NEG_0P5_DB = 0x01, + GAIN_NEG_1P0_DB = 0x02, + GAIN_NEG_1P5_DB = 0x03, + GAIN_NEG_3P0_DB = 0x06, + GAIN_NEG_4P5_DB = 0x09, + GAIN_NEG_6P0_DB = 0x10, + GAIN_NONE = 0xFF, +}; + +enum aw882xx_vmax_percentage { + VMAX_100_PERCENTAGE = 0x00000000, + VMAX_086_PERCENTAGE = 0xFFED714D, + VMAX_075_PERCENTAGE = 0xFFD80505, + VMAX_063_PERCENTAGE = 0xFFBEAE7E, + VMAX_NONE = 0xFFFFFFFF, +}; + +#define AW882XX_MONITOR_DEFAULT_FLAG 0 +#define AW882XX_MONITOR_DEFAULT_TIMER_VAL 30000 +#define AW882XX_MONITOR_VBAT_RANGE 6025 +#define AW882XX_MONITOR_INT_10BIT 1023 +#define AW882XX_MONITOR_TEMP_SIGN_MASK (1<<9) +#define AW882XX_MONITOR_TEMP_NEG_MASK (0XFC00) +#define AW882XX_BIT_SYSCTRL2_BST_IPEAK_MASK ( 15<< 0) +#define AW882XX_BIT_HAGCCFG4_GAIN_SHIFT (8) +#define AW882XX_BIT_HAGCCFG4_GAIN_MASK (0x00ff) + +struct aw882xx_low_vol { + uint32_t vol; + uint8_t ipeak; + uint8_t gain; +}; + +struct aw882xx_low_temp { + int16_t temp; + uint8_t ipeak; + uint8_t gain; + uint32_t vmax; +}; + +struct aw882xx_monitor{ + struct hrtimer timer; + uint32_t timer_val; + struct work_struct work; + uint32_t is_enable; + uint16_t pre_vol; + uint16_t vol_count; + uint8_t first_entry; + + struct aw882xx_low_vol vol_cfg; + struct aw882xx_low_temp temp_cfg; + int vol_up_num; + int vol_down_num; + int temp_up_num; + int temp_down_num; + struct aw882xx_low_vol *vol_up_table; + struct aw882xx_low_vol *vol_down_table; + struct aw882xx_low_temp *temp_up_table; + struct aw882xx_low_temp *temp_down_table; + +#ifdef AW_DEBUG + uint16_t test_vol; + int16_t test_temp; +#endif +}; + +/*control runin test function*/ +#define AW882XX_RUNIN_TEST +enum AWINIC_PROFILE{ + AW_PROFILE_MUSIC = 0, + AW_PROFILE_RINGTONE, + AW_PROFILE_NOTIFICATION, + AW_PROFILE_VOICE, + AW_PROFILE_MAX, +}; + +#define VERSION_MAX 4 +#define PROJECT_NAME_MAX 24 +#define VOLUME_STEP_DB (6) +#define VOLUME_MIN_NEG_90_DB (90) +#define FADE_STEP_DB (6) + +typedef struct awinic_afe_param_header{ + uint8_t fw[VERSION_MAX]; + uint8_t cfg[VERSION_MAX]; + uint8_t project[PROJECT_NAME_MAX]; + uint32_t start; + uint32_t params_len; + uint8_t check_sum; + uint8_t profile_num; + uint8_t reserve[2]; +}aw_afe_params_hdr_t; + +struct profile_info { + struct mutex lock; + int cur_profile; + int status; + int len; + char* data[AW_PROFILE_MAX]; +}; + +struct aw882xx { + struct regmap *regmap; + struct i2c_client *i2c; + struct snd_soc_component *component; + struct device *dev; + struct mutex lock; + + struct aw882xx_monitor monitor; +#ifdef AW882XX_RUNIN_TEST + struct delayed_work adsp_status; +#endif + int sysclk; + int rate; + int pstream; + int cstream; + + int reset_gpio; + int irq_gpio; + int power_flag; + + unsigned char reg_addr; + + unsigned int flags; + unsigned int chipid; + unsigned int init; + unsigned int spk_rcv_mode; + uint32_t cali_re; + uint32_t default_re; + uint8_t cur_gain; + uint8_t need_fade; + unsigned int cfg_num; + unsigned int afe_rx_portid; + unsigned int afe_tx_portid; + unsigned int afe_profile; + struct profile_info profile; +}; + +struct aw882xx_container { + int len; + unsigned char data[]; +}; +#endif diff --git a/asoc/codecs/awinic/aw882xx_reg.h b/asoc/codecs/awinic/aw882xx_reg.h new file mode 100644 index 000000000000..4dd70dbd6246 --- /dev/null +++ b/asoc/codecs/awinic/aw882xx_reg.h @@ -0,0 +1,1570 @@ +#ifndef __AW882XX_REG_H__ +#define __AW882XX_REG_H__ + +/* registers list */ +#define AW882XX_ID_REG (0x00) +#define AW882XX_SYSST_REG (0x01) +#define AW882XX_SYSINT_REG (0x02) +#define AW882XX_SYSINTM_REG (0x03) +#define AW882XX_SYSCTRL_REG (0x04) +#define AW882XX_SYSCTRL2_REG (0x05) +#define AW882XX_I2SCTRL_REG (0x06) +#define AW882XX_I2SCFG1_REG (0x07) +#define AW882XX_I2SCFG2_REG (0x08) +#define AW882XX_HAGCCFG1_REG (0x09) +#define AW882XX_HAGCCFG2_REG (0x0A) +#define AW882XX_HAGCCFG3_REG (0x0B) +#define AW882XX_HAGCCFG4_REG (0x0C) +#define AW882XX_HAGCCFG5_REG (0x0D) +#define AW882XX_HAGCCFG6_REG (0x0E) +#define AW882XX_HAGCCFG7_REG (0x0F) +#define AW882XX_HAGCST_REG (0x10) +#define AW882XX_PRODID_REG (0x11) +#define AW882XX_VBAT_REG (0x12) +#define AW882XX_TEMP_REG (0x13) +#define AW882XX_PVDD_REG (0x14) +#define AW882XX_DBGCTRL_REG (0x20) +#define AW882XX_I2SINT_REG (0x21) +#define AW882XX_I2SCAPCNT_REG (0x22) +#define AW882XX_CRCIN_REG (0x38) +#define AW882XX_CRCOUT_REG (0x39) +#define AW882XX_VSNCTRL1_REG (0x50) +#define AW882XX_ISNCTRL1_REG (0x52) +#define AW882XX_ISNCTRL2_REG (0x53) +#define AW882XX_VTMCTRL1_REG (0x54) +#define AW882XX_VTMCTRL2_REG (0x55) +#define AW882XX_VTMCTRL3_REG (0x56) +#define AW882XX_ISNDAT_REG (0x57) +#define AW882XX_VSNDAT_REG (0x58) +#define AW882XX_PWMCTRL_REG (0x59) +#define AW882XX_PWMCTRL2_REG (0x5A) +#define AW882XX_BSTCTRL1_REG (0x60) +#define AW882XX_BSTCTRL2_REG (0x61) +#define AW882XX_BSTCTRL3_REG (0x62) +#define AW882XX_BSTDBG1_REG (0x63) +#define AW882XX_BSTDBG2_REG (0x64) +#define AW882XX_BSTDBG3_REG (0x65) +#define AW882XX_PLLCTRL1_REG (0x66) +#define AW882XX_PLLCTRL2_REG (0x67) +#define AW882XX_PLLCTRL3_REG (0x68) +#define AW882XX_CDACTRL1_REG (0x69) +#define AW882XX_CDACTRL2_REG (0x6A) +#define AW882XX_SADCCTRL_REG (0x6B) +#define AW882XX_TESTCTRL1_REG (0x70) +#define AW882XX_TESTCTRL2_REG (0x71) +#define AW882XX_EFCTRL1_REG (0x72) +#define AW882XX_EFCTRL2_REG (0x73) +#define AW882XX_EFWH_REG (0x74) +#define AW882XX_EFWM2_REG (0x75) +#define AW882XX_EFWM1_REG (0x76) +#define AW882XX_EFWL_REG (0x77) +#define AW882XX_EFRH_REG (0x78) +#define AW882XX_EFRM2_REG (0x79) +#define AW882XX_EFRM1_REG (0x7A) +#define AW882XX_EFRL_REG (0x7B) +#define AW882XX_TESTDET_REG (0x7C) + +/******************************************** + * Register Access + *******************************************/ +#define AW882XX_REG_MAX (0x7D) + +#define REG_NONE_ACCESS (0) +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) + +const unsigned char aw882xx_reg_access[AW882XX_REG_MAX] = { + [AW882XX_ID_REG] = (REG_RD_ACCESS), + [AW882XX_SYSST_REG] = (REG_RD_ACCESS), + [AW882XX_SYSINT_REG] = (REG_RD_ACCESS), + [AW882XX_SYSINTM_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_SYSCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_SYSCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_I2SCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_I2SCFG1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_I2SCFG2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG4_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG5_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG6_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG7_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCST_REG] = (REG_RD_ACCESS), + [AW882XX_PRODID_REG] = (REG_RD_ACCESS), + [AW882XX_VBAT_REG] = (REG_RD_ACCESS), + [AW882XX_TEMP_REG] = (REG_RD_ACCESS), + [AW882XX_PVDD_REG] = (REG_RD_ACCESS), + [AW882XX_DBGCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_I2SINT_REG] = (REG_RD_ACCESS), + [AW882XX_I2SCAPCNT_REG] = (REG_RD_ACCESS), + [AW882XX_CRCIN_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_CRCOUT_REG] = (REG_RD_ACCESS), + [AW882XX_VSNCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_ISNCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_ISNCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_VTMCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_VTMCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_VTMCTRL3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_ISNDAT_REG] = (REG_RD_ACCESS), + [AW882XX_VSNDAT_REG] = (REG_RD_ACCESS), + [AW882XX_PWMCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_PWMCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTCTRL3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTDBG1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTDBG2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTDBG3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_PLLCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_PLLCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_PLLCTRL3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_CDACTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_CDACTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_SADCCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_TESTCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_TESTCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFWH_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFWM2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFWM1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFWL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFRH_REG] = (REG_RD_ACCESS), + [AW882XX_EFRM2_REG] = (REG_RD_ACCESS), + [AW882XX_EFRM1_REG] = (REG_RD_ACCESS), + [AW882XX_EFRL_REG] = (REG_RD_ACCESS), + [AW882XX_TESTDET_REG] = (REG_RD_ACCESS), +}; + +/******************************************** + * Register Detail + *******************************************/ +/* detail information of registers begin */ +/* ID (0x00) detail */ +/* IDCODE bit 15:0 (ID 0x00) */ +#define AW882XX_IDCODE_START_BIT (0) +#define AW882XX_IDCODE_BITS_LEN (16) +#define AW882XX_IDCODE_MASK \ + (~(((1<= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + if (enable < 0 || enable > 1) { + pr_err("%s: Invalid value for enable %d\n", __func__, enable); + return -EINVAL; + } + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = module_id; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = param_id; + param_hdr.param_size = sizeof(enable_param); + enable_param = enable; + + rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (uint8_t *) &enable_param); + + if (rc) + pr_err("%s: Failed to set enable of module(%d) instance(%d) to %d, err %d\n", + __func__, module_id, INSTANCE_ID_0, enable, rc); + return rc; +} +EXPORT_SYMBOL(aw_adm_param_enable); +#endif + /** * adm_param_enable - * command to send params to ADM for given module diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 5a2a7d464ed2..7bee21008db5 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -43,6 +43,16 @@ enum { static struct afe_avcs_payload_port_mapping *pm[MAX_ALLOWED_USE_CASES]; +#ifdef CONFIG_AW882XX_DSP +#define AFE_MODULE_ID_AWDSP_TX (0x10013D00) +#define AFE_MODULE_ID_AWDSP_RX (0x10013D01) +#define AFE_PARAM_ID_AWDSP_RX_SET_ENABLE (0x10013D11) +#define AFE_PARAM_ID_AWDSP_TX_SET_ENABLE (0x10013D13) +#define AFE_PARAM_ID_AWDSP_RX_PARAMS (0x10013D12) +//#define AFE_PORT_ID_AWDSP_RX (AFE_PORT_ID_PRIMARY_MI2S_RX) +//#define AFE_PORT_ID_AWDSP_TX (AFE_PORT_ID_PRIMARY_MI2S_TX) +#endif + enum { AFE_COMMON_RX_CAL = 0, AFE_COMMON_TX_CAL, @@ -235,6 +245,10 @@ struct afe_ctl { struct mutex afe_apr_lock; struct mutex afe_clk_lock; int set_custom_topology; +#ifdef CONFIG_AW882XX_DSP + struct rtac_cal_block_data aw_cal; + atomic_t aw_state; +#endif int dev_acdb_id[AFE_MAX_PORTS]; routing_cb rt_cb; struct audio_uevent_data *uevent_data; @@ -1000,6 +1014,20 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) return -EINVAL; } +#ifdef CONFIG_AW882XX_DSP + if (atomic_read(&this_afe.aw_state) == 1) { + if (!payload[0]) { + atomic_set(&this_afe.state, 0); + } else { + pr_debug("%s: status: %d", __func__, payload[0]); + atomic_set(&this_afe.state, -1); + } + atomic_set(&this_afe.aw_state, 0); + wake_up(&this_afe.wait[data->token]); + return 0; + } +#endif + if (rtac_make_afe_callback(data->payload, data->payload_size)) return 0; @@ -2381,6 +2409,16 @@ static int afe_spk_prot_prepare(int src_port, int dst_port, int param_id, case AFE_PARAM_ID_SP_V4_EX_VI_FTM_CFG: param_info.module_id = AFE_MODULE_SPEAKER_PROTECTION_V4_VI; break; +#ifdef CONFIG_AW882XX_DSP + case AFE_PARAM_ID_AWDSP_RX_SET_ENABLE: + case AFE_PARAM_ID_AWDSP_RX_PARAMS: + param_info.module_id = AFE_MODULE_ID_AWDSP_RX; + break; + case AFE_PARAM_ID_AWDSP_TX_SET_ENABLE: + param_info.module_id = AFE_MODULE_ID_AWDSP_TX; + break; +#endif + default: pr_err("%s: default case 0x%x\n", __func__, param_id); goto fail_cmd; @@ -3664,6 +3702,181 @@ done: return ret; } +#ifdef CONFIG_AW882XX_DSP +int aw_send_afe_rx_module_enable(uint32_t rx_port_id, void *buf, int size) +{ + union afe_spkr_prot_config config; + + if (size > sizeof(config)) + return -EINVAL; + + memcpy(&config, buf, size); + + if (afe_spk_prot_prepare(rx_port_id, 0, + AFE_PARAM_ID_AWDSP_RX_SET_ENABLE, &config, + sizeof(union afe_spkr_prot_config))) { + pr_err("%s: set bypass failed \n", __func__); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL(aw_send_afe_rx_module_enable); + +int aw_send_afe_tx_module_enable(uint32_t tx_port_id, void *buf, int size) +{ + union afe_spkr_prot_config config; + + if (size > sizeof(config)) + return -EINVAL; + + memcpy(&config, buf, size); + + if (afe_spk_prot_prepare(tx_port_id, 0, + AFE_PARAM_ID_AWDSP_TX_SET_ENABLE, &config, + sizeof(union afe_spkr_prot_config))) { + pr_err("%s: set bypass failed \n", __func__); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL(aw_send_afe_tx_module_enable); + +int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id, + uint32_t param_id, void *buf, int cmd_size, bool write) +{ + int32_t result = 0; + uint32_t port_id = 0; + int32_t module_id = AFE_MODULE_ID_AWDSP_RX; + uint32_t port_index = 0; + uint32_t payload_size = 0; + size_t len; + struct rtac_cal_block_data *aw_cal = &(this_afe.aw_cal); + struct mem_mapping_hdr mem_hdr; + struct param_hdr_v3 param_hdr; + + pr_debug("%s: enter\n", __func__); + port_id = rx_port_id; + if (param_id == AFE_PARAM_ID_AWDSP_TX_SET_ENABLE) { + port_id = tx_port_id; + module_id = AFE_MODULE_ID_AWDSP_TX; + } + + if (aw_cal->map_data.dma_buf == 0) { + /*Minimal chunk size is 4K*/ + aw_cal->map_data.map_size = SZ_4K; + result = msm_audio_ion_alloc(&(aw_cal->map_data.dma_buf), + aw_cal->map_data.map_size, + &(aw_cal->cal_data.paddr),&len, + &(aw_cal->cal_data.kvaddr)); + if (result < 0) { + pr_err("%s: allocate buffer failed! ret = %d\n", + __func__, result); + goto err; + } + } + + if (aw_cal->map_data.map_handle == 0) { + result = afe_map_rtac_block(aw_cal); + if (result < 0) { + pr_err("%s: map buffer failed! ret = %d\n", + __func__, result); + goto err; + } + } + + port_index = q6audio_get_port_index(port_id); + if (port_index >= AFE_MAX_PORTS) { + pr_err("%s: Invalid AFE port = 0x%x\n", __func__, port_id); + goto err; + } + + if (cmd_size > (SZ_4K - sizeof(struct param_hdr_v3))) { + pr_err("%s: Invalid payload size = %d\n", __func__, cmd_size); + result = -EINVAL; + goto err; + } + + /* Pack message header with data */ + param_hdr.module_id = module_id; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_size = cmd_size; + + if (write) { + param_hdr.param_id = param_id; + q6common_pack_pp_params(aw_cal->cal_data.kvaddr, + ¶m_hdr, + buf, + &payload_size); + aw_cal->cal_data.size = payload_size; + } else { + param_hdr.param_id = param_id; + aw_cal->cal_data.size = cmd_size + sizeof(struct param_hdr_v3); + } + + /*Send/Get package to/from ADSP*/ + mem_hdr.data_payload_addr_lsw = + lower_32_bits(aw_cal->cal_data.paddr); + mem_hdr.data_payload_addr_msw = + msm_audio_populate_upper_32_bits(aw_cal->cal_data.paddr); + mem_hdr.mem_map_handle = + aw_cal->map_data.map_handle; + + pr_debug("%s: Sending aw_cal port = 0x%x, cal size = %zd, cal addr = 0x%pK\n", + __func__, port_id, aw_cal->cal_data.size, &aw_cal->cal_data.paddr); + + result = afe_q6_interface_prepare(); + if (result != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, result); + goto err; + } + + if (write) { + if (q6common_is_instance_id_supported()) + result = q6afe_set_params_v3(port_id, port_index, &mem_hdr, NULL, payload_size); + else + result = q6afe_set_params_v2(port_id, port_index, &mem_hdr, NULL, payload_size); + } else { + int8_t *resp = (int8_t *)aw_cal->cal_data.kvaddr; + + atomic_set(&this_afe.aw_state, 1); + if (q6common_is_instance_id_supported()) { + result = q6afe_get_params_v3(port_id, port_index, &mem_hdr, ¶m_hdr); + resp += sizeof(struct param_hdr_v3); + } else { + result = q6afe_get_params_v2(port_id, port_index, &mem_hdr, ¶m_hdr); + resp += sizeof(struct param_hdr_v1); + } + + if (result) { + pr_err("%s: get response from port 0x%x failed %d\n", + __func__, port_id, result); + goto err; + } + else { + /*Copy response data to command buffer*/ + memcpy(buf, resp, cmd_size); + } + } +err: + return result; +} +EXPORT_SYMBOL(aw_send_afe_cal_apr); + +void aw_cal_unmap_memory(void) +{ + int result = 0; + + if (this_afe.aw_cal.map_data.map_handle) { + result = afe_unmap_rtac_block(&this_afe.aw_cal.map_data.map_handle); + + /*Force to remap after unmap failed*/ + if (result) + this_afe.aw_cal.map_data.map_handle = 0; + } +} +EXPORT_SYMBOL(aw_cal_unmap_memory); +#endif + static int afe_init_cdc_reg_config(void) { struct param_hdr_v3 param_hdr; @@ -11090,6 +11303,10 @@ void afe_exit(void) q6core_destroy_uevent_data(this_afe.uevent_data); +#ifdef CONFIG_AW882XX_DSP + aw_cal_unmap_memory(); +#endif + afe_delete_cal_data(); config_debug_fs_exit(); From 81c4cb937b97826182c472e01004679a0383a6fe Mon Sep 17 00:00:00 2001 From: yaozm1 Date: Wed, 27 May 2020 11:02:30 +0800 Subject: [PATCH 02/40] asoc:Enable necessary log for mbhc Propogate changes from (CR). For headset debug purpose; Enable necessary log. Change-Id: Ib34f66978a7659ca396ef6db4d0a8e5f18363bd4 Signed-off-by: yaozm1 Reviewed-on: https://gerrit.mot.com/1581555 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key (cherry picked from commit 08300c96a3e1454a6c8e2de9df5a3df69ecc1776) Reviewed-on: https://gerrit.mot.com/1744665 Reviewed-by: Yunfei Guo Reviewed-by: Wang Wang --- asoc/codecs/wcd-mbhc-v2.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/asoc/codecs/wcd-mbhc-v2.c b/asoc/codecs/wcd-mbhc-v2.c index ee24ea63a031..55cc0a37bd40 100644 --- a/asoc/codecs/wcd-mbhc-v2.c +++ b/asoc/codecs/wcd-mbhc-v2.c @@ -594,7 +594,7 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->hph_type = WCD_MBHC_HPH_NONE; mbhc->zl = mbhc->zr = 0; - pr_debug("%s: Reporting removal %d(%x)\n", __func__, + pr_info("%s: Reporting removal %d(%x)\n", __func__, jack_type, mbhc->hph_status); wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, mbhc->hph_status, WCD_MBHC_JACK_MASK); @@ -692,6 +692,8 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, &mbhc->zl, &mbhc->zr); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, fsm_en); + pr_info("%s,compute impedance,zl=%d,zr=%d\n", + __func__, mbhc->zl, mbhc->zr); if ((mbhc->zl > mbhc->mbhc_cfg->linein_th) && (mbhc->zr > mbhc->mbhc_cfg->linein_th) && (jack_type == SND_JACK_HEADPHONE)) { @@ -707,7 +709,7 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->hph_status, WCD_MBHC_JACK_MASK); } - pr_debug("%s: Marking jack type as SND_JACK_LINEOUT\n", + pr_info("%s: Marking jack type as SND_JACK_LINEOUT\n", __func__); } } @@ -737,7 +739,7 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->mbhc_cb->mbhc_micb_ramp_control) mbhc->mbhc_cb->mbhc_micb_ramp_control(component, false); - pr_debug("%s: Reporting insertion %d(%x)\n", __func__, + pr_info("%s: Reporting insertion %d(%x)\n", __func__, jack_type, mbhc->hph_status); wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, (mbhc->hph_status | SND_JACK_MECHANICAL), @@ -760,7 +762,7 @@ void wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc *mbhc) else pr_info("%s: hs_detect_plug work not cancelled\n", __func__); - pr_debug("%s: Report extension cable\n", __func__); + pr_info("%s: Report extension cable\n", __func__); wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT); /* * If PA is enabled HPHL schmitt trigger can @@ -1064,7 +1066,7 @@ static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data) int r = IRQ_HANDLED; struct wcd_mbhc *mbhc = data; - pr_debug("%s: enter\n", __func__); + pr_info("%s: enter\n", __func__); if (mbhc == NULL) { pr_err("%s: NULL irq data\n", __func__); return IRQ_NONE; @@ -1247,7 +1249,7 @@ static irqreturn_t wcd_mbhc_release_handler(int irq, void *data) if (mbhc->buttons_pressed & WCD_MBHC_JACK_BUTTON_MASK) { ret = wcd_cancel_btn_work(mbhc); if (ret == 0) { - pr_debug("%s: Reporting long button release event\n", + pr_info("%s: Reporting long button release event\n", __func__); wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, 0, mbhc->buttons_pressed); @@ -1256,8 +1258,8 @@ static irqreturn_t wcd_mbhc_release_handler(int irq, void *data) pr_debug("%s: Switch irq kicked in, ignore\n", __func__); } else { - pr_debug("%s: Reporting btn press\n", - __func__); + pr_info("%s: Reporting btn press %x\n", + __func__, mbhc->buttons_pressed); wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, mbhc->buttons_pressed, From b660be1d976b776ba69a317c080fa23e432b9f71 Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Tue, 28 Apr 2020 19:51:55 +0530 Subject: [PATCH 03/40] soc: soundwire: Update hw_ctl on HCLK at bootup Propogate changes from (CR). Merge qcom patch: case number: 04629828 soc: soundwire: Update hw_ctl on HCLK at bootup For soundwire version 1.6, HCLK remove HW controlled at bootup before soundwire frame generation status check. Change-Id: Idc2420d086261766ea4e79f69320f12c4d6b2259 Signed-off-by: Laxminath Kasam Reviewed-on: https://gerrit.mot.com/1592905 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Yunfei Guo Reviewed-by: Zhengming Yao Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key Reviewed-on: https://gerrit.mot.com/1745677 Reviewed-by: Wen Xie --- soc/swr-mstr-ctrl.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/soc/swr-mstr-ctrl.c b/soc/swr-mstr-ctrl.c index c536fe9f08e1..d536a7c82d22 100644 --- a/soc/swr-mstr-ctrl.c +++ b/soc/swr-mstr-ctrl.c @@ -2524,6 +2524,14 @@ static int swrm_master_init(struct swr_mstr_ctrl *swrm) swr_master_bulk_write(swrm, reg, value, len); + /* SW workaround to gate hw_ctl for SWR version >=1.6 */ + if (swrm->version >= SWRM_VERSION_1_6) { + if (swrm->swrm_hctl_reg) { + temp = ioread32(swrm->swrm_hctl_reg); + temp &= 0xFFFFFFFD; + iowrite32(temp, swrm->swrm_hctl_reg); + } + } if (!swrm_check_link_status(swrm, 0x1)) { dev_err(swrm->dev, "%s: swr link failed to connect\n", From 9a6f021a1cbddfd4992a66c5a808b42b23ff7c1c Mon Sep 17 00:00:00 2001 From: guoyf2 Date: Thu, 24 Sep 2020 15:33:36 +0800 Subject: [PATCH 04/40] update aw882xx driver to v0.1.16 - Modify the setting of gain initial value, set it according to temperature and voltage, and modify the setting of volume when fade in. - Problems solved: When the battery power is high and low-temperature and low-pressure protection is not triggered, the initial GAIN will be set to -6dB. After the low-temperature and low-pressure trigger, the sound will become louder. Change-Id: Ib3f4eddd3117f148d1b3e1242c36621882346574 Signed-off-by: guoyf2 Reviewed-on: https://gerrit.mot.com/1758919 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Yunfei Guo Reviewed-by: Wang Wang Submit-Approved: Jira Key --- asoc/codecs/awinic/aw882xx.c | 199 +++++++++++++++++++++++++++++++---- asoc/codecs/awinic/aw882xx.h | 4 + 2 files changed, 182 insertions(+), 21 deletions(-) diff --git a/asoc/codecs/awinic/aw882xx.c b/asoc/codecs/awinic/aw882xx.c index cd9d25ce6501..05999ae8954a 100644 --- a/asoc/codecs/awinic/aw882xx.c +++ b/asoc/codecs/awinic/aw882xx.c @@ -1,7 +1,7 @@ /* * aw882xx.c aw882xx codec module * - * Version: v0.1.13 + * Version: v0.1.16 * * keep same with AW882XX_VERSION * @@ -50,7 +50,7 @@ ******************************************************/ #define AW882XX_I2C_NAME "aw882xx_smartpa" -#define AW882XX_VERSION "v0.1.13" +#define AW882XX_VERSION "v0.1.16" #define AW882XX_RATES SNDRV_PCM_RATE_8000_48000 #define AW882XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ @@ -65,6 +65,7 @@ #define CALI_BUF_MAX 100 +#define DELAY_TIME_MAX 300 #define AWINIC_CALI_FILE "/mnt/vendor/persist/factory/audio/aw_cali.bin" #ifdef CONFIG_AW882XX_DSP @@ -100,7 +101,8 @@ static int aw882xx_set_cali_re(struct aw882xx *aw882xx, uint32_t cali_re); static int aw882xx_load_profile_params(struct aw882xx *aw882xx); static int aw882xx_skt_set_dsp(int value); static int aw882xx_send_profile_params_to_dsp(struct aw882xx *aw882xx, int profile_id, bool is_fade); -static void aw882xx_fade_in_out(struct aw882xx *aw882xx, bool is_fade_in); +static void aw882xx_fade_in_out(struct aw882xx *aw882xx); +static void aw882xx_volume_set(struct aw882xx *aw882xx, unsigned int value); /*monitor voltage and temperature table*/ static struct aw882xx_low_vol vol_down_table[] = { @@ -295,8 +297,12 @@ static void aw882xx_run_mute(struct aw882xx *aw882xx, bool mute) pr_debug("%s: enter\n", __func__); if (mute) { - if (aw882xx->need_fade) - aw882xx_fade_in_out(aw882xx, false); + if (aw882xx->need_fade) { + aw882xx->is_fade_in = 0; + aw882xx->fade_work_start = 1; + cancel_delayed_work_sync(&aw882xx->fade_work); + aw882xx_fade_in_out(aw882xx); + } aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL2_REG, AW882XX_HMUTE_MASK, AW882XX_HMUTE_ENABLE_VALUE); @@ -304,8 +310,13 @@ static void aw882xx_run_mute(struct aw882xx *aw882xx, bool mute) aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL2_REG, AW882XX_HMUTE_MASK, AW882XX_HMUTE_DISABLE_VALUE); - if (aw882xx->need_fade) - aw882xx_fade_in_out(aw882xx, true); + if (aw882xx->need_fade) { + aw882xx->is_fade_in = 1; + aw882xx->fade_work_start = 1; + aw882xx_volume_set(aw882xx, VOLUME_MIN_NEG_90_DB); + schedule_delayed_work(&aw882xx->fade_work, + msecs_to_jiffies(aw882xx->delayed_time)); + } } } @@ -661,7 +672,11 @@ static void aw882xx_reg_loaded(const struct firmware *cont, void *context) pr_err("%s: reg update fail\n", __func__); } else { pr_err("%s: reg update sucess\n", __func__); - aw882xx_run_mute(aw882xx, true); + //aw882xx_run_mute(aw882xx, true); + aw882xx_volume_set(aw882xx, VOLUME_MIN_NEG_90_DB); + aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL2_REG, + AW882XX_HMUTE_MASK, + AW882XX_HMUTE_ENABLE_VALUE); aw882xx_set_vcalb(aw882xx); } mutex_unlock(&aw882xx->lock); @@ -1070,25 +1085,30 @@ static void aw882xx_volume_set(struct aw882xx *aw882xx, unsigned int value) unsigned int reg_value = 0; unsigned int real_value = ((value / VOLUME_STEP_DB) << 4) + (value % VOLUME_STEP_DB) * 2; - if(real_value > aw882xx->cur_gain) { - /* cal real value */ - aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_value); + aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_value); + if(real_value >= aw882xx->cur_gain) { real_value = (real_value << 8) | (reg_value & 0x00ff); - /* write value */ - aw882xx_i2c_write(aw882xx, AW882XX_HAGCCFG4_REG, real_value); + } else { + real_value = (aw882xx->cur_gain << 8) | (reg_value & 0x00ff); } + /* write value */ + aw882xx_i2c_write(aw882xx, AW882XX_HAGCCFG4_REG, real_value); } -static void aw882xx_fade_in_out(struct aw882xx *aw882xx, bool is_fade_in) +static void aw882xx_fade_in_out(struct aw882xx *aw882xx) { int i = 0; + if(aw882xx == NULL) { + aw882xx->fade_work_start = 0; + return; + } //volume up - if (is_fade_in) { + if (aw882xx->is_fade_in) { i = VOLUME_MIN_NEG_90_DB; do { aw882xx_volume_set(aw882xx, i); - i -= FADE_STEP_DB; + i -= FADE_STEP_DB; //6 usleep_range(1400,1600); } while (i >= 0); } else { // volume down @@ -1098,6 +1118,24 @@ static void aw882xx_fade_in_out(struct aw882xx *aw882xx, bool is_fade_in) usleep_range(1400,1600); } while (i <= VOLUME_MIN_NEG_90_DB); } + aw882xx->fade_work_start = 0; +} + +static void aw882xx_fade_work_func(struct work_struct *work) +{ + struct delayed_work *d_work; + struct aw882xx *aw882xx; + + d_work = to_delayed_work(work); + aw882xx = container_of(d_work, struct aw882xx, fade_work); + if(aw882xx == NULL) { + pr_debug("%s: can not get aw882xx from fade_work\n", __func__); + return; + } + pr_debug("%s: enter in: %d\n", __func__, aw882xx->is_fade_in); + + aw882xx_fade_in_out(aw882xx); + pr_debug("%s: exit\n", __func__); } static int aw882xx_send_profile_params_to_dsp(struct aw882xx *aw882xx, int profile_id, bool is_fade) @@ -1256,6 +1294,34 @@ static int aw882xx_monitor_set(struct snd_kcontrol *kcontrol, return 0; } +static int aw882xx_fade_dealyed_time_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + + pr_debug("%s: aw882xx_fade_dealyed_time_get %d\n", + __func__, aw882xx->delayed_time); + + ucontrol->value.integer.value[0] = aw882xx->delayed_time; + + return 0; +} + +static int aw882xx_fade_dealyed_time_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + aw882xx->delayed_time = ucontrol->value.integer.value[0]; + + pr_debug("%s: aw882xx_fade_dealyed_time_set %d ms \n", __func__, aw882xx->delayed_time); + return 0; +} + + #ifdef AW882XX_RUNIN_TEST static int aw882xx_runin_test_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1330,6 +1396,8 @@ static struct snd_kcontrol_new aw882xx_controls[] = { aw882xx_skt_disable_get, aw882xx_skt_disable_set), SOC_ENUM_EXT("aw882xx_monitor_switch", aw882xx_snd_enum[1], aw882xx_monitor_get, aw882xx_monitor_set), + SOC_SINGLE_EXT("aw882xx_fade_dealyed_time", SND_SOC_NOPM, 0, DELAY_TIME_MAX, 0, + aw882xx_fade_dealyed_time_get, aw882xx_fade_dealyed_time_set), #ifdef AW882XX_RUNIN_TEST SOC_ENUM_EXT("aw882xx_runin_test", aw882xx_snd_enum[0], aw882xx_runin_test_get, aw882xx_runin_test_set), @@ -1390,8 +1458,8 @@ static int aw882xx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_I2S: if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { -// dev_err(component->dev, "%s: invalid codec master mode\n", -// __func__); + dev_err(component->dev, "%s: invalid codec master mode\n", + __func__); return -EINVAL; } break; @@ -2308,6 +2376,54 @@ static ssize_t aw882xx_default_re_show(struct device *dev, return len; } +static ssize_t aw882xx_spk_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + int ret = -1; + int16_t data_len = _IOC_SIZE(AW882XX_IOCTL_GET_CALI_DATA); + int32_t spk_temp = 0; + char *data_ptr = NULL; + unsigned int reg_value = 0; + + if(aw882xx == NULL) { + goto fail_temp; + } + data_ptr = kmalloc(data_len, GFP_KERNEL); + if (data_ptr == NULL) { + pr_err("%s : malloc failed !\n", __func__); + goto fail_temp; + } + + aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_value); + if (!(reg_value & AW882XX_PLLS_LOCKED_VALUE)) { + pr_err("%s: NO I2S CLK\n", __func__); + goto exit_temp; + } + + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, + aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_REAL_DATA_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error: %d\n", __func__, ret); + goto exit_temp; + } + + memcpy(&spk_temp, data_ptr + sizeof(int32_t), sizeof(int32_t)); + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx spk_temp: %d\n", spk_temp); + return len; + +exit_temp: + kfree(data_ptr); +fail_temp: + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx spk_temp: fail\n"); + return len; +} + #ifdef AW_DEBUG static ssize_t aw882xx_vol_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -2633,6 +2749,8 @@ static DEVICE_ATTR(mec, S_IWUSR | S_IRUGO, aw882xx_mec_show, aw882xx_mec_store); static DEVICE_ATTR(default_re, S_IWUSR | S_IRUGO, aw882xx_default_re_show, NULL); +static DEVICE_ATTR(spk_temp, S_IWUSR | S_IRUGO, + aw882xx_spk_temp_show, NULL); #ifdef AW_DEBUG static DEVICE_ATTR(vol, S_IWUSR | S_IRUGO, aw882xx_vol_show, aw882xx_vol_store); @@ -2647,6 +2765,7 @@ static struct attribute *aw882xx_attributes[] = { &dev_attr_spk_rcv.attr, &dev_attr_mec.attr, &dev_attr_default_re.attr, + &dev_attr_spk_temp.attr, #ifdef AW_DEBUG &dev_attr_vol.attr, &dev_attr_temp.attr, @@ -2998,7 +3117,11 @@ static void aw882xx_monitor_set_gain(struct aw882xx *aw882xx, uint8_t gain) unsigned int read_reg_val; int ret; - if (gain == GAIN_NONE) + if(aw882xx == NULL) { + return; + } + + if (gain == GAIN_NONE || (aw882xx->fade_work_start == 1)) return; ret = aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_val); @@ -3086,6 +3209,36 @@ static void aw882xx_monitor_work(struct aw882xx *aw882xx) aw882xx_monitor_set_vmax(aw882xx, set_cfg.vmax); } +static void aw882xx_monitor_init_gain(struct aw882xx *aw882xx) +{ + struct aw882xx_low_vol *vol_cfg = &aw882xx->monitor.vol_cfg; + struct aw882xx_low_temp *temp_cfg = &aw882xx->monitor.temp_cfg; + struct aw882xx_low_temp set_cfg; + int ret; + + if (aw882xx == NULL) { + pr_err("%s: pointer is NULL\n", __func__); + return; + } + + ret = aw882xx_monitor_voltage(aw882xx, vol_cfg); + if (ret < 0) { + pr_err("%s: monitor voltage failed\n", __func__); + return; + } + + ret = aw882xx_monitor_temperature(aw882xx, temp_cfg); + if (ret < 0) { + pr_err("%s: monitor temperature failed\n", __func__); + return; + } + + memcpy(&set_cfg, temp_cfg, sizeof(struct aw882xx_low_temp)); + + aw882xx_monitor_get_cfg(&set_cfg, vol_cfg); + aw882xx->cur_gain = set_cfg.gain; +} + static int aw882xx_get_hmute(struct aw882xx *aw882xx) { unsigned int reg_val = 0; @@ -3130,7 +3283,6 @@ void init_aw882xx_monitor(struct aw882xx_monitor *monitor) monitor->temp_cfg.ipeak = monitor->temp_up_table[0].ipeak; monitor->temp_cfg.gain = monitor->temp_up_table[0].gain; monitor->temp_cfg.vmax = monitor->temp_up_table[0].vmax; - #ifdef AW_DEBUG monitor->test_vol = 0; monitor->test_temp = 0; @@ -3268,12 +3420,16 @@ static int aw882xx_i2c_probe(struct i2c_client *i2c, } init_aw882xx_monitor(&aw882xx->monitor); - + aw882xx_monitor_init_gain(aw882xx); init_aw882xx_misc_driver(aw882xx); g_aw882xx = aw882xx; aw882xx->default_re = 0; aw882xx->need_fade = 0; + aw882xx->fade_work_start = 0; + aw882xx->delayed_time = 0; + aw882xx->is_fade_in = 0; + /*init profile*/ mutex_init(&aw882xx->profile.lock); aw882xx->profile.cur_profile = 0; @@ -3283,6 +3439,7 @@ static int aw882xx_i2c_probe(struct i2c_client *i2c, aw882xx->profile.data[i] = NULL; } + INIT_DELAYED_WORK(&aw882xx->fade_work, aw882xx_fade_work_func); /*global init*/ atomic_set(&g_algo_rx_enable, 0); atomic_set(&g_algo_tx_enable, 0); diff --git a/asoc/codecs/awinic/aw882xx.h b/asoc/codecs/awinic/aw882xx.h index 82c8decfe390..9e7da86833cb 100644 --- a/asoc/codecs/awinic/aw882xx.h +++ b/asoc/codecs/awinic/aw882xx.h @@ -244,6 +244,10 @@ struct aw882xx { #ifdef AW882XX_RUNIN_TEST struct delayed_work adsp_status; #endif + struct delayed_work fade_work; + int delayed_time; + int fade_work_start; + int is_fade_in; int sysclk; int rate; int pstream; From 39d96b8ecc6c55ed163127628a832caff1d61b25 Mon Sep 17 00:00:00 2001 From: guoyf2 Date: Mon, 26 Oct 2020 14:15:31 +0800 Subject: [PATCH 05/40] ASoC: update aw882xx driver to v0.1.17 Volume fade in/out feature doesn't depend on afe-profile, ues a new property fade-flag to control it. Change-Id: Ifc14f98538710c8fcf0ba669ad2daa81c59ca52f Signed-off-by: guoyf2 Reviewed-on: https://gerrit.mot.com/1784580 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Wen Xie Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/awinic/aw882xx.c | 18 +++++++++++++----- asoc/codecs/awinic/aw882xx.h | 1 + 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/asoc/codecs/awinic/aw882xx.c b/asoc/codecs/awinic/aw882xx.c index 05999ae8954a..bb269c1defd4 100644 --- a/asoc/codecs/awinic/aw882xx.c +++ b/asoc/codecs/awinic/aw882xx.c @@ -1,7 +1,7 @@ /* * aw882xx.c aw882xx codec module * - * Version: v0.1.16 + * Version: v0.1.17 * * keep same with AW882XX_VERSION * @@ -50,7 +50,7 @@ ******************************************************/ #define AW882XX_I2C_NAME "aw882xx_smartpa" -#define AW882XX_VERSION "v0.1.16" +#define AW882XX_VERSION "v0.1.17" #define AW882XX_RATES SNDRV_PCM_RATE_8000_48000 #define AW882XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ @@ -683,11 +683,10 @@ static void aw882xx_reg_loaded(const struct firmware *cont, void *context) kfree(aw882xx_cfg); if (aw882xx->afe_profile) { aw882xx_load_profile_params(aw882xx); - aw882xx->need_fade = 0; } + aw882xx->need_fade = 0; aw882xx_start(aw882xx); - - if (aw882xx->afe_profile) { + if(aw882xx->afe_profile || aw882xx->fade_flag) { aw882xx->need_fade = 1; } } @@ -1986,6 +1985,15 @@ static int aw882xx_parse_dt(struct device *dev, struct aw882xx *aw882xx, __func__, aw882xx->afe_profile); } + ret = of_property_read_u32(np, "fade-flag", &aw882xx->fade_flag); + if (ret) { + aw882xx->fade_flag = 0; + dev_err(dev, "%s: fade_flag get failed,use default value!\n", __func__); + } else { + dev_info(dev, "%s: fade_flag = %d\n", + __func__, aw882xx->fade_flag); + } + /*get low vol table cfg*/ ret = aw882xx_parse_low_vol_cfg(dev, aw882xx, np, "up", &monitor->vol_up_table, &monitor->vol_up_num); diff --git a/asoc/codecs/awinic/aw882xx.h b/asoc/codecs/awinic/aw882xx.h index 9e7da86833cb..f6a22aa936a8 100644 --- a/asoc/codecs/awinic/aw882xx.h +++ b/asoc/codecs/awinic/aw882xx.h @@ -271,6 +271,7 @@ struct aw882xx { unsigned int afe_rx_portid; unsigned int afe_tx_portid; unsigned int afe_profile; + unsigned int fade_flag; struct profile_info profile; }; From 793f3936a64ed44ae9cb838344aaad70246406ad Mon Sep 17 00:00:00 2001 From: yaozm1 Date: Tue, 10 Mar 2020 17:16:33 +0800 Subject: [PATCH 06/40] asoc: Add fs1818 support for guamp/guamna Propagation of the https://gerrit.mot.com/#/c/1510671/ in order to enable fs1818 as audio PA for guamp and guamna EVT sample devices. Change-Id: I772278e04a98bd4c95443e911d228d746797a263 Signed-off-by: yaozm1 Reviewed-on: https://gerrit.mot.com/1510671 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key Reviewed-on: https://gerrit.mot.com/1914385 Reviewed-by: Monica Mabuchi Reviewed-by: Nelson Guedes Paulo Junior Reviewed-by: Jian Luan --- asoc/bengal.c | 6 + asoc/codecs/Android.mk | 9 +- asoc/codecs/Kbuild | 11 + asoc/codecs/foursemi/Kconfig | 5 + asoc/codecs/foursemi/Makefile | 9 + asoc/codecs/foursemi/fs16xx.c | 1718 +++++++++++++++++++++ asoc/codecs/foursemi/fs16xx.h | 190 +++ asoc/codecs/foursemi/fs16xx_calibration.c | 734 +++++++++ asoc/codecs/foursemi/fs16xx_calibration.h | 128 ++ asoc/codecs/foursemi/fs16xx_define.h | 120 ++ asoc/codecs/foursemi/fs16xx_preset.c | 452 ++++++ asoc/codecs/foursemi/fs16xx_preset.h | 99 ++ asoc/codecs/foursemi/fs16xx_reg_def.h | 81 + asoc/codecs/foursemi/fs16xx_regs.h | 1210 +++++++++++++++ config/bengalauto.conf | 1 + config/bengalautoconf.h | 1 + 16 files changed, 4773 insertions(+), 1 deletion(-) create mode 100644 asoc/codecs/foursemi/Kconfig create mode 100644 asoc/codecs/foursemi/Makefile create mode 100644 asoc/codecs/foursemi/fs16xx.c create mode 100644 asoc/codecs/foursemi/fs16xx.h create mode 100644 asoc/codecs/foursemi/fs16xx_calibration.c create mode 100644 asoc/codecs/foursemi/fs16xx_calibration.h create mode 100644 asoc/codecs/foursemi/fs16xx_define.h create mode 100644 asoc/codecs/foursemi/fs16xx_preset.c create mode 100644 asoc/codecs/foursemi/fs16xx_preset.h create mode 100644 asoc/codecs/foursemi/fs16xx_reg_def.h create mode 100644 asoc/codecs/foursemi/fs16xx_regs.h diff --git a/asoc/bengal.c b/asoc/bengal.c index 5cfdca61cd55..99733bed120e 100644 --- a/asoc/bengal.c +++ b/asoc/bengal.c @@ -5441,8 +5441,13 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .stream_name = "Primary MI2S Playback", .cpu_dai_name = "msm-dai-q6-mi2s.0", .platform_name = "msm-pcm-routing", +#ifdef CONFIG_SND_SOC_FS16XX + .codec_name = "fs16xx.1-0034", + .codec_dai_name = "fs16xx_codec_left", +#else .codec_name = "msm-stub-codec.1", .codec_dai_name = "msm-stub-rx", +#endif .no_pcm = 1, .dpcm_playback = 1, .id = MSM_BACKEND_DAI_PRI_MI2S_RX, @@ -6220,6 +6225,7 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) rc = of_property_read_u32(dev->of_node, "qcom,afe-rxtx-lb", &val); if (!rc && val) { + memcpy(msm_bengal_dai_links + total_links, msm_afe_rxtx_lb_be_dai_link, sizeof(msm_afe_rxtx_lb_be_dai_link)); diff --git a/asoc/codecs/Android.mk b/asoc/codecs/Android.mk index 95f6c1abfe92..6715a8b3ab34 100644 --- a/asoc/codecs/Android.mk +++ b/asoc/codecs/Android.mk @@ -175,7 +175,14 @@ LOCAL_MODULE_DEBUG_ENABLE := true LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) include $(DLKM_DIR)/AndroidKernelModule.mk ########################################################### - +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_snd_soc_fs16xx.ko +LOCAL_MODULE_KBUILD_NAME := fs16xx_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### endif # DLKM check endif # supported target check diff --git a/asoc/codecs/Kbuild b/asoc/codecs/Kbuild index 5051969904cb..298002e27de1 100644 --- a/asoc/codecs/Kbuild +++ b/asoc/codecs/Kbuild @@ -252,6 +252,7 @@ ifeq ($(KERNEL_BUILD), 1) obj-y += wsa883x/ obj-y += sdm660_cdc/ obj-y += awinic/ + obj-y += foursemi/ endif # Module information used by KBuild framework obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd_core_dlkm.o @@ -293,5 +294,15 @@ obj-$(CONFIG_SND_SMARTPA_AW882XX) += aw882xx_dlkm.o aw882xx_dlkm-y := $(SND_SOC_AW882XX_OBJS) #Add awinic driver end +#Add fs1818 driver begain +ifdef CONFIG_SND_SOC_FS16XX + SND_SOC_FS16XX_OBJS += foursemi/fs16xx.o + SND_SOC_FS16XX_OBJS += foursemi/fs16xx_preset.o + SND_SOC_FS16XX_OBJS += foursemi/fs16xx_calibration.o +endif +obj-$(CONFIG_SND_SOC_FS16XX) += fs16xx_dlkm.o +fs16xx_dlkm-y := $(SND_SOC_FS16XX_OBJS) +#Add fs1818 driver end + # inject some build related information DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/asoc/codecs/foursemi/Kconfig b/asoc/codecs/foursemi/Kconfig new file mode 100644 index 000000000000..3f080886161d --- /dev/null +++ b/asoc/codecs/foursemi/Kconfig @@ -0,0 +1,5 @@ +config SND_SOC_FS16XX + tristate "Support foursemi fs16xx chip" + depends on I2C + help + foursemi fs16xx driver \ No newline at end of file diff --git a/asoc/codecs/foursemi/Makefile b/asoc/codecs/foursemi/Makefile new file mode 100644 index 000000000000..89f77b90b6a6 --- /dev/null +++ b/asoc/codecs/foursemi/Makefile @@ -0,0 +1,9 @@ +### +# Copyright (C) 2018 Fourier Semiconductor Inc. All rights reserved. +# + +# FS16XX VERSION INFO, DON'T REMOVE THIS LINE!!! +snd-soc-fs16xx-objs := fs16xx.o +snd-soc-fs16xx-objs += fs16xx_preset.o +snd-soc-fs16xx-objs += fs16xx_calibration.o +obj-$(CONFIG_SND_SOC_FS16XX) += snd-soc-fs16xx.o diff --git a/asoc/codecs/foursemi/fs16xx.c b/asoc/codecs/foursemi/fs16xx.c new file mode 100644 index 000000000000..3d61b32bf271 --- /dev/null +++ b/asoc/codecs/foursemi/fs16xx.c @@ -0,0 +1,1718 @@ +/* + * Fs16xx device driver implementation. + * Copyright (C) 2017 FourSemi Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "fs16xx_regs.h" +#include "fs16xx_reg_def.h" +#include "fs16xx_calibration.h" +#include "fs16xx_preset.h" +#include "fs16xx.h" + + +static DEFINE_MUTEX(lr_lock); +static int stereo_mode; +//static const char *fs_dev_name = "Right"; +static struct fs16xx_priv * g_fs16xx_priv[MAX_DEV_COUNT] = {NULL, NULL}; + + +fs16xx_dev_t *get_drvdata_by_context(struct snd_soc_codec *codec) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)) + return snd_soc_codec_get_drvdata(codec); +#else + return snd_soc_component_get_drvdata(codec); +#endif +} + +fs16xx_dev_t *get_drvdata_by_dai(struct snd_soc_dai *dai) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)) + struct snd_soc_codec *codec = dai->codec; +#else + struct snd_soc_codec *codec = dai->component; +#endif + return get_drvdata_by_context(codec); +} + +fs16xx_dev_t *get_drvdata_by_kctrl(struct snd_kcontrol *kcontrol) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)) + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +#else + struct snd_soc_codec *codec = snd_soc_kcontrol_component(kcontrol); +#endif + return get_drvdata_by_context(codec); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +static int snd_soc_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int val) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_context(codec); + if (!fs16xx) { + PRINT_ERROR("%s: invalid dev", __func__); + return -EINVAL; + } + return fs16xx_write_register16(fs16xx->id, reg, val); +} + +static unsigned int snd_soc_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_context(codec); + unsigned short val; + int ret; + + if (!fs16xx) { + PRINT_ERROR("%s: invalid dev", __func__); + return -EINVAL; + } + ret = fs16xx_read_register16(fs16xx->id, reg, &val); + if (ret != Fs16xx_Error_OK) { + return -1; + } + return val; +} +#endif + +/* + * I2C Read/Write Functions + */ +static int fs16xx_i2c_read(struct i2c_client *fs16xx_client, + u8 reg, u8 *value, int len) +{ + int err; + int tries = 0; + + struct i2c_msg msgs[] = { + { + .addr = fs16xx_client->addr, + .flags = 0, + .len = 1, + .buf = ®, + }, + { + .addr = fs16xx_client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = value, + }, + }; + + do { + err = i2c_transfer(fs16xx_client->adapter, msgs, + ARRAY_SIZE(msgs)); + if (err != ARRAY_SIZE(msgs)) + msleep_interruptible(I2C_RETRY_DELAY); + } while ((err != ARRAY_SIZE(msgs)) && (++tries < I2C_RETRIES)); + + if (err != ARRAY_SIZE(msgs)) { + dev_err(&fs16xx_client->dev, "read transfer error %d\n" + , err); + err = -EIO; + } else { + err = 0; + } + + return err; +} + +static int fs16xx_i2c_write(struct i2c_client *fs16xx_client, + u8 *value, u8 len) +{ + int err; + int tries = 0; + + struct i2c_msg msgs[] = { + { + .addr = fs16xx_client->addr, + .flags = 0, + .len = len, + .buf = value, + }, + }; + + do { + err = i2c_transfer(fs16xx_client->adapter, msgs, + ARRAY_SIZE(msgs)); + if (err != ARRAY_SIZE(msgs)) + msleep_interruptible(I2C_RETRY_DELAY); + } while ((err != ARRAY_SIZE(msgs)) && (++tries < I2C_RETRIES)); + + if (err != ARRAY_SIZE(msgs)) { + dev_err(&fs16xx_client->dev, "write transfer error\n"); + err = -EIO; + } else { + err = 0; + } + + return err; +} +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 108)) +static int fs16xx_bulk_write(struct snd_soc_codec *codec, unsigned int reg, + const void *data, size_t len) +{ + struct fs16xx_priv *fs16xx = get_drvdata_by_context(codec); + u8 chunk_buf[FS16XX_MAX_I2C_SIZE + 1]; + int offset = 0; + int ret = 0; + /* first byte is mem address */ + int remaining_bytes = len - 1; + int chunk_size = FS16XX_MAX_I2C_SIZE; + + chunk_buf[0] = reg & 0xff; + mutex_lock(&fs16xx->i2c_rw_lock); + while ((remaining_bytes > 0)) { + if (remaining_bytes < chunk_size) + chunk_size = remaining_bytes; + + memcpy(chunk_buf + 1, data + 1 + offset, chunk_size); + ret = fs16xx_i2c_write(fs16xx->control_data, chunk_buf, + chunk_size + 1); + offset = offset + chunk_size; + remaining_bytes = remaining_bytes - chunk_size; + } + + mutex_unlock(&fs16xx->i2c_rw_lock); + return ret; +} +#endif +/* +static int fs16xx_bulk_read(struct snd_soc_codec *codec, u8 reg, + u8 *data, int len) +{ + struct fs16xx_priv *fs16xx = get_drvdata_by_context(codec); + int ret = 0; + int offset = 0; + int remaining_bytes = len; + int chunk_size = FS16XX_MAX_I2C_SIZE; + + mutex_lock(&fs16xx->i2c_rw_lock); + while ((remaining_bytes > 0)) { + if (remaining_bytes < chunk_size) + chunk_size = remaining_bytes; + ret = fs16xx_i2c_read(fs16xx->control_data, reg, data + offset, + chunk_size); + offset = offset + chunk_size; + remaining_bytes = remaining_bytes - chunk_size; + } + mutex_unlock(&fs16xx->i2c_rw_lock); + + return ret; +}*/ + +static int fs16xx_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) +{ + struct fs16xx_priv *fs16xx = get_drvdata_by_context(codec); + u8 buf[3] = {0, 0, 0}; + int ret; + buf[0] = (reg & 0xff); + buf[1] = (val >> 8) & 0xff; + buf[2] = (val & 0xff); + + if (!fs16xx) { + PRINT_ERROR("%s: invalid param", __func__); + return -EINVAL; + } + + mutex_lock(&fs16xx->i2c_rw_lock); + ret = fs16xx_i2c_write(fs16xx->control_data, buf, ARRAY_SIZE(buf)); + mutex_unlock(&fs16xx->i2c_rw_lock); + return ret; +} + +static unsigned int fs16xx_read(struct snd_soc_codec *codec, unsigned int reg) +{ + struct fs16xx_priv *fs16xx = get_drvdata_by_context(codec); + u8 buf[3] = {0, 0, 0}; + int len = 2; + int val = -EIO; + + if (!fs16xx) { + PRINT_ERROR("%s: invalid param", __func__); + return -EINVAL; + } + mutex_lock(&fs16xx->i2c_rw_lock); + if (fs16xx_i2c_read(fs16xx->control_data, reg & 0xff, buf, len) == 0) { + val = (buf[0] << 8 | buf[1]); + } + mutex_unlock(&fs16xx->i2c_rw_lock); + return val; +} + +Fs16xx_Error_t fs16xx_handle_is_open(Fs16xx_devId_t id) { + Fs16xx_Error_t err = Fs16xx_Error_NotOpen; + if(id >= 0 && id < MAX_DEV_COUNT && g_fs16xx_priv[id]) { + err = Fs16xx_Error_OK; + } + return err; +} + +Fs_Dev_State* fs16xx_get_dev_state_by_id(Fs16xx_devId_t id) { + + struct fs16xx_priv *fs16xx; + if(id >= 0 && id < MAX_DEV_COUNT) { + fs16xx = g_fs16xx_priv[id]; + if(fs16xx) { + return &(fs16xx->dev_state); + } else { + return NULL; + } + } + return NULL; +} + +struct snd_soc_codec * fs16xx_get_codec_by_id(Fs16xx_devId_t id) { + if(id >= 0 && id < MAX_DEV_COUNT && g_fs16xx_priv[id]) { + return g_fs16xx_priv[id]->codec; + } else { + return NULL; + } +} + +Fs16xx_Error_t fs16xx_write_register16(Fs16xx_devId_t id, + unsigned char subaddress, + unsigned short value) +{ + Fs16xx_Error_t err = Fs16xx_Error_OK; + struct fs16xx_priv *fs16xx; + int ret; + u8 buf[3] = {0, 0, 0}; + buf[0] = (subaddress & 0xff); + buf[1] = (value >> 8) & 0xff; + buf[2] = (value & 0xff); + + if(id >= 0 && id < MAX_DEV_COUNT) { + fs16xx = g_fs16xx_priv[id]; + if(fs16xx) { + mutex_lock(&fs16xx->i2c_rw_lock); + ret = fs16xx_i2c_write(fs16xx->control_data, + buf, ARRAY_SIZE(buf)); + mutex_unlock(&fs16xx->i2c_rw_lock); + if(ret < 0) { + err = Fs16xx_Error_I2C_Fatal; + PRINT_ERROR("%s: i2c error!", __func__); + } + } else { + err = Fs16xx_Error_Bad_Parameter; + PRINT_ERROR("%s: Invalid fs16xx device instance!", __func__); + } + } else { + err = Fs16xx_Error_Bad_Parameter; + PRINT_ERROR("%s: Invalid fs16xx codec instance!", __func__); + } + + return err; +} + +Fs16xx_Error_t fs16xx_read_register16(Fs16xx_devId_t id, + unsigned char subaddress, + unsigned short *pValue) +{ + Fs16xx_Error_t err = Fs16xx_Error_OK; + struct fs16xx_priv *fs16xx; + int ret = 0; + u8 buf[2] = {0, 0}; + + //printk(KERN_INFO "%s: id: %d, g_fs16xx_priv[%d]: %p\n", __func__, id, id, g_fs16xx_priv[id]); + if(id >= 0 && id < MAX_DEV_COUNT) { + fs16xx = g_fs16xx_priv[id]; + if(fs16xx) { + mutex_lock(&fs16xx->i2c_rw_lock); + ret = fs16xx_i2c_read(fs16xx->control_data, + subaddress & 0xff, buf, 2); + mutex_unlock(&fs16xx->i2c_rw_lock); + + if(ret == 0) { + *pValue = (buf[0] << 8 | buf[1]); + } else { + err = Fs16xx_Error_I2C_Fatal; + PRINT_ERROR("%s: i2c error!", __func__); + } + } else { + err = Fs16xx_Error_Bad_Parameter; + PRINT_ERROR("%s: Invalid fs16xx device instance!", __func__); + } + } else { + err = Fs16xx_Error_Bad_Parameter; + PRINT_ERROR("%s: Invalid fs16xx codec instance!", __func__); + } + + return err; +} + +#if 0 +static const struct snd_kcontrol_new fs16xx_left_snd_controls[] = { + SOC_SINGLE("fs16xx Left device Right channel select", FS16XX_I2SCTRL_REG, 3, 3, 0), + SOC_SINGLE("fs16xx Left device MUTE", FS16XX_DACCTRL_REG, FS16XX_DACCTRL_REG_MUTE_POS, 1, 0), + SOC_SINGLE("fs16xx Left device DSP ALG", FS16XX_DSPCTRL_REG, FS16XX_DSPCTRL_REG_DSPEN_POS, 1, 0), +}; +static const struct snd_kcontrol_new fs16xx_right_snd_controls[] = { + SOC_SINGLE("fs16xx Right device Left channel select", FS16XX_I2SCTRL_REG, 3, 3, 0), + SOC_SINGLE("fs16xx Right device MUTE", FS16XX_DACCTRL_REG, FS16XX_DACCTRL_REG_MUTE_POS, 1, 0), + SOC_SINGLE("fs16xx Right device DSP ALG", FS16XX_DSPCTRL_REG, FS16XX_DSPCTRL_REG_DSPEN_POS, 1, 0), +}; +#else + +/* + * ASOC controls + */ +static int fs16xx_get_preset_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_kctrl(kcontrol); + Fs_Dev_State *dev_state = &(fs16xx->dev_state); + DEBUGPRINT("%s: enter codec name=%s preset=%d", __func__, + fs16xx->fs16xx_dev, dev_state->cur_preset); + mutex_lock(&lr_lock); + ucontrol->value.integer.value[0] = dev_state->cur_preset; + mutex_unlock(&lr_lock); + return 0; +} + +static int fs16xx_put_preset_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_kctrl(kcontrol); + int preset = ucontrol->value.integer.value[0]; + int id; + + if(fs16xx == NULL) { + PRINT_ERROR("%s: Bad parameter.\n", __func__); + } + PRINT("%s: enter codec name=%s set preset=%d\n", __func__, + fs16xx->fs16xx_dev, preset); + + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + id = FS16XX_DEV_INDEX_L; + } else if(!strncmp("right", fs16xx->fs16xx_dev, 5)) { + id = FS16XX_DEV_INDEX_R; + } else { + PRINT_ERROR("%s: invalid device name!\n", __func__); + return 1; + } + mutex_lock(&lr_lock); + fs16xx_set_preset(id, preset); + mutex_unlock(&lr_lock); + return 1; +} + +static int fs16xx_ext_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_kctrl(kcontrol); + struct snd_soc_codec *codec = fs16xx->codec; + u16 valPwr; + u16 powerstate; + int ret; + + valPwr = 0x1; + powerstate = 0; + // printk("%s,codec_name = %s\n" , __func__, codec->component.name); + mutex_lock(&lr_lock); + valPwr = snd_soc_read(codec, FS16XX_SYSCTRL_REG); + powerstate = valPwr & FS16XX_SYSCTRL_REG_PWDN_MSK; + PRINT("%s: valPwr = 0x%04x,powerstate = 0x%04x",__func__,valPwr,powerstate); + if(powerstate){ + PRINT("%s: need to power up",__func__); + valPwr &= (~FS16XX_SYSCTRL_REG_PWDN_MSK); + snd_soc_write(codec, FS16XX_SYSCTRL_REG, valPwr); + } + mutex_unlock(&lr_lock); + ret = snd_soc_put_volsw(kcontrol, ucontrol); + return 0; +} + +static const struct snd_kcontrol_new fs16xx_left_snd_controls[] = { + SOC_SINGLE_EXT("fs16xx Left device Right channel select", FS16XX_I2SCTRL_REG, 3, 3, 0, + snd_soc_get_volsw,fs16xx_ext_put), + SOC_SINGLE_EXT("fs16xx Left device MUTE", FS16XX_DACCTRL_REG, FS16XX_DACCTRL_REG_MUTE_POS, 1, 0, + snd_soc_get_volsw,fs16xx_ext_put), + SOC_SINGLE_EXT("fs16xx Left device DSP ALG", FS16XX_DSPCTRL_REG, FS16XX_DSPCTRL_REG_DSPEN_POS, 1, 0, + snd_soc_get_volsw,fs16xx_ext_put), + SOC_SINGLE_EXT("fs16xx Left preset mode", SND_SOC_NOPM, 0, 2, 0, fs16xx_get_preset_mode, fs16xx_put_preset_mode), +}; +static const struct snd_kcontrol_new fs16xx_right_snd_controls[] = { + SOC_SINGLE_EXT("fs16xx Right device Left channel select", FS16XX_I2SCTRL_REG, 3, 3, 0, + snd_soc_get_volsw,fs16xx_ext_put), + SOC_SINGLE_EXT("fs16xx Right device MUTE", FS16XX_DACCTRL_REG, FS16XX_DACCTRL_REG_MUTE_POS, 1, 0, + snd_soc_get_volsw,fs16xx_ext_put), + SOC_SINGLE_EXT("fs16xx Right device DSP ALG", FS16XX_DSPCTRL_REG, FS16XX_DSPCTRL_REG_DSPEN_POS, 1, 0, + snd_soc_get_volsw,fs16xx_ext_put), + SOC_SINGLE_EXT("fs16xx Right preset mode", SND_SOC_NOPM, 0, 2, 0, fs16xx_get_preset_mode, fs16xx_put_preset_mode), +}; +#endif + +static Fs16xx_Error_t fs16xx_UpdateOTCTRL(struct snd_soc_codec *codec) { + u16 value = 0, offset = 0; + + value = snd_soc_read(codec, FS16XX_OTPPG1W0_REG); + offset = ((value & 0x80) == 0) ? (value & 0x7F) : (-1 * (value & 0x7F)); + + value = snd_soc_read(codec, FS16XX_OTCTRL_REG); + + if ((offset + value) < 0xFFFF && (offset + value > 0)) { + snd_soc_write(codec, FS16XX_OTCTRL_REG, (u16)(offset + value)); + PRINT("%s: update OTCTRL 0x%04x", __func__, (u16)(offset + value)); + } else { + PRINT_ERROR("%s: mono mode!", __func__); + } + + return Fs16xx_Error_StateTimedOut; +} + +static Fs16xx_Error_t fs16xx_wait_clk_stable(struct snd_soc_codec *codec) { + int max_try = 500; + u16 value; + + while(max_try > 0) { + value = snd_soc_read(codec, FS16XX_STATUS_REG); + if((value & FS16XX_STATUS_REG_PLLS) != 0) { + return Fs16xx_Error_OK; + } + max_try--; + Sleep(1); + } + + return Fs16xx_Error_StateTimedOut; +} + +Fs16xx_Error_t fs16xx_wait_boost_ssend(struct snd_soc_codec *codec) { + int max_try = 150; + unsigned short value; + + while(max_try > 0) { + value = snd_soc_read(codec, FS16XX_BSTCTRL_REG); + if((value & FS16XX_BSTCTRL_REG_SSEND_MSK) != 0) { + return Fs16xx_Error_OK; + } + max_try--; + Sleep(1); + } + + return Fs16xx_Error_StateTimedOut; +} + +Fs16xx_Error_t fs16xx_wait_dsp_off(Fs16xx_devId_t id, int wait_for_adc, int max_try) { + Fs16xx_Error_t err; + unsigned short value; + unsigned short flag = FS16XX_DIGSTAT_REG_DACRUN; + if(wait_for_adc) { + flag = FS16XX_DIGSTAT_REG_DACRUN | FS16XX_DIGSTAT_REG_ADCRUN; + } + + while(max_try > 0) { + err = fs16xx_read_register16(id, FS16XX_DIGSTAT_REG, &value); + if(Fs16xx_Error_OK == err && ((value & flag) == 0)) { + return Fs16xx_Error_OK; + } + max_try--; + Sleep(1); + } + + return Fs16xx_Error_StateTimedOut; +} + +static void fs16xx_power(struct snd_soc_codec *codec, int on) +{ + u16 valPwr; + fs16xx_dev_t *fs16xx = get_drvdata_by_context(codec); + int id = -1; + + DEBUGPRINT("%s: %s enter on=%d", __func__, fs16xx->fs16xx_dev, on); +#ifndef FS_STEREO_MODE + if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + PRINT_ERROR("%s: mono mode!", __func__); + return; + } +#endif + valPwr = snd_soc_read(codec, FS16XX_SYSCTRL_REG); + if (on) { + snd_soc_write(codec, FS16XX_PLLCTRL4_REG, FS16XX_PLLCTRL_EN_ALL); + + valPwr &= (~FS16XX_SYSCTRL_REG_PWDN_MSK); + /* Amplifier always on */ + valPwr |= FS16XX_SYSCTRL_REG_AMPE; + snd_soc_write(codec, FS16XX_SYSCTRL_REG, valPwr); + } else { + valPwr |= FS16XX_SYSCTRL_REG_PWDN_MSK; + snd_soc_write(codec, FS16XX_SYSCTRL_REG, valPwr); // Power off + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + id = FS16XX_DEV_INDEX_L; + } else if(!strncmp("right", fs16xx->fs16xx_dev, 5)) { + id = FS16XX_DEV_INDEX_R; + } else { + PRINT_ERROR("%s: invalid device name!\n", __func__); + return; + } + fs16xx_wait_dsp_off(id, 1, 50); + + /* Disable bandgap, osc, pll */ + snd_soc_write(codec, FS16XX_PLLCTRL4_REG, 0); + } + + DEBUGPRINT("%s: valPwr=0x%04x", __func__, valPwr); +} + +static void fs16xx_set_mute(struct snd_soc_codec *codec, int mute_state) +{ + u16 valPwr; + fs16xx_dev_t *fs16xx = get_drvdata_by_context(codec); + DEBUGPRINT("%s: enter mute_state = %d", __func__, mute_state); + + /* read sys ctrl register */ + valPwr = snd_soc_read(codec, FS16XX_SYSCTRL_REG); + + switch (mute_state) { + case FS_UMUTE: + /* unmute and enable amp */ + valPwr |= FS16XX_SYSCTRL_REG_AMPE; + snd_soc_write(codec, FS16XX_SYSCTRL_REG, valPwr); + break; + case FS_MUTE: + /* turn off amp */ + valPwr &= (~FS16XX_SYSCTRL_REG_AMPE); + snd_soc_write(codec, FS16XX_SYSCTRL_REG, valPwr); + break; + default: + break; + } + + DEBUGPRINT("%s: write valPwr=0x%04X %s", __func__, valPwr, fs16xx->fs16xx_dev); +} + +static int fs16xx_calibaration(fs16xx_dev_t *fs16xx, int write_otp) +{ + static int calibrationOK = 0; + Fs16xx_Error_t result = Fs16xx_Error_OK; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + if(calibrationOK){ + if(!strncmp("left", fs16xx->fs16xx_dev, 4)) { + PRINT("%s: left write_otp %d", __func__, write_otp); + result = fs16xx_calibration(FS16XX_DEV_INDEX_L, 1, 0); + } else if(!strncmp("right", fs16xx->fs16xx_dev, 5)) { + PRINT("%s: right write_otp %d", __func__, write_otp); + result = fs16xx_calibration(FS16XX_DEV_INDEX_R, 1, 0); + } + }else{ + if(!strncmp("left", fs16xx->fs16xx_dev, 4)) { + PRINT("%s: left write_otp %d", __func__, write_otp); + result = fs16xx_calibration(FS16XX_DEV_INDEX_L, 1, 1); + } else if(!strncmp("right", fs16xx->fs16xx_dev, 5)) { + PRINT("%s: right write_otp %d", __func__, write_otp); + result = fs16xx_calibration(FS16XX_DEV_INDEX_R, 1, write_otp); + } + } + if(Fs16xx_Error_OK == result) { + calibrationOK = 1; + return 0; + } else { + return -101; + } +} + +static void fs16xx_work_read_imp(struct work_struct *work) +{ + fs16xx_dev_t *fs16xx = + container_of(work, fs16xx_dev_t, calib_work); + struct snd_soc_codec *codec = fs16xx->codec; + u16 val; + Fs16xx_devId_t id; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + if(!strncmp("left", fs16xx->fs16xx_dev, 4)) { + id = FS16XX_DEV_INDEX_L; + } else if(!strncmp("right", fs16xx->fs16xx_dev, 5)) { + id = FS16XX_DEV_INDEX_R; + } else { + PRINT_ERROR("%s: invalid codec name=%s!!!", __func__, fs16xx->fs16xx_dev); + return; + } + mutex_lock(&lr_lock); + val = snd_soc_read(codec, FS16XX_STATUS_REG); + + if((val & FS16XX_STATUS_REG_PLLS) != 0) { + fs16xx_get_calibration_result(id, &fs16xx->speaker_imp); + } + mutex_unlock(&lr_lock); +} + +static void fs16xx_work_mode(struct work_struct *work) +{ + fs16xx_dev_t *fs16xx = + container_of(work, fs16xx_dev_t, + mode_work.work); + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + mutex_lock(&lr_lock); + + mutex_unlock(&lr_lock); + return; +} + +static void fs16xx_load_preset(struct work_struct *work) +{ + fs16xx_dev_t *fs16xx = + container_of(work, fs16xx_dev_t, load_preset); + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + mutex_lock(&lr_lock); + + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + fs16xx_load_presets(FS16XX_DEV_INDEX_L); + fs16xx_set_preset(FS16XX_DEV_INDEX_L, 0); + } else if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + fs16xx_load_presets(FS16XX_DEV_INDEX_R); + fs16xx_set_preset(FS16XX_DEV_INDEX_R, 0); + } + mutex_unlock(&lr_lock); +} + +/* + * ASOC OPS +*/ +static int fs16xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_dai(codec_dai); + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + fs16xx->sysclk = freq; + return 0; +} + +static int fs16xx_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_dai(codec_dai); + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* default value */ + break; + case SND_SOC_DAIFMT_CBM_CFM: + default: + /* only supports Slave mode */ + PRINT_ERROR("fs16xx: invalid DAI master/slave interface"); + return -EINVAL; + } + + return 0; +} + +static int fs16xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_dai(dai); + struct snd_soc_codec *codec = fs16xx->codec; + u16 val; + int bclk_ws_ratio; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + /* validate and set params */ + /* if (params_format(params) != SNDRV_PCM_FORMAT_S16_LE) { + PRINT_ERROR("fs16xx: invalid pcm bit lenght"); + return -EINVAL; + }*/ + PRINT("fs16xx: params_format = %x\n",params_format(params)); + + mutex_lock(&lr_lock); + switch(params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_write(codec, 0xC3, 0x04); + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S32_LE: + snd_soc_write(codec, 0xC3, 0x08); + break; + default: + PRINT_ERROR("fs16xx: invalid pcm bit lenght"); + snd_soc_write(codec, 0xC3, 0x08); + } + + val = snd_soc_read(codec, FS16XX_I2SCTRL_REG); + /* clear Sample rate bits */ + val = val & ~(FS16XX_I2SCTRL_REG_I2SSR_MSK); + + switch (params_rate(params)) { + case 8000: + val = val | FS16XX_I2SCTRL_RATE_08000; + break; + case 16000: + val = val | FS16XX_I2SCTRL_RATE_16000; + break; + case 44100: + val = val | FS16XX_I2SCTRL_RATE_44100; + break; + case 48000: + val = val | FS16XX_I2SCTRL_RATE_48000; + break; + default: + PRINT_ERROR("fs16xx: invalid sample rate = %d", params_rate(params)); + mutex_unlock(&lr_lock); + return -EINVAL; + } + + snd_soc_write(codec, FS16XX_I2SCTRL_REG, val); + mutex_unlock(&lr_lock); + + /* calc bclk to ws freq ratio, fs16xx supports only 32, 48, 64 */ + bclk_ws_ratio = fs16xx->sysclk/params_rate(params); + if (bclk_ws_ratio != 32 && bclk_ws_ratio != 48 + && bclk_ws_ratio != 64) { + PRINT_ERROR("fs16xx: invalid bit clk to ws freq ratio %d:", + bclk_ws_ratio); + return -EINVAL; + } + + return 0; +} + +static int fs16xx_mute(struct snd_soc_dai *dai, int mute) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_dai(dai); + struct snd_soc_codec *codec = fs16xx->codec; + + DEBUGPRINT("%s: enter codec name=%s mute=%d", __func__, fs16xx->fs16xx_dev, mute); +#ifndef FS_STEREO_MODE + if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + PRINT_ERROR("%s: mono mode!", __func__); + return 0; + } +#endif + mutex_lock(&lr_lock); + fs16xx_wait_clk_stable(codec); + fs16xx_wait_boost_ssend(codec); + if (mute) { + cancel_delayed_work_sync(&fs16xx->mode_work); + } + + if (mute) { + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + fs16xx_set_tempr_protection(FS16XX_DEV_INDEX_L, FS_DISABLE); + } else if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + fs16xx_set_tempr_protection(FS16XX_DEV_INDEX_R, FS_DISABLE); + } + fs16xx_set_mute(codec, FS_MUTE); + } else { + fs16xx_set_mute(codec, FS_UMUTE); + Sleep(10); + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + fs16xx_set_tempr_protection(FS16XX_DEV_INDEX_L, FS_ENABLE); + } else if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + fs16xx_set_tempr_protection(FS16XX_DEV_INDEX_R, FS_ENABLE); + } + } + mutex_unlock(&lr_lock); + return 0; +} + +static int fs16xx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_dai(dai); + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + mutex_lock(&lr_lock); + fs16xx_power(fs16xx->codec, 1); + mutex_unlock(&lr_lock); + return 0; +} + +static void fs16xx_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_dai(dai); + DEBUGPRINT("%s: enter", __func__); + mutex_lock(&lr_lock); + fs16xx_power(fs16xx->codec, 0); + mutex_unlock(&lr_lock); +} + +/* Trigger callback is atomic function, It gets called when pcm is started */ +static int fs16xx_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_dai(dai); + int ret = 0; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + fs16xx->is_pcm_triggered = 1; + /* if in bypass mode dont't do anything */ + if (fs16xx->is_spkr_prot_en) + break; + /* To initialize dsp all the I2S signals should be bought up, + * so that the DSP's internal PLL can sync up and memory becomes + * accessible. Trigger callback is called when pcm write starts, + * so this should be the place where DSP is initialized + */ + if (fs16xx->dsp_init == FS16XX_DSP_INIT_PENDING) + queue_work(fs16xx->fs16xx_wq, &fs16xx->init_work); + + /* will need to read speaker impedence here if its not read yet + * to complete the calibartion process. This step will enable + * device to calibrate if its not calibrated/validated in the + * factory. When the factory process is in place speaker imp + * will be read from sysfs and validated. + */ + else if (fs16xx->dsp_init == FS16XX_DSP_INIT_OK) { + if ((fs16xx->mode_switched == 1) || + (fs16xx->update_cfg == 1)) + queue_delayed_work(fs16xx->fs16xx_wq, &fs16xx->mode_work, + 0); + if (fs16xx->speaker_imp == 0) + queue_work(fs16xx->fs16xx_wq, + &fs16xx->calib_work); + + } + /* else nothing to do */ + break; + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + break; + case SNDRV_PCM_TRIGGER_STOP: + fs16xx->is_pcm_triggered = 0; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +/* Called from fs16xx stereo codec stub driver to set mute + * on the individual codecs, this is needed to smother pops + * in stereo configuration. +*/ + +/*int fs16xx_stereo_sync_set_mute(int mute) +{ + // TODO: sync mute + return 0; +}*/ + +/* + * SysFS support + */ + +static ssize_t fs16xx_show_spkr_imp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + fs16xx_dev_t *fs16xx = + i2c_get_clientdata(to_i2c_client(dev)); + u16 val; + Fs16xx_devId_t id; + u32 r25 = 0; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + if(!strncmp("left", fs16xx->fs16xx_dev, 4)) { + id = FS16XX_DEV_INDEX_L; + } else if(!strncmp("right", fs16xx->fs16xx_dev, 5)) { + id = FS16XX_DEV_INDEX_R; + } else { + PRINT_ERROR("%s: invalid codec name=%s!!!", __func__, fs16xx->fs16xx_dev); + id = FS16XX_DEV_INDEX_R; // Read right channel + } + + if (fs16xx->codec) { + mutex_lock(&lr_lock); + val = snd_soc_read(fs16xx->codec, FS16XX_STATUS_REG); + + if((val & FS16XX_STATUS_REG_PLLS) != 0) { + fs16xx_get_calibration_result(id, &r25); + fs16xx->speaker_imp = r25; + } + mutex_unlock(&lr_lock); + } + return scnprintf(buf, PAGE_SIZE, "%u\n", fs16xx->speaker_imp); +} + +static ssize_t fs16xx_show_dev_state_info(struct file *filp, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + fs16xx_dev_t *fs16xx = + i2c_get_clientdata(kobj_to_i2c_client(kobj)); + u16 val; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + if (off >= attr->size) + return 0; + + if (off + count > attr->size) + count = attr->size - off; + + if (fs16xx->codec) { + mutex_lock(&lr_lock); + val = snd_soc_read(fs16xx->codec, FS16XX_STATUS_REG); + mutex_unlock(&lr_lock); + if(count > 4) { + scnprintf(buf, count, "%04X", val); + } + } + return 4; +} + +static ssize_t fs16xx_show_spkr_imp_model(struct file *filp, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + fs16xx_dev_t *fs16xx = + i2c_get_clientdata(kobj_to_i2c_client(kobj)); + u16 val; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + if (off >= attr->size) + return 0; + + if (off + count > attr->size) + count = attr->size - off; + + if (fs16xx->codec) { + mutex_lock(&lr_lock); + val = snd_soc_read(fs16xx->codec, FS16XX_STATUS_REG); + mutex_unlock(&lr_lock); + } + return 0; +} + +static ssize_t fs16xx_show_spkr_exc_model(struct file *filp, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + fs16xx_dev_t *fs16xx = + i2c_get_clientdata(kobj_to_i2c_client(kobj)); + u16 val; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + if (off >= attr->size) + return 0; + + if (off + count > attr->size) + count = attr->size - off; + + if (fs16xx->codec) { + mutex_lock(&lr_lock); + val = snd_soc_read(fs16xx->codec, FS16XX_STATUS_REG); + mutex_unlock(&lr_lock); + } + return 0; +} + +static ssize_t fs16xx_show_config_preset(struct file *filp, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + fs16xx_dev_t *fs16xx = + i2c_get_clientdata(kobj_to_i2c_client(kobj)); + u16 val; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + if (off >= attr->size) + return 0; + + if (off + count > (attr->size)) + count = (attr->size) - off; + + if (fs16xx->codec) { + mutex_lock(&lr_lock); + val = snd_soc_read(fs16xx->codec, FS16XX_STATUS_REG); + mutex_unlock(&lr_lock); + } + return 0; +} + +static ssize_t fs16xx_show_ic_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + fs16xx_dev_t *fs16xx = i2c_get_clientdata(to_i2c_client(dev)); + u16 val; + int temp = 0; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + if (fs16xx->codec) { + mutex_lock(&lr_lock); + val = snd_soc_read(fs16xx->codec, FS16XX_STATUS_REG); + mutex_unlock(&lr_lock); + } + return scnprintf(buf, PAGE_SIZE, "%u\n", temp); +} + +static ssize_t fs16xx_show_calib(struct device *dev, + struct device_attribute *attr, char *buf) +{ + fs16xx_dev_t *fs16xx = i2c_get_clientdata(to_i2c_client(dev)); + int val = 1; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + mutex_lock(&lr_lock); + val = fs16xx_calibaration(fs16xx, val); + mutex_unlock(&lr_lock); + + return scnprintf(buf, PAGE_SIZE, "%d\n", val); +} +static ssize_t fs16xx_show_calib_result(struct device *dev, + struct device_attribute *attr, char *buf) +{ + fs16xx_dev_t *fs16xx = i2c_get_clientdata(to_i2c_client(dev)); + int val = 0; + int id = 0; + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + id = FS16XX_DEV_INDEX_L; + } + if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + id = FS16XX_DEV_INDEX_R; + } + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + mutex_lock(&lr_lock); + fs16xx_get_calibration_result(id,&val); + mutex_unlock(&lr_lock); + + return scnprintf(buf, PAGE_SIZE, "%u\n", val); +} + +// Read reg interface for debugging +static ssize_t fs16xx_reg_read_bin_attr(struct file *filp, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + fs16xx_dev_t *fs16xx = i2c_get_clientdata(kobj_to_i2c_client(kobj)); + struct snd_soc_codec *codec = fs16xx->codec; + int offset = 0; + u16 regVal = 0; + + if(off + count > attr->size) { + count = attr->size - off; + //PRINT_ERROR("trim count to %d", count); + } + + if (fs16xx->codec) { + if(count >= FS16XX_MAX_REGISTER * 2) { + for(offset = 0; offset <= FS16XX_MAX_REGISTER; offset++) { + mutex_lock(&lr_lock); + regVal = snd_soc_read(codec, offset); + mutex_unlock(&lr_lock); + buf[offset * 2 ] = (u8)((regVal >> 8) & 0xFF); + buf[offset * 2 + 1] = (u8)((regVal) & 0xFF); + } + } + } + return count; +} + +// Write reg interface for debugging +/*************************************** +* reg write command: address value +* address should be in hex mode without 0x or \x +* value should be in hex mode without 0x or \x +****************************************/ +static ssize_t fs16xx_reg_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + fs16xx_dev_t *fs16xx = i2c_get_clientdata(client); + struct snd_soc_codec *codec = fs16xx->codec; + int reg=0, nVal=0; + u16 val=0; + + if (fs16xx->codec) { + DEBUGPRINT("%s: enter force right 0x35", __func__); + //DEBUGPRINT("%s: count=%d buf=%s stereo mode=%d", __func__, count, buf, stereo_mode); + if (count >= 3 && count <= 8) { + if(sscanf(buf, "%x %x", ®, &nVal) == 2) { + DEBUGPRINT("%s reg=0x%x val=0x%x", __func__, reg, nVal); + val = (u16)nVal; + // write single reg + mutex_lock(&lr_lock); + snd_soc_write(codec, reg, val); + mutex_unlock(&lr_lock); + } else { + PRINT_ERROR("%s parsing failed", __func__ ); + } + } + } + + return count; +} + +static const struct bin_attribute fs16xx_raw_bin_attr[] = { +{ + .attr = {.name = "device_state", .mode = S_IRUGO}, + .read = fs16xx_show_dev_state_info, + .size = FS16XX_DEV_STATE_SIZE, +}, +{ + .attr = {.name = "spkr_imp_model", .mode = S_IRUGO}, + .read = fs16xx_show_spkr_imp_model, + .size = FS16XX_SPK_FW_SIZE - 1, +}, +{ + .attr = {.name = "config_preset", .mode = S_IRUGO}, + .read = fs16xx_show_config_preset, + .size = FS16XX_CFG_FW_SIZE + FS16XX_PST_FW_SIZE - 2, +}, +{ + .attr = {.name = "spkr_exc_model", .mode = S_IRUGO}, + .read = fs16xx_show_spkr_exc_model, + .size = FS16XX_SPK_EX_FW_SIZE - 1, +}, +{ + .attr = {.name = "fs_reg_read", .mode = S_IRUGO}, + .read = fs16xx_reg_read_bin_attr, + .size = FS16XX_MAX_REGISTER * sizeof(u16), +} +}; + +static DEVICE_ATTR(ic_temp, S_IRUGO, + fs16xx_show_ic_temp, NULL); + +static DEVICE_ATTR(spkr_imp, S_IRUGO, + fs16xx_show_spkr_imp, NULL); + + +static DEVICE_ATTR(force_calib, S_IRUGO, + fs16xx_show_calib, NULL); +static DEVICE_ATTR(calib_result, S_IRUGO, + fs16xx_show_calib_result, NULL); + +static DEVICE_ATTR(fs_reg_write, S_IWUSR, + NULL, fs16xx_reg_write); + +static struct attribute *fs16xx_attributes[] = { + &dev_attr_spkr_imp.attr, + &dev_attr_force_calib.attr, + &dev_attr_calib_result.attr, + &dev_attr_ic_temp.attr, + &dev_attr_fs_reg_write.attr, + NULL +}; + +static const struct attribute_group fs16xx_attr_group = { + .attrs = fs16xx_attributes, +}; + + +static const struct snd_soc_dai_ops fs16xx_ops = { + .hw_params = fs16xx_hw_params, + .digital_mute = fs16xx_mute, + .set_fmt = fs16xx_set_dai_fmt, + .set_sysclk = fs16xx_set_dai_sysclk, + .startup = fs16xx_startup, + .shutdown = fs16xx_shutdown, + .trigger = fs16xx_trigger, +}; + +static struct snd_soc_dai_driver fs16xx_left_dai = { + .name = "fs16xx_codec_left", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = FS16XX_RATES, + .formats = FS16XX_FORMATS,}, + .ops = &fs16xx_ops, + .symmetric_rates = 1, +}; + +static struct snd_soc_dai_driver fs16xx_right_dai = { + .name = "fs16xx_codec_right", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = FS16XX_RATES, + .formats = FS16XX_FORMATS,}, + .ops = &fs16xx_ops, + .symmetric_rates = 1, +}; + +static int fs16xx_probe(struct snd_soc_codec *codec) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_context(codec); + int i; +#ifdef FS_STEREO_MODE + u16 val; +#endif + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + /* set codec Bulk write method, will be used for + * loading DSP firmware and config files. + */ +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 108)) + codec->bulk_write_raw = fs16xx_bulk_write; +#endif + fs16xx->codec = codec; + + /* reset registers to Default values */ + mutex_lock(&lr_lock); + snd_soc_write(codec, FS16XX_SYSCTRL_REG, 0x0002); + snd_soc_write(codec, FS16XX_SYSCTRL_REG, 0x0001); + Sleep(10); + + printk(KERN_INFO "%s enter.\n", __func__); + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + printk(KERN_INFO "%s in left\n", __func__); + snd_soc_write(codec, 0xC4, 0x000A); + /* initialize I2C registers */ + for (i = 0; i < ARRAY_SIZE(Fs16xx_reg_defaults); i++) { + snd_soc_write(codec, Fs16xx_reg_defaults[i].reg, + Fs16xx_reg_defaults[i].value); + } + + fs16xx_UpdateOTCTRL(codec); + } else if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + printk(KERN_INFO "%s in right\n", __func__); + snd_soc_write(codec, 0xC4, 0x000A); + usleep_range(5000, 5001); + for (i = 0; i < ARRAY_SIZE(Fs16xx_reg_defaults); i++) { + snd_soc_write(codec, Fs16xx_reg_defaults[i].reg, + Fs16xx_reg_defaults[i].value); + } + + fs16xx_UpdateOTCTRL(codec); + } +/* STEREO_MODE left device just select left channel, + right device just select right channel; */ +#ifdef FS_STEREO_MODE + printk("%s, stereo mode.\n",__func__); + val = snd_soc_read(codec, FS16XX_I2SCTRL_REG); + if(!strncmp("left", fs16xx->fs16xx_dev, 4)) { + val &= ~(0x3 << FS16XX_I2SCTRL_REG_CHS12_POS); + val |= (0x1 << FS16XX_I2SCTRL_REG_CHS12_POS); + } + if(!strncmp("right", fs16xx->fs16xx_dev, 5)) { + val &= ~(0x3 << FS16XX_I2SCTRL_REG_CHS12_POS); + val |= (0x2 << FS16XX_I2SCTRL_REG_CHS12_POS); + } + snd_soc_write(codec, FS16XX_I2SCTRL_REG, val); +#endif + mutex_unlock(&lr_lock); + + /* add controls and widgets */ + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + snd_soc_add_codec_controls(codec, fs16xx_left_snd_controls, + ARRAY_SIZE(fs16xx_left_snd_controls)); + /*snd_soc_dapm_new_controls(&codec->dapm, + fs16xx_left_dapm_widgets, + ARRAY_SIZE(fs16xx_left_dapm_widgets)); + + snd_soc_dapm_add_routes(&codec->dapm, fs16xx_left_dapm_routes, + ARRAY_SIZE(fs16xx_left_dapm_routes));*/ + } else if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + snd_soc_add_codec_controls(codec, fs16xx_right_snd_controls, + ARRAY_SIZE(fs16xx_right_snd_controls)); + /*snd_soc_dapm_new_controls(&codec->dapm, + fs16xx_right_dapm_widgets, + ARRAY_SIZE(fs16xx_right_dapm_widgets)); + snd_soc_dapm_add_routes(&codec->dapm, fs16xx_right_dapm_routes, + ARRAY_SIZE(fs16xx_right_dapm_routes));*/ + } +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 108)) + snd_soc_dapm_new_widgets(&codec->dapm); + snd_soc_dapm_sync(&codec->dapm); +#elif (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 18, 72)) + snd_soc_dapm_new_widgets(codec->component.card); + snd_soc_dapm_sync(&codec->dapm); +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)) + snd_soc_dapm_new_widgets(codec->component.card); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); +#else + snd_soc_dapm_new_widgets(codec->card); + snd_soc_dapm_sync(snd_soc_component_get_dapm(codec)); +#endif + + /* load preset tables */ + queue_work(fs16xx->fs16xx_wq, &fs16xx->load_preset); + printk("fs16xx codec probe successfully. \n"); + return 0; +} + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)) +static int fs16xx_remove(struct snd_soc_codec *codec) +{ + pr_debug("%s: enter\n", __func__); + mutex_lock(&lr_lock); + fs16xx_power(codec, 0); + mutex_unlock(&lr_lock); + return 0; +} +#else +static void fs16xx_remove(struct snd_soc_codec *codec) +{ + pr_debug("%s: enter\n", __func__); + mutex_lock(&lr_lock); + fs16xx_power(codec, 0); + mutex_unlock(&lr_lock); +} +#endif + +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 14, 72)) +/* table used by ALSA core while creating codec register + * access debug fs. + */ +static u8 fs16xx_reg_readable[FS16XX_REG_CACHE_SIZE] = { +[FS16XX_STATUS_REG] = 1, +[FS16XX_BATS_REG] = 1, +[FS16XX_TEMPS_REG] = 1, +[FS16XX_ID_REG] = 1, +[FS16XX_I2SCTRL_REG] = 1, +[FS16XX_AUDIOCTRL_REG] = 1, +[FS16XX_TEMPSEL_REG] = 1, +[FS16XX_SYSCTRL_REG] = 1, +[FS16XX_SPKSTAUTS_REG] = 1, +[FS16XX_CHIPINI_REG] = 1, +[FS16XX_I2CADD_REG] = 1, +[FS16XX_BSTCTRL_REG] = 1 +}; + +static int fs16xx_readable(struct snd_soc_codec *codec, unsigned int reg) +{ + return fs16xx_reg_readable[reg]; +} +#endif + +static struct snd_soc_codec_driver soc_codec_dev_fs16xx = { + .read = fs16xx_read, + .write = fs16xx_write, + .probe = fs16xx_probe, + .remove = fs16xx_remove, +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 14, 72)) + .readable_register = fs16xx_readable, +#endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)) + .reg_cache_size = FS16XX_REG_CACHE_SIZE, + .reg_word_size = 2, +#endif +}; +#ifdef CONFIG_OF +static struct fs16xx_pdata * +fs16xx_of_init(struct i2c_client *client) +{ + struct fs16xx_pdata *pdata; + struct device_node *np = client->dev.of_node; + + DEBUGPRINT("%s: enter i2c addr=0x%x", __func__, client->addr); + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + PRINT_ERROR("%s : pdata allocation failure.", __func__); + return NULL; + } + if (of_property_read_string(np, "fs,fs_bin_path", + &pdata->fw_path)) + pdata->fw_path = "."; + + if (of_property_read_string(np, "fs,fs-dev", &pdata->fs_dev)) + pdata->fs_dev = "left"; + + if (of_property_read_string(np, "fs,fs-firmware-part-name", + &pdata->fw_name)) + pdata->fw_name = "fs16xx"; + + return pdata; +} +#else +static inline struct fs16xx_pdata * +fs16xx_of_init(struct i2c_client *client) +{ + return NULL; +} +#endif + +static int fs16xx_detect_device(Fs16xx_devId_t id) +{ + uint16_t dev_id; + int ret; + + ret = fs16xx_read_register16(id, 0x03, &dev_id); + if (ret) { + return ret; + } + switch ((dev_id >> 8) & 0xff) { + case 0x06: // FS1818 + case 0x0B: // FS1896 + PRINT("%s: FS1818/FS1896 detected", __func__); + break; + default: + PRINT_ERROR("%s: invalid device_id: %X", __func__, dev_id); + return -EINVAL; + break; + } + + return 0; +} + +static int fs16xx_set_codec_info(struct device *dev, struct snd_soc_dai_driver *dai) +{ + char name[32]; + char tmp[32]; + int id1, id2; + + if (dev_name(dev) == NULL) + return -EINVAL; + + strlcpy(name, dev_name(dev), 32); + if (sscanf(name, "%x-%x", &id1, &id2) != 2) + return -EINVAL; + + /* sanitize component name for DAI link creation */ + snprintf(tmp, 32, "%s.%s", dev->driver->name, name); + strlcpy(name, tmp, 32); + printk("Codec: %s", name); + printk("Codec DAI: %s", dai->name); + + return 0; +} + +static int fs16xx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + fs16xx_dev_t *fs16xx; + struct fs16xx_pdata *pdata; + int ret; + int i; + + PRINT("%s: enter i2c addr=0x%x", __func__, i2c->addr); + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed."); + return -EIO; + } + + if (i2c->dev.of_node) + pdata = fs16xx_of_init(i2c); + else + pdata = i2c->dev.platform_data; + + /* check platform data */ + if (pdata == NULL) { + dev_err(&i2c->dev, "platform data is NULL."); + return -EINVAL; + } + + fs16xx = devm_kzalloc(&i2c->dev, sizeof(fs16xx_dev_t), GFP_KERNEL); + if (fs16xx == NULL) { + dev_err(&i2c->dev, "alloc memory fail"); + return -ENOMEM; + } + + fs16xx->control_data = i2c; + fs16xx->dsp_init = 1; + fs16xx->fs16xx_dev = pdata->fs_dev; + fs16xx->sysclk = SYS_CLK_DEFAULT; + fs16xx->update_eq = 1; + + // Initialize dev_state + fs16xx->dev_state.cur_preset = -1; + fs16xx->dev_state.calib_state = Fs16xx_Calib_Unknown; + fs16xx->dev_state.dev_preset = NULL; + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + fs16xx->id = FS16XX_DEV_INDEX_L; + #ifdef USE_RAS_MODE + fs16xx->dev_state.ras= true; + #else + fs16xx->dev_state.ras = false; + #endif + } else if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + fs16xx->id = FS16XX_DEV_INDEX_R; + } else { + PRINT_ERROR("%s: invalid dev:%s", __func__, fs16xx->fs16xx_dev); + return -EINVAL; + } + g_fs16xx_priv[fs16xx->id] = fs16xx; + + i2c_set_clientdata(i2c, fs16xx); + + mutex_init(&fs16xx->i2c_rw_lock); + + // check device id + ret = fs16xx_detect_device(fs16xx->id); + if (ret) { + PRINT_ERROR("%s: detect device[%X] failed", __func__, i2c->addr); + g_fs16xx_priv[fs16xx->id] = NULL; + return ret; + } + /* register sysfs hooks */ + ret = sysfs_create_group(&i2c->dev.kobj, &fs16xx_attr_group); + if (!ret) { + for (i = 0; i < ARRAY_SIZE(fs16xx_raw_bin_attr); i++) { + ret = sysfs_create_bin_file(&i2c->dev.kobj, + &fs16xx_raw_bin_attr[i]); + if (ret) + PRINT_ERROR("%s: Error creating fs16xx sysfs bin attr", __func__); + } + } + else { + PRINT_ERROR("%s: Error registering fs16xx sysfs", __func__); + } + + /* setup work queue, will be used to initial DSP on first boot up */ + fs16xx->fs16xx_wq = create_singlethread_workqueue("fs16xx"); + if (fs16xx->fs16xx_wq == NULL) { + ret = -ENOMEM; + goto wq_fail; + } + + INIT_WORK(&fs16xx->calib_work, fs16xx_work_read_imp); + INIT_DELAYED_WORK(&fs16xx->mode_work, fs16xx_work_mode); + INIT_WORK(&fs16xx->load_preset, fs16xx_load_preset); +#if 0 + fs16xx->vdd = regulator_get(&i2c->dev, "fs_vdd"); + if (IS_ERR(fs16xx->vdd)) { + PRINT_ERROR("%s: Error getting vdd regulator.", __func__); + ret = PTR_ERR(fs16xx->vdd); + goto codec_fail; + } + + regulator_set_voltage(fs16xx->vdd, 1800000, 1800000); + ret = regulator_enable(fs16xx->vdd); + if (ret < 0) { + PRINT_ERROR("%s: Error enabling vdd regulator %d:", + __func__, ret); + goto reg_enable_fail; + } +#endif + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + /* register codec */ + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_fs16xx, + &fs16xx_left_dai, 1); + if (ret < 0) { + PRINT_ERROR("%s: Error registering fs16xx left codec", + __func__); + goto codec_fail; + } + fs16xx_set_codec_info(&i2c->dev,&fs16xx_left_dai); + } else if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + /* register codec */ + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_fs16xx, + &fs16xx_right_dai, 1); + if (ret < 0) { + PRINT_ERROR("%s: Error registering fs16xx right codec", + __func__); + goto codec_fail; + } + fs16xx_set_codec_info(&i2c->dev,&fs16xx_right_dai); + stereo_mode = 1; + } + + PRINT("fs16xx %s probed successfully!", fs16xx->fs16xx_dev); + + return ret; + +codec_fail: +#if 0 + regulator_disable(fs16xx->vdd); +reg_enable_fail: + regulator_put(fs16xx->vdd); +#endif +wq_fail: + return ret; +} + +static int fs16xx_i2c_remove(struct i2c_client *client) +{ + fs16xx_dev_t *fs16xx = i2c_get_clientdata(client); + + DEBUGPRINT("%s: enter", __func__); + snd_soc_unregister_codec(&client->dev); +#if 0 + regulator_disable(fs16xx->vdd); + regulator_put(fs16xx->vdd); +#endif + destroy_workqueue(fs16xx->fs16xx_wq); + return 0; +} + +static const struct i2c_device_id fs16xx_i2c_id[] = { + { "fs16xx", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, fs16xx_i2c_id); + +#ifdef CONFIG_OF +static struct of_device_id fs16xx_match_tbl[] = { + { .compatible = "foursemi,fs16xx" }, + { }, +}; +MODULE_DEVICE_TABLE(of, fs16xx_match_tbl); +#endif + +static struct i2c_driver fs16xx_i2c_driver = { + .driver = { + .name = "fs16xx", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(fs16xx_match_tbl), + }, + .probe = fs16xx_i2c_probe, + .remove = fs16xx_i2c_remove, + .id_table = fs16xx_i2c_id, +}; + + + +static int __init fs16xx_modinit(void) +{ + int ret; + DEBUGPRINT("%s: enter.", __func__); + PRINT("fs16xx branch: %s\n", FS16XX_GIT_BRANCH); + PRINT("fs16xx commit: %s\n", FS16XX_GIT_COMMIT); + PRINT("fs16xx date: %s\n", FS16XX_CODE_DATE); + PRINT("fs16xx version: %s\n", FS16XX_CODE_VERSION); + ret = i2c_add_driver(&fs16xx_i2c_driver); + if (ret != 0) { + PRINT_ERROR("Failed to register fs16xx I2C driver:%d", ret); + } + return ret; +} +module_init(fs16xx_modinit); + +static void __exit fs16xx_exit(void) +{ + i2c_del_driver(&fs16xx_i2c_driver); +} +module_exit(fs16xx_exit); + +MODULE_DESCRIPTION("ASoC fs16xx codec driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("FourSemi SW"); diff --git a/asoc/codecs/foursemi/fs16xx.h b/asoc/codecs/foursemi/fs16xx.h new file mode 100644 index 000000000000..844722f7e552 --- /dev/null +++ b/asoc/codecs/foursemi/fs16xx.h @@ -0,0 +1,190 @@ +/* + * Fs16xx device driver header file. + * Copyright (C) 2017 FourSemi Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef __FS16XX_DRIVER_H__ +#define __FS16XX_DRIVER_H__ + +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif +#include "fs16xx_define.h" +#include "fs16xx_preset.h" +#include "fs16xx_calibration.h" + +// Macro for kernel mode implementation +#define FS16XX_KERNEL_IMPL + +#define MAX_DEV_HANDLES 4 + + +struct Fs16xx_device_handle { + int in_use; + unsigned char device_address; + unsigned char device_id; + unsigned char revision; + void *fs16xx; /* Pointer to fs16xx_dev_t */ +}; + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0])) +#endif + +struct fs16xx_regs { + int reg; + int value; +}; + +/* I2S_CONTROL bits for sample rates */ +#define FS16XX_I2SCTRL_RATE_08000 (0<= KERNEL_VERSION(4, 19, 0)) +#define snd_soc_codec snd_soc_component +#define snd_soc_add_codec_controls snd_soc_add_component_controls +#define snd_soc_register_codec devm_snd_soc_register_component +#define snd_soc_unregister_codec snd_soc_unregister_component +#define snd_soc_codec_driver snd_soc_component_driver +#endif + +struct _fs_dev_state { + int cur_preset; + bool ras; + unsigned short tmax; + unsigned short tempr_coef; + unsigned short tempr_sel; + unsigned short spk_set; + unsigned short adp_bst; + unsigned short bst_ctrl; + unsigned short ts_ctrl; + unsigned short acs_drc_ctrl[ACS_DRC_CTRL_LEN]; + Fs16xx_preset *dev_preset; + Fs16xx_Calib_State_t calib_state; + Fs16xx_Calib_Result_t calib_result; +}; +typedef struct _fs_dev_state Fs_Dev_State; + +struct fs16xx_priv { +#ifdef __KERNEL__ + struct i2c_client *control_data; + struct regulator *vdd; + struct snd_soc_codec *codec; + struct workqueue_struct *fs16xx_wq; + struct work_struct init_work; + struct work_struct calib_work; + struct delayed_work mode_work; + struct work_struct load_preset; + struct delayed_work delay_work; + struct mutex i2c_rw_lock; +#elif defined(__ANDROID__) + int fd; /* file handle of opening i2c node. */ + struct i2c_client *client[2]; /* i2c client for i2c operation in kernel. */ +#endif + int dsp_init; + int speaker_imp; + int sysclk; + int mode; + int mode_switched; + int curr_mode; + int cfg_preset; + int ic_version; + char const *fs16xx_dev; + char const *fw_path; + char const *fw_name; + int is_spkr_prot_en; + int update_eq; + int update_cfg; + int is_pcm_triggered; + Fs_Dev_State dev_state; + Fs16xx_devId_t id; +}; +typedef struct fs16xx_priv fs16xx_dev_t; + +struct fs16xx_pdata { + int reset_gpio; + int max_vol_steps; + const char *fs_dev; + const char *fw_path; + const char *fw_name; +}; + +/* DSP init status */ +#define FS16XX_DSP_INIT_PENDING 0 +#define FS16XX_DSP_INIT_OK 1 +#define FS16XX_DSP_INIT_FAIL -1 + +/* FsDbg print for Linux kernel */ +#define PRINT(msg,va...) pr_info(msg "\n", ##va) //pr_info +#define PRINT_ERROR(msg,va...) pr_err(msg "\n", ##va) +#define DEBUGPRINT(msg,va...) pr_debug(msg "\n", ##va) //pr_debug + +Fs16xx_Error_t fs16xx_write_register16(Fs16xx_devId_t id, + unsigned char subaddress, + unsigned short value); +Fs16xx_Error_t fs16xx_read_register16(Fs16xx_devId_t id, + unsigned char subaddress, + unsigned short *pValue); + +Fs16xx_Error_t fs16xx_handle_is_open(Fs16xx_devId_t id); + +Fs_Dev_State* fs16xx_get_dev_state_by_id(Fs16xx_devId_t id); + +struct snd_soc_codec * fs16xx_get_codec_by_id(Fs16xx_devId_t id); + +Fs16xx_Error_t fs16xx_wait_boost_ssend(struct snd_soc_codec *codec); + +Fs16xx_Error_t fs16xx_wait_dsp_off(Fs16xx_devId_t id, int wait_for_adc, int max_try); + +#endif diff --git a/asoc/codecs/foursemi/fs16xx_calibration.c b/asoc/codecs/foursemi/fs16xx_calibration.c new file mode 100644 index 000000000000..be694fd4fe4c --- /dev/null +++ b/asoc/codecs/foursemi/fs16xx_calibration.c @@ -0,0 +1,734 @@ +#include "fs16xx.h" +#include "fs16xx_regs.h" +#include "fs16xx_calibration.h" + +static int calib_r0 = R0_DEFAULT; +static int calib_allow_low = R0_ALLOWANCE_LOW; +static int calib_allow_high = R0_ALLOWANCE_HIGH; +static int calib_r0_step = FS16XX_CALIB_OTP_R25_STEP; +static int calib_r0_delta = OTP_MIN_DELTA; + + +//#ifdef USE_RAS_MODE +static int calib_r0_ras = R0_DEFAULT_RAS; +static int calib_allow_low_ras = R0_ALLOWANCE_LOW_RAS; +static int calib_allow_high_ras = R0_ALLOWANCE_HIGH_RAS; +static int calib_r0_step_ras = FS16XX_CALIB_OTP_R25_STEP_RAS; +//#endif + +Fs16xx_Error_t fs16xx_calibration(Fs16xx_devId_t id, int force, int storeResult) { + int count = 0, maxTryCount = 100; + unsigned short val, preVal = 0, minVal = 0; + unsigned int done = 0; + const int zmReadCount = 10; + Fs_Dev_State *dev_state = NULL; + + PRINT("%s enter. id=%d force=%d storeResult=%d", __func__, id, force, storeResult); + + dev_state = fs16xx_get_dev_state_by_id(id); + if(!dev_state) { + PRINT_ERROR("%s invalid id, exit!", __func__); + return Fs16xx_Error_StateInvalid; + } + + if(force || dev_state->calib_state == Fs16xx_Calib_NotStarted + || dev_state->calib_state == Fs16xx_Calib_Failed + || dev_state->calib_state == Fs16xx_Calib_OTP_Failed) { + + fs16xx_write_register16(id, FS16XX_OTPACC_REG, FS16XX_OTP_ACC_KEY2); + + // Start calibration + fs16xx_write_register16(id, FS16XX_ZMCONFIG_REG, 0x0010); + + fs16xx_write_register16(id, 0xAF, CALIB_REG_CALIB_MODE_AF); + fs16xx_write_register16(id, FS16XX_AUDIOCTRL_REG, 0x8F00); + fs16xx_write_register16(id, 0xC9, 0x0000); + fs16xx_write_register16(id, 0xCA, 0x0000); + fs16xx_write_register16(id, 0xCB, 0x0000); + fs16xx_write_register16(id, 0xCC, 0x0000); + fs16xx_write_register16(id, 0xB9, 0xFFFF); + fs16xx_write_register16(id, 0xBA, CALIB_REG_NORMAL_BA); + + if(!force && dev_state->calib_state == Fs16xx_Calib_NotStarted) { + // Sleep during the first time calibration + Sleep(2500); + } + + (dev_state->calib_result).calib_count++; + dev_state->calib_state = Fs16xx_Calib_OnGoing; + + while((!done) && (maxTryCount > 0)) { + Sleep(100); + maxTryCount--; + if(Fs16xx_Error_OK == fs16xx_read_register16(id, FS16XX_ZMDATA_REG, &val)) { + if(val != 0 && val != 0xFFFF) { + if (preVal == 0 || abs(preVal - val) > SPK_CALIBRATION_MAX_ZMDELTA) { + preVal = val; + count = 1; // Reset counter + minVal = val; + } else { + count++; + if(val < minVal) { + minVal = val; + } + } + } + } + if(count >= zmReadCount) { + done = 1; + } + } + + if(done && minVal != 0) { + dev_state->calib_state = fs16xx_calc_coef_from_zm(id, minVal, storeResult); + if(Fs16xx_Calib_OK == dev_state->calib_state) { + fs16xx_write_register16(id, FS16XX_AUDIOCTRL_REG, 0xFF00); + // Calibration OK, exit calibration + fs16xx_write_register16(id, FS16XX_ZMCONFIG_REG, 0x0000); + + fs16xx_write_register16(id, 0xAF, CALIB_REG_NORMAL_AF); + } + + if(Fs16xx_Calib_OTP_ExcceedMaxTry == dev_state->calib_state) { + // Calibration was done. However, OTP write failed due to counter limitation. + // In this case, we will NOT exit calibration mode in order to report calibration failure. + } + } else { + // Calibration failed + dev_state->calib_state = Fs16xx_Calib_Failed; + PRINT_ERROR("%s calibration failed. done=%d minVal=0x%04X", __func__, done, minVal); + } + } else { + fs16xx_write_register16(id, FS16XX_OTPACC_REG, 0); + return Fs16xx_Error_Calib_Error; + } + + PRINT("%s calibration end, result = %s.", __func__, dev_state->calib_state == Fs16xx_Calib_OK ? "OK" : "Failed"); + + fs16xx_write_register16(id, FS16XX_OTPACC_REG, 0); + return Fs16xx_Error_OK; +} + +Fs16xx_Calib_State_t fs16xx_check_calibration(Fs16xx_devId_t id) { +#ifndef FS16XX_CALIB_STORE_TO_OTP + FILE *fp; +#endif + int count; + Fs16xx_Error_t err = Fs16xx_Error_OK; + unsigned short value = 0, valueC4 = 0; + unsigned int r25; + Fs_Dev_State *dev_state = NULL; + Fs16xx_Calib_Result_t *calib_result; + + DEBUGPRINT("%s enter. id=%d", __func__, id); + + dev_state = fs16xx_get_dev_state_by_id(id); + if(!dev_state) { + PRINT_ERROR("%s invalid id, exit!", __func__); + //return Fs16xx_Error_StateInvalid; + return Fs16xx_Calib_Unknown; + } + calib_result = &(dev_state->calib_result); + + PRINT("%s enter id=%d, calib_state=%d", __func__, id, dev_state->calib_state); + + if(Fs16xx_Calib_Unknown != dev_state->calib_state) { + return dev_state->calib_state; + } + + // Apply values from preset + fs16xx_spkset_to_calib_coef(dev_state->ras,dev_state->spk_set); + + calib_result->calib_count = 0; + + err |= fs16xx_read_register16(id, FS16XX_PLLCTRL4_REG, &valueC4); + err |= fs16xx_write_register16(id, FS16XX_PLLCTRL4_REG, 0x000F); + + err |= fs16xx_write_register16(id, FS16XX_OTPACC_REG, FS16XX_OTP_ACC_KEY2); + +#ifdef FS16XX_CALIB_STORE_TO_OTP + // Read from 0xE8 first + err |= fs16xx_read_register16(id, 0xE8, &value); + if(Fs16xx_Error_OK != err) { + PRINT_ERROR("%s failed to read calibration result by otp cmd.", __func__); + } + + if(value == OTP_INIT_VALUE) { + count = 0; + } else { + count = fs16xx_get_otp_write_count(value & 0xFF); + } + PRINT("%s read calibration result = 0x%04x count=%d.", __func__, value, count); + // Calibration Count + if(count > 0) { + PRINT("%s calibrated already done.", __func__); + calib_result->calib_count = count; + } else { + // Need calibration + dev_state->calib_state = Fs16xx_Calib_NotStarted; + PRINT("%s calibrated not started.", __func__); + goto calib_exit; + } + + r25 = fs16xx_byte_to_r25(dev_state->ras,(unsigned char)((value >> 8) & 0xFF)); + PRINT("%s read calibration result from otp r25=%d.", __func__, r25); + dev_state->calib_state = fs16xx_update_coef_from_r25(id, r25); + + // ADC EN and TS EN should be set when osc is enabled. + fs16xx_write_register16(id, FS16XX_ADCTIME_REG, CALIB_REG_NORMAL_BA); + fs16xx_write_register16(id, FS16XX_TSCTRL_REG, CALIB_REG_NORMAL_AF); +#else + fp = fopen( FS16XX_PARAM_DIR FS16XX_CALIB_RESULT_FILE_NAME, "rb"); + if (!fp) + { + PRINT_ERROR("%s read calibration result failed.", __func__); + dev_state->calib_state = Fs16xx_Calib_NotStarted; + goto calib_exit; + } + + count = fread( (void*)calib_result, 1, sizeof(Fs16xx_Calib_Result_t), fp ); + fclose(fp); + + PRINT("%s read calibration file, count=%d.", __func__, count); + + if(count != sizeof(Fs16xx_Calib_Result_t)) { + // Calibration result file corrupted. + PRINT_ERROR("%s read calibration result corrupted.", __func__); + dev_state->calib_state = Fs16xx_Calib_NotStarted; + goto calib_exit; + } + + fs16xx_calc_coef_from_zm(id, calib_result->calib_result_zm, 0); +#endif + +calib_exit: + err |= fs16xx_write_register16(id, FS16XX_OTPACC_REG, 0); + err |= fs16xx_write_register16(id, FS16XX_PLLCTRL4_REG, valueC4); + + DEBUGPRINT("%s Exit.", __func__); + + return dev_state->calib_state; +} + +Fs16xx_Error_t fs16xx_get_calibration_result(Fs16xx_devId_t id, int *r25) { + Fs_Dev_State *dev_state = NULL; + + dev_state = fs16xx_get_dev_state_by_id(id); + if(!dev_state) { + PRINT_ERROR("%s invalid id, exit!", __func__); + return Fs16xx_Error_StateInvalid; + } + + if(dev_state->calib_state != Fs16xx_Calib_OK) { + PRINT_ERROR("%s calibration invalid result: %d", __func__, dev_state->calib_state); + return Fs16xx_Error_Calib_Error; + } + + *r25 = (int)(dev_state->calib_result.r25); + PRINT("%s r25=%d.", __func__, dev_state->calib_result.r25); + return Fs16xx_Error_OK; +} + +int fs16xx_otp_is_busy(Fs16xx_devId_t id) { + int retry = 20; + unsigned short value = 0; + + Sleep(5); + while(retry > 0) { + // Checking OTP busy bit + fs16xx_read_register16(id, FS16XX_OTPCMD_REG, &value); + if((value & FS16XX_OTPCMD_REG_BUSY_MSK) == 0) { + return 0; + } + Sleep(1); + retry--; + } + // Return non-0, otp op timeout... + return -1; +} + +int fs16xx_get_otp_write_count(unsigned short val) { + // OTP offset start from 0x10 to 0x20(Totally 16) + return (val >= OTP_OP_COUNTER_OFFSET) ? (val - OTP_OP_COUNTER_OFFSET + 1): 0; +} + +unsigned char fs16xx_r25_to_byte(bool ras,int r25) { + unsigned char val; + if(ras){ + val = (unsigned char)((int)abs(r25 - calib_r0_ras) * FS16XX_CALIB_STEP_MAGNIF_FACTOR / calib_r0_step_ras); + if (val > 0x7F) val = 0x7F; + val = (r25 >= calib_r0_ras) ? val : (unsigned char)(val | 0x80); + } else { + val = (unsigned char)((int)abs(r25 - calib_r0) * FS16XX_CALIB_STEP_MAGNIF_FACTOR / calib_r0_step); + if (val > 0x7F) val = 0x7F; + val = (r25 >= calib_r0) ? val : (unsigned char)(val | 0x80); + } + + // For Fs1801/Fs1818 otp value cannot be 0xFF(0xFF is reserved value). + if(0xFF == val) val -= 1; + return val; +} + +int fs16xx_byte_to_r25(bool ras,unsigned char b) { + if(ras){ + if((b & 0x80) != 0) { + // minus value + return calib_r0_ras - calib_r0_step_ras * (b & 0x7F) / FS16XX_CALIB_STEP_MAGNIF_FACTOR; + } + return calib_r0_ras + calib_r0_step_ras * b / FS16XX_CALIB_STEP_MAGNIF_FACTOR; + } else { + if((b & 0x80) != 0) { + // minus value + return calib_r0 - calib_r0_step * (b & 0x7F) / FS16XX_CALIB_STEP_MAGNIF_FACTOR; + } + return calib_r0 + calib_r0_step * b / FS16XX_CALIB_STEP_MAGNIF_FACTOR; + } +} + +int fs16xx_spkset_to_calib_coef(bool ras, unsigned short spkset) { + int spkset_idx = (spkset & FS16XX_SPKSET_REG_SPKR_MSK) >> FS16XX_SPKSET_REG_SPKR_POS; + int r = 0; + if( ras ){ + r=32; + } else { + switch(spkset_idx) { + case 0: + r = 4; + break; + case 1: + r = 6; + break; + case 2: + r = 7; + break; + case 3: + default: + r = 8; + break; + } + } + if(ras){ + //#ifdef USE_RAS_MODE + calib_r0_ras = r * MAGNIF_FACTOR;; + calib_allow_low_ras = (r * R0_ALLOWANCE_LOW_RAS) / 32; + calib_allow_high_ras = (r * R0_ALLOWANCE_HIGH_RAS) / 32; + calib_r0_step_ras = (r * FS16XX_CALIB_OTP_R25_STEP_RAS) / 32; + //#endif + } else { + calib_r0 = r * MAGNIF_FACTOR; + calib_allow_low = (r * R0_ALLOWANCE_LOW) / 8; + calib_allow_high = (r * R0_ALLOWANCE_HIGH) / 8; + calib_r0_step = (r * FS16XX_CALIB_OTP_R25_STEP) / 8; + calib_r0_delta = (r * OTP_MIN_DELTA) / 8; + } + + if(ras){ + PRINT("%s calib_r0_ras=0x%X calib_allow_low_ras=0x%X calib_allow_high_ras=0x%X calib_r0_step_ras = 0x%X.", \ + __func__, calib_r0_ras, calib_allow_low_ras, calib_allow_high_ras, calib_r0_step_ras); + } else { + PRINT("%s calib_r0=0x%X calib_allow_low=0x%X calib_allow_high=0x%X calib_r0_step = 0x%X calib_r0_delta = %d.", \ + __func__, calib_r0, calib_allow_low, calib_allow_high, calib_r0_step,calib_r0_delta); + } + + return r; +} + +Fs16xx_Calib_State_t fs16xx_calc_coef_from_zm(Fs16xx_devId_t id, unsigned short zm, int storeResult) { + int r25; + unsigned short spk_err, spk_m24, spk_m6, spk_rec, ext_tempr, value = 0; + Fs16xx_Error_t err = Fs16xx_Error_OK; + Fs_Dev_State *dev_state = NULL; + Fs16xx_Calib_Result_t *calib_result; + + dev_state = fs16xx_get_dev_state_by_id(id); + if(!dev_state) { + PRINT_ERROR("%s invalid id, exit!", __func__); + return Fs16xx_Calib_Failed; + } + calib_result = &(dev_state->calib_result); + ext_tempr = (dev_state->tempr_sel >> 1); + // Read spk_set everytime to make sure spk_set value updated. + err |= fs16xx_read_register16(id, FS16XX_SPKSET_REG, &(dev_state->spk_set)); + // Apply values from preset + fs16xx_spkset_to_calib_coef(dev_state->ras,dev_state->spk_set); + + if(zm != 0) { + // Read 0xE6 + fs16xx_read_register16(id, FS16XX_OTPPG1W2_REG, &value); + if(value != 0) { + value = value & 0xFF; + } else { + PRINT_ERROR("%s OTPPG1W2_REG unexpected! Set default value.", __func__); + value = RS_TRIM_DEFAULT; + } + r25 = value * RS2RL_RATIO * MAGNIF_FACTOR / zm; + } else { + r25 = 0xFFFF; // Invalid r25 value + } + + // Update calibration result even if the result is not valid + calib_result->r25 = r25; + calib_result->calib_ver = FS16XX_CALIB_VERSION; + calib_result->calib_result_zm = zm; + + PRINT("%s calibration result zm=0x%04X r25=%d.", __func__, zm, r25); + if(dev_state->ras){ + if (r25 > calib_allow_low_ras && r25 < calib_allow_high_ras) { + PRINT("%s Receiver r25=%d.", __func__, r25); + } else { + // Calibration result invalid, need to re-calibrate + PRINT("%s Receiver invalid result r25=%d.", __func__, r25); + return Fs16xx_Calib_Failed; + } + } else { + if(r25 < calib_allow_high && r25 > calib_allow_low) { + PRINT("%s Speaker r25=%d.",__func__,r25); + } else { + // Calibration result invalid, need to re-calibrate + PRINT("%s Speaker invalid result r25=%d.", __func__, r25); + return Fs16xx_Calib_Failed; + } + } + spk_m24 = (unsigned short)((unsigned int)zm * MAGNIF_TEMPR_COEF / + (MAGNIF_TEMPR_COEF + dev_state->tempr_coef * (((dev_state->tmax + 20)- ext_tempr)))); + + spk_err = spk_m24 / 2; + + spk_m6 = (unsigned short)((unsigned int)zm * MAGNIF_TEMPR_COEF / + (MAGNIF_TEMPR_COEF + dev_state->tempr_coef * (((dev_state->tmax) - ext_tempr)))); + + spk_rec = (unsigned short)((unsigned int)zm * MAGNIF_TEMPR_COEF / + (MAGNIF_TEMPR_COEF + dev_state->tempr_coef * (((dev_state->tmax - 10) - ext_tempr)))); + + calib_result->spk_err = spk_err; + calib_result->spk_m24 = spk_m24; + calib_result->spk_m6 = spk_m6; + calib_result->spk_recover = spk_rec; + + err |= fs16xx_write_register16(id, FS16XX_SPKERR_REG, spk_err); + err |= fs16xx_write_register16(id, FS16XX_SPKM24_REG, spk_m24); + err |= fs16xx_write_register16(id, FS16XX_SPKM6_REG, spk_m6); + err |= fs16xx_write_register16(id, FS16XX_SPKRE_REG, spk_rec); + + fs16xx_write_register16(id, FS16XX_ADCTIME_REG, CALIB_REG_NORMAL_BA); + fs16xx_write_register16(id, FS16XX_TSCTRL_REG, CALIB_REG_NORMAL_AF); + + if(storeResult) { +#ifdef FS16XX_CALIB_STORE_TO_OTP + err |= fs16xx_write_to_otp(id, fs16xx_r25_to_byte(dev_state->ras,r25)); +#else + err |= fs16xx_save_calib_result(id, zm); +#endif + } + + PRINT("%s calibration result spk_err=0x%04X spk_m24=0x%04X spk_m6=0x%04X spk_rec=0x%04X reg set-%s.", + __func__, spk_err, spk_m24, spk_m6, spk_rec, (Fs16xx_Error_OK == err) ? "OK" : "Fail"); + return (Fs16xx_Error_OK == err) ? Fs16xx_Calib_OK : dev_state->calib_state; +} + +Fs16xx_Calib_State_t fs16xx_update_coef_from_r25(Fs16xx_devId_t id, int r25) { + unsigned short spk_err, spk_m24, spk_m6, spk_rec, value = 0; + Fs16xx_Error_t err = Fs16xx_Error_OK; + unsigned int zm, ext_tempr; + Fs_Dev_State *dev_state = NULL; + Fs16xx_Calib_Result_t *calib_result; + + dev_state = fs16xx_get_dev_state_by_id(id); + if(!dev_state) { + PRINT_ERROR("%s invalid id, exit!", __func__); + return Fs16xx_Calib_Failed; + } + calib_result = &(dev_state->calib_result); + ext_tempr = (dev_state->tempr_sel >> 1); + + if(dev_state->ras){ + if (r25 < calib_allow_low_ras || r25 > calib_allow_high_ras) { + // Calibration result invalide, need to re-calibrate + PRINT_ERROR("%s calibration result out of range r25=%d.", __func__, r25); + return Fs16xx_Calib_Failed; + } + } else { + if (r25 < calib_allow_low || r25 > calib_allow_high) { + // Calibration result invalide, need to re-calibrate + PRINT_ERROR("%s calibration result out of range r25=%d.", __func__, r25); + return Fs16xx_Calib_Failed; + } + } + + if(r25 != 0) { + // Read 0xE6 + fs16xx_read_register16(id, FS16XX_OTPPG1W2_REG, &value); + if(value != 0) { + value = value & 0xFF; + } else { + PRINT_ERROR("%s OTPPG1W2_REG unexpected! Set default value.", __func__); + value = RS_TRIM_DEFAULT; + } + zm = value * RS2RL_RATIO * MAGNIF_FACTOR / r25; + } else { + PRINT_ERROR("%s Invalid r25!", __func__); + return Fs16xx_Calib_Failed; + } + spk_m24 = (unsigned short)((unsigned int)zm * MAGNIF_TEMPR_COEF / + (MAGNIF_TEMPR_COEF + dev_state->tempr_coef * (((dev_state->tmax + 20) - ext_tempr)))); + + spk_err = spk_m24 / 2; + + spk_m6 = (unsigned short)((unsigned int)zm * MAGNIF_TEMPR_COEF / + (MAGNIF_TEMPR_COEF + dev_state->tempr_coef * (((dev_state->tmax) - ext_tempr)))); + + spk_rec = (unsigned short)((unsigned int)zm * MAGNIF_TEMPR_COEF / + (MAGNIF_TEMPR_COEF + dev_state->tempr_coef * (((dev_state->tmax - 10) - ext_tempr)))); + + calib_result->spk_err = spk_err; + calib_result->spk_m24 = spk_m24; + calib_result->spk_m6 = spk_m6; + calib_result->spk_recover = spk_rec; + calib_result->r25 = r25; + + err |= fs16xx_write_register16(id, FS16XX_SPKERR_REG, spk_err); + err |= fs16xx_write_register16(id, FS16XX_SPKM24_REG, spk_m24); + err |= fs16xx_write_register16(id, FS16XX_SPKM6_REG, spk_m6); + err |= fs16xx_write_register16(id, FS16XX_SPKRE_REG, spk_rec); + + PRINT("%s read calibration result spk_err=0x%04X spk_m24=0x%04X spk_m6=0x%04X spk_rec=0x%04X reg set-%s.", + __func__, spk_err, spk_m24, spk_m6, spk_rec, (Fs16xx_Error_OK == err) ? "OK" : "Fail"); + return (Fs16xx_Error_OK == err) ? Fs16xx_Calib_OK : Fs16xx_Calib_Failed; +} + + +#ifdef FS16XX_CALIB_STORE_TO_OTP + +Fs16xx_Error_t fs16xx_write_to_otp(Fs16xx_devId_t id, unsigned char valOTP) { + int count, delta; + Fs16xx_Error_t err = Fs16xx_Error_OK; + unsigned short value = 0, valPwr = 0, valueC0 = 0, valueC4 = 0; + Fs_Dev_State *dev_state = NULL; + Fs16xx_Calib_Result_t *calib_result; + + dev_state = fs16xx_get_dev_state_by_id(id); + if(!dev_state) { + PRINT_ERROR("%s invalid id, exit!", __func__); + return Fs16xx_Error_StateInvalid; + } + calib_result = &(dev_state->calib_result); + + PRINT("%s enter valOTP=0x%02x.", __func__, valOTP); + err |= fs16xx_read_register16(id, FS16XX_SYSCTRL_REG, &valPwr); + // power up, amp off + err |= fs16xx_write_register16(id, FS16XX_SYSCTRL_REG, 0x0000); + + err |= fs16xx_read_register16(id, FS16XX_PLLCTRL4_REG, &valueC4); + err |= fs16xx_write_register16(id, FS16XX_PLLCTRL4_REG, 0x000A); + + err |= fs16xx_read_register16(id, FS16XX_BSTCTRL_REG, &valueC0); + err |= fs16xx_write_register16(id, FS16XX_BSTCTRL_REG, 0x0000); + + // OTP page + err |= fs16xx_write_register16(id, FS16XX_OTPADDR_REG, 0x0010); + // OTP read clear + err |= fs16xx_write_register16(id, FS16XX_OTPCMD_REG, 0x0000); + // OTP read + err |= fs16xx_write_register16(id, FS16XX_OTPCMD_REG, 0x0001); + + if(fs16xx_otp_is_busy(id) != 0) { + dev_state->calib_state= Fs16xx_Calib_OTP_Failed; + PRINT_ERROR("%s, Error, otp busy bit not cleared!", __func__); + goto otp_op_exit; + } + + fs16xx_read_register16(id, FS16XX_OTPRDATA_REG, &value); + if(value == OTP_INIT_VALUE) { + count = 0; + } else { + count = fs16xx_get_otp_write_count(value & 0xFF); + } + PRINT("%s read calibration result = 0x%04x otp write count=%d.", + __func__, value, count); + + delta = (int)fs16xx_byte_to_r25(dev_state->ras,valOTP) + - fs16xx_byte_to_r25(dev_state->ras,(value >> 8) & 0xFF); + if(delta < 0) delta *= -1; + + // OTP won't updated if calibrated r25 is not changed much(defined by otpDelta). + if(dev_state->ras) { + PRINT("%s delta = %d otpDelta_ras = %d.", __func__, delta, OTP_MIN_DELTA_RAS); + } else { + PRINT("%s delta = %d otpDelta = %d.", __func__, delta, calib_r0_delta); + } + if(dev_state->ras){ + if(count > 0 && delta < OTP_MIN_DELTA_RAS) { + PRINT("%s recevier no need to write to otp.", __func__); + goto otp_op_exit; + } + }else{ + if(count > 0 && delta < calib_r0_delta) { + PRINT("%s no need to write to otp.", __func__); + goto otp_op_exit; + } + } + + // Calibration Count + if(count >= FS16XX_CALIB_MAX_USER_TRY) { + PRINT_ERROR("%s calibrated count exceeds max.", __func__); + calib_result->calib_count = count; + dev_state->calib_state = Fs16xx_Calib_OTP_ExcceedMaxTry; + err = Fs16xx_Error_Calib_Error; + goto otp_op_exit; + } + + err |= fs16xx_write_register16(id, FS16XX_PLLCTRL4_REG, 0x000B); + + // enable Boost, and set it to follow mode + err |= fs16xx_write_register16(id, FS16XX_BSTCTRL_REG, 0x0008); + + // Write OTP page + err |= fs16xx_write_register16(id, FS16XX_OTPADDR_REG, 0x0010); + + // OTP write byte + err |= fs16xx_write_register16(id, FS16XX_OTPWDATA_REG, (unsigned short)valOTP); + + // Clear OTP commands + err |= fs16xx_write_register16(id, FS16XX_OTPCMD_REG, 0x0000); + // OTP enable Boost 7.6V mode + err |= fs16xx_write_register16(id, FS16XX_OTPCMD_REG, 0x2000); + + // Wait until ss_end + if(fs16xx_wait_boost_ssend(fs16xx_get_codec_by_id(id)) != Fs16xx_Error_OK) { + PRINT_ERROR("%s, Error, failed to wait for boost ssend!", __func__); + goto otp_op_exit; + } + + // Send single-byte write command + err |= fs16xx_write_register16(id, FS16XX_OTPCMD_REG, 0x2002); + + // Wait for 5ms for otp operation to complete. + Sleep(5); + if(fs16xx_otp_is_busy(id) != 0) { + dev_state->calib_state = Fs16xx_Calib_OTP_Failed; + err = Fs16xx_Error_Calib_Error; + goto otp_op_exit; + } + + // Clear OTP commands + err |= fs16xx_write_register16(id, FS16XX_OTPCMD_REG, 0x0000); + + Sleep(1); + + // disable boost + err |= fs16xx_write_register16(id, FS16XX_BSTCTRL_REG, 0x0000); + + calib_result->calib_count = count + 1; + + // Read back + err |= fs16xx_write_register16(id, FS16XX_PLLCTRL4_REG, 0x000A); + + err |= fs16xx_write_register16(id, FS16XX_BSTCTRL_REG, 0x0000); + + // OTP page + err |= fs16xx_write_register16(id, FS16XX_OTPADDR_REG, 0x0010); + // OTP read clear + err |= fs16xx_write_register16(id, FS16XX_OTPCMD_REG, 0x0000); + // OTP read + err |= fs16xx_write_register16(id, FS16XX_OTPCMD_REG, 0x0001); + + if(fs16xx_otp_is_busy(id) != 0) { + dev_state->calib_state = Fs16xx_Calib_OTP_Failed; + err = Fs16xx_Error_Calib_Error; + goto otp_op_exit; + } + + fs16xx_read_register16(id, FS16XX_OTPRDATA_REG, &value); + if(((value >> 8) & 0xFF) == valOTP) { + // Update count + calib_result->calib_count = fs16xx_get_otp_write_count(value & 0xFF); + PRINT("%s readback succeeded with count = %d.", __func__, calib_result->calib_count); + } else { + err = Fs16xx_Error_Calib_Error; + PRINT_ERROR("%s calibrated read back failed with value = 0x%04X(expected = 0x%02X--). Should not happen.", + __func__, value, valOTP); + } +otp_op_exit: + err |= fs16xx_write_register16(id, FS16XX_BSTCTRL_REG, valueC0); + err |= fs16xx_write_register16(id, FS16XX_PLLCTRL4_REG, valueC4); + // Recover power state + err |= fs16xx_write_register16(id, FS16XX_SYSCTRL_REG, valPwr); + + DEBUGPRINT("%s exit, error = %d.", __func__, err); + + return err; +} + +#else + +Fs16xx_Error_t fs16xx_save_calib_result(Fs16xx_devId_t id, unsigned short zmdata) { + FILE *fp; + int count; + Fs_Dev_State *dev_state = NULL; + Fs16xx_Calib_Result_t *calib_result; + + dev_state = fs16xx_get_dev_state_by_id(id); + if(!dev_state) { + PRINT_ERROR("%s invalid id, exit!", __func__); + return Fs16xx_Error_StateInvalid; + } + calib_result = &(dev_state->calib_result); + + DEBUGPRINT("%s enter.", __func__); + + // Write to file + fp = fopen( FS16XX_PARAM_DIR FS16XX_CALIB_RESULT_FILE_NAME, "wb"); + if (!fp) + { + PRINT_ERROR("%s open calibration file failed.", __func__); + return Fs16xx_Error_Calib_Error; + } + + calib_result.calib_count = g_calib_count; + calib_result.calib_ver = FS16XX_CALIB_VERSION; + calib_result.calib_result_zm = zmdata; + count = fwrite( (void*)&calib_result, 1, sizeof(Fs16xx_Calib_Result_t), fp ); + fclose(fp); + + PRINT("%s write to calibration file, count=%d.", __func__, count); + + return Fs16xx_Error_OK; +} +#endif + +Fs16xx_Error_t fs16xx_set_tempr_protection(Fs16xx_devId_t id, int enable) { + Fs16xx_Error_t err = Fs16xx_Error_OK; + Fs_Dev_State *dev_state = NULL; + Fs16xx_Calib_Result_t *calib_result; + + dev_state = fs16xx_get_dev_state_by_id(id); + if(!dev_state) { + PRINT_ERROR("%s invalid id, exit!", __func__); + return Fs16xx_Error_StateInvalid; + } + calib_result = &(dev_state->calib_result); + + PRINT("%s enter. enable=%d", __func__, enable); + if(enable) { + // TS on + err = fs16xx_write_register16(id, FS16XX_TSCTRL_REG, CALIB_REG_NORMAL_AF); + if(Fs16xx_Calib_Unknown == dev_state->calib_state) { + fs16xx_check_calibration(id); + } + if(Fs16xx_Calib_OK != dev_state->calib_state + && Fs16xx_Calib_OnGoing != dev_state->calib_state + && Fs16xx_Calib_OTP_ExcceedMaxTry != dev_state->calib_state) { + fs16xx_calibration(id, 0, 1); + } + }else { + // TS off + err = fs16xx_write_register16(id, FS16XX_TSCTRL_REG, CALIB_REG_TSOFF_AF); + // Delay enough time for ts off gracefully to avoid pop. + Sleep(35); + //fs16xx_wait_ts_off(id, 60); + } + + DEBUGPRINT("%s exit.", __func__); + return err; +} + diff --git a/asoc/codecs/foursemi/fs16xx_calibration.h b/asoc/codecs/foursemi/fs16xx_calibration.h new file mode 100644 index 000000000000..74b941cd255c --- /dev/null +++ b/asoc/codecs/foursemi/fs16xx_calibration.h @@ -0,0 +1,128 @@ +/* +* Copyright (C) 2016 Fourier Semiconductor Inc. +* +*/ + +#ifndef FS16XX_CALIBRATION_H +#define FS16XX_CALIBRATION_H + +#include "fs16xx_define.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#define MAGNIF_FACTOR 0x400 +#define RS2RL_RATIO 2700 +// TODO: to be loaded from preset +#define RS_TRIM_DEFAULT 0x8F + +#define R0_DEFAULT (8 * MAGNIF_FACTOR) + +#define R0_ALLOWANCE 30 // 30% +#define FS16XX_CALIB_STEP_MAGNIF_FACTOR 100 +#define R0_ALLOWANCE_LOW ((R0_DEFAULT * (100 - R0_ALLOWANCE)) / FS16XX_CALIB_STEP_MAGNIF_FACTOR) +#define R0_ALLOWANCE_HIGH ((R0_DEFAULT * (100 + R0_ALLOWANCE)) / FS16XX_CALIB_STEP_MAGNIF_FACTOR) + +#define FS16XX_CALIB_OTP_R25_STEP ((int)(R0_ALLOWANCE * R0_DEFAULT) / 0x7F) + +#define OTP_MIN_DELTA (int)(R0_DEFAULT * 0.05f) + +//RAS MODE +//#ifdef USE_RAS_MODE +#define R0_DEFAULT_RAS (32 * MAGNIF_FACTOR) +#define R0_ALLOWANCE_RAS 20 +#define R0_ALLOWANCE_LOW_RAS ((R0_DEFAULT_RAS * (100 - R0_ALLOWANCE_RAS)) / FS16XX_CALIB_STEP_MAGNIF_FACTOR) +#define R0_ALLOWANCE_HIGH_RAS ((R0_DEFAULT_RAS * (100 + R0_ALLOWANCE_RAS)) / FS16XX_CALIB_STEP_MAGNIF_FACTOR) +#define FS16XX_CALIB_OTP_R25_STEP_RAS ((int)(R0_ALLOWANCE_RAS * R0_DEFAULT_RAS) / 0x7F) +#define OTP_MIN_DELTA_RAS (int)(R0_DEFAULT_RAS * 0.05f) +//#endif + +#define MAGNIF_TEMPR_COEF 0xFFFF + +#define TMAX 120 +#define TMIN -20 +#define SPKTMPRM24 115 +#define SPKTMPRM6 110 +#define SPKTMPRECOVERY 100 + +#define OTP_OP_COUNTER_OFFSET 16 +#define OTP_INIT_VALUE 0xFF10 + +#define SPK_CALIBRATION_MAX_ZMDELTA 0x150 + +#define FS16XX_CALIB_VERSION 1 + +// Store calibration result to OTP, remove following macro to store calibration result to file system. +#define FS16XX_CALIB_STORE_TO_OTP 1 + +#define FS16XX_CALIB_RESULT_FILE_NAME "fs16xxcali.bin" + +#define CALIB_REG_NORMAL_BA 0x0031 +#define CALIB_REG_CALIB_MODE_AF 0x162B +#define CALIB_REG_NORMAL_AF 0x362B +#define CALIB_REG_TSOFF_AF 0x3623 + +/* DO NOT CHANGE THIS VALUE */ +#define FS16XX_CALIB_MAX_USER_TRY 15 + +/* Type containing all the possible errors that can occur + * + */ +enum Fs16xx_Calib_State { + Fs16xx_Calib_OK = 0, + Fs16xx_Calib_NotStarted = 1, + Fs16xx_Calib_OnGoing = 2, + Fs16xx_Calib_Failed = 3, + Fs16xx_Calib_OTP_Failed = 4, + Fs16xx_Calib_OTP_ExcceedMaxTry = 5, + Fs16xx_Calib_Unknown = 10 +}; +typedef enum Fs16xx_Calib_State Fs16xx_Calib_State_t; + +struct Fs16xx_Calib_Result { + unsigned short calib_ver; + unsigned short calib_count; + unsigned short calib_result_zm; + unsigned short spk_err; + unsigned short spk_m24; + unsigned short spk_m6; + unsigned short spk_recover; + int r25; +}; +typedef struct Fs16xx_Calib_Result Fs16xx_Calib_Result_t; + +Fs16xx_Error_t fs16xx_calibration(Fs16xx_devId_t id, int force, int storeResult); + +Fs16xx_Calib_State_t fs16xx_check_calibration(Fs16xx_devId_t id); + +Fs16xx_Error_t fs16xx_get_calibration_result(Fs16xx_devId_t id, int *r25); + +Fs16xx_Calib_State_t fs16xx_calc_coef_from_zm(Fs16xx_devId_t id, unsigned short zm, int storeResult); + +Fs16xx_Calib_State_t fs16xx_update_coef_from_r25(Fs16xx_devId_t id, int r25); + +int fs16xx_otp_is_busy(Fs16xx_devId_t id); + +int fs16xx_get_otp_write_count(unsigned short val); + +unsigned char fs16xx_r25_to_byte(bool ras,int r25); + +int fs16xx_byte_to_r25(bool ras,unsigned char b); + +int fs16xx_spkset_to_calib_coef(bool ras,unsigned short spkset); + +Fs16xx_Error_t fs16xx_set_tempr_protection(Fs16xx_devId_t id, int enable); + +#ifdef FS16XX_CALIB_STORE_TO_OTP +Fs16xx_Error_t fs16xx_write_to_otp(Fs16xx_devId_t id, unsigned char valOTP); +#else +Fs16xx_Error_t fs16xx_save_calib_result(Fs16xx_devId_t id, unsigned short zmdata); +#endif + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/asoc/codecs/foursemi/fs16xx_define.h b/asoc/codecs/foursemi/fs16xx_define.h new file mode 100644 index 000000000000..c543f8d387bc --- /dev/null +++ b/asoc/codecs/foursemi/fs16xx_define.h @@ -0,0 +1,120 @@ +/* + * Fs16xx device driver header file. + * Copyright (C) 2017 FourSemi Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef __FS16XX_DEFINE_H__ +#define __FS16XX_DEFINE_H__ + +#define FS16XX_MAX_REGISTER 0xE8 + +#define I2C_RETRY_DELAY 5 /* ms */ +#define I2C_RETRIES 20 +#define PLL_SYNC_RETRIES 10 +#define MTPB_RETRIES 5 +#define MAX_DEV_COUNT 2 + +/* DSP Firmware Size in bytes*/ +#define FS16XX_SPK_FW_SIZE 512 +#define FS16XX_CFG_FW_SIZE 512 +#define FS16XX_PST_FW_SIZE 512 +#define FS16XX_COEFF_FW_SIZE 512 +#define FS16XX_DEV_STATE_SIZE 24 +#define FS16XX_SPK_EX_FW_SIZE 512 + +#define FS16XX_REG_CACHE_SIZE 193 + +#define FS16XX_RATES (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|\ + SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000) +#define FS16XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE|SNDRV_PCM_FMTBIT_S24_LE|SNDRV_PCM_FMTBIT_S32_LE|\ + SNDRV_PCM_FORMAT_MU_LAW|SNDRV_PCM_FORMAT_S24_3LE) + +#define FIRMWARE_NAME_SIZE 128 + +#define FS16XX_MAX_I2C_SIZE 512 + +#define SYS_CLK_DEFAULT 1536000 + +#define FS16XX_DEV_INDEX_L 0 +#define FS16XX_DEV_INDEX_R 1 + +// DSP init status +#define FS16XX_DSP_INIT_PENDING 0 +#define FS16XX_DSP_INIT_OK 1 +#define FS16XX_DSP_INIT_FAILED -1 + +/* Type containing all the possible errors that can occur + * + */ +enum Fs16xx_Error { + Fs16xx_Error_OK = 0, + Fs16xx_Error_Bad_Parameter, + Fs16xx_Error_NotOpen, + Fs16xx_Error_OutOfHandles, + Fs16xx_Error_StateTimedOut, + Fs16xx_Error_StateInvalid, + + Fs16xx_Error_Not_Implemented, + Fs16xx_Error_Not_Supported, + Fs16xx_Error_I2C_Fatal, + Fs16xx_Error_I2C_NonFatal, + Fs16xx_Error_Other = 1000, + Fs16xx_Error_Not_FsDev = 1001, + Fs16xx_Error_Calib_Error = 1002, + Fs16xx_Error_Invalid_Preset = 1003 +}; +typedef enum Fs16xx_Error Fs16xx_Error_t; + +enum Fs16xx_Channel { + Fs16xx_Channel_L, // I2S left channel + Fs16xx_Channel_R, // I2S right channel + Fs16xx_Channel_L_R, // I2S (left + right channel)/2 + Fs16xx_Channel_Stereo +}; +typedef enum Fs16xx_Channel Fs16xx_Channel_t; + +enum Fs16xx_Mode { + Fs16xx_Mode_Normal = 0, + Fs16xx_Mode_RCV +}; +typedef enum Fs16xx_Mode Fs16xx_Mode_t; + +enum Fs16xx_PwrState { + Fs16xx_PwrOn = 0, + Fs16xx_PwrOff = 1 +}; +typedef enum Fs16xx_PwrState Fs16xx_PwrState_t; + +enum Fs16xx_OutputSel{ + Fs16xx_OutputSel_None = 0, + Fs16xx_OutputSel_DataL, // I2S left channel output + Fs16xx_OutputSel_DataR, // I2S right channel output + Fs16xx_OutputSel_TS // Test tone +}; +typedef enum Fs16xx_OutputSel Fs16xx_OutputSel_t; + +#define FS_DISABLE 0 +#define FS_ENABLE 1 + +#define FS_UMUTE 0 +#define FS_MUTE 1 + +typedef int Fs16xx_devId_t; + +//#define FS_STEREO_MODE +//#define USE_RAS_MODE + +#endif + diff --git a/asoc/codecs/foursemi/fs16xx_preset.c b/asoc/codecs/foursemi/fs16xx_preset.c new file mode 100644 index 000000000000..c7d8ea1e5588 --- /dev/null +++ b/asoc/codecs/foursemi/fs16xx_preset.c @@ -0,0 +1,452 @@ +/* +* Copyright (C) 2016 Fourier Semiconductor Inc. +* +*/ +#include "fs16xx.h" +#include "fs16xx_regs.h" +#include "fs16xx_preset.h" + +static int preset_init = 0; +static Fs16xx_preset gFs16xxPresets[(int)fs16xx_preset_type_max]; + +#ifndef FS_STEREO_MODE +static char const *gFs16xxPresetName[(int)fs16xx_preset_type_max] = { + "fs1801_mono.preset", + "fs1801_mono.preset" // prevent from crash in kernel +}; +#else +static char const *gFs16xxPresetName[(int)fs16xx_preset_type_max] = { + "fs1801_left.preset", + "fs1801_right.preset" +}; +#endif + +#define CRC16_TABLE_SIZE 256 +const uint16_t gCrc16Polynomial = 0xA001; +static uint16_t gCrc16Table[CRC16_TABLE_SIZE]; + +#define PRESET_VALID 1 +#define PRESET_INVALID 0 + +/* Initialize presets should only be called once in fs16xx_init +*/ +Fs16xx_Error_t fs16xx_initialize_presets(void) { + uint16_t value; + uint16_t temp, i; + uint8_t j; + if(preset_init) return Fs16xx_Error_OK; + memset(gFs16xxPresets, 0, sizeof(Fs16xx_preset) * (int)fs16xx_preset_type_max); + + // Initialize CRC16 table + for (i = 0; i < CRC16_TABLE_SIZE; ++i) + { + value = 0; + temp = i; + for (j = 0; j < 8; ++j) + { + if (((value ^ temp) & 0x0001) != 0) + { + value = (uint16_t)((value >> 1) ^ gCrc16Polynomial); + } + else + { + value >>= 1; + } + temp >>= 1; + } + gCrc16Table[i] = value; + } + preset_init = 1; + return Fs16xx_Error_OK; +} + +Fs16xx_Error_t fs16xx_deinitialize_presets(void) { + int i; + for(i = 0; i < (int)fs16xx_preset_type_max; i++) { + if(PRESET_VALID == gFs16xxPresets[i].valid) { + if(gFs16xxPresets[i].preset_header.data) { + gFs16xxPresets[i].valid = PRESET_INVALID; + kfree(gFs16xxPresets[i].preset_header.data); + } + } + } + + return Fs16xx_Error_OK; +} + +// load_presets should only be called once after initialization +Fs16xx_Error_t fs16xx_load_presets(Fs16xx_devId_t id) { + const struct firmware *fs_firmware = NULL; + struct snd_soc_codec *codec; + int j, data_len, buf_size, ret, pos; + uint16_t *ptr_data = NULL; + uint16_t *ptr_crc_data = NULL; + int32_t *ptr_excur_prot_ram = NULL; + uint16_t checksum; + // Preset crc data length in uint16_t : size from member tmax to the start of excur_prot_ram + const int preset_header_crc_data_len = + (sizeof(Fs16xx_preset_header_t) - sizeof(int32_t *) - sizeof(uint16_t *) + - sizeof(uint16_t) * 3 - 32 - sizeof(int64_t)) / 2; + const int preset_header_len = sizeof(Fs16xx_preset_header_t) - sizeof(int32_t *) + - sizeof(uint16_t *); + + PRINT("%s enter. id = %d", __func__, id); + + if(FS16XX_DEV_INDEX_L != id && FS16XX_DEV_INDEX_R != id) { + PRINT_ERROR("%s invalid id[%d]", __func__, id); + return Fs16xx_Error_Bad_Parameter; + } + + codec = fs16xx_get_codec_by_id(id); + if(NULL == codec) { + PRINT_ERROR("%s invalid codec with id[%d]", __func__, id); + return Fs16xx_Error_Bad_Parameter; + } + + fs16xx_initialize_presets(); + + if(gFs16xxPresets[id].valid) { + PRINT_ERROR("%s invalid status, presets already loaded.", __func__); + return Fs16xx_Error_StateInvalid; + } + + ret = request_firmware(&fs_firmware, gFs16xxPresetName[id], codec->dev); + if (ret) { + PRINT_ERROR("%s Failed to load fs preset table %s", __func__, gFs16xxPresetName[id]); + return Fs16xx_Error_Invalid_Preset; + } + PRINT("fs16xx: loaded firmware size = 0x%04X", fs_firmware->size); + if(fs_firmware->size < sizeof(Fs16xx_preset_header_t)) { + PRINT_ERROR("%s Invalid preset table %s size=%d", __func__, + gFs16xxPresetName[id], fs_firmware->size); + release_firmware(fs_firmware); + return Fs16xx_Error_Invalid_Preset; + } + + // Parse header + memcpy(&(gFs16xxPresets[id].preset_header), fs_firmware->data, preset_header_len); + + // Checking preset version + if(PRESET_VERSION != gFs16xxPresets[id].preset_header.preset_ver) { + PRINT_ERROR("%s preset version[0x%04X] invalid!", + __func__, gFs16xxPresets[id].preset_header.preset_ver); + release_firmware(fs_firmware); + return Fs16xx_Error_Invalid_Preset; + } + PRINT("fs16xx: customer_name: %s\n", gFs16xxPresets[id].preset_header.customer_name); + data_len = gFs16xxPresets[id].preset_header.data_len; + if(data_len <= 0 || (data_len % 2) != 0) { + PRINT_ERROR("%s preset header [%s] validation checking failed. data_len=%d", + __func__, gFs16xxPresetName[id], data_len); + release_firmware(fs_firmware); + return Fs16xx_Error_Invalid_Preset; + } + + // Copy excur prot + buf_size = EXCUR_PROT_RAM_LEN * sizeof(int32_t); + ptr_excur_prot_ram = (int32_t *)kzalloc(buf_size, GFP_KERNEL); + if (!ptr_excur_prot_ram) { + PRINT_ERROR("%s failed to allocate %d bytes for excur_prot_ram.\n", __func__, buf_size); + gFs16xxPresets[id].preset_header.excur_prot_ram = 0; + release_firmware(fs_firmware); + return Fs16xx_Error_Invalid_Preset; + } + memcpy(ptr_excur_prot_ram, ((u8*)fs_firmware->data) + preset_header_len, buf_size); + + buf_size = data_len * sizeof(uint16_t); + ptr_data = kzalloc(buf_size, GFP_KERNEL); + if (!ptr_data) { + PRINT_ERROR("%s failed to allocate %d bytes.", __func__, buf_size); + gFs16xxPresets[id].preset_header.data = 0; + release_firmware(fs_firmware); + return Fs16xx_Error_Invalid_Preset; + } + + memcpy(ptr_data, ((u8*)fs_firmware->data) + preset_header_len + + (EXCUR_PROT_RAM_LEN * sizeof(int32_t)), buf_size); + release_firmware(fs_firmware); + + // Fill ptr_crc_data + buf_size = (data_len + preset_header_crc_data_len) * sizeof(uint16_t) + + EXCUR_PROT_RAM_LEN * sizeof(int32_t); + DEBUGPRINT("%s buf_size = 0x%04X", __func__, buf_size); + ptr_crc_data = (uint16_t *)kzalloc(buf_size, GFP_KERNEL); + if (!ptr_crc_data) { + PRINT_ERROR("%s failed to allocate %d bytes.\n", __func__, buf_size); + gFs16xxPresets[id].preset_header.data = 0; + return Fs16xx_Error_Invalid_Preset; + } + + pos = 0; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.tmax; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.tempr_coef; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.tempr_sel; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.spk_set; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.adp_bst[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.adp_bst[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.bst_ctrl[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.bst_ctrl[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.agc_ctrl[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.agc_ctrl[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.agc_thd[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.agc_thd[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.agc_para[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.agc_para[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.agc_mg[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.agc_mg[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.dac_para1[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.dac_para1[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.acs_drc[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.acs_drc[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.acs_drcs[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.acs_drcs[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.acs_drcp[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.acs_drcp[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.ts_ctrl[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.ts_ctrl[1]; pos++; + + for (j = 0; j < EXCUR_PROT_RAM_LEN; j++) + { + ptr_crc_data[pos] = (uint16_t)(ptr_excur_prot_ram[j] & 0xFFFF); + ptr_crc_data[pos + 1] = (uint16_t)((ptr_excur_prot_ram[j] & 0xFF0000) >> 16); + pos += 2; + } + + for (j = 0; j < data_len; j++) + { + ptr_crc_data[pos] = ptr_data[j]; + pos++; + } + + /*if(pos != (data_len + preset_header_crc_data_len)) { + PRINT_ERROR("%s preset length not matching! pos=%d expected=%d.\n", __func__, + pos, (data_len + preset_header_crc_data_len)); + kfree(ptr_data); + kfree(ptr_crc_data); + return Fs16xx_Error_Invalid_Preset; + }*/ + + checksum = fs16xx_calc_checksum(ptr_crc_data, buf_size / 2); + kfree(ptr_crc_data); + + if(checksum != gFs16xxPresets[id].preset_header.crc16) { + PRINT_ERROR("%s preset data [%s] checksum not match. checksum=0x%04X, expected=0x%04X", + __func__, gFs16xxPresetName[id], checksum, gFs16xxPresets[id].preset_header.crc16); + gFs16xxPresets[id].preset_header.data = 0; + kfree(ptr_data); + + return Fs16xx_Error_Invalid_Preset; + } + + gFs16xxPresets[id].preset_header.data = ptr_data; + gFs16xxPresets[id].preset_header.excur_prot_ram = ptr_excur_prot_ram; + gFs16xxPresets[id].valid = PRESET_VALID; + + fs16xx_load_presets_to_dev(id); + PRINT("%s successfully exit. boost 0x%04X", __func__, + gFs16xxPresets[id].preset_header.bst_ctrl[0]); + return Fs16xx_Error_OK; +} + +Fs16xx_Error_t fs16xx_load_presets_to_dev(Fs16xx_devId_t id) { + int i, data_len; + Fs16xx_Error_t err = Fs16xx_Error_OK; + uint16_t *ptr_data = 0; + int32_t *ptr_prot_coef = 0; + uint16_t valPwr; + Fs_Dev_State *dev_state = fs16xx_get_dev_state_by_id(id); + + if(FS16XX_DEV_INDEX_L != id && FS16XX_DEV_INDEX_R != id) { + PRINT_ERROR("%s invalid id[%d]", __func__, id); + return Fs16xx_Error_Bad_Parameter; + } + if(dev_state == NULL){ + PRINT_ERROR("%s dev_state is null!", __func__); + return Fs16xx_Error_Bad_Parameter; + } + + if(gFs16xxPresets[id].valid != PRESET_VALID + || gFs16xxPresets[id].preset_header.data == NULL) { + PRINT_ERROR("%s invalid preset id[%d] data=%p", __func__, + id, gFs16xxPresets[id].preset_header.data); + return Fs16xx_Error_Invalid_Preset; + } + + dev_state->cur_preset = -1; // Reset preset index + dev_state->tmax = gFs16xxPresets[id].preset_header.tmax; + dev_state->tempr_coef = gFs16xxPresets[id].preset_header.tempr_coef; + dev_state->tempr_sel = gFs16xxPresets[id].preset_header.tempr_sel; + dev_state->spk_set = gFs16xxPresets[id].preset_header.spk_set; + dev_state->adp_bst = gFs16xxPresets[id].preset_header.adp_bst[0]; + dev_state->bst_ctrl = gFs16xxPresets[id].preset_header.bst_ctrl[0]; + dev_state->acs_drc_ctrl[0] = gFs16xxPresets[id].preset_header.acs_drc[0]; + dev_state->acs_drc_ctrl[1] = gFs16xxPresets[id].preset_header.acs_drcs[0]; + dev_state->acs_drc_ctrl[2] = gFs16xxPresets[id].preset_header.acs_drcp[0]; + + dev_state->ts_ctrl = gFs16xxPresets[id].preset_header.ts_ctrl[0]; + + data_len = gFs16xxPresets[id].preset_header.data_len; + ptr_data = gFs16xxPresets[id].preset_header.data; + ptr_prot_coef = gFs16xxPresets[id].preset_header.excur_prot_ram; + dev_state->dev_preset = &(gFs16xxPresets[id]); + + DEBUGPRINT("%s ptr_data=%p data_len=%d", __func__, ptr_data, data_len); + + // Amp off + err |= fs16xx_read_register16(id, FS16XX_SYSCTRL_REG, &valPwr); + err |= fs16xx_write_register16(id, FS16XX_SYSCTRL_REG, valPwr & (~FS16XX_SYSCTRL_REG_AMPE)); + + // Set spk_set + err |= fs16xx_write_register16(id, FS16XX_SPKSET_REG, gFs16xxPresets[id].preset_header.spk_set); + // Set tempr coef to TEMPSEL, refer to Bug 55 + err |= fs16xx_write_register16(id, FS16XX_TEMPSEL_REG, (gFs16xxPresets[id].preset_header.tempr_coef) << 1); + + fs16xx_write_register16(id, FS16XX_OTPACC_REG, FS16XX_OTP_ACC_KEY2); + + if(Fs16xx_Error_OK == fs16xx_wait_dsp_off(id, 0, 100)) { + // Start from position 0 + err |= fs16xx_write_register16(id, FS16XX_DACEQA_REG, 0); + // Write ACS data + for (i = 0; i < gFs16xxPresets[id].preset_header.data_len; i+=2) { + fs16xx_write_register16(id, FS16XX_DACEQWL_REG, ptr_data[i] & 0xFFFF); + fs16xx_write_register16(id, FS16XX_DACEQWH_REG, ptr_data[i + 1] & 0xFF); + //DEBUGPRINT("%s __DEBUG__ DSP write 0x%08X", __func__, ptr_data[i]); + } + + // Start from position 0 + err |= fs16xx_write_register16(id, FS16XX_DACEQA_REG, 0xD2); + // Write prot coef + for (i = 0; i < EXCUR_PROT_RAM_LEN; i++) { + fs16xx_write_register16(id, FS16XX_DACEQWL_REG, ptr_prot_coef[i] & 0xFFFF); + fs16xx_write_register16(id, FS16XX_DACEQWH_REG, (ptr_prot_coef[i] >> 16) & 0xFF); + //PRINT_ERROR("%s __DEBUG__ DSP write 0x%08X", __func__, ptr_prot_coef[i]); + } + + } else { + PRINT_ERROR("%s failed to wait for dsp off!", __func__); + return Fs16xx_Error_StateInvalid; + } + + err |= fs16xx_write_register16(id, FS16XX_OTPACC_REG, 0); + // Recover amp + err |= fs16xx_write_register16(id, FS16XX_SYSCTRL_REG, valPwr); + + return err; +} + +uint16_t fs16xx_calc_checksum(uint16_t *data, int len) { + uint16_t crc = 0; + uint8_t b, index; + int i; + if(len <= 0) return 0; + + for (i = 0; i < len; i++) + { + b = (uint8_t)(data[i] & 0xFF); + index = (uint8_t)(crc ^ b); + crc = (uint16_t)((crc >> 8) ^ gCrc16Table[index]); + + b = (uint8_t)((data[i] >> 8) & 0xFF); + index = (uint8_t)(crc ^ b); + crc = (uint16_t)((crc >> 8) ^ gCrc16Table[index]); + } + return crc; +} + +/* fs16xx_set_preset must be called when osc is on */ +Fs16xx_Error_t fs16xx_set_preset(Fs16xx_devId_t id, int preset) { + Fs16xx_Error_t err = Fs16xx_Error_OK; + uint16_t valPwr, valAcsCtrl, valPll, tmpC0; + int i; + Fs_Dev_State *dev_state = NULL; + Fs16xx_preset_header_t *preset_header = NULL; + + PRINT("%s set preset %d.", __func__, preset); + + if (Fs16xx_Error_OK != fs16xx_handle_is_open(id)) { + PRINT_ERROR("%s device not opened.", __func__); + return Fs16xx_Error_NotOpen; + } + + dev_state = fs16xx_get_dev_state_by_id(id); + if(!dev_state) { + PRINT_ERROR("%s invalid id, exit!", __func__); + return Fs16xx_Error_StateInvalid; + } + + if(!dev_state->dev_preset) { + PRINT_ERROR("%s invalid dev_preset, exit!", __func__); + return Fs16xx_Error_StateInvalid; + } + + if(preset != 0 && preset != 1) { + PRINT_ERROR("%s invalid preset value! preset = %d", __func__, preset); + return Fs16xx_Error_Bad_Parameter; + } + + if(preset == dev_state->cur_preset) { + PRINT("%s cur preset already set %d, exit.", __func__, preset); + return Fs16xx_Error_OK; + } + i = preset; + preset_header = &(dev_state->dev_preset->preset_header); + + if(!dev_state->dev_preset->valid || preset_header == NULL) { + PRINT_ERROR("%s preset invalid! preset = %d preset_header = %p", __func__, preset, preset_header); + return Fs16xx_Error_Bad_Parameter; + } + + // Amp off + err |= fs16xx_read_register16(id, FS16XX_SYSCTRL_REG, &valPwr); + err |= fs16xx_write_register16(id, FS16XX_SYSCTRL_REG, valPwr & (~FS16XX_SYSCTRL_REG_AMPE)); + + if(Fs16xx_Error_OK == fs16xx_wait_dsp_off(id, 0, 50)) { + // Enable osc + err |= fs16xx_read_register16(id, FS16XX_PLLCTRL4_REG, &valPll); + err |= fs16xx_write_register16(id, FS16XX_PLLCTRL4_REG, valPll | FS16XX_PLLCTRL4_REG_OSCEN); + dev_state->tempr_sel = preset_header->tempr_sel; + dev_state->spk_set = preset_header->spk_set; + dev_state->adp_bst = preset_header->adp_bst[i]; + dev_state->bst_ctrl = preset_header->bst_ctrl[i]; + dev_state->acs_drc_ctrl[0] = preset_header->acs_drc[i]; + dev_state->acs_drc_ctrl[1] = preset_header->acs_drcs[i]; + dev_state->acs_drc_ctrl[2] = preset_header->acs_drcp[i]; + // TODO: TS control is not set to register + dev_state->ts_ctrl = preset_header->ts_ctrl[i]; + + err |= fs16xx_write_register16(id, FS16XX_BFLCTRL_REG, preset_header->agc_ctrl[i]); + err |= fs16xx_write_register16(id, FS16XX_BFLSET_REG, preset_header->agc_thd[i]); + err |= fs16xx_write_register16(id, FS16XX_SQC_REG, preset_header->agc_para[i]); + err |= fs16xx_write_register16(id, FS16XX_AGC_REG, preset_header->agc_mg[i]); + err |= fs16xx_write_register16(id, FS16XX_DRPARA_REG, preset_header->dac_para1[i]); + err |= fs16xx_write_register16(id, FS16XX_ACSDRC_REG, dev_state->acs_drc_ctrl[0]); + err |= fs16xx_write_register16(id, FS16XX_ACSDRCS_REG, dev_state->acs_drc_ctrl[1]); + // acs drcp not used. + // err |= fs16xx_write_register16(id, FS16XX_ACSDRCP_REG, dev_state->acs_drc_ctrl[2]); + err |= fs16xx_write_register16(id, FS16XX_ADPBST_REG, dev_state->adp_bst); + + if(preset == 0) { + err |= fs16xx_write_register16(id, FS16XX_BSTCTRL_REG, dev_state->bst_ctrl); + valAcsCtrl = (uint16_t)FS16XX_ACSCTRL_REG_DEFAULT; + } else { + tmpC0 = dev_state->bst_ctrl & (~FS16XX_BSTCTRL_REG_MODE_CTRL_MSK); + tmpC0 |= (0x1 << FS16XX_BSTCTRL_REG_MODE_CTRL_POS); + err |= fs16xx_write_register16(id, FS16XX_BSTCTRL_REG, tmpC0); + valAcsCtrl = (uint16_t)(FS16XX_ACSCTRL_REG_DEFAULT | FS16XX_ACSCTRL_REG_ACS_COE_SEL_MSK); + } + + err |= fs16xx_write_register16(id, FS16XX_ACSCTRL_REG, valAcsCtrl); + dev_state->cur_preset = (int)preset; + + err |= fs16xx_write_register16(id, FS16XX_PLLCTRL4_REG, valPll); + } else { + PRINT_ERROR("%s failed to wait for dsp off!", __func__); + } + + err |= fs16xx_write_register16(id, FS16XX_SYSCTRL_REG, valPwr); + + PRINT("%s exit err=%d. __DEBUG__ boost 0x%04X", __func__, err, + dev_state->bst_ctrl); + return err; +} + diff --git a/asoc/codecs/foursemi/fs16xx_preset.h b/asoc/codecs/foursemi/fs16xx_preset.h new file mode 100644 index 000000000000..870dbfaba1ce --- /dev/null +++ b/asoc/codecs/foursemi/fs16xx_preset.h @@ -0,0 +1,99 @@ +/* +* Copyright (C) 2016 Fourier Semiconductor Inc. +* +*/ + +#ifndef FS16XX_PRESET_H +#define FS16XX_PRESET_H + +#include "fs16xx_define.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PRESET_NAME_SIZE 256 + +// Preset version for 1801 series +#define PRESET_VERSION 0x0A01 + +// Size in int32 +#define EXCUR_PROT_RAM_LEN 0x39 + +#pragma pack(push, 1) +struct fs16xx_preset_header { + uint16_t preset_ver; /* Preset version, should be PRESET_VERSION */ + char customer_name[32]; /* Customer info */ + int64_t create_time; /* Time tick when the preset created */ + uint16_t data_len;/* Length of the data in int16_t */ + uint16_t crc16; /* CRC check sum of the data */ + uint16_t tmax; /* speaker max temprature */ + uint16_t tempr_coef; /* speaker temperature coef */ + uint16_t tempr_sel; /* default value of reg TEMPSEL (0x08)*/ + uint16_t spk_set; /* default value of reg SPKSET (0x0A)*/ + uint16_t adp_bst[2]; /* default value of reg ADPBST (0xBF)*/ + uint16_t bst_ctrl[2]; /* default value of reg BSTCTRL (0xC0)*/ + uint16_t agc_ctrl[2]; /* default value of reg AGCCTRL (0xA7)*/ + uint16_t agc_thd[2]; /* default value of reg AGCTHD (0xA8)*/ + uint16_t agc_para[2]; /* default value of reg AGCPARA (0xA9)*/ + uint16_t agc_mg[2]; /* default value of reg AGCMG (0xAA)*/ + uint16_t dac_para1[2]; /* default value of reg DACPARA1 (0xCD)*/ + uint16_t acs_drc[2]; /* default value of reg ACSDRC (0x8A)*/ + uint16_t acs_drcs[2]; /* default value of reg ACSDRCS (0x8B)*/ + uint16_t acs_drcp[2]; /* default value of reg ACSDRCP (0x8C)*/ + uint16_t ts_ctrl[2]; /* default value of reg TSCTRL (0xAF)*/ + int32_t *excur_prot_ram; /* excur prot coef ram */ + uint16_t *data; /* Point to coef ram data */ +}; +typedef struct fs16xx_preset_header Fs16xx_preset_header_t; + +#define FS_PRESET_HEADER_LEN (sizeof(Fs16xx_preset_header_t) - sizeof(uint16_t *)) + +enum _fs16xx_preset_type { + fs16xx_preset_left = 0, + fs16xx_preset_right = 1, + fs16xx_preset_type_max = 2 // Set to count of preset types +}; +typedef enum _fs16xx_preset_type fs16xx_preset_type; + +struct fs16xx_preset_content { + int valid; + Fs16xx_preset_header_t preset_header; +}; +typedef struct fs16xx_preset_content Fs16xx_preset; +#pragma pack(pop) +/** + * Fs16xx initialize presets + */ +Fs16xx_Error_t fs16xx_initialize_presets(void); + +/** + * Fs16xx deinitialize presets + */ +Fs16xx_Error_t fs16xx_deinitialize_presets(void); + +/** + * Fs16xx load presets from storage + */ +Fs16xx_Error_t fs16xx_load_presets(Fs16xx_devId_t id); + +/** + * Fs16xx load presets to fs16xx device + */ +Fs16xx_Error_t fs16xx_load_presets_to_dev(Fs16xx_devId_t id); + +uint16_t fs16xx_calc_checksum(uint16_t *data, int len); + +/** + * Fs16xx set dsp preset + * @param id to opened instance + * @param preset to set in dsp + */ +Fs16xx_Error_t fs16xx_set_preset(Fs16xx_devId_t id, int preset); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/asoc/codecs/foursemi/fs16xx_reg_def.h b/asoc/codecs/foursemi/fs16xx_reg_def.h new file mode 100644 index 000000000000..937c7ff84af0 --- /dev/null +++ b/asoc/codecs/foursemi/fs16xx_reg_def.h @@ -0,0 +1,81 @@ +/* +* Copyright (C) 2016 Fourier Semiconductor Inc. +* +*/ + +#ifndef FS16XX_REG_DEF_H +#define FS16XX_REG_DEF_H + +#include "fs16xx_regs.h" +#include "fs16xx.h" + +/* I2S_CONTROL bits for sample rates */ +#define FS16XX_I2SCTRL_RATE_08000 (0< Date: Tue, 26 Oct 2021 16:02:04 +0800 Subject: [PATCH 07/40] asoc:add aw88261 codec driver add aw88261 driver; for rhode evb bringup. Change-Id: I1d70441f873a5177b7209cb3e46283a57ceaeb7d Signed-off-by: yaozm1 Reviewed-on: https://gerrit.mot.com/2099634 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/Kbuild | 10 + asoc/codecs/aw88261/aw882xx.c | 2490 +++++++++++++++++++++++++ asoc/codecs/aw88261/aw882xx.h | 184 ++ asoc/codecs/aw88261/aw_bin_parse.c | 512 +++++ asoc/codecs/aw88261/aw_bin_parse.h | 73 + asoc/codecs/aw88261/aw_calib.c | 2206 ++++++++++++++++++++++ asoc/codecs/aw88261/aw_calib.h | 141 ++ asoc/codecs/aw88261/aw_data_type.h | 125 ++ asoc/codecs/aw88261/aw_device.c | 1560 ++++++++++++++++ asoc/codecs/aw88261/aw_device.h | 292 +++ asoc/codecs/aw88261/aw_dsp.c | 1260 +++++++++++++ asoc/codecs/aw88261/aw_dsp.h | 66 + asoc/codecs/aw88261/aw_init.c | 1653 ++++++++++++++++ asoc/codecs/aw88261/aw_log.h | 34 + asoc/codecs/aw88261/aw_monitor.c | 1230 ++++++++++++ asoc/codecs/aw88261/aw_monitor.h | 173 ++ asoc/codecs/aw88261/aw_pid_1852_reg.h | 1899 +++++++++++++++++++ asoc/codecs/aw88261/aw_pid_2013_reg.h | 2126 +++++++++++++++++++++ asoc/codecs/aw88261/aw_pid_2032_reg.h | 1980 ++++++++++++++++++++ asoc/codecs/aw88261/aw_pid_2055_reg.h | 2024 ++++++++++++++++++++ asoc/codecs/aw88261/aw_pid_2071_reg.h | 2038 ++++++++++++++++++++ asoc/codecs/aw88261/aw_pid_2113_reg.h | 2306 +++++++++++++++++++++++ 22 files changed, 24382 insertions(+) create mode 100644 asoc/codecs/aw88261/aw882xx.c create mode 100644 asoc/codecs/aw88261/aw882xx.h create mode 100644 asoc/codecs/aw88261/aw_bin_parse.c create mode 100644 asoc/codecs/aw88261/aw_bin_parse.h create mode 100644 asoc/codecs/aw88261/aw_calib.c create mode 100644 asoc/codecs/aw88261/aw_calib.h create mode 100644 asoc/codecs/aw88261/aw_data_type.h create mode 100644 asoc/codecs/aw88261/aw_device.c create mode 100644 asoc/codecs/aw88261/aw_device.h create mode 100644 asoc/codecs/aw88261/aw_dsp.c create mode 100644 asoc/codecs/aw88261/aw_dsp.h create mode 100644 asoc/codecs/aw88261/aw_init.c create mode 100644 asoc/codecs/aw88261/aw_log.h create mode 100644 asoc/codecs/aw88261/aw_monitor.c create mode 100644 asoc/codecs/aw88261/aw_monitor.h create mode 100644 asoc/codecs/aw88261/aw_pid_1852_reg.h create mode 100644 asoc/codecs/aw88261/aw_pid_2013_reg.h create mode 100644 asoc/codecs/aw88261/aw_pid_2032_reg.h create mode 100644 asoc/codecs/aw88261/aw_pid_2055_reg.h create mode 100644 asoc/codecs/aw88261/aw_pid_2071_reg.h create mode 100644 asoc/codecs/aw88261/aw_pid_2113_reg.h diff --git a/asoc/codecs/Kbuild b/asoc/codecs/Kbuild index 298002e27de1..6ef33da1c951 100644 --- a/asoc/codecs/Kbuild +++ b/asoc/codecs/Kbuild @@ -288,8 +288,18 @@ hdmi_dlkm-y := $(HDMICODEC_OBJS) #Add awinic driver begain ifdef CONFIG_SND_SMARTPA_AW882XX +ifneq (,$(filter rhode%, $(TARGET_PRODUCT))) + SND_SOC_AW882XX_OBJS += aw88261/aw882xx.o + SND_SOC_AW882XX_OBJS += aw88261/aw_init.o + SND_SOC_AW882XX_OBJS += aw88261/aw_bin_parse.o + SND_SOC_AW882XX_OBJS += aw88261/aw_calib.o + SND_SOC_AW882XX_OBJS += aw88261/aw_device.o + SND_SOC_AW882XX_OBJS += aw88261/aw_dsp.o + SND_SOC_AW882XX_OBJS += aw88261/aw_monitor.o +else SND_SOC_AW882XX_OBJS += awinic/aw882xx.o endif +endif obj-$(CONFIG_SND_SMARTPA_AW882XX) += aw882xx_dlkm.o aw882xx_dlkm-y := $(SND_SOC_AW882XX_OBJS) #Add awinic driver end diff --git a/asoc/codecs/aw88261/aw882xx.c b/asoc/codecs/aw88261/aw882xx.c new file mode 100644 index 000000000000..bfaf9c597ece --- /dev/null +++ b/asoc/codecs/aw88261/aw882xx.c @@ -0,0 +1,2490 @@ +/* + * aw882xx.c aw882xx codec module + * + * + * Copyright (c) 2020 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/*#define DEBUG*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aw882xx.h" +#include "aw_log.h" +#include "aw_dsp.h" + +#define AW882XX_DRIVER_VERSION "v1.6.0" +#define AW882XX_I2C_NAME "aw882xxacf_smartpa" + +#define AW_READ_CHIPID_RETRIES 5 /* 5 times */ +#define AW_READ_CHIPID_RETRY_DELAY 5 /* 5 ms */ + +static unsigned int g_aw882xx_dev_cnt = 0; +static unsigned int g_print_dbg = 0; +static unsigned int g_algo_rx_en = false; +static unsigned int g_algo_tx_en = false; +static unsigned int g_algo_copp_en = false; +#ifdef AW_SPIN_ENABLE +static unsigned int g_spin_value = 0; +#endif +#ifdef AW882XX_RUNIN_TEST +static unsigned int g_runin_test; +#endif + +static DEFINE_MUTEX(g_aw882xx_lock); +struct aw_container *g_awinic_cfg = NULL; + +static const char *const switch_status[] = { "Off", "On" }; +static const char *const aw882xx_switch[] = {"Disable", "Enable"}; +#ifdef AW_SPIN_ENABLE +static const char *const aw882xx_spin[] = {"spin_0", "spin_90", + "spin_180", "spin_270"}; +#endif + +/****************************************************** + * + * aw882xx distinguish between codecs and components by version + * + ******************************************************/ +#ifdef AW_KERNEL_VER_OVER_4_19_1 +static struct aw_componet_codec_ops aw_componet_codec_ops = { + .kcontrol_codec = snd_soc_kcontrol_component, + .codec_get_drvdata = snd_soc_component_get_drvdata, + .add_codec_controls = snd_soc_add_component_controls, + .unregister_codec = snd_soc_unregister_component, + .register_codec = snd_soc_register_component, +}; +#else +static struct aw_componet_codec_ops aw_componet_codec_ops = { + .kcontrol_codec = snd_soc_kcontrol_codec, + .codec_get_drvdata = snd_soc_codec_get_drvdata, + .add_codec_controls = snd_soc_add_codec_controls, + .unregister_codec = snd_soc_unregister_codec, + .register_codec = snd_soc_register_codec, +}; +#endif + +static aw_snd_soc_codec_t *aw_get_codec(struct snd_soc_dai *dai) +{ +#ifdef AW_KERNEL_VER_OVER_4_19_1 + return dai->component; +#else + return dai->codec; +#endif +} + + +/****************************************************** + * + * aw882xx i2c write/read + * + ******************************************************/ +int aw882xx_get_version(char *buf, int size) +{ + if (size > strlen(AW882XX_DRIVER_VERSION)) { + memcpy(buf, AW882XX_DRIVER_VERSION, strlen(AW882XX_DRIVER_VERSION)); + return strlen(AW882XX_DRIVER_VERSION); + } else { + return -ENOMEM; + } +} + +int aw882xx_get_dev_num(void) { + return g_aw882xx_dev_cnt; +} + +static int aw882xx_i2c_writes(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned char *buf, unsigned int len) +{ + int ret = -1; + unsigned char *data; + + data = kmalloc(len+1, GFP_KERNEL); + if (data == NULL) { + aw_dev_err(aw882xx->dev, "can not allocate memory"); + return -ENOMEM; + } + + data[0] = reg_addr; + memcpy(&data[1], buf, len); + + ret = i2c_master_send(aw882xx->i2c, data, len+1); + if (ret < 0) + aw_dev_err(aw882xx->dev, "i2c master send error"); + + kfree(data); + + return ret; +} + +static int aw882xx_i2c_reads(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned char *data_buf, unsigned int data_len) +{ + int ret; + struct i2c_msg msg[] = { + [0] = { + .addr = aw882xx->i2c->addr, + .flags = 0, + .len = sizeof(uint8_t), + .buf = ®_addr, + }, + [1] = { + .addr = aw882xx->i2c->addr, + .flags = I2C_M_RD, + .len = data_len, + .buf = data_buf, + }, + }; + + ret = i2c_transfer(aw882xx->i2c->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "transfer failed."); + return ret; + } else if (ret != AW882XX_I2C_READ_MSG_NUM) { + aw_dev_err(aw882xx->dev, "transfer failed(size error)."); + return -ENXIO; + } + + return 0; +} + +int aw882xx_i2c_write(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + unsigned char buf[2]; + + buf[0] = (reg_data&0xff00)>>8; + buf[1] = (reg_data&0x00ff)>>0; + + while (cnt < AW_I2C_RETRIES) { + ret = aw882xx_i2c_writes(aw882xx, reg_addr, buf, 2); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "i2c_write cnt=%d error=%d", + cnt, ret); + } else { + if (g_print_dbg) + aw_dev_info(aw882xx->dev, "reg_addr: 0x%04x, reg_data :0x%04x", + (uint16_t)reg_addr, (uint16_t)reg_data); + break; + } + cnt++; + } + + return ret; +} + +int aw882xx_i2c_read(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int *reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + unsigned char buf[2]; + + while (cnt < AW_I2C_RETRIES) { + ret = aw882xx_i2c_reads(aw882xx, reg_addr, buf, 2); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "i2c_read cnt=%d error=%d", + cnt, ret); + } else { + *reg_data = (buf[0]<<8) | (buf[1]<<0); + if (g_print_dbg) + aw_dev_info(aw882xx->dev, "reg_addr: 0x%04x, reg_data :0x%04x", + (uint16_t)reg_addr, (uint16_t)(*reg_data)); + break; + } + cnt++; + } + + return ret; +} + +int aw882xx_i2c_write_bits(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int mask, unsigned int reg_data) +{ + int ret = -1; + unsigned int reg_val = 0; + + ret = aw882xx_i2c_read(aw882xx, reg_addr, ®_val); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "i2c read error, ret=%d", ret); + return ret; + } + reg_val &= mask; + reg_val |= reg_data; + ret = aw882xx_i2c_write(aw882xx, reg_addr, reg_val); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "i2c read error, ret=%d", ret); + return ret; + } + + return 0; +} +#ifdef CONFIG_AW882XX_STEREO_SMARTPA +static void *aw882xx_devm_kstrdup(struct device *dev, char *buf) +{ + char *str; + + str = devm_kzalloc(dev, strlen(buf) + 1, GFP_KERNEL); + if (!str) + return str; + + memcpy(str, buf, strlen(buf)); + return str; +} +#endif +/***************************************************** + * + * snd_soc_dai_driver ops + * + *****************************************************/ +static int aw882xx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aw_dev_info(aw882xx->dev, "playback enter"); + /*load cali re*/ + aw_dev_init_cali_re(aw882xx->aw_pa); + } else { + aw_dev_info(aw882xx->dev, "capture enter"); + } + return 0; +} + +static int aw882xx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + /*struct aw882xx *aw882xx = aw_snd_soc_codec_get_drvdata(dai->codec);*/ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + + aw_dev_info(codec->dev, "fmt=0x%x", fmt); + + /* Supported mode: regular I2S, slave, or PDM */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != + SND_SOC_DAIFMT_CBS_CFS) { + aw_dev_err(codec->dev, "invalid codec master mode"); + return -EINVAL; + } + break; + default: + aw_dev_err(codec->dev, "unsupported DAI format %d", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + return 0; +} + +static int aw882xx_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + aw_dev_info(aw882xx->dev, "freq=%d", freq); + + aw882xx->sysclk = freq; + return 0; +} + +static int aw882xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + aw_dev_dbg(aw882xx->dev, "stream capture requested rate: %d, sample size: %d", + params_rate(params), params_width(params)); + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aw_dev_dbg(aw882xx->dev, "stream playback requested rate: %d, sample size: %d", + params_rate(params), params_width(params)); + } + + return 0; +} + +static void aw882xx_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aw882xx->rate = 0; + aw_dev_info(aw882xx->dev, "stream playback"); + } else { + aw_dev_info(aw882xx->dev, "stream capture"); + } +} + +static void aw882xx_start_pa(struct aw882xx *aw882xx) +{ + int ret; + int i; + + aw_dev_info(aw882xx->dev, "enter"); + + if (aw882xx->fw_status == AW_DEV_FW_OK) { + if (aw882xx->allow_pw == false) { + aw_dev_info(aw882xx->dev, "dev can not allow power "); + return; + } + + for (i = 0; i < AW_START_RETRIES; i++) { + /*if PA already power ,stop PA then start*/ + if (aw882xx->aw_pa->status) { + aw_dev_info(aw882xx->dev, "already start"); + return; + } + + ret = aw_dev_reg_update(aw882xx->aw_pa, aw882xx->phase_sync); + if (ret) { + aw_dev_err(aw882xx->dev, "fw update failed, cnt:%d", i); + continue; + } + + ret = aw_device_start(aw882xx->aw_pa); + if (ret) { + aw_dev_err(aw882xx->dev, "start failed, cnt:%d", i); + continue; + } else { + if (aw882xx->dc_flag) + queue_delayed_work(aw882xx->work_queue, + &aw882xx->dc_work, + msecs_to_jiffies(AW882XX_DC_DELAY_TIME)); + aw_dev_info(aw882xx->dev, "start success"); +#ifdef AW882XX_RUNIN_TEST + schedule_delayed_work(&aw882xx->adsp_status, msecs_to_jiffies(50)); +#endif + break; + } + } + } else { + aw_dev_info(aw882xx->dev, "dev acf load failed"); + } + +} + +static int aw882xx_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int ret = 0; + + aw_dev_info(aw882xx->dev, "mute state=%d", mute); + + if (stream != SNDRV_PCM_STREAM_PLAYBACK) { + aw_dev_info(aw882xx->dev, "capture"); + return 0; + } + + if (mute) { + aw882xx->pstream = false; + cancel_delayed_work_sync(&aw882xx->dc_work); + cancel_delayed_work_sync(&aw882xx->start_work); + mutex_lock(&aw882xx->lock); + aw_device_stop(aw882xx->aw_pa); + mutex_unlock(&aw882xx->lock); + } else { + if (aw882xx->fw_status == AW_DEV_FW_FAILED) { + aw_dev_info(aw882xx->dev, "fw_load failed ,can not start PA"); + return 0; + } + aw882xx->pstream = true; + mutex_lock(&aw882xx->lock); + /*aw882xx_start_pa(aw882xx);*/ + queue_delayed_work(aw882xx->work_queue, + &aw882xx->start_work, 0); +#ifdef AW_SPIN_ENABLE + if (aw882xx->index == 0) { + ret = aw_dev_set_spin(g_spin_value); + if (ret) + aw_dev_err(aw882xx->dev, "set spin error, ret=%d", ret); + } +#endif + mutex_unlock(&aw882xx->lock); + } + + return ret; +} + +static const struct snd_soc_dai_ops aw882xx_dai_ops = { + .startup = aw882xx_startup, + .set_fmt = aw882xx_set_fmt, + .set_sysclk = aw882xx_set_dai_sysclk, + .hw_params = aw882xx_hw_params, + .mute_stream = aw882xx_mute, + .shutdown = aw882xx_shutdown, +}; + +/***************************************************** + * + * snd_soc_codec_driver | snd_soc_component_driver| + * + *****************************************************/ +static int aw882xx_profile_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count, ret; + char *name = NULL; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + count = aw_dev_get_profile_count(aw882xx->aw_pa); + if (count <= 0) { + uinfo->value.enumerated.items = 0; + aw_dev_err(aw882xx->dev, "get count[%d] failed ", count); + return 0; + } + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + name = uinfo->value.enumerated.name; + count = uinfo->value.enumerated.item; + ret = aw_dev_get_profile_name(aw882xx->aw_pa, name, count); + if (ret) { + strlcpy(uinfo->value.enumerated.name, "null", strlen("null") + 1); + return 0; + } + + return 0; +} + +static int aw882xx_profile_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw_dev_get_profile_index(aw882xx->aw_pa); + aw_dev_dbg(codec->dev, "profile index [%d]", aw_dev_get_profile_index(aw882xx->aw_pa)); + return 0; + +} + +static int aw882xx_profile_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int ret; + int cur_index; + + if (aw882xx->dbg_en_prof == false) { + aw_dev_info(codec->dev, "profile close "); + return 0; + } + + /* check value valid */ + ret = aw_dev_check_profile_index(aw882xx->aw_pa, ucontrol->value.integer.value[0]); + if (ret) { + aw_dev_info(codec->dev, "unsupported index %d", + (int)ucontrol->value.integer.value[0]); + return 0; + } + + /*check cur_index == set value*/ + cur_index = aw_dev_get_profile_index(aw882xx->aw_pa); + if (cur_index == ucontrol->value.integer.value[0]) { + aw_dev_info(codec->dev, "index no change"); + return 0; + } + + mutex_lock(&aw882xx->lock); + aw_dev_set_profile_index(aw882xx->aw_pa, ucontrol->value.integer.value[0]); + /*pstream = 0 no pcm just set status*/ + if (aw882xx->pstream && aw882xx->allow_pw) + aw_dev_prof_update(aw882xx->aw_pa, aw882xx->phase_sync); + mutex_unlock(&aw882xx->lock); + aw_dev_info(codec->dev, "prof id %d", (int)ucontrol->value.integer.value[0]); + return 0; +} + +static int aw882xx_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + count = 2; + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + strlcpy(uinfo->value.enumerated.name, + aw882xx_switch[uinfo->value.enumerated.item], + strlen(aw882xx_switch[uinfo->value.enumerated.item]) + 1); + + return 0; +} + +static int aw882xx_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw882xx->allow_pw; + + return 0; + +} + +static int aw882xx_switch_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + if (aw882xx->pstream) { + if (ucontrol->value.integer.value[0] == 0) { + cancel_delayed_work_sync(&aw882xx->dc_work); + cancel_delayed_work_sync(&aw882xx->start_work); + mutex_lock(&aw882xx->lock); + aw_device_stop(aw882xx->aw_pa); + aw882xx->allow_pw = false; + mutex_unlock(&aw882xx->lock); + aw_dev_info(aw882xx->dev, "stop pa"); + } else { + cancel_delayed_work_sync(&aw882xx->start_work); + mutex_lock(&aw882xx->lock); + aw882xx->allow_pw = true; + if (aw882xx->fw_status == AW_DEV_FW_OK) + aw882xx_start_pa(aw882xx); + else + aw_dev_info(aw882xx->dev, "fw_load failed ,can not start PA"); + mutex_unlock(&aw882xx->lock); + } + } else { + mutex_lock(&aw882xx->lock); + if (ucontrol->value.integer.value[0]) + aw882xx->allow_pw = true; + else + aw882xx->allow_pw = false; + mutex_unlock(&aw882xx->lock); + } + + return 0; +} + +static int aw882xx_dynamic_create_controls(struct aw882xx *aw882xx) +{ + struct snd_kcontrol_new *aw882xx_dev_control = NULL; + char *kctl_name; + + aw882xx_dev_control = devm_kzalloc(aw882xx->codec->dev, sizeof(struct snd_kcontrol_new) * 2, GFP_KERNEL); + if (aw882xx_dev_control == NULL) { + aw_dev_err(aw882xx->codec->dev, "kcontrol malloc failed!"); + return -ENOMEM; + } + + kctl_name = devm_kzalloc(aw882xx->codec->dev, AW_NAME_BUF_MAX, GFP_KERNEL); + if (!kctl_name) + return -ENOMEM; + + snprintf(kctl_name, AW_NAME_BUF_MAX, "aw_dev_%d_prof", aw882xx->index); + + aw882xx_dev_control[0].name = kctl_name; + aw882xx_dev_control[0].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw882xx_dev_control[0].info = aw882xx_profile_info; + aw882xx_dev_control[0].get = aw882xx_profile_get; + aw882xx_dev_control[0].put = aw882xx_profile_set; + + kctl_name = devm_kzalloc(aw882xx->codec->dev, AW_NAME_BUF_MAX, GFP_KERNEL); + if (!kctl_name) + return -ENOMEM; + + snprintf(kctl_name, AW_NAME_BUF_MAX, "aw_dev_%d_switch", aw882xx->index); + + aw882xx_dev_control[1].name = kctl_name; + aw882xx_dev_control[1].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw882xx_dev_control[1].info = aw882xx_switch_info; + aw882xx_dev_control[1].get = aw882xx_switch_get; + aw882xx_dev_control[1].put = aw882xx_switch_set; + + aw_componet_codec_ops.add_codec_controls(aw882xx->codec, + aw882xx_dev_control, 2); + + return 0; +} + +static void aw882xx_request_firmware(struct work_struct *work) +{ + struct aw882xx *aw882xx = + container_of(work, struct aw882xx, fw_work.work); + const struct firmware *cont = NULL; + struct aw_container *aw_cfg = NULL; + int ret = -1; + + aw882xx->fw_status = AW_DEV_FW_FAILED; + ret = request_firmware(&cont, aw882xx->aw_pa->acf_name, aw882xx->dev); + if ((ret) || (!cont)) { + aw_dev_info(aw882xx->dev, "load [%s] failed!", aw882xx->aw_pa->acf_name); + if (aw882xx->fw_retry_cnt == AW_READ_CHIPID_RETRIES) { + aw882xx->fw_retry_cnt = 0; + } else { + aw882xx->fw_retry_cnt++; + /* sleep 1s */ + msleep(1000); + aw_dev_info(aw882xx->dev, "load [%s] try [%d]!", + aw882xx->aw_pa->acf_name, aw882xx->fw_retry_cnt); + aw882xx_request_firmware(work); + } + return; + } + + aw_dev_info(aw882xx->dev, "load [%s] , file size: [%zu]", + aw882xx->aw_pa->acf_name, cont ? cont->size : 0); + + mutex_lock(&g_aw882xx_lock); + if (g_awinic_cfg == NULL) { + aw_cfg = vzalloc(cont->size + sizeof(int)); + if (aw_cfg == NULL) { + release_firmware(cont); + aw_dev_err(aw882xx->dev, "malloc failed"); + mutex_unlock(&g_aw882xx_lock); + return; + } + aw_cfg->len = cont->size; + memcpy(aw_cfg->data, cont->data, cont->size); + release_firmware(cont); + ret = aw_dev_load_acf_check(aw_cfg); + if (ret) { + aw_dev_err(aw882xx->dev, "Load [%s] failed ....!", aw882xx->aw_pa->acf_name); + vfree(aw_cfg); + aw_cfg = NULL; + mutex_unlock(&g_aw882xx_lock); + return; + } + g_awinic_cfg = aw_cfg; + } else { + aw_cfg = g_awinic_cfg; + release_firmware(cont); + aw_dev_info(aw882xx->dev, "[%s] already loaded...", aw882xx->aw_pa->acf_name); + } + mutex_unlock(&g_aw882xx_lock); + + mutex_lock(&aw882xx->lock); + /*aw device init*/ + ret = aw_device_init(aw882xx->aw_pa, aw_cfg); + if (ret < 0) { + aw_dev_info(aw882xx->dev, "dev init failed"); + mutex_unlock(&aw882xx->lock); + return; + } + + /*create kcontrol by profile*/ + aw882xx_dynamic_create_controls(aw882xx); + + aw882xx->fw_status = AW_DEV_FW_OK; + aw882xx->fw_retry_cnt = 0; + + mutex_unlock(&aw882xx->lock); +} + +static void aw882xx_startup_work(struct work_struct *work) +{ + struct aw882xx *aw882xx = container_of(work, struct aw882xx, start_work.work); + + aw_dev_info(aw882xx->dev, "enter"); + + mutex_lock(&aw882xx->lock); + aw882xx_start_pa(aw882xx); + mutex_unlock(&aw882xx->lock); +} + +static void aw882xx_dc_prot_work(struct work_struct *work) +{ + struct aw882xx *aw882xx = container_of(work, struct aw882xx, dc_work.work); + int dc_status = -1; + int dev_status = aw_dev_status(aw882xx->aw_pa); + + if (aw882xx->dc_flag) { + if (dev_status) { + dc_status = aw_dev_dc_status(aw882xx->aw_pa); + if (dc_status > 0) { + cancel_delayed_work_sync(&aw882xx->start_work); + mutex_lock(&aw882xx->lock); + aw_device_stop(aw882xx->aw_pa); + mutex_unlock(&aw882xx->lock); + } else { + queue_delayed_work(aw882xx->work_queue, + &aw882xx->dc_work, + msecs_to_jiffies(AW882XX_DC_DELAY_TIME)); + } + } + } +} + +static void aw882xx_irq_restart(struct aw882xx *aw882xx) +{ + int ret; + + aw_dev_dbg(aw882xx->dev, "enter"); + + mutex_lock(&aw882xx->lock); + + /*stop pa*/ + aw_device_stop(aw882xx->aw_pa); + + /*hw reset*/ + aw882xx_hw_reset(aw882xx); + + /*aw reinit*/ + if (aw882xx->fw_status == AW_DEV_FW_OK) { + ret = aw_device_irq_reinit(aw882xx->aw_pa); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "irq reinit failed"); + goto failed_exit; + } + + if (aw882xx->allow_pw && aw882xx->pstream) { + ret = aw_device_start(aw882xx->aw_pa); + if (ret) { + aw_dev_err(aw882xx->dev, "start failed"); + goto failed_exit; + } + } else { + aw_dev_info(aw882xx->dev, "allow_pw [%d] ,pstream[%d], not start", + aw882xx->allow_pw, aw882xx->pstream); + } + } else { + aw_dev_err(aw882xx->dev, "fw not load ,cannot init device"); + } +failed_exit: + mutex_unlock(&aw882xx->lock); +} + +static void aw882xx_interrupt_work(struct work_struct *work) +{ + struct aw882xx *aw882xx = container_of(work, struct aw882xx, interrupt_work.work); + int16_t reg_value; + int ret; + + aw_dev_info(aw882xx->dev, "enter"); + + /*read reg value*/ + ret = aw_dev_get_int_status(aw882xx->aw_pa, ®_value); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "get init_reg value failed"); + } else { + aw_dev_info(aw882xx->dev, "int value 0x%x", reg_value); + if (aw882xx->aw_pa->ops.aw_get_irq_type) { + ret = aw882xx->aw_pa->ops.aw_get_irq_type(aw882xx->aw_pa, reg_value); + if (ret != INT_TYPE_NONE) { + aw882xx_irq_restart(aw882xx); + return; + } + } + } + + /*clear init reg*/ + aw_dev_clear_int_status(aw882xx->aw_pa); + + /*unmask interrupt*/ + aw_dev_set_intmask(aw882xx->aw_pa, true); +} + +static int aw882xx_set_rx_en(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + struct aw_device *aw_dev = NULL; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + aw_dev = aw882xx->aw_pa; + ctrl_value = ucontrol->value.integer.value[0]; + + ret = aw_dev_set_afe_module_en(AW_RX_MODULE, ctrl_value); + if (ret) + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + + g_algo_rx_en = ctrl_value; + aw_dev_info(aw882xx->dev, "set value %d", ctrl_value); + + if (ctrl_value) { + ret = aw_dev_set_algo_params_path(aw_dev); + if (ret < 0) + aw_dev_err(aw882xx->dev, "set algo params path failed, ret=%d", ret); + } + return 0; +} + +static int aw882xx_get_rx_en(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + + if (aw882xx->pstream) { + ret = aw_dev_get_afe_module_en(AW_RX_MODULE, &ctrl_value); + if (ret) + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + + ucontrol->value.integer.value[0] = ctrl_value; + } else { + ucontrol->value.integer.value[0] = false;//g_algo_rx_en; + aw_dev_info(aw882xx->dev, "no stream, rx disable"); + } + + aw_dev_dbg(aw882xx->dev, "aw882xx_rx_enable %d", + ctrl_value); + return 0; +} + +static int aw882xx_set_tx_en(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + ctrl_value = ucontrol->value.integer.value[0]; + + ret = aw_dev_set_afe_module_en(AW_TX_MODULE, ctrl_value); + if (ret) + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + + g_algo_tx_en = ctrl_value; + aw_dev_info(aw882xx->dev, "set value %d", ctrl_value); + return 0; +} + +static int aw882xx_get_tx_en(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + /*ret = aw_dev_get_afe_module_en(AW_TX_MODULE, &ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + ctrl_value = 0; + } + ucontrol->value.integer.value[0] = ctrl_value;*/ + + if (aw882xx->pstream) { + ret = aw_dev_get_afe_module_en(AW_TX_MODULE, &ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + ctrl_value = 0; + } + ucontrol->value.integer.value[0] = ctrl_value; + } else { + ucontrol->value.integer.value[0] = false;//g_algo_tx_en; + aw_dev_info(aw882xx->dev, "no stream, tx disable"); + } + + aw_dev_dbg(aw882xx->dev, "aw882xx_tx_enable %d", + ctrl_value); + return 0; +} + +static int aw882xx_set_copp_dis(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + ctrl_value = ucontrol->value.integer.value[0]; + + ret = aw_dev_set_copp_module_en(!ctrl_value); + if (ret) + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + + g_algo_copp_en = !ctrl_value; + aw_dev_info(aw882xx->dev, "set value %d", g_algo_copp_en); + + return 0; +} + +static int aw882xx_get_copp_dis(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = !g_algo_copp_en; + + aw_dev_info(aw882xx->dev, "copp_dis: %d", !g_algo_copp_en); + return 0; +} + +void aw882xx_kcontorl_set(struct aw882xx *aw882xx) +{ + int ret; + + ret = aw_dev_set_afe_module_en(AW_RX_MODULE, g_algo_rx_en); + if (ret) + aw_dev_err(aw882xx->dev, "afe set, ret=%d", ret); + + ret = aw_dev_set_copp_module_en(g_algo_copp_en); + if (ret) + aw_dev_err(aw882xx->dev, "copp set error, ret=%d", ret); +} + +#ifdef AW_SPIN_ENABLE +static int aw882xx_set_spin(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + aw_dev = aw882xx->aw_pa; + + ctrl_value = ucontrol->value.integer.value[0]; + if (aw882xx->pstream) { + ret = aw_dev_set_spin(ctrl_value); + if (ret) + aw_dev_err(aw882xx->dev, "set spin error, ret=%d", ret); + } else { + aw_dev_info(aw882xx->dev, "stream no start only record"); + } + + g_spin_value = ctrl_value; + return 0; +} + +static int aw882xx_get_spin(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int ctrl_value; + int ret = -EINVAL; + + aw_dev = aw882xx->aw_pa; + + if (aw882xx->pstream) { + ret = aw_dev_get_spin(&ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "get spin failed!, ret = %d", ret); + ctrl_value = 0; + } + ucontrol->value.integer.value[0] = ctrl_value; + } else { + ucontrol->value.integer.value[0] = g_spin_value; + aw_dev_info(aw882xx->dev, "no stream, use record value"); + + } + aw_dev_dbg(aw882xx->dev, "spin value is %ld", ucontrol->value.integer.value[0]); + return 0; +} +#endif + +static int aw882xx_set_prof_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int ctrl_value; + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + aw_dev = aw882xx->aw_pa; + + ctrl_value = ucontrol->value.integer.value[0]; + + aw_dev_set_algo_prof(aw_dev, ctrl_value); + + return 0; +} + +static int aw882xx_get_prof_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int ctrl_value; + + aw_dev = aw882xx->aw_pa; + + aw_dev_get_algo_prof(aw_dev, &ctrl_value); + + ucontrol->value.integer.value[0] = ctrl_value; + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + return 0; +} + +static int aw882xx_get_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int time; + + aw_dev_get_fade_time(&time, true); + ucontrol->value.integer.value[0] = time; + + aw_pr_dbg("step time %ld", ucontrol->value.integer.value[0]); + + return 0; +} + +static int aw882xx_set_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + if (ucontrol->value.integer.value[0] > mc->max) { + aw_pr_dbg("set val %ld overflow %d", + ucontrol->value.integer.value[0], mc->max); + return 0; + } + aw_dev_set_fade_time(ucontrol->value.integer.value[0], true); + + aw_pr_dbg("step time %ld", ucontrol->value.integer.value[0]); + return 0; +} + +static int aw882xx_get_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int time; + + aw_dev_get_fade_time(&time, false); + ucontrol->value.integer.value[0] = time; + + aw_pr_dbg("step time %ld", ucontrol->value.integer.value[0]); + + return 0; +} + +static int aw882xx_set_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + if (ucontrol->value.integer.value[0] > mc->max) { + aw_pr_dbg("set val %ld overflow %d", + ucontrol->value.integer.value[0], mc->max); + return 0; + } + + aw_dev_set_fade_time(ucontrol->value.integer.value[0], false); + + aw_pr_dbg("step time %ld", ucontrol->value.integer.value[0]); + + return 0; +} + +#ifdef AW882XX_RUNIN_TEST +static int aw882xx_runin_test_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_pr_info("aw882xx_runin test get %d\n", g_runin_test); + + ucontrol->value.integer.value[0] = g_runin_test; + + return 0; +} + +static int aw882xx_runin_test_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int value = ucontrol->value.integer.value[0]; + + aw_pr_info("aw882xx_runin_test set %d \n", value); + + if (value > 1 ) { + value = 0; + } + + g_runin_test = value; + + return 0; +} + +static void aw882xx_set_adsp_module_status(struct work_struct *work) +{ + int ret = 0; + uint32_t ctrl_value = 0; + + /*no test reg set default value*/ + if (g_runin_test == 0) { + return; + } + + /*set afe rx module*/ + ret = aw_dev_set_afe_module_en(AW_RX_MODULE, ctrl_value); + if (ret) { + aw_pr_err("disable afe rx module %d falied , ret=%d\n", ctrl_value, ret); + } + + /*set skt module*/ + ret = aw_dev_set_copp_module_en(ctrl_value); + if (ret) { + aw_pr_err("disable skt failed !\n"); + } + + aw_pr_info("disable skt and afe module success\n"); +} +#endif + +static int aw882xx_monitor_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw882xx->aw_pa; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + + pr_debug("%s: aw882xx_monitor_get %d\n", + __func__, monitor_cfg->monitor_switch); + + ucontrol->value.integer.value[0] = monitor_cfg->monitor_switch; + + return 0; +} + +static int aw882xx_monitor_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw882xx->aw_pa; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + int enable = ucontrol->value.integer.value[0]; + + pr_debug("%s: aw882xx_monitor_set %d \n", __func__, enable); + + mutex_lock(&aw882xx->lock); + if (enable == monitor_cfg->monitor_switch) { + mutex_unlock(&aw882xx->lock); + return 1; + } + + monitor_cfg->monitor_switch = enable; + if (enable) + aw_monitor_start(&aw_dev->monitor_desc); + mutex_unlock(&aw882xx->lock); + + return 0; +} + +static const struct soc_enum aw882xx_snd_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(aw882xx_switch), aw882xx_switch), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(switch_status), switch_status), +#ifdef AW_SPIN_ENABLE + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(aw882xx_spin), aw882xx_spin), +#endif +}; + +static struct snd_kcontrol_new aw882xx_controls[] = { + SOC_ENUM_EXT("aw882xx_rx_switch", aw882xx_snd_enum[0], + aw882xx_get_rx_en, aw882xx_set_rx_en), + SOC_ENUM_EXT("aw882xx_tx_switch", aw882xx_snd_enum[0], + aw882xx_get_tx_en, aw882xx_set_tx_en), + SOC_ENUM_EXT("aw882xx_skt_disable", aw882xx_snd_enum[1], + aw882xx_get_copp_dis, aw882xx_set_copp_dis), +#ifdef AW_SPIN_ENABLE + SOC_ENUM_EXT("aw882xx_spin_switch", aw882xx_snd_enum[2], + aw882xx_get_spin, aw882xx_set_spin), +#endif +#ifdef AW882XX_RUNIN_TEST + SOC_ENUM_EXT("aw882xx_runin_test", aw882xx_snd_enum[1], + aw882xx_runin_test_get, aw882xx_runin_test_set), +#endif + SOC_ENUM_EXT("aw882xx_monitor_switch", aw882xx_snd_enum[0], + aw882xx_monitor_get, aw882xx_monitor_set), + SOC_SINGLE_EXT("aw882xx_fadein_us", 0, 0, 1000000, 0, + aw882xx_get_fade_in_time, aw882xx_set_fade_in_time), + SOC_SINGLE_EXT("aw882xx_fadeout_us", 0, 0, 1000000, 0, + aw882xx_get_fade_out_time, aw882xx_set_fade_out_time), + SOC_SINGLE_EXT("aw882xx_algo_prof_switch", 0, 0, 100, 0, + aw882xx_get_prof_id, aw882xx_set_prof_id), +}; + +static void aw882xx_add_codec_controls(struct aw882xx *aw882xx) +{ + aw_dev_info(aw882xx->dev, "enter"); + + aw_componet_codec_ops.add_codec_controls(aw882xx->codec, + &aw882xx_controls[0], ARRAY_SIZE(aw882xx_controls)); +} + +#ifdef AW_MTK_PLATFORM_WITH_DSP +static int aw882xx_name_append_suffix(struct aw882xx *aw882xx, const char **name) +{ + char buf[50]; + int i2cbus = aw882xx->i2c->adapter->nr; + int i2caddr = aw882xx->i2c->addr; + + snprintf(buf, 50, "%s-%x-%x", *name, i2cbus, i2caddr); + (*name) = aw882xx_devm_kstrdup(aw882xx->dev, buf); + if (!(*name)) + return -ENOMEM; + + aw_dev_info(aw882xx->dev, "name is %s", (*name)); + return 0; +} + +static const struct snd_soc_dapm_widget aw882xx_dapm_widgets[] = { + /* playback */ + SND_SOC_DAPM_AIF_IN("AIF_RX", "Speaker_Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("audio_out"), + /* capture */ + SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_INPUT("iv_in"), +}; + +static const struct snd_soc_dapm_route aw882xx_audio_map[] = { + {"audio_out", NULL, "AIF_RX"}, + {"AIF_TX", NULL, "iv_in"}, +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0) +static struct snd_soc_dapm_context *snd_soc_codec_get_dapm(struct snd_soc_codec *codec) +{ + return &codec->dapm; +} +#endif +#endif + +static void aw882xx_add_widgets(struct aw882xx *aw882xx) +{ +#ifdef AW_MTK_PLATFORM_WITH_DSP + int i = 0; + int ret; + struct snd_soc_dapm_widget *aw_widgets = NULL; + struct snd_soc_dapm_route *aw_route = NULL; +#ifdef AW_KERNEL_VER_OVER_4_19_1 + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(aw882xx->codec); +#else + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(aw882xx->codec); +#endif + + + /*add widgets*/ + aw_widgets = devm_kzalloc(aw882xx->dev, + sizeof(struct snd_soc_dapm_widget) * ARRAY_SIZE(aw882xx_dapm_widgets), + GFP_KERNEL); + if (!aw_widgets) { + aw_dev_err(aw882xx->dev, "alloc widget memory failed!"); + return; + } + memcpy(aw_widgets, aw882xx_dapm_widgets, + sizeof(struct snd_soc_dapm_widget) * ARRAY_SIZE(aw882xx_dapm_widgets)); + + for (i = 0; i < ARRAY_SIZE(aw882xx_dapm_widgets); i++) { + if (aw_widgets[i].name) { + ret = aw882xx_name_append_suffix(aw882xx, &aw_widgets[i].name); + if (ret) { + aw_dev_err(aw882xx->dev, "append widget name suffix failed!"); + return; + } + } + + if (aw_widgets[i].sname) { + ret = aw882xx_name_append_suffix(aw882xx, &aw_widgets[i].sname); + if (ret) { + aw_dev_err(aw882xx->dev, "append widget sname suffix failed!"); + return; + } + } + } + + snd_soc_dapm_new_controls(dapm, aw_widgets, ARRAY_SIZE(aw882xx_dapm_widgets)); + + /*add route*/ + aw_route = devm_kzalloc(aw882xx->dev, + sizeof(struct snd_soc_dapm_route) * ARRAY_SIZE(aw882xx_audio_map), + GFP_KERNEL); + if (!aw_route) { + aw_dev_err(aw882xx->dev, "alloc route memory failed!"); + return; + } + memcpy(aw_route, aw882xx_audio_map, + sizeof(struct snd_soc_dapm_route) * ARRAY_SIZE(aw882xx_audio_map)); + + for (i = 0; i < ARRAY_SIZE(aw882xx_audio_map); i++) { + if (aw_route[i].sink) { + ret = aw882xx_name_append_suffix(aw882xx, &aw_route[i].sink); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "append sink name suffix failed!"); + return; + } + } + + if (aw_route[i].source) { + ret = aw882xx_name_append_suffix(aw882xx, &aw_route[i].source); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "append source name suffix failed!"); + return; + } + } + } + snd_soc_dapm_add_routes(dapm, aw_route, ARRAY_SIZE(aw882xx_audio_map)); +#endif +} + +static void aw882xx_load_fw(struct aw882xx *aw882xx) +{ +#ifdef CONFIG_AW882XX_DSP + aw882xx_request_firmware(&aw882xx->fw_work.work); +#else + queue_delayed_work(aw882xx->work_queue, + &aw882xx->fw_work, + msecs_to_jiffies(AW882XX_LOAD_FW_DELAY_TIME)); +#endif +} + +static int aw882xx_codec_probe(aw_snd_soc_codec_t *aw_codec) +{ + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(aw_codec); + aw_dev_info(aw882xx->dev, "enter"); + + aw882xx->work_queue = create_singlethread_workqueue("aw882xx"); + if (!aw882xx->work_queue) { + aw_dev_err(aw882xx->dev, "create workqueue failed !"); + return -EINVAL; + } + + INIT_DELAYED_WORK(&aw882xx->start_work, aw882xx_startup_work); + INIT_DELAYED_WORK(&aw882xx->interrupt_work, aw882xx_interrupt_work); + INIT_DELAYED_WORK(&aw882xx->dc_work, aw882xx_dc_prot_work); + INIT_DELAYED_WORK(&aw882xx->fw_work, aw882xx_request_firmware); + + aw882xx->codec = aw_codec; + + if (aw882xx->index == 0) + aw882xx_add_codec_controls(aw882xx); + + aw882xx_add_widgets(aw882xx); + + /*load fw bin*/ + aw882xx_load_fw(aw882xx); + + /*load cali re*/ + aw_dev_init_cali_re(aw882xx->aw_pa); + + return 0; +} + +#ifdef AW_KERNEL_VER_OVER_4_19_1 +static void aw882xx_codec_remove(aw_snd_soc_codec_t *aw_codec) +{ + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(aw_codec); + aw_dev_info(aw882xx->dev, "enter"); + aw_dev_deinit(aw882xx->aw_pa); + destroy_workqueue(aw882xx->work_queue); + aw882xx->work_queue = NULL; +} +#else +static int aw882xx_codec_remove(aw_snd_soc_codec_t *aw_codec) +{ + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(aw_codec); + + aw_dev_info(aw882xx->dev, "enter"); + aw_dev_deinit(aw882xx->aw_pa); + destroy_workqueue(aw882xx->work_queue); + aw882xx->work_queue = NULL; + return 0; +} +#endif + +#ifdef CONFIG_AW882XX_STEREO_SMARTPA +static int aw882xx_dai_drv_append_suffix(struct aw882xx *aw882xx, + struct snd_soc_dai_driver *dai_drv, + int num_dai) +{ + char buf[50]; + int i; + int i2cbus = aw882xx->i2c->adapter->nr; + int addr = aw882xx->i2c->addr; + + if ((dai_drv != NULL) && (num_dai > 0)) + for (i = 0; i < num_dai; i++) { + snprintf(buf, 50, "%s-%x-%x", dai_drv[i].name, i2cbus, + addr); + dai_drv[i].name = aw882xx_devm_kstrdup(aw882xx->dev, buf); + + snprintf(buf, 50, "%s-%x-%x", + dai_drv[i].playback.stream_name, + i2cbus, addr); + dai_drv[i].playback.stream_name = aw882xx_devm_kstrdup(aw882xx->dev, buf); + + snprintf(buf, 50, "%s-%x-%x", + dai_drv[i].capture.stream_name, + i2cbus, addr); + dai_drv[i].capture.stream_name = aw882xx_devm_kstrdup(aw882xx->dev, buf); + aw_dev_info(aw882xx->dev, "dai name [%s]", dai_drv[i].name); + aw_dev_info(aw882xx->dev, "pstream_name name [%s]", dai_drv[i].playback.stream_name); + aw_dev_info(aw882xx->dev, "cstream_name name [%s]", dai_drv[i].capture.stream_name); + } + + return 0; +} +#endif + +static struct snd_soc_dai_driver aw882xx_dai[] = { + { + .name = "aw882xx-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE), + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE), + }, + .ops = &aw882xx_dai_ops, + /* .symmetric_rates = 1,*/ + }, +}; + + +#ifdef AW_KERNEL_VER_OVER_4_19_1 +static struct snd_soc_component_driver soc_codec_dev_aw882xx = { + .probe = aw882xx_codec_probe, + .remove = aw882xx_codec_remove, +}; +#else +static struct snd_soc_codec_driver soc_codec_dev_aw882xx = { + .probe = aw882xx_codec_probe, + .remove = aw882xx_codec_remove, +}; +#endif + +int aw_componet_codec_register(struct aw882xx *aw882xx) +{ + struct snd_soc_dai_driver *dai_drv; + int ret; + dai_drv = devm_kzalloc(aw882xx->dev, sizeof(aw882xx_dai), GFP_KERNEL); + if (dai_drv == NULL) { + aw_dev_err(aw882xx->dev, "dai_driver malloc failed"); + return -ENOMEM; + } + + memcpy(dai_drv, aw882xx_dai, sizeof(aw882xx_dai)); + +#ifdef CONFIG_AW882XX_STEREO_SMARTPA + aw882xx_dai_drv_append_suffix(aw882xx, dai_drv, ARRAY_SIZE(aw882xx_dai)); +#endif + + ret = aw882xx->codec_ops->register_codec(aw882xx->dev, + &soc_codec_dev_aw882xx, + dai_drv, ARRAY_SIZE(aw882xx_dai)); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "failed to register aw882xx: %d", ret); + return -EINVAL; + } + return 0; +} +/***************************************************** + * + * device tree + * + *****************************************************/ +static int aw882xx_parse_gpio_dt(struct aw882xx *aw882xx, + struct device_node *np) +{ + if (!np) { + aw882xx->reset_gpio = -1; + aw882xx->irq_gpio = -1; + return -EINVAL; + } + + aw882xx->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (aw882xx->reset_gpio < 0) { + aw_dev_info(aw882xx->dev, "no reset gpio provided, will not HW reset device"); + } else { + aw_dev_info(aw882xx->dev, "reset gpio provided ok"); + } + aw882xx->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (aw882xx->irq_gpio < 0) { + aw_dev_info(aw882xx->dev, "no irq gpio provided."); + } else { + aw_dev_info(aw882xx->dev, "irq gpio provided ok."); + } + + return 0; +} + +static struct aw882xx *aw882xx_malloc_init(struct i2c_client *i2c) +{ + struct aw882xx *aw882xx = devm_kzalloc(&i2c->dev, sizeof(struct aw882xx), GFP_KERNEL); + if (aw882xx == NULL) { + dev_err(&i2c->dev, "devm_kzalloc failed."); + return NULL; + } + + aw882xx->dev = &i2c->dev; + aw882xx->i2c = i2c; + aw882xx->aw_pa = NULL; + aw882xx->codec = NULL; + aw882xx->codec_ops = &aw_componet_codec_ops; + aw882xx->fw_status = AW_DEV_FW_FAILED; + aw882xx->fw_retry_cnt = 0; + aw882xx->dbg_en_prof = true; + aw882xx->allow_pw = true; + aw882xx->work_queue = NULL; + + mutex_init(&aw882xx->lock); + + return aw882xx; +} + +static int aw882xx_gpio_request(struct aw882xx *aw882xx) +{ + int ret; + if (gpio_is_valid(aw882xx->reset_gpio)) { + ret = devm_gpio_request_one(aw882xx->dev, aw882xx->reset_gpio, + GPIOF_OUT_INIT_LOW, "aw882xx_rst"); + if (ret) { + aw_dev_err(aw882xx->dev, "rst request failed"); + return ret; + } + } + + if (gpio_is_valid(aw882xx->irq_gpio)) { + ret = devm_gpio_request_one(aw882xx->dev, aw882xx->irq_gpio, + GPIOF_DIR_IN, "aw882xx_int"); + if (ret) { + aw_dev_err(aw882xx->dev, "int request failed"); + return ret; + } + } + + return 0; +} + +static int aw882xx_parse_dt(struct device *dev, struct aw882xx *aw882xx, + struct device_node *np) +{ + int ret; + int32_t dc_enable = 0; + int32_t sync_enable = 0; + + /*gpio dts parser*/ + ret = aw882xx_parse_gpio_dt(aw882xx, np); + if (ret) + return ret; + + ret = of_property_read_u32(np, "dc-flag", &dc_enable); + if (ret) { + dc_enable = false; + aw_dev_info(aw882xx->dev, "close dc protect!"); + } else { + aw_dev_info(aw882xx->dev, "dc-flag = %d", dc_enable); + } + + aw882xx->dc_flag = dc_enable; + + + ret = of_property_read_u32(np, "sync-flag", &sync_enable); + if (ret < 0) { + aw_dev_info(aw882xx->dev, + "read sync flag failed,default phase sync off"); + sync_enable = false; + } else { + aw_dev_info(aw882xx->dev, + "sync flag is %d", sync_enable); + } + + aw882xx->phase_sync = sync_enable; + + ret = of_property_read_u32(np, "fade-flag", &aw882xx->fade_flag); + if (ret) { + aw882xx->fade_flag = 0; + dev_err(dev, "%s: fade_flag get failed,use default value!\n", __func__); + } else { + dev_info(dev, "%s: fade_flag = %d\n", + __func__, aw882xx->fade_flag); + } + + return 0; +} + +int aw882xx_hw_reset(struct aw882xx *aw882xx) +{ + aw_dev_info(aw882xx->dev, "enter"); + + if (gpio_is_valid(aw882xx->reset_gpio)) { + gpio_set_value_cansleep(aw882xx->reset_gpio, 0); + mdelay(1); + gpio_set_value_cansleep(aw882xx->reset_gpio, 1); + mdelay(2); + } else { + aw_dev_info(aw882xx->dev, "has no reset gpio"); + } + return 0; +} + +static int aw882xx_read_chipid(struct aw882xx *aw882xx) +{ + int ret = -1; + unsigned int cnt = 0; + unsigned int reg_value = 0; + + while (cnt < AW_READ_CHIPID_RETRIES) { + ret = aw882xx_i2c_read(aw882xx, AW882XX_CHIP_ID_REG, ®_value); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "failed to read REG_ID: %d", ret); + return -EIO; + } + switch (reg_value) { + case PID_1852_ID: { + aw_dev_info(aw882xx->dev, "aw882xx 1852 detected"); + aw882xx->chip_id = reg_value; + return 0; + } + case PID_2013_ID: { + aw_dev_info(aw882xx->dev, "aw882xx 2013 detected"); + aw882xx->chip_id = reg_value; + return 0; + } + case PID_2032_ID: { + aw_dev_info(aw882xx->dev, "aw882xx 2032 detected"); + aw882xx->chip_id = reg_value; + return 0; + } + case PID_2055_ID: { + aw_dev_info(aw882xx->dev, "aw882xx 2055 detected"); + aw882xx->chip_id = reg_value; + return 0; + } + case PID_2071_ID: { + aw_dev_info(aw882xx->dev, "aw882xx 2071 detected"); + aw882xx->chip_id = reg_value; + return 0; + } + case PID_2113_ID: { + aw_dev_info(aw882xx->dev, "aw882xx 2113 detected"); + aw882xx->chip_id = reg_value; + return 0; + } + default: + aw_dev_info(aw882xx->dev, "unsupported device revision (0x%x)", + reg_value); + break; + } + cnt++; + + msleep(AW_READ_CHIPID_RETRY_DELAY); + } + + return -EINVAL; +} + +static irqreturn_t aw882xx_irq(int irq, void *data) +{ + struct aw882xx *aw882xx = (struct aw882xx *)data; + if (!aw882xx) { + aw_pr_err("pointer is NULL"); + return -EINVAL; + } + aw_dev_info(aw882xx->dev, "enter"); + + /* mask all irq */ + aw_dev_set_intmask(aw882xx->aw_pa, false); + + /* upload workqueue */ + if (aw882xx->work_queue) + queue_delayed_work(aw882xx->work_queue, &aw882xx->interrupt_work, 0); + + return IRQ_HANDLED; +} + +static int aw882xx_interrupt_init(struct aw882xx *aw882xx) +{ + int irq_flags; + int ret; + + if (gpio_is_valid(aw882xx->irq_gpio)) { + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(aw882xx->dev, + gpio_to_irq(aw882xx->irq_gpio), + NULL, aw882xx_irq, irq_flags, + "aw882xx", aw882xx); + if (ret != 0) { + aw_dev_err(aw882xx->dev, "Failed to request IRQ %d: %d", + gpio_to_irq(aw882xx->irq_gpio), ret); + return ret; + } + } else { + aw_dev_info(aw882xx->dev, "gpio invalid"); + /* disable interrupt */ + } + + return 0; +} + +static ssize_t aw882xx_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + unsigned int databuf[2] = {0}; + + if (2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) + aw882xx_i2c_write(aw882xx, databuf[0], databuf[1]); + + return count; +} + +static ssize_t aw882xx_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned char i = 0; + unsigned int reg_val = 0; + int reg_num = aw882xx->aw_pa->ops.aw_get_reg_num(); + + for (i = 0; i < reg_num; i++) { + if (aw882xx->aw_pa->ops.aw_check_rd_access(i)) { + aw882xx_i2c_read(aw882xx, i, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, + "reg:0x%02x=0x%04x\n", i, reg_val); + } + } + + return len; +} + +static ssize_t aw882xx_rw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0}; + + if (2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) { + aw882xx->rw_reg_addr = (unsigned char)databuf[0]; + if (aw882xx->aw_pa->ops.aw_check_rd_access(databuf[0])) + aw882xx_i2c_write(aw882xx, databuf[0], databuf[1]); + } else if (1 == sscanf(buf, "%x", &databuf[0])) { + aw882xx->rw_reg_addr = (unsigned char)databuf[0]; + } + + return count; +} + +static ssize_t aw882xx_rw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned int reg_val = 0; + + if (aw882xx->aw_pa->ops.aw_check_rd_access(aw882xx->rw_reg_addr)) { + aw882xx_i2c_read(aw882xx, aw882xx->rw_reg_addr, ®_val); + len += snprintf(buf + len, PAGE_SIZE - len, + "reg:0x%02x=0x%04x\n", aw882xx->rw_reg_addr, reg_val); + } + return len; +} + + int aw882xx_awrw_write(struct aw882xx *aw882xx, const char *buf, size_t count) +{ + int i, ret; + char *data_buf = NULL; + int str_len, data_len, temp_data; + char *reg_data; + struct aw882xx_i2c_packet *packet = &aw882xx->i2c_packet; + + aw_dev_info(aw882xx->dev, "write:reg_addr[0x%02x], reg_num[%d]", + packet->reg_addr, packet->reg_num); + + data_len = AWRW_DATA_BYTES * packet->reg_num; + + str_len = count - AWRW_HDR_LEN - 1; + + if ((data_len * 5 - 1) > str_len) { + aw_dev_err(aw882xx->dev, "data_str_len [%d], requeset len [%d]", + str_len, (data_len * 5 - 1)); + return -EINVAL; + } + + data_buf = kmalloc(data_len + 1, GFP_KERNEL); + if (data_buf == NULL) { + aw_dev_err(aw882xx->dev, "alloc memory failed"); + return -ENOMEM; + } + + data_buf[0] = packet->reg_addr; + reg_data = data_buf + 1; + + aw_dev_dbg(aw882xx->dev, "reg_addr: 0x%02x", data_buf[0]); + + for (i = 0; i < data_len; i++) { + sscanf(buf + AWRW_HDR_LEN + 1 + i * 5, "0x%02x", &temp_data); + reg_data[i] = temp_data; + aw_dev_dbg(aw882xx->dev, "[%d] : 0x%02x", i, reg_data[i]); + } + + ret = i2c_master_send(aw882xx->i2c, data_buf, data_len + 1); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "write failed"); + kfree(data_buf); + data_buf = NULL; + return -EFAULT; + } + + kfree(data_buf); + data_buf = NULL; + + aw_dev_info(aw882xx->dev, "write success"); + return 0; +} + +static int aw882xx_awrw_data_check(struct aw882xx *aw882xx, int *data) +{ + int reg_num_max = aw882xx->aw_pa->ops.aw_get_reg_num(); + + if ((data[AWRW_HDR_ADDR_BYTES] != AWRW_ADDR_BYTES) || + (data[AWRW_HDR_DATA_BYTES] != AWRW_DATA_BYTES)) { + aw_dev_err(aw882xx->dev, "addr_bytes [%d] or data_bytes [%d] unsupport", + data[AWRW_HDR_ADDR_BYTES], data[AWRW_HDR_DATA_BYTES]); + return -EINVAL; + } + + if (data[AWRW_HDR_REG_ADDR] >= reg_num_max) { + aw_dev_err(aw882xx->dev, "reg_addr[%d] > reg_max[%d]", + data[AWRW_HDR_REG_ADDR], reg_num_max); + return -EINVAL; + } + + return 0; +} + +/* flag addr_bytes data_bytes reg_num reg_addr*/ +static int aw882xx_awrw_parse_buf(struct aw882xx *aw882xx, + const char *buf, size_t count) +{ + int data[AWRW_HDR_MAX] = {0}; + struct aw882xx_i2c_packet *packet = &aw882xx->i2c_packet; + int ret; + + if (sscanf(buf, "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", + &data[AWRW_HDR_WR_FLAG], + &data[AWRW_HDR_ADDR_BYTES], + &data[AWRW_HDR_DATA_BYTES], + &data[AWRW_HDR_REG_NUM], + &data[AWRW_HDR_REG_ADDR]) == 5) { + ret = aw882xx_awrw_data_check(aw882xx, data); + if (ret < 0) + return ret; + + packet->reg_addr = data[AWRW_HDR_REG_ADDR]; + packet->reg_num = data[AWRW_HDR_REG_NUM]; + + if (data[AWRW_HDR_WR_FLAG] == AWRW_FLAG_WRITE) { + return aw882xx_awrw_write(aw882xx, buf, count); + } else if (data[AWRW_HDR_WR_FLAG] == AWRW_FLAG_READ) { + packet->status = AWRW_I2C_ST_READ; + aw_dev_info(aw882xx->dev, "read_cmd:reg_addr[0x%02x], reg_num[%d]", + packet->reg_addr, packet->reg_num); + return 0; + } else { + aw_dev_err(aw882xx->dev, "please check str format, unsupport flag %d", + data[AWRW_HDR_WR_FLAG]); + return -EINVAL; + } + } else { + aw_dev_err(aw882xx->dev, "can not parse string"); + return -EINVAL; + } + +} + +static ssize_t aw882xx_awrw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + int ret; + + if (count < AWRW_HDR_LEN) { + aw_dev_err(dev, "data count too smaller, please check write format"); + aw_dev_err(dev, "string %s", buf); + return -EINVAL; + } + + ret = aw882xx_awrw_parse_buf(aw882xx, buf, count); + if (ret) + return -EINVAL; + + return count; +} + +static ssize_t aw882xx_awrw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw882xx_i2c_packet *packet = &aw882xx->i2c_packet; + int data_len, len = 0; + int ret, i; + char *reg_data = NULL; + + if (packet->status != AWRW_I2C_ST_READ) { + aw_dev_err(aw882xx->dev, "please write read cmd first"); + return -EINVAL; + } + + data_len = AWRW_DATA_BYTES * packet->reg_num; + reg_data = (char *)kmalloc(data_len, GFP_KERNEL); + if (reg_data == NULL) { + aw_dev_err(aw882xx->dev, "memory alloc failed"); + ret = -EINVAL; + goto exit; + } + + ret = aw882xx_i2c_reads(aw882xx, packet->reg_addr, (char *)reg_data, data_len); + if (ret < 0) { + ret = -EFAULT; + goto exit; + } + + aw_dev_info(aw882xx->dev, "reg_addr 0x%02x, reg_num %d", + packet->reg_addr, packet->reg_num); + + for (i = 0; i < data_len; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "0x%02x,", reg_data[i]); + aw_dev_dbg(aw882xx->dev, "0x%02x", reg_data[i]); + } + + ret = len; + +exit: + if (reg_data) + kfree(reg_data); + + packet->status = AWRW_I2C_ST_NONE; + return ret; +} + + +static ssize_t aw882xx_drv_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "driver_ver: %s \n", AW882XX_DRIVER_VERSION); + + return len; +} + +static ssize_t aw882xx_dsp_re_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + int cali_re; + int ret; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + ret = aw_dev_get_cali_re(aw882xx->aw_pa, &cali_re); + if (ret) { + len += snprintf(buf + len, PAGE_SIZE - len, + "read dsp_re failed!\n"); + return len; + } + + len += snprintf(buf + len, PAGE_SIZE - len, + "%d \n", cali_re); + + return len; +} + +static ssize_t aw882xx_fade_step_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0}; + + /*step 0 - 12*/ + if (1 == sscanf(buf, "%d", &databuf[0])) { + if (databuf[0] > (aw882xx->aw_pa->volume_desc.mute_volume)) { + aw_dev_info(aw882xx->dev, "step overflow %d Db", databuf[0]); + return count; + } + aw_dev_set_fade_vol_step(aw882xx->aw_pa, databuf[0]); + } + aw_dev_info(aw882xx->dev, "set step %d Done", databuf[0]); + + return count; +} + +static ssize_t aw882xx_fade_step_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "step: %d \n", aw_dev_get_fade_vol_step(aw882xx->aw_pa)); + + return len; +} + +static ssize_t aw882xx_dbg_prof_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0}; + + if (1 == sscanf(buf, "%d", &databuf[0])) { + if (databuf[0]) + aw882xx->dbg_en_prof = true; + else + aw882xx->dbg_en_prof = false; + } + aw_dev_info(aw882xx->dev, "en_prof %d Done", databuf[0]); + + return count; +} + +static ssize_t aw882xx_dbg_prof_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + " %d \n", aw882xx->dbg_en_prof); + + return len; +} + +static ssize_t aw882xx_sync_flag_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + unsigned int flag = 0; + int ret; + + ret = kstrtouint(buf, 0, &flag); + if (ret < 0) + return ret; + + flag = ((flag == false) ? false : true); + + aw_dev_info(aw882xx->dev, "set phase sync flag : [%d]", flag); + + aw882xx->phase_sync = flag; + + return count; +} + +static ssize_t aw882xx_sync_flag_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "sync flag : %d\n", aw882xx->phase_sync); + + return len; +} + +static ssize_t aw882xx_print_dbg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + int ret; + + ret = kstrtouint(buf, 0, &g_print_dbg); + if (ret < 0) + return ret; + + g_print_dbg = ((g_print_dbg == false) ? false : true); + + aw_dev_info(aw882xx->dev, "set g_print_dbg : [%d]", g_print_dbg); + + return count; +} + +static ssize_t aw882xx_print_dbg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "g_print_dbg : %d\n", g_print_dbg); + + return len; +} + +static ssize_t aw882xx_algo_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + int ret; + char algo_ver_buf[ALGO_VERSION_MAX] = { 0 }; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + ret = aw_get_algo_version(aw882xx->aw_pa, algo_ver_buf); + if (ret < 0) { + len += snprintf(buf + len, PAGE_SIZE - len, + "read algo version failed!\n"); + return len; + } + len += snprintf(buf + len, PAGE_SIZE - len, "%s\n", algo_ver_buf); + + return len; +} + +static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, + aw882xx_reg_show, aw882xx_reg_store); +static DEVICE_ATTR(rw, S_IWUSR | S_IRUGO, + aw882xx_rw_show, aw882xx_rw_store); +static DEVICE_ATTR(awrw, S_IWUSR | S_IRUGO, + aw882xx_awrw_show, aw882xx_awrw_store); +static DEVICE_ATTR(drv_ver, S_IRUGO, + aw882xx_drv_ver_show, NULL); +static DEVICE_ATTR(dsp_re, S_IRUGO, + aw882xx_dsp_re_show, NULL); +static DEVICE_ATTR(fade_step, S_IWUSR | S_IRUGO, + aw882xx_fade_step_show, aw882xx_fade_step_store); +static DEVICE_ATTR(dbg_prof, S_IWUSR | S_IRUGO, + aw882xx_dbg_prof_show, aw882xx_dbg_prof_store); +static DEVICE_ATTR(phase_sync, S_IWUSR | S_IRUGO, + aw882xx_sync_flag_show, aw882xx_sync_flag_store); +static DEVICE_ATTR(print_dbg, S_IWUSR | S_IRUGO, + aw882xx_print_dbg_show, aw882xx_print_dbg_store); +static DEVICE_ATTR(algo_ver, S_IRUGO, + aw882xx_algo_ver_show, NULL); + + +static struct attribute *aw882xx_attributes[] = { + &dev_attr_reg.attr, + &dev_attr_rw.attr, + &dev_attr_awrw.attr, + &dev_attr_drv_ver.attr, + &dev_attr_fade_step.attr, + &dev_attr_dbg_prof.attr, + &dev_attr_dsp_re.attr, + &dev_attr_phase_sync.attr, + &dev_attr_print_dbg.attr, + &dev_attr_algo_ver.attr, + NULL +}; + +static struct attribute_group aw882xx_attribute_group = { + .attrs = aw882xx_attributes, +}; + +static int aw882xx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct aw882xx *aw882xx; + struct device_node *np = i2c->dev.of_node; + int ret; + + aw_pr_info("enter addr=0x%x", i2c->addr); + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + aw_dev_err(&i2c->dev, "check_functionality failed"); + return -EIO; + } + + /*dev free all auto free*/ + aw882xx = aw882xx_malloc_init(i2c); + if (aw882xx == NULL) { + aw_dev_err(&i2c->dev, "malloc aw882xx failed"); + return -ENOMEM; + } + + i2c_set_clientdata(i2c, aw882xx); + + ret = aw882xx_parse_dt(&i2c->dev, aw882xx, np); + if (ret) { + aw_dev_err(&i2c->dev, "failed to parse device tree node"); + return ret; + } + + /*get gpio resource*/ + ret = aw882xx_gpio_request(aw882xx); + if (ret) + return ret; + + /* hardware reset */ + aw882xx_hw_reset(aw882xx); + + /* aw882xx chip id */ + ret = aw882xx_read_chipid(aw882xx); + if (ret < 0) { + aw_dev_err(&i2c->dev, "aw882xx_read_chipid failed ret=%d", ret); + return ret; + } + + /*aw pa init*/ + ret = aw882xx_init(aw882xx, g_aw882xx_dev_cnt); + if (ret) + return ret; + + /*aw882xx irq*/ + aw882xx_interrupt_init(aw882xx); + + /*codec register*/ + ret = aw_componet_codec_register(aw882xx); + if (ret) { + aw_dev_err(&i2c->dev, "codec register failde"); + return ret; + } + + /*create attr*/ + ret = sysfs_create_group(&i2c->dev.kobj, &aw882xx_attribute_group); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "error creating sysfs attr files"); + goto err_sysfs; + } + + /*set aw882xx to dev private*/ + dev_set_drvdata(&i2c->dev, aw882xx); + + /*i2c packet init*/ + aw882xx->i2c_packet.status = AWRW_I2C_ST_NONE; + aw882xx->i2c_packet.reg_num = 0; + aw882xx->i2c_packet.reg_addr = 0xff; + aw882xx->i2c_packet.reg_data = NULL; + + aw882xx->index = g_aw882xx_dev_cnt; +#ifdef AW882XX_RUNIN_TEST + g_runin_test = 0; + INIT_DELAYED_WORK(&aw882xx->adsp_status, aw882xx_set_adsp_module_status); +#endif + /*add device to total list*/ + mutex_lock(&g_aw882xx_lock); + g_aw882xx_dev_cnt++; + mutex_unlock(&g_aw882xx_lock); + aw_dev_info(&i2c->dev, "dev_cnt %d", g_aw882xx_dev_cnt); + return ret; +err_sysfs: + aw_componet_codec_ops.unregister_codec(&i2c->dev); + + return ret; +} + +static int aw882xx_i2c_remove(struct i2c_client *i2c) +{ + struct aw882xx *aw882xx = i2c_get_clientdata(i2c); + + aw_dev_info(aw882xx->dev, "enter"); + + /*rm irq*/ + if (gpio_to_irq(aw882xx->irq_gpio)) + devm_free_irq(&i2c->dev, + gpio_to_irq(aw882xx->irq_gpio), + aw882xx); + + /*free gpio*/ + if (gpio_is_valid(aw882xx->irq_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->irq_gpio); + if (gpio_is_valid(aw882xx->reset_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->reset_gpio); + + /*rm attr node*/ + sysfs_remove_group(&i2c->dev.kobj, &aw882xx_attribute_group); + + /*free device resource */ + aw_device_remove(aw882xx->aw_pa); + + /*unregister codec*/ + aw882xx->codec_ops->unregister_codec(&i2c->dev); + + /*remove device to total list*/ + mutex_lock(&g_aw882xx_lock); + g_aw882xx_dev_cnt--; + if (g_aw882xx_dev_cnt == 0) { + if (g_awinic_cfg) { + vfree(g_awinic_cfg); + g_awinic_cfg = NULL; + } + } + mutex_unlock(&g_aw882xx_lock); + + return 0; + +} + +static void aw882xx_i2c_shutdown(struct i2c_client *i2c) +{ + struct aw882xx *aw882xx = i2c_get_clientdata(i2c); + + aw_dev_info(aw882xx->dev, "enter"); + mutex_lock(&aw882xx->lock); + aw_device_stop(aw882xx->aw_pa); + mutex_unlock(&aw882xx->lock); +} + +static const struct i2c_device_id aw882xx_i2c_id[] = { + { AW882XX_I2C_NAME, 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, aw882xx_i2c_id); + +static struct of_device_id aw882xx_dt_match[] = { + { .compatible = "awinic,aw882xx_smartpa" }, + { }, +}; + +static struct i2c_driver aw882xx_i2c_driver = { + .driver = { + .name = AW882XX_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw882xx_dt_match), + }, + .probe = aw882xx_i2c_probe, + .remove = aw882xx_i2c_remove, + .shutdown = aw882xx_i2c_shutdown, + .id_table = aw882xx_i2c_id, +}; + + +static int __init aw882xx_i2c_init(void) +{ + int ret = -1; + + aw_pr_info("aw882xx driver version %s", AW882XX_DRIVER_VERSION); + + ret = i2c_add_driver(&aw882xx_i2c_driver); + if (ret) + aw_pr_err("fail to add aw882xx device into i2c"); + + return ret; +} +module_init(aw882xx_i2c_init); + + +static void __exit aw882xx_i2c_exit(void) +{ + i2c_del_driver(&aw882xx_i2c_driver); +} +module_exit(aw882xx_i2c_exit); + + +MODULE_DESCRIPTION("ASoC AW882XX Smart PA Driver"); +MODULE_LICENSE("GPL v2"); + + + diff --git a/asoc/codecs/aw88261/aw882xx.h b/asoc/codecs/aw88261/aw882xx.h new file mode 100644 index 000000000000..d50a66456c27 --- /dev/null +++ b/asoc/codecs/aw88261/aw882xx.h @@ -0,0 +1,184 @@ +#ifndef _AW882XX_H_ +#define _AW882XX_H_ +#include +#include +#include "aw_device.h" + +/* + * i2c transaction on Linux limited to 64k + * (See Linux kernel documentation: Documentation/i2c/writing-clients) +*/ +#define AW882XX_CHIP_ID_REG (0x00) +#define MAX_I2C_BUFFER_SIZE 65536 +#define AW882XX_I2C_READ_MSG_NUM 2 +#define AW882XX_DC_DELAY_TIME (1000) +#define AW882XX_LOAD_FW_DELAY_TIME (0) +#define AW_START_RETRIES (5) +#define AW882XX_RUNIN_TEST + +#define AW_I2C_RETRIES 5 /* 5 times */ +#define AW_I2C_RETRY_DELAY 5 /* 5 ms */ + + +#define AW882XX_RATES SNDRV_PCM_RATE_8000_48000 +#define AW882XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +enum { + AW882XX_STREAM_CLOSE = 0, + AW882XX_STREAM_OPEN, +}; + +enum aw882xx_chipid { + PID_1852_ID = 0x1852, + PID_2013_ID = 0x2013, + PID_2032_ID = 0x2032, + PID_2055_ID = 0x2055, + PID_2071_ID = 0x2071, + PID_2113_ID = 0x2113, +}; + +#define AW882XX_SOFT_RESET_VALUE (0x55aa) + +enum aw882xx_int_type { + INT_TYPE_NONE = 0, + INT_TYPE_UVLO = 0x1, + INT_TYPE_BSTOC = 0x2, + INT_TYPE_OCDI = 0x4, + INT_TYPE_OTHI = 0x8, +}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 1) +#define AW_KERNEL_VER_OVER_4_19_1 +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) +#define AW_KERNEL_VER_OVER_5_4_0 +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif + + +#ifdef AW_KERNEL_VER_OVER_4_19_1 +typedef struct snd_soc_component aw_snd_soc_codec_t; +typedef struct snd_soc_component_driver aw_snd_soc_codec_driver_t; +#else +typedef struct snd_soc_codec aw_snd_soc_codec_t; +typedef struct snd_soc_codec_driver aw_snd_soc_codec_driver_t; +#endif + +struct aw_componet_codec_ops { + aw_snd_soc_codec_t *(*kcontrol_codec)(struct snd_kcontrol *kcontrol); + void *(*codec_get_drvdata)(aw_snd_soc_codec_t *codec); + int (*add_codec_controls)(aw_snd_soc_codec_t *codec, + const struct snd_kcontrol_new *controls, unsigned int num_controls); + void (*unregister_codec)(struct device *dev); + int (*register_codec)(struct device *dev, + const aw_snd_soc_codec_driver_t *codec_drv, + struct snd_soc_dai_driver *dai_drv, + int num_dai); +}; + +enum { + AWRW_I2C_ST_NONE = 0, + AWRW_I2C_ST_READ, + AWRW_I2C_ST_WRITE, +}; + +#define AWRW_ADDR_BYTES (1) +#define AWRW_DATA_BYTES (2) +#define AWRW_HDR_LEN (24) + +enum { + AWRW_FLAG_WRITE = 0, + AWRW_FLAG_READ, +}; + +enum { + AW_BSTCFG_DISABLE = 0, + AW_BSTCFG_ENABLE, +}; + +enum { + AW_FRCSET_DISABLE = 0, + AW_FRCSET_ENABLE, +}; + +enum { + AW_BOP_DISABLE = 0, + AW_BOP_ENABLE, +}; + +enum { + AWRW_HDR_WR_FLAG = 0, + AWRW_HDR_ADDR_BYTES, + AWRW_HDR_DATA_BYTES, + AWRW_HDR_REG_NUM, + AWRW_HDR_REG_ADDR, + AWRW_HDR_MAX, +}; + +struct aw882xx_i2c_packet{ + char status; + unsigned int reg_num; + unsigned int reg_addr; + char *reg_data; +}; + +/******************************************** + * struct aw882xx + *******************************************/ +struct aw882xx { + int sysclk; + int rate; + int pstream; + int cstream; + + unsigned int fade_flag; + unsigned char index; + unsigned char phase_sync; /* phase sync */ + unsigned char dc_flag; + unsigned char dbg_en_prof; /* debug enable/disable profile function */ + unsigned char allow_pw; /* allow power */ + int reset_gpio; + int irq_gpio; + unsigned int chip_id; + unsigned char fw_status; + unsigned char fw_retry_cnt; + unsigned char rw_reg_addr; /* rw attr node store read addr */ + + aw_snd_soc_codec_t *codec; + struct aw_componet_codec_ops *codec_ops; + + struct i2c_client *i2c; + struct device *dev; + struct aw882xx_i2c_packet i2c_packet; + struct aw_device *aw_pa; + + struct workqueue_struct *work_queue; + struct delayed_work start_work; + struct delayed_work interrupt_work; + struct delayed_work dc_work; + struct delayed_work fw_work; +#ifdef AW882XX_RUNIN_TEST + struct delayed_work adsp_status; +#endif + struct mutex lock; +}; + +void aw882xx_kcontorl_set(struct aw882xx *aw882xx); +int aw882xx_get_version(char *buf, int size); +int aw882xx_get_dev_num(void); +int aw882xx_i2c_write(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int reg_data); +int aw882xx_i2c_read(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int *reg_data); +int aw882xx_i2c_write_bits(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int mask, unsigned int reg_data); +int aw882xx_init(struct aw882xx *aw882xx, int index); +int aw882xx_hw_reset(struct aw882xx *aw882xx); + + +#endif + diff --git a/asoc/codecs/aw88261/aw_bin_parse.c b/asoc/codecs/aw88261/aw_bin_parse.c new file mode 100644 index 000000000000..d7cfcb555df3 --- /dev/null +++ b/asoc/codecs/aw88261/aw_bin_parse.c @@ -0,0 +1,512 @@ +/* +* aw_bin_parse.c +* +* Copyright (c) 2020 AWINIC Technology CO., LTD +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw_bin_parse.h" + +#define AWINIC_CODE_VERSION "V0.0.7-V1.0.4" /* "code version"-"excel version" */ + +#define DEBUG_LOG_LEVEL +#ifdef DEBUG_LOG_LEVEL +#define DBG(fmt, arg...) do {\ +printk("AWINIC_BIN %s,line= %d,"fmt, __func__, __LINE__, ##arg);\ +} while (0) +#define DBG_ERR(fmt, arg...) do {\ +printk("AWINIC_BIN_ERR %s,line= %d,"fmt, __func__, __LINE__, ##arg);\ +} while (0) +#else +#define DBG(fmt, arg...) do {} while (0) +#define DBG_ERR(fmt, arg...) do {} while (0) +#endif + +#define printing_data_code + +typedef unsigned short int aw_uint16; +typedef unsigned long int aw_uint32; + +#define BigLittleSwap16(A) ((((aw_uint16)(A) & 0xff00) >> 8) | \ + (((aw_uint16)(A) & 0x00ff) << 8)) + +#define BigLittleSwap32(A) ((((aw_uint32)(A) & 0xff000000) >> 24) | \ + (((aw_uint32)(A) & 0x00ff0000) >> 8) | \ + (((aw_uint32)(A) & 0x0000ff00) << 8) | \ + (((aw_uint32)(A) & 0x000000ff) << 24)) + +/** +* +* Interface function +* +* return value: +* value = 0 :success; +* value = -1 :check bin header version +* value = -2 :check bin data type +* value = -3 :check sum or check bin data len error +* value = -4 :check data version +* value = -5 :check register num +* value = -6 :check dsp reg num +* value = -7 :check soc app num +* value = -8 :bin is NULL point +* +**/ + +/******************************************************** +* +* check sum data +* +********************************************************/ +int aw_check_sum(struct aw_bin *bin, int bin_num) +{ + unsigned int i = 0; + unsigned int sum_data = 0; + unsigned int check_sum = 0; + unsigned char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr - + bin->header_info[bin_num].header_len)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + check_sum = GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + + for (i = 4; + i < + bin->header_info[bin_num].bin_data_len + + bin->header_info[bin_num].header_len; i++) { + sum_data += *(p_check_sum + i); + } + DBG("aw_bin_parse bin_num=%d, check_sum = 0x%x, sum_data = 0x%x\n", + bin_num, check_sum, sum_data); + if (sum_data != check_sum) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse check sum or check bin data len error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, check_sum = 0x%x, sum_data = 0x%x\n", bin_num, check_sum, sum_data); + return -3; + } + p_check_sum = NULL; + + return 0; +} + +int aw_check_data_version(struct aw_bin *bin, int bin_num) +{ + int i = 0; + DBG("enter\n"); + + for (i = DATA_VERSION_V1; i < DATA_VERSION_MAX; i++) { + if (bin->header_info[bin_num].bin_data_ver == i) { + return 0; + } + } + DBG_ERR("aw_bin_parse Unrecognized this bin data version\n"); + return -4; +} + +int aw_check_register_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_register_num = 0; + unsigned int parse_register_num = 0; + char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + parse_register_num = GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + check_register_num = (bin->header_info[bin_num].bin_data_len - 4) / + (bin->header_info[bin_num].reg_byte_len + + bin->header_info[bin_num].data_byte_len); + DBG + ("aw_bin_parse bin_num=%d, parse_register_num = 0x%x, check_register_num = 0x%x\n", + bin_num, parse_register_num, check_register_num); + if (parse_register_num != check_register_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse register num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_register_num = 0x%x, check_register_num = 0x%x\n", bin_num, parse_register_num, check_register_num); + return -5; + } + bin->header_info[bin_num].reg_num = parse_register_num; + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 4; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 4; + return 0; +} + +int aw_check_dsp_reg_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_dsp_reg_num = 0; + unsigned int parse_dsp_reg_num = 0; + char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + parse_dsp_reg_num = GET_32_DATA(*(p_check_sum + 7), + *(p_check_sum + 6), + *(p_check_sum + 5), *(p_check_sum + 4)); + bin->header_info[bin_num].reg_data_byte_len = + GET_32_DATA(*(p_check_sum + 11), *(p_check_sum + 10), + *(p_check_sum + 9), *(p_check_sum + 8)); + check_dsp_reg_num = + (bin->header_info[bin_num].bin_data_len - + 12) / bin->header_info[bin_num].reg_data_byte_len; + DBG + ("aw_bin_parse bin_num=%d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x\n", + bin_num, parse_dsp_reg_num, check_dsp_reg_num); + if (parse_dsp_reg_num != check_dsp_reg_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse dsp reg num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x\n", bin_num, parse_dsp_reg_num, check_dsp_reg_num); + return -6; + } + bin->header_info[bin_num].download_addr = + GET_32_DATA(*(p_check_sum + 3), *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + bin->header_info[bin_num].reg_num = parse_dsp_reg_num; + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 12; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 12; + return 0; +} + +int aw_check_soc_app_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_soc_app_num = 0; + unsigned int parse_soc_app_num = 0; + char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + bin->header_info[bin_num].app_version = GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), + *(p_check_sum)); + parse_soc_app_num = GET_32_DATA(*(p_check_sum + 11), + *(p_check_sum + 10), + *(p_check_sum + 9), *(p_check_sum + 8)); + check_soc_app_num = bin->header_info[bin_num].bin_data_len - 12; + DBG + ("aw_bin_parse bin_num=%d, parse_soc_app_num = 0x%x, check_soc_app_num = 0x%x\n", + bin_num, parse_soc_app_num, check_soc_app_num); + if (parse_soc_app_num != check_soc_app_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse soc app num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_soc_app_num = 0x%x, check_soc_app_num = 0x%x\n", bin_num, parse_soc_app_num, check_soc_app_num); + return -7; + } + bin->header_info[bin_num].reg_num = parse_soc_app_num; + bin->header_info[bin_num].download_addr = + GET_32_DATA(*(p_check_sum + 7), *(p_check_sum + 6), + *(p_check_sum + 5), *(p_check_sum + 4)); + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 12; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 12; + return 0; +} + +/************************ +*** +***bin header 1_0_0 +*** +************************/ +void aw_get_single_bin_header_1_0_0(struct aw_bin *bin) +{ + int i; + DBG("enter %s\n", __func__); + bin->header_info[bin->all_bin_parse_num].header_len = 60; + bin->header_info[bin->all_bin_parse_num].check_sum = + GET_32_DATA(*(bin->p_addr + 3), *(bin->p_addr + 2), + *(bin->p_addr + 1), *(bin->p_addr)); + bin->header_info[bin->all_bin_parse_num].header_ver = + GET_32_DATA(*(bin->p_addr + 7), *(bin->p_addr + 6), + *(bin->p_addr + 5), *(bin->p_addr + 4)); + bin->header_info[bin->all_bin_parse_num].bin_data_type = + GET_32_DATA(*(bin->p_addr + 11), *(bin->p_addr + 10), + *(bin->p_addr + 9), *(bin->p_addr + 8)); + bin->header_info[bin->all_bin_parse_num].bin_data_ver = + GET_32_DATA(*(bin->p_addr + 15), *(bin->p_addr + 14), + *(bin->p_addr + 13), *(bin->p_addr + 12)); + bin->header_info[bin->all_bin_parse_num].bin_data_len = + GET_32_DATA(*(bin->p_addr + 19), *(bin->p_addr + 18), + *(bin->p_addr + 17), *(bin->p_addr + 16)); + bin->header_info[bin->all_bin_parse_num].ui_ver = + GET_32_DATA(*(bin->p_addr + 23), *(bin->p_addr + 22), + *(bin->p_addr + 21), *(bin->p_addr + 20)); + bin->header_info[bin->all_bin_parse_num].reg_byte_len = + GET_32_DATA(*(bin->p_addr + 35), *(bin->p_addr + 34), + *(bin->p_addr + 33), *(bin->p_addr + 32)); + bin->header_info[bin->all_bin_parse_num].data_byte_len = + GET_32_DATA(*(bin->p_addr + 39), *(bin->p_addr + 38), + *(bin->p_addr + 37), *(bin->p_addr + 36)); + bin->header_info[bin->all_bin_parse_num].device_addr = + GET_32_DATA(*(bin->p_addr + 43), *(bin->p_addr + 42), + *(bin->p_addr + 41), *(bin->p_addr + 40)); + for (i = 0; i < 8; i++) { + bin->header_info[bin->all_bin_parse_num].chip_type[i] = + *(bin->p_addr + 24 + i); + } + bin->header_info[bin->all_bin_parse_num].reg_num = 0x00000000; + bin->header_info[bin->all_bin_parse_num].reg_data_byte_len = 0x00000000; + bin->header_info[bin->all_bin_parse_num].download_addr = 0x00000000; + bin->header_info[bin->all_bin_parse_num].app_version = 0x00000000; + bin->header_info[bin->all_bin_parse_num].valid_data_len = 0x00000000; + bin->all_bin_parse_num += 1; +} + +int aw_parse_each_of_multi_bins_1_0_0(unsigned int bin_num, int bin_serial_num, + struct aw_bin *bin) +{ + int ret = 0; + unsigned int bin_start_addr = 0; + unsigned int valid_data_len = 0; + DBG("aw_bin_parse enter multi bin branch -- %s\n", __func__); + if (!bin_serial_num) { + bin_start_addr = GET_32_DATA(*(bin->p_addr + 67), + *(bin->p_addr + 66), + *(bin->p_addr + 65), + *(bin->p_addr + 64)); + bin->p_addr += (60 + bin_start_addr); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + bin->header_info[bin->all_bin_parse_num - + 1].valid_data_addr + 4 + 8 * bin_num + 60; + } else { + valid_data_len = + bin->header_info[bin->all_bin_parse_num - 1].bin_data_len; + bin->p_addr += (60 + valid_data_len); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + bin->header_info[bin->all_bin_parse_num - + 1].valid_data_addr + + bin->header_info[bin->all_bin_parse_num - 1].bin_data_len + + 60; + } + + ret = aw_parse_bin_header_1_0_0(bin); + return ret; +} + +/* Get the number of bins in multi bins, and set a for loop, loop processing each bin data */ +int aw_get_multi_bin_header_1_0_0(struct aw_bin *bin) +{ + int i = 0; + int ret = 0; + unsigned int bin_num = 0; + DBG("aw_bin_parse enter multi bin branch -- %s\n", __func__); + bin_num = GET_32_DATA(*(bin->p_addr + 63), + *(bin->p_addr + 62), + *(bin->p_addr + 61), *(bin->p_addr + 60)); + if (bin->multi_bin_parse_num == 1) { + bin->header_info[bin->all_bin_parse_num].valid_data_addr = 60; + } + aw_get_single_bin_header_1_0_0(bin); + + for (i = 0; i < bin_num; i++) { + DBG("aw_bin_parse enter multi bin for is %d\n", i); + ret = aw_parse_each_of_multi_bins_1_0_0(bin_num, i, bin); + if (ret < 0) { + return ret; + } + } + return 0; +} + +/******************************************************** +* +* If the bin framework header version is 1.0.0, + determine the data type of bin, and then perform different processing + according to the data type + If it is a single bin data type, write the data directly into the structure array + If it is a multi-bin data type, first obtain the number of bins, + and then recursively call the bin frame header processing function + according to the bin number to process the frame header information of each bin separately +* +********************************************************/ +int aw_parse_bin_header_1_0_0(struct aw_bin *bin) +{ + int ret = 0; + unsigned int bin_data_type; + DBG("enter %s\n", __func__); + bin_data_type = GET_32_DATA(*(bin->p_addr + 11), + *(bin->p_addr + 10), + *(bin->p_addr + 9), *(bin->p_addr + 8)); + DBG("aw_bin_parse bin_data_type 0x%x\n", bin_data_type); + switch (bin_data_type) { + case DATA_TYPE_REGISTER: + case DATA_TYPE_DSP_REG: + case DATA_TYPE_SOC_APP: + /* Divided into two processing methods, + one is single bin processing, + and the other is single bin processing in multi bin */ + DBG("aw_bin_parse enter single bin branch\n"); + bin->single_bin_parse_num += 1; + DBG("%s bin->single_bin_parse_num is %d\n", __func__, + bin->single_bin_parse_num); + if (!bin->multi_bin_parse_num) { + bin->header_info[bin-> + all_bin_parse_num].valid_data_addr = + 60; + } + aw_get_single_bin_header_1_0_0(bin); + break; + case DATA_TYPE_MULTI_BINS: + /* Get the number of times to enter multi bins */ + DBG("aw_bin_parse enter multi bin branch\n"); + bin->multi_bin_parse_num += 1; + DBG("%s bin->multi_bin_parse_num is %d\n", __func__, + bin->multi_bin_parse_num); + ret = aw_get_multi_bin_header_1_0_0(bin); + if (ret < 0) { + return ret; + } + break; + default: + DBG_ERR("aw_bin_parse Unrecognized this bin data type\n"); + return -2; + } + return 0; +} + +/* get the bin's header version */ +static int aw_check_bin_header_version(struct aw_bin *bin) +{ + int ret = 0; + unsigned int header_version = 0; + + header_version = GET_32_DATA(*(bin->p_addr + 7), + *(bin->p_addr + 6), + *(bin->p_addr + 5), *(bin->p_addr + 4)); + + DBG("aw_bin_parse header_version 0x%x\n", header_version); + + /* Write data to the corresponding structure array + according to different formats of the bin frame header version */ + switch (header_version) { + case HEADER_VERSION_1_0_0: + ret = aw_parse_bin_header_1_0_0(bin); + return ret; + default: + DBG_ERR("aw_bin_parse Unrecognized this bin header version \n"); + return -1; + } +} + +int aw_parsing_bin_file(struct aw_bin *bin) +{ + int i = 0; + int ret = 0; + + DBG("aw_bin_parse code version:%s\n", AWINIC_CODE_VERSION); + if (!bin) { + DBG_ERR("aw_bin_parse bin is NULL\n"); + return -8; + } + bin->p_addr = bin->info.data; + bin->all_bin_parse_num = 0; + bin->multi_bin_parse_num = 0; + bin->single_bin_parse_num = 0; + + /* filling bins header info */ + ret = aw_check_bin_header_version(bin); + if (ret < 0) { + DBG_ERR("aw_bin_parse check bin header version error\n"); + return ret; + } + bin->p_addr = NULL; + + /* check bin header info */ + for (i = 0; i < bin->all_bin_parse_num; i++) { + /* check sum */ + ret = aw_check_sum(bin, i); + if (ret < 0) { + DBG_ERR("aw_bin_parse check sum data error\n"); + return ret; + } + /* check bin data version */ + ret = aw_check_data_version(bin, i); + if (ret < 0) { + DBG_ERR("aw_bin_parse check data version error\n"); + return ret; + } + /* check valid data */ + if (bin->header_info[i].bin_data_ver == DATA_VERSION_V1) { + /* check register num */ + if (bin->header_info[i].bin_data_type == + DATA_TYPE_REGISTER) { + ret = aw_check_register_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check register num error\n"); + return ret; + } + /* check dsp reg num */ + } else if (bin->header_info[i].bin_data_type == + DATA_TYPE_DSP_REG) { + ret = aw_check_dsp_reg_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check dsp reg num error\n"); + return ret; + } + /* check soc app num */ + } else if (bin->header_info[i].bin_data_type == + DATA_TYPE_SOC_APP) { + ret = aw_check_soc_app_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check soc app num error\n"); + return ret; + } + } else { + bin->header_info[i].valid_data_len = + bin->header_info[i].bin_data_len; + } + } + } + DBG("aw_bin_parse parsing success\n"); + + return 0; +} diff --git a/asoc/codecs/aw88261/aw_bin_parse.h b/asoc/codecs/aw88261/aw_bin_parse.h new file mode 100644 index 000000000000..e8fa6dd84199 --- /dev/null +++ b/asoc/codecs/aw88261/aw_bin_parse.h @@ -0,0 +1,73 @@ +#ifndef __AW_BIN_PARSE_H__ +#define __AW_BIN_PARSE_H__ + +#define NULL ((void *)0) +#define GET_32_DATA(w, x, y, z) ((unsigned int)((((uint32_t)w) << 24) | (((uint32_t)x) << 16) | (((uint32_t)y) << 8) | ((uint32_t)z))) +#define BIN_NUM_MAX 100 +#define HEADER_LEN 60 +/********************************************************* + * + * header information + * + ********************************************************/ +enum bin_header_version_enum { + HEADER_VERSION_1_0_0 = 0x01000000, +}; + +enum data_type_enum { + DATA_TYPE_REGISTER = 0x00000000, + DATA_TYPE_DSP_REG = 0x00000010, + DATA_TYPE_DSP_CFG = 0x00000011, + DATA_TYPE_SOC_REG = 0x00000020, + DATA_TYPE_SOC_APP = 0x00000021, + DATA_TYPE_MULTI_BINS = 0x00002000, +}; + +enum data_version_enum { + DATA_VERSION_V1 = 0X00000001, /*default little edian */ + DATA_VERSION_MAX, +}; + +struct bin_header_info { + unsigned int header_len; /* Frame header length */ + unsigned int check_sum; /* Frame header information-Checksum */ + unsigned int header_ver; /* Frame header information-Frame header version */ + unsigned int bin_data_type; /* Frame header information-Data type */ + unsigned int bin_data_ver; /* Frame header information-Data version */ + unsigned int bin_data_len; /* Frame header information-Data length */ + unsigned int ui_ver; /* Frame header information-ui version */ + unsigned char chip_type[8]; /* Frame header information-chip type */ + unsigned int reg_byte_len; /* Frame header information-reg byte len */ + unsigned int data_byte_len; /* Frame header information-data byte len */ + unsigned int device_addr; /* Frame header information-device addr */ + unsigned int valid_data_len; /* Length of valid data obtained after parsing */ + unsigned int valid_data_addr; /* The offset address of the valid data obtained after parsing relative to info */ + + unsigned int reg_num; /* The number of registers obtained after parsing */ + unsigned int reg_data_byte_len; /* The byte length of the register obtained after parsing */ + unsigned int download_addr; /* The starting address or download address obtained after parsing */ + unsigned int app_version; /* The software version number obtained after parsing */ +}; + +/************************************************************ +* +* function define +* +************************************************************/ +struct bin_container { + unsigned int len; /* The size of the bin file obtained from the firmware */ + unsigned char data[]; /* Store the bin file obtained from the firmware */ +}; + +struct aw_bin { + unsigned char *p_addr; /* Offset pointer (backward offset pointer to obtain frame header information and important information) */ + unsigned int all_bin_parse_num; /* The number of all bin files */ + unsigned int multi_bin_parse_num; /* The number of single bin files */ + unsigned int single_bin_parse_num; /* The number of multiple bin files */ + struct bin_header_info header_info[BIN_NUM_MAX]; /* Frame header information and other important data obtained after parsing */ + struct bin_container info; /* Obtained bin file data that needs to be parsed */ +}; + +int aw_parsing_bin_file(struct aw_bin *bin); +int aw_parse_bin_header_1_0_0(struct aw_bin *bin); +#endif diff --git a/asoc/codecs/aw88261/aw_calib.c b/asoc/codecs/aw88261/aw_calib.c new file mode 100644 index 000000000000..674150422c12 --- /dev/null +++ b/asoc/codecs/aw88261/aw_calib.c @@ -0,0 +1,2206 @@ +/* + * aw_calibration.c cali_module + * + * + * Copyright (c) 2020 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +/*#define DEBUG*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aw882xx.h" +#include "aw_dsp.h" +#include "aw_log.h" +#include "aw_calib.h" + +static bool is_single_cali = false; /*if mutli_dev cali false, single dev true*/ + +static const char *cali_str[CALI_STR_MAX] = {"none", "start_cali", "cali_re", + "cali_f0", "store_re", "show_re", "show_r0", "show_cali_f0", "show_f0", + "show_te", "show_st", "dev_sel", "get_ver", "get_dev_num" +}; + +static char *ch_name[AW_DEV_CH_MAX] = {"pri_l", "pri_r", "sec_l", "sec_r"}; +static unsigned int g_cali_re_time = AW_CALI_RE_DEFAULT_TIMER; +static unsigned int g_msic_wr_flag = CALI_STR_NONE; +static unsigned int g_dev_select = AW_DEV_CH_PRI_L; +static unsigned int g_cali_status = false; +static struct miscdevice *g_misc_dev = NULL; +static DEFINE_MUTEX(g_cali_lock); + + +#ifdef AW_CALI_STORE_EXAMPLE + /*write cali to persist file example*/ +#define AWINIC_CALI_FILE "/mnt/vendor/persist/factory/audio/aw_cali.bin" +#define AW_INT_DEC_DIGIT 10 + +static int aw_cali_write_cali_re_to_file(int32_t cali_re, int channel) +{ + struct file *fp = NULL; + char buf[50] = {0}; + loff_t pos = 0; + + fp = filp_open(AWINIC_CALI_FILE, O_RDWR | O_CREAT, 0644); + if (IS_ERR(fp)) { + pr_err("%s:channel:%d open %s failed!\n", + __func__, channel, AWINIC_CALI_FILE); + return -EINVAL; + } + + pos = AW_INT_DEC_DIGIT * channel; + + snprintf(buf, sizeof(buf), "%10d", cali_re); + + kernel_write(fp, buf, strlen(buf), &pos); + + pr_info("%s: channel:%d buf:%s cali_re:%d\n", + __func__, channel, buf, cali_re); + + filp_close(fp, NULL); + return 0; +} + +static int aw_cali_get_read_cali_re(int32_t *cali_re, int channel) +{ + struct file *fp = NULL; + /*struct inode *node;*/ + int f_size; + char *buf = NULL; + int32_t int_cali_re = 0; + loff_t pos = 0; + + fp = filp_open(AWINIC_CALI_FILE, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("%s:channel:%d open %s failed!\n", + __func__, channel, AWINIC_CALI_FILE); + return -EINVAL; + } + + pos = AW_INT_DEC_DIGIT * channel; + + /*node = fp->f_dentry->d_inode;*/ + /*f_size = node->i_size;*/ + f_size = AW_INT_DEC_DIGIT; + + buf = kzalloc(f_size + 1, GFP_ATOMIC); + if (!buf) { + pr_err("%s: channel:%d malloc mem %d failed!\n", + __func__, channel, f_size); + filp_close(fp, NULL); + return -EINVAL; + } + + kernel_read(fp, buf, f_size, &pos); + + + if (sscanf(buf, "%d", &int_cali_re) == 1) + *cali_re = int_cali_re; + else + *cali_re = AW_ERRO_CALI_VALUE; + + pr_info("%s: channel:%d buf:%s int_cali_re: %d\n", + __func__, channel, buf, int_cali_re); + + kfree(buf); + buf = NULL; + filp_close(fp, NULL); + + return 0; +} +#endif + +/*custom need add to set/get cali_re form/to nv*/ +int aw_cali_write_re_to_nvram(int32_t cali_re, int32_t channel) +{ +#ifdef AW_CALI_STORE_EXAMPLE + if (channel >= AW_DEV_CH_MAX) { + pr_err("%s: unsupported channel [%d] \n", __func__, channel); + return -EINVAL; + } + return aw_cali_write_cali_re_to_file(cali_re, channel); +#else + return 0; +#endif +} + +int aw_cali_read_re_from_nvram(int32_t *cali_re, int32_t channel) +{ + /*custom add, if success return value is 0 , else -1*/ +#ifdef AW_CALI_STORE_EXAMPLE + if (channel >= AW_DEV_CH_MAX) { + pr_err("%s: unsupported channel [%d] \n", __func__, channel); + return -EINVAL; + } + return aw_cali_get_read_cali_re(cali_re, channel); +#else + return 0; +#endif +} + +int aw_cali_store_cali_re(struct aw_device *aw_dev, int32_t re) +{ + int ret = 0; + + if ((re >= aw_dev->re_min) && (re <= aw_dev->re_max)) { + aw_dev->cali_desc.cali_re = re; + ret = aw_cali_write_re_to_nvram(re, aw_dev->channel); + if (ret) + aw_dev_err(aw_dev->dev, "write re to nvram failed!"); + } else { + aw_dev_err(aw_dev->dev, "invalid cali re %d !", re); + return -EINVAL; + } + + aw_dev_info(aw_dev->dev, "store cali re is %d", re); + return ret; +} + +/*********************************aw_cali_sevice*********************************************************/ +static void aw_cali_svc_set_cali_status(struct aw_device *aw_dev, int status) +{ + if (status) + g_cali_status = true; + else + g_cali_status = false; + + aw_dev_info(aw_dev->dev, "cali %s", + (status == 0) ? ("disable") : ("enable")); +} + +int aw_cali_svc_get_cali_status(void) +{ + return g_cali_status; +} + +static int aw_cali_svc_dev_get_re(struct aw_device *aw_dev) +{ + int32_t re[AW_CALI_READ_TIMES]; + int ret, i; + int32_t sum = 0; + + for (i = 0; i < AW_CALI_READ_TIMES; i++) { + ret = aw_dsp_read_r0(aw_dev, &re[i]); + if (ret) { + aw_dev_err(aw_dev->dev, "get re failed!"); + return ret; + } + sum += re[i]; + usleep_range(AW_10000_US, AW_10000_US + 10); + } + re[0] = sum / AW_CALI_READ_TIMES; + + aw_dev->cali_desc.cali_re = re[0]; + + /*if ((aw_dev->cali_desc.cali_re >= aw_dev->re_min) && + (aw_dev->cali_desc.cali_re <= aw_dev->re_max)) { + ret = aw_cali_write_re_to_nvram(aw_dev->cali_desc.cali_re, aw_dev->channel); + if (ret) + aw_dev_err(aw_dev->dev, "write re to nvram failed!"); + }*/ + return 0; +} + +static int aw_cali_svc_dev_get_f0(struct aw_device *aw_dev) +{ + int32_t f0[AW_CALI_READ_TIMES]; + int ret, i; + int32_t sum = 0; + + for (i = 0; i < AW_CALI_READ_TIMES; i++) { + ret = aw_dsp_read_f0(aw_dev, &f0[i]); + if (ret) { + aw_dev_err(aw_dev->dev, "get f0 failed!"); + return ret; + } + sum += f0[i]; + usleep_range(AW_10000_US, AW_10000_US + 10); + } + f0[0] = sum / AW_CALI_READ_TIMES; + + aw_dev->cali_desc.cali_f0 = f0[0]; + return 0; +} + +static int aw_cali_svc_dev_get_f0_q(struct aw_device *aw_dev) +{ + int32_t f0[AW_CALI_READ_TIMES], q[AW_CALI_READ_TIMES]; + int ret, i; + int32_t sum_f0 = 0, sum_q = 0; + + for (i = 0; i < AW_CALI_READ_TIMES; i++) { + ret = aw_dsp_read_f0_q(aw_dev, &f0[i], &q[i]); + if (ret) { + aw_dev_err(aw_dev->dev, "get f0 failed!"); + return ret; + } + sum_f0 += f0[i]; + sum_q += q[i]; + usleep_range(AW_10000_US, AW_10000_US + 10); + } + f0[0] = sum_f0 / AW_CALI_READ_TIMES; + q[0] = sum_q / AW_CALI_READ_TIMES; + aw_dev->cali_desc.cali_f0 = f0[0]; + aw_dev->cali_desc.cali_q = q[0]; + return 0; +} + + +static int aw_cali_svc_dev_cali_mode_en(struct aw_device *aw_dev, int type, bool is_enable, unsigned int flag) +{ + int ret; + + /* open cali mode */ + if (is_enable) { + aw_cali_svc_set_cali_status(aw_dev, true); + + if (type == CALI_TYPE_RE) { + if (flag & CALI_OPS_HMUTE) { + ret = aw_dsp_hmute_en(aw_dev, true); + if (ret < 0) + return ret; + } + } else { + if (flag & CALI_OPS_NOISE) { + ret = aw_dsp_noise_en(aw_dev, true); + if (ret < 0) + return ret; + } + } + + ret = aw_dsp_cali_en(aw_dev, true); + if (ret < 0) + return ret; + } else { + aw_dsp_cali_en(aw_dev, false); + + if (type == CALI_TYPE_RE) { + /*close mute*/ + if (flag & CALI_OPS_HMUTE) + aw_dsp_hmute_en(aw_dev, false); + } else { + /*close mute*/ + if (flag & CALI_OPS_NOISE) + aw_dsp_noise_en(aw_dev, false); + } + + /*close cali mode*/ + aw_cali_svc_set_cali_status(aw_dev, false); + } + return 0; +} + +static int aw_cali_svc_dev_cali_re(struct aw_device *aw_dev, unsigned int flag) +{ + int ret; + + ret = aw_cali_svc_dev_cali_mode_en(aw_dev, CALI_TYPE_RE, true, flag); + if (ret < 0) + goto exit; + + /*wait time*/ + msleep(g_cali_re_time); + + /*dev get echo cali re*/ + ret = aw_cali_svc_dev_get_re(aw_dev); + if (ret < 0) + goto exit; +exit: + aw_cali_svc_dev_cali_mode_en(aw_dev, CALI_TYPE_RE, false, flag); + + return ret; +} + +static int aw_cali_svc_devs_cali_re(struct aw_device *aw_dev, unsigned int flag) +{ + struct list_head *dev_list; + struct list_head *pos; + struct aw_device *local_dev; + int ret; + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + /* enable all dev cali mode */ + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + ret = aw_cali_svc_dev_cali_mode_en(local_dev, CALI_TYPE_RE, true, flag); + if (ret < 0) + goto exit; + } + + /* wait time */ + msleep(g_cali_re_time); + + /* dev get echo cali re */ + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + ret = aw_cali_svc_dev_get_re(local_dev); + if (ret < 0) + goto exit; + } + +exit: + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + /* close cali mode */ + aw_cali_svc_dev_cali_mode_en(local_dev, CALI_TYPE_RE, false, flag); + } + + return ret; +} + +static int aw_cali_svc_cali_re(struct aw_device *aw_dev, bool is_single, unsigned int flag) +{ + if (is_single) + return aw_cali_svc_dev_cali_re(aw_dev, flag); + else + return aw_cali_svc_devs_cali_re(aw_dev, flag); + return 0; +} + +static int aw_cali_svc_dev_cali_f0(struct aw_device *aw_dev, unsigned int flag) +{ + int ret; + + ret = aw_cali_svc_dev_cali_mode_en(aw_dev, CALI_TYPE_F0, true, flag); + if (ret < 0) + goto exit; + + /*wait time*/ + msleep(5 * 1000); + + /*dev get echo cali re*/ + ret = aw_cali_svc_dev_get_f0(aw_dev); + if (ret < 0) + goto exit; + +exit: + aw_cali_svc_dev_cali_mode_en(aw_dev, CALI_TYPE_F0, false, flag); + + return ret; +} + +static int aw_cali_svc_devs_cali_f0(struct aw_device *aw_dev, unsigned int flag) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, " get dev list failed"); + return ret; + } + + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + /* set mode status */ + ret = aw_cali_svc_dev_cali_mode_en(local_dev, CALI_TYPE_F0, true, flag); + if (ret < 0) + goto exit; + } + + /*wait time*/ + msleep(5 * 1000); + + /*dev get echo cali re*/ + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + ret = aw_cali_svc_dev_get_f0(local_dev); + if (ret < 0) + goto exit; + } + +exit: + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + /*close cali*/ + aw_cali_svc_dev_cali_mode_en(local_dev, CALI_TYPE_F0, false, flag); + } + + return ret; +} + +static int aw_cali_svc_cali_f0(struct aw_device *aw_dev, bool is_single, unsigned int flag) +{ + if (is_single) + return aw_cali_svc_dev_cali_f0(aw_dev, flag); + else + return aw_cali_svc_devs_cali_f0(aw_dev, flag); + return 0; +} + +static int aw_cali_svc_dev_cali_f0_q(struct aw_device *aw_dev, unsigned int flag) +{ + int ret; + + ret = aw_cali_svc_dev_cali_mode_en(aw_dev, CALI_TYPE_F0, true, flag); + if (ret < 0) + goto exit; + + + /*wait time*/ + msleep(5 * 1000); + + /*dev get echo cali re*/ + ret = aw_cali_svc_dev_get_f0_q(aw_dev); + if (ret < 0) + goto exit; + +exit: + aw_cali_svc_dev_cali_mode_en(aw_dev, CALI_TYPE_F0, false, flag); + + return ret; + +} + +static int aw_cali_svc_devs_cali_f0_q(struct aw_device *aw_dev, unsigned int flag) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed "); + return ret; + } + + + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + /*set mode status*/ + ret = aw_cali_svc_dev_cali_mode_en(local_dev, CALI_TYPE_F0, true, flag); + if (ret < 0) + goto exit; + } + + /*wait time*/ + msleep(5 * 1000); + + /*dev get echo cali re*/ + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + ret = aw_cali_svc_dev_get_f0_q(local_dev); + if (ret < 0) + goto exit; + } + +exit: + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + aw_cali_svc_dev_cali_mode_en(local_dev, CALI_TYPE_F0, false, flag); + } + + return ret; +} + +static int aw_cali_svc_cali_f0_q(struct aw_device *aw_dev, bool is_single, unsigned int flag) +{ + if (is_single) + return aw_cali_svc_dev_cali_f0_q(aw_dev, flag); + else + return aw_cali_svc_devs_cali_f0_q(aw_dev, flag); + + return 0; +} + +static int aw_cali_svc_cali_re_f0(struct aw_device *aw_dev, bool is_single, unsigned int flag) +{ + int ret; + + ret = aw_cali_svc_cali_re(aw_dev, is_single, flag); + if (ret) + return ret; + + ret = aw_cali_svc_cali_f0(aw_dev, is_single, flag); + if (ret) + return ret; + + return 0; +} + +static int aw_cali_svc_cali_re_f0_q(struct aw_device *aw_dev, bool is_single, unsigned int flag) +{ + int ret; + + ret = aw_cali_svc_cali_re(aw_dev, is_single, flag); + if (ret) + return ret; + + ret = aw_cali_svc_cali_f0_q(aw_dev, is_single, flag); + if (ret) + return ret; + + return 0; +} + +int aw_cali_svc_cali_cmd(struct aw_device *aw_dev, int cali_cmd, bool is_single, unsigned int flag) +{ + switch (cali_cmd) { + case AW_CALI_CMD_RE: + return aw_cali_svc_cali_re(aw_dev, is_single, flag); + case AW_CALI_CMD_F0: + return aw_cali_svc_cali_f0(aw_dev, is_single, flag); + case AW_CALI_CMD_F0_Q: + return aw_cali_svc_cali_f0_q(aw_dev, is_single, flag); + case AW_CALI_CMD_RE_F0: + return aw_cali_svc_cali_re_f0(aw_dev, is_single, flag); + case AW_CALI_CMD_RE_F0_Q: + return aw_cali_svc_cali_re_f0_q(aw_dev, is_single, flag); + default: + aw_dev_err(aw_dev->dev, "unsupported cmd %d", cali_cmd); + return -EINVAL; + } + return 0; +} + +int aw_cali_svc_get_devs_cali_re(struct aw_device *aw_dev, int32_t *re_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + re_buf[local_dev->channel] = local_dev->cali_desc.cali_re; + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d]", + local_dev->channel, num); + return -EINVAL; + } + } + + return cnt; +} + +int aw_cali_svc_get_devs_r0(struct aw_device *aw_dev, int32_t *re_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + ret = aw_dsp_read_r0(local_dev, &re_buf[local_dev->channel]); + if (ret) { + aw_dev_err(local_dev->dev, "get re failed!"); + return ret; + } + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d] ", + local_dev->channel, num); + } + } + return cnt; +} + +int aw_cali_svc_get_devs_te(struct aw_device *aw_dev, int32_t *te_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + ret = aw_dsp_read_te(local_dev, &te_buf[local_dev->channel]); + if (ret) { + aw_dev_err(local_dev->dev, "get re failed!"); + return ret; + } + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d]", + local_dev->channel, num); + } + } + + return cnt; +} + +int aw_cali_svc_get_devs_st(struct aw_device *aw_dev, int32_t *st_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /*get dev list*/ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + ret = aw_dsp_read_st(local_dev, &st_buf[local_dev->channel << 1], &st_buf[(local_dev->channel << 1) + 1]); + if (ret) { + aw_dev_err(local_dev->dev, "get re failed!"); + return ret; + } + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d]", + local_dev->channel, num); + } + } + return cnt; +} + +int aw_cali_svc_get_devs_cali_f0(struct aw_device *aw_dev, int32_t *f0_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /*get dev list*/ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + f0_buf[local_dev->channel] = local_dev->cali_desc.cali_f0; + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d]", + local_dev->channel, num); + } + } + + return cnt; +} + +int aw_cali_svc_get_devs_f0(struct aw_device *aw_dev, int32_t *f0_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + ret = aw_dsp_read_f0(local_dev, &f0_buf[local_dev->channel]); + if (ret) { + aw_dev_err(local_dev->dev, "get re failed!"); + return ret; + } + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d]", + local_dev->channel, num); + } + } + return cnt; +} + +int aw_cali_svc_get_devs_cali_f0_q(struct aw_device *aw_dev, + int32_t *f0_buf, int32_t *q_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /*get dev list*/ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + f0_buf[local_dev->channel] = local_dev->cali_desc.cali_f0; + q_buf[local_dev->channel] = local_dev->cali_desc.cali_q; + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d]", + local_dev->channel, num); + } + } + return cnt; +} + +static int aw_cali_svc_set_devs_re_str(struct aw_device *aw_dev, const char *re_str) +{ + struct list_head *dev_list, *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + int re_data[AW_DEV_CH_MAX] = {0}; + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + ret = sscanf(re_str, "pri_l:%d pri_r:%d sec_l:%d sec_r:%d", + &re_data[AW_DEV_CH_PRI_L], + &re_data[AW_DEV_CH_PRI_R], + &re_data[AW_DEV_CH_SEC_L], + &re_data[AW_DEV_CH_SEC_R]); + + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "unsupport str[%s]", re_str); + return ret; + } + + aw_dev_dbg(aw_dev->dev, "pri_l:%d pri_r:%d sec_l:%d sec_r:%d", + re_data[AW_DEV_CH_PRI_L], + re_data[AW_DEV_CH_PRI_R], + re_data[AW_DEV_CH_SEC_L], + re_data[AW_DEV_CH_SEC_R]); + + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < AW_DEV_CH_MAX) { + ret = aw_cali_store_cali_re(local_dev, re_data[local_dev->channel]); + if (ret < 0) + return ret; + cnt++; + } + } + return cnt; + +} + +static int aw_cali_svc_get_cmd_form_str(struct aw_device *aw_dev, const char *buf) +{ + int i; + + for (i = 0; i < CALI_STR_MAX; i++) { + if (!strncmp(cali_str[i], buf, strlen(cali_str[i]))) { + break; + } + } + + if (i == CALI_STR_MAX) { + aw_dev_err(aw_dev->dev, "supported cmd [%s]!", buf); + return -EINVAL; + } + + aw_dev_info(aw_dev->dev, "find str [%s]", cali_str[i]); + return i; +} + +/*********************************aw_cali_sevice*********************************************************/ + + +/*****************************attr start***************************************************/ +static ssize_t aw_cali_attr_time_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + uint32_t time; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + + ret = kstrtoint(buf, 0, &time); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "read buf %s failed\n", + buf); + return ret; + } + + if (time < 400) { + aw_dev_err(aw_dev->dev, "time:%d is too short, no set", + time); + return -EINVAL; + } + + g_cali_re_time = time; + aw_dev_dbg(aw_dev->dev, "time:%d", time); + + return count; +} + +static ssize_t aw_cali_attr_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "time: %d \n", g_cali_re_time); + + return len; +} + +static ssize_t aw_cali_attr_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + int ret; + + ret = aw_cali_svc_get_cmd_form_str(aw_dev, buf); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "supported cmd [%s]!", buf); + return -EPERM; + } + + if (ret == CALI_STR_CALI_RE_F0) { + aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_RE_F0, + is_single_cali, CALI_OPS_HMUTE|CALI_OPS_NOISE); + return count; + } else if (ret == CALI_STR_CALI_RE) { + aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_RE, + is_single_cali, CALI_OPS_HMUTE); + return count; + } else if (ret == CALI_STR_CALI_F0) { + aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_F0, + is_single_cali, CALI_OPS_NOISE); + return count; + } else { + aw_dev_err(aw_dev->dev, "supported cmd [%s]!", buf); + } + + return -EPERM; + +} + +static ssize_t aw_cali_attr_re_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + int ret; + int data = 0; + + if (is_single_cali) { + ret = kstrtoint(buf, 0, &data); + if (ret < 0) { + aw_dev_err(aw882xx->dev, " read buf %s failed", buf); + return ret; + } + ret = aw_cali_store_cali_re(aw_dev, data); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "set re %d failed", data); + return -EPERM; + } + } else { + ret = aw_cali_svc_set_devs_re_str(aw_dev, buf); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "set re str %s failed", buf); + return -EPERM; + } + } + + return count; +} + +static ssize_t aw_cali_attr_re_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, i; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + int32_t cali_re[AW_DEV_CH_MAX] = {0}; + + ret = aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_RE, is_single_cali, CALI_OPS_HMUTE); + if (ret < 0) { + len += snprintf(buf+len, PAGE_SIZE-len, "cali Re cmd failed\n"); + return len; + } + + if (is_single_cali) { + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", aw_dev->cali_desc.cali_re); + } else { + ret = aw_cali_svc_get_devs_cali_re(aw_dev, cali_re, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get re failed "); + len += snprintf(buf+len, PAGE_SIZE-len, "get re failed\n"); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d mOhms ", ch_name[i], cali_re[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + } + + return len; +} + +static ssize_t aw_cali_attr_f0_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + + aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_F0, is_single_cali, CALI_OPS_NOISE); + return count; +} + +static ssize_t aw_cali_attr_f0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, i; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + int32_t cali_f0[AW_DEV_CH_MAX] = {0}; + + ret = aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_F0, is_single_cali, CALI_OPS_NOISE); + if (ret < 0) { + len += snprintf(buf+len, PAGE_SIZE-len, "cali f0 cmd failed\n"); + return len; + } + + if (is_single_cali) { + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", aw_dev->cali_desc.cali_f0); + } else { + ret = aw_cali_svc_get_devs_cali_f0(aw_dev, cali_f0, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get f0 failed "); + len += snprintf(buf+len, PAGE_SIZE-len, "get f0 failed \n"); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d ", ch_name[i], cali_f0[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + } + + return len; +} + +static ssize_t aw_cali_attr_show_re(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, i; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + int32_t cali_re[AW_DEV_CH_MAX] = {0}; + + if (is_single_cali) { + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", aw_dev->cali_desc.cali_re); + } else { + ret = aw_cali_svc_get_devs_cali_re(aw_dev, cali_re, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get re failed "); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d mOhms ", ch_name[i], cali_re[i]); + } + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + + return len; +} + +static ssize_t aw_cali_attr_show_f0(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, i; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + int32_t cali_f0[AW_DEV_CH_MAX] = {0}; + + if (is_single_cali) { + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", aw_dev->cali_desc.cali_f0); + } else { + ret = aw_cali_svc_get_devs_cali_f0(aw_dev, cali_f0, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get f0 failed "); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d ", ch_name[i], cali_f0[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + } + + return len; +} + +/*set cali time*/ +static DEVICE_ATTR(cali_time, S_IWUSR | S_IRUGO, + aw_cali_attr_time_show, aw_cali_attr_time_store); +/*start cali*/ +static DEVICE_ATTR(cali, S_IWUSR, + NULL, aw_cali_attr_store); +/*cali re*/ +static DEVICE_ATTR(cali_re, S_IRUGO | S_IWUSR, + aw_cali_attr_re_show, aw_cali_attr_re_store); +/*cali_f0*/ +static DEVICE_ATTR(cali_f0, S_IRUGO | S_IWUSR, + aw_cali_attr_f0_show, aw_cali_attr_f0_store); +/*show cali_re*/ +static DEVICE_ATTR(re_show, S_IRUGO, + aw_cali_attr_show_re, NULL); +/*show cali_f0*/ +static DEVICE_ATTR(f0_show, S_IRUGO, + aw_cali_attr_show_f0, NULL); + + +static struct attribute *aw_cali_attr[] = { + &dev_attr_cali_time.attr, + &dev_attr_cali.attr, + &dev_attr_cali_re.attr, + &dev_attr_cali_f0.attr, + &dev_attr_re_show.attr, + &dev_attr_f0_show.attr, + NULL +}; + +static struct attribute_group aw_cali_attr_group = { + .attrs = aw_cali_attr +}; + +static void aw_cali_attr_init(struct aw_device *aw_dev) +{ + int ret; + + aw_dev_info(aw_dev->dev, "enter"); + + ret = sysfs_create_group(&aw_dev->dev->kobj, &aw_cali_attr_group); + if (ret < 0) + aw_dev_info(aw_dev->dev, "error creating sysfs cali attr files"); +} + +static void aw_cali_attr_deinit(struct aw_device *aw_dev) +{ + sysfs_remove_group(&aw_dev->dev->kobj, &aw_cali_attr_group); + aw_dev_info(aw_dev->dev, "attr files deinit"); +} + +/*****************************attr end***************************************************/ + +/*****************************class node******************************************************/ +static ssize_t aw_cali_class_time_show(struct class *class, struct class_attribute *attr, char *buf) +{ + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "time: %d \n", g_cali_re_time); + + return len; +} + +static ssize_t aw_cali_class_time_store(struct class *class, + struct class_attribute *attr, const char *buf, size_t len) +{ + int ret; + uint32_t time; + + ret = kstrtoint(buf, 0, &time); + if (ret < 0) { + pr_err("[Awinic] %s, read buf %s failed\n", + __func__, buf); + return ret; + } + + if (time < 400) { + pr_err("[Awinic] %s:time:%d is too short, no set\n", + __func__, time); + return -EINVAL; + } + + g_cali_re_time = time; + pr_debug("%s:time:%d\n", __func__, time); + + return len; +} + +static ssize_t aw_cali_class_cali_re_show(struct class *class, struct class_attribute *attr, char *buf) +{ + struct list_head *dev_list; + struct aw_device *local_dev; + int ret, i; + ssize_t len = 0; + int32_t cali_re[AW_DEV_CH_MAX] = {0}; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + return ret; + } + + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + + ret = aw_cali_svc_cali_cmd(local_dev, AW_CALI_CMD_RE, false, CALI_OPS_HMUTE); + if (ret < 0) { + len += snprintf(buf+len, PAGE_SIZE-len, "cali Re cmd failed\n"); + return len; + } + + ret = aw_cali_svc_get_devs_cali_re(local_dev, cali_re, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(local_dev->dev, "get re failed "); + len += snprintf(buf+len, PAGE_SIZE-len, "get re failed \n"); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d mOhms ", ch_name[i], cali_re[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + + return len; +} + +static ssize_t aw_cali_class_cali_re_store(struct class *class, + struct class_attribute *attr, const char *buf, size_t len) +{ + struct list_head *dev_list; + struct aw_device *local_dev; + int ret; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + return ret; + } + + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + + ret = aw_cali_svc_set_devs_re_str(local_dev, buf); + if (ret <= 0) { + aw_dev_err(local_dev->dev, "set re str %s failed", buf); + return -EPERM; + } + + return len; +} + +static ssize_t aw_cali_class_cali_f0_show(struct class *class, struct class_attribute *attr, char *buf) +{ + struct list_head *dev_list; + struct aw_device *local_dev; + int ret, i; + ssize_t len = 0; + int32_t cali_f0[AW_DEV_CH_MAX] = {0}; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + return ret; + } + + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + + ret = aw_cali_svc_cali_cmd(local_dev, AW_CALI_CMD_F0, false, CALI_OPS_NOISE); + if (ret < 0) { + len += snprintf(buf+len, PAGE_SIZE-len, "cali f0 cmd failed\n"); + return len; + } + + ret = aw_cali_svc_get_devs_cali_f0(local_dev, cali_f0, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(local_dev->dev, "get f0 failed "); + len += snprintf(buf+len, PAGE_SIZE-len, "get f0 failed \n"); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d ", ch_name[i], cali_f0[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + + return len; +} + +static ssize_t aw_cali_class_cali_f0_store(struct class *class, + struct class_attribute *attr, const char *buf, size_t len) +{ + struct list_head *dev_list; + struct aw_device *local_dev; + int ret; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + return ret; + } + + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + + aw_cali_svc_cali_cmd(local_dev, AW_CALI_CMD_F0, false, CALI_OPS_NOISE); + + return len; +} + +static ssize_t aw_cali_class_f0_show(struct class *class, struct class_attribute *attr, char *buf) +{ + struct list_head *dev_list; + struct aw_device *local_dev; + int ret, i; + ssize_t len = 0; + int32_t cali_f0[AW_DEV_CH_MAX] = {0}; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + return ret; + } + + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + + ret = aw_cali_svc_get_devs_cali_f0(local_dev, cali_f0, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(local_dev->dev, "get re failed "); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d ", ch_name[i], cali_f0[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + + return len; +} + +static ssize_t aw_cali_class_re_show(struct class *class, struct class_attribute *attr, char *buf) +{ + struct list_head *dev_list; + struct aw_device *local_dev; + int ret, i; + ssize_t len = 0; + int32_t cali_re[AW_DEV_CH_MAX] = {0}; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + return ret; + } + + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + + ret = aw_cali_svc_get_devs_cali_re(local_dev, cali_re, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(local_dev->dev, "get re failed "); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d mOhms ", ch_name[i], cali_re[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + + return len; +} + +static struct class_attribute class_attr_cali_time = \ + __ATTR(cali_time, S_IWUSR | S_IRUGO, \ + aw_cali_class_time_show, aw_cali_class_time_store); + +static struct class_attribute class_attr_re25_calib = \ + __ATTR(re25_calib, S_IWUSR | S_IRUGO, \ + aw_cali_class_cali_re_show, aw_cali_class_cali_re_store); + +static struct class_attribute class_attr_f0_calib = \ + __ATTR(f0_calib, S_IWUSR | S_IRUGO, \ + aw_cali_class_cali_f0_show, aw_cali_class_cali_f0_store); + +static struct class_attribute class_attr_re_show = \ + __ATTR(re_show, S_IRUGO, \ + aw_cali_class_re_show, NULL); + +static struct class_attribute class_attr_f0_show = \ + __ATTR(f0_show, S_IRUGO, \ + aw_cali_class_f0_show, NULL); + + +static struct class aw_cali_class = { + .name = "smartpa", + .owner = THIS_MODULE, +}; + +static void aw_cali_class_attr_init(struct aw_device *aw_dev) +{ + int ret; + + if (aw_dev->index != 0) { + aw_dev_info(aw_dev->dev, "class node already register"); + return; + } + + ret = class_register(&aw_cali_class); + if (ret < 0) { + aw_dev_info(aw_dev->dev, "error creating class node"); + return; + } + ret = class_create_file(&aw_cali_class, &class_attr_re25_calib); + if (ret) { + aw_dev_info(aw_dev->dev, "creat class_attr_re25_calib fail"); + } + ret = class_create_file(&aw_cali_class, &class_attr_f0_calib); + if (ret) { + aw_dev_info(aw_dev->dev, "creat class_attr_re25_calib fail"); + } + ret = class_create_file(&aw_cali_class, &class_attr_cali_time); + if (ret) { + aw_dev_info(aw_dev->dev, "creat class_attr_cali_time fail"); + } + ret = class_create_file(&aw_cali_class, &class_attr_re_show); + if (ret) { + aw_dev_info(aw_dev->dev, "creat class_attr_re_show fail"); + } + ret = class_create_file(&aw_cali_class, &class_attr_f0_show); + if (ret) { + aw_dev_info(aw_dev->dev, "creat class_attr_f0_show fail"); + } +} + +static void aw_cali_class_attr_deinit(struct aw_device *aw_dev) +{ + class_remove_file(&aw_cali_class, &class_attr_re25_calib); + class_remove_file(&aw_cali_class, &class_attr_f0_calib); + class_remove_file(&aw_cali_class, &class_attr_cali_time); + class_remove_file(&aw_cali_class, &class_attr_re_show); + class_remove_file(&aw_cali_class, &class_attr_f0_show); + + class_unregister(&aw_cali_class); + aw_dev_info(aw_dev->dev, "unregister class node"); +} +/*****************************class node******************************************************/ + +/*****************************misc node******************************************************/ +static int aw_cali_misc_open(struct inode *inode, struct file *file) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + file->private_data = NULL; + return -EINVAL; + } + + /* find select dev */ + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel == g_dev_select) { + break; + } + } + + /* cannot find sel dev, use list first dev */ + if (local_dev->channel != g_dev_select) { + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + aw_dev_dbg(local_dev->dev, "can not find [%s], use default", ch_name[g_dev_select]); + } + + file->private_data = (void *)local_dev; + + aw_dev_dbg(local_dev->dev, "misc open success\n"); + return 0; +} + +static int aw_cali_misc_release(struct inode *inode, struct file *file) +{ + file->private_data = (void *)NULL; + + pr_debug("misc release successi\n"); + return 0; +} + +static int aw_cali_misc_params_ptr(struct aw_device *aw_dev, struct ptr_params_data *p_params) +{ + char *p_data = NULL; + int ret = 0; + + if (p_params->data == NULL || (!p_params->len)) { + aw_dev_err(aw_dev->dev, "p_params error"); + ret = -EFAULT; + return ret; + } + + p_data = kzalloc(p_params->len, GFP_KERNEL); + if (p_data == NULL) { + aw_dev_err(aw_dev->dev, "error allocating memory"); + ret = -ENOMEM; + goto exit; + } + + if (copy_from_user(p_data, + (void __user *)p_params->data, + p_params->len)) { + ret = -EFAULT; + goto exit; + } + + ret = aw_dsp_write_params(aw_dev, p_data, p_params->len); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "write params failed "); + ret = -EFAULT; + goto exit; + } + +exit: + if (p_data != NULL) { + kfree(p_data); + p_data = NULL; + } + + return ret; +} + +static int aw_cali_misc_ops_write(struct aw_device *aw_dev, + unsigned int cmd, unsigned long arg) +{ + unsigned int data_len = _IOC_SIZE(cmd); + char *data_ptr = NULL; + int32_t data = 0; + int ret = 0; + + if (aw_dev->cali_desc.mode == AW_CALI_MODE_NONE) { + aw_dev_info(aw_dev->dev, "cali mode is none, can't support misc ops write"); + return -EINVAL; + } + + data_ptr = kzalloc(data_len, GFP_KERNEL); + if (!data_ptr) { + aw_dev_err(aw_dev->dev, "malloc failed !"); + return -ENOMEM; + } + + if (copy_from_user(data_ptr, (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + + switch (cmd) { + case AW_IOCTL_ENABLE_CALI: { + aw_cali_svc_set_cali_status(aw_dev, data_ptr[0]); + } break; + case AW_IOCTL_SET_CALI_CFG: { + ret = aw_dsp_write_cali_cfg(aw_dev, data_ptr, data_len); + } break; + case AW_IOCTL_SET_NOISE: { + data = *(int32_t *)data_ptr; + ret = aw_dsp_noise_en(aw_dev, data); + } break; + case AW_IOCTL_SET_VMAX: { + ret = aw_dsp_write_vmax(aw_dev, data_ptr, data_len); + } break; + case AW_IOCTL_SET_PARAM: { + ret = aw_dsp_write_params(aw_dev, data_ptr, data_len); + } break; + case AW_IOCTL_SET_PTR_PARAM_NUM: { + ret = aw_cali_misc_params_ptr(aw_dev, (struct ptr_params_data *)data_ptr); + } break; + case AW_IOCTL_SET_CALI_RE: { + ret = aw_cali_store_cali_re(aw_dev, *((int32_t *)data_ptr)); + /*ret = aw_dsp_write_cali_re(aw_dev, *(int32_t *)data_ptr);*/ + } break; + case AW_IOCTL_SET_DSP_HMUTE: { + data = *(int32_t *)data_ptr; + ret = aw_dsp_hmute_en(aw_dev, data); + } break; + case AW_IOCTL_SET_CALI_CFG_FLAG: { + data = *(int32_t *)data_ptr; + /*aw_cali_svc_set_cali_status(aw_dev, data);*/ + ret = aw_dsp_cali_en(aw_dev, data); + } break; + default:{ + aw_dev_err(aw_dev->dev, "unsupported cmd %d", cmd); + ret = -EINVAL; + } break; + } + +exit: + kfree(data_ptr); + return ret; +} + +static int aw_cali_misc_ops_read(struct aw_device *aw_dev, + unsigned int cmd, unsigned long arg) +{ + int16_t data_len = _IOC_SIZE(cmd); + char *data_ptr = NULL; + int32_t *data_32_ptr = NULL; + int ret = 0; + + if (aw_dev->cali_desc.mode == AW_CALI_MODE_NONE) { + aw_dev_info(aw_dev->dev, "cali mode is none, can't support misc ops read"); + return -EINVAL; + } + + data_ptr = kzalloc(data_len, GFP_KERNEL); + if (!data_ptr) { + aw_dev_err(aw_dev->dev, "malloc failed !"); + return -ENOMEM; + } + + switch (cmd) { + case AW_IOCTL_GET_CALI_CFG: { + ret = aw_dsp_read_cali_cfg(aw_dev, data_ptr, data_len); + } break; + case AW_IOCTL_GET_CALI_DATA: { + ret = aw_dsp_read_cali_data(aw_dev, data_ptr, data_len); + } break; + case AW_IOCTL_GET_F0: { + data_32_ptr = (int32_t *)data_ptr; + ret = aw_dsp_read_f0(aw_dev, data_32_ptr); + } break; + case AW_IOCTL_GET_CALI_RE: { + data_32_ptr = (int32_t *)data_ptr; + ret = aw_dsp_read_cali_re(aw_dev, data_32_ptr); + } break; + case AW_IOCTL_GET_VMAX: { + ret = aw_dsp_read_vmax(aw_dev, data_ptr, data_len); + } break; + case AW_IOCTL_GET_F0_Q: { + data_32_ptr = (int32_t *)data_ptr; + ret = aw_dsp_read_f0_q(aw_dev, &data_32_ptr[0], &data_32_ptr[1]); + } break; + default:{ + aw_dev_err(aw_dev->dev, "unsupported cmd %d", cmd); + ret = -EINVAL; + } break; + } + + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + } + + kfree(data_ptr); + return ret; +} + +static int aw_cali_misc_read_dsp(struct aw_device *aw_dev, aw_ioctl_msg_t *msg) +{ + char __user *user_data = (char __user *)msg->data_buf; + uint32_t dsp_msg_id = (uint32_t)msg->opcode_id; + int data_len = msg->data_len; + int ret; + char *data_ptr; + + data_ptr = kzalloc(data_len, GFP_KERNEL); + if (!data_ptr) { + aw_dev_err(aw_dev->dev, "malloc failed !"); + return -ENOMEM; + } + + ret = aw_dsp_read_msg(aw_dev, dsp_msg_id, data_ptr, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, " write failed"); + goto exit; + } + + if (copy_to_user((void __user *)user_data, + data_ptr, data_len)) + ret = -EFAULT; +exit: + kfree(data_ptr); + return ret; +} + +static int aw_cali_misc_write_dsp(struct aw_device *aw_dev, aw_ioctl_msg_t *msg) +{ + char __user *user_data = (char __user *)msg->data_buf; + uint32_t dsp_msg_id = (uint32_t)msg->opcode_id; + int data_len = msg->data_len; + int ret; + char *data_ptr; + + data_ptr = kzalloc(data_len, GFP_KERNEL); + if (!data_ptr) { + aw_dev_err(aw_dev->dev, "malloc failed !\n"); + return -ENOMEM; + } + + if (copy_from_user(data_ptr, (void __user *)user_data, data_len)) { + aw_dev_err(aw_dev->dev, "copy data failed"); + ret = -EFAULT; + goto exit; + } + + ret = aw_dsp_write_msg(aw_dev, dsp_msg_id, data_ptr, data_len); + if (ret) + aw_dev_err(aw_dev->dev, "write failed"); + +exit: + kfree(data_ptr); + return ret; +} + + +static int aw_cali_misc_ops(struct aw_device *aw_dev, + unsigned int cmd, unsigned long arg); +static int aw_cali_misc_ops_msg(struct aw_device *aw_dev, unsigned long arg) +{ + aw_ioctl_msg_t ioctl_msg; + + if (copy_from_user(&ioctl_msg, (void __user *)arg, sizeof(aw_ioctl_msg_t))) + return -EFAULT; + + if (ioctl_msg.version != AW_IOCTL_MSG_VERSION) { + aw_dev_err(aw_dev->dev, "unsupported msg version %d", ioctl_msg.version); + return -EINVAL; + } + + if (ioctl_msg.type == AW_IOCTL_MSG_RD_DSP) { + return aw_cali_misc_read_dsp(aw_dev, &ioctl_msg); + } else if (ioctl_msg.type == AW_IOCTL_MSG_WR_DSP) { + return aw_cali_misc_write_dsp(aw_dev, &ioctl_msg); + } else if (ioctl_msg.type == AW_IOCTL_MSG_IOCTL) { + return aw_cali_misc_ops(aw_dev, ioctl_msg.opcode_id, (unsigned long)ioctl_msg.data_buf); + } else { + aw_dev_err(aw_dev->dev, "unsupported msg type %d", ioctl_msg.type); + return -EINVAL; + } +} + +static int aw_cali_misc_ops(struct aw_device *aw_dev, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + switch (cmd) { + case AW_IOCTL_ENABLE_CALI: + case AW_IOCTL_SET_CALI_CFG: + case AW_IOCTL_SET_NOISE: + case AW_IOCTL_SET_VMAX: + case AW_IOCTL_SET_PARAM: + case AW_IOCTL_SET_PTR_PARAM_NUM: + case AW_IOCTL_SET_CALI_RE: + case AW_IOCTL_SET_DSP_HMUTE: + case AW_IOCTL_SET_CALI_CFG_FLAG: + ret = aw_cali_misc_ops_write(aw_dev, cmd, arg); + break; + case AW_IOCTL_GET_CALI_CFG: + case AW_IOCTL_GET_CALI_DATA: + case AW_IOCTL_GET_F0: + case AW_IOCTL_GET_CALI_RE: + case AW_IOCTL_GET_VMAX: + case AW_IOCTL_GET_F0_Q: + ret = aw_cali_misc_ops_read(aw_dev, cmd, arg); + break; + case AW_IOCTL_MSG: { + ret = aw_cali_misc_ops_msg(aw_dev, arg); + } break; + default: + aw_dev_err(aw_dev->dev, "unsupported cmd %d", cmd); + ret = -EINVAL; + break; + } + + return ret; +} + +static long aw_cali_misc_unlocked_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct aw_device *aw_dev = NULL; + + if (((_IOC_TYPE(cmd)) != (AW_IOCTL_MAGIC))) { + aw_dev_err(aw_dev->dev, " cmd magic err"); + return -EINVAL; + } + aw_dev = (struct aw_device *)file->private_data; + ret = aw_cali_misc_ops(aw_dev, cmd, arg); + if (ret) + return -EINVAL; + + return 0; +} + +#ifdef CONFIG_COMPAT +static long aw_cali_misc_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct aw_device *aw_dev = NULL; + + if (((_IOC_TYPE(cmd)) != (AW_IOCTL_MAGIC))) { + aw_dev_err(aw_dev->dev, "cmd magic err"); + return -EINVAL; + } + aw_dev = (struct aw_device *)file->private_data; + ret = aw_cali_misc_ops(aw_dev, cmd, arg); + if (ret) + return -EINVAL; + + return 0; +} +#endif + +static ssize_t aw_cali_misc_read(struct file *filp, char __user *buf, size_t size, loff_t *pos) +{ + int len = 0; + int i, ret; + struct aw_device *aw_dev = (struct aw_device *)filp->private_data; + char local_buf[128] = { 0 }; + unsigned int dev_num; + int32_t temp_data[AW_DEV_CH_MAX << 1] = {0}; + + aw_dev_info(aw_dev->dev, "enter"); + + if (aw_dev->cali_desc.mode == AW_CALI_MODE_NONE) { + aw_dev_info(aw_dev->dev, "cali mode is none, can't support misc read"); + return -EINVAL; + } + + if (*pos) { + *pos = 0; + return 0; + } + + switch (g_msic_wr_flag) { + case CALI_STR_SHOW_RE: { + ret = aw_cali_svc_get_devs_cali_re(aw_dev, temp_data, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get cali_re failed"); + return ret; + } else { + for (i = 0; i < ret; i++) + len += snprintf(local_buf+len, sizeof(local_buf)-len, + "%s:%d mOhms ", ch_name[i], temp_data[i]); + + len += snprintf(local_buf+len, sizeof(local_buf)-len, "\n"); + } + } break; + case CALI_STR_SHOW_CALI_F0: { + ret = aw_cali_svc_get_devs_cali_f0(aw_dev, temp_data, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get cali f0 failed"); + return ret; + } else { + for (i = 0; i < ret; i++) + len += snprintf(local_buf+len, sizeof(local_buf)-len, + "%s:%d ", ch_name[i], temp_data[i]); + + len += snprintf(local_buf+len, sizeof(local_buf)-len, "\n"); + } + } break; + case CALI_STR_SHOW_R0: { + ret = aw_cali_svc_get_devs_r0(aw_dev, temp_data, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get r0 failed"); + return ret; + } else { + for (i = 0; i < ret; i++) + len += snprintf(local_buf+len, sizeof(local_buf)-len, + "%s:%d mOhms ", ch_name[i], temp_data[i]); + len += snprintf(local_buf+len, sizeof(local_buf)-len, "\n"); + } + } break; + case CALI_STR_SHOW_TE: { + ret = aw_cali_svc_get_devs_te(aw_dev, temp_data, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get te failed"); + return ret; + } else { + for (i = 0; i < ret; i++) + len += snprintf(local_buf+len, sizeof(local_buf)-len, + "%s:%d ", ch_name[i], temp_data[i]); + len += snprintf(local_buf+len, sizeof(local_buf)-len, "\n"); + } + } break; + case CALI_STR_SHOW_ST: { + ret = aw_cali_svc_get_devs_st(aw_dev, temp_data, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get spkr status failed"); + return ret; + } else { + for (i = 0; i < ret; i++) { + len += snprintf(local_buf+len, sizeof(local_buf)-len, + "%s:R0 %d mOhms Te %d ", + ch_name[i], temp_data[i << 1], temp_data[(i << 1) + 1]); + } + len += snprintf(local_buf+len, sizeof(local_buf)-len, "\n"); + } + } break; + case CALI_STR_SHOW_F0: { + ret = aw_cali_svc_get_devs_f0(aw_dev, temp_data, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get f0 failed"); + return ret; + } else { + for (i = 0; i < ret; i++) + len += snprintf(local_buf+len, sizeof(local_buf)-len, + "%s:%d ", ch_name[i], temp_data[i]); + + len += snprintf(local_buf+len, sizeof(local_buf) - len, "\n"); + } + } break; + case CALI_STR_VER: { + if (aw_dev->ops.aw_get_version) { + len = aw_dev->ops.aw_get_version(local_buf, sizeof(local_buf)); + if (len < 0) { + aw_dev_err(aw_dev->dev, "get version failed"); + return -EINVAL; + } + len += snprintf(local_buf+len, sizeof(local_buf) - len, "\n"); + } else { + aw_dev_err(aw_dev->dev, "get version is NULL"); + return -EINVAL; + } + } break; + case CALI_STR_DEV_NUM: { + if (aw_dev->ops.aw_get_dev_num) { + dev_num = aw_dev->ops.aw_get_dev_num(); + len += snprintf(local_buf + len, sizeof(local_buf) - len, "dev_num:%d\n", dev_num); + } else { + aw_dev_err(aw_dev->dev, "get dev num is NULL"); + return -EINVAL; + } + break; + } + default: { + if (g_msic_wr_flag == CALI_STR_NONE) { + aw_dev_info(aw_dev->dev, "please write cmd first"); + return -EINVAL; + } else { + aw_dev_err(aw_dev->dev, "unsupported flag [%d]", g_msic_wr_flag); + g_msic_wr_flag = CALI_STR_NONE; + return -EINVAL; + } + } break; + } + + if (copy_to_user((void __user *)buf, local_buf, len)) { + aw_dev_err(aw_dev->dev, "copy_to_user error"); + g_msic_wr_flag = CALI_STR_NONE; + return -EFAULT; + } + + g_msic_wr_flag = CALI_STR_NONE; + *pos += len; + return len; +} + +static int aw_cali_misc_switch_dev(struct file *filp, struct aw_device *aw_dev, char *cmd_buf) +{ + int i; + char dev_select[50]; + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret; + + /* get sel dev str */ + sscanf(cmd_buf, "dev_sel:%s", dev_select); + + for (i = 0; i < AW_DEV_CH_MAX; i++) { + if (strnstr(dev_select, ch_name[i], strlen(ch_name[i]))) + break; + } + + if (i == AW_DEV_CH_MAX) { + aw_dev_err(aw_dev->dev, "unsupport dev [%s]", dev_select); + return -EINVAL; + } + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed "); + return ret; + } + + /* find sel dev */ + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel == i) { + filp->private_data = (void *)local_dev; + g_dev_select = i; + aw_dev_info(local_dev->dev, "switch dev to [%s]", ch_name[i]); + return 0; + } + } + aw_dev_err(aw_dev->dev, " unsupport [%s]", dev_select); + return -EINVAL; +} + +static ssize_t aw_cali_misc_write(struct file *filp, const char __user *buf, size_t size, loff_t *pos) +{ + char *kernel_buf = NULL; + struct aw_device *aw_dev = (struct aw_device *)filp->private_data; + int ret = 0; + + aw_dev_info(aw_dev->dev, "enter, write size:%d", (int)size); + + if (aw_dev->cali_desc.mode == AW_CALI_MODE_NONE) { + aw_dev_info(aw_dev->dev, "cali mode is none, can't support misc write"); + return -EINVAL; + } + + kernel_buf = kzalloc(size + 1, GFP_KERNEL); + if (kernel_buf == NULL) { + aw_dev_err(aw_dev->dev, "kzalloc failed !"); + return -ENOMEM; + } + + if (copy_from_user(kernel_buf, + (void __user *)buf, + size)) { + ret = -EFAULT; + goto exit; + } + + ret = aw_cali_svc_get_cmd_form_str(aw_dev, kernel_buf); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "upported cmd [%s]! ", kernel_buf); + ret = -EINVAL; + goto exit; + } + + switch (ret) { + case CALI_STR_CALI_RE_F0: { + ret = aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_RE_F0, + is_single_cali, CALI_OPS_HMUTE|CALI_OPS_NOISE); + } break; + case CALI_STR_CALI_RE: { + ret = aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_RE, + is_single_cali, CALI_OPS_HMUTE); + } break; + case CALI_STR_CALI_F0: { + ret = aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_F0, + is_single_cali, CALI_OPS_HMUTE|CALI_OPS_NOISE); + } break; + case CALI_STR_SET_RE: { + /*skip store_re*/ + ret = aw_cali_svc_set_devs_re_str(aw_dev, + kernel_buf + strlen(cali_str[CALI_STR_SET_RE]) + 1); + } break; + case CALI_STR_DEV_SEL: { + ret = aw_cali_misc_switch_dev(filp, aw_dev, kernel_buf); + } break; + case CALI_STR_SHOW_RE: /*show cali_re*/ + case CALI_STR_SHOW_R0: /*show real r0*/ + case CALI_STR_SHOW_CALI_F0: /*GET DEV CALI_F0*/ + case CALI_STR_SHOW_F0: /*SHOW REAL F0*/ + case CALI_STR_SHOW_TE: + case CALI_STR_SHOW_ST: + case CALI_STR_VER: + case CALI_STR_DEV_NUM: { + g_msic_wr_flag = ret; + ret = 0; + } break; + default: { + aw_dev_err(aw_dev->dev, "unsupported [%s]! ", kernel_buf); + ret = -EINVAL; + } break; + }; + +exit: + aw_dev_info(aw_dev->dev, "cmd [%s]! ", kernel_buf); + if (kernel_buf) { + kfree(kernel_buf); + kernel_buf = NULL; + } + if (ret < 0) + return -EINVAL; + else + return size; +} + +static const struct file_operations aw_cali_misc_fops = { + .owner = THIS_MODULE, + .open = aw_cali_misc_open, + .read = aw_cali_misc_read, + .write = aw_cali_misc_write, + .release = aw_cali_misc_release, + .unlocked_ioctl = aw_cali_misc_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = aw_cali_misc_compat_ioctl, +#endif + +}; + +struct miscdevice misc_cali = { + .name = "aw882xx_smartpa", + .minor = MISC_DYNAMIC_MINOR, + .fops = &aw_cali_misc_fops, +}; + +static int aw_cali_misc_init(struct aw_device *aw_dev) +{ + int ret; + + mutex_lock(&g_cali_lock); + if (g_misc_dev == NULL) { + ret = misc_register(&misc_cali); + if (ret) { + aw_dev_err(aw_dev->dev, "misc register fail: %d\n", ret); + mutex_unlock(&g_cali_lock); + return -EINVAL; + } + g_misc_dev = &misc_cali; + aw_dev_dbg(aw_dev->dev, "misc register success"); + } else { + aw_dev_dbg(aw_dev->dev, "misc already register"); + } + mutex_unlock(&g_cali_lock); + + return 0; +} + +static void aw_cali_misc_deinit(struct aw_device *aw_dev) +{ + mutex_lock(&g_cali_lock); + if (g_misc_dev) { + misc_deregister(g_misc_dev); + g_misc_dev = NULL; + } + mutex_unlock(&g_cali_lock); + aw_dev_dbg(aw_dev->dev, " misc unregister done"); +} + +/*****************************misc node******************************************************/ +static void aw_cali_parse_dt(struct aw_device *aw_dev) +{ + struct device_node *np = aw_dev->dev->of_node; + int ret = -1; + const char *cali_mode_str; + struct aw_cali_desc *desc = &aw_dev->cali_desc; + + ret = of_property_read_string(np, "aw-cali-mode", &cali_mode_str); + if (ret < 0) { + aw_dev_info(aw_dev->dev, " aw-cali-mode get failed ,user default misc way"); + desc->mode = AW_CALI_MODE_MISC; + return; + } + + if (!strcmp(cali_mode_str, "none")) + desc->mode = AW_CALI_MODE_NONE; + else if (!strcmp(cali_mode_str, "aw_class")) + desc->mode = AW_CALI_MODE_CLASS; + else if (!strcmp(cali_mode_str, "aw_attr")) + desc->mode = AW_CALI_MODE_ATTR; + else + desc->mode = AW_CALI_MODE_MISC; /*default misc*/ + + aw_dev_info(aw_dev->dev, "cali mode str:%s num:%d", + cali_mode_str, desc->mode); +} + +int aw_cali_parse_re_dt(struct aw_device *aw_dev) +{ + int ret; + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-re-min", &aw_dev->re_min); + if (ret < 0) { + aw_dev->re_min = AW_CALI_RE_DEFAULT_MIN; + aw_dev_info(aw_dev->dev, "read aw-re-min failed, use default"); + } + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-re-max", &aw_dev->re_max); + if (ret < 0) { + aw_dev->re_max = AW_CALI_RE_DEFAULT_MAX; + aw_dev_info(aw_dev->dev, "read aw-re-max failed, use default"); + } + + if (aw_dev->re_min >= aw_dev->re_max) { + aw_dev_err(aw_dev->dev, "re max must be greater than re min"); + return -EINVAL; + } + + aw_dev_info(aw_dev->dev, "re min: %d, re max: %d", + aw_dev->re_min, aw_dev->re_max); + return 0; +} + +int aw_cali_init(struct aw_cali_desc *cali_desc) +{ + int ret; + + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + + cali_desc->cali_f0 = 0; + cali_desc->cali_re = 0; + cali_desc->cali_q = 0; + cali_desc->status = 0; + + aw_cali_parse_dt(aw_dev); + + if (cali_desc->mode == AW_CALI_MODE_ATTR) + aw_cali_attr_init(aw_dev); + else if (cali_desc->mode == AW_CALI_MODE_CLASS) + aw_cali_class_attr_init(aw_dev); + + aw_cali_misc_init(aw_dev); + + ret = aw_cali_parse_re_dt(aw_dev); + + return ret; +} + +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif + +void aw_cali_deinit(struct aw_cali_desc *cali_desc) +{ + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + + if (cali_desc->mode == AW_CALI_MODE_ATTR) + aw_cali_attr_deinit(aw_dev); + else if (cali_desc->mode == AW_CALI_MODE_CLASS) + aw_cali_class_attr_deinit(aw_dev); + + aw_cali_misc_deinit(aw_dev); +} + diff --git a/asoc/codecs/aw88261/aw_calib.h b/asoc/codecs/aw88261/aw_calib.h new file mode 100644 index 000000000000..ff3f15bebd98 --- /dev/null +++ b/asoc/codecs/aw88261/aw_calib.h @@ -0,0 +1,141 @@ +#ifndef __AWINIC_CALIBRATION_H__ +#define __AWINIC_CALIBRATION_H__ + +#define AW_CALI_STORE_EXAMPLE + +#define AW_CALI_READ_TIMES (3) +#define AW_ERRO_CALI_VALUE (0) +#define AW_CALI_RE_DEFAULT_TIMER (3000) + +#define AW_CALI_RE_MAX (15000) +#define AW_CALI_RE_MIN (4000) + +#define AW_CALI_CFG_NUM (3) +#define AW_CALI_DATA_NUM (6) +#define AW_PARAMS_NUM (600) +#define AW_KILO_PARAMS_NUM (1000) + +#define AW_CALI_RE_DEFAULT_MAX (50000) +#define AW_CALI_RE_DEFAULT_MIN (1000) + +struct aw_device; + +enum afe_module_type { + AW_RX_MODULE = 0, + AW_TX_MODULE = 1, +}; + +struct cali_cfg { + int32_t data[AW_CALI_CFG_NUM]; +}; + +struct cali_data { + int32_t data[AW_CALI_DATA_NUM]; +}; + +struct params_data { + int32_t data[AW_PARAMS_NUM]; +}; + +struct ptr_params_data { + int len; + int32_t *data; +}; + +struct f0_q_data { + int32_t data[4]; +}; + +enum { + AW_IOCTL_MSG_IOCTL = 0, + AW_IOCTL_MSG_RD_DSP, + AW_IOCTL_MSG_WR_DSP +}; + +#define AW_IOCTL_MSG_VERSION (0) +typedef struct { + int32_t type; + int32_t opcode_id; + int32_t version; + int32_t data_len; + char *data_buf; + int32_t reseriver[2]; +} aw_ioctl_msg_t; + +#define AW_IOCTL_MAGIC 'a' +#define AW_IOCTL_SET_CALI_CFG _IOWR(AW_IOCTL_MAGIC, 1, struct cali_cfg) +#define AW_IOCTL_GET_CALI_CFG _IOWR(AW_IOCTL_MAGIC, 2, struct cali_cfg) +#define AW_IOCTL_GET_CALI_DATA _IOWR(AW_IOCTL_MAGIC, 3, struct cali_data) +#define AW_IOCTL_SET_NOISE _IOWR(AW_IOCTL_MAGIC, 4, int32_t) +#define AW_IOCTL_GET_F0 _IOWR(AW_IOCTL_MAGIC, 5, int32_t) +#define AW_IOCTL_SET_CALI_RE _IOWR(AW_IOCTL_MAGIC, 6, int32_t) +#define AW_IOCTL_GET_CALI_RE _IOWR(AW_IOCTL_MAGIC, 7, int32_t) +#define AW_IOCTL_SET_VMAX _IOWR(AW_IOCTL_MAGIC, 8, int32_t) +#define AW_IOCTL_GET_VMAX _IOWR(AW_IOCTL_MAGIC, 9, int32_t) +#define AW_IOCTL_SET_PARAM _IOWR(AW_IOCTL_MAGIC, 10, struct params_data) +#define AW_IOCTL_ENABLE_CALI _IOWR(AW_IOCTL_MAGIC, 11, int8_t) +#define AW_IOCTL_SET_PTR_PARAM_NUM _IOWR(AW_IOCTL_MAGIC, 12, struct ptr_params_data) +#define AW_IOCTL_GET_F0_Q _IOWR(AW_IOCTL_MAGIC, 13, struct f0_q_data) +#define AW_IOCTL_SET_DSP_HMUTE _IOWR(AW_IOCTL_MAGIC, 14, int32_t) +#define AW_IOCTL_SET_CALI_CFG_FLAG _IOWR(AW_IOCTL_MAGIC, 15, int32_t) +#define AW_IOCTL_MSG _IOWR(AW_IOCTL_MAGIC, 16, aw_ioctl_msg_t) + +enum{ + AW_CALI_MODE_NONE = 0, + AW_CALI_MODE_ATTR, + AW_CALI_MODE_CLASS, + AW_CALI_MODE_MISC, + AW_CALI_MODE_MAX +}; + +enum { + AW_CALI_CMD_RE = 0, + AW_CALI_CMD_F0, + AW_CALI_CMD_RE_F0, + AW_CALI_CMD_F0_Q, + AW_CALI_CMD_RE_F0_Q, +}; + +enum { + CALI_OPS_HMUTE = 0X0001, + CALI_OPS_NOISE = 0X0002, +}; + +enum { + CALI_TYPE_RE = 0, + CALI_TYPE_F0, +}; + +enum { + CALI_STR_NONE = 0, + CALI_STR_CALI_RE_F0, + CALI_STR_CALI_RE, + CALI_STR_CALI_F0, + CALI_STR_SET_RE, + CALI_STR_SHOW_RE, /*show cali_re*/ + CALI_STR_SHOW_R0, /*show real r0*/ + CALI_STR_SHOW_CALI_F0, /*GET DEV CALI_F0*/ + CALI_STR_SHOW_F0, /*SHOW REAL F0*/ + CALI_STR_SHOW_TE, + CALI_STR_SHOW_ST, + CALI_STR_DEV_SEL, /*switch device*/ + CALI_STR_VER, + CALI_STR_DEV_NUM, + CALI_STR_MAX, +}; + +struct aw_cali_desc { + unsigned char status; + unsigned char mode; /*0:NONE 1:ATTR 2:CLASS 3:MISC */ + int32_t cali_re; /*set cali_re*/ + int32_t cali_f0; /*store cali_f0*/ + int32_t cali_q; /*store cali q*/ +}; + +int aw_cali_init(struct aw_cali_desc *cali_desc); +void aw_cali_deinit(struct aw_cali_desc *cali_desc); +int aw_cali_svc_get_cali_status(void); +int aw_cali_read_re_from_nvram(int32_t *cali_re, int32_t ch_index); + + +#endif diff --git a/asoc/codecs/aw88261/aw_data_type.h b/asoc/codecs/aw88261/aw_data_type.h new file mode 100644 index 000000000000..36aaf4f077ce --- /dev/null +++ b/asoc/codecs/aw88261/aw_data_type.h @@ -0,0 +1,125 @@ +#ifndef __AWINIC_DATA_TYPE_H__ +#define __AWINIC_DATA_TYPE_H__ + +#define AW_NAME_BUF_MAX (50) + +struct aw_msg_hdr { + int32_t type; + int32_t opcode_id; + int32_t version; + int32_t reseriver[3]; +}; + + +/****************************************************************** +* aw profile +*******************************************************************/ +#define PROJECT_NAME_MAX (24) +#define CUSTOMER_NAME_MAX (16) +#define CFG_VERSION_MAX (4) +#define DEV_NAME_MAX (16) + +#define ACF_FILE_ID (0xa15f908) + + +enum aw_cfg_hdr_version { + AW_CFG_HDR_VER_0_0_0_1 = 0x00000001, +}; + +enum aw_cfg_dde_type { + AW_DEV_TYPE_ID = 0x00000000, + AW_SKT_TYPE_ID = 0x00000001, + AW_DEV_DEFAULT_TYPE_ID = 0x00000002, +}; + +enum aw_sec_type { + ACF_SEC_TYPE_REG = 0, + ACF_SEC_TYPE_DSP, + ACF_SEC_TYPE_DSP_CFG, + ACF_SEC_TYPE_DSP_FW, + ACF_SEC_TYPE_HDR_REG, + ACF_SEC_TYPE_HDR_DSP_CFG, + ACF_SEC_TYPE_HDR_DSP_FW, + ACF_SEC_TYPE_MUTLBIN, + ACF_SEC_TYPE_SKT_PROJECT, + ACF_SEC_TYPE_DSP_PROJECT, + ACF_SEC_TYPE_MONITOR, + ACF_SEC_TYPE_MAX, +}; + +enum profile_data_type { + AW_PROFILE_DATA_TYPE_REG = 0, + AW_PROFILE_DATA_TYPE_DSP, + AW_PROFILE_DATA_TYPE_MAX, +}; + +enum aw_prof_type { + AW_PROFILE_MUSIC = 0, + AW_PROFILE_VOICE, + AW_PROFILE_VOIP, + AW_PROFILE_RINGTONE, + AW_PROFILE_RINGTONE_HS, + AW_PROFILE_LOWPOWER, + AW_PROFILE_BYPASS, + AW_PROFILE_MMI, + AW_PROFILE_FM, + AW_PROFILE_NOTIFICATION, + AW_PROFILE_RECEIVER, + AW_PROFILE_MAX, +}; + +enum aw_profile_status { + AW_PROFILE_WAIT = 0, + AW_PROFILE_OK, +}; + +struct aw_cfg_hdr { + uint32_t a_id; /*acf file ID 0xa15f908*/ + char a_project[PROJECT_NAME_MAX]; /*project name*/ + char a_custom[CUSTOMER_NAME_MAX]; /*custom name :huawei xiaomi vivo oppo*/ + char a_version[CFG_VERSION_MAX]; /*author update version*/ + uint32_t a_author_id; /*author id*/ + uint32_t a_ddt_size; /*sub section table entry size*/ + uint32_t a_ddt_num; /*sub section table entry num*/ + uint32_t a_hdr_offset; /*sub section table offset in file*/ + uint32_t a_hdr_version; /*sub section table version*/ + uint32_t reserve[3]; +}; + +struct aw_cfg_dde { + uint32_t type; /*DDE type id*/ + char dev_name[DEV_NAME_MAX]; + uint16_t dev_index; /*dev id*/ + uint16_t dev_bus; /*dev bus id*/ + uint16_t dev_addr; /*dev addr id*/ + uint16_t dev_profile; /*dev profile id*/ + uint32_t data_type; /*data type id*/ + uint32_t data_size; + uint32_t data_offset; + uint32_t data_crc; + uint32_t reserve[5]; +}; + +struct aw_sec_data_desc { + uint32_t len; + unsigned char *data; +}; + +struct aw_prof_desc { + uint32_t id; + uint32_t prof_st; + struct aw_sec_data_desc sec_desc[AW_PROFILE_DATA_TYPE_MAX]; +}; + +struct aw_all_prof_info { + struct aw_prof_desc prof_desc[AW_PROFILE_MAX]; +}; + +struct aw_prof_info { + int count; + struct aw_prof_desc *prof_desc; +}; + + +#endif + diff --git a/asoc/codecs/aw88261/aw_device.c b/asoc/codecs/aw88261/aw_device.c new file mode 100644 index 000000000000..6c2d289a4721 --- /dev/null +++ b/asoc/codecs/aw88261/aw_device.c @@ -0,0 +1,1560 @@ +/* #define DEBUG */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "aw_data_type.h" +#include "aw_log.h" +#include "aw_device.h" +#include "aw_dsp.h" +/*#include "aw_afe.h"*/ +#include "aw_bin_parse.h" +#include "aw882xx.h" + +#define AW_DEV_SYSST_CHECK_MAX (10) +#define AW_ALGO_PARAMS_PATH "/vendor/firmware/aw_skt.bin" + +enum { + AW_EXT_DSP_WRITE_NONE = 0, + AW_EXT_DSP_WRITE, +}; + +static char *profile_name[AW_PROFILE_MAX] = { + "Music", "Voice", "Voip", "Ringtone", "Ringtone_hs", + "Lowpower", "Bypass", "Mmi", "Fm", "Notification", "Receiver" + }; + +static char ext_dsp_prof_write = AW_EXT_DSP_WRITE_NONE; +static DEFINE_MUTEX(g_ext_dsp_prof_wr_lock); /*lock ext wr flag*/ +static unsigned int g_fade_in_time = AW_1000_US / 10; +static unsigned int g_fade_out_time = AW_1000_US >> 1; +static LIST_HEAD(g_dev_list); +static DEFINE_MUTEX(g_dev_lock); + +/*********************************awinic acf*************************************/ +static void aw_dev_reg_dump(struct aw_device *aw_dev) +{ + int reg_num = aw_dev->ops.aw_get_reg_num(); + uint8_t i = 0; + unsigned int reg_val = 0; + + for (i = 0; i < reg_num; i++) { + if (aw_dev->ops.aw_check_rd_access(i)) { + aw_dev->ops.aw_i2c_read(aw_dev, i, ®_val); + aw_dev_info(aw_dev->dev, "read: reg = 0x%02x, val = 0x%04x", + i, reg_val); + } + } +} + +static uint8_t aw_dev_crc8_check(unsigned char *data, uint32_t data_size) +{ + uint8_t crc_value = 0x00; + uint8_t pdatabuf = 0; + int i; + + while (data_size--) { + pdatabuf = *data++; + for (i = 0; i < 8; i++) { + /*if the lowest bit is 1*/ + if ((crc_value ^ (pdatabuf)) & 0x01) { + /*Xor multinomial*/ + crc_value ^= 0x18; + crc_value >>= 1; + crc_value |= 0x80; + } else { + crc_value >>= 1; + } + pdatabuf >>= 1; + } + } + return crc_value; +} + +static int aw_dev_check_cfg_by_hdr(struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = NULL; + struct aw_cfg_dde *cfg_dde = NULL; + unsigned int end_data_offset = 0; + unsigned int act_data = 0; + unsigned int hdr_ddt_len = 0; + uint8_t act_crc8 = 0; + int i; + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + + /*check file type id is awinic acf file*/ + if (cfg_hdr->a_id != ACF_FILE_ID) { + aw_pr_err("not acf type file"); + return -EINVAL; + } + + hdr_ddt_len = cfg_hdr->a_hdr_offset + cfg_hdr->a_ddt_size; + if (hdr_ddt_len > aw_cfg->len) { + aw_pr_err("hdrlen with ddt_len [%d] overflow file size[%d]", + cfg_hdr->a_hdr_offset, aw_cfg->len); + return -EINVAL; + } + + /*check data size*/ + cfg_dde = (struct aw_cfg_dde *)((char *)aw_cfg->data + cfg_hdr->a_hdr_offset); + act_data += hdr_ddt_len; + for (i = 0; i < cfg_hdr->a_ddt_num; i++) + act_data += cfg_dde[i].data_size; + + if (act_data != aw_cfg->len) { + aw_pr_err("act_data[%d] not equal to file size[%d]!", + act_data, aw_cfg->len); + return -EINVAL; + } + + for (i = 0; i < cfg_hdr->a_ddt_num; i++) { + /* data check */ + end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size; + if (end_data_offset > aw_cfg->len) { + aw_pr_err("a_ddt_num[%d] end_data_offset[%d] overflow file size[%d]", + i, end_data_offset, aw_cfg->len); + return -EINVAL; + } + + /* crc check */ + act_crc8 = aw_dev_crc8_check(aw_cfg->data + cfg_dde[i].data_offset, cfg_dde[i].data_size); + if (act_crc8 != cfg_dde[i].data_crc) { + aw_pr_err("a_ddt_num[%d] crc8 check failed, act_crc8:0x%x != data_crc 0x%x", + i, (uint32_t)act_crc8, cfg_dde[i].data_crc); + return -EINVAL; + } + } + + aw_pr_info("project name [%s]", cfg_hdr->a_project); + aw_pr_info("custom name [%s]", cfg_hdr->a_custom); + aw_pr_info("version name [%d.%d.%d.%d]", cfg_hdr->a_version[3], cfg_hdr->a_version[2], + cfg_hdr->a_version[1], cfg_hdr->a_version[0]); + aw_pr_info("author id %d", cfg_hdr->a_author_id); + + return 0; +} + +int aw_dev_load_acf_check(struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = NULL; + + if (aw_cfg == NULL) { + aw_pr_err("aw_prof is NULL"); + return -ENOMEM; + } + + if (aw_cfg->len < sizeof(struct aw_cfg_hdr)) { + aw_pr_err("cfg hdr size[%d] overflow file size[%d]", + aw_cfg->len, (int)sizeof(struct aw_cfg_hdr)); + return -EINVAL; + } + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + switch (cfg_hdr->a_hdr_version) { + case AW_CFG_HDR_VER_0_0_0_1: + return aw_dev_check_cfg_by_hdr(aw_cfg); + default: + aw_pr_err("unsupported hdr_version [0x%x]", cfg_hdr->a_hdr_version); + return -EINVAL; + } + + return 0; +} + + +static int aw_dev_parse_raw_reg(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc) +{ + aw_dev_info(aw_dev->dev, "data_size:%d enter", data_len); + + if (data_len % 4) { + aw_dev_err(aw_dev->dev, "bin data len get error!"); + return -EINVAL; + } + + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_REG].data = data; + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_REG].len = data_len; + + prof_desc->prof_st = AW_PROFILE_OK; + + return 0; +} + +static void aw_dev_parse_raw_dsp(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc) +{ + aw_dev_info(aw_dev->dev, "data_size:%d enter", data_len); + + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_DSP].data = data; + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_DSP].len = data_len; +} + +static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc) +{ + struct aw_bin *aw_bin = NULL; + int ret; + + aw_dev_info(aw_dev->dev, "data_size:%d enter", data_len); + + aw_bin = kzalloc(data_len + sizeof(struct aw_bin), GFP_KERNEL); + if (aw_bin == NULL) { + aw_dev_err(aw_dev->dev, "devm_kzalloc aw_bin failed"); + return -ENOMEM; + } + + aw_bin->info.len = data_len; + memcpy(aw_bin->info.data, data, data_len); + + ret = aw_parsing_bin_file(aw_bin); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "parse bin failed"); + goto parse_bin_failed; + } + + if ((aw_bin->all_bin_parse_num != 1) || + (aw_bin->header_info[0].bin_data_type != DATA_TYPE_REGISTER)) { + aw_dev_err(aw_dev->dev, "bin num or type error"); + goto parse_bin_failed; + } + + if (aw_bin->header_info[0].valid_data_len % 4) { + aw_dev_err(aw_dev->dev, "bin data len get error!"); + return -EINVAL; + } + + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_REG].data = + data + aw_bin->header_info[0].valid_data_addr; + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_REG].len = + aw_bin->header_info[0].valid_data_len; + prof_desc->prof_st = AW_PROFILE_OK; + + kfree(aw_bin); + aw_bin = NULL; + + return 0; + +parse_bin_failed: + kfree(aw_bin); + aw_bin = NULL; + return ret; +} + +static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg_hdr *cfg_hdr, + struct aw_cfg_dde *prof_hdr, struct aw_prof_desc *scene_prof_desc) +{ + switch (prof_hdr->data_type) { + case ACF_SEC_TYPE_REG: + return aw_dev_parse_raw_reg(aw_dev, + (uint8_t *)cfg_hdr + prof_hdr->data_offset, + prof_hdr->data_size, + scene_prof_desc); + break; + case ACF_SEC_TYPE_HDR_REG: + return aw_dev_parse_reg_bin_with_hdr(aw_dev, + (uint8_t *)cfg_hdr + prof_hdr->data_offset, + prof_hdr->data_size, + scene_prof_desc); + break; + case ACF_SEC_TYPE_MONITOR: + return aw_monitor_parse_fw(&aw_dev->monitor_desc, + (uint8_t *)cfg_hdr + prof_hdr->data_offset, + prof_hdr->data_size); + break; + } + + return 0; +} + +static int aw_dev_parse_dev_type(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) +{ + int i = 0; + int ret; + int sec_num = 0; + struct aw_cfg_dde *cfg_dde = + (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->a_hdr_offset); + + aw_dev_info(aw_dev->dev, "enter"); + + for (i = 0; i < prof_hdr->a_ddt_num; i++) { + if ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr == cfg_dde[i].dev_addr) && + (cfg_dde[i].type == AW_DEV_TYPE_ID)) { + if (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR) { + ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], + &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "parse dev driver bin data failed"); + return ret; + } + sec_num++; + } else { + ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], NULL); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "parse monitor bin data failed"); + return ret; + } + } + } + } + + if (sec_num == 0) { + aw_dev_info(aw_dev->dev, "get dev type num is %d, please use default", sec_num); + return AW_DEV_TYPE_NONE; + } + + return AW_DEV_TYPE_OK; +} + +static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) +{ + int i = 0; + int ret; + int sec_num = 0; + struct aw_cfg_dde *cfg_dde = + (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->a_hdr_offset); + + aw_dev_info(aw_dev->dev, "enter"); + + for (i = 0; i < prof_hdr->a_ddt_num; i++) { + if ((aw_dev->index == cfg_dde[i].dev_index) && + (cfg_dde[i].type == AW_DEV_DEFAULT_TYPE_ID)) { + if (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR) { + ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], + &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "parse dev driver bin data failed"); + return ret; + } + sec_num++; + } else { + ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], NULL); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "parse monitor bin data failed"); + return ret; + } + } + } + } + + if (sec_num == 0) { + aw_dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", sec_num); + return -EINVAL; + } + + return 0; +} + +static void aw_dev_parse_skt_type(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) +{ + int i = 0; + int sec_num = 0; + struct aw_cfg_dde *cfg_dde = + (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->a_hdr_offset); + + aw_dev_info(aw_dev->dev, "enter"); + + for (i = 0; i < prof_hdr->a_ddt_num; i++) { + if ((aw_dev->index == cfg_dde[i].dev_index) && + (cfg_dde[i].type == AW_SKT_TYPE_ID)) { + if (cfg_dde[i].data_type == ACF_SEC_TYPE_DSP) { + aw_dev_parse_raw_dsp(aw_dev, + (uint8_t *)prof_hdr + cfg_dde[i].data_offset, + cfg_dde[i].data_size, + &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); + sec_num++; + } + } + } + + aw_dev_info(aw_dev->dev, "get dsp data prof cnt is %d ", sec_num); +} + +static int aw_dev_acf_load_by_hdr(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) +{ + int ret; + + ret = aw_dev_parse_dev_type(aw_dev, prof_hdr, all_prof_info); + if (ret < 0) { + return ret; + } else if (ret == AW_DEV_TYPE_NONE) { + aw_dev_info(aw_dev->dev, "get dev type num is0, parse default dev type"); + ret = aw_dev_parse_dev_default_type(aw_dev, prof_hdr, all_prof_info); + if (ret < 0) + return ret; + } + + aw_dev_parse_skt_type(aw_dev, prof_hdr, all_prof_info); + + return 0; +} + + +static int aw_dev_cfg_get_vaild_prof(struct aw_device *aw_dev, + struct aw_all_prof_info all_prof_info) +{ + int i; + int num = 0; + struct aw_prof_desc *prof_desc = all_prof_info.prof_desc; + struct aw_prof_info *prof_info = &aw_dev->prof_info; + + for (i = 0; i < AW_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW_PROFILE_OK) + aw_dev->prof_info.count++; + } + + aw_dev_info(aw_dev->dev, "get vaild profile:%d", aw_dev->prof_info.count); + + if (!aw_dev->prof_info.count) { + aw_dev_err(aw_dev->dev, "no profile data"); + return -EPERM; + } + + prof_info->prof_desc = kzalloc(prof_info->count * sizeof(struct aw_prof_desc), GFP_KERNEL); + if (prof_info->prof_desc == NULL) { + aw_dev_err(aw_dev->dev, "prof_desc kzalloc failed"); + return -ENOMEM; + } + + for (i = 0; i < AW_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW_PROFILE_OK) { + if (num >= prof_info->count) { + aw_dev_err(aw_dev->dev, "get scene num[%d] overflow count[%d]", + num, prof_info->count); + return -ENOMEM; + } + prof_info->prof_desc[num] = prof_desc[i]; + prof_info->prof_desc[num].id = i; + num++; + } + } + + return 0; +} + +static int aw_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = NULL; + struct aw_all_prof_info all_prof_info; + int ret; + + aw_dev_info(aw_dev->dev, "enter"); + memset(&all_prof_info, 0, sizeof(struct aw_all_prof_info)); + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + switch (cfg_hdr->a_hdr_version) { + case AW_CFG_HDR_VER_0_0_0_1: + ret = aw_dev_acf_load_by_hdr(aw_dev, cfg_hdr, &all_prof_info); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "hdr_cersion[0x%x] parse failed", + cfg_hdr->a_hdr_version); + return ret; + } + break; + default: + aw_pr_err("unsupported hdr_version [0x%x]", cfg_hdr->a_hdr_version); + return -EINVAL; + } + + ret = aw_dev_cfg_get_vaild_prof(aw_dev, all_prof_info); + if (ret < 0) + return ret; + + aw_dev_info(aw_dev->dev, "parse cfg success"); + return 0; +} + +static struct aw_sec_data_desc *aw_dev_get_prof_data(struct aw_device *aw_dev, int index, int data_type) +{ + struct aw_sec_data_desc *sec_data = NULL; + struct aw_prof_desc *prof_desc = NULL; + + if (index >= aw_dev->prof_info.count) { + aw_dev_err(aw_dev->dev, "index[%d] overflow count[%d]", + index, aw_dev->prof_info.count); + return NULL; + } + + prof_desc = &aw_dev->prof_info.prof_desc[index]; + + if (data_type >= AW_PROFILE_DATA_TYPE_MAX) { + aw_dev_err(aw_dev->dev, "unsupport data type id [%d]", data_type); + return NULL; + } + + sec_data = &prof_desc->sec_desc[data_type]; + + aw_dev_dbg(aw_dev->dev, "get prof[%s] data len[%d]", profile_name[prof_desc->id], sec_data->len); + + return sec_data; +} + + +/*****************************awinic device*************************************/ +static char *aw_dev_get_prof_name(struct aw_device *aw_dev, int index) +{ + struct aw_prof_desc *prof_desc = NULL; + + if (index < 0) { + aw_dev_err(aw_dev->dev, "index[%d] error", index); + return NULL; + } + + if (index >= aw_dev->prof_info.count) { + aw_dev_err(aw_dev->dev, "index[%d] overflow count[%d]", + index, aw_dev->prof_info.count); + return NULL; + } + + prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return profile_name[prof_desc->id]; +} + +/*static int aw_dev_dsp_fw_update(struct aw_device *aw_dev); +{ + int ret; + struct aw_sec_data_desc *dsp_data; + + char *prof_name = aw_dev_get_prof_name(aw_dev, aw_dev->set_prof); + + if (prof_name == NULL) { + aw_dev_err(aw_dev->dev, "get prof name failed"); + return -EINVAL; + } + + dsp_data = aw_dev_get_prof_data(aw_dev, + aw_dev->set_prof, AW_PROFILE_DATA_TYPE_DSP); + if (dsp_data == NULL || + dsp_data->data == NULL || + dsp_data->len == 0) { + aw_dev_info(aw_dev->dev, "dsp data is NULL"); + return 0; + } + + mutex_lock(&g_ext_dsp_prof_wr_lock); + if (ext_dsp_prof_write == AW_EXT_DSP_WRITE_NONE) { + ret = aw_dsp_write_params(aw_dev, dsp_data->data, dsp_data->len); + if (ret) { + aw_dev_err(aw_dev->dev, "dsp params update failed !"); + mutex_unlock(&g_ext_dsp_prof_wr_lock); + return ret; + } + ext_dsp_prof_write = AW_EXT_DSP_WRITE; + } else { + aw_dev_dbg(aw_dev->dev, "dsp params already update !"); + } + mutex_unlock(&g_ext_dsp_prof_wr_lock); + + aw_dev_info(aw_dev->dev, "load %s done", prof_name); + return 0; +}*/ + +/*pwd enable update reg*/ +static int aw_dev_reg_fw_update(struct aw_device *aw_dev) +{ + int i = 0; + unsigned int reg_addr = 0; + unsigned int reg_val = 0; + unsigned int read_val; + int ret = -1; + unsigned int init_volume = 0; + struct aw_int_desc *int_desc = &aw_dev->int_desc; + struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; + struct aw_bstctrl_desc *bstctrl_desc = &aw_dev->bstctrl_desc; + struct aw_sec_data_desc *reg_data; + int16_t *data; + int data_len; + + char *prof_name = aw_dev_get_prof_name(aw_dev, aw_dev->set_prof); + + if (prof_name == NULL) { + aw_dev_err(aw_dev->dev, "get prof name failed"); + return -EINVAL; + } + + reg_data = aw_dev_get_prof_data(aw_dev, aw_dev->set_prof, AW_PROFILE_DATA_TYPE_REG); + if (reg_data == NULL) + return -EINVAL; + + data = (int16_t *)reg_data->data; + data_len = reg_data->len >> 1; + + for (i = 0; i < data_len; i += 2) { + reg_addr = data[i]; + reg_val = data[i + 1]; + + if (reg_addr == int_desc->mask_reg) { + int_desc->int_mask = reg_val; + reg_val = int_desc->mask_default; + } + + if (aw_dev->bstcfg_enable) { + if (reg_addr == profctrl_desc->reg) { + profctrl_desc->cfg_prof_mode = + reg_val & (~profctrl_desc->mask); + } + + if (reg_addr == bstctrl_desc->reg) { + bstctrl_desc->cfg_bst_type = + reg_val & (~bstctrl_desc->mask); + } + } + + /*keep amppd status*/ + if (reg_addr == aw_dev->amppd_desc.reg) { + aw_dev->amppd_st = reg_val & (~aw_dev->amppd_desc.mask); + aw_dev_info(aw_dev->dev, "amppd_st=0x%04x", aw_dev->amppd_st); + aw_dev->ops.aw_i2c_read(aw_dev, + (unsigned char)reg_addr, + (unsigned int *)&read_val); + read_val &= (~aw_dev->amppd_desc.mask); + reg_val &= aw_dev->amppd_desc.mask; + reg_val |= read_val; + } + + /*keep pwd status*/ + if (reg_addr == aw_dev->pwd_desc.reg) { + aw_dev->ops.aw_i2c_read(aw_dev, + (unsigned char)reg_addr, + (unsigned int *)&read_val); + read_val &= (~aw_dev->pwd_desc.mask); + reg_val &= aw_dev->pwd_desc.mask; + reg_val |= read_val; + } + /*keep mute status*/ + if (reg_addr == aw_dev->mute_desc.reg) { + /*get bin value*/ + aw_dev->mute_st = reg_val & (~aw_dev->mute_desc.mask); + aw_dev_info(aw_dev->dev, "mute_st=0x%04x", aw_dev->mute_st); + aw_dev->ops.aw_i2c_read(aw_dev, + (unsigned char)reg_addr, + (unsigned int *)&read_val); + read_val &= (~aw_dev->mute_desc.mask); + reg_val &= aw_dev->mute_desc.mask; + reg_val |= read_val; + } + + if (reg_addr == aw_dev->vcalb_desc.vcalb_reg) + continue; + + aw_dev_dbg(aw_dev->dev, "reg=0x%04x, val = 0x%04x", + (uint16_t)reg_addr, (uint16_t)reg_val); + ret = aw_dev->ops.aw_i2c_write(aw_dev, + (unsigned char)reg_addr, + (unsigned int)reg_val); + if (ret < 0) + break; + } + + aw_dev->ops.aw_get_volume(aw_dev, &init_volume); + aw_dev->volume_desc.init_volume = init_volume; + + /*keep min volume*/ + aw_dev->ops.aw_set_volume(aw_dev, aw_dev->volume_desc.mute_volume); + + aw_dev_info(aw_dev->dev, "load %s done", prof_name); + + return ret; +} + +static void aw_dev_fade_in(struct aw_device *aw_dev) +{ + int i = 0; + int fade_step = aw_dev->vol_step; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + int fade_flag = aw882xx->fade_flag; + struct aw_volume_desc *desc = &aw_dev->volume_desc; + + if (fade_step == 0 || g_fade_in_time == 0 || fade_flag == 0) { + aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); + return; + } + /*volume up*/ + for (i = desc->mute_volume; i >= desc->init_volume; i -= fade_step) { + aw_dev->ops.aw_set_volume(aw_dev, i); + usleep_range(g_fade_in_time, g_fade_in_time + 10); + } + if (i != desc->init_volume) + aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); +} + +static void aw_dev_fade_out(struct aw_device *aw_dev) +{ + int i = 0; + unsigned start_volume = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + int fade_flag = aw882xx->fade_flag; + int fade_step = aw_dev->vol_step; + struct aw_volume_desc *desc = &aw_dev->volume_desc; + + if (fade_step == 0 || g_fade_out_time == 0 || fade_flag == 0) { + aw_dev->ops.aw_set_volume(aw_dev, desc->mute_volume); + return; + } + + aw_dev->ops.aw_get_volume(aw_dev, &start_volume); + i = start_volume; + for (i = start_volume; i <= desc->mute_volume; i += fade_step) { + aw_dev->ops.aw_set_volume(aw_dev, i); + usleep_range(g_fade_out_time, g_fade_out_time + 10); + } + if (i != desc->mute_volume) { + aw_dev->ops.aw_set_volume(aw_dev, desc->mute_volume); + usleep_range(g_fade_out_time, g_fade_out_time + 10); + } +} + +static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd) +{ + struct aw_pwd_desc *pwd_desc = &aw_dev->pwd_desc; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (pwd) { + aw_dev->ops.aw_i2c_write_bits(aw_dev, pwd_desc->reg, + pwd_desc->mask, + pwd_desc->enable); + } else { + aw_dev->ops.aw_i2c_write_bits(aw_dev, pwd_desc->reg, + pwd_desc->mask, + pwd_desc->disable); + } + aw_dev_info(aw_dev->dev, "done"); +} + +static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd) +{ + struct aw_amppd_desc *amppd_desc = &aw_dev->amppd_desc; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (amppd) { + aw_dev->ops.aw_i2c_write_bits(aw_dev, amppd_desc->reg, + amppd_desc->mask, + amppd_desc->enable); + } else { + aw_dev->ops.aw_i2c_write_bits(aw_dev, amppd_desc->reg, + amppd_desc->mask, + amppd_desc->disable); + } + aw_dev_info(aw_dev->dev, "done"); +} + +static void aw_dev_mute(struct aw_device *aw_dev, bool mute) +{ + struct aw_mute_desc *mute_desc = &aw_dev->mute_desc; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (mute) { + aw_dev_fade_out(aw_dev); + aw_dev->ops.aw_i2c_write_bits(aw_dev, mute_desc->reg, + mute_desc->mask, + mute_desc->enable); + } else { + aw_dev->ops.aw_i2c_write_bits(aw_dev, mute_desc->reg, + mute_desc->mask, + mute_desc->disable); + aw_dev_fade_in(aw_dev); + } + aw_dev_info(aw_dev->dev, "done"); +} + +static int aw_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t reg_icalk = 0; + uint16_t reg_icalkl = 0; + struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; + + if (desc->icalkl_reg == AW_REG_NONE) { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalk_reg, ®_val); + reg_icalk = (uint16_t)reg_val & (~desc->icalk_reg_mask); + } else { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalk_reg, ®_val); + reg_icalk = (uint16_t)reg_val & (~desc->icalk_reg_mask); + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalkl_reg, ®_val); + reg_icalkl = (uint16_t)reg_val & (~desc->icalkl_reg_mask); + reg_icalk = (reg_icalk >> desc->icalk_shift) | (reg_icalkl >> desc->icalkl_shift); + } + + if (reg_icalk & (~desc->icalk_sign_mask)) + reg_icalk = reg_icalk | (~desc->icalk_neg_mask); + + *icalk = (int16_t)reg_icalk; + + return ret; +} + +static int aw_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t reg_vcalk = 0; + uint16_t reg_vcalkl = 0; + struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; + + if (desc->vcalkl_reg == AW_REG_NONE) { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalk_reg, ®_val); + reg_vcalk = (uint16_t)reg_val & (~desc->vcalk_reg_mask); + } else { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalk_reg, ®_val); + reg_vcalk = (uint16_t)reg_val & (~desc->vcalk_reg_mask); + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalkl_reg, ®_val); + reg_vcalkl = (uint16_t)reg_val & (~desc->vcalkl_reg_mask); + reg_vcalk = (reg_vcalk >> desc->vcalk_shift) | (reg_vcalkl >> desc->vcalkl_shift); + } + + if (reg_vcalk & (~desc->vcalk_sign_mask)) + reg_vcalk = reg_vcalk | (~desc->vcalk_neg_mask); + + *vcalk = (int16_t)reg_vcalk; + + return ret; +} + +static int aw_dev_set_vcalb(struct aw_device *aw_dev) +{ + int ret = -1; + unsigned int reg_val; + int vcalb; + int icalk; + int vcalk; + int16_t icalk_val = 0; + int16_t vcalk_val = 0; + + struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; + + if (desc->icalk_reg == AW_REG_NONE || desc->vcalb_reg == AW_REG_NONE) { + aw_dev_info(aw_dev->dev, "REG None!"); + return 0; + } + + ret = aw_dev_get_icalk(aw_dev, &icalk_val); + if (ret < 0) + return ret; + + ret = aw_dev_get_vcalk(aw_dev, &vcalk_val); + if (ret < 0) + return ret; + + icalk = desc->cabl_base_value + desc->icalk_value_factor * icalk_val; + vcalk = desc->cabl_base_value + desc->vcalk_value_factor * vcalk_val; + if (!vcalk) { + aw_dev_err(aw_dev->dev, "vcalk is 0"); + return -EINVAL; + } + + vcalb = desc->vcal_factor * icalk / vcalk; + + reg_val = (unsigned int)vcalb; + aw_dev_info(aw_dev->dev, "icalk=%d, vcalk=%d, vcalb=%d, reg_val=0x%04x", + icalk, vcalk, vcalb, reg_val); + + ret = aw_dev->ops.aw_i2c_write(aw_dev, desc->vcalb_reg, reg_val); + + aw_dev_info(aw_dev->dev, "done"); + + return ret; +} + +int aw_dev_get_int_status(struct aw_device *aw_dev, uint16_t *int_status) +{ + int ret = -1; + unsigned int reg_val = 0; + + ret = aw_dev->ops.aw_i2c_read(aw_dev, aw_dev->int_desc.st_reg, ®_val); + if (ret < 0) + aw_dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret); + else + *int_status = reg_val; + + aw_dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", *int_status); + return ret; +} + +void aw_dev_clear_int_status(struct aw_device *aw_dev) +{ + uint16_t int_status = 0; + + /*read int status and clear*/ + aw_dev_get_int_status(aw_dev, &int_status); + /*make suer int status is clear*/ + aw_dev_get_int_status(aw_dev, &int_status); + aw_dev_info(aw_dev->dev, "done"); +} + +int aw_dev_set_intmask(struct aw_device *aw_dev, bool flag) +{ + struct aw_int_desc *desc = &aw_dev->int_desc; + int ret = -1; + + if (flag) + ret = aw_dev->ops.aw_i2c_write(aw_dev, desc->mask_reg, + desc->int_mask); + else + ret = aw_dev->ops.aw_i2c_write(aw_dev, desc->mask_reg, + desc->mask_default); + aw_dev_info(aw_dev->dev, "done"); + return ret; +} + +static int aw_dev_mode1_pll_check(struct aw_device *aw_dev) +{ + int ret = -1; + unsigned char i; + unsigned int reg_val = 0; + struct aw_sysst_desc *desc = &aw_dev->sysst_desc; + + for (i = 0; i < AW_DEV_SYSST_CHECK_MAX; i++) { + aw_dev->ops.aw_i2c_read(aw_dev, desc->reg, ®_val); + if (reg_val & desc->pll_check) { + ret = 0; + break; + } else { + aw_dev_dbg(aw_dev->dev, "check pll lock fail, cnt=%d, reg_val=0x%04x", + i, reg_val); + usleep_range(AW_2000_US, AW_2000_US + 10); + } + } + if (ret < 0) + aw_dev_err(aw_dev->dev, "check fail"); + else + aw_dev_info(aw_dev->dev, "done"); + + return ret; +} + +static int aw_dev_mode2_pll_check(struct aw_device *aw_dev) +{ + int ret = -1; + unsigned int reg_val = 0; + struct aw_cco_mux_desc *cco_mux_desc = &aw_dev->cco_mux_desc; + + aw_dev->ops.aw_i2c_read(aw_dev, cco_mux_desc->reg, ®_val); + reg_val &= (~cco_mux_desc->mask); + aw_dev_dbg(aw_dev->dev, "REG_PLLCTRL1_bit14 = 0x%04x", reg_val); + if (reg_val == cco_mux_desc->divided_val) { + aw_dev_dbg(aw_dev->dev, "CCO_MUX is already divided"); + return ret; + } + + /* change mode2 */ + aw_dev->ops.aw_i2c_write_bits(aw_dev, cco_mux_desc->reg, + cco_mux_desc->mask, cco_mux_desc->bypass_val); + ret = aw_dev_mode1_pll_check(aw_dev); + + /* change mode1 */ + aw_dev->ops.aw_i2c_write_bits(aw_dev, cco_mux_desc->reg, + cco_mux_desc->mask, cco_mux_desc->divided_val); + if (ret == 0) { + usleep_range(AW_2000_US, AW_2000_US + 10); + ret = aw_dev_mode1_pll_check(aw_dev); + } + + return ret; +} + +static int aw_dev_syspll_check(struct aw_device *aw_dev) +{ + int ret = -1; + + ret = aw_dev_mode1_pll_check(aw_dev); + if (ret < 0) { + aw_dev_err(aw_dev->dev, + "mode1 check iis failed try switch to mode2 check"); + + ret= aw_dev_mode2_pll_check(aw_dev); + if (ret < 0) + aw_dev_err(aw_dev->dev, "mode2 check iis failed"); + } + + return ret; +} + +static int aw_dev_sysst_check(struct aw_device *aw_dev) +{ + int ret = -1; + unsigned char i; + unsigned int reg_val = 0; + struct aw_sysst_desc *desc = &aw_dev->sysst_desc; + + for (i = 0; i < AW_DEV_SYSST_CHECK_MAX; i++) { + aw_dev->ops.aw_i2c_read(aw_dev, desc->reg, ®_val); + if (((reg_val & (~desc->mask)) & desc->st_check) == desc->st_check) { + ret = 0; + break; + } else { + aw_dev_info(aw_dev->dev, "check fail, cnt=%d, reg_val=0x%04x", + i, reg_val); + usleep_range(AW_2000_US, AW_2000_US + 10); + } + } + if (ret < 0) + aw_dev_err(aw_dev->dev, "check fail"); + else + aw_dev_info(aw_dev->dev, "done"); + + return ret; +} + +int aw_dev_get_profile_count(struct aw_device *aw_dev) +{ + if (aw_dev == NULL) { + aw_pr_err("aw_dev is NULL"); + return -ENOMEM; + } + + return aw_dev->prof_info.count; +} + +int aw_dev_get_profile_name(struct aw_device *aw_dev, char *name, int index) +{ + int dev_profile_id; + + if (index < 0) { + aw_dev_err(aw_dev->dev, "index[%d] error", index); + return -EINVAL; + } + + if (index > aw_dev->prof_info.count) { + aw_dev_err(aw_dev->dev, "index[%d] overflow dev prof num[%d]", + index, aw_dev->prof_info.count); + return -EINVAL; + } + + if (aw_dev->prof_info.prof_desc[index].id >= AW_PROFILE_MAX) { + aw_dev_err(aw_dev->dev, "can not find match id "); + return -EINVAL; + } + + dev_profile_id = aw_dev->prof_info.prof_desc[index].id; + + strlcpy(name, profile_name[dev_profile_id], + strlen(profile_name[dev_profile_id]) + 1); + /*aw_dev_dbg(aw_dev->dev, "%s: get name [%s]", + __func__, profile_name[dev_profile_id]);*/ + return 0; +} + +int aw_dev_check_profile_index(struct aw_device *aw_dev, int index) +{ + if ((index >= aw_dev->prof_info.count) || (index < 0)) + return -EINVAL; + else + return 0; +} + +int aw_dev_get_profile_index(struct aw_device *aw_dev) +{ + return aw_dev->set_prof; +} + +int aw_dev_set_profile_index(struct aw_device *aw_dev, int index) +{ + if (index >= aw_dev->prof_info.count || index < 0) { + return -EINVAL; + } else { + aw_dev->set_prof = index; + aw_dev_info(aw_dev->dev, "set prof[%s]", + profile_name[aw_dev->prof_info.prof_desc[index].id]); + } + mutex_lock(&g_ext_dsp_prof_wr_lock); + ext_dsp_prof_write = AW_EXT_DSP_WRITE_NONE; + mutex_unlock(&g_ext_dsp_prof_wr_lock); + + return 0; +} + +int aw_dev_get_fade_vol_step(struct aw_device *aw_dev) +{ + return aw_dev->vol_step; +} + +void aw_dev_set_fade_vol_step(struct aw_device *aw_dev, unsigned int step) +{ + aw_dev->vol_step = step; +} + +void aw_dev_get_fade_time(unsigned int *time, bool fade_in) +{ + if (fade_in) + *time = g_fade_in_time; + else + *time = g_fade_out_time; +} + +void aw_dev_set_fade_time(unsigned int time, bool fade_in) +{ + if (fade_in) + g_fade_in_time = time; + else + g_fade_out_time = time; +} + +/*init aw_device*/ +void aw_dev_deinit(struct aw_device *aw_dev) +{ + if (aw_dev == NULL) + return; + + if (aw_dev->prof_info.prof_desc != NULL) { + kfree(aw_dev->prof_info.prof_desc); + aw_dev->prof_info.prof_desc = NULL; + aw_dev->prof_info.count = 0; + } +} + +int aw_dev_get_cali_re(struct aw_device *aw_dev, int32_t *cali_re) +{ + + return aw_dsp_read_cali_re(aw_dev, cali_re); +} + +int aw_dev_dc_status(struct aw_device *aw_dev) +{ + return aw_dsp_get_dc_status(aw_dev); +} + +int aw_dev_status(struct aw_device *aw_dev) +{ + return aw_dev->status; +} + +int aw_dev_init_cali_re(struct aw_device *aw_dev) +{ + int ret = 0; + + if (aw_dev->cali_desc.mode) { + if (aw_dev->cali_desc.cali_re == AW_ERRO_CALI_VALUE) { + ret = aw_cali_read_re_from_nvram(&aw_dev->cali_desc.cali_re, aw_dev->channel); + if (ret) { + aw_dev_info(aw_dev->dev, "read nvram cali failed, use default Re"); + aw_dev->cali_desc.cali_re = AW_ERRO_CALI_VALUE; + } + } + } else { + aw_dev_info(aw_dev->dev, "no cali, needn't init cali re"); + } + return ret; +} + +static void aw_dev_soft_reset(struct aw_device *aw_dev) +{ + struct aw_soft_rst *reset = &aw_dev->soft_rst; + + aw_dev->ops.aw_i2c_write(aw_dev, reset->reg, reset->reg_value); + aw_dev_info(aw_dev->dev, "soft reset done"); +} + +int aw_device_irq_reinit(struct aw_device *aw_dev) +{ + int ret; + + /*reg re load*/ + ret = aw_dev_reg_fw_update(aw_dev); + if (ret < 0) + return ret; + + /*update vcalb*/ + aw_dev_set_vcalb(aw_dev); + + return 0; +} + +int aw_device_init(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + /*acf_hdr_t *hdr;*/ + int ret; + + if (aw_dev == NULL || aw_cfg == NULL) { + aw_dev_err(aw_dev->dev, "pointer is NULL"); + return -ENOMEM; + } + + ret = aw_dev_cfg_load(aw_dev, aw_cfg); + if (ret) { + aw_dev_deinit(aw_dev); + aw_dev_err(aw_dev->dev, "aw_dev acf load failed"); + return -EINVAL; + } + + aw_dev_soft_reset(aw_dev); + + aw_dev->cur_prof = AW_PROFILE_MUSIC; + aw_dev->set_prof = AW_PROFILE_MUSIC; + ret = aw_dev_reg_fw_update(aw_dev); + if (ret < 0) + return ret; + + ret = aw_dev_set_vcalb(aw_dev); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "can't set vcalb"); + return ret; + } + + aw_dev->status = AW_DEV_PW_ON; + aw_device_stop(aw_dev); + + aw_dev_info(aw_dev->dev, "init done"); + return 0; +} + +int aw_dev_reg_update(struct aw_device *aw_dev, bool force) +{ + int ret; + + if (force) { + aw_dev_soft_reset(aw_dev); + ret = aw_dev_reg_fw_update(aw_dev); + if (ret < 0) + return ret; + } else { + if (aw_dev->cur_prof != aw_dev->set_prof) { + ret = aw_dev_reg_fw_update(aw_dev); + if (ret < 0) + return ret; + } + } + + aw_dev->cur_prof = aw_dev->set_prof; + + aw_dev_info(aw_dev->dev, "done"); + return 0; +} + +static void aw_dev_cali_re_update(struct aw_device *aw_dev) +{ + struct aw_cali_desc *desc = &aw_dev->cali_desc; + + if (desc->mode) { + if ((desc->cali_re >= aw_dev->re_min) && + (desc->cali_re <= aw_dev->re_max)) + aw_dsp_write_cali_re(aw_dev, desc->cali_re); + } +} + +int aw_dev_prof_update(struct aw_device *aw_dev, bool force) +{ + int ret; + + /*if power on need off -- load -- on*/ + if (aw_dev->status == AW_DEV_PW_ON) { + aw_device_stop(aw_dev); + + ret = aw_dev_reg_update(aw_dev, force); + if (ret) { + aw_dev_err(aw_dev->dev, "fw update failed "); + return ret; + } + + ret = aw_device_start(aw_dev); + if (ret) { + aw_dev_err(aw_dev->dev, "start failed "); + return ret; + } + } else { + /*if pa off , only update set_prof value*/ + aw_dev_info(aw_dev->dev, "set prof[%d] done !", aw_dev->set_prof); + } + + aw_dev_info(aw_dev->dev, "update done !"); + return 0; +} + +static void aw_dev_boost_type_set(struct aw_device *aw_dev) +{ + struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; + struct aw_bstctrl_desc *bstctrl_desc = &aw_dev->bstctrl_desc; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (aw_dev->bstcfg_enable) { + /*set spk mode*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, profctrl_desc->reg, + profctrl_desc->mask, profctrl_desc->spk_mode); + + /*force boost*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, bstctrl_desc->reg, + bstctrl_desc->mask, bstctrl_desc->frc_bst); + + aw_dev_dbg(aw_dev->dev, "boost type set done"); + } +} + +static void aw_dev_boost_type_recover(struct aw_device *aw_dev) +{ + + struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; + struct aw_bstctrl_desc *bstctrl_desc = &aw_dev->bstctrl_desc; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (aw_dev->bstcfg_enable) { + /*set transprant*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, bstctrl_desc->reg, + bstctrl_desc->mask, bstctrl_desc->tsp_type); + + usleep_range(AW_5000_US, AW_5000_US + 50); + /*set cfg boost type*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, bstctrl_desc->reg, + bstctrl_desc->mask, bstctrl_desc->cfg_bst_type); + + /*set cfg prof mode*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, profctrl_desc->reg, + profctrl_desc->mask, profctrl_desc->cfg_prof_mode); + + aw_dev_dbg(aw_dev->dev, "boost type recover done"); + } +} + +int aw_device_start(struct aw_device *aw_dev) +{ + int ret; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (aw_dev->status == AW_DEV_PW_ON) { + aw_dev_info(aw_dev->dev, "already power on"); + return 0; + } + + /*set froce boost*/ + aw_dev_boost_type_set(aw_dev); + + /*power on*/ + aw_dev_pwd(aw_dev, false); + usleep_range(AW_2000_US, AW_2000_US + 10); + + ret = aw_dev_syspll_check(aw_dev); + if (ret < 0) { + aw_dev_reg_dump(aw_dev); + aw_dev_pwd(aw_dev, true); + aw_dev_dbg(aw_dev->dev, "pll check failed cannot start"); + return ret; + } + + /*amppd on*/ + aw_dev_amppd(aw_dev, false); + usleep_range(AW_1000_US, AW_1000_US + 50); + + /*check i2s status*/ + ret = aw_dev_sysst_check(aw_dev); + if (ret < 0) { + aw_dev_reg_dump(aw_dev); + /*close tx feedback*/ + if (aw_dev->ops.aw_i2s_enable) + aw_dev->ops.aw_i2s_enable(aw_dev, false); + /*clear interrupt*/ + aw_dev_clear_int_status(aw_dev); + /*close amppd*/ + aw_dev_amppd(aw_dev, true); + /*power down*/ + aw_dev_pwd(aw_dev, true); + return -EINVAL; + } + + /*boost type recover*/ + aw_dev_boost_type_recover(aw_dev); + + /*enable tx feedback*/ + if (aw_dev->ops.aw_i2s_enable) + aw_dev->ops.aw_i2s_enable(aw_dev, true); + + if (aw_dev->amppd_st) { + aw_dev_amppd(aw_dev, true); + } + + if (aw_dev->ops.aw_reg_force_set) + aw_dev->ops.aw_reg_force_set(aw_dev); + + if (!aw_dev->mute_st) { + /*close mute*/ + aw_dev_mute(aw_dev, false); + } + + /*clear inturrupt*/ + aw_dev_clear_int_status(aw_dev); + /*set inturrupt mask*/ + aw_dev_set_intmask(aw_dev, true); + + aw_monitor_start(&aw_dev->monitor_desc); + aw_dev_cali_re_update(aw_dev); + + aw_dev->status = AW_DEV_PW_ON; + aw_dev_info(aw_dev->dev, "done"); + return 0; +} + +int aw_device_stop(struct aw_device *aw_dev) +{ + aw_dev_dbg(aw_dev->dev, "enter"); + + if (aw_dev->status == AW_DEV_PW_OFF) { + aw_dev_dbg(aw_dev->dev, "already power off"); + return 0; + } + + aw_dev->status = AW_DEV_PW_OFF; + + aw_monitor_stop(&aw_dev->monitor_desc); + /*clear interrupt*/ + aw_dev_clear_int_status(aw_dev); + + /*set defaut int mask*/ + aw_dev_set_intmask(aw_dev, false); + + /*set mute*/ + aw_dev_mute(aw_dev, true); + + /*close tx feedback*/ + if (aw_dev->ops.aw_i2s_enable) + aw_dev->ops.aw_i2s_enable(aw_dev, false); + + usleep_range(AW_1000_US, AW_1000_US + 100); + + /*enable amppd*/ + aw_dev_amppd(aw_dev, true); + + /*set power down*/ + aw_dev_pwd(aw_dev, true); + + ext_dsp_prof_write = AW_EXT_DSP_WRITE_NONE; + aw_dev_info(aw_dev->dev, "done"); + return 0; +} + +int aw_dev_set_afe_module_en(int type, int enable) +{ + return aw_dsp_set_afe_module_en(type, enable); +} + +int aw_dev_get_afe_module_en(int type, int *status) +{ + return aw_dsp_get_afe_module_en(type, status); +} + +int aw_dev_set_copp_module_en(bool enable) +{ + return aw_dsp_set_copp_module_en(enable); +} + +int aw_dev_set_spin(int spin_mode) +{ + return aw_dsp_write_spin(spin_mode); +} + +int aw_dev_get_spin(int *spin_mode) +{ + return aw_dsp_read_spin(spin_mode); +} + +int aw_dev_set_algo_prof(struct aw_device *aw_dev, int prof_id) +{ + return aw_dsp_set_algo_prof(aw_dev, prof_id); +} + +int aw_dev_get_algo_prof(struct aw_device *aw_dev, int *prof_id) +{ + return aw_dsp_get_algo_prof(aw_dev, prof_id); +} + +int aw_dev_set_algo_params_path(struct aw_device *aw_dev) +{ + return aw_dsp_set_algo_params_path(aw_dev); +} + +static void aw_device_parse_sound_channel_dt(struct aw_device *aw_dev) +{ + int ret; + uint32_t channel_value; + + aw_dev->channel = AW_DEV_CH_PRI_L; + ret = of_property_read_u32(aw_dev->dev->of_node, "sound-channel", &channel_value); + if (ret < 0) { + aw_dev_info(aw_dev->dev, "read sound-channel failed,use default"); + return; + } + + aw_dev_dbg(aw_dev->dev, "read sound-channel value is : %d", channel_value); + if (channel_value >= AW_DEV_CH_MAX) { + channel_value = AW_DEV_CH_PRI_L; + } + aw_dev->channel = channel_value; +} + +static void aw_device_parse_dt(struct aw_device *aw_dev) +{ + aw_device_parse_sound_channel_dt(aw_dev); + aw_device_parse_topo_id_dt(aw_dev); + aw_device_parse_port_id_dt(aw_dev); +} + +int aw_dev_get_list_head(struct list_head **head) +{ + if (list_empty(&g_dev_list)) + return -EINVAL; + + *head = &g_dev_list; + + return 0; +} + +void aw_device_algo_path_init(struct aw_device *aw_dev) +{ + + memcpy(aw_dev->algo_path, AW_ALGO_PARAMS_PATH, sizeof(AW_ALGO_PARAMS_PATH)); + aw_dev_dbg(aw_dev->dev, "aw_device_algo_path_init"); +} + +int aw_device_probe(struct aw_device *aw_dev) +{ + int ret; + INIT_LIST_HEAD(&aw_dev->list_node); + aw_dev_dbg(aw_dev->dev, "aw_device_probe 01"); + aw_device_parse_dt(aw_dev); + ret = aw_cali_init(&aw_dev->cali_desc); + if (ret) + return ret; + aw_dev_dbg(aw_dev->dev, "aw_device_probe 02"); + aw_monitor_init(&aw_dev->monitor_desc); + /*aw_afe_init();*/ + + aw_device_algo_path_init(aw_dev); + + mutex_lock(&g_dev_lock); + list_add(&aw_dev->list_node, &g_dev_list); + mutex_unlock(&g_dev_lock); + + return 0; +} + +int aw_device_remove(struct aw_device *aw_dev) +{ + aw_monitor_deinit(&aw_dev->monitor_desc); + aw_cali_deinit(&aw_dev->cali_desc); + /*aw_afe_deinit();*/ + return 0; +} + diff --git a/asoc/codecs/aw88261/aw_device.h b/asoc/codecs/aw88261/aw_device.h new file mode 100644 index 000000000000..7b7315d24555 --- /dev/null +++ b/asoc/codecs/aw88261/aw_device.h @@ -0,0 +1,292 @@ +#ifndef __AWINIC_DEVICE_FILE_H__ +#define __AWINIC_DEVICE_FILE_H__ +#include "aw_data_type.h" +#include "aw_calib.h" +#include "aw_monitor.h" + + +#define AW_VOLUME_STEP_DB (6 * 2) +#define AW_REG_NONE (0xFF) +#define AW_NAME_MAX (50) +#define ALGO_VERSION_MAX (80) +#define AW_ALGO_PATH_MAX (256) + + +enum { + AW_1000_US = 1000, + AW_2000_US = 2000, + AW_3000_US = 3000, + AW_4000_US = 4000, + AW_5000_US = 5000, + AW_10000_US = 10000, +}; + +struct aw_device; + +enum { + AW_DEV_TYPE_OK = 0, + AW_DEV_TYPE_NONE = 1, +}; + +enum { + AW_DEV_CH_PRI_L = 0, + AW_DEV_CH_PRI_R = 1, + AW_DEV_CH_SEC_L = 2, + AW_DEV_CH_SEC_R = 3, + AW_DEV_CH_MAX, +}; + +enum AW_DEV_INIT { + AW_DEV_INIT_ST = 0, + AW_DEV_INIT_OK = 1, + AW_DEV_INIT_NG = 2, +}; + +enum AW_DEV_STATUS { + AW_DEV_PW_OFF = 0, + AW_DEV_PW_ON, +}; + +enum AW_DEV_FW_STATUS { + AW_DEV_FW_FAILED = 0, + AW_DEV_FW_OK, +}; + +struct aw_device_ops { + int (*aw_i2c_write)(struct aw_device *aw_dev, unsigned char reg_addr, unsigned int reg_data); + int (*aw_i2c_read)(struct aw_device *aw_dev, unsigned char reg_addr, unsigned int *reg_data); + int (*aw_i2c_write_bits)(struct aw_device *aw_dev, unsigned char reg_addr, unsigned int mask, unsigned int reg_data); + int (*aw_set_volume)(struct aw_device *aw_dev, unsigned int value); + int (*aw_get_volume)(struct aw_device *aw_dev, unsigned int *value); + unsigned int (*aw_reg_val_to_db)(unsigned int value); + void (*aw_i2s_enable)(struct aw_device *aw_dev, bool flag); + bool (*aw_check_wr_access)(int reg); + bool (*aw_check_rd_access)(int reg); + int (*aw_get_reg_num)(void); + int (*aw_get_version)(char *buf, int size); + int (*aw_get_dev_num)(void); + void (*aw_set_algo)(struct aw_device *aw_dev); + unsigned int (*aw_get_irq_type)(struct aw_device *aw_dev, unsigned int value); + void (*aw_reg_force_set)(struct aw_device *aw_dev); +}; + +struct aw_int_desc { + unsigned int mask_reg; /*interrupt mask reg*/ + unsigned int st_reg; /*interrupt status reg*/ + unsigned int mask_default; /*default mask close all*/ + unsigned int int_mask; /*set mask*/ +}; + +struct aw_soft_rst { + int reg; + int reg_value; +}; + +struct aw_pwd_desc { + unsigned int reg; + unsigned int mask; + unsigned int enable; + unsigned int disable; +}; + +struct aw_amppd_desc { + unsigned int reg; + unsigned int mask; + unsigned int enable; + unsigned int disable; +}; + +struct aw_bop_desc { + unsigned int reg; + unsigned int mask; + unsigned int enable; + unsigned int disbale; +}; + +struct aw_vcalb_desc { + unsigned int icalk_reg; + unsigned int icalk_reg_mask; + unsigned int icalk_shift; + unsigned int icalkl_reg; + unsigned int icalkl_reg_mask; + unsigned int icalkl_shift; + unsigned int icalk_sign_mask; + unsigned int icalk_neg_mask; + int icalk_value_factor; + + unsigned int vcalk_reg; + unsigned int vcalk_reg_mask; + unsigned int vcalk_shift; + unsigned int vcalkl_reg; + unsigned int vcalkl_reg_mask; + unsigned int vcalkl_shift; + unsigned int vcalk_sign_mask; + unsigned int vcalk_neg_mask; + int vcalk_value_factor; + + unsigned int vcalb_reg; + int cabl_base_value; + int vcal_factor; +}; + +struct aw_mute_desc { + unsigned int reg; + unsigned int mask; + unsigned int enable; + unsigned int disable; +}; + +struct aw_sysst_desc { + unsigned int reg; + unsigned int mask; + unsigned int st_check; + unsigned int pll_check; +}; + +struct aw_profctrl_desc { + unsigned int reg; + unsigned int mask; + unsigned int spk_mode; + unsigned int cfg_prof_mode; +}; + +struct aw_bstctrl_desc { + unsigned int reg; + unsigned int mask; + unsigned int frc_bst; + unsigned int tsp_type; + unsigned int cfg_bst_type; +}; + +struct aw_cco_mux_desc { + unsigned int reg; + unsigned int mask; + unsigned int divided_val; + unsigned int bypass_val; +}; + +struct aw_volume_desc { + unsigned int reg; + unsigned int mask; + unsigned int shift; + int init_volume; + int mute_volume; +}; + +struct aw_voltage_desc { + unsigned int reg; + unsigned int vbat_range; + unsigned int int_bit; +}; + +struct aw_temperature_desc { + unsigned int reg; + unsigned int sign_mask; + unsigned int neg_mask; +}; + +struct aw_ipeak_desc { + unsigned int reg; + unsigned int mask; +}; + +struct aw_container { + int len; + uint8_t data[]; +}; + +struct aw_device { + int index; + int status; + int bstcfg_enable; + int frcset_en; + int bop_en; + unsigned int mute_st; + unsigned int amppd_st; + + unsigned char cur_prof; /*current profile index*/ + unsigned char set_prof; /*set profile index*/ + unsigned int channel; /*pa channel select*/ + unsigned int vol_step; + unsigned int re_max; + unsigned int re_min; + + struct device *dev; + struct i2c_client *i2c; + char acf_name[AW_NAME_MAX]; + char monitor_name[AW_NAME_MAX]; + void *private_data; + char algo_path[AW_ALGO_PATH_MAX]; + + struct aw_int_desc int_desc; + struct aw_pwd_desc pwd_desc; + struct aw_amppd_desc amppd_desc; + struct aw_mute_desc mute_desc; + struct aw_vcalb_desc vcalb_desc; + struct aw_sysst_desc sysst_desc; + struct aw_profctrl_desc profctrl_desc; + struct aw_bstctrl_desc bstctrl_desc; + struct aw_cco_mux_desc cco_mux_desc; + struct aw_voltage_desc voltage_desc; + struct aw_temperature_desc temp_desc; + struct aw_ipeak_desc ipeak_desc; + struct aw_volume_desc volume_desc; + struct aw_prof_info prof_info; + struct aw_cali_desc cali_desc; + struct aw_monitor_desc monitor_desc; + struct aw_soft_rst soft_rst; + struct aw_bop_desc bop_desc; + struct aw_device_ops ops; + struct list_head list_node; +}; + + +int aw_dev_load_acf_check(struct aw_container *aw_cfg); +void aw_dev_deinit(struct aw_device *aw_dev); +int aw_device_init(struct aw_device *aw_dev, struct aw_container *aw_cfg); +int aw_device_start(struct aw_device *aw_dev); +int aw_device_stop(struct aw_device *aw_dev); +int aw_dev_reg_update(struct aw_device *aw_dev, bool force); +int aw_device_irq_reinit(struct aw_device *aw_dev); + +/*profile*/ +int aw_dev_prof_update(struct aw_device *aw_dev, bool force); +int aw_dev_get_profile_count(struct aw_device *aw_dev); +int aw_dev_get_profile_name(struct aw_device *aw_dev, char *name, int index); +int aw_dev_check_profile_index(struct aw_device *aw_dev, int index); +int aw_dev_get_profile_index(struct aw_device *aw_dev); +int aw_dev_set_profile_index(struct aw_device *aw_dev, int index); + +/*re*/ +int aw_dev_get_cali_re(struct aw_device *aw_dev, int32_t *cali_re); +int aw_dev_init_cali_re(struct aw_device *aw_dev); +int aw_dev_dc_status(struct aw_device *aw_dev); + +/*interrupt*/ +int aw_dev_status(struct aw_device *aw_dev); +int aw_dev_get_int_status(struct aw_device *aw_dev, uint16_t *int_status); +void aw_dev_clear_int_status(struct aw_device *aw_dev); +int aw_dev_set_intmask(struct aw_device *aw_dev, bool flag); + +/*fade int / out*/ +void aw_dev_set_fade_vol_step(struct aw_device *aw_dev, unsigned int step); +int aw_dev_get_fade_vol_step(struct aw_device *aw_dev); +void aw_dev_get_fade_time(unsigned int *time, bool fade_in); +void aw_dev_set_fade_time(unsigned int time, bool fade_in); + +/*dsp kcontrol*/ +int aw_dev_set_afe_module_en(int type, int enable); +int aw_dev_get_afe_module_en(int type, int *status); +int aw_dev_set_copp_module_en(bool enable); +int aw_dev_set_spin(int spin_mode); +int aw_dev_get_spin(int *spin_mode); +int aw_dev_set_algo_prof(struct aw_device *aw_dev, int prof_id); +int aw_dev_get_algo_prof(struct aw_device *aw_dev, int *prof_id); +int aw_dev_set_algo_params_path(struct aw_device *aw_dev); + +int aw_device_probe(struct aw_device *aw_dev); +int aw_device_remove(struct aw_device *aw_dev); +int aw_dev_get_list_head(struct list_head **head); + +#endif + diff --git a/asoc/codecs/aw88261/aw_dsp.c b/asoc/codecs/aw88261/aw_dsp.c new file mode 100644 index 000000000000..4d2424854c5b --- /dev/null +++ b/asoc/codecs/aw88261/aw_dsp.c @@ -0,0 +1,1260 @@ +/*#define DEBUG*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aw_device.h" +#include "aw_dsp.h" +#include "aw_log.h" +/*#include "aw_afe.h"*/ + +static DEFINE_MUTEX(g_aw_dsp_msg_lock); +static DEFINE_MUTEX(g_aw_dsp_lock); + + +#define AW_MSG_ID_ENABLE_CALI (0x00000001) +#define AW_MSG_ID_ENABLE_HMUTE (0x00000002) +#define AW_MSG_ID_F0_Q (0x00000003) +#define AW_MSG_ID_DIRECT_CUR_FLAG (0x00000006) +#define AW_MSG_ID_SPK_STATUS (0x00000007) +#define AW_MSG_ID_SWITCH_SCENE (0x00000013) +#define AW_MSG_ID_ALGO_PARAMS_PATH (0x00000014) +#define AW_MSG_ID_VERSION (0x00000008) +#define AW_MSG_ID_VERSION_NEW (0x00000012) + + +/*dsp params id*/ +#define AW_MSG_ID_RX_SET_ENABLE (0x10013D11) +#define AW_MSG_ID_PARAMS (0x10013D12) +#define AW_MSG_ID_TX_SET_ENABLE (0x10013D13) +#define AW_MSG_ID_VMAX_L (0X10013D17) +#define AW_MSG_ID_VMAX_R (0X10013D18) +#define AW_MSG_ID_CALI_CFG_L (0X10013D19) +#define AW_MSG_ID_CALI_CFG_R (0x10013d1A) +#define AW_MSG_ID_RE_L (0x10013d1B) +#define AW_MSG_ID_RE_R (0X10013D1C) +#define AW_MSG_ID_NOISE_L (0X10013D1D) +#define AW_MSG_ID_NOISE_R (0X10013D1E) +#define AW_MSG_ID_F0_L (0X10013D1F) +#define AW_MSG_ID_F0_R (0X10013D20) +#define AW_MSG_ID_REAL_DATA_L (0X10013D21) +#define AW_MSG_ID_REAL_DATA_R (0X10013D22) +#define AW_MSG_ID_SET_BIN_PARAMS (0X10013D30) + +#define AFE_MSG_ID_MSG_0 (0X10013D2A) +#define AFE_MSG_ID_MSG_1 (0X10013D2B) +#define AW_MSG_ID_PARAMS_1 (0x10013D2D) + +#define AW_MSG_ID_SPIN (0x10013D2E) + +int g_tx_topo_id = AW_TX_DEFAULT_TOPO_ID; +int g_rx_topo_id = AW_RX_DEFAULT_TOPO_ID; +int g_tx_port_id = AW_TX_DEFAULT_PORT_ID; +int g_rx_port_id = AW_RX_DEFAULT_PORT_ID; + +enum { + MSG_PARAM_ID_0 = 0, + MSG_PARAM_ID_1, + MSG_PARAM_ID_MAX, +}; + +static uint32_t afe_param_msg_id[MSG_PARAM_ID_MAX] = { + AFE_MSG_ID_MSG_0, + AFE_MSG_ID_MSG_1, +}; + +/***************dsp communicate**************/ +#ifdef CONFIG_AW882XX_DSP +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 1)) +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#endif +#endif + +#define AW_COPP_MODULE_ID (0X10013D02) /*SKT module id*/ +#define AW_COPP_PARAMS_ID_AWDSP_ENABLE (0X10013D14) /*SKT enable param id*/ + +#ifdef AW_MTK_PLATFORM_WITH_DSP +extern int mtk_spk_send_ipi_buf_to_dsp(void *data_buffer, uint32_t data_size); +extern int mtk_spk_recv_ipi_buf_from_dsp(int8_t *buffer, int16_t size, uint32_t *buf_len); +#elif defined CONFIG_AW882XX_DSP +extern int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id,uint32_t param_id, + void *buf, int cmd_size, bool write); +extern int aw_send_afe_rx_module_enable(int32_t rx_port_id, void *buf, int size); +extern int aw_send_afe_tx_module_enable(int32_t tx_port_id, void *buf, int size); +extern int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable); +#else +static int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id, uint32_t param_id, + void *buf, int cmd_size, bool write) +{ + return 0; +} +static int aw_send_afe_rx_module_enable(int32_t rx_port_id, void *buf, int size) +{ + return 0; +} +static int aw_send_afe_tx_module_enable(int32_t tx_port_id, void *buf, int size) +{ + return 0; +} + +#endif + +#ifdef CONFIG_AW882XX_DSP +extern int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable); +#else +static int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable) +{ +#if 0 + /*for v3*/ + int copp_idx = 0; + uint32_t enable_param; + struct param_hdr_v3 param_hdr; + int rc = 0; + + pr_debug("%s port_id %d, module_id 0x%x, enable %d\n", + __func__, port_id, module_id, enable); + + copp_idx = adm_get_default_copp_idx(port_id); + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + if (enable < 0 || enable > 1) { + pr_err("%s: Invalid value for enable %d\n", __func__, enable); + return -EINVAL; + } + + pr_debug("%s port_id %d, module_id 0x%x, copp_idx 0x%x, enable %d\n", + __func__, port_id, module_id, copp_idx, enable); + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = module_id; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = param_id; + param_hdr.param_size = sizeof(enable_param); + enable_param = enable; + + rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (uint8_t *) &enable_param); + if (rc) + pr_err("%s: Failed to set enable of module(%d) instance(%d) to %d, err %d\n", + __func__, module_id, INSTANCE_ID_0, enable, rc); + return rc; +#endif + return 0; +} +#endif + +static int aw_get_msg_num(int dev_ch, int *msg_num) +{ + switch (dev_ch) { + case AW_DEV_CH_PRI_L: + *msg_num = MSG_PARAM_ID_0; + break; + case AW_DEV_CH_PRI_R: + *msg_num = MSG_PARAM_ID_0; + break; + case AW_DEV_CH_SEC_L: + *msg_num = MSG_PARAM_ID_1; + break; + case AW_DEV_CH_SEC_R: + *msg_num = MSG_PARAM_ID_1; + break; + default: + aw_pr_err("can not find msg num, channel %d ", dev_ch); + return -EINVAL; + } + + aw_pr_dbg("msg num[%d] ", *msg_num); + return 0; +} + +#ifdef AW_MTK_PLATFORM_WITH_DSP +/*****************mtk dsp communication function start**********************/ +static int aw_mtk_write_data_to_dsp(int param_id, void *data, int size) +{ + int32_t *dsp_data = NULL; + aw_dsp_msg_t *hdr = NULL; + int ret; + + dsp_data = kzalloc(sizeof(aw_dsp_msg_t) + size, GFP_KERNEL); + if (!dsp_data) { + pr_err("%s: kzalloc dsp_msg error\n", __func__); + return -ENOMEM; + } + + hdr = (aw_dsp_msg_t *)dsp_data; + hdr->type = AW_DSP_MSG_TYPE_DATA; + hdr->opcode_id = param_id; + hdr->version = AW_DSP_MSG_HDR_VER; + + memcpy(((char *)dsp_data) + sizeof(aw_dsp_msg_t), + data, size); + + ret = mtk_spk_send_ipi_buf_to_dsp(dsp_data, + sizeof(aw_dsp_msg_t) + size); + if (ret < 0) { + pr_err("%s:write data failed\n", __func__); + kfree(dsp_data); + dsp_data = NULL; + return ret; + } + + kfree(dsp_data); + dsp_data = NULL; + return 0; +} + +static int aw_mtk_read_data_from_dsp(int param_id, void *data, int size) +{ + int ret; + aw_dsp_msg_t hdr; + + hdr.type = AW_DSP_MSG_TYPE_CMD; + hdr.opcode_id = param_id; + hdr.version = AW_DSP_MSG_HDR_VER; + + mutex_lock(&g_aw_dsp_msg_lock); + ret = mtk_spk_send_ipi_buf_to_dsp(&hdr, sizeof(aw_dsp_msg_t)); + if (ret < 0) { + pr_err("%s:send cmd failed\n", __func__); + goto dsp_msg_failed; + } + + ret = mtk_spk_recv_ipi_buf_from_dsp(data, size, &size); + if (ret < 0) { + pr_err("%s:get data failed\n", __func__); + goto dsp_msg_failed; + } + mutex_unlock(&g_aw_dsp_msg_lock); + return 0; + +dsp_msg_failed: + mutex_unlock(&g_aw_dsp_msg_lock); + return ret; +} + +static int aw_mtk_write_msg_to_dsp(int msg_num, int inline_id, + void *data, int size) +{ + int32_t *dsp_msg = NULL; + aw_dsp_msg_t *hdr = NULL; + int ret; + + dsp_msg = kzalloc(sizeof(aw_dsp_msg_t) + size, + GFP_KERNEL); + if (!dsp_msg) { + pr_err("%s: inline_id:0x%x kzalloc dsp_msg error\n", + __func__, inline_id); + return -ENOMEM; + } + hdr = (aw_dsp_msg_t *)dsp_msg; + hdr->type = AW_DSP_MSG_TYPE_DATA; + hdr->opcode_id = inline_id; + hdr->version = AW_DSP_MSG_HDR_VER; + + memcpy(((char *)dsp_msg) + sizeof(aw_dsp_msg_t), + data, size); + + ret = aw_mtk_write_data_to_dsp(afe_param_msg_id[msg_num], (void *)dsp_msg, + sizeof(aw_dsp_msg_t) + size); + if (ret < 0) { + pr_err("%s:inline_id:0x%x, write data failed\n", + __func__, inline_id); + kfree(dsp_msg); + dsp_msg = NULL; + return ret; + } + + kfree(dsp_msg); + dsp_msg = NULL; + return 0; +} + +static int aw_mtk_read_msg_from_dsp(int msg_num, int inline_id, + char *data, int size) +{ + aw_dsp_msg_t hdr[2]; + int ret; + + hdr[0].type = AW_DSP_MSG_TYPE_DATA; + hdr[0].opcode_id = afe_param_msg_id[msg_num]; + hdr[0].version = AW_DSP_MSG_HDR_VER; + hdr[1].type = AW_DSP_MSG_TYPE_CMD; + hdr[1].opcode_id = inline_id; + hdr[1].version = AW_DSP_MSG_HDR_VER; + + mutex_lock(&g_aw_dsp_msg_lock); + ret = mtk_spk_send_ipi_buf_to_dsp(&hdr, 2 * sizeof(aw_dsp_msg_t)); + if (ret < 0) { + pr_err("%s:send cmd failed\n", __func__); + goto dsp_msg_failed; + } + + ret = mtk_spk_recv_ipi_buf_from_dsp(data, size, &size); + if (ret < 0) { + pr_err("%s:get data failed\n", __func__); + goto dsp_msg_failed; + } + mutex_unlock(&g_aw_dsp_msg_lock); + return 0; + +dsp_msg_failed: + mutex_unlock(&g_aw_dsp_msg_lock); + return ret; +} + +/*****************mtk dsp communication function end**********************/ +#else +/*****************qcom dsp communication function start**********************/ +#if 0 +static int aw_afe_get_topology(uint32_t param_id) +{ + if (param_id == AW_MSG_ID_TX_SET_ENABLE) + return afe_get_topology(g_tx_port_id); + else + return afe_get_topology(g_rx_port_id); +} + +static int aw_check_dsp_ready(uint32_t param_id) +{ + int ret = 0; + + //ret = aw_afe_get_topology(param_id); + + aw_pr_dbg("topo_id 0x%x ", ret); + + if (param_id == AW_MSG_ID_TX_SET_ENABLE) { + if (ret != g_tx_topo_id) + return false; + else + return true; + } else { + if (ret != g_rx_topo_id) + return false; + else + return true; + } +} +#endif + +static int aw_qcom_write_data_to_dsp(uint32_t param_id, void *data, int size) +{ + int ret; + + mutex_lock(&g_aw_dsp_lock); + ret = aw_send_afe_cal_apr(g_rx_port_id, g_tx_port_id, param_id, data, size, true); + mutex_unlock(&g_aw_dsp_lock); + return ret; +} + +static int aw_qcom_read_data_from_dsp(uint32_t param_id, void *data, int size) +{ + int ret; + + mutex_lock(&g_aw_dsp_lock); + ret = aw_send_afe_cal_apr(g_rx_port_id, g_tx_port_id, param_id, data, size, false); + mutex_unlock(&g_aw_dsp_lock); + return ret; +} + +static int aw_qcom_write_msg_to_dsp(int msg_num, uint32_t msg_id, char *data_ptr, unsigned int size) +{ + int32_t *dsp_msg; + int ret = 0; + int msg_len = (int)(sizeof(aw_dsp_msg_t) + size); + + mutex_lock(&g_aw_dsp_msg_lock); + dsp_msg = kzalloc(msg_len, GFP_KERNEL); + if (!dsp_msg) { + aw_pr_err("msg_id:0x%x kzalloc dsp_msg error", + msg_id); + ret = -ENOMEM; + goto w_mem_err; + } + dsp_msg[0] = AW_DSP_MSG_TYPE_DATA; + dsp_msg[1] = msg_id; + dsp_msg[2] = AW_DSP_MSG_HDR_VER; + + memcpy(dsp_msg + (sizeof(aw_dsp_msg_t) / sizeof(int32_t)), + data_ptr, size); + + ret = aw_qcom_write_data_to_dsp(afe_param_msg_id[msg_num], + (void *)dsp_msg, msg_len); + if (ret < 0) { + aw_pr_err("msg_id:0x%x, write data to dsp failed", msg_id); + kfree(dsp_msg); + goto w_mem_err; + } + + aw_pr_dbg("msg_id:0x%x, write data[%d] to dsp success", msg_id, msg_len); + mutex_unlock(&g_aw_dsp_msg_lock); + kfree(dsp_msg); + return 0; +w_mem_err: + mutex_unlock(&g_aw_dsp_msg_lock); + return ret; +} + +static int aw_qcom_read_msg_from_dsp(int msg_num, uint32_t msg_id, char *data_ptr, unsigned int size) +{ + aw_dsp_msg_t cmd_msg; + int ret; + + mutex_lock(&g_aw_dsp_msg_lock); + cmd_msg.type = AW_DSP_MSG_TYPE_CMD; + cmd_msg.opcode_id = msg_id; + cmd_msg.version = AW_DSP_MSG_HDR_VER; + + ret = aw_qcom_write_data_to_dsp(afe_param_msg_id[msg_num], + &cmd_msg, sizeof(aw_dsp_msg_t)); + if (ret < 0) { + aw_pr_err("msg_id:0x%x, write cmd to dsp failed", msg_id); + goto dsp_msg_failed; + } + + ret = aw_qcom_read_data_from_dsp(afe_param_msg_id[msg_num], + data_ptr, (int)size); + if (ret < 0) { + aw_pr_err("msg_id:0x%x, read data from dsp failed", msg_id); + goto dsp_msg_failed; + } + + aw_pr_dbg("msg_id:0x%x, read data[%d] from dsp success", msg_id, size); + mutex_unlock(&g_aw_dsp_msg_lock); + return 0; +dsp_msg_failed: + mutex_unlock(&g_aw_dsp_msg_lock); + return ret; +} + +/******************* afe module communication function ************************/ +static int aw_dsp_set_afe_rx_module_enable(void *buf, int size) +{ + return aw_send_afe_rx_module_enable(g_rx_port_id, buf, size); +} + +static int aw_dsp_set_afe_tx_module_enable(void *buf, int size) +{ + return aw_send_afe_tx_module_enable(g_tx_port_id, buf, size); +} + +static int aw_dsp_get_afe_rx_module_enable(void *buf, int size) +{ + return aw_qcom_read_data_from_dsp(AW_MSG_ID_RX_SET_ENABLE, buf, size); +} + +static int aw_dsp_get_afe_tx_module_enable(void *buf, int size) +{ + return aw_qcom_read_data_from_dsp(AW_MSG_ID_TX_SET_ENABLE, buf, size); +} + +/******************* read/write msg communication function ***********************/ +static int aw_read_msg_from_dsp(int msg_num, uint32_t msg_id, char *data_ptr, unsigned int size) +{ + return aw_qcom_read_msg_from_dsp(msg_num, msg_id, data_ptr, size); +} + +static int aw_write_msg_to_dsp(int msg_num, uint32_t msg_id, char *data_ptr, unsigned int size) +{ + return aw_qcom_write_msg_to_dsp(msg_num, msg_id, data_ptr, size); +} + +/******************* read/write data communication function ***********************/ +static int aw_read_data_from_dsp(uint32_t param_id, void *data, int size) +{ + return aw_qcom_read_data_from_dsp(param_id, data, size); +} + +static int aw_write_data_to_dsp(uint32_t param_id, void *data, int size) +{ + return aw_qcom_write_data_to_dsp(param_id, data, size); +} +#endif + +/************************* dsp communication function *****************************/ +int aw_dsp_set_algo_prof(struct aw_device *aw_dev, int prof_id) +{ + int ret; + int msg_num; + int32_t data = prof_id; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_write_msg_to_dsp(msg_num, AW_MSG_ID_SWITCH_SCENE, + (char *)&data, sizeof(int32_t)); + if (ret < 0) { + aw_pr_err("write prof_id failed "); + return ret; + } + aw_pr_dbg("write prof_id done"); + + return 0; + +} + +int aw_dsp_get_algo_prof(struct aw_device *aw_dev, int *prof_id) +{ + int ret; + int msg_num; + int32_t data = 0; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_SWITCH_SCENE, + (char *)&data, sizeof(int32_t)); + if (ret < 0) { + *prof_id = 0; + aw_dev_err(aw_dev->dev, "read algo prof failed"); + return ret; + } + + *prof_id = data; + aw_pr_dbg("read prof_id:%d done", *prof_id); + return 0; +} + +int aw_dsp_set_algo_params_path(struct aw_device *aw_dev) +{ + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_write_msg_to_dsp(msg_num, AW_MSG_ID_ALGO_PARAMS_PATH, + (char *)aw_dev->algo_path, + sizeof(aw_dev->algo_path)); + if (ret) + return -EINVAL; + + aw_pr_info("set algo params path: %s", aw_dev->algo_path); + return 0; +} + +int aw_dsp_set_afe_module_en(int type, int enable) +{ + int ret; + + switch (type) { + case AW_RX_MODULE: + ret = aw_dsp_set_afe_rx_module_enable(&enable, sizeof(int32_t)); + break; + case AW_TX_MODULE: + ret = aw_dsp_set_afe_tx_module_enable(&enable, sizeof(int32_t)); + break; + default: + pr_err("%s: unsupported type %d\n", __func__, type); + return -EINVAL; + } + + return ret; +} + +int aw_dsp_get_afe_module_en(int type, int *status) +{ + int ret; + + switch (type) { + case AW_RX_MODULE: + ret = aw_dsp_get_afe_rx_module_enable(status, sizeof(int32_t)); + break; + case AW_TX_MODULE: + ret = aw_dsp_get_afe_tx_module_enable(status, sizeof(int32_t)); + break; + default: + pr_err("%s: unsupported type %d\n", __func__, type); + return -EINVAL; + } + + return ret; +} + +int aw_dsp_read_te(struct aw_device *aw_dev, int32_t *te) +{ + int ret; + int msg_num; + int32_t data[8]; /*[re:r0:Te:r0_te]*/ + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_SPK_STATUS, + (char *)data, sizeof(int32_t) * 8); + if (ret) { + aw_dev_err(aw_dev->dev, " read Te failed "); + return ret; + } + + if ((aw_dev->channel % 2) == 0) + *te = data[2]; + else + *te = data[6]; + + aw_dev_dbg(aw_dev->dev, "read Te %d", *te); + return 0; +} + +int aw_dsp_read_st(struct aw_device *aw_dev, int32_t *r0, int32_t *te) +{ + int ret; + int msg_num; + int32_t data[8]; /*[re:r0:Te:r0_te]*/ + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_SPK_STATUS, + (char *)data, sizeof(int32_t) * 8); + if (ret) { + aw_dev_err(aw_dev->dev, "read spk st failed"); + return ret; + } + + if ((aw_dev->channel % 2) == 0) { + *r0 = AW_DSP_RE_TO_SHOW_RE(data[0]); + *te = data[2]; + } else { + *r0 = AW_DSP_RE_TO_SHOW_RE(data[4]); + *te = data[6]; + } + aw_dev_dbg(aw_dev->dev, "read Re %d , Te %d", *r0, *te); + return 0; +} + +int aw_dsp_read_spin(int *spin_mode) +{ + int ret; + int32_t spin = 0; + + ret = aw_read_data_from_dsp(AW_MSG_ID_SPIN, &spin, sizeof(int32_t)); + if (ret) { + *spin_mode = 0; + aw_pr_err("read spin failed "); + return ret; + } + *spin_mode = spin; + aw_pr_dbg("read spin done"); + return 0; +} + +int aw_dsp_write_spin(int spin_mode) +{ + int ret; + int32_t spin = spin_mode; + + if (spin >= AW_SPIN_MAX) { + aw_pr_err("spin [%d] unsupported ", spin); + return -EINVAL; + } + + ret = aw_write_data_to_dsp(AW_MSG_ID_SPIN, &spin, sizeof(int32_t)); + if (ret) { + aw_pr_err("write spin failed "); + return ret; + } + aw_pr_dbg("write spin done"); + return 0; +} + +int aw_dsp_read_r0(struct aw_device *aw_dev, int32_t *r0) +{ + uint32_t msg_id; + int ret; + int msg_num; + int32_t data[6]; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_REAL_DATA_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_REAL_DATA_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_read_msg_from_dsp(msg_num, msg_id, (char *)data, sizeof(int32_t) * 6); + if (ret) { + aw_dev_err(aw_dev->dev, "read real re failed "); + return ret; + } + + *r0 = AW_DSP_RE_TO_SHOW_RE(data[0]); + aw_dev_dbg(aw_dev->dev, "read r0 %d\n", *r0); + return 0; +} + +int aw_dsp_read_cali_data(struct aw_device *aw_dev, char *data, unsigned int data_len) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_REAL_DATA_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_REAL_DATA_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_read_msg_from_dsp(msg_num, msg_id, data, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, "read cali dara failed "); + return ret; + } + aw_dev_dbg(aw_dev->dev, "read cali_data"); + return 0; +} + +int aw_dsp_get_dc_status(struct aw_device *aw_dev) +{ + int ret; + int msg_num; + int32_t data[2]; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_DIRECT_CUR_FLAG, (char *)data, sizeof(int32_t) * 2); + if (ret) { + aw_dev_err(aw_dev->dev, "read dc flag failed"); + return ret; + } + + if ((aw_dev->channel % 2) == 0) + ret = data[0]; + else + ret = data[1]; + + aw_dev_dbg(aw_dev->dev, "read direct current status:%d", ret); + return ret; +} + +int aw_dsp_read_f0_q(struct aw_device *aw_dev, int32_t *f0, int32_t *q) +{ + int ret; + int msg_num; + int32_t data[4]; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_F0_Q, (char *)data, sizeof(int32_t) * 4); + if (ret) { + aw_dev_err(aw_dev->dev, "read f0 & q failed"); + return ret; + } + + if ((aw_dev->channel % 2) == 0) { + *f0 = data[0]; + *q = data[1]; + } else { + *f0 = data[2]; + *q = data[3]; + } + aw_dev_dbg(aw_dev->dev, "read f0 is %d, q is %d", *f0, *q); + return ret; +} + +int aw_dsp_read_f0(struct aw_device *aw_dev, int32_t *f0) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_F0_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_F0_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_read_msg_from_dsp(msg_num, msg_id, (char *)f0, sizeof(int32_t)); + if (ret) { + aw_dev_err(aw_dev->dev, "read f0 failed"); + return ret; + } + aw_dev_dbg(aw_dev->dev, "read f0"); + return 0; +} + +int aw_dsp_cali_en(struct aw_device *aw_dev, bool is_enable) +{ + int ret; + int msg_num; + int32_t enable = is_enable; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + ret = aw_write_msg_to_dsp(msg_num, AW_MSG_ID_ENABLE_CALI, (char *)&enable, sizeof(int32_t)); + if (ret) { + aw_dev_err(aw_dev->dev, "write cali en failed"); + return ret; + } + aw_dev_dbg(aw_dev->dev, "write cali_en[%d]", is_enable); + return 0; +} + +int aw_dsp_hmute_en(struct aw_device *aw_dev, bool is_hmute) +{ + int32_t hmute = is_hmute; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_write_msg_to_dsp(msg_num, AW_MSG_ID_ENABLE_HMUTE, (char *)&hmute, sizeof(int32_t)); + if (ret) { + aw_dev_err(aw_dev->dev, "write hmue failed "); + return ret; + } + aw_dev_dbg(aw_dev->dev, "write hmute[%d]", is_hmute); + return 0; +} + +int aw_dsp_read_cali_re(struct aw_device *aw_dev, int32_t *cali_re) +{ + uint32_t msg_id; + int ret; + int msg_num; + int32_t read_re = 0; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_RE_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_RE_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_read_msg_from_dsp(msg_num, msg_id, (char *)&read_re, sizeof(int32_t)); + if (ret) { + aw_dev_err(aw_dev->dev, "read cali re failed "); + return ret; + } + *cali_re = AW_DSP_RE_TO_SHOW_RE(read_re); + aw_dev_dbg(aw_dev->dev, "read cali re done"); + return 0; +} + +int aw_dsp_write_cali_re(struct aw_device *aw_dev, int32_t cali_re) +{ + uint32_t msg_id; + int ret; + int msg_num; + int32_t local_re = AW_SHOW_RE_TO_DSP_RE(cali_re); + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_RE_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_RE_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_write_msg_to_dsp(msg_num, msg_id, (char *)&local_re, sizeof(int32_t)); + if (ret) { + aw_dev_err(aw_dev->dev, "write cali re failed "); + return ret; + } + aw_dev_dbg(aw_dev->dev, "write cali re done"); + return 0; +} + +int aw_dsp_write_params(struct aw_device *aw_dev, char *data, unsigned int data_len) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + if (msg_num == MSG_PARAM_ID_0) + msg_id = AW_MSG_ID_PARAMS; + else + msg_id = AW_MSG_ID_PARAMS_1; + + ret = aw_write_data_to_dsp(msg_id, data, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, "write params failed"); + return ret; + } + aw_dev_dbg(aw_dev->dev, "write params done"); + return 0; +} + +int aw_dsp_read_vmax(struct aw_device *aw_dev, char *data, unsigned int data_len) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_VMAX_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_VMAX_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_read_msg_from_dsp(msg_num, msg_id, data, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, "read vmax failed"); + return ret; + } + aw_dev_dbg(aw_dev->dev, "read vmax done"); + return 0; +} + +int aw_dsp_write_vmax(struct aw_device *aw_dev, char *data, unsigned int data_len) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_VMAX_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_VMAX_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_write_msg_to_dsp(msg_num, msg_id, data, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, "write vmax failed "); + return ret; + } + aw_dev_dbg(aw_dev->dev, "write vmax done"); + return 0; +} + +int aw_dsp_noise_en(struct aw_device *aw_dev, bool is_noise) +{ + int32_t noise = is_noise; + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_NOISE_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_NOISE_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_write_msg_to_dsp(msg_num, msg_id, (char *)&noise, sizeof(int32_t)); + if (ret) { + aw_dev_err(aw_dev->dev, "write noise failed "); + return ret; + } + aw_dev_dbg(aw_dev->dev, "write noise[%d] done", noise); + return 0; +} + +int aw_dsp_read_cali_cfg(struct aw_device *aw_dev, char *data, unsigned int data_len) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_CALI_CFG_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_CALI_CFG_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_read_msg_from_dsp(msg_num, msg_id, data, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, "read cali_cfg failed "); + return ret; + } + aw_dev_dbg(aw_dev->dev, "read cali_cfg done"); + return 0; +} + +int aw_dsp_write_cali_cfg(struct aw_device *aw_dev, char *data, unsigned int data_len) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_CALI_CFG_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_CALI_CFG_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_write_msg_to_dsp(msg_num, msg_id, data, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, "write cali_cfg failed "); + return ret; + } + + aw_dev_dbg(aw_dev->dev, "write cali_cfg done"); + return 0; +} + +int aw_dsp_read_msg(struct aw_device *aw_dev, + uint32_t msg_id, char *data_ptr, unsigned int data_size) +{ + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + return aw_read_msg_from_dsp(msg_num, msg_id, data_ptr, data_size); +} + +int aw_dsp_write_msg(struct aw_device *aw_dev, + uint32_t msg_id, char *data_ptr, unsigned int data_size) +{ + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + return aw_write_msg_to_dsp(msg_num, msg_id, data_ptr, data_size); +} + +int aw_dsp_set_copp_module_en(bool enable) +{ + int ret; + + ret = aw_adm_param_enable(g_rx_port_id, AW_COPP_MODULE_ID, + AW_COPP_PARAMS_ID_AWDSP_ENABLE, enable); + if (ret) + return -EINVAL; + + aw_pr_info("set skt %s", enable == 1 ? "enable" : "disable"); + return 0; +} + +int aw_get_algo_version(struct aw_device *aw_dev, char *algo_ver_buf) +{ + int ret; + unsigned int algo_ver = 0; + char *algo_data = NULL; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_VERSION, + (char *)&algo_ver, sizeof(uint32_t)); + if ((ret < 0) || (algo_ver == 0)) { + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_VERSION_NEW, + algo_ver_buf, ALGO_VERSION_MAX); + if (ret < 0) + return ret; + } else { + algo_data = (char *)&algo_ver; + snprintf(algo_ver_buf, ALGO_VERSION_MAX, "aw_algo_v%d.%d.%d.%d", + algo_data[3], algo_data[2], algo_data[1], algo_data[0]); + } + + aw_dev_dbg(aw_dev->dev, "%s", algo_ver_buf); + return 0; +} + +void aw_device_parse_topo_id_dt(struct aw_device *aw_dev) +{ + int ret; + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-tx-topo-id", &g_tx_topo_id); + if (ret < 0) { + g_tx_topo_id = AW_TX_DEFAULT_TOPO_ID; + aw_dev_info(aw_dev->dev, "read aw-tx-topo-id failed,use default"); + } + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-rx-topo-id", &g_rx_topo_id); + if (ret < 0) { + g_rx_topo_id = AW_RX_DEFAULT_TOPO_ID; + aw_dev_info(aw_dev->dev, "read aw-rx-topo-id failed,use default"); + } + + aw_dev_info(aw_dev->dev, "tx-topo-id: 0x%x, rx-topo-id: 0x%x", + g_tx_topo_id, g_rx_topo_id); +} + +void aw_device_parse_port_id_dt(struct aw_device *aw_dev) +{ + int ret; + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-tx-port-id", &g_tx_port_id); + if (ret < 0) { + g_tx_port_id = AW_TX_DEFAULT_PORT_ID; + aw_dev_info(aw_dev->dev, "read aw-tx-port-id failed,use default"); + } + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-rx-port-id", &g_rx_port_id); + if (ret < 0) { + g_rx_port_id = AW_RX_DEFAULT_PORT_ID; + aw_dev_info(aw_dev->dev, "read aw-rx-port-id failed,use default"); + } + + aw_dev_info(aw_dev->dev, "tx-port-id: 0x%x, rx-port-id: 0x%x", + g_tx_port_id, g_rx_port_id); + +} + diff --git a/asoc/codecs/aw88261/aw_dsp.h b/asoc/codecs/aw88261/aw_dsp.h new file mode 100644 index 000000000000..aaf0089a0d92 --- /dev/null +++ b/asoc/codecs/aw88261/aw_dsp.h @@ -0,0 +1,66 @@ +#ifndef __AWINIC_DSP_H__ +#define __AWINIC_DSP_H__ + +#define AW_QCOM_PLATFORM + +/*factor form 12bit(4096) to 1000*/ +#define AW_DSP_RE_TO_SHOW_RE(re) (((re) * (1000)) >> (12)) +#define AW_SHOW_RE_TO_DSP_RE(re) (((re) << 12) / (1000)) + +#define AW_DSP_TRY_TIME (3) +#define AW_DSP_SLEEP_TIME (10) + +#define AW_TX_DEFAULT_TOPO_ID (0x1000FF00) +#define AW_RX_DEFAULT_TOPO_ID (0x1000FF01) +#define AW_TX_DEFAULT_PORT_ID (0x1007) +#define AW_RX_DEFAULT_PORT_ID (0x1006) + +enum aw_dsp_msg_type { + AW_DSP_MSG_TYPE_DATA = 0, + AW_DSP_MSG_TYPE_CMD = 1, +}; + +enum { + AW_SPIN_0 = 0, + AW_SPIN_90, + AW_SPIN_180, + AW_SPIN_270, + AW_SPIN_MAX, +}; + +#define AW_DSP_MSG_HDR_VER (1) +typedef struct aw_msg_hdr aw_dsp_msg_t; + +int aw_dsp_write_msg(struct aw_device *aw_dev, uint32_t msg_id, char *data_ptr, unsigned int data_size); +int aw_dsp_read_msg(struct aw_device *aw_dev, uint32_t msg_id, char *data_ptr, unsigned int data_size); +int aw_dsp_write_cali_cfg(struct aw_device *aw_dev, char *data, unsigned int data_len); +int aw_dsp_read_cali_cfg(struct aw_device *aw_dev, char *data, unsigned int data_len); +int aw_dsp_noise_en(struct aw_device *aw_dev, bool is_noise); +int aw_dsp_write_vmax(struct aw_device *aw_dev, char *data, unsigned int data_len); +int aw_dsp_read_vmax(struct aw_device *aw_dev, char *data, unsigned int data_len); +int aw_dsp_write_params(struct aw_device *aw_dev, char *data, unsigned int data_len); +int aw_dsp_write_cali_re(struct aw_device *aw_dev, int32_t cali_re); +int aw_dsp_read_cali_re(struct aw_device *aw_dev, int32_t *cali_re); +int aw_dsp_read_r0(struct aw_device *aw_dev, int32_t *r0); +int aw_dsp_read_st(struct aw_device *aw_dev, int32_t *r0, int32_t *te); +int aw_dsp_read_te(struct aw_device *aw_dev, int32_t *te); +int aw_dsp_get_dc_status(struct aw_device *aw_dev); +int aw_dsp_hmute_en(struct aw_device *aw_dev, bool is_hmute); +int aw_dsp_cali_en(struct aw_device *aw_dev, bool is_enable); +int aw_dsp_read_f0(struct aw_device *aw_dev, int32_t *f0); +int aw_dsp_read_f0_q(struct aw_device *aw_dev, int32_t *f0, int32_t *q); +int aw_dsp_read_cali_data(struct aw_device *aw_dev, char *data, unsigned int data_len); +int aw_dsp_set_afe_module_en(int type, int enable); +int aw_dsp_get_afe_module_en(int type, int *status); +int aw_dsp_set_copp_module_en(bool enable); +int aw_dsp_write_spin(int spin_mode); +int aw_dsp_read_spin(int *spin_mode); +int aw_get_algo_version(struct aw_device *aw_dev, char *algo_ver_buf); +int aw_dsp_set_algo_prof(struct aw_device *aw_dev, int prof_id); +int aw_dsp_get_algo_prof(struct aw_device *aw_dev, int *prof_id); +int aw_dsp_set_algo_params_path(struct aw_device *aw_dev); +void aw_device_parse_topo_id_dt(struct aw_device *aw_dev); +void aw_device_parse_port_id_dt(struct aw_device *aw_dev); + +#endif + diff --git a/asoc/codecs/aw88261/aw_init.c b/asoc/codecs/aw88261/aw_init.c new file mode 100644 index 000000000000..8acd57c63f76 --- /dev/null +++ b/asoc/codecs/aw88261/aw_init.c @@ -0,0 +1,1653 @@ +/* + * aw_init.c aw882xx codec module + * + * + * Copyright (c) 2019 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* #define DEBUG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aw882xx.h" +#include "aw_pid_1852_reg.h" +#include "aw_pid_2013_reg.h" +#include "aw_pid_2032_reg.h" +#include "aw_pid_2055_reg.h" +#include "aw_pid_2071_reg.h" +#include "aw_pid_2113_reg.h" +#include "aw_log.h" + +int aw882xx_dev_i2c_write_bits(struct aw_device *aw_dev, + unsigned char reg_addr, unsigned int mask, unsigned int reg_data) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + return aw882xx_i2c_write_bits(aw882xx, reg_addr, mask, reg_data); +} + +int aw882xx_dev_i2c_write(struct aw_device *aw_dev, + unsigned char reg_addr, unsigned int reg_data) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + return aw882xx_i2c_write(aw882xx, reg_addr, reg_data); +} + +int aw882xx_dev_i2c_read(struct aw_device *aw_dev, + unsigned char reg_addr, unsigned int *reg_data) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + return aw882xx_i2c_read(aw882xx, reg_addr, reg_data); +} + +void aw882xx_dev_set_algo_en(struct aw_device *aw_dev) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + aw882xx_kcontorl_set(aw882xx); +} + +/* [7 : 4]: -6DB ; [3 : 0]: 0.5DB real_value = value * 2 : 0.5db --> 1 */ +static unsigned int aw_pid_1852_reg_val_to_db(unsigned int value) +{ + return ((value >> 4) * AW_PID_1852_VOL_STEP_DB + (value & 0x0f)); +} + +/* [7 : 4]: -6DB ; [3 : 0]: -0.5DB reg_value = value / step << 4 + value % step ; step = 6 * 2 */ +static unsigned int aw_pid_1852_db_val_to_reg(unsigned int value) +{ + return (((value / AW_PID_1852_VOL_STEP_DB) << 4) + (value % AW_PID_1852_VOL_STEP_DB)); +} + +static int aw_pid_1852_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + unsigned int reg_value = 0; + unsigned int real_value = aw_pid_1852_db_val_to_reg(value); + + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW_PID_1852_HAGCCFG4_REG, ®_value); + + aw_dev_dbg(aw882xx->dev, "value %d , 0x%x", value, real_value); + + /* 15 : 8] volume */ + real_value = (real_value << 8) | (reg_value & 0x00ff); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW_PID_1852_HAGCCFG4_REG, real_value); + return 0; +} + +static int aw_pid_1852_get_volume(struct aw_device *aw_dev, unsigned int *value) +{ + unsigned int reg_value = 0; + unsigned int real_value = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + /* read value */ + aw882xx_i2c_read(aw882xx, AW_PID_1852_HAGCCFG4_REG, ®_value); + + /* [15 : 8] volume */ + real_value = reg_value >> 8; + + real_value = aw_pid_1852_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static void aw_pid_1852_i2s_enable(struct aw_device *aw_dev, bool flag) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + struct aw_cali_desc *desc = &aw_dev->cali_desc; + + aw_dev_dbg(aw882xx->dev, "enter"); + + if (!desc->mode) { + aw_dev_dbg(aw_dev->dev, "cali mode is none, needn't set i2s status"); + return; + } + + if (flag) { + aw882xx_i2c_write_bits(aw882xx, AW_PID_1852_I2SCFG1_REG, + AW_PID_1852_I2STXEN_MASK, + AW_PID_1852_I2STXEN_ENABLE_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW_PID_1852_I2SCFG1_REG, + AW_PID_1852_I2STXEN_MASK, + AW_PID_1852_I2STXEN_DISABLE_VALUE); + } +} + +static bool aw_pid_1852_check_rd_access(int reg) +{ + if (reg >= AW_PID_1852_REG_MAX) + return false; + + + if (aw_pid_1852_reg_access[reg] & AW_PID_1852_REG_RD_ACCESS) + return true; + else + return false; +} + +static bool aw_pid_1852_check_wr_access(int reg) +{ + if (reg >= AW_PID_1852_REG_MAX) + return false; + + + if (aw_pid_1852_reg_access[reg] & AW_PID_1852_REG_WR_ACCESS) + return true; + else + return false; +} + +static int aw_pid_1852_get_reg_num(void) +{ + return AW_PID_1852_REG_MAX; +} + +static unsigned int aw_pid_1852_get_irq_type(struct aw_device *aw_dev, + unsigned int value) +{ + unsigned int ret = INT_TYPE_NONE; + /* UVL0 */ + if (value & (~AW_PID_1852_UVLI_MASK)) { + aw_dev_info(aw_dev->dev, "UVLO: occur"); + ret |= INT_TYPE_UVLO; + } + + /* BSTOCM */ + if (value & (~AW_PID_1852_BSTOCI_MASK)) { + aw_dev_info(aw_dev->dev, "BSTOCI: occur"); + ret |= INT_TYPE_BSTOC; + } + + /* OCDI */ + if (value & (~AW_PID_1852_OCDI_MASK)) { + aw_dev_info(aw_dev->dev, "OCDI: occur"); + ret |= INT_TYPE_OCDI; + } + + /* OTHI */ + if (value & (~AW_PID_1852_OTHI_MASK)) { + aw_dev_info(aw_dev->dev, "OTHI: occur"); + ret |= INT_TYPE_OTHI; + } + + return ret; +} + +static int aw_pid_1852_dev_init(struct aw882xx *aw882xx, int index) +{ + int ret; + struct aw_device *aw_pa = NULL; + + aw_pa = devm_kzalloc(aw882xx->dev, sizeof(struct aw_device), GFP_KERNEL); + if (aw_pa == NULL) { + aw_dev_err(aw882xx->dev, "dev kalloc failed"); + return -ENOMEM; + } + + /* call aw device init func */ + memset(aw_pa->acf_name, 0, AW_NAME_MAX); + memcpy(aw_pa->acf_name, AW_PID_1852_ACF_FILE, strlen(AW_PID_1852_ACF_FILE)); + memset(aw_pa->monitor_name, 0, AW_NAME_MAX); + memcpy(aw_pa->monitor_name, AW_PID_1852_MONITOR_FILE, strlen(AW_PID_1852_MONITOR_FILE)); + + aw_pa->prof_info.prof_desc = NULL; + aw_pa->prof_info.count = 0; + aw_pa->channel = 0; + aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_pa->bop_en = AW_BOP_DISABLE; + aw_pa->vol_step = AW_PID_1852_VOL_STEP; + + aw_pa->index = index; + aw_pa->private_data = (void *)aw882xx; + aw_pa->dev = aw882xx->dev; + aw_pa->i2c = aw882xx->i2c; + aw_pa->ops.aw_get_version = aw882xx_get_version; + aw_pa->ops.aw_get_dev_num = aw882xx_get_dev_num; + aw_pa->ops.aw_set_algo = aw882xx_dev_set_algo_en; + aw_pa->ops.aw_i2c_read = aw882xx_dev_i2c_read; + aw_pa->ops.aw_i2c_write = aw882xx_dev_i2c_write; + aw_pa->ops.aw_i2c_write_bits = aw882xx_dev_i2c_write_bits; + aw_pa->ops.aw_get_volume = aw_pid_1852_get_volume; + aw_pa->ops.aw_set_volume = aw_pid_1852_set_volume; + aw_pa->ops.aw_reg_val_to_db = aw_pid_1852_reg_val_to_db; + aw_pa->ops.aw_i2s_enable = aw_pid_1852_i2s_enable; + aw_pa->ops.aw_check_rd_access = aw_pid_1852_check_rd_access; + aw_pa->ops.aw_check_wr_access = aw_pid_1852_check_wr_access; + aw_pa->ops.aw_get_reg_num = aw_pid_1852_get_reg_num; + aw_pa->ops.aw_get_irq_type = aw_pid_1852_get_irq_type; + + aw_pa->int_desc.mask_reg = AW_PID_1852_SYSINTM_REG; + aw_pa->int_desc.mask_default = AW_PID_1852_SYSINTM_DEFAULT; + aw_pa->int_desc.int_mask = AW_PID_1852_SYSINTM_DEFAULT; + aw_pa->int_desc.st_reg = AW_PID_1852_SYSINT_REG; + + aw_pa->pwd_desc.reg = AW_PID_1852_SYSCTRL_REG; + aw_pa->pwd_desc.mask = AW_PID_1852_PWDN_MASK; + aw_pa->pwd_desc.enable = AW_PID_1852_PWDN_POWER_DOWN_VALUE; + aw_pa->pwd_desc.disable = AW_PID_1852_PWDN_NORMAL_WORKING_VALUE; + + aw_pa->amppd_desc.reg = AW_PID_1852_SYSCTRL_REG; + aw_pa->amppd_desc.mask = AW_PID_1852_AMPPD_MASK; + aw_pa->amppd_desc.enable = AW_PID_1852_AMPPD_POWER_DOWN_VALUE; + aw_pa->amppd_desc.disable = AW_PID_1852_AMPPD_NORMAL_WORKING_VALUE; + + aw_pa->mute_desc.reg = AW_PID_1852_SYSCTRL2_REG; + aw_pa->mute_desc.mask = AW_PID_1852_HMUTE_MASK; + aw_pa->mute_desc.enable = AW_PID_1852_HMUTE_ENABLE_VALUE; + aw_pa->mute_desc.disable = AW_PID_1852_HMUTE_DISABLE_VALUE; + + aw_pa->vcalb_desc.vcalb_reg = AW_PID_1852_VTMCTRL3_REG; + aw_pa->vcalb_desc.vcal_factor = AW_PID_1852_VCAL_FACTOR; + aw_pa->vcalb_desc.cabl_base_value = AW_PID_1852_CABL_BASE_VALUE; + + aw_pa->vcalb_desc.icalk_value_factor = AW_PID_1852_ICABLK_FACTOR; + aw_pa->vcalb_desc.icalk_reg = AW_PID_1852_EFRM1_REG; + aw_pa->vcalb_desc.icalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.icalk_reg_mask = AW_PID_1852_EF_ISN_GESLP_MASK; + aw_pa->vcalb_desc.icalk_sign_mask = AW_PID_1852_EF_ISN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.icalk_neg_mask = AW_PID_1852_EF_ISN_GESLP_NEG; + + aw_pa->vcalb_desc.vcalk_reg = AW_PID_1852_EFRH_REG; + aw_pa->vcalb_desc.vcalk_reg_mask = AW_PID_1852_EF_VSN_GESLP_MASK; + aw_pa->vcalb_desc.vcalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.vcalk_sign_mask = AW_PID_1852_EF_VSN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.vcalk_neg_mask = AW_PID_1852_EF_VSN_GESLP_NEG; + aw_pa->vcalb_desc.vcalk_value_factor = AW_PID_1852_VCABLK_FACTOR; + + aw_pa->sysst_desc.reg = AW_PID_1852_SYSST_REG; + aw_pa->sysst_desc.mask = AW_PID_1852_SYSST_CHECK_MASK; + aw_pa->sysst_desc.st_check = AW_PID_1852_SYSST_CHECK; + aw_pa->sysst_desc.pll_check = AW_PID_1852_PLLS_LOCKED_VALUE; + + aw_pa->cco_mux_desc.reg = AW_PID_1852_PLLCTRL1_REG; + aw_pa->cco_mux_desc.mask = AW_PID_1852_I2S_CCO_MUX_MASK; + aw_pa->cco_mux_desc.divided_val = AW_PID_1852_I2S_CCO_MUX_8_16_32KHZ_VALUE; + aw_pa->cco_mux_desc.bypass_val = AW_PID_1852_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + + aw_pa->voltage_desc.reg = AW_PID_1852_VBAT_REG; + aw_pa->voltage_desc.int_bit = AW_PID_1852_MONITOR_INT_10BIT; + aw_pa->voltage_desc.vbat_range = AW_PID_1852_MONITOR_VBAT_RANGE; + + aw_pa->temp_desc.reg = AW_PID_1852_TEMP_REG; + aw_pa->temp_desc.neg_mask = AW_PID_1852_MONITOR_TEMP_NEG_MASK; + aw_pa->temp_desc.sign_mask = AW_PID_1852_MONITOR_TEMP_SIGN_MASK; + + aw_pa->ipeak_desc.reg = AW_PID_1852_SYSCTRL2_REG; + aw_pa->ipeak_desc.mask = AW_PID_1852_BST_IPEAK_MASK; + + aw_pa->volume_desc.reg = AW_PID_1852_HAGCCFG4_REG; + aw_pa->volume_desc.mask = AW_PID_1852_VOL_MASK; + aw_pa->volume_desc.shift = AW_PID_1852_VOL_START_BIT; + aw_pa->volume_desc.mute_volume = AW_PID_1852_MUTE_VOL; + + aw_pa->bop_desc.reg = AW_REG_NONE; + + aw_pa->soft_rst.reg = AW_PID_1852_ID_REG; + aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + + ret = aw_device_probe(aw_pa); + + aw882xx->aw_pa = aw_pa; + return ret; +} + +/****************************************************** + * + * A2013 init + * + ******************************************************/ +/*[9 : 6]: -6DB ; [5 : 0]: 0.125DB real_value = value * 8 : 0.125db --> 1*/ +static unsigned int aw_pid_2013_reg_val_to_db(unsigned int value) +{ + return ((value >> 6) * AW_PID_2013_VOL_STEP_DB + (value & 0x003f)); +} + +/*[9 : 6]: -6DB ; [5 : 0]: -0.125DB reg_value = value / step << 6 + value % step ; step = 6 * 8*/ +static unsigned int aw_pid_2013_db_val_to_reg(unsigned int value) +{ + return (((value / AW_PID_2013_VOL_STEP_DB) << 6) + (value % AW_PID_2013_VOL_STEP_DB)); +} + +static int aw_pid_2013_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + unsigned int reg_value = 0; + unsigned int real_value = aw_pid_2013_db_val_to_reg(value); + + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW_PID_2013_SYSCTRL2_REG, ®_value); + + aw_dev_dbg(aw882xx->dev, "value %d , 0x%x", value, real_value); + + /*9 : 0] volume*/ + real_value = real_value | (reg_value & 0xfc00); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW_PID_2013_SYSCTRL2_REG, real_value); + return 0; +} + +static int aw_pid_2013_get_volume(struct aw_device *aw_dev, unsigned int* value) +{ + unsigned int reg_value = 0; + unsigned int real_value = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + /* read value */ + aw882xx_i2c_read(aw882xx, AW_PID_2013_SYSCTRL2_REG, ®_value); + + /*[9 : 0] volume*/ + real_value = reg_value & 0x03ff; + + real_value = aw_pid_2013_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static void aw_pid_2013_i2s_enable(struct aw_device *aw_dev, bool flag) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + aw_dev_dbg(aw882xx->dev, "enter"); + + if (flag) + aw882xx_i2c_write_bits(aw882xx, AW_PID_2013_I2SCTRL1_REG, + AW_PID_2013_I2STXEN_MASK, + AW_PID_2013_I2STXEN_ENABLE_VALUE); + else + aw882xx_i2c_write_bits(aw882xx, AW_PID_2013_I2SCFG1_REG, + AW_PID_2013_I2STXEN_MASK, + AW_PID_2013_I2STXEN_DISABLE_VALUE); +} + +static bool aw_pid_2013_check_rd_access(int reg) +{ + if (reg >= AW_PID_2013_REG_MAX) + return false; + + if (aw_pid_2013_reg_access[reg] & AW_PID_2013_REG_RD_ACCESS) + return true; + else + return false; +} + +static bool aw_pid_2013_check_wr_access(int reg) +{ + if (reg >= AW_PID_2013_REG_MAX) + return false; + + if (aw_pid_2013_reg_access[reg] & AW_PID_2013_REG_WR_ACCESS) + return true; + else + return false; +} + +static int aw_pid_2013_get_reg_num(void) +{ + return AW_PID_2013_REG_MAX; +} + +static unsigned int aw_pid_2013_get_irq_type(struct aw_device *aw_dev, + unsigned int value) +{ + unsigned int ret = INT_TYPE_NONE; + + /*UVL0*/ + if (value & (~AW_PID_2013_UVLI_MASK)) { + aw_dev_info(aw_dev->dev, "UVLO: occur"); + ret |= INT_TYPE_UVLO; + } + + /*BSTOCM*/ + if (value & (~AW_PID_2013_BSTOCI_MASK)) { + aw_dev_info(aw_dev->dev, "BSTOCI: occur"); + ret |= INT_TYPE_BSTOC; + } + + /*OCDI*/ + if (value & (~AW_PID_2013_OCDI_MASK)) { + aw_dev_info(aw_dev->dev, "OCDI: occur"); + ret |= INT_TYPE_OCDI; + } + + /*OTHI*/ + if (value & (~AW_PID_2013_OTHI_MASK)) { + aw_dev_info(aw_dev->dev, "OTHI: occur"); + ret |= INT_TYPE_OTHI; + } + + return ret; +} + +static void aw_pid_2013_efver_check(struct aw_device *aw_dev) +{ + unsigned int reg_val; + unsigned int efverh; + unsigned int efverl; + + aw_dev->ops.aw_i2c_read(aw_dev, + AW_PID_2013_EFRM1_REG, ®_val); + + efverh = (((reg_val & (~AW_PID_2013_EFVERH_MASK)) >> + AW_PID_2013_EFVERH_START_BIT) ^ + AW_PID_2013_EFVER_CHECK); + efverl = (((reg_val & (~AW_PID_2013_EFVERL_MASK)) >> + AW_PID_2013_EFVERL_START_BIT) ^ + AW_PID_2013_EFVER_CHECK); + + aw_dev_dbg(aw_dev->dev, "efverh: 0x%0x, efverl: 0x%0x", efverh, efverl); + + if (efverh && efverl) { + aw_dev->bstcfg_enable = AW_BSTCFG_ENABLE; + aw_dev_info(aw_dev->dev, "A2013 EFVER A"); + } else if (efverh && !efverl) { + aw_dev->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_dev_info(aw_dev->dev, "A2013 EFVER B"); + } else { + aw_dev->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_dev_info(aw_dev->dev, "unsupport A2013 EFVER"); + } + aw_dev_info(aw_dev->dev, "bstcfg enable: %d", aw_dev->bstcfg_enable); +} + +static int aw_pid_2013_dev_init(struct aw882xx *aw882xx, int index) +{ + int ret; + struct aw_device *aw_pa = NULL; + aw_pa = devm_kzalloc(aw882xx->dev, sizeof(struct aw_device), GFP_KERNEL); + if (aw_pa == NULL) { + aw_dev_err(aw882xx->dev, "dev kalloc failed"); + return -ENOMEM; + } + + /*call aw device init func*/ + memset(aw_pa->acf_name, 0, AW_NAME_MAX); + memcpy(aw_pa->acf_name, AW_PID_2013_ACF_FILE, strlen(AW_PID_2013_ACF_FILE)); + memset(aw_pa->monitor_name, 0, AW_NAME_MAX); + memcpy(aw_pa->monitor_name, AW_PID_2013_MONITOR_FILE, strlen(AW_PID_2013_MONITOR_FILE)); + + aw_pa->prof_info.prof_desc = NULL; + aw_pa->prof_info.count = 0; + aw_pa->channel = 0; + aw_pa->bop_en = AW_BOP_DISABLE; + aw_pa->vol_step = AW_PID_2013_VOL_STEP; + + aw_pa->index = index; + aw_pa->private_data = (void *)aw882xx; + aw_pa->dev = aw882xx->dev; + aw_pa->i2c = aw882xx->i2c; + aw_pa->ops.aw_get_version = aw882xx_get_version; + aw_pa->ops.aw_get_dev_num = aw882xx_get_dev_num; + aw_pa->ops.aw_set_algo = aw882xx_dev_set_algo_en; + aw_pa->ops.aw_i2c_read = aw882xx_dev_i2c_read; + aw_pa->ops.aw_i2c_write = aw882xx_dev_i2c_write; + aw_pa->ops.aw_i2c_write_bits = aw882xx_dev_i2c_write_bits; + aw_pa->ops.aw_get_volume = aw_pid_2013_get_volume; + aw_pa->ops.aw_set_volume = aw_pid_2013_set_volume; + aw_pa->ops.aw_reg_val_to_db = aw_pid_2013_reg_val_to_db; + aw_pa->ops.aw_i2s_enable = aw_pid_2013_i2s_enable; + aw_pa->ops.aw_check_rd_access = aw_pid_2013_check_rd_access; + aw_pa->ops.aw_check_wr_access = aw_pid_2013_check_wr_access; + aw_pa->ops.aw_get_reg_num = aw_pid_2013_get_reg_num; + aw_pa->ops.aw_get_irq_type =aw_pid_2013_get_irq_type; + + aw_pa->int_desc.mask_reg = AW_PID_2013_SYSINTM_REG; + aw_pa->int_desc.mask_default = AW_PID_2013_SYSINTM_DEFAULT; + aw_pa->int_desc.int_mask = AW_PID_2013_SYSINTM_DEFAULT; + aw_pa->int_desc.st_reg = AW_PID_2013_SYSINT_REG; + + aw_pa->pwd_desc.reg = AW_PID_2013_SYSCTRL_REG; + aw_pa->pwd_desc.mask = AW_PID_2013_PWDN_MASK; + aw_pa->pwd_desc.enable = AW_PID_2013_PWDN_POWER_DOWN_VALUE; + aw_pa->pwd_desc.disable = AW_PID_2013_PWDN_WORKING_VALUE; + + aw_pa->amppd_desc.reg = AW_PID_2013_SYSCTRL_REG; + aw_pa->amppd_desc.mask = AW_PID_2013_AMPPD_MASK; + aw_pa->amppd_desc.enable = AW_PID_2013_AMPPD_POWER_DOWN_VALUE; + aw_pa->amppd_desc.disable = AW_PID_2013_AMPPD_WORKING_VALUE; + + aw_pa->mute_desc.reg = AW_PID_2013_SYSCTRL_REG; + aw_pa->mute_desc.mask = AW_PID_2013_HMUTE_MASK; + aw_pa->mute_desc.enable = AW_PID_2013_HMUTE_ENABLE_VALUE; + aw_pa->mute_desc.disable = AW_PID_2013_HMUTE_DISABLE_VALUE; + + aw_pa->vcalb_desc.vcalb_reg = AW_PID_2013_VSNTM1_REG; + aw_pa->vcalb_desc.vcal_factor = AW_PID_2013_VCAL_FACTOR; + aw_pa->vcalb_desc.cabl_base_value = AW_PID_2013_CABL_BASE_VALUE; + + aw_pa->vcalb_desc.icalk_value_factor = AW_PID_2013_ICABLK_FACTOR; + aw_pa->vcalb_desc.icalk_reg = AW_PID_2013_EFRH_REG; + aw_pa->vcalb_desc.icalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.icalk_reg_mask = AW_PID_2013_EF_ISN_GESLP_MASK; + aw_pa->vcalb_desc.icalk_sign_mask = AW_PID_2013_EF_ISN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.icalk_neg_mask = AW_PID_2013_EF_ISN_GESLP_NEG; + + aw_pa->vcalb_desc.vcalk_reg = AW_PID_2013_EFRM2_REG; + aw_pa->vcalb_desc.vcalk_reg_mask = AW_PID_2013_EF_VSN_GESLP_MASK; + aw_pa->vcalb_desc.vcalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.vcalk_sign_mask = AW_PID_2013_EF_ISN_GESLP2_SIGN_MASK; + aw_pa->vcalb_desc.vcalk_neg_mask = AW_PID_2013_EF_ISN_GESLP2_NEG; + aw_pa->vcalb_desc.vcalk_value_factor = AW_PID_2013_VCABLK_FACTOR; + + aw_pa->sysst_desc.reg = AW_PID_2013_SYSST_REG; + aw_pa->sysst_desc.mask = AW_PID_2013_SYSST_CHECK_MASK; + aw_pa->sysst_desc.st_check = AW_PID_2013_SYSST_CHECK; + aw_pa->sysst_desc.pll_check = AW_PID_2013_PLLS_LOCKED_VALUE; + + aw_pa->profctrl_desc.reg = AW_PID_2013_SYSCTRL_REG; + aw_pa->profctrl_desc.mask = AW_PID_2013_EN_TRAN_MASK; + aw_pa->profctrl_desc.spk_mode = AW_PID_2013_EN_TRAN_SPK_VALUE; + + aw_pa->bstctrl_desc.reg = AW_PID_2013_BSTCTRL2_REG; + aw_pa->bstctrl_desc.mask = AW_PID_2013_BST_MODE_MASK; + aw_pa->bstctrl_desc.frc_bst = AW_PID_2013_BST_MODE_FORCE_BOOST_VALUE; + aw_pa->bstctrl_desc.tsp_type = AW_PID_2013_BST_MODE_TRANSPARENT_VALUE; + + aw_pa->voltage_desc.reg = AW_PID_2013_VBAT_REG; + aw_pa->voltage_desc.int_bit = AW_PID_2013_MONITOR_INT_10BIT; + aw_pa->voltage_desc.vbat_range = AW_PID_2013_MONITOR_VBAT_RANGE; + + aw_pa->temp_desc.reg = AW_PID_2013_TEMP_REG; + aw_pa->temp_desc.neg_mask = AW_PID_2013_MONITOR_TEMP_NEG_MASK; + aw_pa->temp_desc.sign_mask = AW_PID_2013_MONITOR_TEMP_SIGN_MASK; + + aw_pa->cco_mux_desc.reg = AW_PID_2013_PLLCTRL3_REG; + aw_pa->cco_mux_desc.mask = AW_PID_2013_CCO_MUX_MASK; + aw_pa->cco_mux_desc.divided_val = AW_PID_2013_CCO_MUX_DIVIDED_VALUE; + aw_pa->cco_mux_desc.bypass_val = AW_PID_2013_CCO_MUX_BYPASS_VALUE; + + aw_pa->ipeak_desc.reg = AW_PID_2013_BSTCTRL2_REG; + aw_pa->ipeak_desc.mask = AW_PID_2013_BST_IPEAK_MASK; + + aw_pa->volume_desc.reg = AW_PID_2013_SYSCTRL2_REG; + aw_pa->volume_desc.mask = AW_PID_2013_VOL_MASK; + aw_pa->volume_desc.shift = AW_PID_2013_VOL_START_BIT; + aw_pa->volume_desc.mute_volume = AW_PID_2013_MUTE_VOL; + + aw_pa->bop_desc.reg = AW_REG_NONE; + + aw_pa->soft_rst.reg = AW_PID_2013_ID_REG; + aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + + aw_pid_2013_efver_check(aw_pa); + ret = aw_device_probe(aw_pa); + + aw882xx->aw_pa = aw_pa; + return ret; +} + +/****************************************************** + * + * A2032 init + * + ******************************************************/ +/*[7 : 4]: -6DB ; [3 : 0]: 0.5DB real_value = value * 2 : 0.5db --> 1*/ +static unsigned int aw_pid_2032_reg_val_to_db(unsigned int value) +{ + return ((value >> 6) * AW_PID_2032_VOL_STEP_DB + (value & 0x3f)); +} + +/* [7 : 4]: -6DB ; [3 : 0]: -0.5DB reg_value = value / step << 4 + value % step ; step = 6 * 2 */ +static unsigned int aw_pid_2032_db_val_to_reg(unsigned int value) +{ + return (((value / AW_PID_2032_VOL_STEP_DB) << 6) + (value % AW_PID_2032_VOL_STEP_DB)); +} + + +static int aw_pid_2032_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + unsigned int reg_value = 0; + unsigned int real_value = aw_pid_2032_db_val_to_reg(value); + + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW_PID_2032_SYSCTRL2_REG, ®_value); + + aw_dev_dbg(aw882xx->dev, "value %d , 0x%x", value, real_value); + + /* [15 : 6] volume */ + real_value = (real_value << 6) | (reg_value & 0x003f); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW_PID_2032_SYSCTRL2_REG, real_value); + return 0; +} + +static int aw_pid_2032_get_volume(struct aw_device *aw_dev, unsigned int *value) +{ + unsigned int reg_value = 0; + unsigned int real_value = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + /* read value */ + aw882xx_i2c_read(aw882xx, AW_PID_2032_SYSCTRL2_REG, ®_value); + + /* [15 : 6] volume */ + real_value = reg_value >> 6; + + real_value = aw_pid_2032_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static void aw_pid_2032_i2s_enable(struct aw_device *aw_dev, bool flag) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + struct aw_cali_desc *desc = &aw_dev->cali_desc; + aw_dev_dbg(aw882xx->dev, "enter"); + + if (!desc->mode) { + aw_dev_dbg(aw_dev->dev, "cali mode is none, needn't set i2s status"); + return; + } + + if (flag) { + aw882xx_i2c_write_bits(aw882xx, AW_PID_2032_I2SCFG1_REG, + AW_PID_2032_I2STXEN_MASK, + AW_PID_2032_I2STXEN_ENABLE_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW_PID_2032_I2SCFG1_REG, + AW_PID_2032_I2STXEN_MASK, + AW_PID_2032_I2STXEN_DISABLE_VALUE); + } +} + +static bool aw_pid_2032_check_rd_access(int reg) +{ + if (reg >= AW_PID_2032_REG_MAX) + return false; + + if (aw_pid_2032_reg_access[reg] & AW_PID_2032_REG_RD_ACCESS) + return true; + else + return false; +} + +static bool aw_pid_2032_check_wr_access(int reg) +{ + if (reg >= AW_PID_2032_REG_MAX) + return false; + + if (aw_pid_2032_reg_access[reg] & + AW_PID_2032_REG_WR_ACCESS) + return true; + else + return false; +} + +static int aw_pid_2032_get_reg_num(void) +{ + return AW_PID_2032_REG_MAX; +} + +static unsigned int aw_pid_2032_get_irq_type(struct aw_device *aw_dev, + unsigned int value) +{ + unsigned int ret = INT_TYPE_NONE; + + /* UVL0 */ + if (value & (~AW_PID_2032_UVLI_MASK)) { + aw_dev_info(aw_dev->dev, "UVLO: occur"); + ret |= INT_TYPE_UVLO; + } + + /* BSTOCM */ + if (value & (~AW_PID_2032_BSTOCI_MASK)) { + aw_dev_info(aw_dev->dev, "BSTOCI: occur"); + ret |= INT_TYPE_BSTOC; + } + + /* OCDI */ + if (value & (~AW_PID_2032_OCDI_MASK)) { + aw_dev_info(aw_dev->dev, "OCDI: occur"); + ret |= INT_TYPE_OCDI; + } + + /* OTHI */ + if (value & (~AW_PID_2032_OTHI_MASK)) { + aw_dev_info(aw_dev->dev, "OTHI: occur"); + ret |= INT_TYPE_OTHI; + } + + return ret; +} + +static int aw_pid_2032_dev_init(struct aw882xx *aw882xx, int index) +{ + int ret; + struct aw_device *aw_pa = NULL; + + aw_pa = devm_kzalloc(aw882xx->dev, sizeof(struct aw_device), GFP_KERNEL); + if (aw_pa == NULL) { + aw_dev_err(aw882xx->dev, "dev kalloc failed"); + return -ENOMEM; + } + + /* call aw device init func */ + memset(aw_pa->acf_name, 0, AW_NAME_MAX); + memcpy(aw_pa->acf_name, AW_PID_2032_ACF_FILE, strlen(AW_PID_2032_ACF_FILE)); + memset(aw_pa->monitor_name, 0, AW_NAME_MAX); + memcpy(aw_pa->monitor_name, AW_PID_2032_MONITOR_FILE, strlen(AW_PID_2032_MONITOR_FILE)); + + aw_pa->prof_info.prof_desc = NULL; + aw_pa->prof_info.count = 0; + aw_pa->channel = 0; + aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_pa->bop_en = AW_BOP_DISABLE; + aw_pa->vol_step = AW_PID_2032_VOL_STEP; + + aw_pa->index = index; + aw_pa->private_data = (void *)aw882xx; + aw_pa->dev = aw882xx->dev; + aw_pa->i2c = aw882xx->i2c; + aw_pa->ops.aw_get_version = aw882xx_get_version; + aw_pa->ops.aw_get_dev_num = aw882xx_get_dev_num; + aw_pa->ops.aw_set_algo = aw882xx_dev_set_algo_en; + aw_pa->ops.aw_i2c_read = aw882xx_dev_i2c_read; + aw_pa->ops.aw_i2c_write = aw882xx_dev_i2c_write; + aw_pa->ops.aw_i2c_write_bits = aw882xx_dev_i2c_write_bits; + aw_pa->ops.aw_get_volume = aw_pid_2032_get_volume; + aw_pa->ops.aw_set_volume = aw_pid_2032_set_volume; + aw_pa->ops.aw_reg_val_to_db = aw_pid_2032_reg_val_to_db; + aw_pa->ops.aw_i2s_enable = aw_pid_2032_i2s_enable; + aw_pa->ops.aw_check_rd_access = aw_pid_2032_check_rd_access; + aw_pa->ops.aw_check_wr_access = aw_pid_2032_check_wr_access; + aw_pa->ops.aw_get_reg_num = aw_pid_2032_get_reg_num; + aw_pa->ops.aw_get_irq_type = aw_pid_2032_get_irq_type; + + aw_pa->int_desc.mask_reg = AW_PID_2032_SYSINTM_REG; + aw_pa->int_desc.mask_default = AW_PID_2032_SYSINTM_DEFAULT; + aw_pa->int_desc.int_mask = AW_PID_2032_SYSINTM_DEFAULT; + aw_pa->int_desc.st_reg = AW_PID_2032_SYSINT_REG; + + aw_pa->pwd_desc.reg = AW_PID_2032_SYSCTRL_REG; + aw_pa->pwd_desc.mask = AW_PID_2032_PWDN_MASK; + aw_pa->pwd_desc.enable = AW_PID_2032_PWDN_POWER_DOWN_VALUE; + aw_pa->pwd_desc.disable = AW_PID_2032_PWDN_WORKING_VALUE; + + aw_pa->amppd_desc.reg = AW_PID_2032_SYSCTRL_REG; + aw_pa->amppd_desc.mask = AW_PID_2032_AMPPD_MASK; + aw_pa->amppd_desc.enable = AW_PID_2032_AMPPD_POWER_DOWN_VALUE; + aw_pa->amppd_desc.disable = AW_PID_2032_AMPPD_WORKING_VALUE; + + aw_pa->mute_desc.reg = AW_PID_2032_SYSCTRL2_REG; + aw_pa->mute_desc.mask = AW_PID_2032_HMUTE_MASK; + aw_pa->mute_desc.enable = AW_PID_2032_HMUTE_ENABLE_VALUE; + aw_pa->mute_desc.disable = AW_PID_2032_HMUTE_DISABLE_VALUE; + + aw_pa->vcalb_desc.vcalb_reg = AW_PID_2032_VTMCTRL3_REG; + aw_pa->vcalb_desc.vcal_factor = AW_PID_2032_VCAL_FACTOR; + aw_pa->vcalb_desc.cabl_base_value = AW_PID_2032_CABL_BASE_VALUE; + + aw_pa->vcalb_desc.icalk_value_factor = AW_PID_2032_ICABLK_FACTOR; + aw_pa->vcalb_desc.icalk_reg = AW_PID_2032_EFRM1_REG; + aw_pa->vcalb_desc.icalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.icalk_reg_mask = AW_PID_2032_EF_ISN_GESLP_MASK; + aw_pa->vcalb_desc.icalk_sign_mask = AW_PID_2032_EF_ISN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.icalk_neg_mask = AW_PID_2032_EF_ISN_GESLP_NEG; + + aw_pa->vcalb_desc.vcalk_reg = AW_PID_2032_EFRH_REG; + aw_pa->vcalb_desc.vcalk_reg_mask = AW_PID_2032_EF_VSN_GESLP_MASK; + aw_pa->vcalb_desc.vcalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.vcalk_sign_mask = AW_PID_2032_EF_VSN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.vcalk_neg_mask = AW_PID_2032_EF_VSN_GESLP_NEG; + aw_pa->vcalb_desc.vcalk_value_factor = AW_PID_2032_VCABLK_FACTOR; + + aw_pa->sysst_desc.reg = AW_PID_2032_SYSST_REG; + aw_pa->sysst_desc.mask = AW_PID_2032_SYSST_CHECK_MASK; + aw_pa->sysst_desc.st_check = AW_PID_2032_SYSST_CHECK; + aw_pa->sysst_desc.pll_check = AW_PID_2032_PLLS_LOCKED_VALUE; + + aw_pa->cco_mux_desc.reg = AW_PID_2032_PLLCTRL1_REG; + aw_pa->cco_mux_desc.mask = AW_PID_2032_CCO_MUX_MASK; + aw_pa->cco_mux_desc.divided_val = AW_PID_2032_CCO_MUX_DIVIDED_VALUE; + aw_pa->cco_mux_desc.bypass_val = AW_PID_2032_CCO_MUX_BYPASS_VALUE; + + aw_pa->voltage_desc.reg = AW_PID_2032_VBAT_REG; + aw_pa->voltage_desc.int_bit = AW_PID_2032_MONITOR_INT_10BIT; + aw_pa->voltage_desc.vbat_range = AW_PID_2032_MONITOR_VBAT_RANGE; + + aw_pa->temp_desc.reg = AW_PID_2032_TEMP_REG; + aw_pa->temp_desc.neg_mask = AW_PID_2032_MONITOR_TEMP_NEG_MASK; + aw_pa->temp_desc.sign_mask = AW_PID_2032_MONITOR_TEMP_SIGN_MASK; + + aw_pa->ipeak_desc.reg = AW_PID_2032_SYSCTRL2_REG; + aw_pa->ipeak_desc.mask = AW_PID_2032_BST_IPEAK_MASK; + + aw_pa->volume_desc.reg = AW_PID_2032_SYSCTRL2_REG; + aw_pa->volume_desc.mask = AW_PID_2032_VOL_MASK; + aw_pa->volume_desc.shift = AW_PID_2032_VOL_START_BIT; + aw_pa->volume_desc.mute_volume = AW_PID_2032_MUTE_VOL; + + aw_pa->bop_desc.reg = AW_REG_NONE; + + aw_pa->soft_rst.reg = AW_PID_2032_ID_REG; + aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + + ret = aw_device_probe(aw_pa); + + aw882xx->aw_pa = aw_pa; + return ret; +} + +/* [9 : 6]: -6DB ; [5 : 0]: 0.125DB real_value = value * 8 : 0.125db --> 1 */ +static unsigned int aw_pid_2055_reg_val_to_db(unsigned int value) +{ + return ((value >> 6) * AW_PID_2055_VOL_STEP_DB + (value & 0x3f)); +} + +/* [9 : 6]: -6DB ; [5 : 0]: 0.125DB reg_value = value / step << 6 + value % step ; step = 6 * 8 */ +static unsigned int aw_pid_2055_db_val_to_reg(unsigned int value) +{ + return (((value / AW_PID_2055_VOL_STEP_DB) << 6) + (value % AW_PID_2055_VOL_STEP_DB)); +} + +static int aw_pid_2055_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + unsigned int reg_value = 0; + unsigned int real_value = aw_pid_2055_db_val_to_reg(value); + + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW_PID_2055_SYSCTRL2_REG, ®_value); + + aw_dev_dbg(aw882xx->dev, "value %d , 0x%x", value, real_value); + + /*[9 : 0] volume*/ + real_value = (real_value | (reg_value & 0xfc00)); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW_PID_2055_SYSCTRL2_REG, real_value); + return 0; +} + +static int aw_pid_2055_get_volume(struct aw_device *aw_dev, unsigned int *value) +{ + unsigned int reg_value = 0; + unsigned int real_value = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + /* read value */ + aw882xx_i2c_read(aw882xx, AW_PID_2055_SYSCTRL2_REG, ®_value); + + /* [9 : 0] volume */ + real_value = (reg_value & 0x03ff); + + real_value = aw_pid_2055_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static bool aw_pid_2055_check_rd_access(int reg) +{ + if (reg >= AW_PID_2055_REG_MAX) { + return false; + } + + if (aw_pid_2055_reg_access[reg] & AW_PID_2055_REG_RD_ACCESS) { + return true; + } else { + return false; + } +} + +static bool aw_pid_2055_check_wr_access(int reg) +{ + if (reg >= AW_PID_2055_REG_MAX) + return false; + + if (aw_pid_2055_reg_access[reg] & + AW_PID_2055_REG_WR_ACCESS) + return true; + else + return false; +} + +static int aw_pid_2055_get_reg_num(void) +{ + return AW_PID_2055_REG_MAX; +} + +static unsigned int aw_pid_2055_get_irq_type(struct aw_device *aw_dev, + unsigned int value) +{ + unsigned int ret = INT_TYPE_NONE; + + /* UVL0 */ + if (value & (~AW_PID_2055_UVLI_MASK)) { + aw_dev_info(aw_dev->dev, "UVLO: occur"); + ret |= INT_TYPE_UVLO; + } + + /* BSTOCM */ + if (value & (~AW_PID_2055_BSTOCI_MASK)) { + aw_dev_info(aw_dev->dev, "BSTOCI: occur"); + ret |= INT_TYPE_BSTOC; + } + + /* OCDI */ + if (value & (~AW_PID_2055_OCDI_MASK)) { + aw_dev_info(aw_dev->dev, "OCDI: occur"); + ret |= INT_TYPE_OCDI; + } + + /* OTHI */ + if (value & (~AW_PID_2055_OTHI_MASK)) { + aw_dev_info(aw_dev->dev, "OTHI: occur"); + ret |= INT_TYPE_OTHI; + } + + return ret; +} + +static int aw_pid_2055_dev_init(struct aw882xx *aw882xx, int index) +{ + int ret; + struct aw_device *aw_pa = NULL; + aw_pa = devm_kzalloc(aw882xx->dev, sizeof(struct aw_device), GFP_KERNEL); + if (aw_pa == NULL) { + aw_dev_err(aw882xx->dev, "dev kalloc failed"); + return -ENOMEM; + } + + /*call aw device init func*/ + memset(aw_pa->acf_name, 0, AW_NAME_MAX); + memcpy(aw_pa->acf_name, AW_PID_2055_ACF_FILE, strlen(AW_PID_2055_ACF_FILE)); + memset(aw_pa->monitor_name, 0, AW_NAME_MAX); + memcpy(aw_pa->monitor_name, AW_PID_2055_MONITOR_FILE, strlen(AW_PID_2055_MONITOR_FILE)); + + aw_pa->prof_info.prof_desc = NULL; + aw_pa->prof_info.count = 0; + aw_pa->channel = 0; + aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_pa->bop_en = AW_BOP_DISABLE; + aw_pa->vol_step = AW_PID_2055_VOL_STEP; + + aw_pa->index = index; + aw_pa->private_data = (void *)aw882xx; + aw_pa->dev = aw882xx->dev; + aw_pa->i2c = aw882xx->i2c; + aw_pa->ops.aw_get_version = aw882xx_get_version; + aw_pa->ops.aw_get_dev_num = aw882xx_get_dev_num; + aw_pa->ops.aw_set_algo = aw882xx_dev_set_algo_en; + aw_pa->ops.aw_i2c_read = aw882xx_dev_i2c_read; + aw_pa->ops.aw_i2c_write = aw882xx_dev_i2c_write; + aw_pa->ops.aw_i2c_write_bits = aw882xx_dev_i2c_write_bits; + aw_pa->ops.aw_get_volume = aw_pid_2055_get_volume; + aw_pa->ops.aw_set_volume = aw_pid_2055_set_volume; + aw_pa->ops.aw_reg_val_to_db = aw_pid_2055_reg_val_to_db; + aw_pa->ops.aw_check_rd_access = aw_pid_2055_check_rd_access; + aw_pa->ops.aw_check_wr_access = aw_pid_2055_check_wr_access; + aw_pa->ops.aw_get_reg_num = aw_pid_2055_get_reg_num; + aw_pa->ops.aw_get_irq_type =aw_pid_2055_get_irq_type; + + aw_pa->int_desc.mask_reg = AW_PID_2055_SYSINTM_REG; + aw_pa->int_desc.mask_default = AW_PID_2055_SYSINTM_DEFAULT; + aw_pa->int_desc.int_mask = AW_PID_2055_SYSINTM_DEFAULT; + aw_pa->int_desc.st_reg = AW_PID_2055_SYSINT_REG; + + aw_pa->pwd_desc.reg = AW_PID_2055_SYSCTRL_REG; + aw_pa->pwd_desc.mask = AW_PID_2055_PWDN_MASK; + aw_pa->pwd_desc.enable = AW_PID_2055_PWDN_POWER_DOWN_VALUE; + aw_pa->pwd_desc.disable = AW_PID_2055_PWDN_WORKING_VALUE; + + aw_pa->amppd_desc.reg = AW_PID_2055_SYSCTRL_REG; + aw_pa->amppd_desc.mask = AW_PID_2055_AMPPD_MASK; + aw_pa->amppd_desc.enable = AW_PID_2055_AMPPD_POWER_DOWN_VALUE; + aw_pa->amppd_desc.disable = AW_PID_2055_AMPPD_WORKING_VALUE; + + aw_pa->mute_desc.reg = AW_PID_2055_SYSCTRL_REG; + aw_pa->mute_desc.mask = AW_PID_2055_HMUTE_MASK; + aw_pa->mute_desc.enable = AW_PID_2055_HMUTE_ENABLE_VALUE; + aw_pa->mute_desc.disable = AW_PID_2055_HMUTE_DISABLE_VALUE; + + aw_pa->vcalb_desc.vcalb_reg = AW_REG_NONE; + aw_pa->vcalb_desc.vcalk_reg = AW_REG_NONE; + aw_pa->vcalb_desc.icalk_reg = AW_REG_NONE; + + aw_pa->sysst_desc.reg = AW_PID_2055_SYSST_REG; + aw_pa->sysst_desc.mask = AW_PID_2055_SYSST_CHECK_MASK; + aw_pa->sysst_desc.st_check = AW_PID_2055_SYSST_CHECK; + aw_pa->sysst_desc.pll_check = AW_PID_2055_PLLS_LOCKED_VALUE; + + aw_pa->voltage_desc.reg = AW_REG_NONE; + aw_pa->temp_desc.reg = AW_REG_NONE; + + aw_pa->ipeak_desc.reg = AW_PID_2055_BSTCTRL2_REG; + aw_pa->ipeak_desc.mask = AW_PID_2055_BST_IPEAK_MASK; + + aw_pa->volume_desc.reg = AW_PID_2055_SYSCTRL2_REG; + aw_pa->volume_desc.mask = AW_PID_2055_VOL_MASK; + aw_pa->volume_desc.shift = AW_PID_2055_VOL_START_BIT; + aw_pa->volume_desc.mute_volume = AW_PID_2055_MUTE_VOL; + + aw_pa->cco_mux_desc.reg = AW_PID_2055_PLLCTRL3_REG; + aw_pa->cco_mux_desc.mask = AW_PID_2055_CCO_MUX_MASK; + aw_pa->cco_mux_desc.divided_val = AW_PID_2055_CCO_MUX_DIVIDED_VALUE; + aw_pa->cco_mux_desc.bypass_val = AW_PID_2055_CCO_MUX_BYPASS_VALUE; + + aw_pa->bop_desc.reg = AW_REG_NONE; + + aw_pa->soft_rst.reg = AW_PID_2055_ID_REG; + aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + + ret = aw_device_probe(aw_pa); + + aw882xx->aw_pa = aw_pa; + return ret; +} + +/* [9 : 6]: -6DB ; [5 : 0]: 0.125DB real_value = value * 8 : 0.125db --> 1 */ +static unsigned int aw_pid_2071_reg_val_to_db(unsigned int value) +{ + return ((value >> 6) * AW_PID_2071_VOL_STEP_DB + (value & 0x3f)); +} + +/* [9 : 6]: -6DB ; [5 : 0]: 0.125DB reg_value = value / step << 6 + value % step ; step = 6 * 8 */ +static unsigned int aw_pid_2071_db_val_to_reg(unsigned int value) +{ + return (((value / AW_PID_2071_VOL_STEP_DB) << 6) + (value % AW_PID_2071_VOL_STEP_DB)); +} + + +static int aw_pid_2071_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + unsigned int reg_value = 0; + unsigned int real_value = aw_pid_2071_db_val_to_reg(value); + + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW_PID_2071_SYSCTRL2_REG, ®_value); + + aw_dev_dbg(aw882xx->dev, "value %d , 0x%x", value, real_value); + + /* [15 : 6] volume */ + real_value = (real_value << 6) | (reg_value & 0x003f); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW_PID_2071_SYSCTRL2_REG, real_value); + return 0; +} + +static int aw_pid_2071_get_volume(struct aw_device *aw_dev, unsigned int *value) +{ + unsigned int reg_value = 0; + unsigned int real_value = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + /* read value */ + aw882xx_i2c_read(aw882xx, AW_PID_2071_SYSCTRL2_REG, ®_value); + + /* [15 : 6] volume */ + real_value = reg_value >> 6; + + real_value = aw_pid_2071_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static void aw_pid_2071_i2s_enable(struct aw_device *aw_dev, bool flag) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + struct aw_cali_desc *desc = &aw_dev->cali_desc; + aw_dev_dbg(aw882xx->dev, "enter"); + + if (!desc->mode) { + aw_dev_dbg(aw_dev->dev, "cali mode is none, needn't set i2s status"); + return; + } + + if (flag) { + aw882xx_i2c_write_bits(aw882xx, AW_PID_2071_I2SCFG1_REG, + AW_PID_2071_I2STXEN_MASK, + AW_PID_2071_I2STXEN_ENABLE_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW_PID_2071_I2SCFG1_REG, + AW_PID_2071_I2STXEN_MASK, + AW_PID_2071_I2STXEN_DISABLE_VALUE); + } +} + +static bool aw_pid_2071_check_rd_access(int reg) +{ + if (reg >= AW_PID_2071_REG_MAX) + return false; + + if (aw_pid_2071_reg_access[reg] & AW_PID_2071_REG_RD_ACCESS) + return true; + else + return false; +} + +static bool aw_pid_2071_check_wr_access(int reg) +{ + if (reg >= AW_PID_2071_REG_MAX) + return false; + + if (aw_pid_2071_reg_access[reg] & + AW_PID_2071_REG_WR_ACCESS) + return true; + else + return false; +} + +static int aw_pid_2071_get_reg_num(void) +{ + return AW_PID_2071_REG_MAX; +} + +static unsigned int aw_pid_2071_get_irq_type(struct aw_device *aw_dev, + unsigned int value) +{ + unsigned int ret = INT_TYPE_NONE; + + /* UVL0 */ + if (value & (~AW_PID_2071_UVLI_MASK)) { + aw_dev_info(aw_dev->dev, "UVLO: occur"); + ret |= INT_TYPE_UVLO; + } + + /* BSTOCM */ + if (value & (~AW_PID_2071_BSTOCI_MASK)) { + aw_dev_info(aw_dev->dev, "BSTOCI: occur"); + ret |= INT_TYPE_BSTOC; + } + + /* OCDI */ + if (value & (~AW_PID_2071_OCDI_MASK)) { + aw_dev_info(aw_dev->dev, "OCDI: occur"); + ret |= INT_TYPE_OCDI; + } + + /* OTHI */ + if (value & (~AW_PID_2071_OTHI_MASK)) { + aw_dev_info(aw_dev->dev, "OTHI: occur"); + ret |= INT_TYPE_OTHI; + } + + return ret; +} + +static int aw_pid_2071_dev_init(struct aw882xx *aw882xx, int index) +{ + int ret; + struct aw_device *aw_pa = NULL; + aw_pa = devm_kzalloc(aw882xx->dev, sizeof(struct aw_device), GFP_KERNEL); + if (aw_pa == NULL) { + aw_dev_err(aw882xx->dev, "dev kalloc failed"); + return -ENOMEM; + } + + /*call aw device init func*/ + memset(aw_pa->acf_name, 0, AW_NAME_MAX); + memcpy(aw_pa->acf_name, AW_PID_2071_ACF_FILE, strlen(AW_PID_2071_ACF_FILE)); + memset(aw_pa->monitor_name, 0, AW_NAME_MAX); + memcpy(aw_pa->monitor_name, AW_PID_2071_MONITOR_FILE, strlen(AW_PID_2071_MONITOR_FILE)); + + aw_pa->prof_info.prof_desc = NULL; + aw_pa->prof_info.count = 0; + aw_pa->channel = 0; + aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_pa->bop_en = AW_BOP_DISABLE; + aw_pa->vol_step = AW_PID_2071_VOL_STEP; + + aw_pa->index = index; + aw_pa->private_data = (void *)aw882xx; + aw_pa->dev = aw882xx->dev; + aw_pa->i2c = aw882xx->i2c; + aw_pa->ops.aw_get_version = aw882xx_get_version; + aw_pa->ops.aw_get_dev_num = aw882xx_get_dev_num; + aw_pa->ops.aw_set_algo = aw882xx_dev_set_algo_en; + aw_pa->ops.aw_i2c_read = aw882xx_dev_i2c_read; + aw_pa->ops.aw_i2c_write = aw882xx_dev_i2c_write; + aw_pa->ops.aw_i2c_write_bits = aw882xx_dev_i2c_write_bits; + aw_pa->ops.aw_get_volume = aw_pid_2071_get_volume; + aw_pa->ops.aw_set_volume = aw_pid_2071_set_volume; + aw_pa->ops.aw_reg_val_to_db = aw_pid_2071_reg_val_to_db; + aw_pa->ops.aw_i2s_enable = aw_pid_2071_i2s_enable; + aw_pa->ops.aw_check_rd_access = aw_pid_2071_check_rd_access; + aw_pa->ops.aw_check_wr_access = aw_pid_2071_check_wr_access; + aw_pa->ops.aw_get_reg_num = aw_pid_2071_get_reg_num; + aw_pa->ops.aw_get_irq_type = aw_pid_2071_get_irq_type; + + aw_pa->int_desc.mask_reg = AW_PID_2071_SYSINTM_REG; + aw_pa->int_desc.mask_default = AW_PID_2071_SYSINTM_DEFAULT; + aw_pa->int_desc.int_mask = AW_PID_2071_SYSINTM_DEFAULT; + aw_pa->int_desc.st_reg = AW_PID_2071_SYSINT_REG; + + aw_pa->pwd_desc.reg = AW_PID_2071_SYSCTRL_REG; + aw_pa->pwd_desc.mask = AW_PID_2071_PWDN_MASK; + aw_pa->pwd_desc.enable = AW_PID_2071_PWDN_POWER_DOWN_VALUE; + aw_pa->pwd_desc.disable = AW_PID_2071_PWDN_WORKING_VALUE; + + aw_pa->amppd_desc.reg = AW_PID_2071_SYSCTRL_REG; + aw_pa->amppd_desc.mask = AW_PID_2071_AMPPD_MASK; + aw_pa->amppd_desc.enable = AW_PID_2071_AMPPD_POWER_DOWN_VALUE; + aw_pa->amppd_desc.disable = AW_PID_2071_AMPPD_WORKING_VALUE; + + aw_pa->mute_desc.reg = AW_PID_2071_SYSCTRL2_REG; + aw_pa->mute_desc.mask = AW_PID_2071_HMUTE_MASK; + aw_pa->mute_desc.enable = AW_PID_2071_HMUTE_ENABLE_VALUE; + aw_pa->mute_desc.disable = AW_PID_2071_HMUTE_DISABLE_VALUE; + + aw_pa->vcalb_desc.vcalb_reg = AW_PID_2071_VTMCTRL3_REG; + aw_pa->vcalb_desc.vcal_factor = AW_PID_2071_VCAL_FACTOR; + aw_pa->vcalb_desc.cabl_base_value = AW_PID_2071_CABL_BASE_VALUE; + + aw_pa->vcalb_desc.icalk_value_factor = AW_PID_2071_ICABLK_FACTOR; + aw_pa->vcalb_desc.icalk_reg = AW_PID_2071_EFRH_REG; + aw_pa->vcalb_desc.icalk_reg_mask = AW_PID_2071_EF_VSN_OFFSET_MASK; + aw_pa->vcalb_desc.icalk_shift = AW_PID_2071_ICALK_SHIFT; + aw_pa->vcalb_desc.icalkl_reg = AW_PID_2071_EFRM1_REG; + aw_pa->vcalb_desc.icalkl_reg_mask = AW_PID_2071_EF_ISN_OFFSET_MASK; + aw_pa->vcalb_desc.icalkl_shift = AW_PID_2071_ICALKL_SHIFT; + aw_pa->vcalb_desc.icalk_sign_mask = AW_PID_2071_EF_ISN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.icalk_neg_mask = AW_PID_2071_EF_ISN_GESLP_NEG; + + aw_pa->vcalb_desc.vcalk_reg = AW_PID_2071_EFRH_REG; + aw_pa->vcalb_desc.vcalk_reg_mask = AW_PID_2071_EF_VSN_GESLP_MASK; + aw_pa->vcalb_desc.vcalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.vcalk_sign_mask = AW_PID_2071_EF_VSN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.vcalk_neg_mask = AW_PID_2071_EF_VSN_GESLP_NEG; + aw_pa->vcalb_desc.vcalk_value_factor = AW_PID_2071_VCABLK_FACTOR; + + aw_pa->sysst_desc.reg = AW_PID_2071_SYSST_REG; + aw_pa->sysst_desc.mask = AW_PID_2071_SYSST_CHECK_MASK; + aw_pa->sysst_desc.st_check = AW_PID_2071_SYSST_CHECK; + aw_pa->sysst_desc.pll_check = AW_PID_2071_PLLS_LOCKED_VALUE; + + aw_pa->cco_mux_desc.reg = AW_PID_2071_PLLCTRL1_REG; + aw_pa->cco_mux_desc.mask = AW_PID_2071_CCO_MUX_MASK; + aw_pa->cco_mux_desc.divided_val = AW_PID_2071_CCO_MUX_DIVIDED_VALUE; + aw_pa->cco_mux_desc.bypass_val = AW_PID_2071_CCO_MUX_BYPASS_VALUE; + + aw_pa->voltage_desc.reg = AW_PID_2071_VBAT_REG; + aw_pa->voltage_desc.int_bit = AW_PID_2071_MONITOR_INT_10BIT; + aw_pa->voltage_desc.vbat_range = AW_PID_2071_MONITOR_VBAT_RANGE; + + aw_pa->temp_desc.reg = AW_PID_2071_TEMP_REG; + aw_pa->temp_desc.neg_mask = AW_PID_2071_MONITOR_TEMP_NEG_MASK; + aw_pa->temp_desc.sign_mask = AW_PID_2071_MONITOR_TEMP_SIGN_MASK; + + aw_pa->ipeak_desc.reg = AW_PID_2071_SYSCTRL2_REG; + aw_pa->ipeak_desc.mask = AW_PID_2071_BST_IPEAK_MASK; + + aw_pa->volume_desc.reg = AW_PID_2071_SYSCTRL2_REG; + aw_pa->volume_desc.mask = AW_PID_2071_VOL_MASK; + aw_pa->volume_desc.shift = AW_PID_2071_VOL_START_BIT; + aw_pa->volume_desc.mute_volume = AW_PID_2071_MUTE_VOL; + + aw_pa->bop_desc.reg = AW_REG_NONE; + + aw_pa->soft_rst.reg = AW_PID_2071_ID_REG; + aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + + ret = aw_device_probe(aw_pa); + + aw882xx->aw_pa = aw_pa; + return ret; +} + +/* [9 : 6]: -6DB ; [5 : 0]: 0.125DB real_value = value * 8 : 0.125db --> 1 */ +static unsigned int aw_pid_2113_reg_val_to_db(unsigned int value) +{ + return ((value >> 6) * AW_PID_2113_VOL_STEP_DB + (value & 0x3f)); +} + +/* [9 : 6]: -6DB ; [5 : 0]: 0.125DB reg_value = value / step << 6 + value % step ; step = 6 * 8 */ +static unsigned int aw_pid_2113_db_val_to_reg(unsigned int value) +{ + return (((value / AW_PID_2113_VOL_STEP_DB) << 6) + (value % AW_PID_2113_VOL_STEP_DB)); +} + +static int aw_pid_2113_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + unsigned int reg_value = 0; + unsigned int real_value = aw_pid_2113_db_val_to_reg(value); + + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW_PID_2113_SYSCTRL2_REG, ®_value); + + aw_dev_dbg(aw882xx->dev, "value %d , 0x%x", value, real_value); + + /*[9 : 0] volume*/ + real_value = (real_value | (reg_value & 0xfc00)); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW_PID_2113_SYSCTRL2_REG, real_value); + return 0; +} + +static int aw_pid_2113_get_volume(struct aw_device *aw_dev, unsigned int *value) +{ + unsigned int reg_value = 0; + unsigned int real_value = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + /* read value */ + aw882xx_i2c_read(aw882xx, AW_PID_2113_SYSCTRL2_REG, ®_value); + + /* [9 : 0] volume */ + real_value = (reg_value & 0x03ff); + + real_value = aw_pid_2113_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static void aw_pid_2113_i2s_enable(struct aw_device *aw_dev, bool flag) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + struct aw_cali_desc *desc = &aw_dev->cali_desc; + aw_dev_dbg(aw882xx->dev, "enter"); + + if (!desc->mode) { + aw_dev_dbg(aw_dev->dev, "cali mode is none, needn't set i2s status"); + return; + } + + if (flag) { + aw882xx_i2c_write_bits(aw882xx, AW_PID_2113_I2SCTRL3_REG, + AW_PID_2113_I2STXEN_MASK, + AW_PID_2113_I2STXEN_ENABLE_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW_PID_2113_I2SCTRL3_REG, + AW_PID_2113_I2STXEN_MASK, + AW_PID_2113_I2STXEN_DISABLE_VALUE); + } +} + +static bool aw_pid_2113_check_rd_access(int reg) +{ + if (reg >= AW_PID_2113_REG_MAX) + return false; + + if (aw_pid_2113_reg_access[reg] & AW_PID_2113_REG_RD_ACCESS) + return true; + else + return false; +} + +static bool aw_pid_2113_check_wr_access(int reg) +{ + if (reg >= AW_PID_2113_REG_MAX) + return false; + + if (aw_pid_2113_reg_access[reg] & + AW_PID_2113_REG_WR_ACCESS) + return true; + else + return false; +} + +static int aw_pid_2113_get_reg_num(void) +{ + return AW_PID_2113_REG_MAX; +} + +static unsigned int aw_pid_2113_get_irq_type(struct aw_device *aw_dev, + unsigned int value) +{ + unsigned int ret = INT_TYPE_NONE; + + /* UVL0 */ + if (value & (~AW_PID_2113_UVLI_MASK)) { + aw_dev_info(aw_dev->dev, "UVLO: occur"); + ret |= INT_TYPE_UVLO; + } + + /* BSTOCM */ + if (value & (~AW_PID_2113_BSTOCI_MASK)) { + aw_dev_info(aw_dev->dev, "BSTOCI: occur"); + ret |= INT_TYPE_BSTOC; + } + + /* OCDI */ + if (value & (~AW_PID_2113_OCDI_MASK)) { + aw_dev_info(aw_dev->dev, "OCDI: occur"); + ret |= INT_TYPE_OCDI; + } + + /* OTHI */ + if (value & (~AW_PID_2113_OTHI_MASK)) { + aw_dev_info(aw_dev->dev, "OTHI: occur"); + ret |= INT_TYPE_OTHI; + } + + return ret; +} + +static void aw_pid_2113_efver_check(struct aw_device *aw_dev) +{ + unsigned int reg_val = 0; + uint16_t temh = 0; + uint16_t teml = 0; + + aw_dev->ops.aw_i2c_read(aw_dev, + AW_PID_2113_EFRH3_REG, ®_val); + temh = ((uint16_t)reg_val & (~AW_PID_2113_TEMH_MASK)); + + aw_dev->ops.aw_i2c_read(aw_dev, + AW_PID_2113_EFRL3_REG, ®_val); + teml = ((uint16_t)reg_val & (~AW_PID_2113_TEML_MASK)); + + if ((temh | teml) == AW_PID_2113_DEFAULT_CFG) + aw_dev->frcset_en = AW_FRCSET_ENABLE; + else + aw_dev->frcset_en = AW_FRCSET_DISABLE; + + aw_dev_info(aw_dev->dev, "tem is 0x%04x, frcset_en is %d", + (temh | teml), aw_dev->frcset_en); +} + +static void aw_pid_2113_reg_force_set(struct aw_device *aw_dev) +{ + aw_dev_dbg(aw_dev->dev, "enter"); + + if (aw_dev->frcset_en == AW_FRCSET_ENABLE) { + /*set FORCE_PWM*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, AW_PID_2113_BSTCTRL3_REG, + AW_PID_2113_FORCE_PWM_MASK, AW_PID_2113_FORCE_PWM_FORCEMINUS_PWM_VALUE); + /*set BOOST_OC_WIDTH*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, AW_PID_2113_BSTCTRL5_REG, + AW_PID_2113_BST_OS_WIDTH_MASK, AW_PID_2113_BST_OS_WIDTH_50NS_VALUE); + /*set BURST_LOOPR*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, AW_PID_2113_BSTCTRL6_REG, + AW_PID_2113_BST_LOOPR_MASK, AW_PID_2113_BST_LOOPR_340K_VALUE); + /*set RSQN_DLY*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, AW_PID_2113_BSTCTRL7_REG, + AW_PID_2113_RSQN_DLY_MASK, AW_PID_2113_RSQN_DLY_35NS_VALUE); + /*set BURST_SSMODE*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, AW_PID_2113_BSTCTRL8_REG, + AW_PID_2113_BURST_SSMODE_MASK, AW_PID_2113_BURST_SSMODE_FAST_VALUE); + /*set BST_BURST*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, AW_PID_2113_BSTCTRL9_REG, + AW_PID_2113_BST_BURST_MASK, AW_PID_2113_BST_BURST_30MA_VALUE); + aw_dev_dbg(aw_dev->dev, "force set reg done!"); + }else { + aw_dev_info(aw_dev->dev, "needn't set reg value"); + } +} + +static int aw_pid_2113_dev_init(struct aw882xx *aw882xx, int index) +{ + int ret; + struct aw_device *aw_pa = NULL; + aw_pa = devm_kzalloc(aw882xx->dev, sizeof(struct aw_device), GFP_KERNEL); + if (aw_pa == NULL) { + aw_dev_err(aw882xx->dev, "dev kalloc failed"); + return -ENOMEM; + } + + /*call aw device init func*/ + memset(aw_pa->acf_name, 0, AW_NAME_MAX); + memcpy(aw_pa->acf_name, AW_PID_2113_ACF_FILE, strlen(AW_PID_2113_ACF_FILE)); + memset(aw_pa->monitor_name, 0, AW_NAME_MAX); + memcpy(aw_pa->monitor_name, AW_PID_2113_MONITOR_FILE, strlen(AW_PID_2113_MONITOR_FILE)); + + aw_pa->prof_info.prof_desc = NULL; + aw_pa->prof_info.count = 0; + aw_pa->channel = 0; + aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_pa->bop_en = AW_BOP_DISABLE; + aw_pa->vol_step = AW_PID_2113_VOL_STEP; + + aw_pa->index = index; + aw_pa->private_data = (void *)aw882xx; + aw_pa->dev = aw882xx->dev; + aw_pa->i2c = aw882xx->i2c; + aw_pa->ops.aw_get_version = aw882xx_get_version; + aw_pa->ops.aw_get_dev_num = aw882xx_get_dev_num; + aw_pa->ops.aw_set_algo = aw882xx_dev_set_algo_en; + aw_pa->ops.aw_i2c_read = aw882xx_dev_i2c_read; + aw_pa->ops.aw_i2c_write = aw882xx_dev_i2c_write; + aw_pa->ops.aw_i2c_write_bits = aw882xx_dev_i2c_write_bits; + aw_pa->ops.aw_get_volume = aw_pid_2113_get_volume; + aw_pa->ops.aw_set_volume = aw_pid_2113_set_volume; + aw_pa->ops.aw_reg_val_to_db = aw_pid_2113_reg_val_to_db; + aw_pa->ops.aw_i2s_enable = aw_pid_2113_i2s_enable; + aw_pa->ops.aw_check_rd_access = aw_pid_2113_check_rd_access; + aw_pa->ops.aw_check_wr_access = aw_pid_2113_check_wr_access; + aw_pa->ops.aw_get_reg_num = aw_pid_2113_get_reg_num; + aw_pa->ops.aw_get_irq_type = aw_pid_2113_get_irq_type; + aw_pa->ops.aw_reg_force_set = aw_pid_2113_reg_force_set; + + aw_pa->int_desc.mask_reg = AW_PID_2113_SYSINTM_REG; + aw_pa->int_desc.mask_default = AW_PID_2113_SYSINTM_DEFAULT; + aw_pa->int_desc.int_mask = AW_PID_2113_SYSINTM_DEFAULT; + aw_pa->int_desc.st_reg = AW_PID_2113_SYSINT_REG; + + aw_pa->pwd_desc.reg = AW_PID_2113_SYSCTRL_REG; + aw_pa->pwd_desc.mask = AW_PID_2113_PWDN_MASK; + aw_pa->pwd_desc.enable = AW_PID_2113_PWDN_POWER_DOWN_VALUE; + aw_pa->pwd_desc.disable = AW_PID_2113_PWDN_WORKING_VALUE; + + aw_pa->amppd_desc.reg = AW_PID_2113_SYSCTRL_REG; + aw_pa->amppd_desc.mask = AW_PID_2113_AMPPD_MASK; + aw_pa->amppd_desc.enable = AW_PID_2113_AMPPD_POWER_DOWN_VALUE; + aw_pa->amppd_desc.disable = AW_PID_2113_AMPPD_WORKING_VALUE; + + aw_pa->mute_desc.reg = AW_PID_2113_SYSCTRL_REG; + aw_pa->mute_desc.mask = AW_PID_2113_HMUTE_MASK; + aw_pa->mute_desc.enable = AW_PID_2113_HMUTE_ENABLE_VALUE; + aw_pa->mute_desc.disable = AW_PID_2113_HMUTE_DISABLE_VALUE; + + aw_pa->vcalb_desc.vcalb_reg = AW_PID_2113_VSNTM1_REG; + aw_pa->vcalb_desc.vcal_factor = AW_PID_2113_VCAL_FACTOR; + aw_pa->vcalb_desc.cabl_base_value = AW_PID_2113_CABL_BASE_VALUE; + + aw_pa->vcalb_desc.icalk_reg = AW_PID_2113_EFRH4_REG; + aw_pa->vcalb_desc.icalk_reg_mask = AW_PID_2113_EF_ISN_GESLP_H_MASK; + aw_pa->vcalb_desc.icalk_shift = AW_PID_2113_ICALK_SHIFT; + aw_pa->vcalb_desc.icalkl_reg = AW_PID_2113_EFRL4_REG; + aw_pa->vcalb_desc.icalkl_reg_mask = AW_PID_2113_EF_ISN_GESLP_L_MASK; + aw_pa->vcalb_desc.icalkl_shift = AW_PID_2113_ICALKL_SHIFT; + aw_pa->vcalb_desc.icalk_sign_mask = AW_PID_2113_EF_ISN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.icalk_neg_mask = AW_PID_2113_EF_ISN_GESLP_NEG; + aw_pa->vcalb_desc.icalk_value_factor = AW_PID_2113_ICABLK_FACTOR; + + aw_pa->vcalb_desc.vcalk_reg = AW_PID_2113_EFRH3_REG; + aw_pa->vcalb_desc.vcalk_reg_mask = AW_PID_2113_EF_VSN_GESLP_H_MASK; + aw_pa->vcalb_desc.vcalk_shift = AW_PID_2113_VCALK_SHIFT; + aw_pa->vcalb_desc.vcalkl_reg = AW_PID_2113_EFRL3_REG; + aw_pa->vcalb_desc.vcalkl_reg_mask = AW_PID_2113_EF_VSN_GESLP_L_MASK; + aw_pa->vcalb_desc.vcalkl_shift = AW_PID_2113_VCALKL_SHIFT; + aw_pa->vcalb_desc.vcalk_sign_mask = AW_PID_2113_EF_VSN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.vcalk_neg_mask = AW_PID_2113_EF_VSN_GESLP_NEG; + aw_pa->vcalb_desc.vcalk_value_factor = AW_PID_2113_VCABLK_FACTOR; + + aw_pa->sysst_desc.reg = AW_PID_2113_SYSST_REG; + aw_pa->sysst_desc.mask = AW_PID_2113_SYSST_CHECK_MASK; + aw_pa->sysst_desc.st_check = AW_PID_2113_SYSST_CHECK; + aw_pa->sysst_desc.pll_check = AW_PID_2113_PLLS_LOCKED_VALUE; + + aw_pa->cco_mux_desc.reg = AW_PID_2113_PLLCTRL1_REG; + aw_pa->cco_mux_desc.mask = AW_PID_2113_CCO_MUX_MASK; + aw_pa->cco_mux_desc.divided_val = AW_PID_2113_CCO_MUX_DIVIDED_VALUE; + aw_pa->cco_mux_desc.bypass_val = AW_PID_2113_CCO_MUX_BYPASS_VALUE; + + aw_pa->voltage_desc.reg = AW_PID_2113_VBAT_REG; + aw_pa->voltage_desc.int_bit = AW_PID_2113_MONITOR_INT_10BIT; + aw_pa->voltage_desc.vbat_range = AW_PID_2113_MONITOR_VBAT_RANGE; + + aw_pa->temp_desc.reg = AW_PID_2113_TEMP_REG; + aw_pa->temp_desc.neg_mask = AW_PID_2113_MONITOR_TEMP_NEG_MASK; + aw_pa->temp_desc.sign_mask = AW_PID_2113_MONITOR_TEMP_SIGN_MASK; + + aw_pa->ipeak_desc.reg = AW_PID_2113_BSTCTRL2_REG; + aw_pa->ipeak_desc.mask = AW_PID_2113_BST_IPEAK_MASK; + + aw_pa->volume_desc.reg = AW_PID_2113_SYSCTRL2_REG; + aw_pa->volume_desc.mask = AW_PID_2113_VOL_MASK; + aw_pa->volume_desc.shift = AW_PID_2113_VOL_START_BIT; + aw_pa->volume_desc.mute_volume = AW_PID_2113_MUTE_VOL; + + aw_pa->bop_desc.reg = AW_PID_2113_SADCCTRL3_REG; + aw_pa->bop_desc.mask = AW_PID_2113_BOP_EN_MASK; + aw_pa->bop_desc.enable = AW_PID_2113_BOP_EN_ENABLE_VALUE; + aw_pa->bop_desc.disbale = AW_PID_2113_BOP_EN_DISABLE_VALUE; + + aw_pa->soft_rst.reg = AW_PID_2113_ID_REG; + aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + aw_pid_2113_efver_check(aw_pa); + + ret = aw_device_probe(aw_pa); + + aw882xx->aw_pa = aw_pa; + return ret; +} + +int aw882xx_init(struct aw882xx *aw882xx, int index) +{ + if (aw882xx->chip_id == PID_1852_ID) + return aw_pid_1852_dev_init(aw882xx, index); + else if (aw882xx->chip_id == PID_2013_ID) + return aw_pid_2013_dev_init(aw882xx, index); + else if (aw882xx->chip_id == PID_2032_ID) + return aw_pid_2032_dev_init(aw882xx, index); + else if (aw882xx->chip_id == PID_2055_ID) + return aw_pid_2055_dev_init(aw882xx, index); + else if (aw882xx->chip_id == PID_2071_ID) + return aw_pid_2071_dev_init(aw882xx, index); + else if (aw882xx->chip_id == PID_2113_ID) + return aw_pid_2113_dev_init(aw882xx, index); + + aw_dev_err(aw882xx->dev, "unsupported chip id 0x%04x", aw882xx->chip_id); + return -EINVAL; +} + diff --git a/asoc/codecs/aw88261/aw_log.h b/asoc/codecs/aw88261/aw_log.h new file mode 100644 index 000000000000..31e0f8e1e510 --- /dev/null +++ b/asoc/codecs/aw88261/aw_log.h @@ -0,0 +1,34 @@ +#ifndef __AWINIC_LOG_H__ +#define __AWINIC_LOG_H__ + +/******************************************** + * print information control + *******************************************/ +#define aw_dev_err(dev, format, ...) \ + do { \ + pr_err("[Awinic][%s]%s: " format "\n", dev_name(dev), __func__, ##__VA_ARGS__); \ + } while (0) +#define aw_dev_info(dev, format, ...) \ + do { \ + pr_info("[Awinic][%s]%s: " format "\n", dev_name(dev), __func__, ##__VA_ARGS__); \ + } while (0) +#define aw_dev_dbg(dev, format, ...) \ + do { \ + pr_debug("[Awinic][%s]%s: " format "\n", dev_name(dev), __func__, ##__VA_ARGS__); \ + } while (0) + +#define aw_pr_err(format, ...) \ + do { \ + pr_err("[Awinic]%s: " format "\n", __func__, ##__VA_ARGS__); \ + } while (0) +#define aw_pr_info(format, ...) \ + do { \ + pr_info("[Awinic]%s: " format "\n", __func__, ##__VA_ARGS__); \ + } while (0) +#define aw_pr_dbg(format, ...) \ + do { \ + pr_debug("[Awinic]%s: " format "\n", __func__, ##__VA_ARGS__); \ + } while (0) + +#endif + diff --git a/asoc/codecs/aw88261/aw_monitor.c b/asoc/codecs/aw88261/aw_monitor.c new file mode 100644 index 000000000000..2783fba1a9c6 --- /dev/null +++ b/asoc/codecs/aw88261/aw_monitor.c @@ -0,0 +1,1230 @@ +/* + * awinic_monitor.c monitor_module + * + * Version: v0.1.17 + * + * Copyright (c) 2019 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw882xx.h" +#include "aw_dsp.h" +#include "aw_monitor.h" +#include "aw_log.h" + + +/***************************************************** + * device monitor + *****************************************************/ +static int aw_get_hmute(struct aw_device *aw_dev) +{ + unsigned int reg_val = 0; + int ret; + struct aw_mute_desc *desc = &aw_dev->mute_desc; + + aw_dev_dbg(aw_dev->dev, "enter"); + + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->reg, ®_val); + if (ret < 0) + return ret; + + if (reg_val & (~desc->mask)) + ret = 1; + else + ret = 0; + + return ret; +} + +static int aw_monitor_get_data_form_system(struct aw_device *aw_dev, + int *data, int data_type) +{ + char name[] = "battery"; + int ret; + union power_supply_propval prop = { 0 }; + struct power_supply *psy = NULL; + + psy = power_supply_get_by_name(name); + if (psy) { + ret = power_supply_get_property(psy, data_type, &prop); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get data failed"); + return -EINVAL; + } + *data = prop.intval; + aw_dev_dbg(aw_dev->dev, "data: %d", *data); + } else { + aw_dev_err(aw_dev->dev, "no struct power supply name : %s", name); + return -EINVAL; + } + return 0; + +} + +static int aw_monitor_get_voltage(struct aw_device *aw_dev, unsigned int *vol) +{ + int ret = -1; + uint16_t local_vol = 0; + struct aw_voltage_desc *desc = &aw_dev->voltage_desc; + + if (desc->reg == AW_REG_NONE) { + ret = aw_monitor_get_data_form_system(aw_dev, (int *)vol, POWER_SUPPLY_PROP_VOLTAGE_NOW); + if (ret) { + aw_dev_err(aw_dev->dev, "get voltage from system failed!"); + return ret; + } + *vol = (*vol) / 1000; + } else { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->reg, vol); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "read voltage failed!"); + return ret; + } + local_vol = ((*vol) * desc->vbat_range) / desc->int_bit; + *vol = local_vol; + } + + aw_dev_info(aw_dev->dev, "chip voltage is %d", *vol); + + return 0; +} + +static int aw_monitor_get_temperature(struct aw_device *aw_dev, int *temp) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t local_temp; + struct aw_temperature_desc *desc = &aw_dev->temp_desc; + + if (desc->reg == AW_REG_NONE) { + ret = aw_monitor_get_data_form_system(aw_dev, temp, POWER_SUPPLY_PROP_TEMP); + if (ret) { + aw_dev_err(aw_dev->dev, "get temperature from system failed!"); + return ret; + } + *temp = (*temp) / 10; + } else { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->reg, ®_val); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get temperature failed!"); + return ret; + } + aw_dev_info(aw_dev->dev, "reg val is 0x%04x", reg_val); + local_temp = reg_val; + if (local_temp & (~desc->sign_mask)) + local_temp = local_temp | desc->neg_mask; + + *temp = (int16_t)local_temp; + } + + aw_dev_info(aw_dev->dev, "chip temperature = %d", *temp); + return 0; +} + +static int aw_monitor_get_temp_and_vol(struct aw_device *aw_dev) +{ + struct aw_monitor_desc *monitor = &aw_dev->monitor_desc; + unsigned int voltage = 0; + int current_temp = 0; + int ret = -1; + +#ifdef AW_DEBUG + if (monitor->test_vol == 0) { + ret = aw_monitor_get_voltage(aw_dev, &voltage); + if (ret < 0) + return ret; + } else { + voltage = monitor->test_vol; + } + + if (monitor->test_temp == 0) { + ret = aw_monitor_get_temperature(aw_dev, ¤t_temp); + if (ret) + return ret; + } else { + current_temp = monitor->test_temp; + } +#else + ret = aw_monitor_get_voltage(aw_dev, &voltage); + if (ret < 0) + return ret; + + ret = aw_monitor_get_temperature(aw_dev, ¤t_temp); + if (ret < 0) + return ret; +#endif + + monitor->vol_trace.sum_val += voltage; + monitor->temp_trace.sum_val += current_temp; + monitor->samp_count++; + + return 0; +} + +static int aw_monitor_first_get_data_form_table(struct aw_device *aw_dev, + struct aw_table_info table_info, + struct aw_monitor_trace *data_trace) +{ + int i; + + if (table_info.aw_table == NULL) { + aw_dev_err(aw_dev->dev, "table_info.aw_table is null"); + return -EINVAL; + } + + for (i = 0; i < table_info.table_num; i++) { + if (data_trace->sum_val >= table_info.aw_table[i].min_val) { + memcpy(&data_trace->aw_table, &table_info.aw_table[i], + sizeof(struct aw_table)); + break; + } + } + return 0; +} + +static int aw_monitor_trace_data_from_table(struct aw_device *aw_dev, + struct aw_table_info table_info, + struct aw_monitor_trace *data_trace) +{ + int i; + + if (table_info.aw_table == NULL) { + aw_dev_err(aw_dev->dev, "table_info.aw_table is null"); + return -EINVAL; + } + + for (i = 0; i < table_info.table_num; i++) { + if (data_trace->sum_val >= table_info.aw_table[i].min_val && + data_trace->sum_val <= table_info.aw_table[i].max_val) { + memcpy(&data_trace->aw_table, &table_info.aw_table[i], + sizeof(struct aw_table)); + break; + } + } + + return 0; +} + +static int aw_monitor_get_data_from_table(struct aw_device *aw_dev, + struct aw_table_info table_info, + struct aw_monitor_trace *data_trace, + uint32_t aplha) +{ + struct aw_monitor_desc *monitor = &aw_dev->monitor_desc; + + if (monitor->first_entry == AW_FIRST_ENTRY) { + return aw_monitor_first_get_data_form_table(aw_dev, + table_info, data_trace); + } else { + data_trace->sum_val = data_trace->sum_val / monitor->samp_count; + data_trace->sum_val = ((int32_t)aplha * data_trace->sum_val + + (1000 - (int32_t)aplha) * data_trace->pre_val) / 1000; + return aw_monitor_trace_data_from_table(aw_dev, + table_info, data_trace); + } + + return 0; +} + +static int aw_monitor_get_data(struct aw_device *aw_dev) +{ + struct aw_monitor_desc *monitor = &aw_dev->monitor_desc; + struct aw_monitor_cfg *monitor_cfg = &monitor->monitor_cfg; + struct aw_monitor_trace *vol_trace = &monitor->vol_trace; + struct aw_monitor_trace *temp_trace = &monitor->temp_trace; + int ret; + + if (monitor_cfg->vol_switch) { + ret = aw_monitor_get_data_from_table(aw_dev, + monitor_cfg->vol_info, vol_trace, + monitor_cfg->vol_aplha); + if (ret < 0) + return ret; + } else { + vol_trace->aw_table.ipeak = IPEAK_NONE; + vol_trace->aw_table.gain = GAIN_NONE; + vol_trace->aw_table.vmax = VMAX_NONE; + } + + if (monitor_cfg->temp_switch) { + ret = aw_monitor_get_data_from_table(aw_dev, + monitor_cfg->temp_info, temp_trace, + monitor_cfg->temp_aplha); + if (ret < 0) + return ret; + } else { + temp_trace->aw_table.ipeak = IPEAK_NONE; + temp_trace->aw_table.gain = GAIN_NONE; + temp_trace->aw_table.vmax = VMAX_NONE; + } + + aw_dev_dbg(aw_dev->dev, "filter_vol:%d, vol: ipeak = 0x%x, gain = 0x%x, vmax = 0x%x", + monitor->vol_trace.sum_val, vol_trace->aw_table.ipeak, + vol_trace->aw_table.gain, vol_trace->aw_table.vmax); + + aw_dev_dbg(aw_dev->dev, "filter_temp:%d, temp: ipeak = 0x%x, gain = 0x%x, vmax = 0x%x", + monitor->temp_trace.sum_val, temp_trace->aw_table.ipeak, + temp_trace->aw_table.gain, temp_trace->aw_table.vmax); + return 0; +} + +static void aw_monitor_get_cfg(struct aw_device *aw_dev, + struct aw_table *set_table) +{ + struct aw_monitor_desc *monitor = &aw_dev->monitor_desc; + struct aw_table *temp_data = &monitor->temp_trace.aw_table; + struct aw_table *vol_data = &monitor->vol_trace.aw_table; + + if (temp_data->ipeak == IPEAK_NONE && vol_data->ipeak == IPEAK_NONE) { + memcpy(set_table, temp_data, sizeof(struct aw_table)); + } else if (temp_data->ipeak == IPEAK_NONE) { + memcpy(set_table, vol_data, sizeof(struct aw_table)); + } else if (vol_data->ipeak == IPEAK_NONE) { + memcpy(set_table, temp_data, sizeof(struct aw_table)); + } else { + if (monitor->monitor_cfg.logic_switch == AW_MON_LOGIC_OR) { + set_table->ipeak = (temp_data->ipeak < vol_data->ipeak ? + temp_data->ipeak : vol_data->ipeak); + set_table->gain = (temp_data->gain < vol_data->gain ? + vol_data->gain : temp_data->gain); + set_table->vmax = (temp_data->vmax < vol_data->vmax ? + vol_data->vmax : temp_data->vmax); + } else { + set_table->ipeak = (temp_data->ipeak < vol_data->ipeak ? + vol_data->ipeak : temp_data->ipeak); + set_table->gain = (temp_data->gain < vol_data->gain ? + temp_data->gain : vol_data->gain); + set_table->vmax = (temp_data->vmax < vol_data->vmax ? + temp_data->vmax : vol_data->vmax); + } + } +} + +static void aw_monitor_set_ipeak(struct aw_device *aw_dev, + uint16_t ipeak) +{ + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + unsigned int reg_val = 0; + unsigned int read_reg_val; + int ret; + struct aw_ipeak_desc *desc = &aw_dev->ipeak_desc; + + if (ipeak == IPEAK_NONE || (!monitor_cfg->ipeak_switch)) + return; + + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->reg, ®_val); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "read ipeak failed"); + return; + } + + read_reg_val = reg_val; + + read_reg_val &= (~desc->mask); + + if (read_reg_val == ipeak) { + aw_dev_dbg(aw_dev->dev, "ipeak = 0x%x, no change", + read_reg_val); + return; + } + + reg_val &= desc->mask; + read_reg_val = ipeak; + reg_val |= read_reg_val; + + ret = aw_dev->ops.aw_i2c_write(aw_dev, desc->reg, reg_val); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "write ipeak failed"); + return; + } + aw_dev_info(aw_dev->dev, "set reg val = 0x%x, ipeak = 0x%x", + reg_val, ipeak); + +} + +static void aw_monitor_set_gain(struct aw_device *aw_dev, uint16_t gain) +{ + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + unsigned int read_volume; + unsigned int set_volume; + int ret; + + if (gain == GAIN_NONE || (!monitor_cfg->gain_switch)) + return; + + ret = aw_dev->ops.aw_get_volume(aw_dev, &read_volume); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "read volume failed"); + return; + } + + gain = aw_dev->ops.aw_reg_val_to_db(gain); + + /*add offset*/ + set_volume = gain + aw_dev->volume_desc.init_volume; + + if (read_volume == set_volume) { + aw_dev_dbg(aw_dev->dev, "gain = 0x%x, no change", read_volume); + return; + } + + ret = aw_dev->ops.aw_set_volume(aw_dev, set_volume); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "set gain failed"); + return; + } + aw_dev_info(aw_dev->dev, "set reg val = 0x%x, gain = 0x%x", + set_volume, gain); + +} + +static void aw_monitor_set_vmax(struct aw_device *aw_dev, + uint32_t vmax) +{ + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + int ret; + + if (vmax == VMAX_NONE || (!monitor_cfg->vmax_switch)) + return; + + if ((vmax == aw_dev->monitor_desc.pre_vmax) && + (aw_dev->monitor_desc.first_entry != AW_FIRST_ENTRY)) { + aw_dev_dbg(aw_dev->dev, "vmax = 0x%x, no change", vmax); + return; + } + + ret = aw_dsp_write_vmax(aw_dev, (char *)&vmax, sizeof(uint32_t)); + if (ret) + return; + + aw_dev->monitor_desc.pre_vmax = vmax; + aw_dev_dbg(aw_dev->dev, "set vmax = 0x%x", vmax); +} + +static int aw_monitor_work(struct aw_device *aw_dev) +{ + struct aw_monitor_desc *monitor = &aw_dev->monitor_desc; + struct aw_monitor_cfg *monitor_cfg = &monitor->monitor_cfg; + struct aw_table set_table; + int ret = -1; + + if (aw_cali_svc_get_cali_status()) { + aw_dev_info(aw_dev->dev, "done nothing during calibration"); + return 0; + } + + ret = aw_monitor_get_temp_and_vol(aw_dev); + if (ret < 0) + return ret; + + if (monitor->samp_count < monitor_cfg->monitor_count && + (monitor->first_entry == AW_NOT_FIRST_ENTRY)) + return 0; + + ret = aw_monitor_get_data(aw_dev); + if (ret < 0) + return ret; + + aw_monitor_get_cfg(aw_dev, &set_table); + + aw_dev_dbg(aw_dev->dev, "set_ipeak = 0x%x, set_gain = 0x%x, set_vmax = 0x%x", + set_table.ipeak, set_table.gain, set_table.vmax); + + aw_monitor_set_ipeak(aw_dev, set_table.ipeak); + + aw_monitor_set_gain(aw_dev, set_table.gain); + + aw_monitor_set_vmax(aw_dev, set_table.vmax); + + + monitor->samp_count = 0; + monitor->temp_trace.pre_val = monitor->temp_trace.sum_val; + monitor->temp_trace.sum_val = 0; + + monitor->vol_trace.pre_val = monitor->vol_trace.sum_val; + monitor->vol_trace.sum_val = 0; + + if (monitor->first_entry == AW_FIRST_ENTRY) + monitor->first_entry = AW_NOT_FIRST_ENTRY; + + return 0; +} + +void aw_monitor_work_func(struct work_struct *work) +{ + struct aw_device *aw_dev = container_of(work, + struct aw_device, monitor_desc.delay_work.work); + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + struct aw_monitor_desc *monitor = &aw_dev->monitor_desc; + + aw_dev_dbg(aw_dev->dev, "scene_mode %d, monitor_status:%d, monitor_switch:%d", + aw_dev->cur_prof, monitor_cfg->monitor_status, monitor_cfg->monitor_switch); + + if ((aw_dev->prof_info.prof_desc[aw_dev->cur_prof].id != AW_PROFILE_RECEIVER) && + (monitor_cfg->monitor_status == AW_MON_CFG_OK) && + monitor_cfg->monitor_switch) { + if (!aw_get_hmute(aw_dev)) { + aw_monitor_work(aw_dev); + queue_delayed_work(aw882xx->work_queue, + &monitor->delay_work, msecs_to_jiffies(monitor_cfg->monitor_time)); + } + } +} + +void aw_check_bop_status(struct aw_device *aw_dev) +{ + struct aw_bop_desc *bop_desc = &aw_dev->bop_desc; + unsigned int reg_val = 0; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (aw_dev->bop_desc.reg == AW_REG_NONE) + return; + + aw_dev->ops.aw_i2c_read(aw_dev, bop_desc->reg, ®_val); + reg_val = (uint16_t)reg_val & (~bop_desc->mask); + if (reg_val == bop_desc->enable) + aw_dev->bop_en = AW_BOP_ENABLE; + else + aw_dev->bop_en = AW_BOP_DISABLE; + + aw_dev_dbg(aw_dev->dev, "check done! bop status is %d", aw_dev->bop_en); +} + +void aw_monitor_start(struct aw_monitor_desc *monitor_desc) +{ + struct aw_device *aw_dev = container_of(monitor_desc, + struct aw_device, monitor_desc); + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + aw_dev_info(aw_dev->dev, "enter"); + + monitor_desc->first_entry = AW_FIRST_ENTRY; + monitor_desc->samp_count = 0; + monitor_desc->vol_trace.sum_val = 0; + monitor_desc->temp_trace.sum_val = 0; + + aw_check_bop_status(aw_dev); + + if (aw_dev->bop_en == AW_BOP_ENABLE) { + aw_dev_info(aw_dev->dev, "bop status is enable, monitor can't start"); + return; + } + + queue_delayed_work(aw882xx->work_queue, + &monitor_desc->delay_work, 0); +} + +int aw_monitor_stop(struct aw_monitor_desc *monitor_desc) +{ + struct aw_device *aw_dev = container_of(monitor_desc, + struct aw_device, monitor_desc); + + aw_dev_info(aw_dev->dev, "enter"); + if (delayed_work_pending(&monitor_desc->delay_work)) + cancel_delayed_work_sync(&monitor_desc->delay_work); + + return 0; + +} + +/***************************************************** +* load monitor config +*****************************************************/ +static int aw_monitor_param_check_sum(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len) +{ + int i, check_sum = 0; + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)data; + + if (data_len < sizeof(struct aw_monitor_hdr)) { + aw_dev_err(aw_dev->dev, "data size smaller than hdr , please check monitor bin"); + return -ENOMEM; + } + + for (i = 4; i < data_len; i++) + check_sum += (uint8_t)data[i]; + + if (monitor_hdr->check_sum != check_sum) { + aw_dev_err(aw_dev->dev, "check_sum[%d] is not equal to actual check_sum[%d]", + monitor_hdr->check_sum, check_sum); + return -ENOMEM; + } + + return 0; +} + +static int aw_monitor_check_fw(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)data; + int temp_size, vol_size; + + if (data_len < sizeof(struct aw_monitor_hdr)) { + aw_dev_err(aw_dev->dev, "params size[%d] < struct aw_monitor_hdr size[%d]!", + data_len, (int)sizeof(struct aw_monitor_hdr)); + return -ENOMEM; + } + + if (monitor_hdr->temp_offset > data_len) { + aw_dev_err(aw_dev->dev, "temp_offset[%d] overflow file size[%d]!", + monitor_hdr->temp_offset, data_len); + return -ENOMEM; + } + + if (monitor_hdr->vol_offset > data_len) { + aw_dev_err(aw_dev->dev, "vol_offset[%d] overflow file size[%d]!", + monitor_hdr->vol_offset, data_len); + return -ENOMEM; + } + + temp_size = monitor_hdr->temp_num * monitor_hdr->single_temp_size; + if (temp_size > data_len) { + aw_dev_err(aw_dev->dev, "temp_size:[%d] overflow file size[%d]!", + temp_size, data_len); + return -ENOMEM; + } + + vol_size = monitor_hdr->vol_num * monitor_hdr->single_vol_size; + if (vol_size > data_len) { + aw_dev_err(aw_dev->dev, "vol_size:[%d] overflow file size[%d]!", + vol_size, data_len); + return -ENOMEM; + } + + return 0; +} + +static int aw_monitor_check_fw_v_0_1_1(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)data; + int temp_size, vol_size; + + if (data_len < sizeof(struct aw_monitor_hdr_v_0_1_1)) { + aw_dev_err(aw_dev->dev, "params size[%d] < struct aw_monitor_hdr size[%d]!", + data_len, (int)sizeof(struct aw_monitor_hdr)); + return -ENOMEM; + } + + if (monitor_hdr->temp_offset > data_len) { + aw_dev_err(aw_dev->dev, "temp_offset[%d] overflow file size[%d]!", + monitor_hdr->temp_offset, data_len); + return -ENOMEM; + } + + if (monitor_hdr->vol_offset > data_len) { + aw_dev_err(aw_dev->dev, "vol_offset[%d] overflow file size[%d]!", + monitor_hdr->vol_offset, data_len); + return -ENOMEM; + } + + temp_size = monitor_hdr->temp_num * monitor_hdr->single_temp_size; + if (temp_size > data_len) { + aw_dev_err(aw_dev->dev, "temp_size:[%d] overflow file size[%d]!", + temp_size, data_len); + return -ENOMEM; + } + + vol_size = monitor_hdr->vol_num * monitor_hdr->single_vol_size; + if (vol_size > data_len) { + aw_dev_err(aw_dev->dev, "vol_size:[%d] overflow file size[%d]!", + vol_size, data_len); + return -ENOMEM; + } + + return 0; +} + + +static void aw_monitor_parse_hdr(struct aw_device *aw_dev, uint8_t *data) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)data; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + + monitor_cfg->monitor_switch = monitor_hdr->monitor_switch; + monitor_cfg->monitor_time = monitor_hdr->monitor_time; + monitor_cfg->monitor_count = monitor_hdr->monitor_count; + monitor_cfg->ipeak_switch = monitor_hdr->ipeak_switch; + monitor_cfg->gain_switch = monitor_hdr->gain_switch; + monitor_cfg->vmax_switch = monitor_hdr->vmax_switch; + monitor_cfg->temp_switch = monitor_hdr->temp_switch; + monitor_cfg->temp_aplha = monitor_hdr->temp_aplha; + monitor_cfg->vol_switch = monitor_hdr->vol_switch; + monitor_cfg->vol_aplha = monitor_hdr->vol_aplha; + + aw_dev_info(aw_dev->dev, "chip name:%s", + monitor_hdr->chip_type); + aw_dev_info(aw_dev->dev, "ui ver:0x%x", + monitor_hdr->ui_ver); + + aw_dev_info(aw_dev->dev, "monitor_switch:%d, monitor_time:%d (ms), monitor_count:%d", + monitor_cfg->monitor_switch, monitor_cfg->monitor_time, + monitor_cfg->monitor_count); + + aw_dev_info(aw_dev->dev, "ipeak_switch:%d, gain_switch:%d, vmax_switch:%d", + monitor_cfg->ipeak_switch, monitor_cfg->gain_switch, + monitor_cfg->vmax_switch); + + aw_dev_info(aw_dev->dev, "temp_switch:%d, temp_aplha:%d, vol_switch:%d, vol_aplha:%d", + monitor_cfg->temp_switch, monitor_cfg->temp_aplha, + monitor_cfg->vol_switch, monitor_cfg->vol_aplha); +} + +static void aw_monitor_parse_hdr_v_0_1_1(struct aw_device *aw_dev, uint8_t *data) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)data; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + + monitor_cfg->monitor_switch = (monitor_hdr->enable_flag >> MONITOR_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->monitor_time = monitor_hdr->monitor_time; + monitor_cfg->monitor_count = monitor_hdr->monitor_count; + monitor_cfg->ipeak_switch = (monitor_hdr->enable_flag >> MONITOR_IPEAK_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->logic_switch = (monitor_hdr->enable_flag >> MONITOR_LOGIC_BIT) & MONITOR_EN_MASK; + monitor_cfg->gain_switch = (monitor_hdr->enable_flag >> MONITOR_GAIN_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->vmax_switch = (monitor_hdr->enable_flag >> MONITOR_VMAX_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->temp_switch = (monitor_hdr->enable_flag >> MONITOR_TEMP_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->temp_aplha = monitor_hdr->temp_aplha; + monitor_cfg->vol_switch = (monitor_hdr->enable_flag >> MONITOR_VOL_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->vol_aplha = monitor_hdr->vol_aplha; + + aw_dev_info(aw_dev->dev, "chip name:%s", + monitor_hdr->chip_type); + aw_dev_info(aw_dev->dev, "ui ver:0x%x", + monitor_hdr->ui_ver); + + aw_dev_info(aw_dev->dev, "monitor_switch:%d, monitor_time:%d (ms), monitor_count:%d", + monitor_cfg->monitor_switch, monitor_cfg->monitor_time, + monitor_cfg->monitor_count); + + aw_dev_info(aw_dev->dev, "logic_switch:%d, ipeak_switch:%d, gain_switch:%d, vmax_switch:%d", + monitor_cfg->logic_switch, monitor_cfg->ipeak_switch, + monitor_cfg->gain_switch, monitor_cfg->vmax_switch); + + aw_dev_info(aw_dev->dev, "temp_switch:%d, temp_aplha:%d, vol_switch:%d, vol_aplha:%d", + monitor_cfg->temp_switch, monitor_cfg->temp_aplha, + monitor_cfg->vol_switch, monitor_cfg->vol_aplha); +} + +static void aw_monitor_write_data_to_table(struct aw_device *aw_dev, + struct aw_table_info *table_info, const char *offset_ptr) +{ + int i; + + for (i = 0; i < table_info->table_num * AW_TABLE_SIZE; i += AW_TABLE_SIZE) { + table_info->aw_table[i / AW_TABLE_SIZE].min_val = + AW_GET_16_DATA(offset_ptr[1 + i], offset_ptr[i]); + table_info->aw_table[i / AW_TABLE_SIZE].max_val = + AW_GET_16_DATA(offset_ptr[3 + i], offset_ptr[2 + i]); + table_info->aw_table[i / AW_TABLE_SIZE].ipeak = + AW_GET_16_DATA(offset_ptr[5 + i], offset_ptr[4 + i]); + table_info->aw_table[i / AW_TABLE_SIZE].gain = + AW_GET_16_DATA(offset_ptr[7 + i], offset_ptr[6 + i]); + table_info->aw_table[i / AW_TABLE_SIZE].vmax = + AW_GET_32_DATA(offset_ptr[11 + i], offset_ptr[10 + i], + offset_ptr[9 + i], offset_ptr[8 + i]); + } + + for (i = 0; i < table_info->table_num; i++) + aw_dev_info(aw_dev->dev, + "min_val:%d, max_val:%d, ipeak:0x%x, gain:0x%x, vmax:0x%x", + table_info->aw_table[i].min_val, + table_info->aw_table[i].max_val, + table_info->aw_table[i].ipeak, + table_info->aw_table[i].gain, + table_info->aw_table[i].vmax); + +} + + +static int aw_monitor_parse_temp_data(struct aw_device *aw_dev, uint8_t *data) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)data; + struct aw_table_info *temp_info = + &aw_dev->monitor_desc.monitor_cfg.temp_info; + + aw_dev_info(aw_dev->dev, "===parse temp start ==="); + + if (temp_info->aw_table != NULL) { + devm_kfree(aw_dev->dev, temp_info->aw_table); + temp_info->aw_table = NULL; + } + + temp_info->aw_table = devm_kzalloc(aw_dev->dev, + (monitor_hdr->temp_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (temp_info->aw_table == NULL) + return -ENOMEM; + + temp_info->table_num = monitor_hdr->temp_num; + aw_monitor_write_data_to_table(aw_dev, temp_info, + &data[monitor_hdr->temp_offset]); + aw_dev_info(aw_dev->dev, "===parse temp end ==="); + return 0; +} + +static int aw_monitor_parse_temp_data_v_0_1_1(struct aw_device *aw_dev, uint8_t *data) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)data; + struct aw_table_info *temp_info = + &aw_dev->monitor_desc.monitor_cfg.temp_info; + + aw_dev_info(aw_dev->dev, "===parse temp start ==="); + + if (temp_info->aw_table != NULL) { + devm_kfree(aw_dev->dev, temp_info->aw_table); + temp_info->aw_table = NULL; + } + + temp_info->aw_table = devm_kzalloc(aw_dev->dev, + (monitor_hdr->temp_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (temp_info->aw_table == NULL) + return -ENOMEM; + + temp_info->table_num = monitor_hdr->temp_num; + aw_monitor_write_data_to_table(aw_dev, temp_info, + &data[monitor_hdr->temp_offset]); + aw_dev_info(aw_dev->dev, "===parse temp end ==="); + return 0; +} + + +static int aw_monitor_parse_vol_data(struct aw_device *aw_dev, uint8_t *data) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)data; + struct aw_table_info *vol_info = + &aw_dev->monitor_desc.monitor_cfg.vol_info; + + aw_dev_info(aw_dev->dev, "===parse vol start ==="); + if (vol_info->aw_table != NULL) { + devm_kfree(aw_dev->dev, vol_info->aw_table); + vol_info->aw_table = NULL; + } + + vol_info->aw_table = devm_kzalloc(aw_dev->dev, + (monitor_hdr->vol_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (vol_info->aw_table == NULL) + return -ENOMEM; + + vol_info->table_num = monitor_hdr->vol_num; + aw_monitor_write_data_to_table(aw_dev, vol_info, + &data[monitor_hdr->vol_offset]); + aw_dev_info(aw_dev->dev, "===parse vol end ==="); + return 0; +} + +static int aw_monitor_parse_vol_data_v_0_1_1(struct aw_device *aw_dev, uint8_t *data) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)data; + struct aw_table_info *vol_info = + &aw_dev->monitor_desc.monitor_cfg.vol_info; + + aw_dev_info(aw_dev->dev, "===parse vol start ==="); + if (vol_info->aw_table != NULL) { + devm_kfree(aw_dev->dev, vol_info->aw_table); + vol_info->aw_table = NULL; + } + + vol_info->aw_table = devm_kzalloc(aw_dev->dev, + (monitor_hdr->vol_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (vol_info->aw_table == NULL) + return -ENOMEM; + + vol_info->table_num = monitor_hdr->vol_num; + aw_monitor_write_data_to_table(aw_dev, vol_info, + &data[monitor_hdr->vol_offset]); + aw_dev_info(aw_dev->dev, "===parse vol end ==="); + return 0; +} + + +static int aw_monitor_parse_data(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len) +{ + int ret; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + + ret = aw_monitor_check_fw(aw_dev, data, data_len); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "check monitor failed"); + return ret; + } + + aw_monitor_parse_hdr(aw_dev, data); + + ret = aw_monitor_parse_temp_data(aw_dev, data); + if (ret < 0) + return ret; + + ret = aw_monitor_parse_vol_data(aw_dev, data); + if (ret < 0) { + if (monitor_cfg->temp_info.aw_table != NULL) { + devm_kfree(aw_dev->dev, monitor_cfg->temp_info.aw_table); + monitor_cfg->temp_info.aw_table = NULL; + monitor_cfg->temp_info.table_num = 0; + } + return ret; + } + + monitor_cfg->monitor_status = AW_MON_CFG_OK; + return 0; +} + + +static int aw_monitor_parse_data_v_0_1_1(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len) +{ + int ret; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + + ret = aw_monitor_check_fw_v_0_1_1(aw_dev, data, data_len); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "check monitor failed"); + return ret; + } + + aw_monitor_parse_hdr_v_0_1_1(aw_dev, data); + + ret = aw_monitor_parse_temp_data_v_0_1_1(aw_dev, data); + if (ret < 0) + return ret; + + ret = aw_monitor_parse_vol_data_v_0_1_1(aw_dev, data); + if (ret < 0) { + if (monitor_cfg->temp_info.aw_table != NULL) { + devm_kfree(aw_dev->dev, monitor_cfg->temp_info.aw_table); + monitor_cfg->temp_info.aw_table = NULL; + monitor_cfg->temp_info.table_num = 0; + } + return ret; + } + + monitor_cfg->monitor_status = AW_MON_CFG_OK; + return 0; +} + + +int aw_monitor_parse_fw(struct aw_monitor_desc *monitor_desc, + uint8_t *data, uint32_t data_len) +{ + struct aw_monitor_hdr *monitor_hdr = NULL; + struct aw_device *aw_dev = NULL; + int ret; + + if (monitor_desc == NULL || data == NULL) { + pr_err("monitor_desc or data is NULL"); + return -EINVAL; + } + + monitor_hdr = (struct aw_monitor_hdr *)data; + aw_dev = container_of(monitor_desc, + struct aw_device, monitor_desc); + + ret = aw_monitor_param_check_sum(aw_dev, data, data_len); + if (ret < 0) + return ret; + + switch (monitor_hdr->monitor_ver) { + case AW_MONITOR_HDR_VER_0_1_0: + return aw_monitor_parse_data(aw_dev, data, data_len); + case AW_MONITOR_HDR_VER_0_1_1: + return aw_monitor_parse_data_v_0_1_1(aw_dev, data, data_len); + default: + aw_dev_err(aw_dev->dev, "cfg version:0x%x unsupported", + monitor_hdr->monitor_ver); + return -EINVAL; + } +} + +static void aw_monitor_free_firmware(struct aw_device *aw_dev) +{ + struct aw_monitor_cfg *monitor_cfg = + &aw_dev->monitor_desc.monitor_cfg; + + monitor_cfg->monitor_status = AW_MON_CFG_ST; + + if (monitor_cfg->temp_info.aw_table != NULL) { + devm_kfree(aw_dev->dev, monitor_cfg->temp_info.aw_table); + monitor_cfg->temp_info.aw_table = NULL; + } + + if (monitor_cfg->vol_info.aw_table != NULL) { + devm_kfree(aw_dev->dev, monitor_cfg->vol_info.aw_table); + monitor_cfg->vol_info.aw_table = NULL; + } + + memset(monitor_cfg, 0, sizeof(struct aw_monitor_cfg)); +} + +static int aw_monitor_real_time_update_monitor(struct aw_device *aw_dev) +{ + const struct firmware *cont = NULL; + struct aw_container *aw_monitor_cnt = NULL; + int ret; + + ret = request_firmware(&cont, aw_dev->monitor_name, aw_dev->dev); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "failed to read %s", aw_dev->monitor_name); + release_firmware(cont); + return ret; + } + + aw_monitor_cnt = devm_kzalloc(aw_dev->dev, + cont->size + sizeof(uint32_t), GFP_KERNEL); + if (aw_monitor_cnt == NULL) { + aw_dev_err(aw_dev->dev, "alloc failed!"); + release_firmware(cont); + return ret; + } + + aw_monitor_cnt->len = cont->size; + memcpy(aw_monitor_cnt->data, cont->data, cont->size); + release_firmware(cont); + + ret = aw_monitor_parse_fw(&aw_dev->monitor_desc, + aw_monitor_cnt->data, aw_monitor_cnt->len); + if (ret < 0) + aw_dev_err(aw_dev->dev, "parse monitor firmware failed!"); + + devm_kfree(aw_dev->dev, aw_monitor_cnt); + aw_monitor_cnt = NULL; + + return ret; +} + +/***************************************************** + * monitor init + *****************************************************/ +#ifdef AW_DEBUG +static ssize_t aw_vol_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + uint32_t vol = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &vol); + if (ret < 0) + return ret; + + aw_dev_info(aw_dev->dev, "vol set =%d", vol); + aw_dev->monitor_desc.test_vol = vol; + + return count; +} + +static ssize_t aw_vol_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "vol: %d\n", + aw_dev->monitor_desc.test_vol); + return len; +} + +static ssize_t aw_temp_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + int32_t temp = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtoint(buf, 0, &temp); + if (ret < 0) + return ret; + + aw_dev_info(aw_dev->dev, "temp set =%d", temp); + + aw_dev->monitor_desc.test_temp = temp; + + return count; +} + +static ssize_t aw_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx temp: %d\n", + aw_dev->monitor_desc.test_temp); + + return len; +} + +static DEVICE_ATTR(vol, S_IWUSR | S_IRUGO, + aw_vol_show, aw_vol_store); +static DEVICE_ATTR(temp, S_IWUSR | S_IRUGO, + aw_temp_show, aw_temp_store); +#endif + +static ssize_t aw_monitor_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + uint32_t enable = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &enable); + if (ret < 0) + return ret; + + aw_dev_info(aw_dev->dev, "monitor enable set =%d", enable); + + if (aw_dev->monitor_desc.monitor_cfg.monitor_switch == enable) { + return count; + } else { + aw_dev->monitor_desc.monitor_cfg.monitor_switch = enable; + if (enable) + aw_monitor_start(&aw_dev->monitor_desc); + } + + return count; +} + +static ssize_t aw_monitor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + + + len += snprintf(buf+len, PAGE_SIZE-len, + "monitor enable: %d\n", + aw_dev->monitor_desc.monitor_cfg.monitor_switch); + return len; +} + +static ssize_t aw_monitor_update_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + uint32_t update = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &update); + if (ret < 0) + return ret; + + aw_dev_info(aw_dev->dev, "monitor update = %d", update); + + if (update) { + aw_monitor_stop(&aw_dev->monitor_desc); + aw_monitor_free_firmware(aw_dev); + ret = aw_monitor_real_time_update_monitor(aw_dev); + if (ret < 0) + return ret; + aw_monitor_start(&aw_dev->monitor_desc); + } + + return count; +} + +static DEVICE_ATTR(monitor, S_IWUSR | S_IRUGO, + aw_monitor_show, aw_monitor_store); +static DEVICE_ATTR(monitor_update, S_IWUSR, + NULL, aw_monitor_update_store); + + +static struct attribute *aw_monitor_attr[] = { + &dev_attr_monitor.attr, + &dev_attr_monitor_update.attr, +#ifdef AW_DEBUG + &dev_attr_vol.attr, + &dev_attr_temp.attr, +#endif + NULL +}; + +static struct attribute_group aw_monitor_attr_group = { + .attrs = aw_monitor_attr, +}; + +void aw_monitor_init(struct aw_monitor_desc *monitor_desc) +{ + int ret; + struct aw_device *aw_dev = container_of(monitor_desc, + struct aw_device, monitor_desc); + + aw_dev_info(aw_dev->dev, "enter"); +#ifdef AW_DEBUG + monitor_desc->test_vol = 0; + monitor_desc->test_temp = 0; +#endif + + INIT_DELAYED_WORK(&monitor_desc->delay_work, aw_monitor_work_func); + + ret = sysfs_create_group(&aw_dev->dev->kobj, + &aw_monitor_attr_group); + if (ret < 0) + aw_dev_err(aw_dev->dev, "error creating sysfs attr files"); +} + +void aw_monitor_deinit(struct aw_monitor_desc *monitor_desc) +{ + struct aw_device *aw_dev = + container_of(monitor_desc, struct aw_device, monitor_desc); + + aw_monitor_stop(monitor_desc); + + sysfs_remove_group(&aw_dev->dev->kobj, &aw_monitor_attr_group); +} + diff --git a/asoc/codecs/aw88261/aw_monitor.h b/asoc/codecs/aw88261/aw_monitor.h new file mode 100644 index 000000000000..ef42608b212f --- /dev/null +++ b/asoc/codecs/aw88261/aw_monitor.h @@ -0,0 +1,173 @@ +#ifndef __AW_MONITOR_H__ +#define __AW_MONITOR_H__ + +/*#define AW_DEBUG*/ + +struct aw_table; + +#define AW_TABLE_SIZE sizeof(struct aw_table) +#define AW_MONITOR_DEFAULT_FLAG (0) + +#define IPEAK_NONE (0xFF) +#define GAIN_NONE (0xFF) +#define VMAX_NONE (0xFFFFFFFF) + + +#define AW_GET_32_DATA(w, x, y, z) \ + ((uint32_t)((((uint8_t)w) << 24) | (((uint8_t)x) << 16) | (((uint8_t)y) << 8) | ((uint8_t)z))) +#define AW_GET_16_DATA(x, y) \ + ((uint16_t)((((uint8_t)x) << 8) | (uint8_t)y)) + +enum { + AW_MON_LOGIC_OR = 0, + AW_MON_LOGIC_AND = 1, +}; + +enum { + AW_FIRST_ENTRY = 0, + AW_NOT_FIRST_ENTRY = 1, +}; + +enum aw_monitor_hdr_ver { + AW_MONITOR_HDR_VER_0_1_0 = 0x00010000, + AW_MONITOR_HDR_VER_0_1_1 = 0x00010100, +}; + +enum aw_monitor_init { + AW_MON_CFG_ST = 0, + AW_MON_CFG_OK = 1, +}; + +struct aw_monitor_hdr { + uint32_t check_sum; + uint32_t monitor_ver; + char chip_type[8]; + uint32_t ui_ver; + uint32_t monitor_switch; + uint32_t monitor_time; + uint32_t monitor_count; + uint32_t ipeak_switch; + uint32_t gain_switch; + uint32_t vmax_switch; + uint32_t temp_switch; + uint32_t temp_aplha; + uint32_t temp_num; + uint32_t single_temp_size; + uint32_t temp_offset; + uint32_t vol_switch; + uint32_t vol_aplha; + uint32_t vol_num; + uint32_t single_vol_size; + uint32_t vol_offset; +}; + +#define MONITOR_EN_MASK (0x01) + +enum { + MONITOR_EN_BIT = 0, + MONITOR_LOGIC_BIT = 1, + MONITOR_IPEAK_EN_BIT = 2, + MONITOR_GAIN_EN_BIT = 3, + MONITOR_VMAX_EN_BIT = 4, + MONITOR_TEMP_EN_BIT = 5, + MONITOR_VOL_EN_BIT = 6, +}; + +struct aw_monitor_hdr_v_0_1_1 { + uint32_t check_sum; + uint32_t monitor_ver; + char chip_type[16]; + uint32_t ui_ver; + uint32_t monitor_time; + uint32_t monitor_count; + uint32_t enable_flag; + /* [bit 31:7] */ + /* [bit 6: vol en] */ + /* [bit 5: temp en] */ + /* [bit 4: vmax en] */ + /* [bit 3: gain en] */ + /* [bit 2: ipeak en] */ + /* [bit 1: & or | flag] */ + /* [bit 0: monitor en] */ + uint32_t temp_aplha; + uint32_t temp_num; + uint32_t single_temp_size; + uint32_t temp_offset; + uint32_t vol_aplha; + uint32_t vol_num; + uint32_t single_vol_size; + uint32_t vol_offset; + uint32_t reserver[3]; +}; + + +struct aw_table { + int16_t min_val; + int16_t max_val; + uint16_t ipeak; + uint16_t gain; + uint32_t vmax; +}; + +struct aw_table_info { + uint8_t table_num; + struct aw_table *aw_table; +}; + +struct aw_monitor_cfg { + uint8_t monitor_status; + uint32_t monitor_switch; + uint32_t monitor_time; + uint32_t monitor_count; + uint32_t logic_switch; + uint32_t temp_switch; + uint32_t temp_aplha; + uint32_t vol_switch; + uint32_t vol_aplha; + uint32_t ipeak_switch; + uint32_t gain_switch; + uint32_t vmax_switch; + struct aw_table_info temp_info; + struct aw_table_info vol_info; +}; + +struct aw_monitor_trace { + int32_t pre_val; + int32_t sum_val; + struct aw_table aw_table; +}; + + +/****************************************************************** +* struct aw882xx monitor +*******************************************************************/ +struct aw_monitor_desc { + struct delayed_work delay_work; + struct aw_monitor_cfg monitor_cfg; + + uint8_t first_entry; + uint8_t samp_count; + uint8_t db_offset; + + uint32_t pre_vmax; + struct aw_monitor_trace temp_trace; + struct aw_monitor_trace vol_trace; + +#ifdef AW_DEBUG + uint16_t test_vol; + int16_t test_temp; +#endif +}; + +/****************************************************************** +* aw882xx monitor functions +*******************************************************************/ +void aw_monitor_start(struct aw_monitor_desc *monitor_desc); +int aw_monitor_stop(struct aw_monitor_desc *monitor_desc); +void aw_monitor_init(struct aw_monitor_desc *monitor_desc); +void aw_monitor_deinit(struct aw_monitor_desc *monitor_desc); +int aw_monitor_parse_fw(struct aw_monitor_desc *monitor_desc, + uint8_t *data, uint32_t data_len); + +#endif + diff --git a/asoc/codecs/aw88261/aw_pid_1852_reg.h b/asoc/codecs/aw88261/aw_pid_1852_reg.h new file mode 100644 index 000000000000..6f43a1e2693a --- /dev/null +++ b/asoc/codecs/aw88261/aw_pid_1852_reg.h @@ -0,0 +1,1899 @@ +#ifndef __AW_PID_1852_REG_H__ +#define __AW_PID_1852_REG_H__ + +#define AW_PID_1852_ACF_FILE "aw882xx_pid_1852_acf.bin" +#define AW_PID_1852_MONITOR_FILE "aw882xx_pid_1852_monitor.bin" + +/* registers list */ +#define AW_PID_1852_ID_REG (0x00) +#define AW_PID_1852_SYSST_REG (0x01) +#define AW_PID_1852_SYSINT_REG (0x02) +#define AW_PID_1852_SYSINTM_REG (0x03) +#define AW_PID_1852_SYSCTRL_REG (0x04) +#define AW_PID_1852_SYSCTRL2_REG (0x05) +#define AW_PID_1852_I2SCTRL_REG (0x06) +#define AW_PID_1852_I2SCFG1_REG (0x07) +#define AW_PID_1852_I2SCFG2_REG (0x08) +#define AW_PID_1852_HAGCCFG1_REG (0x09) +#define AW_PID_1852_HAGCCFG2_REG (0x0A) +#define AW_PID_1852_HAGCCFG3_REG (0x0B) +#define AW_PID_1852_HAGCCFG4_REG (0x0C) +#define AW_PID_1852_HAGCCFG5_REG (0x0D) +#define AW_PID_1852_HAGCCFG6_REG (0x0E) +#define AW_PID_1852_HAGCCFG7_REG (0x0F) +#define AW_PID_1852_HAGCST_REG (0x10) +#define AW_PID_1852_PRODID_REG (0x11) +#define AW_PID_1852_VBAT_REG (0x12) +#define AW_PID_1852_TEMP_REG (0x13) +#define AW_PID_1852_PVDD_REG (0x14) +#define AW_PID_1852_DBGCTRL_REG (0x20) +#define AW_PID_1852_I2SINT_REG (0x21) +#define AW_PID_1852_I2SCAPCNT_REG (0x22) +#define AW_PID_1852_CRCIN_REG (0x38) +#define AW_PID_1852_CRCOUT_REG (0x39) +#define AW_PID_1852_VSNCTRL1_REG (0x50) +#define AW_PID_1852_ISNCTRL1_REG (0x52) +#define AW_PID_1852_ISNCTRL2_REG (0x53) +#define AW_PID_1852_VTMCTRL1_REG (0x54) +#define AW_PID_1852_VTMCTRL2_REG (0x55) +#define AW_PID_1852_VTMCTRL3_REG (0x56) +#define AW_PID_1852_ISNDAT_REG (0x57) +#define AW_PID_1852_VSNDAT_REG (0x58) +#define AW_PID_1852_PWMCTRL_REG (0x59) +#define AW_PID_1852_PWMCTRL2_REG (0x5A) +#define AW_PID_1852_BSTCTRL1_REG (0x60) +#define AW_PID_1852_BSTCTRL2_REG (0x61) +#define AW_PID_1852_BSTCTRL3_REG (0x62) +#define AW_PID_1852_BSTDBG1_REG (0x63) +#define AW_PID_1852_BSTDBG2_REG (0x64) +#define AW_PID_1852_BSTDBG3_REG (0x65) +#define AW_PID_1852_PLLCTRL1_REG (0x66) +#define AW_PID_1852_PLLCTRL2_REG (0x67) +#define AW_PID_1852_PLLCTRL3_REG (0x68) +#define AW_PID_1852_CDACTRL1_REG (0x69) +#define AW_PID_1852_CDACTRL2_REG (0x6A) +#define AW_PID_1852_SADCCTRL_REG (0x6B) +#define AW_PID_1852_TESTCTRL1_REG (0x70) +#define AW_PID_1852_TESTCTRL2_REG (0x71) +#define AW_PID_1852_EFCTRL1_REG (0x72) +#define AW_PID_1852_EFCTRL2_REG (0x73) +#define AW_PID_1852_EFWH_REG (0x74) +#define AW_PID_1852_EFWM2_REG (0x75) +#define AW_PID_1852_EFWM1_REG (0x76) +#define AW_PID_1852_EFWL_REG (0x77) +#define AW_PID_1852_EFRH_REG (0x78) +#define AW_PID_1852_EFRM2_REG (0x79) +#define AW_PID_1852_EFRM1_REG (0x7A) +#define AW_PID_1852_EFRL_REG (0x7B) +#define AW_PID_1852_TESTDET_REG (0x7C) + +/******************************************** + * Register Access + *******************************************/ +#define AW_PID_1852_REG_MAX (0x7D) + + +#define AW_PID_1852_REG_NONE_ACCESS (0) +#define AW_PID_1852_REG_RD_ACCESS (1 << 0) +#define AW_PID_1852_REG_WR_ACCESS (1 << 1) + + +static const unsigned char aw_pid_1852_reg_access[AW_PID_1852_REG_MAX] = { + [AW_PID_1852_ID_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_SYSST_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_SYSINT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_SYSINTM_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_SYSCTRL_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_SYSCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_I2SCTRL_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_I2SCFG1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_I2SCFG2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG3_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG4_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG5_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG6_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG7_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCST_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_PRODID_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_VBAT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_TEMP_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_PVDD_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_DBGCTRL_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_I2SINT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_I2SCAPCNT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_CRCIN_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_CRCOUT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_VSNCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_ISNCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_ISNCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_VTMCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_VTMCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_VTMCTRL3_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_ISNDAT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_VSNDAT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_PWMCTRL_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_PWMCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_BSTCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_BSTCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_BSTCTRL3_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_BSTDBG1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_BSTDBG2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_BSTDBG3_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_PLLCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_PLLCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_PLLCTRL3_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_CDACTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_CDACTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_SADCCTRL_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_TESTCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_TESTCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFWH_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFWM2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFWM1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFWL_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFRH_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_EFRM2_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_EFRM1_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_EFRL_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_TESTDET_REG] = (AW_PID_1852_REG_RD_ACCESS), +}; + +/* detail information of registers begin */ +/* ID (0x00) detail */ +/* IDCODE bit 15:0 (ID 0x00) */ +#define AW_PID_1852_IDCODE_START_BIT (0) +#define AW_PID_1852_IDCODE_BITS_LEN (16) +#define AW_PID_1852_IDCODE_MASK \ + (~(((1< Date: Thu, 4 Nov 2021 16:14:09 +0800 Subject: [PATCH 08/40] asoc:init rhode audio Change-Id: Ia605774ce9438516dea037f2e2538e3b8037560c Signed-off-by: yaozm1 Reviewed-on: https://gerrit.mot.com/2107467 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/Kbuild | 4 +++ asoc/bengal.c | 58 +++++++++++++++++++++++++++++++++++++++++- asoc/codecs/Kbuild | 3 +++ config/rhodeautoconf.h | 4 +++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 config/rhodeautoconf.h diff --git a/asoc/Kbuild b/asoc/Kbuild index e718931c1e8f..28f5d7d2266f 100644 --- a/asoc/Kbuild +++ b/asoc/Kbuild @@ -96,6 +96,10 @@ ifeq ($(KERNEL_BUILD), 0) export INCS += -include $(AUDIO_ROOT)/config/sdm450autoconf.h endif + ifneq (,$(filter rhode%, $(TARGET_PRODUCT))) + INCS += -include $(AUDIO_ROOT)/config/rhodeautoconf.h + endif + endif # As per target team, build is done as follows: diff --git a/asoc/bengal.c b/asoc/bengal.c index 99733bed120e..e45f17c3dbe2 100644 --- a/asoc/bengal.c +++ b/asoc/bengal.c @@ -5432,6 +5432,51 @@ static struct snd_soc_dai_link msm_awinic_be_dai_links[] = { .ignore_suspend = 1, }, }; + +static struct snd_soc_dai_link_component awinic_codecs[] = { + { + .of_node = NULL, + .dai_name = "aw882xx-aif-0-34", + .name = "aw882xxacf_smartpa.0-0034", + }, + { + .of_node = NULL, + .dai_name = "aw882xx-aif-0-37", + .name = "aw882xxacf_smartpa.0-0037", + }, +}; + +static struct snd_soc_dai_link msm_stereo_awinic_be_dai_links[] = { + { + .name = LPASS_BE_PRI_MI2S_RX, + .stream_name = "Primary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .num_codecs = ARRAY_SIZE(awinic_codecs), + .codecs = awinic_codecs, + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = LPASS_BE_PRI_MI2S_TX, + .stream_name = "Primary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.0", + .platform_name = "msm-pcm-routing", + .num_codecs = ARRAY_SIZE(awinic_codecs), + .codecs = awinic_codecs, + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_mi2s_be_ops, + .ignore_suspend = 1, + }, +}; #endif #if !defined(CONFIG_SND_SMARTPA_AW882XX) @@ -6266,7 +6311,7 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) __func__); } else { dev_dbg(dev,"%s,aw,has-882xx=%d\n",__func__,has_awinic_pa); - if (has_awinic_pa) { + if (has_awinic_pa == 1) { memcpy(msm_bengal_dai_links + total_links, msm_awinic_fe_dai_links, sizeof(msm_awinic_fe_dai_links)); @@ -6277,6 +6322,17 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) sizeof(msm_awinic_be_dai_links)); total_links += ARRAY_SIZE(msm_awinic_be_dai_links); + } else if (has_awinic_pa == 2) { + memcpy(msm_bengal_dai_links + total_links, + msm_awinic_fe_dai_links, + sizeof(msm_awinic_fe_dai_links)); + total_links += + ARRAY_SIZE(msm_awinic_fe_dai_links); + memcpy(msm_bengal_dai_links + total_links, + msm_stereo_awinic_be_dai_links, + sizeof(msm_stereo_awinic_be_dai_links)); + total_links += + ARRAY_SIZE(msm_stereo_awinic_be_dai_links); } } #endif diff --git a/asoc/codecs/Kbuild b/asoc/codecs/Kbuild index 6ef33da1c951..fffbc368f46b 100644 --- a/asoc/codecs/Kbuild +++ b/asoc/codecs/Kbuild @@ -90,6 +90,9 @@ ifeq ($(KERNEL_BUILD), 0) export INCS += -include $(AUDIO_ROOT)/config/sdm450autoconf.h endif + ifneq (,$(filter rhode%, $(TARGET_PRODUCT))) + INCS += -include $(AUDIO_ROOT)/config/rhodeautoconf.h + endif endif # As per target team, build is done as follows: diff --git a/config/rhodeautoconf.h b/config/rhodeautoconf.h new file mode 100644 index 000000000000..84c56f585f7e --- /dev/null +++ b/config/rhodeautoconf.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + */ +#define CONFIG_AW882XX_STEREO_SMARTPA 1 From 50680956a7ec026bdf25a28630e922f7f48f5dd6 Mon Sep 17 00:00:00 2001 From: yaozm1 Date: Fri, 5 Nov 2021 17:59:06 +0800 Subject: [PATCH 09/40] mbhc:disable moisture detect for rhode update mbhc for rhode; disable moisture detect. Change-Id: I9f563b4007c9e9fc9064856bcf27e9c97c174be9 Signed-off-by: yaozm1 Reviewed-on: https://gerrit.mot.com/2108749 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/wcd-mbhc-v2.c | 4 +++- asoc/codecs/wcd937x/Kbuild | 3 +++ asoc/codecs/wcd937x/wcd937x-mbhc.c | 6 ++++++ config/rhodeautoconf.h | 1 + 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/asoc/codecs/wcd-mbhc-v2.c b/asoc/codecs/wcd-mbhc-v2.c index 55cc0a37bd40..bdd6e0d8404a 100644 --- a/asoc/codecs/wcd-mbhc-v2.c +++ b/asoc/codecs/wcd-mbhc-v2.c @@ -943,8 +943,10 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc) /* If moisture is present, then enable polling, disable * moisture detection and wait for interrupt */ - if (wcd_mbhc_moisture_detect(mbhc, detection_type)) + if (wcd_mbhc_moisture_detect(mbhc, detection_type)){ + pr_info("%s: hwcd_mbhc_moisture_detect\n", __func__); goto done; + } /* Make sure MASTER_BIAS_CTL is enabled */ mbhc->mbhc_cb->mbhc_bias(component, true); diff --git a/asoc/codecs/wcd937x/Kbuild b/asoc/codecs/wcd937x/Kbuild index 5bbbad7cbdf5..775568186f04 100644 --- a/asoc/codecs/wcd937x/Kbuild +++ b/asoc/codecs/wcd937x/Kbuild @@ -36,6 +36,9 @@ ifeq ($(KERNEL_BUILD), 0) export INCS += -include $(AUDIO_ROOT)/config/litoautoconf.h endif + ifneq (,$(filter rhode%, $(TARGET_PRODUCT))) + INCS += -include $(AUDIO_ROOT)/config/rhodeautoconf.h + endif endif # As per target team, build is done as follows: diff --git a/asoc/codecs/wcd937x/wcd937x-mbhc.c b/asoc/codecs/wcd937x/wcd937x-mbhc.c index 039f5118bed1..5f2c5a21995f 100644 --- a/asoc/codecs/wcd937x/wcd937x-mbhc.c +++ b/asoc/codecs/wcd937x/wcd937x-mbhc.c @@ -767,6 +767,12 @@ static bool wcd937x_mbhc_get_moisture_status(struct wcd_mbhc *mbhc) struct snd_soc_component *component = mbhc->component; bool ret = false; +#ifdef CONFIG_MBHC_DISABLE_MOISTURE_DETECT + pr_info("%s: disable moisture detection \n", __func__); + snd_soc_component_update_bits(component, WCD937X_MBHC_NEW_CTL_2, 0x0C, R_OFF << 2); + goto done; +#endif + if ((mbhc->moist_rref == R_OFF) || (mbhc->mbhc_cfg->enable_usbc_analog)) { snd_soc_component_update_bits(component, WCD937X_MBHC_NEW_CTL_2, diff --git a/config/rhodeautoconf.h b/config/rhodeautoconf.h index 84c56f585f7e..d1de799450f8 100644 --- a/config/rhodeautoconf.h +++ b/config/rhodeautoconf.h @@ -2,3 +2,4 @@ /* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */ #define CONFIG_AW882XX_STEREO_SMARTPA 1 +#define CONFIG_MBHC_DISABLE_MOISTURE_DETECT 1 From 7a40ed55b77b3756156015381929c97e07be71c2 Mon Sep 17 00:00:00 2001 From: yaozm1 Date: Thu, 11 Nov 2021 16:54:43 +0800 Subject: [PATCH 10/40] asoc:update audio config for rhode update awinic codec config; for rhode evt. Change-Id: I8ab0f2b1239c74510894463fc46607997cfaee17 Signed-off-by: yaozm1 Reviewed-on: https://gerrit.mot.com/2113385 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/bengal.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/asoc/bengal.c b/asoc/bengal.c index e45f17c3dbe2..28311f423717 100644 --- a/asoc/bengal.c +++ b/asoc/bengal.c @@ -5436,13 +5436,13 @@ static struct snd_soc_dai_link msm_awinic_be_dai_links[] = { static struct snd_soc_dai_link_component awinic_codecs[] = { { .of_node = NULL, - .dai_name = "aw882xx-aif-0-34", - .name = "aw882xxacf_smartpa.0-0034", + .dai_name = "aw882xx-aif-1-34", + .name = "aw882xxacf_smartpa.1-0034", }, { .of_node = NULL, - .dai_name = "aw882xx-aif-0-37", - .name = "aw882xxacf_smartpa.0-0037", + .dai_name = "aw882xx-aif-1-37", + .name = "aw882xxacf_smartpa.1-0037", }, }; From 249139754c68c1fe7e04f520f8e88d2b316dc662 Mon Sep 17 00:00:00 2001 From: huaqin95 Date: Tue, 23 Nov 2021 17:29:12 +0800 Subject: [PATCH 11/40] rhode: audio: enable aw882xx algo param store in aw_skt.bin update the lib to store the params to aw_skt.bin, enable algo_prof switch. Change-Id: I34e10cc27d60f6fd2bb40500f90e916f93de7e5f Signed-off-by: huaqin95 Reviewed-on: https://gerrit.mot.com/2124816 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: wangzhe Reviewed-by: Zhengming Yao Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- config/rhodeautoconf.h | 1 + dsp/Kbuild | 3 +++ dsp/q6afe.c | 3 +++ 3 files changed, 7 insertions(+) diff --git a/config/rhodeautoconf.h b/config/rhodeautoconf.h index d1de799450f8..f0193a3f0288 100644 --- a/config/rhodeautoconf.h +++ b/config/rhodeautoconf.h @@ -3,3 +3,4 @@ */ #define CONFIG_AW882XX_STEREO_SMARTPA 1 #define CONFIG_MBHC_DISABLE_MOISTURE_DETECT 1 +#define CONFIG_AW882XX_PARAMS_STORED_IN_BIN 1 diff --git a/dsp/Kbuild b/dsp/Kbuild index dd3c886ec647..41f6d4951b9d 100644 --- a/dsp/Kbuild +++ b/dsp/Kbuild @@ -96,6 +96,9 @@ ifeq ($(KERNEL_BUILD), 0) export INCS += -include $(AUDIO_ROOT)/config/sdm450autoconf.h endif + ifneq (,$(filter rhode%, $(TARGET_PRODUCT))) + INCS += -include $(AUDIO_ROOT)/config/rhodeautoconf.h + endif endif diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 7bee21008db5..ca381c51c5e1 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -11031,6 +11031,9 @@ static int afe_unmap_cal_data(int32_t cal_type, ret = afe_cmd_memory_unmap_nowait( cal_block->map_data.q6map_handle); atomic_set(&this_afe.mem_map_cal_index, -1); +#ifdef CONFIG_AW882XX_PARAMS_STORED_IN_BIN + atomic_set(&this_afe.mem_map_cal_handles[cal_index],0); +#endif if (ret < 0) { pr_err("%s: unmap did not work! cal_type %i ret %d\n", __func__, cal_index, ret); From 51299445860a284593f45d89bdc6d394cf4eb8e0 Mon Sep 17 00:00:00 2001 From: huaqin95 Date: Thu, 25 Nov 2021 17:21:49 +0800 Subject: [PATCH 12/40] rhode: audio: update awinic smartpa driver to V1.8.0.1 update awinic smartpa driver to V1.8.0.1 test playback, smartpa cali, cqa test pass Change-Id: I95d757065de1de1e5b6ef50ba55cea79c8883b84 Signed-off-by: huaqin95 Reviewed-on: https://gerrit.mot.com/2127502 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Zhengming Yao Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/aw88261/aw882xx.c | 13 +- asoc/codecs/aw88261/aw_calib.c | 56 ++++++-- asoc/codecs/aw88261/aw_calib.h | 3 +- asoc/codecs/aw88261/aw_device.c | 2 +- asoc/codecs/aw88261/aw_dsp.c | 182 ++++++-------------------- asoc/codecs/aw88261/aw_init.c | 11 ++ asoc/codecs/aw88261/aw_pid_2055_reg.h | 6 + asoc/codecs/aw88261/aw_pid_2113_reg.h | 6 + 8 files changed, 118 insertions(+), 161 deletions(-) diff --git a/asoc/codecs/aw88261/aw882xx.c b/asoc/codecs/aw88261/aw882xx.c index bfaf9c597ece..74ee9c6e2492 100644 --- a/asoc/codecs/aw88261/aw882xx.c +++ b/asoc/codecs/aw88261/aw882xx.c @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.6.0" +#define AW882XX_DRIVER_VERSION "v1.8.0.1" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ @@ -882,6 +882,11 @@ static int aw882xx_get_rx_en(struct snd_kcontrol *kcontrol, struct aw882xx *aw882xx = aw_componet_codec_ops.codec_get_drvdata(codec); + /*ret = aw_dev_get_afe_module_en(AW_RX_MODULE, &ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + } + ucontrol->value.integer.value[0] = ctrl_value;*/ if (aw882xx->pstream) { ret = aw_dev_get_afe_module_en(AW_RX_MODULE, &ctrl_value); @@ -937,7 +942,6 @@ static int aw882xx_get_tx_en(struct snd_kcontrol *kcontrol, /*ret = aw_dev_get_afe_module_en(AW_TX_MODULE, &ctrl_value); if (ret) { aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); - ctrl_value = 0; } ucontrol->value.integer.value[0] = ctrl_value;*/ @@ -2444,7 +2448,7 @@ static const struct i2c_device_id aw882xx_i2c_id[] = { MODULE_DEVICE_TABLE(i2c, aw882xx_i2c_id); static struct of_device_id aw882xx_dt_match[] = { - { .compatible = "awinic,aw882xx_smartpa" }, + { .compatible = "awinic,aw882xxacf_smartpa" }, { }, }; @@ -2485,6 +2489,3 @@ module_exit(aw882xx_i2c_exit); MODULE_DESCRIPTION("ASoC AW882XX Smart PA Driver"); MODULE_LICENSE("GPL v2"); - - - diff --git a/asoc/codecs/aw88261/aw_calib.c b/asoc/codecs/aw88261/aw_calib.c index 674150422c12..42fb249a8a28 100644 --- a/asoc/codecs/aw88261/aw_calib.c +++ b/asoc/codecs/aw88261/aw_calib.c @@ -47,17 +47,35 @@ static unsigned int g_cali_status = false; static struct miscdevice *g_misc_dev = NULL; static DEFINE_MUTEX(g_cali_lock); - #ifdef AW_CALI_STORE_EXAMPLE /*write cali to persist file example*/ #define AWINIC_CALI_FILE "/mnt/vendor/persist/factory/audio/aw_cali.bin" #define AW_INT_DEC_DIGIT 10 +static void aw_fs_read(struct file *file, char *buf, size_t count, loff_t *pos) +{ +#ifdef AW_KERNEL_VER_OVER_5_4_0 + kernel_read(file, buf, count, pos); +#else + vfs_read(file, buf, count, pos); +#endif +} + +static void aw_fs_write(struct file *file, char *buf, size_t count, loff_t *pos) +{ +#ifdef AW_KERNEL_VER_OVER_5_4_0 + kernel_write(file, buf, count, pos); +#else + vfs_write(file, buf, count, pos); +#endif +} + static int aw_cali_write_cali_re_to_file(int32_t cali_re, int channel) { struct file *fp = NULL; char buf[50] = {0}; loff_t pos = 0; + mm_segment_t fs; fp = filp_open(AWINIC_CALI_FILE, O_RDWR | O_CREAT, 0644); if (IS_ERR(fp)) { @@ -70,7 +88,12 @@ static int aw_cali_write_cali_re_to_file(int32_t cali_re, int channel) snprintf(buf, sizeof(buf), "%10d", cali_re); - kernel_write(fp, buf, strlen(buf), &pos); + fs = get_fs(); + set_fs(KERNEL_DS); + + aw_fs_write(fp, buf, strlen(buf), &pos); + + set_fs(fs); pr_info("%s: channel:%d buf:%s cali_re:%d\n", __func__, channel, buf, cali_re); @@ -87,6 +110,7 @@ static int aw_cali_get_read_cali_re(int32_t *cali_re, int channel) char *buf = NULL; int32_t int_cali_re = 0; loff_t pos = 0; + mm_segment_t fs; fp = filp_open(AWINIC_CALI_FILE, O_RDONLY, 0); if (IS_ERR(fp)) { @@ -109,8 +133,12 @@ static int aw_cali_get_read_cali_re(int32_t *cali_re, int channel) return -EINVAL; } - kernel_read(fp, buf, f_size, &pos); + fs = get_fs(); + set_fs(KERNEL_DS); + aw_fs_read(fp, buf, f_size, &pos); + + set_fs(fs); if (sscanf(buf, "%d", &int_cali_re) == 1) *cali_re = int_cali_re; @@ -2128,6 +2156,8 @@ static void aw_cali_parse_dt(struct aw_device *aw_dev) desc->mode = AW_CALI_MODE_CLASS; else if (!strcmp(cali_mode_str, "aw_attr")) desc->mode = AW_CALI_MODE_ATTR; + else if (!strcmp(cali_mode_str, "aw_cali_all")) + desc->mode = AW_CALI_MODE_ALL; else desc->mode = AW_CALI_MODE_MISC; /*default misc*/ @@ -2175,10 +2205,14 @@ int aw_cali_init(struct aw_cali_desc *cali_desc) aw_cali_parse_dt(aw_dev); - if (cali_desc->mode == AW_CALI_MODE_ATTR) + if (cali_desc->mode == AW_CALI_MODE_ATTR) { aw_cali_attr_init(aw_dev); - else if (cali_desc->mode == AW_CALI_MODE_CLASS) + } else if (cali_desc->mode == AW_CALI_MODE_CLASS) { aw_cali_class_attr_init(aw_dev); + } else if (cali_desc->mode == AW_CALI_MODE_ALL) { + aw_cali_attr_init(aw_dev); + aw_cali_class_attr_init(aw_dev); + } aw_cali_misc_init(aw_dev); @@ -2187,19 +2221,19 @@ int aw_cali_init(struct aw_cali_desc *cali_desc) return ret; } -#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE -MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); -#endif - void aw_cali_deinit(struct aw_cali_desc *cali_desc) { struct aw_device *aw_dev = container_of(cali_desc, struct aw_device, cali_desc); - if (cali_desc->mode == AW_CALI_MODE_ATTR) + if (cali_desc->mode == AW_CALI_MODE_ATTR) { aw_cali_attr_deinit(aw_dev); - else if (cali_desc->mode == AW_CALI_MODE_CLASS) + } else if (cali_desc->mode == AW_CALI_MODE_CLASS) { aw_cali_class_attr_deinit(aw_dev); + } else if (cali_desc->mode == AW_CALI_MODE_ALL) { + aw_cali_attr_deinit(aw_dev); + aw_cali_class_attr_deinit(aw_dev); + } aw_cali_misc_deinit(aw_dev); } diff --git a/asoc/codecs/aw88261/aw_calib.h b/asoc/codecs/aw88261/aw_calib.h index ff3f15bebd98..fbcc2999282d 100644 --- a/asoc/codecs/aw88261/aw_calib.h +++ b/asoc/codecs/aw88261/aw_calib.h @@ -85,7 +85,8 @@ enum{ AW_CALI_MODE_ATTR, AW_CALI_MODE_CLASS, AW_CALI_MODE_MISC, - AW_CALI_MODE_MAX + AW_CALI_MODE_ALL, + AW_CALI_MODE_MAX, }; enum { diff --git a/asoc/codecs/aw88261/aw_device.c b/asoc/codecs/aw88261/aw_device.c index 6c2d289a4721..25c036e22cd8 100644 --- a/asoc/codecs/aw88261/aw_device.c +++ b/asoc/codecs/aw88261/aw_device.c @@ -682,7 +682,7 @@ static void aw_dev_fade_in(struct aw_device *aw_dev) int i = 0; int fade_step = aw_dev->vol_step; struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; - int fade_flag = aw882xx->fade_flag; + int fade_flag = aw882xx->fade_flag; struct aw_volume_desc *desc = &aw_dev->volume_desc; if (fade_step == 0 || g_fade_in_time == 0 || fade_flag == 0) { diff --git a/asoc/codecs/aw88261/aw_dsp.c b/asoc/codecs/aw88261/aw_dsp.c index 4d2424854c5b..eabb7764526f 100644 --- a/asoc/codecs/aw88261/aw_dsp.c +++ b/asoc/codecs/aw88261/aw_dsp.c @@ -94,6 +94,7 @@ static uint32_t afe_param_msg_id[MSG_PARAM_ID_MAX] = { extern int mtk_spk_send_ipi_buf_to_dsp(void *data_buffer, uint32_t data_size); extern int mtk_spk_recv_ipi_buf_from_dsp(int8_t *buffer, int16_t size, uint32_t *buf_len); #elif defined CONFIG_AW882XX_DSP +//extern int afe_get_topology(int port_id); extern int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id,uint32_t param_id, void *buf, int cmd_size, bool write); extern int aw_send_afe_rx_module_enable(int32_t rx_port_id, void *buf, int size); @@ -113,15 +114,24 @@ static int aw_send_afe_tx_module_enable(int32_t tx_port_id, void *buf, int size) { return 0; } +//static int afe_get_topology(int port_id) +//{ + //return 0; +//} #endif -#ifdef CONFIG_AW882XX_DSP -extern int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable); +/*#ifdef AW_QCOM_PLATFORM +extern void aw_set_port_id(int tx_port_id, int rx_port_id); #else +static void aw_set_port_id(int tx_port_id, int rx_port_id) { + return; +} +#endif*/ + +#if 0 static int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable) { -#if 0 /*for v3*/ int copp_idx = 0; uint32_t enable_param; @@ -158,7 +168,7 @@ static int aw_adm_param_enable(int port_id, int module_id, int param_id, int ena pr_err("%s: Failed to set enable of module(%d) instance(%d) to %d, err %d\n", __func__, module_id, INSTANCE_ID_0, enable, rc); return rc; -#endif + return 0; } #endif @@ -187,143 +197,6 @@ static int aw_get_msg_num(int dev_ch, int *msg_num) return 0; } -#ifdef AW_MTK_PLATFORM_WITH_DSP -/*****************mtk dsp communication function start**********************/ -static int aw_mtk_write_data_to_dsp(int param_id, void *data, int size) -{ - int32_t *dsp_data = NULL; - aw_dsp_msg_t *hdr = NULL; - int ret; - - dsp_data = kzalloc(sizeof(aw_dsp_msg_t) + size, GFP_KERNEL); - if (!dsp_data) { - pr_err("%s: kzalloc dsp_msg error\n", __func__); - return -ENOMEM; - } - - hdr = (aw_dsp_msg_t *)dsp_data; - hdr->type = AW_DSP_MSG_TYPE_DATA; - hdr->opcode_id = param_id; - hdr->version = AW_DSP_MSG_HDR_VER; - - memcpy(((char *)dsp_data) + sizeof(aw_dsp_msg_t), - data, size); - - ret = mtk_spk_send_ipi_buf_to_dsp(dsp_data, - sizeof(aw_dsp_msg_t) + size); - if (ret < 0) { - pr_err("%s:write data failed\n", __func__); - kfree(dsp_data); - dsp_data = NULL; - return ret; - } - - kfree(dsp_data); - dsp_data = NULL; - return 0; -} - -static int aw_mtk_read_data_from_dsp(int param_id, void *data, int size) -{ - int ret; - aw_dsp_msg_t hdr; - - hdr.type = AW_DSP_MSG_TYPE_CMD; - hdr.opcode_id = param_id; - hdr.version = AW_DSP_MSG_HDR_VER; - - mutex_lock(&g_aw_dsp_msg_lock); - ret = mtk_spk_send_ipi_buf_to_dsp(&hdr, sizeof(aw_dsp_msg_t)); - if (ret < 0) { - pr_err("%s:send cmd failed\n", __func__); - goto dsp_msg_failed; - } - - ret = mtk_spk_recv_ipi_buf_from_dsp(data, size, &size); - if (ret < 0) { - pr_err("%s:get data failed\n", __func__); - goto dsp_msg_failed; - } - mutex_unlock(&g_aw_dsp_msg_lock); - return 0; - -dsp_msg_failed: - mutex_unlock(&g_aw_dsp_msg_lock); - return ret; -} - -static int aw_mtk_write_msg_to_dsp(int msg_num, int inline_id, - void *data, int size) -{ - int32_t *dsp_msg = NULL; - aw_dsp_msg_t *hdr = NULL; - int ret; - - dsp_msg = kzalloc(sizeof(aw_dsp_msg_t) + size, - GFP_KERNEL); - if (!dsp_msg) { - pr_err("%s: inline_id:0x%x kzalloc dsp_msg error\n", - __func__, inline_id); - return -ENOMEM; - } - hdr = (aw_dsp_msg_t *)dsp_msg; - hdr->type = AW_DSP_MSG_TYPE_DATA; - hdr->opcode_id = inline_id; - hdr->version = AW_DSP_MSG_HDR_VER; - - memcpy(((char *)dsp_msg) + sizeof(aw_dsp_msg_t), - data, size); - - ret = aw_mtk_write_data_to_dsp(afe_param_msg_id[msg_num], (void *)dsp_msg, - sizeof(aw_dsp_msg_t) + size); - if (ret < 0) { - pr_err("%s:inline_id:0x%x, write data failed\n", - __func__, inline_id); - kfree(dsp_msg); - dsp_msg = NULL; - return ret; - } - - kfree(dsp_msg); - dsp_msg = NULL; - return 0; -} - -static int aw_mtk_read_msg_from_dsp(int msg_num, int inline_id, - char *data, int size) -{ - aw_dsp_msg_t hdr[2]; - int ret; - - hdr[0].type = AW_DSP_MSG_TYPE_DATA; - hdr[0].opcode_id = afe_param_msg_id[msg_num]; - hdr[0].version = AW_DSP_MSG_HDR_VER; - hdr[1].type = AW_DSP_MSG_TYPE_CMD; - hdr[1].opcode_id = inline_id; - hdr[1].version = AW_DSP_MSG_HDR_VER; - - mutex_lock(&g_aw_dsp_msg_lock); - ret = mtk_spk_send_ipi_buf_to_dsp(&hdr, 2 * sizeof(aw_dsp_msg_t)); - if (ret < 0) { - pr_err("%s:send cmd failed\n", __func__); - goto dsp_msg_failed; - } - - ret = mtk_spk_recv_ipi_buf_from_dsp(data, size, &size); - if (ret < 0) { - pr_err("%s:get data failed\n", __func__); - goto dsp_msg_failed; - } - mutex_unlock(&g_aw_dsp_msg_lock); - return 0; - -dsp_msg_failed: - mutex_unlock(&g_aw_dsp_msg_lock); - return ret; -} - -/*****************mtk dsp communication function end**********************/ -#else /*****************qcom dsp communication function start**********************/ #if 0 static int aw_afe_get_topology(uint32_t param_id) @@ -359,21 +232,46 @@ static int aw_check_dsp_ready(uint32_t param_id) static int aw_qcom_write_data_to_dsp(uint32_t param_id, void *data, int size) { int ret; + //int try = 0; mutex_lock(&g_aw_dsp_lock); + //while (try < AW_DSP_TRY_TIME) { + //if (aw_check_dsp_ready(param_id)) { ret = aw_send_afe_cal_apr(g_rx_port_id, g_tx_port_id, param_id, data, size, true); mutex_unlock(&g_aw_dsp_lock); return ret; + //} else { + // try++; + // usleep_range(AW_10000_US, AW_10000_US + 10); + // aw_pr_info("afe topo not ready try again"); + //} + //} + //mutex_unlock(&g_aw_dsp_lock); + + //return -EINVAL; } static int aw_qcom_read_data_from_dsp(uint32_t param_id, void *data, int size) { int ret; + //int try = 0; mutex_lock(&g_aw_dsp_lock); + //while (try < AW_DSP_TRY_TIME) { + // if (aw_check_dsp_ready(param_id)) { ret = aw_send_afe_cal_apr(g_rx_port_id, g_tx_port_id, param_id, data, size, false); mutex_unlock(&g_aw_dsp_lock); return ret; +/* } else { + try++; + usleep_range(AW_10000_US, AW_10000_US + 10); + aw_pr_info("afe topo not ready try again"); + } + } + mutex_unlock(&g_aw_dsp_lock); + + return -EINVAL; +*/ } static int aw_qcom_write_msg_to_dsp(int msg_num, uint32_t msg_id, char *data_ptr, unsigned int size) @@ -488,9 +386,9 @@ static int aw_write_data_to_dsp(uint32_t param_id, void *data, int size) { return aw_qcom_write_data_to_dsp(param_id, data, size); } -#endif /************************* dsp communication function *****************************/ + int aw_dsp_set_algo_prof(struct aw_device *aw_dev, int prof_id) { int ret; diff --git a/asoc/codecs/aw88261/aw_init.c b/asoc/codecs/aw88261/aw_init.c index 8acd57c63f76..7198290a535a 100644 --- a/asoc/codecs/aw88261/aw_init.c +++ b/asoc/codecs/aw88261/aw_init.c @@ -1062,6 +1062,11 @@ static int aw_pid_2055_dev_init(struct aw882xx *aw882xx, int index) aw_pa->soft_rst.reg = AW_PID_2055_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + + usleep_range(AW_2000_US, AW_2000_US +10); + aw_pa->ops.aw_i2c_write(aw_pa, AW_PID_2055_INIT_CHECK_REG, + AW_PID_2055_INIT_CHECK_VALUE); + usleep_range(AW_3000_US, AW_3000_US +10); ret = aw_device_probe(aw_pa); @@ -1624,6 +1629,12 @@ static int aw_pid_2113_dev_init(struct aw882xx *aw882xx, int index) aw_pa->soft_rst.reg = AW_PID_2113_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + + usleep_range(AW_2000_US, AW_2000_US +10); + aw_pa->ops.aw_i2c_write(aw_pa, AW_PID_2113_INIT_CHECK_REG, + AW_PID_2113_INIT_CHECK_VALUE); + usleep_range(AW_3000_US, AW_3000_US +10); + aw_pid_2113_efver_check(aw_pa); ret = aw_device_probe(aw_pa); diff --git a/asoc/codecs/aw88261/aw_pid_2055_reg.h b/asoc/codecs/aw88261/aw_pid_2055_reg.h index 66b9401b1cb7..86e0448f89d2 100644 --- a/asoc/codecs/aw88261/aw_pid_2055_reg.h +++ b/asoc/codecs/aw88261/aw_pid_2055_reg.h @@ -2021,4 +2021,10 @@ const unsigned char aw_pid_2055_reg_access[AW_PID_2055_REG_MAX] = { *******************************************/ #define AW_PID_2055_VOL_STEP (6 * 8) +/******************************************** + * Init check + *******************************************/ +#define AW_PID_2055_INIT_CHECK_REG (0x73) +#define AW_PID_2055_INIT_CHECK_VALUE (0x1D40) + #endif /* #ifndef __AW_PID_2055_REG_H__ */ diff --git a/asoc/codecs/aw88261/aw_pid_2113_reg.h b/asoc/codecs/aw88261/aw_pid_2113_reg.h index 7c43888a0165..b2146b1b20d3 100644 --- a/asoc/codecs/aw88261/aw_pid_2113_reg.h +++ b/asoc/codecs/aw88261/aw_pid_2113_reg.h @@ -2303,4 +2303,10 @@ const unsigned char aw_pid_2113_reg_access[AW_PID_2113_REG_MAX] = { #define AW_PID_2113_TEML_MASK (0x83ff) #define AW_PID_2113_DEFAULT_CFG (0x0000) +/******************************************** + * Init check + *******************************************/ +#define AW_PID_2113_INIT_CHECK_REG (0x73) +#define AW_PID_2113_INIT_CHECK_VALUE (0x1D40) + #endif /* #ifndef __AW_PID_2113_REG_H__ */ From 2ea4212fc3676d1a20b89bf14b4cb4a1dfb6571a Mon Sep 17 00:00:00 2001 From: huaqin95 Date: Wed, 24 Nov 2021 15:26:10 +0800 Subject: [PATCH 13/40] rhode:mbhc:enable moisture detect for rhode update mbhc for rhode; enable moisture detect. Change-Id: I5b82baca51ee62511419076e8cf025b4b1e020f6 Signed-off-by: huaqin95 Reviewed-on: https://gerrit.mot.com/2125940 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Zhengming Yao Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- config/rhodeautoconf.h | 1 - 1 file changed, 1 deletion(-) diff --git a/config/rhodeautoconf.h b/config/rhodeautoconf.h index f0193a3f0288..8312cd204184 100644 --- a/config/rhodeautoconf.h +++ b/config/rhodeautoconf.h @@ -2,5 +2,4 @@ /* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */ #define CONFIG_AW882XX_STEREO_SMARTPA 1 -#define CONFIG_MBHC_DISABLE_MOISTURE_DETECT 1 #define CONFIG_AW882XX_PARAMS_STORED_IN_BIN 1 From 3390566b8c0d7dc510ad30301d9819498c52357b Mon Sep 17 00:00:00 2001 From: yaozm1 Date: Tue, 30 Nov 2021 14:19:29 +0800 Subject: [PATCH 14/40] asoc:upgrade aw88261 driver to v1.9 debug version add profile control; v1.9 debug version. Change-Id: I2964cb83649917af1cdcd97bf53c0e1fb05411b1 Signed-off-by: yaozm1 Reviewed-on: https://gerrit.mot.com/2130885 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/aw88261/aw882xx.c | 627 +++++++++++++++++++++++++++++++- asoc/codecs/aw88261/aw882xx.h | 78 ++++ asoc/codecs/aw88261/aw_device.c | 158 ++++++-- asoc/codecs/aw88261/aw_dsp.c | 2 +- 4 files changed, 834 insertions(+), 31 deletions(-) diff --git a/asoc/codecs/aw88261/aw882xx.c b/asoc/codecs/aw88261/aw882xx.c index 74ee9c6e2492..60e54cf4fbae 100644 --- a/asoc/codecs/aw88261/aw882xx.c +++ b/asoc/codecs/aw88261/aw882xx.c @@ -12,7 +12,7 @@ * option) any later version. */ -/*#define DEBUG*/ +#define DEBUG #include #include #include @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.8.0.1" +#define AW882XX_DRIVER_VERSION "v1.9.0" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ @@ -56,7 +56,77 @@ static unsigned int g_runin_test; static DEFINE_MUTEX(g_aw882xx_lock); struct aw_container *g_awinic_cfg = NULL; + +#define AW882XX_SCENE_CALIBRATION_ID 0 +#define AW882XX_SCENE_BYPASS_ID 1 +#define AW882XX_SCENE_HANDSET_ID 2 +#define AW882XX_SCENE_VOICE_ID 3 +#define AW882XX_SCENE_VOIP_ID 4 +#define AW882XX_SCENE_DEEPBUFFER_ID 5 +#define AW882XX_SCENE_FASTTRACK_ID 6 + + +struct aw882xx_scene_info aw882xx_scene_state[AW_ALGO_PROFILE_ID_MAX] = { + { + AW882XX_SCENE_CALIBRATION_ID, + "Calibration", + 1, + 1, + 0, + 0, + }, + { + AW882XX_SCENE_BYPASS_ID, + "Bypass", + 2, + 2, + 0, + 0, + }, + { + AW882XX_SCENE_HANDSET_ID, + "Handset", + 3, + 3, + 0, + 0, + }, + { + AW882XX_SCENE_VOICE_ID, + "Voice", + 4, + 4, + 0, + 0, + }, + { + AW882XX_SCENE_VOIP_ID, + "Voip", + 5, + 5, + 0, + 0, + }, + { + AW882XX_SCENE_DEEPBUFFER_ID, + "deepbuffer", + 6, + 6, + 0, + 0, + }, + { + AW882XX_SCENE_FASTTRACK_ID, + "fasttrack", + 7, + 7, + 0, + 0, + }, +}; + static const char *const switch_status[] = { "Off", "On" }; +static const char *const aw882xx_ramp_status[] = { "Off", "On" }; static const char *const aw882xx_switch[] = {"Disable", "Enable"}; #ifdef AW_SPIN_ENABLE static const char *const aw882xx_spin[] = {"spin_0", "spin_90", @@ -621,12 +691,71 @@ static int aw882xx_switch_set(struct snd_kcontrol *kcontrol, return 0; } +static int aw882xx_ramp_status_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + count = 2; + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + strlcpy(uinfo->value.enumerated.name, + aw882xx_ramp_status[uinfo->value.enumerated.item], + strlen(aw882xx_ramp_status[uinfo->value.enumerated.item]) + 1); + + return 0; +} + +static int aw882xx_ramp_status_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int *ramp_status = &aw882xx->aw882xx_ramp_status; + + aw_dev_dbg(aw882xx->dev, "%s: aw_dev_ramp_status_switch=%d\n", + __func__, *ramp_status); + + ucontrol->value.integer.value[0] = *ramp_status; + + return 0; +} + +static int aw882xx_ramp_status_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int *ramp_status = &aw882xx->aw882xx_ramp_status; + + aw_dev_dbg(aw882xx->dev, "%s: ucontrol->value.integer.value[0]=%ld\n", + __func__, ucontrol->value.integer.value[0]); + + *ramp_status = ucontrol->value.integer.value[0]; + + if (*ramp_status == AW882XX_RAMP_ON) + aw882xx->aw882xx_ramp_status = AW882XX_RAMP_ON; + else if (*ramp_status == AW882XX_RAMP_OFF) + aw882xx->aw882xx_ramp_status = AW882XX_RAMP_OFF; + + return 0; +} + static int aw882xx_dynamic_create_controls(struct aw882xx *aw882xx) { struct snd_kcontrol_new *aw882xx_dev_control = NULL; char *kctl_name; - aw882xx_dev_control = devm_kzalloc(aw882xx->codec->dev, sizeof(struct snd_kcontrol_new) * 2, GFP_KERNEL); + aw882xx_dev_control = devm_kzalloc(aw882xx->codec->dev, sizeof(struct snd_kcontrol_new) * 3, GFP_KERNEL); if (aw882xx_dev_control == NULL) { aw_dev_err(aw882xx->codec->dev, "kcontrol malloc failed!"); return -ENOMEM; @@ -655,9 +784,22 @@ static int aw882xx_dynamic_create_controls(struct aw882xx *aw882xx) aw882xx_dev_control[1].info = aw882xx_switch_info; aw882xx_dev_control[1].get = aw882xx_switch_get; aw882xx_dev_control[1].put = aw882xx_switch_set; + + //ramp control + kctl_name = devm_kzalloc(aw882xx->codec->dev, AW_NAME_BUF_MAX, GFP_KERNEL); + if (!kctl_name) + return -ENOMEM; + snprintf(kctl_name, AW_NAME_BUF_MAX, "aw_dev_%d_ramp_status_switch", aw882xx->index); + + aw882xx_dev_control[2].name = kctl_name; + aw882xx_dev_control[2].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw882xx_dev_control[2].info = aw882xx_ramp_status_info; + aw882xx_dev_control[2].get = aw882xx_ramp_status_get; + aw882xx_dev_control[2].put = aw882xx_ramp_status_set; + //end ramp control aw_componet_codec_ops.add_codec_controls(aw882xx->codec, - aw882xx_dev_control, 2); + aw882xx_dev_control, 3); return 0; } @@ -1074,6 +1216,7 @@ static int aw882xx_get_spin(struct snd_kcontrol *kcontrol, } #endif + static int aw882xx_set_prof_id(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1095,7 +1238,6 @@ static int aw882xx_set_prof_id(struct snd_kcontrol *kcontrol, return 0; } - static int aw882xx_get_prof_id(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1118,6 +1260,7 @@ static int aw882xx_get_prof_id(struct snd_kcontrol *kcontrol, return 0; } + static int aw882xx_get_fade_in_time(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1180,6 +1323,88 @@ static int aw882xx_set_fade_out_time(struct snd_kcontrol *kcontrol, return 0; } +static int aw882xx_0_ramp_step_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int time; + + aw_dev_get_fade_time(&time, true); + ucontrol->value.integer.value[0] = time; + + aw_pr_dbg("aw_dev_ch_pri_l_fadein_time time %ld", ucontrol->value.integer.value[0]); + + return 0; +} + +static int aw882xx_0_ramp_step_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + struct aw_device *aw_dev = aw882xx->aw_pa; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int channel = aw_dev->channel; + + if (ucontrol->value.integer.value[0] > mc->max) { + aw_pr_dbg("set val %ld overflow %d", + ucontrol->value.integer.value[0], mc->max); + return 0; + } + + if (aw882xx->aw882xx_ramp_status == AW882XX_RAMP_ON && channel == AW_DEV_CH_PRI_L) + aw_dev_set_fade_time(ucontrol->value.integer.value[0], true); + else if (aw882xx->aw882xx_ramp_status == AW882XX_RAMP_OFF) + aw_dev_info(aw882xx->dev, + "%s: fade status is off,time can not set\n", __func__); + + aw_pr_dbg("step time %ld", ucontrol->value.integer.value[0]); + return 0; +} + +static int aw882xx_1_ramp_step_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int time; + + aw_dev_get_fade_time(&time, true); + ucontrol->value.integer.value[0] = time; + + aw_pr_dbg("aw_dev_ch_pri_r_fadein_time time %ld", ucontrol->value.integer.value[0]); + + return 0; +} + +static int aw882xx_1_ramp_step_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + struct aw_device *aw_dev = aw882xx->aw_pa; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int channel = aw_dev->channel; + + if (ucontrol->value.integer.value[0] > mc->max) { + aw_pr_dbg("set val %ld overflow %d", + ucontrol->value.integer.value[0], mc->max); + return 0; + } + + if (aw882xx->aw882xx_ramp_status == AW882XX_RAMP_ON && channel == AW_DEV_CH_PRI_L) + aw_dev_set_fade_time(ucontrol->value.integer.value[0], true); + else if (aw882xx->aw882xx_ramp_status == AW882XX_RAMP_OFF) + aw_dev_info(aw882xx->dev, + "%s: fade status is off,time can not set\n", __func__); + + aw_pr_dbg("step time %ld", ucontrol->value.integer.value[0]); + return 0; +} + #ifdef AW882XX_RUNIN_TEST static int aw882xx_runin_test_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1233,6 +1458,110 @@ static void aw882xx_set_adsp_module_status(struct work_struct *work) } #endif + +static int aw882xx_get_algo_prof_id_by_scene_st(struct aw882xx *aw882xx) +{ + int index = -1; + int i = 0; + + /* Get the highest priority array subscript from all active scene */ + for (i = 0; i < AW_ALGO_PROFILE_ID_MAX; i++) { + if (aw882xx_scene_state[i].is_active) { + if (index < 0) { + index = i; + } else { + if(aw882xx_scene_state[i].priority < + aw882xx_scene_state[index].priority) { + index = i; + } + } + } + } + + if (index >= 0) { + return aw882xx_scene_state[index].skt_profile_id; + } + + return AW_ALGO_PROFILE_ID_MAX; +} + +static void aw882xx_update_algo_scene_st(struct aw882xx *aw882xx, + int scene_id, bool is_active) { + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[scene_id]; + + if (is_active) { + p_aw882xx_scene_st->active_cnt++; + p_aw882xx_scene_st->is_active = 1; + } else { + p_aw882xx_scene_st->active_cnt--; + if (p_aw882xx_scene_st->active_cnt < 0) { + p_aw882xx_scene_st->active_cnt = 0; + } + if (p_aw882xx_scene_st->active_cnt == 0) { + p_aw882xx_scene_st->is_active = 0; + } + } +} + + +static int aw882xx_update_algo_profile(struct aw882xx *aw882xx) +{ + int ret = 0; + int new_skt_prof_id = 0; + int cur_skt_prof_id = aw882xx->cur_algo_prof_id; + struct aw_device *aw_dev = NULL; + aw_dev_info(aw882xx->dev, "enter"); + + new_skt_prof_id = aw882xx_get_algo_prof_id_by_scene_st(aw882xx); + aw_dev_dbg(aw882xx->dev, "new_skt_prof_id = %d,cur_skt_prof_id=%d\n", + new_skt_prof_id, + cur_skt_prof_id); + + if (new_skt_prof_id > AW_ALGO_PROFILE_ID_MAX) { + /* no active scene */ + aw_dev_info(aw882xx->dev, "all scene disactive,do nothing"); + return 0; + } + + if (cur_skt_prof_id != new_skt_prof_id) { + aw_dev_info(aw882xx->dev, "algo scene switch. [new] %d,[old] %d", + new_skt_prof_id, cur_skt_prof_id); + aw882xx->cur_algo_prof_id = new_skt_prof_id; + aw_dev = aw882xx->aw_pa; + + /* set new scene pramas to skt */ + ret = aw_dev_set_algo_prof(aw_dev, aw882xx->cur_algo_prof_id); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "set algo prof failed"); + return -1; + } + } else { + aw_dev_info(aw882xx->dev, "algo scene hold, cur scene %d", + new_skt_prof_id); + } + + return 0; + +} + + +static int aw882xx_algo_dump_scene_st(struct aw882xx *aw882xx) +{ + int i = 0; + + aw_dev_info(aw882xx->dev, "aw882xx cur algo prof id =%d", + aw882xx->cur_algo_prof_id); + for (i = 0; i < AW_ALGO_PROFILE_ID_MAX; i++) { + aw_dev_info(aw882xx->dev, "scene[%s] %s active_cnt %d", + aw882xx_scene_state[i].name, + aw882xx_scene_state[i].is_active? "[active]" : "[disactive]", + aw882xx_scene_state[i].active_cnt); + } + return 0; +} + + static int aw882xx_monitor_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1241,7 +1570,7 @@ static int aw882xx_monitor_get(struct snd_kcontrol *kcontrol, struct aw_device *aw_dev = aw882xx->aw_pa; struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; - pr_debug("%s: aw882xx_monitor_get %d\n", + pr_debug("%s: aw882xx_monitor_get %d", __func__, monitor_cfg->monitor_switch); ucontrol->value.integer.value[0] = monitor_cfg->monitor_switch; @@ -1274,6 +1603,272 @@ static int aw882xx_monitor_set(struct snd_kcontrol *kcontrol, return 0; } +static int aw882xx_get_algo_cali_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_CALIBRATION_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_set_algo_cali_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_CALIBRATION_ID, is_active); + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_get_algo_bypass_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_BYPASS_ID]; + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + return 0; +} + +static int aw882xx_set_algo_bypass_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_BYPASS_ID, is_active); + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_get_algo_handset_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_HANDSET_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + + return 0; +} + +static int aw882xx_set_algo_handset_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_HANDSET_ID, is_active); + + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_get_algo_voice_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_VOICE_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + + return 0; +} + +static int aw882xx_set_algo_voice_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_VOICE_ID, is_active); + + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + + return 0; +} + +static int aw882xx_get_algo_voip_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_VOIP_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + + return 0; +} + +static int aw882xx_set_algo_voip_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_VOIP_ID, is_active); + + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_get_algo_music_deepbuffer_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_DEEPBUFFER_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + + return 0; +} + +static int aw882xx_set_algo_music_deepbuffer_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_DEEPBUFFER_ID, is_active); + + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_get_algo_music_fasttrack_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_FASTTRACK_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + + return 0; +} + +static int aw882xx_set_algo_music_fasttrack_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_FASTTRACK_ID, is_active); + + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + static const struct soc_enum aw882xx_snd_enum[] = { SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(aw882xx_switch), aw882xx_switch), SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(switch_status), switch_status), @@ -1305,6 +1900,24 @@ static struct snd_kcontrol_new aw882xx_controls[] = { aw882xx_get_fade_out_time, aw882xx_set_fade_out_time), SOC_SINGLE_EXT("aw882xx_algo_prof_switch", 0, 0, 100, 0, aw882xx_get_prof_id, aw882xx_set_prof_id), + SOC_SINGLE_EXT("aw_dev_0_ramp_step_switch", 0, 0, 1000000, 0, + aw882xx_0_ramp_step_get, aw882xx_0_ramp_step_set), + SOC_SINGLE_EXT("aw_dev_1_ramp_step_switch", 0, 0, 1000000, 0, + aw882xx_1_ramp_step_get, aw882xx_1_ramp_step_set), + SOC_ENUM_EXT("aw882xx_algo_cali_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_cali_switch, aw882xx_set_algo_cali_switch), + SOC_ENUM_EXT("aw882xx_algo_bypass_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_bypass_switch, aw882xx_set_algo_bypass_switch), + SOC_ENUM_EXT("aw882xx_algo_handset_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_handset_switch, aw882xx_set_algo_handset_switch), + SOC_ENUM_EXT("aw882xx_algo_voice_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_voice_switch, aw882xx_set_algo_voice_switch), + SOC_ENUM_EXT("aw882xx_algo_voip_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_voip_switch, aw882xx_set_algo_voip_switch), + SOC_ENUM_EXT("aw882xx_algo_music_deepbuffer_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_music_deepbuffer_switch, aw882xx_set_algo_music_deepbuffer_switch), + SOC_ENUM_EXT("aw882xx_algo_music_fasttrack_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_music_fasttrack_switch, aw882xx_set_algo_music_fasttrack_switch), }; static void aw882xx_add_codec_controls(struct aw882xx *aw882xx) @@ -2372,6 +2985,8 @@ static int aw882xx_i2c_probe(struct i2c_client *i2c, aw882xx->i2c_packet.reg_data = NULL; aw882xx->index = g_aw882xx_dev_cnt; + + aw882xx->aw882xx_ramp_status = AW882XX_RAMP_OFF; #ifdef AW882XX_RUNIN_TEST g_runin_test = 0; INIT_DELAYED_WORK(&aw882xx->adsp_status, aw882xx_set_adsp_module_status); diff --git a/asoc/codecs/aw88261/aw882xx.h b/asoc/codecs/aw88261/aw882xx.h index d50a66456c27..d235668550f2 100644 --- a/asoc/codecs/aw88261/aw882xx.h +++ b/asoc/codecs/aw88261/aw882xx.h @@ -24,6 +24,9 @@ #define AW882XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE) + +#define AW882XX_ALGO_SET_PROF_ENABLE 1 +#define AW882XX_ALGO_SET_PROF_DISABLE 0 enum { AW882XX_STREAM_CLOSE = 0, @@ -49,6 +52,27 @@ enum aw882xx_int_type { INT_TYPE_OTHI = 0x8, }; +enum { + AW_ALGO_PROFILE_ID_0 = 0, + AW_ALGO_PROFILE_ID_1, + AW_ALGO_PROFILE_ID_2, + AW_ALGO_PROFILE_ID_3, + AW_ALGO_PROFILE_ID_4, + AW_ALGO_PROFILE_ID_5, + AW_ALGO_PROFILE_ID_6, + AW_ALGO_PROFILE_ID_MAX, +}; + +struct aw882xx_scene_info { + int id; + char name[20]; + int priority; + int skt_profile_id; + int is_active; + int active_cnt; +}; + + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 1) #define AW_KERNEL_VER_OVER_4_19_1 #endif @@ -110,6 +134,48 @@ enum { AW_BOP_ENABLE, }; +enum { + AW882XX_RAMP_OFF = 0, + AW882XX_RAMP_ON, +}; + +enum { + AW_ALGO_CALI_OFF = 0, + AW_ALGO_CALI_ON, +}; + +enum { + AW_ALGO_BYPASS_OFF = 0, + AW_ALGO_BYPASS_ON, +}; + +enum { + AW_ALGO_HANDSET_OFF = 0, + AW_ALGO_HANDSET_ON, +}; + +enum { + AW_ALGO_VOICE_OFF = 0, + AW_ALGO_VOICE_ON, +}; + +enum { + AW_ALGO_VOIP_OFF = 0, + AW_ALGO_VOIP_ON, +}; + +enum { + AW_ALGO_MUSIC_DEEPBUFFER_OFF = 0, + AW_ALGO_MUSIC_DEEPBUFFER_ON, +}; + +enum { + AW_ALGO_MUSIC_FASTTRACK_OFF = 0, + AW_ALGO_MUSIC_FASTTRACK_ON, +}; + + + enum { AWRW_HDR_WR_FLAG = 0, AWRW_HDR_ADDR_BYTES, @@ -134,6 +200,18 @@ struct aw882xx { int rate; int pstream; int cstream; + int aw882xx_ramp_status; /* ramp status */ + int aw882xx_algo_cali; + int aw882xx_algo_bypass; + int aw882xx_algo_handset; + int aw882xx_algo_voice; + int aw882xx_algo_voip; + int aw882xx_algo_music_deepbuffer; + int aw882xx_algo_music_fasttrack; + + int aw882xx_algo_prof_id; + int cur_algo_prof_id; + unsigned int fade_flag; unsigned char index; diff --git a/asoc/codecs/aw88261/aw_device.c b/asoc/codecs/aw88261/aw_device.c index 25c036e22cd8..251fad2f0f0a 100644 --- a/asoc/codecs/aw88261/aw_device.c +++ b/asoc/codecs/aw88261/aw_device.c @@ -1,4 +1,4 @@ -/* #define DEBUG */ + #define DEBUG #include #include #include @@ -40,7 +40,11 @@ static char *profile_name[AW_PROFILE_MAX] = { static char ext_dsp_prof_write = AW_EXT_DSP_WRITE_NONE; static DEFINE_MUTEX(g_ext_dsp_prof_wr_lock); /*lock ext wr flag*/ -static unsigned int g_fade_in_time = AW_1000_US / 10; +static unsigned int aw_dev_ch_pri_l_fadein_time = AW_1000_US / 10; +static unsigned int aw_dev_ch_pri_r_fadein_time = AW_1000_US / 10; +static unsigned int aw_dev_ch_sec_l_fadein_time = AW_1000_US / 10; +static unsigned int aw_dev_ch_sec_r_fadein_time = AW_1000_US / 10; + static unsigned int g_fade_out_time = AW_1000_US >> 1; static LIST_HEAD(g_dev_list); static DEFINE_MUTEX(g_dev_lock); @@ -684,18 +688,70 @@ static void aw_dev_fade_in(struct aw_device *aw_dev) struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; int fade_flag = aw882xx->fade_flag; struct aw_volume_desc *desc = &aw_dev->volume_desc; - - if (fade_step == 0 || g_fade_in_time == 0 || fade_flag == 0) { - aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); - return; + int channel = aw_dev->channel; + + switch (channel) { + case AW_DEV_CH_PRI_L: + if (fade_step == 0 || aw_dev_ch_pri_l_fadein_time == 0 || fade_flag == 0) { + aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); + return; + } + + /*volume up*/ + for (i = desc->mute_volume; i >= desc->init_volume; i -= fade_step) { + aw_dev->ops.aw_set_volume(aw_dev, i); + usleep_range(aw_dev_ch_pri_l_fadein_time, aw_dev_ch_pri_l_fadein_time + 10); + } + if (i != desc->init_volume) + aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); + break; + case AW_DEV_CH_PRI_R: + if (fade_step == 0 || aw_dev_ch_pri_r_fadein_time == 0 || fade_flag == 0) { + aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); + return; + } + + /*volume up*/ + for (i = desc->mute_volume; i >= desc->init_volume; i -= fade_step) { + aw_dev->ops.aw_set_volume(aw_dev, i); + usleep_range(aw_dev_ch_pri_r_fadein_time, aw_dev_ch_pri_r_fadein_time + 10); + } + if (i != desc->init_volume) + aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); + break; + case AW_DEV_CH_SEC_L: + if (fade_step == 0 || aw_dev_ch_sec_l_fadein_time == 0 || fade_flag == 0) { + aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); + return; + } + + /*volume up*/ + for (i = desc->mute_volume; i >= desc->init_volume; i -= fade_step) { + aw_dev->ops.aw_set_volume(aw_dev, i); + usleep_range(aw_dev_ch_sec_l_fadein_time, aw_dev_ch_sec_l_fadein_time + 10); + } + if (i != desc->init_volume) + aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); + break; + case AW_DEV_CH_SEC_R: + if (fade_step == 0 || aw_dev_ch_sec_r_fadein_time == 0 || fade_flag == 0) { + aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); + return; + } + + /*volume up*/ + for (i = desc->mute_volume; i >= desc->init_volume; i -= fade_step) { + aw_dev->ops.aw_set_volume(aw_dev, i); + usleep_range(aw_dev_ch_sec_r_fadein_time, aw_dev_ch_sec_r_fadein_time + 10); + } + if (i != desc->init_volume) + aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); + break; + default: + aw_dev_err(aw882xx->dev, + "%s: fade time can not set\n", __func__); + break; } - /*volume up*/ - for (i = desc->mute_volume; i >= desc->init_volume; i -= fade_step) { - aw_dev->ops.aw_set_volume(aw_dev, i); - usleep_range(g_fade_in_time, g_fade_in_time + 10); - } - if (i != desc->init_volume) - aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); } static void aw_dev_fade_out(struct aw_device *aw_dev) @@ -763,7 +819,7 @@ static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd) static void aw_dev_mute(struct aw_device *aw_dev, bool mute) { struct aw_mute_desc *mute_desc = &aw_dev->mute_desc; - + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; aw_dev_dbg(aw_dev->dev, "enter"); if (mute) { @@ -772,10 +828,22 @@ static void aw_dev_mute(struct aw_device *aw_dev, bool mute) mute_desc->mask, mute_desc->enable); } else { - aw_dev->ops.aw_i2c_write_bits(aw_dev, mute_desc->reg, - mute_desc->mask, - mute_desc->disable); - aw_dev_fade_in(aw_dev); + if (aw882xx->aw882xx_ramp_status == AW882XX_RAMP_ON) { + aw_dev->ops.aw_i2c_write_bits(aw_dev, mute_desc->reg, + mute_desc->mask, + mute_desc->disable); + aw_dev_fade_in(aw_dev); + } else if (aw882xx->aw882xx_ramp_status == AW882XX_RAMP_OFF) { + aw_dev_ch_pri_l_fadein_time = AW_1000_US / 10; + aw_dev_ch_pri_r_fadein_time = AW_1000_US / 10; + aw_dev_ch_sec_l_fadein_time = AW_1000_US / 10; + aw_dev_ch_sec_r_fadein_time = AW_1000_US / 10; + + aw_dev->ops.aw_i2c_write_bits(aw_dev, mute_desc->reg, + mute_desc->mask, + mute_desc->disable); + aw_dev_fade_in(aw_dev); + } } aw_dev_info(aw_dev->dev, "done"); } @@ -1099,17 +1167,59 @@ void aw_dev_set_fade_vol_step(struct aw_device *aw_dev, unsigned int step) void aw_dev_get_fade_time(unsigned int *time, bool fade_in) { - if (fade_in) - *time = g_fade_in_time; - else + struct aw_device *aw_dev; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + int channel = aw_dev->channel; + + if (fade_in) { + switch (channel) { + case AW_DEV_CH_PRI_L: + *time = aw_dev_ch_pri_l_fadein_time; + break; + case AW_DEV_CH_PRI_R: + *time = aw_dev_ch_pri_r_fadein_time; + break; + case AW_DEV_CH_SEC_L: + *time = aw_dev_ch_sec_l_fadein_time; + break; + case AW_DEV_CH_SEC_R: + *time = aw_dev_ch_sec_r_fadein_time; + break; + default: + aw_dev_err(aw882xx->dev, + "%s: fade time can not set\n", __func__); + break; + } + } else *time = g_fade_out_time; } void aw_dev_set_fade_time(unsigned int time, bool fade_in) { - if (fade_in) - g_fade_in_time = time; - else + struct aw_device *aw_dev; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + int channel = aw_dev->channel; + + if (fade_in) { + switch (channel) { + case AW_DEV_CH_PRI_L: + aw_dev_ch_pri_l_fadein_time = time; + break; + case AW_DEV_CH_PRI_R: + aw_dev_ch_pri_r_fadein_time = time; + break; + case AW_DEV_CH_SEC_L: + aw_dev_ch_sec_l_fadein_time = time; + break; + case AW_DEV_CH_SEC_R: + aw_dev_ch_sec_r_fadein_time = time; + break; + default: + aw_dev_err(aw882xx->dev, + "%s: fade time can not set\n", __func__); + break; + } + }else g_fade_out_time = time; } diff --git a/asoc/codecs/aw88261/aw_dsp.c b/asoc/codecs/aw88261/aw_dsp.c index eabb7764526f..808ad98d6e2a 100644 --- a/asoc/codecs/aw88261/aw_dsp.c +++ b/asoc/codecs/aw88261/aw_dsp.c @@ -1,4 +1,4 @@ -/*#define DEBUG*/ +#define DEBUG #include #include #include From f6c4ec73dc2b5e6c4be319afd27bad4a3192a2a2 Mon Sep 17 00:00:00 2001 From: yaozm1 Date: Tue, 14 Dec 2021 17:15:55 +0800 Subject: [PATCH 15/40] asoc:upgrade aw88261 driver to v1.9.0.2 release v1.9.0.2, add ramp support. Change-Id: I0b13202e458c6c9fdd732edcedbdb78c4db8f826 Signed-off-by: yaozm1 Reviewed-on: https://gerrit.mot.com/2143963 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/aw88261/aw882xx.c | 518 ++++++++++++++++---------------- asoc/codecs/aw88261/aw882xx.h | 12 +- asoc/codecs/aw88261/aw_device.c | 139 ++------- asoc/codecs/aw88261/aw_device.h | 2 + asoc/codecs/aw88261/aw_init.c | 11 +- 5 files changed, 296 insertions(+), 386 deletions(-) diff --git a/asoc/codecs/aw88261/aw882xx.c b/asoc/codecs/aw88261/aw882xx.c index 60e54cf4fbae..c7bc965d85fa 100644 --- a/asoc/codecs/aw88261/aw882xx.c +++ b/asoc/codecs/aw88261/aw882xx.c @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.9.0" +#define AW882XX_DRIVER_VERSION "v1.9.0.2" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ @@ -57,6 +57,8 @@ static DEFINE_MUTEX(g_aw882xx_lock); struct aw_container *g_awinic_cfg = NULL; +#define AW882XX_MOTO_MAX_GAIN (127) + #define AW882XX_SCENE_CALIBRATION_ID 0 #define AW882XX_SCENE_BYPASS_ID 1 #define AW882XX_SCENE_HANDSET_ID 2 @@ -65,7 +67,7 @@ struct aw_container *g_awinic_cfg = NULL; #define AW882XX_SCENE_DEEPBUFFER_ID 5 #define AW882XX_SCENE_FASTTRACK_ID 6 - +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS struct aw882xx_scene_info aw882xx_scene_state[AW_ALGO_PROFILE_ID_MAX] = { { AW882XX_SCENE_CALIBRATION_ID, @@ -124,6 +126,7 @@ struct aw882xx_scene_info aw882xx_scene_state[AW_ALGO_PROFILE_ID_MAX] = { 0, }, }; +#endif static const char *const switch_status[] = { "Off", "On" }; static const char *const aw882xx_ramp_status[] = { "Off", "On" }; @@ -739,14 +742,80 @@ static int aw882xx_ramp_status_set(struct snd_kcontrol *kcontrol, aw_dev_dbg(aw882xx->dev, "%s: ucontrol->value.integer.value[0]=%ld\n", __func__, ucontrol->value.integer.value[0]); - + *ramp_status = ucontrol->value.integer.value[0]; if (*ramp_status == AW882XX_RAMP_ON) aw882xx->aw882xx_ramp_status = AW882XX_RAMP_ON; else if (*ramp_status == AW882XX_RAMP_OFF) aw882xx->aw882xx_ramp_status = AW882XX_RAMP_OFF; - + + return 0; +} + + +int aw882xx_dev_gain_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = AW882XX_MOTO_MAX_GAIN; + return 0; +} + +/* aw882xx gain ctl */ +static int aw882xx_dev_gain_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw882xx->aw_pa; + unsigned int volume = 0; + aw_dev->ops.aw_get_volume(aw_dev, &volume); + + ucontrol->value.integer.value[0] = aw_dev->cur_gain; + + aw_dev_info(aw882xx->dev, "cur gain = %d, volume = %d", + aw_dev->cur_gain, volume); + + return 0; +} + +static int aw882xx_dev_gain_ctl_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw882xx->aw_pa; + struct aw_volume_desc *desc = &aw_dev->volume_desc; + + int value = 0; + int aw882xx_volume = 0; + + /* Note: The larger the volume value is, the smaller the actual volume */ + int useful_range = desc->mute_volume - desc->init_volume; + + aw_dev_info(aw882xx->dev, "desc->init_volume = %d mute_volume = %d", + desc->init_volume, desc->mute_volume ); + + if (ucontrol->value.integer.value[0] > AW882XX_MOTO_MAX_GAIN) { + aw_dev_err(aw882xx->dev, "set val %ld overflow %d", + ucontrol->value.integer.value[0], AW882XX_MOTO_MAX_GAIN); + return 0; + } + + aw_dev->cur_gain = ucontrol->value.integer.value[0]; + + /* hal gain map to aw882xx valume value */ + value = ucontrol->value.integer.value[0]; + aw882xx_volume = ((AW882XX_MOTO_MAX_GAIN - value) * useful_range) / AW882XX_MOTO_MAX_GAIN + + desc->init_volume; + + aw_dev_info(aw882xx->dev,"set value = %d, set aw882xx volume = %d", value, aw882xx_volume); + aw_dev->ops.aw_set_volume(aw_dev, aw882xx_volume); + return 0; } @@ -755,7 +824,7 @@ static int aw882xx_dynamic_create_controls(struct aw882xx *aw882xx) struct snd_kcontrol_new *aw882xx_dev_control = NULL; char *kctl_name; - aw882xx_dev_control = devm_kzalloc(aw882xx->codec->dev, sizeof(struct snd_kcontrol_new) * 3, GFP_KERNEL); + aw882xx_dev_control = devm_kzalloc(aw882xx->codec->dev, sizeof(struct snd_kcontrol_new) * 4, GFP_KERNEL); if (aw882xx_dev_control == NULL) { aw_dev_err(aw882xx->codec->dev, "kcontrol malloc failed!"); return -ENOMEM; @@ -784,7 +853,7 @@ static int aw882xx_dynamic_create_controls(struct aw882xx *aw882xx) aw882xx_dev_control[1].info = aw882xx_switch_info; aw882xx_dev_control[1].get = aw882xx_switch_get; aw882xx_dev_control[1].put = aw882xx_switch_set; - + //ramp control kctl_name = devm_kzalloc(aw882xx->codec->dev, AW_NAME_BUF_MAX, GFP_KERNEL); if (!kctl_name) @@ -798,8 +867,22 @@ static int aw882xx_dynamic_create_controls(struct aw882xx *aw882xx) aw882xx_dev_control[2].get = aw882xx_ramp_status_get; aw882xx_dev_control[2].put = aw882xx_ramp_status_set; //end ramp control + + + kctl_name = devm_kzalloc(aw882xx->codec->dev, AW_NAME_BUF_MAX, GFP_KERNEL); + if (!kctl_name) + return -ENOMEM; + + snprintf(kctl_name, AW_NAME_BUF_MAX, "aw_dev_%d_gain_ctl", aw882xx->index); + + aw882xx_dev_control[3].name = kctl_name; + aw882xx_dev_control[3].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw882xx_dev_control[3].info = aw882xx_dev_gain_ctl_info; + aw882xx_dev_control[3].get = aw882xx_dev_gain_ctl_get; + aw882xx_dev_control[3].put = aw882xx_dev_gain_ctl_set; + aw_componet_codec_ops.add_codec_controls(aw882xx->codec, - aw882xx_dev_control, 3); + aw882xx_dev_control, 4); return 0; } @@ -1216,51 +1299,6 @@ static int aw882xx_get_spin(struct snd_kcontrol *kcontrol, } #endif - -static int aw882xx_set_prof_id(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct aw_device *aw_dev; - aw_snd_soc_codec_t *codec = - aw_componet_codec_ops.kcontrol_codec(kcontrol); - struct aw882xx *aw882xx = - aw_componet_codec_ops.codec_get_drvdata(codec); - int ctrl_value; - - aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", - ucontrol->value.integer.value[0]); - - aw_dev = aw882xx->aw_pa; - - ctrl_value = ucontrol->value.integer.value[0]; - - aw_dev_set_algo_prof(aw_dev, ctrl_value); - - return 0; -} -static int aw882xx_get_prof_id(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct aw_device *aw_dev; - aw_snd_soc_codec_t *codec = - aw_componet_codec_ops.kcontrol_codec(kcontrol); - struct aw882xx *aw882xx = - aw_componet_codec_ops.codec_get_drvdata(codec); - int ctrl_value; - - aw_dev = aw882xx->aw_pa; - - aw_dev_get_algo_prof(aw_dev, &ctrl_value); - - ucontrol->value.integer.value[0] = ctrl_value; - - aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", - ucontrol->value.integer.value[0]); - - return 0; -} - - static int aw882xx_get_fade_in_time(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1323,88 +1361,6 @@ static int aw882xx_set_fade_out_time(struct snd_kcontrol *kcontrol, return 0; } -static int aw882xx_0_ramp_step_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - unsigned int time; - - aw_dev_get_fade_time(&time, true); - ucontrol->value.integer.value[0] = time; - - aw_pr_dbg("aw_dev_ch_pri_l_fadein_time time %ld", ucontrol->value.integer.value[0]); - - return 0; -} - -static int aw882xx_0_ramp_step_set(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - aw_snd_soc_codec_t *codec = - aw_componet_codec_ops.kcontrol_codec(kcontrol); - struct aw882xx *aw882xx = - aw_componet_codec_ops.codec_get_drvdata(codec); - struct aw_device *aw_dev = aw882xx->aw_pa; - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int channel = aw_dev->channel; - - if (ucontrol->value.integer.value[0] > mc->max) { - aw_pr_dbg("set val %ld overflow %d", - ucontrol->value.integer.value[0], mc->max); - return 0; - } - - if (aw882xx->aw882xx_ramp_status == AW882XX_RAMP_ON && channel == AW_DEV_CH_PRI_L) - aw_dev_set_fade_time(ucontrol->value.integer.value[0], true); - else if (aw882xx->aw882xx_ramp_status == AW882XX_RAMP_OFF) - aw_dev_info(aw882xx->dev, - "%s: fade status is off,time can not set\n", __func__); - - aw_pr_dbg("step time %ld", ucontrol->value.integer.value[0]); - return 0; -} - -static int aw882xx_1_ramp_step_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - unsigned int time; - - aw_dev_get_fade_time(&time, true); - ucontrol->value.integer.value[0] = time; - - aw_pr_dbg("aw_dev_ch_pri_r_fadein_time time %ld", ucontrol->value.integer.value[0]); - - return 0; -} - -static int aw882xx_1_ramp_step_set(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - aw_snd_soc_codec_t *codec = - aw_componet_codec_ops.kcontrol_codec(kcontrol); - struct aw882xx *aw882xx = - aw_componet_codec_ops.codec_get_drvdata(codec); - struct aw_device *aw_dev = aw882xx->aw_pa; - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int channel = aw_dev->channel; - - if (ucontrol->value.integer.value[0] > mc->max) { - aw_pr_dbg("set val %ld overflow %d", - ucontrol->value.integer.value[0], mc->max); - return 0; - } - - if (aw882xx->aw882xx_ramp_status == AW882XX_RAMP_ON && channel == AW_DEV_CH_PRI_L) - aw_dev_set_fade_time(ucontrol->value.integer.value[0], true); - else if (aw882xx->aw882xx_ramp_status == AW882XX_RAMP_OFF) - aw_dev_info(aw882xx->dev, - "%s: fade status is off,time can not set\n", __func__); - - aw_pr_dbg("step time %ld", ucontrol->value.integer.value[0]); - return 0; -} - #ifdef AW882XX_RUNIN_TEST static int aw882xx_runin_test_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1458,110 +1414,6 @@ static void aw882xx_set_adsp_module_status(struct work_struct *work) } #endif - -static int aw882xx_get_algo_prof_id_by_scene_st(struct aw882xx *aw882xx) -{ - int index = -1; - int i = 0; - - /* Get the highest priority array subscript from all active scene */ - for (i = 0; i < AW_ALGO_PROFILE_ID_MAX; i++) { - if (aw882xx_scene_state[i].is_active) { - if (index < 0) { - index = i; - } else { - if(aw882xx_scene_state[i].priority < - aw882xx_scene_state[index].priority) { - index = i; - } - } - } - } - - if (index >= 0) { - return aw882xx_scene_state[index].skt_profile_id; - } - - return AW_ALGO_PROFILE_ID_MAX; -} - -static void aw882xx_update_algo_scene_st(struct aw882xx *aw882xx, - int scene_id, bool is_active) { - struct aw882xx_scene_info *p_aw882xx_scene_st = - &aw882xx_scene_state[scene_id]; - - if (is_active) { - p_aw882xx_scene_st->active_cnt++; - p_aw882xx_scene_st->is_active = 1; - } else { - p_aw882xx_scene_st->active_cnt--; - if (p_aw882xx_scene_st->active_cnt < 0) { - p_aw882xx_scene_st->active_cnt = 0; - } - if (p_aw882xx_scene_st->active_cnt == 0) { - p_aw882xx_scene_st->is_active = 0; - } - } -} - - -static int aw882xx_update_algo_profile(struct aw882xx *aw882xx) -{ - int ret = 0; - int new_skt_prof_id = 0; - int cur_skt_prof_id = aw882xx->cur_algo_prof_id; - struct aw_device *aw_dev = NULL; - aw_dev_info(aw882xx->dev, "enter"); - - new_skt_prof_id = aw882xx_get_algo_prof_id_by_scene_st(aw882xx); - aw_dev_dbg(aw882xx->dev, "new_skt_prof_id = %d,cur_skt_prof_id=%d\n", - new_skt_prof_id, - cur_skt_prof_id); - - if (new_skt_prof_id > AW_ALGO_PROFILE_ID_MAX) { - /* no active scene */ - aw_dev_info(aw882xx->dev, "all scene disactive,do nothing"); - return 0; - } - - if (cur_skt_prof_id != new_skt_prof_id) { - aw_dev_info(aw882xx->dev, "algo scene switch. [new] %d,[old] %d", - new_skt_prof_id, cur_skt_prof_id); - aw882xx->cur_algo_prof_id = new_skt_prof_id; - aw_dev = aw882xx->aw_pa; - - /* set new scene pramas to skt */ - ret = aw_dev_set_algo_prof(aw_dev, aw882xx->cur_algo_prof_id); - if (ret < 0) { - aw_dev_err(aw882xx->dev, "set algo prof failed"); - return -1; - } - } else { - aw_dev_info(aw882xx->dev, "algo scene hold, cur scene %d", - new_skt_prof_id); - } - - return 0; - -} - - -static int aw882xx_algo_dump_scene_st(struct aw882xx *aw882xx) -{ - int i = 0; - - aw_dev_info(aw882xx->dev, "aw882xx cur algo prof id =%d", - aw882xx->cur_algo_prof_id); - for (i = 0; i < AW_ALGO_PROFILE_ID_MAX; i++) { - aw_dev_info(aw882xx->dev, "scene[%s] %s active_cnt %d", - aw882xx_scene_state[i].name, - aw882xx_scene_state[i].is_active? "[active]" : "[disactive]", - aw882xx_scene_state[i].active_cnt); - } - return 0; -} - - static int aw882xx_monitor_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1603,16 +1455,166 @@ static int aw882xx_monitor_set(struct snd_kcontrol *kcontrol, return 0; } +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS +static int aw882xx_set_prof_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int ctrl_value; + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + aw_dev = aw882xx->aw_pa; + + ctrl_value = ucontrol->value.integer.value[0]; + + aw_dev_set_algo_prof(aw_dev, ctrl_value); + + return 0; +} +static int aw882xx_get_prof_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int ctrl_value; + int ret = -EINVAL; + + aw_dev = aw882xx->aw_pa; + + if (aw882xx->pstream) { + ret = aw_dev_get_algo_prof(aw_dev, &ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "get algo prof id failed!, ret = %d", ret); + ctrl_value = 0; + } + ucontrol->value.integer.value[0] = ctrl_value; + } else { + aw_dev_info(aw882xx->dev, "no stream, get algo prof failed"); + } + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + return 0; +} + +static int aw882xx_get_algo_prof_id_by_scene_st(struct aw882xx *aw882xx) +{ + int index = -1; + int i = 0; + + /* Get the highest priority array subscript from all active scene */ + for (i = 0; i < AW_ALGO_PROFILE_ID_MAX; i++) { + if (aw882xx_scene_state[i].is_active) { + if (index < 0) { + index = i; + } else { + if(aw882xx_scene_state[i].priority < + aw882xx_scene_state[index].priority) { + index = i; + } + } + } + } + + if (index >= 0) { + return aw882xx_scene_state[index].skt_profile_id; + } + + return AW_ALGO_PROFILE_ID_0; +} + +static void aw882xx_update_algo_scene_st(struct aw882xx *aw882xx, + int scene_id, bool is_active) { + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[scene_id]; + + if (is_active) { + p_aw882xx_scene_st->active_cnt++; + p_aw882xx_scene_st->is_active = 1; + } else { + p_aw882xx_scene_st->active_cnt--; + if (p_aw882xx_scene_st->active_cnt < 0) { + p_aw882xx_scene_st->active_cnt = 0; + } + if (p_aw882xx_scene_st->active_cnt == 0) { + p_aw882xx_scene_st->is_active = 0; + } + } +} + + +static int aw882xx_update_algo_profile(struct aw882xx *aw882xx) +{ + int ret = 0; + int new_skt_prof_id = 0; + int cur_skt_prof_id = aw882xx->cur_algo_prof_id; + struct aw_device *aw_dev = NULL; + + aw_dev_info(aw882xx->dev, "enter"); + + new_skt_prof_id = aw882xx_get_algo_prof_id_by_scene_st(aw882xx); + if (new_skt_prof_id > AW_ALGO_PROFILE_ID_MAX) { + /* no active scene */ + aw_dev_info(aw882xx->dev, "all scene disactive,do nothing"); + return 0; + } + + if (cur_skt_prof_id != new_skt_prof_id) { + aw_dev_info(aw882xx->dev, "algo scene switch. [new] %d,[old] %d", + new_skt_prof_id, cur_skt_prof_id); + aw882xx->cur_algo_prof_id = new_skt_prof_id; + aw_dev = aw882xx->aw_pa; + + /* set new scene pramas to skt */ + ret = aw_dev_set_algo_prof(aw_dev, aw882xx->cur_algo_prof_id); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "set algo prof failed"); + return -1; + } + } else { + aw_dev_info(aw882xx->dev, "algo scene hold, cur scene %d", + new_skt_prof_id); + } + + return 0; + +} + +static int aw882xx_algo_dump_scene_st(struct aw882xx *aw882xx) +{ + int i = 0; + + aw_dev_info(aw882xx->dev, "aw882xx cur algo prof id =%d", + aw882xx->cur_algo_prof_id); + for (i = 0; i < AW_ALGO_PROFILE_ID_MAX; i++) { + aw_dev_info(aw882xx->dev, "scene[%s] %s active_cnt %d", + aw882xx_scene_state[i].name, + aw882xx_scene_state[i].is_active? "[active]" : "[disactive]", + aw882xx_scene_state[i].active_cnt); + } + return 0; +} + static int aw882xx_get_algo_cali_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); - struct aw882xx_scene_info *p_aw882xx_scene_st = + struct aw882xx_scene_info *p_aw882xx_scene_st = &aw882xx_scene_state[AW882XX_SCENE_CALIBRATION_ID]; aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", - __func__, + __func__, p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", p_aw882xx_scene_st->active_cnt); ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; @@ -1646,10 +1648,10 @@ static int aw882xx_get_algo_bypass_switch(struct snd_kcontrol *kcontrol, struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); - struct aw882xx_scene_info *p_aw882xx_scene_st = + struct aw882xx_scene_info *p_aw882xx_scene_st = &aw882xx_scene_state[AW882XX_SCENE_BYPASS_ID]; aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", - __func__, + __func__, p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", p_aw882xx_scene_st->active_cnt); ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; @@ -1681,11 +1683,11 @@ static int aw882xx_get_algo_handset_switch(struct snd_kcontrol *kcontrol, struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); - struct aw882xx_scene_info *p_aw882xx_scene_st = + struct aw882xx_scene_info *p_aw882xx_scene_st = &aw882xx_scene_state[AW882XX_SCENE_HANDSET_ID]; aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", - __func__, + __func__, p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", p_aw882xx_scene_st->active_cnt); ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; @@ -1720,11 +1722,11 @@ static int aw882xx_get_algo_voice_switch(struct snd_kcontrol *kcontrol, struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); - struct aw882xx_scene_info *p_aw882xx_scene_st = + struct aw882xx_scene_info *p_aw882xx_scene_st = &aw882xx_scene_state[AW882XX_SCENE_VOICE_ID]; aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", - __func__, + __func__, p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", p_aw882xx_scene_st->active_cnt); ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; @@ -1750,7 +1752,6 @@ static int aw882xx_set_algo_voice_switch(struct snd_kcontrol *kcontrol, aw882xx_algo_dump_scene_st(aw882xx); - return 0; } @@ -1761,11 +1762,11 @@ static int aw882xx_get_algo_voip_switch(struct snd_kcontrol *kcontrol, struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); - struct aw882xx_scene_info *p_aw882xx_scene_st = + struct aw882xx_scene_info *p_aw882xx_scene_st = &aw882xx_scene_state[AW882XX_SCENE_VOIP_ID]; aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", - __func__, + __func__, p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", p_aw882xx_scene_st->active_cnt); ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; @@ -1799,11 +1800,11 @@ static int aw882xx_get_algo_music_deepbuffer_switch(struct snd_kcontrol *kcontro struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); - struct aw882xx_scene_info *p_aw882xx_scene_st = + struct aw882xx_scene_info *p_aw882xx_scene_st = &aw882xx_scene_state[AW882XX_SCENE_DEEPBUFFER_ID]; aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", - __func__, + __func__, p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", p_aw882xx_scene_st->active_cnt); ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; @@ -1837,11 +1838,11 @@ static int aw882xx_get_algo_music_fasttrack_switch(struct snd_kcontrol *kcontrol struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); - struct aw882xx_scene_info *p_aw882xx_scene_st = + struct aw882xx_scene_info *p_aw882xx_scene_st = &aw882xx_scene_state[AW882XX_SCENE_FASTTRACK_ID]; aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", - __func__, + __func__, p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", p_aw882xx_scene_st->active_cnt); ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; @@ -1868,6 +1869,7 @@ static int aw882xx_set_algo_music_fasttrack_switch(struct snd_kcontrol *kcontrol return 0; } +#endif static const struct soc_enum aw882xx_snd_enum[] = { SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(aw882xx_switch), aw882xx_switch), @@ -1898,12 +1900,11 @@ static struct snd_kcontrol_new aw882xx_controls[] = { aw882xx_get_fade_in_time, aw882xx_set_fade_in_time), SOC_SINGLE_EXT("aw882xx_fadeout_us", 0, 0, 1000000, 0, aw882xx_get_fade_out_time, aw882xx_set_fade_out_time), +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS SOC_SINGLE_EXT("aw882xx_algo_prof_switch", 0, 0, 100, 0, aw882xx_get_prof_id, aw882xx_set_prof_id), - SOC_SINGLE_EXT("aw_dev_0_ramp_step_switch", 0, 0, 1000000, 0, - aw882xx_0_ramp_step_get, aw882xx_0_ramp_step_set), - SOC_SINGLE_EXT("aw_dev_1_ramp_step_switch", 0, 0, 1000000, 0, - aw882xx_1_ramp_step_get, aw882xx_1_ramp_step_set), + + /* for skt scene param switch */ SOC_ENUM_EXT("aw882xx_algo_cali_switch", aw882xx_snd_enum[1], aw882xx_get_algo_cali_switch, aw882xx_set_algo_cali_switch), SOC_ENUM_EXT("aw882xx_algo_bypass_switch", aw882xx_snd_enum[1], @@ -1918,6 +1919,7 @@ static struct snd_kcontrol_new aw882xx_controls[] = { aw882xx_get_algo_music_deepbuffer_switch, aw882xx_set_algo_music_deepbuffer_switch), SOC_ENUM_EXT("aw882xx_algo_music_fasttrack_switch", aw882xx_snd_enum[1], aw882xx_get_algo_music_fasttrack_switch, aw882xx_set_algo_music_fasttrack_switch), +#endif }; static void aw882xx_add_codec_controls(struct aw882xx *aw882xx) @@ -2985,7 +2987,7 @@ static int aw882xx_i2c_probe(struct i2c_client *i2c, aw882xx->i2c_packet.reg_data = NULL; aw882xx->index = g_aw882xx_dev_cnt; - + aw882xx->aw882xx_ramp_status = AW882XX_RAMP_OFF; #ifdef AW882XX_RUNIN_TEST g_runin_test = 0; diff --git a/asoc/codecs/aw88261/aw882xx.h b/asoc/codecs/aw88261/aw882xx.h index d235668550f2..be576beed982 100644 --- a/asoc/codecs/aw88261/aw882xx.h +++ b/asoc/codecs/aw88261/aw882xx.h @@ -15,6 +15,7 @@ #define AW882XX_LOAD_FW_DELAY_TIME (0) #define AW_START_RETRIES (5) #define AW882XX_RUNIN_TEST +#define CONFIG_AW882XX_ALGO_BIN_PARAMS #define AW_I2C_RETRIES 5 /* 5 times */ #define AW_I2C_RETRY_DELAY 5 /* 5 ms */ @@ -24,9 +25,6 @@ #define AW882XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE) - -#define AW882XX_ALGO_SET_PROF_ENABLE 1 -#define AW882XX_ALGO_SET_PROF_DISABLE 0 enum { AW882XX_STREAM_CLOSE = 0, @@ -52,6 +50,7 @@ enum aw882xx_int_type { INT_TYPE_OTHI = 0x8, }; +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS enum { AW_ALGO_PROFILE_ID_0 = 0, AW_ALGO_PROFILE_ID_1, @@ -71,6 +70,7 @@ struct aw882xx_scene_info { int is_active; int active_cnt; }; +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 1) @@ -139,6 +139,7 @@ enum { AW882XX_RAMP_ON, }; +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS enum { AW_ALGO_CALI_OFF = 0, AW_ALGO_CALI_ON, @@ -173,7 +174,7 @@ enum { AW_ALGO_MUSIC_FASTTRACK_OFF = 0, AW_ALGO_MUSIC_FASTTRACK_ON, }; - +#endif enum { @@ -201,6 +202,7 @@ struct aw882xx { int pstream; int cstream; int aw882xx_ramp_status; /* ramp status */ +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS int aw882xx_algo_cali; int aw882xx_algo_bypass; int aw882xx_algo_handset; @@ -208,9 +210,9 @@ struct aw882xx { int aw882xx_algo_voip; int aw882xx_algo_music_deepbuffer; int aw882xx_algo_music_fasttrack; - int aw882xx_algo_prof_id; int cur_algo_prof_id; +#endif unsigned int fade_flag; diff --git a/asoc/codecs/aw88261/aw_device.c b/asoc/codecs/aw88261/aw_device.c index 251fad2f0f0a..6e1a44747a5a 100644 --- a/asoc/codecs/aw88261/aw_device.c +++ b/asoc/codecs/aw88261/aw_device.c @@ -40,10 +40,7 @@ static char *profile_name[AW_PROFILE_MAX] = { static char ext_dsp_prof_write = AW_EXT_DSP_WRITE_NONE; static DEFINE_MUTEX(g_ext_dsp_prof_wr_lock); /*lock ext wr flag*/ -static unsigned int aw_dev_ch_pri_l_fadein_time = AW_1000_US / 10; -static unsigned int aw_dev_ch_pri_r_fadein_time = AW_1000_US / 10; -static unsigned int aw_dev_ch_sec_l_fadein_time = AW_1000_US / 10; -static unsigned int aw_dev_ch_sec_r_fadein_time = AW_1000_US / 10; +static unsigned int g_fade_in_time = AW_1000_US / 10; static unsigned int g_fade_out_time = AW_1000_US >> 1; static LIST_HEAD(g_dev_list); @@ -688,70 +685,18 @@ static void aw_dev_fade_in(struct aw_device *aw_dev) struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; int fade_flag = aw882xx->fade_flag; struct aw_volume_desc *desc = &aw_dev->volume_desc; - int channel = aw_dev->channel; - - switch (channel) { - case AW_DEV_CH_PRI_L: - if (fade_step == 0 || aw_dev_ch_pri_l_fadein_time == 0 || fade_flag == 0) { - aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); - return; - } - - /*volume up*/ - for (i = desc->mute_volume; i >= desc->init_volume; i -= fade_step) { - aw_dev->ops.aw_set_volume(aw_dev, i); - usleep_range(aw_dev_ch_pri_l_fadein_time, aw_dev_ch_pri_l_fadein_time + 10); - } - if (i != desc->init_volume) - aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); - break; - case AW_DEV_CH_PRI_R: - if (fade_step == 0 || aw_dev_ch_pri_r_fadein_time == 0 || fade_flag == 0) { - aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); - return; - } - - /*volume up*/ - for (i = desc->mute_volume; i >= desc->init_volume; i -= fade_step) { - aw_dev->ops.aw_set_volume(aw_dev, i); - usleep_range(aw_dev_ch_pri_r_fadein_time, aw_dev_ch_pri_r_fadein_time + 10); - } - if (i != desc->init_volume) - aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); - break; - case AW_DEV_CH_SEC_L: - if (fade_step == 0 || aw_dev_ch_sec_l_fadein_time == 0 || fade_flag == 0) { - aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); - return; - } - - /*volume up*/ - for (i = desc->mute_volume; i >= desc->init_volume; i -= fade_step) { - aw_dev->ops.aw_set_volume(aw_dev, i); - usleep_range(aw_dev_ch_sec_l_fadein_time, aw_dev_ch_sec_l_fadein_time + 10); - } - if (i != desc->init_volume) - aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); - break; - case AW_DEV_CH_SEC_R: - if (fade_step == 0 || aw_dev_ch_sec_r_fadein_time == 0 || fade_flag == 0) { - aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); - return; - } - - /*volume up*/ - for (i = desc->mute_volume; i >= desc->init_volume; i -= fade_step) { - aw_dev->ops.aw_set_volume(aw_dev, i); - usleep_range(aw_dev_ch_sec_r_fadein_time, aw_dev_ch_sec_r_fadein_time + 10); - } - if (i != desc->init_volume) - aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); - break; - default: - aw_dev_err(aw882xx->dev, - "%s: fade time can not set\n", __func__); - break; + + if (fade_step == 0 || g_fade_in_time == 0 || fade_flag == 0) { + aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); + return; } + /*volume up*/ + for (i = desc->mute_volume; i >= desc->init_volume; i -= fade_step) { + aw_dev->ops.aw_set_volume(aw_dev, i); + usleep_range(g_fade_in_time, g_fade_in_time + 10); + } + if (i != desc->init_volume) + aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); } static void aw_dev_fade_out(struct aw_device *aw_dev) @@ -834,11 +779,7 @@ static void aw_dev_mute(struct aw_device *aw_dev, bool mute) mute_desc->disable); aw_dev_fade_in(aw_dev); } else if (aw882xx->aw882xx_ramp_status == AW882XX_RAMP_OFF) { - aw_dev_ch_pri_l_fadein_time = AW_1000_US / 10; - aw_dev_ch_pri_r_fadein_time = AW_1000_US / 10; - aw_dev_ch_sec_l_fadein_time = AW_1000_US / 10; - aw_dev_ch_sec_r_fadein_time = AW_1000_US / 10; - + g_fade_in_time = AW_1000_US / 10; aw_dev->ops.aw_i2c_write_bits(aw_dev, mute_desc->reg, mute_desc->mask, mute_desc->disable); @@ -1167,59 +1108,17 @@ void aw_dev_set_fade_vol_step(struct aw_device *aw_dev, unsigned int step) void aw_dev_get_fade_time(unsigned int *time, bool fade_in) { - struct aw_device *aw_dev; - struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; - int channel = aw_dev->channel; - - if (fade_in) { - switch (channel) { - case AW_DEV_CH_PRI_L: - *time = aw_dev_ch_pri_l_fadein_time; - break; - case AW_DEV_CH_PRI_R: - *time = aw_dev_ch_pri_r_fadein_time; - break; - case AW_DEV_CH_SEC_L: - *time = aw_dev_ch_sec_l_fadein_time; - break; - case AW_DEV_CH_SEC_R: - *time = aw_dev_ch_sec_r_fadein_time; - break; - default: - aw_dev_err(aw882xx->dev, - "%s: fade time can not set\n", __func__); - break; - } - } else + if (fade_in) + *time = g_fade_in_time; + else *time = g_fade_out_time; } void aw_dev_set_fade_time(unsigned int time, bool fade_in) { - struct aw_device *aw_dev; - struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; - int channel = aw_dev->channel; - - if (fade_in) { - switch (channel) { - case AW_DEV_CH_PRI_L: - aw_dev_ch_pri_l_fadein_time = time; - break; - case AW_DEV_CH_PRI_R: - aw_dev_ch_pri_r_fadein_time = time; - break; - case AW_DEV_CH_SEC_L: - aw_dev_ch_sec_l_fadein_time = time; - break; - case AW_DEV_CH_SEC_R: - aw_dev_ch_sec_r_fadein_time = time; - break; - default: - aw_dev_err(aw882xx->dev, - "%s: fade time can not set\n", __func__); - break; - } - }else + if (fade_in) + g_fade_in_time = time; + else g_fade_out_time = time; } diff --git a/asoc/codecs/aw88261/aw_device.h b/asoc/codecs/aw88261/aw_device.h index 7b7315d24555..0629d9e71968 100644 --- a/asoc/codecs/aw88261/aw_device.h +++ b/asoc/codecs/aw88261/aw_device.h @@ -211,6 +211,8 @@ struct aw_device { unsigned int re_max; unsigned int re_min; + int cur_gain; + struct device *dev; struct i2c_client *i2c; char acf_name[AW_NAME_MAX]; diff --git a/asoc/codecs/aw88261/aw_init.c b/asoc/codecs/aw88261/aw_init.c index 7198290a535a..dfc112442d92 100644 --- a/asoc/codecs/aw88261/aw_init.c +++ b/asoc/codecs/aw88261/aw_init.c @@ -1062,7 +1062,12 @@ static int aw_pid_2055_dev_init(struct aw882xx *aw882xx, int index) aw_pa->soft_rst.reg = AW_PID_2055_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; - + + usleep_range(AW_2000_US, AW_2000_US +10); + aw_pa->ops.aw_i2c_write(aw_pa, AW_PID_2055_INIT_CHECK_REG, + AW_PID_2055_INIT_CHECK_VALUE); + usleep_range(AW_3000_US, AW_3000_US +10); + usleep_range(AW_2000_US, AW_2000_US +10); aw_pa->ops.aw_i2c_write(aw_pa, AW_PID_2055_INIT_CHECK_REG, AW_PID_2055_INIT_CHECK_VALUE); @@ -1629,12 +1634,12 @@ static int aw_pid_2113_dev_init(struct aw882xx *aw882xx, int index) aw_pa->soft_rst.reg = AW_PID_2113_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; - + usleep_range(AW_2000_US, AW_2000_US +10); aw_pa->ops.aw_i2c_write(aw_pa, AW_PID_2113_INIT_CHECK_REG, AW_PID_2113_INIT_CHECK_VALUE); usleep_range(AW_3000_US, AW_3000_US +10); - + aw_pid_2113_efver_check(aw_pa); ret = aw_device_probe(aw_pa); From 55e843eee5ab1585573344c737062affbbfc4f62 Mon Sep 17 00:00:00 2001 From: tinno16 Date: Sat, 4 Dec 2021 12:30:34 +0800 Subject: [PATCH 16/40] hawaii OLED: hawao audio bringup hawao audio driver bringup audio-kernel add hawao audio kernel config for aw88261 driver Change-Id: If2042ac150d89495a756baaa6ae744d540179eab Reviewed-on: https://gerrit.mot.com/2135169 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Submit-Approved: Jira Key Tested-by: Jira Key Reviewed-by: Reviewed-by: Shengchun Li (cherry picked from commit 4e3e26abb5bdc395797ec7b3680d8e500ed66a0c) Reviewed-on: https://gerrit.mot.com/2144807 Reviewed-by: Yunfei Guo Reviewed-by: Hujun Liao --- asoc/Kbuild | 4 ++++ asoc/bengal.c | 8 ++++++++ asoc/codecs/Kbuild | 6 +++++- config/hawaoautoconf.h | 6 ++++++ 4 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 config/hawaoautoconf.h diff --git a/asoc/Kbuild b/asoc/Kbuild index 28f5d7d2266f..4163a09c56f7 100644 --- a/asoc/Kbuild +++ b/asoc/Kbuild @@ -100,6 +100,10 @@ ifeq ($(KERNEL_BUILD), 0) INCS += -include $(AUDIO_ROOT)/config/rhodeautoconf.h endif + ifneq (,$(filter hawao%, $(TARGET_PRODUCT))) + INCS += -include $(AUDIO_ROOT)/config/hawaoautoconf.h + endif + endif # As per target team, build is done as follows: diff --git a/asoc/bengal.c b/asoc/bengal.c index 28311f423717..aa6bfdaadf81 100644 --- a/asoc/bengal.c +++ b/asoc/bengal.c @@ -5439,11 +5439,19 @@ static struct snd_soc_dai_link_component awinic_codecs[] = { .dai_name = "aw882xx-aif-1-34", .name = "aw882xxacf_smartpa.1-0034", }, +#ifdef CONFIG_HAWAO_AW882XX_SMARTPA +{ + .of_node = NULL, + .dai_name = "aw882xx-aif-1-35", + .name = "aw882xxacf_smartpa.1-0035", + }, +#else { .of_node = NULL, .dai_name = "aw882xx-aif-1-37", .name = "aw882xxacf_smartpa.1-0037", }, +#endif }; static struct snd_soc_dai_link msm_stereo_awinic_be_dai_links[] = { diff --git a/asoc/codecs/Kbuild b/asoc/codecs/Kbuild index fffbc368f46b..9af58e0d7cfe 100644 --- a/asoc/codecs/Kbuild +++ b/asoc/codecs/Kbuild @@ -93,6 +93,10 @@ ifeq ($(KERNEL_BUILD), 0) ifneq (,$(filter rhode%, $(TARGET_PRODUCT))) INCS += -include $(AUDIO_ROOT)/config/rhodeautoconf.h endif + + ifneq (,$(filter hawao%, $(TARGET_PRODUCT))) + INCS += -include $(AUDIO_ROOT)/config/hawaoautoconf.h + endif endif # As per target team, build is done as follows: @@ -291,7 +295,7 @@ hdmi_dlkm-y := $(HDMICODEC_OBJS) #Add awinic driver begain ifdef CONFIG_SND_SMARTPA_AW882XX -ifneq (,$(filter rhode%, $(TARGET_PRODUCT))) +ifneq (,$(filter rhode% hawao%, $(TARGET_PRODUCT))) SND_SOC_AW882XX_OBJS += aw88261/aw882xx.o SND_SOC_AW882XX_OBJS += aw88261/aw_init.o SND_SOC_AW882XX_OBJS += aw88261/aw_bin_parse.o diff --git a/config/hawaoautoconf.h b/config/hawaoautoconf.h new file mode 100644 index 000000000000..91cf4167b34e --- /dev/null +++ b/config/hawaoautoconf.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + */ +#define CONFIG_AW882XX_STEREO_SMARTPA 1 +#define CONFIG_AW882XX_PARAMS_STORED_IN_BIN 1 +#define CONFIG_HAWAO_AW882XX_SMARTPA 1 From 0bd5ad5e362560e105755d2111e1d3cb226fdcd6 Mon Sep 17 00:00:00 2001 From: tinno16 Date: Mon, 6 Dec 2021 20:51:21 +0800 Subject: [PATCH 17/40] hawaii OLED: hawao audio bringup hawao audio driver bringup smartpa calibration add Change-Id: Ic7b61ea858abeeaff5c4c482740b29f6f0db87d3 Reviewed-on: https://gerrit.mot.com/2136276 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Submit-Approved: Jira Key Tested-by: Jira Key Reviewed-by: Yunfei Guo (cherry picked from commit f20f799cccbcedface54a69fdeae35bd5f97ef36) Reviewed-on: https://gerrit.mot.com/2144882 Reviewed-by: Reviewed-by: Hujun Liao --- asoc/codecs/wcd937x/Kbuild | 4 ++++ dsp/Kbuild | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/asoc/codecs/wcd937x/Kbuild b/asoc/codecs/wcd937x/Kbuild index 775568186f04..5a4c10c45f0a 100644 --- a/asoc/codecs/wcd937x/Kbuild +++ b/asoc/codecs/wcd937x/Kbuild @@ -39,6 +39,10 @@ ifeq ($(KERNEL_BUILD), 0) ifneq (,$(filter rhode%, $(TARGET_PRODUCT))) INCS += -include $(AUDIO_ROOT)/config/rhodeautoconf.h endif + ifneq (,$(filter hawao%, $(TARGET_PRODUCT))) + INCS += -include $(AUDIO_ROOT)/config/hawaoautoconf.h + endif + endif # As per target team, build is done as follows: diff --git a/dsp/Kbuild b/dsp/Kbuild index 41f6d4951b9d..4e9e82aa4197 100644 --- a/dsp/Kbuild +++ b/dsp/Kbuild @@ -99,6 +99,10 @@ ifeq ($(KERNEL_BUILD), 0) ifneq (,$(filter rhode%, $(TARGET_PRODUCT))) INCS += -include $(AUDIO_ROOT)/config/rhodeautoconf.h endif + + ifneq (,$(filter hawao%, $(TARGET_PRODUCT))) + INCS += -include $(AUDIO_ROOT)/config/hawaoautoconf.h + endif endif From 18e0cfd6524d042a59183f266be0766d2d15a34c Mon Sep 17 00:00:00 2001 From: yaozm1 Date: Mon, 20 Dec 2021 17:23:26 +0800 Subject: [PATCH 18/40] asoc:upgrade aw88261 driver to v1.9.0.3 integrate awinic's aw88261 release; version: 1.9.0.3 Change-Id: I8a1b52089cff107d09faf89ed9c5381ea4b7803b Signed-off-by: yaozm1 Reviewed-on: https://gerrit.mot.com/2149458 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Hujun Liao Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/aw88261/aw882xx.c | 30 ++++++++++++++---------------- asoc/codecs/aw88261/aw_device.c | 2 +- asoc/codecs/aw88261/aw_dsp.c | 2 +- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/asoc/codecs/aw88261/aw882xx.c b/asoc/codecs/aw88261/aw882xx.c index c7bc965d85fa..3505912bcd42 100644 --- a/asoc/codecs/aw88261/aw882xx.c +++ b/asoc/codecs/aw88261/aw882xx.c @@ -12,7 +12,7 @@ * option) any later version. */ -#define DEBUG +/*#define DEBUG*/ #include #include #include @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.9.0.2" +#define AW882XX_DRIVER_VERSION "v1.9.0.3" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ @@ -1530,7 +1530,7 @@ static int aw882xx_get_algo_prof_id_by_scene_st(struct aw882xx *aw882xx) return aw882xx_scene_state[index].skt_profile_id; } - return AW_ALGO_PROFILE_ID_0; + return 0; } static void aw882xx_update_algo_scene_st(struct aw882xx *aw882xx, @@ -1563,28 +1563,26 @@ static int aw882xx_update_algo_profile(struct aw882xx *aw882xx) aw_dev_info(aw882xx->dev, "enter"); new_skt_prof_id = aw882xx_get_algo_prof_id_by_scene_st(aw882xx); - if (new_skt_prof_id > AW_ALGO_PROFILE_ID_MAX) { + if (new_skt_prof_id < AW_ALGO_PROFILE_ID_1) { /* no active scene */ aw_dev_info(aw882xx->dev, "all scene disactive,do nothing"); return 0; } - if (cur_skt_prof_id != new_skt_prof_id) { - aw_dev_info(aw882xx->dev, "algo scene switch. [new] %d,[old] %d", - new_skt_prof_id, cur_skt_prof_id); + aw_dev_info(aw882xx->dev, "algo scene switch. [new] %d,[old] %d", + new_skt_prof_id, cur_skt_prof_id); aw882xx->cur_algo_prof_id = new_skt_prof_id; aw_dev = aw882xx->aw_pa; - /* set new scene pramas to skt */ - ret = aw_dev_set_algo_prof(aw_dev, aw882xx->cur_algo_prof_id); - if (ret < 0) { - aw_dev_err(aw882xx->dev, "set algo prof failed"); - return -1; - } - } else { - aw_dev_info(aw882xx->dev, "algo scene hold, cur scene %d", - new_skt_prof_id); + /* set new scene pramas to skt */ + ret = aw_dev_set_algo_prof(aw_dev, aw882xx->cur_algo_prof_id); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "set algo prof failed"); + return -1; } + + aw_dev_info(aw882xx->dev, "algo scene hold, cur scene %d", + new_skt_prof_id); return 0; diff --git a/asoc/codecs/aw88261/aw_device.c b/asoc/codecs/aw88261/aw_device.c index 6e1a44747a5a..bb2608e2d5d0 100644 --- a/asoc/codecs/aw88261/aw_device.c +++ b/asoc/codecs/aw88261/aw_device.c @@ -1,4 +1,4 @@ - #define DEBUG +/* #define DEBUG */ #include #include #include diff --git a/asoc/codecs/aw88261/aw_dsp.c b/asoc/codecs/aw88261/aw_dsp.c index 808ad98d6e2a..eabb7764526f 100644 --- a/asoc/codecs/aw88261/aw_dsp.c +++ b/asoc/codecs/aw88261/aw_dsp.c @@ -1,4 +1,4 @@ -#define DEBUG +/*#define DEBUG*/ #include #include #include From 6e3bdd99d8683098390c4924ca03dc6e28e4b33f Mon Sep 17 00:00:00 2001 From: yaozm1 Date: Wed, 5 Jan 2022 16:56:28 +0800 Subject: [PATCH 19/40] asoc:upgrade aw88261 driver to v 1.9.0.4 version 1.9.0.4; add speaker spin function; fix compile errors. Change-Id: I1ef2a6b32d373cc87ec0615e1ed4b3709e13051e Signed-off-by: yaozm1 Reviewed-on: https://gerrit.mot.com/2167927 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/aw88261/aw882xx.c | 173 ++++++++++++++++++++++++++++++-- asoc/codecs/aw88261/aw882xx.h | 7 +- asoc/codecs/aw88261/aw_device.c | 12 +++ asoc/codecs/aw88261/aw_device.h | 4 + asoc/codecs/aw88261/aw_dsp.c | 52 ++++++++++ asoc/codecs/aw88261/aw_dsp.h | 18 +++- 6 files changed, 258 insertions(+), 8 deletions(-) diff --git a/asoc/codecs/aw88261/aw882xx.c b/asoc/codecs/aw88261/aw882xx.c index 3505912bcd42..bbbe61dcbe42 100644 --- a/asoc/codecs/aw88261/aw882xx.c +++ b/asoc/codecs/aw88261/aw882xx.c @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.9.0.3" +#define AW882XX_DRIVER_VERSION "v1.9.0.4" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ @@ -48,11 +48,18 @@ static unsigned int g_algo_tx_en = false; static unsigned int g_algo_copp_en = false; #ifdef AW_SPIN_ENABLE static unsigned int g_spin_value = 0; +static uint32_t g_spin_en = 0; #endif #ifdef AW882XX_RUNIN_TEST static unsigned int g_runin_test; #endif +#define AW882XX_SPIN_FOR_LOW_LATENCY_ONLY 1 + +#ifdef AW882XX_SPIN_FOR_LOW_LATENCY_ONLY +static unsigned int g_skt_prof_id = 0; +#endif + static DEFINE_MUTEX(g_aw882xx_lock); struct aw_container *g_awinic_cfg = NULL; @@ -1257,12 +1264,21 @@ static int aw882xx_set_spin(struct snd_kcontrol *kcontrol, aw_dev = aw882xx->aw_pa; ctrl_value = ucontrol->value.integer.value[0]; + +#ifdef AW882XX_SPIN_FOR_LOW_LATENCY_ONLY + if (g_skt_prof_id == (AW882XX_SCENE_FASTTRACK_ID+1)) { +#else if (aw882xx->pstream) { +#endif ret = aw_dev_set_spin(ctrl_value); if (ret) aw_dev_err(aw882xx->dev, "set spin error, ret=%d", ret); } else { +#ifdef AW882XX_SPIN_FOR_LOW_LATENCY_ONLY + aw_dev_info(aw882xx->dev, "fasttrack stream no start"); +#else aw_dev_info(aw882xx->dev, "stream no start only record"); +#endif } g_spin_value = ctrl_value; @@ -1297,6 +1313,127 @@ static int aw882xx_get_spin(struct snd_kcontrol *kcontrol, aw_dev_dbg(aw882xx->dev, "spin value is %ld", ucontrol->value.integer.value[0]); return 0; } + +static int aw882xx_get_spin_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + struct aw_spin_param spin_param; + int ctrl_value; + int ret = -EINVAL; + + aw_dev = aw882xx->aw_pa; + + if (aw882xx->pstream) { + ret = aw_dev_get_spin_param(aw_dev, &ctrl_value, &(spin_param.relase_time)); + if (ret) { + aw_dev_err(aw882xx->dev, "get spin status failed!, ret = %d", ret); + ctrl_value = 0; + } + ucontrol->value.integer.value[0] = ctrl_value; + } else { + aw_dev_info(aw882xx->dev, "no stream, use record value"); + + } + aw_dev_dbg(aw882xx->dev, "spin value is %ld", ucontrol->value.integer.value[0]); + return 0; +} + +static int aw882xx_set_spin_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + struct aw_spin_param spin_param; + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + aw_dev = aw882xx->aw_pa; + + ctrl_value = ucontrol->value.integer.value[0]; + + if (g_algo_rx_en == true) { + ret = aw_dev_set_spin_param(aw_dev, ctrl_value, spin_param.relase_time); + if (ret) + aw_dev_err(aw882xx->dev, "set spin status error, ret=%d", ret); + + g_spin_en = ctrl_value; + } else { + aw_dev_info(aw882xx->dev, "stream no start only record"); + } + + return 0; +} + +static int aw882xx_get_spin_relase_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + struct aw_spin_param spin_param; + int ctrl_value; + int ret = -EINVAL; + + aw_dev = aw882xx->aw_pa; + + if (aw882xx->pstream) { + ret = aw_dev_get_spin_param(aw_dev, &(spin_param.enable), &ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "get spin release time failed!, ret = %d", ret); + ctrl_value = 0; + } + ucontrol->value.integer.value[0] = ctrl_value; + } else { + aw_dev_info(aw882xx->dev, "no stream, use record value"); + + } + aw_dev_dbg(aw882xx->dev, "spin value is %ld", ucontrol->value.integer.value[0]); + return 0; +} + +static int aw882xx_set_spin_relase_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + //struct aw_spin_param spin_param; + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + aw_dev = aw882xx->aw_pa; + + ctrl_value = ucontrol->value.integer.value[0]; + //if ((aw882xx->pstream) && (g_spin_en == 1) && (g_algo_rx_en == true)) { + if ( (g_spin_en == 1) && (g_algo_rx_en == true)) { + ret = aw_dev_set_spin_param(aw_dev, g_spin_en , ctrl_value); + if (ret) + aw_dev_err(aw882xx->dev, "set spin release time error, ret=%d", ret); + } else { + aw_dev_info(aw882xx->dev, "set release time failed"); + } + + return 0; +} + #endif static int aw882xx_get_fade_in_time(struct snd_kcontrol *kcontrol, @@ -1569,10 +1706,10 @@ static int aw882xx_update_algo_profile(struct aw882xx *aw882xx) return 0; } - aw_dev_info(aw882xx->dev, "algo scene switch. [new] %d,[old] %d", + aw_dev_info(aw882xx->dev, "algo scene switch. [new] %d,[old] %d", new_skt_prof_id, cur_skt_prof_id); - aw882xx->cur_algo_prof_id = new_skt_prof_id; - aw_dev = aw882xx->aw_pa; + aw882xx->cur_algo_prof_id = new_skt_prof_id; + aw_dev = aw882xx->aw_pa; /* set new scene pramas to skt */ ret = aw_dev_set_algo_prof(aw_dev, aw882xx->cur_algo_prof_id); @@ -1580,10 +1717,27 @@ static int aw882xx_update_algo_profile(struct aw882xx *aw882xx) aw_dev_err(aw882xx->dev, "set algo prof failed"); return -1; } - - aw_dev_info(aw882xx->dev, "algo scene hold, cur scene %d", + + aw_dev_info(aw882xx->dev, "algo scene hold, cur scene %d", new_skt_prof_id); +#ifdef AW882XX_SPIN_FOR_LOW_LATENCY_ONLY + aw_dev_info(aw882xx->dev, "g_skt_prof_id=%d\n",g_skt_prof_id); + + if (new_skt_prof_id == (AW882XX_SCENE_FASTTRACK_ID+1)){ + ret = aw_dev_set_spin(g_spin_value); + if (ret) + aw_dev_err(aw882xx->dev, "set spin error when switch prof id to %d, ret=%d", ret, new_skt_prof_id); + } else if ((g_skt_prof_id == (AW882XX_SCENE_FASTTRACK_ID+1)) + && (new_skt_prof_id != (AW882XX_SCENE_FASTTRACK_ID+1))){ + ret = aw_dev_set_spin(1); + if (ret) + aw_dev_err(aw882xx->dev, "set spin error when switch prof id to %d, ret=%d", ret, new_skt_prof_id); + } + + g_skt_prof_id = new_skt_prof_id; +#endif + return 0; } @@ -1887,6 +2041,10 @@ static struct snd_kcontrol_new aw882xx_controls[] = { #ifdef AW_SPIN_ENABLE SOC_ENUM_EXT("aw882xx_spin_switch", aw882xx_snd_enum[2], aw882xx_get_spin, aw882xx_set_spin), + SOC_ENUM_EXT("aw882xx_spin_status", aw882xx_snd_enum[1], + aw882xx_get_spin_status, aw882xx_set_spin_status), + SOC_SINGLE_EXT("aw882xx_spin_relase_time", 0, 0, 1000000, 0, + aw882xx_get_spin_relase_time, aw882xx_set_spin_relase_time), #endif #ifdef AW882XX_RUNIN_TEST SOC_ENUM_EXT("aw882xx_runin_test", aw882xx_snd_enum[1], @@ -3104,3 +3262,6 @@ module_exit(aw882xx_i2c_exit); MODULE_DESCRIPTION("ASoC AW882XX Smart PA Driver"); MODULE_LICENSE("GPL v2"); + + + diff --git a/asoc/codecs/aw88261/aw882xx.h b/asoc/codecs/aw88261/aw882xx.h index be576beed982..c9b41f61c91c 100644 --- a/asoc/codecs/aw88261/aw882xx.h +++ b/asoc/codecs/aw88261/aw882xx.h @@ -2,6 +2,11 @@ #define _AW882XX_H_ #include #include + +#ifndef AW_SPIN_ENABLE +#define AW_SPIN_ENABLE 1 +#endif + #include "aw_device.h" /* @@ -201,7 +206,7 @@ struct aw882xx { int rate; int pstream; int cstream; - int aw882xx_ramp_status; /* ramp status */ + int aw882xx_ramp_status; /* ramp status */ #ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS int aw882xx_algo_cali; int aw882xx_algo_bypass; diff --git a/asoc/codecs/aw88261/aw_device.c b/asoc/codecs/aw88261/aw_device.c index bb2608e2d5d0..a4758c3a2102 100644 --- a/asoc/codecs/aw88261/aw_device.c +++ b/asoc/codecs/aw88261/aw_device.c @@ -1494,6 +1494,18 @@ int aw_dev_set_algo_params_path(struct aw_device *aw_dev) return aw_dsp_set_algo_params_path(aw_dev); } +#ifdef AW_SPIN_ENABLE +int aw_dev_set_spin_param(struct aw_device *aw_dev, uint32_t enable, uint32_t relase_time) +{ + return aw_dsp_set_spin_param(aw_dev, enable, relase_time); +} + +int aw_dev_get_spin_param(struct aw_device *aw_dev, uint32_t *enable, uint32_t *relase_time) +{ + return aw_dsp_get_spin_param(aw_dev, enable, relase_time); +} +#endif + static void aw_device_parse_sound_channel_dt(struct aw_device *aw_dev) { int ret; diff --git a/asoc/codecs/aw88261/aw_device.h b/asoc/codecs/aw88261/aw_device.h index 0629d9e71968..2b181a59e030 100644 --- a/asoc/codecs/aw88261/aw_device.h +++ b/asoc/codecs/aw88261/aw_device.h @@ -289,6 +289,10 @@ int aw_dev_set_algo_params_path(struct aw_device *aw_dev); int aw_device_probe(struct aw_device *aw_dev); int aw_device_remove(struct aw_device *aw_dev); int aw_dev_get_list_head(struct list_head **head); +#ifdef AW_SPIN_ENABLE +int aw_dev_set_spin_param(struct aw_device *aw_dev, uint32_t enable, uint32_t relase_time); +int aw_dev_get_spin_param(struct aw_device *aw_dev, uint32_t* enable, uint32_t* relase_time); +#endif #endif diff --git a/asoc/codecs/aw88261/aw_dsp.c b/asoc/codecs/aw88261/aw_dsp.c index eabb7764526f..e8ec82e9e7c0 100644 --- a/asoc/codecs/aw88261/aw_dsp.c +++ b/asoc/codecs/aw88261/aw_dsp.c @@ -48,6 +48,7 @@ static DEFINE_MUTEX(g_aw_dsp_lock); #define AW_MSG_ID_REAL_DATA_L (0X10013D21) #define AW_MSG_ID_REAL_DATA_R (0X10013D22) #define AW_MSG_ID_SET_BIN_PARAMS (0X10013D30) +#define AW_MSG_ID_SPIN_ENABLE (0x10013D2F) #define AFE_MSG_ID_MSG_0 (0X10013D2A) #define AFE_MSG_ID_MSG_1 (0X10013D2B) @@ -555,6 +556,57 @@ int aw_dsp_read_st(struct aw_device *aw_dev, int32_t *r0, int32_t *te) return 0; } +#ifdef AW_SPIN_ENABLE +int aw_dsp_get_spin_param(struct aw_device *aw_dev, + uint32_t *enable, uint32_t *relase_time) +{ + int ret; + struct aw_spin_param spin_param; + + ret = aw_read_data_from_dsp(AW_MSG_ID_SPIN_ENABLE, + &spin_param, + sizeof(spin_param)); + if (ret) { + spin_param.enable = 0; + spin_param.relase_time = 0; + aw_pr_err("yao,read spin params failed "); + return ret; + } + + *enable = spin_param.enable; + *relase_time = spin_param.relase_time; + + aw_pr_info("yao,get spin params done"); + return 0; +} + +int aw_dsp_set_spin_param(struct aw_device *aw_dev, + uint32_t enable, uint32_t relase_time) +{ + int ret; + struct aw_spin_param spin_param; + + if (!enable) { + aw_pr_dbg("yao,need to set the rotation parameters"); + return 0; + } + + spin_param.enable = enable; + spin_param.relase_time = relase_time; + + ret = aw_write_data_to_dsp(AW_MSG_ID_SPIN_ENABLE, + &spin_param, + sizeof(spin_param)); + if (ret){ + pr_err("yao,222,set spin params failed\n"); + return -EINVAL; + } + + pr_err("yao,222,set spin params with ret 0"); + return 0; +} +#endif + int aw_dsp_read_spin(int *spin_mode) { int ret; diff --git a/asoc/codecs/aw88261/aw_dsp.h b/asoc/codecs/aw88261/aw_dsp.h index aaf0089a0d92..703c8a8cdf46 100644 --- a/asoc/codecs/aw88261/aw_dsp.h +++ b/asoc/codecs/aw88261/aw_dsp.h @@ -3,6 +3,10 @@ #define AW_QCOM_PLATFORM +#ifndef AW_SPIN_ENABLE +#define AW_SPIN_ENABLE 1 +#endif + /*factor form 12bit(4096) to 1000*/ #define AW_DSP_RE_TO_SHOW_RE(re) (((re) * (1000)) >> (12)) #define AW_SHOW_RE_TO_DSP_RE(re) (((re) << 12) / (1000)) @@ -31,6 +35,13 @@ enum { #define AW_DSP_MSG_HDR_VER (1) typedef struct aw_msg_hdr aw_dsp_msg_t; +#ifdef AW_SPIN_ENABLE +struct aw_spin_param { + uint32_t enable; + uint32_t relase_time; +}; +#endif + int aw_dsp_write_msg(struct aw_device *aw_dev, uint32_t msg_id, char *data_ptr, unsigned int data_size); int aw_dsp_read_msg(struct aw_device *aw_dev, uint32_t msg_id, char *data_ptr, unsigned int data_size); int aw_dsp_write_cali_cfg(struct aw_device *aw_dev, char *data, unsigned int data_len); @@ -61,6 +72,11 @@ int aw_dsp_get_algo_prof(struct aw_device *aw_dev, int *prof_id); int aw_dsp_set_algo_params_path(struct aw_device *aw_dev); void aw_device_parse_topo_id_dt(struct aw_device *aw_dev); void aw_device_parse_port_id_dt(struct aw_device *aw_dev); - +#ifdef AW_SPIN_ENABLE +int aw_dsp_set_spin_param(struct aw_device *aw_dev, + uint32_t enable, uint32_t relase_time); +int aw_dsp_get_spin_param(struct aw_device *aw_dev, + uint32_t *enable, uint32_t *relase_time); +#endif #endif From 9e90cbf47c3a8c08e71fe7c5329b19bd0eaf7285 Mon Sep 17 00:00:00 2001 From: yaozm1 Date: Wed, 26 Jan 2022 17:30:43 +0800 Subject: [PATCH 20/40] asoc:remove debug info for aw88261 update aw88261 driver; remove spin debug info. Change-Id: I3b0032a11b6794ea6ed80f4a1e46d795aa105641 Signed-off-by: yaozm1 Reviewed-on: https://gerrit.mot.com/2179434 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/aw88261/aw_dsp.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/asoc/codecs/aw88261/aw_dsp.c b/asoc/codecs/aw88261/aw_dsp.c index e8ec82e9e7c0..ed2425d92dc6 100644 --- a/asoc/codecs/aw88261/aw_dsp.c +++ b/asoc/codecs/aw88261/aw_dsp.c @@ -569,14 +569,13 @@ int aw_dsp_get_spin_param(struct aw_device *aw_dev, if (ret) { spin_param.enable = 0; spin_param.relase_time = 0; - aw_pr_err("yao,read spin params failed "); + aw_pr_err("read spin params failed "); return ret; } *enable = spin_param.enable; *relase_time = spin_param.relase_time; - aw_pr_info("yao,get spin params done"); return 0; } @@ -587,7 +586,6 @@ int aw_dsp_set_spin_param(struct aw_device *aw_dev, struct aw_spin_param spin_param; if (!enable) { - aw_pr_dbg("yao,need to set the rotation parameters"); return 0; } @@ -598,11 +596,10 @@ int aw_dsp_set_spin_param(struct aw_device *aw_dev, &spin_param, sizeof(spin_param)); if (ret){ - pr_err("yao,222,set spin params failed\n"); + pr_err("set spin params failed\n"); return -EINVAL; } - pr_err("yao,222,set spin params with ret 0"); return 0; } #endif From e8b5e9bb14385c2c610c5bb7c3fa1fd73dc95f64 Mon Sep 17 00:00:00 2001 From: yaozm1 Date: Tue, 25 Jan 2022 15:28:42 +0800 Subject: [PATCH 21/40] asoc:update aw88261 spin config remove low-latency only limitation; spin will work for all case. Change-Id: I157f35d824b51b36686bb0a4c0c4d289014c881b Signed-off-by: yaozm1 Reviewed-on: https://gerrit.mot.com/2178463 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Yunfei Guo Reviewed-by: Wanlong Zhou Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/aw88261/aw882xx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/asoc/codecs/aw88261/aw882xx.c b/asoc/codecs/aw88261/aw882xx.c index bbbe61dcbe42..d02d350a09d5 100644 --- a/asoc/codecs/aw88261/aw882xx.c +++ b/asoc/codecs/aw88261/aw882xx.c @@ -54,8 +54,6 @@ static uint32_t g_spin_en = 0; static unsigned int g_runin_test; #endif -#define AW882XX_SPIN_FOR_LOW_LATENCY_ONLY 1 - #ifdef AW882XX_SPIN_FOR_LOW_LATENCY_ONLY static unsigned int g_skt_prof_id = 0; #endif From 93396078c5139c5e4ba16f2d42471e3995195826 Mon Sep 17 00:00:00 2001 From: guoyf2 Date: Tue, 1 Mar 2022 15:43:05 +0800 Subject: [PATCH 22/40] asoc: upgrade aw88261 driver to v1.9.0.5 Version: 1.9.0.5 Optimize driver initialization timing Change-Id: I7cbc9e056e3afd0b4dd3471ec4fc718c71f3443b Signed-off-by: guoyf2 Reviewed-on: https://gerrit.mot.com/2204136 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Zhengming Yao Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- asoc/codecs/aw88261/aw882xx.c | 2 +- asoc/codecs/aw88261/aw_device.c | 232 ++++++++++++++------------ asoc/codecs/aw88261/aw_device.h | 16 ++ asoc/codecs/aw88261/aw_init.c | 24 ++- asoc/codecs/aw88261/aw_pid_2113_reg.h | 11 ++ 5 files changed, 176 insertions(+), 109 deletions(-) diff --git a/asoc/codecs/aw88261/aw882xx.c b/asoc/codecs/aw88261/aw882xx.c index d02d350a09d5..7a2a5567c980 100644 --- a/asoc/codecs/aw88261/aw882xx.c +++ b/asoc/codecs/aw88261/aw882xx.c @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.9.0.4" +#define AW882XX_DRIVER_VERSION "v1.9.0.5" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ diff --git a/asoc/codecs/aw88261/aw_device.c b/asoc/codecs/aw88261/aw_device.c index a4758c3a2102..c2d768060f8c 100644 --- a/asoc/codecs/aw88261/aw_device.c +++ b/asoc/codecs/aw88261/aw_device.c @@ -570,6 +570,113 @@ static char *aw_dev_get_prof_name(struct aw_device *aw_dev, int index) return 0; }*/ +static int aw_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t reg_icalk = 0; + uint16_t reg_icalkl = 0; + struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; + + if (desc->icalkl_reg == AW_REG_NONE) { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalk_reg, ®_val); + reg_icalk = (uint16_t)reg_val & (~desc->icalk_reg_mask); + } else { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalk_reg, ®_val); + reg_icalk = (uint16_t)reg_val & (~desc->icalk_reg_mask); + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalkl_reg, ®_val); + reg_icalkl = (uint16_t)reg_val & (~desc->icalkl_reg_mask); + if (aw_dev->efuse_check == AW_EF_OR_CHECK) + reg_icalk = (reg_icalk >> desc->icalk_shift) | (reg_icalkl >> desc->icalkl_shift); + else + reg_icalk = (reg_icalk >> desc->icalk_shift) & (reg_icalkl >> desc->icalkl_shift); + } + + if (reg_icalk & (~desc->icalk_sign_mask)) + reg_icalk = reg_icalk | (~desc->icalk_neg_mask); + + *icalk = (int16_t)reg_icalk; + + return ret; +} + +static int aw_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t reg_vcalk = 0; + uint16_t reg_vcalkl = 0; + struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; + + if (desc->vcalkl_reg == AW_REG_NONE) { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalk_reg, ®_val); + reg_vcalk = (uint16_t)reg_val & (~desc->vcalk_reg_mask); + } else { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalk_reg, ®_val); + reg_vcalk = (uint16_t)reg_val & (~desc->vcalk_reg_mask); + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalkl_reg, ®_val); + reg_vcalkl = (uint16_t)reg_val & (~desc->vcalkl_reg_mask); + if (aw_dev->efuse_check == AW_EF_OR_CHECK) + reg_vcalk = (reg_vcalk >> desc->vcalk_shift) | (reg_vcalkl >> desc->vcalkl_shift); + else + reg_vcalk = (reg_vcalk >> desc->vcalk_shift) & (reg_vcalkl >> desc->vcalkl_shift); + } + + if (reg_vcalk & (~desc->vcalk_sign_mask)) + reg_vcalk = reg_vcalk | (~desc->vcalk_neg_mask); + + *vcalk = (int16_t)reg_vcalk; + + return ret; +} + +static int aw_dev_set_vcalb(struct aw_device *aw_dev) +{ + int ret = -1; + unsigned int reg_val; + int vcalb; + int icalk; + int vcalk; + int16_t icalk_val = 0; + int16_t vcalk_val = 0; + + struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; + + if (desc->icalk_reg == AW_REG_NONE || desc->vcalb_reg == AW_REG_NONE) { + aw_dev_info(aw_dev->dev, "REG None!"); + return 0; + } + + ret = aw_dev_get_icalk(aw_dev, &icalk_val); + if (ret < 0) + return ret; + + ret = aw_dev_get_vcalk(aw_dev, &vcalk_val); + if (ret < 0) + return ret; + + icalk = desc->cabl_base_value + desc->icalk_value_factor * icalk_val; + vcalk = desc->cabl_base_value + desc->vcalk_value_factor * vcalk_val; + if (!vcalk) { + aw_dev_err(aw_dev->dev, "vcalk is 0"); + return -EINVAL; + } + + vcalb = desc->vcal_factor * icalk / vcalk; + + reg_val = (unsigned int)vcalb; + aw_dev_info(aw_dev->dev, "icalk=%d, vcalk=%d, vcalb=%d, reg_val=0x%04x", + icalk, vcalk, vcalb, reg_val); + + ret = aw_dev->ops.aw_i2c_write(aw_dev, desc->vcalb_reg, reg_val); + + aw_dev_info(aw_dev->dev, "done"); + + return ret; +} + + + /*pwd enable update reg*/ static int aw_dev_reg_fw_update(struct aw_device *aw_dev) { @@ -579,6 +686,7 @@ static int aw_dev_reg_fw_update(struct aw_device *aw_dev) unsigned int read_val; int ret = -1; unsigned int init_volume = 0; + unsigned int efcheck_val = 0; struct aw_int_desc *int_desc = &aw_dev->int_desc; struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; struct aw_bstctrl_desc *bstctrl_desc = &aw_dev->bstctrl_desc; @@ -655,6 +763,16 @@ static int aw_dev_reg_fw_update(struct aw_device *aw_dev) reg_val |= read_val; } + if (reg_addr == aw_dev->efcheck_desc.reg) { + efcheck_val = reg_val & (~aw_dev->efcheck_desc.mask); + if (efcheck_val == aw_dev->efcheck_desc.or_val) + aw_dev->efuse_check = AW_EF_OR_CHECK; + else + aw_dev->efuse_check = AW_EF_AND_CHECK; + + aw_dev_info(aw_dev->dev, "efuse check: %d", aw_dev->efuse_check); + } + if (reg_addr == aw_dev->vcalb_desc.vcalb_reg) continue; @@ -667,6 +785,12 @@ static int aw_dev_reg_fw_update(struct aw_device *aw_dev) break; } + ret = aw_dev_set_vcalb(aw_dev); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "can't set vcalb"); + return ret; + } + aw_dev->ops.aw_get_volume(aw_dev, &init_volume); aw_dev->volume_desc.init_volume = init_volume; @@ -789,104 +913,6 @@ static void aw_dev_mute(struct aw_device *aw_dev, bool mute) aw_dev_info(aw_dev->dev, "done"); } -static int aw_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk) -{ - int ret = -1; - unsigned int reg_val = 0; - uint16_t reg_icalk = 0; - uint16_t reg_icalkl = 0; - struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; - - if (desc->icalkl_reg == AW_REG_NONE) { - ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalk_reg, ®_val); - reg_icalk = (uint16_t)reg_val & (~desc->icalk_reg_mask); - } else { - ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalk_reg, ®_val); - reg_icalk = (uint16_t)reg_val & (~desc->icalk_reg_mask); - ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalkl_reg, ®_val); - reg_icalkl = (uint16_t)reg_val & (~desc->icalkl_reg_mask); - reg_icalk = (reg_icalk >> desc->icalk_shift) | (reg_icalkl >> desc->icalkl_shift); - } - - if (reg_icalk & (~desc->icalk_sign_mask)) - reg_icalk = reg_icalk | (~desc->icalk_neg_mask); - - *icalk = (int16_t)reg_icalk; - - return ret; -} - -static int aw_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk) -{ - int ret = -1; - unsigned int reg_val = 0; - uint16_t reg_vcalk = 0; - uint16_t reg_vcalkl = 0; - struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; - - if (desc->vcalkl_reg == AW_REG_NONE) { - ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalk_reg, ®_val); - reg_vcalk = (uint16_t)reg_val & (~desc->vcalk_reg_mask); - } else { - ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalk_reg, ®_val); - reg_vcalk = (uint16_t)reg_val & (~desc->vcalk_reg_mask); - ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalkl_reg, ®_val); - reg_vcalkl = (uint16_t)reg_val & (~desc->vcalkl_reg_mask); - reg_vcalk = (reg_vcalk >> desc->vcalk_shift) | (reg_vcalkl >> desc->vcalkl_shift); - } - - if (reg_vcalk & (~desc->vcalk_sign_mask)) - reg_vcalk = reg_vcalk | (~desc->vcalk_neg_mask); - - *vcalk = (int16_t)reg_vcalk; - - return ret; -} - -static int aw_dev_set_vcalb(struct aw_device *aw_dev) -{ - int ret = -1; - unsigned int reg_val; - int vcalb; - int icalk; - int vcalk; - int16_t icalk_val = 0; - int16_t vcalk_val = 0; - - struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; - - if (desc->icalk_reg == AW_REG_NONE || desc->vcalb_reg == AW_REG_NONE) { - aw_dev_info(aw_dev->dev, "REG None!"); - return 0; - } - - ret = aw_dev_get_icalk(aw_dev, &icalk_val); - if (ret < 0) - return ret; - - ret = aw_dev_get_vcalk(aw_dev, &vcalk_val); - if (ret < 0) - return ret; - - icalk = desc->cabl_base_value + desc->icalk_value_factor * icalk_val; - vcalk = desc->cabl_base_value + desc->vcalk_value_factor * vcalk_val; - if (!vcalk) { - aw_dev_err(aw_dev->dev, "vcalk is 0"); - return -EINVAL; - } - - vcalb = desc->vcal_factor * icalk / vcalk; - - reg_val = (unsigned int)vcalb; - aw_dev_info(aw_dev->dev, "icalk=%d, vcalk=%d, vcalb=%d, reg_val=0x%04x", - icalk, vcalk, vcalb, reg_val); - - ret = aw_dev->ops.aw_i2c_write(aw_dev, desc->vcalb_reg, reg_val); - - aw_dev_info(aw_dev->dev, "done"); - - return ret; -} int aw_dev_get_int_status(struct aw_device *aw_dev, uint16_t *int_status) { @@ -1186,8 +1212,6 @@ int aw_device_irq_reinit(struct aw_device *aw_dev) if (ret < 0) return ret; - /*update vcalb*/ - aw_dev_set_vcalb(aw_dev); return 0; } @@ -1217,10 +1241,10 @@ int aw_device_init(struct aw_device *aw_dev, struct aw_container *aw_cfg) if (ret < 0) return ret; - ret = aw_dev_set_vcalb(aw_dev); - if (ret < 0) { - aw_dev_err(aw_dev->dev, "can't set vcalb"); - return ret; + if (aw_dev->ops.aw_frcset_check) { + ret = aw_dev->ops.aw_frcset_check(aw_dev); + if (ret) + return ret; } aw_dev->status = AW_DEV_PW_ON; diff --git a/asoc/codecs/aw88261/aw_device.h b/asoc/codecs/aw88261/aw_device.h index 2b181a59e030..cbc3eec67c48 100644 --- a/asoc/codecs/aw88261/aw_device.h +++ b/asoc/codecs/aw88261/aw_device.h @@ -28,6 +28,12 @@ enum { AW_DEV_TYPE_NONE = 1, }; + +enum { + AW_EF_AND_CHECK = 0, + AW_EF_OR_CHECK, +}; + enum { AW_DEV_CH_PRI_L = 0, AW_DEV_CH_PRI_R = 1, @@ -68,6 +74,7 @@ struct aw_device_ops { void (*aw_set_algo)(struct aw_device *aw_dev); unsigned int (*aw_get_irq_type)(struct aw_device *aw_dev, unsigned int value); void (*aw_reg_force_set)(struct aw_device *aw_dev); + int (*aw_frcset_check)(struct aw_device *aw_dev); }; struct aw_int_desc { @@ -195,12 +202,20 @@ struct aw_container { uint8_t data[]; }; +struct aw_efcheck_desc { + unsigned int reg; + unsigned int mask; + unsigned int and_val; + unsigned int or_val; +}; + struct aw_device { int index; int status; int bstcfg_enable; int frcset_en; int bop_en; + int efuse_check; unsigned int mute_st; unsigned int amppd_st; @@ -238,6 +253,7 @@ struct aw_device { struct aw_monitor_desc monitor_desc; struct aw_soft_rst soft_rst; struct aw_bop_desc bop_desc; + struct aw_efcheck_desc efcheck_desc; struct aw_device_ops ops; struct list_head list_node; }; diff --git a/asoc/codecs/aw88261/aw_init.c b/asoc/codecs/aw88261/aw_init.c index dfc112442d92..e42f983d2d6d 100644 --- a/asoc/codecs/aw88261/aw_init.c +++ b/asoc/codecs/aw88261/aw_init.c @@ -311,6 +311,7 @@ static int aw_pid_1852_dev_init(struct aw882xx *aw882xx, int index) aw_pa->volume_desc.mute_volume = AW_PID_1852_MUTE_VOL; aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->efcheck_desc.reg = AW_REG_NONE; aw_pa->soft_rst.reg = AW_PID_1852_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; @@ -595,6 +596,7 @@ static int aw_pid_2013_dev_init(struct aw882xx *aw882xx, int index) aw_pa->volume_desc.mute_volume = AW_PID_2013_MUTE_VOL; aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->efcheck_desc.reg = AW_REG_NONE; aw_pa->soft_rst.reg = AW_PID_2013_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; @@ -851,6 +853,7 @@ static int aw_pid_2032_dev_init(struct aw882xx *aw882xx, int index) aw_pa->volume_desc.mute_volume = AW_PID_2032_MUTE_VOL; aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->efcheck_desc.reg = AW_REG_NONE; aw_pa->soft_rst.reg = AW_PID_2032_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; @@ -1059,6 +1062,7 @@ static int aw_pid_2055_dev_init(struct aw882xx *aw882xx, int index) aw_pa->cco_mux_desc.bypass_val = AW_PID_2055_CCO_MUX_BYPASS_VALUE; aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->efcheck_desc.reg = AW_REG_NONE; aw_pa->soft_rst.reg = AW_PID_2055_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; @@ -1321,6 +1325,7 @@ static int aw_pid_2071_dev_init(struct aw882xx *aw882xx, int index) aw_pa->volume_desc.mute_volume = AW_PID_2071_MUTE_VOL; aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->efcheck_desc.reg = AW_REG_NONE; aw_pa->soft_rst.reg = AW_PID_2071_ID_REG; aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; @@ -1462,11 +1467,12 @@ static unsigned int aw_pid_2113_get_irq_type(struct aw_device *aw_dev, return ret; } -static void aw_pid_2113_efver_check(struct aw_device *aw_dev) +static int aw_pid_2113_frcset_check(struct aw_device *aw_dev) { unsigned int reg_val = 0; uint16_t temh = 0; uint16_t teml = 0; + uint16_t tem = 0; aw_dev->ops.aw_i2c_read(aw_dev, AW_PID_2113_EFRH3_REG, ®_val); @@ -1476,13 +1482,19 @@ static void aw_pid_2113_efver_check(struct aw_device *aw_dev) AW_PID_2113_EFRL3_REG, ®_val); teml = ((uint16_t)reg_val & (~AW_PID_2113_TEML_MASK)); - if ((temh | teml) == AW_PID_2113_DEFAULT_CFG) + if (aw_dev->efuse_check == AW_EF_OR_CHECK) + tem = (temh | teml); + else + tem = (temh & teml); + + if (tem == AW_PID_2113_DEFAULT_CFG) aw_dev->frcset_en = AW_FRCSET_ENABLE; else aw_dev->frcset_en = AW_FRCSET_DISABLE; aw_dev_info(aw_dev->dev, "tem is 0x%04x, frcset_en is %d", - (temh | teml), aw_dev->frcset_en); + tem, aw_dev->frcset_en); + return 0; } static void aw_pid_2113_reg_force_set(struct aw_device *aw_dev) @@ -1556,6 +1568,7 @@ static int aw_pid_2113_dev_init(struct aw882xx *aw882xx, int index) aw_pa->ops.aw_get_reg_num = aw_pid_2113_get_reg_num; aw_pa->ops.aw_get_irq_type = aw_pid_2113_get_irq_type; aw_pa->ops.aw_reg_force_set = aw_pid_2113_reg_force_set; + aw_pa->ops.aw_frcset_check = aw_pid_2113_frcset_check; aw_pa->int_desc.mask_reg = AW_PID_2113_SYSINTM_REG; aw_pa->int_desc.mask_default = AW_PID_2113_SYSINTM_DEFAULT; @@ -1640,7 +1653,10 @@ static int aw_pid_2113_dev_init(struct aw882xx *aw882xx, int index) AW_PID_2113_INIT_CHECK_VALUE); usleep_range(AW_3000_US, AW_3000_US +10); - aw_pid_2113_efver_check(aw_pa); + aw_pa->efcheck_desc.reg = AW_PID_2113_DBGCTRL_REG; + aw_pa->efcheck_desc.mask = AW_PID_2113_EF_DBMD_MASK; + aw_pa->efcheck_desc.and_val = AW_PID_2113_AND_VALUE; + aw_pa->efcheck_desc.or_val = AW_PID_2113_OR_VALUE; ret = aw_device_probe(aw_pa); diff --git a/asoc/codecs/aw88261/aw_pid_2113_reg.h b/asoc/codecs/aw88261/aw_pid_2113_reg.h index b2146b1b20d3..d3c9a35af9dc 100644 --- a/asoc/codecs/aw88261/aw_pid_2113_reg.h +++ b/asoc/codecs/aw88261/aw_pid_2113_reg.h @@ -2270,6 +2270,17 @@ const unsigned char aw_pid_2113_reg_access[AW_PID_2113_REG_MAX] = { /* detail information of registers end */ + + +/******************************************** + * Efdbmd + *******************************************/ +#define AW_PID_2113_EF_DBMD_MASK (0xfff7) +#define AW_PID_2113_AND_VALUE (0x0000) +#define AW_PID_2113_OR_VALUE (0x0008) + + + /******************************************** * Volume Coefficient *******************************************/ From 4da94a78dac73be770bae52f2e0f3c093801ebbe Mon Sep 17 00:00:00 2001 From: longc103 Date: Wed, 2 Mar 2022 20:32:59 +0800 Subject: [PATCH 23/40] audio-kernel: devon audio bring up changes: 1. add aw88261 config 2. cause hw i2c address conflict, disable receiver smartpa. Change-Id: I242f0edb9ea1db9d0620126d332f5f01cf3837dd Reviewed-on: https://gerrit.mot.com/2205301 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Submit-Approved: Jira Key Tested-by: Jira Key Reviewed-by: SLTApproved: Submit-Approved: Tested-by: Reviewed-by: Yunfei Guo (cherry picked from commit 244d714b75be72eaff960780de0b3f0643f62ce8) Reviewed-on: https://gerrit.mot.com/2206429 Reviewed-by: Hujun Liao --- asoc/Kbuild | 3 +++ asoc/bengal.c | 7 +++++++ asoc/codecs/Kbuild | 6 +++++- asoc/codecs/wcd937x/Kbuild | 4 +++- config/devonautoconf.h | 6 ++++++ dsp/Kbuild | 4 ++++ 6 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 config/devonautoconf.h diff --git a/asoc/Kbuild b/asoc/Kbuild index 4163a09c56f7..8e56deead965 100644 --- a/asoc/Kbuild +++ b/asoc/Kbuild @@ -104,6 +104,9 @@ ifeq ($(KERNEL_BUILD), 0) INCS += -include $(AUDIO_ROOT)/config/hawaoautoconf.h endif + ifneq (,$(filter devon%, $(TARGET_PRODUCT))) + INCS += -include $(AUDIO_ROOT)/config/devonautoconf.h + endif endif # As per target team, build is done as follows: diff --git a/asoc/bengal.c b/asoc/bengal.c index aa6bfdaadf81..f1d93e1cbd75 100644 --- a/asoc/bengal.c +++ b/asoc/bengal.c @@ -5445,6 +5445,13 @@ static struct snd_soc_dai_link_component awinic_codecs[] = { .dai_name = "aw882xx-aif-1-35", .name = "aw882xxacf_smartpa.1-0035", }, +#elif CONFIG_DEVON_AW882XX_SMARTPA +/* { + .of_node = NULL, + .dai_name = "aw882xx-aif-1-36", + .name = "aw882xxacf_smartpa.1-0036", + }, +*/ #else { .of_node = NULL, diff --git a/asoc/codecs/Kbuild b/asoc/codecs/Kbuild index 9af58e0d7cfe..87cd0432ee94 100644 --- a/asoc/codecs/Kbuild +++ b/asoc/codecs/Kbuild @@ -97,6 +97,10 @@ ifeq ($(KERNEL_BUILD), 0) ifneq (,$(filter hawao%, $(TARGET_PRODUCT))) INCS += -include $(AUDIO_ROOT)/config/hawaoautoconf.h endif + + ifneq (,$(filter devon%, $(TARGET_PRODUCT))) + INCS += -include $(AUDIO_ROOT)/config/devonautoconf.h + endif endif # As per target team, build is done as follows: @@ -295,7 +299,7 @@ hdmi_dlkm-y := $(HDMICODEC_OBJS) #Add awinic driver begain ifdef CONFIG_SND_SMARTPA_AW882XX -ifneq (,$(filter rhode% hawao%, $(TARGET_PRODUCT))) +ifneq (,$(filter rhode% hawao% devon%, $(TARGET_PRODUCT))) SND_SOC_AW882XX_OBJS += aw88261/aw882xx.o SND_SOC_AW882XX_OBJS += aw88261/aw_init.o SND_SOC_AW882XX_OBJS += aw88261/aw_bin_parse.o diff --git a/asoc/codecs/wcd937x/Kbuild b/asoc/codecs/wcd937x/Kbuild index 5a4c10c45f0a..c61f84db1666 100644 --- a/asoc/codecs/wcd937x/Kbuild +++ b/asoc/codecs/wcd937x/Kbuild @@ -42,7 +42,9 @@ ifeq ($(KERNEL_BUILD), 0) ifneq (,$(filter hawao%, $(TARGET_PRODUCT))) INCS += -include $(AUDIO_ROOT)/config/hawaoautoconf.h endif - + ifneq (,$(filter devon%, $(TARGET_PRODUCT))) + INCS += -include $(AUDIO_ROOT)/config/devonautoconf.h + endif endif # As per target team, build is done as follows: diff --git a/config/devonautoconf.h b/config/devonautoconf.h new file mode 100644 index 000000000000..888e2a32129f --- /dev/null +++ b/config/devonautoconf.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + */ +#define CONFIG_AW882XX_STEREO_SMARTPA 1 +#define CONFIG_AW882XX_PARAMS_STORED_IN_BIN 1 +#define CONFIG_DEVON_AW882XX_SMARTPA 1 diff --git a/dsp/Kbuild b/dsp/Kbuild index 4e9e82aa4197..33e70a4ca839 100644 --- a/dsp/Kbuild +++ b/dsp/Kbuild @@ -103,6 +103,10 @@ ifeq ($(KERNEL_BUILD), 0) ifneq (,$(filter hawao%, $(TARGET_PRODUCT))) INCS += -include $(AUDIO_ROOT)/config/hawaoautoconf.h endif + + ifneq (,$(filter devon%, $(TARGET_PRODUCT))) + INCS += -include $(AUDIO_ROOT)/config/devonautoconf.h + endif endif From 342bfbb98c3a094ad55790d17cdc4113eac7c046 Mon Sep 17 00:00:00 2001 From: longc103 Date: Fri, 4 Mar 2022 22:26:04 +0800 Subject: [PATCH 24/40] audio-kernel: devon fix build error changes: fix build error Change-Id: I89abc2e606c900a5822d28910665d5f9865dcbc8 Reviewed-on: https://gerrit.mot.com/2207776 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Hujun Liao Reviewed-by: Submit-Approved: Jira Key --- asoc/bengal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asoc/bengal.c b/asoc/bengal.c index f1d93e1cbd75..2db0fab7d1bd 100644 --- a/asoc/bengal.c +++ b/asoc/bengal.c @@ -5445,7 +5445,7 @@ static struct snd_soc_dai_link_component awinic_codecs[] = { .dai_name = "aw882xx-aif-1-35", .name = "aw882xxacf_smartpa.1-0035", }, -#elif CONFIG_DEVON_AW882XX_SMARTPA +#elif defined(CONFIG_DEVON_AW882XX_SMARTPA) /* { .of_node = NULL, .dai_name = "aw882xx-aif-1-36", From 740ed6fca56d7019fb02aaf8cc41beb6a6eb51e5 Mon Sep 17 00:00:00 2001 From: longc103 Date: Mon, 7 Mar 2022 18:16:24 +0800 Subject: [PATCH 25/40] audio-kernel: modify rcv and spk smartpa config changes: add receiver smartpa dai name DVT hardware config: spk smartpa : 0x34 in I2C1; rcv smartpa : 0x35 in I2C1 Change-Id: I7924c0de447ca4745992d645951dfdb597eb99dd Reviewed-on: https://gerrit.mot.com/2208911 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Reviewed-by: Yunfei Guo Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/bengal.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/asoc/bengal.c b/asoc/bengal.c index 2db0fab7d1bd..b3bd4f624e64 100644 --- a/asoc/bengal.c +++ b/asoc/bengal.c @@ -5446,12 +5446,11 @@ static struct snd_soc_dai_link_component awinic_codecs[] = { .name = "aw882xxacf_smartpa.1-0035", }, #elif defined(CONFIG_DEVON_AW882XX_SMARTPA) -/* { + { .of_node = NULL, - .dai_name = "aw882xx-aif-1-36", - .name = "aw882xxacf_smartpa.1-0036", + .dai_name = "aw882xx-aif-1-35", + .name = "aw882xxacf_smartpa.1-0035", }, -*/ #else { .of_node = NULL, From fb92e85f31701bc6e3d2884fe329da5d5fc8faa8 Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Sat, 20 Feb 2021 12:39:35 +0530 Subject: [PATCH 26/40] audio: wcd937x_slave: Add retry while getting logical addr for wcd937x slave cr#2888685 rx slave is not enumerated sometimes, add retry while getting logical addr for wcd937x slave. Change-Id: Iff3f6a071c13f8bdccc72da69e95432be2d4f90a Signed-off-by: Vatsal Bucha Reviewed-on: https://gerrit.mot.com/2220048 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/wcd937x/wcd937x_slave.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/asoc/codecs/wcd937x/wcd937x_slave.c b/asoc/codecs/wcd937x/wcd937x_slave.c index 761e5fe5bacd..9dcd0d2438ab 100644 --- a/asoc/codecs/wcd937x/wcd937x_slave.c +++ b/asoc/codecs/wcd937x/wcd937x_slave.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2018, 2021, The Linux Foundation. All rights reserved. */ #include @@ -11,6 +11,8 @@ #include #include +#define SWR_MAX_RETRY 5 + struct wcd937x_slave_priv { struct swr_device *swr_slave; }; @@ -22,6 +24,7 @@ static int wcd937x_slave_bind(struct device *dev, struct wcd937x_slave_priv *wcd937x_slave = NULL; uint8_t devnum = 0; struct swr_device *pdev = to_swr_device(dev); + int retry = SWR_MAX_RETRY; if (pdev == NULL) { dev_err(dev, "%s: pdev is NULL\n", __func__); @@ -37,7 +40,12 @@ static int wcd937x_slave_bind(struct device *dev, wcd937x_slave->swr_slave = pdev; - ret = swr_get_logical_dev_num(pdev, pdev->addr, &devnum); + do { + /* Add delay for soundwire enumeration */ + usleep_range(100, 110); + ret = swr_get_logical_dev_num(pdev, pdev->addr, &devnum); + } while (ret && --retry); + if (ret) { dev_dbg(&pdev->dev, "%s get devnum %d for dev addr %lx failed\n", From a72bbed4eb84bcd8faef221386e736513a7396ae Mon Sep 17 00:00:00 2001 From: guoyf2 Date: Mon, 21 Mar 2022 11:17:32 +0800 Subject: [PATCH 27/40] asoc: fix wcd937x build error changes; fix https://gerrit.mot.com/#/c/2220048/ build error. Change-Id: I7c88bf76322c5ac32267395d4dab06253826b06e Signed-off-by: guoyf2 Reviewed-on: https://gerrit.mot.com/2221136 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Hong Zhang Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/wcd937x/wcd937x_slave.c | 1 + 1 file changed, 1 insertion(+) diff --git a/asoc/codecs/wcd937x/wcd937x_slave.c b/asoc/codecs/wcd937x/wcd937x_slave.c index 9dcd0d2438ab..3aa4ae45bcc8 100644 --- a/asoc/codecs/wcd937x/wcd937x_slave.c +++ b/asoc/codecs/wcd937x/wcd937x_slave.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include From 4ea53be58898fcd518000352a8a26d9d72b96a0a Mon Sep 17 00:00:00 2001 From: guoyf2 Date: Wed, 23 Mar 2022 15:54:19 +0800 Subject: [PATCH 28/40] asoc: upgrade aw88261 driver to v1.9.0.6 v1.9.0.6: Fix ramp volume issue Change-Id: If6ea0382d77df50d1eac33a6fc78bcdf607698a2 Signed-off-by: guoyf2 Reviewed-on: https://gerrit.mot.com/2223946 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Yunfei Guo Reviewed-by: Hong Zhang Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- asoc/codecs/aw88261/aw882xx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/asoc/codecs/aw88261/aw882xx.c b/asoc/codecs/aw88261/aw882xx.c index 7a2a5567c980..03ba625c9699 100644 --- a/asoc/codecs/aw88261/aw882xx.c +++ b/asoc/codecs/aw88261/aw882xx.c @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.9.0.5" +#define AW882XX_DRIVER_VERSION "v1.9.0.6" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ @@ -818,8 +818,10 @@ static int aw882xx_dev_gain_ctl_set(struct snd_kcontrol *kcontrol, aw882xx_volume = ((AW882XX_MOTO_MAX_GAIN - value) * useful_range) / AW882XX_MOTO_MAX_GAIN + desc->init_volume; - aw_dev_info(aw882xx->dev,"set value = %d, set aw882xx volume = %d", value, aw882xx_volume); + mutex_lock(&aw882xx->lock); aw_dev->ops.aw_set_volume(aw_dev, aw882xx_volume); + mutex_unlock(&aw882xx->lock); + aw_dev_info(aw882xx->dev,"set value = %d, set aw882xx volume = %d", value, aw882xx_volume); return 0; } From aedadd21e6866b190ef2e71b47954f9459a2cdb5 Mon Sep 17 00:00:00 2001 From: guoyf2 Date: Tue, 10 May 2022 13:41:58 +0800 Subject: [PATCH 29/40] asoc: audio:update aw882xx driver to v1.9.0.7 update aw882xx driver to v1.9.0.7 support get algo params from ram. skt-prof-mode = 1 to support by product. Change-Id: Id3d8ebc09cd616ad159486405ec95d67419eaf21 Signed-off-by: guoyf2 Reviewed-on: https://gerrit.mot.com/2264458 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Yunfei Guo Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- asoc/codecs/aw88261/aw882xx.c | 160 +++++++++----- asoc/codecs/aw88261/aw882xx.h | 11 +- asoc/codecs/aw88261/aw_data_type.h | 26 +++ asoc/codecs/aw88261/aw_device.c | 331 +++++++++++++++++++++++++++++ asoc/codecs/aw88261/aw_device.h | 13 +- asoc/codecs/aw88261/aw_dsp.c | 28 +++ asoc/codecs/aw88261/aw_dsp.h | 1 + 7 files changed, 519 insertions(+), 51 deletions(-) diff --git a/asoc/codecs/aw88261/aw882xx.c b/asoc/codecs/aw88261/aw882xx.c index 03ba625c9699..657051144303 100644 --- a/asoc/codecs/aw88261/aw882xx.c +++ b/asoc/codecs/aw88261/aw882xx.c @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.9.0.6" +#define AW882XX_DRIVER_VERSION "v1.9.0.7" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ @@ -54,12 +54,9 @@ static uint32_t g_spin_en = 0; static unsigned int g_runin_test; #endif -#ifdef AW882XX_SPIN_FOR_LOW_LATENCY_ONLY -static unsigned int g_skt_prof_id = 0; -#endif - static DEFINE_MUTEX(g_aw882xx_lock); struct aw_container *g_awinic_cfg = NULL; +struct aw_container *g_awinic_skt_cfg = NULL; #define AW882XX_MOTO_MAX_GAIN (127) @@ -493,6 +490,7 @@ static int aw882xx_mute(struct snd_soc_dai *dai, int mute, int stream) if (mute) { aw882xx->pstream = false; + aw882xx->aw_pa->pre_prof_id = AW_DEFAULT_PRO_ID; cancel_delayed_work_sync(&aw882xx->dc_work); cancel_delayed_work_sync(&aw882xx->start_work); mutex_lock(&aw882xx->lock); @@ -900,6 +898,8 @@ static void aw882xx_request_firmware(struct work_struct *work) container_of(work, struct aw882xx, fw_work.work); const struct firmware *cont = NULL; struct aw_container *aw_cfg = NULL; + const struct firmware *skt_cont = NULL; + struct aw_container *aw_skt_cfg = NULL; int ret = -1; aw882xx->fw_status = AW_DEV_FW_FAILED; @@ -950,6 +950,56 @@ static void aw882xx_request_firmware(struct work_struct *work) } mutex_unlock(&g_aw882xx_lock); + + /*load skt bin*/ + if (aw882xx->skt_prof_mode == AW_PARAMS_DATA_MODE) { + ret = request_firmware(&skt_cont, AW_ALGO_SKT_BIN, aw882xx->dev); + if ((ret) || (!skt_cont)) { + aw_dev_info(aw882xx->dev, "load [%s] failed!", AW_ALGO_SKT_BIN); + return; + } + + aw_dev_info(aw882xx->dev, "load [%s] , file size: [%zu]", + AW_ALGO_SKT_BIN, skt_cont ? skt_cont->size : 0); + + if (g_awinic_skt_cfg == NULL) { + aw_skt_cfg = vzalloc(skt_cont->size + sizeof(int)); + if (aw_skt_cfg == NULL) { + release_firmware(skt_cont); + aw_dev_err(aw882xx->dev, "malloc failed"); + return; + } + aw_skt_cfg->len = skt_cont->size; + memcpy(aw_skt_cfg->data, skt_cont->data, skt_cont->size); + release_firmware(skt_cont); + + ret = aw_dev_load_acf_check(aw_skt_cfg); + if (ret) { + aw_dev_err(aw882xx->dev, "Load [%s] failed ....!", AW_ALGO_SKT_BIN); + vfree(aw_skt_cfg); + aw_skt_cfg = NULL; + return; + } + g_awinic_skt_cfg = aw_skt_cfg; + /*set init algo prof*/ + aw882xx->aw_pa->pre_prof_id = AW_DEFAULT_PRO_ID; + + } else { + aw_skt_cfg = g_awinic_skt_cfg; + release_firmware(skt_cont); + aw_dev_info(aw882xx->dev, "[%s] already loaded...", AW_ALGO_SKT_BIN); + } + + /*parse skt bin*/ + if ((aw882xx->aw_pa->channel == AW_DEV_CH_PRI_L) || (aw882xx->aw_pa->channel == AW_DEV_CH_SEC_L)) { + ret = aw_dev_parse_skt_bin(aw882xx->aw_pa, aw_skt_cfg); + if (ret < 0) { + aw_dev_info(aw882xx->dev, "parse skt bin failed"); + return; + } + } + } + mutex_lock(&aw882xx->lock); /*aw device init*/ ret = aw_device_init(aw882xx->aw_pa, aw_cfg); @@ -1095,10 +1145,22 @@ static int aw882xx_set_rx_en(struct snd_kcontrol *kcontrol, g_algo_rx_en = ctrl_value; aw_dev_info(aw882xx->dev, "set value %d", ctrl_value); - if (ctrl_value) { - ret = aw_dev_set_algo_params_path(aw_dev); - if (ret < 0) - aw_dev_err(aw882xx->dev, "set algo params path failed, ret=%d", ret); + if (aw882xx->skt_prof_mode == AW_PARAMS_PATH_MODE) { + if (ctrl_value) { + ret = aw_dev_set_algo_params_path(aw_dev); + if (ret < 0) + aw_dev_err(aw882xx->dev, "set algo params path failed, ret=%d", ret); + } + } else { + if (aw882xx->aw_pa->pre_prof_id != AW_DEFAULT_PRO_ID) { + aw882xx->cur_algo_prof_id = aw882xx->aw_pa->pre_prof_id; + aw882xx->aw_pa->pre_prof_id = AW_DEFAULT_PRO_ID; + ret = aw_dev_skt_prof_mode(aw_dev, aw882xx->cur_algo_prof_id); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "set algo prof failed"); + return -EINVAL; + } + } } return 0; } @@ -1264,21 +1326,12 @@ static int aw882xx_set_spin(struct snd_kcontrol *kcontrol, aw_dev = aw882xx->aw_pa; ctrl_value = ucontrol->value.integer.value[0]; - -#ifdef AW882XX_SPIN_FOR_LOW_LATENCY_ONLY - if (g_skt_prof_id == (AW882XX_SCENE_FASTTRACK_ID+1)) { -#else if (aw882xx->pstream) { -#endif ret = aw_dev_set_spin(ctrl_value); if (ret) aw_dev_err(aw882xx->dev, "set spin error, ret=%d", ret); } else { -#ifdef AW882XX_SPIN_FOR_LOW_LATENCY_ONLY - aw_dev_info(aw882xx->dev, "fasttrack stream no start"); -#else aw_dev_info(aw882xx->dev, "stream no start only record"); -#endif } g_spin_value = ctrl_value; @@ -1602,6 +1655,7 @@ static int aw882xx_set_prof_id(struct snd_kcontrol *kcontrol, struct aw882xx *aw882xx = aw_componet_codec_ops.codec_get_drvdata(codec); int ctrl_value; + int ret = -1; aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", ucontrol->value.integer.value[0]); @@ -1610,7 +1664,15 @@ static int aw882xx_set_prof_id(struct snd_kcontrol *kcontrol, ctrl_value = ucontrol->value.integer.value[0]; - aw_dev_set_algo_prof(aw_dev, ctrl_value); + if (aw882xx->skt_prof_mode == AW_PARAMS_PATH_MODE) { + aw_dev_set_algo_prof(aw_dev, ctrl_value); + } else { + ret = aw_dev_skt_prof_mode(aw_dev, ctrl_value); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "set_prof failed!, ret = %d", ret); + } + aw882xx->cur_algo_prof_id = ctrl_value; + } return 0; } @@ -1628,10 +1690,14 @@ static int aw882xx_get_prof_id(struct snd_kcontrol *kcontrol, aw_dev = aw882xx->aw_pa; if (aw882xx->pstream) { - ret = aw_dev_get_algo_prof(aw_dev, &ctrl_value); - if (ret) { - aw_dev_err(aw882xx->dev, "get algo prof id failed!, ret = %d", ret); - ctrl_value = 0; + if (aw882xx->skt_prof_mode == AW_PARAMS_PATH_MODE) { + ret = aw_dev_get_algo_prof(aw_dev, &ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "get algo prof id failed!, ret = %d", ret); + ctrl_value = 0; + } + } else { + ctrl_value = aw882xx->cur_algo_prof_id; } ucontrol->value.integer.value[0] = ctrl_value; } else { @@ -1692,7 +1758,7 @@ static void aw882xx_update_algo_scene_st(struct aw882xx *aw882xx, static int aw882xx_update_algo_profile(struct aw882xx *aw882xx) { - int ret = 0; + int ret = -1; int new_skt_prof_id = 0; int cur_skt_prof_id = aw882xx->cur_algo_prof_id; struct aw_device *aw_dev = NULL; @@ -1712,32 +1778,15 @@ static int aw882xx_update_algo_profile(struct aw882xx *aw882xx) aw_dev = aw882xx->aw_pa; /* set new scene pramas to skt */ - ret = aw_dev_set_algo_prof(aw_dev, aw882xx->cur_algo_prof_id); - if (ret < 0) { - aw_dev_err(aw882xx->dev, "set algo prof failed"); - return -1; + if (aw882xx->skt_prof_mode == AW_PARAMS_PATH_MODE) { + ret = aw_dev_set_algo_prof(aw_dev, aw882xx->cur_algo_prof_id); + } else { + ret = aw_dev_skt_prof_mode(aw_dev, aw882xx->cur_algo_prof_id); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "set algo prof failed"); + return -1; + } } - - aw_dev_info(aw882xx->dev, "algo scene hold, cur scene %d", - new_skt_prof_id); - -#ifdef AW882XX_SPIN_FOR_LOW_LATENCY_ONLY - aw_dev_info(aw882xx->dev, "g_skt_prof_id=%d\n",g_skt_prof_id); - - if (new_skt_prof_id == (AW882XX_SCENE_FASTTRACK_ID+1)){ - ret = aw_dev_set_spin(g_spin_value); - if (ret) - aw_dev_err(aw882xx->dev, "set spin error when switch prof id to %d, ret=%d", ret, new_skt_prof_id); - } else if ((g_skt_prof_id == (AW882XX_SCENE_FASTTRACK_ID+1)) - && (new_skt_prof_id != (AW882XX_SCENE_FASTTRACK_ID+1))){ - ret = aw_dev_set_spin(1); - if (ret) - aw_dev_err(aw882xx->dev, "set spin error when switch prof id to %d, ret=%d", ret, new_skt_prof_id); - } - - g_skt_prof_id = new_skt_prof_id; -#endif - return 0; } @@ -2490,6 +2539,15 @@ static int aw882xx_parse_dt(struct device *dev, struct aw882xx *aw882xx, __func__, aw882xx->fade_flag); } + ret = of_property_read_u32(np, "skt-prof-mode", &aw882xx->skt_prof_mode); + if (ret) { + aw882xx->skt_prof_mode = AW_PARAMS_PATH_MODE; + dev_err(dev, "%s: skt-prof-mode get failed,use default value!\n", __func__); + } else { + dev_info(dev, "%s: skt-prof-mode = %d\n", + __func__, aw882xx->skt_prof_mode); + } + return 0; } @@ -3196,6 +3254,10 @@ static int aw882xx_i2c_remove(struct i2c_client *i2c) vfree(g_awinic_cfg); g_awinic_cfg = NULL; } + if (g_awinic_skt_cfg) { + vfree(g_awinic_skt_cfg); + g_awinic_skt_cfg = NULL; + } } mutex_unlock(&g_aw882xx_lock); diff --git a/asoc/codecs/aw88261/aw882xx.h b/asoc/codecs/aw88261/aw882xx.h index c9b41f61c91c..562033451be8 100644 --- a/asoc/codecs/aw88261/aw882xx.h +++ b/asoc/codecs/aw88261/aw882xx.h @@ -20,6 +20,8 @@ #define AW882XX_LOAD_FW_DELAY_TIME (0) #define AW_START_RETRIES (5) #define AW882XX_RUNIN_TEST +#define AW_ALGO_SKT_BIN "aw_skt.bin" +#define AW_DEFAULT_PRO_ID (0xFFFFFFFF) #define CONFIG_AW882XX_ALGO_BIN_PARAMS #define AW_I2C_RETRIES 5 /* 5 times */ @@ -75,6 +77,13 @@ struct aw882xx_scene_info { int is_active; int active_cnt; }; + +enum { + AW_PARAMS_PATH_MODE = 0, + AW_PARAMS_DATA_MODE, +}; + + #endif @@ -218,7 +227,7 @@ struct aw882xx { int aw882xx_algo_prof_id; int cur_algo_prof_id; #endif - + unsigned int skt_prof_mode; unsigned int fade_flag; unsigned char index; diff --git a/asoc/codecs/aw88261/aw_data_type.h b/asoc/codecs/aw88261/aw_data_type.h index 36aaf4f077ce..5184dfa01189 100644 --- a/asoc/codecs/aw88261/aw_data_type.h +++ b/asoc/codecs/aw88261/aw_data_type.h @@ -18,12 +18,14 @@ struct aw_msg_hdr { #define CUSTOMER_NAME_MAX (16) #define CFG_VERSION_MAX (4) #define DEV_NAME_MAX (16) +#define PROFILE_STR_MAX (32) #define ACF_FILE_ID (0xa15f908) enum aw_cfg_hdr_version { AW_CFG_HDR_VER_0_0_0_1 = 0x00000001, + AW_CFG_HDR_VER_1_0_0_0 = 0x01000000, }; enum aw_cfg_dde_type { @@ -53,6 +55,12 @@ enum profile_data_type { AW_PROFILE_DATA_TYPE_MAX, }; +enum algo_profile_data_type { + AW_PROFILE_DATA_TYPE_ALGO_DSP, + AW_PROFILE_DATA_TYPE_ALGO_MAX, +}; + + enum aw_prof_type { AW_PROFILE_MUSIC = 0, AW_PROFILE_VOICE, @@ -100,6 +108,22 @@ struct aw_cfg_dde { uint32_t reserve[5]; }; +struct aw_cfg_dde_v_1_0_0_0 { + uint32_t type; /*DDE type id*/ + char dev_name[DEV_NAME_MAX]; + uint16_t dev_index; /*dev id*/ + uint16_t dev_bus; /*dev bus id*/ + uint16_t dev_addr; /*dev addr id*/ + uint16_t dev_profile; /*dev profile id*/ + uint32_t data_type; /*data type id*/ + uint32_t data_size; + uint32_t data_offset; + uint32_t data_crc; + char dev_profile_str[PROFILE_STR_MAX]; + uint32_t chip_id; + uint32_t reserve[4]; +}; + struct aw_sec_data_desc { uint32_t len; unsigned char *data; @@ -108,6 +132,7 @@ struct aw_sec_data_desc { struct aw_prof_desc { uint32_t id; uint32_t prof_st; + char *prf_str; struct aw_sec_data_desc sec_desc[AW_PROFILE_DATA_TYPE_MAX]; }; @@ -117,6 +142,7 @@ struct aw_all_prof_info { struct aw_prof_info { int count; + char **prof_name_list; struct aw_prof_desc *prof_desc; }; diff --git a/asoc/codecs/aw88261/aw_device.c b/asoc/codecs/aw88261/aw_device.c index c2d768060f8c..193d77598b42 100644 --- a/asoc/codecs/aw88261/aw_device.c +++ b/asoc/codecs/aw88261/aw_device.c @@ -150,6 +150,102 @@ static int aw_dev_check_cfg_by_hdr(struct aw_container *aw_cfg) return 0; } +static int aw_dev_parse_check_acf_by_hdr_v_1_0_0_0(struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = NULL; + struct aw_cfg_dde_v_1_0_0_0 *cfg_dde = NULL; + unsigned int end_data_offset = 0; + unsigned int act_data = 0; + unsigned int hdr_ddt_len = 0; + uint8_t act_crc8 = 0; + int i; + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + + /*check file type id is awinic acf file*/ + if (cfg_hdr->a_id != ACF_FILE_ID) { + aw_pr_err("not acf type file"); + return -EINVAL; + } + + hdr_ddt_len = cfg_hdr->a_hdr_offset + cfg_hdr->a_ddt_size; + if (hdr_ddt_len > aw_cfg->len) { + aw_pr_err("hdrlen with ddt_len [%d] overflow file size[%d]", + cfg_hdr->a_hdr_offset, aw_cfg->len); + return -EINVAL; + } + + /*check data size*/ + cfg_dde = (struct aw_cfg_dde_v_1_0_0_0 *)((char *)aw_cfg->data + cfg_hdr->a_hdr_offset); + act_data += hdr_ddt_len; + for (i = 0; i < cfg_hdr->a_ddt_num; i++) + act_data += cfg_dde[i].data_size; + + if (act_data != aw_cfg->len) { + aw_pr_err("act_data[%d] not equal to file size[%d]!", + act_data, aw_cfg->len); + return -EINVAL; + } + + for (i = 0; i < cfg_hdr->a_ddt_num; i++) { + /* data check */ + end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size; + if (end_data_offset > aw_cfg->len) { + aw_pr_err("a_ddt_num[%d] end_data_offset[%d] overflow file size[%d]", + i, end_data_offset, aw_cfg->len); + return -EINVAL; + } + + /* crc check */ + act_crc8 = aw_dev_crc8_check(aw_cfg->data + cfg_dde[i].data_offset, cfg_dde[i].data_size); + if (act_crc8 != cfg_dde[i].data_crc) { + aw_pr_err("a_ddt_num[%d] crc8 check failed, act_crc8:0x%x != data_crc 0x%x", + i, (uint32_t)act_crc8, cfg_dde[i].data_crc); + return -EINVAL; + } + } + + aw_pr_info("project name [%s]", cfg_hdr->a_project); + aw_pr_info("custom name [%s]", cfg_hdr->a_custom); + aw_pr_info("version name [%d.%d.%d.%d]", cfg_hdr->a_version[3], cfg_hdr->a_version[2], + cfg_hdr->a_version[1], cfg_hdr->a_version[0]); + aw_pr_info("author id %d", cfg_hdr->a_author_id); + + return 0; +} + +static int aw_dev_parse_raw_dsp_skt(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc) +{ + aw_dev_info(aw_dev->dev, "data_size:%d enter", data_len); + + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_ALGO_DSP].data = data; + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_ALGO_DSP].len = data_len; + + return 0; +} + +static int aw_dev_get_chan_group(struct aw_device *aw_dev, int *chan_group) +{ + switch (aw_dev->channel) { + + case AW_DEV_CH_PRI_L: + case AW_DEV_CH_PRI_R: + *chan_group = AW_DEV_CH_GROUP_PRI; + break; + case AW_DEV_CH_SEC_L: + case AW_DEV_CH_SEC_R: + *chan_group = AW_DEV_CH_GROUP_SEC; + break; + default: + aw_pr_err("can not find chan_group, channel %d ", aw_dev->channel); + return -EINVAL; + } + + aw_pr_dbg("chan_group[%d] ", *chan_group); + return 0; +} + int aw_dev_load_acf_check(struct aw_container *aw_cfg) { struct aw_cfg_hdr *cfg_hdr = NULL; @@ -169,6 +265,8 @@ int aw_dev_load_acf_check(struct aw_container *aw_cfg) switch (cfg_hdr->a_hdr_version) { case AW_CFG_HDR_VER_0_0_0_1: return aw_dev_check_cfg_by_hdr(aw_cfg); + case AW_CFG_HDR_VER_1_0_0_0: + return aw_dev_parse_check_acf_by_hdr_v_1_0_0_0(aw_cfg); default: aw_pr_err("unsupported hdr_version [0x%x]", cfg_hdr->a_hdr_version); return -EINVAL; @@ -452,6 +550,234 @@ static int aw_dev_cfg_get_vaild_prof(struct aw_device *aw_dev, return 0; } + +/******************ACF bin format V1.0.0.0************************/ +static int aw_dev_parse_get_scene_count_v1_0_0_0(struct aw_device *aw_dev, + struct aw_container *aw_cfg, int *count) +{ + struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + struct aw_cfg_dde_v_1_0_0_0 *cfg_dde = + (struct aw_cfg_dde_v_1_0_0_0 *)(aw_cfg->data + cfg_hdr->a_hdr_offset); + int i = 0; + int chan_group = -1; + int ret = -1; + + ret = aw_dev_get_chan_group(aw_dev, &chan_group); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get chan_group failed "); + return ret; + } + + for (i = 0; i < cfg_hdr->a_ddt_num; ++i) { + if ((cfg_dde[i].data_type == ACF_SEC_TYPE_DSP) && + (cfg_dde[i].type == AW_SKT_TYPE_ID) && + (chan_group == cfg_dde[i].dev_index)) { + (*count)++; + } + } + + if ((*count) == 0) { + aw_dev_err(aw_dev->dev, "can't find scene"); + return -EINVAL; + } + + aw_dev_info(aw_dev->dev, "scene count is %d", (*count)); + return 0; +} + + +static int aw_dev_parse_create_prof_name_list_v_1_0_0_0(struct aw_device *aw_dev) +{ + struct aw_prof_info *prof_info = &aw_dev->skt_prof_info; + struct aw_prof_desc *prof_desc= prof_info->prof_desc; + int i; + + if (prof_desc == NULL) { + aw_dev_err(aw_dev->dev, "prof_desc is NULL"); + return -EINVAL; + } + + prof_info->prof_name_list = devm_kzalloc(aw_dev->dev, + prof_info->count * PROFILE_STR_MAX, + GFP_KERNEL); + if (prof_info->prof_name_list == NULL) { + aw_dev_err(aw_dev->dev, "prof_name_list devm_kzalloc failed"); + return -ENOMEM; + } + + for (i = 0; i < prof_info->count; i++) { + prof_info->prof_name_list[i] = prof_desc[i].prf_str; + aw_dev_info(aw_dev->dev, "prof name list[%d]:%s, id=%d", i, prof_info->prof_name_list[i], prof_desc[i].id); + } + + return 0; +} + +static int aw_dev_parse_skt_type_v_1_0_0_0(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr) +{ + int i = 0; + int ret = -1; + int scene_count = 0; + struct aw_prof_info *prof_info = &aw_dev->skt_prof_info; + struct aw_cfg_dde_v_1_0_0_0 *cfg_dde = + (struct aw_cfg_dde_v_1_0_0_0 *)((char *)prof_hdr + prof_hdr->a_hdr_offset); + int chan_group = -1; + aw_dev_info(aw_dev->dev, "enter"); + + ret = aw_dev_get_chan_group(aw_dev, &chan_group); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get chan_group failed "); + return ret; + } + + for (i = 0; i < prof_hdr->a_ddt_num; i++) { + if ((cfg_dde[i].type == AW_SKT_TYPE_ID) && + (cfg_dde[i].data_type == ACF_SEC_TYPE_DSP) && + (chan_group == cfg_dde[i].dev_index)) { + + ret = aw_dev_parse_raw_dsp_skt(aw_dev, + (uint8_t *)prof_hdr + cfg_dde[i].data_offset, + cfg_dde[i].data_size, &prof_info->prof_desc[scene_count]); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "parse dsp bin data failed"); + return ret; + } + + prof_info->prof_desc[scene_count].prf_str = cfg_dde[i].dev_profile_str; + prof_info->prof_desc[scene_count].id = cfg_dde[i].dev_profile; + + scene_count++; + } + } + aw_dev_dbg(aw_dev->dev, "scene_count = %d", scene_count); + return 0; +} + +static int aw_dev_parse_acf_by_hdr_v_1_0_0_0(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_prof_info *prof_info = &aw_dev->skt_prof_info; + struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + int ret; + + ret = aw_dev_parse_get_scene_count_v1_0_0_0(aw_dev, aw_cfg, &prof_info->count); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get scene count failed"); + return ret; + } + + prof_info->prof_desc = devm_kzalloc(aw_dev->dev, + prof_info->count * sizeof(struct aw_prof_desc), + GFP_KERNEL); + if (prof_info->prof_desc == NULL) { + aw_dev_err(aw_dev->dev, "prof_desc devm_kzalloc failed"); + return -ENOMEM; + } + + ret = aw_dev_parse_skt_type_v_1_0_0_0(aw_dev, cfg_hdr); + if (ret < 0) { + aw_dev_err(aw_dev->dev, " failed"); + return ret; + } + + ret = aw_dev_parse_create_prof_name_list_v_1_0_0_0(aw_dev); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "create prof name list failed"); + return ret; + } + + return 0; +} + +int aw_dev_parse_skt_bin(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = NULL; + int ret = -1; + + if (aw_dev == NULL || aw_cfg == NULL) { + aw_dev_err(aw_dev->dev, "pointer is NULL"); + return -ENOMEM; + } + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + + if (cfg_hdr->a_hdr_version == AW_CFG_HDR_VER_1_0_0_0) { + ret = aw_dev_parse_acf_by_hdr_v_1_0_0_0(aw_dev, aw_cfg); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "hdr_cersion[0x%x] parse failed", + cfg_hdr->a_hdr_version); + return ret; + } + } else { + aw_dev_err(aw_dev->dev, "skt bin format [0x%x] unsupported", cfg_hdr->a_hdr_version); + } + + return 0; +} + +struct aw_sec_data_desc *aw_dev_get_algo_prof_data(struct aw_device *aw_dev, int prof_id) +{ + struct aw_sec_data_desc *prof_data = NULL; + + struct aw_prof_info *prof_info = &aw_dev->skt_prof_info; + int i; + + aw_dev_dbg(aw_dev->dev, "prof num = %d", prof_info->count); + for (i = 0; i < prof_info->count; i++) { + if (prof_id == prof_info->prof_desc[i].id) { + prof_data = &prof_info->prof_desc[i].sec_desc[AW_PROFILE_DATA_TYPE_ALGO_DSP]; + break; + } + } + + return prof_data; +} + +int aw_dev_skt_prof_mode(struct aw_device *aw_dev, int prof_id) +{ + struct aw_sec_data_desc *prof_data = NULL; + int ret = -1; + struct list_head *dev_list = NULL; + struct list_head *pos = NULL; + struct aw_device *local_dev = NULL; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed "); + return ret; + } + + aw_dev_info(aw_dev->dev, "set_cur_prof=%d,pre_prof=%d", + prof_id, aw_dev->pre_prof_id); + + if (prof_id != aw_dev->pre_prof_id) { + + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + + if ((local_dev->channel == AW_DEV_CH_PRI_L) || (local_dev->channel == AW_DEV_CH_SEC_L)) { + prof_data = aw_dev_get_algo_prof_data(local_dev, prof_id); + if (prof_data) { + ret = aw_dev_set_algo_prof_data(local_dev, prof_data->data, prof_data->len); + if (ret < 0) { + aw_dev_err(local_dev->dev, "set algo prof [%d] failed", prof_id); + return -EINVAL; + } + } else { + aw_dev_err(local_dev->dev, "prof data is NULL"); + return -EINVAL; + } + } + } + + aw_dev->pre_prof_id = prof_id; + aw_dev_info(aw_dev->dev, "set algo prof [%d] ok", prof_id); + } + + return 0; +} + + static int aw_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg) { struct aw_cfg_hdr *cfg_hdr = NULL; @@ -1503,6 +1829,11 @@ int aw_dev_get_spin(int *spin_mode) return aw_dsp_read_spin(spin_mode); } +int aw_dev_set_algo_prof_data(struct aw_device *aw_dev, void *prof_data, unsigned int prof_len) +{ + return aw_dsp_set_algo_prof_data(aw_dev, prof_data, prof_len); +} + int aw_dev_set_algo_prof(struct aw_device *aw_dev, int prof_id) { return aw_dsp_set_algo_prof(aw_dev, prof_id); diff --git a/asoc/codecs/aw88261/aw_device.h b/asoc/codecs/aw88261/aw_device.h index cbc3eec67c48..4c5ec7a5c961 100644 --- a/asoc/codecs/aw88261/aw_device.h +++ b/asoc/codecs/aw88261/aw_device.h @@ -58,6 +58,12 @@ enum AW_DEV_FW_STATUS { AW_DEV_FW_OK, }; +enum { + AW_DEV_CH_GROUP_PRI = 0, + AW_DEV_CH_GROUP_SEC = 1, + AW_DEV_CH_GROUP_MAX, +}; + struct aw_device_ops { int (*aw_i2c_write)(struct aw_device *aw_dev, unsigned char reg_addr, unsigned int reg_data); int (*aw_i2c_read)(struct aw_device *aw_dev, unsigned char reg_addr, unsigned int *reg_data); @@ -234,6 +240,7 @@ struct aw_device { char monitor_name[AW_NAME_MAX]; void *private_data; char algo_path[AW_ALGO_PATH_MAX]; + int pre_prof_id; struct aw_int_desc int_desc; struct aw_pwd_desc pwd_desc; @@ -249,6 +256,7 @@ struct aw_device { struct aw_ipeak_desc ipeak_desc; struct aw_volume_desc volume_desc; struct aw_prof_info prof_info; + struct aw_prof_info skt_prof_info; struct aw_cali_desc cali_desc; struct aw_monitor_desc monitor_desc; struct aw_soft_rst soft_rst; @@ -258,10 +266,11 @@ struct aw_device { struct list_head list_node; }; - +struct aw_sec_data_desc *aw_dev_get_algo_prof_data(struct aw_device *aw_dev, int prof_id); int aw_dev_load_acf_check(struct aw_container *aw_cfg); void aw_dev_deinit(struct aw_device *aw_dev); int aw_device_init(struct aw_device *aw_dev, struct aw_container *aw_cfg); +int aw_dev_parse_skt_bin(struct aw_device *aw_dev, struct aw_container *aw_cfg); int aw_device_start(struct aw_device *aw_dev); int aw_device_stop(struct aw_device *aw_dev); int aw_dev_reg_update(struct aw_device *aw_dev, bool force); @@ -298,6 +307,8 @@ int aw_dev_get_afe_module_en(int type, int *status); int aw_dev_set_copp_module_en(bool enable); int aw_dev_set_spin(int spin_mode); int aw_dev_get_spin(int *spin_mode); +int aw_dev_skt_prof_mode(struct aw_device *aw_dev, int prof_id); +int aw_dev_set_algo_prof_data(struct aw_device *aw_dev, void *prof_data, unsigned int prof_len); int aw_dev_set_algo_prof(struct aw_device *aw_dev, int prof_id); int aw_dev_get_algo_prof(struct aw_device *aw_dev, int *prof_id); int aw_dev_set_algo_params_path(struct aw_device *aw_dev); diff --git a/asoc/codecs/aw88261/aw_dsp.c b/asoc/codecs/aw88261/aw_dsp.c index ed2425d92dc6..ba2baa236795 100644 --- a/asoc/codecs/aw88261/aw_dsp.c +++ b/asoc/codecs/aw88261/aw_dsp.c @@ -29,6 +29,8 @@ static DEFINE_MUTEX(g_aw_dsp_lock); #define AW_MSG_ID_ALGO_PARAMS_PATH (0x00000014) #define AW_MSG_ID_VERSION (0x00000008) #define AW_MSG_ID_VERSION_NEW (0x00000012) +#define AW_MSG_ID_SET_ALGO_PROF_PRI (0x10013D25) +#define AW_MSG_ID_SET_ALGO_PROF_SEC (0x10013D2C) /*dsp params id*/ @@ -389,6 +391,32 @@ static int aw_write_data_to_dsp(uint32_t param_id, void *data, int size) } /************************* dsp communication function *****************************/ +int aw_dsp_set_algo_prof_data(struct aw_device *aw_dev, void *data, unsigned int size) +{ + int ret = 0; + uint32_t params_id = 0; + + if (aw_dev->channel == AW_DEV_CH_PRI_L) { + params_id = AW_MSG_ID_SET_ALGO_PROF_PRI; + } else if (aw_dev->channel == AW_DEV_CH_SEC_L) { + params_id = AW_MSG_ID_SET_ALGO_PROF_SEC; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + aw_dev_dbg(aw_dev->dev, "params_id=%u,prof_data:%p, len=%u", params_id, data, size); + + ret = aw_qcom_write_data_to_dsp(params_id, data, size); + if (ret < 0) { + aw_pr_err("write prof_id failed "); + return ret; + } + + aw_pr_dbg("write prof_id done"); + return 0; + +} int aw_dsp_set_algo_prof(struct aw_device *aw_dev, int prof_id) { diff --git a/asoc/codecs/aw88261/aw_dsp.h b/asoc/codecs/aw88261/aw_dsp.h index 703c8a8cdf46..25c25310c93c 100644 --- a/asoc/codecs/aw88261/aw_dsp.h +++ b/asoc/codecs/aw88261/aw_dsp.h @@ -67,6 +67,7 @@ int aw_dsp_set_copp_module_en(bool enable); int aw_dsp_write_spin(int spin_mode); int aw_dsp_read_spin(int *spin_mode); int aw_get_algo_version(struct aw_device *aw_dev, char *algo_ver_buf); +int aw_dsp_set_algo_prof_data(struct aw_device *aw_dev, void *data, unsigned int size); int aw_dsp_set_algo_prof(struct aw_device *aw_dev, int prof_id); int aw_dsp_get_algo_prof(struct aw_device *aw_dev, int *prof_id); int aw_dsp_set_algo_params_path(struct aw_device *aw_dev); From 5d5f2bc88ed8246572b5bad9f5d56ec2e359004a Mon Sep 17 00:00:00 2001 From: guoyf2 Date: Tue, 10 May 2022 13:46:08 +0800 Subject: [PATCH 30/40] audio-kernel: enlarge map size for aw882xx algo after aw882xx algo upate to support get params from ram. need enlarge mapsize to 16k. Change-Id: I69c6933148b96b014e0497b8e240067387de9bc9 Signed-off-by: guoyf2 Reviewed-on: https://gerrit.mot.com/2264461 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Zhengming Yao Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- config/hawaoautoconf.h | 1 + dsp/q6afe.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/config/hawaoautoconf.h b/config/hawaoautoconf.h index 91cf4167b34e..b39a7c599171 100644 --- a/config/hawaoautoconf.h +++ b/config/hawaoautoconf.h @@ -4,3 +4,4 @@ #define CONFIG_AW882XX_STEREO_SMARTPA 1 #define CONFIG_AW882XX_PARAMS_STORED_IN_BIN 1 #define CONFIG_HAWAO_AW882XX_SMARTPA 1 +#define CONFIG_AW882XX_MAPSIZE_16K 1 diff --git a/dsp/q6afe.c b/dsp/q6afe.c index ca381c51c5e1..13d3b22e1da7 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -3763,7 +3763,11 @@ int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id, if (aw_cal->map_data.dma_buf == 0) { /*Minimal chunk size is 4K*/ +#ifdef CONFIG_AW882XX_MAPSIZE_16K + aw_cal->map_data.map_size = SZ_16K; +#else aw_cal->map_data.map_size = SZ_4K; +#endif result = msm_audio_ion_alloc(&(aw_cal->map_data.dma_buf), aw_cal->map_data.map_size, &(aw_cal->cal_data.paddr),&len, @@ -3790,7 +3794,11 @@ int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id, goto err; } +#ifdef CONFIG_AW882XX_MAPSIZE_16K + if (cmd_size > (SZ_16K - sizeof(struct param_hdr_v3))) { +#else if (cmd_size > (SZ_4K - sizeof(struct param_hdr_v3))) { +#endif pr_err("%s: Invalid payload size = %d\n", __func__, cmd_size); result = -EINVAL; goto err; From 521195a275db57959d78a78187bd85929903fdb6 Mon Sep 17 00:00:00 2001 From: zhangh12 Date: Wed, 11 May 2022 10:37:48 +0800 Subject: [PATCH 31/40] audio-kernel: enable CONFIG_AW882XX_MAPSIZE_16K for rhode after aw882xx algo upate to support get params from ram. need enlarge mapsize to 16k. Change-Id: Ib9c6e46965d071cd640627ff00be0e200f5a7488 Signed-off-by: zhangh12 Reviewed-on: https://gerrit.mot.com/2265470 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- config/rhodeautoconf.h | 1 + 1 file changed, 1 insertion(+) diff --git a/config/rhodeautoconf.h b/config/rhodeautoconf.h index 8312cd204184..396a010428b8 100644 --- a/config/rhodeautoconf.h +++ b/config/rhodeautoconf.h @@ -3,3 +3,4 @@ */ #define CONFIG_AW882XX_STEREO_SMARTPA 1 #define CONFIG_AW882XX_PARAMS_STORED_IN_BIN 1 +#define CONFIG_AW882XX_MAPSIZE_16K 1 From 4045b34a01a24e09f1feff09b0903c2d6a4184e3 Mon Sep 17 00:00:00 2001 From: guoyf2 Date: Thu, 12 May 2022 14:21:20 +0800 Subject: [PATCH 32/40] asoc: devon support get awinic skt params from ram changes: support get awinic skt params from ram Change-Id: I17aba7f05fcf5ab182de2859faf7ba733062af24 Signed-off-by: guoyf2 Reviewed-on: https://gerrit.mot.com/2266732 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Hong Zhang Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- config/devonautoconf.h | 1 + 1 file changed, 1 insertion(+) diff --git a/config/devonautoconf.h b/config/devonautoconf.h index 888e2a32129f..073924714781 100644 --- a/config/devonautoconf.h +++ b/config/devonautoconf.h @@ -4,3 +4,4 @@ #define CONFIG_AW882XX_STEREO_SMARTPA 1 #define CONFIG_AW882XX_PARAMS_STORED_IN_BIN 1 #define CONFIG_DEVON_AW882XX_SMARTPA 1 +#define CONFIG_AW882XX_MAPSIZE_16K 1 From c9072fb61feed0063c708371079dc744b812b0f4 Mon Sep 17 00:00:00 2001 From: guoyf2 Date: Mon, 16 May 2022 10:30:05 +0800 Subject: [PATCH 33/40] asoc: update aw88261 driver's spin relase function Changes: update aw88261 driver's spin relase function Change-Id: Iee49b81aad887656194647a6e79efc78bcf366a0 Signed-off-by: guoyf2 Reviewed-on: https://gerrit.mot.com/2269496 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Zhengming Yao Reviewed-by: Wanlong Zhou Reviewed-by: Wang Wang Submit-Approved: Jira Key --- asoc/codecs/aw88261/aw882xx.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/asoc/codecs/aw88261/aw882xx.c b/asoc/codecs/aw88261/aw882xx.c index 657051144303..67dedcb4fd3c 100644 --- a/asoc/codecs/aw88261/aw882xx.c +++ b/asoc/codecs/aw88261/aw882xx.c @@ -49,6 +49,7 @@ static unsigned int g_algo_copp_en = false; #ifdef AW_SPIN_ENABLE static unsigned int g_spin_value = 0; static uint32_t g_spin_en = 0; +static uint32_t g_spin_relase_time = 100; #endif #ifdef AW882XX_RUNIN_TEST static unsigned int g_runin_test; @@ -1406,7 +1407,7 @@ static int aw882xx_set_spin_status(struct snd_kcontrol *kcontrol, aw_componet_codec_ops.kcontrol_codec(kcontrol); struct aw882xx *aw882xx = aw_componet_codec_ops.codec_get_drvdata(codec); - struct aw_spin_param spin_param; + //struct aw_spin_param spin_param; aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", ucontrol->value.integer.value[0]); @@ -1416,7 +1417,7 @@ static int aw882xx_set_spin_status(struct snd_kcontrol *kcontrol, ctrl_value = ucontrol->value.integer.value[0]; if (g_algo_rx_en == true) { - ret = aw_dev_set_spin_param(aw_dev, ctrl_value, spin_param.relase_time); + ret = aw_dev_set_spin_param(aw_dev, ctrl_value, g_spin_relase_time); if (ret) aw_dev_err(aw882xx->dev, "set spin status error, ret=%d", ret); @@ -1449,9 +1450,10 @@ static int aw882xx_get_spin_relase_time(struct snd_kcontrol *kcontrol, ctrl_value = 0; } ucontrol->value.integer.value[0] = ctrl_value; + g_spin_relase_time = ctrl_value; } else { aw_dev_info(aw882xx->dev, "no stream, use record value"); - + ucontrol->value.integer.value[0] = g_spin_relase_time; } aw_dev_dbg(aw882xx->dev, "spin value is %ld", ucontrol->value.integer.value[0]); return 0; @@ -1481,9 +1483,10 @@ static int aw882xx_set_spin_relase_time(struct snd_kcontrol *kcontrol, if (ret) aw_dev_err(aw882xx->dev, "set spin release time error, ret=%d", ret); } else { - aw_dev_info(aw882xx->dev, "set release time failed"); + aw_dev_info(aw882xx->dev, "set release time failed, g_spin_en=%d, g_algo_rx_en=%d\n", + g_spin_en, g_algo_rx_en); } - + g_spin_relase_time = ucontrol->value.integer.value[0]; return 0; } From 48ce8d105ada9fc47b52e0eaea04c9006089b2d4 Mon Sep 17 00:00:00 2001 From: guoyf2 Date: Tue, 24 May 2022 17:55:24 +0800 Subject: [PATCH 34/40] asoc: change linein threshold Qcom mbhc default linein threshold is 5K, but some AUX carkit speaker impedance is not large than it, mbhc detects it as headphone, not use LINE audio parameter, causes audio performance issue. As kp required , change it as 4K. Change-Id: I2f99963d24f7822de5fda9b824f60b2d5cba4144 Signed-off-by: guoyf2 Reviewed-on: https://gerrit.mot.com/2278335 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Yunfei Guo Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- asoc/bengal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asoc/bengal.c b/asoc/bengal.c index b3bd4f624e64..eecdf4a79e72 100644 --- a/asoc/bengal.c +++ b/asoc/bengal.c @@ -583,7 +583,7 @@ static struct wcd_mbhc_config wcd_mbhc_cfg = { .key_code[5] = 0, .key_code[6] = 0, .key_code[7] = 0, - .linein_th = 5000, + .linein_th = 4000, .moisture_en = false, .mbhc_micbias = MIC_BIAS_2, .anc_micbias = MIC_BIAS_2, From 91411c23508c3b9ff77a443ed675017644f40056 Mon Sep 17 00:00:00 2001 From: guoyf2 Date: Wed, 8 Jun 2022 12:12:54 +0800 Subject: [PATCH 35/40] asoc: update aw882xx driver to v1.9.0.9 changes: fix set gain ctl timing issue. Change-Id: I1e52b9ce38deee2c12b5bb4417fc9a975e481b0a Signed-off-by: guoyf2 Reviewed-on: https://gerrit.mot.com/2293053 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Yunfei Guo Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- asoc/codecs/aw88261/aw882xx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/asoc/codecs/aw88261/aw882xx.c b/asoc/codecs/aw88261/aw882xx.c index 67dedcb4fd3c..aadc5ff74ca9 100644 --- a/asoc/codecs/aw88261/aw882xx.c +++ b/asoc/codecs/aw88261/aw882xx.c @@ -35,7 +35,7 @@ #include "aw_log.h" #include "aw_dsp.h" -#define AW882XX_DRIVER_VERSION "v1.9.0.7" +#define AW882XX_DRIVER_VERSION "v1.9.0.9" #define AW882XX_I2C_NAME "aw882xxacf_smartpa" #define AW_READ_CHIPID_RETRIES 5 /* 5 times */ @@ -797,9 +797,11 @@ static int aw882xx_dev_gain_ctl_set(struct snd_kcontrol *kcontrol, int value = 0; int aw882xx_volume = 0; + int useful_range = 0; + mutex_lock(&aw882xx->lock); /* Note: The larger the volume value is, the smaller the actual volume */ - int useful_range = desc->mute_volume - desc->init_volume; + useful_range = desc->mute_volume - desc->init_volume; aw_dev_info(aw882xx->dev, "desc->init_volume = %d mute_volume = %d", desc->init_volume, desc->mute_volume ); @@ -817,7 +819,7 @@ static int aw882xx_dev_gain_ctl_set(struct snd_kcontrol *kcontrol, aw882xx_volume = ((AW882XX_MOTO_MAX_GAIN - value) * useful_range) / AW882XX_MOTO_MAX_GAIN + desc->init_volume; - mutex_lock(&aw882xx->lock); + aw_dev->ops.aw_set_volume(aw_dev, aw882xx_volume); mutex_unlock(&aw882xx->lock); aw_dev_info(aw882xx->dev,"set value = %d, set aw882xx volume = %d", value, aw882xx_volume); From 47aa08b26fffb51bc39d207c2b498a42c90bf333 Mon Sep 17 00:00:00 2001 From: guoyf2 Date: Wed, 15 Jun 2022 11:18:09 +0800 Subject: [PATCH 36/40] mbhc: set the type to lineout when zl or zr is zero. surge will cause zl or zr is zero. this kind of surge happened on aux. so set this kind as lineout to avoid aux params wrong. Propagation from (CR) Change-Id: I2cc654a3db7cede55bc2ccdd6b67bcdd4da118ed Signed-off-by: guoyf2 Reviewed-on: https://gerrit.mot.com/2301019 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Yunfei Guo Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- asoc/codecs/wcd-mbhc-v2.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/asoc/codecs/wcd-mbhc-v2.c b/asoc/codecs/wcd-mbhc-v2.c index bdd6e0d8404a..3b16b58d4592 100644 --- a/asoc/codecs/wcd-mbhc-v2.c +++ b/asoc/codecs/wcd-mbhc-v2.c @@ -692,10 +692,12 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, &mbhc->zl, &mbhc->zr); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, fsm_en); - pr_info("%s,compute impedance,zl=%d,zr=%d\n", - __func__, mbhc->zl, mbhc->zr); - if ((mbhc->zl > mbhc->mbhc_cfg->linein_th) && - (mbhc->zr > mbhc->mbhc_cfg->linein_th) && + pr_info("%s,compute impedance,zl = %d,zr = %d\n", + __func__, mbhc->zl, mbhc->zr); + if ((((mbhc->zl > mbhc->mbhc_cfg->linein_th) && + (mbhc->zr > mbhc->mbhc_cfg->linein_th)) || + (mbhc->zl == 0) || + (mbhc->zr == 0)) && (jack_type == SND_JACK_HEADPHONE)) { jack_type = SND_JACK_LINEOUT; mbhc->force_linein = true; From 464114a7078dc8c141b0b265c14c419cd6399a70 Mon Sep 17 00:00:00 2001 From: zhangh12 Date: Thu, 30 Jun 2022 17:57:03 +0800 Subject: [PATCH 37/40] asoc: update q6afe for aw882xx update afe_callback to handle RESET_EVENTS for aw882xx; call aw_cal_unmap_memory to unmap memory. Change-Id: I38dc659654e063286a50e712a06aec32d2d4d7b0 Signed-off-by: zhangh12 Reviewed-on: https://gerrit.mot.com/2318890 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key --- dsp/q6afe.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 13d3b22e1da7..d49016c60fd8 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -51,6 +51,8 @@ static struct afe_avcs_payload_port_mapping *pm[MAX_ALLOWED_USE_CASES]; #define AFE_PARAM_ID_AWDSP_RX_PARAMS (0x10013D12) //#define AFE_PORT_ID_AWDSP_RX (AFE_PORT_ID_PRIMARY_MI2S_RX) //#define AFE_PORT_ID_AWDSP_TX (AFE_PORT_ID_PRIMARY_MI2S_TX) + +void aw_cal_unmap_memory(void); #endif enum { @@ -943,6 +945,9 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) return -EINVAL; } if (data->opcode == RESET_EVENTS) { + #ifdef CONFIG_AW882XX_DSP + aw_cal_unmap_memory(); + #endif /*CONFIG_AW882XX_DSP*/ pr_debug("%s: reset event = %d %d apr[%pK]\n", __func__, data->reset_event, data->reset_proc, this_afe.apr); From d3da11881a0105ab8106e835bff1cf06d4c4cf56 Mon Sep 17 00:00:00 2001 From: Shalini Manjunatha Date: Mon, 10 Apr 2023 16:19:35 +0530 Subject: [PATCH 38/40] dsp: afe: check for param size before copying check for the proper param size before copying, to avoid buffer overflow. Mot-CRs-fixed: (CR) CVE-Fixed: CVE-2023-33035 CRs-Fixed: 3438021 Change-Id: I70c52e6ab76f528ea3714784ab9013b070839c40 Signed-off-by: Shalini Manjunatha Signed-off-by: Ashutosh Verma Reviewed-on: https://gerrit.mot.com/2674847 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- dsp/q6afe.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/dsp/q6afe.c b/dsp/q6afe.c index d49016c60fd8..cf096e023911 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include @@ -767,32 +768,74 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload, switch (param_hdr.param_id) { case AFE_PARAM_ID_CALIB_RES_CFG_V2: expected_size += sizeof(struct asm_calib_res_cfg); + if (param_hdr.param_size != sizeof(struct asm_calib_res_cfg)) { + pr_err("%s: Error: param_size %d is greater than expected\n", + __func__,param_hdr.param_size); + return -EINVAL; + } data_dest = (u32 *) &this_afe.calib_data; break; case AFE_PARAM_ID_SP_V2_TH_VI_FTM_PARAMS: expected_size += sizeof(struct afe_sp_th_vi_ftm_params); + if (param_hdr.param_size != sizeof(struct afe_sp_th_vi_ftm_params)) { + pr_err("%s: Error: param_size %d is greater than expected\n", + __func__,param_hdr.param_size); + return -EINVAL; + } data_dest = (u32 *) &this_afe.th_vi_resp; break; case AFE_PARAM_ID_SP_V2_TH_VI_V_VALI_PARAMS: expected_size += sizeof(struct afe_sp_th_vi_v_vali_params); + if (param_hdr.param_size != sizeof(struct afe_sp_th_vi_v_vali_params)) { + pr_err("%s: Error: param_size %d is greater than expected\n", + __func__,param_hdr.param_size); + return -EINVAL; + } data_dest = (u32 *) &this_afe.th_vi_v_vali_resp; break; case AFE_PARAM_ID_SP_V2_EX_VI_FTM_PARAMS: expected_size += sizeof(struct afe_sp_ex_vi_ftm_params); + if (param_hdr.param_size != sizeof(struct afe_sp_ex_vi_ftm_params)) { + pr_err("%s: Error: param_size %d is greater than expected\n", + __func__,param_hdr.param_size); + return -EINVAL; + } data_dest = (u32 *) &this_afe.ex_vi_resp; break; case AFE_PARAM_ID_SP_RX_TMAX_XMAX_LOGGING: expected_size += sizeof( struct afe_sp_rx_tmax_xmax_logging_param); + if (param_hdr.param_size != sizeof(struct afe_sp_rx_tmax_xmax_logging_param)) { + pr_err("%s: Error: param_size %d is greater than expected\n", + __func__,param_hdr.param_size); + return -EINVAL; + } data_dest = (u32 *) &this_afe.xt_logging_resp; break; case AFE_PARAM_ID_SP_V4_CALIB_RES_CFG: expected_size += sizeof( struct afe_sp_v4_param_th_vi_calib_res_cfg); + if (param_hdr.param_size != sizeof( + struct afe_sp_v4_param_th_vi_calib_res_cfg)) { + pr_err("%s: Error: param_size %d is greater than expected\n", + __func__,param_hdr.param_size); + return -EINVAL; + } data_dest = (u32 *) &this_afe.spv4_calib_data; break; case AFE_PARAM_ID_SP_V4_TH_VI_FTM_PARAMS: num_ch = data_start[0]; + if (num_ch > SP_V2_NUM_MAX_SPKRS) { + pr_err("%s: Error: num_ch %d is greater than expected\n", + __func__,num_ch); + return -EINVAL; + } + if (param_hdr.param_size != (sizeof(struct afe_sp_v4_param_th_vi_ftm_params) + + (num_ch * sizeof(struct afe_sp_v4_channel_ftm_params)))) { + pr_err("%s: Error: param_size %d is greater than expected\n", + __func__,param_hdr.param_size); + return -EINVAL; + } this_afe.spv4_th_vi_ftm_rcvd_param_size = param_hdr.param_size; data_dest = (u32 *)&this_afe.spv4_th_vi_ftm_resp; expected_size += @@ -801,6 +844,18 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload, break; case AFE_PARAM_ID_SP_V4_TH_VI_V_VALI_PARAMS: num_ch = data_start[0]; + if (num_ch > SP_V2_NUM_MAX_SPKRS) { + pr_err("%s: Error: num_ch %d is greater than expected\n", + __func__,num_ch); + return -EINVAL; + } + if (param_hdr.param_size != (sizeof(struct afe_sp_v4_param_th_vi_v_vali_params) + + (num_ch * + sizeof(struct afe_sp_v4_channel_v_vali_params)))) { + pr_err("%s: Error: param_size %d is greater than expected\n", + __func__,param_hdr.param_size); + return -EINVAL; + } this_afe.spv4_v_vali_rcvd_param_size = param_hdr.param_size; data_dest = (u32 *)&this_afe.spv4_v_vali_resp; expected_size += @@ -810,6 +865,18 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload, break; case AFE_PARAM_ID_SP_V4_EX_VI_FTM_PARAMS: num_ch = data_start[0]; + if (num_ch > SP_V2_NUM_MAX_SPKRS) { + pr_err("%s: Error: num_ch %d is greater than expected\n", + __func__,num_ch); + return -EINVAL; + } + if (param_hdr.param_size != (sizeof(struct afe_sp_v4_param_ex_vi_ftm_params) + + (num_ch * + sizeof(struct afe_sp_v4_channel_ex_vi_ftm_params)))) { + pr_err("%s: Error: param_size %d is greater than expected\n", + __func__,param_hdr.param_size); + return -EINVAL; + } this_afe.spv4_ex_vi_ftm_rcvd_param_size = param_hdr.param_size; data_dest = (u32 *)&this_afe.spv4_ex_vi_ftm_resp; expected_size += @@ -818,6 +885,18 @@ static int32_t sp_make_afe_callback(uint32_t opcode, uint32_t *payload, break; case AFE_PARAM_ID_SP_V4_RX_TMAX_XMAX_LOGGING: num_ch = data_start[0]; + if (num_ch > SP_V2_NUM_MAX_SPKRS) { + pr_err("%s: Error: num_ch %d is greater than expected\n", + __func__,num_ch); + return -EINVAL; + } + if (param_hdr.param_size != (sizeof(struct afe_sp_v4_param_tmax_xmax_logging) + + (num_ch * + sizeof(struct afe_sp_v4_channel_tmax_xmax_params)))) { + pr_err("%s: Error: param_size %d is greater than expected\n", + __func__,param_hdr.param_size); + return -EINVAL; + } this_afe.spv4_max_log_rcvd_param_size = param_hdr.param_size; data_dest = (u32 *)&this_afe.spv4_max_log_resp; expected_size += From 2a747d37bb11d6c8b4d3d87c4a7394efbd81638a Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Wed, 5 Apr 2023 15:46:55 +0530 Subject: [PATCH 39/40] dsp: q6adm: Resolve mem corruption in adm cb Conversion of negative "num_modules" var value will result in max possible unsigned int value and hence can cause mem corruption when accessed. Resolve this by assigning the same data type to "num_modules" var as used in the calling fn. Mot-CRs-fixed: (CR) CVE-Fixed: CVE-2023-33034 CRs-Fixed: 3438425 Change-Id: I4c9d7215b9c7345637e1eb3a1992a41fef71c5cb Signed-off-by: Soumya Managoli Signed-off-by: Ashutosh Verma Reviewed-on: https://gerrit.mot.com/2673745 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- dsp/q6adm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dsp/q6adm.c b/dsp/q6adm.c index bb316c313afc..1021f8347ec4 100644 --- a/dsp/q6adm.c +++ b/dsp/q6adm.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include @@ -1550,7 +1551,7 @@ static int32_t adm_callback(struct apr_client_data *data, void *priv) { uint32_t *payload; int port_idx, copp_idx, idx, client_id; - int num_modules; + uint32_t num_modules; int ret; if (data == NULL) { From 2f467e069db289a9e36be1eb1cbd42eb8283a761 Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Wed, 12 Apr 2023 14:27:28 +0530 Subject: [PATCH 40/40] ASoC: msm-pcm-host-voice: Address buffer overflow in hpcm capture copy There is no check for the copy data size of the ADSP returned payload. Add check for the max hpcm_buf_node size before copy to avoid buffer out of bounds issue. Mot-CRs-fixed: (CR) CVE-Fixed: CVE-2023-33031 CRs-Fixed: 3442627 Change-Id: Id647888430ce302359a857ef54d321bee99889bf Signed-off-by: Soumya Managoli Signed-off-by: Ashutosh Verma Reviewed-on: https://gerrit.mot.com/2671710 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Hujun Liao Submit-Approved: Jira Key --- asoc/msm-pcm-host-voice-v2.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/asoc/msm-pcm-host-voice-v2.c b/asoc/msm-pcm-host-voice-v2.c index 41c3982dd343..8d1a486b96a7 100644 --- a/asoc/msm-pcm-host-voice-v2.c +++ b/asoc/msm-pcm-host-voice-v2.c @@ -683,6 +683,12 @@ static void hpcm_copy_capture_data_to_queue(struct dai_data *dai_data, if (dai_data->substream == NULL) return; + if (len >= HPCM_MAX_VOC_PKT_SIZE) { + pr_err("%s: Copy capture data len %d is > HPCM_MAX_VOC_PKT_SIZE\n", + __func__, len); + return; + } + /* Copy out buffer packet into free_queue */ spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags);