1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
240e0aa64SRichard Purdie /*
340e0aa64SRichard Purdie * wm8731.c -- WM8731 ALSA SoC Audio driver
440e0aa64SRichard Purdie *
540e0aa64SRichard Purdie * Copyright 2005 Openedhand Ltd.
6656baaebSMark Brown * Copyright 2006-12 Wolfson Microelectronics, plc
740e0aa64SRichard Purdie *
840e0aa64SRichard Purdie * Author: Richard Purdie <richard@openedhand.com>
940e0aa64SRichard Purdie *
1040e0aa64SRichard Purdie * Based on wm8753.c by Liam Girdwood
1140e0aa64SRichard Purdie */
1240e0aa64SRichard Purdie
1340e0aa64SRichard Purdie #include <linux/module.h>
1440e0aa64SRichard Purdie #include <linux/moduleparam.h>
1540e0aa64SRichard Purdie #include <linux/init.h>
1640e0aa64SRichard Purdie #include <linux/delay.h>
1740e0aa64SRichard Purdie #include <linux/pm.h>
185a0e3ad6STejun Heo #include <linux/slab.h>
1905d448e2SMark Brown #include <linux/regmap.h>
207dea7c01SMark Brown #include <linux/regulator/consumer.h>
2199d42234SSongjun Wu #include <linux/clk.h>
2240e0aa64SRichard Purdie #include <sound/core.h>
2340e0aa64SRichard Purdie #include <sound/pcm.h>
2440e0aa64SRichard Purdie #include <sound/pcm_params.h>
2540e0aa64SRichard Purdie #include <sound/soc.h>
2640e0aa64SRichard Purdie #include <sound/initval.h>
27d00efa64SMark Brown #include <sound/tlv.h>
2840e0aa64SRichard Purdie
2940e0aa64SRichard Purdie #include "wm8731.h"
3040e0aa64SRichard Purdie
317dea7c01SMark Brown static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = {
327dea7c01SMark Brown "AVDD",
337dea7c01SMark Brown "HPVDD",
347dea7c01SMark Brown "DCVDD",
357dea7c01SMark Brown "DBVDD",
367dea7c01SMark Brown };
377dea7c01SMark Brown
3840e0aa64SRichard Purdie /*
3940e0aa64SRichard Purdie * wm8731 register cache
4040e0aa64SRichard Purdie */
4105d448e2SMark Brown static const struct reg_default wm8731_reg_defaults[] = {
4205d448e2SMark Brown { 0, 0x0097 },
4305d448e2SMark Brown { 1, 0x0097 },
4405d448e2SMark Brown { 2, 0x0079 },
4505d448e2SMark Brown { 3, 0x0079 },
4605d448e2SMark Brown { 4, 0x000a },
4705d448e2SMark Brown { 5, 0x0008 },
4805d448e2SMark Brown { 6, 0x009f },
4905d448e2SMark Brown { 7, 0x000a },
5005d448e2SMark Brown { 8, 0x0000 },
5105d448e2SMark Brown { 9, 0x0000 },
5240e0aa64SRichard Purdie };
5340e0aa64SRichard Purdie
wm8731_volatile(struct device * dev,unsigned int reg)5405d448e2SMark Brown static bool wm8731_volatile(struct device *dev, unsigned int reg)
5505d448e2SMark Brown {
5605d448e2SMark Brown return reg == WM8731_RESET;
5705d448e2SMark Brown }
5805d448e2SMark Brown
596702dfccSSergey Kiselev #define wm8731_reset(m) regmap_write(m, WM8731_RESET, 0)
6040e0aa64SRichard Purdie
6140e0aa64SRichard Purdie static const char *wm8731_input_select[] = {"Line In", "Mic"};
6259f72970SMark Brown
639e74b14aSTakashi Iwai static SOC_ENUM_SINGLE_DECL(wm8731_insel_enum,
649e74b14aSTakashi Iwai WM8731_APANA, 2, wm8731_input_select);
6559f72970SMark Brown
66dd31b310SMark Brown static int wm8731_deemph[] = { 0, 32000, 44100, 48000 };
6759f72970SMark Brown
wm8731_set_deemph(struct snd_soc_component * component)68cde596c7SKuninori Morimoto static int wm8731_set_deemph(struct snd_soc_component *component)
69dd31b310SMark Brown {
70cde596c7SKuninori Morimoto struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component);
71dd31b310SMark Brown int val, i, best;
72dd31b310SMark Brown
73dd31b310SMark Brown /* If we're using deemphasis select the nearest available sample
74dd31b310SMark Brown * rate.
75dd31b310SMark Brown */
76dd31b310SMark Brown if (wm8731->deemph) {
77dd31b310SMark Brown best = 1;
78dd31b310SMark Brown for (i = 2; i < ARRAY_SIZE(wm8731_deemph); i++) {
79dd31b310SMark Brown if (abs(wm8731_deemph[i] - wm8731->playback_fs) <
80dd31b310SMark Brown abs(wm8731_deemph[best] - wm8731->playback_fs))
81dd31b310SMark Brown best = i;
82dd31b310SMark Brown }
83dd31b310SMark Brown
84dd31b310SMark Brown val = best << 1;
85dd31b310SMark Brown } else {
86dd31b310SMark Brown best = 0;
87dd31b310SMark Brown val = 0;
88dd31b310SMark Brown }
89dd31b310SMark Brown
90cde596c7SKuninori Morimoto dev_dbg(component->dev, "Set deemphasis %d (%dHz)\n",
91dd31b310SMark Brown best, wm8731_deemph[best]);
92dd31b310SMark Brown
93cde596c7SKuninori Morimoto return snd_soc_component_update_bits(component, WM8731_APDIGI, 0x6, val);
94dd31b310SMark Brown }
95dd31b310SMark Brown
wm8731_get_deemph(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)96dd31b310SMark Brown static int wm8731_get_deemph(struct snd_kcontrol *kcontrol,
97dd31b310SMark Brown struct snd_ctl_elem_value *ucontrol)
98dd31b310SMark Brown {
99cde596c7SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
100cde596c7SKuninori Morimoto struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component);
101dd31b310SMark Brown
102bd14016fSTakashi Iwai ucontrol->value.integer.value[0] = wm8731->deemph;
103dd31b310SMark Brown
104dd31b310SMark Brown return 0;
105dd31b310SMark Brown }
106dd31b310SMark Brown
wm8731_put_deemph(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)107dd31b310SMark Brown static int wm8731_put_deemph(struct snd_kcontrol *kcontrol,
108dd31b310SMark Brown struct snd_ctl_elem_value *ucontrol)
109dd31b310SMark Brown {
110cde596c7SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
111cde596c7SKuninori Morimoto struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component);
112523bade2SDan Carpenter unsigned int deemph = ucontrol->value.integer.value[0];
113dd31b310SMark Brown int ret = 0;
114dd31b310SMark Brown
115dd31b310SMark Brown if (deemph > 1)
116dd31b310SMark Brown return -EINVAL;
117dd31b310SMark Brown
118a51ff30fSLars-Peter Clausen mutex_lock(&wm8731->lock);
119dd31b310SMark Brown if (wm8731->deemph != deemph) {
120dd31b310SMark Brown wm8731->deemph = deemph;
121dd31b310SMark Brown
122cde596c7SKuninori Morimoto wm8731_set_deemph(component);
123dd31b310SMark Brown
124dd31b310SMark Brown ret = 1;
125dd31b310SMark Brown }
126a51ff30fSLars-Peter Clausen mutex_unlock(&wm8731->lock);
127dd31b310SMark Brown
128dd31b310SMark Brown return ret;
129dd31b310SMark Brown }
13040e0aa64SRichard Purdie
131d00efa64SMark Brown static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0);
132d00efa64SMark Brown static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -1500, 300, 0);
133d00efa64SMark Brown static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
134d921184eSMark Brown static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 2000, 0);
135d00efa64SMark Brown
13640e0aa64SRichard Purdie static const struct snd_kcontrol_new wm8731_snd_controls[] = {
13740e0aa64SRichard Purdie
138d00efa64SMark Brown SOC_DOUBLE_R_TLV("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V,
139d00efa64SMark Brown 0, 127, 0, out_tlv),
140bd903b6eSLiam Girdwood SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V,
141bd903b6eSLiam Girdwood 7, 1, 0),
14240e0aa64SRichard Purdie
143d00efa64SMark Brown SOC_DOUBLE_R_TLV("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0,
144d00efa64SMark Brown in_tlv),
14540e0aa64SRichard Purdie SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1),
14640e0aa64SRichard Purdie
147d921184eSMark Brown SOC_SINGLE_TLV("Mic Boost Volume", WM8731_APANA, 0, 1, 0, mic_tlv),
148ef38ed88SMark Brown SOC_SINGLE("Mic Capture Switch", WM8731_APANA, 1, 1, 1),
14940e0aa64SRichard Purdie
150d00efa64SMark Brown SOC_SINGLE_TLV("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1,
151d00efa64SMark Brown sidetone_tlv),
15240e0aa64SRichard Purdie
15340e0aa64SRichard Purdie SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1),
15440e0aa64SRichard Purdie SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0),
15540e0aa64SRichard Purdie
156dd31b310SMark Brown SOC_SINGLE_BOOL_EXT("Playback Deemphasis Switch", 0,
157dd31b310SMark Brown wm8731_get_deemph, wm8731_put_deemph),
15840e0aa64SRichard Purdie };
15940e0aa64SRichard Purdie
16040e0aa64SRichard Purdie /* Output Mixer */
16140e0aa64SRichard Purdie static const struct snd_kcontrol_new wm8731_output_mixer_controls[] = {
16240e0aa64SRichard Purdie SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),
16340e0aa64SRichard Purdie SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0),
16440e0aa64SRichard Purdie SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
16540e0aa64SRichard Purdie };
16640e0aa64SRichard Purdie
16740e0aa64SRichard Purdie /* Input mux */
16840e0aa64SRichard Purdie static const struct snd_kcontrol_new wm8731_input_mux_controls =
16959f72970SMark Brown SOC_DAPM_ENUM("Input Select", wm8731_insel_enum);
17040e0aa64SRichard Purdie
17140e0aa64SRichard Purdie static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
1728a27bd9aSMark Brown SND_SOC_DAPM_SUPPLY("ACTIVE",WM8731_ACTIVE, 0, 0, NULL, 0),
1739745e824SMark Brown SND_SOC_DAPM_SUPPLY("OSC", WM8731_PWR, 5, 1, NULL, 0),
17440e0aa64SRichard Purdie SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1,
17540e0aa64SRichard Purdie &wm8731_output_mixer_controls[0],
17640e0aa64SRichard Purdie ARRAY_SIZE(wm8731_output_mixer_controls)),
17740e0aa64SRichard Purdie SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8731_PWR, 3, 1),
17840e0aa64SRichard Purdie SND_SOC_DAPM_OUTPUT("LOUT"),
17940e0aa64SRichard Purdie SND_SOC_DAPM_OUTPUT("LHPOUT"),
18040e0aa64SRichard Purdie SND_SOC_DAPM_OUTPUT("ROUT"),
18140e0aa64SRichard Purdie SND_SOC_DAPM_OUTPUT("RHPOUT"),
18240e0aa64SRichard Purdie SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8731_PWR, 2, 1),
18340e0aa64SRichard Purdie SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &wm8731_input_mux_controls),
18440e0aa64SRichard Purdie SND_SOC_DAPM_PGA("Line Input", WM8731_PWR, 0, 1, NULL, 0),
18540e0aa64SRichard Purdie SND_SOC_DAPM_MICBIAS("Mic Bias", WM8731_PWR, 1, 1),
18640e0aa64SRichard Purdie SND_SOC_DAPM_INPUT("MICIN"),
18740e0aa64SRichard Purdie SND_SOC_DAPM_INPUT("RLINEIN"),
18840e0aa64SRichard Purdie SND_SOC_DAPM_INPUT("LLINEIN"),
18940e0aa64SRichard Purdie };
19040e0aa64SRichard Purdie
wm8731_check_osc(struct snd_soc_dapm_widget * source,struct snd_soc_dapm_widget * sink)1919745e824SMark Brown static int wm8731_check_osc(struct snd_soc_dapm_widget *source,
1929745e824SMark Brown struct snd_soc_dapm_widget *sink)
1939745e824SMark Brown {
194cde596c7SKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
195cde596c7SKuninori Morimoto struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component);
1969745e824SMark Brown
1975a195b44SNicolas Ferre return wm8731->sysclk_type == WM8731_SYSCLK_XTAL;
1989745e824SMark Brown }
1999745e824SMark Brown
2005e251aecSMark Brown static const struct snd_soc_dapm_route wm8731_intercon[] = {
2019745e824SMark Brown {"DAC", NULL, "OSC", wm8731_check_osc},
2029745e824SMark Brown {"ADC", NULL, "OSC", wm8731_check_osc},
2038a27bd9aSMark Brown {"DAC", NULL, "ACTIVE"},
2048a27bd9aSMark Brown {"ADC", NULL, "ACTIVE"},
2059745e824SMark Brown
20640e0aa64SRichard Purdie /* output mixer */
20740e0aa64SRichard Purdie {"Output Mixer", "Line Bypass Switch", "Line Input"},
20840e0aa64SRichard Purdie {"Output Mixer", "HiFi Playback Switch", "DAC"},
20940e0aa64SRichard Purdie {"Output Mixer", "Mic Sidetone Switch", "Mic Bias"},
21040e0aa64SRichard Purdie
21140e0aa64SRichard Purdie /* outputs */
21240e0aa64SRichard Purdie {"RHPOUT", NULL, "Output Mixer"},
21340e0aa64SRichard Purdie {"ROUT", NULL, "Output Mixer"},
21440e0aa64SRichard Purdie {"LHPOUT", NULL, "Output Mixer"},
21540e0aa64SRichard Purdie {"LOUT", NULL, "Output Mixer"},
21640e0aa64SRichard Purdie
21740e0aa64SRichard Purdie /* input mux */
21840e0aa64SRichard Purdie {"Input Mux", "Line In", "Line Input"},
21940e0aa64SRichard Purdie {"Input Mux", "Mic", "Mic Bias"},
22040e0aa64SRichard Purdie {"ADC", NULL, "Input Mux"},
22140e0aa64SRichard Purdie
22240e0aa64SRichard Purdie /* inputs */
22340e0aa64SRichard Purdie {"Line Input", NULL, "LLINEIN"},
22440e0aa64SRichard Purdie {"Line Input", NULL, "RLINEIN"},
22540e0aa64SRichard Purdie {"Mic Bias", NULL, "MICIN"},
22640e0aa64SRichard Purdie };
22740e0aa64SRichard Purdie
22840e0aa64SRichard Purdie struct _coeff_div {
22940e0aa64SRichard Purdie u32 mclk;
23040e0aa64SRichard Purdie u32 rate;
23140e0aa64SRichard Purdie u16 fs;
23240e0aa64SRichard Purdie u8 sr:4;
23340e0aa64SRichard Purdie u8 bosr:1;
23440e0aa64SRichard Purdie u8 usb:1;
23540e0aa64SRichard Purdie };
23640e0aa64SRichard Purdie
23740e0aa64SRichard Purdie /* codec mclk clock divider coefficients */
23840e0aa64SRichard Purdie static const struct _coeff_div coeff_div[] = {
23940e0aa64SRichard Purdie /* 48k */
24040e0aa64SRichard Purdie {12288000, 48000, 256, 0x0, 0x0, 0x0},
24140e0aa64SRichard Purdie {18432000, 48000, 384, 0x0, 0x1, 0x0},
24240e0aa64SRichard Purdie {12000000, 48000, 250, 0x0, 0x0, 0x1},
24340e0aa64SRichard Purdie
24440e0aa64SRichard Purdie /* 32k */
24540e0aa64SRichard Purdie {12288000, 32000, 384, 0x6, 0x0, 0x0},
24640e0aa64SRichard Purdie {18432000, 32000, 576, 0x6, 0x1, 0x0},
247298a2c75SFrank Mandarino {12000000, 32000, 375, 0x6, 0x0, 0x1},
24840e0aa64SRichard Purdie
24940e0aa64SRichard Purdie /* 8k */
25040e0aa64SRichard Purdie {12288000, 8000, 1536, 0x3, 0x0, 0x0},
25140e0aa64SRichard Purdie {18432000, 8000, 2304, 0x3, 0x1, 0x0},
25240e0aa64SRichard Purdie {11289600, 8000, 1408, 0xb, 0x0, 0x0},
25340e0aa64SRichard Purdie {16934400, 8000, 2112, 0xb, 0x1, 0x0},
25440e0aa64SRichard Purdie {12000000, 8000, 1500, 0x3, 0x0, 0x1},
25540e0aa64SRichard Purdie
25640e0aa64SRichard Purdie /* 96k */
25740e0aa64SRichard Purdie {12288000, 96000, 128, 0x7, 0x0, 0x0},
25840e0aa64SRichard Purdie {18432000, 96000, 192, 0x7, 0x1, 0x0},
25940e0aa64SRichard Purdie {12000000, 96000, 125, 0x7, 0x0, 0x1},
26040e0aa64SRichard Purdie
26140e0aa64SRichard Purdie /* 44.1k */
26240e0aa64SRichard Purdie {11289600, 44100, 256, 0x8, 0x0, 0x0},
26340e0aa64SRichard Purdie {16934400, 44100, 384, 0x8, 0x1, 0x0},
26440e0aa64SRichard Purdie {12000000, 44100, 272, 0x8, 0x1, 0x1},
26540e0aa64SRichard Purdie
26640e0aa64SRichard Purdie /* 88.2k */
26740e0aa64SRichard Purdie {11289600, 88200, 128, 0xf, 0x0, 0x0},
26840e0aa64SRichard Purdie {16934400, 88200, 192, 0xf, 0x1, 0x0},
26940e0aa64SRichard Purdie {12000000, 88200, 136, 0xf, 0x1, 0x1},
27040e0aa64SRichard Purdie };
27140e0aa64SRichard Purdie
2720890c2b7SRichard Genoud /* rates constraints */
2730890c2b7SRichard Genoud static const unsigned int wm8731_rates_12000000[] = {
2740890c2b7SRichard Genoud 8000, 32000, 44100, 48000, 96000, 88200,
2750890c2b7SRichard Genoud };
2760890c2b7SRichard Genoud
2770890c2b7SRichard Genoud static const unsigned int wm8731_rates_12288000_18432000[] = {
2780890c2b7SRichard Genoud 8000, 32000, 48000, 96000,
2790890c2b7SRichard Genoud };
2800890c2b7SRichard Genoud
2810890c2b7SRichard Genoud static const unsigned int wm8731_rates_11289600_16934400[] = {
2820890c2b7SRichard Genoud 8000, 44100, 88200,
2830890c2b7SRichard Genoud };
2840890c2b7SRichard Genoud
2850890c2b7SRichard Genoud static const struct snd_pcm_hw_constraint_list wm8731_constraints_12000000 = {
2860890c2b7SRichard Genoud .list = wm8731_rates_12000000,
2870890c2b7SRichard Genoud .count = ARRAY_SIZE(wm8731_rates_12000000),
2880890c2b7SRichard Genoud };
2890890c2b7SRichard Genoud
2900890c2b7SRichard Genoud static const
2910890c2b7SRichard Genoud struct snd_pcm_hw_constraint_list wm8731_constraints_12288000_18432000 = {
2920890c2b7SRichard Genoud .list = wm8731_rates_12288000_18432000,
2930890c2b7SRichard Genoud .count = ARRAY_SIZE(wm8731_rates_12288000_18432000),
2940890c2b7SRichard Genoud };
2950890c2b7SRichard Genoud
2960890c2b7SRichard Genoud static const
2970890c2b7SRichard Genoud struct snd_pcm_hw_constraint_list wm8731_constraints_11289600_16934400 = {
2980890c2b7SRichard Genoud .list = wm8731_rates_11289600_16934400,
2990890c2b7SRichard Genoud .count = ARRAY_SIZE(wm8731_rates_11289600_16934400),
3000890c2b7SRichard Genoud };
3010890c2b7SRichard Genoud
get_coeff(int mclk,int rate)30240e0aa64SRichard Purdie static inline int get_coeff(int mclk, int rate)
30340e0aa64SRichard Purdie {
30440e0aa64SRichard Purdie int i;
30540e0aa64SRichard Purdie
30640e0aa64SRichard Purdie for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
30740e0aa64SRichard Purdie if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
30840e0aa64SRichard Purdie return i;
30940e0aa64SRichard Purdie }
31040e0aa64SRichard Purdie return 0;
31140e0aa64SRichard Purdie }
31240e0aa64SRichard Purdie
wm8731_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)313b36d61d4SFrank Mandarino static int wm8731_hw_params(struct snd_pcm_substream *substream,
314dee89c4dSMark Brown struct snd_pcm_hw_params *params,
315dee89c4dSMark Brown struct snd_soc_dai *dai)
31640e0aa64SRichard Purdie {
317cde596c7SKuninori Morimoto struct snd_soc_component *component = dai->component;
318cde596c7SKuninori Morimoto struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component);
3196d75dfc3SKuninori Morimoto u16 iface = snd_soc_component_read(component, WM8731_IFACE) & 0xfff3;
320b36d61d4SFrank Mandarino int i = get_coeff(wm8731->sysclk, params_rate(params));
321b36d61d4SFrank Mandarino u16 srate = (coeff_div[i].sr << 2) |
322b36d61d4SFrank Mandarino (coeff_div[i].bosr << 1) | coeff_div[i].usb;
32340e0aa64SRichard Purdie
324dd31b310SMark Brown wm8731->playback_fs = params_rate(params);
325dd31b310SMark Brown
326cde596c7SKuninori Morimoto snd_soc_component_write(component, WM8731_SRATE, srate);
32740e0aa64SRichard Purdie
328b36d61d4SFrank Mandarino /* bit size */
329dfb6778eSMark Brown switch (params_width(params)) {
330dfb6778eSMark Brown case 16:
331b36d61d4SFrank Mandarino break;
332dfb6778eSMark Brown case 20:
333b36d61d4SFrank Mandarino iface |= 0x0004;
334b36d61d4SFrank Mandarino break;
335dfb6778eSMark Brown case 24:
336b36d61d4SFrank Mandarino iface |= 0x0008;
337b36d61d4SFrank Mandarino break;
338cf5ef3a2SMatt Flax case 32:
339cf5ef3a2SMatt Flax iface |= 0x000c;
340cf5ef3a2SMatt Flax break;
341b36d61d4SFrank Mandarino }
342b36d61d4SFrank Mandarino
343cde596c7SKuninori Morimoto wm8731_set_deemph(component);
344dd31b310SMark Brown
345cde596c7SKuninori Morimoto snd_soc_component_write(component, WM8731_IFACE, iface);
346b36d61d4SFrank Mandarino return 0;
34740e0aa64SRichard Purdie }
34840e0aa64SRichard Purdie
wm8731_mute(struct snd_soc_dai * dai,int mute,int direction)34926d3c16eSKuninori Morimoto static int wm8731_mute(struct snd_soc_dai *dai, int mute, int direction)
35040e0aa64SRichard Purdie {
351cde596c7SKuninori Morimoto struct snd_soc_component *component = dai->component;
3526d75dfc3SKuninori Morimoto u16 mute_reg = snd_soc_component_read(component, WM8731_APDIGI) & 0xfff7;
353b36d61d4SFrank Mandarino
35440e0aa64SRichard Purdie if (mute)
355cde596c7SKuninori Morimoto snd_soc_component_write(component, WM8731_APDIGI, mute_reg | 0x8);
35640e0aa64SRichard Purdie else
357cde596c7SKuninori Morimoto snd_soc_component_write(component, WM8731_APDIGI, mute_reg);
35840e0aa64SRichard Purdie return 0;
35940e0aa64SRichard Purdie }
36040e0aa64SRichard Purdie
wm8731_set_dai_sysclk(struct snd_soc_dai * codec_dai,int clk_id,unsigned int freq,int dir)361e550e17fSLiam Girdwood static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai,
362b36d61d4SFrank Mandarino int clk_id, unsigned int freq, int dir)
363b36d61d4SFrank Mandarino {
364cde596c7SKuninori Morimoto struct snd_soc_component *component = codec_dai->component;
365cde596c7SKuninori Morimoto struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
366cde596c7SKuninori Morimoto struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component);
367b36d61d4SFrank Mandarino
3689745e824SMark Brown switch (clk_id) {
3699745e824SMark Brown case WM8731_SYSCLK_XTAL:
3709745e824SMark Brown case WM8731_SYSCLK_MCLK:
37199d42234SSongjun Wu if (wm8731->mclk && clk_set_rate(wm8731->mclk, freq))
37299d42234SSongjun Wu return -EINVAL;
3739745e824SMark Brown wm8731->sysclk_type = clk_id;
3749745e824SMark Brown break;
3759745e824SMark Brown default:
3769745e824SMark Brown return -EINVAL;
3779745e824SMark Brown }
3789745e824SMark Brown
379b36d61d4SFrank Mandarino switch (freq) {
3800890c2b7SRichard Genoud case 0:
3810890c2b7SRichard Genoud wm8731->constraints = NULL;
3820890c2b7SRichard Genoud break;
383b36d61d4SFrank Mandarino case 12000000:
3840890c2b7SRichard Genoud wm8731->constraints = &wm8731_constraints_12000000;
3850890c2b7SRichard Genoud break;
386b36d61d4SFrank Mandarino case 12288000:
387b36d61d4SFrank Mandarino case 18432000:
3880890c2b7SRichard Genoud wm8731->constraints = &wm8731_constraints_12288000_18432000;
3890890c2b7SRichard Genoud break;
3900890c2b7SRichard Genoud case 16934400:
3910890c2b7SRichard Genoud case 11289600:
3920890c2b7SRichard Genoud wm8731->constraints = &wm8731_constraints_11289600_16934400;
3939745e824SMark Brown break;
3949745e824SMark Brown default:
395b36d61d4SFrank Mandarino return -EINVAL;
396b36d61d4SFrank Mandarino }
397b36d61d4SFrank Mandarino
3980890c2b7SRichard Genoud wm8731->sysclk = freq;
3990890c2b7SRichard Genoud
400fc31fda6SLars-Peter Clausen snd_soc_dapm_sync(dapm);
4019745e824SMark Brown
4029745e824SMark Brown return 0;
4039745e824SMark Brown }
4049745e824SMark Brown
405b36d61d4SFrank Mandarino
wm8731_set_dai_fmt(struct snd_soc_dai * codec_dai,unsigned int fmt)406e550e17fSLiam Girdwood static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai,
407b36d61d4SFrank Mandarino unsigned int fmt)
408b36d61d4SFrank Mandarino {
409cde596c7SKuninori Morimoto struct snd_soc_component *component = codec_dai->component;
410b36d61d4SFrank Mandarino u16 iface = 0;
411b36d61d4SFrank Mandarino
41200b87e18SMark Brown switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
41300b87e18SMark Brown case SND_SOC_DAIFMT_CBP_CFP:
414b36d61d4SFrank Mandarino iface |= 0x0040;
415b36d61d4SFrank Mandarino break;
41600b87e18SMark Brown case SND_SOC_DAIFMT_CBC_CFC:
417b36d61d4SFrank Mandarino break;
418b36d61d4SFrank Mandarino default:
419b36d61d4SFrank Mandarino return -EINVAL;
420b36d61d4SFrank Mandarino }
421b36d61d4SFrank Mandarino
422b36d61d4SFrank Mandarino /* interface format */
423b36d61d4SFrank Mandarino switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
424b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_I2S:
425b36d61d4SFrank Mandarino iface |= 0x0002;
426b36d61d4SFrank Mandarino break;
427b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_RIGHT_J:
428b36d61d4SFrank Mandarino break;
429b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_LEFT_J:
430b36d61d4SFrank Mandarino iface |= 0x0001;
431b36d61d4SFrank Mandarino break;
432b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_DSP_A:
433b4af6ef9SBo Shen iface |= 0x0013;
434b36d61d4SFrank Mandarino break;
435b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_DSP_B:
436b4af6ef9SBo Shen iface |= 0x0003;
437b36d61d4SFrank Mandarino break;
438b36d61d4SFrank Mandarino default:
439b36d61d4SFrank Mandarino return -EINVAL;
440b36d61d4SFrank Mandarino }
441b36d61d4SFrank Mandarino
442b36d61d4SFrank Mandarino /* clock inversion */
443b36d61d4SFrank Mandarino switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
444b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_NB_NF:
445b36d61d4SFrank Mandarino break;
446b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_IB_IF:
447b36d61d4SFrank Mandarino iface |= 0x0090;
448b36d61d4SFrank Mandarino break;
449b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_IB_NF:
450b36d61d4SFrank Mandarino iface |= 0x0080;
451b36d61d4SFrank Mandarino break;
452b36d61d4SFrank Mandarino case SND_SOC_DAIFMT_NB_IF:
453b36d61d4SFrank Mandarino iface |= 0x0010;
454b36d61d4SFrank Mandarino break;
455b36d61d4SFrank Mandarino default:
456b36d61d4SFrank Mandarino return -EINVAL;
457b36d61d4SFrank Mandarino }
458b36d61d4SFrank Mandarino
459b36d61d4SFrank Mandarino /* set iface */
460cde596c7SKuninori Morimoto snd_soc_component_write(component, WM8731_IFACE, iface);
461b36d61d4SFrank Mandarino return 0;
462b36d61d4SFrank Mandarino }
463b36d61d4SFrank Mandarino
wm8731_set_bias_level(struct snd_soc_component * component,enum snd_soc_bias_level level)464cde596c7SKuninori Morimoto static int wm8731_set_bias_level(struct snd_soc_component *component,
4650be9898aSMark Brown enum snd_soc_bias_level level)
46640e0aa64SRichard Purdie {
467cde596c7SKuninori Morimoto struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(component);
4689bf311feSAxel Lin int ret;
46922d22ee5SMark Brown u16 reg;
47040e0aa64SRichard Purdie
4710be9898aSMark Brown switch (level) {
4720be9898aSMark Brown case SND_SOC_BIAS_ON:
473cef6daa9SFabio Estevam if (wm8731->mclk) {
474cef6daa9SFabio Estevam ret = clk_prepare_enable(wm8731->mclk);
475cef6daa9SFabio Estevam if (ret)
476cef6daa9SFabio Estevam return ret;
477cef6daa9SFabio Estevam }
47840e0aa64SRichard Purdie break;
4790be9898aSMark Brown case SND_SOC_BIAS_PREPARE:
48040e0aa64SRichard Purdie break;
4810be9898aSMark Brown case SND_SOC_BIAS_STANDBY:
482cde596c7SKuninori Morimoto if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
48306ae9988SMark Brown ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
48406ae9988SMark Brown wm8731->supplies);
48506ae9988SMark Brown if (ret != 0)
48606ae9988SMark Brown return ret;
48706ae9988SMark Brown
48805d448e2SMark Brown regcache_sync(wm8731->regmap);
48906ae9988SMark Brown }
49006ae9988SMark Brown
49122d22ee5SMark Brown /* Clear PWROFF, gate CLKOUT, everything else as-is */
4926d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8731_PWR) & 0xff7f;
493cde596c7SKuninori Morimoto snd_soc_component_write(component, WM8731_PWR, reg | 0x0040);
49440e0aa64SRichard Purdie break;
4950be9898aSMark Brown case SND_SOC_BIAS_OFF:
49699d42234SSongjun Wu if (wm8731->mclk)
49799d42234SSongjun Wu clk_disable_unprepare(wm8731->mclk);
498cde596c7SKuninori Morimoto snd_soc_component_write(component, WM8731_PWR, 0xffff);
49906ae9988SMark Brown regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies),
50006ae9988SMark Brown wm8731->supplies);
50105d448e2SMark Brown regcache_mark_dirty(wm8731->regmap);
50240e0aa64SRichard Purdie break;
50340e0aa64SRichard Purdie }
50440e0aa64SRichard Purdie return 0;
50540e0aa64SRichard Purdie }
50640e0aa64SRichard Purdie
wm8731_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)5070890c2b7SRichard Genoud static int wm8731_startup(struct snd_pcm_substream *substream,
5080890c2b7SRichard Genoud struct snd_soc_dai *dai)
5090890c2b7SRichard Genoud {
510cde596c7SKuninori Morimoto struct wm8731_priv *wm8731 = snd_soc_component_get_drvdata(dai->component);
5110890c2b7SRichard Genoud
5120890c2b7SRichard Genoud if (wm8731->constraints)
5130890c2b7SRichard Genoud snd_pcm_hw_constraint_list(substream->runtime, 0,
5140890c2b7SRichard Genoud SNDRV_PCM_HW_PARAM_RATE,
5150890c2b7SRichard Genoud wm8731->constraints);
5160890c2b7SRichard Genoud
5170890c2b7SRichard Genoud return 0;
5180890c2b7SRichard Genoud }
5190890c2b7SRichard Genoud
520e135443eSBill Gatliff #define WM8731_RATES SNDRV_PCM_RATE_8000_96000
521b36d61d4SFrank Mandarino
522b36d61d4SFrank Mandarino #define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
523cf5ef3a2SMatt Flax SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
524b36d61d4SFrank Mandarino
52585e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops wm8731_dai_ops = {
5260890c2b7SRichard Genoud .startup = wm8731_startup,
5276335d055SEric Miao .hw_params = wm8731_hw_params,
52826d3c16eSKuninori Morimoto .mute_stream = wm8731_mute,
5296335d055SEric Miao .set_sysclk = wm8731_set_dai_sysclk,
5306335d055SEric Miao .set_fmt = wm8731_set_dai_fmt,
53126d3c16eSKuninori Morimoto .no_capture_mute = 1,
5326335d055SEric Miao };
5336335d055SEric Miao
534f0fba2adSLiam Girdwood static struct snd_soc_dai_driver wm8731_dai = {
535f0fba2adSLiam Girdwood .name = "wm8731-hifi",
53640e0aa64SRichard Purdie .playback = {
53740e0aa64SRichard Purdie .stream_name = "Playback",
53840e0aa64SRichard Purdie .channels_min = 1,
53940e0aa64SRichard Purdie .channels_max = 2,
540b36d61d4SFrank Mandarino .rates = WM8731_RATES,
541b36d61d4SFrank Mandarino .formats = WM8731_FORMATS,},
54240e0aa64SRichard Purdie .capture = {
54340e0aa64SRichard Purdie .stream_name = "Capture",
54440e0aa64SRichard Purdie .channels_min = 1,
54540e0aa64SRichard Purdie .channels_max = 2,
546b36d61d4SFrank Mandarino .rates = WM8731_RATES,
547b36d61d4SFrank Mandarino .formats = WM8731_FORMATS,},
5486335d055SEric Miao .ops = &wm8731_dai_ops,
54907695752SKuninori Morimoto .symmetric_rate = 1,
55040e0aa64SRichard Purdie };
55140e0aa64SRichard Purdie
5525f1b9d1eSMark Brown static const struct snd_soc_component_driver soc_component_dev_wm8731 = {
5535f1b9d1eSMark Brown .set_bias_level = wm8731_set_bias_level,
5545f1b9d1eSMark Brown .controls = wm8731_snd_controls,
5555f1b9d1eSMark Brown .num_controls = ARRAY_SIZE(wm8731_snd_controls),
5565f1b9d1eSMark Brown .dapm_widgets = wm8731_dapm_widgets,
5575f1b9d1eSMark Brown .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
5585f1b9d1eSMark Brown .dapm_routes = wm8731_intercon,
5595f1b9d1eSMark Brown .num_dapm_routes = ARRAY_SIZE(wm8731_intercon),
5605f1b9d1eSMark Brown .suspend_bias_off = 1,
5615f1b9d1eSMark Brown .idle_bias_on = 1,
5625f1b9d1eSMark Brown .use_pmdown_time = 1,
5635f1b9d1eSMark Brown .endianness = 1,
5645f1b9d1eSMark Brown };
5655f1b9d1eSMark Brown
wm8731_init(struct device * dev,struct wm8731_priv * wm8731)5669dc15f81SMark Brown int wm8731_init(struct device *dev, struct wm8731_priv *wm8731)
56740e0aa64SRichard Purdie {
568f0fba2adSLiam Girdwood int ret = 0, i;
56940e0aa64SRichard Purdie
5708875d104SMark Brown wm8731->mclk = devm_clk_get(dev, "mclk");
5718875d104SMark Brown if (IS_ERR(wm8731->mclk)) {
5728875d104SMark Brown ret = PTR_ERR(wm8731->mclk);
5738875d104SMark Brown if (ret == -ENOENT) {
5748875d104SMark Brown wm8731->mclk = NULL;
5758875d104SMark Brown dev_warn(dev, "Assuming static MCLK\n");
5768875d104SMark Brown } else {
5778875d104SMark Brown dev_err(dev, "Failed to get MCLK: %d\n", ret);
5788875d104SMark Brown return ret;
5798875d104SMark Brown }
5808875d104SMark Brown }
5818875d104SMark Brown
5828875d104SMark Brown mutex_init(&wm8731->lock);
5838875d104SMark Brown
5847dea7c01SMark Brown for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++)
5857dea7c01SMark Brown wm8731->supplies[i].supply = wm8731_supply_names[i];
5867dea7c01SMark Brown
5876702dfccSSergey Kiselev ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8731->supplies),
5887dea7c01SMark Brown wm8731->supplies);
5897dea7c01SMark Brown if (ret != 0) {
5906702dfccSSergey Kiselev dev_err(dev, "Failed to request supplies: %d\n", ret);
591f0fba2adSLiam Girdwood return ret;
5927dea7c01SMark Brown }
5937dea7c01SMark Brown
5947dea7c01SMark Brown ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
5957dea7c01SMark Brown wm8731->supplies);
5967dea7c01SMark Brown if (ret != 0) {
5976702dfccSSergey Kiselev dev_err(dev, "Failed to enable supplies: %d\n", ret);
5983598aad5SFabio Estevam return ret;
5997dea7c01SMark Brown }
6007dea7c01SMark Brown
6016702dfccSSergey Kiselev ret = wm8731_reset(wm8731->regmap);
602519cf2dfSMark Brown if (ret < 0) {
6036702dfccSSergey Kiselev dev_err(dev, "Failed to issue reset: %d\n", ret);
6047dea7c01SMark Brown goto err_regulator_enable;
605519cf2dfSMark Brown }
606519cf2dfSMark Brown
6076702dfccSSergey Kiselev /* Clear POWEROFF, keep everything else disabled */
6086702dfccSSergey Kiselev regmap_write(wm8731->regmap, WM8731_PWR, 0x7f);
6095998102bSMark Brown
6105998102bSMark Brown /* Latch the update bits */
6116702dfccSSergey Kiselev regmap_update_bits(wm8731->regmap, WM8731_LOUT1V, 0x100, 0);
6126702dfccSSergey Kiselev regmap_update_bits(wm8731->regmap, WM8731_ROUT1V, 0x100, 0);
6136702dfccSSergey Kiselev regmap_update_bits(wm8731->regmap, WM8731_LINVOL, 0x100, 0);
6146702dfccSSergey Kiselev regmap_update_bits(wm8731->regmap, WM8731_RINVOL, 0x100, 0);
6155998102bSMark Brown
616ce3bdaa8SMark Brown /* Disable bypass path by default */
6176702dfccSSergey Kiselev regmap_update_bits(wm8731->regmap, WM8731_APANA, 0x8, 0);
618ce3bdaa8SMark Brown
6196702dfccSSergey Kiselev regcache_mark_dirty(wm8731->regmap);
620fe5422fcSMark Brown
6213f4fb905SMark Brown ret = devm_snd_soc_register_component(dev,
6223f4fb905SMark Brown &soc_component_dev_wm8731, &wm8731_dai, 1);
6233f4fb905SMark Brown if (ret != 0) {
6243f4fb905SMark Brown dev_err(dev, "Failed to register CODEC: %d\n", ret);
6253f4fb905SMark Brown goto err_regulator_enable;
6263f4fb905SMark Brown }
6273f4fb905SMark Brown
6283f4fb905SMark Brown return 0;
6293f4fb905SMark Brown
6307dea7c01SMark Brown err_regulator_enable:
6316702dfccSSergey Kiselev /* Regulators will be enabled by bias management */
6327dea7c01SMark Brown regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
633f0fba2adSLiam Girdwood
634fe5422fcSMark Brown return ret;
635a8035c8fSMark Brown }
6369dc15f81SMark Brown EXPORT_SYMBOL_GPL(wm8731_init);
637a8035c8fSMark Brown
6389dc15f81SMark Brown const struct regmap_config wm8731_regmap = {
63905d448e2SMark Brown .reg_bits = 7,
64005d448e2SMark Brown .val_bits = 9,
64105d448e2SMark Brown
64205d448e2SMark Brown .max_register = WM8731_RESET,
64305d448e2SMark Brown .volatile_reg = wm8731_volatile,
64405d448e2SMark Brown
645*59bd5113SMark Brown .cache_type = REGCACHE_MAPLE,
64605d448e2SMark Brown .reg_defaults = wm8731_reg_defaults,
64705d448e2SMark Brown .num_reg_defaults = ARRAY_SIZE(wm8731_reg_defaults),
64805d448e2SMark Brown };
6499dc15f81SMark Brown EXPORT_SYMBOL_GPL(wm8731_regmap);
65064089b84SMark Brown
65140e0aa64SRichard Purdie MODULE_DESCRIPTION("ASoC WM8731 driver");
65240e0aa64SRichard Purdie MODULE_AUTHOR("Richard Purdie");
65340e0aa64SRichard Purdie MODULE_LICENSE("GPL");
654