18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for C-Media CMI8338 and 8738 PCI soundcards. 48c2ecf20Sopenharmony_ci * Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci/* Does not work. Warning may block system in capture mode */ 88c2ecf20Sopenharmony_ci/* #define USE_VAR48KRATE */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/init.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/mutex.h> 198c2ecf20Sopenharmony_ci#include <sound/core.h> 208c2ecf20Sopenharmony_ci#include <sound/info.h> 218c2ecf20Sopenharmony_ci#include <sound/control.h> 228c2ecf20Sopenharmony_ci#include <sound/pcm.h> 238c2ecf20Sopenharmony_ci#include <sound/rawmidi.h> 248c2ecf20Sopenharmony_ci#include <sound/mpu401.h> 258c2ecf20Sopenharmony_ci#include <sound/opl3.h> 268c2ecf20Sopenharmony_ci#include <sound/sb.h> 278c2ecf20Sopenharmony_ci#include <sound/asoundef.h> 288c2ecf20Sopenharmony_ci#include <sound/initval.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); 318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("C-Media CMI8x38 PCI"); 328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 338c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{C-Media,CMI8738}," 348c2ecf20Sopenharmony_ci "{C-Media,CMI8738B}," 358c2ecf20Sopenharmony_ci "{C-Media,CMI8338A}," 368c2ecf20Sopenharmony_ci "{C-Media,CMI8338B}}"); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#if IS_REACHABLE(CONFIG_GAMEPORT) 398c2ecf20Sopenharmony_ci#define SUPPORT_JOYSTICK 1 408c2ecf20Sopenharmony_ci#endif 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 438c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 448c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ 458c2ecf20Sopenharmony_cistatic long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1}; 468c2ecf20Sopenharmony_cistatic long fm_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)]=1}; 478c2ecf20Sopenharmony_cistatic bool soft_ac3[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)]=1}; 488c2ecf20Sopenharmony_ci#ifdef SUPPORT_JOYSTICK 498c2ecf20Sopenharmony_cistatic int joystick_port[SNDRV_CARDS]; 508c2ecf20Sopenharmony_ci#endif 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for C-Media PCI soundcard."); 548c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 558c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for C-Media PCI soundcard."); 568c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable C-Media PCI soundcard."); 588c2ecf20Sopenharmony_cimodule_param_hw_array(mpu_port, long, ioport, NULL, 0444); 598c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mpu_port, "MPU-401 port."); 608c2ecf20Sopenharmony_cimodule_param_hw_array(fm_port, long, ioport, NULL, 0444); 618c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fm_port, "FM port."); 628c2ecf20Sopenharmony_cimodule_param_array(soft_ac3, bool, NULL, 0444); 638c2ecf20Sopenharmony_ciMODULE_PARM_DESC(soft_ac3, "Software-conversion of raw SPDIF packets (model 033 only)."); 648c2ecf20Sopenharmony_ci#ifdef SUPPORT_JOYSTICK 658c2ecf20Sopenharmony_cimodule_param_hw_array(joystick_port, int, ioport, NULL, 0444); 668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(joystick_port, "Joystick port address."); 678c2ecf20Sopenharmony_ci#endif 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* 708c2ecf20Sopenharmony_ci * CM8x38 registers definition 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define CM_REG_FUNCTRL0 0x00 748c2ecf20Sopenharmony_ci#define CM_RST_CH1 0x00080000 758c2ecf20Sopenharmony_ci#define CM_RST_CH0 0x00040000 768c2ecf20Sopenharmony_ci#define CM_CHEN1 0x00020000 /* ch1: enable */ 778c2ecf20Sopenharmony_ci#define CM_CHEN0 0x00010000 /* ch0: enable */ 788c2ecf20Sopenharmony_ci#define CM_PAUSE1 0x00000008 /* ch1: pause */ 798c2ecf20Sopenharmony_ci#define CM_PAUSE0 0x00000004 /* ch0: pause */ 808c2ecf20Sopenharmony_ci#define CM_CHADC1 0x00000002 /* ch1, 0:playback, 1:record */ 818c2ecf20Sopenharmony_ci#define CM_CHADC0 0x00000001 /* ch0, 0:playback, 1:record */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define CM_REG_FUNCTRL1 0x04 848c2ecf20Sopenharmony_ci#define CM_DSFC_MASK 0x0000E000 /* channel 1 (DAC?) sampling frequency */ 858c2ecf20Sopenharmony_ci#define CM_DSFC_SHIFT 13 868c2ecf20Sopenharmony_ci#define CM_ASFC_MASK 0x00001C00 /* channel 0 (ADC?) sampling frequency */ 878c2ecf20Sopenharmony_ci#define CM_ASFC_SHIFT 10 888c2ecf20Sopenharmony_ci#define CM_SPDF_1 0x00000200 /* SPDIF IN/OUT at channel B */ 898c2ecf20Sopenharmony_ci#define CM_SPDF_0 0x00000100 /* SPDIF OUT only channel A */ 908c2ecf20Sopenharmony_ci#define CM_SPDFLOOP 0x00000080 /* ext. SPDIIF/IN -> OUT loopback */ 918c2ecf20Sopenharmony_ci#define CM_SPDO2DAC 0x00000040 /* SPDIF/OUT can be heard from internal DAC */ 928c2ecf20Sopenharmony_ci#define CM_INTRM 0x00000020 /* master control block (MCB) interrupt enabled */ 938c2ecf20Sopenharmony_ci#define CM_BREQ 0x00000010 /* bus master enabled */ 948c2ecf20Sopenharmony_ci#define CM_VOICE_EN 0x00000008 /* legacy voice (SB16,FM) */ 958c2ecf20Sopenharmony_ci#define CM_UART_EN 0x00000004 /* legacy UART */ 968c2ecf20Sopenharmony_ci#define CM_JYSTK_EN 0x00000002 /* legacy joystick */ 978c2ecf20Sopenharmony_ci#define CM_ZVPORT 0x00000001 /* ZVPORT */ 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci#define CM_REG_CHFORMAT 0x08 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#define CM_CHB3D5C 0x80000000 /* 5,6 channels */ 1028c2ecf20Sopenharmony_ci#define CM_FMOFFSET2 0x40000000 /* initial FM PCM offset 2 when Fmute=1 */ 1038c2ecf20Sopenharmony_ci#define CM_CHB3D 0x20000000 /* 4 channels */ 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci#define CM_CHIP_MASK1 0x1f000000 1068c2ecf20Sopenharmony_ci#define CM_CHIP_037 0x01000000 1078c2ecf20Sopenharmony_ci#define CM_SETLAT48 0x00800000 /* set latency timer 48h */ 1088c2ecf20Sopenharmony_ci#define CM_EDGEIRQ 0x00400000 /* emulated edge trigger legacy IRQ */ 1098c2ecf20Sopenharmony_ci#define CM_SPD24SEL39 0x00200000 /* 24-bit spdif: model 039 */ 1108c2ecf20Sopenharmony_ci#define CM_AC3EN1 0x00100000 /* enable AC3: model 037 */ 1118c2ecf20Sopenharmony_ci#define CM_SPDIF_SELECT1 0x00080000 /* for model <= 037 ? */ 1128c2ecf20Sopenharmony_ci#define CM_SPD24SEL 0x00020000 /* 24bit spdif: model 037 */ 1138c2ecf20Sopenharmony_ci/* #define CM_SPDIF_INVERSE 0x00010000 */ /* ??? */ 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci#define CM_ADCBITLEN_MASK 0x0000C000 1168c2ecf20Sopenharmony_ci#define CM_ADCBITLEN_16 0x00000000 1178c2ecf20Sopenharmony_ci#define CM_ADCBITLEN_15 0x00004000 1188c2ecf20Sopenharmony_ci#define CM_ADCBITLEN_14 0x00008000 1198c2ecf20Sopenharmony_ci#define CM_ADCBITLEN_13 0x0000C000 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci#define CM_ADCDACLEN_MASK 0x00003000 /* model 037 */ 1228c2ecf20Sopenharmony_ci#define CM_ADCDACLEN_060 0x00000000 1238c2ecf20Sopenharmony_ci#define CM_ADCDACLEN_066 0x00001000 1248c2ecf20Sopenharmony_ci#define CM_ADCDACLEN_130 0x00002000 1258c2ecf20Sopenharmony_ci#define CM_ADCDACLEN_280 0x00003000 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci#define CM_ADCDLEN_MASK 0x00003000 /* model 039 */ 1288c2ecf20Sopenharmony_ci#define CM_ADCDLEN_ORIGINAL 0x00000000 1298c2ecf20Sopenharmony_ci#define CM_ADCDLEN_EXTRA 0x00001000 1308c2ecf20Sopenharmony_ci#define CM_ADCDLEN_24K 0x00002000 1318c2ecf20Sopenharmony_ci#define CM_ADCDLEN_WEIGHT 0x00003000 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci#define CM_CH1_SRATE_176K 0x00000800 1348c2ecf20Sopenharmony_ci#define CM_CH1_SRATE_96K 0x00000800 /* model 055? */ 1358c2ecf20Sopenharmony_ci#define CM_CH1_SRATE_88K 0x00000400 1368c2ecf20Sopenharmony_ci#define CM_CH0_SRATE_176K 0x00000200 1378c2ecf20Sopenharmony_ci#define CM_CH0_SRATE_96K 0x00000200 /* model 055? */ 1388c2ecf20Sopenharmony_ci#define CM_CH0_SRATE_88K 0x00000100 1398c2ecf20Sopenharmony_ci#define CM_CH0_SRATE_128K 0x00000300 1408c2ecf20Sopenharmony_ci#define CM_CH0_SRATE_MASK 0x00000300 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci#define CM_SPDIF_INVERSE2 0x00000080 /* model 055? */ 1438c2ecf20Sopenharmony_ci#define CM_DBLSPDS 0x00000040 /* double SPDIF sample rate 88.2/96 */ 1448c2ecf20Sopenharmony_ci#define CM_POLVALID 0x00000020 /* inverse SPDIF/IN valid bit */ 1458c2ecf20Sopenharmony_ci#define CM_SPDLOCKED 0x00000010 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci#define CM_CH1FMT_MASK 0x0000000C /* bit 3: 16 bits, bit 2: stereo */ 1488c2ecf20Sopenharmony_ci#define CM_CH1FMT_SHIFT 2 1498c2ecf20Sopenharmony_ci#define CM_CH0FMT_MASK 0x00000003 /* bit 1: 16 bits, bit 0: stereo */ 1508c2ecf20Sopenharmony_ci#define CM_CH0FMT_SHIFT 0 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci#define CM_REG_INT_HLDCLR 0x0C 1538c2ecf20Sopenharmony_ci#define CM_CHIP_MASK2 0xff000000 1548c2ecf20Sopenharmony_ci#define CM_CHIP_8768 0x20000000 1558c2ecf20Sopenharmony_ci#define CM_CHIP_055 0x08000000 1568c2ecf20Sopenharmony_ci#define CM_CHIP_039 0x04000000 1578c2ecf20Sopenharmony_ci#define CM_CHIP_039_6CH 0x01000000 1588c2ecf20Sopenharmony_ci#define CM_UNKNOWN_INT_EN 0x00080000 /* ? */ 1598c2ecf20Sopenharmony_ci#define CM_TDMA_INT_EN 0x00040000 1608c2ecf20Sopenharmony_ci#define CM_CH1_INT_EN 0x00020000 1618c2ecf20Sopenharmony_ci#define CM_CH0_INT_EN 0x00010000 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci#define CM_REG_INT_STATUS 0x10 1648c2ecf20Sopenharmony_ci#define CM_INTR 0x80000000 1658c2ecf20Sopenharmony_ci#define CM_VCO 0x08000000 /* Voice Control? CMI8738 */ 1668c2ecf20Sopenharmony_ci#define CM_MCBINT 0x04000000 /* Master Control Block abort cond.? */ 1678c2ecf20Sopenharmony_ci#define CM_UARTINT 0x00010000 1688c2ecf20Sopenharmony_ci#define CM_LTDMAINT 0x00008000 1698c2ecf20Sopenharmony_ci#define CM_HTDMAINT 0x00004000 1708c2ecf20Sopenharmony_ci#define CM_XDO46 0x00000080 /* Modell 033? Direct programming EEPROM (read data register) */ 1718c2ecf20Sopenharmony_ci#define CM_LHBTOG 0x00000040 /* High/Low status from DMA ctrl register */ 1728c2ecf20Sopenharmony_ci#define CM_LEG_HDMA 0x00000020 /* Legacy is in High DMA channel */ 1738c2ecf20Sopenharmony_ci#define CM_LEG_STEREO 0x00000010 /* Legacy is in Stereo mode */ 1748c2ecf20Sopenharmony_ci#define CM_CH1BUSY 0x00000008 1758c2ecf20Sopenharmony_ci#define CM_CH0BUSY 0x00000004 1768c2ecf20Sopenharmony_ci#define CM_CHINT1 0x00000002 1778c2ecf20Sopenharmony_ci#define CM_CHINT0 0x00000001 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci#define CM_REG_LEGACY_CTRL 0x14 1808c2ecf20Sopenharmony_ci#define CM_NXCHG 0x80000000 /* don't map base reg dword->sample */ 1818c2ecf20Sopenharmony_ci#define CM_VMPU_MASK 0x60000000 /* MPU401 i/o port address */ 1828c2ecf20Sopenharmony_ci#define CM_VMPU_330 0x00000000 1838c2ecf20Sopenharmony_ci#define CM_VMPU_320 0x20000000 1848c2ecf20Sopenharmony_ci#define CM_VMPU_310 0x40000000 1858c2ecf20Sopenharmony_ci#define CM_VMPU_300 0x60000000 1868c2ecf20Sopenharmony_ci#define CM_ENWR8237 0x10000000 /* enable bus master to write 8237 base reg */ 1878c2ecf20Sopenharmony_ci#define CM_VSBSEL_MASK 0x0C000000 /* SB16 base address */ 1888c2ecf20Sopenharmony_ci#define CM_VSBSEL_220 0x00000000 1898c2ecf20Sopenharmony_ci#define CM_VSBSEL_240 0x04000000 1908c2ecf20Sopenharmony_ci#define CM_VSBSEL_260 0x08000000 1918c2ecf20Sopenharmony_ci#define CM_VSBSEL_280 0x0C000000 1928c2ecf20Sopenharmony_ci#define CM_FMSEL_MASK 0x03000000 /* FM OPL3 base address */ 1938c2ecf20Sopenharmony_ci#define CM_FMSEL_388 0x00000000 1948c2ecf20Sopenharmony_ci#define CM_FMSEL_3C8 0x01000000 1958c2ecf20Sopenharmony_ci#define CM_FMSEL_3E0 0x02000000 1968c2ecf20Sopenharmony_ci#define CM_FMSEL_3E8 0x03000000 1978c2ecf20Sopenharmony_ci#define CM_ENSPDOUT 0x00800000 /* enable XSPDIF/OUT to I/O interface */ 1988c2ecf20Sopenharmony_ci#define CM_SPDCOPYRHT 0x00400000 /* spdif in/out copyright bit */ 1998c2ecf20Sopenharmony_ci#define CM_DAC2SPDO 0x00200000 /* enable wave+fm_midi -> SPDIF/OUT */ 2008c2ecf20Sopenharmony_ci#define CM_INVIDWEN 0x00100000 /* internal vendor ID write enable, model 039? */ 2018c2ecf20Sopenharmony_ci#define CM_SETRETRY 0x00100000 /* 0: legacy i/o wait (default), 1: legacy i/o bus retry */ 2028c2ecf20Sopenharmony_ci#define CM_C_EEACCESS 0x00080000 /* direct programming eeprom regs */ 2038c2ecf20Sopenharmony_ci#define CM_C_EECS 0x00040000 2048c2ecf20Sopenharmony_ci#define CM_C_EEDI46 0x00020000 2058c2ecf20Sopenharmony_ci#define CM_C_EECK46 0x00010000 2068c2ecf20Sopenharmony_ci#define CM_CHB3D6C 0x00008000 /* 5.1 channels support */ 2078c2ecf20Sopenharmony_ci#define CM_CENTR2LIN 0x00004000 /* line-in as center out */ 2088c2ecf20Sopenharmony_ci#define CM_BASE2LIN 0x00002000 /* line-in as bass out */ 2098c2ecf20Sopenharmony_ci#define CM_EXBASEN 0x00001000 /* external bass input enable */ 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci#define CM_REG_MISC_CTRL 0x18 2128c2ecf20Sopenharmony_ci#define CM_PWD 0x80000000 /* power down */ 2138c2ecf20Sopenharmony_ci#define CM_RESET 0x40000000 2148c2ecf20Sopenharmony_ci#define CM_SFIL_MASK 0x30000000 /* filter control at front end DAC, model 037? */ 2158c2ecf20Sopenharmony_ci#define CM_VMGAIN 0x10000000 /* analog master amp +6dB, model 039? */ 2168c2ecf20Sopenharmony_ci#define CM_TXVX 0x08000000 /* model 037? */ 2178c2ecf20Sopenharmony_ci#define CM_N4SPK3D 0x04000000 /* copy front to rear */ 2188c2ecf20Sopenharmony_ci#define CM_SPDO5V 0x02000000 /* 5V spdif output (1 = 0.5v (coax)) */ 2198c2ecf20Sopenharmony_ci#define CM_SPDIF48K 0x01000000 /* write */ 2208c2ecf20Sopenharmony_ci#define CM_SPATUS48K 0x01000000 /* read */ 2218c2ecf20Sopenharmony_ci#define CM_ENDBDAC 0x00800000 /* enable double dac */ 2228c2ecf20Sopenharmony_ci#define CM_XCHGDAC 0x00400000 /* 0: front=ch0, 1: front=ch1 */ 2238c2ecf20Sopenharmony_ci#define CM_SPD32SEL 0x00200000 /* 0: 16bit SPDIF, 1: 32bit */ 2248c2ecf20Sopenharmony_ci#define CM_SPDFLOOPI 0x00100000 /* int. SPDIF-OUT -> int. IN */ 2258c2ecf20Sopenharmony_ci#define CM_FM_EN 0x00080000 /* enable legacy FM */ 2268c2ecf20Sopenharmony_ci#define CM_AC3EN2 0x00040000 /* enable AC3: model 039 */ 2278c2ecf20Sopenharmony_ci#define CM_ENWRASID 0x00010000 /* choose writable internal SUBID (audio) */ 2288c2ecf20Sopenharmony_ci#define CM_VIDWPDSB 0x00010000 /* model 037? */ 2298c2ecf20Sopenharmony_ci#define CM_SPDF_AC97 0x00008000 /* 0: SPDIF/OUT 44.1K, 1: 48K */ 2308c2ecf20Sopenharmony_ci#define CM_MASK_EN 0x00004000 /* activate channel mask on legacy DMA */ 2318c2ecf20Sopenharmony_ci#define CM_ENWRMSID 0x00002000 /* choose writable internal SUBID (modem) */ 2328c2ecf20Sopenharmony_ci#define CM_VIDWPPRT 0x00002000 /* model 037? */ 2338c2ecf20Sopenharmony_ci#define CM_SFILENB 0x00001000 /* filter stepping at front end DAC, model 037? */ 2348c2ecf20Sopenharmony_ci#define CM_MMODE_MASK 0x00000E00 /* model DAA interface mode */ 2358c2ecf20Sopenharmony_ci#define CM_SPDIF_SELECT2 0x00000100 /* for model > 039 ? */ 2368c2ecf20Sopenharmony_ci#define CM_ENCENTER 0x00000080 2378c2ecf20Sopenharmony_ci#define CM_FLINKON 0x00000040 /* force modem link detection on, model 037 */ 2388c2ecf20Sopenharmony_ci#define CM_MUTECH1 0x00000040 /* mute PCI ch1 to DAC */ 2398c2ecf20Sopenharmony_ci#define CM_FLINKOFF 0x00000020 /* force modem link detection off, model 037 */ 2408c2ecf20Sopenharmony_ci#define CM_MIDSMP 0x00000010 /* 1/2 interpolation at front end DAC */ 2418c2ecf20Sopenharmony_ci#define CM_UPDDMA_MASK 0x0000000C /* TDMA position update notification */ 2428c2ecf20Sopenharmony_ci#define CM_UPDDMA_2048 0x00000000 2438c2ecf20Sopenharmony_ci#define CM_UPDDMA_1024 0x00000004 2448c2ecf20Sopenharmony_ci#define CM_UPDDMA_512 0x00000008 2458c2ecf20Sopenharmony_ci#define CM_UPDDMA_256 0x0000000C 2468c2ecf20Sopenharmony_ci#define CM_TWAIT_MASK 0x00000003 /* model 037 */ 2478c2ecf20Sopenharmony_ci#define CM_TWAIT1 0x00000002 /* FM i/o cycle, 0: 48, 1: 64 PCICLKs */ 2488c2ecf20Sopenharmony_ci#define CM_TWAIT0 0x00000001 /* i/o cycle, 0: 4, 1: 6 PCICLKs */ 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci#define CM_REG_TDMA_POSITION 0x1C 2518c2ecf20Sopenharmony_ci#define CM_TDMA_CNT_MASK 0xFFFF0000 /* current byte/word count */ 2528c2ecf20Sopenharmony_ci#define CM_TDMA_ADR_MASK 0x0000FFFF /* current address */ 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* byte */ 2558c2ecf20Sopenharmony_ci#define CM_REG_MIXER0 0x20 2568c2ecf20Sopenharmony_ci#define CM_REG_SBVR 0x20 /* write: sb16 version */ 2578c2ecf20Sopenharmony_ci#define CM_REG_DEV 0x20 /* read: hardware device version */ 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci#define CM_REG_MIXER21 0x21 2608c2ecf20Sopenharmony_ci#define CM_UNKNOWN_21_MASK 0x78 /* ? */ 2618c2ecf20Sopenharmony_ci#define CM_X_ADPCM 0x04 /* SB16 ADPCM enable */ 2628c2ecf20Sopenharmony_ci#define CM_PROINV 0x02 /* SBPro left/right channel switching */ 2638c2ecf20Sopenharmony_ci#define CM_X_SB16 0x01 /* SB16 compatible */ 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci#define CM_REG_SB16_DATA 0x22 2668c2ecf20Sopenharmony_ci#define CM_REG_SB16_ADDR 0x23 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci#define CM_REFFREQ_XIN (315*1000*1000)/22 /* 14.31818 Mhz reference clock frequency pin XIN */ 2698c2ecf20Sopenharmony_ci#define CM_ADCMULT_XIN 512 /* Guessed (487 best for 44.1kHz, not for 88/176kHz) */ 2708c2ecf20Sopenharmony_ci#define CM_TOLERANCE_RATE 0.001 /* Tolerance sample rate pitch (1000ppm) */ 2718c2ecf20Sopenharmony_ci#define CM_MAXIMUM_RATE 80000000 /* Note more than 80MHz */ 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci#define CM_REG_MIXER1 0x24 2748c2ecf20Sopenharmony_ci#define CM_FMMUTE 0x80 /* mute FM */ 2758c2ecf20Sopenharmony_ci#define CM_FMMUTE_SHIFT 7 2768c2ecf20Sopenharmony_ci#define CM_WSMUTE 0x40 /* mute PCM */ 2778c2ecf20Sopenharmony_ci#define CM_WSMUTE_SHIFT 6 2788c2ecf20Sopenharmony_ci#define CM_REAR2LIN 0x20 /* lin-in -> rear line out */ 2798c2ecf20Sopenharmony_ci#define CM_REAR2LIN_SHIFT 5 2808c2ecf20Sopenharmony_ci#define CM_REAR2FRONT 0x10 /* exchange rear/front */ 2818c2ecf20Sopenharmony_ci#define CM_REAR2FRONT_SHIFT 4 2828c2ecf20Sopenharmony_ci#define CM_WAVEINL 0x08 /* digital wave rec. left chan */ 2838c2ecf20Sopenharmony_ci#define CM_WAVEINL_SHIFT 3 2848c2ecf20Sopenharmony_ci#define CM_WAVEINR 0x04 /* digical wave rec. right */ 2858c2ecf20Sopenharmony_ci#define CM_WAVEINR_SHIFT 2 2868c2ecf20Sopenharmony_ci#define CM_X3DEN 0x02 /* 3D surround enable */ 2878c2ecf20Sopenharmony_ci#define CM_X3DEN_SHIFT 1 2888c2ecf20Sopenharmony_ci#define CM_CDPLAY 0x01 /* enable SPDIF/IN PCM -> DAC */ 2898c2ecf20Sopenharmony_ci#define CM_CDPLAY_SHIFT 0 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci#define CM_REG_MIXER2 0x25 2928c2ecf20Sopenharmony_ci#define CM_RAUXREN 0x80 /* AUX right capture */ 2938c2ecf20Sopenharmony_ci#define CM_RAUXREN_SHIFT 7 2948c2ecf20Sopenharmony_ci#define CM_RAUXLEN 0x40 /* AUX left capture */ 2958c2ecf20Sopenharmony_ci#define CM_RAUXLEN_SHIFT 6 2968c2ecf20Sopenharmony_ci#define CM_VAUXRM 0x20 /* AUX right mute */ 2978c2ecf20Sopenharmony_ci#define CM_VAUXRM_SHIFT 5 2988c2ecf20Sopenharmony_ci#define CM_VAUXLM 0x10 /* AUX left mute */ 2998c2ecf20Sopenharmony_ci#define CM_VAUXLM_SHIFT 4 3008c2ecf20Sopenharmony_ci#define CM_VADMIC_MASK 0x0e /* mic gain level (0-3) << 1 */ 3018c2ecf20Sopenharmony_ci#define CM_VADMIC_SHIFT 1 3028c2ecf20Sopenharmony_ci#define CM_MICGAINZ 0x01 /* mic boost */ 3038c2ecf20Sopenharmony_ci#define CM_MICGAINZ_SHIFT 0 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci#define CM_REG_AUX_VOL 0x26 3068c2ecf20Sopenharmony_ci#define CM_VAUXL_MASK 0xf0 3078c2ecf20Sopenharmony_ci#define CM_VAUXR_MASK 0x0f 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci#define CM_REG_MISC 0x27 3108c2ecf20Sopenharmony_ci#define CM_UNKNOWN_27_MASK 0xd8 /* ? */ 3118c2ecf20Sopenharmony_ci#define CM_XGPO1 0x20 3128c2ecf20Sopenharmony_ci// #define CM_XGPBIO 0x04 3138c2ecf20Sopenharmony_ci#define CM_MIC_CENTER_LFE 0x04 /* mic as center/lfe out? (model 039 or later?) */ 3148c2ecf20Sopenharmony_ci#define CM_SPDIF_INVERSE 0x04 /* spdif input phase inverse (model 037) */ 3158c2ecf20Sopenharmony_ci#define CM_SPDVALID 0x02 /* spdif input valid check */ 3168c2ecf20Sopenharmony_ci#define CM_DMAUTO 0x01 /* SB16 DMA auto detect */ 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci#define CM_REG_AC97 0x28 /* hmmm.. do we have ac97 link? */ 3198c2ecf20Sopenharmony_ci/* 3208c2ecf20Sopenharmony_ci * For CMI-8338 (0x28 - 0x2b) .. is this valid for CMI-8738 3218c2ecf20Sopenharmony_ci * or identical with AC97 codec? 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_ci#define CM_REG_EXTERN_CODEC CM_REG_AC97 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci/* 3268c2ecf20Sopenharmony_ci * MPU401 pci port index address 0x40 - 0x4f (CMI-8738 spec ver. 0.6) 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_ci#define CM_REG_MPU_PCI 0x40 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci/* 3318c2ecf20Sopenharmony_ci * FM pci port index address 0x50 - 0x5f (CMI-8738 spec ver. 0.6) 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_ci#define CM_REG_FM_PCI 0x50 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci/* 3368c2ecf20Sopenharmony_ci * access from SB-mixer port 3378c2ecf20Sopenharmony_ci */ 3388c2ecf20Sopenharmony_ci#define CM_REG_EXTENT_IND 0xf0 3398c2ecf20Sopenharmony_ci#define CM_VPHONE_MASK 0xe0 /* Phone volume control (0-3) << 5 */ 3408c2ecf20Sopenharmony_ci#define CM_VPHONE_SHIFT 5 3418c2ecf20Sopenharmony_ci#define CM_VPHOM 0x10 /* Phone mute control */ 3428c2ecf20Sopenharmony_ci#define CM_VSPKM 0x08 /* Speaker mute control, default high */ 3438c2ecf20Sopenharmony_ci#define CM_RLOOPREN 0x04 /* Rec. R-channel enable */ 3448c2ecf20Sopenharmony_ci#define CM_RLOOPLEN 0x02 /* Rec. L-channel enable */ 3458c2ecf20Sopenharmony_ci#define CM_VADMIC3 0x01 /* Mic record boost */ 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci/* 3488c2ecf20Sopenharmony_ci * CMI-8338 spec ver 0.5 (this is not valid for CMI-8738): 3498c2ecf20Sopenharmony_ci * the 8 registers 0xf8 - 0xff are used for programming m/n counter by the PLL 3508c2ecf20Sopenharmony_ci * unit (readonly?). 3518c2ecf20Sopenharmony_ci */ 3528c2ecf20Sopenharmony_ci#define CM_REG_PLL 0xf8 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci/* 3558c2ecf20Sopenharmony_ci * extended registers 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_ci#define CM_REG_CH0_FRAME1 0x80 /* write: base address */ 3588c2ecf20Sopenharmony_ci#define CM_REG_CH0_FRAME2 0x84 /* read: current address */ 3598c2ecf20Sopenharmony_ci#define CM_REG_CH1_FRAME1 0x88 /* 0-15: count of samples at bus master; buffer size */ 3608c2ecf20Sopenharmony_ci#define CM_REG_CH1_FRAME2 0x8C /* 16-31: count of samples at codec; fragment size */ 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci#define CM_REG_EXT_MISC 0x90 3638c2ecf20Sopenharmony_ci#define CM_ADC48K44K 0x10000000 /* ADC parameters group, 0: 44k, 1: 48k */ 3648c2ecf20Sopenharmony_ci#define CM_CHB3D8C 0x00200000 /* 7.1 channels support */ 3658c2ecf20Sopenharmony_ci#define CM_SPD32FMT 0x00100000 /* SPDIF/IN 32k sample rate */ 3668c2ecf20Sopenharmony_ci#define CM_ADC2SPDIF 0x00080000 /* ADC output to SPDIF/OUT */ 3678c2ecf20Sopenharmony_ci#define CM_SHAREADC 0x00040000 /* DAC in ADC as Center/LFE */ 3688c2ecf20Sopenharmony_ci#define CM_REALTCMP 0x00020000 /* monitor the CMPL/CMPR of ADC */ 3698c2ecf20Sopenharmony_ci#define CM_INVLRCK 0x00010000 /* invert ZVPORT's LRCK */ 3708c2ecf20Sopenharmony_ci#define CM_UNKNOWN_90_MASK 0x0000FFFF /* ? */ 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci/* 3738c2ecf20Sopenharmony_ci * size of i/o region 3748c2ecf20Sopenharmony_ci */ 3758c2ecf20Sopenharmony_ci#define CM_EXTENT_CODEC 0x100 3768c2ecf20Sopenharmony_ci#define CM_EXTENT_MIDI 0x2 3778c2ecf20Sopenharmony_ci#define CM_EXTENT_SYNTH 0x4 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci/* 3818c2ecf20Sopenharmony_ci * channels for playback / capture 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_ci#define CM_CH_PLAY 0 3848c2ecf20Sopenharmony_ci#define CM_CH_CAPT 1 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/* 3878c2ecf20Sopenharmony_ci * flags to check device open/close 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_ci#define CM_OPEN_NONE 0 3908c2ecf20Sopenharmony_ci#define CM_OPEN_CH_MASK 0x01 3918c2ecf20Sopenharmony_ci#define CM_OPEN_DAC 0x10 3928c2ecf20Sopenharmony_ci#define CM_OPEN_ADC 0x20 3938c2ecf20Sopenharmony_ci#define CM_OPEN_SPDIF 0x40 3948c2ecf20Sopenharmony_ci#define CM_OPEN_MCHAN 0x80 3958c2ecf20Sopenharmony_ci#define CM_OPEN_PLAYBACK (CM_CH_PLAY | CM_OPEN_DAC) 3968c2ecf20Sopenharmony_ci#define CM_OPEN_PLAYBACK2 (CM_CH_CAPT | CM_OPEN_DAC) 3978c2ecf20Sopenharmony_ci#define CM_OPEN_PLAYBACK_MULTI (CM_CH_PLAY | CM_OPEN_DAC | CM_OPEN_MCHAN) 3988c2ecf20Sopenharmony_ci#define CM_OPEN_CAPTURE (CM_CH_CAPT | CM_OPEN_ADC) 3998c2ecf20Sopenharmony_ci#define CM_OPEN_SPDIF_PLAYBACK (CM_CH_PLAY | CM_OPEN_DAC | CM_OPEN_SPDIF) 4008c2ecf20Sopenharmony_ci#define CM_OPEN_SPDIF_CAPTURE (CM_CH_CAPT | CM_OPEN_ADC | CM_OPEN_SPDIF) 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci#if CM_CH_PLAY == 1 4048c2ecf20Sopenharmony_ci#define CM_PLAYBACK_SRATE_176K CM_CH1_SRATE_176K 4058c2ecf20Sopenharmony_ci#define CM_PLAYBACK_SPDF CM_SPDF_1 4068c2ecf20Sopenharmony_ci#define CM_CAPTURE_SPDF CM_SPDF_0 4078c2ecf20Sopenharmony_ci#else 4088c2ecf20Sopenharmony_ci#define CM_PLAYBACK_SRATE_176K CM_CH0_SRATE_176K 4098c2ecf20Sopenharmony_ci#define CM_PLAYBACK_SPDF CM_SPDF_0 4108c2ecf20Sopenharmony_ci#define CM_CAPTURE_SPDF CM_SPDF_1 4118c2ecf20Sopenharmony_ci#endif 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci/* 4158c2ecf20Sopenharmony_ci * driver data 4168c2ecf20Sopenharmony_ci */ 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistruct cmipci_pcm { 4198c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 4208c2ecf20Sopenharmony_ci u8 running; /* dac/adc running? */ 4218c2ecf20Sopenharmony_ci u8 fmt; /* format bits */ 4228c2ecf20Sopenharmony_ci u8 is_dac; 4238c2ecf20Sopenharmony_ci u8 needs_silencing; 4248c2ecf20Sopenharmony_ci unsigned int dma_size; /* in frames */ 4258c2ecf20Sopenharmony_ci unsigned int shift; 4268c2ecf20Sopenharmony_ci unsigned int ch; /* channel (0/1) */ 4278c2ecf20Sopenharmony_ci unsigned int offset; /* physical address of the buffer */ 4288c2ecf20Sopenharmony_ci}; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci/* mixer elements toggled/resumed during ac3 playback */ 4318c2ecf20Sopenharmony_cistruct cmipci_mixer_auto_switches { 4328c2ecf20Sopenharmony_ci const char *name; /* switch to toggle */ 4338c2ecf20Sopenharmony_ci int toggle_on; /* value to change when ac3 mode */ 4348c2ecf20Sopenharmony_ci}; 4358c2ecf20Sopenharmony_cistatic const struct cmipci_mixer_auto_switches cm_saved_mixer[] = { 4368c2ecf20Sopenharmony_ci {"PCM Playback Switch", 0}, 4378c2ecf20Sopenharmony_ci {"IEC958 Output Switch", 1}, 4388c2ecf20Sopenharmony_ci {"IEC958 Mix Analog", 0}, 4398c2ecf20Sopenharmony_ci // {"IEC958 Out To DAC", 1}, // no longer used 4408c2ecf20Sopenharmony_ci {"IEC958 Loop", 0}, 4418c2ecf20Sopenharmony_ci}; 4428c2ecf20Sopenharmony_ci#define CM_SAVED_MIXERS ARRAY_SIZE(cm_saved_mixer) 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistruct cmipci { 4458c2ecf20Sopenharmony_ci struct snd_card *card; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci struct pci_dev *pci; 4488c2ecf20Sopenharmony_ci unsigned int device; /* device ID */ 4498c2ecf20Sopenharmony_ci int irq; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci unsigned long iobase; 4528c2ecf20Sopenharmony_ci unsigned int ctrl; /* FUNCTRL0 current value */ 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci struct snd_pcm *pcm; /* DAC/ADC PCM */ 4558c2ecf20Sopenharmony_ci struct snd_pcm *pcm2; /* 2nd DAC */ 4568c2ecf20Sopenharmony_ci struct snd_pcm *pcm_spdif; /* SPDIF */ 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci int chip_version; 4598c2ecf20Sopenharmony_ci int max_channels; 4608c2ecf20Sopenharmony_ci unsigned int can_ac3_sw: 1; 4618c2ecf20Sopenharmony_ci unsigned int can_ac3_hw: 1; 4628c2ecf20Sopenharmony_ci unsigned int can_multi_ch: 1; 4638c2ecf20Sopenharmony_ci unsigned int can_96k: 1; /* samplerate above 48k */ 4648c2ecf20Sopenharmony_ci unsigned int do_soft_ac3: 1; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci unsigned int spdif_playback_avail: 1; /* spdif ready? */ 4678c2ecf20Sopenharmony_ci unsigned int spdif_playback_enabled: 1; /* spdif switch enabled? */ 4688c2ecf20Sopenharmony_ci int spdif_counter; /* for software AC3 */ 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci unsigned int dig_status; 4718c2ecf20Sopenharmony_ci unsigned int dig_pcm_status; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci struct snd_pcm_hardware *hw_info[3]; /* for playbacks */ 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci int opened[2]; /* open mode */ 4768c2ecf20Sopenharmony_ci struct mutex open_mutex; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci unsigned int mixer_insensitive: 1; 4798c2ecf20Sopenharmony_ci struct snd_kcontrol *mixer_res_ctl[CM_SAVED_MIXERS]; 4808c2ecf20Sopenharmony_ci int mixer_res_status[CM_SAVED_MIXERS]; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci struct cmipci_pcm channel[2]; /* ch0 - DAC, ch1 - ADC or 2nd DAC */ 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* external MIDI */ 4858c2ecf20Sopenharmony_ci struct snd_rawmidi *rmidi; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci#ifdef SUPPORT_JOYSTICK 4888c2ecf20Sopenharmony_ci struct gameport *gameport; 4898c2ecf20Sopenharmony_ci#endif 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci spinlock_t reg_lock; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4948c2ecf20Sopenharmony_ci unsigned int saved_regs[0x20]; 4958c2ecf20Sopenharmony_ci unsigned char saved_mixers[0x20]; 4968c2ecf20Sopenharmony_ci#endif 4978c2ecf20Sopenharmony_ci}; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci/* read/write operations for dword register */ 5018c2ecf20Sopenharmony_cistatic inline void snd_cmipci_write(struct cmipci *cm, unsigned int cmd, unsigned int data) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci outl(data, cm->iobase + cmd); 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic inline unsigned int snd_cmipci_read(struct cmipci *cm, unsigned int cmd) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci return inl(cm->iobase + cmd); 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci/* read/write operations for word register */ 5128c2ecf20Sopenharmony_cistatic inline void snd_cmipci_write_w(struct cmipci *cm, unsigned int cmd, unsigned short data) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci outw(data, cm->iobase + cmd); 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_cistatic inline unsigned short snd_cmipci_read_w(struct cmipci *cm, unsigned int cmd) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci return inw(cm->iobase + cmd); 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci/* read/write operations for byte register */ 5238c2ecf20Sopenharmony_cistatic inline void snd_cmipci_write_b(struct cmipci *cm, unsigned int cmd, unsigned char data) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci outb(data, cm->iobase + cmd); 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic inline unsigned char snd_cmipci_read_b(struct cmipci *cm, unsigned int cmd) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci return inb(cm->iobase + cmd); 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci/* bit operations for dword register */ 5348c2ecf20Sopenharmony_cistatic int snd_cmipci_set_bit(struct cmipci *cm, unsigned int cmd, unsigned int flag) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci unsigned int val, oval; 5378c2ecf20Sopenharmony_ci val = oval = inl(cm->iobase + cmd); 5388c2ecf20Sopenharmony_ci val |= flag; 5398c2ecf20Sopenharmony_ci if (val == oval) 5408c2ecf20Sopenharmony_ci return 0; 5418c2ecf20Sopenharmony_ci outl(val, cm->iobase + cmd); 5428c2ecf20Sopenharmony_ci return 1; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic int snd_cmipci_clear_bit(struct cmipci *cm, unsigned int cmd, unsigned int flag) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci unsigned int val, oval; 5488c2ecf20Sopenharmony_ci val = oval = inl(cm->iobase + cmd); 5498c2ecf20Sopenharmony_ci val &= ~flag; 5508c2ecf20Sopenharmony_ci if (val == oval) 5518c2ecf20Sopenharmony_ci return 0; 5528c2ecf20Sopenharmony_ci outl(val, cm->iobase + cmd); 5538c2ecf20Sopenharmony_ci return 1; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci/* bit operations for byte register */ 5578c2ecf20Sopenharmony_cistatic int snd_cmipci_set_bit_b(struct cmipci *cm, unsigned int cmd, unsigned char flag) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci unsigned char val, oval; 5608c2ecf20Sopenharmony_ci val = oval = inb(cm->iobase + cmd); 5618c2ecf20Sopenharmony_ci val |= flag; 5628c2ecf20Sopenharmony_ci if (val == oval) 5638c2ecf20Sopenharmony_ci return 0; 5648c2ecf20Sopenharmony_ci outb(val, cm->iobase + cmd); 5658c2ecf20Sopenharmony_ci return 1; 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic int snd_cmipci_clear_bit_b(struct cmipci *cm, unsigned int cmd, unsigned char flag) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci unsigned char val, oval; 5718c2ecf20Sopenharmony_ci val = oval = inb(cm->iobase + cmd); 5728c2ecf20Sopenharmony_ci val &= ~flag; 5738c2ecf20Sopenharmony_ci if (val == oval) 5748c2ecf20Sopenharmony_ci return 0; 5758c2ecf20Sopenharmony_ci outb(val, cm->iobase + cmd); 5768c2ecf20Sopenharmony_ci return 1; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci/* 5818c2ecf20Sopenharmony_ci * PCM interface 5828c2ecf20Sopenharmony_ci */ 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci/* 5858c2ecf20Sopenharmony_ci * calculate frequency 5868c2ecf20Sopenharmony_ci */ 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic const unsigned int rates[] = { 5512, 11025, 22050, 44100, 8000, 16000, 32000, 48000 }; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic unsigned int snd_cmipci_rate_freq(unsigned int rate) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci unsigned int i; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rates); i++) { 5958c2ecf20Sopenharmony_ci if (rates[i] == rate) 5968c2ecf20Sopenharmony_ci return i; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci snd_BUG(); 5998c2ecf20Sopenharmony_ci return 0; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci#ifdef USE_VAR48KRATE 6038c2ecf20Sopenharmony_ci/* 6048c2ecf20Sopenharmony_ci * Determine PLL values for frequency setup, maybe the CMI8338 (CMI8738???) 6058c2ecf20Sopenharmony_ci * does it this way .. maybe not. Never get any information from C-Media about 6068c2ecf20Sopenharmony_ci * that <werner@suse.de>. 6078c2ecf20Sopenharmony_ci */ 6088c2ecf20Sopenharmony_cistatic int snd_cmipci_pll_rmn(unsigned int rate, unsigned int adcmult, int *r, int *m, int *n) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci unsigned int delta, tolerance; 6118c2ecf20Sopenharmony_ci int xm, xn, xr; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci for (*r = 0; rate < CM_MAXIMUM_RATE/adcmult; *r += (1<<5)) 6148c2ecf20Sopenharmony_ci rate <<= 1; 6158c2ecf20Sopenharmony_ci *n = -1; 6168c2ecf20Sopenharmony_ci if (*r > 0xff) 6178c2ecf20Sopenharmony_ci goto out; 6188c2ecf20Sopenharmony_ci tolerance = rate*CM_TOLERANCE_RATE; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci for (xn = (1+2); xn < (0x1f+2); xn++) { 6218c2ecf20Sopenharmony_ci for (xm = (1+2); xm < (0xff+2); xm++) { 6228c2ecf20Sopenharmony_ci xr = ((CM_REFFREQ_XIN/adcmult) * xm) / xn; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (xr < rate) 6258c2ecf20Sopenharmony_ci delta = rate - xr; 6268c2ecf20Sopenharmony_ci else 6278c2ecf20Sopenharmony_ci delta = xr - rate; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* 6308c2ecf20Sopenharmony_ci * If we found one, remember this, 6318c2ecf20Sopenharmony_ci * and try to find a closer one 6328c2ecf20Sopenharmony_ci */ 6338c2ecf20Sopenharmony_ci if (delta < tolerance) { 6348c2ecf20Sopenharmony_ci tolerance = delta; 6358c2ecf20Sopenharmony_ci *m = xm - 2; 6368c2ecf20Sopenharmony_ci *n = xn - 2; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ciout: 6418c2ecf20Sopenharmony_ci return (*n > -1); 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci/* 6458c2ecf20Sopenharmony_ci * Program pll register bits, I assume that the 8 registers 0xf8 up to 0xff 6468c2ecf20Sopenharmony_ci * are mapped onto the 8 ADC/DAC sampling frequency which can be chosen 6478c2ecf20Sopenharmony_ci * at the register CM_REG_FUNCTRL1 (0x04). 6488c2ecf20Sopenharmony_ci * Problem: other ways are also possible (any information about that?) 6498c2ecf20Sopenharmony_ci */ 6508c2ecf20Sopenharmony_cistatic void snd_cmipci_set_pll(struct cmipci *cm, unsigned int rate, unsigned int slot) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci unsigned int reg = CM_REG_PLL + slot; 6538c2ecf20Sopenharmony_ci /* 6548c2ecf20Sopenharmony_ci * Guess that this programs at reg. 0x04 the pos 15:13/12:10 6558c2ecf20Sopenharmony_ci * for DSFC/ASFC (000 up to 111). 6568c2ecf20Sopenharmony_ci */ 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci /* FIXME: Init (Do we've to set an other register first before programming?) */ 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci /* FIXME: Is this correct? Or shouldn't the m/n/r values be used for that? */ 6618c2ecf20Sopenharmony_ci snd_cmipci_write_b(cm, reg, rate>>8); 6628c2ecf20Sopenharmony_ci snd_cmipci_write_b(cm, reg, rate&0xff); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci /* FIXME: Setup (Do we've to set an other register first to enable this?) */ 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci#endif /* USE_VAR48KRATE */ 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic int snd_cmipci_playback2_hw_params(struct snd_pcm_substream *substream, 6698c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 6728c2ecf20Sopenharmony_ci if (params_channels(hw_params) > 2) { 6738c2ecf20Sopenharmony_ci mutex_lock(&cm->open_mutex); 6748c2ecf20Sopenharmony_ci if (cm->opened[CM_CH_PLAY]) { 6758c2ecf20Sopenharmony_ci mutex_unlock(&cm->open_mutex); 6768c2ecf20Sopenharmony_ci return -EBUSY; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci /* reserve the channel A */ 6798c2ecf20Sopenharmony_ci cm->opened[CM_CH_PLAY] = CM_OPEN_PLAYBACK_MULTI; 6808c2ecf20Sopenharmony_ci mutex_unlock(&cm->open_mutex); 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci return 0; 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_cistatic void snd_cmipci_ch_reset(struct cmipci *cm, int ch) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci int reset = CM_RST_CH0 << (cm->channel[ch].ch); 6888c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | reset); 6898c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~reset); 6908c2ecf20Sopenharmony_ci udelay(10); 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci/* 6958c2ecf20Sopenharmony_ci */ 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cistatic const unsigned int hw_channels[] = {1, 2, 4, 6, 8}; 6988c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_channels_4 = { 6998c2ecf20Sopenharmony_ci .count = 3, 7008c2ecf20Sopenharmony_ci .list = hw_channels, 7018c2ecf20Sopenharmony_ci .mask = 0, 7028c2ecf20Sopenharmony_ci}; 7038c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_channels_6 = { 7048c2ecf20Sopenharmony_ci .count = 4, 7058c2ecf20Sopenharmony_ci .list = hw_channels, 7068c2ecf20Sopenharmony_ci .mask = 0, 7078c2ecf20Sopenharmony_ci}; 7088c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_channels_8 = { 7098c2ecf20Sopenharmony_ci .count = 5, 7108c2ecf20Sopenharmony_ci .list = hw_channels, 7118c2ecf20Sopenharmony_ci .mask = 0, 7128c2ecf20Sopenharmony_ci}; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_cistatic int set_dac_channels(struct cmipci *cm, struct cmipci_pcm *rec, int channels) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci if (channels > 2) { 7178c2ecf20Sopenharmony_ci if (!cm->can_multi_ch || !rec->ch) 7188c2ecf20Sopenharmony_ci return -EINVAL; 7198c2ecf20Sopenharmony_ci if (rec->fmt != 0x03) /* stereo 16bit only */ 7208c2ecf20Sopenharmony_ci return -EINVAL; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (cm->can_multi_ch) { 7248c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 7258c2ecf20Sopenharmony_ci if (channels > 2) { 7268c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG); 7278c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC); 7288c2ecf20Sopenharmony_ci } else { 7298c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG); 7308c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC); 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci if (channels == 8) 7338c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_EXT_MISC, CM_CHB3D8C); 7348c2ecf20Sopenharmony_ci else 7358c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_EXT_MISC, CM_CHB3D8C); 7368c2ecf20Sopenharmony_ci if (channels == 6) { 7378c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C); 7388c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C); 7398c2ecf20Sopenharmony_ci } else { 7408c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C); 7418c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C); 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci if (channels == 4) 7448c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D); 7458c2ecf20Sopenharmony_ci else 7468c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D); 7478c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci return 0; 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci/* 7548c2ecf20Sopenharmony_ci * prepare playback/capture channel 7558c2ecf20Sopenharmony_ci * channel to be used must have been set in rec->ch. 7568c2ecf20Sopenharmony_ci */ 7578c2ecf20Sopenharmony_cistatic int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec, 7588c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci unsigned int reg, freq, freq_ext, val; 7618c2ecf20Sopenharmony_ci unsigned int period_size; 7628c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci rec->fmt = 0; 7658c2ecf20Sopenharmony_ci rec->shift = 0; 7668c2ecf20Sopenharmony_ci if (snd_pcm_format_width(runtime->format) >= 16) { 7678c2ecf20Sopenharmony_ci rec->fmt |= 0x02; 7688c2ecf20Sopenharmony_ci if (snd_pcm_format_width(runtime->format) > 16) 7698c2ecf20Sopenharmony_ci rec->shift++; /* 24/32bit */ 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci if (runtime->channels > 1) 7728c2ecf20Sopenharmony_ci rec->fmt |= 0x01; 7738c2ecf20Sopenharmony_ci if (rec->is_dac && set_dac_channels(cm, rec, runtime->channels) < 0) { 7748c2ecf20Sopenharmony_ci dev_dbg(cm->card->dev, "cannot set dac channels\n"); 7758c2ecf20Sopenharmony_ci return -EINVAL; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci rec->offset = runtime->dma_addr; 7798c2ecf20Sopenharmony_ci /* buffer and period sizes in frame */ 7808c2ecf20Sopenharmony_ci rec->dma_size = runtime->buffer_size << rec->shift; 7818c2ecf20Sopenharmony_ci period_size = runtime->period_size << rec->shift; 7828c2ecf20Sopenharmony_ci if (runtime->channels > 2) { 7838c2ecf20Sopenharmony_ci /* multi-channels */ 7848c2ecf20Sopenharmony_ci rec->dma_size = (rec->dma_size * runtime->channels) / 2; 7858c2ecf20Sopenharmony_ci period_size = (period_size * runtime->channels) / 2; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci /* set buffer address */ 7918c2ecf20Sopenharmony_ci reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1; 7928c2ecf20Sopenharmony_ci snd_cmipci_write(cm, reg, rec->offset); 7938c2ecf20Sopenharmony_ci /* program sample counts */ 7948c2ecf20Sopenharmony_ci reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2; 7958c2ecf20Sopenharmony_ci snd_cmipci_write_w(cm, reg, rec->dma_size - 1); 7968c2ecf20Sopenharmony_ci snd_cmipci_write_w(cm, reg + 2, period_size - 1); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci /* set adc/dac flag */ 7998c2ecf20Sopenharmony_ci val = rec->ch ? CM_CHADC1 : CM_CHADC0; 8008c2ecf20Sopenharmony_ci if (rec->is_dac) 8018c2ecf20Sopenharmony_ci cm->ctrl &= ~val; 8028c2ecf20Sopenharmony_ci else 8038c2ecf20Sopenharmony_ci cm->ctrl |= val; 8048c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); 8058c2ecf20Sopenharmony_ci /* dev_dbg(cm->card->dev, "functrl0 = %08x\n", cm->ctrl); */ 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci /* set sample rate */ 8088c2ecf20Sopenharmony_ci freq = 0; 8098c2ecf20Sopenharmony_ci freq_ext = 0; 8108c2ecf20Sopenharmony_ci if (runtime->rate > 48000) 8118c2ecf20Sopenharmony_ci switch (runtime->rate) { 8128c2ecf20Sopenharmony_ci case 88200: freq_ext = CM_CH0_SRATE_88K; break; 8138c2ecf20Sopenharmony_ci case 96000: freq_ext = CM_CH0_SRATE_96K; break; 8148c2ecf20Sopenharmony_ci case 128000: freq_ext = CM_CH0_SRATE_128K; break; 8158c2ecf20Sopenharmony_ci default: snd_BUG(); break; 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci else 8188c2ecf20Sopenharmony_ci freq = snd_cmipci_rate_freq(runtime->rate); 8198c2ecf20Sopenharmony_ci val = snd_cmipci_read(cm, CM_REG_FUNCTRL1); 8208c2ecf20Sopenharmony_ci if (rec->ch) { 8218c2ecf20Sopenharmony_ci val &= ~CM_DSFC_MASK; 8228c2ecf20Sopenharmony_ci val |= (freq << CM_DSFC_SHIFT) & CM_DSFC_MASK; 8238c2ecf20Sopenharmony_ci } else { 8248c2ecf20Sopenharmony_ci val &= ~CM_ASFC_MASK; 8258c2ecf20Sopenharmony_ci val |= (freq << CM_ASFC_SHIFT) & CM_ASFC_MASK; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_FUNCTRL1, val); 8288c2ecf20Sopenharmony_ci dev_dbg(cm->card->dev, "functrl1 = %08x\n", val); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci /* set format */ 8318c2ecf20Sopenharmony_ci val = snd_cmipci_read(cm, CM_REG_CHFORMAT); 8328c2ecf20Sopenharmony_ci if (rec->ch) { 8338c2ecf20Sopenharmony_ci val &= ~CM_CH1FMT_MASK; 8348c2ecf20Sopenharmony_ci val |= rec->fmt << CM_CH1FMT_SHIFT; 8358c2ecf20Sopenharmony_ci } else { 8368c2ecf20Sopenharmony_ci val &= ~CM_CH0FMT_MASK; 8378c2ecf20Sopenharmony_ci val |= rec->fmt << CM_CH0FMT_SHIFT; 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci if (cm->can_96k) { 8408c2ecf20Sopenharmony_ci val &= ~(CM_CH0_SRATE_MASK << (rec->ch * 2)); 8418c2ecf20Sopenharmony_ci val |= freq_ext << (rec->ch * 2); 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_CHFORMAT, val); 8448c2ecf20Sopenharmony_ci dev_dbg(cm->card->dev, "chformat = %08x\n", val); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (!rec->is_dac && cm->chip_version) { 8478c2ecf20Sopenharmony_ci if (runtime->rate > 44100) 8488c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_EXT_MISC, CM_ADC48K44K); 8498c2ecf20Sopenharmony_ci else 8508c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_EXT_MISC, CM_ADC48K44K); 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci rec->running = 0; 8548c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci return 0; 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci/* 8608c2ecf20Sopenharmony_ci * PCM trigger/stop 8618c2ecf20Sopenharmony_ci */ 8628c2ecf20Sopenharmony_cistatic int snd_cmipci_pcm_trigger(struct cmipci *cm, struct cmipci_pcm *rec, 8638c2ecf20Sopenharmony_ci int cmd) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci unsigned int inthld, chen, reset, pause; 8668c2ecf20Sopenharmony_ci int result = 0; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci inthld = CM_CH0_INT_EN << rec->ch; 8698c2ecf20Sopenharmony_ci chen = CM_CHEN0 << rec->ch; 8708c2ecf20Sopenharmony_ci reset = CM_RST_CH0 << rec->ch; 8718c2ecf20Sopenharmony_ci pause = CM_PAUSE0 << rec->ch; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci spin_lock(&cm->reg_lock); 8748c2ecf20Sopenharmony_ci switch (cmd) { 8758c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 8768c2ecf20Sopenharmony_ci rec->running = 1; 8778c2ecf20Sopenharmony_ci /* set interrupt */ 8788c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, inthld); 8798c2ecf20Sopenharmony_ci cm->ctrl |= chen; 8808c2ecf20Sopenharmony_ci /* enable channel */ 8818c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); 8828c2ecf20Sopenharmony_ci dev_dbg(cm->card->dev, "functrl0 = %08x\n", cm->ctrl); 8838c2ecf20Sopenharmony_ci break; 8848c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 8858c2ecf20Sopenharmony_ci rec->running = 0; 8868c2ecf20Sopenharmony_ci /* disable interrupt */ 8878c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, inthld); 8888c2ecf20Sopenharmony_ci /* reset */ 8898c2ecf20Sopenharmony_ci cm->ctrl &= ~chen; 8908c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | reset); 8918c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~reset); 8928c2ecf20Sopenharmony_ci rec->needs_silencing = rec->is_dac; 8938c2ecf20Sopenharmony_ci break; 8948c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 8958c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 8968c2ecf20Sopenharmony_ci cm->ctrl |= pause; 8978c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); 8988c2ecf20Sopenharmony_ci break; 8998c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 9008c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 9018c2ecf20Sopenharmony_ci cm->ctrl &= ~pause; 9028c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); 9038c2ecf20Sopenharmony_ci break; 9048c2ecf20Sopenharmony_ci default: 9058c2ecf20Sopenharmony_ci result = -EINVAL; 9068c2ecf20Sopenharmony_ci break; 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci spin_unlock(&cm->reg_lock); 9098c2ecf20Sopenharmony_ci return result; 9108c2ecf20Sopenharmony_ci} 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci/* 9138c2ecf20Sopenharmony_ci * return the current pointer 9148c2ecf20Sopenharmony_ci */ 9158c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_cmipci_pcm_pointer(struct cmipci *cm, struct cmipci_pcm *rec, 9168c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 9178c2ecf20Sopenharmony_ci{ 9188c2ecf20Sopenharmony_ci size_t ptr; 9198c2ecf20Sopenharmony_ci unsigned int reg, rem, tries; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci if (!rec->running) 9228c2ecf20Sopenharmony_ci return 0; 9238c2ecf20Sopenharmony_ci#if 1 // this seems better.. 9248c2ecf20Sopenharmony_ci reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2; 9258c2ecf20Sopenharmony_ci for (tries = 0; tries < 3; tries++) { 9268c2ecf20Sopenharmony_ci rem = snd_cmipci_read_w(cm, reg); 9278c2ecf20Sopenharmony_ci if (rem < rec->dma_size) 9288c2ecf20Sopenharmony_ci goto ok; 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci dev_err(cm->card->dev, "invalid PCM pointer: %#x\n", rem); 9318c2ecf20Sopenharmony_ci return SNDRV_PCM_POS_XRUN; 9328c2ecf20Sopenharmony_ciok: 9338c2ecf20Sopenharmony_ci ptr = (rec->dma_size - (rem + 1)) >> rec->shift; 9348c2ecf20Sopenharmony_ci#else 9358c2ecf20Sopenharmony_ci reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1; 9368c2ecf20Sopenharmony_ci ptr = snd_cmipci_read(cm, reg) - rec->offset; 9378c2ecf20Sopenharmony_ci ptr = bytes_to_frames(substream->runtime, ptr); 9388c2ecf20Sopenharmony_ci#endif 9398c2ecf20Sopenharmony_ci if (substream->runtime->channels > 2) 9408c2ecf20Sopenharmony_ci ptr = (ptr * 2) / substream->runtime->channels; 9418c2ecf20Sopenharmony_ci return ptr; 9428c2ecf20Sopenharmony_ci} 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci/* 9458c2ecf20Sopenharmony_ci * playback 9468c2ecf20Sopenharmony_ci */ 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_cistatic int snd_cmipci_playback_trigger(struct snd_pcm_substream *substream, 9498c2ecf20Sopenharmony_ci int cmd) 9508c2ecf20Sopenharmony_ci{ 9518c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 9528c2ecf20Sopenharmony_ci return snd_cmipci_pcm_trigger(cm, &cm->channel[CM_CH_PLAY], cmd); 9538c2ecf20Sopenharmony_ci} 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_cmipci_playback_pointer(struct snd_pcm_substream *substream) 9568c2ecf20Sopenharmony_ci{ 9578c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 9588c2ecf20Sopenharmony_ci return snd_cmipci_pcm_pointer(cm, &cm->channel[CM_CH_PLAY], substream); 9598c2ecf20Sopenharmony_ci} 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci/* 9648c2ecf20Sopenharmony_ci * capture 9658c2ecf20Sopenharmony_ci */ 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_cistatic int snd_cmipci_capture_trigger(struct snd_pcm_substream *substream, 9688c2ecf20Sopenharmony_ci int cmd) 9698c2ecf20Sopenharmony_ci{ 9708c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 9718c2ecf20Sopenharmony_ci return snd_cmipci_pcm_trigger(cm, &cm->channel[CM_CH_CAPT], cmd); 9728c2ecf20Sopenharmony_ci} 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_cmipci_capture_pointer(struct snd_pcm_substream *substream) 9758c2ecf20Sopenharmony_ci{ 9768c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 9778c2ecf20Sopenharmony_ci return snd_cmipci_pcm_pointer(cm, &cm->channel[CM_CH_CAPT], substream); 9788c2ecf20Sopenharmony_ci} 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci/* 9828c2ecf20Sopenharmony_ci * hw preparation for spdif 9838c2ecf20Sopenharmony_ci */ 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_cistatic int snd_cmipci_spdif_default_info(struct snd_kcontrol *kcontrol, 9868c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 9878c2ecf20Sopenharmony_ci{ 9888c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 9898c2ecf20Sopenharmony_ci uinfo->count = 1; 9908c2ecf20Sopenharmony_ci return 0; 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_cistatic int snd_cmipci_spdif_default_get(struct snd_kcontrol *kcontrol, 9948c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci struct cmipci *chip = snd_kcontrol_chip(kcontrol); 9978c2ecf20Sopenharmony_ci int i; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 10008c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 10018c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[i] = (chip->dig_status >> (i * 8)) & 0xff; 10028c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 10038c2ecf20Sopenharmony_ci return 0; 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_cistatic int snd_cmipci_spdif_default_put(struct snd_kcontrol *kcontrol, 10078c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 10088c2ecf20Sopenharmony_ci{ 10098c2ecf20Sopenharmony_ci struct cmipci *chip = snd_kcontrol_chip(kcontrol); 10108c2ecf20Sopenharmony_ci int i, change; 10118c2ecf20Sopenharmony_ci unsigned int val; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci val = 0; 10148c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 10158c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 10168c2ecf20Sopenharmony_ci val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); 10178c2ecf20Sopenharmony_ci change = val != chip->dig_status; 10188c2ecf20Sopenharmony_ci chip->dig_status = val; 10198c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 10208c2ecf20Sopenharmony_ci return change; 10218c2ecf20Sopenharmony_ci} 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_cmipci_spdif_default = 10248c2ecf20Sopenharmony_ci{ 10258c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 10268c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), 10278c2ecf20Sopenharmony_ci .info = snd_cmipci_spdif_default_info, 10288c2ecf20Sopenharmony_ci .get = snd_cmipci_spdif_default_get, 10298c2ecf20Sopenharmony_ci .put = snd_cmipci_spdif_default_put 10308c2ecf20Sopenharmony_ci}; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_cistatic int snd_cmipci_spdif_mask_info(struct snd_kcontrol *kcontrol, 10338c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 10348c2ecf20Sopenharmony_ci{ 10358c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 10368c2ecf20Sopenharmony_ci uinfo->count = 1; 10378c2ecf20Sopenharmony_ci return 0; 10388c2ecf20Sopenharmony_ci} 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_cistatic int snd_cmipci_spdif_mask_get(struct snd_kcontrol *kcontrol, 10418c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 10428c2ecf20Sopenharmony_ci{ 10438c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[0] = 0xff; 10448c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[1] = 0xff; 10458c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[2] = 0xff; 10468c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[3] = 0xff; 10478c2ecf20Sopenharmony_ci return 0; 10488c2ecf20Sopenharmony_ci} 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_cmipci_spdif_mask = 10518c2ecf20Sopenharmony_ci{ 10528c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 10538c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 10548c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), 10558c2ecf20Sopenharmony_ci .info = snd_cmipci_spdif_mask_info, 10568c2ecf20Sopenharmony_ci .get = snd_cmipci_spdif_mask_get, 10578c2ecf20Sopenharmony_ci}; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_cistatic int snd_cmipci_spdif_stream_info(struct snd_kcontrol *kcontrol, 10608c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 10618c2ecf20Sopenharmony_ci{ 10628c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 10638c2ecf20Sopenharmony_ci uinfo->count = 1; 10648c2ecf20Sopenharmony_ci return 0; 10658c2ecf20Sopenharmony_ci} 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_cistatic int snd_cmipci_spdif_stream_get(struct snd_kcontrol *kcontrol, 10688c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 10698c2ecf20Sopenharmony_ci{ 10708c2ecf20Sopenharmony_ci struct cmipci *chip = snd_kcontrol_chip(kcontrol); 10718c2ecf20Sopenharmony_ci int i; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 10748c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 10758c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[i] = (chip->dig_pcm_status >> (i * 8)) & 0xff; 10768c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 10778c2ecf20Sopenharmony_ci return 0; 10788c2ecf20Sopenharmony_ci} 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_cistatic int snd_cmipci_spdif_stream_put(struct snd_kcontrol *kcontrol, 10818c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 10828c2ecf20Sopenharmony_ci{ 10838c2ecf20Sopenharmony_ci struct cmipci *chip = snd_kcontrol_chip(kcontrol); 10848c2ecf20Sopenharmony_ci int i, change; 10858c2ecf20Sopenharmony_ci unsigned int val; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci val = 0; 10888c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 10898c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 10908c2ecf20Sopenharmony_ci val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); 10918c2ecf20Sopenharmony_ci change = val != chip->dig_pcm_status; 10928c2ecf20Sopenharmony_ci chip->dig_pcm_status = val; 10938c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 10948c2ecf20Sopenharmony_ci return change; 10958c2ecf20Sopenharmony_ci} 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_cmipci_spdif_stream = 10988c2ecf20Sopenharmony_ci{ 10998c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, 11008c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 11018c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), 11028c2ecf20Sopenharmony_ci .info = snd_cmipci_spdif_stream_info, 11038c2ecf20Sopenharmony_ci .get = snd_cmipci_spdif_stream_get, 11048c2ecf20Sopenharmony_ci .put = snd_cmipci_spdif_stream_put 11058c2ecf20Sopenharmony_ci}; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci/* 11088c2ecf20Sopenharmony_ci */ 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci/* save mixer setting and mute for AC3 playback */ 11118c2ecf20Sopenharmony_cistatic int save_mixer_state(struct cmipci *cm) 11128c2ecf20Sopenharmony_ci{ 11138c2ecf20Sopenharmony_ci if (! cm->mixer_insensitive) { 11148c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *val; 11158c2ecf20Sopenharmony_ci unsigned int i; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci val = kmalloc(sizeof(*val), GFP_KERNEL); 11188c2ecf20Sopenharmony_ci if (!val) 11198c2ecf20Sopenharmony_ci return -ENOMEM; 11208c2ecf20Sopenharmony_ci for (i = 0; i < CM_SAVED_MIXERS; i++) { 11218c2ecf20Sopenharmony_ci struct snd_kcontrol *ctl = cm->mixer_res_ctl[i]; 11228c2ecf20Sopenharmony_ci if (ctl) { 11238c2ecf20Sopenharmony_ci int event; 11248c2ecf20Sopenharmony_ci memset(val, 0, sizeof(*val)); 11258c2ecf20Sopenharmony_ci ctl->get(ctl, val); 11268c2ecf20Sopenharmony_ci cm->mixer_res_status[i] = val->value.integer.value[0]; 11278c2ecf20Sopenharmony_ci val->value.integer.value[0] = cm_saved_mixer[i].toggle_on; 11288c2ecf20Sopenharmony_ci event = SNDRV_CTL_EVENT_MASK_INFO; 11298c2ecf20Sopenharmony_ci if (cm->mixer_res_status[i] != val->value.integer.value[0]) { 11308c2ecf20Sopenharmony_ci ctl->put(ctl, val); /* toggle */ 11318c2ecf20Sopenharmony_ci event |= SNDRV_CTL_EVENT_MASK_VALUE; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; 11348c2ecf20Sopenharmony_ci snd_ctl_notify(cm->card, event, &ctl->id); 11358c2ecf20Sopenharmony_ci } 11368c2ecf20Sopenharmony_ci } 11378c2ecf20Sopenharmony_ci kfree(val); 11388c2ecf20Sopenharmony_ci cm->mixer_insensitive = 1; 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci return 0; 11418c2ecf20Sopenharmony_ci} 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci/* restore the previously saved mixer status */ 11458c2ecf20Sopenharmony_cistatic void restore_mixer_state(struct cmipci *cm) 11468c2ecf20Sopenharmony_ci{ 11478c2ecf20Sopenharmony_ci if (cm->mixer_insensitive) { 11488c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *val; 11498c2ecf20Sopenharmony_ci unsigned int i; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci val = kmalloc(sizeof(*val), GFP_KERNEL); 11528c2ecf20Sopenharmony_ci if (!val) 11538c2ecf20Sopenharmony_ci return; 11548c2ecf20Sopenharmony_ci cm->mixer_insensitive = 0; /* at first clear this; 11558c2ecf20Sopenharmony_ci otherwise the changes will be ignored */ 11568c2ecf20Sopenharmony_ci for (i = 0; i < CM_SAVED_MIXERS; i++) { 11578c2ecf20Sopenharmony_ci struct snd_kcontrol *ctl = cm->mixer_res_ctl[i]; 11588c2ecf20Sopenharmony_ci if (ctl) { 11598c2ecf20Sopenharmony_ci int event; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci memset(val, 0, sizeof(*val)); 11628c2ecf20Sopenharmony_ci ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 11638c2ecf20Sopenharmony_ci ctl->get(ctl, val); 11648c2ecf20Sopenharmony_ci event = SNDRV_CTL_EVENT_MASK_INFO; 11658c2ecf20Sopenharmony_ci if (val->value.integer.value[0] != cm->mixer_res_status[i]) { 11668c2ecf20Sopenharmony_ci val->value.integer.value[0] = cm->mixer_res_status[i]; 11678c2ecf20Sopenharmony_ci ctl->put(ctl, val); 11688c2ecf20Sopenharmony_ci event |= SNDRV_CTL_EVENT_MASK_VALUE; 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci snd_ctl_notify(cm->card, event, &ctl->id); 11718c2ecf20Sopenharmony_ci } 11728c2ecf20Sopenharmony_ci } 11738c2ecf20Sopenharmony_ci kfree(val); 11748c2ecf20Sopenharmony_ci } 11758c2ecf20Sopenharmony_ci} 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci/* spinlock held! */ 11788c2ecf20Sopenharmony_cistatic void setup_ac3(struct cmipci *cm, struct snd_pcm_substream *subs, int do_ac3, int rate) 11798c2ecf20Sopenharmony_ci{ 11808c2ecf20Sopenharmony_ci if (do_ac3) { 11818c2ecf20Sopenharmony_ci /* AC3EN for 037 */ 11828c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_AC3EN1); 11838c2ecf20Sopenharmony_ci /* AC3EN for 039 */ 11848c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_AC3EN2); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci if (cm->can_ac3_hw) { 11878c2ecf20Sopenharmony_ci /* SPD24SEL for 037, 0x02 */ 11888c2ecf20Sopenharmony_ci /* SPD24SEL for 039, 0x20, but cannot be set */ 11898c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL); 11908c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); 11918c2ecf20Sopenharmony_ci } else { /* can_ac3_sw */ 11928c2ecf20Sopenharmony_ci /* SPD32SEL for 037 & 039, 0x20 */ 11938c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); 11948c2ecf20Sopenharmony_ci /* set 176K sample rate to fix 033 HW bug */ 11958c2ecf20Sopenharmony_ci if (cm->chip_version == 33) { 11968c2ecf20Sopenharmony_ci if (rate >= 48000) { 11978c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K); 11988c2ecf20Sopenharmony_ci } else { 11998c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K); 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci } 12028c2ecf20Sopenharmony_ci } 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci } else { 12058c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_AC3EN1); 12068c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_AC3EN2); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci if (cm->can_ac3_hw) { 12098c2ecf20Sopenharmony_ci /* chip model >= 37 */ 12108c2ecf20Sopenharmony_ci if (snd_pcm_format_width(subs->runtime->format) > 16) { 12118c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); 12128c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL); 12138c2ecf20Sopenharmony_ci } else { 12148c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); 12158c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL); 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci } else { 12188c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); 12198c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_SPD24SEL); 12208c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K); 12218c2ecf20Sopenharmony_ci } 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci} 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_cistatic int setup_spdif_playback(struct cmipci *cm, struct snd_pcm_substream *subs, int up, int do_ac3) 12268c2ecf20Sopenharmony_ci{ 12278c2ecf20Sopenharmony_ci int rate, err; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci rate = subs->runtime->rate; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci if (up && do_ac3) 12328c2ecf20Sopenharmony_ci if ((err = save_mixer_state(cm)) < 0) 12338c2ecf20Sopenharmony_ci return err; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 12368c2ecf20Sopenharmony_ci cm->spdif_playback_avail = up; 12378c2ecf20Sopenharmony_ci if (up) { 12388c2ecf20Sopenharmony_ci /* they are controlled via "IEC958 Output Switch" */ 12398c2ecf20Sopenharmony_ci /* snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); */ 12408c2ecf20Sopenharmony_ci /* snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */ 12418c2ecf20Sopenharmony_ci if (cm->spdif_playback_enabled) 12428c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); 12438c2ecf20Sopenharmony_ci setup_ac3(cm, subs, do_ac3, rate); 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci if (rate == 48000 || rate == 96000) 12468c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K | CM_SPDF_AC97); 12478c2ecf20Sopenharmony_ci else 12488c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K | CM_SPDF_AC97); 12498c2ecf20Sopenharmony_ci if (rate > 48000) 12508c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS); 12518c2ecf20Sopenharmony_ci else 12528c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS); 12538c2ecf20Sopenharmony_ci } else { 12548c2ecf20Sopenharmony_ci /* they are controlled via "IEC958 Output Switch" */ 12558c2ecf20Sopenharmony_ci /* snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); */ 12568c2ecf20Sopenharmony_ci /* snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */ 12578c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS); 12588c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); 12598c2ecf20Sopenharmony_ci setup_ac3(cm, subs, 0, 0); 12608c2ecf20Sopenharmony_ci } 12618c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 12628c2ecf20Sopenharmony_ci return 0; 12638c2ecf20Sopenharmony_ci} 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci/* 12678c2ecf20Sopenharmony_ci * preparation 12688c2ecf20Sopenharmony_ci */ 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci/* playback - enable spdif only on the certain condition */ 12718c2ecf20Sopenharmony_cistatic int snd_cmipci_playback_prepare(struct snd_pcm_substream *substream) 12728c2ecf20Sopenharmony_ci{ 12738c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 12748c2ecf20Sopenharmony_ci int rate = substream->runtime->rate; 12758c2ecf20Sopenharmony_ci int err, do_spdif, do_ac3 = 0; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci do_spdif = (rate >= 44100 && rate <= 96000 && 12788c2ecf20Sopenharmony_ci substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE && 12798c2ecf20Sopenharmony_ci substream->runtime->channels == 2); 12808c2ecf20Sopenharmony_ci if (do_spdif && cm->can_ac3_hw) 12818c2ecf20Sopenharmony_ci do_ac3 = cm->dig_pcm_status & IEC958_AES0_NONAUDIO; 12828c2ecf20Sopenharmony_ci if ((err = setup_spdif_playback(cm, substream, do_spdif, do_ac3)) < 0) 12838c2ecf20Sopenharmony_ci return err; 12848c2ecf20Sopenharmony_ci return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream); 12858c2ecf20Sopenharmony_ci} 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci/* playback (via device #2) - enable spdif always */ 12888c2ecf20Sopenharmony_cistatic int snd_cmipci_playback_spdif_prepare(struct snd_pcm_substream *substream) 12898c2ecf20Sopenharmony_ci{ 12908c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 12918c2ecf20Sopenharmony_ci int err, do_ac3; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci if (cm->can_ac3_hw) 12948c2ecf20Sopenharmony_ci do_ac3 = cm->dig_pcm_status & IEC958_AES0_NONAUDIO; 12958c2ecf20Sopenharmony_ci else 12968c2ecf20Sopenharmony_ci do_ac3 = 1; /* doesn't matter */ 12978c2ecf20Sopenharmony_ci if ((err = setup_spdif_playback(cm, substream, 1, do_ac3)) < 0) 12988c2ecf20Sopenharmony_ci return err; 12998c2ecf20Sopenharmony_ci return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_PLAY], substream); 13008c2ecf20Sopenharmony_ci} 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci/* 13038c2ecf20Sopenharmony_ci * Apparently, the samples last played on channel A stay in some buffer, even 13048c2ecf20Sopenharmony_ci * after the channel is reset, and get added to the data for the rear DACs when 13058c2ecf20Sopenharmony_ci * playing a multichannel stream on channel B. This is likely to generate 13068c2ecf20Sopenharmony_ci * wraparounds and thus distortions. 13078c2ecf20Sopenharmony_ci * To avoid this, we play at least one zero sample after the actual stream has 13088c2ecf20Sopenharmony_ci * stopped. 13098c2ecf20Sopenharmony_ci */ 13108c2ecf20Sopenharmony_cistatic void snd_cmipci_silence_hack(struct cmipci *cm, struct cmipci_pcm *rec) 13118c2ecf20Sopenharmony_ci{ 13128c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = rec->substream->runtime; 13138c2ecf20Sopenharmony_ci unsigned int reg, val; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci if (rec->needs_silencing && runtime && runtime->dma_area) { 13168c2ecf20Sopenharmony_ci /* set up a small silence buffer */ 13178c2ecf20Sopenharmony_ci memset(runtime->dma_area, 0, PAGE_SIZE); 13188c2ecf20Sopenharmony_ci reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2; 13198c2ecf20Sopenharmony_ci val = ((PAGE_SIZE / 4) - 1) | (((PAGE_SIZE / 4) / 2 - 1) << 16); 13208c2ecf20Sopenharmony_ci snd_cmipci_write(cm, reg, val); 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci /* configure for 16 bits, 2 channels, 8 kHz */ 13238c2ecf20Sopenharmony_ci if (runtime->channels > 2) 13248c2ecf20Sopenharmony_ci set_dac_channels(cm, rec, 2); 13258c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 13268c2ecf20Sopenharmony_ci val = snd_cmipci_read(cm, CM_REG_FUNCTRL1); 13278c2ecf20Sopenharmony_ci val &= ~(CM_ASFC_MASK << (rec->ch * 3)); 13288c2ecf20Sopenharmony_ci val |= (4 << CM_ASFC_SHIFT) << (rec->ch * 3); 13298c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_FUNCTRL1, val); 13308c2ecf20Sopenharmony_ci val = snd_cmipci_read(cm, CM_REG_CHFORMAT); 13318c2ecf20Sopenharmony_ci val &= ~(CM_CH0FMT_MASK << (rec->ch * 2)); 13328c2ecf20Sopenharmony_ci val |= (3 << CM_CH0FMT_SHIFT) << (rec->ch * 2); 13338c2ecf20Sopenharmony_ci if (cm->can_96k) 13348c2ecf20Sopenharmony_ci val &= ~(CM_CH0_SRATE_MASK << (rec->ch * 2)); 13358c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_CHFORMAT, val); 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci /* start stream (we don't need interrupts) */ 13388c2ecf20Sopenharmony_ci cm->ctrl |= CM_CHEN0 << rec->ch; 13398c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl); 13408c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci msleep(1); 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci /* stop and reset stream */ 13458c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 13468c2ecf20Sopenharmony_ci cm->ctrl &= ~(CM_CHEN0 << rec->ch); 13478c2ecf20Sopenharmony_ci val = CM_RST_CH0 << rec->ch; 13488c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | val); 13498c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~val); 13508c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci rec->needs_silencing = 0; 13538c2ecf20Sopenharmony_ci } 13548c2ecf20Sopenharmony_ci} 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_cistatic int snd_cmipci_playback_hw_free(struct snd_pcm_substream *substream) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 13598c2ecf20Sopenharmony_ci setup_spdif_playback(cm, substream, 0, 0); 13608c2ecf20Sopenharmony_ci restore_mixer_state(cm); 13618c2ecf20Sopenharmony_ci snd_cmipci_silence_hack(cm, &cm->channel[0]); 13628c2ecf20Sopenharmony_ci return 0; 13638c2ecf20Sopenharmony_ci} 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_cistatic int snd_cmipci_playback2_hw_free(struct snd_pcm_substream *substream) 13668c2ecf20Sopenharmony_ci{ 13678c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 13688c2ecf20Sopenharmony_ci snd_cmipci_silence_hack(cm, &cm->channel[1]); 13698c2ecf20Sopenharmony_ci return 0; 13708c2ecf20Sopenharmony_ci} 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci/* capture */ 13738c2ecf20Sopenharmony_cistatic int snd_cmipci_capture_prepare(struct snd_pcm_substream *substream) 13748c2ecf20Sopenharmony_ci{ 13758c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 13768c2ecf20Sopenharmony_ci return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream); 13778c2ecf20Sopenharmony_ci} 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci/* capture with spdif (via device #2) */ 13808c2ecf20Sopenharmony_cistatic int snd_cmipci_capture_spdif_prepare(struct snd_pcm_substream *substream) 13818c2ecf20Sopenharmony_ci{ 13828c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 13858c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF); 13868c2ecf20Sopenharmony_ci if (cm->can_96k) { 13878c2ecf20Sopenharmony_ci if (substream->runtime->rate > 48000) 13888c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS); 13898c2ecf20Sopenharmony_ci else 13908c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_DBLSPDS); 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci if (snd_pcm_format_width(substream->runtime->format) > 16) 13938c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); 13948c2ecf20Sopenharmony_ci else 13958c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci return snd_cmipci_pcm_prepare(cm, &cm->channel[CM_CH_CAPT], substream); 14008c2ecf20Sopenharmony_ci} 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_cistatic int snd_cmipci_capture_spdif_hw_free(struct snd_pcm_substream *subs) 14038c2ecf20Sopenharmony_ci{ 14048c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(subs); 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 14078c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_CAPTURE_SPDF); 14088c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_SPD32SEL); 14098c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci return 0; 14128c2ecf20Sopenharmony_ci} 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci/* 14168c2ecf20Sopenharmony_ci * interrupt handler 14178c2ecf20Sopenharmony_ci */ 14188c2ecf20Sopenharmony_cistatic irqreturn_t snd_cmipci_interrupt(int irq, void *dev_id) 14198c2ecf20Sopenharmony_ci{ 14208c2ecf20Sopenharmony_ci struct cmipci *cm = dev_id; 14218c2ecf20Sopenharmony_ci unsigned int status, mask = 0; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci /* fastpath out, to ease interrupt sharing */ 14248c2ecf20Sopenharmony_ci status = snd_cmipci_read(cm, CM_REG_INT_STATUS); 14258c2ecf20Sopenharmony_ci if (!(status & CM_INTR)) 14268c2ecf20Sopenharmony_ci return IRQ_NONE; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci /* acknowledge interrupt */ 14298c2ecf20Sopenharmony_ci spin_lock(&cm->reg_lock); 14308c2ecf20Sopenharmony_ci if (status & CM_CHINT0) 14318c2ecf20Sopenharmony_ci mask |= CM_CH0_INT_EN; 14328c2ecf20Sopenharmony_ci if (status & CM_CHINT1) 14338c2ecf20Sopenharmony_ci mask |= CM_CH1_INT_EN; 14348c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, mask); 14358c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, mask); 14368c2ecf20Sopenharmony_ci spin_unlock(&cm->reg_lock); 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci if (cm->rmidi && (status & CM_UARTINT)) 14398c2ecf20Sopenharmony_ci snd_mpu401_uart_interrupt(irq, cm->rmidi->private_data); 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci if (cm->pcm) { 14428c2ecf20Sopenharmony_ci if ((status & CM_CHINT0) && cm->channel[0].running) 14438c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(cm->channel[0].substream); 14448c2ecf20Sopenharmony_ci if ((status & CM_CHINT1) && cm->channel[1].running) 14458c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(cm->channel[1].substream); 14468c2ecf20Sopenharmony_ci } 14478c2ecf20Sopenharmony_ci return IRQ_HANDLED; 14488c2ecf20Sopenharmony_ci} 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci/* 14518c2ecf20Sopenharmony_ci * h/w infos 14528c2ecf20Sopenharmony_ci */ 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci/* playback on channel A */ 14558c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_cmipci_playback = 14568c2ecf20Sopenharmony_ci{ 14578c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 14588c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | 14598c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), 14608c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 14618c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000, 14628c2ecf20Sopenharmony_ci .rate_min = 5512, 14638c2ecf20Sopenharmony_ci .rate_max = 48000, 14648c2ecf20Sopenharmony_ci .channels_min = 1, 14658c2ecf20Sopenharmony_ci .channels_max = 2, 14668c2ecf20Sopenharmony_ci .buffer_bytes_max = (128*1024), 14678c2ecf20Sopenharmony_ci .period_bytes_min = 64, 14688c2ecf20Sopenharmony_ci .period_bytes_max = (128*1024), 14698c2ecf20Sopenharmony_ci .periods_min = 2, 14708c2ecf20Sopenharmony_ci .periods_max = 1024, 14718c2ecf20Sopenharmony_ci .fifo_size = 0, 14728c2ecf20Sopenharmony_ci}; 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci/* capture on channel B */ 14758c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_cmipci_capture = 14768c2ecf20Sopenharmony_ci{ 14778c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 14788c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | 14798c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), 14808c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 14818c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000, 14828c2ecf20Sopenharmony_ci .rate_min = 5512, 14838c2ecf20Sopenharmony_ci .rate_max = 48000, 14848c2ecf20Sopenharmony_ci .channels_min = 1, 14858c2ecf20Sopenharmony_ci .channels_max = 2, 14868c2ecf20Sopenharmony_ci .buffer_bytes_max = (128*1024), 14878c2ecf20Sopenharmony_ci .period_bytes_min = 64, 14888c2ecf20Sopenharmony_ci .period_bytes_max = (128*1024), 14898c2ecf20Sopenharmony_ci .periods_min = 2, 14908c2ecf20Sopenharmony_ci .periods_max = 1024, 14918c2ecf20Sopenharmony_ci .fifo_size = 0, 14928c2ecf20Sopenharmony_ci}; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci/* playback on channel B - stereo 16bit only? */ 14958c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_cmipci_playback2 = 14968c2ecf20Sopenharmony_ci{ 14978c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 14988c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | 14998c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), 15008c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 15018c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000, 15028c2ecf20Sopenharmony_ci .rate_min = 5512, 15038c2ecf20Sopenharmony_ci .rate_max = 48000, 15048c2ecf20Sopenharmony_ci .channels_min = 2, 15058c2ecf20Sopenharmony_ci .channels_max = 2, 15068c2ecf20Sopenharmony_ci .buffer_bytes_max = (128*1024), 15078c2ecf20Sopenharmony_ci .period_bytes_min = 64, 15088c2ecf20Sopenharmony_ci .period_bytes_max = (128*1024), 15098c2ecf20Sopenharmony_ci .periods_min = 2, 15108c2ecf20Sopenharmony_ci .periods_max = 1024, 15118c2ecf20Sopenharmony_ci .fifo_size = 0, 15128c2ecf20Sopenharmony_ci}; 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci/* spdif playback on channel A */ 15158c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_cmipci_playback_spdif = 15168c2ecf20Sopenharmony_ci{ 15178c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 15188c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | 15198c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), 15208c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 15218c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, 15228c2ecf20Sopenharmony_ci .rate_min = 44100, 15238c2ecf20Sopenharmony_ci .rate_max = 48000, 15248c2ecf20Sopenharmony_ci .channels_min = 2, 15258c2ecf20Sopenharmony_ci .channels_max = 2, 15268c2ecf20Sopenharmony_ci .buffer_bytes_max = (128*1024), 15278c2ecf20Sopenharmony_ci .period_bytes_min = 64, 15288c2ecf20Sopenharmony_ci .period_bytes_max = (128*1024), 15298c2ecf20Sopenharmony_ci .periods_min = 2, 15308c2ecf20Sopenharmony_ci .periods_max = 1024, 15318c2ecf20Sopenharmony_ci .fifo_size = 0, 15328c2ecf20Sopenharmony_ci}; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci/* spdif playback on channel A (32bit, IEC958 subframes) */ 15358c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_cmipci_playback_iec958_subframe = 15368c2ecf20Sopenharmony_ci{ 15378c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 15388c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | 15398c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), 15408c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, 15418c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, 15428c2ecf20Sopenharmony_ci .rate_min = 44100, 15438c2ecf20Sopenharmony_ci .rate_max = 48000, 15448c2ecf20Sopenharmony_ci .channels_min = 2, 15458c2ecf20Sopenharmony_ci .channels_max = 2, 15468c2ecf20Sopenharmony_ci .buffer_bytes_max = (128*1024), 15478c2ecf20Sopenharmony_ci .period_bytes_min = 64, 15488c2ecf20Sopenharmony_ci .period_bytes_max = (128*1024), 15498c2ecf20Sopenharmony_ci .periods_min = 2, 15508c2ecf20Sopenharmony_ci .periods_max = 1024, 15518c2ecf20Sopenharmony_ci .fifo_size = 0, 15528c2ecf20Sopenharmony_ci}; 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci/* spdif capture on channel B */ 15558c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_cmipci_capture_spdif = 15568c2ecf20Sopenharmony_ci{ 15578c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 15588c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE | 15598c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), 15608c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | 15618c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, 15628c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, 15638c2ecf20Sopenharmony_ci .rate_min = 44100, 15648c2ecf20Sopenharmony_ci .rate_max = 48000, 15658c2ecf20Sopenharmony_ci .channels_min = 2, 15668c2ecf20Sopenharmony_ci .channels_max = 2, 15678c2ecf20Sopenharmony_ci .buffer_bytes_max = (128*1024), 15688c2ecf20Sopenharmony_ci .period_bytes_min = 64, 15698c2ecf20Sopenharmony_ci .period_bytes_max = (128*1024), 15708c2ecf20Sopenharmony_ci .periods_min = 2, 15718c2ecf20Sopenharmony_ci .periods_max = 1024, 15728c2ecf20Sopenharmony_ci .fifo_size = 0, 15738c2ecf20Sopenharmony_ci}; 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_cistatic const unsigned int rate_constraints[] = { 5512, 8000, 11025, 16000, 22050, 15768c2ecf20Sopenharmony_ci 32000, 44100, 48000, 88200, 96000, 128000 }; 15778c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_rates = { 15788c2ecf20Sopenharmony_ci .count = ARRAY_SIZE(rate_constraints), 15798c2ecf20Sopenharmony_ci .list = rate_constraints, 15808c2ecf20Sopenharmony_ci .mask = 0, 15818c2ecf20Sopenharmony_ci}; 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci/* 15848c2ecf20Sopenharmony_ci * check device open/close 15858c2ecf20Sopenharmony_ci */ 15868c2ecf20Sopenharmony_cistatic int open_device_check(struct cmipci *cm, int mode, struct snd_pcm_substream *subs) 15878c2ecf20Sopenharmony_ci{ 15888c2ecf20Sopenharmony_ci int ch = mode & CM_OPEN_CH_MASK; 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci /* FIXME: a file should wait until the device becomes free 15918c2ecf20Sopenharmony_ci * when it's opened on blocking mode. however, since the current 15928c2ecf20Sopenharmony_ci * pcm framework doesn't pass file pointer before actually opened, 15938c2ecf20Sopenharmony_ci * we can't know whether blocking mode or not in open callback.. 15948c2ecf20Sopenharmony_ci */ 15958c2ecf20Sopenharmony_ci mutex_lock(&cm->open_mutex); 15968c2ecf20Sopenharmony_ci if (cm->opened[ch]) { 15978c2ecf20Sopenharmony_ci mutex_unlock(&cm->open_mutex); 15988c2ecf20Sopenharmony_ci return -EBUSY; 15998c2ecf20Sopenharmony_ci } 16008c2ecf20Sopenharmony_ci cm->opened[ch] = mode; 16018c2ecf20Sopenharmony_ci cm->channel[ch].substream = subs; 16028c2ecf20Sopenharmony_ci if (! (mode & CM_OPEN_DAC)) { 16038c2ecf20Sopenharmony_ci /* disable dual DAC mode */ 16048c2ecf20Sopenharmony_ci cm->channel[ch].is_dac = 0; 16058c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 16068c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC); 16078c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 16088c2ecf20Sopenharmony_ci } 16098c2ecf20Sopenharmony_ci mutex_unlock(&cm->open_mutex); 16108c2ecf20Sopenharmony_ci return 0; 16118c2ecf20Sopenharmony_ci} 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_cistatic void close_device_check(struct cmipci *cm, int mode) 16148c2ecf20Sopenharmony_ci{ 16158c2ecf20Sopenharmony_ci int ch = mode & CM_OPEN_CH_MASK; 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci mutex_lock(&cm->open_mutex); 16188c2ecf20Sopenharmony_ci if (cm->opened[ch] == mode) { 16198c2ecf20Sopenharmony_ci if (cm->channel[ch].substream) { 16208c2ecf20Sopenharmony_ci snd_cmipci_ch_reset(cm, ch); 16218c2ecf20Sopenharmony_ci cm->channel[ch].running = 0; 16228c2ecf20Sopenharmony_ci cm->channel[ch].substream = NULL; 16238c2ecf20Sopenharmony_ci } 16248c2ecf20Sopenharmony_ci cm->opened[ch] = 0; 16258c2ecf20Sopenharmony_ci if (! cm->channel[ch].is_dac) { 16268c2ecf20Sopenharmony_ci /* enable dual DAC mode again */ 16278c2ecf20Sopenharmony_ci cm->channel[ch].is_dac = 1; 16288c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 16298c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC); 16308c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci mutex_unlock(&cm->open_mutex); 16348c2ecf20Sopenharmony_ci} 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci/* 16378c2ecf20Sopenharmony_ci */ 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_cistatic int snd_cmipci_playback_open(struct snd_pcm_substream *substream) 16408c2ecf20Sopenharmony_ci{ 16418c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 16428c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 16438c2ecf20Sopenharmony_ci int err; 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci if ((err = open_device_check(cm, CM_OPEN_PLAYBACK, substream)) < 0) 16468c2ecf20Sopenharmony_ci return err; 16478c2ecf20Sopenharmony_ci runtime->hw = snd_cmipci_playback; 16488c2ecf20Sopenharmony_ci if (cm->chip_version == 68) { 16498c2ecf20Sopenharmony_ci runtime->hw.rates |= SNDRV_PCM_RATE_88200 | 16508c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_96000; 16518c2ecf20Sopenharmony_ci runtime->hw.rate_max = 96000; 16528c2ecf20Sopenharmony_ci } else if (cm->chip_version == 55) { 16538c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_list(runtime, 0, 16548c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); 16558c2ecf20Sopenharmony_ci if (err < 0) 16568c2ecf20Sopenharmony_ci return err; 16578c2ecf20Sopenharmony_ci runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; 16588c2ecf20Sopenharmony_ci runtime->hw.rate_max = 128000; 16598c2ecf20Sopenharmony_ci } 16608c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); 16618c2ecf20Sopenharmony_ci cm->dig_pcm_status = cm->dig_status; 16628c2ecf20Sopenharmony_ci return 0; 16638c2ecf20Sopenharmony_ci} 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_cistatic int snd_cmipci_capture_open(struct snd_pcm_substream *substream) 16668c2ecf20Sopenharmony_ci{ 16678c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 16688c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 16698c2ecf20Sopenharmony_ci int err; 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci if ((err = open_device_check(cm, CM_OPEN_CAPTURE, substream)) < 0) 16728c2ecf20Sopenharmony_ci return err; 16738c2ecf20Sopenharmony_ci runtime->hw = snd_cmipci_capture; 16748c2ecf20Sopenharmony_ci if (cm->chip_version == 68) { // 8768 only supports 44k/48k recording 16758c2ecf20Sopenharmony_ci runtime->hw.rate_min = 41000; 16768c2ecf20Sopenharmony_ci runtime->hw.rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000; 16778c2ecf20Sopenharmony_ci } else if (cm->chip_version == 55) { 16788c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_list(runtime, 0, 16798c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); 16808c2ecf20Sopenharmony_ci if (err < 0) 16818c2ecf20Sopenharmony_ci return err; 16828c2ecf20Sopenharmony_ci runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; 16838c2ecf20Sopenharmony_ci runtime->hw.rate_max = 128000; 16848c2ecf20Sopenharmony_ci } 16858c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); 16868c2ecf20Sopenharmony_ci return 0; 16878c2ecf20Sopenharmony_ci} 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_cistatic int snd_cmipci_playback2_open(struct snd_pcm_substream *substream) 16908c2ecf20Sopenharmony_ci{ 16918c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 16928c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 16938c2ecf20Sopenharmony_ci int err; 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci if ((err = open_device_check(cm, CM_OPEN_PLAYBACK2, substream)) < 0) /* use channel B */ 16968c2ecf20Sopenharmony_ci return err; 16978c2ecf20Sopenharmony_ci runtime->hw = snd_cmipci_playback2; 16988c2ecf20Sopenharmony_ci mutex_lock(&cm->open_mutex); 16998c2ecf20Sopenharmony_ci if (! cm->opened[CM_CH_PLAY]) { 17008c2ecf20Sopenharmony_ci if (cm->can_multi_ch) { 17018c2ecf20Sopenharmony_ci runtime->hw.channels_max = cm->max_channels; 17028c2ecf20Sopenharmony_ci if (cm->max_channels == 4) 17038c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_4); 17048c2ecf20Sopenharmony_ci else if (cm->max_channels == 6) 17058c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_6); 17068c2ecf20Sopenharmony_ci else if (cm->max_channels == 8) 17078c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels_8); 17088c2ecf20Sopenharmony_ci } 17098c2ecf20Sopenharmony_ci } 17108c2ecf20Sopenharmony_ci mutex_unlock(&cm->open_mutex); 17118c2ecf20Sopenharmony_ci if (cm->chip_version == 68) { 17128c2ecf20Sopenharmony_ci runtime->hw.rates |= SNDRV_PCM_RATE_88200 | 17138c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_96000; 17148c2ecf20Sopenharmony_ci runtime->hw.rate_max = 96000; 17158c2ecf20Sopenharmony_ci } else if (cm->chip_version == 55) { 17168c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_list(runtime, 0, 17178c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); 17188c2ecf20Sopenharmony_ci if (err < 0) 17198c2ecf20Sopenharmony_ci return err; 17208c2ecf20Sopenharmony_ci runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; 17218c2ecf20Sopenharmony_ci runtime->hw.rate_max = 128000; 17228c2ecf20Sopenharmony_ci } 17238c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x10000); 17248c2ecf20Sopenharmony_ci return 0; 17258c2ecf20Sopenharmony_ci} 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_cistatic int snd_cmipci_playback_spdif_open(struct snd_pcm_substream *substream) 17288c2ecf20Sopenharmony_ci{ 17298c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 17308c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 17318c2ecf20Sopenharmony_ci int err; 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci if ((err = open_device_check(cm, CM_OPEN_SPDIF_PLAYBACK, substream)) < 0) /* use channel A */ 17348c2ecf20Sopenharmony_ci return err; 17358c2ecf20Sopenharmony_ci if (cm->can_ac3_hw) { 17368c2ecf20Sopenharmony_ci runtime->hw = snd_cmipci_playback_spdif; 17378c2ecf20Sopenharmony_ci if (cm->chip_version >= 37) { 17388c2ecf20Sopenharmony_ci runtime->hw.formats |= SNDRV_PCM_FMTBIT_S32_LE; 17398c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); 17408c2ecf20Sopenharmony_ci } 17418c2ecf20Sopenharmony_ci if (cm->can_96k) { 17428c2ecf20Sopenharmony_ci runtime->hw.rates |= SNDRV_PCM_RATE_88200 | 17438c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_96000; 17448c2ecf20Sopenharmony_ci runtime->hw.rate_max = 96000; 17458c2ecf20Sopenharmony_ci } 17468c2ecf20Sopenharmony_ci } else { 17478c2ecf20Sopenharmony_ci runtime->hw = snd_cmipci_playback_iec958_subframe; 17488c2ecf20Sopenharmony_ci } 17498c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000); 17508c2ecf20Sopenharmony_ci cm->dig_pcm_status = cm->dig_status; 17518c2ecf20Sopenharmony_ci return 0; 17528c2ecf20Sopenharmony_ci} 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_cistatic int snd_cmipci_capture_spdif_open(struct snd_pcm_substream *substream) 17558c2ecf20Sopenharmony_ci{ 17568c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 17578c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 17588c2ecf20Sopenharmony_ci int err; 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci if ((err = open_device_check(cm, CM_OPEN_SPDIF_CAPTURE, substream)) < 0) /* use channel B */ 17618c2ecf20Sopenharmony_ci return err; 17628c2ecf20Sopenharmony_ci runtime->hw = snd_cmipci_capture_spdif; 17638c2ecf20Sopenharmony_ci if (cm->can_96k && !(cm->chip_version == 68)) { 17648c2ecf20Sopenharmony_ci runtime->hw.rates |= SNDRV_PCM_RATE_88200 | 17658c2ecf20Sopenharmony_ci SNDRV_PCM_RATE_96000; 17668c2ecf20Sopenharmony_ci runtime->hw.rate_max = 96000; 17678c2ecf20Sopenharmony_ci } 17688c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 0x40000); 17698c2ecf20Sopenharmony_ci return 0; 17708c2ecf20Sopenharmony_ci} 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci/* 17748c2ecf20Sopenharmony_ci */ 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_cistatic int snd_cmipci_playback_close(struct snd_pcm_substream *substream) 17778c2ecf20Sopenharmony_ci{ 17788c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 17798c2ecf20Sopenharmony_ci close_device_check(cm, CM_OPEN_PLAYBACK); 17808c2ecf20Sopenharmony_ci return 0; 17818c2ecf20Sopenharmony_ci} 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_cistatic int snd_cmipci_capture_close(struct snd_pcm_substream *substream) 17848c2ecf20Sopenharmony_ci{ 17858c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 17868c2ecf20Sopenharmony_ci close_device_check(cm, CM_OPEN_CAPTURE); 17878c2ecf20Sopenharmony_ci return 0; 17888c2ecf20Sopenharmony_ci} 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_cistatic int snd_cmipci_playback2_close(struct snd_pcm_substream *substream) 17918c2ecf20Sopenharmony_ci{ 17928c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 17938c2ecf20Sopenharmony_ci close_device_check(cm, CM_OPEN_PLAYBACK2); 17948c2ecf20Sopenharmony_ci close_device_check(cm, CM_OPEN_PLAYBACK_MULTI); 17958c2ecf20Sopenharmony_ci return 0; 17968c2ecf20Sopenharmony_ci} 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_cistatic int snd_cmipci_playback_spdif_close(struct snd_pcm_substream *substream) 17998c2ecf20Sopenharmony_ci{ 18008c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 18018c2ecf20Sopenharmony_ci close_device_check(cm, CM_OPEN_SPDIF_PLAYBACK); 18028c2ecf20Sopenharmony_ci return 0; 18038c2ecf20Sopenharmony_ci} 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_cistatic int snd_cmipci_capture_spdif_close(struct snd_pcm_substream *substream) 18068c2ecf20Sopenharmony_ci{ 18078c2ecf20Sopenharmony_ci struct cmipci *cm = snd_pcm_substream_chip(substream); 18088c2ecf20Sopenharmony_ci close_device_check(cm, CM_OPEN_SPDIF_CAPTURE); 18098c2ecf20Sopenharmony_ci return 0; 18108c2ecf20Sopenharmony_ci} 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci/* 18148c2ecf20Sopenharmony_ci */ 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_cmipci_playback_ops = { 18178c2ecf20Sopenharmony_ci .open = snd_cmipci_playback_open, 18188c2ecf20Sopenharmony_ci .close = snd_cmipci_playback_close, 18198c2ecf20Sopenharmony_ci .hw_free = snd_cmipci_playback_hw_free, 18208c2ecf20Sopenharmony_ci .prepare = snd_cmipci_playback_prepare, 18218c2ecf20Sopenharmony_ci .trigger = snd_cmipci_playback_trigger, 18228c2ecf20Sopenharmony_ci .pointer = snd_cmipci_playback_pointer, 18238c2ecf20Sopenharmony_ci}; 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_cmipci_capture_ops = { 18268c2ecf20Sopenharmony_ci .open = snd_cmipci_capture_open, 18278c2ecf20Sopenharmony_ci .close = snd_cmipci_capture_close, 18288c2ecf20Sopenharmony_ci .prepare = snd_cmipci_capture_prepare, 18298c2ecf20Sopenharmony_ci .trigger = snd_cmipci_capture_trigger, 18308c2ecf20Sopenharmony_ci .pointer = snd_cmipci_capture_pointer, 18318c2ecf20Sopenharmony_ci}; 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_cmipci_playback2_ops = { 18348c2ecf20Sopenharmony_ci .open = snd_cmipci_playback2_open, 18358c2ecf20Sopenharmony_ci .close = snd_cmipci_playback2_close, 18368c2ecf20Sopenharmony_ci .hw_params = snd_cmipci_playback2_hw_params, 18378c2ecf20Sopenharmony_ci .hw_free = snd_cmipci_playback2_hw_free, 18388c2ecf20Sopenharmony_ci .prepare = snd_cmipci_capture_prepare, /* channel B */ 18398c2ecf20Sopenharmony_ci .trigger = snd_cmipci_capture_trigger, /* channel B */ 18408c2ecf20Sopenharmony_ci .pointer = snd_cmipci_capture_pointer, /* channel B */ 18418c2ecf20Sopenharmony_ci}; 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_cmipci_playback_spdif_ops = { 18448c2ecf20Sopenharmony_ci .open = snd_cmipci_playback_spdif_open, 18458c2ecf20Sopenharmony_ci .close = snd_cmipci_playback_spdif_close, 18468c2ecf20Sopenharmony_ci .hw_free = snd_cmipci_playback_hw_free, 18478c2ecf20Sopenharmony_ci .prepare = snd_cmipci_playback_spdif_prepare, /* set up rate */ 18488c2ecf20Sopenharmony_ci .trigger = snd_cmipci_playback_trigger, 18498c2ecf20Sopenharmony_ci .pointer = snd_cmipci_playback_pointer, 18508c2ecf20Sopenharmony_ci}; 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_cmipci_capture_spdif_ops = { 18538c2ecf20Sopenharmony_ci .open = snd_cmipci_capture_spdif_open, 18548c2ecf20Sopenharmony_ci .close = snd_cmipci_capture_spdif_close, 18558c2ecf20Sopenharmony_ci .hw_free = snd_cmipci_capture_spdif_hw_free, 18568c2ecf20Sopenharmony_ci .prepare = snd_cmipci_capture_spdif_prepare, 18578c2ecf20Sopenharmony_ci .trigger = snd_cmipci_capture_trigger, 18588c2ecf20Sopenharmony_ci .pointer = snd_cmipci_capture_pointer, 18598c2ecf20Sopenharmony_ci}; 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci/* 18638c2ecf20Sopenharmony_ci */ 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_cistatic int snd_cmipci_pcm_new(struct cmipci *cm, int device) 18668c2ecf20Sopenharmony_ci{ 18678c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 18688c2ecf20Sopenharmony_ci int err; 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 1, &pcm); 18718c2ecf20Sopenharmony_ci if (err < 0) 18728c2ecf20Sopenharmony_ci return err; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_ops); 18758c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cmipci_capture_ops); 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci pcm->private_data = cm; 18788c2ecf20Sopenharmony_ci pcm->info_flags = 0; 18798c2ecf20Sopenharmony_ci strcpy(pcm->name, "C-Media PCI DAC/ADC"); 18808c2ecf20Sopenharmony_ci cm->pcm = pcm; 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 18838c2ecf20Sopenharmony_ci &cm->pci->dev, 64*1024, 128*1024); 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci return 0; 18868c2ecf20Sopenharmony_ci} 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_cistatic int snd_cmipci_pcm2_new(struct cmipci *cm, int device) 18898c2ecf20Sopenharmony_ci{ 18908c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 18918c2ecf20Sopenharmony_ci int err; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 0, &pcm); 18948c2ecf20Sopenharmony_ci if (err < 0) 18958c2ecf20Sopenharmony_ci return err; 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback2_ops); 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci pcm->private_data = cm; 19008c2ecf20Sopenharmony_ci pcm->info_flags = 0; 19018c2ecf20Sopenharmony_ci strcpy(pcm->name, "C-Media PCI 2nd DAC"); 19028c2ecf20Sopenharmony_ci cm->pcm2 = pcm; 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 19058c2ecf20Sopenharmony_ci &cm->pci->dev, 64*1024, 128*1024); 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci return 0; 19088c2ecf20Sopenharmony_ci} 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_cistatic int snd_cmipci_pcm_spdif_new(struct cmipci *cm, int device) 19118c2ecf20Sopenharmony_ci{ 19128c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 19138c2ecf20Sopenharmony_ci int err; 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci err = snd_pcm_new(cm->card, cm->card->driver, device, 1, 1, &pcm); 19168c2ecf20Sopenharmony_ci if (err < 0) 19178c2ecf20Sopenharmony_ci return err; 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cmipci_playback_spdif_ops); 19208c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cmipci_capture_spdif_ops); 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci pcm->private_data = cm; 19238c2ecf20Sopenharmony_ci pcm->info_flags = 0; 19248c2ecf20Sopenharmony_ci strcpy(pcm->name, "C-Media PCI IEC958"); 19258c2ecf20Sopenharmony_ci cm->pcm_spdif = pcm; 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 19288c2ecf20Sopenharmony_ci &cm->pci->dev, 64*1024, 128*1024); 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, 19318c2ecf20Sopenharmony_ci snd_pcm_alt_chmaps, cm->max_channels, 0, 19328c2ecf20Sopenharmony_ci NULL); 19338c2ecf20Sopenharmony_ci if (err < 0) 19348c2ecf20Sopenharmony_ci return err; 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci return 0; 19378c2ecf20Sopenharmony_ci} 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci/* 19408c2ecf20Sopenharmony_ci * mixer interface: 19418c2ecf20Sopenharmony_ci * - CM8338/8738 has a compatible mixer interface with SB16, but 19428c2ecf20Sopenharmony_ci * lack of some elements like tone control, i/o gain and AGC. 19438c2ecf20Sopenharmony_ci * - Access to native registers: 19448c2ecf20Sopenharmony_ci * - A 3D switch 19458c2ecf20Sopenharmony_ci * - Output mute switches 19468c2ecf20Sopenharmony_ci */ 19478c2ecf20Sopenharmony_ci 19488c2ecf20Sopenharmony_cistatic void snd_cmipci_mixer_write(struct cmipci *s, unsigned char idx, unsigned char data) 19498c2ecf20Sopenharmony_ci{ 19508c2ecf20Sopenharmony_ci outb(idx, s->iobase + CM_REG_SB16_ADDR); 19518c2ecf20Sopenharmony_ci outb(data, s->iobase + CM_REG_SB16_DATA); 19528c2ecf20Sopenharmony_ci} 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_cistatic unsigned char snd_cmipci_mixer_read(struct cmipci *s, unsigned char idx) 19558c2ecf20Sopenharmony_ci{ 19568c2ecf20Sopenharmony_ci unsigned char v; 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci outb(idx, s->iobase + CM_REG_SB16_ADDR); 19598c2ecf20Sopenharmony_ci v = inb(s->iobase + CM_REG_SB16_DATA); 19608c2ecf20Sopenharmony_ci return v; 19618c2ecf20Sopenharmony_ci} 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci/* 19648c2ecf20Sopenharmony_ci * general mixer element 19658c2ecf20Sopenharmony_ci */ 19668c2ecf20Sopenharmony_cistruct cmipci_sb_reg { 19678c2ecf20Sopenharmony_ci unsigned int left_reg, right_reg; 19688c2ecf20Sopenharmony_ci unsigned int left_shift, right_shift; 19698c2ecf20Sopenharmony_ci unsigned int mask; 19708c2ecf20Sopenharmony_ci unsigned int invert: 1; 19718c2ecf20Sopenharmony_ci unsigned int stereo: 1; 19728c2ecf20Sopenharmony_ci}; 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci#define COMPOSE_SB_REG(lreg,rreg,lshift,rshift,mask,invert,stereo) \ 19758c2ecf20Sopenharmony_ci ((lreg) | ((rreg) << 8) | (lshift << 16) | (rshift << 19) | (mask << 24) | (invert << 22) | (stereo << 23)) 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_ci#define CMIPCI_DOUBLE(xname, left_reg, right_reg, left_shift, right_shift, mask, invert, stereo) \ 19788c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 19798c2ecf20Sopenharmony_ci .info = snd_cmipci_info_volume, \ 19808c2ecf20Sopenharmony_ci .get = snd_cmipci_get_volume, .put = snd_cmipci_put_volume, \ 19818c2ecf20Sopenharmony_ci .private_value = COMPOSE_SB_REG(left_reg, right_reg, left_shift, right_shift, mask, invert, stereo), \ 19828c2ecf20Sopenharmony_ci} 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_ci#define CMIPCI_SB_VOL_STEREO(xname,reg,shift,mask) CMIPCI_DOUBLE(xname, reg, reg+1, shift, shift, mask, 0, 1) 19858c2ecf20Sopenharmony_ci#define CMIPCI_SB_VOL_MONO(xname,reg,shift,mask) CMIPCI_DOUBLE(xname, reg, reg, shift, shift, mask, 0, 0) 19868c2ecf20Sopenharmony_ci#define CMIPCI_SB_SW_STEREO(xname,lshift,rshift) CMIPCI_DOUBLE(xname, SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, lshift, rshift, 1, 0, 1) 19878c2ecf20Sopenharmony_ci#define CMIPCI_SB_SW_MONO(xname,shift) CMIPCI_DOUBLE(xname, SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, shift, shift, 1, 0, 0) 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_cistatic void cmipci_sb_reg_decode(struct cmipci_sb_reg *r, unsigned long val) 19908c2ecf20Sopenharmony_ci{ 19918c2ecf20Sopenharmony_ci r->left_reg = val & 0xff; 19928c2ecf20Sopenharmony_ci r->right_reg = (val >> 8) & 0xff; 19938c2ecf20Sopenharmony_ci r->left_shift = (val >> 16) & 0x07; 19948c2ecf20Sopenharmony_ci r->right_shift = (val >> 19) & 0x07; 19958c2ecf20Sopenharmony_ci r->invert = (val >> 22) & 1; 19968c2ecf20Sopenharmony_ci r->stereo = (val >> 23) & 1; 19978c2ecf20Sopenharmony_ci r->mask = (val >> 24) & 0xff; 19988c2ecf20Sopenharmony_ci} 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_cistatic int snd_cmipci_info_volume(struct snd_kcontrol *kcontrol, 20018c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 20028c2ecf20Sopenharmony_ci{ 20038c2ecf20Sopenharmony_ci struct cmipci_sb_reg reg; 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci cmipci_sb_reg_decode(®, kcontrol->private_value); 20068c2ecf20Sopenharmony_ci uinfo->type = reg.mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 20078c2ecf20Sopenharmony_ci uinfo->count = reg.stereo + 1; 20088c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 20098c2ecf20Sopenharmony_ci uinfo->value.integer.max = reg.mask; 20108c2ecf20Sopenharmony_ci return 0; 20118c2ecf20Sopenharmony_ci} 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_cistatic int snd_cmipci_get_volume(struct snd_kcontrol *kcontrol, 20148c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 20158c2ecf20Sopenharmony_ci{ 20168c2ecf20Sopenharmony_ci struct cmipci *cm = snd_kcontrol_chip(kcontrol); 20178c2ecf20Sopenharmony_ci struct cmipci_sb_reg reg; 20188c2ecf20Sopenharmony_ci int val; 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci cmipci_sb_reg_decode(®, kcontrol->private_value); 20218c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 20228c2ecf20Sopenharmony_ci val = (snd_cmipci_mixer_read(cm, reg.left_reg) >> reg.left_shift) & reg.mask; 20238c2ecf20Sopenharmony_ci if (reg.invert) 20248c2ecf20Sopenharmony_ci val = reg.mask - val; 20258c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = val; 20268c2ecf20Sopenharmony_ci if (reg.stereo) { 20278c2ecf20Sopenharmony_ci val = (snd_cmipci_mixer_read(cm, reg.right_reg) >> reg.right_shift) & reg.mask; 20288c2ecf20Sopenharmony_ci if (reg.invert) 20298c2ecf20Sopenharmony_ci val = reg.mask - val; 20308c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = val; 20318c2ecf20Sopenharmony_ci } 20328c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 20338c2ecf20Sopenharmony_ci return 0; 20348c2ecf20Sopenharmony_ci} 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_cistatic int snd_cmipci_put_volume(struct snd_kcontrol *kcontrol, 20378c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 20388c2ecf20Sopenharmony_ci{ 20398c2ecf20Sopenharmony_ci struct cmipci *cm = snd_kcontrol_chip(kcontrol); 20408c2ecf20Sopenharmony_ci struct cmipci_sb_reg reg; 20418c2ecf20Sopenharmony_ci int change; 20428c2ecf20Sopenharmony_ci int left, right, oleft, oright; 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci cmipci_sb_reg_decode(®, kcontrol->private_value); 20458c2ecf20Sopenharmony_ci left = ucontrol->value.integer.value[0] & reg.mask; 20468c2ecf20Sopenharmony_ci if (reg.invert) 20478c2ecf20Sopenharmony_ci left = reg.mask - left; 20488c2ecf20Sopenharmony_ci left <<= reg.left_shift; 20498c2ecf20Sopenharmony_ci if (reg.stereo) { 20508c2ecf20Sopenharmony_ci right = ucontrol->value.integer.value[1] & reg.mask; 20518c2ecf20Sopenharmony_ci if (reg.invert) 20528c2ecf20Sopenharmony_ci right = reg.mask - right; 20538c2ecf20Sopenharmony_ci right <<= reg.right_shift; 20548c2ecf20Sopenharmony_ci } else 20558c2ecf20Sopenharmony_ci right = 0; 20568c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 20578c2ecf20Sopenharmony_ci oleft = snd_cmipci_mixer_read(cm, reg.left_reg); 20588c2ecf20Sopenharmony_ci left |= oleft & ~(reg.mask << reg.left_shift); 20598c2ecf20Sopenharmony_ci change = left != oleft; 20608c2ecf20Sopenharmony_ci if (reg.stereo) { 20618c2ecf20Sopenharmony_ci if (reg.left_reg != reg.right_reg) { 20628c2ecf20Sopenharmony_ci snd_cmipci_mixer_write(cm, reg.left_reg, left); 20638c2ecf20Sopenharmony_ci oright = snd_cmipci_mixer_read(cm, reg.right_reg); 20648c2ecf20Sopenharmony_ci } else 20658c2ecf20Sopenharmony_ci oright = left; 20668c2ecf20Sopenharmony_ci right |= oright & ~(reg.mask << reg.right_shift); 20678c2ecf20Sopenharmony_ci change |= right != oright; 20688c2ecf20Sopenharmony_ci snd_cmipci_mixer_write(cm, reg.right_reg, right); 20698c2ecf20Sopenharmony_ci } else 20708c2ecf20Sopenharmony_ci snd_cmipci_mixer_write(cm, reg.left_reg, left); 20718c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 20728c2ecf20Sopenharmony_ci return change; 20738c2ecf20Sopenharmony_ci} 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci/* 20768c2ecf20Sopenharmony_ci * input route (left,right) -> (left,right) 20778c2ecf20Sopenharmony_ci */ 20788c2ecf20Sopenharmony_ci#define CMIPCI_SB_INPUT_SW(xname, left_shift, right_shift) \ 20798c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 20808c2ecf20Sopenharmony_ci .info = snd_cmipci_info_input_sw, \ 20818c2ecf20Sopenharmony_ci .get = snd_cmipci_get_input_sw, .put = snd_cmipci_put_input_sw, \ 20828c2ecf20Sopenharmony_ci .private_value = COMPOSE_SB_REG(SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, left_shift, right_shift, 1, 0, 1), \ 20838c2ecf20Sopenharmony_ci} 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_cistatic int snd_cmipci_info_input_sw(struct snd_kcontrol *kcontrol, 20868c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 20878c2ecf20Sopenharmony_ci{ 20888c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 20898c2ecf20Sopenharmony_ci uinfo->count = 4; 20908c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 20918c2ecf20Sopenharmony_ci uinfo->value.integer.max = 1; 20928c2ecf20Sopenharmony_ci return 0; 20938c2ecf20Sopenharmony_ci} 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_cistatic int snd_cmipci_get_input_sw(struct snd_kcontrol *kcontrol, 20968c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 20978c2ecf20Sopenharmony_ci{ 20988c2ecf20Sopenharmony_ci struct cmipci *cm = snd_kcontrol_chip(kcontrol); 20998c2ecf20Sopenharmony_ci struct cmipci_sb_reg reg; 21008c2ecf20Sopenharmony_ci int val1, val2; 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ci cmipci_sb_reg_decode(®, kcontrol->private_value); 21038c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 21048c2ecf20Sopenharmony_ci val1 = snd_cmipci_mixer_read(cm, reg.left_reg); 21058c2ecf20Sopenharmony_ci val2 = snd_cmipci_mixer_read(cm, reg.right_reg); 21068c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 21078c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = (val1 >> reg.left_shift) & 1; 21088c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = (val2 >> reg.left_shift) & 1; 21098c2ecf20Sopenharmony_ci ucontrol->value.integer.value[2] = (val1 >> reg.right_shift) & 1; 21108c2ecf20Sopenharmony_ci ucontrol->value.integer.value[3] = (val2 >> reg.right_shift) & 1; 21118c2ecf20Sopenharmony_ci return 0; 21128c2ecf20Sopenharmony_ci} 21138c2ecf20Sopenharmony_ci 21148c2ecf20Sopenharmony_cistatic int snd_cmipci_put_input_sw(struct snd_kcontrol *kcontrol, 21158c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 21168c2ecf20Sopenharmony_ci{ 21178c2ecf20Sopenharmony_ci struct cmipci *cm = snd_kcontrol_chip(kcontrol); 21188c2ecf20Sopenharmony_ci struct cmipci_sb_reg reg; 21198c2ecf20Sopenharmony_ci int change; 21208c2ecf20Sopenharmony_ci int val1, val2, oval1, oval2; 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_ci cmipci_sb_reg_decode(®, kcontrol->private_value); 21238c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 21248c2ecf20Sopenharmony_ci oval1 = snd_cmipci_mixer_read(cm, reg.left_reg); 21258c2ecf20Sopenharmony_ci oval2 = snd_cmipci_mixer_read(cm, reg.right_reg); 21268c2ecf20Sopenharmony_ci val1 = oval1 & ~((1 << reg.left_shift) | (1 << reg.right_shift)); 21278c2ecf20Sopenharmony_ci val2 = oval2 & ~((1 << reg.left_shift) | (1 << reg.right_shift)); 21288c2ecf20Sopenharmony_ci val1 |= (ucontrol->value.integer.value[0] & 1) << reg.left_shift; 21298c2ecf20Sopenharmony_ci val2 |= (ucontrol->value.integer.value[1] & 1) << reg.left_shift; 21308c2ecf20Sopenharmony_ci val1 |= (ucontrol->value.integer.value[2] & 1) << reg.right_shift; 21318c2ecf20Sopenharmony_ci val2 |= (ucontrol->value.integer.value[3] & 1) << reg.right_shift; 21328c2ecf20Sopenharmony_ci change = val1 != oval1 || val2 != oval2; 21338c2ecf20Sopenharmony_ci snd_cmipci_mixer_write(cm, reg.left_reg, val1); 21348c2ecf20Sopenharmony_ci snd_cmipci_mixer_write(cm, reg.right_reg, val2); 21358c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 21368c2ecf20Sopenharmony_ci return change; 21378c2ecf20Sopenharmony_ci} 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci/* 21408c2ecf20Sopenharmony_ci * native mixer switches/volumes 21418c2ecf20Sopenharmony_ci */ 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci#define CMIPCI_MIXER_SW_STEREO(xname, reg, lshift, rshift, invert) \ 21448c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 21458c2ecf20Sopenharmony_ci .info = snd_cmipci_info_native_mixer, \ 21468c2ecf20Sopenharmony_ci .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \ 21478c2ecf20Sopenharmony_ci .private_value = COMPOSE_SB_REG(reg, reg, lshift, rshift, 1, invert, 1), \ 21488c2ecf20Sopenharmony_ci} 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_ci#define CMIPCI_MIXER_SW_MONO(xname, reg, shift, invert) \ 21518c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 21528c2ecf20Sopenharmony_ci .info = snd_cmipci_info_native_mixer, \ 21538c2ecf20Sopenharmony_ci .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \ 21548c2ecf20Sopenharmony_ci .private_value = COMPOSE_SB_REG(reg, reg, shift, shift, 1, invert, 0), \ 21558c2ecf20Sopenharmony_ci} 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci#define CMIPCI_MIXER_VOL_STEREO(xname, reg, lshift, rshift, mask) \ 21588c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 21598c2ecf20Sopenharmony_ci .info = snd_cmipci_info_native_mixer, \ 21608c2ecf20Sopenharmony_ci .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \ 21618c2ecf20Sopenharmony_ci .private_value = COMPOSE_SB_REG(reg, reg, lshift, rshift, mask, 0, 1), \ 21628c2ecf20Sopenharmony_ci} 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci#define CMIPCI_MIXER_VOL_MONO(xname, reg, shift, mask) \ 21658c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 21668c2ecf20Sopenharmony_ci .info = snd_cmipci_info_native_mixer, \ 21678c2ecf20Sopenharmony_ci .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \ 21688c2ecf20Sopenharmony_ci .private_value = COMPOSE_SB_REG(reg, reg, shift, shift, mask, 0, 0), \ 21698c2ecf20Sopenharmony_ci} 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_cistatic int snd_cmipci_info_native_mixer(struct snd_kcontrol *kcontrol, 21728c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 21738c2ecf20Sopenharmony_ci{ 21748c2ecf20Sopenharmony_ci struct cmipci_sb_reg reg; 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_ci cmipci_sb_reg_decode(®, kcontrol->private_value); 21778c2ecf20Sopenharmony_ci uinfo->type = reg.mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; 21788c2ecf20Sopenharmony_ci uinfo->count = reg.stereo + 1; 21798c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 21808c2ecf20Sopenharmony_ci uinfo->value.integer.max = reg.mask; 21818c2ecf20Sopenharmony_ci return 0; 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci} 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_cistatic int snd_cmipci_get_native_mixer(struct snd_kcontrol *kcontrol, 21868c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 21878c2ecf20Sopenharmony_ci{ 21888c2ecf20Sopenharmony_ci struct cmipci *cm = snd_kcontrol_chip(kcontrol); 21898c2ecf20Sopenharmony_ci struct cmipci_sb_reg reg; 21908c2ecf20Sopenharmony_ci unsigned char oreg, val; 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_ci cmipci_sb_reg_decode(®, kcontrol->private_value); 21938c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 21948c2ecf20Sopenharmony_ci oreg = inb(cm->iobase + reg.left_reg); 21958c2ecf20Sopenharmony_ci val = (oreg >> reg.left_shift) & reg.mask; 21968c2ecf20Sopenharmony_ci if (reg.invert) 21978c2ecf20Sopenharmony_ci val = reg.mask - val; 21988c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = val; 21998c2ecf20Sopenharmony_ci if (reg.stereo) { 22008c2ecf20Sopenharmony_ci val = (oreg >> reg.right_shift) & reg.mask; 22018c2ecf20Sopenharmony_ci if (reg.invert) 22028c2ecf20Sopenharmony_ci val = reg.mask - val; 22038c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = val; 22048c2ecf20Sopenharmony_ci } 22058c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 22068c2ecf20Sopenharmony_ci return 0; 22078c2ecf20Sopenharmony_ci} 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_cistatic int snd_cmipci_put_native_mixer(struct snd_kcontrol *kcontrol, 22108c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 22118c2ecf20Sopenharmony_ci{ 22128c2ecf20Sopenharmony_ci struct cmipci *cm = snd_kcontrol_chip(kcontrol); 22138c2ecf20Sopenharmony_ci struct cmipci_sb_reg reg; 22148c2ecf20Sopenharmony_ci unsigned char oreg, nreg, val; 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_ci cmipci_sb_reg_decode(®, kcontrol->private_value); 22178c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 22188c2ecf20Sopenharmony_ci oreg = inb(cm->iobase + reg.left_reg); 22198c2ecf20Sopenharmony_ci val = ucontrol->value.integer.value[0] & reg.mask; 22208c2ecf20Sopenharmony_ci if (reg.invert) 22218c2ecf20Sopenharmony_ci val = reg.mask - val; 22228c2ecf20Sopenharmony_ci nreg = oreg & ~(reg.mask << reg.left_shift); 22238c2ecf20Sopenharmony_ci nreg |= (val << reg.left_shift); 22248c2ecf20Sopenharmony_ci if (reg.stereo) { 22258c2ecf20Sopenharmony_ci val = ucontrol->value.integer.value[1] & reg.mask; 22268c2ecf20Sopenharmony_ci if (reg.invert) 22278c2ecf20Sopenharmony_ci val = reg.mask - val; 22288c2ecf20Sopenharmony_ci nreg &= ~(reg.mask << reg.right_shift); 22298c2ecf20Sopenharmony_ci nreg |= (val << reg.right_shift); 22308c2ecf20Sopenharmony_ci } 22318c2ecf20Sopenharmony_ci outb(nreg, cm->iobase + reg.left_reg); 22328c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 22338c2ecf20Sopenharmony_ci return (nreg != oreg); 22348c2ecf20Sopenharmony_ci} 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_ci/* 22378c2ecf20Sopenharmony_ci * special case - check mixer sensitivity 22388c2ecf20Sopenharmony_ci */ 22398c2ecf20Sopenharmony_cistatic int snd_cmipci_get_native_mixer_sensitive(struct snd_kcontrol *kcontrol, 22408c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 22418c2ecf20Sopenharmony_ci{ 22428c2ecf20Sopenharmony_ci //struct cmipci *cm = snd_kcontrol_chip(kcontrol); 22438c2ecf20Sopenharmony_ci return snd_cmipci_get_native_mixer(kcontrol, ucontrol); 22448c2ecf20Sopenharmony_ci} 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_cistatic int snd_cmipci_put_native_mixer_sensitive(struct snd_kcontrol *kcontrol, 22478c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 22488c2ecf20Sopenharmony_ci{ 22498c2ecf20Sopenharmony_ci struct cmipci *cm = snd_kcontrol_chip(kcontrol); 22508c2ecf20Sopenharmony_ci if (cm->mixer_insensitive) { 22518c2ecf20Sopenharmony_ci /* ignored */ 22528c2ecf20Sopenharmony_ci return 0; 22538c2ecf20Sopenharmony_ci } 22548c2ecf20Sopenharmony_ci return snd_cmipci_put_native_mixer(kcontrol, ucontrol); 22558c2ecf20Sopenharmony_ci} 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_cmipci_mixers[] = { 22598c2ecf20Sopenharmony_ci CMIPCI_SB_VOL_STEREO("Master Playback Volume", SB_DSP4_MASTER_DEV, 3, 31), 22608c2ecf20Sopenharmony_ci CMIPCI_MIXER_SW_MONO("3D Control - Switch", CM_REG_MIXER1, CM_X3DEN_SHIFT, 0), 22618c2ecf20Sopenharmony_ci CMIPCI_SB_VOL_STEREO("PCM Playback Volume", SB_DSP4_PCM_DEV, 3, 31), 22628c2ecf20Sopenharmony_ci //CMIPCI_MIXER_SW_MONO("PCM Playback Switch", CM_REG_MIXER1, CM_WSMUTE_SHIFT, 1), 22638c2ecf20Sopenharmony_ci { /* switch with sensitivity */ 22648c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 22658c2ecf20Sopenharmony_ci .name = "PCM Playback Switch", 22668c2ecf20Sopenharmony_ci .info = snd_cmipci_info_native_mixer, 22678c2ecf20Sopenharmony_ci .get = snd_cmipci_get_native_mixer_sensitive, 22688c2ecf20Sopenharmony_ci .put = snd_cmipci_put_native_mixer_sensitive, 22698c2ecf20Sopenharmony_ci .private_value = COMPOSE_SB_REG(CM_REG_MIXER1, CM_REG_MIXER1, CM_WSMUTE_SHIFT, CM_WSMUTE_SHIFT, 1, 1, 0), 22708c2ecf20Sopenharmony_ci }, 22718c2ecf20Sopenharmony_ci CMIPCI_MIXER_SW_STEREO("PCM Capture Switch", CM_REG_MIXER1, CM_WAVEINL_SHIFT, CM_WAVEINR_SHIFT, 0), 22728c2ecf20Sopenharmony_ci CMIPCI_SB_VOL_STEREO("Synth Playback Volume", SB_DSP4_SYNTH_DEV, 3, 31), 22738c2ecf20Sopenharmony_ci CMIPCI_MIXER_SW_MONO("Synth Playback Switch", CM_REG_MIXER1, CM_FMMUTE_SHIFT, 1), 22748c2ecf20Sopenharmony_ci CMIPCI_SB_INPUT_SW("Synth Capture Route", 6, 5), 22758c2ecf20Sopenharmony_ci CMIPCI_SB_VOL_STEREO("CD Playback Volume", SB_DSP4_CD_DEV, 3, 31), 22768c2ecf20Sopenharmony_ci CMIPCI_SB_SW_STEREO("CD Playback Switch", 2, 1), 22778c2ecf20Sopenharmony_ci CMIPCI_SB_INPUT_SW("CD Capture Route", 2, 1), 22788c2ecf20Sopenharmony_ci CMIPCI_SB_VOL_STEREO("Line Playback Volume", SB_DSP4_LINE_DEV, 3, 31), 22798c2ecf20Sopenharmony_ci CMIPCI_SB_SW_STEREO("Line Playback Switch", 4, 3), 22808c2ecf20Sopenharmony_ci CMIPCI_SB_INPUT_SW("Line Capture Route", 4, 3), 22818c2ecf20Sopenharmony_ci CMIPCI_SB_VOL_MONO("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31), 22828c2ecf20Sopenharmony_ci CMIPCI_SB_SW_MONO("Mic Playback Switch", 0), 22838c2ecf20Sopenharmony_ci CMIPCI_DOUBLE("Mic Capture Switch", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0, 1, 0, 0), 22848c2ecf20Sopenharmony_ci CMIPCI_SB_VOL_MONO("Beep Playback Volume", SB_DSP4_SPEAKER_DEV, 6, 3), 22858c2ecf20Sopenharmony_ci CMIPCI_MIXER_VOL_STEREO("Aux Playback Volume", CM_REG_AUX_VOL, 4, 0, 15), 22868c2ecf20Sopenharmony_ci CMIPCI_MIXER_SW_STEREO("Aux Playback Switch", CM_REG_MIXER2, CM_VAUXLM_SHIFT, CM_VAUXRM_SHIFT, 0), 22878c2ecf20Sopenharmony_ci CMIPCI_MIXER_SW_STEREO("Aux Capture Switch", CM_REG_MIXER2, CM_RAUXLEN_SHIFT, CM_RAUXREN_SHIFT, 0), 22888c2ecf20Sopenharmony_ci CMIPCI_MIXER_SW_MONO("Mic Boost Playback Switch", CM_REG_MIXER2, CM_MICGAINZ_SHIFT, 1), 22898c2ecf20Sopenharmony_ci CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7), 22908c2ecf20Sopenharmony_ci CMIPCI_SB_VOL_MONO("Phone Playback Volume", CM_REG_EXTENT_IND, 5, 7), 22918c2ecf20Sopenharmony_ci CMIPCI_DOUBLE("Phone Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 4, 4, 1, 0, 0), 22928c2ecf20Sopenharmony_ci CMIPCI_DOUBLE("Beep Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0), 22938c2ecf20Sopenharmony_ci CMIPCI_DOUBLE("Mic Boost Capture Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 0, 0, 1, 0, 0), 22948c2ecf20Sopenharmony_ci}; 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ci/* 22978c2ecf20Sopenharmony_ci * other switches 22988c2ecf20Sopenharmony_ci */ 22998c2ecf20Sopenharmony_ci 23008c2ecf20Sopenharmony_cistruct cmipci_switch_args { 23018c2ecf20Sopenharmony_ci int reg; /* register index */ 23028c2ecf20Sopenharmony_ci unsigned int mask; /* mask bits */ 23038c2ecf20Sopenharmony_ci unsigned int mask_on; /* mask bits to turn on */ 23048c2ecf20Sopenharmony_ci unsigned int is_byte: 1; /* byte access? */ 23058c2ecf20Sopenharmony_ci unsigned int ac3_sensitive: 1; /* access forbidden during 23068c2ecf20Sopenharmony_ci * non-audio operation? 23078c2ecf20Sopenharmony_ci */ 23088c2ecf20Sopenharmony_ci}; 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_ci#define snd_cmipci_uswitch_info snd_ctl_boolean_mono_info 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_cistatic int _snd_cmipci_uswitch_get(struct snd_kcontrol *kcontrol, 23138c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol, 23148c2ecf20Sopenharmony_ci struct cmipci_switch_args *args) 23158c2ecf20Sopenharmony_ci{ 23168c2ecf20Sopenharmony_ci unsigned int val; 23178c2ecf20Sopenharmony_ci struct cmipci *cm = snd_kcontrol_chip(kcontrol); 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 23208c2ecf20Sopenharmony_ci if (args->ac3_sensitive && cm->mixer_insensitive) { 23218c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = 0; 23228c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 23238c2ecf20Sopenharmony_ci return 0; 23248c2ecf20Sopenharmony_ci } 23258c2ecf20Sopenharmony_ci if (args->is_byte) 23268c2ecf20Sopenharmony_ci val = inb(cm->iobase + args->reg); 23278c2ecf20Sopenharmony_ci else 23288c2ecf20Sopenharmony_ci val = snd_cmipci_read(cm, args->reg); 23298c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = ((val & args->mask) == args->mask_on) ? 1 : 0; 23308c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 23318c2ecf20Sopenharmony_ci return 0; 23328c2ecf20Sopenharmony_ci} 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_cistatic int snd_cmipci_uswitch_get(struct snd_kcontrol *kcontrol, 23358c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 23368c2ecf20Sopenharmony_ci{ 23378c2ecf20Sopenharmony_ci struct cmipci_switch_args *args; 23388c2ecf20Sopenharmony_ci args = (struct cmipci_switch_args *)kcontrol->private_value; 23398c2ecf20Sopenharmony_ci if (snd_BUG_ON(!args)) 23408c2ecf20Sopenharmony_ci return -EINVAL; 23418c2ecf20Sopenharmony_ci return _snd_cmipci_uswitch_get(kcontrol, ucontrol, args); 23428c2ecf20Sopenharmony_ci} 23438c2ecf20Sopenharmony_ci 23448c2ecf20Sopenharmony_cistatic int _snd_cmipci_uswitch_put(struct snd_kcontrol *kcontrol, 23458c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol, 23468c2ecf20Sopenharmony_ci struct cmipci_switch_args *args) 23478c2ecf20Sopenharmony_ci{ 23488c2ecf20Sopenharmony_ci unsigned int val; 23498c2ecf20Sopenharmony_ci int change; 23508c2ecf20Sopenharmony_ci struct cmipci *cm = snd_kcontrol_chip(kcontrol); 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 23538c2ecf20Sopenharmony_ci if (args->ac3_sensitive && cm->mixer_insensitive) { 23548c2ecf20Sopenharmony_ci /* ignored */ 23558c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 23568c2ecf20Sopenharmony_ci return 0; 23578c2ecf20Sopenharmony_ci } 23588c2ecf20Sopenharmony_ci if (args->is_byte) 23598c2ecf20Sopenharmony_ci val = inb(cm->iobase + args->reg); 23608c2ecf20Sopenharmony_ci else 23618c2ecf20Sopenharmony_ci val = snd_cmipci_read(cm, args->reg); 23628c2ecf20Sopenharmony_ci change = (val & args->mask) != (ucontrol->value.integer.value[0] ? 23638c2ecf20Sopenharmony_ci args->mask_on : (args->mask & ~args->mask_on)); 23648c2ecf20Sopenharmony_ci if (change) { 23658c2ecf20Sopenharmony_ci val &= ~args->mask; 23668c2ecf20Sopenharmony_ci if (ucontrol->value.integer.value[0]) 23678c2ecf20Sopenharmony_ci val |= args->mask_on; 23688c2ecf20Sopenharmony_ci else 23698c2ecf20Sopenharmony_ci val |= (args->mask & ~args->mask_on); 23708c2ecf20Sopenharmony_ci if (args->is_byte) 23718c2ecf20Sopenharmony_ci outb((unsigned char)val, cm->iobase + args->reg); 23728c2ecf20Sopenharmony_ci else 23738c2ecf20Sopenharmony_ci snd_cmipci_write(cm, args->reg, val); 23748c2ecf20Sopenharmony_ci } 23758c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 23768c2ecf20Sopenharmony_ci return change; 23778c2ecf20Sopenharmony_ci} 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_cistatic int snd_cmipci_uswitch_put(struct snd_kcontrol *kcontrol, 23808c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 23818c2ecf20Sopenharmony_ci{ 23828c2ecf20Sopenharmony_ci struct cmipci_switch_args *args; 23838c2ecf20Sopenharmony_ci args = (struct cmipci_switch_args *)kcontrol->private_value; 23848c2ecf20Sopenharmony_ci if (snd_BUG_ON(!args)) 23858c2ecf20Sopenharmony_ci return -EINVAL; 23868c2ecf20Sopenharmony_ci return _snd_cmipci_uswitch_put(kcontrol, ucontrol, args); 23878c2ecf20Sopenharmony_ci} 23888c2ecf20Sopenharmony_ci 23898c2ecf20Sopenharmony_ci#define DEFINE_SWITCH_ARG(sname, xreg, xmask, xmask_on, xis_byte, xac3) \ 23908c2ecf20Sopenharmony_cistatic struct cmipci_switch_args cmipci_switch_arg_##sname = { \ 23918c2ecf20Sopenharmony_ci .reg = xreg, \ 23928c2ecf20Sopenharmony_ci .mask = xmask, \ 23938c2ecf20Sopenharmony_ci .mask_on = xmask_on, \ 23948c2ecf20Sopenharmony_ci .is_byte = xis_byte, \ 23958c2ecf20Sopenharmony_ci .ac3_sensitive = xac3, \ 23968c2ecf20Sopenharmony_ci} 23978c2ecf20Sopenharmony_ci 23988c2ecf20Sopenharmony_ci#define DEFINE_BIT_SWITCH_ARG(sname, xreg, xmask, xis_byte, xac3) \ 23998c2ecf20Sopenharmony_ci DEFINE_SWITCH_ARG(sname, xreg, xmask, xmask, xis_byte, xac3) 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci#if 0 /* these will be controlled in pcm device */ 24028c2ecf20Sopenharmony_ciDEFINE_BIT_SWITCH_ARG(spdif_in, CM_REG_FUNCTRL1, CM_SPDF_1, 0, 0); 24038c2ecf20Sopenharmony_ciDEFINE_BIT_SWITCH_ARG(spdif_out, CM_REG_FUNCTRL1, CM_SPDF_0, 0, 0); 24048c2ecf20Sopenharmony_ci#endif 24058c2ecf20Sopenharmony_ciDEFINE_BIT_SWITCH_ARG(spdif_in_sel1, CM_REG_CHFORMAT, CM_SPDIF_SELECT1, 0, 0); 24068c2ecf20Sopenharmony_ciDEFINE_BIT_SWITCH_ARG(spdif_in_sel2, CM_REG_MISC_CTRL, CM_SPDIF_SELECT2, 0, 0); 24078c2ecf20Sopenharmony_ciDEFINE_BIT_SWITCH_ARG(spdif_enable, CM_REG_LEGACY_CTRL, CM_ENSPDOUT, 0, 0); 24088c2ecf20Sopenharmony_ciDEFINE_BIT_SWITCH_ARG(spdo2dac, CM_REG_FUNCTRL1, CM_SPDO2DAC, 0, 1); 24098c2ecf20Sopenharmony_ciDEFINE_BIT_SWITCH_ARG(spdi_valid, CM_REG_MISC, CM_SPDVALID, 1, 0); 24108c2ecf20Sopenharmony_ciDEFINE_BIT_SWITCH_ARG(spdif_copyright, CM_REG_LEGACY_CTRL, CM_SPDCOPYRHT, 0, 0); 24118c2ecf20Sopenharmony_ciDEFINE_BIT_SWITCH_ARG(spdif_dac_out, CM_REG_LEGACY_CTRL, CM_DAC2SPDO, 0, 1); 24128c2ecf20Sopenharmony_ciDEFINE_SWITCH_ARG(spdo_5v, CM_REG_MISC_CTRL, CM_SPDO5V, 0, 0, 0); /* inverse: 0 = 5V */ 24138c2ecf20Sopenharmony_ci// DEFINE_BIT_SWITCH_ARG(spdo_48k, CM_REG_MISC_CTRL, CM_SPDF_AC97|CM_SPDIF48K, 0, 1); 24148c2ecf20Sopenharmony_ciDEFINE_BIT_SWITCH_ARG(spdif_loop, CM_REG_FUNCTRL1, CM_SPDFLOOP, 0, 1); 24158c2ecf20Sopenharmony_ciDEFINE_BIT_SWITCH_ARG(spdi_monitor, CM_REG_MIXER1, CM_CDPLAY, 1, 0); 24168c2ecf20Sopenharmony_ci/* DEFINE_BIT_SWITCH_ARG(spdi_phase, CM_REG_CHFORMAT, CM_SPDIF_INVERSE, 0, 0); */ 24178c2ecf20Sopenharmony_ciDEFINE_BIT_SWITCH_ARG(spdi_phase, CM_REG_MISC, CM_SPDIF_INVERSE, 1, 0); 24188c2ecf20Sopenharmony_ciDEFINE_BIT_SWITCH_ARG(spdi_phase2, CM_REG_CHFORMAT, CM_SPDIF_INVERSE2, 0, 0); 24198c2ecf20Sopenharmony_ci#if CM_CH_PLAY == 1 24208c2ecf20Sopenharmony_ciDEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, 0, 0, 0); /* reversed */ 24218c2ecf20Sopenharmony_ci#else 24228c2ecf20Sopenharmony_ciDEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, CM_XCHGDAC, 0, 0); 24238c2ecf20Sopenharmony_ci#endif 24248c2ecf20Sopenharmony_ciDEFINE_BIT_SWITCH_ARG(fourch, CM_REG_MISC_CTRL, CM_N4SPK3D, 0, 0); 24258c2ecf20Sopenharmony_ci// DEFINE_BIT_SWITCH_ARG(line_rear, CM_REG_MIXER1, CM_REAR2LIN, 1, 0); 24268c2ecf20Sopenharmony_ci// DEFINE_BIT_SWITCH_ARG(line_bass, CM_REG_LEGACY_CTRL, CM_CENTR2LIN|CM_BASE2LIN, 0, 0); 24278c2ecf20Sopenharmony_ci// DEFINE_BIT_SWITCH_ARG(joystick, CM_REG_FUNCTRL1, CM_JYSTK_EN, 0, 0); /* now module option */ 24288c2ecf20Sopenharmony_ciDEFINE_SWITCH_ARG(modem, CM_REG_MISC_CTRL, CM_FLINKON|CM_FLINKOFF, CM_FLINKON, 0, 0); 24298c2ecf20Sopenharmony_ci 24308c2ecf20Sopenharmony_ci#define DEFINE_SWITCH(sname, stype, sarg) \ 24318c2ecf20Sopenharmony_ci{ .name = sname, \ 24328c2ecf20Sopenharmony_ci .iface = stype, \ 24338c2ecf20Sopenharmony_ci .info = snd_cmipci_uswitch_info, \ 24348c2ecf20Sopenharmony_ci .get = snd_cmipci_uswitch_get, \ 24358c2ecf20Sopenharmony_ci .put = snd_cmipci_uswitch_put, \ 24368c2ecf20Sopenharmony_ci .private_value = (unsigned long)&cmipci_switch_arg_##sarg,\ 24378c2ecf20Sopenharmony_ci} 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_ci#define DEFINE_CARD_SWITCH(sname, sarg) DEFINE_SWITCH(sname, SNDRV_CTL_ELEM_IFACE_CARD, sarg) 24408c2ecf20Sopenharmony_ci#define DEFINE_MIXER_SWITCH(sname, sarg) DEFINE_SWITCH(sname, SNDRV_CTL_ELEM_IFACE_MIXER, sarg) 24418c2ecf20Sopenharmony_ci 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_ci/* 24448c2ecf20Sopenharmony_ci * callbacks for spdif output switch 24458c2ecf20Sopenharmony_ci * needs toggle two registers.. 24468c2ecf20Sopenharmony_ci */ 24478c2ecf20Sopenharmony_cistatic int snd_cmipci_spdout_enable_get(struct snd_kcontrol *kcontrol, 24488c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 24498c2ecf20Sopenharmony_ci{ 24508c2ecf20Sopenharmony_ci int changed; 24518c2ecf20Sopenharmony_ci changed = _snd_cmipci_uswitch_get(kcontrol, ucontrol, &cmipci_switch_arg_spdif_enable); 24528c2ecf20Sopenharmony_ci changed |= _snd_cmipci_uswitch_get(kcontrol, ucontrol, &cmipci_switch_arg_spdo2dac); 24538c2ecf20Sopenharmony_ci return changed; 24548c2ecf20Sopenharmony_ci} 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_cistatic int snd_cmipci_spdout_enable_put(struct snd_kcontrol *kcontrol, 24578c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 24588c2ecf20Sopenharmony_ci{ 24598c2ecf20Sopenharmony_ci struct cmipci *chip = snd_kcontrol_chip(kcontrol); 24608c2ecf20Sopenharmony_ci int changed; 24618c2ecf20Sopenharmony_ci changed = _snd_cmipci_uswitch_put(kcontrol, ucontrol, &cmipci_switch_arg_spdif_enable); 24628c2ecf20Sopenharmony_ci changed |= _snd_cmipci_uswitch_put(kcontrol, ucontrol, &cmipci_switch_arg_spdo2dac); 24638c2ecf20Sopenharmony_ci if (changed) { 24648c2ecf20Sopenharmony_ci if (ucontrol->value.integer.value[0]) { 24658c2ecf20Sopenharmony_ci if (chip->spdif_playback_avail) 24668c2ecf20Sopenharmony_ci snd_cmipci_set_bit(chip, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); 24678c2ecf20Sopenharmony_ci } else { 24688c2ecf20Sopenharmony_ci if (chip->spdif_playback_avail) 24698c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(chip, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF); 24708c2ecf20Sopenharmony_ci } 24718c2ecf20Sopenharmony_ci } 24728c2ecf20Sopenharmony_ci chip->spdif_playback_enabled = ucontrol->value.integer.value[0]; 24738c2ecf20Sopenharmony_ci return changed; 24748c2ecf20Sopenharmony_ci} 24758c2ecf20Sopenharmony_ci 24768c2ecf20Sopenharmony_ci 24778c2ecf20Sopenharmony_cistatic int snd_cmipci_line_in_mode_info(struct snd_kcontrol *kcontrol, 24788c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 24798c2ecf20Sopenharmony_ci{ 24808c2ecf20Sopenharmony_ci struct cmipci *cm = snd_kcontrol_chip(kcontrol); 24818c2ecf20Sopenharmony_ci static const char *const texts[3] = { 24828c2ecf20Sopenharmony_ci "Line-In", "Rear Output", "Bass Output" 24838c2ecf20Sopenharmony_ci }; 24848c2ecf20Sopenharmony_ci 24858c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 24868c2ecf20Sopenharmony_ci cm->chip_version >= 39 ? 3 : 2, texts); 24878c2ecf20Sopenharmony_ci} 24888c2ecf20Sopenharmony_ci 24898c2ecf20Sopenharmony_cistatic inline unsigned int get_line_in_mode(struct cmipci *cm) 24908c2ecf20Sopenharmony_ci{ 24918c2ecf20Sopenharmony_ci unsigned int val; 24928c2ecf20Sopenharmony_ci if (cm->chip_version >= 39) { 24938c2ecf20Sopenharmony_ci val = snd_cmipci_read(cm, CM_REG_LEGACY_CTRL); 24948c2ecf20Sopenharmony_ci if (val & (CM_CENTR2LIN | CM_BASE2LIN)) 24958c2ecf20Sopenharmony_ci return 2; 24968c2ecf20Sopenharmony_ci } 24978c2ecf20Sopenharmony_ci val = snd_cmipci_read_b(cm, CM_REG_MIXER1); 24988c2ecf20Sopenharmony_ci if (val & CM_REAR2LIN) 24998c2ecf20Sopenharmony_ci return 1; 25008c2ecf20Sopenharmony_ci return 0; 25018c2ecf20Sopenharmony_ci} 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_cistatic int snd_cmipci_line_in_mode_get(struct snd_kcontrol *kcontrol, 25048c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 25058c2ecf20Sopenharmony_ci{ 25068c2ecf20Sopenharmony_ci struct cmipci *cm = snd_kcontrol_chip(kcontrol); 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 25098c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = get_line_in_mode(cm); 25108c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 25118c2ecf20Sopenharmony_ci return 0; 25128c2ecf20Sopenharmony_ci} 25138c2ecf20Sopenharmony_ci 25148c2ecf20Sopenharmony_cistatic int snd_cmipci_line_in_mode_put(struct snd_kcontrol *kcontrol, 25158c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 25168c2ecf20Sopenharmony_ci{ 25178c2ecf20Sopenharmony_ci struct cmipci *cm = snd_kcontrol_chip(kcontrol); 25188c2ecf20Sopenharmony_ci int change; 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 25218c2ecf20Sopenharmony_ci if (ucontrol->value.enumerated.item[0] == 2) 25228c2ecf20Sopenharmony_ci change = snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_CENTR2LIN | CM_BASE2LIN); 25238c2ecf20Sopenharmony_ci else 25248c2ecf20Sopenharmony_ci change = snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CENTR2LIN | CM_BASE2LIN); 25258c2ecf20Sopenharmony_ci if (ucontrol->value.enumerated.item[0] == 1) 25268c2ecf20Sopenharmony_ci change |= snd_cmipci_set_bit_b(cm, CM_REG_MIXER1, CM_REAR2LIN); 25278c2ecf20Sopenharmony_ci else 25288c2ecf20Sopenharmony_ci change |= snd_cmipci_clear_bit_b(cm, CM_REG_MIXER1, CM_REAR2LIN); 25298c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 25308c2ecf20Sopenharmony_ci return change; 25318c2ecf20Sopenharmony_ci} 25328c2ecf20Sopenharmony_ci 25338c2ecf20Sopenharmony_cistatic int snd_cmipci_mic_in_mode_info(struct snd_kcontrol *kcontrol, 25348c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 25358c2ecf20Sopenharmony_ci{ 25368c2ecf20Sopenharmony_ci static const char *const texts[2] = { "Mic-In", "Center/LFE Output" }; 25378c2ecf20Sopenharmony_ci 25388c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 2, texts); 25398c2ecf20Sopenharmony_ci} 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_cistatic int snd_cmipci_mic_in_mode_get(struct snd_kcontrol *kcontrol, 25428c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 25438c2ecf20Sopenharmony_ci{ 25448c2ecf20Sopenharmony_ci struct cmipci *cm = snd_kcontrol_chip(kcontrol); 25458c2ecf20Sopenharmony_ci /* same bit as spdi_phase */ 25468c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 25478c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = 25488c2ecf20Sopenharmony_ci (snd_cmipci_read_b(cm, CM_REG_MISC) & CM_SPDIF_INVERSE) ? 1 : 0; 25498c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 25508c2ecf20Sopenharmony_ci return 0; 25518c2ecf20Sopenharmony_ci} 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_cistatic int snd_cmipci_mic_in_mode_put(struct snd_kcontrol *kcontrol, 25548c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 25558c2ecf20Sopenharmony_ci{ 25568c2ecf20Sopenharmony_ci struct cmipci *cm = snd_kcontrol_chip(kcontrol); 25578c2ecf20Sopenharmony_ci int change; 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 25608c2ecf20Sopenharmony_ci if (ucontrol->value.enumerated.item[0]) 25618c2ecf20Sopenharmony_ci change = snd_cmipci_set_bit_b(cm, CM_REG_MISC, CM_SPDIF_INVERSE); 25628c2ecf20Sopenharmony_ci else 25638c2ecf20Sopenharmony_ci change = snd_cmipci_clear_bit_b(cm, CM_REG_MISC, CM_SPDIF_INVERSE); 25648c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 25658c2ecf20Sopenharmony_ci return change; 25668c2ecf20Sopenharmony_ci} 25678c2ecf20Sopenharmony_ci 25688c2ecf20Sopenharmony_ci/* both for CM8338/8738 */ 25698c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_cmipci_mixer_switches[] = { 25708c2ecf20Sopenharmony_ci DEFINE_MIXER_SWITCH("Four Channel Mode", fourch), 25718c2ecf20Sopenharmony_ci { 25728c2ecf20Sopenharmony_ci .name = "Line-In Mode", 25738c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 25748c2ecf20Sopenharmony_ci .info = snd_cmipci_line_in_mode_info, 25758c2ecf20Sopenharmony_ci .get = snd_cmipci_line_in_mode_get, 25768c2ecf20Sopenharmony_ci .put = snd_cmipci_line_in_mode_put, 25778c2ecf20Sopenharmony_ci }, 25788c2ecf20Sopenharmony_ci}; 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci/* for non-multichannel chips */ 25818c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_cmipci_nomulti_switch = 25828c2ecf20Sopenharmony_ciDEFINE_MIXER_SWITCH("Exchange DAC", exchange_dac); 25838c2ecf20Sopenharmony_ci 25848c2ecf20Sopenharmony_ci/* only for CM8738 */ 25858c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_cmipci_8738_mixer_switches[] = { 25868c2ecf20Sopenharmony_ci#if 0 /* controlled in pcm device */ 25878c2ecf20Sopenharmony_ci DEFINE_MIXER_SWITCH("IEC958 In Record", spdif_in), 25888c2ecf20Sopenharmony_ci DEFINE_MIXER_SWITCH("IEC958 Out", spdif_out), 25898c2ecf20Sopenharmony_ci DEFINE_MIXER_SWITCH("IEC958 Out To DAC", spdo2dac), 25908c2ecf20Sopenharmony_ci#endif 25918c2ecf20Sopenharmony_ci // DEFINE_MIXER_SWITCH("IEC958 Output Switch", spdif_enable), 25928c2ecf20Sopenharmony_ci { .name = "IEC958 Output Switch", 25938c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 25948c2ecf20Sopenharmony_ci .info = snd_cmipci_uswitch_info, 25958c2ecf20Sopenharmony_ci .get = snd_cmipci_spdout_enable_get, 25968c2ecf20Sopenharmony_ci .put = snd_cmipci_spdout_enable_put, 25978c2ecf20Sopenharmony_ci }, 25988c2ecf20Sopenharmony_ci DEFINE_MIXER_SWITCH("IEC958 In Valid", spdi_valid), 25998c2ecf20Sopenharmony_ci DEFINE_MIXER_SWITCH("IEC958 Copyright", spdif_copyright), 26008c2ecf20Sopenharmony_ci DEFINE_MIXER_SWITCH("IEC958 5V", spdo_5v), 26018c2ecf20Sopenharmony_ci// DEFINE_MIXER_SWITCH("IEC958 In/Out 48KHz", spdo_48k), 26028c2ecf20Sopenharmony_ci DEFINE_MIXER_SWITCH("IEC958 Loop", spdif_loop), 26038c2ecf20Sopenharmony_ci DEFINE_MIXER_SWITCH("IEC958 In Monitor", spdi_monitor), 26048c2ecf20Sopenharmony_ci}; 26058c2ecf20Sopenharmony_ci 26068c2ecf20Sopenharmony_ci/* only for model 033/037 */ 26078c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_cmipci_old_mixer_switches[] = { 26088c2ecf20Sopenharmony_ci DEFINE_MIXER_SWITCH("IEC958 Mix Analog", spdif_dac_out), 26098c2ecf20Sopenharmony_ci DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase), 26108c2ecf20Sopenharmony_ci DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel1), 26118c2ecf20Sopenharmony_ci}; 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_ci/* only for model 039 or later */ 26148c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_cmipci_extra_mixer_switches[] = { 26158c2ecf20Sopenharmony_ci DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel2), 26168c2ecf20Sopenharmony_ci DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase2), 26178c2ecf20Sopenharmony_ci { 26188c2ecf20Sopenharmony_ci .name = "Mic-In Mode", 26198c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 26208c2ecf20Sopenharmony_ci .info = snd_cmipci_mic_in_mode_info, 26218c2ecf20Sopenharmony_ci .get = snd_cmipci_mic_in_mode_get, 26228c2ecf20Sopenharmony_ci .put = snd_cmipci_mic_in_mode_put, 26238c2ecf20Sopenharmony_ci } 26248c2ecf20Sopenharmony_ci}; 26258c2ecf20Sopenharmony_ci 26268c2ecf20Sopenharmony_ci/* card control switches */ 26278c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_cmipci_modem_switch = 26288c2ecf20Sopenharmony_ciDEFINE_CARD_SWITCH("Modem", modem); 26298c2ecf20Sopenharmony_ci 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_cistatic int snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_device) 26328c2ecf20Sopenharmony_ci{ 26338c2ecf20Sopenharmony_ci struct snd_card *card; 26348c2ecf20Sopenharmony_ci const struct snd_kcontrol_new *sw; 26358c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 26368c2ecf20Sopenharmony_ci unsigned int idx; 26378c2ecf20Sopenharmony_ci int err; 26388c2ecf20Sopenharmony_ci 26398c2ecf20Sopenharmony_ci if (snd_BUG_ON(!cm || !cm->card)) 26408c2ecf20Sopenharmony_ci return -EINVAL; 26418c2ecf20Sopenharmony_ci 26428c2ecf20Sopenharmony_ci card = cm->card; 26438c2ecf20Sopenharmony_ci 26448c2ecf20Sopenharmony_ci strcpy(card->mixername, "CMedia PCI"); 26458c2ecf20Sopenharmony_ci 26468c2ecf20Sopenharmony_ci spin_lock_irq(&cm->reg_lock); 26478c2ecf20Sopenharmony_ci snd_cmipci_mixer_write(cm, 0x00, 0x00); /* mixer reset */ 26488c2ecf20Sopenharmony_ci spin_unlock_irq(&cm->reg_lock); 26498c2ecf20Sopenharmony_ci 26508c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_mixers); idx++) { 26518c2ecf20Sopenharmony_ci if (cm->chip_version == 68) { // 8768 has no PCM volume 26528c2ecf20Sopenharmony_ci if (!strcmp(snd_cmipci_mixers[idx].name, 26538c2ecf20Sopenharmony_ci "PCM Playback Volume")) 26548c2ecf20Sopenharmony_ci continue; 26558c2ecf20Sopenharmony_ci } 26568c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cmipci_mixers[idx], cm))) < 0) 26578c2ecf20Sopenharmony_ci return err; 26588c2ecf20Sopenharmony_ci } 26598c2ecf20Sopenharmony_ci 26608c2ecf20Sopenharmony_ci /* mixer switches */ 26618c2ecf20Sopenharmony_ci sw = snd_cmipci_mixer_switches; 26628c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_mixer_switches); idx++, sw++) { 26638c2ecf20Sopenharmony_ci err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); 26648c2ecf20Sopenharmony_ci if (err < 0) 26658c2ecf20Sopenharmony_ci return err; 26668c2ecf20Sopenharmony_ci } 26678c2ecf20Sopenharmony_ci if (! cm->can_multi_ch) { 26688c2ecf20Sopenharmony_ci err = snd_ctl_add(cm->card, snd_ctl_new1(&snd_cmipci_nomulti_switch, cm)); 26698c2ecf20Sopenharmony_ci if (err < 0) 26708c2ecf20Sopenharmony_ci return err; 26718c2ecf20Sopenharmony_ci } 26728c2ecf20Sopenharmony_ci if (cm->device == PCI_DEVICE_ID_CMEDIA_CM8738 || 26738c2ecf20Sopenharmony_ci cm->device == PCI_DEVICE_ID_CMEDIA_CM8738B) { 26748c2ecf20Sopenharmony_ci sw = snd_cmipci_8738_mixer_switches; 26758c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_8738_mixer_switches); idx++, sw++) { 26768c2ecf20Sopenharmony_ci err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); 26778c2ecf20Sopenharmony_ci if (err < 0) 26788c2ecf20Sopenharmony_ci return err; 26798c2ecf20Sopenharmony_ci } 26808c2ecf20Sopenharmony_ci if (cm->can_ac3_hw) { 26818c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_default, cm))) < 0) 26828c2ecf20Sopenharmony_ci return err; 26838c2ecf20Sopenharmony_ci kctl->id.device = pcm_spdif_device; 26848c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_mask, cm))) < 0) 26858c2ecf20Sopenharmony_ci return err; 26868c2ecf20Sopenharmony_ci kctl->id.device = pcm_spdif_device; 26878c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_cmipci_spdif_stream, cm))) < 0) 26888c2ecf20Sopenharmony_ci return err; 26898c2ecf20Sopenharmony_ci kctl->id.device = pcm_spdif_device; 26908c2ecf20Sopenharmony_ci } 26918c2ecf20Sopenharmony_ci if (cm->chip_version <= 37) { 26928c2ecf20Sopenharmony_ci sw = snd_cmipci_old_mixer_switches; 26938c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_old_mixer_switches); idx++, sw++) { 26948c2ecf20Sopenharmony_ci err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); 26958c2ecf20Sopenharmony_ci if (err < 0) 26968c2ecf20Sopenharmony_ci return err; 26978c2ecf20Sopenharmony_ci } 26988c2ecf20Sopenharmony_ci } 26998c2ecf20Sopenharmony_ci } 27008c2ecf20Sopenharmony_ci if (cm->chip_version >= 39) { 27018c2ecf20Sopenharmony_ci sw = snd_cmipci_extra_mixer_switches; 27028c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_cmipci_extra_mixer_switches); idx++, sw++) { 27038c2ecf20Sopenharmony_ci err = snd_ctl_add(cm->card, snd_ctl_new1(sw, cm)); 27048c2ecf20Sopenharmony_ci if (err < 0) 27058c2ecf20Sopenharmony_ci return err; 27068c2ecf20Sopenharmony_ci } 27078c2ecf20Sopenharmony_ci } 27088c2ecf20Sopenharmony_ci 27098c2ecf20Sopenharmony_ci /* card switches */ 27108c2ecf20Sopenharmony_ci /* 27118c2ecf20Sopenharmony_ci * newer chips don't have the register bits to force modem link 27128c2ecf20Sopenharmony_ci * detection; the bit that was FLINKON now mutes CH1 27138c2ecf20Sopenharmony_ci */ 27148c2ecf20Sopenharmony_ci if (cm->chip_version < 39) { 27158c2ecf20Sopenharmony_ci err = snd_ctl_add(cm->card, 27168c2ecf20Sopenharmony_ci snd_ctl_new1(&snd_cmipci_modem_switch, cm)); 27178c2ecf20Sopenharmony_ci if (err < 0) 27188c2ecf20Sopenharmony_ci return err; 27198c2ecf20Sopenharmony_ci } 27208c2ecf20Sopenharmony_ci 27218c2ecf20Sopenharmony_ci for (idx = 0; idx < CM_SAVED_MIXERS; idx++) { 27228c2ecf20Sopenharmony_ci struct snd_ctl_elem_id elem_id; 27238c2ecf20Sopenharmony_ci struct snd_kcontrol *ctl; 27248c2ecf20Sopenharmony_ci memset(&elem_id, 0, sizeof(elem_id)); 27258c2ecf20Sopenharmony_ci elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 27268c2ecf20Sopenharmony_ci strcpy(elem_id.name, cm_saved_mixer[idx].name); 27278c2ecf20Sopenharmony_ci ctl = snd_ctl_find_id(cm->card, &elem_id); 27288c2ecf20Sopenharmony_ci if (ctl) 27298c2ecf20Sopenharmony_ci cm->mixer_res_ctl[idx] = ctl; 27308c2ecf20Sopenharmony_ci } 27318c2ecf20Sopenharmony_ci 27328c2ecf20Sopenharmony_ci return 0; 27338c2ecf20Sopenharmony_ci} 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_ci 27368c2ecf20Sopenharmony_ci/* 27378c2ecf20Sopenharmony_ci * proc interface 27388c2ecf20Sopenharmony_ci */ 27398c2ecf20Sopenharmony_ci 27408c2ecf20Sopenharmony_cistatic void snd_cmipci_proc_read(struct snd_info_entry *entry, 27418c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 27428c2ecf20Sopenharmony_ci{ 27438c2ecf20Sopenharmony_ci struct cmipci *cm = entry->private_data; 27448c2ecf20Sopenharmony_ci int i, v; 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_ci snd_iprintf(buffer, "%s\n", cm->card->longname); 27478c2ecf20Sopenharmony_ci for (i = 0; i < 0x94; i++) { 27488c2ecf20Sopenharmony_ci if (i == 0x28) 27498c2ecf20Sopenharmony_ci i = 0x90; 27508c2ecf20Sopenharmony_ci v = inb(cm->iobase + i); 27518c2ecf20Sopenharmony_ci if (i % 4 == 0) 27528c2ecf20Sopenharmony_ci snd_iprintf(buffer, "\n%02x:", i); 27538c2ecf20Sopenharmony_ci snd_iprintf(buffer, " %02x", v); 27548c2ecf20Sopenharmony_ci } 27558c2ecf20Sopenharmony_ci snd_iprintf(buffer, "\n"); 27568c2ecf20Sopenharmony_ci} 27578c2ecf20Sopenharmony_ci 27588c2ecf20Sopenharmony_cistatic void snd_cmipci_proc_init(struct cmipci *cm) 27598c2ecf20Sopenharmony_ci{ 27608c2ecf20Sopenharmony_ci snd_card_ro_proc_new(cm->card, "cmipci", cm, snd_cmipci_proc_read); 27618c2ecf20Sopenharmony_ci} 27628c2ecf20Sopenharmony_ci 27638c2ecf20Sopenharmony_cistatic const struct pci_device_id snd_cmipci_ids[] = { 27648c2ecf20Sopenharmony_ci {PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A), 0}, 27658c2ecf20Sopenharmony_ci {PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B), 0}, 27668c2ecf20Sopenharmony_ci {PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738), 0}, 27678c2ecf20Sopenharmony_ci {PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738B), 0}, 27688c2ecf20Sopenharmony_ci {PCI_VDEVICE(AL, PCI_DEVICE_ID_CMEDIA_CM8738), 0}, 27698c2ecf20Sopenharmony_ci {0,}, 27708c2ecf20Sopenharmony_ci}; 27718c2ecf20Sopenharmony_ci 27728c2ecf20Sopenharmony_ci 27738c2ecf20Sopenharmony_ci/* 27748c2ecf20Sopenharmony_ci * check chip version and capabilities 27758c2ecf20Sopenharmony_ci * driver name is modified according to the chip model 27768c2ecf20Sopenharmony_ci */ 27778c2ecf20Sopenharmony_cistatic void query_chip(struct cmipci *cm) 27788c2ecf20Sopenharmony_ci{ 27798c2ecf20Sopenharmony_ci unsigned int detect; 27808c2ecf20Sopenharmony_ci 27818c2ecf20Sopenharmony_ci /* check reg 0Ch, bit 24-31 */ 27828c2ecf20Sopenharmony_ci detect = snd_cmipci_read(cm, CM_REG_INT_HLDCLR) & CM_CHIP_MASK2; 27838c2ecf20Sopenharmony_ci if (! detect) { 27848c2ecf20Sopenharmony_ci /* check reg 08h, bit 24-28 */ 27858c2ecf20Sopenharmony_ci detect = snd_cmipci_read(cm, CM_REG_CHFORMAT) & CM_CHIP_MASK1; 27868c2ecf20Sopenharmony_ci switch (detect) { 27878c2ecf20Sopenharmony_ci case 0: 27888c2ecf20Sopenharmony_ci cm->chip_version = 33; 27898c2ecf20Sopenharmony_ci if (cm->do_soft_ac3) 27908c2ecf20Sopenharmony_ci cm->can_ac3_sw = 1; 27918c2ecf20Sopenharmony_ci else 27928c2ecf20Sopenharmony_ci cm->can_ac3_hw = 1; 27938c2ecf20Sopenharmony_ci break; 27948c2ecf20Sopenharmony_ci case CM_CHIP_037: 27958c2ecf20Sopenharmony_ci cm->chip_version = 37; 27968c2ecf20Sopenharmony_ci cm->can_ac3_hw = 1; 27978c2ecf20Sopenharmony_ci break; 27988c2ecf20Sopenharmony_ci default: 27998c2ecf20Sopenharmony_ci cm->chip_version = 39; 28008c2ecf20Sopenharmony_ci cm->can_ac3_hw = 1; 28018c2ecf20Sopenharmony_ci break; 28028c2ecf20Sopenharmony_ci } 28038c2ecf20Sopenharmony_ci cm->max_channels = 2; 28048c2ecf20Sopenharmony_ci } else { 28058c2ecf20Sopenharmony_ci if (detect & CM_CHIP_039) { 28068c2ecf20Sopenharmony_ci cm->chip_version = 39; 28078c2ecf20Sopenharmony_ci if (detect & CM_CHIP_039_6CH) /* 4 or 6 channels */ 28088c2ecf20Sopenharmony_ci cm->max_channels = 6; 28098c2ecf20Sopenharmony_ci else 28108c2ecf20Sopenharmony_ci cm->max_channels = 4; 28118c2ecf20Sopenharmony_ci } else if (detect & CM_CHIP_8768) { 28128c2ecf20Sopenharmony_ci cm->chip_version = 68; 28138c2ecf20Sopenharmony_ci cm->max_channels = 8; 28148c2ecf20Sopenharmony_ci cm->can_96k = 1; 28158c2ecf20Sopenharmony_ci } else { 28168c2ecf20Sopenharmony_ci cm->chip_version = 55; 28178c2ecf20Sopenharmony_ci cm->max_channels = 6; 28188c2ecf20Sopenharmony_ci cm->can_96k = 1; 28198c2ecf20Sopenharmony_ci } 28208c2ecf20Sopenharmony_ci cm->can_ac3_hw = 1; 28218c2ecf20Sopenharmony_ci cm->can_multi_ch = 1; 28228c2ecf20Sopenharmony_ci } 28238c2ecf20Sopenharmony_ci} 28248c2ecf20Sopenharmony_ci 28258c2ecf20Sopenharmony_ci#ifdef SUPPORT_JOYSTICK 28268c2ecf20Sopenharmony_cistatic int snd_cmipci_create_gameport(struct cmipci *cm, int dev) 28278c2ecf20Sopenharmony_ci{ 28288c2ecf20Sopenharmony_ci static const int ports[] = { 0x201, 0x200, 0 }; /* FIXME: majority is 0x201? */ 28298c2ecf20Sopenharmony_ci struct gameport *gp; 28308c2ecf20Sopenharmony_ci struct resource *r = NULL; 28318c2ecf20Sopenharmony_ci int i, io_port = 0; 28328c2ecf20Sopenharmony_ci 28338c2ecf20Sopenharmony_ci if (joystick_port[dev] == 0) 28348c2ecf20Sopenharmony_ci return -ENODEV; 28358c2ecf20Sopenharmony_ci 28368c2ecf20Sopenharmony_ci if (joystick_port[dev] == 1) { /* auto-detect */ 28378c2ecf20Sopenharmony_ci for (i = 0; ports[i]; i++) { 28388c2ecf20Sopenharmony_ci io_port = ports[i]; 28398c2ecf20Sopenharmony_ci r = request_region(io_port, 1, "CMIPCI gameport"); 28408c2ecf20Sopenharmony_ci if (r) 28418c2ecf20Sopenharmony_ci break; 28428c2ecf20Sopenharmony_ci } 28438c2ecf20Sopenharmony_ci } else { 28448c2ecf20Sopenharmony_ci io_port = joystick_port[dev]; 28458c2ecf20Sopenharmony_ci r = request_region(io_port, 1, "CMIPCI gameport"); 28468c2ecf20Sopenharmony_ci } 28478c2ecf20Sopenharmony_ci 28488c2ecf20Sopenharmony_ci if (!r) { 28498c2ecf20Sopenharmony_ci dev_warn(cm->card->dev, "cannot reserve joystick ports\n"); 28508c2ecf20Sopenharmony_ci return -EBUSY; 28518c2ecf20Sopenharmony_ci } 28528c2ecf20Sopenharmony_ci 28538c2ecf20Sopenharmony_ci cm->gameport = gp = gameport_allocate_port(); 28548c2ecf20Sopenharmony_ci if (!gp) { 28558c2ecf20Sopenharmony_ci dev_err(cm->card->dev, "cannot allocate memory for gameport\n"); 28568c2ecf20Sopenharmony_ci release_and_free_resource(r); 28578c2ecf20Sopenharmony_ci return -ENOMEM; 28588c2ecf20Sopenharmony_ci } 28598c2ecf20Sopenharmony_ci gameport_set_name(gp, "C-Media Gameport"); 28608c2ecf20Sopenharmony_ci gameport_set_phys(gp, "pci%s/gameport0", pci_name(cm->pci)); 28618c2ecf20Sopenharmony_ci gameport_set_dev_parent(gp, &cm->pci->dev); 28628c2ecf20Sopenharmony_ci gp->io = io_port; 28638c2ecf20Sopenharmony_ci gameport_set_port_data(gp, r); 28648c2ecf20Sopenharmony_ci 28658c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_JYSTK_EN); 28668c2ecf20Sopenharmony_ci 28678c2ecf20Sopenharmony_ci gameport_register_port(cm->gameport); 28688c2ecf20Sopenharmony_ci 28698c2ecf20Sopenharmony_ci return 0; 28708c2ecf20Sopenharmony_ci} 28718c2ecf20Sopenharmony_ci 28728c2ecf20Sopenharmony_cistatic void snd_cmipci_free_gameport(struct cmipci *cm) 28738c2ecf20Sopenharmony_ci{ 28748c2ecf20Sopenharmony_ci if (cm->gameport) { 28758c2ecf20Sopenharmony_ci struct resource *r = gameport_get_port_data(cm->gameport); 28768c2ecf20Sopenharmony_ci 28778c2ecf20Sopenharmony_ci gameport_unregister_port(cm->gameport); 28788c2ecf20Sopenharmony_ci cm->gameport = NULL; 28798c2ecf20Sopenharmony_ci 28808c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_JYSTK_EN); 28818c2ecf20Sopenharmony_ci release_and_free_resource(r); 28828c2ecf20Sopenharmony_ci } 28838c2ecf20Sopenharmony_ci} 28848c2ecf20Sopenharmony_ci#else 28858c2ecf20Sopenharmony_cistatic inline int snd_cmipci_create_gameport(struct cmipci *cm, int dev) { return -ENOSYS; } 28868c2ecf20Sopenharmony_cistatic inline void snd_cmipci_free_gameport(struct cmipci *cm) { } 28878c2ecf20Sopenharmony_ci#endif 28888c2ecf20Sopenharmony_ci 28898c2ecf20Sopenharmony_cistatic int snd_cmipci_free(struct cmipci *cm) 28908c2ecf20Sopenharmony_ci{ 28918c2ecf20Sopenharmony_ci if (cm->irq >= 0) { 28928c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN); 28938c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); 28948c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0); /* disable ints */ 28958c2ecf20Sopenharmony_ci snd_cmipci_ch_reset(cm, CM_CH_PLAY); 28968c2ecf20Sopenharmony_ci snd_cmipci_ch_reset(cm, CM_CH_CAPT); 28978c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_FUNCTRL0, 0); /* disable channels */ 28988c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_FUNCTRL1, 0); 28998c2ecf20Sopenharmony_ci 29008c2ecf20Sopenharmony_ci /* reset mixer */ 29018c2ecf20Sopenharmony_ci snd_cmipci_mixer_write(cm, 0, 0); 29028c2ecf20Sopenharmony_ci 29038c2ecf20Sopenharmony_ci free_irq(cm->irq, cm); 29048c2ecf20Sopenharmony_ci } 29058c2ecf20Sopenharmony_ci 29068c2ecf20Sopenharmony_ci snd_cmipci_free_gameport(cm); 29078c2ecf20Sopenharmony_ci pci_release_regions(cm->pci); 29088c2ecf20Sopenharmony_ci pci_disable_device(cm->pci); 29098c2ecf20Sopenharmony_ci kfree(cm); 29108c2ecf20Sopenharmony_ci return 0; 29118c2ecf20Sopenharmony_ci} 29128c2ecf20Sopenharmony_ci 29138c2ecf20Sopenharmony_cistatic int snd_cmipci_dev_free(struct snd_device *device) 29148c2ecf20Sopenharmony_ci{ 29158c2ecf20Sopenharmony_ci struct cmipci *cm = device->device_data; 29168c2ecf20Sopenharmony_ci return snd_cmipci_free(cm); 29178c2ecf20Sopenharmony_ci} 29188c2ecf20Sopenharmony_ci 29198c2ecf20Sopenharmony_cistatic int snd_cmipci_create_fm(struct cmipci *cm, long fm_port) 29208c2ecf20Sopenharmony_ci{ 29218c2ecf20Sopenharmony_ci long iosynth; 29228c2ecf20Sopenharmony_ci unsigned int val; 29238c2ecf20Sopenharmony_ci struct snd_opl3 *opl3; 29248c2ecf20Sopenharmony_ci int err; 29258c2ecf20Sopenharmony_ci 29268c2ecf20Sopenharmony_ci if (!fm_port) 29278c2ecf20Sopenharmony_ci goto disable_fm; 29288c2ecf20Sopenharmony_ci 29298c2ecf20Sopenharmony_ci if (cm->chip_version >= 39) { 29308c2ecf20Sopenharmony_ci /* first try FM regs in PCI port range */ 29318c2ecf20Sopenharmony_ci iosynth = cm->iobase + CM_REG_FM_PCI; 29328c2ecf20Sopenharmony_ci err = snd_opl3_create(cm->card, iosynth, iosynth + 2, 29338c2ecf20Sopenharmony_ci OPL3_HW_OPL3, 1, &opl3); 29348c2ecf20Sopenharmony_ci } else { 29358c2ecf20Sopenharmony_ci err = -EIO; 29368c2ecf20Sopenharmony_ci } 29378c2ecf20Sopenharmony_ci if (err < 0) { 29388c2ecf20Sopenharmony_ci /* then try legacy ports */ 29398c2ecf20Sopenharmony_ci val = snd_cmipci_read(cm, CM_REG_LEGACY_CTRL) & ~CM_FMSEL_MASK; 29408c2ecf20Sopenharmony_ci iosynth = fm_port; 29418c2ecf20Sopenharmony_ci switch (iosynth) { 29428c2ecf20Sopenharmony_ci case 0x3E8: val |= CM_FMSEL_3E8; break; 29438c2ecf20Sopenharmony_ci case 0x3E0: val |= CM_FMSEL_3E0; break; 29448c2ecf20Sopenharmony_ci case 0x3C8: val |= CM_FMSEL_3C8; break; 29458c2ecf20Sopenharmony_ci case 0x388: val |= CM_FMSEL_388; break; 29468c2ecf20Sopenharmony_ci default: 29478c2ecf20Sopenharmony_ci goto disable_fm; 29488c2ecf20Sopenharmony_ci } 29498c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, val); 29508c2ecf20Sopenharmony_ci /* enable FM */ 29518c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN); 29528c2ecf20Sopenharmony_ci 29538c2ecf20Sopenharmony_ci if (snd_opl3_create(cm->card, iosynth, iosynth + 2, 29548c2ecf20Sopenharmony_ci OPL3_HW_OPL3, 0, &opl3) < 0) { 29558c2ecf20Sopenharmony_ci dev_err(cm->card->dev, 29568c2ecf20Sopenharmony_ci "no OPL device at %#lx, skipping...\n", 29578c2ecf20Sopenharmony_ci iosynth); 29588c2ecf20Sopenharmony_ci goto disable_fm; 29598c2ecf20Sopenharmony_ci } 29608c2ecf20Sopenharmony_ci } 29618c2ecf20Sopenharmony_ci if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { 29628c2ecf20Sopenharmony_ci dev_err(cm->card->dev, "cannot create OPL3 hwdep\n"); 29638c2ecf20Sopenharmony_ci return err; 29648c2ecf20Sopenharmony_ci } 29658c2ecf20Sopenharmony_ci return 0; 29668c2ecf20Sopenharmony_ci 29678c2ecf20Sopenharmony_ci disable_fm: 29688c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_FMSEL_MASK); 29698c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_FM_EN); 29708c2ecf20Sopenharmony_ci return 0; 29718c2ecf20Sopenharmony_ci} 29728c2ecf20Sopenharmony_ci 29738c2ecf20Sopenharmony_cistatic int snd_cmipci_create(struct snd_card *card, struct pci_dev *pci, 29748c2ecf20Sopenharmony_ci int dev, struct cmipci **rcmipci) 29758c2ecf20Sopenharmony_ci{ 29768c2ecf20Sopenharmony_ci struct cmipci *cm; 29778c2ecf20Sopenharmony_ci int err; 29788c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 29798c2ecf20Sopenharmony_ci .dev_free = snd_cmipci_dev_free, 29808c2ecf20Sopenharmony_ci }; 29818c2ecf20Sopenharmony_ci unsigned int val; 29828c2ecf20Sopenharmony_ci long iomidi = 0; 29838c2ecf20Sopenharmony_ci int integrated_midi = 0; 29848c2ecf20Sopenharmony_ci char modelstr[16]; 29858c2ecf20Sopenharmony_ci int pcm_index, pcm_spdif_index; 29868c2ecf20Sopenharmony_ci static const struct pci_device_id intel_82437vx[] = { 29878c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437VX) }, 29888c2ecf20Sopenharmony_ci { }, 29898c2ecf20Sopenharmony_ci }; 29908c2ecf20Sopenharmony_ci 29918c2ecf20Sopenharmony_ci *rcmipci = NULL; 29928c2ecf20Sopenharmony_ci 29938c2ecf20Sopenharmony_ci if ((err = pci_enable_device(pci)) < 0) 29948c2ecf20Sopenharmony_ci return err; 29958c2ecf20Sopenharmony_ci 29968c2ecf20Sopenharmony_ci cm = kzalloc(sizeof(*cm), GFP_KERNEL); 29978c2ecf20Sopenharmony_ci if (cm == NULL) { 29988c2ecf20Sopenharmony_ci pci_disable_device(pci); 29998c2ecf20Sopenharmony_ci return -ENOMEM; 30008c2ecf20Sopenharmony_ci } 30018c2ecf20Sopenharmony_ci 30028c2ecf20Sopenharmony_ci spin_lock_init(&cm->reg_lock); 30038c2ecf20Sopenharmony_ci mutex_init(&cm->open_mutex); 30048c2ecf20Sopenharmony_ci cm->device = pci->device; 30058c2ecf20Sopenharmony_ci cm->card = card; 30068c2ecf20Sopenharmony_ci cm->pci = pci; 30078c2ecf20Sopenharmony_ci cm->irq = -1; 30088c2ecf20Sopenharmony_ci cm->channel[0].ch = 0; 30098c2ecf20Sopenharmony_ci cm->channel[1].ch = 1; 30108c2ecf20Sopenharmony_ci cm->channel[0].is_dac = cm->channel[1].is_dac = 1; /* dual DAC mode */ 30118c2ecf20Sopenharmony_ci 30128c2ecf20Sopenharmony_ci if ((err = pci_request_regions(pci, card->driver)) < 0) { 30138c2ecf20Sopenharmony_ci kfree(cm); 30148c2ecf20Sopenharmony_ci pci_disable_device(pci); 30158c2ecf20Sopenharmony_ci return err; 30168c2ecf20Sopenharmony_ci } 30178c2ecf20Sopenharmony_ci cm->iobase = pci_resource_start(pci, 0); 30188c2ecf20Sopenharmony_ci 30198c2ecf20Sopenharmony_ci if (request_irq(pci->irq, snd_cmipci_interrupt, 30208c2ecf20Sopenharmony_ci IRQF_SHARED, KBUILD_MODNAME, cm)) { 30218c2ecf20Sopenharmony_ci dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); 30228c2ecf20Sopenharmony_ci snd_cmipci_free(cm); 30238c2ecf20Sopenharmony_ci return -EBUSY; 30248c2ecf20Sopenharmony_ci } 30258c2ecf20Sopenharmony_ci cm->irq = pci->irq; 30268c2ecf20Sopenharmony_ci card->sync_irq = cm->irq; 30278c2ecf20Sopenharmony_ci 30288c2ecf20Sopenharmony_ci pci_set_master(cm->pci); 30298c2ecf20Sopenharmony_ci 30308c2ecf20Sopenharmony_ci /* 30318c2ecf20Sopenharmony_ci * check chip version, max channels and capabilities 30328c2ecf20Sopenharmony_ci */ 30338c2ecf20Sopenharmony_ci 30348c2ecf20Sopenharmony_ci cm->chip_version = 0; 30358c2ecf20Sopenharmony_ci cm->max_channels = 2; 30368c2ecf20Sopenharmony_ci cm->do_soft_ac3 = soft_ac3[dev]; 30378c2ecf20Sopenharmony_ci 30388c2ecf20Sopenharmony_ci if (pci->device != PCI_DEVICE_ID_CMEDIA_CM8338A && 30398c2ecf20Sopenharmony_ci pci->device != PCI_DEVICE_ID_CMEDIA_CM8338B) 30408c2ecf20Sopenharmony_ci query_chip(cm); 30418c2ecf20Sopenharmony_ci /* added -MCx suffix for chip supporting multi-channels */ 30428c2ecf20Sopenharmony_ci if (cm->can_multi_ch) 30438c2ecf20Sopenharmony_ci sprintf(cm->card->driver + strlen(cm->card->driver), 30448c2ecf20Sopenharmony_ci "-MC%d", cm->max_channels); 30458c2ecf20Sopenharmony_ci else if (cm->can_ac3_sw) 30468c2ecf20Sopenharmony_ci strcpy(cm->card->driver + strlen(cm->card->driver), "-SWIEC"); 30478c2ecf20Sopenharmony_ci 30488c2ecf20Sopenharmony_ci cm->dig_status = SNDRV_PCM_DEFAULT_CON_SPDIF; 30498c2ecf20Sopenharmony_ci cm->dig_pcm_status = SNDRV_PCM_DEFAULT_CON_SPDIF; 30508c2ecf20Sopenharmony_ci 30518c2ecf20Sopenharmony_ci#if CM_CH_PLAY == 1 30528c2ecf20Sopenharmony_ci cm->ctrl = CM_CHADC0; /* default FUNCNTRL0 */ 30538c2ecf20Sopenharmony_ci#else 30548c2ecf20Sopenharmony_ci cm->ctrl = CM_CHADC1; /* default FUNCNTRL0 */ 30558c2ecf20Sopenharmony_ci#endif 30568c2ecf20Sopenharmony_ci 30578c2ecf20Sopenharmony_ci /* initialize codec registers */ 30588c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_RESET); 30598c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_RESET); 30608c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0); /* disable ints */ 30618c2ecf20Sopenharmony_ci snd_cmipci_ch_reset(cm, CM_CH_PLAY); 30628c2ecf20Sopenharmony_ci snd_cmipci_ch_reset(cm, CM_CH_CAPT); 30638c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_FUNCTRL0, 0); /* disable channels */ 30648c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_FUNCTRL1, 0); 30658c2ecf20Sopenharmony_ci 30668c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_CHFORMAT, 0); 30678c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENDBDAC|CM_N4SPK3D); 30688c2ecf20Sopenharmony_ci#if CM_CH_PLAY == 1 30698c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC); 30708c2ecf20Sopenharmony_ci#else 30718c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC); 30728c2ecf20Sopenharmony_ci#endif 30738c2ecf20Sopenharmony_ci if (cm->chip_version) { 30748c2ecf20Sopenharmony_ci snd_cmipci_write_b(cm, CM_REG_EXT_MISC, 0x20); /* magic */ 30758c2ecf20Sopenharmony_ci snd_cmipci_write_b(cm, CM_REG_EXT_MISC + 1, 0x09); /* more magic */ 30768c2ecf20Sopenharmony_ci } 30778c2ecf20Sopenharmony_ci /* Set Bus Master Request */ 30788c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_BREQ); 30798c2ecf20Sopenharmony_ci 30808c2ecf20Sopenharmony_ci /* Assume TX and compatible chip set (Autodetection required for VX chip sets) */ 30818c2ecf20Sopenharmony_ci switch (pci->device) { 30828c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_CMEDIA_CM8738: 30838c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_CMEDIA_CM8738B: 30848c2ecf20Sopenharmony_ci if (!pci_dev_present(intel_82437vx)) 30858c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_TXVX); 30868c2ecf20Sopenharmony_ci break; 30878c2ecf20Sopenharmony_ci default: 30888c2ecf20Sopenharmony_ci break; 30898c2ecf20Sopenharmony_ci } 30908c2ecf20Sopenharmony_ci 30918c2ecf20Sopenharmony_ci if (cm->chip_version < 68) { 30928c2ecf20Sopenharmony_ci val = pci->device < 0x110 ? 8338 : 8738; 30938c2ecf20Sopenharmony_ci } else { 30948c2ecf20Sopenharmony_ci switch (snd_cmipci_read_b(cm, CM_REG_INT_HLDCLR + 3) & 0x03) { 30958c2ecf20Sopenharmony_ci case 0: 30968c2ecf20Sopenharmony_ci val = 8769; 30978c2ecf20Sopenharmony_ci break; 30988c2ecf20Sopenharmony_ci case 2: 30998c2ecf20Sopenharmony_ci val = 8762; 31008c2ecf20Sopenharmony_ci break; 31018c2ecf20Sopenharmony_ci default: 31028c2ecf20Sopenharmony_ci switch ((pci->subsystem_vendor << 16) | 31038c2ecf20Sopenharmony_ci pci->subsystem_device) { 31048c2ecf20Sopenharmony_ci case 0x13f69761: 31058c2ecf20Sopenharmony_ci case 0x584d3741: 31068c2ecf20Sopenharmony_ci case 0x584d3751: 31078c2ecf20Sopenharmony_ci case 0x584d3761: 31088c2ecf20Sopenharmony_ci case 0x584d3771: 31098c2ecf20Sopenharmony_ci case 0x72848384: 31108c2ecf20Sopenharmony_ci val = 8770; 31118c2ecf20Sopenharmony_ci break; 31128c2ecf20Sopenharmony_ci default: 31138c2ecf20Sopenharmony_ci val = 8768; 31148c2ecf20Sopenharmony_ci break; 31158c2ecf20Sopenharmony_ci } 31168c2ecf20Sopenharmony_ci } 31178c2ecf20Sopenharmony_ci } 31188c2ecf20Sopenharmony_ci sprintf(card->shortname, "C-Media CMI%d", val); 31198c2ecf20Sopenharmony_ci if (cm->chip_version < 68) 31208c2ecf20Sopenharmony_ci sprintf(modelstr, " (model %d)", cm->chip_version); 31218c2ecf20Sopenharmony_ci else 31228c2ecf20Sopenharmony_ci modelstr[0] = '\0'; 31238c2ecf20Sopenharmony_ci sprintf(card->longname, "%s%s at %#lx, irq %i", 31248c2ecf20Sopenharmony_ci card->shortname, modelstr, cm->iobase, cm->irq); 31258c2ecf20Sopenharmony_ci 31268c2ecf20Sopenharmony_ci if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, cm, &ops)) < 0) { 31278c2ecf20Sopenharmony_ci snd_cmipci_free(cm); 31288c2ecf20Sopenharmony_ci return err; 31298c2ecf20Sopenharmony_ci } 31308c2ecf20Sopenharmony_ci 31318c2ecf20Sopenharmony_ci if (cm->chip_version >= 39) { 31328c2ecf20Sopenharmony_ci val = snd_cmipci_read_b(cm, CM_REG_MPU_PCI + 1); 31338c2ecf20Sopenharmony_ci if (val != 0x00 && val != 0xff) { 31348c2ecf20Sopenharmony_ci if (mpu_port[dev]) 31358c2ecf20Sopenharmony_ci iomidi = cm->iobase + CM_REG_MPU_PCI; 31368c2ecf20Sopenharmony_ci integrated_midi = 1; 31378c2ecf20Sopenharmony_ci } 31388c2ecf20Sopenharmony_ci } 31398c2ecf20Sopenharmony_ci if (!integrated_midi) { 31408c2ecf20Sopenharmony_ci val = 0; 31418c2ecf20Sopenharmony_ci iomidi = mpu_port[dev]; 31428c2ecf20Sopenharmony_ci switch (iomidi) { 31438c2ecf20Sopenharmony_ci case 0x320: val = CM_VMPU_320; break; 31448c2ecf20Sopenharmony_ci case 0x310: val = CM_VMPU_310; break; 31458c2ecf20Sopenharmony_ci case 0x300: val = CM_VMPU_300; break; 31468c2ecf20Sopenharmony_ci case 0x330: val = CM_VMPU_330; break; 31478c2ecf20Sopenharmony_ci default: 31488c2ecf20Sopenharmony_ci iomidi = 0; break; 31498c2ecf20Sopenharmony_ci } 31508c2ecf20Sopenharmony_ci if (iomidi > 0) { 31518c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_LEGACY_CTRL, val); 31528c2ecf20Sopenharmony_ci /* enable UART */ 31538c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_UART_EN); 31548c2ecf20Sopenharmony_ci if (inb(iomidi + 1) == 0xff) { 31558c2ecf20Sopenharmony_ci dev_err(cm->card->dev, 31568c2ecf20Sopenharmony_ci "cannot enable MPU-401 port at %#lx\n", 31578c2ecf20Sopenharmony_ci iomidi); 31588c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, 31598c2ecf20Sopenharmony_ci CM_UART_EN); 31608c2ecf20Sopenharmony_ci iomidi = 0; 31618c2ecf20Sopenharmony_ci } 31628c2ecf20Sopenharmony_ci } 31638c2ecf20Sopenharmony_ci } 31648c2ecf20Sopenharmony_ci 31658c2ecf20Sopenharmony_ci if (cm->chip_version < 68) { 31668c2ecf20Sopenharmony_ci err = snd_cmipci_create_fm(cm, fm_port[dev]); 31678c2ecf20Sopenharmony_ci if (err < 0) 31688c2ecf20Sopenharmony_ci return err; 31698c2ecf20Sopenharmony_ci } 31708c2ecf20Sopenharmony_ci 31718c2ecf20Sopenharmony_ci /* reset mixer */ 31728c2ecf20Sopenharmony_ci snd_cmipci_mixer_write(cm, 0, 0); 31738c2ecf20Sopenharmony_ci 31748c2ecf20Sopenharmony_ci snd_cmipci_proc_init(cm); 31758c2ecf20Sopenharmony_ci 31768c2ecf20Sopenharmony_ci /* create pcm devices */ 31778c2ecf20Sopenharmony_ci pcm_index = pcm_spdif_index = 0; 31788c2ecf20Sopenharmony_ci if ((err = snd_cmipci_pcm_new(cm, pcm_index)) < 0) 31798c2ecf20Sopenharmony_ci return err; 31808c2ecf20Sopenharmony_ci pcm_index++; 31818c2ecf20Sopenharmony_ci if ((err = snd_cmipci_pcm2_new(cm, pcm_index)) < 0) 31828c2ecf20Sopenharmony_ci return err; 31838c2ecf20Sopenharmony_ci pcm_index++; 31848c2ecf20Sopenharmony_ci if (cm->can_ac3_hw || cm->can_ac3_sw) { 31858c2ecf20Sopenharmony_ci pcm_spdif_index = pcm_index; 31868c2ecf20Sopenharmony_ci if ((err = snd_cmipci_pcm_spdif_new(cm, pcm_index)) < 0) 31878c2ecf20Sopenharmony_ci return err; 31888c2ecf20Sopenharmony_ci } 31898c2ecf20Sopenharmony_ci 31908c2ecf20Sopenharmony_ci /* create mixer interface & switches */ 31918c2ecf20Sopenharmony_ci if ((err = snd_cmipci_mixer_new(cm, pcm_spdif_index)) < 0) 31928c2ecf20Sopenharmony_ci return err; 31938c2ecf20Sopenharmony_ci 31948c2ecf20Sopenharmony_ci if (iomidi > 0) { 31958c2ecf20Sopenharmony_ci if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI, 31968c2ecf20Sopenharmony_ci iomidi, 31978c2ecf20Sopenharmony_ci (integrated_midi ? 31988c2ecf20Sopenharmony_ci MPU401_INFO_INTEGRATED : 0) | 31998c2ecf20Sopenharmony_ci MPU401_INFO_IRQ_HOOK, 32008c2ecf20Sopenharmony_ci -1, &cm->rmidi)) < 0) { 32018c2ecf20Sopenharmony_ci dev_err(cm->card->dev, 32028c2ecf20Sopenharmony_ci "no UART401 device at 0x%lx\n", iomidi); 32038c2ecf20Sopenharmony_ci } 32048c2ecf20Sopenharmony_ci } 32058c2ecf20Sopenharmony_ci 32068c2ecf20Sopenharmony_ci#ifdef USE_VAR48KRATE 32078c2ecf20Sopenharmony_ci for (val = 0; val < ARRAY_SIZE(rates); val++) 32088c2ecf20Sopenharmony_ci snd_cmipci_set_pll(cm, rates[val], val); 32098c2ecf20Sopenharmony_ci 32108c2ecf20Sopenharmony_ci /* 32118c2ecf20Sopenharmony_ci * (Re-)Enable external switch spdo_48k 32128c2ecf20Sopenharmony_ci */ 32138c2ecf20Sopenharmony_ci snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K|CM_SPDF_AC97); 32148c2ecf20Sopenharmony_ci#endif /* USE_VAR48KRATE */ 32158c2ecf20Sopenharmony_ci 32168c2ecf20Sopenharmony_ci if (snd_cmipci_create_gameport(cm, dev) < 0) 32178c2ecf20Sopenharmony_ci snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_JYSTK_EN); 32188c2ecf20Sopenharmony_ci 32198c2ecf20Sopenharmony_ci *rcmipci = cm; 32208c2ecf20Sopenharmony_ci return 0; 32218c2ecf20Sopenharmony_ci} 32228c2ecf20Sopenharmony_ci 32238c2ecf20Sopenharmony_ci/* 32248c2ecf20Sopenharmony_ci */ 32258c2ecf20Sopenharmony_ci 32268c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_cmipci_ids); 32278c2ecf20Sopenharmony_ci 32288c2ecf20Sopenharmony_cistatic int snd_cmipci_probe(struct pci_dev *pci, 32298c2ecf20Sopenharmony_ci const struct pci_device_id *pci_id) 32308c2ecf20Sopenharmony_ci{ 32318c2ecf20Sopenharmony_ci static int dev; 32328c2ecf20Sopenharmony_ci struct snd_card *card; 32338c2ecf20Sopenharmony_ci struct cmipci *cm; 32348c2ecf20Sopenharmony_ci int err; 32358c2ecf20Sopenharmony_ci 32368c2ecf20Sopenharmony_ci if (dev >= SNDRV_CARDS) 32378c2ecf20Sopenharmony_ci return -ENODEV; 32388c2ecf20Sopenharmony_ci if (! enable[dev]) { 32398c2ecf20Sopenharmony_ci dev++; 32408c2ecf20Sopenharmony_ci return -ENOENT; 32418c2ecf20Sopenharmony_ci } 32428c2ecf20Sopenharmony_ci 32438c2ecf20Sopenharmony_ci err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 32448c2ecf20Sopenharmony_ci 0, &card); 32458c2ecf20Sopenharmony_ci if (err < 0) 32468c2ecf20Sopenharmony_ci return err; 32478c2ecf20Sopenharmony_ci 32488c2ecf20Sopenharmony_ci switch (pci->device) { 32498c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_CMEDIA_CM8738: 32508c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_CMEDIA_CM8738B: 32518c2ecf20Sopenharmony_ci strcpy(card->driver, "CMI8738"); 32528c2ecf20Sopenharmony_ci break; 32538c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_CMEDIA_CM8338A: 32548c2ecf20Sopenharmony_ci case PCI_DEVICE_ID_CMEDIA_CM8338B: 32558c2ecf20Sopenharmony_ci strcpy(card->driver, "CMI8338"); 32568c2ecf20Sopenharmony_ci break; 32578c2ecf20Sopenharmony_ci default: 32588c2ecf20Sopenharmony_ci strcpy(card->driver, "CMIPCI"); 32598c2ecf20Sopenharmony_ci break; 32608c2ecf20Sopenharmony_ci } 32618c2ecf20Sopenharmony_ci 32628c2ecf20Sopenharmony_ci err = snd_cmipci_create(card, pci, dev, &cm); 32638c2ecf20Sopenharmony_ci if (err < 0) 32648c2ecf20Sopenharmony_ci goto free_card; 32658c2ecf20Sopenharmony_ci 32668c2ecf20Sopenharmony_ci card->private_data = cm; 32678c2ecf20Sopenharmony_ci 32688c2ecf20Sopenharmony_ci err = snd_card_register(card); 32698c2ecf20Sopenharmony_ci if (err < 0) 32708c2ecf20Sopenharmony_ci goto free_card; 32718c2ecf20Sopenharmony_ci 32728c2ecf20Sopenharmony_ci pci_set_drvdata(pci, card); 32738c2ecf20Sopenharmony_ci dev++; 32748c2ecf20Sopenharmony_ci return 0; 32758c2ecf20Sopenharmony_ci 32768c2ecf20Sopenharmony_cifree_card: 32778c2ecf20Sopenharmony_ci snd_card_free(card); 32788c2ecf20Sopenharmony_ci return err; 32798c2ecf20Sopenharmony_ci} 32808c2ecf20Sopenharmony_ci 32818c2ecf20Sopenharmony_cistatic void snd_cmipci_remove(struct pci_dev *pci) 32828c2ecf20Sopenharmony_ci{ 32838c2ecf20Sopenharmony_ci snd_card_free(pci_get_drvdata(pci)); 32848c2ecf20Sopenharmony_ci} 32858c2ecf20Sopenharmony_ci 32868c2ecf20Sopenharmony_ci 32878c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 32888c2ecf20Sopenharmony_ci/* 32898c2ecf20Sopenharmony_ci * power management 32908c2ecf20Sopenharmony_ci */ 32918c2ecf20Sopenharmony_cistatic const unsigned char saved_regs[] = { 32928c2ecf20Sopenharmony_ci CM_REG_FUNCTRL1, CM_REG_CHFORMAT, CM_REG_LEGACY_CTRL, CM_REG_MISC_CTRL, 32938c2ecf20Sopenharmony_ci CM_REG_MIXER0, CM_REG_MIXER1, CM_REG_MIXER2, CM_REG_AUX_VOL, CM_REG_PLL, 32948c2ecf20Sopenharmony_ci CM_REG_CH0_FRAME1, CM_REG_CH0_FRAME2, 32958c2ecf20Sopenharmony_ci CM_REG_CH1_FRAME1, CM_REG_CH1_FRAME2, CM_REG_EXT_MISC, 32968c2ecf20Sopenharmony_ci CM_REG_INT_STATUS, CM_REG_INT_HLDCLR, CM_REG_FUNCTRL0, 32978c2ecf20Sopenharmony_ci}; 32988c2ecf20Sopenharmony_ci 32998c2ecf20Sopenharmony_cistatic const unsigned char saved_mixers[] = { 33008c2ecf20Sopenharmony_ci SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1, 33018c2ecf20Sopenharmony_ci SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1, 33028c2ecf20Sopenharmony_ci SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1, 33038c2ecf20Sopenharmony_ci SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1, 33048c2ecf20Sopenharmony_ci SB_DSP4_LINE_DEV, SB_DSP4_LINE_DEV + 1, 33058c2ecf20Sopenharmony_ci SB_DSP4_MIC_DEV, SB_DSP4_SPEAKER_DEV, 33068c2ecf20Sopenharmony_ci CM_REG_EXTENT_IND, SB_DSP4_OUTPUT_SW, 33078c2ecf20Sopenharmony_ci SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 33088c2ecf20Sopenharmony_ci}; 33098c2ecf20Sopenharmony_ci 33108c2ecf20Sopenharmony_cistatic int snd_cmipci_suspend(struct device *dev) 33118c2ecf20Sopenharmony_ci{ 33128c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 33138c2ecf20Sopenharmony_ci struct cmipci *cm = card->private_data; 33148c2ecf20Sopenharmony_ci int i; 33158c2ecf20Sopenharmony_ci 33168c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 33178c2ecf20Sopenharmony_ci 33188c2ecf20Sopenharmony_ci /* save registers */ 33198c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(saved_regs); i++) 33208c2ecf20Sopenharmony_ci cm->saved_regs[i] = snd_cmipci_read(cm, saved_regs[i]); 33218c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(saved_mixers); i++) 33228c2ecf20Sopenharmony_ci cm->saved_mixers[i] = snd_cmipci_mixer_read(cm, saved_mixers[i]); 33238c2ecf20Sopenharmony_ci 33248c2ecf20Sopenharmony_ci /* disable ints */ 33258c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0); 33268c2ecf20Sopenharmony_ci return 0; 33278c2ecf20Sopenharmony_ci} 33288c2ecf20Sopenharmony_ci 33298c2ecf20Sopenharmony_cistatic int snd_cmipci_resume(struct device *dev) 33308c2ecf20Sopenharmony_ci{ 33318c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 33328c2ecf20Sopenharmony_ci struct cmipci *cm = card->private_data; 33338c2ecf20Sopenharmony_ci int i; 33348c2ecf20Sopenharmony_ci 33358c2ecf20Sopenharmony_ci /* reset / initialize to a sane state */ 33368c2ecf20Sopenharmony_ci snd_cmipci_write(cm, CM_REG_INT_HLDCLR, 0); 33378c2ecf20Sopenharmony_ci snd_cmipci_ch_reset(cm, CM_CH_PLAY); 33388c2ecf20Sopenharmony_ci snd_cmipci_ch_reset(cm, CM_CH_CAPT); 33398c2ecf20Sopenharmony_ci snd_cmipci_mixer_write(cm, 0, 0); 33408c2ecf20Sopenharmony_ci 33418c2ecf20Sopenharmony_ci /* restore registers */ 33428c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(saved_regs); i++) 33438c2ecf20Sopenharmony_ci snd_cmipci_write(cm, saved_regs[i], cm->saved_regs[i]); 33448c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(saved_mixers); i++) 33458c2ecf20Sopenharmony_ci snd_cmipci_mixer_write(cm, saved_mixers[i], cm->saved_mixers[i]); 33468c2ecf20Sopenharmony_ci 33478c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D0); 33488c2ecf20Sopenharmony_ci return 0; 33498c2ecf20Sopenharmony_ci} 33508c2ecf20Sopenharmony_ci 33518c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(snd_cmipci_pm, snd_cmipci_suspend, snd_cmipci_resume); 33528c2ecf20Sopenharmony_ci#define SND_CMIPCI_PM_OPS &snd_cmipci_pm 33538c2ecf20Sopenharmony_ci#else 33548c2ecf20Sopenharmony_ci#define SND_CMIPCI_PM_OPS NULL 33558c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 33568c2ecf20Sopenharmony_ci 33578c2ecf20Sopenharmony_cistatic struct pci_driver cmipci_driver = { 33588c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 33598c2ecf20Sopenharmony_ci .id_table = snd_cmipci_ids, 33608c2ecf20Sopenharmony_ci .probe = snd_cmipci_probe, 33618c2ecf20Sopenharmony_ci .remove = snd_cmipci_remove, 33628c2ecf20Sopenharmony_ci .driver = { 33638c2ecf20Sopenharmony_ci .pm = SND_CMIPCI_PM_OPS, 33648c2ecf20Sopenharmony_ci }, 33658c2ecf20Sopenharmony_ci}; 33668c2ecf20Sopenharmony_ci 33678c2ecf20Sopenharmony_cimodule_pci_driver(cmipci_driver); 3368