linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [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).