Loading sound/soc/codecs/wm8741.c +14 −48 Original line number Diff line number Diff line Loading @@ -61,25 +61,6 @@ static const struct reg_default wm8741_reg_defaults[] = { { 32, 0x0002 }, /* R32 - ADDITONAL_CONTROL_1 */ }; static bool wm8741_readable(struct device *dev, unsigned int reg) { switch (reg) { case WM8741_DACLLSB_ATTENUATION: case WM8741_DACLMSB_ATTENUATION: case WM8741_DACRLSB_ATTENUATION: case WM8741_DACRMSB_ATTENUATION: case WM8741_VOLUME_CONTROL: case WM8741_FORMAT_CONTROL: case WM8741_FILTER_CONTROL: case WM8741_MODE_CONTROL_1: case WM8741_MODE_CONTROL_2: case WM8741_ADDITIONAL_CONTROL_1: return true; default: return false; } } static int wm8741_reset(struct snd_soc_codec *codec) { return snd_soc_write(codec, WM8741_RESET, 0); Loading Loading @@ -278,52 +259,39 @@ static int wm8741_set_dai_sysclk(struct snd_soc_dai *codec_dai, switch (freq) { case 0: wm8741->sysclk_constraints = NULL; wm8741->sysclk = freq; return 0; break; case 11289600: wm8741->sysclk_constraints = &constraints_11289; wm8741->sysclk = freq; return 0; break; case 12288000: wm8741->sysclk_constraints = &constraints_12288; wm8741->sysclk = freq; return 0; break; case 16384000: wm8741->sysclk_constraints = &constraints_16384; wm8741->sysclk = freq; return 0; break; case 16934400: wm8741->sysclk_constraints = &constraints_16934; wm8741->sysclk = freq; return 0; break; case 18432000: wm8741->sysclk_constraints = &constraints_18432; wm8741->sysclk = freq; return 0; break; case 22579200: case 33868800: wm8741->sysclk_constraints = &constraints_22579; wm8741->sysclk = freq; return 0; break; case 24576000: wm8741->sysclk_constraints = &constraints_24576; wm8741->sysclk = freq; return 0; break; case 36864000: wm8741->sysclk_constraints = &constraints_36864; break; default: return -EINVAL; } wm8741->sysclk = freq; return 0; } return -EINVAL; } static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) Loading Loading @@ -554,8 +522,6 @@ static const struct regmap_config wm8741_regmap = { .reg_defaults = wm8741_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8741_reg_defaults), .cache_type = REGCACHE_RBTREE, .readable_reg = wm8741_readable, }; static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741) Loading sound/soc/codecs/wm8753.c +0 −6 Original line number Diff line number Diff line Loading @@ -138,11 +138,6 @@ static bool wm8753_volatile(struct device *dev, unsigned int reg) return reg == WM8753_RESET; } static bool wm8753_writeable(struct device *dev, unsigned int reg) { return reg <= WM8753_ADCTL2; } /* codec private data */ struct wm8753_priv { struct regmap *regmap; Loading Loading @@ -1509,7 +1504,6 @@ static const struct regmap_config wm8753_regmap = { .val_bits = 9, .max_register = WM8753_ADCTL2, .writeable_reg = wm8753_writeable, .volatile_reg = wm8753_volatile, .cache_type = REGCACHE_RBTREE, Loading sound/soc/codecs/wm8904.c +3 −1 Original line number Diff line number Diff line Loading @@ -1837,7 +1837,9 @@ static int wm8904_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: clk_prepare_enable(wm8904->mclk); ret = clk_prepare_enable(wm8904->mclk); if (ret) return ret; break; case SND_SOC_BIAS_PREPARE: Loading sound/soc/codecs/wm8960.c +179 −41 Original line number Diff line number Diff line Loading @@ -48,6 +48,9 @@ #define WM8960_DISOP 0x40 #define WM8960_DRES_MASK 0x30 static bool is_pll_freq_available(unsigned int source, unsigned int target); static int wm8960_set_pll(struct snd_soc_codec *codec, unsigned int freq_in, unsigned int freq_out); /* * wm8960 register cache * We can't read the WM8960 register space when we are Loading Loading @@ -126,9 +129,12 @@ struct wm8960_priv { struct snd_soc_dapm_widget *rout1; struct snd_soc_dapm_widget *out3; bool deemph; int playback_fs; int lrclk; int bclk; int sysclk; int clk_id; int freq_in; bool is_stream_in_use[2]; struct wm8960_data pdata; }; Loading Loading @@ -164,8 +170,8 @@ static int wm8960_set_deemph(struct snd_soc_codec *codec) if (wm8960->deemph) { best = 1; for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) { if (abs(deemph_settings[i] - wm8960->playback_fs) < abs(deemph_settings[best] - wm8960->playback_fs)) if (abs(deemph_settings[i] - wm8960->lrclk) < abs(deemph_settings[best] - wm8960->lrclk)) best = i; } Loading Loading @@ -565,6 +571,9 @@ static struct { { 8000, 5 }, }; /* -1 for reserved value */ static const int sysclk_divs[] = { 1, -1, 2, -1 }; /* Multiply 256 for internal 256 div */ static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 }; Loading @@ -574,61 +583,110 @@ static const int bclk_divs[] = { 120, 160, 220, 240, 320, 320, 320 }; static void wm8960_configure_clocking(struct snd_soc_codec *codec, bool tx, int lrclk) 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; u16 iface1 = snd_soc_read(codec, WM8960_IFACE1); u16 iface2 = snd_soc_read(codec, WM8960_IFACE2); u32 sysclk; int i, j; int i, j, k; if (!(iface1 & (1<<6))) { dev_dbg(codec->dev, "Codec is slave mode, no need to configure clock\n"); return; return 0; } if (!wm8960->sysclk) { dev_dbg(codec->dev, "No SYSCLK configured\n"); return; if (wm8960->clk_id != WM8960_SYSCLK_MCLK && !wm8960->freq_in) { dev_err(codec->dev, "No MCLK configured\n"); return -EINVAL; } if (!wm8960->bclk || !lrclk) { dev_dbg(codec->dev, "No audio clocks configured\n"); return; 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 * directly. Otherwise, auto select a available pll out frequency * and set PLL. */ if (wm8960->clk_id == WM8960_SYSCLK_AUTO) { /* disable the PLL and using MCLK to provide sysclk */ wm8960_set_pll(codec, 0, 0); freq_out = freq_in; } else if (wm8960->sysclk) { freq_out = wm8960->sysclk; } else { dev_err(codec->dev, "No SYSCLK configured\n"); return -EINVAL; } for (i = 0; i < ARRAY_SIZE(dac_divs); ++i) { if (wm8960->sysclk == lrclk * dac_divs[i]) { for (j = 0; j < ARRAY_SIZE(bclk_divs); ++j) { sysclk = wm8960->bclk * bclk_divs[j] / 10; if (wm8960->sysclk == sysclk) /* 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) { 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(bclk_divs)) } if (j != ARRAY_SIZE(dac_divs)) break; } if (i != ARRAY_SIZE(sysclk_divs)) { 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(dac_divs)) { dev_err(codec->dev, "Unsupported sysclk %d\n", wm8960->sysclk); return; if (i == ARRAY_SIZE(sysclk_divs)) { dev_err(codec->dev, "failed to configure clock\n"); return -EINVAL; } /* * configure frame clock. If ADCLRC configure as GPIO pin, DACLRC * pin is used as a frame clock for ADCs and DACs. */ if (iface2 & (1<<6)) snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3); else if (tx) snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3); else if (!tx) snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, i << 6); configure_clock: /* configure sysclk clock */ snd_soc_update_bits(codec, WM8960_CLOCK1, 3 << 1, i << 1); /* configure frame clock */ snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, j << 3); snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, j << 6); /* configure bit clock */ snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, j); snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, k); return 0; } static int wm8960_hw_params(struct snd_pcm_substream *substream, Loading Loading @@ -667,9 +725,9 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } wm8960->lrclk = params_rate(params); /* Update filters for the new rate */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { wm8960->playback_fs = params_rate(params); if (tx) { wm8960_set_deemph(codec); } else { for (i = 0; i < ARRAY_SIZE(alc_rates); i++) Loading @@ -682,7 +740,23 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, /* set iface */ snd_soc_write(codec, WM8960_IFACE1, iface); wm8960_configure_clocking(codec, tx, params_rate(params)); wm8960->is_stream_in_use[tx] = true; if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON && !wm8960->is_stream_in_use[!tx]) return wm8960_configure_clocking(codec); return 0; } static int wm8960_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; wm8960->is_stream_in_use[tx] = false; return 0; } Loading @@ -702,6 +776,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); u16 pm2 = snd_soc_read(codec, WM8960_POWER2); int ret; switch (level) { Loading @@ -721,11 +796,22 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, } } ret = wm8960_configure_clocking(codec); if (ret) return ret; /* Set VMID to 2x50k */ snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80); break; case SND_SOC_BIAS_ON: /* * If it's sysclk auto mode, and the pll is enabled, * disable the pll */ if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1)) wm8960_set_pll(codec, 0, 0); if (!IS_ERR(wm8960->mclk)) clk_disable_unprepare(wm8960->mclk); break; Loading Loading @@ -780,6 +866,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); u16 pm2 = snd_soc_read(codec, WM8960_POWER2); int reg, ret; switch (level) { Loading Loading @@ -831,9 +918,21 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, return ret; } } ret = wm8960_configure_clocking(codec); if (ret) return ret; break; case SND_SOC_BIAS_ON: /* * If it's sysclk auto mode, and the pll is enabled, * disable the pll */ if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1)) wm8960_set_pll(codec, 0, 0); if (!IS_ERR(wm8960->mclk)) clk_disable_unprepare(wm8960->mclk); Loading Loading @@ -892,6 +991,28 @@ struct _pll_div { u32 k:24; }; static bool is_pll_freq_available(unsigned int source, unsigned int target) { unsigned int Ndiv; if (source == 0 || target == 0) return false; /* Scale up target to PLL operating frequency */ target *= 4; Ndiv = target / source; if (Ndiv < 6) { source >>= 1; Ndiv = target / source; } if ((Ndiv < 6) || (Ndiv > 12)) return false; return true; } /* The size in bits of the pll divide multiplied by 10 * to allow rounding later */ #define FIXED_PLL_SIZE ((1 << 24) * 10) Loading Loading @@ -943,10 +1064,9 @@ static int pll_factors(unsigned int source, unsigned int target, return 0; } static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) static int wm8960_set_pll(struct snd_soc_codec *codec, unsigned int freq_in, unsigned int freq_out) { struct snd_soc_codec *codec = codec_dai->codec; u16 reg; static struct _pll_div pll_div; int ret; Loading Loading @@ -986,6 +1106,20 @@ static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, return 0; } static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) { struct snd_soc_codec *codec = codec_dai->codec; struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); wm8960->freq_in = freq_in; if (pll_id == WM8960_SYSCLK_AUTO) return 0; return wm8960_set_pll(codec, freq_in, freq_out); } static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) { Loading Loading @@ -1043,11 +1177,14 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, WM8960_SYSCLK_PLL); break; case WM8960_SYSCLK_AUTO: break; default: return -EINVAL; } wm8960->sysclk = freq; wm8960->clk_id = clk_id; return 0; } Loading @@ -1060,6 +1197,7 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, static const struct snd_soc_dai_ops wm8960_dai_ops = { .hw_params = wm8960_hw_params, .hw_free = wm8960_hw_free, .digital_mute = wm8960_mute, .set_fmt = wm8960_set_dai_fmt, .set_clkdiv = wm8960_set_dai_clkdiv, Loading sound/soc/codecs/wm8960.h +1 −0 Original line number Diff line number Diff line Loading @@ -82,6 +82,7 @@ #define WM8960_SYSCLK_MCLK (0 << 0) #define WM8960_SYSCLK_PLL (1 << 0) #define WM8960_SYSCLK_AUTO (2 << 0) #define WM8960_DAC_DIV_1 (0 << 3) #define WM8960_DAC_DIV_1_5 (1 << 3) Loading Loading
sound/soc/codecs/wm8741.c +14 −48 Original line number Diff line number Diff line Loading @@ -61,25 +61,6 @@ static const struct reg_default wm8741_reg_defaults[] = { { 32, 0x0002 }, /* R32 - ADDITONAL_CONTROL_1 */ }; static bool wm8741_readable(struct device *dev, unsigned int reg) { switch (reg) { case WM8741_DACLLSB_ATTENUATION: case WM8741_DACLMSB_ATTENUATION: case WM8741_DACRLSB_ATTENUATION: case WM8741_DACRMSB_ATTENUATION: case WM8741_VOLUME_CONTROL: case WM8741_FORMAT_CONTROL: case WM8741_FILTER_CONTROL: case WM8741_MODE_CONTROL_1: case WM8741_MODE_CONTROL_2: case WM8741_ADDITIONAL_CONTROL_1: return true; default: return false; } } static int wm8741_reset(struct snd_soc_codec *codec) { return snd_soc_write(codec, WM8741_RESET, 0); Loading Loading @@ -278,52 +259,39 @@ static int wm8741_set_dai_sysclk(struct snd_soc_dai *codec_dai, switch (freq) { case 0: wm8741->sysclk_constraints = NULL; wm8741->sysclk = freq; return 0; break; case 11289600: wm8741->sysclk_constraints = &constraints_11289; wm8741->sysclk = freq; return 0; break; case 12288000: wm8741->sysclk_constraints = &constraints_12288; wm8741->sysclk = freq; return 0; break; case 16384000: wm8741->sysclk_constraints = &constraints_16384; wm8741->sysclk = freq; return 0; break; case 16934400: wm8741->sysclk_constraints = &constraints_16934; wm8741->sysclk = freq; return 0; break; case 18432000: wm8741->sysclk_constraints = &constraints_18432; wm8741->sysclk = freq; return 0; break; case 22579200: case 33868800: wm8741->sysclk_constraints = &constraints_22579; wm8741->sysclk = freq; return 0; break; case 24576000: wm8741->sysclk_constraints = &constraints_24576; wm8741->sysclk = freq; return 0; break; case 36864000: wm8741->sysclk_constraints = &constraints_36864; break; default: return -EINVAL; } wm8741->sysclk = freq; return 0; } return -EINVAL; } static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) Loading Loading @@ -554,8 +522,6 @@ static const struct regmap_config wm8741_regmap = { .reg_defaults = wm8741_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8741_reg_defaults), .cache_type = REGCACHE_RBTREE, .readable_reg = wm8741_readable, }; static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741) Loading
sound/soc/codecs/wm8753.c +0 −6 Original line number Diff line number Diff line Loading @@ -138,11 +138,6 @@ static bool wm8753_volatile(struct device *dev, unsigned int reg) return reg == WM8753_RESET; } static bool wm8753_writeable(struct device *dev, unsigned int reg) { return reg <= WM8753_ADCTL2; } /* codec private data */ struct wm8753_priv { struct regmap *regmap; Loading Loading @@ -1509,7 +1504,6 @@ static const struct regmap_config wm8753_regmap = { .val_bits = 9, .max_register = WM8753_ADCTL2, .writeable_reg = wm8753_writeable, .volatile_reg = wm8753_volatile, .cache_type = REGCACHE_RBTREE, Loading
sound/soc/codecs/wm8904.c +3 −1 Original line number Diff line number Diff line Loading @@ -1837,7 +1837,9 @@ static int wm8904_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: clk_prepare_enable(wm8904->mclk); ret = clk_prepare_enable(wm8904->mclk); if (ret) return ret; break; case SND_SOC_BIAS_PREPARE: Loading
sound/soc/codecs/wm8960.c +179 −41 Original line number Diff line number Diff line Loading @@ -48,6 +48,9 @@ #define WM8960_DISOP 0x40 #define WM8960_DRES_MASK 0x30 static bool is_pll_freq_available(unsigned int source, unsigned int target); static int wm8960_set_pll(struct snd_soc_codec *codec, unsigned int freq_in, unsigned int freq_out); /* * wm8960 register cache * We can't read the WM8960 register space when we are Loading Loading @@ -126,9 +129,12 @@ struct wm8960_priv { struct snd_soc_dapm_widget *rout1; struct snd_soc_dapm_widget *out3; bool deemph; int playback_fs; int lrclk; int bclk; int sysclk; int clk_id; int freq_in; bool is_stream_in_use[2]; struct wm8960_data pdata; }; Loading Loading @@ -164,8 +170,8 @@ static int wm8960_set_deemph(struct snd_soc_codec *codec) if (wm8960->deemph) { best = 1; for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) { if (abs(deemph_settings[i] - wm8960->playback_fs) < abs(deemph_settings[best] - wm8960->playback_fs)) if (abs(deemph_settings[i] - wm8960->lrclk) < abs(deemph_settings[best] - wm8960->lrclk)) best = i; } Loading Loading @@ -565,6 +571,9 @@ static struct { { 8000, 5 }, }; /* -1 for reserved value */ static const int sysclk_divs[] = { 1, -1, 2, -1 }; /* Multiply 256 for internal 256 div */ static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 }; Loading @@ -574,61 +583,110 @@ static const int bclk_divs[] = { 120, 160, 220, 240, 320, 320, 320 }; static void wm8960_configure_clocking(struct snd_soc_codec *codec, bool tx, int lrclk) 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; u16 iface1 = snd_soc_read(codec, WM8960_IFACE1); u16 iface2 = snd_soc_read(codec, WM8960_IFACE2); u32 sysclk; int i, j; int i, j, k; if (!(iface1 & (1<<6))) { dev_dbg(codec->dev, "Codec is slave mode, no need to configure clock\n"); return; return 0; } if (!wm8960->sysclk) { dev_dbg(codec->dev, "No SYSCLK configured\n"); return; if (wm8960->clk_id != WM8960_SYSCLK_MCLK && !wm8960->freq_in) { dev_err(codec->dev, "No MCLK configured\n"); return -EINVAL; } if (!wm8960->bclk || !lrclk) { dev_dbg(codec->dev, "No audio clocks configured\n"); return; 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 * directly. Otherwise, auto select a available pll out frequency * and set PLL. */ if (wm8960->clk_id == WM8960_SYSCLK_AUTO) { /* disable the PLL and using MCLK to provide sysclk */ wm8960_set_pll(codec, 0, 0); freq_out = freq_in; } else if (wm8960->sysclk) { freq_out = wm8960->sysclk; } else { dev_err(codec->dev, "No SYSCLK configured\n"); return -EINVAL; } for (i = 0; i < ARRAY_SIZE(dac_divs); ++i) { if (wm8960->sysclk == lrclk * dac_divs[i]) { for (j = 0; j < ARRAY_SIZE(bclk_divs); ++j) { sysclk = wm8960->bclk * bclk_divs[j] / 10; if (wm8960->sysclk == sysclk) /* 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) { 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(bclk_divs)) } if (j != ARRAY_SIZE(dac_divs)) break; } if (i != ARRAY_SIZE(sysclk_divs)) { 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(dac_divs)) { dev_err(codec->dev, "Unsupported sysclk %d\n", wm8960->sysclk); return; if (i == ARRAY_SIZE(sysclk_divs)) { dev_err(codec->dev, "failed to configure clock\n"); return -EINVAL; } /* * configure frame clock. If ADCLRC configure as GPIO pin, DACLRC * pin is used as a frame clock for ADCs and DACs. */ if (iface2 & (1<<6)) snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3); else if (tx) snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, i << 3); else if (!tx) snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, i << 6); configure_clock: /* configure sysclk clock */ snd_soc_update_bits(codec, WM8960_CLOCK1, 3 << 1, i << 1); /* configure frame clock */ snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, j << 3); snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, j << 6); /* configure bit clock */ snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, j); snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, k); return 0; } static int wm8960_hw_params(struct snd_pcm_substream *substream, Loading Loading @@ -667,9 +725,9 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } wm8960->lrclk = params_rate(params); /* Update filters for the new rate */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { wm8960->playback_fs = params_rate(params); if (tx) { wm8960_set_deemph(codec); } else { for (i = 0; i < ARRAY_SIZE(alc_rates); i++) Loading @@ -682,7 +740,23 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, /* set iface */ snd_soc_write(codec, WM8960_IFACE1, iface); wm8960_configure_clocking(codec, tx, params_rate(params)); wm8960->is_stream_in_use[tx] = true; if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON && !wm8960->is_stream_in_use[!tx]) return wm8960_configure_clocking(codec); return 0; } static int wm8960_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; wm8960->is_stream_in_use[tx] = false; return 0; } Loading @@ -702,6 +776,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); u16 pm2 = snd_soc_read(codec, WM8960_POWER2); int ret; switch (level) { Loading @@ -721,11 +796,22 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, } } ret = wm8960_configure_clocking(codec); if (ret) return ret; /* Set VMID to 2x50k */ snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80); break; case SND_SOC_BIAS_ON: /* * If it's sysclk auto mode, and the pll is enabled, * disable the pll */ if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1)) wm8960_set_pll(codec, 0, 0); if (!IS_ERR(wm8960->mclk)) clk_disable_unprepare(wm8960->mclk); break; Loading Loading @@ -780,6 +866,7 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); u16 pm2 = snd_soc_read(codec, WM8960_POWER2); int reg, ret; switch (level) { Loading Loading @@ -831,9 +918,21 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, return ret; } } ret = wm8960_configure_clocking(codec); if (ret) return ret; break; case SND_SOC_BIAS_ON: /* * If it's sysclk auto mode, and the pll is enabled, * disable the pll */ if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1)) wm8960_set_pll(codec, 0, 0); if (!IS_ERR(wm8960->mclk)) clk_disable_unprepare(wm8960->mclk); Loading Loading @@ -892,6 +991,28 @@ struct _pll_div { u32 k:24; }; static bool is_pll_freq_available(unsigned int source, unsigned int target) { unsigned int Ndiv; if (source == 0 || target == 0) return false; /* Scale up target to PLL operating frequency */ target *= 4; Ndiv = target / source; if (Ndiv < 6) { source >>= 1; Ndiv = target / source; } if ((Ndiv < 6) || (Ndiv > 12)) return false; return true; } /* The size in bits of the pll divide multiplied by 10 * to allow rounding later */ #define FIXED_PLL_SIZE ((1 << 24) * 10) Loading Loading @@ -943,10 +1064,9 @@ static int pll_factors(unsigned int source, unsigned int target, return 0; } static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) static int wm8960_set_pll(struct snd_soc_codec *codec, unsigned int freq_in, unsigned int freq_out) { struct snd_soc_codec *codec = codec_dai->codec; u16 reg; static struct _pll_div pll_div; int ret; Loading Loading @@ -986,6 +1106,20 @@ static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, return 0; } static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) { struct snd_soc_codec *codec = codec_dai->codec; struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); wm8960->freq_in = freq_in; if (pll_id == WM8960_SYSCLK_AUTO) return 0; return wm8960_set_pll(codec, freq_in, freq_out); } static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) { Loading Loading @@ -1043,11 +1177,14 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, WM8960_SYSCLK_PLL); break; case WM8960_SYSCLK_AUTO: break; default: return -EINVAL; } wm8960->sysclk = freq; wm8960->clk_id = clk_id; return 0; } Loading @@ -1060,6 +1197,7 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, static const struct snd_soc_dai_ops wm8960_dai_ops = { .hw_params = wm8960_hw_params, .hw_free = wm8960_hw_free, .digital_mute = wm8960_mute, .set_fmt = wm8960_set_dai_fmt, .set_clkdiv = wm8960_set_dai_clkdiv, Loading
sound/soc/codecs/wm8960.h +1 −0 Original line number Diff line number Diff line Loading @@ -82,6 +82,7 @@ #define WM8960_SYSCLK_MCLK (0 << 0) #define WM8960_SYSCLK_PLL (1 << 0) #define WM8960_SYSCLK_AUTO (2 << 0) #define WM8960_DAC_DIV_1 (0 << 3) #define WM8960_DAC_DIV_1_5 (1 << 3) Loading