From: Zhe Wang Date: Mon, 13 May 2019 06:56:25 +0000 (+0800) Subject: audio: add i2s and spdif fine clk tuning interface [1/1] X-Git-Tag: hardkernel-4.9.236-104~1221 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=296f033b85f7e5ecce2ef842bf804cbb829ed770;p=platform%2Fkernel%2Flinux-amlogic.git audio: add i2s and spdif fine clk tuning interface [1/1] PD#SWPL-8310 Problem: DTV, a/v is out of sync Solution: add i2s and spdif fine clk tuning interface Verify: verify on R311. Change-Id: I8219774bd5fe334fa21227d427ce4dbb06177dc8 Signed-off-by: Zhe Wang --- diff --git a/sound/soc/amlogic/meson/i2s_dai.c b/sound/soc/amlogic/meson/i2s_dai.c index 79a54e9..381f389 100644 --- a/sound/soc/amlogic/meson/i2s_dai.c +++ b/sound/soc/amlogic/meson/i2s_dai.c @@ -49,6 +49,63 @@ #include "spdif_dai.h" #include "dmic.h" +static int i2s_clk_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct aml_i2s *p_i2s = snd_soc_dai_get_drvdata(cpu_dai); + + ucontrol->value.enumerated.item[0] = clk_get_rate(p_i2s->clk_mclk); + return 0; +} + +static int i2s_clk_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct aml_i2s *p_i2s = snd_soc_dai_get_drvdata(cpu_dai); + int ret = 0; + + unsigned long mclk_rate = p_i2s->mclk; + int value = ucontrol->value.enumerated.item[0]; + + if (value > 2000000 || value < 0) { + pr_err("Fine tdm clk setting range (0~2000000), %d\n", value); + return 0; + } + mclk_rate += (value - 1000000); + + ret = clk_set_rate(p_i2s->clk_mpll, mclk_rate * 10); + if (ret) { + pr_err("Cannot set i2s mpll\n"); + return ret; + } + + ret = clk_set_rate(p_i2s->clk_mclk, mclk_rate); + if (ret) { + pr_err("Cannot set i2s mclk %ld\n", mclk_rate); + return ret; + } + + p_i2s->mclk = mclk_rate; + return 0; +} + +static const struct snd_kcontrol_new snd_i2s_controls[] = { + SOC_SINGLE_EXT("TDM MCLK Fine Setting", + 0, 0, 2000000, 0, + i2s_clk_get, + i2s_clk_set), +}; + +static int aml_dai_i2s_probe(struct snd_soc_dai *dai) +{ + snd_soc_add_dai_controls(dai, + snd_i2s_controls, ARRAY_SIZE(snd_i2s_controls)); + return 0; +} + + /* extern int set_i2s_iec958_samesource(int enable); * * the I2S hw and IEC958 PCM output initiation,958 initiation here, @@ -140,6 +197,8 @@ static int aml_i2s_set_amclk(struct aml_i2s *i2s, unsigned long rate) if (ret) { pr_info("Cannot set i2s mclk %lu\n", rate); return ret; + } else { + i2s->mclk = rate; } audio_set_i2s_clk_div(); @@ -379,6 +438,7 @@ static struct snd_soc_dai_ops aml_dai_i2s_ops = { struct snd_soc_dai_driver aml_i2s_dai[] = { { .id = 0, + .probe = aml_dai_i2s_probe, .suspend = aml_dai_i2s_suspend, .resume = aml_dai_i2s_resume, .playback = { diff --git a/sound/soc/amlogic/meson/i2s_dai.h b/sound/soc/amlogic/meson/i2s_dai.h index 9246fed..202b6f5 100644 --- a/sound/soc/amlogic/meson/i2s_dai.h +++ b/sound/soc/amlogic/meson/i2s_dai.h @@ -26,5 +26,6 @@ struct aml_i2s { int audin_fifo_src; int i2s_pos_sync; int clk_data_pos; + unsigned long mclk; }; #endif diff --git a/sound/soc/amlogic/meson/spdif_dai.c b/sound/soc/amlogic/meson/spdif_dai.c index 58990a6..8965908 100644 --- a/sound/soc/amlogic/meson/spdif_dai.c +++ b/sound/soc/amlogic/meson/spdif_dai.c @@ -62,6 +62,7 @@ struct aml_spdif { * !Check this with chip spec. */ uint src; + unsigned long spdifout_clk; }; struct aml_spdif *spdif_p; @@ -548,6 +549,7 @@ int aml_set_spdif_clk(unsigned long rate, bool src_i2s) pr_err("Can't set spdif clk parent: %d\n", ret); return ret; } + spdif_p->spdifout_clk = rate; } return 0; @@ -588,6 +590,51 @@ static int aml_dai_spdif_resume(struct snd_soc_dai *cpu_dai) return 0; } +static int spdif_clk_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct aml_spdif *p_spdif = snd_soc_dai_get_drvdata(cpu_dai); + + ucontrol->value.enumerated.item[0] = + clk_get_rate(p_spdif->clk_spdif); + return 0; +} + +static int spdif_clk_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct aml_spdif *p_spdif = snd_soc_dai_get_drvdata(cpu_dai); + unsigned long sysclk = p_spdif->spdifout_clk; + int value = ucontrol->value.enumerated.item[0]; + + if (value > 2000000 || value < 0) { + pr_err("Fine spdif sysclk setting range(0~2000000), %d\n", + value); + return 0; + } + value = value - 1000000; + sysclk += value; + + aml_set_spdif_clk(sysclk, 0); + return 0; +} + +static const struct snd_kcontrol_new aml_spdif_dai_controls[] = { + SOC_SINGLE_EXT("SPDIF CLK Fine Setting", + 0, 0, 2000000, 0, + spdif_clk_get, + spdif_clk_set), +}; + +static int aml_spdif_probe(struct snd_soc_dai *dai) +{ + snd_soc_add_dai_controls(dai, + aml_spdif_dai_controls, ARRAY_SIZE(aml_spdif_dai_controls)); + return 0; +} + static struct snd_soc_dai_ops spdif_dai_ops = { .set_sysclk = aml_dai_spdif_set_sysclk, .trigger = aml_dai_spdif_trigger, @@ -628,6 +675,7 @@ static struct snd_soc_dai_driver aml_spdif_dai[] = { .ops = &spdif_dai_ops, .suspend = aml_dai_spdif_suspend, .resume = aml_dai_spdif_resume, + .probe = aml_spdif_probe, } }; @@ -692,6 +740,7 @@ static int aml_dai_spdif_probe(struct platform_device *pdev) ret = PTR_ERR(spdif_priv->clk_spdif); goto err; } + ret = clk_set_parent(spdif_priv->clk_i958, spdif_priv->clk_mpl1); if (ret) { pr_err("Can't set i958 clk parent: %d\n", ret); @@ -703,6 +752,7 @@ static int aml_dai_spdif_probe(struct platform_device *pdev) pr_err("Can't set spdif clk parent: %d\n", ret); return ret; } + ret = clk_prepare_enable(spdif_priv->clk_spdif); if (ret) { pr_err("Can't enable spdif clock: %d\n", ret);