18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for S3 SonicVibes soundcard 48c2ecf20Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * BUGS: 78c2ecf20Sopenharmony_ci * It looks like 86c617 rev 3 doesn't supports DDMA buffers above 16MB? 88c2ecf20Sopenharmony_ci * Driver sometimes hangs... Nobody knows why at this moment... 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/pci.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/gameport.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <sound/core.h> 228c2ecf20Sopenharmony_ci#include <sound/pcm.h> 238c2ecf20Sopenharmony_ci#include <sound/info.h> 248c2ecf20Sopenharmony_ci#include <sound/control.h> 258c2ecf20Sopenharmony_ci#include <sound/mpu401.h> 268c2ecf20Sopenharmony_ci#include <sound/opl3.h> 278c2ecf20Sopenharmony_ci#include <sound/initval.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("S3 SonicVibes PCI"); 318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 328c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{S3,SonicVibes PCI}}"); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#if IS_REACHABLE(CONFIG_GAMEPORT) 358c2ecf20Sopenharmony_ci#define SUPPORT_JOYSTICK 1 368c2ecf20Sopenharmony_ci#endif 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 398c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 408c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ 418c2ecf20Sopenharmony_cistatic bool reverb[SNDRV_CARDS]; 428c2ecf20Sopenharmony_cistatic bool mge[SNDRV_CARDS]; 438c2ecf20Sopenharmony_cistatic unsigned int dmaio = 0x7a00; /* DDMA i/o address */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for S3 SonicVibes soundcard."); 478c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 488c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for S3 SonicVibes soundcard."); 498c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 508c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable S3 SonicVibes soundcard."); 518c2ecf20Sopenharmony_cimodule_param_array(reverb, bool, NULL, 0444); 528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(reverb, "Enable reverb (SRAM is present) for S3 SonicVibes soundcard."); 538c2ecf20Sopenharmony_cimodule_param_array(mge, bool, NULL, 0444); 548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mge, "MIC Gain Enable for S3 SonicVibes soundcard."); 558c2ecf20Sopenharmony_cimodule_param_hw(dmaio, uint, ioport, 0444); 568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dmaio, "DDMA i/o base address for S3 SonicVibes soundcard."); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * Enhanced port direct registers 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define SV_REG(sonic, x) ((sonic)->enh_port + SV_REG_##x) 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define SV_REG_CONTROL 0x00 /* R/W: CODEC/Mixer control register */ 658c2ecf20Sopenharmony_ci#define SV_ENHANCED 0x01 /* audio mode select - enhanced mode */ 668c2ecf20Sopenharmony_ci#define SV_TEST 0x02 /* test bit */ 678c2ecf20Sopenharmony_ci#define SV_REVERB 0x04 /* reverb enable */ 688c2ecf20Sopenharmony_ci#define SV_WAVETABLE 0x08 /* wavetable active / FM active if not set */ 698c2ecf20Sopenharmony_ci#define SV_INTA 0x20 /* INTA driving - should be always 1 */ 708c2ecf20Sopenharmony_ci#define SV_RESET 0x80 /* reset chip */ 718c2ecf20Sopenharmony_ci#define SV_REG_IRQMASK 0x01 /* R/W: CODEC/Mixer interrupt mask register */ 728c2ecf20Sopenharmony_ci#define SV_DMAA_MASK 0x01 /* mask DMA-A interrupt */ 738c2ecf20Sopenharmony_ci#define SV_DMAC_MASK 0x04 /* mask DMA-C interrupt */ 748c2ecf20Sopenharmony_ci#define SV_SPEC_MASK 0x08 /* special interrupt mask - should be always masked */ 758c2ecf20Sopenharmony_ci#define SV_UD_MASK 0x40 /* Up/Down button interrupt mask */ 768c2ecf20Sopenharmony_ci#define SV_MIDI_MASK 0x80 /* mask MIDI interrupt */ 778c2ecf20Sopenharmony_ci#define SV_REG_STATUS 0x02 /* R/O: CODEC/Mixer status register */ 788c2ecf20Sopenharmony_ci#define SV_DMAA_IRQ 0x01 /* DMA-A interrupt */ 798c2ecf20Sopenharmony_ci#define SV_DMAC_IRQ 0x04 /* DMA-C interrupt */ 808c2ecf20Sopenharmony_ci#define SV_SPEC_IRQ 0x08 /* special interrupt */ 818c2ecf20Sopenharmony_ci#define SV_UD_IRQ 0x40 /* Up/Down interrupt */ 828c2ecf20Sopenharmony_ci#define SV_MIDI_IRQ 0x80 /* MIDI interrupt */ 838c2ecf20Sopenharmony_ci#define SV_REG_INDEX 0x04 /* R/W: CODEC/Mixer index address register */ 848c2ecf20Sopenharmony_ci#define SV_MCE 0x40 /* mode change enable */ 858c2ecf20Sopenharmony_ci#define SV_TRD 0x80 /* DMA transfer request disabled */ 868c2ecf20Sopenharmony_ci#define SV_REG_DATA 0x05 /* R/W: CODEC/Mixer index data register */ 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* 898c2ecf20Sopenharmony_ci * Enhanced port indirect registers 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define SV_IREG_LEFT_ADC 0x00 /* Left ADC Input Control */ 938c2ecf20Sopenharmony_ci#define SV_IREG_RIGHT_ADC 0x01 /* Right ADC Input Control */ 948c2ecf20Sopenharmony_ci#define SV_IREG_LEFT_AUX1 0x02 /* Left AUX1 Input Control */ 958c2ecf20Sopenharmony_ci#define SV_IREG_RIGHT_AUX1 0x03 /* Right AUX1 Input Control */ 968c2ecf20Sopenharmony_ci#define SV_IREG_LEFT_CD 0x04 /* Left CD Input Control */ 978c2ecf20Sopenharmony_ci#define SV_IREG_RIGHT_CD 0x05 /* Right CD Input Control */ 988c2ecf20Sopenharmony_ci#define SV_IREG_LEFT_LINE 0x06 /* Left Line Input Control */ 998c2ecf20Sopenharmony_ci#define SV_IREG_RIGHT_LINE 0x07 /* Right Line Input Control */ 1008c2ecf20Sopenharmony_ci#define SV_IREG_MIC 0x08 /* MIC Input Control */ 1018c2ecf20Sopenharmony_ci#define SV_IREG_GAME_PORT 0x09 /* Game Port Control */ 1028c2ecf20Sopenharmony_ci#define SV_IREG_LEFT_SYNTH 0x0a /* Left Synth Input Control */ 1038c2ecf20Sopenharmony_ci#define SV_IREG_RIGHT_SYNTH 0x0b /* Right Synth Input Control */ 1048c2ecf20Sopenharmony_ci#define SV_IREG_LEFT_AUX2 0x0c /* Left AUX2 Input Control */ 1058c2ecf20Sopenharmony_ci#define SV_IREG_RIGHT_AUX2 0x0d /* Right AUX2 Input Control */ 1068c2ecf20Sopenharmony_ci#define SV_IREG_LEFT_ANALOG 0x0e /* Left Analog Mixer Output Control */ 1078c2ecf20Sopenharmony_ci#define SV_IREG_RIGHT_ANALOG 0x0f /* Right Analog Mixer Output Control */ 1088c2ecf20Sopenharmony_ci#define SV_IREG_LEFT_PCM 0x10 /* Left PCM Input Control */ 1098c2ecf20Sopenharmony_ci#define SV_IREG_RIGHT_PCM 0x11 /* Right PCM Input Control */ 1108c2ecf20Sopenharmony_ci#define SV_IREG_DMA_DATA_FMT 0x12 /* DMA Data Format */ 1118c2ecf20Sopenharmony_ci#define SV_IREG_PC_ENABLE 0x13 /* Playback/Capture Enable Register */ 1128c2ecf20Sopenharmony_ci#define SV_IREG_UD_BUTTON 0x14 /* Up/Down Button Register */ 1138c2ecf20Sopenharmony_ci#define SV_IREG_REVISION 0x15 /* Revision */ 1148c2ecf20Sopenharmony_ci#define SV_IREG_ADC_OUTPUT_CTRL 0x16 /* ADC Output Control */ 1158c2ecf20Sopenharmony_ci#define SV_IREG_DMA_A_UPPER 0x18 /* DMA A Upper Base Count */ 1168c2ecf20Sopenharmony_ci#define SV_IREG_DMA_A_LOWER 0x19 /* DMA A Lower Base Count */ 1178c2ecf20Sopenharmony_ci#define SV_IREG_DMA_C_UPPER 0x1c /* DMA C Upper Base Count */ 1188c2ecf20Sopenharmony_ci#define SV_IREG_DMA_C_LOWER 0x1d /* DMA C Lower Base Count */ 1198c2ecf20Sopenharmony_ci#define SV_IREG_PCM_RATE_LOW 0x1e /* PCM Sampling Rate Low Byte */ 1208c2ecf20Sopenharmony_ci#define SV_IREG_PCM_RATE_HIGH 0x1f /* PCM Sampling Rate High Byte */ 1218c2ecf20Sopenharmony_ci#define SV_IREG_SYNTH_RATE_LOW 0x20 /* Synthesizer Sampling Rate Low Byte */ 1228c2ecf20Sopenharmony_ci#define SV_IREG_SYNTH_RATE_HIGH 0x21 /* Synthesizer Sampling Rate High Byte */ 1238c2ecf20Sopenharmony_ci#define SV_IREG_ADC_CLOCK 0x22 /* ADC Clock Source Selection */ 1248c2ecf20Sopenharmony_ci#define SV_IREG_ADC_ALT_RATE 0x23 /* ADC Alternative Sampling Rate Selection */ 1258c2ecf20Sopenharmony_ci#define SV_IREG_ADC_PLL_M 0x24 /* ADC PLL M Register */ 1268c2ecf20Sopenharmony_ci#define SV_IREG_ADC_PLL_N 0x25 /* ADC PLL N Register */ 1278c2ecf20Sopenharmony_ci#define SV_IREG_SYNTH_PLL_M 0x26 /* Synthesizer PLL M Register */ 1288c2ecf20Sopenharmony_ci#define SV_IREG_SYNTH_PLL_N 0x27 /* Synthesizer PLL N Register */ 1298c2ecf20Sopenharmony_ci#define SV_IREG_MPU401 0x2a /* MPU-401 UART Operation */ 1308c2ecf20Sopenharmony_ci#define SV_IREG_DRIVE_CTRL 0x2b /* Drive Control */ 1318c2ecf20Sopenharmony_ci#define SV_IREG_SRS_SPACE 0x2c /* SRS Space Control */ 1328c2ecf20Sopenharmony_ci#define SV_IREG_SRS_CENTER 0x2d /* SRS Center Control */ 1338c2ecf20Sopenharmony_ci#define SV_IREG_WAVE_SOURCE 0x2e /* Wavetable Sample Source Select */ 1348c2ecf20Sopenharmony_ci#define SV_IREG_ANALOG_POWER 0x30 /* Analog Power Down Control */ 1358c2ecf20Sopenharmony_ci#define SV_IREG_DIGITAL_POWER 0x31 /* Digital Power Down Control */ 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci#define SV_IREG_ADC_PLL SV_IREG_ADC_PLL_M 1388c2ecf20Sopenharmony_ci#define SV_IREG_SYNTH_PLL SV_IREG_SYNTH_PLL_M 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* 1418c2ecf20Sopenharmony_ci * DMA registers 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci#define SV_DMA_ADDR0 0x00 1458c2ecf20Sopenharmony_ci#define SV_DMA_ADDR1 0x01 1468c2ecf20Sopenharmony_ci#define SV_DMA_ADDR2 0x02 1478c2ecf20Sopenharmony_ci#define SV_DMA_ADDR3 0x03 1488c2ecf20Sopenharmony_ci#define SV_DMA_COUNT0 0x04 1498c2ecf20Sopenharmony_ci#define SV_DMA_COUNT1 0x05 1508c2ecf20Sopenharmony_ci#define SV_DMA_COUNT2 0x06 1518c2ecf20Sopenharmony_ci#define SV_DMA_MODE 0x0b 1528c2ecf20Sopenharmony_ci#define SV_DMA_RESET 0x0d 1538c2ecf20Sopenharmony_ci#define SV_DMA_MASK 0x0f 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* 1568c2ecf20Sopenharmony_ci * Record sources 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci#define SV_RECSRC_RESERVED (0x00<<5) 1608c2ecf20Sopenharmony_ci#define SV_RECSRC_CD (0x01<<5) 1618c2ecf20Sopenharmony_ci#define SV_RECSRC_DAC (0x02<<5) 1628c2ecf20Sopenharmony_ci#define SV_RECSRC_AUX2 (0x03<<5) 1638c2ecf20Sopenharmony_ci#define SV_RECSRC_LINE (0x04<<5) 1648c2ecf20Sopenharmony_ci#define SV_RECSRC_AUX1 (0x05<<5) 1658c2ecf20Sopenharmony_ci#define SV_RECSRC_MIC (0x06<<5) 1668c2ecf20Sopenharmony_ci#define SV_RECSRC_OUT (0x07<<5) 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/* 1698c2ecf20Sopenharmony_ci * constants 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci#define SV_FULLRATE 48000 1738c2ecf20Sopenharmony_ci#define SV_REFFREQUENCY 24576000 1748c2ecf20Sopenharmony_ci#define SV_ADCMULT 512 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci#define SV_MODE_PLAY 1 1778c2ecf20Sopenharmony_ci#define SV_MODE_CAPTURE 2 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistruct sonicvibes { 1848c2ecf20Sopenharmony_ci unsigned long dma1size; 1858c2ecf20Sopenharmony_ci unsigned long dma2size; 1868c2ecf20Sopenharmony_ci int irq; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci unsigned long sb_port; 1898c2ecf20Sopenharmony_ci unsigned long enh_port; 1908c2ecf20Sopenharmony_ci unsigned long synth_port; 1918c2ecf20Sopenharmony_ci unsigned long midi_port; 1928c2ecf20Sopenharmony_ci unsigned long game_port; 1938c2ecf20Sopenharmony_ci unsigned int dmaa_port; 1948c2ecf20Sopenharmony_ci struct resource *res_dmaa; 1958c2ecf20Sopenharmony_ci unsigned int dmac_port; 1968c2ecf20Sopenharmony_ci struct resource *res_dmac; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci unsigned char enable; 1998c2ecf20Sopenharmony_ci unsigned char irqmask; 2008c2ecf20Sopenharmony_ci unsigned char revision; 2018c2ecf20Sopenharmony_ci unsigned char format; 2028c2ecf20Sopenharmony_ci unsigned char srs_space; 2038c2ecf20Sopenharmony_ci unsigned char srs_center; 2048c2ecf20Sopenharmony_ci unsigned char mpu_switch; 2058c2ecf20Sopenharmony_ci unsigned char wave_source; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci unsigned int mode; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci struct pci_dev *pci; 2108c2ecf20Sopenharmony_ci struct snd_card *card; 2118c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 2128c2ecf20Sopenharmony_ci struct snd_pcm_substream *playback_substream; 2138c2ecf20Sopenharmony_ci struct snd_pcm_substream *capture_substream; 2148c2ecf20Sopenharmony_ci struct snd_rawmidi *rmidi; 2158c2ecf20Sopenharmony_ci struct snd_hwdep *fmsynth; /* S3FM */ 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci spinlock_t reg_lock; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci unsigned int p_dma_size; 2208c2ecf20Sopenharmony_ci unsigned int c_dma_size; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci struct snd_kcontrol *master_mute; 2238c2ecf20Sopenharmony_ci struct snd_kcontrol *master_volume; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci#ifdef SUPPORT_JOYSTICK 2268c2ecf20Sopenharmony_ci struct gameport *gameport; 2278c2ecf20Sopenharmony_ci#endif 2288c2ecf20Sopenharmony_ci}; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic const struct pci_device_id snd_sonic_ids[] = { 2318c2ecf20Sopenharmony_ci { PCI_VDEVICE(S3, 0xca00), 0, }, 2328c2ecf20Sopenharmony_ci { 0, } 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_sonic_ids); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic const struct snd_ratden sonicvibes_adc_clock = { 2388c2ecf20Sopenharmony_ci .num_min = 4000 * 65536, 2398c2ecf20Sopenharmony_ci .num_max = 48000UL * 65536, 2408c2ecf20Sopenharmony_ci .num_step = 1, 2418c2ecf20Sopenharmony_ci .den = 65536, 2428c2ecf20Sopenharmony_ci}; 2438c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_ratdens snd_sonicvibes_hw_constraints_adc_clock = { 2448c2ecf20Sopenharmony_ci .nrats = 1, 2458c2ecf20Sopenharmony_ci .rats = &sonicvibes_adc_clock, 2468c2ecf20Sopenharmony_ci}; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/* 2498c2ecf20Sopenharmony_ci * common I/O routines 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic inline void snd_sonicvibes_setdmaa(struct sonicvibes * sonic, 2538c2ecf20Sopenharmony_ci unsigned int addr, 2548c2ecf20Sopenharmony_ci unsigned int count) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci count--; 2578c2ecf20Sopenharmony_ci outl(addr, sonic->dmaa_port + SV_DMA_ADDR0); 2588c2ecf20Sopenharmony_ci outl(count, sonic->dmaa_port + SV_DMA_COUNT0); 2598c2ecf20Sopenharmony_ci outb(0x18, sonic->dmaa_port + SV_DMA_MODE); 2608c2ecf20Sopenharmony_ci#if 0 2618c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, "program dmaa: addr = 0x%x, paddr = 0x%x\n", 2628c2ecf20Sopenharmony_ci addr, inl(sonic->dmaa_port + SV_DMA_ADDR0)); 2638c2ecf20Sopenharmony_ci#endif 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic inline void snd_sonicvibes_setdmac(struct sonicvibes * sonic, 2678c2ecf20Sopenharmony_ci unsigned int addr, 2688c2ecf20Sopenharmony_ci unsigned int count) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci /* note: dmac is working in word mode!!! */ 2718c2ecf20Sopenharmony_ci count >>= 1; 2728c2ecf20Sopenharmony_ci count--; 2738c2ecf20Sopenharmony_ci outl(addr, sonic->dmac_port + SV_DMA_ADDR0); 2748c2ecf20Sopenharmony_ci outl(count, sonic->dmac_port + SV_DMA_COUNT0); 2758c2ecf20Sopenharmony_ci outb(0x14, sonic->dmac_port + SV_DMA_MODE); 2768c2ecf20Sopenharmony_ci#if 0 2778c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, "program dmac: addr = 0x%x, paddr = 0x%x\n", 2788c2ecf20Sopenharmony_ci addr, inl(sonic->dmac_port + SV_DMA_ADDR0)); 2798c2ecf20Sopenharmony_ci#endif 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic inline unsigned int snd_sonicvibes_getdmaa(struct sonicvibes * sonic) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci return (inl(sonic->dmaa_port + SV_DMA_COUNT0) & 0xffffff) + 1; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic inline unsigned int snd_sonicvibes_getdmac(struct sonicvibes * sonic) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci /* note: dmac is working in word mode!!! */ 2908c2ecf20Sopenharmony_ci return ((inl(sonic->dmac_port + SV_DMA_COUNT0) & 0xffffff) + 1) << 1; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic void snd_sonicvibes_out1(struct sonicvibes * sonic, 2948c2ecf20Sopenharmony_ci unsigned char reg, 2958c2ecf20Sopenharmony_ci unsigned char value) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci outb(reg, SV_REG(sonic, INDEX)); 2988c2ecf20Sopenharmony_ci udelay(10); 2998c2ecf20Sopenharmony_ci outb(value, SV_REG(sonic, DATA)); 3008c2ecf20Sopenharmony_ci udelay(10); 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic void snd_sonicvibes_out(struct sonicvibes * sonic, 3048c2ecf20Sopenharmony_ci unsigned char reg, 3058c2ecf20Sopenharmony_ci unsigned char value) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci unsigned long flags; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci spin_lock_irqsave(&sonic->reg_lock, flags); 3108c2ecf20Sopenharmony_ci outb(reg, SV_REG(sonic, INDEX)); 3118c2ecf20Sopenharmony_ci udelay(10); 3128c2ecf20Sopenharmony_ci outb(value, SV_REG(sonic, DATA)); 3138c2ecf20Sopenharmony_ci udelay(10); 3148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sonic->reg_lock, flags); 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic unsigned char snd_sonicvibes_in1(struct sonicvibes * sonic, unsigned char reg) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci unsigned char value; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci outb(reg, SV_REG(sonic, INDEX)); 3228c2ecf20Sopenharmony_ci udelay(10); 3238c2ecf20Sopenharmony_ci value = inb(SV_REG(sonic, DATA)); 3248c2ecf20Sopenharmony_ci udelay(10); 3258c2ecf20Sopenharmony_ci return value; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic unsigned char snd_sonicvibes_in(struct sonicvibes * sonic, unsigned char reg) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci unsigned long flags; 3318c2ecf20Sopenharmony_ci unsigned char value; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci spin_lock_irqsave(&sonic->reg_lock, flags); 3348c2ecf20Sopenharmony_ci outb(reg, SV_REG(sonic, INDEX)); 3358c2ecf20Sopenharmony_ci udelay(10); 3368c2ecf20Sopenharmony_ci value = inb(SV_REG(sonic, DATA)); 3378c2ecf20Sopenharmony_ci udelay(10); 3388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sonic->reg_lock, flags); 3398c2ecf20Sopenharmony_ci return value; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci#if 0 3438c2ecf20Sopenharmony_cistatic void snd_sonicvibes_debug(struct sonicvibes * sonic) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 3468c2ecf20Sopenharmony_ci "SV REGS: INDEX = 0x%02x STATUS = 0x%02x\n", 3478c2ecf20Sopenharmony_ci inb(SV_REG(sonic, INDEX)), inb(SV_REG(sonic, STATUS))); 3488c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 3498c2ecf20Sopenharmony_ci " 0x00: left input = 0x%02x 0x20: synth rate low = 0x%02x\n", 3508c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x00), snd_sonicvibes_in(sonic, 0x20)); 3518c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 3528c2ecf20Sopenharmony_ci " 0x01: right input = 0x%02x 0x21: synth rate high = 0x%02x\n", 3538c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x01), snd_sonicvibes_in(sonic, 0x21)); 3548c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 3558c2ecf20Sopenharmony_ci " 0x02: left AUX1 = 0x%02x 0x22: ADC clock = 0x%02x\n", 3568c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x02), snd_sonicvibes_in(sonic, 0x22)); 3578c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 3588c2ecf20Sopenharmony_ci " 0x03: right AUX1 = 0x%02x 0x23: ADC alt rate = 0x%02x\n", 3598c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x03), snd_sonicvibes_in(sonic, 0x23)); 3608c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 3618c2ecf20Sopenharmony_ci " 0x04: left CD = 0x%02x 0x24: ADC pll M = 0x%02x\n", 3628c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x04), snd_sonicvibes_in(sonic, 0x24)); 3638c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 3648c2ecf20Sopenharmony_ci " 0x05: right CD = 0x%02x 0x25: ADC pll N = 0x%02x\n", 3658c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x05), snd_sonicvibes_in(sonic, 0x25)); 3668c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 3678c2ecf20Sopenharmony_ci " 0x06: left line = 0x%02x 0x26: Synth pll M = 0x%02x\n", 3688c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x06), snd_sonicvibes_in(sonic, 0x26)); 3698c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 3708c2ecf20Sopenharmony_ci " 0x07: right line = 0x%02x 0x27: Synth pll N = 0x%02x\n", 3718c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x07), snd_sonicvibes_in(sonic, 0x27)); 3728c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 3738c2ecf20Sopenharmony_ci " 0x08: MIC = 0x%02x 0x28: --- = 0x%02x\n", 3748c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x08), snd_sonicvibes_in(sonic, 0x28)); 3758c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 3768c2ecf20Sopenharmony_ci " 0x09: Game port = 0x%02x 0x29: --- = 0x%02x\n", 3778c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x09), snd_sonicvibes_in(sonic, 0x29)); 3788c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 3798c2ecf20Sopenharmony_ci " 0x0a: left synth = 0x%02x 0x2a: MPU401 = 0x%02x\n", 3808c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x0a), snd_sonicvibes_in(sonic, 0x2a)); 3818c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 3828c2ecf20Sopenharmony_ci " 0x0b: right synth = 0x%02x 0x2b: drive ctrl = 0x%02x\n", 3838c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x0b), snd_sonicvibes_in(sonic, 0x2b)); 3848c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 3858c2ecf20Sopenharmony_ci " 0x0c: left AUX2 = 0x%02x 0x2c: SRS space = 0x%02x\n", 3868c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x0c), snd_sonicvibes_in(sonic, 0x2c)); 3878c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 3888c2ecf20Sopenharmony_ci " 0x0d: right AUX2 = 0x%02x 0x2d: SRS center = 0x%02x\n", 3898c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x0d), snd_sonicvibes_in(sonic, 0x2d)); 3908c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 3918c2ecf20Sopenharmony_ci " 0x0e: left analog = 0x%02x 0x2e: wave source = 0x%02x\n", 3928c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x0e), snd_sonicvibes_in(sonic, 0x2e)); 3938c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 3948c2ecf20Sopenharmony_ci " 0x0f: right analog = 0x%02x 0x2f: --- = 0x%02x\n", 3958c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x0f), snd_sonicvibes_in(sonic, 0x2f)); 3968c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 3978c2ecf20Sopenharmony_ci " 0x10: left PCM = 0x%02x 0x30: analog power = 0x%02x\n", 3988c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x10), snd_sonicvibes_in(sonic, 0x30)); 3998c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 4008c2ecf20Sopenharmony_ci " 0x11: right PCM = 0x%02x 0x31: analog power = 0x%02x\n", 4018c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x11), snd_sonicvibes_in(sonic, 0x31)); 4028c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 4038c2ecf20Sopenharmony_ci " 0x12: DMA data format = 0x%02x 0x32: --- = 0x%02x\n", 4048c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x12), snd_sonicvibes_in(sonic, 0x32)); 4058c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 4068c2ecf20Sopenharmony_ci " 0x13: P/C enable = 0x%02x 0x33: --- = 0x%02x\n", 4078c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x13), snd_sonicvibes_in(sonic, 0x33)); 4088c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 4098c2ecf20Sopenharmony_ci " 0x14: U/D button = 0x%02x 0x34: --- = 0x%02x\n", 4108c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x14), snd_sonicvibes_in(sonic, 0x34)); 4118c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 4128c2ecf20Sopenharmony_ci " 0x15: revision = 0x%02x 0x35: --- = 0x%02x\n", 4138c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x15), snd_sonicvibes_in(sonic, 0x35)); 4148c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 4158c2ecf20Sopenharmony_ci " 0x16: ADC output ctrl = 0x%02x 0x36: --- = 0x%02x\n", 4168c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x16), snd_sonicvibes_in(sonic, 0x36)); 4178c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 4188c2ecf20Sopenharmony_ci " 0x17: --- = 0x%02x 0x37: --- = 0x%02x\n", 4198c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x17), snd_sonicvibes_in(sonic, 0x37)); 4208c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 4218c2ecf20Sopenharmony_ci " 0x18: DMA A upper cnt = 0x%02x 0x38: --- = 0x%02x\n", 4228c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x18), snd_sonicvibes_in(sonic, 0x38)); 4238c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 4248c2ecf20Sopenharmony_ci " 0x19: DMA A lower cnt = 0x%02x 0x39: --- = 0x%02x\n", 4258c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x19), snd_sonicvibes_in(sonic, 0x39)); 4268c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 4278c2ecf20Sopenharmony_ci " 0x1a: --- = 0x%02x 0x3a: --- = 0x%02x\n", 4288c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x1a), snd_sonicvibes_in(sonic, 0x3a)); 4298c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 4308c2ecf20Sopenharmony_ci " 0x1b: --- = 0x%02x 0x3b: --- = 0x%02x\n", 4318c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x1b), snd_sonicvibes_in(sonic, 0x3b)); 4328c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 4338c2ecf20Sopenharmony_ci " 0x1c: DMA C upper cnt = 0x%02x 0x3c: --- = 0x%02x\n", 4348c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x1c), snd_sonicvibes_in(sonic, 0x3c)); 4358c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 4368c2ecf20Sopenharmony_ci " 0x1d: DMA C upper cnt = 0x%02x 0x3d: --- = 0x%02x\n", 4378c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x1d), snd_sonicvibes_in(sonic, 0x3d)); 4388c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 4398c2ecf20Sopenharmony_ci " 0x1e: PCM rate low = 0x%02x 0x3e: --- = 0x%02x\n", 4408c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x1e), snd_sonicvibes_in(sonic, 0x3e)); 4418c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 4428c2ecf20Sopenharmony_ci " 0x1f: PCM rate high = 0x%02x 0x3f: --- = 0x%02x\n", 4438c2ecf20Sopenharmony_ci snd_sonicvibes_in(sonic, 0x1f), snd_sonicvibes_in(sonic, 0x3f)); 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci#endif 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic void snd_sonicvibes_setfmt(struct sonicvibes * sonic, 4498c2ecf20Sopenharmony_ci unsigned char mask, 4508c2ecf20Sopenharmony_ci unsigned char value) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci unsigned long flags; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci spin_lock_irqsave(&sonic->reg_lock, flags); 4558c2ecf20Sopenharmony_ci outb(SV_MCE | SV_IREG_DMA_DATA_FMT, SV_REG(sonic, INDEX)); 4568c2ecf20Sopenharmony_ci if (mask) { 4578c2ecf20Sopenharmony_ci sonic->format = inb(SV_REG(sonic, DATA)); 4588c2ecf20Sopenharmony_ci udelay(10); 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci sonic->format = (sonic->format & mask) | value; 4618c2ecf20Sopenharmony_ci outb(sonic->format, SV_REG(sonic, DATA)); 4628c2ecf20Sopenharmony_ci udelay(10); 4638c2ecf20Sopenharmony_ci outb(0, SV_REG(sonic, INDEX)); 4648c2ecf20Sopenharmony_ci udelay(10); 4658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sonic->reg_lock, flags); 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic void snd_sonicvibes_pll(unsigned int rate, 4698c2ecf20Sopenharmony_ci unsigned int *res_r, 4708c2ecf20Sopenharmony_ci unsigned int *res_m, 4718c2ecf20Sopenharmony_ci unsigned int *res_n) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci unsigned int r, m = 0, n = 0; 4748c2ecf20Sopenharmony_ci unsigned int xm, xn, xr, xd, metric = ~0U; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (rate < 625000 / SV_ADCMULT) 4778c2ecf20Sopenharmony_ci rate = 625000 / SV_ADCMULT; 4788c2ecf20Sopenharmony_ci if (rate > 150000000 / SV_ADCMULT) 4798c2ecf20Sopenharmony_ci rate = 150000000 / SV_ADCMULT; 4808c2ecf20Sopenharmony_ci /* slight violation of specs, needed for continuous sampling rates */ 4818c2ecf20Sopenharmony_ci for (r = 0; rate < 75000000 / SV_ADCMULT; r += 0x20, rate <<= 1); 4828c2ecf20Sopenharmony_ci for (xn = 3; xn < 33; xn++) /* 35 */ 4838c2ecf20Sopenharmony_ci for (xm = 3; xm < 257; xm++) { 4848c2ecf20Sopenharmony_ci xr = ((SV_REFFREQUENCY / SV_ADCMULT) * xm) / xn; 4858c2ecf20Sopenharmony_ci if (xr >= rate) 4868c2ecf20Sopenharmony_ci xd = xr - rate; 4878c2ecf20Sopenharmony_ci else 4888c2ecf20Sopenharmony_ci xd = rate - xr; 4898c2ecf20Sopenharmony_ci if (xd < metric) { 4908c2ecf20Sopenharmony_ci metric = xd; 4918c2ecf20Sopenharmony_ci m = xm - 2; 4928c2ecf20Sopenharmony_ci n = xn - 2; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci *res_r = r; 4968c2ecf20Sopenharmony_ci *res_m = m; 4978c2ecf20Sopenharmony_ci *res_n = n; 4988c2ecf20Sopenharmony_ci#if 0 4998c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 5008c2ecf20Sopenharmony_ci "metric = %i, xm = %i, xn = %i\n", metric, xm, xn); 5018c2ecf20Sopenharmony_ci dev_dbg(sonic->card->dev, 5028c2ecf20Sopenharmony_ci "pll: m = 0x%x, r = 0x%x, n = 0x%x\n", reg, m, r, n); 5038c2ecf20Sopenharmony_ci#endif 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic void snd_sonicvibes_setpll(struct sonicvibes * sonic, 5078c2ecf20Sopenharmony_ci unsigned char reg, 5088c2ecf20Sopenharmony_ci unsigned int rate) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci unsigned long flags; 5118c2ecf20Sopenharmony_ci unsigned int r, m, n; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci snd_sonicvibes_pll(rate, &r, &m, &n); 5148c2ecf20Sopenharmony_ci if (sonic != NULL) { 5158c2ecf20Sopenharmony_ci spin_lock_irqsave(&sonic->reg_lock, flags); 5168c2ecf20Sopenharmony_ci snd_sonicvibes_out1(sonic, reg, m); 5178c2ecf20Sopenharmony_ci snd_sonicvibes_out1(sonic, reg + 1, r | n); 5188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sonic->reg_lock, flags); 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic void snd_sonicvibes_set_adc_rate(struct sonicvibes * sonic, unsigned int rate) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci unsigned long flags; 5258c2ecf20Sopenharmony_ci unsigned int div; 5268c2ecf20Sopenharmony_ci unsigned char clock; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci div = 48000 / rate; 5298c2ecf20Sopenharmony_ci if (div > 8) 5308c2ecf20Sopenharmony_ci div = 8; 5318c2ecf20Sopenharmony_ci if ((48000 / div) == rate) { /* use the alternate clock */ 5328c2ecf20Sopenharmony_ci clock = 0x10; 5338c2ecf20Sopenharmony_ci } else { /* use the PLL source */ 5348c2ecf20Sopenharmony_ci clock = 0x00; 5358c2ecf20Sopenharmony_ci snd_sonicvibes_setpll(sonic, SV_IREG_ADC_PLL, rate); 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci spin_lock_irqsave(&sonic->reg_lock, flags); 5388c2ecf20Sopenharmony_ci snd_sonicvibes_out1(sonic, SV_IREG_ADC_ALT_RATE, (div - 1) << 4); 5398c2ecf20Sopenharmony_ci snd_sonicvibes_out1(sonic, SV_IREG_ADC_CLOCK, clock); 5408c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sonic->reg_lock, flags); 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic int snd_sonicvibes_hw_constraint_dac_rate(struct snd_pcm_hw_params *params, 5448c2ecf20Sopenharmony_ci struct snd_pcm_hw_rule *rule) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci unsigned int rate, div, r, m, n; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min == 5498c2ecf20Sopenharmony_ci hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->max) { 5508c2ecf20Sopenharmony_ci rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min; 5518c2ecf20Sopenharmony_ci div = 48000 / rate; 5528c2ecf20Sopenharmony_ci if (div > 8) 5538c2ecf20Sopenharmony_ci div = 8; 5548c2ecf20Sopenharmony_ci if ((48000 / div) == rate) { 5558c2ecf20Sopenharmony_ci params->rate_num = rate; 5568c2ecf20Sopenharmony_ci params->rate_den = 1; 5578c2ecf20Sopenharmony_ci } else { 5588c2ecf20Sopenharmony_ci snd_sonicvibes_pll(rate, &r, &m, &n); 5598c2ecf20Sopenharmony_ci snd_BUG_ON(SV_REFFREQUENCY % 16); 5608c2ecf20Sopenharmony_ci snd_BUG_ON(SV_ADCMULT % 512); 5618c2ecf20Sopenharmony_ci params->rate_num = (SV_REFFREQUENCY/16) * (n+2) * r; 5628c2ecf20Sopenharmony_ci params->rate_den = (SV_ADCMULT/512) * (m+2); 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci return 0; 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic void snd_sonicvibes_set_dac_rate(struct sonicvibes * sonic, unsigned int rate) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci unsigned int div; 5718c2ecf20Sopenharmony_ci unsigned long flags; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci div = (rate * 65536 + SV_FULLRATE / 2) / SV_FULLRATE; 5748c2ecf20Sopenharmony_ci if (div > 65535) 5758c2ecf20Sopenharmony_ci div = 65535; 5768c2ecf20Sopenharmony_ci spin_lock_irqsave(&sonic->reg_lock, flags); 5778c2ecf20Sopenharmony_ci snd_sonicvibes_out1(sonic, SV_IREG_PCM_RATE_HIGH, div >> 8); 5788c2ecf20Sopenharmony_ci snd_sonicvibes_out1(sonic, SV_IREG_PCM_RATE_LOW, div); 5798c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sonic->reg_lock, flags); 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_cistatic int snd_sonicvibes_trigger(struct sonicvibes * sonic, int what, int cmd) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci int result = 0; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci spin_lock(&sonic->reg_lock); 5878c2ecf20Sopenharmony_ci if (cmd == SNDRV_PCM_TRIGGER_START) { 5888c2ecf20Sopenharmony_ci if (!(sonic->enable & what)) { 5898c2ecf20Sopenharmony_ci sonic->enable |= what; 5908c2ecf20Sopenharmony_ci snd_sonicvibes_out1(sonic, SV_IREG_PC_ENABLE, sonic->enable); 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { 5938c2ecf20Sopenharmony_ci if (sonic->enable & what) { 5948c2ecf20Sopenharmony_ci sonic->enable &= ~what; 5958c2ecf20Sopenharmony_ci snd_sonicvibes_out1(sonic, SV_IREG_PC_ENABLE, sonic->enable); 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci } else { 5988c2ecf20Sopenharmony_ci result = -EINVAL; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci spin_unlock(&sonic->reg_lock); 6018c2ecf20Sopenharmony_ci return result; 6028c2ecf20Sopenharmony_ci} 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_cistatic irqreturn_t snd_sonicvibes_interrupt(int irq, void *dev_id) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci struct sonicvibes *sonic = dev_id; 6078c2ecf20Sopenharmony_ci unsigned char status; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci status = inb(SV_REG(sonic, STATUS)); 6108c2ecf20Sopenharmony_ci if (!(status & (SV_DMAA_IRQ | SV_DMAC_IRQ | SV_MIDI_IRQ))) 6118c2ecf20Sopenharmony_ci return IRQ_NONE; 6128c2ecf20Sopenharmony_ci if (status == 0xff) { /* failure */ 6138c2ecf20Sopenharmony_ci outb(sonic->irqmask = ~0, SV_REG(sonic, IRQMASK)); 6148c2ecf20Sopenharmony_ci dev_err(sonic->card->dev, 6158c2ecf20Sopenharmony_ci "IRQ failure - interrupts disabled!!\n"); 6168c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci if (sonic->pcm) { 6198c2ecf20Sopenharmony_ci if (status & SV_DMAA_IRQ) 6208c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(sonic->playback_substream); 6218c2ecf20Sopenharmony_ci if (status & SV_DMAC_IRQ) 6228c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(sonic->capture_substream); 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci if (sonic->rmidi) { 6258c2ecf20Sopenharmony_ci if (status & SV_MIDI_IRQ) 6268c2ecf20Sopenharmony_ci snd_mpu401_uart_interrupt(irq, sonic->rmidi->private_data); 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci if (status & SV_UD_IRQ) { 6298c2ecf20Sopenharmony_ci unsigned char udreg; 6308c2ecf20Sopenharmony_ci int vol, oleft, oright, mleft, mright; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci spin_lock(&sonic->reg_lock); 6338c2ecf20Sopenharmony_ci udreg = snd_sonicvibes_in1(sonic, SV_IREG_UD_BUTTON); 6348c2ecf20Sopenharmony_ci vol = udreg & 0x3f; 6358c2ecf20Sopenharmony_ci if (!(udreg & 0x40)) 6368c2ecf20Sopenharmony_ci vol = -vol; 6378c2ecf20Sopenharmony_ci oleft = mleft = snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ANALOG); 6388c2ecf20Sopenharmony_ci oright = mright = snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ANALOG); 6398c2ecf20Sopenharmony_ci oleft &= 0x1f; 6408c2ecf20Sopenharmony_ci oright &= 0x1f; 6418c2ecf20Sopenharmony_ci oleft += vol; 6428c2ecf20Sopenharmony_ci if (oleft < 0) 6438c2ecf20Sopenharmony_ci oleft = 0; 6448c2ecf20Sopenharmony_ci if (oleft > 0x1f) 6458c2ecf20Sopenharmony_ci oleft = 0x1f; 6468c2ecf20Sopenharmony_ci oright += vol; 6478c2ecf20Sopenharmony_ci if (oright < 0) 6488c2ecf20Sopenharmony_ci oright = 0; 6498c2ecf20Sopenharmony_ci if (oright > 0x1f) 6508c2ecf20Sopenharmony_ci oright = 0x1f; 6518c2ecf20Sopenharmony_ci if (udreg & 0x80) { 6528c2ecf20Sopenharmony_ci mleft ^= 0x80; 6538c2ecf20Sopenharmony_ci mright ^= 0x80; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci oleft |= mleft & 0x80; 6568c2ecf20Sopenharmony_ci oright |= mright & 0x80; 6578c2ecf20Sopenharmony_ci snd_sonicvibes_out1(sonic, SV_IREG_LEFT_ANALOG, oleft); 6588c2ecf20Sopenharmony_ci snd_sonicvibes_out1(sonic, SV_IREG_RIGHT_ANALOG, oright); 6598c2ecf20Sopenharmony_ci spin_unlock(&sonic->reg_lock); 6608c2ecf20Sopenharmony_ci snd_ctl_notify(sonic->card, SNDRV_CTL_EVENT_MASK_VALUE, &sonic->master_mute->id); 6618c2ecf20Sopenharmony_ci snd_ctl_notify(sonic->card, SNDRV_CTL_EVENT_MASK_VALUE, &sonic->master_volume->id); 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci/* 6678c2ecf20Sopenharmony_ci * PCM part 6688c2ecf20Sopenharmony_ci */ 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cistatic int snd_sonicvibes_playback_trigger(struct snd_pcm_substream *substream, 6718c2ecf20Sopenharmony_ci int cmd) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci struct sonicvibes *sonic = snd_pcm_substream_chip(substream); 6748c2ecf20Sopenharmony_ci return snd_sonicvibes_trigger(sonic, 1, cmd); 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_cistatic int snd_sonicvibes_capture_trigger(struct snd_pcm_substream *substream, 6788c2ecf20Sopenharmony_ci int cmd) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci struct sonicvibes *sonic = snd_pcm_substream_chip(substream); 6818c2ecf20Sopenharmony_ci return snd_sonicvibes_trigger(sonic, 2, cmd); 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cistatic int snd_sonicvibes_playback_prepare(struct snd_pcm_substream *substream) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci struct sonicvibes *sonic = snd_pcm_substream_chip(substream); 6878c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 6888c2ecf20Sopenharmony_ci unsigned char fmt = 0; 6898c2ecf20Sopenharmony_ci unsigned int size = snd_pcm_lib_buffer_bytes(substream); 6908c2ecf20Sopenharmony_ci unsigned int count = snd_pcm_lib_period_bytes(substream); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci sonic->p_dma_size = size; 6938c2ecf20Sopenharmony_ci count--; 6948c2ecf20Sopenharmony_ci if (runtime->channels > 1) 6958c2ecf20Sopenharmony_ci fmt |= 1; 6968c2ecf20Sopenharmony_ci if (snd_pcm_format_width(runtime->format) == 16) 6978c2ecf20Sopenharmony_ci fmt |= 2; 6988c2ecf20Sopenharmony_ci snd_sonicvibes_setfmt(sonic, ~3, fmt); 6998c2ecf20Sopenharmony_ci snd_sonicvibes_set_dac_rate(sonic, runtime->rate); 7008c2ecf20Sopenharmony_ci spin_lock_irq(&sonic->reg_lock); 7018c2ecf20Sopenharmony_ci snd_sonicvibes_setdmaa(sonic, runtime->dma_addr, size); 7028c2ecf20Sopenharmony_ci snd_sonicvibes_out1(sonic, SV_IREG_DMA_A_UPPER, count >> 8); 7038c2ecf20Sopenharmony_ci snd_sonicvibes_out1(sonic, SV_IREG_DMA_A_LOWER, count); 7048c2ecf20Sopenharmony_ci spin_unlock_irq(&sonic->reg_lock); 7058c2ecf20Sopenharmony_ci return 0; 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cistatic int snd_sonicvibes_capture_prepare(struct snd_pcm_substream *substream) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci struct sonicvibes *sonic = snd_pcm_substream_chip(substream); 7118c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 7128c2ecf20Sopenharmony_ci unsigned char fmt = 0; 7138c2ecf20Sopenharmony_ci unsigned int size = snd_pcm_lib_buffer_bytes(substream); 7148c2ecf20Sopenharmony_ci unsigned int count = snd_pcm_lib_period_bytes(substream); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci sonic->c_dma_size = size; 7178c2ecf20Sopenharmony_ci count >>= 1; 7188c2ecf20Sopenharmony_ci count--; 7198c2ecf20Sopenharmony_ci if (runtime->channels > 1) 7208c2ecf20Sopenharmony_ci fmt |= 0x10; 7218c2ecf20Sopenharmony_ci if (snd_pcm_format_width(runtime->format) == 16) 7228c2ecf20Sopenharmony_ci fmt |= 0x20; 7238c2ecf20Sopenharmony_ci snd_sonicvibes_setfmt(sonic, ~0x30, fmt); 7248c2ecf20Sopenharmony_ci snd_sonicvibes_set_adc_rate(sonic, runtime->rate); 7258c2ecf20Sopenharmony_ci spin_lock_irq(&sonic->reg_lock); 7268c2ecf20Sopenharmony_ci snd_sonicvibes_setdmac(sonic, runtime->dma_addr, size); 7278c2ecf20Sopenharmony_ci snd_sonicvibes_out1(sonic, SV_IREG_DMA_C_UPPER, count >> 8); 7288c2ecf20Sopenharmony_ci snd_sonicvibes_out1(sonic, SV_IREG_DMA_C_LOWER, count); 7298c2ecf20Sopenharmony_ci spin_unlock_irq(&sonic->reg_lock); 7308c2ecf20Sopenharmony_ci return 0; 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_sonicvibes_playback_pointer(struct snd_pcm_substream *substream) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci struct sonicvibes *sonic = snd_pcm_substream_chip(substream); 7368c2ecf20Sopenharmony_ci size_t ptr; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci if (!(sonic->enable & 1)) 7398c2ecf20Sopenharmony_ci return 0; 7408c2ecf20Sopenharmony_ci ptr = sonic->p_dma_size - snd_sonicvibes_getdmaa(sonic); 7418c2ecf20Sopenharmony_ci return bytes_to_frames(substream->runtime, ptr); 7428c2ecf20Sopenharmony_ci} 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_sonicvibes_capture_pointer(struct snd_pcm_substream *substream) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci struct sonicvibes *sonic = snd_pcm_substream_chip(substream); 7478c2ecf20Sopenharmony_ci size_t ptr; 7488c2ecf20Sopenharmony_ci if (!(sonic->enable & 2)) 7498c2ecf20Sopenharmony_ci return 0; 7508c2ecf20Sopenharmony_ci ptr = sonic->c_dma_size - snd_sonicvibes_getdmac(sonic); 7518c2ecf20Sopenharmony_ci return bytes_to_frames(substream->runtime, ptr); 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_sonicvibes_playback = 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 7578c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 7588c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 7598c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 7608c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 7618c2ecf20Sopenharmony_ci .rate_min = 4000, 7628c2ecf20Sopenharmony_ci .rate_max = 48000, 7638c2ecf20Sopenharmony_ci .channels_min = 1, 7648c2ecf20Sopenharmony_ci .channels_max = 2, 7658c2ecf20Sopenharmony_ci .buffer_bytes_max = (128*1024), 7668c2ecf20Sopenharmony_ci .period_bytes_min = 32, 7678c2ecf20Sopenharmony_ci .period_bytes_max = (128*1024), 7688c2ecf20Sopenharmony_ci .periods_min = 1, 7698c2ecf20Sopenharmony_ci .periods_max = 1024, 7708c2ecf20Sopenharmony_ci .fifo_size = 0, 7718c2ecf20Sopenharmony_ci}; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_sonicvibes_capture = 7748c2ecf20Sopenharmony_ci{ 7758c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 7768c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 7778c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 7788c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 7798c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 7808c2ecf20Sopenharmony_ci .rate_min = 4000, 7818c2ecf20Sopenharmony_ci .rate_max = 48000, 7828c2ecf20Sopenharmony_ci .channels_min = 1, 7838c2ecf20Sopenharmony_ci .channels_max = 2, 7848c2ecf20Sopenharmony_ci .buffer_bytes_max = (128*1024), 7858c2ecf20Sopenharmony_ci .period_bytes_min = 32, 7868c2ecf20Sopenharmony_ci .period_bytes_max = (128*1024), 7878c2ecf20Sopenharmony_ci .periods_min = 1, 7888c2ecf20Sopenharmony_ci .periods_max = 1024, 7898c2ecf20Sopenharmony_ci .fifo_size = 0, 7908c2ecf20Sopenharmony_ci}; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_cistatic int snd_sonicvibes_playback_open(struct snd_pcm_substream *substream) 7938c2ecf20Sopenharmony_ci{ 7948c2ecf20Sopenharmony_ci struct sonicvibes *sonic = snd_pcm_substream_chip(substream); 7958c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci sonic->mode |= SV_MODE_PLAY; 7988c2ecf20Sopenharmony_ci sonic->playback_substream = substream; 7998c2ecf20Sopenharmony_ci runtime->hw = snd_sonicvibes_playback; 8008c2ecf20Sopenharmony_ci snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, snd_sonicvibes_hw_constraint_dac_rate, NULL, SNDRV_PCM_HW_PARAM_RATE, -1); 8018c2ecf20Sopenharmony_ci return 0; 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_cistatic int snd_sonicvibes_capture_open(struct snd_pcm_substream *substream) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci struct sonicvibes *sonic = snd_pcm_substream_chip(substream); 8078c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci sonic->mode |= SV_MODE_CAPTURE; 8108c2ecf20Sopenharmony_ci sonic->capture_substream = substream; 8118c2ecf20Sopenharmony_ci runtime->hw = snd_sonicvibes_capture; 8128c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 8138c2ecf20Sopenharmony_ci &snd_sonicvibes_hw_constraints_adc_clock); 8148c2ecf20Sopenharmony_ci return 0; 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_cistatic int snd_sonicvibes_playback_close(struct snd_pcm_substream *substream) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci struct sonicvibes *sonic = snd_pcm_substream_chip(substream); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci sonic->playback_substream = NULL; 8228c2ecf20Sopenharmony_ci sonic->mode &= ~SV_MODE_PLAY; 8238c2ecf20Sopenharmony_ci return 0; 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cistatic int snd_sonicvibes_capture_close(struct snd_pcm_substream *substream) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci struct sonicvibes *sonic = snd_pcm_substream_chip(substream); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci sonic->capture_substream = NULL; 8318c2ecf20Sopenharmony_ci sonic->mode &= ~SV_MODE_CAPTURE; 8328c2ecf20Sopenharmony_ci return 0; 8338c2ecf20Sopenharmony_ci} 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_sonicvibes_playback_ops = { 8368c2ecf20Sopenharmony_ci .open = snd_sonicvibes_playback_open, 8378c2ecf20Sopenharmony_ci .close = snd_sonicvibes_playback_close, 8388c2ecf20Sopenharmony_ci .prepare = snd_sonicvibes_playback_prepare, 8398c2ecf20Sopenharmony_ci .trigger = snd_sonicvibes_playback_trigger, 8408c2ecf20Sopenharmony_ci .pointer = snd_sonicvibes_playback_pointer, 8418c2ecf20Sopenharmony_ci}; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_sonicvibes_capture_ops = { 8448c2ecf20Sopenharmony_ci .open = snd_sonicvibes_capture_open, 8458c2ecf20Sopenharmony_ci .close = snd_sonicvibes_capture_close, 8468c2ecf20Sopenharmony_ci .prepare = snd_sonicvibes_capture_prepare, 8478c2ecf20Sopenharmony_ci .trigger = snd_sonicvibes_capture_trigger, 8488c2ecf20Sopenharmony_ci .pointer = snd_sonicvibes_capture_pointer, 8498c2ecf20Sopenharmony_ci}; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_cistatic int snd_sonicvibes_pcm(struct sonicvibes *sonic, int device) 8528c2ecf20Sopenharmony_ci{ 8538c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 8548c2ecf20Sopenharmony_ci int err; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci if ((err = snd_pcm_new(sonic->card, "s3_86c617", device, 1, 1, &pcm)) < 0) 8578c2ecf20Sopenharmony_ci return err; 8588c2ecf20Sopenharmony_ci if (snd_BUG_ON(!pcm)) 8598c2ecf20Sopenharmony_ci return -EINVAL; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sonicvibes_playback_ops); 8628c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sonicvibes_capture_ops); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci pcm->private_data = sonic; 8658c2ecf20Sopenharmony_ci pcm->info_flags = 0; 8668c2ecf20Sopenharmony_ci strcpy(pcm->name, "S3 SonicVibes"); 8678c2ecf20Sopenharmony_ci sonic->pcm = pcm; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 8708c2ecf20Sopenharmony_ci &sonic->pci->dev, 64*1024, 128*1024); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci return 0; 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci/* 8768c2ecf20Sopenharmony_ci * Mixer part 8778c2ecf20Sopenharmony_ci */ 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci#define SONICVIBES_MUX(xname, xindex) \ 8808c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 8818c2ecf20Sopenharmony_ci .info = snd_sonicvibes_info_mux, \ 8828c2ecf20Sopenharmony_ci .get = snd_sonicvibes_get_mux, .put = snd_sonicvibes_put_mux } 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_cistatic int snd_sonicvibes_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci static const char * const texts[7] = { 8878c2ecf20Sopenharmony_ci "CD", "PCM", "Aux1", "Line", "Aux0", "Mic", "Mix" 8888c2ecf20Sopenharmony_ci }; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 2, 7, texts); 8918c2ecf20Sopenharmony_ci} 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_cistatic int snd_sonicvibes_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 8948c2ecf20Sopenharmony_ci{ 8958c2ecf20Sopenharmony_ci struct sonicvibes *sonic = snd_kcontrol_chip(kcontrol); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci spin_lock_irq(&sonic->reg_lock); 8988c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = ((snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ADC) & SV_RECSRC_OUT) >> 5) - 1; 8998c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[1] = ((snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ADC) & SV_RECSRC_OUT) >> 5) - 1; 9008c2ecf20Sopenharmony_ci spin_unlock_irq(&sonic->reg_lock); 9018c2ecf20Sopenharmony_ci return 0; 9028c2ecf20Sopenharmony_ci} 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_cistatic int snd_sonicvibes_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 9058c2ecf20Sopenharmony_ci{ 9068c2ecf20Sopenharmony_ci struct sonicvibes *sonic = snd_kcontrol_chip(kcontrol); 9078c2ecf20Sopenharmony_ci unsigned short left, right, oval1, oval2; 9088c2ecf20Sopenharmony_ci int change; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci if (ucontrol->value.enumerated.item[0] >= 7 || 9118c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[1] >= 7) 9128c2ecf20Sopenharmony_ci return -EINVAL; 9138c2ecf20Sopenharmony_ci left = (ucontrol->value.enumerated.item[0] + 1) << 5; 9148c2ecf20Sopenharmony_ci right = (ucontrol->value.enumerated.item[1] + 1) << 5; 9158c2ecf20Sopenharmony_ci spin_lock_irq(&sonic->reg_lock); 9168c2ecf20Sopenharmony_ci oval1 = snd_sonicvibes_in1(sonic, SV_IREG_LEFT_ADC); 9178c2ecf20Sopenharmony_ci oval2 = snd_sonicvibes_in1(sonic, SV_IREG_RIGHT_ADC); 9188c2ecf20Sopenharmony_ci left = (oval1 & ~SV_RECSRC_OUT) | left; 9198c2ecf20Sopenharmony_ci right = (oval2 & ~SV_RECSRC_OUT) | right; 9208c2ecf20Sopenharmony_ci change = left != oval1 || right != oval2; 9218c2ecf20Sopenharmony_ci snd_sonicvibes_out1(sonic, SV_IREG_LEFT_ADC, left); 9228c2ecf20Sopenharmony_ci snd_sonicvibes_out1(sonic, SV_IREG_RIGHT_ADC, right); 9238c2ecf20Sopenharmony_ci spin_unlock_irq(&sonic->reg_lock); 9248c2ecf20Sopenharmony_ci return change; 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci#define SONICVIBES_SINGLE(xname, xindex, reg, shift, mask, invert) \ 9288c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 9298c2ecf20Sopenharmony_ci .info = snd_sonicvibes_info_single, \ 9308c2ecf20Sopenharmony_ci .get = snd_sonicvibes_get_single, .put = snd_sonicvibes_put_single, \ 9318c2ecf20Sopenharmony_ci .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_cistatic int snd_sonicvibes_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 9388c2ecf20Sopenharmony_ci uinfo->count = 1; 9398c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 9408c2ecf20Sopenharmony_ci uinfo->value.integer.max = mask; 9418c2ecf20Sopenharmony_ci return 0; 9428c2ecf20Sopenharmony_ci} 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_cistatic int snd_sonicvibes_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 9458c2ecf20Sopenharmony_ci{ 9468c2ecf20Sopenharmony_ci struct sonicvibes *sonic = snd_kcontrol_chip(kcontrol); 9478c2ecf20Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 9488c2ecf20Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 9498c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 9508c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci spin_lock_irq(&sonic->reg_lock); 9538c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = (snd_sonicvibes_in1(sonic, reg)>> shift) & mask; 9548c2ecf20Sopenharmony_ci spin_unlock_irq(&sonic->reg_lock); 9558c2ecf20Sopenharmony_ci if (invert) 9568c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 9578c2ecf20Sopenharmony_ci return 0; 9588c2ecf20Sopenharmony_ci} 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_cistatic int snd_sonicvibes_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 9618c2ecf20Sopenharmony_ci{ 9628c2ecf20Sopenharmony_ci struct sonicvibes *sonic = snd_kcontrol_chip(kcontrol); 9638c2ecf20Sopenharmony_ci int reg = kcontrol->private_value & 0xff; 9648c2ecf20Sopenharmony_ci int shift = (kcontrol->private_value >> 8) & 0xff; 9658c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 16) & 0xff; 9668c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 24) & 0xff; 9678c2ecf20Sopenharmony_ci int change; 9688c2ecf20Sopenharmony_ci unsigned short val, oval; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci val = (ucontrol->value.integer.value[0] & mask); 9718c2ecf20Sopenharmony_ci if (invert) 9728c2ecf20Sopenharmony_ci val = mask - val; 9738c2ecf20Sopenharmony_ci val <<= shift; 9748c2ecf20Sopenharmony_ci spin_lock_irq(&sonic->reg_lock); 9758c2ecf20Sopenharmony_ci oval = snd_sonicvibes_in1(sonic, reg); 9768c2ecf20Sopenharmony_ci val = (oval & ~(mask << shift)) | val; 9778c2ecf20Sopenharmony_ci change = val != oval; 9788c2ecf20Sopenharmony_ci snd_sonicvibes_out1(sonic, reg, val); 9798c2ecf20Sopenharmony_ci spin_unlock_irq(&sonic->reg_lock); 9808c2ecf20Sopenharmony_ci return change; 9818c2ecf20Sopenharmony_ci} 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci#define SONICVIBES_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ 9848c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 9858c2ecf20Sopenharmony_ci .info = snd_sonicvibes_info_double, \ 9868c2ecf20Sopenharmony_ci .get = snd_sonicvibes_get_double, .put = snd_sonicvibes_put_double, \ 9878c2ecf20Sopenharmony_ci .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_cistatic int snd_sonicvibes_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 9948c2ecf20Sopenharmony_ci uinfo->count = 2; 9958c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 9968c2ecf20Sopenharmony_ci uinfo->value.integer.max = mask; 9978c2ecf20Sopenharmony_ci return 0; 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_cistatic int snd_sonicvibes_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 10018c2ecf20Sopenharmony_ci{ 10028c2ecf20Sopenharmony_ci struct sonicvibes *sonic = snd_kcontrol_chip(kcontrol); 10038c2ecf20Sopenharmony_ci int left_reg = kcontrol->private_value & 0xff; 10048c2ecf20Sopenharmony_ci int right_reg = (kcontrol->private_value >> 8) & 0xff; 10058c2ecf20Sopenharmony_ci int shift_left = (kcontrol->private_value >> 16) & 0x07; 10068c2ecf20Sopenharmony_ci int shift_right = (kcontrol->private_value >> 19) & 0x07; 10078c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 10088c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 22) & 1; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci spin_lock_irq(&sonic->reg_lock); 10118c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = (snd_sonicvibes_in1(sonic, left_reg) >> shift_left) & mask; 10128c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = (snd_sonicvibes_in1(sonic, right_reg) >> shift_right) & mask; 10138c2ecf20Sopenharmony_ci spin_unlock_irq(&sonic->reg_lock); 10148c2ecf20Sopenharmony_ci if (invert) { 10158c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; 10168c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; 10178c2ecf20Sopenharmony_ci } 10188c2ecf20Sopenharmony_ci return 0; 10198c2ecf20Sopenharmony_ci} 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_cistatic int snd_sonicvibes_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci struct sonicvibes *sonic = snd_kcontrol_chip(kcontrol); 10248c2ecf20Sopenharmony_ci int left_reg = kcontrol->private_value & 0xff; 10258c2ecf20Sopenharmony_ci int right_reg = (kcontrol->private_value >> 8) & 0xff; 10268c2ecf20Sopenharmony_ci int shift_left = (kcontrol->private_value >> 16) & 0x07; 10278c2ecf20Sopenharmony_ci int shift_right = (kcontrol->private_value >> 19) & 0x07; 10288c2ecf20Sopenharmony_ci int mask = (kcontrol->private_value >> 24) & 0xff; 10298c2ecf20Sopenharmony_ci int invert = (kcontrol->private_value >> 22) & 1; 10308c2ecf20Sopenharmony_ci int change; 10318c2ecf20Sopenharmony_ci unsigned short val1, val2, oval1, oval2; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci val1 = ucontrol->value.integer.value[0] & mask; 10348c2ecf20Sopenharmony_ci val2 = ucontrol->value.integer.value[1] & mask; 10358c2ecf20Sopenharmony_ci if (invert) { 10368c2ecf20Sopenharmony_ci val1 = mask - val1; 10378c2ecf20Sopenharmony_ci val2 = mask - val2; 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci val1 <<= shift_left; 10408c2ecf20Sopenharmony_ci val2 <<= shift_right; 10418c2ecf20Sopenharmony_ci spin_lock_irq(&sonic->reg_lock); 10428c2ecf20Sopenharmony_ci oval1 = snd_sonicvibes_in1(sonic, left_reg); 10438c2ecf20Sopenharmony_ci oval2 = snd_sonicvibes_in1(sonic, right_reg); 10448c2ecf20Sopenharmony_ci val1 = (oval1 & ~(mask << shift_left)) | val1; 10458c2ecf20Sopenharmony_ci val2 = (oval2 & ~(mask << shift_right)) | val2; 10468c2ecf20Sopenharmony_ci change = val1 != oval1 || val2 != oval2; 10478c2ecf20Sopenharmony_ci snd_sonicvibes_out1(sonic, left_reg, val1); 10488c2ecf20Sopenharmony_ci snd_sonicvibes_out1(sonic, right_reg, val2); 10498c2ecf20Sopenharmony_ci spin_unlock_irq(&sonic->reg_lock); 10508c2ecf20Sopenharmony_ci return change; 10518c2ecf20Sopenharmony_ci} 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_sonicvibes_controls[] = { 10548c2ecf20Sopenharmony_ciSONICVIBES_DOUBLE("Capture Volume", 0, SV_IREG_LEFT_ADC, SV_IREG_RIGHT_ADC, 0, 0, 15, 0), 10558c2ecf20Sopenharmony_ciSONICVIBES_DOUBLE("Aux Playback Switch", 0, SV_IREG_LEFT_AUX1, SV_IREG_RIGHT_AUX1, 7, 7, 1, 1), 10568c2ecf20Sopenharmony_ciSONICVIBES_DOUBLE("Aux Playback Volume", 0, SV_IREG_LEFT_AUX1, SV_IREG_RIGHT_AUX1, 0, 0, 31, 1), 10578c2ecf20Sopenharmony_ciSONICVIBES_DOUBLE("CD Playback Switch", 0, SV_IREG_LEFT_CD, SV_IREG_RIGHT_CD, 7, 7, 1, 1), 10588c2ecf20Sopenharmony_ciSONICVIBES_DOUBLE("CD Playback Volume", 0, SV_IREG_LEFT_CD, SV_IREG_RIGHT_CD, 0, 0, 31, 1), 10598c2ecf20Sopenharmony_ciSONICVIBES_DOUBLE("Line Playback Switch", 0, SV_IREG_LEFT_LINE, SV_IREG_RIGHT_LINE, 7, 7, 1, 1), 10608c2ecf20Sopenharmony_ciSONICVIBES_DOUBLE("Line Playback Volume", 0, SV_IREG_LEFT_LINE, SV_IREG_RIGHT_LINE, 0, 0, 31, 1), 10618c2ecf20Sopenharmony_ciSONICVIBES_SINGLE("Mic Playback Switch", 0, SV_IREG_MIC, 7, 1, 1), 10628c2ecf20Sopenharmony_ciSONICVIBES_SINGLE("Mic Playback Volume", 0, SV_IREG_MIC, 0, 15, 1), 10638c2ecf20Sopenharmony_ciSONICVIBES_SINGLE("Mic Boost", 0, SV_IREG_LEFT_ADC, 4, 1, 0), 10648c2ecf20Sopenharmony_ciSONICVIBES_DOUBLE("Synth Playback Switch", 0, SV_IREG_LEFT_SYNTH, SV_IREG_RIGHT_SYNTH, 7, 7, 1, 1), 10658c2ecf20Sopenharmony_ciSONICVIBES_DOUBLE("Synth Playback Volume", 0, SV_IREG_LEFT_SYNTH, SV_IREG_RIGHT_SYNTH, 0, 0, 31, 1), 10668c2ecf20Sopenharmony_ciSONICVIBES_DOUBLE("Aux Playback Switch", 1, SV_IREG_LEFT_AUX2, SV_IREG_RIGHT_AUX2, 7, 7, 1, 1), 10678c2ecf20Sopenharmony_ciSONICVIBES_DOUBLE("Aux Playback Volume", 1, SV_IREG_LEFT_AUX2, SV_IREG_RIGHT_AUX2, 0, 0, 31, 1), 10688c2ecf20Sopenharmony_ciSONICVIBES_DOUBLE("Master Playback Switch", 0, SV_IREG_LEFT_ANALOG, SV_IREG_RIGHT_ANALOG, 7, 7, 1, 1), 10698c2ecf20Sopenharmony_ciSONICVIBES_DOUBLE("Master Playback Volume", 0, SV_IREG_LEFT_ANALOG, SV_IREG_RIGHT_ANALOG, 0, 0, 31, 1), 10708c2ecf20Sopenharmony_ciSONICVIBES_DOUBLE("PCM Playback Switch", 0, SV_IREG_LEFT_PCM, SV_IREG_RIGHT_PCM, 7, 7, 1, 1), 10718c2ecf20Sopenharmony_ciSONICVIBES_DOUBLE("PCM Playback Volume", 0, SV_IREG_LEFT_PCM, SV_IREG_RIGHT_PCM, 0, 0, 63, 1), 10728c2ecf20Sopenharmony_ciSONICVIBES_SINGLE("Loopback Capture Switch", 0, SV_IREG_ADC_OUTPUT_CTRL, 0, 1, 0), 10738c2ecf20Sopenharmony_ciSONICVIBES_SINGLE("Loopback Capture Volume", 0, SV_IREG_ADC_OUTPUT_CTRL, 2, 63, 1), 10748c2ecf20Sopenharmony_ciSONICVIBES_MUX("Capture Source", 0) 10758c2ecf20Sopenharmony_ci}; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_cistatic void snd_sonicvibes_master_free(struct snd_kcontrol *kcontrol) 10788c2ecf20Sopenharmony_ci{ 10798c2ecf20Sopenharmony_ci struct sonicvibes *sonic = snd_kcontrol_chip(kcontrol); 10808c2ecf20Sopenharmony_ci sonic->master_mute = NULL; 10818c2ecf20Sopenharmony_ci sonic->master_volume = NULL; 10828c2ecf20Sopenharmony_ci} 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_cistatic int snd_sonicvibes_mixer(struct sonicvibes *sonic) 10858c2ecf20Sopenharmony_ci{ 10868c2ecf20Sopenharmony_ci struct snd_card *card; 10878c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 10888c2ecf20Sopenharmony_ci unsigned int idx; 10898c2ecf20Sopenharmony_ci int err; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci if (snd_BUG_ON(!sonic || !sonic->card)) 10928c2ecf20Sopenharmony_ci return -EINVAL; 10938c2ecf20Sopenharmony_ci card = sonic->card; 10948c2ecf20Sopenharmony_ci strcpy(card->mixername, "S3 SonicVibes"); 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_sonicvibes_controls); idx++) { 10978c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_sonicvibes_controls[idx], sonic))) < 0) 10988c2ecf20Sopenharmony_ci return err; 10998c2ecf20Sopenharmony_ci switch (idx) { 11008c2ecf20Sopenharmony_ci case 0: 11018c2ecf20Sopenharmony_ci case 1: kctl->private_free = snd_sonicvibes_master_free; break; 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci return 0; 11058c2ecf20Sopenharmony_ci} 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci/* 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci */ 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_cistatic void snd_sonicvibes_proc_read(struct snd_info_entry *entry, 11128c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 11138c2ecf20Sopenharmony_ci{ 11148c2ecf20Sopenharmony_ci struct sonicvibes *sonic = entry->private_data; 11158c2ecf20Sopenharmony_ci unsigned char tmp; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci tmp = sonic->srs_space & 0x0f; 11188c2ecf20Sopenharmony_ci snd_iprintf(buffer, "SRS 3D : %s\n", 11198c2ecf20Sopenharmony_ci sonic->srs_space & 0x80 ? "off" : "on"); 11208c2ecf20Sopenharmony_ci snd_iprintf(buffer, "SRS Space : %s\n", 11218c2ecf20Sopenharmony_ci tmp == 0x00 ? "100%" : 11228c2ecf20Sopenharmony_ci tmp == 0x01 ? "75%" : 11238c2ecf20Sopenharmony_ci tmp == 0x02 ? "50%" : 11248c2ecf20Sopenharmony_ci tmp == 0x03 ? "25%" : "0%"); 11258c2ecf20Sopenharmony_ci tmp = sonic->srs_center & 0x0f; 11268c2ecf20Sopenharmony_ci snd_iprintf(buffer, "SRS Center : %s\n", 11278c2ecf20Sopenharmony_ci tmp == 0x00 ? "100%" : 11288c2ecf20Sopenharmony_ci tmp == 0x01 ? "75%" : 11298c2ecf20Sopenharmony_ci tmp == 0x02 ? "50%" : 11308c2ecf20Sopenharmony_ci tmp == 0x03 ? "25%" : "0%"); 11318c2ecf20Sopenharmony_ci tmp = sonic->wave_source & 0x03; 11328c2ecf20Sopenharmony_ci snd_iprintf(buffer, "WaveTable Source : %s\n", 11338c2ecf20Sopenharmony_ci tmp == 0x00 ? "on-board ROM" : 11348c2ecf20Sopenharmony_ci tmp == 0x01 ? "PCI bus" : "on-board ROM + PCI bus"); 11358c2ecf20Sopenharmony_ci tmp = sonic->mpu_switch; 11368c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Onboard synth : %s\n", tmp & 0x01 ? "on" : "off"); 11378c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Ext. Rx to synth : %s\n", tmp & 0x02 ? "on" : "off"); 11388c2ecf20Sopenharmony_ci snd_iprintf(buffer, "MIDI to ext. Tx : %s\n", tmp & 0x04 ? "on" : "off"); 11398c2ecf20Sopenharmony_ci} 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_cistatic void snd_sonicvibes_proc_init(struct sonicvibes *sonic) 11428c2ecf20Sopenharmony_ci{ 11438c2ecf20Sopenharmony_ci snd_card_ro_proc_new(sonic->card, "sonicvibes", sonic, 11448c2ecf20Sopenharmony_ci snd_sonicvibes_proc_read); 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci/* 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci */ 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci#ifdef SUPPORT_JOYSTICK 11528c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_sonicvibes_game_control = 11538c2ecf20Sopenharmony_ciSONICVIBES_SINGLE("Joystick Speed", 0, SV_IREG_GAME_PORT, 1, 15, 0); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_cistatic int snd_sonicvibes_create_gameport(struct sonicvibes *sonic) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci struct gameport *gp; 11588c2ecf20Sopenharmony_ci int err; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci sonic->gameport = gp = gameport_allocate_port(); 11618c2ecf20Sopenharmony_ci if (!gp) { 11628c2ecf20Sopenharmony_ci dev_err(sonic->card->dev, 11638c2ecf20Sopenharmony_ci "sonicvibes: cannot allocate memory for gameport\n"); 11648c2ecf20Sopenharmony_ci return -ENOMEM; 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci gameport_set_name(gp, "SonicVibes Gameport"); 11688c2ecf20Sopenharmony_ci gameport_set_phys(gp, "pci%s/gameport0", pci_name(sonic->pci)); 11698c2ecf20Sopenharmony_ci gameport_set_dev_parent(gp, &sonic->pci->dev); 11708c2ecf20Sopenharmony_ci gp->io = sonic->game_port; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci gameport_register_port(gp); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci err = snd_ctl_add(sonic->card, 11758c2ecf20Sopenharmony_ci snd_ctl_new1(&snd_sonicvibes_game_control, sonic)); 11768c2ecf20Sopenharmony_ci if (err < 0) 11778c2ecf20Sopenharmony_ci return err; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci return 0; 11808c2ecf20Sopenharmony_ci} 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_cistatic void snd_sonicvibes_free_gameport(struct sonicvibes *sonic) 11838c2ecf20Sopenharmony_ci{ 11848c2ecf20Sopenharmony_ci if (sonic->gameport) { 11858c2ecf20Sopenharmony_ci gameport_unregister_port(sonic->gameport); 11868c2ecf20Sopenharmony_ci sonic->gameport = NULL; 11878c2ecf20Sopenharmony_ci } 11888c2ecf20Sopenharmony_ci} 11898c2ecf20Sopenharmony_ci#else 11908c2ecf20Sopenharmony_cistatic inline int snd_sonicvibes_create_gameport(struct sonicvibes *sonic) { return -ENOSYS; } 11918c2ecf20Sopenharmony_cistatic inline void snd_sonicvibes_free_gameport(struct sonicvibes *sonic) { } 11928c2ecf20Sopenharmony_ci#endif 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_cistatic int snd_sonicvibes_free(struct sonicvibes *sonic) 11958c2ecf20Sopenharmony_ci{ 11968c2ecf20Sopenharmony_ci snd_sonicvibes_free_gameport(sonic); 11978c2ecf20Sopenharmony_ci pci_write_config_dword(sonic->pci, 0x40, sonic->dmaa_port); 11988c2ecf20Sopenharmony_ci pci_write_config_dword(sonic->pci, 0x48, sonic->dmac_port); 11998c2ecf20Sopenharmony_ci if (sonic->irq >= 0) 12008c2ecf20Sopenharmony_ci free_irq(sonic->irq, sonic); 12018c2ecf20Sopenharmony_ci release_and_free_resource(sonic->res_dmaa); 12028c2ecf20Sopenharmony_ci release_and_free_resource(sonic->res_dmac); 12038c2ecf20Sopenharmony_ci pci_release_regions(sonic->pci); 12048c2ecf20Sopenharmony_ci pci_disable_device(sonic->pci); 12058c2ecf20Sopenharmony_ci kfree(sonic); 12068c2ecf20Sopenharmony_ci return 0; 12078c2ecf20Sopenharmony_ci} 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_cistatic int snd_sonicvibes_dev_free(struct snd_device *device) 12108c2ecf20Sopenharmony_ci{ 12118c2ecf20Sopenharmony_ci struct sonicvibes *sonic = device->device_data; 12128c2ecf20Sopenharmony_ci return snd_sonicvibes_free(sonic); 12138c2ecf20Sopenharmony_ci} 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_cistatic int snd_sonicvibes_create(struct snd_card *card, 12168c2ecf20Sopenharmony_ci struct pci_dev *pci, 12178c2ecf20Sopenharmony_ci int reverb, 12188c2ecf20Sopenharmony_ci int mge, 12198c2ecf20Sopenharmony_ci struct sonicvibes **rsonic) 12208c2ecf20Sopenharmony_ci{ 12218c2ecf20Sopenharmony_ci struct sonicvibes *sonic; 12228c2ecf20Sopenharmony_ci unsigned int dmaa, dmac; 12238c2ecf20Sopenharmony_ci int err; 12248c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 12258c2ecf20Sopenharmony_ci .dev_free = snd_sonicvibes_dev_free, 12268c2ecf20Sopenharmony_ci }; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci *rsonic = NULL; 12298c2ecf20Sopenharmony_ci /* enable PCI device */ 12308c2ecf20Sopenharmony_ci if ((err = pci_enable_device(pci)) < 0) 12318c2ecf20Sopenharmony_ci return err; 12328c2ecf20Sopenharmony_ci /* check, if we can restrict PCI DMA transfers to 24 bits */ 12338c2ecf20Sopenharmony_ci if (dma_set_mask(&pci->dev, DMA_BIT_MASK(24)) < 0 || 12348c2ecf20Sopenharmony_ci dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(24)) < 0) { 12358c2ecf20Sopenharmony_ci dev_err(card->dev, 12368c2ecf20Sopenharmony_ci "architecture does not support 24bit PCI busmaster DMA\n"); 12378c2ecf20Sopenharmony_ci pci_disable_device(pci); 12388c2ecf20Sopenharmony_ci return -ENXIO; 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci sonic = kzalloc(sizeof(*sonic), GFP_KERNEL); 12428c2ecf20Sopenharmony_ci if (sonic == NULL) { 12438c2ecf20Sopenharmony_ci pci_disable_device(pci); 12448c2ecf20Sopenharmony_ci return -ENOMEM; 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci spin_lock_init(&sonic->reg_lock); 12478c2ecf20Sopenharmony_ci sonic->card = card; 12488c2ecf20Sopenharmony_ci sonic->pci = pci; 12498c2ecf20Sopenharmony_ci sonic->irq = -1; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci if ((err = pci_request_regions(pci, "S3 SonicVibes")) < 0) { 12528c2ecf20Sopenharmony_ci kfree(sonic); 12538c2ecf20Sopenharmony_ci pci_disable_device(pci); 12548c2ecf20Sopenharmony_ci return err; 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci sonic->sb_port = pci_resource_start(pci, 0); 12588c2ecf20Sopenharmony_ci sonic->enh_port = pci_resource_start(pci, 1); 12598c2ecf20Sopenharmony_ci sonic->synth_port = pci_resource_start(pci, 2); 12608c2ecf20Sopenharmony_ci sonic->midi_port = pci_resource_start(pci, 3); 12618c2ecf20Sopenharmony_ci sonic->game_port = pci_resource_start(pci, 4); 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci if (request_irq(pci->irq, snd_sonicvibes_interrupt, IRQF_SHARED, 12648c2ecf20Sopenharmony_ci KBUILD_MODNAME, sonic)) { 12658c2ecf20Sopenharmony_ci dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); 12668c2ecf20Sopenharmony_ci snd_sonicvibes_free(sonic); 12678c2ecf20Sopenharmony_ci return -EBUSY; 12688c2ecf20Sopenharmony_ci } 12698c2ecf20Sopenharmony_ci sonic->irq = pci->irq; 12708c2ecf20Sopenharmony_ci card->sync_irq = sonic->irq; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci pci_read_config_dword(pci, 0x40, &dmaa); 12738c2ecf20Sopenharmony_ci pci_read_config_dword(pci, 0x48, &dmac); 12748c2ecf20Sopenharmony_ci dmaio &= ~0x0f; 12758c2ecf20Sopenharmony_ci dmaa &= ~0x0f; 12768c2ecf20Sopenharmony_ci dmac &= ~0x0f; 12778c2ecf20Sopenharmony_ci if (!dmaa) { 12788c2ecf20Sopenharmony_ci dmaa = dmaio; 12798c2ecf20Sopenharmony_ci dmaio += 0x10; 12808c2ecf20Sopenharmony_ci dev_info(card->dev, 12818c2ecf20Sopenharmony_ci "BIOS did not allocate DDMA channel A i/o, allocated at 0x%x\n", 12828c2ecf20Sopenharmony_ci dmaa); 12838c2ecf20Sopenharmony_ci } 12848c2ecf20Sopenharmony_ci if (!dmac) { 12858c2ecf20Sopenharmony_ci dmac = dmaio; 12868c2ecf20Sopenharmony_ci dmaio += 0x10; 12878c2ecf20Sopenharmony_ci dev_info(card->dev, 12888c2ecf20Sopenharmony_ci "BIOS did not allocate DDMA channel C i/o, allocated at 0x%x\n", 12898c2ecf20Sopenharmony_ci dmac); 12908c2ecf20Sopenharmony_ci } 12918c2ecf20Sopenharmony_ci pci_write_config_dword(pci, 0x40, dmaa); 12928c2ecf20Sopenharmony_ci pci_write_config_dword(pci, 0x48, dmac); 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci if ((sonic->res_dmaa = request_region(dmaa, 0x10, "S3 SonicVibes DDMA-A")) == NULL) { 12958c2ecf20Sopenharmony_ci snd_sonicvibes_free(sonic); 12968c2ecf20Sopenharmony_ci dev_err(card->dev, 12978c2ecf20Sopenharmony_ci "unable to grab DDMA-A port at 0x%x-0x%x\n", 12988c2ecf20Sopenharmony_ci dmaa, dmaa + 0x10 - 1); 12998c2ecf20Sopenharmony_ci return -EBUSY; 13008c2ecf20Sopenharmony_ci } 13018c2ecf20Sopenharmony_ci if ((sonic->res_dmac = request_region(dmac, 0x10, "S3 SonicVibes DDMA-C")) == NULL) { 13028c2ecf20Sopenharmony_ci snd_sonicvibes_free(sonic); 13038c2ecf20Sopenharmony_ci dev_err(card->dev, 13048c2ecf20Sopenharmony_ci "unable to grab DDMA-C port at 0x%x-0x%x\n", 13058c2ecf20Sopenharmony_ci dmac, dmac + 0x10 - 1); 13068c2ecf20Sopenharmony_ci return -EBUSY; 13078c2ecf20Sopenharmony_ci } 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci pci_read_config_dword(pci, 0x40, &sonic->dmaa_port); 13108c2ecf20Sopenharmony_ci pci_read_config_dword(pci, 0x48, &sonic->dmac_port); 13118c2ecf20Sopenharmony_ci sonic->dmaa_port &= ~0x0f; 13128c2ecf20Sopenharmony_ci sonic->dmac_port &= ~0x0f; 13138c2ecf20Sopenharmony_ci pci_write_config_dword(pci, 0x40, sonic->dmaa_port | 9); /* enable + enhanced */ 13148c2ecf20Sopenharmony_ci pci_write_config_dword(pci, 0x48, sonic->dmac_port | 9); /* enable */ 13158c2ecf20Sopenharmony_ci /* ok.. initialize S3 SonicVibes chip */ 13168c2ecf20Sopenharmony_ci outb(SV_RESET, SV_REG(sonic, CONTROL)); /* reset chip */ 13178c2ecf20Sopenharmony_ci udelay(100); 13188c2ecf20Sopenharmony_ci outb(0, SV_REG(sonic, CONTROL)); /* release reset */ 13198c2ecf20Sopenharmony_ci udelay(100); 13208c2ecf20Sopenharmony_ci outb(SV_ENHANCED | SV_INTA | (reverb ? SV_REVERB : 0), SV_REG(sonic, CONTROL)); 13218c2ecf20Sopenharmony_ci inb(SV_REG(sonic, STATUS)); /* clear IRQs */ 13228c2ecf20Sopenharmony_ci#if 1 13238c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_DRIVE_CTRL, 0); /* drive current 16mA */ 13248c2ecf20Sopenharmony_ci#else 13258c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_DRIVE_CTRL, 0x40); /* drive current 8mA */ 13268c2ecf20Sopenharmony_ci#endif 13278c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_PC_ENABLE, sonic->enable = 0); /* disable playback & capture */ 13288c2ecf20Sopenharmony_ci outb(sonic->irqmask = ~(SV_DMAA_MASK | SV_DMAC_MASK | SV_UD_MASK), SV_REG(sonic, IRQMASK)); 13298c2ecf20Sopenharmony_ci inb(SV_REG(sonic, STATUS)); /* clear IRQs */ 13308c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_ADC_CLOCK, 0); /* use PLL as clock source */ 13318c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_ANALOG_POWER, 0); /* power up analog parts */ 13328c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_DIGITAL_POWER, 0); /* power up digital parts */ 13338c2ecf20Sopenharmony_ci snd_sonicvibes_setpll(sonic, SV_IREG_ADC_PLL, 8000); 13348c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_SRS_SPACE, sonic->srs_space = 0x80); /* SRS space off */ 13358c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_SRS_CENTER, sonic->srs_center = 0x00);/* SRS center off */ 13368c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_MPU401, sonic->mpu_switch = 0x05); /* MPU-401 switch */ 13378c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_WAVE_SOURCE, sonic->wave_source = 0x00); /* onboard ROM */ 13388c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_PCM_RATE_LOW, (8000 * 65536 / SV_FULLRATE) & 0xff); 13398c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_PCM_RATE_HIGH, ((8000 * 65536 / SV_FULLRATE) >> 8) & 0xff); 13408c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_LEFT_ADC, mge ? 0xd0 : 0xc0); 13418c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_RIGHT_ADC, 0xc0); 13428c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_LEFT_AUX1, 0x9f); 13438c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_RIGHT_AUX1, 0x9f); 13448c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_LEFT_CD, 0x9f); 13458c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_RIGHT_CD, 0x9f); 13468c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_LEFT_LINE, 0x9f); 13478c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_RIGHT_LINE, 0x9f); 13488c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_MIC, 0x8f); 13498c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_LEFT_SYNTH, 0x9f); 13508c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_RIGHT_SYNTH, 0x9f); 13518c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_LEFT_AUX2, 0x9f); 13528c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_RIGHT_AUX2, 0x9f); 13538c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_LEFT_ANALOG, 0x9f); 13548c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_RIGHT_ANALOG, 0x9f); 13558c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_LEFT_PCM, 0xbf); 13568c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_RIGHT_PCM, 0xbf); 13578c2ecf20Sopenharmony_ci snd_sonicvibes_out(sonic, SV_IREG_ADC_OUTPUT_CTRL, 0xfc); 13588c2ecf20Sopenharmony_ci#if 0 13598c2ecf20Sopenharmony_ci snd_sonicvibes_debug(sonic); 13608c2ecf20Sopenharmony_ci#endif 13618c2ecf20Sopenharmony_ci sonic->revision = snd_sonicvibes_in(sonic, SV_IREG_REVISION); 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, sonic, &ops)) < 0) { 13648c2ecf20Sopenharmony_ci snd_sonicvibes_free(sonic); 13658c2ecf20Sopenharmony_ci return err; 13668c2ecf20Sopenharmony_ci } 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci snd_sonicvibes_proc_init(sonic); 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci *rsonic = sonic; 13718c2ecf20Sopenharmony_ci return 0; 13728c2ecf20Sopenharmony_ci} 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci/* 13758c2ecf20Sopenharmony_ci * MIDI section 13768c2ecf20Sopenharmony_ci */ 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_sonicvibes_midi_controls[] = { 13798c2ecf20Sopenharmony_ciSONICVIBES_SINGLE("SonicVibes Wave Source RAM", 0, SV_IREG_WAVE_SOURCE, 0, 1, 0), 13808c2ecf20Sopenharmony_ciSONICVIBES_SINGLE("SonicVibes Wave Source RAM+ROM", 0, SV_IREG_WAVE_SOURCE, 1, 1, 0), 13818c2ecf20Sopenharmony_ciSONICVIBES_SINGLE("SonicVibes Onboard Synth", 0, SV_IREG_MPU401, 0, 1, 0), 13828c2ecf20Sopenharmony_ciSONICVIBES_SINGLE("SonicVibes External Rx to Synth", 0, SV_IREG_MPU401, 1, 1, 0), 13838c2ecf20Sopenharmony_ciSONICVIBES_SINGLE("SonicVibes External Tx", 0, SV_IREG_MPU401, 2, 1, 0) 13848c2ecf20Sopenharmony_ci}; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_cistatic int snd_sonicvibes_midi_input_open(struct snd_mpu401 * mpu) 13878c2ecf20Sopenharmony_ci{ 13888c2ecf20Sopenharmony_ci struct sonicvibes *sonic = mpu->private_data; 13898c2ecf20Sopenharmony_ci outb(sonic->irqmask &= ~SV_MIDI_MASK, SV_REG(sonic, IRQMASK)); 13908c2ecf20Sopenharmony_ci return 0; 13918c2ecf20Sopenharmony_ci} 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_cistatic void snd_sonicvibes_midi_input_close(struct snd_mpu401 * mpu) 13948c2ecf20Sopenharmony_ci{ 13958c2ecf20Sopenharmony_ci struct sonicvibes *sonic = mpu->private_data; 13968c2ecf20Sopenharmony_ci outb(sonic->irqmask |= SV_MIDI_MASK, SV_REG(sonic, IRQMASK)); 13978c2ecf20Sopenharmony_ci} 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_cistatic int snd_sonicvibes_midi(struct sonicvibes *sonic, 14008c2ecf20Sopenharmony_ci struct snd_rawmidi *rmidi) 14018c2ecf20Sopenharmony_ci{ 14028c2ecf20Sopenharmony_ci struct snd_mpu401 * mpu = rmidi->private_data; 14038c2ecf20Sopenharmony_ci struct snd_card *card = sonic->card; 14048c2ecf20Sopenharmony_ci unsigned int idx; 14058c2ecf20Sopenharmony_ci int err; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci mpu->private_data = sonic; 14088c2ecf20Sopenharmony_ci mpu->open_input = snd_sonicvibes_midi_input_open; 14098c2ecf20Sopenharmony_ci mpu->close_input = snd_sonicvibes_midi_input_close; 14108c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_sonicvibes_midi_controls); idx++) 14118c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_sonicvibes_midi_controls[idx], sonic))) < 0) 14128c2ecf20Sopenharmony_ci return err; 14138c2ecf20Sopenharmony_ci return 0; 14148c2ecf20Sopenharmony_ci} 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_cistatic int snd_sonic_probe(struct pci_dev *pci, 14178c2ecf20Sopenharmony_ci const struct pci_device_id *pci_id) 14188c2ecf20Sopenharmony_ci{ 14198c2ecf20Sopenharmony_ci static int dev; 14208c2ecf20Sopenharmony_ci struct snd_card *card; 14218c2ecf20Sopenharmony_ci struct sonicvibes *sonic; 14228c2ecf20Sopenharmony_ci struct snd_rawmidi *midi_uart; 14238c2ecf20Sopenharmony_ci struct snd_opl3 *opl3; 14248c2ecf20Sopenharmony_ci int idx, err; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci if (dev >= SNDRV_CARDS) 14278c2ecf20Sopenharmony_ci return -ENODEV; 14288c2ecf20Sopenharmony_ci if (!enable[dev]) { 14298c2ecf20Sopenharmony_ci dev++; 14308c2ecf20Sopenharmony_ci return -ENOENT; 14318c2ecf20Sopenharmony_ci } 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 14348c2ecf20Sopenharmony_ci 0, &card); 14358c2ecf20Sopenharmony_ci if (err < 0) 14368c2ecf20Sopenharmony_ci return err; 14378c2ecf20Sopenharmony_ci for (idx = 0; idx < 5; idx++) { 14388c2ecf20Sopenharmony_ci if (pci_resource_start(pci, idx) == 0 || 14398c2ecf20Sopenharmony_ci !(pci_resource_flags(pci, idx) & IORESOURCE_IO)) { 14408c2ecf20Sopenharmony_ci snd_card_free(card); 14418c2ecf20Sopenharmony_ci return -ENODEV; 14428c2ecf20Sopenharmony_ci } 14438c2ecf20Sopenharmony_ci } 14448c2ecf20Sopenharmony_ci if ((err = snd_sonicvibes_create(card, pci, 14458c2ecf20Sopenharmony_ci reverb[dev] ? 1 : 0, 14468c2ecf20Sopenharmony_ci mge[dev] ? 1 : 0, 14478c2ecf20Sopenharmony_ci &sonic)) < 0) { 14488c2ecf20Sopenharmony_ci snd_card_free(card); 14498c2ecf20Sopenharmony_ci return err; 14508c2ecf20Sopenharmony_ci } 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci strcpy(card->driver, "SonicVibes"); 14538c2ecf20Sopenharmony_ci strcpy(card->shortname, "S3 SonicVibes"); 14548c2ecf20Sopenharmony_ci sprintf(card->longname, "%s rev %i at 0x%llx, irq %i", 14558c2ecf20Sopenharmony_ci card->shortname, 14568c2ecf20Sopenharmony_ci sonic->revision, 14578c2ecf20Sopenharmony_ci (unsigned long long)pci_resource_start(pci, 1), 14588c2ecf20Sopenharmony_ci sonic->irq); 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci if ((err = snd_sonicvibes_pcm(sonic, 0)) < 0) { 14618c2ecf20Sopenharmony_ci snd_card_free(card); 14628c2ecf20Sopenharmony_ci return err; 14638c2ecf20Sopenharmony_ci } 14648c2ecf20Sopenharmony_ci if ((err = snd_sonicvibes_mixer(sonic)) < 0) { 14658c2ecf20Sopenharmony_ci snd_card_free(card); 14668c2ecf20Sopenharmony_ci return err; 14678c2ecf20Sopenharmony_ci } 14688c2ecf20Sopenharmony_ci if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SONICVIBES, 14698c2ecf20Sopenharmony_ci sonic->midi_port, 14708c2ecf20Sopenharmony_ci MPU401_INFO_INTEGRATED | 14718c2ecf20Sopenharmony_ci MPU401_INFO_IRQ_HOOK, 14728c2ecf20Sopenharmony_ci -1, &midi_uart)) < 0) { 14738c2ecf20Sopenharmony_ci snd_card_free(card); 14748c2ecf20Sopenharmony_ci return err; 14758c2ecf20Sopenharmony_ci } 14768c2ecf20Sopenharmony_ci snd_sonicvibes_midi(sonic, midi_uart); 14778c2ecf20Sopenharmony_ci if ((err = snd_opl3_create(card, sonic->synth_port, 14788c2ecf20Sopenharmony_ci sonic->synth_port + 2, 14798c2ecf20Sopenharmony_ci OPL3_HW_OPL3_SV, 1, &opl3)) < 0) { 14808c2ecf20Sopenharmony_ci snd_card_free(card); 14818c2ecf20Sopenharmony_ci return err; 14828c2ecf20Sopenharmony_ci } 14838c2ecf20Sopenharmony_ci if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { 14848c2ecf20Sopenharmony_ci snd_card_free(card); 14858c2ecf20Sopenharmony_ci return err; 14868c2ecf20Sopenharmony_ci } 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci err = snd_sonicvibes_create_gameport(sonic); 14898c2ecf20Sopenharmony_ci if (err < 0) { 14908c2ecf20Sopenharmony_ci snd_card_free(card); 14918c2ecf20Sopenharmony_ci return err; 14928c2ecf20Sopenharmony_ci } 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci if ((err = snd_card_register(card)) < 0) { 14958c2ecf20Sopenharmony_ci snd_card_free(card); 14968c2ecf20Sopenharmony_ci return err; 14978c2ecf20Sopenharmony_ci } 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci pci_set_drvdata(pci, card); 15008c2ecf20Sopenharmony_ci dev++; 15018c2ecf20Sopenharmony_ci return 0; 15028c2ecf20Sopenharmony_ci} 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_cistatic void snd_sonic_remove(struct pci_dev *pci) 15058c2ecf20Sopenharmony_ci{ 15068c2ecf20Sopenharmony_ci snd_card_free(pci_get_drvdata(pci)); 15078c2ecf20Sopenharmony_ci} 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_cistatic struct pci_driver sonicvibes_driver = { 15108c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 15118c2ecf20Sopenharmony_ci .id_table = snd_sonic_ids, 15128c2ecf20Sopenharmony_ci .probe = snd_sonic_probe, 15138c2ecf20Sopenharmony_ci .remove = snd_sonic_remove, 15148c2ecf20Sopenharmony_ci}; 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_cimodule_pci_driver(sonicvibes_driver); 1517