* [PATCH v2 0/4] wm8904: adapt driver for use with audio-graph-card
@ 2019-08-25 12:17 Michał Mirosław
2019-08-25 12:17 ` [PATCH v2 1/4] ASoC: wm_fll: extract common code for Wolfson FLLs Michał Mirosław
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Michał Mirosław @ 2019-08-25 12:17 UTC (permalink / raw)
To: alsa-devel, patches
Cc: Kate Stewart, Maxime Jourdan, Alexandre Belloni,
Kuninori Morimoto, Kirill Marinushkin, Liam Girdwood,
Paul Cercueil, Srinivas Kandagatla, Jerome Brunet, Anders Roxell,
Takashi Iwai, Ludovic Desroches, linux-arm-kernel,
Codrin Ciubotariu, Charles Keepax, Piotr Stankiewicz,
Annaliese McDermond, Richard Fitzgerald, Mark Brown,
Nariman Poushin, Thomas Gleixner, Jaroslav Kysela, zhong jiang,
Allison Randal, Greg Kroah-Hartman, Randy Dunlap, Nikesh Oswal,
linux-kernel, Enrico Weigelt
This series allows to use WM8904 codec as audio-graph-card component.
It starts with rework of FLL handling in the codec's driver, and as an
example includes (untested) rework for codec with similar FLL: WM8994.
Series based on tiwai/sound/for-next tree. You can also pull from:
https://rere.qmqm.pl/git/linux
branch:
wm8904
(branch includes two fixes already sent to alsa-devel, but not merged yet).
Michał Mirosław (4):
ASoC: wm_fll: extract common code for Wolfson FLLs
ASoC: wm8904: use common FLL code
ASoC: wm8904: automatically choose clock source
[RFT] ASoC: wm8994: use common FLL code
sound/soc/atmel/atmel_wm8904.c | 11 +-
sound/soc/codecs/Kconfig | 9 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/wm8904.c | 516 +++++++++++---------------------
sound/soc/codecs/wm8904.h | 5 -
sound/soc/codecs/wm8994.c | 281 +++++-------------
sound/soc/codecs/wm8994.h | 4 +-
sound/soc/codecs/wm_fll.c | 518 +++++++++++++++++++++++++++++++++
sound/soc/codecs/wm_fll.h | 60 ++++
9 files changed, 849 insertions(+), 557 deletions(-)
create mode 100644 sound/soc/codecs/wm_fll.c
create mode 100644 sound/soc/codecs/wm_fll.h
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2 1/4] ASoC: wm_fll: extract common code for Wolfson FLLs
2019-08-25 12:17 [PATCH v2 0/4] wm8904: adapt driver for use with audio-graph-card Michał Mirosław
@ 2019-08-25 12:17 ` Michał Mirosław
2019-08-25 12:17 ` [PATCH v2 2/4] ASoC: wm8904: use common FLL code Michał Mirosław
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Michał Mirosław @ 2019-08-25 12:17 UTC (permalink / raw)
To: alsa-devel, patches
Cc: Maxime Jourdan, Kate Stewart, Alexandre Belloni,
Kuninori Morimoto, Kirill Marinushkin, Liam Girdwood,
Paul Cercueil, Srinivas Kandagatla, Jerome Brunet, Anders Roxell,
Ludovic Desroches, linux-arm-kernel, Codrin Ciubotariu,
Charles Keepax, Piotr Stankiewicz, Annaliese McDermond,
Richard Fitzgerald, Mark Brown, Nariman Poushin, Thomas Gleixner,
Jaroslav Kysela, zhong jiang, Allison Randal, Greg Kroah-Hartman,
Randy Dunlap, Nikesh Oswal, Takashi Iwai, linux-kernel,
Enrico Weigelt
A new implementation for FLLs as contained in WM8904, WM8994 and a few
other Cirrus Logic (formerly Wolfson) codecs. Patches using this common
code follow.
Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
---
sound/soc/codecs/Kconfig | 6 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/wm_fll.c | 518 ++++++++++++++++++++++++++++++++++++++
sound/soc/codecs/wm_fll.h | 60 +++++
4 files changed, 586 insertions(+)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 9f89a5346299..04086acf6d93 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -281,6 +281,12 @@ config SND_SOC_ARIZONA
default m if SND_SOC_WM8997=m
default m if SND_SOC_WM8998=m
+config SND_SOC_WM_FLL
+ tristate
+
+config SND_SOC_WM_FLL_EFS
+ bool
+
config SND_SOC_WM_HUBS
tristate
default y if SND_SOC_WM8993=y || SND_SOC_WM8994=y
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 5b4bb8cf4325..22704fbb7497 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -269,6 +269,7 @@ snd-soc-wm9090-objs := wm9090.o
snd-soc-wm9705-objs := wm9705.o
snd-soc-wm9712-objs := wm9712.o
snd-soc-wm9713-objs := wm9713.o
+snd-soc-wm-fll-objs := wm_fll.o
snd-soc-wm-hubs-objs := wm_hubs.o
snd-soc-zx-aud96p22-objs := zx_aud96p22.o
# Amp
@@ -549,6 +550,7 @@ obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o
+obj-$(CONFIG_SND_SOC_WM_FLL) += snd-soc-wm-fll.o
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
obj-$(CONFIG_SND_SOC_ZX_AUD96P22) += snd-soc-zx-aud96p22.o
diff --git a/sound/soc/codecs/wm_fll.c b/sound/soc/codecs/wm_fll.c
new file mode 100644
index 000000000000..0d8217287030
--- /dev/null
+++ b/sound/soc/codecs/wm_fll.c
@@ -0,0 +1,518 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * wm_fll.c -- WM89xx FLL support
+ *
+ * Copyright 2019 Michał Mirosław
+ *
+ * WM can generate its clock directly from MCLK, from
+ * internal FLL synchronizing to one of hw frame clocks
+ * or from FLL's VCO in free-running mode
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/gcd.h>
+#include <linux/kernel.h>
+
+#include "wm_fll.h"
+
+/* FLL Control 1 */
+#define WM_FLL_CONTROL_1 (hw->desc->ctl_offset + 0)
+#define WM_FLL_FRACN_ENA BIT(2)
+#define WM_FLL_OSC_ENA BIT(1)
+#define WM_FLL_ENA BIT(0)
+
+/* FLL Control 2 */
+#define WM_FLL_CONTROL_2 (hw->desc->ctl_offset + 1)
+#define WM_FLL_OUTDIV GENMASK(13, 8)
+#define WM_FLL_CTRL_RATE GENMASK(6, 4)
+#define WM_FLL_FRATIO GENMASK(2, 0)
+
+/* FLL Control 3 */
+#define WM_FLL_CONTROL_3 (hw->desc->ctl_offset + 2)
+#define WM_FLL_K GENMASK(15, 0)
+
+/* FLL Control 4 */
+#define WM_FLL_CONTROL_4 (hw->desc->ctl_offset + 3)
+#define WM_FLL_N GENMASK(14, 5)
+#define WM_FLL_GAIN GENMASK(3, 0)
+
+/* FLL Control 5 */
+#define WM_FLL_CONTROL_5 (hw->desc->ctl_offset + 4)
+#define WM_FLL_CLK_REF_DIV GENMASK(4, 3)
+#define WM_FLL_CLK_REF_SRC GENMASK(1, 0)
+
+/* Interrupt Status */
+#define WM_INTERRUPT_STATUS (hw->desc->int_offset + 0)
+
+/* FLL NCO Test 0 (part of FLL Control 5 on some chips) */
+#define WM_FLL_NCO_TEST_0 (hw->desc->nco_reg0)
+#define WM_FLL_FRC_NCO BIT(0)
+
+/* FLL NCO Test 1 (part of FLL Control 5 on some chips) */
+#define WM_FLL_NCO_TEST_1 (hw->desc->nco_reg1)
+#define WM_FLL_FRC_NCO_VAL GENMASK(5, 0)
+
+/* FLL EFS 1 (chips with N+THETA/LAMBDA instead of N.K multiplier) */
+#define WM_FLL_EFS_1 (hw->desc->efs_offset + 0)
+#define WM_FLL_LAMBDA GENMASK(15, 0)
+
+/* FLL EFS 2 (chips with N+THETA/LAMBDA instead of N.K multiplier) */
+#define WM_FLL_EFS_2 (hw->desc->efs_offset + 1)
+#define WM_FLL_EFS_ENA BIT(0)
+
+
+/* feature tests */
+#define WM_FLL_USES_EFS(hw) (IS_ENABLED(CONFIG_SND_SOC_WM_FLL_EFS) && hw->desc->efs_offset)
+
+
+static bool wm_fll_in_free_running_mode(struct wm_fll_data *hw)
+{
+ unsigned int val;
+
+ if (!hw->desc->nco_reg0)
+ return false;
+ if (regmap_read(hw->regmap, WM_FLL_NCO_TEST_0, &val) < 0)
+ return false;
+
+ val >>= hw->desc->frc_nco_shift;
+ return FIELD_GET(WM_FLL_FRC_NCO, val);
+}
+
+static int wm_fll_set_free_running_mode(struct wm_fll_data *hw, bool enable)
+{
+ unsigned int val, mask;
+ int err;
+
+ if (!hw->desc->nco_reg0)
+ return enable ? -EINVAL : 0;
+
+ if (enable) {
+ /* set osc freq (approx 96MHz) */
+ val = FIELD_PREP(WM_FLL_FRC_NCO_VAL, 0x19);
+ mask = WM_FLL_FRC_NCO_VAL;
+
+ val <<= hw->desc->frc_nco_val_shift;
+ mask <<= hw->desc->frc_nco_val_shift;
+
+ err = regmap_update_bits(hw->regmap, WM_FLL_NCO_TEST_1,
+ mask, val);
+ if (err)
+ return err;
+ }
+
+ /* set free-running mode */
+ val = FIELD_PREP(WM_FLL_FRC_NCO, enable);
+ mask = WM_FLL_FRC_NCO;
+
+ val <<= hw->desc->frc_nco_shift;
+ mask <<= hw->desc->frc_nco_shift;
+
+ return regmap_update_bits(hw->regmap, WM_FLL_NCO_TEST_0,
+ mask, val);
+}
+
+static int wm_fll_get_parent(struct wm_fll_data *hw)
+{
+ unsigned int val;
+ int err;
+
+ /* free-running mode? */
+ if (wm_fll_in_free_running_mode(hw))
+ return FLL_REF_OSC;
+
+ err = regmap_read(hw->regmap, WM_FLL_CONTROL_5, &val);
+ if (err < 0)
+ return err;
+
+ val = FIELD_GET(WM_FLL_CLK_REF_SRC, val);
+ return hw->desc->clk_ref_map[val];
+}
+
+/**
+ * wm_fll_set_parent() - Change FLL clock source
+ *
+ * @hw: FLL hardware info
+ * @index: FLL source clock id
+ *
+ * Configures FLL for using @index clock as input.
+ *
+ * Return 0 if successful, error code if not.
+ */
+int wm_fll_set_parent(struct wm_fll_data *hw, enum wm_fll_ref_source index)
+{
+ unsigned int ref;
+ bool osc_en;
+ int err;
+
+ osc_en = index == FLL_REF_OSC;
+ err = wm_fll_set_free_running_mode(hw, osc_en);
+ if (osc_en || err)
+ return err;
+
+ err = -EINVAL;
+ for (ref = 0; ref < ARRAY_SIZE(hw->desc->clk_ref_map); ++ref) {
+ if (hw->desc->clk_ref_map[ref] != index)
+ continue;
+
+ err = 0;
+ break;
+ }
+ if (err < 0)
+ return err;
+
+ /* set FLL reference input */
+ ref = FIELD_PREP(WM_FLL_CLK_REF_SRC, ref);
+ err = regmap_update_bits(hw->regmap, WM_FLL_CONTROL_5,
+ WM_FLL_CLK_REF_SRC, ref);
+ return err;
+}
+EXPORT_SYMBOL_GPL(wm_fll_set_parent);
+
+/**
+ * wm_fll_enable() - Enable FLL
+ *
+ * @hw: initialized FLL hardware info
+ *
+ * Requests source clock and starts the FLL.
+ * Waits for lock before returning.
+ *
+ * Return 0 if successful, error code if not.
+ */
+int wm_fll_enable(struct wm_fll_data *hw)
+{
+ unsigned int val;
+ int clk_src;
+ int err, retry;
+
+ err = clk_src = wm_fll_get_parent(hw);
+ if (err < 0)
+ return err;
+
+ if (clk_src == FLL_REF_MCLK) {
+ err = clk_prepare_enable(hw->mclk);
+ if (err < 0)
+ return err;
+ }
+
+ err = regmap_update_bits(hw->regmap, WM_FLL_CONTROL_1,
+ WM_FLL_OSC_ENA, WM_FLL_OSC_ENA);
+ if (err < 0)
+ goto err_out;
+
+ err = regmap_write(hw->regmap, WM_INTERRUPT_STATUS,
+ hw->desc->int_mask);
+ if (err < 0)
+ goto err_out;
+
+ err = regmap_update_bits(hw->regmap, WM_FLL_CONTROL_1,
+ WM_FLL_ENA, WM_FLL_ENA);
+ if (err)
+ goto err_out;
+
+ if (clk_src == FLL_REF_OSC) {
+ usleep_range(150, 250);
+ return 0;
+ }
+
+ for (retry = 3; retry; --retry) {
+ msleep(1);
+ err = regmap_read(hw->regmap, WM_INTERRUPT_STATUS, &val);
+ if (err < 0)
+ goto err_out;
+
+ if (val & hw->desc->int_mask)
+ break;
+ }
+
+ /* it seems that FLL_LOCK might never be asserted */
+ /* eg. WM8904's FLL doesn't, but works anyway */
+ return 0;
+
+err_out:
+ wm_fll_disable(hw);
+
+ if (clk_src == FLL_REF_MCLK)
+ clk_disable_unprepare(hw->mclk);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(wm_fll_enable);
+
+/**
+ * wm_fll_disable() - Disable FLL
+ *
+ * @hw: initialized FLL hardware info
+ *
+ * Return 0 if successful, error code if not.
+ */
+int wm_fll_disable(struct wm_fll_data *hw)
+{
+ return regmap_update_bits(hw->regmap, WM_FLL_CONTROL_1,
+ WM_FLL_ENA|WM_FLL_OSC_ENA,
+ 0);
+}
+EXPORT_SYMBOL_GPL(wm_fll_disable);
+
+/**
+ * wm_fll_is_enabled() - Check whether FLL is enabled
+ *
+ * @hw: initialized FLL hardware info
+ *
+ * Returns 0 if disabled, 1 if enabled, or negative error code.
+ */
+int wm_fll_is_enabled(struct wm_fll_data *hw)
+{
+ unsigned int val;
+ int err;
+
+ err = regmap_read(hw->regmap, WM_FLL_CONTROL_1, &val);
+ if (err < 0)
+ return err;
+
+ return FIELD_GET(WM_FLL_ENA, val);
+}
+EXPORT_SYMBOL_GPL(wm_fll_is_enabled);
+
+static unsigned int wm_fll_apply_refdiv(unsigned long *parent_rate)
+{
+ unsigned int refdiv;
+
+ /* FLL input divider; should ensure Fin <= 13.5MHz */
+
+ refdiv = DIV_ROUND_UP(*parent_rate, 13500000);
+ refdiv = order_base_2(refdiv);
+ if (refdiv > 3)
+ refdiv = 3;
+
+ *parent_rate >>= refdiv;
+
+ return refdiv;
+}
+
+static unsigned int wm_fll_apply_fratio(unsigned long *parent_rate)
+{
+ unsigned int fratio;
+
+ /* FLL comparator divider; efectively Fin multiplier */
+ /* as tabularized in WM datasheet */
+
+ if (*parent_rate >= 256000)
+ fratio = *parent_rate < 1024000;
+ else if (*parent_rate >= 64000)
+ fratio = 2 + (*parent_rate < 128000);
+ else
+ fratio = 4;
+
+ *parent_rate <<= fratio;
+
+ return fratio;
+}
+
+static unsigned int wm_fll_apply_outdiv_rev(unsigned long *rate)
+{
+ unsigned int div;
+
+ /* Fvco -> Fout divider; target: 90 <= Fvco <= 100 MHz */
+
+ div = DIV_ROUND_UP(90000000, *rate);
+ if (div > 64) {
+ *rate = 90000000;
+ return 64;
+ }
+
+ if (div < 4)
+ div = 4;
+
+ *rate *= div;
+ return div;
+}
+
+static unsigned long wm_fll_apply_frac(struct wm_fll_data *hw,
+ unsigned long rate_in, unsigned long *rate_out,
+ unsigned int *kdiv_out)
+{
+ unsigned long long freq;
+ unsigned long rate = *rate_out;
+ unsigned int kdiv = 0x10000;
+
+ if (WM_FLL_USES_EFS(hw)) {
+ unsigned int cd, num;
+
+ cd = gcd(rate, rate_in);
+ freq = rate / rate_in;
+ num = rate - freq * rate_in;
+ num /= cd;
+ kdiv = rate_in / cd;
+
+ rate = freq * rate_in + num * rate_in / kdiv;
+ freq = (freq << 16) | num;
+ } else {
+ freq = (unsigned long long)rate << 16;
+ freq += rate_in / 2;
+ do_div(freq, rate_in);
+
+ rate = (freq * rate_in) >> 16;
+ }
+
+ *rate_out = rate;
+ *kdiv_out = kdiv;
+ return freq;
+}
+
+/**
+ * wm_fll_set_rate() - Configures FLL for specified bitrate
+ *
+ * @hw: initialized FLL hardware info
+ * @rate: bitrate to configure for
+ *
+ * Return 0 if successful, error code if not. FLL must be disabled
+ * on entry.
+ */
+int wm_fll_set_rate(struct wm_fll_data *hw, unsigned long rate)
+{
+ unsigned long freq, mclk_rate;
+ unsigned int val, mask, refdiv, outdiv, fratio, kdiv;
+ int err;
+
+ err = wm_fll_is_enabled(hw);
+ if (err < 0)
+ return err;
+ if (err > 0)
+ return -EBUSY;
+
+ err = wm_fll_get_parent(hw);
+ if (err < 0)
+ return err;
+
+ if (err != FLL_REF_OSC) {
+ unsigned long parent_rate;
+
+ if (hw->mclk)
+ hw->freq_in = clk_get_rate(hw->mclk);
+
+ parent_rate = mclk_rate = hw->freq_in;
+ refdiv = wm_fll_apply_refdiv(&parent_rate);
+ fratio = wm_fll_apply_fratio(&parent_rate);
+ outdiv = wm_fll_apply_outdiv_rev(&rate);
+ freq = wm_fll_apply_frac(hw, parent_rate, &rate, &kdiv);
+ } else {
+ unsigned long vco_rate = 96000000;
+
+ mclk_rate = 0;
+ fratio = refdiv = 0;
+ rate = DIV_ROUND_CLOSEST(vco_rate, rate);
+ outdiv = clamp_t(unsigned long, rate, 4, 64);
+ freq = 0x177 << 16;
+ kdiv = 0;
+
+ rate = vco_rate;
+ }
+
+ /* configure */
+
+ dev_dbg(regmap_get_device(hw->regmap),
+ "configuring FLL for %luHz -> %luHz -> %luHz\n",
+ mclk_rate, rate, rate / outdiv);
+ dev_dbg(regmap_get_device(hw->regmap),
+ "FLL settings: N=%lu K=%lu/%u FRATIO=%u OUTDIV=%u REF_DIV=%u\n",
+ freq >> 16, freq & 0xFFFF, kdiv, fratio, outdiv, refdiv);
+
+ val = FIELD_PREP(WM_FLL_CLK_REF_DIV, refdiv);
+ err = regmap_update_bits(hw->regmap, WM_FLL_CONTROL_5,
+ WM_FLL_CLK_REF_DIV, val);
+ if (err < 0)
+ return err;
+
+ val = FIELD_PREP(WM_FLL_OUTDIV, outdiv - 1) |
+ FIELD_PREP(WM_FLL_FRATIO, fratio);
+ mask = WM_FLL_OUTDIV | WM_FLL_FRATIO;
+ err = regmap_update_bits(hw->regmap, WM_FLL_CONTROL_2, mask, val);
+ if (err < 0)
+ return err;
+
+ err = regmap_write(hw->regmap, WM_FLL_CONTROL_3, (uint16_t)freq);
+ if (err < 0)
+ return err;
+
+ if (WM_FLL_USES_EFS(hw)) {
+ val = FIELD_PREP(WM_FLL_EFS_ENA, !!(uint16_t)freq);
+ err = regmap_update_bits(hw->regmap, WM_FLL_EFS_2,
+ WM_FLL_EFS_ENA, val);
+ if (err < 0)
+ return err;
+
+ val = FIELD_PREP(WM_FLL_LAMBDA, kdiv);
+ err = regmap_update_bits(hw->regmap, WM_FLL_EFS_1,
+ WM_FLL_LAMBDA, val);
+ } else {
+ val = FIELD_PREP(WM_FLL_FRACN_ENA, !!(uint16_t)freq);
+ err = regmap_update_bits(hw->regmap, WM_FLL_CONTROL_1,
+ WM_FLL_FRACN_ENA, val);
+ }
+ if (err < 0)
+ return err;
+
+ val = FIELD_PREP(WM_FLL_N, freq >> 16);
+ err = regmap_update_bits(hw->regmap, WM_FLL_CONTROL_4,
+ WM_FLL_N, val);
+ return err;
+}
+EXPORT_SYMBOL_GPL(wm_fll_set_rate);
+
+/**
+ * wm_fll_init() - Initialize FLL
+ *
+ * @hw: FLL hardware info
+ *
+ * Checks and initializes FLL structure.
+ * Requires hw->desc and hw->regmap to be filled in by caller.
+ *
+ * Return 0 if successful, negative error code if not.
+ * A message is logged on error.
+ */
+int wm_fll_init(struct wm_fll_data *hw)
+{
+ if (!IS_ENABLED(CONFIG_SND_SOC_WM_FLL_EFS) && hw->desc->efs_offset) {
+ struct device *dev = regmap_get_device(hw->regmap);
+
+ dev_err(dev, "FLL EFS support not compiled in\n");
+ return -ENOSYS;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_fll_init);
+
+/**
+ * wm_fll_init_with_clk() - Initialize FLL
+ *
+ * @hw: FLL hardware info
+ *
+ * Checks and initializes FLL described in @hw, and requests MCLK input clock.
+ * Requires hw->desc and hw->regmap to be filled in by caller.
+ *
+ * Return 0 if successful, negative error code if not.
+ * A message is logged on error.
+ */
+int wm_fll_init_with_clk(struct wm_fll_data *hw)
+{
+ struct device *dev = regmap_get_device(hw->regmap);
+ int err;
+
+ err = wm_fll_init(hw);
+ if (err)
+ return err;
+
+ hw->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(hw->mclk)) {
+ err = PTR_ERR(hw->mclk);
+ dev_err(dev, "Failed to get MCLK for FLL @0x%x: %d\n",
+ hw->desc->ctl_offset, err);
+ return err;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_fll_init_with_clk);
diff --git a/sound/soc/codecs/wm_fll.h b/sound/soc/codecs/wm_fll.h
new file mode 100644
index 000000000000..8519a8691397
--- /dev/null
+++ b/sound/soc/codecs/wm_fll.h
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * wm_fll.h -- FLL support for Wolfson codecs
+ *
+ * Copyright 2019 Michał Mirosław
+ */
+
+#ifndef _WM_FLL_H
+#define _WM_FLL_H
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+
+enum wm_fll_ref_source
+{
+ FLL_REF_MCLK = 1,
+ FLL_REF_MCLK2,
+ FLL_REF_BCLK,
+ FLL_REF_FSCLK,
+ FLL_REF_OSC,
+};
+
+/**
+ * struct wm_fll_desc - FLL variant description
+ *
+ * @offset: FLL control register block offset
+ * @clk_ref_map: FLL_REF_* assignment for each of FLL.REF_SRC field value
+ */
+struct wm_fll_desc
+{
+ uint16_t ctl_offset;
+ uint16_t int_offset;
+ uint16_t int_mask;
+ uint16_t nco_reg0;
+ uint16_t nco_reg1;
+ uint8_t frc_nco_shift;
+ uint8_t frc_nco_val_shift;
+ uint16_t efs_offset;
+ uint8_t clk_ref_map[4];
+};
+
+struct wm_fll_data
+{
+ const struct wm_fll_desc *desc;
+ struct regmap *regmap;
+ unsigned long freq_in;
+
+ struct clk *mclk;
+};
+
+int wm_fll_init(struct wm_fll_data *hw);
+int wm_fll_init_with_clk(struct wm_fll_data *hw);
+int wm_fll_is_enabled(struct wm_fll_data *hw);
+int wm_fll_enable(struct wm_fll_data *hw);
+int wm_fll_disable(struct wm_fll_data *hw);
+int wm_fll_set_parent(struct wm_fll_data *hw, enum wm_fll_ref_source index);
+int wm_fll_set_rate(struct wm_fll_data *hw, unsigned long rate);
+
+#endif /* _WM_FLL_H */
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 2/4] ASoC: wm8904: use common FLL code
2019-08-25 12:17 [PATCH v2 0/4] wm8904: adapt driver for use with audio-graph-card Michał Mirosław
2019-08-25 12:17 ` [PATCH v2 1/4] ASoC: wm_fll: extract common code for Wolfson FLLs Michał Mirosław
@ 2019-08-25 12:17 ` Michał Mirosław
2019-08-25 12:17 ` [PATCH v2 3/4] ASoC: wm8904: automatically choose clock source Michał Mirosław
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Michał Mirosław @ 2019-08-25 12:17 UTC (permalink / raw)
To: alsa-devel, patches
Cc: Maxime Jourdan, Kate Stewart, Alexandre Belloni,
Kuninori Morimoto, Kirill Marinushkin, Paul Cercueil,
Srinivas Kandagatla, Jerome Brunet, Anders Roxell, Takashi Iwai,
Ludovic Desroches, linux-arm-kernel, Codrin Ciubotariu,
Charles Keepax, Piotr Stankiewicz, Annaliese McDermond,
Richard Fitzgerald, Mark Brown, Nariman Poushin, Thomas Gleixner,
Jaroslav Kysela, zhong jiang, Allison Randal, Greg Kroah-Hartman,
Randy Dunlap, Nikesh Oswal, Liam Girdwood, linux-kernel,
Enrico Weigelt
Rework FLL handling to use common code introduced earlier.
Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
---
sound/soc/atmel/atmel_wm8904.c | 11 +-
sound/soc/codecs/Kconfig | 1 +
sound/soc/codecs/wm8904.c | 476 ++++++++++-----------------------
sound/soc/codecs/wm8904.h | 5 -
4 files changed, 140 insertions(+), 353 deletions(-)
diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c
index 776b27d3686e..b77ea2495efe 100644
--- a/sound/soc/atmel/atmel_wm8904.c
+++ b/sound/soc/atmel/atmel_wm8904.c
@@ -30,20 +30,11 @@ static int atmel_asoc_wm8904_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int ret;
- ret = snd_soc_dai_set_pll(codec_dai, WM8904_FLL_MCLK, WM8904_FLL_MCLK,
- 32768, params_rate(params) * 256);
- if (ret < 0) {
- pr_err("%s - failed to set wm8904 codec PLL.", __func__);
- return ret;
- }
-
/*
* As here wm8904 use FLL output as its system clock
- * so calling set_sysclk won't care freq parameter
- * then we pass 0
*/
ret = snd_soc_dai_set_sysclk(codec_dai, WM8904_CLK_FLL,
- 0, SND_SOC_CLOCK_IN);
+ params_rate(params) * 256, SND_SOC_CLOCK_IN);
if (ret < 0) {
pr_err("%s -failed to set wm8904 SYSCLK\n", __func__);
return ret;
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 04086acf6d93..1a680023af7d 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -1331,6 +1331,7 @@ config SND_SOC_WM8903
config SND_SOC_WM8904
tristate "Wolfson Microelectronics WM8904 CODEC"
depends on I2C
+ select SND_SOC_WM_FLL
config SND_SOC_WM8940
tristate
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index bcb3c9d5abf0..c9318fe34f91 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -24,6 +24,7 @@
#include <sound/tlv.h>
#include <sound/wm8904.h>
+#include "wm_fll.h"
#include "wm8904.h"
enum wm8904_type {
@@ -66,12 +67,8 @@ struct wm8904_priv {
int retune_mobile_cfg;
struct soc_enum retune_mobile_enum;
- /* FLL setup */
- int fll_src;
- int fll_fref;
- int fll_fout;
-
/* Clocking configuration */
+ struct wm_fll_data fll;
unsigned int mclk_rate;
int sysclk_src;
unsigned int sysclk_rate;
@@ -311,35 +308,111 @@ static bool wm8904_readable_register(struct device *dev, unsigned int reg)
}
}
-static int wm8904_configure_clocking(struct snd_soc_component *component)
+static void wm8904_unprepare_sysclk(struct wm8904_priv *priv)
{
+ switch (priv->sysclk_src) {
+ case WM8904_CLK_MCLK:
+ clk_disable_unprepare(priv->mclk);
+ break;
+
+ case WM8904_CLK_FLL:
+ wm_fll_disable(&priv->fll);
+ break;
+ }
+}
+
+static int wm8904_prepare_sysclk(struct wm8904_priv *priv)
+{
+ int err;
+
+ switch (priv->sysclk_src) {
+ case WM8904_CLK_MCLK:
+ err = clk_set_rate(priv->mclk, priv->mclk_rate);
+ if (!err)
+ err = clk_prepare_enable(priv->mclk);
+ break;
+
+ case WM8904_CLK_FLL:
+ err = wm_fll_enable(&priv->fll);
+ break;
+
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+static void wm8904_disable_sysclk(struct wm8904_priv *priv)
+{
+ regmap_update_bits(priv->regmap, WM8904_CLOCK_RATES_2,
+ WM8904_CLK_SYS_ENA, 0);
+ wm8904_unprepare_sysclk(priv);
+}
+
+static int wm8904_enable_sysclk(struct wm8904_priv *priv)
+{
+ int err;
+
+ err = wm8904_prepare_sysclk(priv);
+ if (err < 0)
+ return err;
+
+ err = regmap_update_bits(priv->regmap, WM8904_CLOCK_RATES_2,
+ WM8904_CLK_SYS_ENA_MASK, WM8904_CLK_SYS_ENA);
+ if (err < 0)
+ wm8904_unprepare_sysclk(priv);
+
+ return err;
+}
+
+static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int rate, int dir)
+{
+ struct snd_soc_component *component = dai->component;
struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
- unsigned int clock0, clock2, rate;
+ unsigned int clock0, clock2;
+ int err;
+
+ switch (clk_id) {
+ case WM8904_CLK_MCLK:
+ case WM8904_CLK_FLL:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (clk_id == wm8904->sysclk_src && rate == wm8904->mclk_rate)
+ return 0;
+
+ dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, rate);
/* Gate the clock while we're updating to avoid misclocking */
clock2 = snd_soc_component_read32(component, WM8904_CLOCK_RATES_2);
- snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2,
- WM8904_SYSCLK_SRC, 0);
+ wm8904_disable_sysclk(wm8904);
+
+ wm8904->sysclk_src = clk_id;
+ wm8904->mclk_rate = rate;
- /* This should be done on init() for bypass paths */
switch (wm8904->sysclk_src) {
case WM8904_CLK_MCLK:
- dev_dbg(component->dev, "Using %dHz MCLK\n", wm8904->mclk_rate);
+ dev_dbg(component->dev, "Using %dHz MCLK\n", rate);
clock2 &= ~WM8904_SYSCLK_SRC;
- rate = wm8904->mclk_rate;
-
- /* Ensure the FLL is stopped */
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
- WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
break;
case WM8904_CLK_FLL:
- dev_dbg(component->dev, "Using %dHz FLL clock\n",
- wm8904->fll_fout);
+ err = wm_fll_set_rate(&wm8904->fll, rate);
+ if (err < 0) {
+ dev_err(component->dev, "Failed to set FLL rate: %d\n", err);
+ return err;
+ }
+
+ dev_dbg(component->dev, "Using %dHz FLL clock\n", rate);
clock2 |= WM8904_SYSCLK_SRC;
- rate = wm8904->fll_fout;
break;
default:
@@ -356,11 +429,18 @@ static int wm8904_configure_clocking(struct snd_soc_component *component)
wm8904->sysclk_rate = rate;
}
- snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_0, WM8904_MCLK_DIV,
- clock0);
-
+ snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_0,
+ WM8904_MCLK_DIV, clock0);
snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2,
- WM8904_CLK_SYS_ENA | WM8904_SYSCLK_SRC, clock2);
+ WM8904_SYSCLK_SRC, clock2);
+
+ if (clock2 & WM8904_CLK_SYS_ENA) {
+ err = wm8904_enable_sysclk(wm8904);
+ if (err < 0) {
+ dev_err(component->dev, "Failed to reenable CLK_SYS: %d\n", err);
+ return err;
+ }
+ }
dev_dbg(component->dev, "CLK_SYS is %dHz\n", wm8904->sysclk_rate);
@@ -655,33 +735,21 @@ static int sysclk_event(struct snd_soc_dapm_widget *w,
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- /* If we're using the FLL then we only start it when
- * required; we assume that the configuration has been
- * done previously and all we need to do is kick it
- * off.
- */
- switch (wm8904->sysclk_src) {
- case WM8904_CLK_FLL:
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
- WM8904_FLL_OSC_ENA,
- WM8904_FLL_OSC_ENA);
-
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
- WM8904_FLL_ENA,
- WM8904_FLL_ENA);
- break;
-
- default:
- break;
- }
+ ret = wm8904_prepare_sysclk(wm8904);
+ if (ret)
+ dev_err(component->dev,
+ "Failed to prepare SYSCLK: %d\n", ret);
+ else
+ dev_dbg(component->dev, "SYSCLK on\n");
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
- WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
+ wm8904_unprepare_sysclk(wm8904);
+ dev_dbg(component->dev, "SYSCLK off\n");
break;
}
@@ -1289,7 +1357,7 @@ static int wm8904_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
- int ret, i, best, best_val, cur_val;
+ int i, best, best_val, cur_val;
unsigned int aif1 = 0;
unsigned int aif2 = 0;
unsigned int aif3 = 0;
@@ -1324,13 +1392,8 @@ static int wm8904_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
-
dev_dbg(component->dev, "Target BCLK is %dHz\n", wm8904->bclk);
- ret = wm8904_configure_clocking(component);
- if (ret != 0)
- return ret;
-
/* Select nearest CLK_SYS_RATE */
best = 0;
best_val = abs((wm8904->sysclk_rate / clk_sys_rates[0].ratio)
@@ -1382,8 +1445,8 @@ static int wm8904_hw_params(struct snd_pcm_substream *substream,
}
}
wm8904->bclk = (wm8904->sysclk_rate * 10) / bclk_divs[best].div;
- dev_dbg(component->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n",
- bclk_divs[best].div, wm8904->bclk);
+ dev_dbg(component->dev, "Selected BCLK_DIV of %d.%d for %dHz BCLK\n",
+ bclk_divs[best].div / 10, bclk_divs[best].div % 10, wm8904->bclk);
aif2 |= bclk_divs[best].bclk_div;
/* LRCLK is a simple fraction of BCLK */
@@ -1410,34 +1473,6 @@ static int wm8904_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-
-static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id,
- unsigned int freq, int dir)
-{
- struct snd_soc_component *component = dai->component;
- struct wm8904_priv *priv = snd_soc_component_get_drvdata(component);
-
- switch (clk_id) {
- case WM8904_CLK_MCLK:
- priv->sysclk_src = clk_id;
- priv->mclk_rate = freq;
- break;
-
- case WM8904_CLK_FLL:
- priv->sysclk_src = clk_id;
- break;
-
- default:
- return -EINVAL;
- }
-
- dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
-
- wm8904_configure_clocking(component);
-
- return 0;
-}
-
static int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_component *component = dai->component;
@@ -1577,253 +1612,6 @@ static int wm8904_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return 0;
}
-struct _fll_div {
- u16 fll_fratio;
- u16 fll_outdiv;
- u16 fll_clk_ref_div;
- u16 n;
- u16 k;
-};
-
-/* The size in bits of the FLL divide multiplied by 10
- * to allow rounding later */
-#define FIXED_FLL_SIZE ((1 << 16) * 10)
-
-static struct {
- unsigned int min;
- unsigned int max;
- u16 fll_fratio;
- int ratio;
-} fll_fratios[] = {
- { 0, 64000, 4, 16 },
- { 64000, 128000, 3, 8 },
- { 128000, 256000, 2, 4 },
- { 256000, 1000000, 1, 2 },
- { 1000000, 13500000, 0, 1 },
-};
-
-static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
- unsigned int Fout)
-{
- u64 Kpart;
- unsigned int K, Ndiv, Nmod, target;
- unsigned int div;
- int i;
-
- /* Fref must be <=13.5MHz */
- div = 1;
- fll_div->fll_clk_ref_div = 0;
- while ((Fref / div) > 13500000) {
- div *= 2;
- fll_div->fll_clk_ref_div++;
-
- if (div > 8) {
- pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
- Fref);
- return -EINVAL;
- }
- }
-
- pr_debug("Fref=%u Fout=%u\n", Fref, Fout);
-
- /* Apply the division for our remaining calculations */
- Fref /= div;
-
- /* Fvco should be 90-100MHz; don't check the upper bound */
- div = 4;
- while (Fout * div < 90000000) {
- div++;
- if (div > 64) {
- pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
- Fout);
- return -EINVAL;
- }
- }
- target = Fout * div;
- fll_div->fll_outdiv = div - 1;
-
- pr_debug("Fvco=%dHz\n", target);
-
- /* Find an appropriate FLL_FRATIO and factor it out of the target */
- for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
- if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
- fll_div->fll_fratio = fll_fratios[i].fll_fratio;
- target /= fll_fratios[i].ratio;
- break;
- }
- }
- if (i == ARRAY_SIZE(fll_fratios)) {
- pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
- return -EINVAL;
- }
-
- /* Now, calculate N.K */
- Ndiv = target / Fref;
-
- fll_div->n = Ndiv;
- Nmod = target % Fref;
- pr_debug("Nmod=%d\n", Nmod);
-
- /* Calculate fractional part - scale up so we can round. */
- Kpart = FIXED_FLL_SIZE * (long long)Nmod;
-
- do_div(Kpart, Fref);
-
- K = Kpart & 0xFFFFFFFF;
-
- if ((K % 10) >= 5)
- K += 5;
-
- /* Move down to proper range now rounding is done */
- fll_div->k = K / 10;
-
- pr_debug("N=%x K=%x FLL_FRATIO=%x FLL_OUTDIV=%x FLL_CLK_REF_DIV=%x\n",
- fll_div->n, fll_div->k,
- fll_div->fll_fratio, fll_div->fll_outdiv,
- fll_div->fll_clk_ref_div);
-
- return 0;
-}
-
-static int wm8904_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
- unsigned int Fref, unsigned int Fout)
-{
- struct snd_soc_component *component = dai->component;
- struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
- struct _fll_div fll_div;
- int ret, val;
- int clock2, fll1;
-
- /* Any change? */
- if (source == wm8904->fll_src && Fref == wm8904->fll_fref &&
- Fout == wm8904->fll_fout)
- return 0;
-
- clock2 = snd_soc_component_read32(component, WM8904_CLOCK_RATES_2);
-
- if (Fout == 0) {
- dev_dbg(component->dev, "FLL disabled\n");
-
- wm8904->fll_fref = 0;
- wm8904->fll_fout = 0;
-
- /* Gate SYSCLK to avoid glitches */
- snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2,
- WM8904_CLK_SYS_ENA, 0);
-
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
- WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
-
- goto out;
- }
-
- /* Validate the FLL ID */
- switch (source) {
- case WM8904_FLL_MCLK:
- case WM8904_FLL_LRCLK:
- case WM8904_FLL_BCLK:
- ret = fll_factors(&fll_div, Fref, Fout);
- if (ret != 0)
- return ret;
- break;
-
- case WM8904_FLL_FREE_RUNNING:
- dev_dbg(component->dev, "Using free running FLL\n");
- /* Force 12MHz and output/4 for now */
- Fout = 12000000;
- Fref = 12000000;
-
- memset(&fll_div, 0, sizeof(fll_div));
- fll_div.fll_outdiv = 3;
- break;
-
- default:
- dev_err(component->dev, "Unknown FLL ID %d\n", fll_id);
- return -EINVAL;
- }
-
- /* Save current state then disable the FLL and SYSCLK to avoid
- * misclocking */
- fll1 = snd_soc_component_read32(component, WM8904_FLL_CONTROL_1);
- snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2,
- WM8904_CLK_SYS_ENA, 0);
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
- WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
-
- /* Unlock forced oscilator control to switch it on/off */
- snd_soc_component_update_bits(component, WM8904_CONTROL_INTERFACE_TEST_1,
- WM8904_USER_KEY, WM8904_USER_KEY);
-
- if (fll_id == WM8904_FLL_FREE_RUNNING) {
- val = WM8904_FLL_FRC_NCO;
- } else {
- val = 0;
- }
-
- snd_soc_component_update_bits(component, WM8904_FLL_NCO_TEST_1, WM8904_FLL_FRC_NCO,
- val);
- snd_soc_component_update_bits(component, WM8904_CONTROL_INTERFACE_TEST_1,
- WM8904_USER_KEY, 0);
-
- switch (fll_id) {
- case WM8904_FLL_MCLK:
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_5,
- WM8904_FLL_CLK_REF_SRC_MASK, 0);
- break;
-
- case WM8904_FLL_LRCLK:
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_5,
- WM8904_FLL_CLK_REF_SRC_MASK, 1);
- break;
-
- case WM8904_FLL_BCLK:
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_5,
- WM8904_FLL_CLK_REF_SRC_MASK, 2);
- break;
- }
-
- if (fll_div.k)
- val = WM8904_FLL_FRACN_ENA;
- else
- val = 0;
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
- WM8904_FLL_FRACN_ENA, val);
-
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_2,
- WM8904_FLL_OUTDIV_MASK | WM8904_FLL_FRATIO_MASK,
- (fll_div.fll_outdiv << WM8904_FLL_OUTDIV_SHIFT) |
- (fll_div.fll_fratio << WM8904_FLL_FRATIO_SHIFT));
-
- snd_soc_component_write(component, WM8904_FLL_CONTROL_3, fll_div.k);
-
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_4, WM8904_FLL_N_MASK,
- fll_div.n << WM8904_FLL_N_SHIFT);
-
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_5,
- WM8904_FLL_CLK_REF_DIV_MASK,
- fll_div.fll_clk_ref_div
- << WM8904_FLL_CLK_REF_DIV_SHIFT);
-
- dev_dbg(component->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
-
- wm8904->fll_fref = Fref;
- wm8904->fll_fout = Fout;
- wm8904->fll_src = source;
-
- /* Enable the FLL if it was previously active */
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
- WM8904_FLL_OSC_ENA, fll1);
- snd_soc_component_update_bits(component, WM8904_FLL_CONTROL_1,
- WM8904_FLL_ENA, fll1);
-
-out:
- /* Reenable SYSCLK if it was previously active */
- snd_soc_component_update_bits(component, WM8904_CLOCK_RATES_2,
- WM8904_CLK_SYS_ENA, clock2);
-
- return 0;
-}
-
static int wm8904_digital_mute(struct snd_soc_dai *codec_dai, int mute)
{
struct snd_soc_component *component = codec_dai->component;
@@ -1871,15 +1659,6 @@ static int wm8904_set_bias_level(struct snd_soc_component *component,
return ret;
}
- ret = clk_prepare_enable(wm8904->mclk);
- if (ret) {
- dev_err(component->dev,
- "Failed to enable MCLK: %d\n", ret);
- regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies),
- wm8904->supplies);
- return ret;
- }
-
regcache_cache_only(wm8904->regmap, false);
regcache_sync(wm8904->regmap);
@@ -1922,7 +1701,6 @@ static int wm8904_set_bias_level(struct snd_soc_component *component,
regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies),
wm8904->supplies);
- clk_disable_unprepare(wm8904->mclk);
break;
}
return 0;
@@ -1937,7 +1715,6 @@ static const struct snd_soc_dai_ops wm8904_dai_ops = {
.set_sysclk = wm8904_set_sysclk,
.set_fmt = wm8904_set_fmt,
.set_tdm_slot = wm8904_set_tdm_slot,
- .set_pll = wm8904_set_fll,
.hw_params = wm8904_hw_params,
.digital_mute = wm8904_digital_mute,
};
@@ -2123,6 +1900,15 @@ static const struct regmap_config wm8904_regmap = {
.num_reg_defaults = ARRAY_SIZE(wm8904_reg_defaults),
};
+static const struct wm_fll_desc wm8904_fll_desc = {
+ .ctl_offset = WM8904_FLL_CONTROL_1,
+ .int_offset = WM8904_INTERRUPT_STATUS,
+ .int_mask = WM8904_FLL_LOCK_EINT_MASK,
+ .nco_reg0 = WM8904_FLL_NCO_TEST_0,
+ .nco_reg1 = WM8904_FLL_NCO_TEST_1,
+ .clk_ref_map = { FLL_REF_MCLK, FLL_REF_BCLK, FLL_REF_FSCLK, /* reserved */ 0 },
+};
+
#ifdef CONFIG_OF
static const struct of_device_id wm8904_of_match[] = {
{
@@ -2165,6 +1951,19 @@ static int wm8904_i2c_probe(struct i2c_client *i2c,
return ret;
}
+ wm8904->fll.regmap = wm8904->regmap;
+ wm8904->fll.desc = &wm8904_fll_desc;
+ ret = wm_fll_init_with_clk(&wm8904->fll);
+ if (ret)
+ return ret;
+
+ ret = wm_fll_set_parent(&wm8904->fll, FLL_REF_MCLK);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to select MCLK as FLL input: %d\n",
+ ret);
+ return ret;
+ }
+
if (i2c->dev.of_node) {
const struct of_device_id *match;
@@ -2276,6 +2075,7 @@ static int wm8904_i2c_probe(struct i2c_client *i2c,
WM8904_POBCTRL, 0);
/* Can leave the device powered off until we need it */
+ wm8904_disable_sysclk(wm8904);
regcache_cache_only(wm8904->regmap, true);
regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
diff --git a/sound/soc/codecs/wm8904.h b/sound/soc/codecs/wm8904.h
index c1bca52f9927..60af09e0bb15 100644
--- a/sound/soc/codecs/wm8904.h
+++ b/sound/soc/codecs/wm8904.h
@@ -13,11 +13,6 @@
#define WM8904_CLK_MCLK 1
#define WM8904_CLK_FLL 2
-#define WM8904_FLL_MCLK 1
-#define WM8904_FLL_BCLK 2
-#define WM8904_FLL_LRCLK 3
-#define WM8904_FLL_FREE_RUNNING 4
-
/*
* Register values.
*/
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 3/4] ASoC: wm8904: automatically choose clock source
2019-08-25 12:17 [PATCH v2 0/4] wm8904: adapt driver for use with audio-graph-card Michał Mirosław
2019-08-25 12:17 ` [PATCH v2 1/4] ASoC: wm_fll: extract common code for Wolfson FLLs Michał Mirosław
2019-08-25 12:17 ` [PATCH v2 2/4] ASoC: wm8904: use common FLL code Michał Mirosław
@ 2019-08-25 12:17 ` Michał Mirosław
2019-08-25 12:17 ` [PATCH v2 4/4] [RFT] ASoC: wm8994: use common FLL code Michał Mirosław
2019-08-27 18:40 ` [PATCH v2 0/4] wm8904: adapt driver for use with audio-graph-card Mark Brown
4 siblings, 0 replies; 6+ messages in thread
From: Michał Mirosław @ 2019-08-25 12:17 UTC (permalink / raw)
To: alsa-devel, patches
Cc: Kate Stewart, Maxime Jourdan, Alexandre Belloni,
Kuninori Morimoto, Kirill Marinushkin, Takashi Iwai,
Paul Cercueil, Srinivas Kandagatla, Jerome Brunet, Anders Roxell,
Ludovic Desroches, linux-arm-kernel, Codrin Ciubotariu,
Charles Keepax, Liam Girdwood, Piotr Stankiewicz,
Annaliese McDermond, Richard Fitzgerald, Mark Brown,
Nariman Poushin, Thomas Gleixner, Jaroslav Kysela, zhong jiang,
Allison Randal, Greg Kroah-Hartman, Randy Dunlap, Nikesh Oswal,
linux-kernel, Enrico Weigelt
Choose clock source automatically if not provided. This will be the case
with eg. audio-graph-card.
Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
---
sound/soc/codecs/wm8904.c | 42 +++++++++++++++++++++++++++++++++++++--
1 file changed, 40 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index c9318fe34f91..946315d4cecf 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -367,15 +367,34 @@ static int wm8904_enable_sysclk(struct wm8904_priv *priv)
return err;
}
+static int wm8904_bump_fll_sysclk(unsigned int *rate);
+
static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int rate, int dir)
{
struct snd_soc_component *component = dai->component;
struct wm8904_priv *wm8904 = snd_soc_component_get_drvdata(component);
unsigned int clock0, clock2;
- int err;
+ int err, do_div = false;
switch (clk_id) {
+ case 0:
+ if (rate == clk_round_rate(wm8904->mclk, rate)) {
+ clk_id = WM8904_CLK_MCLK;
+ } else if (rate * 2 == clk_round_rate(wm8904->mclk, rate * 2)) {
+ rate *= 2;
+ clk_id = WM8904_CLK_MCLK;
+ do_div = true;
+ } else {
+ clk_id = WM8904_CLK_FLL;
+ err = wm8904_bump_fll_sysclk(&rate);
+ if (err) {
+ dev_dbg(component->dev, "Can't match %u over FLL 1406250 Hz minimum\n", rate);
+ return err;
+ }
+ }
+ break;
+
case WM8904_CLK_MCLK:
case WM8904_CLK_FLL:
break;
@@ -421,7 +440,9 @@ static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id,
}
/* SYSCLK shouldn't be over 13.5MHz */
- if (rate > 13500000) {
+ if (rate > 13500000)
+ do_div = true;
+ if (do_div) {
clock0 = WM8904_MCLK_DIV;
wm8904->sysclk_rate = rate / 2;
} else {
@@ -1350,6 +1371,23 @@ static struct {
{ 480, 20 },
};
+static int wm8904_bump_fll_sysclk(unsigned int *rate)
+{
+ int i;
+
+ /* bump SYSCLK rate if below minimal FLL output */
+
+ for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+ if (*rate * bclk_divs[i].div >= 1406250 * 10)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(bclk_divs))
+ return -ERANGE;
+
+ *rate = (*rate * bclk_divs[i].div) / 10;
+ return 0;
+}
static int wm8904_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v2 4/4] [RFT] ASoC: wm8994: use common FLL code
2019-08-25 12:17 [PATCH v2 0/4] wm8904: adapt driver for use with audio-graph-card Michał Mirosław
` (2 preceding siblings ...)
2019-08-25 12:17 ` [PATCH v2 3/4] ASoC: wm8904: automatically choose clock source Michał Mirosław
@ 2019-08-25 12:17 ` Michał Mirosław
2019-08-27 18:40 ` [PATCH v2 0/4] wm8904: adapt driver for use with audio-graph-card Mark Brown
4 siblings, 0 replies; 6+ messages in thread
From: Michał Mirosław @ 2019-08-25 12:17 UTC (permalink / raw)
To: alsa-devel, patches
Cc: Maxime Jourdan, Kate Stewart, Alexandre Belloni,
Kuninori Morimoto, Kirill Marinushkin, Liam Girdwood,
Paul Cercueil, Srinivas Kandagatla, Jerome Brunet, Anders Roxell,
Ludovic Desroches, linux-arm-kernel, Codrin Ciubotariu,
Charles Keepax, Piotr Stankiewicz, Nariman Poushin,
Richard Fitzgerald, Mark Brown, Annaliese McDermond,
Thomas Gleixner, Jaroslav Kysela, zhong jiang, Allison Randal,
Greg Kroah-Hartman, Randy Dunlap, Nikesh Oswal, Takashi Iwai,
linux-kernel, Enrico Weigelt
Rework FLL handling to use common code.
This uses polling for now to wait for FLL lock.
Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
---
sound/soc/codecs/Kconfig | 2 +
sound/soc/codecs/wm8994.c | 281 +++++++++++---------------------------
sound/soc/codecs/wm8994.h | 4 +-
3 files changed, 84 insertions(+), 203 deletions(-)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 1a680023af7d..1ff6290ce18d 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -1382,6 +1382,8 @@ config SND_SOC_WM8993
config SND_SOC_WM8994
tristate
+ select SND_SOC_WM_FLL
+ select SND_SOC_WM_FLL_EFS
config SND_SOC_WM8995
tristate
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index c3d06e8bc54f..d0dbc352303b 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -2030,101 +2030,57 @@ static const struct snd_soc_dapm_route wm8958_intercon[] = {
{ "AIF3ADC Mux", "Mono PCM", "Mono PCM Out Mux" },
};
-/* The size in bits of the FLL divide multiplied by 10
- * to allow rounding later */
-#define FIXED_FLL_SIZE ((1 << 16) * 10)
-
-struct fll_div {
- u16 outdiv;
- u16 n;
- u16 k;
- u16 lambda;
- u16 clk_ref_div;
- u16 fll_fratio;
+static const struct wm_fll_desc wm8994_fll_desc[2] = {
+ /* FLL1 */
+ {
+ .ctl_offset = WM8994_FLL1_CONTROL_1,
+ .int_offset = WM8994_INTERRUPT_RAW_STATUS_2,
+ .int_mask = WM8994_IM_FLL1_LOCK_EINT_MASK,
+ .nco_reg0 = WM8994_FLL1_CONTROL_5,
+ .frc_nco_shift = 6,
+ .nco_reg1 = WM8994_FLL1_CONTROL_5,
+ .frc_nco_val_shift = 7,
+ .clk_ref_map = { FLL_REF_MCLK, FLL_REF_MCLK2, FLL_REF_FSCLK, FLL_REF_BCLK },
+ },
+ /* FLL2 */
+ {
+ .ctl_offset = WM8994_FLL2_CONTROL_1,
+ .int_offset = WM8994_INTERRUPT_RAW_STATUS_2,
+ .int_mask = WM8994_IM_FLL2_LOCK_EINT_MASK,
+ .nco_reg0 = WM8994_FLL2_CONTROL_5,
+ .frc_nco_shift = 6,
+ .nco_reg1 = WM8994_FLL2_CONTROL_5,
+ .frc_nco_val_shift = 7,
+ .clk_ref_map = { FLL_REF_MCLK, FLL_REF_MCLK2, FLL_REF_FSCLK, FLL_REF_BCLK },
+ },
};
-static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll,
- int freq_in, int freq_out)
-{
- u64 Kpart;
- unsigned int K, Ndiv, Nmod, gcd_fll;
-
- pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out);
-
- /* Scale the input frequency down to <= 13.5MHz */
- fll->clk_ref_div = 0;
- while (freq_in > 13500000) {
- fll->clk_ref_div++;
- freq_in /= 2;
-
- if (fll->clk_ref_div > 3)
- return -EINVAL;
- }
- pr_debug("CLK_REF_DIV=%d, Fref=%dHz\n", fll->clk_ref_div, freq_in);
-
- /* Scale the output to give 90MHz<=Fvco<=100MHz */
- fll->outdiv = 3;
- while (freq_out * (fll->outdiv + 1) < 90000000) {
- fll->outdiv++;
- if (fll->outdiv > 63)
- return -EINVAL;
- }
- freq_out *= fll->outdiv + 1;
- pr_debug("OUTDIV=%d, Fvco=%dHz\n", fll->outdiv, freq_out);
-
- if (freq_in > 1000000) {
- fll->fll_fratio = 0;
- } else if (freq_in > 256000) {
- fll->fll_fratio = 1;
- freq_in *= 2;
- } else if (freq_in > 128000) {
- fll->fll_fratio = 2;
- freq_in *= 4;
- } else if (freq_in > 64000) {
- fll->fll_fratio = 3;
- freq_in *= 8;
- } else {
- fll->fll_fratio = 4;
- freq_in *= 16;
- }
- pr_debug("FLL_FRATIO=%d, Fref=%dHz\n", fll->fll_fratio, freq_in);
-
- /* Now, calculate N.K */
- Ndiv = freq_out / freq_in;
-
- fll->n = Ndiv;
- Nmod = freq_out % freq_in;
- pr_debug("Nmod=%d\n", Nmod);
-
- switch (control->type) {
- case WM8994:
- /* Calculate fractional part - scale up so we can round. */
- Kpart = FIXED_FLL_SIZE * (long long)Nmod;
-
- do_div(Kpart, freq_in);
-
- K = Kpart & 0xFFFFFFFF;
-
- if ((K % 10) >= 5)
- K += 5;
-
- /* Move down to proper range now rounding is done */
- fll->k = K / 10;
- fll->lambda = 0;
-
- pr_debug("N=%x K=%x\n", fll->n, fll->k);
- break;
-
- default:
- gcd_fll = gcd(freq_out, freq_in);
-
- fll->k = (freq_out - (freq_in * fll->n)) / gcd_fll;
- fll->lambda = freq_in / gcd_fll;
-
- }
-
- return 0;
-}
+static const struct wm_fll_desc wm8958_fll_desc[2] = {
+ /* FLL1 */
+ {
+ .ctl_offset = WM8994_FLL1_CONTROL_1,
+ .int_offset = WM8994_INTERRUPT_RAW_STATUS_2,
+ .int_mask = WM8994_IM_FLL1_LOCK_EINT_MASK,
+ .nco_reg0 = WM8994_FLL1_CONTROL_5,
+ .frc_nco_shift = 6,
+ .nco_reg1 = WM8994_FLL1_CONTROL_5,
+ .frc_nco_val_shift = 7,
+ .efs_offset = WM8958_FLL1_EFS_1,
+ .clk_ref_map = { FLL_REF_MCLK, FLL_REF_MCLK2, FLL_REF_FSCLK, FLL_REF_BCLK },
+ },
+ /* FLL2 */
+ {
+ .ctl_offset = WM8994_FLL2_CONTROL_1,
+ .int_offset = WM8994_INTERRUPT_RAW_STATUS_2,
+ .int_mask = WM8994_IM_FLL2_LOCK_EINT_MASK,
+ .nco_reg0 = WM8994_FLL2_CONTROL_5,
+ .frc_nco_shift = 6,
+ .nco_reg1 = WM8994_FLL2_CONTROL_5,
+ .frc_nco_val_shift = 7,
+ .efs_offset = WM8958_FLL1_EFS_1,
+ .clk_ref_map = { FLL_REF_MCLK, FLL_REF_MCLK2, FLL_REF_FSCLK, FLL_REF_BCLK },
+ },
+};
static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
unsigned int freq_in, unsigned int freq_out)
@@ -2132,9 +2088,7 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
int reg_offset, ret;
- struct fll_div fll;
u16 reg, clk1, aif_reg, aif_src;
- unsigned long timeout;
bool was_enabled;
switch (id) {
@@ -2152,9 +2106,6 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
return -EINVAL;
}
- reg = snd_soc_component_read32(component, WM8994_FLL1_CONTROL_1 + reg_offset);
- was_enabled = reg & WM8994_FLL1_ENA;
-
switch (src) {
case 0:
/* Allow no source specification when stopping */
@@ -2166,10 +2117,12 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
case WM8994_FLL_SRC_MCLK2:
case WM8994_FLL_SRC_LRCLK:
case WM8994_FLL_SRC_BCLK:
+ src = wm8994_fll_desc[0].clk_ref_map[src - WM8994_FLL_SRC_MCLK1];
break;
case WM8994_FLL_SRC_INTERNAL:
freq_in = 12000000;
freq_out = 12000000;
+ src = FLL_REF_OSC;
break;
default:
return -EINVAL;
@@ -2180,18 +2133,6 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
wm8994->fll[id].in == freq_in && wm8994->fll[id].out == freq_out)
return 0;
- /* If we're stopping the FLL redo the old config - no
- * registers will actually be written but we avoid GCC flow
- * analysis bugs spewing warnings.
- */
- if (freq_out)
- ret = wm8994_get_fll_config(control, &fll, freq_in, freq_out);
- else
- ret = wm8994_get_fll_config(control, &fll, wm8994->fll[id].in,
- wm8994->fll[id].out);
- if (ret < 0)
- return ret;
-
/* Make sure that we're not providing SYSCLK right now */
clk1 = snd_soc_component_read32(component, WM8994_CLOCKING_1);
if (clk1 & WM8994_SYSCLK_SRC)
@@ -2207,9 +2148,11 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
return -EBUSY;
}
- /* We always need to disable the FLL while reconfiguring */
- snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_1 + reg_offset,
- WM8994_FLL1_ENA, 0);
+ was_enabled = wm_fll_is_enabled(&wm8994->fll_hw[id]) > 0;
+
+ ret = wm_fll_disable(&wm8994->fll_hw[id]);
+ if (ret)
+ return ret;
if (wm8994->fll_byp && src == WM8994_FLL_SRC_BCLK &&
freq_in == freq_out && freq_out) {
@@ -2217,46 +2160,21 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_5 + reg_offset,
WM8958_FLL1_BYP, WM8958_FLL1_BYP);
goto out;
+ } else if (wm8994->fll_byp) {
+ snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_5 + reg_offset,
+ WM8958_FLL1_BYP, 0);
}
- reg = (fll.outdiv << WM8994_FLL1_OUTDIV_SHIFT) |
- (fll.fll_fratio << WM8994_FLL1_FRATIO_SHIFT);
- snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_2 + reg_offset,
- WM8994_FLL1_OUTDIV_MASK |
- WM8994_FLL1_FRATIO_MASK, reg);
-
- snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_3 + reg_offset,
- WM8994_FLL1_K_MASK, fll.k);
-
- snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_4 + reg_offset,
- WM8994_FLL1_N_MASK,
- fll.n << WM8994_FLL1_N_SHIFT);
-
- if (fll.lambda) {
- snd_soc_component_update_bits(component, WM8958_FLL1_EFS_1 + reg_offset,
- WM8958_FLL1_LAMBDA_MASK,
- fll.lambda);
- snd_soc_component_update_bits(component, WM8958_FLL1_EFS_2 + reg_offset,
- WM8958_FLL1_EFS_ENA, WM8958_FLL1_EFS_ENA);
- } else {
- snd_soc_component_update_bits(component, WM8958_FLL1_EFS_2 + reg_offset,
- WM8958_FLL1_EFS_ENA, 0);
- }
-
- snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_5 + reg_offset,
- WM8994_FLL1_FRC_NCO | WM8958_FLL1_BYP |
- WM8994_FLL1_REFCLK_DIV_MASK |
- WM8994_FLL1_REFCLK_SRC_MASK,
- ((src == WM8994_FLL_SRC_INTERNAL)
- << WM8994_FLL1_FRC_NCO_SHIFT) |
- (fll.clk_ref_div << WM8994_FLL1_REFCLK_DIV_SHIFT) |
- (src - 1));
-
- /* Clear any pending completion from a previous failure */
- try_wait_for_completion(&wm8994->fll_locked[id]);
-
- /* Enable (with fractional mode if required) */
if (freq_out) {
+ wm8994->fll_hw[id].freq_in = freq_in;
+ ret = wm_fll_set_parent(&wm8994->fll_hw[id], src);
+ if (!ret)
+ ret = wm_fll_set_rate(&wm8994->fll_hw[id], freq_out);
+ if (!ret)
+ ret = wm_fll_enable(&wm8994->fll_hw[id]);
+ if (ret < 0)
+ return ret;
+
/* Enable VMID if we need it */
if (!was_enabled) {
active_reference(component);
@@ -2273,27 +2191,6 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
break;
}
}
-
- reg = WM8994_FLL1_ENA;
-
- if (fll.k)
- reg |= WM8994_FLL1_FRAC;
- if (src == WM8994_FLL_SRC_INTERNAL)
- reg |= WM8994_FLL1_OSC_ENA;
-
- snd_soc_component_update_bits(component, WM8994_FLL1_CONTROL_1 + reg_offset,
- WM8994_FLL1_ENA | WM8994_FLL1_OSC_ENA |
- WM8994_FLL1_FRAC, reg);
-
- if (wm8994->fll_locked_irq) {
- timeout = wait_for_completion_timeout(&wm8994->fll_locked[id],
- msecs_to_jiffies(10));
- if (timeout == 0)
- dev_warn(component->dev,
- "Timed out waiting for FLL lock\n");
- } else {
- msleep(5);
- }
} else {
if (was_enabled) {
switch (control->type) {
@@ -2350,15 +2247,6 @@ static int _wm8994_set_fll(struct snd_soc_component *component, int id, int src,
return 0;
}
-static irqreturn_t wm8994_fll_locked_irq(int irq, void *data)
-{
- struct completion *completion = data;
-
- complete(completion);
-
- return IRQ_HANDLED;
-}
-
static int opclk_divs[] = { 10, 20, 30, 40, 55, 60, 80, 120, 160 };
static int wm8994_set_fll(struct snd_soc_dai *dai, int id, int src,
@@ -3992,6 +3880,18 @@ static int wm8994_component_probe(struct snd_soc_component *component)
snd_soc_component_init_regmap(component, control->regmap);
+ for (i = 0; i < ARRAY_SIZE(wm8994->fll_hw); ++i) {
+ wm8994->fll_hw[i].regmap = control->regmap;
+ if (control->type == WM8994)
+ wm8994->fll_hw[i].desc = wm8994_fll_desc;
+ else
+ wm8994->fll_hw[i].desc = wm8958_fll_desc;
+
+ ret = wm_fll_init(&wm8994->fll_hw[i]);
+ if (ret)
+ return ret;
+ }
+
wm8994->hubs.component = component;
mutex_init(&wm8994->accdet_lock);
@@ -4013,9 +3913,6 @@ static int wm8994_component_probe(struct snd_soc_component *component)
INIT_DELAYED_WORK(&wm8994->mic_complete_work, wm8958_mic_work);
- for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
- init_completion(&wm8994->fll_locked[i]);
-
wm8994->micdet_irq = control->pdata.micdet_irq;
/* By default use idle_bias_off, will override for WM8994 */
@@ -4166,16 +4063,6 @@ static int wm8994_component_probe(struct snd_soc_component *component)
break;
}
- wm8994->fll_locked_irq = true;
- for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) {
- ret = wm8994_request_irq(wm8994->wm8994,
- WM8994_IRQ_FLL1_LOCK + i,
- wm8994_fll_locked_irq, "FLL lock",
- &wm8994->fll_locked[i]);
- if (ret != 0)
- wm8994->fll_locked_irq = false;
- }
-
/* Make sure we can read from the GPIOs if they're inputs */
pm_runtime_get_sync(component->dev);
@@ -4377,9 +4264,6 @@ static int wm8994_component_probe(struct snd_soc_component *component)
wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC1_SHRT, wm8994);
if (wm8994->micdet_irq)
free_irq(wm8994->micdet_irq, wm8994);
- for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
- wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i,
- &wm8994->fll_locked[i]);
wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE,
&wm8994->hubs);
wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FIFOS_ERR, component);
@@ -4393,11 +4277,6 @@ static void wm8994_component_remove(struct snd_soc_component *component)
{
struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
struct wm8994 *control = wm8994->wm8994;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
- wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i,
- &wm8994->fll_locked[i]);
wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE,
&wm8994->hubs);
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
index 1d6f2abe1c11..9c61d95c9053 100644
--- a/sound/soc/codecs/wm8994.h
+++ b/sound/soc/codecs/wm8994.h
@@ -13,6 +13,7 @@
#include <linux/mutex.h>
#include "wm_hubs.h"
+#include "wm_fll.h"
/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */
#define WM8994_SYSCLK_MCLK1 1
@@ -80,8 +81,7 @@ struct wm8994_priv {
int aifdiv[2];
int channels[2];
struct wm8994_fll_config fll[2], fll_suspend[2];
- struct completion fll_locked[2];
- bool fll_locked_irq;
+ struct wm_fll_data fll_hw[2];
bool fll_byp;
bool clk_has_run;
--
2.20.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v2 0/4] wm8904: adapt driver for use with audio-graph-card
2019-08-25 12:17 [PATCH v2 0/4] wm8904: adapt driver for use with audio-graph-card Michał Mirosław
` (3 preceding siblings ...)
2019-08-25 12:17 ` [PATCH v2 4/4] [RFT] ASoC: wm8994: use common FLL code Michał Mirosław
@ 2019-08-27 18:40 ` Mark Brown
4 siblings, 0 replies; 6+ messages in thread
From: Mark Brown @ 2019-08-27 18:40 UTC (permalink / raw)
To: Michał Mirosław
Cc: Kate Stewart, Maxime Jourdan, alsa-devel, Kuninori Morimoto,
Kirill Marinushkin, Alexandre Belloni, Paul Cercueil,
Srinivas Kandagatla, Jerome Brunet, Anders Roxell, Takashi Iwai,
Ludovic Desroches, linux-arm-kernel, Codrin Ciubotariu,
Charles Keepax, Piotr Stankiewicz, Annaliese McDermond,
Richard Fitzgerald, Nariman Poushin, Thomas Gleixner,
Jaroslav Kysela, zhong jiang, Allison Randal, patches,
Randy Dunlap, Nikesh Oswal, Liam Girdwood, linux-kernel,
Greg Kroah-Hartman, Enrico Weigelt
[-- Attachment #1.1: Type: text/plain, Size: 625 bytes --]
On Sun, Aug 25, 2019 at 02:17:30PM +0200, Michał Mirosław wrote:
> This series allows to use WM8904 codec as audio-graph-card component.
> It starts with rework of FLL handling in the codec's driver, and as an
> example includes (untested) rework for codec with similar FLL: WM8994.
Please make some effort to focus your CC list on only relevant
people, many upstream developers get a lot of e-mail and cutting
down on that helps everyone stay more productive, too many can
also set off anti-spam software. You've sent this to a lot of
people and I'm struggling to figure out why most of them are on
the list.
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
[-- Attachment #2: Type: text/plain, Size: 176 bytes --]
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2019-08-27 18:41 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-25 12:17 [PATCH v2 0/4] wm8904: adapt driver for use with audio-graph-card Michał Mirosław
2019-08-25 12:17 ` [PATCH v2 1/4] ASoC: wm_fll: extract common code for Wolfson FLLs Michał Mirosław
2019-08-25 12:17 ` [PATCH v2 2/4] ASoC: wm8904: use common FLL code Michał Mirosław
2019-08-25 12:17 ` [PATCH v2 3/4] ASoC: wm8904: automatically choose clock source Michał Mirosław
2019-08-25 12:17 ` [PATCH v2 4/4] [RFT] ASoC: wm8994: use common FLL code Michał Mirosław
2019-08-27 18:40 ` [PATCH v2 0/4] wm8904: adapt driver for use with audio-graph-card Mark Brown
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).