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