aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2011-03-11 13:09:04 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2011-03-22 14:41:20 -0400
commitfbbf592002ee46ed14d5bd88f1150c604b34e705 (patch)
treeb4fe275ab768a411c2ca85fa925c40d813d5df19
parentaf9af866020ea341aca32123b3109b6a9408dd8c (diff)
ASoC: Support download of WM8958 MBC firmware
Allow userspace to supply an update to the ROM firmware. The firmware request is non-blocking so userspace can load the firmware at its leisure without delaying startup, the driver will begin using the firmware the next time MBC is started after it has been supplied. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Acked-by: Liam Girdwood <lrg@ti.com>
-rw-r--r--sound/soc/codecs/wm8958-dsp2.c223
-rw-r--r--sound/soc/codecs/wm8994.c4
-rw-r--r--sound/soc/codecs/wm8994.h4
3 files changed, 231 insertions, 0 deletions
diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c
index f07775ebec04..58fe40416709 100644
--- a/sound/soc/codecs/wm8958-dsp2.c
+++ b/sound/soc/codecs/wm8958-dsp2.c
@@ -30,10 +30,212 @@
30 30
31#include "wm8994.h" 31#include "wm8994.h"
32 32
33#define WM_FW_BLOCK_INFO 0xff
34#define WM_FW_BLOCK_PM 0x00
35#define WM_FW_BLOCK_X 0x01
36#define WM_FW_BLOCK_Y 0x02
37#define WM_FW_BLOCK_Z 0x03
38#define WM_FW_BLOCK_I 0x06
39#define WM_FW_BLOCK_A 0x08
40#define WM_FW_BLOCK_C 0x0c
41
42static int wm8958_dsp2_fw(struct snd_soc_codec *codec, const char *name,
43 const struct firmware *fw, bool check)
44{
45 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
46 u64 data64;
47 u32 data32;
48 const u8 *data;
49 char *str;
50 size_t block_len, len;
51 int ret = 0;
52
53 /* Suppress unneeded downloads */
54 if (wm8994->cur_fw == fw)
55 return 0;
56
57 if (fw->size < 32) {
58 dev_err(codec->dev, "%s: firmware too short\n", name);
59 goto err;
60 }
61
62 if (memcmp(fw->data, "WMFW", 4) != 0) {
63 dev_err(codec->dev, "%s: firmware has bad file magic %08x\n",
64 name, data32);
65 goto err;
66 }
67
68 memcpy(&data32, fw->data + 4, sizeof(data32));
69 len = be32_to_cpu(data32);
70
71 memcpy(&data32, fw->data + 8, sizeof(data32));
72 data32 = be32_to_cpu(data32);
73 if ((data32 >> 24) & 0xff) {
74 dev_err(codec->dev, "%s: unsupported firmware version %d\n",
75 name, (data32 >> 24) & 0xff);
76 goto err;
77 }
78 if ((data32 & 0xffff) != 8958) {
79 dev_err(codec->dev, "%s: unsupported target device %d\n",
80 name, data32 & 0xffff);
81 goto err;
82 }
83 if (((data32 >> 16) & 0xff) != 0xc) {
84 dev_err(codec->dev, "%s: unsupported target core %d\n",
85 name, (data32 >> 16) & 0xff);
86 goto err;
87 }
88
89 if (check) {
90 memcpy(&data64, fw->data + 24, sizeof(u64));
91 dev_info(codec->dev, "%s timestamp %llx\n",
92 name, be64_to_cpu(data64));
93 } else {
94 snd_soc_write(codec, 0x102, 0x2);
95 snd_soc_write(codec, 0x900, 0x2);
96 }
97
98 data = fw->data + len;
99 len = fw->size - len;
100 while (len) {
101 if (len < 12) {
102 dev_err(codec->dev, "%s short data block of %d\n",
103 name, len);
104 goto err;
105 }
106
107 memcpy(&data32, data + 4, sizeof(data32));
108 block_len = be32_to_cpu(data32);
109 if (block_len + 8 > len) {
110 dev_err(codec->dev, "%d byte block longer than file\n",
111 block_len);
112 goto err;
113 }
114 if (block_len == 0) {
115 dev_err(codec->dev, "Zero length block\n");
116 goto err;
117 }
118
119 memcpy(&data32, data, sizeof(data32));
120 data32 = be32_to_cpu(data32);
121
122 switch ((data32 >> 24) & 0xff) {
123 case WM_FW_BLOCK_INFO:
124 /* Informational text */
125 if (!check)
126 break;
127
128 str = kzalloc(block_len + 1, GFP_KERNEL);
129 if (str) {
130 memcpy(str, data + 8, block_len);
131 dev_info(codec->dev, "%s: %s\n", name, str);
132 kfree(str);
133 } else {
134 dev_err(codec->dev, "Out of memory\n");
135 }
136 break;
137 case WM_FW_BLOCK_PM:
138 case WM_FW_BLOCK_X:
139 case WM_FW_BLOCK_Y:
140 case WM_FW_BLOCK_Z:
141 case WM_FW_BLOCK_I:
142 case WM_FW_BLOCK_A:
143 case WM_FW_BLOCK_C:
144 dev_dbg(codec->dev, "%s: %d bytes of %x@%x\n", name,
145 block_len, (data32 >> 24) & 0xff,
146 data32 & 0xffffff);
147
148 if (check)
149 break;
150
151 data32 &= 0xffffff;
152
153 wm8994_bulk_write(codec->control_data,
154 data32 & 0xffffff,
155 block_len / 2,
156 (void *)(data + 8));
157
158 break;
159 default:
160 dev_warn(codec->dev, "%s: unknown block type %d\n",
161 name, (data32 >> 24) & 0xff);
162 break;
163 }
164
165 /* Round up to the next 32 bit word */
166 block_len += block_len % 4;
167
168 data += block_len + 8;
169 len -= block_len + 8;
170 }
171
172 if (!check) {
173 dev_dbg(codec->dev, "%s: download done\n", name);
174 wm8994->cur_fw = fw;
175 } else {
176 dev_info(codec->dev, "%s: got firmware\n", name);
177 }
178
179 goto ok;
180
181err:
182 ret = -EINVAL;
183ok:
184 if (!check) {
185 snd_soc_write(codec, 0x900, 0x0);
186 snd_soc_write(codec, 0x102, 0x0);
187 }
188
189 return ret;
190}
191
33static void wm8958_mbc_apply(struct snd_soc_codec *codec, int mbc, int start) 192static void wm8958_mbc_apply(struct snd_soc_codec *codec, int mbc, int start)
34{ 193{
35 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); 194 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
36 struct wm8994_pdata *pdata = wm8994->pdata; 195 struct wm8994_pdata *pdata = wm8994->pdata;
196 int i;
197
198 /* If the DSP is already running then noop */
199 if (snd_soc_read(codec, WM8958_DSP2_PROGRAM) & WM8958_DSP2_ENA)
200 return;
201
202 /* If we have MBC firmware download it */
203 if (wm8994->mbc)
204 wm8958_dsp2_fw(codec, "MBC", wm8994->mbc, false);
205
206 snd_soc_update_bits(codec, WM8958_DSP2_PROGRAM,
207 WM8958_DSP2_ENA, WM8958_DSP2_ENA);
208
209 /* If we've got user supplied MBC settings use them */
210 if (pdata && pdata->num_mbc_cfgs) {
211 struct wm8958_mbc_cfg *cfg
212 = &pdata->mbc_cfgs[wm8994->mbc_cfg];
213
214 for (i = 0; i < ARRAY_SIZE(cfg->coeff_regs); i++)
215 snd_soc_write(codec, i + WM8958_MBC_BAND_1_K_1,
216 cfg->coeff_regs[i]);
217
218 for (i = 0; i < ARRAY_SIZE(cfg->cutoff_regs); i++)
219 snd_soc_write(codec,
220 i + WM8958_MBC_BAND_2_LOWER_CUTOFF_C1_1,
221 cfg->cutoff_regs[i]);
222 }
223
224 /* Run the DSP */
225 snd_soc_write(codec, WM8958_DSP2_EXECCONTROL,
226 WM8958_DSP2_RUNR);
227
228 /* And we're off! */
229 snd_soc_update_bits(codec, WM8958_DSP2_CONFIG,
230 WM8958_MBC_ENA |
231 WM8958_MBC_SEL_MASK,
232 path << WM8958_MBC_SEL_SHIFT |
233 WM8958_MBC_ENA);
234}
235
236static void wm8958_dsp_apply(struct snd_soc_codec *codec, int path, int start)
237{
238 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
37 int pwr_reg = snd_soc_read(codec, WM8994_POWER_MANAGEMENT_5); 239 int pwr_reg = snd_soc_read(codec, WM8994_POWER_MANAGEMENT_5);
38 int ena, reg, aif, i; 240 int ena, reg, aif, i;
39 241
@@ -76,6 +278,10 @@ static void wm8958_mbc_apply(struct snd_soc_codec *codec, int mbc, int start)
76 & WM8994_AIF2CLK_ENA_MASK)) 278 & WM8994_AIF2CLK_ENA_MASK))
77 return; 279 return;
78 280
281 /* If we have MBC firmware download it */
282 if (wm8994->mbc && wm8994->mbc_ena[mbc])
283 wm8958_dsp2_fw(codec, "MBC", wm8994->mbc, false);
284
79 /* Switch the clock over to the appropriate AIF */ 285 /* Switch the clock over to the appropriate AIF */
80 snd_soc_update_bits(codec, WM8994_CLOCKING_1, 286 snd_soc_update_bits(codec, WM8994_CLOCKING_1,
81 WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA, 287 WM8958_DSP2CLK_SRC | WM8958_DSP2CLK_ENA,
@@ -238,6 +444,18 @@ WM8958_MBC_SWITCH("AIF1DAC2 MBC Switch", 1),
238WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2), 444WM8958_MBC_SWITCH("AIF2DAC MBC Switch", 2),
239}; 445};
240 446
447static void wm8958_mbc_loaded(const struct firmware *fw, void *context)
448{
449 struct snd_soc_codec *codec = context;
450 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
451
452 if (fw && wm8958_dsp2_fw(codec, "MBC", fw, true) != 0) {
453 mutex_lock(&codec->mutex);
454 wm8994->mbc = fw;
455 mutex_unlock(&codec->mutex);
456 }
457}
458
241void wm8958_dsp2_init(struct snd_soc_codec *codec) 459void wm8958_dsp2_init(struct snd_soc_codec *codec)
242{ 460{
243 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); 461 struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
@@ -247,6 +465,11 @@ void wm8958_dsp2_init(struct snd_soc_codec *codec)
247 snd_soc_add_controls(codec, wm8958_mbc_snd_controls, 465 snd_soc_add_controls(codec, wm8958_mbc_snd_controls,
248 ARRAY_SIZE(wm8958_mbc_snd_controls)); 466 ARRAY_SIZE(wm8958_mbc_snd_controls));
249 467
468 /* We don't require firmware and don't want to delay boot */
469 request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
470 "wm8958_mbc.wfw", codec->dev, GFP_KERNEL,
471 codec, wm8958_mbc_loaded);
472
250 if (!pdata) 473 if (!pdata)
251 return; 474 return;
252 475
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index bdd1ac75178a..f622ff691b41 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -1922,6 +1922,8 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
1922 WM8994_VMID_BUF_ENA | 1922 WM8994_VMID_BUF_ENA |
1923 WM8994_VMID_RAMP_MASK, 0); 1923 WM8994_VMID_RAMP_MASK, 0);
1924 1924
1925 wm8994->cur_fw = NULL;
1926
1925 pm_runtime_put(codec->dev); 1927 pm_runtime_put(codec->dev);
1926 } 1928 }
1927 break; 1929 break;
@@ -3136,6 +3138,8 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec)
3136 free_irq(wm8994->micdet_irq, wm8994); 3138 free_irq(wm8994->micdet_irq, wm8994);
3137 break; 3139 break;
3138 } 3140 }
3141 if (wm8994->mbc)
3142 release_firmware(wm8994->mbc);
3139 kfree(wm8994->retune_mobile_texts); 3143 kfree(wm8994->retune_mobile_texts);
3140 kfree(wm8994->drc_texts); 3144 kfree(wm8994->drc_texts);
3141 kfree(wm8994); 3145 kfree(wm8994);
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
index 93a6cf1e1308..1aa365b31b08 100644
--- a/sound/soc/codecs/wm8994.h
+++ b/sound/soc/codecs/wm8994.h
@@ -10,6 +10,7 @@
10#define _WM8994_H 10#define _WM8994_H
11 11
12#include <sound/soc.h> 12#include <sound/soc.h>
13#include <linux/firmware.h>
13 14
14#include "wm_hubs.h" 15#include "wm_hubs.h"
15 16
@@ -114,6 +115,9 @@ struct wm8994_priv {
114 115
115 unsigned int aif1clk_disable:1; 116 unsigned int aif1clk_disable:1;
116 unsigned int aif2clk_disable:1; 117 unsigned int aif2clk_disable:1;
118
119 const struct firmware *cur_fw;
120 const struct firmware *mbc;
117}; 121};
118 122
119#endif 123#endif