1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
25d421516SMark Brown /*
35d421516SMark Brown * wm8510.c -- WM8510 ALSA Soc Audio driver
45d421516SMark Brown *
55d421516SMark Brown * Copyright 2006 Wolfson Microelectronics PLC.
65d421516SMark Brown *
7d331124dSLiam Girdwood * Author: Liam Girdwood <lrg@slimlogic.co.uk>
85d421516SMark Brown */
95d421516SMark Brown
105d421516SMark Brown #include <linux/module.h>
115d421516SMark Brown #include <linux/moduleparam.h>
125d421516SMark Brown #include <linux/kernel.h>
135d421516SMark Brown #include <linux/init.h>
145d421516SMark Brown #include <linux/delay.h>
155d421516SMark Brown #include <linux/pm.h>
165d421516SMark Brown #include <linux/i2c.h>
175e357952SMark Brown #include <linux/spi/spi.h>
185a0e3ad6STejun Heo #include <linux/slab.h>
190a422e1aSMark Brown #include <linux/of_device.h>
20e643049dSMark Brown #include <linux/regmap.h>
215d421516SMark Brown #include <sound/core.h>
225d421516SMark Brown #include <sound/pcm.h>
235d421516SMark Brown #include <sound/pcm_params.h>
245d421516SMark Brown #include <sound/soc.h>
255d421516SMark Brown #include <sound/initval.h>
265d421516SMark Brown
275d421516SMark Brown #include "wm8510.h"
285d421516SMark Brown
295d421516SMark Brown /*
305d421516SMark Brown * wm8510 register cache
315d421516SMark Brown * We can't read the WM8510 register space when we are
325d421516SMark Brown * using 2 wire for device control, so we cache them instead.
335d421516SMark Brown */
34e643049dSMark Brown static const struct reg_default wm8510_reg_defaults[] = {
35e643049dSMark Brown { 1, 0x0000 },
36e643049dSMark Brown { 2, 0x0000 },
37e643049dSMark Brown { 3, 0x0000 },
38e643049dSMark Brown { 4, 0x0050 },
39e643049dSMark Brown { 5, 0x0000 },
40e643049dSMark Brown { 6, 0x0140 },
41e643049dSMark Brown { 7, 0x0000 },
42e643049dSMark Brown { 8, 0x0000 },
43e643049dSMark Brown { 9, 0x0000 },
44e643049dSMark Brown { 10, 0x0000 },
45e643049dSMark Brown { 11, 0x00ff },
46e643049dSMark Brown { 12, 0x0000 },
47e643049dSMark Brown { 13, 0x0000 },
48e643049dSMark Brown { 14, 0x0100 },
49e643049dSMark Brown { 15, 0x00ff },
50e643049dSMark Brown { 16, 0x0000 },
51e643049dSMark Brown { 17, 0x0000 },
52e643049dSMark Brown { 18, 0x012c },
53e643049dSMark Brown { 19, 0x002c },
54e643049dSMark Brown { 20, 0x002c },
55e643049dSMark Brown { 21, 0x002c },
56e643049dSMark Brown { 22, 0x002c },
57e643049dSMark Brown { 23, 0x0000 },
58e643049dSMark Brown { 24, 0x0032 },
59e643049dSMark Brown { 25, 0x0000 },
60e643049dSMark Brown { 26, 0x0000 },
61e643049dSMark Brown { 27, 0x0000 },
62e643049dSMark Brown { 28, 0x0000 },
63e643049dSMark Brown { 29, 0x0000 },
64e643049dSMark Brown { 30, 0x0000 },
65e643049dSMark Brown { 31, 0x0000 },
66e643049dSMark Brown { 32, 0x0038 },
67e643049dSMark Brown { 33, 0x000b },
68e643049dSMark Brown { 34, 0x0032 },
69e643049dSMark Brown { 35, 0x0000 },
70e643049dSMark Brown { 36, 0x0008 },
71e643049dSMark Brown { 37, 0x000c },
72e643049dSMark Brown { 38, 0x0093 },
73e643049dSMark Brown { 39, 0x00e9 },
74e643049dSMark Brown { 40, 0x0000 },
75e643049dSMark Brown { 41, 0x0000 },
76e643049dSMark Brown { 42, 0x0000 },
77e643049dSMark Brown { 43, 0x0000 },
78e643049dSMark Brown { 44, 0x0003 },
79e643049dSMark Brown { 45, 0x0010 },
80e643049dSMark Brown { 46, 0x0000 },
81e643049dSMark Brown { 47, 0x0000 },
82e643049dSMark Brown { 48, 0x0000 },
83e643049dSMark Brown { 49, 0x0002 },
84e643049dSMark Brown { 50, 0x0001 },
85e643049dSMark Brown { 51, 0x0000 },
86e643049dSMark Brown { 52, 0x0000 },
87e643049dSMark Brown { 53, 0x0000 },
88e643049dSMark Brown { 54, 0x0039 },
89e643049dSMark Brown { 55, 0x0000 },
90e643049dSMark Brown { 56, 0x0001 },
915d421516SMark Brown };
925d421516SMark Brown
wm8510_volatile(struct device * dev,unsigned int reg)93e643049dSMark Brown static bool wm8510_volatile(struct device *dev, unsigned int reg)
94e643049dSMark Brown {
95e643049dSMark Brown switch (reg) {
96e643049dSMark Brown case WM8510_RESET:
97e643049dSMark Brown return true;
98e643049dSMark Brown default:
99e643049dSMark Brown return false;
100e643049dSMark Brown }
101e643049dSMark Brown }
102e643049dSMark Brown
10309af98b0SMark Brown #define WM8510_POWER1_BIASEN 0x08
10409af98b0SMark Brown #define WM8510_POWER1_BUFIOEN 0x10
10509af98b0SMark Brown
10651bf02ccSKuninori Morimoto #define wm8510_reset(c) snd_soc_component_write(c, WM8510_RESET, 0)
1075d421516SMark Brown
108f0fba2adSLiam Girdwood /* codec private data */
109f0fba2adSLiam Girdwood struct wm8510_priv {
110e643049dSMark Brown struct regmap *regmap;
111f0fba2adSLiam Girdwood };
112f0fba2adSLiam Girdwood
1135d421516SMark Brown static const char *wm8510_companding[] = { "Off", "NC", "u-law", "A-law" };
1145d421516SMark Brown static const char *wm8510_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
1155d421516SMark Brown static const char *wm8510_alc[] = { "ALC", "Limiter" };
1165d421516SMark Brown
1175d421516SMark Brown static const struct soc_enum wm8510_enum[] = {
1185d421516SMark Brown SOC_ENUM_SINGLE(WM8510_COMP, 1, 4, wm8510_companding), /* adc */
1195d421516SMark Brown SOC_ENUM_SINGLE(WM8510_COMP, 3, 4, wm8510_companding), /* dac */
1205d421516SMark Brown SOC_ENUM_SINGLE(WM8510_DAC, 4, 4, wm8510_deemp),
1215d421516SMark Brown SOC_ENUM_SINGLE(WM8510_ALC3, 8, 2, wm8510_alc),
1225d421516SMark Brown };
1235d421516SMark Brown
1245d421516SMark Brown static const struct snd_kcontrol_new wm8510_snd_controls[] = {
1255d421516SMark Brown
1265d421516SMark Brown SOC_SINGLE("Digital Loopback Switch", WM8510_COMP, 0, 1, 0),
1275d421516SMark Brown
1285d421516SMark Brown SOC_ENUM("DAC Companding", wm8510_enum[1]),
1295d421516SMark Brown SOC_ENUM("ADC Companding", wm8510_enum[0]),
1305d421516SMark Brown
1315d421516SMark Brown SOC_ENUM("Playback De-emphasis", wm8510_enum[2]),
1325d421516SMark Brown SOC_SINGLE("DAC Inversion Switch", WM8510_DAC, 0, 1, 0),
1335d421516SMark Brown
1345d421516SMark Brown SOC_SINGLE("Master Playback Volume", WM8510_DACVOL, 0, 127, 0),
1355d421516SMark Brown
1365d421516SMark Brown SOC_SINGLE("High Pass Filter Switch", WM8510_ADC, 8, 1, 0),
1375d421516SMark Brown SOC_SINGLE("High Pass Cut Off", WM8510_ADC, 4, 7, 0),
1385d421516SMark Brown SOC_SINGLE("ADC Inversion Switch", WM8510_COMP, 0, 1, 0),
1395d421516SMark Brown
1405d421516SMark Brown SOC_SINGLE("Capture Volume", WM8510_ADCVOL, 0, 127, 0),
1415d421516SMark Brown
1425d421516SMark Brown SOC_SINGLE("DAC Playback Limiter Switch", WM8510_DACLIM1, 8, 1, 0),
1435d421516SMark Brown SOC_SINGLE("DAC Playback Limiter Decay", WM8510_DACLIM1, 4, 15, 0),
1445d421516SMark Brown SOC_SINGLE("DAC Playback Limiter Attack", WM8510_DACLIM1, 0, 15, 0),
1455d421516SMark Brown
1465d421516SMark Brown SOC_SINGLE("DAC Playback Limiter Threshold", WM8510_DACLIM2, 4, 7, 0),
1475d421516SMark Brown SOC_SINGLE("DAC Playback Limiter Boost", WM8510_DACLIM2, 0, 15, 0),
1485d421516SMark Brown
1495d421516SMark Brown SOC_SINGLE("ALC Enable Switch", WM8510_ALC1, 8, 1, 0),
1505d421516SMark Brown SOC_SINGLE("ALC Capture Max Gain", WM8510_ALC1, 3, 7, 0),
1515d421516SMark Brown SOC_SINGLE("ALC Capture Min Gain", WM8510_ALC1, 0, 7, 0),
1525d421516SMark Brown
1535d421516SMark Brown SOC_SINGLE("ALC Capture ZC Switch", WM8510_ALC2, 8, 1, 0),
1545d421516SMark Brown SOC_SINGLE("ALC Capture Hold", WM8510_ALC2, 4, 7, 0),
1555d421516SMark Brown SOC_SINGLE("ALC Capture Target", WM8510_ALC2, 0, 15, 0),
1565d421516SMark Brown
1575d421516SMark Brown SOC_ENUM("ALC Capture Mode", wm8510_enum[3]),
1585d421516SMark Brown SOC_SINGLE("ALC Capture Decay", WM8510_ALC3, 4, 15, 0),
1595d421516SMark Brown SOC_SINGLE("ALC Capture Attack", WM8510_ALC3, 0, 15, 0),
1605d421516SMark Brown
1615d421516SMark Brown SOC_SINGLE("ALC Capture Noise Gate Switch", WM8510_NGATE, 3, 1, 0),
1625d421516SMark Brown SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8510_NGATE, 0, 7, 0),
1635d421516SMark Brown
1645d421516SMark Brown SOC_SINGLE("Capture PGA ZC Switch", WM8510_INPPGA, 7, 1, 0),
1655d421516SMark Brown SOC_SINGLE("Capture PGA Volume", WM8510_INPPGA, 0, 63, 0),
1665d421516SMark Brown
1675d421516SMark Brown SOC_SINGLE("Speaker Playback ZC Switch", WM8510_SPKVOL, 7, 1, 0),
1685d421516SMark Brown SOC_SINGLE("Speaker Playback Switch", WM8510_SPKVOL, 6, 1, 1),
1695d421516SMark Brown SOC_SINGLE("Speaker Playback Volume", WM8510_SPKVOL, 0, 63, 0),
1705d421516SMark Brown SOC_SINGLE("Speaker Boost", WM8510_OUTPUT, 2, 1, 0),
1715d421516SMark Brown
1725d421516SMark Brown SOC_SINGLE("Capture Boost(+20dB)", WM8510_ADCBOOST, 8, 1, 0),
1735d421516SMark Brown SOC_SINGLE("Mono Playback Switch", WM8510_MONOMIX, 6, 1, 1),
1745d421516SMark Brown };
1755d421516SMark Brown
1765d421516SMark Brown /* Speaker Output Mixer */
1775d421516SMark Brown static const struct snd_kcontrol_new wm8510_speaker_mixer_controls[] = {
1785d421516SMark Brown SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_SPKMIX, 1, 1, 0),
1795d421516SMark Brown SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_SPKMIX, 5, 1, 0),
1805d421516SMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_SPKMIX, 0, 1, 0),
1815d421516SMark Brown };
1825d421516SMark Brown
1835d421516SMark Brown /* Mono Output Mixer */
1845d421516SMark Brown static const struct snd_kcontrol_new wm8510_mono_mixer_controls[] = {
1855d421516SMark Brown SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_MONOMIX, 1, 1, 0),
1865d421516SMark Brown SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_MONOMIX, 2, 1, 0),
1875d421516SMark Brown SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_MONOMIX, 0, 1, 0),
1885d421516SMark Brown };
1895d421516SMark Brown
1905d421516SMark Brown static const struct snd_kcontrol_new wm8510_boost_controls[] = {
1918ae23ec3SMark Brown SOC_DAPM_SINGLE("Mic PGA Switch", WM8510_INPPGA, 6, 1, 1),
1925d421516SMark Brown SOC_DAPM_SINGLE("Aux Volume", WM8510_ADCBOOST, 0, 7, 0),
1935d421516SMark Brown SOC_DAPM_SINGLE("Mic Volume", WM8510_ADCBOOST, 4, 7, 0),
1945d421516SMark Brown };
1955d421516SMark Brown
1965d421516SMark Brown static const struct snd_kcontrol_new wm8510_micpga_controls[] = {
1975d421516SMark Brown SOC_DAPM_SINGLE("MICP Switch", WM8510_INPUT, 0, 1, 0),
1985d421516SMark Brown SOC_DAPM_SINGLE("MICN Switch", WM8510_INPUT, 1, 1, 0),
1995d421516SMark Brown SOC_DAPM_SINGLE("AUX Switch", WM8510_INPUT, 2, 1, 0),
2005d421516SMark Brown };
2015d421516SMark Brown
2025d421516SMark Brown static const struct snd_soc_dapm_widget wm8510_dapm_widgets[] = {
2035d421516SMark Brown SND_SOC_DAPM_MIXER("Speaker Mixer", WM8510_POWER3, 2, 0,
2045d421516SMark Brown &wm8510_speaker_mixer_controls[0],
2055d421516SMark Brown ARRAY_SIZE(wm8510_speaker_mixer_controls)),
2065d421516SMark Brown SND_SOC_DAPM_MIXER("Mono Mixer", WM8510_POWER3, 3, 0,
2075d421516SMark Brown &wm8510_mono_mixer_controls[0],
2085d421516SMark Brown ARRAY_SIZE(wm8510_mono_mixer_controls)),
2095d421516SMark Brown SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8510_POWER3, 0, 0),
2105d421516SMark Brown SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8510_POWER2, 0, 0),
2115d421516SMark Brown SND_SOC_DAPM_PGA("Aux Input", WM8510_POWER1, 6, 0, NULL, 0),
2125d421516SMark Brown SND_SOC_DAPM_PGA("SpkN Out", WM8510_POWER3, 5, 0, NULL, 0),
2135d421516SMark Brown SND_SOC_DAPM_PGA("SpkP Out", WM8510_POWER3, 6, 0, NULL, 0),
2145d421516SMark Brown SND_SOC_DAPM_PGA("Mono Out", WM8510_POWER3, 7, 0, NULL, 0),
2155d421516SMark Brown
2162b5f34c5SMark Brown SND_SOC_DAPM_MIXER("Mic PGA", WM8510_POWER2, 2, 0,
2175d421516SMark Brown &wm8510_micpga_controls[0],
2185d421516SMark Brown ARRAY_SIZE(wm8510_micpga_controls)),
2195d421516SMark Brown SND_SOC_DAPM_MIXER("Boost Mixer", WM8510_POWER2, 4, 0,
2205d421516SMark Brown &wm8510_boost_controls[0],
2215d421516SMark Brown ARRAY_SIZE(wm8510_boost_controls)),
2225d421516SMark Brown
2235d421516SMark Brown SND_SOC_DAPM_MICBIAS("Mic Bias", WM8510_POWER1, 4, 0),
2245d421516SMark Brown
2255d421516SMark Brown SND_SOC_DAPM_INPUT("MICN"),
2265d421516SMark Brown SND_SOC_DAPM_INPUT("MICP"),
2275d421516SMark Brown SND_SOC_DAPM_INPUT("AUX"),
2285d421516SMark Brown SND_SOC_DAPM_OUTPUT("MONOOUT"),
2295d421516SMark Brown SND_SOC_DAPM_OUTPUT("SPKOUTP"),
2305d421516SMark Brown SND_SOC_DAPM_OUTPUT("SPKOUTN"),
2315d421516SMark Brown };
2325d421516SMark Brown
233b6709f3bSMark Brown static const struct snd_soc_dapm_route wm8510_dapm_routes[] = {
2345d421516SMark Brown /* Mono output mixer */
2355d421516SMark Brown {"Mono Mixer", "PCM Playback Switch", "DAC"},
2365d421516SMark Brown {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
2375d421516SMark Brown {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
2385d421516SMark Brown
2395d421516SMark Brown /* Speaker output mixer */
2405d421516SMark Brown {"Speaker Mixer", "PCM Playback Switch", "DAC"},
2415d421516SMark Brown {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
2425d421516SMark Brown {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
2435d421516SMark Brown
2445d421516SMark Brown /* Outputs */
2455d421516SMark Brown {"Mono Out", NULL, "Mono Mixer"},
2465d421516SMark Brown {"MONOOUT", NULL, "Mono Out"},
2475d421516SMark Brown {"SpkN Out", NULL, "Speaker Mixer"},
2485d421516SMark Brown {"SpkP Out", NULL, "Speaker Mixer"},
2495d421516SMark Brown {"SPKOUTN", NULL, "SpkN Out"},
2505d421516SMark Brown {"SPKOUTP", NULL, "SpkP Out"},
2515d421516SMark Brown
2525d421516SMark Brown /* Microphone PGA */
2535d421516SMark Brown {"Mic PGA", "MICN Switch", "MICN"},
2545d421516SMark Brown {"Mic PGA", "MICP Switch", "MICP"},
2555d421516SMark Brown { "Mic PGA", "AUX Switch", "Aux Input" },
2565d421516SMark Brown
2575d421516SMark Brown /* Boost Mixer */
2585d421516SMark Brown {"Boost Mixer", "Mic PGA Switch", "Mic PGA"},
2595d421516SMark Brown {"Boost Mixer", "Mic Volume", "MICP"},
2605d421516SMark Brown {"Boost Mixer", "Aux Volume", "Aux Input"},
2615d421516SMark Brown
2625d421516SMark Brown {"ADC", NULL, "Boost Mixer"},
2635d421516SMark Brown };
2645d421516SMark Brown
2655d421516SMark Brown struct pll_ {
2665d421516SMark Brown unsigned int pre_div:4; /* prescale - 1 */
2675d421516SMark Brown unsigned int n:4;
2685d421516SMark Brown unsigned int k;
2695d421516SMark Brown };
2705d421516SMark Brown
2715d421516SMark Brown static struct pll_ pll_div;
2725d421516SMark Brown
2735d421516SMark Brown /* The size in bits of the pll divide multiplied by 10
2745d421516SMark Brown * to allow rounding later */
2755d421516SMark Brown #define FIXED_PLL_SIZE ((1 << 24) * 10)
2765d421516SMark Brown
pll_factors(unsigned int target,unsigned int source)2775d421516SMark Brown static void pll_factors(unsigned int target, unsigned int source)
2785d421516SMark Brown {
2795d421516SMark Brown unsigned long long Kpart;
2805d421516SMark Brown unsigned int K, Ndiv, Nmod;
2815d421516SMark Brown
2825d421516SMark Brown Ndiv = target / source;
2835d421516SMark Brown if (Ndiv < 6) {
2845d421516SMark Brown source >>= 1;
2855d421516SMark Brown pll_div.pre_div = 1;
2865d421516SMark Brown Ndiv = target / source;
2875d421516SMark Brown } else
2885d421516SMark Brown pll_div.pre_div = 0;
2895d421516SMark Brown
2905d421516SMark Brown if ((Ndiv < 6) || (Ndiv > 12))
2915d421516SMark Brown printk(KERN_WARNING
292449bd54dSRoel Kluin "WM8510 N value %u outwith recommended range!d\n",
2935d421516SMark Brown Ndiv);
2945d421516SMark Brown
2955d421516SMark Brown pll_div.n = Ndiv;
2965d421516SMark Brown Nmod = target % source;
2975d421516SMark Brown Kpart = FIXED_PLL_SIZE * (long long)Nmod;
2985d421516SMark Brown
2995d421516SMark Brown do_div(Kpart, source);
3005d421516SMark Brown
3015d421516SMark Brown K = Kpart & 0xFFFFFFFF;
3025d421516SMark Brown
3035d421516SMark Brown /* Check if we need to round */
3045d421516SMark Brown if ((K % 10) >= 5)
3055d421516SMark Brown K += 5;
3065d421516SMark Brown
3075d421516SMark Brown /* Move down to proper range now rounding is done */
3085d421516SMark Brown K /= 10;
3095d421516SMark Brown
3105d421516SMark Brown pll_div.k = K;
3115d421516SMark Brown }
3125d421516SMark Brown
wm8510_set_dai_pll(struct snd_soc_dai * codec_dai,int pll_id,int source,unsigned int freq_in,unsigned int freq_out)31385488037SMark Brown static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
31485488037SMark Brown int source, unsigned int freq_in, unsigned int freq_out)
3155d421516SMark Brown {
31651bf02ccSKuninori Morimoto struct snd_soc_component *component = codec_dai->component;
3175d421516SMark Brown u16 reg;
3185d421516SMark Brown
3195d421516SMark Brown if (freq_in == 0 || freq_out == 0) {
3205d421516SMark Brown /* Clock CODEC directly from MCLK */
3216d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8510_CLOCK);
32251bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_CLOCK, reg & 0x0ff);
3235d421516SMark Brown
3245d421516SMark Brown /* Turn off PLL */
3256d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8510_POWER1);
32651bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_POWER1, reg & 0x1df);
3275d421516SMark Brown return 0;
3285d421516SMark Brown }
3295d421516SMark Brown
33086027ae7SJonas Andersson pll_factors(freq_out*4, freq_in);
3315d421516SMark Brown
33251bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n);
33351bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_PLLK1, pll_div.k >> 18);
33451bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_PLLK2, (pll_div.k >> 9) & 0x1ff);
33551bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_PLLK3, pll_div.k & 0x1ff);
3366d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8510_POWER1);
33751bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_POWER1, reg | 0x020);
3385d421516SMark Brown
3395d421516SMark Brown /* Run CODEC from PLL instead of MCLK */
3406d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8510_CLOCK);
34151bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_CLOCK, reg | 0x100);
3425d421516SMark Brown
3435d421516SMark Brown return 0;
3445d421516SMark Brown }
3455d421516SMark Brown
3465d421516SMark Brown /*
3475d421516SMark Brown * Configure WM8510 clock dividers.
3485d421516SMark Brown */
wm8510_set_dai_clkdiv(struct snd_soc_dai * codec_dai,int div_id,int div)349e550e17fSLiam Girdwood static int wm8510_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
3505d421516SMark Brown int div_id, int div)
3515d421516SMark Brown {
35251bf02ccSKuninori Morimoto struct snd_soc_component *component = codec_dai->component;
3535d421516SMark Brown u16 reg;
3545d421516SMark Brown
3555d421516SMark Brown switch (div_id) {
3565d421516SMark Brown case WM8510_OPCLKDIV:
3576d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8510_GPIO) & 0x1cf;
35851bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_GPIO, reg | div);
3595d421516SMark Brown break;
3605d421516SMark Brown case WM8510_MCLKDIV:
3616d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8510_CLOCK) & 0x11f;
36251bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_CLOCK, reg | div);
3635d421516SMark Brown break;
3645d421516SMark Brown case WM8510_ADCCLK:
3656d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8510_ADC) & 0x1f7;
36651bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_ADC, reg | div);
3675d421516SMark Brown break;
3685d421516SMark Brown case WM8510_DACCLK:
3696d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8510_DAC) & 0x1f7;
37051bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_DAC, reg | div);
3715d421516SMark Brown break;
3725d421516SMark Brown case WM8510_BCLKDIV:
3736d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8510_CLOCK) & 0x1e3;
37451bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_CLOCK, reg | div);
3755d421516SMark Brown break;
3765d421516SMark Brown default:
3775d421516SMark Brown return -EINVAL;
3785d421516SMark Brown }
3795d421516SMark Brown
3805d421516SMark Brown return 0;
3815d421516SMark Brown }
3825d421516SMark Brown
wm8510_set_dai_fmt(struct snd_soc_dai * codec_dai,unsigned int fmt)383e550e17fSLiam Girdwood static int wm8510_set_dai_fmt(struct snd_soc_dai *codec_dai,
3845d421516SMark Brown unsigned int fmt)
3855d421516SMark Brown {
38651bf02ccSKuninori Morimoto struct snd_soc_component *component = codec_dai->component;
3875d421516SMark Brown u16 iface = 0;
3886d75dfc3SKuninori Morimoto u16 clk = snd_soc_component_read(component, WM8510_CLOCK) & 0x1fe;
3895d421516SMark Brown
3905d421516SMark Brown /* set master/slave audio interface */
3915d421516SMark Brown switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
3925d421516SMark Brown case SND_SOC_DAIFMT_CBM_CFM:
3935d421516SMark Brown clk |= 0x0001;
3945d421516SMark Brown break;
3955d421516SMark Brown case SND_SOC_DAIFMT_CBS_CFS:
3965d421516SMark Brown break;
3975d421516SMark Brown default:
3985d421516SMark Brown return -EINVAL;
3995d421516SMark Brown }
4005d421516SMark Brown
4015d421516SMark Brown /* interface format */
4025d421516SMark Brown switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
4035d421516SMark Brown case SND_SOC_DAIFMT_I2S:
4045d421516SMark Brown iface |= 0x0010;
4055d421516SMark Brown break;
4065d421516SMark Brown case SND_SOC_DAIFMT_RIGHT_J:
4075d421516SMark Brown break;
4085d421516SMark Brown case SND_SOC_DAIFMT_LEFT_J:
4095d421516SMark Brown iface |= 0x0008;
4105d421516SMark Brown break;
4115d421516SMark Brown case SND_SOC_DAIFMT_DSP_A:
4125d421516SMark Brown iface |= 0x00018;
4135d421516SMark Brown break;
4145d421516SMark Brown default:
4155d421516SMark Brown return -EINVAL;
4165d421516SMark Brown }
4175d421516SMark Brown
4185d421516SMark Brown /* clock inversion */
4195d421516SMark Brown switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
4205d421516SMark Brown case SND_SOC_DAIFMT_NB_NF:
4215d421516SMark Brown break;
4225d421516SMark Brown case SND_SOC_DAIFMT_IB_IF:
4235d421516SMark Brown iface |= 0x0180;
4245d421516SMark Brown break;
4255d421516SMark Brown case SND_SOC_DAIFMT_IB_NF:
4265d421516SMark Brown iface |= 0x0100;
4275d421516SMark Brown break;
4285d421516SMark Brown case SND_SOC_DAIFMT_NB_IF:
4295d421516SMark Brown iface |= 0x0080;
4305d421516SMark Brown break;
4315d421516SMark Brown default:
4325d421516SMark Brown return -EINVAL;
4335d421516SMark Brown }
4345d421516SMark Brown
43551bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_IFACE, iface);
43651bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_CLOCK, clk);
4375d421516SMark Brown return 0;
4385d421516SMark Brown }
4395d421516SMark Brown
wm8510_pcm_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)4405d421516SMark Brown static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream,
441dee89c4dSMark Brown struct snd_pcm_hw_params *params,
442dee89c4dSMark Brown struct snd_soc_dai *dai)
4435d421516SMark Brown {
44451bf02ccSKuninori Morimoto struct snd_soc_component *component = dai->component;
4456d75dfc3SKuninori Morimoto u16 iface = snd_soc_component_read(component, WM8510_IFACE) & 0x19f;
4466d75dfc3SKuninori Morimoto u16 adn = snd_soc_component_read(component, WM8510_ADD) & 0x1f1;
4475d421516SMark Brown
4485d421516SMark Brown /* bit size */
44919e4b788SMark Brown switch (params_width(params)) {
45019e4b788SMark Brown case 16:
4515d421516SMark Brown break;
45219e4b788SMark Brown case 20:
4535d421516SMark Brown iface |= 0x0020;
4545d421516SMark Brown break;
45519e4b788SMark Brown case 24:
4565d421516SMark Brown iface |= 0x0040;
4575d421516SMark Brown break;
45819e4b788SMark Brown case 32:
4595d421516SMark Brown iface |= 0x0060;
4605d421516SMark Brown break;
4615d421516SMark Brown }
4625d421516SMark Brown
4635d421516SMark Brown /* filter coefficient */
4645d421516SMark Brown switch (params_rate(params)) {
465b3172f22SGuennadi Liakhovetski case 8000:
4665d421516SMark Brown adn |= 0x5 << 1;
4675d421516SMark Brown break;
468b3172f22SGuennadi Liakhovetski case 11025:
4695d421516SMark Brown adn |= 0x4 << 1;
4705d421516SMark Brown break;
471b3172f22SGuennadi Liakhovetski case 16000:
4725d421516SMark Brown adn |= 0x3 << 1;
4735d421516SMark Brown break;
474b3172f22SGuennadi Liakhovetski case 22050:
4755d421516SMark Brown adn |= 0x2 << 1;
4765d421516SMark Brown break;
477b3172f22SGuennadi Liakhovetski case 32000:
4785d421516SMark Brown adn |= 0x1 << 1;
4795d421516SMark Brown break;
480b3172f22SGuennadi Liakhovetski case 44100:
481b3172f22SGuennadi Liakhovetski case 48000:
4825d421516SMark Brown break;
4835d421516SMark Brown }
4845d421516SMark Brown
48551bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_IFACE, iface);
48651bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_ADD, adn);
4875d421516SMark Brown return 0;
4885d421516SMark Brown }
4895d421516SMark Brown
wm8510_mute(struct snd_soc_dai * dai,int mute,int direction)49026d3c16eSKuninori Morimoto static int wm8510_mute(struct snd_soc_dai *dai, int mute, int direction)
4915d421516SMark Brown {
49251bf02ccSKuninori Morimoto struct snd_soc_component *component = dai->component;
4936d75dfc3SKuninori Morimoto u16 mute_reg = snd_soc_component_read(component, WM8510_DAC) & 0xffbf;
4945d421516SMark Brown
4955d421516SMark Brown if (mute)
49651bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_DAC, mute_reg | 0x40);
4975d421516SMark Brown else
49851bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_DAC, mute_reg);
4995d421516SMark Brown return 0;
5005d421516SMark Brown }
5015d421516SMark Brown
5025d421516SMark Brown /* liam need to make this lower power with dapm */
wm8510_set_bias_level(struct snd_soc_component * component,enum snd_soc_bias_level level)50351bf02ccSKuninori Morimoto static int wm8510_set_bias_level(struct snd_soc_component *component,
5045d421516SMark Brown enum snd_soc_bias_level level)
5055d421516SMark Brown {
50651bf02ccSKuninori Morimoto struct wm8510_priv *wm8510 = snd_soc_component_get_drvdata(component);
5076d75dfc3SKuninori Morimoto u16 power1 = snd_soc_component_read(component, WM8510_POWER1) & ~0x3;
5085d421516SMark Brown
5095d421516SMark Brown switch (level) {
5105d421516SMark Brown case SND_SOC_BIAS_ON:
5115d421516SMark Brown case SND_SOC_BIAS_PREPARE:
51209af98b0SMark Brown power1 |= 0x1; /* VMID 50k */
51351bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_POWER1, power1);
5145d421516SMark Brown break;
51509af98b0SMark Brown
51609af98b0SMark Brown case SND_SOC_BIAS_STANDBY:
51709af98b0SMark Brown power1 |= WM8510_POWER1_BIASEN | WM8510_POWER1_BUFIOEN;
51809af98b0SMark Brown
51951bf02ccSKuninori Morimoto if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
520e643049dSMark Brown regcache_sync(wm8510->regmap);
52194f17e9cSAxel Lin
52209af98b0SMark Brown /* Initial cap charge at VMID 5k */
52351bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_POWER1, power1 | 0x3);
52409af98b0SMark Brown mdelay(100);
52509af98b0SMark Brown }
52609af98b0SMark Brown
52709af98b0SMark Brown power1 |= 0x2; /* VMID 500k */
52851bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_POWER1, power1);
52909af98b0SMark Brown break;
53009af98b0SMark Brown
5315d421516SMark Brown case SND_SOC_BIAS_OFF:
53251bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_POWER1, 0);
53351bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_POWER2, 0);
53451bf02ccSKuninori Morimoto snd_soc_component_write(component, WM8510_POWER3, 0);
5355d421516SMark Brown break;
5365d421516SMark Brown }
53709af98b0SMark Brown
5385d421516SMark Brown return 0;
5395d421516SMark Brown }
5405d421516SMark Brown
5415d421516SMark Brown #define WM8510_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
5425d421516SMark Brown SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
5435d421516SMark Brown SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
5445d421516SMark Brown
5455d421516SMark Brown #define WM8510_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
5465d421516SMark Brown SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
5475d421516SMark Brown
54885e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops wm8510_dai_ops = {
5496335d055SEric Miao .hw_params = wm8510_pcm_hw_params,
55026d3c16eSKuninori Morimoto .mute_stream = wm8510_mute,
5516335d055SEric Miao .set_fmt = wm8510_set_dai_fmt,
5526335d055SEric Miao .set_clkdiv = wm8510_set_dai_clkdiv,
5536335d055SEric Miao .set_pll = wm8510_set_dai_pll,
55426d3c16eSKuninori Morimoto .no_capture_mute = 1,
5556335d055SEric Miao };
5566335d055SEric Miao
557f0fba2adSLiam Girdwood static struct snd_soc_dai_driver wm8510_dai = {
558f0fba2adSLiam Girdwood .name = "wm8510-hifi",
5595d421516SMark Brown .playback = {
5605d421516SMark Brown .stream_name = "Playback",
5615d421516SMark Brown .channels_min = 2,
5625d421516SMark Brown .channels_max = 2,
5635d421516SMark Brown .rates = WM8510_RATES,
5645d421516SMark Brown .formats = WM8510_FORMATS,},
5655d421516SMark Brown .capture = {
5665d421516SMark Brown .stream_name = "Capture",
5675d421516SMark Brown .channels_min = 2,
5685d421516SMark Brown .channels_max = 2,
5695d421516SMark Brown .rates = WM8510_RATES,
5705d421516SMark Brown .formats = WM8510_FORMATS,},
5716335d055SEric Miao .ops = &wm8510_dai_ops,
57207695752SKuninori Morimoto .symmetric_rate = 1,
5735d421516SMark Brown };
5745d421516SMark Brown
wm8510_probe(struct snd_soc_component * component)57551bf02ccSKuninori Morimoto static int wm8510_probe(struct snd_soc_component *component)
5765d421516SMark Brown {
57751bf02ccSKuninori Morimoto wm8510_reset(component);
5785d421516SMark Brown
5795d421516SMark Brown return 0;
5805d421516SMark Brown }
5815d421516SMark Brown
58251bf02ccSKuninori Morimoto static const struct snd_soc_component_driver soc_component_dev_wm8510 = {
583f0fba2adSLiam Girdwood .probe = wm8510_probe,
584f0fba2adSLiam Girdwood .set_bias_level = wm8510_set_bias_level,
585b6709f3bSMark Brown .controls = wm8510_snd_controls,
586b6709f3bSMark Brown .num_controls = ARRAY_SIZE(wm8510_snd_controls),
587b6709f3bSMark Brown .dapm_widgets = wm8510_dapm_widgets,
588b6709f3bSMark Brown .num_dapm_widgets = ARRAY_SIZE(wm8510_dapm_widgets),
589b6709f3bSMark Brown .dapm_routes = wm8510_dapm_routes,
590b6709f3bSMark Brown .num_dapm_routes = ARRAY_SIZE(wm8510_dapm_routes),
59151bf02ccSKuninori Morimoto .suspend_bias_off = 1,
59251bf02ccSKuninori Morimoto .idle_bias_on = 1,
59351bf02ccSKuninori Morimoto .use_pmdown_time = 1,
59451bf02ccSKuninori Morimoto .endianness = 1,
59541759c2eSJean Delvare };
5965d421516SMark Brown
5970a422e1aSMark Brown static const struct of_device_id wm8510_of_match[] = {
5980a422e1aSMark Brown { .compatible = "wlf,wm8510" },
5990a422e1aSMark Brown { },
6000a422e1aSMark Brown };
601bf08f39eSJavier Martinez Canillas MODULE_DEVICE_TABLE(of, wm8510_of_match);
6020a422e1aSMark Brown
603e643049dSMark Brown static const struct regmap_config wm8510_regmap = {
604e643049dSMark Brown .reg_bits = 7,
605e643049dSMark Brown .val_bits = 9,
606e643049dSMark Brown .max_register = WM8510_MONOMIX,
607e643049dSMark Brown
608e643049dSMark Brown .reg_defaults = wm8510_reg_defaults,
609e643049dSMark Brown .num_reg_defaults = ARRAY_SIZE(wm8510_reg_defaults),
610*d3c4ba7dSMark Brown .cache_type = REGCACHE_MAPLE,
611e643049dSMark Brown
612e643049dSMark Brown .volatile_reg = wm8510_volatile,
613e643049dSMark Brown };
614e643049dSMark Brown
6155e357952SMark Brown #if defined(CONFIG_SPI_MASTER)
wm8510_spi_probe(struct spi_device * spi)6167a79e94eSBill Pemberton static int wm8510_spi_probe(struct spi_device *spi)
6175e357952SMark Brown {
618f0fba2adSLiam Girdwood struct wm8510_priv *wm8510;
6195e357952SMark Brown int ret;
6205e357952SMark Brown
6213217b0f5SMark Brown wm8510 = devm_kzalloc(&spi->dev, sizeof(struct wm8510_priv),
6223217b0f5SMark Brown GFP_KERNEL);
623f0fba2adSLiam Girdwood if (wm8510 == NULL)
624f0fba2adSLiam Girdwood return -ENOMEM;
6255e357952SMark Brown
626e643049dSMark Brown wm8510->regmap = devm_regmap_init_spi(spi, &wm8510_regmap);
627e643049dSMark Brown if (IS_ERR(wm8510->regmap))
628e643049dSMark Brown return PTR_ERR(wm8510->regmap);
629e643049dSMark Brown
630f0fba2adSLiam Girdwood spi_set_drvdata(spi, wm8510);
631f0fba2adSLiam Girdwood
63251bf02ccSKuninori Morimoto ret = devm_snd_soc_register_component(&spi->dev,
63351bf02ccSKuninori Morimoto &soc_component_dev_wm8510, &wm8510_dai, 1);
6343217b0f5SMark Brown
6355e357952SMark Brown return ret;
6365e357952SMark Brown }
6375e357952SMark Brown
6385e357952SMark Brown static struct spi_driver wm8510_spi_driver = {
6395e357952SMark Brown .driver = {
6405e357952SMark Brown .name = "wm8510",
6410a422e1aSMark Brown .of_match_table = wm8510_of_match,
6425e357952SMark Brown },
6435e357952SMark Brown .probe = wm8510_spi_probe,
6445e357952SMark Brown };
6455e357952SMark Brown #endif /* CONFIG_SPI_MASTER */
6465e357952SMark Brown
6478e6ad35aSFabio Estevam #if IS_ENABLED(CONFIG_I2C)
wm8510_i2c_probe(struct i2c_client * i2c)64897b0b6e3SStephen Kitt static int wm8510_i2c_probe(struct i2c_client *i2c)
6495d421516SMark Brown {
650f0fba2adSLiam Girdwood struct wm8510_priv *wm8510;
651f0fba2adSLiam Girdwood int ret;
6525d421516SMark Brown
6533217b0f5SMark Brown wm8510 = devm_kzalloc(&i2c->dev, sizeof(struct wm8510_priv),
6543217b0f5SMark Brown GFP_KERNEL);
655f0fba2adSLiam Girdwood if (wm8510 == NULL)
6565d421516SMark Brown return -ENOMEM;
6575d421516SMark Brown
658e643049dSMark Brown wm8510->regmap = devm_regmap_init_i2c(i2c, &wm8510_regmap);
659e643049dSMark Brown if (IS_ERR(wm8510->regmap))
660e643049dSMark Brown return PTR_ERR(wm8510->regmap);
661e643049dSMark Brown
662f0fba2adSLiam Girdwood i2c_set_clientdata(i2c, wm8510);
6635d421516SMark Brown
66451bf02ccSKuninori Morimoto ret = devm_snd_soc_register_component(&i2c->dev,
66551bf02ccSKuninori Morimoto &soc_component_dev_wm8510, &wm8510_dai, 1);
6663217b0f5SMark Brown
6675d421516SMark Brown return ret;
6685d421516SMark Brown }
6695d421516SMark Brown
670f0fba2adSLiam Girdwood static const struct i2c_device_id wm8510_i2c_id[] = {
671f0fba2adSLiam Girdwood { "wm8510", 0 },
672f0fba2adSLiam Girdwood { }
6735d421516SMark Brown };
674f0fba2adSLiam Girdwood MODULE_DEVICE_TABLE(i2c, wm8510_i2c_id);
675f0fba2adSLiam Girdwood
676f0fba2adSLiam Girdwood static struct i2c_driver wm8510_i2c_driver = {
677f0fba2adSLiam Girdwood .driver = {
678091edccfSMark Brown .name = "wm8510",
6790a422e1aSMark Brown .of_match_table = wm8510_of_match,
680f0fba2adSLiam Girdwood },
6819abcd240SUwe Kleine-König .probe = wm8510_i2c_probe,
682f0fba2adSLiam Girdwood .id_table = wm8510_i2c_id,
683f0fba2adSLiam Girdwood };
684f0fba2adSLiam Girdwood #endif
6855d421516SMark Brown
wm8510_modinit(void)686c9b3a40fSTakashi Iwai static int __init wm8510_modinit(void)
68764089b84SMark Brown {
688f0fba2adSLiam Girdwood int ret = 0;
6898e6ad35aSFabio Estevam #if IS_ENABLED(CONFIG_I2C)
690f0fba2adSLiam Girdwood ret = i2c_add_driver(&wm8510_i2c_driver);
691f0fba2adSLiam Girdwood if (ret != 0) {
692f0fba2adSLiam Girdwood printk(KERN_ERR "Failed to register WM8510 I2C driver: %d\n",
693f0fba2adSLiam Girdwood ret);
694f0fba2adSLiam Girdwood }
695f0fba2adSLiam Girdwood #endif
696f0fba2adSLiam Girdwood #if defined(CONFIG_SPI_MASTER)
697f0fba2adSLiam Girdwood ret = spi_register_driver(&wm8510_spi_driver);
698f0fba2adSLiam Girdwood if (ret != 0) {
699f0fba2adSLiam Girdwood printk(KERN_ERR "Failed to register WM8510 SPI driver: %d\n",
700f0fba2adSLiam Girdwood ret);
701f0fba2adSLiam Girdwood }
702f0fba2adSLiam Girdwood #endif
703f0fba2adSLiam Girdwood return ret;
70464089b84SMark Brown }
70564089b84SMark Brown module_init(wm8510_modinit);
70664089b84SMark Brown
wm8510_exit(void)70764089b84SMark Brown static void __exit wm8510_exit(void)
70864089b84SMark Brown {
7098e6ad35aSFabio Estevam #if IS_ENABLED(CONFIG_I2C)
710f0fba2adSLiam Girdwood i2c_del_driver(&wm8510_i2c_driver);
711f0fba2adSLiam Girdwood #endif
712f0fba2adSLiam Girdwood #if defined(CONFIG_SPI_MASTER)
713f0fba2adSLiam Girdwood spi_unregister_driver(&wm8510_spi_driver);
714f0fba2adSLiam Girdwood #endif
71564089b84SMark Brown }
71664089b84SMark Brown module_exit(wm8510_exit);
71764089b84SMark Brown
7185d421516SMark Brown MODULE_DESCRIPTION("ASoC WM8510 driver");
7195d421516SMark Brown MODULE_AUTHOR("Liam Girdwood");
7205d421516SMark Brown MODULE_LICENSE("GPL");
721