18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Cirrus Logic CS4281 based PCI soundcard 48c2ecf20Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz>, 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/io.h> 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/pci.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/gameport.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <sound/core.h> 168c2ecf20Sopenharmony_ci#include <sound/control.h> 178c2ecf20Sopenharmony_ci#include <sound/pcm.h> 188c2ecf20Sopenharmony_ci#include <sound/rawmidi.h> 198c2ecf20Sopenharmony_ci#include <sound/ac97_codec.h> 208c2ecf20Sopenharmony_ci#include <sound/tlv.h> 218c2ecf20Sopenharmony_ci#include <sound/opl3.h> 228c2ecf20Sopenharmony_ci#include <sound/initval.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); 268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Cirrus Logic CS4281"); 278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 288c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Cirrus Logic,CS4281}}"); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ 318c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 328c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ 338c2ecf20Sopenharmony_cistatic bool dual_codec[SNDRV_CARDS]; /* dual codec */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 368c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for CS4281 soundcard."); 378c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for CS4281 soundcard."); 398c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 408c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable CS4281 soundcard."); 418c2ecf20Sopenharmony_cimodule_param_array(dual_codec, bool, NULL, 0444); 428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dual_codec, "Secondary Codec ID (0 = disabled)."); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* 458c2ecf20Sopenharmony_ci * Direct registers 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define CS4281_BA0_SIZE 0x1000 498c2ecf20Sopenharmony_ci#define CS4281_BA1_SIZE 0x10000 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* 528c2ecf20Sopenharmony_ci * BA0 registers 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ci#define BA0_HISR 0x0000 /* Host Interrupt Status Register */ 558c2ecf20Sopenharmony_ci#define BA0_HISR_INTENA (1<<31) /* Internal Interrupt Enable Bit */ 568c2ecf20Sopenharmony_ci#define BA0_HISR_MIDI (1<<22) /* MIDI port interrupt */ 578c2ecf20Sopenharmony_ci#define BA0_HISR_FIFOI (1<<20) /* FIFO polled interrupt */ 588c2ecf20Sopenharmony_ci#define BA0_HISR_DMAI (1<<18) /* DMA interrupt (half or end) */ 598c2ecf20Sopenharmony_ci#define BA0_HISR_FIFO(c) (1<<(12+(c))) /* FIFO channel interrupt */ 608c2ecf20Sopenharmony_ci#define BA0_HISR_DMA(c) (1<<(8+(c))) /* DMA channel interrupt */ 618c2ecf20Sopenharmony_ci#define BA0_HISR_GPPI (1<<5) /* General Purpose Input (Primary chip) */ 628c2ecf20Sopenharmony_ci#define BA0_HISR_GPSI (1<<4) /* General Purpose Input (Secondary chip) */ 638c2ecf20Sopenharmony_ci#define BA0_HISR_GP3I (1<<3) /* GPIO3 pin Interrupt */ 648c2ecf20Sopenharmony_ci#define BA0_HISR_GP1I (1<<2) /* GPIO1 pin Interrupt */ 658c2ecf20Sopenharmony_ci#define BA0_HISR_VUPI (1<<1) /* VOLUP pin Interrupt */ 668c2ecf20Sopenharmony_ci#define BA0_HISR_VDNI (1<<0) /* VOLDN pin Interrupt */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define BA0_HICR 0x0008 /* Host Interrupt Control Register */ 698c2ecf20Sopenharmony_ci#define BA0_HICR_CHGM (1<<1) /* INTENA Change Mask */ 708c2ecf20Sopenharmony_ci#define BA0_HICR_IEV (1<<0) /* INTENA Value */ 718c2ecf20Sopenharmony_ci#define BA0_HICR_EOI (3<<0) /* End of Interrupt command */ 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define BA0_HIMR 0x000c /* Host Interrupt Mask Register */ 748c2ecf20Sopenharmony_ci /* Use same contants as for BA0_HISR */ 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define BA0_IIER 0x0010 /* ISA Interrupt Enable Register */ 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define BA0_HDSR0 0x00f0 /* Host DMA Engine 0 Status Register */ 798c2ecf20Sopenharmony_ci#define BA0_HDSR1 0x00f4 /* Host DMA Engine 1 Status Register */ 808c2ecf20Sopenharmony_ci#define BA0_HDSR2 0x00f8 /* Host DMA Engine 2 Status Register */ 818c2ecf20Sopenharmony_ci#define BA0_HDSR3 0x00fc /* Host DMA Engine 3 Status Register */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define BA0_HDSR_CH1P (1<<25) /* Channel 1 Pending */ 848c2ecf20Sopenharmony_ci#define BA0_HDSR_CH2P (1<<24) /* Channel 2 Pending */ 858c2ecf20Sopenharmony_ci#define BA0_HDSR_DHTC (1<<17) /* DMA Half Terminal Count */ 868c2ecf20Sopenharmony_ci#define BA0_HDSR_DTC (1<<16) /* DMA Terminal Count */ 878c2ecf20Sopenharmony_ci#define BA0_HDSR_DRUN (1<<15) /* DMA Running */ 888c2ecf20Sopenharmony_ci#define BA0_HDSR_RQ (1<<7) /* Pending Request */ 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define BA0_DCA0 0x0110 /* Host DMA Engine 0 Current Address */ 918c2ecf20Sopenharmony_ci#define BA0_DCC0 0x0114 /* Host DMA Engine 0 Current Count */ 928c2ecf20Sopenharmony_ci#define BA0_DBA0 0x0118 /* Host DMA Engine 0 Base Address */ 938c2ecf20Sopenharmony_ci#define BA0_DBC0 0x011c /* Host DMA Engine 0 Base Count */ 948c2ecf20Sopenharmony_ci#define BA0_DCA1 0x0120 /* Host DMA Engine 1 Current Address */ 958c2ecf20Sopenharmony_ci#define BA0_DCC1 0x0124 /* Host DMA Engine 1 Current Count */ 968c2ecf20Sopenharmony_ci#define BA0_DBA1 0x0128 /* Host DMA Engine 1 Base Address */ 978c2ecf20Sopenharmony_ci#define BA0_DBC1 0x012c /* Host DMA Engine 1 Base Count */ 988c2ecf20Sopenharmony_ci#define BA0_DCA2 0x0130 /* Host DMA Engine 2 Current Address */ 998c2ecf20Sopenharmony_ci#define BA0_DCC2 0x0134 /* Host DMA Engine 2 Current Count */ 1008c2ecf20Sopenharmony_ci#define BA0_DBA2 0x0138 /* Host DMA Engine 2 Base Address */ 1018c2ecf20Sopenharmony_ci#define BA0_DBC2 0x013c /* Host DMA Engine 2 Base Count */ 1028c2ecf20Sopenharmony_ci#define BA0_DCA3 0x0140 /* Host DMA Engine 3 Current Address */ 1038c2ecf20Sopenharmony_ci#define BA0_DCC3 0x0144 /* Host DMA Engine 3 Current Count */ 1048c2ecf20Sopenharmony_ci#define BA0_DBA3 0x0148 /* Host DMA Engine 3 Base Address */ 1058c2ecf20Sopenharmony_ci#define BA0_DBC3 0x014c /* Host DMA Engine 3 Base Count */ 1068c2ecf20Sopenharmony_ci#define BA0_DMR0 0x0150 /* Host DMA Engine 0 Mode */ 1078c2ecf20Sopenharmony_ci#define BA0_DCR0 0x0154 /* Host DMA Engine 0 Command */ 1088c2ecf20Sopenharmony_ci#define BA0_DMR1 0x0158 /* Host DMA Engine 1 Mode */ 1098c2ecf20Sopenharmony_ci#define BA0_DCR1 0x015c /* Host DMA Engine 1 Command */ 1108c2ecf20Sopenharmony_ci#define BA0_DMR2 0x0160 /* Host DMA Engine 2 Mode */ 1118c2ecf20Sopenharmony_ci#define BA0_DCR2 0x0164 /* Host DMA Engine 2 Command */ 1128c2ecf20Sopenharmony_ci#define BA0_DMR3 0x0168 /* Host DMA Engine 3 Mode */ 1138c2ecf20Sopenharmony_ci#define BA0_DCR3 0x016c /* Host DMA Engine 3 Command */ 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci#define BA0_DMR_DMA (1<<29) /* Enable DMA mode */ 1168c2ecf20Sopenharmony_ci#define BA0_DMR_POLL (1<<28) /* Enable poll mode */ 1178c2ecf20Sopenharmony_ci#define BA0_DMR_TBC (1<<25) /* Transfer By Channel */ 1188c2ecf20Sopenharmony_ci#define BA0_DMR_CBC (1<<24) /* Count By Channel (0 = frame resolution) */ 1198c2ecf20Sopenharmony_ci#define BA0_DMR_SWAPC (1<<22) /* Swap Left/Right Channels */ 1208c2ecf20Sopenharmony_ci#define BA0_DMR_SIZE20 (1<<20) /* Sample is 20-bit */ 1218c2ecf20Sopenharmony_ci#define BA0_DMR_USIGN (1<<19) /* Unsigned */ 1228c2ecf20Sopenharmony_ci#define BA0_DMR_BEND (1<<18) /* Big Endian */ 1238c2ecf20Sopenharmony_ci#define BA0_DMR_MONO (1<<17) /* Mono */ 1248c2ecf20Sopenharmony_ci#define BA0_DMR_SIZE8 (1<<16) /* Sample is 8-bit */ 1258c2ecf20Sopenharmony_ci#define BA0_DMR_TYPE_DEMAND (0<<6) 1268c2ecf20Sopenharmony_ci#define BA0_DMR_TYPE_SINGLE (1<<6) 1278c2ecf20Sopenharmony_ci#define BA0_DMR_TYPE_BLOCK (2<<6) 1288c2ecf20Sopenharmony_ci#define BA0_DMR_TYPE_CASCADE (3<<6) /* Not supported */ 1298c2ecf20Sopenharmony_ci#define BA0_DMR_DEC (1<<5) /* Access Increment (0) or Decrement (1) */ 1308c2ecf20Sopenharmony_ci#define BA0_DMR_AUTO (1<<4) /* Auto-Initialize */ 1318c2ecf20Sopenharmony_ci#define BA0_DMR_TR_VERIFY (0<<2) /* Verify Transfer */ 1328c2ecf20Sopenharmony_ci#define BA0_DMR_TR_WRITE (1<<2) /* Write Transfer */ 1338c2ecf20Sopenharmony_ci#define BA0_DMR_TR_READ (2<<2) /* Read Transfer */ 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci#define BA0_DCR_HTCIE (1<<17) /* Half Terminal Count Interrupt */ 1368c2ecf20Sopenharmony_ci#define BA0_DCR_TCIE (1<<16) /* Terminal Count Interrupt */ 1378c2ecf20Sopenharmony_ci#define BA0_DCR_MSK (1<<0) /* DMA Mask bit */ 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci#define BA0_FCR0 0x0180 /* FIFO Control 0 */ 1408c2ecf20Sopenharmony_ci#define BA0_FCR1 0x0184 /* FIFO Control 1 */ 1418c2ecf20Sopenharmony_ci#define BA0_FCR2 0x0188 /* FIFO Control 2 */ 1428c2ecf20Sopenharmony_ci#define BA0_FCR3 0x018c /* FIFO Control 3 */ 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci#define BA0_FCR_FEN (1<<31) /* FIFO Enable bit */ 1458c2ecf20Sopenharmony_ci#define BA0_FCR_DACZ (1<<30) /* DAC Zero */ 1468c2ecf20Sopenharmony_ci#define BA0_FCR_PSH (1<<29) /* Previous Sample Hold */ 1478c2ecf20Sopenharmony_ci#define BA0_FCR_RS(x) (((x)&0x1f)<<24) /* Right Slot Mapping */ 1488c2ecf20Sopenharmony_ci#define BA0_FCR_LS(x) (((x)&0x1f)<<16) /* Left Slot Mapping */ 1498c2ecf20Sopenharmony_ci#define BA0_FCR_SZ(x) (((x)&0x7f)<<8) /* FIFO buffer size (in samples) */ 1508c2ecf20Sopenharmony_ci#define BA0_FCR_OF(x) (((x)&0x7f)<<0) /* FIFO starting offset (in samples) */ 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci#define BA0_FPDR0 0x0190 /* FIFO Polled Data 0 */ 1538c2ecf20Sopenharmony_ci#define BA0_FPDR1 0x0194 /* FIFO Polled Data 1 */ 1548c2ecf20Sopenharmony_ci#define BA0_FPDR2 0x0198 /* FIFO Polled Data 2 */ 1558c2ecf20Sopenharmony_ci#define BA0_FPDR3 0x019c /* FIFO Polled Data 3 */ 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci#define BA0_FCHS 0x020c /* FIFO Channel Status */ 1588c2ecf20Sopenharmony_ci#define BA0_FCHS_RCO(x) (1<<(7+(((x)&3)<<3))) /* Right Channel Out */ 1598c2ecf20Sopenharmony_ci#define BA0_FCHS_LCO(x) (1<<(6+(((x)&3)<<3))) /* Left Channel Out */ 1608c2ecf20Sopenharmony_ci#define BA0_FCHS_MRP(x) (1<<(5+(((x)&3)<<3))) /* Move Read Pointer */ 1618c2ecf20Sopenharmony_ci#define BA0_FCHS_FE(x) (1<<(4+(((x)&3)<<3))) /* FIFO Empty */ 1628c2ecf20Sopenharmony_ci#define BA0_FCHS_FF(x) (1<<(3+(((x)&3)<<3))) /* FIFO Full */ 1638c2ecf20Sopenharmony_ci#define BA0_FCHS_IOR(x) (1<<(2+(((x)&3)<<3))) /* Internal Overrun Flag */ 1648c2ecf20Sopenharmony_ci#define BA0_FCHS_RCI(x) (1<<(1+(((x)&3)<<3))) /* Right Channel In */ 1658c2ecf20Sopenharmony_ci#define BA0_FCHS_LCI(x) (1<<(0+(((x)&3)<<3))) /* Left Channel In */ 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci#define BA0_FSIC0 0x0210 /* FIFO Status and Interrupt Control 0 */ 1688c2ecf20Sopenharmony_ci#define BA0_FSIC1 0x0214 /* FIFO Status and Interrupt Control 1 */ 1698c2ecf20Sopenharmony_ci#define BA0_FSIC2 0x0218 /* FIFO Status and Interrupt Control 2 */ 1708c2ecf20Sopenharmony_ci#define BA0_FSIC3 0x021c /* FIFO Status and Interrupt Control 3 */ 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci#define BA0_FSIC_FIC(x) (((x)&0x7f)<<24) /* FIFO Interrupt Count */ 1738c2ecf20Sopenharmony_ci#define BA0_FSIC_FORIE (1<<23) /* FIFO OverRun Interrupt Enable */ 1748c2ecf20Sopenharmony_ci#define BA0_FSIC_FURIE (1<<22) /* FIFO UnderRun Interrupt Enable */ 1758c2ecf20Sopenharmony_ci#define BA0_FSIC_FSCIE (1<<16) /* FIFO Sample Count Interrupt Enable */ 1768c2ecf20Sopenharmony_ci#define BA0_FSIC_FSC(x) (((x)&0x7f)<<8) /* FIFO Sample Count */ 1778c2ecf20Sopenharmony_ci#define BA0_FSIC_FOR (1<<7) /* FIFO OverRun */ 1788c2ecf20Sopenharmony_ci#define BA0_FSIC_FUR (1<<6) /* FIFO UnderRun */ 1798c2ecf20Sopenharmony_ci#define BA0_FSIC_FSCR (1<<0) /* FIFO Sample Count Reached */ 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci#define BA0_PMCS 0x0344 /* Power Management Control/Status */ 1828c2ecf20Sopenharmony_ci#define BA0_CWPR 0x03e0 /* Configuration Write Protect */ 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci#define BA0_EPPMC 0x03e4 /* Extended PCI Power Management Control */ 1858c2ecf20Sopenharmony_ci#define BA0_EPPMC_FPDN (1<<14) /* Full Power DowN */ 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci#define BA0_GPIOR 0x03e8 /* GPIO Pin Interface Register */ 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci#define BA0_SPMC 0x03ec /* Serial Port Power Management Control (& ASDIN2 enable) */ 1908c2ecf20Sopenharmony_ci#define BA0_SPMC_GIPPEN (1<<15) /* GP INT Primary PME# Enable */ 1918c2ecf20Sopenharmony_ci#define BA0_SPMC_GISPEN (1<<14) /* GP INT Secondary PME# Enable */ 1928c2ecf20Sopenharmony_ci#define BA0_SPMC_EESPD (1<<9) /* EEPROM Serial Port Disable */ 1938c2ecf20Sopenharmony_ci#define BA0_SPMC_ASDI2E (1<<8) /* ASDIN2 Enable */ 1948c2ecf20Sopenharmony_ci#define BA0_SPMC_ASDO (1<<7) /* Asynchronous ASDOUT Assertion */ 1958c2ecf20Sopenharmony_ci#define BA0_SPMC_WUP2 (1<<3) /* Wakeup for Secondary Input */ 1968c2ecf20Sopenharmony_ci#define BA0_SPMC_WUP1 (1<<2) /* Wakeup for Primary Input */ 1978c2ecf20Sopenharmony_ci#define BA0_SPMC_ASYNC (1<<1) /* Asynchronous ASYNC Assertion */ 1988c2ecf20Sopenharmony_ci#define BA0_SPMC_RSTN (1<<0) /* Reset Not! */ 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci#define BA0_CFLR 0x03f0 /* Configuration Load Register (EEPROM or BIOS) */ 2018c2ecf20Sopenharmony_ci#define BA0_CFLR_DEFAULT 0x00000001 /* CFLR must be in AC97 link mode */ 2028c2ecf20Sopenharmony_ci#define BA0_IISR 0x03f4 /* ISA Interrupt Select */ 2038c2ecf20Sopenharmony_ci#define BA0_TMS 0x03f8 /* Test Register */ 2048c2ecf20Sopenharmony_ci#define BA0_SSVID 0x03fc /* Subsystem ID register */ 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci#define BA0_CLKCR1 0x0400 /* Clock Control Register 1 */ 2078c2ecf20Sopenharmony_ci#define BA0_CLKCR1_CLKON (1<<25) /* Read Only */ 2088c2ecf20Sopenharmony_ci#define BA0_CLKCR1_DLLRDY (1<<24) /* DLL Ready */ 2098c2ecf20Sopenharmony_ci#define BA0_CLKCR1_DLLOS (1<<6) /* DLL Output Select */ 2108c2ecf20Sopenharmony_ci#define BA0_CLKCR1_SWCE (1<<5) /* Clock Enable */ 2118c2ecf20Sopenharmony_ci#define BA0_CLKCR1_DLLP (1<<4) /* DLL PowerUp */ 2128c2ecf20Sopenharmony_ci#define BA0_CLKCR1_DLLSS (((x)&3)<<3) /* DLL Source Select */ 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci#define BA0_FRR 0x0410 /* Feature Reporting Register */ 2158c2ecf20Sopenharmony_ci#define BA0_SLT12O 0x041c /* Slot 12 GPIO Output Register for AC-Link */ 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci#define BA0_SERMC 0x0420 /* Serial Port Master Control */ 2188c2ecf20Sopenharmony_ci#define BA0_SERMC_FCRN (1<<27) /* Force Codec Ready Not */ 2198c2ecf20Sopenharmony_ci#define BA0_SERMC_ODSEN2 (1<<25) /* On-Demand Support Enable ASDIN2 */ 2208c2ecf20Sopenharmony_ci#define BA0_SERMC_ODSEN1 (1<<24) /* On-Demand Support Enable ASDIN1 */ 2218c2ecf20Sopenharmony_ci#define BA0_SERMC_SXLB (1<<21) /* ASDIN2 to ASDOUT Loopback */ 2228c2ecf20Sopenharmony_ci#define BA0_SERMC_SLB (1<<20) /* ASDOUT to ASDIN2 Loopback */ 2238c2ecf20Sopenharmony_ci#define BA0_SERMC_LOVF (1<<19) /* Loopback Output Valid Frame bit */ 2248c2ecf20Sopenharmony_ci#define BA0_SERMC_TCID(x) (((x)&3)<<16) /* Target Secondary Codec ID */ 2258c2ecf20Sopenharmony_ci#define BA0_SERMC_PXLB (5<<1) /* Primary Port External Loopback */ 2268c2ecf20Sopenharmony_ci#define BA0_SERMC_PLB (4<<1) /* Primary Port Internal Loopback */ 2278c2ecf20Sopenharmony_ci#define BA0_SERMC_PTC (7<<1) /* Port Timing Configuration */ 2288c2ecf20Sopenharmony_ci#define BA0_SERMC_PTC_AC97 (1<<1) /* AC97 mode */ 2298c2ecf20Sopenharmony_ci#define BA0_SERMC_MSPE (1<<0) /* Master Serial Port Enable */ 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci#define BA0_SERC1 0x0428 /* Serial Port Configuration 1 */ 2328c2ecf20Sopenharmony_ci#define BA0_SERC1_SO1F(x) (((x)&7)>>1) /* Primary Output Port Format */ 2338c2ecf20Sopenharmony_ci#define BA0_SERC1_AC97 (1<<1) 2348c2ecf20Sopenharmony_ci#define BA0_SERC1_SO1EN (1<<0) /* Primary Output Port Enable */ 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci#define BA0_SERC2 0x042c /* Serial Port Configuration 2 */ 2378c2ecf20Sopenharmony_ci#define BA0_SERC2_SI1F(x) (((x)&7)>>1) /* Primary Input Port Format */ 2388c2ecf20Sopenharmony_ci#define BA0_SERC2_AC97 (1<<1) 2398c2ecf20Sopenharmony_ci#define BA0_SERC2_SI1EN (1<<0) /* Primary Input Port Enable */ 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci#define BA0_SLT12M 0x045c /* Slot 12 Monitor Register for Primary AC-Link */ 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci#define BA0_ACCTL 0x0460 /* AC'97 Control */ 2448c2ecf20Sopenharmony_ci#define BA0_ACCTL_TC (1<<6) /* Target Codec */ 2458c2ecf20Sopenharmony_ci#define BA0_ACCTL_CRW (1<<4) /* 0=Write, 1=Read Command */ 2468c2ecf20Sopenharmony_ci#define BA0_ACCTL_DCV (1<<3) /* Dynamic Command Valid */ 2478c2ecf20Sopenharmony_ci#define BA0_ACCTL_VFRM (1<<2) /* Valid Frame */ 2488c2ecf20Sopenharmony_ci#define BA0_ACCTL_ESYN (1<<1) /* Enable Sync */ 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci#define BA0_ACSTS 0x0464 /* AC'97 Status */ 2518c2ecf20Sopenharmony_ci#define BA0_ACSTS_VSTS (1<<1) /* Valid Status */ 2528c2ecf20Sopenharmony_ci#define BA0_ACSTS_CRDY (1<<0) /* Codec Ready */ 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci#define BA0_ACOSV 0x0468 /* AC'97 Output Slot Valid */ 2558c2ecf20Sopenharmony_ci#define BA0_ACOSV_SLV(x) (1<<((x)-3)) 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci#define BA0_ACCAD 0x046c /* AC'97 Command Address */ 2588c2ecf20Sopenharmony_ci#define BA0_ACCDA 0x0470 /* AC'97 Command Data */ 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci#define BA0_ACISV 0x0474 /* AC'97 Input Slot Valid */ 2618c2ecf20Sopenharmony_ci#define BA0_ACISV_SLV(x) (1<<((x)-3)) 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci#define BA0_ACSAD 0x0478 /* AC'97 Status Address */ 2648c2ecf20Sopenharmony_ci#define BA0_ACSDA 0x047c /* AC'97 Status Data */ 2658c2ecf20Sopenharmony_ci#define BA0_JSPT 0x0480 /* Joystick poll/trigger */ 2668c2ecf20Sopenharmony_ci#define BA0_JSCTL 0x0484 /* Joystick control */ 2678c2ecf20Sopenharmony_ci#define BA0_JSC1 0x0488 /* Joystick control */ 2688c2ecf20Sopenharmony_ci#define BA0_JSC2 0x048c /* Joystick control */ 2698c2ecf20Sopenharmony_ci#define BA0_JSIO 0x04a0 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci#define BA0_MIDCR 0x0490 /* MIDI Control */ 2728c2ecf20Sopenharmony_ci#define BA0_MIDCR_MRST (1<<5) /* Reset MIDI Interface */ 2738c2ecf20Sopenharmony_ci#define BA0_MIDCR_MLB (1<<4) /* MIDI Loop Back Enable */ 2748c2ecf20Sopenharmony_ci#define BA0_MIDCR_TIE (1<<3) /* MIDI Transmuit Interrupt Enable */ 2758c2ecf20Sopenharmony_ci#define BA0_MIDCR_RIE (1<<2) /* MIDI Receive Interrupt Enable */ 2768c2ecf20Sopenharmony_ci#define BA0_MIDCR_RXE (1<<1) /* MIDI Receive Enable */ 2778c2ecf20Sopenharmony_ci#define BA0_MIDCR_TXE (1<<0) /* MIDI Transmit Enable */ 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci#define BA0_MIDCMD 0x0494 /* MIDI Command (wo) */ 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci#define BA0_MIDSR 0x0494 /* MIDI Status (ro) */ 2828c2ecf20Sopenharmony_ci#define BA0_MIDSR_RDA (1<<15) /* Sticky bit (RBE 1->0) */ 2838c2ecf20Sopenharmony_ci#define BA0_MIDSR_TBE (1<<14) /* Sticky bit (TBF 0->1) */ 2848c2ecf20Sopenharmony_ci#define BA0_MIDSR_RBE (1<<7) /* Receive Buffer Empty */ 2858c2ecf20Sopenharmony_ci#define BA0_MIDSR_TBF (1<<6) /* Transmit Buffer Full */ 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci#define BA0_MIDWP 0x0498 /* MIDI Write */ 2888c2ecf20Sopenharmony_ci#define BA0_MIDRP 0x049c /* MIDI Read (ro) */ 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci#define BA0_AODSD1 0x04a8 /* AC'97 On-Demand Slot Disable for primary link (ro) */ 2918c2ecf20Sopenharmony_ci#define BA0_AODSD1_NDS(x) (1<<((x)-3)) 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci#define BA0_AODSD2 0x04ac /* AC'97 On-Demand Slot Disable for secondary link (ro) */ 2948c2ecf20Sopenharmony_ci#define BA0_AODSD2_NDS(x) (1<<((x)-3)) 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci#define BA0_CFGI 0x04b0 /* Configure Interface (EEPROM interface) */ 2978c2ecf20Sopenharmony_ci#define BA0_SLT12M2 0x04dc /* Slot 12 Monitor Register 2 for secondary AC-link */ 2988c2ecf20Sopenharmony_ci#define BA0_ACSTS2 0x04e4 /* AC'97 Status Register 2 */ 2998c2ecf20Sopenharmony_ci#define BA0_ACISV2 0x04f4 /* AC'97 Input Slot Valid Register 2 */ 3008c2ecf20Sopenharmony_ci#define BA0_ACSAD2 0x04f8 /* AC'97 Status Address Register 2 */ 3018c2ecf20Sopenharmony_ci#define BA0_ACSDA2 0x04fc /* AC'97 Status Data Register 2 */ 3028c2ecf20Sopenharmony_ci#define BA0_FMSR 0x0730 /* FM Synthesis Status (ro) */ 3038c2ecf20Sopenharmony_ci#define BA0_B0AP 0x0730 /* FM Bank 0 Address Port (wo) */ 3048c2ecf20Sopenharmony_ci#define BA0_FMDP 0x0734 /* FM Data Port */ 3058c2ecf20Sopenharmony_ci#define BA0_B1AP 0x0738 /* FM Bank 1 Address Port */ 3068c2ecf20Sopenharmony_ci#define BA0_B1DP 0x073c /* FM Bank 1 Data Port */ 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci#define BA0_SSPM 0x0740 /* Sound System Power Management */ 3098c2ecf20Sopenharmony_ci#define BA0_SSPM_MIXEN (1<<6) /* Playback SRC + FM/Wavetable MIX */ 3108c2ecf20Sopenharmony_ci#define BA0_SSPM_CSRCEN (1<<5) /* Capture Sample Rate Converter Enable */ 3118c2ecf20Sopenharmony_ci#define BA0_SSPM_PSRCEN (1<<4) /* Playback Sample Rate Converter Enable */ 3128c2ecf20Sopenharmony_ci#define BA0_SSPM_JSEN (1<<3) /* Joystick Enable */ 3138c2ecf20Sopenharmony_ci#define BA0_SSPM_ACLEN (1<<2) /* Serial Port Engine and AC-Link Enable */ 3148c2ecf20Sopenharmony_ci#define BA0_SSPM_FMEN (1<<1) /* FM Synthesis Block Enable */ 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci#define BA0_DACSR 0x0744 /* DAC Sample Rate - Playback SRC */ 3178c2ecf20Sopenharmony_ci#define BA0_ADCSR 0x0748 /* ADC Sample Rate - Capture SRC */ 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci#define BA0_SSCR 0x074c /* Sound System Control Register */ 3208c2ecf20Sopenharmony_ci#define BA0_SSCR_HVS1 (1<<23) /* Hardwave Volume Step (0=1,1=2) */ 3218c2ecf20Sopenharmony_ci#define BA0_SSCR_MVCS (1<<19) /* Master Volume Codec Select */ 3228c2ecf20Sopenharmony_ci#define BA0_SSCR_MVLD (1<<18) /* Master Volume Line Out Disable */ 3238c2ecf20Sopenharmony_ci#define BA0_SSCR_MVAD (1<<17) /* Master Volume Alternate Out Disable */ 3248c2ecf20Sopenharmony_ci#define BA0_SSCR_MVMD (1<<16) /* Master Volume Mono Out Disable */ 3258c2ecf20Sopenharmony_ci#define BA0_SSCR_XLPSRC (1<<8) /* External SRC Loopback Mode */ 3268c2ecf20Sopenharmony_ci#define BA0_SSCR_LPSRC (1<<7) /* SRC Loopback Mode */ 3278c2ecf20Sopenharmony_ci#define BA0_SSCR_CDTX (1<<5) /* CD Transfer Data */ 3288c2ecf20Sopenharmony_ci#define BA0_SSCR_HVC (1<<3) /* Harware Volume Control Enable */ 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci#define BA0_FMLVC 0x0754 /* FM Synthesis Left Volume Control */ 3318c2ecf20Sopenharmony_ci#define BA0_FMRVC 0x0758 /* FM Synthesis Right Volume Control */ 3328c2ecf20Sopenharmony_ci#define BA0_SRCSA 0x075c /* SRC Slot Assignments */ 3338c2ecf20Sopenharmony_ci#define BA0_PPLVC 0x0760 /* PCM Playback Left Volume Control */ 3348c2ecf20Sopenharmony_ci#define BA0_PPRVC 0x0764 /* PCM Playback Right Volume Control */ 3358c2ecf20Sopenharmony_ci#define BA0_PASR 0x0768 /* playback sample rate */ 3368c2ecf20Sopenharmony_ci#define BA0_CASR 0x076C /* capture sample rate */ 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci/* Source Slot Numbers - Playback */ 3398c2ecf20Sopenharmony_ci#define SRCSLOT_LEFT_PCM_PLAYBACK 0 3408c2ecf20Sopenharmony_ci#define SRCSLOT_RIGHT_PCM_PLAYBACK 1 3418c2ecf20Sopenharmony_ci#define SRCSLOT_PHONE_LINE_1_DAC 2 3428c2ecf20Sopenharmony_ci#define SRCSLOT_CENTER_PCM_PLAYBACK 3 3438c2ecf20Sopenharmony_ci#define SRCSLOT_LEFT_SURROUND_PCM_PLAYBACK 4 3448c2ecf20Sopenharmony_ci#define SRCSLOT_RIGHT_SURROUND_PCM_PLAYBACK 5 3458c2ecf20Sopenharmony_ci#define SRCSLOT_LFE_PCM_PLAYBACK 6 3468c2ecf20Sopenharmony_ci#define SRCSLOT_PHONE_LINE_2_DAC 7 3478c2ecf20Sopenharmony_ci#define SRCSLOT_HEADSET_DAC 8 3488c2ecf20Sopenharmony_ci#define SRCSLOT_LEFT_WT 29 /* invalid for BA0_SRCSA */ 3498c2ecf20Sopenharmony_ci#define SRCSLOT_RIGHT_WT 30 /* invalid for BA0_SRCSA */ 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci/* Source Slot Numbers - Capture */ 3528c2ecf20Sopenharmony_ci#define SRCSLOT_LEFT_PCM_RECORD 10 3538c2ecf20Sopenharmony_ci#define SRCSLOT_RIGHT_PCM_RECORD 11 3548c2ecf20Sopenharmony_ci#define SRCSLOT_PHONE_LINE_1_ADC 12 3558c2ecf20Sopenharmony_ci#define SRCSLOT_MIC_ADC 13 3568c2ecf20Sopenharmony_ci#define SRCSLOT_PHONE_LINE_2_ADC 17 3578c2ecf20Sopenharmony_ci#define SRCSLOT_HEADSET_ADC 18 3588c2ecf20Sopenharmony_ci#define SRCSLOT_SECONDARY_LEFT_PCM_RECORD 20 3598c2ecf20Sopenharmony_ci#define SRCSLOT_SECONDARY_RIGHT_PCM_RECORD 21 3608c2ecf20Sopenharmony_ci#define SRCSLOT_SECONDARY_PHONE_LINE_1_ADC 22 3618c2ecf20Sopenharmony_ci#define SRCSLOT_SECONDARY_MIC_ADC 23 3628c2ecf20Sopenharmony_ci#define SRCSLOT_SECONDARY_PHONE_LINE_2_ADC 27 3638c2ecf20Sopenharmony_ci#define SRCSLOT_SECONDARY_HEADSET_ADC 28 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci/* Source Slot Numbers - Others */ 3668c2ecf20Sopenharmony_ci#define SRCSLOT_POWER_DOWN 31 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci/* MIDI modes */ 3698c2ecf20Sopenharmony_ci#define CS4281_MODE_OUTPUT (1<<0) 3708c2ecf20Sopenharmony_ci#define CS4281_MODE_INPUT (1<<1) 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci/* joystick bits */ 3738c2ecf20Sopenharmony_ci/* Bits for JSPT */ 3748c2ecf20Sopenharmony_ci#define JSPT_CAX 0x00000001 3758c2ecf20Sopenharmony_ci#define JSPT_CAY 0x00000002 3768c2ecf20Sopenharmony_ci#define JSPT_CBX 0x00000004 3778c2ecf20Sopenharmony_ci#define JSPT_CBY 0x00000008 3788c2ecf20Sopenharmony_ci#define JSPT_BA1 0x00000010 3798c2ecf20Sopenharmony_ci#define JSPT_BA2 0x00000020 3808c2ecf20Sopenharmony_ci#define JSPT_BB1 0x00000040 3818c2ecf20Sopenharmony_ci#define JSPT_BB2 0x00000080 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci/* Bits for JSCTL */ 3848c2ecf20Sopenharmony_ci#define JSCTL_SP_MASK 0x00000003 3858c2ecf20Sopenharmony_ci#define JSCTL_SP_SLOW 0x00000000 3868c2ecf20Sopenharmony_ci#define JSCTL_SP_MEDIUM_SLOW 0x00000001 3878c2ecf20Sopenharmony_ci#define JSCTL_SP_MEDIUM_FAST 0x00000002 3888c2ecf20Sopenharmony_ci#define JSCTL_SP_FAST 0x00000003 3898c2ecf20Sopenharmony_ci#define JSCTL_ARE 0x00000004 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci/* Data register pairs masks */ 3928c2ecf20Sopenharmony_ci#define JSC1_Y1V_MASK 0x0000FFFF 3938c2ecf20Sopenharmony_ci#define JSC1_X1V_MASK 0xFFFF0000 3948c2ecf20Sopenharmony_ci#define JSC1_Y1V_SHIFT 0 3958c2ecf20Sopenharmony_ci#define JSC1_X1V_SHIFT 16 3968c2ecf20Sopenharmony_ci#define JSC2_Y2V_MASK 0x0000FFFF 3978c2ecf20Sopenharmony_ci#define JSC2_X2V_MASK 0xFFFF0000 3988c2ecf20Sopenharmony_ci#define JSC2_Y2V_SHIFT 0 3998c2ecf20Sopenharmony_ci#define JSC2_X2V_SHIFT 16 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci/* JS GPIO */ 4028c2ecf20Sopenharmony_ci#define JSIO_DAX 0x00000001 4038c2ecf20Sopenharmony_ci#define JSIO_DAY 0x00000002 4048c2ecf20Sopenharmony_ci#define JSIO_DBX 0x00000004 4058c2ecf20Sopenharmony_ci#define JSIO_DBY 0x00000008 4068c2ecf20Sopenharmony_ci#define JSIO_AXOE 0x00000010 4078c2ecf20Sopenharmony_ci#define JSIO_AYOE 0x00000020 4088c2ecf20Sopenharmony_ci#define JSIO_BXOE 0x00000040 4098c2ecf20Sopenharmony_ci#define JSIO_BYOE 0x00000080 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci/* 4128c2ecf20Sopenharmony_ci * 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistruct cs4281_dma { 4168c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 4178c2ecf20Sopenharmony_ci unsigned int regDBA; /* offset to DBA register */ 4188c2ecf20Sopenharmony_ci unsigned int regDCA; /* offset to DCA register */ 4198c2ecf20Sopenharmony_ci unsigned int regDBC; /* offset to DBC register */ 4208c2ecf20Sopenharmony_ci unsigned int regDCC; /* offset to DCC register */ 4218c2ecf20Sopenharmony_ci unsigned int regDMR; /* offset to DMR register */ 4228c2ecf20Sopenharmony_ci unsigned int regDCR; /* offset to DCR register */ 4238c2ecf20Sopenharmony_ci unsigned int regHDSR; /* offset to HDSR register */ 4248c2ecf20Sopenharmony_ci unsigned int regFCR; /* offset to FCR register */ 4258c2ecf20Sopenharmony_ci unsigned int regFSIC; /* offset to FSIC register */ 4268c2ecf20Sopenharmony_ci unsigned int valDMR; /* DMA mode */ 4278c2ecf20Sopenharmony_ci unsigned int valDCR; /* DMA command */ 4288c2ecf20Sopenharmony_ci unsigned int valFCR; /* FIFO control */ 4298c2ecf20Sopenharmony_ci unsigned int fifo_offset; /* FIFO offset within BA1 */ 4308c2ecf20Sopenharmony_ci unsigned char left_slot; /* FIFO left slot */ 4318c2ecf20Sopenharmony_ci unsigned char right_slot; /* FIFO right slot */ 4328c2ecf20Sopenharmony_ci int frag; /* period number */ 4338c2ecf20Sopenharmony_ci}; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci#define SUSPEND_REGISTERS 20 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistruct cs4281 { 4388c2ecf20Sopenharmony_ci int irq; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci void __iomem *ba0; /* virtual (accessible) address */ 4418c2ecf20Sopenharmony_ci void __iomem *ba1; /* virtual (accessible) address */ 4428c2ecf20Sopenharmony_ci unsigned long ba0_addr; 4438c2ecf20Sopenharmony_ci unsigned long ba1_addr; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci int dual_codec; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci struct snd_ac97_bus *ac97_bus; 4488c2ecf20Sopenharmony_ci struct snd_ac97 *ac97; 4498c2ecf20Sopenharmony_ci struct snd_ac97 *ac97_secondary; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci struct pci_dev *pci; 4528c2ecf20Sopenharmony_ci struct snd_card *card; 4538c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 4548c2ecf20Sopenharmony_ci struct snd_rawmidi *rmidi; 4558c2ecf20Sopenharmony_ci struct snd_rawmidi_substream *midi_input; 4568c2ecf20Sopenharmony_ci struct snd_rawmidi_substream *midi_output; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci struct cs4281_dma dma[4]; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci unsigned char src_left_play_slot; 4618c2ecf20Sopenharmony_ci unsigned char src_right_play_slot; 4628c2ecf20Sopenharmony_ci unsigned char src_left_rec_slot; 4638c2ecf20Sopenharmony_ci unsigned char src_right_rec_slot; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci unsigned int spurious_dhtc_irq; 4668c2ecf20Sopenharmony_ci unsigned int spurious_dtc_irq; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci spinlock_t reg_lock; 4698c2ecf20Sopenharmony_ci unsigned int midcr; 4708c2ecf20Sopenharmony_ci unsigned int uartm; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci struct gameport *gameport; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4758c2ecf20Sopenharmony_ci u32 suspend_regs[SUSPEND_REGISTERS]; 4768c2ecf20Sopenharmony_ci#endif 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci}; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic const struct pci_device_id snd_cs4281_ids[] = { 4838c2ecf20Sopenharmony_ci { PCI_VDEVICE(CIRRUS, 0x6005), 0, }, /* CS4281 */ 4848c2ecf20Sopenharmony_ci { 0, } 4858c2ecf20Sopenharmony_ci}; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_cs4281_ids); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci/* 4908c2ecf20Sopenharmony_ci * constants 4918c2ecf20Sopenharmony_ci */ 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci#define CS4281_FIFO_SIZE 32 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci/* 4968c2ecf20Sopenharmony_ci * common I/O routines 4978c2ecf20Sopenharmony_ci */ 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic inline void snd_cs4281_pokeBA0(struct cs4281 *chip, unsigned long offset, 5008c2ecf20Sopenharmony_ci unsigned int val) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci writel(val, chip->ba0 + offset); 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic inline unsigned int snd_cs4281_peekBA0(struct cs4281 *chip, unsigned long offset) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci return readl(chip->ba0 + offset); 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic void snd_cs4281_ac97_write(struct snd_ac97 *ac97, 5118c2ecf20Sopenharmony_ci unsigned short reg, unsigned short val) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci /* 5148c2ecf20Sopenharmony_ci * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address 5158c2ecf20Sopenharmony_ci * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 5168c2ecf20Sopenharmony_ci * 3. Write ACCTL = Control Register = 460h for initiating the write 5178c2ecf20Sopenharmony_ci * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h 5188c2ecf20Sopenharmony_ci * 5. if DCV not cleared, break and return error 5198c2ecf20Sopenharmony_ci */ 5208c2ecf20Sopenharmony_ci struct cs4281 *chip = ac97->private_data; 5218c2ecf20Sopenharmony_ci int count; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci /* 5248c2ecf20Sopenharmony_ci * Setup the AC97 control registers on the CS461x to send the 5258c2ecf20Sopenharmony_ci * appropriate command to the AC97 to perform the read. 5268c2ecf20Sopenharmony_ci * ACCAD = Command Address Register = 46Ch 5278c2ecf20Sopenharmony_ci * ACCDA = Command Data Register = 470h 5288c2ecf20Sopenharmony_ci * ACCTL = Control Register = 460h 5298c2ecf20Sopenharmony_ci * set DCV - will clear when process completed 5308c2ecf20Sopenharmony_ci * reset CRW - Write command 5318c2ecf20Sopenharmony_ci * set VFRM - valid frame enabled 5328c2ecf20Sopenharmony_ci * set ESYN - ASYNC generation enabled 5338c2ecf20Sopenharmony_ci * set RSTN - ARST# inactive, AC97 codec not reset 5348c2ecf20Sopenharmony_ci */ 5358c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_ACCAD, reg); 5368c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_ACCDA, val); 5378c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_DCV | BA0_ACCTL_VFRM | 5388c2ecf20Sopenharmony_ci BA0_ACCTL_ESYN | (ac97->num ? BA0_ACCTL_TC : 0)); 5398c2ecf20Sopenharmony_ci for (count = 0; count < 2000; count++) { 5408c2ecf20Sopenharmony_ci /* 5418c2ecf20Sopenharmony_ci * First, we want to wait for a short time. 5428c2ecf20Sopenharmony_ci */ 5438c2ecf20Sopenharmony_ci udelay(10); 5448c2ecf20Sopenharmony_ci /* 5458c2ecf20Sopenharmony_ci * Now, check to see if the write has completed. 5468c2ecf20Sopenharmony_ci * ACCTL = 460h, DCV should be reset by now and 460h = 07h 5478c2ecf20Sopenharmony_ci */ 5488c2ecf20Sopenharmony_ci if (!(snd_cs4281_peekBA0(chip, BA0_ACCTL) & BA0_ACCTL_DCV)) { 5498c2ecf20Sopenharmony_ci return; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 5538c2ecf20Sopenharmony_ci "AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val); 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic unsigned short snd_cs4281_ac97_read(struct snd_ac97 *ac97, 5578c2ecf20Sopenharmony_ci unsigned short reg) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci struct cs4281 *chip = ac97->private_data; 5608c2ecf20Sopenharmony_ci int count; 5618c2ecf20Sopenharmony_ci unsigned short result; 5628c2ecf20Sopenharmony_ci // FIXME: volatile is necessary in the following due to a bug of 5638c2ecf20Sopenharmony_ci // some gcc versions 5648c2ecf20Sopenharmony_ci volatile int ac97_num = ((volatile struct snd_ac97 *)ac97)->num; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* 5678c2ecf20Sopenharmony_ci * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address 5688c2ecf20Sopenharmony_ci * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 5698c2ecf20Sopenharmony_ci * 3. Write ACCTL = Control Register = 460h for initiating the write 5708c2ecf20Sopenharmony_ci * 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h 5718c2ecf20Sopenharmony_ci * 5. if DCV not cleared, break and return error 5728c2ecf20Sopenharmony_ci * 6. Read ACSTS = Status Register = 464h, check VSTS bit 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSDA2 : BA0_ACSDA); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci /* 5788c2ecf20Sopenharmony_ci * Setup the AC97 control registers on the CS461x to send the 5798c2ecf20Sopenharmony_ci * appropriate command to the AC97 to perform the read. 5808c2ecf20Sopenharmony_ci * ACCAD = Command Address Register = 46Ch 5818c2ecf20Sopenharmony_ci * ACCDA = Command Data Register = 470h 5828c2ecf20Sopenharmony_ci * ACCTL = Control Register = 460h 5838c2ecf20Sopenharmony_ci * set DCV - will clear when process completed 5848c2ecf20Sopenharmony_ci * set CRW - Read command 5858c2ecf20Sopenharmony_ci * set VFRM - valid frame enabled 5868c2ecf20Sopenharmony_ci * set ESYN - ASYNC generation enabled 5878c2ecf20Sopenharmony_ci * set RSTN - ARST# inactive, AC97 codec not reset 5888c2ecf20Sopenharmony_ci */ 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_ACCAD, reg); 5918c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_ACCDA, 0); 5928c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_DCV | BA0_ACCTL_CRW | 5938c2ecf20Sopenharmony_ci BA0_ACCTL_VFRM | BA0_ACCTL_ESYN | 5948c2ecf20Sopenharmony_ci (ac97_num ? BA0_ACCTL_TC : 0)); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* 5988c2ecf20Sopenharmony_ci * Wait for the read to occur. 5998c2ecf20Sopenharmony_ci */ 6008c2ecf20Sopenharmony_ci for (count = 0; count < 500; count++) { 6018c2ecf20Sopenharmony_ci /* 6028c2ecf20Sopenharmony_ci * First, we want to wait for a short time. 6038c2ecf20Sopenharmony_ci */ 6048c2ecf20Sopenharmony_ci udelay(10); 6058c2ecf20Sopenharmony_ci /* 6068c2ecf20Sopenharmony_ci * Now, check to see if the read has completed. 6078c2ecf20Sopenharmony_ci * ACCTL = 460h, DCV should be reset by now and 460h = 17h 6088c2ecf20Sopenharmony_ci */ 6098c2ecf20Sopenharmony_ci if (!(snd_cs4281_peekBA0(chip, BA0_ACCTL) & BA0_ACCTL_DCV)) 6108c2ecf20Sopenharmony_ci goto __ok1; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 6148c2ecf20Sopenharmony_ci "AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg); 6158c2ecf20Sopenharmony_ci result = 0xffff; 6168c2ecf20Sopenharmony_ci goto __end; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci __ok1: 6198c2ecf20Sopenharmony_ci /* 6208c2ecf20Sopenharmony_ci * Wait for the valid status bit to go active. 6218c2ecf20Sopenharmony_ci */ 6228c2ecf20Sopenharmony_ci for (count = 0; count < 100; count++) { 6238c2ecf20Sopenharmony_ci /* 6248c2ecf20Sopenharmony_ci * Read the AC97 status register. 6258c2ecf20Sopenharmony_ci * ACSTS = Status Register = 464h 6268c2ecf20Sopenharmony_ci * VSTS - Valid Status 6278c2ecf20Sopenharmony_ci */ 6288c2ecf20Sopenharmony_ci if (snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSTS2 : BA0_ACSTS) & BA0_ACSTS_VSTS) 6298c2ecf20Sopenharmony_ci goto __ok2; 6308c2ecf20Sopenharmony_ci udelay(10); 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 6348c2ecf20Sopenharmony_ci "AC'97 read problem (ACSTS_VSTS), reg = 0x%x\n", reg); 6358c2ecf20Sopenharmony_ci result = 0xffff; 6368c2ecf20Sopenharmony_ci goto __end; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci __ok2: 6398c2ecf20Sopenharmony_ci /* 6408c2ecf20Sopenharmony_ci * Read the data returned from the AC97 register. 6418c2ecf20Sopenharmony_ci * ACSDA = Status Data Register = 474h 6428c2ecf20Sopenharmony_ci */ 6438c2ecf20Sopenharmony_ci result = snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSDA2 : BA0_ACSDA); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci __end: 6468c2ecf20Sopenharmony_ci return result; 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci/* 6508c2ecf20Sopenharmony_ci * PCM part 6518c2ecf20Sopenharmony_ci */ 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic int snd_cs4281_trigger(struct snd_pcm_substream *substream, int cmd) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci struct cs4281_dma *dma = substream->runtime->private_data; 6568c2ecf20Sopenharmony_ci struct cs4281 *chip = snd_pcm_substream_chip(substream); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci spin_lock(&chip->reg_lock); 6598c2ecf20Sopenharmony_ci switch (cmd) { 6608c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 6618c2ecf20Sopenharmony_ci dma->valDCR |= BA0_DCR_MSK; 6628c2ecf20Sopenharmony_ci dma->valFCR |= BA0_FCR_FEN; 6638c2ecf20Sopenharmony_ci break; 6648c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 6658c2ecf20Sopenharmony_ci dma->valDCR &= ~BA0_DCR_MSK; 6668c2ecf20Sopenharmony_ci dma->valFCR &= ~BA0_FCR_FEN; 6678c2ecf20Sopenharmony_ci break; 6688c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 6698c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 6708c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, dma->regDMR, dma->valDMR & ~BA0_DMR_DMA); 6718c2ecf20Sopenharmony_ci dma->valDMR |= BA0_DMR_DMA; 6728c2ecf20Sopenharmony_ci dma->valDCR &= ~BA0_DCR_MSK; 6738c2ecf20Sopenharmony_ci dma->valFCR |= BA0_FCR_FEN; 6748c2ecf20Sopenharmony_ci break; 6758c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 6768c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 6778c2ecf20Sopenharmony_ci dma->valDMR &= ~(BA0_DMR_DMA|BA0_DMR_POLL); 6788c2ecf20Sopenharmony_ci dma->valDCR |= BA0_DCR_MSK; 6798c2ecf20Sopenharmony_ci dma->valFCR &= ~BA0_FCR_FEN; 6808c2ecf20Sopenharmony_ci /* Leave wave playback FIFO enabled for FM */ 6818c2ecf20Sopenharmony_ci if (dma->regFCR != BA0_FCR0) 6828c2ecf20Sopenharmony_ci dma->valFCR &= ~BA0_FCR_FEN; 6838c2ecf20Sopenharmony_ci break; 6848c2ecf20Sopenharmony_ci default: 6858c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 6868c2ecf20Sopenharmony_ci return -EINVAL; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, dma->regDMR, dma->valDMR); 6898c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR); 6908c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, dma->regDCR, dma->valDCR); 6918c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 6928c2ecf20Sopenharmony_ci return 0; 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_cistatic unsigned int snd_cs4281_rate(unsigned int rate, unsigned int *real_rate) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci unsigned int val; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci if (real_rate) 7008c2ecf20Sopenharmony_ci *real_rate = rate; 7018c2ecf20Sopenharmony_ci /* special "hardcoded" rates */ 7028c2ecf20Sopenharmony_ci switch (rate) { 7038c2ecf20Sopenharmony_ci case 8000: return 5; 7048c2ecf20Sopenharmony_ci case 11025: return 4; 7058c2ecf20Sopenharmony_ci case 16000: return 3; 7068c2ecf20Sopenharmony_ci case 22050: return 2; 7078c2ecf20Sopenharmony_ci case 44100: return 1; 7088c2ecf20Sopenharmony_ci case 48000: return 0; 7098c2ecf20Sopenharmony_ci default: 7108c2ecf20Sopenharmony_ci break; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci val = 1536000 / rate; 7138c2ecf20Sopenharmony_ci if (real_rate) 7148c2ecf20Sopenharmony_ci *real_rate = 1536000 / val; 7158c2ecf20Sopenharmony_ci return val; 7168c2ecf20Sopenharmony_ci} 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic void snd_cs4281_mode(struct cs4281 *chip, struct cs4281_dma *dma, 7198c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime, 7208c2ecf20Sopenharmony_ci int capture, int src) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci int rec_mono; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci dma->valDMR = BA0_DMR_TYPE_SINGLE | BA0_DMR_AUTO | 7258c2ecf20Sopenharmony_ci (capture ? BA0_DMR_TR_WRITE : BA0_DMR_TR_READ); 7268c2ecf20Sopenharmony_ci if (runtime->channels == 1) 7278c2ecf20Sopenharmony_ci dma->valDMR |= BA0_DMR_MONO; 7288c2ecf20Sopenharmony_ci if (snd_pcm_format_unsigned(runtime->format) > 0) 7298c2ecf20Sopenharmony_ci dma->valDMR |= BA0_DMR_USIGN; 7308c2ecf20Sopenharmony_ci if (snd_pcm_format_big_endian(runtime->format) > 0) 7318c2ecf20Sopenharmony_ci dma->valDMR |= BA0_DMR_BEND; 7328c2ecf20Sopenharmony_ci switch (snd_pcm_format_width(runtime->format)) { 7338c2ecf20Sopenharmony_ci case 8: dma->valDMR |= BA0_DMR_SIZE8; 7348c2ecf20Sopenharmony_ci if (runtime->channels == 1) 7358c2ecf20Sopenharmony_ci dma->valDMR |= BA0_DMR_SWAPC; 7368c2ecf20Sopenharmony_ci break; 7378c2ecf20Sopenharmony_ci case 32: dma->valDMR |= BA0_DMR_SIZE20; break; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci dma->frag = 0; /* for workaround */ 7408c2ecf20Sopenharmony_ci dma->valDCR = BA0_DCR_TCIE | BA0_DCR_MSK; 7418c2ecf20Sopenharmony_ci if (runtime->buffer_size != runtime->period_size) 7428c2ecf20Sopenharmony_ci dma->valDCR |= BA0_DCR_HTCIE; 7438c2ecf20Sopenharmony_ci /* Initialize DMA */ 7448c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, dma->regDBA, runtime->dma_addr); 7458c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, dma->regDBC, runtime->buffer_size - 1); 7468c2ecf20Sopenharmony_ci rec_mono = (chip->dma[1].valDMR & BA0_DMR_MONO) == BA0_DMR_MONO; 7478c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_SRCSA, (chip->src_left_play_slot << 0) | 7488c2ecf20Sopenharmony_ci (chip->src_right_play_slot << 8) | 7498c2ecf20Sopenharmony_ci (chip->src_left_rec_slot << 16) | 7508c2ecf20Sopenharmony_ci ((rec_mono ? 31 : chip->src_right_rec_slot) << 24)); 7518c2ecf20Sopenharmony_ci if (!src) 7528c2ecf20Sopenharmony_ci goto __skip_src; 7538c2ecf20Sopenharmony_ci if (!capture) { 7548c2ecf20Sopenharmony_ci if (dma->left_slot == chip->src_left_play_slot) { 7558c2ecf20Sopenharmony_ci unsigned int val = snd_cs4281_rate(runtime->rate, NULL); 7568c2ecf20Sopenharmony_ci snd_BUG_ON(dma->right_slot != chip->src_right_play_slot); 7578c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_DACSR, val); 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci } else { 7608c2ecf20Sopenharmony_ci if (dma->left_slot == chip->src_left_rec_slot) { 7618c2ecf20Sopenharmony_ci unsigned int val = snd_cs4281_rate(runtime->rate, NULL); 7628c2ecf20Sopenharmony_ci snd_BUG_ON(dma->right_slot != chip->src_right_rec_slot); 7638c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_ADCSR, val); 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci __skip_src: 7678c2ecf20Sopenharmony_ci /* Deactivate wave playback FIFO before changing slot assignments */ 7688c2ecf20Sopenharmony_ci if (dma->regFCR == BA0_FCR0) 7698c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, dma->regFCR, snd_cs4281_peekBA0(chip, dma->regFCR) & ~BA0_FCR_FEN); 7708c2ecf20Sopenharmony_ci /* Initialize FIFO */ 7718c2ecf20Sopenharmony_ci dma->valFCR = BA0_FCR_LS(dma->left_slot) | 7728c2ecf20Sopenharmony_ci BA0_FCR_RS(capture && (dma->valDMR & BA0_DMR_MONO) ? 31 : dma->right_slot) | 7738c2ecf20Sopenharmony_ci BA0_FCR_SZ(CS4281_FIFO_SIZE) | 7748c2ecf20Sopenharmony_ci BA0_FCR_OF(dma->fifo_offset); 7758c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR | (capture ? BA0_FCR_PSH : 0)); 7768c2ecf20Sopenharmony_ci /* Activate FIFO again for FM playback */ 7778c2ecf20Sopenharmony_ci if (dma->regFCR == BA0_FCR0) 7788c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR | BA0_FCR_FEN); 7798c2ecf20Sopenharmony_ci /* Clear FIFO Status and Interrupt Control Register */ 7808c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, dma->regFSIC, 0); 7818c2ecf20Sopenharmony_ci} 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_cistatic int snd_cs4281_playback_prepare(struct snd_pcm_substream *substream) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 7868c2ecf20Sopenharmony_ci struct cs4281_dma *dma = runtime->private_data; 7878c2ecf20Sopenharmony_ci struct cs4281 *chip = snd_pcm_substream_chip(substream); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 7908c2ecf20Sopenharmony_ci snd_cs4281_mode(chip, dma, runtime, 0, 1); 7918c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 7928c2ecf20Sopenharmony_ci return 0; 7938c2ecf20Sopenharmony_ci} 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_cistatic int snd_cs4281_capture_prepare(struct snd_pcm_substream *substream) 7968c2ecf20Sopenharmony_ci{ 7978c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 7988c2ecf20Sopenharmony_ci struct cs4281_dma *dma = runtime->private_data; 7998c2ecf20Sopenharmony_ci struct cs4281 *chip = snd_pcm_substream_chip(substream); 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 8028c2ecf20Sopenharmony_ci snd_cs4281_mode(chip, dma, runtime, 1, 1); 8038c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 8048c2ecf20Sopenharmony_ci return 0; 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_cs4281_pointer(struct snd_pcm_substream *substream) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 8108c2ecf20Sopenharmony_ci struct cs4281_dma *dma = runtime->private_data; 8118c2ecf20Sopenharmony_ci struct cs4281 *chip = snd_pcm_substream_chip(substream); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci /* 8148c2ecf20Sopenharmony_ci dev_dbg(chip->card->dev, 8158c2ecf20Sopenharmony_ci "DCC = 0x%x, buffer_size = 0x%x, jiffies = %li\n", 8168c2ecf20Sopenharmony_ci snd_cs4281_peekBA0(chip, dma->regDCC), runtime->buffer_size, 8178c2ecf20Sopenharmony_ci jiffies); 8188c2ecf20Sopenharmony_ci */ 8198c2ecf20Sopenharmony_ci return runtime->buffer_size - 8208c2ecf20Sopenharmony_ci snd_cs4281_peekBA0(chip, dma->regDCC) - 1; 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_cs4281_playback = 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP | 8268c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 8278c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 8288c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | 8298c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME, 8308c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 8318c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE | 8328c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | 8338c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | 8348c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE, 8358c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 8368c2ecf20Sopenharmony_ci .rate_min = 4000, 8378c2ecf20Sopenharmony_ci .rate_max = 48000, 8388c2ecf20Sopenharmony_ci .channels_min = 1, 8398c2ecf20Sopenharmony_ci .channels_max = 2, 8408c2ecf20Sopenharmony_ci .buffer_bytes_max = (512*1024), 8418c2ecf20Sopenharmony_ci .period_bytes_min = 64, 8428c2ecf20Sopenharmony_ci .period_bytes_max = (512*1024), 8438c2ecf20Sopenharmony_ci .periods_min = 1, 8448c2ecf20Sopenharmony_ci .periods_max = 2, 8458c2ecf20Sopenharmony_ci .fifo_size = CS4281_FIFO_SIZE, 8468c2ecf20Sopenharmony_ci}; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_cs4281_capture = 8498c2ecf20Sopenharmony_ci{ 8508c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP | 8518c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 8528c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 8538c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | 8548c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_RESUME, 8558c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 8568c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE | 8578c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE | 8588c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE | 8598c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE, 8608c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, 8618c2ecf20Sopenharmony_ci .rate_min = 4000, 8628c2ecf20Sopenharmony_ci .rate_max = 48000, 8638c2ecf20Sopenharmony_ci .channels_min = 1, 8648c2ecf20Sopenharmony_ci .channels_max = 2, 8658c2ecf20Sopenharmony_ci .buffer_bytes_max = (512*1024), 8668c2ecf20Sopenharmony_ci .period_bytes_min = 64, 8678c2ecf20Sopenharmony_ci .period_bytes_max = (512*1024), 8688c2ecf20Sopenharmony_ci .periods_min = 1, 8698c2ecf20Sopenharmony_ci .periods_max = 2, 8708c2ecf20Sopenharmony_ci .fifo_size = CS4281_FIFO_SIZE, 8718c2ecf20Sopenharmony_ci}; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_cistatic int snd_cs4281_playback_open(struct snd_pcm_substream *substream) 8748c2ecf20Sopenharmony_ci{ 8758c2ecf20Sopenharmony_ci struct cs4281 *chip = snd_pcm_substream_chip(substream); 8768c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 8778c2ecf20Sopenharmony_ci struct cs4281_dma *dma; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci dma = &chip->dma[0]; 8808c2ecf20Sopenharmony_ci dma->substream = substream; 8818c2ecf20Sopenharmony_ci dma->left_slot = 0; 8828c2ecf20Sopenharmony_ci dma->right_slot = 1; 8838c2ecf20Sopenharmony_ci runtime->private_data = dma; 8848c2ecf20Sopenharmony_ci runtime->hw = snd_cs4281_playback; 8858c2ecf20Sopenharmony_ci /* should be detected from the AC'97 layer, but it seems 8868c2ecf20Sopenharmony_ci that although CS4297A rev B reports 18-bit ADC resolution, 8878c2ecf20Sopenharmony_ci samples are 20-bit */ 8888c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_msbits(runtime, 0, 32, 20); 8898c2ecf20Sopenharmony_ci return 0; 8908c2ecf20Sopenharmony_ci} 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_cistatic int snd_cs4281_capture_open(struct snd_pcm_substream *substream) 8938c2ecf20Sopenharmony_ci{ 8948c2ecf20Sopenharmony_ci struct cs4281 *chip = snd_pcm_substream_chip(substream); 8958c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 8968c2ecf20Sopenharmony_ci struct cs4281_dma *dma; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci dma = &chip->dma[1]; 8998c2ecf20Sopenharmony_ci dma->substream = substream; 9008c2ecf20Sopenharmony_ci dma->left_slot = 10; 9018c2ecf20Sopenharmony_ci dma->right_slot = 11; 9028c2ecf20Sopenharmony_ci runtime->private_data = dma; 9038c2ecf20Sopenharmony_ci runtime->hw = snd_cs4281_capture; 9048c2ecf20Sopenharmony_ci /* should be detected from the AC'97 layer, but it seems 9058c2ecf20Sopenharmony_ci that although CS4297A rev B reports 18-bit ADC resolution, 9068c2ecf20Sopenharmony_ci samples are 20-bit */ 9078c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_msbits(runtime, 0, 32, 20); 9088c2ecf20Sopenharmony_ci return 0; 9098c2ecf20Sopenharmony_ci} 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_cistatic int snd_cs4281_playback_close(struct snd_pcm_substream *substream) 9128c2ecf20Sopenharmony_ci{ 9138c2ecf20Sopenharmony_ci struct cs4281_dma *dma = substream->runtime->private_data; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci dma->substream = NULL; 9168c2ecf20Sopenharmony_ci return 0; 9178c2ecf20Sopenharmony_ci} 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_cistatic int snd_cs4281_capture_close(struct snd_pcm_substream *substream) 9208c2ecf20Sopenharmony_ci{ 9218c2ecf20Sopenharmony_ci struct cs4281_dma *dma = substream->runtime->private_data; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci dma->substream = NULL; 9248c2ecf20Sopenharmony_ci return 0; 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_cs4281_playback_ops = { 9288c2ecf20Sopenharmony_ci .open = snd_cs4281_playback_open, 9298c2ecf20Sopenharmony_ci .close = snd_cs4281_playback_close, 9308c2ecf20Sopenharmony_ci .prepare = snd_cs4281_playback_prepare, 9318c2ecf20Sopenharmony_ci .trigger = snd_cs4281_trigger, 9328c2ecf20Sopenharmony_ci .pointer = snd_cs4281_pointer, 9338c2ecf20Sopenharmony_ci}; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_cs4281_capture_ops = { 9368c2ecf20Sopenharmony_ci .open = snd_cs4281_capture_open, 9378c2ecf20Sopenharmony_ci .close = snd_cs4281_capture_close, 9388c2ecf20Sopenharmony_ci .prepare = snd_cs4281_capture_prepare, 9398c2ecf20Sopenharmony_ci .trigger = snd_cs4281_trigger, 9408c2ecf20Sopenharmony_ci .pointer = snd_cs4281_pointer, 9418c2ecf20Sopenharmony_ci}; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_cistatic int snd_cs4281_pcm(struct cs4281 *chip, int device) 9448c2ecf20Sopenharmony_ci{ 9458c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 9468c2ecf20Sopenharmony_ci int err; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci err = snd_pcm_new(chip->card, "CS4281", device, 1, 1, &pcm); 9498c2ecf20Sopenharmony_ci if (err < 0) 9508c2ecf20Sopenharmony_ci return err; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs4281_playback_ops); 9538c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs4281_capture_ops); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci pcm->private_data = chip; 9568c2ecf20Sopenharmony_ci pcm->info_flags = 0; 9578c2ecf20Sopenharmony_ci strcpy(pcm->name, "CS4281"); 9588c2ecf20Sopenharmony_ci chip->pcm = pcm; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pci->dev, 9618c2ecf20Sopenharmony_ci 64*1024, 512*1024); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci return 0; 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci/* 9678c2ecf20Sopenharmony_ci * Mixer section 9688c2ecf20Sopenharmony_ci */ 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci#define CS_VOL_MASK 0x1f 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_cistatic int snd_cs4281_info_volume(struct snd_kcontrol *kcontrol, 9738c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 9748c2ecf20Sopenharmony_ci{ 9758c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 9768c2ecf20Sopenharmony_ci uinfo->count = 2; 9778c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 9788c2ecf20Sopenharmony_ci uinfo->value.integer.max = CS_VOL_MASK; 9798c2ecf20Sopenharmony_ci return 0; 9808c2ecf20Sopenharmony_ci} 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_cistatic int snd_cs4281_get_volume(struct snd_kcontrol *kcontrol, 9838c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 9848c2ecf20Sopenharmony_ci{ 9858c2ecf20Sopenharmony_ci struct cs4281 *chip = snd_kcontrol_chip(kcontrol); 9868c2ecf20Sopenharmony_ci int regL = (kcontrol->private_value >> 16) & 0xffff; 9878c2ecf20Sopenharmony_ci int regR = kcontrol->private_value & 0xffff; 9888c2ecf20Sopenharmony_ci int volL, volR; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci volL = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regL) & CS_VOL_MASK); 9918c2ecf20Sopenharmony_ci volR = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regR) & CS_VOL_MASK); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = volL; 9948c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = volR; 9958c2ecf20Sopenharmony_ci return 0; 9968c2ecf20Sopenharmony_ci} 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_cistatic int snd_cs4281_put_volume(struct snd_kcontrol *kcontrol, 9998c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 10008c2ecf20Sopenharmony_ci{ 10018c2ecf20Sopenharmony_ci struct cs4281 *chip = snd_kcontrol_chip(kcontrol); 10028c2ecf20Sopenharmony_ci int change = 0; 10038c2ecf20Sopenharmony_ci int regL = (kcontrol->private_value >> 16) & 0xffff; 10048c2ecf20Sopenharmony_ci int regR = kcontrol->private_value & 0xffff; 10058c2ecf20Sopenharmony_ci int volL, volR; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci volL = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regL) & CS_VOL_MASK); 10088c2ecf20Sopenharmony_ci volR = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regR) & CS_VOL_MASK); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci if (ucontrol->value.integer.value[0] != volL) { 10118c2ecf20Sopenharmony_ci volL = CS_VOL_MASK - (ucontrol->value.integer.value[0] & CS_VOL_MASK); 10128c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, regL, volL); 10138c2ecf20Sopenharmony_ci change = 1; 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci if (ucontrol->value.integer.value[1] != volR) { 10168c2ecf20Sopenharmony_ci volR = CS_VOL_MASK - (ucontrol->value.integer.value[1] & CS_VOL_MASK); 10178c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, regR, volR); 10188c2ecf20Sopenharmony_ci change = 1; 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci return change; 10218c2ecf20Sopenharmony_ci} 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_dsp, -4650, 150, 0); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_cs4281_fm_vol = 10268c2ecf20Sopenharmony_ci{ 10278c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 10288c2ecf20Sopenharmony_ci .name = "Synth Playback Volume", 10298c2ecf20Sopenharmony_ci .info = snd_cs4281_info_volume, 10308c2ecf20Sopenharmony_ci .get = snd_cs4281_get_volume, 10318c2ecf20Sopenharmony_ci .put = snd_cs4281_put_volume, 10328c2ecf20Sopenharmony_ci .private_value = ((BA0_FMLVC << 16) | BA0_FMRVC), 10338c2ecf20Sopenharmony_ci .tlv = { .p = db_scale_dsp }, 10348c2ecf20Sopenharmony_ci}; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_cs4281_pcm_vol = 10378c2ecf20Sopenharmony_ci{ 10388c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 10398c2ecf20Sopenharmony_ci .name = "PCM Stream Playback Volume", 10408c2ecf20Sopenharmony_ci .info = snd_cs4281_info_volume, 10418c2ecf20Sopenharmony_ci .get = snd_cs4281_get_volume, 10428c2ecf20Sopenharmony_ci .put = snd_cs4281_put_volume, 10438c2ecf20Sopenharmony_ci .private_value = ((BA0_PPLVC << 16) | BA0_PPRVC), 10448c2ecf20Sopenharmony_ci .tlv = { .p = db_scale_dsp }, 10458c2ecf20Sopenharmony_ci}; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_cistatic void snd_cs4281_mixer_free_ac97_bus(struct snd_ac97_bus *bus) 10488c2ecf20Sopenharmony_ci{ 10498c2ecf20Sopenharmony_ci struct cs4281 *chip = bus->private_data; 10508c2ecf20Sopenharmony_ci chip->ac97_bus = NULL; 10518c2ecf20Sopenharmony_ci} 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_cistatic void snd_cs4281_mixer_free_ac97(struct snd_ac97 *ac97) 10548c2ecf20Sopenharmony_ci{ 10558c2ecf20Sopenharmony_ci struct cs4281 *chip = ac97->private_data; 10568c2ecf20Sopenharmony_ci if (ac97->num) 10578c2ecf20Sopenharmony_ci chip->ac97_secondary = NULL; 10588c2ecf20Sopenharmony_ci else 10598c2ecf20Sopenharmony_ci chip->ac97 = NULL; 10608c2ecf20Sopenharmony_ci} 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_cistatic int snd_cs4281_mixer(struct cs4281 *chip) 10638c2ecf20Sopenharmony_ci{ 10648c2ecf20Sopenharmony_ci struct snd_card *card = chip->card; 10658c2ecf20Sopenharmony_ci struct snd_ac97_template ac97; 10668c2ecf20Sopenharmony_ci int err; 10678c2ecf20Sopenharmony_ci static const struct snd_ac97_bus_ops ops = { 10688c2ecf20Sopenharmony_ci .write = snd_cs4281_ac97_write, 10698c2ecf20Sopenharmony_ci .read = snd_cs4281_ac97_read, 10708c2ecf20Sopenharmony_ci }; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci if ((err = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus)) < 0) 10738c2ecf20Sopenharmony_ci return err; 10748c2ecf20Sopenharmony_ci chip->ac97_bus->private_free = snd_cs4281_mixer_free_ac97_bus; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci memset(&ac97, 0, sizeof(ac97)); 10778c2ecf20Sopenharmony_ci ac97.private_data = chip; 10788c2ecf20Sopenharmony_ci ac97.private_free = snd_cs4281_mixer_free_ac97; 10798c2ecf20Sopenharmony_ci if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0) 10808c2ecf20Sopenharmony_ci return err; 10818c2ecf20Sopenharmony_ci if (chip->dual_codec) { 10828c2ecf20Sopenharmony_ci ac97.num = 1; 10838c2ecf20Sopenharmony_ci if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97_secondary)) < 0) 10848c2ecf20Sopenharmony_ci return err; 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4281_fm_vol, chip))) < 0) 10878c2ecf20Sopenharmony_ci return err; 10888c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4281_pcm_vol, chip))) < 0) 10898c2ecf20Sopenharmony_ci return err; 10908c2ecf20Sopenharmony_ci return 0; 10918c2ecf20Sopenharmony_ci} 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci/* 10958c2ecf20Sopenharmony_ci * proc interface 10968c2ecf20Sopenharmony_ci */ 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_cistatic void snd_cs4281_proc_read(struct snd_info_entry *entry, 10998c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci struct cs4281 *chip = entry->private_data; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Cirrus Logic CS4281\n\n"); 11048c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Spurious half IRQs : %u\n", chip->spurious_dhtc_irq); 11058c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Spurious end IRQs : %u\n", chip->spurious_dtc_irq); 11068c2ecf20Sopenharmony_ci} 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_cistatic ssize_t snd_cs4281_BA0_read(struct snd_info_entry *entry, 11098c2ecf20Sopenharmony_ci void *file_private_data, 11108c2ecf20Sopenharmony_ci struct file *file, char __user *buf, 11118c2ecf20Sopenharmony_ci size_t count, loff_t pos) 11128c2ecf20Sopenharmony_ci{ 11138c2ecf20Sopenharmony_ci struct cs4281 *chip = entry->private_data; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci if (copy_to_user_fromio(buf, chip->ba0 + pos, count)) 11168c2ecf20Sopenharmony_ci return -EFAULT; 11178c2ecf20Sopenharmony_ci return count; 11188c2ecf20Sopenharmony_ci} 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_cistatic ssize_t snd_cs4281_BA1_read(struct snd_info_entry *entry, 11218c2ecf20Sopenharmony_ci void *file_private_data, 11228c2ecf20Sopenharmony_ci struct file *file, char __user *buf, 11238c2ecf20Sopenharmony_ci size_t count, loff_t pos) 11248c2ecf20Sopenharmony_ci{ 11258c2ecf20Sopenharmony_ci struct cs4281 *chip = entry->private_data; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci if (copy_to_user_fromio(buf, chip->ba1 + pos, count)) 11288c2ecf20Sopenharmony_ci return -EFAULT; 11298c2ecf20Sopenharmony_ci return count; 11308c2ecf20Sopenharmony_ci} 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_cistatic const struct snd_info_entry_ops snd_cs4281_proc_ops_BA0 = { 11338c2ecf20Sopenharmony_ci .read = snd_cs4281_BA0_read, 11348c2ecf20Sopenharmony_ci}; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_cistatic const struct snd_info_entry_ops snd_cs4281_proc_ops_BA1 = { 11378c2ecf20Sopenharmony_ci .read = snd_cs4281_BA1_read, 11388c2ecf20Sopenharmony_ci}; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_cistatic void snd_cs4281_proc_init(struct cs4281 *chip) 11418c2ecf20Sopenharmony_ci{ 11428c2ecf20Sopenharmony_ci struct snd_info_entry *entry; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci snd_card_ro_proc_new(chip->card, "cs4281", chip, snd_cs4281_proc_read); 11458c2ecf20Sopenharmony_ci if (! snd_card_proc_new(chip->card, "cs4281_BA0", &entry)) { 11468c2ecf20Sopenharmony_ci entry->content = SNDRV_INFO_CONTENT_DATA; 11478c2ecf20Sopenharmony_ci entry->private_data = chip; 11488c2ecf20Sopenharmony_ci entry->c.ops = &snd_cs4281_proc_ops_BA0; 11498c2ecf20Sopenharmony_ci entry->size = CS4281_BA0_SIZE; 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci if (! snd_card_proc_new(chip->card, "cs4281_BA1", &entry)) { 11528c2ecf20Sopenharmony_ci entry->content = SNDRV_INFO_CONTENT_DATA; 11538c2ecf20Sopenharmony_ci entry->private_data = chip; 11548c2ecf20Sopenharmony_ci entry->c.ops = &snd_cs4281_proc_ops_BA1; 11558c2ecf20Sopenharmony_ci entry->size = CS4281_BA1_SIZE; 11568c2ecf20Sopenharmony_ci } 11578c2ecf20Sopenharmony_ci} 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci/* 11608c2ecf20Sopenharmony_ci * joystick support 11618c2ecf20Sopenharmony_ci */ 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci#if IS_REACHABLE(CONFIG_GAMEPORT) 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_cistatic void snd_cs4281_gameport_trigger(struct gameport *gameport) 11668c2ecf20Sopenharmony_ci{ 11678c2ecf20Sopenharmony_ci struct cs4281 *chip = gameport_get_port_data(gameport); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci if (snd_BUG_ON(!chip)) 11708c2ecf20Sopenharmony_ci return; 11718c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_JSPT, 0xff); 11728c2ecf20Sopenharmony_ci} 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_cistatic unsigned char snd_cs4281_gameport_read(struct gameport *gameport) 11758c2ecf20Sopenharmony_ci{ 11768c2ecf20Sopenharmony_ci struct cs4281 *chip = gameport_get_port_data(gameport); 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci if (snd_BUG_ON(!chip)) 11798c2ecf20Sopenharmony_ci return 0; 11808c2ecf20Sopenharmony_ci return snd_cs4281_peekBA0(chip, BA0_JSPT); 11818c2ecf20Sopenharmony_ci} 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci#ifdef COOKED_MODE 11848c2ecf20Sopenharmony_cistatic int snd_cs4281_gameport_cooked_read(struct gameport *gameport, 11858c2ecf20Sopenharmony_ci int *axes, int *buttons) 11868c2ecf20Sopenharmony_ci{ 11878c2ecf20Sopenharmony_ci struct cs4281 *chip = gameport_get_port_data(gameport); 11888c2ecf20Sopenharmony_ci unsigned js1, js2, jst; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci if (snd_BUG_ON(!chip)) 11918c2ecf20Sopenharmony_ci return 0; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci js1 = snd_cs4281_peekBA0(chip, BA0_JSC1); 11948c2ecf20Sopenharmony_ci js2 = snd_cs4281_peekBA0(chip, BA0_JSC2); 11958c2ecf20Sopenharmony_ci jst = snd_cs4281_peekBA0(chip, BA0_JSPT); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci *buttons = (~jst >> 4) & 0x0F; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci axes[0] = ((js1 & JSC1_Y1V_MASK) >> JSC1_Y1V_SHIFT) & 0xFFFF; 12008c2ecf20Sopenharmony_ci axes[1] = ((js1 & JSC1_X1V_MASK) >> JSC1_X1V_SHIFT) & 0xFFFF; 12018c2ecf20Sopenharmony_ci axes[2] = ((js2 & JSC2_Y2V_MASK) >> JSC2_Y2V_SHIFT) & 0xFFFF; 12028c2ecf20Sopenharmony_ci axes[3] = ((js2 & JSC2_X2V_MASK) >> JSC2_X2V_SHIFT) & 0xFFFF; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci for (jst = 0; jst < 4; ++jst) 12058c2ecf20Sopenharmony_ci if (axes[jst] == 0xFFFF) axes[jst] = -1; 12068c2ecf20Sopenharmony_ci return 0; 12078c2ecf20Sopenharmony_ci} 12088c2ecf20Sopenharmony_ci#else 12098c2ecf20Sopenharmony_ci#define snd_cs4281_gameport_cooked_read NULL 12108c2ecf20Sopenharmony_ci#endif 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_cistatic int snd_cs4281_gameport_open(struct gameport *gameport, int mode) 12138c2ecf20Sopenharmony_ci{ 12148c2ecf20Sopenharmony_ci switch (mode) { 12158c2ecf20Sopenharmony_ci#ifdef COOKED_MODE 12168c2ecf20Sopenharmony_ci case GAMEPORT_MODE_COOKED: 12178c2ecf20Sopenharmony_ci return 0; 12188c2ecf20Sopenharmony_ci#endif 12198c2ecf20Sopenharmony_ci case GAMEPORT_MODE_RAW: 12208c2ecf20Sopenharmony_ci return 0; 12218c2ecf20Sopenharmony_ci default: 12228c2ecf20Sopenharmony_ci return -1; 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci return 0; 12258c2ecf20Sopenharmony_ci} 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_cistatic int snd_cs4281_create_gameport(struct cs4281 *chip) 12288c2ecf20Sopenharmony_ci{ 12298c2ecf20Sopenharmony_ci struct gameport *gp; 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci chip->gameport = gp = gameport_allocate_port(); 12328c2ecf20Sopenharmony_ci if (!gp) { 12338c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 12348c2ecf20Sopenharmony_ci "cannot allocate memory for gameport\n"); 12358c2ecf20Sopenharmony_ci return -ENOMEM; 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci gameport_set_name(gp, "CS4281 Gameport"); 12398c2ecf20Sopenharmony_ci gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci)); 12408c2ecf20Sopenharmony_ci gameport_set_dev_parent(gp, &chip->pci->dev); 12418c2ecf20Sopenharmony_ci gp->open = snd_cs4281_gameport_open; 12428c2ecf20Sopenharmony_ci gp->read = snd_cs4281_gameport_read; 12438c2ecf20Sopenharmony_ci gp->trigger = snd_cs4281_gameport_trigger; 12448c2ecf20Sopenharmony_ci gp->cooked_read = snd_cs4281_gameport_cooked_read; 12458c2ecf20Sopenharmony_ci gameport_set_port_data(gp, chip); 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_JSIO, 0xFF); // ? 12488c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW); 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci gameport_register_port(gp); 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci return 0; 12538c2ecf20Sopenharmony_ci} 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_cistatic void snd_cs4281_free_gameport(struct cs4281 *chip) 12568c2ecf20Sopenharmony_ci{ 12578c2ecf20Sopenharmony_ci if (chip->gameport) { 12588c2ecf20Sopenharmony_ci gameport_unregister_port(chip->gameport); 12598c2ecf20Sopenharmony_ci chip->gameport = NULL; 12608c2ecf20Sopenharmony_ci } 12618c2ecf20Sopenharmony_ci} 12628c2ecf20Sopenharmony_ci#else 12638c2ecf20Sopenharmony_cistatic inline int snd_cs4281_create_gameport(struct cs4281 *chip) { return -ENOSYS; } 12648c2ecf20Sopenharmony_cistatic inline void snd_cs4281_free_gameport(struct cs4281 *chip) { } 12658c2ecf20Sopenharmony_ci#endif /* IS_REACHABLE(CONFIG_GAMEPORT) */ 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_cistatic int snd_cs4281_free(struct cs4281 *chip) 12688c2ecf20Sopenharmony_ci{ 12698c2ecf20Sopenharmony_ci snd_cs4281_free_gameport(chip); 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci /* Mask interrupts */ 12728c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_HIMR, 0x7fffffff); 12738c2ecf20Sopenharmony_ci /* Stop the DLL Clock logic. */ 12748c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0); 12758c2ecf20Sopenharmony_ci /* Sound System Power Management - Turn Everything OFF */ 12768c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_SSPM, 0); 12778c2ecf20Sopenharmony_ci /* PCI interface - D3 state */ 12788c2ecf20Sopenharmony_ci pci_set_power_state(chip->pci, PCI_D3hot); 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci if (chip->irq >= 0) 12818c2ecf20Sopenharmony_ci free_irq(chip->irq, chip); 12828c2ecf20Sopenharmony_ci iounmap(chip->ba0); 12838c2ecf20Sopenharmony_ci iounmap(chip->ba1); 12848c2ecf20Sopenharmony_ci pci_release_regions(chip->pci); 12858c2ecf20Sopenharmony_ci pci_disable_device(chip->pci); 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci kfree(chip); 12888c2ecf20Sopenharmony_ci return 0; 12898c2ecf20Sopenharmony_ci} 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_cistatic int snd_cs4281_dev_free(struct snd_device *device) 12928c2ecf20Sopenharmony_ci{ 12938c2ecf20Sopenharmony_ci struct cs4281 *chip = device->device_data; 12948c2ecf20Sopenharmony_ci return snd_cs4281_free(chip); 12958c2ecf20Sopenharmony_ci} 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_cistatic int snd_cs4281_chip_init(struct cs4281 *chip); /* defined below */ 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_cistatic int snd_cs4281_create(struct snd_card *card, 13008c2ecf20Sopenharmony_ci struct pci_dev *pci, 13018c2ecf20Sopenharmony_ci struct cs4281 **rchip, 13028c2ecf20Sopenharmony_ci int dual_codec) 13038c2ecf20Sopenharmony_ci{ 13048c2ecf20Sopenharmony_ci struct cs4281 *chip; 13058c2ecf20Sopenharmony_ci unsigned int tmp; 13068c2ecf20Sopenharmony_ci int err; 13078c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 13088c2ecf20Sopenharmony_ci .dev_free = snd_cs4281_dev_free, 13098c2ecf20Sopenharmony_ci }; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci *rchip = NULL; 13128c2ecf20Sopenharmony_ci if ((err = pci_enable_device(pci)) < 0) 13138c2ecf20Sopenharmony_ci return err; 13148c2ecf20Sopenharmony_ci chip = kzalloc(sizeof(*chip), GFP_KERNEL); 13158c2ecf20Sopenharmony_ci if (chip == NULL) { 13168c2ecf20Sopenharmony_ci pci_disable_device(pci); 13178c2ecf20Sopenharmony_ci return -ENOMEM; 13188c2ecf20Sopenharmony_ci } 13198c2ecf20Sopenharmony_ci spin_lock_init(&chip->reg_lock); 13208c2ecf20Sopenharmony_ci chip->card = card; 13218c2ecf20Sopenharmony_ci chip->pci = pci; 13228c2ecf20Sopenharmony_ci chip->irq = -1; 13238c2ecf20Sopenharmony_ci pci_set_master(pci); 13248c2ecf20Sopenharmony_ci if (dual_codec < 0 || dual_codec > 3) { 13258c2ecf20Sopenharmony_ci dev_err(card->dev, "invalid dual_codec option %d\n", dual_codec); 13268c2ecf20Sopenharmony_ci dual_codec = 0; 13278c2ecf20Sopenharmony_ci } 13288c2ecf20Sopenharmony_ci chip->dual_codec = dual_codec; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci if ((err = pci_request_regions(pci, "CS4281")) < 0) { 13318c2ecf20Sopenharmony_ci kfree(chip); 13328c2ecf20Sopenharmony_ci pci_disable_device(pci); 13338c2ecf20Sopenharmony_ci return err; 13348c2ecf20Sopenharmony_ci } 13358c2ecf20Sopenharmony_ci chip->ba0_addr = pci_resource_start(pci, 0); 13368c2ecf20Sopenharmony_ci chip->ba1_addr = pci_resource_start(pci, 1); 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci chip->ba0 = pci_ioremap_bar(pci, 0); 13398c2ecf20Sopenharmony_ci chip->ba1 = pci_ioremap_bar(pci, 1); 13408c2ecf20Sopenharmony_ci if (!chip->ba0 || !chip->ba1) { 13418c2ecf20Sopenharmony_ci snd_cs4281_free(chip); 13428c2ecf20Sopenharmony_ci return -ENOMEM; 13438c2ecf20Sopenharmony_ci } 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci if (request_irq(pci->irq, snd_cs4281_interrupt, IRQF_SHARED, 13468c2ecf20Sopenharmony_ci KBUILD_MODNAME, chip)) { 13478c2ecf20Sopenharmony_ci dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); 13488c2ecf20Sopenharmony_ci snd_cs4281_free(chip); 13498c2ecf20Sopenharmony_ci return -ENOMEM; 13508c2ecf20Sopenharmony_ci } 13518c2ecf20Sopenharmony_ci chip->irq = pci->irq; 13528c2ecf20Sopenharmony_ci card->sync_irq = chip->irq; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci tmp = snd_cs4281_chip_init(chip); 13558c2ecf20Sopenharmony_ci if (tmp) { 13568c2ecf20Sopenharmony_ci snd_cs4281_free(chip); 13578c2ecf20Sopenharmony_ci return tmp; 13588c2ecf20Sopenharmony_ci } 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { 13618c2ecf20Sopenharmony_ci snd_cs4281_free(chip); 13628c2ecf20Sopenharmony_ci return err; 13638c2ecf20Sopenharmony_ci } 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci snd_cs4281_proc_init(chip); 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci *rchip = chip; 13688c2ecf20Sopenharmony_ci return 0; 13698c2ecf20Sopenharmony_ci} 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_cistatic int snd_cs4281_chip_init(struct cs4281 *chip) 13728c2ecf20Sopenharmony_ci{ 13738c2ecf20Sopenharmony_ci unsigned int tmp; 13748c2ecf20Sopenharmony_ci unsigned long end_time; 13758c2ecf20Sopenharmony_ci int retry_count = 2; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci /* Having EPPMC.FPDN=1 prevent proper chip initialisation */ 13788c2ecf20Sopenharmony_ci tmp = snd_cs4281_peekBA0(chip, BA0_EPPMC); 13798c2ecf20Sopenharmony_ci if (tmp & BA0_EPPMC_FPDN) 13808c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_EPPMC, tmp & ~BA0_EPPMC_FPDN); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci __retry: 13838c2ecf20Sopenharmony_ci tmp = snd_cs4281_peekBA0(chip, BA0_CFLR); 13848c2ecf20Sopenharmony_ci if (tmp != BA0_CFLR_DEFAULT) { 13858c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_CFLR, BA0_CFLR_DEFAULT); 13868c2ecf20Sopenharmony_ci tmp = snd_cs4281_peekBA0(chip, BA0_CFLR); 13878c2ecf20Sopenharmony_ci if (tmp != BA0_CFLR_DEFAULT) { 13888c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 13898c2ecf20Sopenharmony_ci "CFLR setup failed (0x%x)\n", tmp); 13908c2ecf20Sopenharmony_ci return -EIO; 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci } 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci /* Set the 'Configuration Write Protect' register 13958c2ecf20Sopenharmony_ci * to 4281h. Allows vendor-defined configuration 13968c2ecf20Sopenharmony_ci * space between 0e4h and 0ffh to be written. */ 13978c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_CWPR, 0x4281); 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC1)) != (BA0_SERC1_SO1EN | BA0_SERC1_AC97)) { 14008c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 14018c2ecf20Sopenharmony_ci "SERC1 AC'97 check failed (0x%x)\n", tmp); 14028c2ecf20Sopenharmony_ci return -EIO; 14038c2ecf20Sopenharmony_ci } 14048c2ecf20Sopenharmony_ci if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC2)) != (BA0_SERC2_SI1EN | BA0_SERC2_AC97)) { 14058c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 14068c2ecf20Sopenharmony_ci "SERC2 AC'97 check failed (0x%x)\n", tmp); 14078c2ecf20Sopenharmony_ci return -EIO; 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci /* Sound System Power Management */ 14118c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_SSPM, BA0_SSPM_MIXEN | BA0_SSPM_CSRCEN | 14128c2ecf20Sopenharmony_ci BA0_SSPM_PSRCEN | BA0_SSPM_JSEN | 14138c2ecf20Sopenharmony_ci BA0_SSPM_ACLEN | BA0_SSPM_FMEN); 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci /* Serial Port Power Management */ 14168c2ecf20Sopenharmony_ci /* Blast the clock control register to zero so that the 14178c2ecf20Sopenharmony_ci * PLL starts out in a known state, and blast the master serial 14188c2ecf20Sopenharmony_ci * port control register to zero so that the serial ports also 14198c2ecf20Sopenharmony_ci * start out in a known state. */ 14208c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0); 14218c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_SERMC, 0); 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci /* Make ESYN go to zero to turn off 14248c2ecf20Sopenharmony_ci * the Sync pulse on the AC97 link. */ 14258c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_ACCTL, 0); 14268c2ecf20Sopenharmony_ci udelay(50); 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci /* Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97 14298c2ecf20Sopenharmony_ci * spec) and then drive it high. This is done for non AC97 modes since 14308c2ecf20Sopenharmony_ci * there might be logic external to the CS4281 that uses the ARST# line 14318c2ecf20Sopenharmony_ci * for a reset. */ 14328c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_SPMC, 0); 14338c2ecf20Sopenharmony_ci udelay(50); 14348c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_SPMC, BA0_SPMC_RSTN); 14358c2ecf20Sopenharmony_ci msleep(50); 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci if (chip->dual_codec) 14388c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_SPMC, BA0_SPMC_RSTN | BA0_SPMC_ASDI2E); 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci /* 14418c2ecf20Sopenharmony_ci * Set the serial port timing configuration. 14428c2ecf20Sopenharmony_ci */ 14438c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_SERMC, 14448c2ecf20Sopenharmony_ci (chip->dual_codec ? BA0_SERMC_TCID(chip->dual_codec) : BA0_SERMC_TCID(1)) | 14458c2ecf20Sopenharmony_ci BA0_SERMC_PTC_AC97 | BA0_SERMC_MSPE); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci /* 14488c2ecf20Sopenharmony_ci * Start the DLL Clock logic. 14498c2ecf20Sopenharmony_ci */ 14508c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_CLKCR1, BA0_CLKCR1_DLLP); 14518c2ecf20Sopenharmony_ci msleep(50); 14528c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_CLKCR1, BA0_CLKCR1_SWCE | BA0_CLKCR1_DLLP); 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci /* 14558c2ecf20Sopenharmony_ci * Wait for the DLL ready signal from the clock logic. 14568c2ecf20Sopenharmony_ci */ 14578c2ecf20Sopenharmony_ci end_time = jiffies + HZ; 14588c2ecf20Sopenharmony_ci do { 14598c2ecf20Sopenharmony_ci /* 14608c2ecf20Sopenharmony_ci * Read the AC97 status register to see if we've seen a CODEC 14618c2ecf20Sopenharmony_ci * signal from the AC97 codec. 14628c2ecf20Sopenharmony_ci */ 14638c2ecf20Sopenharmony_ci if (snd_cs4281_peekBA0(chip, BA0_CLKCR1) & BA0_CLKCR1_DLLRDY) 14648c2ecf20Sopenharmony_ci goto __ok0; 14658c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(1); 14668c2ecf20Sopenharmony_ci } while (time_after_eq(end_time, jiffies)); 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "DLLRDY not seen\n"); 14698c2ecf20Sopenharmony_ci return -EIO; 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci __ok0: 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci /* 14748c2ecf20Sopenharmony_ci * The first thing we do here is to enable sync generation. As soon 14758c2ecf20Sopenharmony_ci * as we start receiving bit clock, we'll start producing the SYNC 14768c2ecf20Sopenharmony_ci * signal. 14778c2ecf20Sopenharmony_ci */ 14788c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_ESYN); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci /* 14818c2ecf20Sopenharmony_ci * Wait for the codec ready signal from the AC97 codec. 14828c2ecf20Sopenharmony_ci */ 14838c2ecf20Sopenharmony_ci end_time = jiffies + HZ; 14848c2ecf20Sopenharmony_ci do { 14858c2ecf20Sopenharmony_ci /* 14868c2ecf20Sopenharmony_ci * Read the AC97 status register to see if we've seen a CODEC 14878c2ecf20Sopenharmony_ci * signal from the AC97 codec. 14888c2ecf20Sopenharmony_ci */ 14898c2ecf20Sopenharmony_ci if (snd_cs4281_peekBA0(chip, BA0_ACSTS) & BA0_ACSTS_CRDY) 14908c2ecf20Sopenharmony_ci goto __ok1; 14918c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(1); 14928c2ecf20Sopenharmony_ci } while (time_after_eq(end_time, jiffies)); 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci dev_err(chip->card->dev, 14958c2ecf20Sopenharmony_ci "never read codec ready from AC'97 (0x%x)\n", 14968c2ecf20Sopenharmony_ci snd_cs4281_peekBA0(chip, BA0_ACSTS)); 14978c2ecf20Sopenharmony_ci return -EIO; 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci __ok1: 15008c2ecf20Sopenharmony_ci if (chip->dual_codec) { 15018c2ecf20Sopenharmony_ci end_time = jiffies + HZ; 15028c2ecf20Sopenharmony_ci do { 15038c2ecf20Sopenharmony_ci if (snd_cs4281_peekBA0(chip, BA0_ACSTS2) & BA0_ACSTS_CRDY) 15048c2ecf20Sopenharmony_ci goto __codec2_ok; 15058c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(1); 15068c2ecf20Sopenharmony_ci } while (time_after_eq(end_time, jiffies)); 15078c2ecf20Sopenharmony_ci dev_info(chip->card->dev, 15088c2ecf20Sopenharmony_ci "secondary codec doesn't respond. disable it...\n"); 15098c2ecf20Sopenharmony_ci chip->dual_codec = 0; 15108c2ecf20Sopenharmony_ci __codec2_ok: ; 15118c2ecf20Sopenharmony_ci } 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci /* 15148c2ecf20Sopenharmony_ci * Assert the valid frame signal so that we can start sending commands 15158c2ecf20Sopenharmony_ci * to the AC97 codec. 15168c2ecf20Sopenharmony_ci */ 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_VFRM | BA0_ACCTL_ESYN); 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci /* 15218c2ecf20Sopenharmony_ci * Wait until we've sampled input slots 3 and 4 as valid, meaning that 15228c2ecf20Sopenharmony_ci * the codec is pumping ADC data across the AC-link. 15238c2ecf20Sopenharmony_ci */ 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci end_time = jiffies + HZ; 15268c2ecf20Sopenharmony_ci do { 15278c2ecf20Sopenharmony_ci /* 15288c2ecf20Sopenharmony_ci * Read the input slot valid register and see if input slots 3 15298c2ecf20Sopenharmony_ci * 4 are valid yet. 15308c2ecf20Sopenharmony_ci */ 15318c2ecf20Sopenharmony_ci if ((snd_cs4281_peekBA0(chip, BA0_ACISV) & (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4))) == (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4))) 15328c2ecf20Sopenharmony_ci goto __ok2; 15338c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(1); 15348c2ecf20Sopenharmony_ci } while (time_after_eq(end_time, jiffies)); 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci if (--retry_count > 0) 15378c2ecf20Sopenharmony_ci goto __retry; 15388c2ecf20Sopenharmony_ci dev_err(chip->card->dev, "never read ISV3 and ISV4 from AC'97\n"); 15398c2ecf20Sopenharmony_ci return -EIO; 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci __ok2: 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci /* 15448c2ecf20Sopenharmony_ci * Now, assert valid frame and the slot 3 and 4 valid bits. This will 15458c2ecf20Sopenharmony_ci * commense the transfer of digital audio data to the AC97 codec. 15468c2ecf20Sopenharmony_ci */ 15478c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_ACOSV, BA0_ACOSV_SLV(3) | BA0_ACOSV_SLV(4)); 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci /* 15508c2ecf20Sopenharmony_ci * Initialize DMA structures 15518c2ecf20Sopenharmony_ci */ 15528c2ecf20Sopenharmony_ci for (tmp = 0; tmp < 4; tmp++) { 15538c2ecf20Sopenharmony_ci struct cs4281_dma *dma = &chip->dma[tmp]; 15548c2ecf20Sopenharmony_ci dma->regDBA = BA0_DBA0 + (tmp * 0x10); 15558c2ecf20Sopenharmony_ci dma->regDCA = BA0_DCA0 + (tmp * 0x10); 15568c2ecf20Sopenharmony_ci dma->regDBC = BA0_DBC0 + (tmp * 0x10); 15578c2ecf20Sopenharmony_ci dma->regDCC = BA0_DCC0 + (tmp * 0x10); 15588c2ecf20Sopenharmony_ci dma->regDMR = BA0_DMR0 + (tmp * 8); 15598c2ecf20Sopenharmony_ci dma->regDCR = BA0_DCR0 + (tmp * 8); 15608c2ecf20Sopenharmony_ci dma->regHDSR = BA0_HDSR0 + (tmp * 4); 15618c2ecf20Sopenharmony_ci dma->regFCR = BA0_FCR0 + (tmp * 4); 15628c2ecf20Sopenharmony_ci dma->regFSIC = BA0_FSIC0 + (tmp * 4); 15638c2ecf20Sopenharmony_ci dma->fifo_offset = tmp * CS4281_FIFO_SIZE; 15648c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, dma->regFCR, 15658c2ecf20Sopenharmony_ci BA0_FCR_LS(31) | 15668c2ecf20Sopenharmony_ci BA0_FCR_RS(31) | 15678c2ecf20Sopenharmony_ci BA0_FCR_SZ(CS4281_FIFO_SIZE) | 15688c2ecf20Sopenharmony_ci BA0_FCR_OF(dma->fifo_offset)); 15698c2ecf20Sopenharmony_ci } 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci chip->src_left_play_slot = 0; /* AC'97 left PCM playback (3) */ 15728c2ecf20Sopenharmony_ci chip->src_right_play_slot = 1; /* AC'97 right PCM playback (4) */ 15738c2ecf20Sopenharmony_ci chip->src_left_rec_slot = 10; /* AC'97 left PCM record (3) */ 15748c2ecf20Sopenharmony_ci chip->src_right_rec_slot = 11; /* AC'97 right PCM record (4) */ 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci /* Activate wave playback FIFO for FM playback */ 15778c2ecf20Sopenharmony_ci chip->dma[0].valFCR = BA0_FCR_FEN | BA0_FCR_LS(0) | 15788c2ecf20Sopenharmony_ci BA0_FCR_RS(1) | 15798c2ecf20Sopenharmony_ci BA0_FCR_SZ(CS4281_FIFO_SIZE) | 15808c2ecf20Sopenharmony_ci BA0_FCR_OF(chip->dma[0].fifo_offset); 15818c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, chip->dma[0].regFCR, chip->dma[0].valFCR); 15828c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_SRCSA, (chip->src_left_play_slot << 0) | 15838c2ecf20Sopenharmony_ci (chip->src_right_play_slot << 8) | 15848c2ecf20Sopenharmony_ci (chip->src_left_rec_slot << 16) | 15858c2ecf20Sopenharmony_ci (chip->src_right_rec_slot << 24)); 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci /* Initialize digital volume */ 15888c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_PPLVC, 0); 15898c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_PPRVC, 0); 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci /* Enable IRQs */ 15928c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI); 15938c2ecf20Sopenharmony_ci /* Unmask interrupts */ 15948c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_HIMR, 0x7fffffff & ~( 15958c2ecf20Sopenharmony_ci BA0_HISR_MIDI | 15968c2ecf20Sopenharmony_ci BA0_HISR_DMAI | 15978c2ecf20Sopenharmony_ci BA0_HISR_DMA(0) | 15988c2ecf20Sopenharmony_ci BA0_HISR_DMA(1) | 15998c2ecf20Sopenharmony_ci BA0_HISR_DMA(2) | 16008c2ecf20Sopenharmony_ci BA0_HISR_DMA(3))); 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci return 0; 16038c2ecf20Sopenharmony_ci} 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci/* 16068c2ecf20Sopenharmony_ci * MIDI section 16078c2ecf20Sopenharmony_ci */ 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_cistatic void snd_cs4281_midi_reset(struct cs4281 *chip) 16108c2ecf20Sopenharmony_ci{ 16118c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr | BA0_MIDCR_MRST); 16128c2ecf20Sopenharmony_ci udelay(100); 16138c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); 16148c2ecf20Sopenharmony_ci} 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_cistatic int snd_cs4281_midi_input_open(struct snd_rawmidi_substream *substream) 16178c2ecf20Sopenharmony_ci{ 16188c2ecf20Sopenharmony_ci struct cs4281 *chip = substream->rmidi->private_data; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 16218c2ecf20Sopenharmony_ci chip->midcr |= BA0_MIDCR_RXE; 16228c2ecf20Sopenharmony_ci chip->midi_input = substream; 16238c2ecf20Sopenharmony_ci if (!(chip->uartm & CS4281_MODE_OUTPUT)) { 16248c2ecf20Sopenharmony_ci snd_cs4281_midi_reset(chip); 16258c2ecf20Sopenharmony_ci } else { 16268c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); 16278c2ecf20Sopenharmony_ci } 16288c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 16298c2ecf20Sopenharmony_ci return 0; 16308c2ecf20Sopenharmony_ci} 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_cistatic int snd_cs4281_midi_input_close(struct snd_rawmidi_substream *substream) 16338c2ecf20Sopenharmony_ci{ 16348c2ecf20Sopenharmony_ci struct cs4281 *chip = substream->rmidi->private_data; 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 16378c2ecf20Sopenharmony_ci chip->midcr &= ~(BA0_MIDCR_RXE | BA0_MIDCR_RIE); 16388c2ecf20Sopenharmony_ci chip->midi_input = NULL; 16398c2ecf20Sopenharmony_ci if (!(chip->uartm & CS4281_MODE_OUTPUT)) { 16408c2ecf20Sopenharmony_ci snd_cs4281_midi_reset(chip); 16418c2ecf20Sopenharmony_ci } else { 16428c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); 16438c2ecf20Sopenharmony_ci } 16448c2ecf20Sopenharmony_ci chip->uartm &= ~CS4281_MODE_INPUT; 16458c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 16468c2ecf20Sopenharmony_ci return 0; 16478c2ecf20Sopenharmony_ci} 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_cistatic int snd_cs4281_midi_output_open(struct snd_rawmidi_substream *substream) 16508c2ecf20Sopenharmony_ci{ 16518c2ecf20Sopenharmony_ci struct cs4281 *chip = substream->rmidi->private_data; 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 16548c2ecf20Sopenharmony_ci chip->uartm |= CS4281_MODE_OUTPUT; 16558c2ecf20Sopenharmony_ci chip->midcr |= BA0_MIDCR_TXE; 16568c2ecf20Sopenharmony_ci chip->midi_output = substream; 16578c2ecf20Sopenharmony_ci if (!(chip->uartm & CS4281_MODE_INPUT)) { 16588c2ecf20Sopenharmony_ci snd_cs4281_midi_reset(chip); 16598c2ecf20Sopenharmony_ci } else { 16608c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); 16618c2ecf20Sopenharmony_ci } 16628c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 16638c2ecf20Sopenharmony_ci return 0; 16648c2ecf20Sopenharmony_ci} 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_cistatic int snd_cs4281_midi_output_close(struct snd_rawmidi_substream *substream) 16678c2ecf20Sopenharmony_ci{ 16688c2ecf20Sopenharmony_ci struct cs4281 *chip = substream->rmidi->private_data; 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 16718c2ecf20Sopenharmony_ci chip->midcr &= ~(BA0_MIDCR_TXE | BA0_MIDCR_TIE); 16728c2ecf20Sopenharmony_ci chip->midi_output = NULL; 16738c2ecf20Sopenharmony_ci if (!(chip->uartm & CS4281_MODE_INPUT)) { 16748c2ecf20Sopenharmony_ci snd_cs4281_midi_reset(chip); 16758c2ecf20Sopenharmony_ci } else { 16768c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); 16778c2ecf20Sopenharmony_ci } 16788c2ecf20Sopenharmony_ci chip->uartm &= ~CS4281_MODE_OUTPUT; 16798c2ecf20Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 16808c2ecf20Sopenharmony_ci return 0; 16818c2ecf20Sopenharmony_ci} 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_cistatic void snd_cs4281_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) 16848c2ecf20Sopenharmony_ci{ 16858c2ecf20Sopenharmony_ci unsigned long flags; 16868c2ecf20Sopenharmony_ci struct cs4281 *chip = substream->rmidi->private_data; 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 16898c2ecf20Sopenharmony_ci if (up) { 16908c2ecf20Sopenharmony_ci if ((chip->midcr & BA0_MIDCR_RIE) == 0) { 16918c2ecf20Sopenharmony_ci chip->midcr |= BA0_MIDCR_RIE; 16928c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci } else { 16958c2ecf20Sopenharmony_ci if (chip->midcr & BA0_MIDCR_RIE) { 16968c2ecf20Sopenharmony_ci chip->midcr &= ~BA0_MIDCR_RIE; 16978c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); 16988c2ecf20Sopenharmony_ci } 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 17018c2ecf20Sopenharmony_ci} 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_cistatic void snd_cs4281_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) 17048c2ecf20Sopenharmony_ci{ 17058c2ecf20Sopenharmony_ci unsigned long flags; 17068c2ecf20Sopenharmony_ci struct cs4281 *chip = substream->rmidi->private_data; 17078c2ecf20Sopenharmony_ci unsigned char byte; 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->reg_lock, flags); 17108c2ecf20Sopenharmony_ci if (up) { 17118c2ecf20Sopenharmony_ci if ((chip->midcr & BA0_MIDCR_TIE) == 0) { 17128c2ecf20Sopenharmony_ci chip->midcr |= BA0_MIDCR_TIE; 17138c2ecf20Sopenharmony_ci /* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */ 17148c2ecf20Sopenharmony_ci while ((chip->midcr & BA0_MIDCR_TIE) && 17158c2ecf20Sopenharmony_ci (snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_TBF) == 0) { 17168c2ecf20Sopenharmony_ci if (snd_rawmidi_transmit(substream, &byte, 1) != 1) { 17178c2ecf20Sopenharmony_ci chip->midcr &= ~BA0_MIDCR_TIE; 17188c2ecf20Sopenharmony_ci } else { 17198c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_MIDWP, byte); 17208c2ecf20Sopenharmony_ci } 17218c2ecf20Sopenharmony_ci } 17228c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); 17238c2ecf20Sopenharmony_ci } 17248c2ecf20Sopenharmony_ci } else { 17258c2ecf20Sopenharmony_ci if (chip->midcr & BA0_MIDCR_TIE) { 17268c2ecf20Sopenharmony_ci chip->midcr &= ~BA0_MIDCR_TIE; 17278c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); 17288c2ecf20Sopenharmony_ci } 17298c2ecf20Sopenharmony_ci } 17308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->reg_lock, flags); 17318c2ecf20Sopenharmony_ci} 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_cistatic const struct snd_rawmidi_ops snd_cs4281_midi_output = 17348c2ecf20Sopenharmony_ci{ 17358c2ecf20Sopenharmony_ci .open = snd_cs4281_midi_output_open, 17368c2ecf20Sopenharmony_ci .close = snd_cs4281_midi_output_close, 17378c2ecf20Sopenharmony_ci .trigger = snd_cs4281_midi_output_trigger, 17388c2ecf20Sopenharmony_ci}; 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_cistatic const struct snd_rawmidi_ops snd_cs4281_midi_input = 17418c2ecf20Sopenharmony_ci{ 17428c2ecf20Sopenharmony_ci .open = snd_cs4281_midi_input_open, 17438c2ecf20Sopenharmony_ci .close = snd_cs4281_midi_input_close, 17448c2ecf20Sopenharmony_ci .trigger = snd_cs4281_midi_input_trigger, 17458c2ecf20Sopenharmony_ci}; 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_cistatic int snd_cs4281_midi(struct cs4281 *chip, int device) 17488c2ecf20Sopenharmony_ci{ 17498c2ecf20Sopenharmony_ci struct snd_rawmidi *rmidi; 17508c2ecf20Sopenharmony_ci int err; 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci if ((err = snd_rawmidi_new(chip->card, "CS4281", device, 1, 1, &rmidi)) < 0) 17538c2ecf20Sopenharmony_ci return err; 17548c2ecf20Sopenharmony_ci strcpy(rmidi->name, "CS4281"); 17558c2ecf20Sopenharmony_ci snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs4281_midi_output); 17568c2ecf20Sopenharmony_ci snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs4281_midi_input); 17578c2ecf20Sopenharmony_ci rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; 17588c2ecf20Sopenharmony_ci rmidi->private_data = chip; 17598c2ecf20Sopenharmony_ci chip->rmidi = rmidi; 17608c2ecf20Sopenharmony_ci return 0; 17618c2ecf20Sopenharmony_ci} 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci/* 17648c2ecf20Sopenharmony_ci * Interrupt handler 17658c2ecf20Sopenharmony_ci */ 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_cistatic irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id) 17688c2ecf20Sopenharmony_ci{ 17698c2ecf20Sopenharmony_ci struct cs4281 *chip = dev_id; 17708c2ecf20Sopenharmony_ci unsigned int status, dma, val; 17718c2ecf20Sopenharmony_ci struct cs4281_dma *cdma; 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci if (chip == NULL) 17748c2ecf20Sopenharmony_ci return IRQ_NONE; 17758c2ecf20Sopenharmony_ci status = snd_cs4281_peekBA0(chip, BA0_HISR); 17768c2ecf20Sopenharmony_ci if ((status & 0x7fffffff) == 0) { 17778c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI); 17788c2ecf20Sopenharmony_ci return IRQ_NONE; 17798c2ecf20Sopenharmony_ci } 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci if (status & (BA0_HISR_DMA(0)|BA0_HISR_DMA(1)|BA0_HISR_DMA(2)|BA0_HISR_DMA(3))) { 17828c2ecf20Sopenharmony_ci for (dma = 0; dma < 4; dma++) 17838c2ecf20Sopenharmony_ci if (status & BA0_HISR_DMA(dma)) { 17848c2ecf20Sopenharmony_ci cdma = &chip->dma[dma]; 17858c2ecf20Sopenharmony_ci spin_lock(&chip->reg_lock); 17868c2ecf20Sopenharmony_ci /* ack DMA IRQ */ 17878c2ecf20Sopenharmony_ci val = snd_cs4281_peekBA0(chip, cdma->regHDSR); 17888c2ecf20Sopenharmony_ci /* workaround, sometimes CS4281 acknowledges */ 17898c2ecf20Sopenharmony_ci /* end or middle transfer position twice */ 17908c2ecf20Sopenharmony_ci cdma->frag++; 17918c2ecf20Sopenharmony_ci if ((val & BA0_HDSR_DHTC) && !(cdma->frag & 1)) { 17928c2ecf20Sopenharmony_ci cdma->frag--; 17938c2ecf20Sopenharmony_ci chip->spurious_dhtc_irq++; 17948c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 17958c2ecf20Sopenharmony_ci continue; 17968c2ecf20Sopenharmony_ci } 17978c2ecf20Sopenharmony_ci if ((val & BA0_HDSR_DTC) && (cdma->frag & 1)) { 17988c2ecf20Sopenharmony_ci cdma->frag--; 17998c2ecf20Sopenharmony_ci chip->spurious_dtc_irq++; 18008c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 18018c2ecf20Sopenharmony_ci continue; 18028c2ecf20Sopenharmony_ci } 18038c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 18048c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(cdma->substream); 18058c2ecf20Sopenharmony_ci } 18068c2ecf20Sopenharmony_ci } 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci if ((status & BA0_HISR_MIDI) && chip->rmidi) { 18098c2ecf20Sopenharmony_ci unsigned char c; 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci spin_lock(&chip->reg_lock); 18128c2ecf20Sopenharmony_ci while ((snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_RBE) == 0) { 18138c2ecf20Sopenharmony_ci c = snd_cs4281_peekBA0(chip, BA0_MIDRP); 18148c2ecf20Sopenharmony_ci if ((chip->midcr & BA0_MIDCR_RIE) == 0) 18158c2ecf20Sopenharmony_ci continue; 18168c2ecf20Sopenharmony_ci snd_rawmidi_receive(chip->midi_input, &c, 1); 18178c2ecf20Sopenharmony_ci } 18188c2ecf20Sopenharmony_ci while ((snd_cs4281_peekBA0(chip, BA0_MIDSR) & BA0_MIDSR_TBF) == 0) { 18198c2ecf20Sopenharmony_ci if ((chip->midcr & BA0_MIDCR_TIE) == 0) 18208c2ecf20Sopenharmony_ci break; 18218c2ecf20Sopenharmony_ci if (snd_rawmidi_transmit(chip->midi_output, &c, 1) != 1) { 18228c2ecf20Sopenharmony_ci chip->midcr &= ~BA0_MIDCR_TIE; 18238c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_MIDCR, chip->midcr); 18248c2ecf20Sopenharmony_ci break; 18258c2ecf20Sopenharmony_ci } 18268c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_MIDWP, c); 18278c2ecf20Sopenharmony_ci } 18288c2ecf20Sopenharmony_ci spin_unlock(&chip->reg_lock); 18298c2ecf20Sopenharmony_ci } 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci /* EOI to the PCI part... reenables interrupts */ 18328c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI); 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci return IRQ_HANDLED; 18358c2ecf20Sopenharmony_ci} 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_ci/* 18398c2ecf20Sopenharmony_ci * OPL3 command 18408c2ecf20Sopenharmony_ci */ 18418c2ecf20Sopenharmony_cistatic void snd_cs4281_opl3_command(struct snd_opl3 *opl3, unsigned short cmd, 18428c2ecf20Sopenharmony_ci unsigned char val) 18438c2ecf20Sopenharmony_ci{ 18448c2ecf20Sopenharmony_ci unsigned long flags; 18458c2ecf20Sopenharmony_ci struct cs4281 *chip = opl3->private_data; 18468c2ecf20Sopenharmony_ci void __iomem *port; 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_ci if (cmd & OPL3_RIGHT) 18498c2ecf20Sopenharmony_ci port = chip->ba0 + BA0_B1AP; /* right port */ 18508c2ecf20Sopenharmony_ci else 18518c2ecf20Sopenharmony_ci port = chip->ba0 + BA0_B0AP; /* left port */ 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci spin_lock_irqsave(&opl3->reg_lock, flags); 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci writel((unsigned int)cmd, port); 18568c2ecf20Sopenharmony_ci udelay(10); 18578c2ecf20Sopenharmony_ci 18588c2ecf20Sopenharmony_ci writel((unsigned int)val, port + 4); 18598c2ecf20Sopenharmony_ci udelay(30); 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&opl3->reg_lock, flags); 18628c2ecf20Sopenharmony_ci} 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_cistatic int snd_cs4281_probe(struct pci_dev *pci, 18658c2ecf20Sopenharmony_ci const struct pci_device_id *pci_id) 18668c2ecf20Sopenharmony_ci{ 18678c2ecf20Sopenharmony_ci static int dev; 18688c2ecf20Sopenharmony_ci struct snd_card *card; 18698c2ecf20Sopenharmony_ci struct cs4281 *chip; 18708c2ecf20Sopenharmony_ci struct snd_opl3 *opl3; 18718c2ecf20Sopenharmony_ci int err; 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci if (dev >= SNDRV_CARDS) 18748c2ecf20Sopenharmony_ci return -ENODEV; 18758c2ecf20Sopenharmony_ci if (!enable[dev]) { 18768c2ecf20Sopenharmony_ci dev++; 18778c2ecf20Sopenharmony_ci return -ENOENT; 18788c2ecf20Sopenharmony_ci } 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 18818c2ecf20Sopenharmony_ci 0, &card); 18828c2ecf20Sopenharmony_ci if (err < 0) 18838c2ecf20Sopenharmony_ci return err; 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci if ((err = snd_cs4281_create(card, pci, &chip, dual_codec[dev])) < 0) { 18868c2ecf20Sopenharmony_ci snd_card_free(card); 18878c2ecf20Sopenharmony_ci return err; 18888c2ecf20Sopenharmony_ci } 18898c2ecf20Sopenharmony_ci card->private_data = chip; 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci if ((err = snd_cs4281_mixer(chip)) < 0) { 18928c2ecf20Sopenharmony_ci snd_card_free(card); 18938c2ecf20Sopenharmony_ci return err; 18948c2ecf20Sopenharmony_ci } 18958c2ecf20Sopenharmony_ci if ((err = snd_cs4281_pcm(chip, 0)) < 0) { 18968c2ecf20Sopenharmony_ci snd_card_free(card); 18978c2ecf20Sopenharmony_ci return err; 18988c2ecf20Sopenharmony_ci } 18998c2ecf20Sopenharmony_ci if ((err = snd_cs4281_midi(chip, 0)) < 0) { 19008c2ecf20Sopenharmony_ci snd_card_free(card); 19018c2ecf20Sopenharmony_ci return err; 19028c2ecf20Sopenharmony_ci } 19038c2ecf20Sopenharmony_ci if ((err = snd_opl3_new(card, OPL3_HW_OPL3_CS4281, &opl3)) < 0) { 19048c2ecf20Sopenharmony_ci snd_card_free(card); 19058c2ecf20Sopenharmony_ci return err; 19068c2ecf20Sopenharmony_ci } 19078c2ecf20Sopenharmony_ci opl3->private_data = chip; 19088c2ecf20Sopenharmony_ci opl3->command = snd_cs4281_opl3_command; 19098c2ecf20Sopenharmony_ci snd_opl3_init(opl3); 19108c2ecf20Sopenharmony_ci if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) { 19118c2ecf20Sopenharmony_ci snd_card_free(card); 19128c2ecf20Sopenharmony_ci return err; 19138c2ecf20Sopenharmony_ci } 19148c2ecf20Sopenharmony_ci snd_cs4281_create_gameport(chip); 19158c2ecf20Sopenharmony_ci strcpy(card->driver, "CS4281"); 19168c2ecf20Sopenharmony_ci strcpy(card->shortname, "Cirrus Logic CS4281"); 19178c2ecf20Sopenharmony_ci sprintf(card->longname, "%s at 0x%lx, irq %d", 19188c2ecf20Sopenharmony_ci card->shortname, 19198c2ecf20Sopenharmony_ci chip->ba0_addr, 19208c2ecf20Sopenharmony_ci chip->irq); 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci if ((err = snd_card_register(card)) < 0) { 19238c2ecf20Sopenharmony_ci snd_card_free(card); 19248c2ecf20Sopenharmony_ci return err; 19258c2ecf20Sopenharmony_ci } 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci pci_set_drvdata(pci, card); 19288c2ecf20Sopenharmony_ci dev++; 19298c2ecf20Sopenharmony_ci return 0; 19308c2ecf20Sopenharmony_ci} 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_cistatic void snd_cs4281_remove(struct pci_dev *pci) 19338c2ecf20Sopenharmony_ci{ 19348c2ecf20Sopenharmony_ci snd_card_free(pci_get_drvdata(pci)); 19358c2ecf20Sopenharmony_ci} 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci/* 19388c2ecf20Sopenharmony_ci * Power Management 19398c2ecf20Sopenharmony_ci */ 19408c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_cistatic const int saved_regs[SUSPEND_REGISTERS] = { 19438c2ecf20Sopenharmony_ci BA0_JSCTL, 19448c2ecf20Sopenharmony_ci BA0_GPIOR, 19458c2ecf20Sopenharmony_ci BA0_SSCR, 19468c2ecf20Sopenharmony_ci BA0_MIDCR, 19478c2ecf20Sopenharmony_ci BA0_SRCSA, 19488c2ecf20Sopenharmony_ci BA0_PASR, 19498c2ecf20Sopenharmony_ci BA0_CASR, 19508c2ecf20Sopenharmony_ci BA0_DACSR, 19518c2ecf20Sopenharmony_ci BA0_ADCSR, 19528c2ecf20Sopenharmony_ci BA0_FMLVC, 19538c2ecf20Sopenharmony_ci BA0_FMRVC, 19548c2ecf20Sopenharmony_ci BA0_PPLVC, 19558c2ecf20Sopenharmony_ci BA0_PPRVC, 19568c2ecf20Sopenharmony_ci}; 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci#define CLKCR1_CKRA 0x00010000L 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_cistatic int cs4281_suspend(struct device *dev) 19618c2ecf20Sopenharmony_ci{ 19628c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 19638c2ecf20Sopenharmony_ci struct cs4281 *chip = card->private_data; 19648c2ecf20Sopenharmony_ci u32 ulCLK; 19658c2ecf20Sopenharmony_ci unsigned int i; 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 19688c2ecf20Sopenharmony_ci snd_ac97_suspend(chip->ac97); 19698c2ecf20Sopenharmony_ci snd_ac97_suspend(chip->ac97_secondary); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1); 19728c2ecf20Sopenharmony_ci ulCLK |= CLKCR1_CKRA; 19738c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK); 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci /* Disable interrupts. */ 19768c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_CHGM); 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci /* remember the status registers */ 19798c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(saved_regs); i++) 19808c2ecf20Sopenharmony_ci if (saved_regs[i]) 19818c2ecf20Sopenharmony_ci chip->suspend_regs[i] = snd_cs4281_peekBA0(chip, saved_regs[i]); 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci /* Turn off the serial ports. */ 19848c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_SERMC, 0); 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci /* Power off FM, Joystick, AC link, */ 19878c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_SSPM, 0); 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_ci /* DLL off. */ 19908c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0); 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci /* AC link off. */ 19938c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_SPMC, 0); 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1); 19968c2ecf20Sopenharmony_ci ulCLK &= ~CLKCR1_CKRA; 19978c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK); 19988c2ecf20Sopenharmony_ci return 0; 19998c2ecf20Sopenharmony_ci} 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_cistatic int cs4281_resume(struct device *dev) 20028c2ecf20Sopenharmony_ci{ 20038c2ecf20Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 20048c2ecf20Sopenharmony_ci struct cs4281 *chip = card->private_data; 20058c2ecf20Sopenharmony_ci unsigned int i; 20068c2ecf20Sopenharmony_ci u32 ulCLK; 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1); 20098c2ecf20Sopenharmony_ci ulCLK |= CLKCR1_CKRA; 20108c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK); 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci snd_cs4281_chip_init(chip); 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci /* restore the status registers */ 20158c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(saved_regs); i++) 20168c2ecf20Sopenharmony_ci if (saved_regs[i]) 20178c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, saved_regs[i], chip->suspend_regs[i]); 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci snd_ac97_resume(chip->ac97); 20208c2ecf20Sopenharmony_ci snd_ac97_resume(chip->ac97_secondary); 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1); 20238c2ecf20Sopenharmony_ci ulCLK &= ~CLKCR1_CKRA; 20248c2ecf20Sopenharmony_ci snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK); 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D0); 20278c2ecf20Sopenharmony_ci return 0; 20288c2ecf20Sopenharmony_ci} 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(cs4281_pm, cs4281_suspend, cs4281_resume); 20318c2ecf20Sopenharmony_ci#define CS4281_PM_OPS &cs4281_pm 20328c2ecf20Sopenharmony_ci#else 20338c2ecf20Sopenharmony_ci#define CS4281_PM_OPS NULL 20348c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_cistatic struct pci_driver cs4281_driver = { 20378c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 20388c2ecf20Sopenharmony_ci .id_table = snd_cs4281_ids, 20398c2ecf20Sopenharmony_ci .probe = snd_cs4281_probe, 20408c2ecf20Sopenharmony_ci .remove = snd_cs4281_remove, 20418c2ecf20Sopenharmony_ci .driver = { 20428c2ecf20Sopenharmony_ci .pm = CS4281_PM_OPS, 20438c2ecf20Sopenharmony_ci }, 20448c2ecf20Sopenharmony_ci}; 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_cimodule_pci_driver(cs4281_driver); 2047