From patchwork Sun Aug 25 12:17:34 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TWljaGHFgiBNaXJvc8WCYXc=?= X-Patchwork-Id: 11113297 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A9AB114DB for ; Sun, 25 Aug 2019 12:18:46 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 836872082F for ; Sun, 25 Aug 2019 12:18:46 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="HXxhd+I7"; dkim=fail reason="signature verification failed" (2048-bit key) header.d=rere.qmqm.pl header.i=@rere.qmqm.pl header.b="irfSnMJA" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 836872082F Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=rere.qmqm.pl Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:MIME-Version:Subject:From:References :In-Reply-To:Message-Id:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=Qh4s/9DVfB3vkDzuThq8/CvzqPhNs2t/PkbGjJo/gD0=; b=HXxhd+I7wYt5bh fLtABbsfKmKZkii7vJd8RzIn5nVppfEObahmnX/topzr6CHVZoHu1NTo2TvgxlICctCT5xkCah6t4 HmT7zwvIocpul6nPGvo4CApUQRmOL8jUEzWWlBek7P70I7y4pHZDMzz13KL5iLiPQTxbJJBfYnOpr kbNGDSXRRymXAz7pKjZi+/hS2slrIwFA6raKnRV2lU7r9uYFGqS8micDeVqMw5BakpenxvQKk14Lv XzZ4HLMMNb1BlXkeHrBhSKzpZhSNTho3oTZMkdRPwB6VDj5MVHAGJT3n4m9XiUlURRmbAmWb3q+XA T/JqA00gUxSM13ML4vFQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92 #3 (Red Hat Linux)) id 1i1rTl-0004PW-1t; Sun, 25 Aug 2019 12:18:41 +0000 Received: from rere.qmqm.pl ([91.227.64.183]) by bombadil.infradead.org with esmtps (Exim 4.92 #3 (Red Hat Linux)) id 1i1rSp-0003eX-46 for linux-arm-kernel@lists.infradead.org; Sun, 25 Aug 2019 12:17:46 +0000 Received: from remote.user (localhost [127.0.0.1]) by rere.qmqm.pl (Postfix) with ESMTPSA id 46GYy71S6PzGc; Sun, 25 Aug 2019 14:15:59 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=rere.qmqm.pl; s=1; t=1566735359; bh=k+g04bWs/AHBWvnKGZhPer08vaUiFby8g4I+oyqQ/wY=; h=Date:In-Reply-To:References:From:Subject:To:Cc:From; b=irfSnMJAOAER/QWDpwp7wQXGS6mqwx60utJ7u9ZY6KZwa4MlsnZ6HtBWms9ix07Ai UIcKo1bIHG0XqkfaC7bR7apydNwN97XrQjq3ZxIpQdMQmiejRq4KmU6Cl97ZmMxmpb jCmtW6g0bIHN/AIOEDUUAr0UKL2+bWFDu7k8UBf1Ho/6qzA0+ILm1G8hPeruXP8uUN MOeFLdTPrcKItyKSyAxUaowCpm2dEg98Pp4KoDDTNR7bxrqe14BR8fj4xN3tEj1/H9 +hzivkflgS33qh0Qn/S7MotZdftK2C4cAkl4kdPbBKm3YRCdo7RWaLtEGGlPuYq/BV 3jYRDgbttLzfw== X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.101.2 at mail Date: Sun, 25 Aug 2019 14:17:34 +0200 Message-Id: In-Reply-To: References: From: =?utf-8?b?TWljaGHFgiBNaXJvc8WCYXc=?= Subject: [PATCH v2 1/4] ASoC: wm_fll: extract common code for Wolfson FLLs MIME-Version: 1.0 To: alsa-devel@alsa-project.org, patches@opensource.cirrus.com X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190825_051743_506231_466A5F14 X-CRM114-Status: GOOD ( 17.34 ) X-Spam-Score: -0.1 (/) X-Spam-Report: SpamAssassin version 3.4.2 on bombadil.infradead.org summary: Content analysis details: (-0.1 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , 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@lists.infradead.org, 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@vger.kernel.org, Enrico Weigelt Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org 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 --- 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 +#include +#include +#include +#include + +#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 +#include +#include + +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 */ From patchwork Sun Aug 25 12:17:35 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TWljaGHFgiBNaXJvc8WCYXc=?= X-Patchwork-Id: 11113299 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 43B4D14F7 for ; Sun, 25 Aug 2019 12:19:01 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 1D07E2082F for ; Sun, 25 Aug 2019 12:19:01 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="q6BCpb2k"; dkim=fail reason="signature verification failed" (2048-bit key) header.d=rere.qmqm.pl header.i=@rere.qmqm.pl header.b="aHZ8nXmp" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 1D07E2082F Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=rere.qmqm.pl Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:MIME-Version:Subject:From:References :In-Reply-To:Message-Id:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=3uokt3fnlDrvQ+v0YS0LpTbbLQLlF5af9t52O4nG5aY=; b=q6BCpb2kTEJVG2 BW/QYABzXXUqAcalrzincVZvPSGNAwSOCt0vHdA3+dHWFH8Q4HhjfzALSy/Xx3lL6YeWLYjwFOcvP kVVf6s8rqja50Nh0409hQjGwVq2LUjWFwQnYG4jSBixl9ft2K1yzjpZq3GLwxsNGUFYwfzqeR4IvL ew0wuVT8v3akQ4UxDVUwm0/+opLv8MKAW9N3PSujIvzGkYbCCs6ShXQGe+U2XAdQaC2CoA1/HvhOg BuruXQAlj4ilVmenSviMHX/NbPryCw/W3WkSBzP4jxF8l1oIouC6mUBwn5CARKpR835r3vcOiyPAA JyBFcmnV/NejkMpr6Rzw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92 #3 (Red Hat Linux)) id 1i1rU4-0004dv-7o; Sun, 25 Aug 2019 12:19:00 +0000 Received: from rere.qmqm.pl ([91.227.64.183]) by bombadil.infradead.org with esmtps (Exim 4.92 #3 (Red Hat Linux)) id 1i1rSp-0003ed-3t for linux-arm-kernel@lists.infradead.org; Sun, 25 Aug 2019 12:17:47 +0000 Received: from remote.user (localhost [127.0.0.1]) by rere.qmqm.pl (Postfix) with ESMTPSA id 46GYy85L4VzLD; Sun, 25 Aug 2019 14:16:00 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=rere.qmqm.pl; s=1; t=1566735361; bh=rDZuxcNd59H8gsxv7pZ5ojvh+ApU8FWsC3eT0ah44MY=; h=Date:In-Reply-To:References:From:Subject:To:Cc:From; b=aHZ8nXmpunGy9FLEowbXz/PAW/e5iDsED9x1HEbD97b6bel7649iDfFQnJJcLb1ZU z5bHwUAAPOYMVRbvzHSYTDYWk0X429IDIqiP1eIRMuSeMUwGP9lXl8n9RPsOq4ZLOn L7Dyg6MRzAIpjMHFLPXP9fLn88ATLwYhNl5FxCSYuTlvEBUP6HjLQfdBU6EuLT0xrt JmBSOM7DBR1IFYvhoQnhWYGxaMZKDnjkEBN9YFpldGPpbk+L1U/7jhqpN6ilGZ39Te eJhjbx8UBQilNkiFzwHe+vzU15AioWg/EF+p/z/EPIUwzPMOAAUB8XzFdQl4UOuDFl +anADmLqIGEBQ== X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.101.2 at mail Date: Sun, 25 Aug 2019 14:17:35 +0200 Message-Id: <1136f2dcc822821afda9f9533f40637647929bdf.1566734630.git.mirq-linux@rere.qmqm.pl> In-Reply-To: References: From: =?utf-8?b?TWljaGHFgiBNaXJvc8WCYXc=?= Subject: [PATCH v2 2/4] ASoC: wm8904: use common FLL code MIME-Version: 1.0 To: alsa-devel@alsa-project.org, patches@opensource.cirrus.com X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190825_051743_509760_1A3562B3 X-CRM114-Status: GOOD ( 20.64 ) X-Spam-Score: -0.1 (/) X-Spam-Report: SpamAssassin version 3.4.2 on bombadil.infradead.org summary: Content analysis details: (-0.1 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , 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@lists.infradead.org, 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@vger.kernel.org, Enrico Weigelt Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org Rework FLL handling to use common code introduced earlier. Signed-off-by: Michał Mirosław --- 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 #include +#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. */ From patchwork Sun Aug 25 12:17:37 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TWljaGHFgiBNaXJvc8WCYXc=?= X-Patchwork-Id: 11113295 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 8381D14F7 for ; Sun, 25 Aug 2019 12:18:27 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 60D9D2082F for ; Sun, 25 Aug 2019 12:18:27 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="EGFNQz62"; dkim=fail reason="signature verification failed" (2048-bit key) header.d=rere.qmqm.pl header.i=@rere.qmqm.pl header.b="AUtdOu+1" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 60D9D2082F Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=rere.qmqm.pl Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:MIME-Version:Subject:From:References :In-Reply-To:Message-Id:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=PLbjtBPAcFYw4i+P3KLkmRkMv0F4CBKbnRvIXtCHXH8=; b=EGFNQz62HzD7N0 uEQfnvakp8X5vtIrw02DyBfWNbbq+Fmfy58QHk9T6OUPne+nGOmCW3p/frTQrE3WQ+OtHp2S3KwGw Zug4ziZYdU0vJs31t2LgEwqZ/z0K2DdM5vXIaZNoiLaRPsIR2ME332Nc4ZGg0yXmwDaSJ8IiU/Oa7 6MdveSkhA0Av1FqLsJW+YD0Iuy2Ix43CXixP7ZlYk/7PPglwWzpfbiFdD4hoxZqHewLB26YiaciZ+ 430RA6n+8yysR1e0MpvsumGEnvWVRz5pgRw/tGouzm5WONNhSq8vch5IuDw8t95NllBgOwIQvoTn9 bF1ARxxsvr2y/C2Jtqyg==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92 #3 (Red Hat Linux)) id 1i1rTW-0004Al-SD; Sun, 25 Aug 2019 12:18:26 +0000 Received: from rere.qmqm.pl ([91.227.64.183]) by bombadil.infradead.org with esmtps (Exim 4.92 #3 (Red Hat Linux)) id 1i1rSp-0003ej-4M for linux-arm-kernel@lists.infradead.org; Sun, 25 Aug 2019 12:17:46 +0000 Received: from remote.user (localhost [127.0.0.1]) by rere.qmqm.pl (Postfix) with ESMTPSA id 46GYyB0Qd1zM6; Sun, 25 Aug 2019 14:16:02 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=rere.qmqm.pl; s=1; t=1566735362; bh=Al1lbbPw+zZcMOPjHh4luaJeNVrX1Hy7lFV9zPv3gns=; h=Date:In-Reply-To:References:From:Subject:To:Cc:From; b=AUtdOu+12L8K334By+FEnF5Sz8vjG/7tlzwrridx/BYCNe1nDD8qbDIXTCIGlTHC4 JJY9OdYg474V+1OT1P4kqrH9WlT1AmltPhjkpH3AXlibgJ8P8sgJGFwfVYbERr0p6z 1H0w93OxlvbHTrC9Z2AhilyUgS4AJMiBFBd4YE88ir9xv/IOUdzaTsZMENESU379Vn UBLu9T3+5CY97OzHZxoBOvux0csC2S7TpFv56DfdeRKaOirB0qhBgsbUnZPXLeiDuT J25VmB+hOeat6/A0o8USeCMQ6+TjfUl0N3DIgaMPHTQVv/RkcnVLVlVzr1EycZ86bQ zK90APevAdooQ== X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.101.2 at mail Date: Sun, 25 Aug 2019 14:17:37 +0200 Message-Id: <0b92f741a4c382cb8d50c1aebfa9da15d7e470ae.1566734631.git.mirq-linux@rere.qmqm.pl> In-Reply-To: References: From: =?utf-8?b?TWljaGHFgiBNaXJvc8WCYXc=?= Subject: [PATCH v2 3/4] ASoC: wm8904: automatically choose clock source MIME-Version: 1.0 To: alsa-devel@alsa-project.org, patches@opensource.cirrus.com X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190825_051743_518749_A3BF3C52 X-CRM114-Status: GOOD ( 13.16 ) X-Spam-Score: -0.1 (/) X-Spam-Report: SpamAssassin version 3.4.2 on bombadil.infradead.org summary: Content analysis details: (-0.1 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , 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@lists.infradead.org, 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@vger.kernel.org, Enrico Weigelt Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org Choose clock source automatically if not provided. This will be the case with eg. audio-graph-card. Signed-off-by: Michał Mirosław --- 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, From patchwork Sun Aug 25 12:17:38 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?TWljaGHFgiBNaXJvc8WCYXc=?= X-Patchwork-Id: 11113293 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A114114F7 for ; Sun, 25 Aug 2019 12:18:05 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 5F4232173E for ; Sun, 25 Aug 2019 12:18:05 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="gukweJda"; dkim=fail reason="signature verification failed" (2048-bit key) header.d=rere.qmqm.pl header.i=@rere.qmqm.pl header.b="ExzvFECv" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 5F4232173E Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=rere.qmqm.pl Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:To:MIME-Version:Subject:From:References :In-Reply-To:Message-Id:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=Vs6kCXvKH06DSMRRtMHeTLmDGywi94OsDbst1v7uvsc=; b=gukweJdaayTi71 UI0OE/0cFXWXXdrpyHqt5Ba7aN2RjwwegCnTGGVGOrHn0dxRAAG+E2xkftlMxU6cMkiYjVCbaPDYM /zk544ZgDi2pZF38oaK+Szd8qqqohs0L6FIot5mW6iGHvV2g9JzPnaKbx19+3C6z+rvbErV3GxA4+ Q+xoo7lPdjUoZ4JxZosC/cy2qaDiGyYW/XPmJMEsdiAkWeWmVmy6fPSiQX36hPTKXgrj2gemnGTRX CvHbuT8SZZtd0rHiooPbgWIcFGpyVnO2dZe0r65emyo3TU+zHe9XffMW/BD5kwH0CV+kz1vExBPZ0 7S7U+xjTwPn/FabDuI4g==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.92 #3 (Red Hat Linux)) id 1i1rT6-0003r3-El; Sun, 25 Aug 2019 12:18:00 +0000 Received: from rere.qmqm.pl ([91.227.64.183]) by bombadil.infradead.org with esmtps (Exim 4.92 #3 (Red Hat Linux)) id 1i1rSp-0003f4-2A for linux-arm-kernel@lists.infradead.org; Sun, 25 Aug 2019 12:17:45 +0000 Received: from remote.user (localhost [127.0.0.1]) by rere.qmqm.pl (Postfix) with ESMTPSA id 46GYyC0MrHzMK; Sun, 25 Aug 2019 14:16:03 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=rere.qmqm.pl; s=1; t=1566735363; bh=gDXuzaQsy05jFmx1JaMN1xelDgVOPOeqv3Tv+dwhC3Y=; h=Date:In-Reply-To:References:From:Subject:To:Cc:From; b=ExzvFECvHk3XkEZHQQAIFjtv+q2HSZhPD8FHfTbLCWyo3NfSuP7OlQo2NPSM/hLZF Qx2+4eVudsYY1BvPTSm9Wi3CDLTRDhqOrWKj0SsQrMYH77li4uZUtwXOFUwa4NHTSN OuIgF3qPoZqJ/70ZOMiPcfNLF/1ydo6tXm0JhKaYY7F8g1H0nXti7SzP0oRnEyiCI5 BbMoJXWjlvFC7720ec7IY/dOFTonWSFT1yIrYn/STUQl5p5C9vsQhpOOAIzozEH/c7 Ojv+82i9e9bdxZs6RA6H88XtRGrTMfg3nVPtKgFZ7ZYNKTcsAaLIptdvWs3Aseki4X 6eAu4O8bgv87A== X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.101.2 at mail Date: Sun, 25 Aug 2019 14:17:38 +0200 Message-Id: In-Reply-To: References: From: =?utf-8?b?TWljaGHFgiBNaXJvc8WCYXc=?= Subject: [PATCH v2 4/4] [RFT] ASoC: wm8994: use common FLL code MIME-Version: 1.0 To: alsa-devel@alsa-project.org, patches@opensource.cirrus.com X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20190825_051743_437896_74395FEC X-CRM114-Status: GOOD ( 19.55 ) X-Spam-Score: -0.1 (/) X-Spam-Report: SpamAssassin version 3.4.2 on bombadil.infradead.org summary: Content analysis details: (-0.1 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , 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@lists.infradead.org, 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@vger.kernel.org, Enrico Weigelt Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org Rework FLL handling to use common code. This uses polling for now to wait for FLL lock. Signed-off-by: Michał Mirosław --- 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 #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;