static int clk_sys_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = w->codec; struct wm9081_priv *wm9081 = codec->private_data; /* This should be done on init() for bypass paths */ switch (wm9081->sysclk_source) { case WM9081_SYSCLK_MCLK: dev_dbg(codec->dev, "Using %dHz MCLK\n", wm9081->mclk_rate); break; case WM9081_SYSCLK_FLL_MCLK: dev_dbg(codec->dev, "Using %dHz MCLK with FLL\n", wm9081->mclk_rate); break; default: dev_err(codec->dev, "System clock not configured\n"); return -EINVAL; } switch (event) { case SND_SOC_DAPM_PRE_PMU: configure_clock(codec); break; case SND_SOC_DAPM_POST_PMD: /* Disable the FLL if it's running */ wm9081_set_fll(codec, 0, 0, 0); break; } return 0; }
static int clk_sys_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { struct snd_soc_codec *codec = w->codec; struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec); switch (wm9081->sysclk_source) { case WM9081_SYSCLK_MCLK: dev_dbg(codec->dev, "Using %dHz MCLK\n", wm9081->mclk_rate); break; case WM9081_SYSCLK_FLL_MCLK: dev_dbg(codec->dev, "Using %dHz MCLK with FLL\n", wm9081->mclk_rate); break; default: dev_err(codec->dev, "System clock not configured\n"); return -EINVAL; } switch (event) { case SND_SOC_DAPM_PRE_PMU: configure_clock(codec); break; case SND_SOC_DAPM_POST_PMD: wm9081_set_fll(codec, 0, 0, 0); break; } return 0; }
static int configure_clock(struct snd_soc_codec *codec) { struct wm9081_priv *wm9081 = codec->private_data; int new_sysclk, i, target; unsigned int reg; int ret = 0; int mclkdiv = 0; int fll = 0; switch (wm9081->sysclk_source) { case WM9081_SYSCLK_MCLK: if (wm9081->mclk_rate > 12225000) { mclkdiv = 1; wm9081->sysclk_rate = wm9081->mclk_rate / 2; } else { wm9081->sysclk_rate = wm9081->mclk_rate; } wm9081_set_fll(codec, WM9081_SYSCLK_FLL_MCLK, 0, 0); break; case WM9081_SYSCLK_FLL_MCLK: /* If we have a sample rate calculate a CLK_SYS that * gives us a suitable DAC configuration, plus BCLK. * Ideally we would check to see if we can clock * directly from MCLK and only use the FLL if this is * not the case, though care must be taken with free * running mode. */ if (wm9081->master && wm9081->bclk) { /* Make sure we can generate CLK_SYS and BCLK * and that we've got 3MHz for optimal * performance. */ for (i = 0; i < ARRAY_SIZE(clk_sys_rates); i++) { target = wm9081->fs * clk_sys_rates[i].ratio; new_sysclk = target; if (target >= wm9081->bclk && target > 3000000) break; } if (i == ARRAY_SIZE(clk_sys_rates)) return -EINVAL; } else if (wm9081->fs) { for (i = 0; i < ARRAY_SIZE(clk_sys_rates); i++) { new_sysclk = clk_sys_rates[i].ratio * wm9081->fs; if (new_sysclk > 3000000) break; } if (i == ARRAY_SIZE(clk_sys_rates)) return -EINVAL; } else { new_sysclk = 12288000; } ret = wm9081_set_fll(codec, WM9081_SYSCLK_FLL_MCLK, wm9081->mclk_rate, new_sysclk); if (ret == 0) { wm9081->sysclk_rate = new_sysclk; /* Switch SYSCLK over to FLL */ fll = 1; } else { wm9081->sysclk_rate = wm9081->mclk_rate; } break; default: return -EINVAL; } reg = snd_soc_read(codec, WM9081_CLOCK_CONTROL_1); if (mclkdiv) reg |= WM9081_MCLKDIV2; else reg &= ~WM9081_MCLKDIV2; snd_soc_write(codec, WM9081_CLOCK_CONTROL_1, reg); reg = snd_soc_read(codec, WM9081_CLOCK_CONTROL_3); if (fll) reg |= WM9081_CLK_SRC_SEL; else reg &= ~WM9081_CLK_SRC_SEL; snd_soc_write(codec, WM9081_CLOCK_CONTROL_3, reg); dev_dbg(codec->dev, "CLK_SYS is %dHz\n", wm9081->sysclk_rate); return ret; }
static int configure_clock(struct snd_soc_codec *codec) { struct wm9081_priv *wm9081 = codec->private_data; int new_sysclk, i, target; unsigned int reg; int ret = 0; int mclkdiv = 0; int fll = 0; switch (wm9081->sysclk_source) { case WM9081_SYSCLK_MCLK: if (wm9081->mclk_rate > 12225000) { mclkdiv = 1; wm9081->sysclk_rate = wm9081->mclk_rate / 2; } else { wm9081->sysclk_rate = wm9081->mclk_rate; } wm9081_set_fll(codec, WM9081_SYSCLK_FLL_MCLK, 0, 0); break; case WM9081_SYSCLK_FLL_MCLK: if (wm9081->master && wm9081->bclk) { for (i = 0; i < ARRAY_SIZE(clk_sys_rates); i++) { target = wm9081->fs * clk_sys_rates[i].ratio; new_sysclk = target; if (target >= wm9081->bclk && target > 3000000) break; } if (i == ARRAY_SIZE(clk_sys_rates)) return -EINVAL; } else if (wm9081->fs) { for (i = 0; i < ARRAY_SIZE(clk_sys_rates); i++) { new_sysclk = clk_sys_rates[i].ratio * wm9081->fs; if (new_sysclk > 3000000) break; } if (i == ARRAY_SIZE(clk_sys_rates)) return -EINVAL; } else { new_sysclk = 12288000; } ret = wm9081_set_fll(codec, WM9081_SYSCLK_FLL_MCLK, wm9081->mclk_rate, new_sysclk); if (ret == 0) { wm9081->sysclk_rate = new_sysclk; fll = 1; } else { wm9081->sysclk_rate = wm9081->mclk_rate; } break; default: return -EINVAL; } reg = snd_soc_read(codec, WM9081_CLOCK_CONTROL_1); if (mclkdiv) reg |= WM9081_MCLKDIV2; else reg &= ~WM9081_MCLKDIV2; snd_soc_write(codec, WM9081_CLOCK_CONTROL_1, reg); reg = snd_soc_read(codec, WM9081_CLOCK_CONTROL_3); if (fll) reg |= WM9081_CLK_SRC_SEL; else reg &= ~WM9081_CLK_SRC_SEL; snd_soc_write(codec, WM9081_CLOCK_CONTROL_3, reg); dev_dbg(codec->dev, "CLK_SYS is %dHz\n", wm9081->sysclk_rate); return ret; }