aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm8958-dsp2.c
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2011-03-09 14:31:01 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2011-03-18 06:52:40 -0400
commitf701a2e594e62b35d895ad5ec1db8d2d0714c158 (patch)
tree1f3b5c23a3258cdd56bd3db65618ccaa8b7479b1 /sound/soc/codecs/wm8958-dsp2.c
parent780e2806986f9cc980808687da95160c65baa78a (diff)
ASoC: Factor WM8958 DSP2 handling into separate file
DSP2 on the WM8958 has a default ROM which provides a multi-band compressor for enhanced performance on mobile devices but can also support runtime download of alternative firmware. In preparation for more exploiting this functionality refactor the code to split the handling of DSP2 into a separate file. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'sound/soc/codecs/wm8958-dsp2.c')
-rw-r--r--sound/soc/codecs/wm8958-dsp2.c289
1 files changed, 289 insertions, 0 deletions
diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c
new file mode 100644
index 000000000000..4b4c93c20331
--- /dev/null
+++ b/sound/soc/codecs/wm8958-dsp2.c
@@ -0,0 +1,289 @@
1/*
2 * wm8958-dsp2.c -- WM8958 DSP2 support
3 *
4 * Copyright 2011 Wolfson Microelectronics plc
5 *
6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/module.h>
14#include <linux/moduleparam.h>
15#include <linux/init.h>
16#include <linux/delay.h>
17#include <linux/pm.h>
18#include <linux/i2c.h>
19#include <linux/platform_device.h>
20#include <linux/slab.h>
21#include <sound/soc.h>
22#include <sound/initval.h>
23#include <sound/tlv.h>
24#include <trace/events/asoc.h>
25
26#include <linux/mfd/wm8994/core.h>
27#include <linux/mfd/wm8994/registers.h>
28#include <linux/mfd/wm8994/pdata.h>
29#include <linux/mfd/wm8994/gpio.h>
30
31#include "wm8994.h"
32
33static void wm8958_mbc_apply(struct snd_soc_codec *codec, int mbc, int start)
34{
35 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
36 struct wm8994_pdata *pdata = wm8994->pdata;
37 int pwr_reg = snd_soc_read(codec, WM8994_POWER_MANAGEMENT_5);
38 int ena, reg, aif, i;
39
40 switch (mbc) {
41 case 0:
42 pwr_reg &= (WM8994_AIF1DAC1L_ENA | WM8994_AIF1DAC1R_ENA);
43 aif = 0;
44 break;
45 case 1:
46 pwr_reg &= (WM8994_AIF1DAC2L_ENA | WM8994_AIF1DAC2R_ENA);
47 aif = 0;
48 break;
49 case 2:
50 pwr_reg &= (WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA);
51 aif = 1;
52 break;
53 default:
54 BUG();
55 return;
56 }
57
58 /* We can only enable the MBC if the AIF is enabled and we
59 * want it to be enabled. */
60 ena = pwr_reg && wm8994->mbc_ena[mbc];
61
62 reg = snd_soc_read(codec, WM8958_DSP2_PROGRAM);
63
64 dev_dbg(codec->dev, "MBC %d startup: %d, power: %x, DSP: %x\n",
65 mbc, start, pwr_reg, reg);
66
67 if (start && ena) {
68 /* If the DSP is already running then noop */
69 if (reg & WM8958_DSP2_ENA)
70 return;
71
72 /* Switch the clock over to the appropriate AIF */
73 snd_soc_update_bits(codec, WM8994_CLOCKING_1,
74 WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA,
75 aif << WM8958_DSP2CLK_SRC_SHIFT |
76 WM8958_DSP2CLK_ENA);
77
78 snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
79 WM8958_DSP2_ENA, WM8958_DSP2_ENA);
80
81 /* If we've got user supplied MBC settings use them */
82 if (pdata && pdata->num_mbc_cfgs) {
83 struct wm8958_mbc_cfg *cfg
84 = &pdata->mbc_cfgs[wm8994->mbc_cfg];
85
86 for (i = 0; i < ARRAY_SIZE(cfg->coeff_regs); i++)
87 snd_soc_write(codec, i + WM8958_MBC_BAND_1_K_1,
88 cfg->coeff_regs[i]);
89
90 for (i = 0; i < ARRAY_SIZE(cfg->cutoff_regs); i++)
91 snd_soc_write(codec,
92 i + WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1,
93 cfg->cutoff_regs[i]);
94 }
95
96 /* Run the DSP */
97 snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
98 WM8958_DSP2_RUNR);
99
100 /* And we're off! */
101 snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
102 WM8958_MBC_ENA | WM8958_MBC_SEL_MASK,
103 mbc << WM8958_MBC_SEL_SHIFT |
104 WM8958_MBC_ENA);
105 } else {
106 /* If the DSP is already stopped then noop */
107 if (!(reg & WM8958_DSP2_ENA))
108 return;
109
110 snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
111 WM8958_MBC_ENA, 0);
112 snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
113 WM8958_DSP2_ENA, 0);
114 snd_soc_update_bits(codec, WM8994_CLOCKING_1,
115 WM8958_DSP2CLK_ENA, 0);
116 }
117}
118
119int wm8958_aif_ev(struct snd_soc_dapm_widget *w,
120 struct snd_kcontrol *kcontrol, int event)
121{
122 struct snd_soc_codec *codec = w->codec;
123 int mbc;
124
125 switch (w->shift) {
126 case 13:
127 case 12:
128 mbc = 2;
129 break;
130 case 11:
131 case 10:
132 mbc = 1;
133 break;
134 case 9:
135 case 8:
136 mbc = 0;
137 break;
138 default:
139 BUG();
140 return -EINVAL;
141 }
142
143 switch (event) {
144 case SND_SOC_DAPM_POST_PMU:
145 wm8958_mbc_apply(codec, mbc, 1);
146 break;
147 case SND_SOC_DAPM_POST_PMD:
148 wm8958_mbc_apply(codec, mbc, 0);
149 break;
150 }
151
152 return 0;
153}
154
155static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol,
156 struct snd_ctl_elem_value *ucontrol)
157{
158 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
159 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
160 struct wm8994_pdata *pdata = wm8994->pdata;
161 int value = ucontrol->value.integer.value[0];
162 int reg;
163
164 /* Don't allow on the fly reconfiguration */
165 reg = snd_soc_read(codec, WM8994_CLOCKING_1);
166 if (reg < 0 || reg & WM8958_DSP2CLK_ENA)
167 return -EBUSY;
168
169 if (value >= pdata->num_mbc_cfgs)
170 return -EINVAL;
171
172 wm8994->mbc_cfg = value;
173
174 return 0;
175}
176
177static int wm8958_get_mbc_enum(struct snd_kcontrol *kcontrol,
178 struct snd_ctl_elem_value *ucontrol)
179{
180 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
181 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
182
183 ucontrol->value.enumerated.item[0] = wm8994->mbc_cfg;
184
185 return 0;
186}
187
188static int wm8958_mbc_info(struct snd_kcontrol *kcontrol,
189 struct snd_ctl_elem_info *uinfo)
190{
191 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
192 uinfo->count = 1;
193 uinfo->value.integer.min = 0;
194 uinfo->value.integer.max = 1;
195 return 0;
196}
197
198static int wm8958_mbc_get(struct snd_kcontrol *kcontrol,
199 struct snd_ctl_elem_value *ucontrol)
200{
201 int mbc = kcontrol->private_value;
202 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
203 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
204
205 ucontrol->value.integer.value[0] = wm8994->mbc_ena[mbc];
206
207 return 0;
208}
209
210static int wm8958_mbc_put(struct snd_kcontrol *kcontrol,
211 struct snd_ctl_elem_value *ucontrol)
212{
213 int mbc = kcontrol->private_value;
214 int i;
215 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
216 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
217
218 if (ucontrol->value.integer.value[0] > 1)
219 return -EINVAL;
220
221 for (i = 0; i < ARRAY_SIZE(wm8994->mbc_ena); i++) {
222 if (mbc != i && wm8994->mbc_ena[i]) {
223 dev_dbg(codec->dev, "MBC %d active already\n", mbc);
224 return -EBUSY;
225 }
226 }
227
228 wm8994->mbc_ena[mbc] = ucontrol->value.integer.value[0];
229
230 wm8958_mbc_apply(codec, mbc, wm8994->mbc_ena[mbc]);
231
232 return 0;
233}
234
235#define WM8958_MBC_SWITCH(xname, xval) {\
236 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
237 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,\
238 .info = wm8958_mbc_info, \
239 .get = wm8958_mbc_get, .put = wm8958_mbc_put, \
240 .private_value = xval }
241
242static const struct snd_kcontrol_new wm8958_mbc_snd_controls[] = {
243WM8958_MBC_SWITCH("AIF1DAC1 MBC Switch", 0),
244WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1),
245WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2),
246};
247
248void wm8958_dsp2_init(struct snd_soc_codec *codec)
249{
250 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
251 struct wm8994_pdata *pdata = wm8994->pdata;
252 int ret, i;
253
254 snd_soc_add_controls(codec, wm8958_mbc_snd_controls,
255 ARRAY_SIZE(wm8958_mbc_snd_controls));
256
257 if (!pdata)
258 return;
259
260 if (pdata->num_mbc_cfgs) {
261 struct snd_kcontrol_new control[] = {
262 SOC_ENUM_EXT("MBC Mode", wm8994->mbc_enum,
263 wm8958_get_mbc_enum, wm8958_put_mbc_enum),
264 };
265
266 /* We need an array of texts for the enum API */
267 wm8994->mbc_texts = kmalloc(sizeof(char *)
268 * pdata->num_mbc_cfgs, GFP_KERNEL);
269 if (!wm8994->mbc_texts) {
270 dev_err(wm8994->codec->dev,
271 "Failed to allocate %d MBC config texts\n",
272 pdata->num_mbc_cfgs);
273 return;
274 }
275
276 for (i = 0; i < pdata->num_mbc_cfgs; i++)
277 wm8994->mbc_texts[i] = pdata->mbc_cfgs[i].name;
278
279 wm8994->mbc_enum.max = pdata->num_mbc_cfgs;
280 wm8994->mbc_enum.texts = wm8994->mbc_texts;
281
282 ret = snd_soc_add_controls(wm8994->codec, control, 1);
283 if (ret != 0)
284 dev_err(wm8994->codec->dev,
285 "Failed to add MBC mode controls: %d\n", ret);
286 }
287
288
289}