Lines Matching +full:4 +full:- +full:switch
1 // SPDX-License-Identifier: GPL-2.0-only
3 * wm8978.c -- WM8978 ALSA SoC Audio Codec driver
5 * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
7 * Copyright 2006-2009 Wolfson Microelectronics PLC.
34 { 4, 0x0050 },
106 static const char *wm8978_companding[] = {"Off", "NC", "u-law", "A-law"};
133 static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
134 static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
135 static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
136 static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
137 static const DECLARE_TLV_DB_SCALE(boost_tlv, -1500, 300, 1);
142 SOC_SINGLE("Digital Loopback Switch",
148 SOC_DOUBLE("DAC Inversion Switch", WM8978_DAC_CONTROL, 0, 1, 1, 0),
154 SOC_SINGLE("High Pass Filter Switch", WM8978_ADC_CONTROL, 8, 1, 0),
155 SOC_SINGLE("High Pass Cut Off", WM8978_ADC_CONTROL, 4, 7, 0),
156 SOC_DOUBLE("ADC Inversion Switch", WM8978_ADC_CONTROL, 0, 1, 1, 0),
181 SOC_SINGLE("DAC Playback Limiter Switch",
184 WM8978_DAC_LIMITER_1, 4, 15, 0),
189 WM8978_DAC_LIMITER_2, 4, 7, 0),
193 SOC_ENUM("ALC Enable Switch", alc1),
197 SOC_SINGLE("ALC Capture Hold", WM8978_ALC_CONTROL_2, 4, 10, 0),
201 SOC_SINGLE("ALC Capture Decay", WM8978_ALC_CONTROL_3, 4, 10, 0),
204 SOC_SINGLE("ALC Capture Noise Gate Switch", WM8978_NOISE_GATE, 3, 1, 0),
208 SOC_DOUBLE_R("Capture PGA ZC Switch",
212 /* OUT1 - Headphones */
213 SOC_DOUBLE_R("Headphone Playback ZC Switch",
220 /* OUT2 - Speakers */
221 SOC_DOUBLE_R("Speaker Playback ZC Switch",
228 /* OUT3/4 - Line Output */
229 SOC_DOUBLE_R("Line Playback Switch",
238 4, 7, 0, boost_tlv),
249 SOC_DOUBLE_R("Headphone Switch",
253 SOC_DOUBLE_R("Speaker Switch",
257 SOC_SINGLE("DAC 128x Oversampling Switch", WM8978_DAC_CONTROL,
259 SOC_SINGLE("ADC 128x Oversampling Switch", WM8978_ADC_CONTROL,
265 SOC_DAPM_SINGLE("Line Bypass Switch", WM8978_LEFT_MIXER_CONTROL, 1, 1, 0),
266 SOC_DAPM_SINGLE("Aux Playback Switch", WM8978_LEFT_MIXER_CONTROL, 5, 1, 0),
267 SOC_DAPM_SINGLE("PCM Playback Switch", WM8978_LEFT_MIXER_CONTROL, 0, 1, 0),
271 SOC_DAPM_SINGLE("Line Bypass Switch", WM8978_RIGHT_MIXER_CONTROL, 1, 1, 0),
272 SOC_DAPM_SINGLE("Aux Playback Switch", WM8978_RIGHT_MIXER_CONTROL, 5, 1, 0),
273 SOC_DAPM_SINGLE("PCM Playback Switch", WM8978_RIGHT_MIXER_CONTROL, 0, 1, 0),
280 SOC_DAPM_SINGLE("L2 Switch", WM8978_INPUT_CONTROL, 2, 1, 0),
281 SOC_DAPM_SINGLE("MicN Switch", WM8978_INPUT_CONTROL, 1, 1, 0),
282 SOC_DAPM_SINGLE("MicP Switch", WM8978_INPUT_CONTROL, 0, 1, 0),
285 SOC_DAPM_SINGLE("R2 Switch", WM8978_INPUT_CONTROL, 6, 1, 0),
286 SOC_DAPM_SINGLE("MicN Switch", WM8978_INPUT_CONTROL, 5, 1, 0),
287 SOC_DAPM_SINGLE("MicP Switch", WM8978_INPUT_CONTROL, 4, 1, 0),
312 4, 0, NULL, 0),
334 SND_SOC_DAPM_MICBIAS("Mic Bias", WM8978_POWER_MANAGEMENT_1, 4, 0),
352 {"Right Output Mixer", "PCM Playback Switch", "Right DAC"},
353 {"Right Output Mixer", "Aux Playback Switch", "RAUX"},
354 {"Right Output Mixer", "Line Bypass Switch", "Right Boost Mixer"},
356 {"Left Output Mixer", "PCM Playback Switch", "Left DAC"},
357 {"Left Output Mixer", "Aux Playback Switch", "LAUX"},
358 {"Left Output Mixer", "Line Bypass Switch", "Left Boost Mixer"},
390 {"Right Input Mixer", "R2 Switch", "R2"},
391 {"Right Input Mixer", "MicN Switch", "RMICN"},
392 {"Right Input Mixer", "MicP Switch", "RMICP"},
394 {"Left Input Mixer", "L2 Switch", "L2"},
395 {"Left Input Mixer", "MicN Switch", "LMICN"},
396 {"Left Input Mixer", "MicP Switch", "LMICP"},
417 pll_div->div2 = 1; in pll_factors()
420 pll_div->div2 = 0; in pll_factors()
424 dev_warn(component->dev, in pll_factors()
428 pll_div->n = n_div; in pll_factors()
429 n_mod = target - source * n_div; in pll_factors()
436 pll_div->k = k; in pll_factors()
440 static const int mclk_numerator[] = {1, 3, 2, 3, 4, 6, 8, 12};
445 * 3 * f_mclk / 4 <= f_PLLOUT < 13 * f_mclk / 4
456 unsigned int f_pllout_x4 = 4 * f_out * mclk_numerator[i] / in wm8978_enum_mclk()
459 *f_pllout = f_pllout_x4 / 4; in wm8978_enum_mclk()
464 return -EINVAL; in wm8978_enum_mclk()
475 unsigned int f_opclk = wm8978->f_opclk, f_mclk = wm8978->f_mclk, in wm8978_configure_pll()
476 f_256fs = wm8978->f_256fs; in wm8978_configure_pll()
480 return -EINVAL; in wm8978_configure_pll()
485 wm8978->mclk_idx = -1; in wm8978_configure_pll()
489 * 6 <= R = f2 / f1 < 13, 1 <= OPCLKDIV <= 4. in wm8978_configure_pll()
490 * f_opclk = f_mclk * prescale * R / 4 / OPCLKDIV, where in wm8978_configure_pll()
493 * f_mclk * 3 / 4 <= f_PLLOUT < f_mclk * 13 / 4. Must be in wm8978_configure_pll()
494 * f_mclk * 3 / 16 <= f_opclk < f_mclk * 13 / 4. in wm8978_configure_pll()
496 if (16 * f_opclk < 3 * f_mclk || 4 * f_opclk >= 13 * f_mclk) in wm8978_configure_pll()
497 return -EINVAL; in wm8978_configure_pll()
499 if (4 * f_opclk < 3 * f_mclk) in wm8978_configure_pll()
501 opclk_div = DIV_ROUND_UP(3 * f_mclk / 4, f_opclk); in wm8978_configure_pll()
505 dev_dbg(component->dev, "%s: OPCLKDIV=%d\n", __func__, opclk_div); in wm8978_configure_pll()
508 (opclk_div - 1) << 4); in wm8978_configure_pll()
510 wm8978->f_pllout = f_opclk * opclk_div; in wm8978_configure_pll()
515 * f_256fs = f_mclk * prescale * R / 4 / MCLKDIV, where in wm8978_configure_pll()
518 * f_mclk * 3 / 4 <= f_PLLOUT < f_mclk * 13 / 4. Must be in wm8978_configure_pll()
519 * f_mclk * 3 / 48 <= f_256fs < f_mclk * 13 / 4. This means MCLK in wm8978_configure_pll()
522 int idx = wm8978_enum_mclk(f_256fs, f_mclk, &wm8978->f_pllout); in wm8978_configure_pll()
526 wm8978->mclk_idx = idx; in wm8978_configure_pll()
528 return -EINVAL; in wm8978_configure_pll()
531 f2 = wm8978->f_pllout * 4; in wm8978_configure_pll()
533 dev_dbg(component->dev, "%s: f_MCLK=%uHz, f_PLLOUT=%uHz\n", __func__, in wm8978_configure_pll()
534 wm8978->f_mclk, wm8978->f_pllout); in wm8978_configure_pll()
536 pll_factors(component, &pll_div, f2, wm8978->f_mclk); in wm8978_configure_pll()
538 dev_dbg(component->dev, "%s: calculated PLL N=0x%x, K=0x%x, div2=%d\n", in wm8978_configure_pll()
544 snd_soc_component_write(component, WM8978_PLL_N, (pll_div.div2 << 4) | pll_div.n); in wm8978_configure_pll()
554 snd_soc_component_update_bits(component, WM8978_GPIO_CONTROL, 7, 4); in wm8978_configure_pll()
565 struct snd_soc_component *component = codec_dai->component; in wm8978_set_dai_clkdiv()
569 switch (div_id) { in wm8978_set_dai_clkdiv()
571 wm8978->f_opclk = div; in wm8978_set_dai_clkdiv()
573 if (wm8978->f_mclk) in wm8978_set_dai_clkdiv()
578 * user-requested OPCLK frquency as good as possible. in wm8978_set_dai_clkdiv()
585 * find an exact MCLK divider configuration - it will in wm8978_set_dai_clkdiv()
592 return -EINVAL; in wm8978_set_dai_clkdiv()
596 return -EINVAL; in wm8978_set_dai_clkdiv()
599 dev_dbg(component->dev, "%s: ID %d, value %u\n", __func__, div_id, div); in wm8978_set_dai_clkdiv()
610 struct snd_soc_component *component = codec_dai->component; in wm8978_set_dai_sysclk()
614 dev_dbg(component->dev, "%s: ID %d, freq %u\n", __func__, clk_id, freq); in wm8978_set_dai_sysclk()
617 wm8978->f_mclk = freq; in wm8978_set_dai_sysclk()
620 if (wm8978->f_opclk) in wm8978_set_dai_sysclk()
626 wm8978->sysclk = clk_id; in wm8978_set_dai_sysclk()
629 if (wm8978->sysclk == WM8978_PLL && (!freq || clk_id == WM8978_MCLK)) { in wm8978_set_dai_sysclk()
633 /* GPIO1 into default mode as input - before configuring PLL */ in wm8978_set_dai_sysclk()
638 wm8978->sysclk = WM8978_MCLK; in wm8978_set_dai_sysclk()
639 wm8978->f_pllout = 0; in wm8978_set_dai_sysclk()
640 wm8978->f_opclk = 0; in wm8978_set_dai_sysclk()
651 struct snd_soc_component *component = codec_dai->component; in wm8978_set_dai_fmt()
659 dev_dbg(component->dev, "%s\n", __func__); in wm8978_set_dai_fmt()
662 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { in wm8978_set_dai_fmt()
670 return -EINVAL; in wm8978_set_dai_fmt()
674 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { in wm8978_set_dai_fmt()
687 return -EINVAL; in wm8978_set_dai_fmt()
691 switch (fmt & SND_SOC_DAIFMT_INV_MASK) { in wm8978_set_dai_fmt()
704 return -EINVAL; in wm8978_set_dai_fmt()
720 struct snd_soc_component *component = dai->component; in wm8978_hw_params()
732 if (!wm8978->f_mclk) in wm8978_hw_params()
733 return -EINVAL; in wm8978_hw_params()
736 switch (params_width(params)) { in wm8978_hw_params()
751 switch (params_rate(params)) { in wm8978_hw_params()
773 wm8978->f_256fs = params_rate(params) * 256; in wm8978_hw_params()
775 if (wm8978->sysclk == WM8978_MCLK) { in wm8978_hw_params()
776 wm8978->mclk_idx = -1; in wm8978_hw_params()
777 f_sel = wm8978->f_mclk; in wm8978_hw_params()
779 if (!wm8978->f_opclk) { in wm8978_hw_params()
785 f_sel = wm8978->f_pllout; in wm8978_hw_params()
788 if (wm8978->mclk_idx < 0) { in wm8978_hw_params()
790 if (f_sel < wm8978->f_256fs || f_sel > 12 * wm8978->f_256fs) in wm8978_hw_params()
791 return -EINVAL; in wm8978_hw_params()
794 diff = abs(wm8978->f_256fs * 3 - in wm8978_hw_params()
807 best = wm8978->mclk_idx; in wm8978_hw_params()
812 dev_warn(component->dev, "Imprecise sampling rate: %uHz%s\n", in wm8978_hw_params()
814 wm8978->sysclk == WM8978_MCLK ? in wm8978_hw_params()
817 dev_dbg(component->dev, "%s: width %d, rate %u, MCLK divisor #%d\n", __func__, in wm8978_hw_params()
826 if (wm8978->sysclk != current_clk_id) { in wm8978_hw_params()
827 if (wm8978->sysclk == WM8978_PLL) in wm8978_hw_params()
841 struct snd_soc_component *component = dai->component; in wm8978_mute()
843 dev_dbg(component->dev, "%s: %d\n", __func__, mute); in wm8978_mute()
858 switch (level) { in wm8978_set_bias_level()
879 /* Preserve PLL - OPCLK may be used by someone */ in wm8978_set_bias_level()
886 dev_dbg(component->dev, "%s: %d, %x\n", __func__, level, power1); in wm8978_set_bias_level()
905 .name = "wm8978-hifi",
929 /* Also switch PLL off */ in wm8978_suspend()
932 regcache_mark_dirty(wm8978->regmap); in wm8978_suspend()
942 regcache_sync(wm8978->regmap); in wm8978_resume()
946 if (wm8978->f_pllout) in wm8978_resume()
947 /* Switch PLL on */ in wm8978_resume()
954 * These registers contain an "update" bit - bit 8. This means, for example,
956 * the update bit is set, will also the volume be updated - simultaneously for
981 wm8978->sysclk = WM8978_PLL; in wm8978_probe()
1027 wm8978 = devm_kzalloc(&i2c->dev, sizeof(struct wm8978_priv), in wm8978_i2c_probe()
1030 return -ENOMEM; in wm8978_i2c_probe()
1032 wm8978->regmap = devm_regmap_init_i2c(i2c, &wm8978_regmap_config); in wm8978_i2c_probe()
1033 if (IS_ERR(wm8978->regmap)) { in wm8978_i2c_probe()
1034 ret = PTR_ERR(wm8978->regmap); in wm8978_i2c_probe()
1035 dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); in wm8978_i2c_probe()
1042 ret = regmap_write(wm8978->regmap, WM8978_RESET, 0); in wm8978_i2c_probe()
1044 dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret); in wm8978_i2c_probe()
1048 ret = devm_snd_soc_register_component(&i2c->dev, in wm8978_i2c_probe()
1051 dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret); in wm8978_i2c_probe()