From patchwork Thu Dec 8 12:23:09 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: wangweidong.a@awinic.com X-Patchwork-Id: 13068358 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 628CAC3A5A7 for ; Thu, 8 Dec 2022 12:24:58 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 6BEF918C7; Thu, 8 Dec 2022 13:24:06 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 6BEF918C7 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1670502296; bh=EM926ALgsLWcE13LfMnNSuoQw20a+Ny8j7N8gxPxRO0=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=WEXhNSPUNQSrcxDBgw2DQuPdf+Vt3UzeqnjikCC2BbBSNmXuEU121HlWAvN7a5RfF PriE38dQ9hH0JCSZjm/dlMUxhM1FisHXQTnJn/1jsrv/J0DZKwz77HvlKayefPxRii qagHLpPkrIRVVbRMGEmtPhJx/277sv0AnD+g1MR8= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 855C8F80559; Thu, 8 Dec 2022 13:23:42 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 53B8DF8055B; Thu, 8 Dec 2022 13:23:40 +0100 (CET) Received: from out29-82.mail.aliyun.com (out29-82.mail.aliyun.com [115.124.29.82]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 401BAF8020D for ; Thu, 8 Dec 2022 13:23:31 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 401BAF8020D X-Alimail-AntiSpam: AC=CONTINUE; BC=0.07436261|-1; CH=green; DM=|CONTINUE|false|; DS=CONTINUE|ham_system_inform|0.00975171-0.000561269-0.989687; FP=0|0|0|0|0|-1|-1|-1; HT=ay29a033018047201; MF=wangweidong.a@awinic.com; NM=0; PH=DS; RN=17; RT=17; SR=0; TI=SMTPD_---.QRKcQry_1670502204; Received: from ubuntu-VirtualBox..(mailfrom:wangweidong.a@awinic.com fp:SMTPD_---.QRKcQry_1670502204) by smtp.aliyun-inc.com; Thu, 08 Dec 2022 20:23:25 +0800 From: wangweidong.a@awinic.com To: broonie@kernel.org, perex@perex.cz, alsa-devel@alsa-project.org, tiwai@suse.com, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, ckeepax@opensource.cirrus.com, tanureal@opensource.cirrus.com, quic_potturu@quicinc.com, pierre-louis.bossart@linux.intel.com, cy_huang@richtek.com Subject: [PATCH V6 1/5] ASoC: codecs: Add i2c and codec registration for aw883xx and their associated operation functions Date: Thu, 8 Dec 2022 20:23:09 +0800 Message-Id: <20221208122313.55118-2-wangweidong.a@awinic.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221208122313.55118-1-wangweidong.a@awinic.com> References: <20221208122313.55118-1-wangweidong.a@awinic.com> MIME-Version: 1.0 Cc: duanyibo@awinic.com, Weidong Wang , zhaolei@awinic.com, liweilei@awinic.com, yijiangtao@awinic.com, zhangjianming@awinic.com X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" From: Weidong Wang The Awinic AW883XX is an I2S/TDM input, high efficiency digital Smart K audio amplifier with an integrated 10.25V smart boost convert Signed-off-by: Nick Li Signed-off-by: Bruce zhao Signed-off-by: Weidong Wang --- sound/soc/codecs/aw883xx/aw883xx.c | 909 +++++++++++++++++++++++++++++ sound/soc/codecs/aw883xx/aw883xx.h | 81 +++ 2 files changed, 990 insertions(+) create mode 100644 sound/soc/codecs/aw883xx/aw883xx.c create mode 100644 sound/soc/codecs/aw883xx/aw883xx.h diff --git a/sound/soc/codecs/aw883xx/aw883xx.c b/sound/soc/codecs/aw883xx/aw883xx.c new file mode 100644 index 000000000000..f82e6d8c71a7 --- /dev/null +++ b/sound/soc/codecs/aw883xx/aw883xx.c @@ -0,0 +1,909 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * aw883xx.c -- ALSA Soc AW883XX codec support + * + * Copyright (c) 2022 AWINIC Technology CO., LTD + * + * Author: Bruce zhao + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw883xx_pid_2049_reg.h" +#include "aw883xx.h" +#include "aw883xx_bin_parse.h" +#include "aw883xx_device.h" + +static const struct regmap_config aw883xx_remap_config = { + .val_bits = 16, + .reg_bits = 8, + .max_register = AW_PID_2049_REG_MAX - 1, +}; + +/* + * aw883xx dsp write/read + */ +static int aw883xx_dsp_write_16bit(struct aw883xx *aw883xx, + unsigned short dsp_addr, unsigned int dsp_data) +{ + int ret; + struct aw_dsp_mem_desc *desc = &aw883xx->aw_pa->dsp_mem_desc; + + ret = regmap_write(aw883xx->regmap, desc->dsp_madd_reg, dsp_addr); + if (ret < 0) { + dev_err(aw883xx->dev, "%s error, ret=%d", __func__, ret); + return ret; + } + + ret = regmap_write(aw883xx->regmap, desc->dsp_mdat_reg, (u16)dsp_data); + if (ret < 0) { + dev_err(aw883xx->dev, "%s error, ret=%d", __func__, ret); + return ret; + } + + return 0; +} + +static int aw883xx_dsp_write_32bit(struct aw883xx *aw883xx, + unsigned short dsp_addr, unsigned int dsp_data) +{ + int ret; + u16 temp_data = 0; + struct aw_dsp_mem_desc *desc = &aw883xx->aw_pa->dsp_mem_desc; + + ret = regmap_write(aw883xx->regmap, desc->dsp_madd_reg, dsp_addr); + if (ret < 0) { + dev_err(aw883xx->dev, "%s error, ret=%d", __func__, ret); + return ret; + } + + temp_data = dsp_data & AW_DSP_16_DATA_MASK; + ret = regmap_write(aw883xx->regmap, desc->dsp_mdat_reg, temp_data); + if (ret < 0) { + dev_err(aw883xx->dev, "%s error, ret=%d", __func__, ret); + return ret; + } + + temp_data = dsp_data >> 16; + ret = regmap_write(aw883xx->regmap, desc->dsp_mdat_reg, temp_data); + if (ret < 0) { + dev_err(aw883xx->dev, "%s error, ret=%d", __func__, ret); + return ret; + } + + return 0; +} + +/* + * aw883xx clear dsp chip select state + */ +static void aw883xx_clear_dsp_sel_st(struct aw883xx *aw883xx) +{ + unsigned int reg_value; + u8 reg = aw883xx->aw_pa->soft_rst.reg; + + regmap_read(aw883xx->regmap, reg, ®_value); +} + +int aw883xx_dsp_write(struct aw883xx *aw883xx, + unsigned short dsp_addr, unsigned int dsp_data, unsigned char data_type) +{ + int ret = -1; + + mutex_lock(&aw883xx->dsp_lock); + switch (data_type) { + case AW_DSP_16_DATA: + ret = aw883xx_dsp_write_16bit(aw883xx, dsp_addr, dsp_data); + if (ret < 0) + dev_err(aw883xx->dev, "write dsp_addr[0x%04x] 16 bit dsp_data[%04x] failed", + (u32)dsp_addr, dsp_data); + break; + case AW_DSP_32_DATA: + ret = aw883xx_dsp_write_32bit(aw883xx, dsp_addr, dsp_data); + if (ret < 0) + dev_err(aw883xx->dev, "write dsp_addr[0x%04x] 32 bit dsp_data[%08x] failed", + (u32)dsp_addr, dsp_data); + break; + default: + dev_err(aw883xx->dev, "data type[%d] unsupported", data_type); + ret = -EINVAL; + break; + } + + aw883xx_clear_dsp_sel_st(aw883xx); + mutex_unlock(&aw883xx->dsp_lock); + return ret; +} + +static int aw883xx_dsp_read_16bit(struct aw883xx *aw883xx, + unsigned short dsp_addr, unsigned int *dsp_data) +{ + int ret; + unsigned int temp_data = 0; + struct aw_dsp_mem_desc *desc = &aw883xx->aw_pa->dsp_mem_desc; + + ret = regmap_write(aw883xx->regmap, desc->dsp_madd_reg, dsp_addr); + if (ret < 0) { + dev_err(aw883xx->dev, "i2c write error, ret=%d", ret); + return ret; + } + + ret = regmap_read(aw883xx->regmap, desc->dsp_mdat_reg, &temp_data); + if (ret < 0) { + dev_err(aw883xx->dev, "i2c write error, ret=%d", ret); + return ret; + } + + *dsp_data = temp_data; + + return 0; +} + +static int aw883xx_dsp_read_32bit(struct aw883xx *aw883xx, + unsigned short dsp_addr, unsigned int *dsp_data) +{ + int ret; + unsigned int temp_data = 0; + struct aw_dsp_mem_desc *desc = &aw883xx->aw_pa->dsp_mem_desc; + + /*write dsp addr*/ + ret = regmap_write(aw883xx->regmap, desc->dsp_madd_reg, dsp_addr); + if (ret < 0) { + dev_err(aw883xx->dev, "i2c write error, ret=%d", ret); + return ret; + } + + /*get Low 16 bit data*/ + ret = regmap_read(aw883xx->regmap, desc->dsp_mdat_reg, &temp_data); + if (ret < 0) { + dev_err(aw883xx->dev, "i2c read error, ret=%d", ret); + return ret; + } + + *dsp_data = temp_data; + + /*get high 16 bit data*/ + ret = regmap_read(aw883xx->regmap, desc->dsp_mdat_reg, &temp_data); + if (ret < 0) { + dev_err(aw883xx->dev, "i2c read error, ret=%d", ret); + return ret; + } + *dsp_data |= (temp_data << 16); + + return 0; +} + +int aw883xx_dsp_read(struct aw883xx *aw883xx, + unsigned short dsp_addr, unsigned int *dsp_data, unsigned char data_type) +{ + int ret = -1; + + mutex_lock(&aw883xx->dsp_lock); + switch (data_type) { + case AW_DSP_16_DATA: + ret = aw883xx_dsp_read_16bit(aw883xx, dsp_addr, dsp_data); + if (ret < 0) + dev_err(aw883xx->dev, "read dsp_addr[0x%04x] 16 bit dsp_data[%04x] failed", + (u32)dsp_addr, *dsp_data); + break; + case AW_DSP_32_DATA: + ret = aw883xx_dsp_read_32bit(aw883xx, dsp_addr, dsp_data); + if (ret < 0) + dev_err(aw883xx->dev, "read dsp_addr[0x%04x] 32 bit dsp_data[%08x] failed", + (u32)dsp_addr, *dsp_data); + break; + default: + dev_err(aw883xx->dev, "data type[%d] unsupported", data_type); + ret = -EINVAL; + break; + } + + aw883xx_clear_dsp_sel_st(aw883xx); + mutex_unlock(&aw883xx->dsp_lock); + return ret; +} + +static void aw883xx_start_pa(struct aw883xx *aw883xx) +{ + int ret, i; + + if (!aw883xx->allow_pw) { + dev_info(aw883xx->dev, "%s:dev can not allow power", __func__); + return; + } + + if (aw883xx->pstream == AW883XX_STREAM_CLOSE) { + dev_info(aw883xx->dev, "%s:pstream is close", __func__); + return; + } + + for (i = 0; i < AW_START_RETRIES; i++) { + ret = aw883xx_device_start(aw883xx->aw_pa); + if (ret) { + dev_err(aw883xx->dev, "aw883xx device start failed. retry = %d", i); + ret = aw883xx_dev_fw_update(aw883xx->aw_pa, AW_DSP_FW_UPDATE_ON, true); + if (ret < 0) { + dev_err(aw883xx->dev, "fw update failed"); + continue; + } + } else { + dev_info(aw883xx->dev, "start success\n"); + break; + } + } +} + +static void aw883xx_startup_work(struct work_struct *work) +{ + struct aw883xx *aw883xx = + container_of(work, struct aw883xx, start_work.work); + + mutex_lock(&aw883xx->lock); + aw883xx_start_pa(aw883xx); + mutex_unlock(&aw883xx->lock); +} + +static void aw883xx_start(struct aw883xx *aw883xx, bool sync_start) +{ + int ret; + int i; + + if (aw883xx->aw_pa->fw_status != AW_DEV_FW_OK) + return; + + if (!aw883xx->allow_pw) { + dev_info(aw883xx->dev, "%s:dev can not allow power", __func__); + return; + } + + if (aw883xx->aw_pa->status == AW_DEV_PW_ON) + return; + + for (i = 0; i < AW_START_RETRIES; i++) { + ret = aw883xx_dev_fw_update(aw883xx->aw_pa, AW_DSP_FW_UPDATE_OFF, + aw883xx->phase_sync); + if (ret < 0) { + dev_err(aw883xx->dev, "fw update failed. retry = %d", i); + continue; + } else { + /*firmware update success*/ + if (sync_start == AW_SYNC_START) + aw883xx_start_pa(aw883xx); + else + queue_delayed_work(aw883xx->work_queue, + &aw883xx->start_work, + AW_START_WORK_DELAY_MS); + return; + } + } +} + +/* + * Digital Audio Interface + */ +static int aw883xx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + struct aw883xx *aw883xx = snd_soc_component_get_drvdata(codec); + + aw883xx->pstream = AW883XX_STREAM_OPEN; + + mutex_lock(&aw883xx->lock); + aw883xx_start(aw883xx, AW_ASYNC_START); + mutex_unlock(&aw883xx->lock); + + return 0; +} + +static void aw883xx_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + struct aw883xx *aw883xx = snd_soc_component_get_drvdata(codec); + + aw883xx->pstream = AW883XX_STREAM_CLOSE; + cancel_delayed_work_sync(&aw883xx->start_work); + mutex_lock(&aw883xx->lock); + aw883xx_device_stop(aw883xx->aw_pa); + mutex_unlock(&aw883xx->lock); + +} + +static const struct snd_soc_dai_ops aw883xx_dai_ops = { + .startup = aw883xx_startup, + .shutdown = aw883xx_shutdown, +}; + +static struct snd_soc_dai_driver aw883xx_dai[] = { + { + .name = "aw883xx-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AW_RATES, + .formats = AW_FORMATS, + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AW_RATES, + .formats = AW_FORMATS, + }, + .ops = &aw883xx_dai_ops, + }, +}; + +/* + * codec driver + */ +static int aw883xx_get_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx = snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev = aw883xx->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->fade_in_time; + + return 0; + +} + +static int aw883xx_set_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int time = 0; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx = snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev = aw883xx->aw_pa; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + time = ucontrol->value.integer.value[0]; + + if (time < mc->min || time > mc->max) + return 0; + + if (time != aw_dev->fade_in_time) { + aw_dev->fade_in_time = time; + return 1; + } + + return 0; +} + +static int aw883xx_get_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx = snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev = aw883xx->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->fade_out_time; + + return 0; +} + +static int aw883xx_set_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev = aw883xx->aw_pa; + unsigned int time = 0; + + time = ucontrol->value.integer.value[0]; + if (time < mc->min || time > mc->max) + return 0; + + if (time != aw_dev->fade_out_time) { + aw_dev->fade_out_time = time; + return 1; + } + + return 0; +} + +static int aw883xx_profile_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int count; + char *name = NULL; + const char *prof_name = NULL; + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx = snd_soc_component_get_drvdata(codec); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + count = aw883xx_dev_get_profile_count(aw883xx->aw_pa); + if (count <= 0) { + uinfo->value.enumerated.items = 0; + 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; + + prof_name = aw_dev_get_prof_name(aw883xx->aw_pa, count); + if (!prof_name) { + strscpy(uinfo->value.enumerated.name, "null", + strlen("null") + 1); + return 0; + } + + strscpy(name, prof_name, sizeof(uinfo->value.enumerated.name)); + + return 0; +} + +static int aw883xx_profile_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw883xx_dev_get_profile_index(aw883xx->aw_pa); + + return 0; +} + +static int aw883xx_profile_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx = snd_soc_component_get_drvdata(codec); + int ret; + int cur_index; + + /* check value valid */ + ret = aw883xx_dev_check_profile_index(aw883xx->aw_pa, ucontrol->value.integer.value[0]); + if (ret) + return 0; + + /*check cur_index == set value*/ + cur_index = aw883xx_dev_get_profile_index(aw883xx->aw_pa); + if (cur_index == ucontrol->value.integer.value[0]) { + dev_dbg(codec->dev, "index no change"); + return 0; + } + + /*pa stop or stopping just set profile*/ + mutex_lock(&aw883xx->lock); + aw883xx_dev_set_profile_index(aw883xx->aw_pa, ucontrol->value.integer.value[0]); + + if (aw883xx->pstream) { + aw883xx_device_stop(aw883xx->aw_pa); + aw883xx_start(aw883xx, AW_SYNC_START); + } + + mutex_unlock(&aw883xx->lock); + + return 1; +} + +static int aw883xx_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw883xx->allow_pw; + + return 0; +} + +static int aw883xx_switch_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int value = 0; + + value = ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return 0; + + if (value == aw883xx->allow_pw) { + dev_dbg(aw883xx->dev, "PA switch not change"); + return 0; + } + aw883xx->allow_pw = value; + + if (aw883xx->pstream) { + if (!aw883xx->allow_pw) { + cancel_delayed_work_sync(&aw883xx->start_work); + mutex_lock(&aw883xx->lock); + aw883xx_device_stop(aw883xx->aw_pa); + mutex_unlock(&aw883xx->lock); + } else { + cancel_delayed_work_sync(&aw883xx->start_work); + mutex_lock(&aw883xx->lock); + aw883xx_start(aw883xx, AW_SYNC_START); + mutex_unlock(&aw883xx->lock); + } + } + + return 1; +} + +static int aw883xx_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx = snd_soc_component_get_drvdata(codec); + struct aw_volume_desc *vol_desc = &aw883xx->aw_pa->volume_desc; + + ucontrol->value.integer.value[0] = vol_desc->ctl_volume; + + return 0; +} + +static int aw883xx_volume_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int value = 0; + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx = snd_soc_component_get_drvdata(codec); + struct aw_volume_desc *vol_desc = &aw883xx->aw_pa->volume_desc; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + value = ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return 0; + + if (vol_desc->ctl_volume != value) { + vol_desc->ctl_volume = value; + aw883xx_dev_set_volume(aw883xx->aw_pa, vol_desc->ctl_volume); + + return 1; + } + + return 0; +} + +static int aw883xx_get_fade_step(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw883xx->aw_pa->fade_step; + + return 0; +} +static int aw883xx_set_fade_step(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int value = 0; + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + value = ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return 0; + + if (aw883xx->aw_pa->fade_step != value) { + aw883xx->aw_pa->fade_step = value; + return 1; + } + + return 0; +} +static int aw883xx_re_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw883xx->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_re; + + return 0; +} + +static int aw883xx_re_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int value = 0; + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw883xx->aw_pa; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + value = ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return 0; + + if (aw_dev->cali_desc.cali_re != value) { + aw_dev->cali_desc.cali_re = value; + return 1; + } + + return 0; +} +static const struct snd_kcontrol_new aw883xx_controls[] = { + SOC_SINGLE_EXT("PCM Playback Switch", SND_SOC_NOPM, 0, 1, 0, + aw883xx_switch_get, aw883xx_switch_set), + SOC_SINGLE_EXT("PCM Playback Volume", AW_PID_2049_SYSCTRL2_REG, + 6, AW_PID_2049_MUTE_VOL, 0, aw883xx_volume_get, + aw883xx_volume_set), + SOC_SINGLE_EXT("Fade Step", 0, 0, AW_PID_2049_MUTE_VOL, 0, + aw883xx_get_fade_step, aw883xx_set_fade_step), + SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN, + aw883xx_get_fade_in_time, aw883xx_set_fade_in_time), + SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN, + aw883xx_get_fade_out_time, aw883xx_set_fade_out_time), + SOC_SINGLE_EXT("Calib", 0, 0, 100, 0, + aw883xx_re_get, aw883xx_re_set), + AW_PROFILE_EXT("Profile Set", aw883xx_profile_info, + aw883xx_profile_get, aw883xx_profile_set), +}; + +static const struct snd_soc_dapm_widget aw883xx_dapm_widgets[] = { + /* playback */ + SND_SOC_DAPM_AIF_IN("AIF_RX", "Speaker_Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("DAC Output"), + /* capture */ + SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_INPUT("ADC Input"), +}; + +static const struct snd_soc_dapm_route aw883xx_audio_map[] = { + {"DAC Output", NULL, "AIF_RX"}, + {"AIF_TX", NULL, "ADC Input"}, +}; + +static int aw883xx_codec_probe(struct snd_soc_component *component) +{ + struct aw883xx *aw883xx = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + int ret = 0; + + /*destroy_workqueue(struct workqueue_struct *wq)*/ + aw883xx->work_queue = create_singlethread_workqueue("aw883xx"); + if (!aw883xx->work_queue) { + dev_err(aw883xx->dev, "create workqueue failed !"); + return -EINVAL; + } + + INIT_DELAYED_WORK(&aw883xx->start_work, aw883xx_startup_work); + + /*add widgets*/ + ret = snd_soc_dapm_new_controls(dapm, aw883xx_dapm_widgets, + ARRAY_SIZE(aw883xx_dapm_widgets)); + if (ret < 0) + return ret; + + /*add route*/ + ret = snd_soc_dapm_add_routes(dapm, aw883xx_audio_map, + ARRAY_SIZE(aw883xx_audio_map)); + if (ret < 0) + return ret; + + ret = snd_soc_add_component_controls(component, aw883xx_controls, + ARRAY_SIZE(aw883xx_controls)); + if (ret < 0) + return ret; + + return ret; +} + +static void aw883xx_codec_remove(struct snd_soc_component *aw_codec) +{ + struct aw883xx *aw883xx = snd_soc_component_get_drvdata(aw_codec); + + cancel_delayed_work_sync(&aw883xx->start_work); + + if (aw883xx->work_queue) + destroy_workqueue(aw883xx->work_queue); + +} + +static const struct snd_soc_component_driver soc_codec_dev_aw883xx = { + .probe = aw883xx_codec_probe, + .remove = aw883xx_codec_remove, +}; + +static struct aw883xx *aw883xx_malloc_init(struct i2c_client *i2c) +{ + struct aw883xx *aw883xx = devm_kzalloc(&i2c->dev, + sizeof(struct aw883xx), GFP_KERNEL); + if (!aw883xx) + return NULL; + + aw883xx->dev = &i2c->dev; + aw883xx->i2c = i2c; + aw883xx->aw_pa = NULL; + aw883xx->codec = NULL; + aw883xx->allow_pw = true; + aw883xx->work_queue = NULL; + mutex_init(&aw883xx->lock); + mutex_init(&aw883xx->dsp_lock); + + return aw883xx; +} + +static void aw883xx_parse_sync_flag_dt(struct aw883xx *aw883xx) +{ + int ret; + int32_t sync_enable = 0; + struct device_node *np = aw883xx->dev->of_node; + + ret = of_property_read_u32(np, "sync-flag", &sync_enable); + if (ret < 0) { + dev_dbg(aw883xx->dev, + "read sync flag failed,default phase sync off"); + sync_enable = false; + } else { + dev_dbg(aw883xx->dev, + "sync flag is %d", sync_enable); + } + + aw883xx->phase_sync = sync_enable; +} + +static void aw883xx_hw_reset(struct aw883xx *aw883xx) +{ + if (aw883xx->reset_gpio) { + gpiod_set_value_cansleep(aw883xx->reset_gpio, 0); + usleep_range(AW_1000_US, AW_1000_US + 10); + gpiod_set_value_cansleep(aw883xx->reset_gpio, 1); + usleep_range(AW_1000_US, AW_1000_US + 10); + } else { + dev_err(aw883xx->dev, "%s failed", __func__); + } +} + +static int aw883xx_read_chipid(struct aw883xx *aw883xx) +{ + int ret = -1; + int reg_val = 0; + + ret = regmap_read(aw883xx->regmap, AW_CHIP_ID_REG, ®_val); + if (ret) + return -EIO; + + dev_info(aw883xx->dev, "chip id = %x\n", reg_val); + aw883xx->chip_id = reg_val; + + return 0; +} + +static void aw883xx_parse_dt(struct aw883xx *aw883xx) +{ + aw883xx_parse_sync_flag_dt(aw883xx); + + aw883xx->reset_gpio = devm_gpiod_get_optional(aw883xx->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(aw883xx->reset_gpio)) + dev_info(aw883xx->dev, "reset gpio not defined\n"); +} +/* + * i2c driver + */ +static int aw883xx_i2c_probe(struct i2c_client *i2c) +{ + struct aw883xx *aw883xx = NULL; + int ret = -1; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed"); + return -EIO; + } + + aw883xx = aw883xx_malloc_init(i2c); + if (!aw883xx) { + dev_err(&i2c->dev, "malloc aw883xx failed"); + return -ENOMEM; + } + i2c_set_clientdata(i2c, aw883xx); + + aw883xx_parse_dt(aw883xx); + + /* hardware reset */ + aw883xx_hw_reset(aw883xx); + + aw883xx->regmap = devm_regmap_init_i2c(i2c, &aw883xx_remap_config); + if (IS_ERR(aw883xx->regmap)) { + ret = PTR_ERR(aw883xx->regmap); + dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + /* aw883xx chip id */ + ret = aw883xx_read_chipid(aw883xx); + if (ret < 0) { + dev_err(&i2c->dev, "aw883xx_read_chipid failed ret=%d", ret); + return ret; + } + + /*aw pa init*/ + ret = aw883xx_init(aw883xx); + if (ret < 0) + return ret; + + ret = aw883xx_request_firmware_file(aw883xx); + if (ret < 0) { + dev_err(aw883xx->dev, "%s failed\n", __func__); + return ret; + } + + ret = snd_soc_register_component(aw883xx->dev, + &soc_codec_dev_aw883xx, + aw883xx_dai, ARRAY_SIZE(aw883xx_dai)); + if (ret < 0) { + dev_err(aw883xx->dev, "failed to register aw883xx: %d", ret); + return ret; + } + + return 0; +} + +static void aw883xx_i2c_remove(struct i2c_client *i2c) +{ + struct aw883xx *aw883xx = i2c_get_clientdata(i2c); + + aw883xx_dev_deinit(aw883xx->aw_pa); + snd_soc_unregister_component(&i2c->dev); + + if (aw883xx->aw_cfg) { + vfree(aw883xx->aw_cfg); + aw883xx->aw_cfg = NULL; + } +} + +static const struct i2c_device_id aw883xx_i2c_id[] = { + { AW_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw883xx_i2c_id); + +static struct i2c_driver aw883xx_i2c_driver = { + .driver = { + .name = AW_I2C_NAME, + .owner = THIS_MODULE, + }, + .probe_new = aw883xx_i2c_probe, + .remove = aw883xx_i2c_remove, + .id_table = aw883xx_i2c_id, +}; +module_i2c_driver(aw883xx_i2c_driver); + +MODULE_DESCRIPTION("ASoC AW883XX Smart PA Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/aw883xx/aw883xx.h b/sound/soc/codecs/aw883xx/aw883xx.h new file mode 100644 index 000000000000..209851cae7ef --- /dev/null +++ b/sound/soc/codecs/aw883xx/aw883xx.h @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * aw883xx.c -- ALSA Soc AW883XX codec support + * + * Copyright (c) 2022 AWINIC Technology CO., LTD + * + * Author: Bruce zhao + */ + +#ifndef __AW883XX_H__ +#define __AW883XX_H__ + +#include +#include +#include +#include "aw883xx_data_type.h" + +#define AW_CHIP_ID_REG (0x00) +#define AW_START_RETRIES (5) +#define AW_START_WORK_DELAY_MS (0) + +#define AW_DSP_16_DATA_MASK (0x0000ffff) + +#define AW_I2C_NAME "aw883xx_smartpa" + +#define AW_RATES (SNDRV_PCM_RATE_8000_48000 | \ + SNDRV_PCM_RATE_96000) +#define AW_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) +#define AW_REQUEST_FW_RETRIES 5 /* 5 times */ +#define AW_SYNC_LOAD + +#define FADE_TIME_MAX 100000 +#define FADE_TIME_MIN 0 + +#define AW_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = profile_info, \ + .get = profile_get, \ + .put = profile_set, \ +} + +enum { + AW_SYNC_START = 0, + AW_ASYNC_START, +}; + +enum { + AW883XX_STREAM_CLOSE = 0, + AW883XX_STREAM_OPEN, +}; + +struct aw883xx { + struct i2c_client *i2c; + struct device *dev; + struct mutex lock; + struct mutex dsp_lock; + struct snd_soc_component *codec; + struct aw_device *aw_pa; + struct gpio_desc *reset_gpio; + unsigned char phase_sync; /*phase sync*/ + bool allow_pw; + u8 pstream; + struct workqueue_struct *work_queue; + struct delayed_work start_work; + u16 chip_id; + struct regmap *regmap; + struct aw_container *aw_cfg; +}; + +int aw883xx_init(struct aw883xx *aw883xx); + +int aw883xx_dsp_write(struct aw883xx *aw883xx, + unsigned short dsp_addr, unsigned int dsp_data, unsigned char data_type); +int aw883xx_dsp_read(struct aw883xx *aw883xx, + unsigned short dsp_addr, unsigned int *dsp_data, unsigned char data_type); + +#endif From patchwork Thu Dec 8 12:23:10 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: wangweidong.a@awinic.com X-Patchwork-Id: 13068359 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 60689C3A5A7 for ; Thu, 8 Dec 2022 12:25:23 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 5B4341919; Thu, 8 Dec 2022 13:24:31 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 5B4341919 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1670502321; bh=vUwEsvYbGpEg74xY+UIj7K8/Tfxq3J1QBOCNT2WXuJs=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=pjvrgUwFQjmT1QHDAuzYmB9C7U7mY8VbAdC9heDDyY+s+Z8P2rDSDQQI/jNZI8fxK iFz2nMFzUAkvkkyQ9Jmnnrxh7nGCy5aKbP0zSIHLTPTyg9XSr/vWVxuwQPbiIOxdEK y3e5cGNJZRI2qoNjV4RhBw1PxWWoJ6qdxl8vURB0= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id D5844F8055A; Thu, 8 Dec 2022 13:23:46 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 16DD4F8056F; Thu, 8 Dec 2022 13:23:45 +0100 (CET) Received: from out28-4.mail.aliyun.com (out28-4.mail.aliyun.com [115.124.28.4]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 11D83F801D8 for ; Thu, 8 Dec 2022 13:23:31 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 11D83F801D8 X-Alimail-AntiSpam: AC=CONTINUE; BC=0.07436261|-1; CH=green; DM=|CONTINUE|false|; DS=CONTINUE|ham_system_inform|0.00130655-6.38866e-05-0.99863; FP=0|0|0|0|0|-1|-1|-1; HT=ay29a033018047209; MF=wangweidong.a@awinic.com; NM=0; PH=DS; RN=17; RT=17; SR=0; TI=SMTPD_---.QRKcQtk_1670502205; Received: from ubuntu-VirtualBox..(mailfrom:wangweidong.a@awinic.com fp:SMTPD_---.QRKcQtk_1670502205) by smtp.aliyun-inc.com; Thu, 08 Dec 2022 20:23:27 +0800 From: wangweidong.a@awinic.com To: broonie@kernel.org, perex@perex.cz, alsa-devel@alsa-project.org, tiwai@suse.com, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, ckeepax@opensource.cirrus.com, tanureal@opensource.cirrus.com, quic_potturu@quicinc.com, pierre-louis.bossart@linux.intel.com, cy_huang@richtek.com Subject: [PATCH V6 2/5] ASoC: codecs: Implementation of aw883xx configuration file parsing function Date: Thu, 8 Dec 2022 20:23:10 +0800 Message-Id: <20221208122313.55118-3-wangweidong.a@awinic.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221208122313.55118-1-wangweidong.a@awinic.com> References: <20221208122313.55118-1-wangweidong.a@awinic.com> MIME-Version: 1.0 Cc: duanyibo@awinic.com, Weidong Wang , zhaolei@awinic.com, liweilei@awinic.com, yijiangtao@awinic.com, zhangjianming@awinic.com X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" From: Weidong Wang The Awinic AW883XX is an I2S/TDM input, high efficiency digital Smart K audio amplifier with an integrated 10.25V smart boost convert Signed-off-by: Nick Li Signed-off-by: Bruce zhao Signed-off-by: Weidong Wang --- sound/soc/codecs/aw883xx/aw883xx_bin_parse.c | 1324 ++++++++++++++++++ sound/soc/codecs/aw883xx/aw883xx_bin_parse.h | 149 ++ 2 files changed, 1473 insertions(+) create mode 100644 sound/soc/codecs/aw883xx/aw883xx_bin_parse.c create mode 100644 sound/soc/codecs/aw883xx/aw883xx_bin_parse.h diff --git a/sound/soc/codecs/aw883xx/aw883xx_bin_parse.c b/sound/soc/codecs/aw883xx/aw883xx_bin_parse.c new file mode 100644 index 000000000000..f3d9b8a9fdf2 --- /dev/null +++ b/sound/soc/codecs/aw883xx/aw883xx_bin_parse.c @@ -0,0 +1,1324 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * aw_bin_parse.c -- ALSA Soc AW883XX codec support + * + * Copyright (c) 2022 AWINIC Technology CO., LTD + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw883xx_bin_parse.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *profile_name[AW_PROFILE_MAX] = { + "Music", "Voice", "Voip", "Ringtone", + "Ringtone_hs", "Lowpower", "Bypass", + "Mmi", "Fm", "Notification", "Receiver" +}; + +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; + + p_check_sum = &(bin->info.data[(bin->header_info[bin_num].valid_data_addr - + bin->header_info[bin_num].header_len)]); + + pr_debug("aw_bin_parse p_check_sum = %p\n", p_check_sum); + + check_sum = le32_to_cpup((void *)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); + } + pr_debug("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; + pr_err("%s. CheckSum Fail.bin_num=%d, CheckSum:0x%x, SumData:0x%x", + __func__, bin_num, check_sum, sum_data); + return -BIN_DATA_LEN_ERR; + } + p_check_sum = NULL; + + return 0; +} + +static int aw_check_data_version(struct aw_bin *bin, int bin_num) +{ + if (bin->header_info[bin_num].bin_data_ver < DATA_VERSION_V1 || + bin->header_info[bin_num].bin_data_ver > DATA_VERSION_MAX) { + pr_err("aw_bin_parse Unrecognized this bin data version\n"); + return -DATA_VER_ERR; + } + + return 0; +} + +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; + struct bin_header_info temp_info; + + temp_info = bin->header_info[bin_num]; + p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]); + pr_debug("aw_bin_parse p_check_sum = %p\n", p_check_sum); + parse_register_num = le32_to_cpup((void *)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); + pr_debug("%s bin_num = %d,parse_register_num = 0x%x,check_register_num = 0x%x\n", + __func__, bin_num, parse_register_num, check_register_num); + if (parse_register_num != check_register_num) { + p_check_sum = NULL; + pr_err("%s bin_num = %d,parse_register_num = 0x%x,check_register_num = 0x%x\n", + __func__, bin_num, parse_register_num, check_register_num); + + return -REG_NUM_ERR; + } + bin->header_info[bin_num].reg_num = parse_register_num; + bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - 4; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = temp_info.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; + struct bin_header_info temp_info; + + temp_info = bin->header_info[bin_num]; + p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]); + pr_debug("aw_bin_parse p_check_sum = %p\n", p_check_sum); + parse_dsp_reg_num = le32_to_cpup((void *)(p_check_sum + 4)); + bin->header_info[bin_num].reg_data_byte_len = + le32_to_cpup((void *)(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; + pr_debug("%s bin_num = %d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x", + __func__, bin_num, check_dsp_reg_num, check_dsp_reg_num); + if (parse_dsp_reg_num != check_dsp_reg_num) { + p_check_sum = NULL; + pr_err("aw_bin_parse check dsp reg num error\n"); + pr_err("%s bin_num = %d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x", + __func__, bin_num, check_dsp_reg_num, check_dsp_reg_num); + return -DSP_REG_NUM_ERR; + } + bin->header_info[bin_num].download_addr = le32_to_cpup((void *)p_check_sum); + bin->header_info[bin_num].reg_num = parse_dsp_reg_num; + bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - 12; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = temp_info.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; + struct bin_header_info temp_info; + + temp_info = bin->header_info[bin_num]; + p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]); + pr_debug("aw_bin_parse p_check_sum = %p\n", p_check_sum); + bin->header_info[bin_num].app_version = le32_to_cpup((void *)p_check_sum); + parse_soc_app_num = le32_to_cpup((void *)(p_check_sum + 8)); + check_soc_app_num = bin->header_info[bin_num].bin_data_len - 12; + pr_debug("%s bin_num = %d, parse_soc_app_num=0x%x, check_soc_app_num = 0x%x\n", + __func__, bin_num, parse_soc_app_num, check_soc_app_num); + if (parse_soc_app_num != check_soc_app_num) { + p_check_sum = NULL; + pr_err("aw_bin_parse check soc app num error\n"); + pr_err("%s bin_num = %d, parse_soc_app_num=0x%x, check_soc_app_num = 0x%x\n", + __func__, bin_num, parse_soc_app_num, check_soc_app_num); + + return -SOC_APP_NUM_ERR; + } + bin->header_info[bin_num].reg_num = parse_soc_app_num; + bin->header_info[bin_num].download_addr = + le32_to_cpup((void *)(p_check_sum + 4)); + bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - 12; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = temp_info.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 = 0; + + bin->header_info[bin->all_bin_parse_num].header_len = HEADER_LEN; + bin->header_info[bin->all_bin_parse_num].check_sum = + le32_to_cpup((void *)(bin->p_addr) + + CHECK_SUM_OFFSET); + bin->header_info[bin->all_bin_parse_num].header_ver = + le32_to_cpup((void *)(bin->p_addr + + HEADER_VER_OFFSET)); + bin->header_info[bin->all_bin_parse_num].bin_data_type = + le32_to_cpup((void *)(bin->p_addr + + BIN_DATA_TYPE_OFFSET)); + bin->header_info[bin->all_bin_parse_num].bin_data_ver = + le32_to_cpup((void *)(bin->p_addr + + BIN_DATA_VER_OFFSET)); + bin->header_info[bin->all_bin_parse_num].bin_data_len = + le32_to_cpup((void *)(bin->p_addr + + BIN_DATA_LEN_OFFSET)); + bin->header_info[bin->all_bin_parse_num].ui_ver = + le32_to_cpup((void *)(bin->p_addr + + UI_VER_OFFSET)); + bin->header_info[bin->all_bin_parse_num].reg_byte_len = + le32_to_cpup((void *)(bin->p_addr + + REG_BYTE_LEN_OFFSET)); + bin->header_info[bin->all_bin_parse_num].data_byte_len = + le32_to_cpup((void *)(bin->p_addr + + DATA_BYTE_LEN_OFFSET)); + bin->header_info[bin->all_bin_parse_num].device_addr = + le32_to_cpup((void *)(bin->p_addr + + DEVICE_ADDR_OFFSET)); + 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_one_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; + struct bin_header_info aw_bin_header_info; + + if (bin->info.len < sizeof(struct bin_header_info)) { + pr_err("bin_header_info size[%d] overflow file size[%d]\n", + (int)sizeof(struct bin_header_info), bin->info.len); + return -EINVAL; + } + + aw_bin_header_info = bin->header_info[bin->all_bin_parse_num - 1]; + if (!bin_serial_num) { + bin_start_addr = le32_to_cpup((void *)(bin->p_addr + 64)); + bin->p_addr += (60 + bin_start_addr); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + aw_bin_header_info.valid_data_addr + 4 + 8 * bin_num + 60; + } else { + valid_data_len = aw_bin_header_info.bin_data_len; + bin->p_addr += (60 + valid_data_len); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + aw_bin_header_info.valid_data_addr + + aw_bin_header_info.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; + + bin_num = le32_to_cpup((void *)(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++) { + pr_debug("aw_bin_parse enter multi bin for is %d\n", i); + ret = aw_parse_one_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; + + if (bin->info.len < sizeof(struct bin_header_info)) { + pr_err("bin_header_info size[%d] overflow file size[%d]\n", + (int)sizeof(struct bin_header_info), bin->info.len); + return -EINVAL; + } + + bin_data_type = le32_to_cpup((void *)(bin->p_addr + 8)); + pr_debug("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 + */ + pr_debug("aw_bin_parse enter single bin branch\n"); + bin->single_bin_parse_num += 1; + pr_debug("%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 */ + pr_debug("aw_bin_parse enter multi bin branch\n"); + bin->multi_bin_parse_num += 1; + pr_debug("%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: + pr_debug("%s There is no corresponding type\n", __func__); + break; + } + 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 = le32_to_cpup((void *)(bin->p_addr + 4)); + pr_debug("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: + pr_err("aw_bin_parse Unrecognized this bin header version\n"); + return -BIN_HEADER_VER_ERR; + } +} + +static int aw_parsing_bin_file(struct aw_bin *bin) +{ + int i = 0; + int ret = 0; + + if (!bin) { + pr_err("aw_bin_parse bin is NULL\n"); + return -BIN_IS_NULL; + } + 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) { + pr_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) { + pr_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) { + pr_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 */ + switch (bin->header_info[i].bin_data_type) { + case DATA_TYPE_REGISTER: + ret = aw_check_register_num_v1(bin, i); + break; + case DATA_TYPE_DSP_REG: + ret = aw_check_dsp_reg_num_v1(bin, i); + break; + case DATA_TYPE_SOC_APP: + ret = aw_check_soc_app_num_v1(bin, i); + break; + default: + bin->header_info[i].valid_data_len = + bin->header_info[i].bin_data_len; + ret = 0; + break; + } + if (ret < 0) + return ret; + } + } + + return 0; +} + +int aw883xx_dev_dsp_data_order(struct aw_device *aw_dev, + unsigned char *data, unsigned int data_len) +{ + int i = 0; + u8 tmp_val = 0; + + if (data_len % 2 != 0) { + dev_dbg(aw_dev->dev, "data_len:%d unsupported", data_len); + return -EINVAL; + } + + for (i = 0; i < data_len; i += 2) { + tmp_val = data[i]; + data[i] = data[i + 1]; + data[i + 1] = tmp_val; + } + + return 0; +} + +static int aw_dev_parse_raw_reg(struct aw_device *aw_dev, + unsigned char *data, unsigned int data_len, + struct aw_prof_desc *prof_desc) +{ + prof_desc->sec_desc[AW_DATA_TYPE_REG].data = data; + prof_desc->sec_desc[AW_DATA_TYPE_REG].len = data_len; + + prof_desc->prof_st = AW_PROFILE_OK; + + return 0; +} + +static int aw_dev_parse_raw_dsp_cfg(struct aw_device *aw_dev, + unsigned char *data, unsigned int data_len, + struct aw_prof_desc *prof_desc) +{ + int ret; + + ret = aw883xx_dev_dsp_data_order(aw_dev, data, data_len); + if (ret < 0) + return ret; + + prof_desc->sec_desc[AW_DATA_TYPE_DSP_CFG].data = data; + prof_desc->sec_desc[AW_DATA_TYPE_DSP_CFG].len = data_len; + + prof_desc->prof_st = AW_PROFILE_OK; + + return 0; +} + +static int aw_dev_parse_raw_dsp_fw(struct aw_device *aw_dev, + unsigned char *data, unsigned int data_len, + struct aw_prof_desc *prof_desc) +{ + int ret; + + ret = aw883xx_dev_dsp_data_order(aw_dev, data, data_len); + if (ret < 0) + return ret; + + prof_desc->sec_desc[AW_DATA_TYPE_DSP_FW].data = data; + prof_desc->sec_desc[AW_DATA_TYPE_DSP_FW].len = data_len; + + prof_desc->prof_st = AW_PROFILE_OK; + + return 0; +} + +static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, + unsigned char *data, unsigned int data_len, + struct aw_prof_desc *prof_desc) +{ + struct aw_bin *aw_bin = NULL; + int i; + int ret; + + aw_bin = devm_kzalloc(aw_dev->dev, data_len + sizeof(struct aw_bin), GFP_KERNEL); + if (!aw_bin) + 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) { + dev_err(aw_dev->dev, "parse bin failed"); + goto parse_bin_failed; + } + + for (i = 0; i < aw_bin->all_bin_parse_num; i++) { + switch (aw_bin->header_info[i].bin_data_type) { + case DATA_TYPE_REGISTER: + prof_desc->sec_desc[AW_DATA_TYPE_REG].len = + aw_bin->header_info[i].valid_data_len; + prof_desc->sec_desc[AW_DATA_TYPE_REG].data = + data + aw_bin->header_info[i].valid_data_addr; + break; + case DATA_TYPE_DSP_REG: + ret = aw883xx_dev_dsp_data_order(aw_dev, + data + aw_bin->header_info[i].valid_data_addr, + aw_bin->header_info[i].valid_data_len); + if (ret < 0) + goto parse_bin_failed; + + prof_desc->sec_desc[AW_DATA_TYPE_DSP_CFG].len = + aw_bin->header_info[i].valid_data_len; + prof_desc->sec_desc[AW_DATA_TYPE_DSP_CFG].data = + data + aw_bin->header_info[i].valid_data_addr; + break; + case DATA_TYPE_DSP_FW: + ret = aw883xx_dev_dsp_data_order(aw_dev, + data + aw_bin->header_info[i].valid_data_addr, + aw_bin->header_info[i].valid_data_len); + if (ret < 0) + goto parse_bin_failed; + + prof_desc->fw_ver = aw_bin->header_info[i].app_version; + prof_desc->sec_desc[AW_DATA_TYPE_DSP_FW].len = + aw_bin->header_info[i].valid_data_len; + prof_desc->sec_desc[AW_DATA_TYPE_DSP_FW].data = + data + aw_bin->header_info[i].valid_data_addr; + break; + default: + dev_dbg(aw_dev->dev, "bin_data_type not found"); + break; + } + } + devm_kfree(aw_dev->dev, aw_bin); + aw_bin = NULL; + prof_desc->prof_st = AW_PROFILE_OK; + return 0; + +parse_bin_failed: + devm_kfree(aw_dev->dev, 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 *cfg_dde, struct aw_prof_desc *scene_prof_desc) +{ + switch (cfg_dde->data_type) { + case ACF_SEC_TYPE_REG: + return aw_dev_parse_raw_reg(aw_dev, + (u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); + case ACF_SEC_TYPE_DSP_CFG: + return aw_dev_parse_raw_dsp_cfg(aw_dev, + (u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); + case ACF_SEC_TYPE_DSP_FW: + return aw_dev_parse_raw_dsp_fw(aw_dev, + (u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); + case ACF_SEC_TYPE_MUTLBIN: + return aw_dev_prof_parse_multi_bin(aw_dev, + (u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); + default: + dev_dbg(aw_dev->dev, "%s cfg_dde->data_type = %d\n", __func__, cfg_dde->data_type); + 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); + + 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) { + if (cfg_dde[i].dev_profile >= AW_PROFILE_MAX) { + dev_err(aw_dev->dev, "dev_profile [%d] overflow", + cfg_dde[i].dev_profile); + return -EINVAL; + } + 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) { + dev_err(aw_dev->dev, "parse failed"); + return ret; + } + sec_num++; + } + } + } + + if (sec_num == 0) { + dev_dbg(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); + + for (i = 0; i < prof_hdr->a_ddt_num; i++) { + if ((aw_dev->channel == cfg_dde[i].dev_index) && + (cfg_dde[i].type == AW_DEV_DEFAULT_TYPE_ID)) { + if (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR) { + if (cfg_dde[i].dev_profile >= AW_PROFILE_MAX) { + dev_err(aw_dev->dev, "dev_profile [%d] overflow", + cfg_dde[i].dev_profile); + return -EINVAL; + } + 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) { + dev_err(aw_dev->dev, "parse failed"); + return ret; + } + sec_num++; + } + } + } + + if (sec_num == 0) { + dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", sec_num); + return -EINVAL; + } + + 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_sec_data_desc *sec_desc = NULL; + 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) { + sec_desc = prof_desc[i].sec_desc; + if ((sec_desc[AW_DATA_TYPE_REG].data != NULL) && + (sec_desc[AW_DATA_TYPE_REG].len != 0) && + (sec_desc[AW_DATA_TYPE_DSP_CFG].data != NULL) && + (sec_desc[AW_DATA_TYPE_DSP_CFG].len != 0) && + (sec_desc[AW_DATA_TYPE_DSP_FW].data != NULL) && + (sec_desc[AW_DATA_TYPE_DSP_FW].len != 0)) { + prof_info->count++; + } + } + } + + dev_dbg(aw_dev->dev, "get valid profile:%d", aw_dev->prof_info.count); + + if (!prof_info->count) { + dev_err(aw_dev->dev, "no profile data"); + return -EPERM; + } + + prof_info->prof_desc = devm_kzalloc(aw_dev->dev, + prof_info->count * sizeof(struct aw_prof_desc), + GFP_KERNEL); + if (!prof_info->prof_desc) { + 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) { + sec_desc = prof_desc[i].sec_desc; + if ((sec_desc[AW_DATA_TYPE_REG].data != NULL) && + (sec_desc[AW_DATA_TYPE_REG].len != 0) && + (sec_desc[AW_DATA_TYPE_DSP_CFG].data != NULL) && + (sec_desc[AW_DATA_TYPE_DSP_CFG].len != 0) && + (sec_desc[AW_DATA_TYPE_DSP_FW].data != NULL) && + (sec_desc[AW_DATA_TYPE_DSP_FW].len != 0)) { + if (num >= prof_info->count) { + dev_err(aw_dev->dev, "get scene num[%d] overflow count[%d]", + num, prof_info->count); + return -ENOMEM; + } + prof_info->prof_desc[num] = prof_desc[i]; + prof_info->prof_desc[num].id = i; + num++; + } + } + } + + return 0; +} + +static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr) +{ + int ret; + struct aw_all_prof_info *all_prof_info; + + all_prof_info = devm_kzalloc(aw_dev->dev, sizeof(struct aw_all_prof_info), GFP_KERNEL); + if (!all_prof_info) + return -ENOMEM; + + ret = aw_dev_parse_dev_type(aw_dev, prof_hdr, all_prof_info); + if (ret < 0) { + goto exit; + } else if (ret == AW_DEV_TYPE_NONE) { + dev_dbg(aw_dev->dev, "get dev type num is 0, parse default dev"); + ret = aw_dev_parse_dev_default_type(aw_dev, prof_hdr, all_prof_info); + if (ret < 0) + goto exit; + } + + ret = aw_dev_cfg_get_vaild_prof(aw_dev, *all_prof_info); + if (ret < 0) + goto exit; + + aw_dev->prof_info.prof_name_list = profile_name; +exit: + devm_kfree(aw_dev->dev, all_prof_info); + return ret; +} + +static int aw_dev_create_prof_name_list_v_1_0_0_0(struct aw_device *aw_dev) +{ + struct aw_prof_info *prof_info = &aw_dev->prof_info; + struct aw_prof_desc *prof_desc = prof_info->prof_desc; + int i; + + if (!prof_desc) { + 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) { + dev_err(aw_dev->dev, "prof_name_list devm_kzalloc failed"); + return -ENOMEM; + } + + for (i = 0; i < prof_info->count; i++) { + prof_desc[i].id = i; + prof_info->prof_name_list[i] = prof_desc[i].prf_str; + dev_dbg(aw_dev->dev, "prof name is %s", prof_info->prof_name_list[i]); + } + + return 0; +} + +static int aw_get_dde_type_info(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + int i; + int dev_num = 0; + int default_num = 0; + 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); + + for (i = 0; i < cfg_hdr->a_ddt_num; i++) { + if (cfg_dde[i].type == AW_DEV_TYPE_ID) + dev_num++; + + if (cfg_dde[i].type == AW_DEV_DEFAULT_TYPE_ID) + default_num++; + } + + if (dev_num != 0) { + aw_dev->prof_info.prof_type = AW_DEV_TYPE_ID; + } else if (default_num != 0) { + aw_dev->prof_info.prof_type = AW_DEV_DEFAULT_TYPE_ID; + } else { + dev_err(aw_dev->dev, "can't find scene"); + return -EINVAL; + } + + return 0; +} + +static int aw_get_dev_scene_count_v_1_0_0_0(struct aw_device *aw_dev, struct aw_container *aw_cfg, + unsigned int *scene_num) +{ + int i; + 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); + for (i = 0; i < cfg_hdr->a_ddt_num; ++i) { + if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MUTLBIN) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr == cfg_dde[i].dev_addr))) + (*scene_num)++; + } + + return 0; +} + +static int aw_get_default_scene_count_v_1_0_0_0(struct aw_device *aw_dev, + struct aw_container *aw_cfg, + unsigned int *scene_num) +{ + int i; + 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); + + for (i = 0; i < cfg_hdr->a_ddt_num; ++i) { + if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MUTLBIN) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->channel == cfg_dde[i].dev_index)) + (*scene_num)++; + } + + return 0; +} + +static int aw_dev_parse_scene_count_v_1_0_0_0(struct aw_device *aw_dev, + struct aw_container *aw_cfg, + unsigned int *count) +{ + int ret; + + ret = aw_get_dde_type_info(aw_dev, aw_cfg); + if (ret < 0) + return ret; + + switch (aw_dev->prof_info.prof_type) { + case AW_DEV_TYPE_ID: + ret = aw_get_dev_scene_count_v_1_0_0_0(aw_dev, aw_cfg, count); + break; + case AW_DEV_DEFAULT_TYPE_ID: + ret = aw_get_default_scene_count_v_1_0_0_0(aw_dev, aw_cfg, count); + break; + default: + dev_err(aw_dev->dev, "unsupported prof_type[%x]", + aw_dev->prof_info.prof_type); + ret = -EINVAL; + break; + } + + dev_dbg(aw_dev->dev, "scene count is %d", (*count)); + return ret; +} + +static int aw_dev_parse_data_by_sec_type_v_1_0_0_0(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, + struct aw_cfg_dde_v_1_0_0_0 *cfg_dde, + int *cur_scene_id) +{ + int ret; + struct aw_prof_info *prof_info = &aw_dev->prof_info; + + switch (cfg_dde->data_type) { + case ACF_SEC_TYPE_MUTLBIN: + ret = aw_dev_prof_parse_multi_bin(aw_dev, + (u8 *)prof_hdr + cfg_dde->data_offset, + cfg_dde->data_size, &prof_info->prof_desc[*cur_scene_id]); + if (ret < 0) { + dev_err(aw_dev->dev, "parse multi bin failed"); + return ret; + } + prof_info->prof_desc[*cur_scene_id].prf_str = cfg_dde->dev_profile_str; + prof_info->prof_desc[*cur_scene_id].id = cfg_dde->dev_profile; + (*cur_scene_id)++; + break; + default: + dev_err(aw_dev->dev, "unsupported SEC_TYPE [%d]", cfg_dde->data_type); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_parse_dev_type_v_1_0_0_0(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr) +{ + int i = 0; + int ret; + int cur_scene_id = 0; + 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); + + 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) && + (aw_dev->chip_id == cfg_dde[i].chip_id)) { + ret = aw_dev_parse_data_by_sec_type_v_1_0_0_0(aw_dev, prof_hdr, + &cfg_dde[i], &cur_scene_id); + if (ret < 0) { + dev_err(aw_dev->dev, "parse failed"); + return ret; + } + } + } + + if (cur_scene_id == 0) { + dev_err(aw_dev->dev, "get dev type failed, get num [%d]", cur_scene_id); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_parse_default_type_v_1_0_0_0(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr) +{ + int i = 0; + int ret; + int cur_scene_id = 0; + 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); + + for (i = 0; i < prof_hdr->a_ddt_num; i++) { + if ((aw_dev->channel == cfg_dde[i].dev_index) && + (aw_dev->chip_id == cfg_dde[i].chip_id)) { + ret = aw_dev_parse_data_by_sec_type_v_1_0_0_0(aw_dev, prof_hdr, + &cfg_dde[i], &cur_scene_id); + if (ret < 0) { + dev_err(aw_dev->dev, "parse failed"); + return ret; + } + } + } + + if (cur_scene_id == 0) { + dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", cur_scene_id); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_parse_by_hdr_v_1_0_0_0(struct aw_device *aw_dev, + struct aw_cfg_hdr *cfg_hdr) +{ + int ret = 0; + + switch (aw_dev->prof_info.prof_type) { + case AW_DEV_TYPE_ID: + ret = aw_dev_parse_dev_type_v_1_0_0_0(aw_dev, cfg_hdr); + break; + case AW_DEV_DEFAULT_TYPE_ID: + ret = aw_dev_parse_default_type_v_1_0_0_0(aw_dev, cfg_hdr); + break; + default: + dev_err(aw_dev->dev, "prof type matched failed, get num[%d]", + aw_dev->prof_info.prof_type); + ret = -EINVAL; + break; + } + + return ret; +} + +static int aw_dev_load_cfg_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->prof_info; + struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + int ret; + + ret = aw_dev_parse_scene_count_v_1_0_0_0(aw_dev, aw_cfg, &prof_info->count); + if (ret < 0) { + 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) + return -ENOMEM; + + ret = aw_dev_parse_by_hdr_v_1_0_0_0(aw_dev, cfg_hdr); + if (ret < 0) { + dev_err(aw_dev->dev, " failed"); + return ret; + } + + ret = aw_dev_create_prof_name_list_v_1_0_0_0(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "create prof name list failed"); + return ret; + } + + return 0; +} + +int aw883xx_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = NULL; + int ret; + + 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_load_cfg_by_hdr(aw_dev, cfg_hdr); + if (ret < 0) { + dev_err(aw_dev->dev, "hdr_cersion[0x%x] parse failed", + cfg_hdr->a_hdr_version); + return ret; + } + break; + case AW_CFG_HDR_VER_1_0_0_0: + ret = aw_dev_load_cfg_by_hdr_v_1_0_0_0(aw_dev, aw_cfg); + if (ret < 0) { + dev_err(aw_dev->dev, "hdr_cersion[0x%x] parse failed", + cfg_hdr->a_hdr_version); + return ret; + } + break; + default: + dev_err(aw_dev->dev, "unsupported hdr_version [0x%x]", cfg_hdr->a_hdr_version); + return -EINVAL; + } + aw_dev->fw_status = AW_DEV_FW_OK; + return 0; +} + +static unsigned char aw_dev_crc8_check(unsigned char *data, unsigned int data_size) +{ + u8 crc_value = 0x00; + u8 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; + u8 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) { + 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) { + 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) { + 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) { + 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) { + pr_err("a_ddt_num[%d] crc8 check failed, act_crc8:0x%x != data_crc 0x%x", + i, (u32)act_crc8, cfg_dde[i].data_crc); + return -EINVAL; + } + } + + return 0; +} + +static int aw_dev_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; + u8 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) { + 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) { + 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) { + 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) { + 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) { + pr_err("a_ddt_num[%d] crc8 check failed, act_crc8:0x%x != data_crc 0x%x", + i, (u32)act_crc8, cfg_dde[i].data_crc); + return -EINVAL; + } + } + + return 0; + +} + +int aw883xx_dev_load_acf_check(struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = NULL; + + if (!aw_cfg) { + pr_err("aw_prof is NULL"); + return -ENOMEM; + } + + if (aw_cfg->len < sizeof(struct aw_cfg_hdr)) { + 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_check_acf_by_hdr_v_1_0_0_0(aw_cfg); + default: + pr_err("unsupported hdr_version [0x%x]", cfg_hdr->a_hdr_version); + return -EINVAL; + } + + return 0; +} + +int aw883xx_dev_get_profile_count(struct aw_device *aw_dev) +{ + if (!aw_dev) { + pr_err("aw_dev is NULL"); + return -ENOMEM; + } + + return aw_dev->prof_info.count; +} + +int aw883xx_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 aw883xx_dev_get_profile_index(struct aw_device *aw_dev) +{ + return aw_dev->set_prof; +} + +int aw883xx_dev_set_profile_index(struct aw_device *aw_dev, int index) +{ + struct aw_prof_desc *prof_desc = NULL; + + if ((index < aw_dev->prof_info.count) && (index >= 0)) { + aw_dev->set_prof = index; + prof_desc = &aw_dev->prof_info.prof_desc[index]; + + dev_dbg(aw_dev->dev, "set prof[%s]", + aw_dev->prof_info.prof_name_list[prof_desc->id]); + } else { + return -EINVAL; + } + + return 0; +} + +char *aw_dev_get_prof_name(struct aw_device *aw_dev, int index) +{ + struct aw_prof_desc *prof_desc = NULL; + struct aw_prof_info *prof_info = &aw_dev->prof_info; + + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + 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 prof_info->prof_name_list[prof_desc->id]; +} + +int aw883xx_dev_get_prof_data(struct aw_device *aw_dev, int index, + struct aw_prof_desc **prof_desc) +{ + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n", + __func__, index, aw_dev->prof_info.count); + return -EINVAL; + } + + *prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return 0; +} + diff --git a/sound/soc/codecs/aw883xx/aw883xx_bin_parse.h b/sound/soc/codecs/aw883xx/aw883xx_bin_parse.h new file mode 100644 index 000000000000..7f5ce0b1f899 --- /dev/null +++ b/sound/soc/codecs/aw883xx/aw883xx_bin_parse.h @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * aw883xx.c -- ALSA Soc AW883XX codec support + * + * Copyright (c) 2022 AWINIC Technology CO., LTD + * + * Author: Bruce zhao + */ + +#ifndef __AW883XX_BIN_PARSE_H__ +#define __AW883XX_BIN_PARSE_H__ + +#include "aw883xx_device.h" + +#define BIN_NUM_MAX (100) +#define HEADER_LEN (60) +#define CHECK_SUM_OFFSET (0) +#define HEADER_VER_OFFSET (4) +#define BIN_DATA_TYPE_OFFSET (8) +#define BIN_DATA_VER_OFFSET (12) +#define BIN_DATA_LEN_OFFSET (16) +#define UI_VER_OFFSET (20) +#define CHIP_TYPE_OFFSET (24) +#define REG_BYTE_LEN_OFFSET (32) +#define DATA_BYTE_LEN_OFFSET (36) +#define DEVICE_ADDR_OFFSET (40) + +#define AW_FW_CHECK_PART (10) + +/* + * header information + */ +enum return_enum { + BIN_HEADER_VER_ERR = 1, + BIN_DATA_TYPE_ERR = 2, + BIN_DATA_LEN_ERR = 3, + DATA_VER_ERR = 4, + REG_NUM_ERR = 5, + DSP_REG_NUM_ERR = 6, + SOC_APP_NUM_ERR = 7, + BIN_IS_NULL = 8, +}; + +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_DSP_FW = DATA_TYPE_SOC_APP, + DATA_TYPE_MULTI_BINS = 0x00002000, +}; + +/** + * @DATA_VERSION_V1:default little edian + */ +enum data_version_enum { + DATA_VERSION_V1 = 0X00000001, + DATA_VERSION_MAX, +}; + +/** + * @header_len: Frame header length + * @check_sum: Frame header information-Checksum + * @header_ver: Frame header information-Frame header version + * @bin_data_type: Frame header information-Data type + * @bin_data_ver: Frame header information-Data version + * @bin_data_len: Frame header information-Data length + * @ui_ver: Frame header information-ui version + * @chip_type[8]: Frame header information-chip type + * @reg_byte_len: Frame header information-reg byte len + * @data_byte_len: Frame header information-data byte len + * @device_addr: Frame header information-device addr + * @valid_data_len: Length of valid data obtained after parsing + * @valid_data_addr: The offset address of the valid data obtained + * after parsing relative to info + * @reg_num: The number of registers obtained after parsing + * @reg_data_byte_len: The byte length of the register obtained after parsing + * @download_addr: The starting address or download address obtained + * after parsing + * @app_version: The software version number obtained after parsing + */ +struct bin_header_info { + unsigned int header_len; + unsigned int check_sum; + unsigned int header_ver; + unsigned int bin_data_type; + unsigned int bin_data_ver; + unsigned int bin_data_len; + unsigned int ui_ver; + unsigned char chip_type[8]; + unsigned int reg_byte_len; + unsigned int data_byte_len; + unsigned int device_addr; + unsigned int valid_data_len; + unsigned int valid_data_addr; + + unsigned int reg_num; + unsigned int reg_data_byte_len; + unsigned int download_addr; + unsigned int app_version; +}; + +/* + * @len: The size of the bin file obtained from the firmware + * @data[]: Store the bin file obtained from the firmware + */ +struct bin_container { + unsigned int len; + unsigned char data[]; +}; + +/** + * @p_addr: Offset pointer (backward offset pointer to obtain frame header + * information and important information) + * @all_bin_parse_num: The number of all bin files + * @multi_bin_parse_num: The number of single bin files + * @single_bin_parse_num: The number of multiple bin files + * @header_info[BIN_NUM_MAX]: Frame header information and other important data + * obtained after parsing + * @info: Obtained bin file data that needs to be parsed + */ +struct aw_bin { + unsigned char *p_addr; + unsigned int all_bin_parse_num; + unsigned int multi_bin_parse_num; + unsigned int single_bin_parse_num; + struct bin_header_info header_info[BIN_NUM_MAX]; + struct bin_container info; +}; + +/*******************awinic audio parse acf***********************/ +int aw883xx_dev_dsp_data_order(struct aw_device *aw_dev, + unsigned char *data, unsigned int data_len); +int aw883xx_dev_get_prof_data(struct aw_device *aw_dev, int index, + struct aw_prof_desc **prof_desc); +char *aw_dev_get_prof_name(struct aw_device *aw_dev, int index); +int aw883xx_dev_set_profile_index(struct aw_device *aw_dev, int index); +int aw883xx_dev_get_profile_index(struct aw_device *aw_dev); +int aw883xx_dev_check_profile_index(struct aw_device *aw_dev, int index); +int aw883xx_dev_get_profile_count(struct aw_device *aw_dev); +int aw883xx_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg); +int aw883xx_dev_load_acf_check(struct aw_container *aw_cfg); + +#endif From patchwork Thu Dec 8 12:23:11 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: wangweidong.a@awinic.com X-Patchwork-Id: 13068360 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 721DCC63703 for ; Thu, 8 Dec 2022 12:25:26 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 502D91914; Thu, 8 Dec 2022 13:24:34 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 502D91914 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1670502324; bh=+cQtcZRRVOKPTtULKhuPhXJX9PfMKVF7CDGSUL6bQDw=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=JldUNhmlgqk3tl6NGTIXSEAIFfibz07Pt+GLf8w7lPpAca8gX5DsbLdpVnf5fRh0C u6pw4lrsZclmb6G2LMtVxJ+xUBA4CH7i/1Vq1PGS3zkkcbuyELq2At9SykU9JJUfZg 8zpCYZ+Iv1XV7vmDpEblhMYcP5jEdw5uaP1KFSDU= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id A70F6F8056F; Thu, 8 Dec 2022 13:23:47 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id A8037F80568; Thu, 8 Dec 2022 13:23:45 +0100 (CET) Received: from out29-153.mail.aliyun.com (out29-153.mail.aliyun.com [115.124.29.153]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id E7A88F80121 for ; Thu, 8 Dec 2022 13:23:32 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz E7A88F80121 X-Alimail-AntiSpam: AC=CONTINUE; BC=0.07436261|-1; CH=green; DM=|CONTINUE|false|; DS=CONTINUE|ham_enroll_verification|0.00640463-0.0045718-0.989024; FP=0|0|0|0|0|-1|-1|-1; HT=ay29a033018047199; MF=wangweidong.a@awinic.com; NM=0; PH=DS; RN=17; RT=17; SR=0; TI=SMTPD_---.QRKcQvI_1670502207; Received: from ubuntu-VirtualBox..(mailfrom:wangweidong.a@awinic.com fp:SMTPD_---.QRKcQvI_1670502207) by smtp.aliyun-inc.com; Thu, 08 Dec 2022 20:23:28 +0800 From: wangweidong.a@awinic.com To: broonie@kernel.org, perex@perex.cz, alsa-devel@alsa-project.org, tiwai@suse.com, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, ckeepax@opensource.cirrus.com, tanureal@opensource.cirrus.com, quic_potturu@quicinc.com, pierre-louis.bossart@linux.intel.com, cy_huang@richtek.com Subject: [PATCH V6 3/5] ASoC: codecs: aw883xx chip control logic, such as power on and off Date: Thu, 8 Dec 2022 20:23:11 +0800 Message-Id: <20221208122313.55118-4-wangweidong.a@awinic.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221208122313.55118-1-wangweidong.a@awinic.com> References: <20221208122313.55118-1-wangweidong.a@awinic.com> MIME-Version: 1.0 Cc: duanyibo@awinic.com, Weidong Wang , zhaolei@awinic.com, liweilei@awinic.com, yijiangtao@awinic.com, zhangjianming@awinic.com X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" From: Weidong Wang The Awinic AW883XX is an I2S/TDM input, high efficiency digital Smart K audio amplifier with an integrated 10.25V smart boost convert Signed-off-by: Nick Li Signed-off-by: Bruce zhao Signed-off-by: Weidong Wang --- sound/soc/codecs/aw883xx/aw883xx_device.c | 1613 +++++++++++++++++++++ sound/soc/codecs/aw883xx/aw883xx_device.h | 537 +++++++ 2 files changed, 2150 insertions(+) create mode 100644 sound/soc/codecs/aw883xx/aw883xx_device.c create mode 100644 sound/soc/codecs/aw883xx/aw883xx_device.h diff --git a/sound/soc/codecs/aw883xx/aw883xx_device.c b/sound/soc/codecs/aw883xx/aw883xx_device.c new file mode 100644 index 000000000000..f4419e1a2fed --- /dev/null +++ b/sound/soc/codecs/aw883xx/aw883xx_device.c @@ -0,0 +1,1613 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * aw883xx.c -- ALSA Soc AW883XX codec support + * + * Copyright (c) 2022 AWINIC Technology CO., LTD + * + * Author: Bruce zhao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw883xx_data_type.h" +#include "aw883xx_device.h" +#include "aw883xx_bin_parse.h" + +int aw883xx_dev_set_volume(struct aw_device *aw_dev, unsigned short set_vol) +{ + u16 hw_vol = 0; + int ret = -1; + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + + hw_vol = set_vol + vol_desc->init_volume; + + ret = aw_dev->ops.aw_set_hw_volume(aw_dev, hw_vol); + if (ret < 0) { + dev_err(aw_dev->dev, "set volume failed"); + return ret; + } + + return 0; +} + +int aw883xx_dev_get_volume(struct aw_device *aw_dev, unsigned short *get_vol) +{ + int ret = -1; + u16 hw_vol = 0; + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + + ret = aw_dev->ops.aw_get_hw_volume(aw_dev, &hw_vol); + if (ret < 0) { + dev_err(aw_dev->dev, "read volume failed"); + return ret; + } + + if (hw_vol > vol_desc->init_volume) + *get_vol = hw_vol - vol_desc->init_volume; + + return 0; +} + +static void aw_dev_fade_in(struct aw_device *aw_dev) +{ + int i = 0; + struct aw_volume_desc *desc = &aw_dev->volume_desc; + int fade_step = aw_dev->fade_step; + u16 fade_in_vol = desc->ctl_volume; + + if (!aw_dev->fade_en) + return; + + if (fade_step == 0 || aw_dev->fade_in_time == 0) { + aw883xx_dev_set_volume(aw_dev, fade_in_vol); + return; + } + /*volume up*/ + for (i = desc->mute_volume; i >= fade_in_vol; i -= fade_step) { + aw883xx_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10); + } + if (i != fade_in_vol) + aw883xx_dev_set_volume(aw_dev, fade_in_vol); + +} + +static void aw_dev_fade_out(struct aw_device *aw_dev) +{ + int i = 0; + struct aw_volume_desc *desc = &aw_dev->volume_desc; + int fade_step = aw_dev->fade_step; + + if (!aw_dev->fade_en) + return; + + if (fade_step == 0 || aw_dev->fade_out_time == 0) { + aw883xx_dev_set_volume(aw_dev, desc->mute_volume); + return; + } + + for (i = desc->ctl_volume; i <= desc->mute_volume; i += fade_step) { + aw883xx_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } + if (i != desc->mute_volume) { + aw883xx_dev_set_volume(aw_dev, desc->mute_volume); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } +} + +static uint64_t aw_dev_dsp_crc32_reflect(uint64_t ref, unsigned char ch) +{ + int i; + uint64_t value = 0; + + for (i = 1; i < (ch + 1); i++) { + if (ref & 1) + value |= 1 << (ch - i); + + ref >>= 1; + } + + return value; +} + +static unsigned int aw_dev_calc_dsp_cfg_crc32(unsigned char *buf, unsigned int len) +{ + u8 i; + u32 crc = 0xffffffff; + + while (len--) { + for (i = 1; i != 0; i <<= 1) { + if ((crc & 0x80000000) != 0) { + crc <<= 1; + crc ^= 0x1EDC6F41; + } else { + crc <<= 1; + } + + if ((*buf & i) != 0) + crc ^= 0x1EDC6F41; + } + buf++; + } + + return (aw_dev_dsp_crc32_reflect(crc, 32)^0xffffffff); +} + +static int aw_dev_set_dsp_crc32(struct aw_device *aw_dev) +{ + u32 crc_value = 0; + u32 crc_data_len = 0; + int ret = -1; + struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg; + struct aw_dsp_crc_desc *desc = &aw_dev->dsp_crc_desc; + + /*get crc data len*/ + crc_data_len = (desc->dsp_reg - aw_dev->dsp_mem_desc.dsp_cfg_base_addr) * 2; + if (crc_data_len > crc_dsp_cfg->len) { + dev_err(aw_dev->dev, "crc data len :%d > cfg_data len:%d", + crc_data_len, crc_dsp_cfg->len); + return -EINVAL; + } + + if (crc_data_len % 4 != 0) { + dev_err(aw_dev->dev, "The crc data len :%d unsupport", crc_data_len); + return -EINVAL; + } + + crc_value = aw_dev_calc_dsp_cfg_crc32(crc_dsp_cfg->data, crc_data_len); + + dev_dbg(aw_dev->dev, "crc_value:0x%x", crc_value); + ret = aw_dev->ops.aw_dsp_write(aw_dev, desc->dsp_reg, crc_value, + desc->data_type); + if (ret < 0) { + dev_err(aw_dev->dev, "set dsp crc value failed"); + return ret; + } + + return 0; +} + +static void aw_dev_dsp_crc_check_enable(struct aw_device *aw_dev, bool flag) +{ + struct aw_dsp_crc_desc *dsp_crc_desc = &aw_dev->dsp_crc_desc; + struct aw883xx *aw883xx = aw_dev->private_data; + int ret; + + if (flag) { + ret = aw_dev->ops.aw_reg_write_bits(aw883xx->regmap, dsp_crc_desc->ctl_reg, + ~dsp_crc_desc->ctl_mask, dsp_crc_desc->ctl_enable); + if (ret < 0) { + dev_err(aw_dev->dev, "enable dsp crc failed"); + return; + } + } else { + ret = aw_dev->ops.aw_reg_write_bits(aw883xx->regmap, dsp_crc_desc->ctl_reg, + ~dsp_crc_desc->ctl_mask, dsp_crc_desc->ctl_disable); + if (ret < 0) { + dev_err(aw_dev->dev, "close dsp crc failed"); + return; + } + } +} + +static int aw_dev_dsp_st_check(struct aw_device *aw_dev) +{ + struct aw_sysst_desc *desc = &aw_dev->sysst_desc; + struct aw883xx *aw883xx = aw_dev->private_data; + int ret = -1; + unsigned int reg_val = 0; + int i; + + for (i = 0; i < AW_DSP_ST_CHECK_MAX; i++) { + ret = aw_dev->ops.aw_reg_read(aw883xx->regmap, desc->reg, ®_val); + if (ret < 0) { + dev_err(aw_dev->dev, "read reg0x%x failed", desc->reg); + continue; + } + + if ((reg_val & (~desc->dsp_mask)) != desc->dsp_check) { + dev_err(aw_dev->dev, "check dsp st fail,reg_val:0x%04x", reg_val); + ret = -EINVAL; + continue; + } else { + dev_dbg(aw_dev->dev, "dsp st check ok, reg_val:0x%04x", reg_val); + return 0; + } + } + + return ret; +} + +static int aw_dev_dsp_crc32_check(struct aw_device *aw_dev) +{ + int ret; + + if (aw_dev->dsp_cfg == AW_DEV_DSP_BYPASS) { + dev_info(aw_dev->dev, "dsp bypass"); + return 0; + } + + ret = aw_dev_set_dsp_crc32(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "set dsp crc32 failed"); + return ret; + } + + aw_dev_dsp_crc_check_enable(aw_dev, true); + + /*dsp enable*/ + aw883xx_dev_dsp_enable(aw_dev, true); + usleep_range(AW_5000_US, AW_5000_US + 100); + + ret = aw_dev_dsp_st_check(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "check crc32 fail"); + return ret; + } + + aw_dev_dsp_crc_check_enable(aw_dev, false); + aw_dev->dsp_crc_st = AW_DSP_CRC_OK; + return 0; +} + +static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd) +{ + struct aw_pwd_desc *pwd_desc = &aw_dev->pwd_desc; + struct aw883xx *aw883xx = aw_dev->private_data; + + if (pwd) { + aw_dev->ops.aw_reg_write_bits(aw883xx->regmap, pwd_desc->reg, + ~pwd_desc->mask, + pwd_desc->enable); + } else { + aw_dev->ops.aw_reg_write_bits(aw883xx->regmap, pwd_desc->reg, + ~pwd_desc->mask, + pwd_desc->disable); + } +} + +static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd) +{ + struct aw_amppd_desc *amppd_desc = &aw_dev->amppd_desc; + struct aw883xx *aw883xx = aw_dev->private_data; + + if (amppd) { + aw_dev->ops.aw_reg_write_bits(aw883xx->regmap, amppd_desc->reg, + ~amppd_desc->mask, + amppd_desc->enable); + } else { + aw_dev->ops.aw_reg_write_bits(aw883xx->regmap, amppd_desc->reg, + ~amppd_desc->mask, + amppd_desc->disable); + } +} + +void aw883xx_dev_mute(struct aw_device *aw_dev, bool mute) +{ + struct aw_mute_desc *mute_desc = &aw_dev->mute_desc; + struct aw883xx *aw883xx = aw_dev->private_data; + + if (mute) { + aw_dev_fade_out(aw_dev); + aw_dev->ops.aw_reg_write_bits(aw883xx->regmap, mute_desc->reg, + ~mute_desc->mask, mute_desc->enable); + } else { + aw_dev->ops.aw_reg_write_bits(aw883xx->regmap, mute_desc->reg, + ~mute_desc->mask, mute_desc->disable); + aw_dev_fade_in(aw_dev); + } +} + +int aw883xx_dev_get_hmute(struct aw_device *aw_dev) +{ + unsigned int reg_val = 0; + int ret; + struct aw_mute_desc *desc = &aw_dev->mute_desc; + struct aw883xx *aw883xx = aw_dev->private_data; + + ret = aw_dev->ops.aw_reg_read(aw883xx->regmap, desc->reg, ®_val); + if (ret < 0) + return ret; + + if (reg_val & (~desc->mask)) + ret = 1; + else + ret = 0; + + return ret; +} + +static int aw_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk) +{ + int ret = -1; + unsigned int reg_val = 0; + u16 reg_icalk = 0; + struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; + struct aw883xx *aw883xx = aw_dev->private_data; + + ret = aw_dev->ops.aw_reg_read(aw883xx->regmap, desc->icalk_reg, ®_val); + if (ret < 0) { + dev_err(aw_dev->dev, "reg read failed"); + return ret; + } + + reg_icalk = reg_val & (~desc->icalk_reg_mask); + + if (reg_icalk & (~desc->icalk_sign_mask)) + reg_icalk = reg_icalk | desc->icalk_neg_mask; + + *icalk = (int16_t)reg_icalk; + + return 0; +} + +static int aw_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk) +{ + int ret = -1; + unsigned int reg_val = 0; + u16 reg_vcalk = 0; + struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; + struct aw883xx *aw883xx = aw_dev->private_data; + + ret = aw_dev->ops.aw_reg_read(aw883xx->regmap, desc->vcalk_reg, ®_val); + if (ret < 0) { + dev_err(aw_dev->dev, "reg read failed"); + return ret; + } + + reg_val = reg_val >> desc->vcalk_shift; + + reg_vcalk = (u16)reg_val & (~desc->vcalk_reg_mask); + + if (reg_vcalk & (~desc->vcalk_sign_mask)) + reg_vcalk = reg_vcalk | desc->vcalk_neg_mask; + + *vcalk = (int16_t)reg_vcalk; + + return 0; +} + +static int aw_dev_get_vcalk_dac(struct aw_device *aw_dev, int16_t *vcalk) +{ + int ret = -1; + unsigned int reg_val = 0; + u16 reg_vcalk = 0; + struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; + struct aw883xx *aw883xx = aw_dev->private_data; + + ret = aw_dev->ops.aw_reg_read(aw883xx->regmap, desc->icalk_reg, ®_val); + if (ret < 0) { + dev_err(aw_dev->dev, "reg read failed"); + return ret; + } + + reg_vcalk = reg_val >> desc->vcalk_dac_shift; + + if (reg_vcalk & desc->vcalk_dac_mask) + reg_vcalk = reg_vcalk | desc->vcalk_dac_neg_mask; + + *vcalk = (int16_t)reg_vcalk; + + return 0; +} + +int aw883xx_dev_modify_dsp_cfg(struct aw_device *aw_dev, + unsigned int addr, unsigned int dsp_data, unsigned char data_type) +{ + u32 addr_offset = 0; + int len = 0; + u8 temp_data[4] = { 0 }; + struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg; + + dev_dbg(aw_dev->dev, "addr:0x%x, dsp_data:0x%x", addr, dsp_data); + + switch (data_type) { + case AW_DSP_16_DATA: + temp_data[0] = (u8)(dsp_data & 0x00ff); + temp_data[1] = (u8)((dsp_data & 0xff00) >> 8); + len = 2; + break; + case AW_DSP_32_DATA: + temp_data[0] = (u8)(dsp_data & 0x000000ff); + temp_data[1] = (u8)((dsp_data & 0x0000ff00) >> 8); + temp_data[2] = (u8)((dsp_data & 0x00ff0000) >> 16); + temp_data[3] = (u8)((dsp_data & 0xff000000) >> 24); + len = 4; + break; + default: + dev_err(aw_dev->dev, "data type[%d] unsupported", data_type); + return -EINVAL; + } + + addr_offset = (addr - aw_dev->dsp_mem_desc.dsp_cfg_base_addr) * 2; + if (addr_offset > crc_dsp_cfg->len) { + dev_err(aw_dev->dev, "addr_offset[%d] > crc_dsp_cfg->len[%d]", + addr_offset, crc_dsp_cfg->len); + return -EINVAL; + } + + memcpy(crc_dsp_cfg->data + addr_offset, temp_data, len); + return 0; +} + +static int aw_dev_vsense_select(struct aw_device *aw_dev, int *vsense_select) +{ + int ret = -1; + struct aw_vcalb_desc *desc = &aw_dev->vcalb_desc; + unsigned int vsense_reg_val; + struct aw883xx *aw883xx = aw_dev->private_data; + + ret = aw_dev->ops.aw_reg_read(aw883xx->regmap, desc->vcalb_vsense_reg, &vsense_reg_val); + if (ret < 0) { + dev_err(aw_dev->dev, "read vsense_reg_val failed"); + return ret; + } + dev_dbg(aw_dev->dev, "vsense_reg = 0x%x", vsense_reg_val); + + if (vsense_reg_val & (~desc->vcalk_vdsel_mask)) { + *vsense_select = AW_DEV_VDSEL_VSENSE; + dev_dbg(aw_dev->dev, "vsense outside"); + return 0; + } + + *vsense_select = AW_DEV_VDSEL_DAC; + dev_dbg(aw_dev->dev, "vsense inside"); + return 0; +} + +static int aw_dev_set_vcalb(struct aw_device *aw_dev) +{ + int ret = -1; + u32 reg_val = 0; + 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; + u32 vcalb_adj; + int vsense_select = -1; + + ret = aw_dev->ops.aw_dsp_read(aw_dev, desc->vcalb_dsp_reg, &vcalb_adj, desc->data_type); + if (ret < 0) { + dev_err(aw_dev->dev, "read vcalb_adj failed"); + return ret; + } + + ret = aw_dev_vsense_select(aw_dev, &vsense_select); + if (ret < 0) + return ret; + dev_dbg(aw_dev->dev, "vsense_select = %d", vsense_select); + + ret = aw_dev_get_icalk(aw_dev, &icalk_val); + if (ret < 0) + return ret; + icalk = desc->cabl_base_value + desc->icalk_value_factor * icalk_val; + + switch (vsense_select) { + case AW_DEV_VDSEL_VSENSE: + ret = aw_dev_get_vcalk(aw_dev, &vcalk_val); + if (ret < 0) + return ret; + vcalk = desc->cabl_base_value + desc->vcalk_value_factor * vcalk_val; + vcalb = desc->vcal_factor * desc->vscal_factor / + desc->iscal_factor * icalk / vcalk * vcalb_adj; + + dev_dbg(aw_dev->dev, "vcalk_factor=%d, vscal_factor=%d, icalk=%d, vcalk=%d", + desc->vcalk_value_factor, desc->vscal_factor, icalk, vcalk); + break; + case AW_DEV_VDSEL_DAC: + ret = aw_dev_get_vcalk_dac(aw_dev, &vcalk_val); + if (ret < 0) + return ret; + vcalk = desc->cabl_base_value + desc->vcalk_value_factor_vsense_in * vcalk_val; + vcalb = desc->vcal_factor * desc->vscal_factor_vsense_in / + desc->iscal_factor * icalk / vcalk * vcalb_adj; + + dev_dbg(aw_dev->dev, "vcalk_dac_factor=%d, vscal_dac_factor=%d, icalk=%d, vcalk=%d", + desc->vcalk_value_factor_vsense_in, + desc->vscal_factor_vsense_in, icalk, vcalk); + break; + default: + dev_err(aw_dev->dev, "unsupport vsense status"); + return -EINVAL; + } + + if ((vcalk == 0) || (desc->iscal_factor == 0)) { + dev_err(aw_dev->dev, "vcalk:%d or desc->iscal_factor:%d unsupported", + vcalk, desc->iscal_factor); + return -EINVAL; + } + + vcalb = vcalb >> aw_dev->vcalb_desc.vcalb_adj_shift; + reg_val = (u32)vcalb; + + dev_dbg(aw_dev->dev, "vcalb=%d, reg_val=0x%x, vcalb_adj =0x%x", + vcalb, reg_val, vcalb_adj); + + ret = aw_dev->ops.aw_dsp_write(aw_dev, desc->vcalb_dsp_reg, reg_val, desc->data_type); + if (ret < 0) { + dev_err(aw_dev->dev, "write vcalb failed"); + return ret; + } + + ret = aw883xx_dev_modify_dsp_cfg(aw_dev, desc->vcalb_dsp_reg, + (u32)reg_val, desc->data_type); + if (ret < 0) { + dev_err(aw_dev->dev, "modify dsp cfg failed"); + return ret; + } + + return 0; +} + +static int aw_dev_get_cali_f0_delay(struct aw_device *aw_dev) +{ + struct aw_cali_delay_desc *desc = &aw_dev->cali_delay_desc; + u32 cali_delay = 0; + int ret = -1; + + ret = aw_dev->ops.aw_dsp_read(aw_dev, + desc->dsp_reg, &cali_delay, desc->data_type); + if (ret < 0) { + dev_err(aw_dev->dev, "read cali delay failed, ret=%d", ret); + return ret; + } + + desc->delay = AW_CALI_DELAY_CACL(cali_delay); + dev_dbg(aw_dev->dev, "read cali delay: %d ms", desc->delay); + + return 0; +} + +static void aw883xx_dev_get_int_status(struct aw_device *aw_dev, unsigned short *int_status) +{ + int ret = -1; + unsigned int reg_val = 0; + struct aw883xx *aw883xx = aw_dev->private_data; + + ret = aw_dev->ops.aw_reg_read(aw883xx->regmap, aw_dev->int_desc.st_reg, ®_val); + if (ret < 0) + dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret); + else + *int_status = reg_val; + + dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", *int_status); +} + +static void aw883xx_dev_clear_int_status(struct aw_device *aw_dev) +{ + u16 int_status = 0; + + /*read int status and clear*/ + aw883xx_dev_get_int_status(aw_dev, &int_status); + /*make sure int status is clear*/ + aw883xx_dev_get_int_status(aw_dev, &int_status); +} + +int aw883xx_dev_get_iis_status(struct aw_device *aw_dev) +{ + int ret = -1; + unsigned int reg_val = 0; + struct aw_sysst_desc *desc = &aw_dev->sysst_desc; + struct aw883xx *aw883xx = aw_dev->private_data; + + aw_dev->ops.aw_reg_read(aw883xx->regmap, desc->reg, ®_val); + if ((reg_val & desc->pll_check) == desc->pll_check) + ret = 0; + else + dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val); + + return ret; +} + +static int aw_dev_mode1_pll_check(struct aw_device *aw_dev) +{ + int ret = -1; + u16 i = 0; + + for (i = 0; i < AW_DEV_SYSST_CHECK_MAX; i++) { + ret = aw883xx_dev_get_iis_status(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "mode1 iis signal check error"); + usleep_range(AW_2000_US, AW_2000_US + 10); + } else { + return 0; + } + } + + return ret; +} + +static int aw_dev_mode2_pll_check(struct aw_device *aw_dev) +{ + int ret = -1; + u16 i = 0; + unsigned int reg_val = 0; + struct aw_cco_mux_desc *cco_mux_desc = &aw_dev->cco_mux_desc; + struct aw883xx *aw883xx = aw_dev->private_data; + + aw_dev->ops.aw_reg_read(aw883xx->regmap, cco_mux_desc->reg, ®_val); + reg_val &= (~cco_mux_desc->mask); + if (reg_val == cco_mux_desc->divider) { + dev_dbg(aw_dev->dev, "CCO_MUX is already divider"); + return ret; + } + + /* change mode2 */ + aw_dev->ops.aw_reg_write_bits(aw883xx->regmap, cco_mux_desc->reg, + ~cco_mux_desc->mask, cco_mux_desc->divider); + + for (i = 0; i < AW_DEV_SYSST_CHECK_MAX; i++) { + ret = aw883xx_dev_get_iis_status(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "mode2 iis signal check error"); + usleep_range(AW_2000_US, AW_2000_US + 10); + } else { + break; + } + } + + /* change mode1*/ + aw_dev->ops.aw_reg_write_bits(aw883xx->regmap, cco_mux_desc->reg, + ~cco_mux_desc->mask, cco_mux_desc->bypass); + + if (ret == 0) { + usleep_range(AW_2000_US, AW_2000_US + 10); + for (i = 0; i < AW_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_mode1_pll_check(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error"); + usleep_range(AW_2000_US, AW_2000_US + 10); + } else { + break; + } + } + } + + return ret; +} + +int aw883xx_dev_syspll_check(struct aw_device *aw_dev) +{ + int ret = -1; + + ret = aw_dev_mode1_pll_check(aw_dev); + if (ret < 0) { + dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check"); + ret = aw_dev_mode2_pll_check(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "mode2 check iis failed"); + return ret; + } + } + + return ret; +} + +int aw883xx_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; + struct aw883xx *aw883xx = aw_dev->private_data; + + for (i = 0; i < AW_DEV_SYSST_CHECK_MAX; i++) { + aw_dev->ops.aw_reg_read(aw883xx->regmap, desc->reg, ®_val); + if (((reg_val & (~desc->st_mask)) & desc->st_check) != desc->st_check) { + dev_dbg(aw_dev->dev, "check fail, cnt=%d, reg_val=0x%04x", + i, reg_val); + usleep_range(AW_2000_US, AW_2000_US + 10); + } else { + ret = 0; + break; + } + } + if (ret < 0) + dev_err(aw_dev->dev, "check fail"); + + return ret; +} + +static int aw_dev_sysint_check(struct aw_device *aw_dev) +{ + int ret = 0; + u16 reg_val = 0; + struct aw_int_desc *desc = &aw_dev->int_desc; + + aw883xx_dev_get_int_status(aw_dev, ®_val); + + if (reg_val & (desc->intst_mask)) { + dev_err(aw_dev->dev, "pa stop check fail:0x%04x", reg_val); + ret = -EINVAL; + } + + return ret; +} + +static void aw_dev_get_cur_mode_st(struct aw_device *aw_dev) +{ + unsigned int reg_val; + struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; + struct aw883xx *aw883xx = aw_dev->private_data; + + aw_dev->ops.aw_reg_read(aw883xx->regmap, aw_dev->pwd_desc.reg, ®_val); + if ((reg_val & (~profctrl_desc->mask)) == profctrl_desc->rcv_mode_val) + profctrl_desc->cur_mode = AW_RCV_MODE; + else + profctrl_desc->cur_mode = AW_NOT_RCV_MODE; +} + +void aw883xx_dev_dsp_enable(struct aw_device *aw_dev, bool dsp) +{ + int ret = -1; + struct aw_dsp_en_desc *desc = &aw_dev->dsp_en_desc; + struct aw883xx *aw883xx = aw_dev->private_data; + + if (dsp) { + ret = aw_dev->ops.aw_reg_write_bits(aw883xx->regmap, desc->reg, + ~desc->mask, desc->enable); + if (ret < 0) + dev_err(aw_dev->dev, "enable dsp failed"); + } else { + ret = aw_dev->ops.aw_reg_write_bits(aw883xx->regmap, desc->reg, + ~desc->mask, desc->disable); + if (ret < 0) + dev_err(aw_dev->dev, "disable dsp failed"); + } +} + +static void aw_dev_get_dsp_config(struct aw_device *aw_dev, unsigned char *dsp_cfg) +{ + int ret = -1; + unsigned int reg_val = 0; + struct aw_dsp_en_desc *desc = &aw_dev->dsp_en_desc; + struct aw883xx *aw883xx = aw_dev->private_data; + + ret = aw_dev->ops.aw_reg_read(aw883xx->regmap, desc->reg, ®_val); + if (ret < 0) { + dev_err(aw_dev->dev, "reg read failed"); + return; + } + + if (reg_val & (~desc->mask)) + *dsp_cfg = AW_DEV_DSP_BYPASS; + else + *dsp_cfg = AW_DEV_DSP_WORK; +} + +void aw883xx_dev_memclk_select(struct aw_device *aw_dev, unsigned char flag) +{ + struct aw_memclk_desc *desc = &aw_dev->memclk_desc; + struct aw883xx *aw883xx = aw_dev->private_data; + int ret = -1; + + switch (flag) { + case AW_DEV_MEMCLK_PLL: + ret = aw_dev->ops.aw_reg_write_bits(aw883xx->regmap, desc->reg, + ~desc->mask, desc->mcu_hclk); + if (ret < 0) + dev_err(aw_dev->dev, "memclk select pll failed"); + break; + case AW_DEV_MEMCLK_OSC: + ret = aw_dev->ops.aw_reg_write_bits(aw883xx->regmap, desc->reg, + ~desc->mask, desc->osc_clk); + if (ret < 0) + dev_err(aw_dev->dev, "memclk select OSC failed"); + break; + default: + dev_err(aw_dev->dev, "unknown memclk config, flag=0x%x", flag); + break; + } +} + +int aw883xx_dev_get_dsp_status(struct aw_device *aw_dev) +{ + int ret = -1; + unsigned int reg_val = 0; + struct aw_watch_dog_desc *desc = &aw_dev->watch_dog_desc; + struct aw883xx *aw883xx = aw_dev->private_data; + + aw_dev->ops.aw_reg_read(aw883xx->regmap, desc->reg, ®_val); + if (reg_val & (~desc->mask)) + ret = 0; + + return ret; +} + +static int aw_dev_get_vmax(struct aw_device *aw_dev, unsigned int *vmax) +{ + int ret = -1; + struct aw_vmax_desc *desc = &aw_dev->vmax_desc; + + ret = aw_dev->ops.aw_dsp_read(aw_dev, desc->dsp_reg, vmax, desc->data_type); + if (ret < 0) { + dev_err(aw_dev->dev, "get vmax failed"); + return ret; + } + + return 0; +} + +/* + * aw_dev update cfg + */ +static int aw_dev_reg_container_update(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int i, ret; + u8 reg_addr = 0; + u16 reg_val = 0; + unsigned int read_val = 0; + u16 read_vol = 0; + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + struct aw883xx *aw883xx = aw_dev->private_data; + int16_t *reg_data = NULL; + int data_len; + + reg_data = (int16_t *)data; + data_len = len >> 1; + + if (data_len % 2 != 0) { + dev_err(aw_dev->dev, "data len:%d unsupported", + data_len); + return -EINVAL; + } + + for (i = 0; i < data_len; i += 2) { + reg_addr = reg_data[i]; + reg_val = reg_data[i + 1]; + dev_dbg(aw_dev->dev, "reg = 0x%02x, val = 0x%04x", + reg_addr, reg_val); + if (reg_addr == aw_dev->mute_desc.reg) { + aw_dev->ops.aw_reg_read(aw883xx->regmap, reg_addr, &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->dsp_crc_desc.ctl_reg) + reg_val &= aw_dev->dsp_crc_desc.ctl_mask; + + if (reg_addr == aw_dev->chansel_desc.txchan_reg) { + /*close tx*/ + reg_val &= aw_dev->tx_en_desc.tx_en_mask; + reg_val |= aw_dev->tx_en_desc.tx_disable; + } + + if (reg_addr == aw_dev->volume_desc.reg) { + read_vol = (reg_val & (~aw_dev->volume_desc.mask)) >> + aw_dev->volume_desc.shift; + aw_dev->volume_desc.init_volume = + aw_dev->ops.aw_reg_val_to_db(read_vol); + } + ret = aw_dev->ops.aw_reg_write(aw883xx->regmap, reg_addr, reg_val); + if (ret < 0) + break; + + } + + aw_dev_get_cur_mode_st(aw_dev); + + if (aw_dev->cur_prof != aw_dev->set_prof) { + /*clear control volume when PA change profile*/ + vol_desc->ctl_volume = 0; + } else { + /*keep control volume when PA start with sync mode*/ + aw883xx_dev_set_volume(aw_dev, vol_desc->ctl_volume); + } + + /*keep min volume*/ + if (aw_dev->fade_en) + aw883xx_dev_set_volume(aw_dev, vol_desc->mute_volume); + + aw_dev_get_dsp_config(aw_dev, &aw_dev->dsp_cfg); + + dev_dbg(aw_dev->dev, "exit"); + + return ret; +} + +static int aw_dev_reg_update(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int ret = 0; + + dev_dbg(aw_dev->dev, "reg len:%d", len); + + if (len && (data != NULL)) { + ret = aw_dev_reg_container_update(aw_dev, data, len); + if (ret < 0) { + dev_err(aw_dev->dev, "reg update failed"); + return ret; + } + } else { + dev_err(aw_dev->dev, "reg data is null or len is 0"); + return -EPERM; + } + + return 0; +} + +static int aw_dev_get_ra(struct aw_cali_desc *cali_desc) +{ + int ret; + u32 dsp_ra; + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + struct aw_ra_desc *desc = &aw_dev->ra_desc; + + ret = aw_dev->ops.aw_dsp_read(aw_dev, desc->dsp_reg, + &dsp_ra, desc->data_type); + if (ret < 0) { + dev_err(aw_dev->dev, "read ra error"); + return ret; + } + + cali_desc->ra = AW_DSP_RE_TO_SHOW_RE(dsp_ra, + aw_dev->adpz_re_desc.shift); + dev_dbg(aw_dev->dev, "get ra:%d", cali_desc->ra); + return 0; +} + +static int aw_dev_dsp_container_update(struct aw_device *aw_dev, + unsigned char *data, unsigned int len, unsigned short base) +{ + int i; + struct aw_dsp_mem_desc *dsp_mem_desc = &aw_dev->dsp_mem_desc; + struct aw883xx *aw883xx = aw_dev->private_data; +#ifdef AW_DSP_I2C_WRITES + u32 tmp_len = 0; +#else + u16 reg_val = 0; +#endif + + mutex_lock(aw_dev->dsp_lock); +#ifdef AW_DSP_I2C_WRITES + /* i2c writes */ + aw_dev->ops.aw_i2c_write(aw883xx->regmap, dsp_mem_desc->dsp_madd_reg, base); + + for (i = 0; i < len; i += AW_MAX_RAM_WRITE_BYTE_SIZE) { + if ((len - i) < AW_MAX_RAM_WRITE_BYTE_SIZE) + tmp_len = len - i; + else + tmp_len = AW_MAX_RAM_WRITE_BYTE_SIZE; + aw_dev->ops.aw_i2c_writes(aw883xx->regmap, dsp_mem_desc->dsp_mdat_reg, + &data[i], tmp_len); + } + +#else + /* i2c write */ + aw_dev->ops.aw_i2c_write(aw883xx->regmap, dsp_mem_desc->dsp_madd_reg, base); + for (i = 0; i < len; i += 2) { + reg_val = (data[i] << 8) + data[i + 1]; + aw_dev->ops.aw_i2c_write(aw883xx->regmap, dsp_mem_desc->dsp_mdat_reg, + reg_val); + } +#endif + mutex_unlock(aw_dev->dsp_lock); + dev_dbg(aw_dev->dev, "exit"); + + return 0; +} + +static int aw_dev_dsp_fw_update(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + struct aw_dsp_mem_desc *dsp_mem_desc = &aw_dev->dsp_mem_desc; + + dev_dbg(aw_dev->dev, "dsp firmware len:%d", len); + + if (len && (data != NULL)) { + aw_dev_dsp_container_update(aw_dev, + data, len, dsp_mem_desc->dsp_fw_base_addr); + aw_dev->dsp_fw_len = len; + } else { + dev_err(aw_dev->dev, "dsp firmware data is null or len is 0"); + return -EPERM; + } + + return 0; +} + +static int aw_dev_copy_to_crc_dsp_cfg(struct aw_device *aw_dev, + unsigned char *data, unsigned int size) +{ + struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg; + int ret; + + if (!crc_dsp_cfg->data) { + crc_dsp_cfg->data = devm_kzalloc(aw_dev->dev, size, GFP_KERNEL); + if (!crc_dsp_cfg->data) + return -ENOMEM; + crc_dsp_cfg->len = size; + } else if (crc_dsp_cfg->len < size) { + devm_kfree(aw_dev->dev, crc_dsp_cfg->data); + crc_dsp_cfg->data = NULL; + crc_dsp_cfg->data = devm_kzalloc(aw_dev->dev, size, GFP_KERNEL); + if (!crc_dsp_cfg->data) { + dev_err(aw_dev->dev, "error allocating memory"); + return -ENOMEM; + } + crc_dsp_cfg->len = size; + } + memcpy(crc_dsp_cfg->data, data, size); + ret = aw883xx_dev_dsp_data_order(aw_dev, crc_dsp_cfg->data, size); + if (ret < 0) + return ret; + + return 0; +} + +int aw883xx_dev_dsp_cfg_update(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + struct aw_dsp_mem_desc *dsp_mem_desc = &aw_dev->dsp_mem_desc; + int ret; + + dev_dbg(aw_dev->dev, "dsp config len:%d", len); + + if (len && (data != NULL)) { + aw_dev_dsp_container_update(aw_dev, + data, len, dsp_mem_desc->dsp_cfg_base_addr); + aw_dev->dsp_cfg_len = len; + + ret = aw_dev_copy_to_crc_dsp_cfg(aw_dev, data, len); + if (ret < 0) + return ret; + + ret = aw_dev_set_vcalb(aw_dev); + if (ret < 0) + return ret; + ret = aw_dev_get_ra(&aw_dev->cali_desc); + if (ret < 0) + return ret; + ret = aw_dev_get_cali_f0_delay(aw_dev); + if (ret < 0) + return ret; + + ret = aw_dev_get_vmax(aw_dev, &aw_dev->vmax_desc.init_vmax); + if (ret < 0) { + dev_err(aw_dev->dev, "get vmax failed"); + return ret; + } + dev_dbg(aw_dev->dev, "get init vmax:0x%x", + aw_dev->vmax_desc.init_vmax); + aw_dev->dsp_crc_st = AW_DSP_CRC_NA; + } else { + dev_err(aw_dev->dev, "dsp config data is null or len is 0"); + return -EPERM; + } + + return 0; +} + +static int aw_dev_sram_check(struct aw_device *aw_dev) +{ + int ret = -1; + unsigned int reg_val = 0; + struct aw_dsp_mem_desc *dsp_mem_desc = &aw_dev->dsp_mem_desc; + struct aw883xx *aw883xx = aw_dev->private_data; + + mutex_lock(aw_dev->dsp_lock); + /*check the odd bits of reg 0x40*/ + aw_dev->ops.aw_i2c_write(aw883xx->regmap, dsp_mem_desc->dsp_madd_reg, + AW_DSP_ODD_NUM_BIT_TEST); + aw_dev->ops.aw_i2c_read(aw883xx->regmap, dsp_mem_desc->dsp_madd_reg, ®_val); + if (reg_val != AW_DSP_ODD_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check reg 0x40 odd bit failed, read[0x%x] does not match write[0x%x]", + reg_val, AW_DSP_ODD_NUM_BIT_TEST); + goto error; + } + + /*check the even bits of reg 0x40*/ + aw_dev->ops.aw_i2c_write(aw883xx->regmap, dsp_mem_desc->dsp_madd_reg, + AW_DSP_EVEN_NUM_BIT_TEST); + aw_dev->ops.aw_i2c_read(aw883xx->regmap, dsp_mem_desc->dsp_madd_reg, ®_val); + if (reg_val != AW_DSP_EVEN_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check reg 0x40 even bit failed, read[0x%x] does not match write[0x%x]", + reg_val, AW_DSP_EVEN_NUM_BIT_TEST); + goto error; + } + + /*check dsp_fw_base_addr*/ + aw_dev->ops.aw_i2c_write(aw883xx->regmap, dsp_mem_desc->dsp_madd_reg, + dsp_mem_desc->dsp_fw_base_addr); + aw_dev->ops.aw_i2c_write(aw883xx->regmap, dsp_mem_desc->dsp_mdat_reg, + AW_DSP_EVEN_NUM_BIT_TEST); + + aw_dev->ops.aw_i2c_write(aw883xx->regmap, dsp_mem_desc->dsp_madd_reg, + dsp_mem_desc->dsp_fw_base_addr); + aw_dev->ops.aw_i2c_read(aw883xx->regmap, dsp_mem_desc->dsp_mdat_reg, ®_val); + if (reg_val != AW_DSP_EVEN_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check dsp fw addr failed, read[0x%x] does not match write[0x%x]", + reg_val, AW_DSP_EVEN_NUM_BIT_TEST); + goto error; + } + + /*check dsp_cfg_base_addr*/ + aw_dev->ops.aw_i2c_write(aw883xx->regmap, dsp_mem_desc->dsp_madd_reg, + dsp_mem_desc->dsp_cfg_base_addr); + aw_dev->ops.aw_i2c_write(aw883xx->regmap, dsp_mem_desc->dsp_mdat_reg, + AW_DSP_ODD_NUM_BIT_TEST); + + aw_dev->ops.aw_i2c_write(aw883xx->regmap, dsp_mem_desc->dsp_madd_reg, + dsp_mem_desc->dsp_cfg_base_addr); + aw_dev->ops.aw_i2c_read(aw883xx->regmap, dsp_mem_desc->dsp_mdat_reg, ®_val); + if (reg_val != AW_DSP_ODD_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] does not match write[0x%x]", + reg_val, AW_DSP_ODD_NUM_BIT_TEST); + goto error; + } + + mutex_unlock(aw_dev->dsp_lock); + return 0; + +error: + mutex_unlock(aw_dev->dsp_lock); + return ret; +} + +int aw883xx_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en) +{ + int ret = -1; + struct aw_prof_desc *set_prof_desc = NULL; + struct aw_sec_data_desc *sec_desc = NULL; + char *prof_name = NULL; + + if ((aw_dev->cur_prof == aw_dev->set_prof) && + (force_up_en == AW_FORCE_UPDATE_OFF)) { + dev_dbg(aw_dev->dev, "scene no change, not update"); + return 0; + } + + if (aw_dev->fw_status == AW_DEV_FW_FAILED) { + dev_err(aw_dev->dev, "fw status[%d] error", aw_dev->fw_status); + return -EPERM; + } + + prof_name = aw_dev_get_prof_name(aw_dev, aw_dev->set_prof); + if (!prof_name) + return -ENOMEM; + + dev_dbg(aw_dev->dev, "start update %s", prof_name); + + ret = aw883xx_dev_get_prof_data(aw_dev, aw_dev->set_prof, &set_prof_desc); + if (ret < 0) + return ret; + + /*update reg*/ + sec_desc = set_prof_desc->sec_desc; + ret = aw_dev_reg_update(aw_dev, sec_desc[AW_DATA_TYPE_REG].data, + sec_desc[AW_DATA_TYPE_REG].len); + if (ret < 0) { + dev_err(aw_dev->dev, "update reg failed"); + return ret; + } + + aw883xx_dev_mute(aw_dev, true); + + if (aw_dev->dsp_cfg == AW_DEV_DSP_WORK) + aw883xx_dev_dsp_enable(aw_dev, false); + + aw883xx_dev_memclk_select(aw_dev, AW_DEV_MEMCLK_OSC); + + if (up_dsp_fw_en) { + ret = aw_dev_sram_check(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "check sram failed"); + goto error; + } + + /*update dsp firmware*/ + dev_dbg(aw_dev->dev, "fw_ver: [%x]", set_prof_desc->fw_ver); + ret = aw_dev_dsp_fw_update(aw_dev, sec_desc[AW_DATA_TYPE_DSP_FW].data, + sec_desc[AW_DATA_TYPE_DSP_FW].len); + if (ret < 0) { + dev_err(aw_dev->dev, "update dsp fw failed"); + goto error; + } + } + + /*update dsp config*/ + ret = aw883xx_dev_dsp_cfg_update(aw_dev, sec_desc[AW_DATA_TYPE_DSP_CFG].data, + sec_desc[AW_DATA_TYPE_DSP_CFG].len); + if (ret < 0) { + dev_err(aw_dev->dev, "update dsp cfg failed"); + goto error; + } + + aw883xx_dev_memclk_select(aw_dev, AW_DEV_MEMCLK_PLL); + + aw_dev->cur_prof = aw_dev->set_prof; + + return 0; + +error: + aw883xx_dev_memclk_select(aw_dev, AW_DEV_MEMCLK_PLL); + + return ret; +} + +int aw883xx_dev_dsp_check(struct aw_device *aw_dev) +{ + int ret = -1; + u16 i = 0; + + switch (aw_dev->dsp_cfg) { + case AW_DEV_DSP_BYPASS: + dev_dbg(aw_dev->dev, "dsp bypass"); + ret = 0; + break; + case AW_DEV_DSP_WORK: + aw883xx_dev_dsp_enable(aw_dev, false); + aw883xx_dev_dsp_enable(aw_dev, true); + usleep_range(AW_1000_US, AW_1000_US + 10); + for (i = 0; i < AW_DEV_DSP_CHECK_MAX; i++) { + ret = aw883xx_dev_get_dsp_status(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "dsp wdt status error=%d", ret); + usleep_range(AW_2000_US, AW_2000_US + 10); + } + } + break; + default: + dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg); + ret = -EINVAL; + break; + } + + return ret; +} + +static void aw_dev_set_cfg_f0_fs(struct aw_device *aw_dev) +{ + u32 f0_fs = 0; + struct aw_cfgf0_fs_desc *cfgf0_fs_desc = &aw_dev->cfgf0_fs_desc; + int ret; + + if (aw_dev->ops.aw_set_cfg_f0_fs) { + aw_dev->ops.aw_set_cfg_f0_fs(aw_dev, &f0_fs); + ret = aw883xx_dev_modify_dsp_cfg(aw_dev, cfgf0_fs_desc->dsp_reg, + f0_fs, cfgf0_fs_desc->data_type); + if (ret < 0) { + dev_err(aw_dev->dev, "modify dsp cfg failed"); + return; + } + } +} + +static void aw_dev_cali_re_update(struct aw_cali_desc *cali_desc) +{ + int ret = 0; + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + + if (aw_dev->cali_desc.cali_re < AW_CALI_RE_MAX && + aw_dev->cali_desc.cali_re > AW_CALI_RE_MIN) { + if (aw_dev->ops.aw_set_cali_re) { + ret = aw_dev->ops.aw_set_cali_re(aw_dev); + if (ret < 0) + dev_err(aw_dev->dev, "set cali re failed"); + } else { + dev_err(aw_dev->dev, "cali_re:%d out of range, no set", + aw_dev->cali_desc.cali_re); + } + } +} + +int aw883xx_device_start(struct aw_device *aw_dev) +{ + int ret = -1; + + if (aw_dev->status == AW_DEV_PW_ON) { + dev_info(aw_dev->dev, "already power on"); + return 0; + } + + /*power on*/ + aw_dev_pwd(aw_dev, false); + usleep_range(AW_2000_US, AW_2000_US + 10); + + ret = aw883xx_dev_syspll_check(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "pll check failed cannot start"); + goto pll_check_fail; + } + + /*amppd on*/ + aw_dev_amppd(aw_dev, false); + usleep_range(AW_1000_US, AW_1000_US + 50); + + /*check i2s status*/ + ret = aw883xx_dev_sysst_check(aw_dev); + if (ret < 0) + goto sysst_check_fail; + + if (aw_dev->dsp_cfg == AW_DEV_DSP_WORK) { + /*dsp bypass*/ + aw883xx_dev_dsp_enable(aw_dev, false); + if (aw_dev->ops.aw_dsp_fw_check) { + ret = aw_dev->ops.aw_dsp_fw_check(aw_dev); + if (ret < 0) + goto dsp_fw_check_fail; + } + aw_dev_set_cfg_f0_fs(aw_dev); + + aw_dev_cali_re_update(&aw_dev->cali_desc); + + if (aw_dev->dsp_crc_st != AW_DSP_CRC_OK) { + ret = aw_dev_dsp_crc32_check(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "dsp crc check failed"); + goto crc_check_fail; + } + } + + ret = aw883xx_dev_dsp_check(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "check dsp status failed"); + goto dsp_check_fail; + } + } else { + dev_dbg(aw_dev->dev, "start pa with dsp bypass"); + } + + /*enable tx feedback*/ + if (aw_dev->ops.aw_i2s_tx_enable) + aw_dev->ops.aw_i2s_tx_enable(aw_dev, true); + + /*close mute*/ + aw883xx_dev_mute(aw_dev, false); + /*clear inturrupt*/ + aw883xx_dev_clear_int_status(aw_dev); + aw_dev->status = AW_DEV_PW_ON; + + return 0; + +dsp_check_fail: +crc_check_fail: + aw883xx_dev_dsp_enable(aw_dev, false); +dsp_fw_check_fail: +sysst_check_fail: + aw883xx_dev_clear_int_status(aw_dev); + aw_dev_amppd(aw_dev, true); +pll_check_fail: + aw_dev_pwd(aw_dev, true); + aw_dev->status = AW_DEV_PW_OFF; + return ret; +} + +int aw883xx_device_stop(struct aw_device *aw_dev) +{ + struct aw_sec_data_desc *dsp_cfg = + &aw_dev->prof_info.prof_desc[aw_dev->cur_prof].sec_desc[AW_DATA_TYPE_DSP_CFG]; + struct aw_sec_data_desc *dsp_fw = + &aw_dev->prof_info.prof_desc[aw_dev->cur_prof].sec_desc[AW_DATA_TYPE_DSP_FW]; + int int_st = 0; + int ret = 0; + + if (aw_dev->status == AW_DEV_PW_OFF) { + dev_info(aw_dev->dev, "already power off"); + return 0; + } + + aw_dev->status = AW_DEV_PW_OFF; + + /*set mute*/ + aw883xx_dev_mute(aw_dev, true); + usleep_range(AW_4000_US, AW_4000_US + 100); + + /*close tx feedback*/ + if (aw_dev->ops.aw_i2s_tx_enable) + aw_dev->ops.aw_i2s_tx_enable(aw_dev, false); + usleep_range(AW_1000_US, AW_1000_US + 100); + + /*check sysint state*/ + int_st = aw_dev_sysint_check(aw_dev); + + /*close dsp*/ + aw883xx_dev_dsp_enable(aw_dev, false); + + /*enable amppd*/ + aw_dev_amppd(aw_dev, true); + + if (int_st < 0) { + /*system status anomaly*/ + aw883xx_dev_memclk_select(aw_dev, AW_DEV_MEMCLK_OSC); + ret = aw_dev_dsp_fw_update(aw_dev, dsp_fw->data, dsp_fw->len); + if (ret < 0) + dev_err(aw_dev->dev, "update dsp fw failed"); + ret = aw883xx_dev_dsp_cfg_update(aw_dev, dsp_cfg->data, dsp_cfg->len); + if (ret < 0) + dev_err(aw_dev->dev, "update dsp cfg failed"); + aw883xx_dev_memclk_select(aw_dev, AW_DEV_MEMCLK_PLL); + } + + /*set power down*/ + aw_dev_pwd(aw_dev, true); + + return 0; +} + +/*deinit aw_device*/ +void aw883xx_dev_deinit(struct aw_device *aw_dev) +{ + if (!aw_dev) + return; + + if (aw_dev->prof_info.prof_desc) { + devm_kfree(aw_dev->dev, aw_dev->prof_info.prof_desc); + aw_dev->prof_info.prof_desc = NULL; + } + aw_dev->prof_info.count = 0; + + if (aw_dev->crc_dsp_cfg.data) { + aw_dev->crc_dsp_cfg.len = 0; + devm_kfree(aw_dev->dev, aw_dev->crc_dsp_cfg.data); + aw_dev->crc_dsp_cfg.data = NULL; + } + +} + +/*init aw_device*/ +int aw883xx_device_init(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + int ret; + + if ((!aw_dev) || (!aw_cfg)) { + pr_err("aw_dev is NULL or aw_cfg is NULL"); + return -ENOMEM; + } + ret = aw883xx_dev_cfg_load(aw_dev, aw_cfg); + if (ret < 0) { + aw883xx_dev_deinit(aw_dev); + dev_err(aw_dev->dev, "aw_dev acf parse failed"); + return -EINVAL; + } + aw_dev->fade_in_time = AW_1000_US / 10; + aw_dev->fade_out_time = AW_1000_US >> 1; + + aw_dev->cur_prof = aw_dev->prof_info.prof_desc[0].id; + aw_dev->set_prof = aw_dev->prof_info.prof_desc[0].id; + ret = aw883xx_dev_fw_update(aw_dev, AW_FORCE_UPDATE_ON, + AW_DSP_FW_UPDATE_ON); + if (ret < 0) { + dev_err(aw_dev->dev, "fw update failed"); + return ret; + } + + /*set mute*/ + aw883xx_dev_mute(aw_dev, true); + usleep_range(AW_4000_US, AW_4000_US + 100); + + /*close tx feedback*/ + if (aw_dev->ops.aw_i2s_tx_enable) + aw_dev->ops.aw_i2s_tx_enable(aw_dev, false); + usleep_range(AW_1000_US, AW_1000_US + 100); + + /*close dsp*/ + aw883xx_dev_dsp_enable(aw_dev, false); + /*enable amppd*/ + aw_dev_amppd(aw_dev, true); + /*set power down*/ + aw_dev_pwd(aw_dev, true); + + return 0; +} + +static void aw883xx_parse_channel_dt(struct aw_device *aw_dev) +{ + int ret; + u32 channel_value; + struct device_node *np = aw_dev->dev->of_node; + + ret = of_property_read_u32(np, "sound-channel", &channel_value); + if (ret < 0) { + dev_dbg(aw_dev->dev, + "read sound-channel failed,use default 0"); + aw_dev->channel = AW_DEV_DEFAULT_CH; + return; + } + + dev_dbg(aw_dev->dev, "read sound-channel value is: %d", + channel_value); + aw_dev->channel = channel_value; +} + +static void aw883xx_parse_fade_enable_dt(struct aw_device *aw_dev) +{ + int ret = -1; + struct device_node *np = aw_dev->dev->of_node; + u32 fade_en; + + ret = of_property_read_u32(np, "fade-enable", &fade_en); + if (ret < 0) { + dev_dbg(aw_dev->dev, + "read fade-enable failed, close fade_in_out"); + fade_en = AW_FADE_IN_OUT_DEFAULT; + } else { + dev_dbg(aw_dev->dev, "read fade-enable value is: %d", fade_en); + } + + aw_dev->fade_en = fade_en; +} + +static void aw_device_parse_dt(struct aw_device *aw_dev) +{ + aw883xx_parse_channel_dt(aw_dev); + aw883xx_parse_fade_enable_dt(aw_dev); +} + +int aw883xx_device_probe(struct aw_device *aw_dev) +{ + aw_device_parse_dt(aw_dev); + + return 0; +} + +int aw883xx_request_firmware_file(struct aw883xx *aw883xx) +{ + const struct firmware *cont = NULL; + int ret = -1; + + aw883xx->aw_pa->fw_status = AW_DEV_FW_FAILED; + + ret = request_firmware(&cont, AW_ACF_FILE, aw883xx->dev); + if ((ret < 0) || (!cont)) { + dev_err(aw883xx->dev, "load [%s] failed!", AW_ACF_FILE); + return ret; + } + + dev_info(aw883xx->dev, "loaded %s - size: %zu", + AW_ACF_FILE, cont ? cont->size : 0); + + aw883xx->aw_cfg = vzalloc(cont->size + sizeof(int)); + if (!aw883xx->aw_cfg) { + release_firmware(cont); + return -ENOMEM; + } + aw883xx->aw_cfg->len = (int)cont->size; + memcpy(aw883xx->aw_cfg->data, cont->data, cont->size); + release_firmware(cont); + + ret = aw883xx_dev_load_acf_check(aw883xx->aw_cfg); + if (ret < 0) { + dev_err(aw883xx->dev, "Load [%s] failed ....!", AW_ACF_FILE); + vfree(aw883xx->aw_cfg); + aw883xx->aw_cfg = NULL; + return ret; + } + + dev_dbg(aw883xx->dev, "%s : bin load success\n", __func__); + + mutex_lock(&aw883xx->lock); + /*aw device init*/ + ret = aw883xx_device_init(aw883xx->aw_pa, aw883xx->aw_cfg); + if (ret < 0) { + dev_err(aw883xx->dev, "dev init failed"); + vfree(aw883xx->aw_cfg); + mutex_unlock(&aw883xx->lock); + return ret; + } + + mutex_unlock(&aw883xx->lock); + + return 0; +} + diff --git a/sound/soc/codecs/aw883xx/aw883xx_device.h b/sound/soc/codecs/aw883xx/aw883xx_device.h new file mode 100644 index 000000000000..505cef1fd3e6 --- /dev/null +++ b/sound/soc/codecs/aw883xx/aw883xx_device.h @@ -0,0 +1,537 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * aw883xx.c -- ALSA Soc AW883XX codec support + * + * Copyright (c) 2022 AWINIC Technology CO., LTD + * + * Author: Bruce zhao + */ + +#ifndef __AW883XX_DEVICE_FILE_H__ +#define __AW883XX_DEVICE_FILE_H__ + +#include "aw883xx_data_type.h" +#include "aw883xx.h" + + +#define AW_DEV_DEFAULT_CH (0) +#define AW_DEV_DSP_CHECK_MAX (5) + +/* + * DSP I2C WRITES + */ +#define AW_DSP_I2C_WRITES +#define AW_MAX_RAM_WRITE_BYTE_SIZE (128) +#define AW_DSP_ODD_NUM_BIT_TEST (0x5555) +#define AW_DSP_EVEN_NUM_BIT_TEST (0xAAAA) +#define AW_DSP_ST_CHECK_MAX (2) +#define AW_FADE_IN_OUT_DEFAULT (0) +#define AW_CALI_DELAY_CACL(value) ((value * 32) / 48) +#define AW_CALI_RE_MAX (15000) +#define AW_CALI_RE_MIN (4000) + +#define AW_GET_MIN_VALUE(value1, value2) \ + ((value1) > (value2) ? (value2) : (value1)) + +#define AW_GET_MAX_VALUE(value1, value2) \ + ((value1) > (value2) ? (value1) : (value2)) + +#define AW_DSP_RE_TO_SHOW_RE(re, shift) (((re) * (1000)) >> (shift)) +#define AW_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000)) + +#define AW_ACF_FILE "aw883xx_acf.bin" +#define AW_DEV_SYSST_CHECK_MAX (10) + +enum { + AW_EXT_DSP_WRITE_NONE = 0, + AW_EXT_DSP_WRITE, +}; + +struct aw_device; + +enum { + AW_DEV_VDSEL_DAC = 0, + AW_DEV_VDSEL_VSENSE = 1, +}; + +enum { + AW_DSP_CRC_NA = 0, + AW_DSP_CRC_OK = 1, +}; + +enum { + AW_DSP_FW_UPDATE_OFF = 0, + AW_DSP_FW_UPDATE_ON = 1, +}; + +enum { + AW_FORCE_UPDATE_OFF = 0, + AW_FORCE_UPDATE_ON = 1, +}; + +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, + AW_100000_US = 100000, +}; + +enum { + AW_DEV_TYPE_OK = 0, + AW_DEV_TYPE_NONE = 1, +}; + + +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_MEMCLK { + AW_DEV_MEMCLK_OSC = 0, + AW_DEV_MEMCLK_PLL = 1, +}; + +enum AW_DEV_DSP_CFG { + AW_DEV_DSP_WORK = 0, + AW_DEV_DSP_BYPASS = 1, +}; + +enum { + AW_DSP_16_DATA = 0, + AW_DSP_32_DATA = 1, +}; + +enum { + AW_NOT_RCV_MODE = 0, + AW_RCV_MODE = 1, +}; + +struct aw_device_ops { + int (*aw_i2c_writes)(struct regmap *map, unsigned int reg_addr, + const void *val, size_t val_len); + int (*aw_i2c_write)(struct regmap *map, unsigned int reg, unsigned int val); + int (*aw_i2c_read)(struct regmap *map, unsigned int reg, unsigned int *val); + + int (*aw_reg_write)(struct regmap *map, unsigned int reg, unsigned int val); + int (*aw_reg_read)(struct regmap *map, unsigned int reg, unsigned int *val); + int (*aw_reg_write_bits)(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val); + + int (*aw_dsp_write)(struct aw_device *aw_dev, + unsigned short dsp_addr, + unsigned int reg_data, + unsigned char data_type); + int (*aw_dsp_read)(struct aw_device *aw_dev, + unsigned short dsp_addr, + unsigned int *dsp_data, + unsigned char data_type); + int (*aw_dsp_write_bits)(struct aw_device *aw_dev, unsigned short dsp_addr, + unsigned short mask, unsigned short dsp_data); + + int (*aw_set_hw_volume)(struct aw_device *aw_dev, unsigned short value); + int (*aw_get_hw_volume)(struct aw_device *aw_dev, unsigned short *value); + unsigned int (*aw_reg_val_to_db)(unsigned int value); + + void (*aw_i2s_tx_enable)(struct aw_device *aw_dev, bool flag); + + bool (*aw_check_wr_access)(int reg); + int (*aw_get_reg_num)(void); + int (*aw_get_version)(char *buf, int size); + int (*aw_read_dsp_pid)(struct aw_device *aw_dev); + void (*aw_set_cfg_f0_fs)(struct aw_device *aw_dev, unsigned int *f0_fs); + int (*aw_dsp_fw_check)(struct aw_device *aw_dev); + int (*aw_set_cali_re)(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*/ + unsigned int intst_mask; /*interrupt check mask*/ + u16 sysint_st; /*interrupt reg status*/ +}; + +struct aw_pwd_desc { + unsigned int reg; + unsigned int mask; + unsigned int enable; + unsigned int disable; +}; + +struct aw_vcalb_desc { + unsigned int icalk_reg; + unsigned int icalk_reg_mask; + 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_sign_mask; + unsigned int vcalk_neg_mask; + unsigned int vcalk_shift; + int vcalk_value_factor; + + unsigned int vcalb_dsp_reg; + unsigned char data_type; + int cabl_base_value; + int vcal_factor; + int vscal_factor; + int iscal_factor; + + unsigned int vcalb_adj_shift; + + unsigned int vcalb_vsense_reg; + int vscal_factor_vsense_in; + int vcalk_value_factor_vsense_in; + unsigned int vcalk_dac_shift; + unsigned int vcalk_dac_mask; + unsigned int vcalk_dac_neg_mask; + unsigned int vcalk_vdsel_mask; +}; + +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 st_check; + unsigned int st_mask; + unsigned int pll_check; + unsigned int dsp_check; + unsigned int dsp_mask; +}; + +struct aw_profctrl_desc { + unsigned int reg; + unsigned int mask; + unsigned int rcv_mode_val; + unsigned int cur_mode; +}; + +struct aw_volume_desc { + unsigned int reg; + unsigned int mask; + unsigned int shift; + unsigned int init_volume; + unsigned int mute_volume; + unsigned int ctl_volume; + unsigned int max_volume; +}; + +struct aw_dsp_en_desc { + unsigned int reg; + unsigned int mask; + unsigned int enable; + unsigned int disable; +}; + +struct aw_memclk_desc { + unsigned int reg; + unsigned int mask; + unsigned int mcu_hclk; + unsigned int osc_clk; +}; + +struct aw_watch_dog_desc { + unsigned int reg; + unsigned int mask; +}; + +struct aw_dsp_mem_desc { + unsigned int dsp_madd_reg; + unsigned int dsp_mdat_reg; + unsigned int dsp_fw_base_addr; + unsigned int dsp_cfg_base_addr; +}; + +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_vmax_desc { + unsigned int dsp_reg; + unsigned char data_type; + unsigned int init_vmax; +}; + +struct aw_soft_rst { + u8 reg; + u16 reg_value; +}; + +struct aw_cali_cfg_desc { + unsigned int actampth_reg; + unsigned char actampth_data_type; + + unsigned int noiseampth_reg; + unsigned char noiseampth_data_type; + + unsigned int ustepn_reg; + unsigned char ustepn_data_type; + + unsigned int alphan_reg; + unsigned int alphan_data_type; +}; + +struct aw_dsp_vol_desc { + unsigned int reg; + unsigned int mute_st; + unsigned int noise_st; + unsigned int mask; +}; + +struct aw_amppd_desc { + unsigned int reg; + unsigned int mask; + unsigned int enable; + unsigned int disable; +}; + +struct aw_f0_desc { + unsigned int dsp_reg; + unsigned char data_type; + unsigned int shift; +}; + +struct aw_cfgf0_fs_desc { + unsigned int dsp_reg; + unsigned char data_type; +}; + +struct aw_q_desc { + unsigned int dsp_reg; + unsigned char data_type; + unsigned int shift; +}; + +struct aw_ra_desc { + unsigned int dsp_reg; + unsigned char data_type; +}; + +struct aw_noise_desc { + unsigned int dsp_reg; + unsigned char data_type; + unsigned int mask; +}; + +struct aw_ste_re_desc { + unsigned int dsp_reg; + unsigned char data_type; + unsigned int shift; +}; + +struct aw_adpz_re_desc { + unsigned int dsp_reg; + unsigned char data_type; + unsigned int shift; +}; + +struct aw_adpz_t0_desc { + unsigned int dsp_reg; + unsigned char data_type; + u16 coilalpha_reg; + unsigned char coil_type; +}; + +struct aw_spkr_temp_desc { + unsigned int reg; +}; + +struct aw_dsp_crc_desc { + unsigned int ctl_reg; + unsigned int ctl_mask; + unsigned int ctl_enable; + unsigned int ctl_disable; + + unsigned int dsp_reg; + unsigned char data_type; +}; + +struct aw_cco_mux_desc { + unsigned int reg; + unsigned int mask; + unsigned int divider; + unsigned int bypass; +}; + +struct aw_hw_temp_desc { + unsigned int dsp_reg; + unsigned char data_type; +}; + +struct aw_cali_delay_desc { + unsigned int dsp_reg; + unsigned char data_type; + unsigned int delay; +}; + +struct aw_chansel_desc { + unsigned int rxchan_reg; + unsigned int rxchan_mask; + unsigned int txchan_reg; + unsigned int txchan_mask; + + unsigned int rx_left; + unsigned int rx_right; + unsigned int tx_left; + unsigned int tx_right; +}; + +struct aw_tx_en_desc { + unsigned int tx_en_mask; + unsigned int tx_disable; +}; + +struct aw_dsp_st { + unsigned int dsp_reg_s1; + unsigned int dsp_reg_e1; + + unsigned int dsp_reg_s2; + unsigned int dsp_reg_e2; +}; + +struct aw_cali_desc { + u32 cali_re; /*cali value*/ + u32 ra; +}; + +struct aw_container { + int len; + u8 data[]; +}; + +struct aw_device { + int status; + struct mutex *dsp_lock; + + unsigned char cur_prof; /*current profile index*/ + unsigned char set_prof; /*set profile index*/ + unsigned char dsp_crc_st; + u16 chip_id; + + unsigned int channel; /*pa channel select*/ + unsigned int fade_step; + + struct i2c_client *i2c; + struct device *dev; + char *acf; + void *private_data; + + u32 fade_en; + unsigned char dsp_cfg; + + u32 dsp_fw_len; + u32 dsp_cfg_len; + u8 platform; + u8 fw_status; /*load cfg status*/ + + unsigned int fade_in_time; + unsigned int fade_out_time; + + struct aw_prof_info prof_info; + struct aw_sec_data_desc crc_dsp_cfg; + struct aw_int_desc int_desc; + struct aw_pwd_desc pwd_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_volume_desc volume_desc; + struct aw_dsp_en_desc dsp_en_desc; + struct aw_memclk_desc memclk_desc; + struct aw_watch_dog_desc watch_dog_desc; + struct aw_dsp_mem_desc dsp_mem_desc; + struct aw_voltage_desc voltage_desc; + struct aw_temperature_desc temp_desc; + struct aw_vmax_desc vmax_desc; + struct aw_ipeak_desc ipeak_desc; + struct aw_soft_rst soft_rst; + struct aw_cali_cfg_desc cali_cfg_desc; + struct aw_ra_desc ra_desc; + struct aw_dsp_vol_desc dsp_vol_desc; + struct aw_noise_desc noise_desc; + struct aw_f0_desc f0_desc; + struct aw_cfgf0_fs_desc cfgf0_fs_desc; + struct aw_q_desc q_desc; + struct aw_ste_re_desc ste_re_desc; + struct aw_adpz_re_desc adpz_re_desc; + struct aw_adpz_t0_desc t0_desc; + struct aw_amppd_desc amppd_desc; + struct aw_spkr_temp_desc spkr_temp_desc; + struct aw_dsp_crc_desc dsp_crc_desc; + struct aw_cco_mux_desc cco_mux_desc; + struct aw_hw_temp_desc hw_temp_desc; + + struct aw_chansel_desc chansel_desc; + struct aw_tx_en_desc tx_en_desc; + struct aw_cali_delay_desc cali_delay_desc; + struct aw_dsp_st dsp_st_desc; + struct aw_cali_desc cali_desc; + + struct aw_device_ops ops; +}; + +void aw883xx_dev_deinit(struct aw_device *aw_dev); +int aw883xx_device_init(struct aw_device *aw_dev, struct aw_container *aw_prof); +int aw883xx_device_start(struct aw_device *aw_dev); +int aw883xx_device_stop(struct aw_device *aw_dev); + +int aw883xx_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en); + +int aw883xx_device_probe(struct aw_device *aw_dev); +int aw883xx_device_remove(struct aw_device *aw_dev); +int aw883xx_dev_syspll_check(struct aw_device *aw_dev); +int aw883xx_dev_get_dsp_status(struct aw_device *aw_dev); + +int aw883xx_dev_get_hmute(struct aw_device *aw_dev); +int aw883xx_dev_sysst_check(struct aw_device *aw_dev); + +int aw883xx_dev_dsp_check(struct aw_device *aw_dev); +void aw883xx_dev_memclk_select(struct aw_device *aw_dev, unsigned char flag); + +void aw883xx_dev_dsp_enable(struct aw_device *aw_dev, bool dsp); + +void aw883xx_dev_mute(struct aw_device *aw_dev, bool mute); +int aw883xx_dev_dsp_fw_update(struct aw_device *aw_dev, + unsigned char *data, unsigned int len); +int aw883xx_dev_dsp_cfg_update(struct aw_device *aw_dev, + unsigned char *data, unsigned int len); +int aw883xx_dev_modify_dsp_cfg(struct aw_device *aw_dev, + unsigned int addr, unsigned int dsp_data, unsigned char data_type); +int aw883xx_dev_get_iis_status(struct aw_device *aw_dev); + +int aw883xx_dev_set_volume(struct aw_device *aw_dev, unsigned short set_vol); +int aw883xx_dev_get_volume(struct aw_device *aw_dev, unsigned short *get_vol); +int aw883xx_request_firmware_file(struct aw883xx *aw883xx); + +#endif + From patchwork Thu Dec 8 12:23:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: wangweidong.a@awinic.com X-Patchwork-Id: 13068362 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 28F32C4332F for ; Thu, 8 Dec 2022 12:26:15 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id B515A18C8; Thu, 8 Dec 2022 13:25:22 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz B515A18C8 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1670502372; bh=S0mZik0OLd/6CXgkM0926qsGbWZv6yIMJwo5uMLAAYA=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=LiOsBEzMP64P2ZM6CgQgzkk2rj0uaJNgH/rDzHz2RqCozAvzeTDBM8ZUnLTH+cF7n 7FA193dU4Vq4DDR2xFr6qy6U/AvNpB6ylIJpOAx7SFQFGlL0opQeY+lAZ3JH4MpUfS o9+6l/TIs1RDbVg/dbcrZPBiejYo6yVTvcycBBz4= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id F0F08F805A1; Thu, 8 Dec 2022 13:23:51 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 7855EF8057F; Thu, 8 Dec 2022 13:23:51 +0100 (CET) Received: from out29-200.mail.aliyun.com (out29-200.mail.aliyun.com [115.124.29.200]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 23A21F8027D for ; Thu, 8 Dec 2022 13:23:34 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 23A21F8027D X-Alimail-AntiSpam: AC=CONTINUE; BC=0.07436261|-1; CH=green; DM=|CONTINUE|false|; DS=CONTINUE|ham_system_inform|0.0128131-0.00164846-0.985538; FP=0|0|0|0|0|-1|-1|-1; HT=ay29a033018047198; MF=wangweidong.a@awinic.com; NM=0; PH=DS; RN=17; RT=17; SR=0; TI=SMTPD_---.QRKcQwi_1670502209; Received: from ubuntu-VirtualBox..(mailfrom:wangweidong.a@awinic.com fp:SMTPD_---.QRKcQwi_1670502209) by smtp.aliyun-inc.com; Thu, 08 Dec 2022 20:23:30 +0800 From: wangweidong.a@awinic.com To: broonie@kernel.org, perex@perex.cz, alsa-devel@alsa-project.org, tiwai@suse.com, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, ckeepax@opensource.cirrus.com, tanureal@opensource.cirrus.com, quic_potturu@quicinc.com, pierre-louis.bossart@linux.intel.com, cy_huang@richtek.com Subject: [PATCH V6 4/5] ASoC: codecs: Configure aw883xx chip register as well as Kconfig and Makefile Date: Thu, 8 Dec 2022 20:23:12 +0800 Message-Id: <20221208122313.55118-5-wangweidong.a@awinic.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221208122313.55118-1-wangweidong.a@awinic.com> References: <20221208122313.55118-1-wangweidong.a@awinic.com> MIME-Version: 1.0 Cc: duanyibo@awinic.com, Weidong Wang , zhaolei@awinic.com, liweilei@awinic.com, yijiangtao@awinic.com, zhangjianming@awinic.com X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" From: Weidong Wang The Awinic AW883XX is an I2S/TDM input, high efficiency digital Smart K audio amplifier with an integrated 10.25V smart boost convert Signed-off-by: Nick Li Signed-off-by: Bruce zhao Signed-off-by: Weidong Wang --- sound/soc/codecs/Kconfig | 10 + sound/soc/codecs/Makefile | 7 + sound/soc/codecs/aw883xx/aw883xx_data_type.h | 146 ++ sound/soc/codecs/aw883xx/aw883xx_init.c | 615 +++++ .../soc/codecs/aw883xx/aw883xx_pid_2049_reg.h | 2300 +++++++++++++++++ 5 files changed, 3078 insertions(+) create mode 100644 sound/soc/codecs/aw883xx/aw883xx_data_type.h create mode 100644 sound/soc/codecs/aw883xx/aw883xx_init.c create mode 100644 sound/soc/codecs/aw883xx/aw883xx_pid_2049_reg.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 7022e6286e6c..f14e0a78acd4 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -54,6 +54,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_ALC5623 imply SND_SOC_ALC5632 imply SND_SOC_AW8738 + imply SND_SOC_AW883XX imply SND_SOC_BT_SCO imply SND_SOC_BD28623 imply SND_SOC_CQ0093VC @@ -2161,4 +2162,13 @@ config SND_SOC_LPASS_TX_MACRO select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)" +config SND_SOC_AW883XX + tristate "Soc Audio for awinic aw883xx series" + depends on I2C + help + this option enables support for aw883xx series Smart PA. + The Awinic AW883XX is an I2S/TDM input, high efficiency + digital Smart K audio amplifier with an integrated 10V + smart boost convert. + endmenu diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 9170ee1447dd..858ca24ad398 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -357,6 +357,11 @@ snd-soc-tas2780-objs := tas2780.o # Mux snd-soc-simple-mux-objs := simple-mux.o +snd_soc_aw883xx-objs := aw883xx/aw883xx.o \ + aw883xx/aw883xx_init.o \ + aw883xx/aw883xx_device.o \ + aw883xx/aw883xx_bin_parse.o \ + obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o @@ -719,3 +724,5 @@ obj-$(CONFIG_SND_SOC_LPASS_TX_MACRO) += snd-soc-lpass-tx-macro.o # Mux obj-$(CONFIG_SND_SOC_SIMPLE_MUX) += snd-soc-simple-mux.o + +obj-$(CONFIG_SND_SOC_AW883XX) +=snd_soc_aw883xx.o diff --git a/sound/soc/codecs/aw883xx/aw883xx_data_type.h b/sound/soc/codecs/aw883xx/aw883xx_data_type.h new file mode 100644 index 000000000000..3aacf5eedde6 --- /dev/null +++ b/sound/soc/codecs/aw883xx/aw883xx_data_type.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * aw883xx.c -- ALSA Soc AW883XX codec support + * + * Copyright (c) 2022 AWINIC Technology CO., LTD + * + * Author: Bruce zhao + */ + +#ifndef __AW883XX_DATA_TYPE_H__ +#define __AW883XX_DATA_TYPE_H__ + +/* + * 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_NONE_TYPE_ID = 0xFFFFFFFF, + 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_DATA_TYPE_REG = 0, + AW_DATA_TYPE_DSP_CFG, + AW_DATA_TYPE_DSP_FW, + AW_DATA_TYPE_MAX, +}; + +enum aw_prof_type { + AW_PROFILE_MUSIC = 0, + AW_PROFILE_VOICE, + AW_PROFILE_VOIP, + AW_PROFILE_RINGTONE, + AW_PROFILE_RINGTONE_HS, + AW_PROFILE_LOWPOWER, + AW_PROFILE_BYPASS, + AW_PROFILE_MMI, + AW_PROFILE_FM, + AW_PROFILE_NOTIFICATION, + AW_PROFILE_RECEIVER, + AW_PROFILE_MAX, +}; + +enum aw_profile_status { + AW_PROFILE_WAIT = 0, + AW_PROFILE_OK, +}; + +struct aw_cfg_hdr { + u32 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*/ + u32 a_author_id; /*author id*/ + u32 a_ddt_size; /*sub section table entry size*/ + u32 a_ddt_num; /*sub section table entry num*/ + u32 a_hdr_offset; /*sub section table offset in file*/ + u32 a_hdr_version; /*sub section table version*/ + u32 reserve[3]; +}; + +struct aw_cfg_dde { + u32 type; /*DDE type id*/ + char dev_name[DEV_NAME_MAX]; + u16 dev_index; /*dev id*/ + u16 dev_bus; /*dev bus id*/ + u16 dev_addr; /*dev addr id*/ + u16 dev_profile; /*dev profile id*/ + u32 data_type; /*data type id*/ + u32 data_size; + u32 data_offset; + u32 data_crc; + u32 reserve[5]; +}; + +struct aw_cfg_dde_v_1_0_0_0 { + u32 type; /*DDE type id*/ + char dev_name[DEV_NAME_MAX]; + u16 dev_index; /*dev id*/ + u16 dev_bus; /*dev bus id*/ + u16 dev_addr; /*dev addr id*/ + u16 dev_profile; /*dev profile id*/ + u32 data_type; /*data type id*/ + u32 data_size; + u32 data_offset; + u32 data_crc; + char dev_profile_str[PROFILE_STR_MAX]; + u32 chip_id; + u32 reserve[4]; +}; + +struct aw_sec_data_desc { + u32 len; + unsigned char *data; +}; + +struct aw_prof_desc { + u32 id; + u32 prof_st; + char *prf_str; + u32 fw_ver; + struct aw_sec_data_desc sec_desc[AW_DATA_TYPE_MAX]; +}; + +struct aw_all_prof_info { + struct aw_prof_desc prof_desc[AW_PROFILE_MAX]; +}; + +struct aw_prof_info { + int count; + int prof_type; + char **prof_name_list; + struct aw_prof_desc *prof_desc; +}; + +#endif + diff --git a/sound/soc/codecs/aw883xx/aw883xx_init.c b/sound/soc/codecs/aw883xx/aw883xx_init.c new file mode 100644 index 000000000000..2ef62fdebb57 --- /dev/null +++ b/sound/soc/codecs/aw883xx/aw883xx_init.c @@ -0,0 +1,615 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * aw883xx.c -- ALSA Soc AW883XX codec support + * + * Copyright (c) 2022 AWINIC Technology CO., LTD + * + * Author: Bruce zhao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw883xx.h" +#include "aw883xx_bin_parse.h" +#include "aw883xx_pid_2049_reg.h" + +static const unsigned char aw_pid_2049_reg_access[AW_PID_2049_REG_MAX] = { + [AW_PID_2049_ID_REG] = (REG_RD_ACCESS), + [AW_PID_2049_SYSST_REG] = (REG_RD_ACCESS), + [AW_PID_2049_SYSINT_REG] = (REG_RD_ACCESS), + [AW_PID_2049_SYSINTM_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_SYSCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_SYSCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_I2SCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_I2SCFG1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_I2SCFG2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_HAGCCFG1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_HAGCCFG2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_HAGCCFG3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_HAGCCFG4_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_HAGCCFG5_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_HAGCCFG6_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_HAGCCFG7_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_MPDCFG_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_PWMCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_I2SCFG3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_DBGCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_HAGCST_REG] = (REG_RD_ACCESS), + [AW_PID_2049_VBAT_REG] = (REG_RD_ACCESS), + [AW_PID_2049_TEMP_REG] = (REG_RD_ACCESS), + [AW_PID_2049_PVDD_REG] = (REG_RD_ACCESS), + [AW_PID_2049_ISNDAT_REG] = (REG_RD_ACCESS), + [AW_PID_2049_VSNDAT_REG] = (REG_RD_ACCESS), + [AW_PID_2049_I2SINT_REG] = (REG_RD_ACCESS), + [AW_PID_2049_I2SCAPCNT_REG] = (REG_RD_ACCESS), + [AW_PID_2049_ANASTA1_REG] = (REG_RD_ACCESS), + [AW_PID_2049_ANASTA2_REG] = (REG_RD_ACCESS), + [AW_PID_2049_ANASTA3_REG] = (REG_RD_ACCESS), + [AW_PID_2049_ANASTA4_REG] = (REG_RD_ACCESS), + [AW_PID_2049_TESTDET_REG] = (REG_RD_ACCESS), + [AW_PID_2049_TESTIN_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_TESTOUT_REG] = (REG_RD_ACCESS), + [AW_PID_2049_DSPMADD_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_DSPMDAT_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_WDT_REG] = (REG_RD_ACCESS), + [AW_PID_2049_ACR1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_ACR2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_ASR1_REG] = (REG_RD_ACCESS), + [AW_PID_2049_ASR2_REG] = (REG_RD_ACCESS), + [AW_PID_2049_DSPCFG_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_ASR3_REG] = (REG_RD_ACCESS), + [AW_PID_2049_ASR4_REG] = (REG_RD_ACCESS), + [AW_PID_2049_VSNCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_ISNCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_PLLCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_PLLCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_PLLCTRL3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_CDACTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_CDACTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_SADCCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_SADCCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_CPCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_BSTCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_BSTCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_BSTCTRL3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_BSTCTRL4_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_BSTCTRL5_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_BSTCTRL6_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_BSTCTRL7_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_DSMCFG1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_DSMCFG2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_DSMCFG3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_DSMCFG4_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_DSMCFG5_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_DSMCFG6_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_DSMCFG7_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_DSMCFG8_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_TESTCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_TESTCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_EFCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_EFCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_EFWH_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_EFWM2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_EFWM1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_EFWL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW_PID_2049_EFRH_REG] = (REG_RD_ACCESS), + [AW_PID_2049_EFRM2_REG] = (REG_RD_ACCESS), + [AW_PID_2049_EFRM1_REG] = (REG_RD_ACCESS), + [AW_PID_2049_EFRL_REG] = (REG_RD_ACCESS), + [AW_PID_2049_TM_REG] = (REG_NONE_ACCESS), +}; + +static int aw883xx_dev_dsp_write(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int dsp_data, unsigned char data_type) +{ + struct aw883xx *aw883xx = (struct aw883xx *)aw_dev->private_data; + + return aw883xx_dsp_write(aw883xx, dsp_addr, dsp_data, data_type); +} + +static int aw883xx_dev_dsp_read(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int *dsp_data, unsigned char data_type) +{ + struct aw883xx *aw883xx = (struct aw883xx *)aw_dev->private_data; + + return aw883xx_dsp_read(aw883xx, dsp_addr, dsp_data, data_type); +} + +/* + * aw883xx i2c write/read + */ +/*[9 : 6]: -6DB ; [5 : 0]: -0.125DB real_value = value * 8 : 0.125db --> 1*/ +static unsigned int aw_pid_2049_reg_val_to_db(unsigned int value) +{ + return (((value >> AW_PID_2049_VOL_6DB_START) * AW_PID_2049_VOLUME_STEP_DB) + + ((value & 0x3f) % AW_PID_2049_VOLUME_STEP_DB)); +} + +/*[9 : 6]: -6DB ; [5 : 0]: -0.125DB reg_value = value / step << 6 + value % step ; step = 6 * 8*/ +static unsigned short aw_pid_2049_db_val_to_reg(unsigned short value) +{ + return (((value / AW_PID_2049_VOLUME_STEP_DB) << AW_PID_2049_VOL_6DB_START) + + (value % AW_PID_2049_VOLUME_STEP_DB)); +} + +static int aw883xx_set_volume(struct aw883xx *aw883xx, unsigned short value) +{ + unsigned int reg_value = 0; + u16 real_value = 0; + u16 volume = 0; + struct aw_volume_desc *vol_desc = &aw883xx->aw_pa->volume_desc; + + volume = AW_GET_MIN_VALUE(value, vol_desc->mute_volume); + real_value = aw_pid_2049_db_val_to_reg(volume); + + /* cal real value */ + regmap_read(aw883xx->regmap, AW_PID_2049_SYSCTRL2_REG, ®_value); + + dev_dbg(aw883xx->dev, "value 0x%x , reg:0x%x", value, real_value); + + /*[15 : 6] volume*/ + real_value = (real_value << AW_PID_2049_VOL_START_BIT) | (reg_value & AW_PID_2049_VOL_MASK); + + /* write value */ + regmap_write(aw883xx->regmap, AW_PID_2049_SYSCTRL2_REG, real_value); + + return 0; +} + +static int aw883xx_get_volume(struct aw883xx *aw883xx, unsigned short *value) +{ + unsigned int reg_value = 0; + u16 real_value = 0; + + /* read value */ + regmap_read(aw883xx->regmap, AW_PID_2049_SYSCTRL2_REG, ®_value); + + /*[15 : 6] volume*/ + real_value = reg_value >> AW_PID_2049_VOL_START_BIT; + + real_value = aw_pid_2049_reg_val_to_db(real_value); + + *value = real_value; + + return 0; +} + +static int aw_pid_2049_set_volume(struct aw_device *aw_dev, unsigned short value) +{ + struct aw883xx *aw883xx = (struct aw883xx *)aw_dev->private_data; + + return aw883xx_set_volume(aw883xx, value); +} + +static int aw_pid_2049_get_volume(struct aw_device *aw_dev, unsigned short *value) +{ + struct aw883xx *aw883xx = (struct aw883xx *)aw_dev->private_data; + + return aw883xx_get_volume(aw883xx, value); +} + +static void aw_pid_2049_i2s_tx_enable(struct aw_device *aw_dev, bool flag) +{ + struct aw883xx *aw883xx = (struct aw883xx *)aw_dev->private_data; + + if (flag) { + regmap_update_bits(aw883xx->regmap, AW_PID_2049_I2SCFG1_REG, + ~AW_PID_2049_I2STXEN_MASK, + AW_PID_2049_I2STXEN_ENABLE_VALUE); + } else { + regmap_update_bits(aw883xx->regmap, AW_PID_2049_I2SCFG1_REG, + ~AW_PID_2049_I2STXEN_MASK, + AW_PID_2049_I2STXEN_DISABLE_VALUE); + } +} + +static void aw_pid_2049_set_cfg_f0_fs(struct aw_device *aw_dev, unsigned int *f0_fs) +{ + unsigned int rate_data = 0; + u32 fs = 0; + struct aw883xx *aw883xx = (struct aw883xx *)aw_dev->private_data; + + regmap_read(aw883xx->regmap, AW_PID_2049_I2SCTRL_REG, &rate_data); + + switch (rate_data & (~AW_PID_2049_I2SSR_MASK)) { + case AW_PID_2049_I2SSR_8_KHZ_VALUE: + fs = 8000; + break; + case AW_PID_2049_I2SSR_16_KHZ_VALUE: + fs = 16000; + break; + case AW_PID_2049_I2SSR_32_KHZ_VALUE: + fs = 32000; + break; + case AW_PID_2049_I2SSR_44_KHZ_VALUE: + fs = 44000; + break; + case AW_PID_2049_I2SSR_48_KHZ_VALUE: + fs = 48000; + break; + case AW_PID_2049_I2SSR_96_KHZ_VALUE: + fs = 96000; + break; + case AW_PID_2049_I2SSR_192KHZ_VALUE: + fs = 192000; + break; + default: + fs = 48000; + dev_err(aw883xx->dev, + "rate can not support, use default 48k"); + break; + } + + dev_dbg(aw883xx->dev, "get i2s fs:%d", fs); + *f0_fs = fs / 8; + + aw883xx_dsp_write(aw883xx, + AW_PID_2049_DSP_REG_CFGF0_FS, *f0_fs, AW_DSP_32_DATA); +} + +static bool aw_pid_2049_check_wr_access(int reg) +{ + if (reg >= AW_PID_2049_REG_MAX) + return false; + + if (aw_pid_2049_reg_access[reg] & REG_WR_ACCESS) + return true; + else + return false; +} + +static int aw_pid_2049_get_reg_num(void) +{ + return AW_PID_2049_REG_MAX; +} + +static int aw_pid_2049_dsp_fw_check(struct aw_device *aw_dev) +{ + struct aw_prof_desc *set_prof_desc = NULL; + struct aw_sec_data_desc *dsp_fw_desc = NULL; + u16 base_addr = AW_PID_2049_DSP_FW_ADDR; + u16 addr = base_addr; + int ret, i; + u32 dsp_val; + u16 bin_val; + + ret = aw883xx_dev_get_prof_data(aw_dev, aw_dev->cur_prof, &set_prof_desc); + if (ret < 0) + return ret; + + /*update reg*/ + dsp_fw_desc = &set_prof_desc->sec_desc[AW_DATA_TYPE_DSP_FW]; + + for (i = 0; i < AW_FW_CHECK_PART; i++) { + ret = aw883xx_dev_dsp_read(aw_dev, addr, &dsp_val, AW_DSP_16_DATA); + if (ret < 0) { + dev_err(aw_dev->dev, "dsp read failed"); + return ret; + } + + bin_val = be16_to_cpup((void *)&dsp_fw_desc->data[2 * (addr - base_addr)]); + + if (dsp_val != bin_val) { + dev_err(aw_dev->dev, "check failed, addr[0x%x], read[0x%x] != bindata[0x%x]", + addr, dsp_val, bin_val); + return -EINVAL; + } + + addr += (dsp_fw_desc->len / 2) / AW_FW_CHECK_PART; + if ((addr - base_addr) > dsp_fw_desc->len) { + dev_err(aw_dev->dev, "check failed, addr[0x%x] too large", addr); + return -EINVAL; + } + } + + return 0; +} + +static int aw_pid_2049_set_cali_re_to_dsp(struct aw_device *aw_dev) +{ + struct aw_adpz_re_desc *adpz_re_desc = &aw_dev->adpz_re_desc; + u32 cali_re = 0; + int ret = 0; + + cali_re = AW_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re + + aw_dev->cali_desc.ra), adpz_re_desc->shift); + + /* set cali re to aw883xx */ + ret = aw_dev->ops.aw_dsp_write(aw_dev, + adpz_re_desc->dsp_reg, cali_re, adpz_re_desc->data_type); + if (ret < 0) { + dev_err(aw_dev->dev, "set cali re error"); + return ret; + } + + ret = aw883xx_dev_modify_dsp_cfg(aw_dev, adpz_re_desc->dsp_reg, + cali_re, adpz_re_desc->data_type); + if (ret < 0) { + dev_err(aw_dev->dev, "modify dsp cfg failed"); + return ret; + } + + return ret; + +} + +static int aw883xx_pid_2049_dev_init(struct aw883xx *aw883xx) +{ + struct aw_device *aw_pa = NULL; + + aw_pa = devm_kzalloc(aw883xx->dev, sizeof(struct aw_device), GFP_KERNEL); + if (!aw_pa) + return -ENOMEM; + + /*call aw device init func*/ + aw_pa->acf = NULL; + aw_pa->prof_info.prof_desc = NULL; + aw_pa->prof_info.count = 0; + aw_pa->prof_info.prof_type = AW_DEV_NONE_TYPE_ID; + aw_pa->channel = 0; + aw_pa->dsp_lock = &aw883xx->dsp_lock; + aw_pa->i2c = aw883xx->i2c; + aw_pa->fw_status = AW_DEV_FW_FAILED; + aw_pa->fade_step = AW_PID_2049_VOLUME_STEP_DB; + + aw_pa->chip_id = aw883xx->chip_id; + aw_pa->private_data = (void *)aw883xx; + aw_pa->dev = aw883xx->dev; + aw_pa->ops.aw_i2c_writes = regmap_raw_write; + aw_pa->ops.aw_i2c_write = regmap_write; + aw_pa->ops.aw_reg_write = regmap_write; + aw_pa->ops.aw_reg_write_bits = regmap_update_bits; + aw_pa->ops.aw_i2c_read = regmap_read; + aw_pa->ops.aw_reg_read = regmap_read; + aw_pa->ops.aw_dsp_read = aw883xx_dev_dsp_read; + aw_pa->ops.aw_dsp_write = aw883xx_dev_dsp_write; + + aw_pa->ops.aw_get_hw_volume = aw_pid_2049_get_volume; + aw_pa->ops.aw_set_hw_volume = aw_pid_2049_set_volume; + aw_pa->ops.aw_reg_val_to_db = aw_pid_2049_reg_val_to_db; + + aw_pa->ops.aw_check_wr_access = aw_pid_2049_check_wr_access; + aw_pa->ops.aw_get_reg_num = aw_pid_2049_get_reg_num; + + aw_pa->ops.aw_i2s_tx_enable = aw_pid_2049_i2s_tx_enable; + + aw_pa->ops.aw_set_cfg_f0_fs = aw_pid_2049_set_cfg_f0_fs; + aw_pa->ops.aw_dsp_fw_check = aw_pid_2049_dsp_fw_check; + + aw_pa->ops.aw_set_cali_re = aw_pid_2049_set_cali_re_to_dsp; + + aw_pa->int_desc.mask_reg = AW_PID_2049_SYSINTM_REG; + aw_pa->int_desc.mask_default = AW_PID_2049_SYSINTM_DEFAULT; + aw_pa->int_desc.int_mask = AW_PID_2049_SYSINTM_DEFAULT; + aw_pa->int_desc.st_reg = AW_PID_2049_SYSINT_REG; + aw_pa->int_desc.intst_mask = AW_PID_2049_BIT_SYSINT_CHECK; + + aw_pa->pwd_desc.reg = AW_PID_2049_SYSCTRL_REG; + aw_pa->pwd_desc.mask = AW_PID_2049_PWDN_MASK; + aw_pa->pwd_desc.enable = AW_PID_2049_PWDN_POWER_DOWN_VALUE; + aw_pa->pwd_desc.disable = AW_PID_2049_PWDN_WORKING_VALUE; + + aw_pa->mute_desc.reg = AW_PID_2049_SYSCTRL_REG; + aw_pa->mute_desc.mask = AW_PID_2049_HMUTE_MASK; + aw_pa->mute_desc.enable = AW_PID_2049_HMUTE_ENABLE_VALUE; + aw_pa->mute_desc.disable = AW_PID_2049_HMUTE_DISABLE_VALUE; + + aw_pa->vcalb_desc.vcalb_dsp_reg = AW_PID_2049_DSP_REG_VCALB; + aw_pa->vcalb_desc.data_type = AW_DSP_16_DATA; + aw_pa->vcalb_desc.vcal_factor = AW_PID_2049_VCAL_FACTOR; + aw_pa->vcalb_desc.cabl_base_value = AW_PID_2049_CABL_BASE_VALUE; + aw_pa->vcalb_desc.vscal_factor = AW_PID_2049_VSCAL_FACTOR; + aw_pa->vcalb_desc.iscal_factor = AW_PID_2049_ISCAL_FACTOR; + + aw_pa->vcalb_desc.vcalb_adj_shift = AW_PID_2049_VCALB_ADJ_FACTOR; + + aw_pa->vcalb_desc.icalk_value_factor = AW_PID_2049_ICABLK_FACTOR; + aw_pa->vcalb_desc.icalk_reg = AW_PID_2049_EFRM2_REG; + aw_pa->vcalb_desc.icalk_reg_mask = AW_PID_2049_EF_ISN_GESLP_MASK; + aw_pa->vcalb_desc.icalk_sign_mask = AW_PID_2049_EF_ISN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.icalk_neg_mask = AW_PID_2049_EF_ISN_GESLP_SIGN_NEG; + + aw_pa->vcalb_desc.vcalk_reg = AW_PID_2049_EFRH_REG; + aw_pa->vcalb_desc.vcalk_reg_mask = AW_PID_2049_EF_VSN_GESLP_MASK; + aw_pa->vcalb_desc.vcalk_sign_mask = AW_PID_2049_EF_VSN_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.vcalk_neg_mask = AW_PID_2049_EF_VSN_GESLP_SIGN_NEG; + aw_pa->vcalb_desc.vcalk_value_factor = AW_PID_2049_VCABLK_FACTOR; + aw_pa->vcalb_desc.vcalk_shift = AW_PID_2049_EF_VSENSE_GAIN_SHIFT; + + aw_pa->vcalb_desc.vcalb_vsense_reg = AW_PID_2049_I2SCFG3_REG; + aw_pa->vcalb_desc.vcalk_vdsel_mask = AW_PID_2049_VDSEL_MASK; + aw_pa->vcalb_desc.vcalk_value_factor_vsense_in = AW_PID_2049_VCABLK_FACTOR_DAC; + aw_pa->vcalb_desc.vscal_factor_vsense_in = AW_PID_2049_VSCAL_FACTOR_DAC; + aw_pa->vcalb_desc.vcalk_dac_shift = AW_PID_2049_EF_DAC_GESLP_SHIFT; + aw_pa->vcalb_desc.vcalk_dac_mask = AW_PID_2049_EF_DAC_GESLP_SIGN_MASK; + aw_pa->vcalb_desc.vcalk_dac_neg_mask = AW_PID_2049_EF_DAC_GESLP_SIGN_NEG; + + aw_pa->sysst_desc.reg = AW_PID_2049_SYSST_REG; + aw_pa->sysst_desc.st_check = AW_PID_2049_BIT_SYSST_CHECK; + aw_pa->sysst_desc.st_mask = AW_PID_2049_BIT_SYSST_CHECK_MASK; + aw_pa->sysst_desc.pll_check = AW_PID_2049_BIT_PLL_CHECK; + aw_pa->sysst_desc.dsp_check = AW_PID_2049_DSPS_NORMAL_VALUE; + aw_pa->sysst_desc.dsp_mask = AW_PID_2049_DSPS_MASK; + + aw_pa->profctrl_desc.reg = AW_PID_2049_SYSCTRL_REG; + aw_pa->profctrl_desc.mask = AW_PID_2049_RCV_MODE_MASK; + aw_pa->profctrl_desc.rcv_mode_val = AW_PID_2049_RCV_MODE_RECEIVER_VALUE; + + aw_pa->volume_desc.reg = AW_PID_2049_SYSCTRL2_REG; + aw_pa->volume_desc.mask = AW_PID_2049_VOL_MASK; + aw_pa->volume_desc.shift = AW_PID_2049_VOL_START_BIT; + aw_pa->volume_desc.mute_volume = AW_PID_2049_MUTE_VOL; + aw_pa->volume_desc.max_volume = AW_PID_2049_VOL_DEFAULT_VALUE; + aw_pa->volume_desc.ctl_volume = AW_PID_2049_VOL_DEFAULT_VALUE; + + aw_pa->dsp_en_desc.reg = AW_PID_2049_SYSCTRL_REG; + aw_pa->dsp_en_desc.mask = AW_PID_2049_DSPBY_MASK; + aw_pa->dsp_en_desc.enable = AW_PID_2049_DSPBY_WORKING_VALUE; + aw_pa->dsp_en_desc.disable = AW_PID_2049_DSPBY_BYPASS_VALUE; + + aw_pa->memclk_desc.reg = AW_PID_2049_DBGCTRL_REG; + aw_pa->memclk_desc.mask = AW_PID_2049_MEM_CLKSEL_MASK; + aw_pa->memclk_desc.mcu_hclk = AW_PID_2049_MEM_CLKSEL_DAP_HCLK_VALUE; + aw_pa->memclk_desc.osc_clk = AW_PID_2049_MEM_CLKSEL_OSC_CLK_VALUE; + + aw_pa->watch_dog_desc.reg = AW_PID_2049_WDT_REG; + aw_pa->watch_dog_desc.mask = AW_PID_2049_WDT_CNT_MASK; + + aw_pa->dsp_mem_desc.dsp_madd_reg = AW_PID_2049_DSPMADD_REG; + aw_pa->dsp_mem_desc.dsp_mdat_reg = AW_PID_2049_DSPMDAT_REG; + aw_pa->dsp_mem_desc.dsp_cfg_base_addr = AW_PID_2049_DSP_CFG_ADDR; + aw_pa->dsp_mem_desc.dsp_fw_base_addr = AW_PID_2049_DSP_FW_ADDR; + + aw_pa->voltage_desc.reg = AW_PID_2049_VBAT_REG; + aw_pa->voltage_desc.vbat_range = AW_PID_2049_VBAT_RANGE; + aw_pa->voltage_desc.int_bit = AW_PID_2049_INT_10BIT; + + aw_pa->temp_desc.reg = AW_PID_2049_TEMP_REG; + aw_pa->temp_desc.sign_mask = AW_PID_2049_TEMP_SIGN_MASK; + aw_pa->temp_desc.neg_mask = AW_PID_2049_TEMP_NEG_MASK; + + aw_pa->vmax_desc.dsp_reg = AW_PID_2049_DSP_REG_VMAX; + aw_pa->vmax_desc.data_type = AW_DSP_16_DATA; + + aw_pa->ipeak_desc.reg = AW_PID_2049_SYSCTRL2_REG; + aw_pa->ipeak_desc.mask = AW_PID_2049_BST_IPEAK_MASK; + + aw_pa->soft_rst.reg = AW_PID_2049_ID_REG; + aw_pa->soft_rst.reg_value = AW_PID_2049_SOFT_RESET_VALUE; + + aw_pa->dsp_vol_desc.reg = AW_PID_2049_DSPCFG_REG; + aw_pa->dsp_vol_desc.mask = AW_PID_2049_DSP_VOL_MASK; + aw_pa->dsp_vol_desc.mute_st = AW_PID_2049_DSP_VOL_MUTE; + aw_pa->dsp_vol_desc.noise_st = AW_PID_2049_DSP_VOL_NOISE_ST; + + aw_pa->amppd_desc.reg = AW_PID_2049_SYSCTRL_REG; + aw_pa->amppd_desc.mask = AW_PID_2049_AMPPD_MASK; + aw_pa->amppd_desc.enable = AW_PID_2049_AMPPD_POWER_DOWN_VALUE; + aw_pa->amppd_desc.disable = AW_PID_2049_AMPPD_WORKING_VALUE; + + aw_pa->spkr_temp_desc.reg = AW_PID_2049_ASR2_REG; + + /*32-bit data types need bypass dsp*/ + aw_pa->ra_desc.dsp_reg = AW_PID_2049_DSP_REG_CFG_ADPZ_RA; + aw_pa->ra_desc.data_type = AW_DSP_32_DATA; + + /*32-bit data types need bypass dsp*/ + aw_pa->cali_cfg_desc.actampth_reg = AW_PID_2049_DSP_REG_CFG_MBMEC_ACTAMPTH; + aw_pa->cali_cfg_desc.actampth_data_type = AW_DSP_32_DATA; + + /*32-bit data types need bypass dsp*/ + aw_pa->cali_cfg_desc.noiseampth_reg = AW_PID_2049_DSP_REG_CFG_MBMEC_NOISEAMPTH; + aw_pa->cali_cfg_desc.noiseampth_data_type = AW_DSP_32_DATA; + + aw_pa->cali_cfg_desc.ustepn_reg = AW_PID_2049_DSP_REG_CFG_ADPZ_USTEPN; + aw_pa->cali_cfg_desc.ustepn_data_type = AW_DSP_16_DATA; + + aw_pa->cali_cfg_desc.alphan_reg = AW_PID_2049_DSP_REG_CFG_RE_ALPHA; + aw_pa->cali_cfg_desc.alphan_data_type = AW_DSP_16_DATA; + + /*32-bit data types need bypass dsp*/ + aw_pa->adpz_re_desc.dsp_reg = AW_PID_2049_DSP_REG_CFG_ADPZ_RE; + aw_pa->adpz_re_desc.data_type = AW_DSP_32_DATA; + aw_pa->adpz_re_desc.shift = AW_PID_2049_DSP_RE_SHIFT; + + aw_pa->t0_desc.dsp_reg = AW_PID_2049_DSP_CFG_ADPZ_T0; + aw_pa->t0_desc.data_type = AW_DSP_16_DATA; + aw_pa->t0_desc.coilalpha_reg = AW_PID_2049_DSP_CFG_ADPZ_COILALPHA; + aw_pa->t0_desc.coil_type = AW_DSP_16_DATA; + + aw_pa->ste_re_desc.shift = AW_PID_2049_DSP_REG_CALRE_SHIFT; + aw_pa->ste_re_desc.dsp_reg = AW_PID_2049_DSP_REG_CALRE; + aw_pa->ste_re_desc.data_type = AW_DSP_16_DATA; + + aw_pa->noise_desc.dsp_reg = AW_PID_2049_DSP_REG_CFG_MBMEC_GLBCFG; + aw_pa->noise_desc.data_type = AW_DSP_16_DATA; + aw_pa->noise_desc.mask = AW_PID_2049_DSP_REG_NOISE_MASK; + + aw_pa->f0_desc.dsp_reg = AW_PID_2049_DSP_REG_RESULT_F0; + aw_pa->f0_desc.shift = AW_PID_2049_DSP_F0_SHIFT; + aw_pa->f0_desc.data_type = AW_DSP_16_DATA; + + /*32-bit data types need bypass dsp*/ + aw_pa->cfgf0_fs_desc.dsp_reg = AW_PID_2049_DSP_REG_CFGF0_FS; + aw_pa->cfgf0_fs_desc.data_type = AW_DSP_32_DATA; + + aw_pa->q_desc.dsp_reg = AW_PID_2049_DSP_REG_RESULT_Q; + aw_pa->q_desc.shift = AW_PID_2049_DSP_Q_SHIFT; + aw_pa->q_desc.data_type = AW_DSP_16_DATA; + + /*32-bit data types need bypass dsp*/ + aw_pa->dsp_crc_desc.dsp_reg = AW_PID_2049_DSP_REG_CRC_ADDR; + aw_pa->dsp_crc_desc.data_type = AW_DSP_32_DATA; + + aw_pa->dsp_crc_desc.ctl_reg = AW_PID_2049_HAGCCFG7_REG; + aw_pa->dsp_crc_desc.ctl_mask = AW_PID_2049_AGC_DSP_CTL_MASK; + aw_pa->dsp_crc_desc.ctl_enable = AW_PID_2049_AGC_DSP_CTL_ENABLE_VALUE; + aw_pa->dsp_crc_desc.ctl_disable = AW_PID_2049_AGC_DSP_CTL_DISABLE_VALUE; + + aw_pa->cco_mux_desc.reg = AW_PID_2049_PLLCTRL1_REG; + aw_pa->cco_mux_desc.mask = AW_PID_2049_CCO_MUX_MASK; + aw_pa->cco_mux_desc.divider = AW_PID_2049_CCO_MUX_DIVIDED_VALUE; + aw_pa->cco_mux_desc.bypass = AW_PID_2049_CCO_MUX_BYPASS_VALUE; + + /*hw monitor temp reg*/ + aw_pa->hw_temp_desc.dsp_reg = AW_PID_2049_DSP_REG_TEMP_ADDR; + aw_pa->hw_temp_desc.data_type = AW_DSP_16_DATA; + + aw_pa->chansel_desc.rxchan_reg = AW_PID_2049_I2SCTRL_REG; + aw_pa->chansel_desc.rxchan_mask = AW_PID_2049_CHSEL_MASK; + aw_pa->chansel_desc.txchan_reg = AW_PID_2049_I2SCFG1_REG; + aw_pa->chansel_desc.txchan_mask = AW_PID_2049_I2SCHS_MASK; + + aw_pa->chansel_desc.rx_left = AW_PID_2049_CHSEL_LEFT_VALUE; + aw_pa->chansel_desc.rx_right = AW_PID_2049_CHSEL_RIGHT_VALUE; + aw_pa->chansel_desc.tx_left = AW_PID_2049_I2SCHS_LEFT_VALUE; + aw_pa->chansel_desc.tx_right = AW_PID_2049_I2SCHS_RIGHT_VALUE; + + aw_pa->tx_en_desc.tx_en_mask = AW_PID_2049_I2STXEN_MASK; + aw_pa->tx_en_desc.tx_disable = AW_PID_2049_I2STXEN_DISABLE_VALUE; + + aw_pa->cali_delay_desc.dsp_reg = AW_PID_2049_DSP_CALI_F0_DELAY; + aw_pa->cali_delay_desc.data_type = AW_DSP_16_DATA; + + aw_pa->dsp_st_desc.dsp_reg_s1 = AW_PID_2049_DSP_ST_S1; + aw_pa->dsp_st_desc.dsp_reg_e1 = AW_PID_2049_DSP_ST_E1; + aw_pa->dsp_st_desc.dsp_reg_s2 = AW_PID_2049_DSP_ST_S2; + aw_pa->dsp_st_desc.dsp_reg_e2 = AW_PID_2049_DSP_ST_E2; + + aw883xx_device_probe(aw_pa); + + aw883xx->aw_pa = aw_pa; + + return 0; +} + +int aw883xx_init(struct aw883xx *aw883xx) +{ + int ret = 0; + + switch (aw883xx->chip_id) { + case AW883XX_PID_2049: + ret = aw883xx_pid_2049_dev_init(aw883xx); + break; + default: + ret = -EINVAL; + dev_err(aw883xx->dev, "unsupported device"); + break; + } + return ret; +} + diff --git a/sound/soc/codecs/aw883xx/aw883xx_pid_2049_reg.h b/sound/soc/codecs/aw883xx/aw883xx_pid_2049_reg.h new file mode 100644 index 000000000000..e7c4703d1627 --- /dev/null +++ b/sound/soc/codecs/aw883xx/aw883xx_pid_2049_reg.h @@ -0,0 +1,2300 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * aw883xx.c -- ALSA Soc AW883XX codec support + * + * Copyright (c) 2022 AWINIC Technology CO., LTD + * + * Author: Bruce zhao + */ + +#ifndef __AW883XX_PID_2049_REG_H__ +#define __AW883XX_PID_2049_REG_H__ + +#define AW_PID_2049_ID_REG (0x00) +#define AW_PID_2049_SYSST_REG (0x01) +#define AW_PID_2049_SYSINT_REG (0x02) +#define AW_PID_2049_SYSINTM_REG (0x03) +#define AW_PID_2049_SYSCTRL_REG (0x04) +#define AW_PID_2049_SYSCTRL2_REG (0x05) +#define AW_PID_2049_I2SCTRL_REG (0x06) +#define AW_PID_2049_I2SCFG1_REG (0x07) +#define AW_PID_2049_I2SCFG2_REG (0x08) +#define AW_PID_2049_HAGCCFG1_REG (0x09) +#define AW_PID_2049_HAGCCFG2_REG (0x0A) +#define AW_PID_2049_HAGCCFG3_REG (0x0B) +#define AW_PID_2049_HAGCCFG4_REG (0x0C) +#define AW_PID_2049_HAGCCFG5_REG (0x0D) +#define AW_PID_2049_HAGCCFG6_REG (0x0E) +#define AW_PID_2049_HAGCCFG7_REG (0x0F) +#define AW_PID_2049_MPDCFG_REG (0x10) +#define AW_PID_2049_PWMCTRL_REG (0x11) +#define AW_PID_2049_I2SCFG3_REG (0x12) +#define AW_PID_2049_DBGCTRL_REG (0x13) +#define AW_PID_2049_HAGCST_REG (0x20) +#define AW_PID_2049_VBAT_REG (0x21) +#define AW_PID_2049_TEMP_REG (0x22) +#define AW_PID_2049_PVDD_REG (0x23) +#define AW_PID_2049_ISNDAT_REG (0x24) +#define AW_PID_2049_VSNDAT_REG (0x25) +#define AW_PID_2049_I2SINT_REG (0x26) +#define AW_PID_2049_I2SCAPCNT_REG (0x27) +#define AW_PID_2049_ANASTA1_REG (0x28) +#define AW_PID_2049_ANASTA2_REG (0x29) +#define AW_PID_2049_ANASTA3_REG (0x2A) +#define AW_PID_2049_ANASTA4_REG (0x2B) +#define AW_PID_2049_TESTDET_REG (0x2C) +#define AW_PID_2049_TESTIN_REG (0x38) +#define AW_PID_2049_TESTOUT_REG (0x39) +#define AW_PID_2049_DSPMADD_REG (0x40) +#define AW_PID_2049_DSPMDAT_REG (0x41) +#define AW_PID_2049_WDT_REG (0x42) +#define AW_PID_2049_ACR1_REG (0x43) +#define AW_PID_2049_ACR2_REG (0x44) +#define AW_PID_2049_ASR1_REG (0x45) +#define AW_PID_2049_ASR2_REG (0x46) +#define AW_PID_2049_DSPCFG_REG (0x47) +#define AW_PID_2049_ASR3_REG (0x48) +#define AW_PID_2049_ASR4_REG (0x49) +#define AW_PID_2049_VSNCTRL1_REG (0x50) +#define AW_PID_2049_ISNCTRL1_REG (0x51) +#define AW_PID_2049_PLLCTRL1_REG (0x52) +#define AW_PID_2049_PLLCTRL2_REG (0x53) +#define AW_PID_2049_PLLCTRL3_REG (0x54) +#define AW_PID_2049_CDACTRL1_REG (0x55) +#define AW_PID_2049_CDACTRL2_REG (0x56) +#define AW_PID_2049_SADCCTRL1_REG (0x57) +#define AW_PID_2049_SADCCTRL2_REG (0x58) +#define AW_PID_2049_CPCTRL1_REG (0x59) +#define AW_PID_2049_BSTCTRL1_REG (0x60) +#define AW_PID_2049_BSTCTRL2_REG (0x61) +#define AW_PID_2049_BSTCTRL3_REG (0x62) +#define AW_PID_2049_BSTCTRL4_REG (0x63) +#define AW_PID_2049_BSTCTRL5_REG (0x64) +#define AW_PID_2049_BSTCTRL6_REG (0x65) +#define AW_PID_2049_BSTCTRL7_REG (0x66) +#define AW_PID_2049_DSMCFG1_REG (0x67) +#define AW_PID_2049_DSMCFG2_REG (0x68) +#define AW_PID_2049_DSMCFG3_REG (0x69) +#define AW_PID_2049_DSMCFG4_REG (0x6A) +#define AW_PID_2049_DSMCFG5_REG (0x6B) +#define AW_PID_2049_DSMCFG6_REG (0x6C) +#define AW_PID_2049_DSMCFG7_REG (0x6D) +#define AW_PID_2049_DSMCFG8_REG (0x6E) +#define AW_PID_2049_TESTCTRL1_REG (0x70) +#define AW_PID_2049_TESTCTRL2_REG (0x71) +#define AW_PID_2049_EFCTRL1_REG (0x72) +#define AW_PID_2049_EFCTRL2_REG (0x73) +#define AW_PID_2049_EFWH_REG (0x74) +#define AW_PID_2049_EFWM2_REG (0x75) +#define AW_PID_2049_EFWM1_REG (0x76) +#define AW_PID_2049_EFWL_REG (0x77) +#define AW_PID_2049_EFRH_REG (0x78) +#define AW_PID_2049_EFRM2_REG (0x79) +#define AW_PID_2049_EFRM1_REG (0x7A) +#define AW_PID_2049_EFRL_REG (0x7B) +#define AW_PID_2049_TM_REG (0x7C) + +/* + * Register Access + */ +enum aw883xx_id { + AW883XX_PID_2049 = 0x2049, +}; + +#define AW_PID_2049_REG_MAX (0x7D) + +#define REG_NONE_ACCESS (0) +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) + +#define AW_PID_2049_VOLUME_STEP_DB (6 * 8) + +/* detail information of registers begin */ +/* ID (0x00) detail */ +/* IDCODE bit 15:0 (ID 0x00) */ +#define AW_PID_2049_IDCODE_START_BIT (0) +#define AW_PID_2049_IDCODE_BITS_LEN (16) +#define AW_PID_2049_IDCODE_MASK \ + (~(((1< X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 9D79BC3A5A7 for ; Thu, 8 Dec 2022 12:25:43 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 8E0B618C0; Thu, 8 Dec 2022 13:24:51 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 8E0B618C0 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1670502341; bh=ZrJLELNQITUBduwngqmQ4WbjatDk4rMERUk2mp53Y0w=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=Vw+VRSTXdQKSDoHuVv8fwB2W/EQaUuqLn8v2wcMY2Zyxcuv7ngAjE3M0uTn6kxLzN 4alDK7HpUc+Bud3G/8pxQJ4rIdY7kadydXlbITDHT1UE9LyaY1m5ibEmz5yIP62knE BLC/0CfG4FSZh+wysS8eKWnQRyZb6nZSO0Mm/+fg= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 7A135F80571; Thu, 8 Dec 2022 13:23:49 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id EBDBFF80564; Thu, 8 Dec 2022 13:23:45 +0100 (CET) Received: from out29-200.mail.aliyun.com (out29-200.mail.aliyun.com [115.124.29.200]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 4F59FF8055A for ; Thu, 8 Dec 2022 13:23:36 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 4F59FF8055A X-Alimail-AntiSpam: AC=CONTINUE; BC=0.2151716|-1; CH=green; DM=|CONTINUE|false|; DS=CONTINUE|ham_system_inform|0.109176-0.000643382-0.89018; FP=0|0|0|0|0|-1|-1|-1; HT=ay29a033018047192; MF=wangweidong.a@awinic.com; NM=1; PH=DS; RN=17; RT=17; SR=0; TI=SMTPD_---.QRKcQyk_1670502211; Received: from ubuntu-VirtualBox..(mailfrom:wangweidong.a@awinic.com fp:SMTPD_---.QRKcQyk_1670502211) by smtp.aliyun-inc.com; Thu, 08 Dec 2022 20:23:32 +0800 From: wangweidong.a@awinic.com To: broonie@kernel.org, perex@perex.cz, alsa-devel@alsa-project.org, tiwai@suse.com, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, ckeepax@opensource.cirrus.com, tanureal@opensource.cirrus.com, quic_potturu@quicinc.com, pierre-louis.bossart@linux.intel.com, cy_huang@richtek.com Subject: [PATCH V6 5/5] ASoC: dt-bindings: Add schema for "awinic,aw883xx" Date: Thu, 8 Dec 2022 20:23:13 +0800 Message-Id: <20221208122313.55118-6-wangweidong.a@awinic.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221208122313.55118-1-wangweidong.a@awinic.com> References: <20221208122313.55118-1-wangweidong.a@awinic.com> MIME-Version: 1.0 Cc: duanyibo@awinic.com, Weidong Wang , zhaolei@awinic.com, liweilei@awinic.com, yijiangtao@awinic.com, zhangjianming@awinic.com X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" From: Weidong Wang Add a DT schema for describing Awinic AW883xx audio amplifiers. They are controlled using I2C. Signed-off-by: Weidong Wang --- .../bindings/sound/awinic,aw883xx.yaml | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/awinic,aw883xx.yaml diff --git a/Documentation/devicetree/bindings/sound/awinic,aw883xx.yaml b/Documentation/devicetree/bindings/sound/awinic,aw883xx.yaml new file mode 100644 index 000000000000..b72c9177ebb7 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/awinic,aw883xx.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/awinic,aw883xx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Awinic AW883xx Smart Audio Amplifier + +maintainers: + - Stephan Gerhold + +description: + The Awinic AW883XX is an I2S/TDM input, high efficiency + digital Smart K audio amplifier with an integrated 10.25V + smart boost convert. + +allOf: + - $ref: name-prefix.yaml# + +properties: + compatible: + const: awinic,aw883xx_smartpa + + reg: + maxItems: 1 + + reset-gpios: + maxItems: 1 + + sound-name-prefix: true + +required: + - compatible + - reg + - reset-gpios + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + audio-codec@34 { + compatible = "awinic,aw883xx_smartpa"; + reg = <0x34>; + reset-gpios = <&gpio 9 GPIO_ACTIVE_LOW>; + }; + };