static int bcmchan_free(kobj_t obj, void *data) { struct bcm2835_audio_chinfo *ch = data; void *buffer; buffer = sndbuf_getbuf(ch->buffer); if (buffer) free(buffer, M_DEVBUF); return (0); }
static int ichchan_free(kobj_t obj, void *data) { struct sc_chinfo *ch; struct sc_info *sc; ch = (struct sc_chinfo *)data; sc = (ch != NULL) ? ch->parent : NULL; if (ch != NULL && sc != NULL) { ICH_DMA_ATTR(sc, sndbuf_getbuf(ch->buffer), sndbuf_getmaxsize(ch->buffer), PAT_WRITE_BACK); } return (1); }
static void adcdac_prog(struct sc_chinfo *ch) { struct sc_info *sc = ch->parent; u_int32_t go; if (!ch->dma_setup) { go = adcdac_go(ch, 0); cs4281_wr(sc, CS4281PCI_DBA(ch->dma_chan), vtophys(sndbuf_getbuf(ch->buffer))); cs4281_wr(sc, CS4281PCI_DBC(ch->dma_chan), sndbuf_getsize(ch->buffer) / ch->bps - 1); ch->dma_setup = 1; adcdac_go(ch, go); } }
static void * ua_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) { device_t pa_dev; u_char *buf,*end; struct ua_info *sc = devinfo; struct ua_chinfo *ch = (dir == PCMDIR_PLAY)? &sc->pch : &sc->rch; ch->parent = sc; ch->channel = c; ch->buffer = b; pa_dev = device_get_parent(sc->sc_dev); /* Create ua_playfmt[] & ua_recfmt[] */ uaudio_query_formats(pa_dev, (u_int32_t *)&ua_playfmt, (u_int32_t *)&ua_recfmt); if (ua_playfmt[0] == 0) { printf("%s channel supported format list invalid\n", dir == PCMDIR_PLAY? "play" : "record"); return NULL; } /* allocate PCM side DMA buffer */ if (sndbuf_alloc(ch->buffer, sc->parent_dmat, UAUDIO_PCM_BUFF_SIZE) != 0) { return NULL; } buf = end = sndbuf_getbuf(b); end += sndbuf_getsize(b); uaudio_chan_set_param_pcm_dma_buff(pa_dev, buf, end, ch->channel); ch->dir = dir; #ifndef NO_RECORDING ch->hwch = 1; if (dir == PCMDIR_PLAY) ch->hwch = 2; #else ch->hwch = 2; #endif return ch; }
/* channel interface */ static void * nmchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) { struct sc_info *sc = devinfo; struct sc_chinfo *ch; u_int32_t chnbuf; chnbuf = (dir == PCMDIR_PLAY)? sc->pbuf : sc->rbuf; ch = (dir == PCMDIR_PLAY)? &sc->pch : &sc->rch; ch->active = 0; ch->blksize = 0; ch->wmark = 0; ch->buffer = b; sndbuf_setup(ch->buffer, (u_int8_t *)rman_get_virtual(sc->buf) + chnbuf, NM_BUFFSIZE); if (bootverbose) device_printf(sc->dev, "%s buf %p\n", (dir == PCMDIR_PLAY)? "play" : "rec", sndbuf_getbuf(ch->buffer)); ch->parent = sc; ch->channel = c; ch->dir = dir; return ch; }
static void bcm2835_audio_write_samples(struct bcm2835_audio_chinfo *ch) { struct bcm2835_audio_info *sc = ch->parent; VC_AUDIO_MSG_T m; void *buf; uint32_t count, size; int ret; VCHIQ_VCHI_LOCK(sc); if (sc->vchi_handle == VCHIQ_SERVICE_HANDLE_INVALID) { VCHIQ_VCHI_UNLOCK(sc); return; } vchi_service_use(sc->vchi_handle); size = sndbuf_getsize(ch->buffer); count = vchiq_unbuffered_bytes(ch); buf = (uint8_t*)sndbuf_getbuf(ch->buffer) + ch->buffered_ptr; if (ch->buffered_ptr + count > size) count = size - ch->buffered_ptr; if (count < VCHIQ_AUDIO_PACKET_SIZE) goto done; count = min(count, ch->free_buffer); count -= count % VCHIQ_AUDIO_PACKET_SIZE; m.type = VC_AUDIO_MSG_TYPE_WRITE; m.u.write.count = count; m.u.write.max_packet = VCHIQ_AUDIO_PACKET_SIZE; m.u.write.callback = NULL; m.u.write.cookie = ch; if (buf) m.u.write.silence = 0; else m.u.write.silence = 1; ret = vchi_msg_queue(sc->vchi_handle, &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); if (ret != 0) printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret); if (buf) { while (count > 0) { int bytes = MIN((int)m.u.write.max_packet, (int)count); ch->free_buffer -= bytes; ch->buffered_ptr += bytes; ch->buffered_ptr = ch->buffered_ptr % size; ret = vchi_msg_queue(sc->vchi_handle, buf, bytes, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); if (ret != 0) printf("%s: vchi_msg_queue failed: %d\n", __func__, ret); buf = (char *)buf + bytes; count -= bytes; } } done: vchi_service_release(sc->vchi_handle); VCHIQ_VCHI_UNLOCK(sc); }
static void bcm2835_audio_worker(void *data) { struct bcm2835_audio_info *sc = (struct bcm2835_audio_info *)data; struct bcm2835_audio_chinfo *ch = &sc->pch; uint32_t speed, format; uint32_t volume, dest; uint32_t flags; uint32_t count, size, readyptr; uint8_t *buf; ch->playback_state = PLAYBACK_IDLE; while (1) { if (sc->worker_state != WORKER_RUNNING) break; BCM2835_AUDIO_LOCK(sc); /* * wait until there are flags set or buffer is ready * to consume more samples */ while ((sc->flags_pending == 0) && bcm2835_audio_buffer_should_sleep(ch)) { cv_wait_sig(&sc->worker_cv, &sc->lock); } flags = sc->flags_pending; /* Clear pending flags */ sc->flags_pending = 0; BCM2835_AUDIO_UNLOCK(sc); /* Requested to change parameters */ if (flags & AUDIO_PARAMS) { BCM2835_AUDIO_LOCK(sc); speed = ch->spd; format = ch->fmt; volume = sc->volume; dest = sc->dest; BCM2835_AUDIO_UNLOCK(sc); if (ch->playback_state == PLAYBACK_IDLE) bcm2835_audio_update_params(sc, format, speed); bcm2835_audio_update_controls(sc, volume, dest); } /* Requested to stop playback */ if ((flags & AUDIO_STOP) && (ch->playback_state == PLAYBACK_PLAYING)) { bcm2835_audio_stop(ch); BCM2835_AUDIO_LOCK(sc); bcm2835_audio_reset_channel(&sc->pch); ch->playback_state = PLAYBACK_IDLE; BCM2835_AUDIO_UNLOCK(sc); continue; } /* Requested to start playback */ if ((flags & AUDIO_PLAY) && (ch->playback_state == PLAYBACK_IDLE)) { BCM2835_AUDIO_LOCK(sc); ch->playback_state = PLAYBACK_PLAYING; BCM2835_AUDIO_UNLOCK(sc); bcm2835_audio_start(ch); } if (ch->playback_state == PLAYBACK_IDLE) continue; if (sndbuf_getready(ch->buffer) == 0) continue; count = sndbuf_getready(ch->buffer); size = sndbuf_getsize(ch->buffer); readyptr = sndbuf_getreadyptr(ch->buffer); BCM2835_AUDIO_LOCK(sc); if (readyptr + count > size) count = size - readyptr; count = min(count, ch->available_space); count -= (count % VCHIQ_AUDIO_PACKET_SIZE); BCM2835_AUDIO_UNLOCK(sc); if (count < VCHIQ_AUDIO_PACKET_SIZE) continue; buf = (uint8_t*)sndbuf_getbuf(ch->buffer) + readyptr; bcm2835_audio_write_samples(ch, buf, count); BCM2835_AUDIO_LOCK(sc); ch->unsubmittedptr = (ch->unsubmittedptr + count) % sndbuf_getsize(ch->buffer); ch->available_space -= count; ch->submitted_samples += count; KASSERT(ch->available_space >= 0, ("ch->available_space == %d\n", ch->available_space)); BCM2835_AUDIO_UNLOCK(sc); } BCM2835_AUDIO_LOCK(sc); sc->worker_state = WORKER_STOPPED; cv_signal(&sc->worker_cv); BCM2835_AUDIO_UNLOCK(sc); kproc_exit(0); }
static void * ichchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) { struct sc_info *sc = devinfo; struct sc_chinfo *ch; unsigned int num; ICH_LOCK(sc); num = sc->chnum++; ch = &sc->ch[num]; ch->num = num; ch->buffer = b; ch->channel = c; ch->parent = sc; ch->run = 0; ch->dtbl = sc->dtbl + (ch->num * ICH_DTBL_LENGTH); ch->desc_addr = sc->desc_addr + (ch->num * ICH_DTBL_LENGTH * sizeof(struct ich_desc)); ch->blkcnt = sc->blkcnt; ch->blksz = sc->bufsz / ch->blkcnt; switch(ch->num) { case 0: /* play */ KASSERT(dir == PCMDIR_PLAY, ("wrong direction")); ch->regbase = ICH_REG_PO_BASE; ch->spdreg = (sc->hasvra) ? AC97_REGEXT_FDACRATE : 0; ch->imask = ICH_GLOB_STA_POINT; break; case 1: /* record */ KASSERT(dir == PCMDIR_REC, ("wrong direction")); ch->regbase = ICH_REG_PI_BASE; ch->spdreg = (sc->hasvra) ? AC97_REGEXT_LADCRATE : 0; ch->imask = ICH_GLOB_STA_PIINT; break; case 2: /* mic */ KASSERT(dir == PCMDIR_REC, ("wrong direction")); ch->regbase = ICH_REG_MC_BASE; ch->spdreg = (sc->hasvrm) ? AC97_REGEXT_MADCRATE : 0; ch->imask = ICH_GLOB_STA_MINT; break; default: return (NULL); } if (sc->flags & ICH_FIXED_RATE) ch->spdreg = 0; ICH_UNLOCK(sc); if (sndbuf_alloc(ch->buffer, sc->chan_dmat, sc->bufsz) != 0) return (NULL); ICH_DMA_ATTR(sc, sndbuf_getbuf(ch->buffer), sndbuf_getmaxsize(ch->buffer), PAT_UNCACHEABLE); ICH_LOCK(sc); ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (uint32_t)(ch->desc_addr), 4); ICH_UNLOCK(sc); return (ch); }