1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f701a2e5SMark Brown /*
3f701a2e5SMark Brown * wm8958-dsp2.c -- WM8958 DSP2 support
4f701a2e5SMark Brown *
5f701a2e5SMark Brown * Copyright 2011 Wolfson Microelectronics plc
6f701a2e5SMark Brown *
7f701a2e5SMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
8f701a2e5SMark Brown */
9f701a2e5SMark Brown
10f701a2e5SMark Brown #include <linux/module.h>
11f701a2e5SMark Brown #include <linux/moduleparam.h>
12f701a2e5SMark Brown #include <linux/init.h>
13f701a2e5SMark Brown #include <linux/delay.h>
14f701a2e5SMark Brown #include <linux/pm.h>
15f701a2e5SMark Brown #include <linux/i2c.h>
16f701a2e5SMark Brown #include <linux/platform_device.h>
17f701a2e5SMark Brown #include <linux/slab.h>
18f701a2e5SMark Brown #include <sound/soc.h>
19f701a2e5SMark Brown #include <sound/initval.h>
20f701a2e5SMark Brown #include <sound/tlv.h>
21f701a2e5SMark Brown #include <trace/events/asoc.h>
22f701a2e5SMark Brown
23f701a2e5SMark Brown #include <linux/mfd/wm8994/core.h>
24f701a2e5SMark Brown #include <linux/mfd/wm8994/registers.h>
25f701a2e5SMark Brown #include <linux/mfd/wm8994/pdata.h>
26f701a2e5SMark Brown #include <linux/mfd/wm8994/gpio.h>
27f701a2e5SMark Brown
283ad00f6aSBen Dooks (Codethink) #include <asm/unaligned.h>
293ad00f6aSBen Dooks (Codethink)
30f701a2e5SMark Brown #include "wm8994.h"
31f701a2e5SMark Brown
32fbbf5920SMark Brown #define WM_FW_BLOCK_INFO 0xff
33fbbf5920SMark Brown #define WM_FW_BLOCK_PM 0x00
34fbbf5920SMark Brown #define WM_FW_BLOCK_X 0x01
35fbbf5920SMark Brown #define WM_FW_BLOCK_Y 0x02
36fbbf5920SMark Brown #define WM_FW_BLOCK_Z 0x03
37fbbf5920SMark Brown #define WM_FW_BLOCK_I 0x06
38fbbf5920SMark Brown #define WM_FW_BLOCK_A 0x08
39fbbf5920SMark Brown #define WM_FW_BLOCK_C 0x0c
40fbbf5920SMark Brown
wm8958_dsp2_fw(struct snd_soc_component * component,const char * name,const struct firmware * fw,bool check)4100a6941cSKuninori Morimoto static int wm8958_dsp2_fw(struct snd_soc_component *component, const char *name,
42fbbf5920SMark Brown const struct firmware *fw, bool check)
43fbbf5920SMark Brown {
4400a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
45fbbf5920SMark Brown u64 data64;
46fbbf5920SMark Brown u32 data32;
47fbbf5920SMark Brown const u8 *data;
48fbbf5920SMark Brown char *str;
49fbbf5920SMark Brown size_t block_len, len;
50fbbf5920SMark Brown int ret = 0;
51fbbf5920SMark Brown
52fbbf5920SMark Brown /* Suppress unneeded downloads */
53fbbf5920SMark Brown if (wm8994->cur_fw == fw)
54fbbf5920SMark Brown return 0;
55fbbf5920SMark Brown
56fbbf5920SMark Brown if (fw->size < 32) {
5700a6941cSKuninori Morimoto dev_err(component->dev, "%s: firmware too short (%zd bytes)\n",
58086834e2SMark Brown name, fw->size);
59fbbf5920SMark Brown goto err;
60fbbf5920SMark Brown }
61fbbf5920SMark Brown
62fbbf5920SMark Brown if (memcmp(fw->data, "WMFW", 4) != 0) {
633ad00f6aSBen Dooks (Codethink) data32 = get_unaligned_be32(fw->data);
6400a6941cSKuninori Morimoto dev_err(component->dev, "%s: firmware has bad file magic %08x\n",
65fbbf5920SMark Brown name, data32);
66fbbf5920SMark Brown goto err;
67fbbf5920SMark Brown }
68fbbf5920SMark Brown
693ad00f6aSBen Dooks (Codethink) len = get_unaligned_be32(fw->data + 4);
703ad00f6aSBen Dooks (Codethink) data32 = get_unaligned_be32(fw->data + 8);
71fbbf5920SMark Brown
72fbbf5920SMark Brown if ((data32 >> 24) & 0xff) {
7300a6941cSKuninori Morimoto dev_err(component->dev, "%s: unsupported firmware version %d\n",
74fbbf5920SMark Brown name, (data32 >> 24) & 0xff);
75fbbf5920SMark Brown goto err;
76fbbf5920SMark Brown }
77fbbf5920SMark Brown if ((data32 & 0xffff) != 8958) {
7800a6941cSKuninori Morimoto dev_err(component->dev, "%s: unsupported target device %d\n",
79fbbf5920SMark Brown name, data32 & 0xffff);
80fbbf5920SMark Brown goto err;
81fbbf5920SMark Brown }
82fbbf5920SMark Brown if (((data32 >> 16) & 0xff) != 0xc) {
8300a6941cSKuninori Morimoto dev_err(component->dev, "%s: unsupported target core %d\n",
84fbbf5920SMark Brown name, (data32 >> 16) & 0xff);
85fbbf5920SMark Brown goto err;
86fbbf5920SMark Brown }
87fbbf5920SMark Brown
88fbbf5920SMark Brown if (check) {
893ad00f6aSBen Dooks (Codethink) data64 = get_unaligned_be64(fw->data + 24);
903ad00f6aSBen Dooks (Codethink) dev_info(component->dev, "%s timestamp %llx\n", name, data64);
91fbbf5920SMark Brown } else {
9200a6941cSKuninori Morimoto snd_soc_component_write(component, 0x102, 0x2);
9300a6941cSKuninori Morimoto snd_soc_component_write(component, 0x900, 0x2);
94fbbf5920SMark Brown }
95fbbf5920SMark Brown
96fbbf5920SMark Brown data = fw->data + len;
97fbbf5920SMark Brown len = fw->size - len;
98fbbf5920SMark Brown while (len) {
99fbbf5920SMark Brown if (len < 12) {
10000a6941cSKuninori Morimoto dev_err(component->dev, "%s short data block of %zd\n",
101fbbf5920SMark Brown name, len);
102fbbf5920SMark Brown goto err;
103fbbf5920SMark Brown }
104fbbf5920SMark Brown
1053ad00f6aSBen Dooks (Codethink) block_len = get_unaligned_be32(data + 4);
106fbbf5920SMark Brown if (block_len + 8 > len) {
10700a6941cSKuninori Morimoto dev_err(component->dev, "%zd byte block longer than file\n",
108fbbf5920SMark Brown block_len);
109fbbf5920SMark Brown goto err;
110fbbf5920SMark Brown }
111fbbf5920SMark Brown if (block_len == 0) {
11200a6941cSKuninori Morimoto dev_err(component->dev, "Zero length block\n");
113fbbf5920SMark Brown goto err;
114fbbf5920SMark Brown }
115fbbf5920SMark Brown
1163ad00f6aSBen Dooks (Codethink) data32 = get_unaligned_be32(data);
117fbbf5920SMark Brown
118fbbf5920SMark Brown switch ((data32 >> 24) & 0xff) {
119fbbf5920SMark Brown case WM_FW_BLOCK_INFO:
120fbbf5920SMark Brown /* Informational text */
121fbbf5920SMark Brown if (!check)
122fbbf5920SMark Brown break;
123fbbf5920SMark Brown
124fbbf5920SMark Brown str = kzalloc(block_len + 1, GFP_KERNEL);
125fbbf5920SMark Brown if (str) {
126fbbf5920SMark Brown memcpy(str, data + 8, block_len);
12700a6941cSKuninori Morimoto dev_info(component->dev, "%s: %s\n", name, str);
128fbbf5920SMark Brown kfree(str);
129fbbf5920SMark Brown } else {
13000a6941cSKuninori Morimoto dev_err(component->dev, "Out of memory\n");
131fbbf5920SMark Brown }
132fbbf5920SMark Brown break;
133fbbf5920SMark Brown case WM_FW_BLOCK_PM:
134fbbf5920SMark Brown case WM_FW_BLOCK_X:
135fbbf5920SMark Brown case WM_FW_BLOCK_Y:
136fbbf5920SMark Brown case WM_FW_BLOCK_Z:
137fbbf5920SMark Brown case WM_FW_BLOCK_I:
138fbbf5920SMark Brown case WM_FW_BLOCK_A:
139fbbf5920SMark Brown case WM_FW_BLOCK_C:
14000a6941cSKuninori Morimoto dev_dbg(component->dev, "%s: %zd bytes of %x@%x\n", name,
141fbbf5920SMark Brown block_len, (data32 >> 24) & 0xff,
142fbbf5920SMark Brown data32 & 0xffffff);
143fbbf5920SMark Brown
144fbbf5920SMark Brown if (check)
145fbbf5920SMark Brown break;
146fbbf5920SMark Brown
147fbbf5920SMark Brown data32 &= 0xffffff;
148fbbf5920SMark Brown
149548da08fSLars-Peter Clausen wm8994_bulk_write(wm8994->wm8994,
150fbbf5920SMark Brown data32 & 0xffffff,
151fbbf5920SMark Brown block_len / 2,
152fbbf5920SMark Brown (void *)(data + 8));
153fbbf5920SMark Brown
154fbbf5920SMark Brown break;
155fbbf5920SMark Brown default:
15600a6941cSKuninori Morimoto dev_warn(component->dev, "%s: unknown block type %d\n",
157fbbf5920SMark Brown name, (data32 >> 24) & 0xff);
158fbbf5920SMark Brown break;
159fbbf5920SMark Brown }
160fbbf5920SMark Brown
161fbbf5920SMark Brown /* Round up to the next 32 bit word */
162fbbf5920SMark Brown block_len += block_len % 4;
163fbbf5920SMark Brown
164fbbf5920SMark Brown data += block_len + 8;
165fbbf5920SMark Brown len -= block_len + 8;
166fbbf5920SMark Brown }
167fbbf5920SMark Brown
168fbbf5920SMark Brown if (!check) {
16900a6941cSKuninori Morimoto dev_dbg(component->dev, "%s: download done\n", name);
170fbbf5920SMark Brown wm8994->cur_fw = fw;
171fbbf5920SMark Brown } else {
17200a6941cSKuninori Morimoto dev_info(component->dev, "%s: got firmware\n", name);
173fbbf5920SMark Brown }
174fbbf5920SMark Brown
175fbbf5920SMark Brown goto ok;
176fbbf5920SMark Brown
177fbbf5920SMark Brown err:
178fbbf5920SMark Brown ret = -EINVAL;
179fbbf5920SMark Brown ok:
180fbbf5920SMark Brown if (!check) {
18100a6941cSKuninori Morimoto snd_soc_component_write(component, 0x900, 0x0);
18200a6941cSKuninori Morimoto snd_soc_component_write(component, 0x102, 0x0);
183fbbf5920SMark Brown }
184fbbf5920SMark Brown
185fbbf5920SMark Brown return ret;
186fbbf5920SMark Brown }
187fbbf5920SMark Brown
wm8958_dsp_start_mbc(struct snd_soc_component * component,int path)18800a6941cSKuninori Morimoto static void wm8958_dsp_start_mbc(struct snd_soc_component *component, int path)
189f701a2e5SMark Brown {
19000a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
191d9dd4adaSMark Brown struct wm8994 *control = wm8994->wm8994;
192fbbf5920SMark Brown int i;
193fbbf5920SMark Brown
194fbbf5920SMark Brown /* If the DSP is already running then noop */
1956d75dfc3SKuninori Morimoto if (snd_soc_component_read(component, WM8958_DSP2_PROGRAM) & WM8958_DSP2_ENA)
196fbbf5920SMark Brown return;
197fbbf5920SMark Brown
198fbbf5920SMark Brown /* If we have MBC firmware download it */
199fbbf5920SMark Brown if (wm8994->mbc)
20000a6941cSKuninori Morimoto wm8958_dsp2_fw(component, "MBC", wm8994->mbc, false);
201fbbf5920SMark Brown
20200a6941cSKuninori Morimoto snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM,
203fbbf5920SMark Brown WM8958_DSP2_ENA, WM8958_DSP2_ENA);
204fbbf5920SMark Brown
205fbbf5920SMark Brown /* If we've got user supplied MBC settings use them */
206d9dd4adaSMark Brown if (control->pdata.num_mbc_cfgs) {
207fbbf5920SMark Brown struct wm8958_mbc_cfg *cfg
208d9dd4adaSMark Brown = &control->pdata.mbc_cfgs[wm8994->mbc_cfg];
209fbbf5920SMark Brown
210fbbf5920SMark Brown for (i = 0; i < ARRAY_SIZE(cfg->coeff_regs); i++)
21100a6941cSKuninori Morimoto snd_soc_component_write(component, i + WM8958_MBC_BAND_1_K_1,
212fbbf5920SMark Brown cfg->coeff_regs[i]);
213fbbf5920SMark Brown
214fbbf5920SMark Brown for (i = 0; i < ARRAY_SIZE(cfg->cutoff_regs); i++)
21500a6941cSKuninori Morimoto snd_soc_component_write(component,
216fbbf5920SMark Brown i + WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1,
217fbbf5920SMark Brown cfg->cutoff_regs[i]);
218fbbf5920SMark Brown }
219fbbf5920SMark Brown
220fbbf5920SMark Brown /* Run the DSP */
22100a6941cSKuninori Morimoto snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL,
222fbbf5920SMark Brown WM8958_DSP2_RUNR);
223fbbf5920SMark Brown
224fbbf5920SMark Brown /* And we're off! */
22500a6941cSKuninori Morimoto snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG,
226fbbf5920SMark Brown WM8958_MBC_ENA |
227fbbf5920SMark Brown WM8958_MBC_SEL_MASK,
228fbbf5920SMark Brown path << WM8958_MBC_SEL_SHIFT |
229fbbf5920SMark Brown WM8958_MBC_ENA);
230fbbf5920SMark Brown }
231fbbf5920SMark Brown
wm8958_dsp_start_vss(struct snd_soc_component * component,int path)23200a6941cSKuninori Morimoto static void wm8958_dsp_start_vss(struct snd_soc_component *component, int path)
23309e10d7fSMark Brown {
23400a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
235d9dd4adaSMark Brown struct wm8994 *control = wm8994->wm8994;
23609e10d7fSMark Brown int i, ena;
23709e10d7fSMark Brown
23809e10d7fSMark Brown if (wm8994->mbc_vss)
23900a6941cSKuninori Morimoto wm8958_dsp2_fw(component, "MBC+VSS", wm8994->mbc_vss, false);
24009e10d7fSMark Brown
24100a6941cSKuninori Morimoto snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM,
24209e10d7fSMark Brown WM8958_DSP2_ENA, WM8958_DSP2_ENA);
24309e10d7fSMark Brown
24409e10d7fSMark Brown /* If we've got user supplied settings use them */
245d9dd4adaSMark Brown if (control->pdata.num_mbc_cfgs) {
24609e10d7fSMark Brown struct wm8958_mbc_cfg *cfg
247d9dd4adaSMark Brown = &control->pdata.mbc_cfgs[wm8994->mbc_cfg];
24809e10d7fSMark Brown
24909e10d7fSMark Brown for (i = 0; i < ARRAY_SIZE(cfg->combined_regs); i++)
25000a6941cSKuninori Morimoto snd_soc_component_write(component, i + 0x2800,
25109e10d7fSMark Brown cfg->combined_regs[i]);
25209e10d7fSMark Brown }
25309e10d7fSMark Brown
254d9dd4adaSMark Brown if (control->pdata.num_vss_cfgs) {
25509e10d7fSMark Brown struct wm8958_vss_cfg *cfg
256d9dd4adaSMark Brown = &control->pdata.vss_cfgs[wm8994->vss_cfg];
25709e10d7fSMark Brown
25809e10d7fSMark Brown for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
25900a6941cSKuninori Morimoto snd_soc_component_write(component, i + 0x2600, cfg->regs[i]);
26009e10d7fSMark Brown }
26109e10d7fSMark Brown
262d9dd4adaSMark Brown if (control->pdata.num_vss_hpf_cfgs) {
26309e10d7fSMark Brown struct wm8958_vss_hpf_cfg *cfg
264d9dd4adaSMark Brown = &control->pdata.vss_hpf_cfgs[wm8994->vss_hpf_cfg];
26509e10d7fSMark Brown
26609e10d7fSMark Brown for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
26700a6941cSKuninori Morimoto snd_soc_component_write(component, i + 0x2400, cfg->regs[i]);
26809e10d7fSMark Brown }
26909e10d7fSMark Brown
27009e10d7fSMark Brown /* Run the DSP */
27100a6941cSKuninori Morimoto snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL,
27209e10d7fSMark Brown WM8958_DSP2_RUNR);
27309e10d7fSMark Brown
27409e10d7fSMark Brown /* Enable the algorithms we've selected */
27509e10d7fSMark Brown ena = 0;
27609e10d7fSMark Brown if (wm8994->mbc_ena[path])
27709e10d7fSMark Brown ena |= 0x8;
27809e10d7fSMark Brown if (wm8994->hpf2_ena[path])
27909e10d7fSMark Brown ena |= 0x4;
28009e10d7fSMark Brown if (wm8994->hpf1_ena[path])
28109e10d7fSMark Brown ena |= 0x2;
28209e10d7fSMark Brown if (wm8994->vss_ena[path])
28309e10d7fSMark Brown ena |= 0x1;
28409e10d7fSMark Brown
28500a6941cSKuninori Morimoto snd_soc_component_write(component, 0x2201, ena);
28609e10d7fSMark Brown
28709e10d7fSMark Brown /* Switch the DSP into the data path */
28800a6941cSKuninori Morimoto snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG,
28909e10d7fSMark Brown WM8958_MBC_SEL_MASK | WM8958_MBC_ENA,
29009e10d7fSMark Brown path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA);
29109e10d7fSMark Brown }
29209e10d7fSMark Brown
wm8958_dsp_start_enh_eq(struct snd_soc_component * component,int path)29300a6941cSKuninori Morimoto static void wm8958_dsp_start_enh_eq(struct snd_soc_component *component, int path)
29431215871SMark Brown {
29500a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
296d9dd4adaSMark Brown struct wm8994 *control = wm8994->wm8994;
29731215871SMark Brown int i;
29831215871SMark Brown
29900a6941cSKuninori Morimoto wm8958_dsp2_fw(component, "ENH_EQ", wm8994->enh_eq, false);
30031215871SMark Brown
30100a6941cSKuninori Morimoto snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM,
30231215871SMark Brown WM8958_DSP2_ENA, WM8958_DSP2_ENA);
30331215871SMark Brown
30431215871SMark Brown /* If we've got user supplied settings use them */
305d9dd4adaSMark Brown if (control->pdata.num_enh_eq_cfgs) {
30631215871SMark Brown struct wm8958_enh_eq_cfg *cfg
307d9dd4adaSMark Brown = &control->pdata.enh_eq_cfgs[wm8994->enh_eq_cfg];
30831215871SMark Brown
30931215871SMark Brown for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
31000a6941cSKuninori Morimoto snd_soc_component_write(component, i + 0x2200,
31131215871SMark Brown cfg->regs[i]);
31231215871SMark Brown }
31331215871SMark Brown
31431215871SMark Brown /* Run the DSP */
31500a6941cSKuninori Morimoto snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL,
31631215871SMark Brown WM8958_DSP2_RUNR);
31731215871SMark Brown
31831215871SMark Brown /* Switch the DSP into the data path */
31900a6941cSKuninori Morimoto snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG,
32031215871SMark Brown WM8958_MBC_SEL_MASK | WM8958_MBC_ENA,
32131215871SMark Brown path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA);
32231215871SMark Brown }
32309e10d7fSMark Brown
wm8958_dsp_apply(struct snd_soc_component * component,int path,int start)32400a6941cSKuninori Morimoto static void wm8958_dsp_apply(struct snd_soc_component *component, int path, int start)
325fbbf5920SMark Brown {
32600a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
3276d75dfc3SKuninori Morimoto int pwr_reg = snd_soc_component_read(component, WM8994_POWER_MANAGEMENT_5);
328f20d77ceSMark Brown int ena, reg, aif;
329f701a2e5SMark Brown
330f20d77ceSMark Brown switch (path) {
331f701a2e5SMark Brown case 0:
332f701a2e5SMark Brown pwr_reg &= (WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA);
333f701a2e5SMark Brown aif = 0;
334f701a2e5SMark Brown break;
335f701a2e5SMark Brown case 1:
336f701a2e5SMark Brown pwr_reg &= (WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);
337f701a2e5SMark Brown aif = 0;
338f701a2e5SMark Brown break;
339f701a2e5SMark Brown case 2:
340f701a2e5SMark Brown pwr_reg &= (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA);
341f701a2e5SMark Brown aif = 1;
342f701a2e5SMark Brown break;
343f701a2e5SMark Brown default:
344a845d59dSTakashi Iwai WARN(1, "Invalid path %d\n", path);
345f701a2e5SMark Brown return;
346f701a2e5SMark Brown }
347f701a2e5SMark Brown
348f20d77ceSMark Brown /* Do we have both an active AIF and an active algorithm? */
34909e10d7fSMark Brown ena = wm8994->mbc_ena[path] || wm8994->vss_ena[path] ||
35031215871SMark Brown wm8994->hpf1_ena[path] || wm8994->hpf2_ena[path] ||
35131215871SMark Brown wm8994->enh_eq_ena[path];
352f20d77ceSMark Brown if (!pwr_reg)
353f20d77ceSMark Brown ena = 0;
354f701a2e5SMark Brown
3556d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8958_DSP2_PROGRAM);
356f701a2e5SMark Brown
35700a6941cSKuninori Morimoto dev_dbg(component->dev, "DSP path %d %d startup: %d, power: %x, DSP: %x\n",
358f20d77ceSMark Brown path, wm8994->dsp_active, start, pwr_reg, reg);
359f701a2e5SMark Brown
360f701a2e5SMark Brown if (start && ena) {
361fa63e477SMark Brown /* If the DSP is already running then noop */
362fa63e477SMark Brown if (reg & WM8958_DSP2_ENA)
363fa63e477SMark Brown return;
364fa63e477SMark Brown
365f20d77ceSMark Brown /* If either AIFnCLK is not yet enabled postpone */
3666d75dfc3SKuninori Morimoto if (!(snd_soc_component_read(component, WM8994_AIF1_CLOCKING_1)
367c6b7b570SMark Brown & WM8994_AIF1CLK_ENA_MASK) &&
3686d75dfc3SKuninori Morimoto !(snd_soc_component_read(component, WM8994_AIF2_CLOCKING_1)
369c6b7b570SMark Brown & WM8994_AIF2CLK_ENA_MASK))
370c6b7b570SMark Brown return;
371c6b7b570SMark Brown
372f701a2e5SMark Brown /* Switch the clock over to the appropriate AIF */
37300a6941cSKuninori Morimoto snd_soc_component_update_bits(component, WM8994_CLOCKING_1,
374f701a2e5SMark Brown WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA,
375f701a2e5SMark Brown aif << WM8958_DSP2CLK_SRC_SHIFT |
376f701a2e5SMark Brown WM8958_DSP2CLK_ENA);
377f701a2e5SMark Brown
37831215871SMark Brown if (wm8994->enh_eq_ena[path])
37900a6941cSKuninori Morimoto wm8958_dsp_start_enh_eq(component, path);
38031215871SMark Brown else if (wm8994->vss_ena[path] || wm8994->hpf1_ena[path] ||
38109e10d7fSMark Brown wm8994->hpf2_ena[path])
38200a6941cSKuninori Morimoto wm8958_dsp_start_vss(component, path);
38309e10d7fSMark Brown else if (wm8994->mbc_ena[path])
38400a6941cSKuninori Morimoto wm8958_dsp_start_mbc(component, path);
385f701a2e5SMark Brown
38609e10d7fSMark Brown wm8994->dsp_active = path;
38709e10d7fSMark Brown
38800a6941cSKuninori Morimoto dev_dbg(component->dev, "DSP running in path %d\n", path);
38909e10d7fSMark Brown }
39009e10d7fSMark Brown
39109e10d7fSMark Brown if (!start && wm8994->dsp_active == path) {
392f701a2e5SMark Brown /* If the DSP is already stopped then noop */
393f701a2e5SMark Brown if (!(reg & WM8958_DSP2_ENA))
394f701a2e5SMark Brown return;
395f701a2e5SMark Brown
39600a6941cSKuninori Morimoto snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG,
397f701a2e5SMark Brown WM8958_MBC_ENA, 0);
39800a6941cSKuninori Morimoto snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL,
399f20d77ceSMark Brown WM8958_DSP2_STOP);
40000a6941cSKuninori Morimoto snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM,
401f701a2e5SMark Brown WM8958_DSP2_ENA, 0);
40200a6941cSKuninori Morimoto snd_soc_component_update_bits(component, WM8994_CLOCKING_1,
403f701a2e5SMark Brown WM8958_DSP2CLK_ENA, 0);
404f20d77ceSMark Brown
405f20d77ceSMark Brown wm8994->dsp_active = -1;
406f20d77ceSMark Brown
40700a6941cSKuninori Morimoto dev_dbg(component->dev, "DSP stopped\n");
408f701a2e5SMark Brown }
409f701a2e5SMark Brown }
410f701a2e5SMark Brown
wm8958_aif_ev(struct snd_soc_dapm_widget * w,struct snd_kcontrol * kcontrol,int event)411f701a2e5SMark Brown int wm8958_aif_ev(struct snd_soc_dapm_widget *w,
412f701a2e5SMark Brown struct snd_kcontrol *kcontrol, int event)
413f701a2e5SMark Brown {
41400a6941cSKuninori Morimoto struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
415f082bb59SSylwester Nawrocki struct wm8994 *control = dev_get_drvdata(component->dev->parent);
416c6b7b570SMark Brown int i;
417f701a2e5SMark Brown
418f082bb59SSylwester Nawrocki if (control->type != WM8958)
419f082bb59SSylwester Nawrocki return 0;
420f082bb59SSylwester Nawrocki
421f701a2e5SMark Brown switch (event) {
422f701a2e5SMark Brown case SND_SOC_DAPM_POST_PMU:
423c6b7b570SMark Brown case SND_SOC_DAPM_PRE_PMU:
424c6b7b570SMark Brown for (i = 0; i < 3; i++)
42500a6941cSKuninori Morimoto wm8958_dsp_apply(component, i, 1);
426f701a2e5SMark Brown break;
427f701a2e5SMark Brown case SND_SOC_DAPM_POST_PMD:
428c6b7b570SMark Brown case SND_SOC_DAPM_PRE_PMD:
429c6b7b570SMark Brown for (i = 0; i < 3; i++)
43000a6941cSKuninori Morimoto wm8958_dsp_apply(component, i, 0);
431f701a2e5SMark Brown break;
432f701a2e5SMark Brown }
433f701a2e5SMark Brown
434f701a2e5SMark Brown return 0;
435f701a2e5SMark Brown }
436f701a2e5SMark Brown
437f20d77ceSMark Brown /* Check if DSP2 is in use on another AIF */
wm8958_dsp2_busy(struct wm8994_priv * wm8994,int aif)438f20d77ceSMark Brown static int wm8958_dsp2_busy(struct wm8994_priv *wm8994, int aif)
439f20d77ceSMark Brown {
440f20d77ceSMark Brown int i;
441f20d77ceSMark Brown
442f20d77ceSMark Brown for (i = 0; i < ARRAY_SIZE(wm8994->mbc_ena); i++) {
443f20d77ceSMark Brown if (i == aif)
444f20d77ceSMark Brown continue;
44509e10d7fSMark Brown if (wm8994->mbc_ena[i] || wm8994->vss_ena[i] ||
44609e10d7fSMark Brown wm8994->hpf1_ena[i] || wm8994->hpf2_ena[i])
447f20d77ceSMark Brown return 1;
448f20d77ceSMark Brown }
449f20d77ceSMark Brown
450f20d77ceSMark Brown return 0;
451f20d77ceSMark Brown }
452f20d77ceSMark Brown
wm8958_put_mbc_enum(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)453f701a2e5SMark Brown static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol,
454f701a2e5SMark Brown struct snd_ctl_elem_value *ucontrol)
455f701a2e5SMark Brown {
45600a6941cSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
45700a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
458d9dd4adaSMark Brown struct wm8994 *control = wm8994->wm8994;
459d0784829STakashi Iwai int value = ucontrol->value.enumerated.item[0];
460f701a2e5SMark Brown int reg;
461f701a2e5SMark Brown
462f701a2e5SMark Brown /* Don't allow on the fly reconfiguration */
4636d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8994_CLOCKING_1);
464f701a2e5SMark Brown if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
465f701a2e5SMark Brown return -EBUSY;
466f701a2e5SMark Brown
467d9dd4adaSMark Brown if (value >= control->pdata.num_mbc_cfgs)
468f701a2e5SMark Brown return -EINVAL;
469f701a2e5SMark Brown
470f701a2e5SMark Brown wm8994->mbc_cfg = value;
471f701a2e5SMark Brown
472f701a2e5SMark Brown return 0;
473f701a2e5SMark Brown }
474f701a2e5SMark Brown
wm8958_get_mbc_enum(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)475f701a2e5SMark Brown static int wm8958_get_mbc_enum(struct snd_kcontrol *kcontrol,
476f701a2e5SMark Brown struct snd_ctl_elem_value *ucontrol)
477f701a2e5SMark Brown {
47800a6941cSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
47900a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
480f701a2e5SMark Brown
481f701a2e5SMark Brown ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg;
482f701a2e5SMark Brown
483f701a2e5SMark Brown return 0;
484f701a2e5SMark Brown }
485f701a2e5SMark Brown
wm8958_mbc_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)486f701a2e5SMark Brown static int wm8958_mbc_info(struct snd_kcontrol *kcontrol,
487f701a2e5SMark Brown struct snd_ctl_elem_info *uinfo)
488f701a2e5SMark Brown {
489f701a2e5SMark Brown uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
490f701a2e5SMark Brown uinfo->count = 1;
491f701a2e5SMark Brown uinfo->value.integer.min = 0;
492f701a2e5SMark Brown uinfo->value.integer.max = 1;
493f701a2e5SMark Brown return 0;
494f701a2e5SMark Brown }
495f701a2e5SMark Brown
wm8958_mbc_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)496f701a2e5SMark Brown static int wm8958_mbc_get(struct snd_kcontrol *kcontrol,
497f701a2e5SMark Brown struct snd_ctl_elem_value *ucontrol)
498f701a2e5SMark Brown {
499f701a2e5SMark Brown int mbc = kcontrol->private_value;
50000a6941cSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
50100a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
502f701a2e5SMark Brown
503f701a2e5SMark Brown ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc];
504f701a2e5SMark Brown
505f701a2e5SMark Brown return 0;
506f701a2e5SMark Brown }
507f701a2e5SMark Brown
wm8958_mbc_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)508f701a2e5SMark Brown static int wm8958_mbc_put(struct snd_kcontrol *kcontrol,
509f701a2e5SMark Brown struct snd_ctl_elem_value *ucontrol)
510f701a2e5SMark Brown {
511f701a2e5SMark Brown int mbc = kcontrol->private_value;
51200a6941cSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
51300a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
514f701a2e5SMark Brown
515d7fdae7cSMark Brown if (wm8994->mbc_ena[mbc] == ucontrol->value.integer.value[0])
516d7fdae7cSMark Brown return 0;
517d7fdae7cSMark Brown
518f701a2e5SMark Brown if (ucontrol->value.integer.value[0] > 1)
519f701a2e5SMark Brown return -EINVAL;
520f701a2e5SMark Brown
521f20d77ceSMark Brown if (wm8958_dsp2_busy(wm8994, mbc)) {
52200a6941cSKuninori Morimoto dev_dbg(component->dev, "DSP2 active on %d already\n", mbc);
523f701a2e5SMark Brown return -EBUSY;
524f701a2e5SMark Brown }
525f701a2e5SMark Brown
52631215871SMark Brown if (wm8994->enh_eq_ena[mbc])
52731215871SMark Brown return -EBUSY;
52831215871SMark Brown
529f701a2e5SMark Brown wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0];
530f701a2e5SMark Brown
53100a6941cSKuninori Morimoto wm8958_dsp_apply(component, mbc, wm8994->mbc_ena[mbc]);
532f701a2e5SMark Brown
533*b4f5c6b2SMark Brown return 1;
534f701a2e5SMark Brown }
535f701a2e5SMark Brown
536f701a2e5SMark Brown #define WM8958_MBC_SWITCH(xname, xval) {\
537f701a2e5SMark Brown .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
538f701a2e5SMark Brown .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
539f701a2e5SMark Brown .info = wm8958_mbc_info, \
540f701a2e5SMark Brown .get = wm8958_mbc_get, .put = wm8958_mbc_put, \
541f701a2e5SMark Brown .private_value = xval }
542f701a2e5SMark Brown
wm8958_put_vss_enum(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)54309e10d7fSMark Brown static int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol,
54409e10d7fSMark Brown struct snd_ctl_elem_value *ucontrol)
54509e10d7fSMark Brown {
54600a6941cSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
54700a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
548d9dd4adaSMark Brown struct wm8994 *control = wm8994->wm8994;
549d0784829STakashi Iwai int value = ucontrol->value.enumerated.item[0];
55009e10d7fSMark Brown int reg;
55109e10d7fSMark Brown
55209e10d7fSMark Brown /* Don't allow on the fly reconfiguration */
5536d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8994_CLOCKING_1);
55409e10d7fSMark Brown if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
55509e10d7fSMark Brown return -EBUSY;
55609e10d7fSMark Brown
557d9dd4adaSMark Brown if (value >= control->pdata.num_vss_cfgs)
55809e10d7fSMark Brown return -EINVAL;
55909e10d7fSMark Brown
56009e10d7fSMark Brown wm8994->vss_cfg = value;
56109e10d7fSMark Brown
56209e10d7fSMark Brown return 0;
56309e10d7fSMark Brown }
56409e10d7fSMark Brown
wm8958_get_vss_enum(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)56509e10d7fSMark Brown static int wm8958_get_vss_enum(struct snd_kcontrol *kcontrol,
56609e10d7fSMark Brown struct snd_ctl_elem_value *ucontrol)
56709e10d7fSMark Brown {
56800a6941cSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
56900a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
57009e10d7fSMark Brown
57109e10d7fSMark Brown ucontrol->value.enumerated.item[0] = wm8994->vss_cfg;
57209e10d7fSMark Brown
57309e10d7fSMark Brown return 0;
57409e10d7fSMark Brown }
57509e10d7fSMark Brown
wm8958_put_vss_hpf_enum(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)57609e10d7fSMark Brown static int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol,
57709e10d7fSMark Brown struct snd_ctl_elem_value *ucontrol)
57809e10d7fSMark Brown {
57900a6941cSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
58000a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
581d9dd4adaSMark Brown struct wm8994 *control = wm8994->wm8994;
582d0784829STakashi Iwai int value = ucontrol->value.enumerated.item[0];
58309e10d7fSMark Brown int reg;
58409e10d7fSMark Brown
58509e10d7fSMark Brown /* Don't allow on the fly reconfiguration */
5866d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8994_CLOCKING_1);
58709e10d7fSMark Brown if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
58809e10d7fSMark Brown return -EBUSY;
58909e10d7fSMark Brown
590d9dd4adaSMark Brown if (value >= control->pdata.num_vss_hpf_cfgs)
59109e10d7fSMark Brown return -EINVAL;
59209e10d7fSMark Brown
59309e10d7fSMark Brown wm8994->vss_hpf_cfg = value;
59409e10d7fSMark Brown
59509e10d7fSMark Brown return 0;
59609e10d7fSMark Brown }
59709e10d7fSMark Brown
wm8958_get_vss_hpf_enum(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)59809e10d7fSMark Brown static int wm8958_get_vss_hpf_enum(struct snd_kcontrol *kcontrol,
59909e10d7fSMark Brown struct snd_ctl_elem_value *ucontrol)
60009e10d7fSMark Brown {
60100a6941cSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
60200a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
60309e10d7fSMark Brown
60409e10d7fSMark Brown ucontrol->value.enumerated.item[0] = wm8994->vss_hpf_cfg;
60509e10d7fSMark Brown
60609e10d7fSMark Brown return 0;
60709e10d7fSMark Brown }
60809e10d7fSMark Brown
wm8958_vss_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)60909e10d7fSMark Brown static int wm8958_vss_info(struct snd_kcontrol *kcontrol,
61009e10d7fSMark Brown struct snd_ctl_elem_info *uinfo)
61109e10d7fSMark Brown {
61209e10d7fSMark Brown uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
61309e10d7fSMark Brown uinfo->count = 1;
61409e10d7fSMark Brown uinfo->value.integer.min = 0;
61509e10d7fSMark Brown uinfo->value.integer.max = 1;
61609e10d7fSMark Brown return 0;
61709e10d7fSMark Brown }
61809e10d7fSMark Brown
wm8958_vss_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)61909e10d7fSMark Brown static int wm8958_vss_get(struct snd_kcontrol *kcontrol,
62009e10d7fSMark Brown struct snd_ctl_elem_value *ucontrol)
62109e10d7fSMark Brown {
62209e10d7fSMark Brown int vss = kcontrol->private_value;
62300a6941cSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
62400a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
62509e10d7fSMark Brown
62609e10d7fSMark Brown ucontrol->value.integer.value[0] = wm8994->vss_ena[vss];
62709e10d7fSMark Brown
62809e10d7fSMark Brown return 0;
62909e10d7fSMark Brown }
63009e10d7fSMark Brown
wm8958_vss_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)63109e10d7fSMark Brown static int wm8958_vss_put(struct snd_kcontrol *kcontrol,
63209e10d7fSMark Brown struct snd_ctl_elem_value *ucontrol)
63309e10d7fSMark Brown {
63409e10d7fSMark Brown int vss = kcontrol->private_value;
63500a6941cSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
63600a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
63709e10d7fSMark Brown
638d7fdae7cSMark Brown if (wm8994->vss_ena[vss] == ucontrol->value.integer.value[0])
639d7fdae7cSMark Brown return 0;
640d7fdae7cSMark Brown
64109e10d7fSMark Brown if (ucontrol->value.integer.value[0] > 1)
64209e10d7fSMark Brown return -EINVAL;
64309e10d7fSMark Brown
64409e10d7fSMark Brown if (!wm8994->mbc_vss)
64509e10d7fSMark Brown return -ENODEV;
64609e10d7fSMark Brown
64709e10d7fSMark Brown if (wm8958_dsp2_busy(wm8994, vss)) {
64800a6941cSKuninori Morimoto dev_dbg(component->dev, "DSP2 active on %d already\n", vss);
64909e10d7fSMark Brown return -EBUSY;
65009e10d7fSMark Brown }
65109e10d7fSMark Brown
65231215871SMark Brown if (wm8994->enh_eq_ena[vss])
65331215871SMark Brown return -EBUSY;
65431215871SMark Brown
65509e10d7fSMark Brown wm8994->vss_ena[vss] = ucontrol->value.integer.value[0];
65609e10d7fSMark Brown
65700a6941cSKuninori Morimoto wm8958_dsp_apply(component, vss, wm8994->vss_ena[vss]);
65809e10d7fSMark Brown
659*b4f5c6b2SMark Brown return 1;
66009e10d7fSMark Brown }
66109e10d7fSMark Brown
66209e10d7fSMark Brown
66309e10d7fSMark Brown #define WM8958_VSS_SWITCH(xname, xval) {\
66409e10d7fSMark Brown .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
66509e10d7fSMark Brown .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
66609e10d7fSMark Brown .info = wm8958_vss_info, \
66709e10d7fSMark Brown .get = wm8958_vss_get, .put = wm8958_vss_put, \
66809e10d7fSMark Brown .private_value = xval }
66909e10d7fSMark Brown
wm8958_hpf_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)67009e10d7fSMark Brown static int wm8958_hpf_info(struct snd_kcontrol *kcontrol,
67109e10d7fSMark Brown struct snd_ctl_elem_info *uinfo)
67209e10d7fSMark Brown {
67309e10d7fSMark Brown uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
67409e10d7fSMark Brown uinfo->count = 1;
67509e10d7fSMark Brown uinfo->value.integer.min = 0;
67609e10d7fSMark Brown uinfo->value.integer.max = 1;
67709e10d7fSMark Brown return 0;
67809e10d7fSMark Brown }
67909e10d7fSMark Brown
wm8958_hpf_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)68009e10d7fSMark Brown static int wm8958_hpf_get(struct snd_kcontrol *kcontrol,
68109e10d7fSMark Brown struct snd_ctl_elem_value *ucontrol)
68209e10d7fSMark Brown {
68309e10d7fSMark Brown int hpf = kcontrol->private_value;
68400a6941cSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
68500a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
68609e10d7fSMark Brown
68709e10d7fSMark Brown if (hpf < 3)
68809e10d7fSMark Brown ucontrol->value.integer.value[0] = wm8994->hpf1_ena[hpf % 3];
68909e10d7fSMark Brown else
69009e10d7fSMark Brown ucontrol->value.integer.value[0] = wm8994->hpf2_ena[hpf % 3];
69109e10d7fSMark Brown
69209e10d7fSMark Brown return 0;
69309e10d7fSMark Brown }
69409e10d7fSMark Brown
wm8958_hpf_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)69509e10d7fSMark Brown static int wm8958_hpf_put(struct snd_kcontrol *kcontrol,
69609e10d7fSMark Brown struct snd_ctl_elem_value *ucontrol)
69709e10d7fSMark Brown {
69809e10d7fSMark Brown int hpf = kcontrol->private_value;
69900a6941cSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
70000a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
70109e10d7fSMark Brown
702d7fdae7cSMark Brown if (hpf < 3) {
703d7fdae7cSMark Brown if (wm8994->hpf1_ena[hpf % 3] ==
704d7fdae7cSMark Brown ucontrol->value.integer.value[0])
705d7fdae7cSMark Brown return 0;
706d7fdae7cSMark Brown } else {
707d7fdae7cSMark Brown if (wm8994->hpf2_ena[hpf % 3] ==
708d7fdae7cSMark Brown ucontrol->value.integer.value[0])
709d7fdae7cSMark Brown return 0;
710d7fdae7cSMark Brown }
711d7fdae7cSMark Brown
71209e10d7fSMark Brown if (ucontrol->value.integer.value[0] > 1)
71309e10d7fSMark Brown return -EINVAL;
71409e10d7fSMark Brown
71509e10d7fSMark Brown if (!wm8994->mbc_vss)
71609e10d7fSMark Brown return -ENODEV;
71709e10d7fSMark Brown
71809e10d7fSMark Brown if (wm8958_dsp2_busy(wm8994, hpf % 3)) {
71900a6941cSKuninori Morimoto dev_dbg(component->dev, "DSP2 active on %d already\n", hpf);
72009e10d7fSMark Brown return -EBUSY;
72109e10d7fSMark Brown }
72209e10d7fSMark Brown
72331215871SMark Brown if (wm8994->enh_eq_ena[hpf % 3])
72409e10d7fSMark Brown return -EBUSY;
72509e10d7fSMark Brown
72609e10d7fSMark Brown if (hpf < 3)
72709e10d7fSMark Brown wm8994->hpf1_ena[hpf % 3] = ucontrol->value.integer.value[0];
72809e10d7fSMark Brown else
72909e10d7fSMark Brown wm8994->hpf2_ena[hpf % 3] = ucontrol->value.integer.value[0];
73009e10d7fSMark Brown
73100a6941cSKuninori Morimoto wm8958_dsp_apply(component, hpf % 3, ucontrol->value.integer.value[0]);
73209e10d7fSMark Brown
733*b4f5c6b2SMark Brown return 1;
73409e10d7fSMark Brown }
73509e10d7fSMark Brown
73609e10d7fSMark Brown #define WM8958_HPF_SWITCH(xname, xval) {\
73709e10d7fSMark Brown .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
73809e10d7fSMark Brown .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
73909e10d7fSMark Brown .info = wm8958_hpf_info, \
74009e10d7fSMark Brown .get = wm8958_hpf_get, .put = wm8958_hpf_put, \
74109e10d7fSMark Brown .private_value = xval }
74209e10d7fSMark Brown
wm8958_put_enh_eq_enum(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)74331215871SMark Brown static int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol,
74431215871SMark Brown struct snd_ctl_elem_value *ucontrol)
74531215871SMark Brown {
74600a6941cSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
74700a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
748d9dd4adaSMark Brown struct wm8994 *control = wm8994->wm8994;
749d0784829STakashi Iwai int value = ucontrol->value.enumerated.item[0];
75031215871SMark Brown int reg;
75131215871SMark Brown
75231215871SMark Brown /* Don't allow on the fly reconfiguration */
7536d75dfc3SKuninori Morimoto reg = snd_soc_component_read(component, WM8994_CLOCKING_1);
75431215871SMark Brown if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
75531215871SMark Brown return -EBUSY;
75631215871SMark Brown
757d9dd4adaSMark Brown if (value >= control->pdata.num_enh_eq_cfgs)
75831215871SMark Brown return -EINVAL;
75931215871SMark Brown
76031215871SMark Brown wm8994->enh_eq_cfg = value;
76131215871SMark Brown
76231215871SMark Brown return 0;
76331215871SMark Brown }
76431215871SMark Brown
wm8958_get_enh_eq_enum(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)76531215871SMark Brown static int wm8958_get_enh_eq_enum(struct snd_kcontrol *kcontrol,
76631215871SMark Brown struct snd_ctl_elem_value *ucontrol)
76731215871SMark Brown {
76800a6941cSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
76900a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
77031215871SMark Brown
77131215871SMark Brown ucontrol->value.enumerated.item[0] = wm8994->enh_eq_cfg;
77231215871SMark Brown
77331215871SMark Brown return 0;
77431215871SMark Brown }
77531215871SMark Brown
wm8958_enh_eq_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)77631215871SMark Brown static int wm8958_enh_eq_info(struct snd_kcontrol *kcontrol,
77731215871SMark Brown struct snd_ctl_elem_info *uinfo)
77831215871SMark Brown {
77931215871SMark Brown uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
78031215871SMark Brown uinfo->count = 1;
78131215871SMark Brown uinfo->value.integer.min = 0;
78231215871SMark Brown uinfo->value.integer.max = 1;
78331215871SMark Brown return 0;
78431215871SMark Brown }
78531215871SMark Brown
wm8958_enh_eq_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)78631215871SMark Brown static int wm8958_enh_eq_get(struct snd_kcontrol *kcontrol,
78731215871SMark Brown struct snd_ctl_elem_value *ucontrol)
78831215871SMark Brown {
78931215871SMark Brown int eq = kcontrol->private_value;
79000a6941cSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
79100a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
79231215871SMark Brown
79331215871SMark Brown ucontrol->value.integer.value[0] = wm8994->enh_eq_ena[eq];
79431215871SMark Brown
79531215871SMark Brown return 0;
79631215871SMark Brown }
79731215871SMark Brown
wm8958_enh_eq_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)79831215871SMark Brown static int wm8958_enh_eq_put(struct snd_kcontrol *kcontrol,
79931215871SMark Brown struct snd_ctl_elem_value *ucontrol)
80031215871SMark Brown {
80131215871SMark Brown int eq = kcontrol->private_value;
80200a6941cSKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
80300a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
80431215871SMark Brown
805d7fdae7cSMark Brown if (wm8994->enh_eq_ena[eq] == ucontrol->value.integer.value[0])
806d7fdae7cSMark Brown return 0;
807d7fdae7cSMark Brown
80831215871SMark Brown if (ucontrol->value.integer.value[0] > 1)
80931215871SMark Brown return -EINVAL;
81031215871SMark Brown
81131215871SMark Brown if (!wm8994->enh_eq)
81231215871SMark Brown return -ENODEV;
81331215871SMark Brown
81431215871SMark Brown if (wm8958_dsp2_busy(wm8994, eq)) {
81500a6941cSKuninori Morimoto dev_dbg(component->dev, "DSP2 active on %d already\n", eq);
81631215871SMark Brown return -EBUSY;
81731215871SMark Brown }
81831215871SMark Brown
81931215871SMark Brown if (wm8994->mbc_ena[eq] || wm8994->vss_ena[eq] ||
82031215871SMark Brown wm8994->hpf1_ena[eq] || wm8994->hpf2_ena[eq])
82131215871SMark Brown return -EBUSY;
82231215871SMark Brown
82331215871SMark Brown wm8994->enh_eq_ena[eq] = ucontrol->value.integer.value[0];
82431215871SMark Brown
82500a6941cSKuninori Morimoto wm8958_dsp_apply(component, eq, ucontrol->value.integer.value[0]);
82631215871SMark Brown
827*b4f5c6b2SMark Brown return 1;
82831215871SMark Brown }
82931215871SMark Brown
83031215871SMark Brown #define WM8958_ENH_EQ_SWITCH(xname, xval) {\
83131215871SMark Brown .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
83231215871SMark Brown .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
83331215871SMark Brown .info = wm8958_enh_eq_info, \
83431215871SMark Brown .get = wm8958_enh_eq_get, .put = wm8958_enh_eq_put, \
83531215871SMark Brown .private_value = xval }
83631215871SMark Brown
837f701a2e5SMark Brown static const struct snd_kcontrol_new wm8958_mbc_snd_controls[] = {
838f701a2e5SMark Brown WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0),
839f701a2e5SMark Brown WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1),
840f701a2e5SMark Brown WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2),
841f701a2e5SMark Brown };
842f701a2e5SMark Brown
84309e10d7fSMark Brown static const struct snd_kcontrol_new wm8958_vss_snd_controls[] = {
84409e10d7fSMark Brown WM8958_VSS_SWITCH("AIF1DAC1 VSS Switch", 0),
84509e10d7fSMark Brown WM8958_VSS_SWITCH("AIF1DAC2 VSS Switch", 1),
84609e10d7fSMark Brown WM8958_VSS_SWITCH("AIF2DAC VSS Switch", 2),
84709e10d7fSMark Brown WM8958_HPF_SWITCH("AIF1DAC1 HPF1 Switch", 0),
84809e10d7fSMark Brown WM8958_HPF_SWITCH("AIF1DAC2 HPF1 Switch", 1),
84909e10d7fSMark Brown WM8958_HPF_SWITCH("AIF2DAC HPF1 Switch", 2),
85009e10d7fSMark Brown WM8958_HPF_SWITCH("AIF1DAC1 HPF2 Switch", 3),
85109e10d7fSMark Brown WM8958_HPF_SWITCH("AIF1DAC2 HPF2 Switch", 4),
85209e10d7fSMark Brown WM8958_HPF_SWITCH("AIF2DAC HPF2 Switch", 5),
85309e10d7fSMark Brown };
85409e10d7fSMark Brown
85531215871SMark Brown static const struct snd_kcontrol_new wm8958_enh_eq_snd_controls[] = {
85631215871SMark Brown WM8958_ENH_EQ_SWITCH("AIF1DAC1 Enhanced EQ Switch", 0),
85731215871SMark Brown WM8958_ENH_EQ_SWITCH("AIF1DAC2 Enhanced EQ Switch", 1),
85831215871SMark Brown WM8958_ENH_EQ_SWITCH("AIF2DAC Enhanced EQ Switch", 2),
85931215871SMark Brown };
86031215871SMark Brown
wm8958_enh_eq_loaded(const struct firmware * fw,void * context)86131215871SMark Brown static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context)
86231215871SMark Brown {
86300a6941cSKuninori Morimoto struct snd_soc_component *component = context;
86400a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
86531215871SMark Brown
86600a6941cSKuninori Morimoto if (fw && (wm8958_dsp2_fw(component, "ENH_EQ", fw, true) == 0)) {
867fabfad2fSLars-Peter Clausen mutex_lock(&wm8994->fw_lock);
86831215871SMark Brown wm8994->enh_eq = fw;
869fabfad2fSLars-Peter Clausen mutex_unlock(&wm8994->fw_lock);
87031215871SMark Brown }
87131215871SMark Brown }
87231215871SMark Brown
wm8958_mbc_vss_loaded(const struct firmware * fw,void * context)87309e10d7fSMark Brown static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context)
87409e10d7fSMark Brown {
87500a6941cSKuninori Morimoto struct snd_soc_component *component = context;
87600a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
87709e10d7fSMark Brown
87800a6941cSKuninori Morimoto if (fw && (wm8958_dsp2_fw(component, "MBC+VSS", fw, true) == 0)) {
879fabfad2fSLars-Peter Clausen mutex_lock(&wm8994->fw_lock);
88009e10d7fSMark Brown wm8994->mbc_vss = fw;
881fabfad2fSLars-Peter Clausen mutex_unlock(&wm8994->fw_lock);
88209e10d7fSMark Brown }
88309e10d7fSMark Brown }
88409e10d7fSMark Brown
wm8958_mbc_loaded(const struct firmware * fw,void * context)885fbbf5920SMark Brown static void wm8958_mbc_loaded(const struct firmware *fw, void *context)
886fbbf5920SMark Brown {
88700a6941cSKuninori Morimoto struct snd_soc_component *component = context;
88800a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
889fbbf5920SMark Brown
89000a6941cSKuninori Morimoto if (fw && (wm8958_dsp2_fw(component, "MBC", fw, true) == 0)) {
891fabfad2fSLars-Peter Clausen mutex_lock(&wm8994->fw_lock);
892fbbf5920SMark Brown wm8994->mbc = fw;
893fabfad2fSLars-Peter Clausen mutex_unlock(&wm8994->fw_lock);
8940ffecd7dSMark Brown }
895fbbf5920SMark Brown }
896fbbf5920SMark Brown
wm8958_dsp2_init(struct snd_soc_component * component)89700a6941cSKuninori Morimoto void wm8958_dsp2_init(struct snd_soc_component *component)
898f701a2e5SMark Brown {
89900a6941cSKuninori Morimoto struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
900d9dd4adaSMark Brown struct wm8994 *control = wm8994->wm8994;
901d9dd4adaSMark Brown struct wm8994_pdata *pdata = &control->pdata;
902f701a2e5SMark Brown int ret, i;
903f701a2e5SMark Brown
904f20d77ceSMark Brown wm8994->dsp_active = -1;
905f20d77ceSMark Brown
90600a6941cSKuninori Morimoto snd_soc_add_component_controls(component, wm8958_mbc_snd_controls,
907f701a2e5SMark Brown ARRAY_SIZE(wm8958_mbc_snd_controls));
90800a6941cSKuninori Morimoto snd_soc_add_component_controls(component, wm8958_vss_snd_controls,
90909e10d7fSMark Brown ARRAY_SIZE(wm8958_vss_snd_controls));
91000a6941cSKuninori Morimoto snd_soc_add_component_controls(component, wm8958_enh_eq_snd_controls,
91131215871SMark Brown ARRAY_SIZE(wm8958_enh_eq_snd_controls));
91209e10d7fSMark Brown
913f701a2e5SMark Brown
914f20d77ceSMark Brown /* We don't *require* firmware and don't want to delay boot */
9150733d839SShawn Guo request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
91600a6941cSKuninori Morimoto "wm8958_mbc.wfw", component->dev, GFP_KERNEL,
91700a6941cSKuninori Morimoto component, wm8958_mbc_loaded);
9180733d839SShawn Guo request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
91900a6941cSKuninori Morimoto "wm8958_mbc_vss.wfw", component->dev, GFP_KERNEL,
92000a6941cSKuninori Morimoto component, wm8958_mbc_vss_loaded);
9210733d839SShawn Guo request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
92200a6941cSKuninori Morimoto "wm8958_enh_eq.wfw", component->dev, GFP_KERNEL,
92300a6941cSKuninori Morimoto component, wm8958_enh_eq_loaded);
924fbbf5920SMark Brown
925f701a2e5SMark Brown if (pdata->num_mbc_cfgs) {
926d28a9dfeSPierre-Louis Bossart struct snd_kcontrol_new mbc_control[] = {
927f701a2e5SMark Brown SOC_ENUM_EXT("MBC Mode", wm8994->mbc_enum,
928f701a2e5SMark Brown wm8958_get_mbc_enum, wm8958_put_mbc_enum),
929f701a2e5SMark Brown };
930f701a2e5SMark Brown
931f701a2e5SMark Brown /* We need an array of texts for the enum API */
9326da2ec56SKees Cook wm8994->mbc_texts = kmalloc_array(pdata->num_mbc_cfgs,
9336da2ec56SKees Cook sizeof(char *),
9346da2ec56SKees Cook GFP_KERNEL);
9352cec4ff7SSachin Kamat if (!wm8994->mbc_texts)
936f701a2e5SMark Brown return;
937f701a2e5SMark Brown
938f701a2e5SMark Brown for (i = 0; i < pdata->num_mbc_cfgs; i++)
939f701a2e5SMark Brown wm8994->mbc_texts[i] = pdata->mbc_cfgs[i].name;
940f701a2e5SMark Brown
9419a8d38dbSTakashi Iwai wm8994->mbc_enum.items = pdata->num_mbc_cfgs;
942f701a2e5SMark Brown wm8994->mbc_enum.texts = wm8994->mbc_texts;
943f701a2e5SMark Brown
94400a6941cSKuninori Morimoto ret = snd_soc_add_component_controls(wm8994->hubs.component,
945d28a9dfeSPierre-Louis Bossart mbc_control, 1);
946f701a2e5SMark Brown if (ret != 0)
94700a6941cSKuninori Morimoto dev_err(wm8994->hubs.component->dev,
948f701a2e5SMark Brown "Failed to add MBC mode controls: %d\n", ret);
949f701a2e5SMark Brown }
950f701a2e5SMark Brown
95109e10d7fSMark Brown if (pdata->num_vss_cfgs) {
952d28a9dfeSPierre-Louis Bossart struct snd_kcontrol_new vss_control[] = {
95309e10d7fSMark Brown SOC_ENUM_EXT("VSS Mode", wm8994->vss_enum,
95409e10d7fSMark Brown wm8958_get_vss_enum, wm8958_put_vss_enum),
95509e10d7fSMark Brown };
956f701a2e5SMark Brown
95709e10d7fSMark Brown /* We need an array of texts for the enum API */
9586da2ec56SKees Cook wm8994->vss_texts = kmalloc_array(pdata->num_vss_cfgs,
9596da2ec56SKees Cook sizeof(char *),
9606da2ec56SKees Cook GFP_KERNEL);
9612cec4ff7SSachin Kamat if (!wm8994->vss_texts)
96209e10d7fSMark Brown return;
96309e10d7fSMark Brown
96409e10d7fSMark Brown for (i = 0; i < pdata->num_vss_cfgs; i++)
96509e10d7fSMark Brown wm8994->vss_texts[i] = pdata->vss_cfgs[i].name;
96609e10d7fSMark Brown
9679a8d38dbSTakashi Iwai wm8994->vss_enum.items = pdata->num_vss_cfgs;
96809e10d7fSMark Brown wm8994->vss_enum.texts = wm8994->vss_texts;
96909e10d7fSMark Brown
97000a6941cSKuninori Morimoto ret = snd_soc_add_component_controls(wm8994->hubs.component,
971d28a9dfeSPierre-Louis Bossart vss_control, 1);
97209e10d7fSMark Brown if (ret != 0)
97300a6941cSKuninori Morimoto dev_err(wm8994->hubs.component->dev,
97409e10d7fSMark Brown "Failed to add VSS mode controls: %d\n", ret);
97509e10d7fSMark Brown }
97609e10d7fSMark Brown
97709e10d7fSMark Brown if (pdata->num_vss_hpf_cfgs) {
978d28a9dfeSPierre-Louis Bossart struct snd_kcontrol_new hpf_control[] = {
97909e10d7fSMark Brown SOC_ENUM_EXT("VSS HPF Mode", wm8994->vss_hpf_enum,
98009e10d7fSMark Brown wm8958_get_vss_hpf_enum,
98109e10d7fSMark Brown wm8958_put_vss_hpf_enum),
98209e10d7fSMark Brown };
98309e10d7fSMark Brown
98409e10d7fSMark Brown /* We need an array of texts for the enum API */
9856da2ec56SKees Cook wm8994->vss_hpf_texts = kmalloc_array(pdata->num_vss_hpf_cfgs,
9866da2ec56SKees Cook sizeof(char *),
9876da2ec56SKees Cook GFP_KERNEL);
9882cec4ff7SSachin Kamat if (!wm8994->vss_hpf_texts)
98909e10d7fSMark Brown return;
99009e10d7fSMark Brown
99109e10d7fSMark Brown for (i = 0; i < pdata->num_vss_hpf_cfgs; i++)
99209e10d7fSMark Brown wm8994->vss_hpf_texts[i] = pdata->vss_hpf_cfgs[i].name;
99309e10d7fSMark Brown
9949a8d38dbSTakashi Iwai wm8994->vss_hpf_enum.items = pdata->num_vss_hpf_cfgs;
99509e10d7fSMark Brown wm8994->vss_hpf_enum.texts = wm8994->vss_hpf_texts;
99609e10d7fSMark Brown
99700a6941cSKuninori Morimoto ret = snd_soc_add_component_controls(wm8994->hubs.component,
998d28a9dfeSPierre-Louis Bossart hpf_control, 1);
99909e10d7fSMark Brown if (ret != 0)
100000a6941cSKuninori Morimoto dev_err(wm8994->hubs.component->dev,
100109e10d7fSMark Brown "Failed to add VSS HPFmode controls: %d\n",
100209e10d7fSMark Brown ret);
100309e10d7fSMark Brown }
100431215871SMark Brown
100531215871SMark Brown if (pdata->num_enh_eq_cfgs) {
1006d28a9dfeSPierre-Louis Bossart struct snd_kcontrol_new eq_control[] = {
100731215871SMark Brown SOC_ENUM_EXT("Enhanced EQ Mode", wm8994->enh_eq_enum,
100831215871SMark Brown wm8958_get_enh_eq_enum,
100931215871SMark Brown wm8958_put_enh_eq_enum),
101031215871SMark Brown };
101131215871SMark Brown
101231215871SMark Brown /* We need an array of texts for the enum API */
10136da2ec56SKees Cook wm8994->enh_eq_texts = kmalloc_array(pdata->num_enh_eq_cfgs,
10146da2ec56SKees Cook sizeof(char *),
10156da2ec56SKees Cook GFP_KERNEL);
10162cec4ff7SSachin Kamat if (!wm8994->enh_eq_texts)
101731215871SMark Brown return;
101831215871SMark Brown
101931215871SMark Brown for (i = 0; i < pdata->num_enh_eq_cfgs; i++)
102031215871SMark Brown wm8994->enh_eq_texts[i] = pdata->enh_eq_cfgs[i].name;
102131215871SMark Brown
10229a8d38dbSTakashi Iwai wm8994->enh_eq_enum.items = pdata->num_enh_eq_cfgs;
102331215871SMark Brown wm8994->enh_eq_enum.texts = wm8994->enh_eq_texts;
102431215871SMark Brown
102500a6941cSKuninori Morimoto ret = snd_soc_add_component_controls(wm8994->hubs.component,
1026d28a9dfeSPierre-Louis Bossart eq_control, 1);
102731215871SMark Brown if (ret != 0)
102800a6941cSKuninori Morimoto dev_err(wm8994->hubs.component->dev,
102931215871SMark Brown "Failed to add enhanced EQ controls: %d\n",
103031215871SMark Brown ret);
103131215871SMark Brown }
1032f701a2e5SMark Brown }
1033