aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2012-10-05 14:43:18 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2012-10-05 15:17:56 -0400
commit6e87badd3f38e1a095d6e1b13828246c3e8486b5 (patch)
treee9e08f552e7d24750e04563ecb3515a90deb835c /sound
parente10f871190ce2f912317c874a56b9cc417e46e84 (diff)
ASoC: wm2200: Provide initial coefficient loading
Allow a coefficient set provided using the Wolfson callibration tools to be provided along with the firmware files. Currently only coefficient files which configure absolute register addresses are supported. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/wm2200.c194
-rw-r--r--sound/soc/codecs/wmfw.h43
2 files changed, 237 insertions, 0 deletions
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index e3f549b65b08..06d4e612a164 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -1150,6 +1150,192 @@ out:
1150 return ret; 1150 return ret;
1151} 1151}
1152 1152
1153static int wm2200_setup_algs(struct snd_soc_codec *codec, int base)
1154{
1155 struct regmap *regmap = codec->control_data;
1156 struct wmfw_adsp1_id_hdr id;
1157 struct wmfw_adsp1_alg_hdr *alg;
1158 size_t algs;
1159 int zm, dm, pm, ret, i;
1160 __be32 val;
1161
1162 switch (base) {
1163 case WM2200_DSP1_CONTROL_1:
1164 dm = WM2200_DSP1_DM_BASE;
1165 pm = WM2200_DSP1_PM_BASE;
1166 zm = WM2200_DSP1_ZM_BASE;
1167 break;
1168 case WM2200_DSP2_CONTROL_1:
1169 dm = WM2200_DSP2_DM_BASE;
1170 pm = WM2200_DSP2_PM_BASE;
1171 zm = WM2200_DSP2_ZM_BASE;
1172 break;
1173 default:
1174 dev_err(codec->dev, "BASE %x\n", base);
1175 BUG_ON(1);
1176 return -EINVAL;
1177 }
1178
1179 ret = regmap_raw_read(regmap, dm, &id, sizeof(id));
1180 if (ret != 0) {
1181 dev_err(codec->dev, "Failed to read algorithm info: %d\n",
1182 ret);
1183 return ret;
1184 }
1185
1186 algs = be32_to_cpu(id.algs);
1187 dev_info(codec->dev, "Firmware: %x v%d.%d.%d, %d algorithms\n",
1188 be32_to_cpu(id.fw.id),
1189 (be32_to_cpu(id.fw.ver) & 0xff000) >> 16,
1190 (be32_to_cpu(id.fw.ver) & 0xff00) >> 8,
1191 be32_to_cpu(id.fw.ver) & 0xff,
1192 algs);
1193
1194 /* Read the terminator first to validate the length */
1195 ret = regmap_raw_read(regmap, dm +
1196 (sizeof(id) + (algs * sizeof(*alg))) / 2,
1197 &val, sizeof(val));
1198 if (ret != 0) {
1199 dev_err(codec->dev, "Failed to read algorithm list end: %d\n",
1200 ret);
1201 return ret;
1202 }
1203
1204 if (be32_to_cpu(val) != 0xbedead)
1205 dev_warn(codec->dev, "Algorithm list end %x 0x%x != 0xbeadead\n",
1206 (sizeof(id) + (algs * sizeof(*alg))) / 2,
1207 be32_to_cpu(val));
1208
1209 alg = kzalloc(sizeof(*alg) * algs, GFP_KERNEL);
1210 if (!alg)
1211 return -ENOMEM;
1212
1213 ret = regmap_raw_read(regmap, dm + (sizeof(id) / 2),
1214 alg, algs * sizeof(*alg));
1215 if (ret != 0) {
1216 dev_err(codec->dev, "Failed to read algorithm list: %d\n",
1217 ret);
1218 goto out;
1219 }
1220
1221 for (i = 0; i < algs; i++) {
1222 dev_info(codec->dev, "%d: ID %x v%d.%d.%d\n",
1223 i, be32_to_cpu(alg[i].alg.id),
1224 (be32_to_cpu(alg[i].alg.ver) & 0xff000) >> 16,
1225 (be32_to_cpu(alg[i].alg.ver) & 0xff00) >> 8,
1226 be32_to_cpu(alg[i].alg.ver) & 0xff);
1227 }
1228
1229out:
1230 kfree(alg);
1231 return ret;
1232}
1233
1234static int wm2200_load_coeff(struct snd_soc_codec *codec, int base)
1235{
1236 struct regmap *regmap = codec->control_data;
1237 struct wmfw_coeff_hdr *hdr;
1238 struct wmfw_coeff_item *blk;
1239 const struct firmware *firmware;
1240 const char *file, *region_name;
1241 int ret, dm, pm, zm, pos, blocks, type, offset, reg;
1242
1243 switch (base) {
1244 case WM2200_DSP1_CONTROL_1:
1245 file = "wm2200-dsp1.bin";
1246 dm = WM2200_DSP1_DM_BASE;
1247 pm = WM2200_DSP1_PM_BASE;
1248 zm = WM2200_DSP1_ZM_BASE;
1249 break;
1250 case WM2200_DSP2_CONTROL_1:
1251 file = "wm2200-dsp2.bin";
1252 dm = WM2200_DSP2_DM_BASE;
1253 pm = WM2200_DSP2_PM_BASE;
1254 zm = WM2200_DSP2_ZM_BASE;
1255 break;
1256 default:
1257 dev_err(codec->dev, "BASE %x\n", base);
1258 BUG_ON(1);
1259 return -EINVAL;
1260 }
1261
1262 ret = request_firmware(&firmware, file, codec->dev);
1263 if (ret != 0) {
1264 dev_err(codec->dev, "Failed to request '%s'\n", file);
1265 return ret;
1266 }
1267
1268 if (sizeof(*hdr) >= firmware->size) {
1269 dev_err(codec->dev, "%s: file too short, %d bytes\n",
1270 file, firmware->size);
1271 return -EINVAL;
1272 }
1273
1274 hdr = (void*)&firmware->data[0];
1275 if (memcmp(hdr->magic, "WMDR", 4) != 0) {
1276 dev_err(codec->dev, "%s: invalid magic\n", file);
1277 return -EINVAL;
1278 }
1279
1280 dev_dbg(codec->dev, "%s: v%d.%d.%d\n", file,
1281 (le32_to_cpu(hdr->ver) >> 16) & 0xff,
1282 (le32_to_cpu(hdr->ver) >> 8) & 0xff,
1283 le32_to_cpu(hdr->ver) & 0xff);
1284
1285 pos = le32_to_cpu(hdr->len);
1286
1287 blocks = 0;
1288 while (pos < firmware->size &&
1289 pos - firmware->size > sizeof(*blk)) {
1290 blk = (void*)(&firmware->data[pos]);
1291
1292 type = be32_to_cpu(blk->type) & 0xff;
1293 offset = le32_to_cpu(blk->offset) & 0xffffff;
1294
1295 dev_dbg(codec->dev, "%s.%d: %x v%d.%d.%d\n",
1296 file, blocks, le32_to_cpu(blk->id),
1297 (le32_to_cpu(blk->ver) >> 16) & 0xff,
1298 (le32_to_cpu(blk->ver) >> 8) & 0xff,
1299 le32_to_cpu(blk->ver) & 0xff);
1300 dev_dbg(codec->dev, "%s.%d: %d bytes at 0x%x in %x\n",
1301 file, blocks, le32_to_cpu(blk->len), offset, type);
1302
1303 reg = 0;
1304 region_name = "Unknown";
1305 switch (type) {
1306 case WMFW_NAME_TEXT:
1307 case WMFW_INFO_TEXT:
1308 break;
1309 case WMFW_ABSOLUTE:
1310 region_name = "register";
1311 reg = offset;
1312 break;
1313 default:
1314 dev_err(codec->dev, "Unknown region type %x\n", type);
1315 break;
1316 }
1317
1318 if (reg) {
1319 ret = regmap_raw_write(regmap, reg, blk->data,
1320 le32_to_cpu(blk->len));
1321 if (ret != 0) {
1322 dev_err(codec->dev,
1323 "%s.%d: Failed to write to %x in %s\n",
1324 file, blocks, reg, region_name);
1325 }
1326 }
1327
1328 pos += le32_to_cpu(blk->len) + sizeof(*blk);
1329 blocks++;
1330 }
1331
1332 if (pos > firmware->size)
1333 dev_warn(codec->dev, "%s.%d: %d bytes at end of file\n",
1334 file, blocks, pos - firmware->size);
1335
1336 return 0;
1337}
1338
1153static int wm2200_dsp_ev(struct snd_soc_dapm_widget *w, 1339static int wm2200_dsp_ev(struct snd_soc_dapm_widget *w,
1154 struct snd_kcontrol *kcontrol, 1340 struct snd_kcontrol *kcontrol,
1155 int event) 1341 int event)
@@ -1164,6 +1350,14 @@ static int wm2200_dsp_ev(struct snd_soc_dapm_widget *w,
1164 if (ret != 0) 1350 if (ret != 0)
1165 return ret; 1351 return ret;
1166 1352
1353 ret = wm2200_setup_algs(codec, base);
1354 if (ret != 0)
1355 return ret;
1356
1357 ret = wm2200_load_coeff(codec, base);
1358 if (ret != 0)
1359 return ret;
1360
1167 /* Start the core running */ 1361 /* Start the core running */
1168 snd_soc_update_bits(codec, w->reg, 1362 snd_soc_update_bits(codec, w->reg,
1169 WM2200_DSP1_CORE_ENA | WM2200_DSP1_START, 1363 WM2200_DSP1_CORE_ENA | WM2200_DSP1_START,
diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h
index ef37316f0643..5791f8e440ad 100644
--- a/sound/soc/codecs/wmfw.h
+++ b/sound/soc/codecs/wmfw.h
@@ -43,6 +43,49 @@ struct wmfw_region {
43 u8 data[]; 43 u8 data[];
44} __packed; 44} __packed;
45 45
46struct wmfw_id_hdr {
47 __be32 core_id;
48 __be32 core_rev;
49 __be32 id;
50 __be32 ver;
51} __packed;
52
53struct wmfw_adsp1_id_hdr {
54 struct wmfw_id_hdr fw;
55 __be32 zm;
56 __be32 dm;
57 __be32 algs;
58} __packed;
59
60struct wmfw_alg_hdr {
61 __be32 id;
62 __be32 ver;
63} __packed;
64
65struct wmfw_adsp1_alg_hdr {
66 struct wmfw_alg_hdr alg;
67 __be32 zm;
68 __be32 dm;
69} __packed;
70
71struct wmfw_coeff_hdr {
72 u8 magic[4];
73 __le32 len;
74 __le32 ver;
75 u8 data[];
76} __packed;
77
78struct wmfw_coeff_item {
79 union {
80 __be32 type;
81 __le32 offset;
82 };
83 __le32 id;
84 __le32 ver;
85 __le32 sr;
86 __le32 len;
87 u8 data[];
88} __packed;
46#define WMFW_ADSP1 1 89#define WMFW_ADSP1 1
47 90
48#define WMFW_ABSOLUTE 0xf0 91#define WMFW_ABSOLUTE 0xf0