1 #ifndef AIC3XXX_CFW_HOST_BLD
2 # include <linux/module.h>
3 # include <linux/delay.h>
4 # define warn(fmt, ...) printk(fmt "\n", ##__VA_ARGS__)
5 # define error(fmt, ...) printk(fmt "\n", ##__VA_ARGS__)
13 #include "aic3xxx_cfw.h"
14 #include "aic3xxx_cfw_ops.h"
18 * Firmware version numbers are used to make sure that the
19 * host and target code stay in sync. It is _not_ recommended
20 * to provide this number from the outside (E.g., from a makefile)
21 * Instead, a set of automated tools are relied upon to keep the numbers
22 * in sync at the time of host testing.
24 #define CFW_FW_VERSION 0x000100AF
27 static int aic3xxx_cfw_dlimage(void *pv, cfw_image *pim);
28 static int aic3xxx_cfw_dlcfg(void *pv, cfw_image *pim);
29 static void aic3xxx_cfw_dlcmds(void *pv, cfw_block *pb);
30 cfw_meta_register *aic3xxx_read_meta(cfw_block *pb, int i);
31 void aic3xxx_wait(void *p, unsigned int reg, u8 mask, u8 data);
32 static cfw_project *aic3xxx_cfw_unpickle(void *p, int n);
34 #if defined(AIC3XXX_CFW_HOST_BLD)
36 extern const aic3xxx_codec_ops dummy_codec_ops;
37 extern void *dummy_codec_ops_obj;
42 for (i=0; i<val*10; i++);
44 void *aic3xxx_cfw_init(void *pcfw, int n)
46 cfw_state *ps = malloc(sizeof(cfw_state));
47 ps->ops = &dummy_codec_ops;
48 ps->ops_obj = dummy_codec_ops_obj;
49 aic3xxx_cfw_reload(ps, pcfw, n);
52 void *aic3xxx_cfw_getpjt(void *ps)
54 return ((cfw_state *)ps)->pjt;
59 int aic3xxx_cfw_reload(void *pv, void *pcfw, int n)
62 ps->pjt = aic3xxx_cfw_unpickle(pcfw, n);
69 int aic3xxx_cfw_setmode(void *pv, int mode)
72 cfw_project *pjt = ps->pjt;
73 return aic3xxx_cfw_setmode_cfg(pv, mode, pjt->mode[mode]->cfg);
75 int aic3xxx_cfw_setcfg(void *pv, int cfg)
78 cfw_project *pjt = ps->pjt;
80 if (ps->cur_pfw >= pjt->npfw)
81 return -1; // Non miniDSP
82 if (!(pjt->mode[ps->cur_mode]->supported_cfgs&(1 << cfg)))
84 if (ps->cur_cfg == cfg)
86 pfw = pjt->pfw[ps->cur_pfw];
88 return aic3xxx_cfw_dlcfg(pv, pfw->ovly_cfg[ps->cur_ovly][ps->cur_cfg]);
90 int aic3xxx_cfw_setmode_cfg(void *pv, int mode, int cfg)
93 cfw_project *pjt = ps->pjt;
98 if (mode >= pjt->nmode)
100 if (pjt->mode[mode]->pfw < pjt->npfw) { // New mode uses miniDSP
101 // FIXME: Add support for entry and exit sequences
102 pfw = pjt->pfw[pjt->mode[mode]->pfw];
103 // Make sure cfg is valid and supported in this mode
104 if (cfg >= pfw->ncfg ||
105 !(pjt->mode[mode]->supported_cfgs&(1u<<cfg)))
108 * Decisions about which miniDSP to stop/restart are taken
109 * on the basis of sections present in the _base_ image
110 * This allows for correct sync mode operation even in cases
111 * where the base PFW uses both miniDSPs where a particular
112 * overlay applies only to one
115 if (im->block[CFW_BLOCK_A_INST])
116 which |= AIC3XX_COPS_MDSP_A;
117 if (im->block[CFW_BLOCK_D_INST])
118 which |= AIC3XX_COPS_MDSP_D;
120 if (pjt->mode[mode]->pfw != ps->cur_pfw) { // New mode requires different PFW
121 ps->cur_pfw = pjt->mode[mode]->pfw;
125 which = ps->ops->stop(ps->ops_obj, which);
126 aic3xxx_cfw_dlimage(pv, im);
127 if (pjt->mode[mode]->ovly && pjt->mode[mode]->ovly < pfw->novly) {
128 // New mode uses ovly
129 aic3xxx_cfw_dlimage(pv, pfw->ovly_cfg[pjt->mode[mode]->ovly][cfg]);
131 // new mode needs only a cfg change
132 aic3xxx_cfw_dlimage(pv, pfw->ovly_cfg[0][cfg]);
134 ps->ops->restore(ps->ops_obj, which);
136 } else if (pjt->mode[mode]->ovly != ps->cur_ovly) {
137 // New mode requires only an ovly change
138 which = ps->ops->stop(ps->ops_obj, which);
139 aic3xxx_cfw_dlimage(pv, pfw->ovly_cfg[pjt->mode[mode]->ovly][cfg]);
140 ps->ops->restore(ps->ops_obj, which);
141 } else if (cfg != ps->cur_cfg) { // New mode requires only a cfg change
142 aic3xxx_cfw_dlcfg(pv, pfw->ovly_cfg[pjt->mode[mode]->ovly][cfg]);
144 ps->cur_ovly = pjt->mode[mode]->ovly;
147 // FIXME: Update PLL settings if present
148 // FIXME: This is hack and needs to go one way or another
150 aic3xxx_cfw_set_pll(pv, 0);
152 } else if (pjt->mode[mode]->pfw == 0xFF) { // Bypass mode
155 warn("Bad pfw setting detected (%d). Max pfw=%d", pjt->mode[mode]->pfw,
159 warn("setmode_cfg: DONE (mode=%d pfw=%d ovly=%d cfg=%d)", ps->cur_mode, ps->cur_pfw, ps->cur_ovly, ps->cur_cfg);
162 int aic3xxx_cfw_transition(void *pv, char *ttype)
166 for (i = 0; i < CFW_TRN_N; ++i) {
167 if (!strcasecmp(ttype, cfw_transition_id[i])) {
168 if (ps->pjt->transition[i]) {
169 DBG("Sending transition %s[%d]", ttype, i);
170 aic3xxx_cfw_dlcmds(pv, ps->pjt->transition[i]->block);
175 warn("Transition %s not present or invalid", ttype);
178 int aic3xxx_cfw_set_pll(void *pv, int asi)
180 // FIXME: no error checks!!
182 cfw_project *pjt = ps->pjt;
183 cfw_pfw *pfw = pjt->pfw[pjt->mode[ps->cur_mode]->pfw];
185 warn("Configuring PLL for ASI%d using PFW%d", asi,
186 pjt->mode[ps->cur_mode]->pfw);
187 aic3xxx_cfw_dlcmds(pv, pfw->pll);
191 static void aic3xxx_cfw_dlcmds(void *pv, cfw_block *pb)
196 while (i < pb->ncmds) {
197 if (CFW_BLOCK_BURSTS(pb->type))
198 ps->ops->bulk_write(ps->ops_obj, pb->cmd[i].burst->reg.bpod,
199 pb->cmd[i].burst->length,
200 pb->cmd[i].burst->data);
202 cfw_meta_delay d = pb->cmd[i].reg.meta.delay;
203 cfw_meta_bitop b = pb->cmd[i].reg.meta.bitop;
204 switch (pb->cmd[i].reg.meta.mcmd) {
208 case CFW_META_UPDTBITS:
209 ps->ops->set_bits(ps->ops_obj, pb->cmd[i+1].reg.bpod,
210 b.mask, pb->cmd[i+1].reg.data);
213 case CFW_META_WAITBITS:
214 aic3xxx_wait(ps, pb->cmd[i+1].reg.bpod,
215 b.mask, pb->cmd[i+1].reg.data);
220 ps->ops->lock(ps->ops_obj);
224 error("Attempt to unlock without first locking");
225 ps->ops->unlock(ps->ops_obj);
230 ps->ops->reg_write(ps->ops_obj, pb->cmd[i].reg.bpod, pb->cmd[i].reg.data);
236 error("exiting blkcmds with lock ON");
239 void aic3xxx_wait(void *p, unsigned int reg, u8 mask, u8 data)
242 while ((ps->ops->reg_read(ps->ops_obj, reg)&mask) != data)
246 static int aic3xxx_cfw_dlcfg(void *pv, cfw_image *pim)
249 int i,run_state,swap;
256 .mdsp=AIC3XX_COPS_MDSP_A,
257 .swap=AIC3XX_ABUF_MDSP_A,
258 .buf_a=CFW_BLOCK_A_A_COEF,
259 .buf_b=CFW_BLOCK_A_B_COEF
262 .mdsp=AIC3XX_COPS_MDSP_D,
263 .swap=AIC3XX_ABUF_MDSP_D1,
264 .buf_a=CFW_BLOCK_D_A1_COEF,
265 .buf_b=CFW_BLOCK_D_B1_COEF
268 .mdsp=AIC3XX_COPS_MDSP_D,
269 .swap=AIC3XX_ABUF_MDSP_D2,
270 .buf_a=CFW_BLOCK_D_A2_COEF,
271 .buf_b=CFW_BLOCK_D_B2_COEF
274 DBG("Download CFG %s", pim->name);
275 run_state = ps->ops->lock(ps->ops_obj);
277 for (i = 0; i < sizeof(csecs)/sizeof(csecs[0]); ++i) {
278 if (pim->block[csecs[i].buf_a]) {
279 if (run_state & csecs[i].mdsp) {
280 aic3xxx_cfw_dlcmds(pv, pim->block[csecs[i].buf_a]);
281 swap |= csecs[i].swap;
282 aic3xxx_cfw_dlcmds(pv, pim->block[csecs[i].buf_b]);
284 aic3xxx_cfw_dlcmds(pv, pim->block[csecs[i].buf_a]);
285 aic3xxx_cfw_dlcmds(pv, pim->block[csecs[i].buf_b]);
289 if (swap) // Check needed? FIXME
290 ps->ops->bswap(ps->ops_obj, swap);
291 ps->ops->unlock(ps->ops_obj);
294 static int aic3xxx_cfw_dlimage(void *pv, cfw_image *pim)
296 //cfw_state *ps = pv;
298 DBG("Download IMAGE %s", pim->name);
299 for (i = 0; i < CFW_BLOCK_N; ++i)
301 aic3xxx_cfw_dlcmds(pv, pim->block[i]);
305 # define FW_NDX2PTR(x, b) do { \
306 x = (void *)((u8 *)(b) + ((int)(x))); \
308 static void aic3xxx_cfw_unpickle_block(cfw_block *pb, void *p)
311 if (CFW_BLOCK_BURSTS(pb->type)) {
312 for (i = 0; i < pb->ncmds; ++i) {
313 FW_NDX2PTR(pb->cmd[i].burst, p);
317 static void aic3xxx_cfw_unpickle_image(cfw_image *im, void *p)
320 for (i = 0; i < CFW_BLOCK_N; ++i)
322 FW_NDX2PTR(im->block[i], p);
323 aic3xxx_cfw_unpickle_block(im->block[i], p);
326 #ifndef AIC3XXX_CFW_HOST_BLD
329 unsigned int crc32(unsigned int *pdata, int n)
331 u32 crc = 0, i, crc_poly = 0x04C11DB7; /* CRC - 32 */
336 for (i = 0; i < (n >> 2); i++) {
338 while (--bits >= 0) {
339 msb = crc & 0x80000000;
340 crc = (crc << 1) ^ ((*pdata >> bits) & 1);
342 crc = crc ^ crc_poly;
351 residue_value = (*pdata & 0xFF);
355 residue_value = (*pdata & 0xFFFF);
359 residue_value = (*pdata & 0xFFFFFF);
365 while (--bits >= 0) {
366 msb = crc & 0x80000000;
367 crc = (crc << 1) ^ ((residue_value >> bits) & 1);
369 crc = crc ^ crc_poly;
374 static int crc_chk(void *p, int n)
376 cfw_project *pjt = (void *)p;
377 u32 crc = pjt->cksum, crc_comp;
379 DBG("Entering crc %d",n);
380 crc_comp = crc32(p, n);
381 if (crc_comp != crc) {
382 DBG("CRC mismatch 0x%08X != 0x%08X", crc, crc_comp);
383 return 0; // Dead code
389 #ifndef AIC3XXX_CFW_HOST_BLD
392 cfw_project *aic3xxx_cfw_unpickle(void *p, int n)
394 cfw_project *pjt = p;
396 if (pjt->magic != CFW_FW_MAGIC ||
398 pjt->bmagic != CFW_FW_VERSION ||
400 error("magic:0x%08X!=0x%08X || size:%d!=%d || version:0x%08X!=0x%08X",
401 pjt->magic, CFW_FW_MAGIC, pjt->size, n, pjt->cksum,
405 DBG("Loaded firmware inside unpickle\n");
407 for (i = 0; i < CFW_MAX_TRANSITIONS; i++) {
408 if (pjt->transition[i]) {
409 FW_NDX2PTR(pjt->transition[i], p);
410 FW_NDX2PTR(pjt->transition[i]->block, p);
411 aic3xxx_cfw_unpickle_block(pjt->transition[i]->block, p);
415 for (i = 0; i < pjt->npfw; i++) {
416 DBG("loading pfw %d\n",i);
417 FW_NDX2PTR(pjt->pfw[i], p);
418 if (pjt->pfw[i]->base) {
419 FW_NDX2PTR(pjt->pfw[i]->base, p);
420 aic3xxx_cfw_unpickle_image(pjt->pfw[i]->base, p);
422 if (pjt->pfw[i]->pll) {
423 FW_NDX2PTR(pjt->pfw[i]->pll, p);
424 aic3xxx_cfw_unpickle_block(pjt->pfw[i]->pll, p);
426 for (j =0; j < pjt->pfw[i]->novly; ++j)
427 for (k =0; k < pjt->pfw[i]->ncfg; ++k) {
428 FW_NDX2PTR(pjt->pfw[i]->ovly_cfg[j][k], p);
429 aic3xxx_cfw_unpickle_image(pjt->pfw[i]->ovly_cfg[j][k], p);
433 DBG("loaded pfw's\n");
434 for (i = 0; i < pjt->nmode; i++) {
435 FW_NDX2PTR(pjt->mode[i], p);
436 if (pjt->mode[i]->entry) {
437 FW_NDX2PTR(pjt->mode[i]->entry, p);
438 aic3xxx_cfw_unpickle_block(pjt->mode[i]->entry, p);
440 if (pjt->mode[i]->exit) {
441 FW_NDX2PTR(pjt->mode[i]->exit, p);
442 aic3xxx_cfw_unpickle_block(pjt->mode[i]->exit, p);
445 DBG("loaded modes\n");