📄 devuda1341.c
字号:
}static voidinenable(void) { /* turn on ADC, set input gain switch */ status1[0] |= 0x22; L3_write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1); if (debug) { print("inenable: status1 = 0x%2.2ux\n", status1[0]); }}static voidindisable(void) { dmastop(audio.i.dma); /* turn off ADC, clear input gain switch */ status1[0] &= ~0x22; L3_write(UDA1341_L3Addr | UDA1341_STATUS, status1, 1); if (debug) { print("indisable: status1 = 0x%2.2ux\n", status1[0]); }}static voidsendaudio(IOstate *s) { /* interrupt routine calls this too */ int n; if (debug > 1) print("#A: sendaudio\n"); ilock(&s->ilock); if ((audio.amode & Aread) && s->next == s->filling && dmaidle(s->dma)) { // send an empty buffer to provide an input clock zerodma |= dmastart(s->dma, Flushbuf, volumes[Vbufsize].ilval) & 0xff; if (zerodma == 0) if (debug) print("emptyfail\n"); iostats.tx.empties++; iunlock(&s->ilock); return; } while (s->next != s->filling) { s->next->nbytes &= ~0x3; /* must be a multiple of 4 */ if(s->next->nbytes) { if ((n = dmastart(s->dma, s->next->phys, s->next->nbytes)) == 0) { iostats.tx.faildma++; break; } iostats.tx.totaldma++; switch (n >> 8) { case 1: iostats.tx.idledma++; break; case 3: iostats.tx.faildma++; break; } if (debug) { if (debug > 1) print("dmastart @%p\n", s->next); else iprint("+"); } s->next->nbytes = 0; } s->next++; if (s->next == &s->buf[Nbuf]) s->next = &s->buf[0]; } iunlock(&s->ilock);}static voidrecvaudio(IOstate *s) { /* interrupt routine calls this too */ int n; if (debug > 1) print("#A: recvaudio\n"); ilock(&s->ilock); while (s->next != s->emptying) { assert(s->next->nbytes == 0); if ((n = dmastart(s->dma, s->next->phys, volumes[Vbufsize].ilval)) == 0) { iostats.rx.faildma++; break; } iostats.rx.totaldma++; switch (n >> 8) { case 1: iostats.rx.idledma++; break; case 3: iostats.rx.faildma++; break; } if (debug) { if (debug > 1) print("dmastart @%p\n", s->next); else iprint("+"); } s->next++; if (s->next == &s->buf[Nbuf]) s->next = &s->buf[0]; } iunlock(&s->ilock);}voidaudiopower(int flag) { IOstate *s; if (debug) { iprint("audiopower %d\n", flag); } if (flag) { /* power on only when necessary */ if (audio.amode) { audioamppower(1); audioicpower(1); egpiobits(EGPIO_codec_reset, 1); enable(); if (audio.amode & Aread) { inenable(); s = &audio.i; dmareset(s->dma, 1, 0, 4, 2, SSPRecvDMA, Port4SSP); recvaudio(s); } if (audio.amode & Awrite) { outenable(); s = &audio.o; dmareset(s->dma, 0, 0, 4, 2, SSPXmitDMA, Port4SSP); sendaudio(s); } mxvolume(); } } else { /* power off */ if (audio.amode & Aread) indisable(); if (audio.amode & Awrite) outdisable(); egpiobits(EGPIO_codec_reset, 0); audioamppower(0); audioicpower(0); }}static voidaudiointr(void *x, ulong ndma) { IOstate *s = x; if (debug) { if (debug > 1) iprint("#A: audio interrupt @%p\n", s->current); else iprint("-"); } if (s == &audio.i || (ndma & ~zerodma)) { /* A dma, not of a zero buffer completed, update current * Only interrupt routine touches s->current */ s->current->nbytes = (s == &audio.i)? volumes[Vbufsize].ilval: 0; s->current++; if (s->current == &s->buf[Nbuf]) s->current = &s->buf[0]; } if (ndma) { if (s == &audio.o) { zerodma &= ~ndma; sendaudio(s); } else if (s == &audio.i) recvaudio(s); } wakeup(&s->vous);}static Chan*audioattach(char *param){ return devattach('A', param);}static Walkqid*audiowalk(Chan *c, Chan *nc, char **name, int nname){ return devwalk(c, nc, name, nname, audiodir, nelem(audiodir), devgen);}static intaudiostat(Chan *c, uchar *db, int n){ return devstat(c, db, n, audiodir, nelem(audiodir), devgen);}static Chan*audioopen(Chan *c, int mode){ IOstate *s; int omode = mode; switch((ulong)c->qid.path) { default: error(Eperm); break; case Qstatus: case Qstats: if((omode&7) != OREAD) error(Eperm); case Qvolume: case Qdir: break; case Qaudio: omode = (omode & 0x7) + 1; if (omode & ~(Aread | Awrite)) error(Ebadarg); qlock(&audio); if(audio.amode & omode){ qunlock(&audio); error(Einuse); } enable(); memset(&iostats, 0, sizeof(iostats)); if (omode & Aread) { inenable(); s = &audio.i; if(s->bufinit == 0) bufinit(s); setempty(s); s->emptying = &s->buf[Nbuf-1]; s->chan = c; s->dma = dmaalloc(1, 0, 4, 2, SSPRecvDMA, Port4SSP, audiointr, (void*)s); audio.amode |= Aread; } if (omode & (Aread|Awrite) && (audio.amode & Awrite) == 0) { s = &audio.o; if(s->bufinit == 0) bufinit(s); setempty(s); s->chan = c; s->dma = dmaalloc(0, 0, 4, 2, SSPXmitDMA, Port4SSP, audiointr, (void*)s); } if (omode & Awrite) { audio.amode |= Awrite; outenable(); } mxvolume(); if (audio.amode & Aread) sendaudio(&audio.o); qunlock(&audio); if (debug) print("open done\n"); break; } c = devopen(c, mode, audiodir, nelem(audiodir), devgen); c->mode = openmode(mode); c->flag |= COPEN; c->offset = 0; return c;}static voidaudioclose(Chan *c){ IOstate *s; switch((ulong)c->qid.path) { default: error(Eperm); break; case Qdir: case Qvolume: case Qstatus: case Qstats: break; case Qaudio: if (debug > 1) print("#A: close\n"); if(c->flag & COPEN) { qlock(&audio); if (audio.i.chan == c) { /* closing the read end */ audio.amode &= ~Aread; s = &audio.i; qlock(s); indisable(); setempty(s); dmafree(s->dma); qunlock(s); if ((audio.amode & Awrite) == 0) { s = &audio.o; qlock(s); while(waserror()) ; dmawait(s->dma); poperror(); outdisable(); setempty(s); dmafree(s->dma); qunlock(s); } } if (audio.o.chan == c) { /* closing the write end */ audio.amode &= ~Awrite; s = &audio.o; qlock(s); if (s->filling->nbytes) { /* send remaining partial buffer */ s->filling++; if (s->filling == &s->buf[Nbuf]) s->filling = &s->buf[0]; sendaudio(s); } while(waserror()) ; dmawait(s->dma); poperror(); outdisable(); setempty(s); if ((audio.amode & Aread) == 0) dmafree(s->dma); qunlock(s); } if (audio.amode == 0) { /* turn audio off */ egpiobits(EGPIO_codec_reset, 0); audioicpower(0); } qunlock(&audio); } break; }}static longaudioread(Chan *c, void *v, long n, vlong off){ int liv, riv, lov, rov; long m, n0; char buf[300]; int j; ulong offset = off; char *p; IOstate *s; n0 = n; p = v; switch((ulong)c->qid.path) { default: error(Eperm); break; case Qdir: return devdirread(c, p, n, audiodir, nelem(audiodir), devgen); case Qaudio: if (debug > 1) print("#A: read %ld\n", n); if((audio.amode & Aread) == 0) error(Emode); s = &audio.i; qlock(s); if(waserror()){ qunlock(s); nexterror(); } while(n > 0) { if(s->emptying->nbytes == 0) { if (debug > 1) print("#A: emptied @%p\n", s->emptying); recvaudio(s); s->emptying++; if (s->emptying == &s->buf[Nbuf]) s->emptying = s->buf; } /* wait if dma in progress */ while (!dmaidle(s->dma) && s->emptying == s->current) { if (debug > 1) print("#A: sleep\n"); sleep(&s->vous, audioqnotempty, s); } m = (s->emptying->nbytes > n)? n: s->emptying->nbytes; memmove(p, s->emptying->virt + volumes[Vbufsize].ilval - s->emptying->nbytes, m); s->emptying->nbytes -= m; n -= m; p += m; } poperror(); qunlock(s); break; case Qstatus: buf[0] = 0; snprint(buf, sizeof(buf), "bytes %lud\ntime %lld\n", audio.totcount, audio.tottime); return readstr(offset, p, n, buf); case Qstats: buf[0] = 0; snprint(buf, sizeof(buf), "bytes %lud\nRX dmas %lud, while idle %lud, while busy %lud, " "out-of-order %lud, empty dmas %lud\n" "TX dmas %lud, while idle %lud, while busy %lud, " "out-of-order %lud, empty dmas %lud\n", iostats.bytes, iostats.rx.totaldma, iostats.rx.idledma, iostats.rx.faildma, iostats.rx.samedma, iostats.rx.empties, iostats.tx.totaldma, iostats.tx.idledma, iostats.tx.faildma, iostats.tx.samedma, iostats.tx.empties); return readstr(offset, p, n, buf); case Qvolume: j = 0; buf[0] = 0; for(m=0; volumes[m].name; m++){ if (m == Vaudio) { liv = audio.livol[m]; riv = audio.rivol[m]; lov = audio.lovol[m]; rov = audio.rovol[m]; } else { lov = liv = volumes[m].ilval; rov = riv = volumes[m].irval; } j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name); if((volumes[m].flag & Fmono) || liv==riv && lov==rov){ if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov) j += snprint(buf+j, sizeof(buf)-j, " %d", liv); else{ if(volumes[m].flag & Fin) j += snprint(buf+j, sizeof(buf)-j, " in %d", liv); if(volumes[m].flag & Fout) j += snprint(buf+j, sizeof(buf)-j, " out %d", lov); } }else{ if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov && riv==rov) j += snprint(buf+j, sizeof(buf)-j, " left %d right %d", liv, riv); else{ if(volumes[m].flag & Fin) j += snprint(buf+j, sizeof(buf)-j, " in left %d right %d", liv, riv); if(volumes[m].flag & Fout) j += snprint(buf+j, sizeof(buf)-j, " out left %d right %d", lov, rov); } } j += snprint(buf+j, sizeof(buf)-j, "\n"); } return readstr(offset, p, n, buf); } return n0-n;}static voidsetaudio(int in, int out, int left, int right, int value){ if (value < 0 || value > 100) error(Evolume); if(left && out) audio.lovol[Vaudio] = value; if(left && in) audio.livol[Vaudio] = value; if(right && out) audio.rovol[Vaudio] = value; if(right && in) audio.rivol[Vaudio] = value;}static void setspeed(int, int, int, int, int speed){ uchar clock; /* external clock configured for 44100 samples/sec */ switch (speed) { case 32000: /* 00 */ gpioregs->clear = GPIO_CLK_SET0_o|GPIO_CLK_SET1_o; clock = SC384FS; /* Only works in MSB mode! */ break; case 48000: /* 00 */ gpioregs->clear = GPIO_CLK_SET0_o|GPIO_CLK_SET1_o; clock = SC256FS; break; default: speed = 44100; case 44100: /* 01 */ gpioregs->set = GPIO_CLK_SET0_o; gpioregs->clear = GPIO_CLK_SET1_o; clock = SC256FS; break; case 8000: /* 10 */ gpioregs->set = GPIO_CLK_SET1_o; gpioregs->clear = GPIO_CLK_SET0_o; clock = SC512FS; /* Only works in MSB mode! */ break; case 16000: /* 10 */ gpioregs->set = GPIO_CLK_SET1_o; gpioregs->clear = GPIO_CLK_SET0_o; clock = SC256FS; break; case 11025: /* 11 */ gpioregs->set = GPIO_CLK_SET0_o|GPIO_CLK_SET1_o; clock = SC512FS; /* Only works in MSB mode! */ break; case 22050: /* 11 */ gpioregs->set = GPIO_CLK_SET0_o|GPIO_CLK_SET1_o; clock = SC256FS; break; } /* Wait for the UDA1341 to wake up */ delay(100); /* Reset the chip */ status0[0] &= ~CLOCKMASK; status0[0] |=clock; volumes[Vspeed].ilval = speed;}static voidsetbufsize(int, int, int, int, int value){ if ((value % 8) != 0 || value < 8 || value >= Bufsize) error(Ebadarg); volumes[Vbufsize].ilval = value;}static longaudiowrite(Chan *c, void *vp, long n, vlong){ long m, n0; int i, v, left, right, in, out; char *p; IOstate *a; Cmdbuf *cb; p = vp; n0 = n; switch((ulong)c->qid.path) { default: error(Eperm); break; case Qvolume: v = Vaudio; left = 1; right = 1; in = 1; out = 1; cb = parsecmd(p, n); if(waserror()){ free(cb); nexterror(); } for(i = 0; i < cb->nf; i++){ /* * a number is volume */ if(cb->f[i][0] >= '0' && cb->f[i][0] <= '9') { m = strtoul(cb->f[i], 0, 10); if (volumes[v].setval) volumes[v].setval(in, out, left, right, m); goto cont0; } for(m=0; volumes[m].name; m++) { if(strcmp(cb->f[i], volumes[m].name) == 0) { v = m; in = 1; out = 1; left = 1; right = 1; goto cont0; } } if(strcmp(cb->f[i], "reset") == 0) { resetlevel(); goto cont0; } if(strcmp(cb->f[i], "debug") == 0) { debug = debug?0:1; goto cont0; } if(strcmp(cb->f[i], "in") == 0) { in = 1; out = 0; goto cont0; } if(strcmp(cb->f[i], "out") == 0) { in = 0; out = 1; goto cont0; } if(strcmp(cb->f[i], "left") == 0) { left = 1; right = 0; goto cont0; } if(strcmp(cb->f[i], "right") == 0) { left = 0; right = 1; goto cont0; } if(strcmp(cb->f[i], "reg") == 0) { if(cb->nf < 3) error(Evolume); setreg(cb->f[1], atoi(cb->f[2]), cb->nf == 4 ? atoi(cb->f[3]):1); return n0; } error(Evolume); break; cont0:; } mxvolume(); poperror(); free(cb); break; case Qaudio: if (debug > 1) print("#A: write %ld\n", n); if((audio.amode & Awrite) == 0) error(Emode); a = &audio.o; qlock(a); if(waserror()){ qunlock(a); nexterror(); } while(n > 0) { /* wait if dma in progress */ while (!dmaidle(a->dma) && !zerodma && a->filling == a->current) { if (debug > 1) print("#A: sleep\n"); sleep(&a->vous, audioqnotfull, a); } m = volumes[Vbufsize].ilval - a->filling->nbytes; if(m > n) m = n; memmove(a->filling->virt + a->filling->nbytes, p, m); a->filling->nbytes += m; n -= m; p += m; if(a->filling->nbytes >= volumes[Vbufsize].ilval) { if (debug > 1) print("#A: filled @%p\n", a->filling); a->filling++; if (a->filling == &a->buf[Nbuf]) a->filling = a->buf; sendaudio(a); } } poperror(); qunlock(a); break; } return n0 - n;}Dev uda1341devtab = { 'A', "audio", devreset, audioinit, devshutdown, audioattach, audiowalk, audiostat, audioopen, devcreate, audioclose, audioread, devbread, audiowrite, devbwrite, devremove, devwstat, audiopower,};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -