18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 48c2ecf20Sopenharmony_ci * Uros Bizjak <uros@kss-loka.si> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Routines for control of 8-bit SoundBlaster cards and clones 78c2ecf20Sopenharmony_ci * Please note: I don't have access to old SB8 soundcards. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * -- 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Thu Apr 29 20:36:17 BST 1999 George David Morrison <gdm@gedamo.demon.co.uk> 128c2ecf20Sopenharmony_ci * DSP can't respond to commands whilst in "high speed" mode. Caused 138c2ecf20Sopenharmony_ci * glitching during playback. Fixed. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Wed Jul 12 22:02:55 CEST 2000 Uros Bizjak <uros@kss-loka.si> 168c2ecf20Sopenharmony_ci * Cleaned up and rewrote lowlevel routines. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci#include <asm/dma.h> 218c2ecf20Sopenharmony_ci#include <linux/init.h> 228c2ecf20Sopenharmony_ci#include <linux/time.h> 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <sound/core.h> 258c2ecf20Sopenharmony_ci#include <sound/sb.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Uros Bizjak <uros@kss-loka.si>"); 288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Routines for control of 8-bit SoundBlaster cards and clones"); 298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define SB8_CLOCK 1000000 328c2ecf20Sopenharmony_ci#define SB8_DEN(v) ((SB8_CLOCK + (v) / 2) / (v)) 338c2ecf20Sopenharmony_ci#define SB8_RATE(v) (SB8_CLOCK / SB8_DEN(v)) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic const struct snd_ratnum clock = { 368c2ecf20Sopenharmony_ci .num = SB8_CLOCK, 378c2ecf20Sopenharmony_ci .den_min = 1, 388c2ecf20Sopenharmony_ci .den_max = 256, 398c2ecf20Sopenharmony_ci .den_step = 1, 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_ratnums hw_constraints_clock = { 438c2ecf20Sopenharmony_ci .nrats = 1, 448c2ecf20Sopenharmony_ci .rats = &clock, 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic const struct snd_ratnum stereo_clocks[] = { 488c2ecf20Sopenharmony_ci { 498c2ecf20Sopenharmony_ci .num = SB8_CLOCK, 508c2ecf20Sopenharmony_ci .den_min = SB8_DEN(22050), 518c2ecf20Sopenharmony_ci .den_max = SB8_DEN(22050), 528c2ecf20Sopenharmony_ci .den_step = 1, 538c2ecf20Sopenharmony_ci }, 548c2ecf20Sopenharmony_ci { 558c2ecf20Sopenharmony_ci .num = SB8_CLOCK, 568c2ecf20Sopenharmony_ci .den_min = SB8_DEN(11025), 578c2ecf20Sopenharmony_ci .den_max = SB8_DEN(11025), 588c2ecf20Sopenharmony_ci .den_step = 1, 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int snd_sb8_hw_constraint_rate_channels(struct snd_pcm_hw_params *params, 638c2ecf20Sopenharmony_ci struct snd_pcm_hw_rule *rule) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); 668c2ecf20Sopenharmony_ci if (c->min > 1) { 678c2ecf20Sopenharmony_ci unsigned int num = 0, den = 0; 688c2ecf20Sopenharmony_ci int err = snd_interval_ratnum(hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE), 698c2ecf20Sopenharmony_ci 2, stereo_clocks, &num, &den); 708c2ecf20Sopenharmony_ci if (err >= 0 && den) { 718c2ecf20Sopenharmony_ci params->rate_num = num; 728c2ecf20Sopenharmony_ci params->rate_den = den; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci return err; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci return 0; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic int snd_sb8_hw_constraint_channels_rate(struct snd_pcm_hw_params *params, 808c2ecf20Sopenharmony_ci struct snd_pcm_hw_rule *rule) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct snd_interval *r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); 838c2ecf20Sopenharmony_ci if (r->min > SB8_RATE(22050) || r->max <= SB8_RATE(11025)) { 848c2ecf20Sopenharmony_ci struct snd_interval t = { .min = 1, .max = 1 }; 858c2ecf20Sopenharmony_ci return snd_interval_refine(hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS), &t); 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci return 0; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int snd_sb8_playback_prepare(struct snd_pcm_substream *substream) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci unsigned long flags; 938c2ecf20Sopenharmony_ci struct snd_sb *chip = snd_pcm_substream_chip(substream); 948c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 958c2ecf20Sopenharmony_ci unsigned int mixreg, rate, size, count; 968c2ecf20Sopenharmony_ci unsigned char format; 978c2ecf20Sopenharmony_ci unsigned char stereo = runtime->channels > 1; 988c2ecf20Sopenharmony_ci int dma; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci rate = runtime->rate; 1018c2ecf20Sopenharmony_ci switch (chip->hardware) { 1028c2ecf20Sopenharmony_ci case SB_HW_JAZZ16: 1038c2ecf20Sopenharmony_ci if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) { 1048c2ecf20Sopenharmony_ci if (chip->mode & SB_MODE_CAPTURE_16) 1058c2ecf20Sopenharmony_ci return -EBUSY; 1068c2ecf20Sopenharmony_ci else 1078c2ecf20Sopenharmony_ci chip->mode |= SB_MODE_PLAYBACK_16; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci chip->playback_format = SB_DSP_LO_OUTPUT_AUTO; 1108c2ecf20Sopenharmony_ci break; 1118c2ecf20Sopenharmony_ci case SB_HW_PRO: 1128c2ecf20Sopenharmony_ci if (runtime->channels > 1) { 1138c2ecf20Sopenharmony_ci if (snd_BUG_ON(rate != SB8_RATE(11025) && 1148c2ecf20Sopenharmony_ci rate != SB8_RATE(22050))) 1158c2ecf20Sopenharmony_ci return -EINVAL; 1168c2ecf20Sopenharmony_ci chip->playback_format = SB_DSP_HI_OUTPUT_AUTO; 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci fallthrough; 1208c2ecf20Sopenharmony_ci case SB_HW_201: 1218c2ecf20Sopenharmony_ci if (rate > 23000) { 1228c2ecf20Sopenharmony_ci chip->playback_format = SB_DSP_HI_OUTPUT_AUTO; 1238c2ecf20Sopenharmony_ci break; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci fallthrough; 1268c2ecf20Sopenharmony_ci case SB_HW_20: 1278c2ecf20Sopenharmony_ci chip->playback_format = SB_DSP_LO_OUTPUT_AUTO; 1288c2ecf20Sopenharmony_ci break; 1298c2ecf20Sopenharmony_ci case SB_HW_10: 1308c2ecf20Sopenharmony_ci chip->playback_format = SB_DSP_OUTPUT; 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci default: 1338c2ecf20Sopenharmony_ci return -EINVAL; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci if (chip->mode & SB_MODE_PLAYBACK_16) { 1368c2ecf20Sopenharmony_ci format = stereo ? SB_DSP_STEREO_16BIT : SB_DSP_MONO_16BIT; 1378c2ecf20Sopenharmony_ci dma = chip->dma16; 1388c2ecf20Sopenharmony_ci } else { 1398c2ecf20Sopenharmony_ci format = stereo ? SB_DSP_STEREO_8BIT : SB_DSP_MONO_8BIT; 1408c2ecf20Sopenharmony_ci chip->mode |= SB_MODE_PLAYBACK_8; 1418c2ecf20Sopenharmony_ci dma = chip->dma8; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream); 1448c2ecf20Sopenharmony_ci count = chip->p_period_size = snd_pcm_lib_period_bytes(substream); 1458c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 1468c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON); 1478c2ecf20Sopenharmony_ci if (chip->hardware == SB_HW_JAZZ16) 1488c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, format); 1498c2ecf20Sopenharmony_ci else if (stereo) { 1508c2ecf20Sopenharmony_ci /* set playback stereo mode */ 1518c2ecf20Sopenharmony_ci spin_lock(&chip->mixer_lock); 1528c2ecf20Sopenharmony_ci mixreg = snd_sbmixer_read(chip, SB_DSP_STEREO_SW); 1538c2ecf20Sopenharmony_ci snd_sbmixer_write(chip, SB_DSP_STEREO_SW, mixreg | 0x02); 1548c2ecf20Sopenharmony_ci spin_unlock(&chip->mixer_lock); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* Soundblaster hardware programming reference guide, 3-23 */ 1578c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_DMA8_EXIT); 1588c2ecf20Sopenharmony_ci runtime->dma_area[0] = 0x80; 1598c2ecf20Sopenharmony_ci snd_dma_program(dma, runtime->dma_addr, 1, DMA_MODE_WRITE); 1608c2ecf20Sopenharmony_ci /* force interrupt */ 1618c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_OUTPUT); 1628c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, 0); 1638c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, 0); 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE); 1668c2ecf20Sopenharmony_ci if (stereo) { 1678c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, 256 - runtime->rate_den / 2); 1688c2ecf20Sopenharmony_ci spin_lock(&chip->mixer_lock); 1698c2ecf20Sopenharmony_ci /* save output filter status and turn it off */ 1708c2ecf20Sopenharmony_ci mixreg = snd_sbmixer_read(chip, SB_DSP_PLAYBACK_FILT); 1718c2ecf20Sopenharmony_ci snd_sbmixer_write(chip, SB_DSP_PLAYBACK_FILT, mixreg | 0x20); 1728c2ecf20Sopenharmony_ci spin_unlock(&chip->mixer_lock); 1738c2ecf20Sopenharmony_ci /* just use force_mode16 for temporary storate... */ 1748c2ecf20Sopenharmony_ci chip->force_mode16 = mixreg; 1758c2ecf20Sopenharmony_ci } else { 1768c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, 256 - runtime->rate_den); 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci if (chip->playback_format != SB_DSP_OUTPUT) { 1798c2ecf20Sopenharmony_ci if (chip->mode & SB_MODE_PLAYBACK_16) 1808c2ecf20Sopenharmony_ci count /= 2; 1818c2ecf20Sopenharmony_ci count--; 1828c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE); 1838c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, count & 0xff); 1848c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, count >> 8); 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 1878c2ecf20Sopenharmony_ci snd_dma_program(dma, runtime->dma_addr, 1888c2ecf20Sopenharmony_ci size, DMA_MODE_WRITE | DMA_AUTOINIT); 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int snd_sb8_playback_trigger(struct snd_pcm_substream *substream, 1938c2ecf20Sopenharmony_ci int cmd) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci unsigned long flags; 1968c2ecf20Sopenharmony_ci struct snd_sb *chip = snd_pcm_substream_chip(substream); 1978c2ecf20Sopenharmony_ci unsigned int count; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 2008c2ecf20Sopenharmony_ci switch (cmd) { 2018c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 2028c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, chip->playback_format); 2038c2ecf20Sopenharmony_ci if (chip->playback_format == SB_DSP_OUTPUT) { 2048c2ecf20Sopenharmony_ci count = chip->p_period_size - 1; 2058c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, count & 0xff); 2068c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, count >> 8); 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 2108c2ecf20Sopenharmony_ci if (chip->playback_format == SB_DSP_HI_OUTPUT_AUTO) { 2118c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 2128c2ecf20Sopenharmony_ci snd_sbdsp_reset(chip); 2138c2ecf20Sopenharmony_ci if (runtime->channels > 1) { 2148c2ecf20Sopenharmony_ci spin_lock(&chip->mixer_lock); 2158c2ecf20Sopenharmony_ci /* restore output filter and set hardware to mono mode */ 2168c2ecf20Sopenharmony_ci snd_sbmixer_write(chip, SB_DSP_STEREO_SW, chip->force_mode16 & ~0x02); 2178c2ecf20Sopenharmony_ci spin_unlock(&chip->mixer_lock); 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci } else { 2208c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF); 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 2258c2ecf20Sopenharmony_ci return 0; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic int snd_sb8_capture_prepare(struct snd_pcm_substream *substream) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci unsigned long flags; 2318c2ecf20Sopenharmony_ci struct snd_sb *chip = snd_pcm_substream_chip(substream); 2328c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 2338c2ecf20Sopenharmony_ci unsigned int mixreg, rate, size, count; 2348c2ecf20Sopenharmony_ci unsigned char format; 2358c2ecf20Sopenharmony_ci unsigned char stereo = runtime->channels > 1; 2368c2ecf20Sopenharmony_ci int dma; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci rate = runtime->rate; 2398c2ecf20Sopenharmony_ci switch (chip->hardware) { 2408c2ecf20Sopenharmony_ci case SB_HW_JAZZ16: 2418c2ecf20Sopenharmony_ci if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) { 2428c2ecf20Sopenharmony_ci if (chip->mode & SB_MODE_PLAYBACK_16) 2438c2ecf20Sopenharmony_ci return -EBUSY; 2448c2ecf20Sopenharmony_ci else 2458c2ecf20Sopenharmony_ci chip->mode |= SB_MODE_CAPTURE_16; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci chip->capture_format = SB_DSP_LO_INPUT_AUTO; 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci case SB_HW_PRO: 2508c2ecf20Sopenharmony_ci if (runtime->channels > 1) { 2518c2ecf20Sopenharmony_ci if (snd_BUG_ON(rate != SB8_RATE(11025) && 2528c2ecf20Sopenharmony_ci rate != SB8_RATE(22050))) 2538c2ecf20Sopenharmony_ci return -EINVAL; 2548c2ecf20Sopenharmony_ci chip->capture_format = SB_DSP_HI_INPUT_AUTO; 2558c2ecf20Sopenharmony_ci break; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci chip->capture_format = (rate > 23000) ? SB_DSP_HI_INPUT_AUTO : SB_DSP_LO_INPUT_AUTO; 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci case SB_HW_201: 2608c2ecf20Sopenharmony_ci if (rate > 13000) { 2618c2ecf20Sopenharmony_ci chip->capture_format = SB_DSP_HI_INPUT_AUTO; 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci fallthrough; 2658c2ecf20Sopenharmony_ci case SB_HW_20: 2668c2ecf20Sopenharmony_ci chip->capture_format = SB_DSP_LO_INPUT_AUTO; 2678c2ecf20Sopenharmony_ci break; 2688c2ecf20Sopenharmony_ci case SB_HW_10: 2698c2ecf20Sopenharmony_ci chip->capture_format = SB_DSP_INPUT; 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci default: 2728c2ecf20Sopenharmony_ci return -EINVAL; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci if (chip->mode & SB_MODE_CAPTURE_16) { 2758c2ecf20Sopenharmony_ci format = stereo ? SB_DSP_STEREO_16BIT : SB_DSP_MONO_16BIT; 2768c2ecf20Sopenharmony_ci dma = chip->dma16; 2778c2ecf20Sopenharmony_ci } else { 2788c2ecf20Sopenharmony_ci format = stereo ? SB_DSP_STEREO_8BIT : SB_DSP_MONO_8BIT; 2798c2ecf20Sopenharmony_ci chip->mode |= SB_MODE_CAPTURE_8; 2808c2ecf20Sopenharmony_ci dma = chip->dma8; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream); 2838c2ecf20Sopenharmony_ci count = chip->c_period_size = snd_pcm_lib_period_bytes(substream); 2848c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 2858c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF); 2868c2ecf20Sopenharmony_ci if (chip->hardware == SB_HW_JAZZ16) 2878c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, format); 2888c2ecf20Sopenharmony_ci else if (stereo) 2898c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_STEREO_8BIT); 2908c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE); 2918c2ecf20Sopenharmony_ci if (stereo) { 2928c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, 256 - runtime->rate_den / 2); 2938c2ecf20Sopenharmony_ci spin_lock(&chip->mixer_lock); 2948c2ecf20Sopenharmony_ci /* save input filter status and turn it off */ 2958c2ecf20Sopenharmony_ci mixreg = snd_sbmixer_read(chip, SB_DSP_CAPTURE_FILT); 2968c2ecf20Sopenharmony_ci snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, mixreg | 0x20); 2978c2ecf20Sopenharmony_ci spin_unlock(&chip->mixer_lock); 2988c2ecf20Sopenharmony_ci /* just use force_mode16 for temporary storate... */ 2998c2ecf20Sopenharmony_ci chip->force_mode16 = mixreg; 3008c2ecf20Sopenharmony_ci } else { 3018c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, 256 - runtime->rate_den); 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci if (chip->capture_format != SB_DSP_INPUT) { 3048c2ecf20Sopenharmony_ci if (chip->mode & SB_MODE_PLAYBACK_16) 3058c2ecf20Sopenharmony_ci count /= 2; 3068c2ecf20Sopenharmony_ci count--; 3078c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE); 3088c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, count & 0xff); 3098c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, count >> 8); 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 3128c2ecf20Sopenharmony_ci snd_dma_program(dma, runtime->dma_addr, 3138c2ecf20Sopenharmony_ci size, DMA_MODE_READ | DMA_AUTOINIT); 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int snd_sb8_capture_trigger(struct snd_pcm_substream *substream, 3188c2ecf20Sopenharmony_ci int cmd) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci unsigned long flags; 3218c2ecf20Sopenharmony_ci struct snd_sb *chip = snd_pcm_substream_chip(substream); 3228c2ecf20Sopenharmony_ci unsigned int count; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 3258c2ecf20Sopenharmony_ci switch (cmd) { 3268c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 3278c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, chip->capture_format); 3288c2ecf20Sopenharmony_ci if (chip->capture_format == SB_DSP_INPUT) { 3298c2ecf20Sopenharmony_ci count = chip->c_period_size - 1; 3308c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, count & 0xff); 3318c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, count >> 8); 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 3358c2ecf20Sopenharmony_ci if (chip->capture_format == SB_DSP_HI_INPUT_AUTO) { 3368c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 3378c2ecf20Sopenharmony_ci snd_sbdsp_reset(chip); 3388c2ecf20Sopenharmony_ci if (runtime->channels > 1) { 3398c2ecf20Sopenharmony_ci /* restore input filter status */ 3408c2ecf20Sopenharmony_ci spin_lock(&chip->mixer_lock); 3418c2ecf20Sopenharmony_ci snd_sbmixer_write(chip, SB_DSP_CAPTURE_FILT, chip->force_mode16); 3428c2ecf20Sopenharmony_ci spin_unlock(&chip->mixer_lock); 3438c2ecf20Sopenharmony_ci /* set hardware to mono mode */ 3448c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_MONO_8BIT); 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci } else { 3478c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_DMA8_OFF); 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF); 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 3528c2ecf20Sopenharmony_ci return 0; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ciirqreturn_t snd_sb8dsp_interrupt(struct snd_sb *chip) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci snd_sb_ack_8bit(chip); 3608c2ecf20Sopenharmony_ci switch (chip->mode) { 3618c2ecf20Sopenharmony_ci case SB_MODE_PLAYBACK_16: /* ok.. playback is active */ 3628c2ecf20Sopenharmony_ci if (chip->hardware != SB_HW_JAZZ16) 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci fallthrough; 3658c2ecf20Sopenharmony_ci case SB_MODE_PLAYBACK_8: 3668c2ecf20Sopenharmony_ci substream = chip->playback_substream; 3678c2ecf20Sopenharmony_ci if (chip->playback_format == SB_DSP_OUTPUT) 3688c2ecf20Sopenharmony_ci snd_sb8_playback_trigger(substream, SNDRV_PCM_TRIGGER_START); 3698c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(substream); 3708c2ecf20Sopenharmony_ci break; 3718c2ecf20Sopenharmony_ci case SB_MODE_CAPTURE_16: 3728c2ecf20Sopenharmony_ci if (chip->hardware != SB_HW_JAZZ16) 3738c2ecf20Sopenharmony_ci break; 3748c2ecf20Sopenharmony_ci fallthrough; 3758c2ecf20Sopenharmony_ci case SB_MODE_CAPTURE_8: 3768c2ecf20Sopenharmony_ci substream = chip->capture_substream; 3778c2ecf20Sopenharmony_ci if (chip->capture_format == SB_DSP_INPUT) 3788c2ecf20Sopenharmony_ci snd_sb8_capture_trigger(substream, SNDRV_PCM_TRIGGER_START); 3798c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(substream); 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_sb8_playback_pointer(struct snd_pcm_substream *substream) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci struct snd_sb *chip = snd_pcm_substream_chip(substream); 3888c2ecf20Sopenharmony_ci size_t ptr; 3898c2ecf20Sopenharmony_ci int dma; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (chip->mode & SB_MODE_PLAYBACK_8) 3928c2ecf20Sopenharmony_ci dma = chip->dma8; 3938c2ecf20Sopenharmony_ci else if (chip->mode & SB_MODE_PLAYBACK_16) 3948c2ecf20Sopenharmony_ci dma = chip->dma16; 3958c2ecf20Sopenharmony_ci else 3968c2ecf20Sopenharmony_ci return 0; 3978c2ecf20Sopenharmony_ci ptr = snd_dma_pointer(dma, chip->p_dma_size); 3988c2ecf20Sopenharmony_ci return bytes_to_frames(substream->runtime, ptr); 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_sb8_capture_pointer(struct snd_pcm_substream *substream) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct snd_sb *chip = snd_pcm_substream_chip(substream); 4048c2ecf20Sopenharmony_ci size_t ptr; 4058c2ecf20Sopenharmony_ci int dma; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (chip->mode & SB_MODE_CAPTURE_8) 4088c2ecf20Sopenharmony_ci dma = chip->dma8; 4098c2ecf20Sopenharmony_ci else if (chip->mode & SB_MODE_CAPTURE_16) 4108c2ecf20Sopenharmony_ci dma = chip->dma16; 4118c2ecf20Sopenharmony_ci else 4128c2ecf20Sopenharmony_ci return 0; 4138c2ecf20Sopenharmony_ci ptr = snd_dma_pointer(dma, chip->c_dma_size); 4148c2ecf20Sopenharmony_ci return bytes_to_frames(substream->runtime, ptr); 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci/* 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_sb8_playback = 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 4248c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 4258c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8, 4268c2ecf20Sopenharmony_ci .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 | 4278c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050), 4288c2ecf20Sopenharmony_ci .rate_min = 4000, 4298c2ecf20Sopenharmony_ci .rate_max = 23000, 4308c2ecf20Sopenharmony_ci .channels_min = 1, 4318c2ecf20Sopenharmony_ci .channels_max = 1, 4328c2ecf20Sopenharmony_ci .buffer_bytes_max = 65536, 4338c2ecf20Sopenharmony_ci .period_bytes_min = 64, 4348c2ecf20Sopenharmony_ci .period_bytes_max = 65536, 4358c2ecf20Sopenharmony_ci .periods_min = 1, 4368c2ecf20Sopenharmony_ci .periods_max = 1024, 4378c2ecf20Sopenharmony_ci .fifo_size = 0, 4388c2ecf20Sopenharmony_ci}; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_sb8_capture = 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 4438c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 4448c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8, 4458c2ecf20Sopenharmony_ci .rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 | 4468c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_11025), 4478c2ecf20Sopenharmony_ci .rate_min = 4000, 4488c2ecf20Sopenharmony_ci .rate_max = 13000, 4498c2ecf20Sopenharmony_ci .channels_min = 1, 4508c2ecf20Sopenharmony_ci .channels_max = 1, 4518c2ecf20Sopenharmony_ci .buffer_bytes_max = 65536, 4528c2ecf20Sopenharmony_ci .period_bytes_min = 64, 4538c2ecf20Sopenharmony_ci .period_bytes_max = 65536, 4548c2ecf20Sopenharmony_ci .periods_min = 1, 4558c2ecf20Sopenharmony_ci .periods_max = 1024, 4568c2ecf20Sopenharmony_ci .fifo_size = 0, 4578c2ecf20Sopenharmony_ci}; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci/* 4608c2ecf20Sopenharmony_ci * 4618c2ecf20Sopenharmony_ci */ 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic int snd_sb8_open(struct snd_pcm_substream *substream) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci struct snd_sb *chip = snd_pcm_substream_chip(substream); 4668c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4678c2ecf20Sopenharmony_ci unsigned long flags; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 4708c2ecf20Sopenharmony_ci if (chip->open) { 4718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 4728c2ecf20Sopenharmony_ci return -EAGAIN; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci chip->open |= SB_OPEN_PCM; 4758c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 4768c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 4778c2ecf20Sopenharmony_ci chip->playback_substream = substream; 4788c2ecf20Sopenharmony_ci runtime->hw = snd_sb8_playback; 4798c2ecf20Sopenharmony_ci } else { 4808c2ecf20Sopenharmony_ci chip->capture_substream = substream; 4818c2ecf20Sopenharmony_ci runtime->hw = snd_sb8_capture; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci switch (chip->hardware) { 4848c2ecf20Sopenharmony_ci case SB_HW_JAZZ16: 4858c2ecf20Sopenharmony_ci if (chip->dma16 == 5 || chip->dma16 == 7) 4868c2ecf20Sopenharmony_ci runtime->hw.formats |= SNDRV_PCM_FMTBIT_S16_LE; 4878c2ecf20Sopenharmony_ci runtime->hw.rates |= SNDRV_PCM_RATE_8000_48000; 4888c2ecf20Sopenharmony_ci runtime->hw.rate_min = 4000; 4898c2ecf20Sopenharmony_ci runtime->hw.rate_max = 50000; 4908c2ecf20Sopenharmony_ci runtime->hw.channels_max = 2; 4918c2ecf20Sopenharmony_ci break; 4928c2ecf20Sopenharmony_ci case SB_HW_PRO: 4938c2ecf20Sopenharmony_ci runtime->hw.rate_max = 44100; 4948c2ecf20Sopenharmony_ci runtime->hw.channels_max = 2; 4958c2ecf20Sopenharmony_ci snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 4968c2ecf20Sopenharmony_ci snd_sb8_hw_constraint_rate_channels, NULL, 4978c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, 4988c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, -1); 4998c2ecf20Sopenharmony_ci snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 5008c2ecf20Sopenharmony_ci snd_sb8_hw_constraint_channels_rate, NULL, 5018c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, -1); 5028c2ecf20Sopenharmony_ci break; 5038c2ecf20Sopenharmony_ci case SB_HW_201: 5048c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 5058c2ecf20Sopenharmony_ci runtime->hw.rate_max = 44100; 5068c2ecf20Sopenharmony_ci } else { 5078c2ecf20Sopenharmony_ci runtime->hw.rate_max = 15000; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci default: 5108c2ecf20Sopenharmony_ci break; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 5138c2ecf20Sopenharmony_ci &hw_constraints_clock); 5148c2ecf20Sopenharmony_ci if (chip->dma8 > 3 || chip->dma16 >= 0) { 5158c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_step(runtime, 0, 5168c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 2); 5178c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_step(runtime, 0, 5188c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 2); 5198c2ecf20Sopenharmony_ci runtime->hw.buffer_bytes_max = 128 * 1024 * 1024; 5208c2ecf20Sopenharmony_ci runtime->hw.period_bytes_max = 128 * 1024 * 1024; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci return 0; 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic int snd_sb8_close(struct snd_pcm_substream *substream) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci unsigned long flags; 5288c2ecf20Sopenharmony_ci struct snd_sb *chip = snd_pcm_substream_chip(substream); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci chip->playback_substream = NULL; 5318c2ecf20Sopenharmony_ci chip->capture_substream = NULL; 5328c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->open_lock, flags); 5338c2ecf20Sopenharmony_ci chip->open &= ~SB_OPEN_PCM; 5348c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 5358c2ecf20Sopenharmony_ci chip->mode &= ~SB_MODE_PLAYBACK; 5368c2ecf20Sopenharmony_ci else 5378c2ecf20Sopenharmony_ci chip->mode &= ~SB_MODE_CAPTURE; 5388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->open_lock, flags); 5398c2ecf20Sopenharmony_ci return 0; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci/* 5438c2ecf20Sopenharmony_ci * Initialization part 5448c2ecf20Sopenharmony_ci */ 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_sb8_playback_ops = { 5478c2ecf20Sopenharmony_ci .open = snd_sb8_open, 5488c2ecf20Sopenharmony_ci .close = snd_sb8_close, 5498c2ecf20Sopenharmony_ci .prepare = snd_sb8_playback_prepare, 5508c2ecf20Sopenharmony_ci .trigger = snd_sb8_playback_trigger, 5518c2ecf20Sopenharmony_ci .pointer = snd_sb8_playback_pointer, 5528c2ecf20Sopenharmony_ci}; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_sb8_capture_ops = { 5558c2ecf20Sopenharmony_ci .open = snd_sb8_open, 5568c2ecf20Sopenharmony_ci .close = snd_sb8_close, 5578c2ecf20Sopenharmony_ci .prepare = snd_sb8_capture_prepare, 5588c2ecf20Sopenharmony_ci .trigger = snd_sb8_capture_trigger, 5598c2ecf20Sopenharmony_ci .pointer = snd_sb8_capture_pointer, 5608c2ecf20Sopenharmony_ci}; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ciint snd_sb8dsp_pcm(struct snd_sb *chip, int device) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci struct snd_card *card = chip->card; 5658c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 5668c2ecf20Sopenharmony_ci int err; 5678c2ecf20Sopenharmony_ci size_t max_prealloc = 64 * 1024; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if ((err = snd_pcm_new(card, "SB8 DSP", device, 1, 1, &pcm)) < 0) 5708c2ecf20Sopenharmony_ci return err; 5718c2ecf20Sopenharmony_ci sprintf(pcm->name, "DSP v%i.%i", chip->version >> 8, chip->version & 0xff); 5728c2ecf20Sopenharmony_ci pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; 5738c2ecf20Sopenharmony_ci pcm->private_data = chip; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb8_playback_ops); 5768c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb8_capture_ops); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (chip->dma8 > 3 || chip->dma16 >= 0) 5798c2ecf20Sopenharmony_ci max_prealloc = 128 * 1024; 5808c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 5818c2ecf20Sopenharmony_ci card->dev, 64*1024, max_prealloc); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci return 0; 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sb8dsp_pcm); 5878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sb8dsp_interrupt); 5888c2ecf20Sopenharmony_ci /* sb8_midi.c */ 5898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sb8dsp_midi_interrupt); 5908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sb8dsp_midi); 591