From nobody Mon Feb 9 02:30:26 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2E08CCA0FF3 for ; Mon, 4 Sep 2023 11:46:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237753AbjIDLqs (ORCPT ); Mon, 4 Sep 2023 07:46:48 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58718 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1352856AbjIDLqr (ORCPT ); Mon, 4 Sep 2023 07:46:47 -0400 Received: from out28-121.mail.aliyun.com (out28-121.mail.aliyun.com [115.124.28.121]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7D239E6; Mon, 4 Sep 2023 04:46:43 -0700 (PDT) X-Alimail-AntiSpam: AC=CONTINUE;BC=0.2192019|-1;CH=green;DM=|CONTINUE|false|;DS=CONTINUE|ham_alarm|0.0431418-0.000499539-0.956359;FP=0|0|0|0|0|-1|-1|-1;HT=ay29a033018047211;MF=wangweidong.a@awinic.com;NM=1;PH=DS;RN=24;RT=24;SR=0;TI=SMTPD_---.UXIjsmr_1693827991; Received: from ubuntu-VirtualBox..(mailfrom:wangweidong.a@awinic.com fp:SMTPD_---.UXIjsmr_1693827991) by smtp.aliyun-inc.com; Mon, 04 Sep 2023 19:46:38 +0800 From: wangweidong.a@awinic.com To: lgirdwood@gmail.com, broonie@kernel.org, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, conor+dt@kernel.org, perex@perex.cz, tiwai@suse.com, rf@opensource.cirrus.com, wangweidong.a@awinic.com, herve.codina@bootlin.com, shumingf@realtek.com, rdunlap@infradead.org, 13916275206@139.com, ryans.lee@analog.com, linus.walleij@linaro.org, ckeepax@opensource.cirrus.com, yijiangtao@awinic.com, liweilei@awinic.com, colin.i.king@gmail.com, trix@redhat.com, alsa-devel@alsa-project.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Cc: zhangjianming@awinic.com Subject: [PATCH V1 1/3] ASoC: dt-bindings: Add schema for "awinic,aw87390" Date: Mon, 4 Sep 2023 19:46:19 +0800 Message-ID: <20230904114621.4457-2-wangweidong.a@awinic.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230904114621.4457-1-wangweidong.a@awinic.com> References: <20230904114621.4457-1-wangweidong.a@awinic.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" From: Weidong Wang Add a DT schema for describing awinic aw87390 audio amplifiers. They are controlled using I2C. Signed-off-by: Weidong Wang --- .../bindings/sound/awinic,aw87390.yaml | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/awinic,aw87390.= yaml diff --git a/Documentation/devicetree/bindings/sound/awinic,aw87390.yaml b/= Documentation/devicetree/bindings/sound/awinic,aw87390.yaml new file mode 100644 index 000000000000..b4de99c9830e --- /dev/null +++ b/Documentation/devicetree/bindings/sound/awinic,aw87390.yaml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/awinic,aw87390.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Awinic Aw87390 Audio Amplifier + +maintainers: + - Weidong Wang + +description: + The awinic aw87390 is specifically designed to improve + the musical output dynamic range, enhance the overall + sound quallity, which is a new high efficiency, low + noise, constant large volume, 6th Smart K audio amplifier. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: awinic,aw87390 + + reg: + maxItems: 1 + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells =3D <1>; + #size-cells =3D <0>; + audio-codec@58 { + compatible =3D "awinic,aw87390"; + reg =3D <0x58>; + }; + }; --=20 2.41.0 From nobody Mon Feb 9 02:30:26 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0592FC83F3F for ; Mon, 4 Sep 2023 11:46:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1352861AbjIDLqz (ORCPT ); Mon, 4 Sep 2023 07:46:55 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56178 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1343693AbjIDLqy (ORCPT ); Mon, 4 Sep 2023 07:46:54 -0400 Received: from out28-124.mail.aliyun.com (out28-124.mail.aliyun.com [115.124.28.124]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EBD5BE6; Mon, 4 Sep 2023 04:46:49 -0700 (PDT) X-Alimail-AntiSpam: AC=CONTINUE;BC=0.06717187|-1;CH=green;DM=|CONTINUE|false|;DS=CONTINUE|ham_system_inform|0.00695684-0.000218789-0.992824;FP=0|0|0|0|0|-1|-1|-1;HT=ay29a033018047198;MF=wangweidong.a@awinic.com;NM=1;PH=DS;RN=24;RT=24;SR=0;TI=SMTPD_---.UXIjstE_1693827998; Received: from ubuntu-VirtualBox..(mailfrom:wangweidong.a@awinic.com fp:SMTPD_---.UXIjstE_1693827998) by smtp.aliyun-inc.com; Mon, 04 Sep 2023 19:46:46 +0800 From: wangweidong.a@awinic.com To: lgirdwood@gmail.com, broonie@kernel.org, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, conor+dt@kernel.org, perex@perex.cz, tiwai@suse.com, rf@opensource.cirrus.com, wangweidong.a@awinic.com, herve.codina@bootlin.com, shumingf@realtek.com, rdunlap@infradead.org, 13916275206@139.com, ryans.lee@analog.com, linus.walleij@linaro.org, ckeepax@opensource.cirrus.com, yijiangtao@awinic.com, liweilei@awinic.com, colin.i.king@gmail.com, trix@redhat.com, alsa-devel@alsa-project.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Cc: zhangjianming@awinic.com Subject: [PATCH V1 2/3] ASoC: codecs: Add code for bin parsing compatible with aw87390 Date: Mon, 4 Sep 2023 19:46:20 +0800 Message-ID: <20230904114621.4457-3-wangweidong.a@awinic.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230904114621.4457-1-wangweidong.a@awinic.com> References: <20230904114621.4457-1-wangweidong.a@awinic.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" From: Weidong Wang Add aw87390 compatible code to the aw88395_lib.c file so that it can parse aw87390's bin file. Signed-off-by: Weidong Wang --- sound/soc/codecs/aw88395/aw88395_lib.c | 23 ++++++++++++++--------- sound/soc/codecs/aw88395/aw88395_reg.h | 1 + 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/aw88395/aw88395_lib.c b/sound/soc/codecs/aw88= 395/aw88395_lib.c index 8ee1baa03269..9d561ae27feb 100644 --- a/sound/soc/codecs/aw88395/aw88395_lib.c +++ b/sound/soc/codecs/aw88395/aw88395_lib.c @@ -455,9 +455,11 @@ static int aw_dev_parse_reg_bin_with_hdr(struct aw_dev= ice *aw_dev, goto parse_bin_failed; } =20 - if (aw_bin->header_info[0].valid_data_len % 4) { - dev_err(aw_dev->dev, "bin data len get error!"); - goto parse_bin_failed; + if (aw_dev->chip_id =3D=3D AW88261_CHIP_ID) { + if (aw_bin->header_info[0].valid_data_len % 4) { + dev_err(aw_dev->dev, "bin data len get error!"); + goto parse_bin_failed; + } } =20 prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data =3D @@ -579,9 +581,9 @@ static int aw_dev_parse_dev_default_type(struct aw_devi= ce *aw_dev, } =20 static int aw88261_dev_cfg_get_valid_prof(struct aw_device *aw_dev, - struct aw_all_prof_info all_prof_info) + struct aw_all_prof_info *all_prof_info) { - struct aw_prof_desc *prof_desc =3D all_prof_info.prof_desc; + struct aw_prof_desc *prof_desc =3D all_prof_info->prof_desc; struct aw_prof_info *prof_info =3D &aw_dev->prof_info; int num =3D 0; int i; @@ -621,9 +623,9 @@ static int aw88261_dev_cfg_get_valid_prof(struct aw_dev= ice *aw_dev, } =20 static int aw88395_dev_cfg_get_valid_prof(struct aw_device *aw_dev, - struct aw_all_prof_info all_prof_info) + struct aw_all_prof_info *all_prof_info) { - struct aw_prof_desc *prof_desc =3D all_prof_info.prof_desc; + struct aw_prof_desc *prof_desc =3D all_prof_info->prof_desc; struct aw_prof_info *prof_info =3D &aw_dev->prof_info; struct aw_sec_data_desc *sec_desc; int num =3D 0; @@ -701,12 +703,13 @@ static int aw_dev_load_cfg_by_hdr(struct aw_device *a= w_dev, =20 switch (aw_dev->chip_id) { case AW88395_CHIP_ID: - ret =3D aw88395_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); + ret =3D aw88395_dev_cfg_get_valid_prof(aw_dev, all_prof_info); if (ret < 0) goto exit; break; case AW88261_CHIP_ID: - ret =3D aw88261_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); + case AW87390_CHIP_ID: + ret =3D aw88261_dev_cfg_get_valid_prof(aw_dev, all_prof_info); if (ret < 0) goto exit; break; @@ -799,6 +802,7 @@ static int aw_get_dev_scene_count_v1(struct aw_device *= aw_dev, struct aw_contain ret =3D 0; break; case AW88261_CHIP_ID: + case AW87390_CHIP_ID: for (i =3D 0; i < cfg_hdr->ddt_num; ++i) { if (((cfg_dde[i].data_type =3D=3D ACF_SEC_TYPE_REG) || (cfg_dde[i].data_type =3D=3D ACF_SEC_TYPE_HDR_REG)) && @@ -839,6 +843,7 @@ static int aw_get_default_scene_count_v1(struct aw_devi= ce *aw_dev, ret =3D 0; break; case AW88261_CHIP_ID: + case AW87390_CHIP_ID: for (i =3D 0; i < cfg_hdr->ddt_num; ++i) { if (((cfg_dde[i].data_type =3D=3D ACF_SEC_TYPE_REG) || (cfg_dde[i].data_type =3D=3D ACF_SEC_TYPE_HDR_REG)) && diff --git a/sound/soc/codecs/aw88395/aw88395_reg.h b/sound/soc/codecs/aw88= 395/aw88395_reg.h index e7a7c02efaf3..d0a273387313 100644 --- a/sound/soc/codecs/aw88395/aw88395_reg.h +++ b/sound/soc/codecs/aw88395/aw88395_reg.h @@ -97,6 +97,7 @@ enum aw88395_id { AW88395_CHIP_ID =3D 0x2049, AW88261_CHIP_ID =3D 0x2113, + AW87390_CHIP_ID =3D 0x76, }; =20 #define AW88395_REG_MAX (0x7D) --=20 2.41.0 From nobody Mon Feb 9 02:30:26 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id C3713C71153 for ; Mon, 4 Sep 2023 11:47:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1351457AbjIDLrP (ORCPT ); Mon, 4 Sep 2023 07:47:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:43112 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233400AbjIDLrO (ORCPT ); Mon, 4 Sep 2023 07:47:14 -0400 Received: from out28-219.mail.aliyun.com (out28-219.mail.aliyun.com [115.124.28.219]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 42496CC7; Mon, 4 Sep 2023 04:46:57 -0700 (PDT) X-Alimail-AntiSpam: AC=CONTINUE;BC=0.06712908|-1;CH=green;DM=|CONTINUE|false|;DS=CONTINUE|ham_system_inform|0.0172678-0.000430609-0.982302;FP=0|0|0|0|0|-1|-1|-1;HT=ay29a033018047211;MF=wangweidong.a@awinic.com;NM=1;PH=DS;RN=24;RT=24;SR=0;TI=SMTPD_---.UXIjt-L_1693828007; Received: from ubuntu-VirtualBox..(mailfrom:wangweidong.a@awinic.com fp:SMTPD_---.UXIjt-L_1693828007) by smtp.aliyun-inc.com; Mon, 04 Sep 2023 19:46:53 +0800 From: wangweidong.a@awinic.com To: lgirdwood@gmail.com, broonie@kernel.org, robh+dt@kernel.org, krzysztof.kozlowski+dt@linaro.org, conor+dt@kernel.org, perex@perex.cz, tiwai@suse.com, rf@opensource.cirrus.com, wangweidong.a@awinic.com, herve.codina@bootlin.com, shumingf@realtek.com, rdunlap@infradead.org, 13916275206@139.com, ryans.lee@analog.com, linus.walleij@linaro.org, ckeepax@opensource.cirrus.com, yijiangtao@awinic.com, liweilei@awinic.com, colin.i.king@gmail.com, trix@redhat.com, alsa-devel@alsa-project.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Cc: zhangjianming@awinic.com Subject: [PATCH V1 3/3] ASoC: codecs: Add aw87390 amplifier driver Date: Mon, 4 Sep 2023 19:46:21 +0800 Message-ID: <20230904114621.4457-4-wangweidong.a@awinic.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230904114621.4457-1-wangweidong.a@awinic.com> References: <20230904114621.4457-1-wangweidong.a@awinic.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" From: Weidong Wang Add i2c and amplifier registration for aw87390 and their associated operation functions. Signed-off-by: Weidong Wang --- sound/soc/codecs/Kconfig | 15 +- sound/soc/codecs/Makefile | 2 + sound/soc/codecs/aw87390.c | 463 +++++++++++++++++++++++++++++++++++++ sound/soc/codecs/aw87390.h | 85 +++++++ 4 files changed, 563 insertions(+), 2 deletions(-) create mode 100644 sound/soc/codecs/aw87390.c create mode 100644 sound/soc/codecs/aw87390.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 95b5bd883215..9f4311b13d6e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -54,6 +54,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_ALC5632 imply SND_SOC_AUDIO_IIO_AUX imply SND_SOC_AW8738 + imply SND_SOC_AW87390 imply SND_SOC_AW88395 imply SND_SOC_AW88261 imply SND_SOC_BT_SCO @@ -638,12 +639,12 @@ config SND_SOC_AW8738 operation mode using the Awinic-specific one-wire pulse control. =20 config SND_SOC_AW88395_LIB + select CRC8 tristate =20 config SND_SOC_AW88395 tristate "Soc Audio for awinic aw88395" depends on I2C - select CRC8 select CRC32 select REGMAP_I2C select GPIOLIB @@ -657,7 +658,6 @@ config SND_SOC_AW88395 config SND_SOC_AW88261 tristate "Soc Audio for awinic aw88261" depends on I2C - select CRC8 select REGMAP_I2C select GPIOLIB select SND_SOC_AW88395_LIB @@ -668,6 +668,17 @@ config SND_SOC_AW88261 boost converter can be adjusted smartly according to the input amplitude. =20 +config SND_SOC_AW87390 + tristate "Soc Audio for awinic aw87390" + depends on I2C + select REGMAP_I2C + select SND_SOC_AW88395_LIB + help + The awinic aw87390 is specifically designed to improve + the musical output dynamic range, enhance the overall + sound quallity, which is a new high efficiency, low + noise, constant large volume, 6th Smart K audio amplifier. + config SND_SOC_BD28623 tristate "ROHM BD28623 CODEC" help diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index c8502a49b40a..12bf52e23d65 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -47,6 +47,7 @@ snd-soc-ak5558-objs :=3D ak5558.o snd-soc-arizona-objs :=3D arizona.o arizona-jack.o snd-soc-audio-iio-aux-objs :=3D audio-iio-aux.o snd-soc-aw8738-objs :=3D aw8738.o +snd-soc-aw87390-objs :=3D aw87390.o snd-soc-aw88395-lib-objs :=3D aw88395/aw88395_lib.o snd-soc-aw88395-objs :=3D aw88395/aw88395.o \ aw88395/aw88395_device.o @@ -433,6 +434,7 @@ obj-$(CONFIG_SND_SOC_ALC5632) +=3D snd-soc-alc5632.o obj-$(CONFIG_SND_SOC_ARIZONA) +=3D snd-soc-arizona.o obj-$(CONFIG_SND_SOC_AUDIO_IIO_AUX) +=3D snd-soc-audio-iio-aux.o obj-$(CONFIG_SND_SOC_AW8738) +=3D snd-soc-aw8738.o +obj-$(CONFIG_SND_SOC_AW87390) +=3D snd-soc-aw87390.o obj-$(CONFIG_SND_SOC_AW88395_LIB) +=3D snd-soc-aw88395-lib.o obj-$(CONFIG_SND_SOC_AW88395) +=3Dsnd-soc-aw88395.o obj-$(CONFIG_SND_SOC_AW88261) +=3Dsnd-soc-aw88261.o diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c new file mode 100644 index 000000000000..cf3065b0a73e --- /dev/null +++ b/sound/soc/codecs/aw87390.c @@ -0,0 +1,463 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw87390.c -- AW87390 ALSA SoC Audio driver +// +// Copyright (c) 2023 awinic Technology CO., LTD +// +// Author: Weidong Wang +// + +#include +#include +#include +#include +#include +#include "aw87390.h" +#include "aw88395/aw88395_data_type.h" +#include "aw88395/aw88395_device.h" + +static const struct regmap_config aw87390_remap_config =3D { + .val_bits =3D 8, + .reg_bits =3D 8, + .max_register =3D AW87390_REG_MAX, + .reg_format_endian =3D REGMAP_ENDIAN_LITTLE, + .val_format_endian =3D REGMAP_ENDIAN_BIG, +}; + +static int aw87390_dev_reg_update(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int i, ret; + + if (!data) { + dev_err(aw_dev->dev, "data is NULL\n"); + return -EINVAL; + } + + for (i =3D 0; i < len; i =3D i + 2) { + if (data[i] =3D=3D AW87390_DELAY_REG_ADDR) { + usleep_range(data[i + 1] * AW87390_REG_DELAY_TIME, + data[i + 1] * AW87390_REG_DELAY_TIME + 10); + continue; + } + ret =3D regmap_write(aw_dev->regmap, data[i], data[i + 1]); + if (ret) + return ret; + } + + return 0; +} + +static char *aw87390_dev_get_prof_name(struct aw_device *aw_dev, int index) +{ + struct aw_prof_info *prof_info =3D &aw_dev->prof_info; + struct aw_prof_desc *prof_desc; + + if ((index >=3D aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "index[%d] overflow count[%d]\n", + index, aw_dev->prof_info.count); + return NULL; + } + + prof_desc =3D &aw_dev->prof_info.prof_desc[index]; + + return prof_info->prof_name_list[prof_desc->id]; +} + +static int aw87390_dev_get_prof_data(struct aw_device *aw_dev, int index, + struct aw_prof_desc **prof_desc) +{ + if ((index >=3D aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n", + __func__, index, aw_dev->prof_info.count); + return -EINVAL; + } + + *prof_desc =3D &aw_dev->prof_info.prof_desc[index]; + + return 0; +} + +static int aw87390_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 =3D aw87390_dev_get_prof_name(aw_dev, aw_dev->prof_index); + if (!prof_name) { + dev_err(aw_dev->dev, "get prof name failed\n"); + return -EINVAL; + } + + dev_dbg(aw_dev->dev, "start update %s", prof_name); + + ret =3D aw87390_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index= _desc); + if (ret) { + dev_err(aw_dev->dev, "aw87390_dev_get_prof_data failed\n"); + return ret; + } + + /* update reg */ + sec_desc =3D prof_index_desc->sec_desc; + ret =3D aw87390_dev_reg_update(aw_dev, sec_desc[AW88395_DATA_TYPE_REG].da= ta, + sec_desc[AW88395_DATA_TYPE_REG].len); + if (ret) { + dev_err(aw_dev->dev, "update reg failed\n"); + return ret; + } + + aw_dev->prof_cur =3D aw_dev->prof_index; + + return ret; +} + +static int aw87390_power_off(struct aw_device *aw_dev) +{ + int ret; + + if (aw_dev->status =3D=3D AW87390_DEV_PW_OFF) { + dev_info(aw_dev->dev, "already power off\n"); + return 0; + } + + ret =3D regmap_write(aw_dev->regmap, AW87390_SYSCTRL_REG, AW87390_POWER_D= OWN_VALUE); + if (ret) + return ret; + aw_dev->status =3D AW87390_DEV_PW_OFF; + + return ret; +} + +static int aw87390_power_on(struct aw_device *aw_dev) +{ + int ret; + + if (aw_dev->status =3D=3D AW87390_DEV_PW_ON) { + dev_info(aw_dev->dev, "already power on\n"); + return 0; + } + + if (!aw_dev->fw_status) { + dev_err(aw_dev->dev, "fw not load\n"); + return -EINVAL; + } + + ret =3D regmap_write(aw_dev->regmap, AW87390_SYSCTRL_REG, AW87390_POWER_D= OWN_VALUE); + if (ret) + return ret; + + ret =3D aw87390_dev_fw_update(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "%s load profile failed\n", __func__); + return ret; + } + aw_dev->status =3D AW87390_DEV_PW_ON; + + return ret; +} + +static int aw87390_dev_set_profile_index(struct aw_device *aw_dev, int ind= ex) +{ + if ((index >=3D aw_dev->prof_info.count) || (index < 0)) + return -EINVAL; + + if (aw_dev->prof_index =3D=3D index) + return -EPERM; + + aw_dev->prof_index =3D index; + + return 0; +} + +static int aw87390_profile_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec =3D snd_soc_kcontrol_component(kcontrol); + struct aw87390 *aw87390 =3D snd_soc_component_get_drvdata(codec); + const char *prof_name; + char *name; + int count; + + uinfo->type =3D SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count =3D 1; + + count =3D aw87390->aw_pa->prof_info.count; + if (count <=3D 0) { + uinfo->value.enumerated.items =3D 0; + return 0; + } + + uinfo->value.enumerated.items =3D count; + + if (uinfo->value.enumerated.item >=3D count) + uinfo->value.enumerated.item =3D count - 1; + + name =3D uinfo->value.enumerated.name; + count =3D uinfo->value.enumerated.item; + + prof_name =3D aw87390_dev_get_prof_name(aw87390->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 aw87390_profile_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec =3D snd_soc_kcontrol_component(kcontrol); + struct aw87390 *aw87390 =3D snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] =3D aw87390->aw_pa->prof_index; + + return 0; +} + +static int aw87390_profile_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec =3D snd_soc_kcontrol_component(kcontrol); + struct aw87390 *aw87390 =3D snd_soc_component_get_drvdata(codec); + int ret; + + mutex_lock(&aw87390->lock); + ret =3D aw87390_dev_set_profile_index(aw87390->aw_pa, ucontrol->value.int= eger.value[0]); + if (ret) { + dev_dbg(codec->dev, "profile index does not change\n"); + mutex_unlock(&aw87390->lock); + return 0; + } + + if (aw87390->aw_pa->status =3D=3D AW87390_DEV_PW_ON) { + aw87390_power_off(aw87390->aw_pa); + aw87390_power_on(aw87390->aw_pa); + } + + mutex_unlock(&aw87390->lock); + + return 1; +} + +static const struct snd_kcontrol_new aw87390_controls[] =3D { + AW87390_PROFILE_EXT("AW87390 Profile Set", aw87390_profile_info, + aw87390_profile_get, aw87390_profile_set), +}; + +static int aw87390_request_firmware_file(struct aw87390 *aw87390) +{ + const struct firmware *cont =3D NULL; + int ret; + + aw87390->aw_pa->fw_status =3D AW87390_DEV_FW_FAILED; + + ret =3D request_firmware(&cont, AW87390_ACF_FILE, aw87390->aw_pa->dev); + if (ret) + return dev_err_probe(aw87390->aw_pa->dev, ret, + "load [%s] failed!\n", AW87390_ACF_FILE); + + dev_dbg(aw87390->aw_pa->dev, "loaded %s - size: %zu\n", + AW87390_ACF_FILE, cont ? cont->size : 0); + + aw87390->aw_cfg =3D devm_kzalloc(aw87390->aw_pa->dev, cont->size + sizeof= (int), GFP_KERNEL); + if (!aw87390->aw_cfg) { + release_firmware(cont); + return -ENOMEM; + } + + aw87390->aw_cfg->len =3D (int)cont->size; + memcpy(aw87390->aw_cfg->data, cont->data, cont->size); + release_firmware(cont); + + ret =3D aw88395_dev_load_acf_check(aw87390->aw_pa, aw87390->aw_cfg); + if (ret) { + dev_err(aw87390->aw_pa->dev, "load [%s] failed !\n", AW87390_ACF_FILE); + return ret; + } + + mutex_lock(&aw87390->lock); + + ret =3D aw88395_dev_cfg_load(aw87390->aw_pa, aw87390->aw_cfg); + if (ret) + dev_err(aw87390->aw_pa->dev, "aw_dev acf parse failed\n"); + + mutex_unlock(&aw87390->lock); + + return ret; +} + +static int aw87390_drv_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component =3D snd_soc_dapm_to_component(w->dapm= ); + struct aw87390 *aw87390 =3D snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev =3D aw87390->aw_pa; + int ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret =3D aw87390_power_on(aw_dev); + break; + case SND_SOC_DAPM_POST_PMD: + ret =3D aw87390_power_off(aw_dev); + break; + default: + dev_err(aw_dev->dev, "%s: invalid event %d\n", __func__, event); + ret =3D -EINVAL; + } + + return ret; +} + +static const struct snd_soc_dapm_widget aw87390_dapm_widgets[] =3D { + SND_SOC_DAPM_INPUT("IN"), + SND_SOC_DAPM_PGA_E("SPK PA", SND_SOC_NOPM, 0, 0, NULL, 0, aw87390_drv_eve= nt, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("OUT"), +}; + +static const struct snd_soc_dapm_route aw87390_dapm_routes[] =3D { + { "SPK PA", NULL, "IN" }, + { "OUT", NULL, "SPK PA" }, +}; + +static int aw87390_codec_probe(struct snd_soc_component *component) +{ + struct aw87390 *aw87390 =3D snd_soc_component_get_drvdata(component); + int ret; + + ret =3D aw87390_request_firmware_file(aw87390); + if (ret) + return dev_err_probe(aw87390->aw_pa->dev, ret, + "aw87390_request_firmware_file failed\n"); + + return 0; +} + +static const struct snd_soc_component_driver soc_codec_dev_aw87390 =3D { + .probe =3D aw87390_codec_probe, + .dapm_widgets =3D aw87390_dapm_widgets, + .num_dapm_widgets =3D ARRAY_SIZE(aw87390_dapm_widgets), + .dapm_routes =3D aw87390_dapm_routes, + .num_dapm_routes =3D ARRAY_SIZE(aw87390_dapm_routes), + .controls =3D aw87390_controls, + .num_controls =3D ARRAY_SIZE(aw87390_controls), +}; + +static void aw87390_parse_channel_dt(struct aw87390 *aw87390) +{ + struct aw_device *aw_dev =3D aw87390->aw_pa; + struct device_node *np =3D aw_dev->dev->of_node; + u32 channel_value =3D AW87390_DEV_DEFAULT_CH; + + of_property_read_u32(np, "sound-channel", &channel_value); + + aw_dev->channel =3D channel_value; +} + +static int aw87390_init(struct aw87390 **aw87390, struct i2c_client *i2c, = struct regmap *regmap) +{ + struct aw_device *aw_dev; + unsigned int chip_id; + int ret; + + /* read chip id */ + ret =3D regmap_read(regmap, AW87390_ID_REG, &chip_id); + if (ret) { + dev_err(&i2c->dev, "%s read chipid error. ret =3D %d\n", __func__, ret); + return ret; + } + + if (chip_id !=3D AW87390_CHIP_ID) { + dev_err(&i2c->dev, "unsupported device\n"); + return -ENXIO; + } + + dev_info(&i2c->dev, "chip id =3D 0x%x\n", chip_id); + + aw_dev =3D devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL); + if (!aw_dev) + return -ENOMEM; + + (*aw87390)->aw_pa =3D aw_dev; + aw_dev->i2c =3D i2c; + aw_dev->regmap =3D regmap; + aw_dev->dev =3D &i2c->dev; + aw_dev->chip_id =3D AW87390_CHIP_ID; + aw_dev->acf =3D NULL; + aw_dev->prof_info.prof_desc =3D NULL; + aw_dev->prof_info.count =3D 0; + aw_dev->prof_info.prof_type =3D AW88395_DEV_NONE_TYPE_ID; + aw_dev->channel =3D AW87390_DEV_DEFAULT_CH; + aw_dev->fw_status =3D AW87390_DEV_FW_FAILED; + aw_dev->prof_index =3D AW87390_INIT_PROFILE; + aw_dev->status =3D AW87390_DEV_PW_OFF; + + aw87390_parse_channel_dt(*aw87390); + + return ret; +} + +static int aw87390_i2c_probe(struct i2c_client *i2c) +{ + struct aw87390 *aw87390; + int ret; + + ret =3D i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C); + if (!ret) + return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed\n"); + + aw87390 =3D devm_kzalloc(&i2c->dev, sizeof(*aw87390), GFP_KERNEL); + if (!aw87390) + return -ENOMEM; + + mutex_init(&aw87390->lock); + + i2c_set_clientdata(i2c, aw87390); + + aw87390->regmap =3D devm_regmap_init_i2c(i2c, &aw87390_remap_config); + if (IS_ERR(aw87390->regmap)) { + ret =3D PTR_ERR(aw87390->regmap); + return dev_err_probe(&i2c->dev, ret, "failed to init regmap: %d\n", ret); + } + + /* aw pa init */ + ret =3D aw87390_init(&aw87390, i2c, aw87390->regmap); + if (ret) + return ret; + + ret =3D regmap_write(aw87390->regmap, AW87390_ID_REG, AW87390_SOFT_RESET_= VALUE); + if (ret) + return ret; + + ret =3D devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_aw87390, NULL, 0); + if (ret) + dev_err(&i2c->dev, "failed to register aw87390: %d\n", ret); + + return ret; +} + +static const struct i2c_device_id aw87390_i2c_id[] =3D { + { AW87390_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw87390_i2c_id); + +static struct i2c_driver aw87390_i2c_driver =3D { + .driver =3D { + .name =3D AW87390_I2C_NAME, + }, + .probe =3D aw87390_i2c_probe, + .id_table =3D aw87390_i2c_id, +}; +module_i2c_driver(aw87390_i2c_driver); + +MODULE_DESCRIPTION("ASoC AW87390 PA Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/aw87390.h b/sound/soc/codecs/aw87390.h new file mode 100644 index 000000000000..468e6eb2e224 --- /dev/null +++ b/sound/soc/codecs/aw87390.h @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw87390.h -- aw87390 ALSA SoC Audio driver +// +// Copyright (c) 2023 awinic Technology CO., LTD +// +// Author: Weidong Wang +// + +#ifndef __AW87390_H__ +#define __AW87390_H__ + +#define AW87390_ID_REG (0x00) +#define AW87390_SYSCTRL_REG (0x01) +#define AW87390_MDCTRL_REG (0x02) +#define AW87390_CPOVP_REG (0x03) +#define AW87390_CPP_REG (0x04) +#define AW87390_PAG_REG (0x05) +#define AW87390_AGC3P_REG (0x06) +#define AW87390_AGC3PA_REG (0x07) +#define AW87390_AGC2P_REG (0x08) +#define AW87390_AGC2PA_REG (0x09) +#define AW87390_AGC1PA_REG (0x0A) +#define AW87390_SYSST_REG (0x59) +#define AW87390_SYSINT_REG (0x60) +#define AW87390_DFT_SYSCTRL_REG (0x61) +#define AW87390_DFT_MDCTRL_REG (0x62) +#define AW87390_DFT_CPADP_REG (0x63) +#define AW87390_DFT_AGCPA_REG (0x64) +#define AW87390_DFT_POFR_REG (0x65) +#define AW87390_DFT_OC_REG (0x66) +#define AW87390_DFT_ADP1_REG (0x67) +#define AW87390_DFT_REF_REG (0x68) +#define AW87390_DFT_LDO_REG (0x69) +#define AW87390_ADP1_REG (0x70) +#define AW87390_ADP2_REG (0x71) +#define AW87390_NG1_REG (0x72) +#define AW87390_NG2_REG (0x73) +#define AW87390_NG3_REG (0x74) +#define AW87390_CP_REG (0x75) +#define AW87390_AB_REG (0x76) +#define AW87390_TEST_REG (0x77) +#define AW87390_ENCR_REG (0x78) +#define AW87390_DELAY_REG_ADDR (0xFE) + +#define AW87390_SOFT_RESET_VALUE (0xAA) +#define AW87390_POWER_DOWN_VALUE (0x00) +#define AW87390_REG_MAX (0xFF) +#define AW87390_DEV_DEFAULT_CH (0) +#define AW87390_INIT_PROFILE (0) +#define AW87390_REG_DELAY_TIME (1000) +#define AW87390_I2C_NAME "aw87390_pa" +#define AW87390_ACF_FILE "aw87390_acf.bin" + +#define AW87390_PROFILE_EXT(xname, profile_info, profile_get, profile_set)= \ +{ \ + .iface =3D SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name =3D xname, \ + .info =3D profile_info, \ + .get =3D profile_get, \ + .put =3D profile_set, \ +} + +enum aw87390_id { + AW87390_CHIP_ID =3D 0x76, +}; + +enum { + AW87390_DEV_FW_FAILED =3D 0, + AW87390_DEV_FW_OK, +}; + +enum { + AW87390_DEV_PW_OFF =3D 0, + AW87390_DEV_PW_ON, +}; + +struct aw87390 { + struct aw_device *aw_pa; + struct mutex lock; + struct regmap *regmap; + struct aw_container *aw_cfg; +}; + +#endif --=20 2.41.0