Viewing file: cardwi.c (9.29 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/* ********************************************************************** * 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. */ 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; u32 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(); emu10k1_wavein_close(wave_dev); return -1; }
emu10k1_set_record_src(card, wiinst);
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->buffer.hw_pos = 0; wiinst->buffer.pos = 0; wiinst->buffer.bytestocopy = 0;
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; u32 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 *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 * 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; }
|