From patchwork Sat Jul 29 09:12:19 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: wangweidong.a@awinic.com X-Patchwork-Id: 13333053 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 289C6C0015E for ; Sat, 29 Jul 2023 09:17:23 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 56BE684D; Sat, 29 Jul 2023 11:16:31 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 56BE684D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1690622241; bh=ICO4rvgKpiWV0UjPKr6lfVoRO3k4D7grUwUq+M2Ssrw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=tafewQOmP9vM2lMdkPDPTivHRfxDwbNJ233W4fKLoEcoMSLOs2R1P5l/scdmdOxDN vKgyJ+xz1nhJjx4TAWByKxWFufuy237c6YoHxnWZiK10dlITRGu6UuwAOUeJqUk5zC xYWcQuzYQIt1jzpBW5ciegRSpl1nBccPRI54ZpTc= Received: by alsa1.perex.cz (Postfix, from userid 50401) id E303CF80558; Sat, 29 Jul 2023 11:16:05 +0200 (CEST) Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id 73036F80558; Sat, 29 Jul 2023 11:16:05 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 63FDBF80551; Sat, 29 Jul 2023 11:16:02 +0200 (CEST) Received: from out28-217.mail.aliyun.com (out28-217.mail.aliyun.com [115.124.28.217]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 71128F80149 for ; Sat, 29 Jul 2023 11:12:37 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 71128F80149 X-Alimail-AntiSpam: AC=CONTINUE;BC=0.3879003|-1;CH=green;DM=|CONTINUE|false|;DS=CONTINUE|ham_system_inform|0.017331-0.000432192-0.982237;FP=0|0|0|0|0|-1|-1|-1;HT=ay29a033018047212;MF=wangweidong.a@awinic.com;NM=1;PH=DS;RN=25;RT=25;SR=0;TI=SMTPD_---.U3WkWhu_1690621950; Received: from ubuntu-VirtualBox..(mailfrom:wangweidong.a@awinic.com fp:SMTPD_---.U3WkWhu_1690621950) by smtp.aliyun-inc.com; Sat, 29 Jul 2023 17:12:32 +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, shumingf@realtek.com, ryans.lee@analog.com, 13916275206@139.com, herve.codina@bootlin.com, ckeepax@opensource.cirrus.com, doug@schmorgal.com, fido_max@inbox.ru, povik+lin@cutebit.org, liweilei@awinic.com, yijiangtao@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 V3 1/5] ASoC: dt-bindings: Add schema for "awinic,aw88261" Date: Sat, 29 Jul 2023 17:12:19 +0800 Message-ID: <20230729091223.193466-2-wangweidong.a@awinic.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230729091223.193466-1-wangweidong.a@awinic.com> References: <20230729091223.193466-1-wangweidong.a@awinic.com> MIME-Version: 1.0 Message-ID-Hash: BA43IEP7HYU5NHIBU2LNYHKB2KHILBSR X-Message-ID-Hash: BA43IEP7HYU5NHIBU2LNYHKB2KHILBSR X-MailFrom: wangweidong.a@awinic.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Weidong Wang Add the awinic,aw88261 property to the awinic,aw88395.yaml file. Signed-off-by: Weidong Wang Acked-by: Krzysztof Kozlowski --- Documentation/devicetree/bindings/sound/awinic,aw88395.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml index 35eef7d818a2..4051c2538caf 100644 --- a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml +++ b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml @@ -19,7 +19,9 @@ allOf: properties: compatible: - const: awinic,aw88395 + enum: + - awinic,aw88395 + - awinic,aw88261 reg: maxItems: 1 From patchwork Sat Jul 29 09:12:20 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: wangweidong.a@awinic.com X-Patchwork-Id: 13333048 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 2C8A0C0015E for ; Sat, 29 Jul 2023 09:14:45 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id B3835847; Sat, 29 Jul 2023 11:13:52 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz B3835847 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1690622082; bh=CKY6wapG15vE1UiFRy/UxhlUEPJTB5XsU3nxklV0dok=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=vLPJV1D85xvjg3sGbul6eJYyv8+X9nz0LMv3nBm+y9ixOkV9xf5UJTZcbTKgQBSH/ H7GbgDFreHdZAi1ATjPtixw/STYEwMKH0Bm4nrIQHFA0yx6EKqqMzW9CaK/W+yUQGN 3e/n6zOdBWSzf2xoJLZlwsQktotkVdS/wpmJJQdw= Received: by alsa1.perex.cz (Postfix, from userid 50401) id DC08FF80558; Sat, 29 Jul 2023 11:13:04 +0200 (CEST) Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id 9159CF80557; Sat, 29 Jul 2023 11:13:04 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 8DB05F80520; Sat, 29 Jul 2023 11:12:59 +0200 (CEST) Received: from out28-52.mail.aliyun.com (out28-52.mail.aliyun.com [115.124.28.52]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 78838F80163 for ; Sat, 29 Jul 2023 11:12:41 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 78838F80163 X-Alimail-AntiSpam: AC=CONTINUE;BC=0.06712908|-1;CH=green;DM=|CONTINUE|false|;DS=CONTINUE|ham_alarm|0.00156891-6.88847e-05-0.998362;FP=0|0|0|0|0|-1|-1|-1;HT=ay29a033018047207;MF=wangweidong.a@awinic.com;NM=1;PH=DS;RN=25;RT=25;SR=0;TI=SMTPD_---.U3WkWlv_1690621953; Received: from ubuntu-VirtualBox..(mailfrom:wangweidong.a@awinic.com fp:SMTPD_---.U3WkWlv_1690621953) by smtp.aliyun-inc.com; Sat, 29 Jul 2023 17:12:36 +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, shumingf@realtek.com, ryans.lee@analog.com, 13916275206@139.com, herve.codina@bootlin.com, ckeepax@opensource.cirrus.com, doug@schmorgal.com, fido_max@inbox.ru, povik+lin@cutebit.org, liweilei@awinic.com, yijiangtao@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 V3 2/5] ASoC: codecs: Add code for bin parsing compatible with aw88261 Date: Sat, 29 Jul 2023 17:12:20 +0800 Message-ID: <20230729091223.193466-3-wangweidong.a@awinic.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230729091223.193466-1-wangweidong.a@awinic.com> References: <20230729091223.193466-1-wangweidong.a@awinic.com> MIME-Version: 1.0 Message-ID-Hash: VSKJQJNJEWHWV3SHH2ICZWZIAZGSJJGG X-Message-ID-Hash: VSKJQJNJEWHWV3SHH2ICZWZIAZGSJJGG X-MailFrom: wangweidong.a@awinic.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Weidong Wang Add aw88261 compatible code to the aw88395_lib.c file so that it can parse aw88261's bin file. Signed-off-by: Weidong Wang --- sound/soc/codecs/aw88395/aw88395_lib.c | 193 ++++++++++++++++++++++--- sound/soc/codecs/aw88395/aw88395_reg.h | 1 + 2 files changed, 177 insertions(+), 17 deletions(-) diff --git a/sound/soc/codecs/aw88395/aw88395_lib.c b/sound/soc/codecs/aw88395/aw88395_lib.c index 05bcf49da857..695ddbc2fa76 100644 --- a/sound/soc/codecs/aw88395/aw88395_lib.c +++ b/sound/soc/codecs/aw88395/aw88395_lib.c @@ -11,6 +11,7 @@ #include #include "aw88395_lib.h" #include "aw88395_device.h" +#include "aw88395_reg.h" #define AW88395_CRC8_POLYNOMIAL 0x8C DECLARE_CRC8_TABLE(aw_crc8_table); @@ -429,6 +430,53 @@ static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char * return ret; } +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[AW88395_DATA_TYPE_REG].data = + data + aw_bin->header_info[0].valid_data_addr; + prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len = + aw_bin->header_info[0].valid_data_len; + prof_desc->prof_st = AW88395_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) { @@ -447,6 +495,9 @@ static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg return aw_dev_prof_parse_multi_bin( aw_dev, (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); default: dev_err(aw_dev->dev, "%s cfg_dde->data_type = %d\n", __func__, cfg_dde->data_type); break; @@ -527,7 +578,49 @@ static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev, return 0; } -static int aw_dev_cfg_get_valid_prof(struct aw_device *aw_dev, +static int aw88261_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 < AW88395_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW88395_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 < AW88395_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW88395_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 aw88395_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; @@ -606,9 +699,22 @@ static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev, goto exit; } - ret = aw_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); - if (ret < 0) - goto exit; + switch (aw_dev->chip_id) { + case AW88395_CHIP_ID: + ret = aw88395_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); + if (ret < 0) + goto exit; + break; + case AW88261_CHIP_ID: + ret = aw88261_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); + if (ret < 0) + goto exit; + break; + default: + dev_err(aw_dev->dev, "valid prof unsupported"); + ret = -EINVAL; + break; + } aw_dev->prof_info.prof_name_list = profile_name; @@ -679,16 +785,37 @@ static int aw_get_dev_scene_count_v1(struct aw_device *aw_dev, struct aw_contain struct aw_cfg_dde_v1 *cfg_dde = (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); unsigned int i; + int ret; - for (i = 0; i < cfg_hdr->ddt_num; ++i) { - if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && - (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)++; + switch (aw_dev->chip_id) { + case AW88395_CHIP_ID: + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && + (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)++; + } + ret = 0; + break; + case AW88261_CHIP_ID: + 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)++; + } + ret = 0; + break; + default: + dev_err(aw_dev->dev, "unsupported device"); + ret = -EINVAL; + break; } - return 0; + return ret; } static int aw_get_default_scene_count_v1(struct aw_device *aw_dev, @@ -699,15 +826,35 @@ static int aw_get_default_scene_count_v1(struct aw_device *aw_dev, struct aw_cfg_dde_v1 *cfg_dde = (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); unsigned int i; + int ret; - for (i = 0; i < cfg_hdr->ddt_num; ++i) { - if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && - (aw_dev->chip_id == cfg_dde[i].chip_id) && - (aw_dev->channel == cfg_dde[i].dev_index)) - (*scene_num)++; + switch (aw_dev->chip_id) { + case AW88395_CHIP_ID: + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->channel == cfg_dde[i].dev_index)) + (*scene_num)++; + } + ret = 0; + break; + case AW88261_CHIP_ID: + 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)++; + } + ret = 0; + break; + default: + dev_err(aw_dev->dev, "unsupported device"); + ret = -EINVAL; + break; } - return 0; + return ret; } static int aw_dev_parse_scene_count_v1(struct aw_device *aw_dev, @@ -756,6 +903,18 @@ static int aw_dev_parse_data_by_sec_type_v1(struct aw_device *aw_dev, 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; diff --git a/sound/soc/codecs/aw88395/aw88395_reg.h b/sound/soc/codecs/aw88395/aw88395_reg.h index e64f24e97150..e7a7c02efaf3 100644 --- a/sound/soc/codecs/aw88395/aw88395_reg.h +++ b/sound/soc/codecs/aw88395/aw88395_reg.h @@ -96,6 +96,7 @@ enum aw88395_id { AW88395_CHIP_ID = 0x2049, + AW88261_CHIP_ID = 0x2113, }; #define AW88395_REG_MAX (0x7D) From patchwork Sat Jul 29 09:12:21 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: wangweidong.a@awinic.com X-Patchwork-Id: 13333052 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 834CAC0015E for ; Sat, 29 Jul 2023 09:16:59 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id B788682A; Sat, 29 Jul 2023 11:16:07 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz B788682A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1690622217; bh=/s5+r6v+FWp3nlGjM83UlF6/QaBUtQ0rFdOQG4LuKDs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=Lwrs98TpFJwOsLyNNxql7njl4NjokwlDEwwGafUhgWADXbx7aKSluDx/MXpdpxO7i cjab6TYwI2CRcCoxwn4Oo7Kon5aAQJweNNw7+scmky9yd2vQu5HPs8mL6RNYagAJ5c wPRpoo6aMGj+dyG9ZUvLwqt/XdMXJQLYID/9y3mE= Received: by alsa1.perex.cz (Postfix, from userid 50401) id 88FFFF80544; Sat, 29 Jul 2023 11:15:40 +0200 (CEST) Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id AD276F80520; Sat, 29 Jul 2023 11:15:39 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 6C58CF80568; Sat, 29 Jul 2023 11:13:11 +0200 (CEST) Received: from out28-173.mail.aliyun.com (out28-173.mail.aliyun.com [115.124.28.173]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 342C7F8019B for ; Sat, 29 Jul 2023 11:12:45 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 342C7F8019B X-Alimail-AntiSpam: AC=CONTINUE;BC=0.06712908|-1;CH=green;DM=|CONTINUE|false|;DS=CONTINUE|ham_system_inform|0.0139393-0.000385875-0.985675;FP=0|0|0|0|0|-1|-1|-1;HT=ay29a033018047188;MF=wangweidong.a@awinic.com;NM=1;PH=DS;RN=25;RT=25;SR=0;TI=SMTPD_---.U3WkWpk_1690621956; Received: from ubuntu-VirtualBox..(mailfrom:wangweidong.a@awinic.com fp:SMTPD_---.U3WkWpk_1690621956) by smtp.aliyun-inc.com; Sat, 29 Jul 2023 17:12:39 +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, shumingf@realtek.com, ryans.lee@analog.com, 13916275206@139.com, herve.codina@bootlin.com, ckeepax@opensource.cirrus.com, doug@schmorgal.com, fido_max@inbox.ru, povik+lin@cutebit.org, liweilei@awinic.com, yijiangtao@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 V3 3/5] ASoC: codecs: Add aw88261 amplifier driver Date: Sat, 29 Jul 2023 17:12:21 +0800 Message-ID: <20230729091223.193466-4-wangweidong.a@awinic.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230729091223.193466-1-wangweidong.a@awinic.com> References: <20230729091223.193466-1-wangweidong.a@awinic.com> MIME-Version: 1.0 Message-ID-Hash: LHB56VKFSQMLHRQBXGSF653HCIUBCQBV X-Message-ID-Hash: LHB56VKFSQMLHRQBXGSF653HCIUBCQBV X-MailFrom: wangweidong.a@awinic.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Weidong Wang Add i2c and amplifier registration for aw88261 and their associated operation functions. Signed-off-by: Weidong Wang --- sound/soc/codecs/aw88261/aw88261.c | 517 +++++++++++++++++++++++++++++ sound/soc/codecs/aw88261/aw88261.h | 52 +++ 2 files changed, 569 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..a0ef141aba0f --- /dev/null +++ b/sound/soc/codecs/aw88261/aw88261.c @@ -0,0 +1,517 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88261.c -- ALSA SoC AW88261 codec support +// +// Copyright (c) 2023 awinic Technology CO., LTD +// +// Author: Jimmy Zhang +// Author: Weidong Wang +// + +#include +#include +#include +#include +#include +#include "aw88261.h" +#include "aw88261_device.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->aw88261_base->fw_status != AW88261_DEV_FW_OK) + return; + + if (aw88261->aw_pa->aw88261_base->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 aw88261_device *aw_dev = aw88261->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->aw88261_base->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 aw88261_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->aw88261_base->fade_in_time) { + aw_dev->aw88261_base->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 aw88261_device *aw_dev = aw88261->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->aw88261_base->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 aw88261_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->aw88261_base->fade_out_time) { + aw_dev->aw88261_base->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->aw_pa->aw88261_base->prof_info.count; + 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->aw_pa->aw88261_base->prof_index; + + 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->aw88261_base->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->aw88261_base->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->aw88261_base->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->aw88261_base->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->aw88261_base->fade_step != value) { + aw88261->aw_pa->aw88261_base->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->aw88261_base->fw_status = AW88261_DEV_FW_FAILED; + + ret = request_firmware(&cont, AW88261_ACF_FILE, aw88261->aw_pa->dev); + if (ret < 0) { + dev_err_probe(aw88261->aw_pa->dev, ret, "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 = aw88395_dev_load_acf_check(aw88261->aw_pa->aw88261_base, aw88261->aw_cfg); + if (ret < 0) { + dev_err_probe(aw88261->aw_pa->dev, ret, "load [%s] failed !", AW88261_ACF_FILE); + return ret; + } + + 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_probe(aw88261->aw_pa->dev, ret, "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 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; + + ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C); + if (!ret) { + dev_err_probe(&i2c->dev, ret, "check_functionality failed"); + return -ENXIO; + } + + aw88261 = devm_kzalloc(&i2c->dev, sizeof(struct aw88261), GFP_KERNEL); + if (!aw88261) + return -ENOMEM; + + mutex_init(&aw88261->lock); + + 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_probe(&i2c->dev, ret, "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_probe(&i2c->dev, ret, "failed to register aw88261: %d", ret); + + return ret; +} + +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, + }, + .probe = 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..3f518b7623b6 --- /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 +// Author: Weidong Wang +// + +#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 aw88261_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 From patchwork Sat Jul 29 09:12:22 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: wangweidong.a@awinic.com X-Patchwork-Id: 13333055 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id E7485C0015E for ; Sat, 29 Jul 2023 09:17:50 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 36B2C93A; Sat, 29 Jul 2023 11:16:58 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 36B2C93A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1690622268; bh=Xx0WIV7ir8hs4y1xrD46ZQ7Qr8gWOV3fM5PTNBodofA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=p58JIGU401A0enMbq+roM2pyFFIwrr36X9hj+sjN42t8FHdZfQyjXmEXvh+muhoC1 f3EheaxKB5K45YMGE27z+IhxzD8cR4pdld9WFoxL/Bbo1lLVLegVUKMbw5GhiXMcN/ ohruNpMxG22mfLXc5+PJyWQiZSRbCmRZxjc+O8OU= Received: by alsa1.perex.cz (Postfix, from userid 50401) id 3E9A9F802E8; Sat, 29 Jul 2023 11:16:17 +0200 (CEST) Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id BD327F802E8; Sat, 29 Jul 2023 11:16:16 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 5329BF80579; Sat, 29 Jul 2023 11:16:12 +0200 (CEST) Received: from out28-173.mail.aliyun.com (out28-173.mail.aliyun.com [115.124.28.173]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id EB5ACF800D2 for ; Sat, 29 Jul 2023 11:12:48 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz EB5ACF800D2 X-Alimail-AntiSpam: AC=CONTINUE;BC=0.06712908|-1;CH=green;DM=|CONTINUE|false|;DS=CONTINUE|ham_enroll_verification|0.00579657-0.00251078-0.991693;FP=0|0|0|0|0|-1|-1|-1;HT=ay29a033018047206;MF=wangweidong.a@awinic.com;NM=1;PH=DS;RN=25;RT=25;SR=0;TI=SMTPD_---.U3WkWtD_1690621960; Received: from ubuntu-VirtualBox..(mailfrom:wangweidong.a@awinic.com fp:SMTPD_---.U3WkWtD_1690621960) by smtp.aliyun-inc.com; Sat, 29 Jul 2023 17:12:42 +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, shumingf@realtek.com, ryans.lee@analog.com, 13916275206@139.com, herve.codina@bootlin.com, ckeepax@opensource.cirrus.com, doug@schmorgal.com, fido_max@inbox.ru, povik+lin@cutebit.org, liweilei@awinic.com, yijiangtao@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 V3 4/5] ASoC: codecs: aw88261 device related operation functions Date: Sat, 29 Jul 2023 17:12:22 +0800 Message-ID: <20230729091223.193466-5-wangweidong.a@awinic.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230729091223.193466-1-wangweidong.a@awinic.com> References: <20230729091223.193466-1-wangweidong.a@awinic.com> MIME-Version: 1.0 Message-ID-Hash: K64WJVPVKTWPNTWQFPQEF7DOZ6ABSA6O X-Message-ID-Hash: K64WJVPVKTWPNTWQFPQEF7DOZ6ABSA6O X-MailFrom: wangweidong.a@awinic.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Weidong Wang Operate the aw88261 chip, including device initialization, chip power-on and power-off, control volume, etc. Signed-off-by: Weidong Wang --- sound/soc/codecs/aw88261/aw88261_device.c | 877 ++++++++++++++++++++++ sound/soc/codecs/aw88261/aw88261_device.h | 79 ++ 2 files changed, 956 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..40fb9a60599d --- /dev/null +++ b/sound/soc/codecs/aw88261/aw88261_device.c @@ -0,0 +1,877 @@ +// 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 +// Author: Weidong Wang +// + +#include +#include +#include +#include "aw88261_device.h" +#include "aw88261_reg.h" +#include "../aw88395/aw88395_data_type.h" + +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)); +} + +void aw88261_dev_set_volume(struct aw88261_device *aw_dev, unsigned int value) +{ + struct aw_volume_desc *vol_desc = &aw_dev->aw88261_base->volume_desc; + unsigned int reg_value; + u16 real_value, volume; + + volume = min((value + vol_desc->init_volume), (unsigned int)AW88261_MUTE_VOL); + real_value = db_to_reg_val(volume); + + /* cal real value */ + regmap_read(aw_dev->aw88261_base->regmap, AW88261_SYSCTRL2_REG, ®_value); + + 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 */ + regmap_write(aw_dev->aw88261_base->regmap, AW88261_SYSCTRL2_REG, real_value); +} + +static void aw_dev_fade_in(struct aw88261_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->aw88261_base->volume_desc; + int fade_step = aw_dev->aw88261_base->fade_step; + int fade_in_vol = desc->ctl_volume; + int i; + + if (fade_step == 0 || aw_dev->aw88261_base->fade_in_time == 0) { + aw88261_dev_set_volume(aw_dev, fade_in_vol); + return; + } + + for (i = AW88261_MUTE_VOL; i >= fade_in_vol; i -= fade_step) { + aw88261_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->aw88261_base->fade_in_time, + aw_dev->aw88261_base->fade_in_time + 10); + } + + if (i != fade_in_vol) + aw88261_dev_set_volume(aw_dev, fade_in_vol); +} + +static void aw_dev_fade_out(struct aw88261_device *aw_dev) +{ + struct aw_device *aw88261_base = aw_dev->aw88261_base; + struct aw_volume_desc *desc = &aw88261_base->volume_desc; + int fade_step = aw88261_base->fade_step; + int i; + + if (fade_step == 0 || aw_dev->aw88261_base->fade_out_time == 0) { + aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL); + return; + } + + for (i = desc->ctl_volume; i <= AW88261_MUTE_VOL; i += fade_step) { + aw88261_dev_set_volume(aw_dev, i); + usleep_range(aw88261_base->fade_out_time, aw88261_base->fade_out_time + 10); + } + + if (i != AW88261_MUTE_VOL) { + aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL); + usleep_range(aw88261_base->fade_out_time, aw88261_base->fade_out_time + 10); + } +} + +static void aw_dev_i2s_tx_enable(struct aw88261_device *aw_dev, bool flag) +{ + if (flag) + regmap_update_bits(aw_dev->aw88261_base->regmap, AW88261_I2SCFG1_REG, + ~AW88261_I2STXEN_MASK, AW88261_I2STXEN_ENABLE_VALUE); + else + regmap_update_bits(aw_dev->aw88261_base->regmap, AW88261_I2SCFG1_REG, + ~AW88261_I2STXEN_MASK, AW88261_I2STXEN_DISABLE_VALUE); +} + +static void aw_dev_pwd(struct aw88261_device *aw_dev, bool pwd) +{ + if (pwd) + regmap_update_bits(aw_dev->aw88261_base->regmap, AW88261_SYSCTRL_REG, + ~AW88261_PWDN_MASK, AW88261_PWDN_POWER_DOWN_VALUE); + else + regmap_update_bits(aw_dev->aw88261_base->regmap, AW88261_SYSCTRL_REG, + ~AW88261_PWDN_MASK, AW88261_PWDN_WORKING_VALUE); +} + +static void aw_dev_amppd(struct aw88261_device *aw_dev, bool amppd) +{ + if (amppd) + regmap_update_bits(aw_dev->aw88261_base->regmap, AW88261_SYSCTRL_REG, + ~AW88261_AMPPD_MASK, AW88261_AMPPD_POWER_DOWN_VALUE); + else + regmap_update_bits(aw_dev->aw88261_base->regmap, AW88261_SYSCTRL_REG, + ~AW88261_AMPPD_MASK, AW88261_AMPPD_WORKING_VALUE); +} + +void aw88261_dev_mute(struct aw88261_device *aw_dev, bool is_mute) +{ + if (is_mute) { + aw_dev_fade_out(aw_dev); + regmap_update_bits(aw_dev->aw88261_base->regmap, AW88261_SYSCTRL_REG, + ~AW88261_HMUTE_MASK, AW88261_HMUTE_ENABLE_VALUE); + } else { + regmap_update_bits(aw_dev->aw88261_base->regmap, AW88261_SYSCTRL_REG, + ~AW88261_HMUTE_MASK, AW88261_HMUTE_DISABLE_VALUE); + aw_dev_fade_in(aw_dev); + } +} + +static void aw88261_dev_uls_hmute(struct aw88261_device *aw_dev, bool uls_hmute) +{ + if (uls_hmute) + regmap_update_bits(aw_dev->aw88261_base->regmap, AW88261_SYSCTRL_REG, + ~AW88261_ULS_HMUTE_MASK, + AW88261_ULS_HMUTE_ENABLE_VALUE); + else + regmap_update_bits(aw_dev->aw88261_base->regmap, AW88261_SYSCTRL_REG, + ~AW88261_ULS_HMUTE_MASK, + AW88261_ULS_HMUTE_DISABLE_VALUE); +} + +static int aw_dev_get_icalk(struct aw88261_device *aw_dev, int16_t *icalk) +{ + u16 reg_icalk, reg_icalkl; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->aw88261_base->regmap, AW88261_EFRH4_REG, ®_val); + if (ret) + return ret; + + reg_icalk = reg_val & (~AW88261_EF_ISN_GESLP_H_MASK); + + ret = regmap_read(aw_dev->aw88261_base->regmap, AW88261_EFRL4_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 aw88261_device *aw_dev, int16_t *vcalk) +{ + u16 reg_vcalk, reg_vcalkl; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->aw88261_base->regmap, AW88261_EFRH3_REG, ®_val); + if (ret) + return ret; + + reg_vcalk = (u16)reg_val & (~AW88261_EF_VSN_GESLP_H_MASK); + + ret = regmap_read(aw_dev->aw88261_base->regmap, AW88261_EFRL3_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 aw88261_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->aw88261_base->regmap, AW88261_VSNTM1_REG, reg_val); + + return ret; +} + +static void aw_dev_clear_int_status(struct aw88261_device *aw_dev) +{ + unsigned int int_status; + + /* read int status and clear */ + regmap_read(aw_dev->aw88261_base->regmap, AW88261_SYSINT_REG, &int_status); + /* make sure int status is clear */ + regmap_read(aw_dev->aw88261_base->regmap, AW88261_SYSINT_REG, &int_status); + + dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", int_status); +} + +static int aw_dev_get_iis_status(struct aw88261_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->aw88261_base->regmap, AW88261_SYSST_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 aw88261_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 aw88261_device *aw_dev) +{ + unsigned int reg_val; + int ret, i; + + ret = regmap_read(aw_dev->aw88261_base->regmap, AW88261_PLLCTRL1_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->aw88261_base->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->aw88261_base->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, "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 aw88261_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 aw88261_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->aw88261_base->regmap, AW88261_SYSST_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, reg_val=0x%04x, check:0x%x", + 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 aw88261_device *aw_dev, + unsigned char *data, unsigned int len) +{ + struct aw_volume_desc *vol_desc = &aw_dev->aw88261_base->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->aw88261_base->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->aw88261_base->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->aw88261_base->regmap, reg_addr, reg_val); + if (ret) + break; + + } + + ret = aw_dev_set_vcalb(aw_dev); + if (ret) + return ret; + + if (aw_dev->aw88261_base->prof_cur != aw_dev->aw88261_base->prof_index) + vol_desc->ctl_volume = 0; + + /* keep min volume */ + aw88261_dev_set_volume(aw_dev, vol_desc->mute_volume); + + return ret; +} + +static int aw_dev_reg_update(struct aw88261_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 int aw_frcset_check(struct aw88261_device *aw_dev) +{ + unsigned int reg_val; + u16 temh, teml, tem; + int ret; + + ret = regmap_read(aw_dev->aw88261_base->regmap, AW88261_EFRH3_REG, ®_val); + if (ret) + return ret; + temh = ((u16)reg_val & (~AW88261_TEMH_MASK)); + + ret = regmap_read(aw_dev->aw88261_base->regmap, AW88261_EFRL3_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 aw88261_device *aw_dev) +{ + if (aw_dev->frcset_en == AW_FRCSET_ENABLE) { + /* set FORCE_PWM */ + regmap_update_bits(aw_dev->aw88261_base->regmap, AW88261_BSTCTRL3_REG, + AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE); + /* set BOOST_OS_WIDTH */ + regmap_update_bits(aw_dev->aw88261_base->regmap, AW88261_BSTCTRL5_REG, + AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE); + /* set BURST_LOOPR */ + regmap_update_bits(aw_dev->aw88261_base->regmap, AW88261_BSTCTRL6_REG, + AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE); + /* set RSQN_DLY */ + regmap_update_bits(aw_dev->aw88261_base->regmap, AW88261_BSTCTRL7_REG, + AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE); + /* set BURST_SSMODE */ + regmap_update_bits(aw_dev->aw88261_base->regmap, AW88261_BSTCTRL8_REG, + AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE); + /* set BST_BURST */ + regmap_update_bits(aw_dev->aw88261_base->regmap, AW88261_BSTCTRL9_REG, + AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE); + } else { + dev_dbg(aw_dev->dev, "needn't set reg value"); + } +} + +static int aw_dev_fw_update(struct aw88261_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->aw88261_base->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->aw88261_base->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[AW88395_DATA_TYPE_REG].data, + sec_desc[AW88395_DATA_TYPE_REG].len); + if (ret) { + dev_err(aw_dev->dev, "update reg failed"); + return ret; + } + + aw_dev->aw88261_base->prof_cur = aw_dev->aw88261_base->prof_index; + + return ret; +} + +int aw88261_dev_reg_update(struct aw88261_device *aw_dev, bool force) +{ + int ret; + + if (force) { + ret = regmap_write(aw_dev->aw88261_base->regmap, + AW88261_ID_REG, AW88261_SOFT_RESET_VALUE); + if (ret < 0) + return ret; + + ret = aw_dev_fw_update(aw_dev); + if (ret < 0) + return ret; + } else { + if (aw_dev->aw88261_base->prof_cur != aw_dev->aw88261_base->prof_index) { + ret = aw_dev_fw_update(aw_dev); + if (ret < 0) + return ret; + } + } + + aw_dev->aw88261_base->prof_cur = aw_dev->aw88261_base->prof_index; + + return ret; +} + +int aw88261_dev_start(struct aw88261_device *aw_dev) +{ + int ret; + + if (aw_dev->aw88261_base->status == AW88261_DEV_PW_ON) { + dev_info(aw_dev->aw88261_base->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->aw88261_base->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->aw88261_base->status = AW88261_DEV_PW_OFF; + + return ret; +} + +int aw88261_dev_stop(struct aw88261_device *aw_dev) +{ + if (aw_dev->aw88261_base->status == AW88261_DEV_PW_OFF) { + dev_info(aw_dev->aw88261_base->dev, "already power off"); + return 0; + } + + aw_dev->aw88261_base->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; +} + +int aw88261_dev_init(struct aw88261_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 = aw88395_dev_cfg_load(aw_dev->aw88261_base, aw_cfg); + if (ret) { + dev_err(aw_dev->dev, "aw_dev acf parse failed"); + return -EINVAL; + } + + ret = regmap_write(aw_dev->aw88261_base->regmap, AW88261_ID_REG, AW88261_SOFT_RESET_VALUE); + if (ret < 0) + return ret; + + aw_dev->aw88261_base->fade_in_time = AW88261_1000_US / 10; + aw_dev->aw88261_base->fade_out_time = AW88261_1000_US >> 1; + aw_dev->aw88261_base->prof_cur = AW_INIT_PROFILE; + aw_dev->aw88261_base->prof_index = AW_INIT_PROFILE; + + ret = aw_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 failed 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; +} + +static void aw_parse_channel_dt(struct aw88261_device *aw_dev) +{ + struct device_node *np = aw_dev->aw88261_base->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->aw88261_base->channel = channel_value; + aw_dev->phase_sync = sync_enable; +} + +static int aw_dev_init(struct aw88261_device *aw_dev) +{ + aw_dev->aw88261_base->chip_id = AW88261_CHIP_ID; + /* call aw device init func */ + aw_dev->aw88261_base->acf = NULL; + aw_dev->aw88261_base->prof_info.prof_desc = NULL; + aw_dev->aw88261_base->prof_info.count = 0; + aw_dev->aw88261_base->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID; + aw_dev->aw88261_base->channel = 0; + aw_dev->aw88261_base->fw_status = AW88261_DEV_FW_FAILED; + + aw_dev->aw88261_base->fade_step = AW88261_VOLUME_STEP_DB; + aw_dev->aw88261_base->volume_desc.ctl_volume = AW88261_VOL_DEFAULT_VALUE; + aw_dev->aw88261_base->volume_desc.mute_volume = AW88261_MUTE_VOL; + aw_parse_channel_dt(aw_dev); + + return 0; +} + +int aw88261_dev_set_profile_index(struct aw88261_device *aw_dev, int index) +{ + struct aw_device *aw88261_base = aw_dev->aw88261_base; + + /* check the index whether is valid */ + if ((index >= aw88261_base->prof_info.count) || (index < 0)) + return -EINVAL; + /* check the index whether change */ + if (aw88261_base->prof_index == index) + return -EINVAL; + + aw88261_base->prof_index = index; + + return 0; +} + +char *aw88261_dev_get_prof_name(struct aw88261_device *aw_dev, int index) +{ + struct aw_prof_info *prof_info = &aw_dev->aw88261_base->prof_info; + struct aw_prof_desc *prof_desc; + + if ((index >= aw_dev->aw88261_base->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "index[%d] overflow count[%d]", + index, aw_dev->aw88261_base->prof_info.count); + return NULL; + } + + prof_desc = &aw_dev->aw88261_base->prof_info.prof_desc[index]; + + return prof_info->prof_name_list[prof_desc->id]; +} + +int aw88261_dev_get_prof_data(struct aw88261_device *aw_dev, int index, + struct aw_prof_desc **prof_desc) +{ + if ((index >= aw_dev->aw88261_base->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n", + __func__, index, aw_dev->aw88261_base->prof_info.count); + return -EINVAL; + } + + *prof_desc = &aw_dev->aw88261_base->prof_info.prof_desc[index]; + + return 0; +} + +int aw88261_init(struct aw88261_device **aw_dev, struct i2c_client *i2c, struct regmap *regmap) +{ + unsigned int 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 aw88261_device), GFP_KERNEL); + if (!(*aw_dev)) + return -ENOMEM; + } + + (*aw_dev)->aw88261_base = + devm_kzalloc(&i2c->dev, sizeof(struct aw_device), GFP_KERNEL); + if (!(*aw_dev)->aw88261_base) + return -ENOMEM; + + (*aw_dev)->aw88261_base->i2c = i2c; + (*aw_dev)->aw88261_base->dev = &i2c->dev; + (*aw_dev)->aw88261_base->regmap = regmap; + (*aw_dev)->dev = &i2c->dev; + + /* read chip id */ + ret = regmap_read((*aw_dev)->aw88261_base->regmap, AW88261_CHIP_ID_REG, &chip_id); + 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", chip_id); + + 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; +} + +MODULE_DESCRIPTION("AW88261 device"); +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..c18f2328f792 --- /dev/null +++ b/sound/soc/codecs/aw88261/aw88261_device.h @@ -0,0 +1,79 @@ +// 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 +// Author: Weidong Wang +// + +#ifndef __AW88261_DEVICE_FILE_H__ +#define __AW88261_DEVICE_FILE_H__ + +#include "aw88261.h" +#include "../aw88395/aw88395_device.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) +#define AW_INIT_PROFILE (0) + +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_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 aw88261_device { + struct aw_device *aw88261_base; + struct device *dev; + + int efuse_check; + int frcset_en; + unsigned int mute_st; + unsigned int amppd_st; + + unsigned char phase_sync; +}; + +int aw88261_init(struct aw88261_device **aw_dev, struct i2c_client *i2c, struct regmap *regmap); +int aw88261_dev_init(struct aw88261_device *aw_dev, struct aw_container *aw_cfg); +int aw88261_dev_start(struct aw88261_device *aw_dev); +int aw88261_dev_stop(struct aw88261_device *aw_dev); +int aw88261_dev_reg_update(struct aw88261_device *aw_dev, bool force); +void aw88261_dev_set_volume(struct aw88261_device *aw_dev, unsigned int set_vol); +int aw88261_dev_get_prof_data(struct aw88261_device *aw_dev, int index, + struct aw_prof_desc **prof_desc); +char *aw88261_dev_get_prof_name(struct aw88261_device *aw_dev, int index); +int aw88261_dev_set_profile_index(struct aw88261_device *aw_dev, int index); +void aw88261_dev_mute(struct aw88261_device *aw_dev, bool is_mute); + +#endif From patchwork Sat Jul 29 09:12:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: wangweidong.a@awinic.com X-Patchwork-Id: 13333054 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id B7467C0015E for ; Sat, 29 Jul 2023 09:17:28 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id E771283A; Sat, 29 Jul 2023 11:16:36 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz E771283A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1690622247; bh=Gz0W4BGi1I4gfSjNKMNH2sKo+zdcUhLCBSQPzJYc62g=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=A/FH+qCbzWRv6w/qJPG7M98+RgQtHO/+rwnF9jk7tkc7hpa5BQ9Cc32gNaQHVAeum 3dW81VKidrBpqrUv49junUkH5nCTDTjP3WQy6IvKx/nItvhdThWsi7apN6aTK0QFSf 4cUpJAUVUin9Z+mYbvrKdJVLRZVqyxkrwH1PvTPg= Received: by alsa1.perex.cz (Postfix, from userid 50401) id 57939F8055A; Sat, 29 Jul 2023 11:16:10 +0200 (CEST) Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id EE9BBF8055A; Sat, 29 Jul 2023 11:16:09 +0200 (CEST) Received: by alsa1.perex.cz (Postfix, from userid 50401) id CD8CFF80564; Sat, 29 Jul 2023 11:16:06 +0200 (CEST) Received: from out28-197.mail.aliyun.com (out28-197.mail.aliyun.com [115.124.28.197]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 5B924F802E8 for ; Sat, 29 Jul 2023 11:12:52 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 5B924F802E8 X-Alimail-AntiSpam: AC=CONTINUE;BC=0.06712923|-1;CH=green;DM=|CONTINUE|false|;DS=CONTINUE|ham_system_inform|0.0145862-0.00466879-0.980745;FP=0|0|0|0|0|-1|-1|-1;HT=ay29a033018047213;MF=wangweidong.a@awinic.com;NM=1;PH=DS;RN=25;RT=25;SR=0;TI=SMTPD_---.U3WkWwU_1690621963; Received: from ubuntu-VirtualBox..(mailfrom:wangweidong.a@awinic.com fp:SMTPD_---.U3WkWwU_1690621963) by smtp.aliyun-inc.com; Sat, 29 Jul 2023 17:12: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, shumingf@realtek.com, ryans.lee@analog.com, 13916275206@139.com, herve.codina@bootlin.com, ckeepax@opensource.cirrus.com, doug@schmorgal.com, fido_max@inbox.ru, povik+lin@cutebit.org, liweilei@awinic.com, yijiangtao@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 V3 5/5] ASoC: codecs: aw88261 chip register file, Kconfig and Makefile Date: Sat, 29 Jul 2023 17:12:23 +0800 Message-ID: <20230729091223.193466-6-wangweidong.a@awinic.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230729091223.193466-1-wangweidong.a@awinic.com> References: <20230729091223.193466-1-wangweidong.a@awinic.com> MIME-Version: 1.0 Message-ID-Hash: QAAQDDTOLX4JJBRBHOWBLSGOLLHPSE7M X-Message-ID-Hash: QAAQDDTOLX4JJBRBHOWBLSGOLLHPSE7M X-MailFrom: wangweidong.a@awinic.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Weidong Wang Mainly includes aw88261 register table, Makefile and Kconfig. Signed-off-by: Weidong Wang --- sound/soc/codecs/Kconfig | 15 + sound/soc/codecs/Makefile | 3 + sound/soc/codecs/aw88261/aw88261_reg.h | 374 +++++++++++++++++++++++++ 3 files changed, 392 insertions(+) create mode 100644 sound/soc/codecs/aw88261/aw88261_reg.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index c2de4ee72183..1e3526812cc8 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_CHV3_CODEC @@ -640,6 +641,20 @@ config SND_SOC_AW88395 digital Smart K audio amplifier with an integrated 10V smart boost convert. +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 + help + This option enables support for aw88261 Smart PA. + The awinic AW88261 is an I2S/TDM input, high efficiency + digital Smart K audio amplifier. The output voltage of + boost converter can be adjusted smartly according to + the input amplitude. + config SND_SOC_BD28623 tristate "ROHM BD28623 CODEC" help diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index b48a9a323b84..9df43de213f0 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -49,6 +49,8 @@ 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-objs := aw88261/aw88261.o \ + aw88261/aw88261_device.o snd-soc-bd28623-objs := bd28623.o snd-soc-bt-sco-objs := bt-sco.o snd-soc-chv3-codec-objs := chv3-codec.o @@ -431,6 +433,7 @@ 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) +=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_CHV3_CODEC) += snd-soc-chv3-codec.o diff --git a/sound/soc/codecs/aw88261/aw88261_reg.h b/sound/soc/codecs/aw88261/aw88261_reg.h new file mode 100644 index 000000000000..7ef128a3e6ee --- /dev/null +++ b/sound/soc/codecs/aw88261/aw88261_reg.h @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88261_reg.h -- AW88261 chip register file +// +// Copyright (c) 2023 awinic Technology CO., LTD +// +// Author: Jimmy Zhang +// Author: Weidong Wang +// + +#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<