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<