brintos

brintos / linux-shallow public Read only

0
0
Text · 28.3 KiB · d08419b Raw
1033 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * wm8958-dsp2.c  --  WM8958 DSP2 support4 *5 * Copyright 2011 Wolfson Microelectronics plc6 *7 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>8 */9 10#include <linux/module.h>11#include <linux/moduleparam.h>12#include <linux/init.h>13#include <linux/delay.h>14#include <linux/pm.h>15#include <linux/i2c.h>16#include <linux/platform_device.h>17#include <linux/slab.h>18#include <sound/soc.h>19#include <sound/initval.h>20#include <sound/tlv.h>21#include <trace/events/asoc.h>22 23#include <linux/mfd/wm8994/core.h>24#include <linux/mfd/wm8994/registers.h>25#include <linux/mfd/wm8994/pdata.h>26#include <linux/mfd/wm8994/gpio.h>27 28#include <linux/unaligned.h>29 30#include "wm8994.h"31 32#define WM_FW_BLOCK_INFO 0xff33#define WM_FW_BLOCK_PM   0x0034#define WM_FW_BLOCK_X    0x0135#define WM_FW_BLOCK_Y    0x0236#define WM_FW_BLOCK_Z    0x0337#define WM_FW_BLOCK_I    0x0638#define WM_FW_BLOCK_A    0x0839#define WM_FW_BLOCK_C    0x0c40 41static int wm8958_dsp2_fw(struct snd_soc_component *component, const char *name,42			  const struct firmware *fw, bool check)43{44	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);45	u64 data64;46	u32 data32;47	const u8 *data;48	char *str;49	size_t block_len, len;50	int ret = 0;51 52	/* Suppress unneeded downloads */53	if (wm8994->cur_fw == fw)54		return 0;55 56	if (fw->size < 32) {57		dev_err(component->dev, "%s: firmware too short (%zd bytes)\n",58			name, fw->size);59		goto err;60	}61 62	if (memcmp(fw->data, "WMFW", 4) != 0) {63		data32 = get_unaligned_be32(fw->data);64		dev_err(component->dev, "%s: firmware has bad file magic %08x\n",65			name, data32);66		goto err;67	}68 69	len = get_unaligned_be32(fw->data + 4);70	data32 = get_unaligned_be32(fw->data + 8);71 72	if ((data32 >> 24) & 0xff) {73		dev_err(component->dev, "%s: unsupported firmware version %d\n",74			name, (data32 >> 24) & 0xff);75		goto err;76	}77	if ((data32 & 0xffff) != 8958) {78		dev_err(component->dev, "%s: unsupported target device %d\n",79			name, data32 & 0xffff);80		goto err;81	}82	if (((data32 >> 16) & 0xff) != 0xc) {83		dev_err(component->dev, "%s: unsupported target core %d\n",84			name, (data32 >> 16) & 0xff);85		goto err;86	}87 88	if (check) {89		data64 = get_unaligned_be64(fw->data + 24);90		dev_info(component->dev, "%s timestamp %llx\n",  name, data64);91	} else {92		snd_soc_component_write(component, 0x102, 0x2);93		snd_soc_component_write(component, 0x900, 0x2);94	}95 96	data = fw->data + len;97	len = fw->size - len;98	while (len) {99		if (len < 12) {100			dev_err(component->dev, "%s short data block of %zd\n",101				name, len);102			goto err;103		}104 105		block_len = get_unaligned_be32(data + 4);106		if (block_len + 8 > len) {107			dev_err(component->dev, "%zd byte block longer than file\n",108				block_len);109			goto err;110		}111		if (block_len == 0) {112			dev_err(component->dev, "Zero length block\n");113			goto err;114		}115 116		data32 = get_unaligned_be32(data);117 118		switch ((data32 >> 24) & 0xff) {119		case WM_FW_BLOCK_INFO:120			/* Informational text */121			if (!check)122				break;123 124			str = kzalloc(block_len + 1, GFP_KERNEL);125			if (str) {126				memcpy(str, data + 8, block_len);127				dev_info(component->dev, "%s: %s\n", name, str);128				kfree(str);129			} else {130				dev_err(component->dev, "Out of memory\n");131			}132			break;133		case WM_FW_BLOCK_PM:134		case WM_FW_BLOCK_X:135		case WM_FW_BLOCK_Y:136		case WM_FW_BLOCK_Z:137		case WM_FW_BLOCK_I:138		case WM_FW_BLOCK_A:139		case WM_FW_BLOCK_C:140			dev_dbg(component->dev, "%s: %zd bytes of %x@%x\n", name,141				block_len, (data32 >> 24) & 0xff,142				data32 & 0xffffff);143 144			if (check)145				break;146 147			data32 &= 0xffffff;148 149			wm8994_bulk_write(wm8994->wm8994,150					  data32 & 0xffffff,151					  block_len / 2,152					  (void *)(data + 8));153 154			break;155		default:156			dev_warn(component->dev, "%s: unknown block type %d\n",157				 name, (data32 >> 24) & 0xff);158			break;159		}160 161		/* Round up to the next 32 bit word */162		block_len += block_len % 4;163 164		data += block_len + 8;165		len -= block_len + 8;166	}167 168	if (!check) {169		dev_dbg(component->dev, "%s: download done\n", name);170		wm8994->cur_fw = fw;171	} else {172		dev_info(component->dev, "%s: got firmware\n", name);173	}174 175	goto ok;176 177err:178	ret = -EINVAL;179ok:180	if (!check) {181		snd_soc_component_write(component, 0x900, 0x0);182		snd_soc_component_write(component, 0x102, 0x0);183	}184 185	return ret;186}187 188static void wm8958_dsp_start_mbc(struct snd_soc_component *component, int path)189{190	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);191	struct wm8994 *control = wm8994->wm8994;192	int i;193 194	/* If the DSP is already running then noop */195	if (snd_soc_component_read(component, WM8958_DSP2_PROGRAM) & WM8958_DSP2_ENA)196		return;197 198	/* If we have MBC firmware download it */199	if (wm8994->mbc)200		wm8958_dsp2_fw(component, "MBC", wm8994->mbc, false);201 202	snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM,203			    WM8958_DSP2_ENA, WM8958_DSP2_ENA);204 205	/* If we've got user supplied MBC settings use them */206	if (control->pdata.num_mbc_cfgs) {207		struct wm8958_mbc_cfg *cfg208			= &control->pdata.mbc_cfgs[wm8994->mbc_cfg];209 210		for (i = 0; i < ARRAY_SIZE(cfg->coeff_regs); i++)211			snd_soc_component_write(component, i + WM8958_MBC_BAND_1_K_1,212				      cfg->coeff_regs[i]);213 214		for (i = 0; i < ARRAY_SIZE(cfg->cutoff_regs); i++)215			snd_soc_component_write(component,216				      i + WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1,217				      cfg->cutoff_regs[i]);218	}219 220	/* Run the DSP */221	snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL,222		      WM8958_DSP2_RUNR);223 224	/* And we're off! */225	snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG,226			    WM8958_MBC_ENA |227			    WM8958_MBC_SEL_MASK,228			    path << WM8958_MBC_SEL_SHIFT |229			    WM8958_MBC_ENA);230}231 232static void wm8958_dsp_start_vss(struct snd_soc_component *component, int path)233{234	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);235	struct wm8994 *control = wm8994->wm8994;236	int i, ena;237 238	if (wm8994->mbc_vss)239		wm8958_dsp2_fw(component, "MBC+VSS", wm8994->mbc_vss, false);240 241	snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM,242			    WM8958_DSP2_ENA, WM8958_DSP2_ENA);243 244	/* If we've got user supplied settings use them */245	if (control->pdata.num_mbc_cfgs) {246		struct wm8958_mbc_cfg *cfg247			= &control->pdata.mbc_cfgs[wm8994->mbc_cfg];248 249		for (i = 0; i < ARRAY_SIZE(cfg->combined_regs); i++)250			snd_soc_component_write(component, i + 0x2800,251				      cfg->combined_regs[i]);252	}253 254	if (control->pdata.num_vss_cfgs) {255		struct wm8958_vss_cfg *cfg256			= &control->pdata.vss_cfgs[wm8994->vss_cfg];257 258		for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)259			snd_soc_component_write(component, i + 0x2600, cfg->regs[i]);260	}261 262	if (control->pdata.num_vss_hpf_cfgs) {263		struct wm8958_vss_hpf_cfg *cfg264			= &control->pdata.vss_hpf_cfgs[wm8994->vss_hpf_cfg];265 266		for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)267			snd_soc_component_write(component, i + 0x2400, cfg->regs[i]);268	}269 270	/* Run the DSP */271	snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL,272		      WM8958_DSP2_RUNR);273 274	/* Enable the algorithms we've selected */275	ena = 0;276	if (wm8994->mbc_ena[path])277		ena |= 0x8;278	if (wm8994->hpf2_ena[path])279		ena |= 0x4;280	if (wm8994->hpf1_ena[path])281		ena |= 0x2;282	if (wm8994->vss_ena[path])283		ena |= 0x1;284 285	snd_soc_component_write(component, 0x2201, ena);286 287	/* Switch the DSP into the data path */288	snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG,289			    WM8958_MBC_SEL_MASK | WM8958_MBC_ENA,290			    path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA);291}292 293static void wm8958_dsp_start_enh_eq(struct snd_soc_component *component, int path)294{295	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);296	struct wm8994 *control = wm8994->wm8994;297	int i;298 299	wm8958_dsp2_fw(component, "ENH_EQ", wm8994->enh_eq, false);300 301	snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM,302			    WM8958_DSP2_ENA, WM8958_DSP2_ENA);303 304	/* If we've got user supplied settings use them */305	if (control->pdata.num_enh_eq_cfgs) {306		struct wm8958_enh_eq_cfg *cfg307			= &control->pdata.enh_eq_cfgs[wm8994->enh_eq_cfg];308 309		for (i = 0; i < ARRAY_SIZE(cfg->regs); i++)310			snd_soc_component_write(component, i + 0x2200,311				      cfg->regs[i]);312	}313 314	/* Run the DSP */315	snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL,316		      WM8958_DSP2_RUNR);317 318	/* Switch the DSP into the data path */319	snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG,320			    WM8958_MBC_SEL_MASK | WM8958_MBC_ENA,321			    path << WM8958_MBC_SEL_SHIFT | WM8958_MBC_ENA);322}323 324static void wm8958_dsp_apply(struct snd_soc_component *component, int path, int start)325{326	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);327	int pwr_reg = snd_soc_component_read(component, WM8994_POWER_MANAGEMENT_5);328	int ena, reg, aif;329 330	switch (path) {331	case 0:332		pwr_reg &= (WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA);333		aif = 0;334		break;335	case 1:336		pwr_reg &= (WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);337		aif = 0;338		break;339	case 2:340		pwr_reg &= (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA);341		aif = 1;342		break;343	default:344		WARN(1, "Invalid path %d\n", path);345		return;346	}347 348	/* Do we have both an active AIF and an active algorithm? */349	ena = wm8994->mbc_ena[path] || wm8994->vss_ena[path] ||350		wm8994->hpf1_ena[path] || wm8994->hpf2_ena[path] ||351		wm8994->enh_eq_ena[path];352	if (!pwr_reg)353		ena = 0;354 355	reg = snd_soc_component_read(component, WM8958_DSP2_PROGRAM);356 357	dev_dbg(component->dev, "DSP path %d %d startup: %d, power: %x, DSP: %x\n",358		path, wm8994->dsp_active, start, pwr_reg, reg);359 360	if (start && ena) {361		/* If the DSP is already running then noop */362		if (reg & WM8958_DSP2_ENA)363			return;364 365		/* If either AIFnCLK is not yet enabled postpone */366		if (!(snd_soc_component_read(component, WM8994_AIF1_CLOCKING_1)367		      & WM8994_AIF1CLK_ENA_MASK) &&368		    !(snd_soc_component_read(component, WM8994_AIF2_CLOCKING_1)369		      & WM8994_AIF2CLK_ENA_MASK))370			return;371 372		/* Switch the clock over to the appropriate AIF */373		snd_soc_component_update_bits(component, WM8994_CLOCKING_1,374				    WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA,375				    aif << WM8958_DSP2CLK_SRC_SHIFT |376				    WM8958_DSP2CLK_ENA);377 378		if (wm8994->enh_eq_ena[path])379			wm8958_dsp_start_enh_eq(component, path);380		else if (wm8994->vss_ena[path] || wm8994->hpf1_ena[path] ||381		    wm8994->hpf2_ena[path])382			wm8958_dsp_start_vss(component, path);383		else if (wm8994->mbc_ena[path])384			wm8958_dsp_start_mbc(component, path);385 386		wm8994->dsp_active = path;387 388		dev_dbg(component->dev, "DSP running in path %d\n", path);389	}390 391	if (!start && wm8994->dsp_active == path) {392		/* If the DSP is already stopped then noop */393		if (!(reg & WM8958_DSP2_ENA))394			return;395 396		snd_soc_component_update_bits(component, WM8958_DSP2_CONFIG,397				    WM8958_MBC_ENA, 0);	398		snd_soc_component_write(component, WM8958_DSP2_EXECCONTROL,399			      WM8958_DSP2_STOP);400		snd_soc_component_update_bits(component, WM8958_DSP2_PROGRAM,401				    WM8958_DSP2_ENA, 0);402		snd_soc_component_update_bits(component, WM8994_CLOCKING_1,403				    WM8958_DSP2CLK_ENA, 0);404 405		wm8994->dsp_active = -1;406 407		dev_dbg(component->dev, "DSP stopped\n");408	}409}410 411int wm8958_aif_ev(struct snd_soc_dapm_widget *w,412		  struct snd_kcontrol *kcontrol, int event)413{414	struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);415	struct wm8994 *control = dev_get_drvdata(component->dev->parent);416	int i;417 418	if (control->type != WM8958)419		return 0;420 421	switch (event) {422	case SND_SOC_DAPM_POST_PMU:423	case SND_SOC_DAPM_PRE_PMU:424		for (i = 0; i < 3; i++)425			wm8958_dsp_apply(component, i, 1);426		break;427	case SND_SOC_DAPM_POST_PMD:428	case SND_SOC_DAPM_PRE_PMD:429		for (i = 0; i < 3; i++)430			wm8958_dsp_apply(component, i, 0);431		break;432	}433 434	return 0;435}436 437/* Check if DSP2 is in use on another AIF */438static int wm8958_dsp2_busy(struct wm8994_priv *wm8994, int aif)439{440	int i;441 442	for (i = 0; i < ARRAY_SIZE(wm8994->mbc_ena); i++) {443		if (i == aif)444			continue;445		if (wm8994->mbc_ena[i] || wm8994->vss_ena[i] ||446		    wm8994->hpf1_ena[i] || wm8994->hpf2_ena[i])447			return 1;448	}449 450	return 0;451}452 453static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol,454			       struct snd_ctl_elem_value *ucontrol)455{456	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);457	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);458	struct wm8994 *control = wm8994->wm8994;459	int value = ucontrol->value.enumerated.item[0];460	int reg;461 462	/* Don't allow on the fly reconfiguration */463	reg = snd_soc_component_read(component, WM8994_CLOCKING_1);464	if (reg < 0 || reg & WM8958_DSP2CLK_ENA)465		return -EBUSY;466 467	if (value >= control->pdata.num_mbc_cfgs)468		return -EINVAL;469 470	wm8994->mbc_cfg = value;471 472	return 0;473}474 475static int wm8958_get_mbc_enum(struct snd_kcontrol *kcontrol,476			       struct snd_ctl_elem_value *ucontrol)477{478	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);479	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);480 481	ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg;482 483	return 0;484}485 486static int wm8958_mbc_info(struct snd_kcontrol *kcontrol,487			   struct snd_ctl_elem_info *uinfo)488{489	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;490	uinfo->count = 1;491	uinfo->value.integer.min = 0;492	uinfo->value.integer.max = 1;493	return 0;494}495 496static int wm8958_mbc_get(struct snd_kcontrol *kcontrol,497			  struct snd_ctl_elem_value *ucontrol)498{499	int mbc = kcontrol->private_value;500	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);501	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);502 503	ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc];504 505	return 0;506}507 508static int wm8958_mbc_put(struct snd_kcontrol *kcontrol,509			  struct snd_ctl_elem_value *ucontrol)510{511	int mbc = kcontrol->private_value;512	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);513	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);514 515	if (wm8994->mbc_ena[mbc] == ucontrol->value.integer.value[0])516		return 0;517 518	if (ucontrol->value.integer.value[0] > 1)519		return -EINVAL;520 521	if (wm8958_dsp2_busy(wm8994, mbc)) {522		dev_dbg(component->dev, "DSP2 active on %d already\n", mbc);523		return -EBUSY;524	}525 526	if (wm8994->enh_eq_ena[mbc])527		return -EBUSY;528 529	wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0];530 531	wm8958_dsp_apply(component, mbc, wm8994->mbc_ena[mbc]);532 533	return 1;534}535 536#define WM8958_MBC_SWITCH(xname, xval) {\537	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \538	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\539	.info = wm8958_mbc_info, \540	.get = wm8958_mbc_get, .put = wm8958_mbc_put, \541	.private_value = xval }542 543static int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol,544			       struct snd_ctl_elem_value *ucontrol)545{546	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);547	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);548	struct wm8994 *control = wm8994->wm8994;549	int value = ucontrol->value.enumerated.item[0];550	int reg;551 552	/* Don't allow on the fly reconfiguration */553	reg = snd_soc_component_read(component, WM8994_CLOCKING_1);554	if (reg < 0 || reg & WM8958_DSP2CLK_ENA)555		return -EBUSY;556 557	if (value >= control->pdata.num_vss_cfgs)558		return -EINVAL;559 560	wm8994->vss_cfg = value;561 562	return 0;563}564 565static int wm8958_get_vss_enum(struct snd_kcontrol *kcontrol,566			       struct snd_ctl_elem_value *ucontrol)567{568	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);569	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);570 571	ucontrol->value.enumerated.item[0] = wm8994->vss_cfg;572 573	return 0;574}575 576static int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol,577				   struct snd_ctl_elem_value *ucontrol)578{579	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);580	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);581	struct wm8994 *control = wm8994->wm8994;582	int value = ucontrol->value.enumerated.item[0];583	int reg;584 585	/* Don't allow on the fly reconfiguration */586	reg = snd_soc_component_read(component, WM8994_CLOCKING_1);587	if (reg < 0 || reg & WM8958_DSP2CLK_ENA)588		return -EBUSY;589 590	if (value >= control->pdata.num_vss_hpf_cfgs)591		return -EINVAL;592 593	wm8994->vss_hpf_cfg = value;594 595	return 0;596}597 598static int wm8958_get_vss_hpf_enum(struct snd_kcontrol *kcontrol,599				   struct snd_ctl_elem_value *ucontrol)600{601	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);602	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);603 604	ucontrol->value.enumerated.item[0] = wm8994->vss_hpf_cfg;605 606	return 0;607}608 609static int wm8958_vss_info(struct snd_kcontrol *kcontrol,610			   struct snd_ctl_elem_info *uinfo)611{612	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;613	uinfo->count = 1;614	uinfo->value.integer.min = 0;615	uinfo->value.integer.max = 1;616	return 0;617}618 619static int wm8958_vss_get(struct snd_kcontrol *kcontrol,620			  struct snd_ctl_elem_value *ucontrol)621{622	int vss = kcontrol->private_value;623	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);624	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);625 626	ucontrol->value.integer.value[0] = wm8994->vss_ena[vss];627 628	return 0;629}630 631static int wm8958_vss_put(struct snd_kcontrol *kcontrol,632			  struct snd_ctl_elem_value *ucontrol)633{634	int vss = kcontrol->private_value;635	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);636	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);637 638	if (wm8994->vss_ena[vss] == ucontrol->value.integer.value[0])639		return 0;640 641	if (ucontrol->value.integer.value[0] > 1)642		return -EINVAL;643 644	if (!wm8994->mbc_vss)645		return -ENODEV;646 647	if (wm8958_dsp2_busy(wm8994, vss)) {648		dev_dbg(component->dev, "DSP2 active on %d already\n", vss);649		return -EBUSY;650	}651 652	if (wm8994->enh_eq_ena[vss])653		return -EBUSY;654 655	wm8994->vss_ena[vss] = ucontrol->value.integer.value[0];656 657	wm8958_dsp_apply(component, vss, wm8994->vss_ena[vss]);658 659	return 1;660}661 662 663#define WM8958_VSS_SWITCH(xname, xval) {\664	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \665	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\666	.info = wm8958_vss_info, \667	.get = wm8958_vss_get, .put = wm8958_vss_put, \668	.private_value = xval }669 670static int wm8958_hpf_info(struct snd_kcontrol *kcontrol,671			   struct snd_ctl_elem_info *uinfo)672{673	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;674	uinfo->count = 1;675	uinfo->value.integer.min = 0;676	uinfo->value.integer.max = 1;677	return 0;678}679 680static int wm8958_hpf_get(struct snd_kcontrol *kcontrol,681			  struct snd_ctl_elem_value *ucontrol)682{683	int hpf = kcontrol->private_value;684	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);685	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);686 687	if (hpf < 3)688		ucontrol->value.integer.value[0] = wm8994->hpf1_ena[hpf % 3];689	else690		ucontrol->value.integer.value[0] = wm8994->hpf2_ena[hpf % 3];691 692	return 0;693}694 695static int wm8958_hpf_put(struct snd_kcontrol *kcontrol,696			  struct snd_ctl_elem_value *ucontrol)697{698	int hpf = kcontrol->private_value;699	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);700	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);701 702	if (hpf < 3) {703		if (wm8994->hpf1_ena[hpf % 3] ==704		    ucontrol->value.integer.value[0])705			return 0;706	} else {707		if (wm8994->hpf2_ena[hpf % 3] ==708		    ucontrol->value.integer.value[0])709			return 0;710	}711 712	if (ucontrol->value.integer.value[0] > 1)713		return -EINVAL;714 715	if (!wm8994->mbc_vss)716		return -ENODEV;717 718	if (wm8958_dsp2_busy(wm8994, hpf % 3)) {719		dev_dbg(component->dev, "DSP2 active on %d already\n", hpf);720		return -EBUSY;721	}722 723	if (wm8994->enh_eq_ena[hpf % 3])724		return -EBUSY;725 726	if (hpf < 3)727		wm8994->hpf1_ena[hpf % 3] = ucontrol->value.integer.value[0];728	else729		wm8994->hpf2_ena[hpf % 3] = ucontrol->value.integer.value[0];730 731	wm8958_dsp_apply(component, hpf % 3, ucontrol->value.integer.value[0]);732 733	return 1;734}735 736#define WM8958_HPF_SWITCH(xname, xval) {\737	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \738	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\739	.info = wm8958_hpf_info, \740	.get = wm8958_hpf_get, .put = wm8958_hpf_put, \741	.private_value = xval }742 743static int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol,744				  struct snd_ctl_elem_value *ucontrol)745{746	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);747	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);748	struct wm8994 *control = wm8994->wm8994;749	int value = ucontrol->value.enumerated.item[0];750	int reg;751 752	/* Don't allow on the fly reconfiguration */753	reg = snd_soc_component_read(component, WM8994_CLOCKING_1);754	if (reg < 0 || reg & WM8958_DSP2CLK_ENA)755		return -EBUSY;756 757	if (value >= control->pdata.num_enh_eq_cfgs)758		return -EINVAL;759 760	wm8994->enh_eq_cfg = value;761 762	return 0;763}764 765static int wm8958_get_enh_eq_enum(struct snd_kcontrol *kcontrol,766				  struct snd_ctl_elem_value *ucontrol)767{768	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);769	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);770 771	ucontrol->value.enumerated.item[0] = wm8994->enh_eq_cfg;772 773	return 0;774}775 776static int wm8958_enh_eq_info(struct snd_kcontrol *kcontrol,777			   struct snd_ctl_elem_info *uinfo)778{779	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;780	uinfo->count = 1;781	uinfo->value.integer.min = 0;782	uinfo->value.integer.max = 1;783	return 0;784}785 786static int wm8958_enh_eq_get(struct snd_kcontrol *kcontrol,787			  struct snd_ctl_elem_value *ucontrol)788{789	int eq = kcontrol->private_value;790	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);791	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);792 793	ucontrol->value.integer.value[0] = wm8994->enh_eq_ena[eq];794 795	return 0;796}797 798static int wm8958_enh_eq_put(struct snd_kcontrol *kcontrol,799			  struct snd_ctl_elem_value *ucontrol)800{801	int eq = kcontrol->private_value;802	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);803	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);804 805	if (wm8994->enh_eq_ena[eq] == ucontrol->value.integer.value[0])806		return 0;807 808	if (ucontrol->value.integer.value[0] > 1)809		return -EINVAL;810 811	if (!wm8994->enh_eq)812		return -ENODEV;813 814	if (wm8958_dsp2_busy(wm8994, eq)) {815		dev_dbg(component->dev, "DSP2 active on %d already\n", eq);816		return -EBUSY;817	}818 819	if (wm8994->mbc_ena[eq] || wm8994->vss_ena[eq] ||820	    wm8994->hpf1_ena[eq] || wm8994->hpf2_ena[eq])821		return -EBUSY;822 823	wm8994->enh_eq_ena[eq] = ucontrol->value.integer.value[0];824 825	wm8958_dsp_apply(component, eq, ucontrol->value.integer.value[0]);826 827	return 1;828}829 830#define WM8958_ENH_EQ_SWITCH(xname, xval) {\831	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \832	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\833	.info = wm8958_enh_eq_info, \834	.get = wm8958_enh_eq_get, .put = wm8958_enh_eq_put, \835	.private_value = xval }836 837static const struct snd_kcontrol_new wm8958_mbc_snd_controls[] = {838WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0),839WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1),840WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2),841};842 843static const struct snd_kcontrol_new wm8958_vss_snd_controls[] = {844WM8958_VSS_SWITCH("AIF1DAC1 VSS Switch", 0),845WM8958_VSS_SWITCH("AIF1DAC2 VSS Switch", 1),846WM8958_VSS_SWITCH("AIF2DAC VSS Switch", 2),847WM8958_HPF_SWITCH("AIF1DAC1 HPF1 Switch", 0),848WM8958_HPF_SWITCH("AIF1DAC2 HPF1 Switch", 1),849WM8958_HPF_SWITCH("AIF2DAC HPF1 Switch", 2),850WM8958_HPF_SWITCH("AIF1DAC1 HPF2 Switch", 3),851WM8958_HPF_SWITCH("AIF1DAC2 HPF2 Switch", 4),852WM8958_HPF_SWITCH("AIF2DAC HPF2 Switch", 5),853};854 855static const struct snd_kcontrol_new wm8958_enh_eq_snd_controls[] = {856WM8958_ENH_EQ_SWITCH("AIF1DAC1 Enhanced EQ Switch", 0),857WM8958_ENH_EQ_SWITCH("AIF1DAC2 Enhanced EQ Switch", 1),858WM8958_ENH_EQ_SWITCH("AIF2DAC Enhanced EQ Switch", 2),859};860 861static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context)862{863	struct snd_soc_component *component = context;864	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);865 866	if (fw && (wm8958_dsp2_fw(component, "ENH_EQ", fw, true) == 0)) {867		mutex_lock(&wm8994->fw_lock);868		wm8994->enh_eq = fw;869		mutex_unlock(&wm8994->fw_lock);870	}871}872 873static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context)874{875	struct snd_soc_component *component = context;876	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);877 878	if (fw && (wm8958_dsp2_fw(component, "MBC+VSS", fw, true) == 0)) {879		mutex_lock(&wm8994->fw_lock);880		wm8994->mbc_vss = fw;881		mutex_unlock(&wm8994->fw_lock);882	}883}884 885static void wm8958_mbc_loaded(const struct firmware *fw, void *context)886{887	struct snd_soc_component *component = context;888	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);889 890	if (fw && (wm8958_dsp2_fw(component, "MBC", fw, true) == 0)) {891		mutex_lock(&wm8994->fw_lock);892		wm8994->mbc = fw;893		mutex_unlock(&wm8994->fw_lock);894	}895}896 897void wm8958_dsp2_init(struct snd_soc_component *component)898{899	struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component);900	struct wm8994 *control = wm8994->wm8994;901	struct wm8994_pdata *pdata = &control->pdata;902	int ret, i;903 904	wm8994->dsp_active = -1;905 906	snd_soc_add_component_controls(component, wm8958_mbc_snd_controls,907			     ARRAY_SIZE(wm8958_mbc_snd_controls));908	snd_soc_add_component_controls(component, wm8958_vss_snd_controls,909			     ARRAY_SIZE(wm8958_vss_snd_controls));910	snd_soc_add_component_controls(component, wm8958_enh_eq_snd_controls,911			     ARRAY_SIZE(wm8958_enh_eq_snd_controls));912 913 914	/* We don't *require* firmware and don't want to delay boot */915	request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,916				"wm8958_mbc.wfw", component->dev, GFP_KERNEL,917				component, wm8958_mbc_loaded);918	request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,919				"wm8958_mbc_vss.wfw", component->dev, GFP_KERNEL,920				component, wm8958_mbc_vss_loaded);921	request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,922				"wm8958_enh_eq.wfw", component->dev, GFP_KERNEL,923				component, wm8958_enh_eq_loaded);924 925	if (pdata->num_mbc_cfgs) {926		struct snd_kcontrol_new mbc_control[] = {927			SOC_ENUM_EXT("MBC Mode", wm8994->mbc_enum,928				     wm8958_get_mbc_enum, wm8958_put_mbc_enum),929		};930 931		/* We need an array of texts for the enum API */932		wm8994->mbc_texts = kmalloc_array(pdata->num_mbc_cfgs,933						  sizeof(char *),934						  GFP_KERNEL);935		if (!wm8994->mbc_texts)936			return;937 938		for (i = 0; i < pdata->num_mbc_cfgs; i++)939			wm8994->mbc_texts[i] = pdata->mbc_cfgs[i].name;940 941		wm8994->mbc_enum.items = pdata->num_mbc_cfgs;942		wm8994->mbc_enum.texts = wm8994->mbc_texts;943 944		ret = snd_soc_add_component_controls(wm8994->hubs.component,945						 mbc_control, 1);946		if (ret != 0)947			dev_err(wm8994->hubs.component->dev,948				"Failed to add MBC mode controls: %d\n", ret);949	}950 951	if (pdata->num_vss_cfgs) {952		struct snd_kcontrol_new vss_control[] = {953			SOC_ENUM_EXT("VSS Mode", wm8994->vss_enum,954				     wm8958_get_vss_enum, wm8958_put_vss_enum),955		};956 957		/* We need an array of texts for the enum API */958		wm8994->vss_texts = kmalloc_array(pdata->num_vss_cfgs,959						  sizeof(char *),960						  GFP_KERNEL);961		if (!wm8994->vss_texts)962			return;963 964		for (i = 0; i < pdata->num_vss_cfgs; i++)965			wm8994->vss_texts[i] = pdata->vss_cfgs[i].name;966 967		wm8994->vss_enum.items = pdata->num_vss_cfgs;968		wm8994->vss_enum.texts = wm8994->vss_texts;969 970		ret = snd_soc_add_component_controls(wm8994->hubs.component,971						 vss_control, 1);972		if (ret != 0)973			dev_err(wm8994->hubs.component->dev,974				"Failed to add VSS mode controls: %d\n", ret);975	}976 977	if (pdata->num_vss_hpf_cfgs) {978		struct snd_kcontrol_new hpf_control[] = {979			SOC_ENUM_EXT("VSS HPF Mode", wm8994->vss_hpf_enum,980				     wm8958_get_vss_hpf_enum,981				     wm8958_put_vss_hpf_enum),982		};983 984		/* We need an array of texts for the enum API */985		wm8994->vss_hpf_texts = kmalloc_array(pdata->num_vss_hpf_cfgs,986						      sizeof(char *),987						      GFP_KERNEL);988		if (!wm8994->vss_hpf_texts)989			return;990 991		for (i = 0; i < pdata->num_vss_hpf_cfgs; i++)992			wm8994->vss_hpf_texts[i] = pdata->vss_hpf_cfgs[i].name;993 994		wm8994->vss_hpf_enum.items = pdata->num_vss_hpf_cfgs;995		wm8994->vss_hpf_enum.texts = wm8994->vss_hpf_texts;996 997		ret = snd_soc_add_component_controls(wm8994->hubs.component,998						 hpf_control, 1);999		if (ret != 0)1000			dev_err(wm8994->hubs.component->dev,1001				"Failed to add VSS HPFmode controls: %d\n",1002				ret);1003	}1004 1005	if (pdata->num_enh_eq_cfgs) {1006		struct snd_kcontrol_new eq_control[] = {1007			SOC_ENUM_EXT("Enhanced EQ Mode", wm8994->enh_eq_enum,1008				     wm8958_get_enh_eq_enum,1009				     wm8958_put_enh_eq_enum),1010		};1011 1012		/* We need an array of texts for the enum API */1013		wm8994->enh_eq_texts = kmalloc_array(pdata->num_enh_eq_cfgs,1014						     sizeof(char *),1015						     GFP_KERNEL);1016		if (!wm8994->enh_eq_texts)1017			return;1018 1019		for (i = 0; i < pdata->num_enh_eq_cfgs; i++)1020			wm8994->enh_eq_texts[i] = pdata->enh_eq_cfgs[i].name;1021 1022		wm8994->enh_eq_enum.items = pdata->num_enh_eq_cfgs;1023		wm8994->enh_eq_enum.texts = wm8994->enh_eq_texts;1024 1025		ret = snd_soc_add_component_controls(wm8994->hubs.component,1026						 eq_control, 1);1027		if (ret != 0)1028			dev_err(wm8994->hubs.component->dev,1029				"Failed to add enhanced EQ controls: %d\n",1030				ret);1031	}1032}1033