patch-2.4.0-test7 linux/drivers/sound/emu10k1/cardwo.c

Next file: linux/drivers/sound/emu10k1/cardwo.h
Previous file: linux/drivers/sound/emu10k1/cardwi.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test6/linux/drivers/sound/emu10k1/cardwo.c linux/drivers/sound/emu10k1/cardwo.c
@@ -1,4 +1,3 @@
-
 /*
  **********************************************************************
  *     cardwo.c - PCM output HAL for emu10k1 driver
@@ -30,47 +29,17 @@
  **********************************************************************
  */
 
+#include <linux/poll.h>
 #include "hwaccess.h"
+#include "8010.h"
+#include "voicemgr.h"
 #include "cardwo.h"
 #include "audio.h"
 
-/* Volume calcs */
-
-static int set_volume_instance(struct emu10k1_waveout *card_waveout, struct wave_out *wave_out, struct voice_param *left)
+static u32 samplerate_to_linearpitch(u32 samplingrate)
 {
-	/* only applicable for playback */
-	u32 volL, volR, vol = 0;
-
-	volL = (wave_out->localvol & 0xffff);
-	volR = ((wave_out->localvol >> 16) & 0xffff);
-
-	if (wave_out->globalvolFactor) {
-		volL = ((u32) (((u16) card_waveout->globalvol & 0xffff) * (u16) volL)) / 0xffff;
-		volR = ((u32) (((u16) (card_waveout->globalvol >> 16) & 0xffff) * ((u16) volR))) / 0xffff;
-	}
-
-	/* BIG ASSUMPTION HERE THAT DEFAULT WAVE PAN/AUX IS 0xff/0xff */
-	/* New volume and pan */
-
-	if (volL == volR) {
-		vol = volL;
-		left->send_c = 0xff;
-		left->send_b = 0xff;
-	} else {
-		if (volL > volR) {
-			vol = volL;
-			left->send_c = 0xff;
-			left->send_b = (char) ((volR * 255) / vol);
-		} else {
-			vol = volR;
-			left->send_b = 0xff;
-			left->send_c = (char) ((volL * 255) / vol);
-		}
-	}
-
-	left->initial_attn = 0xff & sumVolumeToAttenuation(vol * 2);
-
-	return vol;
+	samplingrate = (samplingrate << 8) / 375;
+	return (samplingrate >> 1) + (samplingrate & 1);
 }
 
 static void query_format(struct wave_format *wave_fmt)
@@ -78,593 +47,404 @@
 	if ((wave_fmt->channels != 1) && (wave_fmt->channels != 2))
 		wave_fmt->channels = 2;
 
-	if (wave_fmt->samplingrate >= 0x2EE00)
-		wave_fmt->samplingrate = 0x2EE00;
+	if (wave_fmt->samplingrate >= 0x2ee00)
+		wave_fmt->samplingrate = 0x2ee00;
 
 	if ((wave_fmt->bitsperchannel != 8) && (wave_fmt->bitsperchannel != 16))
 		wave_fmt->bitsperchannel = 16;
 
+	wave_fmt->bytesperchannel = wave_fmt->bitsperchannel >> 3;
+	wave_fmt->bytespersample = wave_fmt->channels * wave_fmt->bytesperchannel;
+	wave_fmt->bytespersec = wave_fmt->bytespersample * wave_fmt->samplingrate;
+
 	return;
 }
 
-static int alloc_xferbuffer(struct emu10k1_card *card, struct wave_out *wave_out, u32 * size, void ***buffer)
+static int alloc_buffer(struct emu10k1_card *card, struct waveout_buffer *buffer)
 {
-	u32 numpages, reqsize, pageindex, pagecount;
-	struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf;
+	u32 pageindex, pagecount;
 	unsigned long busaddx;
 	int i;
 
-	reqsize = *size;
-	numpages = reqsize / PAGE_SIZE;
-
-	/* If size is not a multiple of PAGE_SIZE then we need to round up */
-	if (reqsize % PAGE_SIZE)
-		numpages += 1;
-
-	DPD(2, "requested pages is: %d\n", numpages);
-
-	wavexferbuf->numpages = numpages;
-
-	/* Only for playback, request for emu address space */
-	/* Support non page-aligned buffer, don't need interpolation page */
+	DPD(2, "requested pages is: %d\n", buffer->pages);
 
-	if ((wave_out->emupageindex = emu10k1_addxmgr_alloc(numpages * PAGE_SIZE, card)) < 0)
-		return CTSTATUS_ERROR;
-
-	if ((wave_out->pagetable = (void **) kmalloc(sizeof(void *) * numpages, GFP_KERNEL)) == NULL)
-		return CTSTATUS_ERROR;
+	if ((buffer->emupageindex = emu10k1_addxmgr_alloc(buffer->pages * PAGE_SIZE, card)) < 0)
+		return -1;
 
 	/* Fill in virtual memory table */
-	for (pagecount = 0; pagecount < numpages; pagecount++) {
-		if ((wave_out->pagetable[pagecount] = (void *) __get_free_page(GFP_KERNEL)) == NULL) {
-			wavexferbuf->numpages = pagecount;
-			return CTSTATUS_ERROR;
+	for (pagecount = 0; pagecount < buffer->pages; pagecount++) {
+		if ((buffer->addr[pagecount] = pci_alloc_consistent(card->pci_dev, PAGE_SIZE, &buffer->dma_handle[pagecount])) == NULL) {
+			buffer->pages = pagecount;
+			return -1;
 		}
 
-		DPD(2, "Virtual Addx: %p\n", wave_out->pagetable[pagecount]);
+		DPD(2, "Virtual Addx: %p\n", buffer->addr[pagecount]);
 
 		for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) {
-			busaddx = virt_to_bus((u8 *) wave_out->pagetable[pagecount] + i * EMUPAGESIZE);
+			busaddx = buffer->dma_handle[pagecount] + i * EMUPAGESIZE;
 
 			DPD(3, "Bus Addx: %lx\n", busaddx);
 
-			pageindex = wave_out->emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i;
+			pageindex = buffer->emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i;
 
-			((u32 *) card->virtualpagetable->virtaddx)[pageindex] = ((u32) busaddx * 2) | pageindex;
+			((u32 *) card->virtualpagetable.addr)[pageindex] = (busaddx * 2) | pageindex;
 		}
 	}
 
-	*buffer = wave_out->pagetable;
-
-	return CTSTATUS_SUCCESS;
-}
-
-static int get_xferbuffer(struct emu10k1_card *card, struct wave_out *wave_out, u32 * size)
-{
-	struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf;
-	void **buffer;
-
-	wavexferbuf->xferpos = 0;
-	wavexferbuf->silence_xferpos = 0;
-	wavexferbuf->stopposition = 0;
-	wavexferbuf->is_stereo = (wave_out->wave_fmt.channels == 2) ? 1 : 0;
-	wavexferbuf->is_16bit = (wave_out->wave_fmt.bitsperchannel == 16) ? 1 : 0;
-	wavexferbuf->bytespersample = (wavexferbuf->is_stereo + 1) * (wavexferbuf->is_16bit + 1);
-
-	if (alloc_xferbuffer(card, wave_out, size, &buffer) != CTSTATUS_SUCCESS)
-		return CTSTATUS_ERROR;
-
-	/* xferbufsize contains actual transfer buffer size */
-	wavexferbuf->xferbufsize = *size;
-	wavexferbuf->xferbuffer = buffer;
-
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
-static void dealloc_xferbuffer(struct emu10k1_card *card, struct wave_out *wave_out)
+static void free_buffer(struct emu10k1_card *card, struct waveout_buffer *buffer)
 {
 	u32 pagecount, pageindex;
 	int i;
 
-	if (wave_out->pagetable != NULL) {
-		for (pagecount = 0; pagecount < wave_out->wavexferbuf->numpages; pagecount++) {
-			free_page((unsigned long) wave_out->pagetable[pagecount]);
-
-			for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) {
-				pageindex = wave_out->emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i;
-				((u32 *) card->virtualpagetable->virtaddx)[pageindex] = (card->silentpage->busaddx * 2) | pageindex;
-			}
+	if (buffer->emupageindex < 0)
+		return;
+
+	for (pagecount = 0; pagecount < buffer->pages; pagecount++) {
+		pci_free_consistent(card->pci_dev, PAGE_SIZE, buffer->addr[pagecount], buffer->dma_handle[pagecount]);
+
+		for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) {
+			pageindex = buffer->emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i;
+			((u32 *) card->virtualpagetable.addr)[pageindex] = (card->silentpage.dma_handle * 2) | pageindex;
 		}
-		kfree(wave_out->pagetable);
 	}
 
-	emu10k1_addxmgr_free(card, wave_out->emupageindex);
+	emu10k1_addxmgr_free(card, buffer->emupageindex);
+	buffer->emupageindex = -1;
 
 	return;
 }
 
-static int get_voice(struct emu10k1_card *card, struct wave_out *wave_out, int device)
+static int get_voice(struct emu10k1_card *card, struct woinst *woinst)
 {
-	struct emu10k1_waveout *card_waveout = card->waveout;
-	struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf;
-	struct voice_allocdesc voice_allocdesc;
-	struct voice_param *left, *right;
-	u32 size;
-
+	struct emu_voice *voice = &woinst->voice;
 	/* Allocate voices here, if no voices available, return error.
 	 * Init voice_allocdesc first.*/
 
-	voice_allocdesc.usage = VOICEMGR_USAGE_PLAYBACK;
-
-	voice_allocdesc.flags = 0;
-
-	if (device == 1)
-		voice_allocdesc.flags |= VOICEMGR_FLAGS_FXRT2;
-
-	if (wave_out->wave_fmt.channels == 1)
-		voice_allocdesc.flags |= VOICEMGR_FLAGS_MONO;
+	voice->usage = VOICE_USAGE_PLAYBACK;
 
-	if (wave_out->wave_fmt.bitsperchannel == 16)
-		voice_allocdesc.flags |= VOICEMGR_FLAGS_16BIT;
+	voice->flags = 0;
 
-	if ((wave_out->voice = emu10k1_voice_alloc(&card->voicemgr, &voice_allocdesc)) == NULL)
-		return CTSTATUS_ERROR;
+	if (woinst->format.channels == 2)
+		voice->flags |= VOICE_FLAGS_STEREO;
 
-	/* voice initialization */
+	if (woinst->format.bitsperchannel == 16)
+		voice->flags |= VOICE_FLAGS_16BIT;
 
-	left = &wave_out->voice->params;
+	if (emu10k1_voice_alloc(card, voice) < 0)
+		return -1;
 
 	/* Calculate pitch */
-	left->initial_pitch = (u16) (srToPitch(wave_out->wave_fmt.samplingrate) >> 8);
+	voice->initial_pitch = (u16) (srToPitch(woinst->format.samplingrate) >> 8);
+	voice->pitch_target = samplerate_to_linearpitch(woinst->format.samplingrate);
 
-	DPD(2, "Initial pitch --> %x\n", left->initial_pitch);
+	DPD(2, "Initial pitch --> 0x%x\n", voice->initial_pitch);
 
-	/* Easy way out.. gotta calculate value */
-	left->pitch_target = 0;
-	left->volume_target = 0;
-	left->FC_target = 0;
+	voice->startloop = (woinst->buffer.emupageindex << 12) / woinst->format.bytespersample;
+	voice->endloop = voice->startloop + woinst->buffer.size / woinst->format.bytespersample;
+	voice->start = voice->startloop;
+
+	if (voice->flags & VOICE_FLAGS_STEREO) {
+		voice->params[0].send_a = card->waveout.send_a[1];
+		voice->params[0].send_b = card->waveout.send_b[1];
+		voice->params[0].send_c = card->waveout.send_c[1];
+		voice->params[0].send_d = card->waveout.send_d[1];
 
-	left->byampl_env_sustain = 0x7f;
-	left->byampl_env_decay = 0x7f;
+		if (woinst->device)
+			voice->params[0].send_routing = 0xd23c;
+		else
+			voice->params[0].send_routing = card->waveout.send_routing[1];
 
-	if (wave_out->globalreverbFactor) {
-		u8 t = (card_waveout->globalreverb & 0xff) + (wave_out->localreverb & 0xff);
+		voice->params[0].volume_target = 0xffff;
+		voice->params[0].initial_fc = 0xff;
+		voice->params[0].initial_attn = 0x00;
+		voice->params[0].byampl_env_sustain = 0x7f;
+		voice->params[0].byampl_env_decay = 0x7f;
+
+		voice->params[1].send_a = card->waveout.send_a[2];
+		voice->params[1].send_b = card->waveout.send_b[2];
+		voice->params[1].send_c = card->waveout.send_c[2];
+		voice->params[1].send_d = card->waveout.send_d[2];
 
-		left->send_a = (t > 255) ? 255 : t;
-	} else {
-		left->send_a = 0;
-	}
-
-	if (wave_out->globalchorusFactor) {
-		u8 t = (card_waveout->globalchorus & 0xff) + (wave_out->localchorus & 0xff);
+		if (woinst->device)
+			voice->params[1].send_routing = 0xd23c;
+		else
+			voice->params[1].send_routing = card->waveout.send_routing[2];
 
-		left->send_d = (t > 255) ? 255 : t;
+		voice->params[1].volume_target = 0xffff;
+		voice->params[1].initial_fc = 0xff;
+		voice->params[1].initial_attn = 0x00;
+		voice->params[1].byampl_env_sustain = 0x7f;
+		voice->params[1].byampl_env_decay = 0x7f;
 	} else {
-		left->send_d = 0;
-	}
-
-	set_volume_instance(card_waveout, wave_out, left);
-
-	left->pan_target = left->send_c;
-	left->aux_target = left->send_b;
-
-	size = wavexferbuf->xferbufsize / wavexferbuf->bytespersample;
-	left->start = 2 * (wave_out->emupageindex << 11) / wavexferbuf->bytespersample;
-	left->end = left->start + size;
-	left->startloop = left->start;
-	left->endloop = left->end;
-
-	if (wave_out->voice->linked_voice) {
-		DPF(2, "is stereo\n");
-		right = &wave_out->voice->linked_voice->params;
-
-		right->initial_pitch = left->initial_pitch;
-
-		/* Easy way out.. gotta calculate value */
-		right->pitch_target = 0;
-		right->volume_target = 0;
-		right->FC_target = 0;
-
-		right->byampl_env_sustain = 0x7f;
-		right->byampl_env_decay = 0x7f;
-
-		right->send_d = left->send_d;
-		right->send_a = left->send_a;
-
-		/* Left output of right channel is always zero */
-		right->send_c = 0;
-
-		/* Update right channel aux */
-		right->pan_target = 0;
-		right->send_b = left->send_b;
-		right->aux_target = right->send_b;
-
-		/* Zero out right output of left channel */
-		left->send_b = 0;
-		left->aux_target = 0;
+		voice->params[0].send_a = card->waveout.send_a[0];
+		voice->params[0].send_b = card->waveout.send_b[0];
+		voice->params[0].send_c = card->waveout.send_c[0];
+		voice->params[0].send_d = card->waveout.send_d[0];
 
-		/* Update right channel attenuation */
-		right->initial_attn = left->initial_attn;
-
-		right->start = left->start;
-		right->end = left->end;
-		right->startloop = left->startloop;
-		right->endloop = left->endloop;
+		if (woinst->device)
+                        voice->params[0].send_routing = 0xd23c;
+                else
+			voice->params[0].send_routing = card->waveout.send_routing[0];
 
+		voice->params[0].volume_target = 0xffff;
+		voice->params[0].initial_fc = 0xff;
+		voice->params[0].initial_attn = 0x00;
+		voice->params[0].byampl_env_sustain = 0x7f;
+		voice->params[0].byampl_env_decay = 0x7f;
 	}
 
-	DPD(2, "voice: start=%x, end=%x, startloop=%x, endloop=%x\n", left->start, left->end, left->startloop, left->endloop);
+	DPD(2, "voice: startloop=0x%x, endloop=0x%x\n", voice->startloop, voice->endloop);
+
+	emu10k1_voice_playback_setup(voice);
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 int emu10k1_waveout_open(struct emu10k1_wavedevice *wave_dev)
 {
 	struct emu10k1_card *card = wave_dev->card;
 	struct woinst *woinst = wave_dev->woinst;
-	struct wave_out *wave_out;
-	u32 bytespersec, delay;
-	u32 buffsize;
+	u32 delay;
 
 	DPF(2, "emu10k1_waveout_open()\n");
 
-	if ((wave_out = (struct wave_out *) kmalloc(sizeof(struct wave_out), GFP_KERNEL)) == NULL) {
-		ERROR();
-		emu10k1_waveout_close(wave_dev);
-		return CTSTATUS_ERROR;
-	}
-
-	woinst->wave_out = wave_out;
-
-	/* Init channel object */
-	wave_out->state = CARDWAVE_STATE_STOPPED;
-	wave_out->wave_fmt = woinst->wave_fmt;
-	wave_out->voice = NULL;
-	wave_out->emupageindex = -1;
-	wave_out->wavexferbuf = NULL;
-	wave_out->pagetable = NULL;
-	wave_out->timer = NULL;
-
-	/* Assign default local volume */
-	/* FIXME: Should we be maxing the initial values like this? */
-	wave_out->localvol = 0xffffffff;
-	wave_out->localreverb = 0xffffffff;
-	wave_out->localchorus = 0xffffffff;
-	wave_out->globalvolFactor = 0xffff;
-	wave_out->globalreverbFactor = 0xffff;
-	wave_out->globalchorusFactor = 0xffff;
-
-	wave_out->setpos = 0;
-	wave_out->position = 0;
-
-	wave_out->fill_silence = 0;
-
-	if ((wave_out->wavexferbuf = (struct wave_xferbuf *) kmalloc(sizeof(struct wave_xferbuf), GFP_KERNEL)) == NULL) {
+	if (alloc_buffer(card, &woinst->buffer) < 0) {
 		ERROR();
 		emu10k1_waveout_close(wave_dev);
-		return CTSTATUS_ERROR;
+		return -1;
 	}
 
-	buffsize = woinst->fragment_size * woinst->numfrags;
+	woinst->buffer.fill_silence = 0;
+	woinst->buffer.silence_bytes = 0;
+	woinst->buffer.silence_pos = 0;
+	woinst->buffer.hw_pos = 0;
+	woinst->buffer.bytestocopy = woinst->buffer.size;
 
-	if (get_xferbuffer(card, wave_out, &buffsize) != CTSTATUS_SUCCESS) {
+	if (get_voice(card, woinst) < 0) {
 		ERROR();
 		emu10k1_waveout_close(wave_dev);
-		return CTSTATUS_ERROR;
+		return -1;
 	}
 
-	woinst->fragment_size = buffsize / woinst->numfrags;
-	wave_out->callbacksize = woinst->fragment_size;
+	delay = (48000 * woinst->buffer.fragment_size) / woinst->format.bytespersec;
 
-	if (get_voice(card, wave_out, woinst->device) != CTSTATUS_SUCCESS) {
-		ERROR();
-		emu10k1_waveout_close(wave_dev);
-		return CTSTATUS_ERROR;
-	}
+	emu10k1_timer_install(card, &woinst->timer, delay / 2);
 
-	bytespersec = wave_out->wave_fmt.channels * (wave_out->wave_fmt.bitsperchannel >> 3) * (wave_out->wave_fmt.samplingrate);
-	delay = (48000 * wave_out->callbacksize) / bytespersec;
+	woinst->state = WAVE_STATE_OPEN;
 
-	if ((wave_out->timer = emu10k1_timer_install(card, emu10k1_waveout_bh, (unsigned long) wave_dev, delay / 2)) == NULL) {
-		ERROR();
-		emu10k1_waveout_close(wave_dev);
-		return CTSTATUS_ERROR;
-	}
-
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 void emu10k1_waveout_close(struct emu10k1_wavedevice *wave_dev)
 {
 	struct emu10k1_card *card = wave_dev->card;
-	struct wave_out *wave_out = wave_dev->woinst->wave_out;
+	struct woinst *woinst = wave_dev->woinst;
 
 	DPF(2, "emu10k1_waveout_close()\n");
 
-	if (wave_out->state != CARDWAVE_STATE_STOPPED)
-		emu10k1_waveout_stop(wave_dev);
-
-	if (wave_out->timer != NULL)
-		emu10k1_timer_uninstall(card, wave_out->timer);
+	emu10k1_waveout_stop(wave_dev);
 
-	if (wave_out->voice != NULL)
-		emu10k1_voice_free(&card->voicemgr, wave_out->voice);
+	emu10k1_timer_uninstall(card, &woinst->timer);
 
-	if (wave_out->emupageindex >= 0)
-		dealloc_xferbuffer(card, wave_out);
+	emu10k1_voice_free(&woinst->voice);
 
-	if (wave_out->wavexferbuf != NULL)
-		kfree(wave_out->wavexferbuf);
+	free_buffer(card, &woinst->buffer);
 
-	kfree(wave_out);
-	wave_dev->woinst->wave_out = NULL;
+	woinst->state = WAVE_STATE_CLOSED;
 
 	return;
 }
 
-int emu10k1_waveout_start(struct emu10k1_wavedevice *wave_dev)
+void emu10k1_waveout_start(struct emu10k1_wavedevice *wave_dev)
 {
 	struct emu10k1_card *card = wave_dev->card;
-	struct wave_out *wave_out = wave_dev->woinst->wave_out;
-	u32 start, startPosition;
+	struct woinst *woinst = wave_dev->woinst;
 
 	DPF(2, "emu10k1_waveout_start()\n");
-
-	/* If already started, return success */
-	if (wave_out->state == CARDWAVE_STATE_STARTED)
-		return CTSTATUS_SUCCESS;
-
-	if (wave_out->state == CARDWAVE_STATE_STOPPED && wave_out->setpos)
-		startPosition = wave_out->position / (wave_out->wavexferbuf->bytespersample);
-	else
-		startPosition = wave_out->wavexferbuf->stopposition;
-
-	start = wave_out->voice->params.start;
-	wave_out->voice->params.start += startPosition;
-
-	DPD(2, "CA is %x\n", wave_out->voice->params.start);
-
-	emu10k1_voice_playback_setup(wave_out->voice);
-
-	wave_out->voice->params.start = start;
-
 	/* Actual start */
-	emu10k1_voice_start(wave_out->voice);
 
-	wave_out->state = CARDWAVE_STATE_STARTED;
-	wave_out->setpos = 0;
+	emu10k1_voice_start(&woinst->voice, woinst->total_played);
+
+	emu10k1_timer_enable(card, &woinst->timer);
 
-	emu10k1_timer_enable(card, wave_out->timer);
+	woinst->state |= WAVE_STATE_STARTED;
 
-	return CTSTATUS_SUCCESS;
+	return;
 }
 
-int emu10k1_waveout_setformat(struct emu10k1_wavedevice *wave_dev)
+int emu10k1_waveout_setformat(struct emu10k1_wavedevice *wave_dev, struct wave_format *format)
 {
 	struct emu10k1_card *card = wave_dev->card;
 	struct woinst *woinst = wave_dev->woinst;
-	struct wave_out *wave_out = woinst->wave_out;
-	u32 bytespersec, delay;
+	u32 delay;
 
 	DPF(2, "emu10k1_waveout_setformat()\n");
 
-	query_format(&woinst->wave_fmt);
-
-	if (wave_out == NULL)
-		return CTSTATUS_SUCCESS;
-
-	if (wave_out->state == CARDWAVE_STATE_STARTED) {
-		woinst->wave_fmt = wave_out->wave_fmt;
-		return CTSTATUS_SUCCESS;
-	}
+	if (woinst->state & WAVE_STATE_STARTED)
+		return -1;
 
-	if ((wave_out->wave_fmt.samplingrate != woinst->wave_fmt.samplingrate)
-	    || (wave_out->wave_fmt.bitsperchannel != woinst->wave_fmt.bitsperchannel)
-	    || (wave_out->wave_fmt.channels != woinst->wave_fmt.channels)) {
-		struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf;
+	query_format(format);
 
-		emu10k1_timer_uninstall(card, wave_out->timer);
+	if (woinst->format.samplingrate != format->samplingrate ||
+	    woinst->format.channels != format->channels ||
+	    woinst->format.bitsperchannel != format->bitsperchannel) {
 
-		emu10k1_voice_free(&card->voicemgr, wave_out->voice);
+		woinst->format = *format;
 
-		wave_out->wave_fmt = woinst->wave_fmt;
-		wave_out->timer = NULL;
+		if (woinst->state == WAVE_STATE_CLOSED)
+			return 0;
 
-		wavexferbuf->xferpos = 0;
-		wavexferbuf->silence_xferpos = 0;
-		wavexferbuf->stopposition = 0;
-		wavexferbuf->is_stereo = (wave_out->wave_fmt.channels == 2) ? 1 : 0;
-		wavexferbuf->is_16bit = (wave_out->wave_fmt.bitsperchannel == 16) ? 1 : 0;
-		wavexferbuf->bytespersample = (wavexferbuf->is_stereo + 1) * (wavexferbuf->is_16bit + 1);
+		emu10k1_timer_uninstall(card, &woinst->timer);
+		emu10k1_voice_free(&woinst->voice);
 
-		if (get_voice(card, wave_out, woinst->device) != CTSTATUS_SUCCESS) {
+		if (get_voice(card, woinst) < 0) {
 			ERROR();
 			emu10k1_waveout_close(wave_dev);
-			return CTSTATUS_ERROR;
+			return -1;
 		}
 
-		bytespersec = wave_out->wave_fmt.channels * (wave_out->wave_fmt.bitsperchannel >> 3) * (wave_out->wave_fmt.samplingrate);
-		delay = (48000 * wave_out->callbacksize) / bytespersec;
+		delay = (48000 * woinst->buffer.fragment_size) / woinst->format.bytespersec;
 
-		if ((wave_out->timer = emu10k1_timer_install(card, emu10k1_waveout_bh, (unsigned long) wave_dev, delay / 2)) == NULL) {
-			ERROR();
-			emu10k1_waveout_close(wave_dev);
-			return CTSTATUS_ERROR;
-		}
+		emu10k1_timer_install(card, &woinst->timer, delay / 2);
 	}
 
-	return CTSTATUS_SUCCESS;
+	return 0;
 }
 
 void emu10k1_waveout_stop(struct emu10k1_wavedevice *wave_dev)
 {
 	struct emu10k1_card *card = wave_dev->card;
-	struct wave_out *wave_out = wave_dev->woinst->wave_out;
-	struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf;
-	u32 samples = 32;
-	u32 position;
+	struct woinst *woinst = wave_dev->woinst;
 
 	DPF(2, "emu10k1_waveout_stop()\n");
 
-	if (wave_out->state == CARDWAVE_STATE_STOPPED)
+	if (!(woinst->state & WAVE_STATE_STARTED))
 		return;
 
-	emu10k1_timer_disable(card, wave_out->timer);
+	emu10k1_timer_disable(card, &woinst->timer);
 
 	/* Stop actual voice */
-	emu10k1_voice_stop(wave_out->voice);
-
-	/* Save the stop position */
-	emu10k1_voice_getcontrol(wave_out->voice, CCCA_CURRADDR, &wavexferbuf->stopposition);
-
-	wavexferbuf->stopposition -= wave_out->voice->params.start;
-
-	/* Refer to voicemgr.c, CA is not started at zero.  We need to take this into account. */
-	position = wavexferbuf->stopposition * wavexferbuf->bytespersample;
-
-	if (!wavexferbuf->is_16bit)
-		samples <<= 1;
+	emu10k1_voice_stop(&woinst->voice);
 
-	if (wavexferbuf->is_stereo)
-		samples <<= 1;
+	emu10k1_waveout_update(woinst);
 
-	samples -= 4;
-
-	if (position >= samples * (wavexferbuf->is_16bit + 1))
-		position -= samples * (wavexferbuf->is_16bit + 1);
-	else
-		position += wavexferbuf->xferbufsize - samples * (wavexferbuf->is_16bit + 1);
-
-	wavexferbuf->stopposition = position / wavexferbuf->bytespersample;
-
-	DPD(2, "position is %x\n", wavexferbuf->stopposition);
-
-	wave_out->state = CARDWAVE_STATE_STOPPED;
-	wave_out->setpos = 0;
-	wave_out->position = 0;
+	woinst->state &= ~WAVE_STATE_STARTED;
 
 	return;
 }
 
-void emu10k1_waveout_getxfersize(struct wave_out *wave_out, u32 * size, u32 * pending, u32 * curpos)
+void emu10k1_waveout_getxfersize(struct woinst *woinst, u32 * size)
 {
-	struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf;
-
-	/* Get position of current address, this is in no. of bytes in play buffer */
-	emu10k1_waveout_getcontrol(wave_out, WAVECURPOS, curpos);
+	struct waveout_buffer *buffer = &woinst->buffer;
+	int pending;
 
-	if ((*curpos > wavexferbuf->silence_xferpos)
-	    || ((*curpos == wavexferbuf->silence_xferpos)
-		&& (wave_out->state == CARDWAVE_STATE_STARTED))
-	    || ((*curpos == wavexferbuf->silence_xferpos) && (wavexferbuf->silence_xferpos != 0)
-		&& (wave_out->state == CARDWAVE_STATE_STOPPED))) {
-		*size = *curpos - wavexferbuf->silence_xferpos;
-		*pending = wavexferbuf->xferbufsize - *size;
-	} else {
-		*pending = wavexferbuf->silence_xferpos - *curpos;
-		*size = wavexferbuf->xferbufsize - *pending;
+	if (woinst->mmapped) {
+		*size = buffer->bytestocopy;
+		return;
 	}
 
-	if (wavexferbuf->silence_xferpos != wavexferbuf->xferpos) {
-		if (*pending < wave_out->callbacksize) {
-			wave_out->fill_silence = 2;
-			*pending = 0;
-			*size = wavexferbuf->xferbufsize;
-			wavexferbuf->xferpos = *curpos;
-		} else {
-			if (wave_out->fill_silence == 2) {
-				*pending = 0;
-				*size = wavexferbuf->xferbufsize;
-				wavexferbuf->xferpos = *curpos;
-			} else {
-				*pending -= wave_out->callbacksize;
-				*size += wave_out->callbacksize;
-			}
-		}
+	pending = buffer->size - buffer->bytestocopy;
+
+	buffer->fill_silence = (pending < (signed) buffer->fragment_size) ? 1 : 0;
+
+	if (pending > (signed) buffer->silence_bytes) {
+		*size = buffer->bytestocopy + buffer->silence_bytes;
 	} else {
-		if (*pending < wave_out->callbacksize)
-			wave_out->fill_silence = 1;
-		else
-			wave_out->fill_silence = 0;
+		*size = buffer->size;
+		buffer->silence_bytes = pending;
+		if (pending < 0) {
+			buffer->silence_pos = buffer->hw_pos;
+			buffer->silence_bytes = 0;
+			buffer->bytestocopy = buffer->size;
+			DPF(1, "buffer underrun\n");
+		}
 	}
 
 	return;
 }
 
-static void copy_block(u32 dst, u8 * src, u32 len, void **pt)
+static void copy_block(void **dst, u32 str, u8 *src, u32 len)
 {
 	int i, j, k;
 
-	i = dst / PAGE_SIZE;
-	j = dst % PAGE_SIZE;
-	k = (len > PAGE_SIZE - j) ? PAGE_SIZE - j : len;
-	copy_from_user(pt[i] + j, src, k);
-	len -= k;
-	while (len >= PAGE_SIZE) {
-		copy_from_user(pt[++i], src + k, PAGE_SIZE);
-		k += PAGE_SIZE;
-		len -= PAGE_SIZE;
-	}
-	copy_from_user(pt[++i], src + k, len);
+	i = str / PAGE_SIZE;
+	j = str % PAGE_SIZE;
+
+	if (len > PAGE_SIZE - j) {
+		k = PAGE_SIZE - j;
+		copy_from_user(dst[i] + j, src, k);
+		len -= k;
+		while (len > PAGE_SIZE) {
+                	copy_from_user(dst[++i], src + k, PAGE_SIZE);
+                	k += PAGE_SIZE;
+                	len -= PAGE_SIZE;
+        	}
+        	copy_from_user(dst[++i], src + k, len);
+
+	} else
+		copy_from_user(dst[i] + j, src, len);
 
 	return;
 }
 
-static void fill_block(u32 dst, u8 val, u32 len, void **pt)
+static void fill_block(void **dst, u32 str, u8 src, u32 len)
 {
 	int i, j, k;
 
-	i = dst / PAGE_SIZE;
-	j = dst % PAGE_SIZE;
-	k = (len > PAGE_SIZE - j) ? PAGE_SIZE - j : len;
-	memset(pt[i] + j, val, k);
-	len -= k;
-	while (len >= PAGE_SIZE) {
-		memset(pt[++i], val, PAGE_SIZE);
-		len -= PAGE_SIZE;
-	}
-	memset(pt[++i], val, len);
+	i = str / PAGE_SIZE;
+	j = str % PAGE_SIZE;
+
+	if (len > PAGE_SIZE - j) {
+                k = PAGE_SIZE - j;
+                memset(dst[i] + j, src, k);
+                len -= k;
+                while (len > PAGE_SIZE) {
+                        memset(dst[++i], src, PAGE_SIZE);
+                        len -= PAGE_SIZE;
+                }
+                memset(dst[++i], src, len);
+
+        } else
+                memset(dst[i] + j, src, len);
 
 	return;
 }
 
-void emu10k1_waveout_xferdata(struct woinst *woinst, u8 * data, u32 * size)
+void emu10k1_waveout_xferdata(struct woinst *woinst, u8 *data, u32 *size)
 {
-	struct wave_out *wave_out = woinst->wave_out;
-	struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf;
+	struct waveout_buffer *buffer = &woinst->buffer;
 	u32 sizetocopy, sizetocopy_now, start;
 	unsigned long flags;
 
-	sizetocopy = min(wavexferbuf->xferbufsize, *size);
+	sizetocopy = min(buffer->size, *size);
 	*size = sizetocopy;
 
 	if (!sizetocopy)
 		return;
 
 	spin_lock_irqsave(&woinst->lock, flags);
+	start = (buffer->size + buffer->silence_pos - buffer->silence_bytes) % buffer->size;
 
-	sizetocopy_now = wavexferbuf->xferbufsize - wavexferbuf->xferpos;
+	if(sizetocopy > buffer->silence_bytes) {
+		buffer->silence_pos += sizetocopy - buffer->silence_bytes;
+		buffer->bytestocopy -= sizetocopy - buffer->silence_bytes;
+		buffer->silence_bytes = 0;
+	} else
+		buffer->silence_bytes -= sizetocopy;
 
-	start = wavexferbuf->xferpos;
+	sizetocopy_now = buffer->size - start;
+
+	spin_unlock_irqrestore(&woinst->lock, flags);
 
 	if (sizetocopy > sizetocopy_now) {
 		sizetocopy -= sizetocopy_now;
-		wavexferbuf->xferpos = sizetocopy;
-		wavexferbuf->silence_xferpos = wavexferbuf->xferpos;
-		spin_unlock_irqrestore(&woinst->lock, flags);
-
-		copy_block(start, data, sizetocopy_now, wavexferbuf->xferbuffer);
-		copy_block(0, data + sizetocopy_now, sizetocopy, wavexferbuf->xferbuffer);
+		copy_block(buffer->addr, start, data, sizetocopy_now);
+		copy_block(buffer->addr, 0, data + sizetocopy_now, sizetocopy);
 	} else {
-		if (sizetocopy == sizetocopy_now)
-			wavexferbuf->xferpos = 0;
-		else
-			wavexferbuf->xferpos += sizetocopy;
-
-		wavexferbuf->silence_xferpos = wavexferbuf->xferpos;
-		spin_unlock_irqrestore(&woinst->lock, flags);
-
-		copy_block(start, data, sizetocopy, wavexferbuf->xferbuffer);
+		copy_block(buffer->addr, start, data, sizetocopy);
 	}
 
 	return;
@@ -672,84 +452,63 @@
 
 void emu10k1_waveout_fillsilence(struct woinst *woinst)
 {
-	struct wave_out *wave_out = woinst->wave_out;
-	struct wave_xferbuf *wavexferbuf = wave_out->wavexferbuf;
+	struct waveout_buffer *buffer = &woinst->buffer;
 	u16 filldata;
 	u32 sizetocopy, sizetocopy_now, start;
 	unsigned long flags;
 
-	sizetocopy = wave_out->callbacksize;
+	sizetocopy = woinst->buffer.fragment_size;
 
-	if (wave_out->wave_fmt.bitsperchannel == 8)
-		filldata = 0x8080;
-	else
+	if (woinst->format.bitsperchannel == 16)
 		filldata = 0x0000;
+	else
+		filldata = 0x8080;
 
 	spin_lock_irqsave(&woinst->lock, flags);
+	buffer->silence_bytes += sizetocopy;
+	buffer->bytestocopy -= sizetocopy;
+	buffer->silence_pos %= buffer->size;
+	start = buffer->silence_pos;
+	buffer->silence_pos += sizetocopy;
+	sizetocopy_now = buffer->size - start;
 
-	sizetocopy_now = wavexferbuf->xferbufsize - wavexferbuf->silence_xferpos;
-	start = wavexferbuf->silence_xferpos;
+	spin_unlock_irqrestore(&woinst->lock, flags);
 
 	if (sizetocopy > sizetocopy_now) {
 		sizetocopy -= sizetocopy_now;
-		wavexferbuf->silence_xferpos = sizetocopy;
-		spin_unlock_irqrestore(&woinst->lock, flags);
-		fill_block(start, filldata, sizetocopy_now, wavexferbuf->xferbuffer);
-		fill_block(0, filldata, sizetocopy, wavexferbuf->xferbuffer);
+		fill_block(buffer->addr, start, filldata, sizetocopy_now);
+		fill_block(buffer->addr, 0, filldata, sizetocopy);
 	} else {
-		if (sizetocopy == sizetocopy_now)
-			wavexferbuf->silence_xferpos = 0;
-		else
-			wavexferbuf->silence_xferpos += sizetocopy;
-
-		spin_unlock_irqrestore(&woinst->lock, flags);
-
-		fill_block(start, filldata, sizetocopy, wavexferbuf->xferbuffer);
+		fill_block(buffer->addr, start, filldata, sizetocopy);
 	}
 
 	return;
 }
 
-/* get the specified control value of the wave device. */
-
-int emu10k1_waveout_getcontrol(struct wave_out *wave_out, u32 ctrl_id, u32 * value)
+void emu10k1_waveout_update(struct woinst *woinst)
 {
-	switch (ctrl_id) {
-	case WAVECURPOS:
-		/* There is no actual start yet */
-		if (wave_out->state == CARDWAVE_STATE_STOPPED) {
-			if (wave_out->setpos)
-				*value = wave_out->position;
-			else
-				*value = wave_out->wavexferbuf->stopposition * wave_out->wavexferbuf->bytespersample;
-		} else {
-			emu10k1_voice_getcontrol(wave_out->voice, CCCA_CURRADDR, value);
-
-			*value -= wave_out->voice->params.start;
+	u32 hw_pos;
+	u32 diff;
 
-			/* Get number of bytes in play buffer per channel.
-			 * If 8 bit mode is enabled, this needs to be changed. */
-			{
-				u32 samples = 64 * (wave_out->wavexferbuf->is_stereo + 1);
-
-				*value *= wave_out->wavexferbuf->bytespersample;
-
-				/* Refer to voicemgr.c, CA is not started at zero.
-				 * We need to take this into account. */
-
-				samples -= 4 * (wave_out->wavexferbuf->is_16bit + 1);
+	/* There is no actual start yet */
+	if (!(woinst->state & WAVE_STATE_STARTED)) {
+		hw_pos = woinst->buffer.hw_pos;
+	} else {
+		/* hw_pos in sample units */
+		hw_pos = sblive_readptr(woinst->voice.card, CCCA_CURRADDR, woinst->voice.num);
 
-				if (*value >= samples)
-					*value -= samples;
-				else
-					*value += wave_out->wavexferbuf->xferbufsize - samples;
-			}
-		}
+		if(hw_pos < woinst->voice.start)
+			hw_pos += woinst->buffer.size / woinst->format.bytespersample - woinst->voice.start;
+		else
+			hw_pos -= woinst->voice.start;
 
-		break;
-	default:
-		return CTSTATUS_ERROR;
+		hw_pos *= woinst->format.bytespersample;
 	}
 
-	return CTSTATUS_SUCCESS;
+	diff = (woinst->buffer.size + hw_pos - woinst->buffer.hw_pos) % woinst->buffer.size;
+	woinst->total_played += diff;
+	woinst->buffer.bytestocopy += diff;
+	woinst->buffer.hw_pos = hw_pos;
+
+	return;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)