summaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm8960.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2017-05-02 08:25:25 +0200
committerTakashi Iwai <tiwai@suse.de>2017-05-02 08:25:25 +0200
commita5c3b32a1146e44f6b38fdfdfffc27842953420c (patch)
treeeca93f51c8deabe77ed079a3e9190717b6380009 /sound/soc/codecs/wm8960.c
parentd7dc450d5a7162de96edbed6b1792240c2f3a55f (diff)
parent20d5c84bef067b7e804a163e2abca16c47125bad (diff)
downloadlinux-stable-a5c3b32a1146e44f6b38fdfdfffc27842953420c.tar.gz
linux-stable-a5c3b32a1146e44f6b38fdfdfffc27842953420c.tar.bz2
linux-stable-a5c3b32a1146e44f6b38fdfdfffc27842953420c.zip
Merge tag 'asoc-v4.12' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for v4.12 A quiet release for the core, but lots of new drivers this time around: - A new, generalized, API for hooking up jacks which makes it easier to write generic machine drivers for simple cases. - Continuing fixes for issues with the x86 CPU drivers. - New drivers for Cirrus CS35L35, DIO DIO2125, Everest ES7132, HiSilicon hi6210, Maxim MAX98927, MT2701 systems with WM8960, Nuvoton NAU8824, Odroid systems, ST STM32 SAI controllers and x86 systems with DA7213
Diffstat (limited to 'sound/soc/codecs/wm8960.c')
-rw-r--r--sound/soc/codecs/wm8960.c195
1 files changed, 146 insertions, 49 deletions
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 3bf081a7e450..9ed455700954 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -604,12 +604,150 @@ static const int bclk_divs[] = {
120, 160, 220, 240, 320, 320, 320
};
+/**
+ * wm8960_configure_sysclk - checks if there is a sysclk frequency available
+ * The sysclk must be chosen such that:
+ * - sysclk = MCLK / sysclk_divs
+ * - lrclk = sysclk / dac_divs
+ * - 10 * bclk = sysclk / bclk_divs
+ *
+ * If we cannot find an exact match for (sysclk, lrclk, bclk)
+ * triplet, we relax the bclk such that bclk is chosen as the
+ * closest available frequency greater than expected bclk.
+ *
+ * @wm8960_priv: wm8960 codec private data
+ * @mclk: MCLK used to derive sysclk
+ * @sysclk_idx: sysclk_divs index for found sysclk
+ * @dac_idx: dac_divs index for found lrclk
+ * @bclk_idx: bclk_divs index for found bclk
+ *
+ * Returns:
+ * -1, in case no sysclk frequency available found
+ * >=0, in case we could derive bclk and lrclk from sysclk using
+ * (@sysclk_idx, @dac_idx, @bclk_idx) dividers
+ */
+static
+int wm8960_configure_sysclk(struct wm8960_priv *wm8960, int mclk,
+ int *sysclk_idx, int *dac_idx, int *bclk_idx)
+{
+ int sysclk, bclk, lrclk;
+ int i, j, k;
+ int diff, closest = mclk;
+
+ /* marker for no match */
+ *bclk_idx = -1;
+
+ bclk = wm8960->bclk;
+ lrclk = wm8960->lrclk;
+
+ /* check if the sysclk frequency is available. */
+ for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
+ if (sysclk_divs[i] == -1)
+ continue;
+ sysclk = mclk / sysclk_divs[i];
+ for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
+ if (sysclk != dac_divs[j] * lrclk)
+ continue;
+ for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) {
+ diff = sysclk - bclk * bclk_divs[k] / 10;
+ if (diff == 0) {
+ *sysclk_idx = i;
+ *dac_idx = j;
+ *bclk_idx = k;
+ break;
+ }
+ if (diff > 0 && closest > diff) {
+ *sysclk_idx = i;
+ *dac_idx = j;
+ *bclk_idx = k;
+ closest = diff;
+ }
+ }
+ if (k != ARRAY_SIZE(bclk_divs))
+ break;
+ }
+ if (j != ARRAY_SIZE(dac_divs))
+ break;
+ }
+ return *bclk_idx;
+}
+
+/**
+ * wm8960_configure_pll - checks if there is a PLL out frequency available
+ * The PLL out frequency must be chosen such that:
+ * - sysclk = lrclk * dac_divs
+ * - freq_out = sysclk * sysclk_divs
+ * - 10 * sysclk = bclk * bclk_divs
+ *
+ * If we cannot find an exact match for (sysclk, lrclk, bclk)
+ * triplet, we relax the bclk such that bclk is chosen as the
+ * closest available frequency greater than expected bclk.
+ *
+ * @codec: codec structure
+ * @freq_in: input frequency used to derive freq out via PLL
+ * @sysclk_idx: sysclk_divs index for found sysclk
+ * @dac_idx: dac_divs index for found lrclk
+ * @bclk_idx: bclk_divs index for found bclk
+ *
+ * Returns:
+ * < 0, in case no PLL frequency out available was found
+ * >=0, in case we could derive bclk, lrclk, sysclk from PLL out using
+ * (@sysclk_idx, @dac_idx, @bclk_idx) dividers
+ */
+static
+int wm8960_configure_pll(struct snd_soc_codec *codec, int freq_in,
+ int *sysclk_idx, int *dac_idx, int *bclk_idx)
+{
+ struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ int sysclk, bclk, lrclk, freq_out;
+ int diff, closest, best_freq_out;
+ int i, j, k;
+
+ bclk = wm8960->bclk;
+ lrclk = wm8960->lrclk;
+ closest = freq_in;
+
+ best_freq_out = -EINVAL;
+ *sysclk_idx = *dac_idx = *bclk_idx = -1;
+
+ for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
+ if (sysclk_divs[i] == -1)
+ continue;
+ for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
+ sysclk = lrclk * dac_divs[j];
+ freq_out = sysclk * sysclk_divs[i];
+
+ for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) {
+ if (!is_pll_freq_available(freq_in, freq_out))
+ continue;
+
+ diff = sysclk - bclk * bclk_divs[k] / 10;
+ if (diff == 0) {
+ *sysclk_idx = i;
+ *dac_idx = j;
+ *bclk_idx = k;
+ return freq_out;
+ }
+ if (diff > 0 && closest > diff) {
+ *sysclk_idx = i;
+ *dac_idx = j;
+ *bclk_idx = k;
+ closest = diff;
+ best_freq_out = freq_out;
+ }
+ }
+ }
+ }
+
+ return best_freq_out;
+}
static int wm8960_configure_clocking(struct snd_soc_codec *codec)
{
struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
- int sysclk, bclk, lrclk, freq_out, freq_in;
+ int freq_out, freq_in;
u16 iface1 = snd_soc_read(codec, WM8960_IFACE1);
int i, j, k;
+ int ret;
if (!(iface1 & (1<<6))) {
dev_dbg(codec->dev,
@@ -623,8 +761,6 @@ static int wm8960_configure_clocking(struct snd_soc_codec *codec)
}
freq_in = wm8960->freq_in;
- bclk = wm8960->bclk;
- lrclk = wm8960->lrclk;
/*
* If it's sysclk auto mode, check if the MCLK can provide sysclk or
* not. If MCLK can provide sysclk, using MCLK to provide sysclk
@@ -643,60 +779,21 @@ static int wm8960_configure_clocking(struct snd_soc_codec *codec)
}
if (wm8960->clk_id != WM8960_SYSCLK_PLL) {
- /* check if the sysclk frequency is available. */
- for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
- if (sysclk_divs[i] == -1)
- continue;
- sysclk = freq_out / sysclk_divs[i];
- for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
- if (sysclk != dac_divs[j] * lrclk)
- continue;
- for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k)
- if (sysclk == bclk * bclk_divs[k] / 10)
- break;
- if (k != ARRAY_SIZE(bclk_divs))
- break;
- }
- if (j != ARRAY_SIZE(dac_divs))
- break;
- }
-
- if (i != ARRAY_SIZE(sysclk_divs)) {
+ ret = wm8960_configure_sysclk(wm8960, freq_out, &i, &j, &k);
+ if (ret >= 0) {
goto configure_clock;
} else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) {
dev_err(codec->dev, "failed to configure clock\n");
return -EINVAL;
}
}
- /* get a available pll out frequency and set pll */
- for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
- if (sysclk_divs[i] == -1)
- continue;
- for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
- sysclk = lrclk * dac_divs[j];
- freq_out = sysclk * sysclk_divs[i];
- for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) {
- if (sysclk == bclk * bclk_divs[k] / 10 &&
- is_pll_freq_available(freq_in, freq_out)) {
- wm8960_set_pll(codec,
- freq_in, freq_out);
- break;
- } else {
- continue;
- }
- }
- if (k != ARRAY_SIZE(bclk_divs))
- break;
- }
- if (j != ARRAY_SIZE(dac_divs))
- break;
- }
-
- if (i == ARRAY_SIZE(sysclk_divs)) {
- dev_err(codec->dev, "failed to configure clock\n");
- return -EINVAL;
+ freq_out = wm8960_configure_pll(codec, freq_in, &i, &j, &k);
+ if (freq_out < 0) {
+ dev_err(codec->dev, "failed to configure clock via PLL\n");
+ return freq_out;
}
+ wm8960_set_pll(codec, freq_in, freq_out);
configure_clock:
/* configure sysclk clock */