diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-17 00:20:36 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-17 00:20:36 +0200 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /sound/oss/cs4281 | |
download | linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.xz linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'sound/oss/cs4281')
-rw-r--r-- | sound/oss/cs4281/Makefile | 6 | ||||
-rw-r--r-- | sound/oss/cs4281/cs4281_hwdefs.h | 1234 | ||||
-rw-r--r-- | sound/oss/cs4281/cs4281_wrapper-24.c | 41 | ||||
-rw-r--r-- | sound/oss/cs4281/cs4281m.c | 4505 | ||||
-rw-r--r-- | sound/oss/cs4281/cs4281pm-24.c | 84 | ||||
-rw-r--r-- | sound/oss/cs4281/cs4281pm.h | 74 |
6 files changed, 5944 insertions, 0 deletions
diff --git a/sound/oss/cs4281/Makefile b/sound/oss/cs4281/Makefile new file mode 100644 index 000000000000..6d527e8530d6 --- /dev/null +++ b/sound/oss/cs4281/Makefile @@ -0,0 +1,6 @@ +# Makefile for Cirrus Logic-Crystal CS4281 +# + +obj-$(CONFIG_SOUND_CS4281) += cs4281.o + +cs4281-objs += cs4281m.o diff --git a/sound/oss/cs4281/cs4281_hwdefs.h b/sound/oss/cs4281/cs4281_hwdefs.h new file mode 100644 index 000000000000..701d595e33f5 --- /dev/null +++ b/sound/oss/cs4281/cs4281_hwdefs.h @@ -0,0 +1,1234 @@ +//**************************************************************************** +// +// HWDEFS.H - Definitions of the registers and data structures used by the +// CS4281 +// +// Copyright (c) 1999,2000,2001 Crystal Semiconductor Corp. +// +//**************************************************************************** + +#ifndef _H_HWDEFS +#define _H_HWDEFS + +//**************************************************************************** +// +// The following define the offsets of the registers located in the PCI +// configuration space of the CS4281 part. +// +//**************************************************************************** +#define PCICONFIG_DEVID_VENID 0x00000000L +#define PCICONFIG_STATUS_COMMAND 0x00000004L +#define PCICONFIG_CLASS_REVISION 0x00000008L +#define PCICONFIG_LATENCY_TIMER 0x0000000CL +#define PCICONFIG_BA0 0x00000010L +#define PCICONFIG_BA1 0x00000014L +#define PCICONFIG_SUBSYSID_SUBSYSVENID 0x0000002CL +#define PCICONFIG_INTERRUPT 0x0000003CL + +//**************************************************************************** +// +// The following define the offsets of the registers accessed via base address +// register zero on the CS4281 part. +// +//**************************************************************************** +#define BA0_HISR 0x00000000L +#define BA0_HICR 0x00000008L +#define BA0_HIMR 0x0000000CL +#define BA0_IIER 0x00000010L +#define BA0_HDSR0 0x000000F0L +#define BA0_HDSR1 0x000000F4L +#define BA0_HDSR2 0x000000F8L +#define BA0_HDSR3 0x000000FCL +#define BA0_DCA0 0x00000110L +#define BA0_DCC0 0x00000114L +#define BA0_DBA0 0x00000118L +#define BA0_DBC0 0x0000011CL +#define BA0_DCA1 0x00000120L +#define BA0_DCC1 0x00000124L +#define BA0_DBA1 0x00000128L +#define BA0_DBC1 0x0000012CL +#define BA0_DCA2 0x00000130L +#define BA0_DCC2 0x00000134L +#define BA0_DBA2 0x00000138L +#define BA0_DBC2 0x0000013CL +#define BA0_DCA3 0x00000140L +#define BA0_DCC3 0x00000144L +#define BA0_DBA3 0x00000148L +#define BA0_DBC3 0x0000014CL +#define BA0_DMR0 0x00000150L +#define BA0_DCR0 0x00000154L +#define BA0_DMR1 0x00000158L +#define BA0_DCR1 0x0000015CL +#define BA0_DMR2 0x00000160L +#define BA0_DCR2 0x00000164L +#define BA0_DMR3 0x00000168L +#define BA0_DCR3 0x0000016CL +#define BA0_DLMR 0x00000170L +#define BA0_DLSR 0x00000174L +#define BA0_FCR0 0x00000180L +#define BA0_FCR1 0x00000184L +#define BA0_FCR2 0x00000188L +#define BA0_FCR3 0x0000018CL +#define BA0_FPDR0 0x00000190L +#define BA0_FPDR1 0x00000194L +#define BA0_FPDR2 0x00000198L +#define BA0_FPDR3 0x0000019CL +#define BA0_FCHS 0x0000020CL +#define BA0_FSIC0 0x00000210L +#define BA0_FSIC1 0x00000214L +#define BA0_FSIC2 0x00000218L +#define BA0_FSIC3 0x0000021CL +#define BA0_PCICFG00 0x00000300L +#define BA0_PCICFG04 0x00000304L +#define BA0_PCICFG08 0x00000308L +#define BA0_PCICFG0C 0x0000030CL +#define BA0_PCICFG10 0x00000310L +#define BA0_PCICFG14 0x00000314L +#define BA0_PCICFG18 0x00000318L +#define BA0_PCICFG1C 0x0000031CL +#define BA0_PCICFG20 0x00000320L +#define BA0_PCICFG24 0x00000324L +#define BA0_PCICFG28 0x00000328L +#define BA0_PCICFG2C 0x0000032CL +#define BA0_PCICFG30 0x00000330L +#define BA0_PCICFG34 0x00000334L +#define BA0_PCICFG38 0x00000338L +#define BA0_PCICFG3C 0x0000033CL +#define BA0_PCICFG40 0x00000340L +#define BA0_PMCS 0x00000344L +#define BA0_CWPR 0x000003E0L +#define BA0_EPPMC 0x000003E4L +#define BA0_GPIOR 0x000003E8L +#define BA0_SPMC 0x000003ECL +#define BA0_CFLR 0x000003F0L +#define BA0_IISR 0x000003F4L +#define BA0_TMS 0x000003F8L +#define BA0_SSVID 0x000003FCL +#define BA0_CLKCR1 0x00000400L +#define BA0_FRR 0x00000410L +#define BA0_SLT12O 0x0000041CL +#define BA0_SERMC 0x00000420L +#define BA0_SERC1 0x00000428L +#define BA0_SERC2 0x0000042CL +#define BA0_SLT12M 0x0000045CL +#define BA0_ACCTL 0x00000460L +#define BA0_ACSTS 0x00000464L +#define BA0_ACOSV 0x00000468L +#define BA0_ACCAD 0x0000046CL +#define BA0_ACCDA 0x00000470L +#define BA0_ACISV 0x00000474L +#define BA0_ACSAD 0x00000478L +#define BA0_ACSDA 0x0000047CL +#define BA0_JSPT 0x00000480L +#define BA0_JSCTL 0x00000484L +#define BA0_MIDCR 0x00000490L +#define BA0_MIDCMD 0x00000494L +#define BA0_MIDSR 0x00000494L +#define BA0_MIDWP 0x00000498L +#define BA0_MIDRP 0x0000049CL +#define BA0_AODSD1 0x000004A8L +#define BA0_AODSD2 0x000004ACL +#define BA0_CFGI 0x000004B0L +#define BA0_SLT12M2 0x000004DCL +#define BA0_ACSTS2 0x000004E4L +#define BA0_ACISV2 0x000004F4L +#define BA0_ACSAD2 0x000004F8L +#define BA0_ACSDA2 0x000004FCL +#define BA0_IOTGP 0x00000500L +#define BA0_IOTSB 0x00000504L +#define BA0_IOTFM 0x00000508L +#define BA0_IOTDMA 0x0000050CL +#define BA0_IOTAC0 0x00000500L +#define BA0_IOTAC1 0x00000504L +#define BA0_IOTAC2 0x00000508L +#define BA0_IOTAC3 0x0000050CL +#define BA0_IOTPCP 0x0000052CL +#define BA0_IOTCC 0x00000530L +#define BA0_IOTCR 0x0000058CL +#define BA0_PCPRR 0x00000600L +#define BA0_PCPGR 0x00000604L +#define BA0_PCPCR 0x00000608L +#define BA0_PCPCIEN 0x00000608L +#define BA0_SBMAR 0x00000700L +#define BA0_SBMDR 0x00000704L +#define BA0_SBRR 0x00000708L +#define BA0_SBRDP 0x0000070CL +#define BA0_SBWDP 0x00000710L +#define BA0_SBWBS 0x00000710L +#define BA0_SBRBS 0x00000714L +#define BA0_FMSR 0x00000730L +#define BA0_B0AP 0x00000730L +#define BA0_FMDP 0x00000734L +#define BA0_B1AP 0x00000738L +#define BA0_B1DP 0x0000073CL +#define BA0_SSPM 0x00000740L +#define BA0_DACSR 0x00000744L +#define BA0_ADCSR 0x00000748L +#define BA0_SSCR 0x0000074CL +#define BA0_FMLVC 0x00000754L +#define BA0_FMRVC 0x00000758L +#define BA0_SRCSA 0x0000075CL +#define BA0_PPLVC 0x00000760L +#define BA0_PPRVC 0x00000764L +#define BA0_PASR 0x00000768L +#define BA0_CASR 0x0000076CL + +//**************************************************************************** +// +// The following define the offsets of the AC97 shadow registers, which appear +// as a virtual extension to the base address register zero memory range. +// +//**************************************************************************** +#define AC97_REG_OFFSET_MASK 0x0000007EL +#define AC97_CODEC_NUMBER_MASK 0x00003000L + +#define BA0_AC97_RESET 0x00001000L +#define BA0_AC97_MASTER_VOLUME 0x00001002L +#define BA0_AC97_HEADPHONE_VOLUME 0x00001004L +#define BA0_AC97_MASTER_VOLUME_MONO 0x00001006L +#define BA0_AC97_MASTER_TONE 0x00001008L +#define BA0_AC97_PC_BEEP_VOLUME 0x0000100AL +#define BA0_AC97_PHONE_VOLUME 0x0000100CL +#define BA0_AC97_MIC_VOLUME 0x0000100EL +#define BA0_AC97_LINE_IN_VOLUME 0x00001010L +#define BA0_AC97_CD_VOLUME 0x00001012L +#define BA0_AC97_VIDEO_VOLUME 0x00001014L +#define BA0_AC97_AUX_VOLUME 0x00001016L +#define BA0_AC97_PCM_OUT_VOLUME 0x00001018L +#define BA0_AC97_RECORD_SELECT 0x0000101AL +#define BA0_AC97_RECORD_GAIN 0x0000101CL +#define BA0_AC97_RECORD_GAIN_MIC 0x0000101EL +#define BA0_AC97_GENERAL_PURPOSE 0x00001020L +#define BA0_AC97_3D_CONTROL 0x00001022L +#define BA0_AC97_MODEM_RATE 0x00001024L +#define BA0_AC97_POWERDOWN 0x00001026L +#define BA0_AC97_EXT_AUDIO_ID 0x00001028L +#define BA0_AC97_EXT_AUDIO_POWER 0x0000102AL +#define BA0_AC97_PCM_FRONT_DAC_RATE 0x0000102CL +#define BA0_AC97_PCM_SURR_DAC_RATE 0x0000102EL +#define BA0_AC97_PCM_LFE_DAC_RATE 0x00001030L +#define BA0_AC97_PCM_LR_ADC_RATE 0x00001032L +#define BA0_AC97_MIC_ADC_RATE 0x00001034L +#define BA0_AC97_6CH_VOL_C_LFE 0x00001036L +#define BA0_AC97_6CH_VOL_SURROUND 0x00001038L +#define BA0_AC97_RESERVED_3A 0x0000103AL +#define BA0_AC97_EXT_MODEM_ID 0x0000103CL +#define BA0_AC97_EXT_MODEM_POWER 0x0000103EL +#define BA0_AC97_LINE1_CODEC_RATE 0x00001040L +#define BA0_AC97_LINE2_CODEC_RATE 0x00001042L +#define BA0_AC97_HANDSET_CODEC_RATE 0x00001044L +#define BA0_AC97_LINE1_CODEC_LEVEL 0x00001046L +#define BA0_AC97_LINE2_CODEC_LEVEL 0x00001048L +#define BA0_AC97_HANDSET_CODEC_LEVEL 0x0000104AL +#define BA0_AC97_GPIO_PIN_CONFIG 0x0000104CL +#define BA0_AC97_GPIO_PIN_TYPE 0x0000104EL +#define BA0_AC97_GPIO_PIN_STICKY 0x00001050L +#define BA0_AC97_GPIO_PIN_WAKEUP 0x00001052L +#define BA0_AC97_GPIO_PIN_STATUS 0x00001054L +#define BA0_AC97_MISC_MODEM_AFE_STAT 0x00001056L +#define BA0_AC97_RESERVED_58 0x00001058L +#define BA0_AC97_CRYSTAL_REV_N_FAB_ID 0x0000105AL +#define BA0_AC97_TEST_AND_MISC_CTRL 0x0000105CL +#define BA0_AC97_AC_MODE 0x0000105EL +#define BA0_AC97_MISC_CRYSTAL_CONTROL 0x00001060L +#define BA0_AC97_LINE1_HYPRID_CTRL 0x00001062L +#define BA0_AC97_VENDOR_RESERVED_64 0x00001064L +#define BA0_AC97_VENDOR_RESERVED_66 0x00001066L +#define BA0_AC97_SPDIF_CONTROL 0x00001068L +#define BA0_AC97_VENDOR_RESERVED_6A 0x0000106AL +#define BA0_AC97_VENDOR_RESERVED_6C 0x0000106CL +#define BA0_AC97_VENDOR_RESERVED_6E 0x0000106EL +#define BA0_AC97_VENDOR_RESERVED_70 0x00001070L +#define BA0_AC97_VENDOR_RESERVED_72 0x00001072L +#define BA0_AC97_VENDOR_RESERVED_74 0x00001074L +#define BA0_AC97_CAL_ADDRESS 0x00001076L +#define BA0_AC97_CAL_DATA 0x00001078L +#define BA0_AC97_VENDOR_RESERVED_7A 0x0000107AL +#define BA0_AC97_VENDOR_ID1 0x0000107CL +#define BA0_AC97_VENDOR_ID2 0x0000107EL + +//**************************************************************************** +// +// The following define the offsets of the registers and memories accessed via +// base address register one on the CS4281 part. +// +//**************************************************************************** + +//**************************************************************************** +// +// The following defines are for the flags in the PCI device ID/vendor ID +// register. +// +//**************************************************************************** +#define PDV_VENID_MASK 0x0000FFFFL +#define PDV_DEVID_MASK 0xFFFF0000L +#define PDV_VENID_SHIFT 0L +#define PDV_DEVID_SHIFT 16L +#define VENID_CIRRUS_LOGIC 0x1013L +#define DEVID_CS4281 0x6005L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI status and command +// register. +// +//**************************************************************************** +#define PSC_IO_SPACE_ENABLE 0x00000001L +#define PSC_MEMORY_SPACE_ENABLE 0x00000002L +#define PSC_BUS_MASTER_ENABLE 0x00000004L +#define PSC_SPECIAL_CYCLES 0x00000008L +#define PSC_MWI_ENABLE 0x00000010L +#define PSC_VGA_PALETTE_SNOOP 0x00000020L +#define PSC_PARITY_RESPONSE 0x00000040L +#define PSC_WAIT_CONTROL 0x00000080L +#define PSC_SERR_ENABLE 0x00000100L +#define PSC_FAST_B2B_ENABLE 0x00000200L +#define PSC_UDF_MASK 0x007F0000L +#define PSC_FAST_B2B_CAPABLE 0x00800000L +#define PSC_PARITY_ERROR_DETECTED 0x01000000L +#define PSC_DEVSEL_TIMING_MASK 0x06000000L +#define PSC_TARGET_ABORT_SIGNALLED 0x08000000L +#define PSC_RECEIVED_TARGET_ABORT 0x10000000L +#define PSC_RECEIVED_MASTER_ABORT 0x20000000L +#define PSC_SIGNALLED_SERR 0x40000000L +#define PSC_DETECTED_PARITY_ERROR 0x80000000L +#define PSC_UDF_SHIFT 16L +#define PSC_DEVSEL_TIMING_SHIFT 25L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI class/revision ID +// register. +// +//**************************************************************************** +#define PCR_REVID_MASK 0x000000FFL +#define PCR_INTERFACE_MASK 0x0000FF00L +#define PCR_SUBCLASS_MASK 0x00FF0000L +#define PCR_CLASS_MASK 0xFF000000L +#define PCR_REVID_SHIFT 0L +#define PCR_INTERFACE_SHIFT 8L +#define PCR_SUBCLASS_SHIFT 16L +#define PCR_CLASS_SHIFT 24L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI latency timer register. +// +//**************************************************************************** +#define PLT_CACHE_LINE_SIZE_MASK 0x000000FFL +#define PLT_LATENCY_TIMER_MASK 0x0000FF00L +#define PLT_HEADER_TYPE_MASK 0x00FF0000L +#define PLT_BIST_MASK 0xFF000000L +#define PLT_CACHE_LINE_SIZE_SHIFT 0L +#define PLT_LATENCY_TIMER_SHIFT 8L +#define PLT_HEADER_TYPE_SHIFT 16L +#define PLT_BIST_SHIFT 24L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI base address registers. +// +//**************************************************************************** +#define PBAR_MEMORY_SPACE_INDICATOR 0x00000001L +#define PBAR_LOCATION_TYPE_MASK 0x00000006L +#define PBAR_NOT_PREFETCHABLE 0x00000008L +#define PBAR_ADDRESS_MASK 0xFFFFFFF0L +#define PBAR_LOCATION_TYPE_SHIFT 1L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI subsystem ID/subsystem +// vendor ID register. +// +//**************************************************************************** +#define PSS_SUBSYSTEM_VENDOR_ID_MASK 0x0000FFFFL +#define PSS_SUBSYSTEM_ID_MASK 0xFFFF0000L +#define PSS_SUBSYSTEM_VENDOR_ID_SHIFT 0L +#define PSS_SUBSYSTEM_ID_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the PCI interrupt register. +// +//**************************************************************************** +#define PI_LINE_MASK 0x000000FFL +#define PI_PIN_MASK 0x0000FF00L +#define PI_MIN_GRANT_MASK 0x00FF0000L +#define PI_MAX_LATENCY_MASK 0xFF000000L +#define PI_LINE_SHIFT 0L +#define PI_PIN_SHIFT 8L +#define PI_MIN_GRANT_SHIFT 16L +#define PI_MAX_LATENCY_SHIFT 24L + +//**************************************************************************** +// +// The following defines are for the flags in the host interrupt status +// register. +// +//**************************************************************************** +#define HISR_HVOLMASK 0x00000003L +#define HISR_VDNI 0x00000001L +#define HISR_VUPI 0x00000002L +#define HISR_GP1I 0x00000004L +#define HISR_GP3I 0x00000008L +#define HISR_GPSI 0x00000010L +#define HISR_GPPI 0x00000020L +#define HISR_DMAI 0x00040000L +#define HISR_FIFOI 0x00100000L +#define HISR_HVOL 0x00200000L +#define HISR_MIDI 0x00400000L +#define HISR_SBINT 0x00800000L +#define HISR_INTENA 0x80000000L +#define HISR_DMA_MASK 0x00000F00L +#define HISR_FIFO_MASK 0x0000F000L +#define HISR_DMA_SHIFT 8L +#define HISR_FIFO_SHIFT 12L +#define HISR_FIFO0 0x00001000L +#define HISR_FIFO1 0x00002000L +#define HISR_FIFO2 0x00004000L +#define HISR_FIFO3 0x00008000L +#define HISR_DMA0 0x00000100L +#define HISR_DMA1 0x00000200L +#define HISR_DMA2 0x00000400L +#define HISR_DMA3 0x00000800L +#define HISR_RESERVED 0x40000000L + +//**************************************************************************** +// +// The following defines are for the flags in the host interrupt control +// register. +// +//**************************************************************************** +#define HICR_IEV 0x00000001L +#define HICR_CHGM 0x00000002L + +//**************************************************************************** +// +// The following defines are for the flags in the DMA Mode Register n +// (DMRn) +// +//**************************************************************************** +#define DMRn_TR_MASK 0x0000000CL +#define DMRn_TR_SHIFT 2L +#define DMRn_AUTO 0x00000010L +#define DMRn_TR_READ 0x00000008L +#define DMRn_TR_WRITE 0x00000004L +#define DMRn_TYPE_MASK 0x000000C0L +#define DMRn_TYPE_SHIFT 6L +#define DMRn_SIZE8 0x00010000L +#define DMRn_MONO 0x00020000L +#define DMRn_BEND 0x00040000L +#define DMRn_USIGN 0x00080000L +#define DMRn_SIZE20 0x00100000L +#define DMRn_SWAPC 0x00400000L +#define DMRn_CBC 0x01000000L +#define DMRn_TBC 0x02000000L +#define DMRn_POLL 0x10000000L +#define DMRn_DMA 0x20000000L +#define DMRn_FSEL_MASK 0xC0000000L +#define DMRn_FSEL_SHIFT 30L +#define DMRn_FSEL0 0x00000000L +#define DMRn_FSEL1 0x40000000L +#define DMRn_FSEL2 0x80000000L +#define DMRn_FSEL3 0xC0000000L + +//**************************************************************************** +// +// The following defines are for the flags in the DMA Command Register n +// (DCRn) +// +//**************************************************************************** +#define DCRn_HTCIE 0x00020000L +#define DCRn_TCIE 0x00010000L +#define DCRn_MSK 0x00000001L + +//**************************************************************************** +// +// The following defines are for the flags in the FIFO Control +// register n.(FCRn) +// +//**************************************************************************** +#define FCRn_OF_MASK 0x0000007FL +#define FCRn_OF_SHIFT 0L +#define FCRn_SZ_MASK 0x00007F00L +#define FCRn_SZ_SHIFT 8L +#define FCRn_LS_MASK 0x001F0000L +#define FCRn_LS_SHIFT 16L +#define FCRn_RS_MASK 0x1F000000L +#define FCRn_RS_SHIFT 24L +#define FCRn_FEN 0x80000000L +#define FCRn_PSH 0x20000000L +#define FCRn_DACZ 0x40000000L + +//**************************************************************************** +// +// The following defines are for the flags in the serial port Power Management +// control register.(SPMC) +// +//**************************************************************************** +#define SPMC_RSTN 0x00000001L +#define SPMC_ASYN 0x00000002L +#define SPMC_WUP1 0x00000004L +#define SPMC_WUP2 0x00000008L +#define SPMC_ASDI2E 0x00000100L +#define SPMC_ESSPD 0x00000200L +#define SPMC_GISPEN 0x00004000L +#define SPMC_GIPPEN 0x00008000L + +//**************************************************************************** +// +// The following defines are for the flags in the Configuration Load register. +// (CFLR) +// +//**************************************************************************** +#define CFLR_CLOCK_SOURCE_MASK 0x00000003L +#define CFLR_CLOCK_SOURCE_AC97 0x00000001L + +#define CFLR_CB0_MASK 0x000000FFL +#define CFLR_CB1_MASK 0x0000FF00L +#define CFLR_CB2_MASK 0x00FF0000L +#define CFLR_CB3_MASK 0xFF000000L +#define CFLR_CB0_SHIFT 0L +#define CFLR_CB1_SHIFT 8L +#define CFLR_CB2_SHIFT 16L +#define CFLR_CB3_SHIFT 24L + +#define IOTCR_DMA0 0x00000000L +#define IOTCR_DMA1 0x00000400L +#define IOTCR_DMA2 0x00000800L +#define IOTCR_DMA3 0x00000C00L +#define IOTCR_CCLS 0x00000100L +#define IOTCR_PCPCI 0x00000200L +#define IOTCR_DDMA 0x00000300L + +#define SBWBS_WBB 0x00000080L + +//**************************************************************************** +// +// The following defines are for the flags in the SRC Slot Assignment Register +// (SRCSA) +// +//**************************************************************************** +#define SRCSA_PLSS_MASK 0x0000001FL +#define SRCSA_PLSS_SHIFT 0L +#define SRCSA_PRSS_MASK 0x00001F00L +#define SRCSA_PRSS_SHIFT 8L +#define SRCSA_CLSS_MASK 0x001F0000L +#define SRCSA_CLSS_SHIFT 16L +#define SRCSA_CRSS_MASK 0x1F000000L +#define SRCSA_CRSS_SHIFT 24L + +//**************************************************************************** +// +// The following defines are for the flags in the Sound System Power Management +// register.(SSPM) +// +//**************************************************************************** +#define SSPM_FPDN 0x00000080L +#define SSPM_MIXEN 0x00000040L +#define SSPM_CSRCEN 0x00000020L +#define SSPM_PSRCEN 0x00000010L +#define SSPM_JSEN 0x00000008L +#define SSPM_ACLEN 0x00000004L +#define SSPM_FMEN 0x00000002L + +//**************************************************************************** +// +// The following defines are for the flags in the Sound System Control +// Register. (SSCR) +// +//**************************************************************************** +#define SSCR_SB 0x00000004L +#define SSCR_HVC 0x00000008L +#define SSCR_LPFIFO 0x00000040L +#define SSCR_LPSRC 0x00000080L +#define SSCR_XLPSRC 0x00000100L +#define SSCR_MVMD 0x00010000L +#define SSCR_MVAD 0x00020000L +#define SSCR_MVLD 0x00040000L +#define SSCR_MVCS 0x00080000L + +//**************************************************************************** +// +// The following defines are for the flags in the Clock Control Register 1. +// (CLKCR1) +// +//**************************************************************************** +#define CLKCR1_DLLSS_MASK 0x0000000CL +#define CLKCR1_DLLSS_SHIFT 2L +#define CLKCR1_DLLP 0x00000010L +#define CLKCR1_SWCE 0x00000020L +#define CLKCR1_DLLOS 0x00000040L +#define CLKCR1_CKRA 0x00010000L +#define CLKCR1_CKRN 0x00020000L +#define CLKCR1_DLLRDY 0x01000000L +#define CLKCR1_CLKON 0x02000000L + +//**************************************************************************** +// +// The following defines are for the flags in the Sound Blaster Read Buffer +// Status.(SBRBS) +// +//**************************************************************************** +#define SBRBS_RD_MASK 0x0000007FL +#define SBRBS_RD_SHIFT 0L +#define SBRBS_RBF 0x00000080L + +//**************************************************************************** +// +// The following defines are for the flags in the serial port master control +// register.(SERMC) +// +//**************************************************************************** +#define SERMC_MSPE 0x00000001L +#define SERMC_PTC_MASK 0x0000000EL +#define SERMC_PTC_SHIFT 1L +#define SERMC_PTC_AC97 0x00000002L +#define SERMC_PLB 0x00000010L +#define SERMC_PXLB 0x00000020L +#define SERMC_LOFV 0x00080000L +#define SERMC_SLB 0x00100000L +#define SERMC_SXLB 0x00200000L +#define SERMC_ODSEN1 0x01000000L +#define SERMC_ODSEN2 0x02000000L + +//**************************************************************************** +// +// The following defines are for the flags in the General Purpose I/O Register. +// (GPIOR) +// +//**************************************************************************** +#define GPIOR_VDNS 0x00000001L +#define GPIOR_VUPS 0x00000002L +#define GPIOR_GP1S 0x00000004L +#define GPIOR_GP3S 0x00000008L +#define GPIOR_GPSS 0x00000010L +#define GPIOR_GPPS 0x00000020L +#define GPIOR_GP1D 0x00000400L +#define GPIOR_GP3D 0x00000800L +#define GPIOR_VDNLT 0x00010000L +#define GPIOR_VDNPO 0x00020000L +#define GPIOR_VDNST 0x00040000L +#define GPIOR_VDNW 0x00080000L +#define GPIOR_VUPLT 0x00100000L +#define GPIOR_VUPPO 0x00200000L +#define GPIOR_VUPST 0x00400000L +#define GPIOR_VUPW 0x00800000L +#define GPIOR_GP1OE 0x01000000L +#define GPIOR_GP1PT 0x02000000L +#define GPIOR_GP1ST 0x04000000L +#define GPIOR_GP1W 0x08000000L +#define GPIOR_GP3OE 0x10000000L +#define GPIOR_GP3PT 0x20000000L +#define GPIOR_GP3ST 0x40000000L +#define GPIOR_GP3W 0x80000000L + +//**************************************************************************** +// +// The following defines are for the flags in the clock control register 1. +// +//**************************************************************************** +#define CLKCR1_PLLSS_MASK 0x0000000CL +#define CLKCR1_PLLSS_SERIAL 0x00000000L +#define CLKCR1_PLLSS_CRYSTAL 0x00000004L +#define CLKCR1_PLLSS_PCI 0x00000008L +#define CLKCR1_PLLSS_RESERVED 0x0000000CL +#define CLKCR1_PLLP 0x00000010L +#define CLKCR1_SWCE 0x00000020L +#define CLKCR1_PLLOS 0x00000040L + +//**************************************************************************** +// +// The following defines are for the flags in the feature reporting register. +// +//**************************************************************************** +#define FRR_FAB_MASK 0x00000003L +#define FRR_MASK_MASK 0x0000001CL +#define FRR_ID_MASK 0x00003000L +#define FRR_FAB_SHIFT 0L +#define FRR_MASK_SHIFT 2L +#define FRR_ID_SHIFT 12L + +//**************************************************************************** +// +// The following defines are for the flags in the serial port 1 configuration +// register. +// +//**************************************************************************** +#define SERC1_VALUE 0x00000003L +#define SERC1_SO1EN 0x00000001L +#define SERC1_SO1F_MASK 0x0000000EL +#define SERC1_SO1F_CS423X 0x00000000L +#define SERC1_SO1F_AC97 0x00000002L +#define SERC1_SO1F_DAC 0x00000004L +#define SERC1_SO1F_SPDIF 0x00000006L + +//**************************************************************************** +// +// The following defines are for the flags in the serial port 2 configuration +// register. +// +//**************************************************************************** +#define SERC2_VALUE 0x00000003L +#define SERC2_SI1EN 0x00000001L +#define SERC2_SI1F_MASK 0x0000000EL +#define SERC2_SI1F_CS423X 0x00000000L +#define SERC2_SI1F_AC97 0x00000002L +#define SERC2_SI1F_ADC 0x00000004L +#define SERC2_SI1F_SPDIF 0x00000006L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 control register. +// +//**************************************************************************** +#define ACCTL_ESYN 0x00000002L +#define ACCTL_VFRM 0x00000004L +#define ACCTL_DCV 0x00000008L +#define ACCTL_CRW 0x00000010L +#define ACCTL_TC 0x00000040L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status register. +// +//**************************************************************************** +#define ACSTS_CRDY 0x00000001L +#define ACSTS_VSTS 0x00000002L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 output slot valid +// register. +// +//**************************************************************************** +#define ACOSV_SLV3 0x00000001L +#define ACOSV_SLV4 0x00000002L +#define ACOSV_SLV5 0x00000004L +#define ACOSV_SLV6 0x00000008L +#define ACOSV_SLV7 0x00000010L +#define ACOSV_SLV8 0x00000020L +#define ACOSV_SLV9 0x00000040L +#define ACOSV_SLV10 0x00000080L +#define ACOSV_SLV11 0x00000100L +#define ACOSV_SLV12 0x00000200L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 command address +// register. +// +//**************************************************************************** +#define ACCAD_CI_MASK 0x0000007FL +#define ACCAD_CI_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 command data register. +// +//**************************************************************************** +#define ACCDA_CD_MASK 0x0000FFFFL +#define ACCDA_CD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 input slot valid +// register. +// +//**************************************************************************** +#define ACISV_ISV3 0x00000001L +#define ACISV_ISV4 0x00000002L +#define ACISV_ISV5 0x00000004L +#define ACISV_ISV6 0x00000008L +#define ACISV_ISV7 0x00000010L +#define ACISV_ISV8 0x00000020L +#define ACISV_ISV9 0x00000040L +#define ACISV_ISV10 0x00000080L +#define ACISV_ISV11 0x00000100L +#define ACISV_ISV12 0x00000200L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status address +// register. +// +//**************************************************************************** +#define ACSAD_SI_MASK 0x0000007FL +#define ACSAD_SI_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status data register. +// +//**************************************************************************** +#define ACSDA_SD_MASK 0x0000FFFFL +#define ACSDA_SD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap address and control +// registers (all 12). +// +//**************************************************************************** +#define IOTAC_SA_MASK 0x0000FFFFL +#define IOTAC_MSK_MASK 0x000F0000L +#define IOTAC_IODC_MASK 0x06000000L +#define IOTAC_IODC_16_BIT 0x00000000L +#define IOTAC_IODC_10_BIT 0x02000000L +#define IOTAC_IODC_12_BIT 0x04000000L +#define IOTAC_WSPI 0x08000000L +#define IOTAC_RSPI 0x10000000L +#define IOTAC_WSE 0x20000000L +#define IOTAC_WE 0x40000000L +#define IOTAC_RE 0x80000000L +#define IOTAC_SA_SHIFT 0L +#define IOTAC_MSK_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the PC/PCI master enable +// register. +// +//**************************************************************************** +#define PCPCIEN_EN 0x00000001L + +//**************************************************************************** +// +// The following defines are for the flags in the joystick poll/trigger +// register. +// +//**************************************************************************** +#define JSPT_CAX 0x00000001L +#define JSPT_CAY 0x00000002L +#define JSPT_CBX 0x00000004L +#define JSPT_CBY 0x00000008L +#define JSPT_BA1 0x00000010L +#define JSPT_BA2 0x00000020L +#define JSPT_BB1 0x00000040L +#define JSPT_BB2 0x00000080L + +//**************************************************************************** +// +// The following defines are for the flags in the joystick control register. +// The TBF bit has been moved from MIDSR register to JSCTL register bit 8. +// +//**************************************************************************** +#define JSCTL_SP_MASK 0x00000003L +#define JSCTL_SP_SLOW 0x00000000L +#define JSCTL_SP_MEDIUM_SLOW 0x00000001L +#define JSCTL_SP_MEDIUM_FAST 0x00000002L +#define JSCTL_SP_FAST 0x00000003L +#define JSCTL_ARE 0x00000004L +#define JSCTL_TBF 0x00000100L + + +//**************************************************************************** +// +// The following defines are for the flags in the MIDI control register. +// +//**************************************************************************** +#define MIDCR_TXE 0x00000001L +#define MIDCR_RXE 0x00000002L +#define MIDCR_RIE 0x00000004L +#define MIDCR_TIE 0x00000008L +#define MIDCR_MLB 0x00000010L +#define MIDCR_MRST 0x00000020L + +//**************************************************************************** +// +// The following defines are for the flags in the MIDI status register. +// +//**************************************************************************** +#define MIDSR_RBE 0x00000080L +#define MIDSR_RDA 0x00008000L + +//**************************************************************************** +// +// The following defines are for the flags in the MIDI write port register. +// +//**************************************************************************** +#define MIDWP_MWD_MASK 0x000000FFL +#define MIDWP_MWD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the MIDI read port register. +// +//**************************************************************************** +#define MIDRP_MRD_MASK 0x000000FFL +#define MIDRP_MRD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the configuration interface +// register. +// +//**************************************************************************** +#define CFGI_CLK 0x00000001L +#define CFGI_DOUT 0x00000002L +#define CFGI_DIN_EEN 0x00000004L +#define CFGI_EELD 0x00000008L + +//**************************************************************************** +// +// The following defines are for the flags in the subsystem ID and vendor ID +// register. +// +//**************************************************************************** +#define SSVID_VID_MASK 0x0000FFFFL +#define SSVID_SID_MASK 0xFFFF0000L +#define SSVID_VID_SHIFT 0L +#define SSVID_SID_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the GPIO pin interface register. +// +//**************************************************************************** +#define GPIOR_VOLDN 0x00000001L +#define GPIOR_VOLUP 0x00000002L +#define GPIOR_SI2D 0x00000004L +#define GPIOR_SI2OE 0x00000008L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status register 2. +// +//**************************************************************************** +#define ACSTS2_CRDY 0x00000001L +#define ACSTS2_VSTS 0x00000002L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 input slot valid +// register 2. +// +//**************************************************************************** +#define ACISV2_ISV3 0x00000001L +#define ACISV2_ISV4 0x00000002L +#define ACISV2_ISV5 0x00000004L +#define ACISV2_ISV6 0x00000008L +#define ACISV2_ISV7 0x00000010L +#define ACISV2_ISV8 0x00000020L +#define ACISV2_ISV9 0x00000040L +#define ACISV2_ISV10 0x00000080L +#define ACISV2_ISV11 0x00000100L +#define ACISV2_ISV12 0x00000200L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status address +// register 2. +// +//**************************************************************************** +#define ACSAD2_SI_MASK 0x0000007FL +#define ACSAD2_SI_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 status data register 2. +// +//**************************************************************************** +#define ACSDA2_SD_MASK 0x0000FFFFL +#define ACSDA2_SD_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap control register. +// +//**************************************************************************** +#define IOTCR_ITD 0x00000001L +#define IOTCR_HRV 0x00000002L +#define IOTCR_SRV 0x00000004L +#define IOTCR_DTI 0x00000008L +#define IOTCR_DFI 0x00000010L +#define IOTCR_DDP 0x00000020L +#define IOTCR_JTE 0x00000040L +#define IOTCR_PPE 0x00000080L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap address and control +// registers for Hardware Master Volume. +// +//**************************************************************************** +#define IOTGP_SA_MASK 0x0000FFFFL +#define IOTGP_MSK_MASK 0x000F0000L +#define IOTGP_IODC_MASK 0x06000000L +#define IOTGP_IODC_16_BIT 0x00000000L +#define IOTGP_IODC_10_BIT 0x02000000L +#define IOTGP_IODC_12_BIT 0x04000000L +#define IOTGP_WSPI 0x08000000L +#define IOTGP_RSPI 0x10000000L +#define IOTGP_WSE 0x20000000L +#define IOTGP_WE 0x40000000L +#define IOTGP_RE 0x80000000L +#define IOTGP_SA_SHIFT 0L +#define IOTGP_MSK_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap address and control +// registers for Sound Blaster +// +//**************************************************************************** +#define IOTSB_SA_MASK 0x0000FFFFL +#define IOTSB_MSK_MASK 0x000F0000L +#define IOTSB_IODC_MASK 0x06000000L +#define IOTSB_IODC_16_BIT 0x00000000L +#define IOTSB_IODC_10_BIT 0x02000000L +#define IOTSB_IODC_12_BIT 0x04000000L +#define IOTSB_WSPI 0x08000000L +#define IOTSB_RSPI 0x10000000L +#define IOTSB_WSE 0x20000000L +#define IOTSB_WE 0x40000000L +#define IOTSB_RE 0x80000000L +#define IOTSB_SA_SHIFT 0L +#define IOTSB_MSK_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the I/O trap address and control +// registers for FM. +// +//**************************************************************************** +#define IOTFM_SA_MASK 0x0000FFFFL +#define IOTFM_MSK_MASK 0x000F0000L +#define IOTFM_IODC_MASK 0x06000000L +#define IOTFM_IODC_16_BIT 0x00000000L +#define IOTFM_IODC_10_BIT 0x02000000L +#define IOTFM_IODC_12_BIT 0x04000000L +#define IOTFM_WSPI 0x08000000L +#define IOTFM_RSPI 0x10000000L +#define IOTFM_WSE 0x20000000L +#define IOTFM_WE 0x40000000L +#define IOTFM_RE 0x80000000L +#define IOTFM_SA_SHIFT 0L +#define IOTFM_MSK_SHIFT 16L + +//**************************************************************************** +// +// The following defines are for the flags in the PC/PCI request register. +// +//**************************************************************************** +#define PCPRR_RDC_MASK 0x00000007L +#define PCPRR_REQ 0x00008000L +#define PCPRR_RDC_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the PC/PCI grant register. +// +//**************************************************************************** +#define PCPGR_GDC_MASK 0x00000007L +#define PCPGR_VL 0x00008000L +#define PCPGR_GDC_SHIFT 0L + +//**************************************************************************** +// +// The following defines are for the flags in the PC/PCI Control Register. +// +//**************************************************************************** +#define PCPCR_EN 0x00000001L + +//**************************************************************************** +// +// The following defines are for the flags in the debug index register. +// +//**************************************************************************** +#define DREG_REGID_MASK 0x0000007FL +#define DREG_DEBUG 0x00000080L +#define DREG_RGBK_MASK 0x00000700L +#define DREG_TRAP 0x00000800L +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_TRAPX 0x00001000L +#endif +#endif +#define DREG_REGID_SHIFT 0L +#define DREG_RGBK_SHIFT 8L +#define DREG_RGBK_REGID_MASK 0x0000077FL +#define DREG_REGID_R0 0x00000010L +#define DREG_REGID_R1 0x00000011L +#define DREG_REGID_R2 0x00000012L +#define DREG_REGID_R3 0x00000013L +#define DREG_REGID_R4 0x00000014L +#define DREG_REGID_R5 0x00000015L +#define DREG_REGID_R6 0x00000016L +#define DREG_REGID_R7 0x00000017L +#define DREG_REGID_R8 0x00000018L +#define DREG_REGID_R9 0x00000019L +#define DREG_REGID_RA 0x0000001AL +#define DREG_REGID_RB 0x0000001BL +#define DREG_REGID_RC 0x0000001CL +#define DREG_REGID_RD 0x0000001DL +#define DREG_REGID_RE 0x0000001EL +#define DREG_REGID_RF 0x0000001FL +#define DREG_REGID_RA_BUS_LOW 0x00000020L +#define DREG_REGID_RA_BUS_HIGH 0x00000038L +#define DREG_REGID_YBUS_LOW 0x00000050L +#define DREG_REGID_YBUS_HIGH 0x00000058L +#define DREG_REGID_TRAP_0 0x00000100L +#define DREG_REGID_TRAP_1 0x00000101L +#define DREG_REGID_TRAP_2 0x00000102L +#define DREG_REGID_TRAP_3 0x00000103L +#define DREG_REGID_TRAP_4 0x00000104L +#define DREG_REGID_TRAP_5 0x00000105L +#define DREG_REGID_TRAP_6 0x00000106L +#define DREG_REGID_TRAP_7 0x00000107L +#define DREG_REGID_INDIRECT_ADDRESS 0x0000010EL +#define DREG_REGID_TOP_OF_STACK 0x0000010FL +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_8 0x00000110L +#define DREG_REGID_TRAP_9 0x00000111L +#define DREG_REGID_TRAP_10 0x00000112L +#define DREG_REGID_TRAP_11 0x00000113L +#define DREG_REGID_TRAP_12 0x00000114L +#define DREG_REGID_TRAP_13 0x00000115L +#define DREG_REGID_TRAP_14 0x00000116L +#define DREG_REGID_TRAP_15 0x00000117L +#define DREG_REGID_TRAP_16 0x00000118L +#define DREG_REGID_TRAP_17 0x00000119L +#define DREG_REGID_TRAP_18 0x0000011AL +#define DREG_REGID_TRAP_19 0x0000011BL +#define DREG_REGID_TRAP_20 0x0000011CL +#define DREG_REGID_TRAP_21 0x0000011DL +#define DREG_REGID_TRAP_22 0x0000011EL +#define DREG_REGID_TRAP_23 0x0000011FL +#endif +#endif +#define DREG_REGID_RSA0_LOW 0x00000200L +#define DREG_REGID_RSA0_HIGH 0x00000201L +#define DREG_REGID_RSA1_LOW 0x00000202L +#define DREG_REGID_RSA1_HIGH 0x00000203L +#define DREG_REGID_RSA2 0x00000204L +#define DREG_REGID_RSA3 0x00000205L +#define DREG_REGID_RSI0_LOW 0x00000206L +#define DREG_REGID_RSI0_HIGH 0x00000207L +#define DREG_REGID_RSI1 0x00000208L +#define DREG_REGID_RSI2 0x00000209L +#define DREG_REGID_SAGUSTATUS 0x0000020AL +#define DREG_REGID_RSCONFIG01_LOW 0x0000020BL +#define DREG_REGID_RSCONFIG01_HIGH 0x0000020CL +#define DREG_REGID_RSCONFIG23_LOW 0x0000020DL +#define DREG_REGID_RSCONFIG23_HIGH 0x0000020EL +#define DREG_REGID_RSDMA01E 0x0000020FL +#define DREG_REGID_RSDMA23E 0x00000210L +#define DREG_REGID_RSD0_LOW 0x00000211L +#define DREG_REGID_RSD0_HIGH 0x00000212L +#define DREG_REGID_RSD1_LOW 0x00000213L +#define DREG_REGID_RSD1_HIGH 0x00000214L +#define DREG_REGID_RSD2_LOW 0x00000215L +#define DREG_REGID_RSD2_HIGH 0x00000216L +#define DREG_REGID_RSD3_LOW 0x00000217L +#define DREG_REGID_RSD3_HIGH 0x00000218L +#define DREG_REGID_SRAR_HIGH 0x0000021AL +#define DREG_REGID_SRAR_LOW 0x0000021BL +#define DREG_REGID_DMA_STATE 0x0000021CL +#define DREG_REGID_CURRENT_DMA_STREAM 0x0000021DL +#define DREG_REGID_NEXT_DMA_STREAM 0x0000021EL +#define DREG_REGID_CPU_STATUS 0x00000300L +#define DREG_REGID_MAC_MODE 0x00000301L +#define DREG_REGID_STACK_AND_REPEAT 0x00000302L +#define DREG_REGID_INDEX0 0x00000304L +#define DREG_REGID_INDEX1 0x00000305L +#define DREG_REGID_DMA_STATE_0_3 0x00000400L +#define DREG_REGID_DMA_STATE_4_7 0x00000404L +#define DREG_REGID_DMA_STATE_8_11 0x00000408L +#define DREG_REGID_DMA_STATE_12_15 0x0000040CL +#define DREG_REGID_DMA_STATE_16_19 0x00000410L +#define DREG_REGID_DMA_STATE_20_23 0x00000414L +#define DREG_REGID_DMA_STATE_24_27 0x00000418L +#define DREG_REGID_DMA_STATE_28_31 0x0000041CL +#define DREG_REGID_DMA_STATE_32_35 0x00000420L +#define DREG_REGID_DMA_STATE_36_39 0x00000424L +#define DREG_REGID_DMA_STATE_40_43 0x00000428L +#define DREG_REGID_DMA_STATE_44_47 0x0000042CL +#define DREG_REGID_DMA_STATE_48_51 0x00000430L +#define DREG_REGID_DMA_STATE_52_55 0x00000434L +#define DREG_REGID_DMA_STATE_56_59 0x00000438L +#define DREG_REGID_DMA_STATE_60_63 0x0000043CL +#define DREG_REGID_DMA_STATE_64_67 0x00000440L +#define DREG_REGID_DMA_STATE_68_71 0x00000444L +#define DREG_REGID_DMA_STATE_72_75 0x00000448L +#define DREG_REGID_DMA_STATE_76_79 0x0000044CL +#define DREG_REGID_DMA_STATE_80_83 0x00000450L +#define DREG_REGID_DMA_STATE_84_87 0x00000454L +#define DREG_REGID_DMA_STATE_88_91 0x00000458L +#define DREG_REGID_DMA_STATE_92_95 0x0000045CL +#define DREG_REGID_TRAP_SELECT 0x00000500L +#define DREG_REGID_TRAP_WRITE_0 0x00000500L +#define DREG_REGID_TRAP_WRITE_1 0x00000501L +#define DREG_REGID_TRAP_WRITE_2 0x00000502L +#define DREG_REGID_TRAP_WRITE_3 0x00000503L +#define DREG_REGID_TRAP_WRITE_4 0x00000504L +#define DREG_REGID_TRAP_WRITE_5 0x00000505L +#define DREG_REGID_TRAP_WRITE_6 0x00000506L +#define DREG_REGID_TRAP_WRITE_7 0x00000507L +#if !defined(NO_CS4612) +#if !defined(NO_CS4615) +#define DREG_REGID_TRAP_WRITE_8 0x00000510L +#define DREG_REGID_TRAP_WRITE_9 0x00000511L +#define DREG_REGID_TRAP_WRITE_10 0x00000512L +#define DREG_REGID_TRAP_WRITE_11 0x00000513L +#define DREG_REGID_TRAP_WRITE_12 0x00000514L +#define DREG_REGID_TRAP_WRITE_13 0x00000515L +#define DREG_REGID_TRAP_WRITE_14 0x00000516L +#define DREG_REGID_TRAP_WRITE_15 0x00000517L +#define DREG_REGID_TRAP_WRITE_16 0x00000518L +#define DREG_REGID_TRAP_WRITE_17 0x00000519L +#define DREG_REGID_TRAP_WRITE_18 0x0000051AL +#define DREG_REGID_TRAP_WRITE_19 0x0000051BL +#define DREG_REGID_TRAP_WRITE_20 0x0000051CL +#define DREG_REGID_TRAP_WRITE_21 0x0000051DL +#define DREG_REGID_TRAP_WRITE_22 0x0000051EL +#define DREG_REGID_TRAP_WRITE_23 0x0000051FL +#endif +#endif +#define DREG_REGID_MAC0_ACC0_LOW 0x00000600L +#define DREG_REGID_MAC0_ACC1_LOW 0x00000601L +#define DREG_REGID_MAC0_ACC2_LOW 0x00000602L +#define DREG_REGID_MAC0_ACC3_LOW 0x00000603L +#define DREG_REGID_MAC1_ACC0_LOW 0x00000604L +#define DREG_REGID_MAC1_ACC1_LOW 0x00000605L +#define DREG_REGID_MAC1_ACC2_LOW 0x00000606L +#define DREG_REGID_MAC1_ACC3_LOW 0x00000607L +#define DREG_REGID_MAC0_ACC0_MID 0x00000608L +#define DREG_REGID_MAC0_ACC1_MID 0x00000609L +#define DREG_REGID_MAC0_ACC2_MID 0x0000060AL +#define DREG_REGID_MAC0_ACC3_MID 0x0000060BL +#define DREG_REGID_MAC1_ACC0_MID 0x0000060CL +#define DREG_REGID_MAC1_ACC1_MID 0x0000060DL +#define DREG_REGID_MAC1_ACC2_MID 0x0000060EL +#define DREG_REGID_MAC1_ACC3_MID 0x0000060FL +#define DREG_REGID_MAC0_ACC0_HIGH 0x00000610L +#define DREG_REGID_MAC0_ACC1_HIGH 0x00000611L +#define DREG_REGID_MAC0_ACC2_HIGH 0x00000612L +#define DREG_REGID_MAC0_ACC3_HIGH 0x00000613L +#define DREG_REGID_MAC1_ACC0_HIGH 0x00000614L +#define DREG_REGID_MAC1_ACC1_HIGH 0x00000615L +#define DREG_REGID_MAC1_ACC2_HIGH 0x00000616L +#define DREG_REGID_MAC1_ACC3_HIGH 0x00000617L +#define DREG_REGID_RSHOUT_LOW 0x00000620L +#define DREG_REGID_RSHOUT_MID 0x00000628L +#define DREG_REGID_RSHOUT_HIGH 0x00000630L + +//**************************************************************************** +// +// The following defines are for the flags in the AC97 S/PDIF Control register. +// +//**************************************************************************** +#define SPDIF_CONTROL_SPDIF_EN 0x00008000L +#define SPDIF_CONTROL_VAL 0x00004000L +#define SPDIF_CONTROL_COPY 0x00000004L +#define SPDIF_CONTROL_CC0 0x00000010L +#define SPDIF_CONTROL_CC1 0x00000020L +#define SPDIF_CONTROL_CC2 0x00000040L +#define SPDIF_CONTROL_CC3 0x00000080L +#define SPDIF_CONTROL_CC4 0x00000100L +#define SPDIF_CONTROL_CC5 0x00000200L +#define SPDIF_CONTROL_CC6 0x00000400L +#define SPDIF_CONTROL_L 0x00000800L + +#endif // _H_HWDEFS diff --git a/sound/oss/cs4281/cs4281_wrapper-24.c b/sound/oss/cs4281/cs4281_wrapper-24.c new file mode 100644 index 000000000000..4559f02c9969 --- /dev/null +++ b/sound/oss/cs4281/cs4281_wrapper-24.c @@ -0,0 +1,41 @@ +/******************************************************************************* +* +* "cs4281_wrapper.c" -- Cirrus Logic-Crystal CS4281 linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- tom woller (twoller@crystal.cirrus.com) or +* (audio@crystal.cirrus.com). +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* 12/20/00 trw - new file. +* +*******************************************************************************/ + +#include <linux/spinlock.h> + +static int cs4281_resume_null(struct pci_dev *pcidev) { return 0; } +static int cs4281_suspend_null(struct pci_dev *pcidev, pm_message_t state) { return 0; } + +#define free_dmabuf(state, dmabuf) \ + pci_free_consistent(state->pcidev, \ + PAGE_SIZE << (dmabuf)->buforder, \ + (dmabuf)->rawbuf, (dmabuf)->dmaaddr); +#define free_dmabuf2(state, dmabuf) \ + pci_free_consistent((state)->pcidev, \ + PAGE_SIZE << (state)->buforder_tmpbuff, \ + (state)->tmpbuff, (state)->dmaaddr_tmpbuff); +#define cs4x_pgoff(vma) ((vma)->vm_pgoff) + diff --git a/sound/oss/cs4281/cs4281m.c b/sound/oss/cs4281/cs4281m.c new file mode 100644 index 000000000000..d0d3963e1b83 --- /dev/null +++ b/sound/oss/cs4281/cs4281m.c @@ -0,0 +1,4505 @@ +/******************************************************************************* +* +* "cs4281.c" -- Cirrus Logic-Crystal CS4281 linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- adapted from drivers by Thomas Sailer, +* -- but don't bug him; Problems should go to: +* -- tom woller (twoller@crystal.cirrus.com) or +* (audio@crystal.cirrus.com). +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* Module command line parameters: +* none +* +* Supported devices: +* /dev/dsp standard /dev/dsp device, (mostly) OSS compatible +* /dev/mixer standard /dev/mixer device, (mostly) OSS compatible +* /dev/midi simple MIDI UART interface, no ioctl +* +* Modification History +* 08/20/00 trw - silence and no stopping DAC until release +* 08/23/00 trw - added CS_DBG statements, fix interrupt hang issue on DAC stop. +* 09/18/00 trw - added 16bit only record with conversion +* 09/24/00 trw - added Enhanced Full duplex (separate simultaneous +* capture/playback rates) +* 10/03/00 trw - fixed mmap (fixed GRECORD and the XMMS mmap test plugin +* libOSSm.so) +* 10/11/00 trw - modified for 2.4.0-test9 kernel enhancements (NR_MAP removal) +* 11/03/00 trw - fixed interrupt loss/stutter, added debug. +* 11/10/00 bkz - added __devinit to cs4281_hw_init() +* 11/10/00 trw - fixed SMP and capture spinlock hang. +* 12/04/00 trw - cleaned up CSDEBUG flags and added "defaultorder" moduleparm. +* 12/05/00 trw - fixed polling (myth2), and added underrun swptr fix. +* 12/08/00 trw - added PM support. +* 12/14/00 trw - added wrapper code, builds under 2.4.0, 2.2.17-20, 2.2.17-8 +* (RH/Dell base), 2.2.18, 2.2.12. cleaned up code mods by ident. +* 12/19/00 trw - added PM support for 2.2 base (apm_callback). other PM cleanup. +* 12/21/00 trw - added fractional "defaultorder" inputs. if >100 then use +* defaultorder-100 as power of 2 for the buffer size. example: +* 106 = 2^(106-100) = 2^6 = 64 bytes for the buffer size. +* +*******************************************************************************/ + +/* uncomment the following line to disable building PM support into the driver */ +//#define NOT_CS4281_PM 1 + +#include <linux/list.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/sound.h> +#include <linux/slab.h> +#include <linux/soundcard.h> +#include <linux/pci.h> +#include <linux/bitops.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/poll.h> +#include <linux/fs.h> +#include <linux/wait.h> + +#include <asm/current.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <asm/page.h> +#include <asm/uaccess.h> + +//#include "cs_dm.h" +#include "cs4281_hwdefs.h" +#include "cs4281pm.h" + +struct cs4281_state; + +static void stop_dac(struct cs4281_state *s); +static void stop_adc(struct cs4281_state *s); +static void start_dac(struct cs4281_state *s); +static void start_adc(struct cs4281_state *s); +#undef OSS_DOCUMENTED_MIXER_SEMANTICS + +// --------------------------------------------------------------------- + +#ifndef PCI_VENDOR_ID_CIRRUS +#define PCI_VENDOR_ID_CIRRUS 0x1013 +#endif +#ifndef PCI_DEVICE_ID_CRYSTAL_CS4281 +#define PCI_DEVICE_ID_CRYSTAL_CS4281 0x6005 +#endif + +#define CS4281_MAGIC ((PCI_DEVICE_ID_CRYSTAL_CS4281<<16) | PCI_VENDOR_ID_CIRRUS) +#define CS4281_CFLR_DEFAULT 0x00000001 /* CFLR must be in AC97 link mode */ + +// buffer order determines the size of the dma buffer for the driver. +// under Linux, a smaller buffer allows more responsiveness from many of the +// applications (e.g. games). A larger buffer allows some of the apps (esound) +// to not underrun the dma buffer as easily. As default, use 32k (order=3) +// rather than 64k as some of the games work more responsively. +// log base 2( buff sz = 32k). +static unsigned long defaultorder = 3; +module_param(defaultorder, ulong, 0); + +// +// Turn on/off debugging compilation by commenting out "#define CSDEBUG" +// +#define CSDEBUG 1 +#if CSDEBUG +#define CSDEBUG_INTERFACE 1 +#else +#undef CSDEBUG_INTERFACE +#endif +// +// cs_debugmask areas +// +#define CS_INIT 0x00000001 // initialization and probe functions +#define CS_ERROR 0x00000002 // tmp debugging bit placeholder +#define CS_INTERRUPT 0x00000004 // interrupt handler (separate from all other) +#define CS_FUNCTION 0x00000008 // enter/leave functions +#define CS_WAVE_WRITE 0x00000010 // write information for wave +#define CS_WAVE_READ 0x00000020 // read information for wave +#define CS_MIDI_WRITE 0x00000040 // write information for midi +#define CS_MIDI_READ 0x00000080 // read information for midi +#define CS_MPU401_WRITE 0x00000100 // write information for mpu401 +#define CS_MPU401_READ 0x00000200 // read information for mpu401 +#define CS_OPEN 0x00000400 // all open functions in the driver +#define CS_RELEASE 0x00000800 // all release functions in the driver +#define CS_PARMS 0x00001000 // functional and operational parameters +#define CS_IOCTL 0x00002000 // ioctl (non-mixer) +#define CS_PM 0x00004000 // power management +#define CS_TMP 0x10000000 // tmp debug mask bit + +#define CS_IOCTL_CMD_SUSPEND 0x1 // suspend +#define CS_IOCTL_CMD_RESUME 0x2 // resume +// +// CSDEBUG is usual mode is set to 1, then use the +// cs_debuglevel and cs_debugmask to turn on or off debugging. +// Debug level of 1 has been defined to be kernel errors and info +// that should be printed on any released driver. +// +#if CSDEBUG +#define CS_DBGOUT(mask,level,x) if((cs_debuglevel >= (level)) && ((mask) & cs_debugmask) ) {x;} +#else +#define CS_DBGOUT(mask,level,x) +#endif + +#if CSDEBUG +static unsigned long cs_debuglevel = 1; // levels range from 1-9 +static unsigned long cs_debugmask = CS_INIT | CS_ERROR; // use CS_DBGOUT with various mask values +module_param(cs_debuglevel, ulong, 0); +module_param(cs_debugmask, ulong, 0); +#endif +#define CS_TRUE 1 +#define CS_FALSE 0 + +// MIDI buffer sizes +#define MIDIINBUF 500 +#define MIDIOUTBUF 500 + +#define FMODE_MIDI_SHIFT 3 +#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT) +#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT) + +#define CS4281_MAJOR_VERSION 1 +#define CS4281_MINOR_VERSION 13 +#ifdef __ia64__ +#define CS4281_ARCH 64 //architecture key +#else +#define CS4281_ARCH 32 //architecture key +#endif + +#define CS_TYPE_ADC 0 +#define CS_TYPE_DAC 1 + + +static const char invalid_magic[] = + KERN_CRIT "cs4281: invalid magic value\n"; + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != CS4281_MAGIC) { \ + printk(invalid_magic); \ + return -ENXIO; \ + } \ +}) + +//LIST_HEAD(cs4281_devs); +static struct list_head cs4281_devs = { &cs4281_devs, &cs4281_devs }; + +struct cs4281_state; + +#include "cs4281_wrapper-24.c" + +struct cs4281_state { + // magic + unsigned int magic; + + // we keep the cards in a linked list + struct cs4281_state *next; + + // pcidev is needed to turn off the DDMA controller at driver shutdown + struct pci_dev *pcidev; + struct list_head list; + + // soundcore stuff + int dev_audio; + int dev_mixer; + int dev_midi; + + // hardware resources + unsigned int pBA0phys, pBA1phys; + char __iomem *pBA0; + char __iomem *pBA1; + unsigned int irq; + + // mixer registers + struct { + unsigned short vol[10]; + unsigned int recsrc; + unsigned int modcnt; + unsigned short micpreamp; + } mix; + + // wave stuff + struct properties { + unsigned fmt; + unsigned fmt_original; // original requested format + unsigned channels; + unsigned rate; + unsigned char clkdiv; + } prop_dac, prop_adc; + unsigned conversion:1; // conversion from 16 to 8 bit in progress + void *tmpbuff; // tmp buffer for sample conversions + unsigned ena; + spinlock_t lock; + struct semaphore open_sem; + struct semaphore open_sem_adc; + struct semaphore open_sem_dac; + mode_t open_mode; + wait_queue_head_t open_wait; + wait_queue_head_t open_wait_adc; + wait_queue_head_t open_wait_dac; + + dma_addr_t dmaaddr_tmpbuff; + unsigned buforder_tmpbuff; // Log base 2 of 'rawbuf' size in bytes.. + struct dmabuf { + void *rawbuf; // Physical address of + dma_addr_t dmaaddr; + unsigned buforder; // Log base 2 of 'rawbuf' size in bytes.. + unsigned numfrag; // # of 'fragments' in the buffer. + unsigned fragshift; // Log base 2 of fragment size. + unsigned hwptr, swptr; + unsigned total_bytes; // # bytes process since open. + unsigned blocks; // last returned blocks value GETOPTR + unsigned wakeup; // interrupt occurred on block + int count; + unsigned underrun; // underrun flag + unsigned error; // over/underrun + wait_queue_head_t wait; + // redundant, but makes calculations easier + unsigned fragsize; // 2**fragshift.. + unsigned dmasize; // 2**buforder. + unsigned fragsamples; + // OSS stuff + unsigned mapped:1; // Buffer mapped in cs4281_mmap()? + unsigned ready:1; // prog_dmabuf_dac()/adc() successful? + unsigned endcleared:1; + unsigned type:1; // adc or dac buffer (CS_TYPE_XXX) + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac, dma_adc; + + // midi stuff + struct { + unsigned ird, iwr, icnt; + unsigned ord, owr, ocnt; + wait_queue_head_t iwait; + wait_queue_head_t owait; + struct timer_list timer; + unsigned char ibuf[MIDIINBUF]; + unsigned char obuf[MIDIOUTBUF]; + } midi; + + struct cs4281_pm pm; + struct cs4281_pipeline pl[CS4281_NUMBER_OF_PIPELINES]; +}; + +#include "cs4281pm-24.c" + +#if CSDEBUG + +// DEBUG ROUTINES + +#define SOUND_MIXER_CS_GETDBGLEVEL _SIOWR('M',120, int) +#define SOUND_MIXER_CS_SETDBGLEVEL _SIOWR('M',121, int) +#define SOUND_MIXER_CS_GETDBGMASK _SIOWR('M',122, int) +#define SOUND_MIXER_CS_SETDBGMASK _SIOWR('M',123, int) + +#define SOUND_MIXER_CS_APM _SIOWR('M',124, int) + + +static void cs_printioctl(unsigned int x) +{ + unsigned int i; + unsigned char vidx; + // Index of mixtable1[] member is Device ID + // and must be <= SOUND_MIXER_NRDEVICES. + // Value of array member is index into s->mix.vol[] + static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_PCM] = 1, // voice + [SOUND_MIXER_LINE1] = 2, // AUX + [SOUND_MIXER_CD] = 3, // CD + [SOUND_MIXER_LINE] = 4, // Line + [SOUND_MIXER_SYNTH] = 5, // FM + [SOUND_MIXER_MIC] = 6, // Mic + [SOUND_MIXER_SPEAKER] = 7, // Speaker + [SOUND_MIXER_RECLEV] = 8, // Recording level + [SOUND_MIXER_VOLUME] = 9 // Master Volume + }; + + switch (x) { + case SOUND_MIXER_CS_GETDBGMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_GETDBGMASK:\n")); + break; + case SOUND_MIXER_CS_GETDBGLEVEL: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_GETDBGLEVEL:\n")); + break; + case SOUND_MIXER_CS_SETDBGMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_SETDBGMASK:\n")); + break; + case SOUND_MIXER_CS_SETDBGLEVEL: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_CS_SETDBGLEVEL:\n")); + break; + case OSS_GETVERSION: + CS_DBGOUT(CS_IOCTL, 4, printk("OSS_GETVERSION:\n")); + break; + case SNDCTL_DSP_SYNC: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SYNC:\n")); + break; + case SNDCTL_DSP_SETDUPLEX: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETDUPLEX:\n")); + break; + case SNDCTL_DSP_GETCAPS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETCAPS:\n")); + break; + case SNDCTL_DSP_RESET: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_RESET:\n")); + break; + case SNDCTL_DSP_SPEED: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SPEED:\n")); + break; + case SNDCTL_DSP_STEREO: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_STEREO:\n")); + break; + case SNDCTL_DSP_CHANNELS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_CHANNELS:\n")); + break; + case SNDCTL_DSP_GETFMTS: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETFMTS:\n")); + break; + case SNDCTL_DSP_SETFMT: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETFMT:\n")); + break; + case SNDCTL_DSP_POST: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_POST:\n")); + break; + case SNDCTL_DSP_GETTRIGGER: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETTRIGGER:\n")); + break; + case SNDCTL_DSP_SETTRIGGER: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETTRIGGER:\n")); + break; + case SNDCTL_DSP_GETOSPACE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOSPACE:\n")); + break; + case SNDCTL_DSP_GETISPACE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETISPACE:\n")); + break; + case SNDCTL_DSP_NONBLOCK: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_NONBLOCK:\n")); + break; + case SNDCTL_DSP_GETODELAY: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETODELAY:\n")); + break; + case SNDCTL_DSP_GETIPTR: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETIPTR:\n")); + break; + case SNDCTL_DSP_GETOPTR: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETOPTR:\n")); + break; + case SNDCTL_DSP_GETBLKSIZE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_GETBLKSIZE:\n")); + break; + case SNDCTL_DSP_SETFRAGMENT: + CS_DBGOUT(CS_IOCTL, 4, + printk("SNDCTL_DSP_SETFRAGMENT:\n")); + break; + case SNDCTL_DSP_SUBDIVIDE: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SUBDIVIDE:\n")); + break; + case SOUND_PCM_READ_RATE: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_RATE:\n")); + break; + case SOUND_PCM_READ_CHANNELS: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_PCM_READ_CHANNELS:\n")); + break; + case SOUND_PCM_READ_BITS: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_BITS:\n")); + break; + case SOUND_PCM_WRITE_FILTER: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_PCM_WRITE_FILTER:\n")); + break; + case SNDCTL_DSP_SETSYNCRO: + CS_DBGOUT(CS_IOCTL, 4, printk("SNDCTL_DSP_SETSYNCRO:\n")); + break; + case SOUND_PCM_READ_FILTER: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_FILTER:\n")); + break; + case SOUND_MIXER_PRIVATE1: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE1:\n")); + break; + case SOUND_MIXER_PRIVATE2: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE2:\n")); + break; + case SOUND_MIXER_PRIVATE3: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE3:\n")); + break; + case SOUND_MIXER_PRIVATE4: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE4:\n")); + break; + case SOUND_MIXER_PRIVATE5: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE5:\n")); + break; + case SOUND_MIXER_INFO: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_INFO:\n")); + break; + case SOUND_OLD_MIXER_INFO: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_OLD_MIXER_INFO:\n")); + break; + + default: + switch (_IOC_NR(x)) { + case SOUND_MIXER_VOLUME: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_VOLUME:\n")); + break; + case SOUND_MIXER_SPEAKER: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_SPEAKER:\n")); + break; + case SOUND_MIXER_RECLEV: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_RECLEV:\n")); + break; + case SOUND_MIXER_MIC: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_MIC:\n")); + break; + case SOUND_MIXER_SYNTH: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_SYNTH:\n")); + break; + case SOUND_MIXER_RECSRC: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_RECSRC:\n")); + break; + case SOUND_MIXER_DEVMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_DEVMASK:\n")); + break; + case SOUND_MIXER_RECMASK: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_RECMASK:\n")); + break; + case SOUND_MIXER_STEREODEVS: + CS_DBGOUT(CS_IOCTL, 4, + printk("SOUND_MIXER_STEREODEVS:\n")); + break; + case SOUND_MIXER_CAPS: + CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CAPS:\n")); + break; + default: + i = _IOC_NR(x); + if (i >= SOUND_MIXER_NRDEVICES + || !(vidx = mixtable1[i])) { + CS_DBGOUT(CS_IOCTL, 4, printk + ("UNKNOWN IOCTL: 0x%.8x NR=%d\n", + x, i)); + } else { + CS_DBGOUT(CS_IOCTL, 4, printk + ("SOUND_MIXER_IOCTL AC9x: 0x%.8x NR=%d\n", + x, i)); + } + break; + } + } +} +#endif +static int prog_dmabuf_adc(struct cs4281_state *s); +static void prog_codec(struct cs4281_state *s, unsigned type); + +// --------------------------------------------------------------------- +// +// Hardware Interfaces For the CS4281 +// + + +//****************************************************************************** +// "delayus()-- Delay for the specified # of microseconds. +//****************************************************************************** +static void delayus(struct cs4281_state *s, u32 delay) +{ + u32 j; + if ((delay > 9999) && (s->pm.flags & CS4281_PM_IDLE)) { + j = (delay * HZ) / 1000000; /* calculate delay in jiffies */ + if (j < 1) + j = 1; /* minimum one jiffy. */ + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(j); + } else + udelay(delay); + return; +} + + +//****************************************************************************** +// "cs4281_read_ac97" -- Reads a word from the specified location in the +// CS4281's address space(based on the BA0 register). +// +// 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address +// 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 register, +// 0h for reads. +// 3. Write ACCTL = Control Register = 460h for initiating the write +// 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h +// 5. if DCV not cleared, break and return error +// 6. Read ACSTS = Status Register = 464h, check VSTS bit +//**************************************************************************** +static int cs4281_read_ac97(struct cs4281_state *card, u32 offset, + u32 * value) +{ + u32 count, status; + + // Make sure that there is not data sitting + // around from a previous uncompleted access. + // ACSDA = Status Data Register = 47Ch + status = readl(card->pBA0 + BA0_ACSDA); + + // Setup the AC97 control registers on the CS4281 to send the + // appropriate command to the AC97 to perform the read. + // ACCAD = Command Address Register = 46Ch + // ACCDA = Command Data Register = 470h + // ACCTL = Control Register = 460h + // bit DCV - will clear when process completed + // bit CRW - Read command + // bit VFRM - valid frame enabled + // bit ESYN - ASYNC generation enabled + + // Get the actual AC97 register from the offset + writel(offset - BA0_AC97_RESET, card->pBA0 + BA0_ACCAD); + writel(0, card->pBA0 + BA0_ACCDA); + writel(ACCTL_DCV | ACCTL_CRW | ACCTL_VFRM | ACCTL_ESYN, + card->pBA0 + BA0_ACCTL); + + // Wait for the read to occur. + for (count = 0; count < 10; count++) { + // First, we want to wait for a short time. + udelay(25); + + // Now, check to see if the read has completed. + // ACCTL = 460h, DCV should be reset by now and 460h = 17h + if (!(readl(card->pBA0 + BA0_ACCTL) & ACCTL_DCV)) + break; + } + + // Make sure the read completed. + if (readl(card->pBA0 + BA0_ACCTL) & ACCTL_DCV) + return 1; + + // Wait for the valid status bit to go active. + for (count = 0; count < 10; count++) { + // Read the AC97 status register. + // ACSTS = Status Register = 464h + status = readl(card->pBA0 + BA0_ACSTS); + + // See if we have valid status. + // VSTS - Valid Status + if (status & ACSTS_VSTS) + break; + // Wait for a short while. + udelay(25); + } + + // Make sure we got valid status. + if (!(status & ACSTS_VSTS)) + return 1; + + // Read the data returned from the AC97 register. + // ACSDA = Status Data Register = 474h + *value = readl(card->pBA0 + BA0_ACSDA); + + // Success. + return (0); +} + + +//**************************************************************************** +// +// "cs4281_write_ac97()"-- writes a word to the specified location in the +// CS461x's address space (based on the part's base address zero register). +// +// 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address +// 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 reg. +// 3. Write ACCTL = Control Register = 460h for initiating the write +// 4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h +// 5. if DCV not cleared, break and return error +// +//**************************************************************************** +static int cs4281_write_ac97(struct cs4281_state *card, u32 offset, + u32 value) +{ + u32 count, status=0; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs_4281_write_ac97()+ \n")); + + // Setup the AC97 control registers on the CS4281 to send the + // appropriate command to the AC97 to perform the read. + // ACCAD = Command Address Register = 46Ch + // ACCDA = Command Data Register = 470h + // ACCTL = Control Register = 460h + // set DCV - will clear when process completed + // reset CRW - Write command + // set VFRM - valid frame enabled + // set ESYN - ASYNC generation enabled + // set RSTN - ARST# inactive, AC97 codec not reset + + // Get the actual AC97 register from the offset + + writel(offset - BA0_AC97_RESET, card->pBA0 + BA0_ACCAD); + writel(value, card->pBA0 + BA0_ACCDA); + writel(ACCTL_DCV | ACCTL_VFRM | ACCTL_ESYN, + card->pBA0 + BA0_ACCTL); + + // Wait for the write to occur. + for (count = 0; count < 100; count++) { + // First, we want to wait for a short time. + udelay(25); + // Now, check to see if the write has completed. + // ACCTL = 460h, DCV should be reset by now and 460h = 07h + status = readl(card->pBA0 + BA0_ACCTL); + if (!(status & ACCTL_DCV)) + break; + } + + // Make sure the write completed. + if (status & ACCTL_DCV) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs4281: cs_4281_write_ac97()- unable to write. ACCTL_DCV active\n")); + return 1; + } + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs_4281_write_ac97()- 0\n")); + // Success. + return 0; +} + + +//****************************************************************************** +// "Init4281()" -- Bring up the part. +//****************************************************************************** +static __devinit int cs4281_hw_init(struct cs4281_state *card) +{ + u32 ac97_slotid; + u32 temp1, temp2; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs4281_hw_init()+ \n")); +#ifndef NOT_CS4281_PM + if(!card) + return 1; +#endif + temp2 = readl(card->pBA0 + BA0_CFLR); + CS_DBGOUT(CS_INIT | CS_ERROR | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_hw_init() CFLR 0x%x\n", temp2)); + if(temp2 != CS4281_CFLR_DEFAULT) + { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO + "cs4281: cs4281_hw_init() CFLR invalid - resetting from 0x%x to 0x%x\n", + temp2,CS4281_CFLR_DEFAULT)); + writel(CS4281_CFLR_DEFAULT, card->pBA0 + BA0_CFLR); + temp2 = readl(card->pBA0 + BA0_CFLR); + if(temp2 != CS4281_CFLR_DEFAULT) + { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO + "cs4281: cs4281_hw_init() Invalid hardware - unable to configure CFLR\n")); + return 1; + } + } + + //***************************************7 + // Set up the Sound System Configuration + //*************************************** + + // Set the 'Configuration Write Protect' register + // to 4281h. Allows vendor-defined configuration + // space between 0e4h and 0ffh to be written. + + writel(0x4281, card->pBA0 + BA0_CWPR); // (3e0h) + + // (0), Blast the clock control register to zero so that the + // PLL starts out in a known state, and blast the master serial + // port control register to zero so that the serial ports also + // start out in a known state. + + writel(0, card->pBA0 + BA0_CLKCR1); // (400h) + writel(0, card->pBA0 + BA0_SERMC); // (420h) + + + // (1), Make ESYN go to zero to turn off + // the Sync pulse on the AC97 link. + + writel(0, card->pBA0 + BA0_ACCTL); + udelay(50); + + + // (2) Drive the ARST# pin low for a minimum of 1uS (as defined in + // the AC97 spec) and then drive it high. This is done for non + // AC97 modes since there might be logic external to the CS461x + // that uses the ARST# line for a reset. + + writel(0, card->pBA0 + BA0_SPMC); // (3ech) + udelay(100); + writel(SPMC_RSTN, card->pBA0 + BA0_SPMC); + delayus(card,50000); // Wait 50 ms for ABITCLK to become stable. + + // (3) Turn on the Sound System Clocks. + writel(CLKCR1_PLLP, card->pBA0 + BA0_CLKCR1); // (400h) + delayus(card,50000); // Wait for the PLL to stabilize. + // Turn on clocking of the core (CLKCR1(400h) = 0x00000030) + writel(CLKCR1_PLLP | CLKCR1_SWCE, card->pBA0 + BA0_CLKCR1); + + // (4) Power on everything for now.. + writel(0x7E, card->pBA0 + BA0_SSPM); // (740h) + + // (5) Wait for clock stabilization. + for (temp1 = 0; temp1 < 1000; temp1++) { + udelay(1000); + if (readl(card->pBA0 + BA0_CLKCR1) & CLKCR1_DLLRDY) + break; + } + if (!(readl(card->pBA0 + BA0_CLKCR1) & CLKCR1_DLLRDY)) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: DLLRDY failed!\n")); + return -EIO; + } + // (6) Enable ASYNC generation. + writel(ACCTL_ESYN, card->pBA0 + BA0_ACCTL); // (460h) + + // Now wait 'for a short while' to allow the AC97 + // part to start generating bit clock. (so we don't + // Try to start the PLL without an input clock.) + delayus(card,50000); + + // Set the serial port timing configuration, so that the + // clock control circuit gets its clock from the right place. + writel(SERMC_PTC_AC97, card->pBA0 + BA0_SERMC); // (420h)=2. + + // (7) Wait for the codec ready signal from the AC97 codec. + + for (temp1 = 0; temp1 < 1000; temp1++) { + // Delay a mil to let things settle out and + // to prevent retrying the read too quickly. + udelay(1000); + if (readl(card->pBA0 + BA0_ACSTS) & ACSTS_CRDY) // If ready, (464h) + break; // exit the 'for' loop. + } + if (!(readl(card->pBA0 + BA0_ACSTS) & ACSTS_CRDY)) // If never came ready, + { + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR + "cs4281: ACSTS never came ready!\n")); + return -EIO; // exit initialization. + } + // (8) Assert the 'valid frame' signal so we can + // begin sending commands to the AC97 codec. + writel(ACCTL_VFRM | ACCTL_ESYN, card->pBA0 + BA0_ACCTL); // (460h) + + // (9), Wait until CODEC calibration is finished. + // Print an error message if it doesn't. + for (temp1 = 0; temp1 < 1000; temp1++) { + delayus(card,10000); + // Read the AC97 Powerdown Control/Status Register. + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp2); + if ((temp2 & 0x0000000F) == 0x0000000F) + break; + } + if ((temp2 & 0x0000000F) != 0x0000000F) { + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR + "cs4281: Codec failed to calibrate. Status = %.8x.\n", + temp2)); + return -EIO; + } + // (10), Set the serial port timing configuration, so that the + // clock control circuit gets its clock from the right place. + writel(SERMC_PTC_AC97, card->pBA0 + BA0_SERMC); // (420h)=2. + + + // (11) Wait until we've sampled input slots 3 & 4 as valid, meaning + // that the codec is pumping ADC data across the AC link. + for (temp1 = 0; temp1 < 1000; temp1++) { + // Delay a mil to let things settle out and + // to prevent retrying the read too quickly. + delayus(card,1000); //(test) + + // Read the input slot valid register; See + // if input slots 3 and 4 are valid yet. + if ( + (readl(card->pBA0 + BA0_ACISV) & + (ACISV_ISV3 | ACISV_ISV4)) == + (ACISV_ISV3 | ACISV_ISV4)) break; // Exit the 'for' if slots are valid. + } + // If we never got valid data, exit initialization. + if ((readl(card->pBA0 + BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) + != (ACISV_ISV3 | ACISV_ISV4)) { + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_ERR + "cs4281: Never got valid data!\n")); + return -EIO; // If no valid data, exit initialization. + } + // (12), Start digital data transfer of audio data to the codec. + writel(ACOSV_SLV3 | ACOSV_SLV4, card->pBA0 + BA0_ACOSV); // (468h) + + + //************************************** + // Unmute the Master and Alternate + // (headphone) volumes. Set to max. + //************************************** + cs4281_write_ac97(card, BA0_AC97_HEADPHONE_VOLUME, 0); + cs4281_write_ac97(card, BA0_AC97_MASTER_VOLUME, 0); + + //****************************************** + // Power on the DAC(AddDACUser()from main()) + //****************************************** + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); + cs4281_write_ac97(card, BA0_AC97_POWERDOWN, temp1 &= 0xfdff); + + // Wait until we sample a DAC ready state. + for (temp2 = 0; temp2 < 32; temp2++) { + // Let's wait a mil to let things settle. + delayus(card,1000); + // Read the current state of the power control reg. + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); + // If the DAC ready state bit is set, stop waiting. + if (temp1 & 0x2) + break; + } + + //****************************************** + // Power on the ADC(AddADCUser()from main()) + //****************************************** + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); + cs4281_write_ac97(card, BA0_AC97_POWERDOWN, temp1 &= 0xfeff); + + // Wait until we sample ADC ready state. + for (temp2 = 0; temp2 < 32; temp2++) { + // Let's wait a mil to let things settle. + delayus(card,1000); + // Read the current state of the power control reg. + cs4281_read_ac97(card, BA0_AC97_POWERDOWN, &temp1); + // If the ADC ready state bit is set, stop waiting. + if (temp1 & 0x1) + break; + } + // Set up 4281 Register contents that + // don't change for boot duration. + + // For playback, we map AC97 slot 3 and 4(Left + // & Right PCM playback) to DMA Channel 0. + // Set the fifo to be 15 bytes at offset zero. + + ac97_slotid = 0x01000f00; // FCR0.RS[4:0]=1(=>slot4, right PCM playback). + // FCR0.LS[4:0]=0(=>slot3, left PCM playback). + // FCR0.SZ[6-0]=15; FCR0.OF[6-0]=0. + writel(ac97_slotid, card->pBA0 + BA0_FCR0); // (180h) + writel(ac97_slotid | FCRn_FEN, card->pBA0 + BA0_FCR0); // Turn on FIFO Enable. + + // For capture, we map AC97 slot 10 and 11(Left + // and Right PCM Record) to DMA Channel 1. + // Set the fifo to be 15 bytes at offset sixteen. + ac97_slotid = 0x0B0A0f10; // FCR1.RS[4:0]=11(=>slot11, right PCM record). + // FCR1.LS[4:0]=10(=>slot10, left PCM record). + // FCR1.SZ[6-0]=15; FCR1.OF[6-0]=16. + writel(ac97_slotid | FCRn_PSH, card->pBA0 + BA0_FCR1); // (184h) + writel(ac97_slotid | FCRn_FEN, card->pBA0 + BA0_FCR1); // Turn on FIFO Enable. + + // Map the Playback SRC to the same AC97 slots(3 & 4-- + // --Playback left & right)as DMA channel 0. + // Map the record SRC to the same AC97 slots(10 & 11-- + // -- Record left & right) as DMA channel 1. + + ac97_slotid = 0x0b0a0100; // SCRSA.PRSS[4:0]=1(=>slot4, right PCM playback). + // SCRSA.PLSS[4:0]=0(=>slot3, left PCM playback). + // SCRSA.CRSS[4:0]=11(=>slot11, right PCM record) + // SCRSA.CLSS[4:0]=10(=>slot10, left PCM record). + writel(ac97_slotid, card->pBA0 + BA0_SRCSA); // (75ch) + + // Set 'Half Terminal Count Interrupt Enable' and 'Terminal + // Count Interrupt Enable' in DMA Control Registers 0 & 1. + // Set 'MSK' flag to 1 to keep the DMA engines paused. + temp1 = (DCRn_HTCIE | DCRn_TCIE | DCRn_MSK); // (00030001h) + writel(temp1, card->pBA0 + BA0_DCR0); // (154h + writel(temp1, card->pBA0 + BA0_DCR1); // (15ch) + + // Set 'Auto-Initialize Control' to 'enabled'; For playback, + // set 'Transfer Type Control'(TR[1:0]) to 'read transfer', + // for record, set Transfer Type Control to 'write transfer'. + // All other bits set to zero; Some will be changed @ transfer start. + temp1 = (DMRn_DMA | DMRn_AUTO | DMRn_TR_READ); // (20000018h) + writel(temp1, card->pBA0 + BA0_DMR0); // (150h) + temp1 = (DMRn_DMA | DMRn_AUTO | DMRn_TR_WRITE); // (20000014h) + writel(temp1, card->pBA0 + BA0_DMR1); // (158h) + + // Enable DMA interrupts generally, and + // DMA0 & DMA1 interrupts specifically. + temp1 = readl(card->pBA0 + BA0_HIMR) & 0xfffbfcff; + writel(temp1, card->pBA0 + BA0_HIMR); + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs4281_hw_init()- 0\n")); + return 0; +} + +#ifndef NOT_CS4281_PM +static void printpm(struct cs4281_state *s) +{ + CS_DBGOUT(CS_PM, 9, printk("pm struct:\n")); + CS_DBGOUT(CS_PM, 9, printk("flags:0x%x u32CLKCR1_SAVE: 0%x u32SSPMValue: 0x%x\n", + (unsigned)s->pm.flags,s->pm.u32CLKCR1_SAVE,s->pm.u32SSPMValue)); + CS_DBGOUT(CS_PM, 9, printk("u32PPLVCvalue: 0x%x u32PPRVCvalue: 0x%x\n", + s->pm.u32PPLVCvalue,s->pm.u32PPRVCvalue)); + CS_DBGOUT(CS_PM, 9, printk("u32FMLVCvalue: 0x%x u32FMRVCvalue: 0x%x\n", + s->pm.u32FMLVCvalue,s->pm.u32FMRVCvalue)); + CS_DBGOUT(CS_PM, 9, printk("u32GPIORvalue: 0x%x u32JSCTLvalue: 0x%x\n", + s->pm.u32GPIORvalue,s->pm.u32JSCTLvalue)); + CS_DBGOUT(CS_PM, 9, printk("u32SSCR: 0x%x u32SRCSA: 0x%x\n", + s->pm.u32SSCR,s->pm.u32SRCSA)); + CS_DBGOUT(CS_PM, 9, printk("u32DacASR: 0x%x u32AdcASR: 0x%x\n", + s->pm.u32DacASR,s->pm.u32AdcASR)); + CS_DBGOUT(CS_PM, 9, printk("u32DacSR: 0x%x u32AdcSR: 0x%x\n", + s->pm.u32DacSR,s->pm.u32AdcSR)); + CS_DBGOUT(CS_PM, 9, printk("u32MIDCR_Save: 0x%x\n", + s->pm.u32MIDCR_Save)); + +} +static void printpipe(struct cs4281_pipeline *pl) +{ + + CS_DBGOUT(CS_PM, 9, printk("pm struct:\n")); + CS_DBGOUT(CS_PM, 9, printk("flags:0x%x number: 0%x\n", + (unsigned)pl->flags,pl->number)); + CS_DBGOUT(CS_PM, 9, printk("u32DBAnValue: 0%x u32DBCnValue: 0x%x\n", + pl->u32DBAnValue,pl->u32DBCnValue)); + CS_DBGOUT(CS_PM, 9, printk("u32DMRnValue: 0x%x u32DCRnValue: 0x%x\n", + pl->u32DMRnValue,pl->u32DCRnValue)); + CS_DBGOUT(CS_PM, 9, printk("u32DBAnAddress: 0x%x u32DBCnAddress: 0x%x\n", + pl->u32DBAnAddress,pl->u32DBCnAddress)); + CS_DBGOUT(CS_PM, 9, printk("u32DCAnAddress: 0x%x u32DCCnAddress: 0x%x\n", + pl->u32DCCnAddress,pl->u32DCCnAddress)); + CS_DBGOUT(CS_PM, 9, printk("u32DMRnAddress: 0x%x u32DCRnAddress: 0x%x\n", + pl->u32DMRnAddress,pl->u32DCRnAddress)); + CS_DBGOUT(CS_PM, 9, printk("u32HDSRnAddress: 0x%x u32DBAn_Save: 0x%x\n", + pl->u32HDSRnAddress,pl->u32DBAn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32DBCn_Save: 0x%x u32DMRn_Save: 0x%x\n", + pl->u32DBCn_Save,pl->u32DMRn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32DCRn_Save: 0x%x u32DCCn_Save: 0x%x\n", + pl->u32DCRn_Save,pl->u32DCCn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32DCAn_Save: 0x%x\n", + pl->u32DCAn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32FCRn_Save: 0x%x u32FSICn_Save: 0x%x\n", + pl->u32FCRn_Save,pl->u32FSICn_Save)); + CS_DBGOUT(CS_PM, 9, printk("u32FCRnValue: 0x%x u32FSICnValue: 0x%x\n", + pl->u32FCRnValue,pl->u32FSICnValue)); + CS_DBGOUT(CS_PM, 9, printk("u32FCRnAddress: 0x%x u32FSICnAddress: 0x%x\n", + pl->u32FCRnAddress,pl->u32FSICnAddress)); + CS_DBGOUT(CS_PM, 9, printk("u32FPDRnValue: 0x%x u32FPDRnAddress: 0x%x\n", + pl->u32FPDRnValue,pl->u32FPDRnAddress)); +} +static void printpipelines(struct cs4281_state *s) +{ + int i; + for(i=0;i<CS4281_NUMBER_OF_PIPELINES;i++) + { + if(s->pl[i].flags & CS4281_PIPELINE_VALID) + { + printpipe(&s->pl[i]); + } + } +} +/**************************************************************************** +* +* Suspend - save the ac97 regs, mute the outputs and power down the part. +* +****************************************************************************/ +static void cs4281_ac97_suspend(struct cs4281_state *s) +{ + int Count,i; + + CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_suspend()+\n")); +/* +* change the state, save the current hwptr, then stop the dac/adc +*/ + s->pm.flags &= ~CS4281_PM_IDLE; + s->pm.flags |= CS4281_PM_SUSPENDING; + s->pm.u32hwptr_playback = readl(s->pBA0 + BA0_DCA0); + s->pm.u32hwptr_capture = readl(s->pBA0 + BA0_DCA1); + stop_dac(s); + stop_adc(s); + + for(Count = 0x2, i=0; (Count <= CS4281_AC97_HIGHESTREGTORESTORE) + && (i < CS4281_AC97_NUMBER_RESTORE_REGS); + Count += 2, i++) + { + cs4281_read_ac97(s, BA0_AC97_RESET + Count, &s->pm.ac97[i]); + } +/* +* Save the ac97 volume registers as well as the current powerdown state. +* Now, mute the all the outputs (master, headphone, and mono), as well +* as the PCM volume, in preparation for powering down the entire part. +*/ + cs4281_read_ac97(s, BA0_AC97_MASTER_VOLUME, &s->pm.u32AC97_master_volume); + cs4281_read_ac97(s, BA0_AC97_HEADPHONE_VOLUME, &s->pm.u32AC97_headphone_volume); + cs4281_read_ac97(s, BA0_AC97_MASTER_VOLUME_MONO, &s->pm.u32AC97_master_volume_mono); + cs4281_read_ac97(s, BA0_AC97_PCM_OUT_VOLUME, &s->pm.u32AC97_pcm_out_volume); + + cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME, 0x8000); + cs4281_write_ac97(s, BA0_AC97_HEADPHONE_VOLUME, 0x8000); + cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME_MONO, 0x8000); + cs4281_write_ac97(s, BA0_AC97_PCM_OUT_VOLUME, 0x8000); + + cs4281_read_ac97(s, BA0_AC97_POWERDOWN, &s->pm.u32AC97_powerdown); + cs4281_read_ac97(s, BA0_AC97_GENERAL_PURPOSE, &s->pm.u32AC97_general_purpose); + +/* +* And power down everything on the AC97 codec. +*/ + cs4281_write_ac97(s, BA0_AC97_POWERDOWN, 0xff00); + CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_suspend()-\n")); +} + +/**************************************************************************** +* +* Resume - power up the part and restore its registers.. +* +****************************************************************************/ +static void cs4281_ac97_resume(struct cs4281_state *s) +{ + int Count,i; + + CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_resume()+\n")); + +/* do not save the power state registers at this time + // + // If we saved away the power control registers, write them into the + // shadows so those saved values get restored instead of the current + // shadowed value. + // + if( bPowerStateSaved ) + { + PokeShadow( 0x26, ulSaveReg0x26 ); + bPowerStateSaved = FALSE; + } +*/ + +// +// First, we restore the state of the general purpose register. This +// contains the mic select (mic1 or mic2) and if we restore this after +// we restore the mic volume/boost state and mic2 was selected at +// suspend time, we will end up with a brief period of time where mic1 +// is selected with the volume/boost settings for mic2, causing +// acoustic feedback. So we restore the general purpose register +// first, thereby getting the correct mic selected before we restore +// the mic volume/boost. +// + cs4281_write_ac97(s, BA0_AC97_GENERAL_PURPOSE, s->pm.u32AC97_general_purpose); + +// +// Now, while the outputs are still muted, restore the state of power +// on the AC97 part. +// + cs4281_write_ac97(s, BA0_AC97_POWERDOWN, s->pm.u32AC97_powerdown); + +/* +* Restore just the first set of registers, from register number +* 0x02 to the register number that ulHighestRegToRestore specifies. +*/ + for( Count = 0x2, i=0; + (Count <= CS4281_AC97_HIGHESTREGTORESTORE) + && (i < CS4281_AC97_NUMBER_RESTORE_REGS); + Count += 2, i++) + { + cs4281_write_ac97(s, BA0_AC97_RESET + Count, s->pm.ac97[i]); + } + CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_resume()-\n")); +} + +/* do not save the power state registers at this time +**************************************************************************** +* +* SavePowerState - Save the power registers away. +* +**************************************************************************** +void +HWAC97codec::SavePowerState(void) +{ + ENTRY(TM_OBJECTCALLS, "HWAC97codec::SavePowerState()\r\n"); + + ulSaveReg0x26 = PeekShadow(0x26); + + // + // Note that we have saved registers that need to be restored during a + // resume instead of ulAC97Regs[]. + // + bPowerStateSaved = TRUE; + +} // SavePowerState +*/ + +static void cs4281_SuspendFIFO(struct cs4281_state *s, struct cs4281_pipeline *pl) +{ + /* + * We need to save the contents of the BASIC FIFO Registers. + */ + pl->u32FCRn_Save = readl(s->pBA0 + pl->u32FCRnAddress); + pl->u32FSICn_Save = readl(s->pBA0 + pl->u32FSICnAddress); +} +static void cs4281_ResumeFIFO(struct cs4281_state *s, struct cs4281_pipeline *pl) +{ + /* + * We need to restore the contents of the BASIC FIFO Registers. + */ + writel(pl->u32FCRn_Save,s->pBA0 + pl->u32FCRnAddress); + writel(pl->u32FSICn_Save,s->pBA0 + pl->u32FSICnAddress); +} +static void cs4281_SuspendDMAengine(struct cs4281_state *s, struct cs4281_pipeline *pl) +{ + // + // We need to save the contents of the BASIC DMA Registers. + // + pl->u32DBAn_Save = readl(s->pBA0 + pl->u32DBAnAddress); + pl->u32DBCn_Save = readl(s->pBA0 + pl->u32DBCnAddress); + pl->u32DMRn_Save = readl(s->pBA0 + pl->u32DMRnAddress); + pl->u32DCRn_Save = readl(s->pBA0 + pl->u32DCRnAddress); + pl->u32DCCn_Save = readl(s->pBA0 + pl->u32DCCnAddress); + pl->u32DCAn_Save = readl(s->pBA0 + pl->u32DCAnAddress); +} +static void cs4281_ResumeDMAengine(struct cs4281_state *s, struct cs4281_pipeline *pl) +{ + // + // We need to save the contents of the BASIC DMA Registers. + // + writel( pl->u32DBAn_Save, s->pBA0 + pl->u32DBAnAddress); + writel( pl->u32DBCn_Save, s->pBA0 + pl->u32DBCnAddress); + writel( pl->u32DMRn_Save, s->pBA0 + pl->u32DMRnAddress); + writel( pl->u32DCRn_Save, s->pBA0 + pl->u32DCRnAddress); + writel( pl->u32DCCn_Save, s->pBA0 + pl->u32DCCnAddress); + writel( pl->u32DCAn_Save, s->pBA0 + pl->u32DCAnAddress); +} + +static int cs4281_suspend(struct cs4281_state *s) +{ + int i; + u32 u32CLKCR1; + struct cs4281_pm *pm = &s->pm; + CS_DBGOUT(CS_PM | CS_FUNCTION, 9, + printk("cs4281: cs4281_suspend()+ flags=%d\n", + (unsigned)s->pm.flags)); +/* +* check the current state, only suspend if IDLE +*/ + if(!(s->pm.flags & CS4281_PM_IDLE)) + { + CS_DBGOUT(CS_PM | CS_ERROR, 2, + printk("cs4281: cs4281_suspend() unable to suspend, not IDLE\n")); + return 1; + } + s->pm.flags &= ~CS4281_PM_IDLE; + s->pm.flags |= CS4281_PM_SUSPENDING; + +// +// Gershwin CLKRUN - Set CKRA +// + u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1); + + pm->u32CLKCR1_SAVE = u32CLKCR1; + if(!(u32CLKCR1 & 0x00010000 ) ) + writel(u32CLKCR1 | 0x00010000, s->pBA0 + BA0_CLKCR1); + +// +// First, turn on the clocks (yikes) to the devices, so that they will +// respond when we try to save their state. +// + if(!(u32CLKCR1 & CLKCR1_SWCE)) + { + writel(u32CLKCR1 | CLKCR1_SWCE , s->pBA0 + BA0_CLKCR1); + } + + // + // Save the power state + // + pm->u32SSPMValue = readl(s->pBA0 + BA0_SSPM); + + // + // Disable interrupts. + // + writel(HICR_CHGM, s->pBA0 + BA0_HICR); + + // + // Save the PCM Playback Left and Right Volume Control. + // + pm->u32PPLVCvalue = readl(s->pBA0 + BA0_PPLVC); + pm->u32PPRVCvalue = readl(s->pBA0 + BA0_PPRVC); + + // + // Save the FM Synthesis Left and Right Volume Control. + // + pm->u32FMLVCvalue = readl(s->pBA0 + BA0_FMLVC); + pm->u32FMRVCvalue = readl(s->pBA0 + BA0_FMRVC); + + // + // Save the GPIOR value. + // + pm->u32GPIORvalue = readl(s->pBA0 + BA0_GPIOR); + + // + // Save the JSCTL value. + // + pm->u32JSCTLvalue = readl(s->pBA0 + BA0_GPIOR); + + // + // Save Sound System Control Register + // + pm->u32SSCR = readl(s->pBA0 + BA0_SSCR); + + // + // Save SRC Slot Assinment register + // + pm->u32SRCSA = readl(s->pBA0 + BA0_SRCSA); + + // + // Save sample rate + // + pm->u32DacASR = readl(s->pBA0 + BA0_PASR); + pm->u32AdcASR = readl(s->pBA0 + BA0_CASR); + pm->u32DacSR = readl(s->pBA0 + BA0_DACSR); + pm->u32AdcSR = readl(s->pBA0 + BA0_ADCSR); + + // + // Loop through all of the PipeLines + // + for(i = 0; i < CS4281_NUMBER_OF_PIPELINES; i++) + { + if(s->pl[i].flags & CS4281_PIPELINE_VALID) + { + // + // Ask the DMAengines and FIFOs to Suspend. + // + cs4281_SuspendDMAengine(s,&s->pl[i]); + cs4281_SuspendFIFO(s,&s->pl[i]); + } + } + // + // We need to save the contents of the Midi Control Register. + // + pm->u32MIDCR_Save = readl(s->pBA0 + BA0_MIDCR); +/* +* save off the AC97 part information +*/ + cs4281_ac97_suspend(s); + + // + // Turn off the serial ports. + // + writel(0, s->pBA0 + BA0_SERMC); + + // + // Power off FM, Joystick, AC link, + // + writel(0, s->pBA0 + BA0_SSPM); + + // + // DLL off. + // + writel(0, s->pBA0 + BA0_CLKCR1); + + // + // AC link off. + // + writel(0, s->pBA0 + BA0_SPMC); + + // + // Put the chip into D3(hot) state. + // + // PokeBA0(BA0_PMCS, 0x00000003); + + // + // Gershwin CLKRUN - Clear CKRA + // + u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1); + writel(u32CLKCR1 & 0xFFFEFFFF, s->pBA0 + BA0_CLKCR1); + +#ifdef CSDEBUG + printpm(s); + printpipelines(s); +#endif + + s->pm.flags &= ~CS4281_PM_SUSPENDING; + s->pm.flags |= CS4281_PM_SUSPENDED; + + CS_DBGOUT(CS_PM | CS_FUNCTION, 9, + printk("cs4281: cs4281_suspend()- flags=%d\n", + (unsigned)s->pm.flags)); + return 0; +} + +static int cs4281_resume(struct cs4281_state *s) +{ + int i; + unsigned temp1; + u32 u32CLKCR1; + struct cs4281_pm *pm = &s->pm; + CS_DBGOUT(CS_PM | CS_FUNCTION, 4, + printk( "cs4281: cs4281_resume()+ flags=%d\n", + (unsigned)s->pm.flags)); + if(!(s->pm.flags & CS4281_PM_SUSPENDED)) + { + CS_DBGOUT(CS_PM | CS_ERROR, 2, + printk("cs4281: cs4281_resume() unable to resume, not SUSPENDED\n")); + return 1; + } + s->pm.flags &= ~CS4281_PM_SUSPENDED; + s->pm.flags |= CS4281_PM_RESUMING; + +// +// Gershwin CLKRUN - Set CKRA +// + u32CLKCR1 = readl(s->pBA0 + BA0_CLKCR1); + writel(u32CLKCR1 | 0x00010000, s->pBA0 + BA0_CLKCR1); + + // + // set the power state. + // + //old PokeBA0(BA0_PMCS, 0); + + // + // Program the clock circuit and serial ports. + // + temp1 = cs4281_hw_init(s); + if (temp1) { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, + printk(KERN_ERR + "cs4281: resume cs4281_hw_init() error.\n")); + return -1; + } + + // + // restore the Power state + // + writel(pm->u32SSPMValue, s->pBA0 + BA0_SSPM); + + // + // Set post SRC mix setting (FM or ALT48K) + // + writel(pm->u32SSPM_BITS, s->pBA0 + BA0_SSPM); + + // + // Loop through all of the PipeLines + // + for(i = 0; i < CS4281_NUMBER_OF_PIPELINES; i++) + { + if(s->pl[i].flags & CS4281_PIPELINE_VALID) + { + // + // Ask the DMAengines and FIFOs to Resume. + // + cs4281_ResumeDMAengine(s,&s->pl[i]); + cs4281_ResumeFIFO(s,&s->pl[i]); + } + } + // + // We need to restore the contents of the Midi Control Register. + // + writel(pm->u32MIDCR_Save, s->pBA0 + BA0_MIDCR); + + cs4281_ac97_resume(s); + // + // Restore the PCM Playback Left and Right Volume Control. + // + writel(pm->u32PPLVCvalue, s->pBA0 + BA0_PPLVC); + writel(pm->u32PPRVCvalue, s->pBA0 + BA0_PPRVC); + + // + // Restore the FM Synthesis Left and Right Volume Control. + // + writel(pm->u32FMLVCvalue, s->pBA0 + BA0_FMLVC); + writel(pm->u32FMRVCvalue, s->pBA0 + BA0_FMRVC); + + // + // Restore the JSCTL value. + // + writel(pm->u32JSCTLvalue, s->pBA0 + BA0_JSCTL); + + // + // Restore the GPIOR register value. + // + writel(pm->u32GPIORvalue, s->pBA0 + BA0_GPIOR); + + // + // Restore Sound System Control Register + // + writel(pm->u32SSCR, s->pBA0 + BA0_SSCR); + + // + // Restore SRC Slot Assignment register + // + writel(pm->u32SRCSA, s->pBA0 + BA0_SRCSA); + + // + // Restore sample rate + // + writel(pm->u32DacASR, s->pBA0 + BA0_PASR); + writel(pm->u32AdcASR, s->pBA0 + BA0_CASR); + writel(pm->u32DacSR, s->pBA0 + BA0_DACSR); + writel(pm->u32AdcSR, s->pBA0 + BA0_ADCSR); + + // + // Restore CFL1/2 registers we saved to compensate for OEM bugs. + // + // PokeBA0(BA0_CFLR, ulConfig); + + // + // Gershwin CLKRUN - Clear CKRA + // + writel(pm->u32CLKCR1_SAVE, s->pBA0 + BA0_CLKCR1); + + // + // Enable interrupts on the part. + // + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); + +#ifdef CSDEBUG + printpm(s); + printpipelines(s); +#endif +/* +* change the state, restore the current hwptrs, then stop the dac/adc +*/ + s->pm.flags |= CS4281_PM_IDLE; + s->pm.flags &= ~(CS4281_PM_SUSPENDING | CS4281_PM_SUSPENDED + | CS4281_PM_RESUMING | CS4281_PM_RESUMED); + + writel(s->pm.u32hwptr_playback, s->pBA0 + BA0_DCA0); + writel(s->pm.u32hwptr_capture, s->pBA0 + BA0_DCA1); + start_dac(s); + start_adc(s); + + CS_DBGOUT(CS_PM | CS_FUNCTION, 9, printk("cs4281: cs4281_resume()- flags=%d\n", + (unsigned)s->pm.flags)); + return 0; +} + +#endif + +//****************************************************************************** +// "cs4281_play_rate()" -- +//****************************************************************************** +static void cs4281_play_rate(struct cs4281_state *card, u32 playrate) +{ + u32 DACSRvalue = 1; + + // Based on the sample rate, program the DACSR register. + if (playrate == 8000) + DACSRvalue = 5; + if (playrate == 11025) + DACSRvalue = 4; + else if (playrate == 22050) + DACSRvalue = 2; + else if (playrate == 44100) + DACSRvalue = 1; + else if ((playrate <= 48000) && (playrate >= 6023)) + DACSRvalue = 24576000 / (playrate * 16); + else if (playrate < 6023) + // Not allowed by open. + return; + else if (playrate > 48000) + // Not allowed by open. + return; + CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 2, printk(KERN_INFO + "cs4281: cs4281_play_rate(): DACSRvalue=0x%.8x playrate=%d\n", + DACSRvalue, playrate)); + // Write the 'sample rate select code' + // to the 'DAC Sample Rate' register. + writel(DACSRvalue, card->pBA0 + BA0_DACSR); // (744h) +} + +//****************************************************************************** +// "cs4281_record_rate()" -- Initialize the record sample rate converter. +//****************************************************************************** +static void cs4281_record_rate(struct cs4281_state *card, u32 outrate) +{ + u32 ADCSRvalue = 1; + + // + // Based on the sample rate, program the ADCSR register + // + if (outrate == 8000) + ADCSRvalue = 5; + if (outrate == 11025) + ADCSRvalue = 4; + else if (outrate == 22050) + ADCSRvalue = 2; + else if (outrate == 44100) + ADCSRvalue = 1; + else if ((outrate <= 48000) && (outrate >= 6023)) + ADCSRvalue = 24576000 / (outrate * 16); + else if (outrate < 6023) { + // Not allowed by open. + return; + } else if (outrate > 48000) { + // Not allowed by open. + return; + } + CS_DBGOUT(CS_WAVE_READ | CS_PARMS, 2, printk(KERN_INFO + "cs4281: cs4281_record_rate(): ADCSRvalue=0x%.8x outrate=%d\n", + ADCSRvalue, outrate)); + // Write the 'sample rate select code + // to the 'ADC Sample Rate' register. + writel(ADCSRvalue, card->pBA0 + BA0_ADCSR); // (748h) +} + + + +static void stop_dac(struct cs4281_state *s) +{ + unsigned long flags; + unsigned temp1; + + CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO "cs4281: stop_dac():\n")); + spin_lock_irqsave(&s->lock, flags); + s->ena &= ~FMODE_WRITE; + temp1 = readl(s->pBA0 + BA0_DCR0) | DCRn_MSK; + writel(temp1, s->pBA0 + BA0_DCR0); + + spin_unlock_irqrestore(&s->lock, flags); +} + + +static void start_dac(struct cs4281_state *s) +{ + unsigned long flags; + unsigned temp1; + + CS_DBGOUT(CS_FUNCTION, 3, printk(KERN_INFO "cs4281: start_dac()+\n")); + spin_lock_irqsave(&s->lock, flags); + if (!(s->ena & FMODE_WRITE) && (s->dma_dac.mapped || + (s->dma_dac.count > 0 + && s->dma_dac.ready)) +#ifndef NOT_CS4281_PM + && (s->pm.flags & CS4281_PM_IDLE)) +#else +) +#endif + { + s->ena |= FMODE_WRITE; + temp1 = readl(s->pBA0 + BA0_DCR0) & ~DCRn_MSK; // Clear DMA0 channel mask. + writel(temp1, s->pBA0 + BA0_DCR0); // Start DMA'ing. + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Enable interrupts. + + writel(7, s->pBA0 + BA0_PPRVC); + writel(7, s->pBA0 + BA0_PPLVC); + CS_DBGOUT(CS_WAVE_WRITE | CS_PARMS, 8, printk(KERN_INFO + "cs4281: start_dac(): writel 0x%x start dma\n", temp1)); + + } + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION, 3, + printk(KERN_INFO "cs4281: start_dac()-\n")); +} + + +static void stop_adc(struct cs4281_state *s) +{ + unsigned long flags; + unsigned temp1; + + CS_DBGOUT(CS_FUNCTION, 3, + printk(KERN_INFO "cs4281: stop_adc()+\n")); + + spin_lock_irqsave(&s->lock, flags); + s->ena &= ~FMODE_READ; + + if (s->conversion == 1) { + s->conversion = 0; + s->prop_adc.fmt = s->prop_adc.fmt_original; + } + temp1 = readl(s->pBA0 + BA0_DCR1) | DCRn_MSK; + writel(temp1, s->pBA0 + BA0_DCR1); + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION, 3, + printk(KERN_INFO "cs4281: stop_adc()-\n")); +} + + +static void start_adc(struct cs4281_state *s) +{ + unsigned long flags; + unsigned temp1; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: start_adc()+\n")); + + if (!(s->ena & FMODE_READ) && + (s->dma_adc.mapped || s->dma_adc.count <= + (signed) (s->dma_adc.dmasize - 2 * s->dma_adc.fragsize)) + && s->dma_adc.ready +#ifndef NOT_CS4281_PM + && (s->pm.flags & CS4281_PM_IDLE)) +#else +) +#endif + { + if (s->prop_adc.fmt & AFMT_S8 || s->prop_adc.fmt & AFMT_U8) { + // + // now only use 16 bit capture, due to truncation issue + // in the chip, noticable distortion occurs. + // allocate buffer and then convert from 16 bit to + // 8 bit for the user buffer. + // + s->prop_adc.fmt_original = s->prop_adc.fmt; + if (s->prop_adc.fmt & AFMT_S8) { + s->prop_adc.fmt &= ~AFMT_S8; + s->prop_adc.fmt |= AFMT_S16_LE; + } + if (s->prop_adc.fmt & AFMT_U8) { + s->prop_adc.fmt &= ~AFMT_U8; + s->prop_adc.fmt |= AFMT_U16_LE; + } + // + // prog_dmabuf_adc performs a stop_adc() but that is + // ok since we really haven't started the DMA yet. + // + prog_codec(s, CS_TYPE_ADC); + + if (prog_dmabuf_adc(s) != 0) { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO + "cs4281: start_adc(): error in prog_dmabuf_adc\n")); + } + s->conversion = 1; + } + spin_lock_irqsave(&s->lock, flags); + s->ena |= FMODE_READ; + temp1 = readl(s->pBA0 + BA0_DCR1) & ~DCRn_MSK; // Clear DMA1 channel mask bit. + writel(temp1, s->pBA0 + BA0_DCR1); // Start recording + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Enable interrupts. + spin_unlock_irqrestore(&s->lock, flags); + + CS_DBGOUT(CS_PARMS, 6, printk(KERN_INFO + "cs4281: start_adc(): writel 0x%x \n", temp1)); + } + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: start_adc()-\n")); + +} + + +// --------------------------------------------------------------------- + +#define DMABUF_MINORDER 1 // ==> min buffer size = 8K. + + +static void dealloc_dmabuf(struct cs4281_state *s, struct dmabuf *db) +{ + struct page *map, *mapend; + + if (db->rawbuf) { + // Undo prog_dmabuf()'s marking the pages as reserved + mapend = + virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - + 1); + for (map = virt_to_page(db->rawbuf); map <= mapend; map++) + ClearPageReserved(map); + free_dmabuf(s, db); + } + if (s->tmpbuff && (db->type == CS_TYPE_ADC)) { + // Undo prog_dmabuf()'s marking the pages as reserved + mapend = + virt_to_page(s->tmpbuff + + (PAGE_SIZE << s->buforder_tmpbuff) - 1); + for (map = virt_to_page(s->tmpbuff); map <= mapend; map++) + ClearPageReserved(map); + free_dmabuf2(s, db); + } + s->tmpbuff = NULL; + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct cs4281_state *s, struct dmabuf *db) +{ + int order; + unsigned bytespersec, temp1; + unsigned bufs, sample_shift = 0; + struct page *map, *mapend; + unsigned long df; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: prog_dmabuf()+\n")); + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = + db->endcleared = db->blocks = db->wakeup = db->underrun = 0; +/* +* check for order within limits, but do not overwrite value, check +* later for a fractional defaultorder (i.e. 100+). +*/ + if((defaultorder > 0) && (defaultorder < 12)) + df = defaultorder; + else + df = 1; + + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = df; order >= DMABUF_MINORDER; order--) + if ( (db->rawbuf = (void *) pci_alloc_consistent( + s->pcidev, PAGE_SIZE << order, &db-> dmaaddr))) + break; + if (!db->rawbuf) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: prog_dmabuf(): unable to allocate rawbuf\n")); + return -ENOMEM; + } + db->buforder = order; + // Now mark the pages as reserved; otherwise the + // remap_pfn_range() in cs4281_mmap doesn't work. + // 1. get index to last page in mem_map array for rawbuf. + mapend = virt_to_page(db->rawbuf + + (PAGE_SIZE << db->buforder) - 1); + + // 2. mark each physical page in range as 'reserved'. + for (map = virt_to_page(db->rawbuf); map <= mapend; map++) + SetPageReserved(map); + } + if (!s->tmpbuff && (db->type == CS_TYPE_ADC)) { + for (order = df; order >= DMABUF_MINORDER; + order--) + if ( (s->tmpbuff = (void *) pci_alloc_consistent( + s->pcidev, PAGE_SIZE << order, + &s->dmaaddr_tmpbuff))) + break; + if (!s->tmpbuff) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: prog_dmabuf(): unable to allocate tmpbuff\n")); + return -ENOMEM; + } + s->buforder_tmpbuff = order; + // Now mark the pages as reserved; otherwise the + // remap_pfn_range() in cs4281_mmap doesn't work. + // 1. get index to last page in mem_map array for rawbuf. + mapend = virt_to_page(s->tmpbuff + + (PAGE_SIZE << s->buforder_tmpbuff) - 1); + + // 2. mark each physical page in range as 'reserved'. + for (map = virt_to_page(s->tmpbuff); map <= mapend; map++) + SetPageReserved(map); + } + if (db->type == CS_TYPE_DAC) { + if (s->prop_dac.fmt & (AFMT_S16_LE | AFMT_U16_LE)) + sample_shift++; + if (s->prop_dac.channels > 1) + sample_shift++; + bytespersec = s->prop_dac.rate << sample_shift; + } else // CS_TYPE_ADC + { + if (s->prop_adc.fmt & (AFMT_S16_LE | AFMT_U16_LE)) + sample_shift++; + if (s->prop_adc.channels > 1) + sample_shift++; + bytespersec = s->prop_adc.rate << sample_shift; + } + bufs = PAGE_SIZE << db->buforder; + +/* +* added fractional "defaultorder" inputs. if >100 then use +* defaultorder-100 as power of 2 for the buffer size. example: +* 106 = 2^(106-100) = 2^6 = 64 bytes for the buffer size. +*/ + if(defaultorder >= 100) + { + bufs = 1 << (defaultorder-100); + } + +#define INTERRUPT_RATE_MS 100 // Interrupt rate in milliseconds. + db->numfrag = 2; +/* +* Nominal frag size(bytes/interrupt) +*/ + temp1 = bytespersec / (1000 / INTERRUPT_RATE_MS); + db->fragshift = 8; // Min 256 bytes. + while (1 << db->fragshift < temp1) // Calc power of 2 frag size. + db->fragshift += 1; + db->fragsize = 1 << db->fragshift; + db->dmasize = db->fragsize * 2; + db->fragsamples = db->fragsize >> sample_shift; // # samples/fragment. + +// If the calculated size is larger than the allocated +// buffer, divide the allocated buffer into 2 fragments. + if (db->dmasize > bufs) { + + db->numfrag = 2; // Two fragments. + db->fragsize = bufs >> 1; // Each 1/2 the alloc'ed buffer. + db->fragsamples = db->fragsize >> sample_shift; // # samples/fragment. + db->dmasize = bufs; // Use all the alloc'ed buffer. + + db->fragshift = 0; // Calculate 'fragshift'. + temp1 = db->fragsize; // update_ptr() uses it + while ((temp1 >>= 1) > 1) // to calc 'total-bytes' + db->fragshift += 1; // returned in DSP_GETI/OPTR. + } + CS_DBGOUT(CS_PARMS, 3, printk(KERN_INFO + "cs4281: prog_dmabuf(): numfrag=%d fragsize=%d fragsamples=%d fragshift=%d bufs=%d fmt=0x%x ch=%d\n", + db->numfrag, db->fragsize, db->fragsamples, + db->fragshift, bufs, + (db->type == CS_TYPE_DAC) ? s->prop_dac.fmt : + s->prop_adc.fmt, + (db->type == CS_TYPE_DAC) ? s->prop_dac.channels : + s->prop_adc.channels)); + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: prog_dmabuf()-\n")); + return 0; +} + + +static int prog_dmabuf_adc(struct cs4281_state *s) +{ + unsigned long va; + unsigned count; + int c; + stop_adc(s); + s->dma_adc.type = CS_TYPE_ADC; + if ((c = prog_dmabuf(s, &s->dma_adc))) + return c; + + if (s->dma_adc.rawbuf) { + memset(s->dma_adc.rawbuf, + (s->prop_adc. + fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, + s->dma_adc.dmasize); + } + if (s->tmpbuff) { + memset(s->tmpbuff, + (s->prop_adc. + fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, + PAGE_SIZE << s->buforder_tmpbuff); + } + + va = virt_to_bus(s->dma_adc.rawbuf); + + count = s->dma_adc.dmasize; + + if (s->prop_adc. + fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) + count /= 2; // 16-bit. + + if (s->prop_adc.channels > 1) + count /= 2; // Assume stereo. + + CS_DBGOUT(CS_WAVE_READ, 3, printk(KERN_INFO + "cs4281: prog_dmabuf_adc(): count=%d va=0x%.8x\n", + count, (unsigned) va)); + + writel(va, s->pBA0 + BA0_DBA1); // Set buffer start address. + writel(count - 1, s->pBA0 + BA0_DBC1); // Set count. + s->dma_adc.ready = 1; + return 0; +} + + +static int prog_dmabuf_dac(struct cs4281_state *s) +{ + unsigned long va; + unsigned count; + int c; + stop_dac(s); + s->dma_dac.type = CS_TYPE_DAC; + if ((c = prog_dmabuf(s, &s->dma_dac))) + return c; + memset(s->dma_dac.rawbuf, + (s->prop_dac.fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, + s->dma_dac.dmasize); + + va = virt_to_bus(s->dma_dac.rawbuf); + + count = s->dma_dac.dmasize; + if (s->prop_dac. + fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) + count /= 2; // 16-bit. + + if (s->prop_dac.channels > 1) + count /= 2; // Assume stereo. + + writel(va, s->pBA0 + BA0_DBA0); // Set buffer start address. + writel(count - 1, s->pBA0 + BA0_DBC0); // Set count. + + CS_DBGOUT(CS_WAVE_WRITE, 3, printk(KERN_INFO + "cs4281: prog_dmabuf_dac(): count=%d va=0x%.8x\n", + count, (unsigned) va)); + + s->dma_dac.ready = 1; + return 0; +} + + +static void clear_advance(void *buf, unsigned bsize, unsigned bptr, + unsigned len, unsigned char c) +{ + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(((char *) buf) + bptr, c, x); + bptr = 0; + len -= x; + } + CS_DBGOUT(CS_WAVE_WRITE, 4, printk(KERN_INFO + "cs4281: clear_advance(): memset %d at %p for %d size \n", + (unsigned)c, ((char *) buf) + bptr, len)); + memset(((char *) buf) + bptr, c, len); +} + + + +// call with spinlock held! +static void cs4281_update_ptr(struct cs4281_state *s, int intflag) +{ + int diff; + unsigned hwptr, va; + + // update ADC pointer + if (s->ena & FMODE_READ) { + hwptr = readl(s->pBA0 + BA0_DCA1); // Read capture DMA address. + va = virt_to_bus(s->dma_adc.rawbuf); + hwptr -= (unsigned) va; + diff = + (s->dma_adc.dmasize + hwptr - + s->dma_adc.hwptr) % s->dma_adc.dmasize; + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count > s->dma_adc.dmasize) + s->dma_adc.count = s->dma_adc.dmasize; + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= + (signed) s->dma_adc.fragsize) wake_up(&s-> + dma_adc. + wait); + } else { + if (s->dma_adc.count > 0) + wake_up(&s->dma_adc.wait); + } + CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO + "cs4281: cs4281_update_ptr(): s=%p hwptr=%d total_bytes=%d count=%d \n", + s, s->dma_adc.hwptr, s->dma_adc.total_bytes, s->dma_adc.count)); + } + // update DAC pointer + // + // check for end of buffer, means that we are going to wait for another interrupt + // to allow silence to fill the fifos on the part, to keep pops down to a minimum. + // + if (s->ena & FMODE_WRITE) { + hwptr = readl(s->pBA0 + BA0_DCA0); // Read play DMA address. + va = virt_to_bus(s->dma_dac.rawbuf); + hwptr -= (unsigned) va; + diff = (s->dma_dac.dmasize + hwptr - + s->dma_dac.hwptr) % s->dma_dac.dmasize; + s->dma_dac.hwptr = hwptr; + s->dma_dac.total_bytes += diff; + if (s->dma_dac.mapped) { + s->dma_dac.count += diff; + if (s->dma_dac.count >= s->dma_dac.fragsize) { + s->dma_dac.wakeup = 1; + wake_up(&s->dma_dac.wait); + if (s->dma_dac.count > s->dma_dac.dmasize) + s->dma_dac.count &= + s->dma_dac.dmasize - 1; + } + } else { + s->dma_dac.count -= diff; + if (s->dma_dac.count <= 0) { + // + // fill with silence, and do not shut down the DAC. + // Continue to play silence until the _release. + // + CS_DBGOUT(CS_WAVE_WRITE, 6, printk(KERN_INFO + "cs4281: cs4281_update_ptr(): memset %d at %p for %d size \n", + (unsigned)(s->prop_dac.fmt & + (AFMT_U8 | AFMT_U16_LE)) ? 0x80 : 0, + s->dma_dac.rawbuf, s->dma_dac.dmasize)); + memset(s->dma_dac.rawbuf, + (s->prop_dac. + fmt & (AFMT_U8 | AFMT_U16_LE)) ? + 0x80 : 0, s->dma_dac.dmasize); + if (s->dma_dac.count < 0) { + s->dma_dac.underrun = 1; + s->dma_dac.count = 0; + CS_DBGOUT(CS_ERROR, 9, printk(KERN_INFO + "cs4281: cs4281_update_ptr(): underrun\n")); + } + } else if (s->dma_dac.count <= + (signed) s->dma_dac.fragsize + && !s->dma_dac.endcleared) { + clear_advance(s->dma_dac.rawbuf, + s->dma_dac.dmasize, + s->dma_dac.swptr, + s->dma_dac.fragsize, + (s->prop_dac. + fmt & (AFMT_U8 | + AFMT_U16_LE)) ? 0x80 + : 0); + s->dma_dac.endcleared = 1; + } + if ( (s->dma_dac.count <= (signed) s->dma_dac.dmasize/2) || + intflag) + { + wake_up(&s->dma_dac.wait); + } + } + CS_DBGOUT(CS_PARMS, 8, printk(KERN_INFO + "cs4281: cs4281_update_ptr(): s=%p hwptr=%d total_bytes=%d count=%d \n", + s, s->dma_dac.hwptr, s->dma_dac.total_bytes, s->dma_dac.count)); + } +} + + +// --------------------------------------------------------------------- + +static void prog_codec(struct cs4281_state *s, unsigned type) +{ + unsigned long flags; + unsigned temp1, format; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: prog_codec()+ \n")); + + spin_lock_irqsave(&s->lock, flags); + if (type == CS_TYPE_ADC) { + temp1 = readl(s->pBA0 + BA0_DCR1); + writel(temp1 | DCRn_MSK, s->pBA0 + BA0_DCR1); // Stop capture DMA, if active. + + // program sampling rates + // Note, for CS4281, capture & play rates can be set independently. + cs4281_record_rate(s, s->prop_adc.rate); + + // program ADC parameters + format = DMRn_DMA | DMRn_AUTO | DMRn_TR_WRITE; + if (s->prop_adc. + fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) { // 16-bit + if (s->prop_adc.fmt & (AFMT_S16_BE | AFMT_U16_BE)) // Big-endian? + format |= DMRn_BEND; + if (s->prop_adc.fmt & (AFMT_U16_LE | AFMT_U16_BE)) + format |= DMRn_USIGN; // Unsigned. + } else + format |= DMRn_SIZE8 | DMRn_USIGN; // 8-bit, unsigned + if (s->prop_adc.channels < 2) + format |= DMRn_MONO; + + writel(format, s->pBA0 + BA0_DMR1); + + CS_DBGOUT(CS_PARMS, 2, printk(KERN_INFO + "cs4281: prog_codec(): adc %s %s %s rate=%d DMR0 format=0x%.8x\n", + (format & DMRn_SIZE8) ? "8" : "16", + (format & DMRn_USIGN) ? "Unsigned" : "Signed", + (format & DMRn_MONO) ? "Mono" : "Stereo", + s->prop_adc.rate, format)); + + s->ena &= ~FMODE_READ; // not capturing data yet + } + + + if (type == CS_TYPE_DAC) { + temp1 = readl(s->pBA0 + BA0_DCR0); + writel(temp1 | DCRn_MSK, s->pBA0 + BA0_DCR0); // Stop play DMA, if active. + + // program sampling rates + // Note, for CS4281, capture & play rates can be set independently. + cs4281_play_rate(s, s->prop_dac.rate); + + // program DAC parameters + format = DMRn_DMA | DMRn_AUTO | DMRn_TR_READ; + if (s->prop_dac. + fmt & (AFMT_S16_LE | AFMT_U16_LE | AFMT_S16_BE | AFMT_U16_BE)) { // 16-bit + if (s->prop_dac.fmt & (AFMT_S16_BE | AFMT_U16_BE)) + format |= DMRn_BEND; // Big Endian. + if (s->prop_dac.fmt & (AFMT_U16_LE | AFMT_U16_BE)) + format |= DMRn_USIGN; // Unsigned. + } else + format |= DMRn_SIZE8 | DMRn_USIGN; // 8-bit, unsigned + + if (s->prop_dac.channels < 2) + format |= DMRn_MONO; + + writel(format, s->pBA0 + BA0_DMR0); + + + CS_DBGOUT(CS_PARMS, 2, printk(KERN_INFO + "cs4281: prog_codec(): dac %s %s %s rate=%d DMR0 format=0x%.8x\n", + (format & DMRn_SIZE8) ? "8" : "16", + (format & DMRn_USIGN) ? "Unsigned" : "Signed", + (format & DMRn_MONO) ? "Mono" : "Stereo", + s->prop_dac.rate, format)); + + s->ena &= ~FMODE_WRITE; // not capturing data yet + + } + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: prog_codec()- \n")); +} + + +static int mixer_ioctl(struct cs4281_state *s, unsigned int cmd, + unsigned long arg) +{ + // Index to mixer_src[] is value of AC97 Input Mux Select Reg. + // Value of array member is recording source Device ID Mask. + static const unsigned int mixer_src[8] = { + SOUND_MASK_MIC, SOUND_MASK_CD, 0, SOUND_MASK_LINE1, + SOUND_MASK_LINE, SOUND_MASK_VOLUME, 0, 0 + }; + void __user *argp = (void __user *)arg; + + // Index of mixtable1[] member is Device ID + // and must be <= SOUND_MIXER_NRDEVICES. + // Value of array member is index into s->mix.vol[] + static const unsigned char mixtable1[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_PCM] = 1, // voice + [SOUND_MIXER_LINE1] = 2, // AUX + [SOUND_MIXER_CD] = 3, // CD + [SOUND_MIXER_LINE] = 4, // Line + [SOUND_MIXER_SYNTH] = 5, // FM + [SOUND_MIXER_MIC] = 6, // Mic + [SOUND_MIXER_SPEAKER] = 7, // Speaker + [SOUND_MIXER_RECLEV] = 8, // Recording level + [SOUND_MIXER_VOLUME] = 9 // Master Volume + }; + + + static const unsigned mixreg[] = { + BA0_AC97_PCM_OUT_VOLUME, + BA0_AC97_AUX_VOLUME, + BA0_AC97_CD_VOLUME, + BA0_AC97_LINE_IN_VOLUME + }; + unsigned char l, r, rl, rr, vidx; + unsigned char attentbl[11] = + { 63, 42, 26, 17, 14, 11, 8, 6, 4, 2, 0 }; + unsigned temp1; + int i, val; + + VALIDATE_STATE(s); + CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO + "cs4281: mixer_ioctl(): s=%p cmd=0x%.8x\n", s, cmd)); +#if CSDEBUG + cs_printioctl(cmd); +#endif +#if CSDEBUG_INTERFACE + + if ((cmd == SOUND_MIXER_CS_GETDBGMASK) || + (cmd == SOUND_MIXER_CS_SETDBGMASK) || + (cmd == SOUND_MIXER_CS_GETDBGLEVEL) || + (cmd == SOUND_MIXER_CS_SETDBGLEVEL) || + (cmd == SOUND_MIXER_CS_APM)) + { + switch (cmd) { + + case SOUND_MIXER_CS_GETDBGMASK: + return put_user(cs_debugmask, + (unsigned long __user *) argp); + + case SOUND_MIXER_CS_GETDBGLEVEL: + return put_user(cs_debuglevel, + (unsigned long __user *) argp); + + case SOUND_MIXER_CS_SETDBGMASK: + if (get_user(val, (unsigned long __user *) argp)) + return -EFAULT; + cs_debugmask = val; + return 0; + + case SOUND_MIXER_CS_SETDBGLEVEL: + if (get_user(val, (unsigned long __user *) argp)) + return -EFAULT; + cs_debuglevel = val; + return 0; +#ifndef NOT_CS4281_PM + case SOUND_MIXER_CS_APM: + if (get_user(val, (unsigned long __user *) argp)) + return -EFAULT; + if(val == CS_IOCTL_CMD_SUSPEND) + cs4281_suspend(s); + else if(val == CS_IOCTL_CMD_RESUME) + cs4281_resume(s); + else + { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs4281: mixer_ioctl(): invalid APM cmd (%d)\n", + val)); + } + return 0; +#endif + default: + CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO + "cs4281: mixer_ioctl(): ERROR unknown debug cmd\n")); + return 0; + } + } +#endif + + if (cmd == SOUND_MIXER_PRIVATE1) { + // enable/disable/query mixer preamp + if (get_user(val, (int __user *) argp)) + return -EFAULT; + if (val != -1) { + cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); + temp1 = val ? (temp1 | 0x40) : (temp1 & 0xffbf); + cs4281_write_ac97(s, BA0_AC97_MIC_VOLUME, temp1); + } + cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); + val = (temp1 & 0x40) ? 1 : 0; + return put_user(val, (int __user *) argp); + } + if (cmd == SOUND_MIXER_PRIVATE2) { + // enable/disable/query spatializer + if (get_user(val, (int __user *)argp)) + return -EFAULT; + if (val != -1) { + temp1 = (val & 0x3f) >> 2; + cs4281_write_ac97(s, BA0_AC97_3D_CONTROL, temp1); + cs4281_read_ac97(s, BA0_AC97_GENERAL_PURPOSE, + &temp1); + cs4281_write_ac97(s, BA0_AC97_GENERAL_PURPOSE, + temp1 | 0x2000); + } + cs4281_read_ac97(s, BA0_AC97_3D_CONTROL, &temp1); + return put_user((temp1 << 2) | 3, (int __user *)argp); + } + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strlcpy(info.id, "CS4281", sizeof(info.id)); + strlcpy(info.name, "Crystal CS4281", sizeof(info.name)); + info.modify_counter = s->mix.modcnt; + if (copy_to_user(argp, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strlcpy(info.id, "CS4281", sizeof(info.id)); + strlcpy(info.name, "Crystal CS4281", sizeof(info.name)); + if (copy_to_user(argp, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int __user *) argp); + + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + + // If ioctl has only the SIOC_READ bit(bit 31) + // on, process the only-read commands. + if (_SIOC_DIR(cmd) == _SIOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: // Arg contains a bit for each recording source + cs4281_read_ac97(s, BA0_AC97_RECORD_SELECT, &temp1); + return put_user(mixer_src[temp1&7], (int __user *)argp); + + case SOUND_MIXER_DEVMASK: // Arg contains a bit for each supported device + return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | + SOUND_MASK_CD | SOUND_MASK_LINE | + SOUND_MASK_LINE1 | SOUND_MASK_MIC | + SOUND_MASK_VOLUME | + SOUND_MASK_RECLEV | + SOUND_MASK_SPEAKER, (int __user *)argp); + + case SOUND_MIXER_RECMASK: // Arg contains a bit for each supported recording source + return put_user(SOUND_MASK_LINE | SOUND_MASK_MIC | + SOUND_MASK_CD | SOUND_MASK_VOLUME | + SOUND_MASK_LINE1, (int __user *) argp); + + case SOUND_MIXER_STEREODEVS: // Mixer channels supporting stereo + return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH | + SOUND_MASK_CD | SOUND_MASK_LINE | + SOUND_MASK_LINE1 | SOUND_MASK_MIC | + SOUND_MASK_VOLUME | + SOUND_MASK_RECLEV, (int __user *)argp); + + case SOUND_MIXER_CAPS: + return put_user(SOUND_CAP_EXCL_INPUT, (int __user *)argp); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES + || !(vidx = mixtable1[i])) + return -EINVAL; + return put_user(s->mix.vol[vidx - 1], (int __user *)argp); + } + } + // If ioctl doesn't have both the SIOC_READ and + // the SIOC_WRITE bit set, return invalid. + if (_SIOC_DIR(cmd) != (_SIOC_READ | _SIOC_WRITE)) + return -EINVAL; + + // Increment the count of volume writes. + s->mix.modcnt++; + + // Isolate the command; it must be a write. + switch (_IOC_NR(cmd)) { + + case SOUND_MIXER_RECSRC: // Arg contains a bit for each recording source + if (get_user(val, (int __user *)argp)) + return -EFAULT; + i = hweight32(val); // i = # bits on in val. + if (i != 1) // One & only 1 bit must be on. + return 0; + for (i = 0; i < sizeof(mixer_src) / sizeof(int); i++) { + if (val == mixer_src[i]) { + temp1 = (i << 8) | i; + cs4281_write_ac97(s, + BA0_AC97_RECORD_SELECT, + temp1); + return 0; + } + } + return 0; + + case SOUND_MIXER_VOLUME: + if (get_user(val, (int __user *)argp)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; // Max soundcard.h vol is 100. + if (l < 6) { + rl = 63; + l = 0; + } else + rl = attentbl[(10 * l) / 100]; // Convert 0-100 vol to 63-0 atten. + + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; // Max right volume is 100, too + if (r < 6) { + rr = 63; + r = 0; + } else + rr = attentbl[(10 * r) / 100]; // Convert volume to attenuation. + + if ((rl > 60) && (rr > 60)) // If both l & r are 'low', + temp1 = 0x8000; // turn on the mute bit. + else + temp1 = 0; + + temp1 |= (rl << 8) | rr; + + cs4281_write_ac97(s, BA0_AC97_MASTER_VOLUME, temp1); + cs4281_write_ac97(s, BA0_AC97_HEADPHONE_VOLUME, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[8] = ((unsigned int) r << 8) | l; +#else + s->mix.vol[8] = val; +#endif + return put_user(s->mix.vol[8], (int __user *)argp); + + case SOUND_MIXER_SPEAKER: + if (get_user(val, (int __user *)argp)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (l < 3) { + rl = 0; + l = 0; + } else { + rl = (l * 2 - 5) / 13; // Convert 0-100 range to 0-15. + l = (rl * 13 + 5) / 2; + } + + if (rl < 3) { + temp1 = 0x8000; + rl = 0; + } else + temp1 = 0; + rl = 15 - rl; // Convert volume to attenuation. + temp1 |= rl << 1; + cs4281_write_ac97(s, BA0_AC97_PC_BEEP_VOLUME, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[6] = l << 8; +#else + s->mix.vol[6] = val; +#endif + return put_user(s->mix.vol[6], (int __user *)argp); + + case SOUND_MIXER_RECLEV: + if (get_user(val, (int __user *)argp)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + rl = (l * 2 - 5) / 13; // Convert 0-100 scale to 0-15. + rr = (r * 2 - 5) / 13; + if (rl < 3 && rr < 3) + temp1 = 0x8000; + else + temp1 = 0; + + temp1 = temp1 | (rl << 8) | rr; + cs4281_write_ac97(s, BA0_AC97_RECORD_GAIN, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[7] = ((unsigned int) r << 8) | l; +#else + s->mix.vol[7] = val; +#endif + return put_user(s->mix.vol[7], (int __user *)argp); + + case SOUND_MIXER_MIC: + if (get_user(val, (int __user *)argp)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (l < 1) { + l = 0; + rl = 0; + } else { + rl = ((unsigned) l * 5 - 4) / 16; // Convert 0-100 range to 0-31. + l = (rl * 16 + 4) / 5; + } + cs4281_read_ac97(s, BA0_AC97_MIC_VOLUME, &temp1); + temp1 &= 0x40; // Isolate 20db gain bit. + if (rl < 3) { + temp1 |= 0x8000; + rl = 0; + } + rl = 31 - rl; // Convert volume to attenuation. + temp1 |= rl; + cs4281_write_ac97(s, BA0_AC97_MIC_VOLUME, temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[5] = val << 8; +#else + s->mix.vol[5] = val; +#endif + return put_user(s->mix.vol[5], (int __user *)argp); + + + case SOUND_MIXER_SYNTH: + if (get_user(val, (int __user *)argp)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (get_user(val, (int __user *)argp)) + return -EFAULT; + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + rl = (l * 2 - 11) / 3; // Convert 0-100 range to 0-63. + rr = (r * 2 - 11) / 3; + if (rl < 3) // If l is low, turn on + temp1 = 0x0080; // the mute bit. + else + temp1 = 0; + + rl = 63 - rl; // Convert vol to attenuation. + writel(temp1 | rl, s->pBA0 + BA0_FMLVC); + if (rr < 3) // If rr is low, turn on + temp1 = 0x0080; // the mute bit. + else + temp1 = 0; + rr = 63 - rr; // Convert vol to attenuation. + writel(temp1 | rr, s->pBA0 + BA0_FMRVC); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[4] = (r << 8) | l; +#else + s->mix.vol[4] = val; +#endif + return put_user(s->mix.vol[4], (int __user *)argp); + + + default: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4281: mixer_ioctl(): default\n")); + + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) + return -EINVAL; + if (get_user(val, (int __user *)argp)) + return -EFAULT; + l = val & 0xff; + if (l > 100) + l = 100; + if (l < 1) { + l = 0; + rl = 31; + } else + rl = (attentbl[(l * 10) / 100]) >> 1; + + r = (val >> 8) & 0xff; + if (r > 100) + r = 100; + if (r < 1) { + r = 0; + rr = 31; + } else + rr = (attentbl[(r * 10) / 100]) >> 1; + if ((rl > 30) && (rr > 30)) + temp1 = 0x8000; + else + temp1 = 0; + temp1 = temp1 | (rl << 8) | rr; + cs4281_write_ac97(s, mixreg[vidx - 1], temp1); + +#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS + s->mix.vol[vidx - 1] = ((unsigned int) r << 8) | l; +#else + s->mix.vol[vidx - 1] = val; +#endif +#ifndef NOT_CS4281_PM + CS_DBGOUT(CS_PM, 9, printk(KERN_INFO + "write ac97 mixreg[%d]=0x%x mix.vol[]=0x%x\n", + vidx-1,temp1,s->mix.vol[vidx-1])); +#endif + return put_user(s->mix.vol[vidx - 1], (int __user *)argp); + } +} + + +// --------------------------------------------------------------------- + +static int cs4281_open_mixdev(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + struct cs4281_state *s=NULL; + struct list_head *entry; + + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, + printk(KERN_INFO "cs4281: cs4281_open_mixdev()+\n")); + + list_for_each(entry, &cs4281_devs) + { + s = list_entry(entry, struct cs4281_state, list); + if(s->dev_mixer == minor) + break; + } + if (!s) + { + CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, + printk(KERN_INFO "cs4281: cs4281_open_mixdev()- -ENODEV\n")); + return -ENODEV; + } + VALIDATE_STATE(s); + file->private_data = s; + + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, + printk(KERN_INFO "cs4281: cs4281_open_mixdev()- 0\n")); + + return nonseekable_open(inode, file); +} + + +static int cs4281_release_mixdev(struct inode *inode, struct file *file) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + + VALIDATE_STATE(s); + return 0; +} + + +static int cs4281_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return mixer_ioctl((struct cs4281_state *) file->private_data, cmd, + arg); +} + + +// ****************************************************************************************** +// Mixer file operations struct. +// ****************************************************************************************** +static /*const */ struct file_operations cs4281_mixer_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = cs4281_ioctl_mixdev, + .open = cs4281_open_mixdev, + .release = cs4281_release_mixdev, +}; + +// --------------------------------------------------------------------- + + +static int drain_adc(struct cs4281_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count; + unsigned tmo; + + if (s->dma_adc.mapped) + return 0; + add_wait_queue(&s->dma_adc.wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_adc.count; + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: drain_adc() %d\n", count)); + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) { + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: drain_adc() count<0\n")); + break; + } + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_adc.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = + 3 * HZ * (count + + s->dma_adc.fragsize) / 2 / s->prop_adc.rate; + if (s->prop_adc.fmt & (AFMT_S16_LE | AFMT_U16_LE)) + tmo >>= 1; + if (s->prop_adc.channels > 1) + tmo >>= 1; + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "cs4281: dma timed out??\n"); + } + remove_wait_queue(&s->dma_adc.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +static int drain_dac(struct cs4281_state *s, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count; + unsigned tmo; + + if (s->dma_dac.mapped) + return 0; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = + 3 * HZ * (count + + s->dma_dac.fragsize) / 2 / s->prop_dac.rate; + if (s->prop_dac.fmt & (AFMT_S16_LE | AFMT_U16_LE)) + tmo >>= 1; + if (s->prop_dac.channels > 1) + tmo >>= 1; + if (!schedule_timeout(tmo + 1)) + printk(KERN_DEBUG "cs4281: dma timed out??\n"); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +//**************************************************************************** +// +// CopySamples copies 16-bit stereo samples from the source to the +// destination, possibly converting down to either 8-bit or mono or both. +// count specifies the number of output bytes to write. +// +// Arguments: +// +// dst - Pointer to a destination buffer. +// src - Pointer to a source buffer +// count - The number of bytes to copy into the destination buffer. +// iChannels - Stereo - 2 +// Mono - 1 +// fmt - AFMT_xxx (soundcard.h formats) +// +// NOTES: only call this routine for conversion to 8bit from 16bit +// +//**************************************************************************** +static void CopySamples(char *dst, char *src, int count, int iChannels, + unsigned fmt) +{ + + unsigned short *psSrc; + long lAudioSample; + + CS_DBGOUT(CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: CopySamples()+ ")); + CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO + " dst=%p src=%p count=%d iChannels=%d fmt=0x%x\n", + dst, src, (unsigned) count, (unsigned) iChannels, (unsigned) fmt)); + + // Gershwin does format conversion in hardware so normally + // we don't do any host based coversion. The data formatter + // truncates 16 bit data to 8 bit and that causes some hiss. + // We have already forced the HW to do 16 bit sampling and + // 2 channel so that we can use software to round instead + // of truncate + + // + // See if the data should be output as 8-bit unsigned stereo. + // or if the data should be output at 8-bit unsigned mono. + // + if ( ((iChannels == 2) && (fmt & AFMT_U8)) || + ((iChannels == 1) && (fmt & AFMT_U8)) ) { + // + // Convert each 16-bit unsigned stereo sample to 8-bit unsigned + // stereo using rounding. + // + psSrc = (unsigned short *) src; + count = count / 2; + while (count--) { + lAudioSample = (long) psSrc[count] + (long) 0x80; + if (lAudioSample > 0xffff) { + lAudioSample = 0xffff; + } + dst[count] = (char) (lAudioSample >> 8); + } + } + // + // check for 8-bit signed stereo. + // + else if ((iChannels == 2) && (fmt & AFMT_S8)) { + // + // Convert each 16-bit stereo sample to 8-bit stereo using rounding. + // + psSrc = (short *) src; + while (count--) { + lAudioSample = + (((long) psSrc[0] + (long) psSrc[1]) / 2); + psSrc += 2; + *dst++ = (char) ((short) lAudioSample >> 8); + } + } + // + // Otherwise, the data should be output as 8-bit signed mono. + // + else if ((iChannels == 1) && (fmt & AFMT_S8)) { + // + // Convert each 16-bit signed mono sample to 8-bit signed mono + // using rounding. + // + psSrc = (short *) src; + count = count / 2; + while (count--) { + lAudioSample = + (((long) psSrc[0] + (long) psSrc[1]) / 2); + if (lAudioSample > 0x7fff) { + lAudioSample = 0x7fff; + } + psSrc += 2; + *dst++ = (char) ((short) lAudioSample >> 8); + } + } +} + +// +// cs_copy_to_user() +// replacement for the standard copy_to_user, to allow for a conversion from +// 16 bit to 8 bit if the record conversion is active. the cs4281 has some +// issues with 8 bit capture, so the driver always captures data in 16 bit +// and then if the user requested 8 bit, converts from 16 to 8 bit. +// +static unsigned cs_copy_to_user(struct cs4281_state *s, void __user *dest, + unsigned *hwsrc, unsigned cnt, + unsigned *copied) +{ + void *src = hwsrc; //default to the standard destination buffer addr + + CS_DBGOUT(CS_FUNCTION, 6, printk(KERN_INFO + "cs_copy_to_user()+ fmt=0x%x fmt_o=0x%x cnt=%d dest=%p\n", + s->prop_adc.fmt, s->prop_adc.fmt_original, + (unsigned) cnt, dest)); + + if (cnt > s->dma_adc.dmasize) { + cnt = s->dma_adc.dmasize; + } + if (!cnt) { + *copied = 0; + return 0; + } + if (s->conversion) { + if (!s->tmpbuff) { + *copied = cnt / 2; + return 0; + } + CopySamples(s->tmpbuff, (void *) hwsrc, cnt, + (unsigned) s->prop_adc.channels, + s->prop_adc.fmt_original); + src = s->tmpbuff; + cnt = cnt / 2; + } + + if (copy_to_user(dest, src, cnt)) { + *copied = 0; + return -EFAULT; + } + *copied = cnt; + CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: cs_copy_to_user()- copied bytes is %d \n", cnt)); + return 0; +} + +// --------------------------------------------------------------------- + +static ssize_t cs4281_read(struct file *file, char __user *buffer, size_t count, + loff_t * ppos) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + unsigned copied = 0; + + CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, + printk(KERN_INFO "cs4281: cs4281_read()+ %Zu \n", count)); + + VALIDATE_STATE(s); + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; +// +// "count" is the amount of bytes to read (from app), is decremented each loop +// by the amount of bytes that have been returned to the user buffer. +// "cnt" is the running total of each read from the buffer (changes each loop) +// "buffer" points to the app's buffer +// "ret" keeps a running total of the amount of bytes that have been copied +// to the user buffer. +// "copied" is the total bytes copied into the user buffer for each loop. +// + while (count > 0) { + CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO + "_read() count>0 count=%Zu .count=%d .swptr=%d .hwptr=%d \n", + count, s->dma_adc.count, + s->dma_adc.swptr, s->dma_adc.hwptr)); + spin_lock_irqsave(&s->lock, flags); + + // get the current copy point of the sw buffer + swptr = s->dma_adc.swptr; + + // cnt is the amount of unread bytes from the end of the + // hw buffer to the current sw pointer + cnt = s->dma_adc.dmasize - swptr; + + // dma_adc.count is the current total bytes that have not been read. + // if the amount of unread bytes from the current sw pointer to the + // end of the buffer is greater than the current total bytes that + // have not been read, then set the "cnt" (unread bytes) to the + // amount of unread bytes. + + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + // + // if we are converting from 8/16 then we need to copy + // twice the number of 16 bit bytes then 8 bit bytes. + // + if (s->conversion) { + if (cnt > (count * 2)) + cnt = (count * 2); + } else { + if (cnt > count) + cnt = count; + } + // + // "cnt" NOW is the smaller of the amount that will be read, + // and the amount that is requested in this read (or partial). + // if there are no bytes in the buffer to read, then start the + // ADC and wait for the interrupt handler to wake us up. + // + if (cnt <= 0) { + + // start up the dma engine and then continue back to the top of + // the loop when wake up occurs. + start_adc(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->dma_adc.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + // there are bytes in the buffer to read. + // copy from the hw buffer over to the user buffer. + // user buffer is designated by "buffer" + // virtual address to copy from is rawbuf+swptr + // the "cnt" is the number of bytes to read. + + CS_DBGOUT(CS_WAVE_READ, 2, printk(KERN_INFO + "_read() copy_to cnt=%d count=%Zu ", cnt, count)); + CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO + " .dmasize=%d .count=%d buffer=%p ret=%Zd\n", + s->dma_adc.dmasize, s->dma_adc.count, buffer, ret)); + + if (cs_copy_to_user + (s, buffer, s->dma_adc.rawbuf + swptr, cnt, &copied)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= copied; + buffer += copied; + ret += copied; + start_adc(s); + } + CS_DBGOUT(CS_FUNCTION | CS_WAVE_READ, 2, + printk(KERN_INFO "cs4281: cs4281_read()- %Zd\n", ret)); + return ret; +} + + +static ssize_t cs4281_write(struct file *file, const char __user *buffer, + size_t count, loff_t * ppos) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr, hwptr, busaddr; + int cnt; + + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, + printk(KERN_INFO "cs4281: cs4281_write()+ count=%Zu\n", + count)); + VALIDATE_STATE(s); + + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + if (s->dma_dac.underrun) { + s->dma_dac.underrun = 0; + hwptr = readl(s->pBA0 + BA0_DCA0); + busaddr = virt_to_bus(s->dma_dac.rawbuf); + hwptr -= (unsigned) busaddr; + s->dma_dac.swptr = s->dma_dac.hwptr = hwptr; + } + swptr = s->dma_dac.swptr; + cnt = s->dma_dac.dmasize - swptr; + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + start_dac(s); + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->dma_dac.wait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) + return ret ? ret : -EFAULT; + swptr = (swptr + cnt) % s->dma_dac.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(s); + } + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE, 2, + printk(KERN_INFO "cs4281: cs4281_write()- %Zd\n", ret)); + return ret; +} + + +static unsigned int cs4281_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + unsigned long flags; + unsigned int mask = 0; + + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO "cs4281: cs4281_poll()+\n")); + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO + "cs4281: cs4281_poll() wait on FMODE_WRITE\n")); + if(!s->dma_dac.ready && prog_dmabuf_dac(s)) + return 0; + poll_wait(file, &s->dma_dac.wait, wait); + } + if (file->f_mode & FMODE_READ) { + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO + "cs4281: cs4281_poll() wait on FMODE_READ\n")); + if(!s->dma_dac.ready && prog_dmabuf_adc(s)) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + spin_lock_irqsave(&s->lock, flags); + cs4281_update_ptr(s,CS_FALSE); + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= + (signed) s->dma_dac.fragsize) { + if (s->dma_dac.wakeup) + mask |= POLLOUT | POLLWRNORM; + else + mask = 0; + s->dma_dac.wakeup = 0; + } + } else { + if ((signed) (s->dma_dac.dmasize/2) >= s->dma_dac.count) + mask |= POLLOUT | POLLWRNORM; + } + } else if (file->f_mode & FMODE_READ) { + if (s->dma_adc.mapped) { + if (s->dma_adc.count >= (signed) s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } else { + if (s->dma_adc.count > 0) + mask |= POLLIN | POLLRDNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4, + printk(KERN_INFO "cs4281: cs4281_poll()- 0x%.8x\n", + mask)); + return mask; +} + + +static int cs4281_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + struct dmabuf *db; + int ret; + unsigned long size; + + CS_DBGOUT(CS_FUNCTION | CS_PARMS | CS_OPEN, 4, + printk(KERN_INFO "cs4281: cs4281_mmap()+\n")); + + VALIDATE_STATE(s); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_dac(s)) != 0) + return ret; + db = &s->dma_dac; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_adc(s)) != 0) + return ret; + db = &s->dma_adc; + } else + return -EINVAL; +// +// only support PLAYBACK for now +// + db = &s->dma_dac; + + if (cs4x_pgoff(vma) != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + return -EINVAL; + if (remap_pfn_range(vma, vma->vm_start, + virt_to_phys(db->rawbuf) >> PAGE_SHIFT, + size, vma->vm_page_prot)) + return -EAGAIN; + db->mapped = 1; + + CS_DBGOUT(CS_FUNCTION | CS_PARMS | CS_OPEN, 4, + printk(KERN_INFO "cs4281: cs4281_mmap()- 0 size=%d\n", + (unsigned) size)); + + return 0; +} + + +static int cs4281_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + int __user *p = (int __user *)arg; + + CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): file=%p cmd=0x%.8x\n", file, cmd)); +#if CSDEBUG + cs_printioctl(cmd); +#endif + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): SOUND_VERSION=0x%.8x\n", + SOUND_VERSION)); + return put_user(SOUND_VERSION, p); + + case SNDCTL_DSP_SYNC: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_SYNC\n")); + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, + 0 /*file->f_flags & O_NONBLOCK */ + ); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | + DSP_CAP_TRIGGER | DSP_CAP_MMAP, + p); + + case SNDCTL_DSP_RESET: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_RESET\n")); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(s->irq); + s->dma_dac.swptr = s->dma_dac.hwptr = + s->dma_dac.count = s->dma_dac.total_bytes = + s->dma_dac.blocks = s->dma_dac.wakeup = 0; + prog_codec(s, CS_TYPE_DAC); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(s->irq); + s->dma_adc.swptr = s->dma_adc.hwptr = + s->dma_adc.count = s->dma_adc.total_bytes = + s->dma_adc.blocks = s->dma_dac.wakeup = 0; + prog_codec(s, CS_TYPE_ADC); + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, p)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_SPEED val=%d\n", val)); + // + // support independent capture and playback channels + // assume that the file mode bit determines the + // direction of the data flow. + // + if (file->f_mode & FMODE_READ) { + if (val >= 0) { + stop_adc(s); + s->dma_adc.ready = 0; + // program sampling rates + if (val > 48000) + val = 48000; + if (val < 6300) + val = 6300; + s->prop_adc.rate = val; + prog_codec(s, CS_TYPE_ADC); + } + } + if (file->f_mode & FMODE_WRITE) { + if (val >= 0) { + stop_dac(s); + s->dma_dac.ready = 0; + // program sampling rates + if (val > 48000) + val = 48000; + if (val < 6300) + val = 6300; + s->prop_dac.rate = val; + prog_codec(s, CS_TYPE_DAC); + } + } + + if (file->f_mode & FMODE_WRITE) + val = s->prop_dac.rate; + else if (file->f_mode & FMODE_READ) + val = s->prop_adc.rate; + + return put_user(val, p); + + case SNDCTL_DSP_STEREO: + if (get_user(val, p)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_STEREO val=%d\n", val)); + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + s->prop_adc.channels = val ? 2 : 1; + prog_codec(s, CS_TYPE_ADC); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + s->prop_dac.channels = val ? 2 : 1; + prog_codec(s, CS_TYPE_DAC); + } + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, p)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_CHANNELS val=%d\n", + val)); + if (val != 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val >= 2) + s->prop_adc.channels = 2; + else + s->prop_adc.channels = 1; + prog_codec(s, CS_TYPE_ADC); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + s->prop_dac.channels = 2; + else + s->prop_dac.channels = 1; + prog_codec(s, CS_TYPE_DAC); + } + } + + if (file->f_mode & FMODE_WRITE) + val = s->prop_dac.channels; + else if (file->f_mode & FMODE_READ) + val = s->prop_adc.channels; + + return put_user(val, p); + + case SNDCTL_DSP_GETFMTS: // Returns a mask + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_GETFMT val=0x%.8x\n", + AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 | + AFMT_U8)); + return put_user(AFMT_S16_LE | AFMT_U16_LE | AFMT_S8 | + AFMT_U8, p); + + case SNDCTL_DSP_SETFMT: + if (get_user(val, p)) + return -EFAULT; + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_SETFMT val=0x%.8x\n", + val)); + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + if (val != AFMT_S16_LE + && val != AFMT_U16_LE && val != AFMT_S8 + && val != AFMT_U8) + val = AFMT_U8; + s->prop_adc.fmt = val; + s->prop_adc.fmt_original = s->prop_adc.fmt; + prog_codec(s, CS_TYPE_ADC); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val != AFMT_S16_LE + && val != AFMT_U16_LE && val != AFMT_S8 + && val != AFMT_U8) + val = AFMT_U8; + s->prop_dac.fmt = val; + s->prop_dac.fmt_original = s->prop_dac.fmt; + prog_codec(s, CS_TYPE_DAC); + } + } else { + if (file->f_mode & FMODE_WRITE) + val = s->prop_dac.fmt_original; + else if (file->f_mode & FMODE_READ) + val = s->prop_adc.fmt_original; + } + CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_SETFMT return val=0x%.8x\n", + val)); + return put_user(val, p); + + case SNDCTL_DSP_POST: + CS_DBGOUT(CS_IOCTL, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): DSP_POST\n")); + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & s->ena & FMODE_READ) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & s->ena & FMODE_WRITE) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, p); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, p)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready + && (ret = prog_dmabuf_adc(s))) + return ret; + start_adc(s); + } else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready + && (ret = prog_dmabuf_dac(s))) + return ret; + start_dac(s); + } else + stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s))) + return val; + spin_lock_irqsave(&s->lock, flags); + cs4281_update_ptr(s,CS_FALSE); + abinfo.fragsize = s->dma_dac.fragsize; + if (s->dma_dac.mapped) + abinfo.bytes = s->dma_dac.dmasize; + else + abinfo.bytes = + s->dma_dac.dmasize - s->dma_dac.count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + CS_DBGOUT(CS_FUNCTION | CS_PARMS, 4, printk(KERN_INFO + "cs4281: cs4281_ioctl(): GETOSPACE .fragsize=%d .bytes=%d .fragstotal=%d .fragments=%d\n", + abinfo.fragsize,abinfo.bytes,abinfo.fragstotal, + abinfo.fragments)); + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user(p, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s))) + return val; + spin_lock_irqsave(&s->lock, flags); + cs4281_update_ptr(s,CS_FALSE); + if (s->conversion) { + abinfo.fragsize = s->dma_adc.fragsize / 2; + abinfo.bytes = s->dma_adc.count / 2; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = + abinfo.bytes >> (s->dma_adc.fragshift - 1); + } else { + abinfo.fragsize = s->dma_adc.fragsize; + abinfo.bytes = s->dma_adc.count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = + abinfo.bytes >> s->dma_adc.fragshift; + } + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user(p, &abinfo, + sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if(!s->dma_dac.ready && prog_dmabuf_dac(s)) + return 0; + spin_lock_irqsave(&s->lock, flags); + cs4281_update_ptr(s,CS_FALSE); + val = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, p); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if(!s->dma_adc.ready && prog_dmabuf_adc(s)) + return 0; + spin_lock_irqsave(&s->lock, flags); + cs4281_update_ptr(s,CS_FALSE); + cinfo.bytes = s->dma_adc.total_bytes; + if (s->dma_adc.mapped) { + cinfo.blocks = + (cinfo.bytes >> s->dma_adc.fragshift) - + s->dma_adc.blocks; + s->dma_adc.blocks = + cinfo.bytes >> s->dma_adc.fragshift; + } else { + if (s->conversion) { + cinfo.blocks = + s->dma_adc.count / + 2 >> (s->dma_adc.fragshift - 1); + } else + cinfo.blocks = + s->dma_adc.count >> s->dma_adc. + fragshift; + } + if (s->conversion) + cinfo.ptr = s->dma_adc.hwptr / 2; + else + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize - 1; + spin_unlock_irqrestore(&s->lock, flags); + if (copy_to_user(p, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if(!s->dma_dac.ready && prog_dmabuf_dac(s)) + return 0; + spin_lock_irqsave(&s->lock, flags); + cs4281_update_ptr(s,CS_FALSE); + cinfo.bytes = s->dma_dac.total_bytes; + if (s->dma_dac.mapped) { + cinfo.blocks = + (cinfo.bytes >> s->dma_dac.fragshift) - + s->dma_dac.blocks; + s->dma_dac.blocks = + cinfo.bytes >> s->dma_dac.fragshift; + } else { + cinfo.blocks = + s->dma_dac.count >> s->dma_dac.fragshift; + } + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize - 1; + spin_unlock_irqrestore(&s->lock, flags); + if (copy_to_user(p, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_dac(s))) + return val; + return put_user(s->dma_dac.fragsize, p); + } + if ((val = prog_dmabuf_adc(s))) + return val; + if (s->conversion) + return put_user(s->dma_adc.fragsize / 2, p); + else + return put_user(s->dma_adc.fragsize, p); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, p)) + return -EFAULT; + return 0; // Say OK, but do nothing. + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) + || (file->f_mode & FMODE_WRITE + && s->dma_dac.subdivision)) return -EINVAL; + if (get_user(val, p)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + else if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + if (file->f_mode & FMODE_READ) + return put_user(s->prop_adc.rate, p); + else if (file->f_mode & FMODE_WRITE) + return put_user(s->prop_dac.rate, p); + + case SOUND_PCM_READ_CHANNELS: + if (file->f_mode & FMODE_READ) + return put_user(s->prop_adc.channels, p); + else if (file->f_mode & FMODE_WRITE) + return put_user(s->prop_dac.channels, p); + + case SOUND_PCM_READ_BITS: + if (file->f_mode & FMODE_READ) + return + put_user( + (s->prop_adc. + fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16, + p); + else if (file->f_mode & FMODE_WRITE) + return + put_user( + (s->prop_dac. + fmt & (AFMT_S8 | AFMT_U8)) ? 8 : 16, + p); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + return mixer_ioctl(s, cmd, arg); +} + + +static int cs4281_release(struct inode *inode, struct file *file) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + + CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk(KERN_INFO + "cs4281: cs4281_release(): inode=%p file=%p f_mode=%d\n", + inode, file, file->f_mode)); + + VALIDATE_STATE(s); + + if (file->f_mode & FMODE_WRITE) { + drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem_dac); + stop_dac(s); + dealloc_dmabuf(s, &s->dma_dac); + s->open_mode &= ~FMODE_WRITE; + up(&s->open_sem_dac); + wake_up(&s->open_wait_dac); + } + if (file->f_mode & FMODE_READ) { + drain_adc(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem_adc); + stop_adc(s); + dealloc_dmabuf(s, &s->dma_adc); + s->open_mode &= ~FMODE_READ; + up(&s->open_sem_adc); + wake_up(&s->open_wait_adc); + } + return 0; +} + +static int cs4281_open(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + struct cs4281_state *s=NULL; + struct list_head *entry; + + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs4281: cs4281_open(): inode=%p file=%p f_mode=0x%x\n", + inode, file, file->f_mode)); + + list_for_each(entry, &cs4281_devs) + { + s = list_entry(entry, struct cs4281_state, list); + + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + if (entry == &cs4281_devs) + return -ENODEV; + if (!s) { + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs4281: cs4281_open(): Error - unable to find audio state struct\n")); + return -ENODEV; + } + VALIDATE_STATE(s); + file->private_data = s; + + // wait for device to become free + if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) { + CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, printk(KERN_INFO + "cs4281: cs4281_open(): Error - must open READ and/or WRITE\n")); + return -ENODEV; + } + if (file->f_mode & FMODE_WRITE) { + down(&s->open_sem_dac); + while (s->open_mode & FMODE_WRITE) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem_dac); + return -EBUSY; + } + up(&s->open_sem_dac); + interruptible_sleep_on(&s->open_wait_dac); + + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem_dac); + } + } + if (file->f_mode & FMODE_READ) { + down(&s->open_sem_adc); + while (s->open_mode & FMODE_READ) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem_adc); + return -EBUSY; + } + up(&s->open_sem_adc); + interruptible_sleep_on(&s->open_wait_adc); + + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem_adc); + } + } + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + if (file->f_mode & FMODE_READ) { + s->prop_adc.fmt = AFMT_U8; + s->prop_adc.fmt_original = s->prop_adc.fmt; + s->prop_adc.channels = 1; + s->prop_adc.rate = 8000; + s->prop_adc.clkdiv = 96 | 0x80; + s->conversion = 0; + s->ena &= ~FMODE_READ; + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = + s->dma_adc.subdivision = 0; + up(&s->open_sem_adc); + + if (prog_dmabuf_adc(s)) { + CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR + "cs4281: adc Program dmabufs failed.\n")); + cs4281_release(inode, file); + return -ENOMEM; + } + prog_codec(s, CS_TYPE_ADC); + } + if (file->f_mode & FMODE_WRITE) { + s->prop_dac.fmt = AFMT_U8; + s->prop_dac.fmt_original = s->prop_dac.fmt; + s->prop_dac.channels = 1; + s->prop_dac.rate = 8000; + s->prop_dac.clkdiv = 96 | 0x80; + s->conversion = 0; + s->ena &= ~FMODE_WRITE; + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = + s->dma_dac.subdivision = 0; + up(&s->open_sem_dac); + + if (prog_dmabuf_dac(s)) { + CS_DBGOUT(CS_OPEN | CS_ERROR, 2, printk(KERN_ERR + "cs4281: dac Program dmabufs failed.\n")); + cs4281_release(inode, file); + return -ENOMEM; + } + prog_codec(s, CS_TYPE_DAC); + } + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, + printk(KERN_INFO "cs4281: cs4281_open()- 0\n")); + return nonseekable_open(inode, file); +} + + +// ****************************************************************************************** +// Wave (audio) file operations struct. +// ****************************************************************************************** +static /*const */ struct file_operations cs4281_audio_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = cs4281_read, + .write = cs4281_write, + .poll = cs4281_poll, + .ioctl = cs4281_ioctl, + .mmap = cs4281_mmap, + .open = cs4281_open, + .release = cs4281_release, +}; + +// --------------------------------------------------------------------- + +// hold spinlock for the following! +static void cs4281_handle_midi(struct cs4281_state *s) +{ + unsigned char ch; + int wake; + unsigned temp1; + + wake = 0; + while (!(readl(s->pBA0 + BA0_MIDSR) & 0x80)) { + ch = readl(s->pBA0 + BA0_MIDRP); + if (s->midi.icnt < MIDIINBUF) { + s->midi.ibuf[s->midi.iwr] = ch; + s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF; + s->midi.icnt++; + } + wake = 1; + } + if (wake) + wake_up(&s->midi.iwait); + wake = 0; + while (!(readl(s->pBA0 + BA0_MIDSR) & 0x40) && s->midi.ocnt > 0) { + temp1 = (s->midi.obuf[s->midi.ord]) & 0x000000ff; + writel(temp1, s->pBA0 + BA0_MIDWP); + s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF; + s->midi.ocnt--; + if (s->midi.ocnt < MIDIOUTBUF - 16) + wake = 1; + } + if (wake) + wake_up(&s->midi.owait); +} + + + +static irqreturn_t cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct cs4281_state *s = (struct cs4281_state *) dev_id; + unsigned int temp1; + + // fastpath out, to ease interrupt sharing + temp1 = readl(s->pBA0 + BA0_HISR); // Get Int Status reg. + + CS_DBGOUT(CS_INTERRUPT, 6, printk(KERN_INFO + "cs4281: cs4281_interrupt() BA0_HISR=0x%.8x\n", temp1)); +/* +* If not DMA or MIDI interrupt, then just return. +*/ + if (!(temp1 & (HISR_DMA0 | HISR_DMA1 | HISR_MIDI))) { + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); + CS_DBGOUT(CS_INTERRUPT, 9, printk(KERN_INFO + "cs4281: cs4281_interrupt(): returning not cs4281 interrupt.\n")); + return IRQ_NONE; + } + + if (temp1 & HISR_DMA0) // If play interrupt, + readl(s->pBA0 + BA0_HDSR0); // clear the source. + + if (temp1 & HISR_DMA1) // Same for play. + readl(s->pBA0 + BA0_HDSR1); + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Local EOI + + spin_lock(&s->lock); + cs4281_update_ptr(s,CS_TRUE); + cs4281_handle_midi(s); + spin_unlock(&s->lock); + return IRQ_HANDLED; +} + +// ************************************************************************** + +static void cs4281_midi_timer(unsigned long data) +{ + struct cs4281_state *s = (struct cs4281_state *) data; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + cs4281_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + s->midi.timer.expires = jiffies + 1; + add_timer(&s->midi.timer); +} + + +// --------------------------------------------------------------------- + +static ssize_t cs4281_midi_read(struct file *file, char __user *buffer, + size_t count, loff_t * ppos) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.ird; + cnt = MIDIINBUF - ptr; + if (s->midi.icnt < cnt) + cnt = s->midi.icnt; + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->midi.iwait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIINBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.ird = ptr; + s->midi.icnt -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + } + return ret; +} + + +static ssize_t cs4281_midi_write(struct file *file, const char __user *buffer, + size_t count, loff_t * ppos) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + ssize_t ret; + unsigned long flags; + unsigned ptr; + int cnt; + + VALIDATE_STATE(s); + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + ptr = s->midi.owr; + cnt = MIDIOUTBUF - ptr; + if (s->midi.ocnt + cnt > MIDIOUTBUF) + cnt = MIDIOUTBUF - s->midi.ocnt; + if (cnt <= 0) + cs4281_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (file->f_flags & O_NONBLOCK) + return ret ? ret : -EAGAIN; + interruptible_sleep_on(&s->midi.owait); + if (signal_pending(current)) + return ret ? ret : -ERESTARTSYS; + continue; + } + if (copy_from_user(s->midi.obuf + ptr, buffer, cnt)) + return ret ? ret : -EFAULT; + ptr = (ptr + cnt) % MIDIOUTBUF; + spin_lock_irqsave(&s->lock, flags); + s->midi.owr = ptr; + s->midi.ocnt += cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + spin_lock_irqsave(&s->lock, flags); + cs4281_handle_midi(s); + spin_unlock_irqrestore(&s->lock, flags); + } + return ret; +} + + +static unsigned int cs4281_midi_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_flags & FMODE_WRITE) + poll_wait(file, &s->midi.owait, wait); + if (file->f_flags & FMODE_READ) + poll_wait(file, &s->midi.iwait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_flags & FMODE_READ) { + if (s->midi.icnt > 0) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_flags & FMODE_WRITE) { + if (s->midi.ocnt < MIDIOUTBUF) + mask |= POLLOUT | POLLWRNORM; + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + + +static int cs4281_midi_open(struct inode *inode, struct file *file) +{ + unsigned long flags, temp1; + unsigned int minor = iminor(inode); + struct cs4281_state *s=NULL; + struct list_head *entry; + list_for_each(entry, &cs4281_devs) + { + s = list_entry(entry, struct cs4281_state, list); + + if (s->dev_midi == minor) + break; + } + + if (entry == &cs4281_devs) + return -ENODEV; + if (!s) + { + CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO + "cs4281: cs4281_open(): Error - unable to find audio state struct\n")); + return -ENODEV; + } + VALIDATE_STATE(s); + file->private_data = s; + // wait for device to become free + down(&s->open_sem); + while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + writel(1, s->pBA0 + BA0_MIDCR); // Reset the interface. + writel(0, s->pBA0 + BA0_MIDCR); // Return to normal mode. + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + writel(0x0000000f, s->pBA0 + BA0_MIDCR); // Enable transmit, record, ints. + temp1 = readl(s->pBA0 + BA0_HIMR); + writel(temp1 & 0xffbfffff, s->pBA0 + BA0_HIMR); // Enable midi int. recognition. + writel(HICR_IEV | HICR_CHGM, s->pBA0 + BA0_HICR); // Enable interrupts + init_timer(&s->midi.timer); + s->midi.timer.expires = jiffies + 1; + s->midi.timer.data = (unsigned long) s; + s->midi.timer.function = cs4281_midi_timer; + add_timer(&s->midi.timer); + } + if (file->f_mode & FMODE_READ) { + s->midi.ird = s->midi.iwr = s->midi.icnt = 0; + } + if (file->f_mode & FMODE_WRITE) { + s->midi.ord = s->midi.owr = s->midi.ocnt = 0; + } + spin_unlock_irqrestore(&s->lock, flags); + s->open_mode |= + (file-> + f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | + FMODE_MIDI_WRITE); + up(&s->open_sem); + return nonseekable_open(inode, file); +} + + +static int cs4281_midi_release(struct inode *inode, struct file *file) +{ + struct cs4281_state *s = + (struct cs4281_state *) file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned count, tmo; + + VALIDATE_STATE(s); + + if (file->f_mode & FMODE_WRITE) { + add_wait_queue(&s->midi.owait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->midi.ocnt; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (file->f_flags & O_NONBLOCK) { + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + tmo = (count * HZ) / 3100; + if (!schedule_timeout(tmo ? : 1) && tmo) + printk(KERN_DEBUG + "cs4281: midi timed out??\n"); + } + remove_wait_queue(&s->midi.owait, &wait); + current->state = TASK_RUNNING; + } + down(&s->open_sem); + s->open_mode &= + (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ | + FMODE_MIDI_WRITE); + spin_lock_irqsave(&s->lock, flags); + if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) { + writel(0, s->pBA0 + BA0_MIDCR); // Disable Midi interrupts. + del_timer(&s->midi.timer); + } + spin_unlock_irqrestore(&s->lock, flags); + up(&s->open_sem); + wake_up(&s->open_wait); + return 0; +} + +// ****************************************************************************************** +// Midi file operations struct. +// ****************************************************************************************** +static /*const */ struct file_operations cs4281_midi_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = cs4281_midi_read, + .write = cs4281_midi_write, + .poll = cs4281_midi_poll, + .open = cs4281_midi_open, + .release = cs4281_midi_release, +}; + + +// --------------------------------------------------------------------- + +// maximum number of devices +#define NR_DEVICE 8 // Only eight devices supported currently. + +// --------------------------------------------------------------------- + +static struct initvol { + int mixch; + int vol; +} initvol[] __devinitdata = { + + { + SOUND_MIXER_WRITE_VOLUME, 0x4040}, { + SOUND_MIXER_WRITE_PCM, 0x4040}, { + SOUND_MIXER_WRITE_SYNTH, 0x4040}, { + SOUND_MIXER_WRITE_CD, 0x4040}, { + SOUND_MIXER_WRITE_LINE, 0x4040}, { + SOUND_MIXER_WRITE_LINE1, 0x4040}, { + SOUND_MIXER_WRITE_RECLEV, 0x0000}, { + SOUND_MIXER_WRITE_SPEAKER, 0x4040}, { + SOUND_MIXER_WRITE_MIC, 0x0000} +}; + + +#ifndef NOT_CS4281_PM +static void __devinit cs4281_BuildFIFO( + struct cs4281_pipeline *p, + struct cs4281_state *s) +{ + switch(p->number) + { + case 0: /* playback */ + { + p->u32FCRnAddress = BA0_FCR0; + p->u32FSICnAddress = BA0_FSIC0; + p->u32FPDRnAddress = BA0_FPDR0; + break; + } + case 1: /* capture */ + { + p->u32FCRnAddress = BA0_FCR1; + p->u32FSICnAddress = BA0_FSIC1; + p->u32FPDRnAddress = BA0_FPDR1; + break; + } + + case 2: + { + p->u32FCRnAddress = BA0_FCR2; + p->u32FSICnAddress = BA0_FSIC2; + p->u32FPDRnAddress = BA0_FPDR2; + break; + } + case 3: + { + p->u32FCRnAddress = BA0_FCR3; + p->u32FSICnAddress = BA0_FSIC3; + p->u32FPDRnAddress = BA0_FPDR3; + break; + } + default: + break; + } + // + // first read the hardware to initialize the member variables + // + p->u32FCRnValue = readl(s->pBA0 + p->u32FCRnAddress); + p->u32FSICnValue = readl(s->pBA0 + p->u32FSICnAddress); + p->u32FPDRnValue = readl(s->pBA0 + p->u32FPDRnAddress); + +} + +static void __devinit cs4281_BuildDMAengine( + struct cs4281_pipeline *p, + struct cs4281_state *s) +{ +/* +* initialize all the addresses of this pipeline dma info. +*/ + switch(p->number) + { + case 0: /* playback */ + { + p->u32DBAnAddress = BA0_DBA0; + p->u32DCAnAddress = BA0_DCA0; + p->u32DBCnAddress = BA0_DBC0; + p->u32DCCnAddress = BA0_DCC0; + p->u32DMRnAddress = BA0_DMR0; + p->u32DCRnAddress = BA0_DCR0; + p->u32HDSRnAddress = BA0_HDSR0; + break; + } + + case 1: /* capture */ + { + p->u32DBAnAddress = BA0_DBA1; + p->u32DCAnAddress = BA0_DCA1; + p->u32DBCnAddress = BA0_DBC1; + p->u32DCCnAddress = BA0_DCC1; + p->u32DMRnAddress = BA0_DMR1; + p->u32DCRnAddress = BA0_DCR1; + p->u32HDSRnAddress = BA0_HDSR1; + break; + } + + case 2: + { + p->u32DBAnAddress = BA0_DBA2; + p->u32DCAnAddress = BA0_DCA2; + p->u32DBCnAddress = BA0_DBC2; + p->u32DCCnAddress = BA0_DCC2; + p->u32DMRnAddress = BA0_DMR2; + p->u32DCRnAddress = BA0_DCR2; + p->u32HDSRnAddress = BA0_HDSR2; + break; + } + + case 3: + { + p->u32DBAnAddress = BA0_DBA3; + p->u32DCAnAddress = BA0_DCA3; + p->u32DBCnAddress = BA0_DBC3; + p->u32DCCnAddress = BA0_DCC3; + p->u32DMRnAddress = BA0_DMR3; + p->u32DCRnAddress = BA0_DCR3; + p->u32HDSRnAddress = BA0_HDSR3; + break; + } + default: + break; + } + +// +// Initialize the dma values for this pipeline +// + p->u32DBAnValue = readl(s->pBA0 + p->u32DBAnAddress); + p->u32DBCnValue = readl(s->pBA0 + p->u32DBCnAddress); + p->u32DMRnValue = readl(s->pBA0 + p->u32DMRnAddress); + p->u32DCRnValue = readl(s->pBA0 + p->u32DCRnAddress); + +} + +static void __devinit cs4281_InitPM(struct cs4281_state *s) +{ + int i; + struct cs4281_pipeline *p; + + for(i=0;i<CS4281_NUMBER_OF_PIPELINES;i++) + { + p = &s->pl[i]; + p->number = i; + cs4281_BuildDMAengine(p,s); + cs4281_BuildFIFO(p,s); + /* + * currently only 2 pipelines are used + * so, only set the valid bit on the playback and capture. + */ + if( (i == CS4281_PLAYBACK_PIPELINE_NUMBER) || + (i == CS4281_CAPTURE_PIPELINE_NUMBER)) + p->flags |= CS4281_PIPELINE_VALID; + } + s->pm.u32SSPM_BITS = 0x7e; /* rev c, use 0x7c for rev a or b */ +} +#endif + +static int __devinit cs4281_probe(struct pci_dev *pcidev, + const struct pci_device_id *pciid) +{ +#ifndef NOT_CS4281_PM + struct pm_dev *pmdev; +#endif + struct cs4281_state *s; + dma_addr_t dma_mask; + mm_segment_t fs; + int i, val; + unsigned int temp1, temp2; + + CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, + printk(KERN_INFO "cs4281: probe()+\n")); + + if (pci_enable_device(pcidev)) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4281: pci_enable_device() failed\n")); + return -1; + } + if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_MEM) || + !(pci_resource_flags(pcidev, 1) & IORESOURCE_MEM)) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe()- Memory region not assigned\n")); + return -ENODEV; + } + if (pcidev->irq == 0) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() IRQ not assigned\n")); + return -ENODEV; + } + dma_mask = 0xffffffff; /* this enables playback and recording */ + i = pci_set_dma_mask(pcidev, dma_mask); + if (i) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() architecture does not support 32bit PCI busmaster DMA\n")); + return i; + } + if (!(s = kmalloc(sizeof(struct cs4281_state), GFP_KERNEL))) { + CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() no memory for state struct.\n")); + return -1; + } + memset(s, 0, sizeof(struct cs4281_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_waitqueue_head(&s->open_wait_adc); + init_waitqueue_head(&s->open_wait_dac); + init_waitqueue_head(&s->midi.iwait); + init_waitqueue_head(&s->midi.owait); + init_MUTEX(&s->open_sem); + init_MUTEX(&s->open_sem_adc); + init_MUTEX(&s->open_sem_dac); + spin_lock_init(&s->lock); + s->pBA0phys = pci_resource_start(pcidev, 0); + s->pBA1phys = pci_resource_start(pcidev, 1); + + /* Convert phys to linear. */ + s->pBA0 = ioremap_nocache(s->pBA0phys, 4096); + if (!s->pBA0) { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR + "cs4281: BA0 I/O mapping failed. Skipping part.\n")); + goto err_free; + } + s->pBA1 = ioremap_nocache(s->pBA1phys, 65536); + if (!s->pBA1) { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR + "cs4281: BA1 I/O mapping failed. Skipping part.\n")); + goto err_unmap; + } + + temp1 = readl(s->pBA0 + BA0_PCICFG00); + temp2 = readl(s->pBA0 + BA0_PCICFG04); + + CS_DBGOUT(CS_INIT, 2, + printk(KERN_INFO + "cs4281: probe() BA0=0x%.8x BA1=0x%.8x pBA0=%p pBA1=%p \n", + (unsigned) temp1, (unsigned) temp2, s->pBA0, s->pBA1)); + CS_DBGOUT(CS_INIT, 2, + printk(KERN_INFO + "cs4281: probe() pBA0phys=0x%.8x pBA1phys=0x%.8x\n", + (unsigned) s->pBA0phys, (unsigned) s->pBA1phys)); + +#ifndef NOT_CS4281_PM + s->pm.flags = CS4281_PM_IDLE; +#endif + temp1 = cs4281_hw_init(s); + if (temp1) { + CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_ERR + "cs4281: cs4281_hw_init() failed. Skipping part.\n")); + goto err_irq; + } + s->magic = CS4281_MAGIC; + s->pcidev = pcidev; + s->irq = pcidev->irq; + if (request_irq + (s->irq, cs4281_interrupt, SA_SHIRQ, "Crystal CS4281", s)) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, + printk(KERN_ERR "cs4281: irq %u in use\n", s->irq)); + goto err_irq; + } + if ((s->dev_audio = register_sound_dsp(&cs4281_audio_fops, -1)) < + 0) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() register_sound_dsp() failed.\n")); + goto err_dev1; + } + if ((s->dev_mixer = register_sound_mixer(&cs4281_mixer_fops, -1)) < + 0) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() register_sound_mixer() failed.\n")); + goto err_dev2; + } + if ((s->dev_midi = register_sound_midi(&cs4281_midi_fops, -1)) < 0) { + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR + "cs4281: probe() register_sound_midi() failed.\n")); + goto err_dev3; + } +#ifndef NOT_CS4281_PM + cs4281_InitPM(s); + pmdev = cs_pm_register(PM_PCI_DEV, PM_PCI_ID(pcidev), cs4281_pm_callback); + if (pmdev) + { + CS_DBGOUT(CS_INIT | CS_PM, 4, printk(KERN_INFO + "cs4281: probe() pm_register() succeeded (%p).\n", pmdev)); + pmdev->data = s; + } + else + { + CS_DBGOUT(CS_INIT | CS_PM | CS_ERROR, 0, printk(KERN_INFO + "cs4281: probe() pm_register() failed (%p).\n", pmdev)); + s->pm.flags |= CS4281_PM_NOT_REGISTERED; + } +#endif + + pci_set_master(pcidev); // enable bus mastering + + fs = get_fs(); + set_fs(KERNEL_DS); + val = SOUND_MASK_LINE; + mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long) &val); + for (i = 0; i < sizeof(initvol) / sizeof(initvol[0]); i++) { + val = initvol[i].vol; + mixer_ioctl(s, initvol[i].mixch, (unsigned long) &val); + } + val = 1; // enable mic preamp + mixer_ioctl(s, SOUND_MIXER_PRIVATE1, (unsigned long) &val); + set_fs(fs); + + pci_set_drvdata(pcidev, s); + list_add(&s->list, &cs4281_devs); + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: probe()- device allocated successfully\n")); + return 0; + + err_dev3: + unregister_sound_mixer(s->dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + free_irq(s->irq, s); + err_irq: + iounmap(s->pBA1); + err_unmap: + iounmap(s->pBA0); + err_free: + kfree(s); + + CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO + "cs4281: probe()- no device allocated\n")); + return -ENODEV; +} // probe_cs4281 + + +// --------------------------------------------------------------------- + +static void __devexit cs4281_remove(struct pci_dev *pci_dev) +{ + struct cs4281_state *s = pci_get_drvdata(pci_dev); + // stop DMA controller + synchronize_irq(s->irq); + free_irq(s->irq, s); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_midi(s->dev_midi); + iounmap(s->pBA1); + iounmap(s->pBA0); + pci_set_drvdata(pci_dev,NULL); + list_del(&s->list); + kfree(s); + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: cs4281_remove()-: remove successful\n")); +} + +static struct pci_device_id cs4281_pci_tbl[] = { + { + .vendor = PCI_VENDOR_ID_CIRRUS, + .device = PCI_DEVICE_ID_CRYSTAL_CS4281, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, cs4281_pci_tbl); + +static struct pci_driver cs4281_pci_driver = { + .name = "cs4281", + .id_table = cs4281_pci_tbl, + .probe = cs4281_probe, + .remove = __devexit_p(cs4281_remove), + .suspend = CS4281_SUSPEND_TBL, + .resume = CS4281_RESUME_TBL, +}; + +static int __init cs4281_init_module(void) +{ + int rtn = 0; + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO + "cs4281: cs4281_init_module()+ \n")); + printk(KERN_INFO "cs4281: version v%d.%02d.%d time " __TIME__ " " + __DATE__ "\n", CS4281_MAJOR_VERSION, CS4281_MINOR_VERSION, + CS4281_ARCH); + rtn = pci_module_init(&cs4281_pci_driver); + + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cs4281_init_module()- (%d)\n",rtn)); + return rtn; +} + +static void __exit cs4281_cleanup_module(void) +{ + pci_unregister_driver(&cs4281_pci_driver); +#ifndef NOT_CS4281_PM + cs_pm_unregister_all(cs4281_pm_callback); +#endif + CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, + printk(KERN_INFO "cs4281: cleanup_cs4281() finished\n")); +} +// --------------------------------------------------------------------- + +MODULE_AUTHOR("gw boynton, audio@crystal.cirrus.com"); +MODULE_DESCRIPTION("Cirrus Logic CS4281 Driver"); +MODULE_LICENSE("GPL"); + +// --------------------------------------------------------------------- + +module_init(cs4281_init_module); +module_exit(cs4281_cleanup_module); + diff --git a/sound/oss/cs4281/cs4281pm-24.c b/sound/oss/cs4281/cs4281pm-24.c new file mode 100644 index 000000000000..d2a453aff0aa --- /dev/null +++ b/sound/oss/cs4281/cs4281pm-24.c @@ -0,0 +1,84 @@ +/******************************************************************************* +* +* "cs4281pm.c" -- Cirrus Logic-Crystal CS4281 linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- tom woller (twoller@crystal.cirrus.com) or +* (audio@crystal.cirrus.com). +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* 12/22/00 trw - new file. +* +*******************************************************************************/ + +#ifndef NOT_CS4281_PM +#include <linux/pm.h> + +#define cs_pm_register(a, b, c) pm_register((a), (b), (c)); +#define cs_pm_unregister_all(a) pm_unregister_all((a)); + +static int cs4281_suspend(struct cs4281_state *s); +static int cs4281_resume(struct cs4281_state *s); +/* +* for now (12/22/00) only enable the pm_register PM support. +* allow these table entries to be null. +#define CS4281_SUSPEND_TBL cs4281_suspend_tbl +#define CS4281_RESUME_TBL cs4281_resume_tbl +*/ +#define CS4281_SUSPEND_TBL cs4281_suspend_null +#define CS4281_RESUME_TBL cs4281_resume_null + +static int cs4281_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + struct cs4281_state *state; + + CS_DBGOUT(CS_PM, 2, printk(KERN_INFO + "cs4281: cs4281_pm_callback dev=%p rqst=0x%x state=%p\n", + dev,(unsigned)rqst,data)); + state = (struct cs4281_state *) dev->data; + if (state) { + switch(rqst) { + case PM_SUSPEND: + CS_DBGOUT(CS_PM, 2, printk(KERN_INFO + "cs4281: PM suspend request\n")); + if(cs4281_suspend(state)) + { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO + "cs4281: PM suspend request refused\n")); + return 1; + } + break; + case PM_RESUME: + CS_DBGOUT(CS_PM, 2, printk(KERN_INFO + "cs4281: PM resume request\n")); + if(cs4281_resume(state)) + { + CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO + "cs4281: PM resume request refused\n")); + return 1; + } + break; + } + } + + return 0; +} + +#else /* CS4281_PM */ +#define CS4281_SUSPEND_TBL cs4281_suspend_null +#define CS4281_RESUME_TBL cs4281_resume_null +#endif /* CS4281_PM */ + diff --git a/sound/oss/cs4281/cs4281pm.h b/sound/oss/cs4281/cs4281pm.h new file mode 100644 index 000000000000..b44fdc9ce002 --- /dev/null +++ b/sound/oss/cs4281/cs4281pm.h @@ -0,0 +1,74 @@ +#ifndef NOT_CS4281_PM +/******************************************************************************* +* +* "cs4281pm.h" -- Cirrus Logic-Crystal CS4281 linux audio driver. +* +* Copyright (C) 2000,2001 Cirrus Logic Corp. +* -- tom woller (twoller@crystal.cirrus.com) or +* (audio@crystal.cirrus.com). +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +* 12/22/00 trw - new file. +* +*******************************************************************************/ +/* general pm definitions */ +#define CS4281_AC97_HIGHESTREGTORESTORE 0x26 +#define CS4281_AC97_NUMBER_RESTORE_REGS (CS4281_AC97_HIGHESTREGTORESTORE/2-1) + +/* pipeline definitions */ +#define CS4281_NUMBER_OF_PIPELINES 4 +#define CS4281_PIPELINE_VALID 0x0001 +#define CS4281_PLAYBACK_PIPELINE_NUMBER 0x0000 +#define CS4281_CAPTURE_PIPELINE_NUMBER 0x0001 + +/* PM state defintions */ +#define CS4281_PM_NOT_REGISTERED 0x1000 +#define CS4281_PM_IDLE 0x0001 +#define CS4281_PM_SUSPENDING 0x0002 +#define CS4281_PM_SUSPENDED 0x0004 +#define CS4281_PM_RESUMING 0x0008 +#define CS4281_PM_RESUMED 0x0010 + +struct cs4281_pm { + unsigned long flags; + u32 u32CLKCR1_SAVE,u32SSPMValue,u32PPLVCvalue,u32PPRVCvalue; + u32 u32FMLVCvalue,u32FMRVCvalue,u32GPIORvalue,u32JSCTLvalue,u32SSCR; + u32 u32SRCSA,u32DacASR,u32AdcASR,u32DacSR,u32AdcSR,u32MIDCR_Save; + u32 u32SSPM_BITS; + u32 ac97[CS4281_AC97_NUMBER_RESTORE_REGS]; + u32 u32AC97_master_volume, u32AC97_headphone_volume, u32AC97_master_volume_mono; + u32 u32AC97_pcm_out_volume, u32AC97_powerdown, u32AC97_general_purpose; + u32 u32hwptr_playback,u32hwptr_capture; +}; + +struct cs4281_pipeline { + unsigned flags; + unsigned number; + u32 u32DBAnValue,u32DBCnValue,u32DMRnValue,u32DCRnValue; + u32 u32DBAnAddress,u32DCAnAddress,u32DBCnAddress,u32DCCnAddress; + u32 u32DMRnAddress,u32DCRnAddress,u32HDSRnAddress; + u32 u32DBAn_Save,u32DBCn_Save,u32DMRn_Save,u32DCRn_Save; + u32 u32DCCn_Save,u32DCAn_Save; +/* +* technically, these are fifo variables, but just map the +* first fifo with the first pipeline and then use the fifo +* variables inside of the pipeline struct. +*/ + u32 u32FCRn_Save,u32FSICn_Save; + u32 u32FCRnValue,u32FCRnAddress,u32FSICnValue,u32FSICnAddress; + u32 u32FPDRnValue,u32FPDRnAddress; +}; +#endif |