diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-03 11:58:59 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-03 11:58:59 -0700 |
commit | 221656e7c4ce342b99c31eca96c1cbb6d1dce45f (patch) | |
tree | 1af3655475361f3ec75d8e26bc8d5f4bad8a87b0 /sound/soc/codecs/wm8960.c | |
parent | 2f34c1231bfc9f2550f934acb268ac7315fb3837 (diff) | |
parent | a5c3b32a1146e44f6b38fdfdfffc27842953420c (diff) | |
download | lwn-221656e7c4ce342b99c31eca96c1cbb6d1dce45f.tar.gz lwn-221656e7c4ce342b99c31eca96c1cbb6d1dce45f.zip |
Merge tag 'sound-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"It was a relatively calm development cycle, and no scaring changes are
seen in both core and driver sides. Here are some highlights:
ASoC:
- A new API for hooking up jacks more generically and easily
- Card longname is set based on DMI for a unique UCM profile
- Lots of Intel driver fixes: Atom, Broxton, Skylake and newer chips
- 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
HD-audio:
- Many new quirks to support headset for various devices (mostly ASUS
ones) as usual
- Support for dual codecs on some Gigabyte mobos and Lenovo laptop
- Improvement on PCM position reporting for Skylake and newer
FireWire:
- New drivers for MOTU and RME Fireface series
- Updates for Digidesign Digi00x and TASCAM series
- Support for tracepoints
Others:
- USB-audio: improved support for quirk_alias option
- Cleanups, constification allover the places"
* tag 'sound-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (299 commits)
ASoC: codec: wm8960: Relax bit clock computation when using PLL
ASoC: codec: wm9860: avoid maybe-uninitialized warning
ASoC: nau8824: leave Class D gain at chip default
ASoC: nau8824: rename controls to match DAPM controls
ASoC: Intel: Skylake: Return negative error code
ASoC: Intel: Skylake: Fix unused variable warning
ASoC: Intel: Skylake: fix uninitialized pointer use
ASoC: sti: Fix error handling if of_clk_get() fails
ASoC: cs4271: configure reset GPIO as output
ASoC: dwc: Disallow building designware_pcm as a module
ALSA: ali5451: fix spelling mistake in "ali_capture_preapre"
ASoC: stm32: add SAI driver
ASoC: stm32: add bindings for SAI
ASoC: Intel: Skylake: Add loadable module support on KBL platform
ASoC: Intel: Skylake: Modify load_lib_ipc arguments for a nowait version
ASoC: Intel: Skylake: Register dsp_fw_ops for kabylake
ASoC: Intel: Skylake: Modify arguments to reuse module transfer function
ASoC: Intel: Skylake: Commonize library load
ASoC: Intel: Skylake: Move sst common initialization to a helper function
ASoC: nau8824: new driver
...
Diffstat (limited to 'sound/soc/codecs/wm8960.c')
-rw-r--r-- | sound/soc/codecs/wm8960.c | 195 |
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 */ |