summaryrefslogtreecommitdiff
path: root/sound/soc/codecs/wm8940.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-02-22 21:29:05 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2023-02-22 21:29:05 +0300
commit064d7dcf51a82b480e953a15cca47e5df0426502 (patch)
treeb34ab58ee35e827c14071024dfc79fe50a5f31e6 /sound/soc/codecs/wm8940.c
parentf3dd0c53370e70c0f9b7e931bbec12916f3bb8cc (diff)
parent7933b90b42896f5b6596e6a829bb31c5121fc2a9 (diff)
downloadlinux-064d7dcf51a82b480e953a15cca47e5df0426502.tar.xz
Merge tag 'sound-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "The majority of works in this cycle are about ASoC spread over trees. Most of them are for new devices and cleanups / refactoring works, and not much significant changes are seen in the core side. Below are some highlights: ASoC: - Continued refactoring to move into common helper functions - Lots of DT schema conversons and stylistic nits - Continued work on building out the new SOF IPC4 scheme - Continued work for Intel AVS - New drivers for Awinc AT88395, Infineon PEB2466, Iron Device SMA1303, Mediatek MT8188, Realtek RT712, Renesas IDT821034, Samsung/Tesla FSD SoC I2S, and TI TAS5720A-Q1 ALSA: - A few cleanups to make the remove callbacks to void returns - FireWire refactoring and enhancements - PCM kselftest enhancements" * tag 'sound-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (398 commits) ALSA: hda/hdmi: Register with vga_switcheroo on Dual GPU Macbooks ASoC: soc-ac97: Return correct error codes ASoC: soc-dapm.h: fixup warning struct snd_pcm_substream not declared ASoC: cs35l45: Remove separate namespace for tables ASoC: cs35l45: Remove separate tables module ASoC: soc-ac97: Convert to agnostic GPIO API ASoC: dt-bindings: renesas,rsnd.yaml: drop "dmas/dma-names" from "rcar_sound,ssi" ALSA: hda: cs35l41: Enable Amp High Pass Filter ALSA: hda: cs35l41: Ensure firmware/tuning pairs are always loaded ALSA: hda: cs35l41: Correct error condition handling ASoC: codecs: wcd934x: Use min macro for comparison and assignment ASoC: Intel: Skylake: Fix struct definition ASoC: tlv320adcx140: extend list of supported samplerates ASoC: imx-pcm-rpmsg: Remove unused variable SoC: rt5682s: Disable jack detection interrupt during suspend ASoC: SOF: Intel: hda-dsp: Set streaming flag for d0i3 ASoC: SOF: Intel: Enable d0i3 work for ipc4 ASoC: SOF: ipc4: Wake up dsp core before sending ipc msg ASoC: SOF: Intel: hda-dsp: use set_pm_gate according to ipc version ASoC: SOF: Introduce a new set_pm_gate() IPC PM op ...
Diffstat (limited to 'sound/soc/codecs/wm8940.c')
-rw-r--r--sound/soc/codecs/wm8940.c116
1 files changed, 94 insertions, 22 deletions
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index 8dac9fd88547..8eb4782c9232 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -37,7 +37,9 @@
#include "wm8940.h"
struct wm8940_priv {
- unsigned int sysclk;
+ unsigned int mclk;
+ unsigned int fs;
+
struct regmap *regmap;
};
@@ -387,17 +389,24 @@ static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
+static int wm8940_update_clocks(struct snd_soc_dai *dai);
static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
+ struct wm8940_priv *priv = snd_soc_component_get_drvdata(component);
u16 iface = snd_soc_component_read(component, WM8940_IFACE) & 0xFD9F;
u16 addcntrl = snd_soc_component_read(component, WM8940_ADDCNTRL) & 0xFFF1;
u16 companding = snd_soc_component_read(component,
WM8940_COMPANDINGCTL) & 0xFFDF;
int ret;
+ priv->fs = params_rate(params);
+ ret = wm8940_update_clocks(dai);
+ if (ret)
+ return ret;
+
/* LoutR control */
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE
&& params_channels(params) == 2)
@@ -611,24 +620,6 @@ static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
return 0;
}
-static int wm8940_set_dai_sysclk(struct snd_soc_dai *codec_dai,
- int clk_id, unsigned int freq, int dir)
-{
- struct snd_soc_component *component = codec_dai->component;
- struct wm8940_priv *wm8940 = snd_soc_component_get_drvdata(component);
-
- switch (freq) {
- case 11289600:
- case 12000000:
- case 12288000:
- case 16934400:
- case 18432000:
- wm8940->sysclk = freq;
- return 0;
- }
- return -EINVAL;
-}
-
static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
int div_id, int div)
{
@@ -653,6 +644,78 @@ static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
return ret;
}
+static unsigned int wm8940_get_mclkdiv(unsigned int f_in, unsigned int f_out,
+ int *mclkdiv)
+{
+ unsigned int ratio = 2 * f_in / f_out;
+
+ if (ratio <= 2) {
+ *mclkdiv = WM8940_MCLKDIV_1;
+ ratio = 2;
+ } else if (ratio == 3) {
+ *mclkdiv = WM8940_MCLKDIV_1_5;
+ } else if (ratio == 4) {
+ *mclkdiv = WM8940_MCLKDIV_2;
+ } else if (ratio <= 6) {
+ *mclkdiv = WM8940_MCLKDIV_3;
+ ratio = 6;
+ } else if (ratio <= 8) {
+ *mclkdiv = WM8940_MCLKDIV_4;
+ ratio = 8;
+ } else if (ratio <= 12) {
+ *mclkdiv = WM8940_MCLKDIV_6;
+ ratio = 12;
+ } else if (ratio <= 16) {
+ *mclkdiv = WM8940_MCLKDIV_8;
+ ratio = 16;
+ } else {
+ *mclkdiv = WM8940_MCLKDIV_12;
+ ratio = 24;
+ }
+
+ return f_out * ratio / 2;
+}
+
+static int wm8940_update_clocks(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct wm8940_priv *priv = snd_soc_component_get_drvdata(codec);
+ unsigned int fs256;
+ unsigned int fpll = 0;
+ unsigned int f;
+ int mclkdiv;
+
+ if (!priv->mclk || !priv->fs)
+ return 0;
+
+ fs256 = 256 * priv->fs;
+
+ f = wm8940_get_mclkdiv(priv->mclk, fs256, &mclkdiv);
+ if (f != priv->mclk) {
+ /* The PLL performs best around 90MHz */
+ fpll = wm8940_get_mclkdiv(22500000, fs256, &mclkdiv);
+ }
+
+ wm8940_set_dai_pll(dai, 0, 0, priv->mclk, fpll);
+ wm8940_set_dai_clkdiv(dai, WM8940_MCLKDIV, mclkdiv);
+
+ return 0;
+}
+
+static int wm8940_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct wm8940_priv *priv = snd_soc_component_get_drvdata(codec);
+
+ if (dir != SND_SOC_CLOCK_IN)
+ return -EINVAL;
+
+ priv->mclk = freq;
+
+ return wm8940_update_clocks(dai);
+}
+
#define WM8940_RATES SNDRV_PCM_RATE_8000_48000
#define WM8940_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
@@ -697,6 +760,17 @@ static int wm8940_probe(struct snd_soc_component *component)
int ret;
u16 reg;
+ /*
+ * Check chip ID for wm8940 - value of 0x00 offset
+ * SOFTWARE_RESET on write
+ * CHIP_ID on read
+ */
+ reg = snd_soc_component_read(component, WM8940_SOFTRESET);
+ if (reg != WM8940_CHIP_ID) {
+ dev_err(component->dev, "Wrong wm8940 chip ID: 0x%x\n", reg);
+ return -ENODEV;
+ }
+
ret = wm8940_reset(component);
if (ret < 0) {
dev_err(component->dev, "Failed to issue reset\n");
@@ -709,9 +783,7 @@ static int wm8940_probe(struct snd_soc_component *component)
if (ret < 0)
return ret;
- if (!pdata)
- dev_warn(component->dev, "No platform data supplied\n");
- else {
+ if (pdata) {
reg = snd_soc_component_read(component, WM8940_OUTPUTCTL);
ret = snd_soc_component_write(component, WM8940_OUTPUTCTL, reg | pdata->vroi);
if (ret < 0)