From 647d04f8e07afc7c3b7a42b3ee01a8b28db29631 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 5 Feb 2018 16:43:56 +0100 Subject: ASoC: samsung: i2s: Ensure the RCLK rate is properly determined If the RCLK mux clock configuration is specified in DT and no set_sysclk() callback is used in the sound card driver the sclk_srcrate field will remain set to 0, leading to an incorrect PSR divider setting. To fix this the frequency value is retrieved from the CLK_I2S_RCLK_SRC clock, so the actual RCLK mux selection is taken into account. Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 233f1c9a4b6c..aeba0ae890ea 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -656,8 +656,12 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, tmp |= mod_slave; break; case SND_SOC_DAIFMT_CBS_CFS: - /* Set default source clock in Master mode */ - if (i2s->rclk_srcrate == 0) + /* + * Set default source clock in Master mode, only when the + * CLK_I2S_RCLK_SRC clock is not exposed so we ensure any + * clock configuration assigned in DT is not overwritten. + */ + if (i2s->rclk_srcrate == 0 && i2s->clk_data.clks == NULL) i2s_set_sysclk(dai, SAMSUNG_I2S_RCLKSRC_0, 0, SND_SOC_CLOCK_IN); break; @@ -881,6 +885,11 @@ static int config_setup(struct i2s_dai *i2s) return 0; if (!(i2s->quirks & QUIRK_NO_MUXPSR)) { + struct clk *rclksrc = i2s->clk_table[CLK_I2S_RCLK_SRC]; + + if (i2s->rclk_srcrate == 0 && rclksrc && !IS_ERR(rclksrc)) + i2s->rclk_srcrate = clk_get_rate(rclksrc); + psr = i2s->rclk_srcrate / i2s->frmclk / rfs; writel(((psr - 1) << 8) | PSR_PSREN, i2s->addr + I2SPSR); dev_dbg(&i2s->pdev->dev, -- cgit v1.2.3 From beff23ff26db0c1df2eaabc0dfe877bcbf05f6db Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Mon, 29 Jan 2018 12:26:16 +0000 Subject: ASoC: samsung: clean makefile about inexistant files Clean makefile rules about inexistant files. Thoses files were removed in commit a076d418235f ("ASoC: samsung: Drop AC97 drivers") Signed-off-by: Corentin Labbe Reviewed-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/Makefile | 3 --- 1 file changed, 3 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile index 030949e1e434..c3b76035f69c 100644 --- a/sound/soc/samsung/Makefile +++ b/sound/soc/samsung/Makefile @@ -21,8 +21,6 @@ obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-idma.o # S3C24XX Machine Support snd-soc-jive-wm8750-objs := jive_wm8750.o snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o -snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o -snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.o snd-soc-s3c24xx-simtec-hermes-objs := s3c24xx_simtec_hermes.o @@ -32,7 +30,6 @@ snd-soc-rx1950-uda1380-objs := rx1950_uda1380.o snd-soc-smdk-wm8580-objs := smdk_wm8580.o snd-soc-smdk-wm8994-objs := smdk_wm8994.o snd-soc-snow-objs := snow.o -snd-soc-smdk-wm9713-objs := smdk_wm9713.o snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o snd-soc-smdk-spdif-objs := smdk_spdif.o snd-soc-smdk-wm8994pcm-objs := smdk_wm8994pcm.o -- cgit v1.2.3 From aa274c5cfd13381372d2dace32b7354ccba7d2ab Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 12 Feb 2018 17:15:33 +0100 Subject: ASoC: samsung: i2s: Ensure names of supplied clocks are unique In order to support multiple instances of the I2S IP block the platform device name is prepended to each clock registered by the driver. The clock-output-names property is now not used, this should not cause any issues as, for example, CDCLK clock is referenced through DT 'clocks' property, not by name. This change allows to have both I2S0 and I2S1 enabled simultaneously on exynos5433 and working properly when #clock-cells property is specified in respective DT nodes. Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index aeba0ae890ea..5088d12faeda 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1193,11 +1193,13 @@ static void i2s_unregister_clock_provider(struct platform_device *pdev) static int i2s_register_clock_provider(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct i2s_dai *i2s = dev_get_drvdata(dev); + const char * const i2s_clk_desc[] = { "cdclk", "rclk_src", "prescaler" }; const char *clk_name[2] = { "i2s_opclk0", "i2s_opclk1" }; const char *p_names[2] = { NULL }; + struct device *dev = &pdev->dev; + struct i2s_dai *i2s = dev_get_drvdata(dev); const struct samsung_i2s_variant_regs *reg_info = i2s->variant_regs; + const char *i2s_clk_name[ARRAY_SIZE(i2s_clk_desc)]; struct clk *rclksrc; int ret, i; @@ -1214,30 +1216,38 @@ static int i2s_register_clock_provider(struct platform_device *pdev) clk_put(rclksrc); } + for (i = 0; i < ARRAY_SIZE(i2s_clk_desc); i++) { + i2s_clk_name[i] = devm_kasprintf(dev, GFP_KERNEL, "%s_%s", + dev_name(dev), i2s_clk_desc[i]); + if (!i2s_clk_name[i]) + return -ENOMEM; + } + if (!(i2s->quirks & QUIRK_NO_MUXPSR)) { /* Activate the prescaler */ u32 val = readl(i2s->addr + I2SPSR); writel(val | PSR_PSREN, i2s->addr + I2SPSR); i2s->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(dev, - "i2s_rclksrc", p_names, ARRAY_SIZE(p_names), + i2s_clk_name[CLK_I2S_RCLK_SRC], p_names, + ARRAY_SIZE(p_names), CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, i2s->addr + I2SMOD, reg_info->rclksrc_off, 1, 0, i2s->lock); i2s->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(dev, - "i2s_presc", "i2s_rclksrc", + i2s_clk_name[CLK_I2S_RCLK_PSR], + i2s_clk_name[CLK_I2S_RCLK_SRC], CLK_SET_RATE_PARENT, i2s->addr + I2SPSR, 8, 6, 0, i2s->lock); - p_names[0] = "i2s_presc"; + p_names[0] = i2s_clk_name[CLK_I2S_RCLK_PSR]; i2s->clk_data.clk_num = 2; } - of_property_read_string_index(dev->of_node, - "clock-output-names", 0, &clk_name[0]); - i2s->clk_table[CLK_I2S_CDCLK] = clk_register_gate(dev, clk_name[0], - p_names[0], CLK_SET_RATE_PARENT, + i2s->clk_table[CLK_I2S_CDCLK] = clk_register_gate(dev, + i2s_clk_name[CLK_I2S_CDCLK], p_names[0], + CLK_SET_RATE_PARENT, i2s->addr + I2SMOD, reg_info->cdclkcon_off, CLK_GATE_SET_TO_DISABLE, i2s->lock); -- cgit v1.2.3 From 48279c53fd1d09dc0d1b6ebb9f21732c507cea0b Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 12 Feb 2018 17:15:34 +0100 Subject: ASoC: samsung: i2s: Prevent external abort on exynos5433 I2S1 access It seems both PCLK_I2S1 and SCLK_I2S1 clocks need to be enabled before I2S1 control registers can be accessed on exynos5433. If SCLK clock is disabled an exception is triggered. To fix this parent clock of the RCLK_SRC clock is assigned to pri_dai->op_clk so required gate clock is handled by the runtime PM ops. Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 5088d12faeda..7b57ad11ca15 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1404,9 +1404,14 @@ static int samsung_i2s_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); ret = i2s_register_clock_provider(pdev); - if (!ret) - return 0; + if (ret < 0) + goto err_disable_pm; + + pri_dai->op_clk = clk_get_parent(pri_dai->clk_table[CLK_I2S_RCLK_SRC]); + + return 0; +err_disable_pm: pm_runtime_disable(&pdev->dev); err_disable_clk: clk_disable_unprepare(pri_dai->clk); -- cgit v1.2.3 From 45ae70e8b60e1d1bbc71eeccaef4026c5e6638a3 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 12 Feb 2018 17:15:35 +0100 Subject: ASoC: samsung: i2s: Define the parameters list for SAMSUNG_I2S_OPCLK The SAMSUNG_I2S_OPCLK is not currently used by any card driver thus we can safely change semantics of 'dir' argument of the I2S set_sysclk() callback. Now an enumeration is exported instead of directly using register bit field values. Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/i2s-regs.h | 11 ++++++----- sound/soc/samsung/i2s.c | 2 +- sound/soc/samsung/i2s.h | 11 ++++++++--- 3 files changed, 15 insertions(+), 9 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h index fe6914005494..964985ea2e80 100644 --- a/sound/soc/samsung/i2s-regs.h +++ b/sound/soc/samsung/i2s-regs.h @@ -65,11 +65,12 @@ #define CON_RXDMA_ACTIVE (1 << 1) #define CON_ACTIVE (1 << 0) -#define MOD_OPCLK_CDCLK_OUT (0 << 30) -#define MOD_OPCLK_CDCLK_IN (1 << 30) -#define MOD_OPCLK_BCLK_OUT (2 << 30) -#define MOD_OPCLK_PCLK (3 << 30) -#define MOD_OPCLK_MASK (3 << 30) +#define MOD_OPCLK_SHIFT 30 +#define MOD_OPCLK_CDCLK_OUT (0 << MOD_OPCLK_SHIFT) +#define MOD_OPCLK_CDCLK_IN (1 << MOD_OPCLK_SHIFT) +#define MOD_OPCLK_BCLK_OUT (2 << MOD_OPCLK_SHIFT) +#define MOD_OPCLK_PCLK (3 << MOD_OPCLK_SHIFT) +#define MOD_OPCLK_MASK (3 << MOD_OPCLK_SHIFT) #define MOD_TXS_IDMA (1 << 28) /* Sec_TXFIFO use I-DMA */ #define MOD_BLCS_SHIFT 26 diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 7b57ad11ca15..b6407fbabdd1 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -489,7 +489,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, switch (clk_id) { case SAMSUNG_I2S_OPCLK: mask = MOD_OPCLK_MASK; - val = dir; + val = (dir << MOD_OPCLK_SHIFT) & MOD_OPCLK_MASK; break; case SAMSUNG_I2S_CDCLK: mask = 1 << i2s_regs->cdclkcon_off; diff --git a/sound/soc/samsung/i2s.h b/sound/soc/samsung/i2s.h index 79781de2f247..a9832a9555cb 100644 --- a/sound/soc/samsung/i2s.h +++ b/sound/soc/samsung/i2s.h @@ -16,11 +16,16 @@ #define SAMSUNG_I2S_DAI "samsung-i2s" #define SAMSUNG_I2S_DAI_SEC "samsung-i2s-sec" -#define SAMSUNG_I2S_DIV_BCLK 1 +#define SAMSUNG_I2S_DIV_BCLK 1 -#define SAMSUNG_I2S_RCLKSRC_0 0 -#define SAMSUNG_I2S_RCLKSRC_1 1 +#define SAMSUNG_I2S_RCLKSRC_0 0 +#define SAMSUNG_I2S_RCLKSRC_1 1 #define SAMSUNG_I2S_CDCLK 2 +/* Operation clock for IIS logic */ #define SAMSUNG_I2S_OPCLK 3 +#define SAMSUNG_I2S_OPCLK_CDCLK_OUT 0 /* CODEC clock out */ +#define SAMSUNG_I2S_OPCLK_CDCLK_IN 1 /* CODEC clock in */ +#define SAMSUNG_I2S_OPCLK_BCLK_OUT 2 /* Bit clock out */ +#define SAMSUNG_I2S_OPCLK_PCLK 3 /* Audio bus clock */ #endif /* __SND_SOC_SAMSUNG_I2S_H */ -- cgit v1.2.3 From 8d1513cef51a11dcf8ccb89d089515483a5ad397 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 12 Feb 2018 17:15:37 +0100 Subject: ASoC: samsung: Add support for HDMI audio on TM2 board This patch defines I2S1 - HDMI DAI link and implements related hw_params callback. The AUD PLL frequency is configured through the CLK_SCLK_I2S1 leaf clock, the exynos5433 clock tree definitions are updated in a separate patch. The device tree parsing part is changed is a way it supports older DTBs with just a single CPU DAI specified, without the HDMI link. Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/tm2_wm5110.c | 152 ++++++++++++++++++++++++++++++++++------- 1 file changed, 129 insertions(+), 23 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/samsung/tm2_wm5110.c b/sound/soc/samsung/tm2_wm5110.c index a55d18703fe7..b6a492f1ec02 100644 --- a/sound/soc/samsung/tm2_wm5110.c +++ b/sound/soc/samsung/tm2_wm5110.c @@ -210,6 +210,59 @@ static struct snd_soc_ops tm2_aif2_ops = { .hw_free = tm2_aif2_hw_free, }; +static int tm2_hdmi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + unsigned int bfs; + int bitwidth, ret; + + bitwidth = snd_pcm_format_width(params_format(params)); + if (bitwidth < 0) { + dev_err(rtd->card->dev, "Invalid bit-width: %d\n", bitwidth); + return bitwidth; + } + + switch (bitwidth) { + case 48: + bfs = 64; + break; + case 16: + bfs = 32; + break; + default: + dev_err(rtd->card->dev, "Unsupported bit-width: %d\n", bitwidth); + return -EINVAL; + } + + switch (params_rate(params)) { + case 48000: + case 96000: + case 192000: + break; + default: + dev_err(rtd->card->dev, "Unsupported sample rate: %d\n", + params_rate(params)); + return -EINVAL; + } + + ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_OPCLK, + 0, SAMSUNG_I2S_OPCLK_PCLK); + if (ret < 0) + return ret; + + ret = snd_soc_dai_set_clkdiv(cpu_dai, SAMSUNG_I2S_DIV_BCLK, bfs); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops tm2_hdmi_ops = { + .hw_params = tm2_hdmi_hw_params, +}; + static int tm2_mic_bias(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -405,6 +458,12 @@ static struct snd_soc_dai_link tm2_dai_links[] = { .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM, .ignore_suspend = 1, + }, { + .name = "HDMI", + .stream_name = "i2s1", + .ops = &tm2_hdmi_ops, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, } }; @@ -412,7 +471,6 @@ static struct snd_soc_card tm2_card = { .owner = THIS_MODULE, .dai_link = tm2_dai_links, - .num_links = ARRAY_SIZE(tm2_dai_links), .controls = tm2_controls, .num_controls = ARRAY_SIZE(tm2_controls), .dapm_widgets = tm2_dapm_widgets, @@ -426,11 +484,14 @@ static struct snd_soc_card tm2_card = { static int tm2_probe(struct platform_device *pdev) { + struct device_node *cpu_dai_node[2] = {}; + struct device_node *codec_dai_node[2] = {}; + const char *cells_name = NULL; struct device *dev = &pdev->dev; struct snd_soc_card *card = &tm2_card; struct tm2_machine_priv *priv; - struct device_node *cpu_dai_node, *codec_dai_node; - int ret, i; + struct of_phandle_args args; + int num_codecs, ret, i; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -464,47 +525,92 @@ static int tm2_probe(struct platform_device *pdev) return -EINVAL; } - cpu_dai_node = of_parse_phandle(dev->of_node, "i2s-controller", 0); - if (!cpu_dai_node) { - dev_err(dev, "i2s-controllers property invalid or missing\n"); - ret = -EINVAL; - goto amp_node_put; + num_codecs = of_count_phandle_with_args(dev->of_node, "audio-codec", + NULL); + + /* Skip the HDMI link if not specified in DT */ + if (num_codecs > 1) { + card->num_links = ARRAY_SIZE(tm2_dai_links); + cells_name = "#sound-dai-cells"; + } else { + card->num_links = ARRAY_SIZE(tm2_dai_links) - 1; } - codec_dai_node = of_parse_phandle(dev->of_node, "audio-codec", 0); - if (!codec_dai_node) { - dev_err(dev, "audio-codec property invalid or missing\n"); - ret = -EINVAL; - goto cpu_dai_node_put; + for (i = 0; i < num_codecs; i++) { + struct of_phandle_args args; + + ret = of_parse_phandle_with_args(dev->of_node, "i2s-controller", + cells_name, i, &args); + if (!args.np) { + dev_err(dev, "i2s-controller property parse error: %d\n", i); + ret = -EINVAL; + goto dai_node_put; + } + cpu_dai_node[i] = args.np; + + codec_dai_node[i] = of_parse_phandle(dev->of_node, + "audio-codec", i); + if (!codec_dai_node[i]) { + dev_err(dev, "audio-codec property parse error\n"); + ret = -EINVAL; + goto dai_node_put; + } } + /* Initialize WM5110 - I2S and HDMI - I2S1 DAI links */ for (i = 0; i < card->num_links; i++) { + unsigned int dai_index = 0; /* WM5110 */ + card->dai_link[i].cpu_name = NULL; card->dai_link[i].platform_name = NULL; - card->dai_link[i].codec_of_node = codec_dai_node; - card->dai_link[i].cpu_of_node = cpu_dai_node; - card->dai_link[i].platform_of_node = cpu_dai_node; + + if (num_codecs > 1 && i == card->num_links - 1) + dai_index = 1; /* HDMI */ + + card->dai_link[i].codec_of_node = codec_dai_node[dai_index]; + card->dai_link[i].cpu_of_node = cpu_dai_node[dai_index]; + card->dai_link[i].platform_of_node = cpu_dai_node[dai_index]; + } + + if (num_codecs > 1) { + /* HDMI DAI link (I2S1) */ + i = card->num_links - 1; + + ret = of_parse_phandle_with_fixed_args(dev->of_node, + "audio-codec", 0, 1, &args); + if (ret) { + dev_err(dev, "audio-codec property parse error\n"); + goto dai_node_put; + } + + ret = snd_soc_get_dai_name(&args, &card->dai_link[i].codec_dai_name); + if (ret) { + dev_err(dev, "Unable to get codec_dai_name\n"); + goto dai_node_put; + } } ret = devm_snd_soc_register_component(dev, &tm2_component, tm2_ext_dai, ARRAY_SIZE(tm2_ext_dai)); if (ret < 0) { dev_err(dev, "Failed to register component: %d\n", ret); - goto codec_dai_node_put; + goto dai_node_put; } ret = devm_snd_soc_register_card(dev, card); if (ret < 0) { dev_err(dev, "Failed to register card: %d\n", ret); - goto codec_dai_node_put; + goto dai_node_put; + } + +dai_node_put: + for (i = 0; i < num_codecs; i++) { + of_node_put(codec_dai_node[i]); + of_node_put(cpu_dai_node[i]); } -codec_dai_node_put: - of_node_put(codec_dai_node); -cpu_dai_node_put: - of_node_put(cpu_dai_node); -amp_node_put: of_node_put(card->aux_dev[0].codec_of_node); + return ret; } -- cgit v1.2.3 From d9e575127b4169061d94c9982c4f0e8aae208c78 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 7 Mar 2018 18:46:25 +0100 Subject: ASoC: Use proper DT compatible string for Hardkernel Odroid boards The Odroid boards are manufactured by Hardkernel, not Samsung. New compatible string entries are added, with "hardkernel," instead of "samsung," vendor prefix. Support for the old compatible strings is going to be removed after some time. Signed-off-by: Sylwester Nawrocki Reviewed-by: Rob Herring Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/samsung,odroid.txt | 6 +++--- sound/soc/samsung/odroid.c | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/Documentation/devicetree/bindings/sound/samsung,odroid.txt b/Documentation/devicetree/bindings/sound/samsung,odroid.txt index 625b1b18fd02..f35a2694eb04 100644 --- a/Documentation/devicetree/bindings/sound/samsung,odroid.txt +++ b/Documentation/devicetree/bindings/sound/samsung,odroid.txt @@ -2,8 +2,8 @@ Samsung Exynos Odroid XU3/XU4 audio complex with MAX98090 codec Required properties: - - compatible - "samsung,odroidxu3-audio" - for Odroid XU3 board, - "samsung,odroidxu4-audio" - for Odroid XU4 board + - compatible - "hardkernel,odroid-xu3-audio" - for Odroid XU3 board, + "hardkernel,odroid-xu4-audio" - for Odroid XU4 board - model - the user-visible name of this sound complex - clocks - should contain entries matching clock names in the clock-names property @@ -35,7 +35,7 @@ Required sub-nodes: Example: sound { - compatible = "samsung,odroidxu3-audio"; + compatible = "hardkernel,odroid-xu3-audio"; model = "Odroid-XU3"; samsung,audio-routing = "Headphone Jack", "HPL", diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c index 44b6de5a331a..92d750806d1d 100644 --- a/sound/soc/samsung/odroid.c +++ b/sound/soc/samsung/odroid.c @@ -213,8 +213,10 @@ static int odroid_audio_remove(struct platform_device *pdev) } static const struct of_device_id odroid_audio_of_match[] = { + { .compatible = "hardkernel,odroid-xu3-audio" }, + { .compatible = "hardkernel,odroid-xu4-audio" }, { .compatible = "samsung,odroid-xu3-audio" }, - { .compatible = "samsung,odroid-xu4-audio"}, + { .compatible = "samsung,odroid-xu4-audio" }, { }, }; MODULE_DEVICE_TABLE(of, odroid_audio_of_match); -- cgit v1.2.3 From 946857636d279486eaa47c930930185631f28fce Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 9 Mar 2018 18:48:54 +0100 Subject: ASoC: Add snd_soc_of_put_dai_link_codecs() helper function The code for dereferencing device nodes in the 'codecs' array is moved to a separate function so we can avoid open coding that in drivers. Signed-off-by: Sylwester Nawrocki Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/soc-core.c | 32 +++++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 9 deletions(-) (limited to 'sound/soc') diff --git a/include/sound/soc.h b/include/sound/soc.h index 747fd583b9dc..4a387f0b3d56 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1807,6 +1807,7 @@ int snd_soc_of_get_dai_name(struct device_node *of_node, int snd_soc_of_get_dai_link_codecs(struct device *dev, struct device_node *of_node, struct snd_soc_dai_link *dai_link); +void snd_soc_of_put_dai_link_codecs(struct snd_soc_dai_link *dai_link); int snd_soc_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 96c44f6576c9..a1f86e85bff9 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4397,6 +4397,26 @@ int snd_soc_of_get_dai_name(struct device_node *of_node, } EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name); +/* + * snd_soc_of_put_dai_link_codecs - Dereference device nodes in the codecs array + * @dai_link: DAI link + * + * Dereference device nodes acquired by snd_soc_of_get_dai_link_codecs(). + */ +void snd_soc_of_put_dai_link_codecs(struct snd_soc_dai_link *dai_link) +{ + struct snd_soc_dai_link_component *component = dai_link->codecs; + int index; + + for (index = 0; index < dai_link->num_codecs; index++, component++) { + if (!component->of_node) + break; + of_node_put(component->of_node); + component->of_node = NULL; + } +} +EXPORT_SYMBOL_GPL(snd_soc_of_put_dai_link_codecs); + /* * snd_soc_of_get_dai_link_codecs - Parse a list of CODECs in the devicetree * @dev: Card device @@ -4406,7 +4426,8 @@ EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name); * Builds an array of CODEC DAI components from the DAI link property * 'sound-dai'. * The array is set in the DAI link and the number of DAIs is set accordingly. - * The device nodes in the array (of_node) must be dereferenced by the caller. + * The device nodes in the array (of_node) must be dereferenced by calling + * snd_soc_of_put_dai_link_codecs() on @dai_link. * * Returns 0 for success */ @@ -4454,14 +4475,7 @@ int snd_soc_of_get_dai_link_codecs(struct device *dev, } return 0; err: - for (index = 0, component = dai_link->codecs; - index < dai_link->num_codecs; - index++, component++) { - if (!component->of_node) - break; - of_node_put(component->of_node); - component->of_node = NULL; - } + snd_soc_of_put_dai_link_codecs(dai_link); dai_link->codecs = NULL; dai_link->num_codecs = 0; return ret; -- cgit v1.2.3 From 995e73e55f46dcfae863d72de674447008a85887 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 9 Mar 2018 18:48:55 +0100 Subject: ASoC: samsung: i2s: Fix rclk_srcrate handling As the RCLK clock may be updated through the common clk API before each snd_soc_dai_ops::trigger call, it is not enough to update i2s->rclk_srcrate only once after it has been initially set to 0. To avoid wrong PSR values we always get RCLK frequency from the CLK_I2S_RCLK_SRC clock, when that clock is available. Fixes: e1417fdf3011 "ASoC: samsung: i2s: Ensure the RCLK rate is properly determined" Signed-off-by: Sylwester Nawrocki Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index b6407fbabdd1..f914ed45db7d 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -887,7 +887,7 @@ static int config_setup(struct i2s_dai *i2s) if (!(i2s->quirks & QUIRK_NO_MUXPSR)) { struct clk *rclksrc = i2s->clk_table[CLK_I2S_RCLK_SRC]; - if (i2s->rclk_srcrate == 0 && rclksrc && !IS_ERR(rclksrc)) + if (rclksrc && !IS_ERR(rclksrc)) i2s->rclk_srcrate = clk_get_rate(rclksrc); psr = i2s->rclk_srcrate / i2s->frmclk / rfs; -- cgit v1.2.3 From ca7796683ff63fb1917d205d4e0173c5dadfb37d Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 9 Mar 2018 18:48:57 +0100 Subject: ASoC: samsung: Drop uneeded RCLKSRC setting in the Snow driver The RCLKSRC mux input 0 is a default configuration after reset, so there is no need for this explicit snd_soc_dai_set_sysclk() call. Also, this static mux clock configuration can be specified in DT. Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/snow.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/samsung/snow.c b/sound/soc/samsung/snow.c index d8ac907bbb0d..fc62110f500f 100644 --- a/sound/soc/samsung/snow.c +++ b/sound/soc/samsung/snow.c @@ -50,11 +50,6 @@ static int snow_late_probe(struct snd_soc_card *card) if (ret < 0) return ret; - /* Select I2S Bus clock to set RCLK and BCLK */ - ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0, - 0, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; return 0; } -- cgit v1.2.3 From aff8d2bed9e5a33f40611063de62003c0f3abe9f Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 12 Mar 2018 19:49:39 +0100 Subject: ASoC: samsung: Add HDMI audio support for Snow This patch updates the driver so, in addition to current DT bindings, it also can also use the new DT bindings with cpu, codec subnodes which allow to support sound on the HDMI interface. Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/snow.c | 224 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 177 insertions(+), 47 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/samsung/snow.c b/sound/soc/samsung/snow.c index fc62110f500f..5d8efc2d5c38 100644 --- a/sound/soc/samsung/snow.c +++ b/sound/soc/samsung/snow.c @@ -11,92 +11,207 @@ * General Public License for more details. */ +#include #include #include #include #include - +#include #include #include "i2s.h" #define FIN_PLL_RATE 24000000 -static struct snd_soc_dai_link snow_dai[] = { - { - .name = "Primary", - .stream_name = "Primary", - .codec_dai_name = "HiFi", - .dai_fmt = SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - }, +struct snow_priv { + struct snd_soc_dai_link dai_link; + struct clk *clk_i2s_bus; +}; + +static int snow_card_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + static const unsigned int pll_rate[] = { + 73728000U, 67737602U, 49152000U, 45158401U, 32768001U + }; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snow_priv *priv = snd_soc_card_get_drvdata(rtd->card); + int bfs, psr, rfs, bitwidth; + unsigned long int rclk; + long int freq = -EINVAL; + int ret, i; + + bitwidth = snd_pcm_format_width(params_format(params)); + if (bitwidth < 0) { + dev_err(rtd->card->dev, "Invalid bit-width: %d\n", bitwidth); + return bitwidth; + } + + if (bitwidth != 16 && bitwidth != 24) { + dev_err(rtd->card->dev, "Unsupported bit-width: %d\n", bitwidth); + return -EINVAL; + } + + bfs = 2 * bitwidth; + + switch (params_rate(params)) { + case 16000: + case 22050: + case 24000: + case 32000: + case 44100: + case 48000: + case 88200: + case 96000: + rfs = 8 * bfs; + break; + case 64000: + rfs = 384; + break; + case 8000: + case 11025: + case 12000: + rfs = 16 * bfs; + break; + default: + return -EINVAL; + } + + rclk = params_rate(params) * rfs; + + for (psr = 8; psr > 0; psr /= 2) { + for (i = 0; i < ARRAY_SIZE(pll_rate); i++) { + if ((pll_rate[i] - rclk * psr) <= 2) { + freq = pll_rate[i]; + break; + } + } + } + if (freq < 0) { + dev_err(rtd->card->dev, "Unsupported RCLK rate: %lu\n", rclk); + return -EINVAL; + } + + ret = clk_set_rate(priv->clk_i2s_bus, freq); + if (ret < 0) { + dev_err(rtd->card->dev, "I2S bus clock rate set failed\n"); + return ret; + } + + return 0; +} + +static const struct snd_soc_ops snow_card_ops = { + .hw_params = snow_card_hw_params, }; static int snow_late_probe(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; struct snd_soc_dai *codec_dai; - struct snd_soc_dai *cpu_dai; - int ret; rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name); - codec_dai = rtd->codec_dai; - cpu_dai = rtd->cpu_dai; - - /* Set the MCLK rate for the codec */ - ret = snd_soc_dai_set_sysclk(codec_dai, 0, - FIN_PLL_RATE, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; + /* In the multi-codec case codec_dais 0 is MAX98095 and 1 is HDMI. */ + if (rtd->num_codecs > 1) + codec_dai = rtd->codec_dais[0]; + else + codec_dai = rtd->codec_dai; - return 0; + /* Set the MCLK rate for the codec */ + return snd_soc_dai_set_sysclk(codec_dai, 0, + FIN_PLL_RATE, SND_SOC_CLOCK_IN); } static struct snd_soc_card snow_snd = { .name = "Snow-I2S", .owner = THIS_MODULE, - .dai_link = snow_dai, - .num_links = ARRAY_SIZE(snow_dai), - .late_probe = snow_late_probe, }; static int snow_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct snd_soc_card *card = &snow_snd; - struct device_node *i2s_node, *codec_node; - int i, ret; - - i2s_node = of_parse_phandle(pdev->dev.of_node, - "samsung,i2s-controller", 0); - if (!i2s_node) { - dev_err(&pdev->dev, - "Property 'i2s-controller' missing or invalid\n"); - return -EINVAL; - } + struct device_node *cpu, *codec; + struct snd_soc_dai_link *link; + struct snow_priv *priv; + int ret; - codec_node = of_parse_phandle(pdev->dev.of_node, - "samsung,audio-codec", 0); - if (!codec_node) { - dev_err(&pdev->dev, - "Property 'audio-codec' missing or invalid\n"); - return -EINVAL; - } + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + link = &priv->dai_link; + + link->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS; + + link->name = "Primary"; + link->stream_name = link->name; + + card->dai_link = link; + card->num_links = 1; + card->dev = dev; + + /* Try new DT bindings with HDMI support first. */ + cpu = of_get_child_by_name(dev->of_node, "cpu"); + + if (cpu) { + link->ops = &snow_card_ops; - for (i = 0; i < ARRAY_SIZE(snow_dai); i++) { - snow_dai[i].codec_of_node = codec_node; - snow_dai[i].cpu_of_node = i2s_node; - snow_dai[i].platform_of_node = i2s_node; + link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0); + of_node_put(cpu); + + if (!link->cpu_of_node) { + dev_err(dev, "Failed parsing cpu/sound-dai property\n"); + return -EINVAL; + } + + codec = of_get_child_by_name(dev->of_node, "codec"); + ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); + of_node_put(codec); + + if (ret < 0) { + of_node_put(link->cpu_of_node); + dev_err(dev, "Failed parsing codec node\n"); + return ret; + } + + priv->clk_i2s_bus = of_clk_get_by_name(link->cpu_of_node, + "i2s_opclk0"); + if (IS_ERR(priv->clk_i2s_bus)) { + snd_soc_of_put_dai_link_codecs(link); + of_node_put(link->cpu_of_node); + return PTR_ERR(priv->clk_i2s_bus); + } + } else { + link->codec_dai_name = "HiFi", + + link->cpu_of_node = of_parse_phandle(dev->of_node, + "samsung,i2s-controller", 0); + if (!link->cpu_of_node) { + dev_err(dev, "i2s-controller property parse error\n"); + return -EINVAL; + } + + link->codec_of_node = of_parse_phandle(dev->of_node, + "samsung,audio-codec", 0); + if (!link->codec_of_node) { + of_node_put(link->cpu_of_node); + dev_err(dev, "audio-codec property parse error\n"); + return -EINVAL; + } } - card->dev = &pdev->dev; + link->platform_of_node = link->cpu_of_node; /* Update card-name if provided through DT, else use default name */ snd_soc_of_parse_card_name(card, "samsung,model"); - ret = devm_snd_soc_register_card(&pdev->dev, card); + snd_soc_card_set_drvdata(card, priv); + + ret = devm_snd_soc_register_card(dev, card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); return ret; @@ -105,6 +220,20 @@ static int snow_probe(struct platform_device *pdev) return ret; } +static int snow_remove(struct platform_device *pdev) +{ + struct snow_priv *priv = platform_get_drvdata(pdev); + struct snd_soc_dai_link *link = &priv->dai_link; + + of_node_put(link->cpu_of_node); + of_node_put(link->codec_of_node); + snd_soc_of_put_dai_link_codecs(link); + + clk_put(priv->clk_i2s_bus); + + return 0; +} + static const struct of_device_id snow_of_match[] = { { .compatible = "google,snow-audio-max98090", }, { .compatible = "google,snow-audio-max98091", }, @@ -120,6 +249,7 @@ static struct platform_driver snow_driver = { .of_match_table = snow_of_match, }, .probe = snow_probe, + .remove = snow_remove, }; module_platform_driver(snow_driver); -- cgit v1.2.3 From 1d22c337dc8f3a25638f7262e7bcb5729a34d140 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 14 Mar 2018 17:41:13 +0100 Subject: ASoC: samsung: odroid: Fix 32000 sample rate handling In case of sample rates lower than 44100 currently there is too low MCLK frequency set for the CODEC. Playback fails with following errors: $ speaker-test -c2 -t sine -f 1500 -l2 -r 32000 Sine wave rate is 1500.0000Hz Rate set to 32000Hz (requested 32000Hz) Buffer size range from 128 to 131072 Period size range from 64 to 65536 Using max buffer size 131072 Periods = 4 Unable to set hw params for playback: Invalid argument Setting of hwparams failed: Invalid argument [ 497.883700] max98090 1-0010: Invalid master clock frequency To fix this the I2S root clock's frequency is increased, depending on sampling rate. Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/odroid.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c index 92d750806d1d..e698e67f5f84 100644 --- a/sound/soc/samsung/odroid.c +++ b/sound/soc/samsung/odroid.c @@ -36,23 +36,26 @@ static int odroid_card_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card); - unsigned int pll_freq, rclk_freq; + unsigned int pll_freq, rclk_freq, rfs; int ret; switch (params_rate(params)) { - case 32000: case 64000: - pll_freq = 131072006U; + pll_freq = 196608001U; + rfs = 384; break; case 44100: case 88200: case 176400: pll_freq = 180633609U; + rfs = 512; break; + case 32000: case 48000: case 96000: case 192000: pll_freq = 196608001U; + rfs = 512; break; default: return -EINVAL; @@ -67,7 +70,7 @@ static int odroid_card_hw_params(struct snd_pcm_substream *substream, * frequency values due to the EPLL output frequency not being exact * multiple of the audio sampling rate. */ - rclk_freq = params_rate(params) * 256 + 1; + rclk_freq = params_rate(params) * rfs + 1; ret = clk_set_rate(priv->sclk_i2s, rclk_freq); if (ret < 0) -- cgit v1.2.3 From 23952006ace5a54caddcda3de6f7676dfe520e86 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 14 Mar 2018 17:41:14 +0100 Subject: ASoC: samsung: odroid: Drop sample rates that cannot be supported from hw_params() The I2S controller can handle sample rates only up to 96000 and the CPU DAI has already related constraint set so drop the impossible 176400, 192000 switch cases. Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/odroid.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c index e698e67f5f84..e1b59457a327 100644 --- a/sound/soc/samsung/odroid.c +++ b/sound/soc/samsung/odroid.c @@ -46,14 +46,12 @@ static int odroid_card_hw_params(struct snd_pcm_substream *substream, break; case 44100: case 88200: - case 176400: pll_freq = 180633609U; rfs = 512; break; case 32000: case 48000: case 96000: - case 192000: pll_freq = 196608001U; rfs = 512; break; -- cgit v1.2.3 From 4718840e76f74e5868d2001f0ca3a0e5c4292ea0 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 14 Mar 2018 17:41:15 +0100 Subject: ASoC: samsung: Use snd_soc_of_put_dai_link_codecs() in odroid.c Now when a helper for unreferencing device nodes is available we can get rid of the local implementation. Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/odroid.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c index e1b59457a327..e7b371b07230 100644 --- a/sound/soc/samsung/odroid.c +++ b/sound/soc/samsung/odroid.c @@ -91,18 +91,6 @@ static const struct snd_soc_ops odroid_card_ops = { .hw_params = odroid_card_hw_params, }; -static void odroid_put_codec_of_nodes(struct snd_soc_dai_link *link) -{ - struct snd_soc_dai_link_component *component = link->codecs; - int i; - - for (i = 0; i < link->num_codecs; i++, component++) { - if (!component->of_node) - break; - of_node_put(component->of_node); - } -} - static int odroid_audio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -197,7 +185,7 @@ err_put_sclk: err_put_i2s_n: of_node_put(link->cpu_of_node); err_put_codec_n: - odroid_put_codec_of_nodes(link); + snd_soc_of_put_dai_link_codecs(link); return ret; } @@ -206,7 +194,7 @@ static int odroid_audio_remove(struct platform_device *pdev) struct odroid_priv *priv = platform_get_drvdata(pdev); of_node_put(priv->dai_link.cpu_of_node); - odroid_put_codec_of_nodes(&priv->dai_link); + snd_soc_of_put_dai_link_codecs(&priv->dai_link); clk_put(priv->sclk_i2s); clk_put(priv->clk_i2s_bus); -- cgit v1.2.3