From nobody Tue Sep 16 04:37:52 2025 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 40056C53210 for ; Fri, 6 Jan 2023 03:29:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231667AbjAFD3B (ORCPT ); Thu, 5 Jan 2023 22:29:01 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39872 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229881AbjAFD25 (ORCPT ); Thu, 5 Jan 2023 22:28:57 -0500 Received: from out29-130.mail.aliyun.com (out29-130.mail.aliyun.com [115.124.29.130]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0E8BA676FA; Thu, 5 Jan 2023 19:28:54 -0800 (PST) X-Alimail-AntiSpam: AC=CONTINUE;BC=0.06712911|-1;CH=green;DM=|CONTINUE|false|;DS=CONTINUE|ham_system_inform|0.0154738-0.00144593-0.98308;FP=0|0|0|0|0|-1|-1|-1;HT=ay29a033018047203;MF=wangweidong.a@awinic.com;NM=1;PH=DS;RN=26;RT=26;SR=0;TI=SMTPD_---.Qlbrp6d_1672975727; Received: from ubuntu-VirtualBox..(mailfrom:wangweidong.a@awinic.com fp:SMTPD_---.Qlbrp6d_1672975727) by smtp.aliyun-inc.com; Fri, 06 Jan 2023 11:28:49 +0800 From: wangweidong.a@awinic.com To: lgirdwood@gmail.com, broonie@kernel.org, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, perex@perex.cz, tiwai@suse.com, ckeepax@opensource.cirrus.com, rf@opensource.cirrus.com, pierre-louis.bossart@linux.intel.com, james.schulman@cirrus.com, flatmax@flatmax.com, cezary.rojewski@intel.com, wangweidong.a@awinic.com, povik+lin@cutebit.org, yangxiaohua@everest-semi.com, daniel.beer@igorinstitute.com, 13691752556@139.com, srinivas.kandagatla@linaro.org, jonathan.albrieux@gmail.com, steve@sk2.org, alsa-devel@alsa-project.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Cc: liweilei@awinic.com, zhaolei@awinic.com, yijiangtao@awinic.com Subject: [PATCH V9 1/5] ASoC: codecs: Add i2c and codec registration for aw883xx and their associated operation functions Date: Fri, 6 Jan 2023 11:28:31 +0800 Message-Id: <20230106032835.141918-2-wangweidong.a@awinic.com> X-Mailer: git-send-email 2.39.0 In-Reply-To: <20230106032835.141918-1-wangweidong.a@awinic.com> References: <20230106032835.141918-1-wangweidong.a@awinic.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" 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 | 579 +++++++++++++++++++++++++++++ sound/soc/codecs/aw883xx/aw883xx.h | 58 +++ 2 files changed, 637 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..b75efb3be1e2 --- /dev/null +++ b/sound/soc/codecs/aw883xx/aw883xx.c @@ -0,0 +1,579 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw883xx.c -- ALSA SoC AW883XX codec support +// +// Copyright (c) 2022 AWINIC Technology CO., LTD +// +// Author: Bruce zhao +// Author: Weidong Wang +// + +#include +#include +#include +#include +#include +#include "aw883xx.h" +#include "aw883xx_device.h" +#include "aw883xx_pid_2049_reg.h" + +static const struct regmap_config aw883xx_remap_config =3D { + .val_bits =3D 16, + .reg_bits =3D 8, + .max_register =3D AW_PID_2049_REG_MAX - 1, + .reg_format_endian =3D REGMAP_ENDIAN_LITTLE, + .val_format_endian =3D REGMAP_ENDIAN_BIG, +}; + +static void aw883xx_start_pa(struct aw883xx *aw883xx) +{ + int ret, i; + + for (i =3D 0; i < AW_START_RETRIES; i++) { + ret =3D aw883xx_dev_start(aw883xx->aw_pa); + if (ret) { + dev_err(aw883xx->aw_pa->dev, "aw883xx device start failed. retry =3D %d= ", i); + ret =3D aw883xx_dev_fw_update(aw883xx->aw_pa, AW_DSP_FW_UPDATE_ON, true= ); + if (ret < 0) { + dev_err(aw883xx->aw_pa->dev, "fw update failed"); + continue; + } + } else { + dev_info(aw883xx->aw_pa->dev, "start success\n"); + break; + } + } +} + +static void aw883xx_startup_work(struct work_struct *work) +{ + struct aw883xx *aw883xx =3D + 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; + + if (aw883xx->aw_pa->fw_status !=3D AW_DEV_FW_OK) + return; + + if (aw883xx->aw_pa->status =3D=3D AW_DEV_PW_ON) + return; + + ret =3D aw883xx_dev_fw_update(aw883xx->aw_pa, AW_DSP_FW_UPDATE_OFF, true); + if (ret < 0) { + dev_err(aw883xx->aw_pa->dev, "fw update failed."); + return; + } + + if (sync_start =3D=3D AW_SYNC_START) + aw883xx_start_pa(aw883xx); + else + queue_delayed_work(system_wq, + &aw883xx->start_work, + AW_START_WORK_DELAY_MS); +} + +static struct snd_soc_dai_driver aw883xx_dai[] =3D { + { + .name =3D "aw883xx-aif", + .id =3D 1, + .playback =3D { + .stream_name =3D "Speaker_Playback", + .channels_min =3D 1, + .channels_max =3D 2, + .rates =3D AW_RATES, + .formats =3D AW_FORMATS, + }, + .capture =3D { + .stream_name =3D "Speaker_Capture", + .channels_min =3D 1, + .channels_max =3D 2, + .rates =3D AW_RATES, + .formats =3D AW_FORMATS, + }, + }, +}; + +static int aw883xx_get_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component =3D snd_soc_kcontrol_component(kcontr= ol); + struct aw883xx *aw883xx =3D snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev =3D aw883xx->aw_pa; + + ucontrol->value.integer.value[0] =3D aw_dev->fade_in_time; + + return 0; +} + +static int aw883xx_set_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component =3D snd_soc_kcontrol_component(kcontr= ol); + struct aw883xx *aw883xx =3D snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc =3D + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev =3D aw883xx->aw_pa; + int time; + + time =3D ucontrol->value.integer.value[0]; + + if (time < mc->min || time > mc->max) + return -EINVAL; + + if (time !=3D aw_dev->fade_in_time) { + aw_dev->fade_in_time =3D 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 =3D snd_soc_kcontrol_component(kcontr= ol); + struct aw883xx *aw883xx =3D snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev =3D aw883xx->aw_pa; + + ucontrol->value.integer.value[0] =3D 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 =3D snd_soc_kcontrol_component(kcontr= ol); + struct aw883xx *aw883xx =3D snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc =3D + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev =3D aw883xx->aw_pa; + int time; + + time =3D ucontrol->value.integer.value[0]; + if (time < mc->min || time > mc->max) + return -EINVAL; + + if (time !=3D aw_dev->fade_out_time) { + aw_dev->fade_out_time =3D time; + return 1; + } + + return 0; +} + +static int aw883xx_profile_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec =3D snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx =3D snd_soc_component_get_drvdata(codec); + const char *prof_name; + char *name; + int count; + + uinfo->type =3D SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count =3D 1; + + count =3D aw883xx_dev_get_profile_count(aw883xx->aw_pa); + if (count <=3D 0) { + uinfo->value.enumerated.items =3D 0; + return 0; + } + + uinfo->value.enumerated.items =3D count; + + if (uinfo->value.enumerated.item >=3D count) + uinfo->value.enumerated.item =3D count - 1; + + name =3D uinfo->value.enumerated.name; + count =3D uinfo->value.enumerated.item; + + prof_name =3D aw883xx_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 =3D snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx =3D snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] =3D aw883xx_dev_get_profile_index(aw883x= x->aw_pa); + + return 0; +} + +static int aw883xx_profile_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec =3D snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx =3D snd_soc_component_get_drvdata(codec); + int ret; + + /* pa stop or stopping just set profile */ + mutex_lock(&aw883xx->lock); + ret =3D aw883xx_dev_set_profile_index(aw883xx->aw_pa, ucontrol->value.int= eger.value[0]); + if (ret < 0) { + dev_dbg(codec->dev, "profile index does not change"); + mutex_unlock(&aw883xx->lock); + return 0; + } + + if (aw883xx->aw_pa->status) { + aw883xx_dev_stop(aw883xx->aw_pa); + 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 =3D snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx =3D snd_soc_component_get_drvdata(codec); + struct aw_volume_desc *vol_desc =3D &aw883xx->aw_pa->volume_desc; + + ucontrol->value.integer.value[0] =3D vol_desc->ctl_volume; + + return 0; +} + +static int aw883xx_volume_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec =3D snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx =3D snd_soc_component_get_drvdata(codec); + struct aw_volume_desc *vol_desc =3D &aw883xx->aw_pa->volume_desc; + struct soc_mixer_control *mc =3D + (struct soc_mixer_control *)kcontrol->private_value; + int value; + + value =3D ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (vol_desc->ctl_volume !=3D value) { + vol_desc->ctl_volume =3D 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 =3D snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx =3D snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] =3D aw883xx->aw_pa->fade_step; + + return 0; +} + +static int aw883xx_set_fade_step(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec =3D snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx =3D snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc =3D + (struct soc_mixer_control *)kcontrol->private_value; + int value; + + value =3D ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (aw883xx->aw_pa->fade_step !=3D value) { + aw883xx->aw_pa->fade_step =3D 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 =3D snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx =3D snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev =3D aw883xx->aw_pa; + + ucontrol->value.integer.value[0] =3D aw_dev->cali_desc.cali_re; + + return 0; +} + +static int aw883xx_re_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec =3D snd_soc_kcontrol_component(kcontrol); + struct aw883xx *aw883xx =3D snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc =3D + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev =3D aw883xx->aw_pa; + int value; + + value =3D ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (aw_dev->cali_desc.cali_re !=3D value) { + aw_dev->cali_desc.cali_re =3D value; + return 1; + } + + return 0; +} + +static const struct snd_kcontrol_new aw883xx_controls[] =3D { + 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_MI= N, + 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 int aw883xx_plack_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component =3D snd_soc_dapm_to_component(w->dapm= ); + struct aw883xx *aw883xx =3D snd_soc_component_get_drvdata(component); + + mutex_lock(&aw883xx->lock); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aw883xx_start(aw883xx, AW_ASYNC_START); + break; + case SND_SOC_DAPM_POST_PMD: + aw883xx_dev_stop(aw883xx->aw_pa); + break; + default: + break; + } + mutex_unlock(&aw883xx->lock); + + return 0; +} + +static const struct snd_soc_dapm_widget aw883xx_dapm_widgets[] =3D { + /* playback */ + SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, 0, 0, 0, + aw883xx_plack_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + 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[] =3D { + {"DAC Output", NULL, "AIF_RX"}, + {"AIF_TX", NULL, "ADC Input"}, +}; + +static int aw883xx_codec_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm =3D snd_soc_component_get_dapm(componen= t); + struct aw883xx *aw883xx =3D snd_soc_component_get_drvdata(component); + int ret; + + INIT_DELAYED_WORK(&aw883xx->start_work, aw883xx_startup_work); + + /* add widgets */ + ret =3D snd_soc_dapm_new_controls(dapm, aw883xx_dapm_widgets, + ARRAY_SIZE(aw883xx_dapm_widgets)); + if (ret < 0) + return ret; + + /* add route */ + ret =3D snd_soc_dapm_add_routes(dapm, aw883xx_audio_map, + ARRAY_SIZE(aw883xx_audio_map)); + if (ret < 0) + return ret; + + ret =3D snd_soc_add_component_controls(component, aw883xx_controls, + ARRAY_SIZE(aw883xx_controls)); + + return ret; +} + +static void aw883xx_codec_remove(struct snd_soc_component *aw_codec) +{ + struct aw883xx *aw883xx =3D snd_soc_component_get_drvdata(aw_codec); + + cancel_delayed_work_sync(&aw883xx->start_work); +} + +static const struct snd_soc_component_driver soc_codec_dev_aw883xx =3D { + .probe =3D aw883xx_codec_probe, + .remove =3D aw883xx_codec_remove, +}; + +static struct aw883xx *aw883xx_malloc_init(struct i2c_client *i2c) +{ + struct aw883xx *aw883xx =3D devm_kzalloc(&i2c->dev, + sizeof(struct aw883xx), GFP_KERNEL); + if (!aw883xx) + return NULL; + + mutex_init(&aw883xx->lock); + + return aw883xx; +} + +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->aw_pa->dev, "%s failed", __func__); + } +} + +static int aw883xx_request_firmware_file(struct aw883xx *aw883xx) +{ + const struct firmware *cont =3D NULL; + int ret; + + aw883xx->aw_pa->fw_status =3D AW_DEV_FW_FAILED; + + ret =3D request_firmware(&cont, AW_ACF_FILE, aw883xx->aw_pa->dev); + if ((ret < 0) || (!cont)) { + dev_err(aw883xx->aw_pa->dev, "load [%s] failed!", AW_ACF_FILE); + return ret; + } + + dev_info(aw883xx->aw_pa->dev, "loaded %s - size: %zu\n", + AW_ACF_FILE, cont ? cont->size : 0); + + aw883xx->aw_cfg =3D devm_kzalloc(aw883xx->aw_pa->dev, cont->size + sizeof= (int), GFP_KERNEL); + if (!aw883xx->aw_cfg) { + release_firmware(cont); + return -ENOMEM; + } + aw883xx->aw_cfg->len =3D (int)cont->size; + memcpy(aw883xx->aw_cfg->data, cont->data, cont->size); + release_firmware(cont); + + ret =3D aw883xx_dev_load_acf_check(aw883xx->aw_pa, aw883xx->aw_cfg); + if (ret < 0) { + dev_err(aw883xx->aw_pa->dev, "Load [%s] failed ....!", AW_ACF_FILE); + return ret; + } + + dev_dbg(aw883xx->aw_pa->dev, "%s : bin load success\n", __func__); + + mutex_lock(&aw883xx->lock); + /* aw device init */ + ret =3D aw883xx_dev_init(aw883xx->aw_pa, aw883xx->aw_cfg); + if (ret < 0) + dev_err(aw883xx->aw_pa->dev, "dev init failed"); + mutex_unlock(&aw883xx->lock); + + return ret; +} + +static int aw883xx_i2c_probe(struct i2c_client *i2c) +{ + struct aw883xx *aw883xx; + int ret; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed"); + return -EIO; + } + + aw883xx =3D aw883xx_malloc_init(i2c); + if (!aw883xx) { + dev_err(&i2c->dev, "malloc aw883xx failed"); + return -ENOMEM; + } + i2c_set_clientdata(i2c, aw883xx); + + aw883xx->reset_gpio =3D devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD= _OUT_LOW); + if (IS_ERR(aw883xx->reset_gpio)) + dev_info(&i2c->dev, "reset gpio not defined\n"); + + /* hardware reset */ + aw883xx_hw_reset(aw883xx); + + aw883xx->regmap =3D devm_regmap_init_i2c(i2c, &aw883xx_remap_config); + if (IS_ERR(aw883xx->regmap)) { + ret =3D PTR_ERR(aw883xx->regmap); + dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + /* aw pa init */ + ret =3D aw883xx_init(&aw883xx->aw_pa, i2c, aw883xx->regmap); + if (ret < 0) + return ret; + + ret =3D aw883xx_request_firmware_file(aw883xx); + if (ret < 0) { + dev_err(&i2c->dev, "%s failed\n", __func__); + return ret; + } + + ret =3D devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_aw883xx, + aw883xx_dai, ARRAY_SIZE(aw883xx_dai)); + if (ret < 0) { + dev_err(&i2c->dev, "failed to register aw883xx: %d", ret); + return ret; + } + + return 0; +} + +static const struct i2c_device_id aw883xx_i2c_id[] =3D { + { AW_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw883xx_i2c_id); + +static struct i2c_driver aw883xx_i2c_driver =3D { + .driver =3D { + .name =3D AW_I2C_NAME, + .owner =3D THIS_MODULE, + }, + .probe_new =3D aw883xx_i2c_probe, + .id_table =3D 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..657b790502cb --- /dev/null +++ b/sound/soc/codecs/aw883xx/aw883xx.h @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw883xx.h -- ALSA SoC AW883XX codec support +// +// Copyright (c) 2022 AWINIC Technology CO., LTD +// +// Author: Bruce zhao +// + +#ifndef __AW883XX_H__ +#define __AW883XX_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 FADE_TIME_MAX 100000 +#define FADE_TIME_MIN 0 + +#define AW_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \ +{ \ + .iface =3D SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name =3D xname, \ + .info =3D profile_info, \ + .get =3D profile_get, \ + .put =3D profile_set, \ +} + +enum { + AW_SYNC_START =3D 0, + AW_ASYNC_START, +}; + +enum { + AW883XX_STREAM_CLOSE =3D 0, + AW883XX_STREAM_OPEN, +}; + +struct aw883xx { + struct aw_device *aw_pa; + struct mutex lock; + struct gpio_desc *reset_gpio; + struct delayed_work start_work; + struct regmap *regmap; + struct aw_container *aw_cfg; +}; + +#endif --=20 2.39.0 From nobody Tue Sep 16 04:37:52 2025 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 502F2C4708E for ; Fri, 6 Jan 2023 03:30:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231841AbjAFD3z (ORCPT ); Thu, 5 Jan 2023 22:29:55 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40454 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231839AbjAFD33 (ORCPT ); Thu, 5 Jan 2023 22:29:29 -0500 Received: from out29-178.mail.aliyun.com (out29-178.mail.aliyun.com [115.124.29.178]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E19CE6A0E3; Thu, 5 Jan 2023 19:29:18 -0800 (PST) X-Alimail-AntiSpam: AC=CONTINUE;BC=0.06712911|-1;CH=green;DM=|CONTINUE|false|;DS=CONTINUE|ham_system_inform|0.00738935-0.000479261-0.992131;FP=0|0|0|0|0|-1|-1|-1;HT=ay29a033018047208;MF=wangweidong.a@awinic.com;NM=1;PH=DS;RN=26;RT=26;SR=0;TI=SMTPD_---.QlbrpCJ_1672975730; Received: from ubuntu-VirtualBox..(mailfrom:wangweidong.a@awinic.com fp:SMTPD_---.QlbrpCJ_1672975730) by smtp.aliyun-inc.com; Fri, 06 Jan 2023 11:28:53 +0800 From: wangweidong.a@awinic.com To: lgirdwood@gmail.com, broonie@kernel.org, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, perex@perex.cz, tiwai@suse.com, ckeepax@opensource.cirrus.com, rf@opensource.cirrus.com, pierre-louis.bossart@linux.intel.com, james.schulman@cirrus.com, flatmax@flatmax.com, cezary.rojewski@intel.com, wangweidong.a@awinic.com, povik+lin@cutebit.org, yangxiaohua@everest-semi.com, daniel.beer@igorinstitute.com, 13691752556@139.com, srinivas.kandagatla@linaro.org, jonathan.albrieux@gmail.com, steve@sk2.org, alsa-devel@alsa-project.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Cc: liweilei@awinic.com, zhaolei@awinic.com, yijiangtao@awinic.com Subject: [PATCH V9 2/5] ASoC: codecs: Aw883xx function for ACF file parse and check Date: Fri, 6 Jan 2023 11:28:32 +0800 Message-Id: <20230106032835.141918-3-wangweidong.a@awinic.com> X-Mailer: git-send-email 2.39.0 In-Reply-To: <20230106032835.141918-1-wangweidong.a@awinic.com> References: <20230106032835.141918-1-wangweidong.a@awinic.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" 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 | 1067 ++++++++++++++++++ sound/soc/codecs/aw883xx/aw883xx_bin_parse.h | 92 ++ 2 files changed, 1159 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/codec= s/aw883xx/aw883xx_bin_parse.c new file mode 100644 index 000000000000..6cafa76e92f9 --- /dev/null +++ b/sound/soc/codecs/aw883xx/aw883xx_bin_parse.c @@ -0,0 +1,1067 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw_bin_parse.c -- AW883XX function for ACF file parse and check +// +// Copyright (c) 2022 AWINIC Technology CO., LTD +// +// Author: Bruce zhao +// + +#include +#include +#include "aw883xx_bin_parse.h" +#include "aw883xx_device.h" + +#define AW_CRC8_POLYNOMIAL 0x8C +DECLARE_CRC8_TABLE(aw_crc8_table); + +static char *profile_name[AW_PROFILE_MAX] =3D { + "Music", "Voice", "Voip", "Ringtone", + "Ringtone_hs", "Lowpower", "Bypass", + "Mmi", "Fm", "Notification", "Receiver" +}; + +static int aw_parse_bin_header(struct aw_device *aw_dev, struct aw_bin *bi= n); + +static int aw_check_sum(struct aw_device *aw_dev, struct aw_bin *bin, int = bin_num) +{ + unsigned char *p_check_sum; + unsigned int sum_data =3D 0; + unsigned int check_sum; + unsigned int i, len; + + p_check_sum =3D &(bin->info.data[(bin->header_info[bin_num].valid_data_ad= dr - + bin->header_info[bin_num].header_len)]); + len =3D bin->header_info[bin_num].bin_data_len + bin->header_info[bin_num= ].header_len; + check_sum =3D le32_to_cpup((void *)p_check_sum); + + for (i =3D 4; i < len; i++) + sum_data +=3D *(p_check_sum + i); + + dev_dbg(aw_dev->dev, "%s -- check_sum =3D %p, check_sum =3D 0x%x, sum_dat= a =3D 0x%x", + __func__, p_check_sum, check_sum, sum_data); + if (sum_data !=3D check_sum) { + dev_err(aw_dev->dev, "%s. CheckSum Fail.bin_num=3D%d, CheckSum:0x%x, Sum= Data:0x%x", + __func__, bin_num, check_sum, sum_data); + return -EINVAL; + } + + return 0; +} + +static int aw_check_data_version(struct aw_device *aw_dev, 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) { + dev_err(aw_dev->dev, "aw_bin_parse Unrecognized this bin data version\n"= ); + return -EINVAL; + } + + return 0; +} + +static int aw_check_register_num(struct aw_device *aw_dev, struct aw_bin *= bin, int bin_num) +{ + struct bin_header_info temp_info =3D bin->header_info[bin_num]; + unsigned int check_register_num, parse_register_num; + unsigned char *p_check_sum; + + p_check_sum =3D &(bin->info.data[(temp_info.valid_data_addr)]); + + parse_register_num =3D le32_to_cpup((void *)p_check_sum); + check_register_num =3D (bin->header_info[bin_num].bin_data_len - CHECK_RE= GISTER_NUM_OFFSET) / + (bin->header_info[bin_num].reg_byte_len + + bin->header_info[bin_num].data_byte_len); + dev_dbg(aw_dev->dev, "%s,parse_register_num =3D 0x%x,check_register_num = =3D 0x%x\n", + __func__, parse_register_num, check_register_num); + if (parse_register_num !=3D check_register_num) { + dev_err(aw_dev->dev, "%s parse_register_num =3D 0x%x,check_register_num = =3D 0x%x\n", + __func__, parse_register_num, check_register_num); + return -EINVAL; + } + + bin->header_info[bin_num].reg_num =3D parse_register_num; + bin->header_info[bin_num].valid_data_len =3D temp_info.bin_data_len - VAL= ID_DATA_LEN; + bin->header_info[bin_num].valid_data_addr =3D temp_info.valid_data_addr += VALID_DATA_ADDR; + + return 0; +} + +static int aw_check_dsp_reg_num(struct aw_device *aw_dev, struct aw_bin *b= in, int bin_num) +{ + struct bin_header_info temp_info =3D bin->header_info[bin_num]; + unsigned int check_dsp_reg_num, parse_dsp_reg_num; + unsigned char *p_check_sum; + + p_check_sum =3D &(bin->info.data[(temp_info.valid_data_addr)]); + + parse_dsp_reg_num =3D le32_to_cpup((void *)(p_check_sum + PARSE_DSP_REG_N= UM)); + bin->header_info[bin_num].reg_data_byte_len =3D + le32_to_cpup((void *)(p_check_sum + REG_DATA_BYTP_LEN)); + check_dsp_reg_num =3D (bin->header_info[bin_num].bin_data_len - CHECK_DSP= _REG_NUM) / + bin->header_info[bin_num].reg_data_byte_len; + dev_dbg(aw_dev->dev, "%s bin_num =3D %d, parse_dsp_reg_num =3D 0x%x, chec= k_dsp_reg_num =3D 0x%x", + __func__, bin_num, check_dsp_reg_num, check_dsp_reg_num); + if (parse_dsp_reg_num !=3D check_dsp_reg_num) { + dev_err(aw_dev->dev, "aw_bin_parse check dsp reg num error\n"); + dev_err(aw_dev->dev, "%s parse_dsp_reg_num =3D 0x%x, check_dsp_reg_num = =3D 0x%x", + __func__, check_dsp_reg_num, check_dsp_reg_num); + return -EINVAL; + } + + bin->header_info[bin_num].download_addr =3D le32_to_cpup((void *)p_check_= sum); + bin->header_info[bin_num].reg_num =3D parse_dsp_reg_num; + bin->header_info[bin_num].valid_data_len =3D temp_info.bin_data_len - DSP= _VALID_DATA_LEN; + bin->header_info[bin_num].valid_data_addr =3D temp_info.valid_data_addr + + DSP_VALID_DATA_ADDR; + + return 0; +} + +static int aw_check_soc_app_num(struct aw_device *aw_dev, struct aw_bin *b= in, int bin_num) +{ + struct bin_header_info temp_info =3D bin->header_info[bin_num]; + unsigned int check_soc_app_num, parse_soc_app_num; + unsigned char *p_check_sum; + + p_check_sum =3D &(bin->info.data[(temp_info.valid_data_addr)]); + + bin->header_info[bin_num].app_version =3D le32_to_cpup((void *)p_check_su= m); + parse_soc_app_num =3D le32_to_cpup((void *)(p_check_sum + PARSE_SOC_APP_N= UM)); + check_soc_app_num =3D bin->header_info[bin_num].bin_data_len - CHECK_SOC_= APP_NUM; + dev_dbg(aw_dev->dev, "%s bin_num =3D %d, parse_soc_app_num=3D0x%x, check_= soc_app_num =3D 0x%x\n", + __func__, bin_num, parse_soc_app_num, check_soc_app_num); + if (parse_soc_app_num !=3D check_soc_app_num) { + dev_err(aw_dev->dev, "%s parse_soc_app_num=3D0x%x, check_soc_app_num =3D= 0x%x\n", + __func__, parse_soc_app_num, check_soc_app_num); + return -EINVAL; + } + + bin->header_info[bin_num].reg_num =3D parse_soc_app_num; + bin->header_info[bin_num].download_addr =3D le32_to_cpup((void *)(p_check= _sum + + APP_DOWNLOAD_ADDR)); + bin->header_info[bin_num].valid_data_len =3D temp_info.bin_data_len - APP= _VALID_DATA_LEN; + bin->header_info[bin_num].valid_data_addr =3D temp_info.valid_data_addr + + APP_VALID_DATA_ADDR; + + return 0; +} + +static void aw_get_single_bin_header(struct aw_bin *bin) +{ + memcpy((void *)&bin->header_info[bin->all_bin_parse_num], bin->p_addr, DA= TA_LEN); + + bin->header_info[bin->all_bin_parse_num].header_len =3D HEADER_LEN; + bin->all_bin_parse_num +=3D 1; +} + +static int aw_parse_one_of_multi_bins(struct aw_device *aw_dev, unsigned i= nt bin_num, + int bin_serial_num, struct aw_bin *bin) +{ + struct bin_header_info aw_bin_header_info; + unsigned int bin_start_addr; + unsigned int valid_data_len; + + if (bin->info.len < sizeof(struct bin_header_info)) { + dev_err(aw_dev->dev, "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 =3D bin->header_info[bin->all_bin_parse_num - 1]; + if (!bin_serial_num) { + bin_start_addr =3D le32_to_cpup((void *)(bin->p_addr + START_ADDR_OFFSET= )); + bin->p_addr +=3D (HEADER_LEN + bin_start_addr); + bin->header_info[bin->all_bin_parse_num].valid_data_addr =3D + aw_bin_header_info.valid_data_addr + VALID_DATA_ADDR + 8 * bin_num + + VALID_DATA_ADDR_OFFSET; + } else { + valid_data_len =3D aw_bin_header_info.bin_data_len; + bin->p_addr +=3D (HDADER_LEN + valid_data_len); + bin->header_info[bin->all_bin_parse_num].valid_data_addr =3D + aw_bin_header_info.valid_data_addr + aw_bin_header_info.bin_data_len= + + VALID_DATA_ADDR_OFFSET; + } + + return aw_parse_bin_header(aw_dev, bin); +} + +static int aw_get_multi_bin_header(struct aw_device *aw_dev, struct aw_bin= *bin) +{ + unsigned int bin_num, i; + int ret; + + bin_num =3D le32_to_cpup((void *)(bin->p_addr + VALID_DATA_ADDR_OFFSET)); + if (bin->multi_bin_parse_num =3D=3D 1) + bin->header_info[bin->all_bin_parse_num].valid_data_addr =3D + VALID_DATA_ADDR_OFFSET; + + aw_get_single_bin_header(bin); + + for (i =3D 0; i < bin_num; i++) { + dev_dbg(aw_dev->dev, "aw_bin_parse enter multi bin for is %d\n", i); + ret =3D aw_parse_one_of_multi_bins(aw_dev, bin_num, i, bin); + if (ret < 0) + return ret; + } + + return 0; +} + +static int aw_parse_bin_header(struct aw_device *aw_dev, struct aw_bin *bi= n) +{ + unsigned int bin_data_type; + + if (bin->info.len < sizeof(struct bin_header_info)) { + dev_err(aw_dev->dev, "bin_header_info size[%d] overflow file size[%d]\n", + (int)sizeof(struct bin_header_info), bin->info.len); + return -EINVAL; + } + + bin_data_type =3D le32_to_cpup((void *)(bin->p_addr + BIN_DATA_TYPE_OFFSE= T)); + dev_dbg(aw_dev->dev, "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: + bin->single_bin_parse_num +=3D 1; + dev_dbg(aw_dev->dev, "%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 =3D + VALID_DATA_ADDR_OFFSET; + aw_get_single_bin_header(bin); + return 0; + case DATA_TYPE_MULTI_BINS: + bin->multi_bin_parse_num +=3D 1; + dev_dbg(aw_dev->dev, "%s bin->multi_bin_parse_num is %d\n", __func__, + bin->multi_bin_parse_num); + return aw_get_multi_bin_header(aw_dev, bin); + default: + dev_dbg(aw_dev->dev, "%s There is no corresponding type\n", __func__); + return 0; + } +} + +static int aw_check_bin_header_version(struct aw_device *aw_dev, struct aw= _bin *bin) +{ + unsigned int header_version; + + header_version =3D le32_to_cpup((void *)(bin->p_addr + HEADER_VERSION_OFF= SET)); + dev_dbg(aw_dev->dev, "aw_bin_parse header_version 0x%x\n", header_version= ); + + switch (header_version) { + case HEADER_VERSION_V1: + return aw_parse_bin_header(aw_dev, bin); + default: + dev_err(aw_dev->dev, "aw_bin_parse Unrecognized this bin header version\= n"); + return -EINVAL; + } +} + +static int aw_parsing_bin_file(struct aw_device *aw_dev, struct aw_bin *bi= n) +{ + int ret =3D -EINVAL; + int i; + + if (!bin) { + dev_err(aw_dev->dev, "aw_bin_parse bin is NULL\n"); + return ret; + } + bin->p_addr =3D bin->info.data; + bin->all_bin_parse_num =3D 0; + bin->multi_bin_parse_num =3D 0; + bin->single_bin_parse_num =3D 0; + + ret =3D aw_check_bin_header_version(aw_dev, bin); + if (ret < 0) { + dev_err(aw_dev->dev, "aw_bin_parse check bin header version error\n"); + return ret; + } + + for (i =3D 0; i < bin->all_bin_parse_num; i++) { + ret =3D aw_check_sum(aw_dev, bin, i); + if (ret < 0) { + dev_err(aw_dev->dev, "aw_bin_parse check sum data error\n"); + return ret; + } + ret =3D aw_check_data_version(aw_dev, bin, i); + if (ret < 0) { + dev_err(aw_dev->dev, "aw_bin_parse check data version error\n"); + return ret; + } + if (bin->header_info[i].bin_data_ver =3D=3D DATA_VERSION_V1) { + switch (bin->header_info[i].bin_data_type) { + case DATA_TYPE_REGISTER: + ret =3D aw_check_register_num(aw_dev, bin, i); + break; + case DATA_TYPE_DSP_REG: + ret =3D aw_check_dsp_reg_num(aw_dev, bin, i); + break; + case DATA_TYPE_SOC_APP: + ret =3D aw_check_soc_app_num(aw_dev, bin, i); + break; + default: + bin->header_info[i].valid_data_len =3D + bin->header_info[i].bin_data_len; + ret =3D 0; + break; + } + if (ret < 0) + return ret; + } + } + + return 0; +} + +static int aw_dev_parse_raw_reg(unsigned char *data, unsigned int data_len, + struct aw_prof_desc *prof_desc) +{ + prof_desc->sec_desc[AW_DATA_TYPE_REG].data =3D data; + prof_desc->sec_desc[AW_DATA_TYPE_REG].len =3D data_len; + + prof_desc->prof_st =3D AW_PROFILE_OK; + + return 0; +} + +static int aw_dev_parse_raw_dsp_cfg(unsigned char *data, unsigned int data= _len, + struct aw_prof_desc *prof_desc) +{ + if (data_len & 0x01) + return -EINVAL; + + swab16_array((u16 *)data, data_len >> 1); + + prof_desc->sec_desc[AW_DATA_TYPE_DSP_CFG].data =3D data; + prof_desc->sec_desc[AW_DATA_TYPE_DSP_CFG].len =3D data_len; + + prof_desc->prof_st =3D AW_PROFILE_OK; + + return 0; +} + +static int aw_dev_parse_raw_dsp_fw(unsigned char *data, unsigned int data_= len, + struct aw_prof_desc *prof_desc) +{ + if (data_len & 0x01) + return -EINVAL; + + swab16_array((u16 *)data, data_len >> 1); + + prof_desc->sec_desc[AW_DATA_TYPE_DSP_FW].data =3D data; + prof_desc->sec_desc[AW_DATA_TYPE_DSP_FW].len =3D data_len; + + prof_desc->prof_st =3D 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; + int ret; + int i; + + aw_bin =3D devm_kzalloc(aw_dev->dev, data_len + sizeof(struct aw_bin), GF= P_KERNEL); + if (!aw_bin) + return -ENOMEM; + + aw_bin->info.len =3D data_len; + memcpy(aw_bin->info.data, data, data_len); + + ret =3D aw_parsing_bin_file(aw_dev, aw_bin); + if (ret < 0) { + dev_err(aw_dev->dev, "parse bin failed"); + goto parse_bin_failed; + } + + for (i =3D 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 =3D + aw_bin->header_info[i].valid_data_len; + prof_desc->sec_desc[AW_DATA_TYPE_REG].data =3D + data + aw_bin->header_info[i].valid_data_addr; + break; + case DATA_TYPE_DSP_REG: + if (aw_bin->header_info[i].valid_data_len & 0x01) { + ret =3D -EINVAL; + goto parse_bin_failed; + } + + swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr), + aw_bin->header_info[i].valid_data_len >> 1); + + prof_desc->sec_desc[AW_DATA_TYPE_DSP_CFG].len =3D + aw_bin->header_info[i].valid_data_len; + prof_desc->sec_desc[AW_DATA_TYPE_DSP_CFG].data =3D + data + aw_bin->header_info[i].valid_data_addr; + break; + case DATA_TYPE_DSP_FW: + case DATA_TYPE_SOC_APP: + if (aw_bin->header_info[i].valid_data_len & 0x01) { + ret =3D -EINVAL; + goto parse_bin_failed; + } + + swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr), + aw_bin->header_info[i].valid_data_len >> 1); + + prof_desc->fw_ver =3D aw_bin->header_info[i].app_version; + prof_desc->sec_desc[AW_DATA_TYPE_DSP_FW].len =3D + aw_bin->header_info[i].valid_data_len; + prof_desc->sec_desc[AW_DATA_TYPE_DSP_FW].data =3D + data + aw_bin->header_info[i].valid_data_addr; + break; + default: + dev_dbg(aw_dev->dev, "bin_data_type not found"); + break; + } + } + prof_desc->prof_st =3D AW_PROFILE_OK; + ret =3D 0; + +parse_bin_failed: + devm_kfree(aw_dev->dev, aw_bin); + 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((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((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( + (u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); + case ACF_SEC_TYPE_MULTIPLE_BIN: + 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_err(aw_dev->dev, "%s cfg_dde->data_type =3D %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) +{ + struct aw_cfg_dde *cfg_dde =3D + (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->hdr_offset); + int sec_num =3D 0; + int ret, i; + + for (i =3D 0; i < prof_hdr->ddt_num; i++) { + if ((aw_dev->i2c->adapter->nr =3D=3D cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr =3D=3D cfg_dde[i].dev_addr) && + (cfg_dde[i].type =3D=3D AW_DEV_TYPE_ID) && + (cfg_dde[i].data_type !=3D ACF_SEC_TYPE_MONITOR)) { + if (cfg_dde[i].dev_profile >=3D AW_PROFILE_MAX) { + dev_err(aw_dev->dev, "dev_profile [%d] overflow", + cfg_dde[i].dev_profile); + return -EINVAL; + } + + ret =3D 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 =3D=3D 0) { + dev_dbg(aw_dev->dev, "get dev type num is %d, please use default", sec_n= um); + 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) +{ + struct aw_cfg_dde *cfg_dde =3D + (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->hdr_offset); + int sec_num =3D 0; + int ret, i; + + for (i =3D 0; i < prof_hdr->ddt_num; i++) { + if ((aw_dev->channel =3D=3D cfg_dde[i].dev_index) && + (cfg_dde[i].type =3D=3D AW_DEV_DEFAULT_TYPE_ID) && + (cfg_dde[i].data_type !=3D ACF_SEC_TYPE_MONITOR)) { + if (cfg_dde[i].dev_profile >=3D AW_PROFILE_MAX) { + dev_err(aw_dev->dev, "dev_profile [%d] overflow", + cfg_dde[i].dev_profile); + return -EINVAL; + } + ret =3D 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 =3D=3D 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_valid_prof(struct aw_device *aw_dev, + struct aw_all_prof_info all_prof_info) +{ + struct aw_prof_desc *prof_desc =3D all_prof_info.prof_desc; + struct aw_prof_info *prof_info =3D &aw_dev->prof_info; + struct aw_sec_data_desc *sec_desc; + int num =3D 0; + int i; + + for (i =3D 0; i < AW_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st =3D=3D AW_PROFILE_OK) { + sec_desc =3D prof_desc[i].sec_desc; + if ((sec_desc[AW_DATA_TYPE_REG].data !=3D NULL) && + (sec_desc[AW_DATA_TYPE_REG].len !=3D 0) && + (sec_desc[AW_DATA_TYPE_DSP_CFG].data !=3D NULL) && + (sec_desc[AW_DATA_TYPE_DSP_CFG].len !=3D 0) && + (sec_desc[AW_DATA_TYPE_DSP_FW].data !=3D NULL) && + (sec_desc[AW_DATA_TYPE_DSP_FW].len !=3D 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 =3D devm_kcalloc(aw_dev->dev, + prof_info->count, sizeof(struct aw_prof_desc), + GFP_KERNEL); + if (!prof_info->prof_desc) + return -ENOMEM; + + for (i =3D 0; i < AW_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st =3D=3D AW_PROFILE_OK) { + sec_desc =3D prof_desc[i].sec_desc; + if ((sec_desc[AW_DATA_TYPE_REG].data !=3D NULL) && + (sec_desc[AW_DATA_TYPE_REG].len !=3D 0) && + (sec_desc[AW_DATA_TYPE_DSP_CFG].data !=3D NULL) && + (sec_desc[AW_DATA_TYPE_DSP_CFG].len !=3D 0) && + (sec_desc[AW_DATA_TYPE_DSP_FW].data !=3D NULL) && + (sec_desc[AW_DATA_TYPE_DSP_FW].len !=3D 0)) { + if (num >=3D prof_info->count) { + dev_err(aw_dev->dev, "overflow count[%d]", + prof_info->count); + return -EINVAL; + } + prof_info->prof_desc[num] =3D prof_desc[i]; + prof_info->prof_desc[num].id =3D i; + num++; + } + } + } + + return 0; +} + +static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr) +{ + struct aw_all_prof_info *all_prof_info; + int ret; + + all_prof_info =3D devm_kzalloc(aw_dev->dev, sizeof(struct aw_all_prof_inf= o), GFP_KERNEL); + if (!all_prof_info) + return -ENOMEM; + + ret =3D aw_dev_parse_dev_type(aw_dev, prof_hdr, all_prof_info); + if (ret < 0) { + goto exit; + } else if (ret =3D=3D AW_DEV_TYPE_NONE) { + dev_dbg(aw_dev->dev, "get dev type num is 0, parse default dev"); + ret =3D aw_dev_parse_dev_default_type(aw_dev, prof_hdr, all_prof_info); + if (ret < 0) + goto exit; + } + + ret =3D aw_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); + if (ret < 0) + goto exit; + + aw_dev->prof_info.prof_name_list =3D profile_name; + +exit: + devm_kfree(aw_dev->dev, all_prof_info); + return ret; +} + +static int aw_dev_create_prof_name_list_v1(struct aw_device *aw_dev) +{ + struct aw_prof_info *prof_info =3D &aw_dev->prof_info; + struct aw_prof_desc *prof_desc =3D 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 =3D devm_kzalloc(aw_dev->dev, + prof_info->count * PROFILE_STR_MAX, + GFP_KERNEL); + if (!prof_info->prof_name_list) + return -ENOMEM; + + for (i =3D 0; i < prof_info->count; i++) { + prof_desc[i].id =3D i; + prof_info->prof_name_list[i] =3D prof_desc[i].prf_str; + dev_info(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_contai= ner *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr =3D (struct aw_cfg_hdr *)aw_cfg->data; + struct aw_cfg_dde_v1 *cfg_dde =3D + (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); + int default_num =3D 0; + int dev_num =3D 0; + unsigned int i; + + for (i =3D 0; i < cfg_hdr->ddt_num; i++) { + if (cfg_dde[i].type =3D=3D AW_DEV_TYPE_ID) + dev_num++; + + if (cfg_dde[i].type =3D=3D AW_DEV_DEFAULT_TYPE_ID) + default_num++; + } + + if (dev_num !=3D 0) { + aw_dev->prof_info.prof_type =3D AW_DEV_TYPE_ID; + } else if (default_num !=3D 0) { + aw_dev->prof_info.prof_type =3D 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_v1(struct aw_device *aw_dev, struct aw_c= ontainer *aw_cfg, + unsigned int *scene_num) +{ + struct aw_cfg_hdr *cfg_hdr =3D (struct aw_cfg_hdr *)aw_cfg->data; + struct aw_cfg_dde_v1 *cfg_dde =3D + (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); + unsigned int i; + + for (i =3D 0; i < cfg_hdr->ddt_num; ++i) { + if ((cfg_dde[i].data_type =3D=3D ACF_SEC_TYPE_MULTIPLE_BIN) && + (aw_dev->chip_id =3D=3D cfg_dde[i].chip_id) && + ((aw_dev->i2c->adapter->nr =3D=3D cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr =3D=3D cfg_dde[i].dev_addr))) + (*scene_num)++; + } + + return 0; +} + +static int aw_get_default_scene_count_v1(struct aw_device *aw_dev, + struct aw_container *aw_cfg, + unsigned int *scene_num) +{ + struct aw_cfg_hdr *cfg_hdr =3D (struct aw_cfg_hdr *)aw_cfg->data; + struct aw_cfg_dde_v1 *cfg_dde =3D + (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); + unsigned int i; + + for (i =3D 0; i < cfg_hdr->ddt_num; ++i) { + if ((cfg_dde[i].data_type =3D=3D ACF_SEC_TYPE_MULTIPLE_BIN) && + (aw_dev->chip_id =3D=3D cfg_dde[i].chip_id) && + (aw_dev->channel =3D=3D cfg_dde[i].dev_index)) + (*scene_num)++; + } + + return 0; +} + +static int aw_dev_parse_scene_count_v1(struct aw_device *aw_dev, + struct aw_container *aw_cfg, + unsigned int *count) +{ + int ret; + + ret =3D 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 =3D aw_get_dev_scene_count_v1(aw_dev, aw_cfg, count); + break; + case AW_DEV_DEFAULT_TYPE_ID: + ret =3D aw_get_default_scene_count_v1(aw_dev, aw_cfg, count); + break; + default: + dev_err(aw_dev->dev, "unsupported prof_type[%x]", aw_dev->prof_info.prof= _type); + ret =3D -EINVAL; + break; + } + + return ret; +} + +static int aw_dev_parse_data_by_sec_type_v1(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, + struct aw_cfg_dde_v1 *cfg_dde, + int *cur_scene_id) +{ + struct aw_prof_info *prof_info =3D &aw_dev->prof_info; + int ret; + + switch (cfg_dde->data_type) { + case ACF_SEC_TYPE_MULTIPLE_BIN: + ret =3D aw_dev_prof_parse_multi_bin(aw_dev, (u8 *)prof_hdr + cfg_dde->da= ta_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 =3D cfg_dde->dev_profile_str; + prof_info->prof_desc[*cur_scene_id].id =3D 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_v1(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr) +{ + struct aw_cfg_dde_v1 *cfg_dde =3D + (struct aw_cfg_dde_v1 *)((char *)prof_hdr + prof_hdr->hdr_offset); + int cur_scene_id; + unsigned int i; + int ret; + + for (i =3D 0; i < prof_hdr->ddt_num; i++) { + if ((aw_dev->i2c->adapter->nr =3D=3D cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr =3D=3D cfg_dde[i].dev_addr) && + (aw_dev->chip_id =3D=3D cfg_dde[i].chip_id)) { + ret =3D aw_dev_parse_data_by_sec_type_v1(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 =3D=3D 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_v1(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr) +{ + struct aw_cfg_dde_v1 *cfg_dde =3D + (struct aw_cfg_dde_v1 *)((char *)prof_hdr + prof_hdr->hdr_offset); + int cur_scene_id =3D 0; + unsigned int i; + int ret; + + for (i =3D 0; i < prof_hdr->ddt_num; i++) { + if ((aw_dev->channel =3D=3D cfg_dde[i].dev_index) && + (aw_dev->chip_id =3D=3D cfg_dde[i].chip_id)) { + ret =3D aw_dev_parse_data_by_sec_type_v1(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 =3D=3D 0) { + dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", cur_sce= ne_id); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_parse_by_hdr_v1(struct aw_device *aw_dev, + struct aw_cfg_hdr *cfg_hdr) +{ + int ret; + + switch (aw_dev->prof_info.prof_type) { + case AW_DEV_TYPE_ID: + ret =3D aw_dev_parse_dev_type_v1(aw_dev, cfg_hdr); + break; + case AW_DEV_DEFAULT_TYPE_ID: + ret =3D aw_dev_parse_default_type_v1(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 =3D -EINVAL; + break; + } + + return ret; +} + +static int aw_dev_load_cfg_by_hdr_v1(struct aw_device *aw_dev, + struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr =3D (struct aw_cfg_hdr *)aw_cfg->data; + struct aw_prof_info *prof_info =3D &aw_dev->prof_info; + int ret; + + ret =3D aw_dev_parse_scene_count_v1(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 =3D devm_kcalloc(aw_dev->dev, + prof_info->count, sizeof(struct aw_prof_desc), + GFP_KERNEL); + if (!prof_info->prof_desc) + return -ENOMEM; + + ret =3D aw_dev_parse_by_hdr_v1(aw_dev, cfg_hdr); + if (ret < 0) { + dev_err(aw_dev->dev, "parse hdr failed"); + return ret; + } + + ret =3D aw_dev_create_prof_name_list_v1(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; + int ret; + + cfg_hdr =3D (struct aw_cfg_hdr *)aw_cfg->data; + + switch (cfg_hdr->hdr_version) { + case AW_CFG_HDR_VER: + ret =3D 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->hdr_version); + return ret; + } + break; + case AW_CFG_HDR_VER_V1: + ret =3D aw_dev_load_cfg_by_hdr_v1(aw_dev, aw_cfg); + if (ret < 0) { + dev_err(aw_dev->dev, "hdr_cersion[0x%x] parse failed", + cfg_hdr->hdr_version); + return ret; + } + break; + default: + dev_err(aw_dev->dev, "unsupported hdr_version [0x%x]", cfg_hdr->hdr_vers= ion); + return -EINVAL; + } + aw_dev->fw_status =3D AW_DEV_FW_OK; + + return 0; +} +EXPORT_SYMBOL_GPL(aw883xx_dev_cfg_load); + +static int aw_dev_check_cfg_by_hdr(struct aw_device *aw_dev, struct aw_con= tainer *aw_cfg) +{ + unsigned int end_data_offset; + struct aw_cfg_hdr *cfg_hdr; + struct aw_cfg_dde *cfg_dde; + unsigned int act_data =3D 0; + unsigned int hdr_ddt_len; + unsigned int i; + u8 act_crc8; + + cfg_hdr =3D (struct aw_cfg_hdr *)aw_cfg->data; + /* check file type id is awinic acf file */ + if (cfg_hdr->id !=3D ACF_FILE_ID) { + dev_err(aw_dev->dev, "not acf type file"); + return -EINVAL; + } + + hdr_ddt_len =3D cfg_hdr->hdr_offset + cfg_hdr->ddt_size; + if (hdr_ddt_len > aw_cfg->len) { + dev_err(aw_dev->dev, "hdrlen with ddt_len [%d] overflow file size[%d]", + cfg_hdr->hdr_offset, aw_cfg->len); + return -EINVAL; + } + + /* check data size */ + cfg_dde =3D (struct aw_cfg_dde *)((char *)aw_cfg->data + cfg_hdr->hdr_off= set); + act_data +=3D hdr_ddt_len; + for (i =3D 0; i < cfg_hdr->ddt_num; i++) + act_data +=3D cfg_dde[i].data_size; + + if (act_data !=3D aw_cfg->len) { + dev_err(aw_dev->dev, "act_data[%d] not equal to file size[%d]!", + act_data, aw_cfg->len); + return -EINVAL; + } + + for (i =3D 0; i < cfg_hdr->ddt_num; i++) { + /* data check */ + end_data_offset =3D cfg_dde[i].data_offset + cfg_dde[i].data_size; + if (end_data_offset > aw_cfg->len) { + dev_err(aw_dev->dev, "ddt_num[%d] end_data_offset[%d] overflow size[%d]= ", + i, end_data_offset, aw_cfg->len); + return -EINVAL; + } + + /* crc check */ + act_crc8 =3D crc8(aw_crc8_table, aw_cfg->data + cfg_dde[i].data_offset, + cfg_dde[i].data_size, 0); + if (act_crc8 !=3D cfg_dde[i].data_crc) { + dev_err(aw_dev->dev, "ddt_num[%d] act_crc8:0x%x !=3D 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_v1(struct aw_device *aw_dev, struct aw_= container *aw_cfg) +{ + struct aw_cfg_dde_v1 *cfg_dde; + unsigned int end_data_offset; + struct aw_cfg_hdr *cfg_hdr; + unsigned int act_data =3D 0; + unsigned int hdr_ddt_len; + u8 act_crc8; + int i; + + cfg_hdr =3D (struct aw_cfg_hdr *)aw_cfg->data; + + /* check file type id is awinic acf file */ + if (cfg_hdr->id !=3D ACF_FILE_ID) { + dev_err(aw_dev->dev, "not acf type file"); + return -EINVAL; + } + + hdr_ddt_len =3D cfg_hdr->hdr_offset + cfg_hdr->ddt_size; + if (hdr_ddt_len > aw_cfg->len) { + dev_err(aw_dev->dev, "hdrlen with ddt_len [%d] overflow file size[%d]", + cfg_hdr->hdr_offset, aw_cfg->len); + return -EINVAL; + } + + /* check data size */ + cfg_dde =3D (struct aw_cfg_dde_v1 *)((char *)aw_cfg->data + cfg_hdr->hdr_= offset); + act_data +=3D hdr_ddt_len; + for (i =3D 0; i < cfg_hdr->ddt_num; i++) + act_data +=3D cfg_dde[i].data_size; + + if (act_data !=3D aw_cfg->len) { + dev_err(aw_dev->dev, "act_data[%d] not equal to file size[%d]!", + act_data, aw_cfg->len); + return -EINVAL; + } + + for (i =3D 0; i < cfg_hdr->ddt_num; i++) { + /* data check */ + end_data_offset =3D cfg_dde[i].data_offset + cfg_dde[i].data_size; + if (end_data_offset > aw_cfg->len) { + dev_err(aw_dev->dev, "ddt_num[%d] end_data_offset[%d] overflow size[%d]= ", + i, end_data_offset, aw_cfg->len); + return -EINVAL; + } + + /* crc check */ + act_crc8 =3D crc8(aw_crc8_table, aw_cfg->data + cfg_dde[i].data_offset, + cfg_dde[i].data_size, 0); + if (act_crc8 !=3D cfg_dde[i].data_crc) { + dev_err(aw_dev->dev, "ddt_num[%d] act_crc8:0x%x !=3D 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_device *aw_dev, struct aw_contain= er *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr; + + if (!aw_cfg) { + dev_err(aw_dev->dev, "aw_prof is NULL"); + return -EINVAL; + } + + if (aw_cfg->len < sizeof(struct aw_cfg_hdr)) { + dev_err(aw_dev->dev, "cfg hdr size[%d] overflow file size[%d]", + aw_cfg->len, (int)sizeof(struct aw_cfg_hdr)); + return -EINVAL; + } + + crc8_populate_lsb(aw_crc8_table, AW_CRC8_POLYNOMIAL); + + cfg_hdr =3D (struct aw_cfg_hdr *)aw_cfg->data; + switch (cfg_hdr->hdr_version) { + case AW_CFG_HDR_VER: + return aw_dev_check_cfg_by_hdr(aw_dev, aw_cfg); + case AW_CFG_HDR_VER_V1: + return aw_dev_check_acf_by_hdr_v1(aw_dev, aw_cfg); + default: + dev_err(aw_dev->dev, "unsupported hdr_version [0x%x]", cfg_hdr->hdr_vers= ion); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(aw883xx_dev_load_acf_check); + +MODULE_DESCRIPTION("AW883XX ACF File Parsing Lib"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/aw883xx/aw883xx_bin_parse.h b/sound/soc/codec= s/aw883xx/aw883xx_bin_parse.h new file mode 100644 index 000000000000..cfb3ff403c09 --- /dev/null +++ b/sound/soc/codecs/aw883xx/aw883xx_bin_parse.h @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw_bin_parse.h -- AW883XX function for ACF file parse and check +// +// Copyright (c) 2022 AWINIC Technology CO., LTD +// +// Author: Bruce zhao +// + +#ifndef __AW883XX_BIN_PARSE_H__ +#define __AW883XX_BIN_PARSE_H__ + +#define CHECK_REGISTER_NUM_OFFSET (4) +#define VALID_DATA_LEN (4) +#define VALID_DATA_ADDR (4) +#define PARSE_DSP_REG_NUM (4) +#define REG_DATA_BYTP_LEN (8) +#define CHECK_DSP_REG_NUM (12) +#define DSP_VALID_DATA_LEN (12) +#define DSP_VALID_DATA_ADDR (12) +#define PARSE_SOC_APP_NUM (8) +#define CHECK_SOC_APP_NUM (12) +#define APP_DOWNLOAD_ADDR (4) +#define APP_VALID_DATA_LEN (12) +#define APP_VALID_DATA_ADDR (12) +#define BIN_NUM_MAX (100) +#define HEADER_LEN (60) +#define BIN_DATA_TYPE_OFFSET (8) +#define DATA_LEN (44) +#define VALID_DATA_ADDR_OFFSET (60) +#define START_ADDR_OFFSET (64) + +#define AW_FW_CHECK_PART (10) +#define HDADER_LEN (60) + +#define HEADER_VERSION_OFFSET (4) + +enum bin_header_version_enum { + HEADER_VERSION_V1 =3D 0x01000000, +}; + +enum data_type_enum { + DATA_TYPE_REGISTER =3D 0x00000000, + DATA_TYPE_DSP_REG =3D 0x00000010, + DATA_TYPE_DSP_CFG =3D 0x00000011, + DATA_TYPE_SOC_REG =3D 0x00000020, + DATA_TYPE_SOC_APP =3D 0x00000021, + DATA_TYPE_DSP_FW =3D 0x00000022, + DATA_TYPE_MULTI_BINS =3D 0x00002000, +}; + +enum data_version_enum { + DATA_VERSION_V1 =3D 0x00000001, + DATA_VERSION_MAX, +}; + +struct bin_header_info { + 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; + unsigned int header_len; +}; + +struct bin_container { + unsigned int len; + unsigned char data[]; +}; + +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; +}; + +#endif --=20 2.39.0 From nobody Tue Sep 16 04:37:52 2025 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 40817C53210 for ; Fri, 6 Jan 2023 03:29:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231700AbjAFD3L (ORCPT ); Thu, 5 Jan 2023 22:29:11 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39988 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231710AbjAFD3H (ORCPT ); Thu, 5 Jan 2023 22:29:07 -0500 Received: from out29-152.mail.aliyun.com (out29-152.mail.aliyun.com [115.124.29.152]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D2DA867BC6; Thu, 5 Jan 2023 19:29:02 -0800 (PST) X-Alimail-AntiSpam: AC=CONTINUE;BC=0.06712911|-1;CH=green;DM=|CONTINUE|false|;DS=CONTINUE|ham_social|0.00371199-0.000877699-0.99541;FP=0|0|0|0|0|-1|-1|-1;HT=ay29a033018047209;MF=wangweidong.a@awinic.com;NM=1;PH=DS;RN=26;RT=26;SR=0;TI=SMTPD_---.QlbrpHx_1672975734; Received: from ubuntu-VirtualBox..(mailfrom:wangweidong.a@awinic.com fp:SMTPD_---.QlbrpHx_1672975734) by smtp.aliyun-inc.com; Fri, 06 Jan 2023 11:28:56 +0800 From: wangweidong.a@awinic.com To: lgirdwood@gmail.com, broonie@kernel.org, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, perex@perex.cz, tiwai@suse.com, ckeepax@opensource.cirrus.com, rf@opensource.cirrus.com, pierre-louis.bossart@linux.intel.com, james.schulman@cirrus.com, flatmax@flatmax.com, cezary.rojewski@intel.com, wangweidong.a@awinic.com, povik+lin@cutebit.org, yangxiaohua@everest-semi.com, daniel.beer@igorinstitute.com, 13691752556@139.com, srinivas.kandagatla@linaro.org, jonathan.albrieux@gmail.com, steve@sk2.org, alsa-devel@alsa-project.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Cc: liweilei@awinic.com, zhaolei@awinic.com, yijiangtao@awinic.com Subject: [PATCH V9 3/5] ASoC: codecs: Aw883xx common function for ALSA Audio Driver Date: Fri, 6 Jan 2023 11:28:33 +0800 Message-Id: <20230106032835.141918-4-wangweidong.a@awinic.com> X-Mailer: git-send-email 2.39.0 In-Reply-To: <20230106032835.141918-1-wangweidong.a@awinic.com> References: <20230106032835.141918-1-wangweidong.a@awinic.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" 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: Ben Yi Signed-off-by: Weidong Wang --- sound/soc/codecs/aw883xx/aw883xx_device.c | 1756 +++++++++++++++++++++ sound/soc/codecs/aw883xx/aw883xx_device.h | 196 +++ 2 files changed, 1952 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/a= w883xx/aw883xx_device.c new file mode 100644 index 000000000000..240ca0a84551 --- /dev/null +++ b/sound/soc/codecs/aw883xx/aw883xx_device.c @@ -0,0 +1,1756 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw883xx_device.c -- AW883XX common function for ALSA Audio Driver +// +// Copyright (c) 2022 AWINIC Technology CO., LTD +// +// Author: Bruce zhao +// Author: Ben Yi +// + +#include +#include +#include +#include "aw883xx_device.h" +#include "aw883xx_pid_2049_reg.h" + +static int aw_dev_dsp_write_16bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int dsp_data) +{ + int ret; + + ret =3D regmap_write(aw_dev->regmap, AW_PID_2049_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write addr error, ret=3D%d", __func__, ret); + return ret; + } + + ret =3D regmap_write(aw_dev->regmap, AW_PID_2049_DSPMDAT_REG, (u16)dsp_da= ta); + if (ret) { + dev_err(aw_dev->dev, "%s write data error, ret=3D%d", __func__, ret); + return ret; + } + + return 0; +} + +static int aw_dev_dsp_write_32bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int dsp_data) +{ + u16 temp_data; + int ret; + + ret =3D regmap_write(aw_dev->regmap, AW_PID_2049_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write addr error, ret=3D%d", __func__, ret); + return ret; + } + + temp_data =3D dsp_data & AW_DSP_16_DATA_MASK; + ret =3D regmap_write(aw_dev->regmap, AW_PID_2049_DSPMDAT_REG, (u16)temp_d= ata); + if (ret) { + dev_err(aw_dev->dev, "%s write datal error, ret=3D%d", __func__, ret); + return ret; + } + + temp_data =3D dsp_data >> 16; + ret =3D regmap_write(aw_dev->regmap, AW_PID_2049_DSPMDAT_REG, (u16)temp_d= ata); + if (ret) { + dev_err(aw_dev->dev, "%s write datah error, ret=3D%d", __func__, ret); + return ret; + } + + return 0; +} + +static int aw_dev_dsp_write(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int dsp_data, unsigned char data_type) +{ + u32 reg_value; + int ret; + + mutex_lock(&aw_dev->dsp_lock); + switch (data_type) { + case AW_DSP_16_DATA: + ret =3D aw_dev_dsp_write_16bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "write dsp_addr[0x%x] 16-bit dsp_data[0x%x] failed= ", + (u32)dsp_addr, dsp_data); + break; + case AW_DSP_32_DATA: + ret =3D aw_dev_dsp_write_32bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "write dsp_addr[0x%x] 32-bit dsp_data[0x%x] failed= ", + (u32)dsp_addr, dsp_data); + break; + default: + dev_err(aw_dev->dev, "data type[%d] unsupported", data_type); + ret =3D -EINVAL; + break; + } + + /* clear dsp chip select state*/ + if (regmap_read(aw_dev->regmap, AW_PID_2049_ID_REG, ®_value)) + dev_err(aw_dev->dev, "%s fail to clear chip state. Err=3D%d\n", __func__= , ret); + mutex_unlock(&aw_dev->dsp_lock); + + return ret; +} + +static int aw_dev_dsp_read_16bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int *dsp_data) +{ + unsigned int temp_data; + int ret; + + ret =3D regmap_write(aw_dev->regmap, AW_PID_2049_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write error, ret=3D%d", __func__, ret); + return ret; + } + + ret =3D regmap_read(aw_dev->regmap, AW_PID_2049_DSPMDAT_REG, &temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s read error, ret=3D%d", __func__, ret); + return ret; + } + *dsp_data =3D temp_data; + + return 0; +} + +static int aw_dev_dsp_read_32bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int *dsp_data) +{ + unsigned int temp_data; + int ret; + + ret =3D regmap_write(aw_dev->regmap, AW_PID_2049_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write error, ret=3D%d", __func__, ret); + return ret; + } + + ret =3D regmap_read(aw_dev->regmap, AW_PID_2049_DSPMDAT_REG, &temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s read error, ret=3D%d", __func__, ret); + return ret; + } + *dsp_data =3D temp_data; + + ret =3D regmap_read(aw_dev->regmap, AW_PID_2049_DSPMDAT_REG, &temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s read error, ret=3D%d", __func__, ret); + return ret; + } + *dsp_data |=3D (temp_data << 16); + + return 0; +} + +static int aw_dev_dsp_read(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int *dsp_data, unsigned char data_type) +{ + u32 reg_value; + int ret; + + mutex_lock(&aw_dev->dsp_lock); + switch (data_type) { + case AW_DSP_16_DATA: + ret =3D aw_dev_dsp_read_16bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "read dsp_addr[0x%x] 16-bit dsp_data[0x%x] failed", + (u32)dsp_addr, *dsp_data); + break; + case AW_DSP_32_DATA: + ret =3D aw_dev_dsp_read_32bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "read dsp_addr[0x%x] 32r-bit dsp_data[0x%x] failed= ", + (u32)dsp_addr, *dsp_data); + break; + default: + dev_err(aw_dev->dev, "data type[%d] unsupported", data_type); + ret =3D -EINVAL; + break; + } + + /* clear dsp chip select state*/ + if (regmap_read(aw_dev->regmap, AW_PID_2049_ID_REG, ®_value)) + dev_err(aw_dev->dev, "%s fail to clear chip state. Err=3D%d\n", __func__= , ret); + mutex_unlock(&aw_dev->dsp_lock); + + return ret; +} + + +static int aw_dev_read_chipid(struct aw_device *aw_dev, u16 *chip_id) +{ + int reg_val; + int ret; + + ret =3D regmap_read(aw_dev->regmap, AW_CHIP_ID_REG, ®_val); + if (ret) { + dev_err(aw_dev->dev, "%s read chipid error. ret =3D %d", __func__, ret); + return ret; + } + + dev_info(aw_dev->dev, "chip id =3D %x\n", reg_val); + *chip_id =3D reg_val; + + return 0; +} + +static unsigned int reg_val_to_db(unsigned int value) +{ + return (((value >> AW_PID_2049_VOL_6DB_START) * AW_PID_2049_VOLUME_STEP_D= B) + + ((value & 0x3f) % AW_PID_2049_VOLUME_STEP_DB)); +} + +static unsigned short db_to_reg_val(unsigned short value) +{ + return (((value / AW_PID_2049_VOLUME_STEP_DB) << AW_PID_2049_VOL_6DB_STAR= T) + + (value % AW_PID_2049_VOLUME_STEP_DB)); +} + +static int aw_dev_dsp_fw_check(struct aw_device *aw_dev) +{ + struct aw_sec_data_desc *dsp_fw_desc; + struct aw_prof_desc *set_prof_desc; + u16 base_addr =3D AW_PID_2049_DSP_FW_ADDR; + u16 addr =3D base_addr; + u32 dsp_val; + u16 bin_val; + int ret, i; + + ret =3D aw883xx_dev_get_prof_data(aw_dev, aw_dev->prof_cur, &set_prof_des= c); + if (ret) + return ret; + + /* update reg */ + dsp_fw_desc =3D &set_prof_desc->sec_desc[AW_DATA_TYPE_DSP_FW]; + + for (i =3D 0; i < AW_FW_CHECK_PART; i++) { + ret =3D aw_dev_dsp_read(aw_dev, addr, &dsp_val, AW_DSP_16_DATA); + if (ret) { + dev_err(aw_dev->dev, "dsp read failed"); + return ret; + } + + bin_val =3D be16_to_cpup((void *)&dsp_fw_desc->data[2 * (addr - base_add= r)]); + + if (dsp_val !=3D bin_val) { + dev_err(aw_dev->dev, "fw check failed, addr[0x%x], read[0x%x] !=3D bind= ata[0x%x]", + addr, dsp_val, bin_val); + return -EINVAL; + } + + addr +=3D (dsp_fw_desc->len / 2) / AW_FW_CHECK_PART; + if ((addr - base_addr) > dsp_fw_desc->len) { + dev_err(aw_dev->dev, "fw check failed, addr[0x%x] too large", addr); + return -EINVAL; + } + } + + return 0; +} + +static int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw_volume_desc *vol_desc =3D &aw_dev->volume_desc; + unsigned int reg_value; + u16 real_value, volume; + int ret; + + volume =3D min((value + vol_desc->init_volume), (unsigned int)AW_PID_2049= _MUTE_VOL); + real_value =3D db_to_reg_val(volume); + + /* cal real value */ + ret =3D regmap_read(aw_dev->regmap, AW_PID_2049_SYSCTRL2_REG, ®_value); + if (ret) + return ret; + + dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value); + + /* [15 : 6] volume */ + real_value =3D (real_value << AW_PID_2049_VOL_START_BIT) | (reg_value & A= W_PID_2049_VOL_MASK); + + /* write value */ + ret =3D regmap_write(aw_dev->regmap, AW_PID_2049_SYSCTRL2_REG, real_value= ); + + return ret; +} + +void aw883xx_dev_set_volume(struct aw_device *aw_dev, unsigned short set_v= ol) +{ + int ret; + + ret =3D aw_dev_set_volume(aw_dev, set_vol); + if (ret) + dev_dbg(aw_dev->dev, "set volume failed"); +} +EXPORT_SYMBOL_GPL(aw883xx_dev_set_volume); + +static void aw_dev_fade_in(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc =3D &aw_dev->volume_desc; + u16 fade_in_vol =3D desc->ctl_volume; + int fade_step =3D aw_dev->fade_step; + int i; + + if (!aw_dev->fade_en) + return; + + if (fade_step =3D=3D 0 || aw_dev->fade_in_time =3D=3D 0) { + aw_dev_set_volume(aw_dev, fade_in_vol); + return; + } + + for (i =3D AW_PID_2049_MUTE_VOL; i >=3D fade_in_vol; i -=3D fade_step) { + aw_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10); + } + + if (i !=3D fade_in_vol) + aw_dev_set_volume(aw_dev, fade_in_vol); +} + +static void aw_dev_fade_out(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc =3D &aw_dev->volume_desc; + int fade_step =3D aw_dev->fade_step; + int i; + + if (!aw_dev->fade_en) + return; + + if (fade_step =3D=3D 0 || aw_dev->fade_out_time =3D=3D 0) { + aw_dev_set_volume(aw_dev, AW_PID_2049_MUTE_VOL); + return; + } + + for (i =3D desc->ctl_volume; i <=3D AW_PID_2049_MUTE_VOL; i +=3D fade_ste= p) { + aw_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } + + if (i !=3D AW_PID_2049_MUTE_VOL) { + aw_dev_set_volume(aw_dev, AW_PID_2049_MUTE_VOL); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } +} + +static int aw_dev_modify_dsp_cfg(struct aw_device *aw_dev, + unsigned int addr, unsigned int dsp_data, unsigned char data_type) +{ + struct aw_sec_data_desc *crc_dsp_cfg =3D &aw_dev->crc_dsp_cfg; + unsigned int addr_offset; + unsigned short data1; + unsigned int data2; + + dev_dbg(aw_dev->dev, "addr:0x%x, dsp_data:0x%x", addr, dsp_data); + + addr_offset =3D (addr - AW_PID_2049_DSP_CFG_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; + } + switch (data_type) { + case AW_DSP_16_DATA: + data1 =3D cpu_to_le16((u16)dsp_data); + memcpy(crc_dsp_cfg->data + addr_offset, (u8 *)&data1, 2); + break; + case AW_DSP_32_DATA: + data2 =3D cpu_to_le32(dsp_data); + memcpy(crc_dsp_cfg->data + addr_offset, (u8 *)&data2, 4); + break; + default: + dev_err(aw_dev->dev, "data type[%d] unsupported", data_type); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_dsp_set_cali_re(struct aw_device *aw_dev) +{ + u32 cali_re; + int ret; + + cali_re =3D AW_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re + + aw_dev->cali_desc.ra), AW_PID_2049_DSP_RE_SHIFT); + + /* set cali re to device */ + ret =3D aw_dev_dsp_write(aw_dev, + AW_PID_2049_DSP_REG_CFG_ADPZ_RE, cali_re, AW_DSP_32_DATA); + if (ret) { + dev_err(aw_dev->dev, "set cali re error"); + return ret; + } + + ret =3D aw_dev_modify_dsp_cfg(aw_dev, AW_PID_2049_DSP_REG_CFG_ADPZ_RE, + cali_re, AW_DSP_32_DATA); + if (ret) + dev_err(aw_dev->dev, "modify dsp cfg failed"); + + return ret; +} + +static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag) +{ + int ret; + + if (flag) { + ret =3D regmap_update_bits(aw_dev->regmap, AW_PID_2049_I2SCFG1_REG, + ~AW_PID_2049_I2STXEN_MASK, AW_PID_2049_I2STXEN_ENABLE_VALUE); + } else { + ret =3D regmap_update_bits(aw_dev->regmap, AW_PID_2049_I2SCFG1_REG, + ~AW_PID_2049_I2STXEN_MASK, AW_PID_2049_I2STXEN_DISABLE_VALUE); + } + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +static int aw_dev_dsp_set_crc32(struct aw_device *aw_dev) +{ + struct aw_sec_data_desc *crc_dsp_cfg =3D &aw_dev->crc_dsp_cfg; + u32 crc_value, crc_data_len; + + /* get crc data len */ + crc_data_len =3D (AW_PID_2049_DSP_REG_CRC_ADDR - AW_PID_2049_DSP_CFG_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 & 0x11) { + dev_err(aw_dev->dev, "The crc data len :%d unsupport", crc_data_len); + return -EINVAL; + } + + crc_value =3D __crc32c_le(0xFFFFFFFF, crc_dsp_cfg->data, crc_data_len) ^ = 0xFFFFFFFF; + + return aw_dev_dsp_write(aw_dev, AW_PID_2049_DSP_REG_CRC_ADDR, crc_value, + AW_DSP_32_DATA); +} + +static void aw_dev_dsp_check_crc_enable(struct aw_device *aw_dev, bool fla= g) +{ + int ret; + + if (flag) { + ret =3D regmap_update_bits(aw_dev->regmap, AW_PID_2049_HAGCCFG7_REG, + ~AW_PID_2049_AGC_DSP_CTL_MASK, AW_PID_2049_AGC_DSP_CTL_ENABLE_VALUE); + } else { + ret =3D regmap_update_bits(aw_dev->regmap, AW_PID_2049_HAGCCFG7_REG, + ~AW_PID_2049_AGC_DSP_CTL_MASK, AW_PID_2049_AGC_DSP_CTL_DISABLE_VALUE); + } + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +static int aw_dev_dsp_check_st(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + int i; + + for (i =3D 0; i < AW_DSP_ST_CHECK_MAX; i++) { + ret =3D regmap_read(aw_dev->regmap, AW_PID_2049_SYSST_REG, ®_val); + if (ret) { + dev_err(aw_dev->dev, "read reg0x%x failed", AW_PID_2049_SYSST_REG); + continue; + } + + if ((reg_val & (~AW_PID_2049_DSPS_MASK)) !=3D AW_PID_2049_DSPS_NORMAL_VA= LUE) { + dev_err(aw_dev->dev, "check dsp st fail,reg_val:0x%04x", reg_val); + ret =3D -EPERM; + continue; + } else { + dev_dbg(aw_dev->dev, "dsp st check ok, reg_val:0x%04x", reg_val); + return 0; + } + } + + return ret; +} + +static void aw_dev_dsp_enable(struct aw_device *aw_dev, bool is_enable) +{ + int ret; + + if (is_enable) { + ret =3D regmap_update_bits(aw_dev->regmap, AW_PID_2049_SYSCTRL_REG, + ~AW_PID_2049_DSPBY_MASK, AW_PID_2049_DSPBY_WORKING_VALUE); + if (ret) + dev_dbg(aw_dev->dev, "enable dsp failed"); + } else { + ret =3D regmap_update_bits(aw_dev->regmap, AW_PID_2049_SYSCTRL_REG, + ~AW_PID_2049_DSPBY_MASK, AW_PID_2049_DSPBY_BYPASS_VALUE); + if (ret) + dev_dbg(aw_dev->dev, "disable dsp failed"); + } +} + +static int aw_dev_dsp_check_crc32(struct aw_device *aw_dev) +{ + int ret; + + if (aw_dev->dsp_cfg =3D=3D AW_DEV_DSP_BYPASS) { + dev_info(aw_dev->dev, "dsp bypass"); + return 0; + } + + ret =3D aw_dev_dsp_set_crc32(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "set dsp crc32 failed"); + return ret; + } + + aw_dev_dsp_check_crc_enable(aw_dev, true); + + /* dsp enable */ + aw_dev_dsp_enable(aw_dev, true); + usleep_range(AW_5000_US, AW_5000_US + 100); + + ret =3D aw_dev_dsp_check_st(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "check crc32 fail"); + } else { + aw_dev_dsp_check_crc_enable(aw_dev, false); + aw_dev->dsp_crc_st =3D AW_DSP_CRC_OK; + } + + return ret; +} + +static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd) +{ + int ret; + + if (pwd) { + ret =3D regmap_update_bits(aw_dev->regmap, AW_PID_2049_SYSCTRL_REG, + ~AW_PID_2049_PWDN_MASK, AW_PID_2049_PWDN_POWER_DOWN_VALUE); + } else { + ret =3D regmap_update_bits(aw_dev->regmap, AW_PID_2049_SYSCTRL_REG, + ~AW_PID_2049_PWDN_MASK, AW_PID_2049_PWDN_WORKING_VALUE); + } + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd) +{ + int ret; + + if (amppd) { + ret =3D regmap_update_bits(aw_dev->regmap, AW_PID_2049_SYSCTRL_REG, + ~AW_PID_2049_AMPPD_MASK, AW_PID_2049_AMPPD_POWER_DOWN_VALUE); + } else { + ret =3D regmap_update_bits(aw_dev->regmap, AW_PID_2049_SYSCTRL_REG, + ~AW_PID_2049_AMPPD_MASK, AW_PID_2049_AMPPD_WORKING_VALUE); + } + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +void aw883xx_dev_mute(struct aw_device *aw_dev, bool is_mute) +{ + int ret; + + if (is_mute) { + aw_dev_fade_out(aw_dev); + ret =3D regmap_update_bits(aw_dev->regmap, AW_PID_2049_SYSCTRL_REG, + ~AW_PID_2049_HMUTE_MASK, AW_PID_2049_HMUTE_ENABLE_VALUE); + } else { + ret =3D regmap_update_bits(aw_dev->regmap, AW_PID_2049_SYSCTRL_REG, + ~AW_PID_2049_HMUTE_MASK, AW_PID_2049_HMUTE_DISABLE_VALUE); + aw_dev_fade_in(aw_dev); + } + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} +EXPORT_SYMBOL_GPL(aw883xx_dev_mute); + +static int aw_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk) +{ + unsigned int reg_val; + u16 reg_icalk; + int ret; + + ret =3D regmap_read(aw_dev->regmap, AW_PID_2049_EFRM2_REG, ®_val); + if (ret) + return ret; + + reg_icalk =3D reg_val & (~AW_PID_2049_EF_ISN_GESLP_MASK); + + if (reg_icalk & (~AW_PID_2049_EF_ISN_GESLP_SIGN_MASK)) + reg_icalk =3D reg_icalk | AW_PID_2049_EF_ISN_GESLP_SIGN_NEG; + + *icalk =3D (int16_t)reg_icalk; + + return ret; +} + +static int aw_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk) +{ + unsigned int reg_val; + u16 reg_vcalk; + int ret; + + ret =3D regmap_read(aw_dev->regmap, AW_PID_2049_EFRH_REG, ®_val); + if (ret) + return ret; + + reg_val =3D reg_val >> AW_PID_2049_EF_VSENSE_GAIN_SHIFT; + + reg_vcalk =3D (u16)reg_val & (~AW_PID_2049_EF_VSN_GESLP_MASK); + + if (reg_vcalk & (~AW_PID_2049_EF_VSN_GESLP_SIGN_MASK)) + reg_vcalk =3D reg_vcalk | AW_PID_2049_EF_VSN_GESLP_SIGN_NEG; + + *vcalk =3D (int16_t)reg_vcalk; + + return ret; +} + +static int aw_dev_get_vcalk_dac(struct aw_device *aw_dev, int16_t *vcalk) +{ + unsigned int reg_val; + u16 reg_vcalk; + int ret; + + ret =3D regmap_read(aw_dev->regmap, AW_PID_2049_EFRM2_REG, ®_val); + if (ret) + return ret; + + reg_vcalk =3D reg_val >> AW_PID_2049_EF_DAC_GESLP_SHIFT; + + if (reg_vcalk & AW_PID_2049_EF_DAC_GESLP_SIGN_MASK) + reg_vcalk =3D reg_vcalk | AW_PID_2049_EF_DAC_GESLP_SIGN_NEG; + + *vcalk =3D (int16_t)reg_vcalk; + + return ret; +} + +static int aw_dev_vsense_select(struct aw_device *aw_dev, int *vsense_sele= ct) +{ + unsigned int vsense_reg_val; + int ret; + + ret =3D regmap_read(aw_dev->regmap, AW_PID_2049_I2SCFG3_REG, &vsense_reg_= val); + if (ret) { + dev_err(aw_dev->dev, "read vsense_reg_val failed"); + return ret; + } + dev_dbg(aw_dev->dev, "vsense_reg =3D 0x%x", vsense_reg_val); + + if (vsense_reg_val & (~AW_PID_2049_VDSEL_MASK)) { + *vsense_select =3D AW_DEV_VDSEL_VSENSE; + dev_dbg(aw_dev->dev, "vsense outside"); + } else { + *vsense_select =3D AW_DEV_VDSEL_DAC; + dev_dbg(aw_dev->dev, "vsense inside"); + } + + return 0; +} + +static int aw_dev_set_vcalb(struct aw_device *aw_dev) +{ + int16_t icalk_val, vcalk_val; + int icalk, vsense_select; + u32 vcalb_adj, reg_val; + int vcalb, vcalk; + int ret; + + ret =3D aw_dev_dsp_read(aw_dev, AW_PID_2049_DSP_REG_VCALB, &vcalb_adj, AW= _DSP_16_DATA); + if (ret) { + dev_err(aw_dev->dev, "read vcalb_adj failed"); + return ret; + } + + ret =3D aw_dev_vsense_select(aw_dev, &vsense_select); + if (ret) + return ret; + dev_dbg(aw_dev->dev, "vsense_select =3D %d", vsense_select); + + ret =3D aw_dev_get_icalk(aw_dev, &icalk_val); + if (ret) + return ret; + icalk =3D AW_PID_2049_CABL_BASE_VALUE + AW_PID_2049_ICABLK_FACTOR * icalk= _val; + + switch (vsense_select) { + case AW_DEV_VDSEL_VSENSE: + ret =3D aw_dev_get_vcalk(aw_dev, &vcalk_val); + if (ret) + return ret; + vcalk =3D AW_PID_2049_CABL_BASE_VALUE + AW_PID_2049_VCABLK_FACTOR * vcal= k_val; + vcalb =3D AW_PID_2049_VCAL_FACTOR * AW_PID_2049_VSCAL_FACTOR / + AW_PID_2049_ISCAL_FACTOR * icalk / vcalk * vcalb_adj; + + dev_dbg(aw_dev->dev, "vcalk_factor=3D%d, vscal_factor=3D%d, icalk=3D%d, = vcalk=3D%d", + AW_PID_2049_VCABLK_FACTOR, AW_PID_2049_VSCAL_FACTOR, icalk, vcalk); + break; + case AW_DEV_VDSEL_DAC: + ret =3D aw_dev_get_vcalk_dac(aw_dev, &vcalk_val); + if (ret) + return ret; + vcalk =3D AW_PID_2049_CABL_BASE_VALUE + AW_PID_2049_VCABLK_FACTOR_DAC * = vcalk_val; + vcalb =3D AW_PID_2049_VCAL_FACTOR * AW_PID_2049_VSCAL_FACTOR_DAC / + AW_PID_2049_ISCAL_FACTOR * icalk / vcalk * vcalb_adj; + + dev_dbg(aw_dev->dev, "vcalk_dac_factor=3D%d, vscal_dac_factor=3D%d, ical= k=3D%d, vcalk=3D%d", + AW_PID_2049_VCABLK_FACTOR_DAC, + AW_PID_2049_VSCAL_FACTOR_DAC, icalk, vcalk); + break; + default: + dev_err(aw_dev->dev, "unsupport vsense status"); + return -EINVAL; + } + + if ((vcalk =3D=3D 0) || (AW_PID_2049_ISCAL_FACTOR =3D=3D 0)) { + dev_err(aw_dev->dev, "vcalk:%d or desc->iscal_factor:%d unsupported", + vcalk, AW_PID_2049_ISCAL_FACTOR); + return -EINVAL; + } + + vcalb =3D vcalb >> AW_PID_2049_VCALB_ADJ_FACTOR; + reg_val =3D (u32)vcalb; + + dev_dbg(aw_dev->dev, "vcalb=3D%d, reg_val=3D0x%x, vcalb_adj =3D0x%x", + vcalb, reg_val, vcalb_adj); + + ret =3D aw_dev_dsp_write(aw_dev, AW_PID_2049_DSP_REG_VCALB, reg_val, AW_D= SP_16_DATA); + if (ret) { + dev_err(aw_dev->dev, "write vcalb failed"); + return ret; + } + + ret =3D aw_dev_modify_dsp_cfg(aw_dev, AW_PID_2049_DSP_REG_VCALB, + (u32)reg_val, AW_DSP_16_DATA); + if (ret) + dev_err(aw_dev->dev, "modify dsp cfg failed"); + + return ret; +} + +static int aw_dev_get_cali_f0_delay(struct aw_device *aw_dev) +{ + struct aw_cali_delay_desc *desc =3D &aw_dev->cali_delay_desc; + u32 cali_delay; + int ret; + + ret =3D aw_dev_dsp_read(aw_dev, + AW_PID_2049_DSP_CALI_F0_DELAY, &cali_delay, AW_DSP_16_DATA); + if (ret) + dev_err(aw_dev->dev, "read cali delay failed, ret=3D%d", ret); + else + desc->delay =3D AW_CALI_DELAY_CACL(cali_delay); + + dev_dbg(aw_dev->dev, "read cali delay: %d ms", desc->delay); + + return ret; +} + +static void aw_dev_get_int_status(struct aw_device *aw_dev, unsigned short= *int_status) +{ + unsigned int reg_val; + int ret; + + ret =3D regmap_read(aw_dev->regmap, AW_PID_2049_SYSINT_REG, ®_val); + if (ret) + dev_err(aw_dev->dev, "read interrupt reg fail, ret=3D%d", ret); + else + *int_status =3D reg_val; + + dev_dbg(aw_dev->dev, "read interrupt reg =3D 0x%04x", *int_status); +} + +static void aw_dev_clear_int_status(struct aw_device *aw_dev) +{ + u16 int_status; + + /* read int status and clear */ + aw_dev_get_int_status(aw_dev, &int_status); + /* make sure int status is clear */ + aw_dev_get_int_status(aw_dev, &int_status); + if (int_status) + dev_info(aw_dev->dev, "int status(%d) is not cleaned.\n", int_status); +} + +static int aw_dev_get_iis_status(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret =3D regmap_read(aw_dev->regmap, AW_PID_2049_SYSST_REG, ®_val); + if (ret) + return -EIO; + if ((reg_val & AW_PID_2049_BIT_PLL_CHECK) !=3D AW_PID_2049_BIT_PLL_CHECK)= { + dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_check_mode1_pll(struct aw_device *aw_dev) +{ + int ret, i; + + for (i =3D 0; i < AW_DEV_SYSST_CHECK_MAX; i++) { + ret =3D aw_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 -EPERM; +} + +static int aw_dev_check_mode2_pll(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret, i; + + ret =3D regmap_read(aw_dev->regmap, AW_PID_2049_PLLCTRL1_REG, ®_val); + if (ret) + return ret; + + reg_val &=3D (~AW_PID_2049_CCO_MUX_MASK); + if (reg_val =3D=3D AW_PID_2049_CCO_MUX_DIVIDED_VALUE) { + dev_dbg(aw_dev->dev, "CCO_MUX is already divider"); + return -EPERM; + } + + /* change mode2 */ + ret =3D regmap_update_bits(aw_dev->regmap, AW_PID_2049_PLLCTRL1_REG, + ~AW_PID_2049_CCO_MUX_MASK, AW_PID_2049_CCO_MUX_DIVIDED_VALUE); + if (ret) + return ret; + + for (i =3D 0; i < AW_DEV_SYSST_CHECK_MAX; i++) { + ret =3D aw_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 iis signal check error"); + usleep_range(AW_2000_US, AW_2000_US + 10); + } else { + break; + } + } + + /* change mode1 */ + ret =3D regmap_update_bits(aw_dev->regmap, AW_PID_2049_PLLCTRL1_REG, + ~AW_PID_2049_CCO_MUX_MASK, AW_PID_2049_CCO_MUX_BYPASS_VALUE); + if (ret =3D=3D 0) { + usleep_range(AW_2000_US, AW_2000_US + 10); + for (i =3D 0; i < AW_DEV_SYSST_CHECK_MAX; i++) { + ret =3D aw_dev_check_mode1_pll(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; +} + +static int aw_dev_check_syspll(struct aw_device *aw_dev) +{ + int ret; + + ret =3D aw_dev_check_mode1_pll(aw_dev); + if (ret) { + dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check"); + ret =3D aw_dev_check_mode2_pll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 check iis failed"); + return ret; + } + } + + return ret; +} + +static int aw_dev_check_sysst(struct aw_device *aw_dev) +{ + unsigned int check_val; + unsigned int reg_val; + int ret, i; + + for (i =3D 0; i < AW_DEV_SYSST_CHECK_MAX; i++) { + ret =3D regmap_read(aw_dev->regmap, AW_PID_2049_SYSST_REG, ®_val); + if (ret) + return ret; + + check_val =3D reg_val & (~AW_PID_2049_BIT_SYSST_CHECK_MASK) + & AW_PID_2049_BIT_SYSST_CHECK; + if (((reg_val & (~AW_PID_2049_BIT_SYSST_CHECK_MASK)) + & AW_PID_2049_BIT_SYSST_CHECK) + !=3D AW_PID_2049_BIT_SYSST_CHECK) { + dev_err(aw_dev->dev, "check sysst fail, cnt=3D%d, reg_val=3D0x%04x, che= ck:0x%x", + i, reg_val, check_val); + usleep_range(AW_2000_US, AW_2000_US + 10); + } else { + return 0; + } + } + + return -EPERM; +} + +static int aw_dev_check_sysint(struct aw_device *aw_dev) +{ + u16 reg_val; + + aw_dev_get_int_status(aw_dev, ®_val); + + if (reg_val & AW_PID_2049_BIT_SYSINT_CHECK) { + dev_err(aw_dev->dev, "pa stop check fail:0x%04x", reg_val); + return -EINVAL; + } + + return 0; +} + +static void aw_dev_get_cur_mode_st(struct aw_device *aw_dev) +{ + struct aw_profctrl_desc *profctrl_desc =3D &aw_dev->profctrl_desc; + unsigned int reg_val; + int ret; + + ret =3D regmap_read(aw_dev->regmap, AW_PID_2049_SYSCTRL_REG, ®_val); + if (ret) { + dev_dbg(aw_dev->dev, "%s failed", __func__); + return; + } + if ((reg_val & (~AW_PID_2049_RCV_MODE_MASK)) =3D=3D AW_PID_2049_RCV_MODE_= RECEIVER_VALUE) + profctrl_desc->cur_mode =3D AW_RCV_MODE; + else + profctrl_desc->cur_mode =3D AW_NOT_RCV_MODE; +} + +static void aw_dev_get_dsp_config(struct aw_device *aw_dev, unsigned char = *dsp_cfg) +{ + unsigned int reg_val =3D 0; + int ret; + + ret =3D regmap_read(aw_dev->regmap, AW_PID_2049_SYSCTRL_REG, ®_val); + if (ret) { + dev_dbg(aw_dev->dev, "%s failed", __func__); + return; + } + if (reg_val & (~AW_PID_2049_DSPBY_MASK)) + *dsp_cfg =3D AW_DEV_DSP_BYPASS; + else + *dsp_cfg =3D AW_DEV_DSP_WORK; +} + +static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char f= lag) +{ + int ret; + + switch (flag) { + case AW_DEV_MEMCLK_PLL: + ret =3D regmap_update_bits(aw_dev->regmap, AW_PID_2049_DBGCTRL_REG, + ~AW_PID_2049_MEM_CLKSEL_MASK, + AW_PID_2049_MEM_CLKSEL_DAP_HCLK_VALUE); + if (ret) + dev_err(aw_dev->dev, "memclk select pll failed"); + break; + case AW_DEV_MEMCLK_OSC: + ret =3D regmap_update_bits(aw_dev->regmap, AW_PID_2049_DBGCTRL_REG, + ~AW_PID_2049_MEM_CLKSEL_MASK, + AW_PID_2049_MEM_CLKSEL_OSC_CLK_VALUE); + if (ret) + dev_err(aw_dev->dev, "memclk select OSC failed"); + break; + default: + dev_err(aw_dev->dev, "unknown memclk config, flag=3D0x%x", flag); + break; + } +} + +static int aw_dev_get_dsp_status(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret =3D regmap_read(aw_dev->regmap, AW_PID_2049_WDT_REG, ®_val); + if (ret) + return ret; + if (!(reg_val & (~AW_PID_2049_WDT_CNT_MASK))) + ret =3D -EPERM; + + return ret; +} + +static int aw_dev_get_vmax(struct aw_device *aw_dev, unsigned int *vmax) +{ + return aw_dev_dsp_read(aw_dev, AW_PID_2049_DSP_REG_VMAX, vmax, AW_DSP_16_= DATA); +} + +static int aw_dev_update_reg_container(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + struct aw_volume_desc *vol_desc =3D &aw_dev->volume_desc; + unsigned int read_val; + int16_t *reg_data; + int data_len; + u16 read_vol; + u16 reg_val; + u8 reg_addr; + int i, ret; + + reg_data =3D (int16_t *)data; + data_len =3D len >> 1; + + if (data_len & 0x1) { + dev_err(aw_dev->dev, "data len:%d unsupported", data_len); + return -EINVAL; + } + + for (i =3D 0; i < data_len; i +=3D 2) { + reg_addr =3D reg_data[i]; + reg_val =3D reg_data[i + 1]; + + if (reg_addr =3D=3D AW_PID_2049_SYSCTRL_REG) { + ret =3D regmap_read(aw_dev->regmap, reg_addr, &read_val); + if (ret) + break; + read_val &=3D (~AW_PID_2049_HMUTE_MASK); + reg_val &=3D AW_PID_2049_HMUTE_MASK; + reg_val |=3D read_val; + } + if (reg_addr =3D=3D AW_PID_2049_HAGCCFG7_REG) + reg_val &=3D AW_PID_2049_AGC_DSP_CTL_MASK; + + if (reg_addr =3D=3D AW_PID_2049_I2SCFG1_REG) { + /* close tx */ + reg_val &=3D AW_PID_2049_I2STXEN_MASK; + reg_val |=3D AW_PID_2049_I2STXEN_DISABLE_VALUE; + } + + if (reg_addr =3D=3D AW_PID_2049_SYSCTRL2_REG) { + read_vol =3D (reg_val & (~AW_PID_2049_VOL_MASK)) >> + AW_PID_2049_VOL_START_BIT; + aw_dev->volume_desc.init_volume =3D + reg_val_to_db(read_vol); + } + ret =3D regmap_write(aw_dev->regmap, reg_addr, reg_val); + if (ret) + break; + + } + + aw_dev_get_cur_mode_st(aw_dev); + + if (aw_dev->prof_cur !=3D aw_dev->prof_index) { + /* clear control volume when PA change profile */ + vol_desc->ctl_volume =3D 0; + } else { + /* keep control volume when PA start with sync mode */ + aw_dev_set_volume(aw_dev, vol_desc->ctl_volume); + } + + /* keep min volume */ + if (aw_dev->fade_en) + aw_dev_set_volume(aw_dev, AW_PID_2049_MUTE_VOL); + + aw_dev_get_dsp_config(aw_dev, &aw_dev->dsp_cfg); + + return ret; +} + +static int aw_dev_reg_update(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int ret; + + if (len && (data !=3D NULL)) { + ret =3D aw_dev_update_reg_container(aw_dev, data, len); + if (ret) { + 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 -EINVAL; + } + + return 0; +} + +static int aw_dev_get_ra(struct aw_cali_desc *cali_desc) +{ + struct aw_device *aw_dev =3D + container_of(cali_desc, struct aw_device, cali_desc); + u32 dsp_ra; + int ret; + + ret =3D aw_dev_dsp_read(aw_dev, AW_PID_2049_DSP_REG_CFG_ADPZ_RA, + &dsp_ra, AW_DSP_32_DATA); + if (ret) { + dev_err(aw_dev->dev, "read ra error"); + return ret; + } + + cali_desc->ra =3D AW_DSP_RE_TO_SHOW_RE(dsp_ra, + AW_PID_2049_DSP_RE_SHIFT); + + return ret; +} + +static int aw_dev_dsp_update_container(struct aw_device *aw_dev, + unsigned char *data, unsigned int len, unsigned short base) +{ +#ifdef AW_DSP_I2C_WRITES + u32 tmp_len; +#else + u16 reg_val; +#endif + int i, ret; + + mutex_lock(&aw_dev->dsp_lock); +#ifdef AW_DSP_I2C_WRITES + ret =3D regmap_write(aw_dev->regmap, AW_PID_2049_DSPMADD_REG, base); + if (ret) + goto error_operation; + + for (i =3D 0; i < len; i +=3D AW_MAX_RAM_WRITE_BYTE_SIZE) { + if ((len - i) < AW_MAX_RAM_WRITE_BYTE_SIZE) + tmp_len =3D len - i; + else + tmp_len =3D AW_MAX_RAM_WRITE_BYTE_SIZE; + + ret =3D regmap_raw_write(aw_dev->regmap, AW_PID_2049_DSPMDAT_REG, + &data[i], tmp_len); + if (ret) + goto error_operation; + } + +#else + /* i2c write */ + ret =3D regmap_write(aw_dev->regmap, AW_PID_2049_DSPMADD_REG, base); + if (ret) + goto error_operation; + for (i =3D 0; i < len; i +=3D 2) { + reg_val =3D (data[i] << 8) + data[i + 1]; + ret =3D regmap_write(aw_dev->regmap, AW_PID_2049_DSPMDAT_REG, + reg_val); + if (ret) + goto error_operation; + } +#endif + mutex_unlock(&aw_dev->dsp_lock); + + return 0; + +error_operation: + mutex_unlock(&aw_dev->dsp_lock); + return ret; +} + +static int aw_dev_dsp_update_fw(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + + dev_dbg(aw_dev->dev, "dsp firmware len:%d", len); + + if (len && (data !=3D NULL)) { + aw_dev_dsp_update_container(aw_dev, + data, len, AW_PID_2049_DSP_FW_ADDR); + aw_dev->dsp_fw_len =3D len; + } else { + dev_err(aw_dev->dev, "dsp firmware data is null or len is 0"); + return -EINVAL; + } + + 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 =3D &aw_dev->crc_dsp_cfg; + + if (!crc_dsp_cfg->data) { + crc_dsp_cfg->data =3D devm_kzalloc(aw_dev->dev, size, GFP_KERNEL); + if (!crc_dsp_cfg->data) + return -ENOMEM; + crc_dsp_cfg->len =3D size; + } else if (crc_dsp_cfg->len < size) { + devm_kfree(aw_dev->dev, crc_dsp_cfg->data); + crc_dsp_cfg->data =3D 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 =3D size; + } + memcpy(crc_dsp_cfg->data, data, size); + swab16_array((u16 *)crc_dsp_cfg->data, size >> 1); + + return 0; +} + +static int aw_dev_dsp_update_cfg(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int ret; + + dev_dbg(aw_dev->dev, "dsp config len:%d", len); + + if (len && (data !=3D NULL)) { + aw_dev_dsp_update_container(aw_dev, + data, len, AW_PID_2049_DSP_CFG_ADDR); + aw_dev->dsp_cfg_len =3D len; + + ret =3D aw_dev_copy_to_crc_dsp_cfg(aw_dev, data, len); + if (ret) + return ret; + + ret =3D aw_dev_set_vcalb(aw_dev); + if (ret) + return ret; + ret =3D aw_dev_get_ra(&aw_dev->cali_desc); + if (ret) + return ret; + ret =3D aw_dev_get_cali_f0_delay(aw_dev); + if (ret) + return ret; + + ret =3D aw_dev_get_vmax(aw_dev, &aw_dev->vmax_desc.init_vmax); + if (ret) { + 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 =3D AW_DSP_CRC_NA; + } else { + dev_err(aw_dev->dev, "dsp config data is null or len is 0"); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_check_sram(struct aw_device *aw_dev) +{ + unsigned int reg_val; + + mutex_lock(&aw_dev->dsp_lock); + /* check the odd bits of reg 0x40 */ + regmap_write(aw_dev->regmap, AW_PID_2049_DSPMADD_REG, AW_DSP_ODD_NUM_BIT_= TEST); + regmap_read(aw_dev->regmap, AW_PID_2049_DSPMADD_REG, ®_val); + if (reg_val !=3D AW_DSP_ODD_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check reg 0x40 odd bit failed, read[0x%x] !=3D wri= te[0x%x]", + reg_val, AW_DSP_ODD_NUM_BIT_TEST); + goto error; + } + + /* check the even bits of reg 0x40 */ + regmap_write(aw_dev->regmap, AW_PID_2049_DSPMADD_REG, AW_DSP_EVEN_NUM_BIT= _TEST); + regmap_read(aw_dev->regmap, AW_PID_2049_DSPMADD_REG, ®_val); + if (reg_val !=3D AW_DSP_EVEN_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check reg 0x40 even bit failed, read[0x%x] !=3D wr= ite[0x%x]", + reg_val, AW_DSP_EVEN_NUM_BIT_TEST); + goto error; + } + + /* check dsp_fw_base_addr */ + aw_dev_dsp_write_16bit(aw_dev, AW_PID_2049_DSP_FW_ADDR, AW_DSP_EVEN_NUM_B= IT_TEST); + aw_dev_dsp_read_16bit(aw_dev, AW_PID_2049_DSP_FW_ADDR, ®_val); + if (reg_val !=3D AW_DSP_EVEN_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check dsp fw addr failed, read[0x%x] !=3D write[0x= %x]", + reg_val, AW_DSP_EVEN_NUM_BIT_TEST); + goto error; + } + + /* check dsp_cfg_base_addr */ + aw_dev_dsp_write_16bit(aw_dev, AW_PID_2049_DSP_CFG_ADDR, AW_DSP_ODD_NUM_B= IT_TEST); + aw_dev_dsp_read_16bit(aw_dev, AW_PID_2049_DSP_CFG_ADDR, ®_val); + if (reg_val !=3D AW_DSP_ODD_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] !=3D 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 -EPERM; +} + +int aw883xx_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, boo= l force_up_en) +{ + struct aw_prof_desc *prof_index_desc; + struct aw_sec_data_desc *sec_desc; + char *prof_name; + int ret; + + if ((aw_dev->prof_cur =3D=3D aw_dev->prof_index) && + (force_up_en =3D=3D AW_FORCE_UPDATE_OFF)) { + dev_dbg(aw_dev->dev, "scene no change, not update"); + return 0; + } + + if (aw_dev->fw_status =3D=3D AW_DEV_FW_FAILED) { + dev_err(aw_dev->dev, "fw status[%d] error", aw_dev->fw_status); + return -EPERM; + } + + prof_name =3D aw883xx_dev_get_prof_name(aw_dev, aw_dev->prof_index); + + dev_dbg(aw_dev->dev, "start update %s", prof_name); + + ret =3D aw883xx_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index= _desc); + if (ret) + return ret; + + /* update reg */ + sec_desc =3D prof_index_desc->sec_desc; + ret =3D aw_dev_reg_update(aw_dev, sec_desc[AW_DATA_TYPE_REG].data, + sec_desc[AW_DATA_TYPE_REG].len); + if (ret) { + dev_err(aw_dev->dev, "update reg failed"); + return ret; + } + + aw883xx_dev_mute(aw_dev, true); + + if (aw_dev->dsp_cfg =3D=3D AW_DEV_DSP_WORK) + aw_dev_dsp_enable(aw_dev, false); + + aw_dev_select_memclk(aw_dev, AW_DEV_MEMCLK_OSC); + + if (up_dsp_fw_en) { + ret =3D aw_dev_check_sram(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "check sram failed"); + goto error; + } + + /* update dsp firmware */ + dev_dbg(aw_dev->dev, "fw_ver: [%x]", prof_index_desc->fw_ver); + ret =3D aw_dev_dsp_update_fw(aw_dev, sec_desc[AW_DATA_TYPE_DSP_FW].data, + sec_desc[AW_DATA_TYPE_DSP_FW].len); + if (ret) { + dev_err(aw_dev->dev, "update dsp fw failed"); + goto error; + } + } + + /* update dsp config */ + ret =3D aw_dev_dsp_update_cfg(aw_dev, sec_desc[AW_DATA_TYPE_DSP_CFG].data, + sec_desc[AW_DATA_TYPE_DSP_CFG].len); + if (ret) { + dev_err(aw_dev->dev, "update dsp cfg failed"); + goto error; + } + + aw_dev_select_memclk(aw_dev, AW_DEV_MEMCLK_PLL); + + aw_dev->prof_cur =3D aw_dev->prof_index; + + return 0; + +error: + aw_dev_select_memclk(aw_dev, AW_DEV_MEMCLK_PLL); + return ret; +} +EXPORT_SYMBOL_GPL(aw883xx_dev_fw_update); + +static int aw_dev_dsp_check(struct aw_device *aw_dev) +{ + int ret, i; + + switch (aw_dev->dsp_cfg) { + case AW_DEV_DSP_BYPASS: + dev_dbg(aw_dev->dev, "dsp bypass"); + ret =3D 0; + break; + case AW_DEV_DSP_WORK: + aw_dev_dsp_enable(aw_dev, false); + aw_dev_dsp_enable(aw_dev, true); + usleep_range(AW_1000_US, AW_1000_US + 10); + for (i =3D 0; i < AW_DEV_DSP_CHECK_MAX; i++) { + ret =3D aw_dev_get_dsp_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp wdt status error=3D%d", ret); + usleep_range(AW_2000_US, AW_2000_US + 10); + } + } + break; + default: + dev_err(aw_dev->dev, "unknown dsp cfg=3D%d", aw_dev->dsp_cfg); + ret =3D -EINVAL; + break; + } + + return ret; +} + +static void aw_dev_update_cali_re(struct aw_cali_desc *cali_desc) +{ + struct aw_device *aw_dev =3D + container_of(cali_desc, struct aw_device, cali_desc); + int ret; + + if ((aw_dev->cali_desc.cali_re < AW_CALI_RE_MAX) && + (aw_dev->cali_desc.cali_re > AW_CALI_RE_MIN)) { + + ret =3D aw_dev_dsp_set_cali_re(aw_dev); + if (ret) + dev_err(aw_dev->dev, "set cali re failed"); + } +} + +int aw883xx_dev_start(struct aw_device *aw_dev) +{ + int ret; + + if (aw_dev->status =3D=3D 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 =3D aw_dev_check_syspll(aw_dev); + if (ret) { + 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 =3D aw_dev_check_sysst(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "sysst check failed"); + goto sysst_check_fail; + } + + if (aw_dev->dsp_cfg =3D=3D AW_DEV_DSP_WORK) { + /* dsp bypass */ + aw_dev_dsp_enable(aw_dev, false); + ret =3D aw_dev_dsp_fw_check(aw_dev); + if (ret) + goto dev_dsp_fw_check_fail; + + aw_dev_update_cali_re(&aw_dev->cali_desc); + + if (aw_dev->dsp_crc_st !=3D AW_DSP_CRC_OK) { + ret =3D aw_dev_dsp_check_crc32(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp crc check failed"); + goto crc_check_fail; + } + } + + ret =3D aw_dev_dsp_check(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp status check failed"); + goto dsp_check_fail; + } + } else { + dev_dbg(aw_dev->dev, "start pa with dsp bypass"); + } + + /* enable tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, true); + + /* close mute */ + aw883xx_dev_mute(aw_dev, false); + /* clear inturrupt */ + aw_dev_clear_int_status(aw_dev); + aw_dev->status =3D AW_DEV_PW_ON; + + return 0; + +dsp_check_fail: +crc_check_fail: + aw_dev_dsp_enable(aw_dev, false); +dev_dsp_fw_check_fail: +sysst_check_fail: + aw_dev_clear_int_status(aw_dev); + aw_dev_amppd(aw_dev, true); +pll_check_fail: + aw_dev_pwd(aw_dev, true); + aw_dev->status =3D AW_DEV_PW_OFF; + + return ret; +} +EXPORT_SYMBOL_GPL(aw883xx_dev_start); + +int aw883xx_dev_stop(struct aw_device *aw_dev) +{ + struct aw_sec_data_desc *dsp_cfg =3D + &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW_DATA_TYPE_DSP= _CFG]; + struct aw_sec_data_desc *dsp_fw =3D + &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW_DATA_TYPE_DSP= _FW]; + int int_st =3D 0; + int ret; + + if (aw_dev->status =3D=3D AW_DEV_PW_OFF) { + dev_info(aw_dev->dev, "already power off"); + return 0; + } + + aw_dev->status =3D AW_DEV_PW_OFF; + + /* set mute */ + aw883xx_dev_mute(aw_dev, true); + usleep_range(AW_4000_US, AW_4000_US + 100); + + /* close tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, false); + usleep_range(AW_1000_US, AW_1000_US + 100); + + /* check sysint state */ + int_st =3D aw_dev_check_sysint(aw_dev); + + /* close dsp */ + aw_dev_dsp_enable(aw_dev, false); + + /* enable amppd */ + aw_dev_amppd(aw_dev, true); + + if (int_st < 0) { + /* system status anomaly */ + aw_dev_select_memclk(aw_dev, AW_DEV_MEMCLK_OSC); + ret =3D aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len); + if (ret) + dev_err(aw_dev->dev, "update dsp fw failed"); + ret =3D aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len); + if (ret) + dev_err(aw_dev->dev, "update dsp cfg failed"); + aw_dev_select_memclk(aw_dev, AW_DEV_MEMCLK_PLL); + } + + /* set power down */ + aw_dev_pwd(aw_dev, true); + + return 0; +} +EXPORT_SYMBOL_GPL(aw883xx_dev_stop); + +int aw883xx_dev_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 =3D aw883xx_dev_cfg_load(aw_dev, aw_cfg); + if (ret) { + dev_err(aw_dev->dev, "aw_dev acf parse failed"); + return -EINVAL; + } + aw_dev->fade_in_time =3D AW_1000_US / 10; + aw_dev->fade_out_time =3D AW_1000_US >> 1; + aw_dev->prof_cur =3D aw_dev->prof_info.prof_desc[0].id; + aw_dev->prof_index =3D aw_dev->prof_info.prof_desc[0].id; + + ret =3D aw883xx_dev_fw_update(aw_dev, AW_FORCE_UPDATE_ON, AW_DSP_FW_UPDAT= E_ON); + if (ret) { + dev_err(aw_dev->dev, "fw update failed ret =3D %d\n", ret); + return ret; + } + + /* set mute */ + aw883xx_dev_mute(aw_dev, true); + usleep_range(AW_4000_US, AW_4000_US + 100); + + /* close tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, false); + usleep_range(AW_1000_US, AW_1000_US + 100); + + /* close dsp */ + aw_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; +} +EXPORT_SYMBOL_GPL(aw883xx_dev_init); + +static void aw883xx_parse_channel_dt(struct aw_device *aw_dev) +{ + struct device_node *np =3D aw_dev->dev->of_node; + u32 channel_value; + int ret; + + ret =3D of_property_read_u32(np, "sound-channel", &channel_value); + if (ret) { + dev_dbg(aw_dev->dev, + "read sound-channel failed,use default 0"); + aw_dev->channel =3D AW_DEV_DEFAULT_CH; + return; + } + + dev_dbg(aw_dev->dev, "read sound-channel value is: %d", + channel_value); + aw_dev->channel =3D channel_value; +} + +static void aw883xx_parse_fade_enable_dt(struct aw_device *aw_dev) +{ + struct device_node *np =3D aw_dev->dev->of_node; + u32 fade_en; + int ret; + + ret =3D of_property_read_u32(np, "fade-enable", &fade_en); + if (ret) { + dev_dbg(aw_dev->dev, + "read fade-enable failed, close fade_in_out"); + fade_en =3D AW_FADE_IN_OUT_DEFAULT; + } + + dev_dbg(aw_dev->dev, "read fade-enable value is: %d", fade_en); + + aw_dev->fade_en =3D fade_en; +} + +static int aw883xx_pid_2049_dev_init(struct aw_device *aw_dev) +{ + aw_dev->chip_id =3D AW883XX_PID_2049; + /* call aw device init func */ + aw_dev->acf =3D NULL; + aw_dev->prof_info.prof_desc =3D NULL; + aw_dev->prof_info.count =3D 0; + aw_dev->prof_info.prof_type =3D AW_DEV_NONE_TYPE_ID; + aw_dev->channel =3D 0; + aw_dev->fw_status =3D AW_DEV_FW_FAILED; + + aw_dev->fade_step =3D AW_PID_2049_VOLUME_STEP_DB; + aw_dev->volume_desc.ctl_volume =3D AW_PID_2049_VOL_DEFAULT_VALUE; + aw883xx_parse_channel_dt(aw_dev); + aw883xx_parse_fade_enable_dt(aw_dev); + + return 0; +} + +int aw883xx_dev_get_profile_count(struct aw_device *aw_dev) +{ + return aw_dev->prof_info.count; +} +EXPORT_SYMBOL_GPL(aw883xx_dev_get_profile_count); + +int aw883xx_dev_get_profile_index(struct aw_device *aw_dev) +{ + return aw_dev->prof_index; +} +EXPORT_SYMBOL_GPL(aw883xx_dev_get_profile_index); + +int aw883xx_dev_set_profile_index(struct aw_device *aw_dev, int index) +{ + /* check the index whether is valid */ + if ((index >=3D aw_dev->prof_info.count) || (index < 0)) + return -EINVAL; + /* check the index whether change */ + if (aw_dev->prof_index =3D=3D index) + return -EINVAL; + + aw_dev->prof_index =3D index; + dev_dbg(aw_dev->dev, "set prof[%s]", + aw_dev->prof_info.prof_name_list[aw_dev->prof_info.prof_desc[index].id]); + + return 0; +} +EXPORT_SYMBOL_GPL(aw883xx_dev_set_profile_index); + +char *aw883xx_dev_get_prof_name(struct aw_device *aw_dev, int index) +{ + struct aw_prof_info *prof_info =3D &aw_dev->prof_info; + struct aw_prof_desc *prof_desc; + + if ((index >=3D 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 =3D &aw_dev->prof_info.prof_desc[index]; + + return prof_info->prof_name_list[prof_desc->id]; +} +EXPORT_SYMBOL_GPL(aw883xx_dev_get_prof_name); + +int aw883xx_dev_get_prof_data(struct aw_device *aw_dev, int index, + struct aw_prof_desc **prof_desc) +{ + if ((index >=3D 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 =3D &aw_dev->prof_info.prof_desc[index]; + + return 0; +} +EXPORT_SYMBOL_GPL(aw883xx_dev_get_prof_data); + +int aw883xx_init(struct aw_device **aw_dev, struct i2c_client *i2c, struct= regmap *regmap) +{ + u16 chip_id; + int ret; + + if (*aw_dev) { + dev_info(&i2c->dev, "it should be initialized here.\n"); + } else { + *aw_dev =3D devm_kzalloc(&i2c->dev, sizeof(struct aw_device), GFP_KERNEL= ); + if (!(*aw_dev)) + return -ENOMEM; + } + + (*aw_dev)->i2c =3D i2c; + (*aw_dev)->dev =3D &i2c->dev; + (*aw_dev)->regmap =3D regmap; + mutex_init(&(*aw_dev)->dsp_lock); + + /* read chip id */ + ret =3D aw_dev_read_chipid((*aw_dev), &chip_id); + if (ret) { + dev_err(&i2c->dev, "dev_read_chipid failed ret=3D%d", ret); + return ret; + } + + switch (chip_id) { + case AW883XX_PID_2049: + ret =3D aw883xx_pid_2049_dev_init((*aw_dev)); + break; + default: + ret =3D -EINVAL; + dev_err((*aw_dev)->dev, "unsupported device"); + break; + } + + return ret; +} +EXPORT_SYMBOL_GPL(aw883xx_init); + +MODULE_DESCRIPTION("AW883XX device lib"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/aw883xx/aw883xx_device.h b/sound/soc/codecs/a= w883xx/aw883xx_device.h new file mode 100644 index 000000000000..a8042a07f988 --- /dev/null +++ b/sound/soc/codecs/aw883xx/aw883xx_device.h @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw883xx_device.h -- AW883XX common function for ALSA Audio Driver +// +// Copyright (c) 2022 AWINIC Technology CO., LTD +// +// Author: Bruce zhao +// + +#ifndef __AW883XX_DEVICE_FILE_H__ +#define __AW883XX_DEVICE_FILE_H__ + +#include "aw883xx.h" +#include "aw883xx_data_type.h" +#include "aw883xx_bin_parse.h" + +#define AW_DEV_DEFAULT_CH (0) +#define AW_DEV_DSP_CHECK_MAX (5) + +#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_RE_MAX (15000) +#define AW_CALI_RE_MIN (4000) +#define AW_CALI_DELAY_CACL(value) ((value * 32) / 48) + +#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_DEV_VDSEL_DAC =3D 0, + AW_DEV_VDSEL_VSENSE =3D 1, +}; + +enum { + AW_DSP_CRC_NA =3D 0, + AW_DSP_CRC_OK =3D 1, +}; + +enum { + AW_DSP_FW_UPDATE_OFF =3D 0, + AW_DSP_FW_UPDATE_ON =3D 1, +}; + +enum { + AW_FORCE_UPDATE_OFF =3D 0, + AW_FORCE_UPDATE_ON =3D 1, +}; + +enum { + AW_1000_US =3D 1000, + AW_2000_US =3D 2000, + AW_3000_US =3D 3000, + AW_4000_US =3D 4000, + AW_5000_US =3D 5000, + AW_10000_US =3D 10000, + AW_100000_US =3D 100000, +}; + +enum { + AW_DEV_TYPE_OK =3D 0, + AW_DEV_TYPE_NONE =3D 1, +}; + + +enum AW_DEV_STATUS { + AW_DEV_PW_OFF =3D 0, + AW_DEV_PW_ON, +}; + +enum AW_DEV_FW_STATUS { + AW_DEV_FW_FAILED =3D 0, + AW_DEV_FW_OK, +}; + +enum AW_DEV_MEMCLK { + AW_DEV_MEMCLK_OSC =3D 0, + AW_DEV_MEMCLK_PLL =3D 1, +}; + +enum AW_DEV_DSP_CFG { + AW_DEV_DSP_WORK =3D 0, + AW_DEV_DSP_BYPASS =3D 1, +}; + +enum { + AW_DSP_16_DATA =3D 0, + AW_DSP_32_DATA =3D 1, +}; + +enum { + AW_NOT_RCV_MODE =3D 0, + AW_RCV_MODE =3D 1, +}; + +struct aw_profctrl_desc { + unsigned int cur_mode; +}; + +struct aw_volume_desc { + unsigned int init_volume; + unsigned int mute_volume; + unsigned int ctl_volume; + unsigned int max_volume; +}; + +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_vmax_desc { + unsigned int init_vmax; +}; + +struct aw_cali_delay_desc { + unsigned int delay; +}; + +struct aw_cali_desc { + u32 cali_re; + u32 ra; +}; + +struct aw_container { + int len; + u8 data[]; +}; + +struct aw_device { + int status; + struct mutex dsp_lock; + + unsigned char prof_cur; + unsigned char prof_index; + unsigned char dsp_crc_st; + u16 chip_id; + + unsigned int channel; + unsigned int fade_step; + + struct i2c_client *i2c; + struct device *dev; + struct regmap *regmap; + char *acf; + + u32 fade_en; + unsigned char dsp_cfg; + + u32 dsp_fw_len; + u32 dsp_cfg_len; + u8 platform; + u8 fw_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_profctrl_desc profctrl_desc; + struct aw_volume_desc volume_desc; + struct aw_dsp_mem_desc dsp_mem_desc; + struct aw_vmax_desc vmax_desc; + + struct aw_cali_delay_desc cali_delay_desc; + struct aw_cali_desc cali_desc; + +}; + +int aw883xx_init(struct aw_device **aw_dev, struct i2c_client *i2c, struct= regmap *regmap); +int aw883xx_dev_init(struct aw_device *aw_dev, struct aw_container *aw_cfg= ); +int aw883xx_dev_start(struct aw_device *aw_dev); +int aw883xx_dev_stop(struct aw_device *aw_dev); +int aw883xx_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, boo= l force_up_en); + +void aw883xx_dev_set_volume(struct aw_device *aw_dev, unsigned short set_v= ol); +int aw883xx_dev_get_prof_data(struct aw_device *aw_dev, int index, + struct aw_prof_desc **prof_desc); +char *aw883xx_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_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_device *aw_dev, struct aw_contain= er *aw_cfg); +void aw883xx_dev_mute(struct aw_device *aw_dev, bool is_mute); + +#endif --=20 2.39.0 From nobody Tue Sep 16 04:37:52 2025 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 37DA9C4708E for ; Fri, 6 Jan 2023 03:29:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231751AbjAFD3R (ORCPT ); Thu, 5 Jan 2023 22:29:17 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39986 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231683AbjAFD3J (ORCPT ); Thu, 5 Jan 2023 22:29:09 -0500 Received: from out29-201.mail.aliyun.com (out29-201.mail.aliyun.com [115.124.29.201]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D351D676FA; Thu, 5 Jan 2023 19:29:07 -0800 (PST) X-Alimail-AntiSpam: AC=CONTINUE;BC=0.06712914|-1;CH=green;DM=|CONTINUE|false|;DS=CONTINUE|ham_system_inform|0.00658004-0.0028205-0.990599;FP=0|0|0|0|0|-1|-1|-1;HT=ay29a033018047193;MF=wangweidong.a@awinic.com;NM=1;PH=DS;RN=26;RT=26;SR=0;TI=SMTPD_---.QlbrpNb_1672975737; Received: from ubuntu-VirtualBox..(mailfrom:wangweidong.a@awinic.com fp:SMTPD_---.QlbrpNb_1672975737) by smtp.aliyun-inc.com; Fri, 06 Jan 2023 11:29:00 +0800 From: wangweidong.a@awinic.com To: lgirdwood@gmail.com, broonie@kernel.org, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, perex@perex.cz, tiwai@suse.com, ckeepax@opensource.cirrus.com, rf@opensource.cirrus.com, pierre-louis.bossart@linux.intel.com, james.schulman@cirrus.com, flatmax@flatmax.com, cezary.rojewski@intel.com, wangweidong.a@awinic.com, povik+lin@cutebit.org, yangxiaohua@everest-semi.com, daniel.beer@igorinstitute.com, 13691752556@139.com, srinivas.kandagatla@linaro.org, jonathan.albrieux@gmail.com, steve@sk2.org, alsa-devel@alsa-project.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Cc: liweilei@awinic.com, zhaolei@awinic.com, yijiangtao@awinic.com Subject: [PATCH V9 4/5] ASoC: codecs: Aw883xx chip register file, data type file and Kconfig Makefile Date: Fri, 6 Jan 2023 11:28:34 +0800 Message-Id: <20230106032835.141918-5-wangweidong.a@awinic.com> X-Mailer: git-send-email 2.39.0 In-Reply-To: <20230106032835.141918-1-wangweidong.a@awinic.com> References: <20230106032835.141918-1-wangweidong.a@awinic.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" 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 | 6 + sound/soc/codecs/aw883xx/aw883xx_data_type.h | 142 +++++++ .../soc/codecs/aw883xx/aw883xx_pid_2049_reg.h | 383 ++++++++++++++++++ 4 files changed, 541 insertions(+) create mode 100644 sound/soc/codecs/aw883xx/aw883xx_data_type.h 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 0f9d71490075..ea1dd48c642b 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 @@ -2167,4 +2168,13 @@ config SND_SOC_LPASS_TX_MACRO select SND_SOC_LPASS_MACRO_COMMON tristate "Qualcomm TX Macro in LPASS(Low Power Audio SubSystem)" =20 +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 71d3ce5867e4..afcac34e4d78 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -358,6 +358,10 @@ snd-soc-tas2780-objs :=3D tas2780.o # Mux snd-soc-simple-mux-objs :=3D simple-mux.o =20 +snd_soc_aw883xx-objs :=3D aw883xx/aw883xx.o \ + aw883xx/aw883xx_device.o \ + aw883xx/aw883xx_bin_parse.o \ + obj-$(CONFIG_SND_SOC_88PM860X) +=3D snd-soc-88pm860x.o obj-$(CONFIG_SND_SOC_AB8500_CODEC) +=3D snd-soc-ab8500-codec.o obj-$(CONFIG_SND_SOC_AC97_CODEC) +=3D snd-soc-ac97.o @@ -721,3 +725,5 @@ obj-$(CONFIG_SND_SOC_LPASS_TX_MACRO) +=3D snd-soc-lpass= -tx-macro.o =20 # Mux obj-$(CONFIG_SND_SOC_SIMPLE_MUX) +=3D snd-soc-simple-mux.o + +obj-$(CONFIG_SND_SOC_AW883XX) +=3Dsnd_soc_aw883xx.o diff --git a/sound/soc/codecs/aw883xx/aw883xx_data_type.h b/sound/soc/codec= s/aw883xx/aw883xx_data_type.h new file mode 100644 index 000000000000..080728bfdfac --- /dev/null +++ b/sound/soc/codecs/aw883xx/aw883xx_data_type.h @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw883_data_type.h -- The data type of the AW883XX chip +// +// Copyright (c) 2022 AWINIC Technology CO., LTD +// +// Author: Bruce zhao +// + +#ifndef __AW883XX_DATA_TYPE_H__ +#define __AW883XX_DATA_TYPE_H__ + +#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 =3D 0x00000001, + AW_CFG_HDR_VER_V1 =3D 0x01000000, +}; + +enum aw_cfg_dde_type { + AW_DEV_NONE_TYPE_ID =3D 0xFFFFFFFF, + AW_DEV_TYPE_ID =3D 0x00000000, + AW_SKT_TYPE_ID =3D 0x00000001, + AW_DEV_DEFAULT_TYPE_ID =3D 0x00000002, +}; + +enum aw_sec_type { + ACF_SEC_TYPE_REG =3D 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_MULTIPLE_BIN, + 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 =3D 0, + AW_DATA_TYPE_DSP_CFG, + AW_DATA_TYPE_DSP_FW, + AW_DATA_TYPE_MAX, +}; + +enum aw_prof_type { + AW_PROFILE_MUSIC =3D 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 =3D 0, + AW_PROFILE_OK, +}; + +struct aw_cfg_hdr { + u32 id; + char project[PROJECT_NAME_MAX]; + char custom[CUSTOMER_NAME_MAX]; + char version[CFG_VERSION_MAX]; + u32 author_id; + u32 ddt_size; + u32 ddt_num; + u32 hdr_offset; + u32 hdr_version; + u32 reserved[3]; +}; + +struct aw_cfg_dde { + u32 type; + char dev_name[DEV_NAME_MAX]; + u16 dev_index; + u16 dev_bus; + u16 dev_addr; + u16 dev_profile; + u32 data_type; + u32 data_size; + u32 data_offset; + u32 data_crc; + u32 reserved[5]; +}; + +struct aw_cfg_dde_v1 { + u32 type; + char dev_name[DEV_NAME_MAX]; + u16 dev_index; + u16 dev_bus; + u16 dev_addr; + u16 dev_profile; + u32 data_type; + u32 data_size; + u32 data_offset; + u32 data_crc; + char dev_profile_str[PROFILE_STR_MAX]; + u32 chip_id; + u32 reserved[4]; +}; + +struct aw_sec_data_desc { + u32 len; + u8 *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_pid_2049_reg.h b/sound/soc/co= decs/aw883xx/aw883xx_pid_2049_reg.h new file mode 100644 index 000000000000..bebedc54fce3 --- /dev/null +++ b/sound/soc/codecs/aw883xx/aw883xx_pid_2049_reg.h @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw883xx_pid_2049_reg.h -- AW883XX chip register file +// +// 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) + +enum aw883xx_id { + AW883XX_PID_2049 =3D 0x2049, +}; + +#define AW_PID_2049_REG_MAX (0x7D) + +#define AW_PID_2049_VOLUME_STEP_DB (6 * 8) + +#define AW_PID_2049_UVLS_START_BIT (14) +#define AW_PID_2049_UVLS_NORMAL (0) +#define AW_PID_2049_UVLS_NORMAL_VALUE \ + (AW_PID_2049_UVLS_NORMAL << AW_PID_2049_UVLS_START_BIT) + +#define AW_PID_2049_DSPS_START_BIT (12) +#define AW_PID_2049_DSPS_BITS_LEN (1) +#define AW_PID_2049_DSPS_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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 576F9C3DA7A for ; Fri, 6 Jan 2023 03:29:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231779AbjAFD3V (ORCPT ); Thu, 5 Jan 2023 22:29:21 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:40056 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231702AbjAFD3L (ORCPT ); Thu, 5 Jan 2023 22:29:11 -0500 Received: from out29-129.mail.aliyun.com (out29-129.mail.aliyun.com [115.124.29.129]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1E85E6A0D0; Thu, 5 Jan 2023 19:29:08 -0800 (PST) X-Alimail-AntiSpam: AC=CONTINUE;BC=0.1530381|-1;CH=green;DM=|CONTINUE|false|;DS=CONTINUE|ham_system_inform|0.325954-0.000438573-0.673608;FP=0|0|0|0|0|-1|-1|-1;HT=ay29a033018047207;MF=wangweidong.a@awinic.com;NM=1;PH=DS;RN=26;RT=26;SR=0;TI=SMTPD_---.QlbrpT3_1672975741; Received: from ubuntu-VirtualBox..(mailfrom:wangweidong.a@awinic.com fp:SMTPD_---.QlbrpT3_1672975741) by smtp.aliyun-inc.com; Fri, 06 Jan 2023 11:29:04 +0800 From: wangweidong.a@awinic.com To: lgirdwood@gmail.com, broonie@kernel.org, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, perex@perex.cz, tiwai@suse.com, ckeepax@opensource.cirrus.com, rf@opensource.cirrus.com, pierre-louis.bossart@linux.intel.com, james.schulman@cirrus.com, flatmax@flatmax.com, cezary.rojewski@intel.com, wangweidong.a@awinic.com, povik+lin@cutebit.org, yangxiaohua@everest-semi.com, daniel.beer@igorinstitute.com, 13691752556@139.com, srinivas.kandagatla@linaro.org, jonathan.albrieux@gmail.com, steve@sk2.org, alsa-devel@alsa-project.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Cc: liweilei@awinic.com, zhaolei@awinic.com, yijiangtao@awinic.com Subject: [PATCH V9 5/5] ASoC: dt-bindings: Add schema for "awinic,aw88395" Date: Fri, 6 Jan 2023 11:28:35 +0800 Message-Id: <20230106032835.141918-6-wangweidong.a@awinic.com> X-Mailer: git-send-email 2.39.0 In-Reply-To: <20230106032835.141918-1-wangweidong.a@awinic.com> References: <20230106032835.141918-1-wangweidong.a@awinic.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" From: Weidong Wang Add a DT schema for describing Awinic AW88395 audio amplifiers. They are controlled using I2C. Signed-off-by: Weidong Wang Reviewed-by: Krzysztof Kozlowski --- .../bindings/sound/awinic,aw88395.yaml | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/awinic,aw88395.= yaml diff --git a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml b/= Documentation/devicetree/bindings/sound/awinic,aw88395.yaml new file mode 100644 index 000000000000..35eef7d818a2 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/awinic,aw88395.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Awinic AW88395 Smart Audio Amplifier + +maintainers: + - Weidong Wang + +description: + The Awinic AW88395 is an I2S/TDM input, high efficiency + digital Smart K audio amplifier with an integrated 10.25V + smart boost convert. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: awinic,aw88395 + + reg: + maxItems: 1 + + '#sound-dai-cells': + const: 0 + + reset-gpios: + maxItems: 1 + +required: + - compatible + - reg + - '#sound-dai-cells' + - reset-gpios + +unevaluatedProperties: false + +examples: + - | + #include + i2c { + #address-cells =3D <1>; + #size-cells =3D <0>; + audio-codec@34 { + compatible =3D "awinic,aw88395"; + reg =3D <0x34>; + #sound-dai-cells =3D <0>; + reset-gpios =3D <&gpio 10 GPIO_ACTIVE_LOW>; + }; + }; --=20 2.39.0