162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 462306a36Sopenharmony_ci * Routines for control of 16-bit SoundBlaster cards and clones 562306a36Sopenharmony_ci * Note: This is very ugly hardware which uses one 8-bit DMA channel and 662306a36Sopenharmony_ci * second 16-bit DMA channel. Unfortunately 8-bit DMA channel can't 762306a36Sopenharmony_ci * transfer 16-bit samples and 16-bit DMA channels can't transfer 862306a36Sopenharmony_ci * 8-bit samples. This make full duplex more complicated than 962306a36Sopenharmony_ci * can be... People, don't buy these soundcards for full 16-bit 1062306a36Sopenharmony_ci * duplex!!! 1162306a36Sopenharmony_ci * Note: 16-bit wide is assigned to first direction which made request. 1262306a36Sopenharmony_ci * With full duplex - playback is preferred with abstract layer. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Note: Some chip revisions have hardware bug. Changing capture 1562306a36Sopenharmony_ci * channel from full-duplex 8bit DMA to 16bit DMA will block 1662306a36Sopenharmony_ci * 16bit DMA transfers from DSP chip (capture) until 8bit transfer 1762306a36Sopenharmony_ci * to DSP chip (playback) starts. This bug can be avoided with 1862306a36Sopenharmony_ci * "16bit DMA Allocation" setting set to Playback or Capture. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/io.h> 2262306a36Sopenharmony_ci#include <asm/dma.h> 2362306a36Sopenharmony_ci#include <linux/init.h> 2462306a36Sopenharmony_ci#include <linux/time.h> 2562306a36Sopenharmony_ci#include <linux/module.h> 2662306a36Sopenharmony_ci#include <sound/core.h> 2762306a36Sopenharmony_ci#include <sound/sb.h> 2862306a36Sopenharmony_ci#include <sound/sb16_csp.h> 2962306a36Sopenharmony_ci#include <sound/mpu401.h> 3062306a36Sopenharmony_ci#include <sound/control.h> 3162306a36Sopenharmony_ci#include <sound/info.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 3462306a36Sopenharmony_ciMODULE_DESCRIPTION("Routines for control of 16-bit SoundBlaster cards and clones"); 3562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define runtime_format_bits(runtime) \ 3862306a36Sopenharmony_ci ((unsigned int)pcm_format_to_bits((runtime)->format)) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#ifdef CONFIG_SND_SB16_CSP 4162306a36Sopenharmony_cistatic void snd_sb16_csp_playback_prepare(struct snd_sb *chip, struct snd_pcm_runtime *runtime) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci if (chip->hardware == SB_HW_16CSP) { 4462306a36Sopenharmony_ci struct snd_sb_csp *csp = chip->csp; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (csp->running & SNDRV_SB_CSP_ST_LOADED) { 4762306a36Sopenharmony_ci /* manually loaded codec */ 4862306a36Sopenharmony_ci if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) && 4962306a36Sopenharmony_ci (runtime_format_bits(runtime) == csp->acc_format)) { 5062306a36Sopenharmony_ci /* Supported runtime PCM format for playback */ 5162306a36Sopenharmony_ci if (csp->ops.csp_use(csp) == 0) { 5262306a36Sopenharmony_ci /* If CSP was successfully acquired */ 5362306a36Sopenharmony_ci goto __start_CSP; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci } else if ((csp->mode & SNDRV_SB_CSP_MODE_QSOUND) && (csp->q_enabled)) { 5662306a36Sopenharmony_ci /* QSound decoder is loaded and enabled */ 5762306a36Sopenharmony_ci if (runtime_format_bits(runtime) & (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | 5862306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE)) { 5962306a36Sopenharmony_ci /* Only for simple PCM formats */ 6062306a36Sopenharmony_ci if (csp->ops.csp_use(csp) == 0) { 6162306a36Sopenharmony_ci /* If CSP was successfully acquired */ 6262306a36Sopenharmony_ci goto __start_CSP; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci } else if (csp->ops.csp_use(csp) == 0) { 6762306a36Sopenharmony_ci /* Acquire CSP and try to autoload hardware codec */ 6862306a36Sopenharmony_ci if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_WRITE)) { 6962306a36Sopenharmony_ci /* Unsupported format, release CSP */ 7062306a36Sopenharmony_ci csp->ops.csp_unuse(csp); 7162306a36Sopenharmony_ci } else { 7262306a36Sopenharmony_ci __start_CSP: 7362306a36Sopenharmony_ci /* Try to start CSP */ 7462306a36Sopenharmony_ci if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_PLAYBACK_16) ? 7562306a36Sopenharmony_ci SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT, 7662306a36Sopenharmony_ci (runtime->channels > 1) ? 7762306a36Sopenharmony_ci SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) { 7862306a36Sopenharmony_ci /* Failed, release CSP */ 7962306a36Sopenharmony_ci csp->ops.csp_unuse(csp); 8062306a36Sopenharmony_ci } else { 8162306a36Sopenharmony_ci /* Success, CSP acquired and running */ 8262306a36Sopenharmony_ci chip->open = SNDRV_SB_CSP_MODE_DSP_WRITE; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void snd_sb16_csp_capture_prepare(struct snd_sb *chip, struct snd_pcm_runtime *runtime) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci if (chip->hardware == SB_HW_16CSP) { 9262306a36Sopenharmony_ci struct snd_sb_csp *csp = chip->csp; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (csp->running & SNDRV_SB_CSP_ST_LOADED) { 9562306a36Sopenharmony_ci /* manually loaded codec */ 9662306a36Sopenharmony_ci if ((csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) && 9762306a36Sopenharmony_ci (runtime_format_bits(runtime) == csp->acc_format)) { 9862306a36Sopenharmony_ci /* Supported runtime PCM format for capture */ 9962306a36Sopenharmony_ci if (csp->ops.csp_use(csp) == 0) { 10062306a36Sopenharmony_ci /* If CSP was successfully acquired */ 10162306a36Sopenharmony_ci goto __start_CSP; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci } else if (csp->ops.csp_use(csp) == 0) { 10562306a36Sopenharmony_ci /* Acquire CSP and try to autoload hardware codec */ 10662306a36Sopenharmony_ci if (csp->ops.csp_autoload(csp, runtime->format, SNDRV_SB_CSP_MODE_DSP_READ)) { 10762306a36Sopenharmony_ci /* Unsupported format, release CSP */ 10862306a36Sopenharmony_ci csp->ops.csp_unuse(csp); 10962306a36Sopenharmony_ci } else { 11062306a36Sopenharmony_ci __start_CSP: 11162306a36Sopenharmony_ci /* Try to start CSP */ 11262306a36Sopenharmony_ci if (csp->ops.csp_start(csp, (chip->mode & SB_MODE_CAPTURE_16) ? 11362306a36Sopenharmony_ci SNDRV_SB_CSP_SAMPLE_16BIT : SNDRV_SB_CSP_SAMPLE_8BIT, 11462306a36Sopenharmony_ci (runtime->channels > 1) ? 11562306a36Sopenharmony_ci SNDRV_SB_CSP_STEREO : SNDRV_SB_CSP_MONO)) { 11662306a36Sopenharmony_ci /* Failed, release CSP */ 11762306a36Sopenharmony_ci csp->ops.csp_unuse(csp); 11862306a36Sopenharmony_ci } else { 11962306a36Sopenharmony_ci /* Success, CSP acquired and running */ 12062306a36Sopenharmony_ci chip->open = SNDRV_SB_CSP_MODE_DSP_READ; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic void snd_sb16_csp_update(struct snd_sb *chip) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci if (chip->hardware == SB_HW_16CSP) { 13062306a36Sopenharmony_ci struct snd_sb_csp *csp = chip->csp; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (csp->qpos_changed) { 13362306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 13462306a36Sopenharmony_ci csp->ops.csp_qsound_transfer (csp); 13562306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void snd_sb16_csp_playback_open(struct snd_sb *chip, struct snd_pcm_runtime *runtime) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci /* CSP decoders (QSound excluded) support only 16bit transfers */ 14362306a36Sopenharmony_ci if (chip->hardware == SB_HW_16CSP) { 14462306a36Sopenharmony_ci struct snd_sb_csp *csp = chip->csp; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (csp->running & SNDRV_SB_CSP_ST_LOADED) { 14762306a36Sopenharmony_ci /* manually loaded codec */ 14862306a36Sopenharmony_ci if (csp->mode & SNDRV_SB_CSP_MODE_DSP_WRITE) { 14962306a36Sopenharmony_ci runtime->hw.formats |= csp->acc_format; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci } else { 15262306a36Sopenharmony_ci /* autoloaded codecs */ 15362306a36Sopenharmony_ci runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | 15462306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_IMA_ADPCM; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic void snd_sb16_csp_playback_close(struct snd_sb *chip) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_WRITE)) { 16262306a36Sopenharmony_ci struct snd_sb_csp *csp = chip->csp; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (csp->ops.csp_stop(csp) == 0) { 16562306a36Sopenharmony_ci csp->ops.csp_unuse(csp); 16662306a36Sopenharmony_ci chip->open = 0; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void snd_sb16_csp_capture_open(struct snd_sb *chip, struct snd_pcm_runtime *runtime) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci /* CSP coders support only 16bit transfers */ 17462306a36Sopenharmony_ci if (chip->hardware == SB_HW_16CSP) { 17562306a36Sopenharmony_ci struct snd_sb_csp *csp = chip->csp; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (csp->running & SNDRV_SB_CSP_ST_LOADED) { 17862306a36Sopenharmony_ci /* manually loaded codec */ 17962306a36Sopenharmony_ci if (csp->mode & SNDRV_SB_CSP_MODE_DSP_READ) { 18062306a36Sopenharmony_ci runtime->hw.formats |= csp->acc_format; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci } else { 18362306a36Sopenharmony_ci /* autoloaded codecs */ 18462306a36Sopenharmony_ci runtime->hw.formats |= SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | 18562306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_IMA_ADPCM; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic void snd_sb16_csp_capture_close(struct snd_sb *chip) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci if ((chip->hardware == SB_HW_16CSP) && (chip->open == SNDRV_SB_CSP_MODE_DSP_READ)) { 19362306a36Sopenharmony_ci struct snd_sb_csp *csp = chip->csp; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (csp->ops.csp_stop(csp) == 0) { 19662306a36Sopenharmony_ci csp->ops.csp_unuse(csp); 19762306a36Sopenharmony_ci chip->open = 0; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci#else 20262306a36Sopenharmony_ci#define snd_sb16_csp_playback_prepare(chip, runtime) /*nop*/ 20362306a36Sopenharmony_ci#define snd_sb16_csp_capture_prepare(chip, runtime) /*nop*/ 20462306a36Sopenharmony_ci#define snd_sb16_csp_update(chip) /*nop*/ 20562306a36Sopenharmony_ci#define snd_sb16_csp_playback_open(chip, runtime) /*nop*/ 20662306a36Sopenharmony_ci#define snd_sb16_csp_playback_close(chip) /*nop*/ 20762306a36Sopenharmony_ci#define snd_sb16_csp_capture_open(chip, runtime) /*nop*/ 20862306a36Sopenharmony_ci#define snd_sb16_csp_capture_close(chip) /*nop*/ 20962306a36Sopenharmony_ci#endif 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic void snd_sb16_setup_rate(struct snd_sb *chip, 21362306a36Sopenharmony_ci unsigned short rate, 21462306a36Sopenharmony_ci int channel) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci unsigned long flags; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 21962306a36Sopenharmony_ci if (chip->mode & (channel == SNDRV_PCM_STREAM_PLAYBACK ? SB_MODE_PLAYBACK_16 : SB_MODE_CAPTURE_16)) 22062306a36Sopenharmony_ci snd_sb_ack_16bit(chip); 22162306a36Sopenharmony_ci else 22262306a36Sopenharmony_ci snd_sb_ack_8bit(chip); 22362306a36Sopenharmony_ci if (!(chip->mode & SB_RATE_LOCK)) { 22462306a36Sopenharmony_ci chip->locked_rate = rate; 22562306a36Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_IN); 22662306a36Sopenharmony_ci snd_sbdsp_command(chip, rate >> 8); 22762306a36Sopenharmony_ci snd_sbdsp_command(chip, rate & 0xff); 22862306a36Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_OUT); 22962306a36Sopenharmony_ci snd_sbdsp_command(chip, rate >> 8); 23062306a36Sopenharmony_ci snd_sbdsp_command(chip, rate & 0xff); 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic int snd_sb16_playback_prepare(struct snd_pcm_substream *substream) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci unsigned long flags; 23862306a36Sopenharmony_ci struct snd_sb *chip = snd_pcm_substream_chip(substream); 23962306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 24062306a36Sopenharmony_ci unsigned char format; 24162306a36Sopenharmony_ci unsigned int size, count, dma; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci snd_sb16_csp_playback_prepare(chip, runtime); 24462306a36Sopenharmony_ci if (snd_pcm_format_unsigned(runtime->format) > 0) { 24562306a36Sopenharmony_ci format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO; 24662306a36Sopenharmony_ci } else { 24762306a36Sopenharmony_ci format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_PLAYBACK); 25162306a36Sopenharmony_ci size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream); 25262306a36Sopenharmony_ci dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16; 25362306a36Sopenharmony_ci snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci count = snd_pcm_lib_period_bytes(substream); 25662306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 25762306a36Sopenharmony_ci if (chip->mode & SB_MODE_PLAYBACK_16) { 25862306a36Sopenharmony_ci count >>= 1; 25962306a36Sopenharmony_ci count--; 26062306a36Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP4_OUT16_AI); 26162306a36Sopenharmony_ci snd_sbdsp_command(chip, format); 26262306a36Sopenharmony_ci snd_sbdsp_command(chip, count & 0xff); 26362306a36Sopenharmony_ci snd_sbdsp_command(chip, count >> 8); 26462306a36Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); 26562306a36Sopenharmony_ci } else { 26662306a36Sopenharmony_ci count--; 26762306a36Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP4_OUT8_AI); 26862306a36Sopenharmony_ci snd_sbdsp_command(chip, format); 26962306a36Sopenharmony_ci snd_sbdsp_command(chip, count & 0xff); 27062306a36Sopenharmony_ci snd_sbdsp_command(chip, count >> 8); 27162306a36Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 27462306a36Sopenharmony_ci return 0; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic int snd_sb16_playback_trigger(struct snd_pcm_substream *substream, 27862306a36Sopenharmony_ci int cmd) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct snd_sb *chip = snd_pcm_substream_chip(substream); 28162306a36Sopenharmony_ci int result = 0; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 28462306a36Sopenharmony_ci switch (cmd) { 28562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 28662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 28762306a36Sopenharmony_ci chip->mode |= SB_RATE_LOCK_PLAYBACK; 28862306a36Sopenharmony_ci snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 29162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 29262306a36Sopenharmony_ci snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF); 29362306a36Sopenharmony_ci /* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */ 29462306a36Sopenharmony_ci if (chip->mode & SB_RATE_LOCK_CAPTURE) 29562306a36Sopenharmony_ci snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); 29662306a36Sopenharmony_ci chip->mode &= ~SB_RATE_LOCK_PLAYBACK; 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci default: 29962306a36Sopenharmony_ci result = -EINVAL; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 30262306a36Sopenharmony_ci return result; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int snd_sb16_capture_prepare(struct snd_pcm_substream *substream) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci unsigned long flags; 30862306a36Sopenharmony_ci struct snd_sb *chip = snd_pcm_substream_chip(substream); 30962306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 31062306a36Sopenharmony_ci unsigned char format; 31162306a36Sopenharmony_ci unsigned int size, count, dma; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci snd_sb16_csp_capture_prepare(chip, runtime); 31462306a36Sopenharmony_ci if (snd_pcm_format_unsigned(runtime->format) > 0) { 31562306a36Sopenharmony_ci format = runtime->channels > 1 ? SB_DSP4_MODE_UNS_STEREO : SB_DSP4_MODE_UNS_MONO; 31662306a36Sopenharmony_ci } else { 31762306a36Sopenharmony_ci format = runtime->channels > 1 ? SB_DSP4_MODE_SIGN_STEREO : SB_DSP4_MODE_SIGN_MONO; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci snd_sb16_setup_rate(chip, runtime->rate, SNDRV_PCM_STREAM_CAPTURE); 32062306a36Sopenharmony_ci size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream); 32162306a36Sopenharmony_ci dma = (chip->mode & SB_MODE_CAPTURE_8) ? chip->dma8 : chip->dma16; 32262306a36Sopenharmony_ci snd_dma_program(dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci count = snd_pcm_lib_period_bytes(substream); 32562306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 32662306a36Sopenharmony_ci if (chip->mode & SB_MODE_CAPTURE_16) { 32762306a36Sopenharmony_ci count >>= 1; 32862306a36Sopenharmony_ci count--; 32962306a36Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP4_IN16_AI); 33062306a36Sopenharmony_ci snd_sbdsp_command(chip, format); 33162306a36Sopenharmony_ci snd_sbdsp_command(chip, count & 0xff); 33262306a36Sopenharmony_ci snd_sbdsp_command(chip, count >> 8); 33362306a36Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); 33462306a36Sopenharmony_ci } else { 33562306a36Sopenharmony_ci count--; 33662306a36Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP4_IN8_AI); 33762306a36Sopenharmony_ci snd_sbdsp_command(chip, format); 33862306a36Sopenharmony_ci snd_sbdsp_command(chip, count & 0xff); 33962306a36Sopenharmony_ci snd_sbdsp_command(chip, count >> 8); 34062306a36Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 34362306a36Sopenharmony_ci return 0; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic int snd_sb16_capture_trigger(struct snd_pcm_substream *substream, 34762306a36Sopenharmony_ci int cmd) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct snd_sb *chip = snd_pcm_substream_chip(substream); 35062306a36Sopenharmony_ci int result = 0; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 35362306a36Sopenharmony_ci switch (cmd) { 35462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 35562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 35662306a36Sopenharmony_ci chip->mode |= SB_RATE_LOCK_CAPTURE; 35762306a36Sopenharmony_ci snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); 35862306a36Sopenharmony_ci break; 35962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 36062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 36162306a36Sopenharmony_ci snd_sbdsp_command(chip, chip->mode & SB_MODE_CAPTURE_16 ? SB_DSP_DMA16_OFF : SB_DSP_DMA8_OFF); 36262306a36Sopenharmony_ci /* next two lines are needed for some types of DSP4 (SB AWE 32 - 4.13) */ 36362306a36Sopenharmony_ci if (chip->mode & SB_RATE_LOCK_PLAYBACK) 36462306a36Sopenharmony_ci snd_sbdsp_command(chip, chip->mode & SB_MODE_PLAYBACK_16 ? SB_DSP_DMA16_ON : SB_DSP_DMA8_ON); 36562306a36Sopenharmony_ci chip->mode &= ~SB_RATE_LOCK_CAPTURE; 36662306a36Sopenharmony_ci break; 36762306a36Sopenharmony_ci default: 36862306a36Sopenharmony_ci result = -EINVAL; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 37162306a36Sopenharmony_ci return result; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ciirqreturn_t snd_sb16dsp_interrupt(int irq, void *dev_id) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct snd_sb *chip = dev_id; 37762306a36Sopenharmony_ci unsigned char status; 37862306a36Sopenharmony_ci int ok; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci spin_lock(&chip->mixer_lock); 38162306a36Sopenharmony_ci status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS); 38262306a36Sopenharmony_ci spin_unlock(&chip->mixer_lock); 38362306a36Sopenharmony_ci if ((status & SB_IRQTYPE_MPUIN) && chip->rmidi_callback) 38462306a36Sopenharmony_ci chip->rmidi_callback(irq, chip->rmidi->private_data); 38562306a36Sopenharmony_ci if (status & SB_IRQTYPE_8BIT) { 38662306a36Sopenharmony_ci ok = 0; 38762306a36Sopenharmony_ci if (chip->mode & SB_MODE_PLAYBACK_8) { 38862306a36Sopenharmony_ci snd_pcm_period_elapsed(chip->playback_substream); 38962306a36Sopenharmony_ci snd_sb16_csp_update(chip); 39062306a36Sopenharmony_ci ok++; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci if (chip->mode & SB_MODE_CAPTURE_8) { 39362306a36Sopenharmony_ci snd_pcm_period_elapsed(chip->capture_substream); 39462306a36Sopenharmony_ci ok++; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 39762306a36Sopenharmony_ci if (!ok) 39862306a36Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); 39962306a36Sopenharmony_ci snd_sb_ack_8bit(chip); 40062306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci if (status & SB_IRQTYPE_16BIT) { 40362306a36Sopenharmony_ci ok = 0; 40462306a36Sopenharmony_ci if (chip->mode & SB_MODE_PLAYBACK_16) { 40562306a36Sopenharmony_ci snd_pcm_period_elapsed(chip->playback_substream); 40662306a36Sopenharmony_ci snd_sb16_csp_update(chip); 40762306a36Sopenharmony_ci ok++; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci if (chip->mode & SB_MODE_CAPTURE_16) { 41062306a36Sopenharmony_ci snd_pcm_period_elapsed(chip->capture_substream); 41162306a36Sopenharmony_ci ok++; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 41462306a36Sopenharmony_ci if (!ok) 41562306a36Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_DMA16_OFF); 41662306a36Sopenharmony_ci snd_sb_ack_16bit(chip); 41762306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci return IRQ_HANDLED; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci/* 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_sb16_playback_pointer(struct snd_pcm_substream *substream) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct snd_sb *chip = snd_pcm_substream_chip(substream); 42962306a36Sopenharmony_ci unsigned int dma; 43062306a36Sopenharmony_ci size_t ptr; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci dma = (chip->mode & SB_MODE_PLAYBACK_8) ? chip->dma8 : chip->dma16; 43362306a36Sopenharmony_ci ptr = snd_dma_pointer(dma, chip->p_dma_size); 43462306a36Sopenharmony_ci return bytes_to_frames(substream->runtime, ptr); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_sb16_capture_pointer(struct snd_pcm_substream *substream) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct snd_sb *chip = snd_pcm_substream_chip(substream); 44062306a36Sopenharmony_ci unsigned int dma; 44162306a36Sopenharmony_ci size_t ptr; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci dma = (chip->mode & SB_MODE_CAPTURE_8) ? chip->dma8 : chip->dma16; 44462306a36Sopenharmony_ci ptr = snd_dma_pointer(dma, chip->c_dma_size); 44562306a36Sopenharmony_ci return bytes_to_frames(substream->runtime, ptr); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci/* 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_sb16_playback = 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 45562306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 45662306a36Sopenharmony_ci .formats = 0, 45762306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, 45862306a36Sopenharmony_ci .rate_min = 4000, 45962306a36Sopenharmony_ci .rate_max = 44100, 46062306a36Sopenharmony_ci .channels_min = 1, 46162306a36Sopenharmony_ci .channels_max = 2, 46262306a36Sopenharmony_ci .buffer_bytes_max = (128*1024), 46362306a36Sopenharmony_ci .period_bytes_min = 64, 46462306a36Sopenharmony_ci .period_bytes_max = (128*1024), 46562306a36Sopenharmony_ci .periods_min = 1, 46662306a36Sopenharmony_ci .periods_max = 1024, 46762306a36Sopenharmony_ci .fifo_size = 0, 46862306a36Sopenharmony_ci}; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_sb16_capture = 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 47362306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 47462306a36Sopenharmony_ci .formats = 0, 47562306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100, 47662306a36Sopenharmony_ci .rate_min = 4000, 47762306a36Sopenharmony_ci .rate_max = 44100, 47862306a36Sopenharmony_ci .channels_min = 1, 47962306a36Sopenharmony_ci .channels_max = 2, 48062306a36Sopenharmony_ci .buffer_bytes_max = (128*1024), 48162306a36Sopenharmony_ci .period_bytes_min = 64, 48262306a36Sopenharmony_ci .period_bytes_max = (128*1024), 48362306a36Sopenharmony_ci .periods_min = 1, 48462306a36Sopenharmony_ci .periods_max = 1024, 48562306a36Sopenharmony_ci .fifo_size = 0, 48662306a36Sopenharmony_ci}; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci/* 48962306a36Sopenharmony_ci * open/close 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic int snd_sb16_playback_open(struct snd_pcm_substream *substream) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci unsigned long flags; 49562306a36Sopenharmony_ci struct snd_sb *chip = snd_pcm_substream_chip(substream); 49662306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 49962306a36Sopenharmony_ci if (chip->mode & SB_MODE_PLAYBACK) { 50062306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 50162306a36Sopenharmony_ci return -EAGAIN; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci runtime->hw = snd_sb16_playback; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* skip if 16 bit DMA was reserved for capture */ 50662306a36Sopenharmony_ci if (chip->force_mode16 & SB_MODE_CAPTURE_16) 50762306a36Sopenharmony_ci goto __skip_16bit; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (chip->dma16 >= 0 && !(chip->mode & SB_MODE_CAPTURE_16)) { 51062306a36Sopenharmony_ci chip->mode |= SB_MODE_PLAYBACK_16; 51162306a36Sopenharmony_ci runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; 51262306a36Sopenharmony_ci /* Vibra16X hack */ 51362306a36Sopenharmony_ci if (chip->dma16 <= 3) { 51462306a36Sopenharmony_ci runtime->hw.buffer_bytes_max = 51562306a36Sopenharmony_ci runtime->hw.period_bytes_max = 64 * 1024; 51662306a36Sopenharmony_ci } else { 51762306a36Sopenharmony_ci snd_sb16_csp_playback_open(chip, runtime); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci goto __open_ok; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci __skip_16bit: 52362306a36Sopenharmony_ci if (chip->dma8 >= 0 && !(chip->mode & SB_MODE_CAPTURE_8)) { 52462306a36Sopenharmony_ci chip->mode |= SB_MODE_PLAYBACK_8; 52562306a36Sopenharmony_ci /* DSP v 4.xx can transfer 16bit data through 8bit DMA channel, SBHWPG 2-7 */ 52662306a36Sopenharmony_ci if (chip->dma16 < 0) { 52762306a36Sopenharmony_ci runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; 52862306a36Sopenharmony_ci chip->mode |= SB_MODE_PLAYBACK_16; 52962306a36Sopenharmony_ci } else { 53062306a36Sopenharmony_ci runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci runtime->hw.buffer_bytes_max = 53362306a36Sopenharmony_ci runtime->hw.period_bytes_max = 64 * 1024; 53462306a36Sopenharmony_ci goto __open_ok; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 53762306a36Sopenharmony_ci return -EAGAIN; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci __open_ok: 54062306a36Sopenharmony_ci if (chip->hardware == SB_HW_ALS100) 54162306a36Sopenharmony_ci runtime->hw.rate_max = 48000; 54262306a36Sopenharmony_ci if (chip->hardware == SB_HW_CS5530) { 54362306a36Sopenharmony_ci runtime->hw.buffer_bytes_max = 32 * 1024; 54462306a36Sopenharmony_ci runtime->hw.periods_min = 2; 54562306a36Sopenharmony_ci runtime->hw.rate_min = 44100; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci if (chip->mode & SB_RATE_LOCK) 54862306a36Sopenharmony_ci runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; 54962306a36Sopenharmony_ci chip->playback_substream = substream; 55062306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 55162306a36Sopenharmony_ci return 0; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic int snd_sb16_playback_close(struct snd_pcm_substream *substream) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci unsigned long flags; 55762306a36Sopenharmony_ci struct snd_sb *chip = snd_pcm_substream_chip(substream); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci snd_sb16_csp_playback_close(chip); 56062306a36Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 56162306a36Sopenharmony_ci chip->playback_substream = NULL; 56262306a36Sopenharmony_ci chip->mode &= ~SB_MODE_PLAYBACK; 56362306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 56462306a36Sopenharmony_ci return 0; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic int snd_sb16_capture_open(struct snd_pcm_substream *substream) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci unsigned long flags; 57062306a36Sopenharmony_ci struct snd_sb *chip = snd_pcm_substream_chip(substream); 57162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 57462306a36Sopenharmony_ci if (chip->mode & SB_MODE_CAPTURE) { 57562306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 57662306a36Sopenharmony_ci return -EAGAIN; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci runtime->hw = snd_sb16_capture; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* skip if 16 bit DMA was reserved for playback */ 58162306a36Sopenharmony_ci if (chip->force_mode16 & SB_MODE_PLAYBACK_16) 58262306a36Sopenharmony_ci goto __skip_16bit; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (chip->dma16 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_16)) { 58562306a36Sopenharmony_ci chip->mode |= SB_MODE_CAPTURE_16; 58662306a36Sopenharmony_ci runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; 58762306a36Sopenharmony_ci /* Vibra16X hack */ 58862306a36Sopenharmony_ci if (chip->dma16 <= 3) { 58962306a36Sopenharmony_ci runtime->hw.buffer_bytes_max = 59062306a36Sopenharmony_ci runtime->hw.period_bytes_max = 64 * 1024; 59162306a36Sopenharmony_ci } else { 59262306a36Sopenharmony_ci snd_sb16_csp_capture_open(chip, runtime); 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci goto __open_ok; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci __skip_16bit: 59862306a36Sopenharmony_ci if (chip->dma8 >= 0 && !(chip->mode & SB_MODE_PLAYBACK_8)) { 59962306a36Sopenharmony_ci chip->mode |= SB_MODE_CAPTURE_8; 60062306a36Sopenharmony_ci /* DSP v 4.xx can transfer 16bit data through 8bit DMA channel, SBHWPG 2-7 */ 60162306a36Sopenharmony_ci if (chip->dma16 < 0) { 60262306a36Sopenharmony_ci runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE; 60362306a36Sopenharmony_ci chip->mode |= SB_MODE_CAPTURE_16; 60462306a36Sopenharmony_ci } else { 60562306a36Sopenharmony_ci runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci runtime->hw.buffer_bytes_max = 60862306a36Sopenharmony_ci runtime->hw.period_bytes_max = 64 * 1024; 60962306a36Sopenharmony_ci goto __open_ok; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 61262306a36Sopenharmony_ci return -EAGAIN; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci __open_ok: 61562306a36Sopenharmony_ci if (chip->hardware == SB_HW_ALS100) 61662306a36Sopenharmony_ci runtime->hw.rate_max = 48000; 61762306a36Sopenharmony_ci if (chip->hardware == SB_HW_CS5530) { 61862306a36Sopenharmony_ci runtime->hw.buffer_bytes_max = 32 * 1024; 61962306a36Sopenharmony_ci runtime->hw.periods_min = 2; 62062306a36Sopenharmony_ci runtime->hw.rate_min = 44100; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci if (chip->mode & SB_RATE_LOCK) 62362306a36Sopenharmony_ci runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate; 62462306a36Sopenharmony_ci chip->capture_substream = substream; 62562306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 62662306a36Sopenharmony_ci return 0; 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic int snd_sb16_capture_close(struct snd_pcm_substream *substream) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci unsigned long flags; 63262306a36Sopenharmony_ci struct snd_sb *chip = snd_pcm_substream_chip(substream); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci snd_sb16_csp_capture_close(chip); 63562306a36Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 63662306a36Sopenharmony_ci chip->capture_substream = NULL; 63762306a36Sopenharmony_ci chip->mode &= ~SB_MODE_CAPTURE; 63862306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 63962306a36Sopenharmony_ci return 0; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci/* 64362306a36Sopenharmony_ci * DMA control interface 64462306a36Sopenharmony_ci */ 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic int snd_sb16_set_dma_mode(struct snd_sb *chip, int what) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci if (chip->dma8 < 0 || chip->dma16 < 0) { 64962306a36Sopenharmony_ci if (snd_BUG_ON(what)) 65062306a36Sopenharmony_ci return -EINVAL; 65162306a36Sopenharmony_ci return 0; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci if (what == 0) { 65462306a36Sopenharmony_ci chip->force_mode16 = 0; 65562306a36Sopenharmony_ci } else if (what == 1) { 65662306a36Sopenharmony_ci chip->force_mode16 = SB_MODE_PLAYBACK_16; 65762306a36Sopenharmony_ci } else if (what == 2) { 65862306a36Sopenharmony_ci chip->force_mode16 = SB_MODE_CAPTURE_16; 65962306a36Sopenharmony_ci } else { 66062306a36Sopenharmony_ci return -EINVAL; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci return 0; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic int snd_sb16_get_dma_mode(struct snd_sb *chip) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci if (chip->dma8 < 0 || chip->dma16 < 0) 66862306a36Sopenharmony_ci return 0; 66962306a36Sopenharmony_ci switch (chip->force_mode16) { 67062306a36Sopenharmony_ci case SB_MODE_PLAYBACK_16: 67162306a36Sopenharmony_ci return 1; 67262306a36Sopenharmony_ci case SB_MODE_CAPTURE_16: 67362306a36Sopenharmony_ci return 2; 67462306a36Sopenharmony_ci default: 67562306a36Sopenharmony_ci return 0; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cistatic int snd_sb16_dma_control_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci static const char * const texts[3] = { 68262306a36Sopenharmony_ci "Auto", "Playback", "Capture" 68362306a36Sopenharmony_ci }; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 3, texts); 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic int snd_sb16_dma_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci struct snd_sb *chip = snd_kcontrol_chip(kcontrol); 69162306a36Sopenharmony_ci unsigned long flags; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 69462306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = snd_sb16_get_dma_mode(chip); 69562306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 69662306a36Sopenharmony_ci return 0; 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic int snd_sb16_dma_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci struct snd_sb *chip = snd_kcontrol_chip(kcontrol); 70262306a36Sopenharmony_ci unsigned long flags; 70362306a36Sopenharmony_ci unsigned char nval, oval; 70462306a36Sopenharmony_ci int change; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci nval = ucontrol->value.enumerated.item[0]; 70762306a36Sopenharmony_ci if (nval > 2) 70862306a36Sopenharmony_ci return -EINVAL; 70962306a36Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 71062306a36Sopenharmony_ci oval = snd_sb16_get_dma_mode(chip); 71162306a36Sopenharmony_ci change = nval != oval; 71262306a36Sopenharmony_ci snd_sb16_set_dma_mode(chip, nval); 71362306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 71462306a36Sopenharmony_ci return change; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_sb16_dma_control = { 71862306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_CARD, 71962306a36Sopenharmony_ci .name = "16-bit DMA Allocation", 72062306a36Sopenharmony_ci .info = snd_sb16_dma_control_info, 72162306a36Sopenharmony_ci .get = snd_sb16_dma_control_get, 72262306a36Sopenharmony_ci .put = snd_sb16_dma_control_put 72362306a36Sopenharmony_ci}; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci/* 72662306a36Sopenharmony_ci * Initialization part 72762306a36Sopenharmony_ci */ 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ciint snd_sb16dsp_configure(struct snd_sb * chip) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci unsigned long flags; 73262306a36Sopenharmony_ci unsigned char irqreg = 0, dmareg = 0, mpureg; 73362306a36Sopenharmony_ci unsigned char realirq, realdma, realmpureg; 73462306a36Sopenharmony_ci /* note: mpu register should be present only on SB16 Vibra soundcards */ 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci // printk(KERN_DEBUG "codec->irq=%i, codec->dma8=%i, codec->dma16=%i\n", chip->irq, chip->dma8, chip->dma16); 73762306a36Sopenharmony_ci spin_lock_irqsave(&chip->mixer_lock, flags); 73862306a36Sopenharmony_ci mpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP) & ~0x06; 73962306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->mixer_lock, flags); 74062306a36Sopenharmony_ci switch (chip->irq) { 74162306a36Sopenharmony_ci case 2: 74262306a36Sopenharmony_ci case 9: 74362306a36Sopenharmony_ci irqreg |= SB_IRQSETUP_IRQ9; 74462306a36Sopenharmony_ci break; 74562306a36Sopenharmony_ci case 5: 74662306a36Sopenharmony_ci irqreg |= SB_IRQSETUP_IRQ5; 74762306a36Sopenharmony_ci break; 74862306a36Sopenharmony_ci case 7: 74962306a36Sopenharmony_ci irqreg |= SB_IRQSETUP_IRQ7; 75062306a36Sopenharmony_ci break; 75162306a36Sopenharmony_ci case 10: 75262306a36Sopenharmony_ci irqreg |= SB_IRQSETUP_IRQ10; 75362306a36Sopenharmony_ci break; 75462306a36Sopenharmony_ci default: 75562306a36Sopenharmony_ci return -EINVAL; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci if (chip->dma8 >= 0) { 75862306a36Sopenharmony_ci switch (chip->dma8) { 75962306a36Sopenharmony_ci case 0: 76062306a36Sopenharmony_ci dmareg |= SB_DMASETUP_DMA0; 76162306a36Sopenharmony_ci break; 76262306a36Sopenharmony_ci case 1: 76362306a36Sopenharmony_ci dmareg |= SB_DMASETUP_DMA1; 76462306a36Sopenharmony_ci break; 76562306a36Sopenharmony_ci case 3: 76662306a36Sopenharmony_ci dmareg |= SB_DMASETUP_DMA3; 76762306a36Sopenharmony_ci break; 76862306a36Sopenharmony_ci default: 76962306a36Sopenharmony_ci return -EINVAL; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci if (chip->dma16 >= 0 && chip->dma16 != chip->dma8) { 77362306a36Sopenharmony_ci switch (chip->dma16) { 77462306a36Sopenharmony_ci case 5: 77562306a36Sopenharmony_ci dmareg |= SB_DMASETUP_DMA5; 77662306a36Sopenharmony_ci break; 77762306a36Sopenharmony_ci case 6: 77862306a36Sopenharmony_ci dmareg |= SB_DMASETUP_DMA6; 77962306a36Sopenharmony_ci break; 78062306a36Sopenharmony_ci case 7: 78162306a36Sopenharmony_ci dmareg |= SB_DMASETUP_DMA7; 78262306a36Sopenharmony_ci break; 78362306a36Sopenharmony_ci default: 78462306a36Sopenharmony_ci return -EINVAL; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci switch (chip->mpu_port) { 78862306a36Sopenharmony_ci case 0x300: 78962306a36Sopenharmony_ci mpureg |= 0x04; 79062306a36Sopenharmony_ci break; 79162306a36Sopenharmony_ci case 0x330: 79262306a36Sopenharmony_ci mpureg |= 0x00; 79362306a36Sopenharmony_ci break; 79462306a36Sopenharmony_ci default: 79562306a36Sopenharmony_ci mpureg |= 0x02; /* disable MPU */ 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci spin_lock_irqsave(&chip->mixer_lock, flags); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci snd_sbmixer_write(chip, SB_DSP4_IRQSETUP, irqreg); 80062306a36Sopenharmony_ci realirq = snd_sbmixer_read(chip, SB_DSP4_IRQSETUP); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci snd_sbmixer_write(chip, SB_DSP4_DMASETUP, dmareg); 80362306a36Sopenharmony_ci realdma = snd_sbmixer_read(chip, SB_DSP4_DMASETUP); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci snd_sbmixer_write(chip, SB_DSP4_MPUSETUP, mpureg); 80662306a36Sopenharmony_ci realmpureg = snd_sbmixer_read(chip, SB_DSP4_MPUSETUP); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->mixer_lock, flags); 80962306a36Sopenharmony_ci if ((~realirq) & irqreg || (~realdma) & dmareg) { 81062306a36Sopenharmony_ci snd_printk(KERN_ERR "SB16 [0x%lx]: unable to set DMA & IRQ (PnP device?)\n", chip->port); 81162306a36Sopenharmony_ci snd_printk(KERN_ERR "SB16 [0x%lx]: wanted: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, realirq, realdma, realmpureg); 81262306a36Sopenharmony_ci snd_printk(KERN_ERR "SB16 [0x%lx]: got: irqreg=0x%x, dmareg=0x%x, mpureg = 0x%x\n", chip->port, irqreg, dmareg, mpureg); 81362306a36Sopenharmony_ci return -ENODEV; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci return 0; 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_sb16_playback_ops = { 81962306a36Sopenharmony_ci .open = snd_sb16_playback_open, 82062306a36Sopenharmony_ci .close = snd_sb16_playback_close, 82162306a36Sopenharmony_ci .prepare = snd_sb16_playback_prepare, 82262306a36Sopenharmony_ci .trigger = snd_sb16_playback_trigger, 82362306a36Sopenharmony_ci .pointer = snd_sb16_playback_pointer, 82462306a36Sopenharmony_ci}; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_sb16_capture_ops = { 82762306a36Sopenharmony_ci .open = snd_sb16_capture_open, 82862306a36Sopenharmony_ci .close = snd_sb16_capture_close, 82962306a36Sopenharmony_ci .prepare = snd_sb16_capture_prepare, 83062306a36Sopenharmony_ci .trigger = snd_sb16_capture_trigger, 83162306a36Sopenharmony_ci .pointer = snd_sb16_capture_pointer, 83262306a36Sopenharmony_ci}; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ciint snd_sb16dsp_pcm(struct snd_sb *chip, int device) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci struct snd_card *card = chip->card; 83762306a36Sopenharmony_ci struct snd_pcm *pcm; 83862306a36Sopenharmony_ci int err; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci err = snd_pcm_new(card, "SB16 DSP", device, 1, 1, &pcm); 84162306a36Sopenharmony_ci if (err < 0) 84262306a36Sopenharmony_ci return err; 84362306a36Sopenharmony_ci sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff); 84462306a36Sopenharmony_ci pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; 84562306a36Sopenharmony_ci pcm->private_data = chip; 84662306a36Sopenharmony_ci chip->pcm = pcm; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb16_playback_ops); 84962306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb16_capture_ops); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (chip->dma16 >= 0 && chip->dma8 != chip->dma16) 85262306a36Sopenharmony_ci snd_ctl_add(card, snd_ctl_new1(&snd_sb16_dma_control, chip)); 85362306a36Sopenharmony_ci else 85462306a36Sopenharmony_ci pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 85762306a36Sopenharmony_ci card->dev, 64*1024, 128*1024); 85862306a36Sopenharmony_ci return 0; 85962306a36Sopenharmony_ci} 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ciconst struct snd_pcm_ops *snd_sb16dsp_get_pcm_ops(int direction) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci return direction == SNDRV_PCM_STREAM_PLAYBACK ? 86462306a36Sopenharmony_ci &snd_sb16_playback_ops : &snd_sb16_capture_ops; 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sb16dsp_pcm); 86862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sb16dsp_get_pcm_ops); 86962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sb16dsp_configure); 87062306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sb16dsp_interrupt); 871