From e9f08e779976bbfef7d168c70350083878db7e2e Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Mon, 15 Jun 2026 21:44:39 +0800 Subject: ASoC: SOF: add Intel UAOL sof_ipc_dai_type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The type will be used for Intel USB Audio Offload Link (UAOL) DAI. Signed-off-by: Bard Liao Reviewed-by: Kai Vehmanen Reviewed-by: Péter Ujfalusi Link: https://patch.msgid.link/20260615134439.1044872-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/dai.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h index 36809f712723..0b6a6ba6489a 100644 --- a/include/sound/sof/dai.h +++ b/include/sound/sof/dai.h @@ -90,6 +90,7 @@ enum sof_ipc_dai_type { SOF_DAI_AMD_HS_VIRTUAL, /**< AMD ACP HS VIRTUAL */ SOF_DAI_IMX_MICFIL, /** < i.MX MICFIL PDM */ SOF_DAI_AMD_SDW, /**< AMD ACP SDW */ + SOF_DAI_INTEL_UAOL, /**< Intel UAOL */ }; /* general purpose DAI configuration */ -- cgit v1.2.3 From c97f0bf5f705b16d150f2b0d5ce0ee24eee4f68a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 26 Jun 2026 05:45:10 +0000 Subject: ASoC: sdw_utils: tidyup .count_sidecar count_sidecar() is not using *card. Tidyup it. Current code makes old style / new style conversion difficult. To make future conversions easier to understand, this patch clean up the code a little. but no functional change. Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/87jyrlety1.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc_sdw_utils.h | 28 ++++++++++++++-------------- sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c | 4 +--- sound/soc/sdw_utils/soc_sdw_utils.c | 2 +- 3 files changed, 16 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h index 79c21966220b..443d63dc6ea3 100644 --- a/include/sound/soc_sdw_utils.h +++ b/include/sound/soc_sdw_utils.h @@ -44,6 +44,18 @@ struct asoc_sdw_codec_info; +struct asoc_sdw_mc_private { + struct snd_soc_card card; + struct snd_soc_jack sdw_headset; + struct device *headset_codec_dev; /* only one headset per card */ + struct device *amp_dev1, *amp_dev2; + bool append_dai_type; + bool ignore_internal_dmic; + void *private; + unsigned long mc_quirk; + int codec_info_list_count; +}; + struct asoc_sdw_dai_info { const bool direction[2]; /* playback & capture support */ const char *codec_name; @@ -88,25 +100,13 @@ struct asoc_sdw_codec_info { int (*codec_card_late_probe)(struct snd_soc_card *card); - int (*count_sidecar)(struct snd_soc_card *card, + int (*count_sidecar)(struct asoc_sdw_mc_private *ctx, int *num_dais, int *num_devs); int (*add_sidecar)(struct snd_soc_card *card, struct snd_soc_dai_link **dai_links, struct snd_soc_codec_conf **codec_conf); }; -struct asoc_sdw_mc_private { - struct snd_soc_card card; - struct snd_soc_jack sdw_headset; - struct device *headset_codec_dev; /* only one headset per card */ - struct device *amp_dev1, *amp_dev2; - bool append_dai_type; - bool ignore_internal_dmic; - void *private; - unsigned long mc_quirk; - int codec_info_list_count; -}; - struct asoc_sdw_endpoint { struct list_head list; @@ -235,7 +235,7 @@ int asoc_sdw_es9356_amp_init(struct snd_soc_card *card, int asoc_sdw_es9356_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); /* CS AMP support */ -int asoc_sdw_bridge_cs35l56_count_sidecar(struct snd_soc_card *card, +int asoc_sdw_bridge_cs35l56_count_sidecar(struct asoc_sdw_mc_private *ctx, int *num_dais, int *num_devs); int asoc_sdw_bridge_cs35l56_add_sidecar(struct snd_soc_card *card, struct snd_soc_dai_link **dai_links, diff --git a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c index e0e32a279787..129a437ae397 100644 --- a/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c +++ b/sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c @@ -99,11 +99,9 @@ static const struct snd_soc_dai_link bridge_dai_template = { SND_SOC_DAILINK_REG(asoc_sdw_bridge_dai), }; -int asoc_sdw_bridge_cs35l56_count_sidecar(struct snd_soc_card *card, +int asoc_sdw_bridge_cs35l56_count_sidecar(struct asoc_sdw_mc_private *ctx, int *num_dais, int *num_devs) { - struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); - if (ctx->mc_quirk & SOC_SDW_SIDECAR_AMPS) { (*num_dais)++; (*num_devs) += ARRAY_SIZE(bridge_cs35l56_name_prefixes); diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index d8db8fc5313e..073f3f9205a7 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -2045,7 +2045,7 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic; if (codec_info->count_sidecar && codec_info->add_sidecar) { - ret = codec_info->count_sidecar(card, &num_dais, num_devs); + ret = codec_info->count_sidecar(ctx, &num_dais, num_devs); if (ret) return ret; -- cgit v1.2.3 From a1332be2a07090cf422507ec812ce2b9ba0a558a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 26 Jun 2026 05:45:15 +0000 Subject: ASoC: sdw_utils: tidyup asoc_sdw_parse_sdw_endpoints() We can avoid to use *card. Tidyup it. Current code makes old style / new style conversion difficult. To make future conversions easier to understand, this patch clean up the code a little. but no functional change. Signed-off-by: Kuninori Morimoto Reviewed-by: Cezary Rojewski Reviewed-by: Vijendar Mukunda Link: https://patch.msgid.link/87ik75etxw.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc_sdw_utils.h | 3 ++- sound/soc/amd/acp/acp-sdw-legacy-mach.c | 2 +- sound/soc/amd/acp/acp-sdw-sof-mach.c | 2 +- sound/soc/intel/boards/sof_sdw.c | 2 +- sound/soc/sdw_utils/soc_sdw_utils.c | 5 ++--- 5 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h index 443d63dc6ea3..9b28e9aef4f1 100644 --- a/include/sound/soc_sdw_utils.h +++ b/include/sound/soc_sdw_utils.h @@ -182,7 +182,8 @@ struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks const struct snd_soc_acpi_endpoint *new); int asoc_sdw_get_dai_type(u32 type); -int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, +int asoc_sdw_parse_sdw_endpoints(struct device *dev, + struct asoc_sdw_mc_private *ctx, struct snd_soc_aux_dev *soc_aux, struct asoc_sdw_dailink *soc_dais, struct asoc_sdw_endpoint *soc_ends, diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index e8b6819cc4b4..9726a9d33ec6 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -432,7 +432,7 @@ static int soc_card_dai_links_create(struct snd_soc_card *card) if (!soc_aux) return -ENOMEM; - ret = asoc_sdw_parse_sdw_endpoints(card, soc_aux, soc_dais, soc_ends, &num_confs); + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, soc_aux, soc_dais, soc_ends, &num_confs); if (ret < 0) return ret; diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c index a423853f3a97..963ce6fd4012 100644 --- a/sound/soc/amd/acp/acp-sdw-sof-mach.c +++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c @@ -303,7 +303,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) if (!sof_aux) return -ENOMEM; - ret = asoc_sdw_parse_sdw_endpoints(card, sof_aux, sof_dais, sof_ends, &num_devs); + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_devs); if (ret < 0) return ret; diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index d43daf9b025d..24226a387cc2 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -1285,7 +1285,7 @@ static int sof_card_dai_links_create(struct snd_soc_card *card) goto err_dai; } - ret = asoc_sdw_parse_sdw_endpoints(card, sof_aux, sof_dais, sof_ends, &num_confs); + ret = asoc_sdw_parse_sdw_endpoints(dev, ctx, sof_aux, sof_dais, sof_ends, &num_confs); if (ret < 0) goto err_end; diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 073f3f9205a7..dd2cc57059d6 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -1976,14 +1976,13 @@ put_device: return ret; } -int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, +int asoc_sdw_parse_sdw_endpoints(struct device *dev, + struct asoc_sdw_mc_private *ctx, struct snd_soc_aux_dev *soc_aux, struct asoc_sdw_dailink *soc_dais, struct asoc_sdw_endpoint *soc_ends, int *num_devs) { - struct device *dev = card->dev; - struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); struct snd_soc_acpi_mach *mach = dev_get_platdata(dev); struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; const struct snd_soc_acpi_link_adr *adr_link; -- cgit v1.2.3 From 08c5e98b7b5ff5aa0c2774bf58a5a71e2741f603 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:15 +0530 Subject: ASoC: SOF: amd: add ACP I2S format field and topology token Add format field to sof_ipc_dai_acp_params for ACP I2S format selection. Add SOF_TKN_AMD_ACPI2S_FORMAT (1703) to the existing SOF_ACPI2S_TOKENS tuple and wire it into acpi2s_tokens[] so integrators continue using the same ACPI2S token group as earlier ACP I2S topologies, not a separate ACPTDM-specific token set. Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-15-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- include/sound/sof/dai-amd.h | 1 + include/uapi/sound/sof/tokens.h | 1 + sound/soc/sof/ipc3-topology.c | 7 ++++++- 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/sof/dai-amd.h b/include/sound/sof/dai-amd.h index 59cd014392c1..e2107da6558f 100644 --- a/include/sound/sof/dai-amd.h +++ b/include/sound/sof/dai-amd.h @@ -18,6 +18,7 @@ struct sof_ipc_dai_acp_params { uint32_t fsync_rate; /* FSYNC frequency in Hz */ uint32_t tdm_slots; uint32_t tdm_mode; + uint32_t format; } __packed; /* ACPDMIC Configuration Request - SOF_IPC_DAI_AMD_CONFIG */ diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index f4a7baadb44d..cc694a397987 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -223,6 +223,7 @@ #define SOF_TKN_AMD_ACPI2S_RATE 1700 #define SOF_TKN_AMD_ACPI2S_CH 1701 #define SOF_TKN_AMD_ACPI2S_TDM_MODE 1702 +#define SOF_TKN_AMD_ACPI2S_FORMAT 1703 /* MICFIL PDM */ #define SOF_TKN_IMX_MICFIL_RATE 2000 diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 4e066bbded91..9eb8335a3c07 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -281,7 +281,10 @@ static const struct sof_topology_token acpdmic_tokens[] = { offsetof(struct sof_ipc_dai_acpdmic_params, pdm_ch)}, }; -/* ACPI2S */ +/* + * ACPI2S tokens fill struct sof_ipc_dai_acp_params; SOF_DAI_AMD_I2S (ACPTDM + * on ACP7.B/7.F) reuses this tuple group rather than defining a parallel set. + */ static const struct sof_topology_token acpi2s_tokens[] = { {SOF_TKN_AMD_ACPI2S_RATE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc_dai_acp_params, fsync_rate)}, @@ -289,6 +292,8 @@ static const struct sof_topology_token acpi2s_tokens[] = { offsetof(struct sof_ipc_dai_acp_params, tdm_slots)}, {SOF_TKN_AMD_ACPI2S_TDM_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc_dai_acp_params, tdm_mode)}, + {SOF_TKN_AMD_ACPI2S_FORMAT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_dai_acp_params, format)}, }; /* MICFIL PDM */ -- cgit v1.2.3 From 3805b8e6f932fbf9bfe5803b6a85328cc468b75d Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Wed, 1 Jul 2026 15:25:16 +0530 Subject: ASoC: SOF: amd: add ACP7x I2S DAI type and topology support Add SOF_DAI_AMD_I2S DAI type for ACP7.B/7.F I2S/TDM interfaces. Register the ACPTDM topology DAI name and map it to SOF_DAI_AMD_I2S; IPC3 continues to parse ACP I2S link parameters through SOF_ACPI2S_TOKENS (including the format token from the prior commit), not a new token group named after ACPTDM. Add sof_link_acp_i2s_load() and the SOF_DAI_AMD_I2S PCM dai link fixup path. Signed-off-by: Vijendar Mukunda Reviewed-by: Bard Liao Link: https://patch.msgid.link/20260701095759.1012929-16-Vijendar.Mukunda@amd.com Signed-off-by: Mark Brown --- include/sound/sof/dai.h | 2 ++ sound/soc/sof/ipc3-pcm.c | 6 ++++++ sound/soc/sof/ipc3-topology.c | 32 ++++++++++++++++++++++++++++++++ sound/soc/sof/topology.c | 3 ++- 4 files changed, 42 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/sof/dai.h b/include/sound/sof/dai.h index 0b6a6ba6489a..e3fe492e78f5 100644 --- a/include/sound/sof/dai.h +++ b/include/sound/sof/dai.h @@ -91,6 +91,7 @@ enum sof_ipc_dai_type { SOF_DAI_IMX_MICFIL, /** < i.MX MICFIL PDM */ SOF_DAI_AMD_SDW, /**< AMD ACP SDW */ SOF_DAI_INTEL_UAOL, /**< Intel UAOL */ + SOF_DAI_AMD_I2S, /**< AMD ACP I2S */ }; /* general purpose DAI configuration */ @@ -122,6 +123,7 @@ struct sof_ipc_dai_config { struct sof_ipc_dai_mtk_afe_params afe; struct sof_ipc_dai_micfil_params micfil; struct sof_ipc_dai_acp_sdw_params acp_sdw; + struct sof_ipc_dai_acp_params acp_i2s; }; } __packed; diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index 90ef5d99f626..143bf0fe8dd9 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -421,6 +421,12 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, dev_dbg(component->dev, "AMD_SDW channels_min: %d channels_max: %d\n", channels->min, channels->max); break; + case SOF_DAI_AMD_I2S: + rate->min = private->dai_config->acp_i2s.fsync_rate; + rate->max = private->dai_config->acp_i2s.fsync_rate; + channels->min = private->dai_config->acp_i2s.tdm_slots; + channels->max = private->dai_config->acp_i2s.tdm_slots; + break; default: dev_err(component->dev, "Invalid DAI type %d\n", private->dai_config->type); break; diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 9eb8335a3c07..26d85ca9be26 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -1368,6 +1368,35 @@ static int sof_link_acp_sdw_load(struct snd_soc_component *scomp, struct snd_sof return 0; } +static int sof_link_acp_i2s_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, + struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) +{ + struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; + struct sof_dai_private_data *private = dai->private; + u32 size = sizeof(*config); + int ret; + + /* handle master/slave and inverted clocks */ + sof_dai_set_format(hw_config, config); + + /* init IPC */ + memset(&config->acp_i2s, 0, sizeof(config->acp_i2s)); + config->hdr.size = size; + + ret = sof_update_ipc_object(scomp, &config->acp_i2s, SOF_ACPI2S_TOKENS, slink->tuples, + slink->num_tuples, size, slink->num_hw_configs); + if (ret < 0) + return ret; + + dai->number_configs = 1; + dai->current_config = 0; + private->dai_config = kmemdup(config, size, GFP_KERNEL); + if (!private->dai_config) + return -ENOMEM; + + return 0; +} + static int sof_link_afe_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) { @@ -1697,6 +1726,9 @@ static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) case SOF_DAI_AMD_SDW: ret = sof_link_acp_sdw_load(scomp, slink, config, dai); break; + case SOF_DAI_AMD_I2S: + ret = sof_link_acp_i2s_load(scomp, slink, config, dai); + break; default: break; } diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 6de8a6c1c127..6fd69ba11c41 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -309,7 +309,7 @@ static const struct sof_dai_types sof_dais[] = { {"ACPHS_VIRTUAL", SOF_DAI_AMD_HS_VIRTUAL}, {"MICFIL", SOF_DAI_IMX_MICFIL}, {"ACP_SDW", SOF_DAI_AMD_SDW}, - + {"ACPTDM", SOF_DAI_AMD_I2S}, }; static enum sof_ipc_dai_type find_dai(const char *name) @@ -1994,6 +1994,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ case SOF_DAI_AMD_HS: case SOF_DAI_AMD_SP_VIRTUAL: case SOF_DAI_AMD_HS_VIRTUAL: + case SOF_DAI_AMD_I2S: token_id = SOF_ACPI2S_TOKENS; num_tuples += token_list[SOF_ACPI2S_TOKENS].count; break; -- cgit v1.2.3 From 7ad9d917e251be70cfa9733f990cfa5f7c49f9b5 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 2 Jul 2026 01:45:14 +0000 Subject: ASoC: simple_card_utils: add simple_util_parse_property() We have simple_util_parse_{routing/widgets/pin_switches}(). These are doing almost same things, but has each own implementation. Les't adds new simple_util_parse_property() and share the code. To be more easy cleanup later, change the required parameter from "card" to "priv". Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/87bjcqxiza.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 27 ++++++++++--- sound/soc/generic/audio-graph-card.c | 4 +- sound/soc/generic/audio-graph-card2.c | 4 +- sound/soc/generic/simple-card-utils.c | 72 ++++++++++------------------------- sound/soc/generic/simple-card.c | 6 +-- 5 files changed, 49 insertions(+), 64 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 915e6ae5f68d..dfa1b5fb7aa8 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -189,12 +189,27 @@ bool simple_util_is_convert_required(const struct simple_util_data *data); int simple_util_get_sample_fmt(struct simple_util_data *data); -int simple_util_parse_routing(struct snd_soc_card *card, - char *prefix); -int simple_util_parse_widgets(struct snd_soc_card *card, - char *prefix); -int simple_util_parse_pin_switches(struct snd_soc_card *card, - char *prefix); +int simple_util_parse_property(struct simple_util_priv *priv, + int (*func)(struct snd_soc_card *card, const char *propname), + char *prefix, char *property); +static inline int simple_util_parse_routing(struct simple_util_priv *priv, char *prefix) +{ + return simple_util_parse_property(priv, snd_soc_of_parse_audio_routing, + prefix, "routing"); +} + +static inline int simple_util_parse_widgets(struct simple_util_priv *priv, char *prefix) +{ + return simple_util_parse_property(priv, snd_soc_of_parse_audio_simple_widgets, + prefix, "widgets"); +} + +static inline int simple_util_parse_pin_switches(struct simple_util_priv *priv, char *prefix) +{ + return simple_util_parse_property(priv, snd_soc_of_parse_pin_switches, + prefix, "pin-switches"); +} + int simple_util_init_jack(struct snd_soc_card *card, struct simple_util_jack *sjack, diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 42e1b77fa65e..273b0c82ebea 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -579,11 +579,11 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) goto end; } - ret = simple_util_parse_widgets(card, NULL); + ret = simple_util_parse_widgets(priv, NULL); if (ret < 0) goto end; - ret = simple_util_parse_routing(card, NULL); + ret = simple_util_parse_routing(priv, NULL); if (ret < 0) goto end; diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index c5ada1f83881..b4ae9bb860a1 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -1336,11 +1336,11 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, goto end; } - ret = simple_util_parse_widgets(card, NULL); + ret = simple_util_parse_widgets(priv, NULL); if (ret < 0) goto end; - ret = simple_util_parse_routing(card, NULL); + ret = simple_util_parse_routing(priv, NULL); if (ret < 0) goto end; diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index e5cb602fd248..522dd1e3c96a 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -216,6 +216,27 @@ int simple_util_set_dailink_name(struct simple_util_priv *priv, } EXPORT_SYMBOL_GPL(simple_util_set_dailink_name); +int simple_util_parse_property(struct simple_util_priv *priv, + int (*func)(struct snd_soc_card *card, const char *propname), + char *prefix, char *property) +{ + struct snd_soc_card *card = simple_priv_to_card(priv); + struct device_node *node = card->dev->of_node; + char prop[128]; + + if (!prefix) + prefix = ""; + + snprintf(prop, sizeof(prop), "%s%s", prefix, property); + + /* no property is not error */ + if (!of_property_present(node, prop)) + return 0; + + return func(card, prop); +} +EXPORT_SYMBOL_GPL(simple_util_parse_property); + int simple_util_parse_card_name(struct simple_util_priv *priv, char *prefix) { @@ -747,57 +768,6 @@ void simple_util_clean_reference(struct snd_soc_card *card) } EXPORT_SYMBOL_GPL(simple_util_clean_reference); -int simple_util_parse_routing(struct snd_soc_card *card, - char *prefix) -{ - struct device_node *node = card->dev->of_node; - char prop[128]; - - if (!prefix) - prefix = ""; - - snprintf(prop, sizeof(prop), "%s%s", prefix, "routing"); - - if (!of_property_present(node, prop)) - return 0; - - return snd_soc_of_parse_audio_routing(card, prop); -} -EXPORT_SYMBOL_GPL(simple_util_parse_routing); - -int simple_util_parse_widgets(struct snd_soc_card *card, - char *prefix) -{ - struct device_node *node = card->dev->of_node; - char prop[128]; - - if (!prefix) - prefix = ""; - - snprintf(prop, sizeof(prop), "%s%s", prefix, "widgets"); - - if (of_property_present(node, prop)) - return snd_soc_of_parse_audio_simple_widgets(card, prop); - - /* no widgets is not error */ - return 0; -} -EXPORT_SYMBOL_GPL(simple_util_parse_widgets); - -int simple_util_parse_pin_switches(struct snd_soc_card *card, - char *prefix) -{ - char prop[128]; - - if (!prefix) - prefix = ""; - - snprintf(prop, sizeof(prop), "%s%s", prefix, "pin-switches"); - - return snd_soc_of_parse_pin_switches(card, prop); -} -EXPORT_SYMBOL_GPL(simple_util_parse_pin_switches); - int simple_util_init_jack(struct snd_soc_card *card, struct simple_util_jack *sjack, int is_hp, char *prefix, diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 20e6edd1d7d3..6e432733649f 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -692,15 +692,15 @@ static int simple_parse_of(struct simple_util_priv *priv) if (ret < 0) goto end; - ret = simple_util_parse_widgets(card, PREFIX); + ret = simple_util_parse_widgets(priv, PREFIX); if (ret < 0) goto end; - ret = simple_util_parse_routing(card, PREFIX); + ret = simple_util_parse_routing(priv, PREFIX); if (ret < 0) goto end; - ret = simple_util_parse_pin_switches(card, PREFIX); + ret = simple_util_parse_pin_switches(priv, PREFIX); if (ret < 0) goto end; -- cgit v1.2.3 From d0ab37b02826fc4b2b78cb3f47416cc2dbbd9e7e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 2 Jul 2026 01:45:32 +0000 Subject: ASoC: simple_card_utils: add simple_util_parse_aux_devs() We are using snd_soc_of_parse_aux_devs() directly, but can use simple_util_parse_property(). use it. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/878q7uxiyr.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 5 +++++ sound/soc/generic/audio-graph-card2.c | 2 +- sound/soc/generic/simple-card.c | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index dfa1b5fb7aa8..ff9747c6aa49 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -210,6 +210,11 @@ static inline int simple_util_parse_pin_switches(struct simple_util_priv *priv, prefix, "pin-switches"); } +static inline int simple_util_parse_aux_devs(struct simple_util_priv *priv, char *prefix) +{ + return simple_util_parse_property(priv, snd_soc_of_parse_aux_devs, + prefix, "aux-devs"); +} int simple_util_init_jack(struct snd_soc_card *card, struct simple_util_jack *sjack, diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index b4ae9bb860a1..4dee28276671 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -1363,7 +1363,7 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, simple_util_debug_info(priv); - ret = snd_soc_of_parse_aux_devs(card, "aux-devs"); + ret = simple_util_parse_aux_devs(priv, NULL); if (ret < 0) goto err; diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 6e432733649f..4dd05e726213 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -720,7 +720,7 @@ static int simple_parse_of(struct simple_util_priv *priv) if (ret < 0) goto err; - ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs"); + ret = simple_util_parse_aux_devs(priv, PREFIX); if (ret < 0) goto err; -- cgit v1.2.3 From 279bfbb150aef95a3ee281cdd9ebefdddfb43a1e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 2 Jul 2026 01:46:02 +0000 Subject: ASoC: simple-card-utils: tidyup simple_util_init_aux_jacks() Current code makes old style / new style conversion difficult. To make future conversions easier to understand, this patch clean up the code a little. but no functional change. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/8733y2xixy.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 3 +-- sound/soc/generic/simple-card-utils.c | 4 ++-- sound/soc/generic/simple-card.c | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index ff9747c6aa49..5e2e91578bf5 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -219,8 +219,7 @@ static inline int simple_util_parse_aux_devs(struct simple_util_priv *priv, char int simple_util_init_jack(struct snd_soc_card *card, struct simple_util_jack *sjack, int is_hp, char *prefix, char *pin); -int simple_util_init_aux_jacks(struct simple_util_priv *priv, - char *prefix); +int simple_util_init_aux_jacks(struct snd_soc_card *card, char *prefix); int simple_util_init_priv(struct simple_util_priv *priv, struct link_info *li); void simple_util_remove(struct platform_device *pdev); diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 522dd1e3c96a..44632759b185 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -824,9 +824,9 @@ int simple_util_init_jack(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(simple_util_init_jack); -int simple_util_init_aux_jacks(struct simple_util_priv *priv, char *prefix) +int simple_util_init_aux_jacks(struct snd_soc_card *card, char *prefix) { - struct snd_soc_card *card = simple_priv_to_card(priv); + struct simple_util_priv *priv = snd_soc_card_get_drvdata(card); struct snd_soc_component *component; int found_jack_index = 0; int type = 0; diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index a67f4aad0188..49289cd655ea 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -661,7 +661,7 @@ static int simple_soc_probe(struct snd_soc_card *card) if (ret < 0) goto end; - ret = simple_util_init_aux_jacks(priv, PREFIX); + ret = simple_util_init_aux_jacks(card, PREFIX); end: return simple_ret(priv, ret); } -- cgit v1.2.3 From 6f5c4f6ffa8bd87803145d03b1ad36d4fa50d562 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 2 Jul 2026 01:46:09 +0000 Subject: ASoC: simple-card-utils: tidyup simple_util_clean_reference() Current code makes old style / new style conversion difficult. To make future conversions easier to understand, this patch clean up the code a little. but no functional change. Signed-off-by: Kuninori Morimoto Link: https://patch.msgid.link/871pdmxixq.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 2 +- sound/soc/generic/audio-graph-card.c | 2 +- sound/soc/generic/audio-graph-card2.c | 2 +- sound/soc/generic/simple-card-utils.c | 6 ++++-- sound/soc/generic/simple-card.c | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 5e2e91578bf5..bd8c3a033577 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -181,7 +181,7 @@ void simple_util_canonicalize_platform(struct snd_soc_dai_link_component *platfo void simple_util_canonicalize_cpu(struct snd_soc_dai_link_component *cpus, int is_single_links); -void simple_util_clean_reference(struct snd_soc_card *card); +void simple_util_clean_reference(struct simple_util_priv *priv); void simple_util_parse_convert(struct device_node *np, char *prefix, struct simple_util_data *data); diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index f5e36c777ac6..73ea562ce52b 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -605,7 +605,7 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev) ret = devm_snd_soc_register_card(dev, card); err: if (ret < 0) { - simple_util_clean_reference(card); + simple_util_clean_reference(priv); return dev_err_probe(dev, ret, "parse error\n"); } end: diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 9ef7373a6ca6..e3e92025b317 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -1370,7 +1370,7 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev, ret = devm_snd_soc_register_card(dev, card); err: if (ret < 0) { - simple_util_clean_reference(card); + simple_util_clean_reference(priv); return dev_err_probe(dev, ret, "parse error\n"); } end: diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 44632759b185..42019daa5e04 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -752,11 +752,12 @@ void simple_util_canonicalize_cpu(struct snd_soc_dai_link_component *cpus, } EXPORT_SYMBOL_GPL(simple_util_canonicalize_cpu); -void simple_util_clean_reference(struct snd_soc_card *card) +void simple_util_clean_reference(struct simple_util_priv *priv) { struct snd_soc_dai_link *dai_link; struct snd_soc_dai_link_component *cpu; struct snd_soc_dai_link_component *codec; + struct snd_soc_card *card = simple_priv_to_card(priv); int i, j; for_each_card_prelinks(card, i, dai_link) { @@ -996,8 +997,9 @@ EXPORT_SYMBOL_GPL(simple_util_init_priv); void simple_util_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); + struct simple_util_priv *priv = snd_soc_card_get_drvdata(card); - simple_util_clean_reference(card); + simple_util_clean_reference(priv); } EXPORT_SYMBOL_GPL(simple_util_remove); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 49289cd655ea..abfbc9fd7c6d 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -731,7 +731,7 @@ static int simple_parse_of(struct simple_util_priv *priv) ret = devm_snd_soc_register_card(dev, card); err: if (ret < 0) { - simple_util_clean_reference(card); + simple_util_clean_reference(priv); return dev_err_probe(dev, ret, "parse error\n"); } end: -- cgit v1.2.3 From 7fa44519a2a3a9da715669a7f359f08e49911e2f Mon Sep 17 00:00:00 2001 From: Marco Giunta Date: Fri, 17 Jul 2026 15:25:03 +0200 Subject: ASoC: aw88399: extract shared device library Extract the device-level functions from aw88399.c into a new shared library module (aw88399-lib.c) with a shared header at include/sound/aw88399.h, following the pattern established by CS35L41 (cs35l41-lib.c / include/sound/cs35l41.h) for chips that need both ASoC and HDA drivers. The shared header at include/sound/aw88399.h contains the register definitions, bit-field masks, hardware constants, device enums, the struct aw88399 definition, and the library function declarations. The ASoC-private header at sound/soc/codecs/aw88399.h is reduced to ASoC-specific definitions (PCM formats/rates, ALSA kcontrol helpers, calibration constants) and includes the shared header. The library contains the chip initialization, firmware loading, playback start/stop sequences, and all their internal dependencies (PLL checks, DSP management, volume control, calibration, CRC verification, etc.). The ASoC codec driver retains the ALSA controls, DAPM widgets, codec probe/remove, calibration service, and I2C bus driver registration. A new Kconfig symbol SND_SOC_AW88399_LIB is introduced. SND_SOC_AW88399 (the existing ASoC codec) selects it, ensuring no change for current users. The HDA side codec driver (introduced later in this series) selects the library without pulling in the full ASoC codec module. This avoids a build-time dependency on the full ASoC driver and follows the established pattern used by CS35L41 (SND_SOC_CS35L41_LIB) for chips with both ASoC and HDA drivers. Some library functions (DSP control, volume setting, mute, calibration updates, profile management, and status helpers) are used internally by the library's start/stop sequences but are also called directly by the ASoC driver's remaining code. These are exported from the library so the ASoC module can access them. This is a pure code movement with no functional changes. The moved functions are identical to their originals in aw88399.c. Tested-by: Nadim Kobeissi Tested-by: Xia Yun'an Tested-by: Munzir Taha Signed-off-by: Marco Giunta Link: https://patch.msgid.link/DS7PR19MB772415C485FAF74297673FD7FCC62@DS7PR19MB7724.namprd19.prod.outlook.com Signed-off-by: Mark Brown --- include/sound/aw88399.h | 618 ++++++++++++++++++ sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/aw88399-lib.c | 1381 ++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/aw88399.c | 1349 --------------------------------------- sound/soc/codecs/aw88399.h | 582 +---------------- 6 files changed, 2008 insertions(+), 1929 deletions(-) create mode 100644 include/sound/aw88399.h create mode 100644 sound/soc/codecs/aw88399-lib.c (limited to 'include') diff --git a/include/sound/aw88399.h b/include/sound/aw88399.h new file mode 100644 index 000000000000..3a2153f0ee92 --- /dev/null +++ b/include/sound/aw88399.h @@ -0,0 +1,618 @@ +/* SPDX-License-Identifier: GPL-2.0-only + * + * linux/sound/aw88399.h -- Platform data for AW88399 + * + * Copyright (c) 2023 AWINIC Technology CO., LTD + * + * Author: Weidong Wang + */ + +#ifndef __SOUND_AW88399_H +#define __SOUND_AW88399_H + +#include +#include + +/* registers list */ +#define AW88399_ID_REG (0x00) +#define AW88399_SYSST_REG (0x01) +#define AW88399_SYSINT_REG (0x02) +#define AW88399_SYSINTM_REG (0x03) +#define AW88399_SYSCTRL_REG (0x04) +#define AW88399_SYSCTRL2_REG (0x05) +#define AW88399_I2SCTRL1_REG (0x06) +#define AW88399_I2SCTRL2_REG (0x07) +#define AW88399_I2SCTRL3_REG (0x08) +#define AW88399_DACCFG1_REG (0x09) +#define AW88399_DACCFG2_REG (0x0A) +#define AW88399_DACCFG3_REG (0x0B) +#define AW88399_DACCFG4_REG (0x0C) +#define AW88399_DACCFG5_REG (0x0D) +#define AW88399_DACCFG6_REG (0x0E) +#define AW88399_DACCFG7_REG (0x0F) +#define AW88399_MPDCFG1_REG (0x10) +#define AW88399_MPDCFG2_REG (0x11) +#define AW88399_MPDCFG3_REG (0x12) +#define AW88399_MPDCFG4_REG (0x13) +#define AW88399_PWMCTRL1_REG (0x14) +#define AW88399_PWMCTRL2_REG (0x15) +#define AW88399_PWMCTRL3_REG (0x16) +#define AW88399_I2SCFG1_REG (0x17) +#define AW88399_DBGCTRL_REG (0x18) +#define AW88399_HAGCST_REG (0x20) +#define AW88399_VBAT_REG (0x21) +#define AW88399_TEMP_REG (0x22) +#define AW88399_PVDD_REG (0x23) +#define AW88399_ISNDAT_REG (0x24) +#define AW88399_VSNDAT_REG (0x25) +#define AW88399_I2SINT_REG (0x26) +#define AW88399_I2SCAPCNT_REG (0x27) +#define AW88399_ANASTA1_REG (0x28) +#define AW88399_ANASTA2_REG (0x29) +#define AW88399_ANASTA3_REG (0x2A) +#define AW88399_TESTDET_REG (0x2B) +#define AW88399_DSMCFG1_REG (0x30) +#define AW88399_DSMCFG2_REG (0x31) +#define AW88399_DSMCFG3_REG (0x32) +#define AW88399_DSMCFG4_REG (0x33) +#define AW88399_DSMCFG5_REG (0x34) +#define AW88399_DSMCFG6_REG (0x35) +#define AW88399_DSMCFG7_REG (0x36) +#define AW88399_DSMCFG8_REG (0x37) +#define AW88399_TESTIN_REG (0x38) +#define AW88399_TESTOUT_REG (0x39) +#define AW88399_MEMTEST_REG (0x3A) +#define AW88399_VSNCTRL1_REG (0x3B) +#define AW88399_ISNCTRL1_REG (0x3C) +#define AW88399_ISNCTRL2_REG (0x3D) +#define AW88399_DSPMADD_REG (0x40) +#define AW88399_DSPMDAT_REG (0x41) +#define AW88399_WDT_REG (0x42) +#define AW88399_ACR1_REG (0x43) +#define AW88399_ACR2_REG (0x44) +#define AW88399_ASR1_REG (0x45) +#define AW88399_ASR2_REG (0x46) +#define AW88399_DSPCFG_REG (0x47) +#define AW88399_ASR3_REG (0x48) +#define AW88399_ASR4_REG (0x49) +#define AW88399_DSPVCALB_REG (0x4A) +#define AW88399_CRCCTRL_REG (0x4B) +#define AW88399_DSPDBG1_REG (0x4C) +#define AW88399_DSPDBG2_REG (0x4D) +#define AW88399_DSPDBG3_REG (0x4E) +#define AW88399_PLLCTRL1_REG (0x50) +#define AW88399_PLLCTRL2_REG (0x51) +#define AW88399_PLLCTRL3_REG (0x52) +#define AW88399_CDACTRL1_REG (0x53) +#define AW88399_CDACTRL2_REG (0x54) +#define AW88399_CDACTRL3_REG (0x55) +#define AW88399_SADCCTRL1_REG (0x56) +#define AW88399_SADCCTRL2_REG (0x57) +#define AW88399_BOPCTRL1_REG (0x58) +#define AW88399_BOPCTRL2_REG (0x5A) +#define AW88399_BOPCTRL3_REG (0x5B) +#define AW88399_BOPCTRL4_REG (0x5C) +#define AW88399_BOPCTRL5_REG (0x5D) +#define AW88399_BOPCTRL6_REG (0x5E) +#define AW88399_BOPCTRL7_REG (0x5F) +#define AW88399_BSTCTRL1_REG (0x60) +#define AW88399_BSTCTRL2_REG (0x61) +#define AW88399_BSTCTRL3_REG (0x62) +#define AW88399_BSTCTRL4_REG (0x63) +#define AW88399_BSTCTRL5_REG (0x64) +#define AW88399_BSTCTRL6_REG (0x65) +#define AW88399_BSTCTRL7_REG (0x66) +#define AW88399_BSTCTRL8_REG (0x67) +#define AW88399_BSTCTRL9_REG (0x68) +#define AW88399_BSTCTRL10_REG (0x69) +#define AW88399_CPCTRL_REG (0x6A) +#define AW88399_EFWH_REG (0x6C) +#define AW88399_EFWM2_REG (0x6D) +#define AW88399_EFWM1_REG (0x6E) +#define AW88399_EFWL_REG (0x6F) +#define AW88399_TESTCTRL1_REG (0x70) +#define AW88399_TESTCTRL2_REG (0x71) +#define AW88399_EFCTRL1_REG (0x72) +#define AW88399_EFCTRL2_REG (0x73) +#define AW88399_EFRH4_REG (0x74) +#define AW88399_EFRH3_REG (0x75) +#define AW88399_EFRH2_REG (0x76) +#define AW88399_EFRH1_REG (0x77) +#define AW88399_EFRL4_REG (0x78) +#define AW88399_EFRL3_REG (0x79) +#define AW88399_EFRL2_REG (0x7A) +#define AW88399_EFRL1_REG (0x7B) +#define AW88399_TM_REG (0x7C) +#define AW88399_TM2_REG (0x7D) + +#define AW88399_REG_MAX (0x7E) +#define AW88399_MUTE_VOL (1023) + +#define AW88399_DSP_CFG_ADDR (0x9B00) +#define AW88399_DSP_REG_CFG_ADPZ_RA (0x9B68) +#define AW88399_DSP_FW_ADDR (0x8980) +#define AW88399_DSP_ROM_CHECK_ADDR (0x1F40) +#define AW88399_DSP_ROM_CHECK_DATA (0x4638) + +#define AW88399_CALI_RE_HBITS_MASK (~(0xFFFF0000)) +#define AW88399_CALI_RE_HBITS_SHIFT (16) + +#define AW88399_CALI_RE_LBITS_MASK (~(0xFFFF)) +#define AW88399_CALI_RE_LBITS_SHIFT (0) + +#define AW88399_I2STXEN_START_BIT (9) +#define AW88399_I2STXEN_BITS_LEN (1) +#define AW88399_I2STXEN_MASK \ + (~(((1<> (shift)) +#define AW88399_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000)) +#define AW88399_CRC_CHECK_PASS_VAL (0x4) + +#define AW88399_CRC_CFG_BASE_ADDR (0xD80) +#define AW88399_CRC_FW_BASE_ADDR (0x4C0) +#define AW88399_ACF_FILE "aw88399_acf.bin" +#define AW88399_DEV_SYSST_CHECK_MAX (10) +#define AW88399_CHIP_ID 0x2183 + +#define AW88399_START_RETRIES (5) +#define AW88399_START_WORK_DELAY_MS (0) + +enum { + AW_EF_AND_CHECK = 0, + AW_EF_OR_CHECK, +}; + +enum { + AW88399_DEV_VDSEL_DAC = 0, + AW88399_DEV_VDSEL_VSENSE = 32, +}; + +enum { + AW88399_DSP_CRC_NA = 0, + AW88399_DSP_CRC_OK = 1, +}; + +enum { + AW88399_DSP_FW_UPDATE_OFF = 0, + AW88399_DSP_FW_UPDATE_ON = 1, +}; + +enum { + AW88399_FORCE_UPDATE_OFF = 0, + AW88399_FORCE_UPDATE_ON = 1, +}; + +enum { + AW88399_1000_US = 1000, + AW88399_2000_US = 2000, + AW88399_3000_US = 3000, + AW88399_4000_US = 4000, +}; + +enum AW88399_DEV_STATUS { + AW88399_DEV_PW_OFF = 0, + AW88399_DEV_PW_ON, +}; + +enum AW88399_DEV_FW_STATUS { + AW88399_DEV_FW_FAILED = 0, + AW88399_DEV_FW_OK, +}; + +enum AW88399_DEV_MEMCLK { + AW88399_DEV_MEMCLK_OSC = 0, + AW88399_DEV_MEMCLK_PLL = 1, +}; + +enum AW88399_DEV_DSP_CFG { + AW88399_DEV_DSP_WORK = 0, + AW88399_DEV_DSP_BYPASS = 1, +}; + +enum { + AW88399_NOT_RCV_MODE = 0, + AW88399_RCV_MODE = 1, +}; + +enum { + AW88399_SYNC_START = 0, + AW88399_ASYNC_START, +}; + +struct aw_device; +struct aw_container; +struct aw_cali_desc; +struct gpio_desc; +struct i2c_client; +struct regmap; + +struct aw88399 { + struct aw_device *aw_pa; + struct mutex lock; + struct gpio_desc *reset_gpio; + struct delayed_work start_work; + struct regmap *regmap; + struct aw_container *aw_cfg; + + unsigned int check_val; + unsigned int crc_init_val; + unsigned int vcalb_init_val; + unsigned int dither_st; +}; + +int aw_dev_check_syspll(struct aw_device *aw_dev); +void aw_dev_dsp_enable(struct aw_device *aw_dev, bool is_enable); +int aw_dev_get_dsp_status(struct aw_device *aw_dev); +int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value); +int aw_dev_update_cali_re(struct aw_cali_desc *cali_desc); +int aw88399_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name); +void aw88399_dev_mute(struct aw_device *aw_dev, bool is_mute); +void aw88399_hw_reset(struct aw88399 *aw88399); +int aw88399_init(struct aw88399 *aw88399, struct i2c_client *i2c, struct regmap *regmap); +extern const struct regmap_config aw88399_remap_config; +int aw88399_request_firmware_file(struct aw88399 *aw88399); +void aw88399_start(struct aw88399 *aw88399, bool sync_start); +void aw88399_startup_work(struct work_struct *work); +int aw88399_stop(struct aw_device *aw_dev); + +#endif /* __SOUND_AW88399_H */ diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 252f683be3c1..18e34899566e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -726,6 +726,10 @@ config SND_SOC_AW87390 sound quality, which is a new high efficiency, low noise, constant large volume, 6th Smart K audio amplifier. +config SND_SOC_AW88399_LIB + tristate + select SND_SOC_AW88395_LIB + config SND_SOC_AW88399 tristate "Soc Audio for awinic aw88399" depends on I2C @@ -733,6 +737,7 @@ config SND_SOC_AW88399 select REGMAP_I2C select GPIOLIB select SND_SOC_AW88395_LIB + select SND_SOC_AW88399_LIB help This option enables support for aw88399 Smart PA. The awinic AW88399 is an I2S/TDM input, high efficiency diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index aa0396e5b575..d2a689006d69 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -55,6 +55,7 @@ snd-soc-aw88395-y := aw88395/aw88395.o snd-soc-aw88166-y := aw88166.o snd-soc-aw88261-y := aw88261.o snd-soc-aw88399-y := aw88399.o +snd-soc-aw88399-lib-y := aw88399-lib.o snd-soc-bd28623-y := bd28623.o snd-soc-bt-sco-y := bt-sco.o snd-soc-chv3-codec-y := chv3-codec.o @@ -496,6 +497,7 @@ obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o obj-$(CONFIG_SND_SOC_AW88166) +=snd-soc-aw88166.o obj-$(CONFIG_SND_SOC_AW88261) +=snd-soc-aw88261.o obj-$(CONFIG_SND_SOC_AW88399) += snd-soc-aw88399.o +obj-$(CONFIG_SND_SOC_AW88399_LIB) += snd-soc-aw88399-lib.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/aw88399-lib.c b/sound/soc/codecs/aw88399-lib.c new file mode 100644 index 000000000000..258d6efbf590 --- /dev/null +++ b/sound/soc/codecs/aw88399-lib.c @@ -0,0 +1,1381 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88399-lib.c -- AW88399 Common functions for ASoC and HDA audio drivers +// +// Copyright (c) 2023 AWINIC Technology CO., LTD +// +// Author: Weidong Wang +// + +#include +#include +#include +#include +#include +#include +#include +#include "aw88395/aw88395_device.h" + +const struct regmap_config aw88399_remap_config = { + .val_bits = 16, + .reg_bits = 8, + .max_register = AW88399_REG_MAX, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_BIG, +}; +EXPORT_SYMBOL_GPL(aw88399_remap_config); + +static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd) +{ + int ret; + + if (pwd) + ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_PWDN_MASK, AW88399_PWDN_POWER_DOWN_VALUE); + else + ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_PWDN_MASK, AW88399_PWDN_WORKING_VALUE); + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +static void aw_dev_get_int_status(struct aw_device *aw_dev, unsigned short *int_status) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_SYSINT_REG, ®_val); + if (ret) + dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret); + else + *int_status = reg_val; + + dev_dbg(aw_dev->dev, "read interrupt reg=0x%04x", *int_status); +} + +static void aw_dev_clear_int_status(struct aw_device *aw_dev) +{ + u16 int_status; + + /* read int status and clear */ + aw_dev_get_int_status(aw_dev, &int_status); + /* make sure int status is clear */ + aw_dev_get_int_status(aw_dev, &int_status); + if (int_status) + dev_dbg(aw_dev->dev, "int status(%d) is not cleaned.\n", int_status); +} + +static int aw_dev_get_iis_status(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_SYSST_REG, ®_val); + if (ret) + return ret; + if ((reg_val & AW88399_BIT_PLL_CHECK) != AW88399_BIT_PLL_CHECK) { + dev_err(aw_dev->dev, "check pll lock fail, reg_val:0x%04x", reg_val); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_check_mode1_pll(struct aw_device *aw_dev) +{ + int ret, i; + + for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode1 iis signal check error"); + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + } else { + return 0; + } + } + + return -EPERM; +} + +static int aw_dev_check_mode2_pll(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret, i; + + ret = regmap_read(aw_dev->regmap, AW88399_PLLCTRL2_REG, ®_val); + if (ret) + return ret; + + reg_val &= (~AW88399_CCO_MUX_MASK); + if (reg_val == AW88399_CCO_MUX_DIVIDED_VALUE) { + dev_dbg(aw_dev->dev, "CCO_MUX is already divider"); + return -EPERM; + } + + /* change mode2 */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_PLLCTRL2_REG, + ~AW88399_CCO_MUX_MASK, AW88399_CCO_MUX_DIVIDED_VALUE); + if (ret) + return ret; + + for (i = 0; i < AW88399_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(AW88399_2000_US, AW88399_2000_US + 10); + } else { + break; + } + } + + /* change mode1 */ + regmap_update_bits(aw_dev->regmap, AW88399_PLLCTRL2_REG, + ~AW88399_CCO_MUX_MASK, AW88399_CCO_MUX_BYPASS_VALUE); + if (ret == 0) { + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error"); + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + } else { + break; + } + } + } + + return ret; +} + +int aw_dev_check_syspll(struct aw_device *aw_dev) +{ + int ret; + + ret = aw_dev_check_mode1_pll(aw_dev); + if (ret) { + dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check"); + ret = aw_dev_check_mode2_pll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 check iis failed"); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(aw_dev_check_syspll); + +static int aw_dev_check_sysst(struct aw_device *aw_dev) +{ + unsigned int check_val; + unsigned int reg_val; + int ret, i; + + ret = regmap_read(aw_dev->regmap, AW88399_PWMCTRL3_REG, ®_val); + if (ret) + return ret; + + if (reg_val & (~AW88399_NOISE_GATE_EN_MASK)) + check_val = AW88399_BIT_SYSST_NOSWS_CHECK; + else + check_val = AW88399_BIT_SYSST_SWS_CHECK; + + for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { + ret = regmap_read(aw_dev->regmap, AW88399_SYSST_REG, ®_val); + if (ret) + return ret; + + if ((reg_val & (~AW88399_BIT_SYSST_CHECK_MASK) & check_val) != check_val) { + dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x", + i, reg_val, AW88399_BIT_SYSST_NOSWS_CHECK); + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + } else { + return 0; + } + } + + return -EPERM; +} + +static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd) +{ + int ret; + + if (amppd) + ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_AMPPD_MASK, AW88399_AMPPD_POWER_DOWN_VALUE); + else + ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_AMPPD_MASK, AW88399_AMPPD_WORKING_VALUE); + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +void aw_dev_dsp_enable(struct aw_device *aw_dev, bool is_enable) +{ + int ret; + + if (is_enable) + ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_DSPBY_MASK, AW88399_DSPBY_WORKING_VALUE); + else + ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_DSPBY_MASK, AW88399_DSPBY_BYPASS_VALUE); + + if (ret) + dev_dbg(aw_dev->dev, "%s failed\n", __func__); +} +EXPORT_SYMBOL_GPL(aw_dev_dsp_enable); + +static int aw88399_dev_get_icalk(struct aw88399 *aw88399, int16_t *icalk) +{ + uint16_t icalkh_val, icalkl_val, icalk_val; + struct aw_device *aw_dev = aw88399->aw_pa; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_EFRH4_REG, ®_val); + if (ret) + return ret; + icalkh_val = reg_val & (~AW88399_EF_ISN_GESLP_H_MASK); + + ret = regmap_read(aw_dev->regmap, AW88399_EFRL4_REG, ®_val); + if (ret) + return ret; + icalkl_val = reg_val & (~AW88399_EF_ISN_GESLP_L_MASK); + + if (aw88399->check_val == AW_EF_AND_CHECK) + icalk_val = icalkh_val & icalkl_val; + else + icalk_val = icalkh_val | icalkl_val; + + if (icalk_val & (~AW88399_EF_ISN_GESLP_SIGN_MASK)) + icalk_val = icalk_val | AW88399_EF_ISN_GESLP_SIGN_NEG; + *icalk = (int16_t)icalk_val; + + return 0; +} + +static int aw88399_dev_get_vcalk(struct aw88399 *aw88399, int16_t *vcalk) +{ + uint16_t vcalkh_val, vcalkl_val, vcalk_val; + struct aw_device *aw_dev = aw88399->aw_pa; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_EFRH3_REG, ®_val); + if (ret) + return ret; + + vcalkh_val = reg_val & (~AW88399_EF_VSN_GESLP_H_MASK); + + ret = regmap_read(aw_dev->regmap, AW88399_EFRL3_REG, ®_val); + if (ret) + return ret; + + vcalkl_val = reg_val & (~AW88399_EF_VSN_GESLP_L_MASK); + + if (aw88399->check_val == AW_EF_AND_CHECK) + vcalk_val = vcalkh_val & vcalkl_val; + else + vcalk_val = vcalkh_val | vcalkl_val; + + if (vcalk_val & AW88399_EF_VSN_GESLP_SIGN_MASK) + vcalk_val = vcalk_val | AW88399_EF_VSN_GESLP_SIGN_NEG; + *vcalk = (int16_t)vcalk_val; + + return 0; +} + +static int aw88399_dev_get_internal_vcalk(struct aw88399 *aw88399, int16_t *vcalk) +{ + uint16_t vcalkh_val, vcalkl_val, vcalk_val; + struct aw_device *aw_dev = aw88399->aw_pa; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_EFRH2_REG, ®_val); + if (ret) + return ret; + vcalkh_val = reg_val & (~AW88399_INTERNAL_VSN_TRIM_H_MASK); + + ret = regmap_read(aw_dev->regmap, AW88399_EFRL2_REG, ®_val); + if (ret) + return ret; + vcalkl_val = reg_val & (~AW88399_INTERNAL_VSN_TRIM_L_MASK); + + if (aw88399->check_val == AW_EF_AND_CHECK) + vcalk_val = (vcalkh_val >> AW88399_INTERNAL_VSN_TRIM_H_START_BIT) & + (vcalkl_val >> AW88399_INTERNAL_VSN_TRIM_L_START_BIT); + else + vcalk_val = (vcalkh_val >> AW88399_INTERNAL_VSN_TRIM_H_START_BIT) | + (vcalkl_val >> AW88399_INTERNAL_VSN_TRIM_L_START_BIT); + + if (vcalk_val & (~AW88399_TEM4_SIGN_MASK)) + vcalk_val = vcalk_val | AW88399_TEM4_SIGN_NEG; + + *vcalk = (int16_t)vcalk_val; + + return 0; +} + +static int aw_dev_set_vcalb(struct aw88399 *aw88399) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + unsigned int vsense_select, vsense_value; + int32_t ical_k, vcal_k, vcalb; + int16_t icalk, vcalk; + uint16_t reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_VSNCTRL1_REG, &vsense_value); + if (ret) + return ret; + + vsense_select = vsense_value & (~AW88399_VDSEL_MASK); + + ret = aw88399_dev_get_icalk(aw88399, &icalk); + if (ret) { + dev_err(aw_dev->dev, "get icalk failed\n"); + return ret; + } + + ical_k = icalk * AW88399_ICABLK_FACTOR + AW88399_CABL_BASE_VALUE; + + switch (vsense_select) { + case AW88399_DEV_VDSEL_VSENSE: + ret = aw88399_dev_get_vcalk(aw88399, &vcalk); + vcal_k = vcalk * AW88399_VCABLK_FACTOR + AW88399_CABL_BASE_VALUE; + vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_FACTOR / AW88399_ISCAL_FACTOR * + ical_k / vcal_k * aw88399->vcalb_init_val; + break; + case AW88399_DEV_VDSEL_DAC: + ret = aw88399_dev_get_internal_vcalk(aw88399, &vcalk); + vcal_k = vcalk * AW88399_VCABLK_DAC_FACTOR + AW88399_CABL_BASE_VALUE; + vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_DAC_FACTOR / + AW88399_ISCAL_DAC_FACTOR * ical_k / + vcal_k * aw88399->vcalb_init_val; + break; + default: + dev_err(aw_dev->dev, "%s: unsupported vsense\n", __func__); + ret = -EINVAL; + break; + } + if (ret) + return ret; + + vcalb = vcalb >> AW88399_VCALB_ADJ_FACTOR; + reg_val = (uint32_t)vcalb; + + regmap_write(aw_dev->regmap, AW88399_DSPVCALB_REG, reg_val); + + return 0; +} + +int aw_dev_update_cali_re(struct aw_cali_desc *cali_desc) +{ + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + uint16_t re_lbits, re_hbits; + u32 cali_re; + int ret; + + if ((aw_dev->cali_desc.cali_re >= AW88399_CALI_RE_MAX) || + (aw_dev->cali_desc.cali_re <= AW88399_CALI_RE_MIN)) + return -EINVAL; + + cali_re = AW88399_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re + + aw_dev->cali_desc.ra), AW88399_DSP_RE_SHIFT); + + re_hbits = (cali_re & (~AW88399_CALI_RE_HBITS_MASK)) >> AW88399_CALI_RE_HBITS_SHIFT; + re_lbits = (cali_re & (~AW88399_CALI_RE_LBITS_MASK)) >> AW88399_CALI_RE_LBITS_SHIFT; + + ret = regmap_write(aw_dev->regmap, AW88399_ACR1_REG, re_hbits); + if (ret) { + dev_err(aw_dev->dev, "set cali re error"); + return ret; + } + + ret = regmap_write(aw_dev->regmap, AW88399_ACR2_REG, re_lbits); + if (ret) + dev_err(aw_dev->dev, "set cali re error"); + + return ret; +} +EXPORT_SYMBOL_GPL(aw_dev_update_cali_re); + +static int aw_dev_fw_crc_check(struct aw_device *aw_dev) +{ + uint16_t check_val, fw_len_val; + unsigned int reg_val; + int ret; + + /* calculate fw_end_addr */ + fw_len_val = ((aw_dev->dsp_fw_len / AW_FW_ADDR_LEN) - 1) + AW88399_CRC_FW_BASE_ADDR; + + /* write fw_end_addr to crc_end_addr */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, + ~AW88399_CRC_END_ADDR_MASK, fw_len_val); + if (ret) + return ret; + /* enable fw crc check */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, + ~AW88399_CRC_CODE_EN_MASK, AW88399_CRC_CODE_EN_ENABLE_VALUE); + + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + + /* read crc check result */ + regmap_read(aw_dev->regmap, AW88399_HAGCST_REG, ®_val); + if (ret) + return ret; + + check_val = (reg_val & (~AW88399_CRC_CHECK_BITS_MASK)) >> AW88399_CRC_CHECK_START_BIT; + + /* disable fw crc check */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, + ~AW88399_CRC_CODE_EN_MASK, AW88399_CRC_CODE_EN_DISABLE_VALUE); + if (ret) + return ret; + + if (check_val != AW88399_CRC_CHECK_PASS_VAL) { + dev_err(aw_dev->dev, "%s failed, check_val 0x%x != 0x%x", + __func__, check_val, AW88399_CRC_CHECK_PASS_VAL); + ret = -EINVAL; + } + + return ret; +} + +static int aw_dev_cfg_crc_check(struct aw_device *aw_dev) +{ + uint16_t check_val, cfg_len_val; + unsigned int reg_val; + int ret; + + /* calculate cfg end addr */ + cfg_len_val = ((aw_dev->dsp_cfg_len / AW_FW_ADDR_LEN) - 1) + AW88399_CRC_CFG_BASE_ADDR; + + /* write cfg_end_addr to crc_end_addr */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, + ~AW88399_CRC_END_ADDR_MASK, cfg_len_val); + if (ret) + return ret; + + /* enable cfg crc check */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, + ~AW88399_CRC_CFG_EN_MASK, AW88399_CRC_CFG_EN_ENABLE_VALUE); + if (ret) + return ret; + + usleep_range(AW88399_1000_US, AW88399_1000_US + 10); + + /* read crc check result */ + ret = regmap_read(aw_dev->regmap, AW88399_HAGCST_REG, ®_val); + if (ret) + return ret; + + check_val = (reg_val & (~AW88399_CRC_CHECK_BITS_MASK)) >> AW88399_CRC_CHECK_START_BIT; + + /* disable cfg crc check */ + ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, + ~AW88399_CRC_CFG_EN_MASK, AW88399_CRC_CFG_EN_DISABLE_VALUE); + if (ret) + return ret; + + if (check_val != AW88399_CRC_CHECK_PASS_VAL) { + dev_err(aw_dev->dev, "crc_check failed, check val 0x%x != 0x%x", + check_val, AW88399_CRC_CHECK_PASS_VAL); + ret = -EINVAL; + } + + return ret; +} + +static int aw_dev_hw_crc_check(struct aw88399 *aw88399) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + int ret; + + ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, + ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_BYPASS_VALUE); + if (ret) + return ret; + + ret = aw_dev_fw_crc_check(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "fw_crc_check failed\n"); + goto crc_check_failed; + } + + ret = aw_dev_cfg_crc_check(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "cfg_crc_check failed\n"); + goto crc_check_failed; + } + + ret = regmap_write(aw_dev->regmap, AW88399_CRCCTRL_REG, aw88399->crc_init_val); + if (ret) + return ret; + + ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, + ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_WORK_VALUE); + + return ret; + +crc_check_failed: + regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, + ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_WORK_VALUE); + return ret; +} + +static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag) +{ + int ret; + + if (flag) + ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCTRL3_REG, + ~AW88399_I2STXEN_MASK, AW88399_I2STXEN_ENABLE_VALUE); + else + ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, + ~AW88399_I2STXEN_MASK, AW88399_I2STXEN_DISABLE_VALUE); + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +int aw_dev_get_dsp_status(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_WDT_REG, ®_val); + if (ret) + return ret; + if (!(reg_val & (~AW88399_WDT_CNT_MASK))) + return -EPERM; + + return 0; +} +EXPORT_SYMBOL_GPL(aw_dev_get_dsp_status); + +static int aw_dev_dsp_check(struct aw_device *aw_dev) +{ + int ret, i; + + switch (aw_dev->dsp_cfg) { + case AW88399_DEV_DSP_BYPASS: + dev_dbg(aw_dev->dev, "dsp bypass"); + ret = 0; + break; + case AW88399_DEV_DSP_WORK: + aw_dev_dsp_enable(aw_dev, false); + aw_dev_dsp_enable(aw_dev, true); + usleep_range(AW88399_1000_US, AW88399_1000_US + 10); + for (i = 0; i < AW88399_DEV_DSP_CHECK_MAX; i++) { + ret = aw_dev_get_dsp_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp wdt status error=%d", ret); + usleep_range(AW88399_2000_US, AW88399_2000_US + 10); + } + } + break; + default: + dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg); + ret = -EINVAL; + break; + } + + return ret; +} + +int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + unsigned int reg_value; + u16 real_value; + int ret; + + real_value = min((value + vol_desc->init_volume), (unsigned int)AW88399_MUTE_VOL); + + ret = regmap_read(aw_dev->regmap, AW88399_SYSCTRL2_REG, ®_value); + if (ret) + return ret; + + dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value); + + real_value = (real_value << AW88399_VOL_START_BIT) | (reg_value & AW88399_VOL_MASK); + + ret = regmap_write(aw_dev->regmap, AW88399_SYSCTRL2_REG, real_value); + + return ret; +} +EXPORT_SYMBOL_GPL(aw_dev_set_volume); + +static void aw_dev_fade_in(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + u16 fade_in_vol = desc->ctl_volume; + int fade_step = aw_dev->fade_step; + int i; + + if (fade_step == 0 || aw_dev->fade_in_time == 0) { + aw_dev_set_volume(aw_dev, fade_in_vol); + return; + } + + for (i = AW88399_MUTE_VOL; i >= fade_in_vol; i -= fade_step) { + aw_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10); + } + + if (i != fade_in_vol) + aw_dev_set_volume(aw_dev, fade_in_vol); +} + +static void aw_dev_fade_out(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + int fade_step = aw_dev->fade_step; + int i; + + if (fade_step == 0 || aw_dev->fade_out_time == 0) { + aw_dev_set_volume(aw_dev, AW88399_MUTE_VOL); + return; + } + + for (i = desc->ctl_volume; i <= AW88399_MUTE_VOL; i += fade_step) { + aw_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } + + if (i != AW88399_MUTE_VOL) { + aw_dev_set_volume(aw_dev, AW88399_MUTE_VOL); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } +} + +void aw88399_dev_mute(struct aw_device *aw_dev, bool is_mute) +{ + if (is_mute) { + aw_dev_fade_out(aw_dev); + regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_HMUTE_MASK, AW88399_HMUTE_ENABLE_VALUE); + } else { + regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, + ~AW88399_HMUTE_MASK, AW88399_HMUTE_DISABLE_VALUE); + aw_dev_fade_in(aw_dev); + } +} +EXPORT_SYMBOL_GPL(aw88399_dev_mute); + +static void aw88399_dev_set_dither(struct aw88399 *aw88399, bool dither) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + + if (dither) + regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, + ~AW88399_DITHER_EN_MASK, AW88399_DITHER_EN_ENABLE_VALUE); + else + regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, + ~AW88399_DITHER_EN_MASK, AW88399_DITHER_EN_DISABLE_VALUE); +} + +static int aw88399_dev_start(struct aw88399 *aw88399) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + int ret; + + if (aw_dev->status == AW88399_DEV_PW_ON) { + dev_dbg(aw_dev->dev, "already power on"); + return 0; + } + + aw88399_dev_set_dither(aw88399, false); + + /* power on */ + aw_dev_pwd(aw_dev, false); + usleep_range(AW88399_2000_US, AW88399_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(AW88399_1000_US, AW88399_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; + } + + if (aw_dev->dsp_cfg == AW88399_DEV_DSP_WORK) { + ret = aw_dev_hw_crc_check(aw88399); + if (ret) { + dev_err(aw_dev->dev, "dsp crc check failed"); + goto crc_check_fail; + } + aw_dev_dsp_enable(aw_dev, false); + aw_dev_set_vcalb(aw88399); + aw_dev_update_cali_re(&aw_dev->cali_desc); + + ret = aw_dev_dsp_check(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp status check failed"); + goto dsp_check_fail; + } + } else { + dev_dbg(aw_dev->dev, "start pa with dsp bypass"); + } + + /* enable tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, true); + + if (aw88399->dither_st == AW88399_DITHER_EN_ENABLE_VALUE) + aw88399_dev_set_dither(aw88399, true); + + /* close mute */ + aw88399_dev_mute(aw_dev, false); + /* clear inturrupt */ + aw_dev_clear_int_status(aw_dev); + aw_dev->status = AW88399_DEV_PW_ON; + + return 0; + +dsp_check_fail: +crc_check_fail: + aw_dev_dsp_enable(aw_dev, false); +sysst_check_fail: + aw_dev_clear_int_status(aw_dev); + aw_dev_amppd(aw_dev, true); +pll_check_fail: + aw_dev_pwd(aw_dev, true); + aw_dev->status = AW88399_DEV_PW_OFF; + + return ret; +} + +static int aw_dev_dsp_update_container(struct aw_device *aw_dev, + unsigned char *data, unsigned int len, unsigned short base) +{ + u32 tmp_len; + int i, ret; + + ret = regmap_write(aw_dev->regmap, AW88399_DSPMADD_REG, base); + if (ret) + return ret; + + for (i = 0; i < len; i += AW88399_MAX_RAM_WRITE_BYTE_SIZE) { + tmp_len = min(len - i, AW88399_MAX_RAM_WRITE_BYTE_SIZE); + ret = regmap_raw_write(aw_dev->regmap, AW88399_DSPMDAT_REG, + &data[i], tmp_len); + if (ret) + return ret; + } + + return 0; +} + +static int aw_dev_get_ra(struct aw_cali_desc *cali_desc) +{ + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + u32 dsp_ra; + int ret; + + ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_ADPZ_RA, + &dsp_ra, AW_DSP_32_DATA); + if (ret) { + dev_err(aw_dev->dev, "read ra error"); + return ret; + } + + cali_desc->ra = AW88399_DSP_RE_TO_SHOW_RE(dsp_ra, + AW88399_DSP_RE_SHIFT); + + return 0; +} + +static int aw_dev_dsp_update_cfg(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int ret; + + dev_dbg(aw_dev->dev, "dsp config len:%d", len); + + if (!len || !data) { + dev_err(aw_dev->dev, "dsp config data is null or len is 0"); + return -EINVAL; + } + + ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88399_DSP_CFG_ADDR); + if (ret) + return ret; + + aw_dev->dsp_cfg_len = len; + + ret = aw_dev_get_ra(&aw_dev->cali_desc); + + return ret; +} + +static int aw_dev_dsp_update_fw(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int ret; + + dev_dbg(aw_dev->dev, "dsp firmware len:%d", len); + + if (!len || !data) { + dev_err(aw_dev->dev, "dsp firmware data is null or len is 0"); + return -EINVAL; + } + + aw_dev->dsp_fw_len = len; + ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88399_DSP_FW_ADDR); + + return ret; +} + +static int aw_dev_check_sram(struct aw_device *aw_dev) +{ + unsigned int reg_val; + + /* read dsp_rom_check_reg */ + aw_dev_dsp_read(aw_dev, AW88399_DSP_ROM_CHECK_ADDR, ®_val, AW_DSP_16_DATA); + if (reg_val != AW88399_DSP_ROM_CHECK_DATA) { + dev_err(aw_dev->dev, "check dsp rom failed, read[0x%x] != check[0x%x]", + reg_val, AW88399_DSP_ROM_CHECK_DATA); + return -EPERM; + } + + /* check dsp_cfg_base_addr */ + aw_dev_dsp_write(aw_dev, AW88399_DSP_CFG_ADDR, + AW88399_DSP_ODD_NUM_BIT_TEST, AW_DSP_16_DATA); + aw_dev_dsp_read(aw_dev, AW88399_DSP_CFG_ADDR, ®_val, AW_DSP_16_DATA); + if (reg_val != AW88399_DSP_ODD_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]", + reg_val, AW88399_DSP_ODD_NUM_BIT_TEST); + return -EPERM; + } + + return 0; +} + +static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char flag) +{ + int ret; + + switch (flag) { + case AW88399_DEV_MEMCLK_PLL: + ret = regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, + ~AW88399_MEM_CLKSEL_MASK, + AW88399_MEM_CLKSEL_DAPHCLK_VALUE); + if (ret) + dev_err(aw_dev->dev, "memclk select pll failed"); + break; + case AW88399_DEV_MEMCLK_OSC: + ret = regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, + ~AW88399_MEM_CLKSEL_MASK, + AW88399_MEM_CLKSEL_OSCCLK_VALUE); + if (ret) + dev_err(aw_dev->dev, "memclk select OSC failed"); + break; + default: + dev_err(aw_dev->dev, "unknown memclk config, flag=0x%x", flag); + break; + } +} + +static void aw_dev_get_cur_mode_st(struct aw_device *aw_dev) +{ + struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88399_SYSCTRL_REG, ®_val); + if (ret) { + dev_dbg(aw_dev->dev, "%s failed", __func__); + return; + } + if ((reg_val & (~AW88399_RCV_MODE_MASK)) == AW88399_RCV_MODE_RECEIVER_VALUE) + profctrl_desc->cur_mode = AW88399_RCV_MODE; + else + profctrl_desc->cur_mode = AW88399_NOT_RCV_MODE; +} + +static int aw_dev_update_reg_container(struct aw88399 *aw88399, + unsigned char *data, unsigned int len) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + u16 read_vol, reg_val; + int data_len, i, ret; + int16_t *reg_data; + u8 reg_addr; + + 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 == AW88399_DSPVCALB_REG) { + aw88399->vcalb_init_val = reg_val; + continue; + } + + if (reg_addr == AW88399_SYSCTRL_REG) { + if (reg_val & (~AW88399_DSPBY_MASK)) + aw_dev->dsp_cfg = AW88399_DEV_DSP_BYPASS; + else + aw_dev->dsp_cfg = AW88399_DEV_DSP_WORK; + + reg_val &= (AW88399_HMUTE_MASK | AW88399_PWDN_MASK | + AW88399_DSPBY_MASK); + reg_val |= (AW88399_HMUTE_ENABLE_VALUE | AW88399_PWDN_POWER_DOWN_VALUE | + AW88399_DSPBY_BYPASS_VALUE); + } + + if (reg_addr == AW88399_I2SCTRL3_REG) { + reg_val &= AW88399_I2STXEN_MASK; + reg_val |= AW88399_I2STXEN_DISABLE_VALUE; + } + + if (reg_addr == AW88399_SYSCTRL2_REG) { + read_vol = (reg_val & (~AW88399_VOL_MASK)) >> + AW88399_VOL_START_BIT; + aw_dev->volume_desc.init_volume = read_vol; + } + + if (reg_addr == AW88399_DBGCTRL_REG) { + if ((reg_val & (~AW88399_EF_DBMD_MASK)) == AW88399_EF_DBMD_OR_VALUE) + aw88399->check_val = AW_EF_OR_CHECK; + else + aw88399->check_val = AW_EF_AND_CHECK; + + aw88399->dither_st = reg_val & (~AW88399_DITHER_EN_MASK); + } + + if (reg_addr == AW88399_CRCCTRL_REG) + aw88399->crc_init_val = reg_val; + + ret = regmap_write(aw_dev->regmap, reg_addr, reg_val); + if (ret) + return ret; + } + + aw_dev_pwd(aw_dev, false); + usleep_range(AW88399_1000_US, AW88399_1000_US + 10); + + aw_dev_get_cur_mode_st(aw_dev); + + if (aw_dev->prof_cur != aw_dev->prof_index) + vol_desc->ctl_volume = 0; + else + aw_dev_set_volume(aw_dev, vol_desc->ctl_volume); + + return 0; +} + +static int aw_dev_reg_update(struct aw88399 *aw88399, + unsigned char *data, unsigned int len) +{ + int ret; + + if (!len || !data) { + dev_err(aw88399->aw_pa->dev, "reg data is null or len is 0"); + return -EINVAL; + } + + ret = aw_dev_update_reg_container(aw88399, data, len); + if (ret) + dev_err(aw88399->aw_pa->dev, "reg update failed"); + + return ret; +} + +int aw88399_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name) +{ + struct aw_prof_info *prof_info = &aw_dev->prof_info; + struct aw_prof_desc *prof_desc; + + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "index[%d] overflow count[%d]", + index, aw_dev->prof_info.count); + return -EINVAL; + } + + prof_desc = &aw_dev->prof_info.prof_desc[index]; + + *prof_name = prof_info->prof_name_list[prof_desc->id]; + + return 0; +} +EXPORT_SYMBOL_GPL(aw88399_dev_get_prof_name); + +static int aw88399_dev_get_prof_data(struct aw_device *aw_dev, int index, + struct aw_prof_desc **prof_desc) +{ + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n", + __func__, index, aw_dev->prof_info.count); + return -EINVAL; + } + + *prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return 0; +} + +static int aw88399_dev_fw_update(struct aw88399 *aw88399, bool up_dsp_fw_en, bool force_up_en) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + struct aw_prof_desc *prof_index_desc; + struct aw_sec_data_desc *sec_desc; + char *prof_name; + int ret; + + if ((aw_dev->prof_cur == aw_dev->prof_index) && + (force_up_en == AW88399_FORCE_UPDATE_OFF)) { + dev_dbg(aw_dev->dev, "scene no change, not update"); + return 0; + } + + if (aw_dev->fw_status == AW88399_DEV_FW_FAILED) { + dev_err(aw_dev->dev, "fw status[%d] error", aw_dev->fw_status); + return -EPERM; + } + + ret = aw88399_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name); + if (ret) + return ret; + + dev_dbg(aw_dev->dev, "start update %s", prof_name); + + ret = aw88399_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc); + if (ret) + return ret; + + /* update reg */ + sec_desc = prof_index_desc->sec_desc; + ret = aw_dev_reg_update(aw88399, 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; + } + + aw88399_dev_mute(aw_dev, true); + + if (aw_dev->dsp_cfg == AW88399_DEV_DSP_WORK) + aw_dev_dsp_enable(aw_dev, false); + + aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_OSC); + + ret = aw_dev_check_sram(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "check sram failed"); + goto error; + } + + if (up_dsp_fw_en) { + dev_dbg(aw_dev->dev, "fw_ver: [%x]", prof_index_desc->fw_ver); + ret = aw_dev_dsp_update_fw(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_FW].data, + sec_desc[AW88395_DATA_TYPE_DSP_FW].len); + if (ret) { + dev_err(aw_dev->dev, "update dsp fw failed"); + goto error; + } + } + + /* update dsp config */ + ret = aw_dev_dsp_update_cfg(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_CFG].data, + sec_desc[AW88395_DATA_TYPE_DSP_CFG].len); + if (ret) { + dev_err(aw_dev->dev, "update dsp cfg failed"); + goto error; + } + + aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL); + + aw_dev->prof_cur = aw_dev->prof_index; + + return 0; + +error: + aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL); + return ret; +} + +static void aw88399_start_pa(struct aw88399 *aw88399) +{ + int ret, i; + + for (i = 0; i < AW88399_START_RETRIES; i++) { + ret = aw88399_dev_start(aw88399); + if (ret) { + dev_err(aw88399->aw_pa->dev, "aw88399 device start failed. retry = %d", i); + ret = aw88399_dev_fw_update(aw88399, AW88399_DSP_FW_UPDATE_ON, true); + if (ret) { + dev_err(aw88399->aw_pa->dev, "fw update failed"); + continue; + } + } else { + dev_dbg(aw88399->aw_pa->dev, "start success\n"); + break; + } + } +} + +void aw88399_startup_work(struct work_struct *work) +{ + struct aw88399 *aw88399 = + container_of(work, struct aw88399, start_work.work); + + mutex_lock(&aw88399->lock); + aw88399_start_pa(aw88399); + mutex_unlock(&aw88399->lock); +} +EXPORT_SYMBOL_GPL(aw88399_startup_work); + +void aw88399_start(struct aw88399 *aw88399, bool sync_start) +{ + int ret; + + if (aw88399->aw_pa->fw_status != AW88399_DEV_FW_OK) + return; + + if (aw88399->aw_pa->status == AW88399_DEV_PW_ON) + return; + + ret = aw88399_dev_fw_update(aw88399, AW88399_DSP_FW_UPDATE_OFF, true); + if (ret) { + dev_err(aw88399->aw_pa->dev, "fw update failed."); + return; + } + + if (sync_start == AW88399_SYNC_START) + aw88399_start_pa(aw88399); + else + queue_delayed_work(system_dfl_wq, + &aw88399->start_work, + AW88399_START_WORK_DELAY_MS); +} +EXPORT_SYMBOL_GPL(aw88399_start); + +static int aw_dev_check_sysint(struct aw_device *aw_dev) +{ + u16 reg_val; + + aw_dev_get_int_status(aw_dev, ®_val); + if (reg_val & AW88399_BIT_SYSINT_CHECK) { + dev_err(aw_dev->dev, "pa stop check fail:0x%04x", reg_val); + return -EINVAL; + } + + return 0; +} + +int aw88399_stop(struct aw_device *aw_dev) +{ + struct aw_sec_data_desc *dsp_cfg = + &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_CFG]; + struct aw_sec_data_desc *dsp_fw = + &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_FW]; + int int_st; + + if (aw_dev->status == AW88399_DEV_PW_OFF) { + dev_dbg(aw_dev->dev, "already power off"); + return 0; + } + + aw_dev->status = AW88399_DEV_PW_OFF; + + aw88399_dev_mute(aw_dev, true); + usleep_range(AW88399_4000_US, AW88399_4000_US + 100); + + aw_dev_i2s_tx_enable(aw_dev, false); + usleep_range(AW88399_1000_US, AW88399_1000_US + 100); + + int_st = aw_dev_check_sysint(aw_dev); + + aw_dev_dsp_enable(aw_dev, false); + + aw_dev_amppd(aw_dev, true); + + if (int_st) { + aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_OSC); + aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len); + aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len); + aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL); + } + + aw_dev_pwd(aw_dev, true); + + return 0; +} +EXPORT_SYMBOL_GPL(aw88399_stop); + +static int aw88399_dev_init(struct aw88399 *aw88399, struct aw_container *aw_cfg) +{ + struct aw_device *aw_dev = aw88399->aw_pa; + int ret; + + ret = aw88395_dev_cfg_load(aw_dev, aw_cfg); + if (ret) { + dev_err(aw_dev->dev, "aw_dev acf parse failed"); + return -EINVAL; + } + aw_dev->fade_in_time = AW88399_1000_US / 10; + aw_dev->fade_out_time = AW88399_1000_US >> 1; + aw_dev->prof_cur = aw_dev->prof_info.prof_desc[0].id; + aw_dev->prof_index = aw_dev->prof_info.prof_desc[0].id; + + ret = aw88399_dev_fw_update(aw88399, AW88399_FORCE_UPDATE_ON, AW88399_DSP_FW_UPDATE_ON); + if (ret) { + dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret); + return ret; + } + + aw88399_dev_mute(aw_dev, true); + + /* close tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, false); + usleep_range(AW88399_1000_US, AW88399_1000_US + 100); + + /* enable amppd */ + aw_dev_amppd(aw_dev, true); + + /* close dsp */ + aw_dev_dsp_enable(aw_dev, false); + /* set power down */ + aw_dev_pwd(aw_dev, true); + + return 0; +} + +int aw88399_request_firmware_file(struct aw88399 *aw88399) +{ + const struct firmware *cont = NULL; + int ret; + + aw88399->aw_pa->fw_status = AW88399_DEV_FW_FAILED; + + ret = request_firmware(&cont, AW88399_ACF_FILE, aw88399->aw_pa->dev); + if (ret) { + dev_err(aw88399->aw_pa->dev, "request [%s] failed!", AW88399_ACF_FILE); + return ret; + } + + dev_dbg(aw88399->aw_pa->dev, "loaded %s - size: %zu\n", + AW88399_ACF_FILE, cont ? cont->size : 0); + + aw88399->aw_cfg = devm_kzalloc(aw88399->aw_pa->dev, + struct_size(aw88399->aw_cfg, data, cont->size), GFP_KERNEL); + if (!aw88399->aw_cfg) { + release_firmware(cont); + return -ENOMEM; + } + aw88399->aw_cfg->len = (int)cont->size; + memcpy(aw88399->aw_cfg->data, cont->data, cont->size); + release_firmware(cont); + + ret = aw88395_dev_load_acf_check(aw88399->aw_pa, aw88399->aw_cfg); + if (ret) { + dev_err(aw88399->aw_pa->dev, "load [%s] failed!", AW88399_ACF_FILE); + return ret; + } + + mutex_lock(&aw88399->lock); + /* aw device init */ + ret = aw88399_dev_init(aw88399, aw88399->aw_cfg); + if (ret) + dev_err(aw88399->aw_pa->dev, "dev init failed"); + mutex_unlock(&aw88399->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(aw88399_request_firmware_file); + +void aw88399_hw_reset(struct aw88399 *aw88399) +{ + if (aw88399->reset_gpio) { + gpiod_set_value_cansleep(aw88399->reset_gpio, 1); + usleep_range(AW88399_1000_US, AW88399_1000_US + 10); + gpiod_set_value_cansleep(aw88399->reset_gpio, 0); + usleep_range(AW88399_1000_US, AW88399_1000_US + 10); + gpiod_set_value_cansleep(aw88399->reset_gpio, 1); + usleep_range(AW88399_1000_US, AW88399_1000_US + 10); + } +} +EXPORT_SYMBOL_GPL(aw88399_hw_reset); + +static void aw88399_parse_channel_dt(struct aw_device *aw_dev) +{ + struct device_node *np = aw_dev->dev->of_node; + u32 channel_value; + + of_property_read_u32(np, "awinic,audio-channel", &channel_value); + aw_dev->channel = channel_value; +} + +int aw88399_init(struct aw88399 *aw88399, struct i2c_client *i2c, struct regmap *regmap) +{ + struct aw_device *aw_dev; + unsigned int chip_id; + int ret; + + ret = regmap_read(regmap, AW88399_ID_REG, &chip_id); + if (ret) { + dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret); + return ret; + } + if (chip_id != AW88399_CHIP_ID) { + dev_err(&i2c->dev, "unsupported device"); + return -ENXIO; + } + dev_dbg(&i2c->dev, "chip id = %x\n", chip_id); + + aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL); + if (!aw_dev) + return -ENOMEM; + aw88399->aw_pa = aw_dev; + + aw_dev->i2c = i2c; + aw_dev->dev = &i2c->dev; + aw_dev->regmap = regmap; + mutex_init(&aw_dev->dsp_lock); + + aw_dev->chip_id = chip_id; + aw_dev->acf = NULL; + aw_dev->prof_info.prof_desc = NULL; + aw_dev->prof_info.count = 0; + aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID; + aw_dev->channel = AW88399_DEV_DEFAULT_CH; + aw_dev->fw_status = AW88399_DEV_FW_FAILED; + + aw_dev->fade_step = AW88399_VOLUME_STEP_DB; + aw_dev->volume_desc.ctl_volume = AW88399_VOL_DEFAULT_VALUE; + + aw88399_parse_channel_dt(aw_dev); + + return 0; +} +EXPORT_SYMBOL_GPL(aw88399_init); + +MODULE_DESCRIPTION("AW88399 common device library"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c index b2ec3503f7e2..13495e71ad0e 100644 --- a/sound/soc/codecs/aw88399.c +++ b/sound/soc/codecs/aw88399.c @@ -10,1217 +10,12 @@ #include #include #include -#include -#include #include #include #include #include "aw88399.h" #include "aw88395/aw88395_device.h" -static const struct regmap_config aw88399_remap_config = { - .val_bits = 16, - .reg_bits = 8, - .max_register = AW88399_REG_MAX, - .reg_format_endian = REGMAP_ENDIAN_LITTLE, - .val_format_endian = REGMAP_ENDIAN_BIG, -}; - -static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd) -{ - int ret; - - if (pwd) - ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_PWDN_MASK, AW88399_PWDN_POWER_DOWN_VALUE); - else - ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_PWDN_MASK, AW88399_PWDN_WORKING_VALUE); - - if (ret) - dev_dbg(aw_dev->dev, "%s failed", __func__); -} - -static void aw_dev_get_int_status(struct aw_device *aw_dev, unsigned short *int_status) -{ - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_SYSINT_REG, ®_val); - if (ret) - dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret); - else - *int_status = reg_val; - - dev_dbg(aw_dev->dev, "read interrupt reg=0x%04x", *int_status); -} - -static void aw_dev_clear_int_status(struct aw_device *aw_dev) -{ - u16 int_status; - - /* read int status and clear */ - aw_dev_get_int_status(aw_dev, &int_status); - /* make sure int status is clear */ - aw_dev_get_int_status(aw_dev, &int_status); - if (int_status) - dev_dbg(aw_dev->dev, "int status(%d) is not cleaned.\n", int_status); -} - -static int aw_dev_get_iis_status(struct aw_device *aw_dev) -{ - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_SYSST_REG, ®_val); - if (ret) - return ret; - if ((reg_val & AW88399_BIT_PLL_CHECK) != AW88399_BIT_PLL_CHECK) { - dev_err(aw_dev->dev, "check pll lock fail, reg_val:0x%04x", reg_val); - return -EINVAL; - } - - return 0; -} - -static int aw_dev_check_mode1_pll(struct aw_device *aw_dev) -{ - int ret, i; - - for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { - ret = aw_dev_get_iis_status(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "mode1 iis signal check error"); - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - } else { - return 0; - } - } - - return -EPERM; -} - -static int aw_dev_check_mode2_pll(struct aw_device *aw_dev) -{ - unsigned int reg_val; - int ret, i; - - ret = regmap_read(aw_dev->regmap, AW88399_PLLCTRL2_REG, ®_val); - if (ret) - return ret; - - reg_val &= (~AW88399_CCO_MUX_MASK); - if (reg_val == AW88399_CCO_MUX_DIVIDED_VALUE) { - dev_dbg(aw_dev->dev, "CCO_MUX is already divider"); - return -EPERM; - } - - /* change mode2 */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_PLLCTRL2_REG, - ~AW88399_CCO_MUX_MASK, AW88399_CCO_MUX_DIVIDED_VALUE); - if (ret) - return ret; - - for (i = 0; i < AW88399_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(AW88399_2000_US, AW88399_2000_US + 10); - } else { - break; - } - } - - /* change mode1 */ - regmap_update_bits(aw_dev->regmap, AW88399_PLLCTRL2_REG, - ~AW88399_CCO_MUX_MASK, AW88399_CCO_MUX_BYPASS_VALUE); - if (ret == 0) { - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { - ret = aw_dev_get_iis_status(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error"); - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - } else { - break; - } - } - } - - return ret; -} - -static int aw_dev_check_syspll(struct aw_device *aw_dev) -{ - int ret; - - ret = aw_dev_check_mode1_pll(aw_dev); - if (ret) { - dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check"); - ret = aw_dev_check_mode2_pll(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "mode2 check iis failed"); - return ret; - } - } - - return 0; -} - -static int aw_dev_check_sysst(struct aw_device *aw_dev) -{ - unsigned int check_val; - unsigned int reg_val; - int ret, i; - - ret = regmap_read(aw_dev->regmap, AW88399_PWMCTRL3_REG, ®_val); - if (ret) - return ret; - - if (reg_val & (~AW88399_NOISE_GATE_EN_MASK)) - check_val = AW88399_BIT_SYSST_NOSWS_CHECK; - else - check_val = AW88399_BIT_SYSST_SWS_CHECK; - - for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { - ret = regmap_read(aw_dev->regmap, AW88399_SYSST_REG, ®_val); - if (ret) - return ret; - - if ((reg_val & (~AW88399_BIT_SYSST_CHECK_MASK) & check_val) != check_val) { - dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x", - i, reg_val, AW88399_BIT_SYSST_NOSWS_CHECK); - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - } else { - return 0; - } - } - - return -EPERM; -} - -static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd) -{ - int ret; - - if (amppd) - ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_AMPPD_MASK, AW88399_AMPPD_POWER_DOWN_VALUE); - else - ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_AMPPD_MASK, AW88399_AMPPD_WORKING_VALUE); - - if (ret) - dev_dbg(aw_dev->dev, "%s failed", __func__); -} - -static void aw_dev_dsp_enable(struct aw_device *aw_dev, bool is_enable) -{ - int ret; - - if (is_enable) - ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_DSPBY_MASK, AW88399_DSPBY_WORKING_VALUE); - else - ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_DSPBY_MASK, AW88399_DSPBY_BYPASS_VALUE); - - if (ret) - dev_dbg(aw_dev->dev, "%s failed\n", __func__); -} - -static int aw88399_dev_get_icalk(struct aw88399 *aw88399, int16_t *icalk) -{ - uint16_t icalkh_val, icalkl_val, icalk_val; - struct aw_device *aw_dev = aw88399->aw_pa; - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_EFRH4_REG, ®_val); - if (ret) - return ret; - icalkh_val = reg_val & (~AW88399_EF_ISN_GESLP_H_MASK); - - ret = regmap_read(aw_dev->regmap, AW88399_EFRL4_REG, ®_val); - if (ret) - return ret; - icalkl_val = reg_val & (~AW88399_EF_ISN_GESLP_L_MASK); - - if (aw88399->check_val == AW_EF_AND_CHECK) - icalk_val = icalkh_val & icalkl_val; - else - icalk_val = icalkh_val | icalkl_val; - - if (icalk_val & (~AW88399_EF_ISN_GESLP_SIGN_MASK)) - icalk_val = icalk_val | AW88399_EF_ISN_GESLP_SIGN_NEG; - *icalk = (int16_t)icalk_val; - - return 0; -} - -static int aw88399_dev_get_vcalk(struct aw88399 *aw88399, int16_t *vcalk) -{ - uint16_t vcalkh_val, vcalkl_val, vcalk_val; - struct aw_device *aw_dev = aw88399->aw_pa; - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_EFRH3_REG, ®_val); - if (ret) - return ret; - - vcalkh_val = reg_val & (~AW88399_EF_VSN_GESLP_H_MASK); - - ret = regmap_read(aw_dev->regmap, AW88399_EFRL3_REG, ®_val); - if (ret) - return ret; - - vcalkl_val = reg_val & (~AW88399_EF_VSN_GESLP_L_MASK); - - if (aw88399->check_val == AW_EF_AND_CHECK) - vcalk_val = vcalkh_val & vcalkl_val; - else - vcalk_val = vcalkh_val | vcalkl_val; - - if (vcalk_val & AW88399_EF_VSN_GESLP_SIGN_MASK) - vcalk_val = vcalk_val | AW88399_EF_VSN_GESLP_SIGN_NEG; - *vcalk = (int16_t)vcalk_val; - - return 0; -} - -static int aw88399_dev_get_internal_vcalk(struct aw88399 *aw88399, int16_t *vcalk) -{ - uint16_t vcalkh_val, vcalkl_val, vcalk_val; - struct aw_device *aw_dev = aw88399->aw_pa; - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_EFRH2_REG, ®_val); - if (ret) - return ret; - vcalkh_val = reg_val & (~AW88399_INTERNAL_VSN_TRIM_H_MASK); - - ret = regmap_read(aw_dev->regmap, AW88399_EFRL2_REG, ®_val); - if (ret) - return ret; - vcalkl_val = reg_val & (~AW88399_INTERNAL_VSN_TRIM_L_MASK); - - if (aw88399->check_val == AW_EF_AND_CHECK) - vcalk_val = (vcalkh_val >> AW88399_INTERNAL_VSN_TRIM_H_START_BIT) & - (vcalkl_val >> AW88399_INTERNAL_VSN_TRIM_L_START_BIT); - else - vcalk_val = (vcalkh_val >> AW88399_INTERNAL_VSN_TRIM_H_START_BIT) | - (vcalkl_val >> AW88399_INTERNAL_VSN_TRIM_L_START_BIT); - - if (vcalk_val & (~AW88399_TEM4_SIGN_MASK)) - vcalk_val = vcalk_val | AW88399_TEM4_SIGN_NEG; - - *vcalk = (int16_t)vcalk_val; - - return 0; -} - -static int aw_dev_set_vcalb(struct aw88399 *aw88399) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - unsigned int vsense_select, vsense_value; - int32_t ical_k, vcal_k, vcalb; - int16_t icalk, vcalk; - uint16_t reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_VSNCTRL1_REG, &vsense_value); - if (ret) - return ret; - - vsense_select = vsense_value & (~AW88399_VDSEL_MASK); - - ret = aw88399_dev_get_icalk(aw88399, &icalk); - if (ret) { - dev_err(aw_dev->dev, "get icalk failed\n"); - return ret; - } - - ical_k = icalk * AW88399_ICABLK_FACTOR + AW88399_CABL_BASE_VALUE; - - switch (vsense_select) { - case AW88399_DEV_VDSEL_VSENSE: - ret = aw88399_dev_get_vcalk(aw88399, &vcalk); - vcal_k = vcalk * AW88399_VCABLK_FACTOR + AW88399_CABL_BASE_VALUE; - vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_FACTOR / AW88399_ISCAL_FACTOR * - ical_k / vcal_k * aw88399->vcalb_init_val; - break; - case AW88399_DEV_VDSEL_DAC: - ret = aw88399_dev_get_internal_vcalk(aw88399, &vcalk); - vcal_k = vcalk * AW88399_VCABLK_DAC_FACTOR + AW88399_CABL_BASE_VALUE; - vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_DAC_FACTOR / - AW88399_ISCAL_DAC_FACTOR * ical_k / - vcal_k * aw88399->vcalb_init_val; - break; - default: - dev_err(aw_dev->dev, "%s: unsupported vsense\n", __func__); - ret = -EINVAL; - break; - } - if (ret) - return ret; - - vcalb = vcalb >> AW88399_VCALB_ADJ_FACTOR; - reg_val = (uint32_t)vcalb; - - regmap_write(aw_dev->regmap, AW88399_DSPVCALB_REG, reg_val); - - return 0; -} - -static int aw_dev_update_cali_re(struct aw_cali_desc *cali_desc) -{ - struct aw_device *aw_dev = - container_of(cali_desc, struct aw_device, cali_desc); - uint16_t re_lbits, re_hbits; - u32 cali_re; - int ret; - - if ((aw_dev->cali_desc.cali_re >= AW88399_CALI_RE_MAX) || - (aw_dev->cali_desc.cali_re <= AW88399_CALI_RE_MIN)) - return -EINVAL; - - cali_re = AW88399_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re + - aw_dev->cali_desc.ra), AW88399_DSP_RE_SHIFT); - - re_hbits = (cali_re & (~AW88399_CALI_RE_HBITS_MASK)) >> AW88399_CALI_RE_HBITS_SHIFT; - re_lbits = (cali_re & (~AW88399_CALI_RE_LBITS_MASK)) >> AW88399_CALI_RE_LBITS_SHIFT; - - ret = regmap_write(aw_dev->regmap, AW88399_ACR1_REG, re_hbits); - if (ret) { - dev_err(aw_dev->dev, "set cali re error"); - return ret; - } - - ret = regmap_write(aw_dev->regmap, AW88399_ACR2_REG, re_lbits); - if (ret) - dev_err(aw_dev->dev, "set cali re error"); - - return ret; -} - -static int aw_dev_fw_crc_check(struct aw_device *aw_dev) -{ - uint16_t check_val, fw_len_val; - unsigned int reg_val; - int ret; - - /* calculate fw_end_addr */ - fw_len_val = ((aw_dev->dsp_fw_len / AW_FW_ADDR_LEN) - 1) + AW88399_CRC_FW_BASE_ADDR; - - /* write fw_end_addr to crc_end_addr */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, - ~AW88399_CRC_END_ADDR_MASK, fw_len_val); - if (ret) - return ret; - /* enable fw crc check */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, - ~AW88399_CRC_CODE_EN_MASK, AW88399_CRC_CODE_EN_ENABLE_VALUE); - - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - - /* read crc check result */ - regmap_read(aw_dev->regmap, AW88399_HAGCST_REG, ®_val); - if (ret) - return ret; - - check_val = (reg_val & (~AW88399_CRC_CHECK_BITS_MASK)) >> AW88399_CRC_CHECK_START_BIT; - - /* disable fw crc check */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, - ~AW88399_CRC_CODE_EN_MASK, AW88399_CRC_CODE_EN_DISABLE_VALUE); - if (ret) - return ret; - - if (check_val != AW88399_CRC_CHECK_PASS_VAL) { - dev_err(aw_dev->dev, "%s failed, check_val 0x%x != 0x%x", - __func__, check_val, AW88399_CRC_CHECK_PASS_VAL); - ret = -EINVAL; - } - - return ret; -} - -static int aw_dev_cfg_crc_check(struct aw_device *aw_dev) -{ - uint16_t check_val, cfg_len_val; - unsigned int reg_val; - int ret; - - /* calculate cfg end addr */ - cfg_len_val = ((aw_dev->dsp_cfg_len / AW_FW_ADDR_LEN) - 1) + AW88399_CRC_CFG_BASE_ADDR; - - /* write cfg_end_addr to crc_end_addr */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, - ~AW88399_CRC_END_ADDR_MASK, cfg_len_val); - if (ret) - return ret; - - /* enable cfg crc check */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, - ~AW88399_CRC_CFG_EN_MASK, AW88399_CRC_CFG_EN_ENABLE_VALUE); - if (ret) - return ret; - - usleep_range(AW88399_1000_US, AW88399_1000_US + 10); - - /* read crc check result */ - ret = regmap_read(aw_dev->regmap, AW88399_HAGCST_REG, ®_val); - if (ret) - return ret; - - check_val = (reg_val & (~AW88399_CRC_CHECK_BITS_MASK)) >> AW88399_CRC_CHECK_START_BIT; - - /* disable cfg crc check */ - ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG, - ~AW88399_CRC_CFG_EN_MASK, AW88399_CRC_CFG_EN_DISABLE_VALUE); - if (ret) - return ret; - - if (check_val != AW88399_CRC_CHECK_PASS_VAL) { - dev_err(aw_dev->dev, "crc_check failed, check val 0x%x != 0x%x", - check_val, AW88399_CRC_CHECK_PASS_VAL); - ret = -EINVAL; - } - - return ret; -} - -static int aw_dev_hw_crc_check(struct aw88399 *aw88399) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - int ret; - - ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, - ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_BYPASS_VALUE); - if (ret) - return ret; - - ret = aw_dev_fw_crc_check(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "fw_crc_check failed\n"); - goto crc_check_failed; - } - - ret = aw_dev_cfg_crc_check(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "cfg_crc_check failed\n"); - goto crc_check_failed; - } - - ret = regmap_write(aw_dev->regmap, AW88399_CRCCTRL_REG, aw88399->crc_init_val); - if (ret) - return ret; - - ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, - ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_WORK_VALUE); - - return ret; - -crc_check_failed: - regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, - ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_WORK_VALUE); - return ret; -} - -static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag) -{ - int ret; - - if (flag) - ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCTRL3_REG, - ~AW88399_I2STXEN_MASK, AW88399_I2STXEN_ENABLE_VALUE); - else - ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG, - ~AW88399_I2STXEN_MASK, AW88399_I2STXEN_DISABLE_VALUE); - - if (ret) - dev_dbg(aw_dev->dev, "%s failed", __func__); -} - -static int aw_dev_get_dsp_status(struct aw_device *aw_dev) -{ - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_WDT_REG, ®_val); - if (ret) - return ret; - if (!(reg_val & (~AW88399_WDT_CNT_MASK))) - return -EPERM; - - return 0; -} - -static int aw_dev_dsp_check(struct aw_device *aw_dev) -{ - int ret, i; - - switch (aw_dev->dsp_cfg) { - case AW88399_DEV_DSP_BYPASS: - dev_dbg(aw_dev->dev, "dsp bypass"); - ret = 0; - break; - case AW88399_DEV_DSP_WORK: - aw_dev_dsp_enable(aw_dev, false); - aw_dev_dsp_enable(aw_dev, true); - usleep_range(AW88399_1000_US, AW88399_1000_US + 10); - for (i = 0; i < AW88399_DEV_DSP_CHECK_MAX; i++) { - ret = aw_dev_get_dsp_status(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "dsp wdt status error=%d", ret); - usleep_range(AW88399_2000_US, AW88399_2000_US + 10); - } - } - break; - default: - dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg); - ret = -EINVAL; - break; - } - - return ret; -} - -static int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value) -{ - struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; - unsigned int reg_value; - u16 real_value; - int ret; - - real_value = min((value + vol_desc->init_volume), (unsigned int)AW88399_MUTE_VOL); - - ret = regmap_read(aw_dev->regmap, AW88399_SYSCTRL2_REG, ®_value); - if (ret) - return ret; - - dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value); - - real_value = (real_value << AW88399_VOL_START_BIT) | (reg_value & AW88399_VOL_MASK); - - ret = regmap_write(aw_dev->regmap, AW88399_SYSCTRL2_REG, real_value); - - return ret; -} - -static void aw_dev_fade_in(struct aw_device *aw_dev) -{ - struct aw_volume_desc *desc = &aw_dev->volume_desc; - u16 fade_in_vol = desc->ctl_volume; - int fade_step = aw_dev->fade_step; - int i; - - if (fade_step == 0 || aw_dev->fade_in_time == 0) { - aw_dev_set_volume(aw_dev, fade_in_vol); - return; - } - - for (i = AW88399_MUTE_VOL; i >= fade_in_vol; i -= fade_step) { - aw_dev_set_volume(aw_dev, i); - usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10); - } - - if (i != fade_in_vol) - aw_dev_set_volume(aw_dev, fade_in_vol); -} - -static void aw_dev_fade_out(struct aw_device *aw_dev) -{ - struct aw_volume_desc *desc = &aw_dev->volume_desc; - int fade_step = aw_dev->fade_step; - int i; - - if (fade_step == 0 || aw_dev->fade_out_time == 0) { - aw_dev_set_volume(aw_dev, AW88399_MUTE_VOL); - return; - } - - for (i = desc->ctl_volume; i <= AW88399_MUTE_VOL; i += fade_step) { - aw_dev_set_volume(aw_dev, i); - usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); - } - - if (i != AW88399_MUTE_VOL) { - aw_dev_set_volume(aw_dev, AW88399_MUTE_VOL); - usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); - } -} - -static void aw88399_dev_mute(struct aw_device *aw_dev, bool is_mute) -{ - if (is_mute) { - aw_dev_fade_out(aw_dev); - regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_HMUTE_MASK, AW88399_HMUTE_ENABLE_VALUE); - } else { - regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG, - ~AW88399_HMUTE_MASK, AW88399_HMUTE_DISABLE_VALUE); - aw_dev_fade_in(aw_dev); - } -} - -static void aw88399_dev_set_dither(struct aw88399 *aw88399, bool dither) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - - if (dither) - regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, - ~AW88399_DITHER_EN_MASK, AW88399_DITHER_EN_ENABLE_VALUE); - else - regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, - ~AW88399_DITHER_EN_MASK, AW88399_DITHER_EN_DISABLE_VALUE); -} - -static int aw88399_dev_start(struct aw88399 *aw88399) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - int ret; - - if (aw_dev->status == AW88399_DEV_PW_ON) { - dev_dbg(aw_dev->dev, "already power on"); - return 0; - } - - aw88399_dev_set_dither(aw88399, false); - - /* power on */ - aw_dev_pwd(aw_dev, false); - usleep_range(AW88399_2000_US, AW88399_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(AW88399_1000_US, AW88399_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; - } - - if (aw_dev->dsp_cfg == AW88399_DEV_DSP_WORK) { - ret = aw_dev_hw_crc_check(aw88399); - if (ret) { - dev_err(aw_dev->dev, "dsp crc check failed"); - goto crc_check_fail; - } - aw_dev_dsp_enable(aw_dev, false); - aw_dev_set_vcalb(aw88399); - aw_dev_update_cali_re(&aw_dev->cali_desc); - - ret = aw_dev_dsp_check(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "dsp status check failed"); - goto dsp_check_fail; - } - } else { - dev_dbg(aw_dev->dev, "start pa with dsp bypass"); - } - - /* enable tx feedback */ - aw_dev_i2s_tx_enable(aw_dev, true); - - if (aw88399->dither_st == AW88399_DITHER_EN_ENABLE_VALUE) - aw88399_dev_set_dither(aw88399, true); - - /* close mute */ - aw88399_dev_mute(aw_dev, false); - /* clear inturrupt */ - aw_dev_clear_int_status(aw_dev); - aw_dev->status = AW88399_DEV_PW_ON; - - return 0; - -dsp_check_fail: -crc_check_fail: - aw_dev_dsp_enable(aw_dev, false); -sysst_check_fail: - aw_dev_clear_int_status(aw_dev); - aw_dev_amppd(aw_dev, true); -pll_check_fail: - aw_dev_pwd(aw_dev, true); - aw_dev->status = AW88399_DEV_PW_OFF; - - return ret; -} - -static int aw_dev_dsp_update_container(struct aw_device *aw_dev, - unsigned char *data, unsigned int len, unsigned short base) -{ - u32 tmp_len; - int i, ret; - - ret = regmap_write(aw_dev->regmap, AW88399_DSPMADD_REG, base); - if (ret) - return ret; - - for (i = 0; i < len; i += AW88399_MAX_RAM_WRITE_BYTE_SIZE) { - tmp_len = min(len - i, AW88399_MAX_RAM_WRITE_BYTE_SIZE); - ret = regmap_raw_write(aw_dev->regmap, AW88399_DSPMDAT_REG, - &data[i], tmp_len); - if (ret) - return ret; - } - - return 0; -} - -static int aw_dev_get_ra(struct aw_cali_desc *cali_desc) -{ - struct aw_device *aw_dev = - container_of(cali_desc, struct aw_device, cali_desc); - u32 dsp_ra; - int ret; - - ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_ADPZ_RA, - &dsp_ra, AW_DSP_32_DATA); - if (ret) { - dev_err(aw_dev->dev, "read ra error"); - return ret; - } - - cali_desc->ra = AW88399_DSP_RE_TO_SHOW_RE(dsp_ra, - AW88399_DSP_RE_SHIFT); - - return 0; -} - -static int aw_dev_dsp_update_cfg(struct aw_device *aw_dev, - unsigned char *data, unsigned int len) -{ - int ret; - - dev_dbg(aw_dev->dev, "dsp config len:%d", len); - - if (!len || !data) { - dev_err(aw_dev->dev, "dsp config data is null or len is 0"); - return -EINVAL; - } - - ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88399_DSP_CFG_ADDR); - if (ret) - return ret; - - aw_dev->dsp_cfg_len = len; - - ret = aw_dev_get_ra(&aw_dev->cali_desc); - - return ret; -} - -static int aw_dev_dsp_update_fw(struct aw_device *aw_dev, - unsigned char *data, unsigned int len) -{ - int ret; - - dev_dbg(aw_dev->dev, "dsp firmware len:%d", len); - - if (!len || !data) { - dev_err(aw_dev->dev, "dsp firmware data is null or len is 0"); - return -EINVAL; - } - - aw_dev->dsp_fw_len = len; - ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88399_DSP_FW_ADDR); - - return ret; -} - -static int aw_dev_check_sram(struct aw_device *aw_dev) -{ - unsigned int reg_val; - - /* read dsp_rom_check_reg */ - aw_dev_dsp_read(aw_dev, AW88399_DSP_ROM_CHECK_ADDR, ®_val, AW_DSP_16_DATA); - if (reg_val != AW88399_DSP_ROM_CHECK_DATA) { - dev_err(aw_dev->dev, "check dsp rom failed, read[0x%x] != check[0x%x]", - reg_val, AW88399_DSP_ROM_CHECK_DATA); - return -EPERM; - } - - /* check dsp_cfg_base_addr */ - aw_dev_dsp_write(aw_dev, AW88399_DSP_CFG_ADDR, - AW88399_DSP_ODD_NUM_BIT_TEST, AW_DSP_16_DATA); - aw_dev_dsp_read(aw_dev, AW88399_DSP_CFG_ADDR, ®_val, AW_DSP_16_DATA); - if (reg_val != AW88399_DSP_ODD_NUM_BIT_TEST) { - dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]", - reg_val, AW88399_DSP_ODD_NUM_BIT_TEST); - return -EPERM; - } - - return 0; -} - -static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char flag) -{ - int ret; - - switch (flag) { - case AW88399_DEV_MEMCLK_PLL: - ret = regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, - ~AW88399_MEM_CLKSEL_MASK, - AW88399_MEM_CLKSEL_DAPHCLK_VALUE); - if (ret) - dev_err(aw_dev->dev, "memclk select pll failed"); - break; - case AW88399_DEV_MEMCLK_OSC: - ret = regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG, - ~AW88399_MEM_CLKSEL_MASK, - AW88399_MEM_CLKSEL_OSCCLK_VALUE); - if (ret) - dev_err(aw_dev->dev, "memclk select OSC failed"); - break; - default: - dev_err(aw_dev->dev, "unknown memclk config, flag=0x%x", flag); - break; - } -} - -static void aw_dev_get_cur_mode_st(struct aw_device *aw_dev) -{ - struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; - unsigned int reg_val; - int ret; - - ret = regmap_read(aw_dev->regmap, AW88399_SYSCTRL_REG, ®_val); - if (ret) { - dev_dbg(aw_dev->dev, "%s failed", __func__); - return; - } - if ((reg_val & (~AW88399_RCV_MODE_MASK)) == AW88399_RCV_MODE_RECEIVER_VALUE) - profctrl_desc->cur_mode = AW88399_RCV_MODE; - else - profctrl_desc->cur_mode = AW88399_NOT_RCV_MODE; -} - -static int aw_dev_update_reg_container(struct aw88399 *aw88399, - unsigned char *data, unsigned int len) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; - u16 read_vol, reg_val; - int data_len, i, ret; - int16_t *reg_data; - u8 reg_addr; - - 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 == AW88399_DSPVCALB_REG) { - aw88399->vcalb_init_val = reg_val; - continue; - } - - if (reg_addr == AW88399_SYSCTRL_REG) { - if (reg_val & (~AW88399_DSPBY_MASK)) - aw_dev->dsp_cfg = AW88399_DEV_DSP_BYPASS; - else - aw_dev->dsp_cfg = AW88399_DEV_DSP_WORK; - - reg_val &= (AW88399_HMUTE_MASK | AW88399_PWDN_MASK | - AW88399_DSPBY_MASK); - reg_val |= (AW88399_HMUTE_ENABLE_VALUE | AW88399_PWDN_POWER_DOWN_VALUE | - AW88399_DSPBY_BYPASS_VALUE); - } - - if (reg_addr == AW88399_I2SCTRL3_REG) { - reg_val &= AW88399_I2STXEN_MASK; - reg_val |= AW88399_I2STXEN_DISABLE_VALUE; - } - - if (reg_addr == AW88399_SYSCTRL2_REG) { - read_vol = (reg_val & (~AW88399_VOL_MASK)) >> - AW88399_VOL_START_BIT; - aw_dev->volume_desc.init_volume = read_vol; - } - - if (reg_addr == AW88399_DBGCTRL_REG) { - if ((reg_val & (~AW88399_EF_DBMD_MASK)) == AW88399_EF_DBMD_OR_VALUE) - aw88399->check_val = AW_EF_OR_CHECK; - else - aw88399->check_val = AW_EF_AND_CHECK; - - aw88399->dither_st = reg_val & (~AW88399_DITHER_EN_MASK); - } - - if (reg_addr == AW88399_CRCCTRL_REG) - aw88399->crc_init_val = reg_val; - - ret = regmap_write(aw_dev->regmap, reg_addr, reg_val); - if (ret) - return ret; - } - - aw_dev_pwd(aw_dev, false); - usleep_range(AW88399_1000_US, AW88399_1000_US + 10); - - aw_dev_get_cur_mode_st(aw_dev); - - if (aw_dev->prof_cur != aw_dev->prof_index) - vol_desc->ctl_volume = 0; - else - aw_dev_set_volume(aw_dev, vol_desc->ctl_volume); - - return 0; -} - -static int aw_dev_reg_update(struct aw88399 *aw88399, - unsigned char *data, unsigned int len) -{ - int ret; - - if (!len || !data) { - dev_err(aw88399->aw_pa->dev, "reg data is null or len is 0"); - return -EINVAL; - } - - ret = aw_dev_update_reg_container(aw88399, data, len); - if (ret) - dev_err(aw88399->aw_pa->dev, "reg update failed"); - - return ret; -} - -static int aw88399_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name) -{ - struct aw_prof_info *prof_info = &aw_dev->prof_info; - struct aw_prof_desc *prof_desc; - - if ((index >= aw_dev->prof_info.count) || (index < 0)) { - dev_err(aw_dev->dev, "index[%d] overflow count[%d]", - index, aw_dev->prof_info.count); - return -EINVAL; - } - - prof_desc = &aw_dev->prof_info.prof_desc[index]; - - *prof_name = prof_info->prof_name_list[prof_desc->id]; - - return 0; -} - -static int aw88399_dev_get_prof_data(struct aw_device *aw_dev, int index, - struct aw_prof_desc **prof_desc) -{ - if ((index >= aw_dev->prof_info.count) || (index < 0)) { - dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n", - __func__, index, aw_dev->prof_info.count); - return -EINVAL; - } - - *prof_desc = &aw_dev->prof_info.prof_desc[index]; - - return 0; -} - -static int aw88399_dev_fw_update(struct aw88399 *aw88399, bool up_dsp_fw_en, bool force_up_en) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - struct aw_prof_desc *prof_index_desc; - struct aw_sec_data_desc *sec_desc; - char *prof_name; - int ret; - - if ((aw_dev->prof_cur == aw_dev->prof_index) && - (force_up_en == AW88399_FORCE_UPDATE_OFF)) { - dev_dbg(aw_dev->dev, "scene no change, not update"); - return 0; - } - - if (aw_dev->fw_status == AW88399_DEV_FW_FAILED) { - dev_err(aw_dev->dev, "fw status[%d] error", aw_dev->fw_status); - return -EPERM; - } - - ret = aw88399_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name); - if (ret) - return ret; - - dev_dbg(aw_dev->dev, "start update %s", prof_name); - - ret = aw88399_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc); - if (ret) - return ret; - - /* update reg */ - sec_desc = prof_index_desc->sec_desc; - ret = aw_dev_reg_update(aw88399, 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; - } - - aw88399_dev_mute(aw_dev, true); - - if (aw_dev->dsp_cfg == AW88399_DEV_DSP_WORK) - aw_dev_dsp_enable(aw_dev, false); - - aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_OSC); - - ret = aw_dev_check_sram(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "check sram failed"); - goto error; - } - - if (up_dsp_fw_en) { - dev_dbg(aw_dev->dev, "fw_ver: [%x]", prof_index_desc->fw_ver); - ret = aw_dev_dsp_update_fw(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_FW].data, - sec_desc[AW88395_DATA_TYPE_DSP_FW].len); - if (ret) { - dev_err(aw_dev->dev, "update dsp fw failed"); - goto error; - } - } - - /* update dsp config */ - ret = aw_dev_dsp_update_cfg(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_CFG].data, - sec_desc[AW88395_DATA_TYPE_DSP_CFG].len); - if (ret) { - dev_err(aw_dev->dev, "update dsp cfg failed"); - goto error; - } - - aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL); - - aw_dev->prof_cur = aw_dev->prof_index; - - return 0; - -error: - aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL); - return ret; -} - -static void aw88399_start_pa(struct aw88399 *aw88399) -{ - int ret, i; - - for (i = 0; i < AW88399_START_RETRIES; i++) { - ret = aw88399_dev_start(aw88399); - if (ret) { - dev_err(aw88399->aw_pa->dev, "aw88399 device start failed. retry = %d", i); - ret = aw88399_dev_fw_update(aw88399, AW88399_DSP_FW_UPDATE_ON, true); - if (ret) { - dev_err(aw88399->aw_pa->dev, "fw update failed"); - continue; - } - } else { - dev_dbg(aw88399->aw_pa->dev, "start success\n"); - break; - } - } -} - -static void aw88399_startup_work(struct work_struct *work) -{ - struct aw88399 *aw88399 = - container_of(work, struct aw88399, start_work.work); - - mutex_lock(&aw88399->lock); - aw88399_start_pa(aw88399); - mutex_unlock(&aw88399->lock); -} - -static void aw88399_start(struct aw88399 *aw88399, bool sync_start) -{ - int ret; - - if (aw88399->aw_pa->fw_status != AW88399_DEV_FW_OK) - return; - - if (aw88399->aw_pa->status == AW88399_DEV_PW_ON) - return; - - ret = aw88399_dev_fw_update(aw88399, AW88399_DSP_FW_UPDATE_OFF, true); - if (ret) { - dev_err(aw88399->aw_pa->dev, "fw update failed."); - return; - } - - if (sync_start == AW88399_SYNC_START) - aw88399_start_pa(aw88399); - else - queue_delayed_work(system_dfl_wq, - &aw88399->start_work, - AW88399_START_WORK_DELAY_MS); -} - -static int aw_dev_check_sysint(struct aw_device *aw_dev) -{ - u16 reg_val; - - aw_dev_get_int_status(aw_dev, ®_val); - if (reg_val & AW88399_BIT_SYSINT_CHECK) { - dev_err(aw_dev->dev, "pa stop check fail:0x%04x", reg_val); - return -EINVAL; - } - - return 0; -} - -static int aw88399_stop(struct aw_device *aw_dev) -{ - struct aw_sec_data_desc *dsp_cfg = - &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_CFG]; - struct aw_sec_data_desc *dsp_fw = - &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_FW]; - int int_st; - - if (aw_dev->status == AW88399_DEV_PW_OFF) { - dev_dbg(aw_dev->dev, "already power off"); - return 0; - } - - aw_dev->status = AW88399_DEV_PW_OFF; - - aw88399_dev_mute(aw_dev, true); - usleep_range(AW88399_4000_US, AW88399_4000_US + 100); - - aw_dev_i2s_tx_enable(aw_dev, false); - usleep_range(AW88399_1000_US, AW88399_1000_US + 100); - - int_st = aw_dev_check_sysint(aw_dev); - - aw_dev_dsp_enable(aw_dev, false); - - aw_dev_amppd(aw_dev, true); - - if (int_st) { - aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_OSC); - aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len); - aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len); - aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL); - } - - aw_dev_pwd(aw_dev, true); - - return 0; -} - static struct snd_soc_dai_driver aw88399_dai[] = { { .name = "aw88399-aif", @@ -1869,86 +664,6 @@ static int aw88399_calib_set(struct snd_kcontrol *kcontrol, return 0; } -static int aw88399_dev_init(struct aw88399 *aw88399, struct aw_container *aw_cfg) -{ - struct aw_device *aw_dev = aw88399->aw_pa; - int ret; - - ret = aw88395_dev_cfg_load(aw_dev, aw_cfg); - if (ret) { - dev_err(aw_dev->dev, "aw_dev acf parse failed"); - return -EINVAL; - } - aw_dev->fade_in_time = AW88399_1000_US / 10; - aw_dev->fade_out_time = AW88399_1000_US >> 1; - aw_dev->prof_cur = aw_dev->prof_info.prof_desc[0].id; - aw_dev->prof_index = aw_dev->prof_info.prof_desc[0].id; - - ret = aw88399_dev_fw_update(aw88399, AW88399_FORCE_UPDATE_ON, AW88399_DSP_FW_UPDATE_ON); - if (ret) { - dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret); - return ret; - } - - aw88399_dev_mute(aw_dev, true); - - /* close tx feedback */ - aw_dev_i2s_tx_enable(aw_dev, false); - usleep_range(AW88399_1000_US, AW88399_1000_US + 100); - - /* enable amppd */ - aw_dev_amppd(aw_dev, true); - - /* close dsp */ - aw_dev_dsp_enable(aw_dev, false); - /* set power down */ - aw_dev_pwd(aw_dev, true); - - return 0; -} - -static int aw88399_request_firmware_file(struct aw88399 *aw88399) -{ - const struct firmware *cont = NULL; - int ret; - - aw88399->aw_pa->fw_status = AW88399_DEV_FW_FAILED; - - ret = request_firmware(&cont, AW88399_ACF_FILE, aw88399->aw_pa->dev); - if (ret) { - dev_err(aw88399->aw_pa->dev, "request [%s] failed!", AW88399_ACF_FILE); - return ret; - } - - dev_dbg(aw88399->aw_pa->dev, "loaded %s - size: %zu\n", - AW88399_ACF_FILE, cont ? cont->size : 0); - - aw88399->aw_cfg = devm_kzalloc(aw88399->aw_pa->dev, - struct_size(aw88399->aw_cfg, data, cont->size), GFP_KERNEL); - if (!aw88399->aw_cfg) { - release_firmware(cont); - return -ENOMEM; - } - aw88399->aw_cfg->len = (int)cont->size; - memcpy(aw88399->aw_cfg->data, cont->data, cont->size); - release_firmware(cont); - - ret = aw88395_dev_load_acf_check(aw88399->aw_pa, aw88399->aw_cfg); - if (ret) { - dev_err(aw88399->aw_pa->dev, "load [%s] failed!", AW88399_ACF_FILE); - return ret; - } - - mutex_lock(&aw88399->lock); - /* aw device init */ - ret = aw88399_dev_init(aw88399, aw88399->aw_cfg); - if (ret) - dev_err(aw88399->aw_pa->dev, "dev init failed"); - mutex_unlock(&aw88399->lock); - - return ret; -} - static const struct snd_kcontrol_new aw88399_controls[] = { SOC_SINGLE_EXT("PCM Playback Volume", AW88399_SYSCTRL2_REG, 6, AW88399_MUTE_VOL, 0, aw88399_volume_get, @@ -2040,70 +755,6 @@ static const struct snd_soc_component_driver soc_codec_dev_aw88399 = { .num_controls = ARRAY_SIZE(aw88399_controls), }; -static void aw88399_hw_reset(struct aw88399 *aw88399) -{ - if (aw88399->reset_gpio) { - gpiod_set_value_cansleep(aw88399->reset_gpio, 1); - usleep_range(AW88399_1000_US, AW88399_1000_US + 10); - gpiod_set_value_cansleep(aw88399->reset_gpio, 0); - usleep_range(AW88399_1000_US, AW88399_1000_US + 10); - gpiod_set_value_cansleep(aw88399->reset_gpio, 1); - usleep_range(AW88399_1000_US, AW88399_1000_US + 10); - } -} - -static void aw88399_parse_channel_dt(struct aw_device *aw_dev) -{ - struct device_node *np = aw_dev->dev->of_node; - u32 channel_value; - - of_property_read_u32(np, "awinic,audio-channel", &channel_value); - aw_dev->channel = channel_value; -} - -static int aw88399_init(struct aw88399 *aw88399, struct i2c_client *i2c, struct regmap *regmap) -{ - struct aw_device *aw_dev; - unsigned int chip_id; - int ret; - - ret = regmap_read(regmap, AW88399_ID_REG, &chip_id); - if (ret) { - dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret); - return ret; - } - if (chip_id != AW88399_CHIP_ID) { - dev_err(&i2c->dev, "unsupported device"); - return -ENXIO; - } - dev_dbg(&i2c->dev, "chip id = %x\n", chip_id); - - aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL); - if (!aw_dev) - return -ENOMEM; - aw88399->aw_pa = aw_dev; - - aw_dev->i2c = i2c; - aw_dev->dev = &i2c->dev; - aw_dev->regmap = regmap; - mutex_init(&aw_dev->dsp_lock); - - aw_dev->chip_id = chip_id; - aw_dev->acf = NULL; - aw_dev->prof_info.prof_desc = NULL; - aw_dev->prof_info.count = 0; - aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID; - aw_dev->channel = AW88399_DEV_DEFAULT_CH; - aw_dev->fw_status = AW88399_DEV_FW_FAILED; - - aw_dev->fade_step = AW88399_VOLUME_STEP_DB; - aw_dev->volume_desc.ctl_volume = AW88399_VOL_DEFAULT_VALUE; - - aw88399_parse_channel_dt(aw_dev); - - return 0; -} - static int aw88399_i2c_probe(struct i2c_client *i2c) { struct aw88399 *aw88399; diff --git a/sound/soc/codecs/aw88399.h b/sound/soc/codecs/aw88399.h index b386f4836748..04123bf0ad84 100644 --- a/sound/soc/codecs/aw88399.h +++ b/sound/soc/codecs/aw88399.h @@ -10,512 +10,10 @@ #ifndef __AW88399_H__ #define __AW88399_H__ -/* registers list */ -#define AW88399_ID_REG (0x00) -#define AW88399_SYSST_REG (0x01) -#define AW88399_SYSINT_REG (0x02) -#define AW88399_SYSINTM_REG (0x03) -#define AW88399_SYSCTRL_REG (0x04) -#define AW88399_SYSCTRL2_REG (0x05) -#define AW88399_I2SCTRL1_REG (0x06) -#define AW88399_I2SCTRL2_REG (0x07) -#define AW88399_I2SCTRL3_REG (0x08) -#define AW88399_DACCFG1_REG (0x09) -#define AW88399_DACCFG2_REG (0x0A) -#define AW88399_DACCFG3_REG (0x0B) -#define AW88399_DACCFG4_REG (0x0C) -#define AW88399_DACCFG5_REG (0x0D) -#define AW88399_DACCFG6_REG (0x0E) -#define AW88399_DACCFG7_REG (0x0F) -#define AW88399_MPDCFG1_REG (0x10) -#define AW88399_MPDCFG2_REG (0x11) -#define AW88399_MPDCFG3_REG (0x12) -#define AW88399_MPDCFG4_REG (0x13) -#define AW88399_PWMCTRL1_REG (0x14) -#define AW88399_PWMCTRL2_REG (0x15) -#define AW88399_PWMCTRL3_REG (0x16) -#define AW88399_I2SCFG1_REG (0x17) -#define AW88399_DBGCTRL_REG (0x18) -#define AW88399_HAGCST_REG (0x20) -#define AW88399_VBAT_REG (0x21) -#define AW88399_TEMP_REG (0x22) -#define AW88399_PVDD_REG (0x23) -#define AW88399_ISNDAT_REG (0x24) -#define AW88399_VSNDAT_REG (0x25) -#define AW88399_I2SINT_REG (0x26) -#define AW88399_I2SCAPCNT_REG (0x27) -#define AW88399_ANASTA1_REG (0x28) -#define AW88399_ANASTA2_REG (0x29) -#define AW88399_ANASTA3_REG (0x2A) -#define AW88399_TESTDET_REG (0x2B) -#define AW88399_DSMCFG1_REG (0x30) -#define AW88399_DSMCFG2_REG (0x31) -#define AW88399_DSMCFG3_REG (0x32) -#define AW88399_DSMCFG4_REG (0x33) -#define AW88399_DSMCFG5_REG (0x34) -#define AW88399_DSMCFG6_REG (0x35) -#define AW88399_DSMCFG7_REG (0x36) -#define AW88399_DSMCFG8_REG (0x37) -#define AW88399_TESTIN_REG (0x38) -#define AW88399_TESTOUT_REG (0x39) -#define AW88399_MEMTEST_REG (0x3A) -#define AW88399_VSNCTRL1_REG (0x3B) -#define AW88399_ISNCTRL1_REG (0x3C) -#define AW88399_ISNCTRL2_REG (0x3D) -#define AW88399_DSPMADD_REG (0x40) -#define AW88399_DSPMDAT_REG (0x41) -#define AW88399_WDT_REG (0x42) -#define AW88399_ACR1_REG (0x43) -#define AW88399_ACR2_REG (0x44) -#define AW88399_ASR1_REG (0x45) -#define AW88399_ASR2_REG (0x46) -#define AW88399_DSPCFG_REG (0x47) -#define AW88399_ASR3_REG (0x48) -#define AW88399_ASR4_REG (0x49) -#define AW88399_DSPVCALB_REG (0x4A) -#define AW88399_CRCCTRL_REG (0x4B) -#define AW88399_DSPDBG1_REG (0x4C) -#define AW88399_DSPDBG2_REG (0x4D) -#define AW88399_DSPDBG3_REG (0x4E) -#define AW88399_PLLCTRL1_REG (0x50) -#define AW88399_PLLCTRL2_REG (0x51) -#define AW88399_PLLCTRL3_REG (0x52) -#define AW88399_CDACTRL1_REG (0x53) -#define AW88399_CDACTRL2_REG (0x54) -#define AW88399_CDACTRL3_REG (0x55) -#define AW88399_SADCCTRL1_REG (0x56) -#define AW88399_SADCCTRL2_REG (0x57) -#define AW88399_BOPCTRL1_REG (0x58) -#define AW88399_BOPCTRL2_REG (0x5A) -#define AW88399_BOPCTRL3_REG (0x5B) -#define AW88399_BOPCTRL4_REG (0x5C) -#define AW88399_BOPCTRL5_REG (0x5D) -#define AW88399_BOPCTRL6_REG (0x5E) -#define AW88399_BOPCTRL7_REG (0x5F) -#define AW88399_BSTCTRL1_REG (0x60) -#define AW88399_BSTCTRL2_REG (0x61) -#define AW88399_BSTCTRL3_REG (0x62) -#define AW88399_BSTCTRL4_REG (0x63) -#define AW88399_BSTCTRL5_REG (0x64) -#define AW88399_BSTCTRL6_REG (0x65) -#define AW88399_BSTCTRL7_REG (0x66) -#define AW88399_BSTCTRL8_REG (0x67) -#define AW88399_BSTCTRL9_REG (0x68) -#define AW88399_BSTCTRL10_REG (0x69) -#define AW88399_CPCTRL_REG (0x6A) -#define AW88399_EFWH_REG (0x6C) -#define AW88399_EFWM2_REG (0x6D) -#define AW88399_EFWM1_REG (0x6E) -#define AW88399_EFWL_REG (0x6F) -#define AW88399_TESTCTRL1_REG (0x70) -#define AW88399_TESTCTRL2_REG (0x71) -#define AW88399_EFCTRL1_REG (0x72) -#define AW88399_EFCTRL2_REG (0x73) -#define AW88399_EFRH4_REG (0x74) -#define AW88399_EFRH3_REG (0x75) -#define AW88399_EFRH2_REG (0x76) -#define AW88399_EFRH1_REG (0x77) -#define AW88399_EFRL4_REG (0x78) -#define AW88399_EFRL3_REG (0x79) -#define AW88399_EFRL2_REG (0x7A) -#define AW88399_EFRL1_REG (0x7B) -#define AW88399_TM_REG (0x7C) -#define AW88399_TM2_REG (0x7D) - -#define AW88399_REG_MAX (0x7E) -#define AW88399_MUTE_VOL (1023) - -#define AW88399_DSP_CFG_ADDR (0x9B00) -#define AW88399_DSP_REG_CFG_ADPZ_RA (0x9B68) -#define AW88399_DSP_FW_ADDR (0x8980) -#define AW88399_DSP_ROM_CHECK_ADDR (0x1F40) -#define AW88399_DSP_ROM_CHECK_DATA (0x4638) - -#define AW88399_CALI_RE_HBITS_MASK (~(0xFFFF0000)) -#define AW88399_CALI_RE_HBITS_SHIFT (16) - -#define AW88399_CALI_RE_LBITS_MASK (~(0xFFFF)) -#define AW88399_CALI_RE_LBITS_SHIFT (0) - -#define AW88399_I2STXEN_START_BIT (9) -#define AW88399_I2STXEN_BITS_LEN (1) -#define AW88399_I2STXEN_MASK \ - (~(((1<> (shift)) -#define AW88399_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000)) -#define AW88399_CRC_CHECK_PASS_VAL (0x4) - -#define AW88399_CRC_CFG_BASE_ADDR (0xD80) -#define AW88399_CRC_FW_BASE_ADDR (0x4C0) -#define AW88399_ACF_FILE "aw88399_acf.bin" -#define AW88399_DEV_SYSST_CHECK_MAX (10) -#define AW88399_CHIP_ID 0x2183 +#include #define AW88399_I2C_NAME "aw88399" -#define AW88399_START_RETRIES (5) -#define AW88399_START_WORK_DELAY_MS (0) - #define AW88399_RATES (SNDRV_PCM_RATE_8000_48000 | \ SNDRV_PCM_RATE_96000) #define AW88399_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ @@ -550,80 +48,4 @@ .put = profile_set, \ } -enum { - AW_EF_AND_CHECK = 0, - AW_EF_OR_CHECK, -}; - -enum { - AW88399_DEV_VDSEL_DAC = 0, - AW88399_DEV_VDSEL_VSENSE = 32, -}; - -enum { - AW88399_DSP_CRC_NA = 0, - AW88399_DSP_CRC_OK = 1, -}; - -enum { - AW88399_DSP_FW_UPDATE_OFF = 0, - AW88399_DSP_FW_UPDATE_ON = 1, -}; - -enum { - AW88399_FORCE_UPDATE_OFF = 0, - AW88399_FORCE_UPDATE_ON = 1, -}; - -enum { - AW88399_1000_US = 1000, - AW88399_2000_US = 2000, - AW88399_3000_US = 3000, - AW88399_4000_US = 4000, -}; - -enum AW88399_DEV_STATUS { - AW88399_DEV_PW_OFF = 0, - AW88399_DEV_PW_ON, -}; - -enum AW88399_DEV_FW_STATUS { - AW88399_DEV_FW_FAILED = 0, - AW88399_DEV_FW_OK, -}; - -enum AW88399_DEV_MEMCLK { - AW88399_DEV_MEMCLK_OSC = 0, - AW88399_DEV_MEMCLK_PLL = 1, -}; - -enum AW88399_DEV_DSP_CFG { - AW88399_DEV_DSP_WORK = 0, - AW88399_DEV_DSP_BYPASS = 1, -}; - -enum { - AW88399_NOT_RCV_MODE = 0, - AW88399_RCV_MODE = 1, -}; - -enum { - AW88399_SYNC_START = 0, - AW88399_ASYNC_START, -}; - -struct aw88399 { - struct aw_device *aw_pa; - struct mutex lock; - struct gpio_desc *reset_gpio; - struct delayed_work start_work; - struct regmap *regmap; - struct aw_container *aw_cfg; - - unsigned int check_val; - unsigned int crc_init_val; - unsigned int vcalb_init_val; - unsigned int dither_st; -}; - -#endif +#endif /* __AW88399_H__ */ -- cgit v1.2.3 From b4530a3e4895abfc308e2b45c0ea508f4dddd9f1 Mon Sep 17 00:00:00 2001 From: Marco Giunta Date: Fri, 17 Jul 2026 15:25:05 +0200 Subject: ASoC: aw88399: add per-instance BSTS status bypass flag Add a bsts_unreliable flag to struct aw88399 that, when set, causes the startup status check (aw_dev_check_sysst) to skip the BSTS (boost startup finished) requirement. On some hardware, the BSTS bit in the SYSST register (0x01, bit 9) does not reliably assert even during normal audio playback. Register inspection on affected Lenovo Legion hardware shows both amplifiers reporting BSTS=0 on both channels despite clean audio output. Per the AW88399 datasheet, BSTS indicates boost startup completion. If BSTS never reliably sets to 1, the chip is never allowed to start by aw_dev_check_sysst, regardless of whether the boot failure is genuine. The new flag defaults to false via kzalloc, preserving the original check behavior for all existing users. No existing code path sets this flag; it will be set by the forthcoming HDA side codec property driver for affected hardware. Tested-by: Nadim Kobeissi Tested-by: Xia Yun'an Tested-by: Munzir Taha Signed-off-by: Marco Giunta Link: https://patch.msgid.link/DS7PR19MB77242B8E5BB8BFB5E69816E9FCC62@DS7PR19MB7724.namprd19.prod.outlook.com Signed-off-by: Mark Brown --- include/sound/aw88399.h | 1 + sound/soc/codecs/aw88399-lib.c | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/aw88399.h b/include/sound/aw88399.h index 3a2153f0ee92..dee91b540b81 100644 --- a/include/sound/aw88399.h +++ b/include/sound/aw88399.h @@ -598,6 +598,7 @@ struct aw88399 { unsigned int crc_init_val; unsigned int vcalb_init_val; unsigned int dither_st; + bool bsts_unreliable; }; int aw_dev_check_syspll(struct aw_device *aw_dev); diff --git a/sound/soc/codecs/aw88399-lib.c b/sound/soc/codecs/aw88399-lib.c index b525695c96d1..2045c4171be0 100644 --- a/sound/soc/codecs/aw88399-lib.c +++ b/sound/soc/codecs/aw88399-lib.c @@ -167,8 +167,9 @@ int aw_dev_check_syspll(struct aw_device *aw_dev) } EXPORT_SYMBOL_GPL(aw_dev_check_syspll); -static int aw_dev_check_sysst(struct aw_device *aw_dev) +static int aw_dev_check_sysst(struct aw88399 *aw88399) { + struct aw_device *aw_dev = aw88399->aw_pa; unsigned int check_val; unsigned int reg_val; int ret, i; @@ -182,6 +183,14 @@ static int aw_dev_check_sysst(struct aw_device *aw_dev) else check_val = AW88399_BIT_SYSST_SWS_CHECK; + /* + * On some hardware the BSTS (boost-finished) status bit does not + * reliably assert even when audio output is working normally. + * Allow per-instance bypass when flagged by the side-codec driver. + */ + if (aw88399->bsts_unreliable) + check_val &= ~AW88399_BSTS_FINISHED_VALUE; + for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) { ret = regmap_read(aw_dev->regmap, AW88399_SYSST_REG, ®_val); if (ret) @@ -710,7 +719,7 @@ static int aw88399_dev_start(struct aw88399 *aw88399) usleep_range(AW88399_1000_US, AW88399_1000_US + 50); /* check i2s status */ - ret = aw_dev_check_sysst(aw_dev); + ret = aw_dev_check_sysst(aw88399); if (ret) { dev_err(aw_dev->dev, "sysst check failed"); goto sysst_check_fail; -- cgit v1.2.3 From b5e1d685fb9c9acef5942794a8dc07580c607e66 Mon Sep 17 00:00:00 2001 From: Marco Giunta Date: Fri, 17 Jul 2026 15:25:06 +0200 Subject: ASoC: aw88399: add firmware reload flag for resume Add a fw_needs_reload flag to struct aw88399 that, when set, causes aw88399_start to perform a full DSP firmware upload instead of assuming the firmware binary is already present in memory. After system sleep, the AW88399 loses its memory contents. The existing start sequence assumes the firmware binary persists from initialization and only uploads register configuration and DSP config (AW88399_DSP_FW_UPDATE_OFF). When memory is empty, this causes the subsequent CRC check to fail, triggering the retry mechanism in aw88399_start_pa which re-uploads the firmware on the second attempt. While the retry mechanism recovers correctly, it produces misleading error-level log messages on every resume cycle. The fw_needs_reload flag allows the HDA side codec driver to signal that a full firmware reload is needed after resume, eliminating the spurious CRC failures. The flag defaults to false via kzalloc, preserving the original behavior for existing ASoC users. No existing code path sets this flag; it will be set by the HDA side codec driver's system suspend handler. Tested-by: Nadim Kobeissi Tested-by: Xia Yun'an Tested-by: Munzir Taha Signed-off-by: Marco Giunta Link: https://patch.msgid.link/DS7PR19MB77240CB79188C0B7AE243829FCC62@DS7PR19MB7724.namprd19.prod.outlook.com Signed-off-by: Mark Brown --- include/sound/aw88399.h | 1 + sound/soc/codecs/aw88399-lib.c | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/aw88399.h b/include/sound/aw88399.h index dee91b540b81..3dbc37dc13d1 100644 --- a/include/sound/aw88399.h +++ b/include/sound/aw88399.h @@ -599,6 +599,7 @@ struct aw88399 { unsigned int vcalb_init_val; unsigned int dither_st; bool bsts_unreliable; + bool fw_needs_reload; }; int aw_dev_check_syspll(struct aw_device *aw_dev); diff --git a/sound/soc/codecs/aw88399-lib.c b/sound/soc/codecs/aw88399-lib.c index 2045c4171be0..094a37b66fb5 100644 --- a/sound/soc/codecs/aw88399-lib.c +++ b/sound/soc/codecs/aw88399-lib.c @@ -1171,12 +1171,15 @@ void aw88399_start(struct aw88399 *aw88399, bool sync_start) if (aw88399->aw_pa->status == AW88399_DEV_PW_ON) return; - ret = aw88399_dev_fw_update(aw88399, AW88399_DSP_FW_UPDATE_OFF, true); + ret = aw88399_dev_fw_update(aw88399, aw88399->fw_needs_reload ? + AW88399_DSP_FW_UPDATE_ON : AW88399_DSP_FW_UPDATE_OFF, true); if (ret) { dev_err(aw88399->aw_pa->dev, "fw update failed."); return; } + aw88399->fw_needs_reload = false; + if (sync_start == AW88399_SYNC_START) aw88399_start_pa(aw88399); else -- cgit v1.2.3 From 15c9fdb3c39d5073463ffa746cc64ad9682c875f Mon Sep 17 00:00:00 2001 From: Marco Giunta Date: Fri, 17 Jul 2026 15:25:07 +0200 Subject: ASoC: aw88399: add channel setter for HDA side codec Add aw88399_dev_set_channel() to the shared library so that the HDA side codec driver can set the amplifier's channel assignment without including the aw88395 device header directly. The AW88399's struct aw_device is defined in aw88395_device.h, which lives under sound/soc/codecs/aw88395/. Without this accessor, the HDA driver would need a cross-subsystem relative include path to access the channel field. Providing a setter in the library keeps the interface clean and avoids coupling the HDA driver to ASoC-internal headers. Tested-by: Nadim Kobeissi Tested-by: Xia Yun'an Tested-by: Munzir Taha Signed-off-by: Marco Giunta Link: https://patch.msgid.link/DS7PR19MB7724E8A1AD36D1E623FA2A0AFCC62@DS7PR19MB7724.namprd19.prod.outlook.com Signed-off-by: Mark Brown --- include/sound/aw88399.h | 1 + sound/soc/codecs/aw88399-lib.c | 6 ++++++ 2 files changed, 7 insertions(+) (limited to 'include') diff --git a/include/sound/aw88399.h b/include/sound/aw88399.h index 3dbc37dc13d1..a1e0de6be8ea 100644 --- a/include/sound/aw88399.h +++ b/include/sound/aw88399.h @@ -609,6 +609,7 @@ int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value); int aw_dev_update_cali_re(struct aw_cali_desc *cali_desc); int aw88399_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name); void aw88399_dev_mute(struct aw_device *aw_dev, bool is_mute); +void aw88399_dev_set_channel(struct aw88399 *aw88399, int channel); void aw88399_hw_reset(struct aw88399 *aw88399); int aw88399_init(struct aw88399 *aw88399, struct i2c_client *i2c, struct regmap *regmap); extern const struct regmap_config aw88399_remap_config; diff --git a/sound/soc/codecs/aw88399-lib.c b/sound/soc/codecs/aw88399-lib.c index 094a37b66fb5..5c7982891def 100644 --- a/sound/soc/codecs/aw88399-lib.c +++ b/sound/soc/codecs/aw88399-lib.c @@ -1401,5 +1401,11 @@ int aw88399_init(struct aw88399 *aw88399, struct i2c_client *i2c, struct regmap } EXPORT_SYMBOL_GPL(aw88399_init); +void aw88399_dev_set_channel(struct aw88399 *aw88399, int channel) +{ + aw88399->aw_pa->channel = channel; +} +EXPORT_SYMBOL_GPL(aw88399_dev_set_channel); + MODULE_DESCRIPTION("AW88399 common device library"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 784e04110272590a34d7faad656232b7f9aeb1df Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Wed, 22 Jul 2026 16:46:55 +0530 Subject: ASoC: qcom: qdsp6: Remove unused Q6AFE_MAX_CLK_ID define Q6AFE_MAX_CLK_ID is not used anywhere. Remove the unused define. Signed-off-by: Prasad Kumpatla Reviewed-by: Dmitry Baryshkov Link: https://patch.msgid.link/20260722111655.3558096-1-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown --- include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h index 45850f2d4342..7b553a73bc92 100644 --- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h +++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h @@ -237,8 +237,6 @@ #define LPASS_HW_MACRO_VOTE 102 #define LPASS_HW_DCODEC_VOTE 103 -#define Q6AFE_MAX_CLK_ID 104 - #define LPASS_CLK_ATTRIBUTE_INVALID 0x0 #define LPASS_CLK_ATTRIBUTE_COUPLE_NO 0x1 #define LPASS_CLK_ATTRIBUTE_COUPLE_DIVIDEND 0x2 -- cgit v1.2.3 From 0f97c75d15c3869836cd58d8c3b89add2d9b68f5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 24 Jul 2026 16:34:29 -0700 Subject: ASoC: ti: omap-twl4030: drop support for platform data There are no users of omap_tw4030_pdata in the mainline kernel so remove support for it from the driver. Signed-off-by: Dmitry Torokhov Reviewed-by: Sebastian Reichel Acked-by: Jarkko Nikula Link: https://patch.msgid.link/20260724233432.31325-1-dmitry.torokhov@gmail.com Signed-off-by: Mark Brown --- include/linux/platform_data/omap-twl4030.h | 42 --------- sound/soc/ti/omap-twl4030.c | 134 ++++++++++------------------- 2 files changed, 44 insertions(+), 132 deletions(-) delete mode 100644 include/linux/platform_data/omap-twl4030.h (limited to 'include') diff --git a/include/linux/platform_data/omap-twl4030.h b/include/linux/platform_data/omap-twl4030.h deleted file mode 100644 index 7fcb55fe21c9..000000000000 --- a/include/linux/platform_data/omap-twl4030.h +++ /dev/null @@ -1,42 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/** - * omap-twl4030.h - ASoC machine driver for TI SoC based boards with twl4030 - * codec, header. - * - * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com - * All rights reserved. - * - * Author: Peter Ujfalusi - */ - -#ifndef _OMAP_TWL4030_H_ -#define _OMAP_TWL4030_H_ - -/* To select if only one channel is connected in a stereo port */ -#define OMAP_TWL4030_LEFT (1 << 0) -#define OMAP_TWL4030_RIGHT (1 << 1) - -struct omap_tw4030_pdata { - const char *card_name; - /* Voice port is connected to McBSP3 */ - bool voice_connected; - - /* The driver will parse the connection flags if this flag is set */ - bool custom_routing; - /* Flags to indicate connected audio ports. */ - u8 has_hs; - u8 has_hf; - u8 has_predriv; - u8 has_carkit; - bool has_ear; - - bool has_mainmic; - bool has_submic; - bool has_hsmic; - bool has_carkitmic; - bool has_digimic0; - bool has_digimic1; - u8 has_linein; -}; - -#endif /* _OMAP_TWL4030_H_ */ diff --git a/sound/soc/ti/omap-twl4030.c b/sound/soc/ti/omap-twl4030.c index 4d80f8a7a947..950879fc7275 100644 --- a/sound/soc/ti/omap-twl4030.c +++ b/sound/soc/ti/omap-twl4030.c @@ -17,7 +17,6 @@ */ #include -#include #include #include @@ -133,20 +132,12 @@ static struct snd_soc_jack_gpio hs_jack_gpios[] = { }, }; -static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm, - int connected, char *pin) -{ - if (!connected) - snd_soc_dapm_disable_pin(dapm, pin); -} - static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; - struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); - struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev); + struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card); - int ret = 0; + int ret; /* * This is a bit of a hack, but the GPIO is optional so we @@ -170,29 +161,7 @@ static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) return ret; } - /* - * NULL pdata means we booted with DT. In this case the routing is - * provided and the card is fully routed, no need to mark pins. - */ - if (!pdata || !pdata->custom_routing) - return ret; - - /* Disable not connected paths if not used */ - twl4030_disconnect_pin(dapm, pdata->has_ear, "Earpiece Spk"); - twl4030_disconnect_pin(dapm, pdata->has_hf, "Handsfree Spk"); - twl4030_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone"); - twl4030_disconnect_pin(dapm, pdata->has_predriv, "Ext Spk"); - twl4030_disconnect_pin(dapm, pdata->has_carkit, "Carkit Spk"); - - twl4030_disconnect_pin(dapm, pdata->has_mainmic, "Main Mic"); - twl4030_disconnect_pin(dapm, pdata->has_submic, "Sub Mic"); - twl4030_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic"); - twl4030_disconnect_pin(dapm, pdata->has_carkitmic, "Carkit Mic"); - twl4030_disconnect_pin(dapm, pdata->has_digimic0, "Digital0 Mic"); - twl4030_disconnect_pin(dapm, pdata->has_digimic1, "Digital1 Mic"); - twl4030_disconnect_pin(dapm, pdata->has_linein, "Line In"); - - return ret; + return 0; } /* Digital audio interface glue - connects codec <--> CPU */ @@ -237,11 +206,15 @@ static struct snd_soc_card omap_twl4030_card = { static int omap_twl4030_probe(struct platform_device *pdev) { - struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev); - struct device_node *node = pdev->dev.of_node; struct snd_soc_card *card = &omap_twl4030_card; + struct device_node *node, *dai_node; struct omap_twl4030 *priv; - int ret = 0; + struct property *prop; + int ret; + + node = pdev->dev.of_node; + if (!node) + return -ENODEV; card->dev = &pdev->dev; @@ -249,62 +222,43 @@ static int omap_twl4030_probe(struct platform_device *pdev) if (priv == NULL) return -ENOMEM; - if (node) { - struct device_node *dai_node; - struct property *prop; - - if (snd_soc_of_parse_card_name(card, "ti,model")) { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } - - dai_node = of_parse_phandle(node, "ti,mcbsp", 0); - if (!dai_node) { - dev_err(&pdev->dev, "McBSP node is not provided\n"); - return -EINVAL; - } - omap_twl4030_dai_links[0].cpus->dai_name = NULL; - omap_twl4030_dai_links[0].cpus->of_node = dai_node; - - omap_twl4030_dai_links[0].platforms->name = NULL; - omap_twl4030_dai_links[0].platforms->of_node = dai_node; - - dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0); - if (!dai_node) { - card->num_links = 1; - } else { - omap_twl4030_dai_links[1].cpus->dai_name = NULL; - omap_twl4030_dai_links[1].cpus->of_node = dai_node; - - omap_twl4030_dai_links[1].platforms->name = NULL; - omap_twl4030_dai_links[1].platforms->of_node = dai_node; - } - - /* Optional: audio routing can be provided */ - prop = of_find_property(node, "ti,audio-routing", NULL); - if (prop) { - ret = snd_soc_of_parse_audio_routing(card, - "ti,audio-routing"); - if (ret) - return ret; - - card->fully_routed = 1; - } - } else if (pdata) { - if (pdata->card_name) { - card->name = pdata->card_name; - } else { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } - - if (!pdata->voice_connected) - card->num_links = 1; - } else { - dev_err(&pdev->dev, "Missing pdata\n"); + if (snd_soc_of_parse_card_name(card, "ti,model")) { + dev_err(&pdev->dev, "Card name is not provided\n"); return -ENODEV; } + dai_node = of_parse_phandle(node, "ti,mcbsp", 0); + if (!dai_node) { + dev_err(&pdev->dev, "McBSP node is not provided\n"); + return -EINVAL; + } + omap_twl4030_dai_links[0].cpus->dai_name = NULL; + omap_twl4030_dai_links[0].cpus->of_node = dai_node; + + omap_twl4030_dai_links[0].platforms->name = NULL; + omap_twl4030_dai_links[0].platforms->of_node = dai_node; + + dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0); + if (!dai_node) { + card->num_links = 1; + } else { + omap_twl4030_dai_links[1].cpus->dai_name = NULL; + omap_twl4030_dai_links[1].cpus->of_node = dai_node; + + omap_twl4030_dai_links[1].platforms->name = NULL; + omap_twl4030_dai_links[1].platforms->of_node = dai_node; + } + + /* Optional: audio routing can be provided */ + prop = of_find_property(node, "ti,audio-routing", NULL); + if (prop) { + ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); + if (ret) + return ret; + + card->fully_routed = 1; + } + snd_soc_card_set_drvdata(card, priv); ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { -- cgit v1.2.3 From 34dfc478c963e035d0c2ce61bbe610c9c745b634 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 13 Jul 2026 10:55:09 -0700 Subject: ASoC: SOF: don't use "/**" for non-kernel-doc comments Modify these errant comments to use "/*" since they are not kernel-doc comments. Warning: ../include/sound/sof/header.h:182 This comment starts with '/**', but isn't a kernel-doc comment. * OOPS header architecture specific data. Warning: ../include/sound/sof/header.h:190 This comment starts with '/**', but isn't a kernel-doc comment. * OOPS header platform specific data. Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20260713175510.524728-1-rdunlap@infradead.org Signed-off-by: Mark Brown --- include/sound/sof/header.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/sof/header.h b/include/sound/sof/header.h index 4e406dc22f31..6c2c3a5fc90d 100644 --- a/include/sound/sof/header.h +++ b/include/sound/sof/header.h @@ -179,7 +179,7 @@ struct sof_ipc_compound_hdr { uint32_t count; /**< count of 0 means end of compound sequence */ } __packed; -/** +/* * OOPS header architecture specific data. */ struct sof_ipc_dsp_oops_arch_hdr { @@ -187,7 +187,7 @@ struct sof_ipc_dsp_oops_arch_hdr { uint32_t totalsize; /* Total size of oops message */ } __packed; -/** +/* * OOPS header platform specific data. */ struct sof_ipc_dsp_oops_plat_hdr { -- cgit v1.2.3 From 3906ea776a08d13bd5ef221b1d8112a49988c644 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 25 Jul 2026 18:05:11 -0700 Subject: ASoC: tlv320aic32x4: remove global header with platform data Commit 69d5b62c4bde ("ASoC: codec: tlv320aic32x4: Drop aic32x4_pdata usage") removed support for platform data, but left a global header file with #defines and platform data structure. Move the contents to the driver-private header. Signed-off-by: Dmitry Torokhov Link: https://patch.msgid.link/20260726010519.117805-1-dmitry.torokhov@gmail.com Signed-off-by: Mark Brown --- include/sound/tlv320aic32x4.h | 43 ---------------------------------------- sound/soc/codecs/tlv320aic32x4.c | 5 ++++- sound/soc/codecs/tlv320aic32x4.h | 27 +++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 44 deletions(-) delete mode 100644 include/sound/tlv320aic32x4.h (limited to 'include') diff --git a/include/sound/tlv320aic32x4.h b/include/sound/tlv320aic32x4.h deleted file mode 100644 index b779d671a995..000000000000 --- a/include/sound/tlv320aic32x4.h +++ /dev/null @@ -1,43 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * tlv320aic32x4.h -- TLV320AIC32X4 Soc Audio driver platform data - * - * Copyright 2011 Vista Silicon S.L. - * - * Author: Javier Martin - */ - -#ifndef _AIC32X4_PDATA_H -#define _AIC32X4_PDATA_H - -#define AIC32X4_PWR_MICBIAS_2075_LDOIN 0x00000001 -#define AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE 0x00000002 -#define AIC32X4_PWR_AIC32X4_LDO_ENABLE 0x00000004 -#define AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36 0x00000008 -#define AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED 0x00000010 - -#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K 0x00000001 -#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K 0x00000002 - -/* GPIO API */ -#define AIC32X4_MFPX_DEFAULT_VALUE 0xff - -#define AIC32X4_MFP1_DIN_DISABLED 0 -#define AIC32X4_MFP1_DIN_ENABLED 0x2 -#define AIC32X4_MFP1_GPIO_IN 0x4 - -#define AIC32X4_MFP2_GPIO_OUT_LOW 0x0 -#define AIC32X4_MFP2_GPIO_OUT_HIGH 0x1 - -#define AIC32X4_MFP_GPIO_ENABLED 0x4 - -#define AIC32X4_MFP5_GPIO_DISABLED 0x0 -#define AIC32X4_MFP5_GPIO_INPUT 0x8 -#define AIC32X4_MFP5_GPIO_OUTPUT 0xc -#define AIC32X4_MFP5_GPIO_OUT_LOW 0x0 -#define AIC32X4_MFP5_GPIO_OUT_HIGH 0x1 - -struct aic32x4_setup_data { - unsigned int gpio_func[5]; -}; -#endif diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index d85094557215..da582f2c940c 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -28,10 +28,13 @@ #include #include #include -#include #include "tlv320aic32x4.h" +struct aic32x4_setup_data { + unsigned int gpio_func[5]; +}; + struct aic32x4_priv { struct regmap *regmap; u32 power_cfg; diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h index f68a846ef61d..8eb9c6a4c99e 100644 --- a/sound/soc/codecs/tlv320aic32x4.h +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -234,4 +234,31 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name); #define AIC32X4_MAX_CODEC_CLKIN_FREQ 110000000 #define AIC32X4_MAX_PLL_CLKIN 20000000 +#define AIC32X4_PWR_MICBIAS_2075_LDOIN 0x00000001 +#define AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE 0x00000002 +#define AIC32X4_PWR_AIC32X4_LDO_ENABLE 0x00000004 +#define AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36 0x00000008 +#define AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED 0x00000010 + +#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K 0x00000001 +#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K 0x00000002 + +/* GPIO API */ +#define AIC32X4_MFPX_DEFAULT_VALUE 0xff + +#define AIC32X4_MFP1_DIN_DISABLED 0 +#define AIC32X4_MFP1_DIN_ENABLED 0x2 +#define AIC32X4_MFP1_GPIO_IN 0x4 + +#define AIC32X4_MFP2_GPIO_OUT_LOW 0x0 +#define AIC32X4_MFP2_GPIO_OUT_HIGH 0x1 + +#define AIC32X4_MFP_GPIO_ENABLED 0x4 + +#define AIC32X4_MFP5_GPIO_DISABLED 0x0 +#define AIC32X4_MFP5_GPIO_INPUT 0x8 +#define AIC32X4_MFP5_GPIO_OUTPUT 0xc +#define AIC32X4_MFP5_GPIO_OUT_LOW 0x0 +#define AIC32X4_MFP5_GPIO_OUT_HIGH 0x1 + #endif /* _TLV320AIC32X4_H */ -- cgit v1.2.3 From c24f4797d8e59a7697d0c1152ece30c6bc9c3fc9 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 29 Jul 2026 11:22:27 +0800 Subject: ASoC: SDCA: export sdca_find_entity_by_label() helper Export the sdca_find_entity_by_label() helper so that codec drivers can locate SDCA entities by their labels. Signed-off-by: Shuming Fan Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260729032227.3750770-1-shumingf@realtek.com Signed-off-by: Mark Brown --- include/sound/sdca_function.h | 2 ++ sound/soc/sdca/sdca_functions.c | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h index b1489178b0ef..fb931ae735a2 100644 --- a/include/sound/sdca_function.h +++ b/include/sound/sdca_function.h @@ -1469,5 +1469,7 @@ struct sdca_control_range *sdca_selector_find_range(struct device *dev, struct sdca_cluster *sdca_id_find_cluster(struct device *dev, struct sdca_function_data *function, const int id); +struct sdca_entity *sdca_find_entity_by_label(struct sdca_function_data *function, + const char *entity_label); #endif diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 77940bd6b33c..d9703927325d 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -1617,7 +1617,7 @@ static int find_sdca_entities(struct device *dev, struct sdw_slave *sdw, return 0; } -static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data *function, +struct sdca_entity *sdca_find_entity_by_label(struct sdca_function_data *function, const char *entity_label) { struct sdca_entity *entity = NULL; @@ -1646,6 +1646,7 @@ static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data * return NULL; } +EXPORT_SYMBOL_NS(sdca_find_entity_by_label, "SND_SOC_SDCA"); static struct sdca_entity *find_sdca_entity_by_id(struct sdca_function_data *function, const int id) @@ -1686,7 +1687,7 @@ static int find_sdca_entity_connection_iot(struct device *dev, return ret; } - clock_entity = find_sdca_entity_by_label(function, clock_label); + clock_entity = sdca_find_entity_by_label(function, clock_label); if (!clock_entity) { dev_err(dev, "%s: failed to find clock with label %s\n", entity->label, clock_label); @@ -1871,7 +1872,7 @@ static int find_sdca_entity_connection(struct device *dev, return ret; } - connected_entity = find_sdca_entity_by_label(function, connected_label); + connected_entity = sdca_find_entity_by_label(function, connected_label); if (!connected_entity) { dev_err(dev, "%s: failed to find entity with label %s\n", entity->label, connected_label); -- cgit v1.2.3 From 772e3409961f155d023855d02946ce6e8287593d Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Wed, 29 Jul 2026 11:22:37 +0800 Subject: ASoC: SDCA: export sdca_asoc_populate_rate_format() helper Export populate_rate_format() as sdca_asoc_populate_rate_format() so that it can be used by codec drivers. The codec driver could get rate and format information for the IT/OT entity. Signed-off-by: Shuming Fan Reviewed-by: Charles Keepax Link: https://patch.msgid.link/20260729032237.3750805-1-shumingf@realtek.com Signed-off-by: Mark Brown --- include/sound/sdca_asoc.h | 7 +++++++ sound/soc/sdca/sdca_asoc.c | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/sdca_asoc.h b/include/sound/sdca_asoc.h index ca35d5a44370..d3024c3b38b9 100644 --- a/include/sound/sdca_asoc.h +++ b/include/sound/sdca_asoc.h @@ -25,6 +25,8 @@ struct snd_soc_dai_driver; struct snd_soc_dai_ops; struct snd_soc_dapm_route; struct snd_soc_dapm_widget; +struct snd_soc_pcm_stream; +struct sdca_entity; /* convenient macro to handle the mono volume in 7.8 fixed format representation */ #define SDCA_SINGLE_Q78_TLV(xname, xreg, xmin, xmax, xstep, tlv_array) \ @@ -82,6 +84,11 @@ int sdca_asoc_populate_component(struct device *dev, struct snd_soc_dai_driver **dai_drv, int *num_dai_drv, const struct snd_soc_dai_ops *ops); +int sdca_asoc_populate_rate_format(struct device *dev, + struct sdca_function_data *function, + struct sdca_entity *entity, + struct snd_soc_pcm_stream *stream); + int sdca_asoc_set_constraints(struct device *dev, struct regmap *regmap, struct sdca_function_data *function, struct snd_pcm_substream *substream, diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index b4dedba719dc..9a6c0036b7be 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -1231,7 +1231,7 @@ static u64 width_find_mask(unsigned int bits) } } -static int populate_rate_format(struct device *dev, +int sdca_asoc_populate_rate_format(struct device *dev, struct sdca_function_data *function, struct sdca_entity *entity, struct snd_soc_pcm_stream *stream) @@ -1292,6 +1292,7 @@ static int populate_rate_format(struct device *dev, return 0; } +EXPORT_SYMBOL_NS(sdca_asoc_populate_rate_format, "SND_SOC_SDCA"); /** * sdca_asoc_populate_dais - fill in an array of DAI drivers for a Function @@ -1344,7 +1345,7 @@ int sdca_asoc_populate_dais(struct device *dev, struct sdca_function_data *funct stream->channels_min = 1; stream->channels_max = SDCA_MAX_CHANNEL_COUNT; - ret = populate_rate_format(dev, function, entity, stream); + ret = sdca_asoc_populate_rate_format(dev, function, entity, stream); if (ret) return ret; -- cgit v1.2.3 From c50ed4627e333934aaf0473a822169786c83dce5 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 30 Jul 2026 11:24:44 +0300 Subject: ASoC: SOF: ipc4: Add decoder for RESOURCE_EVENT notifications from firmware Decode and print out the content of currently supported RESOURCE_EVENT notifications from firmware along with the needed data structures and definitions. Signed-off-by: Peter Ujfalusi Reviewed-by: Bard Liao Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260730082444.4828-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/ipc4/header.h | 53 +++++++++++++++++++++++++- sound/soc/sof/ipc4.c | 84 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index 4554e5e8cab5..e747741f35c0 100644 --- a/include/sound/sof/ipc4/header.h +++ b/include/sound/sof/ipc4/header.h @@ -536,12 +536,63 @@ enum sof_ipc4_notification_type { SOF_IPC4_NOTIFY_TYPE_LAST, }; +enum sof_ipc4_resource_type { + SOF_IPC4_MODULE_INSTANCE, + SOF_IPC4_PIPELINE, + SOF_IPC4_GATEWAY, + SOF_IPC4_EDF_TASK, + SOF_IPC4_INVALID_RESOURCE_TYPE, +}; + +enum sof_ipc4_event_type { + /* Underrun detected by the Mixer */ + SOF_IPC4_MIXER_UNDERRUN_DETECTED = 1, + /* Error caught during data processing */ + SOF_IPC4_PROCESS_DATA_ERROR = 3, + /* Underrun detected by gateway. */ + SOF_IPC4_GATEWAY_UNDERRUN_DETECTED = 6, + /* Overrun detected by gateway */ + SOF_IPC4_GATEWAY_OVERRUN_DETECTED, +}; + +/** + * struct sof_ipc4_process_data_error_event_data - process data error event payload + * @error_code: Error code returned by data processing function + */ +struct sof_ipc4_process_data_error_event_data { + uint32_t error_code; +}; + +/** + * struct sof_ipc4_mixer_underrun_event_data - mixer underrun event payload + * @eos_flag: Indicates EndOfStream + * @data_mixed: Data processed by module (in bytes) + * @expected_data_mixed: Expected data to be processed (in bytes) + */ +struct sof_ipc4_mixer_underrun_event_data { + uint32_t eos_flag; + uint32_t data_mixed; + uint32_t expected_data_mixed; +}; + +/** + * union sof_ipc4_resource_event_data - resource event specific payload + * @dws: Raw event data payload as six dwords + * @process_data_error: SOF_IPC4_PROCESS_DATA_ERROR payload + * @mixer_underrun: SOF_IPC4_MIXER_UNDERRUN_DETECTED payload + */ +union sof_ipc4_resource_event_data { + uint32_t dws[6]; + struct sof_ipc4_process_data_error_event_data process_data_error; + struct sof_ipc4_mixer_underrun_event_data mixer_underrun; +}; + struct sof_ipc4_notify_resource_data { uint32_t resource_type; uint32_t resource_id; uint32_t event_type; uint32_t reserved; - uint32_t data[6]; + union sof_ipc4_resource_event_data data; } __packed __aligned(4); #define SOF_IPC4_DEBUG_DESCRIPTOR_SIZE 12 /* 3 x u32 */ diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index c9c6c0c52c62..e77f73390bd8 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -289,6 +289,89 @@ static void sof_ipc4_dump_payload(struct snd_sof_dev *sdev, 16, 4, ipc_data, size, false); } +static const char *sof_ipc4_resource_type_str(u32 type) +{ + switch (type) { + case SOF_IPC4_MODULE_INSTANCE: + return "resource: MODULE_INSTANCE"; + case SOF_IPC4_PIPELINE: + return "resource: PIPELINE"; + case SOF_IPC4_GATEWAY: + return "resource: GATEWAY"; + case SOF_IPC4_EDF_TASK: + return "resource: EDF_TASK"; + case SOF_IPC4_INVALID_RESOURCE_TYPE: + return "Resource is invalid"; + default: + return "Unknown resource type"; + } +} + +static const char *sof_ipc4_resource_event_type_str(u32 event_type) +{ + switch (event_type) { + case SOF_IPC4_MIXER_UNDERRUN_DETECTED: + return "event: MIXER_UNDERRUN_DETECTED"; + case SOF_IPC4_PROCESS_DATA_ERROR: + return "event: PROCESS_DATA_ERROR"; + case SOF_IPC4_GATEWAY_UNDERRUN_DETECTED: + return "event: GATEWAY_UNDERRUN_DETECTED"; + case SOF_IPC4_GATEWAY_OVERRUN_DETECTED: + return "event: GATEWAY_OVERRUN_DETECTED"; + default: + return "Unknown event type"; + } +} + +static void sof_ipc4_resource_event_handler(struct snd_sof_dev *sdev, + struct sof_ipc4_msg *ipc4_msg) +{ + struct sof_ipc4_notify_resource_data *data = ipc4_msg->data_ptr; + + /* Print event details */ + switch (data->event_type) { + case SOF_IPC4_MIXER_UNDERRUN_DETECTED: + dev_dbg(sdev->dev, "%s (%u): eos %u, mixed %u, expected %u\n", + sof_ipc4_resource_event_type_str(data->event_type), + data->event_type, data->data.mixer_underrun.eos_flag, + data->data.mixer_underrun.data_mixed, + data->data.mixer_underrun.expected_data_mixed); + break; + case SOF_IPC4_PROCESS_DATA_ERROR: + dev_dbg(sdev->dev, "%s (%u): error_code %#x\n", + sof_ipc4_resource_event_type_str(data->event_type), + data->event_type, data->data.process_data_error.error_code); + break; + case SOF_IPC4_GATEWAY_UNDERRUN_DETECTED: + case SOF_IPC4_GATEWAY_OVERRUN_DETECTED: + dev_dbg(sdev->dev, "%s (%u)\n", + sof_ipc4_resource_event_type_str(data->event_type), + data->event_type); + break; + default: + dev_dbg(sdev->dev, "%s (%u): raw dws %#x %#x %#x %#x %#x %#x\n", + sof_ipc4_resource_event_type_str(data->event_type), + data->event_type, + data->data.dws[0], data->data.dws[1], data->data.dws[2], + data->data.dws[3], data->data.dws[4], data->data.dws[5]); + break; + } + + /* Print resource details */ + if (data->resource_type == SOF_IPC4_MODULE_INSTANCE) { + u32 module_id = SOF_IPC4_MOD_ID_GET(data->resource_id); + u32 instance_id = SOF_IPC4_MOD_INSTANCE_GET(data->resource_id); + + dev_dbg(sdev->dev, "%s (%u), module_id %u, instance_id %u\n", + sof_ipc4_resource_type_str(data->resource_type), + data->resource_type, module_id, instance_id); + } else if (data->resource_type != SOF_IPC4_INVALID_RESOURCE_TYPE) { + dev_dbg(sdev->dev, "%s (%u), id %u\n", + sof_ipc4_resource_type_str(data->resource_type), + data->resource_type, data->resource_id); + } +} + static int sof_ipc4_get_reply(struct snd_sof_dev *sdev) { struct snd_sof_ipc_msg *msg = sdev->msg; @@ -734,6 +817,7 @@ static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev) break; case SOF_IPC4_NOTIFY_RESOURCE_EVENT: data_size = sizeof(struct sof_ipc4_notify_resource_data); + handler_func = sof_ipc4_resource_event_handler; break; case SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS: sof_ipc4_mtrace_update_pos(sdev, SOF_IPC4_LOG_CORE_GET(ipc4_msg->primary)); -- cgit v1.2.3 From 18d0619ced261e7960efd05125dce604d1d9ac89 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 30 Jul 2026 13:41:37 +0300 Subject: ASoC: SOF: ipc4-topology: Fix SOF_TKN_COMP_STACK_BYTES_REQUIREMENT id The was inconsistency with SOF_TKN_COMP_STACK_BYTES_REQUIREMENT and SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT token ids in the Linux driver code with SOF FW topology code. This commit fixes the Linux side to match tools/topology/topology2/include/common/tokens.conf Link: https://github.com/thesofproject/sof/blob/main/tools/topology/topology2/include/common/tokens.conf#L30 Signed-off-by: Jyri Sarha Reviewed-by: Liam Girdwood Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260730104141.14817-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/uapi/sound/sof/tokens.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index cc694a397987..d42adbef0478 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -111,8 +111,8 @@ #define SOF_TKN_COMP_SCHED_DOMAIN 418 #define SOF_TKN_COMP_DOMAIN_ID 419 -#define SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT 420 -#define SOF_TKN_COMP_STACK_BYTES_REQUIREMENT 421 +#define SOF_TKN_COMP_STACK_BYTES_REQUIREMENT 420 +#define SOF_TKN_COMP_HEAP_BYTES_REQUIREMENT 421 /* SSP */ #define SOF_TKN_INTEL_SSP_CLKS_CONTROL 500 -- cgit v1.2.3 From 52db046c388d32f8b99caa4312ae3712c1cce3e7 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 30 Jul 2026 13:41:38 +0300 Subject: ASoC: SOF: ipc4: Add SOF_IPC4_GLB_CREATE_PIPELINE payload macros and structs Adds SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY macros to set extension bit in SOF_IPC4_GLB_CREATE_PIPELINE indicating presence of the payload, and all necessary macros and structs to create the payload. Signed-off-by: Jyri Sarha Reviewed-by: Liam Girdwood Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260730104141.14817-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/ipc4/header.h | 76 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index 4554e5e8cab5..6fbf62c4075c 100644 --- a/include/sound/sof/ipc4/header.h +++ b/include/sound/sof/ipc4/header.h @@ -187,6 +187,10 @@ enum sof_ipc4_pipeline_state { #define SOF_IPC4_GLB_PIPE_EXT_CORE_ID_MASK GENMASK(23, 20) #define SOF_IPC4_GLB_PIPE_EXT_CORE_ID(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_CORE_ID_SHIFT) +#define SOF_IPC4_GLB_PIPE_PAYLOAD_SHIFT 29 +#define SOF_IPC4_GLB_PIPE_PAYLOAD_MASK BIT(29) +#define SOF_IPC4_GLB_PIPE_PAYLOAD(x) ((x) << SOF_IPC4_GLB_PIPE_PAYLOAD_SHIFT) + /* pipeline set state ipc msg */ #define SOF_IPC4_GLB_PIPE_STATE_ID_SHIFT 16 #define SOF_IPC4_GLB_PIPE_STATE_ID_MASK GENMASK(23, 16) @@ -654,13 +658,83 @@ enum sof_ipc4_mod_init_ext_obj_id { SOF_IPC4_MOD_INIT_DATA_ID_MAX = SOF_IPC4_MOD_INIT_DATA_ID_DP_DATA, }; -/* DP module memory configuration data object for ext_init object array */ +/* DP module memory configuration data object for object array */ struct sof_ipc4_mod_init_ext_dp_memory_data { u32 domain_id; /* userspace domain ID */ u32 stack_bytes; /* stack size in bytes, 0 means default size */ u32 heap_bytes; /* stack size in bytes, 0 means default size */ } __packed __aligned(4); +/* + * This set of macros are very similar to the set above, but these are + * for building payload to SOF_IPC4_GLB_CREATE_PIPELINE message. + * + * Macros for creating struct sof_ipc4_glb_pipe_payload payload with + * its associated data. struct sof_ipc4_glb_pipe_payload should be the + * first piece of payload following SOF_IPC4_GLB_CREATE_PIPELINE msg, + * and its existence is indicated with SOF_IPC4_GLB_PIPE_PAYLOAD bit. + * + * The macros below apply to sof_ipc4_glb_pipe_payload.word0 + */ +#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_SHIFT 0 +#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_MASK GENMASK(23, 0) +#define SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS(x) ((x) << SOF_IPC4_GLB_PIPE_PAYLOAD_WORDS_SHIFT) + +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_SHIFT 24 +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_MASK BIT(24) +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_SHIFT) + +struct sof_ipc4_glb_pipe_payload { + u32 word0; + u32 rsvd1; + u32 rsvd2; +} __packed __aligned(4); + +/* + * SOF_IPC4_GLB_CREATE_PIPELINE payload may be followed by arbitrary + * number of object array objects. SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY-bit + * indicates that an array object follows struct + * sof_ipc4_glb_pipe_payload. + * + * The object header's SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST-bit in struct + * sof_ipc4_glb_pipe_ext_object indicates if the array is continued + * with another object. The header has also fields to identify the + * object, SOF_IPC4_GLB_PIPE_EXT_OBJ_ID, and to indicate the object's + * size in 32-bit words, SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS, not + * including the header itself. + * + * The macros below apply to sof_ipc4_glb_pipe_ext_object.header + */ +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_SHIFT 0 +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_MASK BIT(0) +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_LAST_SHIFT) + +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_SHIFT 1 +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_MASK GENMASK(15, 1) +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_ID(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_ID_SHIFT) + +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_SHIFT 16 +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_MASK GENMASK(31, 16) +#define SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS(x) ((x) << SOF_IPC4_GLB_PIPE_EXT_OBJ_WORDS_SHIFT) + +struct sof_ipc4_glb_pipe_ext_object { + u32 header; + u32 data[]; +} __packed __aligned(4); + +enum sof_ipc4_glb_pipe_ext_obj_id { + SOF_IPC4_GLB_PIPE_DATA_ID_INVALID = 0, + SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA, + SOF_IPC4_GLB_PIPE_DATA_ID_MAX = SOF_IPC4_GLB_PIPE_DATA_ID_MEM_DATA, +}; + +/* Pipeline memory configuration data object for ext_init object array */ +struct sof_ipc4_glb_pipe_ext_obj_memory_data { + u32 domain_id; /* userspace domain ID */ + u32 stack_bytes; /* stack size in bytes */ + u32 heap_bytes; /* heap size in bytes */ +} __packed __aligned(4); + /** @}*/ #endif -- cgit v1.2.3 From e5b0daa6f9744967c04645945c3a5efbc43f3c92 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 30 Jul 2026 13:41:40 +0300 Subject: ASoC: SOF: ipc4-topology: Fix sof_ipc4_mod_init_ext_dp_memory_data comments Fix a copy-paste error in struct sof_ipc4_mod_init_ext_dp_memory_data datamember comments. And while at it, drop the overly specific notes on the datamember values. The values are coming from topology and and what to do with them is decided in SOF FW. Its a bad idea to try to document their meaning in detail here. The Linux driver is only passing the values. Signed-off-by: Jyri Sarha Reviewed-by: Liam Girdwood Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20260730104141.14817-6-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/ipc4/header.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index 6fbf62c4075c..b49a74007bd7 100644 --- a/include/sound/sof/ipc4/header.h +++ b/include/sound/sof/ipc4/header.h @@ -660,9 +660,9 @@ enum sof_ipc4_mod_init_ext_obj_id { /* DP module memory configuration data object for object array */ struct sof_ipc4_mod_init_ext_dp_memory_data { - u32 domain_id; /* userspace domain ID */ - u32 stack_bytes; /* stack size in bytes, 0 means default size */ - u32 heap_bytes; /* stack size in bytes, 0 means default size */ + u32 domain_id; /* userspace domain ID */ + u32 stack_bytes; /* required stack size in bytes */ + u32 heap_bytes; /* required heap size in bytes */ } __packed __aligned(4); /* @@ -732,7 +732,7 @@ enum sof_ipc4_glb_pipe_ext_obj_id { struct sof_ipc4_glb_pipe_ext_obj_memory_data { u32 domain_id; /* userspace domain ID */ u32 stack_bytes; /* stack size in bytes */ - u32 heap_bytes; /* heap size in bytes */ + u32 heap_bytes; /* heap size in bytes */ } __packed __aligned(4); /** @}*/ -- cgit v1.2.3 From f6970d8535a95c137f05e9ca825072de3b217b88 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 30 Jul 2026 14:06:02 +0100 Subject: ASoC: SDCA: Add missing stub for sdca_fdl_free_state() There should be a stub for sdca_fdl_free_state() for the case FDL support isn't built into the kernel. Add the missing stub. Fixes: 0880082c27b6 ("ASoC: SDCA: Remove devm from primary IRQ cleanup") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202607291304.FE3mOcJF-lkp@intel.com/ Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260730130602.3747053-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_fdl.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/sound/sdca_fdl.h b/include/sound/sdca_fdl.h index fbaf4b384c8a..979559e9ee63 100644 --- a/include/sound/sdca_fdl.h +++ b/include/sound/sdca_fdl.h @@ -81,6 +81,10 @@ static inline int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt) return 0; } +static inline void sdca_fdl_free_state(struct sdca_interrupt *interrupt) +{ +} + static inline int sdca_fdl_process(struct sdca_interrupt *interrupt) { return 0; -- cgit v1.2.3 From 15488685319379212084c0513661c7dfbc239636 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 30 Jul 2026 15:51:29 +0300 Subject: ASoC: SOF: Intel: hda: Remove unused hda_bus_ml_put_all() The helper became unused after probe no longer drops all non-alt links, so remove the dead API and implementation. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Bard Liao Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260730125130.29887-4-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/hda-mlink.h | 2 -- sound/soc/sof/intel/hda-mlink.c | 13 ------------- 2 files changed, 15 deletions(-) (limited to 'include') diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index fed69998c93f..d9789b048c61 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -49,7 +49,6 @@ int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num); int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, int channel_mask, int stream_id, int dir); -void hda_bus_ml_put_all(struct hdac_bus *bus); void hda_bus_ml_reset_losidv(struct hdac_bus *bus); int hda_bus_ml_resume(struct hdac_bus *bus); int hda_bus_ml_suspend(struct hdac_bus *bus); @@ -169,7 +168,6 @@ hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, return 0; } -static inline void hda_bus_ml_put_all(struct hdac_bus *bus) { } static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; } diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index ca8551befdb5..e0107cbd06e6 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -884,19 +884,6 @@ int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y, return 0; } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, "SND_SOC_SOF_HDA_MLINK"); -void hda_bus_ml_put_all(struct hdac_bus *bus) -{ - struct hdac_ext_link *hlink; - - list_for_each_entry(hlink, &bus->hlink_list, list) { - struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); - - if (!h2link->alt) - snd_hdac_ext_bus_link_put(bus, hlink); - } -} -EXPORT_SYMBOL_NS(hda_bus_ml_put_all, "SND_SOC_SOF_HDA_MLINK"); - void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { struct hdac_ext_link *hlink; -- cgit v1.2.3 From 34d466aaa0d533f082b35629f8fd91cb8c260296 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 30 Jul 2026 15:51:30 +0300 Subject: ASoC: SOF: Intel: hda: Avoid ACE2+ link DMA stream allocation hazards On ACE2+ platforms the link DMA stream allocator must avoid two hardware errata in mlink-capable systems: - Concurrent (cross-direction) hazard: when SoundWire shares a physical link DMA stream index with HDaudio, iDisp or UAOL across the two directions, the LLP and timestamp values for the affected stream are wrong. SSP and DMIC are not affected because every DMA request from those links carries one sample block. - Sequential (playback only) hazard: once a HDaudio or iDisp link has used a playback stream index, that index cannot drive any non HDA/iDisp link in the same direction until the next controller reset (CRST#). Track the active link type per direction in two masks (one for SoundWire, one for HDA/iDisp/UAOL) and the persistent set of playback stream indices touched by HDA/iDisp in a third mask. The link DMA allocator skips streams that would violate either rule. Streams are released from the active masks when the stream is released; all masks are cleared in hda_dsp_ctrl_init_chip() because the CRST# performed there clears the hardware state as well. A new helper hda_bus_ml_link_get_type() returns the link type from the existing extended link descriptor so the SOF allocator can tell SoundWire, HDA/iDisp and UAOL apart without duplicating the parsing. The implementation is generic. On platforms older than ACE2 every link is reported as HDA, only the sequential mask is ever set and it has no effect because no other link types are present, so behavior is unchanged. Signed-off-by: Peter Ujfalusi Reviewed-by: Kai Vehmanen Reviewed-by: Bard Liao Reviewed-by: Liam Girdwood Link: https://patch.msgid.link/20260730125130.29887-5-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- include/sound/hda-mlink.h | 21 +++++++++++ sound/soc/sof/intel/hda-ctrl.c | 11 ++++++ sound/soc/sof/intel/hda-dai-ops.c | 76 +++++++++++++++++++++++++++++++++++---- sound/soc/sof/intel/hda-dai.c | 2 +- sound/soc/sof/intel/hda-mlink.c | 18 ++++++++++ sound/soc/sof/intel/hda.h | 26 +++++++++++++- 6 files changed, 146 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/sound/hda-mlink.h b/include/sound/hda-mlink.h index d9789b048c61..ba35f03576b9 100644 --- a/include/sound/hda-mlink.h +++ b/include/sound/hda-mlink.h @@ -9,6 +9,22 @@ struct hdac_bus; struct hdac_ext_link; +/** + * enum hda_bus_ml_link_type - mlink link type, used by SOF link DMA + * allocator constraints (see struct sof_intel_hda_dev). + * + * @HDA_BUS_ML_LINK_HDA: non-alt link, i.e. HDA codec or iDisp + * @HDA_BUS_ML_LINK_SDW: alt link, SoundWire + * @HDA_BUS_ML_LINK_UAOL: alt link, USB Audio Offload + * @HDA_BUS_ML_LINK_OTHER: alt link, SSP or DMIC + */ +enum hda_bus_ml_link_type { + HDA_BUS_ML_LINK_HDA, + HDA_BUS_ML_LINK_SDW, + HDA_BUS_ML_LINK_UAOL, + HDA_BUS_ML_LINK_OTHER, +}; + #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK) int hda_bus_ml_init(struct hdac_bus *bus); @@ -53,6 +69,8 @@ void hda_bus_ml_reset_losidv(struct hdac_bus *bus); int hda_bus_ml_resume(struct hdac_bus *bus); int hda_bus_ml_suspend(struct hdac_bus *bus); +enum hda_bus_ml_link_type hda_bus_ml_link_get_type(struct hdac_ext_link *hlink); + struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus); struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus); struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus); @@ -172,6 +190,9 @@ static inline void hda_bus_ml_reset_losidv(struct hdac_bus *bus) { } static inline int hda_bus_ml_resume(struct hdac_bus *bus) { return 0; } static inline int hda_bus_ml_suspend(struct hdac_bus *bus) { return 0; } +static inline enum hda_bus_ml_link_type +hda_bus_ml_link_get_type(struct hdac_ext_link *hlink) { return HDA_BUS_ML_LINK_HDA; } + static inline struct hdac_ext_link * hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus) { return NULL; } diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index a9ead78d3fcb..aeb34310eebd 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -186,6 +186,7 @@ EXPORT_SYMBOL_NS(hda_dsp_ctrl_clock_power_gating, "SND_SOC_SOF_INTEL_HDA_COMMON" int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) { struct hdac_bus *bus = sof_to_bus(sdev); + struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); struct hdac_stream *stream; int sd_offset, ret = 0; u32 gctl; @@ -193,6 +194,16 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool detect_codec) if (bus->chip_init) return 0; + /* + * The controller reset clears the ACE2+ link DMA stream allocation + * constraints; reset the masks to reflect this. + */ + memset(sof_hda->link_dma_active_sdw_mask, 0, + sizeof(sof_hda->link_dma_active_sdw_mask)); + memset(sof_hda->link_dma_active_multi_mask, 0, + sizeof(sof_hda->link_dma_active_multi_mask)); + sof_hda->link_dma_out_hda_used_mask = 0; + hda_codec_set_codec_wakeup(sdev, true); hda_dsp_ctrl_misc_clock_gating(sdev, false); diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index b2c559559962..f0be42048db3 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -20,7 +20,7 @@ /* These ops are only applicable for the HDA DAI's in their current form */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) /* - * This function checks if the host dma channel corresponding + * This function checks if the host DMA stream corresponding * to the link DMA stream_tag argument is assigned to one * of the FEs connected to the BE DAI. */ @@ -42,23 +42,53 @@ static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd, } static struct hdac_ext_stream * -hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream) +hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, + enum hda_bus_ml_link_type link_type) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); struct sof_intel_hda_stream *hda_stream; const struct sof_intel_dsp_desc *chip; struct snd_sof_dev *sdev; struct hdac_ext_stream *res = NULL; struct hdac_stream *hstream = NULL; - int stream_dir = substream->stream; + bool is_multi = link_type == HDA_BUS_ML_LINK_HDA || link_type == HDA_BUS_ML_LINK_UAOL; + bool is_play = stream_dir == SNDRV_PCM_STREAM_PLAYBACK; + bool is_sdw = link_type == HDA_BUS_ML_LINK_SDW; + bool is_hda = link_type == HDA_BUS_ML_LINK_HDA; + u32 concur_block_mask = 0; + u32 seq_block_mask = 0; + unsigned int stream_idx; if (!bus->ppcap) { dev_err(bus->dev, "stream type not supported\n"); return NULL; } + /* + * On ACE2+ the link DMA stream allocator must avoid two HW errata, + * see the comment on struct sof_intel_hda_dev. + * + * - Concurrent cross-direction: SoundWire conflicts with HDA, iDisp + * and UAOL on the same physical stream index; SSP and DMIC are safe. + * - Sequential playback: a stream index previously used by an HDA/iDisp + * link cannot drive any non-HDA/iDisp link in the same direction + * until the next controller reset. + * + * The masks are protected by bus->reg_lock; sample them inside the + * lock together with the stream walk to keep the decision atomic + * with concurrent allocations and releases. + */ guard(spinlock_irq)(&bus->reg_lock); + + if (is_sdw) + concur_block_mask = sof_hda->link_dma_active_multi_mask[!stream_dir]; + else if (is_multi) + concur_block_mask = sof_hda->link_dma_active_sdw_mask[!stream_dir]; + if (is_play && !is_hda) + seq_block_mask = sof_hda->link_dma_out_hda_used_mask; + list_for_each_entry(hstream, &bus->stream_list, list) { struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); @@ -69,6 +99,12 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream sdev = hda_stream->sdev; chip = get_chip_info(sdev->pdata); + stream_idx = hstream->stream_tag - 1; + + /* skip streams blocked by the ACE2+ allocator constraints */ + if ((concur_block_mask | seq_block_mask) & BIT(stream_idx)) + continue; + /* check if link is available */ if (!hext_stream->link_locked) { /* @@ -95,7 +131,7 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream /* * This must be a hostless stream. - * So reserve the host DMA channel. + * So reserve the host DMA stream. */ hda_stream->host_reserved = 1; break; @@ -109,6 +145,16 @@ hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream res->link_locked = 1; res->link_substream = substream; + + stream_idx = res->hstream.stream_tag - 1; + if (is_sdw) + sof_hda->link_dma_active_sdw_mask[stream_dir] |= BIT(stream_idx); + else if (is_multi) + sof_hda->link_dma_active_multi_mask[stream_dir] |= BIT(stream_idx); + + /* persistent OUT HDA/iDisp shadow, cleared only on CRST# */ + if (is_hda && is_play) + sof_hda->link_dma_out_hda_used_mask |= BIT(stream_idx); } return res; @@ -143,11 +189,13 @@ static struct hdac_ext_stream *hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, - struct snd_pcm_substream *substream) + struct snd_pcm_substream *substream, + struct hdac_ext_link *hlink) { struct hdac_ext_stream *hext_stream; + enum hda_bus_ml_link_type link_type = hda_bus_ml_link_get_type(hlink); - hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream); + hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream, link_type); if (!hext_stream) return NULL; @@ -160,6 +208,22 @@ static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai struct snd_pcm_substream *substream) { struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream); + struct sof_intel_hda_dev *sof_hda = sdev->pdata->hw_pdata; + struct hdac_bus *bus = sof_to_bus(sdev); + int dir = substream->stream; + unsigned int stream_idx = hext_stream->hstream.stream_tag - 1; + + /* + * Drop the stream index from the per-direction active concurrency masks. + * The two masks are mutually exclusive for a given stream/direction + * (and a stream of the SSP/DMIC kind appears in neither), so a blind + * clear of both is safe and lets us avoid having to remember the + * link type at allocation time. + */ + scoped_guard(spinlock_irq, &bus->reg_lock) { + sof_hda->link_dma_active_sdw_mask[dir] &= ~BIT(stream_idx); + sof_hda->link_dma_active_multi_mask[dir] &= ~BIT(stream_idx); + } snd_soc_dai_set_dma_data(cpu_dai, substream, NULL); snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK); diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 15faedeec16d..bb44d4f8a4da 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -188,7 +188,7 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream, if (!hext_stream) { if (ops->assign_hext_stream) - hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream); + hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream, hlink); } if (!hext_stream) diff --git a/sound/soc/sof/intel/hda-mlink.c b/sound/soc/sof/intel/hda-mlink.c index e0107cbd06e6..6f02fb5b70ce 100644 --- a/sound/soc/sof/intel/hda-mlink.c +++ b/sound/soc/sof/intel/hda-mlink.c @@ -894,6 +894,24 @@ void hda_bus_ml_reset_losidv(struct hdac_bus *bus) } EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, "SND_SOC_SOF_HDA_MLINK"); +enum hda_bus_ml_link_type hda_bus_ml_link_get_type(struct hdac_ext_link *hlink) +{ + struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink); + + if (!h2link->alt) + return HDA_BUS_ML_LINK_HDA; + + switch (h2link->elid) { + case AZX_REG_ML_LEPTR_ID_SDW: + return HDA_BUS_ML_LINK_SDW; + case AZX_REG_ML_LEPTR_ID_INTEL_UAOL: + return HDA_BUS_ML_LINK_UAOL; + default: + return HDA_BUS_ML_LINK_OTHER; + } +} +EXPORT_SYMBOL_NS(hda_bus_ml_link_get_type, "SND_SOC_SOF_HDA_MLINK"); + int hda_bus_ml_resume(struct hdac_bus *bus) { struct hdac_ext_link *hlink; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 799e49539b4a..1609589929a1 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -523,6 +523,29 @@ struct sof_intel_hda_dev { /* the maximum number of streams (playback + capture) supported */ u32 stream_max; + /* + * ACE2+ link DMA stream allocation constraints (stream index = + * stream_tag - 1, shared between input and output directions). All + * masks are cleared by hda_dsp_ctrl_init_chip() on controller reset + * (CRST#). + * + * - Concurrent (cross-direction) constraint: a SoundWire stream and + * a HDA/iDisp/UAOL stream cannot share a physical stream index + * across directions, the resulting LLP/timestamp values are wrong. + * link_dma_active_sdw_mask and link_dma_active_multi_mask + * (indexed by SNDRV_PCM_STREAM_*) track currently allocated + * streams per direction in each of the conflicting groups; SSP + * and DMIC do not participate. Bits are cleared on stream release. + * + * - Sequential (playback only) constraint: once a HDA/iDisp link + * has used a playback stream index, that index cannot drive a + * non-HDA/iDisp link in the same direction until the next CRST#. + * link_dma_out_hda_used_mask records this. + */ + u32 link_dma_active_sdw_mask[SNDRV_PCM_STREAM_LAST + 1]; + u32 link_dma_active_multi_mask[SNDRV_PCM_STREAM_LAST + 1]; + u32 link_dma_out_hda_used_mask; + /* PM related */ bool l1_disabled;/* is DMI link L1 disabled? */ @@ -1031,7 +1054,8 @@ struct hda_dai_widget_dma_ops { struct snd_pcm_substream *substream); struct hdac_ext_stream *(*assign_hext_stream)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, - struct snd_pcm_substream *substream); + struct snd_pcm_substream *substream, + struct hdac_ext_link *hlink); void (*release_hext_stream)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream); void (*setup_hext_stream)(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream, -- cgit v1.2.3 From cdba62ff7413330451f0f65345166431a5d6e187 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Fri, 24 Jul 2026 19:47:04 +0530 Subject: ASoC: dt-bindings: qcom: add LPASS LPR vote clock ID Add a new clock ID, LPASS_HW_LPR_VOTE, to represent the LPASS low-power resource (LPR) vote through the PRM interface. The LPASS PRM supports a resource voting mechanism to control low-power states via PARAM_ID_RSC_CPU_LPR. Exposing this as a q6prm clock ID allows clients to request the LPR vote using the existing qcom,q6prm clock provider interface. This functionality is required on newer platforms (e.g. Hawi) where LPASS clients need to explicitly manage LPR resource voting via PRM. Acked-by: Krzysztof Kozlowski Signed-off-by: Prasad Kumpatla Acked-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260724141708.2212057-2-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown --- include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h index 7b553a73bc92..8e04106d48be 100644 --- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h +++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h @@ -236,6 +236,7 @@ #define LPASS_HW_AVTIMER_VOTE 101 #define LPASS_HW_MACRO_VOTE 102 #define LPASS_HW_DCODEC_VOTE 103 +#define LPASS_HW_LPR_VOTE 104 #define LPASS_CLK_ATTRIBUTE_INVALID 0x0 #define LPASS_CLK_ATTRIBUTE_COUPLE_NO 0x1 -- cgit v1.2.3 From 96591d7030a68ecdbb1be793d4b067d2087e0290 Mon Sep 17 00:00:00 2001 From: Richard Acayan Date: Thu, 30 Jul 2026 13:43:40 -0400 Subject: ASoC: dt-bindings: qcom: q6dsp: add support for lpi mi2s ports 5-6 There are 7 internal MI2S ports per direction found on devices with the internal sound card for Snapdragon 660. This is similar to the LPI MI2S ports, and the LPI MI2S bindings can be reused for internal MI2S. Extend the bindings for LPI MI2S ports to accommodate the internal MI2S ports. Signed-off-by: Richard Acayan Reviewed-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260730174353.108023-3-mailingradian@gmail.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml | 4 ++-- include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml b/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml index 2b27d6c8f58f..3b03e2acd67e 100644 --- a/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml @@ -127,7 +127,7 @@ patternProperties: contains: # MI2S DAI ID range PRIMARY_MI2S_RX - QUATERNARY_MI2S_TX and # QUINARY_MI2S_RX - QUINARY_MI2S_TX and - # LPI_MI2S_RX_0 - SENARY_MI2S_TX + # LPI_MI2S_RX_0 - LPI_MI2S_TX_6 items: oneOf: - minimum: 16 @@ -135,7 +135,7 @@ patternProperties: - minimum: 127 maximum: 128 - minimum: 137 - maximum: 148 + maximum: 152 then: required: - qcom,sd-lines diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h index 7b553a73bc92..31b9ef65f903 100644 --- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h +++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h @@ -152,6 +152,10 @@ #define LPI_MI2S_TX_4 146 #define SENARY_MI2S_RX 147 #define SENARY_MI2S_TX 148 +#define LPI_MI2S_RX_5 149 +#define LPI_MI2S_TX_5 150 +#define LPI_MI2S_RX_6 151 +#define LPI_MI2S_TX_6 152 #define LPASS_CLK_ID_PRI_MI2S_IBIT 1 #define LPASS_CLK_ID_PRI_MI2S_EBIT 2 -- cgit v1.2.3