alsa-devel.alsa-project.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V1 0/5]  ASoC: codecs: Add Awinic AW88261 audio amplifier driver
@ 2023-07-17 11:58 wangweidong.a
  2023-07-17 11:58 ` [PATCH V1 1/5] ASoC: codecs: Add i2c and codec registration for aw88261 and their associated operation functions wangweidong.a
                   ` (5 more replies)
  0 siblings, 6 replies; 19+ messages in thread
From: wangweidong.a @ 2023-07-17 11:58 UTC (permalink / raw)
  To: lgirdwood, broonie, robh+dt, krzysztof.kozlowski+dt, conor+dt,
	perex, tiwai, rf, wangweidong.a, shumingf, herve.codina, flatmax,
	ckeepax, doug, fido_max, pierre-louis.bossart, kiseok.jo,
	liweilei, colin.i.king, trix, alsa-devel, devicetree,
	linux-kernel
  Cc: yijiangtao, zhangjianming

From: Weidong Wang <wangweidong.a@awinic.com>

The AW88261 is an I2S/TDM input, high efficiency
digital Smart K audio amplifier with an integrated 10.25V
smart boost convert

Add a DT schema for describing Awinic aw88261 audio amplifiers. They are
controlled using I2C.

Weidong Wang (5):
  ASoC: codecs: Add i2c and codec registration for aw88261 and their
    associated operation functions
  ASoC: codecs: aw88261 function for ALSA Audio Driver
  ASoC: codecs: ACF bin parsing and check library file for aw88261
  ASoC: codecs: aw88261 chip register file, data type file and Kconfig
    Makefile
  ASoC: dt-bindings: Add schema for "awinic,aw88261"

 .../bindings/sound/awinic,aw88261.yaml        |  53 +
 sound/soc/codecs/Kconfig                      |  17 +
 sound/soc/codecs/Makefile                     |   5 +
 sound/soc/codecs/aw88261/aw88261.c            | 539 ++++++++++
 sound/soc/codecs/aw88261/aw88261.h            |  52 +
 sound/soc/codecs/aw88261/aw88261_data_type.h  | 144 +++
 sound/soc/codecs/aw88261/aw88261_device.c     | 959 +++++++++++++++++
 sound/soc/codecs/aw88261/aw88261_device.h     | 114 ++
 sound/soc/codecs/aw88261/aw88261_lib.c        | 997 ++++++++++++++++++
 sound/soc/codecs/aw88261/aw88261_lib.h        |  91 ++
 sound/soc/codecs/aw88261/aw88261_reg.h        | 377 +++++++
 11 files changed, 3348 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/awinic,aw88261.yaml
 create mode 100644 sound/soc/codecs/aw88261/aw88261.c
 create mode 100644 sound/soc/codecs/aw88261/aw88261.h
 create mode 100644 sound/soc/codecs/aw88261/aw88261_data_type.h
 create mode 100644 sound/soc/codecs/aw88261/aw88261_device.c
 create mode 100644 sound/soc/codecs/aw88261/aw88261_device.h
 create mode 100644 sound/soc/codecs/aw88261/aw88261_lib.c
 create mode 100644 sound/soc/codecs/aw88261/aw88261_lib.h
 create mode 100644 sound/soc/codecs/aw88261/aw88261_reg.h


base-commit: 8d7868c41df58edabc4e408d119a1aef58a54d9d
-- 
2.41.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH V1 1/5] ASoC: codecs: Add i2c and codec registration for aw88261 and their associated operation functions
  2023-07-17 11:58 [PATCH V1 0/5] ASoC: codecs: Add Awinic AW88261 audio amplifier driver wangweidong.a
@ 2023-07-17 11:58 ` wangweidong.a
  2023-07-17 20:33   ` Krzysztof Kozlowski
  2023-07-17 11:58 ` [PATCH V1 2/5] ASoC: codecs: aw88261 function for ALSA Audio Driver wangweidong.a
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 19+ messages in thread
From: wangweidong.a @ 2023-07-17 11:58 UTC (permalink / raw)
  To: lgirdwood, broonie, robh+dt, krzysztof.kozlowski+dt, conor+dt,
	perex, tiwai, rf, wangweidong.a, shumingf, herve.codina, flatmax,
	ckeepax, doug, fido_max, pierre-louis.bossart, kiseok.jo,
	liweilei, colin.i.king, trix, alsa-devel, devicetree,
	linux-kernel
  Cc: yijiangtao, zhangjianming

From: Weidong Wang <wangweidong.a@awinic.com>

The AW88261 is an I2S/TDM input, high efficiency
digital Smart K audio amplifier with an integrated 10.25V
smart boost convert

Signed-off-by: Weidong Wang <wangweidong.a@awinic.com>
---
 sound/soc/codecs/aw88261/aw88261.c | 539 +++++++++++++++++++++++++++++
 sound/soc/codecs/aw88261/aw88261.h |  52 +++
 2 files changed, 591 insertions(+)
 create mode 100644 sound/soc/codecs/aw88261/aw88261.c
 create mode 100644 sound/soc/codecs/aw88261/aw88261.h

diff --git a/sound/soc/codecs/aw88261/aw88261.c b/sound/soc/codecs/aw88261/aw88261.c
new file mode 100644
index 000000000000..14b072275822
--- /dev/null
+++ b/sound/soc/codecs/aw88261/aw88261.c
@@ -0,0 +1,539 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88261.c --  ALSA SoC AW88261 codec support
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Jimmy Zhang <zhangjianming@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "aw88261.h"
+#include "aw88261_device.h"
+#include "aw88261_lib.h"
+#include "aw88261_reg.h"
+
+static const struct regmap_config aw88261_remap_config = {
+	.val_bits = 16,
+	.reg_bits = 8,
+	.max_register = AW88261_REG_MAX - 1,
+	.reg_format_endian = REGMAP_ENDIAN_LITTLE,
+	.val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static void aw88261_start_pa(struct aw88261 *aw88261)
+{
+	int ret, i;
+
+	for (i = 0; i < AW88261_START_RETRIES; i++) {
+		ret = aw88261_dev_reg_update(aw88261->aw_pa, aw88261->aw_pa->phase_sync);
+		if (ret < 0) {
+			dev_err(aw88261->aw_pa->dev, "fw update failed, cnt:%d\n", i);
+			continue;
+		}
+		ret = aw88261_dev_start(aw88261->aw_pa);
+		if (ret) {
+			dev_err(aw88261->aw_pa->dev, "aw88261 device start failed. retry = %d", i);
+			continue;
+		} else {
+			dev_info(aw88261->aw_pa->dev, "start success\n");
+			break;
+		}
+	}
+}
+
+static void aw88261_startup_work(struct work_struct *work)
+{
+	struct aw88261 *aw88261 =
+		container_of(work, struct aw88261, start_work.work);
+
+	mutex_lock(&aw88261->lock);
+	aw88261_start_pa(aw88261);
+	mutex_unlock(&aw88261->lock);
+}
+
+static void aw88261_start(struct aw88261 *aw88261, bool sync_start)
+{
+	if (aw88261->aw_pa->fw_status != AW88261_DEV_FW_OK)
+		return;
+
+	if (aw88261->aw_pa->status == AW88261_DEV_PW_ON)
+		return;
+
+	if (sync_start == AW88261_SYNC_START)
+		aw88261_start_pa(aw88261);
+	else
+		queue_delayed_work(system_wq,
+			&aw88261->start_work,
+			AW88261_START_WORK_DELAY_MS);
+}
+
+static struct snd_soc_dai_driver aw88261_dai[] = {
+	{
+		.name = "aw88261-aif",
+		.id = 1,
+		.playback = {
+			.stream_name = "Speaker_Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = AW88261_RATES,
+			.formats = AW88261_FORMATS,
+		},
+		.capture = {
+			.stream_name = "Speaker_Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = AW88261_RATES,
+			.formats = AW88261_FORMATS,
+		},
+	},
+};
+
+static int aw88261_get_fade_in_time(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+	struct aw_device *aw_dev = aw88261->aw_pa;
+
+	ucontrol->value.integer.value[0] = aw_dev->fade_in_time;
+
+	return 0;
+}
+
+static int aw88261_set_fade_in_time(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct aw_device *aw_dev = aw88261->aw_pa;
+	int time;
+
+	time = ucontrol->value.integer.value[0];
+
+	if (time < mc->min || time > mc->max)
+		return -EINVAL;
+
+	if (time != aw_dev->fade_in_time) {
+		aw_dev->fade_in_time = time;
+		return 1;
+	}
+
+	return 0;
+}
+
+static int aw88261_get_fade_out_time(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+	struct aw_device *aw_dev = aw88261->aw_pa;
+
+	ucontrol->value.integer.value[0] = aw_dev->fade_out_time;
+
+	return 0;
+}
+
+static int aw88261_set_fade_out_time(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct aw_device *aw_dev = aw88261->aw_pa;
+	int time;
+
+	time = ucontrol->value.integer.value[0];
+	if (time < mc->min || time > mc->max)
+		return -EINVAL;
+
+	if (time != aw_dev->fade_out_time) {
+		aw_dev->fade_out_time = time;
+		return 1;
+	}
+
+	return 0;
+}
+
+static int aw88261_profile_info(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_info *uinfo)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+	const char *prof_name;
+	char *name;
+	int count;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+
+	count = aw88261_dev_get_profile_count(aw88261->aw_pa);
+	if (count <= 0) {
+		uinfo->value.enumerated.items = 0;
+		return 0;
+	}
+
+	uinfo->value.enumerated.items = count;
+
+	if (uinfo->value.enumerated.item >= count)
+		uinfo->value.enumerated.item = count - 1;
+
+	name = uinfo->value.enumerated.name;
+	count = uinfo->value.enumerated.item;
+
+	prof_name = aw88261_dev_get_prof_name(aw88261->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 aw88261_profile_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = aw88261_dev_get_profile_index(aw88261->aw_pa);
+
+	return 0;
+}
+
+static int aw88261_profile_set(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+	int ret;
+
+	/* pa stop or stopping just set profile */
+	mutex_lock(&aw88261->lock);
+	ret = aw88261_dev_set_profile_index(aw88261->aw_pa, ucontrol->value.integer.value[0]);
+	if (ret < 0) {
+		dev_dbg(codec->dev, "profile index does not change");
+		mutex_unlock(&aw88261->lock);
+		return 0;
+	}
+
+	if (aw88261->aw_pa->status) {
+		aw88261_dev_stop(aw88261->aw_pa);
+		aw88261_start(aw88261, AW88261_SYNC_START);
+	}
+
+	mutex_unlock(&aw88261->lock);
+
+	return 1;
+}
+
+static int aw88261_volume_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+	struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc;
+
+	ucontrol->value.integer.value[0] = vol_desc->ctl_volume;
+
+	return 0;
+}
+
+static int aw88261_volume_set(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+	struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc;
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	int value;
+
+	value = ucontrol->value.integer.value[0];
+
+	if (value < mc->min || value > mc->max)
+		return -EINVAL;
+
+	if (vol_desc->ctl_volume != value) {
+		vol_desc->ctl_volume = value;
+		aw88261_dev_set_volume(aw88261->aw_pa, vol_desc->ctl_volume);
+
+		return 1;
+	}
+
+	return 0;
+}
+
+static int aw88261_get_fade_step(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = aw88261->aw_pa->fade_step;
+
+	return 0;
+}
+
+static int aw88261_set_fade_step(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	int value;
+
+	value = ucontrol->value.integer.value[0];
+	if (value < mc->min || value > mc->max)
+		return -EINVAL;
+
+	if (aw88261->aw_pa->fade_step != value) {
+		aw88261->aw_pa->fade_step = value;
+		return 1;
+	}
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new aw88261_controls[] = {
+	SOC_SINGLE_EXT("PCM Playback Volume", AW88261_SYSCTRL2_REG,
+		6, AW88261_MUTE_VOL, 0, aw88261_volume_get,
+		aw88261_volume_set),
+	SOC_SINGLE_EXT("Fade Step", 0, 0, AW88261_MUTE_VOL, 0,
+		aw88261_get_fade_step, aw88261_set_fade_step),
+	SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+		aw88261_get_fade_in_time, aw88261_set_fade_in_time),
+	SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+		aw88261_get_fade_out_time, aw88261_set_fade_out_time),
+	AW88261_PROFILE_EXT("Profile Set", aw88261_profile_info,
+		aw88261_profile_get, aw88261_profile_set),
+};
+
+static int aw88261_playback_event(struct snd_soc_dapm_widget *w,
+				struct snd_kcontrol *k, int event)
+{
+	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+
+	mutex_lock(&aw88261->lock);
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		aw88261_start(aw88261, AW88261_ASYNC_START);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		aw88261_dev_stop(aw88261->aw_pa);
+		break;
+	default:
+		break;
+	}
+	mutex_unlock(&aw88261->lock);
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget aw88261_dapm_widgets[] = {
+	 /* playback */
+	SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, 0, 0, 0,
+					aw88261_playback_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 aw88261_audio_map[] = {
+	{"DAC Output", NULL, "AIF_RX"},
+	{"AIF_TX", NULL, "ADC Input"},
+};
+
+static int aw88261_request_firmware_file(struct aw88261 *aw88261)
+{
+	const struct firmware *cont = NULL;
+	int ret;
+
+	aw88261->aw_pa->fw_status = AW88261_DEV_FW_FAILED;
+
+	ret = request_firmware(&cont, AW88261_ACF_FILE, aw88261->aw_pa->dev);
+	if ((ret < 0) || (!cont)) {
+		dev_err(aw88261->aw_pa->dev, "load [%s] failed!", AW88261_ACF_FILE);
+		return ret;
+	}
+
+	dev_info(aw88261->aw_pa->dev, "loaded %s - size: %zu\n",
+			AW88261_ACF_FILE, cont ? cont->size : 0);
+
+	aw88261->aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL);
+	if (!aw88261->aw_cfg) {
+		release_firmware(cont);
+		return -ENOMEM;
+	}
+	aw88261->aw_cfg->len = (int)cont->size;
+	memcpy(aw88261->aw_cfg->data, cont->data, cont->size);
+	release_firmware(cont);
+
+	ret = aw88261_dev_load_acf_check(aw88261->aw_pa, aw88261->aw_cfg);
+	if (ret < 0) {
+		dev_err(aw88261->aw_pa->dev, "Load [%s] failed ....!", AW88261_ACF_FILE);
+		return ret;
+	}
+
+	dev_dbg(aw88261->aw_pa->dev, "%s : bin load success\n", __func__);
+
+	mutex_lock(&aw88261->lock);
+	/* aw device init */
+	ret = aw88261_dev_init(aw88261->aw_pa, aw88261->aw_cfg);
+	if (ret < 0)
+		dev_err(aw88261->aw_pa->dev, "dev init failed");
+	mutex_unlock(&aw88261->lock);
+
+	return ret;
+}
+
+static int aw88261_codec_probe(struct snd_soc_component *component)
+{
+	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+	int ret;
+
+	INIT_DELAYED_WORK(&aw88261->start_work, aw88261_startup_work);
+
+	ret = aw88261_request_firmware_file(aw88261);
+	if (ret < 0) {
+		dev_err(aw88261->aw_pa->dev, "aw88261_request_firmware_file failed\n");
+		return ret;
+	}
+
+	/* add widgets */
+	ret = snd_soc_dapm_new_controls(dapm, aw88261_dapm_widgets,
+							ARRAY_SIZE(aw88261_dapm_widgets));
+	if (ret < 0)
+		return ret;
+
+	/* add route */
+	ret = snd_soc_dapm_add_routes(dapm, aw88261_audio_map,
+							ARRAY_SIZE(aw88261_audio_map));
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_add_component_controls(component, aw88261_controls,
+							ARRAY_SIZE(aw88261_controls));
+
+	return ret;
+}
+
+static void aw88261_codec_remove(struct snd_soc_component *aw_codec)
+{
+	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(aw_codec);
+
+	cancel_delayed_work_sync(&aw88261->start_work);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_aw88261 = {
+	.probe = aw88261_codec_probe,
+	.remove = aw88261_codec_remove,
+};
+
+static struct aw88261 *aw88261_malloc_init(struct i2c_client *i2c)
+{
+	struct aw88261 *aw88261 = devm_kzalloc(&i2c->dev,
+			sizeof(struct aw88261), GFP_KERNEL);
+	if (!aw88261)
+		return NULL;
+
+	mutex_init(&aw88261->lock);
+
+	return aw88261;
+}
+
+static void aw88261_hw_reset(struct aw88261 *aw88261)
+{
+	gpiod_set_value_cansleep(aw88261->reset_gpio, 0);
+	usleep_range(AW88261_1000_US, AW88261_1000_US + 10);
+	gpiod_set_value_cansleep(aw88261->reset_gpio, 1);
+	usleep_range(AW88261_1000_US, AW88261_1000_US + 10);
+}
+
+static int aw88261_i2c_probe(struct i2c_client *i2c)
+{
+	struct aw88261 *aw88261;
+	int ret;
+
+	if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) {
+		dev_err(&i2c->dev, "check_functionality failed");
+		return -EIO;
+	}
+
+	aw88261 = aw88261_malloc_init(i2c);
+	if (!aw88261) {
+		dev_err(&i2c->dev, "malloc aw88261 failed");
+		return -ENOMEM;
+	}
+	i2c_set_clientdata(i2c, aw88261);
+
+	aw88261->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(aw88261->reset_gpio))
+		dev_info(&i2c->dev, "reset gpio not defined\n");
+	else
+		aw88261_hw_reset(aw88261);
+
+	aw88261->regmap = devm_regmap_init_i2c(i2c, &aw88261_remap_config);
+	if (IS_ERR(aw88261->regmap)) {
+		ret = PTR_ERR(aw88261->regmap);
+		dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
+		return ret;
+	}
+
+	/* aw pa init */
+	ret = aw88261_init(&aw88261->aw_pa, i2c, aw88261->regmap);
+	if (ret < 0)
+		return ret;
+
+	ret = devm_snd_soc_register_component(&i2c->dev,
+			&soc_codec_dev_aw88261,
+			aw88261_dai, ARRAY_SIZE(aw88261_dai));
+	if (ret < 0)
+		dev_err(&i2c->dev, "failed to register aw88261: %d", ret);
+
+	return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id aw88261_of_match[] = {
+	{ .compatible = "awinic,aw88261" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, aw88261_of_match);
+#endif
+
+static const struct i2c_device_id aw88261_i2c_id[] = {
+	{ AW88261_I2C_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, aw88261_i2c_id);
+
+static struct i2c_driver aw88261_i2c_driver = {
+	.driver = {
+		.name = AW88261_I2C_NAME,
+		.of_match_table = of_match_ptr(aw88261_of_match),
+	},
+	.probe_new = aw88261_i2c_probe,
+	.id_table = aw88261_i2c_id,
+};
+module_i2c_driver(aw88261_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AW88261 Smart PA Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88261/aw88261.h b/sound/soc/codecs/aw88261/aw88261.h
new file mode 100644
index 000000000000..c8b9805001cc
--- /dev/null
+++ b/sound/soc/codecs/aw88261/aw88261.h
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88261.h --  ALSA SoC AW88261 codec support
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Jimmy Zhang <zhangjianming@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW88261_H__
+#define __AW88261_H__
+
+#define AW88261_CHIP_ID_REG			(0x00)
+#define AW88261_START_RETRIES			(5)
+#define AW88261_START_WORK_DELAY_MS		(0)
+
+#define AW88261_I2C_NAME			"aw88261_smartpa"
+
+#define AW88261_RATES (SNDRV_PCM_RATE_8000_48000 | \
+			SNDRV_PCM_RATE_96000)
+#define AW88261_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 AW88261_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \
+{ \
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+	.name = xname, \
+	.info = profile_info, \
+	.get = profile_get, \
+	.put = profile_set, \
+}
+
+enum {
+	AW88261_SYNC_START = 0,
+	AW88261_ASYNC_START,
+};
+
+struct aw88261 {
+	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
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH V1 2/5] ASoC: codecs: aw88261 function for ALSA Audio Driver
  2023-07-17 11:58 [PATCH V1 0/5] ASoC: codecs: Add Awinic AW88261 audio amplifier driver wangweidong.a
  2023-07-17 11:58 ` [PATCH V1 1/5] ASoC: codecs: Add i2c and codec registration for aw88261 and their associated operation functions wangweidong.a
@ 2023-07-17 11:58 ` wangweidong.a
  2023-07-17 20:39   ` Krzysztof Kozlowski
  2023-07-17 11:58 ` [PATCH V1 3/5] ASoC: codecs: ACF bin parsing and check library file for aw88261 wangweidong.a
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 19+ messages in thread
From: wangweidong.a @ 2023-07-17 11:58 UTC (permalink / raw)
  To: lgirdwood, broonie, robh+dt, krzysztof.kozlowski+dt, conor+dt,
	perex, tiwai, rf, wangweidong.a, shumingf, herve.codina, flatmax,
	ckeepax, doug, fido_max, pierre-louis.bossart, kiseok.jo,
	liweilei, colin.i.king, trix, alsa-devel, devicetree,
	linux-kernel
  Cc: yijiangtao, zhangjianming

From: Weidong Wang <wangweidong.a@awinic.com>

The AW88261 is an I2S/TDM input, high efficiency
digital Smart K audio amplifier with an integrated 10.25V
smart boost convert

Signed-off-by: Weidong Wang <wangweidong.a@awinic.com>
---
 sound/soc/codecs/aw88261/aw88261_device.c | 959 ++++++++++++++++++++++
 sound/soc/codecs/aw88261/aw88261_device.h | 114 +++
 2 files changed, 1073 insertions(+)
 create mode 100644 sound/soc/codecs/aw88261/aw88261_device.c
 create mode 100644 sound/soc/codecs/aw88261/aw88261_device.h

diff --git a/sound/soc/codecs/aw88261/aw88261_device.c b/sound/soc/codecs/aw88261/aw88261_device.c
new file mode 100644
index 000000000000..855aba8a6364
--- /dev/null
+++ b/sound/soc/codecs/aw88261/aw88261_device.c
@@ -0,0 +1,959 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88261_device.c --  AW88261 function for ALSA Audio Driver
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Jimmy Zhang <zhangjianming@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/crc32.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include "aw88261_device.h"
+#include "aw88261_reg.h"
+
+static int aw_dev_read_chipid(struct aw_device *aw_dev, u16 *chip_id)
+{
+	int reg_val;
+	int ret;
+
+	ret = regmap_read(aw_dev->regmap, AW88261_CHIP_ID_REG, &reg_val);
+	if (ret) {
+		dev_err(aw_dev->dev, "%s read chipid error. ret = %d", __func__, ret);
+		return ret;
+	}
+
+	dev_info(aw_dev->dev, "chip id = %x\n", reg_val);
+	*chip_id = reg_val;
+
+	return ret;
+}
+
+static unsigned int reg_val_to_db(unsigned int value)
+{
+	return (((value >> AW88261_VOL_6DB_START) *
+				AW88261_VOLUME_STEP_DB) + (value & AW88261_REG_TO_DB));
+}
+
+static unsigned short db_to_reg_val(unsigned short value)
+{
+	return (((value / AW88261_VOLUME_STEP_DB) << AW88261_VOL_6DB_START) +
+			(value % AW88261_VOLUME_STEP_DB));
+}
+
+static int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value)
+{
+	struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+	unsigned int reg_value;
+	u16 real_value, volume;
+	int ret;
+
+	volume = min((value + vol_desc->init_volume), (unsigned int)AW88261_MUTE_VOL);
+	real_value = db_to_reg_val(volume);
+
+	/* cal real value */
+	ret = regmap_read(aw_dev->regmap, AW88261_SYSCTRL2_REG, &reg_value);
+	if (ret)
+		return ret;
+
+	real_value = (real_value << AW88261_VOL_START_BIT) | (reg_value & AW88261_VOL_START_MASK);
+
+	dev_dbg(aw_dev->dev, "value 0x%x , real_value:0x%x", value, real_value);
+
+	/* write value */
+	ret = regmap_write(aw_dev->regmap, AW88261_SYSCTRL2_REG, real_value);
+
+	return ret;
+}
+
+void aw88261_dev_set_volume(struct aw_device *aw_dev, unsigned short set_vol)
+{
+	int ret;
+
+	ret = aw_dev_set_volume(aw_dev, set_vol);
+	if (ret)
+		dev_dbg(aw_dev->dev, "set volume failed");
+}
+EXPORT_SYMBOL_GPL(aw88261_dev_set_volume);
+
+static void aw_dev_fade_in(struct aw_device *aw_dev)
+{
+	struct aw_volume_desc *desc = &aw_dev->volume_desc;
+	u16 fade_in_vol = desc->ctl_volume;
+	int fade_step = aw_dev->fade_step;
+	int i;
+
+	if (fade_step == 0 || aw_dev->fade_in_time == 0) {
+		aw_dev_set_volume(aw_dev, fade_in_vol);
+		return;
+	}
+
+	for (i = AW88261_MUTE_VOL; i >= fade_in_vol; i -= fade_step) {
+		aw_dev_set_volume(aw_dev, i);
+		usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10);
+	}
+
+	if (i != 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 = &aw_dev->volume_desc;
+	int fade_step = aw_dev->fade_step;
+	int i;
+
+	if (fade_step == 0 || aw_dev->fade_out_time == 0) {
+		aw_dev_set_volume(aw_dev, AW88261_MUTE_VOL);
+		return;
+	}
+
+	for (i = desc->ctl_volume; i <= AW88261_MUTE_VOL; i += fade_step) {
+		aw_dev_set_volume(aw_dev, i);
+		usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+	}
+
+	if (i != AW88261_MUTE_VOL) {
+		aw_dev_set_volume(aw_dev, AW88261_MUTE_VOL);
+		usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+	}
+}
+
+static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
+{
+	int ret;
+
+	if (flag) {
+		ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG,
+			~AW88261_I2STXEN_MASK, AW88261_I2STXEN_ENABLE_VALUE);
+	} else {
+		ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG,
+			~AW88261_I2STXEN_MASK, AW88261_I2STXEN_DISABLE_VALUE);
+	}
+	if (ret)
+		dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd)
+{
+	int ret;
+
+	if (pwd) {
+		ret = regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+				~AW88261_PWDN_MASK,	AW88261_PWDN_POWER_DOWN_VALUE);
+	} else {
+		ret = regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+				~AW88261_PWDN_MASK,	AW88261_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 = regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+				~AW88261_AMPPD_MASK, AW88261_AMPPD_POWER_DOWN_VALUE);
+	} else {
+		ret = regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+				~AW88261_AMPPD_MASK, AW88261_AMPPD_WORKING_VALUE);
+	}
+	if (ret)
+		dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+void aw88261_dev_mute(struct aw_device *aw_dev, bool is_mute)
+{
+	int ret;
+
+	if (is_mute) {
+		aw_dev_fade_out(aw_dev);
+		ret = regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+				~AW88261_HMUTE_MASK, AW88261_HMUTE_ENABLE_VALUE);
+	} else {
+		ret = regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+				~AW88261_HMUTE_MASK, AW88261_HMUTE_DISABLE_VALUE);
+		aw_dev_fade_in(aw_dev);
+	}
+	if (ret)
+		dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+EXPORT_SYMBOL_GPL(aw88261_dev_mute);
+
+static void aw88261_dev_uls_hmute(struct aw_device *aw_dev, bool uls_hmute)
+{
+	int ret;
+
+	if (uls_hmute) {
+		ret = regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+				~AW88261_ULS_HMUTE_MASK,
+				AW88261_ULS_HMUTE_ENABLE_VALUE);
+	} else {
+		ret = regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+				~AW88261_ULS_HMUTE_MASK,
+				AW88261_ULS_HMUTE_DISABLE_VALUE);
+	}
+	if (ret)
+		dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static int aw_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk)
+{
+	u16 reg_icalk, reg_icalkl;
+	unsigned int reg_val;
+	int ret;
+
+	ret = regmap_read(aw_dev->regmap, AW88261_EFRH4_REG, &reg_val);
+	if (ret)
+		return ret;
+
+	reg_icalk = reg_val & (~AW88261_EF_ISN_GESLP_H_MASK);
+
+	ret = regmap_read(aw_dev->regmap, AW88261_EFRL4_REG, &reg_val);
+	if (ret)
+		return ret;
+
+	reg_icalkl = reg_val & (~AW88261_EF_ISN_GESLP_L_MASK);
+
+	reg_icalk = (reg_icalk >> AW88261_ICALK_SHIFT) & (reg_icalkl >> AW88261_ICALKL_SHIFT);
+
+	if (reg_icalk & (~AW88261_EF_ISN_GESLP_SIGN_MASK))
+		reg_icalk = reg_icalk | ~AW88261_EF_ISN_GESLP_NEG;
+
+	*icalk = (int16_t)reg_icalk;
+
+	return ret;
+}
+
+static int aw_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk)
+{
+	u16 reg_vcalk, reg_vcalkl;
+	unsigned int reg_val;
+	int ret;
+
+	ret = regmap_read(aw_dev->regmap, AW88261_EFRH3_REG, &reg_val);
+	if (ret)
+		return ret;
+
+	reg_vcalk = (u16)reg_val & (~AW88261_EF_VSN_GESLP_H_MASK);
+
+	ret = regmap_read(aw_dev->regmap, AW88261_EFRL3_REG, &reg_val);
+	if (ret)
+		return ret;
+
+	reg_vcalkl = (u16)reg_val & (~AW88261_EF_VSN_GESLP_L_MASK);
+
+	reg_vcalk = (reg_vcalk >> AW88261_VCALK_SHIFT) & (reg_vcalkl >> AW88261_VCALKL_SHIFT);
+
+	if (reg_vcalk & AW88261_EF_VSN_GESLP_SIGN_MASK)
+		reg_vcalk = reg_vcalk | (~AW88261_EF_VSN_GESLP_NEG);
+	*vcalk = (int16_t)reg_vcalk;
+
+	return ret;
+}
+
+static int aw_dev_set_vcalb(struct aw_device *aw_dev)
+{
+	int16_t icalk_val, vcalk_val;
+	int icalk, vcalk, vcalb;
+	u32 reg_val;
+	int ret;
+
+	ret = aw_dev_get_icalk(aw_dev, &icalk_val);
+	if (ret) {
+		dev_err(aw_dev->dev, "%s:get icalk failed\n", __func__);
+		return ret;
+	}
+
+	ret = aw_dev_get_vcalk(aw_dev, &vcalk_val);
+	if (ret) {
+		dev_err(aw_dev->dev, "%s:get vcalk failed\n", __func__);
+		return ret;
+	}
+
+	icalk = AW88261_CABL_BASE_VALUE + AW88261_ICABLK_FACTOR * icalk_val;
+	vcalk = AW88261_CABL_BASE_VALUE + AW88261_VCABLK_FACTOR * vcalk_val;
+	if (!vcalk) {
+		dev_err(aw_dev->dev, "vcalk is 0");
+		return -EINVAL;
+	}
+
+	vcalb = AW88261_VCAL_FACTOR * icalk / vcalk;
+	reg_val = (unsigned int)vcalb;
+
+	dev_dbg(aw_dev->dev, "icalk=%d, vcalk=%d, vcalb=%d, reg_val=0x%04x",
+			icalk, vcalk, vcalb, reg_val);
+	ret = regmap_write(aw_dev->regmap, AW88261_VSNTM1_REG, reg_val);
+
+	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 = regmap_read(aw_dev->regmap, AW88261_SYSINT_REG, &reg_val);
+	if (ret)
+		dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret);
+	else
+		*int_status = reg_val;
+
+	dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", *int_status);
+}
+
+static void 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);
+}
+
+static int aw_dev_get_iis_status(struct aw_device *aw_dev)
+{
+	unsigned int reg_val;
+	int ret;
+
+	ret = regmap_read(aw_dev->regmap, AW88261_SYSST_REG, &reg_val);
+	if (ret)
+		return -EIO;
+	if ((reg_val & AW88261_BIT_PLL_CHECK) != AW88261_BIT_PLL_CHECK) {
+		dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static int aw_dev_check_mode1_pll(struct aw_device *aw_dev)
+{
+	int ret, i;
+
+	for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
+		ret = aw_dev_get_iis_status(aw_dev);
+		if (ret < 0) {
+			dev_err(aw_dev->dev, "mode1 iis signal check error");
+			usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+		} else {
+			return ret;
+		}
+	}
+
+	return -EPERM;
+}
+
+static int aw_dev_check_mode2_pll(struct aw_device *aw_dev)
+{
+	unsigned int reg_val;
+	int ret, i;
+
+	ret = regmap_read(aw_dev->regmap, AW88261_PLLCTRL1_REG, &reg_val);
+	if (ret)
+		return ret;
+
+	reg_val &= (~AW88261_CCO_MUX_MASK);
+	if (reg_val == AW88261_CCO_MUX_DIVIDED_VALUE) {
+		dev_dbg(aw_dev->dev, "CCO_MUX is already divider");
+		return -EPERM;
+	}
+
+	/* change mode2 */
+	ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG,
+			~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_DIVIDED_VALUE);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
+		ret = aw_dev_get_iis_status(aw_dev);
+		if (ret) {
+			dev_err(aw_dev->dev, "mode2 iis signal check error");
+			usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+		} else {
+			break;
+		}
+	}
+
+	/* change mode1 */
+	ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG,
+			~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_BYPASS_VALUE);
+	if (ret == 0) {
+		usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+		for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
+			ret = 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(AW88261_2000_US, AW88261_2000_US + 10);
+			} else {
+				break;
+			}
+		}
+	}
+
+	return ret;
+}
+
+static int aw_dev_check_syspll(struct aw_device *aw_dev)
+{
+	int ret;
+
+	ret = aw_dev_check_mode1_pll(aw_dev);
+	if (ret) {
+		dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check");
+		ret = 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 = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
+		ret = regmap_read(aw_dev->regmap, AW88261_SYSST_REG, &reg_val);
+		if (ret)
+			return ret;
+
+		check_val = reg_val & (~AW88261_BIT_SYSST_CHECK_MASK)
+							& AW88261_BIT_SYSST_CHECK;
+		if (check_val != AW88261_BIT_SYSST_CHECK) {
+			dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x",
+				i, reg_val, AW88261_BIT_SYSST_CHECK);
+			usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+		} else {
+			return 0;
+		}
+	}
+
+	return -EPERM;
+}
+
+static int aw_dev_update_reg_container(struct aw_device *aw_dev,
+				unsigned char *data, unsigned int len)
+{
+	struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+	unsigned int read_val, efcheck_val;
+	int16_t *reg_data;
+	int data_len;
+	u16 read_vol;
+	u16 reg_val;
+	u8 reg_addr;
+	int i, ret;
+
+	reg_data = (int16_t *)data;
+	data_len = len >> 1;
+
+	if (data_len & 0x1) {
+		dev_err(aw_dev->dev, "data len:%d unsupported",	data_len);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < data_len; i += 2) {
+		reg_addr = reg_data[i];
+		reg_val = reg_data[i + 1];
+
+		if (reg_addr == AW88261_SYSCTRL_REG) {
+			aw_dev->amppd_st = reg_val & (~AW88261_AMPPD_MASK);
+			ret = regmap_read(aw_dev->regmap, reg_addr, &read_val);
+			if (ret)
+				break;
+
+			read_val &= (~AW88261_AMPPD_MASK) | (~AW88261_PWDN_MASK) |
+								(~AW88261_HMUTE_MASK);
+			reg_val &= (AW88261_AMPPD_MASK | AW88261_PWDN_MASK | AW88261_HMUTE_MASK);
+			reg_val |= read_val;
+
+			/* enable uls hmute */
+			reg_val &= AW88261_ULS_HMUTE_MASK;
+			reg_val |= AW88261_ULS_HMUTE_ENABLE_VALUE;
+		}
+
+		if (reg_addr == AW88261_DBGCTRL_REG) {
+			efcheck_val = reg_val & (~AW88261_EF_DBMD_MASK);
+			if (efcheck_val == AW88261_OR_VALUE)
+				aw_dev->efuse_check = AW_EF_OR_CHECK;
+			else
+				aw_dev->efuse_check = AW_EF_AND_CHECK;
+
+		}
+
+		/* i2stxen */
+		if (reg_addr == AW88261_I2SCTRL3_REG) {
+			/* close tx */
+			reg_val &= AW88261_I2STXEN_MASK;
+			reg_val |= AW88261_I2STXEN_DISABLE_VALUE;
+		}
+
+		if (reg_addr == AW88261_SYSCTRL2_REG) {
+			read_vol = (reg_val & (~AW88261_VOL_MASK)) >>
+				AW88261_VOL_START_BIT;
+			aw_dev->volume_desc.init_volume =
+				reg_val_to_db(read_vol);
+		}
+
+		if (reg_addr == AW88261_VSNTM1_REG)
+			continue;
+
+		dev_dbg(aw_dev->dev, "reg=0x%04x, val = 0x%04x",
+				(uint16_t)reg_addr, (uint16_t)reg_val);
+
+		ret = regmap_write(aw_dev->regmap, reg_addr, reg_val);
+		if (ret)
+			break;
+
+	}
+
+	ret = aw_dev_set_vcalb(aw_dev);
+	if (ret)
+		return ret;
+
+	if (aw_dev->prof_cur != aw_dev->prof_index)
+		vol_desc->ctl_volume = 0;
+
+	/* keep min volume */
+	aw_dev_set_volume(aw_dev, vol_desc->mute_volume);
+
+	return ret;
+}
+
+static int aw_dev_reg_update(struct aw_device *aw_dev,
+					unsigned char *data, unsigned int len)
+{
+	int ret;
+
+	if (!len || !data) {
+		dev_err(aw_dev->dev, "reg data is null or len is 0");
+		return -EINVAL;
+	}
+
+	ret = aw_dev_update_reg_container(aw_dev, data, len);
+	if (ret)
+		dev_err(aw_dev->dev, "reg update failed");
+
+	return ret;
+}
+
+static void aw88261_dev_soft_reset(struct aw_device *aw_dev)
+{
+	int ret;
+
+	ret = regmap_write(aw_dev->regmap, AW88261_ID_REG, AW88261_SOFT_RESET_VALUE);
+	if (ret)
+		dev_dbg(aw_dev->dev, "%s failed\n", __func__);
+}
+
+static int aw_frcset_check(struct aw_device *aw_dev)
+{
+	unsigned int reg_val, ret;
+	u16 temh, teml, tem;
+
+	ret = regmap_read(aw_dev->regmap, AW88261_EFRH3_REG, &reg_val);
+	if (ret)
+		return ret;
+	temh = ((u16)reg_val & (~AW88261_TEMH_MASK));
+
+	ret = regmap_read(aw_dev->regmap, AW88261_EFRL3_REG, &reg_val);
+	if (ret)
+		return ret;
+	teml = ((u16)reg_val & (~AW88261_TEML_MASK));
+
+	if (aw_dev->efuse_check == AW_EF_OR_CHECK)
+		tem = (temh | teml);
+	else
+		tem = (temh & teml);
+
+	if (tem == AW88261_DEFAULT_CFG)
+		aw_dev->frcset_en = AW_FRCSET_ENABLE;
+	else
+		aw_dev->frcset_en = AW_FRCSET_DISABLE;
+
+	dev_dbg(aw_dev->dev, "tem is 0x%04x, frcset_en is %d",
+						tem, aw_dev->frcset_en);
+
+	return ret;
+}
+
+static void aw88261_reg_force_set(struct aw_device *aw_dev)
+{
+
+	if (aw_dev->frcset_en == AW_FRCSET_ENABLE) {
+		/* set FORCE_PWM */
+		regmap_update_bits(aw_dev->regmap, AW88261_BSTCTRL3_REG,
+				AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE);
+		/* set BOOST_OS_WIDTH */
+		regmap_update_bits(aw_dev->regmap, AW88261_BSTCTRL5_REG,
+				AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE);
+		/* set BURST_LOOPR */
+		regmap_update_bits(aw_dev->regmap, AW88261_BSTCTRL6_REG,
+				AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE);
+		/* set RSQN_DLY */
+		regmap_update_bits(aw_dev->regmap, AW88261_BSTCTRL7_REG,
+				AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE);
+		/* set BURST_SSMODE */
+		regmap_update_bits(aw_dev->regmap, AW88261_BSTCTRL8_REG,
+				AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE);
+		/* set BST_BURST */
+		regmap_update_bits(aw_dev->regmap, AW88261_BSTCTRL9_REG,
+				AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE);
+	} else {
+		dev_dbg(aw_dev->dev, "needn't set reg value");
+	}
+}
+
+int aw88261_dev_fw_update(struct aw_device *aw_dev)
+{
+	struct aw_prof_desc *prof_index_desc;
+	struct aw_sec_data_desc *sec_desc;
+	char *prof_name;
+	int ret;
+
+	prof_name = aw88261_dev_get_prof_name(aw_dev, aw_dev->prof_index);
+	if (!prof_name) {
+		dev_err(aw_dev->dev, "get prof name failed");
+		return -EINVAL;
+	}
+
+	dev_dbg(aw_dev->dev, "start update %s", prof_name);
+
+	ret = aw88261_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc);
+	if (ret)
+		return ret;
+
+	/* update reg */
+	sec_desc = prof_index_desc->sec_desc;
+	ret = aw_dev_reg_update(aw_dev, sec_desc[AW88261_DATA_TYPE_REG].data,
+					sec_desc[AW88261_DATA_TYPE_REG].len);
+	if (ret) {
+		dev_err(aw_dev->dev, "update reg failed");
+		return ret;
+	}
+
+	aw_dev->prof_cur = aw_dev->prof_index;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(aw88261_dev_fw_update);
+
+int aw88261_dev_reg_update(struct aw_device *aw_dev, bool force)
+{
+	int ret;
+
+	if (force) {
+		aw88261_dev_soft_reset(aw_dev);
+		ret = aw88261_dev_fw_update(aw_dev);
+		if (ret < 0)
+			return ret;
+	} else {
+		if (aw_dev->prof_cur != aw_dev->prof_index) {
+			ret = aw88261_dev_fw_update(aw_dev);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	aw_dev->prof_cur = aw_dev->prof_index;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(aw88261_dev_reg_update);
+
+int aw88261_dev_start(struct aw_device *aw_dev)
+{
+	int ret;
+
+	if (aw_dev->status == AW88261_DEV_PW_ON) {
+		dev_info(aw_dev->dev, "already power on");
+		return 0;
+	}
+
+	/* power on */
+	aw_dev_pwd(aw_dev, false);
+	usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+
+	ret = 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(AW88261_1000_US, AW88261_1000_US + 50);
+
+	/* check i2s status */
+	ret = aw_dev_check_sysst(aw_dev);
+	if (ret) {
+		dev_err(aw_dev->dev, "sysst check failed");
+		goto sysst_check_fail;
+	}
+
+	/* enable tx feedback */
+	aw_dev_i2s_tx_enable(aw_dev, true);
+
+	if (aw_dev->amppd_st)
+		aw_dev_amppd(aw_dev, true);
+
+	aw88261_reg_force_set(aw_dev);
+
+	/* close uls mute */
+	aw88261_dev_uls_hmute(aw_dev, false);
+
+	/* close mute */
+	if (!aw_dev->mute_st)
+		aw88261_dev_mute(aw_dev, false);
+
+	/* clear inturrupt */
+	aw_dev_clear_int_status(aw_dev);
+	aw_dev->status = AW88261_DEV_PW_ON;
+
+	return 0;
+
+sysst_check_fail:
+	aw_dev_i2s_tx_enable(aw_dev, false);
+	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 = AW88261_DEV_PW_OFF;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(aw88261_dev_start);
+
+int aw88261_dev_stop(struct aw_device *aw_dev)
+{
+	if (aw_dev->status == AW88261_DEV_PW_OFF) {
+		dev_info(aw_dev->dev, "already power off");
+		return 0;
+	}
+
+	aw_dev->status = AW88261_DEV_PW_OFF;
+
+	/* clear inturrupt */
+	aw_dev_clear_int_status(aw_dev);
+
+	aw88261_dev_uls_hmute(aw_dev, true);
+	/* set mute */
+	aw88261_dev_mute(aw_dev, true);
+
+	/* close tx feedback */
+	aw_dev_i2s_tx_enable(aw_dev, false);
+	usleep_range(AW88261_1000_US, AW88261_1000_US + 100);
+
+	/* enable amppd */
+	aw_dev_amppd(aw_dev, true);
+
+	/* set power down */
+	aw_dev_pwd(aw_dev, true);
+
+	dev_dbg(aw_dev->dev, "pa stop success\n");
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(aw88261_dev_stop);
+
+int aw88261_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 = aw88261_dev_cfg_load(aw_dev, aw_cfg);
+	if (ret) {
+		dev_err(aw_dev->dev, "aw_dev acf parse failed");
+		return -EINVAL;
+	}
+
+	aw88261_dev_soft_reset(aw_dev);
+	aw_dev->fade_in_time = AW88261_1000_US / 10;
+	aw_dev->fade_out_time = AW88261_1000_US >> 1;
+	aw_dev->prof_cur = AW_INIT_PROFILE;
+	aw_dev->prof_index = AW_INIT_PROFILE;
+
+	ret = aw88261_dev_fw_update(aw_dev);
+	if (ret) {
+		dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret);
+		return ret;
+	}
+
+	ret = aw_frcset_check(aw_dev);
+	if (ret) {
+		dev_err(aw_dev->dev, "aw_frcset_check ret = %d\n", ret);
+		return ret;
+	}
+
+	aw_dev_clear_int_status(aw_dev);
+
+	aw88261_dev_uls_hmute(aw_dev, true);
+
+	aw88261_dev_mute(aw_dev, true);
+
+	aw_dev_i2s_tx_enable(aw_dev, false);
+
+	usleep_range(AW88261_1000_US, AW88261_1000_US + 100);
+
+	aw_dev_amppd(aw_dev, true);
+
+	aw_dev_pwd(aw_dev, true);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(aw88261_dev_init);
+
+static void aw_parse_channel_dt(struct aw_device *aw_dev)
+{
+	struct device_node *np = aw_dev->dev->of_node;
+	u32 channel_value;
+	u32 sync_enable;
+	int ret;
+
+	ret = of_property_read_u32(np, "sound-channel", &channel_value);
+	if (ret)
+		channel_value = AW88261_DEV_DEFAULT_CH;
+
+	ret = of_property_read_u32(np, "sync-flag", &sync_enable);
+	if (ret)
+		sync_enable = false;
+
+	dev_dbg(aw_dev->dev,  "sync flag is %d", sync_enable);
+	dev_dbg(aw_dev->dev, "read sound-channel value is: %d", channel_value);
+
+	aw_dev->channel = channel_value;
+	aw_dev->phase_sync = sync_enable;
+}
+
+static int aw_dev_init(struct aw_device *aw_dev)
+{
+	aw_dev->chip_id = AW88261_CHIP_ID;
+	/* call aw device init func */
+	aw_dev->acf = NULL;
+	aw_dev->prof_info.prof_desc = NULL;
+	aw_dev->prof_info.count = 0;
+	aw_dev->prof_info.prof_type = AW88261_DEV_NONE_TYPE_ID;
+	aw_dev->channel = 0;
+	aw_dev->fw_status = AW88261_DEV_FW_FAILED;
+
+	aw_dev->fade_step = AW88261_VOLUME_STEP_DB;
+	aw_dev->volume_desc.ctl_volume = AW88261_VOL_DEFAULT_VALUE;
+	aw_dev->volume_desc.mute_volume = AW88261_MUTE_VOL;
+	aw_parse_channel_dt(aw_dev);
+
+	return 0;
+}
+
+int aw88261_dev_get_profile_count(struct aw_device *aw_dev)
+{
+	return aw_dev->prof_info.count;
+}
+EXPORT_SYMBOL_GPL(aw88261_dev_get_profile_count);
+
+int aw88261_dev_get_profile_index(struct aw_device *aw_dev)
+{
+	return aw_dev->prof_index;
+}
+EXPORT_SYMBOL_GPL(aw88261_dev_get_profile_index);
+
+int aw88261_dev_set_profile_index(struct aw_device *aw_dev, int index)
+{
+	/* check the index whether is valid */
+	if ((index >= aw_dev->prof_info.count) || (index < 0))
+		return -EINVAL;
+	/* check the index whether change */
+	if (aw_dev->prof_index == index)
+		return -EINVAL;
+
+	aw_dev->prof_index = 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(aw88261_dev_set_profile_index);
+
+char *aw88261_dev_get_prof_name(struct aw_device *aw_dev, int index)
+{
+	struct aw_prof_info *prof_info = &aw_dev->prof_info;
+	struct aw_prof_desc *prof_desc;
+
+	if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+		dev_err(aw_dev->dev, "index[%d] overflow count[%d]",
+			index, aw_dev->prof_info.count);
+		return NULL;
+	}
+
+	prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+	return prof_info->prof_name_list[prof_desc->id];
+}
+EXPORT_SYMBOL_GPL(aw88261_dev_get_prof_name);
+
+int aw88261_dev_get_prof_data(struct aw_device *aw_dev, int index,
+			struct aw_prof_desc **prof_desc)
+{
+	if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+		dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n",
+				__func__, index, aw_dev->prof_info.count);
+		return -EINVAL;
+	}
+
+	*prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(aw88261_dev_get_prof_data);
+
+int aw88261_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 = devm_kzalloc(&i2c->dev, sizeof(struct aw_device), GFP_KERNEL);
+		if (!(*aw_dev))
+			return -ENOMEM;
+	}
+
+	(*aw_dev)->i2c = i2c;
+	(*aw_dev)->dev = &i2c->dev;
+	(*aw_dev)->regmap = regmap;
+
+	/* read chip id */
+	ret = aw_dev_read_chipid((*aw_dev), &chip_id);
+	if (ret) {
+		dev_err(&i2c->dev, "dev_read_chipid failed ret=%d", ret);
+		return ret;
+	}
+
+	switch (chip_id) {
+	case AW88261_CHIP_ID:
+		ret = aw_dev_init((*aw_dev));
+		break;
+	default:
+		ret = -EINVAL;
+		dev_err((*aw_dev)->dev, "unsupported device");
+		break;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(aw88261_init);
+
+MODULE_DESCRIPTION("AW88261 device lib");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88261/aw88261_device.h b/sound/soc/codecs/aw88261/aw88261_device.h
new file mode 100644
index 000000000000..56baccd1764f
--- /dev/null
+++ b/sound/soc/codecs/aw88261/aw88261_device.h
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88261_device.h --  AW88261 function for ALSA Audio Driver
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Jimmy Zhang <zhangjianming@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW88261_DEVICE_FILE_H__
+#define __AW88261_DEVICE_FILE_H__
+
+#include "aw88261.h"
+#include "aw88261_data_type.h"
+#include "aw88261_lib.h"
+
+#define AW88261_DEV_DEFAULT_CH				(0)
+#define AW88261_ACF_FILE				"aw88261_acf.bin"
+#define AW88261_DEV_SYSST_CHECK_MAX			(10)
+#define AW88261_SOFT_RESET_VALUE			(0x55aa)
+#define AW88261_REG_TO_DB				(0x3f)
+#define AW88261_VOL_START_MASK				(0xfc00)
+
+enum {
+	AW88261_1000_US = 1000,
+	AW88261_2000_US = 2000,
+	AW88261_3000_US = 3000,
+	AW88261_4000_US = 4000,
+	AW88261_5000_US = 5000,
+	AW88261_10000_US = 10000,
+	AW88261_100000_US = 100000,
+};
+
+enum {
+	AW88261_DEV_TYPE_OK = 0,
+	AW88261_DEV_TYPE_NONE = 1,
+};
+
+
+enum AW88261_DEV_STATUS {
+	AW88261_DEV_PW_OFF = 0,
+	AW88261_DEV_PW_ON,
+};
+
+enum AW88261_DEV_FW_STATUS {
+	AW88261_DEV_FW_FAILED = 0,
+	AW88261_DEV_FW_OK,
+};
+
+enum {
+	AW_EF_AND_CHECK = 0,
+	AW_EF_OR_CHECK,
+};
+
+enum {
+	AW_FRCSET_DISABLE = 0,
+	AW_FRCSET_ENABLE,
+};
+
+struct aw_volume_desc {
+	unsigned int init_volume;
+	unsigned int mute_volume;
+	unsigned int ctl_volume;
+	unsigned int max_volume;
+};
+
+struct aw_container {
+	int len;
+	u8 data[];
+};
+
+struct aw_device {
+	u8 fw_status;
+	unsigned char prof_cur;
+	unsigned char prof_index;
+	unsigned char phase_sync;
+	u16 chip_id;
+
+	char *acf;
+	int status;
+	int efuse_check;
+	int frcset_en;
+	unsigned int mute_st;
+	unsigned int amppd_st;
+	unsigned int channel;
+	unsigned int fade_step;
+	unsigned int fade_in_time;
+	unsigned int fade_out_time;
+
+	struct i2c_client *i2c;
+	struct device *dev;
+	struct regmap *regmap;
+	struct aw_prof_info prof_info;
+	struct aw_volume_desc volume_desc;
+};
+
+int aw88261_init(struct aw_device **aw_dev, struct i2c_client *i2c, struct regmap *regmap);
+int aw88261_dev_init(struct aw_device *aw_dev, struct aw_container *aw_cfg);
+int aw88261_dev_start(struct aw_device *aw_dev);
+int aw88261_dev_stop(struct aw_device *aw_dev);
+int aw88261_dev_fw_update(struct aw_device *aw_dev);
+int aw88261_dev_reg_update(struct aw_device *aw_dev, bool force);
+void aw88261_dev_set_volume(struct aw_device *aw_dev, unsigned short set_vol);
+int aw88261_dev_get_prof_data(struct aw_device *aw_dev, int index,
+			struct aw_prof_desc **prof_desc);
+char *aw88261_dev_get_prof_name(struct aw_device *aw_dev, int index);
+int aw88261_dev_set_profile_index(struct aw_device *aw_dev, int index);
+int aw88261_dev_get_profile_index(struct aw_device *aw_dev);
+int aw88261_dev_get_profile_count(struct aw_device *aw_dev);
+int aw88261_dev_load_acf_check(struct aw_device *aw_dev, struct aw_container *aw_cfg);
+int aw88261_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg);
+void aw88261_dev_mute(struct aw_device *aw_dev, bool is_mute);
+#endif
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH V1 3/5] ASoC: codecs: ACF bin parsing and check library file for aw88261
  2023-07-17 11:58 [PATCH V1 0/5] ASoC: codecs: Add Awinic AW88261 audio amplifier driver wangweidong.a
  2023-07-17 11:58 ` [PATCH V1 1/5] ASoC: codecs: Add i2c and codec registration for aw88261 and their associated operation functions wangweidong.a
  2023-07-17 11:58 ` [PATCH V1 2/5] ASoC: codecs: aw88261 function for ALSA Audio Driver wangweidong.a
@ 2023-07-17 11:58 ` wangweidong.a
  2023-07-17 20:40   ` Krzysztof Kozlowski
  2023-07-17 11:58 ` [PATCH V1 4/5] ASoC: codecs: aw88261 chip register file, data type file and Kconfig Makefile wangweidong.a
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 19+ messages in thread
From: wangweidong.a @ 2023-07-17 11:58 UTC (permalink / raw)
  To: lgirdwood, broonie, robh+dt, krzysztof.kozlowski+dt, conor+dt,
	perex, tiwai, rf, wangweidong.a, shumingf, herve.codina, flatmax,
	ckeepax, doug, fido_max, pierre-louis.bossart, kiseok.jo,
	liweilei, colin.i.king, trix, alsa-devel, devicetree,
	linux-kernel
  Cc: yijiangtao, zhangjianming

From: Weidong Wang <wangweidong.a@awinic.com>

The AW88261 is an I2S/TDM input, high efficiency
digital Smart K audio amplifier with an integrated 10.25V
smart boost convert

Signed-off-by: Weidong Wang <wangweidong.a@awinic.com>
---
 sound/soc/codecs/aw88261/aw88261_lib.c | 997 +++++++++++++++++++++++++
 sound/soc/codecs/aw88261/aw88261_lib.h |  91 +++
 2 files changed, 1088 insertions(+)
 create mode 100644 sound/soc/codecs/aw88261/aw88261_lib.c
 create mode 100644 sound/soc/codecs/aw88261/aw88261_lib.h

diff --git a/sound/soc/codecs/aw88261/aw88261_lib.c b/sound/soc/codecs/aw88261/aw88261_lib.c
new file mode 100644
index 000000000000..b8f00708dacf
--- /dev/null
+++ b/sound/soc/codecs/aw88261/aw88261_lib.c
@@ -0,0 +1,997 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88261_lib.c  -- ACF bin parsing and check library file for aw88261
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Jimmy Zhang <zhangjianming@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/crc8.h>
+#include <linux/i2c.h>
+#include "aw88261_lib.h"
+#include "aw88261_device.h"
+
+#define AW88261_CRC8_POLYNOMIAL 0x8C
+DECLARE_CRC8_TABLE(aw_crc8_table);
+
+static char *profile_name[AW88261_PROFILE_MAX] = {
+	"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 *bin);
+
+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 = 0;
+	unsigned int check_sum;
+	unsigned int i, len;
+
+	p_check_sum = &(bin->info.data[(bin->header_info[bin_num].valid_data_addr -
+						bin->header_info[bin_num].header_len)]);
+	len = bin->header_info[bin_num].bin_data_len + bin->header_info[bin_num].header_len;
+	check_sum = le32_to_cpup((void *)p_check_sum);
+
+	for (i = 4; i < len; i++)
+		sum_data += *(p_check_sum + i);
+
+	dev_dbg(aw_dev->dev, "%s -- check_sum = %p, check_sum = 0x%x, sum_data = 0x%x",
+					__func__, p_check_sum, check_sum, sum_data);
+	if (sum_data != check_sum) {
+		dev_err(aw_dev->dev, "%s. CheckSum Fail.bin_num=%d, CheckSum:0x%x, SumData: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 = bin->header_info[bin_num];
+	unsigned int check_register_num, parse_register_num;
+	unsigned char *p_check_sum;
+
+	p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]);
+
+	parse_register_num = le32_to_cpup((void *)p_check_sum);
+	check_register_num = (bin->header_info[bin_num].bin_data_len - CHECK_REGISTER_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 = 0x%x,check_register_num = 0x%x\n",
+				__func__, parse_register_num, check_register_num);
+	if (parse_register_num != check_register_num) {
+		dev_err(aw_dev->dev, "%s parse_register_num = 0x%x,check_register_num = 0x%x\n",
+				__func__, parse_register_num, check_register_num);
+		return -EINVAL;
+	}
+
+	bin->header_info[bin_num].reg_num = parse_register_num;
+	bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - VALID_DATA_LEN;
+	bin->header_info[bin_num].valid_data_addr = 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 *bin, int bin_num)
+{
+	struct bin_header_info temp_info = bin->header_info[bin_num];
+	unsigned int check_dsp_reg_num, parse_dsp_reg_num;
+	unsigned char *p_check_sum;
+
+	p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]);
+
+	parse_dsp_reg_num = le32_to_cpup((void *)(p_check_sum + PARSE_DSP_REG_NUM));
+	bin->header_info[bin_num].reg_data_byte_len =
+			le32_to_cpup((void *)(p_check_sum + REG_DATA_BYTP_LEN));
+	check_dsp_reg_num = (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 = %d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x",
+					__func__, bin_num, check_dsp_reg_num, check_dsp_reg_num);
+	if (parse_dsp_reg_num != check_dsp_reg_num) {
+		dev_err(aw_dev->dev, "aw_bin_parse check dsp reg num error\n");
+		dev_err(aw_dev->dev, "%s parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x",
+					__func__, check_dsp_reg_num, check_dsp_reg_num);
+		return -EINVAL;
+	}
+
+	bin->header_info[bin_num].download_addr = le32_to_cpup((void *)p_check_sum);
+	bin->header_info[bin_num].reg_num = parse_dsp_reg_num;
+	bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - DSP_VALID_DATA_LEN;
+	bin->header_info[bin_num].valid_data_addr = 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 *bin, int bin_num)
+{
+	struct bin_header_info temp_info = bin->header_info[bin_num];
+	unsigned int check_soc_app_num, parse_soc_app_num;
+	unsigned char *p_check_sum;
+
+	p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]);
+
+	bin->header_info[bin_num].app_version = le32_to_cpup((void *)p_check_sum);
+	parse_soc_app_num = le32_to_cpup((void *)(p_check_sum + PARSE_SOC_APP_NUM));
+	check_soc_app_num = bin->header_info[bin_num].bin_data_len - CHECK_SOC_APP_NUM;
+	dev_dbg(aw_dev->dev, "%s bin_num = %d, parse_soc_app_num=0x%x, check_soc_app_num = 0x%x\n",
+					__func__, bin_num, parse_soc_app_num, check_soc_app_num);
+	if (parse_soc_app_num != check_soc_app_num) {
+		dev_err(aw_dev->dev, "%s parse_soc_app_num=0x%x, check_soc_app_num = 0x%x\n",
+					__func__, parse_soc_app_num, check_soc_app_num);
+		return -EINVAL;
+	}
+
+	bin->header_info[bin_num].reg_num = parse_soc_app_num;
+	bin->header_info[bin_num].download_addr = le32_to_cpup((void *)(p_check_sum +
+								APP_DOWNLOAD_ADDR));
+	bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - APP_VALID_DATA_LEN;
+	bin->header_info[bin_num].valid_data_addr = 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, DATA_LEN);
+
+	bin->header_info[bin->all_bin_parse_num].header_len = HEADER_LEN;
+	bin->all_bin_parse_num += 1;
+}
+
+static int aw_parse_one_of_multi_bins(struct aw_device *aw_dev, unsigned int 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 = bin->header_info[bin->all_bin_parse_num - 1];
+	if (!bin_serial_num) {
+		bin_start_addr = le32_to_cpup((void *)(bin->p_addr + START_ADDR_OFFSET));
+		bin->p_addr += (HEADER_LEN + bin_start_addr);
+		bin->header_info[bin->all_bin_parse_num].valid_data_addr =
+			aw_bin_header_info.valid_data_addr + VALID_DATA_ADDR + 8 * bin_num +
+			VALID_DATA_ADDR_OFFSET;
+	} else {
+		valid_data_len = aw_bin_header_info.bin_data_len;
+		bin->p_addr += (HDADER_LEN + valid_data_len);
+		bin->header_info[bin->all_bin_parse_num].valid_data_addr =
+		    aw_bin_header_info.valid_data_addr + aw_bin_header_info.bin_data_len +
+		    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 = le32_to_cpup((void *)(bin->p_addr + VALID_DATA_ADDR_OFFSET));
+	if (bin->multi_bin_parse_num == 1)
+		bin->header_info[bin->all_bin_parse_num].valid_data_addr =
+							VALID_DATA_ADDR_OFFSET;
+
+	aw_get_single_bin_header(bin);
+
+	for (i = 0; i < bin_num; i++) {
+		dev_dbg(aw_dev->dev, "aw_bin_parse enter multi bin for is %d\n", i);
+		ret = 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 *bin)
+{
+	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 = le32_to_cpup((void *)(bin->p_addr + BIN_DATA_TYPE_OFFSET));
+	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 += 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 =
+								VALID_DATA_ADDR_OFFSET;
+		aw_get_single_bin_header(bin);
+		return 0;
+	case DATA_TYPE_MULTI_BINS:
+		bin->multi_bin_parse_num += 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 = le32_to_cpup((void *)(bin->p_addr + HEADER_VERSION_OFFSET));
+	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 *bin)
+{
+	int ret = -EINVAL;
+	int i;
+
+	if (!bin) {
+		dev_err(aw_dev->dev, "aw_bin_parse bin is NULL\n");
+		return ret;
+	}
+	bin->p_addr = bin->info.data;
+	bin->all_bin_parse_num = 0;
+	bin->multi_bin_parse_num = 0;
+	bin->single_bin_parse_num = 0;
+
+	ret = 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 = 0; i < bin->all_bin_parse_num; i++) {
+		ret = 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 = 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 == DATA_VERSION_V1) {
+			switch (bin->header_info[i].bin_data_type) {
+			case DATA_TYPE_REGISTER:
+				ret = aw_check_register_num(aw_dev, bin, i);
+				break;
+			case DATA_TYPE_DSP_REG:
+				ret = aw_check_dsp_reg_num(aw_dev, bin, i);
+				break;
+			case DATA_TYPE_SOC_APP:
+				ret = aw_check_soc_app_num(aw_dev, bin, i);
+				break;
+			default:
+				bin->header_info[i].valid_data_len =
+						bin->header_info[i].bin_data_len;
+				ret = 0;
+				break;
+			}
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int aw_dev_parse_raw_reg(unsigned char *data, unsigned int data_len,
+		struct aw_prof_desc *prof_desc)
+{
+	prof_desc->sec_desc[AW88261_DATA_TYPE_REG].data = data;
+	prof_desc->sec_desc[AW88261_DATA_TYPE_REG].len = data_len;
+
+	prof_desc->prof_st = AW88261_PROFILE_OK;
+
+	return 0;
+}
+
+static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev,
+			uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc)
+{
+	struct aw_bin *aw_bin;
+	int ret;
+
+	aw_bin = devm_kzalloc(aw_dev->dev, data_len + sizeof(struct aw_bin), GFP_KERNEL);
+	if (!aw_bin)
+		return -ENOMEM;
+
+	aw_bin->info.len = data_len;
+	memcpy(aw_bin->info.data, data, data_len);
+
+	ret = aw_parsing_bin_file(aw_dev, aw_bin);
+	if (ret < 0) {
+		dev_err(aw_dev->dev, "parse bin failed");
+		goto parse_bin_failed;
+	}
+
+	if ((aw_bin->all_bin_parse_num != 1) ||
+		(aw_bin->header_info[0].bin_data_type != DATA_TYPE_REGISTER)) {
+		dev_err(aw_dev->dev, "bin num or type error");
+		goto parse_bin_failed;
+	}
+
+	if (aw_bin->header_info[0].valid_data_len % 4) {
+		dev_err(aw_dev->dev, "bin data len get error!");
+		goto parse_bin_failed;
+	}
+
+	prof_desc->sec_desc[AW88261_DATA_TYPE_REG].data =
+				data + aw_bin->header_info[0].valid_data_addr;
+	prof_desc->sec_desc[AW88261_DATA_TYPE_REG].len =
+				aw_bin->header_info[0].valid_data_len;
+	prof_desc->prof_st = AW88261_PROFILE_OK;
+
+	devm_kfree(aw_dev->dev, aw_bin);
+	aw_bin = NULL;
+
+	return 0;
+
+parse_bin_failed:
+	devm_kfree(aw_dev->dev, aw_bin);
+	aw_bin = NULL;
+	return ret;
+}
+static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg_hdr *cfg_hdr,
+			struct aw_cfg_dde *cfg_dde, struct aw_prof_desc *scene_prof_desc)
+{
+	switch (cfg_dde->data_type) {
+	case ACF_SEC_TYPE_REG:
+		return aw_dev_parse_raw_reg((u8 *)cfg_hdr + cfg_dde->data_offset,
+				cfg_dde->data_size, scene_prof_desc);
+	case ACF_SEC_TYPE_HDR_REG:
+		return aw_dev_parse_reg_bin_with_hdr(aw_dev, (u8 *)cfg_hdr + cfg_dde->data_offset,
+				cfg_dde->data_size, scene_prof_desc);
+		break;
+	default:
+		dev_err(aw_dev->dev, "%s cfg_dde->data_type = %d\n", __func__, cfg_dde->data_type);
+		break;
+	}
+
+	return 0;
+}
+
+static int aw_dev_parse_dev_type(struct aw_device *aw_dev,
+		struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info)
+{
+	struct aw_cfg_dde *cfg_dde =
+		(struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->hdr_offset);
+	int sec_num = 0;
+	int ret, i;
+
+	for (i = 0; i < prof_hdr->ddt_num; i++) {
+		if ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) &&
+		    (aw_dev->i2c->addr == cfg_dde[i].dev_addr) &&
+		    (cfg_dde[i].type == AW88261_DEV_TYPE_ID) &&
+		    (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR)) {
+			ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i],
+					&all_prof_info->prof_desc[cfg_dde[i].dev_profile]);
+			if (ret < 0) {
+				dev_err(aw_dev->dev, "parse failed");
+				return ret;
+			}
+			sec_num++;
+		}
+	}
+
+	if (sec_num == 0) {
+		dev_dbg(aw_dev->dev, "get dev type num is %d, please use default", sec_num);
+		return AW88261_DEV_TYPE_NONE;
+	}
+
+	return AW88261_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 =
+		(struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->hdr_offset);
+	int sec_num = 0;
+	int ret, i;
+
+	for (i = 0; i < prof_hdr->ddt_num; i++) {
+		if ((aw_dev->channel == cfg_dde[i].dev_index) &&
+		    (cfg_dde[i].type == AW88261_DEV_DEFAULT_TYPE_ID) &&
+		    (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR)) {
+			if (cfg_dde[i].dev_profile >= AW88261_PROFILE_MAX) {
+				dev_err(aw_dev->dev, "dev_profile [%d] overflow",
+					cfg_dde[i].dev_profile);
+				return -EINVAL;
+			}
+			ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i],
+					&all_prof_info->prof_desc[cfg_dde[i].dev_profile]);
+			if (ret < 0) {
+				dev_err(aw_dev->dev, "parse failed");
+				return ret;
+			}
+			sec_num++;
+		}
+	}
+
+	if (sec_num == 0) {
+		dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", sec_num);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int aw_dev_cfg_get_valid_prof(struct aw_device *aw_dev,
+				struct aw_all_prof_info all_prof_info)
+{
+	struct aw_prof_desc *prof_desc = all_prof_info.prof_desc;
+	struct aw_prof_info *prof_info = &aw_dev->prof_info;
+	int num = 0;
+	int i;
+
+	for (i = 0; i < AW88261_PROFILE_MAX; i++) {
+		if (prof_desc[i].prof_st == AW88261_PROFILE_OK)
+			prof_info->count++;
+	}
+
+	dev_dbg(aw_dev->dev, "get valid profile:%d", aw_dev->prof_info.count);
+
+	if (!prof_info->count) {
+		dev_err(aw_dev->dev, "no profile data");
+		return -EPERM;
+	}
+
+	prof_info->prof_desc = devm_kcalloc(aw_dev->dev,
+					prof_info->count, sizeof(struct aw_prof_desc),
+					GFP_KERNEL);
+	if (!prof_info->prof_desc)
+		return -ENOMEM;
+
+	for (i = 0; i < AW88261_PROFILE_MAX; i++) {
+		if (prof_desc[i].prof_st == AW88261_PROFILE_OK) {
+			if (num >= prof_info->count) {
+				dev_err(aw_dev->dev, "overflow count[%d]",
+						prof_info->count);
+				return -EINVAL;
+			}
+			prof_info->prof_desc[num] = prof_desc[i];
+			prof_info->prof_desc[num].id = i;
+			num++;
+		}
+
+	}
+
+	return 0;
+}
+
+static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev,
+		struct aw_cfg_hdr *prof_hdr)
+{
+	struct aw_all_prof_info *all_prof_info;
+	int ret;
+
+	all_prof_info = devm_kzalloc(aw_dev->dev, sizeof(struct aw_all_prof_info), GFP_KERNEL);
+	if (!all_prof_info)
+		return -ENOMEM;
+
+	ret = aw_dev_parse_dev_type(aw_dev, prof_hdr, all_prof_info);
+	if (ret < 0) {
+		goto exit;
+	} else if (ret == AW88261_DEV_TYPE_NONE) {
+		dev_dbg(aw_dev->dev, "get dev type num is 0, parse default dev");
+		ret = aw_dev_parse_dev_default_type(aw_dev, prof_hdr, all_prof_info);
+		if (ret < 0)
+			goto exit;
+	}
+
+	ret = aw_dev_cfg_get_valid_prof(aw_dev, *all_prof_info);
+	if (ret < 0)
+		goto exit;
+
+	aw_dev->prof_info.prof_name_list = profile_name;
+
+exit:
+	devm_kfree(aw_dev->dev, all_prof_info);
+	return ret;
+}
+
+static int aw_dev_create_prof_name_list_v1(struct aw_device *aw_dev)
+{
+	struct aw_prof_info *prof_info = &aw_dev->prof_info;
+	struct aw_prof_desc *prof_desc = prof_info->prof_desc;
+	int i;
+
+	if (!prof_desc) {
+		dev_err(aw_dev->dev, "prof_desc is NULL");
+		return -EINVAL;
+	}
+
+	prof_info->prof_name_list = devm_kzalloc(aw_dev->dev,
+					prof_info->count * PROFILE_STR_MAX,
+					GFP_KERNEL);
+	if (!prof_info->prof_name_list)
+		return -ENOMEM;
+
+	for (i = 0; i < prof_info->count; i++) {
+		prof_desc[i].id = i;
+		prof_info->prof_name_list[i] = prof_desc[i].prf_str;
+		dev_dbg(aw_dev->dev, "prof name is %s", prof_info->prof_name_list[i]);
+	}
+
+	return 0;
+}
+
+static int aw_get_dde_type_info(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+	struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+	struct aw_cfg_dde_v1 *cfg_dde =
+		(struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset);
+	int default_num = 0;
+	int dev_num = 0;
+	unsigned int i;
+
+	for (i = 0; i < cfg_hdr->ddt_num; i++) {
+		if (cfg_dde[i].type == AW88261_DEV_TYPE_ID)
+			dev_num++;
+
+		if (cfg_dde[i].type == AW88261_DEV_DEFAULT_TYPE_ID)
+			default_num++;
+	}
+
+	if (dev_num != 0) {
+		aw_dev->prof_info.prof_type = AW88261_DEV_TYPE_ID;
+	} else if (default_num != 0) {
+		aw_dev->prof_info.prof_type = AW88261_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_container *aw_cfg,
+						unsigned int *scene_num)
+{
+	struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+	struct aw_cfg_dde_v1 *cfg_dde =
+		(struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset);
+	unsigned int i;
+
+	for (i = 0; i < cfg_hdr->ddt_num; ++i) {
+		if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) ||
+		     (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) &&
+		    (aw_dev->chip_id == cfg_dde[i].chip_id) &&
+		    (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) &&
+		    (aw_dev->i2c->addr == cfg_dde[i].dev_addr))
+			(*scene_num)++;
+	}
+
+	return 0;
+}
+
+static int aw_get_default_scene_count_v1(struct aw_device *aw_dev,
+						struct aw_container *aw_cfg,
+						unsigned int *scene_num)
+{
+	struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+	struct aw_cfg_dde_v1 *cfg_dde =
+		(struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset);
+	unsigned int i;
+
+	for (i = 0; i < cfg_hdr->ddt_num; ++i) {
+		if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) ||
+		     (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) &&
+		    (aw_dev->chip_id == cfg_dde[i].chip_id) &&
+		    (aw_dev->channel == cfg_dde[i].dev_index))
+			(*scene_num)++;
+	}
+
+	return 0;
+}
+
+static int aw_dev_parse_scene_count_v1(struct aw_device *aw_dev,
+							struct aw_container *aw_cfg,
+							unsigned int *count)
+{
+	int ret;
+
+	ret = aw_get_dde_type_info(aw_dev, aw_cfg);
+	if (ret < 0)
+		return ret;
+
+	switch (aw_dev->prof_info.prof_type) {
+	case AW88261_DEV_TYPE_ID:
+		ret = aw_get_dev_scene_count_v1(aw_dev, aw_cfg, count);
+		break;
+	case AW88261_DEV_DEFAULT_TYPE_ID:
+		ret = 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 = -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 = &aw_dev->prof_info;
+	int ret;
+
+	switch (cfg_dde->data_type) {
+	case ACF_SEC_TYPE_REG:
+		ret =  aw_dev_parse_raw_reg((u8 *)prof_hdr + cfg_dde->data_offset,
+				cfg_dde->data_size, &prof_info->prof_desc[*cur_scene_id]);
+		if (ret < 0) {
+			dev_err(aw_dev->dev, "parse reg bin failed");
+			return ret;
+		}
+		prof_info->prof_desc[*cur_scene_id].prf_str = cfg_dde->dev_profile_str;
+		prof_info->prof_desc[*cur_scene_id].id = cfg_dde->dev_profile;
+		(*cur_scene_id)++;
+		break;
+	case ACF_SEC_TYPE_HDR_REG:
+		ret =  aw_dev_parse_reg_bin_with_hdr(aw_dev,
+				(uint8_t *)prof_hdr + cfg_dde->data_offset,
+				cfg_dde->data_size, &prof_info->prof_desc[*cur_scene_id]);
+		if (ret < 0) {
+			dev_err(aw_dev->dev, "parse reg bin with hdr failed");
+			return ret;
+		}
+		prof_info->prof_desc[*cur_scene_id].prf_str = cfg_dde->dev_profile_str;
+		prof_info->prof_desc[*cur_scene_id].id = cfg_dde->dev_profile;
+		(*cur_scene_id)++;
+		break;
+	default:
+		dev_err(aw_dev->dev, "unsupported SEC_TYPE [%d]", cfg_dde->data_type);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+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 =
+		(struct aw_cfg_dde_v1 *)((char *)prof_hdr + prof_hdr->hdr_offset);
+	int cur_scene_id = 0;
+	unsigned int i;
+	int ret = 0;
+
+	for (i = 0; i < prof_hdr->ddt_num; i++) {
+		if ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) &&
+		    (aw_dev->i2c->addr == cfg_dde[i].dev_addr) &&
+		    (aw_dev->chip_id == cfg_dde[i].chip_id)) {
+
+			ret = aw_dev_parse_data_by_sec_type_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 == 0) {
+		dev_err(aw_dev->dev, "get dev type failed, get num [%d]", cur_scene_id);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+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 =
+		(struct aw_cfg_dde_v1 *)((char *)prof_hdr + prof_hdr->hdr_offset);
+	int cur_scene_id = 0;
+	unsigned int i;
+	int ret = 0;
+
+	for (i = 0; i < prof_hdr->ddt_num; i++) {
+		if ((cfg_dde[i].type == AW88261_DEV_DEFAULT_TYPE_ID) &&
+			(aw_dev->channel == cfg_dde[i].dev_index) &&
+			(aw_dev->chip_id == cfg_dde[i].chip_id)) {
+			ret = aw_dev_parse_data_by_sec_type_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 == 0) {
+		dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", cur_scene_id);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+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 AW88261_DEV_TYPE_ID:
+		ret = aw_dev_parse_dev_type_v1(aw_dev, cfg_hdr);
+		break;
+	case AW88261_DEV_DEFAULT_TYPE_ID:
+		ret = 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 =  -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 = (struct aw_cfg_hdr *)aw_cfg->data;
+	struct aw_prof_info *prof_info = &aw_dev->prof_info;
+	int ret;
+
+	ret = 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 = devm_kcalloc(aw_dev->dev,
+					prof_info->count, sizeof(struct aw_prof_desc),
+					GFP_KERNEL);
+	if (!prof_info->prof_desc)
+		return -ENOMEM;
+
+	ret = aw_dev_parse_by_hdr_v1(aw_dev, cfg_hdr);
+	if (ret < 0) {
+		dev_err(aw_dev->dev, "parse hdr failed");
+		return ret;
+	}
+
+	ret = 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 ret;
+}
+
+int aw88261_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+	struct aw_cfg_hdr *cfg_hdr;
+	int ret;
+
+	cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+
+	switch (cfg_hdr->hdr_version) {
+	case AW88261_CFG_HDR_VER:
+		ret = aw_dev_load_cfg_by_hdr(aw_dev, cfg_hdr);
+		if (ret < 0) {
+			dev_err(aw_dev->dev, "hdr_version[0x%x] parse failed",
+						cfg_hdr->hdr_version);
+			return ret;
+		}
+		break;
+	case AW88261_CFG_HDR_VER_V1:
+		ret = aw_dev_load_cfg_by_hdr_v1(aw_dev, aw_cfg);
+		if (ret < 0) {
+			dev_err(aw_dev->dev, "hdr_version[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_version);
+		return -EINVAL;
+	}
+	aw_dev->fw_status = AW88261_DEV_FW_OK;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(aw88261_dev_cfg_load);
+
+static int aw_dev_check_cfg_by_hdr(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+	unsigned int end_data_offset;
+	struct aw_cfg_hdr *cfg_hdr;
+	struct aw_cfg_dde *cfg_dde;
+	unsigned int act_data = 0;
+	unsigned int hdr_ddt_len;
+	unsigned int i;
+	u8 act_crc8;
+
+	cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+	/* check file type id is awinic acf file */
+	if (cfg_hdr->id != ACF_FILE_ID) {
+		dev_err(aw_dev->dev, "not acf type file");
+		return -EINVAL;
+	}
+
+	hdr_ddt_len = cfg_hdr->hdr_offset + cfg_hdr->ddt_size;
+	if (hdr_ddt_len > aw_cfg->len) {
+		dev_err(aw_dev->dev, "hdr_len with ddt_len [%d] overflow file size[%d]",
+		cfg_hdr->hdr_offset, aw_cfg->len);
+		return -EINVAL;
+	}
+
+	/* check data size */
+	cfg_dde = (struct aw_cfg_dde *)((char *)aw_cfg->data + cfg_hdr->hdr_offset);
+	act_data += hdr_ddt_len;
+	for (i = 0; i < cfg_hdr->ddt_num; i++)
+		act_data += cfg_dde[i].data_size;
+
+	if (act_data != 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 = 0; i < cfg_hdr->ddt_num; i++) {
+		/* data check */
+		end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size;
+		if (end_data_offset > aw_cfg->len) {
+			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 = crc8(aw_crc8_table, aw_cfg->data + cfg_dde[i].data_offset,
+							cfg_dde[i].data_size, 0);
+		if (act_crc8 != cfg_dde[i].data_crc) {
+			dev_err(aw_dev->dev, "ddt_num[%d] act_crc8:0x%x != data_crc:0x%x",
+				i, (u32)act_crc8, cfg_dde[i].data_crc);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int aw_dev_check_acf_by_hdr_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 = 0;
+	unsigned int hdr_ddt_len;
+	u8 act_crc8;
+	int i;
+
+	cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+
+	/* check file type id is awinic acf file */
+	if (cfg_hdr->id != ACF_FILE_ID) {
+		dev_err(aw_dev->dev, "not acf type file");
+		return -EINVAL;
+	}
+
+	hdr_ddt_len = 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 = (struct aw_cfg_dde_v1 *)((char *)aw_cfg->data + cfg_hdr->hdr_offset);
+	act_data += hdr_ddt_len;
+	for (i = 0; i < cfg_hdr->ddt_num; i++)
+		act_data += cfg_dde[i].data_size;
+
+	if (act_data != 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 = 0; i < cfg_hdr->ddt_num; i++) {
+		/* data check */
+		end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size;
+		if (end_data_offset > aw_cfg->len) {
+			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 = crc8(aw_crc8_table, aw_cfg->data + cfg_dde[i].data_offset,
+									cfg_dde[i].data_size, 0);
+		if (act_crc8 != cfg_dde[i].data_crc) {
+			dev_err(aw_dev->dev, "ddt_num[%d] act_crc8:0x%x != data_crc 0x%x",
+				i, (u32)act_crc8, cfg_dde[i].data_crc);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+int aw88261_dev_load_acf_check(struct aw_device *aw_dev, struct aw_container *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, AW88261_CRC8_POLYNOMIAL);
+
+	cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+	switch (cfg_hdr->hdr_version) {
+	case AW88261_CFG_HDR_VER:
+		return aw_dev_check_cfg_by_hdr(aw_dev, aw_cfg);
+	case AW88261_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_version);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(aw88261_dev_load_acf_check);
+
+MODULE_DESCRIPTION("AW88261 ACF File Parsing Lib");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88261/aw88261_lib.h b/sound/soc/codecs/aw88261/aw88261_lib.h
new file mode 100644
index 000000000000..ac8295fcf9d4
--- /dev/null
+++ b/sound/soc/codecs/aw88261/aw88261_lib.h
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88261_lib.h  -- ACF bin parsing and check library file for aw88261
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Jimmy Zhang <zhangjianming@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW88261_LIB_H__
+#define __AW88261_LIB_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 AW88261_FW_CHECK_PART		(10)
+#define HDADER_LEN			(60)
+#define HEADER_VERSION_OFFSET		(4)
+
+enum bin_header_version_enum {
+	HEADER_VERSION_V1 = 0x01000000,
+};
+
+enum data_type_enum {
+	DATA_TYPE_REGISTER   = 0x00000000,
+	DATA_TYPE_DSP_REG    = 0x00000010,
+	DATA_TYPE_DSP_CFG    = 0x00000011,
+	DATA_TYPE_SOC_REG    = 0x00000020,
+	DATA_TYPE_SOC_APP    = 0x00000021,
+	DATA_TYPE_DSP_FW     = 0x00000022,
+	DATA_TYPE_MULTI_BINS = 0x00002000,
+};
+
+enum data_version_enum {
+	DATA_VERSION_V1 = 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
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH V1 4/5] ASoC: codecs: aw88261 chip register file, data type file and Kconfig Makefile
  2023-07-17 11:58 [PATCH V1 0/5] ASoC: codecs: Add Awinic AW88261 audio amplifier driver wangweidong.a
                   ` (2 preceding siblings ...)
  2023-07-17 11:58 ` [PATCH V1 3/5] ASoC: codecs: ACF bin parsing and check library file for aw88261 wangweidong.a
@ 2023-07-17 11:58 ` wangweidong.a
  2023-07-17 12:33   ` Randy Dunlap
  2023-07-17 11:58 ` [PATCH V1 5/5] ASoC: dt-bindings: Add schema for "awinic,aw88261" wangweidong.a
  2023-07-17 13:03 ` [PATCH V1 0/5] ASoC: codecs: Add Awinic AW88261 audio amplifier driver Herve Codina
  5 siblings, 1 reply; 19+ messages in thread
From: wangweidong.a @ 2023-07-17 11:58 UTC (permalink / raw)
  To: lgirdwood, broonie, robh+dt, krzysztof.kozlowski+dt, conor+dt,
	perex, tiwai, rf, wangweidong.a, shumingf, herve.codina, flatmax,
	ckeepax, doug, fido_max, pierre-louis.bossart, kiseok.jo,
	liweilei, colin.i.king, trix, alsa-devel, devicetree,
	linux-kernel
  Cc: yijiangtao, zhangjianming

From: Weidong Wang <wangweidong.a@awinic.com>

The AW88261 is an I2S/TDM input, high efficiency
digital Smart K audio amplifier with an integrated 10.25V
smart boost convert

Signed-off-by: Weidong Wang <wangweidong.a@awinic.com>
---
 sound/soc/codecs/Kconfig                     |  17 +
 sound/soc/codecs/Makefile                    |   5 +
 sound/soc/codecs/aw88261/aw88261_data_type.h | 144 +++++++
 sound/soc/codecs/aw88261/aw88261_reg.h       | 377 +++++++++++++++++++
 4 files changed, 543 insertions(+)
 create mode 100644 sound/soc/codecs/aw88261/aw88261_data_type.h
 create mode 100644 sound/soc/codecs/aw88261/aw88261_reg.h

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 8020097d4e4c..8b1a8071ae2e 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -55,6 +55,7 @@ config SND_SOC_ALL_CODECS
 	imply SND_SOC_ALC5632
 	imply SND_SOC_AW8738
 	imply SND_SOC_AW88395
+	imply SND_SOC_AW88261
 	imply SND_SOC_BT_SCO
 	imply SND_SOC_BD28623
 	imply SND_SOC_CQ0093VC
@@ -633,6 +634,22 @@ config SND_SOC_AW88395
 	  digital Smart K audio amplifier with an integrated 10V
 	  smart boost convert.
 
+config SND_SOC_AW88261_LIB
+	tristate
+
+config SND_SOC_AW88261
+	tristate "Soc Audio for awinic aw88261"
+	depends on I2C
+	select CRC8
+	select REGMAP_I2C
+	select GPIOLIB
+	select SND_SOC_AW88261_LIB
+	help
+	  this option enables support for aw88261 Smart PA.
+	  The Awinic AW88261 is an I2S/TDM input, high efficiency
+	  digital Smart K audio amplifier with an integrated 10.25V
+		smart boost convert.
+
 config SND_SOC_BD28623
 	tristate "ROHM BD28623 CODEC"
 	help
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 5cdbae88e6e3..f64fcc569714 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -49,6 +49,9 @@ snd-soc-aw8738-objs := aw8738.o
 snd-soc-aw88395-lib-objs := aw88395/aw88395_lib.o
 snd-soc-aw88395-objs := aw88395/aw88395.o \
 			aw88395/aw88395_device.o
+snd-soc-aw88261-lib-objs := aw88261/aw88261_lib.o
+snd-soc-aw88261-objs := aw88261/aw88261.o \
+			aw88261/aw88261_device.o
 snd-soc-bd28623-objs := bd28623.o
 snd-soc-bt-sco-objs := bt-sco.o
 snd-soc-cpcap-objs := cpcap.o
@@ -423,6 +426,8 @@ obj-$(CONFIG_SND_SOC_ARIZONA)	+= snd-soc-arizona.o
 obj-$(CONFIG_SND_SOC_AW8738)	+= snd-soc-aw8738.o
 obj-$(CONFIG_SND_SOC_AW88395_LIB) += snd-soc-aw88395-lib.o
 obj-$(CONFIG_SND_SOC_AW88395)	+=snd-soc-aw88395.o
+obj-$(CONFIG_SND_SOC_AW88261_LIB) += snd-soc-aw88261-lib.o
+obj-$(CONFIG_SND_SOC_AW88261)	+=snd-soc-aw88261.o
 obj-$(CONFIG_SND_SOC_BD28623)	+= snd-soc-bd28623.o
 obj-$(CONFIG_SND_SOC_BT_SCO)	+= snd-soc-bt-sco.o
 obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
diff --git a/sound/soc/codecs/aw88261/aw88261_data_type.h b/sound/soc/codecs/aw88261/aw88261_data_type.h
new file mode 100644
index 000000000000..e1d40270beae
--- /dev/null
+++ b/sound/soc/codecs/aw88261/aw88261_data_type.h
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88261_data_type.h --  The data type of the AW88261 chip
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Jimmy Zhang <zhangjianming@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW88261_DATA_TYPE_H__
+#define __AW88261_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 AW_INIT_PROFILE		(0)
+
+#define ACF_FILE_ID			(0xa15f908)
+
+enum aw_cfg_hdr_version {
+	AW88261_CFG_HDR_VER	= 0x00000001,
+	AW88261_CFG_HDR_VER_V1	= 0x01000000,
+};
+
+enum aw_cfg_dde_type {
+	AW88261_DEV_NONE_TYPE_ID	= 0xFFFFFFFF,
+	AW88261_DEV_TYPE_ID		= 0x00000000,
+	AW88261_SKT_TYPE_ID		= 0x00000001,
+	AW88261_DEV_DEFAULT_TYPE_ID	= 0x00000002,
+};
+
+enum aw_sec_type {
+	ACF_SEC_TYPE_REG = 0,
+	ACF_SEC_TYPE_DSP,
+	ACF_SEC_TYPE_DSP_CFG,
+	ACF_SEC_TYPE_DSP_FW,
+	ACF_SEC_TYPE_HDR_REG,
+	ACF_SEC_TYPE_HDR_DSP_CFG,
+	ACF_SEC_TYPE_HDR_DSP_FW,
+	ACF_SEC_TYPE_MULTIPLE_BIN,
+	ACF_SEC_TYPE_SKT_PROJECT,
+	ACF_SEC_TYPE_DSP_PROJECT,
+	ACF_SEC_TYPE_MONITOR,
+	ACF_SEC_TYPE_MAX,
+};
+
+enum profile_data_type {
+	AW88261_DATA_TYPE_REG = 0,
+	AW88261_DATA_TYPE_DSP_CFG,
+	AW88261_DATA_TYPE_DSP_FW,
+	AW88261_DATA_TYPE_MAX,
+};
+
+enum aw_prof_type {
+	AW88261_PROFILE_MUSIC = 0,
+	AW88261_PROFILE_VOICE,
+	AW88261_PROFILE_VOIP,
+	AW88261_PROFILE_RINGTONE,
+	AW88261_PROFILE_RINGTONE_HS,
+	AW88261_PROFILE_LOWPOWER,
+	AW88261_PROFILE_BYPASS,
+	AW88261_PROFILE_MMI,
+	AW88261_PROFILE_FM,
+	AW88261_PROFILE_NOTIFICATION,
+	AW88261_PROFILE_RECEIVER,
+	AW88261_PROFILE_MAX,
+};
+
+enum aw_profile_status {
+	AW88261_PROFILE_WAIT = 0,
+	AW88261_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[AW88261_DATA_TYPE_MAX];
+};
+
+struct aw_all_prof_info {
+	struct aw_prof_desc prof_desc[AW88261_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/aw88261/aw88261_reg.h b/sound/soc/codecs/aw88261/aw88261_reg.h
new file mode 100644
index 000000000000..1d569ba6a583
--- /dev/null
+++ b/sound/soc/codecs/aw88261/aw88261_reg.h
@@ -0,0 +1,377 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88261_reg.h --  AW88261 chip register file
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Jimmy Zhang <zhangjianming@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW88261_REG_H__
+#define __AW88261_REG_H__
+
+#define AW88261_ID_REG			(0x00)
+#define AW88261_SYSST_REG		(0x01)
+#define AW88261_SYSINT_REG		(0x02)
+#define AW88261_SYSINTM_REG		(0x03)
+#define AW88261_SYSCTRL_REG		(0x04)
+#define AW88261_SYSCTRL2_REG		(0x05)
+#define AW88261_I2SCTRL1_REG		(0x06)
+#define AW88261_I2SCTRL2_REG		(0x07)
+#define AW88261_I2SCTRL3_REG		(0x08)
+#define AW88261_DACCFG1_REG		(0x09)
+#define AW88261_DACCFG2_REG		(0x0A)
+#define AW88261_DACCFG3_REG		(0x0B)
+#define AW88261_DACCFG4_REG		(0x0C)
+#define AW88261_DACCFG5_REG		(0x0D)
+#define AW88261_DACCFG6_REG		(0x0E)
+#define AW88261_DACCFG7_REG		(0x0F)
+#define AW88261_DACCFG8_REG		(0x10)
+#define AW88261_PWMCTRL1_REG		(0x11)
+#define AW88261_PWMCTRL2_REG		(0x12)
+#define AW88261_I2SCFG1_REG		(0x13)
+#define AW88261_DBGCTRL_REG		(0x14)
+#define AW88261_DACCFG9_REG		(0x15)
+#define AW88261_DACCFG10_REG		(0x16)
+#define AW88261_DACST_REG		(0x20)
+#define AW88261_VBAT_REG		(0x21)
+#define AW88261_TEMP_REG		(0x22)
+#define AW88261_PVDD_REG		(0x23)
+#define AW88261_ISNDAT_REG		(0x24)
+#define AW88261_VSNDAT_REG		(0x25)
+#define AW88261_I2SINT_REG		(0x26)
+#define AW88261_I2SCAPCNT_REG		(0x27)
+#define AW88261_ANASTA1_REG		(0x28)
+#define AW88261_ANASTA2_REG		(0x29)
+#define AW88261_ANASTA3_REG		(0x2A)
+#define AW88261_TESTDET_REG		(0x2B)
+#define AW88261_DSMCFG1_REG		(0x30)
+#define AW88261_DSMCFG2_REG		(0x31)
+#define AW88261_DSMCFG3_REG		(0x32)
+#define AW88261_DSMCFG4_REG		(0x33)
+#define AW88261_DSMCFG5_REG		(0x34)
+#define AW88261_DSMCFG6_REG		(0x35)
+#define AW88261_DSMCFG7_REG		(0x36)
+#define AW88261_DSMCFG8_REG		(0x37)
+#define AW88261_TESTIN_REG		(0x38)
+#define AW88261_TESTOUT_REG		(0x39)
+#define AW88261_SADCCTRL1_REG		(0x3A)
+#define AW88261_SADCCTRL2_REG		(0x3B)
+#define AW88261_SADCCTRL3_REG		(0x3C)
+#define AW88261_SADCCTRL4_REG		(0x3D)
+#define AW88261_SADCCTRL5_REG		(0x3E)
+#define AW88261_SADCCTRL6_REG		(0x3F)
+#define AW88261_SADCCTRL7_REG		(0x40)
+#define AW88261_VSNTM1_REG		(0x50)
+#define AW88261_VSNTM2_REG		(0x51)
+#define AW88261_ISNCTRL1_REG		(0x52)
+#define AW88261_ISNCTRL2_REG		(0x53)
+#define AW88261_PLLCTRL1_REG		(0x54)
+#define AW88261_PLLCTRL2_REG		(0x55)
+#define AW88261_PLLCTRL3_REG		(0x56)
+#define AW88261_CDACTRL1_REG		(0x57)
+#define AW88261_CDACTRL2_REG		(0x58)
+#define AW88261_DITHERCFG1_REG		(0x59)
+#define AW88261_DITHERCFG2_REG		(0x5A)
+#define AW88261_DITHERCFG3_REG		(0x5B)
+#define AW88261_CPCTRL_REG		(0x5C)
+#define AW88261_BSTCTRL1_REG		(0x60)
+#define AW88261_BSTCTRL2_REG		(0x61)
+#define AW88261_BSTCTRL3_REG		(0x62)
+#define AW88261_BSTCTRL4_REG		(0x63)
+#define AW88261_BSTCTRL5_REG		(0x64)
+#define AW88261_BSTCTRL6_REG		(0x65)
+#define AW88261_BSTCTRL7_REG		(0x66)
+#define AW88261_BSTCTRL8_REG		(0x67)
+#define AW88261_BSTCTRL9_REG		(0x68)
+#define AW88261_TM_REG			(0x6F)
+#define AW88261_TESTCTRL1_REG		(0x70)
+#define AW88261_TESTCTRL2_REG		(0x71)
+#define AW88261_EFCTRL1_REG		(0x72)
+#define AW88261_EFCTRL2_REG		(0x73)
+#define AW88261_EFWH_REG		(0x74)
+#define AW88261_EFWM2_REG		(0x75)
+#define AW88261_EFWM1_REG		(0x76)
+#define AW88261_EFWL_REG		(0x77)
+#define AW88261_EFRH4_REG		(0x78)
+#define AW88261_EFRH3_REG		(0x79)
+#define AW88261_EFRH2_REG		(0x7A)
+#define AW88261_EFRH1_REG		(0x7B)
+#define AW88261_EFRL4_REG		(0x7C)
+#define AW88261_EFRL3_REG		(0x7D)
+#define AW88261_EFRL2_REG		(0x7E)
+#define AW88261_EFRL1_REG		(0x7F)
+
+enum aw88261_id {
+	AW88261_CHIP_ID = 0x2113,
+};
+
+#define AW88261_REG_MAX		(0x80)
+#define AW88261_EF_DBMD_MASK		(0xfff7)
+#define AW88261_OR_VALUE		(0x0008)
+
+#define AW88261_TEMH_MASK		(0x83ff)
+#define AW88261_TEML_MASK		(0x83ff)
+#define AW88261_DEFAULT_CFG		(0x0000)
+
+#define AW88261_ICALK_SHIFT		(0)
+#define AW88261_ICALKL_SHIFT		(0)
+#define AW88261_VCALK_SHIFT		(0)
+#define AW88261_VCALKL_SHIFT		(0)
+
+#define AW88261_AMPPD_START_BIT	(1)
+#define AW88261_AMPPD_BITS_LEN		(1)
+#define AW88261_AMPPD_MASK		\
+	(~(((1<<AW88261_AMPPD_BITS_LEN)-1) << AW88261_AMPPD_START_BIT))
+
+#define AW88261_UVLS_START_BIT		(14)
+#define AW88261_UVLS_NORMAL		(0)
+#define AW88261_UVLS_NORMAL_VALUE	\
+	(AW88261_UVLS_NORMAL << AW88261_UVLS_START_BIT)
+
+#define AW88261_BSTOCS_START_BIT	(11)
+#define AW88261_BSTOCS_OVER_CURRENT	(1)
+#define AW88261_BSTOCS_OVER_CURRENT_VALUE	\
+	(AW88261_BSTOCS_OVER_CURRENT << AW88261_BSTOCS_START_BIT)
+
+#define AW88261_BSTS_START_BIT		(9)
+#define AW88261_BSTS_FINISHED		(1)
+#define AW88261_BSTS_FINISHED_VALUE	\
+	(AW88261_BSTS_FINISHED << AW88261_BSTS_START_BIT)
+
+#define AW88261_SWS_START_BIT		(8)
+#define AW88261_SWS_SWITCHING		(1)
+#define AW88261_SWS_SWITCHING_VALUE	\
+	(AW88261_SWS_SWITCHING << AW88261_SWS_START_BIT)
+
+#define AW88261_NOCLKS_START_BIT	(5)
+#define AW88261_NOCLKS_NO_CLOCK	(1)
+#define AW88261_NOCLKS_NO_CLOCK_VALUE	\
+	(AW88261_NOCLKS_NO_CLOCK << AW88261_NOCLKS_START_BIT)
+
+#define AW88261_CLKS_START_BIT		(4)
+#define AW88261_CLKS_STABLE		(1)
+#define AW88261_CLKS_STABLE_VALUE	\
+	(AW88261_CLKS_STABLE << AW88261_CLKS_START_BIT)
+
+#define AW88261_OCDS_START_BIT		(3)
+#define AW88261_OCDS_OC		(1)
+#define AW88261_OCDS_OC_VALUE		\
+	(AW88261_OCDS_OC << AW88261_OCDS_START_BIT)
+
+#define AW88261_OTHS_START_BIT		(1)
+#define AW88261_OTHS_OT		(1)
+#define AW88261_OTHS_OT_VALUE		\
+	(AW88261_OTHS_OT << AW88261_OTHS_START_BIT)
+
+#define AW88261_PLLS_START_BIT		(0)
+#define AW88261_PLLS_LOCKED		(1)
+#define AW88261_PLLS_LOCKED_VALUE	\
+	(AW88261_PLLS_LOCKED << AW88261_PLLS_START_BIT)
+
+#define AW88261_BIT_PLL_CHECK \
+		(AW88261_CLKS_STABLE_VALUE | \
+		AW88261_PLLS_LOCKED_VALUE)
+
+#define AW88261_BIT_SYSST_CHECK_MASK \
+		(~(AW88261_UVLS_NORMAL_VALUE | \
+		AW88261_BSTOCS_OVER_CURRENT_VALUE | \
+		AW88261_BSTS_FINISHED_VALUE | \
+		AW88261_SWS_SWITCHING_VALUE | \
+		AW88261_NOCLKS_NO_CLOCK_VALUE | \
+		AW88261_CLKS_STABLE_VALUE | \
+		AW88261_OCDS_OC_VALUE | \
+		AW88261_OTHS_OT_VALUE | \
+		AW88261_PLLS_LOCKED_VALUE))
+
+#define AW88261_BIT_SYSST_CHECK \
+		(AW88261_BSTS_FINISHED_VALUE | \
+		AW88261_SWS_SWITCHING_VALUE | \
+		AW88261_CLKS_STABLE_VALUE | \
+		AW88261_PLLS_LOCKED_VALUE)
+
+#define AW88261_ULS_HMUTE_START_BIT	(14)
+#define AW88261_ULS_HMUTE_BITS_LEN	(1)
+#define AW88261_ULS_HMUTE_MASK		\
+	(~(((1<<AW88261_ULS_HMUTE_BITS_LEN)-1) << AW88261_ULS_HMUTE_START_BIT))
+
+#define AW88261_ULS_HMUTE_DISABLE	(0)
+#define AW88261_ULS_HMUTE_DISABLE_VALUE	\
+	(AW88261_ULS_HMUTE_DISABLE << AW88261_ULS_HMUTE_START_BIT)
+
+#define AW88261_ULS_HMUTE_ENABLE	(1)
+#define AW88261_ULS_HMUTE_ENABLE_VALUE	\
+	(AW88261_ULS_HMUTE_ENABLE << AW88261_ULS_HMUTE_START_BIT)
+
+#define AW88261_HMUTE_START_BIT	(8)
+#define AW88261_HMUTE_BITS_LEN		(1)
+#define AW88261_HMUTE_MASK		\
+	(~(((1<<AW88261_HMUTE_BITS_LEN)-1) << AW88261_HMUTE_START_BIT))
+
+#define AW88261_HMUTE_DISABLE		(0)
+#define AW88261_HMUTE_DISABLE_VALUE	\
+	(AW88261_HMUTE_DISABLE << AW88261_HMUTE_START_BIT)
+
+#define AW88261_HMUTE_ENABLE		(1)
+#define AW88261_HMUTE_ENABLE_VALUE	\
+	(AW88261_HMUTE_ENABLE << AW88261_HMUTE_START_BIT)
+
+#define AW88261_AMPPD_START_BIT	(1)
+#define AW88261_AMPPD_BITS_LEN		(1)
+#define AW88261_AMPPD_MASK		\
+	(~(((1<<AW88261_AMPPD_BITS_LEN)-1) << AW88261_AMPPD_START_BIT))
+
+#define AW88261_AMPPD_WORKING		(0)
+#define AW88261_AMPPD_WORKING_VALUE	\
+	(AW88261_AMPPD_WORKING << AW88261_AMPPD_START_BIT)
+
+#define AW88261_AMPPD_POWER_DOWN	(1)
+#define AW88261_AMPPD_POWER_DOWN_VALUE	\
+	(AW88261_AMPPD_POWER_DOWN << AW88261_AMPPD_START_BIT)
+
+#define AW88261_PWDN_START_BIT		(0)
+#define AW88261_PWDN_BITS_LEN		(1)
+#define AW88261_PWDN_MASK		\
+	(~(((1<<AW88261_PWDN_BITS_LEN)-1) << AW88261_PWDN_START_BIT))
+
+#define AW88261_PWDN_WORKING		(0)
+#define AW88261_PWDN_WORKING_VALUE	\
+	(AW88261_PWDN_WORKING << AW88261_PWDN_START_BIT)
+
+#define AW88261_PWDN_POWER_DOWN	(1)
+#define AW88261_PWDN_POWER_DOWN_VALUE	\
+	(AW88261_PWDN_POWER_DOWN << AW88261_PWDN_START_BIT)
+
+#define AW88261_MUTE_VOL		(90 * 8)
+#define AW88261_VOLUME_STEP_DB		(6 * 8)
+
+#define AW88261_VOL_6DB_START		(6)
+
+#define AW88261_VOL_START_BIT		(0)
+#define AW88261_VOL_BITS_LEN		(10)
+#define AW88261_VOL_MASK		\
+	(~(((1<<AW88261_VOL_BITS_LEN)-1) << AW88261_VOL_START_BIT))
+
+#define AW88261_VOL_DEFAULT_VALUE	(0)
+
+#define AW88261_I2STXEN_START_BIT	(6)
+#define AW88261_I2STXEN_BITS_LEN	(1)
+#define AW88261_I2STXEN_MASK		\
+	(~(((1<<AW88261_I2STXEN_BITS_LEN)-1) << AW88261_I2STXEN_START_BIT))
+
+#define AW88261_I2STXEN_DISABLE	(0)
+#define AW88261_I2STXEN_DISABLE_VALUE	\
+	(AW88261_I2STXEN_DISABLE << AW88261_I2STXEN_START_BIT)
+
+#define AW88261_I2STXEN_ENABLE		(1)
+#define AW88261_I2STXEN_ENABLE_VALUE	\
+	(AW88261_I2STXEN_ENABLE << AW88261_I2STXEN_START_BIT)
+
+#define AW88261_CCO_MUX_START_BIT	(14)
+#define AW88261_CCO_MUX_BITS_LEN	(1)
+#define AW88261_CCO_MUX_MASK		\
+	(~(((1<<AW88261_CCO_MUX_BITS_LEN)-1) << AW88261_CCO_MUX_START_BIT))
+
+#define AW88261_CCO_MUX_DIVIDED	(0)
+#define AW88261_CCO_MUX_DIVIDED_VALUE	\
+	(AW88261_CCO_MUX_DIVIDED << AW88261_CCO_MUX_START_BIT)
+
+#define AW88261_CCO_MUX_BYPASS		(1)
+#define AW88261_CCO_MUX_BYPASS_VALUE	\
+	(AW88261_CCO_MUX_BYPASS << AW88261_CCO_MUX_START_BIT)
+
+#define AW88261_EF_VSN_GESLP_H_START_BIT	(0)
+#define AW88261_EF_VSN_GESLP_H_BITS_LEN	(10)
+#define AW88261_EF_VSN_GESLP_H_MASK		\
+	(~(((1<<AW88261_EF_VSN_GESLP_H_BITS_LEN)-1) << AW88261_EF_VSN_GESLP_H_START_BIT))
+
+#define AW88261_EF_VSN_GESLP_L_START_BIT	(0)
+#define AW88261_EF_VSN_GESLP_L_BITS_LEN	(10)
+#define AW88261_EF_VSN_GESLP_L_MASK		\
+	(~(((1<<AW88261_EF_VSN_GESLP_L_BITS_LEN)-1) << AW88261_EF_VSN_GESLP_L_START_BIT))
+
+#define AW88261_FORCE_PWM_START_BIT	(12)
+#define AW88261_FORCE_PWM_BITS_LEN	(1)
+#define AW88261_FORCE_PWM_MASK		\
+	(~(((1<<AW88261_FORCE_PWM_BITS_LEN)-1) << AW88261_FORCE_PWM_START_BIT))
+
+#define AW88261_FORCE_PWM_FORCEMINUS_PWM	(1)
+#define AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE	\
+	(AW88261_FORCE_PWM_FORCEMINUS_PWM << AW88261_FORCE_PWM_START_BIT)
+
+#define AW88261_BST_OS_WIDTH_START_BIT	(0)
+#define AW88261_BST_OS_WIDTH_BITS_LEN	(3)
+#define AW88261_BST_OS_WIDTH_MASK	\
+	(~(((1<<AW88261_BST_OS_WIDTH_BITS_LEN)-1) << AW88261_BST_OS_WIDTH_START_BIT))
+
+#define AW88261_BST_OS_WIDTH_50NS	(4)
+#define AW88261_BST_OS_WIDTH_50NS_VALUE	\
+	(AW88261_BST_OS_WIDTH_50NS << AW88261_BST_OS_WIDTH_START_BIT)
+
+/* BST_LOOPR bit 1:0 (BSTCTRL6 0x65) */
+#define AW88261_BST_LOOPR_START_BIT	(0)
+#define AW88261_BST_LOOPR_BITS_LEN	(2)
+#define AW88261_BST_LOOPR_MASK		\
+	(~(((1<<AW88261_BST_LOOPR_BITS_LEN)-1) << AW88261_BST_LOOPR_START_BIT))
+
+#define AW88261_BST_LOOPR_340K		(2)
+#define AW88261_BST_LOOPR_340K_VALUE	\
+	(AW88261_BST_LOOPR_340K << AW88261_BST_LOOPR_START_BIT)
+
+/* RSQN_DLY bit 15:14 (BSTCTRL7 0x66) */
+#define AW88261_RSQN_DLY_START_BIT	(14)
+#define AW88261_RSQN_DLY_BITS_LEN	(2)
+#define AW88261_RSQN_DLY_MASK		\
+	(~(((1<<AW88261_RSQN_DLY_BITS_LEN)-1) << AW88261_RSQN_DLY_START_BIT))
+
+#define AW88261_RSQN_DLY_35NS		(2)
+#define AW88261_RSQN_DLY_35NS_VALUE	\
+	(AW88261_RSQN_DLY_35NS << AW88261_RSQN_DLY_START_BIT)
+
+/* BURST_SSMODE bit 3 (BSTCTRL8 0x67) */
+#define AW88261_BURST_SSMODE_START_BIT	(3)
+#define AW88261_BURST_SSMODE_BITS_LEN	(1)
+#define AW88261_BURST_SSMODE_MASK	\
+	(~(((1<<AW88261_BURST_SSMODE_BITS_LEN)-1) << AW88261_BURST_SSMODE_START_BIT))
+
+#define AW88261_BURST_SSMODE_FAST	(0)
+#define AW88261_BURST_SSMODE_FAST_VALUE	\
+	(AW88261_BURST_SSMODE_FAST << AW88261_BURST_SSMODE_START_BIT)
+
+/* BST_BURST bit 9:7 (BSTCTRL9 0x68) */
+#define AW88261_BST_BURST_START_BIT	(7)
+#define AW88261_BST_BURST_BITS_LEN	(3)
+#define AW88261_BST_BURST_MASK		\
+	(~(((1<<AW88261_BST_BURST_BITS_LEN)-1) << AW88261_BST_BURST_START_BIT))
+
+#define AW88261_BST_BURST_30MA		(2)
+#define AW88261_BST_BURST_30MA_VALUE	\
+	(AW88261_BST_BURST_30MA << AW88261_BST_BURST_START_BIT)
+
+#define AW88261_EF_VSN_GESLP_SIGN_MASK		(~0x0200)
+#define AW88261_EF_VSN_GESLP_NEG		(~0xfc00)
+
+#define AW88261_EF_ISN_GESLP_SIGN_MASK		(~0x0200)
+#define AW88261_EF_ISN_GESLP_NEG		(~0xfc00)
+
+#define AW88261_EF_ISN_GESLP_H_START_BIT	(0)
+#define AW88261_EF_ISN_GESLP_H_BITS_LEN	(10)
+#define AW88261_EF_ISN_GESLP_H_MASK		\
+	(~(((1<<AW88261_EF_ISN_GESLP_H_BITS_LEN)-1) << AW88261_EF_ISN_GESLP_H_START_BIT))
+
+#define AW88261_EF_ISN_GESLP_L_START_BIT	(0)
+#define AW88261_EF_ISN_GESLP_L_BITS_LEN	(10)
+#define AW88261_EF_ISN_GESLP_L_MASK		\
+	(~(((1<<AW88261_EF_ISN_GESLP_L_BITS_LEN)-1) << AW88261_EF_ISN_GESLP_L_START_BIT))
+
+#define AW88261_CABL_BASE_VALUE	(1000)
+#define AW88261_ICABLK_FACTOR		(1)
+#define AW88261_VCABLK_FACTOR		(1)
+
+#define AW88261_VCAL_FACTOR		(1<<13)
+
+#define AW88261_TEMP_SIGN_MASK		(~(1<<9))
+#define AW88261_TEMP_NEG_MASK		(0xFC00)
+
+#endif
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH V1 5/5] ASoC: dt-bindings: Add schema for "awinic,aw88261"
  2023-07-17 11:58 [PATCH V1 0/5] ASoC: codecs: Add Awinic AW88261 audio amplifier driver wangweidong.a
                   ` (3 preceding siblings ...)
  2023-07-17 11:58 ` [PATCH V1 4/5] ASoC: codecs: aw88261 chip register file, data type file and Kconfig Makefile wangweidong.a
@ 2023-07-17 11:58 ` wangweidong.a
  2023-07-17 20:23   ` Krzysztof Kozlowski
  2023-07-17 13:03 ` [PATCH V1 0/5] ASoC: codecs: Add Awinic AW88261 audio amplifier driver Herve Codina
  5 siblings, 1 reply; 19+ messages in thread
From: wangweidong.a @ 2023-07-17 11:58 UTC (permalink / raw)
  To: lgirdwood, broonie, robh+dt, krzysztof.kozlowski+dt, conor+dt,
	perex, tiwai, rf, wangweidong.a, shumingf, herve.codina, flatmax,
	ckeepax, doug, fido_max, pierre-louis.bossart, kiseok.jo,
	liweilei, colin.i.king, trix, alsa-devel, devicetree,
	linux-kernel
  Cc: yijiangtao, zhangjianming

From: Weidong Wang <wangweidong.a@awinic.com>

Add a DT schema for describing Awinic aw88261 audio amplifiers. They are
controlled using I2C.

Signed-off-by: Weidong Wang <wangweidong.a@awinic.com>
---
 .../bindings/sound/awinic,aw88261.yaml        | 53 +++++++++++++++++++
 1 file changed, 53 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/awinic,aw88261.yaml

diff --git a/Documentation/devicetree/bindings/sound/awinic,aw88261.yaml b/Documentation/devicetree/bindings/sound/awinic,aw88261.yaml
new file mode 100644
index 000000000000..a0f3e3418c1e
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/awinic,aw88261.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,aw88261.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Awinic AW88261 Smart Audio Amplifier
+
+maintainers:
+  - Weidong Wang <wangweidong.a@awinic.com>
+
+description:
+  The Awinic AW88261 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,aw88261
+
+  reg:
+    maxItems: 1
+
+  '#sound-dai-cells':
+    const: 0
+
+  reset-gpios:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - '#sound-dai-cells'
+  - reset-gpios
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        audio-codec@34 {
+            compatible = "awinic,aw88261";
+            reg = <0x34>;
+            #sound-dai-cells = <0>;
+            reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>;
+        };
+    };
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH V1 4/5] ASoC: codecs: aw88261 chip register file, data type file and Kconfig Makefile
  2023-07-17 11:58 ` [PATCH V1 4/5] ASoC: codecs: aw88261 chip register file, data type file and Kconfig Makefile wangweidong.a
@ 2023-07-17 12:33   ` Randy Dunlap
  2023-07-19  2:59     ` wangweidong.a
  0 siblings, 1 reply; 19+ messages in thread
From: Randy Dunlap @ 2023-07-17 12:33 UTC (permalink / raw)
  To: wangweidong.a, lgirdwood, broonie, robh+dt,
	krzysztof.kozlowski+dt, conor+dt, perex, tiwai, rf, shumingf,
	herve.codina, flatmax, ckeepax, doug, fido_max,
	pierre-louis.bossart, kiseok.jo, liweilei, colin.i.king, trix,
	alsa-devel, devicetree, linux-kernel
  Cc: yijiangtao, zhangjianming



On 7/17/23 04:58, wangweidong.a@awinic.com wrote:
> +config SND_SOC_AW88261
> +	tristate "Soc Audio for awinic aw88261"

	                        Awininc
(as below)

> +	depends on I2C
> +	select CRC8
> +	select REGMAP_I2C
> +	select GPIOLIB
> +	select SND_SOC_AW88261_LIB
> +	help
> +	  this option enables support for aw88261 Smart PA.

	  This

> +	  The Awinic AW88261 is an I2S/TDM input, high efficiency
> +	  digital Smart K audio amplifier with an integrated 10.25V
> +		smart boost convert.

Less indentation on the last line (align it with the previous line).

-- 
~Randy

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V1 0/5]  ASoC: codecs: Add Awinic AW88261 audio amplifier driver
  2023-07-17 11:58 [PATCH V1 0/5] ASoC: codecs: Add Awinic AW88261 audio amplifier driver wangweidong.a
                   ` (4 preceding siblings ...)
  2023-07-17 11:58 ` [PATCH V1 5/5] ASoC: dt-bindings: Add schema for "awinic,aw88261" wangweidong.a
@ 2023-07-17 13:03 ` Herve Codina
  5 siblings, 0 replies; 19+ messages in thread
From: Herve Codina @ 2023-07-17 13:03 UTC (permalink / raw)
  Cc: shumingf, flatmax, ckeepax, doug, fido_max, pierre-louis.bossart,
	kiseok.jo, liweilei, colin.i.king, trix, alsa-devel, devicetree,
	linux-kernel, yijiangtao, zhangjianming

Hi Weidong,

On Mon, 17 Jul 2023 19:58:40 +0800
wangweidong.a@awinic.com wrote:

> From: Weidong Wang <wangweidong.a@awinic.com>
> 
> The AW88261 is an I2S/TDM input, high efficiency
> digital Smart K audio amplifier with an integrated 10.25V
> smart boost convert
> 
> Add a DT schema for describing Awinic aw88261 audio amplifiers. They are
> controlled using I2C.
> 
> Weidong Wang (5):
>   ASoC: codecs: Add i2c and codec registration for aw88261 and their
>     associated operation functions
>   ASoC: codecs: aw88261 function for ALSA Audio Driver
>   ASoC: codecs: ACF bin parsing and check library file for aw88261
>   ASoC: codecs: aw88261 chip register file, data type file and Kconfig
>     Makefile
>   ASoC: dt-bindings: Add schema for "awinic,aw88261"
> 
>  .../bindings/sound/awinic,aw88261.yaml        |  53 +
>  sound/soc/codecs/Kconfig                      |  17 +
>  sound/soc/codecs/Makefile                     |   5 +
>  sound/soc/codecs/aw88261/aw88261.c            | 539 ++++++++++
>  sound/soc/codecs/aw88261/aw88261.h            |  52 +
>  sound/soc/codecs/aw88261/aw88261_data_type.h  | 144 +++
>  sound/soc/codecs/aw88261/aw88261_device.c     | 959 +++++++++++++++++
>  sound/soc/codecs/aw88261/aw88261_device.h     | 114 ++
>  sound/soc/codecs/aw88261/aw88261_lib.c        | 997 ++++++++++++++++++
>  sound/soc/codecs/aw88261/aw88261_lib.h        |  91 ++
>  sound/soc/codecs/aw88261/aw88261_reg.h        | 377 +++++++
>  11 files changed, 3348 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/sound/awinic,aw88261.yaml
>  create mode 100644 sound/soc/codecs/aw88261/aw88261.c
>  create mode 100644 sound/soc/codecs/aw88261/aw88261.h
>  create mode 100644 sound/soc/codecs/aw88261/aw88261_data_type.h
>  create mode 100644 sound/soc/codecs/aw88261/aw88261_device.c
>  create mode 100644 sound/soc/codecs/aw88261/aw88261_device.h
>  create mode 100644 sound/soc/codecs/aw88261/aw88261_lib.c
>  create mode 100644 sound/soc/codecs/aw88261/aw88261_lib.h
>  create mode 100644 sound/soc/codecs/aw88261/aw88261_reg.h
> 
> 
> base-commit: 8d7868c41df58edabc4e408d119a1aef58a54d9d

A lot of code looks pretty similar (the same ?) to what was done for aw88395
  sound/soc/codecs/aw88395/

Is there a way to use common code instead of duplication ?

Best regards,
Hervé Codina

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V1 5/5] ASoC: dt-bindings: Add schema for "awinic,aw88261"
  2023-07-17 11:58 ` [PATCH V1 5/5] ASoC: dt-bindings: Add schema for "awinic,aw88261" wangweidong.a
@ 2023-07-17 20:23   ` Krzysztof Kozlowski
  2023-07-19  3:02     ` wangweidong.a
  0 siblings, 1 reply; 19+ messages in thread
From: Krzysztof Kozlowski @ 2023-07-17 20:23 UTC (permalink / raw)
  To: wangweidong.a, lgirdwood, broonie, robh+dt,
	krzysztof.kozlowski+dt, conor+dt, perex, tiwai, rf, shumingf,
	herve.codina, flatmax, ckeepax, doug, fido_max,
	pierre-louis.bossart, kiseok.jo, liweilei, colin.i.king, trix,
	alsa-devel, devicetree, linux-kernel
  Cc: yijiangtao, zhangjianming

On 17/07/2023 13:58, wangweidong.a@awinic.com wrote:
> From: Weidong Wang <wangweidong.a@awinic.com>
> 
> Add a DT schema for describing Awinic aw88261 audio amplifiers. They are
> controlled using I2C.
> 
> Signed-off-by: Weidong Wang <wangweidong.a@awinic.com>

...

> +  compatible:
> +    const: awinic,aw88261
> +
> +  reg:
> +    maxItems: 1
> +
> +  '#sound-dai-cells':
> +    const: 0
> +
> +  reset-gpios:
> +    maxItems: 1
> +
> +required:
> +  - compatible
> +  - reg
> +  - '#sound-dai-cells'
> +  - reset-gpios

I don't see any differences against AW88395. Add the compatible to that
schema.

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V1 1/5] ASoC: codecs: Add i2c and codec registration for aw88261 and their associated operation functions
  2023-07-17 11:58 ` [PATCH V1 1/5] ASoC: codecs: Add i2c and codec registration for aw88261 and their associated operation functions wangweidong.a
@ 2023-07-17 20:33   ` Krzysztof Kozlowski
  2023-07-19  3:06     ` wangweidong.a
  0 siblings, 1 reply; 19+ messages in thread
From: Krzysztof Kozlowski @ 2023-07-17 20:33 UTC (permalink / raw)
  To: wangweidong.a, lgirdwood, broonie, robh+dt,
	krzysztof.kozlowski+dt, conor+dt, perex, tiwai, rf, shumingf,
	herve.codina, flatmax, ckeepax, doug, fido_max,
	pierre-louis.bossart, kiseok.jo, liweilei, colin.i.king, trix,
	alsa-devel, devicetree, linux-kernel
  Cc: yijiangtao, zhangjianming

On 17/07/2023 13:58, wangweidong.a@awinic.com wrote:
> From: Weidong Wang <wangweidong.a@awinic.com>
> 
> The AW88261 is an I2S/TDM input, high efficiency
> digital Smart K audio amplifier with an integrated 10.25V
> smart boost convert
> 

...

> +
> +static int aw88261_request_firmware_file(struct aw88261 *aw88261)
> +{
> +	const struct firmware *cont = NULL;
> +	int ret;
> +
> +	aw88261->aw_pa->fw_status = AW88261_DEV_FW_FAILED;
> +
> +	ret = request_firmware(&cont, AW88261_ACF_FILE, aw88261->aw_pa->dev);
> +	if ((ret < 0) || (!cont)) {
> +		dev_err(aw88261->aw_pa->dev, "load [%s] failed!", AW88261_ACF_FILE);
> +		return ret;

return dev_err_probe?

> +	}
> +
> +	dev_info(aw88261->aw_pa->dev, "loaded %s - size: %zu\n",
> +			AW88261_ACF_FILE, cont ? cont->size : 0);> +
> +	aw88261->aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL);
> +	if (!aw88261->aw_cfg) {
> +		release_firmware(cont);
> +		return -ENOMEM;
> +	}
> +	aw88261->aw_cfg->len = (int)cont->size;
> +	memcpy(aw88261->aw_cfg->data, cont->data, cont->size);
> +	release_firmware(cont);
> +
> +	ret = aw88261_dev_load_acf_check(aw88261->aw_pa, aw88261->aw_cfg);
> +	if (ret < 0) {
> +		dev_err(aw88261->aw_pa->dev, "Load [%s] failed ....!", AW88261_ACF_FILE);
> +		return ret;
> +	}
> +
> +	dev_dbg(aw88261->aw_pa->dev, "%s : bin load success\n", __func__);

Drop dev_dbg on simple probe success/failure. There is tracing
infrastructure for this.

> +
> +	mutex_lock(&aw88261->lock);
> +	/* aw device init */
> +	ret = aw88261_dev_init(aw88261->aw_pa, aw88261->aw_cfg);
> +	if (ret < 0)
> +		dev_err(aw88261->aw_pa->dev, "dev init failed");
> +	mutex_unlock(&aw88261->lock);
> +
> +	return ret;
> +}
> +
> +static int aw88261_codec_probe(struct snd_soc_component *component)
> +{
> +	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
> +	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
> +	int ret;
> +
> +	INIT_DELAYED_WORK(&aw88261->start_work, aw88261_startup_work);
> +
> +	ret = aw88261_request_firmware_file(aw88261);
> +	if (ret < 0) {
> +		dev_err(aw88261->aw_pa->dev, "aw88261_request_firmware_file failed\n");
> +		return ret;

Consider here dev_err_probe, to annotate possibility of probe deferal
(e.g. missing rootfs). I think component probe is called for device probes?

> +	}
> +
> +	/* add widgets */
> +	ret = snd_soc_dapm_new_controls(dapm, aw88261_dapm_widgets,
> +							ARRAY_SIZE(aw88261_dapm_widgets));
> +	if (ret < 0)
> +		return ret;
> +
> +	/* add route */
> +	ret = snd_soc_dapm_add_routes(dapm, aw88261_audio_map,
> +							ARRAY_SIZE(aw88261_audio_map));
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = snd_soc_add_component_controls(component, aw88261_controls,
> +							ARRAY_SIZE(aw88261_controls));
> +
> +	return ret;
> +}
> +

> +static void aw88261_codec_remove(struct snd_soc_component *aw_codec)
> +{
> +	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(aw_codec);
> +
> +	cancel_delayed_work_sync(&aw88261->start_work);
> +}
> +
> +
> +static void aw88261_hw_reset(struct aw88261 *aw88261)
> +{
> +	gpiod_set_value_cansleep(aw88261->reset_gpio, 0);
> +	usleep_range(AW88261_1000_US, AW88261_1000_US + 10);
> +	gpiod_set_value_cansleep(aw88261->reset_gpio, 1);
> +	usleep_range(AW88261_1000_US, AW88261_1000_US + 10);
> +}
> +
> +static int aw88261_i2c_probe(struct i2c_client *i2c)
> +{
> +	struct aw88261 *aw88261;
> +	int ret;
> +
> +	if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) {
> +		dev_err(&i2c->dev, "check_functionality failed");
> +		return -EIO;
> +	}
> +
> +	aw88261 = aw88261_malloc_init(i2c);
> +	if (!aw88261) {
> +		dev_err(&i2c->dev, "malloc aw88261 failed");

Do not print messages on allocation errors.

Also, there is little sense in moving one or two functions - kzalloc -
to separate function. It does not make the code easier to read.

> +		return -ENOMEM;
> +	}
> +	i2c_set_clientdata(i2c, aw88261);
> +
> +	aw88261->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(aw88261->reset_gpio))
> +		dev_info(&i2c->dev, "reset gpio not defined\n");
> +	else
> +		aw88261_hw_reset(aw88261);
> +
> +	aw88261->regmap = devm_regmap_init_i2c(i2c, &aw88261_remap_config);
> +	if (IS_ERR(aw88261->regmap)) {
> +		ret = PTR_ERR(aw88261->regmap);
> +		dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
> +		return ret;

return dev_err_probe

> +	}
> +
> +	/* aw pa init */
> +	ret = aw88261_init(&aw88261->aw_pa, i2c, aw88261->regmap);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = devm_snd_soc_register_component(&i2c->dev,
> +			&soc_codec_dev_aw88261,
> +			aw88261_dai, ARRAY_SIZE(aw88261_dai));
> +	if (ret < 0)
> +		dev_err(&i2c->dev, "failed to register aw88261: %d", ret);
> +
> +	return ret;
> +}
> +
> +#ifdef CONFIG_OF

Drop

> +static const struct of_device_id aw88261_of_match[] = {
> +	{ .compatible = "awinic,aw88261" },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, aw88261_of_match);
> +#endif
> +
> +static const struct i2c_device_id aw88261_i2c_id[] = {
> +	{ AW88261_I2C_NAME, 0 },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, aw88261_i2c_id);
> +
> +static struct i2c_driver aw88261_i2c_driver = {
> +	.driver = {
> +		.name = AW88261_I2C_NAME,
> +		.of_match_table = of_match_ptr(aw88261_of_match),

Drop of_match_ptr.


Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V1 2/5] ASoC: codecs: aw88261 function for ALSA Audio Driver
  2023-07-17 11:58 ` [PATCH V1 2/5] ASoC: codecs: aw88261 function for ALSA Audio Driver wangweidong.a
@ 2023-07-17 20:39   ` Krzysztof Kozlowski
  2023-07-19  3:09     ` wangweidong.a
  0 siblings, 1 reply; 19+ messages in thread
From: Krzysztof Kozlowski @ 2023-07-17 20:39 UTC (permalink / raw)
  To: wangweidong.a, lgirdwood, broonie, robh+dt,
	krzysztof.kozlowski+dt, conor+dt, perex, tiwai, rf, shumingf,
	herve.codina, flatmax, ckeepax, doug, fido_max,
	pierre-louis.bossart, kiseok.jo, liweilei, colin.i.king, trix,
	alsa-devel, devicetree, linux-kernel
  Cc: yijiangtao, zhangjianming

On 17/07/2023 13:58, wangweidong.a@awinic.com wrote:
> From: Weidong Wang <wangweidong.a@awinic.com>
> 
> The AW88261 is an I2S/TDM input, high efficiency
> digital Smart K audio amplifier with an integrated 10.25V
> smart boost convert

It's the same as in patch before. This does not help and does not
justify having one driver split into two.

> 

...

> +
> +static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
> +{
> +	int ret;
> +
> +	if (flag) {
> +		ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG,
> +			~AW88261_I2STXEN_MASK, AW88261_I2STXEN_ENABLE_VALUE);
> +	} else {
> +		ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG,
> +			~AW88261_I2STXEN_MASK, AW88261_I2STXEN_DISABLE_VALUE);
> +	}

You should not need {} here and in multiple other places.

> +	if (ret)
> +		dev_dbg(aw_dev->dev, "%s failed", __func__);

Why you are not handling the errors properly?

> +}
> +
> +static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd)
> +{
> +	int ret;
> +
> +	if (pwd) {
> +		ret = regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
> +				~AW88261_PWDN_MASK,	AW88261_PWDN_POWER_DOWN_VALUE);
> +	} else {
> +		ret = regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
> +				~AW88261_PWDN_MASK,	AW88261_PWDN_WORKING_VALUE);
> +	}
> +	if (ret)
> +		dev_dbg(aw_dev->dev, "%s failed", __func__);
> +}
> +

...

> +
> +int aw88261_dev_fw_update(struct aw_device *aw_dev)
> +{
> +	struct aw_prof_desc *prof_index_desc;
> +	struct aw_sec_data_desc *sec_desc;
> +	char *prof_name;
> +	int ret;
> +
> +	prof_name = aw88261_dev_get_prof_name(aw_dev, aw_dev->prof_index);
> +	if (!prof_name) {
> +		dev_err(aw_dev->dev, "get prof name failed");
> +		return -EINVAL;
> +	}
> +
> +	dev_dbg(aw_dev->dev, "start update %s", prof_name);
> +
> +	ret = aw88261_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc);
> +	if (ret)
> +		return ret;
> +
> +	/* update reg */
> +	sec_desc = prof_index_desc->sec_desc;
> +	ret = aw_dev_reg_update(aw_dev, sec_desc[AW88261_DATA_TYPE_REG].data,
> +					sec_desc[AW88261_DATA_TYPE_REG].len);
> +	if (ret) {
> +		dev_err(aw_dev->dev, "update reg failed");
> +		return ret;
> +	}
> +
> +	aw_dev->prof_cur = aw_dev->prof_index;
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(aw88261_dev_fw_update);

Why do you need to export this? Where is the user?

> +
> +int aw88261_dev_reg_update(struct aw_device *aw_dev, bool force)
> +{
> +	int ret;
> +
> +	if (force) {
> +		aw88261_dev_soft_reset(aw_dev);
> +		ret = aw88261_dev_fw_update(aw_dev);
> +		if (ret < 0)
> +			return ret;
> +	} else {
> +		if (aw_dev->prof_cur != aw_dev->prof_index) {
> +			ret = aw88261_dev_fw_update(aw_dev);
> +			if (ret < 0)
> +				return ret;
> +		}
> +	}
> +
> +	aw_dev->prof_cur = aw_dev->prof_index;
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(aw88261_dev_reg_update);

Same question. And in all other places as well.


Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V1 3/5] ASoC: codecs: ACF bin parsing and check library file for aw88261
  2023-07-17 11:58 ` [PATCH V1 3/5] ASoC: codecs: ACF bin parsing and check library file for aw88261 wangweidong.a
@ 2023-07-17 20:40   ` Krzysztof Kozlowski
  2023-07-19  3:12     ` wangweidong.a
  0 siblings, 1 reply; 19+ messages in thread
From: Krzysztof Kozlowski @ 2023-07-17 20:40 UTC (permalink / raw)
  To: wangweidong.a, lgirdwood, broonie, robh+dt,
	krzysztof.kozlowski+dt, conor+dt, perex, tiwai, rf, shumingf,
	herve.codina, flatmax, ckeepax, doug, fido_max,
	pierre-louis.bossart, kiseok.jo, liweilei, colin.i.king, trix,
	alsa-devel, devicetree, linux-kernel
  Cc: yijiangtao, zhangjianming

On 17/07/2023 13:58, wangweidong.a@awinic.com wrote:
> From: Weidong Wang <wangweidong.a@awinic.com>
> 
> The AW88261 is an I2S/TDM input, high efficiency
> digital Smart K audio amplifier with an integrated 10.25V
> smart boost convert

So all your commits are doing the same?

> 
> Signed-off-by: Weidong Wang <wangweidong.a@awinic.com>
> ---
>  sound/soc/codecs/aw88261/aw88261_lib.c | 997 +++++++++++++++++++++++++
>  sound/soc/codecs/aw88261/aw88261_lib.h |  91 +++
>  2 files changed, 1088 insertions(+)
>  create mode 100644 sound/soc/codecs/aw88261/aw88261_lib.c
>  create mode 100644 sound/soc/codecs/aw88261/aw88261_lib.h
> 
> diff --git a/sound/soc/codecs/aw88261/aw88261_lib.c b/sound/soc/codecs/aw88261/aw88261_lib.c
> new file mode 100644
> index 000000000000..b8f00708dacf
> --- /dev/null
> +++ b/sound/soc/codecs/aw88261/aw88261_lib.c
> @@ -0,0 +1,997 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +//
> +// aw88261_lib.c  -- ACF bin parsing and check library file for aw88261
> +//
> +// Copyright (c) 2023 awinic Technology CO., LTD
> +//
> +// Author: Jimmy Zhang <zhangjianming@awinic.com>
> +// Author: Weidong Wang <wangweidong.a@awinic.com>
> +//
> +
> +#include <linux/crc8.h>
> +#include <linux/i2c.h>
> +#include "aw88261_lib.h"
> +#include "aw88261_device.h"
> +
> +#define AW88261_CRC8_POLYNOMIAL 0x8C
> +DECLARE_CRC8_TABLE(aw_crc8_table);
> +
> +static char *profile_name[AW88261_PROFILE_MAX] = {

Cannot be const char *?

...

> +EXPORT_SYMBOL_GPL(aw88261_dev_load_acf_check);

Why?



Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V1 4/5] ASoC: codecs: aw88261 chip register file, data type file and Kconfig Makefile
  2023-07-17 12:33   ` Randy Dunlap
@ 2023-07-19  2:59     ` wangweidong.a
  0 siblings, 0 replies; 19+ messages in thread
From: wangweidong.a @ 2023-07-19  2:59 UTC (permalink / raw)
  To: rdunlap
  Cc: alsa-devel, broonie, ckeepax, colin.i.king, conor+dt, devicetree,
	doug, fido_max, flatmax, herve.codina, kiseok.jo,
	krzysztof.kozlowski+dt, lgirdwood, linux-kernel, liweilei, perex,
	pierre-louis.bossart, rf, robh+dt, shumingf, tiwai, trix,
	wangweidong.a, yijiangtao, zhangjianming

Hi, Randy Dunlap,
 Thank you very much for your advice

> On 7/17/23 04:58, wangweidong.a@awinic.com wrote:
>> +config SND_SOC_AW88261
>> +	tristate "Soc Audio for awinic aw88261"

>	                        Awininc
> (as below)

I will modify it as suggested.

>> +	depends on I2C
>> +	select CRC8
>> +	select REGMAP_I2C
>> +	select GPIOLIB
>> +	select SND_SOC_AW88261_LIB
>> +	help
>> +	  this option enables support for aw88261 Smart PA.

	  This

>> +	  The Awinic AW88261 is an I2S/TDM input, high efficiency
>> +	  digital Smart K audio amplifier with an integrated 10.25V
>> +		smart boost convert.

> Less indentation on the last line (align it with the previous line).

I will modify it as suggested.

Best regards,
Weidong Wang

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V1 5/5] ASoC: dt-bindings: Add schema for "awinic,aw88261"
  2023-07-17 20:23   ` Krzysztof Kozlowski
@ 2023-07-19  3:02     ` wangweidong.a
  0 siblings, 0 replies; 19+ messages in thread
From: wangweidong.a @ 2023-07-19  3:02 UTC (permalink / raw)
  To: krzysztof.kozlowski
  Cc: alsa-devel, broonie, ckeepax, colin.i.king, conor+dt, devicetree,
	doug, fido_max, flatmax, herve.codina, kiseok.jo,
	krzysztof.kozlowski+dt, lgirdwood, linux-kernel, liweilei, perex,
	pierre-louis.bossart, rf, robh+dt, shumingf, tiwai, trix,
	wangweidong.a, yijiangtao, zhangjianming

Hi, Krzysztof,
Thank you very much for your advice.

On 18/07/2023 16:24, krzysztof.kozlowski@linaro.org wrote:
> On 17/07/2023 13:58, wangweidong.a@awinic.com wrote:
>> From: Weidong Wang <wangweidong.a@awinic.com>
>> 
>> Add a DT schema for describing Awinic aw88261 audio amplifiers. They are
>> controlled using I2C.
>> 
>> Signed-off-by: Weidong Wang <wangweidong.a@awinic.com>

...

>> +  compatible:
>> +    const: awinic,aw88261
>> +
>> +  reg:
>> +    maxItems: 1
>> +
>> +  '#sound-dai-cells':
>> +    const: 0
>> +
>> +  reset-gpios:
>> +    maxItems: 1
>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +  - '#sound-dai-cells'
>> +  - reset-gpios

> I don't see any differences against AW88395. Add the compatible to that
> schema.

I will add "awinic,aw88261" to the awinic,aw88395.yaml

Best regards,
Weidong Wang

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V1 1/5] ASoC: codecs: Add i2c and codec registration for aw88261 and their associated operation functions
  2023-07-17 20:33   ` Krzysztof Kozlowski
@ 2023-07-19  3:06     ` wangweidong.a
  0 siblings, 0 replies; 19+ messages in thread
From: wangweidong.a @ 2023-07-19  3:06 UTC (permalink / raw)
  To: krzysztof.kozlowski
  Cc: alsa-devel, broonie, ckeepax, colin.i.king, conor+dt, devicetree,
	doug, fido_max, flatmax, herve.codina, kiseok.jo,
	krzysztof.kozlowski+dt, lgirdwood, linux-kernel, liweilei, perex,
	pierre-louis.bossart, rf, robh+dt, shumingf, tiwai, trix,
	wangweidong.a, yijiangtao, zhangjianming

Hi, Krzysztof,
Thank you very much for your advice.

On 18/07/2023 16:39, krzysztof.kozlowski@linaro.org wrote:
> On 17/07/2023 13:58, wangweidong.a@awinic.com wrote:
>> From: Weidong Wang <wangweidong.a@awinic.com>
>> 
>> The AW88261 is an I2S/TDM input, high efficiency
>> digital Smart K audio amplifier with an integrated 10.25V
>> smart boost convert
>> 

...

>> +
>> +static int aw88261_request_firmware_file(struct aw88261 *aw88261)
>> +{
>> +	const struct firmware *cont = NULL;
>> +	int ret;
>> +
>> +	aw88261->aw_pa->fw_status = AW88261_DEV_FW_FAILED;
>> +
>> +	ret = request_firmware(&cont, AW88261_ACF_FILE, aw88261->aw_pa->dev);
>> +	if ((ret < 0) || (!cont)) {
>> +		dev_err(aw88261->aw_pa->dev, "load [%s] failed!", AW88261_ACF_FILE);
>> +		return ret;

>return dev_err_probe?

I will modify it as suggested.

>> +	}
>> +
>> +	dev_info(aw88261->aw_pa->dev, "loaded %s - size: %zu\n",
>> +			AW88261_ACF_FILE, cont ? cont->size : 0);> +
>> +	aw88261->aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL);
>> +	if (!aw88261->aw_cfg) {
>> +		release_firmware(cont);
>> +		return -ENOMEM;
>> +	}
>> +	aw88261->aw_cfg->len = (int)cont->size;
>> +	memcpy(aw88261->aw_cfg->data, cont->data, cont->size);
>> +	release_firmware(cont);
>> +
>> +	ret = aw88261_dev_load_acf_check(aw88261->aw_pa, aw88261->aw_cfg);
>> +	if (ret < 0) {
>> +		dev_err(aw88261->aw_pa->dev, "Load [%s] failed ....!", AW88261_ACF_FILE);
>> +		return ret;
>> +	}
>> +
>> +	dev_dbg(aw88261->aw_pa->dev, "%s : bin load success\n", __func__);

> Drop dev_dbg on simple probe success/failure. There is tracing
> infrastructure for this.

I will delete this print statement.

>> +
>> +	mutex_lock(&aw88261->lock);
>> +	/* aw device init */
>> +	ret = aw88261_dev_init(aw88261->aw_pa, aw88261->aw_cfg);
>> +	if (ret < 0)
>> +		dev_err(aw88261->aw_pa->dev, "dev init failed");
>> +	mutex_unlock(&aw88261->lock);
>> +
>> +	return ret;
>> +}
>> +
>> +static int aw88261_codec_probe(struct snd_soc_component *component)
>> +{
>> +	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
>> +	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
>> +	int ret;
>> +
>> +	INIT_DELAYED_WORK(&aw88261->start_work, aw88261_startup_work);
>> +
>> +	ret = aw88261_request_firmware_file(aw88261);
>> +	if (ret < 0) {
>> +		dev_err(aw88261->aw_pa->dev, "aw88261_request_firmware_file failed\n");
>> +		return ret;

> Consider here dev_err_probe, to annotate possibility of probe deferal
> (e.g. missing rootfs). I think component probe is called for device probes?

I will modify it as suggested.

>> +	}
>> +
>> +	/* add widgets */
>> +	ret = snd_soc_dapm_new_controls(dapm, aw88261_dapm_widgets,
>> +							ARRAY_SIZE(aw88261_dapm_widgets));
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	/* add route */
>> +	ret = snd_soc_dapm_add_routes(dapm, aw88261_audio_map,
>> +							ARRAY_SIZE(aw88261_audio_map));
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = snd_soc_add_component_controls(component, aw88261_controls,
>> +							ARRAY_SIZE(aw88261_controls));
>> +
>> +	return ret;
>> +}
>> +

>> +static void aw88261_codec_remove(struct snd_soc_component *aw_codec)
>> +{
>> +	struct aw88261 *aw88261 = snd_soc_component_get_drvdata(aw_codec);
>> +
>> +	cancel_delayed_work_sync(&aw88261->start_work);
>> +}
>> +
>> +
>> +static void aw88261_hw_reset(struct aw88261 *aw88261)
>> +{
>> +	gpiod_set_value_cansleep(aw88261->reset_gpio, 0);
>> +	usleep_range(AW88261_1000_US, AW88261_1000_US + 10);
>> +	gpiod_set_value_cansleep(aw88261->reset_gpio, 1);
>> +	usleep_range(AW88261_1000_US, AW88261_1000_US + 10);
>> +}
>> +
>> +static int aw88261_i2c_probe(struct i2c_client *i2c)
>> +{
>> +	struct aw88261 *aw88261;
>> +	int ret;
>> +
>> +	if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) {
>> +		dev_err(&i2c->dev, "check_functionality failed");
>> +		return -EIO;
>> +	}
>> +
>> +	aw88261 = aw88261_malloc_init(i2c);
>> +	if (!aw88261) {
>> +		dev_err(&i2c->dev, "malloc aw88261 failed");

> Do not print messages on allocation errors.

> Also, there is little sense in moving one or two functions - kzalloc -
> to separate function. It does not make the code easier to read.

I will modify it as suggested.

>> +		return -ENOMEM;
>> +	}
>> +	i2c_set_clientdata(i2c, aw88261);
>> +
>> +	aw88261->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW);
>> +	if (IS_ERR(aw88261->reset_gpio))
>> +		dev_info(&i2c->dev, "reset gpio not defined\n");
>> +	else
>> +		aw88261_hw_reset(aw88261);
>> +
>> +	aw88261->regmap = devm_regmap_init_i2c(i2c, &aw88261_remap_config);
>> +	if (IS_ERR(aw88261->regmap)) {
>> +		ret = PTR_ERR(aw88261->regmap);
>> +		dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
>> +		return ret;

> return dev_err_probe

I will modify it as suggested.

>> +	}
>> +
>> +	/* aw pa init */
>> +	ret = aw88261_init(&aw88261->aw_pa, i2c, aw88261->regmap);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = devm_snd_soc_register_component(&i2c->dev,
>> +			&soc_codec_dev_aw88261,
>> +			aw88261_dai, ARRAY_SIZE(aw88261_dai));
>> +	if (ret < 0)
>> +		dev_err(&i2c->dev, "failed to register aw88261: %d", ret);
>> +
>> +	return ret;
>> +}
>> +
>> +#ifdef CONFIG_OF

> Drop

I will modify it as suggested.

>> +static const struct of_device_id aw88261_of_match[] = {
>> +	{ .compatible = "awinic,aw88261" },
>> +	{ },
>> +};
>> +MODULE_DEVICE_TABLE(of, aw88261_of_match);
>> +#endif
>> +
>> +static const struct i2c_device_id aw88261_i2c_id[] = {
>> +	{ AW88261_I2C_NAME, 0 },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(i2c, aw88261_i2c_id);
>> +
>> +static struct i2c_driver aw88261_i2c_driver = {
>> +	.driver = {
>> +		.name = AW88261_I2C_NAME,
>> +		.of_match_table = of_match_ptr(aw88261_of_match),

> Drop of_match_ptr.

I will modify it as suggested.

Best regards,
Weidong Wang

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V1 2/5] ASoC: codecs: aw88261 function for ALSA Audio Driver
  2023-07-17 20:39   ` Krzysztof Kozlowski
@ 2023-07-19  3:09     ` wangweidong.a
  2023-07-19  6:24       ` Krzysztof Kozlowski
  0 siblings, 1 reply; 19+ messages in thread
From: wangweidong.a @ 2023-07-19  3:09 UTC (permalink / raw)
  To: krzysztof.kozlowski
  Cc: alsa-devel, broonie, ckeepax, colin.i.king, conor+dt, devicetree,
	doug, fido_max, flatmax, herve.codina, kiseok.jo,
	krzysztof.kozlowski+dt, lgirdwood, linux-kernel, liweilei, perex,
	pierre-louis.bossart, rf, robh+dt, shumingf, tiwai, trix,
	wangweidong.a, yijiangtao, zhangjianming

Hi, Krzysztof,
Thank you very much for your advice, 
 but I have a few questions I'd like to discuss with you

On 18/07/2023 16:58, krzysztof.kozlowski@linaro.org wrote:
> On 17/07/2023 13:58, wangweidong.a@awinic.com wrote:
>> From: Weidong Wang <wangweidong.a@awinic.com>
>> 
>> The AW88261 is an I2S/TDM input, high efficiency
>> digital Smart K audio amplifier with an integrated 10.25V
>> smart boost convert

> It's the same as in patch before. This does not help and does not
> justify having one driver split into two.

I will modify the commit information and differentiate the commit 
 information for each file

>> 

...

>> +
>> +static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
>> +{
>> +	int ret;
>> +
>> +	if (flag) {
>> +		ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG,
>> +			~AW88261_I2STXEN_MASK, AW88261_I2STXEN_ENABLE_VALUE);
>> +	} else {
>> +		ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG,
>> +			~AW88261_I2STXEN_MASK, AW88261_I2STXEN_DISABLE_VALUE);
>> +	}

> You should not need {} here and in multiple other places.

I will delete {} as suggested

>> +	if (ret)
>> +		dev_dbg(aw_dev->dev, "%s failed", __func__);

> Why you are not handling the errors properly?

Do you need to use dev_err instead?

>> +}
>> +
>> +static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd)
>> +{
>> +	int ret;
>> +
>> +	if (pwd) {
>> +		ret = regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
>> +				~AW88261_PWDN_MASK,	AW88261_PWDN_POWER_DOWN_VALUE);
>> +	} else {
>> +		ret = regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
>> +				~AW88261_PWDN_MASK,	AW88261_PWDN_WORKING_VALUE);
>> +	}
>> +	if (ret)
>> +		dev_dbg(aw_dev->dev, "%s failed", __func__);
>> +}
>> +

...

>> +
>> +int aw88261_dev_fw_update(struct aw_device *aw_dev)
>> +{
>> +	struct aw_prof_desc *prof_index_desc;
>> +	struct aw_sec_data_desc *sec_desc;
>> +	char *prof_name;
>> +	int ret;
>> +
>> +	prof_name = aw88261_dev_get_prof_name(aw_dev, aw_dev->prof_index);
>> +	if (!prof_name) {
>> +		dev_err(aw_dev->dev, "get prof name failed");
>> +		return -EINVAL;
>> +	}
>> +
>> +	dev_dbg(aw_dev->dev, "start update %s", prof_name);
>> +
>> +	ret = aw88261_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* update reg */
>> +	sec_desc = prof_index_desc->sec_desc;
>> +	ret = aw_dev_reg_update(aw_dev, sec_desc[AW88261_DATA_TYPE_REG].data,
>> +					sec_desc[AW88261_DATA_TYPE_REG].len);
>> +	if (ret) {
>> +		dev_err(aw_dev->dev, "update reg failed");
>> +		return ret;
>> +	}
>> +
>> +	aw_dev->prof_cur = aw_dev->prof_index;
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(aw88261_dev_fw_update);

> Why do you need to export this? Where is the user?

I will delete this export

>> +
>> +int aw88261_dev_reg_update(struct aw_device *aw_dev, bool force)
>> +{
>> +	int ret;
>> +
>> +	if (force) {
>> +		aw88261_dev_soft_reset(aw_dev);
>> +		ret = aw88261_dev_fw_update(aw_dev);
>> +		if (ret < 0)
>> +			return ret;
>> +	} else {
>> +		if (aw_dev->prof_cur != aw_dev->prof_index) {
>> +			ret = aw88261_dev_fw_update(aw_dev);
>> +			if (ret < 0)
>> +				return ret;
>> +		}
>> +	}
>> +
>> +	aw_dev->prof_cur = aw_dev->prof_index;
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(aw88261_dev_reg_update);

> Same question. And in all other places as well.

This function will be called in aw88261.c, can I keep it?

Best regards,
Weidong Wang

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V1 3/5] ASoC: codecs: ACF bin parsing and check library file for aw88261
  2023-07-17 20:40   ` Krzysztof Kozlowski
@ 2023-07-19  3:12     ` wangweidong.a
  2023-07-19  6:23       ` Krzysztof Kozlowski
  0 siblings, 1 reply; 19+ messages in thread
From: wangweidong.a @ 2023-07-19  3:12 UTC (permalink / raw)
  To: krzysztof.kozlowski
  Cc: alsa-devel, broonie, ckeepax, colin.i.king, conor+dt, devicetree,
	doug, fido_max, flatmax, herve.codina, kiseok.jo,
	krzysztof.kozlowski+dt, lgirdwood, linux-kernel, liweilei, perex,
	pierre-louis.bossart, rf, robh+dt, shumingf, tiwai, trix,
	wangweidong.a, yijiangtao, zhangjianming

Hi, Krzysztof,
Thank you very much for your advice, 
 but I have a few questions I'd like to discuss with you

On 18/07/2023 16:41, krzysztof.kozlowski@linaro.org wrote:
> On 17/07/2023 13:58, wangweidong.a@awinic.com wrote:
>> From: Weidong Wang <wangweidong.a@awinic.com>
>> 
>> The AW88261 is an I2S/TDM input, high efficiency
>> digital Smart K audio amplifier with an integrated 10.25V
>> smart boost convert

> So all your commits are doing the same?

I will modify the commit information and differentiate the commit 
 information for each file

>> 
>> Signed-off-by: Weidong Wang <wangweidong.a@awinic.com>
>> ---
>>  sound/soc/codecs/aw88261/aw88261_lib.c | 997 +++++++++++++++++++++++++
>>  sound/soc/codecs/aw88261/aw88261_lib.h |  91 +++
>>  2 files changed, 1088 insertions(+)
>>  create mode 100644 sound/soc/codecs/aw88261/aw88261_lib.c
>>  create mode 100644 sound/soc/codecs/aw88261/aw88261_lib.h
>> 
>> diff --git a/sound/soc/codecs/aw88261/aw88261_lib.c b/sound/soc/codecs/aw88261/aw88261_lib.c
>> new file mode 100644
>> index 000000000000..b8f00708dacf
>> --- /dev/null
>> +++ b/sound/soc/codecs/aw88261/aw88261_lib.c
>> @@ -0,0 +1,997 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +//
>> +// aw88261_lib.c  -- ACF bin parsing and check library file for aw88261
>> +//
>> +// Copyright (c) 2023 awinic Technology CO., LTD
>> +//
>> +// Author: Jimmy Zhang <zhangjianming@awinic.com>
>> +// Author: Weidong Wang <wangweidong.a@awinic.com>
>> +//
>> +
>> +#include <linux/crc8.h>
>> +#include <linux/i2c.h>
>> +#include "aw88261_lib.h"
>> +#include "aw88261_device.h"
>> +
>> +#define AW88261_CRC8_POLYNOMIAL 0x8C
>> +DECLARE_CRC8_TABLE(aw_crc8_table);
>> +
>> +static char *profile_name[AW88261_PROFILE_MAX] = {

> Cannot be const char *?

I will modify it according to your suggestion

...

>> +EXPORT_SYMBOL_GPL(aw88261_dev_load_acf_check);

> Why?

This function is also called in the aw88261.c file

Best regards,
Weidong Wang

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V1 3/5] ASoC: codecs: ACF bin parsing and check library file for aw88261
  2023-07-19  3:12     ` wangweidong.a
@ 2023-07-19  6:23       ` Krzysztof Kozlowski
  0 siblings, 0 replies; 19+ messages in thread
From: Krzysztof Kozlowski @ 2023-07-19  6:23 UTC (permalink / raw)
  To: wangweidong.a
  Cc: alsa-devel, broonie, ckeepax, colin.i.king, conor+dt, devicetree,
	doug, fido_max, flatmax, herve.codina, kiseok.jo,
	krzysztof.kozlowski+dt, lgirdwood, linux-kernel, liweilei, perex,
	pierre-louis.bossart, rf, robh+dt, shumingf, tiwai, trix,
	yijiangtao, zhangjianming

On 19/07/2023 05:12, wangweidong.a@awinic.com wrote:
> ...
> 
>>> +EXPORT_SYMBOL_GPL(aw88261_dev_load_acf_check);
> 
>> Why?
> 
> This function is also called in the aw88261.c file

Is aw88261.c a different module? If yes, then why?

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH V1 2/5] ASoC: codecs: aw88261 function for ALSA Audio Driver
  2023-07-19  3:09     ` wangweidong.a
@ 2023-07-19  6:24       ` Krzysztof Kozlowski
  0 siblings, 0 replies; 19+ messages in thread
From: Krzysztof Kozlowski @ 2023-07-19  6:24 UTC (permalink / raw)
  To: wangweidong.a
  Cc: alsa-devel, broonie, ckeepax, colin.i.king, conor+dt, devicetree,
	doug, fido_max, flatmax, herve.codina, kiseok.jo,
	krzysztof.kozlowski+dt, lgirdwood, linux-kernel, liweilei, perex,
	pierre-louis.bossart, rf, robh+dt, shumingf, tiwai, trix,
	yijiangtao, zhangjianming

On 19/07/2023 05:09, wangweidong.a@awinic.com wrote:
> Hi, Krzysztof,
> Thank you very much for your advice, 
>  but I have a few questions I'd like to discuss with you
> 
> On 18/07/2023 16:58, krzysztof.kozlowski@linaro.org wrote:
>> On 17/07/2023 13:58, wangweidong.a@awinic.com wrote:
>>> From: Weidong Wang <wangweidong.a@awinic.com>
>>>
>>> The AW88261 is an I2S/TDM input, high efficiency
>>> digital Smart K audio amplifier with an integrated 10.25V
>>> smart boost convert
> 
>> It's the same as in patch before. This does not help and does not
>> justify having one driver split into two.
> 
> I will modify the commit information and differentiate the commit 
>  information for each file
> 
>>>
> 
> ...
> 
>>> +
>>> +static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
>>> +{
>>> +	int ret;
>>> +
>>> +	if (flag) {
>>> +		ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG,
>>> +			~AW88261_I2STXEN_MASK, AW88261_I2STXEN_ENABLE_VALUE);
>>> +	} else {
>>> +		ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG,
>>> +			~AW88261_I2STXEN_MASK, AW88261_I2STXEN_DISABLE_VALUE);
>>> +	}
> 
>> You should not need {} here and in multiple other places.
> 
> I will delete {} as suggested
> 
>>> +	if (ret)
>>> +		dev_dbg(aw_dev->dev, "%s failed", __func__);
> 
>> Why you are not handling the errors properly?
> 
> Do you need to use dev_err instead?

No, return err and handle it somehow (if it is reasonable).

..

>>> +	} else {
>>> +		if (aw_dev->prof_cur != aw_dev->prof_index) {
>>> +			ret = aw88261_dev_fw_update(aw_dev);
>>> +			if (ret < 0)
>>> +				return ret;
>>> +		}
>>> +	}
>>> +
>>> +	aw_dev->prof_cur = aw_dev->prof_index;
>>> +
>>> +	return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(aw88261_dev_reg_update);
> 
>> Same question. And in all other places as well.
> 
> This function will be called in aw88261.c, can I keep it?

Not really. Being used in other unit does not mean you need export.

Best regards,
Krzysztof


^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2023-07-19  6:26 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-07-17 11:58 [PATCH V1 0/5] ASoC: codecs: Add Awinic AW88261 audio amplifier driver wangweidong.a
2023-07-17 11:58 ` [PATCH V1 1/5] ASoC: codecs: Add i2c and codec registration for aw88261 and their associated operation functions wangweidong.a
2023-07-17 20:33   ` Krzysztof Kozlowski
2023-07-19  3:06     ` wangweidong.a
2023-07-17 11:58 ` [PATCH V1 2/5] ASoC: codecs: aw88261 function for ALSA Audio Driver wangweidong.a
2023-07-17 20:39   ` Krzysztof Kozlowski
2023-07-19  3:09     ` wangweidong.a
2023-07-19  6:24       ` Krzysztof Kozlowski
2023-07-17 11:58 ` [PATCH V1 3/5] ASoC: codecs: ACF bin parsing and check library file for aw88261 wangweidong.a
2023-07-17 20:40   ` Krzysztof Kozlowski
2023-07-19  3:12     ` wangweidong.a
2023-07-19  6:23       ` Krzysztof Kozlowski
2023-07-17 11:58 ` [PATCH V1 4/5] ASoC: codecs: aw88261 chip register file, data type file and Kconfig Makefile wangweidong.a
2023-07-17 12:33   ` Randy Dunlap
2023-07-19  2:59     ` wangweidong.a
2023-07-17 11:58 ` [PATCH V1 5/5] ASoC: dt-bindings: Add schema for "awinic,aw88261" wangweidong.a
2023-07-17 20:23   ` Krzysztof Kozlowski
2023-07-19  3:02     ` wangweidong.a
2023-07-17 13:03 ` [PATCH V1 0/5] ASoC: codecs: Add Awinic AW88261 audio amplifier driver Herve Codina

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).