Loading Documentation/devicetree/bindings/sound/zte,tdm.txt 0 → 100644 +30 −0 Original line number Diff line number Diff line ZTE TDM DAI driver Required properties: - compatible : should be one of the following. * zte,zx296718-tdm - reg : physical base address of the controller and length of memory mapped region. - clocks : Pairs of phandle and specifier referencing the controller's clocks. - clock-names: "wclk" for the wclk. "pclk" for the pclk. -#clock-cells: should be 1. - zte,tdm-dma-sysctrl : Reference to the sysctrl controller controlling the dma. includes: phandle of sysctrl. register offset in sysctrl for control dma. mask of the register that be written to sysctrl. Example: tdm: tdm@1487000 { compatible = "zte,zx296718-tdm"; reg = <0x01487000 0x1000>; clocks = <&audiocrm AUDIO_TDM_WCLK>, <&audiocrm AUDIO_TDM_PCLK>; clock-names = "wclk", "pclk"; #clock-cells = <1>; pinctrl-names = "default"; pinctrl-0 = <&tdm_global_pin>; zte,tdm-dma-sysctrl = <&sysctrl 0x10c 4>; }; sound/soc/codecs/wm8960.c +146 −49 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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 Loading @@ -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 */ Loading sound/soc/codecs/wm8978.c +7 −0 Original line number Diff line number Diff line Loading @@ -1071,9 +1071,16 @@ static const struct i2c_device_id wm8978_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, wm8978_i2c_id); static const struct of_device_id wm8978_of_match[] = { { .compatible = "wlf,wm8978", }, { } }; MODULE_DEVICE_TABLE(of, wm8978_of_match); static struct i2c_driver wm8978_i2c_driver = { .driver = { .name = "wm8978", .of_match_table = wm8978_of_match, }, .probe = wm8978_i2c_probe, .remove = wm8978_i2c_remove, Loading sound/soc/zte/Kconfig +8 −0 Original line number Diff line number Diff line Loading @@ -15,3 +15,11 @@ config ZX_I2S help Say Y or M if you want to add support for codecs attached to the ZTE ZX I2S interface config ZX_TDM tristate "ZTE ZX TDM Driver Support" depends on COMMON_CLK select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for codecs attached to the ZTE ZX TDM interface sound/soc/zte/Makefile +1 −0 Original line number Diff line number Diff line obj-$(CONFIG_ZX_SPDIF) += zx-spdif.o obj-$(CONFIG_ZX_I2S) += zx-i2s.o obj-$(CONFIG_ZX_TDM) += zx-tdm.o Loading
Documentation/devicetree/bindings/sound/zte,tdm.txt 0 → 100644 +30 −0 Original line number Diff line number Diff line ZTE TDM DAI driver Required properties: - compatible : should be one of the following. * zte,zx296718-tdm - reg : physical base address of the controller and length of memory mapped region. - clocks : Pairs of phandle and specifier referencing the controller's clocks. - clock-names: "wclk" for the wclk. "pclk" for the pclk. -#clock-cells: should be 1. - zte,tdm-dma-sysctrl : Reference to the sysctrl controller controlling the dma. includes: phandle of sysctrl. register offset in sysctrl for control dma. mask of the register that be written to sysctrl. Example: tdm: tdm@1487000 { compatible = "zte,zx296718-tdm"; reg = <0x01487000 0x1000>; clocks = <&audiocrm AUDIO_TDM_WCLK>, <&audiocrm AUDIO_TDM_PCLK>; clock-names = "wclk", "pclk"; #clock-cells = <1>; pinctrl-names = "default"; pinctrl-0 = <&tdm_global_pin>; zte,tdm-dma-sysctrl = <&sysctrl 0x10c 4>; };
sound/soc/codecs/wm8960.c +146 −49 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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 Loading @@ -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 */ Loading
sound/soc/codecs/wm8978.c +7 −0 Original line number Diff line number Diff line Loading @@ -1071,9 +1071,16 @@ static const struct i2c_device_id wm8978_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, wm8978_i2c_id); static const struct of_device_id wm8978_of_match[] = { { .compatible = "wlf,wm8978", }, { } }; MODULE_DEVICE_TABLE(of, wm8978_of_match); static struct i2c_driver wm8978_i2c_driver = { .driver = { .name = "wm8978", .of_match_table = wm8978_of_match, }, .probe = wm8978_i2c_probe, .remove = wm8978_i2c_remove, Loading
sound/soc/zte/Kconfig +8 −0 Original line number Diff line number Diff line Loading @@ -15,3 +15,11 @@ config ZX_I2S help Say Y or M if you want to add support for codecs attached to the ZTE ZX I2S interface config ZX_TDM tristate "ZTE ZX TDM Driver Support" depends on COMMON_CLK select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M if you want to add support for codecs attached to the ZTE ZX TDM interface
sound/soc/zte/Makefile +1 −0 Original line number Diff line number Diff line obj-$(CONFIG_ZX_SPDIF) += zx-spdif.o obj-$(CONFIG_ZX_I2S) += zx-i2s.o obj-$(CONFIG_ZX_TDM) += zx-tdm.o