374 lines
		
	
	
	
		
			9.3 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			374 lines
		
	
	
	
		
			9.3 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  ********************************************************************** | ||
|  |  *     cardwi.c - PCM input HAL for emu10k1 driver | ||
|  |  *     Copyright 1999, 2000 Creative Labs, Inc. | ||
|  |  * | ||
|  |  ********************************************************************** | ||
|  |  * | ||
|  |  *     Date                 Author          Summary of changes | ||
|  |  *     ----                 ------          ------------------ | ||
|  |  *     October 20, 1999     Bertrand Lee    base code release | ||
|  |  * | ||
|  |  ********************************************************************** | ||
|  |  * | ||
|  |  *     This program is free software; you can redistribute it and/or | ||
|  |  *     modify it under the terms of the GNU General Public License as | ||
|  |  *     published by the Free Software Foundation; either version 2 of | ||
|  |  *     the License, or (at your option) any later version. | ||
|  |  * | ||
|  |  *     This program is distributed in the hope that it will be useful, | ||
|  |  *     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
|  |  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||
|  |  *     GNU General Public License for more details. | ||
|  |  * | ||
|  |  *     You should have received a copy of the GNU General Public | ||
|  |  *     License along with this program; if not, write to the Free | ||
|  |  *     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, | ||
|  |  *     USA. | ||
|  |  * | ||
|  |  ********************************************************************** | ||
|  |  */ | ||
|  | 
 | ||
|  | #include <linux/poll.h>
 | ||
|  | #include "hwaccess.h"
 | ||
|  | #include "timer.h"
 | ||
|  | #include "recmgr.h"
 | ||
|  | #include "audio.h"
 | ||
|  | #include "cardwi.h"
 | ||
|  | 
 | ||
|  | /**
 | ||
|  |  * query_format - returns a valid sound format | ||
|  |  * | ||
|  |  * This function will return a valid sound format as close | ||
|  |  * to the requested one as possible.  | ||
|  |  */ | ||
|  | static void query_format(int recsrc, struct wave_format *wave_fmt) | ||
|  | { | ||
|  | 
 | ||
|  | 	switch (recsrc) { | ||
|  | 	case WAVERECORD_AC97: | ||
|  | 
 | ||
|  | 		if ((wave_fmt->channels != 1) && (wave_fmt->channels != 2)) | ||
|  | 			wave_fmt->channels = 2; | ||
|  | 
 | ||
|  | 		if (wave_fmt->samplingrate >= (0xBB80 + 0xAC44) / 2) | ||
|  | 			wave_fmt->samplingrate = 0xBB80; | ||
|  | 		else if (wave_fmt->samplingrate >= (0xAC44 + 0x7D00) / 2) | ||
|  | 			wave_fmt->samplingrate = 0xAC44; | ||
|  | 		else if (wave_fmt->samplingrate >= (0x7D00 + 0x5DC0) / 2) | ||
|  | 			wave_fmt->samplingrate = 0x7D00; | ||
|  | 		else if (wave_fmt->samplingrate >= (0x5DC0 + 0x5622) / 2) | ||
|  | 			wave_fmt->samplingrate = 0x5DC0; | ||
|  | 		else if (wave_fmt->samplingrate >= (0x5622 + 0x3E80) / 2) | ||
|  | 			wave_fmt->samplingrate = 0x5622; | ||
|  | 		else if (wave_fmt->samplingrate >= (0x3E80 + 0x2B11) / 2) | ||
|  | 			wave_fmt->samplingrate = 0x3E80; | ||
|  | 		else if (wave_fmt->samplingrate >= (0x2B11 + 0x1F40) / 2) | ||
|  | 			wave_fmt->samplingrate = 0x2B11; | ||
|  | 		else | ||
|  | 			wave_fmt->samplingrate = 0x1F40; | ||
|  | 
 | ||
|  | 		switch (wave_fmt->id) { | ||
|  | 		case AFMT_S16_LE: | ||
|  | 			wave_fmt->bitsperchannel = 16; | ||
|  | 			break; | ||
|  | 		case AFMT_U8: | ||
|  | 			wave_fmt->bitsperchannel = 8; | ||
|  | 			break; | ||
|  | 		default: | ||
|  | 			wave_fmt->id = AFMT_S16_LE; | ||
|  | 			wave_fmt->bitsperchannel = 16; | ||
|  | 			break; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		break; | ||
|  | 
 | ||
|  | 	/* these can't be changed from the original values */ | ||
|  | 	case WAVERECORD_MIC: | ||
|  | 	case WAVERECORD_FX: | ||
|  | 		break; | ||
|  | 
 | ||
|  | 	default: | ||
|  | 		BUG(); | ||
|  | 		break; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	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; | ||
|  | 	wave_fmt->bytespervoicesample = wave_fmt->bytespersample; | ||
|  | } | ||
|  | 
 | ||
|  | static int alloc_buffer(struct emu10k1_card *card, struct wavein_buffer *buffer) | ||
|  | { | ||
|  | 	buffer->addr = pci_alloc_consistent(card->pci_dev, buffer->size * buffer->cov, | ||
|  | 					    &buffer->dma_handle); | ||
|  | 	if (buffer->addr == NULL) | ||
|  | 		return -1; | ||
|  | 
 | ||
|  | 	return 0; | ||
|  | } | ||
|  | 
 | ||
|  | static void free_buffer(struct emu10k1_card *card, struct wavein_buffer *buffer) | ||
|  | { | ||
|  | 	if (buffer->addr != NULL) | ||
|  | 		pci_free_consistent(card->pci_dev, buffer->size * buffer->cov, | ||
|  | 				    buffer->addr, buffer->dma_handle); | ||
|  | } | ||
|  | 
 | ||
|  | int emu10k1_wavein_open(struct emu10k1_wavedevice *wave_dev) | ||
|  | { | ||
|  | 	struct emu10k1_card *card = wave_dev->card; | ||
|  | 	struct wiinst *wiinst = wave_dev->wiinst; | ||
|  | 	struct wiinst **wiinst_tmp = NULL; | ||
|  | 	u16 delay; | ||
|  | 	unsigned long flags; | ||
|  | 
 | ||
|  | 	DPF(2, "emu10k1_wavein_open()\n"); | ||
|  | 
 | ||
|  | 	switch (wiinst->recsrc) { | ||
|  | 	case WAVERECORD_AC97: | ||
|  | 		wiinst_tmp = &card->wavein.ac97; | ||
|  | 		break; | ||
|  | 	case WAVERECORD_MIC: | ||
|  | 		wiinst_tmp = &card->wavein.mic; | ||
|  | 		break; | ||
|  | 	case WAVERECORD_FX: | ||
|  | 		wiinst_tmp = &card->wavein.fx; | ||
|  | 		break; | ||
|  | 	default: | ||
|  | 		BUG(); | ||
|  | 		break; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	spin_lock_irqsave(&card->lock, flags); | ||
|  | 	if (*wiinst_tmp != NULL) { | ||
|  | 		spin_unlock_irqrestore(&card->lock, flags); | ||
|  | 		return -1; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	*wiinst_tmp = wiinst; | ||
|  | 	spin_unlock_irqrestore(&card->lock, flags); | ||
|  | 
 | ||
|  | 	/* handle 8 bit recording */ | ||
|  | 	if (wiinst->format.bytesperchannel == 1) { | ||
|  | 		if (wiinst->buffer.size > 0x8000) { | ||
|  | 			wiinst->buffer.size = 0x8000; | ||
|  | 			wiinst->buffer.sizeregval = 0x1f; | ||
|  | 		} else | ||
|  | 			wiinst->buffer.sizeregval += 4; | ||
|  | 
 | ||
|  | 		wiinst->buffer.cov = 2; | ||
|  | 	} else | ||
|  | 		wiinst->buffer.cov = 1; | ||
|  | 
 | ||
|  | 	if (alloc_buffer(card, &wiinst->buffer) < 0) { | ||
|  | 		ERROR(); | ||
|  | 		return -1; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	emu10k1_set_record_src(card, wiinst); | ||
|  | 
 | ||
|  | 	emu10k1_reset_record(card, &wiinst->buffer); | ||
|  | 
 | ||
|  | 	wiinst->buffer.hw_pos = 0; | ||
|  | 	wiinst->buffer.pos = 0; | ||
|  | 	wiinst->buffer.bytestocopy = 0; | ||
|  | 
 | ||
|  | 	delay = (48000 * wiinst->buffer.fragment_size) / wiinst->format.bytespersec; | ||
|  | 
 | ||
|  | 	emu10k1_timer_install(card, &wiinst->timer, delay / 2); | ||
|  | 
 | ||
|  | 	wiinst->state = WAVE_STATE_OPEN; | ||
|  | 
 | ||
|  | 	return 0; | ||
|  | } | ||
|  | 
 | ||
|  | void emu10k1_wavein_close(struct emu10k1_wavedevice *wave_dev) | ||
|  | { | ||
|  | 	struct emu10k1_card *card = wave_dev->card; | ||
|  | 	struct wiinst *wiinst = wave_dev->wiinst; | ||
|  | 	unsigned long flags; | ||
|  | 
 | ||
|  | 	DPF(2, "emu10k1_wavein_close()\n"); | ||
|  | 
 | ||
|  | 	emu10k1_wavein_stop(wave_dev); | ||
|  | 
 | ||
|  | 	emu10k1_timer_uninstall(card, &wiinst->timer); | ||
|  | 
 | ||
|  | 	free_buffer(card, &wiinst->buffer); | ||
|  | 
 | ||
|  | 	spin_lock_irqsave(&card->lock, flags); | ||
|  | 	switch (wave_dev->wiinst->recsrc) { | ||
|  | 	case WAVERECORD_AC97: | ||
|  | 		card->wavein.ac97 = NULL; | ||
|  | 		break; | ||
|  | 	case WAVERECORD_MIC: | ||
|  | 		card->wavein.mic = NULL; | ||
|  | 		break; | ||
|  | 	case WAVERECORD_FX: | ||
|  | 		card->wavein.fx = NULL; | ||
|  | 		break; | ||
|  | 	default: | ||
|  | 		BUG(); | ||
|  | 		break; | ||
|  | 	} | ||
|  | 	spin_unlock_irqrestore(&card->lock, flags); | ||
|  | 
 | ||
|  | 	wiinst->state = WAVE_STATE_CLOSED; | ||
|  | } | ||
|  | 
 | ||
|  | void emu10k1_wavein_start(struct emu10k1_wavedevice *wave_dev) | ||
|  | { | ||
|  | 	struct emu10k1_card *card = wave_dev->card; | ||
|  | 	struct wiinst *wiinst = wave_dev->wiinst; | ||
|  | 
 | ||
|  | 	DPF(2, "emu10k1_wavein_start()\n"); | ||
|  | 
 | ||
|  | 	emu10k1_start_record(card, &wiinst->buffer); | ||
|  | 	emu10k1_timer_enable(wave_dev->card, &wiinst->timer); | ||
|  | 
 | ||
|  | 	wiinst->state |= WAVE_STATE_STARTED; | ||
|  | } | ||
|  | 
 | ||
|  | void emu10k1_wavein_stop(struct emu10k1_wavedevice *wave_dev) | ||
|  | { | ||
|  | 	struct emu10k1_card *card = wave_dev->card; | ||
|  | 	struct wiinst *wiinst = wave_dev->wiinst; | ||
|  | 
 | ||
|  | 	DPF(2, "emu10k1_wavein_stop()\n"); | ||
|  | 
 | ||
|  | 	if (!(wiinst->state & WAVE_STATE_STARTED)) | ||
|  | 		return; | ||
|  | 
 | ||
|  | 	emu10k1_timer_disable(card, &wiinst->timer); | ||
|  | 	emu10k1_stop_record(card, &wiinst->buffer); | ||
|  | 
 | ||
|  | 	wiinst->state &= ~WAVE_STATE_STARTED; | ||
|  | } | ||
|  | 
 | ||
|  | int emu10k1_wavein_setformat(struct emu10k1_wavedevice *wave_dev, struct wave_format *format) | ||
|  | { | ||
|  | 	struct emu10k1_card *card = wave_dev->card; | ||
|  | 	struct wiinst *wiinst = wave_dev->wiinst; | ||
|  | 	u16 delay; | ||
|  | 
 | ||
|  | 	DPF(2, "emu10k1_wavein_setformat()\n"); | ||
|  | 
 | ||
|  | 	if (wiinst->state & WAVE_STATE_STARTED) | ||
|  | 		return -1; | ||
|  | 
 | ||
|  | 	query_format(wiinst->recsrc, format); | ||
|  | 
 | ||
|  | 	if ((wiinst->format.samplingrate != format->samplingrate) | ||
|  | 	    || (wiinst->format.bitsperchannel != format->bitsperchannel) | ||
|  | 	    || (wiinst->format.channels != format->channels)) { | ||
|  | 
 | ||
|  | 		wiinst->format = *format; | ||
|  | 
 | ||
|  | 		if (wiinst->state == WAVE_STATE_CLOSED) | ||
|  | 			return 0; | ||
|  | 
 | ||
|  | 		wiinst->buffer.size *= wiinst->buffer.cov; | ||
|  | 
 | ||
|  | 		if (wiinst->format.bytesperchannel == 1) { | ||
|  | 			wiinst->buffer.cov = 2; | ||
|  | 			wiinst->buffer.size /= wiinst->buffer.cov; | ||
|  | 		} else | ||
|  | 			wiinst->buffer.cov = 1; | ||
|  | 
 | ||
|  | 		emu10k1_timer_uninstall(card, &wiinst->timer); | ||
|  | 
 | ||
|  | 		delay = (48000 * wiinst->buffer.fragment_size) / wiinst->format.bytespersec; | ||
|  | 
 | ||
|  | 		emu10k1_timer_install(card, &wiinst->timer, delay / 2); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return 0; | ||
|  | } | ||
|  | 
 | ||
|  | void emu10k1_wavein_getxfersize(struct wiinst *wiinst, u32 * size) | ||
|  | { | ||
|  | 	struct wavein_buffer *buffer = &wiinst->buffer; | ||
|  | 
 | ||
|  | 	*size = buffer->bytestocopy; | ||
|  | 
 | ||
|  | 	if (wiinst->mmapped) | ||
|  | 		return; | ||
|  | 
 | ||
|  | 	if (*size > buffer->size) { | ||
|  | 		*size = buffer->size; | ||
|  | 		buffer->pos = buffer->hw_pos; | ||
|  | 		buffer->bytestocopy = buffer->size; | ||
|  | 		DPF(1, "buffer overrun\n"); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | static void copy_block(u8 __user *dst, u8 * src, u32 str, u32 len, u8 cov) | ||
|  | { | ||
|  | 	if (cov == 1) | ||
|  | 		__copy_to_user(dst, src + str, len); | ||
|  | 	else { | ||
|  | 		u8 byte; | ||
|  | 		u32 i; | ||
|  | 
 | ||
|  | 		src += 1 + 2 * str; | ||
|  | 
 | ||
|  | 		for (i = 0; i < len; i++) { | ||
|  | 			byte = src[2 * i] ^ 0x80; | ||
|  | 			__copy_to_user(dst + i, &byte, 1); | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | void emu10k1_wavein_xferdata(struct wiinst *wiinst, u8 __user *data, u32 * size) | ||
|  | { | ||
|  | 	struct wavein_buffer *buffer = &wiinst->buffer; | ||
|  | 	u32 sizetocopy, sizetocopy_now, start; | ||
|  | 	unsigned long flags; | ||
|  | 
 | ||
|  | 	sizetocopy = min_t(u32, buffer->size, *size); | ||
|  | 	*size = sizetocopy; | ||
|  | 
 | ||
|  | 	if (!sizetocopy) | ||
|  | 		return; | ||
|  | 
 | ||
|  | 	spin_lock_irqsave(&wiinst->lock, flags); | ||
|  | 	start = buffer->pos; | ||
|  | 	buffer->pos += sizetocopy; | ||
|  | 	buffer->pos %= buffer->size; | ||
|  | 	buffer->bytestocopy -= sizetocopy; | ||
|  | 	sizetocopy_now = buffer->size - start; | ||
|  | 
 | ||
|  | 	spin_unlock_irqrestore(&wiinst->lock, flags); | ||
|  | 
 | ||
|  | 	if (sizetocopy > sizetocopy_now) { | ||
|  | 		sizetocopy -= sizetocopy_now; | ||
|  | 
 | ||
|  | 		copy_block(data, buffer->addr, start, sizetocopy_now, buffer->cov); | ||
|  | 		copy_block(data + sizetocopy_now, buffer->addr, 0, sizetocopy, buffer->cov); | ||
|  | 	} else { | ||
|  | 		copy_block(data, buffer->addr, start, sizetocopy, buffer->cov); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | void emu10k1_wavein_update(struct emu10k1_card *card, struct wiinst *wiinst) | ||
|  | { | ||
|  | 	u32 hw_pos; | ||
|  | 	u32 diff; | ||
|  | 
 | ||
|  | 	/* There is no actual start yet */ | ||
|  | 	if (!(wiinst->state & WAVE_STATE_STARTED)) { | ||
|  | 		hw_pos = wiinst->buffer.hw_pos; | ||
|  | 	} else { | ||
|  | 		/* hw_pos in byte units */ | ||
|  | 		hw_pos = sblive_readptr(card, wiinst->buffer.idxreg, 0) / wiinst->buffer.cov; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	diff = (wiinst->buffer.size + hw_pos - wiinst->buffer.hw_pos) % wiinst->buffer.size; | ||
|  | 	wiinst->total_recorded += diff; | ||
|  | 	wiinst->buffer.bytestocopy += diff; | ||
|  | 
 | ||
|  | 	wiinst->buffer.hw_pos = hw_pos; | ||
|  | } |