From 793f3936a64ed44ae9cb838344aaad70246406ad Mon Sep 17 00:00:00 2001 From: yaozm1 Date: Tue, 10 Mar 2020 17:16:33 +0800 Subject: [PATCH] asoc: Add fs1818 support for guamp/guamna Propagation of the https://gerrit.mot.com/#/c/1510671/ in order to enable fs1818 as audio PA for guamp and guamna EVT sample devices. Change-Id: I772278e04a98bd4c95443e911d228d746797a263 Signed-off-by: yaozm1 Reviewed-on: https://gerrit.mot.com/1510671 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Xiangpo Zhao Submit-Approved: Jira Key Reviewed-on: https://gerrit.mot.com/1914385 Reviewed-by: Monica Mabuchi Reviewed-by: Nelson Guedes Paulo Junior Reviewed-by: Jian Luan --- asoc/bengal.c | 6 + asoc/codecs/Android.mk | 9 +- asoc/codecs/Kbuild | 11 + asoc/codecs/foursemi/Kconfig | 5 + asoc/codecs/foursemi/Makefile | 9 + asoc/codecs/foursemi/fs16xx.c | 1718 +++++++++++++++++++++ asoc/codecs/foursemi/fs16xx.h | 190 +++ asoc/codecs/foursemi/fs16xx_calibration.c | 734 +++++++++ asoc/codecs/foursemi/fs16xx_calibration.h | 128 ++ asoc/codecs/foursemi/fs16xx_define.h | 120 ++ asoc/codecs/foursemi/fs16xx_preset.c | 452 ++++++ asoc/codecs/foursemi/fs16xx_preset.h | 99 ++ asoc/codecs/foursemi/fs16xx_reg_def.h | 81 + asoc/codecs/foursemi/fs16xx_regs.h | 1210 +++++++++++++++ config/bengalauto.conf | 1 + config/bengalautoconf.h | 1 + 16 files changed, 4773 insertions(+), 1 deletion(-) create mode 100644 asoc/codecs/foursemi/Kconfig create mode 100644 asoc/codecs/foursemi/Makefile create mode 100644 asoc/codecs/foursemi/fs16xx.c create mode 100644 asoc/codecs/foursemi/fs16xx.h create mode 100644 asoc/codecs/foursemi/fs16xx_calibration.c create mode 100644 asoc/codecs/foursemi/fs16xx_calibration.h create mode 100644 asoc/codecs/foursemi/fs16xx_define.h create mode 100644 asoc/codecs/foursemi/fs16xx_preset.c create mode 100644 asoc/codecs/foursemi/fs16xx_preset.h create mode 100644 asoc/codecs/foursemi/fs16xx_reg_def.h create mode 100644 asoc/codecs/foursemi/fs16xx_regs.h diff --git a/asoc/bengal.c b/asoc/bengal.c index 5cfdca61cd55..99733bed120e 100644 --- a/asoc/bengal.c +++ b/asoc/bengal.c @@ -5441,8 +5441,13 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .stream_name = "Primary MI2S Playback", .cpu_dai_name = "msm-dai-q6-mi2s.0", .platform_name = "msm-pcm-routing", +#ifdef CONFIG_SND_SOC_FS16XX + .codec_name = "fs16xx.1-0034", + .codec_dai_name = "fs16xx_codec_left", +#else .codec_name = "msm-stub-codec.1", .codec_dai_name = "msm-stub-rx", +#endif .no_pcm = 1, .dpcm_playback = 1, .id = MSM_BACKEND_DAI_PRI_MI2S_RX, @@ -6220,6 +6225,7 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) rc = of_property_read_u32(dev->of_node, "qcom,afe-rxtx-lb", &val); if (!rc && val) { + memcpy(msm_bengal_dai_links + total_links, msm_afe_rxtx_lb_be_dai_link, sizeof(msm_afe_rxtx_lb_be_dai_link)); diff --git a/asoc/codecs/Android.mk b/asoc/codecs/Android.mk index 95f6c1abfe92..6715a8b3ab34 100644 --- a/asoc/codecs/Android.mk +++ b/asoc/codecs/Android.mk @@ -175,7 +175,14 @@ LOCAL_MODULE_DEBUG_ENABLE := true LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) include $(DLKM_DIR)/AndroidKernelModule.mk ########################################################### - +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_CHIPSET)_snd_soc_fs16xx.ko +LOCAL_MODULE_KBUILD_NAME := fs16xx_dlkm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +########################################################### endif # DLKM check endif # supported target check diff --git a/asoc/codecs/Kbuild b/asoc/codecs/Kbuild index 5051969904cb..298002e27de1 100644 --- a/asoc/codecs/Kbuild +++ b/asoc/codecs/Kbuild @@ -252,6 +252,7 @@ ifeq ($(KERNEL_BUILD), 1) obj-y += wsa883x/ obj-y += sdm660_cdc/ obj-y += awinic/ + obj-y += foursemi/ endif # Module information used by KBuild framework obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd_core_dlkm.o @@ -293,5 +294,15 @@ obj-$(CONFIG_SND_SMARTPA_AW882XX) += aw882xx_dlkm.o aw882xx_dlkm-y := $(SND_SOC_AW882XX_OBJS) #Add awinic driver end +#Add fs1818 driver begain +ifdef CONFIG_SND_SOC_FS16XX + SND_SOC_FS16XX_OBJS += foursemi/fs16xx.o + SND_SOC_FS16XX_OBJS += foursemi/fs16xx_preset.o + SND_SOC_FS16XX_OBJS += foursemi/fs16xx_calibration.o +endif +obj-$(CONFIG_SND_SOC_FS16XX) += fs16xx_dlkm.o +fs16xx_dlkm-y := $(SND_SOC_FS16XX_OBJS) +#Add fs1818 driver end + # inject some build related information DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/asoc/codecs/foursemi/Kconfig b/asoc/codecs/foursemi/Kconfig new file mode 100644 index 000000000000..3f080886161d --- /dev/null +++ b/asoc/codecs/foursemi/Kconfig @@ -0,0 +1,5 @@ +config SND_SOC_FS16XX + tristate "Support foursemi fs16xx chip" + depends on I2C + help + foursemi fs16xx driver \ No newline at end of file diff --git a/asoc/codecs/foursemi/Makefile b/asoc/codecs/foursemi/Makefile new file mode 100644 index 000000000000..89f77b90b6a6 --- /dev/null +++ b/asoc/codecs/foursemi/Makefile @@ -0,0 +1,9 @@ +### +# Copyright (C) 2018 Fourier Semiconductor Inc. All rights reserved. +# + +# FS16XX VERSION INFO, DON'T REMOVE THIS LINE!!! +snd-soc-fs16xx-objs := fs16xx.o +snd-soc-fs16xx-objs += fs16xx_preset.o +snd-soc-fs16xx-objs += fs16xx_calibration.o +obj-$(CONFIG_SND_SOC_FS16XX) += snd-soc-fs16xx.o diff --git a/asoc/codecs/foursemi/fs16xx.c b/asoc/codecs/foursemi/fs16xx.c new file mode 100644 index 000000000000..3d61b32bf271 --- /dev/null +++ b/asoc/codecs/foursemi/fs16xx.c @@ -0,0 +1,1718 @@ +/* + * Fs16xx device driver implementation. + * Copyright (C) 2017 FourSemi Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "fs16xx_regs.h" +#include "fs16xx_reg_def.h" +#include "fs16xx_calibration.h" +#include "fs16xx_preset.h" +#include "fs16xx.h" + + +static DEFINE_MUTEX(lr_lock); +static int stereo_mode; +//static const char *fs_dev_name = "Right"; +static struct fs16xx_priv * g_fs16xx_priv[MAX_DEV_COUNT] = {NULL, NULL}; + + +fs16xx_dev_t *get_drvdata_by_context(struct snd_soc_codec *codec) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)) + return snd_soc_codec_get_drvdata(codec); +#else + return snd_soc_component_get_drvdata(codec); +#endif +} + +fs16xx_dev_t *get_drvdata_by_dai(struct snd_soc_dai *dai) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)) + struct snd_soc_codec *codec = dai->codec; +#else + struct snd_soc_codec *codec = dai->component; +#endif + return get_drvdata_by_context(codec); +} + +fs16xx_dev_t *get_drvdata_by_kctrl(struct snd_kcontrol *kcontrol) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)) + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); +#else + struct snd_soc_codec *codec = snd_soc_kcontrol_component(kcontrol); +#endif + return get_drvdata_by_context(codec); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +static int snd_soc_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int val) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_context(codec); + if (!fs16xx) { + PRINT_ERROR("%s: invalid dev", __func__); + return -EINVAL; + } + return fs16xx_write_register16(fs16xx->id, reg, val); +} + +static unsigned int snd_soc_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_context(codec); + unsigned short val; + int ret; + + if (!fs16xx) { + PRINT_ERROR("%s: invalid dev", __func__); + return -EINVAL; + } + ret = fs16xx_read_register16(fs16xx->id, reg, &val); + if (ret != Fs16xx_Error_OK) { + return -1; + } + return val; +} +#endif + +/* + * I2C Read/Write Functions + */ +static int fs16xx_i2c_read(struct i2c_client *fs16xx_client, + u8 reg, u8 *value, int len) +{ + int err; + int tries = 0; + + struct i2c_msg msgs[] = { + { + .addr = fs16xx_client->addr, + .flags = 0, + .len = 1, + .buf = ®, + }, + { + .addr = fs16xx_client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = value, + }, + }; + + do { + err = i2c_transfer(fs16xx_client->adapter, msgs, + ARRAY_SIZE(msgs)); + if (err != ARRAY_SIZE(msgs)) + msleep_interruptible(I2C_RETRY_DELAY); + } while ((err != ARRAY_SIZE(msgs)) && (++tries < I2C_RETRIES)); + + if (err != ARRAY_SIZE(msgs)) { + dev_err(&fs16xx_client->dev, "read transfer error %d\n" + , err); + err = -EIO; + } else { + err = 0; + } + + return err; +} + +static int fs16xx_i2c_write(struct i2c_client *fs16xx_client, + u8 *value, u8 len) +{ + int err; + int tries = 0; + + struct i2c_msg msgs[] = { + { + .addr = fs16xx_client->addr, + .flags = 0, + .len = len, + .buf = value, + }, + }; + + do { + err = i2c_transfer(fs16xx_client->adapter, msgs, + ARRAY_SIZE(msgs)); + if (err != ARRAY_SIZE(msgs)) + msleep_interruptible(I2C_RETRY_DELAY); + } while ((err != ARRAY_SIZE(msgs)) && (++tries < I2C_RETRIES)); + + if (err != ARRAY_SIZE(msgs)) { + dev_err(&fs16xx_client->dev, "write transfer error\n"); + err = -EIO; + } else { + err = 0; + } + + return err; +} +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 108)) +static int fs16xx_bulk_write(struct snd_soc_codec *codec, unsigned int reg, + const void *data, size_t len) +{ + struct fs16xx_priv *fs16xx = get_drvdata_by_context(codec); + u8 chunk_buf[FS16XX_MAX_I2C_SIZE + 1]; + int offset = 0; + int ret = 0; + /* first byte is mem address */ + int remaining_bytes = len - 1; + int chunk_size = FS16XX_MAX_I2C_SIZE; + + chunk_buf[0] = reg & 0xff; + mutex_lock(&fs16xx->i2c_rw_lock); + while ((remaining_bytes > 0)) { + if (remaining_bytes < chunk_size) + chunk_size = remaining_bytes; + + memcpy(chunk_buf + 1, data + 1 + offset, chunk_size); + ret = fs16xx_i2c_write(fs16xx->control_data, chunk_buf, + chunk_size + 1); + offset = offset + chunk_size; + remaining_bytes = remaining_bytes - chunk_size; + } + + mutex_unlock(&fs16xx->i2c_rw_lock); + return ret; +} +#endif +/* +static int fs16xx_bulk_read(struct snd_soc_codec *codec, u8 reg, + u8 *data, int len) +{ + struct fs16xx_priv *fs16xx = get_drvdata_by_context(codec); + int ret = 0; + int offset = 0; + int remaining_bytes = len; + int chunk_size = FS16XX_MAX_I2C_SIZE; + + mutex_lock(&fs16xx->i2c_rw_lock); + while ((remaining_bytes > 0)) { + if (remaining_bytes < chunk_size) + chunk_size = remaining_bytes; + ret = fs16xx_i2c_read(fs16xx->control_data, reg, data + offset, + chunk_size); + offset = offset + chunk_size; + remaining_bytes = remaining_bytes - chunk_size; + } + mutex_unlock(&fs16xx->i2c_rw_lock); + + return ret; +}*/ + +static int fs16xx_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) +{ + struct fs16xx_priv *fs16xx = get_drvdata_by_context(codec); + u8 buf[3] = {0, 0, 0}; + int ret; + buf[0] = (reg & 0xff); + buf[1] = (val >> 8) & 0xff; + buf[2] = (val & 0xff); + + if (!fs16xx) { + PRINT_ERROR("%s: invalid param", __func__); + return -EINVAL; + } + + mutex_lock(&fs16xx->i2c_rw_lock); + ret = fs16xx_i2c_write(fs16xx->control_data, buf, ARRAY_SIZE(buf)); + mutex_unlock(&fs16xx->i2c_rw_lock); + return ret; +} + +static unsigned int fs16xx_read(struct snd_soc_codec *codec, unsigned int reg) +{ + struct fs16xx_priv *fs16xx = get_drvdata_by_context(codec); + u8 buf[3] = {0, 0, 0}; + int len = 2; + int val = -EIO; + + if (!fs16xx) { + PRINT_ERROR("%s: invalid param", __func__); + return -EINVAL; + } + mutex_lock(&fs16xx->i2c_rw_lock); + if (fs16xx_i2c_read(fs16xx->control_data, reg & 0xff, buf, len) == 0) { + val = (buf[0] << 8 | buf[1]); + } + mutex_unlock(&fs16xx->i2c_rw_lock); + return val; +} + +Fs16xx_Error_t fs16xx_handle_is_open(Fs16xx_devId_t id) { + Fs16xx_Error_t err = Fs16xx_Error_NotOpen; + if(id >= 0 && id < MAX_DEV_COUNT && g_fs16xx_priv[id]) { + err = Fs16xx_Error_OK; + } + return err; +} + +Fs_Dev_State* fs16xx_get_dev_state_by_id(Fs16xx_devId_t id) { + + struct fs16xx_priv *fs16xx; + if(id >= 0 && id < MAX_DEV_COUNT) { + fs16xx = g_fs16xx_priv[id]; + if(fs16xx) { + return &(fs16xx->dev_state); + } else { + return NULL; + } + } + return NULL; +} + +struct snd_soc_codec * fs16xx_get_codec_by_id(Fs16xx_devId_t id) { + if(id >= 0 && id < MAX_DEV_COUNT && g_fs16xx_priv[id]) { + return g_fs16xx_priv[id]->codec; + } else { + return NULL; + } +} + +Fs16xx_Error_t fs16xx_write_register16(Fs16xx_devId_t id, + unsigned char subaddress, + unsigned short value) +{ + Fs16xx_Error_t err = Fs16xx_Error_OK; + struct fs16xx_priv *fs16xx; + int ret; + u8 buf[3] = {0, 0, 0}; + buf[0] = (subaddress & 0xff); + buf[1] = (value >> 8) & 0xff; + buf[2] = (value & 0xff); + + if(id >= 0 && id < MAX_DEV_COUNT) { + fs16xx = g_fs16xx_priv[id]; + if(fs16xx) { + mutex_lock(&fs16xx->i2c_rw_lock); + ret = fs16xx_i2c_write(fs16xx->control_data, + buf, ARRAY_SIZE(buf)); + mutex_unlock(&fs16xx->i2c_rw_lock); + if(ret < 0) { + err = Fs16xx_Error_I2C_Fatal; + PRINT_ERROR("%s: i2c error!", __func__); + } + } else { + err = Fs16xx_Error_Bad_Parameter; + PRINT_ERROR("%s: Invalid fs16xx device instance!", __func__); + } + } else { + err = Fs16xx_Error_Bad_Parameter; + PRINT_ERROR("%s: Invalid fs16xx codec instance!", __func__); + } + + return err; +} + +Fs16xx_Error_t fs16xx_read_register16(Fs16xx_devId_t id, + unsigned char subaddress, + unsigned short *pValue) +{ + Fs16xx_Error_t err = Fs16xx_Error_OK; + struct fs16xx_priv *fs16xx; + int ret = 0; + u8 buf[2] = {0, 0}; + + //printk(KERN_INFO "%s: id: %d, g_fs16xx_priv[%d]: %p\n", __func__, id, id, g_fs16xx_priv[id]); + if(id >= 0 && id < MAX_DEV_COUNT) { + fs16xx = g_fs16xx_priv[id]; + if(fs16xx) { + mutex_lock(&fs16xx->i2c_rw_lock); + ret = fs16xx_i2c_read(fs16xx->control_data, + subaddress & 0xff, buf, 2); + mutex_unlock(&fs16xx->i2c_rw_lock); + + if(ret == 0) { + *pValue = (buf[0] << 8 | buf[1]); + } else { + err = Fs16xx_Error_I2C_Fatal; + PRINT_ERROR("%s: i2c error!", __func__); + } + } else { + err = Fs16xx_Error_Bad_Parameter; + PRINT_ERROR("%s: Invalid fs16xx device instance!", __func__); + } + } else { + err = Fs16xx_Error_Bad_Parameter; + PRINT_ERROR("%s: Invalid fs16xx codec instance!", __func__); + } + + return err; +} + +#if 0 +static const struct snd_kcontrol_new fs16xx_left_snd_controls[] = { + SOC_SINGLE("fs16xx Left device Right channel select", FS16XX_I2SCTRL_REG, 3, 3, 0), + SOC_SINGLE("fs16xx Left device MUTE", FS16XX_DACCTRL_REG, FS16XX_DACCTRL_REG_MUTE_POS, 1, 0), + SOC_SINGLE("fs16xx Left device DSP ALG", FS16XX_DSPCTRL_REG, FS16XX_DSPCTRL_REG_DSPEN_POS, 1, 0), +}; +static const struct snd_kcontrol_new fs16xx_right_snd_controls[] = { + SOC_SINGLE("fs16xx Right device Left channel select", FS16XX_I2SCTRL_REG, 3, 3, 0), + SOC_SINGLE("fs16xx Right device MUTE", FS16XX_DACCTRL_REG, FS16XX_DACCTRL_REG_MUTE_POS, 1, 0), + SOC_SINGLE("fs16xx Right device DSP ALG", FS16XX_DSPCTRL_REG, FS16XX_DSPCTRL_REG_DSPEN_POS, 1, 0), +}; +#else + +/* + * ASOC controls + */ +static int fs16xx_get_preset_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_kctrl(kcontrol); + Fs_Dev_State *dev_state = &(fs16xx->dev_state); + DEBUGPRINT("%s: enter codec name=%s preset=%d", __func__, + fs16xx->fs16xx_dev, dev_state->cur_preset); + mutex_lock(&lr_lock); + ucontrol->value.integer.value[0] = dev_state->cur_preset; + mutex_unlock(&lr_lock); + return 0; +} + +static int fs16xx_put_preset_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_kctrl(kcontrol); + int preset = ucontrol->value.integer.value[0]; + int id; + + if(fs16xx == NULL) { + PRINT_ERROR("%s: Bad parameter.\n", __func__); + } + PRINT("%s: enter codec name=%s set preset=%d\n", __func__, + fs16xx->fs16xx_dev, preset); + + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + id = FS16XX_DEV_INDEX_L; + } else if(!strncmp("right", fs16xx->fs16xx_dev, 5)) { + id = FS16XX_DEV_INDEX_R; + } else { + PRINT_ERROR("%s: invalid device name!\n", __func__); + return 1; + } + mutex_lock(&lr_lock); + fs16xx_set_preset(id, preset); + mutex_unlock(&lr_lock); + return 1; +} + +static int fs16xx_ext_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_kctrl(kcontrol); + struct snd_soc_codec *codec = fs16xx->codec; + u16 valPwr; + u16 powerstate; + int ret; + + valPwr = 0x1; + powerstate = 0; + // printk("%s,codec_name = %s\n" , __func__, codec->component.name); + mutex_lock(&lr_lock); + valPwr = snd_soc_read(codec, FS16XX_SYSCTRL_REG); + powerstate = valPwr & FS16XX_SYSCTRL_REG_PWDN_MSK; + PRINT("%s: valPwr = 0x%04x,powerstate = 0x%04x",__func__,valPwr,powerstate); + if(powerstate){ + PRINT("%s: need to power up",__func__); + valPwr &= (~FS16XX_SYSCTRL_REG_PWDN_MSK); + snd_soc_write(codec, FS16XX_SYSCTRL_REG, valPwr); + } + mutex_unlock(&lr_lock); + ret = snd_soc_put_volsw(kcontrol, ucontrol); + return 0; +} + +static const struct snd_kcontrol_new fs16xx_left_snd_controls[] = { + SOC_SINGLE_EXT("fs16xx Left device Right channel select", FS16XX_I2SCTRL_REG, 3, 3, 0, + snd_soc_get_volsw,fs16xx_ext_put), + SOC_SINGLE_EXT("fs16xx Left device MUTE", FS16XX_DACCTRL_REG, FS16XX_DACCTRL_REG_MUTE_POS, 1, 0, + snd_soc_get_volsw,fs16xx_ext_put), + SOC_SINGLE_EXT("fs16xx Left device DSP ALG", FS16XX_DSPCTRL_REG, FS16XX_DSPCTRL_REG_DSPEN_POS, 1, 0, + snd_soc_get_volsw,fs16xx_ext_put), + SOC_SINGLE_EXT("fs16xx Left preset mode", SND_SOC_NOPM, 0, 2, 0, fs16xx_get_preset_mode, fs16xx_put_preset_mode), +}; +static const struct snd_kcontrol_new fs16xx_right_snd_controls[] = { + SOC_SINGLE_EXT("fs16xx Right device Left channel select", FS16XX_I2SCTRL_REG, 3, 3, 0, + snd_soc_get_volsw,fs16xx_ext_put), + SOC_SINGLE_EXT("fs16xx Right device MUTE", FS16XX_DACCTRL_REG, FS16XX_DACCTRL_REG_MUTE_POS, 1, 0, + snd_soc_get_volsw,fs16xx_ext_put), + SOC_SINGLE_EXT("fs16xx Right device DSP ALG", FS16XX_DSPCTRL_REG, FS16XX_DSPCTRL_REG_DSPEN_POS, 1, 0, + snd_soc_get_volsw,fs16xx_ext_put), + SOC_SINGLE_EXT("fs16xx Right preset mode", SND_SOC_NOPM, 0, 2, 0, fs16xx_get_preset_mode, fs16xx_put_preset_mode), +}; +#endif + +static Fs16xx_Error_t fs16xx_UpdateOTCTRL(struct snd_soc_codec *codec) { + u16 value = 0, offset = 0; + + value = snd_soc_read(codec, FS16XX_OTPPG1W0_REG); + offset = ((value & 0x80) == 0) ? (value & 0x7F) : (-1 * (value & 0x7F)); + + value = snd_soc_read(codec, FS16XX_OTCTRL_REG); + + if ((offset + value) < 0xFFFF && (offset + value > 0)) { + snd_soc_write(codec, FS16XX_OTCTRL_REG, (u16)(offset + value)); + PRINT("%s: update OTCTRL 0x%04x", __func__, (u16)(offset + value)); + } else { + PRINT_ERROR("%s: mono mode!", __func__); + } + + return Fs16xx_Error_StateTimedOut; +} + +static Fs16xx_Error_t fs16xx_wait_clk_stable(struct snd_soc_codec *codec) { + int max_try = 500; + u16 value; + + while(max_try > 0) { + value = snd_soc_read(codec, FS16XX_STATUS_REG); + if((value & FS16XX_STATUS_REG_PLLS) != 0) { + return Fs16xx_Error_OK; + } + max_try--; + Sleep(1); + } + + return Fs16xx_Error_StateTimedOut; +} + +Fs16xx_Error_t fs16xx_wait_boost_ssend(struct snd_soc_codec *codec) { + int max_try = 150; + unsigned short value; + + while(max_try > 0) { + value = snd_soc_read(codec, FS16XX_BSTCTRL_REG); + if((value & FS16XX_BSTCTRL_REG_SSEND_MSK) != 0) { + return Fs16xx_Error_OK; + } + max_try--; + Sleep(1); + } + + return Fs16xx_Error_StateTimedOut; +} + +Fs16xx_Error_t fs16xx_wait_dsp_off(Fs16xx_devId_t id, int wait_for_adc, int max_try) { + Fs16xx_Error_t err; + unsigned short value; + unsigned short flag = FS16XX_DIGSTAT_REG_DACRUN; + if(wait_for_adc) { + flag = FS16XX_DIGSTAT_REG_DACRUN | FS16XX_DIGSTAT_REG_ADCRUN; + } + + while(max_try > 0) { + err = fs16xx_read_register16(id, FS16XX_DIGSTAT_REG, &value); + if(Fs16xx_Error_OK == err && ((value & flag) == 0)) { + return Fs16xx_Error_OK; + } + max_try--; + Sleep(1); + } + + return Fs16xx_Error_StateTimedOut; +} + +static void fs16xx_power(struct snd_soc_codec *codec, int on) +{ + u16 valPwr; + fs16xx_dev_t *fs16xx = get_drvdata_by_context(codec); + int id = -1; + + DEBUGPRINT("%s: %s enter on=%d", __func__, fs16xx->fs16xx_dev, on); +#ifndef FS_STEREO_MODE + if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + PRINT_ERROR("%s: mono mode!", __func__); + return; + } +#endif + valPwr = snd_soc_read(codec, FS16XX_SYSCTRL_REG); + if (on) { + snd_soc_write(codec, FS16XX_PLLCTRL4_REG, FS16XX_PLLCTRL_EN_ALL); + + valPwr &= (~FS16XX_SYSCTRL_REG_PWDN_MSK); + /* Amplifier always on */ + valPwr |= FS16XX_SYSCTRL_REG_AMPE; + snd_soc_write(codec, FS16XX_SYSCTRL_REG, valPwr); + } else { + valPwr |= FS16XX_SYSCTRL_REG_PWDN_MSK; + snd_soc_write(codec, FS16XX_SYSCTRL_REG, valPwr); // Power off + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + id = FS16XX_DEV_INDEX_L; + } else if(!strncmp("right", fs16xx->fs16xx_dev, 5)) { + id = FS16XX_DEV_INDEX_R; + } else { + PRINT_ERROR("%s: invalid device name!\n", __func__); + return; + } + fs16xx_wait_dsp_off(id, 1, 50); + + /* Disable bandgap, osc, pll */ + snd_soc_write(codec, FS16XX_PLLCTRL4_REG, 0); + } + + DEBUGPRINT("%s: valPwr=0x%04x", __func__, valPwr); +} + +static void fs16xx_set_mute(struct snd_soc_codec *codec, int mute_state) +{ + u16 valPwr; + fs16xx_dev_t *fs16xx = get_drvdata_by_context(codec); + DEBUGPRINT("%s: enter mute_state = %d", __func__, mute_state); + + /* read sys ctrl register */ + valPwr = snd_soc_read(codec, FS16XX_SYSCTRL_REG); + + switch (mute_state) { + case FS_UMUTE: + /* unmute and enable amp */ + valPwr |= FS16XX_SYSCTRL_REG_AMPE; + snd_soc_write(codec, FS16XX_SYSCTRL_REG, valPwr); + break; + case FS_MUTE: + /* turn off amp */ + valPwr &= (~FS16XX_SYSCTRL_REG_AMPE); + snd_soc_write(codec, FS16XX_SYSCTRL_REG, valPwr); + break; + default: + break; + } + + DEBUGPRINT("%s: write valPwr=0x%04X %s", __func__, valPwr, fs16xx->fs16xx_dev); +} + +static int fs16xx_calibaration(fs16xx_dev_t *fs16xx, int write_otp) +{ + static int calibrationOK = 0; + Fs16xx_Error_t result = Fs16xx_Error_OK; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + if(calibrationOK){ + if(!strncmp("left", fs16xx->fs16xx_dev, 4)) { + PRINT("%s: left write_otp %d", __func__, write_otp); + result = fs16xx_calibration(FS16XX_DEV_INDEX_L, 1, 0); + } else if(!strncmp("right", fs16xx->fs16xx_dev, 5)) { + PRINT("%s: right write_otp %d", __func__, write_otp); + result = fs16xx_calibration(FS16XX_DEV_INDEX_R, 1, 0); + } + }else{ + if(!strncmp("left", fs16xx->fs16xx_dev, 4)) { + PRINT("%s: left write_otp %d", __func__, write_otp); + result = fs16xx_calibration(FS16XX_DEV_INDEX_L, 1, 1); + } else if(!strncmp("right", fs16xx->fs16xx_dev, 5)) { + PRINT("%s: right write_otp %d", __func__, write_otp); + result = fs16xx_calibration(FS16XX_DEV_INDEX_R, 1, write_otp); + } + } + if(Fs16xx_Error_OK == result) { + calibrationOK = 1; + return 0; + } else { + return -101; + } +} + +static void fs16xx_work_read_imp(struct work_struct *work) +{ + fs16xx_dev_t *fs16xx = + container_of(work, fs16xx_dev_t, calib_work); + struct snd_soc_codec *codec = fs16xx->codec; + u16 val; + Fs16xx_devId_t id; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + if(!strncmp("left", fs16xx->fs16xx_dev, 4)) { + id = FS16XX_DEV_INDEX_L; + } else if(!strncmp("right", fs16xx->fs16xx_dev, 5)) { + id = FS16XX_DEV_INDEX_R; + } else { + PRINT_ERROR("%s: invalid codec name=%s!!!", __func__, fs16xx->fs16xx_dev); + return; + } + mutex_lock(&lr_lock); + val = snd_soc_read(codec, FS16XX_STATUS_REG); + + if((val & FS16XX_STATUS_REG_PLLS) != 0) { + fs16xx_get_calibration_result(id, &fs16xx->speaker_imp); + } + mutex_unlock(&lr_lock); +} + +static void fs16xx_work_mode(struct work_struct *work) +{ + fs16xx_dev_t *fs16xx = + container_of(work, fs16xx_dev_t, + mode_work.work); + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + mutex_lock(&lr_lock); + + mutex_unlock(&lr_lock); + return; +} + +static void fs16xx_load_preset(struct work_struct *work) +{ + fs16xx_dev_t *fs16xx = + container_of(work, fs16xx_dev_t, load_preset); + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + mutex_lock(&lr_lock); + + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + fs16xx_load_presets(FS16XX_DEV_INDEX_L); + fs16xx_set_preset(FS16XX_DEV_INDEX_L, 0); + } else if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + fs16xx_load_presets(FS16XX_DEV_INDEX_R); + fs16xx_set_preset(FS16XX_DEV_INDEX_R, 0); + } + mutex_unlock(&lr_lock); +} + +/* + * ASOC OPS +*/ +static int fs16xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_dai(codec_dai); + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + fs16xx->sysclk = freq; + return 0; +} + +static int fs16xx_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_dai(codec_dai); + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* default value */ + break; + case SND_SOC_DAIFMT_CBM_CFM: + default: + /* only supports Slave mode */ + PRINT_ERROR("fs16xx: invalid DAI master/slave interface"); + return -EINVAL; + } + + return 0; +} + +static int fs16xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_dai(dai); + struct snd_soc_codec *codec = fs16xx->codec; + u16 val; + int bclk_ws_ratio; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + /* validate and set params */ + /* if (params_format(params) != SNDRV_PCM_FORMAT_S16_LE) { + PRINT_ERROR("fs16xx: invalid pcm bit lenght"); + return -EINVAL; + }*/ + PRINT("fs16xx: params_format = %x\n",params_format(params)); + + mutex_lock(&lr_lock); + switch(params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_write(codec, 0xC3, 0x04); + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S32_LE: + snd_soc_write(codec, 0xC3, 0x08); + break; + default: + PRINT_ERROR("fs16xx: invalid pcm bit lenght"); + snd_soc_write(codec, 0xC3, 0x08); + } + + val = snd_soc_read(codec, FS16XX_I2SCTRL_REG); + /* clear Sample rate bits */ + val = val & ~(FS16XX_I2SCTRL_REG_I2SSR_MSK); + + switch (params_rate(params)) { + case 8000: + val = val | FS16XX_I2SCTRL_RATE_08000; + break; + case 16000: + val = val | FS16XX_I2SCTRL_RATE_16000; + break; + case 44100: + val = val | FS16XX_I2SCTRL_RATE_44100; + break; + case 48000: + val = val | FS16XX_I2SCTRL_RATE_48000; + break; + default: + PRINT_ERROR("fs16xx: invalid sample rate = %d", params_rate(params)); + mutex_unlock(&lr_lock); + return -EINVAL; + } + + snd_soc_write(codec, FS16XX_I2SCTRL_REG, val); + mutex_unlock(&lr_lock); + + /* calc bclk to ws freq ratio, fs16xx supports only 32, 48, 64 */ + bclk_ws_ratio = fs16xx->sysclk/params_rate(params); + if (bclk_ws_ratio != 32 && bclk_ws_ratio != 48 + && bclk_ws_ratio != 64) { + PRINT_ERROR("fs16xx: invalid bit clk to ws freq ratio %d:", + bclk_ws_ratio); + return -EINVAL; + } + + return 0; +} + +static int fs16xx_mute(struct snd_soc_dai *dai, int mute) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_dai(dai); + struct snd_soc_codec *codec = fs16xx->codec; + + DEBUGPRINT("%s: enter codec name=%s mute=%d", __func__, fs16xx->fs16xx_dev, mute); +#ifndef FS_STEREO_MODE + if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + PRINT_ERROR("%s: mono mode!", __func__); + return 0; + } +#endif + mutex_lock(&lr_lock); + fs16xx_wait_clk_stable(codec); + fs16xx_wait_boost_ssend(codec); + if (mute) { + cancel_delayed_work_sync(&fs16xx->mode_work); + } + + if (mute) { + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + fs16xx_set_tempr_protection(FS16XX_DEV_INDEX_L, FS_DISABLE); + } else if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + fs16xx_set_tempr_protection(FS16XX_DEV_INDEX_R, FS_DISABLE); + } + fs16xx_set_mute(codec, FS_MUTE); + } else { + fs16xx_set_mute(codec, FS_UMUTE); + Sleep(10); + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + fs16xx_set_tempr_protection(FS16XX_DEV_INDEX_L, FS_ENABLE); + } else if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + fs16xx_set_tempr_protection(FS16XX_DEV_INDEX_R, FS_ENABLE); + } + } + mutex_unlock(&lr_lock); + return 0; +} + +static int fs16xx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_dai(dai); + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + mutex_lock(&lr_lock); + fs16xx_power(fs16xx->codec, 1); + mutex_unlock(&lr_lock); + return 0; +} + +static void fs16xx_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_dai(dai); + DEBUGPRINT("%s: enter", __func__); + mutex_lock(&lr_lock); + fs16xx_power(fs16xx->codec, 0); + mutex_unlock(&lr_lock); +} + +/* Trigger callback is atomic function, It gets called when pcm is started */ +static int fs16xx_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_dai(dai); + int ret = 0; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + fs16xx->is_pcm_triggered = 1; + /* if in bypass mode dont't do anything */ + if (fs16xx->is_spkr_prot_en) + break; + /* To initialize dsp all the I2S signals should be bought up, + * so that the DSP's internal PLL can sync up and memory becomes + * accessible. Trigger callback is called when pcm write starts, + * so this should be the place where DSP is initialized + */ + if (fs16xx->dsp_init == FS16XX_DSP_INIT_PENDING) + queue_work(fs16xx->fs16xx_wq, &fs16xx->init_work); + + /* will need to read speaker impedence here if its not read yet + * to complete the calibartion process. This step will enable + * device to calibrate if its not calibrated/validated in the + * factory. When the factory process is in place speaker imp + * will be read from sysfs and validated. + */ + else if (fs16xx->dsp_init == FS16XX_DSP_INIT_OK) { + if ((fs16xx->mode_switched == 1) || + (fs16xx->update_cfg == 1)) + queue_delayed_work(fs16xx->fs16xx_wq, &fs16xx->mode_work, + 0); + if (fs16xx->speaker_imp == 0) + queue_work(fs16xx->fs16xx_wq, + &fs16xx->calib_work); + + } + /* else nothing to do */ + break; + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + break; + case SNDRV_PCM_TRIGGER_STOP: + fs16xx->is_pcm_triggered = 0; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +/* Called from fs16xx stereo codec stub driver to set mute + * on the individual codecs, this is needed to smother pops + * in stereo configuration. +*/ + +/*int fs16xx_stereo_sync_set_mute(int mute) +{ + // TODO: sync mute + return 0; +}*/ + +/* + * SysFS support + */ + +static ssize_t fs16xx_show_spkr_imp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + fs16xx_dev_t *fs16xx = + i2c_get_clientdata(to_i2c_client(dev)); + u16 val; + Fs16xx_devId_t id; + u32 r25 = 0; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + if(!strncmp("left", fs16xx->fs16xx_dev, 4)) { + id = FS16XX_DEV_INDEX_L; + } else if(!strncmp("right", fs16xx->fs16xx_dev, 5)) { + id = FS16XX_DEV_INDEX_R; + } else { + PRINT_ERROR("%s: invalid codec name=%s!!!", __func__, fs16xx->fs16xx_dev); + id = FS16XX_DEV_INDEX_R; // Read right channel + } + + if (fs16xx->codec) { + mutex_lock(&lr_lock); + val = snd_soc_read(fs16xx->codec, FS16XX_STATUS_REG); + + if((val & FS16XX_STATUS_REG_PLLS) != 0) { + fs16xx_get_calibration_result(id, &r25); + fs16xx->speaker_imp = r25; + } + mutex_unlock(&lr_lock); + } + return scnprintf(buf, PAGE_SIZE, "%u\n", fs16xx->speaker_imp); +} + +static ssize_t fs16xx_show_dev_state_info(struct file *filp, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + fs16xx_dev_t *fs16xx = + i2c_get_clientdata(kobj_to_i2c_client(kobj)); + u16 val; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + if (off >= attr->size) + return 0; + + if (off + count > attr->size) + count = attr->size - off; + + if (fs16xx->codec) { + mutex_lock(&lr_lock); + val = snd_soc_read(fs16xx->codec, FS16XX_STATUS_REG); + mutex_unlock(&lr_lock); + if(count > 4) { + scnprintf(buf, count, "%04X", val); + } + } + return 4; +} + +static ssize_t fs16xx_show_spkr_imp_model(struct file *filp, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + fs16xx_dev_t *fs16xx = + i2c_get_clientdata(kobj_to_i2c_client(kobj)); + u16 val; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + if (off >= attr->size) + return 0; + + if (off + count > attr->size) + count = attr->size - off; + + if (fs16xx->codec) { + mutex_lock(&lr_lock); + val = snd_soc_read(fs16xx->codec, FS16XX_STATUS_REG); + mutex_unlock(&lr_lock); + } + return 0; +} + +static ssize_t fs16xx_show_spkr_exc_model(struct file *filp, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + fs16xx_dev_t *fs16xx = + i2c_get_clientdata(kobj_to_i2c_client(kobj)); + u16 val; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + if (off >= attr->size) + return 0; + + if (off + count > attr->size) + count = attr->size - off; + + if (fs16xx->codec) { + mutex_lock(&lr_lock); + val = snd_soc_read(fs16xx->codec, FS16XX_STATUS_REG); + mutex_unlock(&lr_lock); + } + return 0; +} + +static ssize_t fs16xx_show_config_preset(struct file *filp, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + fs16xx_dev_t *fs16xx = + i2c_get_clientdata(kobj_to_i2c_client(kobj)); + u16 val; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + if (off >= attr->size) + return 0; + + if (off + count > (attr->size)) + count = (attr->size) - off; + + if (fs16xx->codec) { + mutex_lock(&lr_lock); + val = snd_soc_read(fs16xx->codec, FS16XX_STATUS_REG); + mutex_unlock(&lr_lock); + } + return 0; +} + +static ssize_t fs16xx_show_ic_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + fs16xx_dev_t *fs16xx = i2c_get_clientdata(to_i2c_client(dev)); + u16 val; + int temp = 0; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + if (fs16xx->codec) { + mutex_lock(&lr_lock); + val = snd_soc_read(fs16xx->codec, FS16XX_STATUS_REG); + mutex_unlock(&lr_lock); + } + return scnprintf(buf, PAGE_SIZE, "%u\n", temp); +} + +static ssize_t fs16xx_show_calib(struct device *dev, + struct device_attribute *attr, char *buf) +{ + fs16xx_dev_t *fs16xx = i2c_get_clientdata(to_i2c_client(dev)); + int val = 1; + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + mutex_lock(&lr_lock); + val = fs16xx_calibaration(fs16xx, val); + mutex_unlock(&lr_lock); + + return scnprintf(buf, PAGE_SIZE, "%d\n", val); +} +static ssize_t fs16xx_show_calib_result(struct device *dev, + struct device_attribute *attr, char *buf) +{ + fs16xx_dev_t *fs16xx = i2c_get_clientdata(to_i2c_client(dev)); + int val = 0; + int id = 0; + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + id = FS16XX_DEV_INDEX_L; + } + if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + id = FS16XX_DEV_INDEX_R; + } + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + mutex_lock(&lr_lock); + fs16xx_get_calibration_result(id,&val); + mutex_unlock(&lr_lock); + + return scnprintf(buf, PAGE_SIZE, "%u\n", val); +} + +// Read reg interface for debugging +static ssize_t fs16xx_reg_read_bin_attr(struct file *filp, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + fs16xx_dev_t *fs16xx = i2c_get_clientdata(kobj_to_i2c_client(kobj)); + struct snd_soc_codec *codec = fs16xx->codec; + int offset = 0; + u16 regVal = 0; + + if(off + count > attr->size) { + count = attr->size - off; + //PRINT_ERROR("trim count to %d", count); + } + + if (fs16xx->codec) { + if(count >= FS16XX_MAX_REGISTER * 2) { + for(offset = 0; offset <= FS16XX_MAX_REGISTER; offset++) { + mutex_lock(&lr_lock); + regVal = snd_soc_read(codec, offset); + mutex_unlock(&lr_lock); + buf[offset * 2 ] = (u8)((regVal >> 8) & 0xFF); + buf[offset * 2 + 1] = (u8)((regVal) & 0xFF); + } + } + } + return count; +} + +// Write reg interface for debugging +/*************************************** +* reg write command: address value +* address should be in hex mode without 0x or \x +* value should be in hex mode without 0x or \x +****************************************/ +static ssize_t fs16xx_reg_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + fs16xx_dev_t *fs16xx = i2c_get_clientdata(client); + struct snd_soc_codec *codec = fs16xx->codec; + int reg=0, nVal=0; + u16 val=0; + + if (fs16xx->codec) { + DEBUGPRINT("%s: enter force right 0x35", __func__); + //DEBUGPRINT("%s: count=%d buf=%s stereo mode=%d", __func__, count, buf, stereo_mode); + if (count >= 3 && count <= 8) { + if(sscanf(buf, "%x %x", ®, &nVal) == 2) { + DEBUGPRINT("%s reg=0x%x val=0x%x", __func__, reg, nVal); + val = (u16)nVal; + // write single reg + mutex_lock(&lr_lock); + snd_soc_write(codec, reg, val); + mutex_unlock(&lr_lock); + } else { + PRINT_ERROR("%s parsing failed", __func__ ); + } + } + } + + return count; +} + +static const struct bin_attribute fs16xx_raw_bin_attr[] = { +{ + .attr = {.name = "device_state", .mode = S_IRUGO}, + .read = fs16xx_show_dev_state_info, + .size = FS16XX_DEV_STATE_SIZE, +}, +{ + .attr = {.name = "spkr_imp_model", .mode = S_IRUGO}, + .read = fs16xx_show_spkr_imp_model, + .size = FS16XX_SPK_FW_SIZE - 1, +}, +{ + .attr = {.name = "config_preset", .mode = S_IRUGO}, + .read = fs16xx_show_config_preset, + .size = FS16XX_CFG_FW_SIZE + FS16XX_PST_FW_SIZE - 2, +}, +{ + .attr = {.name = "spkr_exc_model", .mode = S_IRUGO}, + .read = fs16xx_show_spkr_exc_model, + .size = FS16XX_SPK_EX_FW_SIZE - 1, +}, +{ + .attr = {.name = "fs_reg_read", .mode = S_IRUGO}, + .read = fs16xx_reg_read_bin_attr, + .size = FS16XX_MAX_REGISTER * sizeof(u16), +} +}; + +static DEVICE_ATTR(ic_temp, S_IRUGO, + fs16xx_show_ic_temp, NULL); + +static DEVICE_ATTR(spkr_imp, S_IRUGO, + fs16xx_show_spkr_imp, NULL); + + +static DEVICE_ATTR(force_calib, S_IRUGO, + fs16xx_show_calib, NULL); +static DEVICE_ATTR(calib_result, S_IRUGO, + fs16xx_show_calib_result, NULL); + +static DEVICE_ATTR(fs_reg_write, S_IWUSR, + NULL, fs16xx_reg_write); + +static struct attribute *fs16xx_attributes[] = { + &dev_attr_spkr_imp.attr, + &dev_attr_force_calib.attr, + &dev_attr_calib_result.attr, + &dev_attr_ic_temp.attr, + &dev_attr_fs_reg_write.attr, + NULL +}; + +static const struct attribute_group fs16xx_attr_group = { + .attrs = fs16xx_attributes, +}; + + +static const struct snd_soc_dai_ops fs16xx_ops = { + .hw_params = fs16xx_hw_params, + .digital_mute = fs16xx_mute, + .set_fmt = fs16xx_set_dai_fmt, + .set_sysclk = fs16xx_set_dai_sysclk, + .startup = fs16xx_startup, + .shutdown = fs16xx_shutdown, + .trigger = fs16xx_trigger, +}; + +static struct snd_soc_dai_driver fs16xx_left_dai = { + .name = "fs16xx_codec_left", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = FS16XX_RATES, + .formats = FS16XX_FORMATS,}, + .ops = &fs16xx_ops, + .symmetric_rates = 1, +}; + +static struct snd_soc_dai_driver fs16xx_right_dai = { + .name = "fs16xx_codec_right", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = FS16XX_RATES, + .formats = FS16XX_FORMATS,}, + .ops = &fs16xx_ops, + .symmetric_rates = 1, +}; + +static int fs16xx_probe(struct snd_soc_codec *codec) +{ + fs16xx_dev_t *fs16xx = get_drvdata_by_context(codec); + int i; +#ifdef FS_STEREO_MODE + u16 val; +#endif + + DEBUGPRINT("%s: enter codec name=%s", __func__, fs16xx->fs16xx_dev); + + /* set codec Bulk write method, will be used for + * loading DSP firmware and config files. + */ +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 108)) + codec->bulk_write_raw = fs16xx_bulk_write; +#endif + fs16xx->codec = codec; + + /* reset registers to Default values */ + mutex_lock(&lr_lock); + snd_soc_write(codec, FS16XX_SYSCTRL_REG, 0x0002); + snd_soc_write(codec, FS16XX_SYSCTRL_REG, 0x0001); + Sleep(10); + + printk(KERN_INFO "%s enter.\n", __func__); + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + printk(KERN_INFO "%s in left\n", __func__); + snd_soc_write(codec, 0xC4, 0x000A); + /* initialize I2C registers */ + for (i = 0; i < ARRAY_SIZE(Fs16xx_reg_defaults); i++) { + snd_soc_write(codec, Fs16xx_reg_defaults[i].reg, + Fs16xx_reg_defaults[i].value); + } + + fs16xx_UpdateOTCTRL(codec); + } else if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + printk(KERN_INFO "%s in right\n", __func__); + snd_soc_write(codec, 0xC4, 0x000A); + usleep_range(5000, 5001); + for (i = 0; i < ARRAY_SIZE(Fs16xx_reg_defaults); i++) { + snd_soc_write(codec, Fs16xx_reg_defaults[i].reg, + Fs16xx_reg_defaults[i].value); + } + + fs16xx_UpdateOTCTRL(codec); + } +/* STEREO_MODE left device just select left channel, + right device just select right channel; */ +#ifdef FS_STEREO_MODE + printk("%s, stereo mode.\n",__func__); + val = snd_soc_read(codec, FS16XX_I2SCTRL_REG); + if(!strncmp("left", fs16xx->fs16xx_dev, 4)) { + val &= ~(0x3 << FS16XX_I2SCTRL_REG_CHS12_POS); + val |= (0x1 << FS16XX_I2SCTRL_REG_CHS12_POS); + } + if(!strncmp("right", fs16xx->fs16xx_dev, 5)) { + val &= ~(0x3 << FS16XX_I2SCTRL_REG_CHS12_POS); + val |= (0x2 << FS16XX_I2SCTRL_REG_CHS12_POS); + } + snd_soc_write(codec, FS16XX_I2SCTRL_REG, val); +#endif + mutex_unlock(&lr_lock); + + /* add controls and widgets */ + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + snd_soc_add_codec_controls(codec, fs16xx_left_snd_controls, + ARRAY_SIZE(fs16xx_left_snd_controls)); + /*snd_soc_dapm_new_controls(&codec->dapm, + fs16xx_left_dapm_widgets, + ARRAY_SIZE(fs16xx_left_dapm_widgets)); + + snd_soc_dapm_add_routes(&codec->dapm, fs16xx_left_dapm_routes, + ARRAY_SIZE(fs16xx_left_dapm_routes));*/ + } else if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + snd_soc_add_codec_controls(codec, fs16xx_right_snd_controls, + ARRAY_SIZE(fs16xx_right_snd_controls)); + /*snd_soc_dapm_new_controls(&codec->dapm, + fs16xx_right_dapm_widgets, + ARRAY_SIZE(fs16xx_right_dapm_widgets)); + snd_soc_dapm_add_routes(&codec->dapm, fs16xx_right_dapm_routes, + ARRAY_SIZE(fs16xx_right_dapm_routes));*/ + } +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 10, 108)) + snd_soc_dapm_new_widgets(&codec->dapm); + snd_soc_dapm_sync(&codec->dapm); +#elif (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 18, 72)) + snd_soc_dapm_new_widgets(codec->component.card); + snd_soc_dapm_sync(&codec->dapm); +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)) + snd_soc_dapm_new_widgets(codec->component.card); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); +#else + snd_soc_dapm_new_widgets(codec->card); + snd_soc_dapm_sync(snd_soc_component_get_dapm(codec)); +#endif + + /* load preset tables */ + queue_work(fs16xx->fs16xx_wq, &fs16xx->load_preset); + printk("fs16xx codec probe successfully. \n"); + return 0; +} + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)) +static int fs16xx_remove(struct snd_soc_codec *codec) +{ + pr_debug("%s: enter\n", __func__); + mutex_lock(&lr_lock); + fs16xx_power(codec, 0); + mutex_unlock(&lr_lock); + return 0; +} +#else +static void fs16xx_remove(struct snd_soc_codec *codec) +{ + pr_debug("%s: enter\n", __func__); + mutex_lock(&lr_lock); + fs16xx_power(codec, 0); + mutex_unlock(&lr_lock); +} +#endif + +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 14, 72)) +/* table used by ALSA core while creating codec register + * access debug fs. + */ +static u8 fs16xx_reg_readable[FS16XX_REG_CACHE_SIZE] = { +[FS16XX_STATUS_REG] = 1, +[FS16XX_BATS_REG] = 1, +[FS16XX_TEMPS_REG] = 1, +[FS16XX_ID_REG] = 1, +[FS16XX_I2SCTRL_REG] = 1, +[FS16XX_AUDIOCTRL_REG] = 1, +[FS16XX_TEMPSEL_REG] = 1, +[FS16XX_SYSCTRL_REG] = 1, +[FS16XX_SPKSTAUTS_REG] = 1, +[FS16XX_CHIPINI_REG] = 1, +[FS16XX_I2CADD_REG] = 1, +[FS16XX_BSTCTRL_REG] = 1 +}; + +static int fs16xx_readable(struct snd_soc_codec *codec, unsigned int reg) +{ + return fs16xx_reg_readable[reg]; +} +#endif + +static struct snd_soc_codec_driver soc_codec_dev_fs16xx = { + .read = fs16xx_read, + .write = fs16xx_write, + .probe = fs16xx_probe, + .remove = fs16xx_remove, +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 14, 72)) + .readable_register = fs16xx_readable, +#endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)) + .reg_cache_size = FS16XX_REG_CACHE_SIZE, + .reg_word_size = 2, +#endif +}; +#ifdef CONFIG_OF +static struct fs16xx_pdata * +fs16xx_of_init(struct i2c_client *client) +{ + struct fs16xx_pdata *pdata; + struct device_node *np = client->dev.of_node; + + DEBUGPRINT("%s: enter i2c addr=0x%x", __func__, client->addr); + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + PRINT_ERROR("%s : pdata allocation failure.", __func__); + return NULL; + } + if (of_property_read_string(np, "fs,fs_bin_path", + &pdata->fw_path)) + pdata->fw_path = "."; + + if (of_property_read_string(np, "fs,fs-dev", &pdata->fs_dev)) + pdata->fs_dev = "left"; + + if (of_property_read_string(np, "fs,fs-firmware-part-name", + &pdata->fw_name)) + pdata->fw_name = "fs16xx"; + + return pdata; +} +#else +static inline struct fs16xx_pdata * +fs16xx_of_init(struct i2c_client *client) +{ + return NULL; +} +#endif + +static int fs16xx_detect_device(Fs16xx_devId_t id) +{ + uint16_t dev_id; + int ret; + + ret = fs16xx_read_register16(id, 0x03, &dev_id); + if (ret) { + return ret; + } + switch ((dev_id >> 8) & 0xff) { + case 0x06: // FS1818 + case 0x0B: // FS1896 + PRINT("%s: FS1818/FS1896 detected", __func__); + break; + default: + PRINT_ERROR("%s: invalid device_id: %X", __func__, dev_id); + return -EINVAL; + break; + } + + return 0; +} + +static int fs16xx_set_codec_info(struct device *dev, struct snd_soc_dai_driver *dai) +{ + char name[32]; + char tmp[32]; + int id1, id2; + + if (dev_name(dev) == NULL) + return -EINVAL; + + strlcpy(name, dev_name(dev), 32); + if (sscanf(name, "%x-%x", &id1, &id2) != 2) + return -EINVAL; + + /* sanitize component name for DAI link creation */ + snprintf(tmp, 32, "%s.%s", dev->driver->name, name); + strlcpy(name, tmp, 32); + printk("Codec: %s", name); + printk("Codec DAI: %s", dai->name); + + return 0; +} + +static int fs16xx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + fs16xx_dev_t *fs16xx; + struct fs16xx_pdata *pdata; + int ret; + int i; + + PRINT("%s: enter i2c addr=0x%x", __func__, i2c->addr); + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed."); + return -EIO; + } + + if (i2c->dev.of_node) + pdata = fs16xx_of_init(i2c); + else + pdata = i2c->dev.platform_data; + + /* check platform data */ + if (pdata == NULL) { + dev_err(&i2c->dev, "platform data is NULL."); + return -EINVAL; + } + + fs16xx = devm_kzalloc(&i2c->dev, sizeof(fs16xx_dev_t), GFP_KERNEL); + if (fs16xx == NULL) { + dev_err(&i2c->dev, "alloc memory fail"); + return -ENOMEM; + } + + fs16xx->control_data = i2c; + fs16xx->dsp_init = 1; + fs16xx->fs16xx_dev = pdata->fs_dev; + fs16xx->sysclk = SYS_CLK_DEFAULT; + fs16xx->update_eq = 1; + + // Initialize dev_state + fs16xx->dev_state.cur_preset = -1; + fs16xx->dev_state.calib_state = Fs16xx_Calib_Unknown; + fs16xx->dev_state.dev_preset = NULL; + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + fs16xx->id = FS16XX_DEV_INDEX_L; + #ifdef USE_RAS_MODE + fs16xx->dev_state.ras= true; + #else + fs16xx->dev_state.ras = false; + #endif + } else if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + fs16xx->id = FS16XX_DEV_INDEX_R; + } else { + PRINT_ERROR("%s: invalid dev:%s", __func__, fs16xx->fs16xx_dev); + return -EINVAL; + } + g_fs16xx_priv[fs16xx->id] = fs16xx; + + i2c_set_clientdata(i2c, fs16xx); + + mutex_init(&fs16xx->i2c_rw_lock); + + // check device id + ret = fs16xx_detect_device(fs16xx->id); + if (ret) { + PRINT_ERROR("%s: detect device[%X] failed", __func__, i2c->addr); + g_fs16xx_priv[fs16xx->id] = NULL; + return ret; + } + /* register sysfs hooks */ + ret = sysfs_create_group(&i2c->dev.kobj, &fs16xx_attr_group); + if (!ret) { + for (i = 0; i < ARRAY_SIZE(fs16xx_raw_bin_attr); i++) { + ret = sysfs_create_bin_file(&i2c->dev.kobj, + &fs16xx_raw_bin_attr[i]); + if (ret) + PRINT_ERROR("%s: Error creating fs16xx sysfs bin attr", __func__); + } + } + else { + PRINT_ERROR("%s: Error registering fs16xx sysfs", __func__); + } + + /* setup work queue, will be used to initial DSP on first boot up */ + fs16xx->fs16xx_wq = create_singlethread_workqueue("fs16xx"); + if (fs16xx->fs16xx_wq == NULL) { + ret = -ENOMEM; + goto wq_fail; + } + + INIT_WORK(&fs16xx->calib_work, fs16xx_work_read_imp); + INIT_DELAYED_WORK(&fs16xx->mode_work, fs16xx_work_mode); + INIT_WORK(&fs16xx->load_preset, fs16xx_load_preset); +#if 0 + fs16xx->vdd = regulator_get(&i2c->dev, "fs_vdd"); + if (IS_ERR(fs16xx->vdd)) { + PRINT_ERROR("%s: Error getting vdd regulator.", __func__); + ret = PTR_ERR(fs16xx->vdd); + goto codec_fail; + } + + regulator_set_voltage(fs16xx->vdd, 1800000, 1800000); + ret = regulator_enable(fs16xx->vdd); + if (ret < 0) { + PRINT_ERROR("%s: Error enabling vdd regulator %d:", + __func__, ret); + goto reg_enable_fail; + } +#endif + if (!strncmp("left", fs16xx->fs16xx_dev, 4)) { + /* register codec */ + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_fs16xx, + &fs16xx_left_dai, 1); + if (ret < 0) { + PRINT_ERROR("%s: Error registering fs16xx left codec", + __func__); + goto codec_fail; + } + fs16xx_set_codec_info(&i2c->dev,&fs16xx_left_dai); + } else if (!strncmp("right", fs16xx->fs16xx_dev, 5)) { + /* register codec */ + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_fs16xx, + &fs16xx_right_dai, 1); + if (ret < 0) { + PRINT_ERROR("%s: Error registering fs16xx right codec", + __func__); + goto codec_fail; + } + fs16xx_set_codec_info(&i2c->dev,&fs16xx_right_dai); + stereo_mode = 1; + } + + PRINT("fs16xx %s probed successfully!", fs16xx->fs16xx_dev); + + return ret; + +codec_fail: +#if 0 + regulator_disable(fs16xx->vdd); +reg_enable_fail: + regulator_put(fs16xx->vdd); +#endif +wq_fail: + return ret; +} + +static int fs16xx_i2c_remove(struct i2c_client *client) +{ + fs16xx_dev_t *fs16xx = i2c_get_clientdata(client); + + DEBUGPRINT("%s: enter", __func__); + snd_soc_unregister_codec(&client->dev); +#if 0 + regulator_disable(fs16xx->vdd); + regulator_put(fs16xx->vdd); +#endif + destroy_workqueue(fs16xx->fs16xx_wq); + return 0; +} + +static const struct i2c_device_id fs16xx_i2c_id[] = { + { "fs16xx", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, fs16xx_i2c_id); + +#ifdef CONFIG_OF +static struct of_device_id fs16xx_match_tbl[] = { + { .compatible = "foursemi,fs16xx" }, + { }, +}; +MODULE_DEVICE_TABLE(of, fs16xx_match_tbl); +#endif + +static struct i2c_driver fs16xx_i2c_driver = { + .driver = { + .name = "fs16xx", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(fs16xx_match_tbl), + }, + .probe = fs16xx_i2c_probe, + .remove = fs16xx_i2c_remove, + .id_table = fs16xx_i2c_id, +}; + + + +static int __init fs16xx_modinit(void) +{ + int ret; + DEBUGPRINT("%s: enter.", __func__); + PRINT("fs16xx branch: %s\n", FS16XX_GIT_BRANCH); + PRINT("fs16xx commit: %s\n", FS16XX_GIT_COMMIT); + PRINT("fs16xx date: %s\n", FS16XX_CODE_DATE); + PRINT("fs16xx version: %s\n", FS16XX_CODE_VERSION); + ret = i2c_add_driver(&fs16xx_i2c_driver); + if (ret != 0) { + PRINT_ERROR("Failed to register fs16xx I2C driver:%d", ret); + } + return ret; +} +module_init(fs16xx_modinit); + +static void __exit fs16xx_exit(void) +{ + i2c_del_driver(&fs16xx_i2c_driver); +} +module_exit(fs16xx_exit); + +MODULE_DESCRIPTION("ASoC fs16xx codec driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("FourSemi SW"); diff --git a/asoc/codecs/foursemi/fs16xx.h b/asoc/codecs/foursemi/fs16xx.h new file mode 100644 index 000000000000..844722f7e552 --- /dev/null +++ b/asoc/codecs/foursemi/fs16xx.h @@ -0,0 +1,190 @@ +/* + * Fs16xx device driver header file. + * Copyright (C) 2017 FourSemi Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef __FS16XX_DRIVER_H__ +#define __FS16XX_DRIVER_H__ + +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif +#include "fs16xx_define.h" +#include "fs16xx_preset.h" +#include "fs16xx_calibration.h" + +// Macro for kernel mode implementation +#define FS16XX_KERNEL_IMPL + +#define MAX_DEV_HANDLES 4 + + +struct Fs16xx_device_handle { + int in_use; + unsigned char device_address; + unsigned char device_id; + unsigned char revision; + void *fs16xx; /* Pointer to fs16xx_dev_t */ +}; + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0])) +#endif + +struct fs16xx_regs { + int reg; + int value; +}; + +/* I2S_CONTROL bits for sample rates */ +#define FS16XX_I2SCTRL_RATE_08000 (0<= KERNEL_VERSION(4, 19, 0)) +#define snd_soc_codec snd_soc_component +#define snd_soc_add_codec_controls snd_soc_add_component_controls +#define snd_soc_register_codec devm_snd_soc_register_component +#define snd_soc_unregister_codec snd_soc_unregister_component +#define snd_soc_codec_driver snd_soc_component_driver +#endif + +struct _fs_dev_state { + int cur_preset; + bool ras; + unsigned short tmax; + unsigned short tempr_coef; + unsigned short tempr_sel; + unsigned short spk_set; + unsigned short adp_bst; + unsigned short bst_ctrl; + unsigned short ts_ctrl; + unsigned short acs_drc_ctrl[ACS_DRC_CTRL_LEN]; + Fs16xx_preset *dev_preset; + Fs16xx_Calib_State_t calib_state; + Fs16xx_Calib_Result_t calib_result; +}; +typedef struct _fs_dev_state Fs_Dev_State; + +struct fs16xx_priv { +#ifdef __KERNEL__ + struct i2c_client *control_data; + struct regulator *vdd; + struct snd_soc_codec *codec; + struct workqueue_struct *fs16xx_wq; + struct work_struct init_work; + struct work_struct calib_work; + struct delayed_work mode_work; + struct work_struct load_preset; + struct delayed_work delay_work; + struct mutex i2c_rw_lock; +#elif defined(__ANDROID__) + int fd; /* file handle of opening i2c node. */ + struct i2c_client *client[2]; /* i2c client for i2c operation in kernel. */ +#endif + int dsp_init; + int speaker_imp; + int sysclk; + int mode; + int mode_switched; + int curr_mode; + int cfg_preset; + int ic_version; + char const *fs16xx_dev; + char const *fw_path; + char const *fw_name; + int is_spkr_prot_en; + int update_eq; + int update_cfg; + int is_pcm_triggered; + Fs_Dev_State dev_state; + Fs16xx_devId_t id; +}; +typedef struct fs16xx_priv fs16xx_dev_t; + +struct fs16xx_pdata { + int reset_gpio; + int max_vol_steps; + const char *fs_dev; + const char *fw_path; + const char *fw_name; +}; + +/* DSP init status */ +#define FS16XX_DSP_INIT_PENDING 0 +#define FS16XX_DSP_INIT_OK 1 +#define FS16XX_DSP_INIT_FAIL -1 + +/* FsDbg print for Linux kernel */ +#define PRINT(msg,va...) pr_info(msg "\n", ##va) //pr_info +#define PRINT_ERROR(msg,va...) pr_err(msg "\n", ##va) +#define DEBUGPRINT(msg,va...) pr_debug(msg "\n", ##va) //pr_debug + +Fs16xx_Error_t fs16xx_write_register16(Fs16xx_devId_t id, + unsigned char subaddress, + unsigned short value); +Fs16xx_Error_t fs16xx_read_register16(Fs16xx_devId_t id, + unsigned char subaddress, + unsigned short *pValue); + +Fs16xx_Error_t fs16xx_handle_is_open(Fs16xx_devId_t id); + +Fs_Dev_State* fs16xx_get_dev_state_by_id(Fs16xx_devId_t id); + +struct snd_soc_codec * fs16xx_get_codec_by_id(Fs16xx_devId_t id); + +Fs16xx_Error_t fs16xx_wait_boost_ssend(struct snd_soc_codec *codec); + +Fs16xx_Error_t fs16xx_wait_dsp_off(Fs16xx_devId_t id, int wait_for_adc, int max_try); + +#endif diff --git a/asoc/codecs/foursemi/fs16xx_calibration.c b/asoc/codecs/foursemi/fs16xx_calibration.c new file mode 100644 index 000000000000..be694fd4fe4c --- /dev/null +++ b/asoc/codecs/foursemi/fs16xx_calibration.c @@ -0,0 +1,734 @@ +#include "fs16xx.h" +#include "fs16xx_regs.h" +#include "fs16xx_calibration.h" + +static int calib_r0 = R0_DEFAULT; +static int calib_allow_low = R0_ALLOWANCE_LOW; +static int calib_allow_high = R0_ALLOWANCE_HIGH; +static int calib_r0_step = FS16XX_CALIB_OTP_R25_STEP; +static int calib_r0_delta = OTP_MIN_DELTA; + + +//#ifdef USE_RAS_MODE +static int calib_r0_ras = R0_DEFAULT_RAS; +static int calib_allow_low_ras = R0_ALLOWANCE_LOW_RAS; +static int calib_allow_high_ras = R0_ALLOWANCE_HIGH_RAS; +static int calib_r0_step_ras = FS16XX_CALIB_OTP_R25_STEP_RAS; +//#endif + +Fs16xx_Error_t fs16xx_calibration(Fs16xx_devId_t id, int force, int storeResult) { + int count = 0, maxTryCount = 100; + unsigned short val, preVal = 0, minVal = 0; + unsigned int done = 0; + const int zmReadCount = 10; + Fs_Dev_State *dev_state = NULL; + + PRINT("%s enter. id=%d force=%d storeResult=%d", __func__, id, force, storeResult); + + dev_state = fs16xx_get_dev_state_by_id(id); + if(!dev_state) { + PRINT_ERROR("%s invalid id, exit!", __func__); + return Fs16xx_Error_StateInvalid; + } + + if(force || dev_state->calib_state == Fs16xx_Calib_NotStarted + || dev_state->calib_state == Fs16xx_Calib_Failed + || dev_state->calib_state == Fs16xx_Calib_OTP_Failed) { + + fs16xx_write_register16(id, FS16XX_OTPACC_REG, FS16XX_OTP_ACC_KEY2); + + // Start calibration + fs16xx_write_register16(id, FS16XX_ZMCONFIG_REG, 0x0010); + + fs16xx_write_register16(id, 0xAF, CALIB_REG_CALIB_MODE_AF); + fs16xx_write_register16(id, FS16XX_AUDIOCTRL_REG, 0x8F00); + fs16xx_write_register16(id, 0xC9, 0x0000); + fs16xx_write_register16(id, 0xCA, 0x0000); + fs16xx_write_register16(id, 0xCB, 0x0000); + fs16xx_write_register16(id, 0xCC, 0x0000); + fs16xx_write_register16(id, 0xB9, 0xFFFF); + fs16xx_write_register16(id, 0xBA, CALIB_REG_NORMAL_BA); + + if(!force && dev_state->calib_state == Fs16xx_Calib_NotStarted) { + // Sleep during the first time calibration + Sleep(2500); + } + + (dev_state->calib_result).calib_count++; + dev_state->calib_state = Fs16xx_Calib_OnGoing; + + while((!done) && (maxTryCount > 0)) { + Sleep(100); + maxTryCount--; + if(Fs16xx_Error_OK == fs16xx_read_register16(id, FS16XX_ZMDATA_REG, &val)) { + if(val != 0 && val != 0xFFFF) { + if (preVal == 0 || abs(preVal - val) > SPK_CALIBRATION_MAX_ZMDELTA) { + preVal = val; + count = 1; // Reset counter + minVal = val; + } else { + count++; + if(val < minVal) { + minVal = val; + } + } + } + } + if(count >= zmReadCount) { + done = 1; + } + } + + if(done && minVal != 0) { + dev_state->calib_state = fs16xx_calc_coef_from_zm(id, minVal, storeResult); + if(Fs16xx_Calib_OK == dev_state->calib_state) { + fs16xx_write_register16(id, FS16XX_AUDIOCTRL_REG, 0xFF00); + // Calibration OK, exit calibration + fs16xx_write_register16(id, FS16XX_ZMCONFIG_REG, 0x0000); + + fs16xx_write_register16(id, 0xAF, CALIB_REG_NORMAL_AF); + } + + if(Fs16xx_Calib_OTP_ExcceedMaxTry == dev_state->calib_state) { + // Calibration was done. However, OTP write failed due to counter limitation. + // In this case, we will NOT exit calibration mode in order to report calibration failure. + } + } else { + // Calibration failed + dev_state->calib_state = Fs16xx_Calib_Failed; + PRINT_ERROR("%s calibration failed. done=%d minVal=0x%04X", __func__, done, minVal); + } + } else { + fs16xx_write_register16(id, FS16XX_OTPACC_REG, 0); + return Fs16xx_Error_Calib_Error; + } + + PRINT("%s calibration end, result = %s.", __func__, dev_state->calib_state == Fs16xx_Calib_OK ? "OK" : "Failed"); + + fs16xx_write_register16(id, FS16XX_OTPACC_REG, 0); + return Fs16xx_Error_OK; +} + +Fs16xx_Calib_State_t fs16xx_check_calibration(Fs16xx_devId_t id) { +#ifndef FS16XX_CALIB_STORE_TO_OTP + FILE *fp; +#endif + int count; + Fs16xx_Error_t err = Fs16xx_Error_OK; + unsigned short value = 0, valueC4 = 0; + unsigned int r25; + Fs_Dev_State *dev_state = NULL; + Fs16xx_Calib_Result_t *calib_result; + + DEBUGPRINT("%s enter. id=%d", __func__, id); + + dev_state = fs16xx_get_dev_state_by_id(id); + if(!dev_state) { + PRINT_ERROR("%s invalid id, exit!", __func__); + //return Fs16xx_Error_StateInvalid; + return Fs16xx_Calib_Unknown; + } + calib_result = &(dev_state->calib_result); + + PRINT("%s enter id=%d, calib_state=%d", __func__, id, dev_state->calib_state); + + if(Fs16xx_Calib_Unknown != dev_state->calib_state) { + return dev_state->calib_state; + } + + // Apply values from preset + fs16xx_spkset_to_calib_coef(dev_state->ras,dev_state->spk_set); + + calib_result->calib_count = 0; + + err |= fs16xx_read_register16(id, FS16XX_PLLCTRL4_REG, &valueC4); + err |= fs16xx_write_register16(id, FS16XX_PLLCTRL4_REG, 0x000F); + + err |= fs16xx_write_register16(id, FS16XX_OTPACC_REG, FS16XX_OTP_ACC_KEY2); + +#ifdef FS16XX_CALIB_STORE_TO_OTP + // Read from 0xE8 first + err |= fs16xx_read_register16(id, 0xE8, &value); + if(Fs16xx_Error_OK != err) { + PRINT_ERROR("%s failed to read calibration result by otp cmd.", __func__); + } + + if(value == OTP_INIT_VALUE) { + count = 0; + } else { + count = fs16xx_get_otp_write_count(value & 0xFF); + } + PRINT("%s read calibration result = 0x%04x count=%d.", __func__, value, count); + // Calibration Count + if(count > 0) { + PRINT("%s calibrated already done.", __func__); + calib_result->calib_count = count; + } else { + // Need calibration + dev_state->calib_state = Fs16xx_Calib_NotStarted; + PRINT("%s calibrated not started.", __func__); + goto calib_exit; + } + + r25 = fs16xx_byte_to_r25(dev_state->ras,(unsigned char)((value >> 8) & 0xFF)); + PRINT("%s read calibration result from otp r25=%d.", __func__, r25); + dev_state->calib_state = fs16xx_update_coef_from_r25(id, r25); + + // ADC EN and TS EN should be set when osc is enabled. + fs16xx_write_register16(id, FS16XX_ADCTIME_REG, CALIB_REG_NORMAL_BA); + fs16xx_write_register16(id, FS16XX_TSCTRL_REG, CALIB_REG_NORMAL_AF); +#else + fp = fopen( FS16XX_PARAM_DIR FS16XX_CALIB_RESULT_FILE_NAME, "rb"); + if (!fp) + { + PRINT_ERROR("%s read calibration result failed.", __func__); + dev_state->calib_state = Fs16xx_Calib_NotStarted; + goto calib_exit; + } + + count = fread( (void*)calib_result, 1, sizeof(Fs16xx_Calib_Result_t), fp ); + fclose(fp); + + PRINT("%s read calibration file, count=%d.", __func__, count); + + if(count != sizeof(Fs16xx_Calib_Result_t)) { + // Calibration result file corrupted. + PRINT_ERROR("%s read calibration result corrupted.", __func__); + dev_state->calib_state = Fs16xx_Calib_NotStarted; + goto calib_exit; + } + + fs16xx_calc_coef_from_zm(id, calib_result->calib_result_zm, 0); +#endif + +calib_exit: + err |= fs16xx_write_register16(id, FS16XX_OTPACC_REG, 0); + err |= fs16xx_write_register16(id, FS16XX_PLLCTRL4_REG, valueC4); + + DEBUGPRINT("%s Exit.", __func__); + + return dev_state->calib_state; +} + +Fs16xx_Error_t fs16xx_get_calibration_result(Fs16xx_devId_t id, int *r25) { + Fs_Dev_State *dev_state = NULL; + + dev_state = fs16xx_get_dev_state_by_id(id); + if(!dev_state) { + PRINT_ERROR("%s invalid id, exit!", __func__); + return Fs16xx_Error_StateInvalid; + } + + if(dev_state->calib_state != Fs16xx_Calib_OK) { + PRINT_ERROR("%s calibration invalid result: %d", __func__, dev_state->calib_state); + return Fs16xx_Error_Calib_Error; + } + + *r25 = (int)(dev_state->calib_result.r25); + PRINT("%s r25=%d.", __func__, dev_state->calib_result.r25); + return Fs16xx_Error_OK; +} + +int fs16xx_otp_is_busy(Fs16xx_devId_t id) { + int retry = 20; + unsigned short value = 0; + + Sleep(5); + while(retry > 0) { + // Checking OTP busy bit + fs16xx_read_register16(id, FS16XX_OTPCMD_REG, &value); + if((value & FS16XX_OTPCMD_REG_BUSY_MSK) == 0) { + return 0; + } + Sleep(1); + retry--; + } + // Return non-0, otp op timeout... + return -1; +} + +int fs16xx_get_otp_write_count(unsigned short val) { + // OTP offset start from 0x10 to 0x20(Totally 16) + return (val >= OTP_OP_COUNTER_OFFSET) ? (val - OTP_OP_COUNTER_OFFSET + 1): 0; +} + +unsigned char fs16xx_r25_to_byte(bool ras,int r25) { + unsigned char val; + if(ras){ + val = (unsigned char)((int)abs(r25 - calib_r0_ras) * FS16XX_CALIB_STEP_MAGNIF_FACTOR / calib_r0_step_ras); + if (val > 0x7F) val = 0x7F; + val = (r25 >= calib_r0_ras) ? val : (unsigned char)(val | 0x80); + } else { + val = (unsigned char)((int)abs(r25 - calib_r0) * FS16XX_CALIB_STEP_MAGNIF_FACTOR / calib_r0_step); + if (val > 0x7F) val = 0x7F; + val = (r25 >= calib_r0) ? val : (unsigned char)(val | 0x80); + } + + // For Fs1801/Fs1818 otp value cannot be 0xFF(0xFF is reserved value). + if(0xFF == val) val -= 1; + return val; +} + +int fs16xx_byte_to_r25(bool ras,unsigned char b) { + if(ras){ + if((b & 0x80) != 0) { + // minus value + return calib_r0_ras - calib_r0_step_ras * (b & 0x7F) / FS16XX_CALIB_STEP_MAGNIF_FACTOR; + } + return calib_r0_ras + calib_r0_step_ras * b / FS16XX_CALIB_STEP_MAGNIF_FACTOR; + } else { + if((b & 0x80) != 0) { + // minus value + return calib_r0 - calib_r0_step * (b & 0x7F) / FS16XX_CALIB_STEP_MAGNIF_FACTOR; + } + return calib_r0 + calib_r0_step * b / FS16XX_CALIB_STEP_MAGNIF_FACTOR; + } +} + +int fs16xx_spkset_to_calib_coef(bool ras, unsigned short spkset) { + int spkset_idx = (spkset & FS16XX_SPKSET_REG_SPKR_MSK) >> FS16XX_SPKSET_REG_SPKR_POS; + int r = 0; + if( ras ){ + r=32; + } else { + switch(spkset_idx) { + case 0: + r = 4; + break; + case 1: + r = 6; + break; + case 2: + r = 7; + break; + case 3: + default: + r = 8; + break; + } + } + if(ras){ + //#ifdef USE_RAS_MODE + calib_r0_ras = r * MAGNIF_FACTOR;; + calib_allow_low_ras = (r * R0_ALLOWANCE_LOW_RAS) / 32; + calib_allow_high_ras = (r * R0_ALLOWANCE_HIGH_RAS) / 32; + calib_r0_step_ras = (r * FS16XX_CALIB_OTP_R25_STEP_RAS) / 32; + //#endif + } else { + calib_r0 = r * MAGNIF_FACTOR; + calib_allow_low = (r * R0_ALLOWANCE_LOW) / 8; + calib_allow_high = (r * R0_ALLOWANCE_HIGH) / 8; + calib_r0_step = (r * FS16XX_CALIB_OTP_R25_STEP) / 8; + calib_r0_delta = (r * OTP_MIN_DELTA) / 8; + } + + if(ras){ + PRINT("%s calib_r0_ras=0x%X calib_allow_low_ras=0x%X calib_allow_high_ras=0x%X calib_r0_step_ras = 0x%X.", \ + __func__, calib_r0_ras, calib_allow_low_ras, calib_allow_high_ras, calib_r0_step_ras); + } else { + PRINT("%s calib_r0=0x%X calib_allow_low=0x%X calib_allow_high=0x%X calib_r0_step = 0x%X calib_r0_delta = %d.", \ + __func__, calib_r0, calib_allow_low, calib_allow_high, calib_r0_step,calib_r0_delta); + } + + return r; +} + +Fs16xx_Calib_State_t fs16xx_calc_coef_from_zm(Fs16xx_devId_t id, unsigned short zm, int storeResult) { + int r25; + unsigned short spk_err, spk_m24, spk_m6, spk_rec, ext_tempr, value = 0; + Fs16xx_Error_t err = Fs16xx_Error_OK; + Fs_Dev_State *dev_state = NULL; + Fs16xx_Calib_Result_t *calib_result; + + dev_state = fs16xx_get_dev_state_by_id(id); + if(!dev_state) { + PRINT_ERROR("%s invalid id, exit!", __func__); + return Fs16xx_Calib_Failed; + } + calib_result = &(dev_state->calib_result); + ext_tempr = (dev_state->tempr_sel >> 1); + // Read spk_set everytime to make sure spk_set value updated. + err |= fs16xx_read_register16(id, FS16XX_SPKSET_REG, &(dev_state->spk_set)); + // Apply values from preset + fs16xx_spkset_to_calib_coef(dev_state->ras,dev_state->spk_set); + + if(zm != 0) { + // Read 0xE6 + fs16xx_read_register16(id, FS16XX_OTPPG1W2_REG, &value); + if(value != 0) { + value = value & 0xFF; + } else { + PRINT_ERROR("%s OTPPG1W2_REG unexpected! Set default value.", __func__); + value = RS_TRIM_DEFAULT; + } + r25 = value * RS2RL_RATIO * MAGNIF_FACTOR / zm; + } else { + r25 = 0xFFFF; // Invalid r25 value + } + + // Update calibration result even if the result is not valid + calib_result->r25 = r25; + calib_result->calib_ver = FS16XX_CALIB_VERSION; + calib_result->calib_result_zm = zm; + + PRINT("%s calibration result zm=0x%04X r25=%d.", __func__, zm, r25); + if(dev_state->ras){ + if (r25 > calib_allow_low_ras && r25 < calib_allow_high_ras) { + PRINT("%s Receiver r25=%d.", __func__, r25); + } else { + // Calibration result invalid, need to re-calibrate + PRINT("%s Receiver invalid result r25=%d.", __func__, r25); + return Fs16xx_Calib_Failed; + } + } else { + if(r25 < calib_allow_high && r25 > calib_allow_low) { + PRINT("%s Speaker r25=%d.",__func__,r25); + } else { + // Calibration result invalid, need to re-calibrate + PRINT("%s Speaker invalid result r25=%d.", __func__, r25); + return Fs16xx_Calib_Failed; + } + } + spk_m24 = (unsigned short)((unsigned int)zm * MAGNIF_TEMPR_COEF / + (MAGNIF_TEMPR_COEF + dev_state->tempr_coef * (((dev_state->tmax + 20)- ext_tempr)))); + + spk_err = spk_m24 / 2; + + spk_m6 = (unsigned short)((unsigned int)zm * MAGNIF_TEMPR_COEF / + (MAGNIF_TEMPR_COEF + dev_state->tempr_coef * (((dev_state->tmax) - ext_tempr)))); + + spk_rec = (unsigned short)((unsigned int)zm * MAGNIF_TEMPR_COEF / + (MAGNIF_TEMPR_COEF + dev_state->tempr_coef * (((dev_state->tmax - 10) - ext_tempr)))); + + calib_result->spk_err = spk_err; + calib_result->spk_m24 = spk_m24; + calib_result->spk_m6 = spk_m6; + calib_result->spk_recover = spk_rec; + + err |= fs16xx_write_register16(id, FS16XX_SPKERR_REG, spk_err); + err |= fs16xx_write_register16(id, FS16XX_SPKM24_REG, spk_m24); + err |= fs16xx_write_register16(id, FS16XX_SPKM6_REG, spk_m6); + err |= fs16xx_write_register16(id, FS16XX_SPKRE_REG, spk_rec); + + fs16xx_write_register16(id, FS16XX_ADCTIME_REG, CALIB_REG_NORMAL_BA); + fs16xx_write_register16(id, FS16XX_TSCTRL_REG, CALIB_REG_NORMAL_AF); + + if(storeResult) { +#ifdef FS16XX_CALIB_STORE_TO_OTP + err |= fs16xx_write_to_otp(id, fs16xx_r25_to_byte(dev_state->ras,r25)); +#else + err |= fs16xx_save_calib_result(id, zm); +#endif + } + + PRINT("%s calibration result spk_err=0x%04X spk_m24=0x%04X spk_m6=0x%04X spk_rec=0x%04X reg set-%s.", + __func__, spk_err, spk_m24, spk_m6, spk_rec, (Fs16xx_Error_OK == err) ? "OK" : "Fail"); + return (Fs16xx_Error_OK == err) ? Fs16xx_Calib_OK : dev_state->calib_state; +} + +Fs16xx_Calib_State_t fs16xx_update_coef_from_r25(Fs16xx_devId_t id, int r25) { + unsigned short spk_err, spk_m24, spk_m6, spk_rec, value = 0; + Fs16xx_Error_t err = Fs16xx_Error_OK; + unsigned int zm, ext_tempr; + Fs_Dev_State *dev_state = NULL; + Fs16xx_Calib_Result_t *calib_result; + + dev_state = fs16xx_get_dev_state_by_id(id); + if(!dev_state) { + PRINT_ERROR("%s invalid id, exit!", __func__); + return Fs16xx_Calib_Failed; + } + calib_result = &(dev_state->calib_result); + ext_tempr = (dev_state->tempr_sel >> 1); + + if(dev_state->ras){ + if (r25 < calib_allow_low_ras || r25 > calib_allow_high_ras) { + // Calibration result invalide, need to re-calibrate + PRINT_ERROR("%s calibration result out of range r25=%d.", __func__, r25); + return Fs16xx_Calib_Failed; + } + } else { + if (r25 < calib_allow_low || r25 > calib_allow_high) { + // Calibration result invalide, need to re-calibrate + PRINT_ERROR("%s calibration result out of range r25=%d.", __func__, r25); + return Fs16xx_Calib_Failed; + } + } + + if(r25 != 0) { + // Read 0xE6 + fs16xx_read_register16(id, FS16XX_OTPPG1W2_REG, &value); + if(value != 0) { + value = value & 0xFF; + } else { + PRINT_ERROR("%s OTPPG1W2_REG unexpected! Set default value.", __func__); + value = RS_TRIM_DEFAULT; + } + zm = value * RS2RL_RATIO * MAGNIF_FACTOR / r25; + } else { + PRINT_ERROR("%s Invalid r25!", __func__); + return Fs16xx_Calib_Failed; + } + spk_m24 = (unsigned short)((unsigned int)zm * MAGNIF_TEMPR_COEF / + (MAGNIF_TEMPR_COEF + dev_state->tempr_coef * (((dev_state->tmax + 20) - ext_tempr)))); + + spk_err = spk_m24 / 2; + + spk_m6 = (unsigned short)((unsigned int)zm * MAGNIF_TEMPR_COEF / + (MAGNIF_TEMPR_COEF + dev_state->tempr_coef * (((dev_state->tmax) - ext_tempr)))); + + spk_rec = (unsigned short)((unsigned int)zm * MAGNIF_TEMPR_COEF / + (MAGNIF_TEMPR_COEF + dev_state->tempr_coef * (((dev_state->tmax - 10) - ext_tempr)))); + + calib_result->spk_err = spk_err; + calib_result->spk_m24 = spk_m24; + calib_result->spk_m6 = spk_m6; + calib_result->spk_recover = spk_rec; + calib_result->r25 = r25; + + err |= fs16xx_write_register16(id, FS16XX_SPKERR_REG, spk_err); + err |= fs16xx_write_register16(id, FS16XX_SPKM24_REG, spk_m24); + err |= fs16xx_write_register16(id, FS16XX_SPKM6_REG, spk_m6); + err |= fs16xx_write_register16(id, FS16XX_SPKRE_REG, spk_rec); + + PRINT("%s read calibration result spk_err=0x%04X spk_m24=0x%04X spk_m6=0x%04X spk_rec=0x%04X reg set-%s.", + __func__, spk_err, spk_m24, spk_m6, spk_rec, (Fs16xx_Error_OK == err) ? "OK" : "Fail"); + return (Fs16xx_Error_OK == err) ? Fs16xx_Calib_OK : Fs16xx_Calib_Failed; +} + + +#ifdef FS16XX_CALIB_STORE_TO_OTP + +Fs16xx_Error_t fs16xx_write_to_otp(Fs16xx_devId_t id, unsigned char valOTP) { + int count, delta; + Fs16xx_Error_t err = Fs16xx_Error_OK; + unsigned short value = 0, valPwr = 0, valueC0 = 0, valueC4 = 0; + Fs_Dev_State *dev_state = NULL; + Fs16xx_Calib_Result_t *calib_result; + + dev_state = fs16xx_get_dev_state_by_id(id); + if(!dev_state) { + PRINT_ERROR("%s invalid id, exit!", __func__); + return Fs16xx_Error_StateInvalid; + } + calib_result = &(dev_state->calib_result); + + PRINT("%s enter valOTP=0x%02x.", __func__, valOTP); + err |= fs16xx_read_register16(id, FS16XX_SYSCTRL_REG, &valPwr); + // power up, amp off + err |= fs16xx_write_register16(id, FS16XX_SYSCTRL_REG, 0x0000); + + err |= fs16xx_read_register16(id, FS16XX_PLLCTRL4_REG, &valueC4); + err |= fs16xx_write_register16(id, FS16XX_PLLCTRL4_REG, 0x000A); + + err |= fs16xx_read_register16(id, FS16XX_BSTCTRL_REG, &valueC0); + err |= fs16xx_write_register16(id, FS16XX_BSTCTRL_REG, 0x0000); + + // OTP page + err |= fs16xx_write_register16(id, FS16XX_OTPADDR_REG, 0x0010); + // OTP read clear + err |= fs16xx_write_register16(id, FS16XX_OTPCMD_REG, 0x0000); + // OTP read + err |= fs16xx_write_register16(id, FS16XX_OTPCMD_REG, 0x0001); + + if(fs16xx_otp_is_busy(id) != 0) { + dev_state->calib_state= Fs16xx_Calib_OTP_Failed; + PRINT_ERROR("%s, Error, otp busy bit not cleared!", __func__); + goto otp_op_exit; + } + + fs16xx_read_register16(id, FS16XX_OTPRDATA_REG, &value); + if(value == OTP_INIT_VALUE) { + count = 0; + } else { + count = fs16xx_get_otp_write_count(value & 0xFF); + } + PRINT("%s read calibration result = 0x%04x otp write count=%d.", + __func__, value, count); + + delta = (int)fs16xx_byte_to_r25(dev_state->ras,valOTP) + - fs16xx_byte_to_r25(dev_state->ras,(value >> 8) & 0xFF); + if(delta < 0) delta *= -1; + + // OTP won't updated if calibrated r25 is not changed much(defined by otpDelta). + if(dev_state->ras) { + PRINT("%s delta = %d otpDelta_ras = %d.", __func__, delta, OTP_MIN_DELTA_RAS); + } else { + PRINT("%s delta = %d otpDelta = %d.", __func__, delta, calib_r0_delta); + } + if(dev_state->ras){ + if(count > 0 && delta < OTP_MIN_DELTA_RAS) { + PRINT("%s recevier no need to write to otp.", __func__); + goto otp_op_exit; + } + }else{ + if(count > 0 && delta < calib_r0_delta) { + PRINT("%s no need to write to otp.", __func__); + goto otp_op_exit; + } + } + + // Calibration Count + if(count >= FS16XX_CALIB_MAX_USER_TRY) { + PRINT_ERROR("%s calibrated count exceeds max.", __func__); + calib_result->calib_count = count; + dev_state->calib_state = Fs16xx_Calib_OTP_ExcceedMaxTry; + err = Fs16xx_Error_Calib_Error; + goto otp_op_exit; + } + + err |= fs16xx_write_register16(id, FS16XX_PLLCTRL4_REG, 0x000B); + + // enable Boost, and set it to follow mode + err |= fs16xx_write_register16(id, FS16XX_BSTCTRL_REG, 0x0008); + + // Write OTP page + err |= fs16xx_write_register16(id, FS16XX_OTPADDR_REG, 0x0010); + + // OTP write byte + err |= fs16xx_write_register16(id, FS16XX_OTPWDATA_REG, (unsigned short)valOTP); + + // Clear OTP commands + err |= fs16xx_write_register16(id, FS16XX_OTPCMD_REG, 0x0000); + // OTP enable Boost 7.6V mode + err |= fs16xx_write_register16(id, FS16XX_OTPCMD_REG, 0x2000); + + // Wait until ss_end + if(fs16xx_wait_boost_ssend(fs16xx_get_codec_by_id(id)) != Fs16xx_Error_OK) { + PRINT_ERROR("%s, Error, failed to wait for boost ssend!", __func__); + goto otp_op_exit; + } + + // Send single-byte write command + err |= fs16xx_write_register16(id, FS16XX_OTPCMD_REG, 0x2002); + + // Wait for 5ms for otp operation to complete. + Sleep(5); + if(fs16xx_otp_is_busy(id) != 0) { + dev_state->calib_state = Fs16xx_Calib_OTP_Failed; + err = Fs16xx_Error_Calib_Error; + goto otp_op_exit; + } + + // Clear OTP commands + err |= fs16xx_write_register16(id, FS16XX_OTPCMD_REG, 0x0000); + + Sleep(1); + + // disable boost + err |= fs16xx_write_register16(id, FS16XX_BSTCTRL_REG, 0x0000); + + calib_result->calib_count = count + 1; + + // Read back + err |= fs16xx_write_register16(id, FS16XX_PLLCTRL4_REG, 0x000A); + + err |= fs16xx_write_register16(id, FS16XX_BSTCTRL_REG, 0x0000); + + // OTP page + err |= fs16xx_write_register16(id, FS16XX_OTPADDR_REG, 0x0010); + // OTP read clear + err |= fs16xx_write_register16(id, FS16XX_OTPCMD_REG, 0x0000); + // OTP read + err |= fs16xx_write_register16(id, FS16XX_OTPCMD_REG, 0x0001); + + if(fs16xx_otp_is_busy(id) != 0) { + dev_state->calib_state = Fs16xx_Calib_OTP_Failed; + err = Fs16xx_Error_Calib_Error; + goto otp_op_exit; + } + + fs16xx_read_register16(id, FS16XX_OTPRDATA_REG, &value); + if(((value >> 8) & 0xFF) == valOTP) { + // Update count + calib_result->calib_count = fs16xx_get_otp_write_count(value & 0xFF); + PRINT("%s readback succeeded with count = %d.", __func__, calib_result->calib_count); + } else { + err = Fs16xx_Error_Calib_Error; + PRINT_ERROR("%s calibrated read back failed with value = 0x%04X(expected = 0x%02X--). Should not happen.", + __func__, value, valOTP); + } +otp_op_exit: + err |= fs16xx_write_register16(id, FS16XX_BSTCTRL_REG, valueC0); + err |= fs16xx_write_register16(id, FS16XX_PLLCTRL4_REG, valueC4); + // Recover power state + err |= fs16xx_write_register16(id, FS16XX_SYSCTRL_REG, valPwr); + + DEBUGPRINT("%s exit, error = %d.", __func__, err); + + return err; +} + +#else + +Fs16xx_Error_t fs16xx_save_calib_result(Fs16xx_devId_t id, unsigned short zmdata) { + FILE *fp; + int count; + Fs_Dev_State *dev_state = NULL; + Fs16xx_Calib_Result_t *calib_result; + + dev_state = fs16xx_get_dev_state_by_id(id); + if(!dev_state) { + PRINT_ERROR("%s invalid id, exit!", __func__); + return Fs16xx_Error_StateInvalid; + } + calib_result = &(dev_state->calib_result); + + DEBUGPRINT("%s enter.", __func__); + + // Write to file + fp = fopen( FS16XX_PARAM_DIR FS16XX_CALIB_RESULT_FILE_NAME, "wb"); + if (!fp) + { + PRINT_ERROR("%s open calibration file failed.", __func__); + return Fs16xx_Error_Calib_Error; + } + + calib_result.calib_count = g_calib_count; + calib_result.calib_ver = FS16XX_CALIB_VERSION; + calib_result.calib_result_zm = zmdata; + count = fwrite( (void*)&calib_result, 1, sizeof(Fs16xx_Calib_Result_t), fp ); + fclose(fp); + + PRINT("%s write to calibration file, count=%d.", __func__, count); + + return Fs16xx_Error_OK; +} +#endif + +Fs16xx_Error_t fs16xx_set_tempr_protection(Fs16xx_devId_t id, int enable) { + Fs16xx_Error_t err = Fs16xx_Error_OK; + Fs_Dev_State *dev_state = NULL; + Fs16xx_Calib_Result_t *calib_result; + + dev_state = fs16xx_get_dev_state_by_id(id); + if(!dev_state) { + PRINT_ERROR("%s invalid id, exit!", __func__); + return Fs16xx_Error_StateInvalid; + } + calib_result = &(dev_state->calib_result); + + PRINT("%s enter. enable=%d", __func__, enable); + if(enable) { + // TS on + err = fs16xx_write_register16(id, FS16XX_TSCTRL_REG, CALIB_REG_NORMAL_AF); + if(Fs16xx_Calib_Unknown == dev_state->calib_state) { + fs16xx_check_calibration(id); + } + if(Fs16xx_Calib_OK != dev_state->calib_state + && Fs16xx_Calib_OnGoing != dev_state->calib_state + && Fs16xx_Calib_OTP_ExcceedMaxTry != dev_state->calib_state) { + fs16xx_calibration(id, 0, 1); + } + }else { + // TS off + err = fs16xx_write_register16(id, FS16XX_TSCTRL_REG, CALIB_REG_TSOFF_AF); + // Delay enough time for ts off gracefully to avoid pop. + Sleep(35); + //fs16xx_wait_ts_off(id, 60); + } + + DEBUGPRINT("%s exit.", __func__); + return err; +} + diff --git a/asoc/codecs/foursemi/fs16xx_calibration.h b/asoc/codecs/foursemi/fs16xx_calibration.h new file mode 100644 index 000000000000..74b941cd255c --- /dev/null +++ b/asoc/codecs/foursemi/fs16xx_calibration.h @@ -0,0 +1,128 @@ +/* +* Copyright (C) 2016 Fourier Semiconductor Inc. +* +*/ + +#ifndef FS16XX_CALIBRATION_H +#define FS16XX_CALIBRATION_H + +#include "fs16xx_define.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#define MAGNIF_FACTOR 0x400 +#define RS2RL_RATIO 2700 +// TODO: to be loaded from preset +#define RS_TRIM_DEFAULT 0x8F + +#define R0_DEFAULT (8 * MAGNIF_FACTOR) + +#define R0_ALLOWANCE 30 // 30% +#define FS16XX_CALIB_STEP_MAGNIF_FACTOR 100 +#define R0_ALLOWANCE_LOW ((R0_DEFAULT * (100 - R0_ALLOWANCE)) / FS16XX_CALIB_STEP_MAGNIF_FACTOR) +#define R0_ALLOWANCE_HIGH ((R0_DEFAULT * (100 + R0_ALLOWANCE)) / FS16XX_CALIB_STEP_MAGNIF_FACTOR) + +#define FS16XX_CALIB_OTP_R25_STEP ((int)(R0_ALLOWANCE * R0_DEFAULT) / 0x7F) + +#define OTP_MIN_DELTA (int)(R0_DEFAULT * 0.05f) + +//RAS MODE +//#ifdef USE_RAS_MODE +#define R0_DEFAULT_RAS (32 * MAGNIF_FACTOR) +#define R0_ALLOWANCE_RAS 20 +#define R0_ALLOWANCE_LOW_RAS ((R0_DEFAULT_RAS * (100 - R0_ALLOWANCE_RAS)) / FS16XX_CALIB_STEP_MAGNIF_FACTOR) +#define R0_ALLOWANCE_HIGH_RAS ((R0_DEFAULT_RAS * (100 + R0_ALLOWANCE_RAS)) / FS16XX_CALIB_STEP_MAGNIF_FACTOR) +#define FS16XX_CALIB_OTP_R25_STEP_RAS ((int)(R0_ALLOWANCE_RAS * R0_DEFAULT_RAS) / 0x7F) +#define OTP_MIN_DELTA_RAS (int)(R0_DEFAULT_RAS * 0.05f) +//#endif + +#define MAGNIF_TEMPR_COEF 0xFFFF + +#define TMAX 120 +#define TMIN -20 +#define SPKTMPRM24 115 +#define SPKTMPRM6 110 +#define SPKTMPRECOVERY 100 + +#define OTP_OP_COUNTER_OFFSET 16 +#define OTP_INIT_VALUE 0xFF10 + +#define SPK_CALIBRATION_MAX_ZMDELTA 0x150 + +#define FS16XX_CALIB_VERSION 1 + +// Store calibration result to OTP, remove following macro to store calibration result to file system. +#define FS16XX_CALIB_STORE_TO_OTP 1 + +#define FS16XX_CALIB_RESULT_FILE_NAME "fs16xxcali.bin" + +#define CALIB_REG_NORMAL_BA 0x0031 +#define CALIB_REG_CALIB_MODE_AF 0x162B +#define CALIB_REG_NORMAL_AF 0x362B +#define CALIB_REG_TSOFF_AF 0x3623 + +/* DO NOT CHANGE THIS VALUE */ +#define FS16XX_CALIB_MAX_USER_TRY 15 + +/* Type containing all the possible errors that can occur + * + */ +enum Fs16xx_Calib_State { + Fs16xx_Calib_OK = 0, + Fs16xx_Calib_NotStarted = 1, + Fs16xx_Calib_OnGoing = 2, + Fs16xx_Calib_Failed = 3, + Fs16xx_Calib_OTP_Failed = 4, + Fs16xx_Calib_OTP_ExcceedMaxTry = 5, + Fs16xx_Calib_Unknown = 10 +}; +typedef enum Fs16xx_Calib_State Fs16xx_Calib_State_t; + +struct Fs16xx_Calib_Result { + unsigned short calib_ver; + unsigned short calib_count; + unsigned short calib_result_zm; + unsigned short spk_err; + unsigned short spk_m24; + unsigned short spk_m6; + unsigned short spk_recover; + int r25; +}; +typedef struct Fs16xx_Calib_Result Fs16xx_Calib_Result_t; + +Fs16xx_Error_t fs16xx_calibration(Fs16xx_devId_t id, int force, int storeResult); + +Fs16xx_Calib_State_t fs16xx_check_calibration(Fs16xx_devId_t id); + +Fs16xx_Error_t fs16xx_get_calibration_result(Fs16xx_devId_t id, int *r25); + +Fs16xx_Calib_State_t fs16xx_calc_coef_from_zm(Fs16xx_devId_t id, unsigned short zm, int storeResult); + +Fs16xx_Calib_State_t fs16xx_update_coef_from_r25(Fs16xx_devId_t id, int r25); + +int fs16xx_otp_is_busy(Fs16xx_devId_t id); + +int fs16xx_get_otp_write_count(unsigned short val); + +unsigned char fs16xx_r25_to_byte(bool ras,int r25); + +int fs16xx_byte_to_r25(bool ras,unsigned char b); + +int fs16xx_spkset_to_calib_coef(bool ras,unsigned short spkset); + +Fs16xx_Error_t fs16xx_set_tempr_protection(Fs16xx_devId_t id, int enable); + +#ifdef FS16XX_CALIB_STORE_TO_OTP +Fs16xx_Error_t fs16xx_write_to_otp(Fs16xx_devId_t id, unsigned char valOTP); +#else +Fs16xx_Error_t fs16xx_save_calib_result(Fs16xx_devId_t id, unsigned short zmdata); +#endif + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/asoc/codecs/foursemi/fs16xx_define.h b/asoc/codecs/foursemi/fs16xx_define.h new file mode 100644 index 000000000000..c543f8d387bc --- /dev/null +++ b/asoc/codecs/foursemi/fs16xx_define.h @@ -0,0 +1,120 @@ +/* + * Fs16xx device driver header file. + * Copyright (C) 2017 FourSemi Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef __FS16XX_DEFINE_H__ +#define __FS16XX_DEFINE_H__ + +#define FS16XX_MAX_REGISTER 0xE8 + +#define I2C_RETRY_DELAY 5 /* ms */ +#define I2C_RETRIES 20 +#define PLL_SYNC_RETRIES 10 +#define MTPB_RETRIES 5 +#define MAX_DEV_COUNT 2 + +/* DSP Firmware Size in bytes*/ +#define FS16XX_SPK_FW_SIZE 512 +#define FS16XX_CFG_FW_SIZE 512 +#define FS16XX_PST_FW_SIZE 512 +#define FS16XX_COEFF_FW_SIZE 512 +#define FS16XX_DEV_STATE_SIZE 24 +#define FS16XX_SPK_EX_FW_SIZE 512 + +#define FS16XX_REG_CACHE_SIZE 193 + +#define FS16XX_RATES (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|\ + SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000) +#define FS16XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE|SNDRV_PCM_FMTBIT_S24_LE|SNDRV_PCM_FMTBIT_S32_LE|\ + SNDRV_PCM_FORMAT_MU_LAW|SNDRV_PCM_FORMAT_S24_3LE) + +#define FIRMWARE_NAME_SIZE 128 + +#define FS16XX_MAX_I2C_SIZE 512 + +#define SYS_CLK_DEFAULT 1536000 + +#define FS16XX_DEV_INDEX_L 0 +#define FS16XX_DEV_INDEX_R 1 + +// DSP init status +#define FS16XX_DSP_INIT_PENDING 0 +#define FS16XX_DSP_INIT_OK 1 +#define FS16XX_DSP_INIT_FAILED -1 + +/* Type containing all the possible errors that can occur + * + */ +enum Fs16xx_Error { + Fs16xx_Error_OK = 0, + Fs16xx_Error_Bad_Parameter, + Fs16xx_Error_NotOpen, + Fs16xx_Error_OutOfHandles, + Fs16xx_Error_StateTimedOut, + Fs16xx_Error_StateInvalid, + + Fs16xx_Error_Not_Implemented, + Fs16xx_Error_Not_Supported, + Fs16xx_Error_I2C_Fatal, + Fs16xx_Error_I2C_NonFatal, + Fs16xx_Error_Other = 1000, + Fs16xx_Error_Not_FsDev = 1001, + Fs16xx_Error_Calib_Error = 1002, + Fs16xx_Error_Invalid_Preset = 1003 +}; +typedef enum Fs16xx_Error Fs16xx_Error_t; + +enum Fs16xx_Channel { + Fs16xx_Channel_L, // I2S left channel + Fs16xx_Channel_R, // I2S right channel + Fs16xx_Channel_L_R, // I2S (left + right channel)/2 + Fs16xx_Channel_Stereo +}; +typedef enum Fs16xx_Channel Fs16xx_Channel_t; + +enum Fs16xx_Mode { + Fs16xx_Mode_Normal = 0, + Fs16xx_Mode_RCV +}; +typedef enum Fs16xx_Mode Fs16xx_Mode_t; + +enum Fs16xx_PwrState { + Fs16xx_PwrOn = 0, + Fs16xx_PwrOff = 1 +}; +typedef enum Fs16xx_PwrState Fs16xx_PwrState_t; + +enum Fs16xx_OutputSel{ + Fs16xx_OutputSel_None = 0, + Fs16xx_OutputSel_DataL, // I2S left channel output + Fs16xx_OutputSel_DataR, // I2S right channel output + Fs16xx_OutputSel_TS // Test tone +}; +typedef enum Fs16xx_OutputSel Fs16xx_OutputSel_t; + +#define FS_DISABLE 0 +#define FS_ENABLE 1 + +#define FS_UMUTE 0 +#define FS_MUTE 1 + +typedef int Fs16xx_devId_t; + +//#define FS_STEREO_MODE +//#define USE_RAS_MODE + +#endif + diff --git a/asoc/codecs/foursemi/fs16xx_preset.c b/asoc/codecs/foursemi/fs16xx_preset.c new file mode 100644 index 000000000000..c7d8ea1e5588 --- /dev/null +++ b/asoc/codecs/foursemi/fs16xx_preset.c @@ -0,0 +1,452 @@ +/* +* Copyright (C) 2016 Fourier Semiconductor Inc. +* +*/ +#include "fs16xx.h" +#include "fs16xx_regs.h" +#include "fs16xx_preset.h" + +static int preset_init = 0; +static Fs16xx_preset gFs16xxPresets[(int)fs16xx_preset_type_max]; + +#ifndef FS_STEREO_MODE +static char const *gFs16xxPresetName[(int)fs16xx_preset_type_max] = { + "fs1801_mono.preset", + "fs1801_mono.preset" // prevent from crash in kernel +}; +#else +static char const *gFs16xxPresetName[(int)fs16xx_preset_type_max] = { + "fs1801_left.preset", + "fs1801_right.preset" +}; +#endif + +#define CRC16_TABLE_SIZE 256 +const uint16_t gCrc16Polynomial = 0xA001; +static uint16_t gCrc16Table[CRC16_TABLE_SIZE]; + +#define PRESET_VALID 1 +#define PRESET_INVALID 0 + +/* Initialize presets should only be called once in fs16xx_init +*/ +Fs16xx_Error_t fs16xx_initialize_presets(void) { + uint16_t value; + uint16_t temp, i; + uint8_t j; + if(preset_init) return Fs16xx_Error_OK; + memset(gFs16xxPresets, 0, sizeof(Fs16xx_preset) * (int)fs16xx_preset_type_max); + + // Initialize CRC16 table + for (i = 0; i < CRC16_TABLE_SIZE; ++i) + { + value = 0; + temp = i; + for (j = 0; j < 8; ++j) + { + if (((value ^ temp) & 0x0001) != 0) + { + value = (uint16_t)((value >> 1) ^ gCrc16Polynomial); + } + else + { + value >>= 1; + } + temp >>= 1; + } + gCrc16Table[i] = value; + } + preset_init = 1; + return Fs16xx_Error_OK; +} + +Fs16xx_Error_t fs16xx_deinitialize_presets(void) { + int i; + for(i = 0; i < (int)fs16xx_preset_type_max; i++) { + if(PRESET_VALID == gFs16xxPresets[i].valid) { + if(gFs16xxPresets[i].preset_header.data) { + gFs16xxPresets[i].valid = PRESET_INVALID; + kfree(gFs16xxPresets[i].preset_header.data); + } + } + } + + return Fs16xx_Error_OK; +} + +// load_presets should only be called once after initialization +Fs16xx_Error_t fs16xx_load_presets(Fs16xx_devId_t id) { + const struct firmware *fs_firmware = NULL; + struct snd_soc_codec *codec; + int j, data_len, buf_size, ret, pos; + uint16_t *ptr_data = NULL; + uint16_t *ptr_crc_data = NULL; + int32_t *ptr_excur_prot_ram = NULL; + uint16_t checksum; + // Preset crc data length in uint16_t : size from member tmax to the start of excur_prot_ram + const int preset_header_crc_data_len = + (sizeof(Fs16xx_preset_header_t) - sizeof(int32_t *) - sizeof(uint16_t *) + - sizeof(uint16_t) * 3 - 32 - sizeof(int64_t)) / 2; + const int preset_header_len = sizeof(Fs16xx_preset_header_t) - sizeof(int32_t *) + - sizeof(uint16_t *); + + PRINT("%s enter. id = %d", __func__, id); + + if(FS16XX_DEV_INDEX_L != id && FS16XX_DEV_INDEX_R != id) { + PRINT_ERROR("%s invalid id[%d]", __func__, id); + return Fs16xx_Error_Bad_Parameter; + } + + codec = fs16xx_get_codec_by_id(id); + if(NULL == codec) { + PRINT_ERROR("%s invalid codec with id[%d]", __func__, id); + return Fs16xx_Error_Bad_Parameter; + } + + fs16xx_initialize_presets(); + + if(gFs16xxPresets[id].valid) { + PRINT_ERROR("%s invalid status, presets already loaded.", __func__); + return Fs16xx_Error_StateInvalid; + } + + ret = request_firmware(&fs_firmware, gFs16xxPresetName[id], codec->dev); + if (ret) { + PRINT_ERROR("%s Failed to load fs preset table %s", __func__, gFs16xxPresetName[id]); + return Fs16xx_Error_Invalid_Preset; + } + PRINT("fs16xx: loaded firmware size = 0x%04X", fs_firmware->size); + if(fs_firmware->size < sizeof(Fs16xx_preset_header_t)) { + PRINT_ERROR("%s Invalid preset table %s size=%d", __func__, + gFs16xxPresetName[id], fs_firmware->size); + release_firmware(fs_firmware); + return Fs16xx_Error_Invalid_Preset; + } + + // Parse header + memcpy(&(gFs16xxPresets[id].preset_header), fs_firmware->data, preset_header_len); + + // Checking preset version + if(PRESET_VERSION != gFs16xxPresets[id].preset_header.preset_ver) { + PRINT_ERROR("%s preset version[0x%04X] invalid!", + __func__, gFs16xxPresets[id].preset_header.preset_ver); + release_firmware(fs_firmware); + return Fs16xx_Error_Invalid_Preset; + } + PRINT("fs16xx: customer_name: %s\n", gFs16xxPresets[id].preset_header.customer_name); + data_len = gFs16xxPresets[id].preset_header.data_len; + if(data_len <= 0 || (data_len % 2) != 0) { + PRINT_ERROR("%s preset header [%s] validation checking failed. data_len=%d", + __func__, gFs16xxPresetName[id], data_len); + release_firmware(fs_firmware); + return Fs16xx_Error_Invalid_Preset; + } + + // Copy excur prot + buf_size = EXCUR_PROT_RAM_LEN * sizeof(int32_t); + ptr_excur_prot_ram = (int32_t *)kzalloc(buf_size, GFP_KERNEL); + if (!ptr_excur_prot_ram) { + PRINT_ERROR("%s failed to allocate %d bytes for excur_prot_ram.\n", __func__, buf_size); + gFs16xxPresets[id].preset_header.excur_prot_ram = 0; + release_firmware(fs_firmware); + return Fs16xx_Error_Invalid_Preset; + } + memcpy(ptr_excur_prot_ram, ((u8*)fs_firmware->data) + preset_header_len, buf_size); + + buf_size = data_len * sizeof(uint16_t); + ptr_data = kzalloc(buf_size, GFP_KERNEL); + if (!ptr_data) { + PRINT_ERROR("%s failed to allocate %d bytes.", __func__, buf_size); + gFs16xxPresets[id].preset_header.data = 0; + release_firmware(fs_firmware); + return Fs16xx_Error_Invalid_Preset; + } + + memcpy(ptr_data, ((u8*)fs_firmware->data) + preset_header_len + + (EXCUR_PROT_RAM_LEN * sizeof(int32_t)), buf_size); + release_firmware(fs_firmware); + + // Fill ptr_crc_data + buf_size = (data_len + preset_header_crc_data_len) * sizeof(uint16_t) + + EXCUR_PROT_RAM_LEN * sizeof(int32_t); + DEBUGPRINT("%s buf_size = 0x%04X", __func__, buf_size); + ptr_crc_data = (uint16_t *)kzalloc(buf_size, GFP_KERNEL); + if (!ptr_crc_data) { + PRINT_ERROR("%s failed to allocate %d bytes.\n", __func__, buf_size); + gFs16xxPresets[id].preset_header.data = 0; + return Fs16xx_Error_Invalid_Preset; + } + + pos = 0; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.tmax; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.tempr_coef; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.tempr_sel; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.spk_set; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.adp_bst[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.adp_bst[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.bst_ctrl[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.bst_ctrl[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.agc_ctrl[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.agc_ctrl[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.agc_thd[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.agc_thd[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.agc_para[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.agc_para[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.agc_mg[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.agc_mg[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.dac_para1[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.dac_para1[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.acs_drc[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.acs_drc[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.acs_drcs[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.acs_drcs[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.acs_drcp[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.acs_drcp[1]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.ts_ctrl[0]; pos++; + ptr_crc_data[pos] = gFs16xxPresets[id].preset_header.ts_ctrl[1]; pos++; + + for (j = 0; j < EXCUR_PROT_RAM_LEN; j++) + { + ptr_crc_data[pos] = (uint16_t)(ptr_excur_prot_ram[j] & 0xFFFF); + ptr_crc_data[pos + 1] = (uint16_t)((ptr_excur_prot_ram[j] & 0xFF0000) >> 16); + pos += 2; + } + + for (j = 0; j < data_len; j++) + { + ptr_crc_data[pos] = ptr_data[j]; + pos++; + } + + /*if(pos != (data_len + preset_header_crc_data_len)) { + PRINT_ERROR("%s preset length not matching! pos=%d expected=%d.\n", __func__, + pos, (data_len + preset_header_crc_data_len)); + kfree(ptr_data); + kfree(ptr_crc_data); + return Fs16xx_Error_Invalid_Preset; + }*/ + + checksum = fs16xx_calc_checksum(ptr_crc_data, buf_size / 2); + kfree(ptr_crc_data); + + if(checksum != gFs16xxPresets[id].preset_header.crc16) { + PRINT_ERROR("%s preset data [%s] checksum not match. checksum=0x%04X, expected=0x%04X", + __func__, gFs16xxPresetName[id], checksum, gFs16xxPresets[id].preset_header.crc16); + gFs16xxPresets[id].preset_header.data = 0; + kfree(ptr_data); + + return Fs16xx_Error_Invalid_Preset; + } + + gFs16xxPresets[id].preset_header.data = ptr_data; + gFs16xxPresets[id].preset_header.excur_prot_ram = ptr_excur_prot_ram; + gFs16xxPresets[id].valid = PRESET_VALID; + + fs16xx_load_presets_to_dev(id); + PRINT("%s successfully exit. boost 0x%04X", __func__, + gFs16xxPresets[id].preset_header.bst_ctrl[0]); + return Fs16xx_Error_OK; +} + +Fs16xx_Error_t fs16xx_load_presets_to_dev(Fs16xx_devId_t id) { + int i, data_len; + Fs16xx_Error_t err = Fs16xx_Error_OK; + uint16_t *ptr_data = 0; + int32_t *ptr_prot_coef = 0; + uint16_t valPwr; + Fs_Dev_State *dev_state = fs16xx_get_dev_state_by_id(id); + + if(FS16XX_DEV_INDEX_L != id && FS16XX_DEV_INDEX_R != id) { + PRINT_ERROR("%s invalid id[%d]", __func__, id); + return Fs16xx_Error_Bad_Parameter; + } + if(dev_state == NULL){ + PRINT_ERROR("%s dev_state is null!", __func__); + return Fs16xx_Error_Bad_Parameter; + } + + if(gFs16xxPresets[id].valid != PRESET_VALID + || gFs16xxPresets[id].preset_header.data == NULL) { + PRINT_ERROR("%s invalid preset id[%d] data=%p", __func__, + id, gFs16xxPresets[id].preset_header.data); + return Fs16xx_Error_Invalid_Preset; + } + + dev_state->cur_preset = -1; // Reset preset index + dev_state->tmax = gFs16xxPresets[id].preset_header.tmax; + dev_state->tempr_coef = gFs16xxPresets[id].preset_header.tempr_coef; + dev_state->tempr_sel = gFs16xxPresets[id].preset_header.tempr_sel; + dev_state->spk_set = gFs16xxPresets[id].preset_header.spk_set; + dev_state->adp_bst = gFs16xxPresets[id].preset_header.adp_bst[0]; + dev_state->bst_ctrl = gFs16xxPresets[id].preset_header.bst_ctrl[0]; + dev_state->acs_drc_ctrl[0] = gFs16xxPresets[id].preset_header.acs_drc[0]; + dev_state->acs_drc_ctrl[1] = gFs16xxPresets[id].preset_header.acs_drcs[0]; + dev_state->acs_drc_ctrl[2] = gFs16xxPresets[id].preset_header.acs_drcp[0]; + + dev_state->ts_ctrl = gFs16xxPresets[id].preset_header.ts_ctrl[0]; + + data_len = gFs16xxPresets[id].preset_header.data_len; + ptr_data = gFs16xxPresets[id].preset_header.data; + ptr_prot_coef = gFs16xxPresets[id].preset_header.excur_prot_ram; + dev_state->dev_preset = &(gFs16xxPresets[id]); + + DEBUGPRINT("%s ptr_data=%p data_len=%d", __func__, ptr_data, data_len); + + // Amp off + err |= fs16xx_read_register16(id, FS16XX_SYSCTRL_REG, &valPwr); + err |= fs16xx_write_register16(id, FS16XX_SYSCTRL_REG, valPwr & (~FS16XX_SYSCTRL_REG_AMPE)); + + // Set spk_set + err |= fs16xx_write_register16(id, FS16XX_SPKSET_REG, gFs16xxPresets[id].preset_header.spk_set); + // Set tempr coef to TEMPSEL, refer to Bug 55 + err |= fs16xx_write_register16(id, FS16XX_TEMPSEL_REG, (gFs16xxPresets[id].preset_header.tempr_coef) << 1); + + fs16xx_write_register16(id, FS16XX_OTPACC_REG, FS16XX_OTP_ACC_KEY2); + + if(Fs16xx_Error_OK == fs16xx_wait_dsp_off(id, 0, 100)) { + // Start from position 0 + err |= fs16xx_write_register16(id, FS16XX_DACEQA_REG, 0); + // Write ACS data + for (i = 0; i < gFs16xxPresets[id].preset_header.data_len; i+=2) { + fs16xx_write_register16(id, FS16XX_DACEQWL_REG, ptr_data[i] & 0xFFFF); + fs16xx_write_register16(id, FS16XX_DACEQWH_REG, ptr_data[i + 1] & 0xFF); + //DEBUGPRINT("%s __DEBUG__ DSP write 0x%08X", __func__, ptr_data[i]); + } + + // Start from position 0 + err |= fs16xx_write_register16(id, FS16XX_DACEQA_REG, 0xD2); + // Write prot coef + for (i = 0; i < EXCUR_PROT_RAM_LEN; i++) { + fs16xx_write_register16(id, FS16XX_DACEQWL_REG, ptr_prot_coef[i] & 0xFFFF); + fs16xx_write_register16(id, FS16XX_DACEQWH_REG, (ptr_prot_coef[i] >> 16) & 0xFF); + //PRINT_ERROR("%s __DEBUG__ DSP write 0x%08X", __func__, ptr_prot_coef[i]); + } + + } else { + PRINT_ERROR("%s failed to wait for dsp off!", __func__); + return Fs16xx_Error_StateInvalid; + } + + err |= fs16xx_write_register16(id, FS16XX_OTPACC_REG, 0); + // Recover amp + err |= fs16xx_write_register16(id, FS16XX_SYSCTRL_REG, valPwr); + + return err; +} + +uint16_t fs16xx_calc_checksum(uint16_t *data, int len) { + uint16_t crc = 0; + uint8_t b, index; + int i; + if(len <= 0) return 0; + + for (i = 0; i < len; i++) + { + b = (uint8_t)(data[i] & 0xFF); + index = (uint8_t)(crc ^ b); + crc = (uint16_t)((crc >> 8) ^ gCrc16Table[index]); + + b = (uint8_t)((data[i] >> 8) & 0xFF); + index = (uint8_t)(crc ^ b); + crc = (uint16_t)((crc >> 8) ^ gCrc16Table[index]); + } + return crc; +} + +/* fs16xx_set_preset must be called when osc is on */ +Fs16xx_Error_t fs16xx_set_preset(Fs16xx_devId_t id, int preset) { + Fs16xx_Error_t err = Fs16xx_Error_OK; + uint16_t valPwr, valAcsCtrl, valPll, tmpC0; + int i; + Fs_Dev_State *dev_state = NULL; + Fs16xx_preset_header_t *preset_header = NULL; + + PRINT("%s set preset %d.", __func__, preset); + + if (Fs16xx_Error_OK != fs16xx_handle_is_open(id)) { + PRINT_ERROR("%s device not opened.", __func__); + return Fs16xx_Error_NotOpen; + } + + dev_state = fs16xx_get_dev_state_by_id(id); + if(!dev_state) { + PRINT_ERROR("%s invalid id, exit!", __func__); + return Fs16xx_Error_StateInvalid; + } + + if(!dev_state->dev_preset) { + PRINT_ERROR("%s invalid dev_preset, exit!", __func__); + return Fs16xx_Error_StateInvalid; + } + + if(preset != 0 && preset != 1) { + PRINT_ERROR("%s invalid preset value! preset = %d", __func__, preset); + return Fs16xx_Error_Bad_Parameter; + } + + if(preset == dev_state->cur_preset) { + PRINT("%s cur preset already set %d, exit.", __func__, preset); + return Fs16xx_Error_OK; + } + i = preset; + preset_header = &(dev_state->dev_preset->preset_header); + + if(!dev_state->dev_preset->valid || preset_header == NULL) { + PRINT_ERROR("%s preset invalid! preset = %d preset_header = %p", __func__, preset, preset_header); + return Fs16xx_Error_Bad_Parameter; + } + + // Amp off + err |= fs16xx_read_register16(id, FS16XX_SYSCTRL_REG, &valPwr); + err |= fs16xx_write_register16(id, FS16XX_SYSCTRL_REG, valPwr & (~FS16XX_SYSCTRL_REG_AMPE)); + + if(Fs16xx_Error_OK == fs16xx_wait_dsp_off(id, 0, 50)) { + // Enable osc + err |= fs16xx_read_register16(id, FS16XX_PLLCTRL4_REG, &valPll); + err |= fs16xx_write_register16(id, FS16XX_PLLCTRL4_REG, valPll | FS16XX_PLLCTRL4_REG_OSCEN); + dev_state->tempr_sel = preset_header->tempr_sel; + dev_state->spk_set = preset_header->spk_set; + dev_state->adp_bst = preset_header->adp_bst[i]; + dev_state->bst_ctrl = preset_header->bst_ctrl[i]; + dev_state->acs_drc_ctrl[0] = preset_header->acs_drc[i]; + dev_state->acs_drc_ctrl[1] = preset_header->acs_drcs[i]; + dev_state->acs_drc_ctrl[2] = preset_header->acs_drcp[i]; + // TODO: TS control is not set to register + dev_state->ts_ctrl = preset_header->ts_ctrl[i]; + + err |= fs16xx_write_register16(id, FS16XX_BFLCTRL_REG, preset_header->agc_ctrl[i]); + err |= fs16xx_write_register16(id, FS16XX_BFLSET_REG, preset_header->agc_thd[i]); + err |= fs16xx_write_register16(id, FS16XX_SQC_REG, preset_header->agc_para[i]); + err |= fs16xx_write_register16(id, FS16XX_AGC_REG, preset_header->agc_mg[i]); + err |= fs16xx_write_register16(id, FS16XX_DRPARA_REG, preset_header->dac_para1[i]); + err |= fs16xx_write_register16(id, FS16XX_ACSDRC_REG, dev_state->acs_drc_ctrl[0]); + err |= fs16xx_write_register16(id, FS16XX_ACSDRCS_REG, dev_state->acs_drc_ctrl[1]); + // acs drcp not used. + // err |= fs16xx_write_register16(id, FS16XX_ACSDRCP_REG, dev_state->acs_drc_ctrl[2]); + err |= fs16xx_write_register16(id, FS16XX_ADPBST_REG, dev_state->adp_bst); + + if(preset == 0) { + err |= fs16xx_write_register16(id, FS16XX_BSTCTRL_REG, dev_state->bst_ctrl); + valAcsCtrl = (uint16_t)FS16XX_ACSCTRL_REG_DEFAULT; + } else { + tmpC0 = dev_state->bst_ctrl & (~FS16XX_BSTCTRL_REG_MODE_CTRL_MSK); + tmpC0 |= (0x1 << FS16XX_BSTCTRL_REG_MODE_CTRL_POS); + err |= fs16xx_write_register16(id, FS16XX_BSTCTRL_REG, tmpC0); + valAcsCtrl = (uint16_t)(FS16XX_ACSCTRL_REG_DEFAULT | FS16XX_ACSCTRL_REG_ACS_COE_SEL_MSK); + } + + err |= fs16xx_write_register16(id, FS16XX_ACSCTRL_REG, valAcsCtrl); + dev_state->cur_preset = (int)preset; + + err |= fs16xx_write_register16(id, FS16XX_PLLCTRL4_REG, valPll); + } else { + PRINT_ERROR("%s failed to wait for dsp off!", __func__); + } + + err |= fs16xx_write_register16(id, FS16XX_SYSCTRL_REG, valPwr); + + PRINT("%s exit err=%d. __DEBUG__ boost 0x%04X", __func__, err, + dev_state->bst_ctrl); + return err; +} + diff --git a/asoc/codecs/foursemi/fs16xx_preset.h b/asoc/codecs/foursemi/fs16xx_preset.h new file mode 100644 index 000000000000..870dbfaba1ce --- /dev/null +++ b/asoc/codecs/foursemi/fs16xx_preset.h @@ -0,0 +1,99 @@ +/* +* Copyright (C) 2016 Fourier Semiconductor Inc. +* +*/ + +#ifndef FS16XX_PRESET_H +#define FS16XX_PRESET_H + +#include "fs16xx_define.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PRESET_NAME_SIZE 256 + +// Preset version for 1801 series +#define PRESET_VERSION 0x0A01 + +// Size in int32 +#define EXCUR_PROT_RAM_LEN 0x39 + +#pragma pack(push, 1) +struct fs16xx_preset_header { + uint16_t preset_ver; /* Preset version, should be PRESET_VERSION */ + char customer_name[32]; /* Customer info */ + int64_t create_time; /* Time tick when the preset created */ + uint16_t data_len;/* Length of the data in int16_t */ + uint16_t crc16; /* CRC check sum of the data */ + uint16_t tmax; /* speaker max temprature */ + uint16_t tempr_coef; /* speaker temperature coef */ + uint16_t tempr_sel; /* default value of reg TEMPSEL (0x08)*/ + uint16_t spk_set; /* default value of reg SPKSET (0x0A)*/ + uint16_t adp_bst[2]; /* default value of reg ADPBST (0xBF)*/ + uint16_t bst_ctrl[2]; /* default value of reg BSTCTRL (0xC0)*/ + uint16_t agc_ctrl[2]; /* default value of reg AGCCTRL (0xA7)*/ + uint16_t agc_thd[2]; /* default value of reg AGCTHD (0xA8)*/ + uint16_t agc_para[2]; /* default value of reg AGCPARA (0xA9)*/ + uint16_t agc_mg[2]; /* default value of reg AGCMG (0xAA)*/ + uint16_t dac_para1[2]; /* default value of reg DACPARA1 (0xCD)*/ + uint16_t acs_drc[2]; /* default value of reg ACSDRC (0x8A)*/ + uint16_t acs_drcs[2]; /* default value of reg ACSDRCS (0x8B)*/ + uint16_t acs_drcp[2]; /* default value of reg ACSDRCP (0x8C)*/ + uint16_t ts_ctrl[2]; /* default value of reg TSCTRL (0xAF)*/ + int32_t *excur_prot_ram; /* excur prot coef ram */ + uint16_t *data; /* Point to coef ram data */ +}; +typedef struct fs16xx_preset_header Fs16xx_preset_header_t; + +#define FS_PRESET_HEADER_LEN (sizeof(Fs16xx_preset_header_t) - sizeof(uint16_t *)) + +enum _fs16xx_preset_type { + fs16xx_preset_left = 0, + fs16xx_preset_right = 1, + fs16xx_preset_type_max = 2 // Set to count of preset types +}; +typedef enum _fs16xx_preset_type fs16xx_preset_type; + +struct fs16xx_preset_content { + int valid; + Fs16xx_preset_header_t preset_header; +}; +typedef struct fs16xx_preset_content Fs16xx_preset; +#pragma pack(pop) +/** + * Fs16xx initialize presets + */ +Fs16xx_Error_t fs16xx_initialize_presets(void); + +/** + * Fs16xx deinitialize presets + */ +Fs16xx_Error_t fs16xx_deinitialize_presets(void); + +/** + * Fs16xx load presets from storage + */ +Fs16xx_Error_t fs16xx_load_presets(Fs16xx_devId_t id); + +/** + * Fs16xx load presets to fs16xx device + */ +Fs16xx_Error_t fs16xx_load_presets_to_dev(Fs16xx_devId_t id); + +uint16_t fs16xx_calc_checksum(uint16_t *data, int len); + +/** + * Fs16xx set dsp preset + * @param id to opened instance + * @param preset to set in dsp + */ +Fs16xx_Error_t fs16xx_set_preset(Fs16xx_devId_t id, int preset); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/asoc/codecs/foursemi/fs16xx_reg_def.h b/asoc/codecs/foursemi/fs16xx_reg_def.h new file mode 100644 index 000000000000..937c7ff84af0 --- /dev/null +++ b/asoc/codecs/foursemi/fs16xx_reg_def.h @@ -0,0 +1,81 @@ +/* +* Copyright (C) 2016 Fourier Semiconductor Inc. +* +*/ + +#ifndef FS16XX_REG_DEF_H +#define FS16XX_REG_DEF_H + +#include "fs16xx_regs.h" +#include "fs16xx.h" + +/* I2S_CONTROL bits for sample rates */ +#define FS16XX_I2SCTRL_RATE_08000 (0<