~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/codecs/wm8958-dsp2.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * wm8958-dsp2.c  --  WM8958 DSP2 support
  4  *
  5  * Copyright 2011 Wolfson Microelectronics plc
  6  *
  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 <asm/unaligned.h>
 29 
 30 #include "wm8994.h"
 31 
 32 #define WM_FW_BLOCK_INFO 0xff
 33 #define WM_FW_BLOCK_PM   0x00
 34 #define WM_FW_BLOCK_X    0x01
 35 #define WM_FW_BLOCK_Y    0x02
 36 #define WM_FW_BLOCK_Z    0x03
 37 #define WM_FW_BLOCK_I    0x06
 38 #define WM_FW_BLOCK_A    0x08
 39 #define WM_FW_BLOCK_C    0x0c
 40 
 41 static 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 
177 err:
178         ret = -EINVAL;
179 ok:
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 
188 static 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 *cfg
208                         = &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 
232 static 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 *cfg
247                         = &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 *cfg
256                         = &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 *cfg
264                         = &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 
293 static 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 *cfg
307                         = &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 
324 static 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 
411 int 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 */
438 static 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 
453 static 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 
475 static 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 
486 static 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 
496 static 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 
508 static 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 
543 static 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 
565 static 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 
576 static 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 
598 static 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 
609 static 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 
619 static 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 
631 static 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 
670 static 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 
680 static 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         else
690                 ucontrol->value.integer.value[0] = wm8994->hpf2_ena[hpf % 3];
691 
692         return 0;
693 }
694 
695 static 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         else
729                 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 
743 static 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 
765 static 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 
776 static 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 
786 static 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 
798 static 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 
837 static const struct snd_kcontrol_new wm8958_mbc_snd_controls[] = {
838 WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0),
839 WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1),
840 WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2),
841 };
842 
843 static const struct snd_kcontrol_new wm8958_vss_snd_controls[] = {
844 WM8958_VSS_SWITCH("AIF1DAC1 VSS Switch", 0),
845 WM8958_VSS_SWITCH("AIF1DAC2 VSS Switch", 1),
846 WM8958_VSS_SWITCH("AIF2DAC VSS Switch", 2),
847 WM8958_HPF_SWITCH("AIF1DAC1 HPF1 Switch", 0),
848 WM8958_HPF_SWITCH("AIF1DAC2 HPF1 Switch", 1),
849 WM8958_HPF_SWITCH("AIF2DAC HPF1 Switch", 2),
850 WM8958_HPF_SWITCH("AIF1DAC1 HPF2 Switch", 3),
851 WM8958_HPF_SWITCH("AIF1DAC2 HPF2 Switch", 4),
852 WM8958_HPF_SWITCH("AIF2DAC HPF2 Switch", 5),
853 };
854 
855 static const struct snd_kcontrol_new wm8958_enh_eq_snd_controls[] = {
856 WM8958_ENH_EQ_SWITCH("AIF1DAC1 Enhanced EQ Switch", 0),
857 WM8958_ENH_EQ_SWITCH("AIF1DAC2 Enhanced EQ Switch", 1),
858 WM8958_ENH_EQ_SWITCH("AIF2DAC Enhanced EQ Switch", 2),
859 };
860 
861 static 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 
873 static 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 
885 static 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 
897 void 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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php