ASoC: es8323: update codec es8323 driver
[firefly-linux-kernel-4.4.55.git] / sound / soc / codecs / aic3xxx_cfw_ops.c
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__)
6 #else
7 #   define _GNU_SOURCE
8 #   include <stdlib.h>
9 #   include "utils.h"
10 #   include <string.h>
11 #endif
12
13 #include "aic3xxx_cfw.h"
14 #include "aic3xxx_cfw_ops.h"
15
16
17 /*
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.
23  */
24 #define CFW_FW_VERSION 0x000100AF
25
26
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);
33
34 #if defined(AIC3XXX_CFW_HOST_BLD)
35 // Host test only...
36 extern const aic3xxx_codec_ops dummy_codec_ops;
37 extern void *dummy_codec_ops_obj;
38
39 void mdelay(int val)
40 {
41     int i;
42     for (i=0; i<val*10; i++);
43 }
44 void *aic3xxx_cfw_init(void *pcfw, int n)
45 {
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);
50     return ps;
51 }
52 void *aic3xxx_cfw_getpjt(void *ps)
53 {
54     return ((cfw_state *)ps)->pjt;
55 }
56 // ...till here
57 #endif
58
59 int aic3xxx_cfw_reload(void *pv, void *pcfw, int n)
60 {
61     cfw_state *ps = pv;
62     ps->pjt = aic3xxx_cfw_unpickle(pcfw, n);
63     ps->cur_mode =
64             ps->cur_pfw =
65             ps->cur_ovly =
66             ps->cur_cfg = -1;
67     return 0;
68 }
69 int aic3xxx_cfw_setmode(void *pv, int mode)
70 {
71     cfw_state *ps = pv;
72     cfw_project *pjt = ps->pjt;
73     return aic3xxx_cfw_setmode_cfg(pv, mode, pjt->mode[mode]->cfg);
74 }
75 int aic3xxx_cfw_setcfg(void *pv, int cfg)
76 {
77     cfw_state *ps = pv;
78     cfw_project *pjt = ps->pjt;
79     cfw_pfw *pfw;
80     if (ps->cur_pfw >= pjt->npfw)
81         return -1; // Non miniDSP
82     if (!(pjt->mode[ps->cur_mode]->supported_cfgs&(1 << cfg)))
83         return -1;
84     if (ps->cur_cfg == cfg)
85         return 0;
86     pfw = pjt->pfw[ps->cur_pfw];
87     ps->cur_cfg = cfg;
88     return aic3xxx_cfw_dlcfg(pv, pfw->ovly_cfg[ps->cur_ovly][ps->cur_cfg]);
89 }
90 int aic3xxx_cfw_setmode_cfg(void *pv, int mode, int cfg)
91 {
92     cfw_state *ps = pv;
93     cfw_project *pjt = ps->pjt;
94     int which = 0;
95         cfw_pfw *pfw;
96         cfw_image *im;
97         
98     if (mode >= pjt->nmode)
99         return -1;
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)))
106             return -1;
107         /*
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
113          */
114         im = pfw->base;
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;
119
120         if (pjt->mode[mode]->pfw !=  ps->cur_pfw) { // New mode requires different PFW
121             ps->cur_pfw = pjt->mode[mode]->pfw;
122             ps->cur_ovly = 0;
123             ps->cur_cfg = 0;
124
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]);
130             } else if (cfg) {
131                 // new mode needs only a cfg change
132                 aic3xxx_cfw_dlimage(pv, pfw->ovly_cfg[0][cfg]);
133             }
134             ps->ops->restore(ps->ops_obj, which);
135
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]);
143         }
144         ps->cur_ovly = pjt->mode[mode]->ovly;
145         ps->cur_cfg = cfg;
146
147         // FIXME: Update PLL settings if present
148         // FIXME: This is hack and needs to go one way or another
149         ps->cur_mode = mode;
150         aic3xxx_cfw_set_pll(pv, 0);
151
152     } else if (pjt->mode[mode]->pfw  == 0xFF) { // Bypass mode
153         // FIXME
154     } else { // Error
155         warn("Bad pfw setting detected (%d).  Max pfw=%d", pjt->mode[mode]->pfw,
156                 pjt->npfw);
157     }
158     ps->cur_mode = mode;
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);
160     return 0;
161 }
162 int aic3xxx_cfw_transition(void *pv, char *ttype)
163 {
164     cfw_state *ps = pv;
165      int i;
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);
171             }
172             return 0;
173          }
174      }
175      warn("Transition %s not present or invalid", ttype);
176      return 0;
177 }
178 int aic3xxx_cfw_set_pll(void *pv, int asi)
179 {
180     // FIXME: no error checks!!
181     cfw_state *ps = pv;
182     cfw_project *pjt = ps->pjt;
183     cfw_pfw *pfw = pjt->pfw[pjt->mode[ps->cur_mode]->pfw];
184     if (pfw->pll) {
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);
188     }
189     return 0;
190 }
191 static void aic3xxx_cfw_dlcmds(void *pv, cfw_block *pb)
192 {
193     cfw_state *ps = pv;
194     int i = 0, lock = 0;
195
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);
201         else {
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) {
205             case CFW_META_DELAY:
206                 mdelay(d.delay);
207                 break;
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);
211                 i++;
212                 break;
213             case CFW_META_WAITBITS:
214                 aic3xxx_wait(ps,  pb->cmd[i+1].reg.bpod,
215                         b.mask, pb->cmd[i+1].reg.data);
216                 i++;
217                 break;
218             case CFW_META_LOCK:
219                 if (d.delay) {
220                     ps->ops->lock(ps->ops_obj);
221                     lock = 1;
222                 } else {
223                     if (!lock)
224                         error("Attempt to unlock without first locking");
225                     ps->ops->unlock(ps->ops_obj);
226                     lock = 0;
227                 }
228                 break;
229             default:
230                 ps->ops->reg_write(ps->ops_obj, pb->cmd[i].reg.bpod,  pb->cmd[i].reg.data);
231             }
232         }
233         ++i;
234     }
235     if (lock)
236         error("exiting blkcmds with lock ON");
237 }
238
239 void aic3xxx_wait(void *p, unsigned int reg, u8 mask, u8 data)
240 {
241     cfw_state *ps = p;
242     while ((ps->ops->reg_read(ps->ops_obj, reg)&mask) != data)
243         mdelay(2);
244 }
245
246 static int aic3xxx_cfw_dlcfg(void *pv, cfw_image *pim)
247 {
248     cfw_state *ps = pv;
249     int i,run_state,swap;
250     struct {
251         u32 mdsp;
252         int buf_a, buf_b;
253         u32 swap;
254     } csecs[] = {
255         {
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
260         },
261         {
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
266         },
267         {
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
272         },
273     };
274     DBG("Download CFG %s", pim->name);
275     run_state = ps->ops->lock(ps->ops_obj);
276     swap = 0;
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]);
283             } else {
284                 aic3xxx_cfw_dlcmds(pv, pim->block[csecs[i].buf_a]);
285                 aic3xxx_cfw_dlcmds(pv, pim->block[csecs[i].buf_b]);
286             }
287         }
288     }
289     if (swap) // Check needed? FIXME
290         ps->ops->bswap(ps->ops_obj, swap);
291     ps->ops->unlock(ps->ops_obj);
292     return 0;
293 }
294 static int aic3xxx_cfw_dlimage(void *pv, cfw_image *pim)
295 {
296     //cfw_state *ps = pv;
297     int i;
298     DBG("Download IMAGE %s", pim->name);
299     for (i = 0; i < CFW_BLOCK_N; ++i)
300         if (pim->block[i])
301             aic3xxx_cfw_dlcmds(pv, pim->block[i]);
302     return 0;
303 }
304
305 #   define FW_NDX2PTR(x, b) do {                        \
306         x = (void *)((u8 *)(b) + ((int)(x)));           \
307     } while (0)
308 static void aic3xxx_cfw_unpickle_block(cfw_block *pb, void *p)
309 {
310     int i;
311     if (CFW_BLOCK_BURSTS(pb->type)) {
312         for (i = 0; i < pb->ncmds; ++i) {
313             FW_NDX2PTR(pb->cmd[i].burst, p);
314         }
315     }
316 }
317 static void aic3xxx_cfw_unpickle_image(cfw_image *im, void *p)
318 {
319     int i;
320     for (i = 0; i < CFW_BLOCK_N; ++i)
321         if (im->block[i]) {
322             FW_NDX2PTR(im->block[i], p);
323             aic3xxx_cfw_unpickle_block(im->block[i], p);
324         }
325 }
326 #ifndef AIC3XXX_CFW_HOST_BLD
327 static
328 #endif
329 unsigned int crc32(unsigned int *pdata, int n)
330 {
331     u32 crc = 0, i, crc_poly = 0x04C11DB7;                  /* CRC - 32 */
332     u32 msb;
333     u32 residue_value;
334     int bits;
335
336     for (i = 0; i < (n >> 2); i++) {
337         bits = 32;
338         while (--bits >= 0) {
339             msb = crc & 0x80000000;
340             crc = (crc << 1) ^ ((*pdata >> bits) & 1);
341             if (msb)
342                 crc = crc ^ crc_poly;
343         }
344         pdata++;
345     }
346
347     switch (n & 3) {
348         case 0:
349             break;
350         case 1:
351             residue_value = (*pdata & 0xFF);
352             bits = 8;
353             break;
354         case 2:
355             residue_value = (*pdata & 0xFFFF);
356             bits = 16;
357             break;
358         case 3:
359             residue_value = (*pdata & 0xFFFFFF);
360             bits = 24;
361             break;
362     }
363
364     if (n & 3) {
365         while (--bits >= 0) {
366             msb = crc & 0x80000000;
367             crc = (crc << 1) ^ ((residue_value >> bits) & 1);
368             if (msb)
369                 crc = crc ^ crc_poly;
370         }
371     }
372     return (crc);
373 }
374 static int crc_chk(void *p, int n)
375 {
376     cfw_project *pjt = (void *)p;
377     u32 crc  = pjt->cksum, crc_comp;
378     pjt->cksum = 0;
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
384     }
385     DBG("CRC pass");
386     pjt->cksum = crc;
387     return 1;
388 }
389 #ifndef AIC3XXX_CFW_HOST_BLD
390 static
391 #endif
392 cfw_project *aic3xxx_cfw_unpickle(void *p, int n)
393 {
394     cfw_project *pjt = p;
395     int i, j, k;
396     if (pjt->magic != CFW_FW_MAGIC ||
397            pjt->size != n ||
398            pjt->bmagic != CFW_FW_VERSION ||
399             !crc_chk(p, n)) {
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,
402                CFW_FW_VERSION);
403         return NULL;
404     }
405     DBG("Loaded firmware inside unpickle\n");
406
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);
412         }
413     }
414
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);
421         }
422         if (pjt->pfw[i]->pll) {
423             FW_NDX2PTR(pjt->pfw[i]->pll, p);
424             aic3xxx_cfw_unpickle_block(pjt->pfw[i]->pll, p);
425         }
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);
430             }
431     }
432
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);
439         }
440         if (pjt->mode[i]->exit) {
441             FW_NDX2PTR(pjt->mode[i]->exit, p);
442             aic3xxx_cfw_unpickle_block(pjt->mode[i]->exit, p);
443         }
444     }
445     DBG("loaded modes\n");
446     return pjt;
447 }
448
449