diff --git a/techpack/audio/asoc/Kbuild b/techpack/audio/asoc/Kbuild index 42bb0ef77105..f256c31f58da 100644 --- a/techpack/audio/asoc/Kbuild +++ b/techpack/audio/asoc/Kbuild @@ -261,6 +261,18 @@ INCS += $(COMMON_INC) \ EXTRA_CFLAGS += $(INCS) +ifeq ($(CONFIG_AW882XX_STEREO_SMARTPA), y) + EXTRA_CFLAGS += -DCONFIG_AW882XX_STEREO_SMARTPA +endif + +ifeq ($(CONFIG_AW882XX_STEREO_SMARTPA_PRI), y) + EXTRA_CFLAGS += -DCONFIG_AW882XX_STEREO_SMARTPA_PRI +endif + +ifeq ($(CONFIG_SND_SOC_AW8838), y) + EXTRA_CFLAGS += -DCONFIG_SND_SOC_AW8838 +endif + CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ -DANI_LITTLE_BIT_ENDIAN \ -DDOT11F_LITTLE_ENDIAN_HOST \ @@ -271,6 +283,14 @@ CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ -Werror\ -D__linux__ +ifdef CONFIG_SND_SOC_AW87XXX +CDEFINES += -DCONFIG_SND_SOC_AW87XXX +endif + +ifdef CONFIG_SND_SOC_FS1815 +CDEFINES += -DCONFIG_SND_SOC_FS1815 +endif + KBUILD_CPPFLAGS += $(CDEFINES) # Currently, for versions of gcc which support it, the kernel Makefile @@ -291,6 +311,7 @@ KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/wcd934x/Module.symvers KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/wcd937x/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/aw8838/Module.symvers endif ifeq ($(KERNEL_BUILD), 1) obj-y += codecs/ diff --git a/techpack/audio/asoc/codecs/Kbuild b/techpack/audio/asoc/codecs/Kbuild index 711d5bcab690..712cb0ddd519 100755 --- a/techpack/audio/asoc/codecs/Kbuild +++ b/techpack/audio/asoc/codecs/Kbuild @@ -274,6 +274,13 @@ ifeq ($(KERNEL_BUILD), 1) obj-y += bolero/ obj-y += wsa883x/ obj-y += rouleur/ + obj-y += aw882xx/ + obj-y += aw882xxacf/ + obj-y += aw87xxx/ + obj-y += fs1815/ +ifeq ($(CONFIG_SND_SOC_AW8838), y) + obj-y += aw8838/ +endif endif # Module information used by KBuild framework obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd_core_dlkm.o diff --git a/techpack/audio/asoc/codecs/aw87xxx/Kbuild b/techpack/audio/asoc/codecs/aw87xxx/Kbuild new file mode 100644 index 000000000000..d7ab1e501a9b --- /dev/null +++ b/techpack/audio/asoc/codecs/aw87xxx/Kbuild @@ -0,0 +1,16 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/kernel/msm-5.4/techpack/audio/include +EXTRA_CFLAGS += -I$(TOP)/kernel/msm-5.4/techpack/audio/include/uapi/ + +ifdef CONFIG_SND_SOC_AW87XXX +AW87XXX_OBJS += aw87xxx.o +AW87XXX_OBJS += aw87xxx_device.o +AW87XXX_OBJS += aw87xxx_monitor.o +AW87XXX_OBJS += aw87xxx_bin_parse.o +AW87XXX_OBJS += aw87xxx_dsp.o +AW87XXX_OBJS += aw87xxx_acf_bin.o +endif + +obj-$(CONFIG_SND_SOC_AW87XXX) += aw87xxx_dlkm.o +aw87xxx_dlkm-y := $(AW87XXX_OBJS) diff --git a/techpack/audio/asoc/codecs/aw87xxx/aw87xxx.c b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx.c new file mode 100644 index 000000000000..137bd2e02b9f --- /dev/null +++ b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx.c @@ -0,0 +1,1870 @@ +/* + * aw87xxx.c aw87xxx pa module + * + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + * Author: Barry + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw87xxx.h" +#include "aw87xxx_device.h" +#include "aw87xxx_log.h" +#include "aw87xxx_monitor.h" +#include "aw87xxx_acf_bin.h" +#include "aw87xxx_bin_parse.h" +#include "aw87xxx_dsp.h" + +/***************************************************************** +* aw87xxx marco +******************************************************************/ +#define AW87XXX_I2C_NAME "aw87xxx_pa" +#define AW87XXX_DRIVER_VERSION "v2.10.0" +#define AW87XXX_FW_BIN_NAME "aw87xxx_acf.bin" + +/************************************************************************* + * aw87xxx variable + ************************************************************************/ +static LIST_HEAD(g_aw87xxx_list); +static DEFINE_MUTEX(g_aw87xxx_mutex_lock); +unsigned int g_aw87xxx_dev_cnt; + +#ifdef AW87XXX_RUNIN_TEST +static unsigned int g_runin_test; +#endif + +static unsigned int g_saved_spin_value; +static unsigned int g_spin_enable; + +static const char *const aw87xxx_monitor_switch[] = {"Disable", "Enable"}; +static const char *const aw87xxx_spin_switch[] = {"spin_0", "spin_90", + "spin_180", "spin_270"}; +static const char *const aw87xxx_spin_status[] = {"Off", "On"}; + +static const char *const aw87xxx_runin_test_switch[] = {"Off", "On"}; + +#ifdef AW_KERNEL_VER_OVER_4_19_1 +static struct aw_componet_codec_ops aw_componet_codec_ops = { + .add_codec_controls = snd_soc_add_component_controls, + .unregister_codec = snd_soc_unregister_component, +}; +#else +static struct aw_componet_codec_ops aw_componet_codec_ops = { + .add_codec_controls = snd_soc_add_codec_controls, + .unregister_codec = snd_soc_unregister_codec, +}; +#endif + + +/************************************************************************ + * + * aw87xxx device update profile + * + ************************************************************************/ +static void aw87xxx_update_voltage_max(struct aw87xxx *aw87xxx, + struct aw_data_container *data_container) +{ + int i = 0; + struct aw_voltage_desc *vol_desc = &aw87xxx->aw_dev.vol_desc; + + if (data_container == NULL || data_container->len <= 2 + || vol_desc->addr == AW_REG_NONE) + return; + + for (i = 0; i < data_container->len; i = i + 2) { + if (data_container->data[i] == vol_desc->addr) { + vol_desc->vol_max = data_container->data[i + 1]; + AW_DEV_LOGD(aw87xxx->dev, "get voltage max = 0x%02x", + data_container->data[i + 1]); + return; + } + } +} + +static int aw87xxx_power_down(struct aw87xxx *aw87xxx, char *profile) +{ + int ret = 0; + struct aw_prof_desc *prof_desc = NULL; + struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; + struct aw_data_container *data_container = NULL; + struct aw_device *aw_dev = &aw87xxx->aw_dev; + + AW_DEV_LOGD(aw87xxx->dev, "enter"); + + if (!prof_info->status) { + AW_DEV_LOGE(aw87xxx->dev, "profile_cfg not load"); + return -EINVAL; + } + + prof_desc = aw87xxx_acf_get_prof_desc_form_name(aw87xxx->dev, &aw87xxx->acf_info, profile); + if (prof_desc == NULL) + goto no_bin_pwr_off; + + if (!prof_desc->prof_st) + goto no_bin_pwr_off; + + + data_container = &prof_desc->data_container; + AW_DEV_LOGD(aw87xxx->dev, "get profile[%s] data len [%d]", + profile, data_container->len); + + if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { + AW_DEV_LOGI(aw87xxx->dev, "profile[%s] has already load ", profile); + } else { + if (aw_dev->ops.pwr_off_func) { + ret = aw_dev->ops.pwr_off_func(aw_dev, data_container); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", profile); + goto pwr_off_failed; + } + } else { + ret = aw87xxx_dev_default_pwr_off(aw_dev, data_container); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", profile); + goto pwr_off_failed; + } + } + } + + aw87xxx->current_profile = prof_desc->prof_name; + return 0; + +pwr_off_failed: +no_bin_pwr_off: + aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); + aw87xxx->current_profile = aw87xxx->prof_off_name; + return ret; +} + +static int aw87xxx_power_on(struct aw87xxx *aw87xxx, char *profile) +{ + int ret = -EINVAL; + struct aw_prof_desc *prof_desc = NULL; + struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; + struct aw_data_container *data_container = NULL; + struct aw_device *aw_dev = &aw87xxx->aw_dev; + + AW_DEV_LOGD(aw87xxx->dev, "enter"); + + if (!prof_info->status) { + AW_DEV_LOGE(aw87xxx->dev, "profile_cfg not load"); + return -EINVAL; + } + + if (0 == strncmp(profile, aw87xxx->prof_off_name, AW_PROFILE_STR_MAX)) + return aw87xxx_power_down(aw87xxx, profile); + + prof_desc = aw87xxx_acf_get_prof_desc_form_name(aw87xxx->dev, &aw87xxx->acf_info, profile); + if (prof_desc == NULL) { + AW_DEV_LOGE(aw87xxx->dev, "not found [%s] parameter", profile); + return -EINVAL; + } + + if (!prof_desc->prof_st) { + AW_DEV_LOGE(aw87xxx->dev, "not found data container"); + return -EINVAL; + } + + data_container = &prof_desc->data_container; + AW_DEV_LOGD(aw87xxx->dev, "get profile[%s] data len [%d]", + profile, data_container->len); + aw87xxx_update_voltage_max(aw87xxx, data_container); + if (aw_dev->ops.pwr_on_func) { + ret = aw_dev->ops.pwr_on_func(aw_dev, data_container); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", + profile); + return aw87xxx_power_down(aw87xxx, aw87xxx->prof_off_name); + } + } else { + ret = aw87xxx_dev_default_pwr_on(aw_dev, data_container); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", + profile); + return aw87xxx_power_down(aw87xxx, aw87xxx->prof_off_name); + } + } + + aw87xxx->current_profile = prof_desc->prof_name; + AW_DEV_LOGD(aw87xxx->dev, "load profile[%s] succeed", profile); + + return 0; +} + + + +int aw87xxx_update_profile(struct aw87xxx *aw87xxx, char *profile) +{ + int ret = -1; + + AW_DEV_LOGD(aw87xxx->dev, "load profile[%s] enter", profile); + mutex_lock(&aw87xxx->reg_lock); + aw87xxx_monitor_stop(&aw87xxx->monitor); + if (0 == strncmp(profile, aw87xxx->prof_off_name, AW_PROFILE_STR_MAX)) { + ret = aw87xxx_power_down(aw87xxx, profile); + } else { + ret = aw87xxx_power_down(aw87xxx, aw87xxx->prof_off_name); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed!", aw87xxx->prof_off_name); + mutex_unlock(&aw87xxx->reg_lock); + return ret; + } + + ret = aw87xxx_power_on(aw87xxx, profile); + if (!ret) + aw87xxx_monitor_start(&aw87xxx->monitor); +#ifdef AW87XXX_RUNIN_TEST + schedule_delayed_work(&aw87xxx->adsp_status, msecs_to_jiffies(500)); +#endif + schedule_delayed_work(&aw87xxx->spin_status, msecs_to_jiffies(100)); + } + mutex_unlock(&aw87xxx->reg_lock); + + return ret; +} + +int aw87xxx_update_profile_esd(struct aw87xxx *aw87xxx, char *profile) +{ + int ret = -1; + + if (0 == strncmp(profile, aw87xxx->prof_off_name, AW_PROFILE_STR_MAX)) + ret = aw87xxx_power_down(aw87xxx, profile); + else { + aw87xxx_dev_soft_reset(&aw87xxx->aw_dev); + msleep(2); + ret = aw87xxx_power_on(aw87xxx, profile); + } + return ret; +} + +char *aw87xxx_show_current_profile(int dev_index) +{ + struct list_head *pos = NULL; + struct aw87xxx *aw87xxx = NULL; + + list_for_each(pos, &g_aw87xxx_list) { + aw87xxx = list_entry(pos, struct aw87xxx, list); + if (aw87xxx->dev_index == dev_index) { + AW_DEV_LOGI(aw87xxx->dev, "current profile is [%s]", + aw87xxx->current_profile); + return aw87xxx->current_profile; + } + } + + AW_LOGE("not found struct aw87xxx, dev_index = [%d]", dev_index); + return NULL; +} +EXPORT_SYMBOL(aw87xxx_show_current_profile); + +int aw87xxx_set_profile(int dev_index, char *profile) +{ + struct list_head *pos = NULL; + struct aw87xxx *aw87xxx = NULL; + + if (!profile) { + AW_LOGE("profile is NULL"); + return -EINVAL; + } + + list_for_each(pos, &g_aw87xxx_list) { + aw87xxx = list_entry(pos, struct aw87xxx, list); + if (aw87xxx->dev_index == dev_index) { + AW_DEV_LOGD(aw87xxx->dev, "set dev_index = %d, profile = %s", + dev_index, profile); + return aw87xxx_update_profile(aw87xxx, profile); + } + } + + AW_LOGE("not found struct aw87xxx, dev_index = [%d]", dev_index); + return -EINVAL; +} +EXPORT_SYMBOL(aw87xxx_set_profile); + +int aw87xxx_set_profile_by_id(int dev_index, int profile_id) +{ + char *profile = NULL; + + profile = aw87xxx_ctos_get_prof_name(profile_id); + if (profile == NULL) { + AW_LOGE("aw87xxx, dev_index[%d] profile[%d] not support!", + dev_index, profile_id); + return -EINVAL; + } + + AW_LOGI("aw87xxx, dev_index[%d] set profile[%s] by id[%d]", + dev_index, profile, profile_id); + return aw87xxx_set_profile(dev_index, profile); +} +EXPORT_SYMBOL(aw87xxx_set_profile_by_id); + +int aw87xxx_set_boost_voltage(int dev_index, int status) +{ + int ret = 0; + struct list_head *pos = NULL; + struct aw87xxx *aw87xxx = NULL; + struct aw_voltage_desc *vol_desc = NULL; + + list_for_each(pos, &g_aw87xxx_list) { + aw87xxx = list_entry(pos, struct aw87xxx, list); + if (aw87xxx == NULL) { + AW_LOGE("struct aw87xxx not ready"); + return -EINVAL; + } + + if (aw87xxx->dev_index == dev_index) { + AW_DEV_LOGD(aw87xxx->dev, "enter, set dev_index = %d, status = %d", dev_index, status); + if (0 == strncmp(aw87xxx->current_profile, aw87xxx->prof_off_name, + AW_PROFILE_STR_MAX)) { + AW_DEV_LOGE(aw87xxx->dev, "PA is power down, can not set voltage!"); + return -EINVAL; + } + + vol_desc = &aw87xxx->aw_dev.vol_desc; + if (vol_desc->addr == AW_REG_NONE) { + AW_DEV_LOGI(aw87xxx->dev, "PA unsupport set boost voltage!"); + return -EINVAL; + } + if (status == AW_VOLTAGE_HIGH) { + AW_DEV_LOGI(aw87xxx->dev, "set reg voltage max: 0x%02x = 0x%02x", + vol_desc->addr, vol_desc->vol_max); + ret = aw87xxx_dev_i2c_write_byte(&aw87xxx->aw_dev, + vol_desc->addr, vol_desc->vol_max); + if (ret < 0) + return ret; + } else if (status == AW_VOLTAGE_LOW) { + AW_DEV_LOGI(aw87xxx->dev, "set reg voltage min: 0x%02x = 0x%02x", + vol_desc->addr, vol_desc->vol_min); + ret = aw87xxx_dev_i2c_write_byte(&aw87xxx->aw_dev, + vol_desc->addr, vol_desc->vol_min); + if (ret < 0) + return ret; + } else { + AW_DEV_LOGE(aw87xxx->dev, "unsupport status: %d", status); + return -EINVAL; + } + + return 0; + } + } + + AW_LOGE("not found struct aw87xxx, dev_index = [%d]", dev_index); + return -EINVAL; +} +EXPORT_SYMBOL(aw87xxx_set_boost_voltage); + +int aw87xxx_get_boost_voltage(int dev_index, int *status) +{ + int ret = 0; + struct list_head *pos = NULL; + struct aw87xxx *aw87xxx = NULL; + uint8_t reg_voltage_val = 0; + struct aw_voltage_desc *vol_desc = NULL; + + list_for_each(pos, &g_aw87xxx_list) { + aw87xxx = list_entry(pos, struct aw87xxx, list); + if (aw87xxx == NULL) { + AW_LOGE("struct aw87xxx not ready"); + return -EINVAL; + } + + if (aw87xxx->dev_index == dev_index) { + if (0 == strncmp(aw87xxx->current_profile, aw87xxx->prof_off_name, + AW_PROFILE_STR_MAX)) { + AW_DEV_LOGE(aw87xxx->dev, "PA is power down, can not get voltage!"); + return -EINVAL; + } + + vol_desc = &aw87xxx->aw_dev.vol_desc; + if (vol_desc->addr == AW_REG_NONE) { + AW_DEV_LOGI(aw87xxx->dev, "PA unsupport get boost voltage!"); + return -EINVAL; + } + ret = aw87xxx_dev_i2c_read_byte(&aw87xxx->aw_dev, + vol_desc->addr, ®_voltage_val); + if (ret < 0) + return ret; + + if (reg_voltage_val == vol_desc->vol_max) { + *status = AW_VOLTAGE_HIGH; + } else if (reg_voltage_val == vol_desc->vol_min) { + *status = AW_VOLTAGE_LOW; + } else { + AW_DEV_LOGE(aw87xxx->dev, "undefined reg val 0x%02x = 0x%02x", + vol_desc->addr, reg_voltage_val); + return -EINVAL; + } + + return 0; + } + } + + AW_LOGE("not found struct aw87xxx, dev_index = [%d]", dev_index); + return -EINVAL; +} +EXPORT_SYMBOL(aw87xxx_get_boost_voltage); + +/**************************************************************************** + * + * aw87xxx Kcontrols + * + ****************************************************************************/ +static int aw87xxx_profile_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count = 0; + char *name = NULL; + char *profile_name = NULL; + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + + if (aw87xxx == NULL) { + AW_LOGE("get struct aw87xxx failed"); + return -EINVAL; + } + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + /*make sure have prof */ + count = aw87xxx_acf_get_profile_count(aw87xxx->dev, &aw87xxx->acf_info); + if (count <= 0) { + uinfo->value.enumerated.items = 0; + AW_DEV_LOGE(aw87xxx->dev, "get count[%d] failed", count); + return 0; + } + + uinfo->value.enumerated.items = count; + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + name = uinfo->value.enumerated.name; + count = uinfo->value.enumerated.item; + profile_name = aw87xxx_acf_get_prof_name_form_index(aw87xxx->dev, + &aw87xxx->acf_info, count); + if (profile_name == NULL) { + strlcpy(uinfo->value.enumerated.name, "NULL", + strlen("NULL") + 1); + return 0; + } + + strlcpy(name, profile_name, sizeof(uinfo->value.enumerated.name)); + + return 0; +} + +static int aw87xxx_profile_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -1; + char *profile_name = NULL; + int index = ucontrol->value.integer.value[0]; + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + struct acf_bin_info *acf_info = NULL; + + if (aw87xxx == NULL) { + AW_LOGE("get struct aw87xxx failed"); + return -EINVAL; + } + + acf_info = &aw87xxx->acf_info; + + profile_name = aw87xxx_acf_get_prof_name_form_index(aw87xxx->dev, acf_info, index); + if (!profile_name) { + AW_DEV_LOGE(aw87xxx->dev, "not found profile name,index=[%d]", + index); + return -EINVAL; + } + + AW_DEV_LOGI(aw87xxx->dev, "set profile [%s]", profile_name); + + ret = aw87xxx_update_profile(aw87xxx, profile_name); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "set dev_index[%d] profile failed, profile = %s", + aw87xxx->dev_index, profile_name); + return ret; + } + + return 0; +} + +static int aw87xxx_profile_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int index = 0; + char *profile; + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + + if (aw87xxx == NULL) { + AW_LOGE("get struct aw87xxx failed"); + return -EINVAL; + } + + if (!aw87xxx->current_profile) { + AW_DEV_LOGE(aw87xxx->dev, "profile not init"); + return -EINVAL; + } + + profile = aw87xxx->current_profile; + AW_DEV_LOGI(aw87xxx->dev, "current profile:[%s]", + aw87xxx->current_profile); + + + index = aw87xxx_acf_get_prof_index_form_name(aw87xxx->dev, + &aw87xxx->acf_info, aw87xxx->current_profile); + if (index < 0) { + AW_DEV_LOGE(aw87xxx->dev, "get profile index failed"); + return index; + } + + ucontrol->value.integer.value[0] = index; + + return 0; +} + +static int aw87xxx_vmax_get_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = INT_MIN; + uinfo->value.integer.max = AW_VMAX_MAX; + + return 0; +} + +static int aw87xxx_vmax_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -1; + int vmax_val = 0; + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + + if (aw87xxx == NULL) { + AW_LOGE("get struct aw87xxx failed"); + return -EINVAL; + } + + ret = aw87xxx_monitor_no_dsp_get_vmax(&aw87xxx->monitor, &vmax_val); + if (ret < 0) + return ret; + + ucontrol->value.integer.value[0] = vmax_val; + AW_DEV_LOGI(aw87xxx->dev, "get vmax = [0x%x]", vmax_val); + + return 0; +} + +static int aw87xxx_monitor_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + count = ARRAY_SIZE(aw87xxx_monitor_switch); + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + strlcpy(uinfo->value.enumerated.name, + aw87xxx_monitor_switch[uinfo->value.enumerated.item], + strlen(aw87xxx_monitor_switch[uinfo->value.enumerated.item]) + 1); + + return 0; +} + +static int aw87xxx_monitor_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t ctrl_value = ucontrol->value.integer.value[0]; + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + struct aw_monitor *aw_monitor = &aw87xxx->monitor; + int ret = -1; + + ret = aw87xxx_dev_monitor_switch_set(aw_monitor, ctrl_value); + if (ret) + return ret; + + return 0; +} + +static int aw87xxx_monitor_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + struct aw_monitor *aw_monitor = &aw87xxx->monitor; + + ucontrol->value.integer.value[0] = aw_monitor->monitor_hdr.monitor_switch; + + AW_DEV_LOGD(aw87xxx->dev, "monitor switch is %ld", ucontrol->value.integer.value[0]); + return 0; +} + +static int aw87xxx_spin_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + count = ARRAY_SIZE(aw87xxx_spin_switch); + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + strlcpy(uinfo->value.enumerated.name, + aw87xxx_spin_switch[uinfo->value.enumerated.item], + strlen(aw87xxx_spin_switch[uinfo->value.enumerated.item]) + 1); + + return 0; +} + +static int aw87xxx_spin_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t ctrl_value = 0; + int ret = 0; + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + ctrl_value = ucontrol->value.integer.value[0]; + + g_saved_spin_value = ctrl_value; + + //don't send status to adsp until it's enabled when speaker on + if (!g_spin_enable) + return 0; + + ret = aw87xxx_dsp_set_spin(ctrl_value); + if (ret) { + AW_DEV_LOGE(aw87xxx->dev, "write spin failed"); + return ret; + } + AW_DEV_LOGD(aw87xxx->dev, "write spin done ctrl_value=%d", ctrl_value); + return 0; +} + +static int aw87xxx_spin_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + + if (!g_spin_enable){ + ucontrol->value.integer.value[0] = g_saved_spin_value; + return 0; + } + + ucontrol->value.integer.value[0] = aw87xxx_dsp_get_spin(); + AW_DEV_LOGD(aw87xxx->dev, "current spin is %ld", ucontrol->value.integer.value[0]); + + return 0; +} + +static int aw87xxx_hal_monitor_time_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = AW_MONITOR_TIME_MIN; + uinfo->value.integer.max = AW_MONITOR_TIME_MAX; + + return 0; +} + +static int aw87xxx_hal_monitor_time_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + + if (aw87xxx == NULL) { + AW_LOGE("get struct aw87xxx failed"); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = + aw87xxx->monitor.monitor_hdr.monitor_time; + + AW_LOGI("get monitor time %ld", ucontrol->value.integer.value[0]); + + return 0; +} + +static int aw87xxx_spin_status_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + count = ARRAY_SIZE(aw87xxx_spin_status); + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + strlcpy(uinfo->value.enumerated.name, + aw87xxx_spin_status[uinfo->value.enumerated.item], + strlen(aw87xxx_spin_status[uinfo->value.enumerated.item]) + 1); + + return 0; +} + +static int aw87xxx_spin_status_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t ctrl_value = 0; + //int ret = 0; + //struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + ctrl_value = ucontrol->value.integer.value[0]; + + g_spin_enable = ctrl_value; + + return 0; +} + +static int aw87xxx_spin_status_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = g_spin_enable; + AW_DEV_LOGD(aw87xxx->dev, "current spin status enable is %ld", ucontrol->value.integer.value[0]); + + return 0; +} + +// delayed work to update spin value +static void aw87xxx_set_spin_status(struct work_struct *work) +{ + int ret = 0; + AW_LOGI("g_saved_spin_value = %d, g_spin_enable = %d", g_saved_spin_value, g_spin_enable); + + if (!g_spin_enable) + return; + + ret = aw87xxx_dsp_set_spin(g_saved_spin_value); + if (ret) { + AW_LOGE("write spin failed"); + } + AW_LOGD("write spin done g_saved_spin_value=%d", g_saved_spin_value); + + return; +} + +static int aw87xxx_runin_test_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = g_runin_test; + AW_DEV_LOGI(aw87xxx->dev, "aw87xxx_runin test get val %d", g_runin_test); + + return 0; +} + +static int aw87xxx_runin_test_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t ctrl_value = 0; + struct aw87xxx *aw87xxx = (struct aw87xxx *)kcontrol->private_value; + ctrl_value = ucontrol->value.integer.value[0]; + + if (ctrl_value > 1 ) { + ctrl_value = 0; + } + + g_runin_test = ctrl_value; + + AW_DEV_LOGI(aw87xxx->dev, "aw87xxx_runin test set val %d", ctrl_value); + + return 0; +} + +static int aw87xxx_runin_test_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + count = ARRAY_SIZE(aw87xxx_runin_test_switch); + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + strlcpy(uinfo->value.enumerated.name, + aw87xxx_runin_test_switch[uinfo->value.enumerated.item], + strlen(aw87xxx_runin_test_switch[uinfo->value.enumerated.item]) + 1); + + return 0; +} + +static void aw87xxx_set_adsp_module_status(struct work_struct *work) +{ + int ret = 0; + uint32_t ctrl_value = 0; + AW_LOGI("g_runin_test = %d", g_runin_test); + + /*no test reg set default value*/ + if (g_runin_test == 0) { + return; + } + + /*set afe rx module disable*/ + ret = aw87xxx_dsp_set_rx_module_enable(ctrl_value); + if (ret) { + AW_LOGE("disable afe rx module %d falied , ret=%d", ctrl_value, ret); + return; + } + AW_LOGI("disable afe module success"); + + /*set skt module disable*/ + ret = aw87xxx_dsp_set_copp_module_enable(ctrl_value); + if (ret) { + AW_LOGE("disable skt module %d falied , ret=%d\n", ctrl_value, ret); + return; + } + AW_LOGI("disable skt module success"); + +} + +static int aw87xxx_kcontrol_dynamic_create(struct aw87xxx *aw87xxx, + void *codec) +{ + struct snd_kcontrol_new *aw87xxx_kcontrol = NULL; + aw_snd_soc_codec_t *soc_codec = (aw_snd_soc_codec_t *)codec; + char *kctl_name[AW87XXX_PRIVATE_KCONTROL_NUM]; + int kcontrol_num = AW87XXX_PRIVATE_KCONTROL_NUM; + int ret = -1; + + AW_DEV_LOGD(aw87xxx->dev, "enter"); + aw87xxx->codec = soc_codec; + + aw87xxx_kcontrol = devm_kzalloc(aw87xxx->dev, + sizeof(struct snd_kcontrol_new) * kcontrol_num, + GFP_KERNEL); + if (aw87xxx_kcontrol == NULL) { + AW_DEV_LOGE(aw87xxx->dev, "aw87xxx_kcontrol devm_kzalloc failed"); + return -ENOMEM; + } + + kctl_name[0] = devm_kzalloc(aw87xxx->dev, AW_NAME_BUF_MAX, + GFP_KERNEL); + if (kctl_name[0] == NULL) + return -ENOMEM; + + snprintf(kctl_name[0], AW_NAME_BUF_MAX, "aw87xxx_profile_switch_%d", + aw87xxx->dev_index); + + aw87xxx_kcontrol[0].name = kctl_name[0]; + aw87xxx_kcontrol[0].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw87xxx_kcontrol[0].info = aw87xxx_profile_switch_info; + aw87xxx_kcontrol[0].get = aw87xxx_profile_switch_get; + aw87xxx_kcontrol[0].put = aw87xxx_profile_switch_put; + aw87xxx_kcontrol[0].private_value = (unsigned long)aw87xxx; + + kctl_name[1] = devm_kzalloc(aw87xxx->codec->dev, AW_NAME_BUF_MAX, + GFP_KERNEL); + if (kctl_name[1] == NULL) + return -ENOMEM; + + snprintf(kctl_name[1], AW_NAME_BUF_MAX, "aw87xxx_vmax_get_%d", + aw87xxx->dev_index); + + aw87xxx_kcontrol[1].name = kctl_name[1]; + aw87xxx_kcontrol[1].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw87xxx_kcontrol[1].access = SNDRV_CTL_ELEM_ACCESS_READ; + aw87xxx_kcontrol[1].info = aw87xxx_vmax_get_info; + aw87xxx_kcontrol[1].get = aw87xxx_vmax_get; + aw87xxx_kcontrol[1].private_value = (unsigned long)aw87xxx; + + kctl_name[2] = devm_kzalloc(aw87xxx->codec->dev, AW_NAME_BUF_MAX, + GFP_KERNEL); + if (kctl_name[2] == NULL) + return -ENOMEM; + + snprintf(kctl_name[2], AW_NAME_BUF_MAX, "aw87xxx_monitor_switch_%d", + aw87xxx->dev_index); + + aw87xxx_kcontrol[2].name = kctl_name[2]; + aw87xxx_kcontrol[2].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw87xxx_kcontrol[2].info = aw87xxx_monitor_switch_info; + aw87xxx_kcontrol[2].get = aw87xxx_monitor_switch_get; + aw87xxx_kcontrol[2].put = aw87xxx_monitor_switch_put; + aw87xxx_kcontrol[2].private_value = (unsigned long)aw87xxx; + + ret = aw_componet_codec_ops.add_codec_controls(aw87xxx->codec, + aw87xxx_kcontrol, kcontrol_num); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "add codec controls failed, ret = %d", + ret); + return ret; + } + + AW_DEV_LOGI(aw87xxx->dev, "add codec controls[%s,%s,%s]", + aw87xxx_kcontrol[0].name, + aw87xxx_kcontrol[1].name, + aw87xxx_kcontrol[2].name); + + return 0; +} + +static int aw87xxx_public_kcontrol_create(struct aw87xxx *aw87xxx, + void *codec) +{ + struct snd_kcontrol_new *aw87xxx_kcontrol = NULL; + aw_snd_soc_codec_t *soc_codec = (aw_snd_soc_codec_t *)codec; + char *kctl_name[AW87XXX_PUBLIC_KCONTROL_NUM]; + int kcontrol_num = AW87XXX_PUBLIC_KCONTROL_NUM; + int ret = -1; + + AW_DEV_LOGD(aw87xxx->dev, "enter"); + aw87xxx->codec = soc_codec; + + aw87xxx_kcontrol = devm_kzalloc(aw87xxx->dev, + sizeof(struct snd_kcontrol_new) * kcontrol_num, + GFP_KERNEL); + if (aw87xxx_kcontrol == NULL) { + AW_DEV_LOGE(aw87xxx->dev, "aw87xxx_kcontrol devm_kzalloc failed"); + return -ENOMEM; + } + + kctl_name[0] = devm_kzalloc(aw87xxx->dev, AW_NAME_BUF_MAX, + GFP_KERNEL); + if (kctl_name[0] == NULL) + return -ENOMEM; + + snprintf(kctl_name[0], AW_NAME_BUF_MAX, "aw87xxx_spin_switch"); + + aw87xxx_kcontrol[0].name = kctl_name[0]; + aw87xxx_kcontrol[0].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw87xxx_kcontrol[0].info = aw87xxx_spin_switch_info; + aw87xxx_kcontrol[0].get = aw87xxx_spin_switch_get; + aw87xxx_kcontrol[0].put = aw87xxx_spin_switch_put; + aw87xxx_kcontrol[0].private_value = (unsigned long)aw87xxx; + + kctl_name[1] = devm_kzalloc(aw87xxx->dev, AW_NAME_BUF_MAX, + GFP_KERNEL); + if (kctl_name[1] == NULL) + return -ENOMEM; + + snprintf(kctl_name[1], AW_NAME_BUF_MAX, "aw87xxx_runin_test_switch"); + + aw87xxx_kcontrol[1].name = kctl_name[1]; + aw87xxx_kcontrol[1].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw87xxx_kcontrol[1].info = aw87xxx_runin_test_switch_info; + aw87xxx_kcontrol[1].get = aw87xxx_runin_test_switch_get; + aw87xxx_kcontrol[1].put = aw87xxx_runin_test_switch_put; + aw87xxx_kcontrol[1].private_value = (unsigned long)aw87xxx; + + kctl_name[2] = devm_kzalloc(aw87xxx->dev, AW_NAME_BUF_MAX, + GFP_KERNEL); + if (kctl_name[2] == NULL) + return -ENOMEM; + + snprintf(kctl_name[2], AW_NAME_BUF_MAX, "aw87xxx_spin_status"); + + aw87xxx_kcontrol[2].name = kctl_name[2]; + aw87xxx_kcontrol[2].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw87xxx_kcontrol[2].info = aw87xxx_spin_status_info; + aw87xxx_kcontrol[2].get = aw87xxx_spin_status_get; + aw87xxx_kcontrol[2].put = aw87xxx_spin_status_put; + aw87xxx_kcontrol[2].private_value = (unsigned long)aw87xxx; + + kctl_name[3] = devm_kzalloc(aw87xxx->dev, AW_NAME_BUF_MAX, + GFP_KERNEL); + if (kctl_name[3] == NULL) + return -ENOMEM; + + snprintf(kctl_name[3], AW_NAME_BUF_MAX, "aw87xxx_hal_monitor_time"); + + aw87xxx_kcontrol[3].name = kctl_name[3]; + aw87xxx_kcontrol[3].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw87xxx_kcontrol[3].access = SNDRV_CTL_ELEM_ACCESS_READ; + aw87xxx_kcontrol[3].info = aw87xxx_hal_monitor_time_info; + aw87xxx_kcontrol[3].get = aw87xxx_hal_monitor_time_get; + aw87xxx_kcontrol[3].private_value = (unsigned long)aw87xxx; + + ret = aw_componet_codec_ops.add_codec_controls(aw87xxx->codec, + aw87xxx_kcontrol, kcontrol_num); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "add codec controls failed, ret = %d", + ret); + return ret; + } + + AW_DEV_LOGI(aw87xxx->dev, "add public codec controls[%s]", + aw87xxx_kcontrol[0].name); + + return 0; +} + +/**************************************************************************** + * + *aw87xxx kcontrol create + * + ****************************************************************************/ +int aw87xxx_add_codec_controls(void *codec) +{ + struct list_head *pos = NULL; + struct aw87xxx *aw87xxx = NULL; + int ret = -1; + + list_for_each(pos, &g_aw87xxx_list) { + aw87xxx = list_entry(pos, struct aw87xxx, list); + ret = aw87xxx_kcontrol_dynamic_create(aw87xxx, codec); + if (ret < 0) + return ret; + + if (aw87xxx->dev_index == 0) { + ret = aw87xxx_public_kcontrol_create(aw87xxx, codec); + if (ret < 0) + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL(aw87xxx_add_codec_controls); + + +/**************************************************************************** + * + * aw87xxx firmware cfg load + * + ***************************************************************************/ +static void aw87xxx_fw_cfg_free(struct aw87xxx *aw87xxx) +{ + AW_DEV_LOGD(aw87xxx->dev, "enter"); + aw87xxx_acf_profile_free(aw87xxx->dev, &aw87xxx->acf_info); + aw87xxx_monitor_cfg_free(&aw87xxx->monitor); +} + +static int aw87xxx_init_default_prof(struct aw87xxx *aw87xxx) +{ + char *profile = NULL; + + profile = aw87xxx_acf_get_prof_off_name(aw87xxx->dev, &aw87xxx->acf_info); + if (profile == NULL) { + AW_DEV_LOGE(aw87xxx->dev, "get profile off name failed"); + return -EINVAL; + } + + snprintf(aw87xxx->prof_off_name, AW_PROFILE_STR_MAX, "%s", profile); + aw87xxx->current_profile = profile; + AW_DEV_LOGI(aw87xxx->dev, "init profile name [%s]", + aw87xxx->current_profile); + + return 0; +} + +static void aw87xxx_fw_load_work(struct work_struct *work) +{ + int ret = -1; + struct aw87xxx *aw87xxx = container_of(work, + struct aw87xxx, fw_load_work.work); + struct acf_bin_info *acf_info = &aw87xxx->acf_info; + const struct firmware *cont = NULL; + + AW_DEV_LOGD(aw87xxx->dev, "enter"); + + ret = request_firmware(&cont, aw87xxx->fw_name, aw87xxx->dev); + if ((ret) || (!cont)) { + AW_DEV_LOGD(aw87xxx->dev, "load [%s] failed!", aw87xxx->fw_name); + if (acf_info->load_count == AW_READ_CHIPID_RETRIES) { + acf_info->load_count = 0; + } else { + acf_info->load_count++; + /* sleep 1s */ + msleep(1000); + AW_DEV_LOGD(aw87xxx->dev, "load [%s] try [%d]!", + aw87xxx->fw_name, acf_info->load_count); + aw87xxx_fw_load_work(work); + } + return; + } + + AW_DEV_LOGD(aw87xxx->dev, "loaded %s - size: %zu", + aw87xxx->fw_name, (cont ? cont->size : 0)); + + mutex_lock(&aw87xxx->reg_lock); + acf_info->fw_data = vmalloc(cont->size); + if (!acf_info->fw_data) { + AW_DEV_LOGE(aw87xxx->dev, "fw_data kzalloc memory failed"); + goto exit_vmalloc_failed; + } + memset(acf_info->fw_data, 0, cont->size); + memcpy(acf_info->fw_data, cont->data, cont->size); + acf_info->fw_size = cont->size; + + ret = aw87xxx_acf_parse(aw87xxx->dev, &aw87xxx->acf_info); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "fw_data parse failed"); + goto exit_acf_parse_failed; + } + + ret = aw87xxx_init_default_prof(aw87xxx); + if (ret < 0) { + aw87xxx_fw_cfg_free(aw87xxx); + goto exit_acf_parse_failed; + } + AW_DEV_LOGI(aw87xxx->dev, "acf parse succeed"); + +exit_acf_parse_failed: +exit_vmalloc_failed: + mutex_unlock(&aw87xxx->reg_lock); + release_firmware(cont); +} + +static void aw87xxx_fw_load_init(struct aw87xxx *aw87xxx) +{ +#ifdef AW_CFG_UPDATE_DELAY + int cfg_timer_val = AW_CFG_UPDATE_DELAY_TIMER; +#else + int cfg_timer_val = 0; +#endif + AW_DEV_LOGI(aw87xxx->dev, "enter"); + snprintf(aw87xxx->fw_name, AW87XXX_FW_NAME_MAX, "%s", AW87XXX_FW_BIN_NAME); + aw87xxx_acf_init(&aw87xxx->aw_dev, &aw87xxx->acf_info, aw87xxx->dev_index); + + INIT_DELAYED_WORK(&aw87xxx->fw_load_work, aw87xxx_fw_load_work); + schedule_delayed_work(&aw87xxx->fw_load_work, + msecs_to_jiffies(cfg_timer_val)); +} + +/**************************************************************************** + * + *aw87xxx attribute node + * + ****************************************************************************/ +static ssize_t aw87xxx_attr_get_reg(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + int ret = 0; + unsigned int i = 0; + unsigned char reg_val = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_device *aw_dev = &aw87xxx->aw_dev; + + mutex_lock(&aw87xxx->reg_lock); + for (i = 0; i < aw_dev->reg_max_addr; i++) { + if (!(aw_dev->reg_access[i] & AW_DEV_REG_RD_ACCESS)) + continue; + ret = aw87xxx_dev_i2c_read_byte(&aw87xxx->aw_dev, i, ®_val); + if (ret < 0) { + len += snprintf(buf + len, PAGE_SIZE - len, + "read reg [0x%x] failed\n", i); + AW_DEV_LOGE(aw87xxx->dev, "read reg [0x%x] failed", i); + } else { + len += snprintf(buf + len, PAGE_SIZE - len, + "reg:0x%02X=0x%02X\n", i, reg_val); + AW_DEV_LOGD(aw87xxx->dev, "reg:0x%02X=0x%02X", + i, reg_val); + } + } + mutex_unlock(&aw87xxx->reg_lock); + + return len; +} + +static ssize_t aw87xxx_attr_set_reg(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t len) +{ + unsigned int databuf[2] = { 0 }; + int ret = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + + mutex_lock(&aw87xxx->reg_lock); + if (sscanf(buf, "0x%x 0x%x", &databuf[0], &databuf[1]) == 2) { + if (databuf[0] >= aw87xxx->aw_dev.reg_max_addr) { + AW_DEV_LOGE(aw87xxx->dev, "set reg[0x%x] error,is out of reg_addr_max[0x%x]", + databuf[0], aw87xxx->aw_dev.reg_max_addr); + mutex_unlock(&aw87xxx->reg_lock); + return -EINVAL; + } + + ret = aw87xxx_dev_i2c_write_byte(&aw87xxx->aw_dev, + databuf[0], databuf[1]); + if (ret < 0) + AW_DEV_LOGE(aw87xxx->dev, "set [0x%x]=0x%x failed", + databuf[0], databuf[1]); + else + AW_DEV_LOGD(aw87xxx->dev, "set [0x%x]=0x%x succeed", + databuf[0], databuf[1]); + } else { + AW_DEV_LOGE(aw87xxx->dev, "i2c write cmd input error"); + } + mutex_unlock(&aw87xxx->reg_lock); + + return len; +} + +static ssize_t aw87xxx_attr_get_profile(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + unsigned int i = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_prof_info *prof_info = &aw87xxx->acf_info.prof_info; + + if (!prof_info->status) { + len += snprintf(buf + len, PAGE_SIZE - len, + "profile_cfg not load\n"); + return len; + } + + AW_DEV_LOGI(aw87xxx->dev, "current profile:[%s]", aw87xxx->current_profile); + + for (i = 0; i < prof_info->count; i++) { + if (!strncmp(aw87xxx->current_profile, prof_info->prof_name_list[i], + AW_PROFILE_STR_MAX)) + len += snprintf(buf + len, PAGE_SIZE - len, + ">%s\n", prof_info->prof_name_list[i]); + else + len += snprintf(buf + len, PAGE_SIZE - len, + " %s\n", prof_info->prof_name_list[i]); + } + + return len; +} + +static ssize_t aw87xxx_attr_set_profile(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t len) +{ + char profile[AW_PROFILE_STR_MAX] = {0}; + int ret = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + + if (strlen(buf) > AW_PROFILE_STR_MAX) { + AW_DEV_LOGE(aw87xxx->dev, "input profile_str_len is out of max[%d]", + AW_PROFILE_STR_MAX); + return -EINVAL; + } + + if (sscanf(buf, "%s", profile) == 1) { + AW_DEV_LOGD(aw87xxx->dev, "set profile [%s]", profile); + ret = aw87xxx_update_profile(aw87xxx, profile); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "set profile[%s] failed", + profile); + return ret; + } + } + + return len; +} + +static ssize_t aw87xxx_attr_get_hwen(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + int hwen = aw87xxx->aw_dev.hwen_status; + + if (hwen >= AW_DEV_HWEN_INVALID) + len += snprintf(buf + len, PAGE_SIZE - len, "hwen_status: invalid\n"); + else if (hwen == AW_DEV_HWEN_ON) + len += snprintf(buf + len, PAGE_SIZE - len, "hwen_status: on\n"); + else if (hwen == AW_DEV_HWEN_OFF) + len += snprintf(buf + len, PAGE_SIZE - len, "hwen_status: off\n"); + + return len; +} + +static ssize_t aw87xxx_attr_set_hwen(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t len) +{ + int ret = -1; + unsigned int state; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + + ret = kstrtouint(buf, 0, &state); + if (ret) { + AW_DEV_LOGE(aw87xxx->dev, "fail to channelge str to int"); + return ret; + } + + mutex_lock(&aw87xxx->reg_lock); + if (state == AW_DEV_HWEN_OFF) + aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); /*OFF*/ + else if (state == AW_DEV_HWEN_ON) + aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, true); /*ON*/ + else + AW_DEV_LOGE(aw87xxx->dev, "input [%d] error, hwen_on=[%d],hwen_off=[%d]", + state, AW_DEV_HWEN_ON, AW_DEV_HWEN_OFF); + mutex_unlock(&aw87xxx->reg_lock); + return len; +} + +int aw87xxx_awrw_write(struct aw87xxx *aw87xxx, + const char *buf, size_t count) +{ + int i = 0, ret = -1; + char *data_buf = NULL; + int buf_len = 0; + int temp_data = 0; + int data_str_size = 0; + char *reg_data; + struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; + + AW_DEV_LOGD(aw87xxx->dev, "enter"); + /* one addr or one data string Composition of Contains two bytes of symbol(0X)*/ + /* and two byte of hexadecimal data*/ + data_str_size = 2 + 2 * AWRW_DATA_BYTES; + + /* The buf includes the first address of the register to be written and all data */ + buf_len = AWRW_ADDR_BYTES + packet->reg_num * AWRW_DATA_BYTES; + AW_DEV_LOGI(aw87xxx->dev, "buf_len = %d,reg_num = %d", buf_len, packet->reg_num); + data_buf = vmalloc(buf_len); + if (data_buf == NULL) { + AW_DEV_LOGE(aw87xxx->dev, "alloc memory failed"); + return -ENOMEM; + } + memset(data_buf, 0, buf_len); + + data_buf[0] = packet->reg_addr; + reg_data = data_buf + 1; + + AW_DEV_LOGD(aw87xxx->dev, "reg_addr: 0x%02x", data_buf[0]); + + /*ag:0x00 0x01 0x01 0x01 0x01 0x00\x0a*/ + for (i = 0; i < packet->reg_num; i++) { + ret = sscanf(buf + AWRW_HDR_LEN + 1 + i * (data_str_size + 1), + "0x%x", &temp_data); + if (ret != 1) { + AW_DEV_LOGE(aw87xxx->dev, "sscanf failed,ret=%d", ret); + vfree(data_buf); + data_buf = NULL; + return ret; + } + reg_data[i] = temp_data; + AW_DEV_LOGD(aw87xxx->dev, "[%d] : 0x%02x", i, reg_data[i]); + } + + mutex_lock(&aw87xxx->reg_lock); + ret = i2c_master_send(aw87xxx->aw_dev.i2c, data_buf, buf_len); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "write failed"); + vfree(data_buf); + data_buf = NULL; + return -EFAULT; + } + mutex_unlock(&aw87xxx->reg_lock); + + vfree(data_buf); + data_buf = NULL; + + AW_DEV_LOGD(aw87xxx->dev, "down"); + return 0; +} + +static int aw87xxx_awrw_data_check(struct aw87xxx *aw87xxx, + int *data, size_t count) +{ + struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; + int req_data_len = 0; + int act_data_len = 0; + int data_str_size = 0; + + if ((data[AWRW_HDR_ADDR_BYTES] != AWRW_ADDR_BYTES) || + (data[AWRW_HDR_DATA_BYTES] != AWRW_DATA_BYTES)) { + AW_DEV_LOGE(aw87xxx->dev, "addr_bytes [%d] or data_bytes [%d] unsupport", + data[AWRW_HDR_ADDR_BYTES], data[AWRW_HDR_DATA_BYTES]); + return -EINVAL; + } + + /* one data string Composition of Contains two bytes of symbol(0x)*/ + /* and two byte of hexadecimal data*/ + data_str_size = 2 + 2 * AWRW_DATA_BYTES; + act_data_len = count - AWRW_HDR_LEN - 1; + + /* There is a comma(,) or space between each piece of data */ + if (data[AWRW_HDR_WR_FLAG] == AWRW_FLAG_WRITE) { + /*ag:0x00 0x01 0x01 0x01 0x01 0x00\x0a*/ + req_data_len = (data_str_size + 1) * packet->reg_num; + if (req_data_len > act_data_len) { + AW_DEV_LOGE(aw87xxx->dev, "data_len checkfailed,requeset data_len [%d],actaul data_len [%d]", + req_data_len, act_data_len); + return -EINVAL; + } + } + + return 0; +} + +/* flag addr_bytes data_bytes reg_num reg_addr*/ +static int aw87xxx_awrw_parse_buf(struct aw87xxx *aw87xxx, + const char *buf, size_t count, int *wr_status) +{ + int data[AWRW_HDR_MAX] = {0}; + struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; + int ret = -1; + + if (sscanf(buf, "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", + &data[AWRW_HDR_WR_FLAG], &data[AWRW_HDR_ADDR_BYTES], + &data[AWRW_HDR_DATA_BYTES], &data[AWRW_HDR_REG_NUM], + &data[AWRW_HDR_REG_ADDR]) == 5) { + + packet->reg_addr = data[AWRW_HDR_REG_ADDR]; + packet->reg_num = data[AWRW_HDR_REG_NUM]; + *wr_status = data[AWRW_HDR_WR_FLAG]; + ret = aw87xxx_awrw_data_check(aw87xxx, data, count); + if (ret < 0) + return ret; + + return 0; + } + + return -EINVAL; +} + +static ssize_t aw87xxx_attr_awrw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; + int wr_status = 0; + int ret = -1; + + if (count < AWRW_HDR_LEN) { + AW_DEV_LOGE(aw87xxx->dev, "data count too smaller, please check write format"); + AW_DEV_LOGE(aw87xxx->dev, "string %s,count=%ld", + buf, (u_long)count); + return -EINVAL; + } + + AW_DEV_LOGI(aw87xxx->dev, "string:[%s],count=%ld", buf, (u_long)count); + ret = aw87xxx_awrw_parse_buf(aw87xxx, buf, count, &wr_status); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "can not parse string"); + return ret; + } + + if (wr_status == AWRW_FLAG_WRITE) { + ret = aw87xxx_awrw_write(aw87xxx, buf, count); + if (ret < 0) + return ret; + } else if (wr_status == AWRW_FLAG_READ) { + packet->status = AWRW_I2C_ST_READ; + AW_DEV_LOGI(aw87xxx->dev, "read_cmd:reg_addr[0x%02x], reg_num[%d]", + packet->reg_addr, packet->reg_num); + } else { + AW_DEV_LOGE(aw87xxx->dev, "please check str format, unsupport read_write_status: %d", + wr_status); + return -EINVAL; + } + + return count; +} + +static ssize_t aw87xxx_attr_awrw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_i2c_packet *packet = &aw87xxx->i2c_packet; + int data_len = 0; + size_t len = 0; + int ret = -1, i = 0; + char *reg_data = NULL; + + if (packet->status != AWRW_I2C_ST_READ) { + AW_DEV_LOGE(aw87xxx->dev, "please write read cmd first"); + return -EINVAL; + } + + data_len = AWRW_DATA_BYTES * packet->reg_num; + reg_data = (char *)vmalloc(data_len); + if (reg_data == NULL) { + AW_DEV_LOGE(aw87xxx->dev, "memory alloc failed"); + ret = -EINVAL; + goto exit; + } + + mutex_lock(&aw87xxx->reg_lock); + ret = aw87xxx_dev_i2c_read_msg(&aw87xxx->aw_dev, packet->reg_addr, + (char *)reg_data, data_len); + if (ret < 0) { + ret = -EFAULT; + mutex_unlock(&aw87xxx->reg_lock); + goto exit; + } + mutex_unlock(&aw87xxx->reg_lock); + + AW_DEV_LOGI(aw87xxx->dev, "reg_addr 0x%02x, reg_num %d", + packet->reg_addr, packet->reg_num); + + for (i = 0; i < data_len; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "0x%02x,", reg_data[i]); + AW_DEV_LOGI(aw87xxx->dev, "0x%02x", reg_data[i]); + } + + ret = len; + +exit: + if (reg_data) { + vfree(reg_data); + reg_data = NULL; + } + packet->status = AWRW_I2C_ST_NONE; + return ret; +} + +static ssize_t aw87xxx_drv_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "driver_ver: %s \n", AW87XXX_DRIVER_VERSION); + + return len; +} + +static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, + aw87xxx_attr_get_reg, aw87xxx_attr_set_reg); +static DEVICE_ATTR(profile, S_IWUSR | S_IRUGO, + aw87xxx_attr_get_profile, aw87xxx_attr_set_profile); +static DEVICE_ATTR(hwen, S_IWUSR | S_IRUGO, + aw87xxx_attr_get_hwen, aw87xxx_attr_set_hwen); +static DEVICE_ATTR(awrw, S_IWUSR | S_IRUGO, + aw87xxx_attr_awrw_show, aw87xxx_attr_awrw_store); +static DEVICE_ATTR(drv_ver, S_IRUGO, aw87xxx_drv_ver_show, NULL); + +static struct attribute *aw87xxx_attributes[] = { + &dev_attr_reg.attr, + &dev_attr_profile.attr, + &dev_attr_hwen.attr, + &dev_attr_awrw.attr, + &dev_attr_drv_ver.attr, + NULL +}; + +static struct attribute_group aw87xxx_attribute_group = { + .attrs = aw87xxx_attributes +}; + +/**************************************************************************** + * + *aw87xxx device probe + * + ****************************************************************************/ + +int aw87xxx_dtsi_dev_index_check(struct aw87xxx *cur_aw87xxx) +{ + struct list_head *pos = NULL; + struct aw87xxx *list_aw87xxx = NULL; + + list_for_each(pos, &g_aw87xxx_list) { + list_aw87xxx = list_entry(pos, struct aw87xxx, list); + if (list_aw87xxx->dev_index == cur_aw87xxx->dev_index) { + AW_DEV_LOGE(cur_aw87xxx->dev, "dev_index has already existing,check failed"); + return -EINVAL; + } + } + + return 0; +} + +static int aw87xxx_dtsi_parse(struct aw87xxx *aw87xxx, + struct device_node *dev_node) +{ + int ret = -1; + int32_t dev_index = -EINVAL; + int32_t voltage_min = -EINVAL; + struct aw_voltage_desc *vol_desc = &aw87xxx->aw_dev.vol_desc; + + ret = of_property_read_u32(dev_node, "dev_index", &dev_index); + if (ret < 0) { + AW_DEV_LOGI(aw87xxx->dev, "dev_index parse failed, user default[%d], ret=%d", + g_aw87xxx_dev_cnt, ret); + aw87xxx->dev_index = g_aw87xxx_dev_cnt; + } else { + aw87xxx->dev_index = dev_index; + AW_DEV_LOGI(aw87xxx->dev, "parse dev_index=[%d]", + aw87xxx->dev_index); + } + + ret = aw87xxx_dtsi_dev_index_check(aw87xxx); + if (ret < 0) + return ret; + + ret = of_get_named_gpio(dev_node, "reset-gpio", 0); + if (ret < 0) { + AW_DEV_LOGI(aw87xxx->dev, "no reset gpio provided, hardware reset unavailable"); + aw87xxx->aw_dev.rst_gpio = AW_NO_RESET_GPIO; + aw87xxx->aw_dev.hwen_status = AW_DEV_HWEN_INVALID; + } else { + aw87xxx->aw_dev.rst_gpio = ret; + aw87xxx->aw_dev.hwen_status = AW_DEV_HWEN_OFF; + AW_DEV_LOGI(aw87xxx->dev, "reset gpio[%d] parse succeed", ret); + if (gpio_is_valid(aw87xxx->aw_dev.rst_gpio)) { + ret = devm_gpio_request_one(aw87xxx->dev, + aw87xxx->aw_dev.rst_gpio, + GPIOF_OUT_INIT_LOW, "aw87xxx_reset"); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "reset request failed"); + return ret; + } + } + } + + ret = of_property_read_u32(dev_node, "aw-voltage-min", &voltage_min); + if (ret < 0) { + AW_DEV_LOGI(aw87xxx->dev, "voltage_min parse failed, user default[0x%02x]", + AW_BOOST_VOLTAGE_MIN); + vol_desc->vol_min = AW_BOOST_VOLTAGE_MIN; + } else { + vol_desc->vol_min = voltage_min; + AW_DEV_LOGI(aw87xxx->dev, "parse aw_voltage_min=[0x%02x]", + vol_desc->vol_min); + } + + aw87xxx_device_parse_port_id_dt(&aw87xxx->aw_dev); + aw87xxx_device_parse_topo_id_dt(&aw87xxx->aw_dev); + + return 0; +} + +static struct aw87xxx *aw87xxx_malloc_init(struct i2c_client *client) +{ + struct aw87xxx *aw87xxx = NULL; + + aw87xxx = devm_kzalloc(&client->dev, sizeof(struct aw87xxx), + GFP_KERNEL); + if (aw87xxx == NULL) { + AW_DEV_LOGE(&client->dev, "failed to devm_kzalloc aw87xxx"); + return NULL; + } + memset(aw87xxx, 0, sizeof(struct aw87xxx)); + + aw87xxx->dev = &client->dev; + aw87xxx->aw_dev.dev = &client->dev; + aw87xxx->aw_dev.i2c_bus = client->adapter->nr; + aw87xxx->aw_dev.i2c_addr = client->addr; + aw87xxx->aw_dev.i2c = client; + aw87xxx->aw_dev.hwen_status = false; + aw87xxx->aw_dev.reg_access = NULL; + aw87xxx->aw_dev.hwen_status = AW_DEV_HWEN_INVALID; + aw87xxx->off_bin_status = AW87XXX_NO_OFF_BIN; + aw87xxx->codec = NULL; + aw87xxx->current_profile = aw87xxx->prof_off_name; + + mutex_init(&aw87xxx->reg_lock); + + AW_DEV_LOGI(&client->dev, "struct aw87xxx devm_kzalloc and init down"); + return aw87xxx; +} + +static int aw87xxx_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device_node *dev_node = client->dev.of_node; + struct aw87xxx *aw87xxx = NULL; + int ret = -1; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + AW_DEV_LOGE(&client->dev, "check_functionality failed"); + ret = -ENODEV; + goto exit_check_functionality_failed; + } + + /* aw87xxx i2c_dev struct init */ + aw87xxx = aw87xxx_malloc_init(client); + if (aw87xxx == NULL) + goto exit_malloc_init_failed; + + i2c_set_clientdata(client, aw87xxx); + + /* aw87xxx dev_node parse */ + ret = aw87xxx_dtsi_parse(aw87xxx, dev_node); + if (ret < 0) + goto exit_dtsi_parse_failed; + + /*hw power on PA*/ + aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, true); + + /* aw87xxx devices private attributes init */ + ret = aw87xxx_dev_init(&aw87xxx->aw_dev); + if (ret < 0) + goto exit_device_init_failed; + + /*product register reset */ + aw87xxx_dev_soft_reset(&aw87xxx->aw_dev); + + /*hw power off */ + aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); + + /* create debug attrbute nodes */ + ret = sysfs_create_group(&aw87xxx->dev->kobj, &aw87xxx_attribute_group); + if (ret < 0) + AW_DEV_LOGE(aw87xxx->dev, "failed to create sysfs nodes, will not allowed to use"); + + /* cfg_load init */ + aw87xxx_fw_load_init(aw87xxx); + + /*monitor init*/ + aw87xxx_monitor_init(aw87xxx->dev, &aw87xxx->monitor, dev_node); + +#ifdef AW87XXX_RUNIN_TEST + g_runin_test = 0; + INIT_DELAYED_WORK(&aw87xxx->adsp_status, aw87xxx_set_adsp_module_status); +#endif + g_saved_spin_value = AW_SPIN_0; + INIT_DELAYED_WORK(&aw87xxx->spin_status, aw87xxx_set_spin_status); + + g_spin_enable = 0; + + /*add device to total list */ + mutex_lock(&g_aw87xxx_mutex_lock); + g_aw87xxx_dev_cnt++; + list_add(&aw87xxx->list, &g_aw87xxx_list); + mutex_unlock(&g_aw87xxx_mutex_lock); + + AW_DEV_LOGI(aw87xxx->dev, "succeed, dev_index=[%d], g_aw87xxx_dev_cnt= [%d]", + aw87xxx->dev_index, g_aw87xxx_dev_cnt); + + return 0; + +exit_device_init_failed: + aw87xxx_dev_hw_pwr_ctrl(&aw87xxx->aw_dev, false); +exit_dtsi_parse_failed: + AW_DEV_LOGE(aw87xxx->dev, "pa init failed"); + if (gpio_is_valid(aw87xxx->aw_dev.rst_gpio)) + devm_gpio_free(&client->dev, aw87xxx->aw_dev.rst_gpio); + devm_kfree(&client->dev, aw87xxx); + aw87xxx = NULL; +exit_malloc_init_failed: +exit_check_functionality_failed: + return ret; +} + +static int aw87xxx_i2c_remove(struct i2c_client *client) +{ + struct aw87xxx *aw87xxx = i2c_get_clientdata(client); + + if (gpio_is_valid(aw87xxx->aw_dev.rst_gpio)) + devm_gpio_free(&client->dev, aw87xxx->aw_dev.rst_gpio); + + aw87xxx_monitor_exit(&aw87xxx->monitor); + + /*rm attr node*/ + sysfs_remove_group(&aw87xxx->dev->kobj, &aw87xxx_attribute_group); + + aw87xxx_fw_cfg_free(aw87xxx); + + mutex_lock(&g_aw87xxx_mutex_lock); + g_aw87xxx_dev_cnt--; + list_del(&aw87xxx->list); + mutex_unlock(&g_aw87xxx_mutex_lock); + + devm_kfree(&client->dev, aw87xxx); + aw87xxx = NULL; + + return 0; +} + +static void aw87xxx_i2c_shutdown(struct i2c_client *client) +{ + struct aw87xxx *aw87xxx = i2c_get_clientdata(client); + + AW_DEV_LOGI(&client->dev, "enter"); + + /*soft and hw power off*/ + aw87xxx_update_profile(aw87xxx, aw87xxx->prof_off_name); +} + + +static const struct i2c_device_id aw87xxx_i2c_id[] = { + {AW87XXX_I2C_NAME, 0}, + {}, +}; + +static const struct of_device_id extpa_of_match[] = { + {.compatible = "awinic,aw87xxx_pa"}, + {}, +}; + +static struct i2c_driver aw87xxx_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = AW87XXX_I2C_NAME, + .of_match_table = extpa_of_match, + }, + .probe = aw87xxx_i2c_probe, + .remove = aw87xxx_i2c_remove, + .shutdown = aw87xxx_i2c_shutdown, + .id_table = aw87xxx_i2c_id, +}; + +static int __init aw87xxx_pa_init(void) +{ + int ret; + + AW_LOGI("driver version: %s", AW87XXX_DRIVER_VERSION); + + ret = i2c_add_driver(&aw87xxx_i2c_driver); + if (ret < 0) { + AW_LOGE("Unable to register driver, ret= %d", ret); + return ret; + } + return 0; +} + +static void __exit aw87xxx_pa_exit(void) +{ + AW_LOGI("enter"); + i2c_del_driver(&aw87xxx_i2c_driver); +} + +module_init(aw87xxx_pa_init); +module_exit(aw87xxx_pa_exit); + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("awinic aw87xxx pa driver"); +MODULE_LICENSE("GPL v2"); diff --git a/techpack/audio/asoc/codecs/aw87xxx/aw87xxx.h b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx.h new file mode 100644 index 000000000000..a2cfcd98725a --- /dev/null +++ b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx.h @@ -0,0 +1,127 @@ +#ifndef __AW87XXX_H__ +#define __AW87XXX_H__ +#include +#include +#include +#include + +#include "aw87xxx_device.h" +#include "aw87xxx_monitor.h" +#include "aw87xxx_acf_bin.h" + +#define AW_CFG_UPDATE_DELAY +#define AW_CFG_UPDATE_DELAY_TIMER (3000) + +#define AW87XXX_NO_OFF_BIN (0) +#define AW87XXX_OFF_BIN_OK (1) + +#define AW87XXX_PRIVATE_KCONTROL_NUM (3) +#define AW87XXX_PUBLIC_KCONTROL_NUM (4) + +#define AW_I2C_RETRIES (5) +#define AW_I2C_RETRY_DELAY (2) +#define AW_I2C_READ_MSG_NUM (2) + +#define AW87XXX_FW_NAME_MAX (64) +#define AW_NAME_BUF_MAX (64) +#define AW_LOAD_FW_RETRIES (3) + +#define AW_DEV_REG_RD_ACCESS (1 << 0) +#define AW_DEV_REG_WR_ACCESS (1 << 1) + +#define AWRW_ADDR_BYTES (1) +#define AWRW_DATA_BYTES (1) +#define AWRW_HDR_LEN (24) + +#define AW87XXX_RUNIN_TEST 1 + +/*********************************************************** + * + * aw87xxx codec control compatible with kernel 4.19 + * + ***********************************************************/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 1) +#define AW_KERNEL_VER_OVER_4_19_1 +#endif + +#ifdef AW_KERNEL_VER_OVER_4_19_1 +typedef struct snd_soc_component aw_snd_soc_codec_t; +#else +typedef struct snd_soc_codec aw_snd_soc_codec_t; +#endif + +struct aw_componet_codec_ops { + int (*add_codec_controls)(aw_snd_soc_codec_t *codec, + const struct snd_kcontrol_new *controls, unsigned int num_controls); + void (*unregister_codec)(struct device *dev); +}; + + +/******************************************** + * + * aw87xxx devices attributes + * + *******************************************/ +enum { + AWRW_FLAG_WRITE = 0, + AWRW_FLAG_READ, +}; + +enum { + AWRW_I2C_ST_NONE = 0, + AWRW_I2C_ST_READ, + AWRW_I2C_ST_WRITE, +}; + +enum { + AWRW_HDR_WR_FLAG = 0, + AWRW_HDR_ADDR_BYTES, + AWRW_HDR_DATA_BYTES, + AWRW_HDR_REG_NUM, + AWRW_HDR_REG_ADDR, + AWRW_HDR_MAX, +}; + +struct aw_i2c_packet { + char status; + unsigned int reg_num; + unsigned int reg_addr; + char *reg_data; +}; + + +/******************************************** + * + * aw87xxx device struct + * + *******************************************/ +struct aw87xxx { + char fw_name[AW87XXX_FW_NAME_MAX]; + int32_t dev_index; + char *current_profile; + char prof_off_name[AW_PROFILE_STR_MAX]; + uint32_t off_bin_status; + struct device *dev; + + struct mutex reg_lock; + struct aw_device aw_dev; + struct aw_i2c_packet i2c_packet; + + struct delayed_work fw_load_work; + struct acf_bin_info acf_info; + + aw_snd_soc_codec_t *codec; + + struct list_head list; + + struct aw_monitor monitor; +#ifdef AW87XXX_RUNIN_TEST + struct delayed_work adsp_status; +#endif + struct delayed_work spin_status; +}; + +int aw87xxx_update_profile(struct aw87xxx *aw87xxx, char *profile); +int aw87xxx_update_profile_esd(struct aw87xxx *aw87xxx, char *profile); + +#endif diff --git a/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_acf_bin.c b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_acf_bin.c new file mode 100644 index 000000000000..00c7aedb7c11 --- /dev/null +++ b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_acf_bin.c @@ -0,0 +1,1558 @@ +/* + * aw87xxx_acf_bin.c + * + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + * Author: Barry + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw87xxx.h" +#include "aw87xxx_acf_bin.h" +#include "aw87xxx_monitor.h" +#include "aw87xxx_log.h" +#include "aw87xxx_bin_parse.h" + +/************************************************************************* + * + *Table corresponding to customized profile ids to profile names + * + *************************************************************************/ +enum aw_customers_profile_id { + AW_CTOS_PROFILE_OFF = 0, + AW_CTOS_PROFILE_MUSIC, + AW_CTOS_PROFILE_VOICE, + AW_CTOS_PROFILE_VOIP, + AW_CTOS_PROFILE_RINGTONE, + AW_CTOS_PROFILE_RINGTONE_HS, + AW_CTOS_PROFILE_LOWPOWER, + AW_CTOS_PROFILE_BYPASS, + AW_CTOS_PROFILE_MMI, + AW_CTOS_PROFILE_FM, + AW_CTOS_PROFILE_NOTIFICATION, + AW_CTOS_PROFILE_RECEIVER, + AW_CTOS_PROFILE_MAX, +}; + +static char *g_ctos_profile_name[AW_PROFILE_MAX] = { + [AW_CTOS_PROFILE_OFF] = "Off", + [AW_CTOS_PROFILE_MUSIC] = "Music", + [AW_CTOS_PROFILE_VOICE] = "Voice", + [AW_CTOS_PROFILE_VOIP] = "Voip", + [AW_CTOS_PROFILE_RINGTONE] = "Ringtone", + [AW_CTOS_PROFILE_RINGTONE_HS] = "Ringtone_hs", + [AW_CTOS_PROFILE_LOWPOWER] = "Lowpower", + [AW_CTOS_PROFILE_BYPASS] = "Bypass", + [AW_CTOS_PROFILE_MMI] = "Mmi", + [AW_CTOS_PROFILE_FM] = "Fm", + [AW_CTOS_PROFILE_NOTIFICATION] = "Notification", + [AW_CTOS_PROFILE_RECEIVER] = "Receiver", +}; + + +char *aw87xxx_ctos_get_prof_name(int profile_id) +{ + if (profile_id < 0 || profile_id >= AW_CTOS_PROFILE_MAX) + return NULL; + else + return g_ctos_profile_name[profile_id]; +} + + +static char *g_profile_name[] = {"Music", "Voice", "Voip", + "Ringtone", "Ringtone_hs", "Lowpower", "Bypass", "Mmi", + "Fm", "Notification", "Receiver", "Off"}; + +static char *g_power_off_name[] = {"Off", "OFF", "off", "oFF", "power_down"}; + +static char *aw_get_prof_name(int profile) +{ + if (profile < 0 || profile >= AW_PROFILE_MAX) + return "NULL"; + else + return g_profile_name[profile]; +} + +/************************************************************************* + * + *acf check + * + *************************************************************************/ +static int aw_crc8_check(const unsigned char *data, unsigned int data_size) + +{ + unsigned char crc_value = 0x00; + unsigned char *pdata; + int i; + unsigned char pdatabuf = 0; + + pdata = (unsigned char *)data; + + while (data_size--) { + pdatabuf = *pdata++; + for (i = 0; i < 8; i++) { + if ((crc_value ^ (pdatabuf)) & 0x01) { + crc_value ^= 0x18; + crc_value >>= 1; + crc_value |= 0x80; + } else { + crc_value >>= 1; + } + pdatabuf >>= 1; + } + } + + return (int)crc_value; +} + +static int aw_check_file_id(struct device *dev, + char *fw_data, int32_t file_id) +{ + int32_t *acf_file_id = NULL; + + acf_file_id = (int32_t *)fw_data; + if (*acf_file_id != file_id) { + AW_DEV_LOGE(dev, "file id [%x] check failed", *acf_file_id); + return -ENFILE; + } + + return 0; +} + +static int aw_check_header_size(struct device *dev, + char *fw_data, size_t fw_size) +{ + if (fw_size < sizeof(struct aw_acf_hdr)) { + AW_DEV_LOGE(dev, "acf size check failed,size less-than aw_acf_hdr"); + return -ENOEXEC; + } + + return 0; +} + +/*************************************************************************** + * V0.0.0.1 version acf check + **************************************************************************/ +static int aw_check_ddt_size_v_0_0_0_1(struct device *dev, char *fw_data) +{ + struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)fw_data; + struct aw_acf_dde *acf_dde = NULL; + + acf_dde = (struct aw_acf_dde *)(fw_data + acf_hdr->ddt_offset); + + /* check ddt_size in acf_header is aqual to ddt_num multiply by dde_size */ + if (acf_hdr->ddt_size != acf_hdr->dde_num * sizeof(struct aw_acf_dde)) { + AW_DEV_LOGE(dev, "acf ddt size check failed"); + return -EINVAL; + } + + return 0; +} + +static int aw_check_data_size_v_0_0_0_1(struct device *dev, + char *fw_data, size_t fw_size) +{ + int i = 0; + size_t data_size = 0; + struct aw_acf_hdr *acf_hdr = NULL; + struct aw_acf_dde *acf_dde = NULL; + + acf_hdr = (struct aw_acf_hdr *)fw_data; + acf_dde = (struct aw_acf_dde *)(fw_data + acf_hdr->ddt_offset); + + for (i = 0; i < acf_hdr->dde_num; ++i) { + if (acf_dde[i].data_size % 2) { + AW_DEV_LOGE(dev, "acf dde[%d].data_size[%d],dev_name[%s],data_type[%d], data_size check failed", + i, acf_dde[i].data_size, acf_dde[i].dev_name, + acf_dde[i].data_type); + return -EINVAL; + } + data_size += acf_dde[i].data_size; + } + + /* Verify that the file size is equal to the header size plus */ + /* the table size and data size */ + if (fw_size != data_size + sizeof(struct aw_acf_hdr) + acf_hdr->ddt_size) { + AW_DEV_LOGE(dev, "acf size check failed"); + AW_DEV_LOGE(dev, "fw_size=%ld,hdr_size and ddt size and data size =%ld", + (u_long)fw_size, (u_long)(data_size + sizeof(struct aw_acf_hdr) + + acf_hdr->ddt_size)); + return -EINVAL; + } + + return 0; +} + +static int aw_check_data_crc_v_0_0_0_1(struct device *dev, char *fw_data) +{ + int i = 0; + size_t crc_val = 0; + char *data = NULL; + struct aw_acf_hdr *acf_hdr = NULL; + struct aw_acf_dde *acf_dde = NULL; + + acf_hdr = (struct aw_acf_hdr *)fw_data; + acf_dde = (struct aw_acf_dde *)(fw_data + acf_hdr->ddt_offset); + + for (i = 0; i < acf_hdr->dde_num; ++i) { + data = fw_data + acf_dde[i].data_offset; + crc_val = aw_crc8_check(data, acf_dde[i].data_size); + if (crc_val != acf_dde[i].data_crc) { + AW_DEV_LOGE(dev, "acf dde_crc check failed"); + return -EINVAL; + } + } + + return 0; +} + +static int aw_check_profile_id_v_0_0_0_1(struct device *dev, char *fw_data) +{ + int i = 0; + struct aw_acf_hdr *acf_hdr = NULL; + struct aw_acf_dde *acf_dde = NULL; + + acf_hdr = (struct aw_acf_hdr *)fw_data; + acf_dde = (struct aw_acf_dde *)(fw_data + acf_hdr->ddt_offset); + + for (i = 0; i < acf_hdr->dde_num; ++i) { + if (acf_dde[i].data_type == AW_MONITOR) + continue; + if (acf_dde[i].dev_profile > AW_PROFILE_MAX) { + AW_DEV_LOGE(dev, "parse profile_id[%d] failed", acf_dde[i].dev_profile); + return -EINVAL; + } + } + + return 0; +} +static int aw_check_data_v_0_0_0_1(struct device *dev, + char *fw_data, size_t size) +{ + int ret = -1; + + /* check file type id is awinic acf file */ + ret = aw_check_file_id(dev, fw_data, AW_ACF_FILE_ID); + if (ret < 0) + return ret; + + /* check ddt_size in header is equal to all ddt aize */ + ret = aw_check_ddt_size_v_0_0_0_1(dev, fw_data); + if (ret < 0) + return ret; + + /* Verify that the file size is equal to the header size plus */ + /* the table size and data size */ + ret = aw_check_data_size_v_0_0_0_1(dev, fw_data, size); + if (ret < 0) + return ret; + + /* check crc in is equal to dde data crc */ + ret = aw_check_data_crc_v_0_0_0_1(dev, fw_data); + if (ret < 0) + return ret; + + /* check profile id is in profile_id_max */ + ret = aw_check_profile_id_v_0_0_0_1(dev, fw_data); + if (ret < 0) + return ret; + + AW_DEV_LOGI(dev, "acf fimware check succeed"); + + return 0; +} + +/*************************************************************************** + * V1.0.0.0 version acf chack + **************************************************************************/ +static int aw_check_ddt_size_v_1_0_0_0(struct device *dev, char *fw_data) +{ + struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)fw_data; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = NULL; + + acf_dde = (struct aw_acf_dde_v_1_0_0_0 *)(fw_data + acf_hdr->ddt_offset); + + /* check ddt_size in acf_header is aqual to ddt_num multiply by dde_size */ + if (acf_hdr->ddt_size != acf_hdr->dde_num * sizeof(struct aw_acf_dde_v_1_0_0_0)) { + AW_DEV_LOGE(dev, "acf ddt size check failed"); + return -EINVAL; + } + + return 0; +} + +static int aw_check_data_size_v_1_0_0_0(struct device *dev, + char *fw_data, size_t fw_size) +{ + int i = 0; + size_t data_size = 0; + struct aw_acf_hdr *acf_hdr = NULL; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = NULL; + + acf_hdr = (struct aw_acf_hdr *)fw_data; + acf_dde = (struct aw_acf_dde_v_1_0_0_0 *)(fw_data + acf_hdr->ddt_offset); + + for (i = 0; i < acf_hdr->dde_num; ++i) { + if (acf_dde[i].data_size % 2) { + AW_DEV_LOGE(dev, "acf dde[%d].data_size[%d],dev_name[%s],data_type[%d], data_size check failed", + i, acf_dde[i].data_size, acf_dde[i].dev_name, + acf_dde[i].data_type); + return -EINVAL; + } + data_size += acf_dde[i].data_size; + } + + /* Verify that the file size is equal to the header size plus */ + /* the table size and data size */ + if (fw_size != data_size + sizeof(struct aw_acf_hdr) + acf_hdr->ddt_size) { + AW_DEV_LOGE(dev, "acf size check failed"); + AW_DEV_LOGE(dev, "fw_size=%ld,hdr_size and ddt size and data size =%ld", + (u_long)fw_size, (u_long)(data_size + sizeof(struct aw_acf_hdr) + + acf_hdr->ddt_size)); + return -EINVAL; + } + + return 0; +} + +static int aw_check_data_crc_v_1_0_0_0(struct device *dev, char *fw_data) +{ + int i = 0; + size_t crc_val = 0; + char *data = NULL; + struct aw_acf_hdr *acf_hdr = NULL; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = NULL; + + acf_hdr = (struct aw_acf_hdr *)fw_data; + acf_dde = (struct aw_acf_dde_v_1_0_0_0 *)(fw_data + acf_hdr->ddt_offset); + + for (i = 0; i < acf_hdr->dde_num; ++i) { + data = fw_data + acf_dde[i].data_offset; + crc_val = aw_crc8_check(data, acf_dde[i].data_size); + if (crc_val != acf_dde[i].data_crc) { + AW_DEV_LOGE(dev, "acf dde_crc check failed"); + return -EINVAL; + } + } + + return 0; +} + +static int aw_check_data_v_1_0_0_0(struct device *dev, + char *fw_data, size_t size) +{ + int ret = -1; + + /* check file type id is awinic acf file */ + ret = aw_check_file_id(dev, fw_data, AW_ACF_FILE_ID); + if (ret < 0) + return ret; + + /* check ddt_size in header is equal to all ddt aize */ + ret = aw_check_ddt_size_v_1_0_0_0(dev, fw_data); + if (ret < 0) + return ret; + + /* Verify that the file size is equal to the header size plus */ + /* the table size and data size */ + ret = aw_check_data_size_v_1_0_0_0(dev, fw_data, size); + if (ret < 0) + return ret; + + /* check crc in is equal to dde data crc */ + ret = aw_check_data_crc_v_1_0_0_0(dev, fw_data); + if (ret < 0) + return ret; + + AW_DEV_LOGI(dev, "acf fimware check succeed"); + + return 0; +} + +/*************************************************************************** + * acf chack API + **************************************************************************/ +static int aw_check_acf_firmware(struct device *dev, + char *fw_data, size_t size) +{ + int ret = -1; + struct aw_acf_hdr *acf_hdr = NULL; + + if (fw_data == NULL) { + AW_DEV_LOGE(dev, "fw_data is NULL,fw_data check failed"); + return -ENODATA; + } + + /* check file size is less-than header size */ + ret = aw_check_header_size(dev, fw_data, size); + if (ret < 0) + return ret; + + acf_hdr = (struct aw_acf_hdr *)fw_data; + AW_DEV_LOGI(dev, "project name: [%s]", acf_hdr->project); + AW_DEV_LOGI(dev, "custom name: [%s]", acf_hdr->custom); + AW_DEV_LOGI(dev, "version name: [%s]", acf_hdr->version); + AW_DEV_LOGI(dev, "author_id: [%d]", acf_hdr->author_id); + + switch (acf_hdr->hdr_version) { + case AW_ACF_HDR_VER_0_0_0_1: + return aw_check_data_v_0_0_0_1(dev, fw_data, size); + case AW_ACF_HDR_VER_1_0_0_0: + return aw_check_data_v_1_0_0_0(dev, fw_data, size); + default: + AW_DEV_LOGE(dev, "unsupported hdr_version [0x%x]", + acf_hdr->hdr_version); + return -EINVAL; + } + + return ret; +} + + + +/************************************************************************* + * + *acf parse + * + *************************************************************************/ +static int aw_parse_raw_reg(struct device *dev, uint8_t *data, + uint32_t data_len, struct aw_prof_desc *prof_desc) +{ + AW_DEV_LOGD(dev, "data_size:%d enter", data_len); + + prof_desc->data_container.data = data; + prof_desc->data_container.len = data_len; + + prof_desc->prof_st = AW_PROFILE_OK; + + return 0; +} + +static int aw_parse_reg_with_hdr(struct device *dev, uint8_t *data, + uint32_t data_len, struct aw_prof_desc *prof_desc) +{ + struct aw_bin *aw_bin = NULL; + int ret = -1; + + AW_DEV_LOGD(dev, "data_size:%d enter", data_len); + + aw_bin = kzalloc(data_len + sizeof(struct aw_bin), GFP_KERNEL); + if (aw_bin == NULL) { + AW_DEV_LOGE(dev, "devm_kzalloc aw_bin failed"); + return -ENOMEM; + } + + aw_bin->info.len = data_len; + memcpy(aw_bin->info.data, data, data_len); + + ret = aw87xxx_parsing_bin_file(aw_bin); + if (ret < 0) { + AW_DEV_LOGE(dev, "parse bin failed"); + goto parse_bin_failed; + } + + if ((aw_bin->all_bin_parse_num != 1) || + (aw_bin->header_info[0].bin_data_type != DATA_TYPE_REGISTER)) { + AW_DEV_LOGE(dev, "bin num or type error"); + goto parse_bin_failed; + } + + prof_desc->data_container.data = + data + aw_bin->header_info[0].valid_data_addr; + prof_desc->data_container.len = aw_bin->header_info[0].valid_data_len; + prof_desc->prof_st = AW_PROFILE_OK; + + kfree(aw_bin); + aw_bin = NULL; + + return 0; + +parse_bin_failed: + kfree(aw_bin); + aw_bin = NULL; + return ret; +} + +static int aw_parse_monitor_config(struct device *dev, + char *monitor_data, uint32_t data_len) +{ + int ret = -1; + + if (monitor_data == NULL || data_len == 0) { + AW_DEV_LOGE(dev, "no data to parse"); + return -EBFONT; + } + + ret = aw87xxx_monitor_bin_parse(dev, monitor_data, data_len); + if (ret < 0) { + AW_DEV_LOGE(dev, "monitor_config parse failed"); + return ret; + } + + AW_DEV_LOGI(dev, "monitor_bin parse succeed"); + + return 0; +} + +static int aw_check_prof_str_is_off(char *profile_name) +{ + int i = 0; + + for (i = 0; i < AW_POWER_OFF_NAME_SUPPORT_COUNT; i++) { + if (strnstr(profile_name, g_power_off_name[i], + strlen(profile_name) + 1)) + return 0; + } + + return -EINVAL; +} + +/*************************************************************************** + * V0.0.0.1 version acf paese + **************************************************************************/ +static int aw_check_product_name_v_0_0_0_1(struct device *dev, + struct acf_bin_info *acf_info, + struct aw_acf_dde *prof_hdr) +{ + int i = 0; + + for (i = 0; i < acf_info->product_cnt; i++) { + if (0 == strcmp(acf_info->product_tab[i], prof_hdr->dev_name)) { + AW_DEV_LOGD(dev, "bin_dev_name:%s", + prof_hdr->dev_name); + return 0; + } + } + + return -ENXIO; +} + +static int aw_check_data_type_is_monitor_v_0_0_0_1(struct device *dev, + struct aw_acf_dde *prof_hdr) +{ + if (prof_hdr->data_type == AW_MONITOR) { + AW_DEV_LOGD(dev, "bin data is monitor"); + return 0; + } + + return -ENXIO; +} + +static int aw_parse_data_by_sec_type_v_0_0_0_1(struct device *dev, + struct acf_bin_info *acf_info, + struct aw_acf_dde *prof_hdr, + struct aw_prof_desc *profile_prof_desc) +{ + int ret = -1; + char *cfg_data = acf_info->fw_data + prof_hdr->data_offset; + + switch (prof_hdr->data_type) { + case AW_BIN_TYPE_REG: + snprintf(profile_prof_desc->dev_name, sizeof(prof_hdr->dev_name), + "%s", prof_hdr->dev_name); + profile_prof_desc->prof_name = aw_get_prof_name(prof_hdr->dev_profile); + AW_DEV_LOGD(dev, "parse reg type data enter,profile=%s", + aw_get_prof_name(prof_hdr->dev_profile)); + ret = aw_parse_raw_reg(dev, cfg_data, prof_hdr->data_size, + profile_prof_desc); + break; + case AW_BIN_TYPE_HDR_REG: + snprintf(profile_prof_desc->dev_name, sizeof(prof_hdr->dev_name), + "%s", prof_hdr->dev_name); + profile_prof_desc->prof_name = aw_get_prof_name(prof_hdr->dev_profile); + AW_DEV_LOGD(dev, "parse hdr_reg type data enter,profile=%s", + aw_get_prof_name(prof_hdr->dev_profile)); + ret = aw_parse_reg_with_hdr(dev, cfg_data, + prof_hdr->data_size, + profile_prof_desc); + break; + } + + return ret; +} + +static int aw_parse_dev_type_v_0_0_0_1(struct device *dev, + struct acf_bin_info *acf_info, struct aw_all_prof_info *all_prof_info) +{ + int i = 0; + int ret = -1; + int sec_num = 0; + char *cfg_data = NULL; + struct aw_prof_desc *prof_desc = NULL; + struct aw_acf_dde *acf_dde = + (struct aw_acf_dde *)(acf_info->fw_data + acf_info->acf_hdr.ddt_offset); + + AW_DEV_LOGD(dev, "enter"); + + for (i = 0; i < acf_info->acf_hdr.dde_num; i++) { + if ((acf_info->aw_dev->i2c_bus == acf_dde[i].dev_bus) && + (acf_info->aw_dev->i2c_addr == acf_dde[i].dev_addr) && + (acf_dde[i].type == AW_DDE_DEV_TYPE_ID)) { + + ret = aw_check_product_name_v_0_0_0_1(dev, acf_info, &acf_dde[i]); + if (ret < 0) + continue; + + ret = aw_check_data_type_is_monitor_v_0_0_0_1(dev, &acf_dde[i]); + if (ret == 0) { + cfg_data = acf_info->fw_data + acf_dde[i].data_offset; + ret = aw_parse_monitor_config(dev, cfg_data, acf_dde[i].data_size); + if (ret < 0) + return ret; + continue; + } + + prof_desc = &all_prof_info->prof_desc[acf_dde[i].dev_profile]; + ret = aw_parse_data_by_sec_type_v_0_0_0_1(dev, acf_info, &acf_dde[i], + prof_desc); + if (ret < 0) { + AW_DEV_LOGE(dev, "parse dev type data failed"); + return ret; + } + sec_num++; + } + } + + if (sec_num == 0) { + AW_DEV_LOGD(dev, "get dev type num is %d, please use default", + sec_num); + return AW_DEV_TYPE_NONE; + } + + return AW_DEV_TYPE_OK; +} + +static int aw_parse_default_type_v_0_0_0_1(struct device *dev, + struct acf_bin_info *acf_info, struct aw_all_prof_info *all_prof_info) +{ + int i = 0; + int ret = -1; + int sec_num = 0; + char *cfg_data = NULL; + struct aw_prof_desc *prof_desc = NULL; + struct aw_acf_dde *acf_dde = + (struct aw_acf_dde *)(acf_info->fw_data + acf_info->acf_hdr.ddt_offset); + + AW_DEV_LOGD(dev, "enter"); + + for (i = 0; i < acf_info->acf_hdr.dde_num; i++) { + if ((acf_info->dev_index == acf_dde[i].dev_index) && + (acf_dde[i].type == AW_DDE_DEV_DEFAULT_TYPE_ID)) { + + ret = aw_check_product_name_v_0_0_0_1(dev, acf_info, &acf_dde[i]); + if (ret < 0) + continue; + + ret = aw_check_data_type_is_monitor_v_0_0_0_1(dev, &acf_dde[i]); + if (ret == 0) { + cfg_data = acf_info->fw_data + acf_dde[i].data_offset; + ret = aw_parse_monitor_config(dev, cfg_data, acf_dde[i].data_size); + if (ret < 0) + return ret; + continue; + } + + prof_desc = &all_prof_info->prof_desc[acf_dde[i].dev_profile]; + ret = aw_parse_data_by_sec_type_v_0_0_0_1(dev, acf_info, &acf_dde[i], + prof_desc); + if (ret < 0) { + AW_DEV_LOGE(dev, "parse default type data failed"); + return ret; + } + sec_num++; + } + } + + if (sec_num == 0) { + AW_DEV_LOGE(dev, "get dev default type failed, get num[%d]", + sec_num); + return -EINVAL; + } + + return 0; +} + +static int aw_get_prof_count_v_0_0_0_1(struct device *dev, + struct acf_bin_info *acf_info, + struct aw_all_prof_info *all_prof_info) +{ + int i = 0; + int prof_count = 0; + struct aw_prof_desc *prof_desc = all_prof_info->prof_desc; + + for (i = 0; i < AW_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW_PROFILE_OK) { + prof_count++; + } else if (i == AW_PROFILE_OFF) { + prof_count++; + AW_DEV_LOGI(dev, "not found profile [Off], set default"); + } + } + + AW_DEV_LOGI(dev, "get profile count=[%d]", prof_count); + return prof_count; +} + +static int aw_set_prof_off_info_v_0_0_0_1(struct device *dev, + struct acf_bin_info *acf_info, + struct aw_all_prof_info *all_prof_info, + int index) +{ + struct aw_prof_desc *prof_desc = all_prof_info->prof_desc; + struct aw_prof_info *prof_info = &acf_info->prof_info; + + if (index >= prof_info->count) { + AW_DEV_LOGE(dev, "index[%d] is out of table,profile count[%d]", + index, prof_info->count); + return -EINVAL; + } + + if (prof_desc[AW_PROFILE_OFF].prof_st == AW_PROFILE_OK) { + prof_info->prof_desc[index] = prof_desc[AW_PROFILE_OFF]; + AW_DEV_LOGI(dev, "product=[%s]----profile=[%s]", + prof_info->prof_desc[index].dev_name, + aw_get_prof_name(AW_PROFILE_OFF)); + } else { + memset(&prof_info->prof_desc[index].data_container, 0, + sizeof(struct aw_data_container)); + prof_info->prof_desc[index].prof_st = AW_PROFILE_WAIT; + prof_info->prof_desc[index].prof_name = aw_get_prof_name(AW_PROFILE_OFF); + AW_DEV_LOGI(dev, "set default power_off with no data to profile"); + } + + return 0; +} + + +static int aw_get_vaild_prof_v_0_0_0_1(struct device *dev, + struct acf_bin_info *acf_info, + struct aw_all_prof_info *all_prof_info) +{ + int i = 0; + int ret = 0; + int index = 0; + struct aw_prof_desc *prof_desc = all_prof_info->prof_desc; + struct aw_prof_info *prof_info = &acf_info->prof_info; + + prof_info->count = 0; + ret = aw_get_prof_count_v_0_0_0_1(dev, acf_info, all_prof_info); + if (ret < 0) + return ret; + prof_info->count = ret; + prof_info->prof_desc = devm_kzalloc(dev, + prof_info->count * sizeof(struct aw_prof_desc), + GFP_KERNEL); + if (prof_info->prof_desc == NULL) { + AW_DEV_LOGE(dev, "prof_desc kzalloc failed"); + return -ENOMEM; + } + + for (i = 0; i < AW_PROFILE_MAX; i++) { + if (i != AW_PROFILE_OFF && prof_desc[i].prof_st == AW_PROFILE_OK) { + if (index >= prof_info->count) { + AW_DEV_LOGE(dev, "get profile index[%d] overflow count[%d]", + index, prof_info->count); + return -ENOMEM; + } + prof_info->prof_desc[index] = prof_desc[i]; + AW_DEV_LOGI(dev, "product=[%s]----profile=[%s]", + prof_info->prof_desc[index].dev_name, + aw_get_prof_name(i)); + index++; + } + } + + ret = aw_set_prof_off_info_v_0_0_0_1(dev, acf_info, all_prof_info, index); + if (ret < 0) + return ret; + + AW_DEV_LOGD(dev, "get vaild profile succeed"); + return 0; +} + +static int aw_set_prof_name_list_v_0_0_0_1(struct device *dev, + struct acf_bin_info *acf_info) +{ + int i = 0; + int count = acf_info->prof_info.count; + struct aw_prof_info *prof_info = &acf_info->prof_info; + + prof_info->prof_name_list = (char (*)[AW_PROFILE_STR_MAX])devm_kzalloc(dev, + count * (AW_PROFILE_STR_MAX), GFP_KERNEL); + if (prof_info->prof_name_list == NULL) { + AW_DEV_LOGE(dev, "prof_name_list devm_kzalloc failed"); + return -ENOMEM; + } + + for (i = 0; i < count; ++i) { + snprintf(prof_info->prof_name_list[i], AW_PROFILE_STR_MAX, "%s", + prof_info->prof_desc[i].prof_name); + AW_DEV_LOGI(dev, "index=[%d], profile_name=[%s]", + i, prof_info->prof_name_list[i]); + } + + return 0; +} + +static int aw_parse_acf_v_0_0_0_1(struct device *dev, + struct acf_bin_info *acf_info) + +{ + int ret = 0; + struct aw_all_prof_info all_prof_info; + + AW_DEV_LOGD(dev, "enter"); + acf_info->prof_info.status = AW_ACF_WAIT; + + memset(&all_prof_info, 0, sizeof(struct aw_all_prof_info)); + + ret = aw_parse_dev_type_v_0_0_0_1(dev, acf_info, &all_prof_info); + if (ret < 0) { + return ret; + } else if (ret == AW_DEV_TYPE_NONE) { + AW_DEV_LOGD(dev, "get dev type num is 0, parse default dev type"); + ret = aw_parse_default_type_v_0_0_0_1(dev, acf_info, &all_prof_info); + if (ret < 0) + return ret; + } + + ret = aw_get_vaild_prof_v_0_0_0_1(dev, acf_info, &all_prof_info); + if (ret < 0) { + aw87xxx_acf_profile_free(dev, acf_info); + AW_DEV_LOGE(dev, "hdr_cersion[0x%x] parse failed", + acf_info->acf_hdr.hdr_version); + return ret; + } + + ret = aw_set_prof_name_list_v_0_0_0_1(dev, acf_info); + if (ret < 0) { + aw87xxx_acf_profile_free(dev, acf_info); + AW_DEV_LOGE(dev, "creat prof_id_and_name_list failed"); + return ret; + } + + acf_info->prof_info.status = AW_ACF_UPDATE; + AW_DEV_LOGI(dev, "acf parse success"); + return 0; +} + +/*************************************************************************** + * V1.0.0.0 version acf paese + **************************************************************************/ +static int aw_check_product_name_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info, + struct aw_acf_dde_v_1_0_0_0 *prof_hdr) +{ + int i = 0; + + for (i = 0; i < acf_info->product_cnt; i++) { + if (0 == strcmp(acf_info->product_tab[i], prof_hdr->dev_name)) { + AW_DEV_LOGI(dev, "bin_dev_name:%s", prof_hdr->dev_name); + return 0; + } + } + + return -ENXIO; +} + +static int aw_get_dde_type_info_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + int i; + int dev_num = 0; + int default_num = 0; + struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data; + struct aw_prof_info *prof_info = &acf_info->prof_info; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = + (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset); + + prof_info->prof_type = AW_DEV_NONE_TYPE_ID; + for (i = 0; i < acf_hdr->dde_num; i++) { + if (acf_dde[i].type == AW_DDE_DEV_TYPE_ID) + dev_num++; + if (acf_dde[i].type == AW_DDE_DEV_DEFAULT_TYPE_ID) + default_num++; + } + + if (!(dev_num || default_num)) { + AW_DEV_LOGE(dev, "can't find scene"); + return -EINVAL; + } + + if (dev_num != 0) + prof_info->prof_type = AW_DDE_DEV_TYPE_ID; + else if (default_num != 0) + prof_info->prof_type = AW_DDE_DEV_DEFAULT_TYPE_ID; + + return 0; +} + + +static int aw_parse_get_dev_type_prof_count_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = + (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset); + int i = 0; + int ret = 0; + int found_off_prof_flag = 0; + int count = acf_info->prof_info.count; + + for (i = 0; i < acf_hdr->dde_num; ++i) { + if (((acf_dde[i].data_type == AW_BIN_TYPE_REG) || + (acf_dde[i].data_type == AW_BIN_TYPE_HDR_REG)) && + ((acf_info->aw_dev->i2c_bus == acf_dde[i].dev_bus) && + (acf_info->aw_dev->i2c_addr == acf_dde[i].dev_addr)) && + (acf_info->aw_dev->chipid == acf_dde[i].chip_id)) { + + ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]); + if (ret < 0) + continue; + + ret = aw_check_prof_str_is_off(acf_dde[i].dev_profile_str); + if (ret == 0) { + found_off_prof_flag = AW_PROFILE_OK; + } + count++; + } + } + + if (count == 0) { + AW_DEV_LOGE(dev, "can't find profile"); + return -EINVAL; + } + + if (!found_off_prof_flag) { + count++; + AW_DEV_LOGD(dev, "set no config power off profile in count"); + } + + acf_info->prof_info.count = count; + AW_DEV_LOGI(dev, "profile dev_type profile count is %d", acf_info->prof_info.count); + return 0; +} + +static int aw_parse_get_default_type_prof_count_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = + (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset); + int i = 0; + int ret = 0; + int found_off_prof_flag = 0; + int count = acf_info->prof_info.count; + + for (i = 0; i < acf_hdr->dde_num; ++i) { + if (((acf_dde[i].data_type == AW_BIN_TYPE_REG) || + (acf_dde[i].data_type == AW_BIN_TYPE_HDR_REG)) && + (acf_info->dev_index == acf_dde[i].dev_index) && + (acf_info->aw_dev->chipid == acf_dde[i].chip_id)) { + + ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]); + if (ret < 0) + continue; + + ret = aw_check_prof_str_is_off(acf_dde[i].dev_profile_str); + if (ret == 0) { + found_off_prof_flag = AW_PROFILE_OK; + } + count++; + } + } + + if (count == 0) { + AW_DEV_LOGE(dev, "can't find profile"); + return -EINVAL; + } + + if (!found_off_prof_flag) { + count++; + AW_DEV_LOGD(dev, "set no config power off profile in count"); + } + + acf_info->prof_info.count = count; + AW_DEV_LOGI(dev, "profile default_type profile count is %d", acf_info->prof_info.count); + return 0; +} + +static int aw_parse_get_profile_count_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + int ret = 0; + + ret = aw_get_dde_type_info_v_1_0_0_0(dev, acf_info); + if (ret < 0) + return ret; + + if (acf_info->prof_info.prof_type == AW_DDE_DEV_TYPE_ID) { + ret = aw_parse_get_dev_type_prof_count_v_1_0_0_0(dev, acf_info); + if (ret < 0) { + AW_DEV_LOGE(dev, "parse dev_type profile count failed"); + return ret; + } + } else if (acf_info->prof_info.prof_type == AW_DDE_DEV_DEFAULT_TYPE_ID) { + ret = aw_parse_get_default_type_prof_count_v_1_0_0_0(dev, acf_info); + if (ret < 0) { + AW_DEV_LOGE(dev, "parse default_type profile count failed"); + return ret; + } + } else { + AW_DEV_LOGE(dev, "unsupport prof_type[0x%x]", + acf_info->prof_info.prof_type); + return -EINVAL; + } + + AW_DEV_LOGI(dev, "profile count is %d", acf_info->prof_info.count); + return 0; +} + +static int aw_parse_dev_type_prof_name_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = + (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset); + struct aw_prof_info *prof_info = &acf_info->prof_info; + int i, ret, list_index = 0; + + for (i = 0; i < acf_hdr->dde_num; ++i) { + if (((acf_dde[i].data_type == AW_BIN_TYPE_REG) || + (acf_dde[i].data_type == AW_BIN_TYPE_HDR_REG)) && + (acf_info->aw_dev->i2c_bus == acf_dde[i].dev_bus) && + (acf_info->aw_dev->i2c_addr == acf_dde[i].dev_addr) && + (acf_info->aw_dev->chipid == acf_dde[i].chip_id)) { + if (list_index > prof_info->count) { + AW_DEV_LOGE(dev, "%s:Alrealdy set list_index [%d], redundant profile [%s]exist\n", + __func__, list_index, + acf_dde[i].dev_profile_str); + return -EINVAL; + } + + ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]); + if (ret < 0) + continue; + + snprintf(prof_info->prof_name_list[list_index], AW_PROFILE_STR_MAX, "%s", + acf_dde[i].dev_profile_str); + AW_DEV_LOGI(dev, "profile_name=[%s]", + prof_info->prof_name_list[list_index]); + list_index++; + } + } + + return 0; +} + +static int aw_parse_default_type_prof_name_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = + (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset); + struct aw_prof_info *prof_info = &acf_info->prof_info; + int i, ret, list_index = 0; + + for (i = 0; i < acf_hdr->dde_num; ++i) { + if (((acf_dde[i].data_type == AW_BIN_TYPE_REG) || + (acf_dde[i].data_type == AW_BIN_TYPE_HDR_REG)) && + (acf_info->dev_index == acf_dde[i].dev_index) && + (acf_info->aw_dev->chipid == acf_dde[i].chip_id)) { + if (list_index > prof_info->count) { + AW_DEV_LOGE(dev, "%s:Alrealdy set list_index [%d], redundant profile [%s]exist\n", + __func__, list_index, + acf_dde[i].dev_profile_str); + return -EINVAL; + } + + ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]); + if (ret < 0) + continue; + + snprintf(prof_info->prof_name_list[list_index], AW_PROFILE_STR_MAX, "%s", + acf_dde[i].dev_profile_str); + AW_DEV_LOGI(dev, "profile_name=[%s]", + prof_info->prof_name_list[list_index]); + list_index++; + } + } + + return 0; +} + +static int aw_parse_prof_name_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + int ret = 0; + int count = acf_info->prof_info.count; + struct aw_prof_info *prof_info = &acf_info->prof_info; + + prof_info->prof_name_list = (char (*)[AW_PROFILE_STR_MAX])devm_kzalloc(dev, + count * (AW_PROFILE_STR_MAX), GFP_KERNEL); + if (prof_info->prof_name_list == NULL) { + AW_DEV_LOGE(dev, "prof_name_list devm_kzalloc failed"); + return -ENOMEM; + } + + if (acf_info->prof_info.prof_type == AW_DDE_DEV_TYPE_ID) { + ret = aw_parse_dev_type_prof_name_v_1_0_0_0(dev, acf_info); + if (ret < 0) { + AW_DEV_LOGE(dev, "parse dev_type profile count failed"); + return ret; + } + } else if (acf_info->prof_info.prof_type == AW_DDE_DEV_DEFAULT_TYPE_ID) { + ret = aw_parse_default_type_prof_name_v_1_0_0_0(dev, acf_info); + if (ret < 0) { + AW_DEV_LOGE(dev, "parse default_type profile count failed"); + return ret; + } + } else { + AW_DEV_LOGE(dev, "unsupport prof_type[0x%x]", + acf_info->prof_info.prof_type); + return -EINVAL; + } + + AW_DEV_LOGI(dev, "profile name parse succeed"); + return 0; +} + + +static int aw_search_prof_index_from_list_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info, + struct aw_prof_desc **prof_desc, + struct aw_acf_dde_v_1_0_0_0 *prof_hdr) +{ + int i = 0; + int count = acf_info->prof_info.count; + char (*prof_name_list)[AW_PROFILE_STR_MAX] = acf_info->prof_info.prof_name_list; + + for (i = 0; i < count; i++) { + if (!strncmp(prof_name_list[i], prof_hdr->dev_profile_str, AW_PROFILE_STR_MAX)) { + *prof_desc = &(acf_info->prof_info.prof_desc[i]); + return 0; + } + } + + if (i == count) + AW_DEV_LOGE(dev, "not find prof_id and prof_name in list"); + + return -EINVAL; +} + +static int aw_parse_data_by_sec_type_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info, + struct aw_acf_dde_v_1_0_0_0 *prof_hdr) +{ + int ret = -1; + char *cfg_data = acf_info->fw_data + prof_hdr->data_offset; + struct aw_prof_desc *prof_desc = NULL; + + ret = aw_search_prof_index_from_list_v_1_0_0_0(dev, acf_info, &prof_desc, prof_hdr); + if (ret < 0) + return ret; + + switch (prof_hdr->data_type) { + case AW_BIN_TYPE_REG: + snprintf(prof_desc->dev_name, sizeof(prof_hdr->dev_name), + "%s", prof_hdr->dev_name); + AW_DEV_LOGI(dev, "parse reg type data enter,product=[%s],prof_id=[%d],prof_name=[%s]", + prof_hdr->dev_name, prof_hdr->dev_profile, + prof_hdr->dev_profile_str); + prof_desc->prof_name = prof_hdr->dev_profile_str; + ret = aw_parse_raw_reg(dev, cfg_data, prof_hdr->data_size, + prof_desc); + break; + case AW_BIN_TYPE_HDR_REG: + snprintf(prof_desc->dev_name, sizeof(prof_hdr->dev_name), + "%s", prof_hdr->dev_name); + AW_DEV_LOGI(dev, "parse hdr_reg type data enter,product=[%s],prof_id=[%d],prof_name=[%s]", + prof_hdr->dev_name, prof_hdr->dev_profile, + prof_hdr->dev_profile_str); + prof_desc->prof_name = prof_hdr->dev_profile_str; + ret = aw_parse_reg_with_hdr(dev, cfg_data, + prof_hdr->data_size, prof_desc); + break; + } + + return ret; +} + +static int aw_parse_dev_type_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + int i = 0; + int ret; + int parse_prof_count = 0; + char *cfg_data = NULL; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = + (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_info->acf_hdr.ddt_offset); + + AW_DEV_LOGD(dev, "enter"); + + for (i = 0; i < acf_info->acf_hdr.dde_num; i++) { + if ((acf_dde[i].type == AW_DDE_DEV_TYPE_ID) && + (acf_info->aw_dev->i2c_bus == acf_dde[i].dev_bus) && + (acf_info->aw_dev->i2c_addr == acf_dde[i].dev_addr) && + (acf_info->aw_dev->chipid == acf_dde[i].chip_id)) { + ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]); + if (ret < 0) + continue; + + if (acf_dde[i].data_type == AW_MONITOR) { + cfg_data = acf_info->fw_data + acf_dde[i].data_offset; + AW_DEV_LOGD(dev, "parse monitor type data enter"); + ret = aw_parse_monitor_config(dev, cfg_data, + acf_dde[i].data_size); + } else { + ret = aw_parse_data_by_sec_type_v_1_0_0_0(dev, acf_info, + &acf_dde[i]); + if (ret < 0) + AW_DEV_LOGE(dev, "parse dev type data failed"); + else + parse_prof_count++; + } + } + } + + if (parse_prof_count == 0) { + AW_DEV_LOGE(dev, "get dev type num is %d, parse failed", parse_prof_count); + return -EINVAL; + } + + return AW_DEV_TYPE_OK; +} + +static int aw_parse_default_type_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + int i = 0; + int ret; + int parse_prof_count = 0; + char *cfg_data = NULL; + struct aw_acf_dde_v_1_0_0_0 *acf_dde = + (struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_info->acf_hdr.ddt_offset); + + AW_DEV_LOGD(dev, "enter"); + + for (i = 0; i < acf_info->acf_hdr.dde_num; i++) { + if ((acf_dde[i].type == AW_DDE_DEV_DEFAULT_TYPE_ID) && + (acf_info->dev_index == acf_dde[i].dev_index) && + (acf_info->aw_dev->chipid == acf_dde[i].chip_id)) { + ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]); + if (ret < 0) + continue; + + if (acf_dde[i].data_type == AW_MONITOR) { + cfg_data = acf_info->fw_data + acf_dde[i].data_offset; + AW_DEV_LOGD(dev, "parse monitor type data enter"); + ret = aw_parse_monitor_config(dev, cfg_data, + acf_dde[i].data_size); + } else { + ret = aw_parse_data_by_sec_type_v_1_0_0_0(dev, acf_info, + &acf_dde[i]); + if (ret < 0) + AW_DEV_LOGE(dev, "parse default type data failed"); + else + parse_prof_count++; + } + } + } + + if (parse_prof_count == 0) { + AW_DEV_LOGE(dev, "get default type num is %d,parse failed", parse_prof_count); + return -EINVAL; + } + + return AW_DEV_TYPE_OK; +} + +static int aw_parse_by_hdr_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + int ret; + + if (acf_info->prof_info.prof_type == AW_DDE_DEV_TYPE_ID) { + ret = aw_parse_dev_type_v_1_0_0_0(dev, acf_info); + if (ret < 0) + return ret; + } else if (acf_info->prof_info.prof_type == AW_DDE_DEV_DEFAULT_TYPE_ID) { + ret = aw_parse_default_type_v_1_0_0_0(dev, acf_info); + if (ret < 0) + return ret; + } + + return 0; +} + +static int aw_set_prof_off_info_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) +{ + struct aw_prof_info *prof_info = &acf_info->prof_info; + int i = 0; + int ret = 0; + + for (i = 0; i < prof_info->count; ++i) { + if (!(prof_info->prof_desc[i].prof_st)) { + snprintf(prof_info->prof_name_list[i], AW_PROFILE_STR_MAX, "%s", + g_power_off_name[0]); + prof_info->prof_desc[i].prof_name = prof_info->prof_name_list[i]; + prof_info->prof_desc[i].prof_st = AW_PROFILE_WAIT; + memset(&prof_info->prof_desc[i].data_container, 0, + sizeof(struct aw_data_container)); + return 0; + } + + ret = aw_check_prof_str_is_off(prof_info->prof_name_list[i]); + if (ret == 0) { + AW_DEV_LOGD(dev, "found profile off,data_len=[%d]", + prof_info->prof_desc[i].data_container.len); + return 0; + } + } + + AW_DEV_LOGE(dev, "index[%d] is out of table,profile count[%d]", + i, prof_info->count); + return -EINVAL; +} + +static int aw_parse_acf_v_1_0_0_0(struct device *dev, + struct acf_bin_info *acf_info) + +{ + struct aw_prof_info *prof_info = &acf_info->prof_info; + int ret; + + ret = aw_parse_get_profile_count_v_1_0_0_0(dev, acf_info); + if (ret < 0) { + AW_DEV_LOGE(dev, "get profile count failed"); + return ret; + } + + ret = aw_parse_prof_name_v_1_0_0_0(dev, acf_info); + if (ret < 0) { + AW_DEV_LOGE(dev, "get profile count failed"); + return ret; + } + + acf_info->prof_info.prof_desc = devm_kzalloc(dev, + prof_info->count * sizeof(struct aw_prof_desc), GFP_KERNEL); + if (acf_info->prof_info.prof_desc == NULL) { + AW_DEV_LOGE(dev, "prof_desc devm_kzalloc failed"); + return -ENOMEM; + } + + ret = aw_parse_by_hdr_v_1_0_0_0(dev, acf_info); + if (ret < 0) { + AW_DEV_LOGE(dev, "parse data failed"); + return ret; + } + + ret = aw_set_prof_off_info_v_1_0_0_0(dev, acf_info); + if (ret < 0) { + AW_DEV_LOGE(dev, "set profile off info failed"); + return ret; + } + + prof_info->status = AW_ACF_UPDATE; + AW_DEV_LOGI(dev, "acf paese succeed"); + return 0; +} + + +/************************************************************************* + * + *acf parse API + * + *************************************************************************/ +void aw87xxx_acf_profile_free(struct device *dev, struct acf_bin_info *acf_info) +{ + struct aw_prof_info *prof_info = &acf_info->prof_info; + + prof_info->count = 0; + prof_info->status = AW_ACF_WAIT; + memset(&acf_info->acf_hdr, 0, sizeof(struct aw_acf_hdr)); + + if (prof_info->prof_desc) { + devm_kfree(dev, prof_info->prof_desc); + prof_info->prof_desc = NULL; + } + + if (prof_info->prof_name_list) { + devm_kfree(dev, prof_info->prof_name_list); + prof_info->prof_name_list = NULL; + } + + if (acf_info->fw_data) { + vfree(acf_info->fw_data); + acf_info->fw_data = NULL; + } +} + +int aw87xxx_acf_parse(struct device *dev, struct acf_bin_info *acf_info) +{ + int ret = 0; + + AW_DEV_LOGD(dev, "enter"); + acf_info->prof_info.status = AW_ACF_WAIT; + ret = aw_check_acf_firmware(dev, acf_info->fw_data, + acf_info->fw_size); + if (ret < 0) { + AW_DEV_LOGE(dev, "load firmware check failed"); + return -EINVAL; + } + + memcpy(&acf_info->acf_hdr, acf_info->fw_data, + sizeof(struct aw_acf_hdr)); + + switch (acf_info->acf_hdr.hdr_version) { + case AW_ACF_HDR_VER_0_0_0_1: + return aw_parse_acf_v_0_0_0_1(dev, acf_info); + case AW_ACF_HDR_VER_1_0_0_0: + return aw_parse_acf_v_1_0_0_0(dev, acf_info); + default: + AW_DEV_LOGE(dev, "unsupported hdr_version [0x%x]", + acf_info->acf_hdr.hdr_version); + return -EINVAL; + } + + return ret; +} + +struct aw_prof_desc *aw87xxx_acf_get_prof_desc_form_name(struct device *dev, + struct acf_bin_info *acf_info, char *profile_name) +{ + int i = 0; + struct aw_prof_desc *prof_desc = NULL; + struct aw_prof_info *prof_info = &acf_info->prof_info; + + AW_DEV_LOGD(dev, "enter"); + + if (!acf_info->prof_info.status) { + AW_DEV_LOGE(dev, "profile_cfg not load"); + return NULL; + } + + for (i = 0; i < prof_info->count; i++) { + if (!strncmp(profile_name, prof_info->prof_desc[i].prof_name, + AW_PROFILE_STR_MAX)) { + prof_desc = &prof_info->prof_desc[i]; + break; + } + } + + if (i == prof_info->count) { + AW_DEV_LOGE(dev, "profile not found"); + return NULL; + } + + AW_DEV_LOGI(dev, "get prof desc down"); + return prof_desc; +} + +int aw87xxx_acf_get_prof_index_form_name(struct device *dev, + struct acf_bin_info *acf_info, char *profile_name) +{ + int i = 0; + struct aw_prof_info *prof_info = &acf_info->prof_info; + + if (!acf_info->prof_info.status) { + AW_DEV_LOGE(dev, "profile_cfg not load"); + return -EINVAL; + } + + for (i = 0; i < prof_info->count; i++) { + if (!strncmp(profile_name, prof_info->prof_name_list[i], + AW_PROFILE_STR_MAX)) { + return i; + } + } + + AW_DEV_LOGE(dev, "profile_index not found"); + return -EINVAL; +} + +char *aw87xxx_acf_get_prof_name_form_index(struct device *dev, + struct acf_bin_info *acf_info, int index) +{ + struct aw_prof_info *prof_info = &acf_info->prof_info; + + if (!acf_info->prof_info.status) { + AW_DEV_LOGE(dev, "profile_cfg not load"); + return NULL; + } + + if (index >= prof_info->count || index < 0) { + AW_DEV_LOGE(dev, "profile_index out of table"); + return NULL; + } + + return prof_info->prof_desc[index].prof_name; +} + + +int aw87xxx_acf_get_profile_count(struct device *dev, + struct acf_bin_info *acf_info) +{ + struct aw_prof_info *prof_info = &acf_info->prof_info; + + if (!acf_info->prof_info.status) { + AW_DEV_LOGE(dev, "profile_cfg not load"); + return -EINVAL; + } + + if (prof_info->count > 0) { + return prof_info->count; + } + + return -EINVAL; +} + +char *aw87xxx_acf_get_prof_off_name(struct device *dev, + struct acf_bin_info *acf_info) +{ + int i = 0; + int ret = 0; + struct aw_prof_info *prof_info = &acf_info->prof_info; + + if (!acf_info->prof_info.status) { + AW_DEV_LOGE(dev, "profile_cfg not load"); + return NULL; + } + + for (i = 0; i < prof_info->count; i++) { + ret = aw_check_prof_str_is_off(prof_info->prof_name_list[i]); + if (ret == 0) + return prof_info->prof_name_list[i]; + } + + return NULL; +} + +void aw87xxx_acf_init(struct aw_device *aw_dev, struct acf_bin_info *acf_info, int index) +{ + + acf_info->load_count = 0; + acf_info->prof_info.status = AW_ACF_WAIT; + acf_info->dev_index = index; + acf_info->aw_dev = aw_dev; + acf_info->product_cnt = aw_dev->product_cnt; + acf_info->product_tab = aw_dev->product_tab; + acf_info->prof_info.prof_desc = NULL; + acf_info->fw_data = NULL; + acf_info->fw_size = 0; +} + diff --git a/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_acf_bin.h b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_acf_bin.h new file mode 100644 index 000000000000..ebe0c77f5674 --- /dev/null +++ b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_acf_bin.h @@ -0,0 +1,191 @@ +#ifndef __AW87XXX_ACF_BIN_H__ +#define __AW87XXX_ACF_BIN_H__ + +#include "aw87xxx_device.h" + +#define AW_PROJECT_NAME_MAX (24) +#define AW_CUSTOMER_NAME_MAX (16) +#define AW_CFG_VERSION_MAX (4) +#define AW_TBL_VERSION_MAX (4) +#define AW_DDE_DEVICE_TYPE (0) +#define AW_DDE_SKT_TYPE (1) +#define AW_DDE_DEFAULT_TYPE (2) + +#define AW_REG_ADDR_BYTE (1) +#define AW_REG_DATA_BYTE (1) + +#define AW_ACF_FILE_ID (0xa15f908) +#define AW_PROFILE_STR_MAX (32) +#define AW_POWER_OFF_NAME_SUPPORT_COUNT (5) + +enum aw_cfg_hdr_version { + AW_ACF_HDR_VER_0_0_0_1 = 0x00000001, + AW_ACF_HDR_VER_1_0_0_0 = 0x01000000, +}; + +enum aw_acf_dde_type_id { + AW_DEV_NONE_TYPE_ID = 0xFFFFFFFF, + AW_DDE_DEV_TYPE_ID = 0x00000000, + AW_DDE_SKT_TYPE_ID = 0x00000001, + AW_DDE_DEV_DEFAULT_TYPE_ID = 0x00000002, + AW_DDE_TYPE_MAX, +}; + +enum aw_raw_data_type_id { + AW_BIN_TYPE_REG = 0x00000000, + AW_BIN_TYPE_DSP, + AW_BIN_TYPE_DSP_CFG, + AW_BIN_TYPE_DSP_FW, + AW_BIN_TYPE_HDR_REG, + AW_BIN_TYPE_HDR_DSP_CFG, + AW_BIN_TYPE_HDR_DSP_FW, + AW_BIN_TYPE_MUTLBIN, + AW_SKT_UI_PROJECT, + AW_DSP_CFG, + AW_MONITOR, + AW_BIN_TYPE_MAX, +}; + +enum { + AW_DEV_TYPE_OK = 0, + AW_DEV_TYPE_NONE = 1, +}; + +enum aw_profile_status { + AW_PROFILE_WAIT = 0, + AW_PROFILE_OK, +}; + +enum aw_acf_load_status { + AW_ACF_WAIT = 0, + AW_ACF_UPDATE, +}; + +enum aw_bin_dev_profile_id { + AW_PROFILE_MUSIC = 0x0000, + AW_PROFILE_VOICE, + AW_PROFILE_VOIP, + AW_PROFILE_RINGTONE, + AW_PROFILE_RINGTONE_HS, + AW_PROFILE_LOWPOWER, + AW_PROFILE_BYPASS, + AW_PROFILE_MMI, + AW_PROFILE_FM, + AW_PROFILE_NOTIFICATION, + AW_PROFILE_RECEIVER, + AW_PROFILE_OFF, + AW_PROFILE_MAX, +}; + +struct aw_acf_hdr { + int32_t a_id; /* acf file ID 0xa15f908 */ + char project[AW_PROJECT_NAME_MAX]; /* project name */ + char custom[AW_CUSTOMER_NAME_MAX]; /* custom name :huawei xiaomi vivo oppo */ + uint8_t version[AW_CFG_VERSION_MAX]; /* author update version */ + int32_t author_id; /* author id */ + int32_t ddt_size; /* sub section table entry size */ + int32_t dde_num; /* sub section table entry num */ + int32_t ddt_offset; /* sub section table offset in file */ + int32_t hdr_version; /* sub section table version */ + int32_t reserve[3]; /* Reserved Bits */ +}; + +struct aw_acf_dde { + int32_t type; /* dde type id */ + char dev_name[AW_CUSTOMER_NAME_MAX]; /* customer dev name */ + int16_t dev_index; /* dev id */ + int16_t dev_bus; /* dev bus id */ + int16_t dev_addr; /* dev addr id */ + int16_t dev_profile; /* dev profile id */ + int32_t data_type; /* data type id */ + int32_t data_size; /* dde data size in block */ + int32_t data_offset; /* dde data offset in block */ + int32_t data_crc; /* dde data crc checkout */ + int32_t reserve[5]; /* Reserved Bits */ +}; + +struct aw_acf_dde_v_1_0_0_0 { + uint32_t type; /* DDE type id */ + char dev_name[AW_CUSTOMER_NAME_MAX]; /* customer dev name */ + uint16_t dev_index; /* dev id */ + uint16_t dev_bus; /* dev bus id */ + uint16_t dev_addr; /* dev addr id */ + uint16_t dev_profile; /* dev profile id*/ + uint32_t data_type; /* data type id */ + uint32_t data_size; /* dde data size in block */ + uint32_t data_offset; /* dde data offset in block */ + uint32_t data_crc; /* dde data crc checkout */ + char dev_profile_str[AW_PROFILE_STR_MAX]; /* dde custom profile name */ + uint32_t chip_id; /* dde custom product chip id */ + uint32_t reserve[4]; +}; + +struct aw_data_with_header { + uint32_t check_sum; + uint32_t header_ver; + uint32_t bin_data_type; + uint32_t bin_data_ver; + uint32_t bin_data_size; + uint32_t ui_ver; + char product[8]; + uint32_t addr_byte_len; + uint32_t data_byte_len; + uint32_t device_addr; + uint32_t reserve[4]; +}; + +struct aw_data_container { + uint32_t len; + uint8_t *data; +}; + +struct aw_prof_desc { + uint32_t prof_st; + char *prof_name; + char dev_name[AW_CUSTOMER_NAME_MAX]; + struct aw_data_container data_container; +}; + +struct aw_all_prof_info { + struct aw_prof_desc prof_desc[AW_PROFILE_MAX]; +}; + +struct aw_prof_info { + int count; + int status; + int prof_type; + char (*prof_name_list)[AW_PROFILE_STR_MAX]; + struct aw_prof_desc *prof_desc; +}; + +struct acf_bin_info { + int load_count; + int fw_size; + int16_t dev_index; + char *fw_data; + int product_cnt; + const char **product_tab; + struct aw_device *aw_dev; + + struct aw_acf_hdr acf_hdr; + struct aw_prof_info prof_info; +}; + +char *aw87xxx_ctos_get_prof_name(int profile_id); +void aw87xxx_acf_profile_free(struct device *dev, + struct acf_bin_info *acf_info); +int aw87xxx_acf_parse(struct device *dev, struct acf_bin_info *acf_info); +struct aw_prof_desc *aw87xxx_acf_get_prof_desc_form_name(struct device *dev, + struct acf_bin_info *acf_info, char *profile_name); +int aw87xxx_acf_get_prof_index_form_name(struct device *dev, + struct acf_bin_info *acf_info, char *profile_name); +char *aw87xxx_acf_get_prof_name_form_index(struct device *dev, + struct acf_bin_info *acf_info, int index); +int aw87xxx_acf_get_profile_count(struct device *dev, + struct acf_bin_info *acf_info); +char *aw87xxx_acf_get_prof_off_name(struct device *dev, + struct acf_bin_info *acf_info); +void aw87xxx_acf_init(struct aw_device *aw_dev, struct acf_bin_info *acf_info, int index); + + +#endif diff --git a/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_bin_parse.c b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_bin_parse.c new file mode 100644 index 000000000000..7eab9efde147 --- /dev/null +++ b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_bin_parse.c @@ -0,0 +1,515 @@ +/* +* aw87xxx_bin_parse.c +* +* Copyright (c) 2020 AWINIC Technology CO., LTD +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw87xxx_bin_parse.h" + +#define AWINIC_CODE_VERSION "V0.0.7-V1.0.4" /* "code version"-"excel version" */ + +#define DEBUG_LOG_LEVEL +#ifdef DEBUG_LOG_LEVEL +#define DBG(fmt, arg...) do {\ +printk("AWINIC_BIN %s,line= %d,"fmt, __func__, __LINE__, ##arg);\ +} while (0) +#define DBG_ERR(fmt, arg...) do {\ +printk("AWINIC_BIN_ERR %s,line= %d,"fmt, __func__, __LINE__, ##arg);\ +} while (0) +#else +#define DBG(fmt, arg...) do {} while (0) +#define DBG_ERR(fmt, arg...) do {} while (0) +#endif + +#define printing_data_code + +typedef unsigned short int aw_uint16; +typedef unsigned long int aw_uint32; + +#define BigLittleSwap16(A) ((((aw_uint16)(A) & 0xff00) >> 8) | \ + (((aw_uint16)(A) & 0x00ff) << 8)) + +#define BigLittleSwap32(A) ((((aw_uint32)(A) & 0xff000000) >> 24) | \ + (((aw_uint32)(A) & 0x00ff0000) >> 8) | \ + (((aw_uint32)(A) & 0x0000ff00) << 8) | \ + (((aw_uint32)(A) & 0x000000ff) << 24)) + + +static int aw_parse_bin_header_1_0_0(struct aw_bin *bin); + +/** +* +* Interface function +* +* return value: +* value = 0 :success; +* value = -1 :check bin header version +* value = -2 :check bin data type +* value = -3 :check sum or check bin data len error +* value = -4 :check data version +* value = -5 :check register num +* value = -6 :check dsp reg num +* value = -7 :check soc app num +* value = -8 :bin is NULL point +* +**/ + +/******************************************************** +* +* check sum data +* +********************************************************/ +static int aw_check_sum(struct aw_bin *bin, int bin_num) +{ + unsigned int i = 0; + unsigned int sum_data = 0; + unsigned int check_sum = 0; + unsigned char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr - + bin->header_info[bin_num].header_len)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + check_sum = GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + + for (i = 4; + i < + bin->header_info[bin_num].bin_data_len + + bin->header_info[bin_num].header_len; i++) { + sum_data += *(p_check_sum + i); + } + DBG("aw_bin_parse bin_num=%d, check_sum = 0x%x, sum_data = 0x%x\n", + bin_num, check_sum, sum_data); + if (sum_data != check_sum) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse check sum or check bin data len error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, check_sum = 0x%x, sum_data = 0x%x\n", bin_num, check_sum, sum_data); + return -3; + } + p_check_sum = NULL; + + return 0; +} + +static int aw_check_data_version(struct aw_bin *bin, int bin_num) +{ + int i = 0; + DBG("enter\n"); + + for (i = DATA_VERSION_V1; i < DATA_VERSION_MAX; i++) { + if (bin->header_info[bin_num].bin_data_ver == i) { + return 0; + } + } + DBG_ERR("aw_bin_parse Unrecognized this bin data version\n"); + return -4; +} + +static int aw_check_register_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_register_num = 0; + unsigned int parse_register_num = 0; + unsigned char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + parse_register_num = GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + check_register_num = (bin->header_info[bin_num].bin_data_len - 4) / + (bin->header_info[bin_num].reg_byte_len + + bin->header_info[bin_num].data_byte_len); + DBG + ("aw_bin_parse bin_num=%d, parse_register_num = 0x%x, check_register_num = 0x%x\n", + bin_num, parse_register_num, check_register_num); + if (parse_register_num != check_register_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse register num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_register_num = 0x%x, check_register_num = 0x%x\n", bin_num, parse_register_num, check_register_num); + return -5; + } + bin->header_info[bin_num].reg_num = parse_register_num; + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 4; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 4; + return 0; +} + +static int aw_check_dsp_reg_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_dsp_reg_num = 0; + unsigned int parse_dsp_reg_num = 0; + unsigned char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + parse_dsp_reg_num = GET_32_DATA(*(p_check_sum + 7), + *(p_check_sum + 6), + *(p_check_sum + 5), *(p_check_sum + 4)); + bin->header_info[bin_num].reg_data_byte_len = + GET_32_DATA(*(p_check_sum + 11), *(p_check_sum + 10), + *(p_check_sum + 9), *(p_check_sum + 8)); + check_dsp_reg_num = + (bin->header_info[bin_num].bin_data_len - + 12) / bin->header_info[bin_num].reg_data_byte_len; + DBG + ("aw_bin_parse bin_num=%d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x\n", + bin_num, parse_dsp_reg_num, check_dsp_reg_num); + if (parse_dsp_reg_num != check_dsp_reg_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse dsp reg num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x\n", bin_num, parse_dsp_reg_num, check_dsp_reg_num); + return -6; + } + bin->header_info[bin_num].download_addr = + GET_32_DATA(*(p_check_sum + 3), *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + bin->header_info[bin_num].reg_num = parse_dsp_reg_num; + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 12; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 12; + return 0; +} + +static int aw_check_soc_app_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_soc_app_num = 0; + unsigned int parse_soc_app_num = 0; + unsigned char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + bin->header_info[bin_num].app_version = GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), + *(p_check_sum)); + parse_soc_app_num = GET_32_DATA(*(p_check_sum + 11), + *(p_check_sum + 10), + *(p_check_sum + 9), *(p_check_sum + 8)); + check_soc_app_num = bin->header_info[bin_num].bin_data_len - 12; + DBG + ("aw_bin_parse bin_num=%d, parse_soc_app_num = 0x%x, check_soc_app_num = 0x%x\n", + bin_num, parse_soc_app_num, check_soc_app_num); + if (parse_soc_app_num != check_soc_app_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse soc app num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_soc_app_num = 0x%x, check_soc_app_num = 0x%x\n", bin_num, parse_soc_app_num, check_soc_app_num); + return -7; + } + bin->header_info[bin_num].reg_num = parse_soc_app_num; + bin->header_info[bin_num].download_addr = + GET_32_DATA(*(p_check_sum + 7), *(p_check_sum + 6), + *(p_check_sum + 5), *(p_check_sum + 4)); + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 12; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 12; + return 0; +} + +/************************ +*** +***bin header 1_0_0 +*** +************************/ +static void aw_get_single_bin_header_1_0_0(struct aw_bin *bin) +{ + int i; + DBG("enter %s\n", __func__); + bin->header_info[bin->all_bin_parse_num].header_len = 60; + bin->header_info[bin->all_bin_parse_num].check_sum = + GET_32_DATA(*(bin->p_addr + 3), *(bin->p_addr + 2), + *(bin->p_addr + 1), *(bin->p_addr)); + bin->header_info[bin->all_bin_parse_num].header_ver = + GET_32_DATA(*(bin->p_addr + 7), *(bin->p_addr + 6), + *(bin->p_addr + 5), *(bin->p_addr + 4)); + bin->header_info[bin->all_bin_parse_num].bin_data_type = + GET_32_DATA(*(bin->p_addr + 11), *(bin->p_addr + 10), + *(bin->p_addr + 9), *(bin->p_addr + 8)); + bin->header_info[bin->all_bin_parse_num].bin_data_ver = + GET_32_DATA(*(bin->p_addr + 15), *(bin->p_addr + 14), + *(bin->p_addr + 13), *(bin->p_addr + 12)); + bin->header_info[bin->all_bin_parse_num].bin_data_len = + GET_32_DATA(*(bin->p_addr + 19), *(bin->p_addr + 18), + *(bin->p_addr + 17), *(bin->p_addr + 16)); + bin->header_info[bin->all_bin_parse_num].ui_ver = + GET_32_DATA(*(bin->p_addr + 23), *(bin->p_addr + 22), + *(bin->p_addr + 21), *(bin->p_addr + 20)); + bin->header_info[bin->all_bin_parse_num].reg_byte_len = + GET_32_DATA(*(bin->p_addr + 35), *(bin->p_addr + 34), + *(bin->p_addr + 33), *(bin->p_addr + 32)); + bin->header_info[bin->all_bin_parse_num].data_byte_len = + GET_32_DATA(*(bin->p_addr + 39), *(bin->p_addr + 38), + *(bin->p_addr + 37), *(bin->p_addr + 36)); + bin->header_info[bin->all_bin_parse_num].device_addr = + GET_32_DATA(*(bin->p_addr + 43), *(bin->p_addr + 42), + *(bin->p_addr + 41), *(bin->p_addr + 40)); + for (i = 0; i < 8; i++) { + bin->header_info[bin->all_bin_parse_num].chip_type[i] = + *(bin->p_addr + 24 + i); + } + bin->header_info[bin->all_bin_parse_num].reg_num = 0x00000000; + bin->header_info[bin->all_bin_parse_num].reg_data_byte_len = 0x00000000; + bin->header_info[bin->all_bin_parse_num].download_addr = 0x00000000; + bin->header_info[bin->all_bin_parse_num].app_version = 0x00000000; + bin->header_info[bin->all_bin_parse_num].valid_data_len = 0x00000000; + bin->all_bin_parse_num += 1; +} + +static int aw_parse_each_of_multi_bins_1_0_0(unsigned int bin_num, int bin_serial_num, + struct aw_bin *bin) +{ + int ret = 0; + unsigned int bin_start_addr = 0; + unsigned int valid_data_len = 0; + DBG("aw_bin_parse enter multi bin branch -- %s\n", __func__); + if (!bin_serial_num) { + bin_start_addr = GET_32_DATA(*(bin->p_addr + 67), + *(bin->p_addr + 66), + *(bin->p_addr + 65), + *(bin->p_addr + 64)); + bin->p_addr += (60 + bin_start_addr); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + bin->header_info[bin->all_bin_parse_num - + 1].valid_data_addr + 4 + 8 * bin_num + 60; + } else { + valid_data_len = + bin->header_info[bin->all_bin_parse_num - 1].bin_data_len; + bin->p_addr += (60 + valid_data_len); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + bin->header_info[bin->all_bin_parse_num - + 1].valid_data_addr + + bin->header_info[bin->all_bin_parse_num - 1].bin_data_len + + 60; + } + + ret = aw_parse_bin_header_1_0_0(bin); + return ret; +} + +/* Get the number of bins in multi bins, and set a for loop, loop processing each bin data */ +static int aw_get_multi_bin_header_1_0_0(struct aw_bin *bin) +{ + int i = 0; + int ret = 0; + unsigned int bin_num = 0; + DBG("aw_bin_parse enter multi bin branch -- %s\n", __func__); + bin_num = GET_32_DATA(*(bin->p_addr + 63), + *(bin->p_addr + 62), + *(bin->p_addr + 61), *(bin->p_addr + 60)); + if (bin->multi_bin_parse_num == 1) { + bin->header_info[bin->all_bin_parse_num].valid_data_addr = 60; + } + aw_get_single_bin_header_1_0_0(bin); + + for (i = 0; i < bin_num; i++) { + DBG("aw_bin_parse enter multi bin for is %d\n", i); + ret = aw_parse_each_of_multi_bins_1_0_0(bin_num, i, bin); + if (ret < 0) { + return ret; + } + } + return 0; +} + +/******************************************************** +* +* If the bin framework header version is 1.0.0, + determine the data type of bin, and then perform different processing + according to the data type + If it is a single bin data type, write the data directly into the structure array + If it is a multi-bin data type, first obtain the number of bins, + and then recursively call the bin frame header processing function + according to the bin number to process the frame header information of each bin separately +* +********************************************************/ +static int aw_parse_bin_header_1_0_0(struct aw_bin *bin) +{ + int ret = 0; + unsigned int bin_data_type; + DBG("enter %s\n", __func__); + bin_data_type = GET_32_DATA(*(bin->p_addr + 11), + *(bin->p_addr + 10), + *(bin->p_addr + 9), *(bin->p_addr + 8)); + DBG("aw_bin_parse bin_data_type 0x%x\n", bin_data_type); + switch (bin_data_type) { + case DATA_TYPE_REGISTER: + case DATA_TYPE_DSP_REG: + case DATA_TYPE_SOC_APP: + /* Divided into two processing methods, + one is single bin processing, + and the other is single bin processing in multi bin */ + DBG("aw_bin_parse enter single bin branch\n"); + bin->single_bin_parse_num += 1; + DBG("%s bin->single_bin_parse_num is %d\n", __func__, + bin->single_bin_parse_num); + if (!bin->multi_bin_parse_num) { + bin->header_info[bin-> + all_bin_parse_num].valid_data_addr = + 60; + } + aw_get_single_bin_header_1_0_0(bin); + break; + case DATA_TYPE_MULTI_BINS: + /* Get the number of times to enter multi bins */ + DBG("aw_bin_parse enter multi bin branch\n"); + bin->multi_bin_parse_num += 1; + DBG("%s bin->multi_bin_parse_num is %d\n", __func__, + bin->multi_bin_parse_num); + ret = aw_get_multi_bin_header_1_0_0(bin); + if (ret < 0) { + return ret; + } + break; + default: + DBG_ERR("aw_bin_parse Unrecognized this bin data type\n"); + return -2; + } + return 0; +} + +/* get the bin's header version */ +static int aw_check_bin_header_version(struct aw_bin *bin) +{ + int ret = 0; + unsigned int header_version = 0; + + header_version = GET_32_DATA(*(bin->p_addr + 7), + *(bin->p_addr + 6), + *(bin->p_addr + 5), *(bin->p_addr + 4)); + + DBG("aw_bin_parse header_version 0x%x\n", header_version); + + /* Write data to the corresponding structure array + according to different formats of the bin frame header version */ + switch (header_version) { + case HEADER_VERSION_1_0_0: + ret = aw_parse_bin_header_1_0_0(bin); + return ret; + default: + DBG_ERR("aw_bin_parse Unrecognized this bin header version \n"); + return -1; + } +} + +int aw87xxx_parsing_bin_file(struct aw_bin *bin) +{ + int i = 0; + int ret = 0; + + DBG("aw_bin_parse code version:%s\n", AWINIC_CODE_VERSION); + if (!bin) { + DBG_ERR("aw_bin_parse bin is NULL\n"); + return -8; + } + bin->p_addr = bin->info.data; + bin->all_bin_parse_num = 0; + bin->multi_bin_parse_num = 0; + bin->single_bin_parse_num = 0; + + /* filling bins header info */ + ret = aw_check_bin_header_version(bin); + if (ret < 0) { + DBG_ERR("aw_bin_parse check bin header version error\n"); + return ret; + } + bin->p_addr = NULL; + + /* check bin header info */ + for (i = 0; i < bin->all_bin_parse_num; i++) { + /* check sum */ + ret = aw_check_sum(bin, i); + if (ret < 0) { + DBG_ERR("aw_bin_parse check sum data error\n"); + return ret; + } + /* check bin data version */ + ret = aw_check_data_version(bin, i); + if (ret < 0) { + DBG_ERR("aw_bin_parse check data version error\n"); + return ret; + } + /* check valid data */ + if (bin->header_info[i].bin_data_ver == DATA_VERSION_V1) { + /* check register num */ + if (bin->header_info[i].bin_data_type == + DATA_TYPE_REGISTER) { + ret = aw_check_register_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check register num error\n"); + return ret; + } + /* check dsp reg num */ + } else if (bin->header_info[i].bin_data_type == + DATA_TYPE_DSP_REG) { + ret = aw_check_dsp_reg_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check dsp reg num error\n"); + return ret; + } + /* check soc app num */ + } else if (bin->header_info[i].bin_data_type == + DATA_TYPE_SOC_APP) { + ret = aw_check_soc_app_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check soc app num error\n"); + return ret; + } + } else { + bin->header_info[i].valid_data_len = + bin->header_info[i].bin_data_len; + } + } + } + DBG("aw_bin_parse parsing success\n"); + + return 0; +} diff --git a/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_bin_parse.h b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_bin_parse.h new file mode 100644 index 000000000000..c6e6eaa8a7f7 --- /dev/null +++ b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_bin_parse.h @@ -0,0 +1,73 @@ +#ifndef __AW87XXX_BIN_PARSE_H__ +#define __AW87XXX_BIN_PARSE_H__ + +#define NULL ((void *)0) +#define GET_32_DATA(w, x, y, z) ((unsigned int)(((w) << 24) | ((x) << 16) | ((y) << 8) | (z))) +#define BIN_NUM_MAX 100 +#define HEADER_LEN 60 +/********************************************************* + * + * header information + * + ********************************************************/ +enum bin_header_version_enum { + HEADER_VERSION_1_0_0 = 0x01000000, +}; + +enum data_type_enum { + DATA_TYPE_REGISTER = 0x00000000, + DATA_TYPE_DSP_REG = 0x00000010, + DATA_TYPE_DSP_CFG = 0x00000011, + DATA_TYPE_SOC_REG = 0x00000020, + DATA_TYPE_SOC_APP = 0x00000021, + DATA_TYPE_MULTI_BINS = 0x00002000, + DATA_TYPE_MONITOR_ANALOG = 0x00020000, +}; + +enum data_version_enum { + DATA_VERSION_V1 = 0X00000001, /*default little edian */ + DATA_VERSION_MAX, +}; + +struct bin_header_info { + unsigned int header_len; /* Frame header length */ + unsigned int check_sum; /* Frame header information-Checksum */ + unsigned int header_ver; /* Frame header information-Frame header version */ + unsigned int bin_data_type; /* Frame header information-Data type */ + unsigned int bin_data_ver; /* Frame header information-Data version */ + unsigned int bin_data_len; /* Frame header information-Data length */ + unsigned int ui_ver; /* Frame header information-ui version */ + unsigned char chip_type[8]; /* Frame header information-chip type */ + unsigned int reg_byte_len; /* Frame header information-reg byte len */ + unsigned int data_byte_len; /* Frame header information-data byte len */ + unsigned int device_addr; /* Frame header information-device addr */ + unsigned int valid_data_len; /* Length of valid data obtained after parsing */ + unsigned int valid_data_addr; /* The offset address of the valid data obtained after parsing relative to info */ + + unsigned int reg_num; /* The number of registers obtained after parsing */ + unsigned int reg_data_byte_len; /* The byte length of the register obtained after parsing */ + unsigned int download_addr; /* The starting address or download address obtained after parsing */ + unsigned int app_version; /* The software version number obtained after parsing */ +}; + +/************************************************************ +* +* function define +* +************************************************************/ +struct bin_container { + unsigned int len; /* The size of the bin file obtained from the firmware */ + unsigned char data[]; /* Store the bin file obtained from the firmware */ +}; + +struct aw_bin { + unsigned char *p_addr; /* Offset pointer (backward offset pointer to obtain frame header information and important information) */ + unsigned int all_bin_parse_num; /* The number of all bin files */ + unsigned int multi_bin_parse_num; /* The number of single bin files */ + unsigned int single_bin_parse_num; /* The number of multiple bin files */ + struct bin_header_info header_info[BIN_NUM_MAX]; /* Frame header information and other important data obtained after parsing */ + struct bin_container info; /* Obtained bin file data that needs to be parsed */ +}; + +extern int aw87xxx_parsing_bin_file(struct aw_bin *bin); +#endif diff --git a/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_device.c b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_device.c new file mode 100644 index 000000000000..12b9941133ba --- /dev/null +++ b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_device.c @@ -0,0 +1,1018 @@ +/* + * aw87xxx_device.c aw87xxx pa module + * + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + * Author: Barry + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw87xxx.h" +#include "aw87xxx_device.h" +#include "aw87xxx_log.h" +#include "aw87xxx_pid_9b_reg.h" +#include "aw87xxx_pid_18_reg.h" +#include "aw87xxx_pid_39_reg.h" +#include "aw87xxx_pid_59_3x9_reg.h" +#include "aw87xxx_pid_59_5x9_reg.h" +#include "aw87xxx_pid_5a_reg.h" +#include "aw87xxx_pid_76_reg.h" +#include "aw87xxx_pid_60_reg.h" + +/************************************************************************* + * aw87xxx variable + ************************************************************************/ +const char *g_aw_pid_9b_product[] = { + "aw87319", +}; +const char *g_aw_pid_18_product[] = { + "aw87358", +}; + +const char *g_aw_pid_39_product[] = { + "aw87329", + "aw87339", + "aw87349", +}; + +const char *g_aw_pid_59_3x9_product[] = { + "aw87359", + "aw87389", +}; + +const char *g_aw_pid_59_5x9_product[] = { + "aw87509", + "aw87519", + "aw87529", + "aw87539", +}; + +const char *g_aw_pid_5a_product[] = { + "aw87549", + "aw87559", + "aw87569", + "aw87579", + "aw81509", + "aw87579G", +}; + +const char *g_aw_pid_76_product[] = { + "aw87390", + "aw87320", + "aw87401", + "aw87360", + "aw87390G", +}; + +const char *g_aw_pid_60_product[] = { + "aw87560", + "aw87561", + "aw87562", + "aw87501", + "aw87550", +}; + +static int aw87xxx_dev_get_chipid(struct aw_device *aw_dev); + +/*************************************************************************** + * + * reading and writing of I2C bus + * + ***************************************************************************/ +int aw87xxx_dev_i2c_write_byte(struct aw_device *aw_dev, + uint8_t reg_addr, uint8_t reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + while (cnt < AW_I2C_RETRIES) { + ret = i2c_smbus_write_byte_data(aw_dev->i2c, reg_addr, reg_data); + if (ret < 0) + AW_DEV_LOGE(aw_dev->dev, "i2c_write cnt=%d error=%d", + cnt, ret); + else + break; + + cnt++; + msleep(AW_I2C_RETRY_DELAY); + } + + return ret; +} + +int aw87xxx_dev_i2c_read_byte(struct aw_device *aw_dev, + uint8_t reg_addr, uint8_t *reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + while (cnt < AW_I2C_RETRIES) { + ret = i2c_smbus_read_byte_data(aw_dev->i2c, reg_addr); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "i2c_read cnt=%d error=%d", + cnt, ret); + } else { + *reg_data = ret; + break; + } + cnt++; + msleep(AW_I2C_RETRY_DELAY); + } + + return ret; +} + +int aw87xxx_dev_i2c_read_msg(struct aw_device *aw_dev, + uint8_t reg_addr, uint8_t *data_buf, uint32_t data_len) +{ + int ret = -1; + + struct i2c_msg msg[] = { + [0] = { + .addr = aw_dev->i2c_addr, + .flags = 0, + .len = sizeof(uint8_t), + .buf = ®_addr, + }, + [1] = { + .addr = aw_dev->i2c_addr, + .flags = I2C_M_RD, + .len = data_len, + .buf = data_buf, + }, + }; + + ret = i2c_transfer(aw_dev->i2c->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "transfer failed"); + return ret; + } else if (ret != AW_I2C_READ_MSG_NUM) { + AW_DEV_LOGE(aw_dev->dev, "transfer failed(size error)"); + return -ENXIO; + } + + return 0; +} + +int aw87xxx_dev_i2c_write_bits(struct aw_device *aw_dev, + uint8_t reg_addr, uint8_t mask, uint8_t reg_data) +{ + int ret = -1; + unsigned char reg_val = 0; + + ret = aw87xxx_dev_i2c_read_byte(aw_dev, reg_addr, ®_val); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "i2c read error, ret=%d", ret); + return ret; + } + reg_val &= mask; + reg_val |= (reg_data & (~mask)); + ret = aw87xxx_dev_i2c_write_byte(aw_dev, reg_addr, reg_val); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "i2c write error, ret=%d", ret); + return ret; + } + + return 0; +} + +/************************************************************************ + * + * aw87xxx device update profile data to registers + * + ************************************************************************/ +static int aw87xxx_dev_reg_update(struct aw_device *aw_dev, + struct aw_data_container *profile_data) +{ + int i = 0; + int ret = -1; + + if (profile_data == NULL) + return -EINVAL; + + if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { + AW_DEV_LOGE(aw_dev->dev, "dev is pwr_off,can not update reg"); + return -EINVAL; + } + + for (i = 0; i < profile_data->len; i = i + 2) { + //AW_DEV_LOGI(aw_dev->dev, "reg=0x%02x, val = 0x%02x", + // profile_data->data[i], profile_data->data[i + 1]); + + //delay ms + if (profile_data->data[i] == AW87XXX_DELAY_REG_ADDR) { + AW_DEV_LOGI(aw_dev->dev, "delay %d ms", profile_data->data[i + 1]); + usleep_range(profile_data->data[i + 1] * AW87XXX_REG_DELAY_TIME, \ + profile_data->data[i + 1] * AW87XXX_REG_DELAY_TIME + 10); + continue; + } + + ret = aw87xxx_dev_i2c_write_byte(aw_dev, profile_data->data[i], + profile_data->data[i + 1]); + if (ret < 0) + return ret; + } + + return 0; +} + +static void aw87xxx_dev_reg_mute_bits_set(struct aw_device *aw_dev, + uint8_t *reg_val, bool enable) +{ + if (enable) { + *reg_val &= aw_dev->mute_desc.mask; + *reg_val |= aw_dev->mute_desc.enable; + } else { + *reg_val &= aw_dev->mute_desc.mask; + *reg_val |= aw_dev->mute_desc.disable; + } +} + +static int aw87xxx_dev_reg_update_mute(struct aw_device *aw_dev, + struct aw_data_container *profile_data) +{ + int i = 0; + int ret = -1; + uint8_t reg_val = 0; + + if (profile_data == NULL) + return -EINVAL; + + if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { + AW_DEV_LOGE(aw_dev->dev, "hwen is off,can not update reg"); + return -EINVAL; + } + + if (aw_dev->mute_desc.mask == AW_DEV_REG_INVALID_MASK) { + AW_DEV_LOGE(aw_dev->dev, "mute ctrl mask invalid"); + return -EINVAL; + } + + for (i = 0; i < profile_data->len; i = i + 2) { + AW_DEV_LOGI(aw_dev->dev, "reg=0x%02x, val = 0x%02x", + profile_data->data[i], profile_data->data[i + 1]); + //delay ms + if (profile_data->data[i] == AW87XXX_DELAY_REG_ADDR) { + AW_DEV_LOGI(aw_dev->dev, "delay %d ms", profile_data->data[i + 1]); + usleep_range(profile_data->data[i + 1] * AW87XXX_REG_DELAY_TIME, \ + profile_data->data[i + 1] * AW87XXX_REG_DELAY_TIME + 10); + continue; + } + + reg_val = profile_data->data[i + 1]; + if (profile_data->data[i] == aw_dev->mute_desc.addr) { + aw87xxx_dev_reg_mute_bits_set(aw_dev, ®_val, true); + AW_DEV_LOGD(aw_dev->dev, "change mute_mask, val = 0x%02x", + reg_val); + } + + ret = aw87xxx_dev_i2c_write_byte(aw_dev, profile_data->data[i], reg_val); + if (ret < 0) + return ret; + } + + return 0; +} + +/************************************************************************ + * + * aw87xxx device hadware and soft contols + * + ************************************************************************/ +static bool aw87xxx_dev_gpio_is_valid(struct aw_device *aw_dev) +{ + if (gpio_is_valid(aw_dev->rst_gpio)) + return true; + else + return false; +} + +void aw87xxx_dev_hw_pwr_ctrl(struct aw_device *aw_dev, bool enable) +{ + if (aw_dev->hwen_status == AW_DEV_HWEN_INVALID) { + AW_DEV_LOGD(aw_dev->dev, "product not have reset-pin,hardware pwd control invalid"); + return; + } + if (enable) { + if (aw87xxx_dev_gpio_is_valid(aw_dev)) { + gpio_set_value_cansleep(aw_dev->rst_gpio, AW_GPIO_LOW_LEVEL); + mdelay(2); + gpio_set_value_cansleep(aw_dev->rst_gpio, AW_GPIO_HIGHT_LEVEL); + mdelay(2); + aw_dev->hwen_status = AW_DEV_HWEN_ON; + AW_DEV_LOGI(aw_dev->dev, "hw power on"); + } else { + AW_DEV_LOGI(aw_dev->dev, "hw already power on"); + } + } else { + if (aw87xxx_dev_gpio_is_valid(aw_dev)) { + gpio_set_value_cansleep(aw_dev->rst_gpio, AW_GPIO_LOW_LEVEL); + mdelay(2); + aw_dev->hwen_status = AW_DEV_HWEN_OFF; + AW_DEV_LOGI(aw_dev->dev, "hw power off"); + } else { + AW_DEV_LOGI(aw_dev->dev, "hw already power off"); + } + } +} + +static int aw87xxx_dev_mute_ctrl(struct aw_device *aw_dev, bool enable) +{ + int ret = 0; + + if (enable) { + ret = aw87xxx_dev_i2c_write_bits(aw_dev, aw_dev->mute_desc.addr, + aw_dev->mute_desc.mask, aw_dev->mute_desc.enable); + if (ret < 0) + return ret; + AW_DEV_LOGI(aw_dev->dev, "set mute down"); + } else { + ret = aw87xxx_dev_i2c_write_bits(aw_dev, aw_dev->mute_desc.addr, + aw_dev->mute_desc.mask, aw_dev->mute_desc.disable); + if (ret < 0) + return ret; + AW_DEV_LOGI(aw_dev->dev, "close mute down"); + } + + return 0; +} + +void aw87xxx_dev_soft_reset(struct aw_device *aw_dev) +{ + int i = 0; + int ret = -1; + struct aw_soft_rst_desc *soft_rst = &aw_dev->soft_rst_desc; + + AW_DEV_LOGD(aw_dev->dev, "enter"); + + if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { + AW_DEV_LOGE(aw_dev->dev, "hw is off,can not softrst"); + return; + } + + if (aw_dev->soft_rst_enable == AW_DEV_SOFT_RST_DISENABLE) { + AW_DEV_LOGD(aw_dev->dev, "softrst is disenable"); + return; + } + + if (soft_rst->access == NULL || soft_rst->len == 0) { + AW_DEV_LOGE(aw_dev->dev, "softrst_info not init"); + return; + } + + if (soft_rst->len % 2) { + AW_DEV_LOGE(aw_dev->dev, "softrst data_len[%d] is odd number,data not available", + aw_dev->soft_rst_desc.len); + return; + } + + for (i = 0; i < soft_rst->len; i += 2) { + AW_DEV_LOGD(aw_dev->dev, "softrst_reg=0x%02x, val = 0x%02x", + soft_rst->access[i], soft_rst->access[i + 1]); + + ret = aw87xxx_dev_i2c_write_byte(aw_dev, soft_rst->access[i], + soft_rst->access[i + 1]); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "write failed,ret = %d,cnt=%d", + ret, i); + return; + } + } + AW_DEV_LOGD(aw_dev->dev, "down"); +} + + +int aw87xxx_dev_default_pwr_off(struct aw_device *aw_dev, + struct aw_data_container *profile_data) +{ + int ret = 0; + + AW_DEV_LOGD(aw_dev->dev, "enter"); + if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { + AW_DEV_LOGE(aw_dev->dev, "hwen is already off"); + return 0; + } + + if (aw_dev->soft_off_enable && profile_data) { + ret = aw87xxx_dev_reg_update(aw_dev, profile_data); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "update profile[Off] fw config failed"); + goto reg_off_update_failed; + } + } + + aw87xxx_dev_hw_pwr_ctrl(aw_dev, false); + AW_DEV_LOGD(aw_dev->dev, "down"); + return 0; + +reg_off_update_failed: + aw87xxx_dev_hw_pwr_ctrl(aw_dev, false); + return ret; +} + + +/************************************************************************ + * + * aw87xxx device power on process function + * + ************************************************************************/ + +int aw87xxx_dev_default_pwr_on(struct aw_device *aw_dev, + struct aw_data_container *profile_data) +{ + int ret = 0; + + /*hw power on*/ + aw87xxx_dev_hw_pwr_ctrl(aw_dev, true); + + ret = aw87xxx_dev_reg_update(aw_dev, profile_data); + if (ret < 0) + return ret; + + return 0; +} + +/**************************************************************************** + * + * aw87xxx chip esd status check + * + ****************************************************************************/ +int aw87xxx_dev_esd_reg_status_check(struct aw_device *aw_dev) +{ + int ret; + unsigned char reg_val = 0; + struct aw_esd_check_desc *esd_desc = &aw_dev->esd_desc; + + AW_DEV_LOGD(aw_dev->dev, "enter"); + + if (!esd_desc->first_update_reg_addr) { + AW_DEV_LOGE(aw_dev->dev, "esd check info if not init,please check"); + return -EINVAL; + } + + ret = aw87xxx_dev_i2c_read_byte(aw_dev, esd_desc->first_update_reg_addr, + ®_val); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "read reg 0x%02x failed", + esd_desc->first_update_reg_addr); + return ret; + } + + AW_DEV_LOGD(aw_dev->dev, "0x%02x:default val=0x%02x real val=0x%02x", + esd_desc->first_update_reg_addr, + esd_desc->first_update_reg_val, reg_val); + + if (reg_val == esd_desc->first_update_reg_val) { + AW_DEV_LOGE(aw_dev->dev, "reg status check failed"); + return -EINVAL; + } + return 0; +} + +int aw87xxx_dev_check_reg_is_rec_mode(struct aw_device *aw_dev) +{ + int ret; + unsigned char reg_val = 0; + struct aw_rec_mode_desc *rec_desc = &aw_dev->rec_desc; + + if (!rec_desc->addr) { + AW_DEV_LOGE(aw_dev->dev, "rec check info if not init,please check"); + return -EINVAL; + } + + ret = aw87xxx_dev_i2c_read_byte(aw_dev, rec_desc->addr, ®_val); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "read reg 0x%02x failed", + rec_desc->addr); + return ret; + } + + if (rec_desc->enable) { + if (reg_val & ~(rec_desc->mask)) { + AW_DEV_LOGI(aw_dev->dev, "reg status is receiver mode"); + aw_dev->is_rec_mode = AW_IS_REC_MODE; + } else { + aw_dev->is_rec_mode = AW_NOT_REC_MODE; + } + } else { + if (!(reg_val & ~(rec_desc->mask))) { + AW_DEV_LOGI(aw_dev->dev, "reg status is receiver mode"); + aw_dev->is_rec_mode = AW_IS_REC_MODE; + } else { + aw_dev->is_rec_mode = AW_NOT_REC_MODE; + } + } + return 0; +} + + +/**************************************************************************** + * + * aw87xxx product attributes init info + * + ****************************************************************************/ + +/********************** aw87xxx_pid_9A attributes ***************************/ + +static int aw_dev_pid_9b_reg_update(struct aw_device *aw_dev, + struct aw_data_container *profile_data) +{ + int i = 0; + int ret = -1; + uint8_t reg_val = 0; + + if (profile_data == NULL) + return -EINVAL; + + if (aw_dev->hwen_status == AW_DEV_HWEN_OFF) { + AW_DEV_LOGE(aw_dev->dev, "dev is pwr_off,can not update reg"); + return -EINVAL; + } + + if (profile_data->len != AW_PID_9B_BIN_REG_CFG_COUNT) { + AW_DEV_LOGE(aw_dev->dev, "reg_config count of bin is error,can not update reg"); + return -EINVAL; + } + ret = aw87xxx_dev_i2c_write_byte(aw_dev, AW87XXX_PID_9B_ENCRYPTION_REG, + AW87XXX_PID_9B_ENCRYPTION_BOOST_OUTPUT_SET); + if (ret < 0) + return ret; + + for (i = 1; i < AW_PID_9B_BIN_REG_CFG_COUNT; i++) { + AW_DEV_LOGI(aw_dev->dev, "reg=0x%02x, val = 0x%02x", + i, profile_data->data[i]); + //delay ms + if (profile_data->data[i] == AW87XXX_DELAY_REG_ADDR) { + AW_DEV_LOGI(aw_dev->dev, "delay %d ms", profile_data->data[i + 1]); + usleep_range(profile_data->data[i + 1] * AW87XXX_REG_DELAY_TIME, \ + profile_data->data[i + 1] * AW87XXX_REG_DELAY_TIME + 10); + continue; + } + + reg_val = profile_data->data[i]; + if (i == AW87XXX_PID_9B_SYSCTRL_REG) { + aw87xxx_dev_reg_mute_bits_set(aw_dev, ®_val, true); + AW_DEV_LOGD(aw_dev->dev, "change mute_mask, val = 0x%02x", + reg_val); + } + + ret = aw87xxx_dev_i2c_write_byte(aw_dev, i, reg_val); + if (ret < 0) + return ret; + } + + return 0; +} + +static int aw_dev_pid_9b_pwr_on(struct aw_device *aw_dev, struct aw_data_container *data) +{ + int ret = 0; + + /*hw power on*/ + aw87xxx_dev_hw_pwr_ctrl(aw_dev, true); + + /* open the mute */ + ret = aw87xxx_dev_mute_ctrl(aw_dev, true); + if (ret < 0) + return ret; + + /* Update scene parameters in mute mode */ + ret = aw_dev_pid_9b_reg_update(aw_dev, data); + if (ret < 0) + return ret; + + /* close the mute */ + ret = aw87xxx_dev_mute_ctrl(aw_dev, false); + if (ret < 0) + return ret; + + return 0; +} + +static void aw_dev_pid_9b_init(struct aw_device *aw_dev) +{ + /* Product register permission info */ + aw_dev->reg_max_addr = AW87XXX_PID_9B_REG_MAX; + aw_dev->reg_access = aw87xxx_pid_9b_reg_access; + + aw_dev->mute_desc.addr = AW87XXX_PID_9B_SYSCTRL_REG; + aw_dev->mute_desc.mask = AW87XXX_PID_9B_REG_EN_SW_MASK; + aw_dev->mute_desc.enable = AW87XXX_PID_9B_REG_EN_SW_DISABLE_VALUE; + aw_dev->mute_desc.disable = AW87XXX_PID_9B_REG_EN_SW_ENABLE_VALUE; + aw_dev->ops.pwr_on_func = aw_dev_pid_9b_pwr_on; + + /* software reset control info */ + aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_9b_softrst_access); + aw_dev->soft_rst_desc.access = aw87xxx_pid_9b_softrst_access; + aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; + + /* Whether to allow register operation to power off */ + aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_DISENABLE; + + aw_dev->product_tab = g_aw_pid_9b_product; + aw_dev->product_cnt = AW87XXX_PID_9B_PRODUCT_MAX; + + aw_dev->rec_desc.addr = AW87XXX_PID_9B_SYSCTRL_REG; + aw_dev->rec_desc.disable = AW87XXX_PID_9B_SPK_MODE_ENABLE; + aw_dev->rec_desc.enable = AW87XXX_PID_9B_SPK_MODE_DISABLE; + aw_dev->rec_desc.mask = AW87XXX_PID_9B_SPK_MODE_MASK; + + /* esd reg info */ + aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_9B_SYSCTRL_REG; + aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_9B_SYSCTRL_DEFAULT; + + aw_dev->vol_desc.addr = AW_REG_NONE; +} + +static int aw_dev_pid_9a_init(struct aw_device *aw_dev) +{ + int ret = 0; + + ret = aw87xxx_dev_i2c_write_byte(aw_dev, AW87XXX_PID_9B_ENCRYPTION_REG, + AW87XXX_PID_9B_ENCRYPTION_BOOST_OUTPUT_SET); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "write 0x64=0x2C error"); + return -EINVAL; + } + + ret = aw87xxx_dev_get_chipid(aw_dev); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "read chipid is failed,ret=%d", ret); + return ret; + } + + if (aw_dev->chipid == AW_DEV_CHIPID_9B) { + AW_DEV_LOGI(aw_dev->dev, "product is pid_9B class"); + aw_dev_pid_9b_init(aw_dev); + } else { + AW_DEV_LOGE(aw_dev->dev, "product is not pid_9B class,not support"); + return -EINVAL; + } + + return 0; +} + +/********************** aw87xxx_pid_9b attributes end ***********************/ + +/********************** aw87xxx_pid_18 attributes ***************************/ +static int aw_dev_pid_18_pwr_on(struct aw_device *aw_dev, struct aw_data_container *data) +{ + int ret = 0; + + /*hw power on*/ + aw87xxx_dev_hw_pwr_ctrl(aw_dev, true); + + /* open the mute */ + ret = aw87xxx_dev_mute_ctrl(aw_dev, true); + if (ret < 0) + return ret; + + /* Update scene parameters in mute mode */ + ret = aw87xxx_dev_reg_update_mute(aw_dev, data); + if (ret < 0) + return ret; + + /* close the mute */ + ret = aw87xxx_dev_mute_ctrl(aw_dev, false); + if (ret < 0) + return ret; + + return 0; +} + +static void aw_dev_chipid_18_init(struct aw_device *aw_dev) +{ + /* Product register permission info */ + aw_dev->reg_max_addr = AW87XXX_PID_18_REG_MAX; + aw_dev->reg_access = aw87xxx_pid_18_reg_access; + + aw_dev->mute_desc.addr = AW87XXX_PID_18_SYSCTRL_REG; + aw_dev->mute_desc.mask = AW87XXX_PID_18_REG_EN_SW_MASK; + aw_dev->mute_desc.enable = AW87XXX_PID_18_REG_EN_SW_DISABLE_VALUE; + aw_dev->mute_desc.disable = AW87XXX_PID_18_REG_EN_SW_ENABLE_VALUE; + aw_dev->ops.pwr_on_func = aw_dev_pid_18_pwr_on; + + /* software reset control info */ + aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_18_softrst_access); + aw_dev->soft_rst_desc.access = aw87xxx_pid_18_softrst_access; + aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; + + /* Whether to allow register operation to power off */ + aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; + + aw_dev->product_tab = g_aw_pid_18_product; + aw_dev->product_cnt = AW87XXX_PID_18_PRODUCT_MAX; + + aw_dev->rec_desc.addr = AW87XXX_PID_18_SYSCTRL_REG; + aw_dev->rec_desc.disable = AW87XXX_PID_18_REG_REC_MODE_DISABLE; + aw_dev->rec_desc.enable = AW87XXX_PID_18_REG_REC_MODE_ENABLE; + aw_dev->rec_desc.mask = AW87XXX_PID_18_REG_REC_MODE_MASK; + + /* esd reg info */ + aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_18_CLASSD_REG; + aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_18_CLASSD_DEFAULT; + + aw_dev->vol_desc.addr = AW87XXX_PID_18_CPOC_REG; +} +/********************** aw87xxx_pid_18 attributes end ***********************/ + +/********************** aw87xxx_pid_39 attributes ***************************/ +static void aw_dev_chipid_39_init(struct aw_device *aw_dev) +{ + /* Product register permission info */ + aw_dev->reg_max_addr = AW87XXX_PID_39_REG_MAX; + aw_dev->reg_access = aw87xxx_pid_39_reg_access; + + /* software reset control info */ + aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_39_softrst_access); + aw_dev->soft_rst_desc.access = aw87xxx_pid_39_softrst_access; + aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; + + /* Whether to allow register operation to power off */ + aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; + + aw_dev->product_tab = g_aw_pid_39_product; + aw_dev->product_cnt = AW87XXX_PID_39_PRODUCT_MAX; + + aw_dev->rec_desc.addr = AW87XXX_PID_39_REG_MODECTRL; + aw_dev->rec_desc.disable = AW87XXX_PID_39_REC_MODE_DISABLE; + aw_dev->rec_desc.enable = AW87XXX_PID_39_REC_MODE_ENABLE; + aw_dev->rec_desc.mask = AW87XXX_PID_39_REC_MODE_MASK; + + /* esd reg info */ + aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_39_REG_MODECTRL; + aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_39_MODECTRL_DEFAULT; + + aw_dev->vol_desc.addr = AW87XXX_PID_39_REG_CPOVP; +} +/********************* aw87xxx_pid_39 attributes end *************************/ + + +/********************* aw87xxx_pid_59_5x9 attributes *************************/ +static void aw_dev_chipid_59_5x9_init(struct aw_device *aw_dev) +{ + /* Product register permission info */ + aw_dev->reg_max_addr = AW87XXX_PID_59_5X9_REG_MAX; + aw_dev->reg_access = aw87xxx_pid_59_5x9_reg_access; + + /* software reset control info */ + aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_59_5x9_softrst_access); + aw_dev->soft_rst_desc.access = aw87xxx_pid_59_5x9_softrst_access; + aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; + + /* Whether to allow register operation to power off */ + aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; + + aw_dev->product_tab = g_aw_pid_59_5x9_product; + aw_dev->product_cnt = AW87XXX_PID_59_5X9_PRODUCT_MAX; + + aw_dev->rec_desc.addr = AW87XXX_PID_59_5X9_REG_SYSCTRL; + aw_dev->rec_desc.disable = AW87XXX_PID_59_5X9_REC_MODE_DISABLE; + aw_dev->rec_desc.enable = AW87XXX_PID_59_5X9_REC_MODE_ENABLE; + aw_dev->rec_desc.mask = AW87XXX_PID_59_5X9_REC_MODE_MASK; + + /* esd reg info */ + aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_59_5X9_REG_ENCR; + aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_59_5X9_ENCRY_DEFAULT; + + aw_dev->vol_desc.addr = AW_REG_NONE; +} +/******************* aw87xxx_pid_59_5x9 attributes end ***********************/ + +/********************* aw87xxx_pid_59_3x9 attributes *************************/ +static void aw_dev_chipid_59_3x9_init(struct aw_device *aw_dev) +{ + /* Product register permission info */ + aw_dev->reg_max_addr = AW87XXX_PID_59_3X9_REG_MAX; + aw_dev->reg_access = aw87xxx_pid_59_3x9_reg_access; + + /* software reset control info */ + aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_59_3x9_softrst_access); + aw_dev->soft_rst_desc.access = aw87xxx_pid_59_3x9_softrst_access; + aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; + + /* Whether to allow register operation to power off */ + aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; + + aw_dev->product_tab = g_aw_pid_59_3x9_product; + aw_dev->product_cnt = AW87XXX_PID_59_3X9_PRODUCT_MAX; + + aw_dev->rec_desc.addr = AW87XXX_PID_59_3X9_REG_MDCRTL; + aw_dev->rec_desc.disable = AW87XXX_PID_59_3X9_SPK_MODE_ENABLE; + aw_dev->rec_desc.enable = AW87XXX_PID_59_3X9_SPK_MODE_DISABLE; + aw_dev->rec_desc.mask = AW87XXX_PID_59_3X9_SPK_MODE_MASK; + + /* esd reg info */ + aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_59_3X9_REG_ENCR; + aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_59_3X9_ENCR_DEFAULT; + + aw_dev->vol_desc.addr = AW87XXX_PID_59_3X9_REG_CPOVP; +} +/******************* aw87xxx_pid_59_3x9 attributes end ***********************/ + +/********************** aw87xxx_pid_5a attributes ****************************/ +static void aw_dev_chipid_5a_init(struct aw_device *aw_dev) +{ + /* Product register permission info */ + aw_dev->reg_max_addr = AW87XXX_PID_5A_REG_MAX; + aw_dev->reg_access = aw87xxx_pid_5a_reg_access; + + /* software reset control info */ + aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_5a_softrst_access); + aw_dev->soft_rst_desc.access = aw87xxx_pid_5a_softrst_access; + aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; + + /* Whether to allow register operation to power off */ + aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; + + aw_dev->product_tab = g_aw_pid_5a_product; + aw_dev->product_cnt = AW87XXX_PID_5A_PRODUCT_MAX; + + aw_dev->rec_desc.addr = AW87XXX_PID_5A_REG_SYSCTRL_REG; + aw_dev->rec_desc.disable = AW87XXX_PID_5A_REG_RCV_MODE_DISABLE; + aw_dev->rec_desc.enable = AW87XXX_PID_5A_REG_RCV_MODE_ENABLE; + aw_dev->rec_desc.mask = AW87XXX_PID_5A_REG_RCV_MODE_MASK; + + /* esd reg info */ + aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_5A_REG_DFT3R_REG; + aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_5A_DFT3R_DEFAULT; + + aw_dev->vol_desc.addr = AW_REG_NONE; +} +/********************** aw87xxx_pid_5a attributes end ************************/ + +/********************** aw87xxx_pid_76 attributes ****************************/ +static void aw_dev_chipid_76_init(struct aw_device *aw_dev) +{ + /* Product register permission info */ + aw_dev->reg_max_addr = AW87XXX_PID_76_REG_MAX; + aw_dev->reg_access = aw87xxx_pid_76_reg_access; + + /* software reset control info */ + aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_76_softrst_access); + aw_dev->soft_rst_desc.access = aw87xxx_pid_76_softrst_access; + aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; + + /* software power off control info */ + aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; + + aw_dev->product_tab = g_aw_pid_76_product; + aw_dev->product_cnt = AW87XXX_PID_76_PROFUCT_MAX; + + aw_dev->rec_desc.addr = AW87XXX_PID_76_MDCTRL_REG; + aw_dev->rec_desc.disable = AW87XXX_PID_76_EN_SPK_ENABLE; + aw_dev->rec_desc.enable = AW87XXX_PID_76_EN_SPK_DISABLE; + aw_dev->rec_desc.mask = AW87XXX_PID_76_EN_SPK_MASK; + + /* esd reg info */ + aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_76_DFT_ADP1_REG; + aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_76_DFT_ADP1_CHECK; + + aw_dev->vol_desc.addr = AW87XXX_PID_76_CPOVP_REG; +} +/********************** aw87xxx_pid_76 attributes end ************************/ + +/********************** aw87xxx_pid_60 attributes ****************************/ +static void aw_dev_chipid_60_init(struct aw_device *aw_dev) +{ + /* Product register permission info */ + aw_dev->reg_max_addr = AW87XXX_PID_60_REG_MAX; + aw_dev->reg_access = aw87xxx_pid_60_reg_access; + + /* software reset control info */ + aw_dev->soft_rst_desc.len = sizeof(aw87xxx_pid_60_softrst_access); + aw_dev->soft_rst_desc.access = aw87xxx_pid_60_softrst_access; + aw_dev->soft_rst_enable = AW_DEV_SOFT_RST_ENABLE; + + /* software power off control info */ + aw_dev->soft_off_enable = AW_DEV_SOFT_OFF_ENABLE; + + aw_dev->product_tab = g_aw_pid_60_product; + aw_dev->product_cnt = AW87XXX_PID_60_PROFUCT_MAX; + + aw_dev->rec_desc.addr = AW87XXX_PID_60_SYSCTRL_REG; + aw_dev->rec_desc.disable = AW87XXX_PID_60_RCV_MODE_DISABLE; + aw_dev->rec_desc.enable = AW87XXX_PID_60_RCV_MODE_ENABLE; + aw_dev->rec_desc.mask = AW87XXX_PID_60_RCV_MODE_MASK; + + /* esd reg info */ + aw_dev->esd_desc.first_update_reg_addr = AW87XXX_PID_60_NG3_REG; + aw_dev->esd_desc.first_update_reg_val = AW87XXX_PID_60_ESD_REG_VAL; + + aw_dev->vol_desc.addr = AW_REG_NONE; +} +/********************** aw87xxx_pid_60 attributes end ************************/ + +static int aw_dev_chip_init(struct aw_device *aw_dev) +{ + int ret = 0; + + /*get info by chipid*/ + switch (aw_dev->chipid) { + case AW_DEV_CHIPID_9A: + ret = aw_dev_pid_9a_init(aw_dev); + if (ret < 0) + AW_DEV_LOGE(aw_dev->dev, "product is pid_9B init failed"); + break; + case AW_DEV_CHIPID_9B: + aw_dev_pid_9b_init(aw_dev); + AW_DEV_LOGI(aw_dev->dev, "product is pid_9B class"); + break; + case AW_DEV_CHIPID_18: + aw_dev_chipid_18_init(aw_dev); + AW_DEV_LOGI(aw_dev->dev, "product is pid_18 class"); + break; + case AW_DEV_CHIPID_39: + aw_dev_chipid_39_init(aw_dev); + AW_DEV_LOGI(aw_dev->dev, "product is pid_39 class"); + break; + case AW_DEV_CHIPID_59: + if (aw87xxx_dev_gpio_is_valid(aw_dev)) { + aw_dev_chipid_59_5x9_init(aw_dev); + AW_DEV_LOGI(aw_dev->dev, "product is pid_59_5x9 class"); + } else { + aw_dev_chipid_59_3x9_init(aw_dev); + AW_DEV_LOGI(aw_dev->dev, "product is pid_59_3x9 class"); + } + break; + case AW_DEV_CHIPID_5A: + aw_dev_chipid_5a_init(aw_dev); + AW_DEV_LOGI(aw_dev->dev, "product is pid_5A class"); + break; + case AW_DEV_CHIPID_76: + aw_dev_chipid_76_init(aw_dev); + AW_DEV_LOGI(aw_dev->dev, "product is pid_76 class"); + break; + case AW_DEV_CHIPID_60: + aw_dev_chipid_60_init(aw_dev); + AW_DEV_LOGI(aw_dev->dev, "product is pid_60 class"); + break; + default: + AW_DEV_LOGE(aw_dev->dev, "unsupported device revision [0x%x]", + aw_dev->chipid); + return -EINVAL; + } + + return 0; +} + +static int aw87xxx_dev_get_chipid(struct aw_device *aw_dev) +{ + int ret = -1; + unsigned int cnt = 0; + unsigned char reg_val = 0; + + for (cnt = 0; cnt < AW_READ_CHIPID_RETRIES; cnt++) { + ret = aw87xxx_dev_i2c_read_byte(aw_dev, AW_DEV_REG_CHIPID, ®_val); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "[%d] read chip is failed, ret=%d", + cnt, ret); + continue; + } + break; + } + + + if (cnt == AW_READ_CHIPID_RETRIES) { + AW_DEV_LOGE(aw_dev->dev, "read chip is failed,cnt=%d", cnt); + return -EINVAL; + } + + AW_DEV_LOGI(aw_dev->dev, "read chipid[0x%x] succeed", reg_val); + aw_dev->chipid = reg_val; + + return 0; +} + +int aw87xxx_dev_init(struct aw_device *aw_dev) +{ + int ret = -1; + + ret = aw87xxx_dev_get_chipid(aw_dev); + if (ret < 0) { + AW_DEV_LOGE(aw_dev->dev, "read chipid is failed,ret=%d", ret); + return ret; + } + + ret = aw_dev_chip_init(aw_dev); + + return ret; +} + + diff --git a/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_device.h b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_device.h new file mode 100644 index 000000000000..55a50f763c3f --- /dev/null +++ b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_device.h @@ -0,0 +1,167 @@ +#ifndef __AW87XXX_DEVICE_H__ +#define __AW87XXX_DEVICE_H__ +#include +#include +#include +#include +#include "aw87xxx_acf_bin.h" + +#define AW87XXX_PID_9B_PRODUCT_MAX (1) +#define AW87XXX_PID_18_PRODUCT_MAX (1) +#define AW87XXX_PID_39_PRODUCT_MAX (3) +#define AW87XXX_PID_59_3X9_PRODUCT_MAX (2) +#define AW87XXX_PID_59_5X9_PRODUCT_MAX (4) +#define AW87XXX_PID_5A_PRODUCT_MAX (6) +#define AW87XXX_PID_76_PROFUCT_MAX (5) +#define AW87XXX_PID_60_PROFUCT_MAX (5) +#define AW_PRODUCT_NAME_LEN (8) + +#define AW_GPIO_HIGHT_LEVEL (1) +#define AW_GPIO_LOW_LEVEL (0) + +#define AW_I2C_RETRIES (5) +#define AW_I2C_RETRY_DELAY (2) +#define AW_I2C_READ_MSG_NUM (2) + +#define AW_READ_CHIPID_RETRIES (5) +#define AW_READ_CHIPID_RETRY_DELAY (2) +#define AW_DEV_REG_CHIPID (0x00) + +#define AW_DEV_REG_INVALID_MASK (0xff) + +#define AW_NO_RESET_GPIO (-1) + +#define AW_PID_9B_BIN_REG_CFG_COUNT (10) + +#define AW87XXX_DELAY_REG_ADDR (0xFE) +#define AW87XXX_REG_DELAY_TIME (1000) + +#define AW_BOOST_VOLTAGE_MIN (0x00) + +#define AW_REG_NONE (0xFF) +/******************************************** + * + * aw87xxx devices attributes + * + *******************************************/ +struct aw_device; + +struct aw_device_ops { + int (*pwr_on_func)(struct aw_device *aw_dev, struct aw_data_container *data); + int (*pwr_off_func)(struct aw_device *aw_dev, struct aw_data_container *data); +}; + +enum aw_dev_chipid { + AW_DEV_CHIPID_18 = 0x18, + AW_DEV_CHIPID_39 = 0x39, + AW_DEV_CHIPID_59 = 0x59, + AW_DEV_CHIPID_69 = 0x69, + AW_DEV_CHIPID_5A = 0x5A, + AW_DEV_CHIPID_9A = 0x9A, + AW_DEV_CHIPID_9B = 0x9B, + AW_DEV_CHIPID_76 = 0x76, + AW_DEV_CHIPID_60 = 0x60, +}; + +enum aw_dev_hw_status { + AW_DEV_HWEN_OFF = 0, + AW_DEV_HWEN_ON, + AW_DEV_HWEN_INVALID, + AW_DEV_HWEN_STATUS_MAX, +}; + +enum aw_dev_soft_off_enable { + AW_DEV_SOFT_OFF_DISENABLE = 0, + AW_DEV_SOFT_OFF_ENABLE = 1, +}; + +enum aw_dev_soft_rst_enable { + AW_DEV_SOFT_RST_DISENABLE = 0, + AW_DEV_SOFT_RST_ENABLE = 1, +}; + +enum aw_reg_receiver_mode { + AW_NOT_REC_MODE = 0, + AW_IS_REC_MODE = 1, +}; + +enum aw_reg_voltage_status { + AW_VOLTAGE_LOW = 0, + AW_VOLTAGE_HIGH, +}; + +struct aw_mute_desc { + uint8_t addr; + uint8_t enable; + uint8_t disable; + uint16_t mask; +}; + +struct aw_soft_rst_desc { + int len; + unsigned char *access; +}; + +struct aw_esd_check_desc { + uint8_t first_update_reg_addr; + uint8_t first_update_reg_val; +}; + +struct aw_rec_mode_desc { + uint8_t addr; + uint8_t enable; + uint8_t disable; + uint8_t mask; +}; + +struct aw_voltage_desc { + uint8_t addr; + uint8_t vol_max; + uint8_t vol_min; +}; + +struct aw_device { + uint8_t i2c_addr; + uint8_t chipid; + uint8_t soft_rst_enable; + uint8_t soft_off_enable; + uint8_t is_rec_mode; + int hwen_status; + int i2c_bus; + int rst_gpio; + int reg_max_addr; + int product_cnt; + const char **product_tab; + const unsigned char *reg_access; + + struct device *dev; + struct i2c_client *i2c; + struct aw_mute_desc mute_desc; + struct aw_soft_rst_desc soft_rst_desc; + struct aw_esd_check_desc esd_desc; + struct aw_rec_mode_desc rec_desc; + struct aw_voltage_desc vol_desc; + + struct aw_device_ops ops; +}; + + +int aw87xxx_dev_i2c_write_byte(struct aw_device *aw_dev, + uint8_t reg_addr, uint8_t reg_data); +int aw87xxx_dev_i2c_read_byte(struct aw_device *aw_dev, + uint8_t reg_addr, uint8_t *reg_data); +int aw87xxx_dev_i2c_read_msg(struct aw_device *aw_dev, + uint8_t reg_addr, uint8_t *data_buf, uint32_t data_len); +int aw87xxx_dev_i2c_write_bits(struct aw_device *aw_dev, + uint8_t reg_addr, uint8_t mask, uint8_t reg_data); +void aw87xxx_dev_soft_reset(struct aw_device *aw_dev); +void aw87xxx_dev_hw_pwr_ctrl(struct aw_device *aw_dev, bool enable); +int aw87xxx_dev_default_pwr_on(struct aw_device *aw_dev, + struct aw_data_container *profile_data); +int aw87xxx_dev_default_pwr_off(struct aw_device *aw_dev, + struct aw_data_container *profile_data); +int aw87xxx_dev_esd_reg_status_check(struct aw_device *aw_dev); +int aw87xxx_dev_check_reg_is_rec_mode(struct aw_device *aw_dev); +int aw87xxx_dev_init(struct aw_device *aw_dev); + +#endif diff --git a/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_dsp.c b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_dsp.c new file mode 100644 index 000000000000..f623e62ec297 --- /dev/null +++ b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_dsp.c @@ -0,0 +1,433 @@ +/* + * aw87xxx_dsp.c + * + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + * Author: Barry + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw87xxx_log.h" +#include "aw87xxx_dsp.h" + +/***************dsp communicate**************/ +#ifdef AW_QCOM_OPEN_DSP_PLATFORM +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 1)) +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#endif +#endif + +static DEFINE_MUTEX(g_dsp_lock); +static unsigned int g_spin_value; + +static int g_rx_topo_id = AW_RX_DEFAULT_TOPO_ID; +static int g_rx_port_id = AW_RX_DEFAULT_PORT_ID; + +#define AW_COPP_MODULE_ID (0X10013D02) /*SKT module id*/ +#define AW_COPP_PARAMS_ID_AWDSP_ENABLE (0X10013D14) /*SKT enable param id*/ + +#ifdef AW_MTK_OPEN_DSP_PLATFORM +extern int mtk_spk_send_ipi_buf_to_dsp(void *data_buffer, + uint32_t data_size); +extern int mtk_spk_recv_ipi_buf_from_dsp(int8_t *buffer, + int16_t size, uint32_t *buf_len); +/* +static int mtk_spk_send_ipi_buf_to_dsp(void *data_buffer, + uint32_t data_size) +{ + AW_LOGI("enter"); + return 0; +} + +static int mtk_spk_recv_ipi_buf_from_dsp(int8_t *buffer, + int16_t size, uint32_t *buf_len) +{ + AW_LOGI("enter"); + return 0; +} +*/ +#elif defined AW_QCOM_OPEN_DSP_PLATFORM +extern int afe_get_topology(int port_id); +extern int aw_send_afe_cal_apr(uint32_t param_id, + void *buf, int cmd_size, bool write); +/* +static int afe_get_topology(int port_id) +{ + return -EPERM; +} + +static int aw_send_afe_cal_apr(uint32_t param_id, + void *buf, int cmd_size, bool write) +{ + AW_LOGI("enter, no define AWINIC_ADSP_ENABLE", __func__); + return 0; +} +*/ +#endif + +#ifdef AW_QCOM_OPEN_DSP_PLATFORM +extern void aw_set_port_id(int rx_port_id); +#else +static void aw_set_port_id(int rx_port_id) +{ + return; +} +#endif + +static int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable) +{ + /*for v3*/ + int copp_idx = 0; + uint32_t enable_param; + struct param_hdr_v3 param_hdr; + int rc = 0; + + pr_info("%s Awinic port_id 0x%x, module_id 0x%x, enable %d\n", + __func__, port_id, module_id, enable); + + copp_idx = adm_get_default_copp_idx(port_id); + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + if (enable < 0 || enable > 1) { + pr_err("%s: Invalid value for enable %d\n", __func__, enable); + return -EINVAL; + } + + pr_info("%s Awinic port_id 0x%x, module_id 0x%x, copp_idx 0x%x, enable %d\n", + __func__, port_id, module_id, copp_idx, enable); + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = module_id; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = param_id; + param_hdr.param_size = sizeof(enable_param); + enable_param = enable; + + rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (uint8_t *) &enable_param); + if (rc) + pr_err("%s: Awinic Failed to set enable of module(%d) instance(%d) to %d, err %d\n", + __func__, module_id, INSTANCE_ID_0, enable, rc); + return rc; +} + +uint8_t aw87xxx_dsp_isEnable(void) +{ +#if (defined AW_QCOM_OPEN_DSP_PLATFORM) || (defined AW_MTK_OPEN_DSP_PLATFORM) + return true; +#else + return false; +#endif +} + +/*****************mtk dsp communication function start**********************/ +#ifdef AW_MTK_OPEN_DSP_PLATFORM +static int aw_mtk_write_data_to_dsp(int32_t param_id, + void *data, int size) +{ + int32_t *dsp_data = NULL; + mtk_dsp_hdr_t *hdr = NULL; + int ret; + + dsp_data = kzalloc(sizeof(mtk_dsp_hdr_t) + size, GFP_KERNEL); + if (!dsp_data) { + AW_LOGE("kzalloc dsp_msg error"); + return -ENOMEM; + } + + hdr = (mtk_dsp_hdr_t *)dsp_data; + hdr->type = DSP_MSG_TYPE_DATA; + hdr->opcode_id = param_id; + hdr->version = AW_DSP_MSG_HDR_VER; + + memcpy(((char *)dsp_data) + sizeof(mtk_dsp_hdr_t), + data, size); + + ret = mtk_spk_send_ipi_buf_to_dsp(dsp_data, + sizeof(mtk_dsp_hdr_t) + size); + if (ret < 0) { + AW_LOGE("write data failed"); + kfree(dsp_data); + dsp_data = NULL; + return ret; + } + + kfree(dsp_data); + dsp_data = NULL; + return 0; +} + +static int aw_mtk_read_data_from_dsp(int32_t param_id, void *data, + int data_size) +{ + int ret; + mtk_dsp_hdr_t hdr; + + mutex_lock(&g_dsp_lock); + hdr.type = DSP_MSG_TYPE_CMD; + hdr.opcode_id = param_id; + hdr.version = AW_DSP_MSG_HDR_VER; + + ret = mtk_spk_send_ipi_buf_to_dsp(&hdr, sizeof(mtk_dsp_hdr_t)); + if (ret < 0) + goto failed; + + ret = mtk_spk_recv_ipi_buf_from_dsp(data, data_size, &data_size); + if (ret < 0) + goto failed; + + mutex_unlock(&g_dsp_lock); + return 0; + +failed: + mutex_unlock(&g_dsp_lock); + return ret; +} + +#endif +/********************mtk dsp communication function end***********************/ + +/******************qcom dsp communication function start**********************/ +#ifdef AW_QCOM_OPEN_DSP_PLATFORM +static int aw_check_dsp_ready(void) +{ + int ret; + + ret = afe_get_topology(g_rx_port_id); + AW_LOGD("topo_id 0x%x", ret); + + if (ret != g_rx_topo_id) + AW_LOGE("topo id 0x%x", ret); + return ret; + +} + +static int aw_qcom_write_data_to_dsp(int32_t param_id, + void *data, int data_size) +{ + int ret = -1; + int topo = -1; + + AW_LOGI("enter"); + mutex_lock(&g_dsp_lock); + topo = aw_check_dsp_ready(); + if (topo != 0) + ret = aw_send_afe_cal_apr(param_id, data, + data_size, true); + + mutex_unlock(&g_dsp_lock); + return ret; +} + +static int aw_qcom_read_data_from_dsp(int32_t param_id, + void *data, int data_size) +{ + int ret = -1; + int topo = -1; + + AW_LOGI("enter"); + mutex_lock(&g_dsp_lock); + topo = aw_check_dsp_ready(); + if (topo != 0) + ret = aw_send_afe_cal_apr(param_id, data, + data_size, false); + mutex_unlock(&g_dsp_lock); + return ret; +} + +#endif +/*****************qcom dsp communication function end*********************/ + +/*****************read/write msg communication function*********************/ +static int aw_write_data_to_dsp(int32_t param_id, void *data, int data_size) +{ +#if defined AW_QCOM_OPEN_DSP_PLATFORM + return aw_qcom_write_data_to_dsp(param_id, data, data_size); +#elif defined AW_MTK_OPEN_DSP_PLATFORM + return aw_mtk_write_data_to_dsp(param_id, data, data_size); +#else + return -EINVAL; +#endif +} + +static int aw_read_data_from_dsp(int32_t param_id, void *data, int data_size) +{ +#if defined AW_QCOM_OPEN_DSP_PLATFORM + return aw_qcom_read_data_from_dsp(param_id, data, data_size); +#elif defined AW_MTK_OPEN_DSP_PLATFORM + return aw_mtk_read_data_from_dsp(param_id, data, data_size); +#else + return -EINVAL; +#endif +} + +/***************read/write msg communication function end*******************/ + +int aw87xxx_dsp_get_rx_module_enable(int *enable) +{ + if (!enable) { + AW_LOGE("enable is NULL"); + return -EINVAL; + } + + return aw_read_data_from_dsp(AWDSP_RX_SET_ENABLE, + (void *)enable, sizeof(uint32_t)); +} + +int aw87xxx_dsp_set_rx_module_enable(int enable) +{ + switch (enable) { + case AW_RX_MODULE_DISENABLE: + AW_LOGD("set enable=%d", enable); + break; + case AW_RX_MODULE_ENABLE: + AW_LOGD("set enable=%d", enable); + break; + default: + AW_LOGE("unsupport enable=%d", enable); + return -EINVAL; + } + + return aw_write_data_to_dsp(AWDSP_RX_SET_ENABLE, + &enable, sizeof(uint32_t)); +} + +int aw87xxx_dsp_set_copp_module_enable(bool enable) +{ + int ret; + + ret = aw_adm_param_enable(g_rx_port_id, AW_COPP_MODULE_ID, + AW_COPP_PARAMS_ID_AWDSP_ENABLE, enable); + if (ret) + return -EINVAL; + + AW_LOGI("set skt %s", enable == 1 ? "enable" : "disable"); + return 0; +} + +int aw87xxx_dsp_get_vmax(uint32_t *vmax, int dev_index) +{ + int32_t param_id = 0; + + switch (dev_index % AW_DSP_CHANNEL_MAX) { + case AW_DSP_CHANNEL_0: + param_id = AWDSP_RX_VMAX_0; + break; + case AW_DSP_CHANNEL_1: + param_id = AWDSP_RX_VMAX_1; + break; + default: + AW_LOGE("algo only support double PA channel:%d unsupport", + dev_index); + return -EINVAL; + } + + return aw_read_data_from_dsp(param_id, + (void *)vmax, sizeof(uint32_t)); +} + +int aw87xxx_dsp_set_vmax(uint32_t vmax, int dev_index) +{ + int32_t param_id = 0; + + switch (dev_index % AW_DSP_CHANNEL_MAX) { + case AW_DSP_CHANNEL_0: + param_id = AWDSP_RX_VMAX_0; + break; + case AW_DSP_CHANNEL_1: + param_id = AWDSP_RX_VMAX_1; + break; + default: + AW_LOGE("algo only support double PA channel:%d unsupport", + dev_index); + return -EINVAL; + } + + return aw_write_data_to_dsp(param_id, &vmax, sizeof(uint32_t)); +} + +int aw87xxx_dsp_set_spin(uint32_t ctrl_value) +{ + int ret = 0; + + if (ctrl_value >= AW_SPIN_MAX) { + AW_LOGE("spin [%d] unsupported ", ctrl_value); + return -EINVAL; + } + ret = aw_write_data_to_dsp(AW_MSG_ID_SPIN, &ctrl_value, + sizeof(uint32_t)); + if (ret) { + AW_LOGE("spin [%d] set failed ", ctrl_value); + return ret; + } + + g_spin_value = ctrl_value; + return 0; +} + +int aw87xxx_dsp_get_spin(void) +{ + return g_spin_value; +} + +int aw87xxx_spin_set_record_val(void) +{ + AW_LOGD("record write spin enter"); + + return aw87xxx_dsp_set_spin(g_spin_value); +} +EXPORT_SYMBOL(aw87xxx_spin_set_record_val); + +void aw87xxx_device_parse_topo_id_dt(struct aw_device *aw_dev) +{ + int ret; + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-rx-topo-id", &g_rx_topo_id); + if (ret < 0) { + g_rx_topo_id = AW_RX_DEFAULT_TOPO_ID; + AW_DEV_LOGI(aw_dev->dev, "read aw-rx-topo-id failed,use default"); + } + + AW_DEV_LOGI(aw_dev->dev, "rx-topo-id: 0x%x", g_rx_topo_id); +} + +void aw87xxx_device_parse_port_id_dt(struct aw_device *aw_dev) +{ + int ret; + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-rx-port-id", &g_rx_port_id); + if (ret < 0) { + g_rx_port_id = AW_RX_DEFAULT_PORT_ID; + AW_DEV_LOGI(aw_dev->dev, "read aw-rx-port-id failed,use default"); + } + + aw_set_port_id(g_rx_port_id); + AW_DEV_LOGI(aw_dev->dev, "rx-port-id: 0x%x", g_rx_port_id); +} + diff --git a/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_dsp.h b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_dsp.h new file mode 100644 index 000000000000..b79635f777cf --- /dev/null +++ b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_dsp.h @@ -0,0 +1,66 @@ +#ifndef __AW87XXX_DSP_H__ +#define __AW87XXX_DSP_H__ + +#include "aw87xxx_device.h" + +/*#define AW_MTK_OPEN_DSP_PLATFORM*/ +#define AW_QCOM_OPEN_DSP_PLATFORM + +/*Note: The pord_ID is configured according to different platforms*/ +#define AW_DSP_SLEEP_TIME (10) + +#define AW_DSP_MSG_HDR_VER (1) + +#define AW_RX_DEFAULT_TOPO_ID (0x1000FF01) +#define AW_RX_DEFAULT_PORT_ID (0x4000) + +#define AWDSP_RX_SET_ENABLE (0x10013D11) +#define AWDSP_RX_PARAMS (0x10013D12) +#define AWDSP_RX_VMAX_0 (0X10013D17) +#define AWDSP_RX_VMAX_1 (0X10013D18) +#define AW_MSG_ID_SPIN (0x10013D2E) + +enum { + AW_SPIN_0 = 0, + AW_SPIN_90, + AW_SPIN_180, + AW_SPIN_270, + AW_SPIN_MAX, +}; + +typedef struct mtk_dsp_msg_header { + int32_t type; + int32_t opcode_id; + int32_t version; + int32_t reserver[3]; +} mtk_dsp_hdr_t; + +enum aw_rx_module_enable { + AW_RX_MODULE_DISENABLE = 0, + AW_RX_MODULE_ENABLE, +}; + +enum aw_dsp_msg_type { + DSP_MSG_TYPE_DATA = 0, + DSP_MSG_TYPE_CMD = 1, +}; + +enum aw_dsp_channel { + AW_DSP_CHANNEL_0 = 0, + AW_DSP_CHANNEL_1, + AW_DSP_CHANNEL_MAX, +}; + +uint8_t aw87xxx_dsp_isEnable(void); +int aw87xxx_dsp_get_rx_module_enable(int *enable); +int aw87xxx_dsp_set_rx_module_enable(int enable); +int aw87xxx_dsp_set_copp_module_enable(bool enable); +int aw87xxx_dsp_get_vmax(uint32_t *vmax, int channel); +int aw87xxx_dsp_set_vmax(uint32_t vmax, int channel); +int aw87xxx_dsp_set_spin(uint32_t ctrl_value); +int aw87xxx_dsp_get_spin(void); +int aw87xxx_spin_set_record_val(void); +void aw87xxx_device_parse_port_id_dt(struct aw_device *aw_dev); +void aw87xxx_device_parse_topo_id_dt(struct aw_device *aw_dev); + +#endif diff --git a/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_log.h b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_log.h new file mode 100644 index 000000000000..67263484d6d9 --- /dev/null +++ b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_log.h @@ -0,0 +1,33 @@ +#ifndef __AW87XXX_LOG_H__ +#define __AW87XXX_LOG_H__ + +#include + + +/******************************************** + * + * print information control + * + *******************************************/ +#define AW_LOGI(fmt, ...)\ + pr_info("[Awinic] %s:" fmt "\n", __func__, ##__VA_ARGS__) + +#define AW_LOGD(fmt, ...)\ + pr_debug("[Awinic] %s:" fmt "\n", __func__, ##__VA_ARGS__) + +#define AW_LOGE(fmt, ...)\ + pr_err("[Awinic] %s:" fmt "\n", __func__, ##__VA_ARGS__) + + +#define AW_DEV_LOGI(dev, fmt, ...)\ + pr_info("[Awinic] [%s]%s: " fmt "\n", dev_name(dev), __func__, ##__VA_ARGS__) + +#define AW_DEV_LOGD(dev, fmt, ...)\ + pr_debug("[Awinic] [%s]%s: " fmt "\n", dev_name(dev), __func__, ##__VA_ARGS__) + +#define AW_DEV_LOGE(dev, fmt, ...)\ + pr_err("[Awinic] [%s]%s: " fmt "\n", dev_name(dev), __func__, ##__VA_ARGS__) + + + +#endif diff --git a/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_monitor.c b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_monitor.c new file mode 100644 index 000000000000..aecc808ae606 --- /dev/null +++ b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_monitor.c @@ -0,0 +1,1213 @@ +/* + * aw87xxx_monitor.c + * + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + * Author: Barry + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw87xxx.h" +#include "aw87xxx_log.h" +#include "aw87xxx_monitor.h" +#include "aw87xxx_dsp.h" +#include "aw87xxx_bin_parse.h" +#include "aw87xxx_device.h" + +#define AW_MONITOT_BIN_PARSE_VERSION "V0.1.0" + +#define AW_GET_32_DATA(w, x, y, z) \ + ((uint32_t)((((uint8_t)w) << 24) | (((uint8_t)x) << 16) | \ + (((uint8_t)y) << 8) | ((uint8_t)z))) + +/**************************************************************************** + * + * aw87xxx monitor bin check + * + ****************************************************************************/ +static int aw_monitor_check_header_v_1_0_0(struct device *dev, + char *data, uint32_t data_len) +{ + int i = 0; + struct aw_bin_header *header = (struct aw_bin_header *)data; + + if (header->bin_data_type != DATA_TYPE_MONITOR_ANALOG) { + AW_DEV_LOGE(dev, "monitor data_type check error!"); + return -EINVAL; + } + + if (header->bin_data_size != AW_MONITOR_HDR_DATA_SIZE) { + AW_DEV_LOGE(dev, "monitor data_size error!"); + return -EINVAL; + } + + if (header->data_byte_len != AW_MONITOR_HDR_DATA_BYTE_LEN) { + AW_DEV_LOGE(dev, "monitor data_byte_len error!"); + return -EINVAL; + } + + for (i = 0; i < AW_MONITOR_DATA_VER_MAX; i++) { + if (header->bin_data_ver == i) { + AW_LOGD("monitor bin_data_ver[0x%x]", i); + break; + } + } + if (i == AW_MONITOR_DATA_VER_MAX) + return -EINVAL; + + return 0; +} + +static int aw_monitor_check_data_v1_size(struct device *dev, + char *data, int32_t data_len) +{ + int32_t bin_header_len = sizeof(struct aw_bin_header); + int32_t monitor_header_len = sizeof(struct aw_monitor_header); + int32_t monitor_data_len = sizeof(struct vmax_step_config); + int32_t len = 0; + struct aw_monitor_header *monitor_header = NULL; + + AW_DEV_LOGD(dev, "enter"); + + if (data_len < bin_header_len + monitor_header_len) { + AW_DEV_LOGE(dev, "bin len is less than aw_bin_header and monitoor_header,check failed"); + return -EINVAL; + } + + monitor_header = (struct aw_monitor_header *)(data + bin_header_len); + len = data_len - bin_header_len - monitor_header_len; + if (len < monitor_header->step_count * monitor_data_len) { + AW_DEV_LOGE(dev, "bin data len is not enough,check failed"); + return -EINVAL; + } + + AW_DEV_LOGD(dev, "succeed"); + + return 0; +} + +static int aw_monitor_check_data_size(struct device *dev, + char *data, int32_t data_len) +{ + int ret = -1; + struct aw_bin_header *header = (struct aw_bin_header *)data; + + switch (header->bin_data_ver) { + case AW_MONITOR_DATA_VER: + ret = aw_monitor_check_data_v1_size(dev, data, data_len); + if (ret < 0) + return ret; + break; + default: + AW_DEV_LOGE(dev, "bin data_ver[0x%x] non support", + header->bin_data_ver); + return -EINVAL; + } + + return 0; +} + + +static int aw_monitor_check_bin_header(struct device *dev, + char *data, int32_t data_len) +{ + int ret = -1; + struct aw_bin_header *header = NULL; + + if (data_len < sizeof(struct aw_bin_header)) { + AW_DEV_LOGE(dev, "bin len is less than aw_bin_header,check failed"); + return -EINVAL; + } + header = (struct aw_bin_header *)data; + + switch (header->header_ver) { + case HEADER_VERSION_1_0_0: + ret = aw_monitor_check_header_v_1_0_0(dev, data, data_len); + if (ret < 0) { + AW_DEV_LOGE(dev, "monitor bin haeder info check error!"); + return ret; + } + break; + default: + AW_DEV_LOGE(dev, "bin version[0x%x] non support", + header->header_ver); + return -EINVAL; + } + + return 0; +} + +static int aw_monitor_bin_check_sum(struct device *dev, + char *data, int32_t data_len) +{ + int i, data_sum = 0; + uint32_t *check_sum = (uint32_t *)data; + + for (i = 4; i < data_len; i++) + data_sum += data[i]; + + if (*check_sum != data_sum) { + AW_DEV_LOGE(dev, "check_sum[%d] is not equal to data_sum[%d]", + *check_sum, data_sum); + return -ENOMEM; + } + + AW_DEV_LOGD(dev, "succeed"); + + return 0; +} + +static int aw_monitor_bin_check(struct device *dev, + char *monitor_data, uint32_t data_len) +{ + int ret = -1; + + if (monitor_data == NULL || data_len == 0) { + AW_DEV_LOGE(dev, "none data to parse"); + return -EINVAL; + } + + ret = aw_monitor_bin_check_sum(dev, monitor_data, data_len); + if (ret < 0) { + AW_DEV_LOGE(dev, "bin data check sum failed"); + return ret; + } + + ret = aw_monitor_check_bin_header(dev, monitor_data, data_len); + if (ret < 0) { + AW_DEV_LOGE(dev, "bin data len check failed"); + return ret; + } + + ret = aw_monitor_check_data_size(dev, monitor_data, data_len); + if (ret < 0) { + AW_DEV_LOGE(dev, "bin header info check failed"); + return ret; + } + + return 0; +} + +/***************************************************************************** + * + * aw87xxx monitor header bin parse + * + *****************************************************************************/ +static void aw_monitor_write_to_table_v1(struct device *dev, + struct vmax_step_config *vmax_step, + char *vmax_data, uint32_t step_count) +{ + int i = 0; + int index = 0; + int vmax_step_size = (int)sizeof(struct vmax_step_config); + + for (i = 0; i < step_count; i++) { + index = vmax_step_size * i; + vmax_step[i].vbat_min = + AW_GET_32_DATA(vmax_data[index + 3], + vmax_data[index + 2], + vmax_data[index + 1], + vmax_data[index + 0]); + vmax_step[i].vbat_max = + AW_GET_32_DATA(vmax_data[index + 7], + vmax_data[index + 6], + vmax_data[index + 5], + vmax_data[index + 4]); + vmax_step[i].vmax_vol = + AW_GET_32_DATA(vmax_data[index + 11], + vmax_data[index + 10], + vmax_data[index + 9], + vmax_data[index + 8]); + } + + for (i = 0; i < step_count; i++) + AW_DEV_LOGI(dev, "vbat_min:%d, vbat_max%d, vmax_vol:0x%x", + vmax_step[i].vbat_min, + vmax_step[i].vbat_max, + vmax_step[i].vmax_vol); +} + +static int aw_monitor_parse_vol_data_v1(struct device *dev, + struct aw_monitor *monitor, char *monitor_data) +{ + uint32_t step_count = 0; + char *vmax_data = NULL; + struct vmax_step_config *vmax_step = NULL; + + AW_DEV_LOGD(dev, "enter"); + + step_count = monitor->monitor_hdr.step_count; + if (step_count) { + vmax_step = devm_kzalloc(dev, sizeof(struct vmax_step_config) * step_count, + GFP_KERNEL); + if (vmax_step == NULL) { + AW_DEV_LOGE(dev, "vmax_cfg vmalloc failed"); + return -ENOMEM; + } + memset(vmax_step, 0, + sizeof(struct vmax_step_config) * step_count); + } + + vmax_data = monitor_data + sizeof(struct aw_bin_header) + + sizeof(struct aw_monitor_header); + aw_monitor_write_to_table_v1(dev, vmax_step, vmax_data, step_count); + monitor->vmax_cfg = vmax_step; + + AW_DEV_LOGI(dev, "vmax_data parse succeed"); + + return 0; +} + +static int aw_monitor_parse_data_v1(struct device *dev, + struct aw_monitor *monitor, char *monitor_data) +{ + int ret = -1; + int header_len = 0; + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + + header_len = sizeof(struct aw_bin_header); + memcpy(monitor_hdr, monitor_data + header_len, + sizeof(struct aw_monitor_header)); + + AW_DEV_LOGI(dev, "monitor_switch:%d, monitor_time:%d (ms), monitor_count:%d, step_count:%d", + monitor_hdr->monitor_switch, monitor_hdr->monitor_time, + monitor_hdr->monitor_count, monitor_hdr->step_count); + + ret = aw_monitor_parse_vol_data_v1(dev, monitor, monitor_data); + if (ret < 0) { + AW_DEV_LOGE(dev, "vmax_data parse failed"); + return ret; + } + + monitor->bin_status = AW_MONITOR_CFG_OK; + + return 0; +} + + +static int aw_monitor_parse_v_1_0_0(struct device *dev, + struct aw_monitor *monitor, char *monitor_data) +{ + int ret = -1; + struct aw_bin_header *header = (struct aw_bin_header *)monitor_data; + + switch (header->bin_data_ver) { + case AW_MONITOR_DATA_VER: + ret = aw_monitor_parse_data_v1(dev, monitor, monitor_data); + if (ret < 0) + return ret; + break; + default: + return -EINVAL; + } + + return 0; +} + +void aw87xxx_monitor_cfg_free(struct aw_monitor *monitor) +{ + struct aw87xxx *aw87xxx = + container_of(monitor, struct aw87xxx, monitor); + + monitor->bin_status = AW_MONITOR_CFG_WAIT; + memset(&monitor->monitor_hdr, 0, + sizeof(struct aw_monitor_header)); + if (monitor->vmax_cfg) { + devm_kfree(aw87xxx->dev, monitor->vmax_cfg); + monitor->vmax_cfg = NULL; + } +} + +int aw87xxx_monitor_bin_parse(struct device *dev, + char *monitor_data, uint32_t data_len) +{ + int ret = -1; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = NULL; + struct aw_bin_header *bin_header = NULL; + + if (aw87xxx == NULL) { + AW_DEV_LOGE(dev, "get struct aw87xxx failed"); + return -EINVAL; + } + + monitor = &aw87xxx->monitor; + monitor->bin_status = AW_MONITOR_CFG_WAIT; + + AW_DEV_LOGI(dev, "monitor bin parse version: %s", + AW_MONITOT_BIN_PARSE_VERSION); + + ret = aw_monitor_bin_check(dev, monitor_data, data_len); + if (ret < 0) { + AW_DEV_LOGE(dev, "monitor bin check failed"); + return ret; + } + + bin_header = (struct aw_bin_header *)monitor_data; + switch (bin_header->bin_data_ver) { + case DATA_VERSION_V1: + ret = aw_monitor_parse_v_1_0_0(dev, monitor, + monitor_data); + if (ret < 0) { + aw87xxx_monitor_cfg_free(monitor); + return ret; + } + break; + default: + AW_DEV_LOGE(dev, "Unrecognized this bin data version[0x%x]", + bin_header->bin_data_ver); + } + + return 0; +} + +/*************************************************************************** + * + * aw87xxx monitor get adjustment vmax of power + * + ***************************************************************************/ +static int aw_monitor_get_battery_capacity(struct device *dev, + struct aw_monitor *monitor, int *vbat_capacity) +{ + char name[] = "battery"; + int ret = -1; + union power_supply_propval prop = { 0 }; + struct power_supply *psy = NULL; + + psy = power_supply_get_by_name(name); + if (psy == NULL) { + AW_DEV_LOGE(dev, "no struct power supply name:%s", name); + return -EINVAL; + } + + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CAPACITY, &prop); + if (ret < 0) { + AW_DEV_LOGE(dev, "get vbat capacity failed"); + return -EINVAL; + } + *vbat_capacity = prop.intval; + AW_DEV_LOGI(dev, "The percentage is %d", + *vbat_capacity); + + return 0; +} + +static int aw_search_vmax_from_table(struct device *dev, + struct aw_monitor *monitor, + const int vbat_vol, int *vmax_vol) +{ + int i = 0; + int vmax_set = 0; + uint32_t vmax_flag = 0; + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + struct vmax_step_config *vmax_cfg = monitor->vmax_cfg; + + if (monitor->bin_status == AW_MONITOR_CFG_WAIT) { + AW_DEV_LOGE(dev, "vmax_cfg not loaded or parse failed"); + return -ENODATA; + } + + for (i = 0; i < monitor_hdr->step_count; i++) { + if (vbat_vol == AW_VBAT_MAX) { + vmax_set = AW_VMAX_MAX; + vmax_flag = 1; + AW_DEV_LOGD(dev, "vbat=%d, setting vmax=0x%x", + vbat_vol, vmax_set); + break; + } + + if (vbat_vol >= vmax_cfg[i].vbat_min && + vbat_vol < vmax_cfg[i].vbat_max) { + vmax_set = vmax_cfg[i].vmax_vol; + vmax_flag = 1; + AW_DEV_LOGD(dev, "read setting vmax=0x%x, step[%d]: vbat_min=%d,vbat_max=%d", + vmax_set, i, + vmax_cfg[i].vbat_min, + vmax_cfg[i].vbat_max); + break; + } + } + + if (!vmax_flag) { + AW_DEV_LOGE(dev, "vmax_cfg not found"); + return -ENODATA; + } + + *vmax_vol = vmax_set; + return 0; +} + + +/*************************************************************************** + * + *monitor_esd_func + * + ***************************************************************************/ +static int aw_chip_status_recover(struct aw87xxx *aw87xxx) +{ + int ret = -1; + struct aw_monitor *monitor = &aw87xxx->monitor; + char *profile = aw87xxx->current_profile; + + AW_DEV_LOGD(aw87xxx->dev, "enter"); + + ret = aw87xxx_update_profile_esd(aw87xxx, profile); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "load profile[%s] failed ", + profile); + return ret; + } + + AW_DEV_LOGI(aw87xxx->dev, "current prof[%s], dev_index[%d] ", + profile, aw87xxx->dev_index); + + monitor->pre_vmax = AW_VMAX_INIT_VAL; + monitor->first_entry = AW_FIRST_ENTRY; + monitor->timer_cnt = 0; + monitor->vbat_sum = 0; + + return 0; +} + +static int aw_monitor_chip_esd_check_work(struct aw87xxx *aw87xxx) +{ + int ret = 0; + int i = 0; + + for (i = 0; i < REG_STATUS_CHECK_MAX; i++) { + AW_DEV_LOGD(aw87xxx->dev, "reg_status_check[%d]", i); + + ret = aw87xxx_dev_esd_reg_status_check(&aw87xxx->aw_dev); + if (ret < 0) { + aw_chip_status_recover(aw87xxx); + } else { + AW_DEV_LOGD(aw87xxx->dev, "chip status check succeed"); + break; + } + msleep(AW_ESD_CHECK_DELAY); + } + + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "chip status recover failed,chip off"); + aw87xxx_update_profile_esd(aw87xxx, aw87xxx->prof_off_name); + return ret; + } + + return 0; +} + + +/*************************************************************************** + * + * aw87xxx monitor work with dsp + * + ***************************************************************************/ +static int aw_monitor_update_vmax_to_dsp(struct device *dev, + struct aw_monitor *monitor, int vmax_set) +{ + int ret = -1; + uint32_t enable = 0; + + if (monitor->pre_vmax != vmax_set) { + ret = aw87xxx_dsp_get_rx_module_enable(&enable); + if (!enable || ret < 0) { + AW_DEV_LOGE(dev, "get rx failed or rx disable, ret=%d, enable=%d", + ret, enable); + return -EPERM; + } + + ret = aw87xxx_dsp_set_vmax(vmax_set, monitor->dev_index); + if (ret) { + AW_DEV_LOGE(dev, "set dsp msg fail, ret=%d", ret); + return ret; + } + + AW_DEV_LOGI(dev, "set dsp vmax=0x%x sucess", vmax_set); + monitor->pre_vmax = vmax_set; + } else { + AW_DEV_LOGI(dev, "vmax=0x%x no change", vmax_set); + } + + return 0; +} + +static void aw_monitor_with_dsp_vmax_work(struct device *dev, + struct aw_monitor *monitor) +{ + int ret = -1; + int vmax_set = 0; + int vbat_capacity = 0; + int ave_capacity = 0; + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + + AW_DEV_LOGD(dev, "enter with dsp monitor"); + + ret = aw_monitor_get_battery_capacity(dev, monitor, &vbat_capacity); + if (ret < 0) + return; + + if (monitor->timer_cnt < monitor_hdr->monitor_count) { + monitor->timer_cnt++; + monitor->vbat_sum += vbat_capacity; + AW_DEV_LOGI(dev, "timer_cnt = %d", + monitor->timer_cnt); + } + if ((monitor->timer_cnt >= monitor_hdr->monitor_count) || + (monitor->first_entry == AW_FIRST_ENTRY)) { + if (monitor->first_entry == AW_FIRST_ENTRY) + monitor->first_entry = AW_NOT_FIRST_ENTRY; + ave_capacity = monitor->vbat_sum / monitor->timer_cnt; + + if (monitor->custom_capacity) + ave_capacity = monitor->custom_capacity; + + AW_DEV_LOGI(dev, "get average capacity = %d", ave_capacity); + + ret = aw_search_vmax_from_table(dev, monitor, + ave_capacity, &vmax_set); + if (ret < 0) + AW_DEV_LOGE(dev, "not find vmax_vol"); + else + aw_monitor_update_vmax_to_dsp(dev, monitor, vmax_set); + + monitor->timer_cnt = 0; + monitor->vbat_sum = 0; + } +} + +static void aw_monitor_work_func(struct work_struct *work) +{ + int ret = 0; + struct aw87xxx *aw87xxx = container_of(work, + struct aw87xxx, monitor.with_dsp_work.work); + struct device *dev = aw87xxx->dev; + struct aw_monitor *monitor = &aw87xxx->monitor; + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + + AW_DEV_LOGD(dev, "enter"); + + if (monitor->esd_enable) { + ret = aw_monitor_chip_esd_check_work(aw87xxx); + if (ret < 0) + return; + } + + if (monitor_hdr->monitor_switch && !(aw87xxx->aw_dev.is_rec_mode) && + monitor->open_dsp_en && monitor->bin_status == AW_ACF_UPDATE) { + AW_DEV_LOGD(dev, "start low power protection"); + aw_monitor_with_dsp_vmax_work(dev, monitor); + } + + if (monitor->esd_enable || (monitor_hdr->monitor_switch && + !(aw87xxx->aw_dev.is_rec_mode) && monitor->open_dsp_en && + monitor->bin_status == AW_ACF_UPDATE)) { + schedule_delayed_work(&monitor->with_dsp_work, + msecs_to_jiffies(monitor_hdr->monitor_time)); + } +} + +void aw87xxx_monitor_stop(struct aw_monitor *monitor) +{ + struct aw87xxx *aw87xxx = + container_of(monitor, struct aw87xxx, monitor); + + AW_DEV_LOGD(aw87xxx->dev, "enter"); + cancel_delayed_work_sync(&monitor->with_dsp_work); +} + +void aw87xxx_monitor_start(struct aw_monitor *monitor) +{ + struct aw87xxx *aw87xxx = + container_of(monitor, struct aw87xxx, monitor); + int ret = 0; + + ret = aw87xxx_dev_check_reg_is_rec_mode(&aw87xxx->aw_dev); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "get reg current mode failed"); + return; + } + + AW_DEV_LOGI(aw87xxx->dev, + "esd_enable:%d, monitor_switch:%d, monitor_open_dsp_en:%d, is_rec_mode:%d", + monitor->esd_enable, monitor->monitor_hdr.monitor_switch, + monitor->open_dsp_en, aw87xxx->aw_dev.is_rec_mode); + + if (monitor->esd_enable || (monitor->monitor_hdr.monitor_switch && + !(aw87xxx->aw_dev.is_rec_mode) && monitor->open_dsp_en + && monitor->bin_status == AW_ACF_UPDATE)) { + + AW_DEV_LOGD(aw87xxx->dev, "enter"); + monitor->pre_vmax = AW_VMAX_INIT_VAL; + monitor->first_entry = AW_FIRST_ENTRY; + monitor->timer_cnt = 0; + monitor->vbat_sum = 0; + + schedule_delayed_work(&monitor->with_dsp_work, + msecs_to_jiffies(monitor->monitor_hdr.monitor_time)); + } +} +/*************************************************************************** + * + * aw87xxx no dsp monitor func + * + ***************************************************************************/ +int aw87xxx_monitor_no_dsp_get_vmax(struct aw_monitor *monitor, int32_t *vmax) +{ + int vbat_capacity = 0; + int ret = -1; + int vmax_vol = 0; + struct aw87xxx *aw87xxx = + container_of(monitor, struct aw87xxx, monitor); + struct device *dev = aw87xxx->dev; + + ret = aw_monitor_get_battery_capacity(dev, monitor, &vbat_capacity); + if (ret < 0) + return ret; + + if (monitor->custom_capacity) + vbat_capacity = monitor->custom_capacity; + AW_DEV_LOGI(dev, "get_battery_capacity is[%d]", vbat_capacity); + + ret = aw_search_vmax_from_table(dev, monitor, + vbat_capacity, &vmax_vol); + if (ret < 0) { + AW_DEV_LOGE(dev, "not find vmax_vol"); + return ret; + } + + *vmax = vmax_vol; + return 0; +} + + +/*************************************************************************** + * + * aw87xxx monitor sysfs nodes + * + ***************************************************************************/ +static ssize_t aw_attr_get_esd_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + + if (monitor->esd_enable) { + AW_DEV_LOGI(aw87xxx->dev, "esd-enable=true"); + len += snprintf(buf + len, PAGE_SIZE - len, + "esd-enable=true\n"); + } else { + AW_DEV_LOGI(aw87xxx->dev, "esd-enable=false"); + len += snprintf(buf + len, PAGE_SIZE - len, + "esd-enable=false\n"); + } + + return len; +} + +static ssize_t aw_attr_set_esd_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + char esd_enable[AW_ESD_ENABLE_STRLEN] = {0}; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + + if (strlen(buf) > AW_ESD_ENABLE_STRLEN) { + AW_DEV_LOGE(aw87xxx->dev, "input esd_enable_str_len is out of max[%d]", + AW_ESD_ENABLE_STRLEN); + return -EINVAL; + } + + if (sscanf(buf, "%s", esd_enable) == 1) { + AW_DEV_LOGD(aw87xxx->dev, "input esd-enable=[%s]", esd_enable); + if (!strcmp(esd_enable, "true")) + monitor->esd_enable = AW_ESD_ENABLE; + else + monitor->esd_enable = AW_ESD_DISABLE; + AW_DEV_LOGI(dev, "set esd-enable=[%s]", + monitor->esd_enable ? "true" : "false"); + } else { + AW_DEV_LOGE(aw87xxx->dev, "input esd-enable error"); + return -EINVAL; + } + + return len; +} + +static ssize_t aw_attr_get_vbat(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + int ret = -1; + int vbat_capacity = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + + if (monitor->custom_capacity == 0) { + ret = aw_monitor_get_battery_capacity(dev, monitor, + &vbat_capacity); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "get battery_capacity failed"); + return ret; + } + len += snprintf(buf + len, PAGE_SIZE - len, + "vbat capacity=%d\n", vbat_capacity); + } else { + len += snprintf(buf + len, PAGE_SIZE - len, + "vbat capacity=%d\n", + monitor->custom_capacity); + } + + return len; +} + +static ssize_t aw_attr_set_vbat(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int ret = -1; + int capacity = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + + ret = kstrtouint(buf, 0, &capacity); + if (ret < 0) + return ret; + AW_DEV_LOGI(aw87xxx->dev, "set capacity = %d", capacity); + if (capacity >= AW_VBAT_CAPACITY_MIN && + capacity <= AW_VBAT_CAPACITY_MAX){ + monitor->custom_capacity = capacity; + } else { + AW_DEV_LOGE(aw87xxx->dev, "vbat_set=invalid,please input value [%d-%d]", + AW_VBAT_CAPACITY_MIN, AW_VBAT_CAPACITY_MAX); + return -EINVAL; + } + + return len; +} + +static ssize_t aw_attr_get_vmax(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + int ret = -1; + int vbat_capacity = 0; + int vmax_get = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + + if (monitor->open_dsp_en) { + ret = aw87xxx_dsp_get_vmax(&vmax_get, aw87xxx->dev_index); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, + "get dsp vmax fail, ret=%d", ret); + return ret; + } + len += snprintf(buf + len, PAGE_SIZE - len, + "get_vmax=%d\n", vmax_get); + } else { + ret = aw_monitor_get_battery_capacity(dev, monitor, + &vbat_capacity); + if (ret < 0) + return ret; + AW_DEV_LOGI(aw87xxx->dev, "get_battery_capacity is [%d]", + vbat_capacity); + + if (monitor->custom_capacity) { + vbat_capacity = monitor->custom_capacity; + AW_DEV_LOGI(aw87xxx->dev, "get custom_capacity is [%d]", + vbat_capacity); + } + + ret = aw_search_vmax_from_table(aw87xxx->dev, monitor, + vbat_capacity, &vmax_get); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "not find vmax_vol"); + len += snprintf(buf + len, PAGE_SIZE - len, + "not_find_vmax_vol\n"); + return len; + } + len += snprintf(buf + len, PAGE_SIZE - len, + "0x%x\n", vmax_get); + AW_DEV_LOGI(aw87xxx->dev, "0x%x", vmax_get); + } + + return len; +} + +static ssize_t aw_attr_set_vmax(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + uint32_t vmax_set = 0; + int ret = -1; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + + ret = kstrtouint(buf, 0, &vmax_set); + if (ret < 0) + return ret; + + AW_DEV_LOGI(aw87xxx->dev, "vmax_set=0x%x", vmax_set); + + if (monitor->open_dsp_en) { + ret = aw87xxx_dsp_set_vmax(vmax_set, aw87xxx->dev_index); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "send dsp_msg error, ret = %d", + ret); + return ret; + } + msleep(2); + } else { + AW_DEV_LOGE(aw87xxx->dev, "no_dsp system,vmax_set invalid"); + return -EINVAL; + } + + return count; +} + +static ssize_t aw_attr_get_monitor_switch(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + + len += snprintf(buf + len, PAGE_SIZE - len, + "aw87xxx monitor switch: %u\n", + monitor_hdr->monitor_switch); + return len; +} + + +int aw87xxx_dev_monitor_switch_set(struct aw_monitor *monitor, uint32_t enable) +{ + struct aw87xxx *aw87xxx = + container_of(monitor, struct aw87xxx, monitor); + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + + AW_DEV_LOGI(aw87xxx->dev, "monitor switch set =%d", enable); + + if (!monitor->bin_status) { + AW_DEV_LOGE(aw87xxx->dev, "bin parse faile or not loaded,set invalid"); + return -EINVAL; + } + + if (monitor_hdr->monitor_switch == enable) + return 0; + + if (enable > 0) { + monitor_hdr->monitor_switch = 1; + if (monitor->open_dsp_en) { + monitor->pre_vmax = AW_VMAX_INIT_VAL; + monitor->first_entry = AW_FIRST_ENTRY; + monitor->timer_cnt = 0; + monitor->vbat_sum = 0; + } + } else { + monitor_hdr->monitor_switch = 0; + } + + return 0; +} + +static ssize_t aw_attr_set_monitor_switch(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + uint32_t enable = 0; + int ret = -1; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + + ret = kstrtouint(buf, 0, &enable); + if (ret < 0) + return ret; + + ret = aw87xxx_dev_monitor_switch_set(monitor, enable); + if (ret) + return ret; + + return count; +} + +static ssize_t aw_attr_get_monitor_time(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + + len += snprintf(buf + len, PAGE_SIZE - len, + "aw_monitor_timer = %u(ms)\n", + monitor_hdr->monitor_time); + return len; +} + +static ssize_t aw_attr_set_monitor_time(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int timer_val = 0; + int ret = -1; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + + ret = kstrtouint(buf, 0, &timer_val); + if (ret < 0) + return ret; + + AW_DEV_LOGI(aw87xxx->dev, "input monitor timer=%d(ms)", timer_val); + + if (!monitor->bin_status) { + AW_DEV_LOGE(aw87xxx->dev, "bin parse faile or not loaded,set invalid"); + return -EINVAL; + } + + if (timer_val != monitor_hdr->monitor_time) + monitor_hdr->monitor_time = timer_val; + else + AW_DEV_LOGI(aw87xxx->dev, "no_change monitor_time"); + + return count; +} + +static ssize_t aw_attr_get_monitor_count(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + + len += snprintf(buf + len, PAGE_SIZE - len, + "aw_monitor_count = %u\n", + monitor_hdr->monitor_count); + return len; +} + +static ssize_t aw_attr_set_monitor_count(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int monitor_count = 0; + int ret = -1; + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + struct aw_monitor_header *monitor_hdr = &monitor->monitor_hdr; + + ret = kstrtouint(buf, 0, &monitor_count); + if (ret < 0) + return ret; + AW_DEV_LOGI(aw87xxx->dev, "input monitor count=%d", monitor_count); + + if (!monitor->bin_status) { + AW_DEV_LOGE(aw87xxx->dev, "bin parse faile or not loaded,set invalid"); + return -EINVAL; + } + + if (monitor_count != monitor_hdr->monitor_count) + monitor_hdr->monitor_count = monitor_count; + else + AW_DEV_LOGI(aw87xxx->dev, "no_change monitor_count"); + + return count; +} + + +static ssize_t aw_attr_get_rx(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + ssize_t len = 0; + int ret = -1; + uint32_t enable = 0; + + if (monitor->open_dsp_en) { + ret = aw87xxx_dsp_get_rx_module_enable(&enable); + if (ret) { + AW_DEV_LOGE(aw87xxx->dev, "dsp_msg error, ret=%d", ret); + return ret; + } + len += snprintf(buf + len, PAGE_SIZE - len, + "aw87xxx rx: %u\n", enable); + } else { + len += snprintf(buf + len, PAGE_SIZE - len, + "command is invalid\n"); + } + + return len; +} + +static ssize_t aw_attr_set_rx(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw87xxx *aw87xxx = dev_get_drvdata(dev); + struct aw_monitor *monitor = &aw87xxx->monitor; + int ret = -1; + uint32_t enable; + + ret = kstrtouint(buf, 0, &enable); + if (ret < 0) + return ret; + + if (monitor->open_dsp_en) { + AW_DEV_LOGI(aw87xxx->dev, "set rx enable=%d", enable); + + ret = aw87xxx_dsp_set_rx_module_enable(enable); + if (ret < 0) { + AW_DEV_LOGE(aw87xxx->dev, "dsp_msg error, ret=%d", + ret); + return ret; + } + } else { + AW_DEV_LOGE(aw87xxx->dev, "command is invalid"); + return -EINVAL; + } + + return count; +} + + +static DEVICE_ATTR(esd_enable, S_IWUSR | S_IRUGO, + aw_attr_get_esd_enable, aw_attr_set_esd_enable); +static DEVICE_ATTR(vbat, S_IWUSR | S_IRUGO, + aw_attr_get_vbat, aw_attr_set_vbat); +static DEVICE_ATTR(vmax, S_IWUSR | S_IRUGO, + aw_attr_get_vmax, aw_attr_set_vmax); + +static DEVICE_ATTR(monitor, S_IWUSR | S_IRUGO, + aw_attr_get_monitor_switch, aw_attr_set_monitor_switch); +static DEVICE_ATTR(monitor_time, S_IWUSR | S_IRUGO, + aw_attr_get_monitor_time, aw_attr_set_monitor_time); +static DEVICE_ATTR(monitor_count, S_IWUSR | S_IRUGO, + aw_attr_get_monitor_count, aw_attr_set_monitor_count); +static DEVICE_ATTR(rx, S_IWUSR | S_IRUGO, + aw_attr_get_rx, aw_attr_set_rx); + +static struct attribute *aw_monitor_vol_adjust[] = { + &dev_attr_esd_enable.attr, + &dev_attr_vbat.attr, + &dev_attr_vmax.attr, + NULL +}; + +static struct attribute_group aw_monitor_vol_adjust_group = { + .attrs = aw_monitor_vol_adjust, +}; + +static struct attribute *aw_monitor_control[] = { + &dev_attr_monitor.attr, + &dev_attr_monitor_time.attr, + &dev_attr_monitor_count.attr, + &dev_attr_rx.attr, + NULL +}; + +static struct attribute_group aw_monitor_control_group = { + .attrs = aw_monitor_control, +}; + +/*************************************************************************** + * + * aw87xxx monitor init + * + ***************************************************************************/ +static void aw_monitor_dtsi_parse(struct device *dev, + struct aw_monitor *monitor, + struct device_node *dev_node) +{ + int ret = -1; + const char *esd_enable; + + ret = of_property_read_string(dev_node, "esd-enable", &esd_enable); + if (ret < 0) { + AW_DEV_LOGI(dev, "esd_enable parse failed, user default[enable]"); + monitor->esd_enable = AW_ESD_ENABLE; + } else { + if (!strcmp(esd_enable, "false")) + monitor->esd_enable = AW_ESD_DISABLE; + else + monitor->esd_enable = AW_ESD_ENABLE; + + AW_DEV_LOGI(dev, "parse esd-enable=[%s]", + monitor->esd_enable ? "true" : "false"); + } +} + +void aw87xxx_monitor_init(struct device *dev, struct aw_monitor *monitor, + struct device_node *dev_node) +{ + int ret = -1; + struct aw87xxx *aw87xxx = + container_of(monitor, struct aw87xxx, monitor); + + monitor->dev_index = aw87xxx->dev_index; + monitor->monitor_hdr.monitor_time = AW_DEFAULT_MONITOR_TIME; + + aw_monitor_dtsi_parse(dev, monitor, dev_node); + + /* get platform open dsp type */ + monitor->open_dsp_en = aw87xxx_dsp_isEnable(); + + ret = sysfs_create_group(&dev->kobj, &aw_monitor_vol_adjust_group); + if (ret < 0) + AW_DEV_LOGE(dev, "failed to create monitor vol_adjust sysfs nodes"); + + INIT_DELAYED_WORK(&monitor->with_dsp_work, aw_monitor_work_func); + + if (monitor->open_dsp_en) { + ret = sysfs_create_group(&dev->kobj, &aw_monitor_control_group); + if (ret < 0) + AW_DEV_LOGE(dev, "failed to create monitor dsp control sysfs nodes"); + } + + if (!ret) + AW_DEV_LOGI(dev, "monitor init succeed"); +} + +void aw87xxx_monitor_exit(struct aw_monitor *monitor) +{ + struct aw87xxx *aw87xxx = + container_of(monitor, struct aw87xxx, monitor); + /*rm attr node*/ + sysfs_remove_group(&aw87xxx->dev->kobj, + &aw_monitor_vol_adjust_group); + + aw87xxx_monitor_stop(monitor); + + if (monitor->open_dsp_en) { + sysfs_remove_group(&aw87xxx->dev->kobj, + &aw_monitor_control_group); + } +} + diff --git a/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_monitor.h b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_monitor.h new file mode 100644 index 000000000000..83f6fec2599e --- /dev/null +++ b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_monitor.h @@ -0,0 +1,101 @@ +#ifndef __AW87XXX_MONITOR_H__ +#define __AW87XXX_MONITOR_H__ + +#define AW_WAIT_DSP_OPEN_TIME (3000) +#define AW_VBAT_CAPACITY_MIN (0) +#define AW_VBAT_CAPACITY_MAX (100) +#define AW_VMAX_INIT_VAL (0xFFFFFFFF) +#define AW_VBAT_MAX (100) +#define AW_VMAX_MAX (0) +#define AW_DEFAULT_MONITOR_TIME (3000) +#define AW_WAIT_TIME (3000) +#define REG_STATUS_CHECK_MAX (10) +#define AW_ESD_CHECK_DELAY (1) + + +#define AW_MONITOR_TIME_MIN (0) +#define AW_MONITOR_TIME_MAX (50000) + + +#define AW_ESD_ENABLE (true) +#define AW_ESD_DISABLE (false) +#define AW_ESD_ENABLE_STRLEN (16) + +enum aw_monitor_init { + AW_MONITOR_CFG_WAIT = 0, + AW_MONITOR_CFG_OK = 1, +}; + +enum aw_monitor_hdr_info { + AW_MONITOR_HDR_DATA_SIZE = 0x00000004, + AW_MONITOR_HDR_DATA_BYTE_LEN = 0x00000004, +}; + +enum aw_monitor_data_ver { + AW_MONITOR_DATA_VER = 0x00000001, + AW_MONITOR_DATA_VER_MAX, +}; + +enum aw_monitor_first_enter { + AW_FIRST_ENTRY = 0, + AW_NOT_FIRST_ENTRY = 1, +}; + +struct aw_bin_header { + uint32_t check_sum; + uint32_t header_ver; + uint32_t bin_data_type; + uint32_t bin_data_ver; + uint32_t bin_data_size; + uint32_t ui_ver; + char product[8]; + uint32_t addr_byte_len; + uint32_t data_byte_len; + uint32_t device_addr; + uint32_t reserve[4]; +}; + +struct aw_monitor_header { + uint32_t monitor_switch; + uint32_t monitor_time; + uint32_t monitor_count; + uint32_t step_count; + uint32_t reserve[4]; +}; + +struct vmax_step_config { + uint32_t vbat_min; + uint32_t vbat_max; + int vmax_vol; +}; + +struct aw_monitor { + bool open_dsp_en; + bool esd_enable; + int32_t dev_index; + uint8_t first_entry; + uint8_t timer_cnt; + uint32_t vbat_sum; + int32_t custom_capacity; + uint32_t pre_vmax; + + int bin_status; + struct aw_monitor_header monitor_hdr; + struct vmax_step_config *vmax_cfg; + + struct delayed_work with_dsp_work; +}; + +void aw87xxx_monitor_cfg_free(struct aw_monitor *monitor); +int aw87xxx_monitor_bin_parse(struct device *dev, + char *monitor_data, uint32_t data_len); +void aw87xxx_monitor_stop(struct aw_monitor *monitor); +void aw87xxx_monitor_start(struct aw_monitor *monitor); +int aw87xxx_monitor_no_dsp_get_vmax(struct aw_monitor *monitor, + int32_t *vmax); +void aw87xxx_monitor_init(struct device *dev, struct aw_monitor *monitor, + struct device_node *dev_node); +void aw87xxx_monitor_exit(struct aw_monitor *monitor); +int aw87xxx_dev_monitor_switch_set(struct aw_monitor *monitor, uint32_t enable); + +#endif diff --git a/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_pid_18_reg.h b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_pid_18_reg.h new file mode 100644 index 000000000000..7b95d2252e6c --- /dev/null +++ b/techpack/audio/asoc/codecs/aw87xxx/aw87xxx_pid_18_reg.h @@ -0,0 +1,2315 @@ +#ifndef __AW87XXX_PID_18_REG_H__ +#define __AW87XXX_PID_18_REG_H__ + +/* registers list */ +#define AW87XXX_PID_18_CHIPID_REG (0x00) +#define AW87XXX_PID_18_SYSST_REG (0x01) +#define AW87XXX_PID_18_SYSINT_REG (0x02) +#define AW87XXX_PID_18_SYSCTRL_REG (0x03) +#define AW87XXX_PID_18_CPOC_REG (0x04) +#define AW87XXX_PID_18_CLASSD_REG (0x05) +#define AW87XXX_PID_18_MADPVTH_REG (0x06) +#define AW87XXX_PID_18_A3PARAM_REG (0x07) +#define AW87XXX_PID_18_A3A2PO_REG (0x08) +#define AW87XXX_PID_18_A2PARAM_REG (0x09) +#define AW87XXX_PID_18_A1PARAM_REG (0x0A) +#define AW87XXX_PID_18_POPCLK_REG (0x0B) +#define AW87XXX_PID_18_GTDRCPSS_REG (0x0C) +#define AW87XXX_PID_18_MULTI_REG (0x0D) +#define AW87XXX_PID_18_DFT1_REG (0x61) +#define AW87XXX_PID_18_DFT2_REG (0x62) +#define AW87XXX_PID_18_DFT3_REG (0x63) +#define AW87XXX_PID_18_DFT4_REG (0x64) +#define AW87XXX_PID_18_DFT5_REG (0x65) +#define AW87XXX_PID_18_DFT6_REG (0x66) + +#define AW87XXX_PID_18_CLASSD_DEFAULT (0x10) + +/******************************************** + * soft control info + * If you need to update this file, add this information manually + *******************************************/ +unsigned char aw87xxx_pid_18_softrst_access[2] = {0x00, 0xaa}; + +/******************************************** + * Register Access + *******************************************/ +#define AW87XXX_PID_18_REG_MAX (0x67) + +#define REG_NONE_ACCESS (0) +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) + +const unsigned char aw87xxx_pid_18_reg_access[AW87XXX_PID_18_REG_MAX] = { + [AW87XXX_PID_18_CHIPID_REG] = (REG_RD_ACCESS), + [AW87XXX_PID_18_SYSST_REG] = (REG_RD_ACCESS), + [AW87XXX_PID_18_SYSINT_REG] = (REG_RD_ACCESS), + [AW87XXX_PID_18_SYSCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_CPOC_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_CLASSD_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_MADPVTH_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_A3PARAM_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_A3A2PO_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_A2PARAM_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_A1PARAM_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_POPCLK_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_GTDRCPSS_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_MULTI_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_DFT1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_DFT2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_DFT3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_DFT4_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_DFT5_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW87XXX_PID_18_DFT6_REG] = (REG_RD_ACCESS), +}; + +/* detail information of registers begin */ +/* CHIPID (0x00) detail */ +/* IDCODE bit 7:0 (CHIPID 0x00) */ +#define AW87XXX_PID_18_IDCODE_START_BIT (0) +#define AW87XXX_PID_18_IDCODE_BITS_LEN (8) +#define AW87XXX_PID_18_IDCODE_MASK \ + (~(((1< + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#ifdef CONFIG_AW882XX_CODEC + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw882xx.h" +#include "aw882xx_reg.h" + +/****************************************************** + * + * Marco + * + ******************************************************/ +#define AW882XX_I2C_NAME "aw882xx_smartpa" + +#define AW882XX_VERSION "v0.1.18" + +#define AW882XX_RATES SNDRV_PCM_RATE_8000_48000 +#define AW882XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + + +#define AW_I2C_RETRIES 5 /* 5 times */ +#define AW_I2C_RETRY_DELAY 5 /* 5 ms */ +#define AW_READ_CHIPID_RETRIES 5 /* 5 times */ +#define AW_READ_CHIPID_RETRY_DELAY 5 /* 5 ms */ + + +#define CALI_BUF_MAX 100 +#define DELAY_TIME_MAX 300 +#define AWINIC_CALI_FILE "/mnt/vendor/persist/factory/audio/aw_cali.bin" + +static DEFINE_MUTEX(g_msg_dsp_lock); + +#ifdef CONFIG_AW882XX_DSP +extern int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id, + uint32_t param_id, void *buf, int cmd_size, bool write); +extern int aw_send_afe_rx_module_enable(uint32_t rx_port_id, void *buf, int cmd_size); +extern int aw_send_afe_tx_module_enable(uint32_t tx_port_id, void *buf, int cmd_size); +extern int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable); +#else +static int aw_send_afe_cal_apr(uint32_t rx_port_id,uint32_t tx_port_id, + uint32_t param_id, void *buf, int cmd_size, bool write) { + return 0; +} + +static int aw_send_afe_rx_module_enable(uint32_t rx_port_id, void *buf, int cmd_size) +{ + return 0; +} + +static int aw_send_afe_tx_module_enable(uint32_t tx_port_id, void *buf, int cmd_size) +{ + return 0; +} + +static int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable) +{ + return 0; +} +#endif + +static int aw882xx_get_cali_re_from_nv(uint32_t *cali_re); +static int aw882xx_set_cali_re(struct aw882xx *aw882xx, uint32_t cali_re); +static int aw882xx_load_profile_params(struct aw882xx *aw882xx); +static int aw882xx_skt_set_dsp(int value); +static int aw882xx_send_profile_params_to_dsp(struct aw882xx *aw882xx, int profile_id, bool is_fade); +static void aw882xx_fade_in_out(struct aw882xx *aw882xx); +static void aw882xx_volume_set(struct aw882xx *aw882xx, unsigned int value); + +/*monitor voltage and temperature table*/ +static struct aw882xx_low_vol vol_down_table[] = { + {3500, IPEAK_2P50_A, GAIN_NEG_1P5_DB}, + {3700, IPEAK_2P75_A, GAIN_NEG_1P0_DB}, + {3900, IPEAK_3P00_A, GAIN_NEG_0P5_DB}, + }; +static struct aw882xx_low_vol vol_up_table[] = { + {4000, IPEAK_3P50_A, GAIN_NEG_0P0_DB}, + {3800, IPEAK_3P00_A, GAIN_NEG_0P5_DB}, + {3600, IPEAK_2P75_A, GAIN_NEG_1P0_DB}, + }; +static struct aw882xx_low_temp temp_down_table[] = { + {-5, IPEAK_2P50_A, GAIN_NEG_6P0_DB, VMAX_063_PERCENTAGE}, + { 0, IPEAK_2P75_A, GAIN_NEG_4P5_DB, VMAX_075_PERCENTAGE}, + { 5, IPEAK_3P00_A, GAIN_NEG_3P0_DB, VMAX_086_PERCENTAGE}, + }; +static struct aw882xx_low_temp temp_up_table[] = { + { 7, IPEAK_3P50_A, GAIN_NEG_0P0_DB, VMAX_100_PERCENTAGE}, + { 2, IPEAK_3P00_A, GAIN_NEG_3P0_DB, VMAX_086_PERCENTAGE}, + {-2, IPEAK_2P75_A, GAIN_NEG_4P5_DB, VMAX_075_PERCENTAGE}, + }; + +static int aw882xx_monitor_start(struct aw882xx_monitor *monitor); +static int aw882xx_monitor_stop(struct aw882xx_monitor *monitor); + +/****************************************************** + * + * Value + * + ******************************************************/ +static int aw882xx_spk_control; +static int aw882xx_rcv_control; + +#ifdef AW882XX_RUNIN_TEST +static atomic_t g_runin_test; +#endif +static atomic_t g_algo_rx_enable; +static atomic_t g_algo_tx_enable; +static atomic_t g_skt_disable; +static struct aw882xx *g_aw882xx = NULL; +static int8_t g_aw882xx_cali_flag = 0; +static int8_t g_aw882xx_profile_flag = 0; + +#define AW882XX_CFG_NAME_MAX 64 +static char aw882xx_cfg_name[][AW882XX_CFG_NAME_MAX] = { + {"aw882xx_spk_reg.bin"}, + {"aw882xx_rcv_reg.bin"}, +}; + +static unsigned int aw882xx_mode_cfg_shift[AW882XX_MODE_SHIFT_MAX] = { + AW882XX_MODE_SPK_SHIFT, + AW882XX_MODE_RCV_SHIFT, +}; + +/****************************************************** + * + * aw882xx i2c write/read + * + ******************************************************/ +static int aw882xx_i2c_writes(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned char *buf, unsigned int len) +{ + int ret = -1; + unsigned char *data; + + data = kmalloc(len+1, GFP_KERNEL); + if (data == NULL) { + pr_err("%s: can not allocate memory\n", __func__); + return -ENOMEM; + } + + data[0] = reg_addr; + memcpy(&data[1], buf, len); + + ret = i2c_master_send(aw882xx->i2c, data, len+1); + if (ret < 0) + pr_err("%s: i2c master send error\n", __func__); + + kfree(data); + + return ret; +} + +static int aw882xx_i2c_reads(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned char *data_buf, unsigned int data_len) +{ + int ret; + struct i2c_msg msg[] = { + [0] = { + .addr = aw882xx->i2c->addr, + .flags = 0, + .len = sizeof(uint8_t), + .buf = ®_addr, + }, + [1] = { + .addr = aw882xx->i2c->addr, + .flags = I2C_M_RD, + .len = data_len, + .buf = data_buf, + }, + }; + + ret = i2c_transfer(aw882xx->i2c->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) { + pr_err("%s: i2c master send error, ret=%d\n", + __func__, ret); + return ret; + } else if (ret != AW882XX_I2C_READ_MSG_NUM) { + pr_err("%s: couldn't read registers, return %d bytes\n", + __func__, ret); + return -ENXIO; + } + + return 0; +} + +static int aw882xx_i2c_write(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + unsigned char buf[2]; + + buf[0] = (reg_data&0xff00)>>8; + buf[1] = (reg_data&0x00ff)>>0; + while (cnt < AW_I2C_RETRIES) { + ret = aw882xx_i2c_writes(aw882xx, reg_addr, buf, 2); + if (ret < 0) + pr_err("%s: i2c_write cnt=%d error=%d\n", + __func__, cnt, ret); + else + break; + cnt++; + } + + return ret; +} + +static int aw882xx_i2c_read(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int *reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + unsigned char buf[2]; + + while (cnt < AW_I2C_RETRIES) { + ret = aw882xx_i2c_reads(aw882xx, reg_addr, buf, 2); + if (ret < 0) { + pr_err("%s: i2c_read cnt=%d error=%d\n", + __func__, cnt, ret); + } else { + *reg_data = (buf[0]<<8) | (buf[1]<<0); + break; + } + cnt++; + } + + return ret; +} + +static int aw882xx_i2c_write_bits(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int mask, unsigned int reg_data) +{ + int ret = -1; + unsigned int reg_val = 0; + + ret = aw882xx_i2c_read(aw882xx, reg_addr, ®_val); + if (ret < 0) { + pr_err("%s: i2c read error, ret=%d\n", __func__, ret); + return ret; + } + + reg_val &= mask; + reg_val |= reg_data; + ret = aw882xx_i2c_write(aw882xx, reg_addr, reg_val); + if (ret < 0) { + pr_err("%s: i2c read error, ret=%d\n", __func__, ret); + return ret; + } + + return 0; +} + +/****************************************************** + * + * aw882xx control + * + ******************************************************/ +static void aw882xx_run_mute(struct aw882xx *aw882xx, bool mute) +{ + pr_debug("%s: enter\n", __func__); + + if (mute) { + if (aw882xx->need_fade) { + aw882xx->is_fade_in = 0; + aw882xx->fade_work_start = 1; + cancel_delayed_work_sync(&aw882xx->fade_work); + aw882xx_fade_in_out(aw882xx); + } + aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL2_REG, + AW882XX_HMUTE_MASK, + AW882XX_HMUTE_ENABLE_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL2_REG, + AW882XX_HMUTE_MASK, + AW882XX_HMUTE_DISABLE_VALUE); + if (aw882xx->need_fade) { + aw882xx->is_fade_in = 1; + aw882xx->fade_work_start = 1; + aw882xx_volume_set(aw882xx, VOLUME_MIN_NEG_90_DB); + schedule_delayed_work(&aw882xx->fade_work, + msecs_to_jiffies(aw882xx->delayed_time)); + } + } +} + +#if 0 +static bool aw882xx_get_power_status(struct aw882xx *aw882xx) +{ + unsigned int reg_value = 0; + int ret = 0; + pr_debug("%s: enter\n", __func__); + + ret = aw882xx_i2c_read(aw882xx, AW882XX_SYSCTRL_REG, ®_value); + if (ret < 0) { + pr_err("%s: read reg %d failed \n", __func__, AW882XX_SYSCTRL_REG); + return false; + } + /*bit 0: 1 power off, 0 power on*/ + if (reg_value & 0x01) { + return false; + } else { + return true; + } +} +#endif + +static void aw882xx_run_pwd(struct aw882xx *aw882xx, bool pwd) +{ + pr_debug("%s: enter\n", __func__); + + if (pwd) { + aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL_REG, + AW882XX_PWDN_MASK, + AW882XX_PWDN_POWER_DOWN_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL_REG, + AW882XX_PWDN_MASK, + AW882XX_PWDN_NORMAL_WORKING_VALUE); + } +} + +static int aw882xx_sysst_check(struct aw882xx *aw882xx) +{ + int ret = -1; + unsigned char i; + unsigned int reg_val = 0; + + for (i = 0; i < AW882XX_SYSST_CHECK_MAX; i++) { + aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_val); + if (((reg_val & (~AW882XX_SYSST_CHECK_MASK)) & AW882XX_SYSST_CHECK) == + AW882XX_SYSST_CHECK) { + ret = 0; + break; + } else { + pr_debug("%s: check fail, cnt=%d, reg_val=0x%04x\n", + __func__, i, reg_val); + msleep(2); + } + } + if (ret < 0) + pr_info("%s: check fail\n", __func__); + + return ret; +} + +/* +static int aw882xx_get_sysint(struct aw882xx *aw882xx, unsigned int *sysint) +{ + int ret = -1; + unsigned int reg_val = 0; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_SYSINT_REG, ®_val); + if (ret < 0) + pr_info("%s: read sysint fail, ret=%d\n", __func__, ret); + else + *sysint = reg_val; + + return ret; +} + +static int aw882xx_get_iis_status(struct aw882xx *aw882xx) +{ + int ret = -1; + unsigned int reg_val = 0; + + pr_debug("%s: enter\n", __func__); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_val); + if (reg_val & AW882XX_PLLS_LOCKED_VALUE) + ret = 0; + + return ret; +} +*/ + +static int aw882xx_get_icalk(struct aw882xx *aw882xx, int16_t *icalk) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t reg_icalk = 0; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_EFRM1_REG, ®_val); + reg_icalk = (uint16_t)reg_val & AW882XX_EF_ISN_GESLP_MASK; + + if (reg_icalk & AW882XX_EF_ISN_GESLP_SIGN_MASK) + reg_icalk = reg_icalk | AW882XX_EF_ISN_GESLP_NEG; + + *icalk = (int16_t)reg_icalk; + + return ret; +} + +static int aw882xx_get_vcalk(struct aw882xx *aw882xx, int16_t *vcalk) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t reg_vcalk = 0; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_EFRH_REG, ®_val); + + reg_vcalk = (uint16_t)reg_val & AW882XX_EF_VSN_GESLP_MASK; + + if (reg_vcalk & AW882XX_EF_VSN_GESLP_SIGN_MASK) + reg_vcalk = reg_vcalk | AW882XX_EF_VSN_GESLP_NEG; + + *vcalk = (int16_t)reg_vcalk; + + return ret; +} + +static int aw882xx_set_vcalb(struct aw882xx *aw882xx) +{ + int ret = -1; + unsigned int reg_val; + int vcalb; + int icalk; + int vcalk; + int16_t icalk_val = 0; + int16_t vcalk_val = 0; + + ret = aw882xx_get_icalk(aw882xx, &icalk_val); + ret = aw882xx_get_vcalk(aw882xx, &vcalk_val); + + icalk = AW882XX_CABL_BASE_VALUE + AW882XX_ICABLK_FACTOR * icalk_val; + vcalk = AW882XX_CABL_BASE_VALUE + AW882XX_VCABLK_FACTOR * vcalk_val; + + vcalb = AW882XX_VCAL_FACTOR * icalk / vcalk; + + reg_val = (unsigned int)vcalb; + pr_debug("%s: icalk=%d, vcalk=%d, vcalb=%d, reg_val=%d\n", + __func__, icalk, vcalk, vcalb, reg_val); + + ret = aw882xx_i2c_write(aw882xx, AW882XX_VTMCTRL3_REG, reg_val); + + return ret; +} + +static void aw882xx_send_cali_re_to_dsp(struct aw882xx *aw882xx) +{ + int ret = 0; + if((aw882xx != NULL) && (aw882xx->default_re == 0)) { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_RE_L, + &aw882xx->default_re, sizeof(int32_t), false); + pr_info("aw882xx: default_re:%d\n", aw882xx->default_re); + } + if ((aw882xx != NULL) && (aw882xx->cali_re != ERRO_CALI_VALUE)) { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_RE_L, &aw882xx->cali_re, sizeof(int32_t), true); + } + if (ret) + pr_err("%s : set cali re to dsp failed 0x%x\n", + __func__ , AFE_PARAM_ID_AWDSP_RX_RE_L); +} + +static void aw882xx_start(struct aw882xx *aw882xx) +{ + int ret = -1; + uint32_t cali_re; + pr_debug("%s: enter\n", __func__); + + ret = aw882xx_get_cali_re_from_nv(&cali_re); + if (ret < 0) { + cali_re = ERRO_CALI_VALUE; + pr_err("%s: use default vaule %d", __func__ , ERRO_CALI_VALUE); + } + ret = aw882xx_set_cali_re(aw882xx, cali_re); + if (ret < 0) + pr_err("%s: set cali re failed: %d\n", __func__, ret); + + mutex_lock(&aw882xx->lock); + pr_info("%s: monitor is_enable %d,spk_rcv_mode %d\n", + __func__, aw882xx->monitor.is_enable, aw882xx->spk_rcv_mode); + if (aw882xx->monitor.is_enable && + (aw882xx->spk_rcv_mode == AW882XX_SPEAKER_MODE)) { + aw882xx->monitor.first_entry = AW_FIRST_ENTRY; + aw882xx->monitor.pre_vol = 0; + aw882xx->monitor.vol_count = 0; + schedule_work(&aw882xx->monitor.work); + } + if (aw882xx->afe_profile) { + ret = aw882xx_send_profile_params_to_dsp(aw882xx, aw882xx->profile.cur_profile, false); + if (ret) { + pr_err("%s: set profile %d failed \n", __func__,aw882xx->profile.cur_profile); + aw882xx->profile.cur_profile = 0; + } + } + aw882xx_send_cali_re_to_dsp(aw882xx); + + aw882xx_run_pwd(aw882xx, false); + ret = aw882xx_sysst_check(aw882xx); + if (ret < 0) { + aw882xx_run_mute(aw882xx, true); + aw882xx_run_pwd(aw882xx, true); + aw882xx->init = AW882XX_INIT_NG; + aw882xx->power_flag = AW882XX_POWER_DOWN; + } else { + aw882xx_run_mute(aw882xx, false); + aw882xx->init = AW882XX_INIT_OK; + aw882xx->power_flag = AW882XX_POWER_UP; + } +#ifdef AW882XX_RUNIN_TEST + schedule_delayed_work(&aw882xx->adsp_status, msecs_to_jiffies(50)); +#endif + mutex_unlock(&aw882xx->lock); +} + +static void aw882xx_stop(struct aw882xx *aw882xx) +{ + pr_debug("%s: enter\n", __func__); + + mutex_lock(&aw882xx->lock); + aw882xx->power_flag = AW882XX_POWER_DOWN; + aw882xx_run_mute(aw882xx, true); + aw882xx_run_pwd(aw882xx, true); + if (aw882xx->afe_profile) { + aw882xx->profile.cur_profile = 0; + } + if (aw882xx->monitor.is_enable) + aw882xx_monitor_stop(&aw882xx->monitor); + mutex_unlock(&aw882xx->lock); +} + +/****************************************************** + * + * aw882xx config + * + ******************************************************/ +static int aw882xx_get_cali_re_from_nv(uint32_t *cali_re) +{ + /*custom add, if success return value is 0 , else -1*/ + struct file *fp; + char buf[CALI_BUF_MAX]; + uint32_t read_re; + loff_t pos = 0; + mm_segment_t fs; + + memset(buf, 0, CALI_BUF_MAX); + /*open cali file*/ + fp = filp_open(AWINIC_CALI_FILE, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("%s: open %s failed!", __func__, AWINIC_CALI_FILE); + return -EINVAL; + } + /*set fs kernel*/ + fs = get_fs(); + set_fs(KERNEL_DS); + + /*read file*/ + kernel_read(fp, buf, CALI_BUF_MAX - 1, &pos); + + /*get cali re value*/ + if (sscanf(buf, "%d", &read_re) != 1) { + pr_err("%s: file read error", __func__); + set_fs(fs); + filp_close(fp, NULL); + return -EINVAL; + } + set_fs(fs); + + /*close file*/ + filp_close(fp, NULL); + + *cali_re = read_re; + pr_info("%s: %d", __func__, read_re); + return 0; +} + +static int aw882xx_set_cali_re(struct aw882xx *aw882xx, uint32_t cali_re) +{ + if (aw882xx == NULL) + return -EINVAL; + aw882xx->cali_re = cali_re; + return 0; +} + +static int aw882xx_reg_container_update(struct aw882xx *aw882xx, + struct aw882xx_container *aw882xx_cont) +{ + int i = 0; + int reg_addr = 0; + int reg_val = 0; + int ret = -1; + + pr_debug("%s: enter\n", __func__); + + for (i = 0; i < aw882xx_cont->len; i += 4) { + reg_addr = (aw882xx_cont->data[i+1]<<8) + + aw882xx_cont->data[i+0]; + reg_val = (aw882xx_cont->data[i+3]<<8) + + aw882xx_cont->data[i+2]; + pr_debug("%s: reg=0x%04x, val = 0x%04x\n", + __func__, reg_addr, reg_val); + ret = aw882xx_i2c_write(aw882xx, + (unsigned char)reg_addr, + (unsigned int)reg_val); + if (ret < 0) + break; + } + + pr_debug("%s: exit\n", __func__); + + return ret; +} + +static void aw882xx_reg_loaded(const struct firmware *cont, void *context) +{ + struct aw882xx *aw882xx = context; + struct aw882xx_container *aw882xx_cfg; + int ret = -1; + + if (!cont) { + pr_err("%s: failed to read %s\n", __func__, + aw882xx_cfg_name[aw882xx->cfg_num]); + release_firmware(cont); + return; + } + + pr_info("%s: loaded %s - size: %zu\n", __func__, + aw882xx_cfg_name[aw882xx->cfg_num], cont ? cont->size : 0); + + aw882xx_cfg = kzalloc(cont->size+sizeof(int), GFP_KERNEL); + if (!aw882xx_cfg) { + release_firmware(cont); + pr_err("%s: error allocating memory\n", __func__); + return; + } + aw882xx_cfg->len = cont->size; + memcpy(aw882xx_cfg->data, cont->data, cont->size); + release_firmware(cont); + + mutex_lock(&aw882xx->lock); + ret = aw882xx_reg_container_update(aw882xx, aw882xx_cfg); + if (ret < 0) { + pr_err("%s: reg update fail\n", __func__); + } else { + pr_err("%s: reg update sucess\n", __func__); + //aw882xx_run_mute(aw882xx, true); + aw882xx_volume_set(aw882xx, VOLUME_MIN_NEG_90_DB); + aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL2_REG, + AW882XX_HMUTE_MASK, + AW882XX_HMUTE_ENABLE_VALUE); + aw882xx_set_vcalb(aw882xx); + } + mutex_unlock(&aw882xx->lock); + kfree(aw882xx_cfg); + if (aw882xx->afe_profile) { + aw882xx_load_profile_params(aw882xx); + } + aw882xx->need_fade = 0; + aw882xx_start(aw882xx); + if(aw882xx->afe_profile || aw882xx->fade_flag) { + aw882xx->need_fade = 1; + } +} + +static int aw882xx_load_reg(struct aw882xx *aw882xx) +{ + pr_info("%s: enter\n", __func__); + + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + aw882xx_cfg_name[aw882xx->cfg_num], + aw882xx->dev, GFP_KERNEL, + aw882xx, aw882xx_reg_loaded); +} + +static void aw882xx_get_cfg_shift(struct aw882xx *aw882xx) +{ + aw882xx->cfg_num = aw882xx_mode_cfg_shift[aw882xx->spk_rcv_mode]; + pr_debug("%s: cfg_num=%d\n", __func__, aw882xx->cfg_num); +} + +static void aw882xx_cold_start(struct aw882xx *aw882xx) +{ + int ret = -1; + + pr_info("%s: enter\n", __func__); + + aw882xx_get_cfg_shift(aw882xx); + + ret = aw882xx_load_reg(aw882xx); + if (ret < 0) + pr_err("%s: cfg loading requested failed: %d\n", __func__, ret); + +} + +static void aw882xx_smartpa_cfg(struct aw882xx *aw882xx, bool flag) +{ + pr_info("%s: flag = %d\n", __func__, flag); + + if (flag == true) { + if ((aw882xx->init == AW882XX_INIT_ST) || + (aw882xx->init == AW882XX_INIT_NG)) { + pr_info("%s: init = %d\n", __func__, aw882xx->init); + aw882xx_cold_start(aw882xx); + } else { + aw882xx_start(aw882xx); + } + } else { + aw882xx_stop(aw882xx); + } +} + +/****************************************************** + * + * kcontrol + * + ******************************************************/ +static const char *const switch_status[] = { "Off", "On" }; +static const char *const awinic_algo[] = { "Disable", "Enable" }; +static const char *const awinic_profile[AW_PROFILE_MAX] = { +"Music", "Ringtone", "Notification", "Voice"}; +static const char *const awinic_profile_param_file_name[] = { + "aw882xx_afe_params.bin", + }; + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 50, 0); + +struct soc_mixer_control aw882xx_mixer = { + .reg = AW882XX_HAGCCFG4_REG, + .shift = AW882XX_VOL_START_BIT, + .max = AW882XX_VOLUME_MAX, + .min = AW882XX_VOLUME_MIN, +}; + +static int aw882xx_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + /* set kcontrol info */ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mc->max - mc->min; + return 0; +} + +static int aw882xx_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + unsigned int reg_val = 0; + unsigned int value = 0; + struct soc_mixer_control *mc = + (struct soc_mixer_control *) kcontrol->private_value; + + aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_val); + ucontrol->value.integer.value[0] = (value >> mc->shift) & + (AW882XX_VOL_MASK); + return 0; +} + +static int aw882xx_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *) kcontrol->private_value; + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + unsigned int value = 0; + unsigned int reg_value = 0; + + /* value is right */ + value = ucontrol->value.integer.value[0]; + if (value > (mc->max-mc->min) || value < 0) { + pr_err("%s: value over range\n", __func__); + return -ERANGE; + } + + /* smartpa have clk */ + aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_value); + if (!(reg_value & AW882XX_PLLS_LOCKED_VALUE)) { + pr_err("%s: NO I2S CLK ,cat not write reg\n", __func__); + return 0; + } + + /* cal real value */ + value = (value << mc->shift) & AW882XX_VOL_MASK; + aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_value); + value = value | (reg_value & 0x00ff); + + /* write value */ + aw882xx->cur_gain = value >> AW882XX_BIT_HAGCCFG4_GAIN_SHIFT; + aw882xx_i2c_write(aw882xx, AW882XX_HAGCCFG4_REG, value); + + return 0; +} + +static struct snd_kcontrol_new aw882xx_volume = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "aw882xx_rx_volume", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .tlv.p = (digital_gain), + .info = aw882xx_volume_info, + .get = aw882xx_volume_get, + .put = aw882xx_volume_put, + .private_value = (unsigned long)&aw882xx_mixer, +}; + +static int aw882xx_spk_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw882xx_spk_control=%d\n", + __func__, aw882xx_spk_control); + ucontrol->value.integer.value[0] = aw882xx_spk_control; + return 0; +} + +static int aw882xx_spk_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n", + __func__, ucontrol->value.integer.value[0]); + + if (ucontrol->value.integer.value[0] == aw882xx_spk_control) + return 1; + + aw882xx_spk_control = ucontrol->value.integer.value[0]; + + if (AW882XX_SPEAKER_MODE != aw882xx->spk_rcv_mode) { + aw882xx->spk_rcv_mode = AW882XX_SPEAKER_MODE; + aw882xx->init = AW882XX_INIT_ST; + } + + return 0; +} + +static int aw882xx_rcv_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw882xx_rcv_control=%d\n", __func__, aw882xx_rcv_control); + ucontrol->value.integer.value[0] = aw882xx_rcv_control; + return 0; +} + +static int aw882xx_rcv_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n", + __func__, ucontrol->value.integer.value[0]); + + if (ucontrol->value.integer.value[0] == aw882xx_rcv_control) + return 1; + + aw882xx_rcv_control = ucontrol->value.integer.value[0]; + + if (AW882XX_RECEIVER_MODE != aw882xx->spk_rcv_mode) { + aw882xx->spk_rcv_mode = AW882XX_RECEIVER_MODE; + aw882xx->init = AW882XX_INIT_ST; + } + + return 0; +} + +static int aw882xx_algo_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw882xx_algo enable=%d\n", + __func__, atomic_read(&g_algo_rx_enable)); + + ucontrol->value.integer.value[0] = atomic_read(&g_algo_rx_enable); + + return 0; +} + +static int aw882xx_algo_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n", + __func__, ucontrol->value.integer.value[0]); + + ctrl_value = ucontrol->value.integer.value[0]; + ret = aw_send_afe_rx_module_enable(g_aw882xx->afe_rx_portid, &ctrl_value, sizeof(uint32_t)); + if (ret) + pr_err("%s: set algo %d failed, ret=%d\n", + __func__, ctrl_value, ret); + atomic_set(&g_algo_rx_enable, ctrl_value); + return 0; +} + +static int aw882xx_tx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw882xx_tx_control=%d\n", __func__, + atomic_read(&g_algo_tx_enable)); + + ucontrol->value.integer.value[0] = atomic_read(&g_algo_tx_enable); + + return 0; +} + +static int aw882xx_tx_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n", + __func__, ucontrol->value.integer.value[0]); + + ctrl_value = ucontrol->value.integer.value[0]; + ret = aw_send_afe_tx_module_enable(g_aw882xx->afe_tx_portid, &ctrl_value, sizeof(uint32_t)); + if (ret) + pr_err("%s: set tx enable %d, ret=%d\n", __func__, ctrl_value, ret); + atomic_set(&g_algo_tx_enable, ctrl_value); + return 0; +} + +static int aw882xx_profile_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + pr_debug("%s: profile : %s \n", __func__, + awinic_profile[aw882xx->profile.cur_profile]); + if (aw882xx->afe_profile) { + ucontrol->value.integer.value[0] = aw882xx->profile.cur_profile; + } + return 0; +} +static int aw882xx_profile_firmware_parse(struct aw882xx *aw882xx, const struct firmware *cont) +{ + aw_afe_params_hdr_t *hdr; + char *fw_data = NULL; + uint8_t sum = 0; + uint32_t param_size = 0; + char *param_data = NULL; + int i; + pr_info("%s: loaded %s - size: %zu\n", __func__, + awinic_profile_param_file_name[0], cont ? cont->size : 0); + + if (cont->size == 0) { + goto load_failed; + } + + fw_data = kzalloc(cont->size, GFP_KERNEL); + if (fw_data == NULL) { + pr_err("%s: alloc memory failed \n", __func__); + goto load_failed; + } + memcpy(fw_data, cont->data, cont->size); + + /*output bin info*/ + hdr = (aw_afe_params_hdr_t *)fw_data; + pr_info("%s: fw v_%d.%d.%d \n", __func__, + hdr->fw[0], hdr->fw[1], hdr->fw[2]); + pr_info("%s: param v_%d.%d.%d \n", __func__, + hdr->cfg[0], hdr->cfg[1], hdr->cfg[2]); + pr_info("%s: project name : %s \n", __func__, hdr->project); + pr_info("%s: param len: %d", __func__, hdr->params_len); + + param_data = fw_data + sizeof(aw_afe_params_hdr_t); + param_size = cont->size - sizeof(aw_afe_params_hdr_t); + + for (i = 0; i < param_size; i++) { + sum += param_data[i]; + } + if (sum != hdr->check_sum) { + pr_err("%s:data check sum failed : %d -- %d \n", __func__, + sum, hdr->check_sum); + kfree(fw_data); + goto load_failed; + } + + if (hdr->profile_num < AW_PROFILE_MAX) { + pr_err("%s: profile num is not match %d -- %d ", __func__, + hdr->profile_num, AW_PROFILE_MAX); + kfree(fw_data); + goto load_failed; + } + + if (hdr->params_len != (param_size / hdr->profile_num)) { + pr_err("%s: profile params len is not match %d ", __func__, + hdr->params_len); + kfree(fw_data); + goto load_failed; + } + + mutex_lock(&aw882xx->profile.lock); + param_data = fw_data + sizeof(aw_afe_params_hdr_t); + for (i = 0; i < AW_PROFILE_MAX; i++) { + aw882xx->profile.data[i] = kzalloc(hdr->params_len, GFP_KERNEL); + if (aw882xx->profile.data[i] == NULL) { + pr_err("%s: alloc failed !", __func__); + kfree(fw_data); + mutex_unlock(&aw882xx->profile.lock); + goto load_failed; + } + memcpy(aw882xx->profile.data[i], param_data , hdr->params_len); + param_data += hdr->params_len; + } + aw882xx->profile.len = hdr->params_len; + aw882xx->profile.status = AW882XX_INIT_OK; + mutex_unlock(&aw882xx->profile.lock); + kfree(fw_data); + return 0; + +load_failed: + mutex_lock(&aw882xx->profile.lock); + for (i = 0; i < AW_PROFILE_MAX; i++) { + if (aw882xx->profile.data[i]) { + kfree(aw882xx->profile.data[i]); + aw882xx->profile.data[i] = NULL; + } + } + aw882xx->profile.status = AW882XX_INIT_NG; + aw882xx->profile.len = 0; + mutex_unlock(&aw882xx->profile.lock); + pr_err("%s: load params bin failed !", __func__); + return 0; +} + +static int aw882xx_load_profile_params(struct aw882xx *aw882xx) +{ + const struct firmware *fw = NULL; + int ret; + pr_info("%s: enter\n", __func__); + + ret = request_firmware(&fw, + awinic_profile_param_file_name[0], + aw882xx->dev); + if (ret) { + pr_err("%s: load profile params failed !", __func__); + return -EINVAL; + } + + ret = aw882xx_profile_firmware_parse(aw882xx, fw); + return ret; +} + +static void aw882xx_volume_set(struct aw882xx *aw882xx, unsigned int value) +{ + unsigned int reg_value = 0; + unsigned int real_value = ((value / VOLUME_STEP_DB) << 4) + (value % VOLUME_STEP_DB) * 2; + + aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_value); + if(real_value >= aw882xx->cur_gain) { + real_value = (real_value << 8) | (reg_value & 0x00ff); + } else { + real_value = (aw882xx->cur_gain << 8) | (reg_value & 0x00ff); + } + /* write value */ + aw882xx_i2c_write(aw882xx, AW882XX_HAGCCFG4_REG, real_value); +} + +static void aw882xx_fade_in_out(struct aw882xx *aw882xx) +{ + int i = 0; + + if(aw882xx == NULL) { + aw882xx->fade_work_start = 0; + return; + } + //volume up + if (aw882xx->is_fade_in) { + i = VOLUME_MIN_NEG_90_DB; + do { + aw882xx_volume_set(aw882xx, i); + i -= FADE_STEP_DB; //6 + usleep_range(1400,1600); + } while (i >= 0); + } else { // volume down + do { + aw882xx_volume_set(aw882xx, i); + i += FADE_STEP_DB; + usleep_range(1400,1600); + } while (i <= VOLUME_MIN_NEG_90_DB); + } + aw882xx->fade_work_start = 0; +} + +static void aw882xx_fade_work_func(struct work_struct *work) +{ + struct delayed_work *d_work; + struct aw882xx *aw882xx; + + d_work = to_delayed_work(work); + aw882xx = container_of(d_work, struct aw882xx, fade_work); + if(aw882xx == NULL) { + pr_debug("%s: can not get aw882xx from fade_work\n", __func__); + return; + } + pr_debug("%s: enter in: %d\n", __func__, aw882xx->is_fade_in); + + aw882xx_fade_in_out(aw882xx); + pr_debug("%s: exit\n", __func__); +} + +static int aw882xx_send_profile_params_to_dsp(struct aw882xx *aw882xx, int profile_id, bool is_fade) +{ + int ret ; + int32_t params_id = AFE_PARAM_ID_AWDSP_RX_PARAMS; + + mutex_lock(&aw882xx->profile.lock); + if (aw882xx->profile.data[profile_id] == NULL) { + pr_err("%s: profile %s not data is NULL \n", __func__, + awinic_profile[profile_id]); + mutex_unlock(&aw882xx->profile.lock); + return -EINVAL; + } + if (is_fade) { + g_aw882xx_profile_flag = true; + //fade out + //aw882xx_fade_in_out(aw882xx, false); + aw882xx_run_mute(aw882xx, true); + } + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + params_id, + aw882xx->profile.data[profile_id], + aw882xx->profile.len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, params_id); + aw882xx_run_mute(aw882xx, false); + //aw882xx_fade_in_out(aw882xx, true); + g_aw882xx_profile_flag = false; + mutex_unlock(&aw882xx->profile.lock); + return ret; + } + if (is_fade) { + //fade out + aw882xx_run_mute(aw882xx, false); + //aw882xx_fade_in_out(aw882xx, true); + g_aw882xx_profile_flag = false; + } + mutex_unlock(&aw882xx->profile.lock); + + pr_debug("%s: set params done", __func__); + return 0; +} + +static int aw882xx_profile_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + unsigned int ctl_value = 0; + int ret; + ctl_value = ucontrol->value.integer.value[0]; + + if (aw882xx->afe_profile) { + if (ctl_value >= AW_PROFILE_MAX) { + pr_err("%s: unsuport profile %d", __func__, ctl_value); + return -EINVAL; + } + pr_debug("%s: profile switch to %s \n", + __func__, awinic_profile[ctl_value]); + + mutex_lock(&aw882xx->lock); + if (aw882xx->profile.cur_profile != ctl_value) { + /*PA have power on ,set params direct*/ + if (aw882xx->power_flag == AW882XX_POWER_UP) { + ret = aw882xx_send_profile_params_to_dsp(aw882xx, ctl_value, true); + if (ret < 0) { + mutex_unlock(&aw882xx->lock); + return -EINVAL; + } + aw882xx_send_cali_re_to_dsp(aw882xx); + aw882xx->profile.cur_profile = ctl_value; + } else { + /*PA power only set params status*/ + aw882xx->profile.cur_profile = ctl_value; + } + } + mutex_unlock(&aw882xx->lock); + } + return 0; +} + +static int aw882xx_skt_disable_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw882xx_skt_disable %d\n", + __func__, atomic_read(&g_skt_disable)); + + ucontrol->value.integer.value[0] = atomic_read(&g_skt_disable); + + return 0; +} + +static int aw882xx_skt_set_dsp(int value) +{ + int ret; + int port_id = g_aw882xx->afe_rx_portid; + int module_id = AW_MODULE_ID_COPP; + int param_id = AW_MODULE_PARAMS_ID_COPP_ENABLE; + + ret = aw_adm_param_enable(port_id, module_id, param_id, value); + if (ret) { + pr_err("%s: set skt %d failed \n", __func__, value); + return -EINVAL; + } + + pr_info("%s: set skt %s", __func__, value == 1 ? "enable" : "disable"); + return 0; +} + +static int aw882xx_skt_disable_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int value = ucontrol->value.integer.value[0]; + + pr_debug("%s: aw882xx_skt_disable %d \n", __func__, value); + aw882xx_skt_set_dsp(!value); + atomic_set(&g_skt_disable, value); + + return 0; +} + +static int aw882xx_monitor_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + + pr_debug("%s: aw882xx_monitor_get %d\n", + __func__, aw882xx->monitor.is_enable); + + ucontrol->value.integer.value[0] = aw882xx->monitor.is_enable; + + return 0; +} + +static int aw882xx_monitor_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + int enable = ucontrol->value.integer.value[0]; + + pr_debug("%s: aw882xx_monitor_set %d \n", __func__, enable); + + if (enable == aw882xx->monitor.is_enable) + return 1; + + aw882xx->monitor.is_enable = enable; + if (enable) + schedule_work(&aw882xx->monitor.work); + + return 0; +} + +static int aw882xx_fade_dealyed_time_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + + pr_debug("%s: aw882xx_fade_dealyed_time_get %d\n", + __func__, aw882xx->delayed_time); + + ucontrol->value.integer.value[0] = aw882xx->delayed_time; + + return 0; +} + +static int aw882xx_fade_dealyed_time_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + aw882xx->delayed_time = ucontrol->value.integer.value[0]; + + pr_debug("%s: aw882xx_fade_dealyed_time_set %d ms \n", __func__, aw882xx->delayed_time); + return 0; +} + + +#ifdef AW882XX_RUNIN_TEST +static int aw882xx_runin_test_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw882xx_runin test %d\n", + __func__, atomic_read(&g_runin_test)); + + ucontrol->value.integer.value[0] = atomic_read(&g_runin_test); + + return 0; +} + +static int aw882xx_runin_test_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int value = ucontrol->value.integer.value[0]; + + pr_debug("%s: aw882xx_runin_test %d \n", __func__, value); + + if (value > 1 ) { + value = 0; + } + + atomic_set(&g_runin_test, value); + + return 0; +} + +static void aw882xx_set_adsp_module_status(struct work_struct *work) +{ + int ret = 0; + int32_t set_value; + + /*no test reg set default value*/ + if (!atomic_read(&g_runin_test)) { + return; + } + set_value = 0; + /*set afe rx module*/ + ret = aw_send_afe_rx_module_enable(g_aw882xx->afe_rx_portid, &set_value, sizeof(uint32_t)); + if (ret) { + pr_info("%s: disable afe rx module %d falied , ret=%d\n", + __func__, set_value, ret); + } + + /*set skt module*/ + ret = aw882xx_skt_set_dsp(false); + if (ret) { + pr_err("%s: disable skt failed !\n", __func__); + } + + pr_info("%s: disable skt and afe module \n", __func__); +} +#endif + +static const struct soc_enum aw882xx_snd_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(switch_status), switch_status), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(awinic_algo), awinic_algo), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(awinic_profile), awinic_profile), +}; + +static struct snd_kcontrol_new aw882xx_controls[] = { + SOC_ENUM_EXT("aw882xx_speaker_switch", aw882xx_snd_enum[0], + aw882xx_spk_get, aw882xx_spk_set), + SOC_ENUM_EXT("aw882xx_receiver_switch", aw882xx_snd_enum[0], + aw882xx_rcv_get, aw882xx_rcv_set), + SOC_ENUM_EXT("aw882xx_rx_switch", aw882xx_snd_enum[1], + aw882xx_algo_get, aw882xx_algo_set), + SOC_ENUM_EXT("aw882xx_tx_switch", aw882xx_snd_enum[1], + aw882xx_tx_get, aw882xx_tx_set), + SOC_ENUM_EXT("aw882xx_skt_disable", aw882xx_snd_enum[0], + aw882xx_skt_disable_get, aw882xx_skt_disable_set), + SOC_ENUM_EXT("aw882xx_monitor_switch", aw882xx_snd_enum[1], + aw882xx_monitor_get, aw882xx_monitor_set), + SOC_SINGLE_EXT("aw882xx_fade_dealyed_time", SND_SOC_NOPM, 0, DELAY_TIME_MAX, 0, + aw882xx_fade_dealyed_time_get, aw882xx_fade_dealyed_time_set), +#ifdef AW882XX_RUNIN_TEST + SOC_ENUM_EXT("aw882xx_runin_test", aw882xx_snd_enum[0], + aw882xx_runin_test_get, aw882xx_runin_test_set), +#endif +}; + +static struct snd_kcontrol_new aw882xx_profile_controls[] = { + SOC_ENUM_EXT("aw882xx_profile_switch", aw882xx_snd_enum[2], + aw882xx_profile_get, aw882xx_profile_set), +}; + +static void aw882xx_add_codec_controls(struct aw882xx *aw882xx) +{ + pr_info("%s: enter\n", __func__); + + snd_soc_add_component_controls(aw882xx->component, aw882xx_controls, + ARRAY_SIZE(aw882xx_controls)); + if(aw882xx->afe_profile) { + snd_soc_add_component_controls(aw882xx->component, aw882xx_profile_controls, + ARRAY_SIZE(aw882xx_profile_controls)); + } + snd_soc_add_component_controls(aw882xx->component, &aw882xx_volume, 1); +} + +/****************************************************** + * + * Digital Audio Interface + * + ******************************************************/ + + +static int aw882xx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + pr_info("%s: playback enter\n", __func__); + /*aw882xx_run_pwd(aw882xx, false);*/ + aw882xx->power_flag = AW882XX_POWER_ING; + } else { + pr_info("%s: capture enter\n", __func__); + } + + return 0; +} + +static int aw882xx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + /*struct aw882xx *aw882xx = snd_soc_component_get_drvdata(dai->codec);*/ + struct snd_soc_component *component = dai->component; + + pr_info("%s: fmt=0x%x\n", __func__, fmt); + + /* Supported mode: regular I2S, slave, or PDM */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != + SND_SOC_DAIFMT_CBS_CFS) { + dev_err(component->dev, "%s: invalid codec master mode\n", + __func__); + return -EINVAL; + } + break; + default: + dev_err(component->dev, "%s: unsupported DAI format %d\n", + __func__, fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + return 0; +} + +static int aw882xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec_dai->component); + + pr_info("%s: freq=%d\n", __func__, freq); + + aw882xx->sysclk = freq; + return 0; +} + +static int aw882xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + unsigned int rate = 0; + int reg_value = 0; + uint32_t cco_mux_value; + int width = 0; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + pr_debug("%s: requested rate: %d, sample size: %d\n", + __func__, rate, snd_pcm_format_width(params_format(params))); + return 0; + } + /* get rate param */ + aw882xx->rate = rate = params_rate(params); + pr_debug("%s: requested rate: %d, sample size: %d\n", + __func__, rate, snd_pcm_format_width(params_format(params))); + + /* match rate */ + switch (rate) { + case 8000: + reg_value = AW882XX_I2SSR_8KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_8_16_32KHZ_VALUE; + break; + case 16000: + reg_value = AW882XX_I2SSR_16KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_8_16_32KHZ_VALUE; + break; + case 32000: + reg_value = AW882XX_I2SSR_32KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_8_16_32KHZ_VALUE; + break; + case 44100: + reg_value = AW882XX_I2SSR_44P1KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + break; + case 48000: + reg_value = AW882XX_I2SSR_48KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + break; + case 96000: + reg_value = AW882XX_I2SSR_96KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + break; + case 192000: + reg_value = AW882XX_I2SSR_192KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + break; + default: + reg_value = AW882XX_I2SSR_48KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + pr_err("%s: rate can not support\n", __func__); + break; + } + aw882xx_i2c_write_bits(aw882xx, AW882XX_PLLCTRL1_REG, + AW882XX_I2S_CCO_MUX_MASK, cco_mux_value); + + /* set chip rate */ + if (-1 != reg_value) { + aw882xx_i2c_write_bits(aw882xx, AW882XX_I2SCTRL_REG, + AW882XX_I2SSR_MASK, reg_value); + } + + /* get bit width */ + width = params_width(params); + pr_debug("%s: width = %d\n", __func__, width); + switch (width) { + case 16: + reg_value = AW882XX_I2SFS_16_BITS_VALUE; + break; + case 20: + reg_value = AW882XX_I2SFS_20_BITS_VALUE; + break; + case 24: + reg_value = AW882XX_I2SFS_24_BITS_VALUE; + break; + case 32: + reg_value = AW882XX_I2SFS_32_BITS_VALUE; + break; + default: + reg_value = AW882XX_I2SFS_16_BITS_VALUE; + pr_err("%s: width can not support\n", __func__); + break; + } + + /* get width */ + if (-1 != reg_value) { + aw882xx_i2c_write_bits(aw882xx, AW882XX_I2SCTRL_REG, + AW882XX_I2SFS_MASK, reg_value); + } + + return 0; +} + +static int aw882xx_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + + pr_info("%s: mute state=%d\n", __func__, mute); + + if (!(aw882xx->flags & AW882XX_FLAG_START_ON_MUTE)) + return 0; + + if (mute) { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + aw882xx_smartpa_cfg(aw882xx, false); + atomic_set(&g_algo_tx_enable, 0); + atomic_set(&g_algo_rx_enable, 0); + atomic_set(&g_skt_disable, 0); + } else { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + aw882xx_smartpa_cfg(aw882xx, true); + } + + return 0; +} + +static void aw882xx_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aw882xx->rate = 0; + aw882xx_run_pwd(aw882xx, true); + } +} + +static const struct snd_soc_dai_ops aw882xx_dai_ops = { + .startup = aw882xx_startup, + .set_fmt = aw882xx_set_fmt, + .set_sysclk = aw882xx_set_dai_sysclk, + .hw_params = aw882xx_hw_params, + .mute_stream = aw882xx_mute, + .shutdown = aw882xx_shutdown, +}; + +static struct snd_soc_dai_driver aw882xx_dai[] = { + { + .name = "aw882xx-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AW882XX_RATES, + .formats = AW882XX_FORMATS, + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AW882XX_RATES, + .formats = AW882XX_FORMATS, + }, + .ops = &aw882xx_dai_ops, + .symmetric_rates = 1, +#if 0 + .symmetric_channels = 1, + .symmetric_samplebits = 1, +#endif + }, +}; + +/***************************************************** + * + * codec driver + * + *****************************************************/ +static int aw882xx_probe(struct snd_soc_component *component) +{ + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + int ret = -1; + + pr_info("%s: enter\n", __func__); + + aw882xx->component = component; + + aw882xx_add_codec_controls(aw882xx); + + //if (component->dev->of_node) + //dev_set_name(component->dev, "%s", "aw882xx_smartpa"); + + pr_info("%s: exit\n", __func__); + + ret = 0; + return ret; +} + +static void aw882xx_remove(struct snd_soc_component *component) +{ + /*struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec);*/ + pr_info("%s: enter\n", __func__); + return; +} + + +static unsigned int aw882xx_codec_read(struct snd_soc_component *component, + unsigned int reg) +{ + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + unsigned int value = 0; + int ret = -1; + + pr_debug("%s: enter\n", __func__); + + if (aw882xx_reg_access[reg] & REG_RD_ACCESS) { + ret = aw882xx_i2c_read(aw882xx, reg, &value); + if (ret < 0) + pr_debug("%s: read register failed\n", __func__); + } else { + pr_debug("%s: register 0x%x no read access\n", __func__, reg); + } + return ret; +} + +static int aw882xx_codec_write(struct snd_soc_component *component, + unsigned int reg, unsigned int value) +{ + int ret = -1; + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(component); + + pr_debug("%s: enter ,reg is 0x%x value is 0x%x\n", + __func__, reg, value); + + if (aw882xx_reg_access[reg]®_WR_ACCESS) { + ret = aw882xx_i2c_write(aw882xx, reg, value); + } else { + pr_debug("%s: register 0x%x no write access\n", + __func__, reg); + } + + return ret; +} + +static struct snd_soc_component_driver soc_component_dev_aw882xx = { + .probe = aw882xx_probe, + .remove = aw882xx_remove, + .read = aw882xx_codec_read, + .write = aw882xx_codec_write, +// .reg_cache_size = AW882XX_REG_MAX, +// .reg_word_size = 2, +}; + +/****************************************************** + * + * irq + * + ******************************************************/ +static void aw882xx_interrupt_setup(struct aw882xx *aw882xx) +{ + unsigned int reg_val; + + pr_info("%s: enter\n", __func__); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSINTM_REG, ®_val); + reg_val &= (~AW882XX_UVLS_VDD_BELOW_2P8V_VALUE); + reg_val &= (~AW882XX_NOCLKS_TRIG_VALUE); + reg_val &= (~AW882XX_CLKS_TRIG_VALUE); + aw882xx_i2c_write(aw882xx, AW882XX_SYSINTM_REG, reg_val); +} + +static void aw882xx_interrupt_clear(struct aw882xx *aw882xx) +{ + unsigned int reg_val = 0; + + pr_info("%s: enter\n", __func__); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_val); + pr_info("%s: reg SYSST=0x%x\n", __func__, reg_val); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSINT_REG, ®_val); + pr_info("%s: reg SYSINT=0x%x\n", __func__, reg_val); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSINTM_REG, ®_val); + pr_info("%s: reg SYSINTM=0x%x\n", __func__, reg_val); +} + +static irqreturn_t aw882xx_irq(int irq, void *data) +{ + struct aw882xx *aw882xx = data; + + pr_info("%s: enter\n", __func__); + + aw882xx_interrupt_clear(aw882xx); + + pr_info("%s: exit\n", __func__); + + return IRQ_HANDLED; +} + +/***************************************************** + * + * device tree + * + *****************************************************/ +int aw882xx_parse_low_vol_cfg(struct device *dev, struct aw882xx *aw882xx, + struct device_node *np, char *dir, struct aw882xx_low_vol **table, int *num) +{ + int ret; + int i; + uint32_t *cfg_value; + char cfg_name[32] = {0}; + int cfg_len; + + snprintf(cfg_name, sizeof(cfg_name), "low-vol-table-%s", dir); + + cfg_len = of_property_count_u32_elems(np, cfg_name); + if (cfg_len <= 0) { + dev_info(dev, "%s: %s get cfg_len:%d error\n", + __func__, cfg_name, cfg_len); + goto use_default; + } + + cfg_value = devm_kzalloc(dev, sizeof(uint32_t) * cfg_len, GFP_KERNEL); + if (!cfg_value) { + dev_info(dev,"%s: %s aw_cfg kzalloc failed\n", + __func__, cfg_name); + goto use_default; + } + + ret = of_property_read_u32_array(np, cfg_name, cfg_value, cfg_len); + if (ret != 0) { + dev_info(dev, "%s: fail get %s dt\n", + __func__, cfg_name); + devm_kfree(dev, cfg_value); + goto use_default; + } + *num = cfg_len / 3; + *table = devm_kzalloc(dev, sizeof(struct aw882xx_low_vol) * (*num), GFP_KERNEL); + if (!(*table)) { + dev_info(dev, "%s: %s table kzalloc failed\n", + __func__, cfg_name); + devm_kfree(dev, cfg_value); + goto use_default; + } + + for(i = 0; i < (*num); i++) { + (*table)[i].vol = cfg_value[3 * i]; + (*table)[i].ipeak = cfg_value[3 * i + 1]; + (*table)[i].gain = cfg_value[3 * i + 2]; + } + + devm_kfree(dev, cfg_value); + + return 0; + +use_default: + dev_info(dev, "%s: %s low-vol table use default cfg\n", __func__, cfg_name); + if (!strcmp(dir,"up")) { + *table = vol_up_table; + *num = sizeof(vol_up_table) / sizeof(struct aw882xx_low_vol); + } else if (!strcmp(dir,"down")) { + *table = vol_down_table; + *num = sizeof(vol_down_table) / sizeof(struct aw882xx_low_vol); + } else { + dev_err(dev,"%s: unsupport dir %s\n", + __func__, dir); + return -EINVAL; + } + return 0; +} + + +int aw882xx_parse_low_temp_cfg(struct device *dev, struct aw882xx *aw882xx, + struct device_node *np, char *dir, struct aw882xx_low_temp **table, int *num) +{ + int ret; + int i; + uint32_t *cfg_value; + char cfg_name[32] = {0}; + int cfg_len; + + snprintf(cfg_name, sizeof(cfg_name), "low-temp-table-%s", dir); + + cfg_len = of_property_count_u32_elems(np, cfg_name); + if (cfg_len <= 0) { + dev_info(dev, "%s: %s get cfg_len:%d error\n", + __func__, cfg_name, cfg_len); + goto use_default; + } + + cfg_value = devm_kzalloc(dev, sizeof(uint32_t) * cfg_len, GFP_KERNEL); + if (!cfg_value) { + dev_info(dev,"%s: %s aw_cfg kzalloc failed\n", + __func__, cfg_name); + goto use_default; + } + + ret = of_property_read_u32_array(np, cfg_name, cfg_value, cfg_len); + if (ret != 0) { + dev_info(dev, "%s: fail get %s dt\n", + __func__, cfg_name); + devm_kfree(dev, cfg_value); + goto use_default; + } + *num = cfg_len / 4; + *table = devm_kzalloc(dev, sizeof(struct aw882xx_low_temp) * (*num), GFP_KERNEL); + if (!(*table)) { + dev_info(dev, "%s: %s table kzalloc failed\n", + __func__, cfg_name); + devm_kfree(dev, cfg_value); + goto use_default; + } + + for(i = 0; i < (*num); i++) { + (*table)[i].temp = cfg_value[4 * i]; + (*table)[i].ipeak = cfg_value[4 * i + 1]; + (*table)[i].gain = cfg_value[4 * i + 2]; + (*table)[i].vmax = cfg_value[4 * i + 3]; + } + + devm_kfree(dev, cfg_value); + + return 0; + +use_default: + dev_info(dev, "%s: %s temp table use default cfg\n", __func__, cfg_name); + if (!strcmp(dir,"up")) { + *table = temp_up_table; + *num = sizeof(temp_up_table) / sizeof(struct aw882xx_low_temp); + } else if (!strcmp(dir,"down")) { + *table = temp_down_table; + *num = sizeof(temp_down_table) / sizeof(struct aw882xx_low_temp); + } else { + dev_err(dev,"%s: unsupport dir %s\n", + __func__, dir); + return -EINVAL; + } + return 0; +} + +static int aw882xx_parse_dt(struct device *dev, struct aw882xx *aw882xx, + struct device_node *np) +{ + int ret = 0; + int i = 0; + struct aw882xx_monitor *monitor = &aw882xx->monitor; + /* gpio */ + aw882xx->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (aw882xx->reset_gpio < 0) { + dev_err(dev, "%s: no reset gpio provided, will not HW reset device\n", + __func__); + } else { + dev_info(dev, "%s: reset gpio provided ok\n", __func__); + } + aw882xx->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (aw882xx->irq_gpio < 0) + dev_info(dev, "%s: no irq gpio provided.\n", __func__); + else + dev_info(dev, "%s: irq gpio provided ok.\n", __func__); + + ret = of_property_read_u32(np, "monitor-flag", &monitor->is_enable); + if (ret) { + monitor->is_enable = AW882XX_MONITOR_DEFAULT_FLAG; + dev_err(dev, "%s: monitor-flag get failed ,use default value!\n", __func__); + } else { + dev_info(dev, "%s: monitor-flag = %d\n", + __func__, monitor->is_enable); + } + + ret = of_property_read_u32(np, "monitor-timer-val", &monitor->timer_val); + if (ret) { + monitor->timer_val = AW882XX_MONITOR_DEFAULT_TIMER_VAL; + dev_err(dev, "%s: monitor-timer-val get failed,use default value!\n", __func__); + } else { + dev_info(dev, "%s: monitor-timer-val = %d\n", + __func__, monitor->timer_val); + } + + ret = of_property_read_u32(np, "afe-rx-portid", &aw882xx->afe_rx_portid); + if (ret) { + aw882xx->afe_rx_portid = AW882XX_DEFAULT_AFE_RX_PROT_ID; + dev_err(dev, "%s: afe_rx_portid get failed,use default port: 0x%x!\n", + __func__, aw882xx->afe_rx_portid); + } else { + dev_info(dev, "%s: afe_rx_portid = 0x%x\n", + __func__, aw882xx->afe_rx_portid); + } + + ret = of_property_read_u32(np, "afe-tx-portid", &aw882xx->afe_tx_portid); + if (ret) { + aw882xx->afe_tx_portid = AW882XX_DEFAULT_AFE_TX_PROT_ID; + dev_err(dev, "%s: afe_tx_portid get failed,use default port: 0x%x!\n", + __func__, aw882xx->afe_tx_portid); + } else { + dev_info(dev, "%s: afe_tx_portid = 0x%x\n", + __func__, aw882xx->afe_tx_portid); + } + + ret = of_property_read_u32(np, "afe-profile", &aw882xx->afe_profile); + if (ret) { + aw882xx->afe_profile = 0; + dev_err(dev, "%s: afe-profile get failed,use default value!\n", __func__); + } else { + dev_info(dev, "%s: afe-profile = %d\n", + __func__, aw882xx->afe_profile); + } + + ret = of_property_read_u32(np, "fade-flag", &aw882xx->fade_flag); + if (ret) { + aw882xx->fade_flag = 0; + dev_err(dev, "%s: fade_flag get failed,use default value!\n", __func__); + } else { + dev_info(dev, "%s: fade_flag = %d\n", + __func__, aw882xx->fade_flag); + } + + /*get low vol table cfg*/ + ret = aw882xx_parse_low_vol_cfg(dev, aw882xx, np, "up", + &monitor->vol_up_table, &monitor->vol_up_num); + if (ret <0) + return ret; + + ret = aw882xx_parse_low_vol_cfg(dev, aw882xx, np, "down", + &monitor->vol_down_table, &monitor->vol_down_num); + if (ret < 0) + return ret; + + for(i = 0; i < monitor->vol_up_num; i++) + dev_info(dev,"%s:vol_up_table vol:%d, %d, %d\n", + __func__, monitor->vol_up_table[i].vol, + monitor->vol_up_table[i].ipeak, + monitor->vol_up_table[i].gain); + + for(i = 0; i < monitor->vol_down_num; i++) + dev_info(dev, "%s:vol_down_table vol:%d, %d, %d\n", + __func__, monitor->vol_down_table[i].vol, + monitor->vol_down_table[i].ipeak, + monitor->vol_down_table[i].gain); + + for(i = 0; i < monitor->vol_down_num; i++) { + if(monitor->vol_down_table[i].vol >= + monitor->vol_up_table[monitor->vol_up_num - 1 -i].vol) { + dev_err(aw882xx->dev,"%s: use default table\n", __func__); + monitor->vol_up_table = vol_up_table; + monitor->vol_up_num= sizeof(vol_up_table) / + sizeof(struct aw882xx_low_vol); + monitor->vol_down_table = vol_down_table; + monitor->vol_down_num = sizeof(vol_down_table) / + sizeof(struct aw882xx_low_vol); + break; + } + } + + for(i = 1; i < monitor->vol_down_num; i++) { + if(monitor->vol_up_table[monitor->vol_up_num - i].vol >= + monitor->vol_down_table[i].vol) { + dev_err(aw882xx->dev,"%s: use default table\n", __func__); + monitor->vol_up_table = vol_up_table; + monitor->vol_up_num= sizeof(vol_up_table) / + sizeof(struct aw882xx_low_vol); + monitor->vol_down_table = vol_down_table; + monitor->vol_down_num = sizeof(vol_down_table) / + sizeof(struct aw882xx_low_vol); + break; + } + } + /*get low temp table cfg*/ + ret = aw882xx_parse_low_temp_cfg(dev, aw882xx, np, "up", + &monitor->temp_up_table, &monitor->temp_up_num); + if (ret <0) + return ret; + + ret = aw882xx_parse_low_temp_cfg(dev, aw882xx, np, "down", + &monitor->temp_down_table, &monitor->temp_down_num); + + for(i = 0; i < monitor->temp_up_num; i++) + dev_info(dev,"%s:temp_up_table temp:%d, 0x%x, 0x%x, 0x%x\n", + __func__, monitor->temp_up_table[i].temp, + monitor->temp_up_table[i].ipeak, + monitor->temp_up_table[i].gain, + monitor->temp_up_table[i].vmax); + + for(i = 0; i < monitor->temp_down_num; i++) + dev_info(dev, "%s:temp_down_table temp:%d, 0x%x, 0x%x, 0x%x\n", + __func__, monitor->temp_down_table[i].temp, + monitor->temp_down_table[i].ipeak, + monitor->temp_down_table[i].gain, + monitor->temp_down_table[i].vmax); + + for(i = 0; i < monitor->temp_down_num; i++) { + if(monitor->temp_down_table[i].temp >= + monitor->temp_up_table[monitor->temp_up_num - 1 -i].temp) { + dev_err(aw882xx->dev,"%s: use default table\n", __func__); + monitor->temp_up_table = temp_up_table; + monitor->temp_up_num= sizeof(temp_up_table) / + sizeof(struct aw882xx_low_temp); + monitor->temp_down_table = temp_down_table; + monitor->temp_down_num = sizeof(temp_down_table) / + sizeof(struct aw882xx_low_temp); + break; + } + } + + for(i = 1; i < monitor->temp_down_num; i++) { + if(monitor->temp_up_table[monitor->temp_up_num - i].temp >= + monitor->temp_down_table[i].temp) { + dev_err(aw882xx->dev,"%s: use default table\n", __func__); + monitor->temp_up_table = temp_up_table; + monitor->temp_up_num= sizeof(temp_up_table) / + sizeof(struct aw882xx_low_temp); + monitor->temp_down_table = temp_down_table; + monitor->temp_down_num = sizeof(temp_down_table) / + sizeof(struct aw882xx_low_temp); + break; + } + } + return 0; +} + +int aw882xx_hw_reset(struct aw882xx *aw882xx) +{ + pr_info("%s: enter\n", __func__); + + if (aw882xx && gpio_is_valid(aw882xx->reset_gpio)) { + gpio_set_value_cansleep(aw882xx->reset_gpio, 0); + msleep(1); + gpio_set_value_cansleep(aw882xx->reset_gpio, 1); + msleep(2); + } else { + dev_err(aw882xx->dev, "%s: failed\n", __func__); + } + return 0; +} + +/***************************************************** + * + * check chip id + * + *****************************************************/ +int aw882xx_read_chipid(struct aw882xx *aw882xx) +{ + int ret = -1; + unsigned int cnt = 0; + unsigned int reg = 0; + + while (cnt < AW_READ_CHIPID_RETRIES) { + ret = aw882xx_i2c_read(aw882xx, AW882XX_ID_REG, ®); + if (ret < 0) { + dev_err(aw882xx->dev, + "%s: failed to read REG_ID: %d\n", + __func__, ret); + return -EIO; + } + switch (reg) { + case AW882XX_ID: + pr_info("%s: aw882xx detected\n", __func__); + aw882xx->flags |= AW882XX_FLAG_SKIP_INTERRUPTS; + aw882xx->flags |= AW882XX_FLAG_START_ON_MUTE; + aw882xx->chipid = AW882XX_ID; + pr_info("%s: aw882xx->flags=0x%x\n", + __func__, aw882xx->flags); + return 0; + default: + pr_info("%s: unsupported device revision (0x%x)\n", + __func__, reg); + break; + } + cnt++; + + msleep(AW_READ_CHIPID_RETRY_DELAY); + } + + return -EINVAL; +} + +/****************************************************** + * + * sys group attribute: monitor + * + ******************************************************/ +static ssize_t aw882xx_monitor_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + uint32_t enable = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &enable); + if (ret < 0) + return ret; + + dev_info(dev, "%s:monitor enable set =%d\n", + __func__, enable); + aw882xx->monitor.is_enable = enable; + if (enable) + aw882xx_monitor_start(&aw882xx->monitor); + + return count; +} + +static ssize_t aw882xx_monitor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + uint32_t local_enable; + + local_enable = aw882xx->monitor.is_enable; + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx monitor enable: %d\n", local_enable); + return len; +} + +/****************************************************** + * + * sys group attribute: reg + * + ******************************************************/ +static ssize_t aw882xx_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + unsigned int databuf[2] = {0}; + + if (2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) + aw882xx_i2c_write(aw882xx, databuf[0], databuf[1]); + + return count; +} + +static ssize_t aw882xx_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned char i = 0; + unsigned int reg_val = 0; + + for (i = 0; i < AW882XX_REG_MAX; i++) { + if (aw882xx_reg_access[i]®_RD_ACCESS) { + aw882xx_i2c_read(aw882xx, i, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, + "reg:0x%02x=0x%04x\n", i, reg_val); + } + } + return len; +} + +static ssize_t aw882xx_rw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0}; + + if (2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) { + aw882xx->reg_addr = (unsigned char)databuf[0]; + aw882xx_i2c_write(aw882xx, databuf[0], databuf[1]); + } else if (1 == sscanf(buf, "%x", &databuf[0])) { + aw882xx->reg_addr = (unsigned char)databuf[0]; + } + + return count; +} + +static ssize_t aw882xx_rw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned int reg_val = 0; + + if (aw882xx_reg_access[aw882xx->reg_addr] & REG_RD_ACCESS) { + aw882xx_i2c_read(aw882xx, aw882xx->reg_addr, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, + "reg:0x%02x=0x%04x\n", aw882xx->reg_addr, reg_val); + } + return len; +} + +static ssize_t aw882xx_spk_rcv_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + int ret = -1; + unsigned int databuf[2] = {0}; + + ret = kstrtouint(buf, 0, &databuf[0]); + if (ret < 0) + return ret; + + aw882xx->spk_rcv_mode = databuf[0]; + + return count; +} + +static ssize_t aw882xx_spk_rcv_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + + if (aw882xx->spk_rcv_mode == AW882XX_SPEAKER_MODE) + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx spk_rcv: %d, speaker mode\n", + aw882xx->spk_rcv_mode); + else if (aw882xx->spk_rcv_mode == AW882XX_RECEIVER_MODE) + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx spk_rcv: %d, receiver mode\n", + aw882xx->spk_rcv_mode); + else + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx spk_rcv: %d, unknown mode\n", + aw882xx->spk_rcv_mode); + + return len; +} + +static ssize_t aw882xx_mec_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + /*struct aw882xx *aw882xx = dev_get_drvdata(dev);*/ + uint32_t mec_ctr = 0; + uint32_t param_id = AFE_PARAM_ID_AWDSP_RX_SET_ENABLE; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &mec_ctr); + if (ret < 0) + return ret; + + pr_info("%s: mec_ctr=%d\n", __func__, mec_ctr); + + ret = aw_send_afe_cal_apr(g_aw882xx->afe_rx_portid, g_aw882xx->afe_tx_portid, + param_id, &mec_ctr, sizeof(uint32_t), true); + if (ret) + pr_err("%s: dsp_msg error, ret=%d\n", __func__, ret); + + mdelay(2); + + return count; +} + +static ssize_t aw882xx_mec_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + /*struct aw882xx *aw882xx = dev_get_drvdata(dev);*/ + ssize_t len = 0; + int ret = -1; + uint8_t *buffer = NULL; + uint32_t mec_ctr = 0; + uint32_t param_id = AFE_PARAM_ID_AWDSP_RX_SET_ENABLE; + + buffer = kmalloc(sizeof(uint32_t), GFP_KERNEL); + if (buffer == NULL) { + pr_err("%s can not allocate memory\n", __func__); + return -ENOMEM; + } + + ret = aw_send_afe_cal_apr(g_aw882xx->afe_rx_portid, g_aw882xx->afe_tx_portid, + param_id, buffer, sizeof(uint32_t), false); + if (ret) { + pr_err("%s: dsp_msg_read error: %d\n", __func__, ret); + kfree(buffer); + return -EFAULT; + } + + memcpy(&mec_ctr, buffer, sizeof(uint32_t)); + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx mec: %d\n", mec_ctr); + + kfree(buffer); + + return len; +} + +static ssize_t aw882xx_default_re_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + int ret = -1; + + if((aw882xx != NULL) && (aw882xx->default_re == 0)) { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_RE_L, + &aw882xx->default_re, sizeof(int32_t), false); + if (ret) { + pr_err("%s: dsp_msg_read error: %d\n", __func__, ret); + return -EFAULT; + } + } + if(aw882xx != NULL) { + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx default_re: %d\n", aw882xx->default_re); + } + return len; +} + +static ssize_t aw882xx_spk_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + int ret = -1; + int16_t data_len = _IOC_SIZE(AW882XX_IOCTL_GET_CALI_DATA); + int32_t spk_temp = 0; + char *data_ptr = NULL; + unsigned int reg_value = 0; + + if(aw882xx == NULL) { + goto fail_temp; + } + data_ptr = kmalloc(data_len, GFP_KERNEL); + if (data_ptr == NULL) { + pr_err("%s : malloc failed !\n", __func__); + goto fail_temp; + } + + aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_value); + if (!(reg_value & AW882XX_PLLS_LOCKED_VALUE)) { + pr_err("%s: NO I2S CLK\n", __func__); + goto exit_temp; + } + + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, + aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_REAL_DATA_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error: %d\n", __func__, ret); + goto exit_temp; + } + + memcpy(&spk_temp, data_ptr + sizeof(int32_t), sizeof(int32_t)); + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx spk_temp: %d\n", spk_temp); + return len; + +exit_temp: + kfree(data_ptr); +fail_temp: + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx spk_temp: fail\n"); + return len; +} + +#ifdef AW_DEBUG +static ssize_t aw882xx_vol_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + uint32_t vol = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &vol); + if (ret < 0) + return ret; + + pr_info("%s: vol set =%d\n", __func__, vol); + aw882xx->monitor.test_vol = vol; + + return count; +} + +static ssize_t aw882xx_vol_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + uint32_t local_vol = aw882xx->monitor.test_vol; + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx vol: %d\n", local_vol); + return len; +} +static ssize_t aw882xx_temp_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + int32_t temp = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtoint(buf, 0, &temp); + if (ret < 0) + return ret; + + pr_info("%s: temp set =%d\n", __func__, temp); + aw882xx->monitor.test_temp = temp; + + return count; +} + +static ssize_t aw882xx_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + int32_t local_temp = aw882xx->monitor.test_temp; + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx vol: %d\n", local_temp); + return len; +} +#endif + +static int aw_write_msg_to_dsp(struct aw882xx *aw882xx, int inline_id, + char *data, int data_size) +{ + int32_t *dsp_msg = NULL; + struct aw_dsp_msg_hdr *hdr = NULL; + int ret; + + dsp_msg = kzalloc(sizeof(struct aw_dsp_msg_hdr) + data_size, + GFP_KERNEL); + if (!dsp_msg) { + pr_err("%s: inline_id:0x%x kzalloc dsp_msg error\n", + __func__, inline_id); + return -ENOMEM; + } + hdr = (struct aw_dsp_msg_hdr *)dsp_msg; + hdr->type = DSP_MSG_TYPE_DATA; + hdr->opcode_id = inline_id; + hdr->version = AWINIC_DSP_MSG_HDR_VER; + + memcpy(((char *)dsp_msg) + sizeof(struct aw_dsp_msg_hdr), + data, data_size); + + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_MSG, (void *)dsp_msg, + sizeof(struct aw_dsp_msg_hdr) + data_size, true); + if (ret < 0) { + pr_err("%s:inline_id:0x%x, write data failed\n", + __func__, inline_id); + kfree(dsp_msg); + dsp_msg = NULL; + return ret; + } + + kfree(dsp_msg); + dsp_msg = NULL; + return 0; +} + +static int aw_read_msg_from_dsp(struct aw882xx *aw882xx, int inline_id, + char *data, int data_size) +{ + struct aw_dsp_msg_hdr dsp_msg; + int ret; + + dsp_msg.type = DSP_MSG_TYPE_CMD; + dsp_msg.opcode_id = inline_id; + dsp_msg.version = AWINIC_DSP_MSG_HDR_VER; + + mutex_lock(&g_msg_dsp_lock); + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_MSG, + &dsp_msg, sizeof(struct aw_dsp_msg_hdr), true); + if (ret < 0) { + pr_err("%s:inline_id:0x%x, write cmd to dsp failed\n", + __func__, inline_id); + goto dsp_msg_failed; + } + + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_MSG, + data, data_size, false); + if (ret < 0) { + pr_err("%s:inline_id:0x%x, read data from dsp failed\n", + __func__, inline_id); + goto dsp_msg_failed; + } + + mutex_unlock(&g_msg_dsp_lock); + return 0; + +dsp_msg_failed: + mutex_unlock(&g_msg_dsp_lock); + return ret; +} + +static int aw_misc_ops_read_dsp(struct aw882xx *aw882xx, aw_ioctl_msg_t *msg) +{ + char __user* user_data = (char __user*)msg->data_buf; + uint32_t dsp_msg_id = (uint32_t)msg->opcode_id; + int data_len = msg->data_len; + int ret; + char *data_ptr; + + data_ptr = kmalloc(data_len, GFP_KERNEL); + if (!data_ptr) { + pr_err("%s : malloc failed !\n", __func__); + return -ENOMEM; + } + + ret = aw_read_msg_from_dsp(aw882xx, dsp_msg_id, data_ptr, data_len); + if (ret) { + pr_err("%s : write failed\n", __func__); + goto exit; + } + + if (copy_to_user((void __user *)user_data, + data_ptr, data_len)) { + ret = -EFAULT; + } +exit: + kfree(data_ptr); + return ret; +} + +static int aw_misc_ops_write_dsp(struct aw882xx *aw882xx, aw_ioctl_msg_t *msg) +{ + char __user* user_data = (char __user*)msg->data_buf; + uint32_t dsp_msg_id = (uint32_t)msg->opcode_id; + int data_len = msg->data_len; + int ret; + char *data_ptr; + + data_ptr = kmalloc(data_len, GFP_KERNEL); + if (!data_ptr) { + pr_err("%s : malloc failed !\n", __func__); + return -ENOMEM; + } + + if (copy_from_user(data_ptr, (void __user *)user_data, data_len)) { + pr_err("%s : copy data failed\n", __func__); + ret = -EFAULT; + goto exit; + } + + ret = aw_write_msg_to_dsp(aw882xx, dsp_msg_id, data_ptr, data_len); + if (ret) { + pr_err("%s : write failed\n", __func__); + } +exit: + kfree(data_ptr); + return ret; +} + +static int aw_misc_ops_msg(struct aw882xx *aw882xx, unsigned long arg) +{ + aw_ioctl_msg_t ioctl_msg; + + if (copy_from_user(&ioctl_msg, (void __user *)arg, sizeof(aw_ioctl_msg_t))) { + return -EFAULT; + } + + if(ioctl_msg.version != AW_IOCTL_MSG_VERSION) { + pr_err("%s:unsupported msg version %d\n", __func__, ioctl_msg.version); + return -EINVAL; + } + + if (ioctl_msg.type == AW_IOCTL_MSG_RD_DSP) { + return aw_misc_ops_read_dsp(aw882xx, &ioctl_msg); + } else if (ioctl_msg.type == AW_IOCTL_MSG_WR_DSP) { + return aw_misc_ops_write_dsp(aw882xx, &ioctl_msg); + } else { + pr_err("%s:unsupported msg type %d\n", __func__, ioctl_msg.type); + return -EINVAL; + } +} + +static int aw882xx_cali_operation(struct aw882xx *aw882xx, + unsigned int cmd, unsigned long arg) +{ + int16_t data_len = _IOC_SIZE(cmd); + int ret = 0; + char *data_ptr = NULL; + struct ptr_params_data *p_params; + int32_t *p_data; + + + data_ptr = kmalloc(data_len, GFP_KERNEL); + if (data_ptr == NULL) { + pr_err("%s : malloc failed !\n", __func__); + return -EFAULT; + } + + pr_info("cmd : %d, data_len%d\n", cmd , data_len); + switch (cmd) { + case AW882XX_IOCTL_ENABLE_CALI: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + g_aw882xx_cali_flag = (int8_t)data_ptr[0]; + pr_info("%s:set cali %s", __func__, + (g_aw882xx_cali_flag == 0) ? ("disable") : ("enable")); + } break; + case AW882XX_IOCTL_SET_CALI_CFG: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L); + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_GET_CALI_CFG: { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_GET_CALI_DATA: { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_REAL_DATA_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_REAL_DATA_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_SET_NOISE: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_NOISE_L, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_NOISE_L); + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_GET_F0: { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_F0_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_F0_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_SET_CALI_RE: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + if(aw882xx->default_re == 0) { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_RE_L, &aw882xx->default_re, sizeof(int32_t), false); + pr_info("aw882xx: default_re:%d\n", aw882xx->default_re); + } + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_RE_L, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_RE_L); + ret = -EFAULT; + goto exit; + } + aw882xx_set_cali_re(aw882xx, *((int32_t *)data_ptr)); + } break; + case AW882XX_IOCTL_GET_CALI_RE: { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_RE_L, + data_ptr, data_len, false); + if(aw882xx->default_re == 0) { + aw882xx->default_re = *((uint32_t *)data_ptr); + pr_info("aw882xx: default_re:%d\n", aw882xx->default_re); + } + if (ret) { + pr_err("%s: dsp_msg_read error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_RE_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_GET_VMAX: { + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_VMAX_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error:0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_VMAX_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_SET_VMAX: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_VMAX_L, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_VMAX_L); + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_SET_PARAM: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_PARAMS, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_PARAMS); + ret = -EFAULT; + goto exit; + } + pr_debug("%s: set params done", __func__); + } break; + case AW882XX_IOCTL_SET_PTR_PARAM_NUM: { + if (copy_from_user(data_ptr, (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + p_params = (struct ptr_params_data *)data_ptr; + if (p_params->data == NULL || (!p_params->len)) { + pr_err("%s: p_params error\n", __func__); + ret = -EFAULT; + goto exit; + } + p_data = kzalloc(p_params->len, GFP_KERNEL); + if (!p_data) { + pr_err("%s: error allocating memory\n", __func__); + ret = -ENOMEM; + goto exit; + } + + if (copy_from_user(p_data, (void __user *)p_params->data, + p_params->len)) { + kfree(p_data); + p_data = NULL; + ret = -EFAULT; + goto exit; + } + + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, + aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_PARAMS, + p_data, p_params->len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_PARAMS); + kfree(p_data); + p_data = NULL; + ret =-EFAULT; + goto exit; + } + kfree(p_data); + p_data = NULL; + } break; + case AW882XX_IOCTL_MSG: + ret = aw_misc_ops_msg(aw882xx, arg); + if (ret < 0) + goto exit; + break; + default: { + pr_err("%s : cmd %d\n", __func__, cmd); + } break; + } +exit: + kfree(data_ptr); + return ret; +} + +static DEVICE_ATTR(monitor, S_IWUSR | S_IRUGO, + aw882xx_monitor_show, aw882xx_monitor_store); +static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, + aw882xx_reg_show, aw882xx_reg_store); +static DEVICE_ATTR(rw, S_IWUSR | S_IRUGO, + aw882xx_rw_show, aw882xx_rw_store); +static DEVICE_ATTR(spk_rcv, S_IWUSR | S_IRUGO, + aw882xx_spk_rcv_show, aw882xx_spk_rcv_store); +static DEVICE_ATTR(mec, S_IWUSR | S_IRUGO, + aw882xx_mec_show, aw882xx_mec_store); +static DEVICE_ATTR(default_re, S_IWUSR | S_IRUGO, + aw882xx_default_re_show, NULL); +static DEVICE_ATTR(spk_temp, S_IWUSR | S_IRUGO, + aw882xx_spk_temp_show, NULL); +#ifdef AW_DEBUG +static DEVICE_ATTR(vol, S_IWUSR | S_IRUGO, + aw882xx_vol_show, aw882xx_vol_store); +static DEVICE_ATTR(temp, S_IWUSR | S_IRUGO, + aw882xx_temp_show, aw882xx_temp_store); +#endif + +static struct attribute *aw882xx_attributes[] = { + &dev_attr_monitor.attr, + &dev_attr_reg.attr, + &dev_attr_rw.attr, + &dev_attr_spk_rcv.attr, + &dev_attr_mec.attr, + &dev_attr_default_re.attr, + &dev_attr_spk_temp.attr, +#ifdef AW_DEBUG + &dev_attr_vol.attr, + &dev_attr_temp.attr, +#endif + NULL +}; + +static struct attribute_group aw882xx_attribute_group = { + .attrs = aw882xx_attributes +}; + +#define AW882XX_SMARTPA_NAME "aw882xx_smartpa" +static int aw882xx_file_open(struct inode *inode, struct file *file) +{ + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + file->private_data = (void *)g_aw882xx; + + pr_debug("open success"); + return 0; +} + +static int aw882xx_file_release(struct inode *inode, struct file *file) +{ + file->private_data = (void *)NULL; + + pr_debug("release successi\n"); + return 0; +} + +static long aw882xx_file_unlocked_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct aw882xx *aw882xx = NULL; + + if (((_IOC_TYPE(cmd)) != (AW882XX_IOCTL_MAGIC))) { + pr_err("%s: cmd magic err\n", __func__); + return -EINVAL; + } + aw882xx = (struct aw882xx *)file->private_data; + ret = aw882xx_cali_operation(aw882xx, cmd, arg); + if (ret) + return -EINVAL; + + return 0; +} + +static struct file_operations aw882xx_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = aw882xx_file_unlocked_ioctl, + .open = aw882xx_file_open, + .release = aw882xx_file_release, +}; + +static struct miscdevice aw882xx_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = AW882XX_SMARTPA_NAME, + .fops = &aw882xx_fops, +}; + +void init_aw882xx_misc_driver(struct aw882xx *aw882xx) +{ + int ret; + + ret = misc_register(&aw882xx_misc); + if (ret) { + pr_err("%s: misc register fail: %d\n", __func__, ret); + return; + } + pr_debug("%s: misc register success", __func__); +} + +/*monitor*/ +static int aw882xx_monitor_start(struct aw882xx_monitor *monitor) +{ + pr_debug("%s: enter\n", __func__); + + if (!hrtimer_active(&monitor->timer)) { + pr_info("%s: start monitor\n", __func__); + hrtimer_start(&monitor->timer, + ktime_set(monitor->timer_val/1000, + (monitor->timer_val%1000)*1000000), HRTIMER_MODE_REL); + } + + return 0; +} + +static int aw882xx_monitor_stop(struct aw882xx_monitor *monitor) +{ + pr_info("%s: enter\n", __func__); + + if (hrtimer_active(&monitor->timer)) { + pr_info("%s: stop monitor\n", __func__); + hrtimer_cancel(&monitor->timer); + } + return 0; +} + +static enum hrtimer_restart + aw882xx_monitor_timer_func(struct hrtimer *timer) +{ + struct aw882xx_monitor *monitor = + container_of(timer, struct aw882xx_monitor, timer); + + pr_debug("%s : enter\n", __func__); + + if (monitor->is_enable) + schedule_work(&monitor->work); + + return HRTIMER_NORESTART; +} +static int aw882xx_monitor_get_voltage(struct aw882xx *aw882xx, + unsigned int *vol) +{ + int ret = -1; + uint16_t local_vol = 0; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_VBAT_REG, vol); + if (ret < 0) { + pr_err("%s: read voltage failed !\n", __func__); + return ret; + } + local_vol = (*vol) * AW882XX_MONITOR_VBAT_RANGE + / AW882XX_MONITOR_INT_10BIT; + + if(aw882xx->monitor.vol_count < AW_VOL_COUNT) { + aw882xx->monitor.vol_count++; + if(aw882xx->monitor.pre_vol == 0) { + *vol = local_vol; + } else { + *vol = (aw882xx->monitor.pre_vol * (AW_VOL_COUNT -1 ) + + local_vol) / AW_VOL_COUNT; + } + } else { + *vol = (aw882xx->monitor.pre_vol * (AW_VOL_COUNT -1 ) + + local_vol) / AW_VOL_COUNT; + } + aw882xx->monitor.pre_vol = *vol; + pr_debug("%s: cur voltage is %d, vol is %d\n", __func__, local_vol, *vol); + return 0; +} +static int aw882xx_monitor_voltage(struct aw882xx *aw882xx, + struct aw882xx_low_vol *vol_cfg) +{ + int ret = -1; + int i; + unsigned int voltage = 0; + struct aw882xx_monitor *monitor = NULL; + + if (aw882xx == NULL || vol_cfg == NULL) { + pr_err("%s: pointer is NUL\n", __func__); + return ret; + } + monitor = &aw882xx->monitor; +#ifdef AW_DEBUG + if (monitor->test_vol == 0) { + ret = aw882xx_monitor_get_voltage(aw882xx, &voltage); + if (ret < 0) + return ret; + } else { + voltage = monitor->test_vol; + } +#else + ret = aw882xx_monitor_get_voltage(aw882xx, &voltage); + if (ret < 0) + return ret; +#endif + + if (monitor->first_entry == AW_FIRST_ENTRY) { + for (i = 0; i < monitor->vol_down_num; i++) { + if (voltage < monitor->vol_down_table[i].vol) { + *vol_cfg = monitor->vol_down_table[i]; + return 0; + } + } + + *vol_cfg = monitor->vol_up_table[0]; + return 0; + } + + if (voltage < monitor->vol_down_table[0].vol) { + *vol_cfg = monitor->vol_down_table[0]; + return 0; + } + + for (i = 1; i < monitor->vol_down_num; i++) { + if ((monitor->vol_up_table[monitor->vol_down_num - i].vol < voltage) && + (voltage < monitor->vol_down_table[i].vol)) { + *vol_cfg = monitor->vol_down_table[i]; + return 0; + } + } + + if (voltage > monitor->vol_up_table[0].vol) { + *vol_cfg = monitor->vol_up_table[0]; + return 0; + } + + return 0; +} +static int aw882xx_monitor_get_temperature(struct aw882xx *aw882xx, int *temp) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t local_temp; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_TEMP_REG, ®_val); + if (ret < 0) { + pr_err("%s: get temperature failed !\n", __func__); + return ret; + } + + local_temp = reg_val; + if (local_temp & AW882XX_MONITOR_TEMP_SIGN_MASK) + local_temp = local_temp | AW882XX_MONITOR_TEMP_NEG_MASK; + + *temp = (int)local_temp; + pr_debug("%s: chip temperature = %d\n", __func__, local_temp); + return 0; +} + +static int aw882xx_monitor_temperature(struct aw882xx *aw882xx, + struct aw882xx_low_temp *temp_cfg) +{ + int ret; + int i; + struct aw882xx_monitor *monitor = NULL; + int current_temp = 0; + + monitor = &aw882xx->monitor; +#ifdef AW_DEBUG + if (monitor->test_temp == 0) { + ret = aw882xx_monitor_get_temperature(aw882xx, ¤t_temp); + if (ret) + return ret; + } else { + current_temp = monitor->test_temp; + } +#else + ret = aw882xx_monitor_get_temperature(aw882xx, ¤t_temp); + if (ret < 0) + return ret; +#endif + + if (monitor->first_entry == AW_FIRST_ENTRY) { + monitor->first_entry = AW_NOT_FIRST_ENTRY; + for (i = 0; i < monitor->temp_down_num; i++) { + if (current_temp < monitor->temp_down_table[i].temp) { + temp_cfg->ipeak = monitor->temp_down_table[i].ipeak; + temp_cfg->gain = monitor->temp_down_table[i].gain; + temp_cfg->vmax = monitor->temp_down_table[i].vmax; + return 0; + } + } + + temp_cfg->ipeak = monitor->temp_up_table[0].ipeak; + temp_cfg->gain = monitor->temp_up_table[0].gain; + temp_cfg->vmax = monitor->temp_up_table[0].vmax; + return 0; + } + + if (current_temp < monitor->temp_down_table[0].temp) { + temp_cfg->ipeak = monitor->temp_down_table[0].ipeak; + temp_cfg->gain = monitor->temp_down_table[0].gain; + temp_cfg->vmax = monitor->temp_down_table[0].vmax; + return 0; + } + + for (i = 1; i < monitor->temp_down_num; i++) { + if ((monitor->temp_up_table[monitor->temp_down_num - i].temp < current_temp) + && (current_temp < monitor->temp_down_table[i].temp)) { + temp_cfg->ipeak = monitor->temp_down_table[i].ipeak; + temp_cfg->gain = monitor->temp_down_table[i].gain; + temp_cfg->vmax = monitor->temp_down_table[i].vmax; + return 0; + } + } + + if (current_temp > monitor->temp_up_table[0].temp) { + temp_cfg->ipeak = monitor->temp_up_table[0].ipeak; + temp_cfg->gain = monitor->temp_up_table[0].gain; + temp_cfg->vmax = monitor->temp_up_table[0].vmax; + return 0; + } + + return 0; +} + +static void aw882xx_monitor_get_cfg(struct aw882xx_low_temp *temp, + struct aw882xx_low_vol *vol) +{ + if (vol->ipeak == IPEAK_NONE) + return; + + if (temp->ipeak == IPEAK_NONE) { + temp->ipeak = vol->ipeak; + temp->gain = vol->gain; + return; + } + + /*get min ipeak*/ + if (temp->ipeak > vol->ipeak) + temp->ipeak = vol->ipeak; + + /*get min gain*/ + if (temp->gain < vol->gain) + temp->gain = vol->gain; + +} +static void aw882xx_monitor_set_ipeak(struct aw882xx *aw882xx, uint8_t ipeak) +{ + unsigned int reg_val = 0; + unsigned int read_reg_val; + int ret; + + if (ipeak == IPEAK_NONE) + return; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_SYSCTRL2_REG, ®_val); + if (ret < 0) { + pr_err("%s: read ipeak failed\n", __func__); + return; + } + + read_reg_val = reg_val; + read_reg_val &= AW882XX_BIT_SYSCTRL2_BST_IPEAK_MASK; + + if (read_reg_val == ipeak) { + pr_debug("%s: ipeak = 0x%x, no change\n", + __func__, read_reg_val); + return; + } + reg_val &= (~AW882XX_BIT_SYSCTRL2_BST_IPEAK_MASK); + read_reg_val = ipeak; + reg_val |= read_reg_val; + + ret = aw882xx_i2c_write(aw882xx, AW882XX_SYSCTRL2_REG, reg_val); + if (ret < 0) { + pr_err("%s: write ipeak failed\n", __func__); + return; + } + pr_debug("%s: set reg val = 0x%x, ipeak = 0x%x\n", + __func__, reg_val, ipeak); +} +static void aw882xx_monitor_set_gain(struct aw882xx *aw882xx, uint8_t gain) +{ + unsigned int reg_val = 0; + unsigned int read_reg_val; + int ret; + + if(aw882xx == NULL) { + return; + } + + if (gain == GAIN_NONE || (aw882xx->fade_work_start == 1)) + return; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_val); + if (ret < 0) { + pr_err("%s: read gain failed\n", __func__); + return; + } + + read_reg_val = reg_val >> AW882XX_BIT_HAGCCFG4_GAIN_SHIFT; + aw882xx->cur_gain = gain; + if (read_reg_val == gain) { + pr_debug("%s: gain = 0x%x, no change\n", + __func__, read_reg_val); + return; + } + reg_val &= AW882XX_BIT_HAGCCFG4_GAIN_MASK; + read_reg_val = gain; + reg_val |= (read_reg_val << AW882XX_BIT_HAGCCFG4_GAIN_SHIFT); + + ret = aw882xx_i2c_write(aw882xx, AW882XX_HAGCCFG4_REG, reg_val); + if (ret < 0) { + pr_err("%s: write gain failed\n", __func__); + return; + } + pr_debug("%s: set reg val = 0x%x, gain = 0x%x\n", + __func__, reg_val, gain); +} +static void aw882xx_monitor_set_vmax(struct aw882xx *aw882xx, uint32_t vmax) +{ + uint32_t local_vmax = vmax; + int ret; + + if (vmax == VMAX_NONE) + return; + + ret = aw_send_afe_cal_apr(aw882xx->afe_rx_portid, aw882xx->afe_tx_portid, + AFE_PARAM_ID_AWDSP_RX_VMAX_L, + &local_vmax, sizeof(uint32_t), true); + if (ret) + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_VMAX_L); + + pr_debug("%s: set vmax = 0x%x\n", __func__, vmax); +} +static void aw882xx_monitor_work(struct aw882xx *aw882xx) +{ + struct aw882xx_low_vol *vol_cfg = &aw882xx->monitor.vol_cfg; + struct aw882xx_low_temp *temp_cfg = &aw882xx->monitor.temp_cfg; + struct aw882xx_low_temp set_cfg; + int ret; + + if (aw882xx == NULL) { + pr_err("%s: pointer is NULL\n", __func__); + return; + } + if (g_aw882xx_cali_flag != 0 || g_aw882xx_profile_flag != 0) { + pr_info("%s: done nothing while start cali", __func__); + return; + } + + ret = aw882xx_monitor_voltage(aw882xx, vol_cfg); + if (ret < 0) { + pr_err("%s: monitor voltage failed\n", __func__); + return; + } + + ret = aw882xx_monitor_temperature(aw882xx, temp_cfg); + if (ret < 0) { + pr_err("%s: monitor temperature failed\n", __func__); + return; + } + pr_debug("%s: vol: ipeak = 0x%x, gain = 0x%x\n", + __func__, vol_cfg->ipeak, vol_cfg->gain); + pr_debug("%s: temp: ipeak = 0x%x, gain = 0x%x, vmax = 0x%x\n", + __func__, temp_cfg->ipeak, temp_cfg->gain, temp_cfg->vmax); + + memcpy(&set_cfg, temp_cfg, sizeof(struct aw882xx_low_temp)); + + aw882xx_monitor_get_cfg(&set_cfg, vol_cfg); + + aw882xx_monitor_set_ipeak(aw882xx, set_cfg.ipeak); + + aw882xx_monitor_set_gain(aw882xx, set_cfg.gain); + + aw882xx_monitor_set_vmax(aw882xx, set_cfg.vmax); +} + +static void aw882xx_monitor_init_gain(struct aw882xx *aw882xx) +{ + struct aw882xx_low_vol *vol_cfg = &aw882xx->monitor.vol_cfg; + struct aw882xx_low_temp *temp_cfg = &aw882xx->monitor.temp_cfg; + struct aw882xx_low_temp set_cfg; + int ret; + + if (aw882xx == NULL) { + pr_err("%s: pointer is NULL\n", __func__); + return; + } + + ret = aw882xx_monitor_voltage(aw882xx, vol_cfg); + if (ret < 0) { + pr_err("%s: monitor voltage failed\n", __func__); + return; + } + + ret = aw882xx_monitor_temperature(aw882xx, temp_cfg); + if (ret < 0) { + pr_err("%s: monitor temperature failed\n", __func__); + return; + } + + memcpy(&set_cfg, temp_cfg, sizeof(struct aw882xx_low_temp)); + + aw882xx_monitor_get_cfg(&set_cfg, vol_cfg); + aw882xx->cur_gain = set_cfg.gain; +} + +static int aw882xx_get_hmute(struct aw882xx *aw882xx) +{ + unsigned int reg_val = 0; + int ret; + + pr_debug("%s: enter\n", __func__); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSCTRL2_REG, ®_val); + if ((~AW882XX_HMUTE_MASK) & reg_val) + ret = 1; + else + ret = 0; + + return ret; +} +static void aw882xx_monitor_work_func(struct work_struct *work) +{ + struct aw882xx_monitor *monitor = container_of(work, + struct aw882xx_monitor, work); + struct aw882xx *aw882xx = container_of(monitor, + struct aw882xx, monitor); + + pr_debug("%s: enter\n", __func__); + mutex_lock(&aw882xx->lock); + if (!aw882xx_get_hmute(aw882xx)) { + aw882xx_monitor_work(aw882xx); + aw882xx_monitor_start(monitor); + } + mutex_unlock(&aw882xx->lock); +} + +void init_aw882xx_monitor(struct aw882xx_monitor *monitor) +{ + pr_info("%s: enter\n", __func__); + hrtimer_init(&monitor->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + monitor->timer.function = aw882xx_monitor_timer_func; + INIT_WORK(&monitor->work, aw882xx_monitor_work_func); + monitor->pre_vol = 0; + monitor->vol_count = 0; + monitor->vol_cfg.ipeak = monitor->vol_up_table[0].ipeak; + monitor->vol_cfg.gain = monitor->vol_up_table[0].gain; + monitor->temp_cfg.ipeak = monitor->temp_up_table[0].ipeak; + monitor->temp_cfg.gain = monitor->temp_up_table[0].gain; + monitor->temp_cfg.vmax = monitor->temp_up_table[0].vmax; +#ifdef AW_DEBUG + monitor->test_vol = 0; + monitor->test_temp = 0; +#endif +} + +/****************************************************** + * + * i2c driver + * + ******************************************************/ +static int aw882xx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct snd_soc_dai_driver *dai; + struct aw882xx *aw882xx; + struct device_node *np = i2c->dev.of_node; + int irq_flags = 0; + int ret = -1; + int i; + + pr_info("%s: enter\n", __func__); + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed\n"); + return -EIO; + } + + aw882xx = devm_kzalloc(&i2c->dev, sizeof(struct aw882xx), GFP_KERNEL); + if (aw882xx == NULL) + return -ENOMEM; + + aw882xx->dev = &i2c->dev; + aw882xx->i2c = i2c; + + i2c_set_clientdata(i2c, aw882xx); + mutex_init(&aw882xx->lock); + + /* aw882xx rst & int */ + if (np) { + ret = aw882xx_parse_dt(&i2c->dev, aw882xx, np); + if (ret) { + dev_err(&i2c->dev, + "%s: failed to parse device tree node\n", + __func__); + goto err_parse_dt; + } + } else { + aw882xx->reset_gpio = -1; + aw882xx->irq_gpio = -1; + } + + if (gpio_is_valid(aw882xx->reset_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw882xx->reset_gpio, + GPIOF_OUT_INIT_LOW, "aw882xx_rst"); + if (ret) { + dev_err(&i2c->dev, "%s: rst request failed\n", + __func__); + goto err_reset_gpio_request; + } + } + + if (gpio_is_valid(aw882xx->irq_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw882xx->irq_gpio, + GPIOF_DIR_IN, "aw882xx_int"); + if (ret) { + dev_err(&i2c->dev, "%s: int request failed\n", + __func__); + goto err_irq_gpio_request; + } + } + + /* hardware reset */ + aw882xx_hw_reset(aw882xx); + + /* aw882xx chip id */ + ret = aw882xx_read_chipid(aw882xx); + if (ret < 0) { + dev_err(&i2c->dev, "%s: aw882xx_read_chipid failed ret=%d\n", + __func__, ret); + goto err_id; + } + + /* aw882xx device name */ + //if (i2c->dev.of_node) + // dev_set_name(&i2c->dev, "%s", "aw882xx_smartpa"); + //else + // dev_err(&i2c->dev, "%s failed to set device name: %d\n", + // __func__, ret); + + /* register codec */ + dai = devm_kzalloc(&i2c->dev, sizeof(aw882xx_dai), GFP_KERNEL); + if (!dai) + goto err_dai_kzalloc; + + memcpy(dai, aw882xx_dai, sizeof(aw882xx_dai)); + pr_info("%s: dai->name(%s)\n", __func__, dai->name); + + ret = snd_soc_register_component(&i2c->dev, &soc_component_dev_aw882xx, + dai, ARRAY_SIZE(aw882xx_dai)); + if (ret < 0) { + dev_err(&i2c->dev, "%s failed to register aw882xx: %d\n", + __func__, ret); + goto err_register_codec; + } + + /* aw882xx irq */ + if (gpio_is_valid(aw882xx->irq_gpio) && + !(aw882xx->flags & AW882XX_FLAG_SKIP_INTERRUPTS)) { + aw882xx_interrupt_setup(aw882xx); + /* register irq handler */ + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&i2c->dev, + gpio_to_irq(aw882xx->irq_gpio), + NULL, aw882xx_irq, irq_flags, + "aw882xx", aw882xx); + if (ret != 0) { + dev_err(&i2c->dev, "failed to request IRQ %d: %d\n", + gpio_to_irq(aw882xx->irq_gpio), ret); + goto err_irq; + } + } else { + dev_info(&i2c->dev, "%s skipping IRQ registration\n", __func__); + /* disable feature support if gpio was invalid */ + aw882xx->flags |= AW882XX_FLAG_SKIP_INTERRUPTS; + } + + dev_set_drvdata(&i2c->dev, aw882xx); + ret = sysfs_create_group(&i2c->dev.kobj, &aw882xx_attribute_group); + if (ret < 0) { + dev_info(&i2c->dev, + "%s error creating sysfs attr files\n", + __func__); + goto err_sysfs; + } + + init_aw882xx_monitor(&aw882xx->monitor); + aw882xx_monitor_init_gain(aw882xx); + init_aw882xx_misc_driver(aw882xx); + g_aw882xx = aw882xx; + + aw882xx->default_re = 0; + aw882xx->need_fade = 0; + aw882xx->fade_work_start = 0; + aw882xx->delayed_time = 0; + aw882xx->is_fade_in = 0; + + /*init profile*/ + mutex_init(&aw882xx->profile.lock); + aw882xx->profile.cur_profile = 0; + aw882xx->profile.status = AW882XX_INIT_ST; + aw882xx->profile.len = 0; + for (i = 0; i < AW_PROFILE_MAX; i++) { + aw882xx->profile.data[i] = NULL; + } + + INIT_DELAYED_WORK(&aw882xx->fade_work, aw882xx_fade_work_func); + /*global init*/ + atomic_set(&g_algo_rx_enable, 0); + atomic_set(&g_algo_tx_enable, 0); + atomic_set(&g_skt_disable, 0); +#ifdef AW882XX_RUNIN_TEST + atomic_set(&g_runin_test, 0); + INIT_DELAYED_WORK(&aw882xx->adsp_status, aw882xx_set_adsp_module_status); +#endif + pr_info("%s: probe completed successfully!\n", __func__); + + return 0; + +err_sysfs: + devm_free_irq(&i2c->dev, gpio_to_irq(aw882xx->irq_gpio), aw882xx); +err_irq: + snd_soc_unregister_component(&i2c->dev); +err_register_codec: + devm_kfree(&i2c->dev, dai); + dai = NULL; +err_dai_kzalloc: +err_id: + if (gpio_is_valid(aw882xx->irq_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->irq_gpio); +err_irq_gpio_request: + if (gpio_is_valid(aw882xx->reset_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->reset_gpio); +err_reset_gpio_request: +err_parse_dt: + devm_kfree(&i2c->dev, aw882xx); + aw882xx = NULL; + g_aw882xx = NULL; + return ret; +} + +static int aw882xx_i2c_remove(struct i2c_client *i2c) +{ + struct aw882xx *aw882xx = i2c_get_clientdata(i2c); + + pr_info("%s: enter\n", __func__); + misc_deregister(&aw882xx_misc); + if (gpio_to_irq(aw882xx->irq_gpio)) + devm_free_irq(&i2c->dev, + gpio_to_irq(aw882xx->irq_gpio), + aw882xx); + + snd_soc_unregister_component(&i2c->dev); + + if (gpio_is_valid(aw882xx->irq_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->irq_gpio); + if (gpio_is_valid(aw882xx->reset_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->reset_gpio); + + devm_kfree(&i2c->dev, aw882xx); + aw882xx = NULL; + g_aw882xx = NULL; + return 0; +} + +static const struct i2c_device_id aw882xx_i2c_id[] = { + { AW882XX_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw882xx_i2c_id); + +static struct of_device_id aw882xx_dt_match[] = { + { .compatible = "awinic,aw882xx_smartpa" }, + { }, +}; + +static struct i2c_driver aw882xx_i2c_driver = { + .driver = { + .name = AW882XX_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw882xx_dt_match), + }, + .probe = aw882xx_i2c_probe, + .remove = aw882xx_i2c_remove, + .id_table = aw882xx_i2c_id, +}; + +static int __init aw882xx_i2c_init(void) +{ + int ret = -1; + + pr_info("%s: aw882xx driver version %s\n", __func__, AW882XX_VERSION); + + ret = i2c_add_driver(&aw882xx_i2c_driver); + if (ret) + pr_err("%s: fail to add aw882xx device into i2c\n", __func__); + + return ret; +} + +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif + +module_init(aw882xx_i2c_init); + + +static void __exit aw882xx_i2c_exit(void) +{ + i2c_del_driver(&aw882xx_i2c_driver); +} +module_exit(aw882xx_i2c_exit); + +MODULE_DESCRIPTION("ASoC AW882XX Smart PA Driver"); +MODULE_LICENSE("GPL v2"); + +#endif /* CONFIG_AW882XX_CODEC */ diff --git a/techpack/audio/asoc/codecs/aw882xx/aw882xx.h b/techpack/audio/asoc/codecs/aw882xx/aw882xx.h new file mode 100644 index 000000000000..0cbf46a30da3 --- /dev/null +++ b/techpack/audio/asoc/codecs/aw882xx/aw882xx.h @@ -0,0 +1,317 @@ +#ifndef _AW882XX_H_ +#define _AW882XX_H_ + +//#define AW_DEBUG +/* + * i2c transaction on Linux limited to 64k + * (See Linux kernel documentation: Documentation/i2c/writing-clients) +*/ +#define MAX_I2C_BUFFER_SIZE 65536 +#define AW882XX_I2C_READ_MSG_NUM 2 + +#define AW_VOL_COUNT (3) +#define AW882XX_FLAG_START_ON_MUTE (1 << 0) +#define AW882XX_FLAG_SKIP_INTERRUPTS (1 << 1) +#define AW882XX_FLAG_SAAM_AVAILABLE (1 << 2) +#define AW882XX_FLAG_STEREO_DEVICE (1 << 3) +#define AW882XX_FLAG_MULTI_MIC_INPUTS (1 << 4) + +#define AW882XX_NUM_RATES 9 +#define AW882XX_SYSST_CHECK_MAX 10 + +#define AW882XX_MODE_SHIFT_MAX 2 + +#define AW882XX_DEFAULT_AFE_RX_PROT_ID 0x1000 /*AFE_PORT_ID_PRIMARY_MI2S_RX*/ +#define AW882XX_DEFAULT_AFE_TX_PROT_ID 0x1001 /*AFE_PORT_ID_PRIMARY_MI2S_TX*/ +#define AW_MODULE_ID_COPP (0X10013D02) /*SKT module id*/ +#define AW_MODULE_PARAMS_ID_COPP_ENABLE (0X10013D14) /*SKT enable param id*/ + +#define AW_IOCTL_MSG_VERSION (0) + +#define DEFAULT_CALI_VALUE (7) +#define ERRO_CALI_VALUE (0) +#define AFE_PARAM_ID_AWDSP_RX_SET_ENABLE (0x10013D11) +#define AFE_PARAM_ID_AWDSP_RX_PARAMS (0x10013D12) +#define AFE_PARAM_ID_AWDSP_TX_SET_ENABLE (0x10013D13) +#define AFE_PARAM_ID_AWDSP_RX_VMAX_L (0X10013D17) +#define AFE_PARAM_ID_AWDSP_RX_VMAX_R (0X10013D18) +#define AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L (0X10013D19) +#define AFE_PARAM_ID_AWDSP_RX_CALI_CFG_R (0x10013d1A) +#define AFE_PARAM_ID_AWDSP_RX_RE_L (0x10013d1B) +#define AFE_PARAM_ID_AWDSP_RX_RE_R (0X10013D1C) +#define AFE_PARAM_ID_AWDSP_RX_NOISE_L (0X10013D1D) +#define AFE_PARAM_ID_AWDSP_RX_NOISE_R (0X10013D1E) +#define AFE_PARAM_ID_AWDSP_RX_F0_L (0X10013D1F) +#define AFE_PARAM_ID_AWDSP_RX_F0_R (0X10013D20) +#define AFE_PARAM_ID_AWDSP_RX_REAL_DATA_L (0X10013D21) +#define AFE_PARAM_ID_AWDSP_RX_REAL_DATA_R (0X10013D22) +#define AFE_PARAM_ID_AWDSP_RX_MSG (0X10013D2A) + +enum { + AW_FIRST_ENTRY = 0, + AW_NOT_FIRST_ENTRY = 1, +}; + +enum { + AW_IOCTL_MSG_IOCTL = 0, + AW_IOCTL_MSG_RD_DSP, + AW_IOCTL_MSG_WR_DSP +}; + +enum AWINIC_CALI_CMD{ + AW_CALI_CMD_NONE = 0, + AW_CALI_CMD_START, + AW_CALI_CMD_END, + AW_CALI_CMD_SET_CALI_CFG, + AW_CALI_CMD_GET_CALI_CFG, + AW_CALI_CMD_GET_CALI_DATA, + AW_CALI_CMD_SET_NOISE, + AW_CALI_CMD_GET_F0, + AW_CALI_CMD_SET_CALI_RE, + AW_CALI_CMD_GET_CALI_RE, + AW_CALI_CMD_SET_VMAX, + AW_CALI_CMD_GET_VMAX, + AW_CALI_CMD_MAX, +}; + +enum aw882xx_dsp_msg_type { + DSP_MSG_TYPE_DATA = 0, + DSP_MSG_TYPE_CMD = 1, +}; + +#define AW882XX_CALI_CFG_NUM 3 +#define AW882XX_CALI_DATA_NUM 6 +#define AW882XX_PARAMS_NUM 400 +#define AWINIC_DSP_MSG_HDR_VER (1) + +struct cali_cfg{ + int32_t data[AW882XX_CALI_CFG_NUM]; +}; +struct cali_data{ + int32_t data[AW882XX_CALI_DATA_NUM]; +}; +struct params_data{ + int32_t data[AW882XX_PARAMS_NUM]; +}; +struct ptr_params_data { + int len; + int32_t *data; +}; + +typedef struct { + int32_t type; + int32_t opcode_id; + int32_t version; + int32_t data_len; + char *data_buf; + int32_t reseriver[2]; +}aw_ioctl_msg_t; + +struct aw_dsp_msg_hdr { + int32_t type; + int32_t opcode_id; + int32_t version; + int32_t reserver[3]; +}; + + +#define AW882XX_IOCTL_MAGIC 'a' +#define AW882XX_IOCTL_SET_CALI_CFG _IOWR(AW882XX_IOCTL_MAGIC, 1, struct cali_cfg) +#define AW882XX_IOCTL_GET_CALI_CFG _IOWR(AW882XX_IOCTL_MAGIC, 2, struct cali_cfg) +#define AW882XX_IOCTL_GET_CALI_DATA _IOWR(AW882XX_IOCTL_MAGIC, 3, struct cali_data) +#define AW882XX_IOCTL_SET_NOISE _IOWR(AW882XX_IOCTL_MAGIC, 4, int32_t) +#define AW882XX_IOCTL_GET_F0 _IOWR(AW882XX_IOCTL_MAGIC, 5, int32_t) +#define AW882XX_IOCTL_SET_CALI_RE _IOWR(AW882XX_IOCTL_MAGIC, 6, int32_t) +#define AW882XX_IOCTL_GET_CALI_RE _IOWR(AW882XX_IOCTL_MAGIC, 7, int32_t) +#define AW882XX_IOCTL_SET_VMAX _IOWR(AW882XX_IOCTL_MAGIC, 8, int32_t) +#define AW882XX_IOCTL_GET_VMAX _IOWR(AW882XX_IOCTL_MAGIC, 9, int32_t) +#define AW882XX_IOCTL_SET_PARAM _IOWR(AW882XX_IOCTL_MAGIC, 10,struct params_data) +#define AW882XX_IOCTL_ENABLE_CALI _IOWR(AW882XX_IOCTL_MAGIC, 11,int8_t) +#define AW882XX_IOCTL_SET_PTR_PARAM_NUM _IOWR(AW882XX_IOCTL_MAGIC, 12, struct ptr_params_data) +#define AW882XX_IOCTL_MSG _IOWR(AW882XX_IOCTL_MAGIC, 16, aw_ioctl_msg_t) + + +enum aw882xx_init { + AW882XX_INIT_ST = 0, + AW882XX_INIT_OK = 1, + AW882XX_INIT_NG = 2, +}; + +enum aw882xx_power_status { + AW882XX_POWER_DOWN = 0, + AW882XX_POWER_ING = 1, + AW882XX_POWER_UP = 2, +}; + +enum aw882xx_chipid { + AW882XX_ID = 0x1852, +}; + +enum aw882xx_modeshift { + AW882XX_MODE_SPK_SHIFT = 0, + AW882XX_MODE_RCV_SHIFT = 1, +}; + +enum aw882xx_mode_spk_rcv { + AW882XX_SPEAKER_MODE = 0, + AW882XX_RECEIVER_MODE = 1, +}; +//smartpa monitor +enum aw882xx_ipeak { + IPEAK_3P50_A = 0x08, + IPEAK_3P00_A = 0x06, + IPEAK_2P75_A = 0x05, + IPEAK_2P50_A = 0x04, + IPEAK_NONE = 0xFF, +}; + +enum aw882xx_gain { + GAIN_NEG_0P0_DB = 0x00, + GAIN_NEG_0P5_DB = 0x01, + GAIN_NEG_1P0_DB = 0x02, + GAIN_NEG_1P5_DB = 0x03, + GAIN_NEG_3P0_DB = 0x06, + GAIN_NEG_4P5_DB = 0x09, + GAIN_NEG_6P0_DB = 0x10, + GAIN_NONE = 0xFF, +}; + +enum aw882xx_vmax_percentage { + VMAX_100_PERCENTAGE = 0x00000000, + VMAX_086_PERCENTAGE = 0xFFED714D, + VMAX_075_PERCENTAGE = 0xFFD80505, + VMAX_063_PERCENTAGE = 0xFFBEAE7E, + VMAX_NONE = 0xFFFFFFFF, +}; + +#define AW882XX_MONITOR_DEFAULT_FLAG 0 +#define AW882XX_MONITOR_DEFAULT_TIMER_VAL 30000 +#define AW882XX_MONITOR_VBAT_RANGE 6025 +#define AW882XX_MONITOR_INT_10BIT 1023 +#define AW882XX_MONITOR_TEMP_SIGN_MASK (1<<9) +#define AW882XX_MONITOR_TEMP_NEG_MASK (0XFC00) +#define AW882XX_BIT_SYSCTRL2_BST_IPEAK_MASK ( 15<< 0) +#define AW882XX_BIT_HAGCCFG4_GAIN_SHIFT (8) +#define AW882XX_BIT_HAGCCFG4_GAIN_MASK (0x00ff) + +struct aw882xx_low_vol { + uint32_t vol; + uint8_t ipeak; + uint8_t gain; +}; + +struct aw882xx_low_temp { + int16_t temp; + uint8_t ipeak; + uint8_t gain; + uint32_t vmax; +}; + +struct aw882xx_monitor{ + struct hrtimer timer; + uint32_t timer_val; + struct work_struct work; + uint32_t is_enable; + uint16_t pre_vol; + uint16_t vol_count; + uint8_t first_entry; + + struct aw882xx_low_vol vol_cfg; + struct aw882xx_low_temp temp_cfg; + int vol_up_num; + int vol_down_num; + int temp_up_num; + int temp_down_num; + struct aw882xx_low_vol *vol_up_table; + struct aw882xx_low_vol *vol_down_table; + struct aw882xx_low_temp *temp_up_table; + struct aw882xx_low_temp *temp_down_table; + +#ifdef AW_DEBUG + uint16_t test_vol; + int16_t test_temp; +#endif +}; + +/*control runin test function*/ +#define AW882XX_RUNIN_TEST +enum AWINIC_PROFILE{ + AW_PROFILE_MUSIC = 0, + AW_PROFILE_RINGTONE, + AW_PROFILE_NOTIFICATION, + AW_PROFILE_VOICE, + AW_PROFILE_MAX, +}; + +#define VERSION_MAX 4 +#define PROJECT_NAME_MAX 24 +#define VOLUME_STEP_DB (6) +#define VOLUME_MIN_NEG_90_DB (90) +#define FADE_STEP_DB (6) + +typedef struct awinic_afe_param_header{ + uint8_t fw[VERSION_MAX]; + uint8_t cfg[VERSION_MAX]; + uint8_t project[PROJECT_NAME_MAX]; + uint32_t start; + uint32_t params_len; + uint8_t check_sum; + uint8_t profile_num; + uint8_t reserve[2]; +}aw_afe_params_hdr_t; + +struct profile_info { + struct mutex lock; + int cur_profile; + int status; + int len; + char* data[AW_PROFILE_MAX]; +}; + +struct aw882xx { + struct regmap *regmap; + struct i2c_client *i2c; + struct snd_soc_component *component; + struct device *dev; + struct mutex lock; + + struct aw882xx_monitor monitor; +#ifdef AW882XX_RUNIN_TEST + struct delayed_work adsp_status; +#endif + struct delayed_work fade_work; + int delayed_time; + int fade_work_start; + int is_fade_in; + int sysclk; + int rate; + int pstream; + int cstream; + + int reset_gpio; + int irq_gpio; + int power_flag; + + unsigned char reg_addr; + + unsigned int flags; + unsigned int chipid; + unsigned int init; + unsigned int spk_rcv_mode; + uint32_t cali_re; + uint32_t default_re; + uint8_t cur_gain; + uint8_t need_fade; + unsigned int cfg_num; + unsigned int afe_rx_portid; + unsigned int afe_tx_portid; + unsigned int afe_profile; + unsigned int fade_flag; + struct profile_info profile; +}; + +struct aw882xx_container { + int len; + unsigned char data[]; +}; +#endif diff --git a/techpack/audio/asoc/codecs/aw882xx/aw882xx_reg.h b/techpack/audio/asoc/codecs/aw882xx/aw882xx_reg.h new file mode 100644 index 000000000000..4d680beec550 --- /dev/null +++ b/techpack/audio/asoc/codecs/aw882xx/aw882xx_reg.h @@ -0,0 +1,1570 @@ +#ifndef __AW882XX_REG_H__ +#define __AW882XX_REG_H__ + +/* registers list */ +#define AW882XX_ID_REG (0x00) +#define AW882XX_SYSST_REG (0x01) +#define AW882XX_SYSINT_REG (0x02) +#define AW882XX_SYSINTM_REG (0x03) +#define AW882XX_SYSCTRL_REG (0x04) +#define AW882XX_SYSCTRL2_REG (0x05) +#define AW882XX_I2SCTRL_REG (0x06) +#define AW882XX_I2SCFG1_REG (0x07) +#define AW882XX_I2SCFG2_REG (0x08) +#define AW882XX_HAGCCFG1_REG (0x09) +#define AW882XX_HAGCCFG2_REG (0x0A) +#define AW882XX_HAGCCFG3_REG (0x0B) +#define AW882XX_HAGCCFG4_REG (0x0C) +#define AW882XX_HAGCCFG5_REG (0x0D) +#define AW882XX_HAGCCFG6_REG (0x0E) +#define AW882XX_HAGCCFG7_REG (0x0F) +#define AW882XX_HAGCST_REG (0x10) +#define AW882XX_PRODID_REG (0x11) +#define AW882XX_VBAT_REG (0x12) +#define AW882XX_TEMP_REG (0x13) +#define AW882XX_PVDD_REG (0x14) +#define AW882XX_DBGCTRL_REG (0x20) +#define AW882XX_I2SINT_REG (0x21) +#define AW882XX_I2SCAPCNT_REG (0x22) +#define AW882XX_CRCIN_REG (0x38) +#define AW882XX_CRCOUT_REG (0x39) +#define AW882XX_VSNCTRL1_REG (0x50) +#define AW882XX_ISNCTRL1_REG (0x52) +#define AW882XX_ISNCTRL2_REG (0x53) +#define AW882XX_VTMCTRL1_REG (0x54) +#define AW882XX_VTMCTRL2_REG (0x55) +#define AW882XX_VTMCTRL3_REG (0x56) +#define AW882XX_ISNDAT_REG (0x57) +#define AW882XX_VSNDAT_REG (0x58) +#define AW882XX_PWMCTRL_REG (0x59) +#define AW882XX_PWMCTRL2_REG (0x5A) +#define AW882XX_BSTCTRL1_REG (0x60) +#define AW882XX_BSTCTRL2_REG (0x61) +#define AW882XX_BSTCTRL3_REG (0x62) +#define AW882XX_BSTDBG1_REG (0x63) +#define AW882XX_BSTDBG2_REG (0x64) +#define AW882XX_BSTDBG3_REG (0x65) +#define AW882XX_PLLCTRL1_REG (0x66) +#define AW882XX_PLLCTRL2_REG (0x67) +#define AW882XX_PLLCTRL3_REG (0x68) +#define AW882XX_CDACTRL1_REG (0x69) +#define AW882XX_CDACTRL2_REG (0x6A) +#define AW882XX_SADCCTRL_REG (0x6B) +#define AW882XX_TESTCTRL1_REG (0x70) +#define AW882XX_TESTCTRL2_REG (0x71) +#define AW882XX_EFCTRL1_REG (0x72) +#define AW882XX_EFCTRL2_REG (0x73) +#define AW882XX_EFWH_REG (0x74) +#define AW882XX_EFWM2_REG (0x75) +#define AW882XX_EFWM1_REG (0x76) +#define AW882XX_EFWL_REG (0x77) +#define AW882XX_EFRH_REG (0x78) +#define AW882XX_EFRM2_REG (0x79) +#define AW882XX_EFRM1_REG (0x7A) +#define AW882XX_EFRL_REG (0x7B) +#define AW882XX_TESTDET_REG (0x7C) + +/******************************************** + * Register Access + *******************************************/ +#define AW882XX_REG_MAX (0x7D) + +#define REG_NONE_ACCESS (0) +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) + +const unsigned char aw882xx_reg_access[AW882XX_REG_MAX] = { + [AW882XX_ID_REG] = (REG_RD_ACCESS), + [AW882XX_SYSST_REG] = (REG_RD_ACCESS), + [AW882XX_SYSINT_REG] = (REG_RD_ACCESS), + [AW882XX_SYSINTM_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_SYSCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_SYSCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_I2SCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_I2SCFG1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_I2SCFG2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG4_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG5_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG6_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG7_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCST_REG] = (REG_RD_ACCESS), + [AW882XX_PRODID_REG] = (REG_RD_ACCESS), + [AW882XX_VBAT_REG] = (REG_RD_ACCESS), + [AW882XX_TEMP_REG] = (REG_RD_ACCESS), + [AW882XX_PVDD_REG] = (REG_RD_ACCESS), + [AW882XX_DBGCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_I2SINT_REG] = (REG_RD_ACCESS), + [AW882XX_I2SCAPCNT_REG] = (REG_RD_ACCESS), + [AW882XX_CRCIN_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_CRCOUT_REG] = (REG_RD_ACCESS), + [AW882XX_VSNCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_ISNCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_ISNCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_VTMCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_VTMCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_VTMCTRL3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_ISNDAT_REG] = (REG_RD_ACCESS), + [AW882XX_VSNDAT_REG] = (REG_RD_ACCESS), + [AW882XX_PWMCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_PWMCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTCTRL3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTDBG1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTDBG2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTDBG3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_PLLCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_PLLCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_PLLCTRL3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_CDACTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_CDACTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_SADCCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_TESTCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_TESTCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFWH_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFWM2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFWM1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFWL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFRH_REG] = (REG_RD_ACCESS), + [AW882XX_EFRM2_REG] = (REG_RD_ACCESS), + [AW882XX_EFRM1_REG] = (REG_RD_ACCESS), + [AW882XX_EFRL_REG] = (REG_RD_ACCESS), + [AW882XX_TESTDET_REG] = (REG_RD_ACCESS), +}; + +/******************************************** + * Register Detail + *******************************************/ +/* detail information of registers begin */ +/* ID (0x00) detail */ +/* IDCODE bit 15:0 (ID 0x00) */ +#define AW882XX_IDCODE_START_BIT (0) +#define AW882XX_IDCODE_BITS_LEN (16) +#define AW882XX_IDCODE_MASK \ + (~(((1< + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/*#define DEBUG*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aw882xx.h" +#include "aw_log.h" +#include "aw_dsp.h" + +#define AW882XX_DRIVER_VERSION "v1.9.0.9" +#define AW882XX_I2C_NAME "aw882xxacf_smartpa" + +#define AW_READ_CHIPID_RETRIES 5 /* 5 times */ +#define AW_READ_CHIPID_RETRY_DELAY 5 /* 5 ms */ + +static unsigned int g_aw882xx_dev_cnt = 0; +static unsigned int g_print_dbg = 0; +static unsigned int g_algo_rx_en = false; +static unsigned int g_algo_tx_en = false; +static unsigned int g_algo_copp_en = false; +#ifdef AW_SPIN_ENABLE +static unsigned int g_spin_value = 0; +static uint32_t g_spin_en = 0; +static uint32_t g_spin_relase_time = 100; +#endif +#ifdef AW882XX_RUNIN_TEST +static unsigned int g_runin_test; +#endif + +static DEFINE_MUTEX(g_aw882xx_lock); +struct aw_container *g_awinic_cfg = NULL; +struct aw_container *g_awinic_skt_cfg = NULL; + + +#define AW882XX_MOTO_MAX_GAIN (127) + +#define AW882XX_SCENE_CALIBRATION_ID 0 +#define AW882XX_SCENE_BYPASS_ID 1 +#define AW882XX_SCENE_HANDSET_ID 2 +#define AW882XX_SCENE_VOICE_ID 3 +#define AW882XX_SCENE_VOIP_ID 4 +#define AW882XX_SCENE_DEEPBUFFER_ID 5 +#define AW882XX_SCENE_FASTTRACK_ID 6 +#define AW882XX_SCENE_HAC_ID 7 + +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS +struct aw882xx_scene_info aw882xx_scene_state[AW_ALGO_PROFILE_ID_MAX] = { + { + AW882XX_SCENE_CALIBRATION_ID, + "Calibration", + 1, + 1, + 0, + 0, + }, + { + AW882XX_SCENE_BYPASS_ID, + "Bypass", + 2, + 2, + 0, + 0, + }, + { + AW882XX_SCENE_HANDSET_ID, + "Handset", + 4, + 3, + 0, + 0, + }, + { + AW882XX_SCENE_VOICE_ID, + "Voice", + 5, + 4, + 0, + 0, + }, + { + AW882XX_SCENE_VOIP_ID, + "Voip", + 6, + 5, + 0, + 0, + }, + { + AW882XX_SCENE_DEEPBUFFER_ID, + "deepbuffer", + 7, + 6, + 0, + 0, + }, + { + AW882XX_SCENE_FASTTRACK_ID, + "fasttrack", + 8, + 7, + 0, + 0, + }, + { + AW882XX_SCENE_HAC_ID, + "Hac", + 3, + 8, + 0, + 0, + }, + +}; +#endif + +static const char *const switch_status[] = { "Off", "On" }; +static const char *const aw882xx_ramp_status[] = { "Off", "On" }; +static const char *const aw882xx_switch[] = {"Disable", "Enable"}; +#ifdef AW_SPIN_ENABLE +static const char *const aw882xx_spin[] = {"spin_0", "spin_90", + "spin_180", "spin_270"}; +#endif + +/****************************************************** + * + * aw882xx distinguish between codecs and components by version + * + ******************************************************/ +#ifdef AW_KERNEL_VER_OVER_4_19_1 +static struct aw_componet_codec_ops aw_componet_codec_ops = { + .kcontrol_codec = snd_soc_kcontrol_component, + .codec_get_drvdata = snd_soc_component_get_drvdata, + .add_codec_controls = snd_soc_add_component_controls, + .unregister_codec = snd_soc_unregister_component, + .register_codec = snd_soc_register_component, +}; +#else +static struct aw_componet_codec_ops aw_componet_codec_ops = { + .kcontrol_codec = snd_soc_kcontrol_codec, + .codec_get_drvdata = snd_soc_codec_get_drvdata, + .add_codec_controls = snd_soc_add_codec_controls, + .unregister_codec = snd_soc_unregister_codec, + .register_codec = snd_soc_register_codec, +}; +#endif + +static aw_snd_soc_codec_t *aw_get_codec(struct snd_soc_dai *dai) +{ +#ifdef AW_KERNEL_VER_OVER_4_19_1 + return dai->component; +#else + return dai->codec; +#endif +} + + +/****************************************************** + * + * aw882xx i2c write/read + * + ******************************************************/ +int aw882xx_get_version(char *buf, int size) +{ + if (size > strlen(AW882XX_DRIVER_VERSION)) { + memcpy(buf, AW882XX_DRIVER_VERSION, strlen(AW882XX_DRIVER_VERSION)); + return strlen(AW882XX_DRIVER_VERSION); + } else { + return -ENOMEM; + } +} + +int aw882xx_get_dev_num(void) { + return g_aw882xx_dev_cnt; +} + +static int aw882xx_i2c_writes(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned char *buf, unsigned int len) +{ + int ret = -1; + unsigned char *data; + + data = kmalloc(len+1, GFP_KERNEL); + if (data == NULL) { + aw_dev_err(aw882xx->dev, "can not allocate memory"); + return -ENOMEM; + } + + data[0] = reg_addr; + memcpy(&data[1], buf, len); + + ret = i2c_master_send(aw882xx->i2c, data, len+1); + if (ret < 0) + aw_dev_err(aw882xx->dev, "i2c master send error"); + + kfree(data); + + return ret; +} + +static int aw882xx_i2c_reads(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned char *data_buf, unsigned int data_len) +{ + int ret; + struct i2c_msg msg[] = { + [0] = { + .addr = aw882xx->i2c->addr, + .flags = 0, + .len = sizeof(uint8_t), + .buf = ®_addr, + }, + [1] = { + .addr = aw882xx->i2c->addr, + .flags = I2C_M_RD, + .len = data_len, + .buf = data_buf, + }, + }; + + ret = i2c_transfer(aw882xx->i2c->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "transfer failed."); + return ret; + } else if (ret != AW882XX_I2C_READ_MSG_NUM) { + aw_dev_err(aw882xx->dev, "transfer failed(size error)."); + return -ENXIO; + } + + return 0; +} + +int aw882xx_i2c_write(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + unsigned char buf[2]; + + buf[0] = (reg_data&0xff00)>>8; + buf[1] = (reg_data&0x00ff)>>0; + + while (cnt < AW_I2C_RETRIES) { + ret = aw882xx_i2c_writes(aw882xx, reg_addr, buf, 2); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "i2c_write cnt=%d error=%d", + cnt, ret); + } else { + if (g_print_dbg) + aw_dev_info(aw882xx->dev, "reg_addr: 0x%04x, reg_data :0x%04x", + (uint16_t)reg_addr, (uint16_t)reg_data); + break; + } + cnt++; + } + + return ret; +} + +int aw882xx_i2c_read(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int *reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + unsigned char buf[2]; + + while (cnt < AW_I2C_RETRIES) { + ret = aw882xx_i2c_reads(aw882xx, reg_addr, buf, 2); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "i2c_read cnt=%d error=%d", + cnt, ret); + } else { + *reg_data = (buf[0]<<8) | (buf[1]<<0); + if (g_print_dbg) + aw_dev_info(aw882xx->dev, "reg_addr: 0x%04x, reg_data :0x%04x", + (uint16_t)reg_addr, (uint16_t)(*reg_data)); + break; + } + cnt++; + } + + return ret; +} + +int aw882xx_i2c_write_bits(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int mask, unsigned int reg_data) +{ + int ret = -1; + unsigned int reg_val = 0; + + ret = aw882xx_i2c_read(aw882xx, reg_addr, ®_val); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "i2c read error, ret=%d", ret); + return ret; + } + reg_val &= mask; + reg_val |= reg_data; + ret = aw882xx_i2c_write(aw882xx, reg_addr, reg_val); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "i2c read error, ret=%d", ret); + return ret; + } + + return 0; +} +#ifdef CONFIG_AW882XX_STEREO_SMARTPA +static void *aw882xx_devm_kstrdup(struct device *dev, char *buf) +{ + char *str; + + str = devm_kzalloc(dev, strlen(buf) + 1, GFP_KERNEL); + if (!str) + return str; + + memcpy(str, buf, strlen(buf)); + return str; +} +#endif +/***************************************************** + * + * snd_soc_dai_driver ops + * + *****************************************************/ +static int aw882xx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aw_dev_info(aw882xx->dev, "playback enter"); + /*load cali re*/ + aw_dev_init_cali_re(aw882xx->aw_pa); + } else { + aw_dev_info(aw882xx->dev, "capture enter"); + } + return 0; +} + +static int aw882xx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + /*struct aw882xx *aw882xx = aw_snd_soc_codec_get_drvdata(dai->codec);*/ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + + aw_dev_info(codec->dev, "fmt=0x%x", fmt); + + /* Supported mode: regular I2S, slave, or PDM */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != + SND_SOC_DAIFMT_CBS_CFS) { + aw_dev_err(codec->dev, "invalid codec master mode"); + return -EINVAL; + } + break; + default: + aw_dev_err(codec->dev, "unsupported DAI format %d", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + return 0; +} + +static int aw882xx_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + aw_dev_info(aw882xx->dev, "freq=%d", freq); + + aw882xx->sysclk = freq; + return 0; +} + +static int aw882xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + aw_dev_dbg(aw882xx->dev, "stream capture requested rate: %d, sample size: %d", + params_rate(params), params_width(params)); + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aw_dev_dbg(aw882xx->dev, "stream playback requested rate: %d, sample size: %d", + params_rate(params), params_width(params)); + } + + return 0; +} + +static void aw882xx_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aw882xx->rate = 0; + aw_dev_info(aw882xx->dev, "stream playback"); + } else { + aw_dev_info(aw882xx->dev, "stream capture"); + } +} + +static void aw882xx_start_pa(struct aw882xx *aw882xx) +{ + int ret; + int i; + + aw_dev_info(aw882xx->dev, "enter"); + + if (aw882xx->fw_status == AW_DEV_FW_OK) { + if (aw882xx->allow_pw == false) { + aw_dev_info(aw882xx->dev, "dev can not allow power "); + return; + } + + for (i = 0; i < AW_START_RETRIES; i++) { + /*if PA already power ,stop PA then start*/ + if (aw882xx->aw_pa->status) { + aw_dev_info(aw882xx->dev, "already start"); + return; + } + + ret = aw_dev_reg_update(aw882xx->aw_pa, aw882xx->phase_sync); + if (ret) { + aw_dev_err(aw882xx->dev, "fw update failed, cnt:%d", i); + continue; + } + + ret = aw_device_start(aw882xx->aw_pa); + if (ret) { + aw_dev_err(aw882xx->dev, "start failed, cnt:%d", i); + continue; + } else { + if (aw882xx->dc_flag) + queue_delayed_work(aw882xx->work_queue, + &aw882xx->dc_work, + msecs_to_jiffies(AW882XX_DC_DELAY_TIME)); + aw_dev_info(aw882xx->dev, "start success"); +#ifdef AW882XX_RUNIN_TEST + schedule_delayed_work(&aw882xx->adsp_status, msecs_to_jiffies(50)); +#endif + break; + } + } + } else { + aw_dev_info(aw882xx->dev, "dev acf load failed"); + } + +} + +static int aw882xx_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int ret = 0; + + aw_dev_info(aw882xx->dev, "mute state=%d", mute); + + if (stream != SNDRV_PCM_STREAM_PLAYBACK) { + aw_dev_info(aw882xx->dev, "capture"); + return 0; + } + + if (mute) { + aw882xx->pstream = false; + aw882xx->aw_pa->pre_prof_id = AW_DEFAULT_PRO_ID; + cancel_delayed_work_sync(&aw882xx->dc_work); + cancel_delayed_work_sync(&aw882xx->start_work); + mutex_lock(&aw882xx->lock); + aw_device_stop(aw882xx->aw_pa); + mutex_unlock(&aw882xx->lock); + } else { + if (aw882xx->fw_status == AW_DEV_FW_FAILED) { + aw_dev_info(aw882xx->dev, "fw_load failed ,can not start PA"); + return 0; + } + aw882xx->pstream = true; + mutex_lock(&aw882xx->lock); + /*aw882xx_start_pa(aw882xx);*/ + queue_delayed_work(aw882xx->work_queue, + &aw882xx->start_work, 0); +#ifdef AW_SPIN_ENABLE + if (aw882xx->index == 0) { + ret = aw_dev_set_spin(g_spin_value); + if (ret) + aw_dev_err(aw882xx->dev, "set spin error, ret=%d", ret); + } +#endif + mutex_unlock(&aw882xx->lock); + } + + return ret; +} + +static const struct snd_soc_dai_ops aw882xx_dai_ops = { + .startup = aw882xx_startup, + .set_fmt = aw882xx_set_fmt, + .set_sysclk = aw882xx_set_dai_sysclk, + .hw_params = aw882xx_hw_params, + .mute_stream = aw882xx_mute, + .shutdown = aw882xx_shutdown, +}; + +/***************************************************** + * + * snd_soc_codec_driver | snd_soc_component_driver| + * + *****************************************************/ +static int aw882xx_profile_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count, ret; + char *name = NULL; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + count = aw_dev_get_profile_count(aw882xx->aw_pa); + if (count <= 0) { + uinfo->value.enumerated.items = 0; + aw_dev_err(aw882xx->dev, "get count[%d] failed ", count); + return 0; + } + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + name = uinfo->value.enumerated.name; + count = uinfo->value.enumerated.item; + ret = aw_dev_get_profile_name(aw882xx->aw_pa, name, count); + if (ret) { + strlcpy(uinfo->value.enumerated.name, "null", strlen("null") + 1); + return 0; + } + + return 0; +} + +static int aw882xx_profile_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw_dev_get_profile_index(aw882xx->aw_pa); + aw_dev_dbg(codec->dev, "profile index [%d]", aw_dev_get_profile_index(aw882xx->aw_pa)); + return 0; + +} + +static int aw882xx_profile_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int ret; + int cur_index; + + if (aw882xx->dbg_en_prof == false) { + aw_dev_info(codec->dev, "profile close "); + return 0; + } + + /* check value valid */ + ret = aw_dev_check_profile_index(aw882xx->aw_pa, ucontrol->value.integer.value[0]); + if (ret) { + aw_dev_info(codec->dev, "unsupported index %d", + (int)ucontrol->value.integer.value[0]); + return 0; + } + + /*check cur_index == set value*/ + cur_index = aw_dev_get_profile_index(aw882xx->aw_pa); + if (cur_index == ucontrol->value.integer.value[0]) { + aw_dev_info(codec->dev, "index no change"); + return 0; + } + + mutex_lock(&aw882xx->lock); + aw_dev_set_profile_index(aw882xx->aw_pa, ucontrol->value.integer.value[0]); + /*pstream = 0 no pcm just set status*/ + if (aw882xx->pstream && aw882xx->allow_pw) + aw_dev_prof_update(aw882xx->aw_pa, aw882xx->phase_sync); + mutex_unlock(&aw882xx->lock); + aw_dev_info(codec->dev, "prof id %d", (int)ucontrol->value.integer.value[0]); + return 0; +} + +static int aw882xx_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + count = 2; + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + strlcpy(uinfo->value.enumerated.name, + aw882xx_switch[uinfo->value.enumerated.item], + strlen(aw882xx_switch[uinfo->value.enumerated.item]) + 1); + + return 0; +} + +static int aw882xx_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw882xx->allow_pw; + + return 0; + +} + +static int aw882xx_switch_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + if (aw882xx->pstream) { + if (ucontrol->value.integer.value[0] == 0) { + cancel_delayed_work_sync(&aw882xx->dc_work); + cancel_delayed_work_sync(&aw882xx->start_work); + mutex_lock(&aw882xx->lock); + aw_device_stop(aw882xx->aw_pa); + aw882xx->allow_pw = false; + mutex_unlock(&aw882xx->lock); + aw_dev_info(aw882xx->dev, "stop pa"); + } else { + cancel_delayed_work_sync(&aw882xx->start_work); + mutex_lock(&aw882xx->lock); + aw882xx->allow_pw = true; + if (aw882xx->fw_status == AW_DEV_FW_OK) + aw882xx_start_pa(aw882xx); + else + aw_dev_info(aw882xx->dev, "fw_load failed ,can not start PA"); + mutex_unlock(&aw882xx->lock); + } + } else { + mutex_lock(&aw882xx->lock); + if (ucontrol->value.integer.value[0]) + aw882xx->allow_pw = true; + else + aw882xx->allow_pw = false; + mutex_unlock(&aw882xx->lock); + } + + return 0; +} + +static int aw882xx_ramp_status_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + count = 2; + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + strlcpy(uinfo->value.enumerated.name, + aw882xx_ramp_status[uinfo->value.enumerated.item], + strlen(aw882xx_ramp_status[uinfo->value.enumerated.item]) + 1); + + return 0; +} + +static int aw882xx_ramp_status_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int *ramp_status = &aw882xx->aw882xx_ramp_status; + + aw_dev_dbg(aw882xx->dev, "%s: aw_dev_ramp_status_switch=%d\n", + __func__, *ramp_status); + + ucontrol->value.integer.value[0] = *ramp_status; + + return 0; +} + +static int aw882xx_ramp_status_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int *ramp_status = &aw882xx->aw882xx_ramp_status; + + aw_dev_dbg(aw882xx->dev, "%s: ucontrol->value.integer.value[0]=%ld\n", + __func__, ucontrol->value.integer.value[0]); + + *ramp_status = ucontrol->value.integer.value[0]; + + if (*ramp_status == AW882XX_RAMP_ON) + aw882xx->aw882xx_ramp_status = AW882XX_RAMP_ON; + else if (*ramp_status == AW882XX_RAMP_OFF) + aw882xx->aw882xx_ramp_status = AW882XX_RAMP_OFF; + + return 0; +} + + +int aw882xx_dev_gain_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = AW882XX_MOTO_MAX_GAIN; + return 0; +} + +/* aw882xx gain ctl */ +static int aw882xx_dev_gain_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw882xx->aw_pa; + unsigned int volume = 0; + aw_dev->ops.aw_get_volume(aw_dev, &volume); + + ucontrol->value.integer.value[0] = aw_dev->cur_gain; + + aw_dev_info(aw882xx->dev, "cur gain = %d, volume = %d", + aw_dev->cur_gain, volume); + + return 0; +} + +static int aw882xx_dev_gain_ctl_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw882xx->aw_pa; + struct aw_volume_desc *desc = &aw_dev->volume_desc; + + int value = 0; + int aw882xx_volume = 0; + int useful_range = 0; + + mutex_lock(&aw882xx->lock); + /* Note: The larger the volume value is, the smaller the actual volume */ + useful_range = desc->mute_volume - desc->init_volume; + + aw_dev_info(aw882xx->dev, "desc->init_volume = %d mute_volume = %d", + desc->init_volume, desc->mute_volume ); + + if (ucontrol->value.integer.value[0] > AW882XX_MOTO_MAX_GAIN) { + aw_dev_err(aw882xx->dev, "set val %ld overflow %d", + ucontrol->value.integer.value[0], AW882XX_MOTO_MAX_GAIN); + return 0; + } + + aw_dev->cur_gain = ucontrol->value.integer.value[0]; + + /* hal gain map to aw882xx valume value */ + value = ucontrol->value.integer.value[0]; + aw882xx_volume = ((AW882XX_MOTO_MAX_GAIN - value) * useful_range) / AW882XX_MOTO_MAX_GAIN + + desc->init_volume; + + + aw_dev->ops.aw_set_volume(aw_dev, aw882xx_volume); + mutex_unlock(&aw882xx->lock); + aw_dev_info(aw882xx->dev,"set value = %d, set aw882xx volume = %d", value, aw882xx_volume); + + return 0; +} + +static int aw882xx_dynamic_create_controls(struct aw882xx *aw882xx) +{ + struct snd_kcontrol_new *aw882xx_dev_control = NULL; + char *kctl_name; + + aw882xx_dev_control = devm_kzalloc(aw882xx->codec->dev, sizeof(struct snd_kcontrol_new) * 4, GFP_KERNEL); + if (aw882xx_dev_control == NULL) { + aw_dev_err(aw882xx->codec->dev, "kcontrol malloc failed!"); + return -ENOMEM; + } + + kctl_name = devm_kzalloc(aw882xx->codec->dev, AW_NAME_BUF_MAX, GFP_KERNEL); + if (!kctl_name) + return -ENOMEM; + + snprintf(kctl_name, AW_NAME_BUF_MAX, "aw_dev_%d_prof", aw882xx->index); + + aw882xx_dev_control[0].name = kctl_name; + aw882xx_dev_control[0].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw882xx_dev_control[0].info = aw882xx_profile_info; + aw882xx_dev_control[0].get = aw882xx_profile_get; + aw882xx_dev_control[0].put = aw882xx_profile_set; + + kctl_name = devm_kzalloc(aw882xx->codec->dev, AW_NAME_BUF_MAX, GFP_KERNEL); + if (!kctl_name) + return -ENOMEM; + + snprintf(kctl_name, AW_NAME_BUF_MAX, "aw_dev_%d_switch", aw882xx->index); + + aw882xx_dev_control[1].name = kctl_name; + aw882xx_dev_control[1].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw882xx_dev_control[1].info = aw882xx_switch_info; + aw882xx_dev_control[1].get = aw882xx_switch_get; + aw882xx_dev_control[1].put = aw882xx_switch_set; + + //ramp control + kctl_name = devm_kzalloc(aw882xx->codec->dev, AW_NAME_BUF_MAX, GFP_KERNEL); + if (!kctl_name) + return -ENOMEM; + + snprintf(kctl_name, AW_NAME_BUF_MAX, "aw_dev_%d_ramp_status_switch", aw882xx->index); + + aw882xx_dev_control[2].name = kctl_name; + aw882xx_dev_control[2].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw882xx_dev_control[2].info = aw882xx_ramp_status_info; + aw882xx_dev_control[2].get = aw882xx_ramp_status_get; + aw882xx_dev_control[2].put = aw882xx_ramp_status_set; + //end ramp control + + + kctl_name = devm_kzalloc(aw882xx->codec->dev, AW_NAME_BUF_MAX, GFP_KERNEL); + if (!kctl_name) + return -ENOMEM; + + snprintf(kctl_name, AW_NAME_BUF_MAX, "aw_dev_%d_gain_ctl", aw882xx->index); + + aw882xx_dev_control[3].name = kctl_name; + aw882xx_dev_control[3].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + aw882xx_dev_control[3].info = aw882xx_dev_gain_ctl_info; + aw882xx_dev_control[3].get = aw882xx_dev_gain_ctl_get; + aw882xx_dev_control[3].put = aw882xx_dev_gain_ctl_set; + + aw_componet_codec_ops.add_codec_controls(aw882xx->codec, + aw882xx_dev_control, 4); + + return 0; +} + +static void aw882xx_request_firmware(struct work_struct *work) +{ + struct aw882xx *aw882xx = + container_of(work, struct aw882xx, fw_work.work); + const struct firmware *cont = NULL; + struct aw_container *aw_cfg = NULL; +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS + const struct firmware *skt_cont = NULL; + struct aw_container *aw_skt_cfg = NULL; +#endif + int ret = -1; + + aw882xx->fw_status = AW_DEV_FW_FAILED; + ret = request_firmware(&cont, aw882xx->aw_pa->acf_name, aw882xx->dev); + if ((ret) || (!cont)) { + aw_dev_info(aw882xx->dev, "load [%s] failed!", aw882xx->aw_pa->acf_name); + if (aw882xx->fw_retry_cnt == AW_READ_CHIPID_RETRIES) { + aw882xx->fw_retry_cnt = 0; + } else { + aw882xx->fw_retry_cnt++; + /* sleep 1s */ + msleep(1000); + aw_dev_info(aw882xx->dev, "load [%s] try [%d]!", + aw882xx->aw_pa->acf_name, aw882xx->fw_retry_cnt); + aw882xx_request_firmware(work); + } + return; + } + + aw_dev_info(aw882xx->dev, "load [%s] , file size: [%zu]", + aw882xx->aw_pa->acf_name, cont ? cont->size : 0); + + mutex_lock(&g_aw882xx_lock); + if (g_awinic_cfg == NULL) { + aw_cfg = vzalloc(cont->size + sizeof(int)); + if (aw_cfg == NULL) { + release_firmware(cont); + aw_dev_err(aw882xx->dev, "malloc failed"); + mutex_unlock(&g_aw882xx_lock); + return; + } + aw_cfg->len = cont->size; + memcpy(aw_cfg->data, cont->data, cont->size); + release_firmware(cont); + ret = aw_dev_load_acf_check(aw_cfg); + if (ret) { + aw_dev_err(aw882xx->dev, "Load [%s] failed ....!", aw882xx->aw_pa->acf_name); + vfree(aw_cfg); + aw_cfg = NULL; + mutex_unlock(&g_aw882xx_lock); + return; + } + g_awinic_cfg = aw_cfg; + } else { + aw_cfg = g_awinic_cfg; + release_firmware(cont); + aw_dev_info(aw882xx->dev, "[%s] already loaded...", aw882xx->aw_pa->acf_name); + } + mutex_unlock(&g_aw882xx_lock); + +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS + /*load skt bin*/ + if (aw882xx->skt_prof_mode == AW_PARAMS_DATA_MODE) { + ret = request_firmware(&skt_cont, AW_ALGO_SKT_BIN, aw882xx->dev); + if ((ret) || (!skt_cont)) { + aw_dev_info(aw882xx->dev, "load [%s] failed!", AW_ALGO_SKT_BIN); + return; + } + + aw_dev_info(aw882xx->dev, "load [%s] , file size: [%zu]", + AW_ALGO_SKT_BIN, skt_cont ? skt_cont->size : 0); + + if (g_awinic_skt_cfg == NULL) { + aw_skt_cfg = vzalloc(skt_cont->size + sizeof(int)); + if (aw_skt_cfg == NULL) { + release_firmware(skt_cont); + aw_dev_err(aw882xx->dev, "malloc failed"); + return; + } + aw_skt_cfg->len = skt_cont->size; + memcpy(aw_skt_cfg->data, skt_cont->data, skt_cont->size); + release_firmware(skt_cont); + + ret = aw_dev_load_acf_check(aw_skt_cfg); + if (ret) { + aw_dev_err(aw882xx->dev, "Load [%s] failed ....!", AW_ALGO_SKT_BIN); + vfree(aw_skt_cfg); + aw_skt_cfg = NULL; + return; + } + g_awinic_skt_cfg = aw_skt_cfg; + /*set init algo prof*/ + aw882xx->aw_pa->pre_prof_id = AW_DEFAULT_PRO_ID; + + } else { + aw_skt_cfg = g_awinic_skt_cfg; + release_firmware(skt_cont); + aw_dev_info(aw882xx->dev, "[%s] already loaded...", AW_ALGO_SKT_BIN); + } + + /*parse skt bin*/ + if ((aw882xx->aw_pa->channel == AW_DEV_CH_PRI_L) || (aw882xx->aw_pa->channel == AW_DEV_CH_SEC_L)) { + ret = aw_dev_parse_skt_bin(aw882xx->aw_pa, aw_skt_cfg); + if (ret < 0) { + aw_dev_info(aw882xx->dev, "parse skt bin failed"); + return; + } + } + } +#endif + + mutex_lock(&aw882xx->lock); + /*aw device init*/ + ret = aw_device_init(aw882xx->aw_pa, aw_cfg); + if (ret < 0) { + aw_dev_info(aw882xx->dev, "dev init failed"); + mutex_unlock(&aw882xx->lock); + return; + } + + /*create kcontrol by profile*/ + aw882xx_dynamic_create_controls(aw882xx); + + aw882xx->fw_status = AW_DEV_FW_OK; + aw882xx->fw_retry_cnt = 0; + + mutex_unlock(&aw882xx->lock); +} + +static void aw882xx_startup_work(struct work_struct *work) +{ + struct aw882xx *aw882xx = container_of(work, struct aw882xx, start_work.work); + + aw_dev_info(aw882xx->dev, "enter"); + + mutex_lock(&aw882xx->lock); + aw882xx_start_pa(aw882xx); + mutex_unlock(&aw882xx->lock); +} + +static void aw882xx_dc_prot_work(struct work_struct *work) +{ + struct aw882xx *aw882xx = container_of(work, struct aw882xx, dc_work.work); + int dc_status = -1; + int dev_status = aw_dev_status(aw882xx->aw_pa); + + if (aw882xx->dc_flag) { + if (dev_status) { + dc_status = aw_dev_dc_status(aw882xx->aw_pa); + if (dc_status > 0) { + cancel_delayed_work_sync(&aw882xx->start_work); + mutex_lock(&aw882xx->lock); + aw_device_stop(aw882xx->aw_pa); + mutex_unlock(&aw882xx->lock); + } else { + queue_delayed_work(aw882xx->work_queue, + &aw882xx->dc_work, + msecs_to_jiffies(AW882XX_DC_DELAY_TIME)); + } + } + } +} + +static void aw882xx_irq_restart(struct aw882xx *aw882xx) +{ + int ret; + + aw_dev_dbg(aw882xx->dev, "enter"); + + mutex_lock(&aw882xx->lock); + + /*stop pa*/ + aw_device_stop(aw882xx->aw_pa); + + /*hw reset*/ + aw882xx_hw_reset(aw882xx); + + /*aw reinit*/ + if (aw882xx->fw_status == AW_DEV_FW_OK) { + ret = aw_device_irq_reinit(aw882xx->aw_pa); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "irq reinit failed"); + goto failed_exit; + } + + if (aw882xx->allow_pw && aw882xx->pstream) { + ret = aw_device_start(aw882xx->aw_pa); + if (ret) { + aw_dev_err(aw882xx->dev, "start failed"); + goto failed_exit; + } + } else { + aw_dev_info(aw882xx->dev, "allow_pw [%d] ,pstream[%d], not start", + aw882xx->allow_pw, aw882xx->pstream); + } + } else { + aw_dev_err(aw882xx->dev, "fw not load ,cannot init device"); + } +failed_exit: + mutex_unlock(&aw882xx->lock); +} + +static void aw882xx_interrupt_work(struct work_struct *work) +{ + struct aw882xx *aw882xx = container_of(work, struct aw882xx, interrupt_work.work); + int16_t reg_value; + int ret; + + aw_dev_info(aw882xx->dev, "enter"); + + /*read reg value*/ + ret = aw_dev_get_int_status(aw882xx->aw_pa, ®_value); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "get init_reg value failed"); + } else { + aw_dev_info(aw882xx->dev, "int value 0x%x", reg_value); + if (aw882xx->aw_pa->ops.aw_get_irq_type) { + ret = aw882xx->aw_pa->ops.aw_get_irq_type(aw882xx->aw_pa, reg_value); + if (ret != INT_TYPE_NONE) { + aw882xx_irq_restart(aw882xx); + return; + } + } + } + + /*clear init reg*/ + aw_dev_clear_int_status(aw882xx->aw_pa); + + /*unmask interrupt*/ + aw_dev_set_intmask(aw882xx->aw_pa, true); +} + +static int aw882xx_set_rx_en(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + struct aw_device *aw_dev = NULL; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + aw_dev = aw882xx->aw_pa; + ctrl_value = ucontrol->value.integer.value[0]; + + ret = aw_dev_set_afe_module_en(AW_RX_MODULE, ctrl_value); + if (ret) + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + + g_algo_rx_en = ctrl_value; + aw_dev_info(aw882xx->dev, "set value %d", ctrl_value); + +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS + if (aw882xx->skt_prof_mode == AW_PARAMS_PATH_MODE) { + if (ctrl_value) { + ret = aw_dev_set_algo_params_path(aw_dev); + if (ret < 0) + aw_dev_err(aw882xx->dev, "set algo params path failed, ret=%d", ret); + } + } else { + if (aw882xx->aw_pa->pre_prof_id != AW_DEFAULT_PRO_ID) { + aw882xx->cur_algo_prof_id = aw882xx->aw_pa->pre_prof_id; + aw882xx->aw_pa->pre_prof_id = AW_DEFAULT_PRO_ID; + ret = aw_dev_skt_prof_mode(aw_dev, aw882xx->cur_algo_prof_id); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "set algo prof failed"); + return -EINVAL; + } + } + } +#endif + + return 0; +} + +static int aw882xx_get_rx_en(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + /*ret = aw_dev_get_afe_module_en(AW_RX_MODULE, &ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + } + ucontrol->value.integer.value[0] = ctrl_value;*/ + + if (aw882xx->pstream) { + ret = aw_dev_get_afe_module_en(AW_RX_MODULE, &ctrl_value); + if (ret) + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + + ucontrol->value.integer.value[0] = ctrl_value; + } else { + ucontrol->value.integer.value[0] = false;//g_algo_rx_en; + aw_dev_info(aw882xx->dev, "no stream, rx disable"); + } + + aw_dev_dbg(aw882xx->dev, "aw882xx_rx_enable %d", + ctrl_value); + return 0; +} + +static int aw882xx_set_tx_en(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + ctrl_value = ucontrol->value.integer.value[0]; + + ret = aw_dev_set_afe_module_en(AW_TX_MODULE, ctrl_value); + if (ret) + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + + g_algo_tx_en = ctrl_value; + aw_dev_info(aw882xx->dev, "set value %d", ctrl_value); + return 0; +} + +static int aw882xx_get_tx_en(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + /*ret = aw_dev_get_afe_module_en(AW_TX_MODULE, &ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + } + ucontrol->value.integer.value[0] = ctrl_value;*/ + + if (aw882xx->pstream) { + ret = aw_dev_get_afe_module_en(AW_TX_MODULE, &ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + ctrl_value = 0; + } + ucontrol->value.integer.value[0] = ctrl_value; + } else { + ucontrol->value.integer.value[0] = false;//g_algo_tx_en; + aw_dev_info(aw882xx->dev, "no stream, tx disable"); + } + + aw_dev_dbg(aw882xx->dev, "aw882xx_tx_enable %d", + ctrl_value); + return 0; +} + +static int aw882xx_set_copp_dis(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + ctrl_value = ucontrol->value.integer.value[0]; + + ret = aw_dev_set_copp_module_en(!ctrl_value); + if (ret) + aw_dev_err(aw882xx->dev, "dsp_msg error, ret=%d", ret); + + g_algo_copp_en = !ctrl_value; + aw_dev_info(aw882xx->dev, "set value %d", g_algo_copp_en); + + return 0; +} + +static int aw882xx_get_copp_dis(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = !g_algo_copp_en; + + aw_dev_info(aw882xx->dev, "copp_dis: %d", !g_algo_copp_en); + return 0; +} + +void aw882xx_kcontorl_set(struct aw882xx *aw882xx) +{ + int ret; + + ret = aw_dev_set_afe_module_en(AW_RX_MODULE, g_algo_rx_en); + if (ret) + aw_dev_err(aw882xx->dev, "afe set, ret=%d", ret); + + ret = aw_dev_set_copp_module_en(g_algo_copp_en); + if (ret) + aw_dev_err(aw882xx->dev, "copp set error, ret=%d", ret); +} + +#ifdef AW_SPIN_ENABLE +static int aw882xx_set_spin(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + aw_dev = aw882xx->aw_pa; + + ctrl_value = ucontrol->value.integer.value[0]; + if (aw882xx->pstream) { + ret = aw_dev_set_spin(ctrl_value); + if (ret) + aw_dev_err(aw882xx->dev, "set spin error, ret=%d", ret); + } else { + aw_dev_info(aw882xx->dev, "stream no start only record"); + } + + g_spin_value = ctrl_value; + return 0; +} + +static int aw882xx_get_spin(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int ctrl_value; + int ret = -EINVAL; + + aw_dev = aw882xx->aw_pa; + + if (aw882xx->pstream) { + ret = aw_dev_get_spin(&ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "get spin failed!, ret = %d", ret); + ctrl_value = 0; + } + ucontrol->value.integer.value[0] = ctrl_value; + } else { + ucontrol->value.integer.value[0] = g_spin_value; + aw_dev_info(aw882xx->dev, "no stream, use record value"); + + } + aw_dev_dbg(aw882xx->dev, "spin value is %ld", ucontrol->value.integer.value[0]); + return 0; +} + +static int aw882xx_get_spin_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + struct aw_spin_param spin_param; + int ctrl_value; + int ret = -EINVAL; + + aw_dev = aw882xx->aw_pa; + + if (aw882xx->pstream) { + ret = aw_dev_get_spin_param(aw_dev, &ctrl_value, &(spin_param.relase_time)); + if (ret) { + aw_dev_err(aw882xx->dev, "get spin status failed!, ret = %d", ret); + ctrl_value = 0; + } + ucontrol->value.integer.value[0] = ctrl_value; + } else { + aw_dev_info(aw882xx->dev, "no stream, use record value"); + + } + aw_dev_dbg(aw882xx->dev, "spin value is %ld", ucontrol->value.integer.value[0]); + return 0; +} + +static int aw882xx_set_spin_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + //struct aw_spin_param spin_param; + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + aw_dev = aw882xx->aw_pa; + + ctrl_value = ucontrol->value.integer.value[0]; + + if (g_algo_rx_en == true) { + ret = aw_dev_set_spin_param(aw_dev, ctrl_value, g_spin_relase_time); + if (ret) + aw_dev_err(aw882xx->dev, "set spin status error, ret=%d", ret); + + g_spin_en = ctrl_value; + } else { + aw_dev_info(aw882xx->dev, "stream no start only record"); + } + + return 0; +} + +static int aw882xx_get_spin_relase_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + struct aw_spin_param spin_param; + int ctrl_value; + int ret = -EINVAL; + + aw_dev = aw882xx->aw_pa; + + if (aw882xx->pstream) { + ret = aw_dev_get_spin_param(aw_dev, &(spin_param.enable), &ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "get spin release time failed!, ret = %d", ret); + ctrl_value = 0; + } + ucontrol->value.integer.value[0] = ctrl_value; + g_spin_relase_time = ctrl_value; + } else { + aw_dev_info(aw882xx->dev, "no stream, use record value"); + ucontrol->value.integer.value[0] = g_spin_relase_time; + } + aw_dev_dbg(aw882xx->dev, "spin value is %ld", ucontrol->value.integer.value[0]); + return 0; +} + +static int aw882xx_set_spin_relase_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + //struct aw_spin_param spin_param; + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + aw_dev = aw882xx->aw_pa; + + ctrl_value = ucontrol->value.integer.value[0]; + //if ((aw882xx->pstream) && (g_spin_en == 1) && (g_algo_rx_en == true)) { + if ( (g_spin_en == 1) && (g_algo_rx_en == true)) { + ret = aw_dev_set_spin_param(aw_dev, g_spin_en , ctrl_value); + if (ret) + aw_dev_err(aw882xx->dev, "set spin release time error, ret=%d", ret); + } else { + aw_dev_info(aw882xx->dev, "set release time failed, g_spin_en=%d, g_algo_rx_en=%d\n", + g_spin_en, g_algo_rx_en); + } + g_spin_relase_time = ucontrol->value.integer.value[0]; + return 0; +} + +#endif + +static int aw882xx_get_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int time; + + aw_dev_get_fade_time(&time, true); + ucontrol->value.integer.value[0] = time; + + aw_pr_dbg("step time %ld", ucontrol->value.integer.value[0]); + + return 0; +} + +static int aw882xx_set_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + if (ucontrol->value.integer.value[0] > mc->max) { + aw_pr_dbg("set val %ld overflow %d", + ucontrol->value.integer.value[0], mc->max); + return 0; + } + aw_dev_set_fade_time(ucontrol->value.integer.value[0], true); + + aw_pr_dbg("step time %ld", ucontrol->value.integer.value[0]); + return 0; +} + +static int aw882xx_get_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int time; + + aw_dev_get_fade_time(&time, false); + ucontrol->value.integer.value[0] = time; + + aw_pr_dbg("step time %ld", ucontrol->value.integer.value[0]); + + return 0; +} + +static int aw882xx_set_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + if (ucontrol->value.integer.value[0] > mc->max) { + aw_pr_dbg("set val %ld overflow %d", + ucontrol->value.integer.value[0], mc->max); + return 0; + } + + aw_dev_set_fade_time(ucontrol->value.integer.value[0], false); + + aw_pr_dbg("step time %ld", ucontrol->value.integer.value[0]); + + return 0; +} + +#ifdef AW882XX_RUNIN_TEST +static int aw882xx_runin_test_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_pr_info("aw882xx_runin test get %d\n", g_runin_test); + + ucontrol->value.integer.value[0] = g_runin_test; + + return 0; +} + +static int aw882xx_runin_test_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int value = ucontrol->value.integer.value[0]; + + aw_pr_info("aw882xx_runin_test set %d \n", value); + + if (value > 1 ) { + value = 0; + } + + g_runin_test = value; + + return 0; +} + +static void aw882xx_set_adsp_module_status(struct work_struct *work) +{ + int ret = 0; + uint32_t ctrl_value = 0; + + /*no test reg set default value*/ + if (g_runin_test == 0) { + return; + } + + /*set afe rx module*/ + ret = aw_dev_set_afe_module_en(AW_RX_MODULE, ctrl_value); + if (ret) { + aw_pr_err("disable afe rx module %d falied , ret=%d\n", ctrl_value, ret); + } + + /*set skt module*/ + ret = aw_dev_set_copp_module_en(ctrl_value); + if (ret) { + aw_pr_err("disable skt failed !\n"); + } + + aw_pr_info("disable skt and afe module success\n"); +} +#endif + +static int aw882xx_monitor_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw882xx->aw_pa; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + + pr_debug("%s: aw882xx_monitor_get %d", + __func__, monitor_cfg->monitor_switch); + + ucontrol->value.integer.value[0] = monitor_cfg->monitor_switch; + + return 0; +} + +static int aw882xx_monitor_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw882xx->aw_pa; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + int enable = ucontrol->value.integer.value[0]; + + pr_debug("%s: aw882xx_monitor_set %d \n", __func__, enable); + + mutex_lock(&aw882xx->lock); + if (enable == monitor_cfg->monitor_switch) { + mutex_unlock(&aw882xx->lock); + return 1; + } + + monitor_cfg->monitor_switch = enable; + if (enable) + aw_monitor_start(&aw_dev->monitor_desc); + mutex_unlock(&aw882xx->lock); + + return 0; +} + +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS +static int aw882xx_set_prof_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int ctrl_value; + int ret = -1; + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + aw_dev = aw882xx->aw_pa; + + ctrl_value = ucontrol->value.integer.value[0]; + + if (aw882xx->skt_prof_mode == AW_PARAMS_PATH_MODE) { + aw_dev_set_algo_prof(aw_dev, ctrl_value); + } else { + ret = aw_dev_skt_prof_mode(aw_dev, ctrl_value); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "set_prof failed!, ret = %d", ret); + } + aw882xx->cur_algo_prof_id = ctrl_value; + } + + return 0; +} +static int aw882xx_get_prof_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct aw_device *aw_dev; + aw_snd_soc_codec_t *codec = + aw_componet_codec_ops.kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(codec); + int ctrl_value; + int ret = -EINVAL; + + aw_dev = aw882xx->aw_pa; + + if (aw882xx->pstream) { + if (aw882xx->skt_prof_mode == AW_PARAMS_PATH_MODE) { + ret = aw_dev_get_algo_prof(aw_dev, &ctrl_value); + if (ret) { + aw_dev_err(aw882xx->dev, "get algo prof id failed!, ret = %d", ret); + ctrl_value = 0; + } + } else { + ctrl_value = aw882xx->cur_algo_prof_id; + } + ucontrol->value.integer.value[0] = ctrl_value; + } else { + aw_dev_info(aw882xx->dev, "no stream, get algo prof failed"); + } + + aw_dev_dbg(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + return 0; +} + +static int aw882xx_get_algo_prof_id_by_scene_st(struct aw882xx *aw882xx) +{ + int index = -1; + int i = 0; + + /* Get the highest priority array subscript from all active scene */ + for (i = 0; i < AW_ALGO_PROFILE_ID_MAX; i++) { + if (aw882xx_scene_state[i].is_active) { + if (index < 0) { + index = i; + } else { + if(aw882xx_scene_state[i].priority < + aw882xx_scene_state[index].priority) { + index = i; + } + } + } + } + + if (index >= 0) { + return aw882xx_scene_state[index].skt_profile_id; + } + + return 0; +} + +static void aw882xx_update_algo_scene_st(struct aw882xx *aw882xx, + int scene_id, bool is_active) { + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[scene_id]; + + if (is_active) { + p_aw882xx_scene_st->active_cnt++; + p_aw882xx_scene_st->is_active = 1; + } else { + p_aw882xx_scene_st->active_cnt--; + if (p_aw882xx_scene_st->active_cnt < 0) { + p_aw882xx_scene_st->active_cnt = 0; + } + if (p_aw882xx_scene_st->active_cnt == 0) { + p_aw882xx_scene_st->is_active = 0; + } + } +} + + +static int aw882xx_update_algo_profile(struct aw882xx *aw882xx) +{ + int ret = -1; + int new_skt_prof_id = 0; + int cur_skt_prof_id = aw882xx->cur_algo_prof_id; + struct aw_device *aw_dev = NULL; + + aw_dev_info(aw882xx->dev, "enter"); + + new_skt_prof_id = aw882xx_get_algo_prof_id_by_scene_st(aw882xx); + if (new_skt_prof_id < AW_ALGO_PROFILE_ID_1) { + /* no active scene */ + aw_dev_info(aw882xx->dev, "all scene disactive,do nothing"); + return 0; + } + + aw_dev_info(aw882xx->dev, "algo scene switch. [new] %d,[old] %d", + new_skt_prof_id, cur_skt_prof_id); + aw882xx->cur_algo_prof_id = new_skt_prof_id; + aw_dev = aw882xx->aw_pa; + + /* set new scene pramas to skt */ + if (aw882xx->skt_prof_mode == AW_PARAMS_PATH_MODE) { + ret = aw_dev_set_algo_prof(aw_dev, aw882xx->cur_algo_prof_id); + } else { + ret = aw_dev_skt_prof_mode(aw_dev, aw882xx->cur_algo_prof_id); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "set algo prof failed"); + return -1; + } + } + return 0; + +} + +static int aw882xx_algo_dump_scene_st(struct aw882xx *aw882xx) +{ + int i = 0; + + aw_dev_info(aw882xx->dev, "aw882xx cur algo prof id =%d", + aw882xx->cur_algo_prof_id); + for (i = 0; i < AW_ALGO_PROFILE_ID_MAX; i++) { + aw_dev_info(aw882xx->dev, "scene[%s] %s active_cnt %d", + aw882xx_scene_state[i].name, + aw882xx_scene_state[i].is_active? "[active]" : "[disactive]", + aw882xx_scene_state[i].active_cnt); + } + return 0; +} + +static int aw882xx_get_algo_cali_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_CALIBRATION_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_set_algo_cali_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_CALIBRATION_ID, is_active); + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_get_algo_bypass_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_BYPASS_ID]; + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + return 0; +} + +static int aw882xx_set_algo_bypass_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_BYPASS_ID, is_active); + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_get_algo_handset_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_HANDSET_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + + return 0; +} + +static int aw882xx_set_algo_handset_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_HANDSET_ID, is_active); + + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_get_algo_hac_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_HAC_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + + return 0; +} + +static int aw882xx_set_algo_hac_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_HAC_ID, is_active); + + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + + +static int aw882xx_get_algo_voice_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_VOICE_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + + return 0; +} + +static int aw882xx_set_algo_voice_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_VOICE_ID, is_active); + + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_get_algo_voip_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_VOIP_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + + return 0; +} + +static int aw882xx_set_algo_voip_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_VOIP_ID, is_active); + + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_get_algo_music_deepbuffer_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_DEEPBUFFER_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + + return 0; +} + +static int aw882xx_set_algo_music_deepbuffer_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_DEEPBUFFER_ID, is_active); + + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} + +static int aw882xx_get_algo_music_fasttrack_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + + struct aw882xx_scene_info *p_aw882xx_scene_st = + &aw882xx_scene_state[AW882XX_SCENE_FASTTRACK_ID]; + + aw_dev_info(aw882xx->dev, "%s: %s active_cnt %d", + __func__, + p_aw882xx_scene_st->is_active? "[active]" : "[disactive]", + p_aw882xx_scene_st->active_cnt); + ucontrol->value.integer.value[0] = p_aw882xx_scene_st->is_active? 1 : 0; + + return 0; +} + +static int aw882xx_set_algo_music_fasttrack_switch(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw882xx *aw882xx = snd_soc_component_get_drvdata(codec); + bool is_active = false; + + aw_dev_info(aw882xx->dev, "ucontrol->value.integer.value[0]=%ld", + ucontrol->value.integer.value[0]); + + is_active = ucontrol->value.integer.value[0]? true : false; + aw882xx_update_algo_scene_st(aw882xx, AW882XX_SCENE_FASTTRACK_ID, is_active); + + aw882xx_update_algo_profile(aw882xx); + + aw882xx_algo_dump_scene_st(aw882xx); + + return 0; +} +#endif + +static const struct soc_enum aw882xx_snd_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(aw882xx_switch), aw882xx_switch), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(switch_status), switch_status), +#ifdef AW_SPIN_ENABLE + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(aw882xx_spin), aw882xx_spin), +#endif +}; + +static struct snd_kcontrol_new aw882xx_controls[] = { + SOC_ENUM_EXT("aw882xx_rx_switch", aw882xx_snd_enum[0], + aw882xx_get_rx_en, aw882xx_set_rx_en), + SOC_ENUM_EXT("aw882xx_tx_switch", aw882xx_snd_enum[0], + aw882xx_get_tx_en, aw882xx_set_tx_en), + SOC_ENUM_EXT("aw882xx_skt_disable", aw882xx_snd_enum[1], + aw882xx_get_copp_dis, aw882xx_set_copp_dis), +#ifdef AW_SPIN_ENABLE + SOC_ENUM_EXT("aw882xx_spin_switch", aw882xx_snd_enum[2], + aw882xx_get_spin, aw882xx_set_spin), + SOC_ENUM_EXT("aw882xx_spin_status", aw882xx_snd_enum[1], + aw882xx_get_spin_status, aw882xx_set_spin_status), + SOC_SINGLE_EXT("aw882xx_spin_relase_time", 0, 0, 1000000, 0, + aw882xx_get_spin_relase_time, aw882xx_set_spin_relase_time), +#endif +#ifdef AW882XX_RUNIN_TEST + SOC_ENUM_EXT("aw882xx_runin_test", aw882xx_snd_enum[1], + aw882xx_runin_test_get, aw882xx_runin_test_set), +#endif + SOC_ENUM_EXT("aw882xx_monitor_switch", aw882xx_snd_enum[0], + aw882xx_monitor_get, aw882xx_monitor_set), + SOC_SINGLE_EXT("aw882xx_fadein_us", 0, 0, 1000000, 0, + aw882xx_get_fade_in_time, aw882xx_set_fade_in_time), + SOC_SINGLE_EXT("aw882xx_fadeout_us", 0, 0, 1000000, 0, + aw882xx_get_fade_out_time, aw882xx_set_fade_out_time), +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS + SOC_SINGLE_EXT("aw882xx_algo_prof_switch", 0, 0, 100, 0, + aw882xx_get_prof_id, aw882xx_set_prof_id), + + /* for skt scene param switch */ + SOC_ENUM_EXT("aw882xx_algo_cali_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_cali_switch, aw882xx_set_algo_cali_switch), + SOC_ENUM_EXT("aw882xx_algo_bypass_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_bypass_switch, aw882xx_set_algo_bypass_switch), + SOC_ENUM_EXT("aw882xx_algo_handset_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_handset_switch, aw882xx_set_algo_handset_switch), + SOC_ENUM_EXT("aw882xx_algo_voice_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_voice_switch, aw882xx_set_algo_voice_switch), + SOC_ENUM_EXT("aw882xx_algo_voip_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_voip_switch, aw882xx_set_algo_voip_switch), + SOC_ENUM_EXT("aw882xx_algo_music_deepbuffer_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_music_deepbuffer_switch, aw882xx_set_algo_music_deepbuffer_switch), + SOC_ENUM_EXT("aw882xx_algo_music_fasttrack_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_music_fasttrack_switch, aw882xx_set_algo_music_fasttrack_switch), + SOC_ENUM_EXT("aw882xx_algo_hac_switch", aw882xx_snd_enum[1], + aw882xx_get_algo_hac_switch, aw882xx_set_algo_hac_switch), +#endif +}; + +static void aw882xx_add_codec_controls(struct aw882xx *aw882xx) +{ + aw_dev_info(aw882xx->dev, "enter"); + + aw_componet_codec_ops.add_codec_controls(aw882xx->codec, + &aw882xx_controls[0], ARRAY_SIZE(aw882xx_controls)); +} + +#ifdef CONFIG_AW882XX_STEREO_SMARTPA +static int aw882xx_name_append_suffix(struct aw882xx *aw882xx, const char **name) +{ + char buf[50]; + int i2cbus = aw882xx->i2c->adapter->nr; + int i2caddr = aw882xx->i2c->addr; + + snprintf(buf, 50, "%s.%x-00%x", *name, i2cbus, i2caddr); + (*name) = aw882xx_devm_kstrdup(aw882xx->dev, buf); + if (!(*name)) + return -ENOMEM; + + aw_dev_info(aw882xx->dev, "name is %s", (*name)); + return 0; +} +#endif + +#ifdef AW_MTK_PLATFORM_WITH_DSP + +static const struct snd_soc_dapm_widget aw882xx_dapm_widgets[] = { + /* playback */ + SND_SOC_DAPM_AIF_IN("AIF_RX", "Speaker_Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("audio_out"), + /* capture */ + SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_INPUT("iv_in"), +}; + +static const struct snd_soc_dapm_route aw882xx_audio_map[] = { + {"audio_out", NULL, "AIF_RX"}, + {"AIF_TX", NULL, "iv_in"}, +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0) +static struct snd_soc_dapm_context *snd_soc_codec_get_dapm(struct snd_soc_codec *codec) +{ + return &codec->dapm; +} +#endif +#endif + +static void aw882xx_add_widgets(struct aw882xx *aw882xx) +{ +#ifdef AW_MTK_PLATFORM_WITH_DSP + int i = 0; + int ret; + struct snd_soc_dapm_widget *aw_widgets = NULL; + struct snd_soc_dapm_route *aw_route = NULL; +#ifdef AW_KERNEL_VER_OVER_4_19_1 + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(aw882xx->codec); +#else + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(aw882xx->codec); +#endif + + + /*add widgets*/ + aw_widgets = devm_kzalloc(aw882xx->dev, + sizeof(struct snd_soc_dapm_widget) * ARRAY_SIZE(aw882xx_dapm_widgets), + GFP_KERNEL); + if (!aw_widgets) { + aw_dev_err(aw882xx->dev, "alloc widget memory failed!"); + return; + } + memcpy(aw_widgets, aw882xx_dapm_widgets, + sizeof(struct snd_soc_dapm_widget) * ARRAY_SIZE(aw882xx_dapm_widgets)); + + for (i = 0; i < ARRAY_SIZE(aw882xx_dapm_widgets); i++) { + if (aw_widgets[i].name) { + ret = aw882xx_name_append_suffix(aw882xx, &aw_widgets[i].name); + if (ret) { + aw_dev_err(aw882xx->dev, "append widget name suffix failed!"); + return; + } + } + + if (aw_widgets[i].sname) { + ret = aw882xx_name_append_suffix(aw882xx, &aw_widgets[i].sname); + if (ret) { + aw_dev_err(aw882xx->dev, "append widget sname suffix failed!"); + return; + } + } + } + + snd_soc_dapm_new_controls(dapm, aw_widgets, ARRAY_SIZE(aw882xx_dapm_widgets)); + + /*add route*/ + aw_route = devm_kzalloc(aw882xx->dev, + sizeof(struct snd_soc_dapm_route) * ARRAY_SIZE(aw882xx_audio_map), + GFP_KERNEL); + if (!aw_route) { + aw_dev_err(aw882xx->dev, "alloc route memory failed!"); + return; + } + memcpy(aw_route, aw882xx_audio_map, + sizeof(struct snd_soc_dapm_route) * ARRAY_SIZE(aw882xx_audio_map)); + + for (i = 0; i < ARRAY_SIZE(aw882xx_audio_map); i++) { + if (aw_route[i].sink) { + ret = aw882xx_name_append_suffix(aw882xx, &aw_route[i].sink); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "append sink name suffix failed!"); + return; + } + } + + if (aw_route[i].source) { + ret = aw882xx_name_append_suffix(aw882xx, &aw_route[i].source); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "append source name suffix failed!"); + return; + } + } + } + snd_soc_dapm_add_routes(dapm, aw_route, ARRAY_SIZE(aw882xx_audio_map)); +#endif +} + +static void aw882xx_load_fw(struct aw882xx *aw882xx) +{ +#ifdef CONFIG_AW882XX_DSP + aw882xx_request_firmware(&aw882xx->fw_work.work); +#else + queue_delayed_work(aw882xx->work_queue, + &aw882xx->fw_work, + msecs_to_jiffies(AW882XX_LOAD_FW_DELAY_TIME)); +#endif +} + +static int aw882xx_codec_probe(aw_snd_soc_codec_t *aw_codec) +{ + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(aw_codec); + aw_dev_info(aw882xx->dev, "enter"); + + aw882xx->work_queue = create_singlethread_workqueue("aw882xx"); + if (!aw882xx->work_queue) { + aw_dev_err(aw882xx->dev, "create workqueue failed !"); + return -EINVAL; + } + + INIT_DELAYED_WORK(&aw882xx->start_work, aw882xx_startup_work); + INIT_DELAYED_WORK(&aw882xx->interrupt_work, aw882xx_interrupt_work); + INIT_DELAYED_WORK(&aw882xx->dc_work, aw882xx_dc_prot_work); + INIT_DELAYED_WORK(&aw882xx->fw_work, aw882xx_request_firmware); + + aw882xx->codec = aw_codec; + + if (aw882xx->index == 0) + aw882xx_add_codec_controls(aw882xx); + + aw882xx_add_widgets(aw882xx); + + /*load fw bin*/ + aw882xx_load_fw(aw882xx); + + /*load cali re*/ + aw_dev_init_cali_re(aw882xx->aw_pa); + + return 0; +} + +#ifdef AW_KERNEL_VER_OVER_4_19_1 +static void aw882xx_codec_remove(aw_snd_soc_codec_t *aw_codec) +{ + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(aw_codec); + aw_dev_info(aw882xx->dev, "enter"); + aw_dev_deinit(aw882xx->aw_pa); + destroy_workqueue(aw882xx->work_queue); + aw882xx->work_queue = NULL; +} +#else +static int aw882xx_codec_remove(aw_snd_soc_codec_t *aw_codec) +{ + struct aw882xx *aw882xx = + aw_componet_codec_ops.codec_get_drvdata(aw_codec); + + aw_dev_info(aw882xx->dev, "enter"); + aw_dev_deinit(aw882xx->aw_pa); + destroy_workqueue(aw882xx->work_queue); + aw882xx->work_queue = NULL; + return 0; +} +#endif + +#ifdef CONFIG_AW882XX_STEREO_SMARTPA +static int aw882xx_dai_drv_append_suffix(struct aw882xx *aw882xx, + struct snd_soc_dai_driver *dai_drv, + int num_dai) +{ + char buf[50]; + int i; + int i2cbus = aw882xx->i2c->adapter->nr; + int addr = aw882xx->i2c->addr; + + if ((dai_drv != NULL) && (num_dai > 0)) + for (i = 0; i < num_dai; i++) { + snprintf(buf, 50, "%s-%x-%x", dai_drv[i].name, i2cbus, + addr); + dai_drv[i].name = aw882xx_devm_kstrdup(aw882xx->dev, buf); + + snprintf(buf, 50, "%s-%x-%x", + dai_drv[i].playback.stream_name, + i2cbus, addr); + dai_drv[i].playback.stream_name = aw882xx_devm_kstrdup(aw882xx->dev, buf); + + snprintf(buf, 50, "%s-%x-%x", + dai_drv[i].capture.stream_name, + i2cbus, addr); + dai_drv[i].capture.stream_name = aw882xx_devm_kstrdup(aw882xx->dev, buf); + aw_dev_info(aw882xx->dev, "dai name [%s]", dai_drv[i].name); + aw_dev_info(aw882xx->dev, "pstream_name name [%s]", dai_drv[i].playback.stream_name); + aw_dev_info(aw882xx->dev, "cstream_name name [%s]", dai_drv[i].capture.stream_name); + } + + return 0; +} +#endif + +static struct snd_soc_dai_driver aw882xx_dai[] = { + { + .name = "aw882xx-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE), + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE), + }, + .ops = &aw882xx_dai_ops, + /* .symmetric_rates = 1,*/ + }, +}; + + +#ifdef AW_KERNEL_VER_OVER_4_19_1 +static struct snd_soc_component_driver soc_codec_dev_aw882xx = { + .probe = aw882xx_codec_probe, + .remove = aw882xx_codec_remove, +}; +#else +static struct snd_soc_codec_driver soc_codec_dev_aw882xx = { + .probe = aw882xx_codec_probe, + .remove = aw882xx_codec_remove, +}; +#endif + +#ifdef CONFIG_AW882XX_STEREO_SMARTPA +void awinic_set_dai_name(const char* drvdainame, const char*drvname); +#endif + +int aw_componet_codec_register(struct aw882xx *aw882xx) +{ + struct snd_soc_dai_driver *dai_drv; + int ret; +#ifdef CONFIG_AW882XX_STEREO_SMARTPA + char *aw882xxdrvname = AW882XX_I2C_NAME; +#endif + + dai_drv = devm_kzalloc(aw882xx->dev, sizeof(aw882xx_dai), GFP_KERNEL); + if (dai_drv == NULL) { + aw_dev_err(aw882xx->dev, "dai_driver malloc failed"); + return -ENOMEM; + } + + memcpy(dai_drv, aw882xx_dai, sizeof(aw882xx_dai)); + +#ifdef CONFIG_AW882XX_STEREO_SMARTPA + aw882xx_dai_drv_append_suffix(aw882xx, dai_drv, ARRAY_SIZE(aw882xx_dai)); + //change dai name // + aw882xx_name_append_suffix(aw882xx, (const char**)&aw882xxdrvname); + awinic_set_dai_name(dai_drv->name, aw882xxdrvname); + aw_dev_err(aw882xx->dev, "dai name %s codec name %s", dai_drv->name,aw882xxdrvname); + //change dai name end// +#endif + + ret = aw882xx->codec_ops->register_codec(aw882xx->dev, + &soc_codec_dev_aw882xx, + dai_drv, ARRAY_SIZE(aw882xx_dai)); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "failed to register aw882xx: %d", ret); + return -EINVAL; + } + + return 0; +} +/***************************************************** + * + * device tree + * + *****************************************************/ +static int aw882xx_parse_gpio_dt(struct aw882xx *aw882xx, + struct device_node *np) +{ + if (!np) { + aw882xx->reset_gpio = -1; + aw882xx->irq_gpio = -1; + return -EINVAL; + } + + aw882xx->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (aw882xx->reset_gpio < 0) { + aw_dev_info(aw882xx->dev, "no reset gpio provided, will not HW reset device"); + } else { + aw_dev_info(aw882xx->dev, "reset gpio provided ok"); + } + aw882xx->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (aw882xx->irq_gpio < 0) { + aw_dev_info(aw882xx->dev, "no irq gpio provided."); + } else { + aw_dev_info(aw882xx->dev, "irq gpio provided ok."); + } + + return 0; +} + +static struct aw882xx *aw882xx_malloc_init(struct i2c_client *i2c) +{ + struct aw882xx *aw882xx = devm_kzalloc(&i2c->dev, sizeof(struct aw882xx), GFP_KERNEL); + if (aw882xx == NULL) { + dev_err(&i2c->dev, "devm_kzalloc failed."); + return NULL; + } + + aw882xx->dev = &i2c->dev; + aw882xx->i2c = i2c; + aw882xx->aw_pa = NULL; + aw882xx->codec = NULL; + aw882xx->codec_ops = &aw_componet_codec_ops; + aw882xx->fw_status = AW_DEV_FW_FAILED; + aw882xx->fw_retry_cnt = 0; + aw882xx->dbg_en_prof = true; + aw882xx->allow_pw = true; + aw882xx->work_queue = NULL; + + mutex_init(&aw882xx->lock); + + return aw882xx; +} + +static int aw882xx_gpio_request(struct aw882xx *aw882xx) +{ + int ret; + if (gpio_is_valid(aw882xx->reset_gpio)) { + ret = devm_gpio_request_one(aw882xx->dev, aw882xx->reset_gpio, + GPIOF_OUT_INIT_LOW, "aw882xx_rst"); + if (ret) { + aw_dev_err(aw882xx->dev, "rst request failed"); + return ret; + } + } + + if (gpio_is_valid(aw882xx->irq_gpio)) { + ret = devm_gpio_request_one(aw882xx->dev, aw882xx->irq_gpio, + GPIOF_DIR_IN, "aw882xx_int"); + if (ret) { + aw_dev_err(aw882xx->dev, "int request failed"); + return ret; + } + } + + return 0; +} + +static int aw882xx_parse_dt(struct device *dev, struct aw882xx *aw882xx, + struct device_node *np) +{ + int ret; + int32_t dc_enable = 0; + int32_t sync_enable = 0; + + /*gpio dts parser*/ + ret = aw882xx_parse_gpio_dt(aw882xx, np); + if (ret) + return ret; + + ret = of_property_read_u32(np, "dc-flag", &dc_enable); + if (ret) { + dc_enable = false; + aw_dev_info(aw882xx->dev, "close dc protect!"); + } else { + aw_dev_info(aw882xx->dev, "dc-flag = %d", dc_enable); + } + + aw882xx->dc_flag = dc_enable; + + + ret = of_property_read_u32(np, "sync-flag", &sync_enable); + if (ret < 0) { + aw_dev_info(aw882xx->dev, + "read sync flag failed,default phase sync off"); + sync_enable = false; + } else { + aw_dev_info(aw882xx->dev, + "sync flag is %d", sync_enable); + } + + aw882xx->phase_sync = sync_enable; + + ret = of_property_read_u32(np, "fade-flag", &aw882xx->fade_flag); + if (ret) { + aw882xx->fade_flag = 0; + dev_err(dev, "%s: fade_flag get failed,use default value!\n", __func__); + } else { + dev_info(dev, "%s: fade_flag = %d\n", + __func__, aw882xx->fade_flag); + } + +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS + ret = of_property_read_u32(np, "skt-prof-mode", &aw882xx->skt_prof_mode); + if (ret) { + aw882xx->skt_prof_mode = AW_PARAMS_PATH_MODE; + dev_err(dev, "%s: skt-prof-mode get failed,use default value!\n", __func__); + } else { + dev_info(dev, "%s: skt-prof-mode = %d\n", + __func__, aw882xx->skt_prof_mode); + } +#endif + + return 0; +} + +int aw882xx_hw_reset(struct aw882xx *aw882xx) +{ + aw_dev_info(aw882xx->dev, "enter"); + + if (gpio_is_valid(aw882xx->reset_gpio)) { + gpio_set_value_cansleep(aw882xx->reset_gpio, 0); + mdelay(1); + gpio_set_value_cansleep(aw882xx->reset_gpio, 1); + mdelay(2); + } else { + aw_dev_info(aw882xx->dev, "has no reset gpio"); + } + return 0; +} + +static int aw882xx_read_chipid(struct aw882xx *aw882xx) +{ + int ret = -1; + unsigned int cnt = 0; + unsigned int reg_value = 0; + + while (cnt < AW_READ_CHIPID_RETRIES) { + ret = aw882xx_i2c_read(aw882xx, AW882XX_CHIP_ID_REG, ®_value); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "failed to read REG_ID: %d", ret); + return -EIO; + } + switch (reg_value) { + case PID_1852_ID: { + aw_dev_info(aw882xx->dev, "aw882xx 1852 detected"); + aw882xx->chip_id = reg_value; + return 0; + } + case PID_2013_ID: { + aw_dev_info(aw882xx->dev, "aw882xx 2013 detected"); + aw882xx->chip_id = reg_value; + return 0; + } + case PID_2032_ID: { + aw_dev_info(aw882xx->dev, "aw882xx 2032 detected"); + aw882xx->chip_id = reg_value; + return 0; + } + case PID_2055_ID: { + aw_dev_info(aw882xx->dev, "aw882xx 2055 detected"); + aw882xx->chip_id = reg_value; + return 0; + } + case PID_2071_ID: { + aw_dev_info(aw882xx->dev, "aw882xx 2071 detected"); + aw882xx->chip_id = reg_value; + return 0; + } + case PID_2113_ID: { + aw_dev_info(aw882xx->dev, "aw882xx 2113 detected"); + aw882xx->chip_id = reg_value; + return 0; + } + default: + aw_dev_info(aw882xx->dev, "unsupported device revision (0x%x)", + reg_value); + break; + } + cnt++; + + msleep(AW_READ_CHIPID_RETRY_DELAY); + } + + return -EINVAL; +} + +static irqreturn_t aw882xx_irq(int irq, void *data) +{ + struct aw882xx *aw882xx = (struct aw882xx *)data; + if (!aw882xx) { + aw_pr_err("pointer is NULL"); + return -EINVAL; + } + aw_dev_info(aw882xx->dev, "enter"); + + /* mask all irq */ + aw_dev_set_intmask(aw882xx->aw_pa, false); + + /* upload workqueue */ + if (aw882xx->work_queue) + queue_delayed_work(aw882xx->work_queue, &aw882xx->interrupt_work, 0); + + return IRQ_HANDLED; +} + +static int aw882xx_interrupt_init(struct aw882xx *aw882xx) +{ + int irq_flags; + int ret; + + if (gpio_is_valid(aw882xx->irq_gpio)) { + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(aw882xx->dev, + gpio_to_irq(aw882xx->irq_gpio), + NULL, aw882xx_irq, irq_flags, + "aw882xx", aw882xx); + if (ret != 0) { + aw_dev_err(aw882xx->dev, "Failed to request IRQ %d: %d", + gpio_to_irq(aw882xx->irq_gpio), ret); + return ret; + } + } else { + aw_dev_info(aw882xx->dev, "gpio invalid"); + /* disable interrupt */ + } + + return 0; +} + +static ssize_t aw882xx_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + unsigned int databuf[2] = {0}; + + if (2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) + aw882xx_i2c_write(aw882xx, databuf[0], databuf[1]); + + return count; +} + +static ssize_t aw882xx_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned char i = 0; + unsigned int reg_val = 0; + int reg_num = aw882xx->aw_pa->ops.aw_get_reg_num(); + + for (i = 0; i < reg_num; i++) { + if (aw882xx->aw_pa->ops.aw_check_rd_access(i)) { + aw882xx_i2c_read(aw882xx, i, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, + "reg:0x%02x=0x%04x\n", i, reg_val); + } + } + + return len; +} + +static ssize_t aw882xx_rw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0}; + + if (2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) { + aw882xx->rw_reg_addr = (unsigned char)databuf[0]; + if (aw882xx->aw_pa->ops.aw_check_rd_access(databuf[0])) + aw882xx_i2c_write(aw882xx, databuf[0], databuf[1]); + } else if (1 == sscanf(buf, "%x", &databuf[0])) { + aw882xx->rw_reg_addr = (unsigned char)databuf[0]; + } + + return count; +} + +static ssize_t aw882xx_rw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned int reg_val = 0; + + if (aw882xx->aw_pa->ops.aw_check_rd_access(aw882xx->rw_reg_addr)) { + aw882xx_i2c_read(aw882xx, aw882xx->rw_reg_addr, ®_val); + len += snprintf(buf + len, PAGE_SIZE - len, + "reg:0x%02x=0x%04x\n", aw882xx->rw_reg_addr, reg_val); + } + return len; +} + + int aw882xx_awrw_write(struct aw882xx *aw882xx, const char *buf, size_t count) +{ + int i, ret; + char *data_buf = NULL; + int str_len, data_len, temp_data; + char *reg_data; + struct aw882xx_i2c_packet *packet = &aw882xx->i2c_packet; + + aw_dev_info(aw882xx->dev, "write:reg_addr[0x%02x], reg_num[%d]", + packet->reg_addr, packet->reg_num); + + data_len = AWRW_DATA_BYTES * packet->reg_num; + + str_len = count - AWRW_HDR_LEN - 1; + + if ((data_len * 5 - 1) > str_len) { + aw_dev_err(aw882xx->dev, "data_str_len [%d], requeset len [%d]", + str_len, (data_len * 5 - 1)); + return -EINVAL; + } + + data_buf = kmalloc(data_len + 1, GFP_KERNEL); + if (data_buf == NULL) { + aw_dev_err(aw882xx->dev, "alloc memory failed"); + return -ENOMEM; + } + + data_buf[0] = packet->reg_addr; + reg_data = data_buf + 1; + + aw_dev_dbg(aw882xx->dev, "reg_addr: 0x%02x", data_buf[0]); + + for (i = 0; i < data_len; i++) { + sscanf(buf + AWRW_HDR_LEN + 1 + i * 5, "0x%02x", &temp_data); + reg_data[i] = temp_data; + aw_dev_dbg(aw882xx->dev, "[%d] : 0x%02x", i, reg_data[i]); + } + + ret = i2c_master_send(aw882xx->i2c, data_buf, data_len + 1); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "write failed"); + kfree(data_buf); + data_buf = NULL; + return -EFAULT; + } + + kfree(data_buf); + data_buf = NULL; + + aw_dev_info(aw882xx->dev, "write success"); + return 0; +} + +static int aw882xx_awrw_data_check(struct aw882xx *aw882xx, int *data) +{ + int reg_num_max = aw882xx->aw_pa->ops.aw_get_reg_num(); + + if ((data[AWRW_HDR_ADDR_BYTES] != AWRW_ADDR_BYTES) || + (data[AWRW_HDR_DATA_BYTES] != AWRW_DATA_BYTES)) { + aw_dev_err(aw882xx->dev, "addr_bytes [%d] or data_bytes [%d] unsupport", + data[AWRW_HDR_ADDR_BYTES], data[AWRW_HDR_DATA_BYTES]); + return -EINVAL; + } + + if (data[AWRW_HDR_REG_ADDR] >= reg_num_max) { + aw_dev_err(aw882xx->dev, "reg_addr[%d] > reg_max[%d]", + data[AWRW_HDR_REG_ADDR], reg_num_max); + return -EINVAL; + } + + return 0; +} + +/* flag addr_bytes data_bytes reg_num reg_addr*/ +static int aw882xx_awrw_parse_buf(struct aw882xx *aw882xx, + const char *buf, size_t count) +{ + int data[AWRW_HDR_MAX] = {0}; + struct aw882xx_i2c_packet *packet = &aw882xx->i2c_packet; + int ret; + + if (sscanf(buf, "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", + &data[AWRW_HDR_WR_FLAG], + &data[AWRW_HDR_ADDR_BYTES], + &data[AWRW_HDR_DATA_BYTES], + &data[AWRW_HDR_REG_NUM], + &data[AWRW_HDR_REG_ADDR]) == 5) { + ret = aw882xx_awrw_data_check(aw882xx, data); + if (ret < 0) + return ret; + + packet->reg_addr = data[AWRW_HDR_REG_ADDR]; + packet->reg_num = data[AWRW_HDR_REG_NUM]; + + if (data[AWRW_HDR_WR_FLAG] == AWRW_FLAG_WRITE) { + return aw882xx_awrw_write(aw882xx, buf, count); + } else if (data[AWRW_HDR_WR_FLAG] == AWRW_FLAG_READ) { + packet->status = AWRW_I2C_ST_READ; + aw_dev_info(aw882xx->dev, "read_cmd:reg_addr[0x%02x], reg_num[%d]", + packet->reg_addr, packet->reg_num); + return 0; + } else { + aw_dev_err(aw882xx->dev, "please check str format, unsupport flag %d", + data[AWRW_HDR_WR_FLAG]); + return -EINVAL; + } + } else { + aw_dev_err(aw882xx->dev, "can not parse string"); + return -EINVAL; + } + +} + +static ssize_t aw882xx_awrw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + int ret; + + if (count < AWRW_HDR_LEN) { + aw_dev_err(dev, "data count too smaller, please check write format"); + aw_dev_err(dev, "string %s", buf); + return -EINVAL; + } + + ret = aw882xx_awrw_parse_buf(aw882xx, buf, count); + if (ret) + return -EINVAL; + + return count; +} + +static ssize_t aw882xx_awrw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw882xx_i2c_packet *packet = &aw882xx->i2c_packet; + int data_len, len = 0; + int ret, i; + char *reg_data = NULL; + + if (packet->status != AWRW_I2C_ST_READ) { + aw_dev_err(aw882xx->dev, "please write read cmd first"); + return -EINVAL; + } + + data_len = AWRW_DATA_BYTES * packet->reg_num; + reg_data = (char *)kmalloc(data_len, GFP_KERNEL); + if (reg_data == NULL) { + aw_dev_err(aw882xx->dev, "memory alloc failed"); + ret = -EINVAL; + goto exit; + } + + ret = aw882xx_i2c_reads(aw882xx, packet->reg_addr, (char *)reg_data, data_len); + if (ret < 0) { + ret = -EFAULT; + goto exit; + } + + aw_dev_info(aw882xx->dev, "reg_addr 0x%02x, reg_num %d", + packet->reg_addr, packet->reg_num); + + for (i = 0; i < data_len; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "0x%02x,", reg_data[i]); + aw_dev_dbg(aw882xx->dev, "0x%02x", reg_data[i]); + } + + ret = len; + +exit: + if (reg_data) + kfree(reg_data); + + packet->status = AWRW_I2C_ST_NONE; + return ret; +} + + +static ssize_t aw882xx_drv_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "driver_ver: %s \n", AW882XX_DRIVER_VERSION); + + return len; +} + +static ssize_t aw882xx_dsp_re_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + int cali_re; + int ret; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + ret = aw_dev_get_cali_re(aw882xx->aw_pa, &cali_re); + if (ret) { + len += snprintf(buf + len, PAGE_SIZE - len, + "read dsp_re failed!\n"); + return len; + } + + len += snprintf(buf + len, PAGE_SIZE - len, + "%d \n", cali_re); + + return len; +} + +static ssize_t aw882xx_fade_step_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0}; + + /*step 0 - 12*/ + if (1 == sscanf(buf, "%d", &databuf[0])) { + if (databuf[0] > (aw882xx->aw_pa->volume_desc.mute_volume)) { + aw_dev_info(aw882xx->dev, "step overflow %d Db", databuf[0]); + return count; + } + aw_dev_set_fade_vol_step(aw882xx->aw_pa, databuf[0]); + } + aw_dev_info(aw882xx->dev, "set step %d Done", databuf[0]); + + return count; +} + +static ssize_t aw882xx_fade_step_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "step: %d \n", aw_dev_get_fade_vol_step(aw882xx->aw_pa)); + + return len; +} + +static ssize_t aw882xx_dbg_prof_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0}; + + if (1 == sscanf(buf, "%d", &databuf[0])) { + if (databuf[0]) + aw882xx->dbg_en_prof = true; + else + aw882xx->dbg_en_prof = false; + } + aw_dev_info(aw882xx->dev, "en_prof %d Done", databuf[0]); + + return count; +} + +static ssize_t aw882xx_dbg_prof_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + " %d \n", aw882xx->dbg_en_prof); + + return len; +} + +static ssize_t aw882xx_sync_flag_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + unsigned int flag = 0; + int ret; + + ret = kstrtouint(buf, 0, &flag); + if (ret < 0) + return ret; + + flag = ((flag == false) ? false : true); + + aw_dev_info(aw882xx->dev, "set phase sync flag : [%d]", flag); + + aw882xx->phase_sync = flag; + + return count; +} + +static ssize_t aw882xx_sync_flag_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "sync flag : %d\n", aw882xx->phase_sync); + + return len; +} + +static ssize_t aw882xx_print_dbg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + int ret; + + ret = kstrtouint(buf, 0, &g_print_dbg); + if (ret < 0) + return ret; + + g_print_dbg = ((g_print_dbg == false) ? false : true); + + aw_dev_info(aw882xx->dev, "set g_print_dbg : [%d]", g_print_dbg); + + return count; +} + +static ssize_t aw882xx_print_dbg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "g_print_dbg : %d\n", g_print_dbg); + + return len; +} + +static ssize_t aw882xx_algo_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + int ret; + char algo_ver_buf[ALGO_VERSION_MAX] = { 0 }; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + ret = aw_get_algo_version(aw882xx->aw_pa, algo_ver_buf); + if (ret < 0) { + len += snprintf(buf + len, PAGE_SIZE - len, + "read algo version failed!\n"); + return len; + } + len += snprintf(buf + len, PAGE_SIZE - len, "%s\n", algo_ver_buf); + + return len; +} + +static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, + aw882xx_reg_show, aw882xx_reg_store); +static DEVICE_ATTR(rw, S_IWUSR | S_IRUGO, + aw882xx_rw_show, aw882xx_rw_store); +static DEVICE_ATTR(awrw, S_IWUSR | S_IRUGO, + aw882xx_awrw_show, aw882xx_awrw_store); +static DEVICE_ATTR(drv_ver, S_IRUGO, + aw882xx_drv_ver_show, NULL); +static DEVICE_ATTR(dsp_re, S_IRUGO, + aw882xx_dsp_re_show, NULL); +static DEVICE_ATTR(fade_step, S_IWUSR | S_IRUGO, + aw882xx_fade_step_show, aw882xx_fade_step_store); +static DEVICE_ATTR(dbg_prof, S_IWUSR | S_IRUGO, + aw882xx_dbg_prof_show, aw882xx_dbg_prof_store); +static DEVICE_ATTR(phase_sync, S_IWUSR | S_IRUGO, + aw882xx_sync_flag_show, aw882xx_sync_flag_store); +static DEVICE_ATTR(print_dbg, S_IWUSR | S_IRUGO, + aw882xx_print_dbg_show, aw882xx_print_dbg_store); +static DEVICE_ATTR(algo_ver, S_IRUGO, + aw882xx_algo_ver_show, NULL); + + +static struct attribute *aw882xx_attributes[] = { + &dev_attr_reg.attr, + &dev_attr_rw.attr, + &dev_attr_awrw.attr, + &dev_attr_drv_ver.attr, + &dev_attr_fade_step.attr, + &dev_attr_dbg_prof.attr, + &dev_attr_dsp_re.attr, + &dev_attr_phase_sync.attr, + &dev_attr_print_dbg.attr, + &dev_attr_algo_ver.attr, + NULL +}; + +static struct attribute_group aw882xx_attribute_group = { + .attrs = aw882xx_attributes, +}; + +static int aw882xx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct aw882xx *aw882xx; + struct device_node *np = i2c->dev.of_node; + int ret; + + aw_pr_info("enter addr=0x%x", i2c->addr); + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + aw_dev_err(&i2c->dev, "check_functionality failed"); + return -EIO; + } + + /*dev free all auto free*/ + aw882xx = aw882xx_malloc_init(i2c); + if (aw882xx == NULL) { + aw_dev_err(&i2c->dev, "malloc aw882xx failed"); + return -ENOMEM; + } + + i2c_set_clientdata(i2c, aw882xx); + + ret = aw882xx_parse_dt(&i2c->dev, aw882xx, np); + if (ret) { + aw_dev_err(&i2c->dev, "failed to parse device tree node"); + return ret; + } + + /*get gpio resource*/ + ret = aw882xx_gpio_request(aw882xx); + if (ret) + return ret; + + /* hardware reset */ + aw882xx_hw_reset(aw882xx); + + /* aw882xx chip id */ + ret = aw882xx_read_chipid(aw882xx); + if (ret < 0) { + aw_dev_err(&i2c->dev, "aw882xx_read_chipid failed ret=%d", ret); + return ret; + } + + /*aw pa init*/ + ret = aw882xx_init(aw882xx, g_aw882xx_dev_cnt); + if (ret) + return ret; + + /*aw882xx irq*/ + aw882xx_interrupt_init(aw882xx); + + /*codec register*/ + ret = aw_componet_codec_register(aw882xx); + if (ret) { + aw_dev_err(&i2c->dev, "codec register failde"); + return ret; + } + + /*create attr*/ + ret = sysfs_create_group(&i2c->dev.kobj, &aw882xx_attribute_group); + if (ret < 0) { + aw_dev_err(aw882xx->dev, "error creating sysfs attr files"); + goto err_sysfs; + } + + /*set aw882xx to dev private*/ + dev_set_drvdata(&i2c->dev, aw882xx); + + /*i2c packet init*/ + aw882xx->i2c_packet.status = AWRW_I2C_ST_NONE; + aw882xx->i2c_packet.reg_num = 0; + aw882xx->i2c_packet.reg_addr = 0xff; + aw882xx->i2c_packet.reg_data = NULL; + + aw882xx->index = g_aw882xx_dev_cnt; + + aw882xx->aw882xx_ramp_status = AW882XX_RAMP_OFF; +#ifdef AW882XX_RUNIN_TEST + g_runin_test = 0; + INIT_DELAYED_WORK(&aw882xx->adsp_status, aw882xx_set_adsp_module_status); +#endif + /*add device to total list*/ + mutex_lock(&g_aw882xx_lock); + g_aw882xx_dev_cnt++; + mutex_unlock(&g_aw882xx_lock); + aw_dev_info(&i2c->dev, "dev_cnt %d", g_aw882xx_dev_cnt); + return ret; +err_sysfs: + aw_componet_codec_ops.unregister_codec(&i2c->dev); + + return ret; +} + +static int aw882xx_i2c_remove(struct i2c_client *i2c) +{ + struct aw882xx *aw882xx = i2c_get_clientdata(i2c); + + aw_dev_info(aw882xx->dev, "enter"); + + /*rm irq*/ + if (gpio_to_irq(aw882xx->irq_gpio)) + devm_free_irq(&i2c->dev, + gpio_to_irq(aw882xx->irq_gpio), + aw882xx); + + /*free gpio*/ + if (gpio_is_valid(aw882xx->irq_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->irq_gpio); + if (gpio_is_valid(aw882xx->reset_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->reset_gpio); + + /*rm attr node*/ + sysfs_remove_group(&i2c->dev.kobj, &aw882xx_attribute_group); + + /*free device resource */ + aw_device_remove(aw882xx->aw_pa); + + /*unregister codec*/ + aw882xx->codec_ops->unregister_codec(&i2c->dev); + + /*remove device to total list*/ + mutex_lock(&g_aw882xx_lock); + g_aw882xx_dev_cnt--; + if (g_aw882xx_dev_cnt == 0) { + if (g_awinic_cfg) { + vfree(g_awinic_cfg); + g_awinic_cfg = NULL; + } + if (g_awinic_skt_cfg) { + vfree(g_awinic_skt_cfg); + g_awinic_skt_cfg = NULL; + } + } + mutex_unlock(&g_aw882xx_lock); + + return 0; + +} + +static void aw882xx_i2c_shutdown(struct i2c_client *i2c) +{ + struct aw882xx *aw882xx = i2c_get_clientdata(i2c); + + aw_dev_info(aw882xx->dev, "enter"); + mutex_lock(&aw882xx->lock); + aw_device_stop(aw882xx->aw_pa); + mutex_unlock(&aw882xx->lock); +} + +static const struct i2c_device_id aw882xx_i2c_id[] = { + { AW882XX_I2C_NAME, 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, aw882xx_i2c_id); + +static struct of_device_id aw882xx_dt_match[] = { + { .compatible = "awinic,aw882xxacf_smartpa" }, + { }, +}; + +static struct i2c_driver aw882xx_i2c_driver = { + .driver = { + .name = AW882XX_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw882xx_dt_match), + }, + .probe = aw882xx_i2c_probe, + .remove = aw882xx_i2c_remove, + .shutdown = aw882xx_i2c_shutdown, + .id_table = aw882xx_i2c_id, +}; + + +static int __init aw882xx_i2c_init(void) +{ + int ret = -1; + + aw_pr_info("aw882xx driver version %s", AW882XX_DRIVER_VERSION); + + ret = i2c_add_driver(&aw882xx_i2c_driver); + if (ret) + aw_pr_err("fail to add aw882xx device into i2c"); + + return ret; +} +module_init(aw882xx_i2c_init); + + +static void __exit aw882xx_i2c_exit(void) +{ + i2c_del_driver(&aw882xx_i2c_driver); +} +module_exit(aw882xx_i2c_exit); + + +MODULE_DESCRIPTION("ASoC AW882XX Smart PA Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/techpack/audio/asoc/codecs/aw882xxacf/aw882xx.h b/techpack/audio/asoc/codecs/aw882xxacf/aw882xx.h new file mode 100644 index 000000000000..47dea41fdf5d --- /dev/null +++ b/techpack/audio/asoc/codecs/aw882xxacf/aw882xx.h @@ -0,0 +1,274 @@ +#ifndef _AW882XX_H_ +#define _AW882XX_H_ +#include +#include +#include "aw_device.h" + +/* + * i2c transaction on Linux limited to 64k + * (See Linux kernel documentation: Documentation/i2c/writing-clients) +*/ +#define AW882XX_CHIP_ID_REG (0x00) +#define MAX_I2C_BUFFER_SIZE 65536 +#define AW882XX_I2C_READ_MSG_NUM 2 +#define AW882XX_DC_DELAY_TIME (1000) +#define AW882XX_LOAD_FW_DELAY_TIME (0) +#define AW_START_RETRIES (5) +#define AW882XX_RUNIN_TEST +#define AW_ALGO_SKT_BIN "aw_skt.bin" +#define AW_DEFAULT_PRO_ID (0xFFFFFFFF) + + +#define AW_I2C_RETRIES 5 /* 5 times */ +#define AW_I2C_RETRY_DELAY 5 /* 5 ms */ + + +#define AW882XX_RATES SNDRV_PCM_RATE_8000_48000 +#define AW882XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +enum { + AW882XX_STREAM_CLOSE = 0, + AW882XX_STREAM_OPEN, +}; + +enum aw882xx_chipid { + PID_1852_ID = 0x1852, + PID_2013_ID = 0x2013, + PID_2032_ID = 0x2032, + PID_2055_ID = 0x2055, + PID_2071_ID = 0x2071, + PID_2113_ID = 0x2113, +}; + +#define AW882XX_SOFT_RESET_VALUE (0x55aa) + +enum aw882xx_int_type { + INT_TYPE_NONE = 0, + INT_TYPE_UVLO = 0x1, + INT_TYPE_BSTOC = 0x2, + INT_TYPE_OCDI = 0x4, + INT_TYPE_OTHI = 0x8, +}; + +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS +enum { + AW_ALGO_PROFILE_ID_0 = 0, + AW_ALGO_PROFILE_ID_1, + AW_ALGO_PROFILE_ID_2, + AW_ALGO_PROFILE_ID_3, + AW_ALGO_PROFILE_ID_4, + AW_ALGO_PROFILE_ID_5, + AW_ALGO_PROFILE_ID_6, + AW_ALGO_PROFILE_ID_7, + AW_ALGO_PROFILE_ID_MAX, +}; + +struct aw882xx_scene_info { + int id; + char name[20]; + int priority; + int skt_profile_id; + int is_active; + int active_cnt; +}; + +enum { + AW_PARAMS_PATH_MODE = 0, + AW_PARAMS_DATA_MODE, +}; + + +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 1) +#define AW_KERNEL_VER_OVER_4_19_1 +#endif + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) +#define AW_KERNEL_VER_OVER_5_4_0 +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif + + +#ifdef AW_KERNEL_VER_OVER_4_19_1 +typedef struct snd_soc_component aw_snd_soc_codec_t; +typedef struct snd_soc_component_driver aw_snd_soc_codec_driver_t; +#else +typedef struct snd_soc_codec aw_snd_soc_codec_t; +typedef struct snd_soc_codec_driver aw_snd_soc_codec_driver_t; +#endif + +struct aw_componet_codec_ops { + aw_snd_soc_codec_t *(*kcontrol_codec)(struct snd_kcontrol *kcontrol); + void *(*codec_get_drvdata)(aw_snd_soc_codec_t *codec); + int (*add_codec_controls)(aw_snd_soc_codec_t *codec, + const struct snd_kcontrol_new *controls, unsigned int num_controls); + void (*unregister_codec)(struct device *dev); + int (*register_codec)(struct device *dev, + const aw_snd_soc_codec_driver_t *codec_drv, + struct snd_soc_dai_driver *dai_drv, + int num_dai); +}; + +enum { + AWRW_I2C_ST_NONE = 0, + AWRW_I2C_ST_READ, + AWRW_I2C_ST_WRITE, +}; + +#define AWRW_ADDR_BYTES (1) +#define AWRW_DATA_BYTES (2) +#define AWRW_HDR_LEN (24) + +enum { + AWRW_FLAG_WRITE = 0, + AWRW_FLAG_READ, +}; + +enum { + AW_BSTCFG_DISABLE = 0, + AW_BSTCFG_ENABLE, +}; + +enum { + AW_FRCSET_DISABLE = 0, + AW_FRCSET_ENABLE, +}; + +enum { + AW_BOP_DISABLE = 0, + AW_BOP_ENABLE, +}; + +enum { + AW882XX_RAMP_OFF = 0, + AW882XX_RAMP_ON, +}; + +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS +enum { + AW_ALGO_CALI_OFF = 0, + AW_ALGO_CALI_ON, +}; + +enum { + AW_ALGO_BYPASS_OFF = 0, + AW_ALGO_BYPASS_ON, +}; + +enum { + AW_ALGO_HANDSET_OFF = 0, + AW_ALGO_HANDSET_ON, +}; + +enum { + AW_ALGO_VOICE_OFF = 0, + AW_ALGO_VOICE_ON, +}; + +enum { + AW_ALGO_VOIP_OFF = 0, + AW_ALGO_VOIP_ON, +}; + +enum { + AW_ALGO_MUSIC_DEEPBUFFER_OFF = 0, + AW_ALGO_MUSIC_DEEPBUFFER_ON, +}; + +enum { + AW_ALGO_MUSIC_FASTTRACK_OFF = 0, + AW_ALGO_MUSIC_FASTTRACK_ON, +}; +#endif + + +enum { + AWRW_HDR_WR_FLAG = 0, + AWRW_HDR_ADDR_BYTES, + AWRW_HDR_DATA_BYTES, + AWRW_HDR_REG_NUM, + AWRW_HDR_REG_ADDR, + AWRW_HDR_MAX, +}; + +struct aw882xx_i2c_packet{ + char status; + unsigned int reg_num; + unsigned int reg_addr; + char *reg_data; +}; + +/******************************************** + * struct aw882xx + *******************************************/ +struct aw882xx { + int sysclk; + int rate; + int pstream; + int cstream; + int aw882xx_ramp_status; /* ramp status */ +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS + int aw882xx_algo_cali; + int aw882xx_algo_bypass; + int aw882xx_algo_handset; + int aw882xx_algo_voice; + int aw882xx_algo_voip; + int aw882xx_algo_music_deepbuffer; + int aw882xx_algo_music_fasttrack; + int aw882xx_algo_prof_id; + int cur_algo_prof_id; +#endif + unsigned int skt_prof_mode; + + unsigned int fade_flag; + unsigned char index; + unsigned char phase_sync; /* phase sync */ + unsigned char dc_flag; + unsigned char dbg_en_prof; /* debug enable/disable profile function */ + unsigned char allow_pw; /* allow power */ + int reset_gpio; + int irq_gpio; + unsigned int chip_id; + unsigned char fw_status; + unsigned char fw_retry_cnt; + unsigned char rw_reg_addr; /* rw attr node store read addr */ + + aw_snd_soc_codec_t *codec; + struct aw_componet_codec_ops *codec_ops; + + struct i2c_client *i2c; + struct device *dev; + struct aw882xx_i2c_packet i2c_packet; + struct aw_device *aw_pa; + + struct workqueue_struct *work_queue; + struct delayed_work start_work; + struct delayed_work interrupt_work; + struct delayed_work dc_work; + struct delayed_work fw_work; +#ifdef AW882XX_RUNIN_TEST + struct delayed_work adsp_status; +#endif + struct mutex lock; +}; + +void aw882xx_kcontorl_set(struct aw882xx *aw882xx); +int aw882xx_get_version(char *buf, int size); +int aw882xx_get_dev_num(void); +int aw882xx_i2c_write(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int reg_data); +int aw882xx_i2c_read(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int *reg_data); +int aw882xx_i2c_write_bits(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int mask, unsigned int reg_data); +int aw882xx_init(struct aw882xx *aw882xx, int index); +int aw882xx_hw_reset(struct aw882xx *aw882xx); + + +#endif + diff --git a/techpack/audio/asoc/codecs/aw882xxacf/aw_bin_parse.c b/techpack/audio/asoc/codecs/aw882xxacf/aw_bin_parse.c new file mode 100644 index 000000000000..d7cfcb555df3 --- /dev/null +++ b/techpack/audio/asoc/codecs/aw882xxacf/aw_bin_parse.c @@ -0,0 +1,512 @@ +/* +* aw_bin_parse.c +* +* Copyright (c) 2020 AWINIC Technology CO., LTD +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw_bin_parse.h" + +#define AWINIC_CODE_VERSION "V0.0.7-V1.0.4" /* "code version"-"excel version" */ + +#define DEBUG_LOG_LEVEL +#ifdef DEBUG_LOG_LEVEL +#define DBG(fmt, arg...) do {\ +printk("AWINIC_BIN %s,line= %d,"fmt, __func__, __LINE__, ##arg);\ +} while (0) +#define DBG_ERR(fmt, arg...) do {\ +printk("AWINIC_BIN_ERR %s,line= %d,"fmt, __func__, __LINE__, ##arg);\ +} while (0) +#else +#define DBG(fmt, arg...) do {} while (0) +#define DBG_ERR(fmt, arg...) do {} while (0) +#endif + +#define printing_data_code + +typedef unsigned short int aw_uint16; +typedef unsigned long int aw_uint32; + +#define BigLittleSwap16(A) ((((aw_uint16)(A) & 0xff00) >> 8) | \ + (((aw_uint16)(A) & 0x00ff) << 8)) + +#define BigLittleSwap32(A) ((((aw_uint32)(A) & 0xff000000) >> 24) | \ + (((aw_uint32)(A) & 0x00ff0000) >> 8) | \ + (((aw_uint32)(A) & 0x0000ff00) << 8) | \ + (((aw_uint32)(A) & 0x000000ff) << 24)) + +/** +* +* Interface function +* +* return value: +* value = 0 :success; +* value = -1 :check bin header version +* value = -2 :check bin data type +* value = -3 :check sum or check bin data len error +* value = -4 :check data version +* value = -5 :check register num +* value = -6 :check dsp reg num +* value = -7 :check soc app num +* value = -8 :bin is NULL point +* +**/ + +/******************************************************** +* +* check sum data +* +********************************************************/ +int aw_check_sum(struct aw_bin *bin, int bin_num) +{ + unsigned int i = 0; + unsigned int sum_data = 0; + unsigned int check_sum = 0; + unsigned char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr - + bin->header_info[bin_num].header_len)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + check_sum = GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + + for (i = 4; + i < + bin->header_info[bin_num].bin_data_len + + bin->header_info[bin_num].header_len; i++) { + sum_data += *(p_check_sum + i); + } + DBG("aw_bin_parse bin_num=%d, check_sum = 0x%x, sum_data = 0x%x\n", + bin_num, check_sum, sum_data); + if (sum_data != check_sum) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse check sum or check bin data len error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, check_sum = 0x%x, sum_data = 0x%x\n", bin_num, check_sum, sum_data); + return -3; + } + p_check_sum = NULL; + + return 0; +} + +int aw_check_data_version(struct aw_bin *bin, int bin_num) +{ + int i = 0; + DBG("enter\n"); + + for (i = DATA_VERSION_V1; i < DATA_VERSION_MAX; i++) { + if (bin->header_info[bin_num].bin_data_ver == i) { + return 0; + } + } + DBG_ERR("aw_bin_parse Unrecognized this bin data version\n"); + return -4; +} + +int aw_check_register_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_register_num = 0; + unsigned int parse_register_num = 0; + char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + parse_register_num = GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + check_register_num = (bin->header_info[bin_num].bin_data_len - 4) / + (bin->header_info[bin_num].reg_byte_len + + bin->header_info[bin_num].data_byte_len); + DBG + ("aw_bin_parse bin_num=%d, parse_register_num = 0x%x, check_register_num = 0x%x\n", + bin_num, parse_register_num, check_register_num); + if (parse_register_num != check_register_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse register num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_register_num = 0x%x, check_register_num = 0x%x\n", bin_num, parse_register_num, check_register_num); + return -5; + } + bin->header_info[bin_num].reg_num = parse_register_num; + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 4; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 4; + return 0; +} + +int aw_check_dsp_reg_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_dsp_reg_num = 0; + unsigned int parse_dsp_reg_num = 0; + char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + parse_dsp_reg_num = GET_32_DATA(*(p_check_sum + 7), + *(p_check_sum + 6), + *(p_check_sum + 5), *(p_check_sum + 4)); + bin->header_info[bin_num].reg_data_byte_len = + GET_32_DATA(*(p_check_sum + 11), *(p_check_sum + 10), + *(p_check_sum + 9), *(p_check_sum + 8)); + check_dsp_reg_num = + (bin->header_info[bin_num].bin_data_len - + 12) / bin->header_info[bin_num].reg_data_byte_len; + DBG + ("aw_bin_parse bin_num=%d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x\n", + bin_num, parse_dsp_reg_num, check_dsp_reg_num); + if (parse_dsp_reg_num != check_dsp_reg_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse dsp reg num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x\n", bin_num, parse_dsp_reg_num, check_dsp_reg_num); + return -6; + } + bin->header_info[bin_num].download_addr = + GET_32_DATA(*(p_check_sum + 3), *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + bin->header_info[bin_num].reg_num = parse_dsp_reg_num; + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 12; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 12; + return 0; +} + +int aw_check_soc_app_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_soc_app_num = 0; + unsigned int parse_soc_app_num = 0; + char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + bin->header_info[bin_num].app_version = GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), + *(p_check_sum)); + parse_soc_app_num = GET_32_DATA(*(p_check_sum + 11), + *(p_check_sum + 10), + *(p_check_sum + 9), *(p_check_sum + 8)); + check_soc_app_num = bin->header_info[bin_num].bin_data_len - 12; + DBG + ("aw_bin_parse bin_num=%d, parse_soc_app_num = 0x%x, check_soc_app_num = 0x%x\n", + bin_num, parse_soc_app_num, check_soc_app_num); + if (parse_soc_app_num != check_soc_app_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse soc app num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_soc_app_num = 0x%x, check_soc_app_num = 0x%x\n", bin_num, parse_soc_app_num, check_soc_app_num); + return -7; + } + bin->header_info[bin_num].reg_num = parse_soc_app_num; + bin->header_info[bin_num].download_addr = + GET_32_DATA(*(p_check_sum + 7), *(p_check_sum + 6), + *(p_check_sum + 5), *(p_check_sum + 4)); + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 12; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 12; + return 0; +} + +/************************ +*** +***bin header 1_0_0 +*** +************************/ +void aw_get_single_bin_header_1_0_0(struct aw_bin *bin) +{ + int i; + DBG("enter %s\n", __func__); + bin->header_info[bin->all_bin_parse_num].header_len = 60; + bin->header_info[bin->all_bin_parse_num].check_sum = + GET_32_DATA(*(bin->p_addr + 3), *(bin->p_addr + 2), + *(bin->p_addr + 1), *(bin->p_addr)); + bin->header_info[bin->all_bin_parse_num].header_ver = + GET_32_DATA(*(bin->p_addr + 7), *(bin->p_addr + 6), + *(bin->p_addr + 5), *(bin->p_addr + 4)); + bin->header_info[bin->all_bin_parse_num].bin_data_type = + GET_32_DATA(*(bin->p_addr + 11), *(bin->p_addr + 10), + *(bin->p_addr + 9), *(bin->p_addr + 8)); + bin->header_info[bin->all_bin_parse_num].bin_data_ver = + GET_32_DATA(*(bin->p_addr + 15), *(bin->p_addr + 14), + *(bin->p_addr + 13), *(bin->p_addr + 12)); + bin->header_info[bin->all_bin_parse_num].bin_data_len = + GET_32_DATA(*(bin->p_addr + 19), *(bin->p_addr + 18), + *(bin->p_addr + 17), *(bin->p_addr + 16)); + bin->header_info[bin->all_bin_parse_num].ui_ver = + GET_32_DATA(*(bin->p_addr + 23), *(bin->p_addr + 22), + *(bin->p_addr + 21), *(bin->p_addr + 20)); + bin->header_info[bin->all_bin_parse_num].reg_byte_len = + GET_32_DATA(*(bin->p_addr + 35), *(bin->p_addr + 34), + *(bin->p_addr + 33), *(bin->p_addr + 32)); + bin->header_info[bin->all_bin_parse_num].data_byte_len = + GET_32_DATA(*(bin->p_addr + 39), *(bin->p_addr + 38), + *(bin->p_addr + 37), *(bin->p_addr + 36)); + bin->header_info[bin->all_bin_parse_num].device_addr = + GET_32_DATA(*(bin->p_addr + 43), *(bin->p_addr + 42), + *(bin->p_addr + 41), *(bin->p_addr + 40)); + for (i = 0; i < 8; i++) { + bin->header_info[bin->all_bin_parse_num].chip_type[i] = + *(bin->p_addr + 24 + i); + } + bin->header_info[bin->all_bin_parse_num].reg_num = 0x00000000; + bin->header_info[bin->all_bin_parse_num].reg_data_byte_len = 0x00000000; + bin->header_info[bin->all_bin_parse_num].download_addr = 0x00000000; + bin->header_info[bin->all_bin_parse_num].app_version = 0x00000000; + bin->header_info[bin->all_bin_parse_num].valid_data_len = 0x00000000; + bin->all_bin_parse_num += 1; +} + +int aw_parse_each_of_multi_bins_1_0_0(unsigned int bin_num, int bin_serial_num, + struct aw_bin *bin) +{ + int ret = 0; + unsigned int bin_start_addr = 0; + unsigned int valid_data_len = 0; + DBG("aw_bin_parse enter multi bin branch -- %s\n", __func__); + if (!bin_serial_num) { + bin_start_addr = GET_32_DATA(*(bin->p_addr + 67), + *(bin->p_addr + 66), + *(bin->p_addr + 65), + *(bin->p_addr + 64)); + bin->p_addr += (60 + bin_start_addr); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + bin->header_info[bin->all_bin_parse_num - + 1].valid_data_addr + 4 + 8 * bin_num + 60; + } else { + valid_data_len = + bin->header_info[bin->all_bin_parse_num - 1].bin_data_len; + bin->p_addr += (60 + valid_data_len); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + bin->header_info[bin->all_bin_parse_num - + 1].valid_data_addr + + bin->header_info[bin->all_bin_parse_num - 1].bin_data_len + + 60; + } + + ret = aw_parse_bin_header_1_0_0(bin); + return ret; +} + +/* Get the number of bins in multi bins, and set a for loop, loop processing each bin data */ +int aw_get_multi_bin_header_1_0_0(struct aw_bin *bin) +{ + int i = 0; + int ret = 0; + unsigned int bin_num = 0; + DBG("aw_bin_parse enter multi bin branch -- %s\n", __func__); + bin_num = GET_32_DATA(*(bin->p_addr + 63), + *(bin->p_addr + 62), + *(bin->p_addr + 61), *(bin->p_addr + 60)); + if (bin->multi_bin_parse_num == 1) { + bin->header_info[bin->all_bin_parse_num].valid_data_addr = 60; + } + aw_get_single_bin_header_1_0_0(bin); + + for (i = 0; i < bin_num; i++) { + DBG("aw_bin_parse enter multi bin for is %d\n", i); + ret = aw_parse_each_of_multi_bins_1_0_0(bin_num, i, bin); + if (ret < 0) { + return ret; + } + } + return 0; +} + +/******************************************************** +* +* If the bin framework header version is 1.0.0, + determine the data type of bin, and then perform different processing + according to the data type + If it is a single bin data type, write the data directly into the structure array + If it is a multi-bin data type, first obtain the number of bins, + and then recursively call the bin frame header processing function + according to the bin number to process the frame header information of each bin separately +* +********************************************************/ +int aw_parse_bin_header_1_0_0(struct aw_bin *bin) +{ + int ret = 0; + unsigned int bin_data_type; + DBG("enter %s\n", __func__); + bin_data_type = GET_32_DATA(*(bin->p_addr + 11), + *(bin->p_addr + 10), + *(bin->p_addr + 9), *(bin->p_addr + 8)); + DBG("aw_bin_parse bin_data_type 0x%x\n", bin_data_type); + switch (bin_data_type) { + case DATA_TYPE_REGISTER: + case DATA_TYPE_DSP_REG: + case DATA_TYPE_SOC_APP: + /* Divided into two processing methods, + one is single bin processing, + and the other is single bin processing in multi bin */ + DBG("aw_bin_parse enter single bin branch\n"); + bin->single_bin_parse_num += 1; + DBG("%s bin->single_bin_parse_num is %d\n", __func__, + bin->single_bin_parse_num); + if (!bin->multi_bin_parse_num) { + bin->header_info[bin-> + all_bin_parse_num].valid_data_addr = + 60; + } + aw_get_single_bin_header_1_0_0(bin); + break; + case DATA_TYPE_MULTI_BINS: + /* Get the number of times to enter multi bins */ + DBG("aw_bin_parse enter multi bin branch\n"); + bin->multi_bin_parse_num += 1; + DBG("%s bin->multi_bin_parse_num is %d\n", __func__, + bin->multi_bin_parse_num); + ret = aw_get_multi_bin_header_1_0_0(bin); + if (ret < 0) { + return ret; + } + break; + default: + DBG_ERR("aw_bin_parse Unrecognized this bin data type\n"); + return -2; + } + return 0; +} + +/* get the bin's header version */ +static int aw_check_bin_header_version(struct aw_bin *bin) +{ + int ret = 0; + unsigned int header_version = 0; + + header_version = GET_32_DATA(*(bin->p_addr + 7), + *(bin->p_addr + 6), + *(bin->p_addr + 5), *(bin->p_addr + 4)); + + DBG("aw_bin_parse header_version 0x%x\n", header_version); + + /* Write data to the corresponding structure array + according to different formats of the bin frame header version */ + switch (header_version) { + case HEADER_VERSION_1_0_0: + ret = aw_parse_bin_header_1_0_0(bin); + return ret; + default: + DBG_ERR("aw_bin_parse Unrecognized this bin header version \n"); + return -1; + } +} + +int aw_parsing_bin_file(struct aw_bin *bin) +{ + int i = 0; + int ret = 0; + + DBG("aw_bin_parse code version:%s\n", AWINIC_CODE_VERSION); + if (!bin) { + DBG_ERR("aw_bin_parse bin is NULL\n"); + return -8; + } + bin->p_addr = bin->info.data; + bin->all_bin_parse_num = 0; + bin->multi_bin_parse_num = 0; + bin->single_bin_parse_num = 0; + + /* filling bins header info */ + ret = aw_check_bin_header_version(bin); + if (ret < 0) { + DBG_ERR("aw_bin_parse check bin header version error\n"); + return ret; + } + bin->p_addr = NULL; + + /* check bin header info */ + for (i = 0; i < bin->all_bin_parse_num; i++) { + /* check sum */ + ret = aw_check_sum(bin, i); + if (ret < 0) { + DBG_ERR("aw_bin_parse check sum data error\n"); + return ret; + } + /* check bin data version */ + ret = aw_check_data_version(bin, i); + if (ret < 0) { + DBG_ERR("aw_bin_parse check data version error\n"); + return ret; + } + /* check valid data */ + if (bin->header_info[i].bin_data_ver == DATA_VERSION_V1) { + /* check register num */ + if (bin->header_info[i].bin_data_type == + DATA_TYPE_REGISTER) { + ret = aw_check_register_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check register num error\n"); + return ret; + } + /* check dsp reg num */ + } else if (bin->header_info[i].bin_data_type == + DATA_TYPE_DSP_REG) { + ret = aw_check_dsp_reg_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check dsp reg num error\n"); + return ret; + } + /* check soc app num */ + } else if (bin->header_info[i].bin_data_type == + DATA_TYPE_SOC_APP) { + ret = aw_check_soc_app_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check soc app num error\n"); + return ret; + } + } else { + bin->header_info[i].valid_data_len = + bin->header_info[i].bin_data_len; + } + } + } + DBG("aw_bin_parse parsing success\n"); + + return 0; +} diff --git a/techpack/audio/asoc/codecs/aw882xxacf/aw_bin_parse.h b/techpack/audio/asoc/codecs/aw882xxacf/aw_bin_parse.h new file mode 100644 index 000000000000..e8fa6dd84199 --- /dev/null +++ b/techpack/audio/asoc/codecs/aw882xxacf/aw_bin_parse.h @@ -0,0 +1,73 @@ +#ifndef __AW_BIN_PARSE_H__ +#define __AW_BIN_PARSE_H__ + +#define NULL ((void *)0) +#define GET_32_DATA(w, x, y, z) ((unsigned int)((((uint32_t)w) << 24) | (((uint32_t)x) << 16) | (((uint32_t)y) << 8) | ((uint32_t)z))) +#define BIN_NUM_MAX 100 +#define HEADER_LEN 60 +/********************************************************* + * + * header information + * + ********************************************************/ +enum bin_header_version_enum { + HEADER_VERSION_1_0_0 = 0x01000000, +}; + +enum data_type_enum { + DATA_TYPE_REGISTER = 0x00000000, + DATA_TYPE_DSP_REG = 0x00000010, + DATA_TYPE_DSP_CFG = 0x00000011, + DATA_TYPE_SOC_REG = 0x00000020, + DATA_TYPE_SOC_APP = 0x00000021, + DATA_TYPE_MULTI_BINS = 0x00002000, +}; + +enum data_version_enum { + DATA_VERSION_V1 = 0X00000001, /*default little edian */ + DATA_VERSION_MAX, +}; + +struct bin_header_info { + unsigned int header_len; /* Frame header length */ + unsigned int check_sum; /* Frame header information-Checksum */ + unsigned int header_ver; /* Frame header information-Frame header version */ + unsigned int bin_data_type; /* Frame header information-Data type */ + unsigned int bin_data_ver; /* Frame header information-Data version */ + unsigned int bin_data_len; /* Frame header information-Data length */ + unsigned int ui_ver; /* Frame header information-ui version */ + unsigned char chip_type[8]; /* Frame header information-chip type */ + unsigned int reg_byte_len; /* Frame header information-reg byte len */ + unsigned int data_byte_len; /* Frame header information-data byte len */ + unsigned int device_addr; /* Frame header information-device addr */ + unsigned int valid_data_len; /* Length of valid data obtained after parsing */ + unsigned int valid_data_addr; /* The offset address of the valid data obtained after parsing relative to info */ + + unsigned int reg_num; /* The number of registers obtained after parsing */ + unsigned int reg_data_byte_len; /* The byte length of the register obtained after parsing */ + unsigned int download_addr; /* The starting address or download address obtained after parsing */ + unsigned int app_version; /* The software version number obtained after parsing */ +}; + +/************************************************************ +* +* function define +* +************************************************************/ +struct bin_container { + unsigned int len; /* The size of the bin file obtained from the firmware */ + unsigned char data[]; /* Store the bin file obtained from the firmware */ +}; + +struct aw_bin { + unsigned char *p_addr; /* Offset pointer (backward offset pointer to obtain frame header information and important information) */ + unsigned int all_bin_parse_num; /* The number of all bin files */ + unsigned int multi_bin_parse_num; /* The number of single bin files */ + unsigned int single_bin_parse_num; /* The number of multiple bin files */ + struct bin_header_info header_info[BIN_NUM_MAX]; /* Frame header information and other important data obtained after parsing */ + struct bin_container info; /* Obtained bin file data that needs to be parsed */ +}; + +int aw_parsing_bin_file(struct aw_bin *bin); +int aw_parse_bin_header_1_0_0(struct aw_bin *bin); +#endif diff --git a/techpack/audio/asoc/codecs/aw882xxacf/aw_calib.c b/techpack/audio/asoc/codecs/aw882xxacf/aw_calib.c new file mode 100644 index 000000000000..42fb249a8a28 --- /dev/null +++ b/techpack/audio/asoc/codecs/aw882xxacf/aw_calib.c @@ -0,0 +1,2240 @@ +/* + * aw_calibration.c cali_module + * + * + * Copyright (c) 2020 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +/*#define DEBUG*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aw882xx.h" +#include "aw_dsp.h" +#include "aw_log.h" +#include "aw_calib.h" + +static bool is_single_cali = false; /*if mutli_dev cali false, single dev true*/ + +static const char *cali_str[CALI_STR_MAX] = {"none", "start_cali", "cali_re", + "cali_f0", "store_re", "show_re", "show_r0", "show_cali_f0", "show_f0", + "show_te", "show_st", "dev_sel", "get_ver", "get_dev_num" +}; + +static char *ch_name[AW_DEV_CH_MAX] = {"pri_l", "pri_r", "sec_l", "sec_r"}; +static unsigned int g_cali_re_time = AW_CALI_RE_DEFAULT_TIMER; +static unsigned int g_msic_wr_flag = CALI_STR_NONE; +static unsigned int g_dev_select = AW_DEV_CH_PRI_L; +static unsigned int g_cali_status = false; +static struct miscdevice *g_misc_dev = NULL; +static DEFINE_MUTEX(g_cali_lock); + +#ifdef AW_CALI_STORE_EXAMPLE + /*write cali to persist file example*/ +#define AWINIC_CALI_FILE "/mnt/vendor/persist/factory/audio/aw_cali.bin" +#define AW_INT_DEC_DIGIT 10 + +static void aw_fs_read(struct file *file, char *buf, size_t count, loff_t *pos) +{ +#ifdef AW_KERNEL_VER_OVER_5_4_0 + kernel_read(file, buf, count, pos); +#else + vfs_read(file, buf, count, pos); +#endif +} + +static void aw_fs_write(struct file *file, char *buf, size_t count, loff_t *pos) +{ +#ifdef AW_KERNEL_VER_OVER_5_4_0 + kernel_write(file, buf, count, pos); +#else + vfs_write(file, buf, count, pos); +#endif +} + +static int aw_cali_write_cali_re_to_file(int32_t cali_re, int channel) +{ + struct file *fp = NULL; + char buf[50] = {0}; + loff_t pos = 0; + mm_segment_t fs; + + fp = filp_open(AWINIC_CALI_FILE, O_RDWR | O_CREAT, 0644); + if (IS_ERR(fp)) { + pr_err("%s:channel:%d open %s failed!\n", + __func__, channel, AWINIC_CALI_FILE); + return -EINVAL; + } + + pos = AW_INT_DEC_DIGIT * channel; + + snprintf(buf, sizeof(buf), "%10d", cali_re); + + fs = get_fs(); + set_fs(KERNEL_DS); + + aw_fs_write(fp, buf, strlen(buf), &pos); + + set_fs(fs); + + pr_info("%s: channel:%d buf:%s cali_re:%d\n", + __func__, channel, buf, cali_re); + + filp_close(fp, NULL); + return 0; +} + +static int aw_cali_get_read_cali_re(int32_t *cali_re, int channel) +{ + struct file *fp = NULL; + /*struct inode *node;*/ + int f_size; + char *buf = NULL; + int32_t int_cali_re = 0; + loff_t pos = 0; + mm_segment_t fs; + + fp = filp_open(AWINIC_CALI_FILE, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("%s:channel:%d open %s failed!\n", + __func__, channel, AWINIC_CALI_FILE); + return -EINVAL; + } + + pos = AW_INT_DEC_DIGIT * channel; + + /*node = fp->f_dentry->d_inode;*/ + /*f_size = node->i_size;*/ + f_size = AW_INT_DEC_DIGIT; + + buf = kzalloc(f_size + 1, GFP_ATOMIC); + if (!buf) { + pr_err("%s: channel:%d malloc mem %d failed!\n", + __func__, channel, f_size); + filp_close(fp, NULL); + return -EINVAL; + } + + fs = get_fs(); + set_fs(KERNEL_DS); + + aw_fs_read(fp, buf, f_size, &pos); + + set_fs(fs); + + if (sscanf(buf, "%d", &int_cali_re) == 1) + *cali_re = int_cali_re; + else + *cali_re = AW_ERRO_CALI_VALUE; + + pr_info("%s: channel:%d buf:%s int_cali_re: %d\n", + __func__, channel, buf, int_cali_re); + + kfree(buf); + buf = NULL; + filp_close(fp, NULL); + + return 0; +} +#endif + +/*custom need add to set/get cali_re form/to nv*/ +int aw_cali_write_re_to_nvram(int32_t cali_re, int32_t channel) +{ +#ifdef AW_CALI_STORE_EXAMPLE + if (channel >= AW_DEV_CH_MAX) { + pr_err("%s: unsupported channel [%d] \n", __func__, channel); + return -EINVAL; + } + return aw_cali_write_cali_re_to_file(cali_re, channel); +#else + return 0; +#endif +} + +int aw_cali_read_re_from_nvram(int32_t *cali_re, int32_t channel) +{ + /*custom add, if success return value is 0 , else -1*/ +#ifdef AW_CALI_STORE_EXAMPLE + if (channel >= AW_DEV_CH_MAX) { + pr_err("%s: unsupported channel [%d] \n", __func__, channel); + return -EINVAL; + } + return aw_cali_get_read_cali_re(cali_re, channel); +#else + return 0; +#endif +} + +int aw_cali_store_cali_re(struct aw_device *aw_dev, int32_t re) +{ + int ret = 0; + + if ((re >= aw_dev->re_min) && (re <= aw_dev->re_max)) { + aw_dev->cali_desc.cali_re = re; + ret = aw_cali_write_re_to_nvram(re, aw_dev->channel); + if (ret) + aw_dev_err(aw_dev->dev, "write re to nvram failed!"); + } else { + aw_dev_err(aw_dev->dev, "invalid cali re %d !", re); + return -EINVAL; + } + + aw_dev_info(aw_dev->dev, "store cali re is %d", re); + return ret; +} + +/*********************************aw_cali_sevice*********************************************************/ +static void aw_cali_svc_set_cali_status(struct aw_device *aw_dev, int status) +{ + if (status) + g_cali_status = true; + else + g_cali_status = false; + + aw_dev_info(aw_dev->dev, "cali %s", + (status == 0) ? ("disable") : ("enable")); +} + +int aw_cali_svc_get_cali_status(void) +{ + return g_cali_status; +} + +static int aw_cali_svc_dev_get_re(struct aw_device *aw_dev) +{ + int32_t re[AW_CALI_READ_TIMES]; + int ret, i; + int32_t sum = 0; + + for (i = 0; i < AW_CALI_READ_TIMES; i++) { + ret = aw_dsp_read_r0(aw_dev, &re[i]); + if (ret) { + aw_dev_err(aw_dev->dev, "get re failed!"); + return ret; + } + sum += re[i]; + usleep_range(AW_10000_US, AW_10000_US + 10); + } + re[0] = sum / AW_CALI_READ_TIMES; + + aw_dev->cali_desc.cali_re = re[0]; + + /*if ((aw_dev->cali_desc.cali_re >= aw_dev->re_min) && + (aw_dev->cali_desc.cali_re <= aw_dev->re_max)) { + ret = aw_cali_write_re_to_nvram(aw_dev->cali_desc.cali_re, aw_dev->channel); + if (ret) + aw_dev_err(aw_dev->dev, "write re to nvram failed!"); + }*/ + return 0; +} + +static int aw_cali_svc_dev_get_f0(struct aw_device *aw_dev) +{ + int32_t f0[AW_CALI_READ_TIMES]; + int ret, i; + int32_t sum = 0; + + for (i = 0; i < AW_CALI_READ_TIMES; i++) { + ret = aw_dsp_read_f0(aw_dev, &f0[i]); + if (ret) { + aw_dev_err(aw_dev->dev, "get f0 failed!"); + return ret; + } + sum += f0[i]; + usleep_range(AW_10000_US, AW_10000_US + 10); + } + f0[0] = sum / AW_CALI_READ_TIMES; + + aw_dev->cali_desc.cali_f0 = f0[0]; + return 0; +} + +static int aw_cali_svc_dev_get_f0_q(struct aw_device *aw_dev) +{ + int32_t f0[AW_CALI_READ_TIMES], q[AW_CALI_READ_TIMES]; + int ret, i; + int32_t sum_f0 = 0, sum_q = 0; + + for (i = 0; i < AW_CALI_READ_TIMES; i++) { + ret = aw_dsp_read_f0_q(aw_dev, &f0[i], &q[i]); + if (ret) { + aw_dev_err(aw_dev->dev, "get f0 failed!"); + return ret; + } + sum_f0 += f0[i]; + sum_q += q[i]; + usleep_range(AW_10000_US, AW_10000_US + 10); + } + f0[0] = sum_f0 / AW_CALI_READ_TIMES; + q[0] = sum_q / AW_CALI_READ_TIMES; + aw_dev->cali_desc.cali_f0 = f0[0]; + aw_dev->cali_desc.cali_q = q[0]; + return 0; +} + + +static int aw_cali_svc_dev_cali_mode_en(struct aw_device *aw_dev, int type, bool is_enable, unsigned int flag) +{ + int ret; + + /* open cali mode */ + if (is_enable) { + aw_cali_svc_set_cali_status(aw_dev, true); + + if (type == CALI_TYPE_RE) { + if (flag & CALI_OPS_HMUTE) { + ret = aw_dsp_hmute_en(aw_dev, true); + if (ret < 0) + return ret; + } + } else { + if (flag & CALI_OPS_NOISE) { + ret = aw_dsp_noise_en(aw_dev, true); + if (ret < 0) + return ret; + } + } + + ret = aw_dsp_cali_en(aw_dev, true); + if (ret < 0) + return ret; + } else { + aw_dsp_cali_en(aw_dev, false); + + if (type == CALI_TYPE_RE) { + /*close mute*/ + if (flag & CALI_OPS_HMUTE) + aw_dsp_hmute_en(aw_dev, false); + } else { + /*close mute*/ + if (flag & CALI_OPS_NOISE) + aw_dsp_noise_en(aw_dev, false); + } + + /*close cali mode*/ + aw_cali_svc_set_cali_status(aw_dev, false); + } + return 0; +} + +static int aw_cali_svc_dev_cali_re(struct aw_device *aw_dev, unsigned int flag) +{ + int ret; + + ret = aw_cali_svc_dev_cali_mode_en(aw_dev, CALI_TYPE_RE, true, flag); + if (ret < 0) + goto exit; + + /*wait time*/ + msleep(g_cali_re_time); + + /*dev get echo cali re*/ + ret = aw_cali_svc_dev_get_re(aw_dev); + if (ret < 0) + goto exit; +exit: + aw_cali_svc_dev_cali_mode_en(aw_dev, CALI_TYPE_RE, false, flag); + + return ret; +} + +static int aw_cali_svc_devs_cali_re(struct aw_device *aw_dev, unsigned int flag) +{ + struct list_head *dev_list; + struct list_head *pos; + struct aw_device *local_dev; + int ret; + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + /* enable all dev cali mode */ + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + ret = aw_cali_svc_dev_cali_mode_en(local_dev, CALI_TYPE_RE, true, flag); + if (ret < 0) + goto exit; + } + + /* wait time */ + msleep(g_cali_re_time); + + /* dev get echo cali re */ + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + ret = aw_cali_svc_dev_get_re(local_dev); + if (ret < 0) + goto exit; + } + +exit: + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + /* close cali mode */ + aw_cali_svc_dev_cali_mode_en(local_dev, CALI_TYPE_RE, false, flag); + } + + return ret; +} + +static int aw_cali_svc_cali_re(struct aw_device *aw_dev, bool is_single, unsigned int flag) +{ + if (is_single) + return aw_cali_svc_dev_cali_re(aw_dev, flag); + else + return aw_cali_svc_devs_cali_re(aw_dev, flag); + return 0; +} + +static int aw_cali_svc_dev_cali_f0(struct aw_device *aw_dev, unsigned int flag) +{ + int ret; + + ret = aw_cali_svc_dev_cali_mode_en(aw_dev, CALI_TYPE_F0, true, flag); + if (ret < 0) + goto exit; + + /*wait time*/ + msleep(5 * 1000); + + /*dev get echo cali re*/ + ret = aw_cali_svc_dev_get_f0(aw_dev); + if (ret < 0) + goto exit; + +exit: + aw_cali_svc_dev_cali_mode_en(aw_dev, CALI_TYPE_F0, false, flag); + + return ret; +} + +static int aw_cali_svc_devs_cali_f0(struct aw_device *aw_dev, unsigned int flag) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, " get dev list failed"); + return ret; + } + + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + /* set mode status */ + ret = aw_cali_svc_dev_cali_mode_en(local_dev, CALI_TYPE_F0, true, flag); + if (ret < 0) + goto exit; + } + + /*wait time*/ + msleep(5 * 1000); + + /*dev get echo cali re*/ + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + ret = aw_cali_svc_dev_get_f0(local_dev); + if (ret < 0) + goto exit; + } + +exit: + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + /*close cali*/ + aw_cali_svc_dev_cali_mode_en(local_dev, CALI_TYPE_F0, false, flag); + } + + return ret; +} + +static int aw_cali_svc_cali_f0(struct aw_device *aw_dev, bool is_single, unsigned int flag) +{ + if (is_single) + return aw_cali_svc_dev_cali_f0(aw_dev, flag); + else + return aw_cali_svc_devs_cali_f0(aw_dev, flag); + return 0; +} + +static int aw_cali_svc_dev_cali_f0_q(struct aw_device *aw_dev, unsigned int flag) +{ + int ret; + + ret = aw_cali_svc_dev_cali_mode_en(aw_dev, CALI_TYPE_F0, true, flag); + if (ret < 0) + goto exit; + + + /*wait time*/ + msleep(5 * 1000); + + /*dev get echo cali re*/ + ret = aw_cali_svc_dev_get_f0_q(aw_dev); + if (ret < 0) + goto exit; + +exit: + aw_cali_svc_dev_cali_mode_en(aw_dev, CALI_TYPE_F0, false, flag); + + return ret; + +} + +static int aw_cali_svc_devs_cali_f0_q(struct aw_device *aw_dev, unsigned int flag) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed "); + return ret; + } + + + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + /*set mode status*/ + ret = aw_cali_svc_dev_cali_mode_en(local_dev, CALI_TYPE_F0, true, flag); + if (ret < 0) + goto exit; + } + + /*wait time*/ + msleep(5 * 1000); + + /*dev get echo cali re*/ + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + ret = aw_cali_svc_dev_get_f0_q(local_dev); + if (ret < 0) + goto exit; + } + +exit: + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + aw_cali_svc_dev_cali_mode_en(local_dev, CALI_TYPE_F0, false, flag); + } + + return ret; +} + +static int aw_cali_svc_cali_f0_q(struct aw_device *aw_dev, bool is_single, unsigned int flag) +{ + if (is_single) + return aw_cali_svc_dev_cali_f0_q(aw_dev, flag); + else + return aw_cali_svc_devs_cali_f0_q(aw_dev, flag); + + return 0; +} + +static int aw_cali_svc_cali_re_f0(struct aw_device *aw_dev, bool is_single, unsigned int flag) +{ + int ret; + + ret = aw_cali_svc_cali_re(aw_dev, is_single, flag); + if (ret) + return ret; + + ret = aw_cali_svc_cali_f0(aw_dev, is_single, flag); + if (ret) + return ret; + + return 0; +} + +static int aw_cali_svc_cali_re_f0_q(struct aw_device *aw_dev, bool is_single, unsigned int flag) +{ + int ret; + + ret = aw_cali_svc_cali_re(aw_dev, is_single, flag); + if (ret) + return ret; + + ret = aw_cali_svc_cali_f0_q(aw_dev, is_single, flag); + if (ret) + return ret; + + return 0; +} + +int aw_cali_svc_cali_cmd(struct aw_device *aw_dev, int cali_cmd, bool is_single, unsigned int flag) +{ + switch (cali_cmd) { + case AW_CALI_CMD_RE: + return aw_cali_svc_cali_re(aw_dev, is_single, flag); + case AW_CALI_CMD_F0: + return aw_cali_svc_cali_f0(aw_dev, is_single, flag); + case AW_CALI_CMD_F0_Q: + return aw_cali_svc_cali_f0_q(aw_dev, is_single, flag); + case AW_CALI_CMD_RE_F0: + return aw_cali_svc_cali_re_f0(aw_dev, is_single, flag); + case AW_CALI_CMD_RE_F0_Q: + return aw_cali_svc_cali_re_f0_q(aw_dev, is_single, flag); + default: + aw_dev_err(aw_dev->dev, "unsupported cmd %d", cali_cmd); + return -EINVAL; + } + return 0; +} + +int aw_cali_svc_get_devs_cali_re(struct aw_device *aw_dev, int32_t *re_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + re_buf[local_dev->channel] = local_dev->cali_desc.cali_re; + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d]", + local_dev->channel, num); + return -EINVAL; + } + } + + return cnt; +} + +int aw_cali_svc_get_devs_r0(struct aw_device *aw_dev, int32_t *re_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + ret = aw_dsp_read_r0(local_dev, &re_buf[local_dev->channel]); + if (ret) { + aw_dev_err(local_dev->dev, "get re failed!"); + return ret; + } + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d] ", + local_dev->channel, num); + } + } + return cnt; +} + +int aw_cali_svc_get_devs_te(struct aw_device *aw_dev, int32_t *te_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + ret = aw_dsp_read_te(local_dev, &te_buf[local_dev->channel]); + if (ret) { + aw_dev_err(local_dev->dev, "get re failed!"); + return ret; + } + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d]", + local_dev->channel, num); + } + } + + return cnt; +} + +int aw_cali_svc_get_devs_st(struct aw_device *aw_dev, int32_t *st_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /*get dev list*/ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + ret = aw_dsp_read_st(local_dev, &st_buf[local_dev->channel << 1], &st_buf[(local_dev->channel << 1) + 1]); + if (ret) { + aw_dev_err(local_dev->dev, "get re failed!"); + return ret; + } + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d]", + local_dev->channel, num); + } + } + return cnt; +} + +int aw_cali_svc_get_devs_cali_f0(struct aw_device *aw_dev, int32_t *f0_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /*get dev list*/ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + f0_buf[local_dev->channel] = local_dev->cali_desc.cali_f0; + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d]", + local_dev->channel, num); + } + } + + return cnt; +} + +int aw_cali_svc_get_devs_f0(struct aw_device *aw_dev, int32_t *f0_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + ret = aw_dsp_read_f0(local_dev, &f0_buf[local_dev->channel]); + if (ret) { + aw_dev_err(local_dev->dev, "get re failed!"); + return ret; + } + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d]", + local_dev->channel, num); + } + } + return cnt; +} + +int aw_cali_svc_get_devs_cali_f0_q(struct aw_device *aw_dev, + int32_t *f0_buf, int32_t *q_buf, int num) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + + /*get dev list*/ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < num) { + f0_buf[local_dev->channel] = local_dev->cali_desc.cali_f0; + q_buf[local_dev->channel] = local_dev->cali_desc.cali_q; + cnt++; + } else { + aw_dev_err(aw_dev->dev, "channel num[%d] overflow buf num[%d]", + local_dev->channel, num); + } + } + return cnt; +} + +static int aw_cali_svc_set_devs_re_str(struct aw_device *aw_dev, const char *re_str) +{ + struct list_head *dev_list, *pos = NULL; + struct aw_device *local_dev; + int ret, cnt = 0; + int re_data[AW_DEV_CH_MAX] = {0}; + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed"); + return ret; + } + + ret = sscanf(re_str, "pri_l:%d pri_r:%d sec_l:%d sec_r:%d", + &re_data[AW_DEV_CH_PRI_L], + &re_data[AW_DEV_CH_PRI_R], + &re_data[AW_DEV_CH_SEC_L], + &re_data[AW_DEV_CH_SEC_R]); + + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "unsupport str[%s]", re_str); + return ret; + } + + aw_dev_dbg(aw_dev->dev, "pri_l:%d pri_r:%d sec_l:%d sec_r:%d", + re_data[AW_DEV_CH_PRI_L], + re_data[AW_DEV_CH_PRI_R], + re_data[AW_DEV_CH_SEC_L], + re_data[AW_DEV_CH_SEC_R]); + + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel < AW_DEV_CH_MAX) { + ret = aw_cali_store_cali_re(local_dev, re_data[local_dev->channel]); + if (ret < 0) + return ret; + cnt++; + } + } + return cnt; + +} + +static int aw_cali_svc_get_cmd_form_str(struct aw_device *aw_dev, const char *buf) +{ + int i; + + for (i = 0; i < CALI_STR_MAX; i++) { + if (!strncmp(cali_str[i], buf, strlen(cali_str[i]))) { + break; + } + } + + if (i == CALI_STR_MAX) { + aw_dev_err(aw_dev->dev, "supported cmd [%s]!", buf); + return -EINVAL; + } + + aw_dev_info(aw_dev->dev, "find str [%s]", cali_str[i]); + return i; +} + +/*********************************aw_cali_sevice*********************************************************/ + + +/*****************************attr start***************************************************/ +static ssize_t aw_cali_attr_time_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + uint32_t time; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + + ret = kstrtoint(buf, 0, &time); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "read buf %s failed\n", + buf); + return ret; + } + + if (time < 400) { + aw_dev_err(aw_dev->dev, "time:%d is too short, no set", + time); + return -EINVAL; + } + + g_cali_re_time = time; + aw_dev_dbg(aw_dev->dev, "time:%d", time); + + return count; +} + +static ssize_t aw_cali_attr_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "time: %d \n", g_cali_re_time); + + return len; +} + +static ssize_t aw_cali_attr_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + int ret; + + ret = aw_cali_svc_get_cmd_form_str(aw_dev, buf); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "supported cmd [%s]!", buf); + return -EPERM; + } + + if (ret == CALI_STR_CALI_RE_F0) { + aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_RE_F0, + is_single_cali, CALI_OPS_HMUTE|CALI_OPS_NOISE); + return count; + } else if (ret == CALI_STR_CALI_RE) { + aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_RE, + is_single_cali, CALI_OPS_HMUTE); + return count; + } else if (ret == CALI_STR_CALI_F0) { + aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_F0, + is_single_cali, CALI_OPS_NOISE); + return count; + } else { + aw_dev_err(aw_dev->dev, "supported cmd [%s]!", buf); + } + + return -EPERM; + +} + +static ssize_t aw_cali_attr_re_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + int ret; + int data = 0; + + if (is_single_cali) { + ret = kstrtoint(buf, 0, &data); + if (ret < 0) { + aw_dev_err(aw882xx->dev, " read buf %s failed", buf); + return ret; + } + ret = aw_cali_store_cali_re(aw_dev, data); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "set re %d failed", data); + return -EPERM; + } + } else { + ret = aw_cali_svc_set_devs_re_str(aw_dev, buf); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "set re str %s failed", buf); + return -EPERM; + } + } + + return count; +} + +static ssize_t aw_cali_attr_re_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, i; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + int32_t cali_re[AW_DEV_CH_MAX] = {0}; + + ret = aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_RE, is_single_cali, CALI_OPS_HMUTE); + if (ret < 0) { + len += snprintf(buf+len, PAGE_SIZE-len, "cali Re cmd failed\n"); + return len; + } + + if (is_single_cali) { + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", aw_dev->cali_desc.cali_re); + } else { + ret = aw_cali_svc_get_devs_cali_re(aw_dev, cali_re, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get re failed "); + len += snprintf(buf+len, PAGE_SIZE-len, "get re failed\n"); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d mOhms ", ch_name[i], cali_re[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + } + + return len; +} + +static ssize_t aw_cali_attr_f0_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + + aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_F0, is_single_cali, CALI_OPS_NOISE); + return count; +} + +static ssize_t aw_cali_attr_f0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, i; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + int32_t cali_f0[AW_DEV_CH_MAX] = {0}; + + ret = aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_F0, is_single_cali, CALI_OPS_NOISE); + if (ret < 0) { + len += snprintf(buf+len, PAGE_SIZE-len, "cali f0 cmd failed\n"); + return len; + } + + if (is_single_cali) { + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", aw_dev->cali_desc.cali_f0); + } else { + ret = aw_cali_svc_get_devs_cali_f0(aw_dev, cali_f0, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get f0 failed "); + len += snprintf(buf+len, PAGE_SIZE-len, "get f0 failed \n"); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d ", ch_name[i], cali_f0[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + } + + return len; +} + +static ssize_t aw_cali_attr_show_re(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, i; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + int32_t cali_re[AW_DEV_CH_MAX] = {0}; + + if (is_single_cali) { + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", aw_dev->cali_desc.cali_re); + } else { + ret = aw_cali_svc_get_devs_cali_re(aw_dev, cali_re, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get re failed "); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d mOhms ", ch_name[i], cali_re[i]); + } + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + + return len; +} + +static ssize_t aw_cali_attr_show_f0(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret, i; + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + int32_t cali_f0[AW_DEV_CH_MAX] = {0}; + + if (is_single_cali) { + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", aw_dev->cali_desc.cali_f0); + } else { + ret = aw_cali_svc_get_devs_cali_f0(aw_dev, cali_f0, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get f0 failed "); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d ", ch_name[i], cali_f0[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + } + + return len; +} + +/*set cali time*/ +static DEVICE_ATTR(cali_time, S_IWUSR | S_IRUGO, + aw_cali_attr_time_show, aw_cali_attr_time_store); +/*start cali*/ +static DEVICE_ATTR(cali, S_IWUSR, + NULL, aw_cali_attr_store); +/*cali re*/ +static DEVICE_ATTR(cali_re, S_IRUGO | S_IWUSR, + aw_cali_attr_re_show, aw_cali_attr_re_store); +/*cali_f0*/ +static DEVICE_ATTR(cali_f0, S_IRUGO | S_IWUSR, + aw_cali_attr_f0_show, aw_cali_attr_f0_store); +/*show cali_re*/ +static DEVICE_ATTR(re_show, S_IRUGO, + aw_cali_attr_show_re, NULL); +/*show cali_f0*/ +static DEVICE_ATTR(f0_show, S_IRUGO, + aw_cali_attr_show_f0, NULL); + + +static struct attribute *aw_cali_attr[] = { + &dev_attr_cali_time.attr, + &dev_attr_cali.attr, + &dev_attr_cali_re.attr, + &dev_attr_cali_f0.attr, + &dev_attr_re_show.attr, + &dev_attr_f0_show.attr, + NULL +}; + +static struct attribute_group aw_cali_attr_group = { + .attrs = aw_cali_attr +}; + +static void aw_cali_attr_init(struct aw_device *aw_dev) +{ + int ret; + + aw_dev_info(aw_dev->dev, "enter"); + + ret = sysfs_create_group(&aw_dev->dev->kobj, &aw_cali_attr_group); + if (ret < 0) + aw_dev_info(aw_dev->dev, "error creating sysfs cali attr files"); +} + +static void aw_cali_attr_deinit(struct aw_device *aw_dev) +{ + sysfs_remove_group(&aw_dev->dev->kobj, &aw_cali_attr_group); + aw_dev_info(aw_dev->dev, "attr files deinit"); +} + +/*****************************attr end***************************************************/ + +/*****************************class node******************************************************/ +static ssize_t aw_cali_class_time_show(struct class *class, struct class_attribute *attr, char *buf) +{ + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "time: %d \n", g_cali_re_time); + + return len; +} + +static ssize_t aw_cali_class_time_store(struct class *class, + struct class_attribute *attr, const char *buf, size_t len) +{ + int ret; + uint32_t time; + + ret = kstrtoint(buf, 0, &time); + if (ret < 0) { + pr_err("[Awinic] %s, read buf %s failed\n", + __func__, buf); + return ret; + } + + if (time < 400) { + pr_err("[Awinic] %s:time:%d is too short, no set\n", + __func__, time); + return -EINVAL; + } + + g_cali_re_time = time; + pr_debug("%s:time:%d\n", __func__, time); + + return len; +} + +static ssize_t aw_cali_class_cali_re_show(struct class *class, struct class_attribute *attr, char *buf) +{ + struct list_head *dev_list; + struct aw_device *local_dev; + int ret, i; + ssize_t len = 0; + int32_t cali_re[AW_DEV_CH_MAX] = {0}; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + return ret; + } + + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + + ret = aw_cali_svc_cali_cmd(local_dev, AW_CALI_CMD_RE, false, CALI_OPS_HMUTE); + if (ret < 0) { + len += snprintf(buf+len, PAGE_SIZE-len, "cali Re cmd failed\n"); + return len; + } + + ret = aw_cali_svc_get_devs_cali_re(local_dev, cali_re, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(local_dev->dev, "get re failed "); + len += snprintf(buf+len, PAGE_SIZE-len, "get re failed \n"); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d mOhms ", ch_name[i], cali_re[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + + return len; +} + +static ssize_t aw_cali_class_cali_re_store(struct class *class, + struct class_attribute *attr, const char *buf, size_t len) +{ + struct list_head *dev_list; + struct aw_device *local_dev; + int ret; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + return ret; + } + + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + + ret = aw_cali_svc_set_devs_re_str(local_dev, buf); + if (ret <= 0) { + aw_dev_err(local_dev->dev, "set re str %s failed", buf); + return -EPERM; + } + + return len; +} + +static ssize_t aw_cali_class_cali_f0_show(struct class *class, struct class_attribute *attr, char *buf) +{ + struct list_head *dev_list; + struct aw_device *local_dev; + int ret, i; + ssize_t len = 0; + int32_t cali_f0[AW_DEV_CH_MAX] = {0}; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + return ret; + } + + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + + ret = aw_cali_svc_cali_cmd(local_dev, AW_CALI_CMD_F0, false, CALI_OPS_NOISE); + if (ret < 0) { + len += snprintf(buf+len, PAGE_SIZE-len, "cali f0 cmd failed\n"); + return len; + } + + ret = aw_cali_svc_get_devs_cali_f0(local_dev, cali_f0, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(local_dev->dev, "get f0 failed "); + len += snprintf(buf+len, PAGE_SIZE-len, "get f0 failed \n"); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d ", ch_name[i], cali_f0[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + + return len; +} + +static ssize_t aw_cali_class_cali_f0_store(struct class *class, + struct class_attribute *attr, const char *buf, size_t len) +{ + struct list_head *dev_list; + struct aw_device *local_dev; + int ret; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + return ret; + } + + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + + aw_cali_svc_cali_cmd(local_dev, AW_CALI_CMD_F0, false, CALI_OPS_NOISE); + + return len; +} + +static ssize_t aw_cali_class_f0_show(struct class *class, struct class_attribute *attr, char *buf) +{ + struct list_head *dev_list; + struct aw_device *local_dev; + int ret, i; + ssize_t len = 0; + int32_t cali_f0[AW_DEV_CH_MAX] = {0}; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + return ret; + } + + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + + ret = aw_cali_svc_get_devs_cali_f0(local_dev, cali_f0, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(local_dev->dev, "get re failed "); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d ", ch_name[i], cali_f0[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + + return len; +} + +static ssize_t aw_cali_class_re_show(struct class *class, struct class_attribute *attr, char *buf) +{ + struct list_head *dev_list; + struct aw_device *local_dev; + int ret, i; + ssize_t len = 0; + int32_t cali_re[AW_DEV_CH_MAX] = {0}; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + return ret; + } + + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + + ret = aw_cali_svc_get_devs_cali_re(local_dev, cali_re, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(local_dev->dev, "get re failed "); + } else { + for (i = 0; i < ret; i++) + len += snprintf(buf+len, PAGE_SIZE-len, "%s:%d mOhms ", ch_name[i], cali_re[i]); + len += snprintf(buf+len, PAGE_SIZE-len, " \n"); + } + + return len; +} + +static struct class_attribute class_attr_cali_time = \ + __ATTR(cali_time, S_IWUSR | S_IRUGO, \ + aw_cali_class_time_show, aw_cali_class_time_store); + +static struct class_attribute class_attr_re25_calib = \ + __ATTR(re25_calib, S_IWUSR | S_IRUGO, \ + aw_cali_class_cali_re_show, aw_cali_class_cali_re_store); + +static struct class_attribute class_attr_f0_calib = \ + __ATTR(f0_calib, S_IWUSR | S_IRUGO, \ + aw_cali_class_cali_f0_show, aw_cali_class_cali_f0_store); + +static struct class_attribute class_attr_re_show = \ + __ATTR(re_show, S_IRUGO, \ + aw_cali_class_re_show, NULL); + +static struct class_attribute class_attr_f0_show = \ + __ATTR(f0_show, S_IRUGO, \ + aw_cali_class_f0_show, NULL); + + +static struct class aw_cali_class = { + .name = "smartpa", + .owner = THIS_MODULE, +}; + +static void aw_cali_class_attr_init(struct aw_device *aw_dev) +{ + int ret; + + if (aw_dev->index != 0) { + aw_dev_info(aw_dev->dev, "class node already register"); + return; + } + + ret = class_register(&aw_cali_class); + if (ret < 0) { + aw_dev_info(aw_dev->dev, "error creating class node"); + return; + } + ret = class_create_file(&aw_cali_class, &class_attr_re25_calib); + if (ret) { + aw_dev_info(aw_dev->dev, "creat class_attr_re25_calib fail"); + } + ret = class_create_file(&aw_cali_class, &class_attr_f0_calib); + if (ret) { + aw_dev_info(aw_dev->dev, "creat class_attr_re25_calib fail"); + } + ret = class_create_file(&aw_cali_class, &class_attr_cali_time); + if (ret) { + aw_dev_info(aw_dev->dev, "creat class_attr_cali_time fail"); + } + ret = class_create_file(&aw_cali_class, &class_attr_re_show); + if (ret) { + aw_dev_info(aw_dev->dev, "creat class_attr_re_show fail"); + } + ret = class_create_file(&aw_cali_class, &class_attr_f0_show); + if (ret) { + aw_dev_info(aw_dev->dev, "creat class_attr_f0_show fail"); + } +} + +static void aw_cali_class_attr_deinit(struct aw_device *aw_dev) +{ + class_remove_file(&aw_cali_class, &class_attr_re25_calib); + class_remove_file(&aw_cali_class, &class_attr_f0_calib); + class_remove_file(&aw_cali_class, &class_attr_cali_time); + class_remove_file(&aw_cali_class, &class_attr_re_show); + class_remove_file(&aw_cali_class, &class_attr_f0_show); + + class_unregister(&aw_cali_class); + aw_dev_info(aw_dev->dev, "unregister class node"); +} +/*****************************class node******************************************************/ + +/*****************************misc node******************************************************/ +static int aw_cali_misc_open(struct inode *inode, struct file *file) +{ + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + pr_err("[Awinic] %s: get dev list failed \n", __func__); + file->private_data = NULL; + return -EINVAL; + } + + /* find select dev */ + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel == g_dev_select) { + break; + } + } + + /* cannot find sel dev, use list first dev */ + if (local_dev->channel != g_dev_select) { + local_dev = list_first_entry(dev_list, struct aw_device, list_node); + aw_dev_dbg(local_dev->dev, "can not find [%s], use default", ch_name[g_dev_select]); + } + + file->private_data = (void *)local_dev; + + aw_dev_dbg(local_dev->dev, "misc open success\n"); + return 0; +} + +static int aw_cali_misc_release(struct inode *inode, struct file *file) +{ + file->private_data = (void *)NULL; + + pr_debug("misc release successi\n"); + return 0; +} + +static int aw_cali_misc_params_ptr(struct aw_device *aw_dev, struct ptr_params_data *p_params) +{ + char *p_data = NULL; + int ret = 0; + + if (p_params->data == NULL || (!p_params->len)) { + aw_dev_err(aw_dev->dev, "p_params error"); + ret = -EFAULT; + return ret; + } + + p_data = kzalloc(p_params->len, GFP_KERNEL); + if (p_data == NULL) { + aw_dev_err(aw_dev->dev, "error allocating memory"); + ret = -ENOMEM; + goto exit; + } + + if (copy_from_user(p_data, + (void __user *)p_params->data, + p_params->len)) { + ret = -EFAULT; + goto exit; + } + + ret = aw_dsp_write_params(aw_dev, p_data, p_params->len); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "write params failed "); + ret = -EFAULT; + goto exit; + } + +exit: + if (p_data != NULL) { + kfree(p_data); + p_data = NULL; + } + + return ret; +} + +static int aw_cali_misc_ops_write(struct aw_device *aw_dev, + unsigned int cmd, unsigned long arg) +{ + unsigned int data_len = _IOC_SIZE(cmd); + char *data_ptr = NULL; + int32_t data = 0; + int ret = 0; + + if (aw_dev->cali_desc.mode == AW_CALI_MODE_NONE) { + aw_dev_info(aw_dev->dev, "cali mode is none, can't support misc ops write"); + return -EINVAL; + } + + data_ptr = kzalloc(data_len, GFP_KERNEL); + if (!data_ptr) { + aw_dev_err(aw_dev->dev, "malloc failed !"); + return -ENOMEM; + } + + if (copy_from_user(data_ptr, (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + + switch (cmd) { + case AW_IOCTL_ENABLE_CALI: { + aw_cali_svc_set_cali_status(aw_dev, data_ptr[0]); + } break; + case AW_IOCTL_SET_CALI_CFG: { + ret = aw_dsp_write_cali_cfg(aw_dev, data_ptr, data_len); + } break; + case AW_IOCTL_SET_NOISE: { + data = *(int32_t *)data_ptr; + ret = aw_dsp_noise_en(aw_dev, data); + } break; + case AW_IOCTL_SET_VMAX: { + ret = aw_dsp_write_vmax(aw_dev, data_ptr, data_len); + } break; + case AW_IOCTL_SET_PARAM: { + ret = aw_dsp_write_params(aw_dev, data_ptr, data_len); + } break; + case AW_IOCTL_SET_PTR_PARAM_NUM: { + ret = aw_cali_misc_params_ptr(aw_dev, (struct ptr_params_data *)data_ptr); + } break; + case AW_IOCTL_SET_CALI_RE: { + ret = aw_cali_store_cali_re(aw_dev, *((int32_t *)data_ptr)); + /*ret = aw_dsp_write_cali_re(aw_dev, *(int32_t *)data_ptr);*/ + } break; + case AW_IOCTL_SET_DSP_HMUTE: { + data = *(int32_t *)data_ptr; + ret = aw_dsp_hmute_en(aw_dev, data); + } break; + case AW_IOCTL_SET_CALI_CFG_FLAG: { + data = *(int32_t *)data_ptr; + /*aw_cali_svc_set_cali_status(aw_dev, data);*/ + ret = aw_dsp_cali_en(aw_dev, data); + } break; + default:{ + aw_dev_err(aw_dev->dev, "unsupported cmd %d", cmd); + ret = -EINVAL; + } break; + } + +exit: + kfree(data_ptr); + return ret; +} + +static int aw_cali_misc_ops_read(struct aw_device *aw_dev, + unsigned int cmd, unsigned long arg) +{ + int16_t data_len = _IOC_SIZE(cmd); + char *data_ptr = NULL; + int32_t *data_32_ptr = NULL; + int ret = 0; + + if (aw_dev->cali_desc.mode == AW_CALI_MODE_NONE) { + aw_dev_info(aw_dev->dev, "cali mode is none, can't support misc ops read"); + return -EINVAL; + } + + data_ptr = kzalloc(data_len, GFP_KERNEL); + if (!data_ptr) { + aw_dev_err(aw_dev->dev, "malloc failed !"); + return -ENOMEM; + } + + switch (cmd) { + case AW_IOCTL_GET_CALI_CFG: { + ret = aw_dsp_read_cali_cfg(aw_dev, data_ptr, data_len); + } break; + case AW_IOCTL_GET_CALI_DATA: { + ret = aw_dsp_read_cali_data(aw_dev, data_ptr, data_len); + } break; + case AW_IOCTL_GET_F0: { + data_32_ptr = (int32_t *)data_ptr; + ret = aw_dsp_read_f0(aw_dev, data_32_ptr); + } break; + case AW_IOCTL_GET_CALI_RE: { + data_32_ptr = (int32_t *)data_ptr; + ret = aw_dsp_read_cali_re(aw_dev, data_32_ptr); + } break; + case AW_IOCTL_GET_VMAX: { + ret = aw_dsp_read_vmax(aw_dev, data_ptr, data_len); + } break; + case AW_IOCTL_GET_F0_Q: { + data_32_ptr = (int32_t *)data_ptr; + ret = aw_dsp_read_f0_q(aw_dev, &data_32_ptr[0], &data_32_ptr[1]); + } break; + default:{ + aw_dev_err(aw_dev->dev, "unsupported cmd %d", cmd); + ret = -EINVAL; + } break; + } + + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + } + + kfree(data_ptr); + return ret; +} + +static int aw_cali_misc_read_dsp(struct aw_device *aw_dev, aw_ioctl_msg_t *msg) +{ + char __user *user_data = (char __user *)msg->data_buf; + uint32_t dsp_msg_id = (uint32_t)msg->opcode_id; + int data_len = msg->data_len; + int ret; + char *data_ptr; + + data_ptr = kzalloc(data_len, GFP_KERNEL); + if (!data_ptr) { + aw_dev_err(aw_dev->dev, "malloc failed !"); + return -ENOMEM; + } + + ret = aw_dsp_read_msg(aw_dev, dsp_msg_id, data_ptr, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, " write failed"); + goto exit; + } + + if (copy_to_user((void __user *)user_data, + data_ptr, data_len)) + ret = -EFAULT; +exit: + kfree(data_ptr); + return ret; +} + +static int aw_cali_misc_write_dsp(struct aw_device *aw_dev, aw_ioctl_msg_t *msg) +{ + char __user *user_data = (char __user *)msg->data_buf; + uint32_t dsp_msg_id = (uint32_t)msg->opcode_id; + int data_len = msg->data_len; + int ret; + char *data_ptr; + + data_ptr = kzalloc(data_len, GFP_KERNEL); + if (!data_ptr) { + aw_dev_err(aw_dev->dev, "malloc failed !\n"); + return -ENOMEM; + } + + if (copy_from_user(data_ptr, (void __user *)user_data, data_len)) { + aw_dev_err(aw_dev->dev, "copy data failed"); + ret = -EFAULT; + goto exit; + } + + ret = aw_dsp_write_msg(aw_dev, dsp_msg_id, data_ptr, data_len); + if (ret) + aw_dev_err(aw_dev->dev, "write failed"); + +exit: + kfree(data_ptr); + return ret; +} + + +static int aw_cali_misc_ops(struct aw_device *aw_dev, + unsigned int cmd, unsigned long arg); +static int aw_cali_misc_ops_msg(struct aw_device *aw_dev, unsigned long arg) +{ + aw_ioctl_msg_t ioctl_msg; + + if (copy_from_user(&ioctl_msg, (void __user *)arg, sizeof(aw_ioctl_msg_t))) + return -EFAULT; + + if (ioctl_msg.version != AW_IOCTL_MSG_VERSION) { + aw_dev_err(aw_dev->dev, "unsupported msg version %d", ioctl_msg.version); + return -EINVAL; + } + + if (ioctl_msg.type == AW_IOCTL_MSG_RD_DSP) { + return aw_cali_misc_read_dsp(aw_dev, &ioctl_msg); + } else if (ioctl_msg.type == AW_IOCTL_MSG_WR_DSP) { + return aw_cali_misc_write_dsp(aw_dev, &ioctl_msg); + } else if (ioctl_msg.type == AW_IOCTL_MSG_IOCTL) { + return aw_cali_misc_ops(aw_dev, ioctl_msg.opcode_id, (unsigned long)ioctl_msg.data_buf); + } else { + aw_dev_err(aw_dev->dev, "unsupported msg type %d", ioctl_msg.type); + return -EINVAL; + } +} + +static int aw_cali_misc_ops(struct aw_device *aw_dev, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + switch (cmd) { + case AW_IOCTL_ENABLE_CALI: + case AW_IOCTL_SET_CALI_CFG: + case AW_IOCTL_SET_NOISE: + case AW_IOCTL_SET_VMAX: + case AW_IOCTL_SET_PARAM: + case AW_IOCTL_SET_PTR_PARAM_NUM: + case AW_IOCTL_SET_CALI_RE: + case AW_IOCTL_SET_DSP_HMUTE: + case AW_IOCTL_SET_CALI_CFG_FLAG: + ret = aw_cali_misc_ops_write(aw_dev, cmd, arg); + break; + case AW_IOCTL_GET_CALI_CFG: + case AW_IOCTL_GET_CALI_DATA: + case AW_IOCTL_GET_F0: + case AW_IOCTL_GET_CALI_RE: + case AW_IOCTL_GET_VMAX: + case AW_IOCTL_GET_F0_Q: + ret = aw_cali_misc_ops_read(aw_dev, cmd, arg); + break; + case AW_IOCTL_MSG: { + ret = aw_cali_misc_ops_msg(aw_dev, arg); + } break; + default: + aw_dev_err(aw_dev->dev, "unsupported cmd %d", cmd); + ret = -EINVAL; + break; + } + + return ret; +} + +static long aw_cali_misc_unlocked_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct aw_device *aw_dev = NULL; + + if (((_IOC_TYPE(cmd)) != (AW_IOCTL_MAGIC))) { + aw_dev_err(aw_dev->dev, " cmd magic err"); + return -EINVAL; + } + aw_dev = (struct aw_device *)file->private_data; + ret = aw_cali_misc_ops(aw_dev, cmd, arg); + if (ret) + return -EINVAL; + + return 0; +} + +#ifdef CONFIG_COMPAT +static long aw_cali_misc_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct aw_device *aw_dev = NULL; + + if (((_IOC_TYPE(cmd)) != (AW_IOCTL_MAGIC))) { + aw_dev_err(aw_dev->dev, "cmd magic err"); + return -EINVAL; + } + aw_dev = (struct aw_device *)file->private_data; + ret = aw_cali_misc_ops(aw_dev, cmd, arg); + if (ret) + return -EINVAL; + + return 0; +} +#endif + +static ssize_t aw_cali_misc_read(struct file *filp, char __user *buf, size_t size, loff_t *pos) +{ + int len = 0; + int i, ret; + struct aw_device *aw_dev = (struct aw_device *)filp->private_data; + char local_buf[128] = { 0 }; + unsigned int dev_num; + int32_t temp_data[AW_DEV_CH_MAX << 1] = {0}; + + aw_dev_info(aw_dev->dev, "enter"); + + if (aw_dev->cali_desc.mode == AW_CALI_MODE_NONE) { + aw_dev_info(aw_dev->dev, "cali mode is none, can't support misc read"); + return -EINVAL; + } + + if (*pos) { + *pos = 0; + return 0; + } + + switch (g_msic_wr_flag) { + case CALI_STR_SHOW_RE: { + ret = aw_cali_svc_get_devs_cali_re(aw_dev, temp_data, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get cali_re failed"); + return ret; + } else { + for (i = 0; i < ret; i++) + len += snprintf(local_buf+len, sizeof(local_buf)-len, + "%s:%d mOhms ", ch_name[i], temp_data[i]); + + len += snprintf(local_buf+len, sizeof(local_buf)-len, "\n"); + } + } break; + case CALI_STR_SHOW_CALI_F0: { + ret = aw_cali_svc_get_devs_cali_f0(aw_dev, temp_data, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get cali f0 failed"); + return ret; + } else { + for (i = 0; i < ret; i++) + len += snprintf(local_buf+len, sizeof(local_buf)-len, + "%s:%d ", ch_name[i], temp_data[i]); + + len += snprintf(local_buf+len, sizeof(local_buf)-len, "\n"); + } + } break; + case CALI_STR_SHOW_R0: { + ret = aw_cali_svc_get_devs_r0(aw_dev, temp_data, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get r0 failed"); + return ret; + } else { + for (i = 0; i < ret; i++) + len += snprintf(local_buf+len, sizeof(local_buf)-len, + "%s:%d mOhms ", ch_name[i], temp_data[i]); + len += snprintf(local_buf+len, sizeof(local_buf)-len, "\n"); + } + } break; + case CALI_STR_SHOW_TE: { + ret = aw_cali_svc_get_devs_te(aw_dev, temp_data, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get te failed"); + return ret; + } else { + for (i = 0; i < ret; i++) + len += snprintf(local_buf+len, sizeof(local_buf)-len, + "%s:%d ", ch_name[i], temp_data[i]); + len += snprintf(local_buf+len, sizeof(local_buf)-len, "\n"); + } + } break; + case CALI_STR_SHOW_ST: { + ret = aw_cali_svc_get_devs_st(aw_dev, temp_data, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get spkr status failed"); + return ret; + } else { + for (i = 0; i < ret; i++) { + len += snprintf(local_buf+len, sizeof(local_buf)-len, + "%s:R0 %d mOhms Te %d ", + ch_name[i], temp_data[i << 1], temp_data[(i << 1) + 1]); + } + len += snprintf(local_buf+len, sizeof(local_buf)-len, "\n"); + } + } break; + case CALI_STR_SHOW_F0: { + ret = aw_cali_svc_get_devs_f0(aw_dev, temp_data, AW_DEV_CH_MAX); + if (ret <= 0) { + aw_dev_err(aw_dev->dev, "get f0 failed"); + return ret; + } else { + for (i = 0; i < ret; i++) + len += snprintf(local_buf+len, sizeof(local_buf)-len, + "%s:%d ", ch_name[i], temp_data[i]); + + len += snprintf(local_buf+len, sizeof(local_buf) - len, "\n"); + } + } break; + case CALI_STR_VER: { + if (aw_dev->ops.aw_get_version) { + len = aw_dev->ops.aw_get_version(local_buf, sizeof(local_buf)); + if (len < 0) { + aw_dev_err(aw_dev->dev, "get version failed"); + return -EINVAL; + } + len += snprintf(local_buf+len, sizeof(local_buf) - len, "\n"); + } else { + aw_dev_err(aw_dev->dev, "get version is NULL"); + return -EINVAL; + } + } break; + case CALI_STR_DEV_NUM: { + if (aw_dev->ops.aw_get_dev_num) { + dev_num = aw_dev->ops.aw_get_dev_num(); + len += snprintf(local_buf + len, sizeof(local_buf) - len, "dev_num:%d\n", dev_num); + } else { + aw_dev_err(aw_dev->dev, "get dev num is NULL"); + return -EINVAL; + } + break; + } + default: { + if (g_msic_wr_flag == CALI_STR_NONE) { + aw_dev_info(aw_dev->dev, "please write cmd first"); + return -EINVAL; + } else { + aw_dev_err(aw_dev->dev, "unsupported flag [%d]", g_msic_wr_flag); + g_msic_wr_flag = CALI_STR_NONE; + return -EINVAL; + } + } break; + } + + if (copy_to_user((void __user *)buf, local_buf, len)) { + aw_dev_err(aw_dev->dev, "copy_to_user error"); + g_msic_wr_flag = CALI_STR_NONE; + return -EFAULT; + } + + g_msic_wr_flag = CALI_STR_NONE; + *pos += len; + return len; +} + +static int aw_cali_misc_switch_dev(struct file *filp, struct aw_device *aw_dev, char *cmd_buf) +{ + int i; + char dev_select[50]; + struct list_head *dev_list; + struct list_head *pos = NULL; + struct aw_device *local_dev; + int ret; + + /* get sel dev str */ + sscanf(cmd_buf, "dev_sel:%s", dev_select); + + for (i = 0; i < AW_DEV_CH_MAX; i++) { + if (strnstr(dev_select, ch_name[i], strlen(ch_name[i]))) + break; + } + + if (i == AW_DEV_CH_MAX) { + aw_dev_err(aw_dev->dev, "unsupport dev [%s]", dev_select); + return -EINVAL; + } + + /* get dev list */ + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed "); + return ret; + } + + /* find sel dev */ + list_for_each(pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + if (local_dev->channel == i) { + filp->private_data = (void *)local_dev; + g_dev_select = i; + aw_dev_info(local_dev->dev, "switch dev to [%s]", ch_name[i]); + return 0; + } + } + aw_dev_err(aw_dev->dev, " unsupport [%s]", dev_select); + return -EINVAL; +} + +static ssize_t aw_cali_misc_write(struct file *filp, const char __user *buf, size_t size, loff_t *pos) +{ + char *kernel_buf = NULL; + struct aw_device *aw_dev = (struct aw_device *)filp->private_data; + int ret = 0; + + aw_dev_info(aw_dev->dev, "enter, write size:%d", (int)size); + + if (aw_dev->cali_desc.mode == AW_CALI_MODE_NONE) { + aw_dev_info(aw_dev->dev, "cali mode is none, can't support misc write"); + return -EINVAL; + } + + kernel_buf = kzalloc(size + 1, GFP_KERNEL); + if (kernel_buf == NULL) { + aw_dev_err(aw_dev->dev, "kzalloc failed !"); + return -ENOMEM; + } + + if (copy_from_user(kernel_buf, + (void __user *)buf, + size)) { + ret = -EFAULT; + goto exit; + } + + ret = aw_cali_svc_get_cmd_form_str(aw_dev, kernel_buf); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "upported cmd [%s]! ", kernel_buf); + ret = -EINVAL; + goto exit; + } + + switch (ret) { + case CALI_STR_CALI_RE_F0: { + ret = aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_RE_F0, + is_single_cali, CALI_OPS_HMUTE|CALI_OPS_NOISE); + } break; + case CALI_STR_CALI_RE: { + ret = aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_RE, + is_single_cali, CALI_OPS_HMUTE); + } break; + case CALI_STR_CALI_F0: { + ret = aw_cali_svc_cali_cmd(aw_dev, AW_CALI_CMD_F0, + is_single_cali, CALI_OPS_HMUTE|CALI_OPS_NOISE); + } break; + case CALI_STR_SET_RE: { + /*skip store_re*/ + ret = aw_cali_svc_set_devs_re_str(aw_dev, + kernel_buf + strlen(cali_str[CALI_STR_SET_RE]) + 1); + } break; + case CALI_STR_DEV_SEL: { + ret = aw_cali_misc_switch_dev(filp, aw_dev, kernel_buf); + } break; + case CALI_STR_SHOW_RE: /*show cali_re*/ + case CALI_STR_SHOW_R0: /*show real r0*/ + case CALI_STR_SHOW_CALI_F0: /*GET DEV CALI_F0*/ + case CALI_STR_SHOW_F0: /*SHOW REAL F0*/ + case CALI_STR_SHOW_TE: + case CALI_STR_SHOW_ST: + case CALI_STR_VER: + case CALI_STR_DEV_NUM: { + g_msic_wr_flag = ret; + ret = 0; + } break; + default: { + aw_dev_err(aw_dev->dev, "unsupported [%s]! ", kernel_buf); + ret = -EINVAL; + } break; + }; + +exit: + aw_dev_info(aw_dev->dev, "cmd [%s]! ", kernel_buf); + if (kernel_buf) { + kfree(kernel_buf); + kernel_buf = NULL; + } + if (ret < 0) + return -EINVAL; + else + return size; +} + +static const struct file_operations aw_cali_misc_fops = { + .owner = THIS_MODULE, + .open = aw_cali_misc_open, + .read = aw_cali_misc_read, + .write = aw_cali_misc_write, + .release = aw_cali_misc_release, + .unlocked_ioctl = aw_cali_misc_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = aw_cali_misc_compat_ioctl, +#endif + +}; + +struct miscdevice misc_cali = { + .name = "aw882xx_smartpa", + .minor = MISC_DYNAMIC_MINOR, + .fops = &aw_cali_misc_fops, +}; + +static int aw_cali_misc_init(struct aw_device *aw_dev) +{ + int ret; + + mutex_lock(&g_cali_lock); + if (g_misc_dev == NULL) { + ret = misc_register(&misc_cali); + if (ret) { + aw_dev_err(aw_dev->dev, "misc register fail: %d\n", ret); + mutex_unlock(&g_cali_lock); + return -EINVAL; + } + g_misc_dev = &misc_cali; + aw_dev_dbg(aw_dev->dev, "misc register success"); + } else { + aw_dev_dbg(aw_dev->dev, "misc already register"); + } + mutex_unlock(&g_cali_lock); + + return 0; +} + +static void aw_cali_misc_deinit(struct aw_device *aw_dev) +{ + mutex_lock(&g_cali_lock); + if (g_misc_dev) { + misc_deregister(g_misc_dev); + g_misc_dev = NULL; + } + mutex_unlock(&g_cali_lock); + aw_dev_dbg(aw_dev->dev, " misc unregister done"); +} + +/*****************************misc node******************************************************/ +static void aw_cali_parse_dt(struct aw_device *aw_dev) +{ + struct device_node *np = aw_dev->dev->of_node; + int ret = -1; + const char *cali_mode_str; + struct aw_cali_desc *desc = &aw_dev->cali_desc; + + ret = of_property_read_string(np, "aw-cali-mode", &cali_mode_str); + if (ret < 0) { + aw_dev_info(aw_dev->dev, " aw-cali-mode get failed ,user default misc way"); + desc->mode = AW_CALI_MODE_MISC; + return; + } + + if (!strcmp(cali_mode_str, "none")) + desc->mode = AW_CALI_MODE_NONE; + else if (!strcmp(cali_mode_str, "aw_class")) + desc->mode = AW_CALI_MODE_CLASS; + else if (!strcmp(cali_mode_str, "aw_attr")) + desc->mode = AW_CALI_MODE_ATTR; + else if (!strcmp(cali_mode_str, "aw_cali_all")) + desc->mode = AW_CALI_MODE_ALL; + else + desc->mode = AW_CALI_MODE_MISC; /*default misc*/ + + aw_dev_info(aw_dev->dev, "cali mode str:%s num:%d", + cali_mode_str, desc->mode); +} + +int aw_cali_parse_re_dt(struct aw_device *aw_dev) +{ + int ret; + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-re-min", &aw_dev->re_min); + if (ret < 0) { + aw_dev->re_min = AW_CALI_RE_DEFAULT_MIN; + aw_dev_info(aw_dev->dev, "read aw-re-min failed, use default"); + } + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-re-max", &aw_dev->re_max); + if (ret < 0) { + aw_dev->re_max = AW_CALI_RE_DEFAULT_MAX; + aw_dev_info(aw_dev->dev, "read aw-re-max failed, use default"); + } + + if (aw_dev->re_min >= aw_dev->re_max) { + aw_dev_err(aw_dev->dev, "re max must be greater than re min"); + return -EINVAL; + } + + aw_dev_info(aw_dev->dev, "re min: %d, re max: %d", + aw_dev->re_min, aw_dev->re_max); + return 0; +} + +int aw_cali_init(struct aw_cali_desc *cali_desc) +{ + int ret; + + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + + cali_desc->cali_f0 = 0; + cali_desc->cali_re = 0; + cali_desc->cali_q = 0; + cali_desc->status = 0; + + aw_cali_parse_dt(aw_dev); + + if (cali_desc->mode == AW_CALI_MODE_ATTR) { + aw_cali_attr_init(aw_dev); + } else if (cali_desc->mode == AW_CALI_MODE_CLASS) { + aw_cali_class_attr_init(aw_dev); + } else if (cali_desc->mode == AW_CALI_MODE_ALL) { + aw_cali_attr_init(aw_dev); + aw_cali_class_attr_init(aw_dev); + } + + aw_cali_misc_init(aw_dev); + + ret = aw_cali_parse_re_dt(aw_dev); + + return ret; +} + +void aw_cali_deinit(struct aw_cali_desc *cali_desc) +{ + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + + if (cali_desc->mode == AW_CALI_MODE_ATTR) { + aw_cali_attr_deinit(aw_dev); + } else if (cali_desc->mode == AW_CALI_MODE_CLASS) { + aw_cali_class_attr_deinit(aw_dev); + } else if (cali_desc->mode == AW_CALI_MODE_ALL) { + aw_cali_attr_deinit(aw_dev); + aw_cali_class_attr_deinit(aw_dev); + } + + aw_cali_misc_deinit(aw_dev); +} + diff --git a/techpack/audio/asoc/codecs/aw882xxacf/aw_calib.h b/techpack/audio/asoc/codecs/aw882xxacf/aw_calib.h new file mode 100644 index 000000000000..fbcc2999282d --- /dev/null +++ b/techpack/audio/asoc/codecs/aw882xxacf/aw_calib.h @@ -0,0 +1,142 @@ +#ifndef __AWINIC_CALIBRATION_H__ +#define __AWINIC_CALIBRATION_H__ + +#define AW_CALI_STORE_EXAMPLE + +#define AW_CALI_READ_TIMES (3) +#define AW_ERRO_CALI_VALUE (0) +#define AW_CALI_RE_DEFAULT_TIMER (3000) + +#define AW_CALI_RE_MAX (15000) +#define AW_CALI_RE_MIN (4000) + +#define AW_CALI_CFG_NUM (3) +#define AW_CALI_DATA_NUM (6) +#define AW_PARAMS_NUM (600) +#define AW_KILO_PARAMS_NUM (1000) + +#define AW_CALI_RE_DEFAULT_MAX (50000) +#define AW_CALI_RE_DEFAULT_MIN (1000) + +struct aw_device; + +enum afe_module_type { + AW_RX_MODULE = 0, + AW_TX_MODULE = 1, +}; + +struct cali_cfg { + int32_t data[AW_CALI_CFG_NUM]; +}; + +struct cali_data { + int32_t data[AW_CALI_DATA_NUM]; +}; + +struct params_data { + int32_t data[AW_PARAMS_NUM]; +}; + +struct ptr_params_data { + int len; + int32_t *data; +}; + +struct f0_q_data { + int32_t data[4]; +}; + +enum { + AW_IOCTL_MSG_IOCTL = 0, + AW_IOCTL_MSG_RD_DSP, + AW_IOCTL_MSG_WR_DSP +}; + +#define AW_IOCTL_MSG_VERSION (0) +typedef struct { + int32_t type; + int32_t opcode_id; + int32_t version; + int32_t data_len; + char *data_buf; + int32_t reseriver[2]; +} aw_ioctl_msg_t; + +#define AW_IOCTL_MAGIC 'a' +#define AW_IOCTL_SET_CALI_CFG _IOWR(AW_IOCTL_MAGIC, 1, struct cali_cfg) +#define AW_IOCTL_GET_CALI_CFG _IOWR(AW_IOCTL_MAGIC, 2, struct cali_cfg) +#define AW_IOCTL_GET_CALI_DATA _IOWR(AW_IOCTL_MAGIC, 3, struct cali_data) +#define AW_IOCTL_SET_NOISE _IOWR(AW_IOCTL_MAGIC, 4, int32_t) +#define AW_IOCTL_GET_F0 _IOWR(AW_IOCTL_MAGIC, 5, int32_t) +#define AW_IOCTL_SET_CALI_RE _IOWR(AW_IOCTL_MAGIC, 6, int32_t) +#define AW_IOCTL_GET_CALI_RE _IOWR(AW_IOCTL_MAGIC, 7, int32_t) +#define AW_IOCTL_SET_VMAX _IOWR(AW_IOCTL_MAGIC, 8, int32_t) +#define AW_IOCTL_GET_VMAX _IOWR(AW_IOCTL_MAGIC, 9, int32_t) +#define AW_IOCTL_SET_PARAM _IOWR(AW_IOCTL_MAGIC, 10, struct params_data) +#define AW_IOCTL_ENABLE_CALI _IOWR(AW_IOCTL_MAGIC, 11, int8_t) +#define AW_IOCTL_SET_PTR_PARAM_NUM _IOWR(AW_IOCTL_MAGIC, 12, struct ptr_params_data) +#define AW_IOCTL_GET_F0_Q _IOWR(AW_IOCTL_MAGIC, 13, struct f0_q_data) +#define AW_IOCTL_SET_DSP_HMUTE _IOWR(AW_IOCTL_MAGIC, 14, int32_t) +#define AW_IOCTL_SET_CALI_CFG_FLAG _IOWR(AW_IOCTL_MAGIC, 15, int32_t) +#define AW_IOCTL_MSG _IOWR(AW_IOCTL_MAGIC, 16, aw_ioctl_msg_t) + +enum{ + AW_CALI_MODE_NONE = 0, + AW_CALI_MODE_ATTR, + AW_CALI_MODE_CLASS, + AW_CALI_MODE_MISC, + AW_CALI_MODE_ALL, + AW_CALI_MODE_MAX, +}; + +enum { + AW_CALI_CMD_RE = 0, + AW_CALI_CMD_F0, + AW_CALI_CMD_RE_F0, + AW_CALI_CMD_F0_Q, + AW_CALI_CMD_RE_F0_Q, +}; + +enum { + CALI_OPS_HMUTE = 0X0001, + CALI_OPS_NOISE = 0X0002, +}; + +enum { + CALI_TYPE_RE = 0, + CALI_TYPE_F0, +}; + +enum { + CALI_STR_NONE = 0, + CALI_STR_CALI_RE_F0, + CALI_STR_CALI_RE, + CALI_STR_CALI_F0, + CALI_STR_SET_RE, + CALI_STR_SHOW_RE, /*show cali_re*/ + CALI_STR_SHOW_R0, /*show real r0*/ + CALI_STR_SHOW_CALI_F0, /*GET DEV CALI_F0*/ + CALI_STR_SHOW_F0, /*SHOW REAL F0*/ + CALI_STR_SHOW_TE, + CALI_STR_SHOW_ST, + CALI_STR_DEV_SEL, /*switch device*/ + CALI_STR_VER, + CALI_STR_DEV_NUM, + CALI_STR_MAX, +}; + +struct aw_cali_desc { + unsigned char status; + unsigned char mode; /*0:NONE 1:ATTR 2:CLASS 3:MISC */ + int32_t cali_re; /*set cali_re*/ + int32_t cali_f0; /*store cali_f0*/ + int32_t cali_q; /*store cali q*/ +}; + +int aw_cali_init(struct aw_cali_desc *cali_desc); +void aw_cali_deinit(struct aw_cali_desc *cali_desc); +int aw_cali_svc_get_cali_status(void); +int aw_cali_read_re_from_nvram(int32_t *cali_re, int32_t ch_index); + + +#endif diff --git a/techpack/audio/asoc/codecs/aw882xxacf/aw_data_type.h b/techpack/audio/asoc/codecs/aw882xxacf/aw_data_type.h new file mode 100644 index 000000000000..5184dfa01189 --- /dev/null +++ b/techpack/audio/asoc/codecs/aw882xxacf/aw_data_type.h @@ -0,0 +1,151 @@ +#ifndef __AWINIC_DATA_TYPE_H__ +#define __AWINIC_DATA_TYPE_H__ + +#define AW_NAME_BUF_MAX (50) + +struct aw_msg_hdr { + int32_t type; + int32_t opcode_id; + int32_t version; + int32_t reseriver[3]; +}; + + +/****************************************************************** +* aw profile +*******************************************************************/ +#define PROJECT_NAME_MAX (24) +#define CUSTOMER_NAME_MAX (16) +#define CFG_VERSION_MAX (4) +#define DEV_NAME_MAX (16) +#define PROFILE_STR_MAX (32) + +#define ACF_FILE_ID (0xa15f908) + + +enum aw_cfg_hdr_version { + AW_CFG_HDR_VER_0_0_0_1 = 0x00000001, + AW_CFG_HDR_VER_1_0_0_0 = 0x01000000, +}; + +enum aw_cfg_dde_type { + AW_DEV_TYPE_ID = 0x00000000, + AW_SKT_TYPE_ID = 0x00000001, + AW_DEV_DEFAULT_TYPE_ID = 0x00000002, +}; + +enum aw_sec_type { + ACF_SEC_TYPE_REG = 0, + ACF_SEC_TYPE_DSP, + ACF_SEC_TYPE_DSP_CFG, + ACF_SEC_TYPE_DSP_FW, + ACF_SEC_TYPE_HDR_REG, + ACF_SEC_TYPE_HDR_DSP_CFG, + ACF_SEC_TYPE_HDR_DSP_FW, + ACF_SEC_TYPE_MUTLBIN, + ACF_SEC_TYPE_SKT_PROJECT, + ACF_SEC_TYPE_DSP_PROJECT, + ACF_SEC_TYPE_MONITOR, + ACF_SEC_TYPE_MAX, +}; + +enum profile_data_type { + AW_PROFILE_DATA_TYPE_REG = 0, + AW_PROFILE_DATA_TYPE_DSP, + AW_PROFILE_DATA_TYPE_MAX, +}; + +enum algo_profile_data_type { + AW_PROFILE_DATA_TYPE_ALGO_DSP, + AW_PROFILE_DATA_TYPE_ALGO_MAX, +}; + + +enum aw_prof_type { + AW_PROFILE_MUSIC = 0, + AW_PROFILE_VOICE, + AW_PROFILE_VOIP, + AW_PROFILE_RINGTONE, + AW_PROFILE_RINGTONE_HS, + AW_PROFILE_LOWPOWER, + AW_PROFILE_BYPASS, + AW_PROFILE_MMI, + AW_PROFILE_FM, + AW_PROFILE_NOTIFICATION, + AW_PROFILE_RECEIVER, + AW_PROFILE_MAX, +}; + +enum aw_profile_status { + AW_PROFILE_WAIT = 0, + AW_PROFILE_OK, +}; + +struct aw_cfg_hdr { + uint32_t a_id; /*acf file ID 0xa15f908*/ + char a_project[PROJECT_NAME_MAX]; /*project name*/ + char a_custom[CUSTOMER_NAME_MAX]; /*custom name :huawei xiaomi vivo oppo*/ + char a_version[CFG_VERSION_MAX]; /*author update version*/ + uint32_t a_author_id; /*author id*/ + uint32_t a_ddt_size; /*sub section table entry size*/ + uint32_t a_ddt_num; /*sub section table entry num*/ + uint32_t a_hdr_offset; /*sub section table offset in file*/ + uint32_t a_hdr_version; /*sub section table version*/ + uint32_t reserve[3]; +}; + +struct aw_cfg_dde { + uint32_t type; /*DDE type id*/ + char dev_name[DEV_NAME_MAX]; + uint16_t dev_index; /*dev id*/ + uint16_t dev_bus; /*dev bus id*/ + uint16_t dev_addr; /*dev addr id*/ + uint16_t dev_profile; /*dev profile id*/ + uint32_t data_type; /*data type id*/ + uint32_t data_size; + uint32_t data_offset; + uint32_t data_crc; + uint32_t reserve[5]; +}; + +struct aw_cfg_dde_v_1_0_0_0 { + uint32_t type; /*DDE type id*/ + char dev_name[DEV_NAME_MAX]; + uint16_t dev_index; /*dev id*/ + uint16_t dev_bus; /*dev bus id*/ + uint16_t dev_addr; /*dev addr id*/ + uint16_t dev_profile; /*dev profile id*/ + uint32_t data_type; /*data type id*/ + uint32_t data_size; + uint32_t data_offset; + uint32_t data_crc; + char dev_profile_str[PROFILE_STR_MAX]; + uint32_t chip_id; + uint32_t reserve[4]; +}; + +struct aw_sec_data_desc { + uint32_t len; + unsigned char *data; +}; + +struct aw_prof_desc { + uint32_t id; + uint32_t prof_st; + char *prf_str; + struct aw_sec_data_desc sec_desc[AW_PROFILE_DATA_TYPE_MAX]; +}; + +struct aw_all_prof_info { + struct aw_prof_desc prof_desc[AW_PROFILE_MAX]; +}; + +struct aw_prof_info { + int count; + char **prof_name_list; + struct aw_prof_desc *prof_desc; +}; + + +#endif + diff --git a/techpack/audio/asoc/codecs/aw882xxacf/aw_device.c b/techpack/audio/asoc/codecs/aw882xxacf/aw_device.c new file mode 100644 index 000000000000..705a66659a5e --- /dev/null +++ b/techpack/audio/asoc/codecs/aw882xxacf/aw_device.c @@ -0,0 +1,1938 @@ +/* #define DEBUG */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "aw_data_type.h" +#include "aw_log.h" +#include "aw_device.h" +#include "aw_dsp.h" +/*#include "aw_afe.h"*/ +#include "aw_bin_parse.h" +#include "aw882xx.h" + +#define AW_DEV_SYSST_CHECK_MAX (10) +#define AW_ALGO_PARAMS_PATH "/vendor/firmware/aw_skt.bin" + +enum { + AW_EXT_DSP_WRITE_NONE = 0, + AW_EXT_DSP_WRITE, +}; + +static char *profile_name[AW_PROFILE_MAX] = { + "Music", "Voice", "Voip", "Ringtone", "Ringtone_hs", + "Lowpower", "Bypass", "Mmi", "Fm", "Notification", "Receiver" + }; + +static char ext_dsp_prof_write = AW_EXT_DSP_WRITE_NONE; +static DEFINE_MUTEX(g_ext_dsp_prof_wr_lock); /*lock ext wr flag*/ +static unsigned int g_fade_in_time = AW_1000_US / 10; + +static unsigned int g_fade_out_time = AW_1000_US >> 1; +static LIST_HEAD(g_dev_list); +static DEFINE_MUTEX(g_dev_lock); + +/*********************************awinic acf*************************************/ +static void aw_dev_reg_dump(struct aw_device *aw_dev) +{ + int reg_num = aw_dev->ops.aw_get_reg_num(); + uint8_t i = 0; + unsigned int reg_val = 0; + + for (i = 0; i < reg_num; i++) { + if (aw_dev->ops.aw_check_rd_access(i)) { + aw_dev->ops.aw_i2c_read(aw_dev, i, ®_val); + aw_dev_info(aw_dev->dev, "read: reg = 0x%02x, val = 0x%04x", + i, reg_val); + } + } +} + +static uint8_t aw_dev_crc8_check(unsigned char *data, uint32_t data_size) +{ + uint8_t crc_value = 0x00; + uint8_t pdatabuf = 0; + int i; + + while (data_size--) { + pdatabuf = *data++; + for (i = 0; i < 8; i++) { + /*if the lowest bit is 1*/ + if ((crc_value ^ (pdatabuf)) & 0x01) { + /*Xor multinomial*/ + crc_value ^= 0x18; + crc_value >>= 1; + crc_value |= 0x80; + } else { + crc_value >>= 1; + } + pdatabuf >>= 1; + } + } + return crc_value; +} + +static int aw_dev_check_cfg_by_hdr(struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = NULL; + struct aw_cfg_dde *cfg_dde = NULL; + unsigned int end_data_offset = 0; + unsigned int act_data = 0; + unsigned int hdr_ddt_len = 0; + uint8_t act_crc8 = 0; + int i; + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + + /*check file type id is awinic acf file*/ + if (cfg_hdr->a_id != ACF_FILE_ID) { + aw_pr_err("not acf type file"); + return -EINVAL; + } + + hdr_ddt_len = cfg_hdr->a_hdr_offset + cfg_hdr->a_ddt_size; + if (hdr_ddt_len > aw_cfg->len) { + aw_pr_err("hdrlen with ddt_len [%d] overflow file size[%d]", + cfg_hdr->a_hdr_offset, aw_cfg->len); + return -EINVAL; + } + + /*check data size*/ + cfg_dde = (struct aw_cfg_dde *)((char *)aw_cfg->data + cfg_hdr->a_hdr_offset); + act_data += hdr_ddt_len; + for (i = 0; i < cfg_hdr->a_ddt_num; i++) + act_data += cfg_dde[i].data_size; + + if (act_data != aw_cfg->len) { + aw_pr_err("act_data[%d] not equal to file size[%d]!", + act_data, aw_cfg->len); + return -EINVAL; + } + + for (i = 0; i < cfg_hdr->a_ddt_num; i++) { + /* data check */ + end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size; + if (end_data_offset > aw_cfg->len) { + aw_pr_err("a_ddt_num[%d] end_data_offset[%d] overflow file size[%d]", + i, end_data_offset, aw_cfg->len); + return -EINVAL; + } + + /* crc check */ + act_crc8 = aw_dev_crc8_check(aw_cfg->data + cfg_dde[i].data_offset, cfg_dde[i].data_size); + if (act_crc8 != cfg_dde[i].data_crc) { + aw_pr_err("a_ddt_num[%d] crc8 check failed, act_crc8:0x%x != data_crc 0x%x", + i, (uint32_t)act_crc8, cfg_dde[i].data_crc); + return -EINVAL; + } + } + + aw_pr_info("project name [%s]", cfg_hdr->a_project); + aw_pr_info("custom name [%s]", cfg_hdr->a_custom); + aw_pr_info("version name [%d.%d.%d.%d]", cfg_hdr->a_version[3], cfg_hdr->a_version[2], + cfg_hdr->a_version[1], cfg_hdr->a_version[0]); + aw_pr_info("author id %d", cfg_hdr->a_author_id); + + return 0; +} + +static int aw_dev_parse_check_acf_by_hdr_v_1_0_0_0(struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = NULL; + struct aw_cfg_dde_v_1_0_0_0 *cfg_dde = NULL; + unsigned int end_data_offset = 0; + unsigned int act_data = 0; + unsigned int hdr_ddt_len = 0; + uint8_t act_crc8 = 0; + int i; + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + + /*check file type id is awinic acf file*/ + if (cfg_hdr->a_id != ACF_FILE_ID) { + aw_pr_err("not acf type file"); + return -EINVAL; + } + + hdr_ddt_len = cfg_hdr->a_hdr_offset + cfg_hdr->a_ddt_size; + if (hdr_ddt_len > aw_cfg->len) { + aw_pr_err("hdrlen with ddt_len [%d] overflow file size[%d]", + cfg_hdr->a_hdr_offset, aw_cfg->len); + return -EINVAL; + } + + /*check data size*/ + cfg_dde = (struct aw_cfg_dde_v_1_0_0_0 *)((char *)aw_cfg->data + cfg_hdr->a_hdr_offset); + act_data += hdr_ddt_len; + for (i = 0; i < cfg_hdr->a_ddt_num; i++) + act_data += cfg_dde[i].data_size; + + if (act_data != aw_cfg->len) { + aw_pr_err("act_data[%d] not equal to file size[%d]!", + act_data, aw_cfg->len); + return -EINVAL; + } + + for (i = 0; i < cfg_hdr->a_ddt_num; i++) { + /* data check */ + end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size; + if (end_data_offset > aw_cfg->len) { + aw_pr_err("a_ddt_num[%d] end_data_offset[%d] overflow file size[%d]", + i, end_data_offset, aw_cfg->len); + return -EINVAL; + } + + /* crc check */ + act_crc8 = aw_dev_crc8_check(aw_cfg->data + cfg_dde[i].data_offset, cfg_dde[i].data_size); + if (act_crc8 != cfg_dde[i].data_crc) { + aw_pr_err("a_ddt_num[%d] crc8 check failed, act_crc8:0x%x != data_crc 0x%x", + i, (uint32_t)act_crc8, cfg_dde[i].data_crc); + return -EINVAL; + } + } + + aw_pr_info("project name [%s]", cfg_hdr->a_project); + aw_pr_info("custom name [%s]", cfg_hdr->a_custom); + aw_pr_info("version name [%d.%d.%d.%d]", cfg_hdr->a_version[3], cfg_hdr->a_version[2], + cfg_hdr->a_version[1], cfg_hdr->a_version[0]); + aw_pr_info("author id %d", cfg_hdr->a_author_id); + + return 0; +} + +static int aw_dev_parse_raw_dsp_skt(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc) +{ + aw_dev_info(aw_dev->dev, "data_size:%d enter", data_len); + + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_ALGO_DSP].data = data; + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_ALGO_DSP].len = data_len; + + return 0; +} + +static int aw_dev_get_chan_group(struct aw_device *aw_dev, int *chan_group) +{ + switch (aw_dev->channel) { + + case AW_DEV_CH_PRI_L: + case AW_DEV_CH_PRI_R: + *chan_group = AW_DEV_CH_GROUP_PRI; + break; + case AW_DEV_CH_SEC_L: + case AW_DEV_CH_SEC_R: + *chan_group = AW_DEV_CH_GROUP_SEC; + break; + default: + aw_pr_err("can not find chan_group, channel %d ", aw_dev->channel); + return -EINVAL; + } + + aw_pr_dbg("chan_group[%d] ", *chan_group); + return 0; +} + +int aw_dev_load_acf_check(struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = NULL; + + if (aw_cfg == NULL) { + aw_pr_err("aw_prof is NULL"); + return -ENOMEM; + } + + if (aw_cfg->len < sizeof(struct aw_cfg_hdr)) { + aw_pr_err("cfg hdr size[%d] overflow file size[%d]", + aw_cfg->len, (int)sizeof(struct aw_cfg_hdr)); + return -EINVAL; + } + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + switch (cfg_hdr->a_hdr_version) { + case AW_CFG_HDR_VER_0_0_0_1: + return aw_dev_check_cfg_by_hdr(aw_cfg); + case AW_CFG_HDR_VER_1_0_0_0: + return aw_dev_parse_check_acf_by_hdr_v_1_0_0_0(aw_cfg); + default: + aw_pr_err("unsupported hdr_version [0x%x]", cfg_hdr->a_hdr_version); + return -EINVAL; + } + + return 0; +} + + +static int aw_dev_parse_raw_reg(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc) +{ + aw_dev_info(aw_dev->dev, "data_size:%d enter", data_len); + + if (data_len % 4) { + aw_dev_err(aw_dev->dev, "bin data len get error!"); + return -EINVAL; + } + + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_REG].data = data; + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_REG].len = data_len; + + prof_desc->prof_st = AW_PROFILE_OK; + + return 0; +} + +static void aw_dev_parse_raw_dsp(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc) +{ + aw_dev_info(aw_dev->dev, "data_size:%d enter", data_len); + + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_DSP].data = data; + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_DSP].len = data_len; +} + +static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc) +{ + struct aw_bin *aw_bin = NULL; + int ret; + + aw_dev_info(aw_dev->dev, "data_size:%d enter", data_len); + + aw_bin = kzalloc(data_len + sizeof(struct aw_bin), GFP_KERNEL); + if (aw_bin == NULL) { + aw_dev_err(aw_dev->dev, "devm_kzalloc aw_bin failed"); + return -ENOMEM; + } + + aw_bin->info.len = data_len; + memcpy(aw_bin->info.data, data, data_len); + + ret = aw_parsing_bin_file(aw_bin); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "parse bin failed"); + goto parse_bin_failed; + } + + if ((aw_bin->all_bin_parse_num != 1) || + (aw_bin->header_info[0].bin_data_type != DATA_TYPE_REGISTER)) { + aw_dev_err(aw_dev->dev, "bin num or type error"); + goto parse_bin_failed; + } + + if (aw_bin->header_info[0].valid_data_len % 4) { + aw_dev_err(aw_dev->dev, "bin data len get error!"); + return -EINVAL; + } + + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_REG].data = + data + aw_bin->header_info[0].valid_data_addr; + prof_desc->sec_desc[AW_PROFILE_DATA_TYPE_REG].len = + aw_bin->header_info[0].valid_data_len; + prof_desc->prof_st = AW_PROFILE_OK; + + kfree(aw_bin); + aw_bin = NULL; + + return 0; + +parse_bin_failed: + kfree(aw_bin); + aw_bin = NULL; + return ret; +} + +static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg_hdr *cfg_hdr, + struct aw_cfg_dde *prof_hdr, struct aw_prof_desc *scene_prof_desc) +{ + switch (prof_hdr->data_type) { + case ACF_SEC_TYPE_REG: + return aw_dev_parse_raw_reg(aw_dev, + (uint8_t *)cfg_hdr + prof_hdr->data_offset, + prof_hdr->data_size, + scene_prof_desc); + break; + case ACF_SEC_TYPE_HDR_REG: + return aw_dev_parse_reg_bin_with_hdr(aw_dev, + (uint8_t *)cfg_hdr + prof_hdr->data_offset, + prof_hdr->data_size, + scene_prof_desc); + break; + case ACF_SEC_TYPE_MONITOR: + return aw_monitor_parse_fw(&aw_dev->monitor_desc, + (uint8_t *)cfg_hdr + prof_hdr->data_offset, + prof_hdr->data_size); + break; + } + + return 0; +} + +static int aw_dev_parse_dev_type(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) +{ + int i = 0; + int ret; + int sec_num = 0; + struct aw_cfg_dde *cfg_dde = + (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->a_hdr_offset); + + aw_dev_info(aw_dev->dev, "enter"); + + for (i = 0; i < prof_hdr->a_ddt_num; i++) { + if ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr == cfg_dde[i].dev_addr) && + (cfg_dde[i].type == AW_DEV_TYPE_ID)) { + if (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR) { + ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], + &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "parse dev driver bin data failed"); + return ret; + } + sec_num++; + } else { + ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], NULL); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "parse monitor bin data failed"); + return ret; + } + } + } + } + + if (sec_num == 0) { + aw_dev_info(aw_dev->dev, "get dev type num is %d, please use default", sec_num); + return AW_DEV_TYPE_NONE; + } + + return AW_DEV_TYPE_OK; +} + +static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) +{ + int i = 0; + int ret; + int sec_num = 0; + struct aw_cfg_dde *cfg_dde = + (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->a_hdr_offset); + + aw_dev_info(aw_dev->dev, "enter"); + + for (i = 0; i < prof_hdr->a_ddt_num; i++) { + if ((aw_dev->index == cfg_dde[i].dev_index) && + (cfg_dde[i].type == AW_DEV_DEFAULT_TYPE_ID)) { + if (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR) { + ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], + &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "parse dev driver bin data failed"); + return ret; + } + sec_num++; + } else { + ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], NULL); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "parse monitor bin data failed"); + return ret; + } + } + } + } + + if (sec_num == 0) { + aw_dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", sec_num); + return -EINVAL; + } + + return 0; +} + +static void aw_dev_parse_skt_type(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) +{ + int i = 0; + int sec_num = 0; + struct aw_cfg_dde *cfg_dde = + (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->a_hdr_offset); + + aw_dev_info(aw_dev->dev, "enter"); + + for (i = 0; i < prof_hdr->a_ddt_num; i++) { + if ((aw_dev->index == cfg_dde[i].dev_index) && + (cfg_dde[i].type == AW_SKT_TYPE_ID)) { + if (cfg_dde[i].data_type == ACF_SEC_TYPE_DSP) { + aw_dev_parse_raw_dsp(aw_dev, + (uint8_t *)prof_hdr + cfg_dde[i].data_offset, + cfg_dde[i].data_size, + &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); + sec_num++; + } + } + } + + aw_dev_info(aw_dev->dev, "get dsp data prof cnt is %d ", sec_num); +} + +static int aw_dev_acf_load_by_hdr(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) +{ + int ret; + + ret = aw_dev_parse_dev_type(aw_dev, prof_hdr, all_prof_info); + if (ret < 0) { + return ret; + } else if (ret == AW_DEV_TYPE_NONE) { + aw_dev_info(aw_dev->dev, "get dev type num is0, parse default dev type"); + ret = aw_dev_parse_dev_default_type(aw_dev, prof_hdr, all_prof_info); + if (ret < 0) + return ret; + } + + aw_dev_parse_skt_type(aw_dev, prof_hdr, all_prof_info); + + return 0; +} + + +static int aw_dev_cfg_get_vaild_prof(struct aw_device *aw_dev, + struct aw_all_prof_info all_prof_info) +{ + int i; + int num = 0; + struct aw_prof_desc *prof_desc = all_prof_info.prof_desc; + struct aw_prof_info *prof_info = &aw_dev->prof_info; + + for (i = 0; i < AW_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW_PROFILE_OK) + aw_dev->prof_info.count++; + } + + aw_dev_info(aw_dev->dev, "get vaild profile:%d", aw_dev->prof_info.count); + + if (!aw_dev->prof_info.count) { + aw_dev_err(aw_dev->dev, "no profile data"); + return -EPERM; + } + + prof_info->prof_desc = kzalloc(prof_info->count * sizeof(struct aw_prof_desc), GFP_KERNEL); + if (prof_info->prof_desc == NULL) { + aw_dev_err(aw_dev->dev, "prof_desc kzalloc failed"); + return -ENOMEM; + } + + for (i = 0; i < AW_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW_PROFILE_OK) { + if (num >= prof_info->count) { + aw_dev_err(aw_dev->dev, "get scene num[%d] overflow count[%d]", + num, prof_info->count); + return -ENOMEM; + } + prof_info->prof_desc[num] = prof_desc[i]; + prof_info->prof_desc[num].id = i; + num++; + } + } + + return 0; +} + + +/******************ACF bin format V1.0.0.0************************/ +static int aw_dev_parse_get_scene_count_v1_0_0_0(struct aw_device *aw_dev, + struct aw_container *aw_cfg, int *count) +{ + struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + struct aw_cfg_dde_v_1_0_0_0 *cfg_dde = + (struct aw_cfg_dde_v_1_0_0_0 *)(aw_cfg->data + cfg_hdr->a_hdr_offset); + int i = 0; + int chan_group = -1; + int ret = -1; + + ret = aw_dev_get_chan_group(aw_dev, &chan_group); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get chan_group failed "); + return ret; + } + + for (i = 0; i < cfg_hdr->a_ddt_num; ++i) { + if ((cfg_dde[i].data_type == ACF_SEC_TYPE_DSP) && + (cfg_dde[i].type == AW_SKT_TYPE_ID) && + (chan_group == cfg_dde[i].dev_index)) { + (*count)++; + } + } + + if ((*count) == 0) { + aw_dev_err(aw_dev->dev, "can't find scene"); + return -EINVAL; + } + + aw_dev_info(aw_dev->dev, "scene count is %d", (*count)); + return 0; +} + + +static int aw_dev_parse_create_prof_name_list_v_1_0_0_0(struct aw_device *aw_dev) +{ + struct aw_prof_info *prof_info = &aw_dev->skt_prof_info; + struct aw_prof_desc *prof_desc= prof_info->prof_desc; + int i; + + if (prof_desc == NULL) { + aw_dev_err(aw_dev->dev, "prof_desc is NULL"); + return -EINVAL; + } + + prof_info->prof_name_list = devm_kzalloc(aw_dev->dev, + prof_info->count * PROFILE_STR_MAX, + GFP_KERNEL); + if (prof_info->prof_name_list == NULL) { + aw_dev_err(aw_dev->dev, "prof_name_list devm_kzalloc failed"); + return -ENOMEM; + } + + for (i = 0; i < prof_info->count; i++) { + prof_info->prof_name_list[i] = prof_desc[i].prf_str; + aw_dev_info(aw_dev->dev, "prof name list[%d]:%s, id=%d", i, prof_info->prof_name_list[i], prof_desc[i].id); + } + + return 0; +} + +static int aw_dev_parse_skt_type_v_1_0_0_0(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr) +{ + int i = 0; + int ret = -1; + int scene_count = 0; + struct aw_prof_info *prof_info = &aw_dev->skt_prof_info; + struct aw_cfg_dde_v_1_0_0_0 *cfg_dde = + (struct aw_cfg_dde_v_1_0_0_0 *)((char *)prof_hdr + prof_hdr->a_hdr_offset); + int chan_group = -1; + aw_dev_info(aw_dev->dev, "enter"); + + ret = aw_dev_get_chan_group(aw_dev, &chan_group); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get chan_group failed "); + return ret; + } + + for (i = 0; i < prof_hdr->a_ddt_num; i++) { + if ((cfg_dde[i].type == AW_SKT_TYPE_ID) && + (cfg_dde[i].data_type == ACF_SEC_TYPE_DSP) && + (chan_group == cfg_dde[i].dev_index)) { + + ret = aw_dev_parse_raw_dsp_skt(aw_dev, + (uint8_t *)prof_hdr + cfg_dde[i].data_offset, + cfg_dde[i].data_size, &prof_info->prof_desc[scene_count]); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "parse dsp bin data failed"); + return ret; + } + + prof_info->prof_desc[scene_count].prf_str = cfg_dde[i].dev_profile_str; + prof_info->prof_desc[scene_count].id = cfg_dde[i].dev_profile; + + scene_count++; + } + } + aw_dev_dbg(aw_dev->dev, "scene_count = %d", scene_count); + return 0; +} + +static int aw_dev_parse_acf_by_hdr_v_1_0_0_0(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_prof_info *prof_info = &aw_dev->skt_prof_info; + struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + int ret; + + ret = aw_dev_parse_get_scene_count_v1_0_0_0(aw_dev, aw_cfg, &prof_info->count); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get scene count failed"); + return ret; + } + + prof_info->prof_desc = devm_kzalloc(aw_dev->dev, + prof_info->count * sizeof(struct aw_prof_desc), + GFP_KERNEL); + if (prof_info->prof_desc == NULL) { + aw_dev_err(aw_dev->dev, "prof_desc devm_kzalloc failed"); + return -ENOMEM; + } + + ret = aw_dev_parse_skt_type_v_1_0_0_0(aw_dev, cfg_hdr); + if (ret < 0) { + aw_dev_err(aw_dev->dev, " failed"); + return ret; + } + + ret = aw_dev_parse_create_prof_name_list_v_1_0_0_0(aw_dev); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "create prof name list failed"); + return ret; + } + + return 0; +} + +int aw_dev_parse_skt_bin(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = NULL; + int ret = -1; + + if (aw_dev == NULL || aw_cfg == NULL) { + aw_dev_err(aw_dev->dev, "pointer is NULL"); + return -ENOMEM; + } + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + + if (cfg_hdr->a_hdr_version == AW_CFG_HDR_VER_1_0_0_0) { + ret = aw_dev_parse_acf_by_hdr_v_1_0_0_0(aw_dev, aw_cfg); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "hdr_cersion[0x%x] parse failed", + cfg_hdr->a_hdr_version); + return ret; + } + } else { + aw_dev_err(aw_dev->dev, "skt bin format [0x%x] unsupported", cfg_hdr->a_hdr_version); + } + + return 0; +} + +struct aw_sec_data_desc *aw_dev_get_algo_prof_data(struct aw_device *aw_dev, int prof_id) +{ + struct aw_sec_data_desc *prof_data = NULL; + + struct aw_prof_info *prof_info = &aw_dev->skt_prof_info; + int i; + + aw_dev_dbg(aw_dev->dev, "prof num = %d", prof_info->count); + for (i = 0; i < prof_info->count; i++) { + if (prof_id == prof_info->prof_desc[i].id) { + prof_data = &prof_info->prof_desc[i].sec_desc[AW_PROFILE_DATA_TYPE_ALGO_DSP]; + break; + } + } + + return prof_data; +} + +int aw_dev_skt_prof_mode(struct aw_device *aw_dev, int prof_id) +{ + struct aw_sec_data_desc *prof_data = NULL; + int ret = -1; + struct list_head *dev_list = NULL; + struct list_head *pos = NULL; + struct aw_device *local_dev = NULL; + + ret = aw_dev_get_list_head(&dev_list); + if (ret) { + aw_dev_err(aw_dev->dev, "get dev list failed "); + return ret; + } + + aw_dev_info(aw_dev->dev, "set_cur_prof=%d,pre_prof=%d", + prof_id, aw_dev->pre_prof_id); + + if (prof_id != aw_dev->pre_prof_id) { + + list_for_each (pos, dev_list) { + local_dev = container_of(pos, struct aw_device, list_node); + + if ((local_dev->channel == AW_DEV_CH_PRI_L) || (local_dev->channel == AW_DEV_CH_SEC_L)) { + prof_data = aw_dev_get_algo_prof_data(local_dev, prof_id); + if (prof_data) { + ret = aw_dev_set_algo_prof_data(local_dev, prof_data->data, prof_data->len); + if (ret < 0) { + aw_dev_err(local_dev->dev, "set algo prof [%d] failed", prof_id); + return -EINVAL; + } + } else { + aw_dev_err(local_dev->dev, "prof data is NULL"); + return -EINVAL; + } + } + } + + aw_dev->pre_prof_id = prof_id; + aw_dev_info(aw_dev->dev, "set algo prof [%d] ok", prof_id); + } + + return 0; +} + + +static int aw_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = NULL; + struct aw_all_prof_info all_prof_info; + int ret; + + aw_dev_info(aw_dev->dev, "enter"); + memset(&all_prof_info, 0, sizeof(struct aw_all_prof_info)); + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + switch (cfg_hdr->a_hdr_version) { + case AW_CFG_HDR_VER_0_0_0_1: + ret = aw_dev_acf_load_by_hdr(aw_dev, cfg_hdr, &all_prof_info); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "hdr_cersion[0x%x] parse failed", + cfg_hdr->a_hdr_version); + return ret; + } + break; + default: + aw_pr_err("unsupported hdr_version [0x%x]", cfg_hdr->a_hdr_version); + return -EINVAL; + } + + ret = aw_dev_cfg_get_vaild_prof(aw_dev, all_prof_info); + if (ret < 0) + return ret; + + aw_dev_info(aw_dev->dev, "parse cfg success"); + return 0; +} + +static struct aw_sec_data_desc *aw_dev_get_prof_data(struct aw_device *aw_dev, int index, int data_type) +{ + struct aw_sec_data_desc *sec_data = NULL; + struct aw_prof_desc *prof_desc = NULL; + + if (index >= aw_dev->prof_info.count) { + aw_dev_err(aw_dev->dev, "index[%d] overflow count[%d]", + index, aw_dev->prof_info.count); + return NULL; + } + + prof_desc = &aw_dev->prof_info.prof_desc[index]; + + if (data_type >= AW_PROFILE_DATA_TYPE_MAX) { + aw_dev_err(aw_dev->dev, "unsupport data type id [%d]", data_type); + return NULL; + } + + sec_data = &prof_desc->sec_desc[data_type]; + + aw_dev_dbg(aw_dev->dev, "get prof[%s] data len[%d]", profile_name[prof_desc->id], sec_data->len); + + return sec_data; +} + + +/*****************************awinic device*************************************/ +static char *aw_dev_get_prof_name(struct aw_device *aw_dev, int index) +{ + struct aw_prof_desc *prof_desc = NULL; + + if (index < 0) { + aw_dev_err(aw_dev->dev, "index[%d] error", index); + return NULL; + } + + if (index >= aw_dev->prof_info.count) { + aw_dev_err(aw_dev->dev, "index[%d] overflow count[%d]", + index, aw_dev->prof_info.count); + return NULL; + } + + prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return profile_name[prof_desc->id]; +} + +/*static int aw_dev_dsp_fw_update(struct aw_device *aw_dev); +{ + int ret; + struct aw_sec_data_desc *dsp_data; + + char *prof_name = aw_dev_get_prof_name(aw_dev, aw_dev->set_prof); + + if (prof_name == NULL) { + aw_dev_err(aw_dev->dev, "get prof name failed"); + return -EINVAL; + } + + dsp_data = aw_dev_get_prof_data(aw_dev, + aw_dev->set_prof, AW_PROFILE_DATA_TYPE_DSP); + if (dsp_data == NULL || + dsp_data->data == NULL || + dsp_data->len == 0) { + aw_dev_info(aw_dev->dev, "dsp data is NULL"); + return 0; + } + + mutex_lock(&g_ext_dsp_prof_wr_lock); + if (ext_dsp_prof_write == AW_EXT_DSP_WRITE_NONE) { + ret = aw_dsp_write_params(aw_dev, dsp_data->data, dsp_data->len); + if (ret) { + aw_dev_err(aw_dev->dev, "dsp params update failed !"); + mutex_unlock(&g_ext_dsp_prof_wr_lock); + return ret; + } + ext_dsp_prof_write = AW_EXT_DSP_WRITE; + } else { + aw_dev_dbg(aw_dev->dev, "dsp params already update !"); + } + mutex_unlock(&g_ext_dsp_prof_wr_lock); + + aw_dev_info(aw_dev->dev, "load %s done", prof_name); + return 0; +}*/ + +static int aw_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t reg_icalk = 0; + uint16_t reg_icalkl = 0; + struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; + + if (desc->icalkl_reg == AW_REG_NONE) { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalk_reg, ®_val); + reg_icalk = (uint16_t)reg_val & (~desc->icalk_reg_mask); + } else { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalk_reg, ®_val); + reg_icalk = (uint16_t)reg_val & (~desc->icalk_reg_mask); + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->icalkl_reg, ®_val); + reg_icalkl = (uint16_t)reg_val & (~desc->icalkl_reg_mask); + if (aw_dev->efuse_check == AW_EF_OR_CHECK) + reg_icalk = (reg_icalk >> desc->icalk_shift) | (reg_icalkl >> desc->icalkl_shift); + else + reg_icalk = (reg_icalk >> desc->icalk_shift) & (reg_icalkl >> desc->icalkl_shift); + } + + if (reg_icalk & (~desc->icalk_sign_mask)) + reg_icalk = reg_icalk | (~desc->icalk_neg_mask); + + *icalk = (int16_t)reg_icalk; + + return ret; +} + +static int aw_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t reg_vcalk = 0; + uint16_t reg_vcalkl = 0; + struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; + + if (desc->vcalkl_reg == AW_REG_NONE) { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalk_reg, ®_val); + reg_vcalk = (uint16_t)reg_val & (~desc->vcalk_reg_mask); + } else { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalk_reg, ®_val); + reg_vcalk = (uint16_t)reg_val & (~desc->vcalk_reg_mask); + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->vcalkl_reg, ®_val); + reg_vcalkl = (uint16_t)reg_val & (~desc->vcalkl_reg_mask); + if (aw_dev->efuse_check == AW_EF_OR_CHECK) + reg_vcalk = (reg_vcalk >> desc->vcalk_shift) | (reg_vcalkl >> desc->vcalkl_shift); + else + reg_vcalk = (reg_vcalk >> desc->vcalk_shift) & (reg_vcalkl >> desc->vcalkl_shift); + } + + if (reg_vcalk & (~desc->vcalk_sign_mask)) + reg_vcalk = reg_vcalk | (~desc->vcalk_neg_mask); + + *vcalk = (int16_t)reg_vcalk; + + return ret; +} + +static int aw_dev_set_vcalb(struct aw_device *aw_dev) +{ + int ret = -1; + unsigned int reg_val; + int vcalb; + int icalk; + int vcalk; + int16_t icalk_val = 0; + int16_t vcalk_val = 0; + + struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; + + if (desc->icalk_reg == AW_REG_NONE || desc->vcalb_reg == AW_REG_NONE) { + aw_dev_info(aw_dev->dev, "REG None!"); + return 0; + } + + ret = aw_dev_get_icalk(aw_dev, &icalk_val); + if (ret < 0) + return ret; + + ret = aw_dev_get_vcalk(aw_dev, &vcalk_val); + if (ret < 0) + return ret; + + icalk = desc->cabl_base_value + desc->icalk_value_factor * icalk_val; + vcalk = desc->cabl_base_value + desc->vcalk_value_factor * vcalk_val; + if (!vcalk) { + aw_dev_err(aw_dev->dev, "vcalk is 0"); + return -EINVAL; + } + + vcalb = desc->vcal_factor * icalk / vcalk; + + reg_val = (unsigned int)vcalb; + aw_dev_info(aw_dev->dev, "icalk=%d, vcalk=%d, vcalb=%d, reg_val=0x%04x", + icalk, vcalk, vcalb, reg_val); + + ret = aw_dev->ops.aw_i2c_write(aw_dev, desc->vcalb_reg, reg_val); + + aw_dev_info(aw_dev->dev, "done"); + + return ret; +} + + + +/*pwd enable update reg*/ +static int aw_dev_reg_fw_update(struct aw_device *aw_dev) +{ + int i = 0; + unsigned int reg_addr = 0; + unsigned int reg_val = 0; + unsigned int read_val; + int ret = -1; + unsigned int init_volume = 0; + unsigned int efcheck_val = 0; + struct aw_int_desc *int_desc = &aw_dev->int_desc; + struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; + struct aw_bstctrl_desc *bstctrl_desc = &aw_dev->bstctrl_desc; + struct aw_sec_data_desc *reg_data; + int16_t *data; + int data_len; + + char *prof_name = aw_dev_get_prof_name(aw_dev, aw_dev->set_prof); + + if (prof_name == NULL) { + aw_dev_err(aw_dev->dev, "get prof name failed"); + return -EINVAL; + } + + reg_data = aw_dev_get_prof_data(aw_dev, aw_dev->set_prof, AW_PROFILE_DATA_TYPE_REG); + if (reg_data == NULL) + return -EINVAL; + + data = (int16_t *)reg_data->data; + data_len = reg_data->len >> 1; + + for (i = 0; i < data_len; i += 2) { + reg_addr = data[i]; + reg_val = data[i + 1]; + + if (reg_addr == int_desc->mask_reg) { + int_desc->int_mask = reg_val; + reg_val = int_desc->mask_default; + } + + if (aw_dev->bstcfg_enable) { + if (reg_addr == profctrl_desc->reg) { + profctrl_desc->cfg_prof_mode = + reg_val & (~profctrl_desc->mask); + } + + if (reg_addr == bstctrl_desc->reg) { + bstctrl_desc->cfg_bst_type = + reg_val & (~bstctrl_desc->mask); + } + } + + /*keep amppd status*/ + if (reg_addr == aw_dev->amppd_desc.reg) { + aw_dev->amppd_st = reg_val & (~aw_dev->amppd_desc.mask); + aw_dev_info(aw_dev->dev, "amppd_st=0x%04x", aw_dev->amppd_st); + aw_dev->ops.aw_i2c_read(aw_dev, + (unsigned char)reg_addr, + (unsigned int *)&read_val); + read_val &= (~aw_dev->amppd_desc.mask); + reg_val &= aw_dev->amppd_desc.mask; + reg_val |= read_val; + } + + /*keep pwd status*/ + if (reg_addr == aw_dev->pwd_desc.reg) { + aw_dev->ops.aw_i2c_read(aw_dev, + (unsigned char)reg_addr, + (unsigned int *)&read_val); + read_val &= (~aw_dev->pwd_desc.mask); + reg_val &= aw_dev->pwd_desc.mask; + reg_val |= read_val; + } + /*keep mute status*/ + if (reg_addr == aw_dev->mute_desc.reg) { + /*get bin value*/ + aw_dev->mute_st = reg_val & (~aw_dev->mute_desc.mask); + aw_dev_info(aw_dev->dev, "mute_st=0x%04x", aw_dev->mute_st); + aw_dev->ops.aw_i2c_read(aw_dev, + (unsigned char)reg_addr, + (unsigned int *)&read_val); + read_val &= (~aw_dev->mute_desc.mask); + reg_val &= aw_dev->mute_desc.mask; + reg_val |= read_val; + } + + if (reg_addr == aw_dev->efcheck_desc.reg) { + efcheck_val = reg_val & (~aw_dev->efcheck_desc.mask); + if (efcheck_val == aw_dev->efcheck_desc.or_val) + aw_dev->efuse_check = AW_EF_OR_CHECK; + else + aw_dev->efuse_check = AW_EF_AND_CHECK; + + aw_dev_info(aw_dev->dev, "efuse check: %d", aw_dev->efuse_check); + } + + if (reg_addr == aw_dev->vcalb_desc.vcalb_reg) + continue; + + aw_dev_dbg(aw_dev->dev, "reg=0x%04x, val = 0x%04x", + (uint16_t)reg_addr, (uint16_t)reg_val); + ret = aw_dev->ops.aw_i2c_write(aw_dev, + (unsigned char)reg_addr, + (unsigned int)reg_val); + if (ret < 0) + break; + } + + ret = aw_dev_set_vcalb(aw_dev); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "can't set vcalb"); + return ret; + } + + aw_dev->ops.aw_get_volume(aw_dev, &init_volume); + aw_dev->volume_desc.init_volume = init_volume; + + /*keep min volume*/ + aw_dev->ops.aw_set_volume(aw_dev, aw_dev->volume_desc.mute_volume); + + aw_dev_info(aw_dev->dev, "load %s done", prof_name); + + return ret; +} + +static void aw_dev_fade_in(struct aw_device *aw_dev) +{ + int i = 0; + int fade_step = aw_dev->vol_step; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + int fade_flag = aw882xx->fade_flag; + struct aw_volume_desc *desc = &aw_dev->volume_desc; + + if (fade_step == 0 || g_fade_in_time == 0 || fade_flag == 0) { + aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); + return; + } + /*volume up*/ + for (i = desc->mute_volume; i >= desc->init_volume; i -= fade_step) { + aw_dev->ops.aw_set_volume(aw_dev, i); + usleep_range(g_fade_in_time, g_fade_in_time + 10); + } + if (i != desc->init_volume) + aw_dev->ops.aw_set_volume(aw_dev, desc->init_volume); +} + +static void aw_dev_fade_out(struct aw_device *aw_dev) +{ + int i = 0; + unsigned start_volume = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + int fade_flag = aw882xx->fade_flag; + int fade_step = aw_dev->vol_step; + struct aw_volume_desc *desc = &aw_dev->volume_desc; + + if (fade_step == 0 || g_fade_out_time == 0 || fade_flag == 0) { + aw_dev->ops.aw_set_volume(aw_dev, desc->mute_volume); + return; + } + + aw_dev->ops.aw_get_volume(aw_dev, &start_volume); + i = start_volume; + for (i = start_volume; i <= desc->mute_volume; i += fade_step) { + aw_dev->ops.aw_set_volume(aw_dev, i); + usleep_range(g_fade_out_time, g_fade_out_time + 10); + } + if (i != desc->mute_volume) { + aw_dev->ops.aw_set_volume(aw_dev, desc->mute_volume); + usleep_range(g_fade_out_time, g_fade_out_time + 10); + } +} + +static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd) +{ + struct aw_pwd_desc *pwd_desc = &aw_dev->pwd_desc; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (pwd) { + aw_dev->ops.aw_i2c_write_bits(aw_dev, pwd_desc->reg, + pwd_desc->mask, + pwd_desc->enable); + } else { + aw_dev->ops.aw_i2c_write_bits(aw_dev, pwd_desc->reg, + pwd_desc->mask, + pwd_desc->disable); + } + aw_dev_info(aw_dev->dev, "done"); +} + +static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd) +{ + struct aw_amppd_desc *amppd_desc = &aw_dev->amppd_desc; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (amppd) { + aw_dev->ops.aw_i2c_write_bits(aw_dev, amppd_desc->reg, + amppd_desc->mask, + amppd_desc->enable); + } else { + aw_dev->ops.aw_i2c_write_bits(aw_dev, amppd_desc->reg, + amppd_desc->mask, + amppd_desc->disable); + } + aw_dev_info(aw_dev->dev, "done"); +} + +static void aw_dev_mute(struct aw_device *aw_dev, bool mute) +{ + struct aw_mute_desc *mute_desc = &aw_dev->mute_desc; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + aw_dev_dbg(aw_dev->dev, "enter"); + + if (mute) { + aw_dev_fade_out(aw_dev); + aw_dev->ops.aw_i2c_write_bits(aw_dev, mute_desc->reg, + mute_desc->mask, + mute_desc->enable); + } else { + if (aw882xx->aw882xx_ramp_status == AW882XX_RAMP_ON) { + aw_dev->ops.aw_i2c_write_bits(aw_dev, mute_desc->reg, + mute_desc->mask, + mute_desc->disable); + aw_dev_fade_in(aw_dev); + } else if (aw882xx->aw882xx_ramp_status == AW882XX_RAMP_OFF) { + g_fade_in_time = AW_1000_US / 10; + aw_dev->ops.aw_i2c_write_bits(aw_dev, mute_desc->reg, + mute_desc->mask, + mute_desc->disable); + aw_dev_fade_in(aw_dev); + } + } + aw_dev_info(aw_dev->dev, "done"); +} + + +int aw_dev_get_int_status(struct aw_device *aw_dev, uint16_t *int_status) +{ + int ret = -1; + unsigned int reg_val = 0; + + ret = aw_dev->ops.aw_i2c_read(aw_dev, aw_dev->int_desc.st_reg, ®_val); + if (ret < 0) + aw_dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret); + else + *int_status = reg_val; + + aw_dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", *int_status); + return ret; +} + +void aw_dev_clear_int_status(struct aw_device *aw_dev) +{ + uint16_t int_status = 0; + + /*read int status and clear*/ + aw_dev_get_int_status(aw_dev, &int_status); + /*make suer int status is clear*/ + aw_dev_get_int_status(aw_dev, &int_status); + aw_dev_info(aw_dev->dev, "done"); +} + +int aw_dev_set_intmask(struct aw_device *aw_dev, bool flag) +{ + struct aw_int_desc *desc = &aw_dev->int_desc; + int ret = -1; + + if (flag) + ret = aw_dev->ops.aw_i2c_write(aw_dev, desc->mask_reg, + desc->int_mask); + else + ret = aw_dev->ops.aw_i2c_write(aw_dev, desc->mask_reg, + desc->mask_default); + aw_dev_info(aw_dev->dev, "done"); + return ret; +} + +static int aw_dev_mode1_pll_check(struct aw_device *aw_dev) +{ + int ret = -1; + unsigned char i; + unsigned int reg_val = 0; + struct aw_sysst_desc *desc = &aw_dev->sysst_desc; + + for (i = 0; i < AW_DEV_SYSST_CHECK_MAX; i++) { + aw_dev->ops.aw_i2c_read(aw_dev, desc->reg, ®_val); + if (reg_val & desc->pll_check) { + ret = 0; + break; + } else { + aw_dev_dbg(aw_dev->dev, "check pll lock fail, cnt=%d, reg_val=0x%04x", + i, reg_val); + usleep_range(AW_2000_US, AW_2000_US + 10); + } + } + if (ret < 0) + aw_dev_err(aw_dev->dev, "check fail"); + else + aw_dev_info(aw_dev->dev, "done"); + + return ret; +} + +static int aw_dev_mode2_pll_check(struct aw_device *aw_dev) +{ + int ret = -1; + unsigned int reg_val = 0; + struct aw_cco_mux_desc *cco_mux_desc = &aw_dev->cco_mux_desc; + + aw_dev->ops.aw_i2c_read(aw_dev, cco_mux_desc->reg, ®_val); + reg_val &= (~cco_mux_desc->mask); + aw_dev_dbg(aw_dev->dev, "REG_PLLCTRL1_bit14 = 0x%04x", reg_val); + if (reg_val == cco_mux_desc->divided_val) { + aw_dev_dbg(aw_dev->dev, "CCO_MUX is already divided"); + return ret; + } + + /* change mode2 */ + aw_dev->ops.aw_i2c_write_bits(aw_dev, cco_mux_desc->reg, + cco_mux_desc->mask, cco_mux_desc->bypass_val); + ret = aw_dev_mode1_pll_check(aw_dev); + + /* change mode1 */ + aw_dev->ops.aw_i2c_write_bits(aw_dev, cco_mux_desc->reg, + cco_mux_desc->mask, cco_mux_desc->divided_val); + if (ret == 0) { + usleep_range(AW_2000_US, AW_2000_US + 10); + ret = aw_dev_mode1_pll_check(aw_dev); + } + + return ret; +} + +static int aw_dev_syspll_check(struct aw_device *aw_dev) +{ + int ret = -1; + + ret = aw_dev_mode1_pll_check(aw_dev); + if (ret < 0) { + aw_dev_err(aw_dev->dev, + "mode1 check iis failed try switch to mode2 check"); + + ret= aw_dev_mode2_pll_check(aw_dev); + if (ret < 0) + aw_dev_err(aw_dev->dev, "mode2 check iis failed"); + } + + return ret; +} + +static int aw_dev_sysst_check(struct aw_device *aw_dev) +{ + int ret = -1; + unsigned char i; + unsigned int reg_val = 0; + struct aw_sysst_desc *desc = &aw_dev->sysst_desc; + + for (i = 0; i < AW_DEV_SYSST_CHECK_MAX; i++) { + aw_dev->ops.aw_i2c_read(aw_dev, desc->reg, ®_val); + if (((reg_val & (~desc->mask)) & desc->st_check) == desc->st_check) { + ret = 0; + break; + } else { + aw_dev_info(aw_dev->dev, "check fail, cnt=%d, reg_val=0x%04x", + i, reg_val); + usleep_range(AW_2000_US, AW_2000_US + 10); + } + } + if (ret < 0) + aw_dev_err(aw_dev->dev, "check fail"); + else + aw_dev_info(aw_dev->dev, "done"); + + return ret; +} + +int aw_dev_get_profile_count(struct aw_device *aw_dev) +{ + if (aw_dev == NULL) { + aw_pr_err("aw_dev is NULL"); + return -ENOMEM; + } + + return aw_dev->prof_info.count; +} + +int aw_dev_get_profile_name(struct aw_device *aw_dev, char *name, int index) +{ + int dev_profile_id; + + if (index < 0) { + aw_dev_err(aw_dev->dev, "index[%d] error", index); + return -EINVAL; + } + + if (index > aw_dev->prof_info.count) { + aw_dev_err(aw_dev->dev, "index[%d] overflow dev prof num[%d]", + index, aw_dev->prof_info.count); + return -EINVAL; + } + + if (aw_dev->prof_info.prof_desc[index].id >= AW_PROFILE_MAX) { + aw_dev_err(aw_dev->dev, "can not find match id "); + return -EINVAL; + } + + dev_profile_id = aw_dev->prof_info.prof_desc[index].id; + + strlcpy(name, profile_name[dev_profile_id], + strlen(profile_name[dev_profile_id]) + 1); + /*aw_dev_dbg(aw_dev->dev, "%s: get name [%s]", + __func__, profile_name[dev_profile_id]);*/ + return 0; +} + +int aw_dev_check_profile_index(struct aw_device *aw_dev, int index) +{ + if ((index >= aw_dev->prof_info.count) || (index < 0)) + return -EINVAL; + else + return 0; +} + +int aw_dev_get_profile_index(struct aw_device *aw_dev) +{ + return aw_dev->set_prof; +} + +int aw_dev_set_profile_index(struct aw_device *aw_dev, int index) +{ + if (index >= aw_dev->prof_info.count || index < 0) { + return -EINVAL; + } else { + aw_dev->set_prof = index; + aw_dev_info(aw_dev->dev, "set prof[%s]", + profile_name[aw_dev->prof_info.prof_desc[index].id]); + } + mutex_lock(&g_ext_dsp_prof_wr_lock); + ext_dsp_prof_write = AW_EXT_DSP_WRITE_NONE; + mutex_unlock(&g_ext_dsp_prof_wr_lock); + + return 0; +} + +int aw_dev_get_fade_vol_step(struct aw_device *aw_dev) +{ + return aw_dev->vol_step; +} + +void aw_dev_set_fade_vol_step(struct aw_device *aw_dev, unsigned int step) +{ + aw_dev->vol_step = step; +} + +void aw_dev_get_fade_time(unsigned int *time, bool fade_in) +{ + if (fade_in) + *time = g_fade_in_time; + else + *time = g_fade_out_time; +} + +void aw_dev_set_fade_time(unsigned int time, bool fade_in) +{ + if (fade_in) + g_fade_in_time = time; + else + g_fade_out_time = time; +} + +/*init aw_device*/ +void aw_dev_deinit(struct aw_device *aw_dev) +{ + if (aw_dev == NULL) + return; + + if (aw_dev->prof_info.prof_desc != NULL) { + kfree(aw_dev->prof_info.prof_desc); + aw_dev->prof_info.prof_desc = NULL; + aw_dev->prof_info.count = 0; + } +} + +int aw_dev_get_cali_re(struct aw_device *aw_dev, int32_t *cali_re) +{ + + return aw_dsp_read_cali_re(aw_dev, cali_re); +} + +int aw_dev_dc_status(struct aw_device *aw_dev) +{ + return aw_dsp_get_dc_status(aw_dev); +} + +int aw_dev_status(struct aw_device *aw_dev) +{ + return aw_dev->status; +} + +int aw_dev_init_cali_re(struct aw_device *aw_dev) +{ + int ret = 0; + + if (aw_dev->cali_desc.mode) { + if (aw_dev->cali_desc.cali_re == AW_ERRO_CALI_VALUE) { + ret = aw_cali_read_re_from_nvram(&aw_dev->cali_desc.cali_re, aw_dev->channel); + if (ret) { + aw_dev_info(aw_dev->dev, "read nvram cali failed, use default Re"); + aw_dev->cali_desc.cali_re = AW_ERRO_CALI_VALUE; + } + } + } else { + aw_dev_info(aw_dev->dev, "no cali, needn't init cali re"); + } + return ret; +} + +static void aw_dev_soft_reset(struct aw_device *aw_dev) +{ + struct aw_soft_rst *reset = &aw_dev->soft_rst; + + aw_dev->ops.aw_i2c_write(aw_dev, reset->reg, reset->reg_value); + aw_dev_info(aw_dev->dev, "soft reset done"); +} + +int aw_device_irq_reinit(struct aw_device *aw_dev) +{ + int ret; + + /*reg re load*/ + ret = aw_dev_reg_fw_update(aw_dev); + if (ret < 0) + return ret; + + + return 0; +} + +int aw_device_init(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + /*acf_hdr_t *hdr;*/ + int ret; + + if (aw_dev == NULL || aw_cfg == NULL) { + aw_dev_err(aw_dev->dev, "pointer is NULL"); + return -ENOMEM; + } + + ret = aw_dev_cfg_load(aw_dev, aw_cfg); + if (ret) { + aw_dev_deinit(aw_dev); + aw_dev_err(aw_dev->dev, "aw_dev acf load failed"); + return -EINVAL; + } + + aw_dev_soft_reset(aw_dev); + + aw_dev->cur_prof = AW_PROFILE_MUSIC; + aw_dev->set_prof = AW_PROFILE_MUSIC; + ret = aw_dev_reg_fw_update(aw_dev); + if (ret < 0) + return ret; + + if (aw_dev->ops.aw_frcset_check) { + ret = aw_dev->ops.aw_frcset_check(aw_dev); + if (ret) + return ret; + } + + aw_dev->status = AW_DEV_PW_ON; + aw_device_stop(aw_dev); + + aw_dev_info(aw_dev->dev, "init done"); + return 0; +} + +int aw_dev_reg_update(struct aw_device *aw_dev, bool force) +{ + int ret; + + if (force) { + aw_dev_soft_reset(aw_dev); + ret = aw_dev_reg_fw_update(aw_dev); + if (ret < 0) + return ret; + } else { + if (aw_dev->cur_prof != aw_dev->set_prof) { + ret = aw_dev_reg_fw_update(aw_dev); + if (ret < 0) + return ret; + } + } + + aw_dev->cur_prof = aw_dev->set_prof; + + aw_dev_info(aw_dev->dev, "done"); + return 0; +} + +static void aw_dev_cali_re_update(struct aw_device *aw_dev) +{ + struct aw_cali_desc *desc = &aw_dev->cali_desc; + + if (desc->mode) { + if ((desc->cali_re >= aw_dev->re_min) && + (desc->cali_re <= aw_dev->re_max)) + aw_dsp_write_cali_re(aw_dev, desc->cali_re); + } +} + +int aw_dev_prof_update(struct aw_device *aw_dev, bool force) +{ + int ret; + + /*if power on need off -- load -- on*/ + if (aw_dev->status == AW_DEV_PW_ON) { + aw_device_stop(aw_dev); + + ret = aw_dev_reg_update(aw_dev, force); + if (ret) { + aw_dev_err(aw_dev->dev, "fw update failed "); + return ret; + } + + ret = aw_device_start(aw_dev); + if (ret) { + aw_dev_err(aw_dev->dev, "start failed "); + return ret; + } + } else { + /*if pa off , only update set_prof value*/ + aw_dev_info(aw_dev->dev, "set prof[%d] done !", aw_dev->set_prof); + } + + aw_dev_info(aw_dev->dev, "update done !"); + return 0; +} + +static void aw_dev_boost_type_set(struct aw_device *aw_dev) +{ + struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; + struct aw_bstctrl_desc *bstctrl_desc = &aw_dev->bstctrl_desc; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (aw_dev->bstcfg_enable) { + /*set spk mode*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, profctrl_desc->reg, + profctrl_desc->mask, profctrl_desc->spk_mode); + + /*force boost*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, bstctrl_desc->reg, + bstctrl_desc->mask, bstctrl_desc->frc_bst); + + aw_dev_dbg(aw_dev->dev, "boost type set done"); + } +} + +static void aw_dev_boost_type_recover(struct aw_device *aw_dev) +{ + + struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; + struct aw_bstctrl_desc *bstctrl_desc = &aw_dev->bstctrl_desc; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (aw_dev->bstcfg_enable) { + /*set transprant*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, bstctrl_desc->reg, + bstctrl_desc->mask, bstctrl_desc->tsp_type); + + usleep_range(AW_5000_US, AW_5000_US + 50); + /*set cfg boost type*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, bstctrl_desc->reg, + bstctrl_desc->mask, bstctrl_desc->cfg_bst_type); + + /*set cfg prof mode*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, profctrl_desc->reg, + profctrl_desc->mask, profctrl_desc->cfg_prof_mode); + + aw_dev_dbg(aw_dev->dev, "boost type recover done"); + } +} + +int aw_device_start(struct aw_device *aw_dev) +{ + int ret; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (aw_dev->status == AW_DEV_PW_ON) { + aw_dev_info(aw_dev->dev, "already power on"); + return 0; + } + + /*set froce boost*/ + aw_dev_boost_type_set(aw_dev); + + /*power on*/ + aw_dev_pwd(aw_dev, false); + usleep_range(AW_2000_US, AW_2000_US + 10); + + ret = aw_dev_syspll_check(aw_dev); + if (ret < 0) { + aw_dev_reg_dump(aw_dev); + aw_dev_pwd(aw_dev, true); + aw_dev_dbg(aw_dev->dev, "pll check failed cannot start"); + return ret; + } + + /*amppd on*/ + aw_dev_amppd(aw_dev, false); + usleep_range(AW_1000_US, AW_1000_US + 50); + + /*check i2s status*/ + ret = aw_dev_sysst_check(aw_dev); + if (ret < 0) { + aw_dev_reg_dump(aw_dev); + /*close tx feedback*/ + if (aw_dev->ops.aw_i2s_enable) + aw_dev->ops.aw_i2s_enable(aw_dev, false); + /*clear interrupt*/ + aw_dev_clear_int_status(aw_dev); + /*close amppd*/ + aw_dev_amppd(aw_dev, true); + /*power down*/ + aw_dev_pwd(aw_dev, true); + return -EINVAL; + } + + /*boost type recover*/ + aw_dev_boost_type_recover(aw_dev); + + /*enable tx feedback*/ + if (aw_dev->ops.aw_i2s_enable) + aw_dev->ops.aw_i2s_enable(aw_dev, true); + + if (aw_dev->amppd_st) { + aw_dev_amppd(aw_dev, true); + } + + if (aw_dev->ops.aw_reg_force_set) + aw_dev->ops.aw_reg_force_set(aw_dev); + + if (!aw_dev->mute_st) { + /*close mute*/ + aw_dev_mute(aw_dev, false); + } + + /*clear inturrupt*/ + aw_dev_clear_int_status(aw_dev); + /*set inturrupt mask*/ + aw_dev_set_intmask(aw_dev, true); + + aw_monitor_start(&aw_dev->monitor_desc); + aw_dev_cali_re_update(aw_dev); + + aw_dev->status = AW_DEV_PW_ON; + aw_dev_info(aw_dev->dev, "done"); + return 0; +} + +int aw_device_stop(struct aw_device *aw_dev) +{ + aw_dev_dbg(aw_dev->dev, "enter"); + + if (aw_dev->status == AW_DEV_PW_OFF) { + aw_dev_dbg(aw_dev->dev, "already power off"); + return 0; + } + + aw_dev->status = AW_DEV_PW_OFF; + + aw_monitor_stop(&aw_dev->monitor_desc); + /*clear interrupt*/ + aw_dev_clear_int_status(aw_dev); + + /*set defaut int mask*/ + aw_dev_set_intmask(aw_dev, false); + + /*set mute*/ + aw_dev_mute(aw_dev, true); + + /*close tx feedback*/ + if (aw_dev->ops.aw_i2s_enable) + aw_dev->ops.aw_i2s_enable(aw_dev, false); + + usleep_range(AW_1000_US, AW_1000_US + 100); + + /*enable amppd*/ + aw_dev_amppd(aw_dev, true); + + /*set power down*/ + aw_dev_pwd(aw_dev, true); + + ext_dsp_prof_write = AW_EXT_DSP_WRITE_NONE; + aw_dev_info(aw_dev->dev, "done"); + return 0; +} + +int aw_dev_set_afe_module_en(int type, int enable) +{ + return aw_dsp_set_afe_module_en(type, enable); +} + +int aw_dev_get_afe_module_en(int type, int *status) +{ + return aw_dsp_get_afe_module_en(type, status); +} + +int aw_dev_set_copp_module_en(bool enable) +{ + return aw_dsp_set_copp_module_en(enable); +} + +int aw_dev_set_spin(int spin_mode) +{ + return aw_dsp_write_spin(spin_mode); +} + +int aw_dev_get_spin(int *spin_mode) +{ + return aw_dsp_read_spin(spin_mode); +} + +int aw_dev_set_algo_prof_data(struct aw_device *aw_dev, void *prof_data, unsigned int prof_len) +{ + return aw_dsp_set_algo_prof_data(aw_dev, prof_data, prof_len); +} + +int aw_dev_set_algo_prof(struct aw_device *aw_dev, int prof_id) +{ + return aw_dsp_set_algo_prof(aw_dev, prof_id); +} + +int aw_dev_get_algo_prof(struct aw_device *aw_dev, int *prof_id) +{ + return aw_dsp_get_algo_prof(aw_dev, prof_id); +} + +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS +int aw_dev_set_algo_params_path(struct aw_device *aw_dev) +{ + return aw_dsp_set_algo_params_path(aw_dev); +} +#endif + +#ifdef AW_SPIN_ENABLE +int aw_dev_set_spin_param(struct aw_device *aw_dev, uint32_t enable, uint32_t relase_time) +{ + return aw_dsp_set_spin_param(aw_dev, enable, relase_time); +} + +int aw_dev_get_spin_param(struct aw_device *aw_dev, uint32_t *enable, uint32_t *relase_time) +{ + return aw_dsp_get_spin_param(aw_dev, enable, relase_time); +} +#endif + +static void aw_device_parse_sound_channel_dt(struct aw_device *aw_dev) +{ + int ret; + uint32_t channel_value; + + aw_dev->channel = AW_DEV_CH_PRI_L; + ret = of_property_read_u32(aw_dev->dev->of_node, "sound-channel", &channel_value); + if (ret < 0) { + aw_dev_info(aw_dev->dev, "read sound-channel failed,use default"); + return; + } + + aw_dev_dbg(aw_dev->dev, "read sound-channel value is : %d", channel_value); + if (channel_value >= AW_DEV_CH_MAX) { + channel_value = AW_DEV_CH_PRI_L; + } + aw_dev->channel = channel_value; +} + +static void aw_device_parse_dt(struct aw_device *aw_dev) +{ + aw_device_parse_sound_channel_dt(aw_dev); + aw_device_parse_topo_id_dt(aw_dev); + aw_device_parse_port_id_dt(aw_dev); +} + +int aw_dev_get_list_head(struct list_head **head) +{ + if (list_empty(&g_dev_list)) + return -EINVAL; + + *head = &g_dev_list; + + return 0; +} + +void aw_device_algo_path_init(struct aw_device *aw_dev) +{ + + memcpy(aw_dev->algo_path, AW_ALGO_PARAMS_PATH, sizeof(AW_ALGO_PARAMS_PATH)); + aw_dev_dbg(aw_dev->dev, "aw_device_algo_path_init"); +} + +int aw_device_probe(struct aw_device *aw_dev) +{ + int ret; + INIT_LIST_HEAD(&aw_dev->list_node); + aw_dev_dbg(aw_dev->dev, "aw_device_probe 01"); + aw_device_parse_dt(aw_dev); + ret = aw_cali_init(&aw_dev->cali_desc); + if (ret) + return ret; + aw_dev_dbg(aw_dev->dev, "aw_device_probe 02"); + aw_monitor_init(&aw_dev->monitor_desc); + /*aw_afe_init();*/ + + aw_device_algo_path_init(aw_dev); + + mutex_lock(&g_dev_lock); + list_add(&aw_dev->list_node, &g_dev_list); + mutex_unlock(&g_dev_lock); + + return 0; +} + +int aw_device_remove(struct aw_device *aw_dev) +{ + aw_monitor_deinit(&aw_dev->monitor_desc); + aw_cali_deinit(&aw_dev->cali_desc); + /*aw_afe_deinit();*/ + return 0; +} + diff --git a/techpack/audio/asoc/codecs/aw882xxacf/aw_device.h b/techpack/audio/asoc/codecs/aw882xxacf/aw_device.h new file mode 100644 index 000000000000..71c231097e9a --- /dev/null +++ b/techpack/audio/asoc/codecs/aw882xxacf/aw_device.h @@ -0,0 +1,328 @@ +#ifndef __AWINIC_DEVICE_FILE_H__ +#define __AWINIC_DEVICE_FILE_H__ +#include "aw_data_type.h" +#include "aw_calib.h" +#include "aw_monitor.h" + + +#define AW_VOLUME_STEP_DB (6 * 2) +#define AW_REG_NONE (0xFF) +#define AW_NAME_MAX (50) +#define ALGO_VERSION_MAX (80) +#define AW_ALGO_PATH_MAX (256) + + +enum { + AW_1000_US = 1000, + AW_2000_US = 2000, + AW_3000_US = 3000, + AW_4000_US = 4000, + AW_5000_US = 5000, + AW_10000_US = 10000, +}; + +struct aw_device; + +enum { + AW_DEV_TYPE_OK = 0, + AW_DEV_TYPE_NONE = 1, +}; + + +enum { + AW_EF_AND_CHECK = 0, + AW_EF_OR_CHECK, +}; + +enum { + AW_DEV_CH_PRI_L = 0, + AW_DEV_CH_PRI_R = 1, + AW_DEV_CH_SEC_L = 2, + AW_DEV_CH_SEC_R = 3, + AW_DEV_CH_MAX, +}; + +enum AW_DEV_INIT { + AW_DEV_INIT_ST = 0, + AW_DEV_INIT_OK = 1, + AW_DEV_INIT_NG = 2, +}; + +enum AW_DEV_STATUS { + AW_DEV_PW_OFF = 0, + AW_DEV_PW_ON, +}; + +enum AW_DEV_FW_STATUS { + AW_DEV_FW_FAILED = 0, + AW_DEV_FW_OK, +}; + +enum { + AW_DEV_CH_GROUP_PRI = 0, + AW_DEV_CH_GROUP_SEC = 1, + AW_DEV_CH_GROUP_MAX, +}; + +struct aw_device_ops { + int (*aw_i2c_write)(struct aw_device *aw_dev, unsigned char reg_addr, unsigned int reg_data); + int (*aw_i2c_read)(struct aw_device *aw_dev, unsigned char reg_addr, unsigned int *reg_data); + int (*aw_i2c_write_bits)(struct aw_device *aw_dev, unsigned char reg_addr, unsigned int mask, unsigned int reg_data); + int (*aw_set_volume)(struct aw_device *aw_dev, unsigned int value); + int (*aw_get_volume)(struct aw_device *aw_dev, unsigned int *value); + unsigned int (*aw_reg_val_to_db)(unsigned int value); + void (*aw_i2s_enable)(struct aw_device *aw_dev, bool flag); + bool (*aw_check_wr_access)(int reg); + bool (*aw_check_rd_access)(int reg); + int (*aw_get_reg_num)(void); + int (*aw_get_version)(char *buf, int size); + int (*aw_get_dev_num)(void); + void (*aw_set_algo)(struct aw_device *aw_dev); + unsigned int (*aw_get_irq_type)(struct aw_device *aw_dev, unsigned int value); + void (*aw_reg_force_set)(struct aw_device *aw_dev); + int (*aw_frcset_check)(struct aw_device *aw_dev); +}; + +struct aw_int_desc { + unsigned int mask_reg; /*interrupt mask reg*/ + unsigned int st_reg; /*interrupt status reg*/ + unsigned int mask_default; /*default mask close all*/ + unsigned int int_mask; /*set mask*/ +}; + +struct aw_soft_rst { + int reg; + int reg_value; +}; + +struct aw_pwd_desc { + unsigned int reg; + unsigned int mask; + unsigned int enable; + unsigned int disable; +}; + +struct aw_amppd_desc { + unsigned int reg; + unsigned int mask; + unsigned int enable; + unsigned int disable; +}; + +struct aw_bop_desc { + unsigned int reg; + unsigned int mask; + unsigned int enable; + unsigned int disbale; +}; + +struct aw_vcalb_desc { + unsigned int icalk_reg; + unsigned int icalk_reg_mask; + unsigned int icalk_shift; + unsigned int icalkl_reg; + unsigned int icalkl_reg_mask; + unsigned int icalkl_shift; + unsigned int icalk_sign_mask; + unsigned int icalk_neg_mask; + int icalk_value_factor; + + unsigned int vcalk_reg; + unsigned int vcalk_reg_mask; + unsigned int vcalk_shift; + unsigned int vcalkl_reg; + unsigned int vcalkl_reg_mask; + unsigned int vcalkl_shift; + unsigned int vcalk_sign_mask; + unsigned int vcalk_neg_mask; + int vcalk_value_factor; + + unsigned int vcalb_reg; + int cabl_base_value; + int vcal_factor; +}; + +struct aw_mute_desc { + unsigned int reg; + unsigned int mask; + unsigned int enable; + unsigned int disable; +}; + +struct aw_sysst_desc { + unsigned int reg; + unsigned int mask; + unsigned int st_check; + unsigned int pll_check; +}; + +struct aw_profctrl_desc { + unsigned int reg; + unsigned int mask; + unsigned int spk_mode; + unsigned int cfg_prof_mode; +}; + +struct aw_bstctrl_desc { + unsigned int reg; + unsigned int mask; + unsigned int frc_bst; + unsigned int tsp_type; + unsigned int cfg_bst_type; +}; + +struct aw_cco_mux_desc { + unsigned int reg; + unsigned int mask; + unsigned int divided_val; + unsigned int bypass_val; +}; + +struct aw_volume_desc { + unsigned int reg; + unsigned int mask; + unsigned int shift; + int init_volume; + int mute_volume; +}; + +struct aw_voltage_desc { + unsigned int reg; + unsigned int vbat_range; + unsigned int int_bit; +}; + +struct aw_temperature_desc { + unsigned int reg; + unsigned int sign_mask; + unsigned int neg_mask; +}; + +struct aw_ipeak_desc { + unsigned int reg; + unsigned int mask; +}; + +struct aw_container { + int len; + uint8_t data[]; +}; + +struct aw_efcheck_desc { + unsigned int reg; + unsigned int mask; + unsigned int and_val; + unsigned int or_val; +}; + +struct aw_device { + int index; + int status; + int bstcfg_enable; + int frcset_en; + int bop_en; + int efuse_check; + unsigned int mute_st; + unsigned int amppd_st; + + unsigned char cur_prof; /*current profile index*/ + unsigned char set_prof; /*set profile index*/ + unsigned int channel; /*pa channel select*/ + unsigned int vol_step; + unsigned int re_max; + unsigned int re_min; + + int cur_gain; + + struct device *dev; + struct i2c_client *i2c; + char acf_name[AW_NAME_MAX]; + char monitor_name[AW_NAME_MAX]; + void *private_data; + char algo_path[AW_ALGO_PATH_MAX]; + int pre_prof_id; + + struct aw_int_desc int_desc; + struct aw_pwd_desc pwd_desc; + struct aw_amppd_desc amppd_desc; + struct aw_mute_desc mute_desc; + struct aw_vcalb_desc vcalb_desc; + struct aw_sysst_desc sysst_desc; + struct aw_profctrl_desc profctrl_desc; + struct aw_bstctrl_desc bstctrl_desc; + struct aw_cco_mux_desc cco_mux_desc; + struct aw_voltage_desc voltage_desc; + struct aw_temperature_desc temp_desc; + struct aw_ipeak_desc ipeak_desc; + struct aw_volume_desc volume_desc; + struct aw_prof_info prof_info; + struct aw_prof_info skt_prof_info; + struct aw_cali_desc cali_desc; + struct aw_monitor_desc monitor_desc; + struct aw_soft_rst soft_rst; + struct aw_bop_desc bop_desc; + struct aw_efcheck_desc efcheck_desc; + struct aw_device_ops ops; + struct list_head list_node; +}; + +struct aw_sec_data_desc *aw_dev_get_algo_prof_data(struct aw_device *aw_dev, int prof_id); +int aw_dev_load_acf_check(struct aw_container *aw_cfg); +void aw_dev_deinit(struct aw_device *aw_dev); +int aw_device_init(struct aw_device *aw_dev, struct aw_container *aw_cfg); +int aw_dev_parse_skt_bin(struct aw_device *aw_dev, struct aw_container *aw_cfg); +int aw_device_start(struct aw_device *aw_dev); +int aw_device_stop(struct aw_device *aw_dev); +int aw_dev_reg_update(struct aw_device *aw_dev, bool force); +int aw_device_irq_reinit(struct aw_device *aw_dev); + +/*profile*/ +int aw_dev_prof_update(struct aw_device *aw_dev, bool force); +int aw_dev_get_profile_count(struct aw_device *aw_dev); +int aw_dev_get_profile_name(struct aw_device *aw_dev, char *name, int index); +int aw_dev_check_profile_index(struct aw_device *aw_dev, int index); +int aw_dev_get_profile_index(struct aw_device *aw_dev); +int aw_dev_set_profile_index(struct aw_device *aw_dev, int index); + +/*re*/ +int aw_dev_get_cali_re(struct aw_device *aw_dev, int32_t *cali_re); +int aw_dev_init_cali_re(struct aw_device *aw_dev); +int aw_dev_dc_status(struct aw_device *aw_dev); + +/*interrupt*/ +int aw_dev_status(struct aw_device *aw_dev); +int aw_dev_get_int_status(struct aw_device *aw_dev, uint16_t *int_status); +void aw_dev_clear_int_status(struct aw_device *aw_dev); +int aw_dev_set_intmask(struct aw_device *aw_dev, bool flag); + +/*fade int / out*/ +void aw_dev_set_fade_vol_step(struct aw_device *aw_dev, unsigned int step); +int aw_dev_get_fade_vol_step(struct aw_device *aw_dev); +void aw_dev_get_fade_time(unsigned int *time, bool fade_in); +void aw_dev_set_fade_time(unsigned int time, bool fade_in); + +/*dsp kcontrol*/ +int aw_dev_set_afe_module_en(int type, int enable); +int aw_dev_get_afe_module_en(int type, int *status); +int aw_dev_set_copp_module_en(bool enable); +int aw_dev_set_spin(int spin_mode); +int aw_dev_get_spin(int *spin_mode); +int aw_dev_skt_prof_mode(struct aw_device *aw_dev, int prof_id); +int aw_dev_set_algo_prof_data(struct aw_device *aw_dev, void *prof_data, unsigned int prof_len); +int aw_dev_set_algo_prof(struct aw_device *aw_dev, int prof_id); +int aw_dev_get_algo_prof(struct aw_device *aw_dev, int *prof_id); + +#ifdef CONFIG_AW882XX_ALGO_BIN_PARAMS +int aw_dev_set_algo_params_path(struct aw_device *aw_dev); +#endif + +int aw_device_probe(struct aw_device *aw_dev); +int aw_device_remove(struct aw_device *aw_dev); +int aw_dev_get_list_head(struct list_head **head); +#ifdef AW_SPIN_ENABLE +int aw_dev_set_spin_param(struct aw_device *aw_dev, uint32_t enable, uint32_t relase_time); +int aw_dev_get_spin_param(struct aw_device *aw_dev, uint32_t* enable, uint32_t* relase_time); +#endif + +#endif + diff --git a/techpack/audio/asoc/codecs/aw882xxacf/aw_dsp.c b/techpack/audio/asoc/codecs/aw882xxacf/aw_dsp.c new file mode 100644 index 000000000000..ba2baa236795 --- /dev/null +++ b/techpack/audio/asoc/codecs/aw882xxacf/aw_dsp.c @@ -0,0 +1,1235 @@ +/*#define DEBUG*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aw_device.h" +#include "aw_dsp.h" +#include "aw_log.h" +/*#include "aw_afe.h"*/ + +static DEFINE_MUTEX(g_aw_dsp_msg_lock); +static DEFINE_MUTEX(g_aw_dsp_lock); + + +#define AW_MSG_ID_ENABLE_CALI (0x00000001) +#define AW_MSG_ID_ENABLE_HMUTE (0x00000002) +#define AW_MSG_ID_F0_Q (0x00000003) +#define AW_MSG_ID_DIRECT_CUR_FLAG (0x00000006) +#define AW_MSG_ID_SPK_STATUS (0x00000007) +#define AW_MSG_ID_SWITCH_SCENE (0x00000013) +#define AW_MSG_ID_ALGO_PARAMS_PATH (0x00000014) +#define AW_MSG_ID_VERSION (0x00000008) +#define AW_MSG_ID_VERSION_NEW (0x00000012) +#define AW_MSG_ID_SET_ALGO_PROF_PRI (0x10013D25) +#define AW_MSG_ID_SET_ALGO_PROF_SEC (0x10013D2C) + + +/*dsp params id*/ +#define AW_MSG_ID_RX_SET_ENABLE (0x10013D11) +#define AW_MSG_ID_PARAMS (0x10013D12) +#define AW_MSG_ID_TX_SET_ENABLE (0x10013D13) +#define AW_MSG_ID_VMAX_L (0X10013D17) +#define AW_MSG_ID_VMAX_R (0X10013D18) +#define AW_MSG_ID_CALI_CFG_L (0X10013D19) +#define AW_MSG_ID_CALI_CFG_R (0x10013d1A) +#define AW_MSG_ID_RE_L (0x10013d1B) +#define AW_MSG_ID_RE_R (0X10013D1C) +#define AW_MSG_ID_NOISE_L (0X10013D1D) +#define AW_MSG_ID_NOISE_R (0X10013D1E) +#define AW_MSG_ID_F0_L (0X10013D1F) +#define AW_MSG_ID_F0_R (0X10013D20) +#define AW_MSG_ID_REAL_DATA_L (0X10013D21) +#define AW_MSG_ID_REAL_DATA_R (0X10013D22) +#define AW_MSG_ID_SET_BIN_PARAMS (0X10013D30) +#define AW_MSG_ID_SPIN_ENABLE (0x10013D2F) + +#define AFE_MSG_ID_MSG_0 (0X10013D2A) +#define AFE_MSG_ID_MSG_1 (0X10013D2B) +#define AW_MSG_ID_PARAMS_1 (0x10013D2D) + +#define AW_MSG_ID_SPIN (0x10013D2E) + +int g_tx_topo_id = AW_TX_DEFAULT_TOPO_ID; +int g_rx_topo_id = AW_RX_DEFAULT_TOPO_ID; +int g_tx_port_id = AW_TX_DEFAULT_PORT_ID; +int g_rx_port_id = AW_RX_DEFAULT_PORT_ID; + +enum { + MSG_PARAM_ID_0 = 0, + MSG_PARAM_ID_1, + MSG_PARAM_ID_MAX, +}; + +static uint32_t afe_param_msg_id[MSG_PARAM_ID_MAX] = { + AFE_MSG_ID_MSG_0, + AFE_MSG_ID_MSG_1, +}; + +/***************dsp communicate**************/ +#ifdef CONFIG_AW882XX_DSP +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 1)) +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#endif +#endif + +#define AW_COPP_MODULE_ID (0X10013D02) /*SKT module id*/ +#define AW_COPP_PARAMS_ID_AWDSP_ENABLE (0X10013D14) /*SKT enable param id*/ + +#ifdef AW_MTK_PLATFORM_WITH_DSP +extern int mtk_spk_send_ipi_buf_to_dsp(void *data_buffer, uint32_t data_size); +extern int mtk_spk_recv_ipi_buf_from_dsp(int8_t *buffer, int16_t size, uint32_t *buf_len); +#elif defined CONFIG_AW882XX_DSP +//extern int afe_get_topology(int port_id); +extern int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id,uint32_t param_id, + void *buf, int cmd_size, bool write); +extern int aw_send_afe_rx_module_enable(int32_t rx_port_id, void *buf, int size); +extern int aw_send_afe_tx_module_enable(int32_t tx_port_id, void *buf, int size); +extern int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable); +#else +static int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id, uint32_t param_id, + void *buf, int cmd_size, bool write) +{ + return 0; +} +static int aw_send_afe_rx_module_enable(int32_t rx_port_id, void *buf, int size) +{ + return 0; +} +static int aw_send_afe_tx_module_enable(int32_t tx_port_id, void *buf, int size) +{ + return 0; +} +//static int afe_get_topology(int port_id) +//{ + //return 0; +//} + +#endif + +/*#ifdef AW_QCOM_PLATFORM +extern void aw_set_port_id(int tx_port_id, int rx_port_id); +#else +static void aw_set_port_id(int tx_port_id, int rx_port_id) { + return; +} +#endif*/ + +#if 0 +static int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable) +{ + /*for v3*/ + int copp_idx = 0; + uint32_t enable_param; + struct param_hdr_v3 param_hdr; + int rc = 0; + + pr_debug("%s port_id %d, module_id 0x%x, enable %d\n", + __func__, port_id, module_id, enable); + + copp_idx = adm_get_default_copp_idx(port_id); + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + if (enable < 0 || enable > 1) { + pr_err("%s: Invalid value for enable %d\n", __func__, enable); + return -EINVAL; + } + + pr_debug("%s port_id %d, module_id 0x%x, copp_idx 0x%x, enable %d\n", + __func__, port_id, module_id, copp_idx, enable); + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = module_id; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = param_id; + param_hdr.param_size = sizeof(enable_param); + enable_param = enable; + + rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (uint8_t *) &enable_param); + if (rc) + pr_err("%s: Failed to set enable of module(%d) instance(%d) to %d, err %d\n", + __func__, module_id, INSTANCE_ID_0, enable, rc); + return rc; + + return 0; +} +#endif + +static int aw_get_msg_num(int dev_ch, int *msg_num) +{ + switch (dev_ch) { + case AW_DEV_CH_PRI_L: + *msg_num = MSG_PARAM_ID_0; + break; + case AW_DEV_CH_PRI_R: + *msg_num = MSG_PARAM_ID_0; + break; + case AW_DEV_CH_SEC_L: + *msg_num = MSG_PARAM_ID_1; + break; + case AW_DEV_CH_SEC_R: + *msg_num = MSG_PARAM_ID_1; + break; + default: + aw_pr_err("can not find msg num, channel %d ", dev_ch); + return -EINVAL; + } + + aw_pr_dbg("msg num[%d] ", *msg_num); + return 0; +} + +/*****************qcom dsp communication function start**********************/ +#if 0 +static int aw_afe_get_topology(uint32_t param_id) +{ + if (param_id == AW_MSG_ID_TX_SET_ENABLE) + return afe_get_topology(g_tx_port_id); + else + return afe_get_topology(g_rx_port_id); +} + +static int aw_check_dsp_ready(uint32_t param_id) +{ + int ret = 0; + + //ret = aw_afe_get_topology(param_id); + + aw_pr_dbg("topo_id 0x%x ", ret); + + if (param_id == AW_MSG_ID_TX_SET_ENABLE) { + if (ret != g_tx_topo_id) + return false; + else + return true; + } else { + if (ret != g_rx_topo_id) + return false; + else + return true; + } +} +#endif + +static int aw_qcom_write_data_to_dsp(uint32_t param_id, void *data, int size) +{ + int ret; + //int try = 0; + + mutex_lock(&g_aw_dsp_lock); + //while (try < AW_DSP_TRY_TIME) { + //if (aw_check_dsp_ready(param_id)) { + ret = aw_send_afe_cal_apr(g_rx_port_id, g_tx_port_id, param_id, data, size, true); + mutex_unlock(&g_aw_dsp_lock); + return ret; + //} else { + // try++; + // usleep_range(AW_10000_US, AW_10000_US + 10); + // aw_pr_info("afe topo not ready try again"); + //} + //} + //mutex_unlock(&g_aw_dsp_lock); + + //return -EINVAL; +} + +static int aw_qcom_read_data_from_dsp(uint32_t param_id, void *data, int size) +{ + int ret; + //int try = 0; + + mutex_lock(&g_aw_dsp_lock); + //while (try < AW_DSP_TRY_TIME) { + // if (aw_check_dsp_ready(param_id)) { + ret = aw_send_afe_cal_apr(g_rx_port_id, g_tx_port_id, param_id, data, size, false); + mutex_unlock(&g_aw_dsp_lock); + return ret; +/* } else { + try++; + usleep_range(AW_10000_US, AW_10000_US + 10); + aw_pr_info("afe topo not ready try again"); + } + } + mutex_unlock(&g_aw_dsp_lock); + + return -EINVAL; +*/ +} + +static int aw_qcom_write_msg_to_dsp(int msg_num, uint32_t msg_id, char *data_ptr, unsigned int size) +{ + int32_t *dsp_msg; + int ret = 0; + int msg_len = (int)(sizeof(aw_dsp_msg_t) + size); + + mutex_lock(&g_aw_dsp_msg_lock); + dsp_msg = kzalloc(msg_len, GFP_KERNEL); + if (!dsp_msg) { + aw_pr_err("msg_id:0x%x kzalloc dsp_msg error", + msg_id); + ret = -ENOMEM; + goto w_mem_err; + } + dsp_msg[0] = AW_DSP_MSG_TYPE_DATA; + dsp_msg[1] = msg_id; + dsp_msg[2] = AW_DSP_MSG_HDR_VER; + + memcpy(dsp_msg + (sizeof(aw_dsp_msg_t) / sizeof(int32_t)), + data_ptr, size); + + ret = aw_qcom_write_data_to_dsp(afe_param_msg_id[msg_num], + (void *)dsp_msg, msg_len); + if (ret < 0) { + aw_pr_err("msg_id:0x%x, write data to dsp failed", msg_id); + kfree(dsp_msg); + goto w_mem_err; + } + + aw_pr_dbg("msg_id:0x%x, write data[%d] to dsp success", msg_id, msg_len); + mutex_unlock(&g_aw_dsp_msg_lock); + kfree(dsp_msg); + return 0; +w_mem_err: + mutex_unlock(&g_aw_dsp_msg_lock); + return ret; +} + +static int aw_qcom_read_msg_from_dsp(int msg_num, uint32_t msg_id, char *data_ptr, unsigned int size) +{ + aw_dsp_msg_t cmd_msg; + int ret; + + mutex_lock(&g_aw_dsp_msg_lock); + cmd_msg.type = AW_DSP_MSG_TYPE_CMD; + cmd_msg.opcode_id = msg_id; + cmd_msg.version = AW_DSP_MSG_HDR_VER; + + ret = aw_qcom_write_data_to_dsp(afe_param_msg_id[msg_num], + &cmd_msg, sizeof(aw_dsp_msg_t)); + if (ret < 0) { + aw_pr_err("msg_id:0x%x, write cmd to dsp failed", msg_id); + goto dsp_msg_failed; + } + + ret = aw_qcom_read_data_from_dsp(afe_param_msg_id[msg_num], + data_ptr, (int)size); + if (ret < 0) { + aw_pr_err("msg_id:0x%x, read data from dsp failed", msg_id); + goto dsp_msg_failed; + } + + aw_pr_dbg("msg_id:0x%x, read data[%d] from dsp success", msg_id, size); + mutex_unlock(&g_aw_dsp_msg_lock); + return 0; +dsp_msg_failed: + mutex_unlock(&g_aw_dsp_msg_lock); + return ret; +} + +/******************* afe module communication function ************************/ +static int aw_dsp_set_afe_rx_module_enable(void *buf, int size) +{ + return aw_send_afe_rx_module_enable(g_rx_port_id, buf, size); +} + +static int aw_dsp_set_afe_tx_module_enable(void *buf, int size) +{ + return aw_send_afe_tx_module_enable(g_tx_port_id, buf, size); +} + +static int aw_dsp_get_afe_rx_module_enable(void *buf, int size) +{ + return aw_qcom_read_data_from_dsp(AW_MSG_ID_RX_SET_ENABLE, buf, size); +} + +static int aw_dsp_get_afe_tx_module_enable(void *buf, int size) +{ + return aw_qcom_read_data_from_dsp(AW_MSG_ID_TX_SET_ENABLE, buf, size); +} + +/******************* read/write msg communication function ***********************/ +static int aw_read_msg_from_dsp(int msg_num, uint32_t msg_id, char *data_ptr, unsigned int size) +{ + return aw_qcom_read_msg_from_dsp(msg_num, msg_id, data_ptr, size); +} + +static int aw_write_msg_to_dsp(int msg_num, uint32_t msg_id, char *data_ptr, unsigned int size) +{ + return aw_qcom_write_msg_to_dsp(msg_num, msg_id, data_ptr, size); +} + +/******************* read/write data communication function ***********************/ +static int aw_read_data_from_dsp(uint32_t param_id, void *data, int size) +{ + return aw_qcom_read_data_from_dsp(param_id, data, size); +} + +static int aw_write_data_to_dsp(uint32_t param_id, void *data, int size) +{ + return aw_qcom_write_data_to_dsp(param_id, data, size); +} + +/************************* dsp communication function *****************************/ +int aw_dsp_set_algo_prof_data(struct aw_device *aw_dev, void *data, unsigned int size) +{ + int ret = 0; + uint32_t params_id = 0; + + if (aw_dev->channel == AW_DEV_CH_PRI_L) { + params_id = AW_MSG_ID_SET_ALGO_PROF_PRI; + } else if (aw_dev->channel == AW_DEV_CH_SEC_L) { + params_id = AW_MSG_ID_SET_ALGO_PROF_SEC; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + aw_dev_dbg(aw_dev->dev, "params_id=%u,prof_data:%p, len=%u", params_id, data, size); + + ret = aw_qcom_write_data_to_dsp(params_id, data, size); + if (ret < 0) { + aw_pr_err("write prof_id failed "); + return ret; + } + + aw_pr_dbg("write prof_id done"); + return 0; + +} + +int aw_dsp_set_algo_prof(struct aw_device *aw_dev, int prof_id) +{ + int ret; + int msg_num; + int32_t data = prof_id; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_write_msg_to_dsp(msg_num, AW_MSG_ID_SWITCH_SCENE, + (char *)&data, sizeof(int32_t)); + if (ret < 0) { + aw_pr_err("write prof_id failed "); + return ret; + } + aw_pr_dbg("write prof_id done"); + + return 0; + +} + +int aw_dsp_get_algo_prof(struct aw_device *aw_dev, int *prof_id) +{ + int ret; + int msg_num; + int32_t data = 0; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_SWITCH_SCENE, + (char *)&data, sizeof(int32_t)); + if (ret < 0) { + *prof_id = 0; + aw_dev_err(aw_dev->dev, "read algo prof failed"); + return ret; + } + + *prof_id = data; + aw_pr_dbg("read prof_id:%d done", *prof_id); + return 0; +} + +int aw_dsp_set_algo_params_path(struct aw_device *aw_dev) +{ + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_write_msg_to_dsp(msg_num, AW_MSG_ID_ALGO_PARAMS_PATH, + (char *)aw_dev->algo_path, + sizeof(aw_dev->algo_path)); + if (ret) + return -EINVAL; + + aw_pr_info("set algo params path: %s", aw_dev->algo_path); + return 0; +} + +int aw_dsp_set_afe_module_en(int type, int enable) +{ + int ret; + + switch (type) { + case AW_RX_MODULE: + ret = aw_dsp_set_afe_rx_module_enable(&enable, sizeof(int32_t)); + break; + case AW_TX_MODULE: + ret = aw_dsp_set_afe_tx_module_enable(&enable, sizeof(int32_t)); + break; + default: + pr_err("%s: unsupported type %d\n", __func__, type); + return -EINVAL; + } + + return ret; +} + +int aw_dsp_get_afe_module_en(int type, int *status) +{ + int ret; + + switch (type) { + case AW_RX_MODULE: + ret = aw_dsp_get_afe_rx_module_enable(status, sizeof(int32_t)); + break; + case AW_TX_MODULE: + ret = aw_dsp_get_afe_tx_module_enable(status, sizeof(int32_t)); + break; + default: + pr_err("%s: unsupported type %d\n", __func__, type); + return -EINVAL; + } + + return ret; +} + +int aw_dsp_read_te(struct aw_device *aw_dev, int32_t *te) +{ + int ret; + int msg_num; + int32_t data[8]; /*[re:r0:Te:r0_te]*/ + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_SPK_STATUS, + (char *)data, sizeof(int32_t) * 8); + if (ret) { + aw_dev_err(aw_dev->dev, " read Te failed "); + return ret; + } + + if ((aw_dev->channel % 2) == 0) + *te = data[2]; + else + *te = data[6]; + + aw_dev_dbg(aw_dev->dev, "read Te %d", *te); + return 0; +} + +int aw_dsp_read_st(struct aw_device *aw_dev, int32_t *r0, int32_t *te) +{ + int ret; + int msg_num; + int32_t data[8]; /*[re:r0:Te:r0_te]*/ + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_SPK_STATUS, + (char *)data, sizeof(int32_t) * 8); + if (ret) { + aw_dev_err(aw_dev->dev, "read spk st failed"); + return ret; + } + + if ((aw_dev->channel % 2) == 0) { + *r0 = AW_DSP_RE_TO_SHOW_RE(data[0]); + *te = data[2]; + } else { + *r0 = AW_DSP_RE_TO_SHOW_RE(data[4]); + *te = data[6]; + } + aw_dev_dbg(aw_dev->dev, "read Re %d , Te %d", *r0, *te); + return 0; +} + +#ifdef AW_SPIN_ENABLE +int aw_dsp_get_spin_param(struct aw_device *aw_dev, + uint32_t *enable, uint32_t *relase_time) +{ + int ret; + struct aw_spin_param spin_param; + + ret = aw_read_data_from_dsp(AW_MSG_ID_SPIN_ENABLE, + &spin_param, + sizeof(spin_param)); + if (ret) { + spin_param.enable = 0; + spin_param.relase_time = 0; + aw_pr_err("read spin params failed "); + return ret; + } + + *enable = spin_param.enable; + *relase_time = spin_param.relase_time; + + return 0; +} + +int aw_dsp_set_spin_param(struct aw_device *aw_dev, + uint32_t enable, uint32_t relase_time) +{ + int ret; + struct aw_spin_param spin_param; + + if (!enable) { + return 0; + } + + spin_param.enable = enable; + spin_param.relase_time = relase_time; + + ret = aw_write_data_to_dsp(AW_MSG_ID_SPIN_ENABLE, + &spin_param, + sizeof(spin_param)); + if (ret){ + pr_err("set spin params failed\n"); + return -EINVAL; + } + + return 0; +} +#endif + +int aw_dsp_read_spin(int *spin_mode) +{ + int ret; + int32_t spin = 0; + + ret = aw_read_data_from_dsp(AW_MSG_ID_SPIN, &spin, sizeof(int32_t)); + if (ret) { + *spin_mode = 0; + aw_pr_err("read spin failed "); + return ret; + } + *spin_mode = spin; + aw_pr_dbg("read spin done"); + return 0; +} + +int aw_dsp_write_spin(int spin_mode) +{ + int ret; + int32_t spin = spin_mode; + + if (spin >= AW_SPIN_MAX) { + aw_pr_err("spin [%d] unsupported ", spin); + return -EINVAL; + } + + ret = aw_write_data_to_dsp(AW_MSG_ID_SPIN, &spin, sizeof(int32_t)); + if (ret) { + aw_pr_err("write spin failed "); + return ret; + } + aw_pr_dbg("write spin done"); + return 0; +} + +int aw_dsp_read_r0(struct aw_device *aw_dev, int32_t *r0) +{ + uint32_t msg_id; + int ret; + int msg_num; + int32_t data[6]; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_REAL_DATA_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_REAL_DATA_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_read_msg_from_dsp(msg_num, msg_id, (char *)data, sizeof(int32_t) * 6); + if (ret) { + aw_dev_err(aw_dev->dev, "read real re failed "); + return ret; + } + + *r0 = AW_DSP_RE_TO_SHOW_RE(data[0]); + aw_dev_dbg(aw_dev->dev, "read r0 %d\n", *r0); + return 0; +} + +int aw_dsp_read_cali_data(struct aw_device *aw_dev, char *data, unsigned int data_len) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_REAL_DATA_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_REAL_DATA_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_read_msg_from_dsp(msg_num, msg_id, data, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, "read cali dara failed "); + return ret; + } + aw_dev_dbg(aw_dev->dev, "read cali_data"); + return 0; +} + +int aw_dsp_get_dc_status(struct aw_device *aw_dev) +{ + int ret; + int msg_num; + int32_t data[2]; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_DIRECT_CUR_FLAG, (char *)data, sizeof(int32_t) * 2); + if (ret) { + aw_dev_err(aw_dev->dev, "read dc flag failed"); + return ret; + } + + if ((aw_dev->channel % 2) == 0) + ret = data[0]; + else + ret = data[1]; + + aw_dev_dbg(aw_dev->dev, "read direct current status:%d", ret); + return ret; +} + +int aw_dsp_read_f0_q(struct aw_device *aw_dev, int32_t *f0, int32_t *q) +{ + int ret; + int msg_num; + int32_t data[4]; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_F0_Q, (char *)data, sizeof(int32_t) * 4); + if (ret) { + aw_dev_err(aw_dev->dev, "read f0 & q failed"); + return ret; + } + + if ((aw_dev->channel % 2) == 0) { + *f0 = data[0]; + *q = data[1]; + } else { + *f0 = data[2]; + *q = data[3]; + } + aw_dev_dbg(aw_dev->dev, "read f0 is %d, q is %d", *f0, *q); + return ret; +} + +int aw_dsp_read_f0(struct aw_device *aw_dev, int32_t *f0) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_F0_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_F0_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_read_msg_from_dsp(msg_num, msg_id, (char *)f0, sizeof(int32_t)); + if (ret) { + aw_dev_err(aw_dev->dev, "read f0 failed"); + return ret; + } + aw_dev_dbg(aw_dev->dev, "read f0"); + return 0; +} + +int aw_dsp_cali_en(struct aw_device *aw_dev, bool is_enable) +{ + int ret; + int msg_num; + int32_t enable = is_enable; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + ret = aw_write_msg_to_dsp(msg_num, AW_MSG_ID_ENABLE_CALI, (char *)&enable, sizeof(int32_t)); + if (ret) { + aw_dev_err(aw_dev->dev, "write cali en failed"); + return ret; + } + aw_dev_dbg(aw_dev->dev, "write cali_en[%d]", is_enable); + return 0; +} + +int aw_dsp_hmute_en(struct aw_device *aw_dev, bool is_hmute) +{ + int32_t hmute = is_hmute; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + ret = aw_write_msg_to_dsp(msg_num, AW_MSG_ID_ENABLE_HMUTE, (char *)&hmute, sizeof(int32_t)); + if (ret) { + aw_dev_err(aw_dev->dev, "write hmue failed "); + return ret; + } + aw_dev_dbg(aw_dev->dev, "write hmute[%d]", is_hmute); + return 0; +} + +int aw_dsp_read_cali_re(struct aw_device *aw_dev, int32_t *cali_re) +{ + uint32_t msg_id; + int ret; + int msg_num; + int32_t read_re = 0; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_RE_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_RE_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_read_msg_from_dsp(msg_num, msg_id, (char *)&read_re, sizeof(int32_t)); + if (ret) { + aw_dev_err(aw_dev->dev, "read cali re failed "); + return ret; + } + *cali_re = AW_DSP_RE_TO_SHOW_RE(read_re); + aw_dev_dbg(aw_dev->dev, "read cali re done"); + return 0; +} + +int aw_dsp_write_cali_re(struct aw_device *aw_dev, int32_t cali_re) +{ + uint32_t msg_id; + int ret; + int msg_num; + int32_t local_re = AW_SHOW_RE_TO_DSP_RE(cali_re); + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_RE_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_RE_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_write_msg_to_dsp(msg_num, msg_id, (char *)&local_re, sizeof(int32_t)); + if (ret) { + aw_dev_err(aw_dev->dev, "write cali re failed "); + return ret; + } + aw_dev_dbg(aw_dev->dev, "write cali re done"); + return 0; +} + +int aw_dsp_write_params(struct aw_device *aw_dev, char *data, unsigned int data_len) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + if (msg_num == MSG_PARAM_ID_0) + msg_id = AW_MSG_ID_PARAMS; + else + msg_id = AW_MSG_ID_PARAMS_1; + + ret = aw_write_data_to_dsp(msg_id, data, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, "write params failed"); + return ret; + } + aw_dev_dbg(aw_dev->dev, "write params done"); + return 0; +} + +int aw_dsp_read_vmax(struct aw_device *aw_dev, char *data, unsigned int data_len) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_VMAX_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_VMAX_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_read_msg_from_dsp(msg_num, msg_id, data, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, "read vmax failed"); + return ret; + } + aw_dev_dbg(aw_dev->dev, "read vmax done"); + return 0; +} + +int aw_dsp_write_vmax(struct aw_device *aw_dev, char *data, unsigned int data_len) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_VMAX_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_VMAX_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_write_msg_to_dsp(msg_num, msg_id, data, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, "write vmax failed "); + return ret; + } + aw_dev_dbg(aw_dev->dev, "write vmax done"); + return 0; +} + +int aw_dsp_noise_en(struct aw_device *aw_dev, bool is_noise) +{ + int32_t noise = is_noise; + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_NOISE_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_NOISE_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_write_msg_to_dsp(msg_num, msg_id, (char *)&noise, sizeof(int32_t)); + if (ret) { + aw_dev_err(aw_dev->dev, "write noise failed "); + return ret; + } + aw_dev_dbg(aw_dev->dev, "write noise[%d] done", noise); + return 0; +} + +int aw_dsp_read_cali_cfg(struct aw_device *aw_dev, char *data, unsigned int data_len) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_CALI_CFG_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_CALI_CFG_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_read_msg_from_dsp(msg_num, msg_id, data, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, "read cali_cfg failed "); + return ret; + } + aw_dev_dbg(aw_dev->dev, "read cali_cfg done"); + return 0; +} + +int aw_dsp_write_cali_cfg(struct aw_device *aw_dev, char *data, unsigned int data_len) +{ + uint32_t msg_id; + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed "); + return ret; + } + + if (aw_dev->channel == AW_DEV_CH_PRI_L || + aw_dev->channel == AW_DEV_CH_SEC_L) { + msg_id = AW_MSG_ID_CALI_CFG_L; + } else if (aw_dev->channel == AW_DEV_CH_PRI_R || + aw_dev->channel == AW_DEV_CH_SEC_R) { + msg_id = AW_MSG_ID_CALI_CFG_R; + } else { + aw_dev_err(aw_dev->dev, "unsupport dev channel"); + return -EINVAL; + } + + ret = aw_write_msg_to_dsp(msg_num, msg_id, data, data_len); + if (ret) { + aw_dev_err(aw_dev->dev, "write cali_cfg failed "); + return ret; + } + + aw_dev_dbg(aw_dev->dev, "write cali_cfg done"); + return 0; +} + +int aw_dsp_read_msg(struct aw_device *aw_dev, + uint32_t msg_id, char *data_ptr, unsigned int data_size) +{ + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + return aw_read_msg_from_dsp(msg_num, msg_id, data_ptr, data_size); +} + +int aw_dsp_write_msg(struct aw_device *aw_dev, + uint32_t msg_id, char *data_ptr, unsigned int data_size) +{ + int ret; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + return aw_write_msg_to_dsp(msg_num, msg_id, data_ptr, data_size); +} + +int aw_dsp_set_copp_module_en(bool enable) +{ + int ret; + + ret = aw_adm_param_enable(g_rx_port_id, AW_COPP_MODULE_ID, + AW_COPP_PARAMS_ID_AWDSP_ENABLE, enable); + if (ret) + return -EINVAL; + + aw_pr_info("set skt %s", enable == 1 ? "enable" : "disable"); + return 0; +} + +int aw_get_algo_version(struct aw_device *aw_dev, char *algo_ver_buf) +{ + int ret; + unsigned int algo_ver = 0; + char *algo_data = NULL; + int msg_num; + + ret = aw_get_msg_num(aw_dev->channel, &msg_num); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get msg_num failed"); + return ret; + } + + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_VERSION, + (char *)&algo_ver, sizeof(uint32_t)); + if ((ret < 0) || (algo_ver == 0)) { + ret = aw_read_msg_from_dsp(msg_num, AW_MSG_ID_VERSION_NEW, + algo_ver_buf, ALGO_VERSION_MAX); + if (ret < 0) + return ret; + } else { + algo_data = (char *)&algo_ver; + snprintf(algo_ver_buf, ALGO_VERSION_MAX, "aw_algo_v%d.%d.%d.%d", + algo_data[3], algo_data[2], algo_data[1], algo_data[0]); + } + + aw_dev_dbg(aw_dev->dev, "%s", algo_ver_buf); + return 0; +} + +void aw_device_parse_topo_id_dt(struct aw_device *aw_dev) +{ + int ret; + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-tx-topo-id", &g_tx_topo_id); + if (ret < 0) { + g_tx_topo_id = AW_TX_DEFAULT_TOPO_ID; + aw_dev_info(aw_dev->dev, "read aw-tx-topo-id failed,use default"); + } + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-rx-topo-id", &g_rx_topo_id); + if (ret < 0) { + g_rx_topo_id = AW_RX_DEFAULT_TOPO_ID; + aw_dev_info(aw_dev->dev, "read aw-rx-topo-id failed,use default"); + } + + aw_dev_info(aw_dev->dev, "tx-topo-id: 0x%x, rx-topo-id: 0x%x", + g_tx_topo_id, g_rx_topo_id); +} + +void aw_device_parse_port_id_dt(struct aw_device *aw_dev) +{ + int ret; + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-tx-port-id", &g_tx_port_id); + if (ret < 0) { + g_tx_port_id = AW_TX_DEFAULT_PORT_ID; + aw_dev_info(aw_dev->dev, "read aw-tx-port-id failed,use default"); + } + + ret = of_property_read_u32(aw_dev->dev->of_node, "aw-rx-port-id", &g_rx_port_id); + if (ret < 0) { + g_rx_port_id = AW_RX_DEFAULT_PORT_ID; + aw_dev_info(aw_dev->dev, "read aw-rx-port-id failed,use default"); + } + + aw_dev_info(aw_dev->dev, "tx-port-id: 0x%x, rx-port-id: 0x%x", + g_tx_port_id, g_rx_port_id); + +} + diff --git a/techpack/audio/asoc/codecs/aw882xxacf/aw_dsp.h b/techpack/audio/asoc/codecs/aw882xxacf/aw_dsp.h new file mode 100644 index 000000000000..8b99b8092f58 --- /dev/null +++ b/techpack/audio/asoc/codecs/aw882xxacf/aw_dsp.h @@ -0,0 +1,79 @@ +#ifndef __AWINIC_DSP_H__ +#define __AWINIC_DSP_H__ + +#define AW_QCOM_PLATFORM + +/*factor form 12bit(4096) to 1000*/ +#define AW_DSP_RE_TO_SHOW_RE(re) (((re) * (1000)) >> (12)) +#define AW_SHOW_RE_TO_DSP_RE(re) (((re) << 12) / (1000)) + +#define AW_DSP_TRY_TIME (3) +#define AW_DSP_SLEEP_TIME (10) + +#define AW_TX_DEFAULT_TOPO_ID (0x1000FF00) +#define AW_RX_DEFAULT_TOPO_ID (0x1000FF01) +#define AW_TX_DEFAULT_PORT_ID (0x1007) +#define AW_RX_DEFAULT_PORT_ID (0x1006) + +enum aw_dsp_msg_type { + AW_DSP_MSG_TYPE_DATA = 0, + AW_DSP_MSG_TYPE_CMD = 1, +}; + +enum { + AW_SPIN_0 = 0, + AW_SPIN_90, + AW_SPIN_180, + AW_SPIN_270, + AW_SPIN_MAX, +}; + +#define AW_DSP_MSG_HDR_VER (1) +typedef struct aw_msg_hdr aw_dsp_msg_t; + +#ifdef AW_SPIN_ENABLE +struct aw_spin_param { + uint32_t enable; + uint32_t relase_time; +}; +#endif + +int aw_dsp_write_msg(struct aw_device *aw_dev, uint32_t msg_id, char *data_ptr, unsigned int data_size); +int aw_dsp_read_msg(struct aw_device *aw_dev, uint32_t msg_id, char *data_ptr, unsigned int data_size); +int aw_dsp_write_cali_cfg(struct aw_device *aw_dev, char *data, unsigned int data_len); +int aw_dsp_read_cali_cfg(struct aw_device *aw_dev, char *data, unsigned int data_len); +int aw_dsp_noise_en(struct aw_device *aw_dev, bool is_noise); +int aw_dsp_write_vmax(struct aw_device *aw_dev, char *data, unsigned int data_len); +int aw_dsp_read_vmax(struct aw_device *aw_dev, char *data, unsigned int data_len); +int aw_dsp_write_params(struct aw_device *aw_dev, char *data, unsigned int data_len); +int aw_dsp_write_cali_re(struct aw_device *aw_dev, int32_t cali_re); +int aw_dsp_read_cali_re(struct aw_device *aw_dev, int32_t *cali_re); +int aw_dsp_read_r0(struct aw_device *aw_dev, int32_t *r0); +int aw_dsp_read_st(struct aw_device *aw_dev, int32_t *r0, int32_t *te); +int aw_dsp_read_te(struct aw_device *aw_dev, int32_t *te); +int aw_dsp_get_dc_status(struct aw_device *aw_dev); +int aw_dsp_hmute_en(struct aw_device *aw_dev, bool is_hmute); +int aw_dsp_cali_en(struct aw_device *aw_dev, bool is_enable); +int aw_dsp_read_f0(struct aw_device *aw_dev, int32_t *f0); +int aw_dsp_read_f0_q(struct aw_device *aw_dev, int32_t *f0, int32_t *q); +int aw_dsp_read_cali_data(struct aw_device *aw_dev, char *data, unsigned int data_len); +int aw_dsp_set_afe_module_en(int type, int enable); +int aw_dsp_get_afe_module_en(int type, int *status); +int aw_dsp_set_copp_module_en(bool enable); +int aw_dsp_write_spin(int spin_mode); +int aw_dsp_read_spin(int *spin_mode); +int aw_get_algo_version(struct aw_device *aw_dev, char *algo_ver_buf); +int aw_dsp_set_algo_prof_data(struct aw_device *aw_dev, void *data, unsigned int size); +int aw_dsp_set_algo_prof(struct aw_device *aw_dev, int prof_id); +int aw_dsp_get_algo_prof(struct aw_device *aw_dev, int *prof_id); +int aw_dsp_set_algo_params_path(struct aw_device *aw_dev); +void aw_device_parse_topo_id_dt(struct aw_device *aw_dev); +void aw_device_parse_port_id_dt(struct aw_device *aw_dev); +#ifdef AW_SPIN_ENABLE +int aw_dsp_set_spin_param(struct aw_device *aw_dev, + uint32_t enable, uint32_t relase_time); +int aw_dsp_get_spin_param(struct aw_device *aw_dev, + uint32_t *enable, uint32_t *relase_time); +#endif +#endif + diff --git a/techpack/audio/asoc/codecs/aw882xxacf/aw_init.c b/techpack/audio/asoc/codecs/aw882xxacf/aw_init.c new file mode 100644 index 000000000000..e42f983d2d6d --- /dev/null +++ b/techpack/audio/asoc/codecs/aw882xxacf/aw_init.c @@ -0,0 +1,1685 @@ +/* + * aw_init.c aw882xx codec module + * + * + * Copyright (c) 2019 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/* #define DEBUG */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aw882xx.h" +#include "aw_pid_1852_reg.h" +#include "aw_pid_2013_reg.h" +#include "aw_pid_2032_reg.h" +#include "aw_pid_2055_reg.h" +#include "aw_pid_2071_reg.h" +#include "aw_pid_2113_reg.h" +#include "aw_log.h" + +int aw882xx_dev_i2c_write_bits(struct aw_device *aw_dev, + unsigned char reg_addr, unsigned int mask, unsigned int reg_data) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + return aw882xx_i2c_write_bits(aw882xx, reg_addr, mask, reg_data); +} + +int aw882xx_dev_i2c_write(struct aw_device *aw_dev, + unsigned char reg_addr, unsigned int reg_data) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + return aw882xx_i2c_write(aw882xx, reg_addr, reg_data); +} + +int aw882xx_dev_i2c_read(struct aw_device *aw_dev, + unsigned char reg_addr, unsigned int *reg_data) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + return aw882xx_i2c_read(aw882xx, reg_addr, reg_data); +} + +void aw882xx_dev_set_algo_en(struct aw_device *aw_dev) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + aw882xx_kcontorl_set(aw882xx); +} + +/* [7 : 4]: -6DB ; [3 : 0]: 0.5DB real_value = value * 2 : 0.5db --> 1 */ +static unsigned int aw_pid_1852_reg_val_to_db(unsigned int value) +{ + return ((value >> 4) * AW_PID_1852_VOL_STEP_DB + (value & 0x0f)); +} + +/* [7 : 4]: -6DB ; [3 : 0]: -0.5DB reg_value = value / step << 4 + value % step ; step = 6 * 2 */ +static unsigned int aw_pid_1852_db_val_to_reg(unsigned int value) +{ + return (((value / AW_PID_1852_VOL_STEP_DB) << 4) + (value % AW_PID_1852_VOL_STEP_DB)); +} + +static int aw_pid_1852_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + unsigned int reg_value = 0; + unsigned int real_value = aw_pid_1852_db_val_to_reg(value); + + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW_PID_1852_HAGCCFG4_REG, ®_value); + + aw_dev_dbg(aw882xx->dev, "value %d , 0x%x", value, real_value); + + /* 15 : 8] volume */ + real_value = (real_value << 8) | (reg_value & 0x00ff); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW_PID_1852_HAGCCFG4_REG, real_value); + return 0; +} + +static int aw_pid_1852_get_volume(struct aw_device *aw_dev, unsigned int *value) +{ + unsigned int reg_value = 0; + unsigned int real_value = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + /* read value */ + aw882xx_i2c_read(aw882xx, AW_PID_1852_HAGCCFG4_REG, ®_value); + + /* [15 : 8] volume */ + real_value = reg_value >> 8; + + real_value = aw_pid_1852_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static void aw_pid_1852_i2s_enable(struct aw_device *aw_dev, bool flag) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + struct aw_cali_desc *desc = &aw_dev->cali_desc; + + aw_dev_dbg(aw882xx->dev, "enter"); + + if (!desc->mode) { + aw_dev_dbg(aw_dev->dev, "cali mode is none, needn't set i2s status"); + return; + } + + if (flag) { + aw882xx_i2c_write_bits(aw882xx, AW_PID_1852_I2SCFG1_REG, + AW_PID_1852_I2STXEN_MASK, + AW_PID_1852_I2STXEN_ENABLE_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW_PID_1852_I2SCFG1_REG, + AW_PID_1852_I2STXEN_MASK, + AW_PID_1852_I2STXEN_DISABLE_VALUE); + } +} + +static bool aw_pid_1852_check_rd_access(int reg) +{ + if (reg >= AW_PID_1852_REG_MAX) + return false; + + + if (aw_pid_1852_reg_access[reg] & AW_PID_1852_REG_RD_ACCESS) + return true; + else + return false; +} + +static bool aw_pid_1852_check_wr_access(int reg) +{ + if (reg >= AW_PID_1852_REG_MAX) + return false; + + + if (aw_pid_1852_reg_access[reg] & AW_PID_1852_REG_WR_ACCESS) + return true; + else + return false; +} + +static int aw_pid_1852_get_reg_num(void) +{ + return AW_PID_1852_REG_MAX; +} + +static unsigned int aw_pid_1852_get_irq_type(struct aw_device *aw_dev, + unsigned int value) +{ + unsigned int ret = INT_TYPE_NONE; + /* UVL0 */ + if (value & (~AW_PID_1852_UVLI_MASK)) { + aw_dev_info(aw_dev->dev, "UVLO: occur"); + ret |= INT_TYPE_UVLO; + } + + /* BSTOCM */ + if (value & (~AW_PID_1852_BSTOCI_MASK)) { + aw_dev_info(aw_dev->dev, "BSTOCI: occur"); + ret |= INT_TYPE_BSTOC; + } + + /* OCDI */ + if (value & (~AW_PID_1852_OCDI_MASK)) { + aw_dev_info(aw_dev->dev, "OCDI: occur"); + ret |= INT_TYPE_OCDI; + } + + /* OTHI */ + if (value & (~AW_PID_1852_OTHI_MASK)) { + aw_dev_info(aw_dev->dev, "OTHI: occur"); + ret |= INT_TYPE_OTHI; + } + + return ret; +} + +static int aw_pid_1852_dev_init(struct aw882xx *aw882xx, int index) +{ + int ret; + struct aw_device *aw_pa = NULL; + + aw_pa = devm_kzalloc(aw882xx->dev, sizeof(struct aw_device), GFP_KERNEL); + if (aw_pa == NULL) { + aw_dev_err(aw882xx->dev, "dev kalloc failed"); + return -ENOMEM; + } + + /* call aw device init func */ + memset(aw_pa->acf_name, 0, AW_NAME_MAX); + memcpy(aw_pa->acf_name, AW_PID_1852_ACF_FILE, strlen(AW_PID_1852_ACF_FILE)); + memset(aw_pa->monitor_name, 0, AW_NAME_MAX); + memcpy(aw_pa->monitor_name, AW_PID_1852_MONITOR_FILE, strlen(AW_PID_1852_MONITOR_FILE)); + + aw_pa->prof_info.prof_desc = NULL; + aw_pa->prof_info.count = 0; + aw_pa->channel = 0; + aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_pa->bop_en = AW_BOP_DISABLE; + aw_pa->vol_step = AW_PID_1852_VOL_STEP; + + aw_pa->index = index; + aw_pa->private_data = (void *)aw882xx; + aw_pa->dev = aw882xx->dev; + aw_pa->i2c = aw882xx->i2c; + aw_pa->ops.aw_get_version = aw882xx_get_version; + aw_pa->ops.aw_get_dev_num = aw882xx_get_dev_num; + aw_pa->ops.aw_set_algo = aw882xx_dev_set_algo_en; + aw_pa->ops.aw_i2c_read = aw882xx_dev_i2c_read; + aw_pa->ops.aw_i2c_write = aw882xx_dev_i2c_write; + aw_pa->ops.aw_i2c_write_bits = aw882xx_dev_i2c_write_bits; + aw_pa->ops.aw_get_volume = aw_pid_1852_get_volume; + aw_pa->ops.aw_set_volume = aw_pid_1852_set_volume; + aw_pa->ops.aw_reg_val_to_db = aw_pid_1852_reg_val_to_db; + aw_pa->ops.aw_i2s_enable = aw_pid_1852_i2s_enable; + aw_pa->ops.aw_check_rd_access = aw_pid_1852_check_rd_access; + aw_pa->ops.aw_check_wr_access = aw_pid_1852_check_wr_access; + aw_pa->ops.aw_get_reg_num = aw_pid_1852_get_reg_num; + aw_pa->ops.aw_get_irq_type = aw_pid_1852_get_irq_type; + + aw_pa->int_desc.mask_reg = AW_PID_1852_SYSINTM_REG; + aw_pa->int_desc.mask_default = AW_PID_1852_SYSINTM_DEFAULT; + aw_pa->int_desc.int_mask = AW_PID_1852_SYSINTM_DEFAULT; + aw_pa->int_desc.st_reg = AW_PID_1852_SYSINT_REG; + + aw_pa->pwd_desc.reg = AW_PID_1852_SYSCTRL_REG; + aw_pa->pwd_desc.mask = AW_PID_1852_PWDN_MASK; + aw_pa->pwd_desc.enable = AW_PID_1852_PWDN_POWER_DOWN_VALUE; + aw_pa->pwd_desc.disable = AW_PID_1852_PWDN_NORMAL_WORKING_VALUE; + + aw_pa->amppd_desc.reg = AW_PID_1852_SYSCTRL_REG; + aw_pa->amppd_desc.mask = AW_PID_1852_AMPPD_MASK; + aw_pa->amppd_desc.enable = AW_PID_1852_AMPPD_POWER_DOWN_VALUE; + aw_pa->amppd_desc.disable = AW_PID_1852_AMPPD_NORMAL_WORKING_VALUE; + + aw_pa->mute_desc.reg = AW_PID_1852_SYSCTRL2_REG; + aw_pa->mute_desc.mask = AW_PID_1852_HMUTE_MASK; + aw_pa->mute_desc.enable = AW_PID_1852_HMUTE_ENABLE_VALUE; + aw_pa->mute_desc.disable = AW_PID_1852_HMUTE_DISABLE_VALUE; + + aw_pa->vcalb_desc.vcalb_reg = AW_PID_1852_VTMCTRL3_REG; + aw_pa->vcalb_desc.vcal_factor = AW_PID_1852_VCAL_FACTOR; + aw_pa->vcalb_desc.cabl_base_value = AW_PID_1852_CABL_BASE_VALUE; + + aw_pa->vcalb_desc.icalk_value_factor = AW_PID_1852_ICABLK_FACTOR; + aw_pa->vcalb_desc.icalk_reg = AW_PID_1852_EFRM1_REG; + aw_pa->vcalb_desc.icalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.icalk_reg_mask = AW_PID_1852_EF_ISN_GESLP_MASK; + aw_pa->vcalb_desc.icalk_sign_mask = AW_PID_1852_EF_ISN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.icalk_neg_mask = AW_PID_1852_EF_ISN_GESLP_NEG; + + aw_pa->vcalb_desc.vcalk_reg = AW_PID_1852_EFRH_REG; + aw_pa->vcalb_desc.vcalk_reg_mask = AW_PID_1852_EF_VSN_GESLP_MASK; + aw_pa->vcalb_desc.vcalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.vcalk_sign_mask = AW_PID_1852_EF_VSN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.vcalk_neg_mask = AW_PID_1852_EF_VSN_GESLP_NEG; + aw_pa->vcalb_desc.vcalk_value_factor = AW_PID_1852_VCABLK_FACTOR; + + aw_pa->sysst_desc.reg = AW_PID_1852_SYSST_REG; + aw_pa->sysst_desc.mask = AW_PID_1852_SYSST_CHECK_MASK; + aw_pa->sysst_desc.st_check = AW_PID_1852_SYSST_CHECK; + aw_pa->sysst_desc.pll_check = AW_PID_1852_PLLS_LOCKED_VALUE; + + aw_pa->cco_mux_desc.reg = AW_PID_1852_PLLCTRL1_REG; + aw_pa->cco_mux_desc.mask = AW_PID_1852_I2S_CCO_MUX_MASK; + aw_pa->cco_mux_desc.divided_val = AW_PID_1852_I2S_CCO_MUX_8_16_32KHZ_VALUE; + aw_pa->cco_mux_desc.bypass_val = AW_PID_1852_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + + aw_pa->voltage_desc.reg = AW_PID_1852_VBAT_REG; + aw_pa->voltage_desc.int_bit = AW_PID_1852_MONITOR_INT_10BIT; + aw_pa->voltage_desc.vbat_range = AW_PID_1852_MONITOR_VBAT_RANGE; + + aw_pa->temp_desc.reg = AW_PID_1852_TEMP_REG; + aw_pa->temp_desc.neg_mask = AW_PID_1852_MONITOR_TEMP_NEG_MASK; + aw_pa->temp_desc.sign_mask = AW_PID_1852_MONITOR_TEMP_SIGN_MASK; + + aw_pa->ipeak_desc.reg = AW_PID_1852_SYSCTRL2_REG; + aw_pa->ipeak_desc.mask = AW_PID_1852_BST_IPEAK_MASK; + + aw_pa->volume_desc.reg = AW_PID_1852_HAGCCFG4_REG; + aw_pa->volume_desc.mask = AW_PID_1852_VOL_MASK; + aw_pa->volume_desc.shift = AW_PID_1852_VOL_START_BIT; + aw_pa->volume_desc.mute_volume = AW_PID_1852_MUTE_VOL; + + aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->efcheck_desc.reg = AW_REG_NONE; + + aw_pa->soft_rst.reg = AW_PID_1852_ID_REG; + aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + + ret = aw_device_probe(aw_pa); + + aw882xx->aw_pa = aw_pa; + return ret; +} + +/****************************************************** + * + * A2013 init + * + ******************************************************/ +/*[9 : 6]: -6DB ; [5 : 0]: 0.125DB real_value = value * 8 : 0.125db --> 1*/ +static unsigned int aw_pid_2013_reg_val_to_db(unsigned int value) +{ + return ((value >> 6) * AW_PID_2013_VOL_STEP_DB + (value & 0x003f)); +} + +/*[9 : 6]: -6DB ; [5 : 0]: -0.125DB reg_value = value / step << 6 + value % step ; step = 6 * 8*/ +static unsigned int aw_pid_2013_db_val_to_reg(unsigned int value) +{ + return (((value / AW_PID_2013_VOL_STEP_DB) << 6) + (value % AW_PID_2013_VOL_STEP_DB)); +} + +static int aw_pid_2013_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + unsigned int reg_value = 0; + unsigned int real_value = aw_pid_2013_db_val_to_reg(value); + + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW_PID_2013_SYSCTRL2_REG, ®_value); + + aw_dev_dbg(aw882xx->dev, "value %d , 0x%x", value, real_value); + + /*9 : 0] volume*/ + real_value = real_value | (reg_value & 0xfc00); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW_PID_2013_SYSCTRL2_REG, real_value); + return 0; +} + +static int aw_pid_2013_get_volume(struct aw_device *aw_dev, unsigned int* value) +{ + unsigned int reg_value = 0; + unsigned int real_value = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + /* read value */ + aw882xx_i2c_read(aw882xx, AW_PID_2013_SYSCTRL2_REG, ®_value); + + /*[9 : 0] volume*/ + real_value = reg_value & 0x03ff; + + real_value = aw_pid_2013_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static void aw_pid_2013_i2s_enable(struct aw_device *aw_dev, bool flag) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + aw_dev_dbg(aw882xx->dev, "enter"); + + if (flag) + aw882xx_i2c_write_bits(aw882xx, AW_PID_2013_I2SCTRL1_REG, + AW_PID_2013_I2STXEN_MASK, + AW_PID_2013_I2STXEN_ENABLE_VALUE); + else + aw882xx_i2c_write_bits(aw882xx, AW_PID_2013_I2SCFG1_REG, + AW_PID_2013_I2STXEN_MASK, + AW_PID_2013_I2STXEN_DISABLE_VALUE); +} + +static bool aw_pid_2013_check_rd_access(int reg) +{ + if (reg >= AW_PID_2013_REG_MAX) + return false; + + if (aw_pid_2013_reg_access[reg] & AW_PID_2013_REG_RD_ACCESS) + return true; + else + return false; +} + +static bool aw_pid_2013_check_wr_access(int reg) +{ + if (reg >= AW_PID_2013_REG_MAX) + return false; + + if (aw_pid_2013_reg_access[reg] & AW_PID_2013_REG_WR_ACCESS) + return true; + else + return false; +} + +static int aw_pid_2013_get_reg_num(void) +{ + return AW_PID_2013_REG_MAX; +} + +static unsigned int aw_pid_2013_get_irq_type(struct aw_device *aw_dev, + unsigned int value) +{ + unsigned int ret = INT_TYPE_NONE; + + /*UVL0*/ + if (value & (~AW_PID_2013_UVLI_MASK)) { + aw_dev_info(aw_dev->dev, "UVLO: occur"); + ret |= INT_TYPE_UVLO; + } + + /*BSTOCM*/ + if (value & (~AW_PID_2013_BSTOCI_MASK)) { + aw_dev_info(aw_dev->dev, "BSTOCI: occur"); + ret |= INT_TYPE_BSTOC; + } + + /*OCDI*/ + if (value & (~AW_PID_2013_OCDI_MASK)) { + aw_dev_info(aw_dev->dev, "OCDI: occur"); + ret |= INT_TYPE_OCDI; + } + + /*OTHI*/ + if (value & (~AW_PID_2013_OTHI_MASK)) { + aw_dev_info(aw_dev->dev, "OTHI: occur"); + ret |= INT_TYPE_OTHI; + } + + return ret; +} + +static void aw_pid_2013_efver_check(struct aw_device *aw_dev) +{ + unsigned int reg_val; + unsigned int efverh; + unsigned int efverl; + + aw_dev->ops.aw_i2c_read(aw_dev, + AW_PID_2013_EFRM1_REG, ®_val); + + efverh = (((reg_val & (~AW_PID_2013_EFVERH_MASK)) >> + AW_PID_2013_EFVERH_START_BIT) ^ + AW_PID_2013_EFVER_CHECK); + efverl = (((reg_val & (~AW_PID_2013_EFVERL_MASK)) >> + AW_PID_2013_EFVERL_START_BIT) ^ + AW_PID_2013_EFVER_CHECK); + + aw_dev_dbg(aw_dev->dev, "efverh: 0x%0x, efverl: 0x%0x", efverh, efverl); + + if (efverh && efverl) { + aw_dev->bstcfg_enable = AW_BSTCFG_ENABLE; + aw_dev_info(aw_dev->dev, "A2013 EFVER A"); + } else if (efverh && !efverl) { + aw_dev->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_dev_info(aw_dev->dev, "A2013 EFVER B"); + } else { + aw_dev->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_dev_info(aw_dev->dev, "unsupport A2013 EFVER"); + } + aw_dev_info(aw_dev->dev, "bstcfg enable: %d", aw_dev->bstcfg_enable); +} + +static int aw_pid_2013_dev_init(struct aw882xx *aw882xx, int index) +{ + int ret; + struct aw_device *aw_pa = NULL; + aw_pa = devm_kzalloc(aw882xx->dev, sizeof(struct aw_device), GFP_KERNEL); + if (aw_pa == NULL) { + aw_dev_err(aw882xx->dev, "dev kalloc failed"); + return -ENOMEM; + } + + /*call aw device init func*/ + memset(aw_pa->acf_name, 0, AW_NAME_MAX); + memcpy(aw_pa->acf_name, AW_PID_2013_ACF_FILE, strlen(AW_PID_2013_ACF_FILE)); + memset(aw_pa->monitor_name, 0, AW_NAME_MAX); + memcpy(aw_pa->monitor_name, AW_PID_2013_MONITOR_FILE, strlen(AW_PID_2013_MONITOR_FILE)); + + aw_pa->prof_info.prof_desc = NULL; + aw_pa->prof_info.count = 0; + aw_pa->channel = 0; + aw_pa->bop_en = AW_BOP_DISABLE; + aw_pa->vol_step = AW_PID_2013_VOL_STEP; + + aw_pa->index = index; + aw_pa->private_data = (void *)aw882xx; + aw_pa->dev = aw882xx->dev; + aw_pa->i2c = aw882xx->i2c; + aw_pa->ops.aw_get_version = aw882xx_get_version; + aw_pa->ops.aw_get_dev_num = aw882xx_get_dev_num; + aw_pa->ops.aw_set_algo = aw882xx_dev_set_algo_en; + aw_pa->ops.aw_i2c_read = aw882xx_dev_i2c_read; + aw_pa->ops.aw_i2c_write = aw882xx_dev_i2c_write; + aw_pa->ops.aw_i2c_write_bits = aw882xx_dev_i2c_write_bits; + aw_pa->ops.aw_get_volume = aw_pid_2013_get_volume; + aw_pa->ops.aw_set_volume = aw_pid_2013_set_volume; + aw_pa->ops.aw_reg_val_to_db = aw_pid_2013_reg_val_to_db; + aw_pa->ops.aw_i2s_enable = aw_pid_2013_i2s_enable; + aw_pa->ops.aw_check_rd_access = aw_pid_2013_check_rd_access; + aw_pa->ops.aw_check_wr_access = aw_pid_2013_check_wr_access; + aw_pa->ops.aw_get_reg_num = aw_pid_2013_get_reg_num; + aw_pa->ops.aw_get_irq_type =aw_pid_2013_get_irq_type; + + aw_pa->int_desc.mask_reg = AW_PID_2013_SYSINTM_REG; + aw_pa->int_desc.mask_default = AW_PID_2013_SYSINTM_DEFAULT; + aw_pa->int_desc.int_mask = AW_PID_2013_SYSINTM_DEFAULT; + aw_pa->int_desc.st_reg = AW_PID_2013_SYSINT_REG; + + aw_pa->pwd_desc.reg = AW_PID_2013_SYSCTRL_REG; + aw_pa->pwd_desc.mask = AW_PID_2013_PWDN_MASK; + aw_pa->pwd_desc.enable = AW_PID_2013_PWDN_POWER_DOWN_VALUE; + aw_pa->pwd_desc.disable = AW_PID_2013_PWDN_WORKING_VALUE; + + aw_pa->amppd_desc.reg = AW_PID_2013_SYSCTRL_REG; + aw_pa->amppd_desc.mask = AW_PID_2013_AMPPD_MASK; + aw_pa->amppd_desc.enable = AW_PID_2013_AMPPD_POWER_DOWN_VALUE; + aw_pa->amppd_desc.disable = AW_PID_2013_AMPPD_WORKING_VALUE; + + aw_pa->mute_desc.reg = AW_PID_2013_SYSCTRL_REG; + aw_pa->mute_desc.mask = AW_PID_2013_HMUTE_MASK; + aw_pa->mute_desc.enable = AW_PID_2013_HMUTE_ENABLE_VALUE; + aw_pa->mute_desc.disable = AW_PID_2013_HMUTE_DISABLE_VALUE; + + aw_pa->vcalb_desc.vcalb_reg = AW_PID_2013_VSNTM1_REG; + aw_pa->vcalb_desc.vcal_factor = AW_PID_2013_VCAL_FACTOR; + aw_pa->vcalb_desc.cabl_base_value = AW_PID_2013_CABL_BASE_VALUE; + + aw_pa->vcalb_desc.icalk_value_factor = AW_PID_2013_ICABLK_FACTOR; + aw_pa->vcalb_desc.icalk_reg = AW_PID_2013_EFRH_REG; + aw_pa->vcalb_desc.icalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.icalk_reg_mask = AW_PID_2013_EF_ISN_GESLP_MASK; + aw_pa->vcalb_desc.icalk_sign_mask = AW_PID_2013_EF_ISN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.icalk_neg_mask = AW_PID_2013_EF_ISN_GESLP_NEG; + + aw_pa->vcalb_desc.vcalk_reg = AW_PID_2013_EFRM2_REG; + aw_pa->vcalb_desc.vcalk_reg_mask = AW_PID_2013_EF_VSN_GESLP_MASK; + aw_pa->vcalb_desc.vcalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.vcalk_sign_mask = AW_PID_2013_EF_ISN_GESLP2_SIGN_MASK; + aw_pa->vcalb_desc.vcalk_neg_mask = AW_PID_2013_EF_ISN_GESLP2_NEG; + aw_pa->vcalb_desc.vcalk_value_factor = AW_PID_2013_VCABLK_FACTOR; + + aw_pa->sysst_desc.reg = AW_PID_2013_SYSST_REG; + aw_pa->sysst_desc.mask = AW_PID_2013_SYSST_CHECK_MASK; + aw_pa->sysst_desc.st_check = AW_PID_2013_SYSST_CHECK; + aw_pa->sysst_desc.pll_check = AW_PID_2013_PLLS_LOCKED_VALUE; + + aw_pa->profctrl_desc.reg = AW_PID_2013_SYSCTRL_REG; + aw_pa->profctrl_desc.mask = AW_PID_2013_EN_TRAN_MASK; + aw_pa->profctrl_desc.spk_mode = AW_PID_2013_EN_TRAN_SPK_VALUE; + + aw_pa->bstctrl_desc.reg = AW_PID_2013_BSTCTRL2_REG; + aw_pa->bstctrl_desc.mask = AW_PID_2013_BST_MODE_MASK; + aw_pa->bstctrl_desc.frc_bst = AW_PID_2013_BST_MODE_FORCE_BOOST_VALUE; + aw_pa->bstctrl_desc.tsp_type = AW_PID_2013_BST_MODE_TRANSPARENT_VALUE; + + aw_pa->voltage_desc.reg = AW_PID_2013_VBAT_REG; + aw_pa->voltage_desc.int_bit = AW_PID_2013_MONITOR_INT_10BIT; + aw_pa->voltage_desc.vbat_range = AW_PID_2013_MONITOR_VBAT_RANGE; + + aw_pa->temp_desc.reg = AW_PID_2013_TEMP_REG; + aw_pa->temp_desc.neg_mask = AW_PID_2013_MONITOR_TEMP_NEG_MASK; + aw_pa->temp_desc.sign_mask = AW_PID_2013_MONITOR_TEMP_SIGN_MASK; + + aw_pa->cco_mux_desc.reg = AW_PID_2013_PLLCTRL3_REG; + aw_pa->cco_mux_desc.mask = AW_PID_2013_CCO_MUX_MASK; + aw_pa->cco_mux_desc.divided_val = AW_PID_2013_CCO_MUX_DIVIDED_VALUE; + aw_pa->cco_mux_desc.bypass_val = AW_PID_2013_CCO_MUX_BYPASS_VALUE; + + aw_pa->ipeak_desc.reg = AW_PID_2013_BSTCTRL2_REG; + aw_pa->ipeak_desc.mask = AW_PID_2013_BST_IPEAK_MASK; + + aw_pa->volume_desc.reg = AW_PID_2013_SYSCTRL2_REG; + aw_pa->volume_desc.mask = AW_PID_2013_VOL_MASK; + aw_pa->volume_desc.shift = AW_PID_2013_VOL_START_BIT; + aw_pa->volume_desc.mute_volume = AW_PID_2013_MUTE_VOL; + + aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->efcheck_desc.reg = AW_REG_NONE; + + aw_pa->soft_rst.reg = AW_PID_2013_ID_REG; + aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + + aw_pid_2013_efver_check(aw_pa); + ret = aw_device_probe(aw_pa); + + aw882xx->aw_pa = aw_pa; + return ret; +} + +/****************************************************** + * + * A2032 init + * + ******************************************************/ +/*[7 : 4]: -6DB ; [3 : 0]: 0.5DB real_value = value * 2 : 0.5db --> 1*/ +static unsigned int aw_pid_2032_reg_val_to_db(unsigned int value) +{ + return ((value >> 6) * AW_PID_2032_VOL_STEP_DB + (value & 0x3f)); +} + +/* [7 : 4]: -6DB ; [3 : 0]: -0.5DB reg_value = value / step << 4 + value % step ; step = 6 * 2 */ +static unsigned int aw_pid_2032_db_val_to_reg(unsigned int value) +{ + return (((value / AW_PID_2032_VOL_STEP_DB) << 6) + (value % AW_PID_2032_VOL_STEP_DB)); +} + + +static int aw_pid_2032_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + unsigned int reg_value = 0; + unsigned int real_value = aw_pid_2032_db_val_to_reg(value); + + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW_PID_2032_SYSCTRL2_REG, ®_value); + + aw_dev_dbg(aw882xx->dev, "value %d , 0x%x", value, real_value); + + /* [15 : 6] volume */ + real_value = (real_value << 6) | (reg_value & 0x003f); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW_PID_2032_SYSCTRL2_REG, real_value); + return 0; +} + +static int aw_pid_2032_get_volume(struct aw_device *aw_dev, unsigned int *value) +{ + unsigned int reg_value = 0; + unsigned int real_value = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + /* read value */ + aw882xx_i2c_read(aw882xx, AW_PID_2032_SYSCTRL2_REG, ®_value); + + /* [15 : 6] volume */ + real_value = reg_value >> 6; + + real_value = aw_pid_2032_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static void aw_pid_2032_i2s_enable(struct aw_device *aw_dev, bool flag) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + struct aw_cali_desc *desc = &aw_dev->cali_desc; + aw_dev_dbg(aw882xx->dev, "enter"); + + if (!desc->mode) { + aw_dev_dbg(aw_dev->dev, "cali mode is none, needn't set i2s status"); + return; + } + + if (flag) { + aw882xx_i2c_write_bits(aw882xx, AW_PID_2032_I2SCFG1_REG, + AW_PID_2032_I2STXEN_MASK, + AW_PID_2032_I2STXEN_ENABLE_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW_PID_2032_I2SCFG1_REG, + AW_PID_2032_I2STXEN_MASK, + AW_PID_2032_I2STXEN_DISABLE_VALUE); + } +} + +static bool aw_pid_2032_check_rd_access(int reg) +{ + if (reg >= AW_PID_2032_REG_MAX) + return false; + + if (aw_pid_2032_reg_access[reg] & AW_PID_2032_REG_RD_ACCESS) + return true; + else + return false; +} + +static bool aw_pid_2032_check_wr_access(int reg) +{ + if (reg >= AW_PID_2032_REG_MAX) + return false; + + if (aw_pid_2032_reg_access[reg] & + AW_PID_2032_REG_WR_ACCESS) + return true; + else + return false; +} + +static int aw_pid_2032_get_reg_num(void) +{ + return AW_PID_2032_REG_MAX; +} + +static unsigned int aw_pid_2032_get_irq_type(struct aw_device *aw_dev, + unsigned int value) +{ + unsigned int ret = INT_TYPE_NONE; + + /* UVL0 */ + if (value & (~AW_PID_2032_UVLI_MASK)) { + aw_dev_info(aw_dev->dev, "UVLO: occur"); + ret |= INT_TYPE_UVLO; + } + + /* BSTOCM */ + if (value & (~AW_PID_2032_BSTOCI_MASK)) { + aw_dev_info(aw_dev->dev, "BSTOCI: occur"); + ret |= INT_TYPE_BSTOC; + } + + /* OCDI */ + if (value & (~AW_PID_2032_OCDI_MASK)) { + aw_dev_info(aw_dev->dev, "OCDI: occur"); + ret |= INT_TYPE_OCDI; + } + + /* OTHI */ + if (value & (~AW_PID_2032_OTHI_MASK)) { + aw_dev_info(aw_dev->dev, "OTHI: occur"); + ret |= INT_TYPE_OTHI; + } + + return ret; +} + +static int aw_pid_2032_dev_init(struct aw882xx *aw882xx, int index) +{ + int ret; + struct aw_device *aw_pa = NULL; + + aw_pa = devm_kzalloc(aw882xx->dev, sizeof(struct aw_device), GFP_KERNEL); + if (aw_pa == NULL) { + aw_dev_err(aw882xx->dev, "dev kalloc failed"); + return -ENOMEM; + } + + /* call aw device init func */ + memset(aw_pa->acf_name, 0, AW_NAME_MAX); + memcpy(aw_pa->acf_name, AW_PID_2032_ACF_FILE, strlen(AW_PID_2032_ACF_FILE)); + memset(aw_pa->monitor_name, 0, AW_NAME_MAX); + memcpy(aw_pa->monitor_name, AW_PID_2032_MONITOR_FILE, strlen(AW_PID_2032_MONITOR_FILE)); + + aw_pa->prof_info.prof_desc = NULL; + aw_pa->prof_info.count = 0; + aw_pa->channel = 0; + aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_pa->bop_en = AW_BOP_DISABLE; + aw_pa->vol_step = AW_PID_2032_VOL_STEP; + + aw_pa->index = index; + aw_pa->private_data = (void *)aw882xx; + aw_pa->dev = aw882xx->dev; + aw_pa->i2c = aw882xx->i2c; + aw_pa->ops.aw_get_version = aw882xx_get_version; + aw_pa->ops.aw_get_dev_num = aw882xx_get_dev_num; + aw_pa->ops.aw_set_algo = aw882xx_dev_set_algo_en; + aw_pa->ops.aw_i2c_read = aw882xx_dev_i2c_read; + aw_pa->ops.aw_i2c_write = aw882xx_dev_i2c_write; + aw_pa->ops.aw_i2c_write_bits = aw882xx_dev_i2c_write_bits; + aw_pa->ops.aw_get_volume = aw_pid_2032_get_volume; + aw_pa->ops.aw_set_volume = aw_pid_2032_set_volume; + aw_pa->ops.aw_reg_val_to_db = aw_pid_2032_reg_val_to_db; + aw_pa->ops.aw_i2s_enable = aw_pid_2032_i2s_enable; + aw_pa->ops.aw_check_rd_access = aw_pid_2032_check_rd_access; + aw_pa->ops.aw_check_wr_access = aw_pid_2032_check_wr_access; + aw_pa->ops.aw_get_reg_num = aw_pid_2032_get_reg_num; + aw_pa->ops.aw_get_irq_type = aw_pid_2032_get_irq_type; + + aw_pa->int_desc.mask_reg = AW_PID_2032_SYSINTM_REG; + aw_pa->int_desc.mask_default = AW_PID_2032_SYSINTM_DEFAULT; + aw_pa->int_desc.int_mask = AW_PID_2032_SYSINTM_DEFAULT; + aw_pa->int_desc.st_reg = AW_PID_2032_SYSINT_REG; + + aw_pa->pwd_desc.reg = AW_PID_2032_SYSCTRL_REG; + aw_pa->pwd_desc.mask = AW_PID_2032_PWDN_MASK; + aw_pa->pwd_desc.enable = AW_PID_2032_PWDN_POWER_DOWN_VALUE; + aw_pa->pwd_desc.disable = AW_PID_2032_PWDN_WORKING_VALUE; + + aw_pa->amppd_desc.reg = AW_PID_2032_SYSCTRL_REG; + aw_pa->amppd_desc.mask = AW_PID_2032_AMPPD_MASK; + aw_pa->amppd_desc.enable = AW_PID_2032_AMPPD_POWER_DOWN_VALUE; + aw_pa->amppd_desc.disable = AW_PID_2032_AMPPD_WORKING_VALUE; + + aw_pa->mute_desc.reg = AW_PID_2032_SYSCTRL2_REG; + aw_pa->mute_desc.mask = AW_PID_2032_HMUTE_MASK; + aw_pa->mute_desc.enable = AW_PID_2032_HMUTE_ENABLE_VALUE; + aw_pa->mute_desc.disable = AW_PID_2032_HMUTE_DISABLE_VALUE; + + aw_pa->vcalb_desc.vcalb_reg = AW_PID_2032_VTMCTRL3_REG; + aw_pa->vcalb_desc.vcal_factor = AW_PID_2032_VCAL_FACTOR; + aw_pa->vcalb_desc.cabl_base_value = AW_PID_2032_CABL_BASE_VALUE; + + aw_pa->vcalb_desc.icalk_value_factor = AW_PID_2032_ICABLK_FACTOR; + aw_pa->vcalb_desc.icalk_reg = AW_PID_2032_EFRM1_REG; + aw_pa->vcalb_desc.icalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.icalk_reg_mask = AW_PID_2032_EF_ISN_GESLP_MASK; + aw_pa->vcalb_desc.icalk_sign_mask = AW_PID_2032_EF_ISN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.icalk_neg_mask = AW_PID_2032_EF_ISN_GESLP_NEG; + + aw_pa->vcalb_desc.vcalk_reg = AW_PID_2032_EFRH_REG; + aw_pa->vcalb_desc.vcalk_reg_mask = AW_PID_2032_EF_VSN_GESLP_MASK; + aw_pa->vcalb_desc.vcalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.vcalk_sign_mask = AW_PID_2032_EF_VSN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.vcalk_neg_mask = AW_PID_2032_EF_VSN_GESLP_NEG; + aw_pa->vcalb_desc.vcalk_value_factor = AW_PID_2032_VCABLK_FACTOR; + + aw_pa->sysst_desc.reg = AW_PID_2032_SYSST_REG; + aw_pa->sysst_desc.mask = AW_PID_2032_SYSST_CHECK_MASK; + aw_pa->sysst_desc.st_check = AW_PID_2032_SYSST_CHECK; + aw_pa->sysst_desc.pll_check = AW_PID_2032_PLLS_LOCKED_VALUE; + + aw_pa->cco_mux_desc.reg = AW_PID_2032_PLLCTRL1_REG; + aw_pa->cco_mux_desc.mask = AW_PID_2032_CCO_MUX_MASK; + aw_pa->cco_mux_desc.divided_val = AW_PID_2032_CCO_MUX_DIVIDED_VALUE; + aw_pa->cco_mux_desc.bypass_val = AW_PID_2032_CCO_MUX_BYPASS_VALUE; + + aw_pa->voltage_desc.reg = AW_PID_2032_VBAT_REG; + aw_pa->voltage_desc.int_bit = AW_PID_2032_MONITOR_INT_10BIT; + aw_pa->voltage_desc.vbat_range = AW_PID_2032_MONITOR_VBAT_RANGE; + + aw_pa->temp_desc.reg = AW_PID_2032_TEMP_REG; + aw_pa->temp_desc.neg_mask = AW_PID_2032_MONITOR_TEMP_NEG_MASK; + aw_pa->temp_desc.sign_mask = AW_PID_2032_MONITOR_TEMP_SIGN_MASK; + + aw_pa->ipeak_desc.reg = AW_PID_2032_SYSCTRL2_REG; + aw_pa->ipeak_desc.mask = AW_PID_2032_BST_IPEAK_MASK; + + aw_pa->volume_desc.reg = AW_PID_2032_SYSCTRL2_REG; + aw_pa->volume_desc.mask = AW_PID_2032_VOL_MASK; + aw_pa->volume_desc.shift = AW_PID_2032_VOL_START_BIT; + aw_pa->volume_desc.mute_volume = AW_PID_2032_MUTE_VOL; + + aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->efcheck_desc.reg = AW_REG_NONE; + + aw_pa->soft_rst.reg = AW_PID_2032_ID_REG; + aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + + ret = aw_device_probe(aw_pa); + + aw882xx->aw_pa = aw_pa; + return ret; +} + +/* [9 : 6]: -6DB ; [5 : 0]: 0.125DB real_value = value * 8 : 0.125db --> 1 */ +static unsigned int aw_pid_2055_reg_val_to_db(unsigned int value) +{ + return ((value >> 6) * AW_PID_2055_VOL_STEP_DB + (value & 0x3f)); +} + +/* [9 : 6]: -6DB ; [5 : 0]: 0.125DB reg_value = value / step << 6 + value % step ; step = 6 * 8 */ +static unsigned int aw_pid_2055_db_val_to_reg(unsigned int value) +{ + return (((value / AW_PID_2055_VOL_STEP_DB) << 6) + (value % AW_PID_2055_VOL_STEP_DB)); +} + +static int aw_pid_2055_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + unsigned int reg_value = 0; + unsigned int real_value = aw_pid_2055_db_val_to_reg(value); + + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW_PID_2055_SYSCTRL2_REG, ®_value); + + aw_dev_dbg(aw882xx->dev, "value %d , 0x%x", value, real_value); + + /*[9 : 0] volume*/ + real_value = (real_value | (reg_value & 0xfc00)); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW_PID_2055_SYSCTRL2_REG, real_value); + return 0; +} + +static int aw_pid_2055_get_volume(struct aw_device *aw_dev, unsigned int *value) +{ + unsigned int reg_value = 0; + unsigned int real_value = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + /* read value */ + aw882xx_i2c_read(aw882xx, AW_PID_2055_SYSCTRL2_REG, ®_value); + + /* [9 : 0] volume */ + real_value = (reg_value & 0x03ff); + + real_value = aw_pid_2055_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static bool aw_pid_2055_check_rd_access(int reg) +{ + if (reg >= AW_PID_2055_REG_MAX) { + return false; + } + + if (aw_pid_2055_reg_access[reg] & AW_PID_2055_REG_RD_ACCESS) { + return true; + } else { + return false; + } +} + +static bool aw_pid_2055_check_wr_access(int reg) +{ + if (reg >= AW_PID_2055_REG_MAX) + return false; + + if (aw_pid_2055_reg_access[reg] & + AW_PID_2055_REG_WR_ACCESS) + return true; + else + return false; +} + +static int aw_pid_2055_get_reg_num(void) +{ + return AW_PID_2055_REG_MAX; +} + +static unsigned int aw_pid_2055_get_irq_type(struct aw_device *aw_dev, + unsigned int value) +{ + unsigned int ret = INT_TYPE_NONE; + + /* UVL0 */ + if (value & (~AW_PID_2055_UVLI_MASK)) { + aw_dev_info(aw_dev->dev, "UVLO: occur"); + ret |= INT_TYPE_UVLO; + } + + /* BSTOCM */ + if (value & (~AW_PID_2055_BSTOCI_MASK)) { + aw_dev_info(aw_dev->dev, "BSTOCI: occur"); + ret |= INT_TYPE_BSTOC; + } + + /* OCDI */ + if (value & (~AW_PID_2055_OCDI_MASK)) { + aw_dev_info(aw_dev->dev, "OCDI: occur"); + ret |= INT_TYPE_OCDI; + } + + /* OTHI */ + if (value & (~AW_PID_2055_OTHI_MASK)) { + aw_dev_info(aw_dev->dev, "OTHI: occur"); + ret |= INT_TYPE_OTHI; + } + + return ret; +} + +static int aw_pid_2055_dev_init(struct aw882xx *aw882xx, int index) +{ + int ret; + struct aw_device *aw_pa = NULL; + aw_pa = devm_kzalloc(aw882xx->dev, sizeof(struct aw_device), GFP_KERNEL); + if (aw_pa == NULL) { + aw_dev_err(aw882xx->dev, "dev kalloc failed"); + return -ENOMEM; + } + + /*call aw device init func*/ + memset(aw_pa->acf_name, 0, AW_NAME_MAX); + memcpy(aw_pa->acf_name, AW_PID_2055_ACF_FILE, strlen(AW_PID_2055_ACF_FILE)); + memset(aw_pa->monitor_name, 0, AW_NAME_MAX); + memcpy(aw_pa->monitor_name, AW_PID_2055_MONITOR_FILE, strlen(AW_PID_2055_MONITOR_FILE)); + + aw_pa->prof_info.prof_desc = NULL; + aw_pa->prof_info.count = 0; + aw_pa->channel = 0; + aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_pa->bop_en = AW_BOP_DISABLE; + aw_pa->vol_step = AW_PID_2055_VOL_STEP; + + aw_pa->index = index; + aw_pa->private_data = (void *)aw882xx; + aw_pa->dev = aw882xx->dev; + aw_pa->i2c = aw882xx->i2c; + aw_pa->ops.aw_get_version = aw882xx_get_version; + aw_pa->ops.aw_get_dev_num = aw882xx_get_dev_num; + aw_pa->ops.aw_set_algo = aw882xx_dev_set_algo_en; + aw_pa->ops.aw_i2c_read = aw882xx_dev_i2c_read; + aw_pa->ops.aw_i2c_write = aw882xx_dev_i2c_write; + aw_pa->ops.aw_i2c_write_bits = aw882xx_dev_i2c_write_bits; + aw_pa->ops.aw_get_volume = aw_pid_2055_get_volume; + aw_pa->ops.aw_set_volume = aw_pid_2055_set_volume; + aw_pa->ops.aw_reg_val_to_db = aw_pid_2055_reg_val_to_db; + aw_pa->ops.aw_check_rd_access = aw_pid_2055_check_rd_access; + aw_pa->ops.aw_check_wr_access = aw_pid_2055_check_wr_access; + aw_pa->ops.aw_get_reg_num = aw_pid_2055_get_reg_num; + aw_pa->ops.aw_get_irq_type =aw_pid_2055_get_irq_type; + + aw_pa->int_desc.mask_reg = AW_PID_2055_SYSINTM_REG; + aw_pa->int_desc.mask_default = AW_PID_2055_SYSINTM_DEFAULT; + aw_pa->int_desc.int_mask = AW_PID_2055_SYSINTM_DEFAULT; + aw_pa->int_desc.st_reg = AW_PID_2055_SYSINT_REG; + + aw_pa->pwd_desc.reg = AW_PID_2055_SYSCTRL_REG; + aw_pa->pwd_desc.mask = AW_PID_2055_PWDN_MASK; + aw_pa->pwd_desc.enable = AW_PID_2055_PWDN_POWER_DOWN_VALUE; + aw_pa->pwd_desc.disable = AW_PID_2055_PWDN_WORKING_VALUE; + + aw_pa->amppd_desc.reg = AW_PID_2055_SYSCTRL_REG; + aw_pa->amppd_desc.mask = AW_PID_2055_AMPPD_MASK; + aw_pa->amppd_desc.enable = AW_PID_2055_AMPPD_POWER_DOWN_VALUE; + aw_pa->amppd_desc.disable = AW_PID_2055_AMPPD_WORKING_VALUE; + + aw_pa->mute_desc.reg = AW_PID_2055_SYSCTRL_REG; + aw_pa->mute_desc.mask = AW_PID_2055_HMUTE_MASK; + aw_pa->mute_desc.enable = AW_PID_2055_HMUTE_ENABLE_VALUE; + aw_pa->mute_desc.disable = AW_PID_2055_HMUTE_DISABLE_VALUE; + + aw_pa->vcalb_desc.vcalb_reg = AW_REG_NONE; + aw_pa->vcalb_desc.vcalk_reg = AW_REG_NONE; + aw_pa->vcalb_desc.icalk_reg = AW_REG_NONE; + + aw_pa->sysst_desc.reg = AW_PID_2055_SYSST_REG; + aw_pa->sysst_desc.mask = AW_PID_2055_SYSST_CHECK_MASK; + aw_pa->sysst_desc.st_check = AW_PID_2055_SYSST_CHECK; + aw_pa->sysst_desc.pll_check = AW_PID_2055_PLLS_LOCKED_VALUE; + + aw_pa->voltage_desc.reg = AW_REG_NONE; + aw_pa->temp_desc.reg = AW_REG_NONE; + + aw_pa->ipeak_desc.reg = AW_PID_2055_BSTCTRL2_REG; + aw_pa->ipeak_desc.mask = AW_PID_2055_BST_IPEAK_MASK; + + aw_pa->volume_desc.reg = AW_PID_2055_SYSCTRL2_REG; + aw_pa->volume_desc.mask = AW_PID_2055_VOL_MASK; + aw_pa->volume_desc.shift = AW_PID_2055_VOL_START_BIT; + aw_pa->volume_desc.mute_volume = AW_PID_2055_MUTE_VOL; + + aw_pa->cco_mux_desc.reg = AW_PID_2055_PLLCTRL3_REG; + aw_pa->cco_mux_desc.mask = AW_PID_2055_CCO_MUX_MASK; + aw_pa->cco_mux_desc.divided_val = AW_PID_2055_CCO_MUX_DIVIDED_VALUE; + aw_pa->cco_mux_desc.bypass_val = AW_PID_2055_CCO_MUX_BYPASS_VALUE; + + aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->efcheck_desc.reg = AW_REG_NONE; + + aw_pa->soft_rst.reg = AW_PID_2055_ID_REG; + aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + + usleep_range(AW_2000_US, AW_2000_US +10); + aw_pa->ops.aw_i2c_write(aw_pa, AW_PID_2055_INIT_CHECK_REG, + AW_PID_2055_INIT_CHECK_VALUE); + usleep_range(AW_3000_US, AW_3000_US +10); + + usleep_range(AW_2000_US, AW_2000_US +10); + aw_pa->ops.aw_i2c_write(aw_pa, AW_PID_2055_INIT_CHECK_REG, + AW_PID_2055_INIT_CHECK_VALUE); + usleep_range(AW_3000_US, AW_3000_US +10); + + ret = aw_device_probe(aw_pa); + + aw882xx->aw_pa = aw_pa; + return ret; +} + +/* [9 : 6]: -6DB ; [5 : 0]: 0.125DB real_value = value * 8 : 0.125db --> 1 */ +static unsigned int aw_pid_2071_reg_val_to_db(unsigned int value) +{ + return ((value >> 6) * AW_PID_2071_VOL_STEP_DB + (value & 0x3f)); +} + +/* [9 : 6]: -6DB ; [5 : 0]: 0.125DB reg_value = value / step << 6 + value % step ; step = 6 * 8 */ +static unsigned int aw_pid_2071_db_val_to_reg(unsigned int value) +{ + return (((value / AW_PID_2071_VOL_STEP_DB) << 6) + (value % AW_PID_2071_VOL_STEP_DB)); +} + + +static int aw_pid_2071_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + unsigned int reg_value = 0; + unsigned int real_value = aw_pid_2071_db_val_to_reg(value); + + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW_PID_2071_SYSCTRL2_REG, ®_value); + + aw_dev_dbg(aw882xx->dev, "value %d , 0x%x", value, real_value); + + /* [15 : 6] volume */ + real_value = (real_value << 6) | (reg_value & 0x003f); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW_PID_2071_SYSCTRL2_REG, real_value); + return 0; +} + +static int aw_pid_2071_get_volume(struct aw_device *aw_dev, unsigned int *value) +{ + unsigned int reg_value = 0; + unsigned int real_value = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + /* read value */ + aw882xx_i2c_read(aw882xx, AW_PID_2071_SYSCTRL2_REG, ®_value); + + /* [15 : 6] volume */ + real_value = reg_value >> 6; + + real_value = aw_pid_2071_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static void aw_pid_2071_i2s_enable(struct aw_device *aw_dev, bool flag) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + struct aw_cali_desc *desc = &aw_dev->cali_desc; + aw_dev_dbg(aw882xx->dev, "enter"); + + if (!desc->mode) { + aw_dev_dbg(aw_dev->dev, "cali mode is none, needn't set i2s status"); + return; + } + + if (flag) { + aw882xx_i2c_write_bits(aw882xx, AW_PID_2071_I2SCFG1_REG, + AW_PID_2071_I2STXEN_MASK, + AW_PID_2071_I2STXEN_ENABLE_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW_PID_2071_I2SCFG1_REG, + AW_PID_2071_I2STXEN_MASK, + AW_PID_2071_I2STXEN_DISABLE_VALUE); + } +} + +static bool aw_pid_2071_check_rd_access(int reg) +{ + if (reg >= AW_PID_2071_REG_MAX) + return false; + + if (aw_pid_2071_reg_access[reg] & AW_PID_2071_REG_RD_ACCESS) + return true; + else + return false; +} + +static bool aw_pid_2071_check_wr_access(int reg) +{ + if (reg >= AW_PID_2071_REG_MAX) + return false; + + if (aw_pid_2071_reg_access[reg] & + AW_PID_2071_REG_WR_ACCESS) + return true; + else + return false; +} + +static int aw_pid_2071_get_reg_num(void) +{ + return AW_PID_2071_REG_MAX; +} + +static unsigned int aw_pid_2071_get_irq_type(struct aw_device *aw_dev, + unsigned int value) +{ + unsigned int ret = INT_TYPE_NONE; + + /* UVL0 */ + if (value & (~AW_PID_2071_UVLI_MASK)) { + aw_dev_info(aw_dev->dev, "UVLO: occur"); + ret |= INT_TYPE_UVLO; + } + + /* BSTOCM */ + if (value & (~AW_PID_2071_BSTOCI_MASK)) { + aw_dev_info(aw_dev->dev, "BSTOCI: occur"); + ret |= INT_TYPE_BSTOC; + } + + /* OCDI */ + if (value & (~AW_PID_2071_OCDI_MASK)) { + aw_dev_info(aw_dev->dev, "OCDI: occur"); + ret |= INT_TYPE_OCDI; + } + + /* OTHI */ + if (value & (~AW_PID_2071_OTHI_MASK)) { + aw_dev_info(aw_dev->dev, "OTHI: occur"); + ret |= INT_TYPE_OTHI; + } + + return ret; +} + +static int aw_pid_2071_dev_init(struct aw882xx *aw882xx, int index) +{ + int ret; + struct aw_device *aw_pa = NULL; + aw_pa = devm_kzalloc(aw882xx->dev, sizeof(struct aw_device), GFP_KERNEL); + if (aw_pa == NULL) { + aw_dev_err(aw882xx->dev, "dev kalloc failed"); + return -ENOMEM; + } + + /*call aw device init func*/ + memset(aw_pa->acf_name, 0, AW_NAME_MAX); + memcpy(aw_pa->acf_name, AW_PID_2071_ACF_FILE, strlen(AW_PID_2071_ACF_FILE)); + memset(aw_pa->monitor_name, 0, AW_NAME_MAX); + memcpy(aw_pa->monitor_name, AW_PID_2071_MONITOR_FILE, strlen(AW_PID_2071_MONITOR_FILE)); + + aw_pa->prof_info.prof_desc = NULL; + aw_pa->prof_info.count = 0; + aw_pa->channel = 0; + aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_pa->bop_en = AW_BOP_DISABLE; + aw_pa->vol_step = AW_PID_2071_VOL_STEP; + + aw_pa->index = index; + aw_pa->private_data = (void *)aw882xx; + aw_pa->dev = aw882xx->dev; + aw_pa->i2c = aw882xx->i2c; + aw_pa->ops.aw_get_version = aw882xx_get_version; + aw_pa->ops.aw_get_dev_num = aw882xx_get_dev_num; + aw_pa->ops.aw_set_algo = aw882xx_dev_set_algo_en; + aw_pa->ops.aw_i2c_read = aw882xx_dev_i2c_read; + aw_pa->ops.aw_i2c_write = aw882xx_dev_i2c_write; + aw_pa->ops.aw_i2c_write_bits = aw882xx_dev_i2c_write_bits; + aw_pa->ops.aw_get_volume = aw_pid_2071_get_volume; + aw_pa->ops.aw_set_volume = aw_pid_2071_set_volume; + aw_pa->ops.aw_reg_val_to_db = aw_pid_2071_reg_val_to_db; + aw_pa->ops.aw_i2s_enable = aw_pid_2071_i2s_enable; + aw_pa->ops.aw_check_rd_access = aw_pid_2071_check_rd_access; + aw_pa->ops.aw_check_wr_access = aw_pid_2071_check_wr_access; + aw_pa->ops.aw_get_reg_num = aw_pid_2071_get_reg_num; + aw_pa->ops.aw_get_irq_type = aw_pid_2071_get_irq_type; + + aw_pa->int_desc.mask_reg = AW_PID_2071_SYSINTM_REG; + aw_pa->int_desc.mask_default = AW_PID_2071_SYSINTM_DEFAULT; + aw_pa->int_desc.int_mask = AW_PID_2071_SYSINTM_DEFAULT; + aw_pa->int_desc.st_reg = AW_PID_2071_SYSINT_REG; + + aw_pa->pwd_desc.reg = AW_PID_2071_SYSCTRL_REG; + aw_pa->pwd_desc.mask = AW_PID_2071_PWDN_MASK; + aw_pa->pwd_desc.enable = AW_PID_2071_PWDN_POWER_DOWN_VALUE; + aw_pa->pwd_desc.disable = AW_PID_2071_PWDN_WORKING_VALUE; + + aw_pa->amppd_desc.reg = AW_PID_2071_SYSCTRL_REG; + aw_pa->amppd_desc.mask = AW_PID_2071_AMPPD_MASK; + aw_pa->amppd_desc.enable = AW_PID_2071_AMPPD_POWER_DOWN_VALUE; + aw_pa->amppd_desc.disable = AW_PID_2071_AMPPD_WORKING_VALUE; + + aw_pa->mute_desc.reg = AW_PID_2071_SYSCTRL2_REG; + aw_pa->mute_desc.mask = AW_PID_2071_HMUTE_MASK; + aw_pa->mute_desc.enable = AW_PID_2071_HMUTE_ENABLE_VALUE; + aw_pa->mute_desc.disable = AW_PID_2071_HMUTE_DISABLE_VALUE; + + aw_pa->vcalb_desc.vcalb_reg = AW_PID_2071_VTMCTRL3_REG; + aw_pa->vcalb_desc.vcal_factor = AW_PID_2071_VCAL_FACTOR; + aw_pa->vcalb_desc.cabl_base_value = AW_PID_2071_CABL_BASE_VALUE; + + aw_pa->vcalb_desc.icalk_value_factor = AW_PID_2071_ICABLK_FACTOR; + aw_pa->vcalb_desc.icalk_reg = AW_PID_2071_EFRH_REG; + aw_pa->vcalb_desc.icalk_reg_mask = AW_PID_2071_EF_VSN_OFFSET_MASK; + aw_pa->vcalb_desc.icalk_shift = AW_PID_2071_ICALK_SHIFT; + aw_pa->vcalb_desc.icalkl_reg = AW_PID_2071_EFRM1_REG; + aw_pa->vcalb_desc.icalkl_reg_mask = AW_PID_2071_EF_ISN_OFFSET_MASK; + aw_pa->vcalb_desc.icalkl_shift = AW_PID_2071_ICALKL_SHIFT; + aw_pa->vcalb_desc.icalk_sign_mask = AW_PID_2071_EF_ISN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.icalk_neg_mask = AW_PID_2071_EF_ISN_GESLP_NEG; + + aw_pa->vcalb_desc.vcalk_reg = AW_PID_2071_EFRH_REG; + aw_pa->vcalb_desc.vcalk_reg_mask = AW_PID_2071_EF_VSN_GESLP_MASK; + aw_pa->vcalb_desc.vcalkl_reg = AW_REG_NONE; + aw_pa->vcalb_desc.vcalk_sign_mask = AW_PID_2071_EF_VSN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.vcalk_neg_mask = AW_PID_2071_EF_VSN_GESLP_NEG; + aw_pa->vcalb_desc.vcalk_value_factor = AW_PID_2071_VCABLK_FACTOR; + + aw_pa->sysst_desc.reg = AW_PID_2071_SYSST_REG; + aw_pa->sysst_desc.mask = AW_PID_2071_SYSST_CHECK_MASK; + aw_pa->sysst_desc.st_check = AW_PID_2071_SYSST_CHECK; + aw_pa->sysst_desc.pll_check = AW_PID_2071_PLLS_LOCKED_VALUE; + + aw_pa->cco_mux_desc.reg = AW_PID_2071_PLLCTRL1_REG; + aw_pa->cco_mux_desc.mask = AW_PID_2071_CCO_MUX_MASK; + aw_pa->cco_mux_desc.divided_val = AW_PID_2071_CCO_MUX_DIVIDED_VALUE; + aw_pa->cco_mux_desc.bypass_val = AW_PID_2071_CCO_MUX_BYPASS_VALUE; + + aw_pa->voltage_desc.reg = AW_PID_2071_VBAT_REG; + aw_pa->voltage_desc.int_bit = AW_PID_2071_MONITOR_INT_10BIT; + aw_pa->voltage_desc.vbat_range = AW_PID_2071_MONITOR_VBAT_RANGE; + + aw_pa->temp_desc.reg = AW_PID_2071_TEMP_REG; + aw_pa->temp_desc.neg_mask = AW_PID_2071_MONITOR_TEMP_NEG_MASK; + aw_pa->temp_desc.sign_mask = AW_PID_2071_MONITOR_TEMP_SIGN_MASK; + + aw_pa->ipeak_desc.reg = AW_PID_2071_SYSCTRL2_REG; + aw_pa->ipeak_desc.mask = AW_PID_2071_BST_IPEAK_MASK; + + aw_pa->volume_desc.reg = AW_PID_2071_SYSCTRL2_REG; + aw_pa->volume_desc.mask = AW_PID_2071_VOL_MASK; + aw_pa->volume_desc.shift = AW_PID_2071_VOL_START_BIT; + aw_pa->volume_desc.mute_volume = AW_PID_2071_MUTE_VOL; + + aw_pa->bop_desc.reg = AW_REG_NONE; + aw_pa->efcheck_desc.reg = AW_REG_NONE; + + aw_pa->soft_rst.reg = AW_PID_2071_ID_REG; + aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + + ret = aw_device_probe(aw_pa); + + aw882xx->aw_pa = aw_pa; + return ret; +} + +/* [9 : 6]: -6DB ; [5 : 0]: 0.125DB real_value = value * 8 : 0.125db --> 1 */ +static unsigned int aw_pid_2113_reg_val_to_db(unsigned int value) +{ + return ((value >> 6) * AW_PID_2113_VOL_STEP_DB + (value & 0x3f)); +} + +/* [9 : 6]: -6DB ; [5 : 0]: 0.125DB reg_value = value / step << 6 + value % step ; step = 6 * 8 */ +static unsigned int aw_pid_2113_db_val_to_reg(unsigned int value) +{ + return (((value / AW_PID_2113_VOL_STEP_DB) << 6) + (value % AW_PID_2113_VOL_STEP_DB)); +} + +static int aw_pid_2113_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + unsigned int reg_value = 0; + unsigned int real_value = aw_pid_2113_db_val_to_reg(value); + + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW_PID_2113_SYSCTRL2_REG, ®_value); + + aw_dev_dbg(aw882xx->dev, "value %d , 0x%x", value, real_value); + + /*[9 : 0] volume*/ + real_value = (real_value | (reg_value & 0xfc00)); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW_PID_2113_SYSCTRL2_REG, real_value); + return 0; +} + +static int aw_pid_2113_get_volume(struct aw_device *aw_dev, unsigned int *value) +{ + unsigned int reg_value = 0; + unsigned int real_value = 0; + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + /* read value */ + aw882xx_i2c_read(aw882xx, AW_PID_2113_SYSCTRL2_REG, ®_value); + + /* [9 : 0] volume */ + real_value = (reg_value & 0x03ff); + + real_value = aw_pid_2113_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static void aw_pid_2113_i2s_enable(struct aw_device *aw_dev, bool flag) +{ + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + struct aw_cali_desc *desc = &aw_dev->cali_desc; + aw_dev_dbg(aw882xx->dev, "enter"); + + if (!desc->mode) { + aw_dev_dbg(aw_dev->dev, "cali mode is none, needn't set i2s status"); + return; + } + + if (flag) { + aw882xx_i2c_write_bits(aw882xx, AW_PID_2113_I2SCTRL3_REG, + AW_PID_2113_I2STXEN_MASK, + AW_PID_2113_I2STXEN_ENABLE_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW_PID_2113_I2SCTRL3_REG, + AW_PID_2113_I2STXEN_MASK, + AW_PID_2113_I2STXEN_DISABLE_VALUE); + } +} + +static bool aw_pid_2113_check_rd_access(int reg) +{ + if (reg >= AW_PID_2113_REG_MAX) + return false; + + if (aw_pid_2113_reg_access[reg] & AW_PID_2113_REG_RD_ACCESS) + return true; + else + return false; +} + +static bool aw_pid_2113_check_wr_access(int reg) +{ + if (reg >= AW_PID_2113_REG_MAX) + return false; + + if (aw_pid_2113_reg_access[reg] & + AW_PID_2113_REG_WR_ACCESS) + return true; + else + return false; +} + +static int aw_pid_2113_get_reg_num(void) +{ + return AW_PID_2113_REG_MAX; +} + +static unsigned int aw_pid_2113_get_irq_type(struct aw_device *aw_dev, + unsigned int value) +{ + unsigned int ret = INT_TYPE_NONE; + + /* UVL0 */ + if (value & (~AW_PID_2113_UVLI_MASK)) { + aw_dev_info(aw_dev->dev, "UVLO: occur"); + ret |= INT_TYPE_UVLO; + } + + /* BSTOCM */ + if (value & (~AW_PID_2113_BSTOCI_MASK)) { + aw_dev_info(aw_dev->dev, "BSTOCI: occur"); + ret |= INT_TYPE_BSTOC; + } + + /* OCDI */ + if (value & (~AW_PID_2113_OCDI_MASK)) { + aw_dev_info(aw_dev->dev, "OCDI: occur"); + ret |= INT_TYPE_OCDI; + } + + /* OTHI */ + if (value & (~AW_PID_2113_OTHI_MASK)) { + aw_dev_info(aw_dev->dev, "OTHI: occur"); + ret |= INT_TYPE_OTHI; + } + + return ret; +} + +static int aw_pid_2113_frcset_check(struct aw_device *aw_dev) +{ + unsigned int reg_val = 0; + uint16_t temh = 0; + uint16_t teml = 0; + uint16_t tem = 0; + + aw_dev->ops.aw_i2c_read(aw_dev, + AW_PID_2113_EFRH3_REG, ®_val); + temh = ((uint16_t)reg_val & (~AW_PID_2113_TEMH_MASK)); + + aw_dev->ops.aw_i2c_read(aw_dev, + AW_PID_2113_EFRL3_REG, ®_val); + teml = ((uint16_t)reg_val & (~AW_PID_2113_TEML_MASK)); + + if (aw_dev->efuse_check == AW_EF_OR_CHECK) + tem = (temh | teml); + else + tem = (temh & teml); + + if (tem == AW_PID_2113_DEFAULT_CFG) + aw_dev->frcset_en = AW_FRCSET_ENABLE; + else + aw_dev->frcset_en = AW_FRCSET_DISABLE; + + aw_dev_info(aw_dev->dev, "tem is 0x%04x, frcset_en is %d", + tem, aw_dev->frcset_en); + return 0; +} + +static void aw_pid_2113_reg_force_set(struct aw_device *aw_dev) +{ + aw_dev_dbg(aw_dev->dev, "enter"); + + if (aw_dev->frcset_en == AW_FRCSET_ENABLE) { + /*set FORCE_PWM*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, AW_PID_2113_BSTCTRL3_REG, + AW_PID_2113_FORCE_PWM_MASK, AW_PID_2113_FORCE_PWM_FORCEMINUS_PWM_VALUE); + /*set BOOST_OC_WIDTH*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, AW_PID_2113_BSTCTRL5_REG, + AW_PID_2113_BST_OS_WIDTH_MASK, AW_PID_2113_BST_OS_WIDTH_50NS_VALUE); + /*set BURST_LOOPR*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, AW_PID_2113_BSTCTRL6_REG, + AW_PID_2113_BST_LOOPR_MASK, AW_PID_2113_BST_LOOPR_340K_VALUE); + /*set RSQN_DLY*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, AW_PID_2113_BSTCTRL7_REG, + AW_PID_2113_RSQN_DLY_MASK, AW_PID_2113_RSQN_DLY_35NS_VALUE); + /*set BURST_SSMODE*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, AW_PID_2113_BSTCTRL8_REG, + AW_PID_2113_BURST_SSMODE_MASK, AW_PID_2113_BURST_SSMODE_FAST_VALUE); + /*set BST_BURST*/ + aw_dev->ops.aw_i2c_write_bits(aw_dev, AW_PID_2113_BSTCTRL9_REG, + AW_PID_2113_BST_BURST_MASK, AW_PID_2113_BST_BURST_30MA_VALUE); + aw_dev_dbg(aw_dev->dev, "force set reg done!"); + }else { + aw_dev_info(aw_dev->dev, "needn't set reg value"); + } +} + +static int aw_pid_2113_dev_init(struct aw882xx *aw882xx, int index) +{ + int ret; + struct aw_device *aw_pa = NULL; + aw_pa = devm_kzalloc(aw882xx->dev, sizeof(struct aw_device), GFP_KERNEL); + if (aw_pa == NULL) { + aw_dev_err(aw882xx->dev, "dev kalloc failed"); + return -ENOMEM; + } + + /*call aw device init func*/ + memset(aw_pa->acf_name, 0, AW_NAME_MAX); + memcpy(aw_pa->acf_name, AW_PID_2113_ACF_FILE, strlen(AW_PID_2113_ACF_FILE)); + memset(aw_pa->monitor_name, 0, AW_NAME_MAX); + memcpy(aw_pa->monitor_name, AW_PID_2113_MONITOR_FILE, strlen(AW_PID_2113_MONITOR_FILE)); + + aw_pa->prof_info.prof_desc = NULL; + aw_pa->prof_info.count = 0; + aw_pa->channel = 0; + aw_pa->bstcfg_enable = AW_BSTCFG_DISABLE; + aw_pa->bop_en = AW_BOP_DISABLE; + aw_pa->vol_step = AW_PID_2113_VOL_STEP; + + aw_pa->index = index; + aw_pa->private_data = (void *)aw882xx; + aw_pa->dev = aw882xx->dev; + aw_pa->i2c = aw882xx->i2c; + aw_pa->ops.aw_get_version = aw882xx_get_version; + aw_pa->ops.aw_get_dev_num = aw882xx_get_dev_num; + aw_pa->ops.aw_set_algo = aw882xx_dev_set_algo_en; + aw_pa->ops.aw_i2c_read = aw882xx_dev_i2c_read; + aw_pa->ops.aw_i2c_write = aw882xx_dev_i2c_write; + aw_pa->ops.aw_i2c_write_bits = aw882xx_dev_i2c_write_bits; + aw_pa->ops.aw_get_volume = aw_pid_2113_get_volume; + aw_pa->ops.aw_set_volume = aw_pid_2113_set_volume; + aw_pa->ops.aw_reg_val_to_db = aw_pid_2113_reg_val_to_db; + aw_pa->ops.aw_i2s_enable = aw_pid_2113_i2s_enable; + aw_pa->ops.aw_check_rd_access = aw_pid_2113_check_rd_access; + aw_pa->ops.aw_check_wr_access = aw_pid_2113_check_wr_access; + aw_pa->ops.aw_get_reg_num = aw_pid_2113_get_reg_num; + aw_pa->ops.aw_get_irq_type = aw_pid_2113_get_irq_type; + aw_pa->ops.aw_reg_force_set = aw_pid_2113_reg_force_set; + aw_pa->ops.aw_frcset_check = aw_pid_2113_frcset_check; + + aw_pa->int_desc.mask_reg = AW_PID_2113_SYSINTM_REG; + aw_pa->int_desc.mask_default = AW_PID_2113_SYSINTM_DEFAULT; + aw_pa->int_desc.int_mask = AW_PID_2113_SYSINTM_DEFAULT; + aw_pa->int_desc.st_reg = AW_PID_2113_SYSINT_REG; + + aw_pa->pwd_desc.reg = AW_PID_2113_SYSCTRL_REG; + aw_pa->pwd_desc.mask = AW_PID_2113_PWDN_MASK; + aw_pa->pwd_desc.enable = AW_PID_2113_PWDN_POWER_DOWN_VALUE; + aw_pa->pwd_desc.disable = AW_PID_2113_PWDN_WORKING_VALUE; + + aw_pa->amppd_desc.reg = AW_PID_2113_SYSCTRL_REG; + aw_pa->amppd_desc.mask = AW_PID_2113_AMPPD_MASK; + aw_pa->amppd_desc.enable = AW_PID_2113_AMPPD_POWER_DOWN_VALUE; + aw_pa->amppd_desc.disable = AW_PID_2113_AMPPD_WORKING_VALUE; + + aw_pa->mute_desc.reg = AW_PID_2113_SYSCTRL_REG; + aw_pa->mute_desc.mask = AW_PID_2113_HMUTE_MASK; + aw_pa->mute_desc.enable = AW_PID_2113_HMUTE_ENABLE_VALUE; + aw_pa->mute_desc.disable = AW_PID_2113_HMUTE_DISABLE_VALUE; + + aw_pa->vcalb_desc.vcalb_reg = AW_PID_2113_VSNTM1_REG; + aw_pa->vcalb_desc.vcal_factor = AW_PID_2113_VCAL_FACTOR; + aw_pa->vcalb_desc.cabl_base_value = AW_PID_2113_CABL_BASE_VALUE; + + aw_pa->vcalb_desc.icalk_reg = AW_PID_2113_EFRH4_REG; + aw_pa->vcalb_desc.icalk_reg_mask = AW_PID_2113_EF_ISN_GESLP_H_MASK; + aw_pa->vcalb_desc.icalk_shift = AW_PID_2113_ICALK_SHIFT; + aw_pa->vcalb_desc.icalkl_reg = AW_PID_2113_EFRL4_REG; + aw_pa->vcalb_desc.icalkl_reg_mask = AW_PID_2113_EF_ISN_GESLP_L_MASK; + aw_pa->vcalb_desc.icalkl_shift = AW_PID_2113_ICALKL_SHIFT; + aw_pa->vcalb_desc.icalk_sign_mask = AW_PID_2113_EF_ISN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.icalk_neg_mask = AW_PID_2113_EF_ISN_GESLP_NEG; + aw_pa->vcalb_desc.icalk_value_factor = AW_PID_2113_ICABLK_FACTOR; + + aw_pa->vcalb_desc.vcalk_reg = AW_PID_2113_EFRH3_REG; + aw_pa->vcalb_desc.vcalk_reg_mask = AW_PID_2113_EF_VSN_GESLP_H_MASK; + aw_pa->vcalb_desc.vcalk_shift = AW_PID_2113_VCALK_SHIFT; + aw_pa->vcalb_desc.vcalkl_reg = AW_PID_2113_EFRL3_REG; + aw_pa->vcalb_desc.vcalkl_reg_mask = AW_PID_2113_EF_VSN_GESLP_L_MASK; + aw_pa->vcalb_desc.vcalkl_shift = AW_PID_2113_VCALKL_SHIFT; + aw_pa->vcalb_desc.vcalk_sign_mask = AW_PID_2113_EF_VSN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.vcalk_neg_mask = AW_PID_2113_EF_VSN_GESLP_NEG; + aw_pa->vcalb_desc.vcalk_value_factor = AW_PID_2113_VCABLK_FACTOR; + + aw_pa->sysst_desc.reg = AW_PID_2113_SYSST_REG; + aw_pa->sysst_desc.mask = AW_PID_2113_SYSST_CHECK_MASK; + aw_pa->sysst_desc.st_check = AW_PID_2113_SYSST_CHECK; + aw_pa->sysst_desc.pll_check = AW_PID_2113_PLLS_LOCKED_VALUE; + + aw_pa->cco_mux_desc.reg = AW_PID_2113_PLLCTRL1_REG; + aw_pa->cco_mux_desc.mask = AW_PID_2113_CCO_MUX_MASK; + aw_pa->cco_mux_desc.divided_val = AW_PID_2113_CCO_MUX_DIVIDED_VALUE; + aw_pa->cco_mux_desc.bypass_val = AW_PID_2113_CCO_MUX_BYPASS_VALUE; + + aw_pa->voltage_desc.reg = AW_PID_2113_VBAT_REG; + aw_pa->voltage_desc.int_bit = AW_PID_2113_MONITOR_INT_10BIT; + aw_pa->voltage_desc.vbat_range = AW_PID_2113_MONITOR_VBAT_RANGE; + + aw_pa->temp_desc.reg = AW_PID_2113_TEMP_REG; + aw_pa->temp_desc.neg_mask = AW_PID_2113_MONITOR_TEMP_NEG_MASK; + aw_pa->temp_desc.sign_mask = AW_PID_2113_MONITOR_TEMP_SIGN_MASK; + + aw_pa->ipeak_desc.reg = AW_PID_2113_BSTCTRL2_REG; + aw_pa->ipeak_desc.mask = AW_PID_2113_BST_IPEAK_MASK; + + aw_pa->volume_desc.reg = AW_PID_2113_SYSCTRL2_REG; + aw_pa->volume_desc.mask = AW_PID_2113_VOL_MASK; + aw_pa->volume_desc.shift = AW_PID_2113_VOL_START_BIT; + aw_pa->volume_desc.mute_volume = AW_PID_2113_MUTE_VOL; + + aw_pa->bop_desc.reg = AW_PID_2113_SADCCTRL3_REG; + aw_pa->bop_desc.mask = AW_PID_2113_BOP_EN_MASK; + aw_pa->bop_desc.enable = AW_PID_2113_BOP_EN_ENABLE_VALUE; + aw_pa->bop_desc.disbale = AW_PID_2113_BOP_EN_DISABLE_VALUE; + + aw_pa->soft_rst.reg = AW_PID_2113_ID_REG; + aw_pa->soft_rst.reg_value = AW882XX_SOFT_RESET_VALUE; + + usleep_range(AW_2000_US, AW_2000_US +10); + aw_pa->ops.aw_i2c_write(aw_pa, AW_PID_2113_INIT_CHECK_REG, + AW_PID_2113_INIT_CHECK_VALUE); + usleep_range(AW_3000_US, AW_3000_US +10); + + aw_pa->efcheck_desc.reg = AW_PID_2113_DBGCTRL_REG; + aw_pa->efcheck_desc.mask = AW_PID_2113_EF_DBMD_MASK; + aw_pa->efcheck_desc.and_val = AW_PID_2113_AND_VALUE; + aw_pa->efcheck_desc.or_val = AW_PID_2113_OR_VALUE; + + ret = aw_device_probe(aw_pa); + + aw882xx->aw_pa = aw_pa; + return ret; +} + +int aw882xx_init(struct aw882xx *aw882xx, int index) +{ + if (aw882xx->chip_id == PID_1852_ID) + return aw_pid_1852_dev_init(aw882xx, index); + else if (aw882xx->chip_id == PID_2013_ID) + return aw_pid_2013_dev_init(aw882xx, index); + else if (aw882xx->chip_id == PID_2032_ID) + return aw_pid_2032_dev_init(aw882xx, index); + else if (aw882xx->chip_id == PID_2055_ID) + return aw_pid_2055_dev_init(aw882xx, index); + else if (aw882xx->chip_id == PID_2071_ID) + return aw_pid_2071_dev_init(aw882xx, index); + else if (aw882xx->chip_id == PID_2113_ID) + return aw_pid_2113_dev_init(aw882xx, index); + + aw_dev_err(aw882xx->dev, "unsupported chip id 0x%04x", aw882xx->chip_id); + return -EINVAL; +} + diff --git a/techpack/audio/asoc/codecs/aw882xxacf/aw_log.h b/techpack/audio/asoc/codecs/aw882xxacf/aw_log.h new file mode 100644 index 000000000000..31e0f8e1e510 --- /dev/null +++ b/techpack/audio/asoc/codecs/aw882xxacf/aw_log.h @@ -0,0 +1,34 @@ +#ifndef __AWINIC_LOG_H__ +#define __AWINIC_LOG_H__ + +/******************************************** + * print information control + *******************************************/ +#define aw_dev_err(dev, format, ...) \ + do { \ + pr_err("[Awinic][%s]%s: " format "\n", dev_name(dev), __func__, ##__VA_ARGS__); \ + } while (0) +#define aw_dev_info(dev, format, ...) \ + do { \ + pr_info("[Awinic][%s]%s: " format "\n", dev_name(dev), __func__, ##__VA_ARGS__); \ + } while (0) +#define aw_dev_dbg(dev, format, ...) \ + do { \ + pr_debug("[Awinic][%s]%s: " format "\n", dev_name(dev), __func__, ##__VA_ARGS__); \ + } while (0) + +#define aw_pr_err(format, ...) \ + do { \ + pr_err("[Awinic]%s: " format "\n", __func__, ##__VA_ARGS__); \ + } while (0) +#define aw_pr_info(format, ...) \ + do { \ + pr_info("[Awinic]%s: " format "\n", __func__, ##__VA_ARGS__); \ + } while (0) +#define aw_pr_dbg(format, ...) \ + do { \ + pr_debug("[Awinic]%s: " format "\n", __func__, ##__VA_ARGS__); \ + } while (0) + +#endif + diff --git a/techpack/audio/asoc/codecs/aw882xxacf/aw_monitor.c b/techpack/audio/asoc/codecs/aw882xxacf/aw_monitor.c new file mode 100644 index 000000000000..2783fba1a9c6 --- /dev/null +++ b/techpack/audio/asoc/codecs/aw882xxacf/aw_monitor.c @@ -0,0 +1,1230 @@ +/* + * awinic_monitor.c monitor_module + * + * Version: v0.1.17 + * + * Copyright (c) 2019 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw882xx.h" +#include "aw_dsp.h" +#include "aw_monitor.h" +#include "aw_log.h" + + +/***************************************************** + * device monitor + *****************************************************/ +static int aw_get_hmute(struct aw_device *aw_dev) +{ + unsigned int reg_val = 0; + int ret; + struct aw_mute_desc *desc = &aw_dev->mute_desc; + + aw_dev_dbg(aw_dev->dev, "enter"); + + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->reg, ®_val); + if (ret < 0) + return ret; + + if (reg_val & (~desc->mask)) + ret = 1; + else + ret = 0; + + return ret; +} + +static int aw_monitor_get_data_form_system(struct aw_device *aw_dev, + int *data, int data_type) +{ + char name[] = "battery"; + int ret; + union power_supply_propval prop = { 0 }; + struct power_supply *psy = NULL; + + psy = power_supply_get_by_name(name); + if (psy) { + ret = power_supply_get_property(psy, data_type, &prop); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get data failed"); + return -EINVAL; + } + *data = prop.intval; + aw_dev_dbg(aw_dev->dev, "data: %d", *data); + } else { + aw_dev_err(aw_dev->dev, "no struct power supply name : %s", name); + return -EINVAL; + } + return 0; + +} + +static int aw_monitor_get_voltage(struct aw_device *aw_dev, unsigned int *vol) +{ + int ret = -1; + uint16_t local_vol = 0; + struct aw_voltage_desc *desc = &aw_dev->voltage_desc; + + if (desc->reg == AW_REG_NONE) { + ret = aw_monitor_get_data_form_system(aw_dev, (int *)vol, POWER_SUPPLY_PROP_VOLTAGE_NOW); + if (ret) { + aw_dev_err(aw_dev->dev, "get voltage from system failed!"); + return ret; + } + *vol = (*vol) / 1000; + } else { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->reg, vol); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "read voltage failed!"); + return ret; + } + local_vol = ((*vol) * desc->vbat_range) / desc->int_bit; + *vol = local_vol; + } + + aw_dev_info(aw_dev->dev, "chip voltage is %d", *vol); + + return 0; +} + +static int aw_monitor_get_temperature(struct aw_device *aw_dev, int *temp) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t local_temp; + struct aw_temperature_desc *desc = &aw_dev->temp_desc; + + if (desc->reg == AW_REG_NONE) { + ret = aw_monitor_get_data_form_system(aw_dev, temp, POWER_SUPPLY_PROP_TEMP); + if (ret) { + aw_dev_err(aw_dev->dev, "get temperature from system failed!"); + return ret; + } + *temp = (*temp) / 10; + } else { + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->reg, ®_val); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "get temperature failed!"); + return ret; + } + aw_dev_info(aw_dev->dev, "reg val is 0x%04x", reg_val); + local_temp = reg_val; + if (local_temp & (~desc->sign_mask)) + local_temp = local_temp | desc->neg_mask; + + *temp = (int16_t)local_temp; + } + + aw_dev_info(aw_dev->dev, "chip temperature = %d", *temp); + return 0; +} + +static int aw_monitor_get_temp_and_vol(struct aw_device *aw_dev) +{ + struct aw_monitor_desc *monitor = &aw_dev->monitor_desc; + unsigned int voltage = 0; + int current_temp = 0; + int ret = -1; + +#ifdef AW_DEBUG + if (monitor->test_vol == 0) { + ret = aw_monitor_get_voltage(aw_dev, &voltage); + if (ret < 0) + return ret; + } else { + voltage = monitor->test_vol; + } + + if (monitor->test_temp == 0) { + ret = aw_monitor_get_temperature(aw_dev, ¤t_temp); + if (ret) + return ret; + } else { + current_temp = monitor->test_temp; + } +#else + ret = aw_monitor_get_voltage(aw_dev, &voltage); + if (ret < 0) + return ret; + + ret = aw_monitor_get_temperature(aw_dev, ¤t_temp); + if (ret < 0) + return ret; +#endif + + monitor->vol_trace.sum_val += voltage; + monitor->temp_trace.sum_val += current_temp; + monitor->samp_count++; + + return 0; +} + +static int aw_monitor_first_get_data_form_table(struct aw_device *aw_dev, + struct aw_table_info table_info, + struct aw_monitor_trace *data_trace) +{ + int i; + + if (table_info.aw_table == NULL) { + aw_dev_err(aw_dev->dev, "table_info.aw_table is null"); + return -EINVAL; + } + + for (i = 0; i < table_info.table_num; i++) { + if (data_trace->sum_val >= table_info.aw_table[i].min_val) { + memcpy(&data_trace->aw_table, &table_info.aw_table[i], + sizeof(struct aw_table)); + break; + } + } + return 0; +} + +static int aw_monitor_trace_data_from_table(struct aw_device *aw_dev, + struct aw_table_info table_info, + struct aw_monitor_trace *data_trace) +{ + int i; + + if (table_info.aw_table == NULL) { + aw_dev_err(aw_dev->dev, "table_info.aw_table is null"); + return -EINVAL; + } + + for (i = 0; i < table_info.table_num; i++) { + if (data_trace->sum_val >= table_info.aw_table[i].min_val && + data_trace->sum_val <= table_info.aw_table[i].max_val) { + memcpy(&data_trace->aw_table, &table_info.aw_table[i], + sizeof(struct aw_table)); + break; + } + } + + return 0; +} + +static int aw_monitor_get_data_from_table(struct aw_device *aw_dev, + struct aw_table_info table_info, + struct aw_monitor_trace *data_trace, + uint32_t aplha) +{ + struct aw_monitor_desc *monitor = &aw_dev->monitor_desc; + + if (monitor->first_entry == AW_FIRST_ENTRY) { + return aw_monitor_first_get_data_form_table(aw_dev, + table_info, data_trace); + } else { + data_trace->sum_val = data_trace->sum_val / monitor->samp_count; + data_trace->sum_val = ((int32_t)aplha * data_trace->sum_val + + (1000 - (int32_t)aplha) * data_trace->pre_val) / 1000; + return aw_monitor_trace_data_from_table(aw_dev, + table_info, data_trace); + } + + return 0; +} + +static int aw_monitor_get_data(struct aw_device *aw_dev) +{ + struct aw_monitor_desc *monitor = &aw_dev->monitor_desc; + struct aw_monitor_cfg *monitor_cfg = &monitor->monitor_cfg; + struct aw_monitor_trace *vol_trace = &monitor->vol_trace; + struct aw_monitor_trace *temp_trace = &monitor->temp_trace; + int ret; + + if (monitor_cfg->vol_switch) { + ret = aw_monitor_get_data_from_table(aw_dev, + monitor_cfg->vol_info, vol_trace, + monitor_cfg->vol_aplha); + if (ret < 0) + return ret; + } else { + vol_trace->aw_table.ipeak = IPEAK_NONE; + vol_trace->aw_table.gain = GAIN_NONE; + vol_trace->aw_table.vmax = VMAX_NONE; + } + + if (monitor_cfg->temp_switch) { + ret = aw_monitor_get_data_from_table(aw_dev, + monitor_cfg->temp_info, temp_trace, + monitor_cfg->temp_aplha); + if (ret < 0) + return ret; + } else { + temp_trace->aw_table.ipeak = IPEAK_NONE; + temp_trace->aw_table.gain = GAIN_NONE; + temp_trace->aw_table.vmax = VMAX_NONE; + } + + aw_dev_dbg(aw_dev->dev, "filter_vol:%d, vol: ipeak = 0x%x, gain = 0x%x, vmax = 0x%x", + monitor->vol_trace.sum_val, vol_trace->aw_table.ipeak, + vol_trace->aw_table.gain, vol_trace->aw_table.vmax); + + aw_dev_dbg(aw_dev->dev, "filter_temp:%d, temp: ipeak = 0x%x, gain = 0x%x, vmax = 0x%x", + monitor->temp_trace.sum_val, temp_trace->aw_table.ipeak, + temp_trace->aw_table.gain, temp_trace->aw_table.vmax); + return 0; +} + +static void aw_monitor_get_cfg(struct aw_device *aw_dev, + struct aw_table *set_table) +{ + struct aw_monitor_desc *monitor = &aw_dev->monitor_desc; + struct aw_table *temp_data = &monitor->temp_trace.aw_table; + struct aw_table *vol_data = &monitor->vol_trace.aw_table; + + if (temp_data->ipeak == IPEAK_NONE && vol_data->ipeak == IPEAK_NONE) { + memcpy(set_table, temp_data, sizeof(struct aw_table)); + } else if (temp_data->ipeak == IPEAK_NONE) { + memcpy(set_table, vol_data, sizeof(struct aw_table)); + } else if (vol_data->ipeak == IPEAK_NONE) { + memcpy(set_table, temp_data, sizeof(struct aw_table)); + } else { + if (monitor->monitor_cfg.logic_switch == AW_MON_LOGIC_OR) { + set_table->ipeak = (temp_data->ipeak < vol_data->ipeak ? + temp_data->ipeak : vol_data->ipeak); + set_table->gain = (temp_data->gain < vol_data->gain ? + vol_data->gain : temp_data->gain); + set_table->vmax = (temp_data->vmax < vol_data->vmax ? + vol_data->vmax : temp_data->vmax); + } else { + set_table->ipeak = (temp_data->ipeak < vol_data->ipeak ? + vol_data->ipeak : temp_data->ipeak); + set_table->gain = (temp_data->gain < vol_data->gain ? + temp_data->gain : vol_data->gain); + set_table->vmax = (temp_data->vmax < vol_data->vmax ? + temp_data->vmax : vol_data->vmax); + } + } +} + +static void aw_monitor_set_ipeak(struct aw_device *aw_dev, + uint16_t ipeak) +{ + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + unsigned int reg_val = 0; + unsigned int read_reg_val; + int ret; + struct aw_ipeak_desc *desc = &aw_dev->ipeak_desc; + + if (ipeak == IPEAK_NONE || (!monitor_cfg->ipeak_switch)) + return; + + ret = aw_dev->ops.aw_i2c_read(aw_dev, desc->reg, ®_val); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "read ipeak failed"); + return; + } + + read_reg_val = reg_val; + + read_reg_val &= (~desc->mask); + + if (read_reg_val == ipeak) { + aw_dev_dbg(aw_dev->dev, "ipeak = 0x%x, no change", + read_reg_val); + return; + } + + reg_val &= desc->mask; + read_reg_val = ipeak; + reg_val |= read_reg_val; + + ret = aw_dev->ops.aw_i2c_write(aw_dev, desc->reg, reg_val); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "write ipeak failed"); + return; + } + aw_dev_info(aw_dev->dev, "set reg val = 0x%x, ipeak = 0x%x", + reg_val, ipeak); + +} + +static void aw_monitor_set_gain(struct aw_device *aw_dev, uint16_t gain) +{ + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + unsigned int read_volume; + unsigned int set_volume; + int ret; + + if (gain == GAIN_NONE || (!monitor_cfg->gain_switch)) + return; + + ret = aw_dev->ops.aw_get_volume(aw_dev, &read_volume); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "read volume failed"); + return; + } + + gain = aw_dev->ops.aw_reg_val_to_db(gain); + + /*add offset*/ + set_volume = gain + aw_dev->volume_desc.init_volume; + + if (read_volume == set_volume) { + aw_dev_dbg(aw_dev->dev, "gain = 0x%x, no change", read_volume); + return; + } + + ret = aw_dev->ops.aw_set_volume(aw_dev, set_volume); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "set gain failed"); + return; + } + aw_dev_info(aw_dev->dev, "set reg val = 0x%x, gain = 0x%x", + set_volume, gain); + +} + +static void aw_monitor_set_vmax(struct aw_device *aw_dev, + uint32_t vmax) +{ + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + int ret; + + if (vmax == VMAX_NONE || (!monitor_cfg->vmax_switch)) + return; + + if ((vmax == aw_dev->monitor_desc.pre_vmax) && + (aw_dev->monitor_desc.first_entry != AW_FIRST_ENTRY)) { + aw_dev_dbg(aw_dev->dev, "vmax = 0x%x, no change", vmax); + return; + } + + ret = aw_dsp_write_vmax(aw_dev, (char *)&vmax, sizeof(uint32_t)); + if (ret) + return; + + aw_dev->monitor_desc.pre_vmax = vmax; + aw_dev_dbg(aw_dev->dev, "set vmax = 0x%x", vmax); +} + +static int aw_monitor_work(struct aw_device *aw_dev) +{ + struct aw_monitor_desc *monitor = &aw_dev->monitor_desc; + struct aw_monitor_cfg *monitor_cfg = &monitor->monitor_cfg; + struct aw_table set_table; + int ret = -1; + + if (aw_cali_svc_get_cali_status()) { + aw_dev_info(aw_dev->dev, "done nothing during calibration"); + return 0; + } + + ret = aw_monitor_get_temp_and_vol(aw_dev); + if (ret < 0) + return ret; + + if (monitor->samp_count < monitor_cfg->monitor_count && + (monitor->first_entry == AW_NOT_FIRST_ENTRY)) + return 0; + + ret = aw_monitor_get_data(aw_dev); + if (ret < 0) + return ret; + + aw_monitor_get_cfg(aw_dev, &set_table); + + aw_dev_dbg(aw_dev->dev, "set_ipeak = 0x%x, set_gain = 0x%x, set_vmax = 0x%x", + set_table.ipeak, set_table.gain, set_table.vmax); + + aw_monitor_set_ipeak(aw_dev, set_table.ipeak); + + aw_monitor_set_gain(aw_dev, set_table.gain); + + aw_monitor_set_vmax(aw_dev, set_table.vmax); + + + monitor->samp_count = 0; + monitor->temp_trace.pre_val = monitor->temp_trace.sum_val; + monitor->temp_trace.sum_val = 0; + + monitor->vol_trace.pre_val = monitor->vol_trace.sum_val; + monitor->vol_trace.sum_val = 0; + + if (monitor->first_entry == AW_FIRST_ENTRY) + monitor->first_entry = AW_NOT_FIRST_ENTRY; + + return 0; +} + +void aw_monitor_work_func(struct work_struct *work) +{ + struct aw_device *aw_dev = container_of(work, + struct aw_device, monitor_desc.delay_work.work); + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + struct aw_monitor_desc *monitor = &aw_dev->monitor_desc; + + aw_dev_dbg(aw_dev->dev, "scene_mode %d, monitor_status:%d, monitor_switch:%d", + aw_dev->cur_prof, monitor_cfg->monitor_status, monitor_cfg->monitor_switch); + + if ((aw_dev->prof_info.prof_desc[aw_dev->cur_prof].id != AW_PROFILE_RECEIVER) && + (monitor_cfg->monitor_status == AW_MON_CFG_OK) && + monitor_cfg->monitor_switch) { + if (!aw_get_hmute(aw_dev)) { + aw_monitor_work(aw_dev); + queue_delayed_work(aw882xx->work_queue, + &monitor->delay_work, msecs_to_jiffies(monitor_cfg->monitor_time)); + } + } +} + +void aw_check_bop_status(struct aw_device *aw_dev) +{ + struct aw_bop_desc *bop_desc = &aw_dev->bop_desc; + unsigned int reg_val = 0; + + aw_dev_dbg(aw_dev->dev, "enter"); + + if (aw_dev->bop_desc.reg == AW_REG_NONE) + return; + + aw_dev->ops.aw_i2c_read(aw_dev, bop_desc->reg, ®_val); + reg_val = (uint16_t)reg_val & (~bop_desc->mask); + if (reg_val == bop_desc->enable) + aw_dev->bop_en = AW_BOP_ENABLE; + else + aw_dev->bop_en = AW_BOP_DISABLE; + + aw_dev_dbg(aw_dev->dev, "check done! bop status is %d", aw_dev->bop_en); +} + +void aw_monitor_start(struct aw_monitor_desc *monitor_desc) +{ + struct aw_device *aw_dev = container_of(monitor_desc, + struct aw_device, monitor_desc); + struct aw882xx *aw882xx = (struct aw882xx *)aw_dev->private_data; + + aw_dev_info(aw_dev->dev, "enter"); + + monitor_desc->first_entry = AW_FIRST_ENTRY; + monitor_desc->samp_count = 0; + monitor_desc->vol_trace.sum_val = 0; + monitor_desc->temp_trace.sum_val = 0; + + aw_check_bop_status(aw_dev); + + if (aw_dev->bop_en == AW_BOP_ENABLE) { + aw_dev_info(aw_dev->dev, "bop status is enable, monitor can't start"); + return; + } + + queue_delayed_work(aw882xx->work_queue, + &monitor_desc->delay_work, 0); +} + +int aw_monitor_stop(struct aw_monitor_desc *monitor_desc) +{ + struct aw_device *aw_dev = container_of(monitor_desc, + struct aw_device, monitor_desc); + + aw_dev_info(aw_dev->dev, "enter"); + if (delayed_work_pending(&monitor_desc->delay_work)) + cancel_delayed_work_sync(&monitor_desc->delay_work); + + return 0; + +} + +/***************************************************** +* load monitor config +*****************************************************/ +static int aw_monitor_param_check_sum(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len) +{ + int i, check_sum = 0; + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)data; + + if (data_len < sizeof(struct aw_monitor_hdr)) { + aw_dev_err(aw_dev->dev, "data size smaller than hdr , please check monitor bin"); + return -ENOMEM; + } + + for (i = 4; i < data_len; i++) + check_sum += (uint8_t)data[i]; + + if (monitor_hdr->check_sum != check_sum) { + aw_dev_err(aw_dev->dev, "check_sum[%d] is not equal to actual check_sum[%d]", + monitor_hdr->check_sum, check_sum); + return -ENOMEM; + } + + return 0; +} + +static int aw_monitor_check_fw(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)data; + int temp_size, vol_size; + + if (data_len < sizeof(struct aw_monitor_hdr)) { + aw_dev_err(aw_dev->dev, "params size[%d] < struct aw_monitor_hdr size[%d]!", + data_len, (int)sizeof(struct aw_monitor_hdr)); + return -ENOMEM; + } + + if (monitor_hdr->temp_offset > data_len) { + aw_dev_err(aw_dev->dev, "temp_offset[%d] overflow file size[%d]!", + monitor_hdr->temp_offset, data_len); + return -ENOMEM; + } + + if (monitor_hdr->vol_offset > data_len) { + aw_dev_err(aw_dev->dev, "vol_offset[%d] overflow file size[%d]!", + monitor_hdr->vol_offset, data_len); + return -ENOMEM; + } + + temp_size = monitor_hdr->temp_num * monitor_hdr->single_temp_size; + if (temp_size > data_len) { + aw_dev_err(aw_dev->dev, "temp_size:[%d] overflow file size[%d]!", + temp_size, data_len); + return -ENOMEM; + } + + vol_size = monitor_hdr->vol_num * monitor_hdr->single_vol_size; + if (vol_size > data_len) { + aw_dev_err(aw_dev->dev, "vol_size:[%d] overflow file size[%d]!", + vol_size, data_len); + return -ENOMEM; + } + + return 0; +} + +static int aw_monitor_check_fw_v_0_1_1(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)data; + int temp_size, vol_size; + + if (data_len < sizeof(struct aw_monitor_hdr_v_0_1_1)) { + aw_dev_err(aw_dev->dev, "params size[%d] < struct aw_monitor_hdr size[%d]!", + data_len, (int)sizeof(struct aw_monitor_hdr)); + return -ENOMEM; + } + + if (monitor_hdr->temp_offset > data_len) { + aw_dev_err(aw_dev->dev, "temp_offset[%d] overflow file size[%d]!", + monitor_hdr->temp_offset, data_len); + return -ENOMEM; + } + + if (monitor_hdr->vol_offset > data_len) { + aw_dev_err(aw_dev->dev, "vol_offset[%d] overflow file size[%d]!", + monitor_hdr->vol_offset, data_len); + return -ENOMEM; + } + + temp_size = monitor_hdr->temp_num * monitor_hdr->single_temp_size; + if (temp_size > data_len) { + aw_dev_err(aw_dev->dev, "temp_size:[%d] overflow file size[%d]!", + temp_size, data_len); + return -ENOMEM; + } + + vol_size = monitor_hdr->vol_num * monitor_hdr->single_vol_size; + if (vol_size > data_len) { + aw_dev_err(aw_dev->dev, "vol_size:[%d] overflow file size[%d]!", + vol_size, data_len); + return -ENOMEM; + } + + return 0; +} + + +static void aw_monitor_parse_hdr(struct aw_device *aw_dev, uint8_t *data) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)data; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + + monitor_cfg->monitor_switch = monitor_hdr->monitor_switch; + monitor_cfg->monitor_time = monitor_hdr->monitor_time; + monitor_cfg->monitor_count = monitor_hdr->monitor_count; + monitor_cfg->ipeak_switch = monitor_hdr->ipeak_switch; + monitor_cfg->gain_switch = monitor_hdr->gain_switch; + monitor_cfg->vmax_switch = monitor_hdr->vmax_switch; + monitor_cfg->temp_switch = monitor_hdr->temp_switch; + monitor_cfg->temp_aplha = monitor_hdr->temp_aplha; + monitor_cfg->vol_switch = monitor_hdr->vol_switch; + monitor_cfg->vol_aplha = monitor_hdr->vol_aplha; + + aw_dev_info(aw_dev->dev, "chip name:%s", + monitor_hdr->chip_type); + aw_dev_info(aw_dev->dev, "ui ver:0x%x", + monitor_hdr->ui_ver); + + aw_dev_info(aw_dev->dev, "monitor_switch:%d, monitor_time:%d (ms), monitor_count:%d", + monitor_cfg->monitor_switch, monitor_cfg->monitor_time, + monitor_cfg->monitor_count); + + aw_dev_info(aw_dev->dev, "ipeak_switch:%d, gain_switch:%d, vmax_switch:%d", + monitor_cfg->ipeak_switch, monitor_cfg->gain_switch, + monitor_cfg->vmax_switch); + + aw_dev_info(aw_dev->dev, "temp_switch:%d, temp_aplha:%d, vol_switch:%d, vol_aplha:%d", + monitor_cfg->temp_switch, monitor_cfg->temp_aplha, + monitor_cfg->vol_switch, monitor_cfg->vol_aplha); +} + +static void aw_monitor_parse_hdr_v_0_1_1(struct aw_device *aw_dev, uint8_t *data) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)data; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + + monitor_cfg->monitor_switch = (monitor_hdr->enable_flag >> MONITOR_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->monitor_time = monitor_hdr->monitor_time; + monitor_cfg->monitor_count = monitor_hdr->monitor_count; + monitor_cfg->ipeak_switch = (monitor_hdr->enable_flag >> MONITOR_IPEAK_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->logic_switch = (monitor_hdr->enable_flag >> MONITOR_LOGIC_BIT) & MONITOR_EN_MASK; + monitor_cfg->gain_switch = (monitor_hdr->enable_flag >> MONITOR_GAIN_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->vmax_switch = (monitor_hdr->enable_flag >> MONITOR_VMAX_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->temp_switch = (monitor_hdr->enable_flag >> MONITOR_TEMP_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->temp_aplha = monitor_hdr->temp_aplha; + monitor_cfg->vol_switch = (monitor_hdr->enable_flag >> MONITOR_VOL_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->vol_aplha = monitor_hdr->vol_aplha; + + aw_dev_info(aw_dev->dev, "chip name:%s", + monitor_hdr->chip_type); + aw_dev_info(aw_dev->dev, "ui ver:0x%x", + monitor_hdr->ui_ver); + + aw_dev_info(aw_dev->dev, "monitor_switch:%d, monitor_time:%d (ms), monitor_count:%d", + monitor_cfg->monitor_switch, monitor_cfg->monitor_time, + monitor_cfg->monitor_count); + + aw_dev_info(aw_dev->dev, "logic_switch:%d, ipeak_switch:%d, gain_switch:%d, vmax_switch:%d", + monitor_cfg->logic_switch, monitor_cfg->ipeak_switch, + monitor_cfg->gain_switch, monitor_cfg->vmax_switch); + + aw_dev_info(aw_dev->dev, "temp_switch:%d, temp_aplha:%d, vol_switch:%d, vol_aplha:%d", + monitor_cfg->temp_switch, monitor_cfg->temp_aplha, + monitor_cfg->vol_switch, monitor_cfg->vol_aplha); +} + +static void aw_monitor_write_data_to_table(struct aw_device *aw_dev, + struct aw_table_info *table_info, const char *offset_ptr) +{ + int i; + + for (i = 0; i < table_info->table_num * AW_TABLE_SIZE; i += AW_TABLE_SIZE) { + table_info->aw_table[i / AW_TABLE_SIZE].min_val = + AW_GET_16_DATA(offset_ptr[1 + i], offset_ptr[i]); + table_info->aw_table[i / AW_TABLE_SIZE].max_val = + AW_GET_16_DATA(offset_ptr[3 + i], offset_ptr[2 + i]); + table_info->aw_table[i / AW_TABLE_SIZE].ipeak = + AW_GET_16_DATA(offset_ptr[5 + i], offset_ptr[4 + i]); + table_info->aw_table[i / AW_TABLE_SIZE].gain = + AW_GET_16_DATA(offset_ptr[7 + i], offset_ptr[6 + i]); + table_info->aw_table[i / AW_TABLE_SIZE].vmax = + AW_GET_32_DATA(offset_ptr[11 + i], offset_ptr[10 + i], + offset_ptr[9 + i], offset_ptr[8 + i]); + } + + for (i = 0; i < table_info->table_num; i++) + aw_dev_info(aw_dev->dev, + "min_val:%d, max_val:%d, ipeak:0x%x, gain:0x%x, vmax:0x%x", + table_info->aw_table[i].min_val, + table_info->aw_table[i].max_val, + table_info->aw_table[i].ipeak, + table_info->aw_table[i].gain, + table_info->aw_table[i].vmax); + +} + + +static int aw_monitor_parse_temp_data(struct aw_device *aw_dev, uint8_t *data) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)data; + struct aw_table_info *temp_info = + &aw_dev->monitor_desc.monitor_cfg.temp_info; + + aw_dev_info(aw_dev->dev, "===parse temp start ==="); + + if (temp_info->aw_table != NULL) { + devm_kfree(aw_dev->dev, temp_info->aw_table); + temp_info->aw_table = NULL; + } + + temp_info->aw_table = devm_kzalloc(aw_dev->dev, + (monitor_hdr->temp_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (temp_info->aw_table == NULL) + return -ENOMEM; + + temp_info->table_num = monitor_hdr->temp_num; + aw_monitor_write_data_to_table(aw_dev, temp_info, + &data[monitor_hdr->temp_offset]); + aw_dev_info(aw_dev->dev, "===parse temp end ==="); + return 0; +} + +static int aw_monitor_parse_temp_data_v_0_1_1(struct aw_device *aw_dev, uint8_t *data) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)data; + struct aw_table_info *temp_info = + &aw_dev->monitor_desc.monitor_cfg.temp_info; + + aw_dev_info(aw_dev->dev, "===parse temp start ==="); + + if (temp_info->aw_table != NULL) { + devm_kfree(aw_dev->dev, temp_info->aw_table); + temp_info->aw_table = NULL; + } + + temp_info->aw_table = devm_kzalloc(aw_dev->dev, + (monitor_hdr->temp_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (temp_info->aw_table == NULL) + return -ENOMEM; + + temp_info->table_num = monitor_hdr->temp_num; + aw_monitor_write_data_to_table(aw_dev, temp_info, + &data[monitor_hdr->temp_offset]); + aw_dev_info(aw_dev->dev, "===parse temp end ==="); + return 0; +} + + +static int aw_monitor_parse_vol_data(struct aw_device *aw_dev, uint8_t *data) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)data; + struct aw_table_info *vol_info = + &aw_dev->monitor_desc.monitor_cfg.vol_info; + + aw_dev_info(aw_dev->dev, "===parse vol start ==="); + if (vol_info->aw_table != NULL) { + devm_kfree(aw_dev->dev, vol_info->aw_table); + vol_info->aw_table = NULL; + } + + vol_info->aw_table = devm_kzalloc(aw_dev->dev, + (monitor_hdr->vol_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (vol_info->aw_table == NULL) + return -ENOMEM; + + vol_info->table_num = monitor_hdr->vol_num; + aw_monitor_write_data_to_table(aw_dev, vol_info, + &data[monitor_hdr->vol_offset]); + aw_dev_info(aw_dev->dev, "===parse vol end ==="); + return 0; +} + +static int aw_monitor_parse_vol_data_v_0_1_1(struct aw_device *aw_dev, uint8_t *data) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)data; + struct aw_table_info *vol_info = + &aw_dev->monitor_desc.monitor_cfg.vol_info; + + aw_dev_info(aw_dev->dev, "===parse vol start ==="); + if (vol_info->aw_table != NULL) { + devm_kfree(aw_dev->dev, vol_info->aw_table); + vol_info->aw_table = NULL; + } + + vol_info->aw_table = devm_kzalloc(aw_dev->dev, + (monitor_hdr->vol_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (vol_info->aw_table == NULL) + return -ENOMEM; + + vol_info->table_num = monitor_hdr->vol_num; + aw_monitor_write_data_to_table(aw_dev, vol_info, + &data[monitor_hdr->vol_offset]); + aw_dev_info(aw_dev->dev, "===parse vol end ==="); + return 0; +} + + +static int aw_monitor_parse_data(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len) +{ + int ret; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + + ret = aw_monitor_check_fw(aw_dev, data, data_len); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "check monitor failed"); + return ret; + } + + aw_monitor_parse_hdr(aw_dev, data); + + ret = aw_monitor_parse_temp_data(aw_dev, data); + if (ret < 0) + return ret; + + ret = aw_monitor_parse_vol_data(aw_dev, data); + if (ret < 0) { + if (monitor_cfg->temp_info.aw_table != NULL) { + devm_kfree(aw_dev->dev, monitor_cfg->temp_info.aw_table); + monitor_cfg->temp_info.aw_table = NULL; + monitor_cfg->temp_info.table_num = 0; + } + return ret; + } + + monitor_cfg->monitor_status = AW_MON_CFG_OK; + return 0; +} + + +static int aw_monitor_parse_data_v_0_1_1(struct aw_device *aw_dev, + uint8_t *data, uint32_t data_len) +{ + int ret; + struct aw_monitor_cfg *monitor_cfg = &aw_dev->monitor_desc.monitor_cfg; + + ret = aw_monitor_check_fw_v_0_1_1(aw_dev, data, data_len); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "check monitor failed"); + return ret; + } + + aw_monitor_parse_hdr_v_0_1_1(aw_dev, data); + + ret = aw_monitor_parse_temp_data_v_0_1_1(aw_dev, data); + if (ret < 0) + return ret; + + ret = aw_monitor_parse_vol_data_v_0_1_1(aw_dev, data); + if (ret < 0) { + if (monitor_cfg->temp_info.aw_table != NULL) { + devm_kfree(aw_dev->dev, monitor_cfg->temp_info.aw_table); + monitor_cfg->temp_info.aw_table = NULL; + monitor_cfg->temp_info.table_num = 0; + } + return ret; + } + + monitor_cfg->monitor_status = AW_MON_CFG_OK; + return 0; +} + + +int aw_monitor_parse_fw(struct aw_monitor_desc *monitor_desc, + uint8_t *data, uint32_t data_len) +{ + struct aw_monitor_hdr *monitor_hdr = NULL; + struct aw_device *aw_dev = NULL; + int ret; + + if (monitor_desc == NULL || data == NULL) { + pr_err("monitor_desc or data is NULL"); + return -EINVAL; + } + + monitor_hdr = (struct aw_monitor_hdr *)data; + aw_dev = container_of(monitor_desc, + struct aw_device, monitor_desc); + + ret = aw_monitor_param_check_sum(aw_dev, data, data_len); + if (ret < 0) + return ret; + + switch (monitor_hdr->monitor_ver) { + case AW_MONITOR_HDR_VER_0_1_0: + return aw_monitor_parse_data(aw_dev, data, data_len); + case AW_MONITOR_HDR_VER_0_1_1: + return aw_monitor_parse_data_v_0_1_1(aw_dev, data, data_len); + default: + aw_dev_err(aw_dev->dev, "cfg version:0x%x unsupported", + monitor_hdr->monitor_ver); + return -EINVAL; + } +} + +static void aw_monitor_free_firmware(struct aw_device *aw_dev) +{ + struct aw_monitor_cfg *monitor_cfg = + &aw_dev->monitor_desc.monitor_cfg; + + monitor_cfg->monitor_status = AW_MON_CFG_ST; + + if (monitor_cfg->temp_info.aw_table != NULL) { + devm_kfree(aw_dev->dev, monitor_cfg->temp_info.aw_table); + monitor_cfg->temp_info.aw_table = NULL; + } + + if (monitor_cfg->vol_info.aw_table != NULL) { + devm_kfree(aw_dev->dev, monitor_cfg->vol_info.aw_table); + monitor_cfg->vol_info.aw_table = NULL; + } + + memset(monitor_cfg, 0, sizeof(struct aw_monitor_cfg)); +} + +static int aw_monitor_real_time_update_monitor(struct aw_device *aw_dev) +{ + const struct firmware *cont = NULL; + struct aw_container *aw_monitor_cnt = NULL; + int ret; + + ret = request_firmware(&cont, aw_dev->monitor_name, aw_dev->dev); + if (ret < 0) { + aw_dev_err(aw_dev->dev, "failed to read %s", aw_dev->monitor_name); + release_firmware(cont); + return ret; + } + + aw_monitor_cnt = devm_kzalloc(aw_dev->dev, + cont->size + sizeof(uint32_t), GFP_KERNEL); + if (aw_monitor_cnt == NULL) { + aw_dev_err(aw_dev->dev, "alloc failed!"); + release_firmware(cont); + return ret; + } + + aw_monitor_cnt->len = cont->size; + memcpy(aw_monitor_cnt->data, cont->data, cont->size); + release_firmware(cont); + + ret = aw_monitor_parse_fw(&aw_dev->monitor_desc, + aw_monitor_cnt->data, aw_monitor_cnt->len); + if (ret < 0) + aw_dev_err(aw_dev->dev, "parse monitor firmware failed!"); + + devm_kfree(aw_dev->dev, aw_monitor_cnt); + aw_monitor_cnt = NULL; + + return ret; +} + +/***************************************************** + * monitor init + *****************************************************/ +#ifdef AW_DEBUG +static ssize_t aw_vol_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + uint32_t vol = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &vol); + if (ret < 0) + return ret; + + aw_dev_info(aw_dev->dev, "vol set =%d", vol); + aw_dev->monitor_desc.test_vol = vol; + + return count; +} + +static ssize_t aw_vol_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "vol: %d\n", + aw_dev->monitor_desc.test_vol); + return len; +} + +static ssize_t aw_temp_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + int32_t temp = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtoint(buf, 0, &temp); + if (ret < 0) + return ret; + + aw_dev_info(aw_dev->dev, "temp set =%d", temp); + + aw_dev->monitor_desc.test_temp = temp; + + return count; +} + +static ssize_t aw_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx temp: %d\n", + aw_dev->monitor_desc.test_temp); + + return len; +} + +static DEVICE_ATTR(vol, S_IWUSR | S_IRUGO, + aw_vol_show, aw_vol_store); +static DEVICE_ATTR(temp, S_IWUSR | S_IRUGO, + aw_temp_show, aw_temp_store); +#endif + +static ssize_t aw_monitor_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + uint32_t enable = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &enable); + if (ret < 0) + return ret; + + aw_dev_info(aw_dev->dev, "monitor enable set =%d", enable); + + if (aw_dev->monitor_desc.monitor_cfg.monitor_switch == enable) { + return count; + } else { + aw_dev->monitor_desc.monitor_cfg.monitor_switch = enable; + if (enable) + aw_monitor_start(&aw_dev->monitor_desc); + } + + return count; +} + +static ssize_t aw_monitor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + ssize_t len = 0; + + + len += snprintf(buf+len, PAGE_SIZE-len, + "monitor enable: %d\n", + aw_dev->monitor_desc.monitor_cfg.monitor_switch); + return len; +} + +static ssize_t aw_monitor_update_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + struct aw_device *aw_dev = aw882xx->aw_pa; + uint32_t update = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &update); + if (ret < 0) + return ret; + + aw_dev_info(aw_dev->dev, "monitor update = %d", update); + + if (update) { + aw_monitor_stop(&aw_dev->monitor_desc); + aw_monitor_free_firmware(aw_dev); + ret = aw_monitor_real_time_update_monitor(aw_dev); + if (ret < 0) + return ret; + aw_monitor_start(&aw_dev->monitor_desc); + } + + return count; +} + +static DEVICE_ATTR(monitor, S_IWUSR | S_IRUGO, + aw_monitor_show, aw_monitor_store); +static DEVICE_ATTR(monitor_update, S_IWUSR, + NULL, aw_monitor_update_store); + + +static struct attribute *aw_monitor_attr[] = { + &dev_attr_monitor.attr, + &dev_attr_monitor_update.attr, +#ifdef AW_DEBUG + &dev_attr_vol.attr, + &dev_attr_temp.attr, +#endif + NULL +}; + +static struct attribute_group aw_monitor_attr_group = { + .attrs = aw_monitor_attr, +}; + +void aw_monitor_init(struct aw_monitor_desc *monitor_desc) +{ + int ret; + struct aw_device *aw_dev = container_of(monitor_desc, + struct aw_device, monitor_desc); + + aw_dev_info(aw_dev->dev, "enter"); +#ifdef AW_DEBUG + monitor_desc->test_vol = 0; + monitor_desc->test_temp = 0; +#endif + + INIT_DELAYED_WORK(&monitor_desc->delay_work, aw_monitor_work_func); + + ret = sysfs_create_group(&aw_dev->dev->kobj, + &aw_monitor_attr_group); + if (ret < 0) + aw_dev_err(aw_dev->dev, "error creating sysfs attr files"); +} + +void aw_monitor_deinit(struct aw_monitor_desc *monitor_desc) +{ + struct aw_device *aw_dev = + container_of(monitor_desc, struct aw_device, monitor_desc); + + aw_monitor_stop(monitor_desc); + + sysfs_remove_group(&aw_dev->dev->kobj, &aw_monitor_attr_group); +} + diff --git a/techpack/audio/asoc/codecs/aw882xxacf/aw_monitor.h b/techpack/audio/asoc/codecs/aw882xxacf/aw_monitor.h new file mode 100644 index 000000000000..ef42608b212f --- /dev/null +++ b/techpack/audio/asoc/codecs/aw882xxacf/aw_monitor.h @@ -0,0 +1,173 @@ +#ifndef __AW_MONITOR_H__ +#define __AW_MONITOR_H__ + +/*#define AW_DEBUG*/ + +struct aw_table; + +#define AW_TABLE_SIZE sizeof(struct aw_table) +#define AW_MONITOR_DEFAULT_FLAG (0) + +#define IPEAK_NONE (0xFF) +#define GAIN_NONE (0xFF) +#define VMAX_NONE (0xFFFFFFFF) + + +#define AW_GET_32_DATA(w, x, y, z) \ + ((uint32_t)((((uint8_t)w) << 24) | (((uint8_t)x) << 16) | (((uint8_t)y) << 8) | ((uint8_t)z))) +#define AW_GET_16_DATA(x, y) \ + ((uint16_t)((((uint8_t)x) << 8) | (uint8_t)y)) + +enum { + AW_MON_LOGIC_OR = 0, + AW_MON_LOGIC_AND = 1, +}; + +enum { + AW_FIRST_ENTRY = 0, + AW_NOT_FIRST_ENTRY = 1, +}; + +enum aw_monitor_hdr_ver { + AW_MONITOR_HDR_VER_0_1_0 = 0x00010000, + AW_MONITOR_HDR_VER_0_1_1 = 0x00010100, +}; + +enum aw_monitor_init { + AW_MON_CFG_ST = 0, + AW_MON_CFG_OK = 1, +}; + +struct aw_monitor_hdr { + uint32_t check_sum; + uint32_t monitor_ver; + char chip_type[8]; + uint32_t ui_ver; + uint32_t monitor_switch; + uint32_t monitor_time; + uint32_t monitor_count; + uint32_t ipeak_switch; + uint32_t gain_switch; + uint32_t vmax_switch; + uint32_t temp_switch; + uint32_t temp_aplha; + uint32_t temp_num; + uint32_t single_temp_size; + uint32_t temp_offset; + uint32_t vol_switch; + uint32_t vol_aplha; + uint32_t vol_num; + uint32_t single_vol_size; + uint32_t vol_offset; +}; + +#define MONITOR_EN_MASK (0x01) + +enum { + MONITOR_EN_BIT = 0, + MONITOR_LOGIC_BIT = 1, + MONITOR_IPEAK_EN_BIT = 2, + MONITOR_GAIN_EN_BIT = 3, + MONITOR_VMAX_EN_BIT = 4, + MONITOR_TEMP_EN_BIT = 5, + MONITOR_VOL_EN_BIT = 6, +}; + +struct aw_monitor_hdr_v_0_1_1 { + uint32_t check_sum; + uint32_t monitor_ver; + char chip_type[16]; + uint32_t ui_ver; + uint32_t monitor_time; + uint32_t monitor_count; + uint32_t enable_flag; + /* [bit 31:7] */ + /* [bit 6: vol en] */ + /* [bit 5: temp en] */ + /* [bit 4: vmax en] */ + /* [bit 3: gain en] */ + /* [bit 2: ipeak en] */ + /* [bit 1: & or | flag] */ + /* [bit 0: monitor en] */ + uint32_t temp_aplha; + uint32_t temp_num; + uint32_t single_temp_size; + uint32_t temp_offset; + uint32_t vol_aplha; + uint32_t vol_num; + uint32_t single_vol_size; + uint32_t vol_offset; + uint32_t reserver[3]; +}; + + +struct aw_table { + int16_t min_val; + int16_t max_val; + uint16_t ipeak; + uint16_t gain; + uint32_t vmax; +}; + +struct aw_table_info { + uint8_t table_num; + struct aw_table *aw_table; +}; + +struct aw_monitor_cfg { + uint8_t monitor_status; + uint32_t monitor_switch; + uint32_t monitor_time; + uint32_t monitor_count; + uint32_t logic_switch; + uint32_t temp_switch; + uint32_t temp_aplha; + uint32_t vol_switch; + uint32_t vol_aplha; + uint32_t ipeak_switch; + uint32_t gain_switch; + uint32_t vmax_switch; + struct aw_table_info temp_info; + struct aw_table_info vol_info; +}; + +struct aw_monitor_trace { + int32_t pre_val; + int32_t sum_val; + struct aw_table aw_table; +}; + + +/****************************************************************** +* struct aw882xx monitor +*******************************************************************/ +struct aw_monitor_desc { + struct delayed_work delay_work; + struct aw_monitor_cfg monitor_cfg; + + uint8_t first_entry; + uint8_t samp_count; + uint8_t db_offset; + + uint32_t pre_vmax; + struct aw_monitor_trace temp_trace; + struct aw_monitor_trace vol_trace; + +#ifdef AW_DEBUG + uint16_t test_vol; + int16_t test_temp; +#endif +}; + +/****************************************************************** +* aw882xx monitor functions +*******************************************************************/ +void aw_monitor_start(struct aw_monitor_desc *monitor_desc); +int aw_monitor_stop(struct aw_monitor_desc *monitor_desc); +void aw_monitor_init(struct aw_monitor_desc *monitor_desc); +void aw_monitor_deinit(struct aw_monitor_desc *monitor_desc); +int aw_monitor_parse_fw(struct aw_monitor_desc *monitor_desc, + uint8_t *data, uint32_t data_len); + +#endif + diff --git a/techpack/audio/asoc/codecs/aw882xxacf/aw_pid_1852_reg.h b/techpack/audio/asoc/codecs/aw882xxacf/aw_pid_1852_reg.h new file mode 100644 index 000000000000..6f43a1e2693a --- /dev/null +++ b/techpack/audio/asoc/codecs/aw882xxacf/aw_pid_1852_reg.h @@ -0,0 +1,1899 @@ +#ifndef __AW_PID_1852_REG_H__ +#define __AW_PID_1852_REG_H__ + +#define AW_PID_1852_ACF_FILE "aw882xx_pid_1852_acf.bin" +#define AW_PID_1852_MONITOR_FILE "aw882xx_pid_1852_monitor.bin" + +/* registers list */ +#define AW_PID_1852_ID_REG (0x00) +#define AW_PID_1852_SYSST_REG (0x01) +#define AW_PID_1852_SYSINT_REG (0x02) +#define AW_PID_1852_SYSINTM_REG (0x03) +#define AW_PID_1852_SYSCTRL_REG (0x04) +#define AW_PID_1852_SYSCTRL2_REG (0x05) +#define AW_PID_1852_I2SCTRL_REG (0x06) +#define AW_PID_1852_I2SCFG1_REG (0x07) +#define AW_PID_1852_I2SCFG2_REG (0x08) +#define AW_PID_1852_HAGCCFG1_REG (0x09) +#define AW_PID_1852_HAGCCFG2_REG (0x0A) +#define AW_PID_1852_HAGCCFG3_REG (0x0B) +#define AW_PID_1852_HAGCCFG4_REG (0x0C) +#define AW_PID_1852_HAGCCFG5_REG (0x0D) +#define AW_PID_1852_HAGCCFG6_REG (0x0E) +#define AW_PID_1852_HAGCCFG7_REG (0x0F) +#define AW_PID_1852_HAGCST_REG (0x10) +#define AW_PID_1852_PRODID_REG (0x11) +#define AW_PID_1852_VBAT_REG (0x12) +#define AW_PID_1852_TEMP_REG (0x13) +#define AW_PID_1852_PVDD_REG (0x14) +#define AW_PID_1852_DBGCTRL_REG (0x20) +#define AW_PID_1852_I2SINT_REG (0x21) +#define AW_PID_1852_I2SCAPCNT_REG (0x22) +#define AW_PID_1852_CRCIN_REG (0x38) +#define AW_PID_1852_CRCOUT_REG (0x39) +#define AW_PID_1852_VSNCTRL1_REG (0x50) +#define AW_PID_1852_ISNCTRL1_REG (0x52) +#define AW_PID_1852_ISNCTRL2_REG (0x53) +#define AW_PID_1852_VTMCTRL1_REG (0x54) +#define AW_PID_1852_VTMCTRL2_REG (0x55) +#define AW_PID_1852_VTMCTRL3_REG (0x56) +#define AW_PID_1852_ISNDAT_REG (0x57) +#define AW_PID_1852_VSNDAT_REG (0x58) +#define AW_PID_1852_PWMCTRL_REG (0x59) +#define AW_PID_1852_PWMCTRL2_REG (0x5A) +#define AW_PID_1852_BSTCTRL1_REG (0x60) +#define AW_PID_1852_BSTCTRL2_REG (0x61) +#define AW_PID_1852_BSTCTRL3_REG (0x62) +#define AW_PID_1852_BSTDBG1_REG (0x63) +#define AW_PID_1852_BSTDBG2_REG (0x64) +#define AW_PID_1852_BSTDBG3_REG (0x65) +#define AW_PID_1852_PLLCTRL1_REG (0x66) +#define AW_PID_1852_PLLCTRL2_REG (0x67) +#define AW_PID_1852_PLLCTRL3_REG (0x68) +#define AW_PID_1852_CDACTRL1_REG (0x69) +#define AW_PID_1852_CDACTRL2_REG (0x6A) +#define AW_PID_1852_SADCCTRL_REG (0x6B) +#define AW_PID_1852_TESTCTRL1_REG (0x70) +#define AW_PID_1852_TESTCTRL2_REG (0x71) +#define AW_PID_1852_EFCTRL1_REG (0x72) +#define AW_PID_1852_EFCTRL2_REG (0x73) +#define AW_PID_1852_EFWH_REG (0x74) +#define AW_PID_1852_EFWM2_REG (0x75) +#define AW_PID_1852_EFWM1_REG (0x76) +#define AW_PID_1852_EFWL_REG (0x77) +#define AW_PID_1852_EFRH_REG (0x78) +#define AW_PID_1852_EFRM2_REG (0x79) +#define AW_PID_1852_EFRM1_REG (0x7A) +#define AW_PID_1852_EFRL_REG (0x7B) +#define AW_PID_1852_TESTDET_REG (0x7C) + +/******************************************** + * Register Access + *******************************************/ +#define AW_PID_1852_REG_MAX (0x7D) + + +#define AW_PID_1852_REG_NONE_ACCESS (0) +#define AW_PID_1852_REG_RD_ACCESS (1 << 0) +#define AW_PID_1852_REG_WR_ACCESS (1 << 1) + + +static const unsigned char aw_pid_1852_reg_access[AW_PID_1852_REG_MAX] = { + [AW_PID_1852_ID_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_SYSST_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_SYSINT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_SYSINTM_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_SYSCTRL_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_SYSCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_I2SCTRL_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_I2SCFG1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_I2SCFG2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG3_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG4_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG5_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG6_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCCFG7_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_HAGCST_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_PRODID_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_VBAT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_TEMP_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_PVDD_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_DBGCTRL_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_I2SINT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_I2SCAPCNT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_CRCIN_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_CRCOUT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_VSNCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_ISNCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_ISNCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_VTMCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_VTMCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_VTMCTRL3_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_ISNDAT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_VSNDAT_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_PWMCTRL_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_PWMCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_BSTCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_BSTCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_BSTCTRL3_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_BSTDBG1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_BSTDBG2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_BSTDBG3_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_PLLCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_PLLCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_PLLCTRL3_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_CDACTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_CDACTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_SADCCTRL_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_TESTCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_TESTCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFCTRL1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFCTRL2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFWH_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFWM2_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFWM1_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFWL_REG] = (AW_PID_1852_REG_RD_ACCESS | AW_PID_1852_REG_WR_ACCESS), + [AW_PID_1852_EFRH_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_EFRM2_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_EFRM1_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_EFRL_REG] = (AW_PID_1852_REG_RD_ACCESS), + [AW_PID_1852_TESTDET_REG] = (AW_PID_1852_REG_RD_ACCESS), +}; + +/* detail information of registers begin */ +/* ID (0x00) detail */ +/* IDCODE bit 15:0 (ID 0x00) */ +#define AW_PID_1852_IDCODE_START_BIT (0) +#define AW_PID_1852_IDCODE_BITS_LEN (16) +#define AW_PID_1852_IDCODE_MASK \ + (~(((1<_audio.ko as LOCAL_MODULE. +# This means we need to rename the module to _audio.ko +# after audio.ko is built. +KBUILD_OPTIONS += MODNAME=audio_aw8838 +KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) +KBUILD_OPTIONS += $(AUDIO_SELECT) + +########################################################### +include $(CLEAR_VARS) +LOCAL_MODULE := $(AUDIO_SELECT)aw8838.ko +LOCAL_MODULE_KBUILD_NAME := audio_aw8838.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/techpack/audio/asoc/codecs/aw8838/Kbuild b/techpack/audio/asoc/codecs/aw8838/Kbuild new file mode 100644 index 000000000000..6cdbc71ffc2e --- /dev/null +++ b/techpack/audio/asoc/codecs/aw8838/Kbuild @@ -0,0 +1,125 @@ +#ld either as part of a standalone Kernel build or as +# an external module. Determine which mechanism is being used + +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/vendor/qcom/opensource/audio-kernel/include +EXTRA_CFLAGS += -I$(TOP)/vendor/qcom/opensource/audio-kernel/include/uapi/ + +ifeq ($(MODNAME),) + KERNEL_BUILD := 1 +else + KERNEL_BUILD := 0 +endif + + + +ifeq ($(KERNEL_BUILD), 1) + # These are configurable via Kconfig for kernel-based builds + # Need to explicitly configure for Android-based builds + AUDIO_BLD_DIR := $(ANDROID_BUILD_TOP)/kernel/msm-5.4 + AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio +endif + +ifeq ($(KERNEL_BUILD), 0) + ifeq ($(CONFIG_ARCH_SM6150), y) + include $(AUDIO_ROOT)/config/sm6150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h + endif + ifeq ($(CONFIG_ARCH_TRINKET), y) + include $(AUDIO_ROOT)/config/sm6150auto.conf + export + INCS += -include $(AUDIO_ROOT)/config/sm6150autoconf.h + endif + ifeq ($(CONFIG_ARCH_BENGAL), y) + include $(AUDIO_ROOT)/config/bengalauto.conf + export + INCS += -include $(AUDIO_ROOT)/config/bengalautoconf.h + endif + ifeq ($(CONFIG_ARCH_LITO), y) + include $(AUDIO_ROOT)/config/litoauto.conf + export + INCS += -include $(AUDIO_ROOT)/config/litoautoconf.h + endif +endif + +# As per target team, build is done as follows: +# Defconfig : build with default flags +# Slub : defconfig + CONFIG_SLUB_DEBUG := y + +# CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y +# Perf : Using appropriate msmXXXX-perf_defconfig +# +# Shipment builds (user variants) should not have any debug feature +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since +# there is no other way to identify defconfig builds, QTI internal +# representation of perf builds (identified using the string 'perf'), +# is used to identify if the build is a slub or defconfig one. This +# way no critical debug feature will be enabled for perf and shipment +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT +# config. + +############ UAPI ############ +UAPI_DIR := uapi +UAPI_INC := -I$(AUDIO_ROOT)/include/$(UAPI_DIR) + +############ COMMON ############ +COMMON_DIR := include +COMMON_INC := -I$(AUDIO_ROOT)/$(COMMON_DIR) + +############ AW882XX ############ + +# for AW8838 Codec +# 添加 aw8838 相关驱动的所有.c 文件 +ifdef CONFIG_SND_SOC_AW8838 + AW8838_OBJS += aw8838.o + AW8838_OBJS += aw8838_monitor.o +endif + +LINUX_INC += -Iinclude/linux + +INCS += $(COMMON_INC) \ + $(UAPI_INC) + +EXTRA_CFLAGS += $(INCS) + + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +# Currently, for versions of gcc which support it, the kernel Makefile +# is disabling the maybe-uninitialized warning. Re-enable it for the +# AUDIO driver. Note that we must use EXTRA_CFLAGS here so that it +# will override the kernel settings. +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +EXTRA_CFLAGS += -Wmaybe-uninitialized +endif +#EXTRA_CFLAGS += -Wmissing-prototypes + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +EXTRA_CFLAGS += -Wheader-guard +endif + +ifeq ($(KERNEL_BUILD), 0) +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/ipc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers +KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/soc/Module.symvers +endif + +# Module information used by KBuild framework +obj-m += audio_aw8838.o +audio_aw8838-y := $(AW8838_OBJS) +# inject some build related information +DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/techpack/audio/asoc/codecs/aw8838/aw8838.c b/techpack/audio/asoc/codecs/aw8838/aw8838.c new file mode 100644 index 000000000000..159c9efef66c --- /dev/null +++ b/techpack/audio/asoc/codecs/aw8838/aw8838.c @@ -0,0 +1,1582 @@ +/* + * aw8838.c aw8838 codec module + * + * Copyright (c) 2018 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw8838.h" +#include "aw8838_reg.h" + +/****************************************************** + * + * Marco + * + ******************************************************/ +#define AW8838_I2C_NAME "aw8838_smartpa" + +#define AW8838_DRIVER_VERSION "v1.0.5" + +#define AW8838_RATES SNDRV_PCM_RATE_8000_48000 +#define AW8838_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define AW_I2C_RETRIES 5 +#define AW_I2C_RETRY_DELAY 5 +#define AW_READ_CHIPID_RETRIES 5 +#define AW_READ_CHIPID_RETRY_DELAY 5 + +static int aw8838_spk_control = 0; +static int aw8838_rcv_control = 0; + +#define AW8838_MAX_FIRMWARE_LOAD_CNT 20 +static char *aw8838_cfg_name = "aw8838_cfg.bin"; +int aw8838_read_chipid(struct aw8838 *aw8838); +void awinic_set_dai_name(const char* drvdainame, const char*drvname); + +/****************************************************** +* +*distinguish between codecs and components by version +* +******************************************************/ +#ifdef AW_KERNEL_VER_OVER_4_19_1 +static const struct aw_componet_codec_ops aw_componet_codec_ops = { + .aw_snd_soc_kcontrol_codec = snd_soc_kcontrol_component, + .aw_snd_soc_codec_get_drvdata = snd_soc_component_get_drvdata, + .aw_snd_soc_add_codec_controls = snd_soc_add_component_controls, + .aw_snd_soc_unregister_codec = snd_soc_unregister_component, + .aw_snd_soc_register_codec = snd_soc_register_component, +}; +#else +static const struct aw_componet_codec_ops aw_componet_codec_ops = { + .aw_snd_soc_kcontrol_codec = snd_soc_kcontrol_codec, + .aw_snd_soc_codec_get_drvdata = snd_soc_codec_get_drvdata, + .aw_snd_soc_add_codec_controls = snd_soc_add_codec_controls, + .aw_snd_soc_unregister_codec = snd_soc_unregister_codec, + .aw_snd_soc_register_codec = snd_soc_register_codec, +}; +#endif + +static aw_snd_soc_codec_t *aw_get_codec(struct snd_soc_dai *dai) +{ +#ifdef AW_KERNEL_VER_OVER_4_19_1 + return dai->component; +#else + return dai->codec; +#endif +} + + /****************************************************** + * + * aw8838 i2c write/read + * + ******************************************************/ +static int i2c_write(struct aw8838 *aw8838, + unsigned char addr, unsigned int reg_data) +{ + int ret = -1; + u8 wbuf[512] = {0}; + + struct i2c_msg msgs[] = { + { + .addr = aw8838->i2c->addr, + .flags = 0, + .len = 3, + .buf = wbuf, + }, + }; + + wbuf[0] = addr; + wbuf[1] = (unsigned char)((reg_data & 0xff00)>>8); + wbuf[2] = (unsigned char)(reg_data & 0x00ff); + + ret = i2c_transfer(aw8838->i2c->adapter, msgs, 1); + if (ret < 0) { + pr_err("%s: i2c write error: %d\n", __func__, ret); + } + + return ret; +} + +static int i2c_read(struct aw8838 *aw8838, + unsigned char addr, unsigned int *reg_data) +{ + int ret = -1; + unsigned char rbuf[512] = {0}; + unsigned int get_data = 0; + + struct i2c_msg msgs[] = { + { + .addr = aw8838->i2c->addr, + .flags = 0, + .len = 1, + .buf = &addr, + }, + { + .addr = aw8838->i2c->addr, + .flags = I2C_M_RD, + .len = 2, + .buf = rbuf, + }, + }; + + ret = i2c_transfer(aw8838->i2c->adapter, msgs, 2); + if (ret < 0) { + pr_err("%s: i2c read error: %d\n", __func__, ret); + return ret; + } + + get_data = (unsigned int)(rbuf[0] & 0x00ff); + get_data <<= 8; + get_data |= (unsigned int)rbuf[1]; + + *reg_data = get_data; + + return ret; +} + +int aw8838_i2c_write(struct aw8838 *aw8838, + unsigned char reg_addr, unsigned int reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + while(cnt < AW_I2C_RETRIES) { + ret = i2c_write(aw8838, reg_addr, reg_data); + if(ret < 0) { + pr_err("%s: i2c_write cnt=%d error=%d\n", __func__, cnt, ret); + } else { + break; + } + cnt ++; + } + + return ret; +} + +int aw8838_i2c_read(struct aw8838 *aw8838, + unsigned char reg_addr, unsigned int *reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + while(cnt < AW_I2C_RETRIES) { + ret = i2c_read(aw8838, reg_addr, reg_data); + if(ret < 0) { + pr_err("%s: i2c_read cnt=%d error=%d\n", __func__, cnt, ret); + } else { + break; + } + cnt ++; + } + + return ret; +} + +static int aw8838_i2c_write_bits(struct aw8838 *aw8838, + unsigned char reg_addr, unsigned int mask, unsigned int reg_data) +{ + unsigned int reg_val; + + aw8838_i2c_read(aw8838, reg_addr, ®_val); + reg_val &= mask; + reg_val |= reg_data; + aw8838_i2c_write(aw8838, reg_addr, reg_val); + + return 0; +} + +/****************************************************** + * + * aw8838 control + * + ******************************************************/ +int aw8838_get_hmute(struct aw8838 *aw8838) +{ + unsigned int reg_val = 0; + int ret; + + aw_dev_dbg(aw8838->dev, "%s: enter\n", __func__); + + aw8838_i2c_read(aw8838, AW8838_REG_PWMCTRL, ®_val); + if((reg_val & AW8838_BIT_PWMCTRL_HMUTE_ENABLE) == AW8838_BIT_PWMCTRL_HMUTE_DISABLE) + ret = 0; + else + ret = 1; + + return ret; +} + +/*[7 : 4]: -6DB ; [3 : 0]: 0.5DB real_value = value * 2 : 0.5db --> 1*/ +uint32_t aw8838_reg_val_to_db(uint32_t value) +{ + return ((value >> 4) * AW8838_VOL_6DB_STEP + (value & 0x0f)); +} + +/*[7 : 4]: -6DB ; [3 : 0]: -0.5DB reg_value = value / step << 4 + value % step ; step = 6 * 2*/ +static uint32_t aw8838_db_val_to_reg(uint32_t value) +{ + return (((value / AW8838_VOL_6DB_STEP) << 4) + + (value % AW8838_VOL_6DB_STEP)); +} + +int aw8838_set_volume(struct aw8838 *aw8838, uint32_t value) +{ + uint32_t reg_value = 0; + uint32_t real_value = aw8838_db_val_to_reg(value); + + /* cal real value */ + aw8838_i2c_read(aw8838, AW8838_REG_HAGCCFG8, ®_value); + + aw_dev_dbg(aw8838->dev, "%s: value %d , 0x%x\n", + __func__, value, real_value); + + /*15 : 8] volume*/ + real_value = (real_value << AW8838_VOL_REG_SHIFT) | (reg_value & 0x00ff); + + /* write value */ + aw8838_i2c_write(aw8838, AW8838_REG_HAGCCFG8, real_value); + + return 0; +} + +int aw8838_get_volume(struct aw8838 *aw8838, uint32_t *value) +{ + uint32_t reg_value = 0; + uint32_t real_value = 0; + + /* read value */ + aw8838_i2c_read(aw8838, AW8838_REG_HAGCCFG8, ®_value); + + /*[15 : 8] volume*/ + real_value = reg_value >> AW8838_VOL_REG_SHIFT; + + real_value = aw8838_reg_val_to_db(real_value); + *value = real_value; + + return 0; +} + +static void aw8838_run_cpmd(struct aw8838 *aw8838, bool cpmd_flag) +{ + pr_debug("%s enter\n", __func__); + if(cpmd_flag) { + aw8838_i2c_write_bits(aw8838, AW8838_REG_CPCTRL, + AW8838_BIT_CPCTRL_CP_MD_MASK, AW8838_BIT_CPCTRL_CP_MD_TRANS); + } else { + aw8838_i2c_write_bits(aw8838, AW8838_REG_CPCTRL, + AW8838_BIT_CPCTRL_CP_MD_MASK, aw8838->cpmd_default); + } +} + +static void aw8838_run_cppd(struct aw8838 *aw8838, bool cppd) +{ + pr_debug("%s enter\n", __func__); + if(cppd) { + aw8838_i2c_write_bits(aw8838, AW8838_REG_SYSCTRL, + AW8838_BIT_SYSCTRL_CPPD_MASK, AW8838_BIT_SYSCTRL_CPPD_PDN); + } else { + aw8838_i2c_write_bits(aw8838, AW8838_REG_SYSCTRL, + AW8838_BIT_SYSCTRL_CPPD_MASK, AW8838_BIT_SYSCTRL_CPPD_ACTIVE); + } +} + +static void aw8838_run_mute(struct aw8838 *aw8838, bool mute) +{ + pr_debug("%s enter\n", __func__); + + if(mute) { + aw8838_i2c_write_bits(aw8838, AW8838_REG_PWMCTRL, + AW8838_BIT_PWMCTRL_HMUTE_MASK, AW8838_BIT_PWMCTRL_HMUTE_ENABLE); + } else { + aw8838_i2c_write_bits(aw8838, AW8838_REG_PWMCTRL, + AW8838_BIT_PWMCTRL_HMUTE_MASK, AW8838_BIT_PWMCTRL_HMUTE_DISABLE); + } +} + +static void aw8838_run_pwd(struct aw8838 *aw8838, bool pwd) +{ + pr_debug("%s enter\n", __func__); + + if(pwd) { + aw8838_i2c_write_bits(aw8838, AW8838_REG_SYSCTRL, + AW8838_BIT_SYSCTRL_PW_MASK, AW8838_BIT_SYSCTRL_PW_PDN); + } else { + aw8838_i2c_write_bits(aw8838, AW8838_REG_SYSCTRL, + AW8838_BIT_SYSCTRL_PW_MASK, AW8838_BIT_SYSCTRL_PW_ACTIVE); + } +} + +static void aw8838_spk_rcv_mode(struct aw8838 *aw8838) +{ + pr_info("%s spk_rcv=%d\n", __func__, aw8838->spk_rcv_mode); + + if(aw8838->spk_rcv_mode == AW8838_SPEAKER_MODE) { + aw8838_i2c_write_bits(aw8838, AW8838_REG_SYSCTRL, + AW8838_BIT_SYSCTRL_MODE_MASK, AW8838_BIT_SYSCTRL_SPK_MODE); + } else if(aw8838->spk_rcv_mode == AW8838_RECEIVER_MODE){ + aw8838_i2c_write_bits(aw8838, AW8838_REG_SYSCTRL, + AW8838_BIT_SYSCTRL_MODE_MASK, AW8838_BIT_SYSCTRL_RCV_MODE); + } else { + } +} + + +static void aw8838_start(struct aw8838 *aw8838) +{ + unsigned int reg_val = 0; + unsigned int iis_check_max = 5; + unsigned int i = 0; + + pr_info("%s enter\n", __func__); + + if ((aw8838->spk_rcv_mode == AW8838_SPEAKER_MODE) && !aw8838_spk_control) { + pr_info("%s: spk can not allow power\n", __func__); + return; + } else if ((aw8838->spk_rcv_mode == AW8838_RECEIVER_MODE) && !aw8838_rcv_control) { + pr_info("%s: rcv can not allow power\n", __func__); + return; + } + + aw8838_run_cpmd(aw8838, false); + aw8838_run_pwd(aw8838, false); + msleep(1); + aw8838_run_cppd(aw8838, false); + msleep(1); + + for(i=0; imonitor); + + return; + } + msleep(2); + } + aw8838_run_pwd(aw8838, true); + pr_err("%s: iis signal check error\n", __func__); +} + +static void aw8838_stop(struct aw8838 *aw8838) +{ + pr_debug("%s enter\n", __func__); + + aw8838_monitor_stop(&aw8838->monitor); + aw8838_run_mute(aw8838, true); + aw8838_run_cpmd(aw8838, true); + aw8838_run_cppd(aw8838, true); + aw8838_run_pwd(aw8838, true); +} + +static void aw8838_container_update(struct aw8838 *aw8838, + struct aw8838_container *aw8838_cont) +{ + int i = 0; + int reg_addr = 0; + int reg_val = 0; + unsigned int cpmd_val; + + pr_debug("%s enter\n", __func__); + + for(i=0; ilen; i+=4) { + reg_addr = (aw8838_cont->data[i+1]<<8) + aw8838_cont->data[i+0]; + reg_val = (aw8838_cont->data[i+3]<<8) + aw8838_cont->data[i+2]; + pr_info("%s: reg=0x%04x, val = 0x%04x\n", __func__, reg_addr, reg_val); + aw8838_i2c_write(aw8838, (unsigned char)reg_addr, (unsigned int)reg_val); + } + + aw8838_i2c_read(aw8838, AW8838_REG_CPCTRL, &cpmd_val); + aw8838->cpmd_default = cpmd_val & (~AW8838_BIT_CPCTRL_CP_MD_MASK); + pr_debug("%s:cpmd_default=0x%04x\n", __func__, aw8838->cpmd_default); + + aw8838_get_volume(aw8838, &aw8838->db_offset); + pr_debug("%s exit\n", __func__); +} + +static void aw8838_cfg_loaded(const struct firmware *cont, void *context) +{ + struct aw8838 *aw8838 = context; + struct aw8838_container *aw8838_cfg; + unsigned int i = 0; + + if (!cont) { + pr_err("%s: failed to read %s\n", __func__, aw8838_cfg_name); + release_firmware(cont); + return; + } + + pr_info("%s: loaded %s - size: %zu\n", __func__, aw8838_cfg_name, + cont ? cont->size : 0); + + for(i=0; isize; i++) { + pr_info("%s: addr:0x%04x, data:0x%02x\n", __func__, i, *(cont->data+i)); + } + + aw8838_cfg = kzalloc(cont->size+sizeof(int), GFP_KERNEL); + if (!aw8838_cfg) { + release_firmware(cont); + pr_err("%s: error allocating memory\n", __func__); + return; + } + aw8838_cfg->len = cont->size; + memcpy(aw8838_cfg->data, cont->data, cont->size); + release_firmware(cont); + + aw8838_container_update(aw8838, aw8838_cfg); + + kfree(aw8838_cfg); + + aw8838->init = 1; + pr_info("%s: cfg update complete\n", __func__); + + aw8838_spk_rcv_mode(aw8838); + aw8838_start(aw8838); +} + +static int aw8838_load_cfg(struct aw8838 *aw8838) +{ + pr_info("%s enter\n", __func__); + + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + aw8838_cfg_name, aw8838->dev, GFP_KERNEL, + aw8838, aw8838_cfg_loaded); +} + +static void aw8838_cold_start(struct aw8838 *aw8838) +{ + int ret = -1; + + pr_info("%s enter\n", __func__); + + ret = aw8838_load_cfg(aw8838); + if(ret) { + pr_err("%s: cfg loading requested failed: %d\n", __func__, ret); + } +} + +static void aw8838_smartpa_cfg(struct aw8838 *aw8838, bool flag) +{ + pr_info("%s, flag = %d\n", __func__, flag); + + if(flag == true) { + if(aw8838->init == 0) { + pr_info("%s, init = %d\n", __func__, aw8838->init); + aw8838_cold_start(aw8838); + } else { + aw8838_spk_rcv_mode(aw8838); + aw8838_start(aw8838); + } + } else { + aw8838_stop(aw8838); + } +} + +/****************************************************** + * + * kcontrol + * + ******************************************************/ + static const char *const spk_function[] = { "Off", "On" }; + static const char *const rcv_function[] = { "Off", "On" }; + static const DECLARE_TLV_DB_SCALE(digital_gain,0,50,0); + + struct soc_mixer_control aw8838_mixer ={ + .reg = AW8838_REG_HAGCCFG8, + .shift = AW8838_VOL_REG_SHIFT, + .max = AW8838_VOLUME_MAX, + .min = AW8838_VOLUME_MIN, + }; + +static int aw8838_volume_info(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control*) kcontrol->private_value; + + //set kcontrol info + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mc->max - mc->min; + return 0; +} + +static int aw8838_volume_get(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = aw_componet_codec_ops.aw_snd_soc_kcontrol_codec(kcontrol); + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + + struct soc_mixer_control *mc = (struct soc_mixer_control*) kcontrol->private_value; + + aw8838_i2c_read(aw8838, AW8838_REG_HAGCCFG8, ®_val); + ucontrol->value.integer.value[0] = (reg_val >> mc->shift)\ + &(AW8838_BIT_HAGCCFG8_VOL_MASK); + return 0; +} + +static int aw8838_volume_put(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control*) kcontrol->private_value; + aw_snd_soc_codec_t *codec = aw_componet_codec_ops.aw_snd_soc_kcontrol_codec(kcontrol); + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + unsigned int value = 0; + unsigned int reg_value = 0; + + //value is right + value = ucontrol->value.integer.value[0]; + if(value > (mc->max-mc->min)|| value <0){ + pr_err("%s:value over range \n",__func__); + return -1; + } + + //smartpa have clk + aw8838_i2c_read(aw8838, AW8838_REG_SYSST, ®_value); + if(!(reg_value&AW8838_BIT_SYSST_PLLS)){ + pr_err("%s: NO I2S CLK ,cat not write reg \n",__func__); + return 0; + } + //cal real value + value = value << mc->shift& (~AW8838_BIT_HAGCCFG8_VOL_MASK); + aw8838_i2c_read(aw8838, AW8838_REG_HAGCCFG8, ®_value); + value = value | (reg_value&0x00ff); + + //write value + aw8838_i2c_write(aw8838, AW8838_REG_HAGCCFG8, value); + + return 0; +} + +static struct snd_kcontrol_new aw8838_volume = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "aw8838_rx_volume", + .access= SNDRV_CTL_ELEM_ACCESS_TLV_READ|SNDRV_CTL_ELEM_ACCESS_READWRITE, + .tlv.p = (digital_gain), + .info = aw8838_volume_info, + .get = aw8838_volume_get, + .put = aw8838_volume_put, + .private_value = (unsigned long)&aw8838_mixer, +}; + +static int aw8838_spk_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw8838_spk_control=%d\n", __func__, aw8838_spk_control); + ucontrol->value.integer.value[0] = aw8838_spk_control; + return 0; +} + +static int aw8838_spk_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = aw_componet_codec_ops.aw_snd_soc_kcontrol_codec(kcontrol); + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n ", + __func__, ucontrol->value.integer.value[0]); + if(ucontrol->value.integer.value[0] == aw8838_spk_control) + return 1; + + aw8838_spk_control = ucontrol->value.integer.value[0]; + + aw8838->spk_rcv_mode = AW8838_SPEAKER_MODE; + + aw8838_spk_rcv_mode(aw8838); + + if (!aw8838_get_hmute(aw8838)) + aw8838_monitor_start(&aw8838->monitor); + + return 0; +} + +static int aw8838_chipid_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int status = 0; + int ret = 0; + struct aw8838 *aw8838; + + aw_snd_soc_codec_t *codec = aw_componet_codec_ops.aw_snd_soc_kcontrol_codec(kcontrol); + aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + ret = aw8838_read_chipid(aw8838); + if(ret < 0) + status = 0; + else + status = 1; + + ucontrol->value.integer.value[0] = status; + + pr_info("chip id get %ld", ucontrol->value.integer.value[0]); + + return 0; +} + + +static int aw8838_rcv_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw8838_rcv_control=%d\n", __func__, aw8838_rcv_control); + ucontrol->value.integer.value[0] = aw8838_rcv_control; + return 0; +} +static int aw8838_rcv_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + aw_snd_soc_codec_t *codec = aw_componet_codec_ops.aw_snd_soc_kcontrol_codec(kcontrol); + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n ", + __func__, ucontrol->value.integer.value[0]); + if(ucontrol->value.integer.value[0] == aw8838_rcv_control) + return 1; + + aw8838_rcv_control = ucontrol->value.integer.value[0]; + + aw8838->spk_rcv_mode = AW8838_RECEIVER_MODE; + + aw8838_spk_rcv_mode(aw8838); + + return 0; +} +static const struct soc_enum aw8838_snd_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rcv_function), rcv_function), +}; + +static struct snd_kcontrol_new aw8838_controls[] = { + SOC_ENUM_EXT("aw8838_speaker_switch", aw8838_snd_enum[0], + aw8838_spk_get, aw8838_spk_set), + SOC_ENUM_EXT("aw8838_receiver_switch", aw8838_snd_enum[1], + aw8838_rcv_get, aw8838_rcv_set), + SOC_SINGLE_EXT("HAC_HW_STATUS", SND_SOC_NOPM, 0, 0x7fffffff, 0, + aw8838_chipid_get, NULL), +}; + +static void aw8838_add_codec_controls(struct aw8838 *aw8838) +{ + pr_info("%s enter\n", __func__); + + aw_componet_codec_ops.aw_snd_soc_add_codec_controls(aw8838->codec, aw8838_controls, + ARRAY_SIZE(aw8838_controls)); + + aw_componet_codec_ops.aw_snd_soc_add_codec_controls(aw8838->codec, &aw8838_volume,1); +} + + +/****************************************************** + * + * Digital Audio Interface + * + ******************************************************/ +static int aw8838_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + pr_info("%s: enter\n", __func__); + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) { + pr_info("%s:capture\n", __func__); + return 0; + } + + aw8838_run_pwd(aw8838, false); + + return 0; +} + +static int aw8838_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + //struct aw8838 *aw8838 = snd_soc_codec_get_drvdata(dai->codec); + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + + pr_info("%s: fmt=0x%x\n", __func__, fmt); + + /* Supported mode: regular I2S, slave, or PDM */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { + dev_err(codec->dev, "%s: invalid codec master mode\n", __func__); + return -EINVAL; + } + break; + default: + dev_err(codec->dev, "%s: unsupported DAI format %d\n", __func__, + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + return 0; +} + +static int aw8838_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + pr_info("%s: freq=%d\n", __func__, freq); + + aw8838->sysclk = freq; + return 0; +} + +static int aw8838_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + unsigned int rate = 0; + int reg_value = 0; + int width = 0; + /* Supported */ + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) { + pr_debug("%s: requested rate: %d, sample size: %d\n", __func__, + rate, snd_pcm_format_width(params_format(params))); + return 0; + } + + //get rate param + aw8838->rate=rate = params_rate(params); + pr_debug("%s: requested rate: %d, sample size: %d\n", __func__, rate, + snd_pcm_format_width(params_format(params))); + //match rate + switch(rate) + { + case 8000: + reg_value = AW8838_BIT_I2SCTRL_SR_8K; + break; + case 16000: + reg_value = AW8838_BIT_I2SCTRL_SR_16K; + break; + case 32000: + reg_value = AW8838_BIT_I2SCTRL_SR_32K; + break; + case 44100: + reg_value = AW8838_BIT_I2SCTRL_SR_44P1K; + break; + case 48000: + reg_value = AW8838_BIT_I2SCTRL_SR_48K; + break; + default: + reg_value = AW8838_BIT_I2SCTRL_SR_48K; + pr_err("%s: rate can not support\n", __func__); + break; + } + //set chip rate + if(-1 != reg_value){ + aw8838_i2c_write_bits(aw8838, AW8838_REG_I2SCTRL, + AW8838_BIT_I2SCTRL_SR_MASK, reg_value); + } + + //get bit width + width = params_width(params); + pr_debug("%s: width = %d \n",__func__,width); + switch(width) + { + case 16: + reg_value = AW8838_BIT_I2SCTRL_FMS_16BIT; + break; + case 20: + reg_value = AW8838_BIT_I2SCTRL_FMS_20BIT; + break; + case 24: + reg_value = AW8838_BIT_I2SCTRL_FMS_24BIT; + break; + case 32: + reg_value = AW8838_BIT_I2SCTRL_FMS_32BIT; + break; + default: + reg_value = AW8838_BIT_I2SCTRL_FMS_16BIT; + pr_err("%s: width can not support\n", __func__); + break; + } + //set width + if(-1 != reg_value){ + aw8838_i2c_write_bits(aw8838, AW8838_REG_I2SCTRL, + AW8838_BIT_I2SCTRL_FMS_MASK, reg_value); + } + + return 0; +} + +static int aw8838_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + pr_info("%s: mute state=%d\n", __func__, mute); + + if (stream != SNDRV_PCM_STREAM_PLAYBACK) { + pr_info("%s:capture\n", __func__); + return 0; + } + + if (!(aw8838->flags & AW8838_FLAG_START_ON_MUTE)) + return 0; + + if (mute) { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + aw8838->pstream = 0; + else + aw8838->cstream = 0; + if (aw8838->pstream != 0 || aw8838->cstream != 0) + return 0; + + mutex_lock(&aw8838->cfg_lock); + aw8838_smartpa_cfg(aw8838, false); + mutex_unlock(&aw8838->cfg_lock); + } else { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + aw8838->pstream = 1; + else + aw8838->cstream = 1; + + mutex_lock(&aw8838->cfg_lock); + aw8838_smartpa_cfg(aw8838, true); + mutex_unlock(&aw8838->cfg_lock); + } + + return 0; +} + +static void aw8838_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + pr_info("%s: enter\n", __func__); + + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) { + pr_info("%s:capture\n", __func__); + return; + } + + aw8838->rate = 0; + aw8838_run_pwd(aw8838, true); +} + +static const struct snd_soc_dai_ops aw8838_dai_ops = { + .startup = aw8838_startup, + .set_fmt = aw8838_set_fmt, + .set_sysclk = aw8838_set_dai_sysclk, + .hw_params = aw8838_hw_params, + .mute_stream = aw8838_mute, + .shutdown = aw8838_shutdown, +}; + +static struct snd_soc_dai_driver aw8838_dai[] = { + { + .name = "aw8838-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AW8838_RATES, + .formats = AW8838_FORMATS, + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AW8838_RATES, + .formats = AW8838_FORMATS, + }, + .ops = &aw8838_dai_ops, + .symmetric_rates = 1, + .symmetric_channels = 1, + .symmetric_samplebits = 1, + }, +}; + +/***************************************************** + * + * codec driver + * + *****************************************************/ +static int aw8838_probe(aw_snd_soc_codec_t *codec) +{ + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + int ret = 0; + + pr_info("%s enter\n", __func__); + + aw8838->codec = codec; + + aw8838_add_codec_controls(aw8838); + +#if 0 + if (codec->dev->of_node) + dev_set_name(codec->dev, "%s", "aw8838_smartpa"); +#endif + schedule_delayed_work(&aw8838->monitor.load_fw_work, + msecs_to_jiffies(AW_LOAD_MON_FW_DELAY_TIME)); + + pr_info("%s exit\n", __func__); + + return ret; +} + +#ifdef AW_KERNEL_VER_OVER_4_19_1 +static void aw8838_remove(struct snd_soc_component *component) +{ + //struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + pr_info("%s enter\n", __func__); + + return; +} +#else +static int aw8838_remove(struct snd_soc_codec *codec) +{ + //struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + pr_info("%s enter\n", __func__); + + return 0; +} +#endif + +static unsigned int aw8838_codec_read(aw_snd_soc_codec_t *codec,unsigned int reg) +{ + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + unsigned int value =0; + int ret; + pr_debug("%s:enter \n",__func__); + + if(aw8838_reg_access[reg]®_RD_ACCESS){ + ret=aw8838_i2c_read(aw8838,reg,&value); + if(ret<0){ + pr_debug("%s: read register failed \n",__func__); + return ret; + } + }else{ + pr_debug("%s:Register 0x%x NO read access\n",__func__,reg); + return -1; + } + return value; +} + +static int aw8838_codec_write(aw_snd_soc_codec_t *codec,unsigned int reg,unsigned int value) +{ + int ret ; + struct aw8838 *aw8838 = aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + pr_debug("%s:enter ,reg is 0x%x value is 0x%x\n",__func__,reg,value); + + if(aw8838_reg_access[reg]®_WR_ACCESS){ + ret=aw8838_i2c_write(aw8838,reg,value); + return ret; + }else{ + pr_debug("%s: Register 0x%x NO write access \n",__func__,reg); + } + + return -1; +} + +#ifdef AW_KERNEL_VER_OVER_4_19_1 +static struct snd_soc_component_driver soc_codec_dev_aw8838 = { + .probe = aw8838_probe, + .remove = aw8838_remove, + .read = aw8838_codec_read, + .write = aw8838_codec_write, +}; +#else +static struct snd_soc_codec_driver soc_codec_dev_aw8838 = { + .probe = aw8838_probe, + .remove = aw8838_remove, + .read = aw8838_codec_read, + .write = aw8838_codec_write, + .reg_cache_size = AW8838_REG_MAX, + .reg_word_size = 2, +}; +#endif + +/****************************************************** + * + * irq + * + ******************************************************/ +static void aw8838_interrupt_setup(struct aw8838 *aw8838) +{ + unsigned int reg_val; + + pr_info("%s enter\n", __func__); + + aw8838_i2c_read(aw8838, AW8838_REG_SYSINTM, ®_val); + reg_val &= (~AW8838_BIT_SYSINTM_UVLOM); + reg_val &= (~AW8838_BIT_SYSINTM_PLLM); + reg_val &= (~AW8838_BIT_SYSINTM_OTHM); + reg_val &= (~AW8838_BIT_SYSINTM_OCDM); + aw8838_i2c_write(aw8838, AW8838_REG_SYSINTM, reg_val); +} + +static void aw8838_interrupt_clear(struct aw8838 *aw8838) +{ + unsigned int reg_val = 0; + + pr_info("%s enter\n", __func__); + + aw8838_i2c_read(aw8838, AW8838_REG_SYSST, ®_val); + pr_info("%s: reg SYSST=0x%x\n", __func__, reg_val); + + aw8838_i2c_read(aw8838, AW8838_REG_SYSINT, ®_val); + pr_info("%s: reg SYSINT=0x%x\n", __func__, reg_val); + + aw8838_i2c_read(aw8838, AW8838_REG_SYSINTM, ®_val); + pr_info("%s: reg SYSINTM=0x%x\n", __func__, reg_val); +} + +static irqreturn_t aw8838_irq(int irq, void *data) +{ + struct aw8838 *aw8838 = data; + + pr_info("%s enter\n", __func__); + + aw8838_interrupt_clear(aw8838); + + pr_info("%s exit\n", __func__); + + return IRQ_HANDLED; +} + +/***************************************************** + * + * device tree + * + *****************************************************/ +static int aw8838_parse_dt(struct device *dev, struct aw8838 *aw8838, + struct device_node *np) +{ + aw8838->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (aw8838->reset_gpio < 0) { + dev_err(dev, "%s: no reset gpio provided, will not HW reset device\n", __func__); + return -1; + } else { + dev_info(dev, "%s: reset gpio provided ok\n", __func__); + } + aw8838->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (aw8838->irq_gpio < 0) { + dev_info(dev, "%s: no irq gpio provided.\n", __func__); + } else { + dev_info(dev, "%s: irq gpio provided ok.\n", __func__); + } + + aw8838_parse_monitor_dt(&aw8838->monitor); + + return 0; +} + +int aw8838_hw_reset(struct aw8838 *aw8838) +{ + pr_info("%s enter\n", __func__); + + if (aw8838 && gpio_is_valid(aw8838->reset_gpio)) { + gpio_set_value_cansleep(aw8838->reset_gpio, 0); + msleep(1); + gpio_set_value_cansleep(aw8838->reset_gpio, 1); + msleep(1); + } else { + dev_err(aw8838->dev, "%s: failed\n", __func__); + } + return 0; +} + +/***************************************************** + * + * check chip id + * + *****************************************************/ +int aw8838_read_chipid(struct aw8838 *aw8838) +{ + int ret = -1; + unsigned int cnt = 0; + unsigned int reg = 0; + + while(cnt < AW_READ_CHIPID_RETRIES) { + ret = aw8838_i2c_read(aw8838, AW8838_REG_ID, ®); + if (ret < 0) { + dev_err(aw8838->dev, "%s: failed to read register AW8838_REG_ID: %d\n", __func__, ret); + return -EIO; + } + switch (reg) { + case 0x1730: + pr_info("%s aw8838 detected\n", __func__); + aw8838->flags |= AW8838_FLAG_SKIP_INTERRUPTS; + aw8838->flags |= AW8838_FLAG_START_ON_MUTE; + aw8838->chipid = AW8838_ID; + pr_info("%s aw8838->flags=0x%x\n", __func__, aw8838->flags); + return 0; + default: + pr_info("%s unsupported device revision (0x%x)\n", __func__, reg ); + break; + } + cnt ++; + + msleep(AW_READ_CHIPID_RETRY_DELAY); + } + + return -EINVAL; +} + +/****************************************************** + * + * sys bin attribute + * + *****************************************************/ +static ssize_t aw8838_reg_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct aw8838 *aw8838 = dev_get_drvdata(dev); + + if (count != 1) { + pr_info("invalid register address"); + return -EINVAL; + } + + aw8838->reg = buf[0]; + + return 1; +} + +static ssize_t aw8838_rw_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct aw8838 *aw8838 = dev_get_drvdata(dev); + u8 *data; + int ret; + int retries = AW_I2C_RETRIES; + + data = kmalloc(count+1, GFP_KERNEL); + if (data == NULL) { + pr_err("can not allocate memory\n"); + return -ENOMEM; + } + + data[0] = aw8838->reg; + memcpy(&data[1], buf, count); + + retry: + ret = i2c_master_send(aw8838->i2c, data, count+1); + if (ret < 0) { + pr_warn("i2c error, retries left: %d\n", retries); + if (retries) { + retries--; + msleep(AW_I2C_RETRY_DELAY); + goto retry; + } + } + + kfree(data); + return ret; +} + +static ssize_t aw8838_rw_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct aw8838 *aw8838 = dev_get_drvdata(dev); + struct i2c_msg msgs[] = { + { + .addr = aw8838->i2c->addr, + .flags = 0, + .len = 1, + .buf = &aw8838->reg, + }, + { + .addr = aw8838->i2c->addr, + .flags = I2C_M_RD, + .len = count, + .buf = buf, + }, + }; + int ret; + int retries = AW_I2C_RETRIES; + retry: + ret = i2c_transfer(aw8838->i2c->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) { + pr_warn("i2c error, retries left: %d\n", retries); + if (retries) { + retries--; + msleep(AW_I2C_RETRY_DELAY); + goto retry; + } + return ret; + } + /* ret contains the number of i2c messages send */ + return 1 + ((ret > 1) ? count : 0); +} + +static struct bin_attribute dev_attr_rw = { + .attr = { + .name = "rw", + .mode = S_IRUSR | S_IWUSR, + }, + .size = 0, + .read = aw8838_rw_read, + .write = aw8838_rw_write, +}; + +static struct bin_attribute dev_attr_regaddr = { + .attr = { + .name = "regaddr", + .mode = S_IWUSR, + }, + .size = 0, + .read = NULL, + .write = aw8838_reg_write, +}; + +/****************************************************** + * + * sys group attribute: reg + * + ******************************************************/ +static ssize_t aw8838_reg_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0}; + + if(2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) { + aw8838_i2c_write(aw8838, databuf[0], databuf[1]); + } + + return count; +} + +static ssize_t aw8838_reg_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned char i = 0; + unsigned int reg_val = 0; + for(i = 0; i < AW8838_REG_MAX; i ++) { + if(!(aw8838_reg_access[i]®_RD_ACCESS)) + continue; + aw8838_i2c_read(aw8838, i, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, "reg:0x%02x=0x%04x \n", i, reg_val); + } + return len; +} + +static ssize_t aw8838_spk_rcv_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0}; + + if(1 == sscanf(buf, "%d", &databuf[0])) { + aw8838->spk_rcv_mode = databuf[0]; + } + + return count; +} + +static ssize_t aw8838_spk_rcv_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + ssize_t len = 0; + if(aw8838->spk_rcv_mode == AW8838_SPEAKER_MODE) { + len += snprintf(buf+len, PAGE_SIZE-len, "aw8838 spk_rcv: %d, speaker mode\n", aw8838->spk_rcv_mode); + } else if (aw8838->spk_rcv_mode == AW8838_RECEIVER_MODE) { + len += snprintf(buf+len, PAGE_SIZE-len, "aw8838 spk_rcv: %d, receiver mode\n", aw8838->spk_rcv_mode); + } else { + len += snprintf(buf+len, PAGE_SIZE-len, "aw8838 spk_rcv: %d, unknown mode\n", aw8838->spk_rcv_mode); + } + + return len; +} + + +static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, aw8838_reg_show, aw8838_reg_store); +static DEVICE_ATTR(spk_rcv, S_IWUSR | S_IRUGO, aw8838_spk_rcv_show, aw8838_spk_rcv_store); + +static struct attribute *aw8838_attributes[] = { + &dev_attr_reg.attr, + &dev_attr_spk_rcv.attr, + NULL +}; + +static struct attribute_group aw8838_attribute_group = { + .attrs = aw8838_attributes +}; + + +/****************************************************** + * + * i2c driver + * + ******************************************************/ +static int aw8838_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + struct snd_soc_dai_driver *dai; + struct aw8838 *aw8838; + struct device_node *np = i2c->dev.of_node; + int irq_flags = 0; + int ret = -1; + const char *aw8838drvname = AW8838_I2C_NAME; + + pr_info("%s enter\n", __func__); + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed\n"); + return -EIO; + } + + aw8838 = devm_kzalloc(&i2c->dev, sizeof(struct aw8838), GFP_KERNEL); + if (aw8838 == NULL) + return -ENOMEM; + + aw8838->dev = &i2c->dev; + aw8838->i2c = i2c; + + i2c_set_clientdata(i2c, aw8838); + mutex_init(&aw8838->cfg_lock); + + /* aw8838 rst & int */ + if (np) { + ret = aw8838_parse_dt(&i2c->dev, aw8838, np); + if (ret) { + dev_err(&i2c->dev, "%s: failed to parse device tree node\n", __func__); + goto err_parse_dt; + } + } else { + aw8838->reset_gpio = -1; + aw8838->irq_gpio = -1; + } + + if (gpio_is_valid(aw8838->reset_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw8838->reset_gpio, + GPIOF_OUT_INIT_LOW, "aw8838_rst"); + if (ret){ + dev_err(&i2c->dev, "%s: rst request failed\n", __func__); + goto err_gpio_request; + } + } + + if (gpio_is_valid(aw8838->irq_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw8838->irq_gpio, + GPIOF_DIR_IN, "aw8838_int"); + if (ret){ + dev_err(&i2c->dev, "%s: int request failed\n", __func__); + goto err_gpio_request; + } + } + + /* hardware reset */ + aw8838_hw_reset(aw8838); + + /* aw8838 chip id */ + ret = aw8838_read_chipid(aw8838); + if (ret < 0) { + dev_err(&i2c->dev, "%s: aw8838_read_chipid failed ret=%d\n", __func__, ret); + goto err_id; + } + +#if 0 + /* aw8838 device name */ + if (i2c->dev.of_node) { + dev_set_name(&i2c->dev, "%s", "aw8838_smartpa"); + } else { + dev_err(&i2c->dev, "%s failed to set device name: %d\n", __func__, ret); + } +#endif + /* register codec */ + dai = devm_kzalloc(&i2c->dev, sizeof(aw8838_dai), GFP_KERNEL); + if (!dai) { + goto err_dai_kzalloc; + } + + memcpy(dai, aw8838_dai, sizeof(aw8838_dai)); + pr_info("%s dai->name(%s)\n", __func__, dai->name); + + awinic_set_dai_name(dai->name, aw8838drvname); + + ret = aw_componet_codec_ops.aw_snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_aw8838, dai, ARRAY_SIZE(aw8838_dai)); + if (ret < 0) { + dev_err(&i2c->dev, "%s failed to register aw8838: %d\n", __func__, ret); + goto err_register_codec; + } + + /* aw8838 irq */ + if (gpio_is_valid(aw8838->irq_gpio) && + !(aw8838->flags & AW8838_FLAG_SKIP_INTERRUPTS)) { + aw8838_interrupt_setup(aw8838); + /* register irq handler */ + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&i2c->dev, + gpio_to_irq(aw8838->irq_gpio), + NULL, aw8838_irq, irq_flags, + "aw8838", aw8838); + if (ret != 0) { + dev_err(&i2c->dev, "failed to request IRQ %d: %d\n", + gpio_to_irq(aw8838->irq_gpio), ret); + goto err_irq; + } + } else { + dev_info(&i2c->dev, "%s skipping IRQ registration\n", __func__); + /* disable feature support if gpio was invalid */ + aw8838->flags |= AW8838_FLAG_SKIP_INTERRUPTS; + } + + /* Register the sysfs files for climax backdoor access */ + ret = device_create_bin_file(&i2c->dev, &dev_attr_rw); + if (ret) + dev_info(&i2c->dev, "%s error creating sysfs files: rw\n", __func__); + ret = device_create_bin_file(&i2c->dev, &dev_attr_regaddr); + if (ret) + dev_info(&i2c->dev, "%s error creating sysfs files: regaddr\n", __func__); + + dev_set_drvdata(&i2c->dev, aw8838); + ret = sysfs_create_group(&i2c->dev.kobj, &aw8838_attribute_group); + if (ret < 0) { + dev_info(&i2c->dev, "%s error creating sysfs attr files\n", __func__); + goto err_sysfs; + } + + aw8838_monitor_init(&aw8838->monitor); + + pr_info("%s probe completed successfully!\n", __func__); + + return 0; + +err_sysfs: + device_remove_bin_file(&i2c->dev, &dev_attr_regaddr); + device_remove_bin_file(&i2c->dev, &dev_attr_rw); + devm_free_irq(&i2c->dev, gpio_to_irq(aw8838->irq_gpio), aw8838); +err_irq: + aw_componet_codec_ops.aw_snd_soc_unregister_codec(&i2c->dev); +err_register_codec: + devm_kfree(&i2c->dev, dai); + dai = NULL; +err_dai_kzalloc: +err_id: + if (gpio_is_valid(aw8838->reset_gpio)) + devm_gpio_free(&i2c->dev, aw8838->reset_gpio); + if (gpio_is_valid(aw8838->irq_gpio)) + devm_gpio_free(&i2c->dev, aw8838->irq_gpio); +err_gpio_request: +err_parse_dt: + devm_kfree(&i2c->dev, aw8838); + aw8838 = NULL; + return ret; +} + +static int aw8838_i2c_remove(struct i2c_client *i2c) +{ + struct aw8838 *aw8838 = i2c_get_clientdata(i2c); + + pr_info("%s enter\n", __func__); + + aw8838_monitor_deinit(&aw8838->monitor); + device_remove_bin_file(&i2c->dev, &dev_attr_regaddr); + device_remove_bin_file(&i2c->dev, &dev_attr_rw); + devm_free_irq(&i2c->dev, gpio_to_irq(aw8838->irq_gpio), aw8838); + + aw_componet_codec_ops.aw_snd_soc_unregister_codec(&i2c->dev); + + if (gpio_is_valid(aw8838->irq_gpio)) + devm_gpio_free(&i2c->dev, aw8838->irq_gpio); + if (gpio_is_valid(aw8838->reset_gpio)) + devm_gpio_free(&i2c->dev, aw8838->reset_gpio); + + return 0; +} + +static void aw8838_i2c_shutdown(struct i2c_client *i2c) +{ + struct aw8838 *aw8838 = i2c_get_clientdata(i2c); + + pr_info("%s enter!\n", __func__); + aw8838_stop(aw8838); +} + +static const struct i2c_device_id aw8838_i2c_id[] = { + { AW8838_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw8838_i2c_id); + +static struct of_device_id aw8838_dt_match[] = { + { .compatible = "awinic,aw8838_smartpa" }, + { }, +}; + +static struct i2c_driver aw8838_i2c_driver = { + .driver = { + .name = AW8838_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw8838_dt_match), + }, + .probe = aw8838_i2c_probe, + .remove = aw8838_i2c_remove, + .shutdown = aw8838_i2c_shutdown, + .id_table = aw8838_i2c_id, +}; + + +static int __init aw8838_i2c_init(void) +{ + int ret = 0; + + pr_info("aw8838 driver version %s\n", AW8838_DRIVER_VERSION); + + ret = i2c_add_driver(&aw8838_i2c_driver); + if(ret){ + pr_err("fail to add aw8838 device into i2c\n"); + return ret; + } + + return 0; +} +module_init(aw8838_i2c_init); + + +static void __exit aw8838_i2c_exit(void) +{ + i2c_del_driver(&aw8838_i2c_driver); +} +module_exit(aw8838_i2c_exit); + + +MODULE_DESCRIPTION("ASoC AW8838 Smart PA Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/techpack/audio/asoc/codecs/aw8838/aw8838.h b/techpack/audio/asoc/codecs/aw8838/aw8838.h new file mode 100644 index 000000000000..b19eee5cdbde --- /dev/null +++ b/techpack/audio/asoc/codecs/aw8838/aw8838.h @@ -0,0 +1,107 @@ +#ifndef _AW8838_H_ +#define _AW8838_H_ + +#include "aw8838_monitor.h" +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 1) +#define AW_KERNEL_VER_OVER_4_19_1 +#endif + +/* + * i2c transaction on Linux limited to 64k + * (See Linux kernel documentation: Documentation/i2c/writing-clients) +*/ +#define MAX_I2C_BUFFER_SIZE 65536 + +#define AW8838_FLAG_START_ON_MUTE (1 << 0) +#define AW8838_FLAG_SKIP_INTERRUPTS (1 << 1) +#define AW8838_FLAG_SAAM_AVAILABLE (1 << 2) +#define AW8838_FLAG_STEREO_DEVICE (1 << 3) +#define AW8838_FLAG_MULTI_MIC_INPUTS (1 << 4) + +#define AW8838_NUM_RATES 9 + +#define AW8838_MAX_REGISTER 0xff + + +enum aw8838_chipid{ + AW8838_ID, +}; + +enum aw8838_mode_spk_rcv{ + AW8838_SPEAKER_MODE = 0, + AW8838_RECEIVER_MODE = 1, +}; + +/******************************************** + Compatible with codec and component + *******************************************/ + +#ifdef AW_KERNEL_VER_OVER_4_19_1 +typedef struct snd_soc_component aw_snd_soc_codec_t; +typedef struct snd_soc_component_driver aw_snd_soc_codec_driver_t; +#else +typedef struct snd_soc_codec aw_snd_soc_codec_t; +typedef struct snd_soc_codec_driver aw_snd_soc_codec_driver_t; +#endif + +struct aw_componet_codec_ops { + aw_snd_soc_codec_t *(*aw_snd_soc_kcontrol_codec)(struct snd_kcontrol *kcontrol); + void *(*aw_snd_soc_codec_get_drvdata)(aw_snd_soc_codec_t *codec); + int (*aw_snd_soc_add_codec_controls)(aw_snd_soc_codec_t *codec, + const struct snd_kcontrol_new *controls, unsigned int num_controls); + void (*aw_snd_soc_unregister_codec)(struct device *dev); + int (*aw_snd_soc_register_codec)(struct device *dev, + const aw_snd_soc_codec_driver_t *codec_drv, + struct snd_soc_dai_driver *dai_drv, + int num_dai); +}; + +struct aw8838 { + struct regmap *regmap; + struct i2c_client *i2c; + aw_snd_soc_codec_t *codec; + struct device *dev; + struct mutex cfg_lock; +#ifdef AW8838_VBAT_MONITOR + struct hrtimer vbat_monitor_timer; + struct work_struct vbat_monitor_work; +#endif + int sysclk; + int rate; + int pstream; + int cstream; + + int reset_gpio; + int irq_gpio; + +#ifdef CONFIG_DEBUG_FS + struct dentry *dbg_dir; +#endif + u8 reg; + + unsigned int flags; + unsigned int chipid; + unsigned int init; + unsigned int spk_rcv_mode; + struct aw8838_monitor monitor; + uint32_t db_offset; + unsigned int cpmd_default; +}; + +struct aw8838_container{ + int len; + unsigned char data[]; +}; + +int aw8838_i2c_read(struct aw8838 *aw8838, + unsigned char reg_addr, unsigned int *reg_data); +int aw8838_i2c_write(struct aw8838 *aw8838, + unsigned char reg_addr, unsigned int reg_data); +int aw8838_get_volume(struct aw8838 *aw8838, uint32_t *value); +int aw8838_set_volume(struct aw8838 *aw8838, uint32_t value); +uint32_t aw8838_reg_val_to_db(uint32_t value); +int aw8838_get_hmute(struct aw8838 *aw8838); + +#endif diff --git a/techpack/audio/asoc/codecs/aw8838/aw8838_monitor.c b/techpack/audio/asoc/codecs/aw8838/aw8838_monitor.c new file mode 100644 index 000000000000..1bf958d3ff37 --- /dev/null +++ b/techpack/audio/asoc/codecs/aw8838/aw8838_monitor.c @@ -0,0 +1,1280 @@ +/* + * awinic_monitor.c monitor_module + * + * + * Copyright (c) 2019 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw8838.h" +#include "aw8838_reg.h" +#include "aw8838_monitor.h" + + +#define AW_MONITOR_FILE "aw8838_monitor.bin" + +/*DSP communication*/ +#ifdef AW_QCOM_PLATFORM +extern int afe_get_topology(int port_id); +extern int aw_send_afe_cal_apr(uint32_t param_id, + void *buf, int cmd_size, bool write); +#else +static int afe_get_topology(int port_id) +{ + return -EPERM; +} +static int aw_send_afe_cal_apr(uint32_t param_id, + void *buf, int cmd_size, bool write) +{ + return 0; +} +#endif + +static int aw_check_dsp_ready(void) +{ + int topo_id; + + topo_id = afe_get_topology(AFE_PORT_ID_AWDSP_RX); + pr_debug("%s: topo_id 0x%x\n", __func__, topo_id); + + if (topo_id == AW_RX_TOPO_ID) + return true; + else + return false; +} + +static int aw_qcom_write_data_to_dsp(uint32_t param_id, void *data, int data_size) +{ + int try = 0; + + while (try < AW_DSP_TRY_TIME) { + if (aw_check_dsp_ready()) { + return aw_send_afe_cal_apr(param_id, data, + data_size, true); + } else { + try++; + msleep(AW_DSP_SLEEP_TIME); + pr_err("%s: afe not ready try again\n", __func__); + } + } + + return -EINVAL; +} + +static int aw_qcom_set_vmax_to_dsp(uint32_t vmax, int channel) +{ + uint32_t param_id; + + if (channel == AW_CHAN_LEFT) + param_id = AFE_PARAM_ID_AWDSP_RX_VMAX_L; + else + param_id = AFE_PARAM_ID_AWDSP_RX_VMAX_R; + + return aw_qcom_write_data_to_dsp(param_id, &vmax, sizeof(vmax)); +} + +static int aw8838_monitor_get_voltage(struct aw8838 *aw8838, + int *vbat) +{ + char name[] = "battery"; + int ret; + union power_supply_propval prop = { 0 }; + struct power_supply *psy = NULL; + + aw_dev_info(aw8838->dev, "%s enter\n", __func__); + + psy = power_supply_get_by_name(name); + if (psy) { + ret = power_supply_get_property(psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + &prop); + if (ret < 0) { + aw_dev_err(aw8838->dev, + "%s: get voltage failed\n", __func__); + return -EINVAL; + } + *vbat = prop.intval / 1000; + aw_dev_info(aw8838->dev, "%s: get vbat is %d\n", + __func__, *vbat); + } else { + aw_dev_err(aw8838->dev, "%s: no struct power supply name : %s\n", + __func__, name); + return -EINVAL; + } + + return 0; +} + +static int aw8838_monitor_get_temperature(struct aw8838 *aw8838, int *temp) +{ +#ifdef AW_TEMP_EN + char name[] = "battery"; + int ret; + union power_supply_propval sys_temp = { 0 }; + struct power_supply *psy = NULL; + + aw_dev_info(aw8838->dev, "%s enter\n", __func__); + + psy = power_supply_get_by_name(name); + if (psy) { + ret = power_supply_get_property(psy, + POWER_SUPPLY_PROP_TEMP, + &sys_temp); + if (ret < 0) { + aw_dev_err(aw8838->dev, + "%s: get temperature failed\n", __func__); + return -EINVAL; + } + *temp = sys_temp.intval / 10; + aw_dev_info(aw8838->dev, "%s: get temperature is %d\n", + __func__, *temp); + } else { + aw_dev_err(aw8838->dev, "%s: no struct power supply name : %s\n", + __func__, name); + return -EINVAL; + } +#else + aw_dev_info(aw8838->dev, "%s colse temp switch\n", __func__); + aw8838->monitor.monitor_cfg.temp_switch = 0; +#endif + return 0; +} + +static int aw8838_monitor_get_temp_and_vol(struct aw8838 *aw8838) +{ + struct aw8838_monitor *monitor = &aw8838->monitor; + unsigned int voltage = 0; + int current_temp = 0; + int ret = -1; + +#ifdef AW_DEBUG + if (monitor->test_vol == 0) { + ret = aw8838_monitor_get_voltage(aw8838, &voltage); + if (ret < 0) + return ret; + } else { + voltage = monitor->test_vol; + } + + if (monitor->test_temp == 0) { + ret = aw8838_monitor_get_temperature(aw8838, ¤t_temp); + if (ret) + return ret; + } else { + current_temp = monitor->test_temp; + } +#else + ret = aw8838_monitor_get_voltage(aw8838, &voltage); + if (ret < 0) + return ret; + + ret = aw8838_monitor_get_temperature(aw8838, ¤t_temp); + if (ret < 0) + return ret; +#endif + + monitor->vol_trace.sum_val += voltage; + monitor->temp_trace.sum_val += current_temp; + monitor->samp_count++; + + return 0; +} + +static int aw8838_monitor_trace_data_from_table(struct aw8838 *aw8838, + struct aw_table_info table_info, + struct aw_monitor_trace *data_trace) +{ + int i; + + if (table_info.aw_table == NULL) { + aw_dev_err(aw8838->dev, "%s: table_info.aw_table is null\n", + __func__); + return -EINVAL; + } + + for (i = 0; i < table_info.table_num; i++) { + if (data_trace->sum_val >= table_info.aw_table[i].min_val && + data_trace->sum_val <= table_info.aw_table[i].max_val) { + memcpy(&data_trace->aw_table, &table_info.aw_table[i], + sizeof(struct aw_table)); + break; + } + } + + data_trace->pre_val = data_trace->sum_val; + data_trace->sum_val = 0; + return 0; +} + +static int aw8838_monitor_first_get_data_form_table(struct aw8838 *aw8838, + struct aw_table_info table_info, + struct aw_monitor_trace *data_trace) +{ + int i; + + if (table_info.aw_table == NULL) { + aw_dev_err(aw8838->dev, "%s: table_info.aw_table is null\n", + __func__); + return -EINVAL; + } + + for (i = 0; i < table_info.table_num; i++) { + if (data_trace->sum_val >= table_info.aw_table[i].min_val) { + memcpy(&data_trace->aw_table, &table_info.aw_table[i], + sizeof(struct aw_table)); + break; + } + } + data_trace->pre_val = data_trace->sum_val; + data_trace->sum_val = 0; + return 0; +} + +static int aw8838_monitor_get_data_from_table(struct aw8838 *aw8838, + struct aw_table_info table_info, + struct aw_monitor_trace *data_trace, + uint32_t aplha) +{ + struct aw8838_monitor *monitor = &aw8838->monitor; + + if (monitor->first_entry == AW_FIRST_ENTRY) { + return aw8838_monitor_first_get_data_form_table(aw8838, + table_info, data_trace); + } else { + data_trace->sum_val = data_trace->sum_val / monitor->samp_count; + data_trace->sum_val = ((int32_t)aplha * data_trace->sum_val + + (1000 - (int32_t)aplha) * data_trace->pre_val) / 1000; + return aw8838_monitor_trace_data_from_table(aw8838, + table_info, data_trace); + } + + return 0; +} + +static int aw8838_monitor_get_data(struct aw8838 *aw8838) +{ + struct aw8838_monitor *monitor = &aw8838->monitor; + struct aw_monitor_cfg *monitor_cfg = &monitor->monitor_cfg; + struct aw_monitor_trace *vol_trace = &monitor->vol_trace; + struct aw_monitor_trace *temp_trace = &monitor->temp_trace; + int ret; + + if (monitor_cfg->vol_switch) { + ret = aw8838_monitor_get_data_from_table(aw8838, + monitor_cfg->vol_info, vol_trace, + monitor_cfg->vol_aplha); + if (ret < 0) + return ret; + } else { + vol_trace->aw_table.ipeak = IPEAK_NONE; + vol_trace->aw_table.gain = GAIN_NONE; + vol_trace->aw_table.vmax = VMAX_NONE; + } + + if (monitor_cfg->temp_switch) { + ret = aw8838_monitor_get_data_from_table(aw8838, + monitor_cfg->temp_info, temp_trace, + monitor_cfg->temp_aplha); + if (ret < 0) + return ret; + } else { + temp_trace->aw_table.ipeak = IPEAK_NONE; + temp_trace->aw_table.gain = GAIN_NONE; + temp_trace->aw_table.vmax = VMAX_NONE; + } + + aw_dev_info(aw8838->dev, "%s: vol: ipeak = 0x%x, gain = 0x%x, vmax = 0x%x\n", + __func__, vol_trace->aw_table.ipeak, + vol_trace->aw_table.gain, + vol_trace->aw_table.vmax); + + aw_dev_info(aw8838->dev, "%s: temp: ipeak = 0x%x, gain = 0x%x, vmax = 0x%x\n", + __func__, temp_trace->aw_table.ipeak, + temp_trace->aw_table.gain, + temp_trace->aw_table.vmax); + + return 0; +} + +static void aw8838_monitor_get_cfg(struct aw8838 *aw8838, + struct aw_table *set_table) +{ + struct aw8838_monitor *monitor = &aw8838->monitor; + struct aw_table *temp_data = &monitor->temp_trace.aw_table; + struct aw_table *vol_data = &monitor->vol_trace.aw_table; + + if (temp_data->ipeak == IPEAK_NONE && vol_data->ipeak == IPEAK_NONE) { + memcpy(set_table, temp_data, sizeof(struct aw_table)); + } else if (temp_data->ipeak == IPEAK_NONE) { + memcpy(set_table, vol_data, sizeof(struct aw_table)); + } else if (vol_data->ipeak == IPEAK_NONE) { + memcpy(set_table, temp_data, sizeof(struct aw_table)); + } else { + if (monitor->monitor_cfg.logic_switch == AW_MON_LOGIC_OR) { + set_table->ipeak = (temp_data->ipeak < vol_data->ipeak ? + temp_data->ipeak : vol_data->ipeak); + set_table->gain = (temp_data->gain < vol_data->gain ? + vol_data->gain : temp_data->gain); + set_table->vmax = (temp_data->vmax < vol_data->vmax ? + vol_data->vmax : temp_data->vmax); + } else { + set_table->ipeak = (temp_data->ipeak < vol_data->ipeak ? + vol_data->ipeak : temp_data->ipeak); + set_table->gain = (temp_data->gain < vol_data->gain ? + temp_data->gain : vol_data->gain); + set_table->vmax = (temp_data->vmax < vol_data->vmax ? + temp_data->vmax : vol_data->vmax); + } + } +} + +static void aw8838_monitor_set_ipeak(struct aw8838 *aw8838, + uint16_t ipeak) +{ + struct aw_monitor_cfg *monitor_cfg = &aw8838->monitor.monitor_cfg; + unsigned int reg_val = 0; + unsigned int read_reg_val; + int ret; + + if (ipeak == IPEAK_NONE || (!monitor_cfg->ipeak_switch)) + return; + + ret = aw8838_i2c_read(aw8838, AW8838_REG_CPCTRL, ®_val); + if (ret < 0) { + aw_dev_err(aw8838->dev, "%s: read ipeak failed\n", __func__); + return; + } + + read_reg_val = reg_val; + read_reg_val &= (~AW8838_BIT_CPCTRL_CP_IPEAK_MASK); + + if (read_reg_val == ipeak) { + aw_dev_info(aw8838->dev, "%s: ipeak = 0x%x, no change\n", + __func__, read_reg_val); + return; + } + + reg_val &= AW8838_BIT_CPCTRL_CP_IPEAK_MASK; + read_reg_val = ipeak; + reg_val |= read_reg_val; + + ret = aw8838_i2c_write(aw8838, AW8838_REG_CPCTRL, reg_val); + if (ret < 0) { + aw_dev_err(aw8838->dev, "%s: write ipeak failed\n", __func__); + return; + } + + aw_dev_info(aw8838->dev, "%s: set reg val = 0x%x, ipeak = 0x%x\n", + __func__, reg_val, ipeak); +} + +static void aw8838_monitor_set_gain(struct aw8838 *aw8838, + uint16_t gain) +{ + struct aw_monitor_cfg *monitor_cfg = &aw8838->monitor.monitor_cfg; + uint32_t read_volume; + uint32_t set_volume; + uint32_t gain_db; + int ret; + + if (gain == GAIN_NONE || (!monitor_cfg->gain_switch)) + return; + + ret = aw8838_get_volume(aw8838, &read_volume); + if (ret < 0) { + aw_dev_err(aw8838->dev, "%s: read volume failed\n", __func__); + return; + } + + gain_db = aw8838_reg_val_to_db(gain); + + /*add offset*/ + set_volume = gain_db + aw8838->db_offset; + + if (read_volume == set_volume) { + aw_dev_info(aw8838->dev, "%s: set db = %d.%d dB, no change\n", + __func__, GET_DB_INT(read_volume), + GET_DB_DECIMAL(read_volume)); + return; + } + + ret = aw8838_set_volume(aw8838, set_volume); + if (ret < 0) { + aw_dev_err(aw8838->dev, "%s: set volume failed\n", __func__); + return; + } + + aw_dev_info(aw8838->dev, "%s: set_volume = %d.%d dB\n", + __func__, GET_DB_INT(set_volume), + GET_DB_DECIMAL(set_volume)); + +} + +static void aw8838_monitor_set_vmax(struct aw8838 *aw8838, + uint32_t vmax) +{ + struct aw_monitor_cfg *monitor_cfg = &aw8838->monitor.monitor_cfg; + int ret; + + if (vmax == VMAX_NONE || (!monitor_cfg->vmax_switch)) + return; + + if ((aw8838->monitor.pre_vmax == vmax) && + (aw8838->monitor.first_entry != AW_FIRST_ENTRY)) { + aw_dev_info(aw8838->dev, "%s: vmax no change\n", + __func__); + return; + } + + /*left sound channel*/ + ret = aw_qcom_set_vmax_to_dsp(vmax, AW_CHAN_LEFT); + if (ret) { + aw_dev_err(aw8838->dev, "%s: dsp_msg_write error\n", + __func__); + return; + } + aw8838->monitor.pre_vmax = vmax; + aw_dev_info(aw8838->dev, "%s: set vmax = 0x%x\n", __func__, vmax); +} + +static int aw8838_monitor_work(struct aw8838 *aw8838) +{ + struct aw8838_monitor *monitor = &aw8838->monitor; + struct aw_monitor_cfg *monitor_cfg = &monitor->monitor_cfg; + struct aw_table set_table; + int ret = -1; + + ret = aw8838_monitor_get_temp_and_vol(aw8838); + if (ret < 0) + return ret; + + if (monitor->samp_count < monitor_cfg->monitor_count && + (monitor->first_entry == AW_NOT_FIRST_ENTRY)) + return 0; + + ret = aw8838_monitor_get_data(aw8838); + if (ret < 0) + return ret; + + aw8838_monitor_get_cfg(aw8838, &set_table); + + aw_dev_dbg(aw8838->dev, "%s: set_ipeak = 0x%x, set_gain = 0x%x, set_vmax = 0x%x\n", + __func__, set_table.ipeak, set_table.gain, set_table.vmax); + + aw8838_monitor_set_ipeak(aw8838, set_table.ipeak); + + aw8838_monitor_set_gain(aw8838, set_table.gain); + + aw8838_monitor_set_vmax(aw8838, set_table.vmax); + + monitor->samp_count = 0; + + if (monitor->first_entry == AW_FIRST_ENTRY) + monitor->first_entry = AW_NOT_FIRST_ENTRY; + + return 0; +} + +static void aw8838_monitor_work_func(struct work_struct *work) +{ + struct aw8838 *aw8838 = container_of(work, + struct aw8838, monitor.delay_work.work); + struct aw_monitor_cfg *monitor_cfg = &aw8838->monitor.monitor_cfg; + + aw_dev_dbg(aw8838->dev, "%s: monitor is_enable %d,scene_mode %d,monitor_status:%d, monitor_switch:%d\n", + __func__, aw8838->monitor.is_enable, aw8838->spk_rcv_mode, + monitor_cfg->monitor_status, monitor_cfg->monitor_switch); + + if (aw8838->monitor.is_enable && + (aw8838->spk_rcv_mode == AW8838_SPEAKER_MODE) && + (monitor_cfg->monitor_status == AW_MON_CFG_OK) && + monitor_cfg->monitor_switch) { + if (!aw8838_get_hmute(aw8838)) { + aw8838_monitor_work(aw8838); + schedule_delayed_work(&aw8838->monitor.delay_work, + msecs_to_jiffies(monitor_cfg->monitor_time)); + } + } +} + +void aw8838_monitor_start(struct aw8838_monitor *monitor) +{ + struct aw8838 *aw8838 = container_of(monitor, + struct aw8838, monitor); + + aw_dev_info(aw8838->dev, "%s: enter\n", __func__); + monitor->first_entry = AW_FIRST_ENTRY; + monitor->samp_count = 0; + monitor->vol_trace.sum_val = 0; + monitor->temp_trace.sum_val = 0; + aw8838->monitor.pre_vmax = 0; + + schedule_delayed_work(&aw8838->monitor.delay_work, + msecs_to_jiffies(0)); +} + +void aw8838_monitor_stop(struct aw8838_monitor *monitor) +{ + struct aw8838 *aw8838 = container_of(monitor, + struct aw8838, monitor); + + aw_dev_info(aw8838->dev, "%s: enter\n", __func__); + cancel_delayed_work_sync(&aw8838->monitor.delay_work); +} + + +/***************************************************** + * load monitor config + *****************************************************/ +static int aw_check_monitor_profile(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)cont->data; + int temp_size, vol_size; + + if (cont->len < sizeof(struct aw_monitor_hdr)) { + aw_dev_err(aw8838->dev, + "%s:params size[%d] < struct aw_monitor_hdr size[%d]!\n", + __func__, cont->len, + (int)sizeof(struct aw_monitor_hdr)); + return -ENOMEM; + } + + if (monitor_hdr->temp_offset > cont->len) { + aw_dev_err(aw8838->dev, + "%s:temp_offset[%d] overflow file size[%d]!\n", + __func__, monitor_hdr->temp_offset, cont->len); + return -ENOMEM; + } + + if (monitor_hdr->vol_offset > cont->len) { + aw_dev_err(aw8838->dev, + "%s:vol_offset[%d] overflow file size[%d]!\n", + __func__, monitor_hdr->vol_offset, cont->len); + return -ENOMEM; + } + + temp_size = monitor_hdr->temp_num * monitor_hdr->single_temp_size; + if (temp_size > cont->len) { + aw_dev_err(aw8838->dev, + "%s:temp_size:[%d] overflow file size[%d]!!\n", + __func__, temp_size, cont->len); + return -ENOMEM; + } + + vol_size = monitor_hdr->vol_num * monitor_hdr->single_vol_size; + if (vol_size > cont->len) { + aw_dev_err(aw8838->dev, + "%s:vol_size:[%d] overflow file size[%d]!\n", + __func__, vol_size, cont->len); + return -ENOMEM; + } + + return 0; +} + +static int aw_check_monitor_profile_v_0_1_1(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)cont->data; + int temp_size, vol_size; + + if (cont->len < sizeof(struct aw_monitor_hdr_v_0_1_1)) { + aw_dev_err(aw8838->dev, + "%s:params size[%d] < struct aw_monitor_hdr_v_0_1_1 size[%d]!\n", + __func__, cont->len, + (int)sizeof(struct aw_monitor_hdr_v_0_1_1)); + return -ENOMEM; + } + + if (monitor_hdr->temp_offset > cont->len) { + aw_dev_err(aw8838->dev, + "%s:temp_offset[%d] overflow file size[%d]!\n", + __func__, monitor_hdr->temp_offset, cont->len); + return -ENOMEM; + } + + if (monitor_hdr->vol_offset > cont->len) { + aw_dev_err(aw8838->dev, + "%s:vol_offset[%d] overflow file size[%d]!\n", + __func__, monitor_hdr->vol_offset, cont->len); + return -ENOMEM; + } + + temp_size = monitor_hdr->temp_num * monitor_hdr->single_temp_size; + if (temp_size > cont->len) { + aw_dev_err(aw8838->dev, + "%s:temp_size:[%d] overflow file size[%d]!!\n", + __func__, temp_size, cont->len); + return -ENOMEM; + } + + vol_size = monitor_hdr->vol_num * monitor_hdr->single_vol_size; + if (vol_size > cont->len) { + aw_dev_err(aw8838->dev, + "%s:vol_size:[%d] overflow file size[%d]!\n", + __func__, vol_size, cont->len); + return -ENOMEM; + } + + return 0; +} + + +static void aw_parse_monitor_hdr(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)cont->data; + struct aw_monitor_cfg *monitor_cfg = &aw8838->monitor.monitor_cfg; + + monitor_cfg->monitor_switch = monitor_hdr->monitor_switch; + monitor_cfg->monitor_time = monitor_hdr->monitor_time; + monitor_cfg->monitor_count = monitor_hdr->monitor_count; + monitor_cfg->ipeak_switch = monitor_hdr->ipeak_switch; + monitor_cfg->gain_switch = monitor_hdr->gain_switch; + monitor_cfg->vmax_switch = monitor_hdr->vmax_switch; + monitor_cfg->temp_switch = monitor_hdr->temp_switch; + monitor_cfg->temp_aplha = monitor_hdr->temp_aplha; + monitor_cfg->vol_switch = monitor_hdr->vol_switch; + monitor_cfg->vol_aplha = monitor_hdr->vol_aplha; + + aw_dev_info(aw8838->dev, "%s: chip name:%s\n", + __func__, monitor_hdr->chip_type); + aw_dev_info(aw8838->dev, "%s: ui ver:0x%x\n", + __func__, monitor_hdr->ui_ver); + + aw_dev_info(aw8838->dev, + "%s:monitor_switch:%d, monitor_time:%d (ms), monitor_count:%d\n", + __func__, monitor_cfg->monitor_switch, + monitor_cfg->monitor_time, monitor_cfg->monitor_count); + + aw_dev_info(aw8838->dev, + "%s:ipeak_switch:%d, gain_switch:%d, vmax_switch:%d\n", + __func__, monitor_cfg->ipeak_switch, + monitor_cfg->gain_switch, monitor_cfg->vmax_switch); + + aw_dev_info(aw8838->dev, + "%s:temp_switch:%d, temp_aplha:%d, vol_switch:%d, vol_aplha:%d\n", + __func__, monitor_cfg->temp_switch, + monitor_cfg->temp_aplha, monitor_cfg->vol_switch, + monitor_cfg->vol_aplha); +} + +static void aw_parse_monitor_hdr_v_0_1_1(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)cont->data; + struct aw_monitor_cfg *monitor_cfg = &aw8838->monitor.monitor_cfg; + + monitor_cfg->monitor_switch = (monitor_hdr->enable_flag >> MONITOR_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->monitor_time = monitor_hdr->monitor_time; + monitor_cfg->monitor_count = monitor_hdr->monitor_count; + monitor_cfg->ipeak_switch = (monitor_hdr->enable_flag >> MONITOR_IPEAK_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->logic_switch = (monitor_hdr->enable_flag >> MONITOR_LOGIC_BIT) & MONITOR_EN_MASK; + monitor_cfg->gain_switch = (monitor_hdr->enable_flag >> MONITOR_GAIN_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->vmax_switch = (monitor_hdr->enable_flag >> MONITOR_VMAX_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->temp_switch = (monitor_hdr->enable_flag >> MONITOR_TEMP_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->temp_aplha = monitor_hdr->temp_aplha; + monitor_cfg->vol_switch = (monitor_hdr->enable_flag >> MONITOR_VOL_EN_BIT) & MONITOR_EN_MASK; + monitor_cfg->vol_aplha = monitor_hdr->vol_aplha; + + aw_dev_info(aw8838->dev, "%s:chip name:%s\n", + __func__, monitor_hdr->chip_type); + aw_dev_info(aw8838->dev, "%s:ui ver:0x%x\n", + __func__, monitor_hdr->ui_ver); + + aw_dev_info(aw8838->dev, "%s:monitor_switch:%d, monitor_time:%d (ms), monitor_count:%d\n", + __func__, monitor_cfg->monitor_switch, monitor_cfg->monitor_time, + monitor_cfg->monitor_count); + + aw_dev_info(aw8838->dev, "%s: logic_switch:%d, ipeak_switch:%d, gain_switch:%d, vmax_switch:%d\n", + __func__, monitor_cfg->logic_switch, monitor_cfg->ipeak_switch, monitor_cfg->gain_switch, + monitor_cfg->vmax_switch); + + aw_dev_info(aw8838->dev, "%s:temp_switch:%d, temp_aplha:%d, vol_switch:%d, vol_aplha:%d\n", + __func__, monitor_cfg->temp_switch, monitor_cfg->temp_aplha, + monitor_cfg->vol_switch, monitor_cfg->vol_aplha); + +} + +static void aw_populate_data_to_table(struct aw8838 *aw8838, + struct aw_table_info *table_info, const char *offset_ptr) +{ + int i; + + for (i = 0; i < table_info->table_num * AW_TABLE_SIZE; i += AW_TABLE_SIZE) { + table_info->aw_table[i / AW_TABLE_SIZE].min_val = + GET_16_DATA(offset_ptr[1 + i], offset_ptr[i]); + table_info->aw_table[i / AW_TABLE_SIZE].max_val = + GET_16_DATA(offset_ptr[3 + i], offset_ptr[2 + i]); + table_info->aw_table[i / AW_TABLE_SIZE].ipeak = + GET_16_DATA(offset_ptr[5 + i], offset_ptr[4 + i]); + table_info->aw_table[i / AW_TABLE_SIZE].gain = + GET_16_DATA(offset_ptr[7 + i], offset_ptr[6 + i]); + table_info->aw_table[i / AW_TABLE_SIZE].vmax = + GET_32_DATA(offset_ptr[11 + i], offset_ptr[10 + i], + offset_ptr[9 + i], offset_ptr[8 + i]); + } + + for (i = 0; i < table_info->table_num; i++) + aw_dev_info(aw8838->dev, + "min_val:%d, max_val:%d, ipeak:0x%x, gain:0x%x, vmax:0x%x\n", + table_info->aw_table[i].min_val, + table_info->aw_table[i].max_val, + table_info->aw_table[i].ipeak, + table_info->aw_table[i].gain, + table_info->aw_table[i].vmax); + +} + +static int aw_parse_temp_data(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)cont->data; + struct aw_table_info *temp_info = + &aw8838->monitor.monitor_cfg.temp_info; + + aw_dev_info(aw8838->dev, "%s: ===parse temp start ===\n", + __func__); + + if (temp_info->aw_table != NULL) { + kfree(temp_info->aw_table); + temp_info->aw_table = NULL; + } + + temp_info->aw_table = kzalloc((monitor_hdr->temp_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (!temp_info->aw_table) + return -ENOMEM; + + temp_info->table_num = monitor_hdr->temp_num; + aw_populate_data_to_table(aw8838, temp_info, + &cont->data[monitor_hdr->temp_offset]); + aw_dev_info(aw8838->dev, "%s: ===parse temp end ===\n", + __func__); + return 0; +} + +static int aw_parse_temp_data_v_0_1_1(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)cont->data; + struct aw_table_info *temp_info = + &aw8838->monitor.monitor_cfg.temp_info; + + aw_dev_info(aw8838->dev, "%s: ===parse temp start ===\n", + __func__); + + if (temp_info->aw_table != NULL) { + kfree(temp_info->aw_table); + temp_info->aw_table = NULL; + } + + temp_info->aw_table = kzalloc((monitor_hdr->temp_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (!temp_info->aw_table) + return -ENOMEM; + + temp_info->table_num = monitor_hdr->temp_num; + aw_populate_data_to_table(aw8838, temp_info, + &cont->data[monitor_hdr->temp_offset]); + aw_dev_info(aw8838->dev, "%s: ===parse temp end ===\n", + __func__); + return 0; +} + +static int aw_parse_vol_data(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)cont->data; + struct aw_table_info *vol_info = + &aw8838->monitor.monitor_cfg.vol_info; + + aw_dev_info(aw8838->dev, "%s: ===parse vol start ===\n", + __func__); + + if (vol_info->aw_table != NULL) { + kfree(vol_info->aw_table); + vol_info->aw_table = NULL; + } + + vol_info->aw_table = kzalloc((monitor_hdr->vol_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (!vol_info->aw_table) + return -ENOMEM; + + vol_info->table_num = monitor_hdr->vol_num; + aw_populate_data_to_table(aw8838, vol_info, + &cont->data[monitor_hdr->vol_offset]); + aw_dev_info(aw8838->dev, "%s: ===parse vol end ===\n", + __func__); + return 0; +} + +static int aw_parse_vol_data_v_0_1_1(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + struct aw_monitor_hdr_v_0_1_1 *monitor_hdr = + (struct aw_monitor_hdr_v_0_1_1 *)cont->data; + struct aw_table_info *vol_info = + &aw8838->monitor.monitor_cfg.vol_info; + + aw_dev_info(aw8838->dev, "%s: ===parse vol start ===\n", + __func__); + + if (vol_info->aw_table != NULL) { + kfree(vol_info->aw_table); + vol_info->aw_table = NULL; + } + + vol_info->aw_table = kzalloc((monitor_hdr->vol_num * AW_TABLE_SIZE), + GFP_KERNEL); + if (!vol_info->aw_table) + return -ENOMEM; + + vol_info->table_num = monitor_hdr->vol_num; + aw_populate_data_to_table(aw8838, vol_info, + &cont->data[monitor_hdr->vol_offset]); + aw_dev_info(aw8838->dev, "%s: ===parse vol end ===\n", + __func__); + return 0; +} + + +static int aw_parse_monitor_data(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + int ret; + struct aw_monitor_cfg *monitor_cfg = &aw8838->monitor.monitor_cfg; + + ret = aw_check_monitor_profile(aw8838, cont); + if (ret < 0) { + aw_dev_err(aw8838->dev, "%s:check %s failed\n", + __func__, AW_MONITOR_FILE); + return ret; + } + + aw_parse_monitor_hdr(aw8838, cont); + + ret = aw_parse_temp_data(aw8838, cont); + if (ret < 0) + return ret; + + ret = aw_parse_vol_data(aw8838, cont); + if (ret < 0) { + if (monitor_cfg->temp_info.aw_table != NULL) { + kfree(monitor_cfg->temp_info.aw_table); + monitor_cfg->temp_info.aw_table = NULL; + monitor_cfg->temp_info.table_num = 0; + } + return ret; + } + + monitor_cfg->monitor_status = AW_MON_CFG_OK; + return 0; +} + +static int aw_parse_monitor_data_v_0_1_1(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + int ret; + struct aw_monitor_cfg *monitor_cfg = &aw8838->monitor.monitor_cfg; + + ret = aw_check_monitor_profile_v_0_1_1(aw8838, cont); + if (ret < 0) { + aw_dev_err(aw8838->dev, "%s:check %s failed\n", + __func__, AW_MONITOR_FILE); + return ret; + } + + aw_parse_monitor_hdr_v_0_1_1(aw8838, cont); + + ret = aw_parse_temp_data_v_0_1_1(aw8838, cont); + if (ret < 0) + return ret; + + ret = aw_parse_vol_data_v_0_1_1(aw8838, cont); + if (ret < 0) { + if (monitor_cfg->temp_info.aw_table != NULL) { + kfree(monitor_cfg->temp_info.aw_table); + monitor_cfg->temp_info.aw_table = NULL; + monitor_cfg->temp_info.table_num = 0; + } + return ret; + } + + monitor_cfg->monitor_status = AW_MON_CFG_OK; + return 0; +} + + +static int aw_monitor_param_check_sum(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + int i, check_sum = 0; + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)cont->data; + + if (cont->len < sizeof(struct aw_monitor_hdr)) { + aw_dev_err(aw8838->dev, "%s:data size smaller than hdr , please check monitor bin\n", + __func__); + return -ENOMEM; + } + + for (i = 4; i < cont->len; i++) + check_sum += (uint8_t)cont->data[i]; + + if (monitor_hdr->check_sum != check_sum) { + aw_dev_err(aw8838->dev, "%s:check_sum[%d] is not equal to actual check_sum[%d]\n", + __func__, monitor_hdr->check_sum, check_sum); + return -ENOMEM; + } + + return 0; +} + +static int aw_parse_monitor_profile(struct aw8838 *aw8838, + struct aw8838_container *cont) +{ + struct aw_monitor_hdr *monitor_hdr = + (struct aw_monitor_hdr *)cont->data; + int ret; + + ret = aw_monitor_param_check_sum(aw8838, cont); + if (ret < 0) + return ret; + + switch (monitor_hdr->monitor_ver) { + case AW_MONITOR_HDR_VER_0_1_0: + return aw_parse_monitor_data(aw8838, cont); + case AW_MONITOR_HDR_VER_0_1_1: + return aw_parse_monitor_data_v_0_1_1(aw8838, cont); + default: + aw_dev_err(aw8838->dev, "%s:cfg version:0x%x unsupported\n", + __func__, monitor_hdr->monitor_ver); + return -EINVAL; + } +} + +static void aw_monitor_profile_loaded(const struct firmware *cont, + void *context) +{ + struct aw8838 *aw8838 = context; + struct aw8838_container *monitor_cfg = NULL; + int ret; + + if (aw8838->monitor.monitor_cfg.monitor_status == AW_MON_CFG_ST) { + if (!cont) { + aw_dev_err(aw8838->dev, "%s:failed to read %s\n", + __func__, AW_MONITOR_FILE); + goto exit; + } + + aw_dev_info(aw8838->dev, "%s: loaded %s - size: %zu\n", + __func__, AW_MONITOR_FILE, cont ? cont->size : 0); + + monitor_cfg = kzalloc(cont->size + sizeof(int), GFP_KERNEL); + if (!monitor_cfg) { + aw_dev_err(aw8838->dev, "%s: error allocating memory\n", __func__); + goto exit; + } + monitor_cfg->len = cont->size; + memcpy(monitor_cfg->data, cont->data, cont->size); + ret = aw_parse_monitor_profile(aw8838, monitor_cfg); + if (ret < 0) + aw_dev_err(aw8838->dev, "%s:parse monitor cfg failed\n", + __func__); + kfree(monitor_cfg); + monitor_cfg = NULL; + } + +exit: + release_firmware(cont); +} + +int aw8838_load_monitor_profile(struct aw8838_monitor *monitor) +{ + int ret; + struct aw8838 *aw8838 = container_of(monitor, + struct aw8838, monitor); + + if (!monitor->is_enable) { + aw_dev_info(aw8838->dev, "%s: monitor flag:%d, monitor bin noload\n", + __func__, monitor->is_enable); + ret = 0; + } else { + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + AW_MONITOR_FILE, + aw8838->dev, GFP_KERNEL, aw8838, + aw_monitor_profile_loaded); + } + + return ret; +} + +static void aw8838_monitor_load_fw_work_func(struct work_struct *work) +{ + struct aw8838_monitor *monitor = container_of(work, + struct aw8838_monitor, load_fw_work.work); + + aw8838_load_monitor_profile(monitor); +} + +void aw8838_deinit_monitor_profile(struct aw8838_monitor *monitor) +{ + struct aw_monitor_cfg *monitor_cfg = &monitor->monitor_cfg; + + monitor_cfg->monitor_status = AW_MON_CFG_ST; + + if (monitor_cfg->temp_info.aw_table != NULL) { + kfree(monitor_cfg->temp_info.aw_table); + monitor_cfg->temp_info.aw_table = NULL; + } + + if (monitor_cfg->vol_info.aw_table != NULL) { + kfree(monitor_cfg->vol_info.aw_table); + monitor_cfg->vol_info.aw_table = NULL; + } + memset(monitor_cfg, 0, sizeof(struct aw_monitor_cfg)); +} + +#ifdef AW_DEBUG +static ssize_t aw8838_vol_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + uint32_t vol = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &vol); + if (ret < 0) + return ret; + + aw_dev_info(aw8838->dev, "%s: vol set =%d\n", __func__, vol); + aw8838->monitor.test_vol = vol; + + return count; +} + +static ssize_t aw8838_vol_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + ssize_t len = 0; + uint32_t local_vol = aw8838->monitor.test_vol; + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw8838 vol: %d\n", local_vol); + return len; +} + +static ssize_t aw8838_temp_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + int32_t temp = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtoint(buf, 0, &temp); + if (ret < 0) + return ret; + + aw_dev_info(aw8838->dev, "%s: temp set =%d\n", __func__, temp); + aw8838->monitor.test_temp = temp; + + return count; +} + +static ssize_t aw8838_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + ssize_t len = 0; + int32_t local_temp = aw8838->monitor.test_temp; + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw8838 temp: %d\n", local_temp); + return len; +} + +static DEVICE_ATTR(vol, S_IWUSR | S_IRUGO, + aw8838_vol_show, aw8838_vol_store); +static DEVICE_ATTR(temp, S_IWUSR | S_IRUGO, + aw8838_temp_show, aw8838_temp_store); +#endif + +static ssize_t aw8838_monitor_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + uint32_t enable = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &enable); + if (ret < 0) + return ret; + + aw_dev_info(aw8838->dev, "%s:monitor enable set =%d\n", + __func__, enable); + aw8838->monitor.is_enable = enable; + if (enable) + schedule_delayed_work(&aw8838->monitor.delay_work, + msecs_to_jiffies(0)); + + return count; +} + +static ssize_t aw8838_monitor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + ssize_t len = 0; + uint32_t local_enable; + + local_enable = aw8838->monitor.is_enable; + len += snprintf(buf+len, PAGE_SIZE-len, + "aw8838 monitor enable: %d\n", local_enable); + return len; +} + +static ssize_t aw_monitor_update_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw8838 *aw8838 = dev_get_drvdata(dev); + uint32_t update = 0; + int ret; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &update); + if (ret < 0) + return ret; + + aw_dev_info(aw8838->dev, "%s:monitor update = %d\n", + __func__, update); + + aw8838_monitor_stop(&aw8838->monitor); + + aw8838_deinit_monitor_profile(&aw8838->monitor); + aw8838_load_monitor_profile(&aw8838->monitor); + msleep(100); + aw8838_monitor_start(&aw8838->monitor); + + return count; +} + +static DEVICE_ATTR(monitor, S_IWUSR | S_IRUGO, + aw8838_monitor_show, aw8838_monitor_store); +static DEVICE_ATTR(monitor_update, S_IWUSR, + NULL, aw_monitor_update_store); + + +static struct attribute *aw8838_monitor_attr[] = { + &dev_attr_monitor.attr, + &dev_attr_monitor_update.attr, +#ifdef AW_DEBUG + &dev_attr_vol.attr, + &dev_attr_temp.attr, +#endif + NULL +}; + +static struct attribute_group aw8838_monitor_attr_group = { + .attrs = aw8838_monitor_attr, +}; + +void aw8838_monitor_init(struct aw8838_monitor *monitor) +{ + int ret; + struct aw8838 *aw8838 = container_of(monitor, + struct aw8838, monitor); + + aw_dev_info(aw8838->dev, "%s: enter\n", __func__); + +#ifdef AW_DEBUG + monitor->test_vol = 0; + monitor->test_temp = 0; +#endif + + INIT_DELAYED_WORK(&monitor->delay_work, aw8838_monitor_work_func); + INIT_DELAYED_WORK(&monitor->load_fw_work, aw8838_monitor_load_fw_work_func); + + ret = sysfs_create_group(&aw8838->dev->kobj, + &aw8838_monitor_attr_group); + if (ret < 0) + aw_dev_err(aw8838->dev, "%s error creating sysfs attr files\n", + __func__); +} + +void aw8838_monitor_deinit(struct aw8838_monitor *monitor) +{ + struct aw8838 *aw8838 = container_of(monitor, + struct aw8838, monitor); + + aw8838_monitor_stop(monitor); + aw8838_deinit_monitor_profile(monitor); + + sysfs_remove_group(&aw8838->dev->kobj, &aw8838_monitor_attr_group); +} + +/***************************************************** + * device tree parse monitor param + *****************************************************/ +void aw8838_parse_monitor_dt(struct aw8838_monitor *monitor) +{ + int ret; + struct aw8838 *aw8838 = container_of(monitor, + struct aw8838, monitor); + struct device_node *np = aw8838->dev->of_node; + + ret = of_property_read_u32(np, "monitor-flag", &monitor->is_enable); + if (ret) { + monitor->is_enable = AW8838_MONITOR_DEFAULT_FLAG; + aw_dev_info(aw8838->dev, + "%s: monitor-flag get failed ,user default value!\n", + __func__); + } else { + aw_dev_info(aw8838->dev, "%s: monitor-flag = %d\n", + __func__, monitor->is_enable); + } +} + diff --git a/techpack/audio/asoc/codecs/aw8838/aw8838_monitor.h b/techpack/audio/asoc/codecs/aw8838/aw8838_monitor.h new file mode 100644 index 000000000000..ea94796f3d91 --- /dev/null +++ b/techpack/audio/asoc/codecs/aw8838/aw8838_monitor.h @@ -0,0 +1,201 @@ +#ifndef __AWINIC_MONITOR_H__ +#define __AWINIC_MONITOR_H__ + +/*#define AW_DEBUG*/ +/*#define AW_TEMP_EN*/ +/*#define AW_QCOM_PLATFORM*/ + +#define AFE_PORT_ID_AWDSP_RX (0x1006) /*AFE_PORT_ID_QUATERNARY_MI2S_RX*/ +#define AW_RX_TOPO_ID (0x1000FF01) /*RX topology id*/ +#define AW_DSP_SLEEP_TIME (10) +#define AW_DSP_TRY_TIME (3) + + +struct aw_table; + +#define AW_TABLE_SIZE sizeof(struct aw_table) +#define AFE_PARAM_ID_AWDSP_RX_VMAX_L (0X10013D17) +#define AFE_PARAM_ID_AWDSP_RX_VMAX_R (0X10013D18) + + +#define IPEAK_NONE (0xFF) +#define GAIN_NONE (0xFF) +#define VMAX_NONE (0xFFFFFFFF) +#define GET_DB_INT(x) (x / 2) +#define GET_DB_DECIMAL(x) ((x % 2) * 5) +#define AW_LOAD_MON_FW_DELAY_TIME (3000) +#define AW8838_MONITOR_DEFAULT_FLAG (0) + +#define GET_32_DATA(w, x, y, z) \ + ((uint32_t)((((uint8_t)w) << 24) | (((uint8_t)x) << 16) | (((uint8_t)y) << 8) | ((uint8_t)z))) +#define GET_16_DATA(x, y) \ + ((uint16_t)((((uint8_t)x) << 8) | (uint8_t)y)) + +#define MONITOR_EN_MASK 0x01 + +enum { + AW_CHAN_LEFT = 0, + AW_CHAN_RIGHT = 1, +}; + +enum { + AW_MON_LOGIC_OR = 0, + AW_MON_LOGIC_AND = 1, +}; + +enum { + MONITOR_EN_BIT = 0, + MONITOR_LOGIC_BIT = 1, + MONITOR_IPEAK_EN_BIT =2, + MONITOR_GAIN_EN_BIT = 3, + MONITOR_VMAX_EN_BIT = 4, + MONITOR_TEMP_EN_BIT = 5, + MONITOR_VOL_EN_BIT = 6, +}; + +enum aw_monitor_hdr_ver { + AW_MONITOR_HDR_VER_0_1_0 = 0x00010000, + AW_MONITOR_HDR_VER_0_1_1 = 0x00010100, +}; + +enum { + AW_FIRST_ENTRY = 0, + AW_NOT_FIRST_ENTRY = 1, +}; + +struct aw_table { + int16_t min_val; + int16_t max_val; + uint16_t ipeak; + uint16_t gain; + uint32_t vmax; +}; + +struct aw_table_info { + uint8_t table_num; + struct aw_table *aw_table; +}; + + +enum aw_monitor_init { + AW_MON_CFG_ST = 0, + AW_MON_CFG_OK = 1, +}; + +struct aw_monitor_cfg { + uint8_t monitor_status; + uint32_t monitor_switch; + uint32_t monitor_time; + uint32_t monitor_count; + uint32_t logic_switch; + uint32_t temp_switch; + uint32_t temp_aplha; + uint32_t vol_switch; + uint32_t vol_aplha; + uint32_t ipeak_switch; + uint32_t gain_switch; + uint32_t vmax_switch; + struct aw_table_info temp_info; + struct aw_table_info vol_info; +}; + +struct aw_monitor_hdr { + uint32_t check_sum; + uint32_t monitor_ver; + char chip_type[8]; + uint32_t ui_ver; + uint32_t monitor_switch; + uint32_t monitor_time; + uint32_t monitor_count; + uint32_t ipeak_switch; + uint32_t gain_switch; + uint32_t vmax_switch; + uint32_t temp_switch; + uint32_t temp_aplha; + uint32_t temp_num; + uint32_t single_temp_size; + uint32_t temp_offset; + uint32_t vol_switch; + uint32_t vol_aplha; + uint32_t vol_num; + uint32_t single_vol_size; + uint32_t vol_offset; +}; + +struct aw_monitor_hdr_v_0_1_1 { + uint32_t check_sum; + uint32_t monitor_ver; + char chip_type[16]; + uint32_t ui_ver; + uint32_t monitor_time; + uint32_t monitor_count; + uint32_t enable_flag; + // [bit 31:7] + // [bit 6: vol en] + // [bit 5: temp en] + // [bit 4: vmax en] + // [bit 3: gain en] + // [bit 2: ipeak en] + // [bit 1: & or | flag] + // [bit 0: monitor en] + uint32_t temp_aplha; + uint32_t temp_num; + uint32_t single_temp_size; + uint32_t temp_offset; + uint32_t vol_aplha; + uint32_t vol_num; + uint32_t single_vol_size; + uint32_t vol_offset; + uint32_t reserver[3]; +}; + +struct aw_monitor_trace { + int32_t pre_val; + int32_t sum_val; + struct aw_table aw_table; +}; + +/****************************************************************** +* struct aw882xx monitor +*******************************************************************/ +struct aw8838_monitor { + struct delayed_work delay_work; + struct delayed_work load_fw_work; + struct aw_monitor_cfg monitor_cfg; + uint32_t is_enable; + uint8_t first_entry; + uint8_t samp_count; + uint32_t pre_vmax; + + struct aw_monitor_trace temp_trace; + struct aw_monitor_trace vol_trace; +#ifdef AW_DEBUG + uint16_t test_vol; + int16_t test_temp; +#endif +}; + +/****************************************************************** +* aw8838 monitor functions +*******************************************************************/ +void aw8838_monitor_start(struct aw8838_monitor *monitor); +void aw8838_monitor_stop(struct aw8838_monitor *monitor); +void aw8838_parse_monitor_dt(struct aw8838_monitor *monitor); +void aw8838_monitor_init(struct aw8838_monitor *monitor); +void aw8838_monitor_deinit(struct aw8838_monitor *monitor); + +/******************************************** + * print information control + *******************************************/ +#define aw_dev_err(dev, format, ...) \ + pr_err("[%s]" format, dev_name(dev), ##__VA_ARGS__) + +#define aw_dev_info(dev, format, ...) \ + pr_info("[%s]" format, dev_name(dev), ##__VA_ARGS__) + +#define aw_dev_dbg(dev, format, ...) \ + pr_debug("[%s]" format, dev_name(dev), ##__VA_ARGS__) + + +#endif + diff --git a/techpack/audio/asoc/codecs/aw8838/aw8838_reg.h b/techpack/audio/asoc/codecs/aw8838/aw8838_reg.h new file mode 100644 index 000000000000..2128511effbe --- /dev/null +++ b/techpack/audio/asoc/codecs/aw8838/aw8838_reg.h @@ -0,0 +1,258 @@ +#ifndef _AW8838_REG_H_ +#define _AW8838_REG_H_ + +/******************************************** + * Register List + *******************************************/ +#define AW8838_REG_ID 0x00 +#define AW8838_REG_SYSST 0x01 +#define AW8838_REG_SYSINT 0x02 +#define AW8838_REG_SYSINTM 0x03 +#define AW8838_REG_SYSCTRL 0x04 +#define AW8838_REG_I2SCTRL 0x05 +#define AW8838_REG_I2STXCFG 0x06 +#define AW8838_REG_PWMCTRL 0x08 +#define AW8838_REG_HAGCCFG0 0x10 +#define AW8838_REG_HAGCCFG1 0x11 +#define AW8838_REG_HAGCCFG2 0x12 +#define AW8838_REG_HAGCCFG3 0x13 +#define AW8838_REG_HAGCCFG4 0x14 +#define AW8838_REG_HAGCCFG5 0x15 +#define AW8838_REG_HAGCCFG6 0x16 +#define AW8838_REG_HAGCCFG7 0x17 +#define AW8838_REG_HAGCCFG8 0x18 +#define AW8838_REG_DBGCTRL 0x20 +#define AW8838_REG_I2SCFG 0x21 +#define AW8838_REG_I2SSTAT 0x22 +#define AW8838_REG_I2SCAPCNT 0x23 +#define AW8838_REG_HPFCCFG1 0x50 +#define AW8838_REG_HPFCCFG2 0x51 +#define AW8838_REG_HPFCCFG3 0x52 +#define AW8838_REG_HPFCCFG4 0x53 +#define AW8838_REG_HPFCCFG5 0x54 +#define AW8838_REG_CPCTRL 0x60 +#define AW8838_REG_PLLCTRL1 0x61 +#define AW8838_REG_PLLCTRL2 0x62 +#define AW8838_REG_AMPDBG1 0x63 +#define AW8838_REG_AMPDBG2 0x64 +#define AW8838_REG_TESTCTRL 0x65 + +#define AW8838_REG_MAX 0x6F + +/******************************************** + * Register Access + *******************************************/ +#define REG_NONE_ACCESS 0 +#define REG_RD_ACCESS 1 << 0 +#define REG_WR_ACCESS 1 << 1 + +static const unsigned char aw8838_reg_access[AW8838_REG_MAX]={ + [AW8838_REG_ID ] = REG_RD_ACCESS, + [AW8838_REG_SYSST ] = REG_RD_ACCESS, + [AW8838_REG_SYSINT ] = REG_RD_ACCESS, + [AW8838_REG_SYSINTM ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_SYSCTRL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_I2SCTRL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_I2STXCFG ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_PWMCTRL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HAGCCFG0 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HAGCCFG1 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HAGCCFG2 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HAGCCFG3 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HAGCCFG4 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HAGCCFG5 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HAGCCFG6 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HAGCCFG7 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HAGCCFG8 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_DBGCTRL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_I2SCFG ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_I2SSTAT ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_I2SCAPCNT] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HPFCCFG1 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HPFCCFG2 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HPFCCFG3 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HPFCCFG4 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_HPFCCFG5 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_CPCTRL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_PLLCTRL1 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_PLLCTRL2 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_AMPDBG1 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_AMPDBG2 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW8838_REG_TESTCTRL ] = REG_RD_ACCESS|REG_WR_ACCESS, +}; + +/****************************************************** + * Register Detail + *****************************************************/ +// SYSST +#define AW8838_BIT_SYSST_UVLOS ( 1<<14) +#define AW8838_BIT_SYSST_ADPS ( 1<<13) +#define AW8838_BIT_SYSST_OVPS ( 1<<11) +#define AW8838_BIT_SYSST_HVS ( 1<<10) +#define AW8838_BIT_SYSST_VOS ( 1<< 9) +#define AW8838_BIT_SYSST_SWS ( 1<< 8) +#define AW8838_BIT_SYSST_NOCLKS ( 1<< 5) +#define AW8838_BIT_SYSST_CLKS ( 1<< 4) +#define AW8838_BIT_SYSST_OCDS ( 1<< 3) +#define AW8838_BIT_SYSST_OTLS ( 1<< 2) +#define AW8838_BIT_SYSST_OTHS ( 1<< 1) +#define AW8838_BIT_SYSST_PLLS ( 1<< 0) + +// SYSINT +#define AW8838_BIT_SYSINT_UVLOI ( 1<<14) +#define AW8838_BIT_SYSINT_ADPI ( 1<<13) +#define AW8838_BIT_SYSINT_OVPI ( 1<<11) +#define AW8838_BIT_SYSINT_HVI ( 1<<10) +#define AW8838_BIT_SYSINT_VOI ( 1<< 9) +#define AW8838_BIT_SYSINT_SWI ( 1<< 8) +#define AW8838_BIT_SYSINT_NOCLKI ( 1<< 5) +#define AW8838_BIT_SYSINT_CLKI ( 1<< 4) +#define AW8838_BIT_SYSINT_OCDI ( 1<< 3) +#define AW8838_BIT_SYSINT_OTLI ( 1<< 2) +#define AW8838_BIT_SYSINT_OTHI ( 1<< 1) +#define AW8838_BIT_SYSINT_PLLI ( 1<< 0) + +// SYSINTM +#define AW8838_BIT_SYSINTM_UVLOM ( 1<<14) +#define AW8838_BIT_SYSINTM_ADPM ( 1<<13) +#define AW8838_BIT_SYSINTM_OVPM ( 1<<11) +#define AW8838_BIT_SYSINTM_HVM ( 1<<10) +#define AW8838_BIT_SYSINTM_VOM ( 1<< 9) +#define AW8838_BIT_SYSINTM_SWM ( 1<< 8) +#define AW8838_BIT_SYSINTM_NOCLKM ( 1<< 5) +#define AW8838_BIT_SYSINTM_CLKM ( 1<< 4) +#define AW8838_BIT_SYSINTM_OCDM ( 1<< 3) +#define AW8838_BIT_SYSINTM_OTLM ( 1<< 2) +#define AW8838_BIT_SYSINTM_OTHM ( 1<< 1) +#define AW8838_BIT_SYSINTM_PLLM ( 1<< 0) + +// SYSCTRL +#define AW8838_BIT_SYSCTRL_INTMODE_MASK (~( 3<< 8)) +#define AW8838_BIT_SYSCTRL_INT_HIGH_PP ( 3<< 8) +#define AW8838_BIT_SYSCTRL_INT_LOW_PP ( 2<< 8) +#define AW8838_BIT_SYSCTRL_INT_HIGH_OD ( 1<< 8) +#define AW8838_BIT_SYSCTRL_INT_LOW_OD ( 0<< 8) +#define AW8838_BIT_SYSCTRL_MODE_MASK (~( 1<< 7)) +#define AW8838_BIT_SYSCTRL_RCV_MODE ( 1<< 7) +#define AW8838_BIT_SYSCTRL_SPK_MODE ( 0<< 7) +#define AW8838_BIT_SYSCTRL_I2SEN_MASK (~( 1<< 6)) +#define AW8838_BIT_SYSCTRL_I2S_ENABLE ( 1<< 6) +#define AW8838_BIT_SYSCTRL_I2S_DISABLE ( 0<< 6) +#define AW8838_BIT_SYSCTRL_WSINV_MASK (~( 1<< 5)) +#define AW8838_BIT_SYSCTRL_WS_INVERT ( 1<< 5) +#define AW8838_BIT_SYSCTRL_WS_NO_INVERT ( 0<< 5) +#define AW8838_BIT_SYSCTRL_BCKINV_MASK (~( 1<< 4)) +#define AW8838_BIT_SYSCTRL_BCK_INVERT ( 1<< 4) +#define AW8838_BIT_SYSCTRL_BCK_NO_INVERT ( 0<< 4) +#define AW8838_BIT_SYSCTRL_IPLL_MASK (~( 1<< 3)) +#define AW8838_BIT_SYSCTRL_PLL_WORD ( 1<< 3) +#define AW8838_BIT_SYSCTRL_PLL_BIT ( 0<< 3) +#define AW8838_BIT_SYSCTRL_DSPBY_MASK (~( 1<< 2)) +#define AW8838_BIT_SYSCTRL_DSP_BYPASS ( 1<< 2) +#define AW8838_BIT_SYSCTRL_DSP_WORK ( 0<< 2) +#define AW8838_BIT_SYSCTRL_CP_MASK (~( 1<< 1)) +#define AW8838_BIT_SYSCTRL_CP_PDN ( 1<< 1) +#define AW8838_BIT_SYSCTRL_CP_ACTIVE ( 0<< 1) +#define AW8838_BIT_SYSCTRL_PW_MASK (~( 1<< 0)) +#define AW8838_BIT_SYSCTRL_PW_PDN ( 1<< 0) +#define AW8838_BIT_SYSCTRL_PW_ACTIVE ( 0<< 0) +#define AW8838_BIT_SYSCTRL_CPPD_MASK (~( 1<< 1)) +#define AW8838_BIT_SYSCTRL_CPPD_PDN ( 1<< 1) +#define AW8838_BIT_SYSCTRL_CPPD_ACTIVE ( 0<< 1) + +// I2SCTRL +#define AW8838_BIT_I2SCTRL_INPLEV_MASK (~( 1<<13)) +#define AW8838_BIT_I2SCTRL_INPLEV_0DB ( 1<<13) +#define AW8838_BIT_I2SCTRL_INPLEV_NEG_6DB ( 0<<13) +#define AW8838_BIT_I2SCTRL_STEREO_MASK (~( 1<<12)) +#define AW8838_BIT_I2SCTRL_STEREO_ENABLE ( 1<<12) +#define AW8838_BIT_I2SCTRL_STEREO_DISABLE ( 0<<12) +#define AW8838_BIT_I2SCTRL_CHS_MASK (~( 3<<10)) +#define AW8838_BIT_I2SCTRL_CHS_MONO ( 3<<10) +#define AW8838_BIT_I2SCTRL_CHS_RIGHT ( 2<<10) +#define AW8838_BIT_I2SCTRL_CHS_LEFT ( 1<<10) +#define AW8838_BIT_I2SCTRL_MD_MASK (~( 3<< 8)) +#define AW8838_BIT_I2SCTRL_MD_LSB ( 2<< 8) +#define AW8838_BIT_I2SCTRL_MD_MSB ( 1<< 8) +#define AW8838_BIT_I2SCTRL_MD_STD ( 0<< 8) +#define AW8838_BIT_I2SCTRL_FMS_MASK (~( 3<< 6)) +#define AW8838_BIT_I2SCTRL_FMS_32BIT ( 3<< 6) +#define AW8838_BIT_I2SCTRL_FMS_24BIT ( 2<< 6) +#define AW8838_BIT_I2SCTRL_FMS_20BIT ( 1<< 6) +#define AW8838_BIT_I2SCTRL_FMS_16BIT ( 0<< 6) +#define AW8838_BIT_I2SCTRL_BCK_MASK (~( 3<< 4)) +#define AW8838_BIT_I2SCTRL_BCK_64FS ( 2<< 4) +#define AW8838_BIT_I2SCTRL_BCK_48FS ( 1<< 4) +#define AW8838_BIT_I2SCTRL_BCK_32FS ( 0<< 4) +#define AW8838_BIT_I2SCTRL_SR_MASK (~(15<< 0)) +#define AW8838_BIT_I2SCTRL_SR_192K (10<< 0) +#define AW8838_BIT_I2SCTRL_SR_96K ( 9<< 0) +#define AW8838_BIT_I2SCTRL_SR_48K ( 8<< 0) +#define AW8838_BIT_I2SCTRL_SR_44P1K ( 7<< 0) +#define AW8838_BIT_I2SCTRL_SR_32K ( 6<< 0) +#define AW8838_BIT_I2SCTRL_SR_24K ( 5<< 0) +#define AW8838_BIT_I2SCTRL_SR_22K ( 4<< 0) +#define AW8838_BIT_I2SCTRL_SR_16K ( 3<< 0) +#define AW8838_BIT_I2SCTRL_SR_12K ( 2<< 0) +#define AW8838_BIT_I2SCTRL_SR_11K ( 1<< 0) +#define AW8838_BIT_I2SCTRL_SR_8K ( 0<< 0) + + +// I2STXCFG +#define AW8838_BIT_I2STXCFG_FSYNC_MASK (~( 1<<15)) +#define AW8838_BIT_I2STXCFG_FSYNC_BCK_CYCLE ( 1<<15) +#define AW8838_BIT_I2STXCFG_FSYNC_ONE_SLOT ( 0<<15) +#define AW8838_BIT_I2STXCFG_SLOT_NUM_MASK (~( 1<<14)) +#define AW8838_BIT_I2STXCFG_SLOT_NUM_4_TIMES ( 1<<14) +#define AW8838_BIT_I2STXCFG_SLOT_NUM_2_TIMES ( 0<<14) +#define AW8838_BIT_I2STXCFG_TX_SLOT_VLD_MASK (~(15<<12)) +#define AW8838_BIT_I2STXCFG_TX_SLOT_VLD_3 ( 3<<12) +#define AW8838_BIT_I2STXCFG_TX_SLOT_VLD_2 ( 2<<12) +#define AW8838_BIT_I2STXCFG_TX_SLOT_VLD_1 ( 1<<12) +#define AW8838_BIT_I2STXCFG_TX_SLOT_VLD_0 ( 0<<12) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_MASK (~(15<< 8)) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_3_2 (12<< 8) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_3_1 (10<< 8) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_3_0 ( 9<< 8) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_2_1 ( 6<< 8) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_2_0 ( 5<< 8) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_1_0 ( 3<< 8) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_3 ( 8<< 8) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_2 ( 4<< 8) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_1 ( 2<< 8) +#define AW8838_BIT_I2STXCFG_RX_SLOT_VLD_0 ( 1<< 8) +#define AW8838_BIT_I2STXCFG_DRVSTREN_MASK (~( 1<< 5)) +#define AW8838_BIT_I2STXCFG_DRVSTREN_8MA ( 1<< 5) +#define AW8838_BIT_I2STXCFG_DRVSTREN_2MA ( 0<< 5) +#define AW8838_BIT_I2STXCFG_DOHZ_MASK (~( 1<< 4)) +#define AW8838_BIT_I2STXCFG_DOHZ_HIZ ( 1<< 4) +#define AW8838_BIT_I2STXCFG_DOHZ_GND ( 0<< 4) +#define AW8838_BIT_I2STXCFG_DSEL_MASK (~( 3<< 2)) +#define AW8838_BIT_I2STXCFG_DSEL_DSP ( 2<< 2) +#define AW8838_BIT_I2STXCFG_DSEL_GAIN ( 1<< 2) +#define AW8838_BIT_I2STXCFG_DSEL_ZERO ( 0<< 2) +#define AW8838_BIT_I2STXCFG_CHS_MASK (~( 1<< 1)) +#define AW8838_BIT_I2STXCFG_CHS_RIGHT ( 1<< 1) +#define AW8838_BIT_I2STXCFG_CHS_LEFT ( 0<< 1) +#define AW8838_BIT_I2STXCFG_TX_MASK (~( 1<< 0)) +#define AW8838_BIT_I2STXCFG_TX_ENABLE ( 1<< 0) +#define AW8838_BIT_I2STXCFG_TX_DISABLE ( 0<< 0) + +// PWMCTRL +#define AW8838_BIT_PWMCTRL_HMUTE_MASK (~( 1<< 0)) +#define AW8838_BIT_PWMCTRL_HMUTE_ENABLE ( 1<< 0) +#define AW8838_BIT_PWMCTRL_HMUTE_DISABLE ( 0<< 0) + +// HAGCCFG8 +#define AW8838_BIT_HAGCCFG8_VOL_MASK (~(255<< 8)) +#define AW8838_VOLUME_MAX (0) +#define AW8838_VOLUME_MIN (-255) +#define AW8838_VOL_REG_SHIFT (8) +#define AW8838_VOL_6DB_STEP (6 * 2) + +//CPCTRL +#define AW8838_BIT_CPCTRL_CP_IPEAK_MASK (~( 1<< 15)) +#define AW8838_BIT_CPCTRL_CP_MD_MASK (~( 3<< 8)) +#define AW8838_BIT_CPCTRL_CP_MD_TRANS ( 0<<8) + +#endif diff --git a/techpack/audio/asoc/codecs/fs1815/Kbuild b/techpack/audio/asoc/codecs/fs1815/Kbuild new file mode 100644 index 000000000000..fa6f13682dbd --- /dev/null +++ b/techpack/audio/asoc/codecs/fs1815/Kbuild @@ -0,0 +1,15 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/kernel/msm-5.4/techpack/audio/include +EXTRA_CFLAGS += -I$(TOP)/kernel/msm-5.4/techpack/audio/include/uapi/ + +ifdef CONFIG_SND_SOC_FS1815 +FS1815_OBJS += fs1815.o +FS1815_OBJS += fsm_core.o +FS1815_OBJS += fsm_firmware.o +FS1815_OBJS += fsm_i2c.o +FS1815_OBJS += fsm_misc.o +endif + +obj-$(CONFIG_SND_SOC_FS1815) += fs1815_dlkm.o +fs1815_dlkm-y := $(FS1815_OBJS) diff --git a/techpack/audio/asoc/codecs/fs1815/ReleaseNote.txt b/techpack/audio/asoc/codecs/fs1815/ReleaseNote.txt new file mode 100644 index 000000000000..91dfd84a8b7f --- /dev/null +++ b/techpack/audio/asoc/codecs/fs1815/ReleaseNote.txt @@ -0,0 +1,12 @@ +v4.0.8 +2022-12-08 c7d54d6 FSDRV(fs1815): 4*fs1815 with i2c*2 application +2022-12-08 93ab5cc FSDRV(fs1815): SDZ pin 与友商兼容逻辑更新 +2022-11-18 c27e168 Merge "FSDRV(fs181599): Update i2c shutdown same as FS1815&FS1599" into spc-driver-v1 +2022-11-15 33ab193 FSDRV(fs181599): Update i2c shutdown same as FS1815&FS1599 +2022-11-15 7822aa7 FSDRV(fs1815): Update i2c shutdown to make sure device power down +2022-07-12 2aa9943 Merge "fix(fs1815): Add soft reset and shutdown function" into spc-driver-v1 +2022-06-09 5006522 feat(fs181599): Add fs1815&fs1599 compat driver +2022-06-16 4d6b5c2 fix(fs1815): Add soft reset and shutdown function +2022-05-26 eb95fce fix(fs1815): fix typo error of function symbol +2022-06-06 8fd4ca1 Merge "feat(fs1815): Add fs1815 i2c driver" into spc-driver-v1 +2022-04-07 f06858f feat(fs1815): Add fs1815 i2c driver diff --git a/techpack/audio/asoc/codecs/fs1815/fs1815.c b/techpack/audio/asoc/codecs/fs1815/fs1815.c new file mode 100644 index 000000000000..a1d191ed6e57 --- /dev/null +++ b/techpack/audio/asoc/codecs/fs1815/fs1815.c @@ -0,0 +1,171 @@ +/** + * Copyright (C) Fourier Semiconductor Inc. 2016-2020. All rights reserved. + * 2019-01-29 File created. + */ + +#include "fsm_public.h" +#if defined(CONFIG_FSM_FS1815) + +#define FS1815_STATUS 0xF000 +#define FS1815_BOVDS 0x0000 +#define FS1815_OTPDS 0x0200 +#define FS1815_OVDS 0x0300 +#define FS1815_UVDS 0x0400 +#define FS1815_OCDS 0x0500 +#define FS1815_OTWDS 0x0800 +#define FS1815_BOPS 0x0900 +#define FS1815_SPKS 0x0A00 +#define FS1815_SPKT 0x0B00 + +#define FS1815_DEVID 0xF001 +#define FS1815_REVID 0xF002 + +#define FS1815_ANASTAT 0xF003 +#define FS1815_DIGSTAT 0xF004 + +#define FS1815_CHIPINI 0xF00E + +#define FS1815_PWRCTRL 0xF010 +#define FS1815_PWDN 0x0010 +#define FS1815_I2CR 0x0110 + +#define FS1815_SYSCTRL 0xF011 +#define FS1815_OSCEN 0x0011 +#define FS1815_VBGEN 0x0111 +#define FS1815_ZMEN 0x0211 +#define FS1815_ADCEN 0x0311 +#define FS1815_BSTPD 0x0611 +#define FS1815_AMPEN 0x0711 + +#define FS1815_SPKCOEF 0xF014 + +#define FS1815_GAINCTRL 0xF018 + +#define FS1815_ACCKEY 0xF01F + +#define FS1815_BSTCTRL 0xF020 +#define FS1815_SSEND 0x0F20 +#define FS1815_VOUTSEL 0x4820 +#define FS1815_ILIMSEL 0x2420 +#define FS1815_BSTMODE 0x1020 + +#define FS1815_DACCTRL 0xF030 + +#define FS1815_LNMCTRL 0xF03F + +#define FS1815_TSCTRL 0xF04C +#define FS1815_OFFSTA 0x0E4C +#define FS1815_OFF_AUTOEN 0x0D4C +#define FS1815_TSEN 0x034C + +#define FS1815_AGC1 0x3059 +#define FS1815_AGC1RT 0x3059 +#define FS1815_AGC2 0x305A +#define FS1815_AGC3 0x305B +#define FS1815_AGC4 0x305C + +#define FS1815_BSGCTRL 0xF0BC + +#define FS1815_FW_NAME "fs1815.fsm" + + +static int fs1815_i2c_reset(fsm_dev_t *fsm_dev) +{ + uint16_t val; + int ret; + int i; + + if (!fsm_dev) { + return -EINVAL; + } + + for (i = 0; i < FSM_I2C_RETRY; i++) { + fsm_reg_write(fsm_dev, REG(FS1815_PWRCTRL), 0x0002); // reset nack + fsm_reg_read(fsm_dev, REG(FS1815_PWRCTRL), NULL); // dummy read + fsm_delay_ms(15); // 15ms + ret = fsm_reg_write(fsm_dev, REG(FS1815_PWRCTRL), 0x0001); + ret |= fsm_reg_read(fsm_dev, REG(FS1815_CHIPINI), &val); + if ((val == 0x0003) || (val == 0x0300)) { // init finished + break; + } + } + if (i == FSM_I2C_RETRY) { + pr_addr(err, "retry timeout"); + ret = -ETIMEDOUT; + } + + return ret; +} + +int fs1815_reg_init(fsm_dev_t *fsm_dev) +{ + fsm_config_t *cfg = fsm_get_config(); + int ret; + + if (fsm_dev == NULL || cfg == NULL) { + return -EINVAL; + } + + ret = fs1815_i2c_reset(fsm_dev); + ret |= fsm_reg_write(fsm_dev, REG(FS1815_SPKCOEF), (fsm_dev->tcoef << 1)); + ret |= fsm_write_reg_tbl(fsm_dev, FSM_SCENE_COMMON); + ret |= fsm_write_reg_tbl(fsm_dev, cfg->next_scene); + if (ret) { + pr_addr(err, "init failed:%d", ret); + fsm_dev->state.dev_inited = false; + } + + return ret; +} + +int fs1815_start_up(fsm_dev_t *fsm_dev) +{ + int ret; + + if (!fsm_dev) { + return -EINVAL; + } + if (fsm_dev->revid == 0x0011) { + ret = fsm_reg_write(fsm_dev, REG(FS1815_BSGCTRL), 0x9433); + } + // 0x1100EF; all enable + ret = fsm_reg_write(fsm_dev, REG(FS1815_SYSCTRL), 0x008F); + // 0x100000; power up + ret |= fsm_reg_write(fsm_dev, REG(FS1815_PWRCTRL), 0x0000); + ret |= fsm_config_vol(fsm_dev); + fsm_delay_ms(10); + fsm_dev->errcode = ret; + + return ret; +} + +int fs1815_shut_down(fsm_dev_t *fsm_dev) +{ + int ret; + + if (fsm_dev == NULL) { + return -EINVAL; + } + ret = fsm_reg_write(fsm_dev, REG(FS1815_PWRCTRL), 0x0001); + ret |= fsm_reg_write(fsm_dev, REG(FS1815_SYSCTRL), 0x0000); + fsm_delay_ms(10); + + return ret; +} + +void fs1815_ops(fsm_dev_t *fsm_dev) +{ + fsm_config_t *cfg = fsm_get_config(); + + if (!fsm_dev || !cfg) { + return; + } + + fsm_set_fw_name(FS1815_FW_NAME); + fsm_dev->dev_ops.reg_init = fs1815_reg_init; + fsm_dev->dev_ops.start_up = fs1815_start_up; + fsm_dev->dev_ops.shut_down = fs1815_shut_down; + cfg->nondsp_mode = true; + cfg->store_otp = false; +} +#endif diff --git a/techpack/audio/asoc/codecs/fs1815/fsm-dev.h b/techpack/audio/asoc/codecs/fs1815/fsm-dev.h new file mode 100644 index 000000000000..bc10fca1d12b --- /dev/null +++ b/techpack/audio/asoc/codecs/fs1815/fsm-dev.h @@ -0,0 +1,831 @@ +/** + * Copyright (C) Fourier Semiconductor Inc. 2016-2020. All rights reserved. + * 2018-10-16 File created. + */ + +#ifndef __FSM_DEV_H__ +#define __FSM_DEV_H__ + +/* ------- MACRO FEATURE ------- */ + +/* debug print macro, default closed */ +//#define DEBUG +//#define FSM_DEBUG +//#define FSM_DEBUG_I2C + +#if defined(__KERNEL__) +/* kernel module: */ +#define CONFIG_FSM_MISC +//#define CONFIG_FSM_CODEC +#define CONFIG_FSM_FIRMWARE +#define CONFIG_FSM_I2C +//#define CONFIG_FSM_Q6AFE +#endif + +/* device chip select: */ +//#define CONFIG_FSM_NONDSP +//#define CONFIG_FSM_FS1958 +#define CONFIG_FSM_FS1815 + +/* VERSION INFORMATION */ +#define FSM_CODE_VERSION "v4.0.8" +#define FSM_CODE_DATE "20230612" +#define FSM_GIT_BRANCH "spc-driver-v1" +#define FSM_GIT_COMMIT "23749327" + +#define FSM_DRV_NAME "fs16xx" +#define FSM_FW_NAME "fs16xx_dft.fsm" + +// #define FSM_CALIB_SAVE_PATH "/data" +// TODO +#define FSM_CALIB_SAVE_PATH "/mnt/vendor/persist/audio" +//#ifndef BIT +//#define BIT(nr) (1UL << (nr)) +//#endif + +/* scenes defination */ +#define FSM_SCENE_UNKNOW (0) +#define FSM_SCENE_MUSIC BIT(0) +#define FSM_SCENE_VOICE BIT(1) +#define FSM_SCENE_VOIP BIT(2) +#define FSM_SCENE_RING BIT(3) +#define FSM_SCENE_LOW_PWR BIT(4) +#define FSM_SCENE_MMI_ALL BIT(5) +#define FSM_SCENE_MMI_ALL_BYPASS BIT(6) +#define FSM_SCENE_TOP_LEFT BIT(7) +#define FSM_SCENE_TOP_LEFT_BYPASS BIT(8) +#define FSM_SCENE_TOP_RIGHT BIT(9) +#define FSM_SCENE_TOP_RIGHT_BYPASS BIT(10) +#define FSM_SCENE_BOT_LEFT BIT(11) +#define FSM_SCENE_BOT_LEFT_BYPASS BIT(12) +#define FSM_SCENE_BOT_RIGHT BIT(13) +#define FSM_SCENE_BOT_RIGHT_BYPASS BIT(14) +#define FSM_SCENE_RCV BIT(15) +#define FSM_SCENE_MAX (16) // TODO +#define FSM_SCENE_COMMON (0xFFFF) +#define FSM_VOLUME_MAX (0xFF) + +/* ------- MACRO FEATURE ------- */ + +#ifndef pr_fmt +#define pr_fmt(fmt) "%s: " fmt "\n", __func__ +#endif +#if defined(__KERNEL__) +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_REGULATOR) +#include +#include +#include +#endif +#define logprint(fmt, args...) printk(KERN_CONT fmt, ##args) +#elif defined(FSM_HAL_SUPPORT) +#include +#include +#include +#include +#include +#include +#include +#include +#include "fsm_list.h" + +/* debug info */ +#ifdef FSM_DEBUG +#undef NDEBUG +#define LOG_NDEBUG 0 // open LOGV +#endif + +#if defined(LOG_TAG) +#undef LOG_TAG +#endif +#define LOG_TAG "fsm_hal" + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +#if defined(FSM_APP) +#if defined(FSM_DEBUG) +#define pr_debug(fmt, args...) printf("%s: " fmt "\n", __func__, ##args) +#else +#define pr_debug(fmt, args...) +#endif +#define pr_info(fmt, args...) printf("%s: " fmt "\n", __func__, ##args) +#define pr_err(fmt, args...) printf("%s: " fmt "\n", __func__, ##args) +#define pr_warning(fmt, args...) printf("%s: " fmt "\n", __func__, ##args) +#define logprint(fmt, args...) printf(fmt, ##args) +#elif defined(BUILD_FSTOOL) +#ifdef DEBUG +#undef DEBUG +#endif +#ifdef FSM_DEBUG +#undef FSM_DEBUG +#endif +#ifndef FSM_DEBUG_I2C +#define FSM_DEBUG_I2C +#endif +#define pr_debug(fmt, args...) printf(fmt "\n", ##args) +#define pr_info(fmt, args...) printf(fmt "\n", ##args) +#define pr_err(fmt, args...) printf(fmt "\n", ##args) +#define pr_warning(fmt, args...) printf(fmt "\n", ##args) +#define logprint(fmt, args...) printf(fmt, ##args) +#elif defined(__NDK_BUILD__) +#include +#define pr_debug(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG,\ + LOG_TAG, "%s: " fmt, __func__, ##args) +#define pr_info(fmt, args...) __android_log_print(ANDROID_LOG_INFO,\ + LOG_TAG, "%s: " fmt, __func__, ##args) +#define pr_err(fmt, args...) __android_log_print(ANDROID_LOG_ERROR,\ + LOG_TAG, "%s: " fmt, __func__, ##args) +#define pr_warning(fmt, args...) __android_log_print(ANDROID_LOG_ERROR,\ + LOG_TAG, "%s: " fmt, __func__, ##args) +#define logprint(fmt, args...) __android_log_print(ANDROID_LOG_INFO,\ + LOG_TAG, fmt, ##args) +#elif defined(__ANDROID__) +#include +#define pr_debug(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG,\ + LOG_TAG, "%s: " fmt, __func__, ##args) +#define pr_info(fmt, args...) __android_log_print(ANDROID_LOG_INFO,\ + LOG_TAG, "%s: " fmt, __func__, ##args) +#define pr_err(fmt, args...) __android_log_print(ANDROID_LOG_ERROR,\ + LOG_TAG, "%s: " fmt, __func__, ##args) +#define pr_warning(fmt, args...) __android_log_print(ANDROID_LOG_ERROR,\ + LOG_TAG, "%s: " fmt, __func__, ##args) +#define logprint(fmt, args...) __android_log_print(ANDROID_LOG_INFO,\ + LOG_TAG, fmt, ##args) +#else +#define pr_debug(fmt, args...) +#define pr_info(fmt, args...) +#define pr_err(fmt, args...) +#define pr_warning(fmt, args...) +#define logprint(fmt, args...) +#endif // FSM_APP +#endif // FSM_HAL_SUPPORT +#define pr_addr(type, fmt, args...) \ + pr_##type("%02X: " fmt, fsm_dev->addr, ##args) + +#define FSM_FUNC_EXIT(ret) \ + do { \ + if (ret) \ + pr_err("err: %d", ret); \ + } while(0) + +#define FSM_ADDR_EXIT(ret) \ + do { \ + if (ret) \ + pr_addr(err, "err: %d", ret); \ + } while(0) + +#define UNUSED(expr) do { (void)(expr); } while(0) + +enum dev_id_index { + FS1601S_DEV_ID = 0x3, + FS1603_DEV_ID = 0x5, + FS1818_DEV_ID = 0x6, + FS1860_DEV_ID = 0x7, + FS1896_DEV_ID = 0xB, + FS1815_DEV_ID = 0x12, + FS1820_DEV_ID = FS1815_DEV_ID, + FS1958_DEV_ID = 0x17, + FSM_DEV_ID_MAX, +}; +#define FS1603S_V1 0X05B1 +#define FS1603S_SERIES 0X05B0 +#define IS_FS1603S(id) (((id) & 0xFFF0) == FS1603S_SERIES) + +#define CUST_NAME_SIZE (32) +#define STRING_LEN_MAX (255) +#define LOG_BUF_SIZE (8 * 8 + 1) +#define FSM_AFE_HEADER_LEN (5) +#define FSM_DFT_AMB_TEMPR (25) +#define FSM_DFT_AMB_VBAT (3678) + +#define STEREO_COEF_LEN (10) +#define FSM_MAGNIF_FACTOR (10) // magnification factor: 2^(x) +#define FSM_PAYLOAD_SIZE (64) +#define FSM_WAIT_STABLE_RETRY (30) +#define FSM_CALIB_RE25_RETRY (50) +#define FSM_I2C_RETRY (5) +#define FSM_ZMDELTA_MAX 0x150 +#define FSM_RS_TRIM_DEFAULT 0x8F +#define FSM_MAGNIF_TEMPR_COEF 0xFFFF +#define FSM_OTP_ACC_KEY2 0xCA91 +#define FSM_DATA_TYPE_RE25 0 +#define FSM_DATA_TYPE_ZMDATA 1 +#define MODULE_INITED (13579) +#define FSM_VBATH_COUNT (10) +#define FSM_VBAT_HIGH (4000) // mV +#define FSM_VBAT_LOW (3600) // mV +#define FSM_VBAT_STEP ((FSM_VBAT_HIGH - FSM_VBAT_LOW) / 8) // mV + +#define FSM_DEV_MAX (4) +#define FSM_ADDR_BASE (0x34) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define HIGH8(val) ((val >> 8) & 0xFF) +#define LOW8(val) (val & 0xFF) +#define WORD(addr) ((*(addr) << 8) & *((addr) + 1)) +#define REG(bf) (bf & 0xFF) +#define FSM_REG_SIZE sizeof(uint16_t) +#define FSM_ADDR_SIZE sizeof(uint8_t) + +/* f0 test */ +#define COUNT(start, end, step) (((end) - (start)) / (step) + 1) +#define ACS_COEF_COUNT 12 +#define COEF_LEN 5 +#define FREQ_START 200 // 100*N +#define FREQ_END 1500 // bigger than FREQ_START && 100*N +#define F0_FREQ_STEP 100 // 50*N, N =1,2,3... +#define FREQ_COUNT COUNT(FREQ_START, FREQ_END, F0_FREQ_STEP) +#define FREQ_EXT_COUNT 300 +#define RE25_TEST_COUNT 10 +#define RE25_1ST_WAIT_MS 2500 +#define RE25_DELAY_MS 100 +#define F0_1ST_DELAY_MS 800 +#define F0_TEST_DELAY_MS 500 + +#define FSM_POS_MONO (0) +#define FSM_POS_LTOP BIT(3) +#define FSM_POS_LBTM BIT(2) +#define FSM_POS_RTOP BIT(1) +#define FSM_POS_RBTM BIT(0) + +//#undef NULL +//#define NULL ((void *)0) +// +//enum { +// false = 0, +// true = 1 +//}; + +enum fsm_error { + FSM_ERROR_OK = 0, + FSM_ERROR_BAD_PARMS, + FSM_ERROR_I2C_FATAL, + FSM_ERROR_NO_SCENE_MATCH, + FSM_ERROR_RE25_INVAILD, + FSM_ERROR_MAX, +}; + +enum fsm_channel { + FSM_CHN_UNKNOW = 0, + FSM_CHN_LEFT = 1, + FSM_CHN_RIGHT = 2, + FSM_CHN_MONO = 3, +}; + +enum fsm_format { + FSM_FMT_DSP = 1, + FSM_FMT_MSB = 2, + FSM_FMT_I2S = 3, + FSM_FMT_LSB_16 = 4, + FSM_FMT_LSB_20 = 6, + FSM_FMT_LSB_24 = 7, +}; + +enum fsadsp_tx_fmt { + FSADSP_TX_FMT_NONE = 0, + FSADSP_TX_FMT_I2S_S16, + FSADSP_TX_FMT_I2S_S32, + FSADSP_TX_FMT_TDM_MONO_VBAT, // SLOT:2 LEN: 16BIT IV + 16BIT VBAT + FSADSP_TX_FMT_TDM_STEREO, // SLOT:4 LEN: 16BIT IV on LEFT / RIGHT +}; + +enum fsm_bstmode { + FSM_BSTMODE_FLW = 0, + FSM_BSTMODE_BST = 1, + FSM_BSTMODE_ADP = 2, +}; + +enum fsm_dsp_state { + FSM_DSP_OFF = 0, + FSM_DSP_ON = 1, +}; + +enum fsm_wait_type { + FSM_WAIT_AMP_ON = 1, + FSM_WAIT_AMP_ADC_ON, + FSM_WAIT_AMP_OFF, + FSM_WAIT_AMP_ADC_OFF, + FSM_WAIT_AMP_ADC_PLL_OFF, + FSM_WAIT_TSIGNAL_ON, + FSM_WAIT_TSIGNAL_OFF, + FSM_WAIT_OTP_READY, + FSM_WAIT_BOOST_SSEND, + FSM_WAIT_STATUS_ON, +}; + +enum fsm_eq_ram_type { + FSM_EQ_RAM0 = 0, + FSM_EQ_RAM1, + FSM_EQ_RAM_MAX, +}; + +struct fsm_version { + char git_branch[50]; + char git_commit[41]; + char code_date[18]; + char code_version[10]; +}; +typedef struct fsm_version fsm_version_t; + +struct fsm_pll_config { + uint32_t bclk; + uint16_t c1; + uint16_t c2; + uint16_t c3; +}; +typedef struct fsm_pll_config fsm_pll_config_t; + +#define PRESET_VER_FS1601S 0x8301 // FS1601S +#define PRESET_VER_FS1603 0x8501 // FS1603 series +#define PRESET_VER_FS1801 0x8A01 // FS1801 series +#define PRESET_VER_FS1860 0x8701 // FS1860 +#define PRESET_VER3 (0x93) +#define IS_PRESET_V3(ver) (HIGH8(ver) >= PRESET_VER3) + +enum fsm_dsc_type { + FSM_DSC_DEV_INFO = 0, + FSM_DSC_SPK_INFO, + FSM_DSC_REG_COMMON, + FSM_DSC_REG_SCENES, + FSM_DSC_PRESET_EQ, + FSM_DSC_STEREO_COEF, + FSM_DSC_EXCER_RAM, + FSM_DSC_MAX, +}; + +enum fsm_info_type { + FSM_INFO_SPK_TMAX = 0, + FSM_INFO_SPK_TEMPR_COEF, + FSM_INFO_SPK_TEMPR_SEL, + FSM_INFO_SPK_RES, + FSM_INFO_SPK_RAPP, + FSM_INFO_SPK_TRE, + FSM_INFO_SPK_TM6, + FSM_INFO_SPK_TM24, + FSM_INFO_SPK_TERR, + FSM_INFO_SPK_TM01, + FSM_INFO_SPK_TM02, + FSM_INFO_SPK_TM03, + FSM_INFO_SPK_TM04, + FSM_INFO_SPK_TM05, + FSM_INFO_SPK_TM06, + FSM_INFO_RSRL_RATIO, + FSM_INFO_SPK_POSITION, + FSM_INFO_SPK_MAX, +}; + +enum fsm_test_type { + TEST_NONE = 0, + TEST_RE25, + TEST_F0, +}; + +#pragma pack(push, 1) +struct fsm_date { + uint32_t min : 6; + uint32_t hour : 5; + uint32_t day : 5; + uint32_t month : 4; + uint32_t year : 12; +}; +typedef struct fsm_date fsm_date_t; + +struct data32_list { + uint16_t len; + uint32_t data[]; +}; +typedef struct data32_list preset_data_t; +typedef struct data32_list ram_data_t; + +struct uint24 { + uint8_t DL; + uint8_t DM; + uint8_t DH; +}; +typedef struct uint24 uint24_t; + +struct data24_list { + uint16_t len; + uint24_t data[]; +}; + +struct data16_list { + uint16_t len; + uint16_t data[]; +}; +typedef struct data16_list info_list_t; +typedef struct data16_list stereo_coef_t; + +struct fsm_index { + uint16_t offset; + uint16_t type; +}; +typedef struct fsm_index fsm_index_t; + +struct dev_list { + uint16_t preset_ver; + char project[8]; + char customer[8]; + fsm_date_t date; + uint16_t data_len; + uint16_t crc16; + uint16_t len; + uint16_t bus; + uint16_t addr; + uint16_t dev_type; + uint16_t npreset; + uint16_t reg_scenes; + uint16_t eq_scenes; + fsm_index_t index[]; +}; +typedef struct dev_list dev_list_t; + +struct preset_list { + uint16_t len; + uint16_t scene; + uint32_t data[]; +}; +typedef struct preset_list preset_list_t; + +struct reg_unit { + uint16_t addr : 8; + uint16_t pos : 4; + uint16_t len : 4; + uint16_t value; +}; +typedef struct reg_unit reg_unit_t; + +struct reg_wait { + uint16_t val : 8; + uint16_t pos : 4; + uint16_t len : 4; +}; +typedef struct reg_wait reg_wait_t; + +struct reg_comm { + uint16_t len; + reg_unit_t reg[]; +}; +typedef struct reg_comm reg_comm_t; + +struct regs_unit { + uint16_t scene; + reg_unit_t reg; +}; +typedef struct regs_unit regs_unit_t; + +struct reg_scene { + uint16_t len; + regs_unit_t regs[]; +}; +typedef struct reg_scene reg_scene_t; + +struct reg_temp { + uint16_t new_val; + uint16_t old_val; +}; +typedef struct reg_temp reg_temp_t; + +struct preset_header { + uint16_t version; + char customer[8]; + char project[8]; + fsm_date_t date; + uint16_t size; + uint16_t crc16; + uint16_t ndev; +}; + +struct preset_file { + struct preset_header hdr; + fsm_index_t index[]; +}; + +struct calib_index { + uint8_t chn; + int re25; +}; + +struct fsm_calib_bin { + int size; + uint8_t ndev; + struct calib_index index[]; +}; + +#pragma pack(pop) + +struct fsm_srate { + uint32_t srate : 28; + uint32_t bf_val : 4; +}; + +struct fsm_dev; + +struct fsm_ops { + int (*reg_init)(struct fsm_dev *); + int (*dev_init)(struct fsm_dev *); + int (*pll_config)(struct fsm_dev *, bool on); + int (*i2s_config)(struct fsm_dev *); + int (*switch_preset)(struct fsm_dev *); + int (*check_stable)(struct fsm_dev *, int type); + int (*start_up)(struct fsm_dev *); + int (*set_unmute)(struct fsm_dev *); + int (*set_tsignal)(struct fsm_dev *, bool enable); + int (*set_mute)(struct fsm_dev *, bool mute); + int (*shut_down)(struct fsm_dev *); + int (*deinit)(struct fsm_dev *); + int (*dev_recover)(struct fsm_dev *); + int (*cal_zmdata)(struct fsm_dev *); + int (*pre_calib)(struct fsm_dev *); + int (*store_otp)(struct fsm_dev *, uint8_t valOTP); + int (*post_calib)(struct fsm_dev *); + int (*pre_f0_test)(struct fsm_dev *); + int (*f0_test)(struct fsm_dev *); + int (*post_f0_test)(struct fsm_dev *); +}; +typedef struct fsm_ops fsm_ops_t; + +struct fsm_config { + uint8_t dev_count; + uint16_t cur_angle; + uint16_t next_angle; + uint16_t next_scene; + uint16_t test_type; + uint16_t test_freq; + uint16_t freq_start; + uint16_t freq_end; + uint16_t freq_step; + uint16_t freq_count; + uint16_t volume; + uint16_t amb_tempr; + uint16_t state : 8; + uint16_t wait_type : 8; + uint16_t tx_fmt : 8; + uint16_t rsvd : 8; + unsigned int i2s_bclk; + unsigned int i2s_srate; + + // flags + uint32_t codec_inited : 3; + uint32_t force_fw : 1; + uint32_t force_init : 1; + uint32_t force_scene : 1; + uint32_t force_calib : 1; + uint32_t store_otp : 1; + uint32_t force_mute : 1; + uint32_t vddd_on : 1; + uint32_t reset_chip : 1; + uint32_t stop_test : 1; + uint32_t use_monitor : 1; + uint32_t skip_monitor : 1; + uint32_t stream_muted : 1; + uint32_t i2c_debug : 1; + uint32_t nondsp_mode : 1; + uint32_t speaker_on : 1; + //uint32_t bypass_dsp : 1; + + char *codec_name[FSM_DEV_MAX]; + char *codec_dai_name[FSM_DEV_MAX]; + char *fw_name; + void *preset; +}; +typedef struct fsm_config fsm_config_t; + +struct fsm_vbat_state { + uint16_t last_batv; + uint16_t cur_batv; + uint16_t batvh_count; +}; + +struct fsm_calib { + uint16_t pos : 8; + uint16_t addr : 8; + uint16_t cal_zm; + uint8_t count; + int errcode; + int re25; + int f0; +}; + +struct fsm_f0_data { + uint16_t freq; + uint16_t zmdata; + uint16_t min_zm; +}; + +struct fsm_cal_result { + uint16_t ndev; + uint16_t freq_start; + uint16_t freq_end; + uint16_t freq_step; + uint16_t freq_count; + struct fsm_calib info[FSM_DEV_MAX]; + uint16_t f0_zmdata[]; +}; + +struct re25_data { + uint16_t count; + uint16_t pre_val; + uint16_t min_val; + uint16_t zmdata; +}; + +struct f0_data { + uint16_t count : 8; + uint16_t min_idx : 8; + uint16_t min_zm; + uint16_t zmdata[]; +}; + +struct fsm_test_data { + int test_re25; + int test_f0; + int sum_re25; + int count_re25; + int sum_f0; + int count_f0; + struct re25_data re25; + struct f0_data f0; +}; + +struct fsm_livedata { + int Re25; + int R0; + int F0; + int Q; + int T; + int Vbat; + int Excer; +}; + +struct fsm_cal_data { + uint32_t channel; + uint32_t cal_re; + uint32_t cal_tempr; + uint32_t re_min : 16; + uint32_t re_max : 16; + bool calib_pass; // true: pass, false: fail +}; + +struct fsm_calib_v2 { + uint32_t dev_count; + struct fsm_cal_data cal_data[FSM_DEV_MAX]; +}; + +#if !defined(CONFIG_I2C) +struct i2c_client { + int addr; +}; +#endif + +struct fsm_msg { + uint32_t size; // size of *buf + void *buf; +}; +typedef struct fsm_msg fsm_msg_t; + +struct fsm_bpcoef { + int freq; + uint32_t coef[COEF_LEN]; +}; + +struct fsm_state { + uint16_t dev_inited : 1; + uint16_t amp_mute : 1; + uint16_t bypass_dsp : 1; + uint16_t calibrated : 1; + uint16_t otp_stored : 1; + uint16_t re25_runin : 1; + uint16_t f0_runing : 1; +}; + +struct fsm_compat { + uint16_t preset_unit_len; + uint16_t excer_ram_len; + uint16_t addr_excer_ram; + uint16_t otp_max_count; + uint16_t ACSEQA : 8; + uint16_t ACSEQWL : 8; + uint16_t DACEQA : 8; + uint16_t DACEQWL : 8; + int RS2RL_RATIO; +}; + +struct fsm_monitor_data { + uint16_t status; + uint16_t anastat; + uint16_t digstat; + uint16_t tsctrl; + uint16_t zmdata; + uint16_t batv; // mV + int ic_tempr; + int spk_tempr; + int spk_imp; +}; + +#if defined(CONFIG_FSM_MISC) || defined(FSM_HAL_SUPPORT) +#include +/* ioctl magic number and cmd. */ +#define FSM_IOC_MAGIC (0x7C) +#define FSM_IOC_GET_DEVICE _IOR(FSM_IOC_MAGIC, 1, int) +#define FSM_IOC_SET_SRATE _IOW(FSM_IOC_MAGIC, 2, int) +#define FSM_IOC_SET_SCENE _IOW(FSM_IOC_MAGIC, 3, int) +#define FSM_IOC_INIT _IOW(FSM_IOC_MAGIC, 4, int) +#define FSM_IOC_SPEAKER_ON _IOW(FSM_IOC_MAGIC, 5, int) +#define FSM_IOC_SPEAKER_OFF _IOW(FSM_IOC_MAGIC, 6, int) +#define FSM_IOC_CALIBRATE _IOW(FSM_IOC_MAGIC, 7, int) +#define FSM_IOC_F0_TEST _IOW(FSM_IOC_MAGIC, 8, int) +#define FSM_IOC_GET_RESULT _IOR(FSM_IOC_MAGIC, 9, void *) +#define FSM_IOC_SET_ADDR _IOW(FSM_IOC_MAGIC, 10, int) +#define FSM_IOC_SEND_APR _IOWR(FSM_IOC_MAGIC, 11, void *) +#define FSM_IOC_SET_SLAVE (0x0706) +#define FSM_IOC_MAXNR (12) +#ifdef CONFIG_COMPAT +#define FSM_IOC_GET_RESULT32 _IOR(FSM_IOC_MAGIC, 9, compat_uptr_t) +#define FSM_IOC_SEND_APR32 _IOWR(FSM_IOC_MAGIC, 11, compat_uptr_t) +#endif + +struct fsm_misc_args { + uint16_t scene; + int srate; + int bclk; + // init + bool force_init; + // re25 + bool force_calib; + bool store_otp; + // f0 + uint16_t freq_start; + uint16_t freq_end; + uint16_t freq_step; + uint16_t freq_count; +}; +#endif + +struct fsm_dev { + struct i2c_client *i2c; +#if defined(CONFIG_FSM_REGMAP) + struct regmap *regmap; +#endif + struct list_head list; +#if defined(__KERNEL__) + struct workqueue_struct *fsm_wq; + struct delayed_work init_work; + struct delayed_work monitor_work; + struct delayed_work interrupt_work; + struct mutex i2c_lock; + int irq_id; +#endif + struct fsm_ops dev_ops; + struct preset_file *preset; + struct dev_list *dev_list; + struct fsm_state state; + struct fsm_compat compat; + struct fsm_test_data *tdata; + uint16_t bus : 8; + uint16_t addr : 8; + uint16_t version; + uint16_t revid; + uint16_t ram_scene[FSM_EQ_RAM_MAX]; + uint16_t own_scene; + uint16_t cur_scene; + uint16_t cal_count; + uint16_t tmax; + uint16_t tcoef; + uint16_t tsel; + uint16_t rapp; + uint16_t rstrim; + uint16_t pos_mask : 8; // position + uint16_t acc_count : 8; // acc-key count + uint16_t rec_count; + + uint16_t load_fw : 1; + uint16_t use_irq : 1; + uint16_t is1603s : 1; + uint16_t is1801 : 1; + uint16_t is1820 : 1; + uint16_t is1958 : 1; + + int rst_gpio; + int irq_gpio; + int id; + int spkr; + int re25; + int re25_dft; + int f0; + int errcode; + char const *dev_name; +}; +typedef struct fsm_dev fsm_dev_t; + +#endif diff --git a/techpack/audio/asoc/codecs/fs1815/fsm_core.c b/techpack/audio/asoc/codecs/fs1815/fsm_core.c new file mode 100644 index 000000000000..950f4a5b1718 --- /dev/null +++ b/techpack/audio/asoc/codecs/fs1815/fsm_core.c @@ -0,0 +1,1822 @@ +/** + * Copyright (C) Fourier Semiconductor Inc. 2016-2020. All rights reserved. + * 2018-10-17 File created. + */ + +#include "fsm_public.h" +#include + +#define CRC16_TABLE_SIZE 256 +#define CRC16_POLY_NOMIAL 0xA001 + +static int fsm_try_init(void); +static int g_dev_inited = 0; + +static atomic_t fsm_amp_switch; +static atomic_t fsm_adsp_enable; + +//include "q6fsm-v3.h" +extern int q6fsm_set_rotation(int angle); +extern int q6fsm_set_rx_enable(int enable); +extern void q6fsm_fsm_check_scene_status(bool status); +static LIST_HEAD(fsm_dev_list); +#define fsm_list_init(fsm_dev) \ + do { \ + INIT_LIST_HEAD(&fsm_dev->list); \ + list_add(&fsm_dev->list, &fsm_dev_list); \ + } while (0) + +#define fsm_list_entry(fsm_dev, ops) \ + do { \ + list_for_each_entry(fsm_dev, &fsm_dev_list, list) { \ + if (!fsm_skip_device(fsm_dev) && ops) { \ + ops(fsm_dev); \ + } \ + } \ + } while(0) + +#define fsm_list_func(fsm_dev, func) \ + do { \ + list_for_each_entry(fsm_dev, &fsm_dev_list, list) { \ + if (!fsm_skip_device(fsm_dev)) { \ + func(fsm_dev); \ + } \ + } \ + } while(0) + +#define fsm_list_check(fsm_dev, type, ret) \ + do { \ + ret = 0; \ + list_for_each_entry(fsm_dev, &fsm_dev_list, list) { \ + if (!fsm_skip_device(fsm_dev)) { \ + ret |= fsm_stub_check_stable(fsm_dev, type); \ + if (ret) { \ + break; \ + } \ + } \ + } \ + } while(0) + +#define fsm_list_return(fsm_dev, func, ret) \ + do { \ + ret = 0; \ + list_for_each_entry(fsm_dev, &fsm_dev_list, list) { \ + if (!fsm_skip_device(fsm_dev)) { \ + ret |= func(fsm_dev); \ + } \ + } \ + } while(0) + +#define fsm_list_func_arg(fsm_dev, func, argv) \ + do { \ + list_for_each_entry(fsm_dev, &fsm_dev_list, list) { \ + if (!fsm_skip_device(fsm_dev)) { \ + func(fsm_dev, argv); \ + } \ + } \ + } while(0) + + +static uint16_t g_crc16table[CRC16_TABLE_SIZE] = { + 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, + 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, + 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, + 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, + 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, + 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, + 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, + 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, + 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, + 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, + 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, + 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, + 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, + 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, + 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, + 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, + 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, + 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, + 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, + 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, + 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, + 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, + 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, + 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, + 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, + 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, + 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, + 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, + 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, + 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, + 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, + 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040, +}; + +static struct fsm_config g_fsm_config = { + .dev_count = 0, + .volume = FSM_VOLUME_MAX, + .next_scene = FSM_SCENE_MUSIC, + .i2s_bclk = 1536000, + .i2s_srate = 48000, + .test_type = TEST_NONE, + .freq_start = FREQ_START, + .freq_end = FREQ_END, + .freq_step = F0_FREQ_STEP, + .freq_count = FREQ_COUNT, + .amb_tempr = FSM_DFT_AMB_TEMPR, + .cur_angle = 0, + .next_angle = 0, + + // flags + .vddd_on = 0, + .codec_inited = 0, + .force_fw = 0, + .force_init = 0, + .force_scene = 0, + .force_calib = 0, + .store_otp = 0, + .nondsp_mode = 1, +#ifdef FSM_DEBUG + .i2c_debug = 1, +#endif + .force_mute = 0, + .stop_test = 0, + .use_monitor = 0, + .skip_monitor = 0, + .stream_muted = 1, + .fw_name = FSM_FW_NAME, + .preset = NULL, +}; + +static const struct fsm_srate g_srate_tbl[] = { + { 8000, 0 }, + { 16000, 3 }, + { 32000, 8 }, + { 44100, 7 }, + { 48000, 8 }, + // fs1860 support below srate: + { 88200, 9 }, + { 96000, 10 }, +}; + +int fsm_skip_device(fsm_dev_t *fsm_dev) +{ + fsm_config_t *cfg = fsm_get_config(); + + if (!fsm_dev || !cfg) { + return 1; + } + if (!fsm_dev->state.dev_inited) { + return 1; + } + if ((fsm_dev->own_scene & cfg->next_scene) == 0) { + return 1; + } + + return 0; +} + +fsm_config_t *fsm_get_config(void) +{ + return &g_fsm_config; +} +#ifdef __KERNEL__ +EXPORT_SYMBOL(fsm_get_config); +#endif + +void fsm_get_version(fsm_version_t *version) +{ + sprintf(version->git_branch, "%s", FSM_GIT_BRANCH); + sprintf(version->git_commit, "%s", FSM_GIT_COMMIT); + sprintf(version->code_date, "%s", FSM_CODE_DATE); + sprintf(version->code_version, "%s", FSM_CODE_VERSION); + pr_info("version %s", version->code_version); +} + +void *fsm_alloc_mem(int size) +{ +#if defined(__KERNEL__) + return kzalloc(size, GFP_KERNEL); +#elif defined(FSM_HAL_SUPPORT) + return malloc(size); +#else + return NULL; +#endif +} + +void fsm_free_mem(void **buf) +{ + if (*buf == NULL) { + return; + } + +#if defined(__KERNEL__) + kfree(*buf); +#elif defined(FSM_HAL_SUPPORT) + free(*buf); +#endif + *buf = NULL; +} + +void fsm_delay_ms(uint32_t delay_ms) +{ + if (delay_ms == 0) { + return; + } +#if defined(__KERNEL__) + usleep_range(delay_ms * 1000, delay_ms * 1000 + 1); +#elif defined(FSM_HAL_SUPPORT) + usleep(delay_ms * 1000); +#endif +} + +uint16_t set_bf_val(uint16_t *pval, const uint16_t bf, const uint16_t bf_val) +{ + uint8_t len = (bf >> 12) & 0x0F; + uint8_t pos = (bf >> 8) & 0x0F; + uint16_t new_val; + uint16_t old_val; + uint16_t msk; + + if (!pval) { + return 0xFFFF; + } + old_val = new_val = *pval; + msk = ((1 << (len + 1)) - 1) << pos; + new_val &= ~msk; + new_val |= bf_val << pos; + *pval = new_val; + + return old_val; +} + +uint16_t get_bf_val(const uint16_t bf, const uint16_t val) +{ + uint8_t len = (bf >> 12) & 0x0F; + uint8_t pos = (bf >> 8) & 0x0F; + uint16_t msk, value; + + msk = ((1 << (len + 1)) - 1) << pos; + value = (val & msk) >> pos; + + return value; +} + +int fsm_set_bf(fsm_dev_t *fsm_dev, const uint16_t bf, const uint16_t val) +{ + reg_unit_t reg; + uint16_t oldval; + uint16_t msk; + int ret; + + if (fsm_dev == NULL) { + return -EINVAL; + } + reg.len = (bf >> 12) & 0x0F; + reg.pos = (bf >> 8) & 0x0F; + reg.addr = bf & 0xFF; + if (reg.len == 15) { + return fsm_reg_write(fsm_dev, reg.addr, val); + } + ret = fsm_reg_read(fsm_dev, reg.addr, &oldval); + if (ret) { + pr_err("get bf:%04X failed", bf); + return ret; + } + + msk = ((1 << (reg.len + 1)) - 1) << reg.pos; + reg.value = oldval & (~msk); + reg.value |= val << reg.pos; + + if(oldval == reg.value) { + return 0; + } + ret = fsm_reg_write(fsm_dev, reg.addr, reg.value); + if (ret) { + pr_err("set bf:%04X failed", bf); + return ret; + } + + return ret; +} + +int fsm_get_bf(fsm_dev_t *fsm_dev, const uint16_t bf, uint16_t *pval) +{ + reg_unit_t reg; + uint16_t msk; + int ret; + + if (fsm_dev == NULL) { + return -EINVAL; + } + reg.len = (bf >> 12) & 0x0F; + reg.pos = (bf >> 8) & 0x0F; + reg.addr = bf & 0xFF; + ret = fsm_reg_multiread(fsm_dev, reg.addr, ®.value); + if (ret) { + pr_err("get bf:%04X failed", bf); + return ret; + } + msk = ((1 << (reg.len + 1)) - 1) << reg.pos; + reg.value &= msk; + if(pval) { + *pval = reg.value >> reg.pos; + } + + return ret; +} + +struct fsm_dev *fsm_get_fsm_dev(uint8_t addr) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev; + + if (!cfg || cfg->dev_count <= 0) { + return NULL; + } + + list_for_each_entry(fsm_dev, &fsm_dev_list, list) { + if (fsm_dev && addr == fsm_dev->addr) { + return fsm_dev; + } + } + + return NULL; +} + +struct fsm_dev *fsm_get_fsm_dev_by_id(int id) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev; + + if (!cfg || cfg->dev_count <= 0) { + return NULL; + } + + list_for_each_entry(fsm_dev, &fsm_dev_list, list) { + if (fsm_dev && id == fsm_dev->id) { + return fsm_dev; + } + } + + return NULL; +} + +int fsm_reg_write(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t val) +{ + int ret; + + if (fsm_dev == NULL) { + return -EINVAL; + } +#if defined(FSM_DEBUG_I2C) + if (g_fsm_config.i2c_debug) { + pr_addr(info, "%02X<-%04X", reg, val); + } +#endif +#if defined(CONFIG_FSM_REGMAP) + ret = fsm_regmap_write(fsm_dev, reg, val); +#elif defined(CONFIG_FSM_I2C) + ret = fsm_i2c_reg_write(fsm_dev, reg, val); +#elif defined(FSM_HAL_SUPPORT) + ret = fsm_hal_reg_write(fsm_dev, reg, val); +#else + ret = -EINVAL; +#endif + if (ret) { + pr_addr(err, "%02X<-%04X fail:%d", reg, val, ret); + } + + return ret; +} + +int fsm_reg_read(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t *pval) +{ + uint16_t value; + int ret; + + if (fsm_dev == NULL) { + return -EINVAL; + } +#if defined(CONFIG_FSM_REGMAP) + ret = fsm_regmap_read(fsm_dev, reg, &value); +#elif defined(CONFIG_FSM_I2C) + ret = fsm_i2c_reg_read(fsm_dev, reg, &value); +#elif defined(FSM_HAL_SUPPORT) + ret = fsm_hal_reg_read(fsm_dev, reg, &value); +#else + ret = -EINVAL; +#endif +#if defined(FSM_DEBUG_I2C) + if (g_fsm_config.i2c_debug) { + pr_addr(info, " %02X->%04X", reg, value); + } +#endif + if (ret) { + value = 0; +#if !defined(BUILD_FSTOOL) + pr_addr(err, " %02X->%04X fail:%d", reg, value, ret); +#endif + } + if (pval) { + *pval = value; + } + + return ret; +} + +int fsm_burst_write(fsm_dev_t *fsm_dev, uint8_t reg, uint8_t *data, int len) +{ + int ret; + + if (fsm_dev == NULL) { + return -EINVAL; + } +#if defined(FSM_DEBUG_I2C) + if (len >= 4) { + if (g_fsm_config.i2c_debug) { + pr_addr(info, "%02X<-%02X %02X %02X %02X", reg, + data[0], data[1], data[2], data[3]); + } + } + // logprint("%s: %02X: %02X<-", __func__, fsm_dev->addr, reg); + // for (ret = 0; ret < len; ret++) { + // logprint("%02X ", data[ret]); + // } + // logprint("\n"); +#endif + +#if defined(CONFIG_FSM_REGMAP) + ret = fsm_regmap_bulkwrite(fsm_dev, reg, data, len); +#elif defined(CONFIG_FSM_I2C) + ret = fsm_i2c_bulkwrite(fsm_dev, reg, data, len); +#elif defined(FSM_HAL_SUPPORT) + ret = fsm_hal_bulkwrite(fsm_dev, reg, data, len); +#else + ret = -EINVAL; +#endif + if (ret) { + pr_addr(err, "BW %02X fail:%d", reg, ret); + } + + return ret; +} + +int fsm_reg_case(fsm_dev_t *fsm_dev, reg_unit_t *reg) +{ + reg_wait_t *reg_wait; + uint16_t temp; + uint16_t mask; + int count; + int ret = 0; + + if (!fsm_dev || !reg) { + return -EINVAL; + } + switch (reg->len) { + case 1: // delay ms + fsm_delay_ms(reg->value); + break; + case 2: // wait bits, 8bits max + reg_wait = (reg_wait_t *)®->value; + if (reg_wait->len >= 8) { + pr_addr(err, "invalid len:%d", reg_wait->len); + return -EINVAL; + } + mask = ((1 << (reg_wait->len + 1)) - 1); + for (count = 0; count < 35; count++) { + fsm_delay_ms(1); + ret = fsm_reg_multiread(fsm_dev, reg->addr, &temp); + temp = (temp >> reg_wait->pos) & (~mask); + if (temp == reg_wait->val) + break; + } + break; + default: + break; + } + return ret; +} + +static int fs1815_regs_compat_v1(fsm_dev_t *fsm_dev, reg_unit_t *reg) +{ + uint16_t temp; + uint16_t mask; + uint16_t val; + uint8_t addr; + int ret; + + addr = reg->addr; + val = reg->value; + if (addr == 0x20) { + if (reg->pos <= 8 && (reg->pos + reg->len) >= 12) { + mask = ((8 - reg->pos) << 8) | 0x4000 | addr; // bit[12..8] + val = get_bf_val(mask, reg->value); + if (val <= 0x07) val = val + 0; + else if (val <= 0x0F) val = val + 2; + else if (val <= 0x17) val = val + 4; + else if (val == 0x18) val = 0X1E; + else val = 0x1F; + temp = reg->value; + set_bf_val(&temp, mask, val); + val = temp; + } + } + else if (addr >= 0x59 && addr <= 0x5C) { + if (reg->pos == 0 && reg->len >= 3) { + mask = 0x3000 | addr; // bit[3..0] + val = get_bf_val(mask, reg->value); + if (val <= 0x2) val = val * 2 + 1; + else if (val <= 0x4) val = 0x6; + else if (val <= 0x9) val = val + 2; + else if (val <= 0xB) val = 0xC; + else val = 0xE; + temp = reg->value; + set_bf_val(&temp, mask, val); + val = temp; + } + } + mask = ((1 << (reg->len + 1)) - 1) << reg->pos; + val = (val << reg->pos); + ret = fsm_reg_multiread(fsm_dev, addr, &temp); + val = ((~mask & temp) | (val & mask)); + ret |= fsm_reg_write(fsm_dev, addr, val); + + return ret; +} + +int fsm_reg_update_bits(fsm_dev_t *fsm_dev, reg_unit_t *reg) +{ + uint16_t temp; + uint16_t mask; + uint16_t val; + uint8_t addr; + int ret; + + if (!fsm_dev || !reg) { + return -EINVAL; + } + if (reg->pos == 0xF && reg->len > 0) { + return fsm_reg_case(fsm_dev, reg); + } + addr = reg->addr; + if (fsm_dev->is1820 && fsm_dev->revid == 0x0011) { + if (addr == 0x20 || (addr >= 0x59 && addr <= 0x5C)) { + return fs1815_regs_compat_v1(fsm_dev, reg); + } + } + if (reg->pos == 0 && reg->len == 15) { + return fsm_reg_write(fsm_dev, addr, reg->value); + } + mask = ((1 << (reg->len + 1)) - 1) << reg->pos; + val = (reg->value << reg->pos); + ret = fsm_reg_multiread(fsm_dev, addr, &temp); + temp = ((~mask & temp) | (val & mask)); + ret |= fsm_reg_write(fsm_dev, addr, temp); + + return ret; +} + +int fsm_reg_update(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t val) +{ + uint16_t temp; + int ret; + + ret = fsm_reg_multiread(fsm_dev, reg, &temp); + if (!ret && temp != val) { + ret = fsm_reg_write(fsm_dev, reg, val); + } + if (ret) { + pr_addr(err, "update reg:%02X failed:%d", reg, ret); + } + + return ret; +} + +int fsm_reg_multiread(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t *pval) +{ + uint16_t value; + uint16_t old; + int count = 0; + int ret; + + if (fsm_dev == NULL) { + return -EINVAL; + } + while (count++ < FSM_I2C_RETRY) { + ret = fsm_reg_read(fsm_dev, reg, &value); + if (ret) + continue; + if (count > 1 && old == value) { + break; + } + old = value; + } + if (ret) { + value = 0; +#if !defined(BUILD_FSTOOL) + pr_addr(err, " %02X->%04X fail:%d", reg, value, ret); +#endif + } + if (pval) { + *pval = value; + } + + return ret; +} + +uint16_t fsm_calc_checksum(uint16_t *data, int len) +{ + uint16_t crc = 0; + uint8_t index; + uint8_t b; + 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) ^ g_crc16table[index]); + b = (uint8_t)((data[i] >> 8) & 0xFF); + index = (uint8_t)(crc ^ b); + crc = (uint16_t)((crc >> 8) ^ g_crc16table[index]); + } + return crc; +} + +int fsm_get_srate_bits(fsm_dev_t *fsm_dev, uint32_t srate) +{ + int size; + int idx; + + if (!fsm_dev) { + return -EINVAL; + } + if (srate > 48000 && HIGH8(fsm_dev->version) != FS1860_DEV_ID) { + pr_addr(err, "invalid srate:%d", srate); + return -EINVAL; + } + if (srate == 32000 && fsm_dev->is1603s) { + return 6; // I2SSR=6 + } + size = sizeof(g_srate_tbl)/ sizeof(struct fsm_srate); + for (idx = 0; idx < size; idx++) { + if (srate == g_srate_tbl[idx].srate) + return g_srate_tbl[idx].bf_val; + } + return -EINVAL; +} + +int fsm_access_key(fsm_dev_t *fsm_dev, int access) +{ + uint8_t otpacc; + int ret = 0; + + if (!fsm_dev) { + return -EINVAL; + } + if (fsm_dev->is1820) { + otpacc = 0x1F; + } else { + otpacc = 0x0B; + } + ret = fsm_reg_write(fsm_dev, otpacc, (access ? FSM_OTP_ACC_KEY2 : 0)); + + return ret; +} + +int fsm_reg_dump(fsm_dev_t *fsm_dev) +{ + char buf[LOG_BUF_SIZE]; + uint16_t value; + int reg_addr; + int reg_end; + int idx = 0; + int ret = 0; + + if (!fsm_dev) { + return -EINVAL; + } + reg_end = (fsm_dev->is1958 ? 0xEF : (fsm_dev->is1820 ? 0xEA : 0xE8)); + for (reg_addr = 0; reg_addr <= reg_end; reg_addr++) { + if (fsm_dev->is1958) { + if (reg_addr == 0x60) reg_addr = 0x80; + else if (reg_addr == 0x81) reg_addr = 0xA1; + else if (reg_addr == 0xD0) + ret |= fsm_access_key(fsm_dev, 1); + } + if (fsm_dev->is1820) { + if (reg_addr == 0x60) reg_addr = 0x80; + } + else if (reg_addr == 0x10) reg_addr = 0x40; + else if (reg_addr == 0xD0) { + ret |= fsm_access_key(fsm_dev, 1); + } + ret |= fsm_reg_read(fsm_dev, reg_addr, &value); + snprintf(buf+idx*8, 9, "%02X:%04X ", reg_addr, value); + idx++; + if (idx == 8 || reg_addr == reg_end) { + buf[idx*8-1] = '\0'; + pr_addr(info, "%s", buf); + idx = 0; + } + } + ret |= fsm_access_key(fsm_dev, 0); + + return ret; +} + +static int fsm_search_list(fsm_dev_t *fsm_dev, struct preset_file *pfile) +{ + dev_list_t *dev_list; + uint8_t *preset; + uint16_t offset; + uint16_t type; + int idx; + + preset = (uint8_t *)pfile; + for (idx = 0; idx < pfile->hdr.ndev; idx++) { + type = pfile->index[idx].type; + if (FSM_DSC_DEV_INFO != type) { + continue; + } + offset = pfile->index[idx].offset; + pr_addr(debug, "offset[%d]: %d", idx, offset); + dev_list = (struct dev_list *)&preset[offset]; + if (fsm_dev->addr == dev_list->addr) { + pr_addr(info, "found dev_list"); + fsm_dev->dev_list = dev_list; + break; + } + } + if (idx == pfile->hdr.ndev) { + pr_addr(err, "not found dev_list"); + fsm_dev->dev_list = NULL; + return -EINVAL; + } + + return 0; +} + +static int fsm_check_dev_type(fsm_dev_t *fsm_dev) +{ + uint16_t dev_type; + + if (!fsm_dev || !fsm_dev->dev_list) { + pr_addr(err, "bad parameter"); + return -EINVAL; + } + dev_type = fsm_dev->dev_list->dev_type; + if (fsm_dev->is1820) { + if (LOW8(fsm_dev->version) != HIGH8(dev_type)) { + pr_addr(err, "type:%02X not match version:%02X", + HIGH8(dev_type), HIGH8(fsm_dev->version)); + return -EINVAL; + } + return 0; + } + if (HIGH8(fsm_dev->version) == 0x06 || HIGH8(fsm_dev->version) == 0x0B) { + // fs1801 series + if (HIGH8(dev_type) != 0x0A && HIGH8(dev_type) != 0x06 + && HIGH8(dev_type) != 0x0B) { + pr_addr(err, "invalid type:%04X", dev_type); + return -EINVAL; + } + return 0; + } + // other series + if (HIGH8(fsm_dev->version) != HIGH8(dev_type)) { + pr_addr(err, "type:%02X not match version:%02X", + HIGH8(dev_type), HIGH8(fsm_dev->version)); + return -EINVAL; + } + + return 0; +} + +int fsm_init_dev_list(fsm_dev_t *fsm_dev) +{ + struct preset_file *pfile; + dev_list_t *dev_list; + int ret; + + pfile = fsm_get_presets(); + if (!fsm_dev || !pfile) { + pr_addr(err, "bad parameter or invalid FW"); + return -EINVAL; + } + + pr_addr(debug, "ndev:%d", pfile->hdr.ndev); + ret = fsm_search_list(fsm_dev, pfile); + if (ret) { + return ret; + } + dev_list = fsm_dev->dev_list; + // assert(dev_list != NULL); + ret = fsm_check_dev_type(fsm_dev); + if (ret) { + pr_addr(err, "type(%04X) not matched version(%04X)", + dev_list->dev_type, fsm_dev->version); + fsm_dev->dev_list = NULL; + return ret; + } + pr_addr(info, "preset ver:%04X", dev_list->preset_ver); + if ((dev_list->preset_ver & BIT(15)) == 0) { // BIT_15 must be 1 + pr_addr(err, "invalid preset version:%04X", + dev_list->preset_ver); + return -EINVAL; + } + pr_addr(debug, "bus : %d, len: %d", + dev_list->bus, dev_list->len); + pr_addr(debug, "type : %04X, npreset: %d", + dev_list->dev_type, dev_list->npreset); + pr_addr(debug, "scene: eq:%04X, reg:%04X", + dev_list->eq_scenes, dev_list->reg_scenes); + fsm_dev->own_scene = dev_list->reg_scenes | dev_list->eq_scenes; + + return 0; +} + +void *fsm_get_list_by_idx(fsm_dev_t *fsm_dev, int idx) +{ + dev_list_t *dev_list; + fsm_index_t *index; + uint8_t *pdata; + + if (fsm_dev == NULL) { + return NULL; + } + dev_list = fsm_dev->dev_list; + if (dev_list == NULL) { + return NULL; + } + index = &dev_list->index[0]; + pdata = (uint8_t *)index; + + return (void *)(&pdata[index[idx].offset]); +} + +void *fsm_get_data_list(fsm_dev_t *fsm_dev, int type) +{ + dev_list_t *dev_list; + fsm_index_t *index; + int i; + + if (fsm_dev == NULL || fsm_dev->dev_list == NULL) { + return NULL; + } + dev_list = fsm_dev->dev_list; + index = dev_list->index; + if (index == NULL) { + return NULL; + } + for (i = 0; i < dev_list->len; i++) { + if (index[i].type == type) { + break; + } + } + if (i >= dev_list->len) { + return NULL; + } + + return (void *)((uint8_t *)index + index[i].offset); +} + +int fsm_get_spk_info(fsm_dev_t *fsm_dev, uint16_t info_type) +{ + info_list_t *info; + + if (fsm_dev == NULL) { + return -EINVAL; + } + info = (info_list_t *)fsm_get_data_list(fsm_dev, FSM_DSC_SPK_INFO); + if (!info || info_type > info->len) { + return -EINVAL; + } + // pr_addr(debug, "spk info len: %d", info->len); + + return info->data[info_type]; +} + +int fsm_init_info(fsm_dev_t *fsm_dev) +{ + if (!fsm_dev || !fsm_dev->dev_list) { + return -EINVAL; + } + fsm_dev->bus = fsm_dev->dev_list->bus; + fsm_dev->addr = fsm_dev->dev_list->addr; + fsm_dev->tmax = fsm_get_spk_info(fsm_dev, FSM_INFO_SPK_TMAX); + fsm_dev->tcoef = fsm_get_spk_info(fsm_dev, FSM_INFO_SPK_TEMPR_COEF); + fsm_dev->tsel = fsm_get_spk_info(fsm_dev, FSM_INFO_SPK_TEMPR_SEL); + pr_addr(info, "tmax:%d, tcoef:%d, tsel:%d", + fsm_dev->tmax, fsm_dev->tcoef, fsm_dev->tsel); + fsm_dev->spkr = fsm_get_spk_info(fsm_dev, FSM_INFO_SPK_RES); + fsm_dev->rapp = fsm_get_spk_info(fsm_dev, FSM_INFO_SPK_RAPP); + if (IS_PRESET_V3(fsm_dev->dev_list->preset_ver)) { + fsm_dev->pos_mask = fsm_get_spk_info(fsm_dev, FSM_INFO_SPK_POSITION); + fsm_dev->compat.RS2RL_RATIO = \ + fsm_get_spk_info(fsm_dev, FSM_INFO_RSRL_RATIO); + } + pr_addr(info, "pos:%02X, rs_ratio:%d", + fsm_dev->pos_mask, fsm_dev->compat.RS2RL_RATIO); + pr_addr(info, "spkr:%d, rapp:%d", + fsm_dev->spkr, fsm_dev->rapp); + + return 0; +} + +void *fsm_get_presets(void) +{ + return g_fsm_config.preset; +} + +void fsm_set_presets(void *preset) +{ + fsm_config_t *cfg = fsm_get_config(); + + if (cfg) { + fsm_free_mem((void **)&cfg->preset); + cfg->preset = preset; + } +} + +/** + * fsm device interface for external reference + */ +int fsm_parse_preset(const void *data, uint32_t size) +{ + struct preset_file *pfile; + struct preset_header *hdr; + uint16_t checksum; + int crc_size; + + if (data == NULL || size == 0) { + pr_err("bad parameter, size:%d", size); + return -EINVAL; + } + pfile = fsm_get_presets(); + if (pfile) { + pr_debug("already had presets, try init"); + fsm_try_init(); + return 0; + } + pfile = (struct preset_file *)fsm_alloc_mem(size); + if (!pfile) { + pr_err("alloc memery failed"); + return -ENOMEM; + } + memcpy(pfile, data, size); + + hdr = &pfile->hdr; + pr_debug("version : %04X", hdr->version); + pr_info("customer: %s", hdr->customer); + pr_info("project : %s", hdr->project); + pr_info("date : %4d%02d%02d-%02d%02d", hdr->date.year, \ + hdr->date.month, hdr->date.day, hdr->date.hour, hdr->date.min); + pr_debug("size : %d", hdr->size); + pr_debug("crc16 : %04X", hdr->crc16); + + crc_size = (size - sizeof(struct preset_header) + 2)/sizeof(uint16_t); + if (hdr->size == 0 || hdr->size != size) { + pr_err("invalid size: hdr:%d, fw:%d", hdr->size, size); + fsm_free_mem((void **)&pfile); + return -EINVAL; + } + checksum = fsm_calc_checksum((uint16_t *)(&(pfile->hdr.ndev)), crc_size); + if (checksum != hdr->crc16) { + pr_err("checksum(%04X) not match(%04X)", checksum, hdr->crc16); + fsm_free_mem((void **)&pfile); + return -EINVAL; + } + else { + pr_info("checksum success!"); + fsm_set_presets(pfile); + } + fsm_try_init(); + + return 0; +} + +int fsm_swap_channel(fsm_dev_t *fsm_dev, int next_angle) +{ + uint16_t left_chn; + uint8_t i2sctrl; + uint16_t chs12; + int ret = 0; + + if (!fsm_dev) { + return -EINVAL; + } + if (fsm_dev->is1820) { + /* fs1815 not supported */ + return 0; + } + switch (next_angle) { + case 90: + left_chn = (FSM_POS_LBTM | FSM_POS_RBTM); + break; + case 180: + left_chn = (FSM_POS_RTOP | FSM_POS_RBTM); + break; + case 270: + left_chn = (FSM_POS_LTOP | FSM_POS_RTOP); + break; + case 0: + default: + left_chn = (FSM_POS_LTOP | FSM_POS_LBTM); + break; + } + left_chn = (left_chn & fsm_dev->pos_mask); + chs12 = ((left_chn != 0) ? 1 : 2); + if ((g_fsm_config.dev_count == 1) + || (fsm_dev->pos_mask == FSM_POS_MONO)) { + chs12 = 3; + } + if (fsm_dev->is1958) { + i2sctrl = 0x17; + ret = fsm_set_bf(fsm_dev, 0x1317, chs12); // 0x17[4..3] + } else { + i2sctrl = 0x04; + ret = fsm_set_bf(fsm_dev, 0x1304, chs12); // 0x04[4..3] + } + pr_addr(debug, "pos:%02X, CHS12:%d", fsm_dev->pos_mask, chs12); + + return ret; +} + +int fsm_write_reg_tbl(fsm_dev_t *fsm_dev, uint16_t scene) +{ + reg_scene_t *reg_scene; + reg_comm_t *reg_comm; + reg_unit_t *reg; + regs_unit_t *regs; + int ret = 0; + int i; + + if (fsm_dev == NULL) { + return -EINVAL; + } + if (scene == FSM_SCENE_COMMON) { + reg_comm = fsm_get_data_list(fsm_dev, FSM_DSC_REG_COMMON); + if (!reg_comm) { + pr_addr(info, "note: not found reg_common data"); + return 0; + } + reg = reg_comm->reg; + pr_addr(debug, "reg comm len: %d", reg_comm->len); + for (i = 0; i < reg_comm->len; i++) { + ret |= fsm_reg_update_bits(fsm_dev, ®[i]); + } + } else if (scene != FSM_SCENE_UNKNOW) { + reg_scene = fsm_get_data_list(fsm_dev, FSM_DSC_REG_SCENES); + if (!reg_scene) { + pr_addr(info, "note: not found reg_scene data"); + return 0; + } + regs = reg_scene->regs; + pr_addr(debug, "reg scene len: %d", reg_scene->len); + for (i = 0; i < reg_scene->len; i++) { + if ((regs[i].scene & scene) == 0) { + continue; + } + ret |= fsm_reg_update_bits(fsm_dev, ®s[i].reg); + } + } + + FSM_FUNC_EXIT(ret); + return ret; +} + +int fsm_get_rstrim(fsm_dev_t *fsm_dev) +{ + uint16_t reg_rstrim; + uint16_t value; + int ret; + + if (!fsm_dev) { + return -EINVAL; + } + if (fsm_dev->is1958) { + fsm_dev->rstrim = 0; + return 0; + } else if (fsm_dev->is1820) { + reg_rstrim = 0x07; + } else { + reg_rstrim = 0xE6; + } + if (fsm_dev->rstrim != 0) { + return 0; + } + fsm_access_key(fsm_dev, 1); + ret = fsm_reg_read(fsm_dev, reg_rstrim, &value); + fsm_access_key(fsm_dev, 0); + if (ret || value == 0) { + pr_warning("use default rs_trim value"); + value = FSM_RS_TRIM_DEFAULT; + } + fsm_dev->rstrim = value; + + return 0; +} + +int fsm_check_spkcoef(fsm_dev_t *fsm_dev) +{ + uint8_t spkcoef; + uint16_t value; + int ret; + + if (fsm_dev == NULL) { + return -EINVAL; + } + spkcoef = ((fsm_dev->is1958 || fsm_dev->is1820) ? 0x14 : 0x08); + ret = fsm_reg_read(fsm_dev, spkcoef, &value); + if (value == 0) { + // not inited yet + return -EINVAL; + } + + return ret; +} + +int fsm_config_vol(fsm_dev_t *fsm_dev) +{ + fsm_config_t *cfg = fsm_get_config(); + uint16_t volctrl; + uint16_t volume; + + if (!fsm_dev || !cfg) { + return -EINVAL; + } + + if (cfg->volume > FSM_VOLUME_MAX) { + cfg->volume = FSM_VOLUME_MAX; + } + // volume = (fsm_dev->state.calibrated ? cfg->volume : 0xDF); // -12dB + if (fsm_dev->is1820) { + /* fs1815 not supported */ + return 0; + } + else if (fsm_dev->is1958) { + volctrl = 0x16; + volume = ((cfg->volume << 1) + 1) << 7; + } else { + volctrl = 0x06; + volume = ((cfg->volume << 8) & 0xFF00); + } + pr_addr(debug, "vol: %04X", volume); + + return fsm_reg_write(fsm_dev, volctrl, volume); +} + +int fsm_ops_dummy(fsm_dev_t *fsm_dev) +{ + return 0; +} + +/* + * fsm stub api + */ + +int fsm_stub_switch_preset(fsm_dev_t *fsm_dev) +{ + fsm_config_t *cfg = fsm_get_config(); + int ret = 0; + + if (fsm_dev == NULL) { + return -EINVAL; + } + pr_addr(debug, "%s switching", + (cfg->force_scene ? "force" : "auto")); + if (!cfg->force_scene && fsm_dev->cur_scene == cfg->next_scene) { + pr_addr(debug, "same scene, skip"); + return 0; + } + if (cfg->speaker_on) { + ret |= fsm_stub_shut_down(fsm_dev); + } + if ((fsm_dev->own_scene & cfg->next_scene) == 0) { + return ret; + } + if (fsm_dev->dev_ops.switch_preset) { + ret |= fsm_dev->dev_ops.switch_preset(fsm_dev); + } + ret |= fsm_write_reg_tbl(fsm_dev, cfg->next_scene); + fsm_dev->cur_scene = cfg->next_scene; + if (cfg->speaker_on) { + ret |= fsm_stub_start_up(fsm_dev); + } + + FSM_FUNC_EXIT(ret); + return ret; +} + +int fsm_stub_dev_init(fsm_dev_t *fsm_dev) +{ + fsm_config_t *cfg = fsm_get_config(); + int ret; + + if (fsm_dev == NULL || cfg == NULL) { + return -EINVAL; + } + + if (gpio_is_valid(fsm_dev->rst_gpio)) + gpio_direction_output(fsm_dev->rst_gpio, 1); + + if (cfg->force_init) { + pr_addr(info, "force init"); + } + ret = fsm_check_spkcoef(fsm_dev); + if (ret) { + pr_addr(info, "try init device"); + fsm_dev->state.dev_inited = false; + } + if (!cfg->force_init && fsm_dev->state.dev_inited) { + return 0; + } + if (fsm_dev->dev_ops.dev_init) { + return fsm_dev->dev_ops.dev_init(fsm_dev); + } + ret = fsm_init_dev_list(fsm_dev); + if (ret || !fsm_dev->dev_list) { + pr_addr(err, "init dev_list fail:%d", ret); + return ret; + } + fsm_dev->state.dev_inited = true; + ret = fsm_init_info(fsm_dev); + if (fsm_dev->dev_ops.reg_init) { + ret |= fsm_dev->dev_ops.reg_init(fsm_dev); + } + ret |= fsm_get_rstrim(fsm_dev); + ret |= fsm_stub_shut_down(fsm_dev); + fsm_dev->errcode = ret; + + return ret; +} + +int fsm_stub_start_up(fsm_dev_t *fsm_dev) +{ + int ret = 0; + + if (fsm_dev == NULL) { + return -EINVAL; + } + if (fsm_dev->dev_ops.start_up) { + return fsm_dev->dev_ops.start_up(fsm_dev); + } + pr_addr(err, "none ops"); + + return ret; +} + +int fsm_stub_shut_down(fsm_dev_t *fsm_dev) +{ + int ret = 0; + + if (fsm_dev == NULL) { + return -EINVAL; + } + if (fsm_dev->dev_ops.shut_down) { + return fsm_dev->dev_ops.shut_down(fsm_dev); + } + pr_addr(err, "none ops"); + + return ret; +} + +int fsm_stub_dev_deinit(fsm_dev_t *fsm_dev) +{ + if (fsm_dev == NULL) { + return -EINVAL; + } + if (fsm_dev->dev_ops.deinit) { + return fsm_dev->dev_ops.deinit(fsm_dev); + } + pr_addr(err, "none ops"); + + return 0; +} + +int fsm_save_re25(struct fsm_calib_v2 *data) +{ + if (!data) { + return -EINVAL; + } + return -EINVAL; +} + +int fsm_get_re25(struct fsm_calib_v2 *data) +{ + if (!data) { + return -EINVAL; + } + if (data->dev_count > 0) { + // already loaded + return 0; + } + return -EINVAL; +} + +int fsm_dev_count(void) +{ + return g_fsm_config.dev_count; +} + +void fsm_set_i2s_clocks(uint32_t rate, uint32_t bclk) +{ + fsm_config_t *cfg = fsm_get_config(); + + if (!cfg) { + return; + } + fsm_mutex_lock(); + cfg->i2s_bclk = bclk; + cfg->i2s_srate = rate; + if (rate == 32000) { + cfg->i2s_bclk += 32; + } + fsm_mutex_unlock(); +} + +void fsm_set_scene(int scene) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev = NULL; + + if (!cfg || scene < 0 || scene >= FSM_SCENE_MAX) { + pr_err("invaild scene:%d", scene); + return; + } + fsm_mutex_lock(); + cfg->next_scene = BIT(scene); + fsm_list_func(fsm_dev, fsm_stub_switch_preset); + fsm_mutex_unlock(); +} + +void fsm_set_volume(int volume) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev; + + if (!cfg) { + return; + } + fsm_mutex_lock(); + if (volume < 0 || volume > FSM_VOLUME_MAX) { + pr_warning("invalid volume: %d, default 0dB", volume); + volume = FSM_VOLUME_MAX; + } + cfg->volume = volume; + if (!cfg->stream_muted) { + fsm_list_func(fsm_dev, fsm_config_vol); + } + fsm_mutex_unlock(); +} + +void fsm_set_fw_name(char *name) +{ + fsm_config_t *cfg = fsm_get_config(); + + if (cfg) { + cfg->fw_name = name; + } +} + +int fsm_detect_device(fsm_dev_t *fsm_dev, uint8_t dev_id) +{ + if (fsm_dev == NULL) { + return -EINVAL; + } + fsm_dev->use_irq = false; + memset(&fsm_dev->dev_ops, 0, sizeof(fsm_dev->dev_ops)); + switch (dev_id) { + case FS1603_DEV_ID: + //fs1603_ops(fsm_dev); + break; + case FS1818_DEV_ID: + case FS1896_DEV_ID: + fsm_dev->is1801 = true; + //fs1801_ops(fsm_dev); + break; + case FS1958_DEV_ID: + fsm_dev->is1958 = true; + //fs1958_ops(fsm_dev); + break; + case FS1815_DEV_ID: + fsm_dev->is1820 = true; + fsm_reg_read(fsm_dev, 0x02, &fsm_dev->revid); + fs1815_ops(fsm_dev); + break; + default: + pr_addr(err, "invalid dev_id:%02X", dev_id); + return -EINVAL; + } + + return 0; +} + +int fsm_probe(fsm_dev_t *fsm_dev, int addr) +{ + uint16_t id; + int ret; + + if (fsm_dev == NULL) { + return -EINVAL; + } + + fsm_mutex_lock(); + if (fsm_dev->addr == 0) + fsm_dev->addr = addr; + do { + ret = fsm_reg_read(fsm_dev, 0x01, &id); + if (ret) { + fsm_dev->addr = 0; + fsm_mutex_unlock(); + return ret; + } + fsm_dev->is1603s = IS_FS1603S(id); + ret = fsm_detect_device(fsm_dev, LOW8(id)); + if (ret) { + pr_addr(err, "Unknown DEVID: %04X", id); + fsm_dev->addr = 0; + fsm_mutex_unlock(); + return ret; + } + } while (0); +#ifndef BUILD_FSTOOL + pr_addr(info, "Found DEVID: %04X", id); +#endif + fsm_dev->ram_scene[FSM_EQ_RAM0] = FSM_SCENE_UNKNOW; + fsm_dev->ram_scene[FSM_EQ_RAM1] = FSM_SCENE_UNKNOW; + fsm_dev->own_scene = FSM_SCENE_UNKNOW; + fsm_dev->cur_scene = FSM_SCENE_UNKNOW; + fsm_dev->version = id; + fsm_dev->acc_count = 0; + + fsm_list_init(fsm_dev); + g_fsm_config.dev_count++; + fsm_mutex_unlock(); + + return 0; +} + +void fsm_remove(fsm_dev_t *fsm_dev) +{ + if (!fsm_dev) { + return; + } + fsm_mutex_lock(); + list_del(&fsm_dev->list); + if (fsm_dev->tdata) { + fsm_free_mem((void **)&fsm_dev->tdata); + } + g_fsm_config.dev_count--; + fsm_mutex_unlock(); +} + +/** + * try init deivce without lock + */ +static int fsm_try_init(void) +{ + fsm_config_t *cfg = fsm_get_config(); + struct preset_file *preset; + fsm_dev_t *fsm_dev; + uint16_t next_scene; + int ret; + + if (cfg->dev_count <= 0) { + pr_err("no found device"); + return -EINVAL; + } + cfg->force_scene = cfg->force_init; + ret = fsm_firmware_init_sync(cfg->fw_name); + preset = fsm_get_presets(); + if (preset == NULL) { + pr_err("invalid firmware, ret:%d", ret); + return -EINVAL; + } + next_scene = cfg->next_scene; + cfg->next_scene = FSM_SCENE_MUSIC; // init all devices + list_for_each_entry(fsm_dev, &fsm_dev_list, list) { + fsm_stub_dev_init(fsm_dev); + } + cfg->next_scene = next_scene; // recovery scene + cfg->force_scene = cfg->force_init = false; + + return 0; +} + +void fsm_init(void) +{ + int ret; + + ret = fsm_hal_open(); + if (ret) { + pr_err("hal open fail:%d", ret); + return; + } + fsm_mutex_lock(); + pr_info("version: %s", FSM_CODE_VERSION); + pr_info("branch : %s", FSM_GIT_BRANCH); + pr_info("date : %s", FSM_CODE_DATE); + ret = fsm_try_init(); + if (ret) { // no device or firmware + pr_err("init failed: %d", ret); + } + g_dev_inited = 1; + pr_debug("done"); + fsm_mutex_unlock(); +} + +static int fsm_get_scene_index(uint16_t scene) +{ + int index = 0; + + while (scene) { + scene = (scene >> 1); + if (scene == 0) { + break; + } + index++; + } + + return index; +} + +int fsm_scene_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + fsm_config_t *cfg = fsm_get_config(); + int scene_index; + + if (!cfg) { + ucontrol->value.integer.value[0] = -1; + return 0; + } + scene_index = fsm_get_scene_index(cfg->next_scene); + pr_info("scene: %04X, BIT(%d)", cfg->next_scene, scene_index); + ucontrol->value.integer.value[0] = scene_index; + + return 0; +} + +int fsm_scene_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int next_scene = ucontrol->value.integer.value[0]; + + pr_info("next_scene: %d", next_scene); + fsm_set_scene(next_scene); + + return 0; +} + +int fsm_amp_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int enable = atomic_read(&fsm_amp_switch); + + ucontrol->value.integer.value[0] = enable; + pr_info("switch: %s", enable ? "On" : "Off"); + + return 0; +} + +int fsm_amp_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int enable = ucontrol->value.integer.value[0]; + + pr_info("switch: %s", enable ? "On" : "Off"); + atomic_set(&fsm_amp_switch, enable); + if (enable) { + fsm_speaker_onn(); + } else { + fsm_speaker_off(); + } + + return 0; +} + +int fsm_init_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + fsm_init(); + return 0; +} + +int fsm_init_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + ucontrol->value.integer.value[0] = g_dev_inited; + pr_info("dev %s", (g_dev_inited == 1) ? "initilized" : "init failed"); + + return 0; +} + +int fsm_rotation_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + fsm_config_t *cfg = fsm_get_config(); + if (!cfg) { + ucontrol->value.integer.value[0] = -1; + return 0; + } + + ucontrol->value.integer.value[0] = cfg->next_angle; + + return 0; +} + +int fsm_rotation_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + fsm_config_t *cfg = fsm_get_config(); + int angle = ucontrol->value.integer.value[0]; + + if(!cfg){ + pr_err("cfg is null"); + return 0; + } + + pr_info("angle: %d", angle); + + if(cfg->next_angle == angle) + return 0; + + if(q6fsm_set_rotation(angle) == 0){ + cfg->next_angle = angle; + } + + return 0; +} + +int fsm_adsp_enable_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int enable = atomic_read(&fsm_adsp_enable); + + ucontrol->value.integer.value[0] = enable; + pr_info("get: %s", (enable) ? "Enable" : "Disable"); + + return 0; +} + +int fsm_adsp_enable_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int enable = ucontrol->value.integer.value[0]; + + pr_info("switch: %s", enable ? "Enable" : "Disable"); + + if(q6fsm_set_rx_enable(enable) == 0){ + atomic_set(&fsm_adsp_enable, enable); + } + + return 0; +} + +const struct snd_kcontrol_new fsm_snd_controls[] = +{ + SOC_SINGLE_EXT("FSM_Scene", SND_SOC_NOPM, 0, FSM_SCENE_MAX, 0, + fsm_scene_get, fsm_scene_put), + SOC_SINGLE_EXT("FSM_Amp_Switch", SND_SOC_NOPM, 0, 1, 0, + fsm_amp_switch_get, fsm_amp_switch_put), + SOC_SINGLE_EXT("FSM_Init", SND_SOC_NOPM, 0, 1, 0, + fsm_init_get, fsm_init_put), + SOC_SINGLE_EXT("FSM_Rotation", SND_SOC_NOPM, 0, 360, 0, + fsm_rotation_get, fsm_rotation_put), + SOC_SINGLE_EXT("FSM_ADSP_Enable", SND_SOC_NOPM, 0, 1, 0, + fsm_adsp_enable_get, fsm_adsp_enable_put), +}; + +void fsm_add_codec_controls(struct snd_soc_codec *codec) +{ + snd_soc_add_codec_controls(codec, fsm_snd_controls, + ARRAY_SIZE(fsm_snd_controls)); +} +EXPORT_SYMBOL_GPL(fsm_add_codec_controls); + +void fsm_add_card_controls(struct snd_soc_card *card) +{ + snd_soc_add_card_controls(card, fsm_snd_controls, + ARRAY_SIZE(fsm_snd_controls)); +} +EXPORT_SYMBOL_GPL(fsm_add_card_controls); + +static int fsm_check_scene_status() +{ + fsm_config_t *cfg = fsm_get_config(); + if(cfg->speaker_on && cfg->next_scene == FSM_SCENE_MUSIC){ + return 0; + } + + return -1; +} + +void fsm_speaker_onn(void) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev = NULL; + int ret; + + pr_info("scene: %04X", cfg->next_scene); + fsm_mutex_lock(); + cfg->stream_muted = false; + ret = fsm_try_init(); + if (ret) { // no device or firmware + pr_err("init failed: %d", ret); + fsm_mutex_unlock(); + return; + } + fsm_list_func(fsm_dev, fsm_stub_switch_preset); + fsm_list_func(fsm_dev, fsm_stub_start_up); + cfg->cur_angle = cfg->next_angle; + cfg->speaker_on = true; + + if(fsm_check_scene_status() == 0) { + q6fsm_fsm_check_scene_status(true); + } else { + q6fsm_fsm_check_scene_status(false); + } + + pr_debug("done"); + fsm_mutex_unlock(); +} +EXPORT_SYMBOL_GPL(fsm_speaker_onn); + +void fsm_speaker_off(void) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev = NULL; + int ret; + + pr_info("scene: %04X", cfg->next_scene); + fsm_mutex_lock(); + cfg->stream_muted = true; + ret = fsm_try_init(); + if (ret) { + pr_err("try init failed: %d", ret); + fsm_mutex_unlock(); + return; + } + fsm_list_func(fsm_dev, fsm_stub_shut_down); + cfg->speaker_on = false; + cfg->next_angle = 0; + atomic_set(&fsm_adsp_enable, 0); + q6fsm_fsm_check_scene_status(false); + fsm_mutex_unlock(); + fsm_set_scene(0); // scene music + pr_debug("done"); +} +EXPORT_SYMBOL_GPL(fsm_speaker_off); + +void fsm_stereo_rotation(int next_angle) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev = NULL; + + if (next_angle >= 360) { // angle range: [0 ~ 359] + next_angle %= 360; + } + if (cfg->cur_angle == next_angle) { + return; + } + fsm_mutex_lock(); + pr_info("scene:%04X, angle:%d", cfg->next_scene, next_angle); + cfg->next_angle = next_angle; + if (cfg->stream_muted || !(cfg->next_scene & FSM_SCENE_MUSIC)) { + fsm_mutex_unlock(); + return; + } + fsm_list_func_arg(fsm_dev, fsm_swap_channel, cfg->next_angle); + cfg->cur_angle = cfg->next_angle; + fsm_mutex_unlock(); +} + +void fsm_dump(void) +{ + fsm_dev_t *fsm_dev = NULL; + fsm_mutex_lock(); + fsm_list_func(fsm_dev, fsm_reg_dump); + fsm_mutex_unlock(); +} + +void fsm_deinit(void) +{ + fsm_dev_t *fsm_dev = NULL; + + fsm_mutex_lock(); + if (fsm_dev_count() <= 0) { + fsm_mutex_unlock(); + return; + } + fsm_list_func(fsm_dev, fsm_stub_dev_deinit); + fsm_firmware_deinit(); + fsm_mutex_unlock(); + fsm_hal_close(); +} diff --git a/techpack/audio/asoc/codecs/fs1815/fsm_firmware.c b/techpack/audio/asoc/codecs/fs1815/fsm_firmware.c new file mode 100644 index 000000000000..b33be9d25370 --- /dev/null +++ b/techpack/audio/asoc/codecs/fs1815/fsm_firmware.c @@ -0,0 +1,136 @@ +/** + * Copyright (C) Fourier Semiconductor Inc. 2016-2020. All rights reserved. + * 2019-02-22 File created. + */ + +#if defined(CONFIG_FSM_FIRMWARE) +#include "fsm_public.h" +#include +#include + +static int g_fsm_fw_init = 0; + +#ifdef FSM_UNUSED_CODE +static void *fsm_devm_kzalloc(struct device *dev, void *buf, size_t size) +{ + char *devm_buf = devm_kzalloc(dev, size, GFP_KERNEL); + + if (!devm_buf) { + return devm_buf; + } else { + memcpy(devm_buf, buf, size); + } + + return devm_buf; +} + +static void fsm_devm_kfree(struct device *dev, void **buf) +{ + if (*buf) { + devm_kfree(dev, *buf); + *buf = NULL; + } +} +#endif + +static void fsm_firmware_inited(const struct firmware *cont, void *context) +{ + struct device *dev = (struct device *)context; + int ret; + + if (dev == NULL || cont == NULL) { + pr_err("bad parameter"); + return; + } + + pr_info("size: %zu", cont->size); + fsm_mutex_lock(); + ret = fsm_parse_preset(cont->data, (uint32_t)cont->size); + fsm_mutex_unlock(); + release_firmware(cont); + if (ret) { + pr_err("parse firmware fail: %d", ret); + g_fsm_fw_init = 0; + } +} + +int fsm_firmware_init(char *fw_name) +{ + struct device *dev; + int ret; + + if (fw_name == NULL) { + pr_err("invalid firmware name"); + return -EINVAL; + } + if (fsm_get_presets() || g_fsm_fw_init) { + return MODULE_INITED; + } + dev = fsm_get_pdev(); + if (dev == NULL) { + pr_err("invalid device"); + return -EINVAL; + } + + pr_info("loading %s in nowait mode", fw_name); + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + fw_name, dev, GFP_KERNEL, + dev, fsm_firmware_inited); + if (ret) { + pr_err("request %s failed: %d\n", fw_name, ret); + return ret; + } + g_fsm_fw_init = 1; + + return ret; +} + +int fsm_firmware_init_sync(char *fw_name) +{ + fsm_config_t *cfg = fsm_get_config(); + const struct firmware *fw_cont; + struct device *dev; + int ret; + + if (fw_name == NULL) { + pr_err("invalid firmware name"); + return -EINVAL; + } + if (cfg->force_fw) { + pr_info("force loading"); + cfg->force_fw = false; + fsm_set_presets(NULL); + } + if (fsm_get_presets()) { + return MODULE_INITED; + } + dev = fsm_get_pdev(); + if (dev == NULL) { + pr_err("invalid device"); + return -EINVAL; + } + + g_fsm_fw_init = 0; + pr_info("loading %s in sync mode", fw_name); + ret = request_firmware(&fw_cont, fw_name, dev); + if (ret) { + pr_err("request %s failed: %d\n", fw_name, ret); + return ret; + } + ret = fsm_parse_preset(fw_cont->data, (uint32_t)fw_cont->size); + release_firmware(fw_cont); + if (ret) { + pr_err("parse firmware fail: %d", ret); + return ret; + } + g_fsm_fw_init = 1; + + return ret; +} + +void fsm_firmware_deinit(void) +{ + fsm_set_presets(NULL); + g_fsm_fw_init = 0; +} +#endif diff --git a/techpack/audio/asoc/codecs/fs1815/fsm_i2c.c b/techpack/audio/asoc/codecs/fs1815/fsm_i2c.c new file mode 100644 index 000000000000..00004697dd76 --- /dev/null +++ b/techpack/audio/asoc/codecs/fs1815/fsm_i2c.c @@ -0,0 +1,673 @@ +/* + * Copyright (C) Fourier Semiconductor Inc. 2016-2020. All rights reserved. + */ +#include "fsm_public.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#include +#endif +#if defined(CONFIG_REGULATOR) +#include +static struct regulator *g_fsm_vdd = NULL; +#endif + +static DEFINE_MUTEX(g_fsm_mutex); +static struct device *g_fsm_pdev = NULL; + +/* customize configrature */ +#include "fsm_firmware.c" +#include "fsm_misc.c" +//#include "fsm_codec.c" + +void fsm_mutex_lock() +{ + mutex_lock(&g_fsm_mutex); +} + +void fsm_mutex_unlock() +{ + mutex_unlock(&g_fsm_mutex); +} + +int fsm_i2c_reg_read(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t *pVal) +{ + struct i2c_msg msgs[2]; + uint8_t retries = 0; + uint8_t buffer[2]; + int ret; + + if (!fsm_dev || !fsm_dev->i2c || !pVal) { + return -EINVAL; + } + + // write register address. + msgs[0].addr = fsm_dev->i2c->addr; + msgs[0].flags = 0; + msgs[0].len = 1; + msgs[0].buf = ® + // read register buffer. + msgs[1].addr = fsm_dev->i2c->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 2; + msgs[1].buf = &buffer[0]; + + do { + mutex_lock(&fsm_dev->i2c_lock); + ret = i2c_transfer(fsm_dev->i2c->adapter, &msgs[0], ARRAY_SIZE(msgs)); + mutex_unlock(&fsm_dev->i2c_lock); + if (ret != ARRAY_SIZE(msgs)) { + fsm_delay_ms(5); + retries++; + } + } while (ret != ARRAY_SIZE(msgs) && retries < FSM_I2C_RETRY); + + if (ret != ARRAY_SIZE(msgs)) { + pr_err("read %02x transfer error: %d", reg, ret); + return -EIO; + } + + *pVal = ((buffer[0] << 8) | buffer[1]); + + return 0; +} + +int fsm_i2c_reg_write(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t val) +{ + struct i2c_msg msgs[1]; + uint8_t retries = 0; + uint8_t buffer[3]; + int ret; + + if (!fsm_dev || !fsm_dev->i2c) { + return -EINVAL; + } + + buffer[0] = reg; + buffer[1] = (val >> 8) & 0x00ff; + buffer[2] = val & 0x00ff; + msgs[0].addr = fsm_dev->i2c->addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(buffer); + msgs[0].buf = &buffer[0]; + + do { + mutex_lock(&fsm_dev->i2c_lock); + ret = i2c_transfer(fsm_dev->i2c->adapter, &msgs[0], ARRAY_SIZE(msgs)); + mutex_unlock(&fsm_dev->i2c_lock); + if (ret != ARRAY_SIZE(msgs)) { + fsm_delay_ms(5); + retries++; + } + } while (ret != ARRAY_SIZE(msgs) && retries < FSM_I2C_RETRY); + + if (ret != ARRAY_SIZE(msgs)) { + pr_err("write %02x transfer error: %d", reg, ret); + return -EIO; + } + + return 0; +} + +int fsm_i2c_bulkwrite(fsm_dev_t *fsm_dev, uint8_t reg, + uint8_t *data, int len) +{ + uint8_t retries = 0; + uint8_t *buf; + int size; + int ret; + + if (!fsm_dev || !fsm_dev->i2c || !data) { + return -EINVAL; + } + + size = sizeof(uint8_t) + len; + buf = (uint8_t *)fsm_alloc_mem(size); + if (!buf) { + pr_err("alloc memery failed"); + return -ENOMEM; + } + + buf[0] = reg; + memcpy(&buf[1], data, len); + do { + mutex_lock(&fsm_dev->i2c_lock); + ret = i2c_master_send(fsm_dev->i2c, buf, size); + mutex_unlock(&fsm_dev->i2c_lock); + if (ret < 0) { + fsm_delay_ms(5); + retries++; + } else if (ret == size) { + break; + } + } while (ret != size && retries < FSM_I2C_RETRY); + + fsm_free_mem((void **)&buf); + + if (ret != size) { + pr_err("write %02x transfer error: %d", reg, ret); + return -EIO; + } + + return 0; +} + +bool fsm_set_pdev(struct device *dev) +{ + if (g_fsm_pdev == NULL || dev == NULL) { + g_fsm_pdev = dev; + // pr_debug("dev_name: %s", dev_name(dev)); + return true; + } + return false; // already got device +} + +struct device *fsm_get_pdev(void) +{ + return g_fsm_pdev; +} + +int fsm_vddd_on(struct device *dev) +{ + fsm_config_t *cfg = fsm_get_config(); + int ret = 0; + + if (!cfg || cfg->vddd_on) { + return 0; + } +#if defined(CONFIG_REGULATOR) + g_fsm_vdd = regulator_get(dev, "fsm_vddd"); + if (IS_ERR(g_fsm_vdd) != 0) { + pr_err("error getting fsm_vddd regulator"); + ret = PTR_ERR(g_fsm_vdd); + g_fsm_vdd = NULL; + return ret; + } + pr_info("enable regulator"); + regulator_set_voltage(g_fsm_vdd, 1800000, 1800000); + ret = regulator_enable(g_fsm_vdd); + if (ret < 0) { + pr_err("enabling fsm_vddd failed: %d", ret); + } +#endif + cfg->vddd_on = 1; + fsm_delay_ms(10); + + return ret; +} + +void fsm_vddd_off(void) +{ + fsm_config_t *cfg = fsm_get_config(); + + if (!cfg || !cfg->vddd_on || cfg->dev_count > 0) { + return; + } +#if defined(CONFIG_REGULATOR) + if (g_fsm_vdd) { + pr_info("disable regulator"); + regulator_disable(g_fsm_vdd); + regulator_put(g_fsm_vdd); + g_fsm_vdd = NULL; + } +#endif + cfg->vddd_on = 0; +} + +int fsm_get_amb_tempr(void) +{ + union power_supply_propval psp = { 0 }; + struct power_supply *psy; + int tempr = FSM_DFT_AMB_TEMPR; + int vbat = FSM_DFT_AMB_VBAT; + + psy = power_supply_get_by_name("battery"); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)) + if (psy && psy->get_property) { + // battery temperatrue + psy->get_property(psy, POWER_SUPPLY_PROP_TEMP, &psp); + tempr = DIV_ROUND_CLOSEST(psp.intval, 10); + psy->get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &psp); + vbat = DIV_ROUND_CLOSEST(psp.intval, 1000); + } +#else + if (psy && psy->desc && psy->desc->get_property) { + // battery temperatrue + psy->desc->get_property(psy, POWER_SUPPLY_PROP_TEMP, &psp); + tempr = DIV_ROUND_CLOSEST(psp.intval, 10); + psy->desc->get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &psp); + vbat = DIV_ROUND_CLOSEST(psp.intval, 1000); + } +#endif + pr_info("vbat:%d, tempr:%d", vbat, tempr); + + return tempr; +} + +void *fsm_devm_kstrdup(struct device *dev, void *buf, size_t size) +{ + char *devm_buf = devm_kzalloc(dev, size + 1, GFP_KERNEL); + + if (!devm_buf) { + return devm_buf; + } + memcpy(devm_buf, buf, size); + + return devm_buf; +} + +static int fsm_set_irq(fsm_dev_t *fsm_dev, bool enable) +{ + if (!fsm_dev || fsm_dev->irq_id <= 0) { + return -EINVAL; + } + if (enable) + enable_irq(fsm_dev->irq_id); + else + disable_irq(fsm_dev->irq_id); + + return 0; +} + +int fsm_set_monitor(fsm_dev_t *fsm_dev, bool enable) +{ + fsm_config_t *cfg = fsm_get_config(); + + if (!cfg || !fsm_dev || !fsm_dev->fsm_wq) { + return -EINVAL; + } + if (!cfg->use_monitor) { + return 0; + } + if (fsm_dev->use_irq) { + return fsm_set_irq(fsm_dev, enable); + } + if (enable) { + queue_delayed_work(fsm_dev->fsm_wq, + &fsm_dev->monitor_work, 5*HZ); + } + else { + if (delayed_work_pending(&fsm_dev->monitor_work)) { + cancel_delayed_work_sync(&fsm_dev->monitor_work); + } + } + + return 0; +} + +static int fsm_ext_reset(fsm_dev_t *fsm_dev) +{ + fsm_config_t *cfg = fsm_get_config(); + uint16_t id = 0; + int addr; + int ret; + + if (!cfg || !fsm_dev) { + return -EINVAL; + } + if (cfg->reset_chip) { + return 0; + } + if (gpio_is_valid(fsm_dev->rst_gpio)) { + gpio_set_value(fsm_dev->rst_gpio, 0); + fsm_delay_ms(10); // mdelay + gpio_set_value(fsm_dev->rst_gpio, 1); + fsm_delay_ms(1); // mdelay + // TODO: for sharing reset pin in multi-pa application + // cfg->reset_chip = true; + } + fsm_mutex_lock(); + /* soft reset */ + addr = fsm_dev->i2c->addr; + ret = fsm_reg_read(fsm_dev, 0x01, &id); + if (ret && addr != 0x34) { + fsm_dev->i2c->addr = 0x34; + ret = fsm_reg_read(fsm_dev, 0x01, &id); + } + if (!ret && LOW8(id) == FS1815_DEV_ID) { + fsm_reg_write(fsm_dev, 0x10, 0x0002); + fsm_delay_ms(15); + } else { + ret = -ENODEV; + } + fsm_dev->i2c->addr = addr; + fsm_mutex_unlock(); + + return 0; +} + +static irqreturn_t fsm_irq_hander(int irq, void *data) +{ + fsm_dev_t *fsm_dev = data; + + queue_delayed_work(fsm_dev->fsm_wq, &fsm_dev->interrupt_work, 0); + + return IRQ_HANDLED; +} + +static void fsm_work_monitor(struct work_struct *work) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev; + //int ret; + + fsm_dev = container_of(work, struct fsm_dev, monitor_work.work); + if (!cfg || cfg->skip_monitor || !fsm_dev) { + return; + } + fsm_mutex_lock(); + //ret = fsm_dev_recover(fsm_dev); + fsm_mutex_unlock(); + if (fsm_dev->rec_count >= 5) { // 5 time max + pr_addr(warning, "recover max time, stop it"); + return; + } + /* reschedule */ + queue_delayed_work(fsm_dev->fsm_wq, &fsm_dev->monitor_work, + 5*HZ); + +} + +static void fsm_work_interrupt(struct work_struct *work) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev; + //int ret; + + fsm_dev = container_of(work, struct fsm_dev, interrupt_work.work); + if (!cfg || cfg->skip_monitor || !fsm_dev) { + return; + } + fsm_mutex_lock(); + //ret = fsm_dev_recover(fsm_dev); + //fsm_get_spkr_tempr(fsm_dev); + + fsm_mutex_unlock(); +} + +static int fsm_request_irq(fsm_dev_t *fsm_dev) +{ + struct i2c_client *i2c; + int irq_flags; + int ret; + + if (fsm_dev == NULL || fsm_dev->i2c == NULL) { + return -EINVAL; + } + fsm_dev->irq_id = -1; + if (!fsm_dev->use_irq || !gpio_is_valid(fsm_dev->irq_gpio)) { + pr_addr(info, "skip to request irq"); + return 0; + } + i2c = fsm_dev->i2c; + /* register irq handler */ + fsm_dev->irq_id = gpio_to_irq(fsm_dev->irq_gpio); + if (fsm_dev->irq_id <= 0) { + dev_err(&i2c->dev, "invalid irq %d\n", fsm_dev->irq_id); + return -EINVAL; + } + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&i2c->dev, fsm_dev->irq_id, + NULL, fsm_irq_hander, irq_flags, "fs16xx", fsm_dev); + if (ret) { + dev_err(&i2c->dev, "failed to request IRQ %d: %d\n", + fsm_dev->irq_id, ret); + return ret; + } + disable_irq(fsm_dev->irq_id); + + return 0; +} + +#ifdef CONFIG_OF +static int fsm_parse_dts(struct i2c_client *i2c, fsm_dev_t *fsm_dev) +{ + struct device_node *np = i2c->dev.of_node; + uint8_t dummy_addr; + char *str_name; + int ret; + + if (fsm_dev == NULL || np == NULL) { + return -EINVAL; + } + + str_name = devm_kzalloc(&i2c->dev, 32, GFP_KERNEL); + fsm_dev->rst_gpio = of_get_named_gpio(np, "fsm,rst-gpio", 0); + if (gpio_is_valid(fsm_dev->rst_gpio)) { + snprintf(str_name, 32, "FS1815_SDZ_%02X", i2c->addr); + ret = devm_gpio_request_one(&i2c->dev, fsm_dev->rst_gpio, + GPIOF_OUT_INIT_LOW, str_name); + if (ret) + return ret; + } + + ret = of_property_read_u8(np, "fsm,dummy-addr", &dummy_addr); + if (ret) + dummy_addr = 0; + + fsm_dev->addr = dummy_addr; + + return 0; +} + +static struct of_device_id fsm_match_tbl[] = +{ + { .compatible = "foursemi,fs16xx" }, + { .compatible = "foursemi,fs1815" }, + {}, +}; +MODULE_DEVICE_TABLE(of, fsm_match_tbl); +#endif + +static int fsm_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + fsm_config_t *cfg = fsm_get_config(); + fsm_dev_t *fsm_dev; + int ret; + + pr_debug("enter"); + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check I2C_FUNC_I2C failed"); + return -EIO; + } + + fsm_dev = devm_kzalloc(&i2c->dev, sizeof(struct fsm_dev), GFP_KERNEL); + if (fsm_dev == NULL) { + dev_err(&i2c->dev, "alloc memory fialed"); + return -ENOMEM; + } + + memset(fsm_dev, 0, sizeof(struct fsm_dev)); + mutex_init(&fsm_dev->i2c_lock); + fsm_dev->i2c = i2c; + +#ifdef CONFIG_OF + ret = fsm_parse_dts(i2c, fsm_dev); + if (ret) { + dev_err(&i2c->dev, "failed to parse DTS node"); + return ret; + } +#endif +#if defined(CONFIG_FSM_REGMAP) + fsm_dev->regmap = fsm_regmap_i2c_init(i2c); + if (fsm_dev->regmap == NULL) { + devm_kfree(&i2c->dev, fsm_dev); + dev_err(&i2c->dev, "regmap init fialed"); + return -EINVAL; + } +#endif + + fsm_vddd_on(&i2c->dev); + fsm_ext_reset(fsm_dev); + ret = fsm_probe(fsm_dev, i2c->addr); + if (ret) { + dev_err(&i2c->dev, "detect device failed"); +#if defined(CONFIG_FSM_REGMAP) + fsm_regmap_i2c_deinit(fsm_dev->regmap); +#endif + gpio_set_value(fsm_dev->rst_gpio, 0); + devm_gpio_free(&i2c->dev, fsm_dev->rst_gpio); + devm_kfree(&i2c->dev, fsm_dev); + return ret; + } + fsm_dev->id = cfg->dev_count - 1; + i2c_set_clientdata(i2c, fsm_dev); + pr_addr(info, "index:%d", fsm_dev->id); + fsm_dev->fsm_wq = create_singlethread_workqueue("fs16xx"); + INIT_DELAYED_WORK(&fsm_dev->monitor_work, fsm_work_monitor); + INIT_DELAYED_WORK(&fsm_dev->interrupt_work, fsm_work_interrupt); + fsm_request_irq(fsm_dev); + + if(fsm_dev->id == 0) { + // reigster only in the first device +#if !defined(CONFIG_FSM_CODEC) + fsm_set_pdev(&i2c->dev); +#endif + fsm_misc_init(); + fsm_sysfs_init(&i2c->dev); + fsm_codec_register(&i2c->dev, fsm_dev->id); + } + + dev_info(&i2c->dev, "i2c probe completed"); + + return 0; +} + +static int fsm_i2c_remove(struct i2c_client *i2c) +{ + fsm_dev_t *fsm_dev = i2c_get_clientdata(i2c); + + pr_debug("enter"); + if (fsm_dev == NULL) { + pr_err("bad parameter"); + return -EINVAL; + } + if (fsm_dev->fsm_wq) { + cancel_delayed_work_sync(&fsm_dev->interrupt_work); + cancel_delayed_work_sync(&fsm_dev->monitor_work); + destroy_workqueue(fsm_dev->fsm_wq); + } +#if defined(CONFIG_FSM_REGMAP) + fsm_regmap_i2c_deinit(fsm_dev->regmap); +#endif + if (fsm_dev->id == 0) { + fsm_codec_unregister(&i2c->dev); + fsm_sysfs_deinit(&i2c->dev); + fsm_misc_deinit(); + fsm_set_pdev(NULL); + } + + fsm_remove(fsm_dev); + fsm_vddd_off(); + if (gpio_is_valid(fsm_dev->irq_gpio)) { + devm_gpio_free(&i2c->dev, fsm_dev->irq_gpio); + } + if (gpio_is_valid(fsm_dev->rst_gpio)) { + devm_gpio_free(&i2c->dev, fsm_dev->rst_gpio); + } + devm_kfree(&i2c->dev, fsm_dev); + dev_info(&i2c->dev, "i2c removed"); + + return 0; +} + +static void fsm_i2c_shutdown(struct i2c_client *i2c) +{ + fsm_dev_t *fsm_dev = i2c_get_clientdata(i2c); + + pr_debug("enter"); + if (fsm_dev == NULL) { + pr_err("bad parameter"); + return; + } + + fsm_stub_shut_down(fsm_dev); + if (gpio_is_valid(fsm_dev->rst_gpio)) + gpio_set_value(fsm_dev->rst_gpio, 0); + + dev_info(&i2c->dev, "i2c shutdowned"); +} + +static const struct i2c_device_id fsm_i2c_id[] = +{ + { "fs16xx", 0 }, + { "fs1815", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, fsm_i2c_id); + +static struct i2c_driver fsm_i2c_driver = +{ + .driver = { + .name = FSM_DRV_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(fsm_match_tbl), +#endif + }, + .probe = fsm_i2c_probe, + .remove = fsm_i2c_remove, + .shutdown = fsm_i2c_shutdown, + .id_table = fsm_i2c_id, +}; + +int exfsm_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + return fsm_i2c_probe(i2c, id); +} +EXPORT_SYMBOL(exfsm_i2c_probe); + +int exfsm_i2c_remove(struct i2c_client *i2c) +{ + return fsm_i2c_remove(i2c); +} +EXPORT_SYMBOL(exfsm_i2c_remove); + +int fsm_i2c_init(void) +{ + return i2c_add_driver(&fsm_i2c_driver); +} + +void fsm_i2c_exit(void) +{ + i2c_del_driver(&fsm_i2c_driver); +} + +static int __init fsm_mod_init(void) +{ + int ret; + + ret = fsm_i2c_init(); + if (ret) { + pr_err("init fail: %d", ret); + return ret; + } + + return 0; +} + +static void __exit fsm_mod_exit(void) +{ + fsm_i2c_exit(); +} + +//module_i2c_driver(fsm_i2c_driver); +module_init(fsm_mod_init); +module_exit(fsm_mod_exit); + +MODULE_AUTHOR("FourSemi SW "); +MODULE_DESCRIPTION("FourSemi Smart PA Driver"); +MODULE_VERSION(FSM_CODE_VERSION); +MODULE_ALIAS("foursemi:"FSM_DRV_NAME); +MODULE_LICENSE("GPL"); + diff --git a/techpack/audio/asoc/codecs/fs1815/fsm_misc.c b/techpack/audio/asoc/codecs/fs1815/fsm_misc.c new file mode 100644 index 000000000000..196207f400b8 --- /dev/null +++ b/techpack/audio/asoc/codecs/fs1815/fsm_misc.c @@ -0,0 +1,470 @@ +/** + * Copyright (C) Fourier Semiconductor Inc. 2016-2020. All rights reserved. + * 2018-10-16 File created. + */ + +#if defined(CONFIG_FSM_MISC) +#include "fsm_public.h" +#ifdef CONFIG_FSM_NONDSP +#include "fsm_q6afe.h" +#endif +#include +#include +#include +#include +#include + +#define FSM_I2C_MAX_LEN (8192) + +/* + * misc driver for foursemi devices. + */ + +struct fsm_misc { + struct fsm_dev *fsm_dev[FSM_DEV_MAX]; + uint8_t addr; + uint8_t index; +}; + +static int g_misc_opened; + +static int fsm_misc_check_params(unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + if (cmd == FSM_IOC_SET_SLAVE) { + return 0; + } + if (_IOC_TYPE(cmd) != FSM_IOC_MAGIC || _IOC_NR(cmd) >= FSM_IOC_MAXNR) { + return -ENOTTY; + } + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + if (_IOC_DIR(cmd) & _IOC_READ) { + ret = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); + } else if (_IOC_DIR(cmd) & _IOC_WRITE) { + ret = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); + } +#else + ret = !access_ok((void __user *)arg, _IOC_SIZE(cmd)); +#endif + + return (ret ? -EFAULT : 0); +} + +static int fsm_misc_open(struct inode *inode, struct file *filp) +{ + struct fsm_misc *fsm_misc; + + pr_debug("enter"); + if (xchg(&g_misc_opened, 1)) { + pr_warning("device busy now"); + return -EBUSY; + } + fsm_misc = fsm_alloc_mem(sizeof(struct fsm_misc)); + if (fsm_misc == NULL) { + pr_err("allocat memory fail"); + return -EINVAL; + } + memset(fsm_misc, 0, sizeof(struct fsm_misc)); + filp->private_data = fsm_misc; + + return 0; +} + +static int fsm_misc_release(struct inode *inode, struct file *filp) +{ + struct fsm_misc *fsm_misc; + + pr_debug("enter"); + if (!filp) { + return -EINVAL; + } + fsm_misc = filp->private_data; + g_misc_opened = 0; + fsm_free_mem((void **)&fsm_misc); + + return 0; +} + +static int fsm_misc_set_slave(struct fsm_misc *fsm_misc, uint8_t slave) +{ + int index; + + if (fsm_misc == NULL) { + return -EINVAL; + } + if (slave < FSM_ADDR_BASE || slave >= FSM_ADDR_BASE + FSM_DEV_MAX) { + pr_err("invalid address:%02X", slave); + return -EINVAL; + } + index = slave - FSM_ADDR_BASE; + if (fsm_misc->fsm_dev[index] == 0) { + fsm_misc->fsm_dev[index] = fsm_get_fsm_dev(slave); + if (fsm_misc->fsm_dev[index] == NULL) { + pr_debug("not found device:%02X", slave); + fsm_misc->addr = 0; + fsm_misc->index = 0; + return -EINVAL; + } + } + fsm_misc->index = index; + fsm_misc->addr = slave; + + return 0; +} + +static int fsm_misc_cmd_with_args(unsigned int cmd, uint8_t addr, + struct fsm_misc_args *args) +{ + fsm_config_t *cfg = fsm_get_config(); + + if (!cfg || !args) { + pr_err("bad parameters"); + return -EINVAL; + } + switch (cmd) { + case FSM_IOC_SET_SRATE: + fsm_set_i2s_clocks(args->srate, args->bclk); + break; + case FSM_IOC_SET_SCENE: + fsm_set_scene(args->scene); + break; + case FSM_IOC_INIT: + cfg->force_init = args->force_init; + fsm_init(); + cfg->force_init = 0; + break; + case FSM_IOC_CALIBRATE: + break; + case FSM_IOC_F0_TEST: + break; + default: + pr_err("invalid cmd: %X", cmd); + return -EINVAL; + } + + return 0; +} + +#ifdef CONFIG_FSM_NONDSP +int fsm_misc_send_apr(void __user *buf) +{ + uint32_t header[FSM_AFE_HEADER_LEN]; + uint32_t *ptr_data; + struct fsm_afe afe; + int buf_size; + void *buffer; + int ret; + + if (!buf) { + pr_err("bad parameter"); + return -EINVAL; + } + ret = copy_from_user(header, (void *)buf, sizeof(header)); + if (ret) { + pr_err("Copy from user failed! buf = 0x%pK", buf); + return ret; + } + /* buffer: + * [0]: buf_size + * [1]: cmd_size + * [2]: module_id + * [3]: param_id + * [4]: opcode + * [...]: data + */ + buf_size = header[0]; + if (buf_size <= 0) { + pr_err("Invalid buffer size = %d", buf_size); + return -EINVAL; + } + buffer = fsm_alloc_mem(buf_size); + afe.module_id = header[2]; + if (afe.module_id == AFE_MODULE_ID_FSADSP_TX) { + afe.port_id = fsm_afe_get_tx_port(); + } else { + afe.port_id = fsm_afe_get_rx_port(); + } + afe.param_id = header[3]; + ptr_data = (uint32_t *)buffer; + if (header[4]) { // op_set = 1 + ret = copy_from_user(buffer, (void *)buf, buf_size); + if (ret) { + pr_err("copy from user fail:%d", ret); + goto exit; + } + afe.op_set = true; + ret = fsm_afe_send_apr(&afe, &ptr_data[5], + (buf_size - FSM_AFE_HEADER_LEN * sizeof(uint32_t))); + if (ret) { + pr_err("set params fail:%d", ret); + goto exit; + } + } else { + afe.op_set = false; + ret = fsm_afe_send_apr(&afe, &ptr_data[5], + (buf_size - FSM_AFE_HEADER_LEN * sizeof(uint32_t))); + if (ret) { + pr_err("get params fail:%d", ret); + goto exit; + } + // set ptr_data[0] = total size + ptr_data[0] = afe.param_size + FSM_AFE_HEADER_LEN * sizeof(uint32_t); + ret = copy_to_user((void __user *)buf, buffer, ptr_data[0]); + if (ret) { + pr_err("copy to user fail:%d", ret); + goto exit; + } + } +exit: + fsm_free_mem((void **)&buffer); + + return ret; +} +#endif + +static long fsm_misc_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct fsm_misc_args misc_args; + struct fsm_misc *fsm_misc; + int count; + int ret; + + if (filp == NULL || filp->private_data == NULL) { + pr_err("invalid parameter"); + return -EINVAL; + } + fsm_misc = filp->private_data; + ret = fsm_misc_check_params(cmd, arg); + if (ret) { + pr_err("invalid params: %X, %lX", cmd, arg); + return ret; + } + + pr_debug("cmd:%X, arg:%lX", cmd, arg); + switch (cmd) { + case FSM_IOC_SET_SLAVE: + case FSM_IOC_SET_ADDR: + ret = fsm_misc_set_slave(fsm_misc, arg & 0xFF); + break; + case FSM_IOC_GET_DEVICE: + count = fsm_dev_count(); + ret = copy_to_user((int *)arg, &count, sizeof(count)); + if (ret) { + pr_err("cmd:%X, copy to user fail:%d", cmd, ret); + return -EFAULT; + } + break; + case FSM_IOC_SET_SRATE: + case FSM_IOC_SET_SCENE: + case FSM_IOC_INIT: + case FSM_IOC_CALIBRATE: + case FSM_IOC_F0_TEST: + ret = copy_from_user(&misc_args, (void *)arg, sizeof(misc_args)); + if (ret) { + pr_err("cmd:%X, copy from user fail:%d", cmd, ret); + return -EFAULT; + } + ret = fsm_misc_cmd_with_args(cmd, fsm_misc->addr, &misc_args); + if (ret) { + pr_err("cmd:%X, error:%d", cmd, ret); + } + break; + case FSM_IOC_SPEAKER_ON: + fsm_speaker_onn(); + break; + case FSM_IOC_SPEAKER_OFF: + fsm_speaker_off(); + break; + case FSM_IOC_GET_RESULT: + break; +#ifdef CONFIG_FSM_NONDSP + case FSM_IOC_SEND_APR: + ret = fsm_misc_send_apr((void __user *)arg); + if (ret) { + pr_err("send apr fail:%d", ret); + } + break; +#endif + default: + pr_err("unknown cmd:%X", cmd); + ret = -EINVAL; + } + + return ret; +} + +#if defined(CONFIG_COMPAT) +static long fsm_misc_compat_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + switch (cmd) { + case FSM_IOC_GET_RESULT32: + cmd = FSM_IOC_GET_RESULT; + break; +#ifdef CONFIG_FSM_NONDSP + case FSM_IOC_SEND_APR: + case FSM_IOC_SEND_APR32: + cmd = FSM_IOC_SEND_APR; + break; +#endif + default: + break; + } + return fsm_misc_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static ssize_t fsm_misc_read(struct file *filp, char __user *buf, + size_t count, loff_t *offset) +{ + fsm_config_t *cfg = fsm_get_config(); + struct fsm_misc *fsm_misc; + struct fsm_dev *fsm_dev; + int retries = FSM_I2C_RETRY; + uint8_t *tmp; + int ret; + + if (cfg == NULL || filp == NULL || filp->private_data == NULL) { + pr_err("invalid parameter"); + return -EINVAL; + } + if (count > FSM_I2C_MAX_LEN) { + count = FSM_I2C_MAX_LEN; + } + tmp = fsm_alloc_mem(count); + if (tmp == NULL) { + return -ENOMEM; + } + + if (cfg->skip_monitor == false) { + cfg->skip_monitor = true; + } + fsm_misc = filp->private_data; + do { + fsm_dev = fsm_misc->fsm_dev[fsm_misc->index]; + if (fsm_dev == NULL || fsm_dev->i2c == NULL) { + ret = -EINVAL; + break; + } + mutex_lock(&fsm_dev->i2c_lock); + ret = i2c_master_recv(fsm_dev->i2c, tmp, count); + mutex_unlock(&fsm_dev->i2c_lock); + if (ret != count) { + fsm_delay_ms(5); + } + // pr_debug("data: 0x%02x", tmp[0]); + } while((ret != count) && (--retries >= 0)); + + if (ret == count) { + ret = (copy_to_user(buf, tmp, count) ? -EFAULT : ret); + } else { + pr_err("%02X: reading %zu bytes failed:%d", + fsm_misc->addr, count, ret); + } + fsm_free_mem((void **)&tmp); + + return ret; +} + +static ssize_t fsm_misc_write(struct file *filp, const char __user *buf, + size_t count, loff_t *offset) +{ + fsm_config_t *cfg = fsm_get_config(); + struct fsm_misc *fsm_misc; + struct fsm_dev *fsm_dev; + int retries = FSM_I2C_RETRY; + uint8_t *tmp; + int ret; + + if (cfg == NULL || filp == NULL || filp->private_data == NULL) { + pr_err("invalid parameter"); + return -EINVAL; + } + if (count > FSM_I2C_MAX_LEN) { + count = FSM_I2C_MAX_LEN; + } + tmp = memdup_user(buf, count); + if (IS_ERR(tmp)) { + return PTR_ERR(tmp); + } + + if (cfg->skip_monitor == false) { + cfg->skip_monitor = true; + } + fsm_misc = filp->private_data; + do { + fsm_dev = fsm_misc->fsm_dev[fsm_misc->index]; + if (fsm_dev == NULL || fsm_dev->i2c == NULL) { + ret = -EINVAL; + break; + } + // pr_debug("data: 0x%02x-0x%02x", buf[0], tmp[0]); + mutex_lock(&fsm_dev->i2c_lock); + ret = i2c_master_send(fsm_dev->i2c, tmp, count); + mutex_unlock(&fsm_dev->i2c_lock); + if (ret != count) { + fsm_delay_ms(5); + } + } while((ret != count) && (--retries >= 0)); + + if (ret != count) { + pr_err("%02X: writing %zu bytes failed:%d", + fsm_misc->addr, count, ret); + } + kfree(tmp); + + return ret; +} + +static const struct file_operations g_fsm_misc_ops = { + .owner = THIS_MODULE, + .open = fsm_misc_open, + .read = fsm_misc_read, + .write = fsm_misc_write, + .release = fsm_misc_release, + .llseek = no_llseek, + .unlocked_ioctl = fsm_misc_ioctl, +#if defined(CONFIG_COMPAT) + .compat_ioctl = fsm_misc_compat_ioctl, +#endif +}; + +struct miscdevice g_fsm_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = FSM_DRV_NAME, + .fops = &g_fsm_misc_ops, + .this_device = NULL, +}; + +int fsm_misc_init(void) +{ + struct miscdevice *misc = &g_fsm_misc; + int ret; + + pr_debug("enter"); + if (misc->this_device) { + return 0; + } + ret = misc_register(misc); + if (ret) { + misc->this_device = NULL; + } + + FSM_FUNC_EXIT(ret); + return ret; +} + +void fsm_misc_deinit(void) +{ + struct miscdevice *misc = &g_fsm_misc; + + pr_debug("enter"); + if (misc->this_device == NULL) { + return; + } + misc_deregister(misc); +} +#endif diff --git a/techpack/audio/asoc/codecs/fs1815/fsm_public.h b/techpack/audio/asoc/codecs/fs1815/fsm_public.h new file mode 100644 index 000000000000..885744344658 --- /dev/null +++ b/techpack/audio/asoc/codecs/fs1815/fsm_public.h @@ -0,0 +1,241 @@ +/** + * Copyright (C) Fourier Semiconductor Inc. 2016-2020. All rights reserved. + * 2018-10-18 File created. + */ + +#ifndef __FSM_PUBLIC_H__ +#define __FSM_PUBLIC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "fsm-dev.h" +#include +#include + +/* + * module: fsm_regmap + */ +#if defined(CONFIG_FSM_REGMAP) +#include +int fsm_regmap_write(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t val); +int fsm_regmap_bulkwrite(fsm_dev_t *fsm_dev, uint8_t reg, uint8_t *pval, uint32_t len); +int fsm_regmap_read(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t *pval); +int fsm_regmap_bulkread(fsm_dev_t *fsm_dev, uint8_t reg, uint8_t *pval, uint32_t len); +int fsm_regmap_update_bits(fsm_dev_t *fsm_dev, uint8_t reg, uint8_t mask, uint8_t val); +struct regmap *fsm_regmap_i2c_init(struct i2c_client *i2c); +int fsm_regmap_i2c_deinit(struct regmap *map); +#else +#define fsm_regmap_i2c_deinit(...) +#endif +/* + * module: fsm_i2c + */ +#if defined(CONFIG_FSM_I2C) +#include +bool fsm_set_pdev(struct device *dev); +struct device *fsm_get_pdev(void); +void *fsm_devm_kstrdup(struct device *dev, void *buf, size_t size); +int fsm_i2c_reg_read(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t *pVal); +int fsm_i2c_reg_write(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t val); +int fsm_i2c_bulkwrite(fsm_dev_t *fsm_dev, uint8_t reg, + uint8_t *data, int len); +int fsm_get_amb_tempr(void); +int fsm_read_efsdata(struct fsm_calib_v2 *data); +int fsm_write_efsdata(struct fsm_calib_v2 *data); +int fsm_i2c_save_re25(struct fsm_calib_v2 *calib_data); +int fsm_i2c_init(void); +void fsm_i2c_exit(void); +#else +#define fsm_get_amb_tempr(...) (FSM_DFT_AMB_TEMPR) +#endif + +void fsm_mutex_lock(void); +void fsm_mutex_unlock(void); + +/* + * module: fsm_misc + */ +#if defined(CONFIG_FSM_MISC) +int fsm_misc_init(void); +void fsm_misc_deinit(void); +#else +#define fsm_misc_init(...) +#define fsm_misc_deinit(...) +#endif + +/* + * module: fsm_sysfs + */ +#if defined(CONFIG_FSM_SYSFS) +int fsm_sysfs_init(struct device *dev); +void fsm_sysfs_deinit(struct device *dev); +#else +#define fsm_sysfs_init(...) +#define fsm_sysfs_deinit(...) +#endif + +/* + * module: fsm_firmware + */ +#if defined(CONFIG_FSM_FIRMWARE) +int fsm_firmware_init(char *fw_name); +int fsm_firmware_init_sync(char *fw_name); +void fsm_firmware_deinit(void); +#elif defined(FSM_HAL_SUPPORT) +int fsm_firmware_init_sync(char *fw_name); +void fsm_firmware_deinit(void); +#else +#define fsm_firmware_init(...) (-1) +#define fsm_firmware_init_sync(...) (-1) +#define fsm_firmware_deinit(...) +#endif + +/* + * module: fsm_codec + */ +#if defined(CONFIG_FSM_CODEC) +int fsm_codec_register(struct device *dev, int id); +void fsm_codec_unregister(struct device *dev); +#else +#define fsm_codec_register(...) +#define fsm_codec_unregister(...) +#endif + +/* + * module: fsm_core + */ +fsm_config_t *fsm_get_config(void); +void *fsm_get_presets(void); +void fsm_set_presets(void *preset); +void fsm_get_version(fsm_version_t *version); +void fsm_delay_ms(uint32_t delay_ms); +void *fsm_alloc_mem(int size); +void fsm_free_mem(void **buf); +uint16_t set_bf_val(uint16_t *pval, const uint16_t bf, const uint16_t bf_val); +uint16_t get_bf_val(const uint16_t bf, const uint16_t val); +int fsm_set_bf(fsm_dev_t *fsm_dev, const uint16_t bf, const uint16_t val); +int fsm_get_bf(fsm_dev_t *fsm_dev, const uint16_t bf, uint16_t *pval); +int fsm_reg_write(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t val); +int fsm_reg_read(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t *pval); +int fsm_reg_update_bits(fsm_dev_t *fsm_dev, reg_unit_t *reg); +int fsm_reg_update(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t val); +int fsm_reg_multiread(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t *pval); +int fsm_burst_write(fsm_dev_t *fsm_dev, uint8_t reg, uint8_t *data, int len); +uint16_t fsm_calc_checksum(uint16_t *data, int len); +struct fsm_dev *fsm_get_fsm_dev(uint8_t addr); +struct fsm_dev *fsm_get_fsm_dev_by_id(int id); +int fsm_read_vbat(fsm_dev_t *fsm_dev, uint16_t *vbat); +int fsm_get_srate_bits(fsm_dev_t *fsm_dev, uint32_t srate); +int fsm_access_key(fsm_dev_t *fsm_dev, int access); +int fsm_reg_dump(fsm_dev_t *fsm_dev); +int fsm_init_dev_list(fsm_dev_t *fsm_dev); +int fsm_init_info(fsm_dev_t *fsm_dev); +void *fsm_get_list_by_idx(fsm_dev_t *fsm_dev, int idx); +void *fsm_get_data_list(fsm_dev_t *fsm_dev, int type); +int fsm_get_spk_info(fsm_dev_t *fsm_dev, uint16_t info_type); +int zero_bit_counter(uint8_t byte); +int get_otp_counter(uint16_t byte); +int get_otp_max_count(fsm_dev_t *fsm_dev); +void convert_data_to_bytes(uint32_t val, uint8_t *buf); +int get_re25_from_buf(struct fsm_calib_v2 *data, + char *buf, int buf_len); +int set_re25_to_buf(struct fsm_calib_v2 *data, + char *buf, int buf_len); +int fsm_parse_preset(const void *data, uint32_t size); +int fsm_write_preset(fsm_dev_t *fsm_dev); +int fsm_swap_channel(fsm_dev_t *fsm_dev, int next_angle); +int fsm_set_monitor(fsm_dev_t *fsm_dev, bool enable); +int fsm_wait_stable(fsm_dev_t *fsm_dev, int type); +int fsm_parse_otp(fsm_dev_t *fsm_dev, uint16_t value, int *re25, int *count); +int fsm_config_vol(fsm_dev_t *fsm_dev); +int fsm_dev_recover(fsm_dev_t *fsm_dev); +int fsm_get_spkr_tempr(fsm_dev_t *fsm_dev); +int fsm_ops_dummy(fsm_dev_t *fsm_dev); +int fsm_write_reg_tbl(fsm_dev_t *fsm_dev, uint16_t scene); +int fsm_set_cal_scene(fsm_dev_t *fsm_dev); +int fsm_get_livedata(fsm_msg_t *data); +int fsm_save_re25(struct fsm_calib_v2 *data); +int fsm_get_re25(struct fsm_calib_v2 *data); +uint32_t fsm_cal_spkr_zmimp(fsm_dev_t *fsm_dev, uint16_t data); +int fsm_skip_device(fsm_dev_t *fsm_dev); +int fsm_cal_zmdata(fsm_dev_t *fsm_dev); +int fsm_get_rstrim(fsm_dev_t *fsm_dev); +uint16_t fsm_cal_threshold(fsm_dev_t *fsm_dev, + int mt_type, uint16_t zmdata); +int fsm_check_otp(fsm_dev_t *fsm_dev); +int fsm_check_without_otp(fsm_dev_t *fsm_dev); +int fsm_stub_dev_init(fsm_dev_t *fsm_dev); +int fsm_stub_start_up(fsm_dev_t *fsm_dev); +int fsm_stub_check_stable(fsm_dev_t *fsm_dev, int type); +int fsm_stub_store_re25(fsm_dev_t *fsm_dev); +int fsm_stub_pre_calib(fsm_dev_t *fsm_dev); +int fsm_stub_post_calib(fsm_dev_t *fsm_dev); +int fsm_stub_set_mute(fsm_dev_t *fsm_dev, bool mute); +int fsm_stub_set_tsignal(fsm_dev_t *fsm_dev, bool enable); +int fsm_stub_shut_down(fsm_dev_t *fsm_dev); +int fsm_stub_dev_deinit(fsm_dev_t *fsm_dev); + +/* + * module: fsm_hal + */ +#if defined(FSM_HAL_SUPPORT) +int fsm_open_device(void); +void fsm_close_device(void); +int fsm_check_device(char *dev_name); +int fsm_hal_open(void); +int fsm_hal_reg_read(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t *val); +int fsm_hal_reg_write(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t val); +int fsm_hal_bulkwrite(fsm_dev_t *fsm_dev, uint8_t reg, uint8_t *val, uint32_t len); +int fsm_hal_bulkread(fsm_dev_t *fsm_dev, uint8_t reg, uint32_t len, uint8_t *val); +int fsm_hal_get_livedata(fsm_msg_t *data); +int fsm_hal_save_re25(struct fsm_calib_v2 *data); +int fsm_hal_get_re25(struct fsm_calib_v2 *data); +int fstool_reg_read(uint8_t addr, uint8_t reg, uint16_t *val); +int fstool_reg_write(uint8_t addr, uint8_t reg, uint16_t val); +int fstool_bulkwrite(uint8_t addr, uint8_t reg, + uint32_t len, uint8_t *val); +int fstool_bulkread(uint8_t addr, uint8_t reg, + uint32_t len, uint8_t *val); +void fsm_hal_close(void); +#else +#define fsm_hal_open(...) (0) +#define fsm_hal_close(...) +#endif + +void fsm_set_fw_name(char *name); +int fsm_probe(fsm_dev_t *fsm_dev, int addr); +void fsm_remove(fsm_dev_t *fsm_dev); +int fsm_dev_count(void); +void fsm_set_i2s_clocks(uint32_t rate, uint32_t bclk); +void fsm_set_tx_format(uint32_t format); +void fsm_set_scene(int scene); +void fsm_set_volume(int volume); +void fsm_set_cfg_flag(int pos, int mark); +void fsm_init(void); +void fsm_speaker_onn(void); +void fsm_speaker_off(void); +void fsm_stereo_rotation(int next_angle); +void fsm_batv_monitor(void); +void fsm_re25_test(bool force); +void fsm_f0_test(void); +int fsm_test_result(struct fsm_cal_result *result, int size); +void fsm_dump(void); +void fsm_deinit(void); + +void fs1815_ops(fsm_dev_t *fsm_dev); + +#if (LINUX_VERSION_CODE >= 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_codec_get_drvdata snd_soc_component_get_drvdata +#endif +void fsm_add_codec_controls(struct snd_soc_codec *codec); +void fsm_add_card_controls(struct snd_soc_card *card); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/techpack/audio/asoc/codecs/wcd-mbhc-v2.c b/techpack/audio/asoc/codecs/wcd-mbhc-v2.c index 61b1eaf16a1c..3158cb513eee 100644 --- a/techpack/audio/asoc/codecs/wcd-mbhc-v2.c +++ b/techpack/audio/asoc/codecs/wcd-mbhc-v2.c @@ -604,7 +604,7 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->hph_type = WCD_MBHC_HPH_NONE; mbhc->zl = mbhc->zr = 0; - pr_debug("%s: Reporting removal %d(%x)\n", __func__, + pr_info("%s: Reporting removal %d(%x)\n", __func__, jack_type, mbhc->hph_status); wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, mbhc->hph_status, WCD_MBHC_JACK_MASK); @@ -647,7 +647,7 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->hph_type = WCD_MBHC_HPH_NONE; mbhc->zl = mbhc->zr = 0; if (!mbhc->force_linein) { - pr_debug("%s: Reporting removal (%x)\n", + pr_info("%s: Reporting removal (%x)\n", __func__, mbhc->hph_status); wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, 0, WCD_MBHC_JACK_MASK); @@ -711,8 +711,12 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, &mbhc->zl, &mbhc->zr); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, fsm_en); - if ((mbhc->zl > mbhc->mbhc_cfg->linein_th) && - (mbhc->zr > mbhc->mbhc_cfg->linein_th) && + pr_info("%s,compute impedance,zl = %d,zr = %d\n", + __func__, mbhc->zl, mbhc->zr); + if ((((mbhc->zl > mbhc->mbhc_cfg->linein_th) && + (mbhc->zr > mbhc->mbhc_cfg->linein_th)) || + (mbhc->zl == 0) || + (mbhc->zr == 0)) && (jack_type == SND_JACK_HEADPHONE)) { jack_type = SND_JACK_LINEOUT; mbhc->force_linein = true; @@ -731,7 +735,7 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->hph_status, WCD_MBHC_JACK_MASK); } - pr_debug("%s: Marking jack type as SND_JACK_LINEOUT\n", + pr_info("%s: Marking jack type as SND_JACK_LINEOUT\n", __func__); } } @@ -766,7 +770,7 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->mbhc_cb->mbhc_micb_ramp_control) mbhc->mbhc_cb->mbhc_micb_ramp_control(component, false); - pr_debug("%s: Reporting insertion %d(%x)\n", __func__, + pr_info("%s: Reporting insertion %d(%x)\n", __func__, jack_type, mbhc->hph_status); wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, (mbhc->hph_status | SND_JACK_MECHANICAL), @@ -789,7 +793,7 @@ void wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc *mbhc) else pr_info("%s: hs_detect_plug work not cancelled\n", __func__); - pr_debug("%s: Report extension cable\n", __func__); + pr_info("%s: Report extension cable\n", __func__); wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT); /* * If PA is enabled HPHL schmitt trigger can @@ -1096,7 +1100,7 @@ static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data) int r = IRQ_HANDLED; struct wcd_mbhc *mbhc = data; - pr_debug("%s: enter\n", __func__); + pr_info("%s: enter\n", __func__); if (mbhc == NULL) { pr_err("%s: NULL irq data\n", __func__); return IRQ_NONE; @@ -1279,7 +1283,7 @@ static irqreturn_t wcd_mbhc_release_handler(int irq, void *data) if (mbhc->buttons_pressed & WCD_MBHC_JACK_BUTTON_MASK) { ret = wcd_cancel_btn_work(mbhc); if (ret == 0) { - pr_debug("%s: Reporting long button release event\n", + pr_info("%s: Reporting long button release event\n", __func__); wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, 0, mbhc->buttons_pressed); @@ -1288,7 +1292,7 @@ static irqreturn_t wcd_mbhc_release_handler(int irq, void *data) pr_debug("%s: Switch irq kicked in, ignore\n", __func__); } else { - pr_debug("%s: Reporting btn press\n", + pr_info("%s: Reporting btn press\n", __func__); wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, @@ -1653,6 +1657,7 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) struct snd_soc_component *component; struct snd_soc_card *card; const char *usb_c_dt = "qcom,msm-mbhc-usbc-audio-supported"; + const char *linein_th = "qcom,msm-hs-linein-threshold"; if (!mbhc || !mbhc_cfg) return -EINVAL; @@ -1679,6 +1684,12 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) "%s: skipping USB c analog configuration\n", __func__); } + if (of_find_property(card->dev->of_node, linein_th, NULL)) { + rc = of_property_read_u32(card->dev->of_node, linein_th, + &mbhc->mbhc_cfg->linein_th); + } + pr_info("%s, LINEIN threshold = %d\n", __func__, mbhc->mbhc_cfg->linein_th); + /* Parse fsa switch handle */ if (mbhc_cfg->enable_usbc_analog) { dev_dbg(mbhc->component->dev, "%s: usbc analog enabled\n", diff --git a/techpack/audio/asoc/codecs/wcd937x/wcd937x.c b/techpack/audio/asoc/codecs/wcd937x/wcd937x.c index 686430427a75..adaf621d5186 100644 --- a/techpack/audio/asoc/codecs/wcd937x/wcd937x.c +++ b/techpack/audio/asoc/codecs/wcd937x/wcd937x.c @@ -1959,6 +1959,57 @@ static int wcd937x_tx_ch_pwr_level_put(struct snd_kcontrol *kcontrol, return 0; } +static int wcd937x_aux_path_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int aux_mode; + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + + aux_mode = ((snd_soc_component_read32(component, + WCD937X_DIGITAL_CDC_PATH_MODE) & 0x40)>>6); + + ucontrol->value.integer.value[0] = aux_mode; + + dev_dbg(component->dev, "%s: aux_mode = 0x%x ", __func__, + aux_mode); + + return 0; +} + +static int wcd937x_aux_path_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int aux_mode; + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); + dev_dbg(component->dev, "%s: codec version %u, ucontrol->value.integer.value[0] = %ld ", + __func__, wcd937x->version, ucontrol->value.integer.value[0]); + + dev_dbg(component->dev, "%s: ucontrol->value.integer.value[0] = %ld", + __func__, ucontrol->value.integer.value[0]); + + aux_mode = ucontrol->value.integer.value[0]; + + if (aux_mode) { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_PATH_MODE, + 0x40, 0x40); + snd_soc_component_update_bits(component, + WCD937X_AUX_AUXPA, + 0x10, 0x10); + } else { + snd_soc_component_update_bits(component, + WCD937X_DIGITAL_CDC_PATH_MODE, + 0x40, 0x00); + snd_soc_component_update_bits(component, + WCD937X_AUX_AUXPA, + 0x10, 0x00); + } + return 0; +} + static int wcd937x_ear_pa_gain_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2092,6 +2143,11 @@ static int wcd937x_codec_enable_vdd_buck(struct snd_soc_dapm_widget *w, return 0; } + +static const char * const wcd937x_aux_path_mode_text[] = { + "HP_MODE", "NORMAL_MODE", +}; + static const char * const rx_hph_mode_mux_text[] = { "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI", "CLS_H_ULP", "CLS_AB_HIFI", @@ -2221,6 +2277,11 @@ static const struct soc_enum rx_hph_mode_mux_enum = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), rx_hph_mode_mux_text); +static const struct soc_enum wcd937x_aux_path_mode_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wcd937x_aux_path_mode_text), + wcd937x_aux_path_mode_text); + + static SOC_ENUM_SINGLE_EXT_DECL(wcd937x_ear_pa_gain_enum, wcd937x_ear_pa_gain_text); @@ -2269,6 +2330,9 @@ static const struct snd_kcontrol_new wcd937x_snd_controls[] = { wcd937x_tx_ch_pwr_level_get, wcd937x_tx_ch_pwr_level_put), SOC_ENUM_EXT("TX CH3 PWR", wcd937x_tx_ch_pwr_level_enum, wcd937x_tx_ch_pwr_level_get, wcd937x_tx_ch_pwr_level_put), + SOC_ENUM_EXT("AUX PATH Mode", wcd937x_aux_path_mode_enum, + wcd937x_aux_path_mode_get, wcd937x_aux_path_mode_put), + }; static const struct snd_kcontrol_new adc1_switch[] = { diff --git a/techpack/audio/asoc/holi.c b/techpack/audio/asoc/holi.c index c788e26beefc..bc891ce3d51f 100644 --- a/techpack/audio/asoc/holi.c +++ b/techpack/audio/asoc/holi.c @@ -46,6 +46,14 @@ #define __CHIPSET__ "HOLI " #define MSM_DAILINK_NAME(name) (__CHIPSET__#name) +#ifdef CONFIG_SND_SOC_AW87XXX + extern int aw87xxx_add_codec_controls(void *codec); +#endif + +#ifdef CONFIG_SND_SOC_FS1815 + extern int fsm_add_codec_controls(struct snd_soc_component *codec); +#endif + #define SAMPLING_RATE_8KHZ 8000 #define SAMPLING_RATE_11P025KHZ 11025 #define SAMPLING_RATE_16KHZ 16000 @@ -576,6 +584,7 @@ static char const *bt_sample_rate_tx_text[] = {"KHZ_8", "KHZ_16", "KHZ_44P1", "KHZ_48", "KHZ_88P2", "KHZ_96"}; static const char *const afe_loopback_tx_ch_text[] = {"One", "Two"}; +static const char *const earpiece_dsense_text[] = {"On", "Off"}; static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text); @@ -710,6 +719,7 @@ static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate_rx, bt_sample_rate_rx_text); static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate_tx, bt_sample_rate_tx_text); static SOC_ENUM_SINGLE_EXT_DECL(afe_loopback_tx_chs, afe_loopback_tx_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(earpiece_dsense_en, earpiece_dsense_text); static bool is_initial_boot; static bool codec_reg_done; @@ -717,6 +727,8 @@ static struct snd_soc_card snd_soc_card_holi_msm; static int dmic_0_1_gpio_cnt; static int dmic_2_3_gpio_cnt; static int dmic_4_5_gpio_cnt; +static int earpiece_dsense_en_gpio; +static bool is_earpiece_dsense_disable; static void *def_wcd_mbhc_cal(void); @@ -3217,6 +3229,36 @@ static int msm_bt_sample_rate_tx_put(struct snd_kcontrol *kcontrol, return 0; } +/* when gpio output is high, means earpiece dsense is off */ +static int earpiece_dsense_en_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = is_earpiece_dsense_disable; + pr_debug("get earpiece_dsense_pin state: %s\n", + is_earpiece_dsense_disable ? "high" : "low"); + return 0; +} + +static int earpiece_dsense_en_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 0: + gpio_direction_output(earpiece_dsense_en_gpio, 0); + is_earpiece_dsense_disable = 0; + break; + case 1: + gpio_direction_output(earpiece_dsense_en_gpio, 1); + is_earpiece_dsense_disable = 1; + break; + default: + return -EINVAL; + } + pr_debug("set earpiece_dsense_pin: %s\n", + ucontrol->value.integer.value[0] ? "high" : "low"); + return 0; +} + static const struct snd_kcontrol_new msm_int_wcd937x_snd_controls[] = { SOC_ENUM_EXT("RX_CDC_DMA_RX_0 Format", rx_cdc_dma_rx_0_format, cdc_dma_rx_format_get, cdc_dma_rx_format_put), @@ -3591,6 +3633,8 @@ static const struct snd_kcontrol_new msm_common_snd_controls[] = { msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put), SOC_SINGLE_MULTI_EXT("TDM Slot Map", SND_SOC_NOPM, 0, 255, 0, TDM_MAX_SLOTS + MAX_PATH, NULL, tdm_slot_map_put), + SOC_ENUM_EXT("Earpiece Dsense Enable", earpiece_dsense_en, + earpiece_dsense_en_get, earpiece_dsense_en_put), }; static const struct snd_kcontrol_new msm_snd_controls[] = { @@ -4756,8 +4800,8 @@ static void *def_wcd_mbhc_cal(void) btn_high = ((void *)&btn_cfg->_v_btn_low) + (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn); - btn_high[0] = 75; - btn_high[1] = 150; + btn_high[0] = 88; + btn_high[1] = 138; btn_high[2] = 237; btn_high[3] = 500; btn_high[4] = 500; @@ -5212,6 +5256,38 @@ static struct snd_soc_dai_link msm_common_dai_links[] = { }, }; +#ifdef CONFIG_AW882XX_STEREO_SMARTPA_PRI +static struct snd_soc_dai_link msm_pri_mi2s_tx_hotless_dai_links[] = { + { + .name = "Primary MI2S_TX Hostless", + .stream_name = "Primary MI2S_TX Hostless Capture", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + SND_SOC_DAILINK_REG(pri_mi2s_tx_hostless), + }, +}; +#else +static struct snd_soc_dai_link msm_sec_mi2s_tx_hotless_dai_links[] = { + { + .name = "Secondary MI2S_TX Hostless", + .stream_name = "Secondary MI2S_TX Hostless Capture", + .dynamic = 1, + .dpcm_capture = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + SND_SOC_DAILINK_REG(sec_mi2s_tx_hostless), + }, +}; +#endif + static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = { {/* hw:x,33 */ .name = MSM_DAILINK_NAME(ASM Loopback), @@ -5921,7 +5997,12 @@ static struct snd_soc_dai_link msm_holi_dai_links[ ARRAY_SIZE(msm_rx_tx_cdc_dma_be_dai_links) + ARRAY_SIZE(msm_va_cdc_dma_be_dai_links) + ARRAY_SIZE(msm_afe_rxtx_lb_be_dai_link) + - ARRAY_SIZE(msm_wcn_btfm_be_dai_links)]; + ARRAY_SIZE(msm_wcn_btfm_be_dai_links) + +#ifdef CONFIG_AW882XX_STEREO_SMARTPA_PRI + ARRAY_SIZE(msm_pri_mi2s_tx_hotless_dai_links)]; +#else + ARRAY_SIZE(msm_sec_mi2s_tx_hotless_dai_links)]; +#endif static int msm_populate_dai_link_component_of_node( struct snd_soc_card *card) @@ -6164,6 +6245,69 @@ static const struct of_device_id holi_asoc_machine_of_match[] = { {}, }; +#ifdef CONFIG_AW882XX_STEREO_SMARTPA +struct snd_soc_dai_link_component aw_codecs[4]; +static unsigned int aw_codecs_num; +static DEFINE_MUTEX(aw_dailink_mutex); + +void awinic_set_dai_name(const char* drvdainame, const char*drvname) +{ + mutex_lock(&aw_dailink_mutex); + if (aw_codecs_num < ARRAY_SIZE(aw_codecs)) { + aw_codecs[aw_codecs_num].dai_name = drvdainame; + aw_codecs[aw_codecs_num].name = drvname; + aw_codecs[aw_codecs_num].of_node = NULL; + pr_info("aw_codecs_num[%d] codec name %s dai name %s\n", + aw_codecs_num, aw_codecs[aw_codecs_num].name, aw_codecs[aw_codecs_num].dai_name); + aw_codecs_num++; + } + mutex_unlock(&aw_dailink_mutex); +} +EXPORT_SYMBOL(awinic_set_dai_name); + +#ifdef CONFIG_AW882XX_STEREO_SMARTPA_PRI +static int aw_update_dai_link_name(struct snd_soc_dai_link *dailink, int len1) +{ + int i; + + if (aw_codecs_num != 0) { + for (i = 0; i < len1; i++) { + if((!strcmp(dailink[i].stream_name, "Primary MI2S Playback")) || + (!strcmp(dailink[i].stream_name, "Primary MI2S Capture"))){ + dailink[i].num_codecs = aw_codecs_num; + dailink[i].codecs = aw_codecs; + } + } + } else { + pr_err("aw_codecs is empty\n"); + return -EINVAL; + } + return 0; +} +#else +static int aw_update_dai_link_name(struct snd_soc_dai_link *dailink, int len1) +{ + int i; + + if (aw_codecs_num != 0) { + for (i = 0; i < len1; i++) { + if((!strcmp(dailink[i].stream_name, "Secondary MI2S Playback")) || + (!strcmp(dailink[i].stream_name, "Secondary MI2S Capture"))){ + dailink[i].num_codecs = aw_codecs_num; + dailink[i].codecs = aw_codecs; + } + } + } else { + pr_err("aw_codecs is empty\n"); + return -EINVAL; + } + return 0; +} +#endif + +#endif + + static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) { struct snd_soc_card *card = NULL; @@ -6270,6 +6414,22 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) ARRAY_SIZE(msm_wcn_btfm_be_dai_links); } } + + if (mi2s_audio_intf) { + +#ifdef CONFIG_AW882XX_STEREO_SMARTPA_PRI + memcpy(msm_holi_dai_links + total_links, + msm_pri_mi2s_tx_hotless_dai_links, + sizeof(msm_pri_mi2s_tx_hotless_dai_links)); + total_links += ARRAY_SIZE(msm_pri_mi2s_tx_hotless_dai_links); +#else + memcpy(msm_holi_dai_links + total_links, + msm_sec_mi2s_tx_hotless_dai_links, + sizeof(msm_sec_mi2s_tx_hotless_dai_links)); + total_links += ARRAY_SIZE(msm_sec_mi2s_tx_hotless_dai_links); +#endif + } + dailink = msm_holi_dai_links; } else if (!strcmp(match->data, "stub_codec")) { card = &snd_soc_card_stub_msm; @@ -6287,6 +6447,10 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) total_links = len_2; } +#ifdef CONFIG_AW882XX_STEREO_SMARTPA + aw_update_dai_link_name(dailink, total_links); +#endif + if (card) { card->dai_link = dailink; card->num_links = total_links; @@ -6329,6 +6493,24 @@ static int msm_int_audrx_init(struct snd_soc_pcm_runtime *rtd) return ret; } +#ifdef CONFIG_SND_SOC_AW87XXX + ret = aw87xxx_add_codec_controls((void *)component); + if (ret < 0) { + pr_err("%s: add_codec_controls failed, ret %d\n", + __func__, ret); + return ret; + }; +#endif + +#ifdef CONFIG_SND_SOC_FS1815 + ret = fsm_add_codec_controls(component); + if (ret < 0) { + pr_err("%s: add_codec_controls failed, ret %d\n", + __func__, ret); + return ret; + }; +#endif + snd_soc_dapm_new_controls(dapm, msm_int_dapm_widgets, ARRAY_SIZE(msm_int_dapm_widgets)); @@ -6817,6 +6999,19 @@ static int msm_asoc_machine_probe(struct platform_device *pdev) for (index = PRIM_MI2S; index < MI2S_MAX; index++) atomic_set(&(pdata->mi2s_gpio_ref_count[index]), 0); + earpiece_dsense_en_gpio = of_get_named_gpio(pdev->dev.of_node, + "earpiece-dsense-enable", 0); + if (earpiece_dsense_en_gpio < 0) { + pr_err("missing earpiece_dsense_en_gpio in dt node\n"); + } else { + if (!gpio_is_valid(earpiece_dsense_en_gpio)) { + pr_err("Invalid earpiece_dsense_en_gpio\n"); + } else { + gpio_direction_output(earpiece_dsense_en_gpio, 1); + is_earpiece_dsense_disable = 1; + } + } + /* Register LPASS audio hw vote */ lpass_audio_hw_vote = devm_clk_get(&pdev->dev, "lpass_audio_hw_vote"); if (IS_ERR(lpass_audio_hw_vote)) { diff --git a/techpack/audio/asoc/msm-pcm-host-voice-v2.c b/techpack/audio/asoc/msm-pcm-host-voice-v2.c index 75cac617568b..847f35bb7bec 100644 --- a/techpack/audio/asoc/msm-pcm-host-voice-v2.c +++ b/techpack/audio/asoc/msm-pcm-host-voice-v2.c @@ -752,6 +752,13 @@ void hpcm_notify_evt_processing(uint8_t *data, char *session, return; } + if (prtd->mixer_conf.sess_indx < VOICE_INDEX || + prtd->mixer_conf.sess_indx >= MAX_SESSION) { + pr_err("%s:: Invalid session idx %d\n", + __func__, prtd->mixer_conf.sess_indx); + return; + } + if (notify_evt->tap_point == VSS_IVPCM_TAP_POINT_TX_DEFAULT) { tp = &prtd->session[prtd->mixer_conf.sess_indx].tx_tap_point; tmd = &prtd->mixer_conf.tx; diff --git a/techpack/audio/asoc/msm-pcm-routing-v2.c b/techpack/audio/asoc/msm-pcm-routing-v2.c index 7c0051617ea8..c3b5f480993f 100644 --- a/techpack/audio/asoc/msm-pcm-routing-v2.c +++ b/techpack/audio/asoc/msm-pcm-routing-v2.c @@ -33282,6 +33282,15 @@ static const char * const mi2s_rx_vi_fb_tx_mux_text[] = { "ZERO", "SENARY_TX" }; +static const char * const pri_mi2s_rx_vi_fb_tx_mux_text[] = { + "ZERO", "PRI_MI2S_TX" +}; + + +static const char * const sec_mi2s_rx_vi_fb_tx_mux_text[] = { + "ZERO", "SEC_MI2S_TX" +}; + static const char * const int4_mi2s_rx_vi_fb_tx_mono_mux_text[] = { "ZERO", "INT5_MI2S_TX" }; @@ -33314,6 +33323,13 @@ static const int wsa_rx_0_vi_fb_tx_rch_value[] = { MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_WSA_CDC_DMA_TX_0 }; +static const int pri_mi2s_rx_vi_fb_tx_value[] = { + MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_PRI_MI2S_TX +}; + +static const int sec_mi2s_rx_vi_fb_tx_value[] = { + MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_SECONDARY_MI2S_TX +}; static const int mi2s_rx_vi_fb_tx_value[] = { MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_SENARY_MI2S_TX @@ -33362,6 +33378,16 @@ static const struct soc_enum mi2s_rx_vi_fb_mux_enum = ARRAY_SIZE(mi2s_rx_vi_fb_tx_mux_text), mi2s_rx_vi_fb_tx_mux_text, mi2s_rx_vi_fb_tx_value); +static const struct soc_enum pri_mi2s_rx_vi_fb_mux_enum = + SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_PRI_MI2S_RX, 0, 0, + ARRAY_SIZE(pri_mi2s_rx_vi_fb_tx_mux_text), + pri_mi2s_rx_vi_fb_tx_mux_text, pri_mi2s_rx_vi_fb_tx_value); + +static const struct soc_enum sec_mi2s_rx_vi_fb_mux_enum = + SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_SEC_I2S_RX, 0, 0, + ARRAY_SIZE(sec_mi2s_rx_vi_fb_tx_mux_text), + sec_mi2s_rx_vi_fb_tx_mux_text, sec_mi2s_rx_vi_fb_tx_value); + static const struct soc_enum int4_mi2s_rx_vi_fb_mono_ch_mux_enum = SOC_VALUE_ENUM_DOUBLE(SND_SOC_NOPM, MSM_BACKEND_DAI_INT4_MI2S_RX, 0, 0, ARRAY_SIZE(int4_mi2s_rx_vi_fb_tx_mono_mux_text), @@ -33409,6 +33435,16 @@ static const struct snd_kcontrol_new mi2s_rx_vi_fb_mux = mi2s_rx_vi_fb_mux_enum, spkr_prot_get_vi_lch_port, spkr_prot_put_vi_lch_port); +static const struct snd_kcontrol_new pri_mi2s_rx_vi_fb_mux = + SOC_DAPM_ENUM_EXT("PRI_MI2S_RX_VI_FB_MUX", + pri_mi2s_rx_vi_fb_mux_enum, spkr_prot_get_vi_lch_port, + spkr_prot_put_vi_lch_port); + +static const struct snd_kcontrol_new sec_mi2s_rx_vi_fb_mux = + SOC_DAPM_ENUM_EXT("SEC_MI2S_RX_VI_FB_MUX", + sec_mi2s_rx_vi_fb_mux_enum, spkr_prot_get_vi_lch_port, + spkr_prot_put_vi_lch_port); + static const struct snd_kcontrol_new int4_mi2s_rx_vi_fb_mono_ch_mux = SOC_DAPM_ENUM_EXT("INT4_MI2S_RX_VI_FB_MONO_CH_MUX", int4_mi2s_rx_vi_fb_mono_ch_mux_enum, spkr_prot_get_vi_lch_port, @@ -33424,6 +33460,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { /* Widget name equals to Front-End DAI name, * Stream name must contains substring of front-end dai name */ + SND_SOC_DAPM_MUX("PRI_MI2S_RX_VI_FB_MUX", SND_SOC_NOPM, 0, 0, + &pri_mi2s_rx_vi_fb_mux), SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0), @@ -34410,6 +34448,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets_mi2s[] = { ARRAY_SIZE(int4_mi2s_rx_port_mixer_controls)), SND_SOC_DAPM_MUX("PRI_MI2S_RX_VI_FB_MUX", SND_SOC_NOPM, 0, 0, &mi2s_rx_vi_fb_mux), + SND_SOC_DAPM_MUX("SEC_MI2S_RX_VI_FB_MUX", SND_SOC_NOPM, 0, 0, + &sec_mi2s_rx_vi_fb_mux), SND_SOC_DAPM_MUX("INT4_MI2S_RX_VI_FB_MONO_CH_MUX", SND_SOC_NOPM, 0, 0, &int4_mi2s_rx_vi_fb_mono_ch_mux), SND_SOC_DAPM_MUX("INT4_MI2S_RX_VI_FB_STEREO_CH_MUX", SND_SOC_NOPM, 0, 0, @@ -35612,6 +35652,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets_tdm[] = { #endif static const struct snd_soc_dapm_route intercon[] = { + {"PRI_MI2S_RX_VI_FB_MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX_VI_FB_MUX"}, {"SLIMBUS_0_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, {"SLIMBUS_0_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, {"SLIMBUS_0_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, @@ -42094,9 +42136,11 @@ static const struct snd_soc_dapm_route intercon_mi2s[] = { {"SENARY_MI2S_TX", NULL, "BE_IN"}, {"PRI_MI2S_RX_VI_FB_MUX", "SENARY_TX", "SENARY_TX"}, + {"SEC_MI2S_RX_VI_FB_MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, {"INT4_MI2S_RX_VI_FB_MONO_CH_MUX", "INT5_MI2S_TX", "INT5_MI2S_TX"}, {"INT4_MI2S_RX_VI_FB_STEREO_CH_MUX", "INT5_MI2S_TX", "INT5_MI2S_TX"}, {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX_VI_FB_MUX"}, + {"SEC_MI2S_RX", NULL, "SEC_MI2S_RX_VI_FB_MUX"}, {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_VI_FB_MONO_CH_MUX"}, {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_VI_FB_STEREO_CH_MUX"}, }; diff --git a/techpack/audio/asoc/msm_holi_dailink.h b/techpack/audio/asoc/msm_holi_dailink.h index b4707283c052..39e828f84a66 100644 --- a/techpack/audio/asoc/msm_holi_dailink.h +++ b/techpack/audio/asoc/msm_holi_dailink.h @@ -316,6 +316,18 @@ SND_SOC_DAILINK_DEFS(slimbus_8_tx, "btfm_fm_slim_tx")), DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); +#if defined(CONFIG_AW882XX_STEREO_SMARTPA_PRI) +SND_SOC_DAILINK_DEFS(pri_mi2s_rx, + DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.0")), + DAILINK_COMP_ARRAY(COMP_CODEC("aw882xxacf_smartpa.2-0034", "aw882xx-aif-2-34"), + COMP_CODEC("aw882xxacf_smartpa.2-0035", "aw882xx-aif-2-35")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); +SND_SOC_DAILINK_DEFS(pri_mi2s_tx, + DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.1")), + DAILINK_COMP_ARRAY(COMP_CODEC("aw882xxacf_smartpa.2-0034", "aw882xx-aif-2-34"), + COMP_CODEC("aw882xxacf_smartpa.2-0035", "aw882xx-aif-2-35")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); +#else SND_SOC_DAILINK_DEFS(pri_mi2s_rx, DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.0")), DAILINK_COMP_ARRAY(COMP_CODEC("msm-stub-codec.1", "msm-stub-rx")), @@ -325,16 +337,38 @@ SND_SOC_DAILINK_DEFS(pri_mi2s_tx, DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.1")), DAILINK_COMP_ARRAY(COMP_CODEC("msm-stub-codec.1", "msm-stub-tx")), DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); +#endif +#if defined(CONFIG_AW882XX_STEREO_SMARTPA_PRI) SND_SOC_DAILINK_DEFS(sec_mi2s_rx, DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.2")), DAILINK_COMP_ARRAY(COMP_CODEC("msm-stub-codec.1", "msm-stub-rx")), DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); - SND_SOC_DAILINK_DEFS(sec_mi2s_tx, DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.3")), DAILINK_COMP_ARRAY(COMP_CODEC("msm-stub-codec.1", "msm-stub-tx")), DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); +#elif defined(CONFIG_AW882XX_STEREO_SMARTPA) +SND_SOC_DAILINK_DEFS(sec_mi2s_rx, +DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.2")), +DAILINK_COMP_ARRAY(COMP_CODEC("aw882xxacf_smartpa.2-0034", "aw882xx-aif-2-34"), +COMP_CODEC("aw882xxacf_smartpa.2-0035", "aw882xx-aif-2-35")), +DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); +SND_SOC_DAILINK_DEFS(sec_mi2s_tx, +DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.3")), +DAILINK_COMP_ARRAY(COMP_CODEC("aw882xxacf_smartpa.2-0034", "aw882xx-aif-2-34"), +COMP_CODEC("aw882xxacf_smartpa.2-0035", "aw882xx-aif-2-35")), +DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); +#else +SND_SOC_DAILINK_DEFS(sec_mi2s_rx, +DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.2")), +DAILINK_COMP_ARRAY(COMP_CODEC("aw882xx_codec", "aw882xx-aif")), +DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); +SND_SOC_DAILINK_DEFS(sec_mi2s_tx, +DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.3")), +DAILINK_COMP_ARRAY(COMP_CODEC("aw882xx_codec", "aw882xx-aif")), +DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); +#endif SND_SOC_DAILINK_DEFS(tert_mi2s_rx, DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-mi2s.4")), @@ -469,3 +503,15 @@ SND_SOC_DAILINK_DEFS(proxy_rx, DAILINK_COMP_ARRAY(COMP_CPU("msm-dai-q6-dev.8194")), DAILINK_COMP_ARRAY(COMP_CODEC("msm-stub-codec.1", "msm-stub-rx")), DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-routing"))); + +#if defined(CONFIG_AW882XX_STEREO_SMARTPA_PRI) +SND_SOC_DAILINK_DEFS(pri_mi2s_tx_hostless, + DAILINK_COMP_ARRAY(COMP_CPU("PRI_MI2S_TX_HOSTLESS")), + DAILINK_COMP_ARRAY(COMP_CODEC("snd-soc-dummy", "snd-soc-dummy-dai")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-hostless"))); +#else +SND_SOC_DAILINK_DEFS(sec_mi2s_tx_hostless, + DAILINK_COMP_ARRAY(COMP_CPU("SEC_MI2S_TX_HOSTLESS")), + DAILINK_COMP_ARRAY(COMP_CODEC("snd-soc-dummy", "snd-soc-dummy-dai")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("msm-pcm-hostless"))); +#endif diff --git a/techpack/audio/config/holiauto.conf b/techpack/audio/config/holiauto.conf index d0d6dd0456c3..c845fae1986c 100644 --- a/techpack/audio/config/holiauto.conf +++ b/techpack/audio/config/holiauto.conf @@ -33,6 +33,5 @@ export CONFIG_WSA881X_TEMP_SENSOR_DISABLE=m export CONFIG_SND_SOC_HOLI=m export CONFIG_SND_EVENT=m export CONFIG_TDM_DISABLE=m -export CONFIG_MI2S_DISABLE=m export CONFIG_AUXPCM_DISABLE=m export CONFIG_DIGITAL_CDC_RSC_MGR=m diff --git a/techpack/audio/config/holiautoconf.h b/techpack/audio/config/holiautoconf.h index 2eb9a6d5a432..b31857b6ffd7 100644 --- a/techpack/audio/config/holiautoconf.h +++ b/techpack/audio/config/holiautoconf.h @@ -37,6 +37,5 @@ #define CONFIG_SND_SOC_HOLI 1 #define CONFIG_SND_EVENT 1 #define CONFIG_TDM_DISABLE 1 -#define CONFIG_MI2S_DISABLE 1 #define CONFIG_AUXPCM_DISABLE 1 #define CONFIG_DIGITAL_CDC_RSC_MGR 1 diff --git a/techpack/audio/dsp/Kbuild b/techpack/audio/dsp/Kbuild index a0979c03c702..57d811ebd84d 100755 --- a/techpack/audio/dsp/Kbuild +++ b/techpack/audio/dsp/Kbuild @@ -13,6 +13,24 @@ ifeq ($(KERNEL_BUILD), 1) AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio endif +EXTRA_CFLAGS += -DCONFIG_SND_SOC_AWINIC_AW882XX + +ifeq ($(CONFIG_AW882XX_PARAMS_FROM_BIN), y) +CDEFINES += -DCONFIG_AW882XX_PARAMS_STORED_IN_BIN +endif + +ifeq ($(CONFIG_AW882XX_MAPSIZE_16K), y) +CDEFINES += -DCONFIG_AW882XX_MAPSIZE_16K +endif + +ifdef CONFIG_SND_SOC_AW87XXX +CDEFINES += -DCONFIG_SND_SOC_AW87XXX +endif + +ifdef CONFIG_SND_SOC_FS1815 +CDEFINES += -DCONFIG_SND_SOC_FS1815 +endif + ifeq ($(CONFIG_SND_SOC_AUTO), y) ifdef CONFIG_SND_SOC_SA8155 include $(AUDIO_ROOT)/config/sa8155auto.conf diff --git a/techpack/audio/dsp/q6adm.c b/techpack/audio/dsp/q6adm.c index 7455252e2f08..cd876fde3f37 100644 --- a/techpack/audio/dsp/q6adm.c +++ b/techpack/audio/dsp/q6adm.c @@ -3507,7 +3507,8 @@ int adm_open_v2(int port_id, int path, int rate, int channel_mode, int topology, if (topology == VPM_TX_VOICE_SMECNS_V2_COPP_TOPOLOGY || topology == VPM_TX_VOICE_FLUENCE_SM_COPP_TOPOLOGY || topology == VPM_TX_VOICE_FLUENCE_NN_COPP_TOPOLOGY || - topology == AUDIO_RX_MONO_VOIP_COPP_TOPOLOGY) + topology == AUDIO_RX_MONO_VOIP_COPP_TOPOLOGY || + topology == VOICE_TX_FNN_COPP_TOPOLOGY) channel_mode = 1; /* @@ -5179,6 +5180,49 @@ int adm_set_ffecns_freeze_event(bool ffecns_freeze_event) return rc; } EXPORT_SYMBOL(adm_set_ffecns_freeze_event); +#ifdef CONFIG_SND_SOC_AWINIC_AW882XX +int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable) +{ + int copp_idx = 0; + uint32_t enable_param; + struct param_hdr_v3 param_hdr; + int rc = 0; + + pr_debug("%s port_id %d, module_id 0x%x, enable %d\n", + __func__, port_id, module_id, enable); + + copp_idx = adm_get_default_copp_idx(port_id); + + if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) { + pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx); + return -EINVAL; + } + + if (enable < 0 || enable > 1) { + pr_err("%s: Invalid value for enable %d\n", __func__, enable); + return -EINVAL; + } + + pr_debug("%s port_id %d, module_id 0x%x, copp_idx 0x%x, enable %d\n", + __func__, port_id, module_id, copp_idx, enable); + + memset(¶m_hdr, 0, sizeof(param_hdr)); + param_hdr.module_id = module_id; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_id = param_id; + param_hdr.param_size = sizeof(enable_param); + enable_param = enable; + + rc = adm_pack_and_set_one_pp_param(port_id, copp_idx, param_hdr, + (uint8_t *) &enable_param); + + if (rc) + pr_err("%s: Failed to set enable of module(%d) instance(%d) to %d, err %d\n", + __func__, module_id, INSTANCE_ID_0, enable, rc); + return rc; +} +EXPORT_SYMBOL(aw_adm_param_enable); +#endif /** * adm_param_enable - diff --git a/techpack/audio/dsp/q6afe.c b/techpack/audio/dsp/q6afe.c index d5c16bfc5829..7cb45d09e9ae 100644 --- a/techpack/audio/dsp/q6afe.c +++ b/techpack/audio/dsp/q6afe.c @@ -24,6 +24,32 @@ #include "adsp_err.h" #include "q6afecal-hwdep.h" +#ifdef CONFIG_SND_SOC_FS1815 +#include +#endif + + + +#ifdef CONFIG_SND_SOC_AWINIC_AW882XX +#define AFE_MODULE_ID_AWDSP_TX (0x10013D00) +#define AFE_MODULE_ID_AWDSP_RX (0x10013D01) +#define AFE_PARAM_ID_AWDSP_RX_SET_ENABLE (0x10013D11) +#define AFE_PARAM_ID_AWDSP_TX_SET_ENABLE (0x10013D13) +#define AFE_PARAM_ID_AWDSP_RX_PARAMS (0x10013D12) + +void aw_cal_unmap_memory(void); +#endif /* #ifdef CONFIG_SND_SOC_AWINIC_AW882XX */ + +#ifdef CONFIG_SND_SOC_AW87XXX +static int g_rx_port_id = 0; + +void aw_set_port_id(int rx_port_id) +{ + g_rx_port_id = rx_port_id; +} +EXPORT_SYMBOL(aw_set_port_id); +#endif /* #ifdef CONFIG_SND_SOC_AW87XXX */ + #define WAKELOCK_TIMEOUT 5000 #define AFE_CLK_TOKEN 1024 #define AFE_NOWAIT_TOKEN 2048 @@ -275,6 +301,10 @@ struct afe_ctl { uint32_t cps_ch_mask; struct afe_cps_hw_intf_cfg *cps_config; int lsm_afe_ports[MAX_LSM_SESSIONS]; +#ifdef CONFIG_SND_SOC_AWINIC_AW882XX + struct rtac_cal_block_data aw_cal; + atomic_t aw_state; +#endif /*CONFIG_SND_SOC_AWINIC_AW882XX*/ }; struct afe_clkinfo_per_port { @@ -596,6 +626,7 @@ int afe_get_topology(int port_id) done: return topology; } +EXPORT_SYMBOL(afe_get_topology); /** * afe_set_aanc_info - @@ -1078,6 +1109,10 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) return -EINVAL; } if (data->opcode == RESET_EVENTS) { + #ifdef CONFIG_SND_SOC_AWINIC_AW882XX + aw_cal_unmap_memory(); + #endif /*CONFIG_SND_SOC_AWINIC_AW882XX*/ + pr_debug("%s: reset event = %d %d apr[%pK]\n", __func__, data->reset_event, data->reset_proc, this_afe.apr); @@ -1153,7 +1188,31 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) payload, data->token); return -EINVAL; } +#ifdef CONFIG_SND_SOC_AWINIC_AW882XX + if (atomic_read(&this_afe.aw_state) == 1) { + if (!payload[0]) { + atomic_set(&this_afe.state, 0); + } else { + pr_debug("%s: status: %d", __func__, payload[0]); + atomic_set(&this_afe.state, -1); + } + atomic_set(&this_afe.aw_state, 0); + wake_up(&this_afe.wait[data->token]); + return 0; + } +#endif /*CONFIG_SND_SOC_AWINIC_AW882XX*/ +#ifdef CONFIG_SND_SOC_FS1815 + if (!q6fsm_afe_callback(data->payload, data->payload_size)) { + atomic_set(&this_afe.state, 0); + if (afe_token_is_valid(data->token)) { + wake_up(&this_afe.wait[data->token]); + return 0; + } else { + return -EINVAL; + } + } +#endif if (rtac_make_afe_callback(data->payload, data->payload_size)) return 0; @@ -2547,6 +2606,15 @@ static int afe_spk_prot_prepare(int src_port, int dst_port, int param_id, case AFE_PARAM_ID_SP_V4_EX_VI_FTM_CFG: param_info.module_id = AFE_MODULE_SPEAKER_PROTECTION_V4_VI; break; +#ifdef CONFIG_SND_SOC_AWINIC_AW882XX + case AFE_PARAM_ID_AWDSP_RX_SET_ENABLE: + case AFE_PARAM_ID_AWDSP_RX_PARAMS: + param_info.module_id = AFE_MODULE_ID_AWDSP_RX; + break; + case AFE_PARAM_ID_AWDSP_TX_SET_ENABLE: + param_info.module_id = AFE_MODULE_ID_AWDSP_TX; + break; +#endif /*CONFIG_SND_SOC_AWINIC_AW882XX*/ default: pr_err("%s: default case 0x%x\n", __func__, param_id); goto fail_cmd; @@ -3956,6 +4024,233 @@ done: return ret; } +#ifdef CONFIG_SND_SOC_AWINIC_AW882XX +int aw_send_afe_rx_module_enable(uint32_t rx_port_id, void *buf, int cmd_size) +{ + union afe_spkr_prot_config config; + + if (cmd_size > sizeof(config)) + return -EINVAL; + + memcpy(&config, buf, cmd_size); + + if (afe_spk_prot_prepare(rx_port_id, 0, + AFE_PARAM_ID_AWDSP_RX_SET_ENABLE, &config,sizeof(union afe_spkr_prot_config))) { + pr_err("%s: set bypass failed \n", __func__); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL(aw_send_afe_rx_module_enable); +int aw_send_afe_tx_module_enable(uint32_t tx_port_id, void *buf, int cmd_size) +{ + union afe_spkr_prot_config config; + + if (cmd_size > sizeof(config)) + return -EINVAL; + + memcpy(&config, buf, cmd_size); + + if (afe_spk_prot_prepare(tx_port_id, 0, + AFE_PARAM_ID_AWDSP_TX_SET_ENABLE, &config,sizeof(union afe_spkr_prot_config))) { + pr_err("%s: set bypass failed \n", __func__); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL(aw_send_afe_tx_module_enable); + +#ifdef CONFIG_SND_SOC_AW87XXX +int aw_send_afe_cal_apr(uint32_t param_id, void *buf, int cmd_size, bool write) /*aw87xxx*/ +#else +int aw_send_afe_cal_apr(uint32_t rx_port_id, uint32_t tx_port_id, + uint32_t param_id, void *buf, int cmd_size, bool write) /*aw882xx*/ +#endif +{ + int32_t result = 0; +#ifdef CONFIG_SND_SOC_AW87XXX + uint32_t port_id = g_rx_port_id; +#else + uint32_t port_id = rx_port_id; /*aw882xx*/ +#endif + int32_t module_id = AFE_MODULE_ID_AWDSP_RX; + uint32_t port_index = 0; + uint32_t payload_size = 0; + size_t len; + struct rtac_cal_block_data *aw_cal = &(this_afe.aw_cal); + struct mem_mapping_hdr mem_hdr; + struct param_hdr_v3 param_hdr; + + pr_debug("%s: enter\n", __func__); + +#ifndef CONFIG_SND_SOC_AW87XXX + if (param_id == AFE_PARAM_ID_AWDSP_TX_SET_ENABLE) { + port_id = tx_port_id; + module_id = AFE_MODULE_ID_AWDSP_TX; + } +#endif + + if (aw_cal->map_data.dma_buf == 0) { + /*Minimal chunk size is 8K*/ +#ifdef CONFIG_AW882XX_MAPSIZE_16K + aw_cal->map_data.map_size = SZ_16K; +#else + aw_cal->map_data.map_size = SZ_8K; +#endif + result = msm_audio_ion_alloc(&(aw_cal->map_data.dma_buf), + aw_cal->map_data.map_size, + &(aw_cal->cal_data.paddr),&len, + &(aw_cal->cal_data.kvaddr)); + if (result < 0) { + pr_err("%s: allocate buffer failed! ret = %d\n", + __func__, result); + goto err; + } + } + + if (aw_cal->map_data.map_handle == 0) { + result = afe_map_rtac_block(aw_cal); + if (result < 0) { + pr_err("%s: map buffer failed! ret = %d\n", + __func__, result); + goto err; + } + } + + port_index = q6audio_get_port_index(port_id); + if (port_index >= AFE_MAX_PORTS) { + pr_err("%s: Invalid AFE port = 0x%x\n", __func__, port_id); + goto err; + } +#ifdef CONFIG_AW882XX_MAPSIZE_16K + if (cmd_size > (SZ_16K - sizeof(struct param_hdr_v3))) { +#else + if (cmd_size > (SZ_8K - sizeof(struct param_hdr_v3))) { +#endif + pr_err("%s: Invalid payload size = %d\n", __func__, cmd_size); + result = -EINVAL; + goto err; + } + + /* Pack message header with data */ + param_hdr.module_id = module_id; + param_hdr.instance_id = INSTANCE_ID_0; + param_hdr.param_size = cmd_size; + + if (write) { + param_hdr.param_id = param_id; + q6common_pack_pp_params(aw_cal->cal_data.kvaddr, + ¶m_hdr, + buf, + &payload_size); + aw_cal->cal_data.size = payload_size; + } else { + param_hdr.param_id = param_id; + aw_cal->cal_data.size = cmd_size + sizeof(struct param_hdr_v3); + } + + /*Send/Get package to/from ADSP*/ + mem_hdr.data_payload_addr_lsw = + lower_32_bits(aw_cal->cal_data.paddr); + mem_hdr.data_payload_addr_msw = + msm_audio_populate_upper_32_bits(aw_cal->cal_data.paddr); + mem_hdr.mem_map_handle = + aw_cal->map_data.map_handle; + + pr_debug("%s: Sending aw_cal port = 0x%x, cal size = %zd, cal addr = 0x%pK\n", + __func__, port_id, aw_cal->cal_data.size, &aw_cal->cal_data.paddr); + + result = afe_q6_interface_prepare(); + if (result != 0) { + pr_err("%s: Q6 interface prepare failed %d\n", __func__, result); + goto err; + } + + if (write) { + if (q6common_is_instance_id_supported()) + result = q6afe_set_params_v3(port_id, port_index, &mem_hdr, NULL, payload_size); + else + result = q6afe_set_params_v2(port_id, port_index, &mem_hdr, NULL, payload_size); + } else { + int8_t *resp = (int8_t *)aw_cal->cal_data.kvaddr; + + atomic_set(&this_afe.aw_state, 1); + if (q6common_is_instance_id_supported()) { + result = q6afe_get_params_v3(port_id, port_index, &mem_hdr, ¶m_hdr); + resp += sizeof(struct param_hdr_v3); + } else { + result = q6afe_get_params_v2(port_id, port_index, &mem_hdr, ¶m_hdr); + resp += sizeof(struct param_hdr_v1); + } + + if (result) { + pr_err("%s: get response from port 0x%x failed %d\n", + __func__, port_id, result); + goto err; + } + else { + /*Copy response data to command buffer*/ + memcpy(buf, resp, cmd_size); + } + } +err: + return result; +} +EXPORT_SYMBOL(aw_send_afe_cal_apr); + +void aw_cal_unmap_memory(void) +{ + int result = 0; + + if (this_afe.aw_cal.map_data.map_handle) { + result = afe_unmap_rtac_block(&this_afe.aw_cal.map_data.map_handle); + + /*Force to remap after unmap failed*/ + if (result) + this_afe.aw_cal.map_data.map_handle = 0; + } +} +EXPORT_SYMBOL(aw_cal_unmap_memory); + +int aw_send_rx_module_enable(uint32_t rx_port_id, void *buf, int cmd_size) +{ + union afe_spkr_prot_config config; + + if (cmd_size > sizeof(config)) + return -EINVAL; + + memcpy(&config, buf, cmd_size); + if (afe_spk_prot_prepare(rx_port_id, 0, + AFE_PARAM_ID_AWDSP_RX_SET_ENABLE, + &config,sizeof(union afe_spkr_prot_config))) { + pr_err("%s: AW set rx bypass failed\n", + __func__); + } + return 0; +} +EXPORT_SYMBOL(aw_send_rx_module_enable); + +int aw_send_tx_module_enable(uint32_t tx_port_id, void *buf, int cmd_size) +{ + union afe_spkr_prot_config config; + + if (cmd_size > sizeof(config)) + return -EINVAL; + + memcpy(&config, buf, cmd_size); + if (afe_spk_prot_prepare(tx_port_id, 0, + AFE_PARAM_ID_AWDSP_TX_SET_ENABLE, + &config,sizeof(union afe_spkr_prot_config))) { + pr_err("%s: AW set tx module enable failed\n", + __func__); + } + return 0; +} +EXPORT_SYMBOL(aw_send_tx_module_enable); + +#endif + + static int afe_init_cdc_reg_config(void) { struct param_hdr_v3 param_hdr; @@ -4404,6 +4699,16 @@ static int afe_send_cmd_port_start(u16 port_id) pr_err("%s: AFE enable for port 0x%x failed %d\n", __func__, port_id, ret); +#ifdef CONFIG_SND_SOC_FS1815 + //TODO: + // Set the monitor enable when: + // 1) Speaker PA type is matched + // 2) In speaker playback + // 3) The RX Port is the same as the one of speaker playback + if (port_id == Q6FSM_AFE_RX_PORT) + q6fsm_monitor_switch(true); +#endif + return ret; } @@ -9459,6 +9764,11 @@ int afe_close(int port_id) memset(&stop, 0, sizeof(stop)); +#ifdef CONFIG_SND_SOC_FS1815 + if (port_id == Q6FSM_AFE_RX_PORT) + q6fsm_monitor_switch(false); +#endif + if (this_afe.apr == NULL) { pr_err("%s: AFE is already closed\n", __func__); @@ -11930,6 +12240,9 @@ static int afe_unmap_cal_data(int32_t cal_type, ret = afe_cmd_memory_unmap( cal_block->map_data.q6map_handle); atomic_set(&this_afe.mem_map_cal_index, -1); +#if defined(CONFIG_AW882XX_PARAMS_STORED_IN_BIN) || defined(CONFIG_SND_SOC_AW87XXX) + atomic_set(&this_afe.mem_map_cal_handles[cal_index],0); +#endif if (ret < 0) { pr_err("%s: unmap did not work! cal_type %i ret %d\n", __func__, cal_index, ret); @@ -12191,11 +12504,17 @@ int __init afe_init(void) this_afe.event_notifier.notifier_call = afe_aud_event_notify; msm_aud_evt_blocking_register_client(&this_afe.event_notifier); +#ifdef CONFIG_SND_SOC_FS1815 + q6fsm_afe_init(); +#endif return 0; } void afe_exit(void) { +#ifdef CONFIG_SND_SOC_FS1815 + q6fsm_afe_deinit(); +#endif if (this_afe.apr) { apr_reset(this_afe.apr); atomic_set(&this_afe.state, 0); @@ -12205,6 +12524,9 @@ void afe_exit(void) q6core_destroy_uevent_data(this_afe.uevent_data); +#ifdef CONFIG_SND_SOC_AWINIC_AW882XX + aw_cal_unmap_memory(); +#endif /*CONFIG_SND_SOC_AWINIC_AW882XX*/ afe_delete_cal_data(); config_debug_fs_exit(); diff --git a/techpack/audio/dsp/q6lsm.c b/techpack/audio/dsp/q6lsm.c index aa4127c1ae82..b1eaf791f79a 100644 --- a/techpack/audio/dsp/q6lsm.c +++ b/techpack/audio/dsp/q6lsm.c @@ -247,6 +247,7 @@ static int q6lsm_callback(struct apr_client_data *data, void *priv) ret = -EINVAL; goto done; } + memcpy((u8 *)client->get_param_payload, (u8 *)payload + payload_min_size_expected, param_size); done: @@ -538,6 +539,7 @@ static int q6lsm_apr_send_pkt(struct lsm_client *client, void *handle, if (wait) mutex_unlock(&lsm_common.apr_lock); + mmap_handle_p = NULL; if (mmap_p && *mmap_p == 0) ret = -ENOMEM; mmap_handle_p = NULL; diff --git a/techpack/audio/include/dsp/apr_audio-v2.h b/techpack/audio/include/dsp/apr_audio-v2.h index 79e258589e1d..a546339d81b1 100644 --- a/techpack/audio/include/dsp/apr_audio-v2.h +++ b/techpack/audio/include/dsp/apr_audio-v2.h @@ -5798,6 +5798,7 @@ struct afe_param_id_lpass_core_shared_clk_cfg { #define VPM_TX_DM_FLUENCE_EF_COPP_TOPOLOGY 0x10000005 #define AUDIO_RX_MONO_VOIP_COPP_TOPOLOGY 0x11000101 #define VPM_TX_VOICE_FLUENCE_NN_COPP_TOPOLOGY 0x10028008 +#define VOICE_TX_FNN_COPP_TOPOLOGY 0x10010B0F /* Memory map regions command payload used by the * #ASM_CMD_SHARED_MEM_MAP_REGIONS ,#ADM_CMD_SHARED_MEM_MAP_REGIONS diff --git a/techpack/audio/include/dsp/q6fsm-v3.h b/techpack/audio/include/dsp/q6fsm-v3.h new file mode 100644 index 000000000000..0cd53fc0f581 --- /dev/null +++ b/techpack/audio/include/dsp/q6fsm-v3.h @@ -0,0 +1,776 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/** + * Copyright (C) Shanghai FourSemi Semiconductor Co.,Ltd 2016-2022. All rights reserved. + * 2022-12-23 File created. + */ + +#ifndef __Q6FSM_V3_H__ +#define __Q6FSM_V3_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define fsm_info(fmt, args...) pr_info("%s: " fmt "\n", __func__, ##args) +#define fsm_dbg(fmt, args...) pr_debug("%s: " fmt "\n", __func__, ##args) +#define fsm_err(fmt, args...) pr_err("%s: " fmt "\n", __func__, ##args) + +#define Q6FSM_VERSION "v1.1.1" + +// TODO update +#define Q6FSM_AFE_RX_PORT (AFE_PORT_ID_RX_CODEC_DMA_RX_0) +#define Q6FSM_AFE_TX_PORT (AFE_PORT_ID_TX_CODEC_DMA_TX_0) + +#define Q6FSM_CHUNK_SIZE (256) + +// FSADSP MODULE ID +#define AFE_MODULE_ID_FSADSP_TX (0x10001110) +#define AFE_MODULE_ID_FSADSP_RX (0x10001111) +// FSADSP PARAM ID +#define CAPI_V2_PARAM_FSADSP_TX_ENABLE 0x11111601 +#define CAPI_V2_PARAM_FSADSP_RX_ENABLE 0x11111611 +#define CAPI_V2_PARAM_FSADSP_MODULE_ENABLE 0x10001FA1 +#define CAPI_V2_PARAM_FSADSP_ROTATION 0x10001FB1 +#define CAPI_V2_PARAM_FSADSP_BSG_VBAT 0x10001FB2 +#define CAPI_V2_PARAM_FSADSP_BSGV2_PARAM 0x10001FB5 //set +#define CAPI_V2_PARAM_FSADSP_BSGV2_CONFIG 0x10001FB6 //get +#define CAPI_V2_PARAM_FSADSP_ROTATION 0x10001FB1 +#define CAPI_V2_PARAM_FSADSP_FADE 0x10001FB7 + +#define Q6FSM_RETRY_TIME (1) +#define Q6FSM_SLEEP_TIME (10) + +struct bsg_config_v2 { + int bsg_mode; + int bsg_enable; + int bsg_interval; + int tc_mode; + int tc_mode_enable; + int tc_interval; +}; + +struct bsg_param_v2 { + int bsg_mode; + int bsg_val; + int tc_mode; + int tc_val; +}; + +struct q6fsm_afe { + struct workqueue_struct *vbat_wq; + struct delayed_work vbat_work; + struct power_supply *psy; + struct mutex cmd_lock; + atomic_t bsg_switch; + unsigned long delay_period; + bool monitor_en; + int bsg_version; + struct bsg_config_v2 config; +}; + +typedef enum _fs__chann_t +{ + FS_CH_LEFT = 1 << 0, + FS_CH_RIGHT = 1 << 1, + FS_CH_MONO = (FS_CH_LEFT|FS_CH_RIGHT), +}fs_chann_t; + +typedef struct _fsm_fade_info_t +{ + int fade_type; //0x10 mute, 0x11 fade in,0x12 fade out + int fade_time; // fade length time, unit is ms + int start_db; + fs_chann_t ch; // left or right channel +}fsm_fade_info_t; + +typedef struct _fsm_rotation_info_t +{ + int angle; // 0,90,180,270 + int8_t ch_sequence[8]; +}fsm_rotation_info_t; + + +static struct q6fsm_afe *g_fsm_afe; +static uint32_t *q6fsm_get_buffer; +static bool fsm_scene_status = false; + +static int q6afe_set_params(u16 port_id, int index, + struct mem_mapping_hdr *mem_hdr, + u8 *packed_param_data, u32 packed_data_size); +static int q6afe_get_params(u16 port_id, + struct mem_mapping_hdr *mem_hdr, + struct param_hdr_v3 *param_hdr); + +static int q6fsm_afe_callback(void *payload, int size) +{ + uint32_t *payload32 = payload; + + if (payload32[0] != 0) { + pr_err("invalid status: %d", payload32[0]); + return -EINVAL; + } + + fsm_dbg("payload size: %d", size); + switch (payload32[1]) { + case AFE_MODULE_ID_FSADSP_TX: + case AFE_MODULE_ID_FSADSP_RX: + memcpy(q6fsm_get_buffer, payload32 + 1, size - 4); + break; + default: + return -EINVAL; + } + + return 0; +} + +static bool q6fsm_check_dsp_ready(void) +{ + int topo_id; + + topo_id = afe_get_topology(Q6FSM_AFE_RX_PORT); + fsm_dbg("topo_id: 0x%x", topo_id); + + if (topo_id <= 0) + return false; + else + return true; +} + +static int q6fsm_afe_set_params(struct param_hdr_v3 *param_hdr, + uint8_t *params, uint32_t param_size) +{ + u8 *packed_param_data; + int packed_data_size; + int port_id, index; + int retry = 0; + int ret; + + if (param_hdr == NULL || params == NULL) { + fsm_err("Bad parameters"); + return -EINVAL; + } + + if (param_size >= Q6FSM_CHUNK_SIZE) { + fsm_err("Invalid param_size: %d\n", param_size); + return -EINVAL; + } + + if (param_hdr->module_id == AFE_MODULE_ID_FSADSP_RX) + port_id = Q6FSM_AFE_RX_PORT; + else + port_id = Q6FSM_AFE_TX_PORT; + + index = afe_get_port_index(port_id); + if (index < 0) { + fsm_err("invalid port: %x", port_id); + return -EINVAL; + } + + mutex_lock(&g_fsm_afe->cmd_lock); + packed_data_size = sizeof(union param_hdrs) + param_size; + packed_param_data = kzalloc(packed_data_size, GFP_KERNEL); + if (packed_param_data == NULL) { + fsm_err("alloc memory failed!"); + mutex_unlock(&g_fsm_afe->cmd_lock); + return -ENOMEM; + } + + do { + ret = q6common_pack_pp_params(packed_param_data, param_hdr, + params, &packed_data_size); + if (ret) { + fsm_err("Failed to pack data: %d", ret); + break; + } + while (retry++ < Q6FSM_RETRY_TIME) { + if (q6fsm_check_dsp_ready()) { + ret = q6afe_set_params(port_id, index, NULL, + packed_param_data, packed_data_size); + break; + } + msleep(Q6FSM_SLEEP_TIME); + ret = -ETIMEDOUT; + } + } while (0); + if (ret) + fsm_err("Failed to set params: %d", ret); + + mutex_unlock(&g_fsm_afe->cmd_lock); + kfree(packed_param_data); + packed_param_data = NULL; + + return ret; +} + +static int q6fsm_afe_get_params(struct param_hdr_v3 *param_hdr, + uint8_t *params, uint32_t length) +{ + int port_id; + int retry = 0; + int ret; + + if (param_hdr == NULL || params == NULL) { + fsm_err("Bad parameters"); + return -EINVAL; + } + + mutex_lock(&g_fsm_afe->cmd_lock); + q6fsm_get_buffer = kzalloc(length + 16, GFP_KERNEL); + if (q6fsm_get_buffer == NULL) { + fsm_err("Memory allocation failed!"); + mutex_unlock(&g_fsm_afe->cmd_lock); + return -ENOMEM; + } + + if (param_hdr->module_id == AFE_MODULE_ID_FSADSP_RX) + port_id = Q6FSM_AFE_RX_PORT; + else + port_id = Q6FSM_AFE_TX_PORT; + + do { + while (retry++ < Q6FSM_RETRY_TIME) { + if (q6fsm_check_dsp_ready()) { + ret = q6afe_get_params(port_id, NULL, param_hdr); + break; + } + msleep(Q6FSM_SLEEP_TIME); + ret = -ETIMEDOUT; + } + if (!ret) + memcpy(params, &q6fsm_get_buffer[4], length); + else + fsm_err("Get params failed: %d", ret); + } while (0); + + kfree(q6fsm_get_buffer); + q6fsm_get_buffer = NULL; + mutex_unlock(&g_fsm_afe->cmd_lock); + + return ret; +} + +bool q6fsm_get_rx_status(void) +{ + struct param_hdr_v3 param_hdr; + uint32_t enable; + int ret; + + if(fsm_scene_status == false){ + return -EINVAL; + } + + if (!q6fsm_check_dsp_ready()) { + fsm_info("DSP not ready yet!"); + return -EINVAL; + } + + param_hdr.module_id = AFE_MODULE_ID_FSADSP_RX; + param_hdr.instance_id = 0; + param_hdr.reserved = 0; + param_hdr.param_id = CAPI_V2_PARAM_FSADSP_RX_ENABLE; + param_hdr.param_size = sizeof(enable); + + ret = q6fsm_afe_get_params(¶m_hdr, + (uint8_t *)&enable, sizeof(enable)); + if (ret) { + fsm_err("Get params failed: %d", ret); + return false; + } + + return !!enable; +} +EXPORT_SYMBOL(q6fsm_get_rx_status); + +int q6fsm_set_rx_enable(int enable) +{ + struct param_hdr_v3 param_hdr; + uint32_t param; + int ret; + + if(fsm_scene_status == false){ + return -EINVAL; + } + + if (!q6fsm_check_dsp_ready()) { + fsm_info("DSP not ready yet!"); + return -EINVAL; + } + + param = (uint32_t)enable; + param_hdr.module_id = AFE_MODULE_ID_FSADSP_RX; + param_hdr.instance_id = 0; + param_hdr.reserved = 0; + param_hdr.param_id = CAPI_V2_PARAM_FSADSP_RX_ENABLE; + param_hdr.param_size = sizeof(param); + ret = q6fsm_afe_set_params(¶m_hdr, + (uint8_t *)¶m, sizeof(param)); + if (ret) { + fsm_err("Set params fail:%d", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(q6fsm_set_rx_enable); + +int q6fsm_set_rotation(int angle) +{ + struct param_hdr_v3 param_hdr; + fsm_rotation_info_t param = {0}; + int ret; + + + if (!q6fsm_get_rx_status()) { + fsm_info("RX module isn't ready"); + return -EINVAL; + } + + if(angle == 0){ + param.angle = 0; + param.ch_sequence[0] = 0; + param.ch_sequence[1] = 1; + }else if(angle == 1){ + param.angle = 180; + param.ch_sequence[0] = 1; + param.ch_sequence[1] = 0; + }else{ + fsm_err("not support angle:%d",angle); + return -EINVAL; + } + + param_hdr.module_id = AFE_MODULE_ID_FSADSP_RX; + param_hdr.instance_id = 0; + param_hdr.reserved = 0; + param_hdr.param_id = CAPI_V2_PARAM_FSADSP_ROTATION; + param_hdr.param_size = sizeof(fsm_rotation_info_t); + ret = q6fsm_afe_set_params(¶m_hdr, + (uint8_t *)¶m, sizeof(fsm_rotation_info_t)); + if (ret) { + fsm_err("Set params fail:%d", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(q6fsm_set_rotation); + + +void q6fsm_fsm_check_scene_status(bool status) +{ + fsm_scene_status = status; +} +EXPORT_SYMBOL(q6fsm_fsm_check_scene_status); + +#define PSY_DESC psy->desc + +static int q6fsm_get_batv(struct q6fsm_afe *q6fsm, uint32_t *batv) +{ + union power_supply_propval prop = { 0 }; + struct power_supply *psy; + + if (q6fsm == NULL || batv == NULL) { + fsm_err("Bad paramters"); + return -EINVAL; + } + + q6fsm->psy = power_supply_get_by_name("battery"); + if (q6fsm->psy == NULL) { + fsm_err("get power supply failed"); + return -EINVAL; + } + + psy = q6fsm->psy; + if (PSY_DESC == NULL || PSY_DESC->get_property == NULL) { + fsm_err("Power desc/get_property is null!"); + return -EINVAL; + } + PSY_DESC->get_property(psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + *batv = prop.intval; + fsm_dbg("voltage: %d", *batv); + + return 0; +} + +static int q6fsm_get_bsg_config(struct q6fsm_afe *q6fsm) +{ + struct param_hdr_v3 param_hdr; + struct bsg_config_v2 *config; + int interval; + int ret; + + if (q6fsm == NULL) + return -EINVAL; + + if (q6fsm->delay_period > 0) + return 0; + + param_hdr.module_id = AFE_MODULE_ID_FSADSP_RX; + param_hdr.instance_id = 0; + param_hdr.reserved = 0; + param_hdr.param_id = CAPI_V2_PARAM_FSADSP_BSGV2_CONFIG; + param_hdr.param_size = sizeof(q6fsm->config); + ret = q6fsm_afe_get_params(¶m_hdr, + (uint8_t *)&q6fsm->config, sizeof(q6fsm->config)); + if (ret) { + fsm_err("Get params fail:%d", ret); + return ret; + } + + config = &q6fsm->config; + if (!config->bsg_enable && !config->tc_mode_enable) { + fsm_info("BSG/CSG is disabled"); + return 0x1000; /* Not enabled, stop the monitor */ + } + + if (config->bsg_enable) + interval = config->bsg_interval; + if (config->tc_mode_enable) + interval = config->tc_interval; + + fsm_info("interval: %d", interval); + q6fsm->delay_period = interval*HZ/1000; + + return 0; +} + +static int q6fsm_gen_bsg_param(struct q6fsm_afe *q6fsm, + struct bsg_param_v2 *param) +{ + union power_supply_propval prop = { 0 }; + struct power_supply *psy; + + if (q6fsm == NULL || param == NULL) { + fsm_err("Bad paramters"); + return -EINVAL; + } + + q6fsm->psy = power_supply_get_by_name("battery"); + if (q6fsm->psy == NULL) { + fsm_err("get power supply failed"); + return -EINVAL; + } + + psy = q6fsm->psy; + if (PSY_DESC == NULL || PSY_DESC->get_property == NULL) { + fsm_err("Power desc/get_property is null!"); + return -EINVAL; + } + + param->bsg_mode = q6fsm->config.bsg_mode; + if (param->bsg_mode == 1) { + PSY_DESC->get_property(psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + param->bsg_val = prop.intval; + } else if (param->bsg_mode == 2) { + PSY_DESC->get_property(psy, + POWER_SUPPLY_PROP_CAPACITY, &prop); + param->bsg_val = prop.intval; + } else { + param->bsg_val = 0; + } + + param->tc_mode = q6fsm->config.tc_mode; + if (param->tc_mode == 3) { + PSY_DESC->get_property(psy, + POWER_SUPPLY_PROP_TEMP, &prop); + param->tc_val = prop.intval; + } else { + param->tc_val = 0; + } + + fsm_dbg("bsg:%d-%d, tc:%d-%d", + param->bsg_mode, param->bsg_val, + param->tc_mode, param->tc_val); + + return 0; +} + +static int q6fsm_vbat_monitor_work_v2(struct q6fsm_afe *q6fsm) +{ + struct param_hdr_v3 param_hdr; + struct bsg_param_v2 param; + int ret; + + if (q6fsm == NULL) + return -EINVAL; + + ret = q6fsm_get_bsg_config(q6fsm); + if (ret) { + if (ret != 0x1000) + fsm_err("Get bsg config fail:%d", ret); + return ret; + } + + ret = q6fsm_gen_bsg_param(q6fsm, ¶m); + if (ret) { + fsm_err("Get batt voltage fail:%d", ret); + return ret; + } + + param_hdr.module_id = AFE_MODULE_ID_FSADSP_RX; + param_hdr.instance_id = 0; + param_hdr.reserved = 0; + param_hdr.param_id = CAPI_V2_PARAM_FSADSP_BSGV2_PARAM; + param_hdr.param_size = sizeof(param); + ret = q6fsm_afe_set_params(¶m_hdr, + (uint8_t *)¶m, sizeof(param)); + if (ret) { + fsm_err("Set params fail:%d", ret); + return ret; + } + + return 0; +} + +static int q6fsm_vbat_monitor_work(struct q6fsm_afe *q6fsm) +{ + struct param_hdr_v3 param_hdr; + uint32_t batv; + int ret; + + if (q6fsm == NULL) + return -EINVAL; + + if (!q6fsm_get_rx_status()) { + fsm_info("RX module isn't ready"); + return -EINVAL; + } + + if (q6fsm->bsg_version == 2) { + ret = q6fsm_vbat_monitor_work_v2(q6fsm); + if (!ret || ret == 0x1000) + return ret; + } + + q6fsm->bsg_version = 1; + q6fsm->delay_period = 2*HZ; + + ret = q6fsm_get_batv(q6fsm, &batv); + if (ret) { + fsm_err("Get batt voltage fail:%d", ret); + return ret; + } + + param_hdr.module_id = AFE_MODULE_ID_FSADSP_RX; + param_hdr.instance_id = 0; + param_hdr.reserved = 0; + param_hdr.param_id = CAPI_V2_PARAM_FSADSP_BSG_VBAT; + param_hdr.param_size = sizeof(batv); + ret = q6fsm_afe_set_params(¶m_hdr, + (uint8_t *)&batv, sizeof(batv)); + if (ret) { + fsm_err("Set params fail:%d", ret); + return ret; + } + + return 0; +} + +static void q6fsm_vbat_monitor(struct work_struct *work) +{ + struct q6fsm_afe *q6fsm = g_fsm_afe; + int ret; + + if (q6fsm == NULL) + return; + + ret = q6fsm_vbat_monitor_work(q6fsm); + if (ret) + return; + + /* reschedule */ + queue_delayed_work(q6fsm->vbat_wq, + &q6fsm->vbat_work, q6fsm->delay_period); +} + +static void q6fsm_monitor_switch(bool enable) +{ + struct q6fsm_afe *q6fsm = g_fsm_afe; + + if (q6fsm == NULL) + return; + + fsm_info("%s", enable ? "enable" : "disable"); + if (enable) { + if (q6fsm->monitor_en) + return; + if(fsm_scene_status == false){ + fsm_info("no need to start vbat monitor"); + return; + } + q6fsm->monitor_en = true; + queue_delayed_work(q6fsm->vbat_wq, &q6fsm->vbat_work, 0); + } else { + if (!q6fsm->monitor_en) + return; + q6fsm->monitor_en = false; + q6fsm->delay_period = 0; + cancel_delayed_work_sync(&q6fsm->vbat_work); + } +} + +static ssize_t q6fsm_rx_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + struct param_hdr_v3 param_hdr; + int len, enable; + int ret; + + if (!q6fsm_check_dsp_ready()) { + fsm_info("DSP not ready yet!"); + return -EINVAL; + } + + param_hdr.module_id = AFE_MODULE_ID_FSADSP_RX; + param_hdr.instance_id = 0; + param_hdr.reserved = 0; + param_hdr.param_id = CAPI_V2_PARAM_FSADSP_RX_ENABLE; + param_hdr.param_size = sizeof(enable); + + ret = q6fsm_afe_get_params(¶m_hdr, + (uint8_t *)&enable, sizeof(enable)); + if (ret) { + fsm_err("Get params failed: %d", ret); + return ret; + } + len = scnprintf(buf, PAGE_SIZE, "%s\n", + enable ? "On" : "Off"); + + return len; +} + +static ssize_t q6fsm_rx_store(struct class *class, + struct class_attribute *attr, const char *buf, size_t len) +{ + struct param_hdr_v3 param_hdr; + int enable; + int ret = -1; + + if (buf == NULL) + return -ENOMEM; + + enable = simple_strtoul(buf, NULL, 0); + + if (!q6fsm_check_dsp_ready()) { + fsm_info("DSP not ready yet!"); + return ret; + } + + param_hdr.module_id = AFE_MODULE_ID_FSADSP_RX; + param_hdr.instance_id = 0; + param_hdr.reserved = 0; + param_hdr.param_id = CAPI_V2_PARAM_FSADSP_RX_ENABLE; + param_hdr.param_size = sizeof(enable); + enable = !!enable; + + ret = q6fsm_afe_set_params(¶m_hdr, + (uint8_t *)&enable, sizeof(enable)); + if (ret) { + fsm_err("Set params failed: %d", ret); + return ret; + } + + return len; +} + +static ssize_t q6fsm_bsg_show(struct class *class, + struct class_attribute *attr, char *buf) +{ + struct q6fsm_afe *q6fsm = g_fsm_afe; + int len; + + if (q6fsm == NULL) + return -EINVAL; + + len = scnprintf(buf, PAGE_SIZE, "%s\n", + q6fsm->monitor_en ? "On" : "Off"); + + return len; +} + +static ssize_t q6fsm_bsg_store(struct class *class, + struct class_attribute *attr, const char *buf, size_t len) +{ + int enable; + + if (buf == NULL) + return -ENOMEM; + + enable = simple_strtoul(buf, NULL, 0); + q6fsm_monitor_switch(!!enable); + + return len; +} + +static CLASS_ATTR_RW(q6fsm_rx); +static CLASS_ATTR_RW(q6fsm_bsg); + +static struct attribute *q6fsm_class_attrs[] = { + &class_attr_q6fsm_rx.attr, + &class_attr_q6fsm_bsg.attr, + NULL, +}; +ATTRIBUTE_GROUPS(q6fsm_class); + +/** Device model classes */ +struct class g_q6fsm_class = { + .name = "q6fsm", + .class_groups = q6fsm_class_groups, +}; + +static int __init q6fsm_afe_init(void) +{ + struct q6fsm_afe *q6fsm; + + fsm_info("version: %s", Q6FSM_VERSION); + + q6fsm = kzalloc(sizeof(struct q6fsm_afe), GFP_KERNEL); + if (q6fsm == NULL) { + fsm_err("alloc memery failed"); + return -ENOMEM; + } + + mutex_init(&q6fsm->cmd_lock); + atomic_set(&q6fsm->bsg_switch, 0); + + /* BSG V1 */ + //q6fsm->delay_period = 2*HZ; + //q6fsm->bsg_version = 1; + /* BSG V2 */ + q6fsm->delay_period = 0; /* Get from adsp */ + q6fsm->bsg_version = 2; + q6fsm->monitor_en = false; + q6fsm->vbat_wq = create_singlethread_workqueue("q6fsm-vbat"); + INIT_DELAYED_WORK(&q6fsm->vbat_work, q6fsm_vbat_monitor); + /* path: sys/class/$(CLASS NAME) */ + class_register(&g_q6fsm_class); + + g_fsm_afe = q6fsm; + + return 0; +} + +static void q6fsm_afe_deinit(void) +{ + struct q6fsm_afe *q6fsm = g_fsm_afe; + + if (q6fsm == NULL) + return; + + cancel_delayed_work_sync(&q6fsm->vbat_work); + destroy_workqueue(q6fsm->vbat_wq); + mutex_destroy(&q6fsm->cmd_lock); + + kfree(g_fsm_afe); + g_fsm_afe = NULL; +} + +#endif // __Q6FSM_V3_H__ diff --git a/techpack/audio/soc/swr-mstr-ctrl.c b/techpack/audio/soc/swr-mstr-ctrl.c index 7622d18963b6..a77cdfb82583 100644 --- a/techpack/audio/soc/swr-mstr-ctrl.c +++ b/techpack/audio/soc/swr-mstr-ctrl.c @@ -3874,7 +3874,21 @@ static int swrm_suspend(struct device *dev) dev_dbg(swrm->dev, "%s: suspending system, state %d, wlock %d\n", __func__, swrm->pm_state, swrm->wlock_holders); - swrm->pm_state = SWRM_PM_ASLEEP; + /* + * before updating the pm_state to ASLEEP, check if device is + * runtime suspended or not. If it is not, then first make it + * runtime suspend, and then update the pm_state to ASLEEP. + */ + mutex_unlock(&swrm->pm_lock); /* release pm_lock before dev suspend */ + swrm_device_suspend(swrm->dev); /* runtime suspend the device */ + mutex_lock(&swrm->pm_lock); /* acquire pm_lock and update state */ + if (swrm->pm_state == SWRM_PM_SLEEPABLE) { + swrm->pm_state = SWRM_PM_ASLEEP; + } else if (swrm->pm_state == SWRM_PM_AWAKE) { + ret = -EBUSY; + mutex_unlock(&swrm->pm_lock); + goto check_ebusy; + } } else if (swrm->pm_state == SWRM_PM_AWAKE) { /* * unlock to wait for pm_state == SWRM_PM_SLEEPABLE @@ -3927,6 +3941,7 @@ static int swrm_suspend(struct device *dev) pm_runtime_enable(dev); } } +check_ebusy: if (ret == -EBUSY) { /* * There is a possibility that some audio stream is active