18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * wm8958-dsp2.c  --  WM8958 DSP2 support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2011 Wolfson Microelectronics plc
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci#include <linux/pm.h>
158c2ecf20Sopenharmony_ci#include <linux/i2c.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <sound/soc.h>
198c2ecf20Sopenharmony_ci#include <sound/initval.h>
208c2ecf20Sopenharmony_ci#include <sound/tlv.h>
218c2ecf20Sopenharmony_ci#include <trace/events/asoc.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <linux/mfd/wm8994/core.h>
248c2ecf20Sopenharmony_ci#include <linux/mfd/wm8994/registers.h>
258c2ecf20Sopenharmony_ci#include <linux/mfd/wm8994/pdata.h>
268c2ecf20Sopenharmony_ci#include <linux/mfd/wm8994/gpio.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include "wm8994.h"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define WM_FW_BLOCK_INFO 0xff
338c2ecf20Sopenharmony_ci#define WM_FW_BLOCK_PM   0x00
348c2ecf20Sopenharmony_ci#define WM_FW_BLOCK_X    0x01
358c2ecf20Sopenharmony_ci#define WM_FW_BLOCK_Y    0x02
368c2ecf20Sopenharmony_ci#define WM_FW_BLOCK_Z    0x03
378c2ecf20Sopenharmony_ci#define WM_FW_BLOCK_I    0x06
388c2ecf20Sopenharmony_ci#define WM_FW_BLOCK_A    0x08
398c2ecf20Sopenharmony_ci#define WM_FW_BLOCK_C    0x0c
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic int wm8958_dsp2_fw(struct snd_soc_component *component, const char *name,
428c2ecf20Sopenharmony_ci			  const struct firmware *fw, bool check)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
458c2ecf20Sopenharmony_ci	u64 data64;
468c2ecf20Sopenharmony_ci	u32 data32;
478c2ecf20Sopenharmony_ci	const u8 *data;
488c2ecf20Sopenharmony_ci	char *str;
498c2ecf20Sopenharmony_ci	size_t block_len, len;
508c2ecf20Sopenharmony_ci	int ret = 0;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	/* Suppress unneeded downloads */
538c2ecf20Sopenharmony_ci	if (wm8994->cur_fw == fw)
548c2ecf20Sopenharmony_ci		return 0;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	if (fw->size < 32) {
578c2ecf20Sopenharmony_ci		dev_err(component->dev, "%s: firmware too short (%zd bytes)\n",
588c2ecf20Sopenharmony_ci			name, fw->size);
598c2ecf20Sopenharmony_ci		goto err;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (memcmp(fw->data, "WMFW", 4) != 0) {
638c2ecf20Sopenharmony_ci		data32 = get_unaligned_be32(fw->data);
648c2ecf20Sopenharmony_ci		dev_err(component->dev, "%s: firmware has bad file magic %08x\n",
658c2ecf20Sopenharmony_ci			name, data32);
668c2ecf20Sopenharmony_ci		goto err;
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	len = get_unaligned_be32(fw->data + 4);
708c2ecf20Sopenharmony_ci	data32 = get_unaligned_be32(fw->data + 8);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if ((data32 >> 24) & 0xff) {
738c2ecf20Sopenharmony_ci		dev_err(component->dev, "%s: unsupported firmware version %d\n",
748c2ecf20Sopenharmony_ci			name, (data32 >> 24) & 0xff);
758c2ecf20Sopenharmony_ci		goto err;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci	if ((data32 & 0xffff) != 8958) {
788c2ecf20Sopenharmony_ci		dev_err(component->dev, "%s: unsupported target device %d\n",
798c2ecf20Sopenharmony_ci			name, data32 & 0xffff);
808c2ecf20Sopenharmony_ci		goto err;
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci	if (((data32 >> 16) & 0xff) != 0xc) {
838c2ecf20Sopenharmony_ci		dev_err(component->dev, "%s: unsupported target core %d\n",
848c2ecf20Sopenharmony_ci			name, (data32 >> 16) & 0xff);
858c2ecf20Sopenharmony_ci		goto err;
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (check) {
898c2ecf20Sopenharmony_ci		data64 = get_unaligned_be64(fw->data + 24);
908c2ecf20Sopenharmony_ci		dev_info(component->dev, "%s timestamp %llx\n",  name, data64);
918c2ecf20Sopenharmony_ci	} else {
928c2ecf20Sopenharmony_ci		snd_soc_component_write(component, 0x102, 0x2);
938c2ecf20Sopenharmony_ci		snd_soc_component_write(component, 0x900, 0x2);
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	data = fw->data + len;
978c2ecf20Sopenharmony_ci	len = fw->size - len;
988c2ecf20Sopenharmony_ci	while (len) {
998c2ecf20Sopenharmony_ci		if (len < 12) {
1008c2ecf20Sopenharmony_ci			dev_err(component->dev, "%s short data block of %zd\n",
1018c2ecf20Sopenharmony_ci				name, len);
1028c2ecf20Sopenharmony_ci			goto err;
1038c2ecf20Sopenharmony_ci		}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci		block_len = get_unaligned_be32(data + 4);
1068c2ecf20Sopenharmony_ci		if (block_len + 8 > len) {
1078c2ecf20Sopenharmony_ci			dev_err(component->dev, "%zd byte block longer than file\n",
1088c2ecf20Sopenharmony_ci				block_len);
1098c2ecf20Sopenharmony_ci			goto err;
1108c2ecf20Sopenharmony_ci		}
1118c2ecf20Sopenharmony_ci		if (block_len == 0) {
1128c2ecf20Sopenharmony_ci			dev_err(component->dev, "Zero length block\n");
1138c2ecf20Sopenharmony_ci			goto err;
1148c2ecf20Sopenharmony_ci		}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci		data32 = get_unaligned_be32(data);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci		switch ((data32 >> 24) & 0xff) {
1198c2ecf20Sopenharmony_ci		case WM_FW_BLOCK_INFO:
1208c2ecf20Sopenharmony_ci			/* Informational text */
1218c2ecf20Sopenharmony_ci			if (!check)
1228c2ecf20Sopenharmony_ci				break;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci			str = kzalloc(block_len + 1, GFP_KERNEL);
1258c2ecf20Sopenharmony_ci			if (str) {
1268c2ecf20Sopenharmony_ci				memcpy(str, data + 8, block_len);
1278c2ecf20Sopenharmony_ci				dev_info(component->dev, "%s: %s\n", name, str);
1288c2ecf20Sopenharmony_ci				kfree(str);
1298c2ecf20Sopenharmony_ci			} else {
1308c2ecf20Sopenharmony_ci				dev_err(component->dev, "Out of memory\n");
1318c2ecf20Sopenharmony_ci			}
1328c2ecf20Sopenharmony_ci			break;
1338c2ecf20Sopenharmony_ci		case WM_FW_BLOCK_PM:
1348c2ecf20Sopenharmony_ci		case WM_FW_BLOCK_X:
1358c2ecf20Sopenharmony_ci		case WM_FW_BLOCK_Y:
1368c2ecf20Sopenharmony_ci		case WM_FW_BLOCK_Z:
1378c2ecf20Sopenharmony_ci		case WM_FW_BLOCK_I:
1388c2ecf20Sopenharmony_ci		case WM_FW_BLOCK_A:
1398c2ecf20Sopenharmony_ci		case WM_FW_BLOCK_C:
1408c2ecf20Sopenharmony_ci			dev_dbg(component->dev, "%s: %zd bytes of %x@%x\n", name,
1418c2ecf20Sopenharmony_ci				block_len, (data32 >> 24) & 0xff,
1428c2ecf20Sopenharmony_ci				data32 & 0xffffff);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci			if (check)
1458c2ecf20Sopenharmony_ci				break;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci			data32 &= 0xffffff;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci			wm8994_bulk_write(wm8994->wm8994,
1508c2ecf20Sopenharmony_ci					  data32 & 0xffffff,
1518c2ecf20Sopenharmony_ci					  block_len / 2,
1528c2ecf20Sopenharmony_ci					  (void *)(data + 8));
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci			break;
1558c2ecf20Sopenharmony_ci		default:
1568c2ecf20Sopenharmony_ci			dev_warn(component->dev, "%s: unknown block type %d\n",
1578c2ecf20Sopenharmony_ci				 name, (data32 >> 24) & 0xff);
1588c2ecf20Sopenharmony_ci			break;
1598c2ecf20Sopenharmony_ci		}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		/* Round up to the next 32 bit word */
1628c2ecf20Sopenharmony_ci		block_len += block_len % 4;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci		data += block_len + 8;
1658c2ecf20Sopenharmony_ci		len -= block_len + 8;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (!check) {
1698c2ecf20Sopenharmony_ci		dev_dbg(component->dev, "%s: download done\n", name);
1708c2ecf20Sopenharmony_ci		wm8994->cur_fw = fw;
1718c2ecf20Sopenharmony_ci	} else {
1728c2ecf20Sopenharmony_ci		dev_info(component->dev, "%s: got firmware\n", name);
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	goto ok;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cierr:
1788c2ecf20Sopenharmony_ci	ret = -EINVAL;
1798c2ecf20Sopenharmony_ciok:
1808c2ecf20Sopenharmony_ci	if (!check) {
1818c2ecf20Sopenharmony_ci		snd_soc_component_write(component, 0x900, 0x0);
1828c2ecf20Sopenharmony_ci		snd_soc_component_write(component, 0x102, 0x0);
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	return ret;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic void wm8958_dsp_start_mbc(struct snd_soc_component *component, int path)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
1918c2ecf20Sopenharmony_ci	struct wm8994 *control = wm8994->wm8994;
1928c2ecf20Sopenharmony_ci	int i;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	/* If the DSP is already running then noop */
1958c2ecf20Sopenharmony_ci	if (snd_soc_component_read(component, WM8958_DSP2_PROGRAM) & WM8958_DSP2_ENA)
1968c2ecf20Sopenharmony_ci		return;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	/* If we have MBC firmware download it */
1998c2ecf20Sopenharmony_ci	if (wm8994->mbc)
2008c2ecf20Sopenharmony_ci		wm8958_dsp2_fw(component, "MBC", wm8994->mbc, false);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM,
2038c2ecf20Sopenharmony_ci			    WM8958_DSP2_ENA, WM8958_DSP2_ENA);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/* If we've got user supplied MBC settings use them */
2068c2ecf20Sopenharmony_ci	if (control->pdata.num_mbc_cfgs) {
2078c2ecf20Sopenharmony_ci		struct wm8958_mbc_cfg *cfg
2088c2ecf20Sopenharmony_ci			= &control->pdata.mbc_cfgs[wm8994->mbc_cfg];
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(cfg->coeff_regs); i++)
2118c2ecf20Sopenharmony_ci			snd_soc_component_write(component, i + WM8958_MBC_BAND_1_K_1,
2128c2ecf20Sopenharmony_ci				      cfg->coeff_regs[i]);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(cfg->cutoff_regs); i++)
2158c2ecf20Sopenharmony_ci			snd_soc_component_write(component,
2168c2ecf20Sopenharmony_ci				      i + WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1,
2178c2ecf20Sopenharmony_ci				      cfg->cutoff_regs[i]);
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/* Run the DSP */
2218c2ecf20Sopenharmony_ci	snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL,
2228c2ecf20Sopenharmony_ci		      WM8958_DSP2_RUNR);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/* And we're off! */
2258c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG,
2268c2ecf20Sopenharmony_ci			    WM8958_MBC_ENA |
2278c2ecf20Sopenharmony_ci			    WM8958_MBC_SEL_MASK,
2288c2ecf20Sopenharmony_ci			    path << WM8958_MBC_SEL_SHIFT |
2298c2ecf20Sopenharmony_ci			    WM8958_MBC_ENA);
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic void wm8958_dsp_start_vss(struct snd_soc_component *component, int path)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
2358c2ecf20Sopenharmony_ci	struct wm8994 *control = wm8994->wm8994;
2368c2ecf20Sopenharmony_ci	int i, ena;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (wm8994->mbc_vss)
2398c2ecf20Sopenharmony_ci		wm8958_dsp2_fw(component, "MBC+VSS", wm8994->mbc_vss, false);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM,
2428c2ecf20Sopenharmony_ci			    WM8958_DSP2_ENA, WM8958_DSP2_ENA);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	/* If we've got user supplied settings use them */
2458c2ecf20Sopenharmony_ci	if (control->pdata.num_mbc_cfgs) {
2468c2ecf20Sopenharmony_ci		struct wm8958_mbc_cfg *cfg
2478c2ecf20Sopenharmony_ci			= &control->pdata.mbc_cfgs[wm8994->mbc_cfg];
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(cfg->combined_regs); i++)
2508c2ecf20Sopenharmony_ci			snd_soc_component_write(component, i + 0x2800,
2518c2ecf20Sopenharmony_ci				      cfg->combined_regs[i]);
2528c2ecf20Sopenharmony_ci	}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (control->pdata.num_vss_cfgs) {
2558c2ecf20Sopenharmony_ci		struct wm8958_vss_cfg *cfg
2568c2ecf20Sopenharmony_ci			= &control->pdata.vss_cfgs[wm8994->vss_cfg];
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
2598c2ecf20Sopenharmony_ci			snd_soc_component_write(component, i + 0x2600, cfg->regs[i]);
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	if (control->pdata.num_vss_hpf_cfgs) {
2638c2ecf20Sopenharmony_ci		struct wm8958_vss_hpf_cfg *cfg
2648c2ecf20Sopenharmony_ci			= &control->pdata.vss_hpf_cfgs[wm8994->vss_hpf_cfg];
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
2678c2ecf20Sopenharmony_ci			snd_soc_component_write(component, i + 0x2400, cfg->regs[i]);
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	/* Run the DSP */
2718c2ecf20Sopenharmony_ci	snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL,
2728c2ecf20Sopenharmony_ci		      WM8958_DSP2_RUNR);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	/* Enable the algorithms we've selected */
2758c2ecf20Sopenharmony_ci	ena = 0;
2768c2ecf20Sopenharmony_ci	if (wm8994->mbc_ena[path])
2778c2ecf20Sopenharmony_ci		ena |= 0x8;
2788c2ecf20Sopenharmony_ci	if (wm8994->hpf2_ena[path])
2798c2ecf20Sopenharmony_ci		ena |= 0x4;
2808c2ecf20Sopenharmony_ci	if (wm8994->hpf1_ena[path])
2818c2ecf20Sopenharmony_ci		ena |= 0x2;
2828c2ecf20Sopenharmony_ci	if (wm8994->vss_ena[path])
2838c2ecf20Sopenharmony_ci		ena |= 0x1;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	snd_soc_component_write(component, 0x2201, ena);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	/* Switch the DSP into the data path */
2888c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG,
2898c2ecf20Sopenharmony_ci			    WM8958_MBC_SEL_MASK | WM8958_MBC_ENA,
2908c2ecf20Sopenharmony_ci			    path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA);
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic void wm8958_dsp_start_enh_eq(struct snd_soc_component *component, int path)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
2968c2ecf20Sopenharmony_ci	struct wm8994 *control = wm8994->wm8994;
2978c2ecf20Sopenharmony_ci	int i;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	wm8958_dsp2_fw(component, "ENH_EQ", wm8994->enh_eq, false);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM,
3028c2ecf20Sopenharmony_ci			    WM8958_DSP2_ENA, WM8958_DSP2_ENA);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	/* If we've got user supplied settings use them */
3058c2ecf20Sopenharmony_ci	if (control->pdata.num_enh_eq_cfgs) {
3068c2ecf20Sopenharmony_ci		struct wm8958_enh_eq_cfg *cfg
3078c2ecf20Sopenharmony_ci			= &control->pdata.enh_eq_cfgs[wm8994->enh_eq_cfg];
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)
3108c2ecf20Sopenharmony_ci			snd_soc_component_write(component, i + 0x2200,
3118c2ecf20Sopenharmony_ci				      cfg->regs[i]);
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	/* Run the DSP */
3158c2ecf20Sopenharmony_ci	snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL,
3168c2ecf20Sopenharmony_ci		      WM8958_DSP2_RUNR);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	/* Switch the DSP into the data path */
3198c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG,
3208c2ecf20Sopenharmony_ci			    WM8958_MBC_SEL_MASK | WM8958_MBC_ENA,
3218c2ecf20Sopenharmony_ci			    path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA);
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic void wm8958_dsp_apply(struct snd_soc_component *component, int path, int start)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
3278c2ecf20Sopenharmony_ci	int pwr_reg = snd_soc_component_read(component, WM8994_POWER_MANAGEMENT_5);
3288c2ecf20Sopenharmony_ci	int ena, reg, aif;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	switch (path) {
3318c2ecf20Sopenharmony_ci	case 0:
3328c2ecf20Sopenharmony_ci		pwr_reg &= (WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA);
3338c2ecf20Sopenharmony_ci		aif = 0;
3348c2ecf20Sopenharmony_ci		break;
3358c2ecf20Sopenharmony_ci	case 1:
3368c2ecf20Sopenharmony_ci		pwr_reg &= (WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);
3378c2ecf20Sopenharmony_ci		aif = 0;
3388c2ecf20Sopenharmony_ci		break;
3398c2ecf20Sopenharmony_ci	case 2:
3408c2ecf20Sopenharmony_ci		pwr_reg &= (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA);
3418c2ecf20Sopenharmony_ci		aif = 1;
3428c2ecf20Sopenharmony_ci		break;
3438c2ecf20Sopenharmony_ci	default:
3448c2ecf20Sopenharmony_ci		WARN(1, "Invalid path %d\n", path);
3458c2ecf20Sopenharmony_ci		return;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	/* Do we have both an active AIF and an active algorithm? */
3498c2ecf20Sopenharmony_ci	ena = wm8994->mbc_ena[path] || wm8994->vss_ena[path] ||
3508c2ecf20Sopenharmony_ci		wm8994->hpf1_ena[path] || wm8994->hpf2_ena[path] ||
3518c2ecf20Sopenharmony_ci		wm8994->enh_eq_ena[path];
3528c2ecf20Sopenharmony_ci	if (!pwr_reg)
3538c2ecf20Sopenharmony_ci		ena = 0;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	reg = snd_soc_component_read(component, WM8958_DSP2_PROGRAM);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	dev_dbg(component->dev, "DSP path %d %d startup: %d, power: %x, DSP: %x\n",
3588c2ecf20Sopenharmony_ci		path, wm8994->dsp_active, start, pwr_reg, reg);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	if (start && ena) {
3618c2ecf20Sopenharmony_ci		/* If the DSP is already running then noop */
3628c2ecf20Sopenharmony_ci		if (reg & WM8958_DSP2_ENA)
3638c2ecf20Sopenharmony_ci			return;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci		/* If either AIFnCLK is not yet enabled postpone */
3668c2ecf20Sopenharmony_ci		if (!(snd_soc_component_read(component, WM8994_AIF1_CLOCKING_1)
3678c2ecf20Sopenharmony_ci		      & WM8994_AIF1CLK_ENA_MASK) &&
3688c2ecf20Sopenharmony_ci		    !(snd_soc_component_read(component, WM8994_AIF2_CLOCKING_1)
3698c2ecf20Sopenharmony_ci		      & WM8994_AIF2CLK_ENA_MASK))
3708c2ecf20Sopenharmony_ci			return;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci		/* Switch the clock over to the appropriate AIF */
3738c2ecf20Sopenharmony_ci		snd_soc_component_update_bits(component, WM8994_CLOCKING_1,
3748c2ecf20Sopenharmony_ci				    WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA,
3758c2ecf20Sopenharmony_ci				    aif << WM8958_DSP2CLK_SRC_SHIFT |
3768c2ecf20Sopenharmony_ci				    WM8958_DSP2CLK_ENA);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci		if (wm8994->enh_eq_ena[path])
3798c2ecf20Sopenharmony_ci			wm8958_dsp_start_enh_eq(component, path);
3808c2ecf20Sopenharmony_ci		else if (wm8994->vss_ena[path] || wm8994->hpf1_ena[path] ||
3818c2ecf20Sopenharmony_ci		    wm8994->hpf2_ena[path])
3828c2ecf20Sopenharmony_ci			wm8958_dsp_start_vss(component, path);
3838c2ecf20Sopenharmony_ci		else if (wm8994->mbc_ena[path])
3848c2ecf20Sopenharmony_ci			wm8958_dsp_start_mbc(component, path);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci		wm8994->dsp_active = path;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci		dev_dbg(component->dev, "DSP running in path %d\n", path);
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	if (!start && wm8994->dsp_active == path) {
3928c2ecf20Sopenharmony_ci		/* If the DSP is already stopped then noop */
3938c2ecf20Sopenharmony_ci		if (!(reg & WM8958_DSP2_ENA))
3948c2ecf20Sopenharmony_ci			return;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci		snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG,
3978c2ecf20Sopenharmony_ci				    WM8958_MBC_ENA, 0);
3988c2ecf20Sopenharmony_ci		snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL,
3998c2ecf20Sopenharmony_ci			      WM8958_DSP2_STOP);
4008c2ecf20Sopenharmony_ci		snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM,
4018c2ecf20Sopenharmony_ci				    WM8958_DSP2_ENA, 0);
4028c2ecf20Sopenharmony_ci		snd_soc_component_update_bits(component, WM8994_CLOCKING_1,
4038c2ecf20Sopenharmony_ci				    WM8958_DSP2CLK_ENA, 0);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci		wm8994->dsp_active = -1;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci		dev_dbg(component->dev, "DSP stopped\n");
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ciint wm8958_aif_ev(struct snd_soc_dapm_widget *w,
4128c2ecf20Sopenharmony_ci		  struct snd_kcontrol *kcontrol, int event)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
4158c2ecf20Sopenharmony_ci	struct wm8994 *control = dev_get_drvdata(component->dev->parent);
4168c2ecf20Sopenharmony_ci	int i;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	if (control->type != WM8958)
4198c2ecf20Sopenharmony_ci		return 0;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	switch (event) {
4228c2ecf20Sopenharmony_ci	case SND_SOC_DAPM_POST_PMU:
4238c2ecf20Sopenharmony_ci	case SND_SOC_DAPM_PRE_PMU:
4248c2ecf20Sopenharmony_ci		for (i = 0; i < 3; i++)
4258c2ecf20Sopenharmony_ci			wm8958_dsp_apply(component, i, 1);
4268c2ecf20Sopenharmony_ci		break;
4278c2ecf20Sopenharmony_ci	case SND_SOC_DAPM_POST_PMD:
4288c2ecf20Sopenharmony_ci	case SND_SOC_DAPM_PRE_PMD:
4298c2ecf20Sopenharmony_ci		for (i = 0; i < 3; i++)
4308c2ecf20Sopenharmony_ci			wm8958_dsp_apply(component, i, 0);
4318c2ecf20Sopenharmony_ci		break;
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	return 0;
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci/* Check if DSP2 is in use on another AIF */
4388c2ecf20Sopenharmony_cistatic int wm8958_dsp2_busy(struct wm8994_priv *wm8994, int aif)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	int i;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(wm8994->mbc_ena); i++) {
4438c2ecf20Sopenharmony_ci		if (i == aif)
4448c2ecf20Sopenharmony_ci			continue;
4458c2ecf20Sopenharmony_ci		if (wm8994->mbc_ena[i] || wm8994->vss_ena[i] ||
4468c2ecf20Sopenharmony_ci		    wm8994->hpf1_ena[i] || wm8994->hpf2_ena[i])
4478c2ecf20Sopenharmony_ci			return 1;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	return 0;
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol,
4548c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
4578c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
4588c2ecf20Sopenharmony_ci	struct wm8994 *control = wm8994->wm8994;
4598c2ecf20Sopenharmony_ci	int value = ucontrol->value.enumerated.item[0];
4608c2ecf20Sopenharmony_ci	int reg;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	/* Don't allow on the fly reconfiguration */
4638c2ecf20Sopenharmony_ci	reg = snd_soc_component_read(component, WM8994_CLOCKING_1);
4648c2ecf20Sopenharmony_ci	if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
4658c2ecf20Sopenharmony_ci		return -EBUSY;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	if (value >= control->pdata.num_mbc_cfgs)
4688c2ecf20Sopenharmony_ci		return -EINVAL;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	wm8994->mbc_cfg = value;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	return 0;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic int wm8958_get_mbc_enum(struct snd_kcontrol *kcontrol,
4768c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
4798c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	return 0;
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cistatic int wm8958_mbc_info(struct snd_kcontrol *kcontrol,
4878c2ecf20Sopenharmony_ci			   struct snd_ctl_elem_info *uinfo)
4888c2ecf20Sopenharmony_ci{
4898c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
4908c2ecf20Sopenharmony_ci	uinfo->count = 1;
4918c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
4928c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 1;
4938c2ecf20Sopenharmony_ci	return 0;
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_cistatic int wm8958_mbc_get(struct snd_kcontrol *kcontrol,
4978c2ecf20Sopenharmony_ci			  struct snd_ctl_elem_value *ucontrol)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	int mbc = kcontrol->private_value;
5008c2ecf20Sopenharmony_ci	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
5018c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc];
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	return 0;
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cistatic int wm8958_mbc_put(struct snd_kcontrol *kcontrol,
5098c2ecf20Sopenharmony_ci			  struct snd_ctl_elem_value *ucontrol)
5108c2ecf20Sopenharmony_ci{
5118c2ecf20Sopenharmony_ci	int mbc = kcontrol->private_value;
5128c2ecf20Sopenharmony_ci	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
5138c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	if (wm8994->mbc_ena[mbc] == ucontrol->value.integer.value[0])
5168c2ecf20Sopenharmony_ci		return 0;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	if (ucontrol->value.integer.value[0] > 1)
5198c2ecf20Sopenharmony_ci		return -EINVAL;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (wm8958_dsp2_busy(wm8994, mbc)) {
5228c2ecf20Sopenharmony_ci		dev_dbg(component->dev, "DSP2 active on %d already\n", mbc);
5238c2ecf20Sopenharmony_ci		return -EBUSY;
5248c2ecf20Sopenharmony_ci	}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	if (wm8994->enh_eq_ena[mbc])
5278c2ecf20Sopenharmony_ci		return -EBUSY;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0];
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	wm8958_dsp_apply(component, mbc, wm8994->mbc_ena[mbc]);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	return 1;
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci#define WM8958_MBC_SWITCH(xname, xval) {\
5378c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
5388c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
5398c2ecf20Sopenharmony_ci	.info = wm8958_mbc_info, \
5408c2ecf20Sopenharmony_ci	.get = wm8958_mbc_get, .put = wm8958_mbc_put, \
5418c2ecf20Sopenharmony_ci	.private_value = xval }
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_cistatic int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol,
5448c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
5458c2ecf20Sopenharmony_ci{
5468c2ecf20Sopenharmony_ci	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
5478c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
5488c2ecf20Sopenharmony_ci	struct wm8994 *control = wm8994->wm8994;
5498c2ecf20Sopenharmony_ci	int value = ucontrol->value.enumerated.item[0];
5508c2ecf20Sopenharmony_ci	int reg;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	/* Don't allow on the fly reconfiguration */
5538c2ecf20Sopenharmony_ci	reg = snd_soc_component_read(component, WM8994_CLOCKING_1);
5548c2ecf20Sopenharmony_ci	if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
5558c2ecf20Sopenharmony_ci		return -EBUSY;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	if (value >= control->pdata.num_vss_cfgs)
5588c2ecf20Sopenharmony_ci		return -EINVAL;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	wm8994->vss_cfg = value;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	return 0;
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_cistatic int wm8958_get_vss_enum(struct snd_kcontrol *kcontrol,
5668c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_value *ucontrol)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
5698c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = wm8994->vss_cfg;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	return 0;
5748c2ecf20Sopenharmony_ci}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_cistatic int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol,
5778c2ecf20Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
5808c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
5818c2ecf20Sopenharmony_ci	struct wm8994 *control = wm8994->wm8994;
5828c2ecf20Sopenharmony_ci	int value = ucontrol->value.enumerated.item[0];
5838c2ecf20Sopenharmony_ci	int reg;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	/* Don't allow on the fly reconfiguration */
5868c2ecf20Sopenharmony_ci	reg = snd_soc_component_read(component, WM8994_CLOCKING_1);
5878c2ecf20Sopenharmony_ci	if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
5888c2ecf20Sopenharmony_ci		return -EBUSY;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	if (value >= control->pdata.num_vss_hpf_cfgs)
5918c2ecf20Sopenharmony_ci		return -EINVAL;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	wm8994->vss_hpf_cfg = value;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	return 0;
5968c2ecf20Sopenharmony_ci}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_cistatic int wm8958_get_vss_hpf_enum(struct snd_kcontrol *kcontrol,
5998c2ecf20Sopenharmony_ci				   struct snd_ctl_elem_value *ucontrol)
6008c2ecf20Sopenharmony_ci{
6018c2ecf20Sopenharmony_ci	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
6028c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = wm8994->vss_hpf_cfg;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	return 0;
6078c2ecf20Sopenharmony_ci}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_cistatic int wm8958_vss_info(struct snd_kcontrol *kcontrol,
6108c2ecf20Sopenharmony_ci			   struct snd_ctl_elem_info *uinfo)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
6138c2ecf20Sopenharmony_ci	uinfo->count = 1;
6148c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
6158c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 1;
6168c2ecf20Sopenharmony_ci	return 0;
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic int wm8958_vss_get(struct snd_kcontrol *kcontrol,
6208c2ecf20Sopenharmony_ci			  struct snd_ctl_elem_value *ucontrol)
6218c2ecf20Sopenharmony_ci{
6228c2ecf20Sopenharmony_ci	int vss = kcontrol->private_value;
6238c2ecf20Sopenharmony_ci	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
6248c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = wm8994->vss_ena[vss];
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	return 0;
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_cistatic int wm8958_vss_put(struct snd_kcontrol *kcontrol,
6328c2ecf20Sopenharmony_ci			  struct snd_ctl_elem_value *ucontrol)
6338c2ecf20Sopenharmony_ci{
6348c2ecf20Sopenharmony_ci	int vss = kcontrol->private_value;
6358c2ecf20Sopenharmony_ci	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
6368c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	if (wm8994->vss_ena[vss] == ucontrol->value.integer.value[0])
6398c2ecf20Sopenharmony_ci		return 0;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	if (ucontrol->value.integer.value[0] > 1)
6428c2ecf20Sopenharmony_ci		return -EINVAL;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	if (!wm8994->mbc_vss)
6458c2ecf20Sopenharmony_ci		return -ENODEV;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	if (wm8958_dsp2_busy(wm8994, vss)) {
6488c2ecf20Sopenharmony_ci		dev_dbg(component->dev, "DSP2 active on %d already\n", vss);
6498c2ecf20Sopenharmony_ci		return -EBUSY;
6508c2ecf20Sopenharmony_ci	}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	if (wm8994->enh_eq_ena[vss])
6538c2ecf20Sopenharmony_ci		return -EBUSY;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	wm8994->vss_ena[vss] = ucontrol->value.integer.value[0];
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	wm8958_dsp_apply(component, vss, wm8994->vss_ena[vss]);
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	return 1;
6608c2ecf20Sopenharmony_ci}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci#define WM8958_VSS_SWITCH(xname, xval) {\
6648c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
6658c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
6668c2ecf20Sopenharmony_ci	.info = wm8958_vss_info, \
6678c2ecf20Sopenharmony_ci	.get = wm8958_vss_get, .put = wm8958_vss_put, \
6688c2ecf20Sopenharmony_ci	.private_value = xval }
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_cistatic int wm8958_hpf_info(struct snd_kcontrol *kcontrol,
6718c2ecf20Sopenharmony_ci			   struct snd_ctl_elem_info *uinfo)
6728c2ecf20Sopenharmony_ci{
6738c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
6748c2ecf20Sopenharmony_ci	uinfo->count = 1;
6758c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
6768c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 1;
6778c2ecf20Sopenharmony_ci	return 0;
6788c2ecf20Sopenharmony_ci}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_cistatic int wm8958_hpf_get(struct snd_kcontrol *kcontrol,
6818c2ecf20Sopenharmony_ci			  struct snd_ctl_elem_value *ucontrol)
6828c2ecf20Sopenharmony_ci{
6838c2ecf20Sopenharmony_ci	int hpf = kcontrol->private_value;
6848c2ecf20Sopenharmony_ci	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
6858c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	if (hpf < 3)
6888c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[0] = wm8994->hpf1_ena[hpf % 3];
6898c2ecf20Sopenharmony_ci	else
6908c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[0] = wm8994->hpf2_ena[hpf % 3];
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	return 0;
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_cistatic int wm8958_hpf_put(struct snd_kcontrol *kcontrol,
6968c2ecf20Sopenharmony_ci			  struct snd_ctl_elem_value *ucontrol)
6978c2ecf20Sopenharmony_ci{
6988c2ecf20Sopenharmony_ci	int hpf = kcontrol->private_value;
6998c2ecf20Sopenharmony_ci	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
7008c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	if (hpf < 3) {
7038c2ecf20Sopenharmony_ci		if (wm8994->hpf1_ena[hpf % 3] ==
7048c2ecf20Sopenharmony_ci		    ucontrol->value.integer.value[0])
7058c2ecf20Sopenharmony_ci			return 0;
7068c2ecf20Sopenharmony_ci	} else {
7078c2ecf20Sopenharmony_ci		if (wm8994->hpf2_ena[hpf % 3] ==
7088c2ecf20Sopenharmony_ci		    ucontrol->value.integer.value[0])
7098c2ecf20Sopenharmony_ci			return 0;
7108c2ecf20Sopenharmony_ci	}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	if (ucontrol->value.integer.value[0] > 1)
7138c2ecf20Sopenharmony_ci		return -EINVAL;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	if (!wm8994->mbc_vss)
7168c2ecf20Sopenharmony_ci		return -ENODEV;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	if (wm8958_dsp2_busy(wm8994, hpf % 3)) {
7198c2ecf20Sopenharmony_ci		dev_dbg(component->dev, "DSP2 active on %d already\n", hpf);
7208c2ecf20Sopenharmony_ci		return -EBUSY;
7218c2ecf20Sopenharmony_ci	}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	if (wm8994->enh_eq_ena[hpf % 3])
7248c2ecf20Sopenharmony_ci		return -EBUSY;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	if (hpf < 3)
7278c2ecf20Sopenharmony_ci		wm8994->hpf1_ena[hpf % 3] = ucontrol->value.integer.value[0];
7288c2ecf20Sopenharmony_ci	else
7298c2ecf20Sopenharmony_ci		wm8994->hpf2_ena[hpf % 3] = ucontrol->value.integer.value[0];
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	wm8958_dsp_apply(component, hpf % 3, ucontrol->value.integer.value[0]);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	return 1;
7348c2ecf20Sopenharmony_ci}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci#define WM8958_HPF_SWITCH(xname, xval) {\
7378c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
7388c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
7398c2ecf20Sopenharmony_ci	.info = wm8958_hpf_info, \
7408c2ecf20Sopenharmony_ci	.get = wm8958_hpf_get, .put = wm8958_hpf_put, \
7418c2ecf20Sopenharmony_ci	.private_value = xval }
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_cistatic int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol,
7448c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_value *ucontrol)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
7478c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
7488c2ecf20Sopenharmony_ci	struct wm8994 *control = wm8994->wm8994;
7498c2ecf20Sopenharmony_ci	int value = ucontrol->value.enumerated.item[0];
7508c2ecf20Sopenharmony_ci	int reg;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	/* Don't allow on the fly reconfiguration */
7538c2ecf20Sopenharmony_ci	reg = snd_soc_component_read(component, WM8994_CLOCKING_1);
7548c2ecf20Sopenharmony_ci	if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
7558c2ecf20Sopenharmony_ci		return -EBUSY;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	if (value >= control->pdata.num_enh_eq_cfgs)
7588c2ecf20Sopenharmony_ci		return -EINVAL;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	wm8994->enh_eq_cfg = value;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	return 0;
7638c2ecf20Sopenharmony_ci}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_cistatic int wm8958_get_enh_eq_enum(struct snd_kcontrol *kcontrol,
7668c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_value *ucontrol)
7678c2ecf20Sopenharmony_ci{
7688c2ecf20Sopenharmony_ci	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
7698c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] = wm8994->enh_eq_cfg;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	return 0;
7748c2ecf20Sopenharmony_ci}
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_cistatic int wm8958_enh_eq_info(struct snd_kcontrol *kcontrol,
7778c2ecf20Sopenharmony_ci			   struct snd_ctl_elem_info *uinfo)
7788c2ecf20Sopenharmony_ci{
7798c2ecf20Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
7808c2ecf20Sopenharmony_ci	uinfo->count = 1;
7818c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
7828c2ecf20Sopenharmony_ci	uinfo->value.integer.max = 1;
7838c2ecf20Sopenharmony_ci	return 0;
7848c2ecf20Sopenharmony_ci}
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_cistatic int wm8958_enh_eq_get(struct snd_kcontrol *kcontrol,
7878c2ecf20Sopenharmony_ci			  struct snd_ctl_elem_value *ucontrol)
7888c2ecf20Sopenharmony_ci{
7898c2ecf20Sopenharmony_ci	int eq = kcontrol->private_value;
7908c2ecf20Sopenharmony_ci	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
7918c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = wm8994->enh_eq_ena[eq];
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	return 0;
7968c2ecf20Sopenharmony_ci}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_cistatic int wm8958_enh_eq_put(struct snd_kcontrol *kcontrol,
7998c2ecf20Sopenharmony_ci			  struct snd_ctl_elem_value *ucontrol)
8008c2ecf20Sopenharmony_ci{
8018c2ecf20Sopenharmony_ci	int eq = kcontrol->private_value;
8028c2ecf20Sopenharmony_ci	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
8038c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	if (wm8994->enh_eq_ena[eq] == ucontrol->value.integer.value[0])
8068c2ecf20Sopenharmony_ci		return 0;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	if (ucontrol->value.integer.value[0] > 1)
8098c2ecf20Sopenharmony_ci		return -EINVAL;
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	if (!wm8994->enh_eq)
8128c2ecf20Sopenharmony_ci		return -ENODEV;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	if (wm8958_dsp2_busy(wm8994, eq)) {
8158c2ecf20Sopenharmony_ci		dev_dbg(component->dev, "DSP2 active on %d already\n", eq);
8168c2ecf20Sopenharmony_ci		return -EBUSY;
8178c2ecf20Sopenharmony_ci	}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	if (wm8994->mbc_ena[eq] || wm8994->vss_ena[eq] ||
8208c2ecf20Sopenharmony_ci	    wm8994->hpf1_ena[eq] || wm8994->hpf2_ena[eq])
8218c2ecf20Sopenharmony_ci		return -EBUSY;
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	wm8994->enh_eq_ena[eq] = ucontrol->value.integer.value[0];
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	wm8958_dsp_apply(component, eq, ucontrol->value.integer.value[0]);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	return 1;
8288c2ecf20Sopenharmony_ci}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci#define WM8958_ENH_EQ_SWITCH(xname, xval) {\
8318c2ecf20Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
8328c2ecf20Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
8338c2ecf20Sopenharmony_ci	.info = wm8958_enh_eq_info, \
8348c2ecf20Sopenharmony_ci	.get = wm8958_enh_eq_get, .put = wm8958_enh_eq_put, \
8358c2ecf20Sopenharmony_ci	.private_value = xval }
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm8958_mbc_snd_controls[] = {
8388c2ecf20Sopenharmony_ciWM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0),
8398c2ecf20Sopenharmony_ciWM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1),
8408c2ecf20Sopenharmony_ciWM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2),
8418c2ecf20Sopenharmony_ci};
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm8958_vss_snd_controls[] = {
8448c2ecf20Sopenharmony_ciWM8958_VSS_SWITCH("AIF1DAC1 VSS Switch", 0),
8458c2ecf20Sopenharmony_ciWM8958_VSS_SWITCH("AIF1DAC2 VSS Switch", 1),
8468c2ecf20Sopenharmony_ciWM8958_VSS_SWITCH("AIF2DAC VSS Switch", 2),
8478c2ecf20Sopenharmony_ciWM8958_HPF_SWITCH("AIF1DAC1 HPF1 Switch", 0),
8488c2ecf20Sopenharmony_ciWM8958_HPF_SWITCH("AIF1DAC2 HPF1 Switch", 1),
8498c2ecf20Sopenharmony_ciWM8958_HPF_SWITCH("AIF2DAC HPF1 Switch", 2),
8508c2ecf20Sopenharmony_ciWM8958_HPF_SWITCH("AIF1DAC1 HPF2 Switch", 3),
8518c2ecf20Sopenharmony_ciWM8958_HPF_SWITCH("AIF1DAC2 HPF2 Switch", 4),
8528c2ecf20Sopenharmony_ciWM8958_HPF_SWITCH("AIF2DAC HPF2 Switch", 5),
8538c2ecf20Sopenharmony_ci};
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new wm8958_enh_eq_snd_controls[] = {
8568c2ecf20Sopenharmony_ciWM8958_ENH_EQ_SWITCH("AIF1DAC1 Enhanced EQ Switch", 0),
8578c2ecf20Sopenharmony_ciWM8958_ENH_EQ_SWITCH("AIF1DAC2 Enhanced EQ Switch", 1),
8588c2ecf20Sopenharmony_ciWM8958_ENH_EQ_SWITCH("AIF2DAC Enhanced EQ Switch", 2),
8598c2ecf20Sopenharmony_ci};
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_cistatic void wm8958_enh_eq_loaded(const struct firmware *fw, void *context)
8628c2ecf20Sopenharmony_ci{
8638c2ecf20Sopenharmony_ci	struct snd_soc_component *component = context;
8648c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	if (fw && (wm8958_dsp2_fw(component, "ENH_EQ", fw, true) == 0)) {
8678c2ecf20Sopenharmony_ci		mutex_lock(&wm8994->fw_lock);
8688c2ecf20Sopenharmony_ci		wm8994->enh_eq = fw;
8698c2ecf20Sopenharmony_ci		mutex_unlock(&wm8994->fw_lock);
8708c2ecf20Sopenharmony_ci	}
8718c2ecf20Sopenharmony_ci}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_cistatic void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context)
8748c2ecf20Sopenharmony_ci{
8758c2ecf20Sopenharmony_ci	struct snd_soc_component *component = context;
8768c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	if (fw && (wm8958_dsp2_fw(component, "MBC+VSS", fw, true) == 0)) {
8798c2ecf20Sopenharmony_ci		mutex_lock(&wm8994->fw_lock);
8808c2ecf20Sopenharmony_ci		wm8994->mbc_vss = fw;
8818c2ecf20Sopenharmony_ci		mutex_unlock(&wm8994->fw_lock);
8828c2ecf20Sopenharmony_ci	}
8838c2ecf20Sopenharmony_ci}
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_cistatic void wm8958_mbc_loaded(const struct firmware *fw, void *context)
8868c2ecf20Sopenharmony_ci{
8878c2ecf20Sopenharmony_ci	struct snd_soc_component *component = context;
8888c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	if (fw && (wm8958_dsp2_fw(component, "MBC", fw, true) == 0)) {
8918c2ecf20Sopenharmony_ci		mutex_lock(&wm8994->fw_lock);
8928c2ecf20Sopenharmony_ci		wm8994->mbc = fw;
8938c2ecf20Sopenharmony_ci		mutex_unlock(&wm8994->fw_lock);
8948c2ecf20Sopenharmony_ci	}
8958c2ecf20Sopenharmony_ci}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_civoid wm8958_dsp2_init(struct snd_soc_component *component)
8988c2ecf20Sopenharmony_ci{
8998c2ecf20Sopenharmony_ci	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);
9008c2ecf20Sopenharmony_ci	struct wm8994 *control = wm8994->wm8994;
9018c2ecf20Sopenharmony_ci	struct wm8994_pdata *pdata = &control->pdata;
9028c2ecf20Sopenharmony_ci	int ret, i;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	wm8994->dsp_active = -1;
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci	snd_soc_add_component_controls(component, wm8958_mbc_snd_controls,
9078c2ecf20Sopenharmony_ci			     ARRAY_SIZE(wm8958_mbc_snd_controls));
9088c2ecf20Sopenharmony_ci	snd_soc_add_component_controls(component, wm8958_vss_snd_controls,
9098c2ecf20Sopenharmony_ci			     ARRAY_SIZE(wm8958_vss_snd_controls));
9108c2ecf20Sopenharmony_ci	snd_soc_add_component_controls(component, wm8958_enh_eq_snd_controls,
9118c2ecf20Sopenharmony_ci			     ARRAY_SIZE(wm8958_enh_eq_snd_controls));
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	/* We don't *require* firmware and don't want to delay boot */
9158c2ecf20Sopenharmony_ci	request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
9168c2ecf20Sopenharmony_ci				"wm8958_mbc.wfw", component->dev, GFP_KERNEL,
9178c2ecf20Sopenharmony_ci				component, wm8958_mbc_loaded);
9188c2ecf20Sopenharmony_ci	request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
9198c2ecf20Sopenharmony_ci				"wm8958_mbc_vss.wfw", component->dev, GFP_KERNEL,
9208c2ecf20Sopenharmony_ci				component, wm8958_mbc_vss_loaded);
9218c2ecf20Sopenharmony_ci	request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
9228c2ecf20Sopenharmony_ci				"wm8958_enh_eq.wfw", component->dev, GFP_KERNEL,
9238c2ecf20Sopenharmony_ci				component, wm8958_enh_eq_loaded);
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	if (pdata->num_mbc_cfgs) {
9268c2ecf20Sopenharmony_ci		struct snd_kcontrol_new control[] = {
9278c2ecf20Sopenharmony_ci			SOC_ENUM_EXT("MBC Mode", wm8994->mbc_enum,
9288c2ecf20Sopenharmony_ci				     wm8958_get_mbc_enum, wm8958_put_mbc_enum),
9298c2ecf20Sopenharmony_ci		};
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci		/* We need an array of texts for the enum API */
9328c2ecf20Sopenharmony_ci		wm8994->mbc_texts = kmalloc_array(pdata->num_mbc_cfgs,
9338c2ecf20Sopenharmony_ci						  sizeof(char *),
9348c2ecf20Sopenharmony_ci						  GFP_KERNEL);
9358c2ecf20Sopenharmony_ci		if (!wm8994->mbc_texts)
9368c2ecf20Sopenharmony_ci			return;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci		for (i = 0; i < pdata->num_mbc_cfgs; i++)
9398c2ecf20Sopenharmony_ci			wm8994->mbc_texts[i] = pdata->mbc_cfgs[i].name;
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci		wm8994->mbc_enum.items = pdata->num_mbc_cfgs;
9428c2ecf20Sopenharmony_ci		wm8994->mbc_enum.texts = wm8994->mbc_texts;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci		ret = snd_soc_add_component_controls(wm8994->hubs.component,
9458c2ecf20Sopenharmony_ci						 control, 1);
9468c2ecf20Sopenharmony_ci		if (ret != 0)
9478c2ecf20Sopenharmony_ci			dev_err(wm8994->hubs.component->dev,
9488c2ecf20Sopenharmony_ci				"Failed to add MBC mode controls: %d\n", ret);
9498c2ecf20Sopenharmony_ci	}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	if (pdata->num_vss_cfgs) {
9528c2ecf20Sopenharmony_ci		struct snd_kcontrol_new control[] = {
9538c2ecf20Sopenharmony_ci			SOC_ENUM_EXT("VSS Mode", wm8994->vss_enum,
9548c2ecf20Sopenharmony_ci				     wm8958_get_vss_enum, wm8958_put_vss_enum),
9558c2ecf20Sopenharmony_ci		};
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci		/* We need an array of texts for the enum API */
9588c2ecf20Sopenharmony_ci		wm8994->vss_texts = kmalloc_array(pdata->num_vss_cfgs,
9598c2ecf20Sopenharmony_ci						  sizeof(char *),
9608c2ecf20Sopenharmony_ci						  GFP_KERNEL);
9618c2ecf20Sopenharmony_ci		if (!wm8994->vss_texts)
9628c2ecf20Sopenharmony_ci			return;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci		for (i = 0; i < pdata->num_vss_cfgs; i++)
9658c2ecf20Sopenharmony_ci			wm8994->vss_texts[i] = pdata->vss_cfgs[i].name;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci		wm8994->vss_enum.items = pdata->num_vss_cfgs;
9688c2ecf20Sopenharmony_ci		wm8994->vss_enum.texts = wm8994->vss_texts;
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci		ret = snd_soc_add_component_controls(wm8994->hubs.component,
9718c2ecf20Sopenharmony_ci						 control, 1);
9728c2ecf20Sopenharmony_ci		if (ret != 0)
9738c2ecf20Sopenharmony_ci			dev_err(wm8994->hubs.component->dev,
9748c2ecf20Sopenharmony_ci				"Failed to add VSS mode controls: %d\n", ret);
9758c2ecf20Sopenharmony_ci	}
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	if (pdata->num_vss_hpf_cfgs) {
9788c2ecf20Sopenharmony_ci		struct snd_kcontrol_new control[] = {
9798c2ecf20Sopenharmony_ci			SOC_ENUM_EXT("VSS HPF Mode", wm8994->vss_hpf_enum,
9808c2ecf20Sopenharmony_ci				     wm8958_get_vss_hpf_enum,
9818c2ecf20Sopenharmony_ci				     wm8958_put_vss_hpf_enum),
9828c2ecf20Sopenharmony_ci		};
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci		/* We need an array of texts for the enum API */
9858c2ecf20Sopenharmony_ci		wm8994->vss_hpf_texts = kmalloc_array(pdata->num_vss_hpf_cfgs,
9868c2ecf20Sopenharmony_ci						      sizeof(char *),
9878c2ecf20Sopenharmony_ci						      GFP_KERNEL);
9888c2ecf20Sopenharmony_ci		if (!wm8994->vss_hpf_texts)
9898c2ecf20Sopenharmony_ci			return;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci		for (i = 0; i < pdata->num_vss_hpf_cfgs; i++)
9928c2ecf20Sopenharmony_ci			wm8994->vss_hpf_texts[i] = pdata->vss_hpf_cfgs[i].name;
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci		wm8994->vss_hpf_enum.items = pdata->num_vss_hpf_cfgs;
9958c2ecf20Sopenharmony_ci		wm8994->vss_hpf_enum.texts = wm8994->vss_hpf_texts;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci		ret = snd_soc_add_component_controls(wm8994->hubs.component,
9988c2ecf20Sopenharmony_ci						 control, 1);
9998c2ecf20Sopenharmony_ci		if (ret != 0)
10008c2ecf20Sopenharmony_ci			dev_err(wm8994->hubs.component->dev,
10018c2ecf20Sopenharmony_ci				"Failed to add VSS HPFmode controls: %d\n",
10028c2ecf20Sopenharmony_ci				ret);
10038c2ecf20Sopenharmony_ci	}
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	if (pdata->num_enh_eq_cfgs) {
10068c2ecf20Sopenharmony_ci		struct snd_kcontrol_new control[] = {
10078c2ecf20Sopenharmony_ci			SOC_ENUM_EXT("Enhanced EQ Mode", wm8994->enh_eq_enum,
10088c2ecf20Sopenharmony_ci				     wm8958_get_enh_eq_enum,
10098c2ecf20Sopenharmony_ci				     wm8958_put_enh_eq_enum),
10108c2ecf20Sopenharmony_ci		};
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci		/* We need an array of texts for the enum API */
10138c2ecf20Sopenharmony_ci		wm8994->enh_eq_texts = kmalloc_array(pdata->num_enh_eq_cfgs,
10148c2ecf20Sopenharmony_ci						     sizeof(char *),
10158c2ecf20Sopenharmony_ci						     GFP_KERNEL);
10168c2ecf20Sopenharmony_ci		if (!wm8994->enh_eq_texts)
10178c2ecf20Sopenharmony_ci			return;
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci		for (i = 0; i < pdata->num_enh_eq_cfgs; i++)
10208c2ecf20Sopenharmony_ci			wm8994->enh_eq_texts[i] = pdata->enh_eq_cfgs[i].name;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci		wm8994->enh_eq_enum.items = pdata->num_enh_eq_cfgs;
10238c2ecf20Sopenharmony_ci		wm8994->enh_eq_enum.texts = wm8994->enh_eq_texts;
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci		ret = snd_soc_add_component_controls(wm8994->hubs.component,
10268c2ecf20Sopenharmony_ci						 control, 1);
10278c2ecf20Sopenharmony_ci		if (ret != 0)
10288c2ecf20Sopenharmony_ci			dev_err(wm8994->hubs.component->dev,
10298c2ecf20Sopenharmony_ci				"Failed to add enhanced EQ controls: %d\n",
10308c2ecf20Sopenharmony_ci				ret);
10318c2ecf20Sopenharmony_ci	}
10328c2ecf20Sopenharmony_ci}
1033