Beispiel #1
0
static void *
cs4281chan_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 = (dir == PCMDIR_PLAY) ? &sc->pch : &sc->rch;

    ch->buffer = b;
    if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) != 0) {
	return NULL;
    }
    ch->parent = sc;
    ch->channel = c;

    ch->fmt = SND_FORMAT(AFMT_U8, 1, 0);
    ch->spd = DSP_DEFAULT_SPEED;
    ch->bps = 1;
    ch->blksz = sndbuf_getsize(ch->buffer);

    ch->dma_chan = (dir == PCMDIR_PLAY) ? CS4281_DMA_PLAY : CS4281_DMA_REC;
    ch->dma_setup = 0;

    adcdac_go(ch, 0);
    adcdac_prog(ch);

    return ch;
}
Beispiel #2
0
static void
bcm2835_audio_callback(void *param, const VCHI_CALLBACK_REASON_T reason, void *msg_handle)
{
	struct bcm2835_audio_info *sc = (struct bcm2835_audio_info *)param;
	int32_t status;
	uint32_t msg_len;
	VC_AUDIO_MSG_T m;

	if (reason != VCHI_CALLBACK_MSG_AVAILABLE)
		return;

	status = vchi_msg_dequeue(sc->vchi_handle,
	    &m, sizeof m, &msg_len, VCHI_FLAGS_NONE);
	if (m.type == VC_AUDIO_MSG_TYPE_RESULT) {
		sc->msg_result = m.u.result.success;
		cv_signal(&sc->msg_avail_cv);
	} else if (m.type == VC_AUDIO_MSG_TYPE_COMPLETE) {
		struct bcm2835_audio_chinfo *ch = m.u.complete.cookie;

		int count = m.u.complete.count & 0xffff;
		int perr = (m.u.complete.count & (1U << 30)) != 0;

		ch->complete_pos = (ch->complete_pos + count) % sndbuf_getsize(ch->buffer);
		ch->free_buffer += count;

		if (perr || ch->free_buffer >= VCHIQ_AUDIO_PACKET_SIZE) {
			chn_intr(ch->channel);
			cv_signal(&sc->data_cv);
		}
	} else
		printf("%s: unknown m.type: %d\n", __func__, m.type);
}
Beispiel #3
0
static void
ds_setuprch(struct sc_rchinfo *ch)
{
	struct sc_info *sc = ch->parent;
	int stereo, b16, i, sz, pri;
	u_int32_t x, y;
	bus_addr_t addr;

	stereo = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0;
	b16 = (ch->fmt & AFMT_16BIT)? 1 : 0;
	addr = sndbuf_getbufaddr(ch->buffer);
	sz = sndbuf_getsize(ch->buffer);
	pri = (ch->num == DS1_RECPRIMARY)? 1 : 0;

	for (i = 0; i < 2; i++) {
		ch->slot[i].PgBase = addr;
		ch->slot[i].PgLoopEnd = sz;
		ch->slot[i].PgStart = 0;
		ch->slot[i].NumOfLoops = 0;
	}
	x = (b16? 0x00 : 0x01) | (stereo? 0x02 : 0x00);
	y = (48000 * 4096) / ch->spd;
	y--;
	/* printf("pri = %d, x = %d, y = %d\n", pri, x, y); */
	ds_wr(sc, pri? YDSXGR_ADCFORMAT : YDSXGR_RECFORMAT, x, 4);
	ds_wr(sc, pri? YDSXGR_ADCSLOTSR : YDSXGR_RECSLOTSR, y, 4);
}
Beispiel #4
0
static int
alschan_getptr(kobj_t obj, void *data)
{
	struct sc_chinfo *ch = data;
	int32_t pos, sz;

	pos = als_gcr_rd(ch->parent, ch->gcr_fifo_status) & 0xffff;
	sz  = sndbuf_getsize(ch->buffer);
	return (2 * sz - pos - 1) % sz;
}
Beispiel #5
0
static u_int32_t
alschan_getptr(kobj_t obj, void *data)
{
	struct sc_chinfo *ch = data;
	struct sc_info *sc = ch->parent;
	int32_t pos, sz;

	snd_mtxlock(sc->lock);
	pos = als_gcr_rd(ch->parent, ch->gcr_fifo_status) & 0xffff;
	snd_mtxunlock(sc->lock);
	sz  = sndbuf_getsize(ch->buffer);
	return (2 * sz - pos - 1) % sz;
}
Beispiel #6
0
static u_int32_t
cs4281chan_getptr(kobj_t obj, void *data)
{
    struct sc_chinfo *ch = data;
    struct sc_info *sc = ch->parent;
    u_int32_t  dba, dca, ptr;
    int sz;

    sz  = sndbuf_getsize(ch->buffer);
    dba = cs4281_rd(sc, CS4281PCI_DBA(ch->dma_chan));
    dca = cs4281_rd(sc, CS4281PCI_DCA(ch->dma_chan));
    ptr = (dca - dba + sz) % sz;

    return ptr;
}
Beispiel #7
0
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),
		  sndbuf_getbufaddr(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);
    }
}
Beispiel #8
0
static void
ds_setuppch(struct sc_pchinfo *ch)
{
	int stereo, b16, c, sz;
	bus_addr_t addr;

	stereo = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0;
	b16 = (ch->fmt & AFMT_16BIT)? 1 : 0;
	c = stereo? 1 : 0;
	addr = sndbuf_getbufaddr(ch->buffer);
	sz = sndbuf_getsize(ch->buffer);

	ds_initpbank(ch->lslot, c, stereo, b16, ch->spd, addr, sz);
	ds_initpbank(ch->lslot + 1, c, stereo, b16, ch->spd, addr, sz);
	ds_initpbank(ch->rslot, 2, stereo, b16, ch->spd, addr, sz);
	ds_initpbank(ch->rslot + 1, 2, stereo, b16, ch->spd, addr, sz);
}
static int
via_buildsgdt(struct via_chinfo *ch)
{
	u_int32_t phys_addr, flag;
	int i, seg_size;

	seg_size = sndbuf_getsize(ch->buffer) / SEGS_PER_CHAN;
	phys_addr = sndbuf_getbufaddr(ch->buffer);

	for (i = 0; i < SEGS_PER_CHAN; i++) {
		flag = (i == SEGS_PER_CHAN - 1) ? VIA_DMAOP_EOL : VIA_DMAOP_FLAG;
		ch->sgd_table[i].ptr = phys_addr + (i * seg_size);
		ch->sgd_table[i].flags = flag | seg_size;
	}

	return 0;
}
Beispiel #10
0
static int
cs4281chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
{
    struct sc_chinfo *ch = data;
    u_int32_t go;

    go = adcdac_go(ch, 0);

    /* 2 interrupts are possible and used in buffer (half-empty,empty),
     * hence factor of 2. */
    ch->blksz = MIN(blocksize, CS4281_BUFFER_SIZE / 2);
    sndbuf_resize(ch->buffer, 2, ch->blksz);
    ch->dma_setup = 0;
    adcdac_prog(ch);
    adcdac_go(ch, go);

    DEB(printf("cs4281chan_setblocksize: bufsz %d Setting %d\n", blocksize, ch->blksz));

    return sndbuf_getsize(ch->buffer);
}
Beispiel #11
0
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;
}
Beispiel #12
0
static int
viachan_getptr(kobj_t obj, void *data)
{
	struct via_chinfo *ch = data;
	struct via_info *via = ch->parent;
	struct via_dma_op *ado;
	bus_addr_t sgd_addr = ch->sgd_addr;
	int ptr, base, base1, len, seg;

	ado = ch->sgd_table;
	snd_mtxlock(via->lock);
	base1 = via_rd(via, ch->base, 4);
	len = via_rd(via, ch->count, 4);
	base = via_rd(via, ch->base, 4);
	if (base != base1) 	/* Avoid race hazard */
		len = via_rd(via, ch->count, 4);
	snd_mtxunlock(via->lock);

	DEB(kprintf("viachan_getptr: len / base = %x / %x\n", len, base));

	/* Base points to SGD segment to do, one past current */

	/* Determine how many segments have been done */
	seg = (base - sgd_addr) / sizeof(struct via_dma_op);
	if (seg == 0)
		seg = SEGS_PER_CHAN;

	/* Now work out offset: seg less count */
	ptr = (seg * sndbuf_getsize(ch->buffer) / SEGS_PER_CHAN) - len;
	if (ch->dir == PCMDIR_REC) {
		/* DMA appears to operate on memory 'lines' of 32 bytes	*/
		/* so don't return any part line - it isn't in RAM yet	*/
		ptr = ptr & ~0x1f;
	}

	DEB(kprintf("return ptr=%d\n", ptr));
	return ptr;
}
Beispiel #13
0
static __inline uint32_t
vchiq_unbuffered_bytes(struct bcm2835_audio_chinfo *ch)
{
	uint32_t size, ready, readyptr, readyend;

	size = sndbuf_getsize(ch->buffer);
	readyptr = sndbuf_getreadyptr(ch->buffer);
	ready = sndbuf_getready(ch->buffer);

	readyend = readyptr + ready;
	/* Normal case */
	if (ch->buffered_ptr >= readyptr) {
		if (readyend > ch->buffered_ptr)
			return readyend - ch->buffered_ptr;
		else
			return 0;
	}
	else { /* buffered_ptr overflow */
		if (readyend > ch->buffered_ptr + size)
			return readyend - ch->buffered_ptr - size;
		else
			return 0;
	}
}
Beispiel #14
0
static int
via_buildsgdt(struct via_chinfo *ch)
{
	u_int32_t phys_addr, flag;
	int i, segs, seg_size;

	/*
	 *  Build the scatter/gather DMA (SGD) table.
	 *  There are four slots in the table: two for play, two for record.
	 *  This creates two half-buffers, one of which is playing; the other
	 *  is feeding.
	 */
	seg_size = ch->blksz;
	segs = sndbuf_getsize(ch->buffer) / seg_size;
	phys_addr = sndbuf_getbufaddr(ch->buffer);

	for (i = 0; i < segs; i++) {
		flag = (i == segs - 1)? VIA_DMAOP_EOL : VIA_DMAOP_FLAG;
		ch->sgd_table[i].ptr = phys_addr + (i * seg_size);
		ch->sgd_table[i].flags = flag | seg_size;
	}

	return 0;
}
Beispiel #15
0
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);
}
Beispiel #16
0
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);
}