📄 sb16_main.c
字号:
/* * Copyright (c) by Jaroslav Kysela <perex@suse.cz> * Routines for control of 16-bit SoundBlaster cards and clones * Note: This is very ugly hardware which uses one 8-bit DMA channel and * second 16-bit DMA channel. Unfortunately 8-bit DMA channel can't * transfer 16-bit samples and 16-bit DMA channels can't transfer * 8-bit samples. This make full duplex more complicated than * can be... People, don't buy these soundcards for full 16-bit * duplex!!! * Note: 16-bit wide is assigned to first direction which made request. * With full duplex - playback is preferred with abstract layer. * * Note: Some chip revisions have hardware bug. Changing capture * channel from full-duplex 8bit DMA to 16bit DMA will block * 16bit DMA transfers from DSP chip (capture) until 8bit transfer * to DSP chip (playback) starts. This bug can be avoided with * "16bit DMA Allocation" setting set to Playback or Capture. * * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#include <sound/driver.h>#include <asm/io.h>#include <asm/dma.h>#include <linux/init.h>#include <linux/time.h>#include <sound/core.h>#include <sound/sb.h>#include <sound/sb16_csp.h>#include <sound/mpu401.h>#include <sound/control.h>#include <sound/info.h>MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");MODULE_DESCRIPTION("Routines for control of 16-bit SoundBlaster cards and clones");MODULE_LICENSE("GPL");#ifdef CONFIG_SND_SB16_CSPstatic void snd_sb16_csp_playback_prepare(sb_t *chip, snd_pcm_runtime_t *runtime){ if (chip->hardware == SB_HW_16CSP) { snd_sb_csp_t *csp = chip->csp; if (csp->running & SNDRV_SB_CSP_ST_LOADED) { /* manually loaded codec */ if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) && ((1U << runtime->format) == csp->acc_format)) { /* Supported runtime PCM format for playback */ if (csp->ops.csp_use(csp) == 0) { /* If CSP was successfully acquired */ goto __start_CSP; } } else if ((csp->mode & SNDRV_SB_CSP_MODE_QSOUND) && (csp->q_enabled)) { /* QSound decoder is loaded and enabled */ if ((1 << runtime->format) & (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE)) { /* Only for simple PCM formats */ if (csp->ops.csp_use(csp) == 0) { /* If CSP was successfully acquired */ goto __start_CSP; } } } } else if (csp->ops.csp_use(csp) == 0) { /* Acquire CSP and try to autoload hardware codec */ if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_WRITE)) { /* Unsupported format, release CSP */ csp->ops.csp_unuse(csp); } else { __start_CSP: /* Try to start CSP */ if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_PLAYBACK_16) ? SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT, (runtime->channels > 1) ? SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) { /* Failed, release CSP */ csp->ops.csp_unuse(csp); } else { /* Success, CSP acquired and running */ chip->open = SNDRV_SB_CSP_MODE_DSP_WRITE; } } } }}static void snd_sb16_csp_capture_prepare(sb_t *chip, snd_pcm_runtime_t *runtime){ if (chip->hardware == SB_HW_16CSP) { snd_sb_csp_t *csp = chip->csp; if (csp->running & SNDRV_SB_CSP_ST_LOADED) { /* manually loaded codec */ if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) && ((1U << runtime->format) == csp->acc_format)) { /* Supported runtime PCM format for capture */ if (csp->ops.csp_use(csp) == 0) { /* If CSP was successfully acquired */ goto __start_CSP; } } } else if (csp->ops.csp_use(csp) == 0) { /* Acquire CSP and try to autoload hardware codec */ if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_READ)) { /* Unsupported format, release CSP */ csp->ops.csp_unuse(csp); } else { __start_CSP: /* Try to start CSP */ if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_CAPTURE_16) ? SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT, (runtime->channels > 1) ? SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) { /* Failed, release CSP */ csp->ops.csp_unuse(csp); } else { /* Success, CSP acquired and running */ chip->open = SNDRV_SB_CSP_MODE_DSP_READ; } } } }}static void snd_sb16_csp_update(sb_t *chip){ if (chip->hardware == SB_HW_16CSP) { snd_sb_csp_t *csp = chip->csp; if (csp->qpos_changed) { spin_lock(&chip->reg_lock); csp->ops.csp_qsound_transfer (csp); spin_unlock(&chip->reg_lock); } }}static void snd_sb16_csp_playback_open(sb_t *chip, snd_pcm_runtime_t *runtime){ /* CSP decoders (QSound excluded) support only 16bit transfers */ if (chip->hardware == SB_HW_16CSP) { snd_sb_csp_t *csp = chip->csp; if (csp->running & SNDRV_SB_CSP_ST_LOADED) { /* manually loaded codec */ if (csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) { runtime->hw.formats |= csp->acc_format; } } else { /* autoloaded codecs */ runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM; } }}static void snd_sb16_csp_playback_close(sb_t *chip){ if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_WRITE)) { snd_sb_csp_t *csp = chip->csp; if (csp->ops.csp_stop(csp) == 0) { csp->ops.csp_unuse(csp); chip->open = 0; } }}static void snd_sb16_csp_capture_open(sb_t *chip, snd_pcm_runtime_t *runtime){ /* CSP coders support only 16bit transfers */ if (chip->hardware == SB_HW_16CSP) { snd_sb_csp_t *csp = chip->csp; if (csp->running & SNDRV_SB_CSP_ST_LOADED) { /* manually loaded codec */ if (csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) { runtime->hw.formats |= csp->acc_format; } } else { /* autoloaded codecs */ runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM; } }}static void snd_sb16_csp_capture_close(sb_t *chip){ if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_READ)) { snd_sb_csp_t *csp = chip->csp; if (csp->ops.csp_stop(csp) == 0) { csp->ops.csp_unuse(csp); chip->open = 0; } }}#else#define snd_sb16_csp_playback_prepare(chip, runtime) /*nop*/#define snd_sb16_csp_capture_prepare(chip, runtime) /*nop*/#define snd_sb16_csp_update(chip) /*nop*/#define snd_sb16_csp_playback_open(chip, runtime) /*nop*/#define snd_sb16_csp_playback_close(chip) /*nop*/#define snd_sb16_csp_capture_open(chip, runtime) /*nop*/#define snd_sb16_csp_capture_close(chip) /*nop*/#endifstatic void snd_sb16_setup_rate(sb_t *chip, unsigned short rate, int channel){ unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); if (chip->mode & (channel == SNDRV_PCM_STREAM_PLAYBACK ? SB_MODE_PLAYBACK_16 : SB_MODE_CAPTURE_16)) snd_sb_ack_16bit(chip); else snd_sb_ack_8bit(chip); if (!(chip->mode & SB_RATE_LOCK)) { chip->locked_rate = rate; snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_IN); snd_sbdsp_command(chip, rate >> 8); snd_sbdsp_command(chip, rate & 0xff); snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_OUT); snd_sbdsp_command(chip, rate >> 8); snd_sbdsp_command(chip, rate & 0xff); } spin_unlock_irqrestore(&chip->reg_lock, flags);}static int snd_sb16_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * hw_params){ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));}static int snd_sb16_hw_free(snd_pcm_substream_t * substream){ snd_pcm_lib_free_pages(substream); return 0;}static int snd_sb16_playback_prepare(snd_pcm_substream_t * substream){ unsigned long flags; sb_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; unsigned char format; unsigned int size, count, dma; snd_sb16_csp_playback_prepare(chip, runtime); if (snd_pcm_format_unsigned(runtime->format) > 0) { format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO; } else { format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO; } snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_PLAYBACK); size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream); dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16; snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); count = snd_pcm_lib_period_bytes(substream); spin_lock_irqsave(&chip->reg_lock, flags); if (chip->mode & SB_MODE_PLAYBACK_16) { count >>= 1; count--; snd_sbdsp_command(chip, SB_DSP4_OUT16_AI); snd_sbdsp_command(chip, format); snd_sbdsp_command(chip, count & 0xff); snd_sbdsp_command(chip, count >> 8); snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); } else { count--; snd_sbdsp_command(chip, SB_DSP4_OUT8_AI); snd_sbdsp_command(chip, format); snd_sbdsp_command(chip, count & 0xff); snd_sbdsp_command(chip, count >> 8); snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); } spin_unlock_irqrestore(&chip->reg_lock, flags); return 0;}static int snd_sb16_playback_trigger(snd_pcm_substream_t * substream, int cmd){ sb_t *chip = snd_pcm_substream_chip(substream); int result = 0; spin_lock(&chip->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: chip->mode |= SB_RATE_LOCK_PLAYBACK; snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); break; case SNDRV_PCM_TRIGGER_STOP: snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF); /* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */ if (chip->mode & SB_RATE_LOCK_CAPTURE) snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); chip->mode &= ~SB_RATE_LOCK_PLAYBACK; break; default: result = -EINVAL; } spin_unlock(&chip->reg_lock); return result;}static int snd_sb16_capture_prepare(snd_pcm_substream_t * substream){ unsigned long flags; sb_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; unsigned char format; unsigned int size, count, dma; snd_sb16_csp_capture_prepare(chip, runtime); if (snd_pcm_format_unsigned(runtime->format) > 0) { format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO; } else { format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO; } snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_CAPTURE); size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream); dma = (chip->mode & SB_MODE_CAPTURE_8) ? chip->dma8 : chip->dma16; snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); count = snd_pcm_lib_period_bytes(substream); spin_lock_irqsave(&chip->reg_lock, flags); if (chip->mode & SB_MODE_CAPTURE_16) { count >>= 1; count--; snd_sbdsp_command(chip, SB_DSP4_IN16_AI); snd_sbdsp_command(chip, format); snd_sbdsp_command(chip, count & 0xff); snd_sbdsp_command(chip, count >> 8); snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); } else { count--; snd_sbdsp_command(chip, SB_DSP4_IN8_AI); snd_sbdsp_command(chip, format); snd_sbdsp_command(chip, count & 0xff); snd_sbdsp_command(chip, count >> 8); snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); } spin_unlock_irqrestore(&chip->reg_lock, flags); return 0;}static int snd_sb16_capture_trigger(snd_pcm_substream_t * substream, int cmd){ sb_t *chip = snd_pcm_substream_chip(substream); int result = 0; spin_lock(&chip->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: chip->mode |= SB_RATE_LOCK_CAPTURE; snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); break; case SNDRV_PCM_TRIGGER_STOP: snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF); /* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */ if (chip->mode & SB_RATE_LOCK_PLAYBACK) snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); chip->mode &= ~SB_RATE_LOCK_CAPTURE; break; default: result = -EINVAL; } spin_unlock(&chip->reg_lock); return result;}irqreturn_t snd_sb16dsp_interrupt(int irq, void *dev_id, struct pt_regs *regs){ sb_t *chip = dev_id; unsigned char status; int ok; spin_lock(&chip->mixer_lock); status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS); spin_unlock(&chip->mixer_lock); if ((status & SB_IRQTYPE_MPUIN) && chip->rmidi_callback) chip->rmidi_callback(irq, chip->rmidi->private_data, regs); if (status & SB_IRQTYPE_8BIT) { ok = 0; if (chip->mode & SB_MODE_PLAYBACK_8) { snd_pcm_period_elapsed(chip->playback_substream); snd_sb16_csp_update(chip); ok++; } if (chip->mode & SB_MODE_CAPTURE_8) { snd_pcm_period_elapsed(chip->capture_substream); ok++; } spin_lock(&chip->reg_lock); if (!ok) snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); snd_sb_ack_8bit(chip); spin_unlock(&chip->reg_lock); } if (status & SB_IRQTYPE_16BIT) { ok = 0; if (chip->mode & SB_MODE_PLAYBACK_16) { snd_pcm_period_elapsed(chip->playback_substream); snd_sb16_csp_update(chip); ok++; } if (chip->mode & SB_MODE_CAPTURE_16) { snd_pcm_period_elapsed(chip->capture_substream); ok++; } spin_lock(&chip->reg_lock); if (!ok) snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); snd_sb_ack_16bit(chip); spin_unlock(&chip->reg_lock); } return IRQ_HANDLED;}/* */static snd_pcm_uframes_t snd_sb16_playback_pointer(snd_pcm_substream_t * substream){ sb_t *chip = snd_pcm_substream_chip(substream); unsigned int dma; size_t ptr; dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16; ptr = snd_dma_pointer(dma, chip->p_dma_size); return bytes_to_frames(substream->runtime, ptr);}static snd_pcm_uframes_t snd_sb16_capture_pointer(snd_pcm_substream_t * substream){ sb_t *chip = snd_pcm_substream_chip(substream);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -