xref: /linux/sound/soc/codecs/wm8731.c (revision 59bd5113d8ca0765e8f12307a4b1ac0e3daa91f4)
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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