1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2992bee40SIan Lartey /*
3992bee40SIan Lartey * wm8741.c -- WM8741 ALSA SoC Audio driver
4992bee40SIan Lartey *
5656baaebSMark Brown * Copyright 2010-1 Wolfson Microelectronics plc
6992bee40SIan Lartey *
7992bee40SIan Lartey * Author: Ian Lartey <ian@opensource.wolfsonmicro.com>
8992bee40SIan Lartey */
9992bee40SIan Lartey
10992bee40SIan Lartey #include <linux/module.h>
11992bee40SIan Lartey #include <linux/moduleparam.h>
12992bee40SIan Lartey #include <linux/init.h>
13992bee40SIan Lartey #include <linux/delay.h>
14992bee40SIan Lartey #include <linux/pm.h>
15992bee40SIan Lartey #include <linux/i2c.h>
1639e9b8d2SMark Brown #include <linux/spi/spi.h>
17fe98c0cfSMark Brown #include <linux/regmap.h>
18992bee40SIan Lartey #include <linux/regulator/consumer.h>
19992bee40SIan Lartey #include <linux/slab.h>
2080080ec5SMark Brown #include <linux/of_device.h>
21992bee40SIan Lartey #include <sound/core.h>
22992bee40SIan Lartey #include <sound/pcm.h>
23992bee40SIan Lartey #include <sound/pcm_params.h>
24992bee40SIan Lartey #include <sound/soc.h>
25992bee40SIan Lartey #include <sound/initval.h>
26992bee40SIan Lartey #include <sound/tlv.h>
27992bee40SIan Lartey
28992bee40SIan Lartey #include "wm8741.h"
29992bee40SIan Lartey
30992bee40SIan Lartey #define WM8741_NUM_SUPPLIES 2
31992bee40SIan Lartey static const char *wm8741_supply_names[WM8741_NUM_SUPPLIES] = {
32992bee40SIan Lartey "AVDD",
33992bee40SIan Lartey "DVDD",
34992bee40SIan Lartey };
35992bee40SIan Lartey
36992bee40SIan Lartey /* codec private data */
37992bee40SIan Lartey struct wm8741_priv {
38c354b54cSSergej Sawazki struct wm8741_platform_data pdata;
39fe98c0cfSMark Brown struct regmap *regmap;
40992bee40SIan Lartey struct regulator_bulk_data supplies[WM8741_NUM_SUPPLIES];
41992bee40SIan Lartey unsigned int sysclk;
4270bad2c7SLars-Peter Clausen const struct snd_pcm_hw_constraint_list *sysclk_constraints;
43992bee40SIan Lartey };
44992bee40SIan Lartey
45fe98c0cfSMark Brown static const struct reg_default wm8741_reg_defaults[] = {
46fe98c0cfSMark Brown { 0, 0x0000 }, /* R0 - DACLLSB Attenuation */
47fe98c0cfSMark Brown { 1, 0x0000 }, /* R1 - DACLMSB Attenuation */
48fe98c0cfSMark Brown { 2, 0x0000 }, /* R2 - DACRLSB Attenuation */
49fe98c0cfSMark Brown { 3, 0x0000 }, /* R3 - DACRMSB Attenuation */
50fe98c0cfSMark Brown { 4, 0x0000 }, /* R4 - Volume Control */
51fe98c0cfSMark Brown { 5, 0x000A }, /* R5 - Format Control */
52fe98c0cfSMark Brown { 6, 0x0000 }, /* R6 - Filter Control */
53fe98c0cfSMark Brown { 7, 0x0000 }, /* R7 - Mode Control 1 */
54fe98c0cfSMark Brown { 8, 0x0002 }, /* R8 - Mode Control 2 */
55fe98c0cfSMark Brown { 32, 0x0002 }, /* R32 - ADDITONAL_CONTROL_1 */
56992bee40SIan Lartey };
57992bee40SIan Lartey
wm8741_reset(struct snd_soc_component * component)589b6a00f7SKuninori Morimoto static int wm8741_reset(struct snd_soc_component *component)
59992bee40SIan Lartey {
609b6a00f7SKuninori Morimoto return snd_soc_component_write(component, WM8741_RESET, 0);
61992bee40SIan Lartey }
62992bee40SIan Lartey
63992bee40SIan Lartey static const DECLARE_TLV_DB_SCALE(dac_tlv_fine, -12700, 13, 0);
64992bee40SIan Lartey static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 400, 0);
65992bee40SIan Lartey
66c354b54cSSergej Sawazki static const struct snd_kcontrol_new wm8741_snd_controls_stereo[] = {
67992bee40SIan Lartey SOC_DOUBLE_R_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION,
68992bee40SIan Lartey WM8741_DACRLSB_ATTENUATION, 1, 255, 1, dac_tlv_fine),
69992bee40SIan Lartey SOC_DOUBLE_R_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION,
70992bee40SIan Lartey WM8741_DACRMSB_ATTENUATION, 0, 511, 1, dac_tlv),
71992bee40SIan Lartey };
72992bee40SIan Lartey
73c354b54cSSergej Sawazki static const struct snd_kcontrol_new wm8741_snd_controls_mono_left[] = {
74c354b54cSSergej Sawazki SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACLLSB_ATTENUATION,
75c354b54cSSergej Sawazki 1, 255, 1, dac_tlv_fine),
76c354b54cSSergej Sawazki SOC_SINGLE_TLV("Playback Volume", WM8741_DACLMSB_ATTENUATION,
77c354b54cSSergej Sawazki 0, 511, 1, dac_tlv),
78c354b54cSSergej Sawazki };
79c354b54cSSergej Sawazki
80c354b54cSSergej Sawazki static const struct snd_kcontrol_new wm8741_snd_controls_mono_right[] = {
81c354b54cSSergej Sawazki SOC_SINGLE_TLV("Fine Playback Volume", WM8741_DACRLSB_ATTENUATION,
82c354b54cSSergej Sawazki 1, 255, 1, dac_tlv_fine),
83c354b54cSSergej Sawazki SOC_SINGLE_TLV("Playback Volume", WM8741_DACRMSB_ATTENUATION,
84c354b54cSSergej Sawazki 0, 511, 1, dac_tlv),
85c354b54cSSergej Sawazki };
86c354b54cSSergej Sawazki
87992bee40SIan Lartey static const struct snd_soc_dapm_widget wm8741_dapm_widgets[] = {
88992bee40SIan Lartey SND_SOC_DAPM_DAC("DACL", "Playback", SND_SOC_NOPM, 0, 0),
89992bee40SIan Lartey SND_SOC_DAPM_DAC("DACR", "Playback", SND_SOC_NOPM, 0, 0),
90992bee40SIan Lartey SND_SOC_DAPM_OUTPUT("VOUTLP"),
91992bee40SIan Lartey SND_SOC_DAPM_OUTPUT("VOUTLN"),
92992bee40SIan Lartey SND_SOC_DAPM_OUTPUT("VOUTRP"),
93992bee40SIan Lartey SND_SOC_DAPM_OUTPUT("VOUTRN"),
94992bee40SIan Lartey };
95992bee40SIan Lartey
960e62780fSMark Brown static const struct snd_soc_dapm_route wm8741_dapm_routes[] = {
97992bee40SIan Lartey { "VOUTLP", NULL, "DACL" },
98992bee40SIan Lartey { "VOUTLN", NULL, "DACL" },
99992bee40SIan Lartey { "VOUTRP", NULL, "DACR" },
100992bee40SIan Lartey { "VOUTRN", NULL, "DACR" },
101992bee40SIan Lartey };
102992bee40SIan Lartey
10370bad2c7SLars-Peter Clausen static const unsigned int rates_11289[] = {
1048787041dSSergej Sawazki 44100, 88200,
1053fe4a5eeSIan Lartey };
1063fe4a5eeSIan Lartey
10770bad2c7SLars-Peter Clausen static const struct snd_pcm_hw_constraint_list constraints_11289 = {
1083fe4a5eeSIan Lartey .count = ARRAY_SIZE(rates_11289),
1093fe4a5eeSIan Lartey .list = rates_11289,
1103fe4a5eeSIan Lartey };
1113fe4a5eeSIan Lartey
11270bad2c7SLars-Peter Clausen static const unsigned int rates_12288[] = {
1133fe4a5eeSIan Lartey 32000, 48000, 96000,
1143fe4a5eeSIan Lartey };
1153fe4a5eeSIan Lartey
11670bad2c7SLars-Peter Clausen static const struct snd_pcm_hw_constraint_list constraints_12288 = {
1173fe4a5eeSIan Lartey .count = ARRAY_SIZE(rates_12288),
1183fe4a5eeSIan Lartey .list = rates_12288,
1193fe4a5eeSIan Lartey };
1203fe4a5eeSIan Lartey
12170bad2c7SLars-Peter Clausen static const unsigned int rates_16384[] = {
1223fe4a5eeSIan Lartey 32000,
1233fe4a5eeSIan Lartey };
1243fe4a5eeSIan Lartey
12570bad2c7SLars-Peter Clausen static const struct snd_pcm_hw_constraint_list constraints_16384 = {
1263fe4a5eeSIan Lartey .count = ARRAY_SIZE(rates_16384),
1273fe4a5eeSIan Lartey .list = rates_16384,
1283fe4a5eeSIan Lartey };
1293fe4a5eeSIan Lartey
13070bad2c7SLars-Peter Clausen static const unsigned int rates_16934[] = {
1318787041dSSergej Sawazki 44100, 88200,
1323fe4a5eeSIan Lartey };
1333fe4a5eeSIan Lartey
13470bad2c7SLars-Peter Clausen static const struct snd_pcm_hw_constraint_list constraints_16934 = {
1353fe4a5eeSIan Lartey .count = ARRAY_SIZE(rates_16934),
1363fe4a5eeSIan Lartey .list = rates_16934,
1373fe4a5eeSIan Lartey };
1383fe4a5eeSIan Lartey
13970bad2c7SLars-Peter Clausen static const unsigned int rates_18432[] = {
1403fe4a5eeSIan Lartey 48000, 96000,
1413fe4a5eeSIan Lartey };
1423fe4a5eeSIan Lartey
14370bad2c7SLars-Peter Clausen static const struct snd_pcm_hw_constraint_list constraints_18432 = {
1443fe4a5eeSIan Lartey .count = ARRAY_SIZE(rates_18432),
1453fe4a5eeSIan Lartey .list = rates_18432,
1463fe4a5eeSIan Lartey };
1473fe4a5eeSIan Lartey
14870bad2c7SLars-Peter Clausen static const unsigned int rates_22579[] = {
1498787041dSSergej Sawazki 44100, 88200, 176400
1503fe4a5eeSIan Lartey };
1513fe4a5eeSIan Lartey
15270bad2c7SLars-Peter Clausen static const struct snd_pcm_hw_constraint_list constraints_22579 = {
1533fe4a5eeSIan Lartey .count = ARRAY_SIZE(rates_22579),
1543fe4a5eeSIan Lartey .list = rates_22579,
1553fe4a5eeSIan Lartey };
1563fe4a5eeSIan Lartey
15770bad2c7SLars-Peter Clausen static const unsigned int rates_24576[] = {
1583fe4a5eeSIan Lartey 32000, 48000, 96000, 192000
1593fe4a5eeSIan Lartey };
1603fe4a5eeSIan Lartey
16170bad2c7SLars-Peter Clausen static const struct snd_pcm_hw_constraint_list constraints_24576 = {
1623fe4a5eeSIan Lartey .count = ARRAY_SIZE(rates_24576),
1633fe4a5eeSIan Lartey .list = rates_24576,
1643fe4a5eeSIan Lartey };
1653fe4a5eeSIan Lartey
16670bad2c7SLars-Peter Clausen static const unsigned int rates_36864[] = {
1678787041dSSergej Sawazki 48000, 96000, 192000
1683fe4a5eeSIan Lartey };
1693fe4a5eeSIan Lartey
17070bad2c7SLars-Peter Clausen static const struct snd_pcm_hw_constraint_list constraints_36864 = {
1713fe4a5eeSIan Lartey .count = ARRAY_SIZE(rates_36864),
1723fe4a5eeSIan Lartey .list = rates_36864,
173992bee40SIan Lartey };
174992bee40SIan Lartey
wm8741_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)175992bee40SIan Lartey static int wm8741_startup(struct snd_pcm_substream *substream,
176992bee40SIan Lartey struct snd_soc_dai *dai)
177992bee40SIan Lartey {
1789b6a00f7SKuninori Morimoto struct snd_soc_component *component = dai->component;
1799b6a00f7SKuninori Morimoto struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component);
180992bee40SIan Lartey
181e369bd00SSergej Sawazki if (wm8741->sysclk)
182992bee40SIan Lartey snd_pcm_hw_constraint_list(substream->runtime, 0,
183992bee40SIan Lartey SNDRV_PCM_HW_PARAM_RATE,
1843fe4a5eeSIan Lartey wm8741->sysclk_constraints);
185992bee40SIan Lartey
186992bee40SIan Lartey return 0;
187992bee40SIan Lartey }
188992bee40SIan Lartey
wm8741_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)189992bee40SIan Lartey static int wm8741_hw_params(struct snd_pcm_substream *substream,
190992bee40SIan Lartey struct snd_pcm_hw_params *params,
191992bee40SIan Lartey struct snd_soc_dai *dai)
192992bee40SIan Lartey {
1939b6a00f7SKuninori Morimoto struct snd_soc_component *component = dai->component;
1949b6a00f7SKuninori Morimoto struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component);
195e9418629SSergej Sawazki unsigned int iface, mode;
196992bee40SIan Lartey int i;
197992bee40SIan Lartey
198e369bd00SSergej Sawazki /* The set of sample rates that can be supported depends on the
199e369bd00SSergej Sawazki * MCLK supplied to the CODEC - enforce this.
200e369bd00SSergej Sawazki */
201e369bd00SSergej Sawazki if (!wm8741->sysclk) {
2029b6a00f7SKuninori Morimoto dev_err(component->dev,
203e369bd00SSergej Sawazki "No MCLK configured, call set_sysclk() on init or in hw_params\n");
204e369bd00SSergej Sawazki return -EINVAL;
205e369bd00SSergej Sawazki }
206e369bd00SSergej Sawazki
207e369bd00SSergej Sawazki /* Find a supported LRCLK rate */
208e369bd00SSergej Sawazki for (i = 0; i < wm8741->sysclk_constraints->count; i++) {
209e369bd00SSergej Sawazki if (wm8741->sysclk_constraints->list[i] == params_rate(params))
210992bee40SIan Lartey break;
211992bee40SIan Lartey }
212992bee40SIan Lartey
213e369bd00SSergej Sawazki if (i == wm8741->sysclk_constraints->count) {
2149b6a00f7SKuninori Morimoto dev_err(component->dev, "LRCLK %d unsupported with MCLK %d\n",
215e369bd00SSergej Sawazki params_rate(params), wm8741->sysclk);
216992bee40SIan Lartey return -EINVAL;
217992bee40SIan Lartey }
218992bee40SIan Lartey
219992bee40SIan Lartey /* bit size */
22034967ad2SMark Brown switch (params_width(params)) {
22134967ad2SMark Brown case 16:
222eaf8abcfSCharles Keepax iface = 0x0;
223992bee40SIan Lartey break;
22434967ad2SMark Brown case 20:
225eaf8abcfSCharles Keepax iface = 0x1;
226992bee40SIan Lartey break;
22734967ad2SMark Brown case 24:
228eaf8abcfSCharles Keepax iface = 0x2;
229992bee40SIan Lartey break;
23034967ad2SMark Brown case 32:
231eaf8abcfSCharles Keepax iface = 0x3;
232992bee40SIan Lartey break;
233992bee40SIan Lartey default:
2349b6a00f7SKuninori Morimoto dev_dbg(component->dev, "wm8741_hw_params: Unsupported bit size param = %d",
23534967ad2SMark Brown params_width(params));
236992bee40SIan Lartey return -EINVAL;
237992bee40SIan Lartey }
238992bee40SIan Lartey
239e9418629SSergej Sawazki /* oversampling rate */
240e9418629SSergej Sawazki if (params_rate(params) > 96000)
241e9418629SSergej Sawazki mode = 0x40;
242e9418629SSergej Sawazki else if (params_rate(params) > 48000)
243e9418629SSergej Sawazki mode = 0x20;
244e9418629SSergej Sawazki else
245e9418629SSergej Sawazki mode = 0x00;
246e9418629SSergej Sawazki
2479b6a00f7SKuninori Morimoto dev_dbg(component->dev, "wm8741_hw_params: bit size param = %d, rate param = %d",
248e369bd00SSergej Sawazki params_width(params), params_rate(params));
249992bee40SIan Lartey
2509b6a00f7SKuninori Morimoto snd_soc_component_update_bits(component, WM8741_FORMAT_CONTROL, WM8741_IWL_MASK,
251eaf8abcfSCharles Keepax iface);
252e9418629SSergej Sawazki snd_soc_component_update_bits(component, WM8741_MODE_CONTROL_1, WM8741_OSR_MASK,
253e9418629SSergej Sawazki mode);
254eaf8abcfSCharles Keepax
255992bee40SIan Lartey return 0;
256992bee40SIan Lartey }
257992bee40SIan Lartey
wm8741_set_dai_sysclk(struct snd_soc_dai * codec_dai,int clk_id,unsigned int freq,int dir)258992bee40SIan Lartey static int wm8741_set_dai_sysclk(struct snd_soc_dai *codec_dai,
259992bee40SIan Lartey int clk_id, unsigned int freq, int dir)
260992bee40SIan Lartey {
2619b6a00f7SKuninori Morimoto struct snd_soc_component *component = codec_dai->component;
2629b6a00f7SKuninori Morimoto struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component);
263992bee40SIan Lartey
2649b6a00f7SKuninori Morimoto dev_dbg(component->dev, "wm8741_set_dai_sysclk info: freq=%dHz\n", freq);
265992bee40SIan Lartey
2663fe4a5eeSIan Lartey switch (freq) {
267e369bd00SSergej Sawazki case 0:
268e369bd00SSergej Sawazki wm8741->sysclk_constraints = NULL;
2696f55a041SAxel Lin break;
2703fe4a5eeSIan Lartey case 11289600:
2713fe4a5eeSIan Lartey wm8741->sysclk_constraints = &constraints_11289;
2726f55a041SAxel Lin break;
2733fe4a5eeSIan Lartey case 12288000:
2743fe4a5eeSIan Lartey wm8741->sysclk_constraints = &constraints_12288;
2756f55a041SAxel Lin break;
2763fe4a5eeSIan Lartey case 16384000:
2773fe4a5eeSIan Lartey wm8741->sysclk_constraints = &constraints_16384;
2786f55a041SAxel Lin break;
2793fe4a5eeSIan Lartey case 16934400:
2803fe4a5eeSIan Lartey wm8741->sysclk_constraints = &constraints_16934;
2816f55a041SAxel Lin break;
2823fe4a5eeSIan Lartey case 18432000:
2833fe4a5eeSIan Lartey wm8741->sysclk_constraints = &constraints_18432;
2846f55a041SAxel Lin break;
2853fe4a5eeSIan Lartey case 22579200:
2863fe4a5eeSIan Lartey case 33868800:
2873fe4a5eeSIan Lartey wm8741->sysclk_constraints = &constraints_22579;
2886f55a041SAxel Lin break;
2893fe4a5eeSIan Lartey case 24576000:
2903fe4a5eeSIan Lartey wm8741->sysclk_constraints = &constraints_24576;
2916f55a041SAxel Lin break;
2923fe4a5eeSIan Lartey case 36864000:
2933fe4a5eeSIan Lartey wm8741->sysclk_constraints = &constraints_36864;
2946f55a041SAxel Lin break;
2956f55a041SAxel Lin default:
2966f55a041SAxel Lin return -EINVAL;
2976f55a041SAxel Lin }
2986f55a041SAxel Lin
2993fe4a5eeSIan Lartey wm8741->sysclk = freq;
3003fe4a5eeSIan Lartey return 0;
3013fe4a5eeSIan Lartey }
302992bee40SIan Lartey
wm8741_set_dai_fmt(struct snd_soc_dai * codec_dai,unsigned int fmt)303992bee40SIan Lartey static int wm8741_set_dai_fmt(struct snd_soc_dai *codec_dai,
304992bee40SIan Lartey unsigned int fmt)
305992bee40SIan Lartey {
3069b6a00f7SKuninori Morimoto struct snd_soc_component *component = codec_dai->component;
307eaf8abcfSCharles Keepax unsigned int iface;
308992bee40SIan Lartey
309992bee40SIan Lartey /* check master/slave audio interface */
310992bee40SIan Lartey switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
311992bee40SIan Lartey case SND_SOC_DAIFMT_CBS_CFS:
312992bee40SIan Lartey break;
313992bee40SIan Lartey default:
314992bee40SIan Lartey return -EINVAL;
315992bee40SIan Lartey }
316992bee40SIan Lartey
317992bee40SIan Lartey /* interface format */
318992bee40SIan Lartey switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
319992bee40SIan Lartey case SND_SOC_DAIFMT_I2S:
320eaf8abcfSCharles Keepax iface = 0x08;
321992bee40SIan Lartey break;
322992bee40SIan Lartey case SND_SOC_DAIFMT_RIGHT_J:
323eaf8abcfSCharles Keepax iface = 0x00;
324992bee40SIan Lartey break;
325992bee40SIan Lartey case SND_SOC_DAIFMT_LEFT_J:
326eaf8abcfSCharles Keepax iface = 0x04;
327992bee40SIan Lartey break;
328992bee40SIan Lartey case SND_SOC_DAIFMT_DSP_A:
329eaf8abcfSCharles Keepax iface = 0x0C;
330992bee40SIan Lartey break;
331992bee40SIan Lartey case SND_SOC_DAIFMT_DSP_B:
332eaf8abcfSCharles Keepax iface = 0x1C;
333992bee40SIan Lartey break;
334992bee40SIan Lartey default:
335992bee40SIan Lartey return -EINVAL;
336992bee40SIan Lartey }
337992bee40SIan Lartey
338992bee40SIan Lartey /* clock inversion */
339992bee40SIan Lartey switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
340992bee40SIan Lartey case SND_SOC_DAIFMT_NB_NF:
341992bee40SIan Lartey break;
34281b3cc55SSergej Sawazki case SND_SOC_DAIFMT_NB_IF:
343eaf8abcfSCharles Keepax iface |= 0x10;
344992bee40SIan Lartey break;
345992bee40SIan Lartey case SND_SOC_DAIFMT_IB_NF:
346eaf8abcfSCharles Keepax iface |= 0x20;
347992bee40SIan Lartey break;
34881b3cc55SSergej Sawazki case SND_SOC_DAIFMT_IB_IF:
349eaf8abcfSCharles Keepax iface |= 0x30;
350992bee40SIan Lartey break;
351992bee40SIan Lartey default:
352992bee40SIan Lartey return -EINVAL;
353992bee40SIan Lartey }
354992bee40SIan Lartey
355992bee40SIan Lartey
3569b6a00f7SKuninori Morimoto dev_dbg(component->dev, "wm8741_set_dai_fmt: Format=%x, Clock Inv=%x\n",
357992bee40SIan Lartey fmt & SND_SOC_DAIFMT_FORMAT_MASK,
358992bee40SIan Lartey ((fmt & SND_SOC_DAIFMT_INV_MASK)));
359992bee40SIan Lartey
3609b6a00f7SKuninori Morimoto snd_soc_component_update_bits(component, WM8741_FORMAT_CONTROL,
361eaf8abcfSCharles Keepax WM8741_BCP_MASK | WM8741_LRP_MASK | WM8741_FMT_MASK,
362eaf8abcfSCharles Keepax iface);
363eaf8abcfSCharles Keepax
364992bee40SIan Lartey return 0;
365992bee40SIan Lartey }
366992bee40SIan Lartey
wm8741_mute(struct snd_soc_dai * codec_dai,int mute,int direction)36726d3c16eSKuninori Morimoto static int wm8741_mute(struct snd_soc_dai *codec_dai, int mute, int direction)
36836b15993SSergej Sawazki {
36936b15993SSergej Sawazki struct snd_soc_component *component = codec_dai->component;
37036b15993SSergej Sawazki
37136b15993SSergej Sawazki snd_soc_component_update_bits(component, WM8741_VOLUME_CONTROL,
37236b15993SSergej Sawazki WM8741_SOFT_MASK, !!mute << WM8741_SOFT_SHIFT);
37336b15993SSergej Sawazki return 0;
37436b15993SSergej Sawazki }
37536b15993SSergej Sawazki
376992bee40SIan Lartey #define WM8741_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
377992bee40SIan Lartey SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
378992bee40SIan Lartey SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | \
379992bee40SIan Lartey SNDRV_PCM_RATE_192000)
380992bee40SIan Lartey
381992bee40SIan Lartey #define WM8741_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
382992bee40SIan Lartey SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
383992bee40SIan Lartey
38485e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops wm8741_dai_ops = {
385992bee40SIan Lartey .startup = wm8741_startup,
386992bee40SIan Lartey .hw_params = wm8741_hw_params,
387992bee40SIan Lartey .set_sysclk = wm8741_set_dai_sysclk,
388992bee40SIan Lartey .set_fmt = wm8741_set_dai_fmt,
38926d3c16eSKuninori Morimoto .mute_stream = wm8741_mute,
39026d3c16eSKuninori Morimoto .no_capture_mute = 1,
391992bee40SIan Lartey };
392992bee40SIan Lartey
393f0fba2adSLiam Girdwood static struct snd_soc_dai_driver wm8741_dai = {
39430e2d368SIan Lartey .name = "wm8741",
395992bee40SIan Lartey .playback = {
396992bee40SIan Lartey .stream_name = "Playback",
397c354b54cSSergej Sawazki .channels_min = 2,
398992bee40SIan Lartey .channels_max = 2,
399992bee40SIan Lartey .rates = WM8741_RATES,
400992bee40SIan Lartey .formats = WM8741_FORMATS,
401992bee40SIan Lartey },
402992bee40SIan Lartey .ops = &wm8741_dai_ops,
403992bee40SIan Lartey };
404992bee40SIan Lartey
405992bee40SIan Lartey #ifdef CONFIG_PM
wm8741_resume(struct snd_soc_component * component)4069b6a00f7SKuninori Morimoto static int wm8741_resume(struct snd_soc_component *component)
407992bee40SIan Lartey {
4089b6a00f7SKuninori Morimoto snd_soc_component_cache_sync(component);
409992bee40SIan Lartey return 0;
410992bee40SIan Lartey }
411992bee40SIan Lartey #else
412992bee40SIan Lartey #define wm8741_resume NULL
413992bee40SIan Lartey #endif
414992bee40SIan Lartey
wm8741_configure(struct snd_soc_component * component)4159b6a00f7SKuninori Morimoto static int wm8741_configure(struct snd_soc_component *component)
416c354b54cSSergej Sawazki {
4179b6a00f7SKuninori Morimoto struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component);
418c354b54cSSergej Sawazki
419c354b54cSSergej Sawazki /* Configure differential mode */
420c354b54cSSergej Sawazki switch (wm8741->pdata.diff_mode) {
421c354b54cSSergej Sawazki case WM8741_DIFF_MODE_STEREO:
422c354b54cSSergej Sawazki case WM8741_DIFF_MODE_STEREO_REVERSED:
423c354b54cSSergej Sawazki case WM8741_DIFF_MODE_MONO_LEFT:
424c354b54cSSergej Sawazki case WM8741_DIFF_MODE_MONO_RIGHT:
4259b6a00f7SKuninori Morimoto snd_soc_component_update_bits(component, WM8741_MODE_CONTROL_2,
426c354b54cSSergej Sawazki WM8741_DIFF_MASK,
427c354b54cSSergej Sawazki wm8741->pdata.diff_mode << WM8741_DIFF_SHIFT);
428c354b54cSSergej Sawazki break;
429c354b54cSSergej Sawazki default:
430c354b54cSSergej Sawazki return -EINVAL;
431c354b54cSSergej Sawazki }
432c354b54cSSergej Sawazki
433c354b54cSSergej Sawazki /* Change some default settings - latch VU */
4349b6a00f7SKuninori Morimoto snd_soc_component_update_bits(component, WM8741_DACLLSB_ATTENUATION,
435c354b54cSSergej Sawazki WM8741_UPDATELL, WM8741_UPDATELL);
4369b6a00f7SKuninori Morimoto snd_soc_component_update_bits(component, WM8741_DACLMSB_ATTENUATION,
437c354b54cSSergej Sawazki WM8741_UPDATELM, WM8741_UPDATELM);
4389b6a00f7SKuninori Morimoto snd_soc_component_update_bits(component, WM8741_DACRLSB_ATTENUATION,
439c354b54cSSergej Sawazki WM8741_UPDATERL, WM8741_UPDATERL);
4409b6a00f7SKuninori Morimoto snd_soc_component_update_bits(component, WM8741_DACRMSB_ATTENUATION,
441c354b54cSSergej Sawazki WM8741_UPDATERM, WM8741_UPDATERM);
442c354b54cSSergej Sawazki
443c354b54cSSergej Sawazki return 0;
444c354b54cSSergej Sawazki }
445c354b54cSSergej Sawazki
wm8741_add_controls(struct snd_soc_component * component)4469b6a00f7SKuninori Morimoto static int wm8741_add_controls(struct snd_soc_component *component)
447c354b54cSSergej Sawazki {
4489b6a00f7SKuninori Morimoto struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component);
449c354b54cSSergej Sawazki
450c354b54cSSergej Sawazki switch (wm8741->pdata.diff_mode) {
451c354b54cSSergej Sawazki case WM8741_DIFF_MODE_STEREO:
452c354b54cSSergej Sawazki case WM8741_DIFF_MODE_STEREO_REVERSED:
4539b6a00f7SKuninori Morimoto snd_soc_add_component_controls(component,
454c354b54cSSergej Sawazki wm8741_snd_controls_stereo,
455c354b54cSSergej Sawazki ARRAY_SIZE(wm8741_snd_controls_stereo));
456c354b54cSSergej Sawazki break;
457c354b54cSSergej Sawazki case WM8741_DIFF_MODE_MONO_LEFT:
4589b6a00f7SKuninori Morimoto snd_soc_add_component_controls(component,
459c354b54cSSergej Sawazki wm8741_snd_controls_mono_left,
460c354b54cSSergej Sawazki ARRAY_SIZE(wm8741_snd_controls_mono_left));
461c354b54cSSergej Sawazki break;
462c354b54cSSergej Sawazki case WM8741_DIFF_MODE_MONO_RIGHT:
4639b6a00f7SKuninori Morimoto snd_soc_add_component_controls(component,
464c354b54cSSergej Sawazki wm8741_snd_controls_mono_right,
465c354b54cSSergej Sawazki ARRAY_SIZE(wm8741_snd_controls_mono_right));
466c354b54cSSergej Sawazki break;
467c354b54cSSergej Sawazki default:
468c354b54cSSergej Sawazki return -EINVAL;
469c354b54cSSergej Sawazki }
470c354b54cSSergej Sawazki
471c354b54cSSergej Sawazki return 0;
472c354b54cSSergej Sawazki }
473c354b54cSSergej Sawazki
wm8741_probe(struct snd_soc_component * component)4749b6a00f7SKuninori Morimoto static int wm8741_probe(struct snd_soc_component *component)
475992bee40SIan Lartey {
4769b6a00f7SKuninori Morimoto struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component);
477992bee40SIan Lartey int ret = 0;
478398575dbSMark Brown
479398575dbSMark Brown ret = regulator_bulk_enable(ARRAY_SIZE(wm8741->supplies),
480398575dbSMark Brown wm8741->supplies);
481398575dbSMark Brown if (ret != 0) {
4829b6a00f7SKuninori Morimoto dev_err(component->dev, "Failed to enable supplies: %d\n", ret);
483398575dbSMark Brown goto err_get;
484398575dbSMark Brown }
485992bee40SIan Lartey
4869b6a00f7SKuninori Morimoto ret = wm8741_reset(component);
487992bee40SIan Lartey if (ret < 0) {
4889b6a00f7SKuninori Morimoto dev_err(component->dev, "Failed to issue reset\n");
489398575dbSMark Brown goto err_enable;
490992bee40SIan Lartey }
491992bee40SIan Lartey
4929b6a00f7SKuninori Morimoto ret = wm8741_configure(component);
493c354b54cSSergej Sawazki if (ret < 0) {
4949b6a00f7SKuninori Morimoto dev_err(component->dev, "Failed to change default settings\n");
495c354b54cSSergej Sawazki goto err_enable;
496c354b54cSSergej Sawazki }
497c354b54cSSergej Sawazki
4989b6a00f7SKuninori Morimoto ret = wm8741_add_controls(component);
499c354b54cSSergej Sawazki if (ret < 0) {
5009b6a00f7SKuninori Morimoto dev_err(component->dev, "Failed to add controls\n");
501c354b54cSSergej Sawazki goto err_enable;
502c354b54cSSergej Sawazki }
503992bee40SIan Lartey
5049b6a00f7SKuninori Morimoto dev_dbg(component->dev, "Successful registration\n");
505f0fba2adSLiam Girdwood return ret;
506398575dbSMark Brown
507398575dbSMark Brown err_enable:
508398575dbSMark Brown regulator_bulk_disable(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
509398575dbSMark Brown err_get:
510398575dbSMark Brown return ret;
511398575dbSMark Brown }
512398575dbSMark Brown
wm8741_remove(struct snd_soc_component * component)5139b6a00f7SKuninori Morimoto static void wm8741_remove(struct snd_soc_component *component)
514398575dbSMark Brown {
5159b6a00f7SKuninori Morimoto struct wm8741_priv *wm8741 = snd_soc_component_get_drvdata(component);
516398575dbSMark Brown
517398575dbSMark Brown regulator_bulk_disable(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
518f0fba2adSLiam Girdwood }
519f0fba2adSLiam Girdwood
5209b6a00f7SKuninori Morimoto static const struct snd_soc_component_driver soc_component_dev_wm8741 = {
521f0fba2adSLiam Girdwood .probe = wm8741_probe,
522398575dbSMark Brown .remove = wm8741_remove,
523f0fba2adSLiam Girdwood .resume = wm8741_resume,
5240e62780fSMark Brown .dapm_widgets = wm8741_dapm_widgets,
5250e62780fSMark Brown .num_dapm_widgets = ARRAY_SIZE(wm8741_dapm_widgets),
5260e62780fSMark Brown .dapm_routes = wm8741_dapm_routes,
5270e62780fSMark Brown .num_dapm_routes = ARRAY_SIZE(wm8741_dapm_routes),
5289b6a00f7SKuninori Morimoto .idle_bias_on = 1,
5299b6a00f7SKuninori Morimoto .use_pmdown_time = 1,
5309b6a00f7SKuninori Morimoto .endianness = 1,
531f0fba2adSLiam Girdwood };
532f0fba2adSLiam Girdwood
53380080ec5SMark Brown static const struct of_device_id wm8741_of_match[] = {
53480080ec5SMark Brown { .compatible = "wlf,wm8741", },
53580080ec5SMark Brown { }
53680080ec5SMark Brown };
53780080ec5SMark Brown MODULE_DEVICE_TABLE(of, wm8741_of_match);
53880080ec5SMark Brown
539fe98c0cfSMark Brown static const struct regmap_config wm8741_regmap = {
540fe98c0cfSMark Brown .reg_bits = 7,
541fe98c0cfSMark Brown .val_bits = 9,
542fe98c0cfSMark Brown .max_register = WM8741_MAX_REGISTER,
543fe98c0cfSMark Brown
544fe98c0cfSMark Brown .reg_defaults = wm8741_reg_defaults,
545fe98c0cfSMark Brown .num_reg_defaults = ARRAY_SIZE(wm8741_reg_defaults),
546*5dd4dddeSMark Brown .cache_type = REGCACHE_MAPLE,
547fe98c0cfSMark Brown };
548fe98c0cfSMark Brown
wm8741_set_pdata(struct device * dev,struct wm8741_priv * wm8741)549c354b54cSSergej Sawazki static int wm8741_set_pdata(struct device *dev, struct wm8741_priv *wm8741)
550c354b54cSSergej Sawazki {
551c354b54cSSergej Sawazki const struct wm8741_platform_data *pdata = dev_get_platdata(dev);
552c354b54cSSergej Sawazki u32 diff_mode;
553c354b54cSSergej Sawazki
554c354b54cSSergej Sawazki if (dev->of_node) {
555c354b54cSSergej Sawazki if (of_property_read_u32(dev->of_node, "diff-mode", &diff_mode)
556c354b54cSSergej Sawazki >= 0)
557c354b54cSSergej Sawazki wm8741->pdata.diff_mode = diff_mode;
558c354b54cSSergej Sawazki } else {
559c354b54cSSergej Sawazki if (pdata != NULL)
560c354b54cSSergej Sawazki memcpy(&wm8741->pdata, pdata, sizeof(wm8741->pdata));
561c354b54cSSergej Sawazki }
562c354b54cSSergej Sawazki
563c354b54cSSergej Sawazki return 0;
564c354b54cSSergej Sawazki }
565c354b54cSSergej Sawazki
56626090a83SFabio Estevam #if IS_ENABLED(CONFIG_I2C)
wm8741_i2c_probe(struct i2c_client * i2c)56797b0b6e3SStephen Kitt static int wm8741_i2c_probe(struct i2c_client *i2c)
568f0fba2adSLiam Girdwood {
569f0fba2adSLiam Girdwood struct wm8741_priv *wm8741;
570d9780550SMark Brown int ret, i;
571f0fba2adSLiam Girdwood
5725aefb306SMark Brown wm8741 = devm_kzalloc(&i2c->dev, sizeof(struct wm8741_priv),
5735aefb306SMark Brown GFP_KERNEL);
574f0fba2adSLiam Girdwood if (wm8741 == NULL)
575f0fba2adSLiam Girdwood return -ENOMEM;
576f0fba2adSLiam Girdwood
577d9780550SMark Brown for (i = 0; i < ARRAY_SIZE(wm8741->supplies); i++)
578d9780550SMark Brown wm8741->supplies[i].supply = wm8741_supply_names[i];
579d9780550SMark Brown
580d9780550SMark Brown ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8741->supplies),
581d9780550SMark Brown wm8741->supplies);
582d9780550SMark Brown if (ret != 0) {
583fe98c0cfSMark Brown dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
584fe98c0cfSMark Brown return ret;
585fe98c0cfSMark Brown }
586fe98c0cfSMark Brown
587fd64c455STushar Behera wm8741->regmap = devm_regmap_init_i2c(i2c, &wm8741_regmap);
588fe98c0cfSMark Brown if (IS_ERR(wm8741->regmap)) {
589fe98c0cfSMark Brown ret = PTR_ERR(wm8741->regmap);
590fe98c0cfSMark Brown dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
591fe98c0cfSMark Brown return ret;
592d9780550SMark Brown }
593d9780550SMark Brown
5942d52d172SDan Carpenter ret = wm8741_set_pdata(&i2c->dev, wm8741);
595c354b54cSSergej Sawazki if (ret != 0) {
596c354b54cSSergej Sawazki dev_err(&i2c->dev, "Failed to set pdata: %d\n", ret);
597c354b54cSSergej Sawazki return ret;
598c354b54cSSergej Sawazki }
599c354b54cSSergej Sawazki
600f0fba2adSLiam Girdwood i2c_set_clientdata(i2c, wm8741);
601f0fba2adSLiam Girdwood
6029b6a00f7SKuninori Morimoto ret = devm_snd_soc_register_component(&i2c->dev,
6039b6a00f7SKuninori Morimoto &soc_component_dev_wm8741, &wm8741_dai, 1);
604398575dbSMark Brown
605f0fba2adSLiam Girdwood return ret;
606992bee40SIan Lartey }
607992bee40SIan Lartey
608992bee40SIan Lartey static const struct i2c_device_id wm8741_i2c_id[] = {
609992bee40SIan Lartey { "wm8741", 0 },
610992bee40SIan Lartey { }
611992bee40SIan Lartey };
612992bee40SIan Lartey MODULE_DEVICE_TABLE(i2c, wm8741_i2c_id);
613992bee40SIan Lartey
614992bee40SIan Lartey static struct i2c_driver wm8741_i2c_driver = {
615992bee40SIan Lartey .driver = {
6160473e61bSMark Brown .name = "wm8741",
61780080ec5SMark Brown .of_match_table = wm8741_of_match,
618992bee40SIan Lartey },
6199abcd240SUwe Kleine-König .probe = wm8741_i2c_probe,
620992bee40SIan Lartey .id_table = wm8741_i2c_id,
621992bee40SIan Lartey };
622992bee40SIan Lartey #endif
623992bee40SIan Lartey
62439e9b8d2SMark Brown #if defined(CONFIG_SPI_MASTER)
wm8741_spi_probe(struct spi_device * spi)6257a79e94eSBill Pemberton static int wm8741_spi_probe(struct spi_device *spi)
62639e9b8d2SMark Brown {
62739e9b8d2SMark Brown struct wm8741_priv *wm8741;
628d9780550SMark Brown int ret, i;
62939e9b8d2SMark Brown
6305aefb306SMark Brown wm8741 = devm_kzalloc(&spi->dev, sizeof(struct wm8741_priv),
6315aefb306SMark Brown GFP_KERNEL);
63239e9b8d2SMark Brown if (wm8741 == NULL)
63339e9b8d2SMark Brown return -ENOMEM;
63439e9b8d2SMark Brown
635d9780550SMark Brown for (i = 0; i < ARRAY_SIZE(wm8741->supplies); i++)
636d9780550SMark Brown wm8741->supplies[i].supply = wm8741_supply_names[i];
637d9780550SMark Brown
638fe98c0cfSMark Brown ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(wm8741->supplies),
639d9780550SMark Brown wm8741->supplies);
640d9780550SMark Brown if (ret != 0) {
641d9780550SMark Brown dev_err(&spi->dev, "Failed to request supplies: %d\n", ret);
642fe98c0cfSMark Brown return ret;
643d9780550SMark Brown }
644d9780550SMark Brown
645fd64c455STushar Behera wm8741->regmap = devm_regmap_init_spi(spi, &wm8741_regmap);
646fe98c0cfSMark Brown if (IS_ERR(wm8741->regmap)) {
647fe98c0cfSMark Brown ret = PTR_ERR(wm8741->regmap);
648fe98c0cfSMark Brown dev_err(&spi->dev, "Failed to init regmap: %d\n", ret);
649fe98c0cfSMark Brown return ret;
650fe98c0cfSMark Brown }
651fe98c0cfSMark Brown
6522d52d172SDan Carpenter ret = wm8741_set_pdata(&spi->dev, wm8741);
653c354b54cSSergej Sawazki if (ret != 0) {
654c354b54cSSergej Sawazki dev_err(&spi->dev, "Failed to set pdata: %d\n", ret);
655c354b54cSSergej Sawazki return ret;
656c354b54cSSergej Sawazki }
657c354b54cSSergej Sawazki
65839e9b8d2SMark Brown spi_set_drvdata(spi, wm8741);
65939e9b8d2SMark Brown
6609b6a00f7SKuninori Morimoto ret = devm_snd_soc_register_component(&spi->dev,
6619b6a00f7SKuninori Morimoto &soc_component_dev_wm8741, &wm8741_dai, 1);
66239e9b8d2SMark Brown return ret;
66339e9b8d2SMark Brown }
66439e9b8d2SMark Brown
66539e9b8d2SMark Brown static struct spi_driver wm8741_spi_driver = {
66639e9b8d2SMark Brown .driver = {
66739e9b8d2SMark Brown .name = "wm8741",
66880080ec5SMark Brown .of_match_table = wm8741_of_match,
66939e9b8d2SMark Brown },
67039e9b8d2SMark Brown .probe = wm8741_spi_probe,
67139e9b8d2SMark Brown };
67239e9b8d2SMark Brown #endif /* CONFIG_SPI_MASTER */
67339e9b8d2SMark Brown
wm8741_modinit(void)674992bee40SIan Lartey static int __init wm8741_modinit(void)
675992bee40SIan Lartey {
676f0fba2adSLiam Girdwood int ret = 0;
677f0fba2adSLiam Girdwood
67826090a83SFabio Estevam #if IS_ENABLED(CONFIG_I2C)
679992bee40SIan Lartey ret = i2c_add_driver(&wm8741_i2c_driver);
6803fe4a5eeSIan Lartey if (ret != 0)
681f0fba2adSLiam Girdwood pr_err("Failed to register WM8741 I2C driver: %d\n", ret);
682992bee40SIan Lartey #endif
68339e9b8d2SMark Brown #if defined(CONFIG_SPI_MASTER)
68439e9b8d2SMark Brown ret = spi_register_driver(&wm8741_spi_driver);
68539e9b8d2SMark Brown if (ret != 0) {
68639e9b8d2SMark Brown printk(KERN_ERR "Failed to register wm8741 SPI driver: %d\n",
68739e9b8d2SMark Brown ret);
68839e9b8d2SMark Brown }
68939e9b8d2SMark Brown #endif
690f0fba2adSLiam Girdwood
691f0fba2adSLiam Girdwood return ret;
692992bee40SIan Lartey }
693992bee40SIan Lartey module_init(wm8741_modinit);
694992bee40SIan Lartey
wm8741_exit(void)695992bee40SIan Lartey static void __exit wm8741_exit(void)
696992bee40SIan Lartey {
69739e9b8d2SMark Brown #if defined(CONFIG_SPI_MASTER)
69839e9b8d2SMark Brown spi_unregister_driver(&wm8741_spi_driver);
69939e9b8d2SMark Brown #endif
70026090a83SFabio Estevam #if IS_ENABLED(CONFIG_I2C)
701992bee40SIan Lartey i2c_del_driver(&wm8741_i2c_driver);
702992bee40SIan Lartey #endif
703992bee40SIan Lartey }
704992bee40SIan Lartey module_exit(wm8741_exit);
705992bee40SIan Lartey
706992bee40SIan Lartey MODULE_DESCRIPTION("ASoC WM8741 driver");
707992bee40SIan Lartey MODULE_AUTHOR("Ian Lartey <ian@opensource.wolfsonmicro.com>");
708992bee40SIan Lartey MODULE_LICENSE("GPL");
709