[PATCH v3 03/10] ASoC: mediatek: mt8196: support audio clock control
Chen-Yu Tsai
wenst at chromium.org
Thu May 15 01:50:33 PDT 2025
On Thu, May 15, 2025 at 4:40 PM AngeloGioacchino Del Regno
<angelogioacchino.delregno at collabora.com> wrote:
>
> Il 14/05/25 10:11, Darren.Ye ha scritto:
> > From: Darren Ye <darren.ye at mediatek.com>
> >
> > Add audio clock wrapper and audio tuner control.
> >
> > Signed-off-by: Darren Ye <darren.ye at mediatek.com>
> > ---
> > sound/soc/mediatek/mt8196/mt8196-afe-clk.c | 723 ++++++++++++++++++
> > sound/soc/mediatek/mt8196/mt8196-afe-clk.h | 142 ++++
> > sound/soc/mediatek/mt8196/mt8196-audsys-clk.c | 252 ++++++
> > sound/soc/mediatek/mt8196/mt8196-audsys-clk.h | 14 +
> > .../soc/mediatek/mt8196/mt8196-audsys-clkid.h | 78 ++
> > 5 files changed, 1209 insertions(+)
> > create mode 100644 sound/soc/mediatek/mt8196/mt8196-afe-clk.c
> > create mode 100644 sound/soc/mediatek/mt8196/mt8196-afe-clk.h
> > create mode 100644 sound/soc/mediatek/mt8196/mt8196-audsys-clk.c
> > create mode 100644 sound/soc/mediatek/mt8196/mt8196-audsys-clk.h
> > create mode 100644 sound/soc/mediatek/mt8196/mt8196-audsys-clkid.h
> >
> > diff --git a/sound/soc/mediatek/mt8196/mt8196-afe-clk.c b/sound/soc/mediatek/mt8196/mt8196-afe-clk.c
> > new file mode 100644
> > index 000000000000..83b5ee9d30ef
> > --- /dev/null
> > +++ b/sound/soc/mediatek/mt8196/mt8196-afe-clk.c
> > @@ -0,0 +1,723 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * mt8196-afe-clk.c -- Mediatek 8196 afe clock ctrl
>
> mt8196-afe-clk.c - MediaTek MT8196 AFE Clock Control
>
> > + *
> > + * Copyright (c) 2024 MediaTek Inc.
> > + * Author: Darren Ye <darren.ye at mediatek.com>
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/regmap.h>
> > +#include <linux/mfd/syscon.h>
> > +#include "mt8196-afe-common.h"
> > +#include "mt8196-audsys-clk.h"
> > +#include "mt8196-afe-clk.h"
> > +
>
> ..snip..
>
> > +
> > +static int mt8196_afe_disable_apll(struct mtk_base_afe *afe)
> > +{
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + int ret = 0;
> > +
> > + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
> > + if (ret)
> > + return ret;
> > +
> > + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1]);
> > + if (ret)
> > + goto clk_ck_mux_aud1_err;
> > +
> > + ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1],
> > + afe_priv->clk[MT8196_CLK_TOP_CLK26M]);
> > + if (ret)
> > + goto clk_ck_mux_aud1_parent_err;
> > +
> > + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2]);
> > + if (ret)
> > + goto clk_ck_mux_aud2_err;
> > +
> > + ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2],
> > + afe_priv->clk[MT8196_CLK_TOP_CLK26M]);
> > + if (ret)
> > + goto clk_ck_mux_aud2_parent_err;
> > +
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1]);
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2]);
> > + mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H],
> > + afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
> > + return 0;
> > +
> > +clk_ck_mux_aud2_parent_err:
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_2]);
> > +clk_ck_mux_aud2_err:
> > + mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1],
> > + afe_priv->clk[MT8196_CLK_TOP_APLL1_CK]);
> > +clk_ck_mux_aud1_parent_err:
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_MUX_AUD_1]);
> > +clk_ck_mux_aud1_err:
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
> > +
> > + return ret;
> > +}
> > +
> > +static void mt8196_afe_apll_init(struct mtk_base_afe *afe)
> > +{
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > +
>
> if (!afe_priv->vlp_clk) {
> dev_warn...
> return;
> }
>
> regmap_write......
>
> > + if (afe_priv->vlp_ck) {
> > + regmap_write(afe_priv->vlp_ck, VLP_APLL1_TUNER_CON0, VLP_APLL1_TUNER_CON0_VALUE);
> > + regmap_write(afe_priv->vlp_ck, VLP_APLL2_TUNER_CON0, VLP_APLL2_TUNER_CON0_VALUE);
> > + } else {
> > + dev_warn(afe->dev, "vlp_ck regmap is null ptr\n");
> > + }
> > +}
> > +
> > +int mt8196_apll1_enable(struct mtk_base_afe *afe)
> > +{
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + int ret;
> > +
> > + /* setting for APLL */
> > + apll1_mux_setting(afe, true);
> > +
> > + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_AFE_APLL1]);
> > + if (ret)
> > + goto ERR_CLK_APLL1;
> > +
> > + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_AFE_APLL_TUNER1]);
> > + if (ret)
> > + goto ERR_CLK_APLL1_TUNER;
> > +
> > + /* sel 44.1kHz:1, apll_div:7, upper bound:3 */
> > + regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG,
> > + XTAL_EN_128FS_SEL_MASK_SFT | APLL_DIV_MASK_SFT | UPPER_BOUND_MASK_SFT,
> > + (0x1 << XTAL_EN_128FS_SEL_SFT) | (7 << APLL_DIV_SFT) |
> > + (3 << UPPER_BOUND_SFT));
> > +
> > + /* apll1 freq tuner enable */
> > + regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG,
> > + FREQ_TUNER_EN_MASK_SFT,
> > + 0x1 << FREQ_TUNER_EN_SFT);
> > +
> > + /* audio apll1 on */
> > + mt8196_afe_enable_top_cg(afe, MT8196_AUDIO_APLL1_EN_ON);
> > +
> > + return 0;
> > +
> > +ERR_CLK_APLL1_TUNER:
>
> lower case for labels please
>
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_AFE_APLL_TUNER1]);
> > +ERR_CLK_APLL1:
>
> ^^^^^^^^^
>
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_AFE_APLL1]);
> > + return ret;
> > +}
> > +
> > +void mt8196_apll1_disable(struct mtk_base_afe *afe)
> > +{
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > +
> > + /* audio apll1 off */
> > + mt8196_afe_disable_top_cg(afe, MT8196_AUDIO_APLL1_EN_ON);
> > +
> > + /* apll1 freq tuner disable */
> > + regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG,
> > + FREQ_TUNER_EN_MASK_SFT,
> > + 0x0);
> > +
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_AFE_APLL_TUNER1]);
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_AFE_APLL1]);
> > + apll1_mux_setting(afe, false);
> > +}
> > +
> > +int mt8196_apll2_enable(struct mtk_base_afe *afe)
> > +{
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + int ret;
> > +
> > + /* setting for APLL */
> > + apll2_mux_setting(afe, true);
> > +
> > + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_AFE_APLL2]);
> > + if (ret)
> > + goto ERR_CLK_APLL2;
> > +
> > + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_AFE_APLL_TUNER2]);
> > + if (ret)
> > + goto ERR_CLK_APLL2_TUNER;
> > +
> > + /* sel 48kHz: 2, apll_div: 7, upper bound: 3*/
> > + regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG,
> > + XTAL_EN_128FS_SEL_MASK_SFT | APLL_DIV_MASK_SFT | UPPER_BOUND_MASK_SFT,
> > + (0x2 << XTAL_EN_128FS_SEL_SFT) | (7 << APLL_DIV_SFT) |
> > + (3 << UPPER_BOUND_SFT));
> > +
> > + /* apll2 freq tuner enable */
> > + regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG,
> > + FREQ_TUNER_EN_MASK_SFT,
> > + 0x1 << FREQ_TUNER_EN_SFT);
> > +
> > + /* audio apll2 on */
> > + mt8196_afe_enable_top_cg(afe, MT8196_AUDIO_APLL2_EN_ON);
> > + return 0;
> > +
> > +ERR_CLK_APLL2_TUNER:
>
> lower case for labels please
>
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_AFE_APLL_TUNER2]);
> > +ERR_CLK_APLL2:
>
> ditto
>
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_AFE_APLL2]);
> > + return ret;
> > +
> > + return 0;
> > +}
> > +
> > +void mt8196_apll2_disable(struct mtk_base_afe *afe)
> > +{
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > +
> > + /* audio apll2 off */
> > + mt8196_afe_disable_top_cg(afe, MT8196_AUDIO_APLL2_EN_ON);
> > +
> > + /* apll2 freq tuner disable */
> > + regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG,
> > + FREQ_TUNER_EN_MASK_SFT,
> > + 0x0);
> > +
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_AFE_APLL_TUNER2]);
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_AFE_APLL2]);
> > + apll2_mux_setting(afe, false);
> > +}
> > +
> > +int mt8196_get_apll_rate(struct mtk_base_afe *afe, int apll)
> > +{
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + int clk_id = 0;
> > +
> > + if (apll < MT8196_APLL1 || apll > MT8196_APLL2) {
> > + dev_warn(afe->dev, "invalid clk id\n");
>
> ("invalid clk id %d\n", clk_id)
>
> ...otherwise it makes no sense, as it gives no useful information.
> Alternatively, just drop the print.
>
> > + return 0;
> > + }
> > +
> > + if (apll == MT8196_APLL1)
> > + clk_id = MT8196_CLK_TOP_APLL1_CK;
> > + else
> > + clk_id = MT8196_CLK_TOP_APLL2_CK;
> > +
> > + return clk_get_rate(afe_priv->clk[clk_id]);
> > +}
> > +
> > +int mt8196_get_apll_by_rate(struct mtk_base_afe *afe, int rate)
> > +{
> > + return ((rate % 8000) == 0) ? MT8196_APLL2 : MT8196_APLL1;
>
> return (rate % 8000) ? MT8196_APLL1 : MT8196_APLL2;
>
> > +}
> > +
> > +int mt8196_get_apll_by_name(struct mtk_base_afe *afe, const char *name)
> > +{
> > + if (strcmp(name, APLL1_W_NAME) == 0)
> > + return MT8196_APLL1;
> > + else
> > + return MT8196_APLL2;
>
> if (strcmp ....)
> return MT8196_APLL1;
>
> return MT8196_APLL2;
>
> > +}
> > +
> > +/* mck */
> > +struct mt8196_mck_div {
> > + int m_sel_id;
> > + int div_clk_id;
> > +};
> > +
> > +static const struct mt8196_mck_div mck_div[MT8196_MCK_NUM] = {
> > + [MT8196_I2SIN0_MCK] = {
> > + .m_sel_id = MT8196_CLK_TOP_I2SIN0_M_SEL,
> > + .div_clk_id = MT8196_CLK_TOP_APLL12_DIV_I2SIN0,
> > + },
> > + [MT8196_I2SIN1_MCK] = {
> > + .m_sel_id = MT8196_CLK_TOP_I2SIN1_M_SEL,
> > + .div_clk_id = MT8196_CLK_TOP_APLL12_DIV_I2SIN1,
> > + },
> > + [MT8196_FMI2S_MCK] = {
> > + .m_sel_id = MT8196_CLK_TOP_FMI2S_M_SEL,
> > + .div_clk_id = MT8196_CLK_TOP_APLL12_DIV_FMI2S,
> > + },
> > + [MT8196_TDMOUT_MCK] = {
> > + .m_sel_id = MT8196_CLK_TOP_TDMOUT_M_SEL,
> > + .div_clk_id = MT8196_CLK_TOP_APLL12_DIV_TDMOUT_M,
> > + },
> > + [MT8196_TDMOUT_BCK] = {
> > + .m_sel_id = -1,
> > + .div_clk_id = MT8196_CLK_TOP_APLL12_DIV_TDMOUT_B,
> > + },
> > +};
> > +
> > +int mt8196_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate)
> > +{
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + int apll = mt8196_get_apll_by_rate(afe, rate);
> > + int apll_clk_id = apll == MT8196_APLL1 ?
> > + MT8196_CLK_TOP_MUX_AUD_1 : MT8196_CLK_TOP_MUX_AUD_2;
> > + int m_sel_id = 0;
> > + int div_clk_id = 0;
> > + int ret = 0;
>
> this gives double initialzation of all m_sel_id, div_clk_id and ret as you are
> initializing the first two immediately after the mck_id check, and ret later;
> just go for
>
> int m_sel_id, div_clk_id, ret;
>
> or just
>
> int ret;
>
> > +
> > + dev_dbg(afe->dev, "mck_id: %d, rate: %d\n", mck_id, rate);
> > +
> > + if (mck_id >= MT8196_MCK_NUM || mck_id < 0)
> > + return -EINVAL;
> > +
> > + m_sel_id = mck_div[mck_id].m_sel_id;
> > + div_clk_id = mck_div[mck_id].div_clk_id;
> > +
> > + /* select apll */
> > + if (m_sel_id >= 0) {
>
> ...because then I don't understand why don't you just use mck_div[mck_id] directly.
>
> if (mck_div[mck_id].m_sel_id >= 0) {
>
> > + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[m_sel_id]);
> > + if (ret)
> > + return ret;
> > +
> > + ret = mt8196_afe_set_clk_parent(afe, afe_priv->clk[m_sel_id],
> > + afe_priv->clk[apll_clk_id]);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + /* enable div, set rate */
> > + if (div_clk_id < 0) {
>
> if (mck_div[mck_id].div_clk_id == MT8196_CLK_TOP_APLL12_DIV_TDMOUT_B) {
> rate ...
> } else if (mck_div[mck_id].div_clk_id < 0) {
> ....
> }
>
>
> > + dev_err(afe->dev, "invalid div_clk_id %d\n", div_clk_id);
> > + return -EINVAL;
> > + }
> > + if (div_clk_id == MT8196_CLK_TOP_APLL12_DIV_TDMOUT_B)
> > + rate = rate * 16;
> > +
> > + ret = mt8196_afe_enable_clk(afe, afe_priv->clk[div_clk_id]);
> > + if (ret)
> > + return ret;
> > +
> > + ret = mt8196_afe_set_clk_rate(afe, afe_priv->clk[div_clk_id], rate);
> > + if (ret)
> > + return ret;
> > +
> > + return 0;
> > +}
> > +
> > +int mt8196_mck_disable(struct mtk_base_afe *afe, int mck_id)
> > +{
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + int m_sel_id = 0;
> > + int div_clk_id = 0;
>
> Double init again....
>
> > +
> > + dev_dbg(afe->dev, "mck_id: %d.\n", mck_id);
> > +
> > + if (mck_id < 0) {
> > + dev_err(afe->dev, "mck_id = %d < 0\n", mck_id);
> > + return -EINVAL;
> > + }
> > +
> > + m_sel_id = mck_div[mck_id].m_sel_id;
> > + div_clk_id = mck_div[mck_id].div_clk_id;
> > +
> > + if (div_clk_id < 0) {
> > + dev_err(afe->dev, "div_clk_id = %d < 0\n",
> > + div_clk_id);
> > + return -EINVAL;
> > + }
> > +
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[div_clk_id]);
> > +
> > + if (m_sel_id >= 0)
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[m_sel_id]);
> > +
> > + return 0;
> > +}
> > +
> > +int mt8196_afe_enable_reg_rw_clk(struct mtk_base_afe *afe)
> > +{
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > +
> > + /* bus clock for AFE external access, like DRAM */
> > + mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_ADSP_SEL]);
> > +
> > + /* bus clock for AFE internal access, like AFE SRAM */
> > + mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIOINTBUS]);
> > + mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIOINTBUS],
> > + afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
> > + /* enable audio vlp clock source */
> > + mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
> > + mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H],
> > + afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
> > +
> > + /* AFE hw clock */
> > + /* IPM2.0: USE HOPPING & 26M */
> > + mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_AFE_AUDIO_HOPPING]);
> > + mt8196_afe_enable_clk(afe, afe_priv->clk[MT8196_CLK_AFE_AUDIO_F26M]);
> > + return 0;
> > +}
> > +
> > +int mt8196_afe_disable_reg_rw_clk(struct mtk_base_afe *afe)
> > +{
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > +
> > + /* IPM2.0: Use HOPPING & 26M */
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_AFE_AUDIO_HOPPING]);
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_AFE_AUDIO_F26M]);
> > + mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H],
> > + afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
> > +
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIO_H]);
> > + mt8196_afe_set_clk_parent(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIOINTBUS],
> > + afe_priv->clk[MT8196_CLK_VLP_CLK26M]);
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_VLP_MUX_AUDIOINTBUS]);
> > + mt8196_afe_disable_clk(afe, afe_priv->clk[MT8196_CLK_TOP_ADSP_SEL]);
> > + return 0;
> > +}
> > +
> > +int mt8196_afe_enable_main_clock(struct mtk_base_afe *afe)
> > +{
>
> Just directly call
>
> mt8196_afe_enable_top_cg(afe, MT8196_AUDIO_26M_EN_ON);
>
> ...and drop mt8196_afe_enable_afe_on()
>
> > + mt8196_afe_enable_afe_on(afe);
> > + return 0;
> > +}
> > +
> > +int mt8196_afe_disable_main_clock(struct mtk_base_afe *afe)
> > +{
> > + mt8196_afe_disable_afe_on(afe);
>
> mt8196_afe_disable_top_cg(afe, MT8196_AUDIO_26M_EN_ON);
>
>
> > + return 0;
> > +}
> > +
> > +int mt8196_init_clock(struct mtk_base_afe *afe)
> > +{
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + int ret = 0;
> > + int i = 0;
> > +
> > + ret = mt8196_audsys_clk_register(afe);
> > + if (ret) {
> > + dev_err(afe->dev, "register audsys clk fail %d\n", ret);
> > + return ret;
> > + }
> > +
> > + afe_priv->clk = devm_kcalloc(afe->dev, MT8196_CLK_NUM, sizeof(*afe_priv->clk),
> > + GFP_KERNEL);
> > + if (!afe_priv->clk)
> > + return -ENOMEM;
> > +
> > + for (i = 0; i < MT8196_CLK_NUM; i++) {
> > + afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]);
> > + if (IS_ERR(afe_priv->clk[i])) {
> > + dev_err(afe->dev, "devm_clk_get %s fail\n", aud_clks[i]);
> > + return PTR_ERR(afe_priv->clk[i]);
> > + }
> > + }
> > +
> > + afe_priv->vlp_ck = syscon_regmap_lookup_by_phandle(afe->dev->of_node,
> > + "vlpcksys");
> > + if (IS_ERR(afe_priv->vlp_ck)) {
> > + dev_err(afe->dev, "Cannot find vlpcksys\n");
> > + return PTR_ERR(afe_priv->vlp_ck);
> > + }
> > +
> > + mt8196_afe_apll_init(afe);
> > +
> > + ret = mt8196_afe_disable_apll(afe);
> > + if (ret)
> > + return ret;
> > +
> > + return 0;
> > +}
> > +
> > diff --git a/sound/soc/mediatek/mt8196/mt8196-afe-clk.h b/sound/soc/mediatek/mt8196/mt8196-afe-clk.h
> > new file mode 100644
> > index 000000000000..60f5e5a157d5
> > --- /dev/null
> > +++ b/sound/soc/mediatek/mt8196/mt8196-afe-clk.h
> > @@ -0,0 +1,142 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * mt8196-afe-clk.h -- Mediatek 8196 afe clock ctrl definition
>
> mt8196-afe-clk.h - MediaTek MT8195 AFE Clock Control definitions
>
> > + *
> > + * Copyright (c) 2024 MediaTek Inc.
> > + * Author: Darren Ye <darren.ye at mediatek.com>
> > + */
> > +
>
> ..snip..
>
> > diff --git a/sound/soc/mediatek/mt8196/mt8196-audsys-clk.c b/sound/soc/mediatek/mt8196/mt8196-audsys-clk.c
> > new file mode 100644
> > index 000000000000..aa40f02698ac
> > --- /dev/null
> > +++ b/sound/soc/mediatek/mt8196/mt8196-audsys-clk.c
> > @@ -0,0 +1,252 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * mt8196-audsys-clk.c -- MediaTek 8196 audsys clock control
> > + *
> > + * Copyright (c) 2025 MediaTek Inc.
> > + * Author: Darren Ye <darren.ye at mediatek.com>
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/clk-provider.h>
> > +#include <linux/clkdev.h>
> > +#include "mt8196-afe-common.h"
> > +#include "mt8196-audsys-clk.h"
> > +#include "mt8196-audsys-clkid.h"
> > +#include "mt8196-reg.h"
> > +
> ..snip..
>
>
> > +};
> > +
> > +static void mt8196_audsys_clk_unregister(void *data)
> > +{
> > + struct mtk_base_afe *afe = data;
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + struct clk *clk;
> > + struct clk_lookup *cl;
> > + int i;
> > +
> > + if (!afe_priv)
> > + return;
> > +
> > + for (i = 0; i < CLK_AFE_NR_CLK; i++) {
> > + cl = afe_priv->lookup[i];
> > + if (!cl)
> > + continue;
> > +
> > + clk = cl->clk;
> > + clk_unregister_gate(clk);
> > +
> > + clkdev_drop(cl);
> > + }
> > +}
> > +
> > +int mt8196_audsys_clk_register(struct mtk_base_afe *afe)
> > +{
> > + struct mt8196_afe_private *afe_priv = afe->platform_priv;
> > + struct clk *clk;
> > + struct clk_lookup *cl;
> > + int i;
> > +
> > + afe_priv->lookup = devm_kcalloc(afe->dev, CLK_AFE_NR_CLK,
> > + sizeof(*afe_priv->lookup),
> > + GFP_KERNEL);
> > +
> > + if (!afe_priv->lookup)
> > + return -ENOMEM;
> > +
> > + for (i = 0; i < ARRAY_SIZE(aud_clks); i++) {
> > + const struct afe_gate *gate = &aud_clks[i];
> > +
> > + clk = clk_register_gate(afe->dev, gate->name, gate->parent_name,
> > + gate->flags, afe->base_addr + gate->reg,
> > + gate->bit, gate->cg_flags, NULL);
> > +
> > + if (IS_ERR(clk)) {
> > + dev_err(afe->dev, "Failed to register clk %s: %ld\n",
> > + gate->name, PTR_ERR(clk));
> > + continue;
> > + }
> > +
>
> All of the above, until...
>
> > + /* add clk_lookup for devm_clk_get(SND_SOC_DAPM_CLOCK_SUPPLY) */
> > + cl = kzalloc(sizeof(*cl), GFP_KERNEL);
> > + if (!cl)
> > + return -ENOMEM;
> > +
> > + cl->clk = clk;
> > + cl->con_id = gate->name;
> > + cl->dev_id = dev_name(afe->dev);
> > + cl->clk_hw = NULL;
> > + clkdev_add(cl);
>
>
> ...here, can be simplified with a single call to
>
> clk_register_clkdev(clk, gate->name, dev_name(afe->dev))
>
> or alternatively, you could simplify it even more:
>
>
> static void mt8196_audsys_clk_unregister(void *data)
> {
> /* nothing to do here, remove this function */
> }
>
> int mt8196_audsys_clk_register(struct mtk_base_afe *afe)
> {
> struct mt8196_afe_private *afe_priv = afe->platform_priv;
> int i, ret;
>
> for (i = 0; i < ARRAY_SIZE(aud_clks); i++) {
> const struct afe_gate *gate = &aud_clks[i];
> struct clk_hw *hw;
>
> hw = devm_clk_hw_register_gate(afe->dev, gate->name, gate->parent_name,
> gate->flags, afe->base_addr + gate->reg,
> gate->bit, gate->cg_flags, NULL);
> if (IS_ERR(clk)) {
> dev_err(afe->dev, "Failed to register clk %s: %ld\n",
> gate->name, PTR_ERR(clk));
> continue;
> }
>
> ret = devm_clk_hw_register_clkdev(afe->dev, hw, gate->name, dev_name(afe->dev));
> if (ret)
> return ret;
> }
>
> return 0;
> }
There is no need to involve the clk subsystem. These are simply supply
gates, be them for power or clks, one per bit. Simply model them as ASoC
supply widgets, add appropriate routes for each so that the dependencies
are correct, and ASoC will deal with them for you. No code is needed,
just descriptions.
_That_ is why I asked for he audio clocks to be integrated into the
AFE driver.
ChenYu
> > +
> > + afe_priv->lookup[i] = cl;
> > + }
> > +
> > + return devm_add_action_or_reset(afe->dev, mt8196_audsys_clk_unregister, afe);
> > +}
>
> Cheers,
> Angelo
>
More information about the Linux-mediatek
mailing list