mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-09 01:28:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
8
sound/pci/oxygen/Makefile
Normal file
8
sound/pci/oxygen/Makefile
Normal file
|
@ -0,0 +1,8 @@
|
|||
snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
|
||||
snd-oxygen-objs := oxygen.o xonar_dg_mixer.o xonar_dg.o
|
||||
snd-virtuoso-objs := virtuoso.o xonar_lib.o \
|
||||
xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o
|
||||
|
||||
obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o
|
||||
obj-$(CONFIG_SND_OXYGEN) += snd-oxygen.o
|
||||
obj-$(CONFIG_SND_VIRTUOSO) += snd-virtuoso.o
|
44
sound/pci/oxygen/ak4396.h
Normal file
44
sound/pci/oxygen/ak4396.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
#ifndef AK4396_H_INCLUDED
|
||||
#define AK4396_H_INCLUDED
|
||||
|
||||
#define AK4396_WRITE 0x2000
|
||||
|
||||
#define AK4396_CONTROL_1 0
|
||||
#define AK4396_CONTROL_2 1
|
||||
#define AK4396_CONTROL_3 2
|
||||
#define AK4396_LCH_ATT 3
|
||||
#define AK4396_RCH_ATT 4
|
||||
|
||||
/* control 1 */
|
||||
#define AK4396_RSTN 0x01
|
||||
#define AK4396_DIF_MASK 0x0e
|
||||
#define AK4396_DIF_16_LSB 0x00
|
||||
#define AK4396_DIF_20_LSB 0x02
|
||||
#define AK4396_DIF_24_MSB 0x04
|
||||
#define AK4396_DIF_24_I2S 0x06
|
||||
#define AK4396_DIF_24_LSB 0x08
|
||||
#define AK4396_ACKS 0x80
|
||||
/* control 2 */
|
||||
#define AK4396_SMUTE 0x01
|
||||
#define AK4396_DEM_MASK 0x06
|
||||
#define AK4396_DEM_441 0x00
|
||||
#define AK4396_DEM_OFF 0x02
|
||||
#define AK4396_DEM_48 0x04
|
||||
#define AK4396_DEM_32 0x06
|
||||
#define AK4396_DFS_MASK 0x18
|
||||
#define AK4396_DFS_NORMAL 0x00
|
||||
#define AK4396_DFS_DOUBLE 0x08
|
||||
#define AK4396_DFS_QUAD 0x10
|
||||
#define AK4396_SLOW 0x20
|
||||
#define AK4396_DZFM 0x40
|
||||
#define AK4396_DZFE 0x80
|
||||
/* control 3 */
|
||||
#define AK4396_DZFB 0x04
|
||||
#define AK4396_DCKB 0x10
|
||||
#define AK4396_DCKS 0x20
|
||||
#define AK4396_DSDM 0x40
|
||||
#define AK4396_D_P_MASK 0x80
|
||||
#define AK4396_PCM 0x00
|
||||
#define AK4396_DSD 0x80
|
||||
|
||||
#endif
|
63
sound/pci/oxygen/cm9780.h
Normal file
63
sound/pci/oxygen/cm9780.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
#ifndef CM9780_H_INCLUDED
|
||||
#define CM9780_H_INCLUDED
|
||||
|
||||
#define CM9780_JACK 0x62
|
||||
#define CM9780_MIXER 0x64
|
||||
#define CM9780_GPIO_SETUP 0x70
|
||||
#define CM9780_GPIO_STATUS 0x72
|
||||
|
||||
/* jack control */
|
||||
#define CM9780_RSOE 0x0001
|
||||
#define CM9780_CBOE 0x0002
|
||||
#define CM9780_SSOE 0x0004
|
||||
#define CM9780_FROE 0x0008
|
||||
#define CM9780_HP2FMICOE 0x0010
|
||||
#define CM9780_CB2MICOE 0x0020
|
||||
#define CM9780_FMIC2LI 0x0040
|
||||
#define CM9780_FMIC2MIC 0x0080
|
||||
#define CM9780_HP2LI 0x0100
|
||||
#define CM9780_HP2MIC 0x0200
|
||||
#define CM9780_MIC2LI 0x0400
|
||||
#define CM9780_MIC2MIC 0x0800
|
||||
#define CM9780_LI2LI 0x1000
|
||||
#define CM9780_LI2MIC 0x2000
|
||||
#define CM9780_LO2LI 0x4000
|
||||
#define CM9780_LO2MIC 0x8000
|
||||
|
||||
/* mixer control */
|
||||
#define CM9780_BSTSEL 0x0001
|
||||
#define CM9780_STRO_MIC 0x0002
|
||||
#define CM9780_SPDI_FREX 0x0004
|
||||
#define CM9780_SPDI_SSEX 0x0008
|
||||
#define CM9780_SPDI_CBEX 0x0010
|
||||
#define CM9780_SPDI_RSEX 0x0020
|
||||
#define CM9780_MIX2FR 0x0040
|
||||
#define CM9780_MIX2SS 0x0080
|
||||
#define CM9780_MIX2CB 0x0100
|
||||
#define CM9780_MIX2RS 0x0200
|
||||
#define CM9780_MIX2FR_EX 0x0400
|
||||
#define CM9780_MIX2SS_EX 0x0800
|
||||
#define CM9780_MIX2CB_EX 0x1000
|
||||
#define CM9780_MIX2RS_EX 0x2000
|
||||
#define CM9780_P47_IO 0x4000
|
||||
#define CM9780_PCBSW 0x8000
|
||||
|
||||
/* GPIO setup */
|
||||
#define CM9780_GPI0EN 0x0001
|
||||
#define CM9780_GPI1EN 0x0002
|
||||
#define CM9780_SENSE_P 0x0004
|
||||
#define CM9780_LOCK_P 0x0008
|
||||
#define CM9780_GPIO0P 0x0010
|
||||
#define CM9780_GPIO1P 0x0020
|
||||
#define CM9780_GPIO0IO 0x0100
|
||||
#define CM9780_GPIO1IO 0x0200
|
||||
|
||||
/* GPIO status */
|
||||
#define CM9780_GPO0 0x0001
|
||||
#define CM9780_GPO1 0x0002
|
||||
#define CM9780_GPIO0S 0x0010
|
||||
#define CM9780_GPIO1S 0x0020
|
||||
#define CM9780_GPII0S 0x0100
|
||||
#define CM9780_GPII1S 0x0200
|
||||
|
||||
#endif
|
83
sound/pci/oxygen/cs2000.h
Normal file
83
sound/pci/oxygen/cs2000.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
#ifndef CS2000_H_INCLUDED
|
||||
#define CS2000_H_INCLUDED
|
||||
|
||||
#define CS2000_DEV_ID 0x01
|
||||
#define CS2000_DEV_CTRL 0x02
|
||||
#define CS2000_DEV_CFG_1 0x03
|
||||
#define CS2000_DEV_CFG_2 0x04
|
||||
#define CS2000_GLOBAL_CFG 0x05
|
||||
#define CS2000_RATIO_0 0x06 /* 32 bits, big endian */
|
||||
#define CS2000_RATIO_1 0x0a
|
||||
#define CS2000_RATIO_2 0x0e
|
||||
#define CS2000_RATIO_3 0x12
|
||||
#define CS2000_FUN_CFG_1 0x16
|
||||
#define CS2000_FUN_CFG_2 0x17
|
||||
#define CS2000_FUN_CFG_3 0x1e
|
||||
|
||||
/* DEV_ID */
|
||||
#define CS2000_DEVICE_MASK 0xf8
|
||||
#define CS2000_REVISION_MASK 0x07
|
||||
|
||||
/* DEV_CTRL */
|
||||
#define CS2000_UNLOCK 0x80
|
||||
#define CS2000_AUX_OUT_DIS 0x02
|
||||
#define CS2000_CLK_OUT_DIS 0x01
|
||||
|
||||
/* DEV_CFG_1 */
|
||||
#define CS2000_R_MOD_SEL_MASK 0xe0
|
||||
#define CS2000_R_MOD_SEL_1 0x00
|
||||
#define CS2000_R_MOD_SEL_2 0x20
|
||||
#define CS2000_R_MOD_SEL_4 0x40
|
||||
#define CS2000_R_MOD_SEL_8 0x60
|
||||
#define CS2000_R_MOD_SEL_1_2 0x80
|
||||
#define CS2000_R_MOD_SEL_1_4 0xa0
|
||||
#define CS2000_R_MOD_SEL_1_8 0xc0
|
||||
#define CS2000_R_MOD_SEL_1_16 0xe0
|
||||
#define CS2000_R_SEL_MASK 0x18
|
||||
#define CS2000_R_SEL_SHIFT 3
|
||||
#define CS2000_AUX_OUT_SRC_MASK 0x06
|
||||
#define CS2000_AUX_OUT_SRC_REF_CLK 0x00
|
||||
#define CS2000_AUX_OUT_SRC_CLK_IN 0x02
|
||||
#define CS2000_AUX_OUT_SRC_CLK_OUT 0x04
|
||||
#define CS2000_AUX_OUT_SRC_PLL_LOCK 0x06
|
||||
#define CS2000_EN_DEV_CFG_1 0x01
|
||||
|
||||
/* DEV_CFG_2 */
|
||||
#define CS2000_LOCK_CLK_MASK 0x06
|
||||
#define CS2000_LOCK_CLK_SHIFT 1
|
||||
#define CS2000_FRAC_N_SRC_MASK 0x01
|
||||
#define CS2000_FRAC_N_SRC_STATIC 0x00
|
||||
#define CS2000_FRAC_N_SRC_DYNAMIC 0x01
|
||||
|
||||
/* GLOBAL_CFG */
|
||||
#define CS2000_FREEZE 0x08
|
||||
#define CS2000_EN_DEV_CFG_2 0x01
|
||||
|
||||
/* FUN_CFG_1 */
|
||||
#define CS2000_CLK_SKIP_EN 0x80
|
||||
#define CS2000_AUX_LOCK_CFG_MASK 0x40
|
||||
#define CS2000_AUX_LOCK_CFG_PP_HIGH 0x00
|
||||
#define CS2000_AUX_LOCK_CFG_OD_LOW 0x40
|
||||
#define CS2000_REF_CLK_DIV_MASK 0x18
|
||||
#define CS2000_REF_CLK_DIV_4 0x00
|
||||
#define CS2000_REF_CLK_DIV_2 0x08
|
||||
#define CS2000_REF_CLK_DIV_1 0x10
|
||||
|
||||
/* FUN_CFG_2 */
|
||||
#define CS2000_CLK_OUT_UNL 0x10
|
||||
#define CS2000_L_F_RATIO_CFG_MASK 0x08
|
||||
#define CS2000_L_F_RATIO_CFG_20_12 0x00
|
||||
#define CS2000_L_F_RATIO_CFG_12_20 0x08
|
||||
|
||||
/* FUN_CFG_3 */
|
||||
#define CS2000_CLK_IN_BW_MASK 0x70
|
||||
#define CS2000_CLK_IN_BW_1 0x00
|
||||
#define CS2000_CLK_IN_BW_2 0x10
|
||||
#define CS2000_CLK_IN_BW_4 0x20
|
||||
#define CS2000_CLK_IN_BW_8 0x30
|
||||
#define CS2000_CLK_IN_BW_16 0x40
|
||||
#define CS2000_CLK_IN_BW_32 0x50
|
||||
#define CS2000_CLK_IN_BW_64 0x60
|
||||
#define CS2000_CLK_IN_BW_128 0x70
|
||||
|
||||
#endif
|
110
sound/pci/oxygen/cs4245.h
Normal file
110
sound/pci/oxygen/cs4245.h
Normal file
|
@ -0,0 +1,110 @@
|
|||
#define CS4245_CHIP_ID 0x01
|
||||
#define CS4245_POWER_CTRL 0x02
|
||||
#define CS4245_DAC_CTRL_1 0x03
|
||||
#define CS4245_ADC_CTRL 0x04
|
||||
#define CS4245_MCLK_FREQ 0x05
|
||||
#define CS4245_SIGNAL_SEL 0x06
|
||||
#define CS4245_PGA_B_CTRL 0x07
|
||||
#define CS4245_PGA_A_CTRL 0x08
|
||||
#define CS4245_ANALOG_IN 0x09
|
||||
#define CS4245_DAC_A_CTRL 0x0a
|
||||
#define CS4245_DAC_B_CTRL 0x0b
|
||||
#define CS4245_DAC_CTRL_2 0x0c
|
||||
#define CS4245_INT_STATUS 0x0d
|
||||
#define CS4245_INT_MASK 0x0e
|
||||
#define CS4245_INT_MODE_MSB 0x0f
|
||||
#define CS4245_INT_MODE_LSB 0x10
|
||||
|
||||
/* Chip ID */
|
||||
#define CS4245_CHIP_PART_MASK 0xf0
|
||||
#define CS4245_CHIP_REV_MASK 0x0f
|
||||
|
||||
/* Power Control */
|
||||
#define CS4245_FREEZE 0x80
|
||||
#define CS4245_PDN_MIC 0x08
|
||||
#define CS4245_PDN_ADC 0x04
|
||||
#define CS4245_PDN_DAC 0x02
|
||||
#define CS4245_PDN 0x01
|
||||
|
||||
/* DAC Control */
|
||||
#define CS4245_DAC_FM_MASK 0xc0
|
||||
#define CS4245_DAC_FM_SINGLE 0x00
|
||||
#define CS4245_DAC_FM_DOUBLE 0x40
|
||||
#define CS4245_DAC_FM_QUAD 0x80
|
||||
#define CS4245_DAC_DIF_MASK 0x30
|
||||
#define CS4245_DAC_DIF_LJUST 0x00
|
||||
#define CS4245_DAC_DIF_I2S 0x10
|
||||
#define CS4245_DAC_DIF_RJUST_16 0x20
|
||||
#define CS4245_DAC_DIF_RJUST_24 0x30
|
||||
#define CS4245_RESERVED_1 0x08
|
||||
#define CS4245_MUTE_DAC 0x04
|
||||
#define CS4245_DEEMPH 0x02
|
||||
#define CS4245_DAC_MASTER 0x01
|
||||
|
||||
/* ADC Control */
|
||||
#define CS4245_ADC_FM_MASK 0xc0
|
||||
#define CS4245_ADC_FM_SINGLE 0x00
|
||||
#define CS4245_ADC_FM_DOUBLE 0x40
|
||||
#define CS4245_ADC_FM_QUAD 0x80
|
||||
#define CS4245_ADC_DIF_MASK 0x10
|
||||
#define CS4245_ADC_DIF_LJUST 0x00
|
||||
#define CS4245_ADC_DIF_I2S 0x10
|
||||
#define CS4245_MUTE_ADC 0x04
|
||||
#define CS4245_HPF_FREEZE 0x02
|
||||
#define CS4245_ADC_MASTER 0x01
|
||||
|
||||
/* MCLK Frequency */
|
||||
#define CS4245_MCLK1_MASK 0x70
|
||||
#define CS4245_MCLK1_SHIFT 4
|
||||
#define CS4245_MCLK2_MASK 0x07
|
||||
#define CS4245_MCLK2_SHIFT 0
|
||||
#define CS4245_MCLK_1 0
|
||||
#define CS4245_MCLK_1_5 1
|
||||
#define CS4245_MCLK_2 2
|
||||
#define CS4245_MCLK_3 3
|
||||
#define CS4245_MCLK_4 4
|
||||
|
||||
/* Signal Selection */
|
||||
#define CS4245_A_OUT_SEL_MASK 0x60
|
||||
#define CS4245_A_OUT_SEL_HIZ 0x00
|
||||
#define CS4245_A_OUT_SEL_DAC 0x20
|
||||
#define CS4245_A_OUT_SEL_PGA 0x40
|
||||
#define CS4245_LOOP 0x02
|
||||
#define CS4245_ASYNCH 0x01
|
||||
|
||||
/* Channel B/A PGA Control */
|
||||
#define CS4245_PGA_GAIN_MASK 0x3f
|
||||
|
||||
/* ADC Input Control */
|
||||
#define CS4245_PGA_SOFT 0x10
|
||||
#define CS4245_PGA_ZERO 0x08
|
||||
#define CS4245_SEL_MASK 0x07
|
||||
#define CS4245_SEL_MIC 0x00
|
||||
#define CS4245_SEL_INPUT_1 0x01
|
||||
#define CS4245_SEL_INPUT_2 0x02
|
||||
#define CS4245_SEL_INPUT_3 0x03
|
||||
#define CS4245_SEL_INPUT_4 0x04
|
||||
#define CS4245_SEL_INPUT_5 0x05
|
||||
#define CS4245_SEL_INPUT_6 0x06
|
||||
|
||||
/* DAC Channel A/B Volume Control */
|
||||
#define CS4245_VOL_MASK 0xff
|
||||
|
||||
/* DAC Control 2 */
|
||||
#define CS4245_DAC_SOFT 0x80
|
||||
#define CS4245_DAC_ZERO 0x40
|
||||
#define CS4245_INVERT_DAC 0x20
|
||||
#define CS4245_INT_ACTIVE_HIGH 0x01
|
||||
|
||||
/* Interrupt Status/Mask/Mode */
|
||||
#define CS4245_ADC_CLK_ERR 0x08
|
||||
#define CS4245_DAC_CLK_ERR 0x04
|
||||
#define CS4245_ADC_OVFL 0x02
|
||||
#define CS4245_ADC_UNDRFL 0x01
|
||||
|
||||
#define CS4245_SPI_ADDRESS_S (0x9e << 16)
|
||||
#define CS4245_SPI_WRITE_S (0 << 16)
|
||||
|
||||
#define CS4245_SPI_ADDRESS 0x9e
|
||||
#define CS4245_SPI_WRITE 0
|
||||
#define CS4245_SPI_READ 1
|
69
sound/pci/oxygen/cs4362a.h
Normal file
69
sound/pci/oxygen/cs4362a.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
/* register 01h */
|
||||
#define CS4362A_PDN 0x01
|
||||
#define CS4362A_DAC1_DIS 0x02
|
||||
#define CS4362A_DAC2_DIS 0x04
|
||||
#define CS4362A_DAC3_DIS 0x08
|
||||
#define CS4362A_MCLKDIV 0x20
|
||||
#define CS4362A_FREEZE 0x40
|
||||
#define CS4362A_CPEN 0x80
|
||||
/* register 02h */
|
||||
#define CS4362A_DIF_MASK 0x70
|
||||
#define CS4362A_DIF_LJUST 0x00
|
||||
#define CS4362A_DIF_I2S 0x10
|
||||
#define CS4362A_DIF_RJUST_16 0x20
|
||||
#define CS4362A_DIF_RJUST_24 0x30
|
||||
#define CS4362A_DIF_RJUST_20 0x40
|
||||
#define CS4362A_DIF_RJUST_18 0x50
|
||||
/* register 03h */
|
||||
#define CS4362A_MUTEC_MASK 0x03
|
||||
#define CS4362A_MUTEC_6 0x00
|
||||
#define CS4362A_MUTEC_1 0x01
|
||||
#define CS4362A_MUTEC_3 0x03
|
||||
#define CS4362A_AMUTE 0x04
|
||||
#define CS4362A_MUTEC_POL 0x08
|
||||
#define CS4362A_RMP_UP 0x10
|
||||
#define CS4362A_SNGLVOL 0x20
|
||||
#define CS4362A_ZERO_CROSS 0x40
|
||||
#define CS4362A_SOFT_RAMP 0x80
|
||||
/* register 04h */
|
||||
#define CS4362A_RMP_DN 0x01
|
||||
#define CS4362A_DEM_MASK 0x06
|
||||
#define CS4362A_DEM_NONE 0x00
|
||||
#define CS4362A_DEM_44100 0x02
|
||||
#define CS4362A_DEM_48000 0x04
|
||||
#define CS4362A_DEM_32000 0x06
|
||||
#define CS4362A_FILT_SEL 0x10
|
||||
/* register 05h */
|
||||
#define CS4362A_INV_A1 0x01
|
||||
#define CS4362A_INV_B1 0x02
|
||||
#define CS4362A_INV_A2 0x04
|
||||
#define CS4362A_INV_B2 0x08
|
||||
#define CS4362A_INV_A3 0x10
|
||||
#define CS4362A_INV_B3 0x20
|
||||
/* register 06h */
|
||||
#define CS4362A_FM_MASK 0x03
|
||||
#define CS4362A_FM_SINGLE 0x00
|
||||
#define CS4362A_FM_DOUBLE 0x01
|
||||
#define CS4362A_FM_QUAD 0x02
|
||||
#define CS4362A_FM_DSD 0x03
|
||||
#define CS4362A_ATAPI_MASK 0x7c
|
||||
#define CS4362A_ATAPI_B_MUTE 0x00
|
||||
#define CS4362A_ATAPI_B_R 0x04
|
||||
#define CS4362A_ATAPI_B_L 0x08
|
||||
#define CS4362A_ATAPI_B_LR 0x0c
|
||||
#define CS4362A_ATAPI_A_MUTE 0x00
|
||||
#define CS4362A_ATAPI_A_R 0x10
|
||||
#define CS4362A_ATAPI_A_L 0x20
|
||||
#define CS4362A_ATAPI_A_LR 0x30
|
||||
#define CS4362A_ATAPI_MIX_LR_VOL 0x40
|
||||
#define CS4362A_A_EQ_B 0x80
|
||||
/* register 07h */
|
||||
#define CS4362A_VOL_MASK 0x7f
|
||||
#define CS4362A_MUTE 0x80
|
||||
/* register 08h: like 07h */
|
||||
/* registers 09h..0Bh: like 06h..08h */
|
||||
/* registers 0Ch..0Eh: like 06h..08h */
|
||||
/* register 12h */
|
||||
#define CS4362A_REV_MASK 0x07
|
||||
#define CS4362A_PART_MASK 0xf8
|
||||
#define CS4362A_PART_CS4362A 0x50
|
69
sound/pci/oxygen/cs4398.h
Normal file
69
sound/pci/oxygen/cs4398.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
/* register 1 */
|
||||
#define CS4398_REV_MASK 0x07
|
||||
#define CS4398_PART_MASK 0xf8
|
||||
#define CS4398_PART_CS4398 0x70
|
||||
/* register 2 */
|
||||
#define CS4398_FM_MASK 0x03
|
||||
#define CS4398_FM_SINGLE 0x00
|
||||
#define CS4398_FM_DOUBLE 0x01
|
||||
#define CS4398_FM_QUAD 0x02
|
||||
#define CS4398_FM_DSD 0x03
|
||||
#define CS4398_DEM_MASK 0x0c
|
||||
#define CS4398_DEM_NONE 0x00
|
||||
#define CS4398_DEM_44100 0x04
|
||||
#define CS4398_DEM_48000 0x08
|
||||
#define CS4398_DEM_32000 0x0c
|
||||
#define CS4398_DIF_MASK 0x70
|
||||
#define CS4398_DIF_LJUST 0x00
|
||||
#define CS4398_DIF_I2S 0x10
|
||||
#define CS4398_DIF_RJUST_16 0x20
|
||||
#define CS4398_DIF_RJUST_24 0x30
|
||||
#define CS4398_DIF_RJUST_20 0x40
|
||||
#define CS4398_DIF_RJUST_18 0x50
|
||||
#define CS4398_DSD_SRC 0x80
|
||||
/* register 3 */
|
||||
#define CS4398_ATAPI_MASK 0x1f
|
||||
#define CS4398_ATAPI_B_MUTE 0x00
|
||||
#define CS4398_ATAPI_B_R 0x01
|
||||
#define CS4398_ATAPI_B_L 0x02
|
||||
#define CS4398_ATAPI_B_LR 0x03
|
||||
#define CS4398_ATAPI_A_MUTE 0x00
|
||||
#define CS4398_ATAPI_A_R 0x04
|
||||
#define CS4398_ATAPI_A_L 0x08
|
||||
#define CS4398_ATAPI_A_LR 0x0c
|
||||
#define CS4398_ATAPI_MIX_LR_VOL 0x10
|
||||
#define CS4398_INVERT_B 0x20
|
||||
#define CS4398_INVERT_A 0x40
|
||||
#define CS4398_VOL_B_EQ_A 0x80
|
||||
/* register 4 */
|
||||
#define CS4398_MUTEP_MASK 0x03
|
||||
#define CS4398_MUTEP_AUTO 0x00
|
||||
#define CS4398_MUTEP_LOW 0x02
|
||||
#define CS4398_MUTEP_HIGH 0x03
|
||||
#define CS4398_MUTE_B 0x08
|
||||
#define CS4398_MUTE_A 0x10
|
||||
#define CS4398_MUTEC_A_EQ_B 0x20
|
||||
#define CS4398_DAMUTE 0x40
|
||||
#define CS4398_PAMUTE 0x80
|
||||
/* register 5 */
|
||||
#define CS4398_VOL_A_MASK 0xff
|
||||
/* register 6 */
|
||||
#define CS4398_VOL_B_MASK 0xff
|
||||
/* register 7 */
|
||||
#define CS4398_DIR_DSD 0x01
|
||||
#define CS4398_FILT_SEL 0x04
|
||||
#define CS4398_RMP_DN 0x10
|
||||
#define CS4398_RMP_UP 0x20
|
||||
#define CS4398_ZERO_CROSS 0x40
|
||||
#define CS4398_SOFT_RAMP 0x80
|
||||
/* register 8 */
|
||||
#define CS4398_MCLKDIV3 0x08
|
||||
#define CS4398_MCLKDIV2 0x10
|
||||
#define CS4398_FREEZE 0x20
|
||||
#define CS4398_CPEN 0x40
|
||||
#define CS4398_PDN 0x80
|
||||
/* register 9 */
|
||||
#define CS4398_DSD_PM_EN 0x01
|
||||
#define CS4398_DSD_PM_MODE 0x02
|
||||
#define CS4398_INVALID_DSD 0x04
|
||||
#define CS4398_STATIC_DSD 0x08
|
882
sound/pci/oxygen/oxygen.c
Normal file
882
sound/pci/oxygen/oxygen.c
Normal file
|
@ -0,0 +1,882 @@
|
|||
/*
|
||||
* C-Media CMI8788 driver for C-Media's reference design and similar models
|
||||
*
|
||||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||
*
|
||||
*
|
||||
* This driver is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2.
|
||||
*
|
||||
* This driver 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 driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* CMI8788:
|
||||
*
|
||||
* SPI 0 -> 1st AK4396 (front)
|
||||
* SPI 1 -> 2nd AK4396 (surround)
|
||||
* SPI 2 -> 3rd AK4396 (center/LFE)
|
||||
* SPI 3 -> WM8785
|
||||
* SPI 4 -> 4th AK4396 (back)
|
||||
*
|
||||
* GPIO 0 -> DFS0 of AK5385
|
||||
* GPIO 1 -> DFS1 of AK5385
|
||||
*
|
||||
* X-Meridian models:
|
||||
* GPIO 4 -> enable extension S/PDIF input
|
||||
* GPIO 6 -> enable on-board S/PDIF input
|
||||
*
|
||||
* Claro models:
|
||||
* GPIO 6 -> S/PDIF from optical (0) or coaxial (1) input
|
||||
* GPIO 8 -> enable headphone amplifier
|
||||
*
|
||||
* CM9780:
|
||||
*
|
||||
* LINE_OUT -> input of ADC
|
||||
*
|
||||
* AUX_IN <- aux
|
||||
* CD_IN <- CD
|
||||
* MIC_IN <- mic
|
||||
*
|
||||
* GPO 0 -> route line-in (0) or AC97 output (1) to ADC input
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/module.h>
|
||||
#include <sound/ac97_codec.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "oxygen.h"
|
||||
#include "xonar_dg.h"
|
||||
#include "ak4396.h"
|
||||
#include "wm8785.h"
|
||||
|
||||
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
|
||||
MODULE_DESCRIPTION("C-Media CMI8788 driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8786}"
|
||||
",{C-Media,CMI8787}"
|
||||
",{C-Media,CMI8788}}");
|
||||
|
||||
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
|
||||
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
|
||||
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
|
||||
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "card index");
|
||||
module_param_array(id, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(id, "ID string");
|
||||
module_param_array(enable, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(enable, "enable card");
|
||||
|
||||
enum {
|
||||
MODEL_CMEDIA_REF,
|
||||
MODEL_MERIDIAN,
|
||||
MODEL_MERIDIAN_2G,
|
||||
MODEL_CLARO,
|
||||
MODEL_CLARO_HALO,
|
||||
MODEL_FANTASIA,
|
||||
MODEL_SERENADE,
|
||||
MODEL_2CH_OUTPUT,
|
||||
MODEL_HG2PCI,
|
||||
MODEL_XONAR_DG,
|
||||
MODEL_XONAR_DGX,
|
||||
};
|
||||
|
||||
static const struct pci_device_id oxygen_ids[] = {
|
||||
/* C-Media's reference design */
|
||||
{ OXYGEN_PCI_SUBID(0x10b0, 0x0216), .driver_data = MODEL_CMEDIA_REF },
|
||||
{ OXYGEN_PCI_SUBID(0x10b0, 0x0217), .driver_data = MODEL_CMEDIA_REF },
|
||||
{ OXYGEN_PCI_SUBID(0x10b0, 0x0218), .driver_data = MODEL_CMEDIA_REF },
|
||||
{ OXYGEN_PCI_SUBID(0x10b0, 0x0219), .driver_data = MODEL_CMEDIA_REF },
|
||||
{ OXYGEN_PCI_SUBID(0x13f6, 0x0001), .driver_data = MODEL_CMEDIA_REF },
|
||||
{ OXYGEN_PCI_SUBID(0x13f6, 0x0010), .driver_data = MODEL_CMEDIA_REF },
|
||||
{ OXYGEN_PCI_SUBID(0x13f6, 0x8788), .driver_data = MODEL_CMEDIA_REF },
|
||||
{ OXYGEN_PCI_SUBID(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF },
|
||||
{ OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF },
|
||||
/* Asus Xonar DG */
|
||||
{ OXYGEN_PCI_SUBID(0x1043, 0x8467), .driver_data = MODEL_XONAR_DG },
|
||||
/* Asus Xonar DGX */
|
||||
{ OXYGEN_PCI_SUBID(0x1043, 0x8521), .driver_data = MODEL_XONAR_DGX },
|
||||
/* PCI 2.0 HD Audio */
|
||||
{ OXYGEN_PCI_SUBID(0x13f6, 0x8782), .driver_data = MODEL_2CH_OUTPUT },
|
||||
/* Kuroutoshikou CMI8787-HG2PCI */
|
||||
{ OXYGEN_PCI_SUBID(0x13f6, 0xffff), .driver_data = MODEL_HG2PCI },
|
||||
/* TempoTec HiFier Fantasia */
|
||||
{ OXYGEN_PCI_SUBID(0x14c3, 0x1710), .driver_data = MODEL_FANTASIA },
|
||||
/* TempoTec HiFier Serenade */
|
||||
{ OXYGEN_PCI_SUBID(0x14c3, 0x1711), .driver_data = MODEL_SERENADE },
|
||||
/* AuzenTech X-Meridian */
|
||||
{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN },
|
||||
/* AuzenTech X-Meridian 2G */
|
||||
{ OXYGEN_PCI_SUBID(0x5431, 0x017a), .driver_data = MODEL_MERIDIAN_2G },
|
||||
/* HT-Omega Claro */
|
||||
{ OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CLARO },
|
||||
/* HT-Omega Claro halo */
|
||||
{ OXYGEN_PCI_SUBID(0x7284, 0x9781), .driver_data = MODEL_CLARO_HALO },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, oxygen_ids);
|
||||
|
||||
|
||||
#define GPIO_AK5385_DFS_MASK 0x0003
|
||||
#define GPIO_AK5385_DFS_NORMAL 0x0000
|
||||
#define GPIO_AK5385_DFS_DOUBLE 0x0001
|
||||
#define GPIO_AK5385_DFS_QUAD 0x0002
|
||||
|
||||
#define GPIO_MERIDIAN_DIG_MASK 0x0050
|
||||
#define GPIO_MERIDIAN_DIG_EXT 0x0010
|
||||
#define GPIO_MERIDIAN_DIG_BOARD 0x0040
|
||||
|
||||
#define GPIO_CLARO_DIG_COAX 0x0040
|
||||
#define GPIO_CLARO_HP 0x0100
|
||||
|
||||
struct generic_data {
|
||||
unsigned int dacs;
|
||||
u8 ak4396_regs[4][5];
|
||||
u16 wm8785_regs[3];
|
||||
};
|
||||
|
||||
static void ak4396_write(struct oxygen *chip, unsigned int codec,
|
||||
u8 reg, u8 value)
|
||||
{
|
||||
/* maps ALSA channel pair number to SPI output */
|
||||
static const u8 codec_spi_map[4] = {
|
||||
0, 1, 2, 4
|
||||
};
|
||||
struct generic_data *data = chip->model_data;
|
||||
|
||||
oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
|
||||
OXYGEN_SPI_DATA_LENGTH_2 |
|
||||
OXYGEN_SPI_CLOCK_160 |
|
||||
(codec_spi_map[codec] << OXYGEN_SPI_CODEC_SHIFT) |
|
||||
OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
|
||||
AK4396_WRITE | (reg << 8) | value);
|
||||
data->ak4396_regs[codec][reg] = value;
|
||||
}
|
||||
|
||||
static void ak4396_write_cached(struct oxygen *chip, unsigned int codec,
|
||||
u8 reg, u8 value)
|
||||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
|
||||
if (value != data->ak4396_regs[codec][reg])
|
||||
ak4396_write(chip, codec, reg, value);
|
||||
}
|
||||
|
||||
static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value)
|
||||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
|
||||
oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
|
||||
OXYGEN_SPI_DATA_LENGTH_2 |
|
||||
OXYGEN_SPI_CLOCK_160 |
|
||||
(3 << OXYGEN_SPI_CODEC_SHIFT) |
|
||||
OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
|
||||
(reg << 9) | value);
|
||||
if (reg < ARRAY_SIZE(data->wm8785_regs))
|
||||
data->wm8785_regs[reg] = value;
|
||||
}
|
||||
|
||||
static void ak4396_registers_init(struct oxygen *chip)
|
||||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < data->dacs; ++i) {
|
||||
ak4396_write(chip, i, AK4396_CONTROL_1,
|
||||
AK4396_DIF_24_MSB | AK4396_RSTN);
|
||||
ak4396_write(chip, i, AK4396_CONTROL_2,
|
||||
data->ak4396_regs[0][AK4396_CONTROL_2]);
|
||||
ak4396_write(chip, i, AK4396_CONTROL_3,
|
||||
AK4396_PCM);
|
||||
ak4396_write(chip, i, AK4396_LCH_ATT,
|
||||
chip->dac_volume[i * 2]);
|
||||
ak4396_write(chip, i, AK4396_RCH_ATT,
|
||||
chip->dac_volume[i * 2 + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
static void ak4396_init(struct oxygen *chip)
|
||||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
|
||||
data->dacs = chip->model.dac_channels_pcm / 2;
|
||||
data->ak4396_regs[0][AK4396_CONTROL_2] =
|
||||
AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
|
||||
ak4396_registers_init(chip);
|
||||
snd_component_add(chip->card, "AK4396");
|
||||
}
|
||||
|
||||
static void ak5385_init(struct oxygen *chip)
|
||||
{
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_AK5385_DFS_MASK);
|
||||
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_AK5385_DFS_MASK);
|
||||
snd_component_add(chip->card, "AK5385");
|
||||
}
|
||||
|
||||
static void wm8785_registers_init(struct oxygen *chip)
|
||||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
|
||||
wm8785_write(chip, WM8785_R7, 0);
|
||||
wm8785_write(chip, WM8785_R0, data->wm8785_regs[0]);
|
||||
wm8785_write(chip, WM8785_R2, data->wm8785_regs[2]);
|
||||
}
|
||||
|
||||
static void wm8785_init(struct oxygen *chip)
|
||||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
|
||||
data->wm8785_regs[0] =
|
||||
WM8785_MCR_SLAVE | WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST;
|
||||
data->wm8785_regs[2] = WM8785_HPFR | WM8785_HPFL;
|
||||
wm8785_registers_init(chip);
|
||||
snd_component_add(chip->card, "WM8785");
|
||||
}
|
||||
|
||||
static void generic_init(struct oxygen *chip)
|
||||
{
|
||||
ak4396_init(chip);
|
||||
wm8785_init(chip);
|
||||
}
|
||||
|
||||
static void meridian_init(struct oxygen *chip)
|
||||
{
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
|
||||
GPIO_MERIDIAN_DIG_MASK);
|
||||
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
|
||||
GPIO_MERIDIAN_DIG_BOARD, GPIO_MERIDIAN_DIG_MASK);
|
||||
ak4396_init(chip);
|
||||
ak5385_init(chip);
|
||||
}
|
||||
|
||||
static void claro_enable_hp(struct oxygen *chip)
|
||||
{
|
||||
msleep(300);
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_HP);
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_HP);
|
||||
}
|
||||
|
||||
static void claro_init(struct oxygen *chip)
|
||||
{
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_DIG_COAX);
|
||||
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_DIG_COAX);
|
||||
ak4396_init(chip);
|
||||
wm8785_init(chip);
|
||||
claro_enable_hp(chip);
|
||||
}
|
||||
|
||||
static void claro_halo_init(struct oxygen *chip)
|
||||
{
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CLARO_DIG_COAX);
|
||||
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_DIG_COAX);
|
||||
ak4396_init(chip);
|
||||
ak5385_init(chip);
|
||||
claro_enable_hp(chip);
|
||||
}
|
||||
|
||||
static void fantasia_init(struct oxygen *chip)
|
||||
{
|
||||
ak4396_init(chip);
|
||||
snd_component_add(chip->card, "CS5340");
|
||||
}
|
||||
|
||||
static void stereo_output_init(struct oxygen *chip)
|
||||
{
|
||||
ak4396_init(chip);
|
||||
}
|
||||
|
||||
static void generic_cleanup(struct oxygen *chip)
|
||||
{
|
||||
}
|
||||
|
||||
static void claro_disable_hp(struct oxygen *chip)
|
||||
{
|
||||
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_CLARO_HP);
|
||||
}
|
||||
|
||||
static void claro_cleanup(struct oxygen *chip)
|
||||
{
|
||||
claro_disable_hp(chip);
|
||||
}
|
||||
|
||||
static void claro_suspend(struct oxygen *chip)
|
||||
{
|
||||
claro_disable_hp(chip);
|
||||
}
|
||||
|
||||
static void generic_resume(struct oxygen *chip)
|
||||
{
|
||||
ak4396_registers_init(chip);
|
||||
wm8785_registers_init(chip);
|
||||
}
|
||||
|
||||
static void meridian_resume(struct oxygen *chip)
|
||||
{
|
||||
ak4396_registers_init(chip);
|
||||
}
|
||||
|
||||
static void claro_resume(struct oxygen *chip)
|
||||
{
|
||||
ak4396_registers_init(chip);
|
||||
claro_enable_hp(chip);
|
||||
}
|
||||
|
||||
static void stereo_resume(struct oxygen *chip)
|
||||
{
|
||||
ak4396_registers_init(chip);
|
||||
}
|
||||
|
||||
static void set_ak4396_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
unsigned int i;
|
||||
u8 value;
|
||||
|
||||
value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_DFS_MASK;
|
||||
if (params_rate(params) <= 54000)
|
||||
value |= AK4396_DFS_NORMAL;
|
||||
else if (params_rate(params) <= 108000)
|
||||
value |= AK4396_DFS_DOUBLE;
|
||||
else
|
||||
value |= AK4396_DFS_QUAD;
|
||||
|
||||
msleep(1); /* wait for the new MCLK to become stable */
|
||||
|
||||
if (value != data->ak4396_regs[0][AK4396_CONTROL_2]) {
|
||||
for (i = 0; i < data->dacs; ++i) {
|
||||
ak4396_write(chip, i, AK4396_CONTROL_1,
|
||||
AK4396_DIF_24_MSB);
|
||||
ak4396_write(chip, i, AK4396_CONTROL_2, value);
|
||||
ak4396_write(chip, i, AK4396_CONTROL_1,
|
||||
AK4396_DIF_24_MSB | AK4396_RSTN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void update_ak4396_volume(struct oxygen *chip)
|
||||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < data->dacs; ++i) {
|
||||
ak4396_write_cached(chip, i, AK4396_LCH_ATT,
|
||||
chip->dac_volume[i * 2]);
|
||||
ak4396_write_cached(chip, i, AK4396_RCH_ATT,
|
||||
chip->dac_volume[i * 2 + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_ak4396_mute(struct oxygen *chip)
|
||||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
unsigned int i;
|
||||
u8 value;
|
||||
|
||||
value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_SMUTE;
|
||||
if (chip->dac_mute)
|
||||
value |= AK4396_SMUTE;
|
||||
for (i = 0; i < data->dacs; ++i)
|
||||
ak4396_write_cached(chip, i, AK4396_CONTROL_2, value);
|
||||
}
|
||||
|
||||
static void set_wm8785_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
unsigned int value;
|
||||
|
||||
value = WM8785_MCR_SLAVE | WM8785_FORMAT_LJUST;
|
||||
if (params_rate(params) <= 48000)
|
||||
value |= WM8785_OSR_SINGLE;
|
||||
else if (params_rate(params) <= 96000)
|
||||
value |= WM8785_OSR_DOUBLE;
|
||||
else
|
||||
value |= WM8785_OSR_QUAD;
|
||||
if (value != data->wm8785_regs[0]) {
|
||||
wm8785_write(chip, WM8785_R7, 0);
|
||||
wm8785_write(chip, WM8785_R0, value);
|
||||
wm8785_write(chip, WM8785_R2, data->wm8785_regs[2]);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_ak5385_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
unsigned int value;
|
||||
|
||||
if (params_rate(params) <= 54000)
|
||||
value = GPIO_AK5385_DFS_NORMAL;
|
||||
else if (params_rate(params) <= 108000)
|
||||
value = GPIO_AK5385_DFS_DOUBLE;
|
||||
else
|
||||
value = GPIO_AK5385_DFS_QUAD;
|
||||
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
|
||||
value, GPIO_AK5385_DFS_MASK);
|
||||
}
|
||||
|
||||
static void set_no_params(struct oxygen *chip, struct snd_pcm_hw_params *params)
|
||||
{
|
||||
}
|
||||
|
||||
static int rolloff_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[2] = {
|
||||
"Sharp Roll-off", "Slow Roll-off"
|
||||
};
|
||||
|
||||
return snd_ctl_enum_info(info, 1, 2, names);
|
||||
}
|
||||
|
||||
static int rolloff_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct generic_data *data = chip->model_data;
|
||||
|
||||
value->value.enumerated.item[0] =
|
||||
(data->ak4396_regs[0][AK4396_CONTROL_2] & AK4396_SLOW) != 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rolloff_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct generic_data *data = chip->model_data;
|
||||
unsigned int i;
|
||||
int changed;
|
||||
u8 reg;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
reg = data->ak4396_regs[0][AK4396_CONTROL_2];
|
||||
if (value->value.enumerated.item[0])
|
||||
reg |= AK4396_SLOW;
|
||||
else
|
||||
reg &= ~AK4396_SLOW;
|
||||
changed = reg != data->ak4396_regs[0][AK4396_CONTROL_2];
|
||||
if (changed) {
|
||||
for (i = 0; i < data->dacs; ++i)
|
||||
ak4396_write(chip, i, AK4396_CONTROL_2, reg);
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new rolloff_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "DAC Filter Playback Enum",
|
||||
.info = rolloff_info,
|
||||
.get = rolloff_get,
|
||||
.put = rolloff_put,
|
||||
};
|
||||
|
||||
static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[2] = {
|
||||
"None", "High-pass Filter"
|
||||
};
|
||||
|
||||
return snd_ctl_enum_info(info, 1, 2, names);
|
||||
}
|
||||
|
||||
static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct generic_data *data = chip->model_data;
|
||||
|
||||
value->value.enumerated.item[0] =
|
||||
(data->wm8785_regs[WM8785_R2] & WM8785_HPFR) != 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct generic_data *data = chip->model_data;
|
||||
unsigned int reg;
|
||||
int changed;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
reg = data->wm8785_regs[WM8785_R2] & ~(WM8785_HPFR | WM8785_HPFL);
|
||||
if (value->value.enumerated.item[0])
|
||||
reg |= WM8785_HPFR | WM8785_HPFL;
|
||||
changed = reg != data->wm8785_regs[WM8785_R2];
|
||||
if (changed)
|
||||
wm8785_write(chip, WM8785_R2, reg);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new hpf_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "ADC Filter Capture Enum",
|
||||
.info = hpf_info,
|
||||
.get = hpf_get,
|
||||
.put = hpf_put,
|
||||
};
|
||||
|
||||
static int meridian_dig_source_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[2] = { "On-board", "Extension" };
|
||||
|
||||
return snd_ctl_enum_info(info, 1, 2, names);
|
||||
}
|
||||
|
||||
static int claro_dig_source_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[2] = { "Optical", "Coaxial" };
|
||||
|
||||
return snd_ctl_enum_info(info, 1, 2, names);
|
||||
}
|
||||
|
||||
static int meridian_dig_source_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
|
||||
value->value.enumerated.item[0] =
|
||||
!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) &
|
||||
GPIO_MERIDIAN_DIG_EXT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int claro_dig_source_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
|
||||
value->value.enumerated.item[0] =
|
||||
!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) &
|
||||
GPIO_CLARO_DIG_COAX);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meridian_dig_source_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
u16 old_reg, new_reg;
|
||||
int changed;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA);
|
||||
new_reg = old_reg & ~GPIO_MERIDIAN_DIG_MASK;
|
||||
if (value->value.enumerated.item[0] == 0)
|
||||
new_reg |= GPIO_MERIDIAN_DIG_BOARD;
|
||||
else
|
||||
new_reg |= GPIO_MERIDIAN_DIG_EXT;
|
||||
changed = new_reg != old_reg;
|
||||
if (changed)
|
||||
oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
static int claro_dig_source_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
u16 old_reg, new_reg;
|
||||
int changed;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA);
|
||||
new_reg = old_reg & ~GPIO_CLARO_DIG_COAX;
|
||||
if (value->value.enumerated.item[0])
|
||||
new_reg |= GPIO_CLARO_DIG_COAX;
|
||||
changed = new_reg != old_reg;
|
||||
if (changed)
|
||||
oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new meridian_dig_source_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "IEC958 Source Capture Enum",
|
||||
.info = meridian_dig_source_info,
|
||||
.get = meridian_dig_source_get,
|
||||
.put = meridian_dig_source_put,
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new claro_dig_source_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "IEC958 Source Capture Enum",
|
||||
.info = claro_dig_source_info,
|
||||
.get = claro_dig_source_get,
|
||||
.put = claro_dig_source_put,
|
||||
};
|
||||
|
||||
static int generic_mixer_init(struct oxygen *chip)
|
||||
{
|
||||
return snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
|
||||
}
|
||||
|
||||
static int generic_wm8785_mixer_init(struct oxygen *chip)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = generic_mixer_init(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_ctl_add(chip->card, snd_ctl_new1(&hpf_control, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meridian_mixer_init(struct oxygen *chip)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = generic_mixer_init(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_ctl_add(chip->card,
|
||||
snd_ctl_new1(&meridian_dig_source_control, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int claro_mixer_init(struct oxygen *chip)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = generic_wm8785_mixer_init(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_ctl_add(chip->card,
|
||||
snd_ctl_new1(&claro_dig_source_control, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int claro_halo_mixer_init(struct oxygen *chip)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = generic_mixer_init(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_ctl_add(chip->card,
|
||||
snd_ctl_new1(&claro_dig_source_control, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dump_ak4396_registers(struct oxygen *chip,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
unsigned int dac, i;
|
||||
|
||||
for (dac = 0; dac < data->dacs; ++dac) {
|
||||
snd_iprintf(buffer, "\nAK4396 %u:", dac + 1);
|
||||
for (i = 0; i < 5; ++i)
|
||||
snd_iprintf(buffer, " %02x", data->ak4396_regs[dac][i]);
|
||||
}
|
||||
snd_iprintf(buffer, "\n");
|
||||
}
|
||||
|
||||
static void dump_wm8785_registers(struct oxygen *chip,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct generic_data *data = chip->model_data;
|
||||
unsigned int i;
|
||||
|
||||
snd_iprintf(buffer, "\nWM8785:");
|
||||
for (i = 0; i < 3; ++i)
|
||||
snd_iprintf(buffer, " %03x", data->wm8785_regs[i]);
|
||||
snd_iprintf(buffer, "\n");
|
||||
}
|
||||
|
||||
static void dump_oxygen_registers(struct oxygen *chip,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
dump_ak4396_registers(chip, buffer);
|
||||
dump_wm8785_registers(chip, buffer);
|
||||
}
|
||||
|
||||
static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
|
||||
|
||||
static const struct oxygen_model model_generic = {
|
||||
.shortname = "C-Media CMI8788",
|
||||
.longname = "C-Media Oxygen HD Audio",
|
||||
.chip = "CMI8788",
|
||||
.init = generic_init,
|
||||
.mixer_init = generic_wm8785_mixer_init,
|
||||
.cleanup = generic_cleanup,
|
||||
.resume = generic_resume,
|
||||
.set_dac_params = set_ak4396_params,
|
||||
.set_adc_params = set_wm8785_params,
|
||||
.update_dac_volume = update_ak4396_volume,
|
||||
.update_dac_mute = update_ak4396_mute,
|
||||
.dump_registers = dump_oxygen_registers,
|
||||
.dac_tlv = ak4396_db_scale,
|
||||
.model_data_size = sizeof(struct generic_data),
|
||||
.device_config = PLAYBACK_0_TO_I2S |
|
||||
PLAYBACK_1_TO_SPDIF |
|
||||
PLAYBACK_2_TO_AC97_1 |
|
||||
CAPTURE_0_FROM_I2S_1 |
|
||||
CAPTURE_1_FROM_SPDIF |
|
||||
CAPTURE_2_FROM_AC97_1 |
|
||||
AC97_CD_INPUT,
|
||||
.dac_channels_pcm = 8,
|
||||
.dac_channels_mixer = 8,
|
||||
.dac_volume_min = 0,
|
||||
.dac_volume_max = 255,
|
||||
.function_flags = OXYGEN_FUNCTION_SPI |
|
||||
OXYGEN_FUNCTION_ENABLE_SPI_4_5,
|
||||
.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
|
||||
.adc_mclks = OXYGEN_MCLKS(256, 256, 128),
|
||||
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
};
|
||||
|
||||
static int get_oxygen_model(struct oxygen *chip,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
static const char *const names[] = {
|
||||
[MODEL_MERIDIAN] = "AuzenTech X-Meridian",
|
||||
[MODEL_MERIDIAN_2G] = "AuzenTech X-Meridian 2G",
|
||||
[MODEL_CLARO] = "HT-Omega Claro",
|
||||
[MODEL_CLARO_HALO] = "HT-Omega Claro halo",
|
||||
[MODEL_FANTASIA] = "TempoTec HiFier Fantasia",
|
||||
[MODEL_SERENADE] = "TempoTec HiFier Serenade",
|
||||
[MODEL_HG2PCI] = "CMI8787-HG2PCI",
|
||||
};
|
||||
|
||||
chip->model = model_generic;
|
||||
switch (id->driver_data) {
|
||||
case MODEL_MERIDIAN:
|
||||
case MODEL_MERIDIAN_2G:
|
||||
chip->model.init = meridian_init;
|
||||
chip->model.mixer_init = meridian_mixer_init;
|
||||
chip->model.resume = meridian_resume;
|
||||
chip->model.set_adc_params = set_ak5385_params;
|
||||
chip->model.dump_registers = dump_ak4396_registers;
|
||||
chip->model.device_config = PLAYBACK_0_TO_I2S |
|
||||
PLAYBACK_1_TO_SPDIF |
|
||||
CAPTURE_0_FROM_I2S_2 |
|
||||
CAPTURE_1_FROM_SPDIF;
|
||||
if (id->driver_data == MODEL_MERIDIAN)
|
||||
chip->model.device_config |= AC97_CD_INPUT;
|
||||
break;
|
||||
case MODEL_CLARO:
|
||||
chip->model.init = claro_init;
|
||||
chip->model.mixer_init = claro_mixer_init;
|
||||
chip->model.cleanup = claro_cleanup;
|
||||
chip->model.suspend = claro_suspend;
|
||||
chip->model.resume = claro_resume;
|
||||
break;
|
||||
case MODEL_CLARO_HALO:
|
||||
chip->model.init = claro_halo_init;
|
||||
chip->model.mixer_init = claro_halo_mixer_init;
|
||||
chip->model.cleanup = claro_cleanup;
|
||||
chip->model.suspend = claro_suspend;
|
||||
chip->model.resume = claro_resume;
|
||||
chip->model.set_adc_params = set_ak5385_params;
|
||||
chip->model.dump_registers = dump_ak4396_registers;
|
||||
chip->model.device_config = PLAYBACK_0_TO_I2S |
|
||||
PLAYBACK_1_TO_SPDIF |
|
||||
CAPTURE_0_FROM_I2S_2 |
|
||||
CAPTURE_1_FROM_SPDIF;
|
||||
break;
|
||||
case MODEL_FANTASIA:
|
||||
case MODEL_SERENADE:
|
||||
case MODEL_2CH_OUTPUT:
|
||||
case MODEL_HG2PCI:
|
||||
chip->model.shortname = "C-Media CMI8787";
|
||||
chip->model.chip = "CMI8787";
|
||||
if (id->driver_data == MODEL_FANTASIA)
|
||||
chip->model.init = fantasia_init;
|
||||
else
|
||||
chip->model.init = stereo_output_init;
|
||||
chip->model.resume = stereo_resume;
|
||||
chip->model.mixer_init = generic_mixer_init;
|
||||
chip->model.set_adc_params = set_no_params;
|
||||
chip->model.dump_registers = dump_ak4396_registers;
|
||||
chip->model.device_config = PLAYBACK_0_TO_I2S |
|
||||
PLAYBACK_1_TO_SPDIF;
|
||||
if (id->driver_data == MODEL_FANTASIA) {
|
||||
chip->model.device_config |= CAPTURE_0_FROM_I2S_1;
|
||||
chip->model.adc_mclks = OXYGEN_MCLKS(256, 128, 128);
|
||||
}
|
||||
chip->model.dac_channels_pcm = 2;
|
||||
chip->model.dac_channels_mixer = 2;
|
||||
break;
|
||||
case MODEL_XONAR_DG:
|
||||
chip->model = model_xonar_dg;
|
||||
chip->model.shortname = "Xonar DG";
|
||||
break;
|
||||
case MODEL_XONAR_DGX:
|
||||
chip->model = model_xonar_dg;
|
||||
chip->model.shortname = "Xonar DGX";
|
||||
break;
|
||||
}
|
||||
if (id->driver_data == MODEL_MERIDIAN ||
|
||||
id->driver_data == MODEL_MERIDIAN_2G ||
|
||||
id->driver_data == MODEL_CLARO_HALO) {
|
||||
chip->model.misc_flags = OXYGEN_MISC_MIDI;
|
||||
chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT;
|
||||
}
|
||||
if (id->driver_data < ARRAY_SIZE(names) && names[id->driver_data])
|
||||
chip->model.shortname = names[id->driver_data];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generic_oxygen_probe(struct pci_dev *pci,
|
||||
const struct pci_device_id *pci_id)
|
||||
{
|
||||
static int dev;
|
||||
int err;
|
||||
|
||||
if (dev >= SNDRV_CARDS)
|
||||
return -ENODEV;
|
||||
if (!enable[dev]) {
|
||||
++dev;
|
||||
return -ENOENT;
|
||||
}
|
||||
err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
|
||||
oxygen_ids, get_oxygen_model);
|
||||
if (err >= 0)
|
||||
++dev;
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct pci_driver oxygen_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.id_table = oxygen_ids,
|
||||
.probe = generic_oxygen_probe,
|
||||
.remove = oxygen_pci_remove,
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.driver = {
|
||||
.pm = &oxygen_pci_pm,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
module_pci_driver(oxygen_driver);
|
259
sound/pci/oxygen/oxygen.h
Normal file
259
sound/pci/oxygen/oxygen.h
Normal file
|
@ -0,0 +1,259 @@
|
|||
#ifndef OXYGEN_H_INCLUDED
|
||||
#define OXYGEN_H_INCLUDED
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include "oxygen_regs.h"
|
||||
|
||||
/* 1 << PCM_x == OXYGEN_CHANNEL_x */
|
||||
#define PCM_A 0
|
||||
#define PCM_B 1
|
||||
#define PCM_C 2
|
||||
#define PCM_SPDIF 3
|
||||
#define PCM_MULTICH 4
|
||||
#define PCM_AC97 5
|
||||
#define PCM_COUNT 6
|
||||
|
||||
#define OXYGEN_MCLKS(f_single, f_double, f_quad) ((MCLK_##f_single << 0) | \
|
||||
(MCLK_##f_double << 2) | \
|
||||
(MCLK_##f_quad << 4))
|
||||
|
||||
#define OXYGEN_IO_SIZE 0x100
|
||||
|
||||
#define OXYGEN_EEPROM_ID 0x434d /* "CM" */
|
||||
|
||||
/* model-specific configuration of outputs/inputs */
|
||||
#define PLAYBACK_0_TO_I2S 0x0001
|
||||
/* PLAYBACK_0_TO_AC97_0 not implemented */
|
||||
#define PLAYBACK_1_TO_SPDIF 0x0004
|
||||
#define PLAYBACK_2_TO_AC97_1 0x0008
|
||||
#define CAPTURE_0_FROM_I2S_1 0x0010
|
||||
#define CAPTURE_0_FROM_I2S_2 0x0020
|
||||
/* CAPTURE_0_FROM_AC97_0 not implemented */
|
||||
#define CAPTURE_1_FROM_SPDIF 0x0080
|
||||
#define CAPTURE_2_FROM_I2S_2 0x0100
|
||||
#define CAPTURE_2_FROM_AC97_1 0x0200
|
||||
/* CAPTURE_3_FROM_I2S_3 not implemented */
|
||||
#define MIDI_OUTPUT 0x0800
|
||||
#define MIDI_INPUT 0x1000
|
||||
#define AC97_CD_INPUT 0x2000
|
||||
#define AC97_FMIC_SWITCH 0x4000
|
||||
|
||||
enum {
|
||||
CONTROL_SPDIF_PCM,
|
||||
CONTROL_SPDIF_INPUT_BITS,
|
||||
CONTROL_MIC_CAPTURE_SWITCH,
|
||||
CONTROL_LINE_CAPTURE_SWITCH,
|
||||
CONTROL_CD_CAPTURE_SWITCH,
|
||||
CONTROL_AUX_CAPTURE_SWITCH,
|
||||
CONTROL_COUNT
|
||||
};
|
||||
|
||||
#define OXYGEN_PCI_SUBID(sv, sd) \
|
||||
.vendor = PCI_VENDOR_ID_CMEDIA, \
|
||||
.device = 0x8788, \
|
||||
.subvendor = sv, \
|
||||
.subdevice = sd
|
||||
|
||||
#define BROKEN_EEPROM_DRIVER_DATA ((unsigned long)-1)
|
||||
#define OXYGEN_PCI_SUBID_BROKEN_EEPROM \
|
||||
OXYGEN_PCI_SUBID(PCI_VENDOR_ID_CMEDIA, 0x8788), \
|
||||
.driver_data = BROKEN_EEPROM_DRIVER_DATA
|
||||
|
||||
struct pci_dev;
|
||||
struct pci_device_id;
|
||||
struct snd_card;
|
||||
struct snd_pcm_substream;
|
||||
struct snd_pcm_hardware;
|
||||
struct snd_pcm_hw_params;
|
||||
struct snd_kcontrol_new;
|
||||
struct snd_rawmidi;
|
||||
struct snd_info_buffer;
|
||||
struct oxygen;
|
||||
|
||||
struct oxygen_model {
|
||||
const char *shortname;
|
||||
const char *longname;
|
||||
const char *chip;
|
||||
void (*init)(struct oxygen *chip);
|
||||
int (*control_filter)(struct snd_kcontrol_new *template);
|
||||
int (*mixer_init)(struct oxygen *chip);
|
||||
void (*cleanup)(struct oxygen *chip);
|
||||
void (*suspend)(struct oxygen *chip);
|
||||
void (*resume)(struct oxygen *chip);
|
||||
void (*pcm_hardware_filter)(unsigned int channel,
|
||||
struct snd_pcm_hardware *hardware);
|
||||
void (*set_dac_params)(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params);
|
||||
void (*set_adc_params)(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params);
|
||||
void (*update_dac_volume)(struct oxygen *chip);
|
||||
void (*update_dac_mute)(struct oxygen *chip);
|
||||
void (*update_center_lfe_mix)(struct oxygen *chip, bool mixed);
|
||||
unsigned int (*adjust_dac_routing)(struct oxygen *chip,
|
||||
unsigned int play_routing);
|
||||
void (*gpio_changed)(struct oxygen *chip);
|
||||
void (*uart_input)(struct oxygen *chip);
|
||||
void (*ac97_switch)(struct oxygen *chip,
|
||||
unsigned int reg, unsigned int mute);
|
||||
void (*dump_registers)(struct oxygen *chip,
|
||||
struct snd_info_buffer *buffer);
|
||||
const unsigned int *dac_tlv;
|
||||
size_t model_data_size;
|
||||
unsigned int device_config;
|
||||
u8 dac_channels_pcm;
|
||||
u8 dac_channels_mixer;
|
||||
u8 dac_volume_min;
|
||||
u8 dac_volume_max;
|
||||
u8 misc_flags;
|
||||
u8 function_flags;
|
||||
u8 dac_mclks;
|
||||
u8 adc_mclks;
|
||||
u16 dac_i2s_format;
|
||||
u16 adc_i2s_format;
|
||||
};
|
||||
|
||||
struct oxygen {
|
||||
unsigned long addr;
|
||||
spinlock_t reg_lock;
|
||||
struct mutex mutex;
|
||||
struct snd_card *card;
|
||||
struct pci_dev *pci;
|
||||
struct snd_rawmidi *midi;
|
||||
int irq;
|
||||
void *model_data;
|
||||
unsigned int interrupt_mask;
|
||||
u8 dac_volume[8];
|
||||
u8 dac_mute;
|
||||
u8 pcm_active;
|
||||
u8 pcm_running;
|
||||
u8 dac_routing;
|
||||
u8 spdif_playback_enable;
|
||||
u8 has_ac97_0;
|
||||
u8 has_ac97_1;
|
||||
u32 spdif_bits;
|
||||
u32 spdif_pcm_bits;
|
||||
struct snd_pcm_substream *streams[PCM_COUNT];
|
||||
struct snd_kcontrol *controls[CONTROL_COUNT];
|
||||
struct work_struct spdif_input_bits_work;
|
||||
struct work_struct gpio_work;
|
||||
wait_queue_head_t ac97_waitqueue;
|
||||
union {
|
||||
u8 _8[OXYGEN_IO_SIZE];
|
||||
__le16 _16[OXYGEN_IO_SIZE / 2];
|
||||
__le32 _32[OXYGEN_IO_SIZE / 4];
|
||||
} saved_registers;
|
||||
u16 saved_ac97_registers[2][0x40];
|
||||
unsigned int uart_input_count;
|
||||
u8 uart_input[32];
|
||||
struct oxygen_model model;
|
||||
};
|
||||
|
||||
/* oxygen_lib.c */
|
||||
|
||||
int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
|
||||
struct module *owner,
|
||||
const struct pci_device_id *ids,
|
||||
int (*get_model)(struct oxygen *chip,
|
||||
const struct pci_device_id *id
|
||||
)
|
||||
);
|
||||
void oxygen_pci_remove(struct pci_dev *pci);
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
extern const struct dev_pm_ops oxygen_pci_pm;
|
||||
#endif
|
||||
void oxygen_pci_shutdown(struct pci_dev *pci);
|
||||
|
||||
/* oxygen_mixer.c */
|
||||
|
||||
int oxygen_mixer_init(struct oxygen *chip);
|
||||
void oxygen_update_dac_routing(struct oxygen *chip);
|
||||
void oxygen_update_spdif_source(struct oxygen *chip);
|
||||
|
||||
/* oxygen_pcm.c */
|
||||
|
||||
int oxygen_pcm_init(struct oxygen *chip);
|
||||
|
||||
/* oxygen_io.c */
|
||||
|
||||
u8 oxygen_read8(struct oxygen *chip, unsigned int reg);
|
||||
u16 oxygen_read16(struct oxygen *chip, unsigned int reg);
|
||||
u32 oxygen_read32(struct oxygen *chip, unsigned int reg);
|
||||
void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value);
|
||||
void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value);
|
||||
void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value);
|
||||
void oxygen_write8_masked(struct oxygen *chip, unsigned int reg,
|
||||
u8 value, u8 mask);
|
||||
void oxygen_write16_masked(struct oxygen *chip, unsigned int reg,
|
||||
u16 value, u16 mask);
|
||||
void oxygen_write32_masked(struct oxygen *chip, unsigned int reg,
|
||||
u32 value, u32 mask);
|
||||
|
||||
u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec,
|
||||
unsigned int index);
|
||||
void oxygen_write_ac97(struct oxygen *chip, unsigned int codec,
|
||||
unsigned int index, u16 data);
|
||||
void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,
|
||||
unsigned int index, u16 data, u16 mask);
|
||||
|
||||
int oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data);
|
||||
void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data);
|
||||
|
||||
void oxygen_reset_uart(struct oxygen *chip);
|
||||
void oxygen_write_uart(struct oxygen *chip, u8 data);
|
||||
|
||||
u16 oxygen_read_eeprom(struct oxygen *chip, unsigned int index);
|
||||
void oxygen_write_eeprom(struct oxygen *chip, unsigned int index, u16 value);
|
||||
|
||||
static inline void oxygen_set_bits8(struct oxygen *chip,
|
||||
unsigned int reg, u8 value)
|
||||
{
|
||||
oxygen_write8_masked(chip, reg, value, value);
|
||||
}
|
||||
|
||||
static inline void oxygen_set_bits16(struct oxygen *chip,
|
||||
unsigned int reg, u16 value)
|
||||
{
|
||||
oxygen_write16_masked(chip, reg, value, value);
|
||||
}
|
||||
|
||||
static inline void oxygen_set_bits32(struct oxygen *chip,
|
||||
unsigned int reg, u32 value)
|
||||
{
|
||||
oxygen_write32_masked(chip, reg, value, value);
|
||||
}
|
||||
|
||||
static inline void oxygen_clear_bits8(struct oxygen *chip,
|
||||
unsigned int reg, u8 value)
|
||||
{
|
||||
oxygen_write8_masked(chip, reg, 0, value);
|
||||
}
|
||||
|
||||
static inline void oxygen_clear_bits16(struct oxygen *chip,
|
||||
unsigned int reg, u16 value)
|
||||
{
|
||||
oxygen_write16_masked(chip, reg, 0, value);
|
||||
}
|
||||
|
||||
static inline void oxygen_clear_bits32(struct oxygen *chip,
|
||||
unsigned int reg, u32 value)
|
||||
{
|
||||
oxygen_write32_masked(chip, reg, 0, value);
|
||||
}
|
||||
|
||||
static inline void oxygen_ac97_set_bits(struct oxygen *chip, unsigned int codec,
|
||||
unsigned int index, u16 value)
|
||||
{
|
||||
oxygen_write_ac97_masked(chip, codec, index, value, value);
|
||||
}
|
||||
|
||||
static inline void oxygen_ac97_clear_bits(struct oxygen *chip,
|
||||
unsigned int codec,
|
||||
unsigned int index, u16 value)
|
||||
{
|
||||
oxygen_write_ac97_masked(chip, codec, index, 0, value);
|
||||
}
|
||||
|
||||
#endif
|
292
sound/pci/oxygen/oxygen_io.c
Normal file
292
sound/pci/oxygen/oxygen_io.c
Normal file
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
* C-Media CMI8788 driver - helper functions
|
||||
*
|
||||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||
*
|
||||
*
|
||||
* This driver is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2.
|
||||
*
|
||||
* This driver 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 driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/export.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/mpu401.h>
|
||||
#include <asm/io.h>
|
||||
#include "oxygen.h"
|
||||
|
||||
u8 oxygen_read8(struct oxygen *chip, unsigned int reg)
|
||||
{
|
||||
return inb(chip->addr + reg);
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_read8);
|
||||
|
||||
u16 oxygen_read16(struct oxygen *chip, unsigned int reg)
|
||||
{
|
||||
return inw(chip->addr + reg);
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_read16);
|
||||
|
||||
u32 oxygen_read32(struct oxygen *chip, unsigned int reg)
|
||||
{
|
||||
return inl(chip->addr + reg);
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_read32);
|
||||
|
||||
void oxygen_write8(struct oxygen *chip, unsigned int reg, u8 value)
|
||||
{
|
||||
outb(value, chip->addr + reg);
|
||||
chip->saved_registers._8[reg] = value;
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_write8);
|
||||
|
||||
void oxygen_write16(struct oxygen *chip, unsigned int reg, u16 value)
|
||||
{
|
||||
outw(value, chip->addr + reg);
|
||||
chip->saved_registers._16[reg / 2] = cpu_to_le16(value);
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_write16);
|
||||
|
||||
void oxygen_write32(struct oxygen *chip, unsigned int reg, u32 value)
|
||||
{
|
||||
outl(value, chip->addr + reg);
|
||||
chip->saved_registers._32[reg / 4] = cpu_to_le32(value);
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_write32);
|
||||
|
||||
void oxygen_write8_masked(struct oxygen *chip, unsigned int reg,
|
||||
u8 value, u8 mask)
|
||||
{
|
||||
u8 tmp = inb(chip->addr + reg);
|
||||
tmp &= ~mask;
|
||||
tmp |= value & mask;
|
||||
outb(tmp, chip->addr + reg);
|
||||
chip->saved_registers._8[reg] = tmp;
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_write8_masked);
|
||||
|
||||
void oxygen_write16_masked(struct oxygen *chip, unsigned int reg,
|
||||
u16 value, u16 mask)
|
||||
{
|
||||
u16 tmp = inw(chip->addr + reg);
|
||||
tmp &= ~mask;
|
||||
tmp |= value & mask;
|
||||
outw(tmp, chip->addr + reg);
|
||||
chip->saved_registers._16[reg / 2] = cpu_to_le16(tmp);
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_write16_masked);
|
||||
|
||||
void oxygen_write32_masked(struct oxygen *chip, unsigned int reg,
|
||||
u32 value, u32 mask)
|
||||
{
|
||||
u32 tmp = inl(chip->addr + reg);
|
||||
tmp &= ~mask;
|
||||
tmp |= value & mask;
|
||||
outl(tmp, chip->addr + reg);
|
||||
chip->saved_registers._32[reg / 4] = cpu_to_le32(tmp);
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_write32_masked);
|
||||
|
||||
static int oxygen_ac97_wait(struct oxygen *chip, unsigned int mask)
|
||||
{
|
||||
u8 status = 0;
|
||||
|
||||
/*
|
||||
* Reading the status register also clears the bits, so we have to save
|
||||
* the read bits in status.
|
||||
*/
|
||||
wait_event_timeout(chip->ac97_waitqueue,
|
||||
({ status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS);
|
||||
status & mask; }),
|
||||
msecs_to_jiffies(1) + 1);
|
||||
/*
|
||||
* Check even after a timeout because this function should not require
|
||||
* the AC'97 interrupt to be enabled.
|
||||
*/
|
||||
status |= oxygen_read8(chip, OXYGEN_AC97_INTERRUPT_STATUS);
|
||||
return status & mask ? 0 : -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* About 10% of AC'97 register reads or writes fail to complete, but even those
|
||||
* where the controller indicates completion aren't guaranteed to have actually
|
||||
* happened.
|
||||
*
|
||||
* It's hard to assign blame to either the controller or the codec because both
|
||||
* were made by C-Media ...
|
||||
*/
|
||||
|
||||
void oxygen_write_ac97(struct oxygen *chip, unsigned int codec,
|
||||
unsigned int index, u16 data)
|
||||
{
|
||||
unsigned int count, succeeded;
|
||||
u32 reg;
|
||||
|
||||
reg = data;
|
||||
reg |= index << OXYGEN_AC97_REG_ADDR_SHIFT;
|
||||
reg |= OXYGEN_AC97_REG_DIR_WRITE;
|
||||
reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT;
|
||||
succeeded = 0;
|
||||
for (count = 5; count > 0; --count) {
|
||||
udelay(5);
|
||||
oxygen_write32(chip, OXYGEN_AC97_REGS, reg);
|
||||
/* require two "completed" writes, just to be sure */
|
||||
if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_WRITE_DONE) >= 0 &&
|
||||
++succeeded >= 2) {
|
||||
chip->saved_ac97_registers[codec][index / 2] = data;
|
||||
return;
|
||||
}
|
||||
}
|
||||
dev_err(chip->card->dev, "AC'97 write timeout\n");
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_write_ac97);
|
||||
|
||||
u16 oxygen_read_ac97(struct oxygen *chip, unsigned int codec,
|
||||
unsigned int index)
|
||||
{
|
||||
unsigned int count;
|
||||
unsigned int last_read = UINT_MAX;
|
||||
u32 reg;
|
||||
|
||||
reg = index << OXYGEN_AC97_REG_ADDR_SHIFT;
|
||||
reg |= OXYGEN_AC97_REG_DIR_READ;
|
||||
reg |= codec << OXYGEN_AC97_REG_CODEC_SHIFT;
|
||||
for (count = 5; count > 0; --count) {
|
||||
udelay(5);
|
||||
oxygen_write32(chip, OXYGEN_AC97_REGS, reg);
|
||||
udelay(10);
|
||||
if (oxygen_ac97_wait(chip, OXYGEN_AC97_INT_READ_DONE) >= 0) {
|
||||
u16 value = oxygen_read16(chip, OXYGEN_AC97_REGS);
|
||||
/* we require two consecutive reads of the same value */
|
||||
if (value == last_read)
|
||||
return value;
|
||||
last_read = value;
|
||||
/*
|
||||
* Invert the register value bits to make sure that two
|
||||
* consecutive unsuccessful reads do not return the same
|
||||
* value.
|
||||
*/
|
||||
reg ^= 0xffff;
|
||||
}
|
||||
}
|
||||
dev_err(chip->card->dev, "AC'97 read timeout on codec %u\n", codec);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_read_ac97);
|
||||
|
||||
void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,
|
||||
unsigned int index, u16 data, u16 mask)
|
||||
{
|
||||
u16 value = oxygen_read_ac97(chip, codec, index);
|
||||
value &= ~mask;
|
||||
value |= data & mask;
|
||||
oxygen_write_ac97(chip, codec, index, value);
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_write_ac97_masked);
|
||||
|
||||
static int oxygen_wait_spi(struct oxygen *chip)
|
||||
{
|
||||
unsigned int count;
|
||||
|
||||
/*
|
||||
* Higher timeout to be sure: 200 us;
|
||||
* actual transaction should not need more than 40 us.
|
||||
*/
|
||||
for (count = 50; count > 0; count--) {
|
||||
udelay(4);
|
||||
if ((oxygen_read8(chip, OXYGEN_SPI_CONTROL) &
|
||||
OXYGEN_SPI_BUSY) == 0)
|
||||
return 0;
|
||||
}
|
||||
dev_err(chip->card->dev, "oxygen: SPI wait timeout\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data)
|
||||
{
|
||||
/*
|
||||
* We need to wait AFTER initiating the SPI transaction,
|
||||
* otherwise read operations will not work.
|
||||
*/
|
||||
oxygen_write8(chip, OXYGEN_SPI_DATA1, data);
|
||||
oxygen_write8(chip, OXYGEN_SPI_DATA2, data >> 8);
|
||||
if (control & OXYGEN_SPI_DATA_LENGTH_3)
|
||||
oxygen_write8(chip, OXYGEN_SPI_DATA3, data >> 16);
|
||||
oxygen_write8(chip, OXYGEN_SPI_CONTROL, control);
|
||||
return oxygen_wait_spi(chip);
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_write_spi);
|
||||
|
||||
void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data)
|
||||
{
|
||||
/* should not need more than about 300 us */
|
||||
msleep(1);
|
||||
|
||||
oxygen_write8(chip, OXYGEN_2WIRE_MAP, map);
|
||||
oxygen_write8(chip, OXYGEN_2WIRE_DATA, data);
|
||||
oxygen_write8(chip, OXYGEN_2WIRE_CONTROL,
|
||||
device | OXYGEN_2WIRE_DIR_WRITE);
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_write_i2c);
|
||||
|
||||
static void _write_uart(struct oxygen *chip, unsigned int port, u8 data)
|
||||
{
|
||||
if (oxygen_read8(chip, OXYGEN_MPU401 + 1) & MPU401_TX_FULL)
|
||||
msleep(1);
|
||||
oxygen_write8(chip, OXYGEN_MPU401 + port, data);
|
||||
}
|
||||
|
||||
void oxygen_reset_uart(struct oxygen *chip)
|
||||
{
|
||||
_write_uart(chip, 1, MPU401_RESET);
|
||||
msleep(1); /* wait for ACK */
|
||||
_write_uart(chip, 1, MPU401_ENTER_UART);
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_reset_uart);
|
||||
|
||||
void oxygen_write_uart(struct oxygen *chip, u8 data)
|
||||
{
|
||||
_write_uart(chip, 0, data);
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_write_uart);
|
||||
|
||||
u16 oxygen_read_eeprom(struct oxygen *chip, unsigned int index)
|
||||
{
|
||||
unsigned int timeout;
|
||||
|
||||
oxygen_write8(chip, OXYGEN_EEPROM_CONTROL,
|
||||
index | OXYGEN_EEPROM_DIR_READ);
|
||||
for (timeout = 0; timeout < 100; ++timeout) {
|
||||
udelay(1);
|
||||
if (!(oxygen_read8(chip, OXYGEN_EEPROM_STATUS)
|
||||
& OXYGEN_EEPROM_BUSY))
|
||||
break;
|
||||
}
|
||||
return oxygen_read16(chip, OXYGEN_EEPROM_DATA);
|
||||
}
|
||||
|
||||
void oxygen_write_eeprom(struct oxygen *chip, unsigned int index, u16 value)
|
||||
{
|
||||
unsigned int timeout;
|
||||
|
||||
oxygen_write16(chip, OXYGEN_EEPROM_DATA, value);
|
||||
oxygen_write8(chip, OXYGEN_EEPROM_CONTROL,
|
||||
index | OXYGEN_EEPROM_DIR_WRITE);
|
||||
for (timeout = 0; timeout < 10; ++timeout) {
|
||||
msleep(1);
|
||||
if (!(oxygen_read8(chip, OXYGEN_EEPROM_STATUS)
|
||||
& OXYGEN_EEPROM_BUSY))
|
||||
return;
|
||||
}
|
||||
dev_err(chip->card->dev, "EEPROM write timeout\n");
|
||||
}
|
836
sound/pci/oxygen/oxygen_lib.c
Normal file
836
sound/pci/oxygen/oxygen_lib.c
Normal file
|
@ -0,0 +1,836 @@
|
|||
/*
|
||||
* C-Media CMI8788 driver - main driver module
|
||||
*
|
||||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||
*
|
||||
*
|
||||
* This driver is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2.
|
||||
*
|
||||
* This driver 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 driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <sound/ac97_codec.h>
|
||||
#include <sound/asoundef.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/mpu401.h>
|
||||
#include <sound/pcm.h>
|
||||
#include "oxygen.h"
|
||||
#include "cm9780.h"
|
||||
|
||||
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
|
||||
MODULE_DESCRIPTION("C-Media CMI8788 helper library");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
#define DRIVER "oxygen"
|
||||
|
||||
static inline int oxygen_uart_input_ready(struct oxygen *chip)
|
||||
{
|
||||
return !(oxygen_read8(chip, OXYGEN_MPU401 + 1) & MPU401_RX_EMPTY);
|
||||
}
|
||||
|
||||
static void oxygen_read_uart(struct oxygen *chip)
|
||||
{
|
||||
if (unlikely(!oxygen_uart_input_ready(chip))) {
|
||||
/* no data, but read it anyway to clear the interrupt */
|
||||
oxygen_read8(chip, OXYGEN_MPU401);
|
||||
return;
|
||||
}
|
||||
do {
|
||||
u8 data = oxygen_read8(chip, OXYGEN_MPU401);
|
||||
if (data == MPU401_ACK)
|
||||
continue;
|
||||
if (chip->uart_input_count >= ARRAY_SIZE(chip->uart_input))
|
||||
chip->uart_input_count = 0;
|
||||
chip->uart_input[chip->uart_input_count++] = data;
|
||||
} while (oxygen_uart_input_ready(chip));
|
||||
if (chip->model.uart_input)
|
||||
chip->model.uart_input(chip);
|
||||
}
|
||||
|
||||
static irqreturn_t oxygen_interrupt(int dummy, void *dev_id)
|
||||
{
|
||||
struct oxygen *chip = dev_id;
|
||||
unsigned int status, clear, elapsed_streams, i;
|
||||
|
||||
status = oxygen_read16(chip, OXYGEN_INTERRUPT_STATUS);
|
||||
if (!status)
|
||||
return IRQ_NONE;
|
||||
|
||||
spin_lock(&chip->reg_lock);
|
||||
|
||||
clear = status & (OXYGEN_CHANNEL_A |
|
||||
OXYGEN_CHANNEL_B |
|
||||
OXYGEN_CHANNEL_C |
|
||||
OXYGEN_CHANNEL_SPDIF |
|
||||
OXYGEN_CHANNEL_MULTICH |
|
||||
OXYGEN_CHANNEL_AC97 |
|
||||
OXYGEN_INT_SPDIF_IN_DETECT |
|
||||
OXYGEN_INT_GPIO |
|
||||
OXYGEN_INT_AC97);
|
||||
if (clear) {
|
||||
if (clear & OXYGEN_INT_SPDIF_IN_DETECT)
|
||||
chip->interrupt_mask &= ~OXYGEN_INT_SPDIF_IN_DETECT;
|
||||
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
|
||||
chip->interrupt_mask & ~clear);
|
||||
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
|
||||
chip->interrupt_mask);
|
||||
}
|
||||
|
||||
elapsed_streams = status & chip->pcm_running;
|
||||
|
||||
spin_unlock(&chip->reg_lock);
|
||||
|
||||
for (i = 0; i < PCM_COUNT; ++i)
|
||||
if ((elapsed_streams & (1 << i)) && chip->streams[i])
|
||||
snd_pcm_period_elapsed(chip->streams[i]);
|
||||
|
||||
if (status & OXYGEN_INT_SPDIF_IN_DETECT) {
|
||||
spin_lock(&chip->reg_lock);
|
||||
i = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
|
||||
if (i & (OXYGEN_SPDIF_SENSE_INT | OXYGEN_SPDIF_LOCK_INT |
|
||||
OXYGEN_SPDIF_RATE_INT)) {
|
||||
/* write the interrupt bit(s) to clear */
|
||||
oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, i);
|
||||
schedule_work(&chip->spdif_input_bits_work);
|
||||
}
|
||||
spin_unlock(&chip->reg_lock);
|
||||
}
|
||||
|
||||
if (status & OXYGEN_INT_GPIO)
|
||||
schedule_work(&chip->gpio_work);
|
||||
|
||||
if (status & OXYGEN_INT_MIDI) {
|
||||
if (chip->midi)
|
||||
snd_mpu401_uart_interrupt(0, chip->midi->private_data);
|
||||
else
|
||||
oxygen_read_uart(chip);
|
||||
}
|
||||
|
||||
if (status & OXYGEN_INT_AC97)
|
||||
wake_up(&chip->ac97_waitqueue);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void oxygen_spdif_input_bits_changed(struct work_struct *work)
|
||||
{
|
||||
struct oxygen *chip = container_of(work, struct oxygen,
|
||||
spdif_input_bits_work);
|
||||
u32 reg;
|
||||
|
||||
/*
|
||||
* This function gets called when there is new activity on the SPDIF
|
||||
* input, or when we lose lock on the input signal, or when the rate
|
||||
* changes.
|
||||
*/
|
||||
msleep(1);
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
reg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
|
||||
if ((reg & (OXYGEN_SPDIF_SENSE_STATUS |
|
||||
OXYGEN_SPDIF_LOCK_STATUS))
|
||||
== OXYGEN_SPDIF_SENSE_STATUS) {
|
||||
/*
|
||||
* If we detect activity on the SPDIF input but cannot lock to
|
||||
* a signal, the clock bit is likely to be wrong.
|
||||
*/
|
||||
reg ^= OXYGEN_SPDIF_IN_CLOCK_MASK;
|
||||
oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
msleep(1);
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
reg = oxygen_read32(chip, OXYGEN_SPDIF_CONTROL);
|
||||
if ((reg & (OXYGEN_SPDIF_SENSE_STATUS |
|
||||
OXYGEN_SPDIF_LOCK_STATUS))
|
||||
== OXYGEN_SPDIF_SENSE_STATUS) {
|
||||
/* nothing detected with either clock; give up */
|
||||
if ((reg & OXYGEN_SPDIF_IN_CLOCK_MASK)
|
||||
== OXYGEN_SPDIF_IN_CLOCK_192) {
|
||||
/*
|
||||
* Reset clock to <= 96 kHz because this is
|
||||
* more likely to be received next time.
|
||||
*/
|
||||
reg &= ~OXYGEN_SPDIF_IN_CLOCK_MASK;
|
||||
reg |= OXYGEN_SPDIF_IN_CLOCK_96;
|
||||
oxygen_write32(chip, OXYGEN_SPDIF_CONTROL, reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
|
||||
if (chip->controls[CONTROL_SPDIF_INPUT_BITS]) {
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT;
|
||||
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK,
|
||||
chip->interrupt_mask);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
|
||||
/*
|
||||
* We don't actually know that any channel status bits have
|
||||
* changed, but let's send a notification just to be sure.
|
||||
*/
|
||||
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
|
||||
&chip->controls[CONTROL_SPDIF_INPUT_BITS]->id);
|
||||
}
|
||||
}
|
||||
|
||||
static void oxygen_gpio_changed(struct work_struct *work)
|
||||
{
|
||||
struct oxygen *chip = container_of(work, struct oxygen, gpio_work);
|
||||
|
||||
if (chip->model.gpio_changed)
|
||||
chip->model.gpio_changed(chip);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static void oxygen_proc_read(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct oxygen *chip = entry->private_data;
|
||||
int i, j;
|
||||
|
||||
switch (oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_PACKAGE_ID_MASK) {
|
||||
case OXYGEN_PACKAGE_ID_8786: i = '6'; break;
|
||||
case OXYGEN_PACKAGE_ID_8787: i = '7'; break;
|
||||
case OXYGEN_PACKAGE_ID_8788: i = '8'; break;
|
||||
default: i = '?'; break;
|
||||
}
|
||||
snd_iprintf(buffer, "CMI878%c:\n", i);
|
||||
for (i = 0; i < OXYGEN_IO_SIZE; i += 0x10) {
|
||||
snd_iprintf(buffer, "%02x:", i);
|
||||
for (j = 0; j < 0x10; ++j)
|
||||
snd_iprintf(buffer, " %02x", oxygen_read8(chip, i + j));
|
||||
snd_iprintf(buffer, "\n");
|
||||
}
|
||||
if (mutex_lock_interruptible(&chip->mutex) < 0)
|
||||
return;
|
||||
if (chip->has_ac97_0) {
|
||||
snd_iprintf(buffer, "\nAC97:\n");
|
||||
for (i = 0; i < 0x80; i += 0x10) {
|
||||
snd_iprintf(buffer, "%02x:", i);
|
||||
for (j = 0; j < 0x10; j += 2)
|
||||
snd_iprintf(buffer, " %04x",
|
||||
oxygen_read_ac97(chip, 0, i + j));
|
||||
snd_iprintf(buffer, "\n");
|
||||
}
|
||||
}
|
||||
if (chip->has_ac97_1) {
|
||||
snd_iprintf(buffer, "\nAC97 2:\n");
|
||||
for (i = 0; i < 0x80; i += 0x10) {
|
||||
snd_iprintf(buffer, "%02x:", i);
|
||||
for (j = 0; j < 0x10; j += 2)
|
||||
snd_iprintf(buffer, " %04x",
|
||||
oxygen_read_ac97(chip, 1, i + j));
|
||||
snd_iprintf(buffer, "\n");
|
||||
}
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
if (chip->model.dump_registers)
|
||||
chip->model.dump_registers(chip, buffer);
|
||||
}
|
||||
|
||||
static void oxygen_proc_init(struct oxygen *chip)
|
||||
{
|
||||
struct snd_info_entry *entry;
|
||||
|
||||
if (!snd_card_proc_new(chip->card, "oxygen", &entry))
|
||||
snd_info_set_text_ops(entry, chip, oxygen_proc_read);
|
||||
}
|
||||
#else
|
||||
#define oxygen_proc_init(chip)
|
||||
#endif
|
||||
|
||||
static const struct pci_device_id *
|
||||
oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[])
|
||||
{
|
||||
u16 subdevice;
|
||||
|
||||
/*
|
||||
* Make sure the EEPROM pins are available, i.e., not used for SPI.
|
||||
* (This function is called before we initialize or use SPI.)
|
||||
*/
|
||||
oxygen_clear_bits8(chip, OXYGEN_FUNCTION,
|
||||
OXYGEN_FUNCTION_ENABLE_SPI_4_5);
|
||||
/*
|
||||
* Read the subsystem device ID directly from the EEPROM, because the
|
||||
* chip didn't if the first EEPROM word was overwritten.
|
||||
*/
|
||||
subdevice = oxygen_read_eeprom(chip, 2);
|
||||
/* use default ID if EEPROM is missing */
|
||||
if (subdevice == 0xffff && oxygen_read_eeprom(chip, 1) == 0xffff)
|
||||
subdevice = 0x8788;
|
||||
/*
|
||||
* We use only the subsystem device ID for searching because it is
|
||||
* unique even without the subsystem vendor ID, which may have been
|
||||
* overwritten in the EEPROM.
|
||||
*/
|
||||
for (; ids->vendor; ++ids)
|
||||
if (ids->subdevice == subdevice &&
|
||||
ids->driver_data != BROKEN_EEPROM_DRIVER_DATA)
|
||||
return ids;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void oxygen_restore_eeprom(struct oxygen *chip,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
u16 eeprom_id;
|
||||
|
||||
eeprom_id = oxygen_read_eeprom(chip, 0);
|
||||
if (eeprom_id != OXYGEN_EEPROM_ID &&
|
||||
(eeprom_id != 0xffff || id->subdevice != 0x8788)) {
|
||||
/*
|
||||
* This function gets called only when a known card model has
|
||||
* been detected, i.e., we know there is a valid subsystem
|
||||
* product ID at index 2 in the EEPROM. Therefore, we have
|
||||
* been able to deduce the correct subsystem vendor ID, and
|
||||
* this is enough information to restore the original EEPROM
|
||||
* contents.
|
||||
*/
|
||||
oxygen_write_eeprom(chip, 1, id->subvendor);
|
||||
oxygen_write_eeprom(chip, 0, OXYGEN_EEPROM_ID);
|
||||
|
||||
oxygen_set_bits8(chip, OXYGEN_MISC,
|
||||
OXYGEN_MISC_WRITE_PCI_SUBID);
|
||||
pci_write_config_word(chip->pci, PCI_SUBSYSTEM_VENDOR_ID,
|
||||
id->subvendor);
|
||||
pci_write_config_word(chip->pci, PCI_SUBSYSTEM_ID,
|
||||
id->subdevice);
|
||||
oxygen_clear_bits8(chip, OXYGEN_MISC,
|
||||
OXYGEN_MISC_WRITE_PCI_SUBID);
|
||||
|
||||
dev_info(chip->card->dev, "EEPROM ID restored\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void configure_pcie_bridge(struct pci_dev *pci)
|
||||
{
|
||||
enum { PEX811X, PI7C9X110 };
|
||||
static const struct pci_device_id bridge_ids[] = {
|
||||
{ PCI_VDEVICE(PLX, 0x8111), .driver_data = PEX811X },
|
||||
{ PCI_VDEVICE(PLX, 0x8112), .driver_data = PEX811X },
|
||||
{ PCI_DEVICE(0x12d8, 0xe110), .driver_data = PI7C9X110 },
|
||||
{ }
|
||||
};
|
||||
struct pci_dev *bridge;
|
||||
const struct pci_device_id *id;
|
||||
u32 tmp;
|
||||
|
||||
if (!pci->bus || !pci->bus->self)
|
||||
return;
|
||||
bridge = pci->bus->self;
|
||||
|
||||
id = pci_match_id(bridge_ids, bridge);
|
||||
if (!id)
|
||||
return;
|
||||
|
||||
switch (id->driver_data) {
|
||||
case PEX811X: /* PLX PEX8111/PEX8112 PCIe/PCI bridge */
|
||||
pci_read_config_dword(bridge, 0x48, &tmp);
|
||||
tmp |= 1; /* enable blind prefetching */
|
||||
tmp |= 1 << 11; /* enable beacon generation */
|
||||
pci_write_config_dword(bridge, 0x48, tmp);
|
||||
|
||||
pci_write_config_dword(bridge, 0x84, 0x0c);
|
||||
pci_read_config_dword(bridge, 0x88, &tmp);
|
||||
tmp &= ~(7 << 27);
|
||||
tmp |= 2 << 27; /* set prefetch size to 128 bytes */
|
||||
pci_write_config_dword(bridge, 0x88, tmp);
|
||||
break;
|
||||
|
||||
case PI7C9X110: /* Pericom PI7C9X110 PCIe/PCI bridge */
|
||||
pci_read_config_dword(bridge, 0x40, &tmp);
|
||||
tmp |= 1; /* park the PCI arbiter to the sound chip */
|
||||
pci_write_config_dword(bridge, 0x40, tmp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void oxygen_init(struct oxygen *chip)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
chip->dac_routing = 1;
|
||||
for (i = 0; i < 8; ++i)
|
||||
chip->dac_volume[i] = chip->model.dac_volume_min;
|
||||
chip->dac_mute = 1;
|
||||
chip->spdif_playback_enable = 1;
|
||||
chip->spdif_bits = OXYGEN_SPDIF_C | OXYGEN_SPDIF_ORIGINAL |
|
||||
(IEC958_AES1_CON_PCM_CODER << OXYGEN_SPDIF_CATEGORY_SHIFT);
|
||||
chip->spdif_pcm_bits = chip->spdif_bits;
|
||||
|
||||
if (!(oxygen_read8(chip, OXYGEN_REVISION) & OXYGEN_REVISION_2))
|
||||
oxygen_set_bits8(chip, OXYGEN_MISC,
|
||||
OXYGEN_MISC_PCI_MEM_W_1_CLOCK);
|
||||
|
||||
i = oxygen_read16(chip, OXYGEN_AC97_CONTROL);
|
||||
chip->has_ac97_0 = (i & OXYGEN_AC97_CODEC_0) != 0;
|
||||
chip->has_ac97_1 = (i & OXYGEN_AC97_CODEC_1) != 0;
|
||||
|
||||
oxygen_write8_masked(chip, OXYGEN_FUNCTION,
|
||||
OXYGEN_FUNCTION_RESET_CODEC |
|
||||
chip->model.function_flags,
|
||||
OXYGEN_FUNCTION_RESET_CODEC |
|
||||
OXYGEN_FUNCTION_2WIRE_SPI_MASK |
|
||||
OXYGEN_FUNCTION_ENABLE_SPI_4_5);
|
||||
oxygen_write8(chip, OXYGEN_DMA_STATUS, 0);
|
||||
oxygen_write8(chip, OXYGEN_DMA_PAUSE, 0);
|
||||
oxygen_write8(chip, OXYGEN_PLAY_CHANNELS,
|
||||
OXYGEN_PLAY_CHANNELS_2 |
|
||||
OXYGEN_DMA_A_BURST_8 |
|
||||
OXYGEN_DMA_MULTICH_BURST_8);
|
||||
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
|
||||
oxygen_write8_masked(chip, OXYGEN_MISC,
|
||||
chip->model.misc_flags,
|
||||
OXYGEN_MISC_WRITE_PCI_SUBID |
|
||||
OXYGEN_MISC_REC_C_FROM_SPDIF |
|
||||
OXYGEN_MISC_REC_B_FROM_AC97 |
|
||||
OXYGEN_MISC_REC_A_FROM_MULTICH |
|
||||
OXYGEN_MISC_MIDI);
|
||||
oxygen_write8(chip, OXYGEN_REC_FORMAT,
|
||||
(OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_A_SHIFT) |
|
||||
(OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_B_SHIFT) |
|
||||
(OXYGEN_FORMAT_16 << OXYGEN_REC_FORMAT_C_SHIFT));
|
||||
oxygen_write8(chip, OXYGEN_PLAY_FORMAT,
|
||||
(OXYGEN_FORMAT_16 << OXYGEN_SPDIF_FORMAT_SHIFT) |
|
||||
(OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT));
|
||||
oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2);
|
||||
oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT,
|
||||
OXYGEN_RATE_48000 |
|
||||
chip->model.dac_i2s_format |
|
||||
OXYGEN_I2S_MCLK(chip->model.dac_mclks) |
|
||||
OXYGEN_I2S_BITS_16 |
|
||||
OXYGEN_I2S_MASTER |
|
||||
OXYGEN_I2S_BCLK_64);
|
||||
if (chip->model.device_config & CAPTURE_0_FROM_I2S_1)
|
||||
oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
|
||||
OXYGEN_RATE_48000 |
|
||||
chip->model.adc_i2s_format |
|
||||
OXYGEN_I2S_MCLK(chip->model.adc_mclks) |
|
||||
OXYGEN_I2S_BITS_16 |
|
||||
OXYGEN_I2S_MASTER |
|
||||
OXYGEN_I2S_BCLK_64);
|
||||
else
|
||||
oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
|
||||
OXYGEN_I2S_MASTER |
|
||||
OXYGEN_I2S_MUTE_MCLK);
|
||||
if (chip->model.device_config & (CAPTURE_0_FROM_I2S_2 |
|
||||
CAPTURE_2_FROM_I2S_2))
|
||||
oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
|
||||
OXYGEN_RATE_48000 |
|
||||
chip->model.adc_i2s_format |
|
||||
OXYGEN_I2S_MCLK(chip->model.adc_mclks) |
|
||||
OXYGEN_I2S_BITS_16 |
|
||||
OXYGEN_I2S_MASTER |
|
||||
OXYGEN_I2S_BCLK_64);
|
||||
else
|
||||
oxygen_write16(chip, OXYGEN_I2S_B_FORMAT,
|
||||
OXYGEN_I2S_MASTER |
|
||||
OXYGEN_I2S_MUTE_MCLK);
|
||||
oxygen_write16(chip, OXYGEN_I2S_C_FORMAT,
|
||||
OXYGEN_I2S_MASTER |
|
||||
OXYGEN_I2S_MUTE_MCLK);
|
||||
oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
|
||||
OXYGEN_SPDIF_OUT_ENABLE |
|
||||
OXYGEN_SPDIF_LOOPBACK);
|
||||
if (chip->model.device_config & CAPTURE_1_FROM_SPDIF)
|
||||
oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
|
||||
OXYGEN_SPDIF_SENSE_MASK |
|
||||
OXYGEN_SPDIF_LOCK_MASK |
|
||||
OXYGEN_SPDIF_RATE_MASK |
|
||||
OXYGEN_SPDIF_LOCK_PAR |
|
||||
OXYGEN_SPDIF_IN_CLOCK_96,
|
||||
OXYGEN_SPDIF_SENSE_MASK |
|
||||
OXYGEN_SPDIF_LOCK_MASK |
|
||||
OXYGEN_SPDIF_RATE_MASK |
|
||||
OXYGEN_SPDIF_SENSE_PAR |
|
||||
OXYGEN_SPDIF_LOCK_PAR |
|
||||
OXYGEN_SPDIF_IN_CLOCK_MASK);
|
||||
else
|
||||
oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
|
||||
OXYGEN_SPDIF_SENSE_MASK |
|
||||
OXYGEN_SPDIF_LOCK_MASK |
|
||||
OXYGEN_SPDIF_RATE_MASK);
|
||||
oxygen_write32(chip, OXYGEN_SPDIF_OUTPUT_BITS, chip->spdif_bits);
|
||||
oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
|
||||
OXYGEN_2WIRE_LENGTH_8 |
|
||||
OXYGEN_2WIRE_INTERRUPT_MASK |
|
||||
OXYGEN_2WIRE_SPEED_STANDARD);
|
||||
oxygen_clear_bits8(chip, OXYGEN_MPU401_CONTROL, OXYGEN_MPU401_LOOPBACK);
|
||||
oxygen_write8(chip, OXYGEN_GPI_INTERRUPT_MASK, 0);
|
||||
oxygen_write16(chip, OXYGEN_GPIO_INTERRUPT_MASK, 0);
|
||||
oxygen_write16(chip, OXYGEN_PLAY_ROUTING,
|
||||
OXYGEN_PLAY_MULTICH_I2S_DAC |
|
||||
OXYGEN_PLAY_SPDIF_SPDIF |
|
||||
(0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
|
||||
(1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
|
||||
(2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
|
||||
(3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT));
|
||||
oxygen_write8(chip, OXYGEN_REC_ROUTING,
|
||||
OXYGEN_REC_A_ROUTE_I2S_ADC_1 |
|
||||
OXYGEN_REC_B_ROUTE_I2S_ADC_2 |
|
||||
OXYGEN_REC_C_ROUTE_SPDIF);
|
||||
oxygen_write8(chip, OXYGEN_ADC_MONITOR, 0);
|
||||
oxygen_write8(chip, OXYGEN_A_MONITOR_ROUTING,
|
||||
(0 << OXYGEN_A_MONITOR_ROUTE_0_SHIFT) |
|
||||
(1 << OXYGEN_A_MONITOR_ROUTE_1_SHIFT) |
|
||||
(2 << OXYGEN_A_MONITOR_ROUTE_2_SHIFT) |
|
||||
(3 << OXYGEN_A_MONITOR_ROUTE_3_SHIFT));
|
||||
|
||||
if (chip->has_ac97_0 | chip->has_ac97_1)
|
||||
oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK,
|
||||
OXYGEN_AC97_INT_READ_DONE |
|
||||
OXYGEN_AC97_INT_WRITE_DONE);
|
||||
else
|
||||
oxygen_write8(chip, OXYGEN_AC97_INTERRUPT_MASK, 0);
|
||||
oxygen_write32(chip, OXYGEN_AC97_OUT_CONFIG, 0);
|
||||
oxygen_write32(chip, OXYGEN_AC97_IN_CONFIG, 0);
|
||||
if (!(chip->has_ac97_0 | chip->has_ac97_1))
|
||||
oxygen_set_bits16(chip, OXYGEN_AC97_CONTROL,
|
||||
OXYGEN_AC97_CLOCK_DISABLE);
|
||||
if (!chip->has_ac97_0) {
|
||||
oxygen_set_bits16(chip, OXYGEN_AC97_CONTROL,
|
||||
OXYGEN_AC97_NO_CODEC_0);
|
||||
} else {
|
||||
oxygen_write_ac97(chip, 0, AC97_RESET, 0);
|
||||
msleep(1);
|
||||
oxygen_ac97_set_bits(chip, 0, CM9780_GPIO_SETUP,
|
||||
CM9780_GPIO0IO | CM9780_GPIO1IO);
|
||||
oxygen_ac97_set_bits(chip, 0, CM9780_MIXER,
|
||||
CM9780_BSTSEL | CM9780_STRO_MIC |
|
||||
CM9780_MIX2FR | CM9780_PCBSW);
|
||||
oxygen_ac97_set_bits(chip, 0, CM9780_JACK,
|
||||
CM9780_RSOE | CM9780_CBOE |
|
||||
CM9780_SSOE | CM9780_FROE |
|
||||
CM9780_MIC2MIC | CM9780_LI2LI);
|
||||
oxygen_write_ac97(chip, 0, AC97_MASTER, 0x0000);
|
||||
oxygen_write_ac97(chip, 0, AC97_PC_BEEP, 0x8000);
|
||||
oxygen_write_ac97(chip, 0, AC97_MIC, 0x8808);
|
||||
oxygen_write_ac97(chip, 0, AC97_LINE, 0x0808);
|
||||
oxygen_write_ac97(chip, 0, AC97_CD, 0x8808);
|
||||
oxygen_write_ac97(chip, 0, AC97_VIDEO, 0x8808);
|
||||
oxygen_write_ac97(chip, 0, AC97_AUX, 0x8808);
|
||||
oxygen_write_ac97(chip, 0, AC97_REC_GAIN, 0x8000);
|
||||
oxygen_write_ac97(chip, 0, AC97_CENTER_LFE_MASTER, 0x8080);
|
||||
oxygen_write_ac97(chip, 0, AC97_SURROUND_MASTER, 0x8080);
|
||||
oxygen_ac97_clear_bits(chip, 0, CM9780_GPIO_STATUS,
|
||||
CM9780_GPO0);
|
||||
/* power down unused ADCs and DACs */
|
||||
oxygen_ac97_set_bits(chip, 0, AC97_POWERDOWN,
|
||||
AC97_PD_PR0 | AC97_PD_PR1);
|
||||
oxygen_ac97_set_bits(chip, 0, AC97_EXTENDED_STATUS,
|
||||
AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK);
|
||||
}
|
||||
if (chip->has_ac97_1) {
|
||||
oxygen_set_bits32(chip, OXYGEN_AC97_OUT_CONFIG,
|
||||
OXYGEN_AC97_CODEC1_SLOT3 |
|
||||
OXYGEN_AC97_CODEC1_SLOT4);
|
||||
oxygen_write_ac97(chip, 1, AC97_RESET, 0);
|
||||
msleep(1);
|
||||
oxygen_write_ac97(chip, 1, AC97_MASTER, 0x0000);
|
||||
oxygen_write_ac97(chip, 1, AC97_HEADPHONE, 0x8000);
|
||||
oxygen_write_ac97(chip, 1, AC97_PC_BEEP, 0x8000);
|
||||
oxygen_write_ac97(chip, 1, AC97_MIC, 0x8808);
|
||||
oxygen_write_ac97(chip, 1, AC97_LINE, 0x8808);
|
||||
oxygen_write_ac97(chip, 1, AC97_CD, 0x8808);
|
||||
oxygen_write_ac97(chip, 1, AC97_VIDEO, 0x8808);
|
||||
oxygen_write_ac97(chip, 1, AC97_AUX, 0x8808);
|
||||
oxygen_write_ac97(chip, 1, AC97_PCM, 0x0808);
|
||||
oxygen_write_ac97(chip, 1, AC97_REC_SEL, 0x0000);
|
||||
oxygen_write_ac97(chip, 1, AC97_REC_GAIN, 0x0000);
|
||||
oxygen_ac97_set_bits(chip, 1, 0x6a, 0x0040);
|
||||
}
|
||||
}
|
||||
|
||||
static void oxygen_shutdown(struct oxygen *chip)
|
||||
{
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
chip->interrupt_mask = 0;
|
||||
chip->pcm_running = 0;
|
||||
oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
|
||||
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
}
|
||||
|
||||
static void oxygen_card_free(struct snd_card *card)
|
||||
{
|
||||
struct oxygen *chip = card->private_data;
|
||||
|
||||
oxygen_shutdown(chip);
|
||||
if (chip->irq >= 0)
|
||||
free_irq(chip->irq, chip);
|
||||
flush_work(&chip->spdif_input_bits_work);
|
||||
flush_work(&chip->gpio_work);
|
||||
chip->model.cleanup(chip);
|
||||
kfree(chip->model_data);
|
||||
mutex_destroy(&chip->mutex);
|
||||
pci_release_regions(chip->pci);
|
||||
pci_disable_device(chip->pci);
|
||||
}
|
||||
|
||||
int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
|
||||
struct module *owner,
|
||||
const struct pci_device_id *ids,
|
||||
int (*get_model)(struct oxygen *chip,
|
||||
const struct pci_device_id *id
|
||||
)
|
||||
)
|
||||
{
|
||||
struct snd_card *card;
|
||||
struct oxygen *chip;
|
||||
const struct pci_device_id *pci_id;
|
||||
int err;
|
||||
|
||||
err = snd_card_new(&pci->dev, index, id, owner,
|
||||
sizeof(*chip), &card);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
chip = card->private_data;
|
||||
chip->card = card;
|
||||
chip->pci = pci;
|
||||
chip->irq = -1;
|
||||
spin_lock_init(&chip->reg_lock);
|
||||
mutex_init(&chip->mutex);
|
||||
INIT_WORK(&chip->spdif_input_bits_work,
|
||||
oxygen_spdif_input_bits_changed);
|
||||
INIT_WORK(&chip->gpio_work, oxygen_gpio_changed);
|
||||
init_waitqueue_head(&chip->ac97_waitqueue);
|
||||
|
||||
err = pci_enable_device(pci);
|
||||
if (err < 0)
|
||||
goto err_card;
|
||||
|
||||
err = pci_request_regions(pci, DRIVER);
|
||||
if (err < 0) {
|
||||
dev_err(card->dev, "cannot reserve PCI resources\n");
|
||||
goto err_pci_enable;
|
||||
}
|
||||
|
||||
if (!(pci_resource_flags(pci, 0) & IORESOURCE_IO) ||
|
||||
pci_resource_len(pci, 0) < OXYGEN_IO_SIZE) {
|
||||
dev_err(card->dev, "invalid PCI I/O range\n");
|
||||
err = -ENXIO;
|
||||
goto err_pci_regions;
|
||||
}
|
||||
chip->addr = pci_resource_start(pci, 0);
|
||||
|
||||
pci_id = oxygen_search_pci_id(chip, ids);
|
||||
if (!pci_id) {
|
||||
err = -ENODEV;
|
||||
goto err_pci_regions;
|
||||
}
|
||||
oxygen_restore_eeprom(chip, pci_id);
|
||||
err = get_model(chip, pci_id);
|
||||
if (err < 0)
|
||||
goto err_pci_regions;
|
||||
|
||||
if (chip->model.model_data_size) {
|
||||
chip->model_data = kzalloc(chip->model.model_data_size,
|
||||
GFP_KERNEL);
|
||||
if (!chip->model_data) {
|
||||
err = -ENOMEM;
|
||||
goto err_pci_regions;
|
||||
}
|
||||
}
|
||||
|
||||
pci_set_master(pci);
|
||||
card->private_free = oxygen_card_free;
|
||||
|
||||
configure_pcie_bridge(pci);
|
||||
oxygen_init(chip);
|
||||
chip->model.init(chip);
|
||||
|
||||
err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED,
|
||||
KBUILD_MODNAME, chip);
|
||||
if (err < 0) {
|
||||
dev_err(card->dev, "cannot grab interrupt %d\n", pci->irq);
|
||||
goto err_card;
|
||||
}
|
||||
chip->irq = pci->irq;
|
||||
|
||||
strcpy(card->driver, chip->model.chip);
|
||||
strcpy(card->shortname, chip->model.shortname);
|
||||
sprintf(card->longname, "%s at %#lx, irq %i",
|
||||
chip->model.longname, chip->addr, chip->irq);
|
||||
strcpy(card->mixername, chip->model.chip);
|
||||
snd_component_add(card, chip->model.chip);
|
||||
|
||||
err = oxygen_pcm_init(chip);
|
||||
if (err < 0)
|
||||
goto err_card;
|
||||
|
||||
err = oxygen_mixer_init(chip);
|
||||
if (err < 0)
|
||||
goto err_card;
|
||||
|
||||
if (chip->model.device_config & (MIDI_OUTPUT | MIDI_INPUT)) {
|
||||
unsigned int info_flags =
|
||||
MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK;
|
||||
if (chip->model.device_config & MIDI_OUTPUT)
|
||||
info_flags |= MPU401_INFO_OUTPUT;
|
||||
if (chip->model.device_config & MIDI_INPUT)
|
||||
info_flags |= MPU401_INFO_INPUT;
|
||||
err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
|
||||
chip->addr + OXYGEN_MPU401,
|
||||
info_flags, -1, &chip->midi);
|
||||
if (err < 0)
|
||||
goto err_card;
|
||||
}
|
||||
|
||||
oxygen_proc_init(chip);
|
||||
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
if (chip->model.device_config & CAPTURE_1_FROM_SPDIF)
|
||||
chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT;
|
||||
if (chip->has_ac97_0 | chip->has_ac97_1)
|
||||
chip->interrupt_mask |= OXYGEN_INT_AC97;
|
||||
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
|
||||
err = snd_card_register(card);
|
||||
if (err < 0)
|
||||
goto err_card;
|
||||
|
||||
pci_set_drvdata(pci, card);
|
||||
return 0;
|
||||
|
||||
err_pci_regions:
|
||||
pci_release_regions(pci);
|
||||
err_pci_enable:
|
||||
pci_disable_device(pci);
|
||||
err_card:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_pci_probe);
|
||||
|
||||
void oxygen_pci_remove(struct pci_dev *pci)
|
||||
{
|
||||
snd_card_free(pci_get_drvdata(pci));
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_pci_remove);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int oxygen_pci_suspend(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci = to_pci_dev(dev);
|
||||
struct snd_card *card = dev_get_drvdata(dev);
|
||||
struct oxygen *chip = card->private_data;
|
||||
unsigned int i, saved_interrupt_mask;
|
||||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
||||
|
||||
for (i = 0; i < PCM_COUNT; ++i)
|
||||
if (chip->streams[i])
|
||||
snd_pcm_suspend(chip->streams[i]);
|
||||
|
||||
if (chip->model.suspend)
|
||||
chip->model.suspend(chip);
|
||||
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
saved_interrupt_mask = chip->interrupt_mask;
|
||||
chip->interrupt_mask = 0;
|
||||
oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
|
||||
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
|
||||
synchronize_irq(chip->irq);
|
||||
flush_work(&chip->spdif_input_bits_work);
|
||||
flush_work(&chip->gpio_work);
|
||||
chip->interrupt_mask = saved_interrupt_mask;
|
||||
|
||||
pci_disable_device(pci);
|
||||
pci_save_state(pci);
|
||||
pci_set_power_state(pci, PCI_D3hot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const u32 registers_to_restore[OXYGEN_IO_SIZE / 32] = {
|
||||
0xffffffff, 0x00ff077f, 0x00011d08, 0x007f00ff,
|
||||
0x00300000, 0x00000fe4, 0x0ff7001f, 0x00000000
|
||||
};
|
||||
static const u32 ac97_registers_to_restore[2][0x40 / 32] = {
|
||||
{ 0x18284fa2, 0x03060000 },
|
||||
{ 0x00007fa6, 0x00200000 }
|
||||
};
|
||||
|
||||
static inline int is_bit_set(const u32 *bitmap, unsigned int bit)
|
||||
{
|
||||
return bitmap[bit / 32] & (1 << (bit & 31));
|
||||
}
|
||||
|
||||
static void oxygen_restore_ac97(struct oxygen *chip, unsigned int codec)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
oxygen_write_ac97(chip, codec, AC97_RESET, 0);
|
||||
msleep(1);
|
||||
for (i = 1; i < 0x40; ++i)
|
||||
if (is_bit_set(ac97_registers_to_restore[codec], i))
|
||||
oxygen_write_ac97(chip, codec, i * 2,
|
||||
chip->saved_ac97_registers[codec][i]);
|
||||
}
|
||||
|
||||
static int oxygen_pci_resume(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci = to_pci_dev(dev);
|
||||
struct snd_card *card = dev_get_drvdata(dev);
|
||||
struct oxygen *chip = card->private_data;
|
||||
unsigned int i;
|
||||
|
||||
pci_set_power_state(pci, PCI_D0);
|
||||
pci_restore_state(pci);
|
||||
if (pci_enable_device(pci) < 0) {
|
||||
dev_err(dev, "cannot reenable device");
|
||||
snd_card_disconnect(card);
|
||||
return -EIO;
|
||||
}
|
||||
pci_set_master(pci);
|
||||
|
||||
oxygen_write16(chip, OXYGEN_DMA_STATUS, 0);
|
||||
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);
|
||||
for (i = 0; i < OXYGEN_IO_SIZE; ++i)
|
||||
if (is_bit_set(registers_to_restore, i))
|
||||
oxygen_write8(chip, i, chip->saved_registers._8[i]);
|
||||
if (chip->has_ac97_0)
|
||||
oxygen_restore_ac97(chip, 0);
|
||||
if (chip->has_ac97_1)
|
||||
oxygen_restore_ac97(chip, 1);
|
||||
|
||||
if (chip->model.resume)
|
||||
chip->model.resume(chip);
|
||||
|
||||
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
|
||||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SIMPLE_DEV_PM_OPS(oxygen_pci_pm, oxygen_pci_suspend, oxygen_pci_resume);
|
||||
EXPORT_SYMBOL(oxygen_pci_pm);
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
void oxygen_pci_shutdown(struct pci_dev *pci)
|
||||
{
|
||||
struct snd_card *card = pci_get_drvdata(pci);
|
||||
struct oxygen *chip = card->private_data;
|
||||
|
||||
oxygen_shutdown(chip);
|
||||
chip->model.cleanup(chip);
|
||||
}
|
||||
EXPORT_SYMBOL(oxygen_pci_shutdown);
|
1103
sound/pci/oxygen/oxygen_mixer.c
Normal file
1103
sound/pci/oxygen/oxygen_mixer.c
Normal file
File diff suppressed because it is too large
Load diff
776
sound/pci/oxygen/oxygen_pcm.c
Normal file
776
sound/pci/oxygen/oxygen_pcm.c
Normal file
|
@ -0,0 +1,776 @@
|
|||
/*
|
||||
* C-Media CMI8788 driver - PCM code
|
||||
*
|
||||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||
*
|
||||
*
|
||||
* This driver is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2.
|
||||
*
|
||||
* This driver 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 driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include "oxygen.h"
|
||||
|
||||
/* most DMA channels have a 16-bit counter for 32-bit words */
|
||||
#define BUFFER_BYTES_MAX ((1 << 16) * 4)
|
||||
/* the multichannel DMA channel has a 24-bit counter */
|
||||
#define BUFFER_BYTES_MAX_MULTICH ((1 << 24) * 4)
|
||||
|
||||
#define FIFO_BYTES 256
|
||||
#define FIFO_BYTES_MULTICH 1024
|
||||
|
||||
#define PERIOD_BYTES_MIN 64
|
||||
|
||||
#define DEFAULT_BUFFER_BYTES (BUFFER_BYTES_MAX / 2)
|
||||
#define DEFAULT_BUFFER_BYTES_MULTICH (1024 * 1024)
|
||||
|
||||
static const struct snd_pcm_hardware oxygen_stereo_hardware = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_SYNC_START |
|
||||
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
.rates = SNDRV_PCM_RATE_32000 |
|
||||
SNDRV_PCM_RATE_44100 |
|
||||
SNDRV_PCM_RATE_48000 |
|
||||
SNDRV_PCM_RATE_64000 |
|
||||
SNDRV_PCM_RATE_88200 |
|
||||
SNDRV_PCM_RATE_96000 |
|
||||
SNDRV_PCM_RATE_176400 |
|
||||
SNDRV_PCM_RATE_192000,
|
||||
.rate_min = 32000,
|
||||
.rate_max = 192000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = BUFFER_BYTES_MAX,
|
||||
.period_bytes_min = PERIOD_BYTES_MIN,
|
||||
.period_bytes_max = BUFFER_BYTES_MAX,
|
||||
.periods_min = 1,
|
||||
.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
|
||||
.fifo_size = FIFO_BYTES,
|
||||
};
|
||||
static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_SYNC_START |
|
||||
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_S32_LE,
|
||||
.rates = SNDRV_PCM_RATE_32000 |
|
||||
SNDRV_PCM_RATE_44100 |
|
||||
SNDRV_PCM_RATE_48000 |
|
||||
SNDRV_PCM_RATE_64000 |
|
||||
SNDRV_PCM_RATE_88200 |
|
||||
SNDRV_PCM_RATE_96000 |
|
||||
SNDRV_PCM_RATE_176400 |
|
||||
SNDRV_PCM_RATE_192000,
|
||||
.rate_min = 32000,
|
||||
.rate_max = 192000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 8,
|
||||
.buffer_bytes_max = BUFFER_BYTES_MAX_MULTICH,
|
||||
.period_bytes_min = PERIOD_BYTES_MIN,
|
||||
.period_bytes_max = BUFFER_BYTES_MAX_MULTICH,
|
||||
.periods_min = 1,
|
||||
.periods_max = BUFFER_BYTES_MAX_MULTICH / PERIOD_BYTES_MIN,
|
||||
.fifo_size = FIFO_BYTES_MULTICH,
|
||||
};
|
||||
static const struct snd_pcm_hardware oxygen_ac97_hardware = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_SYNC_START |
|
||||
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.rate_min = 48000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = BUFFER_BYTES_MAX,
|
||||
.period_bytes_min = PERIOD_BYTES_MIN,
|
||||
.period_bytes_max = BUFFER_BYTES_MAX,
|
||||
.periods_min = 1,
|
||||
.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
|
||||
.fifo_size = FIFO_BYTES,
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hardware *const oxygen_hardware[PCM_COUNT] = {
|
||||
[PCM_A] = &oxygen_stereo_hardware,
|
||||
[PCM_B] = &oxygen_stereo_hardware,
|
||||
[PCM_C] = &oxygen_stereo_hardware,
|
||||
[PCM_SPDIF] = &oxygen_stereo_hardware,
|
||||
[PCM_MULTICH] = &oxygen_multichannel_hardware,
|
||||
[PCM_AC97] = &oxygen_ac97_hardware,
|
||||
};
|
||||
|
||||
static inline unsigned int
|
||||
oxygen_substream_channel(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return (unsigned int)(uintptr_t)substream->runtime->private_data;
|
||||
}
|
||||
|
||||
static int oxygen_open(struct snd_pcm_substream *substream,
|
||||
unsigned int channel)
|
||||
{
|
||||
struct oxygen *chip = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int err;
|
||||
|
||||
runtime->private_data = (void *)(uintptr_t)channel;
|
||||
if (channel == PCM_B && chip->has_ac97_1 &&
|
||||
(chip->model.device_config & CAPTURE_2_FROM_AC97_1))
|
||||
runtime->hw = oxygen_ac97_hardware;
|
||||
else
|
||||
runtime->hw = *oxygen_hardware[channel];
|
||||
switch (channel) {
|
||||
case PCM_C:
|
||||
runtime->hw.rates &= ~(SNDRV_PCM_RATE_32000 |
|
||||
SNDRV_PCM_RATE_64000);
|
||||
runtime->hw.rate_min = 44100;
|
||||
/* fall through */
|
||||
case PCM_A:
|
||||
case PCM_B:
|
||||
runtime->hw.fifo_size = 0;
|
||||
break;
|
||||
case PCM_MULTICH:
|
||||
runtime->hw.channels_max = chip->model.dac_channels_pcm;
|
||||
break;
|
||||
}
|
||||
if (chip->model.pcm_hardware_filter)
|
||||
chip->model.pcm_hardware_filter(channel, &runtime->hw);
|
||||
err = snd_pcm_hw_constraint_step(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_pcm_hw_constraint_step(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (runtime->hw.formats & SNDRV_PCM_FMTBIT_S32_LE) {
|
||||
err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
if (runtime->hw.channels_max > 2) {
|
||||
err = snd_pcm_hw_constraint_step(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS,
|
||||
2);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
snd_pcm_set_sync(substream);
|
||||
chip->streams[channel] = substream;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
chip->pcm_active |= 1 << channel;
|
||||
if (channel == PCM_SPDIF) {
|
||||
chip->spdif_pcm_bits = chip->spdif_bits;
|
||||
chip->controls[CONTROL_SPDIF_PCM]->vd[0].access &=
|
||||
~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
|
||||
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
|
||||
SNDRV_CTL_EVENT_MASK_INFO,
|
||||
&chip->controls[CONTROL_SPDIF_PCM]->id);
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oxygen_rec_a_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return oxygen_open(substream, PCM_A);
|
||||
}
|
||||
|
||||
static int oxygen_rec_b_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return oxygen_open(substream, PCM_B);
|
||||
}
|
||||
|
||||
static int oxygen_rec_c_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return oxygen_open(substream, PCM_C);
|
||||
}
|
||||
|
||||
static int oxygen_spdif_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return oxygen_open(substream, PCM_SPDIF);
|
||||
}
|
||||
|
||||
static int oxygen_multich_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return oxygen_open(substream, PCM_MULTICH);
|
||||
}
|
||||
|
||||
static int oxygen_ac97_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return oxygen_open(substream, PCM_AC97);
|
||||
}
|
||||
|
||||
static int oxygen_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct oxygen *chip = snd_pcm_substream_chip(substream);
|
||||
unsigned int channel = oxygen_substream_channel(substream);
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
chip->pcm_active &= ~(1 << channel);
|
||||
if (channel == PCM_SPDIF) {
|
||||
chip->controls[CONTROL_SPDIF_PCM]->vd[0].access |=
|
||||
SNDRV_CTL_ELEM_ACCESS_INACTIVE;
|
||||
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
|
||||
SNDRV_CTL_EVENT_MASK_INFO,
|
||||
&chip->controls[CONTROL_SPDIF_PCM]->id);
|
||||
}
|
||||
if (channel == PCM_SPDIF || channel == PCM_MULTICH)
|
||||
oxygen_update_spdif_source(chip);
|
||||
mutex_unlock(&chip->mutex);
|
||||
|
||||
chip->streams[channel] = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int oxygen_format(struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE)
|
||||
return OXYGEN_FORMAT_24;
|
||||
else
|
||||
return OXYGEN_FORMAT_16;
|
||||
}
|
||||
|
||||
static unsigned int oxygen_rate(struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
switch (params_rate(hw_params)) {
|
||||
case 32000:
|
||||
return OXYGEN_RATE_32000;
|
||||
case 44100:
|
||||
return OXYGEN_RATE_44100;
|
||||
default: /* 48000 */
|
||||
return OXYGEN_RATE_48000;
|
||||
case 64000:
|
||||
return OXYGEN_RATE_64000;
|
||||
case 88200:
|
||||
return OXYGEN_RATE_88200;
|
||||
case 96000:
|
||||
return OXYGEN_RATE_96000;
|
||||
case 176400:
|
||||
return OXYGEN_RATE_176400;
|
||||
case 192000:
|
||||
return OXYGEN_RATE_192000;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE)
|
||||
return OXYGEN_I2S_BITS_24;
|
||||
else
|
||||
return OXYGEN_I2S_BITS_16;
|
||||
}
|
||||
|
||||
static unsigned int oxygen_play_channels(struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
switch (params_channels(hw_params)) {
|
||||
default: /* 2 */
|
||||
return OXYGEN_PLAY_CHANNELS_2;
|
||||
case 4:
|
||||
return OXYGEN_PLAY_CHANNELS_4;
|
||||
case 6:
|
||||
return OXYGEN_PLAY_CHANNELS_6;
|
||||
case 8:
|
||||
return OXYGEN_PLAY_CHANNELS_8;
|
||||
}
|
||||
}
|
||||
|
||||
static const unsigned int channel_base_registers[PCM_COUNT] = {
|
||||
[PCM_A] = OXYGEN_DMA_A_ADDRESS,
|
||||
[PCM_B] = OXYGEN_DMA_B_ADDRESS,
|
||||
[PCM_C] = OXYGEN_DMA_C_ADDRESS,
|
||||
[PCM_SPDIF] = OXYGEN_DMA_SPDIF_ADDRESS,
|
||||
[PCM_MULTICH] = OXYGEN_DMA_MULTICH_ADDRESS,
|
||||
[PCM_AC97] = OXYGEN_DMA_AC97_ADDRESS,
|
||||
};
|
||||
|
||||
static int oxygen_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct oxygen *chip = snd_pcm_substream_chip(substream);
|
||||
unsigned int channel = oxygen_substream_channel(substream);
|
||||
int err;
|
||||
|
||||
err = snd_pcm_lib_malloc_pages(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
oxygen_write32(chip, channel_base_registers[channel],
|
||||
(u32)substream->runtime->dma_addr);
|
||||
if (channel == PCM_MULTICH) {
|
||||
oxygen_write32(chip, OXYGEN_DMA_MULTICH_COUNT,
|
||||
params_buffer_bytes(hw_params) / 4 - 1);
|
||||
oxygen_write32(chip, OXYGEN_DMA_MULTICH_TCOUNT,
|
||||
params_period_bytes(hw_params) / 4 - 1);
|
||||
} else {
|
||||
oxygen_write16(chip, channel_base_registers[channel] + 4,
|
||||
params_buffer_bytes(hw_params) / 4 - 1);
|
||||
oxygen_write16(chip, channel_base_registers[channel] + 6,
|
||||
params_period_bytes(hw_params) / 4 - 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u16 get_mclk(struct oxygen *chip, unsigned int channel,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
unsigned int mclks, shift;
|
||||
|
||||
if (channel == PCM_MULTICH)
|
||||
mclks = chip->model.dac_mclks;
|
||||
else
|
||||
mclks = chip->model.adc_mclks;
|
||||
|
||||
if (params_rate(params) <= 48000)
|
||||
shift = 0;
|
||||
else if (params_rate(params) <= 96000)
|
||||
shift = 2;
|
||||
else
|
||||
shift = 4;
|
||||
|
||||
return OXYGEN_I2S_MCLK(mclks >> shift);
|
||||
}
|
||||
|
||||
static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct oxygen *chip = snd_pcm_substream_chip(substream);
|
||||
int err;
|
||||
|
||||
err = oxygen_hw_params(substream, hw_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
|
||||
oxygen_format(hw_params) << OXYGEN_REC_FORMAT_A_SHIFT,
|
||||
OXYGEN_REC_FORMAT_A_MASK);
|
||||
oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
|
||||
oxygen_rate(hw_params) |
|
||||
chip->model.adc_i2s_format |
|
||||
get_mclk(chip, PCM_A, hw_params) |
|
||||
oxygen_i2s_bits(hw_params),
|
||||
OXYGEN_I2S_RATE_MASK |
|
||||
OXYGEN_I2S_FORMAT_MASK |
|
||||
OXYGEN_I2S_MCLK_MASK |
|
||||
OXYGEN_I2S_BITS_MASK);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
chip->model.set_adc_params(chip, hw_params);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct oxygen *chip = snd_pcm_substream_chip(substream);
|
||||
int is_ac97;
|
||||
int err;
|
||||
|
||||
err = oxygen_hw_params(substream, hw_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
is_ac97 = chip->has_ac97_1 &&
|
||||
(chip->model.device_config & CAPTURE_2_FROM_AC97_1);
|
||||
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
|
||||
oxygen_format(hw_params) << OXYGEN_REC_FORMAT_B_SHIFT,
|
||||
OXYGEN_REC_FORMAT_B_MASK);
|
||||
if (!is_ac97)
|
||||
oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
|
||||
oxygen_rate(hw_params) |
|
||||
chip->model.adc_i2s_format |
|
||||
get_mclk(chip, PCM_B, hw_params) |
|
||||
oxygen_i2s_bits(hw_params),
|
||||
OXYGEN_I2S_RATE_MASK |
|
||||
OXYGEN_I2S_FORMAT_MASK |
|
||||
OXYGEN_I2S_MCLK_MASK |
|
||||
OXYGEN_I2S_BITS_MASK);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
|
||||
if (!is_ac97) {
|
||||
mutex_lock(&chip->mutex);
|
||||
chip->model.set_adc_params(chip, hw_params);
|
||||
mutex_unlock(&chip->mutex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oxygen_rec_c_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct oxygen *chip = snd_pcm_substream_chip(substream);
|
||||
int err;
|
||||
|
||||
err = oxygen_hw_params(substream, hw_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
|
||||
oxygen_format(hw_params) << OXYGEN_REC_FORMAT_C_SHIFT,
|
||||
OXYGEN_REC_FORMAT_C_MASK);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct oxygen *chip = snd_pcm_substream_chip(substream);
|
||||
int err;
|
||||
|
||||
err = oxygen_hw_params(substream, hw_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
|
||||
OXYGEN_SPDIF_OUT_ENABLE);
|
||||
oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT,
|
||||
oxygen_format(hw_params) << OXYGEN_SPDIF_FORMAT_SHIFT,
|
||||
OXYGEN_SPDIF_FORMAT_MASK);
|
||||
oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,
|
||||
oxygen_rate(hw_params) << OXYGEN_SPDIF_OUT_RATE_SHIFT,
|
||||
OXYGEN_SPDIF_OUT_RATE_MASK);
|
||||
oxygen_update_spdif_source(chip);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
struct oxygen *chip = snd_pcm_substream_chip(substream);
|
||||
int err;
|
||||
|
||||
err = oxygen_hw_params(substream, hw_params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
oxygen_write8_masked(chip, OXYGEN_PLAY_CHANNELS,
|
||||
oxygen_play_channels(hw_params),
|
||||
OXYGEN_PLAY_CHANNELS_MASK);
|
||||
oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT,
|
||||
oxygen_format(hw_params) << OXYGEN_MULTICH_FORMAT_SHIFT,
|
||||
OXYGEN_MULTICH_FORMAT_MASK);
|
||||
oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
|
||||
oxygen_rate(hw_params) |
|
||||
chip->model.dac_i2s_format |
|
||||
get_mclk(chip, PCM_MULTICH, hw_params) |
|
||||
oxygen_i2s_bits(hw_params),
|
||||
OXYGEN_I2S_RATE_MASK |
|
||||
OXYGEN_I2S_FORMAT_MASK |
|
||||
OXYGEN_I2S_MCLK_MASK |
|
||||
OXYGEN_I2S_BITS_MASK);
|
||||
oxygen_update_spdif_source(chip);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
|
||||
chip->model.set_dac_params(chip, hw_params);
|
||||
oxygen_update_dac_routing(chip);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oxygen_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct oxygen *chip = snd_pcm_substream_chip(substream);
|
||||
unsigned int channel = oxygen_substream_channel(substream);
|
||||
unsigned int channel_mask = 1 << channel;
|
||||
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
chip->interrupt_mask &= ~channel_mask;
|
||||
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
|
||||
|
||||
oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
|
||||
oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
}
|
||||
|
||||
static int oxygen_spdif_hw_free(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct oxygen *chip = snd_pcm_substream_chip(substream);
|
||||
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
|
||||
OXYGEN_SPDIF_OUT_ENABLE);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
return oxygen_hw_free(substream);
|
||||
}
|
||||
|
||||
static int oxygen_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct oxygen *chip = snd_pcm_substream_chip(substream);
|
||||
unsigned int channel = oxygen_substream_channel(substream);
|
||||
unsigned int channel_mask = 1 << channel;
|
||||
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
|
||||
oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
|
||||
|
||||
if (substream->runtime->no_period_wakeup)
|
||||
chip->interrupt_mask &= ~channel_mask;
|
||||
else
|
||||
chip->interrupt_mask |= channel_mask;
|
||||
oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oxygen_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct oxygen *chip = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_substream *s;
|
||||
unsigned int mask = 0;
|
||||
int pausing;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
pausing = 0;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
pausing = 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snd_pcm_group_for_each_entry(s, substream) {
|
||||
if (snd_pcm_substream_chip(s) == chip) {
|
||||
mask |= 1 << oxygen_substream_channel(s);
|
||||
snd_pcm_trigger_done(s, substream);
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock(&chip->reg_lock);
|
||||
if (!pausing) {
|
||||
if (cmd == SNDRV_PCM_TRIGGER_START)
|
||||
chip->pcm_running |= mask;
|
||||
else
|
||||
chip->pcm_running &= ~mask;
|
||||
oxygen_write8(chip, OXYGEN_DMA_STATUS, chip->pcm_running);
|
||||
} else {
|
||||
if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
|
||||
oxygen_set_bits8(chip, OXYGEN_DMA_PAUSE, mask);
|
||||
else
|
||||
oxygen_clear_bits8(chip, OXYGEN_DMA_PAUSE, mask);
|
||||
}
|
||||
spin_unlock(&chip->reg_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t oxygen_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct oxygen *chip = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
unsigned int channel = oxygen_substream_channel(substream);
|
||||
u32 curr_addr;
|
||||
|
||||
/* no spinlock, this read should be atomic */
|
||||
curr_addr = oxygen_read32(chip, channel_base_registers[channel]);
|
||||
return bytes_to_frames(runtime, curr_addr - (u32)runtime->dma_addr);
|
||||
}
|
||||
|
||||
static struct snd_pcm_ops oxygen_rec_a_ops = {
|
||||
.open = oxygen_rec_a_open,
|
||||
.close = oxygen_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = oxygen_rec_a_hw_params,
|
||||
.hw_free = oxygen_hw_free,
|
||||
.prepare = oxygen_prepare,
|
||||
.trigger = oxygen_trigger,
|
||||
.pointer = oxygen_pointer,
|
||||
};
|
||||
|
||||
static struct snd_pcm_ops oxygen_rec_b_ops = {
|
||||
.open = oxygen_rec_b_open,
|
||||
.close = oxygen_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = oxygen_rec_b_hw_params,
|
||||
.hw_free = oxygen_hw_free,
|
||||
.prepare = oxygen_prepare,
|
||||
.trigger = oxygen_trigger,
|
||||
.pointer = oxygen_pointer,
|
||||
};
|
||||
|
||||
static struct snd_pcm_ops oxygen_rec_c_ops = {
|
||||
.open = oxygen_rec_c_open,
|
||||
.close = oxygen_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = oxygen_rec_c_hw_params,
|
||||
.hw_free = oxygen_hw_free,
|
||||
.prepare = oxygen_prepare,
|
||||
.trigger = oxygen_trigger,
|
||||
.pointer = oxygen_pointer,
|
||||
};
|
||||
|
||||
static struct snd_pcm_ops oxygen_spdif_ops = {
|
||||
.open = oxygen_spdif_open,
|
||||
.close = oxygen_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = oxygen_spdif_hw_params,
|
||||
.hw_free = oxygen_spdif_hw_free,
|
||||
.prepare = oxygen_prepare,
|
||||
.trigger = oxygen_trigger,
|
||||
.pointer = oxygen_pointer,
|
||||
};
|
||||
|
||||
static struct snd_pcm_ops oxygen_multich_ops = {
|
||||
.open = oxygen_multich_open,
|
||||
.close = oxygen_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = oxygen_multich_hw_params,
|
||||
.hw_free = oxygen_hw_free,
|
||||
.prepare = oxygen_prepare,
|
||||
.trigger = oxygen_trigger,
|
||||
.pointer = oxygen_pointer,
|
||||
};
|
||||
|
||||
static struct snd_pcm_ops oxygen_ac97_ops = {
|
||||
.open = oxygen_ac97_open,
|
||||
.close = oxygen_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = oxygen_hw_params,
|
||||
.hw_free = oxygen_hw_free,
|
||||
.prepare = oxygen_prepare,
|
||||
.trigger = oxygen_trigger,
|
||||
.pointer = oxygen_pointer,
|
||||
};
|
||||
|
||||
static void oxygen_pcm_free(struct snd_pcm *pcm)
|
||||
{
|
||||
snd_pcm_lib_preallocate_free_for_all(pcm);
|
||||
}
|
||||
|
||||
int oxygen_pcm_init(struct oxygen *chip)
|
||||
{
|
||||
struct snd_pcm *pcm;
|
||||
int outs, ins;
|
||||
int err;
|
||||
|
||||
outs = !!(chip->model.device_config & PLAYBACK_0_TO_I2S);
|
||||
ins = !!(chip->model.device_config & (CAPTURE_0_FROM_I2S_1 |
|
||||
CAPTURE_0_FROM_I2S_2));
|
||||
if (outs | ins) {
|
||||
err = snd_pcm_new(chip->card, "Multichannel",
|
||||
0, outs, ins, &pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (outs)
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
|
||||
&oxygen_multich_ops);
|
||||
if (chip->model.device_config & CAPTURE_0_FROM_I2S_1)
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
|
||||
&oxygen_rec_a_ops);
|
||||
else if (chip->model.device_config & CAPTURE_0_FROM_I2S_2)
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
|
||||
&oxygen_rec_b_ops);
|
||||
pcm->private_data = chip;
|
||||
pcm->private_free = oxygen_pcm_free;
|
||||
strcpy(pcm->name, "Multichannel");
|
||||
if (outs)
|
||||
snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
|
||||
SNDRV_DMA_TYPE_DEV,
|
||||
snd_dma_pci_data(chip->pci),
|
||||
DEFAULT_BUFFER_BYTES_MULTICH,
|
||||
BUFFER_BYTES_MAX_MULTICH);
|
||||
if (ins)
|
||||
snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
|
||||
SNDRV_DMA_TYPE_DEV,
|
||||
snd_dma_pci_data(chip->pci),
|
||||
DEFAULT_BUFFER_BYTES,
|
||||
BUFFER_BYTES_MAX);
|
||||
}
|
||||
|
||||
outs = !!(chip->model.device_config & PLAYBACK_1_TO_SPDIF);
|
||||
ins = !!(chip->model.device_config & CAPTURE_1_FROM_SPDIF);
|
||||
if (outs | ins) {
|
||||
err = snd_pcm_new(chip->card, "Digital", 1, outs, ins, &pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (outs)
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
|
||||
&oxygen_spdif_ops);
|
||||
if (ins)
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
|
||||
&oxygen_rec_c_ops);
|
||||
pcm->private_data = chip;
|
||||
pcm->private_free = oxygen_pcm_free;
|
||||
strcpy(pcm->name, "Digital");
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
||||
snd_dma_pci_data(chip->pci),
|
||||
DEFAULT_BUFFER_BYTES,
|
||||
BUFFER_BYTES_MAX);
|
||||
}
|
||||
|
||||
if (chip->has_ac97_1) {
|
||||
outs = !!(chip->model.device_config & PLAYBACK_2_TO_AC97_1);
|
||||
ins = !!(chip->model.device_config & CAPTURE_2_FROM_AC97_1);
|
||||
} else {
|
||||
outs = 0;
|
||||
ins = !!(chip->model.device_config & CAPTURE_2_FROM_I2S_2);
|
||||
}
|
||||
if (outs | ins) {
|
||||
err = snd_pcm_new(chip->card, outs ? "AC97" : "Analog2",
|
||||
2, outs, ins, &pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (outs) {
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
|
||||
&oxygen_ac97_ops);
|
||||
oxygen_write8_masked(chip, OXYGEN_REC_ROUTING,
|
||||
OXYGEN_REC_B_ROUTE_AC97_1,
|
||||
OXYGEN_REC_B_ROUTE_MASK);
|
||||
}
|
||||
if (ins)
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
|
||||
&oxygen_rec_b_ops);
|
||||
pcm->private_data = chip;
|
||||
pcm->private_free = oxygen_pcm_free;
|
||||
strcpy(pcm->name, outs ? "Front Panel" : "Analog 2");
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
||||
snd_dma_pci_data(chip->pci),
|
||||
DEFAULT_BUFFER_BYTES,
|
||||
BUFFER_BYTES_MAX);
|
||||
}
|
||||
return 0;
|
||||
}
|
458
sound/pci/oxygen/oxygen_regs.h
Normal file
458
sound/pci/oxygen/oxygen_regs.h
Normal file
|
@ -0,0 +1,458 @@
|
|||
#ifndef OXYGEN_REGS_H_INCLUDED
|
||||
#define OXYGEN_REGS_H_INCLUDED
|
||||
|
||||
/* recording channel A */
|
||||
#define OXYGEN_DMA_A_ADDRESS 0x00 /* 32-bit base address */
|
||||
#define OXYGEN_DMA_A_COUNT 0x04 /* buffer counter (dwords) */
|
||||
#define OXYGEN_DMA_A_TCOUNT 0x06 /* interrupt counter (dwords) */
|
||||
|
||||
/* recording channel B */
|
||||
#define OXYGEN_DMA_B_ADDRESS 0x08
|
||||
#define OXYGEN_DMA_B_COUNT 0x0c
|
||||
#define OXYGEN_DMA_B_TCOUNT 0x0e
|
||||
|
||||
/* recording channel C */
|
||||
#define OXYGEN_DMA_C_ADDRESS 0x10
|
||||
#define OXYGEN_DMA_C_COUNT 0x14
|
||||
#define OXYGEN_DMA_C_TCOUNT 0x16
|
||||
|
||||
/* SPDIF playback channel */
|
||||
#define OXYGEN_DMA_SPDIF_ADDRESS 0x18
|
||||
#define OXYGEN_DMA_SPDIF_COUNT 0x1c
|
||||
#define OXYGEN_DMA_SPDIF_TCOUNT 0x1e
|
||||
|
||||
/* multichannel playback channel */
|
||||
#define OXYGEN_DMA_MULTICH_ADDRESS 0x20
|
||||
#define OXYGEN_DMA_MULTICH_COUNT 0x24 /* 24 bits */
|
||||
#define OXYGEN_DMA_MULTICH_TCOUNT 0x28 /* 24 bits */
|
||||
|
||||
/* AC'97 (front panel) playback channel */
|
||||
#define OXYGEN_DMA_AC97_ADDRESS 0x30
|
||||
#define OXYGEN_DMA_AC97_COUNT 0x34
|
||||
#define OXYGEN_DMA_AC97_TCOUNT 0x36
|
||||
|
||||
/* all registers 0x00..0x36 return current position on read */
|
||||
|
||||
#define OXYGEN_DMA_STATUS 0x40 /* 1 = running, 0 = stop */
|
||||
#define OXYGEN_CHANNEL_A 0x01
|
||||
#define OXYGEN_CHANNEL_B 0x02
|
||||
#define OXYGEN_CHANNEL_C 0x04
|
||||
#define OXYGEN_CHANNEL_SPDIF 0x08
|
||||
#define OXYGEN_CHANNEL_MULTICH 0x10
|
||||
#define OXYGEN_CHANNEL_AC97 0x20
|
||||
|
||||
#define OXYGEN_DMA_PAUSE 0x41 /* 1 = pause */
|
||||
/* OXYGEN_CHANNEL_* */
|
||||
|
||||
#define OXYGEN_DMA_RESET 0x42
|
||||
/* OXYGEN_CHANNEL_* */
|
||||
|
||||
#define OXYGEN_PLAY_CHANNELS 0x43
|
||||
#define OXYGEN_PLAY_CHANNELS_MASK 0x03
|
||||
#define OXYGEN_PLAY_CHANNELS_2 0x00
|
||||
#define OXYGEN_PLAY_CHANNELS_4 0x01
|
||||
#define OXYGEN_PLAY_CHANNELS_6 0x02
|
||||
#define OXYGEN_PLAY_CHANNELS_8 0x03
|
||||
#define OXYGEN_DMA_A_BURST_MASK 0x04
|
||||
#define OXYGEN_DMA_A_BURST_8 0x00 /* dwords */
|
||||
#define OXYGEN_DMA_A_BURST_16 0x04
|
||||
#define OXYGEN_DMA_MULTICH_BURST_MASK 0x08
|
||||
#define OXYGEN_DMA_MULTICH_BURST_8 0x00
|
||||
#define OXYGEN_DMA_MULTICH_BURST_16 0x08
|
||||
|
||||
#define OXYGEN_INTERRUPT_MASK 0x44
|
||||
/* OXYGEN_CHANNEL_* */
|
||||
#define OXYGEN_INT_SPDIF_IN_DETECT 0x0100
|
||||
#define OXYGEN_INT_MCU 0x0200
|
||||
#define OXYGEN_INT_2WIRE 0x0400
|
||||
#define OXYGEN_INT_GPIO 0x0800
|
||||
#define OXYGEN_INT_MCB 0x2000
|
||||
#define OXYGEN_INT_AC97 0x4000
|
||||
|
||||
#define OXYGEN_INTERRUPT_STATUS 0x46
|
||||
/* OXYGEN_CHANNEL_* amd OXYGEN_INT_* */
|
||||
#define OXYGEN_INT_MIDI 0x1000
|
||||
|
||||
#define OXYGEN_MISC 0x48
|
||||
#define OXYGEN_MISC_WRITE_PCI_SUBID 0x01
|
||||
#define OXYGEN_MISC_LATENCY_3F 0x02
|
||||
#define OXYGEN_MISC_REC_C_FROM_SPDIF 0x04
|
||||
#define OXYGEN_MISC_REC_B_FROM_AC97 0x08
|
||||
#define OXYGEN_MISC_REC_A_FROM_MULTICH 0x10
|
||||
#define OXYGEN_MISC_PCI_MEM_W_1_CLOCK 0x20
|
||||
#define OXYGEN_MISC_MIDI 0x40
|
||||
#define OXYGEN_MISC_CRYSTAL_MASK 0x80
|
||||
#define OXYGEN_MISC_CRYSTAL_24576 0x00
|
||||
#define OXYGEN_MISC_CRYSTAL_27 0x80 /* MHz */
|
||||
|
||||
#define OXYGEN_REC_FORMAT 0x4a
|
||||
#define OXYGEN_REC_FORMAT_A_MASK 0x03
|
||||
#define OXYGEN_REC_FORMAT_A_SHIFT 0
|
||||
#define OXYGEN_REC_FORMAT_B_MASK 0x0c
|
||||
#define OXYGEN_REC_FORMAT_B_SHIFT 2
|
||||
#define OXYGEN_REC_FORMAT_C_MASK 0x30
|
||||
#define OXYGEN_REC_FORMAT_C_SHIFT 4
|
||||
#define OXYGEN_FORMAT_16 0x00
|
||||
#define OXYGEN_FORMAT_24 0x01
|
||||
#define OXYGEN_FORMAT_32 0x02
|
||||
|
||||
#define OXYGEN_PLAY_FORMAT 0x4b
|
||||
#define OXYGEN_SPDIF_FORMAT_MASK 0x03
|
||||
#define OXYGEN_SPDIF_FORMAT_SHIFT 0
|
||||
#define OXYGEN_MULTICH_FORMAT_MASK 0x0c
|
||||
#define OXYGEN_MULTICH_FORMAT_SHIFT 2
|
||||
/* OXYGEN_FORMAT_* */
|
||||
|
||||
#define OXYGEN_REC_CHANNELS 0x4c
|
||||
#define OXYGEN_REC_CHANNELS_MASK 0x07
|
||||
#define OXYGEN_REC_CHANNELS_2_2_2 0x00 /* DMA A, B, C */
|
||||
#define OXYGEN_REC_CHANNELS_4_2_2 0x01
|
||||
#define OXYGEN_REC_CHANNELS_6_0_2 0x02
|
||||
#define OXYGEN_REC_CHANNELS_6_2_0 0x03
|
||||
#define OXYGEN_REC_CHANNELS_8_0_0 0x04
|
||||
|
||||
#define OXYGEN_FUNCTION 0x50
|
||||
#define OXYGEN_FUNCTION_CLOCK_MASK 0x01
|
||||
#define OXYGEN_FUNCTION_CLOCK_PLL 0x00
|
||||
#define OXYGEN_FUNCTION_CLOCK_CRYSTAL 0x01
|
||||
#define OXYGEN_FUNCTION_RESET_CODEC 0x02
|
||||
#define OXYGEN_FUNCTION_RESET_POL 0x04
|
||||
#define OXYGEN_FUNCTION_PWDN 0x08
|
||||
#define OXYGEN_FUNCTION_PWDN_EN 0x10
|
||||
#define OXYGEN_FUNCTION_PWDN_POL 0x20
|
||||
#define OXYGEN_FUNCTION_2WIRE_SPI_MASK 0x40
|
||||
#define OXYGEN_FUNCTION_SPI 0x00
|
||||
#define OXYGEN_FUNCTION_2WIRE 0x40
|
||||
#define OXYGEN_FUNCTION_ENABLE_SPI_4_5 0x80 /* 0 = EEPROM */
|
||||
|
||||
#define OXYGEN_I2S_MULTICH_FORMAT 0x60
|
||||
#define OXYGEN_I2S_RATE_MASK 0x0007 /* LRCK */
|
||||
#define OXYGEN_RATE_32000 0x0000
|
||||
#define OXYGEN_RATE_44100 0x0001
|
||||
#define OXYGEN_RATE_48000 0x0002
|
||||
#define OXYGEN_RATE_64000 0x0003
|
||||
#define OXYGEN_RATE_88200 0x0004
|
||||
#define OXYGEN_RATE_96000 0x0005
|
||||
#define OXYGEN_RATE_176400 0x0006
|
||||
#define OXYGEN_RATE_192000 0x0007
|
||||
#define OXYGEN_I2S_FORMAT_MASK 0x0008
|
||||
#define OXYGEN_I2S_FORMAT_I2S 0x0000
|
||||
#define OXYGEN_I2S_FORMAT_LJUST 0x0008
|
||||
#define OXYGEN_I2S_MCLK_MASK 0x0030 /* MCLK/LRCK */
|
||||
#define OXYGEN_I2S_MCLK_SHIFT 4
|
||||
#define MCLK_128 0
|
||||
#define MCLK_256 1
|
||||
#define MCLK_512 2
|
||||
#define OXYGEN_I2S_MCLK(f) (((f) & 3) << OXYGEN_I2S_MCLK_SHIFT)
|
||||
#define OXYGEN_I2S_BITS_MASK 0x00c0
|
||||
#define OXYGEN_I2S_BITS_16 0x0000
|
||||
#define OXYGEN_I2S_BITS_20 0x0040
|
||||
#define OXYGEN_I2S_BITS_24 0x0080
|
||||
#define OXYGEN_I2S_BITS_32 0x00c0
|
||||
#define OXYGEN_I2S_MASTER 0x0100
|
||||
#define OXYGEN_I2S_BCLK_MASK 0x0600 /* BCLK/LRCK */
|
||||
#define OXYGEN_I2S_BCLK_64 0x0000
|
||||
#define OXYGEN_I2S_BCLK_128 0x0200
|
||||
#define OXYGEN_I2S_BCLK_256 0x0400
|
||||
#define OXYGEN_I2S_MUTE_MCLK 0x0800
|
||||
|
||||
#define OXYGEN_I2S_A_FORMAT 0x62
|
||||
#define OXYGEN_I2S_B_FORMAT 0x64
|
||||
#define OXYGEN_I2S_C_FORMAT 0x66
|
||||
/* like OXYGEN_I2S_MULTICH_FORMAT */
|
||||
|
||||
#define OXYGEN_SPDIF_CONTROL 0x70
|
||||
#define OXYGEN_SPDIF_OUT_ENABLE 0x00000002
|
||||
#define OXYGEN_SPDIF_LOOPBACK 0x00000004 /* in to out */
|
||||
#define OXYGEN_SPDIF_SENSE_MASK 0x00000008
|
||||
#define OXYGEN_SPDIF_LOCK_MASK 0x00000010
|
||||
#define OXYGEN_SPDIF_RATE_MASK 0x00000020
|
||||
#define OXYGEN_SPDIF_SPDVALID 0x00000040
|
||||
#define OXYGEN_SPDIF_SENSE_PAR 0x00000200
|
||||
#define OXYGEN_SPDIF_LOCK_PAR 0x00000400
|
||||
#define OXYGEN_SPDIF_SENSE_STATUS 0x00000800
|
||||
#define OXYGEN_SPDIF_LOCK_STATUS 0x00001000
|
||||
#define OXYGEN_SPDIF_SENSE_INT 0x00002000 /* r/wc */
|
||||
#define OXYGEN_SPDIF_LOCK_INT 0x00004000 /* r/wc */
|
||||
#define OXYGEN_SPDIF_RATE_INT 0x00008000 /* r/wc */
|
||||
#define OXYGEN_SPDIF_IN_CLOCK_MASK 0x00010000
|
||||
#define OXYGEN_SPDIF_IN_CLOCK_96 0x00000000 /* <= 96 kHz */
|
||||
#define OXYGEN_SPDIF_IN_CLOCK_192 0x00010000 /* > 96 kHz */
|
||||
#define OXYGEN_SPDIF_OUT_RATE_MASK 0x07000000
|
||||
#define OXYGEN_SPDIF_OUT_RATE_SHIFT 24
|
||||
/* OXYGEN_RATE_* << OXYGEN_SPDIF_OUT_RATE_SHIFT */
|
||||
|
||||
#define OXYGEN_SPDIF_OUTPUT_BITS 0x74
|
||||
#define OXYGEN_SPDIF_NONAUDIO 0x00000002
|
||||
#define OXYGEN_SPDIF_C 0x00000004
|
||||
#define OXYGEN_SPDIF_PREEMPHASIS 0x00000008
|
||||
#define OXYGEN_SPDIF_CATEGORY_MASK 0x000007f0
|
||||
#define OXYGEN_SPDIF_CATEGORY_SHIFT 4
|
||||
#define OXYGEN_SPDIF_ORIGINAL 0x00000800
|
||||
#define OXYGEN_SPDIF_CS_RATE_MASK 0x0000f000
|
||||
#define OXYGEN_SPDIF_CS_RATE_SHIFT 12
|
||||
#define OXYGEN_SPDIF_V 0x00010000 /* 0 = valid */
|
||||
|
||||
#define OXYGEN_SPDIF_INPUT_BITS 0x78
|
||||
/* 32 bits, IEC958_AES_* */
|
||||
|
||||
#define OXYGEN_EEPROM_CONTROL 0x80
|
||||
#define OXYGEN_EEPROM_ADDRESS_MASK 0x7f
|
||||
#define OXYGEN_EEPROM_DIR_MASK 0x80
|
||||
#define OXYGEN_EEPROM_DIR_READ 0x00
|
||||
#define OXYGEN_EEPROM_DIR_WRITE 0x80
|
||||
|
||||
#define OXYGEN_EEPROM_STATUS 0x81
|
||||
#define OXYGEN_EEPROM_VALID 0x40
|
||||
#define OXYGEN_EEPROM_BUSY 0x80
|
||||
|
||||
#define OXYGEN_EEPROM_DATA 0x82 /* 16 bits */
|
||||
|
||||
#define OXYGEN_2WIRE_CONTROL 0x90
|
||||
#define OXYGEN_2WIRE_DIR_MASK 0x01
|
||||
#define OXYGEN_2WIRE_DIR_WRITE 0x00
|
||||
#define OXYGEN_2WIRE_DIR_READ 0x01
|
||||
#define OXYGEN_2WIRE_ADDRESS_MASK 0xfe /* slave device address */
|
||||
#define OXYGEN_2WIRE_ADDRESS_SHIFT 1
|
||||
|
||||
#define OXYGEN_2WIRE_MAP 0x91 /* address, 8 bits */
|
||||
#define OXYGEN_2WIRE_DATA 0x92 /* data, 16 bits */
|
||||
|
||||
#define OXYGEN_2WIRE_BUS_STATUS 0x94
|
||||
#define OXYGEN_2WIRE_BUSY 0x0001
|
||||
#define OXYGEN_2WIRE_LENGTH_MASK 0x0002
|
||||
#define OXYGEN_2WIRE_LENGTH_8 0x0000
|
||||
#define OXYGEN_2WIRE_LENGTH_16 0x0002
|
||||
#define OXYGEN_2WIRE_MANUAL_READ 0x0004 /* 0 = auto read */
|
||||
#define OXYGEN_2WIRE_WRITE_MAP_ONLY 0x0008
|
||||
#define OXYGEN_2WIRE_SLAVE_AD_MASK 0x0030 /* AD0, AD1 */
|
||||
#define OXYGEN_2WIRE_INTERRUPT_MASK 0x0040 /* 0 = int. if not responding */
|
||||
#define OXYGEN_2WIRE_SLAVE_NO_RESPONSE 0x0080
|
||||
#define OXYGEN_2WIRE_SPEED_MASK 0x0100
|
||||
#define OXYGEN_2WIRE_SPEED_STANDARD 0x0000
|
||||
#define OXYGEN_2WIRE_SPEED_FAST 0x0100
|
||||
#define OXYGEN_2WIRE_CLOCK_SYNC 0x0200
|
||||
#define OXYGEN_2WIRE_BUS_RESET 0x0400
|
||||
|
||||
#define OXYGEN_SPI_CONTROL 0x98
|
||||
#define OXYGEN_SPI_BUSY 0x01 /* read */
|
||||
#define OXYGEN_SPI_TRIGGER 0x01 /* write */
|
||||
#define OXYGEN_SPI_DATA_LENGTH_MASK 0x02
|
||||
#define OXYGEN_SPI_DATA_LENGTH_2 0x00
|
||||
#define OXYGEN_SPI_DATA_LENGTH_3 0x02
|
||||
#define OXYGEN_SPI_CLOCK_MASK 0x0c
|
||||
#define OXYGEN_SPI_CLOCK_160 0x00 /* ns */
|
||||
#define OXYGEN_SPI_CLOCK_320 0x04
|
||||
#define OXYGEN_SPI_CLOCK_640 0x08
|
||||
#define OXYGEN_SPI_CLOCK_1280 0x0c
|
||||
#define OXYGEN_SPI_CODEC_MASK 0x70 /* 0..5 */
|
||||
#define OXYGEN_SPI_CODEC_SHIFT 4
|
||||
#define OXYGEN_SPI_CEN_MASK 0x80
|
||||
#define OXYGEN_SPI_CEN_LATCH_CLOCK_LO 0x00
|
||||
#define OXYGEN_SPI_CEN_LATCH_CLOCK_HI 0x80
|
||||
|
||||
#define OXYGEN_SPI_DATA1 0x99
|
||||
#define OXYGEN_SPI_DATA2 0x9a
|
||||
#define OXYGEN_SPI_DATA3 0x9b
|
||||
|
||||
#define OXYGEN_MPU401 0xa0
|
||||
|
||||
#define OXYGEN_MPU401_CONTROL 0xa2
|
||||
#define OXYGEN_MPU401_LOOPBACK 0x01 /* TXD to RXD */
|
||||
|
||||
#define OXYGEN_GPI_DATA 0xa4
|
||||
/* bits 0..5 = pin XGPI0..XGPI5 */
|
||||
|
||||
#define OXYGEN_GPI_INTERRUPT_MASK 0xa5
|
||||
/* bits 0..5, 1 = enable */
|
||||
|
||||
#define OXYGEN_GPIO_DATA 0xa6
|
||||
/* bits 0..9 */
|
||||
|
||||
#define OXYGEN_GPIO_CONTROL 0xa8
|
||||
/* bits 0..9, 0 = input, 1 = output */
|
||||
#define OXYGEN_GPIO1_XSLAVE_RDY 0x8000
|
||||
|
||||
#define OXYGEN_GPIO_INTERRUPT_MASK 0xaa
|
||||
/* bits 0..9, 1 = enable */
|
||||
|
||||
#define OXYGEN_DEVICE_SENSE 0xac
|
||||
#define OXYGEN_HEAD_PHONE_DETECT 0x01
|
||||
#define OXYGEN_HEAD_PHONE_MASK 0x06
|
||||
#define OXYGEN_HEAD_PHONE_PASSIVE_SPK 0x00
|
||||
#define OXYGEN_HEAD_PHONE_HP 0x02
|
||||
#define OXYGEN_HEAD_PHONE_ACTIVE_SPK 0x04
|
||||
|
||||
#define OXYGEN_MCU_2WIRE_DATA 0xb0
|
||||
|
||||
#define OXYGEN_MCU_2WIRE_MAP 0xb2
|
||||
|
||||
#define OXYGEN_MCU_2WIRE_STATUS 0xb3
|
||||
#define OXYGEN_MCU_2WIRE_BUSY 0x01
|
||||
#define OXYGEN_MCU_2WIRE_LENGTH_MASK 0x06
|
||||
#define OXYGEN_MCU_2WIRE_LENGTH_1 0x00
|
||||
#define OXYGEN_MCU_2WIRE_LENGTH_2 0x02
|
||||
#define OXYGEN_MCU_2WIRE_LENGTH_3 0x04
|
||||
#define OXYGEN_MCU_2WIRE_WRITE 0x08 /* r/wc */
|
||||
#define OXYGEN_MCU_2WIRE_READ 0x10 /* r/wc */
|
||||
#define OXYGEN_MCU_2WIRE_DRV_XACT_FAIL 0x20 /* r/wc */
|
||||
#define OXYGEN_MCU_2WIRE_RESET 0x40
|
||||
|
||||
#define OXYGEN_MCU_2WIRE_CONTROL 0xb4
|
||||
#define OXYGEN_MCU_2WIRE_DRV_ACK 0x01
|
||||
#define OXYGEN_MCU_2WIRE_DRV_XACT 0x02
|
||||
#define OXYGEN_MCU_2WIRE_INT_MASK 0x04
|
||||
#define OXYGEN_MCU_2WIRE_SYNC_MASK 0x08
|
||||
#define OXYGEN_MCU_2WIRE_SYNC_RDY_PIN 0x00
|
||||
#define OXYGEN_MCU_2WIRE_SYNC_DATA 0x08
|
||||
#define OXYGEN_MCU_2WIRE_ADDRESS_MASK 0x30
|
||||
#define OXYGEN_MCU_2WIRE_ADDRESS_10 0x00
|
||||
#define OXYGEN_MCU_2WIRE_ADDRESS_12 0x10
|
||||
#define OXYGEN_MCU_2WIRE_ADDRESS_14 0x20
|
||||
#define OXYGEN_MCU_2WIRE_ADDRESS_16 0x30
|
||||
#define OXYGEN_MCU_2WIRE_INT_POL 0x40
|
||||
#define OXYGEN_MCU_2WIRE_SYNC_ENABLE 0x80
|
||||
|
||||
#define OXYGEN_PLAY_ROUTING 0xc0
|
||||
#define OXYGEN_PLAY_MUTE01 0x0001
|
||||
#define OXYGEN_PLAY_MUTE23 0x0002
|
||||
#define OXYGEN_PLAY_MUTE45 0x0004
|
||||
#define OXYGEN_PLAY_MUTE67 0x0008
|
||||
#define OXYGEN_PLAY_MUTE_MASK 0x000f
|
||||
#define OXYGEN_PLAY_MULTICH_MASK 0x0010
|
||||
#define OXYGEN_PLAY_MULTICH_I2S_DAC 0x0000
|
||||
#define OXYGEN_PLAY_MULTICH_AC97 0x0010
|
||||
#define OXYGEN_PLAY_SPDIF_MASK 0x00e0
|
||||
#define OXYGEN_PLAY_SPDIF_SPDIF 0x0000
|
||||
#define OXYGEN_PLAY_SPDIF_MULTICH_01 0x0020
|
||||
#define OXYGEN_PLAY_SPDIF_MULTICH_23 0x0040
|
||||
#define OXYGEN_PLAY_SPDIF_MULTICH_45 0x0060
|
||||
#define OXYGEN_PLAY_SPDIF_MULTICH_67 0x0080
|
||||
#define OXYGEN_PLAY_SPDIF_REC_A 0x00a0
|
||||
#define OXYGEN_PLAY_SPDIF_REC_B 0x00c0
|
||||
#define OXYGEN_PLAY_SPDIF_I2S_ADC_3 0x00e0
|
||||
#define OXYGEN_PLAY_DAC0_SOURCE_MASK 0x0300
|
||||
#define OXYGEN_PLAY_DAC0_SOURCE_SHIFT 8
|
||||
#define OXYGEN_PLAY_DAC1_SOURCE_MASK 0x0c00
|
||||
#define OXYGEN_PLAY_DAC1_SOURCE_SHIFT 10
|
||||
#define OXYGEN_PLAY_DAC2_SOURCE_MASK 0x3000
|
||||
#define OXYGEN_PLAY_DAC2_SOURCE_SHIFT 12
|
||||
#define OXYGEN_PLAY_DAC3_SOURCE_MASK 0xc000
|
||||
#define OXYGEN_PLAY_DAC3_SOURCE_SHIFT 14
|
||||
|
||||
#define OXYGEN_REC_ROUTING 0xc2
|
||||
#define OXYGEN_MUTE_I2S_ADC_1 0x01
|
||||
#define OXYGEN_MUTE_I2S_ADC_2 0x02
|
||||
#define OXYGEN_MUTE_I2S_ADC_3 0x04
|
||||
#define OXYGEN_REC_A_ROUTE_MASK 0x08
|
||||
#define OXYGEN_REC_A_ROUTE_I2S_ADC_1 0x00
|
||||
#define OXYGEN_REC_A_ROUTE_AC97_0 0x08
|
||||
#define OXYGEN_REC_B_ROUTE_MASK 0x10
|
||||
#define OXYGEN_REC_B_ROUTE_I2S_ADC_2 0x00
|
||||
#define OXYGEN_REC_B_ROUTE_AC97_1 0x10
|
||||
#define OXYGEN_REC_C_ROUTE_MASK 0x20
|
||||
#define OXYGEN_REC_C_ROUTE_SPDIF 0x00
|
||||
#define OXYGEN_REC_C_ROUTE_I2S_ADC_3 0x20
|
||||
|
||||
#define OXYGEN_ADC_MONITOR 0xc3
|
||||
#define OXYGEN_ADC_MONITOR_A 0x01
|
||||
#define OXYGEN_ADC_MONITOR_A_HALF_VOL 0x02
|
||||
#define OXYGEN_ADC_MONITOR_B 0x04
|
||||
#define OXYGEN_ADC_MONITOR_B_HALF_VOL 0x08
|
||||
#define OXYGEN_ADC_MONITOR_C 0x10
|
||||
#define OXYGEN_ADC_MONITOR_C_HALF_VOL 0x20
|
||||
|
||||
#define OXYGEN_A_MONITOR_ROUTING 0xc4
|
||||
#define OXYGEN_A_MONITOR_ROUTE_0_MASK 0x03
|
||||
#define OXYGEN_A_MONITOR_ROUTE_0_SHIFT 0
|
||||
#define OXYGEN_A_MONITOR_ROUTE_1_MASK 0x0c
|
||||
#define OXYGEN_A_MONITOR_ROUTE_1_SHIFT 2
|
||||
#define OXYGEN_A_MONITOR_ROUTE_2_MASK 0x30
|
||||
#define OXYGEN_A_MONITOR_ROUTE_2_SHIFT 4
|
||||
#define OXYGEN_A_MONITOR_ROUTE_3_MASK 0xc0
|
||||
#define OXYGEN_A_MONITOR_ROUTE_3_SHIFT 6
|
||||
|
||||
#define OXYGEN_AC97_CONTROL 0xd0
|
||||
#define OXYGEN_AC97_COLD_RESET 0x0001
|
||||
#define OXYGEN_AC97_SUSPENDED 0x0002 /* read */
|
||||
#define OXYGEN_AC97_RESUME 0x0002 /* write */
|
||||
#define OXYGEN_AC97_CLOCK_DISABLE 0x0004
|
||||
#define OXYGEN_AC97_NO_CODEC_0 0x0008
|
||||
#define OXYGEN_AC97_CODEC_0 0x0010
|
||||
#define OXYGEN_AC97_CODEC_1 0x0020
|
||||
|
||||
#define OXYGEN_AC97_INTERRUPT_MASK 0xd2
|
||||
#define OXYGEN_AC97_INT_READ_DONE 0x01
|
||||
#define OXYGEN_AC97_INT_WRITE_DONE 0x02
|
||||
#define OXYGEN_AC97_INT_CODEC_0 0x10
|
||||
#define OXYGEN_AC97_INT_CODEC_1 0x20
|
||||
|
||||
#define OXYGEN_AC97_INTERRUPT_STATUS 0xd3
|
||||
/* OXYGEN_AC97_INT_* */
|
||||
|
||||
#define OXYGEN_AC97_OUT_CONFIG 0xd4
|
||||
#define OXYGEN_AC97_CODEC1_SLOT3 0x00000001
|
||||
#define OXYGEN_AC97_CODEC1_SLOT3_VSR 0x00000002
|
||||
#define OXYGEN_AC97_CODEC1_SLOT4 0x00000010
|
||||
#define OXYGEN_AC97_CODEC1_SLOT4_VSR 0x00000020
|
||||
#define OXYGEN_AC97_CODEC0_FRONTL 0x00000100
|
||||
#define OXYGEN_AC97_CODEC0_FRONTR 0x00000200
|
||||
#define OXYGEN_AC97_CODEC0_SIDEL 0x00000400
|
||||
#define OXYGEN_AC97_CODEC0_SIDER 0x00000800
|
||||
#define OXYGEN_AC97_CODEC0_CENTER 0x00001000
|
||||
#define OXYGEN_AC97_CODEC0_BASE 0x00002000
|
||||
#define OXYGEN_AC97_CODEC0_REARL 0x00004000
|
||||
#define OXYGEN_AC97_CODEC0_REARR 0x00008000
|
||||
|
||||
#define OXYGEN_AC97_IN_CONFIG 0xd8
|
||||
#define OXYGEN_AC97_CODEC1_LINEL 0x00000001
|
||||
#define OXYGEN_AC97_CODEC1_LINEL_VSR 0x00000002
|
||||
#define OXYGEN_AC97_CODEC1_LINEL_16 0x00000000
|
||||
#define OXYGEN_AC97_CODEC1_LINEL_18 0x00000004
|
||||
#define OXYGEN_AC97_CODEC1_LINEL_20 0x00000008
|
||||
#define OXYGEN_AC97_CODEC1_LINER 0x00000010
|
||||
#define OXYGEN_AC97_CODEC1_LINER_VSR 0x00000020
|
||||
#define OXYGEN_AC97_CODEC1_LINER_16 0x00000000
|
||||
#define OXYGEN_AC97_CODEC1_LINER_18 0x00000040
|
||||
#define OXYGEN_AC97_CODEC1_LINER_20 0x00000080
|
||||
#define OXYGEN_AC97_CODEC0_LINEL 0x00000100
|
||||
#define OXYGEN_AC97_CODEC0_LINER 0x00000200
|
||||
|
||||
#define OXYGEN_AC97_REGS 0xdc
|
||||
#define OXYGEN_AC97_REG_DATA_MASK 0x0000ffff
|
||||
#define OXYGEN_AC97_REG_ADDR_MASK 0x007f0000
|
||||
#define OXYGEN_AC97_REG_ADDR_SHIFT 16
|
||||
#define OXYGEN_AC97_REG_DIR_MASK 0x00800000
|
||||
#define OXYGEN_AC97_REG_DIR_WRITE 0x00000000
|
||||
#define OXYGEN_AC97_REG_DIR_READ 0x00800000
|
||||
#define OXYGEN_AC97_REG_CODEC_MASK 0x01000000
|
||||
#define OXYGEN_AC97_REG_CODEC_SHIFT 24
|
||||
|
||||
#define OXYGEN_TEST 0xe0
|
||||
#define OXYGEN_TEST_RAM_SUCCEEDED 0x01
|
||||
#define OXYGEN_TEST_PLAYBACK_RAM 0x02
|
||||
#define OXYGEN_TEST_RECORD_RAM 0x04
|
||||
#define OXYGEN_TEST_PLL 0x08
|
||||
#define OXYGEN_TEST_2WIRE_LOOPBACK 0x10
|
||||
|
||||
#define OXYGEN_DMA_FLUSH 0xe1
|
||||
/* OXYGEN_CHANNEL_* */
|
||||
|
||||
#define OXYGEN_CODEC_VERSION 0xe4
|
||||
#define OXYGEN_CODEC_ID_MASK 0x07
|
||||
|
||||
#define OXYGEN_REVISION 0xe6
|
||||
#define OXYGEN_PACKAGE_ID_MASK 0x0007
|
||||
#define OXYGEN_PACKAGE_ID_8786 0x0004
|
||||
#define OXYGEN_PACKAGE_ID_8787 0x0006
|
||||
#define OXYGEN_PACKAGE_ID_8788 0x0007
|
||||
#define OXYGEN_REVISION_MASK 0xfff8
|
||||
#define OXYGEN_REVISION_2 0x0008
|
||||
|
||||
#define OXYGEN_OFFSIN_48K 0xe8
|
||||
#define OXYGEN_OFFSBASE_48K 0xe9
|
||||
#define OXYGEN_OFFSBASE_MASK 0x0fff
|
||||
#define OXYGEN_OFFSIN_44K 0xec
|
||||
#define OXYGEN_OFFSBASE_44K 0xed
|
||||
|
||||
#endif
|
58
sound/pci/oxygen/pcm1796.h
Normal file
58
sound/pci/oxygen/pcm1796.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
#ifndef PCM1796_H_INCLUDED
|
||||
#define PCM1796_H_INCLUDED
|
||||
|
||||
/* register 16 */
|
||||
#define PCM1796_ATL_MASK 0xff
|
||||
/* register 17 */
|
||||
#define PCM1796_ATR_MASK 0xff
|
||||
/* register 18 */
|
||||
#define PCM1796_MUTE 0x01
|
||||
#define PCM1796_DME 0x02
|
||||
#define PCM1796_DMF_MASK 0x0c
|
||||
#define PCM1796_DMF_DISABLED 0x00
|
||||
#define PCM1796_DMF_48 0x04
|
||||
#define PCM1796_DMF_441 0x08
|
||||
#define PCM1796_DMF_32 0x0c
|
||||
#define PCM1796_FMT_MASK 0x70
|
||||
#define PCM1796_FMT_16_RJUST 0x00
|
||||
#define PCM1796_FMT_20_RJUST 0x10
|
||||
#define PCM1796_FMT_24_RJUST 0x20
|
||||
#define PCM1796_FMT_24_LJUST 0x30
|
||||
#define PCM1796_FMT_16_I2S 0x40
|
||||
#define PCM1796_FMT_24_I2S 0x50
|
||||
#define PCM1796_ATLD 0x80
|
||||
/* register 19 */
|
||||
#define PCM1796_INZD 0x01
|
||||
#define PCM1796_FLT_MASK 0x02
|
||||
#define PCM1796_FLT_SHARP 0x00
|
||||
#define PCM1796_FLT_SLOW 0x02
|
||||
#define PCM1796_DFMS 0x04
|
||||
#define PCM1796_OPE 0x10
|
||||
#define PCM1796_ATS_MASK 0x60
|
||||
#define PCM1796_ATS_1 0x00
|
||||
#define PCM1796_ATS_2 0x20
|
||||
#define PCM1796_ATS_4 0x40
|
||||
#define PCM1796_ATS_8 0x60
|
||||
#define PCM1796_REV 0x80
|
||||
/* register 20 */
|
||||
#define PCM1796_OS_MASK 0x03
|
||||
#define PCM1796_OS_64 0x00
|
||||
#define PCM1796_OS_32 0x01
|
||||
#define PCM1796_OS_128 0x02
|
||||
#define PCM1796_CHSL_MASK 0x04
|
||||
#define PCM1796_CHSL_LEFT 0x00
|
||||
#define PCM1796_CHSL_RIGHT 0x04
|
||||
#define PCM1796_MONO 0x08
|
||||
#define PCM1796_DFTH 0x10
|
||||
#define PCM1796_DSD 0x20
|
||||
#define PCM1796_SRST 0x40
|
||||
/* register 21 */
|
||||
#define PCM1796_PCMZ 0x01
|
||||
#define PCM1796_DZ_MASK 0x06
|
||||
/* register 22 */
|
||||
#define PCM1796_ZFGL 0x01
|
||||
#define PCM1796_ZFGR 0x02
|
||||
/* register 23 */
|
||||
#define PCM1796_ID_MASK 0x1f
|
||||
|
||||
#endif
|
107
sound/pci/oxygen/virtuoso.c
Normal file
107
sound/pci/oxygen/virtuoso.c
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* C-Media CMI8788 driver for Asus Xonar cards
|
||||
*
|
||||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||
*
|
||||
*
|
||||
* This driver is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2.
|
||||
*
|
||||
* This driver 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 driver; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include "xonar.h"
|
||||
|
||||
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
|
||||
MODULE_DESCRIPTION("Asus Virtuoso driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_SUPPORTED_DEVICE("{{Asus,AV66},{Asus,AV100},{Asus,AV200}}");
|
||||
|
||||
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
|
||||
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
|
||||
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
|
||||
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "card index");
|
||||
module_param_array(id, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(id, "ID string");
|
||||
module_param_array(enable, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(enable, "enable card");
|
||||
|
||||
static const struct pci_device_id xonar_ids[] = {
|
||||
{ OXYGEN_PCI_SUBID(0x1043, 0x8269) },
|
||||
{ OXYGEN_PCI_SUBID(0x1043, 0x8275) },
|
||||
{ OXYGEN_PCI_SUBID(0x1043, 0x82b7) },
|
||||
{ OXYGEN_PCI_SUBID(0x1043, 0x8314) },
|
||||
{ OXYGEN_PCI_SUBID(0x1043, 0x8327) },
|
||||
{ OXYGEN_PCI_SUBID(0x1043, 0x834f) },
|
||||
{ OXYGEN_PCI_SUBID(0x1043, 0x835c) },
|
||||
{ OXYGEN_PCI_SUBID(0x1043, 0x835d) },
|
||||
{ OXYGEN_PCI_SUBID(0x1043, 0x835e) },
|
||||
{ OXYGEN_PCI_SUBID(0x1043, 0x838e) },
|
||||
{ OXYGEN_PCI_SUBID(0x1043, 0x8428) },
|
||||
{ OXYGEN_PCI_SUBID(0x1043, 0x8522) },
|
||||
{ OXYGEN_PCI_SUBID(0x1043, 0x85f4) },
|
||||
{ OXYGEN_PCI_SUBID_BROKEN_EEPROM },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, xonar_ids);
|
||||
|
||||
static int get_xonar_model(struct oxygen *chip,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
if (get_xonar_pcm179x_model(chip, id) >= 0)
|
||||
return 0;
|
||||
if (get_xonar_cs43xx_model(chip, id) >= 0)
|
||||
return 0;
|
||||
if (get_xonar_wm87x6_model(chip, id) >= 0)
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int xonar_probe(struct pci_dev *pci,
|
||||
const struct pci_device_id *pci_id)
|
||||
{
|
||||
static int dev;
|
||||
int err;
|
||||
|
||||
if (dev >= SNDRV_CARDS)
|
||||
return -ENODEV;
|
||||
if (!enable[dev]) {
|
||||
++dev;
|
||||
return -ENOENT;
|
||||
}
|
||||
err = oxygen_pci_probe(pci, index[dev], id[dev], THIS_MODULE,
|
||||
xonar_ids, get_xonar_model);
|
||||
if (err >= 0)
|
||||
++dev;
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct pci_driver xonar_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.id_table = xonar_ids,
|
||||
.probe = xonar_probe,
|
||||
.remove = oxygen_pci_remove,
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.driver = {
|
||||
.pm = &oxygen_pci_pm,
|
||||
},
|
||||
#endif
|
||||
.shutdown = oxygen_pci_shutdown,
|
||||
};
|
||||
|
||||
module_pci_driver(xonar_driver);
|
73
sound/pci/oxygen/wm8766.h
Normal file
73
sound/pci/oxygen/wm8766.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
#ifndef WM8766_H_INCLUDED
|
||||
#define WM8766_H_INCLUDED
|
||||
|
||||
#define WM8766_LDA1 0x00
|
||||
#define WM8766_RDA1 0x01
|
||||
#define WM8766_DAC_CTRL 0x02
|
||||
#define WM8766_INT_CTRL 0x03
|
||||
#define WM8766_LDA2 0x04
|
||||
#define WM8766_RDA2 0x05
|
||||
#define WM8766_LDA3 0x06
|
||||
#define WM8766_RDA3 0x07
|
||||
#define WM8766_MASTDA 0x08
|
||||
#define WM8766_DAC_CTRL2 0x09
|
||||
#define WM8766_DAC_CTRL3 0x0a
|
||||
#define WM8766_MUTE1 0x0c
|
||||
#define WM8766_MUTE2 0x0f
|
||||
#define WM8766_RESET 0x1f
|
||||
|
||||
/* LDAx/RDAx/MASTDA */
|
||||
#define WM8766_ATT_MASK 0x0ff
|
||||
#define WM8766_UPDATE 0x100
|
||||
/* DAC_CTRL */
|
||||
#define WM8766_MUTEALL 0x001
|
||||
#define WM8766_DEEMPALL 0x002
|
||||
#define WM8766_PWDN 0x004
|
||||
#define WM8766_ATC 0x008
|
||||
#define WM8766_IZD 0x010
|
||||
#define WM8766_PL_LEFT_MASK 0x060
|
||||
#define WM8766_PL_LEFT_MUTE 0x000
|
||||
#define WM8766_PL_LEFT_LEFT 0x020
|
||||
#define WM8766_PL_LEFT_RIGHT 0x040
|
||||
#define WM8766_PL_LEFT_LRMIX 0x060
|
||||
#define WM8766_PL_RIGHT_MASK 0x180
|
||||
#define WM8766_PL_RIGHT_MUTE 0x000
|
||||
#define WM8766_PL_RIGHT_LEFT 0x080
|
||||
#define WM8766_PL_RIGHT_RIGHT 0x100
|
||||
#define WM8766_PL_RIGHT_LRMIX 0x180
|
||||
/* INT_CTRL */
|
||||
#define WM8766_FMT_MASK 0x003
|
||||
#define WM8766_FMT_RJUST 0x000
|
||||
#define WM8766_FMT_LJUST 0x001
|
||||
#define WM8766_FMT_I2S 0x002
|
||||
#define WM8766_FMT_DSP 0x003
|
||||
#define WM8766_LRP 0x004
|
||||
#define WM8766_BCP 0x008
|
||||
#define WM8766_IWL_MASK 0x030
|
||||
#define WM8766_IWL_16 0x000
|
||||
#define WM8766_IWL_20 0x010
|
||||
#define WM8766_IWL_24 0x020
|
||||
#define WM8766_IWL_32 0x030
|
||||
#define WM8766_PHASE_MASK 0x1c0
|
||||
/* DAC_CTRL2 */
|
||||
#define WM8766_ZCD 0x001
|
||||
#define WM8766_DZFM_MASK 0x006
|
||||
#define WM8766_DMUTE_MASK 0x038
|
||||
#define WM8766_DEEMP_MASK 0x1c0
|
||||
/* DAC_CTRL3 */
|
||||
#define WM8766_DACPD_MASK 0x00e
|
||||
#define WM8766_PWRDNALL 0x010
|
||||
#define WM8766_MS 0x020
|
||||
#define WM8766_RATE_MASK 0x1c0
|
||||
#define WM8766_RATE_128 0x000
|
||||
#define WM8766_RATE_192 0x040
|
||||
#define WM8766_RATE_256 0x080
|
||||
#define WM8766_RATE_384 0x0c0
|
||||
#define WM8766_RATE_512 0x100
|
||||
#define WM8766_RATE_768 0x140
|
||||
/* MUTE1 */
|
||||
#define WM8766_MPD1 0x040
|
||||
/* MUTE2 */
|
||||
#define WM8766_MPD2 0x020
|
||||
|
||||
#endif
|
177
sound/pci/oxygen/wm8776.h
Normal file
177
sound/pci/oxygen/wm8776.h
Normal file
|
@ -0,0 +1,177 @@
|
|||
#ifndef WM8776_H_INCLUDED
|
||||
#define WM8776_H_INCLUDED
|
||||
|
||||
/*
|
||||
* the following register names are from:
|
||||
* wm8776.h -- WM8776 ASoC driver
|
||||
*
|
||||
* Copyright 2009 Wolfson Microelectronics plc
|
||||
*
|
||||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#define WM8776_HPLVOL 0x00
|
||||
#define WM8776_HPRVOL 0x01
|
||||
#define WM8776_HPMASTER 0x02
|
||||
#define WM8776_DACLVOL 0x03
|
||||
#define WM8776_DACRVOL 0x04
|
||||
#define WM8776_DACMASTER 0x05
|
||||
#define WM8776_PHASESWAP 0x06
|
||||
#define WM8776_DACCTRL1 0x07
|
||||
#define WM8776_DACMUTE 0x08
|
||||
#define WM8776_DACCTRL2 0x09
|
||||
#define WM8776_DACIFCTRL 0x0a
|
||||
#define WM8776_ADCIFCTRL 0x0b
|
||||
#define WM8776_MSTRCTRL 0x0c
|
||||
#define WM8776_PWRDOWN 0x0d
|
||||
#define WM8776_ADCLVOL 0x0e
|
||||
#define WM8776_ADCRVOL 0x0f
|
||||
#define WM8776_ALCCTRL1 0x10
|
||||
#define WM8776_ALCCTRL2 0x11
|
||||
#define WM8776_ALCCTRL3 0x12
|
||||
#define WM8776_NOISEGATE 0x13
|
||||
#define WM8776_LIMITER 0x14
|
||||
#define WM8776_ADCMUX 0x15
|
||||
#define WM8776_OUTMUX 0x16
|
||||
#define WM8776_RESET 0x17
|
||||
|
||||
|
||||
/* HPLVOL/HPRVOL/HPMASTER */
|
||||
#define WM8776_HPATT_MASK 0x07f
|
||||
#define WM8776_HPZCEN 0x080
|
||||
#define WM8776_UPDATE 0x100
|
||||
|
||||
/* DACLVOL/DACRVOL/DACMASTER */
|
||||
#define WM8776_DATT_MASK 0x0ff
|
||||
/*#define WM8776_UPDATE 0x100*/
|
||||
|
||||
/* PHASESWAP */
|
||||
#define WM8776_PH_MASK 0x003
|
||||
|
||||
/* DACCTRL1 */
|
||||
#define WM8776_DZCEN 0x001
|
||||
#define WM8776_ATC 0x002
|
||||
#define WM8776_IZD 0x004
|
||||
#define WM8776_TOD 0x008
|
||||
#define WM8776_PL_LEFT_MASK 0x030
|
||||
#define WM8776_PL_LEFT_MUTE 0x000
|
||||
#define WM8776_PL_LEFT_LEFT 0x010
|
||||
#define WM8776_PL_LEFT_RIGHT 0x020
|
||||
#define WM8776_PL_LEFT_LRMIX 0x030
|
||||
#define WM8776_PL_RIGHT_MASK 0x0c0
|
||||
#define WM8776_PL_RIGHT_MUTE 0x000
|
||||
#define WM8776_PL_RIGHT_LEFT 0x040
|
||||
#define WM8776_PL_RIGHT_RIGHT 0x080
|
||||
#define WM8776_PL_RIGHT_LRMIX 0x0c0
|
||||
|
||||
/* DACMUTE */
|
||||
#define WM8776_DMUTE 0x001
|
||||
|
||||
/* DACCTRL2 */
|
||||
#define WM8776_DEEMPH 0x001
|
||||
#define WM8776_DZFM_MASK 0x006
|
||||
#define WM8776_DZFM_NONE 0x000
|
||||
#define WM8776_DZFM_LR 0x002
|
||||
#define WM8776_DZFM_BOTH 0x004
|
||||
#define WM8776_DZFM_EITHER 0x006
|
||||
|
||||
/* DACIFCTRL */
|
||||
#define WM8776_DACFMT_MASK 0x003
|
||||
#define WM8776_DACFMT_RJUST 0x000
|
||||
#define WM8776_DACFMT_LJUST 0x001
|
||||
#define WM8776_DACFMT_I2S 0x002
|
||||
#define WM8776_DACFMT_DSP 0x003
|
||||
#define WM8776_DACLRP 0x004
|
||||
#define WM8776_DACBCP 0x008
|
||||
#define WM8776_DACWL_MASK 0x030
|
||||
#define WM8776_DACWL_16 0x000
|
||||
#define WM8776_DACWL_20 0x010
|
||||
#define WM8776_DACWL_24 0x020
|
||||
#define WM8776_DACWL_32 0x030
|
||||
|
||||
/* ADCIFCTRL */
|
||||
#define WM8776_ADCFMT_MASK 0x003
|
||||
#define WM8776_ADCFMT_RJUST 0x000
|
||||
#define WM8776_ADCFMT_LJUST 0x001
|
||||
#define WM8776_ADCFMT_I2S 0x002
|
||||
#define WM8776_ADCFMT_DSP 0x003
|
||||
#define WM8776_ADCLRP 0x004
|
||||
#define WM8776_ADCBCP 0x008
|
||||
#define WM8776_ADCWL_MASK 0x030
|
||||
#define WM8776_ADCWL_16 0x000
|
||||
#define WM8776_ADCWL_20 0x010
|
||||
#define WM8776_ADCWL_24 0x020
|
||||
#define WM8776_ADCWL_32 0x030
|
||||
#define WM8776_ADCMCLK 0x040
|
||||
#define WM8776_ADCHPD 0x100
|
||||
|
||||
/* MSTRCTRL */
|
||||
#define WM8776_ADCRATE_MASK 0x007
|
||||
#define WM8776_ADCRATE_256 0x002
|
||||
#define WM8776_ADCRATE_384 0x003
|
||||
#define WM8776_ADCRATE_512 0x004
|
||||
#define WM8776_ADCRATE_768 0x005
|
||||
#define WM8776_ADCOSR 0x008
|
||||
#define WM8776_DACRATE_MASK 0x070
|
||||
#define WM8776_DACRATE_128 0x000
|
||||
#define WM8776_DACRATE_192 0x010
|
||||
#define WM8776_DACRATE_256 0x020
|
||||
#define WM8776_DACRATE_384 0x030
|
||||
#define WM8776_DACRATE_512 0x040
|
||||
#define WM8776_DACRATE_768 0x050
|
||||
#define WM8776_DACMS 0x080
|
||||
#define WM8776_ADCMS 0x100
|
||||
|
||||
/* PWRDOWN */
|
||||
#define WM8776_PDWN 0x001
|
||||
#define WM8776_ADCPD 0x002
|
||||
#define WM8776_DACPD 0x004
|
||||
#define WM8776_HPPD 0x008
|
||||
#define WM8776_AINPD 0x040
|
||||
|
||||
/* ADCLVOL/ADCRVOL */
|
||||
#define WM8776_AGMASK 0x0ff
|
||||
#define WM8776_ZCA 0x100
|
||||
|
||||
/* ALCCTRL1 */
|
||||
#define WM8776_LCT_MASK 0x00f
|
||||
#define WM8776_MAXGAIN_MASK 0x070
|
||||
#define WM8776_LCSEL_MASK 0x180
|
||||
#define WM8776_LCSEL_LIMITER 0x000
|
||||
#define WM8776_LCSEL_ALC_RIGHT 0x080
|
||||
#define WM8776_LCSEL_ALC_LEFT 0x100
|
||||
#define WM8776_LCSEL_ALC_STEREO 0x180
|
||||
|
||||
/* ALCCTRL2 */
|
||||
#define WM8776_HLD_MASK 0x00f
|
||||
#define WM8776_ALCZC 0x080
|
||||
#define WM8776_LCEN 0x100
|
||||
|
||||
/* ALCCTRL3 */
|
||||
#define WM8776_ATK_MASK 0x00f
|
||||
#define WM8776_DCY_MASK 0x0f0
|
||||
|
||||
/* NOISEGATE */
|
||||
#define WM8776_NGAT 0x001
|
||||
#define WM8776_NGTH_MASK 0x01c
|
||||
|
||||
/* LIMITER */
|
||||
#define WM8776_MAXATTEN_MASK 0x00f
|
||||
#define WM8776_TRANWIN_MASK 0x070
|
||||
|
||||
/* ADCMUX */
|
||||
#define WM8776_AMX_MASK 0x01f
|
||||
#define WM8776_MUTERA 0x040
|
||||
#define WM8776_MUTELA 0x080
|
||||
#define WM8776_LRBOTH 0x100
|
||||
|
||||
/* OUTMUX */
|
||||
#define WM8776_MX_DAC 0x001
|
||||
#define WM8776_MX_AUX 0x002
|
||||
#define WM8776_MX_BYPASS 0x004
|
||||
|
||||
#endif
|
45
sound/pci/oxygen/wm8785.h
Normal file
45
sound/pci/oxygen/wm8785.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
#ifndef WM8785_H_INCLUDED
|
||||
#define WM8785_H_INCLUDED
|
||||
|
||||
#define WM8785_R0 0
|
||||
#define WM8785_R1 1
|
||||
#define WM8785_R2 2
|
||||
#define WM8785_R7 7
|
||||
|
||||
/* R0 */
|
||||
#define WM8785_MCR_MASK 0x007
|
||||
#define WM8785_MCR_SLAVE 0x000
|
||||
#define WM8785_MCR_MASTER_128 0x001
|
||||
#define WM8785_MCR_MASTER_192 0x002
|
||||
#define WM8785_MCR_MASTER_256 0x003
|
||||
#define WM8785_MCR_MASTER_384 0x004
|
||||
#define WM8785_MCR_MASTER_512 0x005
|
||||
#define WM8785_MCR_MASTER_768 0x006
|
||||
#define WM8785_OSR_MASK 0x018
|
||||
#define WM8785_OSR_SINGLE 0x000
|
||||
#define WM8785_OSR_DOUBLE 0x008
|
||||
#define WM8785_OSR_QUAD 0x010
|
||||
#define WM8785_FORMAT_MASK 0x060
|
||||
#define WM8785_FORMAT_RJUST 0x000
|
||||
#define WM8785_FORMAT_LJUST 0x020
|
||||
#define WM8785_FORMAT_I2S 0x040
|
||||
#define WM8785_FORMAT_DSP 0x060
|
||||
/* R1 */
|
||||
#define WM8785_WL_MASK 0x003
|
||||
#define WM8785_WL_16 0x000
|
||||
#define WM8785_WL_20 0x001
|
||||
#define WM8785_WL_24 0x002
|
||||
#define WM8785_WL_32 0x003
|
||||
#define WM8785_LRP 0x004
|
||||
#define WM8785_BCLKINV 0x008
|
||||
#define WM8785_LRSWAP 0x010
|
||||
#define WM8785_DEVNO_MASK 0x0e0
|
||||
/* R2 */
|
||||
#define WM8785_HPFR 0x001
|
||||
#define WM8785_HPFL 0x002
|
||||
#define WM8785_SDODIS 0x004
|
||||
#define WM8785_PWRDNR 0x008
|
||||
#define WM8785_PWRDNL 0x010
|
||||
#define WM8785_TDM_MASK 0x1c0
|
||||
|
||||
#endif
|
54
sound/pci/oxygen/xonar.h
Normal file
54
sound/pci/oxygen/xonar.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
#ifndef XONAR_H_INCLUDED
|
||||
#define XONAR_H_INCLUDED
|
||||
|
||||
#include "oxygen.h"
|
||||
|
||||
struct xonar_generic {
|
||||
unsigned int anti_pop_delay;
|
||||
u16 output_enable_bit;
|
||||
u8 ext_power_reg;
|
||||
u8 ext_power_int_reg;
|
||||
u8 ext_power_bit;
|
||||
u8 has_power;
|
||||
};
|
||||
|
||||
struct xonar_hdmi {
|
||||
u8 params[5];
|
||||
};
|
||||
|
||||
/* generic helper functions */
|
||||
|
||||
void xonar_enable_output(struct oxygen *chip);
|
||||
void xonar_disable_output(struct oxygen *chip);
|
||||
void xonar_init_ext_power(struct oxygen *chip);
|
||||
void xonar_init_cs53x1(struct oxygen *chip);
|
||||
void xonar_set_cs53x1_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params);
|
||||
|
||||
#define XONAR_GPIO_BIT_INVERT (1 << 16)
|
||||
int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value);
|
||||
int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value);
|
||||
|
||||
/* model-specific card drivers */
|
||||
|
||||
int get_xonar_pcm179x_model(struct oxygen *chip,
|
||||
const struct pci_device_id *id);
|
||||
int get_xonar_cs43xx_model(struct oxygen *chip,
|
||||
const struct pci_device_id *id);
|
||||
int get_xonar_wm87x6_model(struct oxygen *chip,
|
||||
const struct pci_device_id *id);
|
||||
|
||||
/* HDMI helper functions */
|
||||
|
||||
void xonar_hdmi_init(struct oxygen *chip, struct xonar_hdmi *data);
|
||||
void xonar_hdmi_cleanup(struct oxygen *chip);
|
||||
void xonar_hdmi_resume(struct oxygen *chip, struct xonar_hdmi *hdmi);
|
||||
void xonar_hdmi_pcm_hardware_filter(unsigned int channel,
|
||||
struct snd_pcm_hardware *hardware);
|
||||
void xonar_set_hdmi_params(struct oxygen *chip, struct xonar_hdmi *hdmi,
|
||||
struct snd_pcm_hw_params *params);
|
||||
void xonar_hdmi_uart_input(struct oxygen *chip);
|
||||
|
||||
#endif
|
452
sound/pci/oxygen/xonar_cs43xx.c
Normal file
452
sound/pci/oxygen/xonar_cs43xx.c
Normal file
|
@ -0,0 +1,452 @@
|
|||
/*
|
||||
* card driver for models with CS4398/CS4362A DACs (Xonar D1/DX)
|
||||
*
|
||||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||
*
|
||||
*
|
||||
* This driver is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2.
|
||||
*
|
||||
* This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Xonar D1/DX
|
||||
* -----------
|
||||
*
|
||||
* CMI8788:
|
||||
*
|
||||
* I²C <-> CS4398 (addr 1001111) (front)
|
||||
* <-> CS4362A (addr 0011000) (surround, center/LFE, back)
|
||||
*
|
||||
* GPI 0 <- external power present (DX only)
|
||||
*
|
||||
* GPIO 0 -> enable output to speakers
|
||||
* GPIO 1 -> route output to front panel
|
||||
* GPIO 2 -> M0 of CS5361
|
||||
* GPIO 3 -> M1 of CS5361
|
||||
* GPIO 6 -> ?
|
||||
* GPIO 7 -> ?
|
||||
* GPIO 8 -> route input jack to line-in (0) or mic-in (1)
|
||||
*
|
||||
* CM9780:
|
||||
*
|
||||
* LINE_OUT -> input of ADC
|
||||
*
|
||||
* AUX_IN <- aux
|
||||
* MIC_IN <- mic
|
||||
* FMIC_IN <- front mic
|
||||
*
|
||||
* GPO 0 -> route line-in (0) or AC97 output (1) to CS5361 input
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <sound/ac97_codec.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "xonar.h"
|
||||
#include "cm9780.h"
|
||||
#include "cs4398.h"
|
||||
#include "cs4362a.h"
|
||||
|
||||
#define GPI_EXT_POWER 0x01
|
||||
#define GPIO_D1_OUTPUT_ENABLE 0x0001
|
||||
#define GPIO_D1_FRONT_PANEL 0x0002
|
||||
#define GPIO_D1_MAGIC 0x00c0
|
||||
#define GPIO_D1_INPUT_ROUTE 0x0100
|
||||
|
||||
#define I2C_DEVICE_CS4398 0x9e /* 10011, AD1=1, AD0=1, /W=0 */
|
||||
#define I2C_DEVICE_CS4362A 0x30 /* 001100, AD0=0, /W=0 */
|
||||
|
||||
struct xonar_cs43xx {
|
||||
struct xonar_generic generic;
|
||||
u8 cs4398_regs[8];
|
||||
u8 cs4362a_regs[15];
|
||||
};
|
||||
|
||||
static void cs4398_write(struct oxygen *chip, u8 reg, u8 value)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
|
||||
oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value);
|
||||
if (reg < ARRAY_SIZE(data->cs4398_regs))
|
||||
data->cs4398_regs[reg] = value;
|
||||
}
|
||||
|
||||
static void cs4398_write_cached(struct oxygen *chip, u8 reg, u8 value)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
|
||||
if (value != data->cs4398_regs[reg])
|
||||
cs4398_write(chip, reg, value);
|
||||
}
|
||||
|
||||
static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
|
||||
oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value);
|
||||
if (reg < ARRAY_SIZE(data->cs4362a_regs))
|
||||
data->cs4362a_regs[reg] = value;
|
||||
}
|
||||
|
||||
static void cs4362a_write_cached(struct oxygen *chip, u8 reg, u8 value)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
|
||||
if (value != data->cs4362a_regs[reg])
|
||||
cs4362a_write(chip, reg, value);
|
||||
}
|
||||
|
||||
static void cs43xx_registers_init(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
unsigned int i;
|
||||
|
||||
/* set CPEN (control port mode) and power down */
|
||||
cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN);
|
||||
cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
|
||||
/* configure */
|
||||
cs4398_write(chip, 2, data->cs4398_regs[2]);
|
||||
cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L);
|
||||
cs4398_write(chip, 4, data->cs4398_regs[4]);
|
||||
cs4398_write(chip, 5, data->cs4398_regs[5]);
|
||||
cs4398_write(chip, 6, data->cs4398_regs[6]);
|
||||
cs4398_write(chip, 7, data->cs4398_regs[7]);
|
||||
cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST);
|
||||
cs4362a_write(chip, 0x03, CS4362A_MUTEC_6 | CS4362A_AMUTE |
|
||||
CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP);
|
||||
cs4362a_write(chip, 0x04, data->cs4362a_regs[0x04]);
|
||||
cs4362a_write(chip, 0x05, 0);
|
||||
for (i = 6; i <= 14; ++i)
|
||||
cs4362a_write(chip, i, data->cs4362a_regs[i]);
|
||||
/* clear power down */
|
||||
cs4398_write(chip, 8, CS4398_CPEN);
|
||||
cs4362a_write(chip, 0x01, CS4362A_CPEN);
|
||||
}
|
||||
|
||||
static void xonar_d1_init(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
|
||||
data->generic.anti_pop_delay = 800;
|
||||
data->generic.output_enable_bit = GPIO_D1_OUTPUT_ENABLE;
|
||||
data->cs4398_regs[2] =
|
||||
CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST;
|
||||
data->cs4398_regs[4] = CS4398_MUTEP_LOW |
|
||||
CS4398_MUTE_B | CS4398_MUTE_A | CS4398_PAMUTE;
|
||||
data->cs4398_regs[5] = 60 * 2;
|
||||
data->cs4398_regs[6] = 60 * 2;
|
||||
data->cs4398_regs[7] = CS4398_RMP_DN | CS4398_RMP_UP |
|
||||
CS4398_ZERO_CROSS | CS4398_SOFT_RAMP;
|
||||
data->cs4362a_regs[4] = CS4362A_RMP_DN | CS4362A_DEM_NONE;
|
||||
data->cs4362a_regs[6] = CS4362A_FM_SINGLE |
|
||||
CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
|
||||
data->cs4362a_regs[7] = 60 | CS4362A_MUTE;
|
||||
data->cs4362a_regs[8] = 60 | CS4362A_MUTE;
|
||||
data->cs4362a_regs[9] = data->cs4362a_regs[6];
|
||||
data->cs4362a_regs[10] = 60 | CS4362A_MUTE;
|
||||
data->cs4362a_regs[11] = 60 | CS4362A_MUTE;
|
||||
data->cs4362a_regs[12] = data->cs4362a_regs[6];
|
||||
data->cs4362a_regs[13] = 60 | CS4362A_MUTE;
|
||||
data->cs4362a_regs[14] = 60 | CS4362A_MUTE;
|
||||
|
||||
oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
|
||||
OXYGEN_2WIRE_LENGTH_8 |
|
||||
OXYGEN_2WIRE_INTERRUPT_MASK |
|
||||
OXYGEN_2WIRE_SPEED_FAST);
|
||||
|
||||
cs43xx_registers_init(chip);
|
||||
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
|
||||
GPIO_D1_FRONT_PANEL |
|
||||
GPIO_D1_MAGIC |
|
||||
GPIO_D1_INPUT_ROUTE);
|
||||
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
|
||||
GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE);
|
||||
|
||||
xonar_init_cs53x1(chip);
|
||||
xonar_enable_output(chip);
|
||||
|
||||
snd_component_add(chip->card, "CS4398");
|
||||
snd_component_add(chip->card, "CS4362A");
|
||||
snd_component_add(chip->card, "CS5361");
|
||||
}
|
||||
|
||||
static void xonar_dx_init(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
|
||||
data->generic.ext_power_reg = OXYGEN_GPI_DATA;
|
||||
data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
|
||||
data->generic.ext_power_bit = GPI_EXT_POWER;
|
||||
xonar_init_ext_power(chip);
|
||||
xonar_d1_init(chip);
|
||||
}
|
||||
|
||||
static void xonar_d1_cleanup(struct oxygen *chip)
|
||||
{
|
||||
xonar_disable_output(chip);
|
||||
cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
|
||||
oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
|
||||
}
|
||||
|
||||
static void xonar_d1_suspend(struct oxygen *chip)
|
||||
{
|
||||
xonar_d1_cleanup(chip);
|
||||
}
|
||||
|
||||
static void xonar_d1_resume(struct oxygen *chip)
|
||||
{
|
||||
oxygen_set_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
|
||||
msleep(1);
|
||||
cs43xx_registers_init(chip);
|
||||
xonar_enable_output(chip);
|
||||
}
|
||||
|
||||
static void set_cs43xx_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
u8 cs4398_fm, cs4362a_fm;
|
||||
|
||||
if (params_rate(params) <= 50000) {
|
||||
cs4398_fm = CS4398_FM_SINGLE;
|
||||
cs4362a_fm = CS4362A_FM_SINGLE;
|
||||
} else if (params_rate(params) <= 100000) {
|
||||
cs4398_fm = CS4398_FM_DOUBLE;
|
||||
cs4362a_fm = CS4362A_FM_DOUBLE;
|
||||
} else {
|
||||
cs4398_fm = CS4398_FM_QUAD;
|
||||
cs4362a_fm = CS4362A_FM_QUAD;
|
||||
}
|
||||
cs4398_fm |= CS4398_DEM_NONE | CS4398_DIF_LJUST;
|
||||
cs4398_write_cached(chip, 2, cs4398_fm);
|
||||
cs4362a_fm |= data->cs4362a_regs[6] & ~CS4362A_FM_MASK;
|
||||
cs4362a_write_cached(chip, 6, cs4362a_fm);
|
||||
cs4362a_write_cached(chip, 12, cs4362a_fm);
|
||||
cs4362a_fm &= CS4362A_FM_MASK;
|
||||
cs4362a_fm |= data->cs4362a_regs[9] & ~CS4362A_FM_MASK;
|
||||
cs4362a_write_cached(chip, 9, cs4362a_fm);
|
||||
}
|
||||
|
||||
static void update_cs4362a_volumes(struct oxygen *chip)
|
||||
{
|
||||
unsigned int i;
|
||||
u8 mute;
|
||||
|
||||
mute = chip->dac_mute ? CS4362A_MUTE : 0;
|
||||
for (i = 0; i < 6; ++i)
|
||||
cs4362a_write_cached(chip, 7 + i + i / 2,
|
||||
(127 - chip->dac_volume[2 + i]) | mute);
|
||||
}
|
||||
|
||||
static void update_cs43xx_volume(struct oxygen *chip)
|
||||
{
|
||||
cs4398_write_cached(chip, 5, (127 - chip->dac_volume[0]) * 2);
|
||||
cs4398_write_cached(chip, 6, (127 - chip->dac_volume[1]) * 2);
|
||||
update_cs4362a_volumes(chip);
|
||||
}
|
||||
|
||||
static void update_cs43xx_mute(struct oxygen *chip)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
reg = CS4398_MUTEP_LOW | CS4398_PAMUTE;
|
||||
if (chip->dac_mute)
|
||||
reg |= CS4398_MUTE_B | CS4398_MUTE_A;
|
||||
cs4398_write_cached(chip, 4, reg);
|
||||
update_cs4362a_volumes(chip);
|
||||
}
|
||||
|
||||
static void update_cs43xx_center_lfe_mix(struct oxygen *chip, bool mixed)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
u8 reg;
|
||||
|
||||
reg = data->cs4362a_regs[9] & ~CS4362A_ATAPI_MASK;
|
||||
if (mixed)
|
||||
reg |= CS4362A_ATAPI_B_LR | CS4362A_ATAPI_A_LR;
|
||||
else
|
||||
reg |= CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
|
||||
cs4362a_write_cached(chip, 9, reg);
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new front_panel_switch = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Front Panel Playback Switch",
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = xonar_gpio_bit_switch_get,
|
||||
.put = xonar_gpio_bit_switch_put,
|
||||
.private_value = GPIO_D1_FRONT_PANEL,
|
||||
};
|
||||
|
||||
static int rolloff_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[2] = {
|
||||
"Fast Roll-off", "Slow Roll-off"
|
||||
};
|
||||
|
||||
return snd_ctl_enum_info(info, 1, 2, names);
|
||||
}
|
||||
|
||||
static int rolloff_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
|
||||
value->value.enumerated.item[0] =
|
||||
(data->cs4398_regs[7] & CS4398_FILT_SEL) != 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rolloff_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
int changed;
|
||||
u8 reg;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
reg = data->cs4398_regs[7];
|
||||
if (value->value.enumerated.item[0])
|
||||
reg |= CS4398_FILT_SEL;
|
||||
else
|
||||
reg &= ~CS4398_FILT_SEL;
|
||||
changed = reg != data->cs4398_regs[7];
|
||||
if (changed) {
|
||||
cs4398_write(chip, 7, reg);
|
||||
if (reg & CS4398_FILT_SEL)
|
||||
reg = data->cs4362a_regs[0x04] | CS4362A_FILT_SEL;
|
||||
else
|
||||
reg = data->cs4362a_regs[0x04] & ~CS4362A_FILT_SEL;
|
||||
cs4362a_write(chip, 0x04, reg);
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
static const struct snd_kcontrol_new rolloff_control = {
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "DAC Filter Playback Enum",
|
||||
.info = rolloff_info,
|
||||
.get = rolloff_get,
|
||||
.put = rolloff_put,
|
||||
};
|
||||
|
||||
static void xonar_d1_line_mic_ac97_switch(struct oxygen *chip,
|
||||
unsigned int reg, unsigned int mute)
|
||||
{
|
||||
if (reg == AC97_LINE) {
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
|
||||
mute ? GPIO_D1_INPUT_ROUTE : 0,
|
||||
GPIO_D1_INPUT_ROUTE);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -6000, 100, 0);
|
||||
|
||||
static int xonar_d1_mixer_init(struct oxygen *chip)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dump_cs4362a_registers(struct xonar_cs43xx *data,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
snd_iprintf(buffer, "\nCS4362A:");
|
||||
for (i = 1; i <= 14; ++i)
|
||||
snd_iprintf(buffer, " %02x", data->cs4362a_regs[i]);
|
||||
snd_iprintf(buffer, "\n");
|
||||
}
|
||||
|
||||
static void dump_d1_registers(struct oxygen *chip,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct xonar_cs43xx *data = chip->model_data;
|
||||
unsigned int i;
|
||||
|
||||
snd_iprintf(buffer, "\nCS4398: 7?");
|
||||
for (i = 2; i < 8; ++i)
|
||||
snd_iprintf(buffer, " %02x", data->cs4398_regs[i]);
|
||||
snd_iprintf(buffer, "\n");
|
||||
dump_cs4362a_registers(data, buffer);
|
||||
}
|
||||
|
||||
static const struct oxygen_model model_xonar_d1 = {
|
||||
.longname = "Asus Virtuoso 100",
|
||||
.chip = "AV200",
|
||||
.init = xonar_d1_init,
|
||||
.mixer_init = xonar_d1_mixer_init,
|
||||
.cleanup = xonar_d1_cleanup,
|
||||
.suspend = xonar_d1_suspend,
|
||||
.resume = xonar_d1_resume,
|
||||
.set_dac_params = set_cs43xx_params,
|
||||
.set_adc_params = xonar_set_cs53x1_params,
|
||||
.update_dac_volume = update_cs43xx_volume,
|
||||
.update_dac_mute = update_cs43xx_mute,
|
||||
.update_center_lfe_mix = update_cs43xx_center_lfe_mix,
|
||||
.ac97_switch = xonar_d1_line_mic_ac97_switch,
|
||||
.dump_registers = dump_d1_registers,
|
||||
.dac_tlv = cs4362a_db_scale,
|
||||
.model_data_size = sizeof(struct xonar_cs43xx),
|
||||
.device_config = PLAYBACK_0_TO_I2S |
|
||||
PLAYBACK_1_TO_SPDIF |
|
||||
CAPTURE_0_FROM_I2S_2 |
|
||||
CAPTURE_1_FROM_SPDIF |
|
||||
AC97_FMIC_SWITCH,
|
||||
.dac_channels_pcm = 8,
|
||||
.dac_channels_mixer = 8,
|
||||
.dac_volume_min = 127 - 60,
|
||||
.dac_volume_max = 127,
|
||||
.function_flags = OXYGEN_FUNCTION_2WIRE,
|
||||
.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
|
||||
.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
|
||||
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
};
|
||||
|
||||
int get_xonar_cs43xx_model(struct oxygen *chip,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
switch (id->subdevice) {
|
||||
case 0x834f:
|
||||
chip->model = model_xonar_d1;
|
||||
chip->model.shortname = "Xonar D1";
|
||||
break;
|
||||
case 0x8275:
|
||||
case 0x8327:
|
||||
chip->model = model_xonar_d1;
|
||||
chip->model.shortname = "Xonar DX";
|
||||
chip->model.init = xonar_dx_init;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
295
sound/pci/oxygen/xonar_dg.c
Normal file
295
sound/pci/oxygen/xonar_dg.c
Normal file
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
* card driver for the Xonar DG/DGX
|
||||
*
|
||||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||
* Copyright (c) Roman Volkov <v1ron@mail.ru>
|
||||
*
|
||||
* This driver is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2.
|
||||
*
|
||||
* This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Xonar DG/DGX
|
||||
* ------------
|
||||
*
|
||||
* CS4245 and CS4361 both will mute all outputs if any clock ratio
|
||||
* is invalid.
|
||||
*
|
||||
* CMI8788:
|
||||
*
|
||||
* SPI 0 -> CS4245
|
||||
*
|
||||
* Playback:
|
||||
* I²S 1 -> CS4245
|
||||
* I²S 2 -> CS4361 (center/LFE)
|
||||
* I²S 3 -> CS4361 (surround)
|
||||
* I²S 4 -> CS4361 (front)
|
||||
* Capture:
|
||||
* I²S ADC 1 <- CS4245
|
||||
*
|
||||
* GPIO 3 <- ?
|
||||
* GPIO 4 <- headphone detect
|
||||
* GPIO 5 -> enable ADC analog circuit for the left channel
|
||||
* GPIO 6 -> enable ADC analog circuit for the right channel
|
||||
* GPIO 7 -> switch green rear output jack between CS4245 and and the first
|
||||
* channel of CS4361 (mechanical relay)
|
||||
* GPIO 8 -> enable output to speakers
|
||||
*
|
||||
* CS4245:
|
||||
*
|
||||
* input 0 <- mic
|
||||
* input 1 <- aux
|
||||
* input 2 <- front mic
|
||||
* input 4 <- line
|
||||
* DAC out -> headphones
|
||||
* aux out -> front panel headphones
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "oxygen.h"
|
||||
#include "xonar_dg.h"
|
||||
#include "cs4245.h"
|
||||
|
||||
int cs4245_write_spi(struct oxygen *chip, u8 reg)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
unsigned int packet;
|
||||
|
||||
packet = reg << 8;
|
||||
packet |= (CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 16;
|
||||
packet |= data->cs4245_shadow[reg];
|
||||
|
||||
return oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
|
||||
OXYGEN_SPI_DATA_LENGTH_3 |
|
||||
OXYGEN_SPI_CLOCK_1280 |
|
||||
(0 << OXYGEN_SPI_CODEC_SHIFT) |
|
||||
OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
|
||||
packet);
|
||||
}
|
||||
|
||||
int cs4245_read_spi(struct oxygen *chip, u8 addr)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
int ret;
|
||||
|
||||
ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
|
||||
OXYGEN_SPI_DATA_LENGTH_2 |
|
||||
OXYGEN_SPI_CEN_LATCH_CLOCK_HI |
|
||||
OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),
|
||||
((CS4245_SPI_ADDRESS | CS4245_SPI_WRITE) << 8) | addr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
|
||||
OXYGEN_SPI_DATA_LENGTH_2 |
|
||||
OXYGEN_SPI_CEN_LATCH_CLOCK_HI |
|
||||
OXYGEN_SPI_CLOCK_1280 | (0 << OXYGEN_SPI_CODEC_SHIFT),
|
||||
(CS4245_SPI_ADDRESS | CS4245_SPI_READ) << 8);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->cs4245_shadow[addr] = oxygen_read8(chip, OXYGEN_SPI_DATA1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
unsigned char addr;
|
||||
int ret;
|
||||
|
||||
for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++) {
|
||||
ret = (op == CS4245_SAVE_TO_SHADOW ?
|
||||
cs4245_read_spi(chip, addr) :
|
||||
cs4245_write_spi(chip, addr));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cs4245_init(struct oxygen *chip)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
/* save the initial state: codec version, registers */
|
||||
cs4245_shadow_control(chip, CS4245_SAVE_TO_SHADOW);
|
||||
|
||||
/*
|
||||
* Power up the CODEC internals, enable soft ramp & zero cross, work in
|
||||
* async. mode, enable aux output from DAC. Invert DAC output as in the
|
||||
* Windows driver.
|
||||
*/
|
||||
data->cs4245_shadow[CS4245_POWER_CTRL] = 0;
|
||||
data->cs4245_shadow[CS4245_SIGNAL_SEL] =
|
||||
CS4245_A_OUT_SEL_DAC | CS4245_ASYNCH;
|
||||
data->cs4245_shadow[CS4245_DAC_CTRL_1] =
|
||||
CS4245_DAC_FM_SINGLE | CS4245_DAC_DIF_LJUST;
|
||||
data->cs4245_shadow[CS4245_DAC_CTRL_2] =
|
||||
CS4245_DAC_SOFT | CS4245_DAC_ZERO | CS4245_INVERT_DAC;
|
||||
data->cs4245_shadow[CS4245_ADC_CTRL] =
|
||||
CS4245_ADC_FM_SINGLE | CS4245_ADC_DIF_LJUST;
|
||||
data->cs4245_shadow[CS4245_ANALOG_IN] =
|
||||
CS4245_PGA_SOFT | CS4245_PGA_ZERO;
|
||||
data->cs4245_shadow[CS4245_PGA_B_CTRL] = 0;
|
||||
data->cs4245_shadow[CS4245_PGA_A_CTRL] = 0;
|
||||
data->cs4245_shadow[CS4245_DAC_A_CTRL] = 8;
|
||||
data->cs4245_shadow[CS4245_DAC_B_CTRL] = 8;
|
||||
|
||||
cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
|
||||
snd_component_add(chip->card, "CS4245");
|
||||
}
|
||||
|
||||
void dg_init(struct oxygen *chip)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
data->output_sel = PLAYBACK_DST_HP_FP;
|
||||
data->input_sel = CAPTURE_SRC_MIC;
|
||||
|
||||
cs4245_init(chip);
|
||||
oxygen_write16(chip, OXYGEN_GPIO_CONTROL,
|
||||
GPIO_OUTPUT_ENABLE | GPIO_HP_REAR | GPIO_INPUT_ROUTE);
|
||||
/* anti-pop delay, wait some time before enabling the output */
|
||||
msleep(2500);
|
||||
oxygen_write16(chip, OXYGEN_GPIO_DATA,
|
||||
GPIO_OUTPUT_ENABLE | GPIO_INPUT_ROUTE);
|
||||
}
|
||||
|
||||
void dg_cleanup(struct oxygen *chip)
|
||||
{
|
||||
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
|
||||
}
|
||||
|
||||
void dg_suspend(struct oxygen *chip)
|
||||
{
|
||||
dg_cleanup(chip);
|
||||
}
|
||||
|
||||
void dg_resume(struct oxygen *chip)
|
||||
{
|
||||
cs4245_shadow_control(chip, CS4245_LOAD_FROM_SHADOW);
|
||||
msleep(2500);
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_OUTPUT_ENABLE);
|
||||
}
|
||||
|
||||
void set_cs4245_dac_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
unsigned char dac_ctrl;
|
||||
unsigned char mclk_freq;
|
||||
|
||||
dac_ctrl = data->cs4245_shadow[CS4245_DAC_CTRL_1] & ~CS4245_DAC_FM_MASK;
|
||||
mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK1_MASK;
|
||||
if (params_rate(params) <= 50000) {
|
||||
dac_ctrl |= CS4245_DAC_FM_SINGLE;
|
||||
mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
|
||||
} else if (params_rate(params) <= 100000) {
|
||||
dac_ctrl |= CS4245_DAC_FM_DOUBLE;
|
||||
mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK1_SHIFT;
|
||||
} else {
|
||||
dac_ctrl |= CS4245_DAC_FM_QUAD;
|
||||
mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK1_SHIFT;
|
||||
}
|
||||
data->cs4245_shadow[CS4245_DAC_CTRL_1] = dac_ctrl;
|
||||
data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
|
||||
cs4245_write_spi(chip, CS4245_DAC_CTRL_1);
|
||||
cs4245_write_spi(chip, CS4245_MCLK_FREQ);
|
||||
}
|
||||
|
||||
void set_cs4245_adc_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
unsigned char adc_ctrl;
|
||||
unsigned char mclk_freq;
|
||||
|
||||
adc_ctrl = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_ADC_FM_MASK;
|
||||
mclk_freq = data->cs4245_shadow[CS4245_MCLK_FREQ] & ~CS4245_MCLK2_MASK;
|
||||
if (params_rate(params) <= 50000) {
|
||||
adc_ctrl |= CS4245_ADC_FM_SINGLE;
|
||||
mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
|
||||
} else if (params_rate(params) <= 100000) {
|
||||
adc_ctrl |= CS4245_ADC_FM_DOUBLE;
|
||||
mclk_freq |= CS4245_MCLK_1 << CS4245_MCLK2_SHIFT;
|
||||
} else {
|
||||
adc_ctrl |= CS4245_ADC_FM_QUAD;
|
||||
mclk_freq |= CS4245_MCLK_2 << CS4245_MCLK2_SHIFT;
|
||||
}
|
||||
data->cs4245_shadow[CS4245_ADC_CTRL] = adc_ctrl;
|
||||
data->cs4245_shadow[CS4245_MCLK_FREQ] = mclk_freq;
|
||||
cs4245_write_spi(chip, CS4245_ADC_CTRL);
|
||||
cs4245_write_spi(chip, CS4245_MCLK_FREQ);
|
||||
}
|
||||
|
||||
static inline unsigned int shift_bits(unsigned int value,
|
||||
unsigned int shift_from,
|
||||
unsigned int shift_to,
|
||||
unsigned int mask)
|
||||
{
|
||||
if (shift_from < shift_to)
|
||||
return (value << (shift_to - shift_from)) & mask;
|
||||
else
|
||||
return (value >> (shift_from - shift_to)) & mask;
|
||||
}
|
||||
|
||||
unsigned int adjust_dg_dac_routing(struct oxygen *chip,
|
||||
unsigned int play_routing)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
switch (data->output_sel) {
|
||||
case PLAYBACK_DST_HP:
|
||||
case PLAYBACK_DST_HP_FP:
|
||||
oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
|
||||
OXYGEN_PLAY_MUTE23 | OXYGEN_PLAY_MUTE45 |
|
||||
OXYGEN_PLAY_MUTE67, OXYGEN_PLAY_MUTE_MASK);
|
||||
break;
|
||||
case PLAYBACK_DST_MULTICH:
|
||||
oxygen_write8_masked(chip, OXYGEN_PLAY_ROUTING,
|
||||
OXYGEN_PLAY_MUTE01, OXYGEN_PLAY_MUTE_MASK);
|
||||
break;
|
||||
}
|
||||
return (play_routing & OXYGEN_PLAY_DAC0_SOURCE_MASK) |
|
||||
shift_bits(play_routing,
|
||||
OXYGEN_PLAY_DAC2_SOURCE_SHIFT,
|
||||
OXYGEN_PLAY_DAC1_SOURCE_SHIFT,
|
||||
OXYGEN_PLAY_DAC1_SOURCE_MASK) |
|
||||
shift_bits(play_routing,
|
||||
OXYGEN_PLAY_DAC1_SOURCE_SHIFT,
|
||||
OXYGEN_PLAY_DAC2_SOURCE_SHIFT,
|
||||
OXYGEN_PLAY_DAC2_SOURCE_MASK) |
|
||||
shift_bits(play_routing,
|
||||
OXYGEN_PLAY_DAC0_SOURCE_SHIFT,
|
||||
OXYGEN_PLAY_DAC3_SOURCE_SHIFT,
|
||||
OXYGEN_PLAY_DAC3_SOURCE_MASK);
|
||||
}
|
||||
|
||||
void dump_cs4245_registers(struct oxygen *chip,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
unsigned int addr;
|
||||
|
||||
snd_iprintf(buffer, "\nCS4245:");
|
||||
cs4245_read_spi(chip, CS4245_INT_STATUS);
|
||||
for (addr = 1; addr < ARRAY_SIZE(data->cs4245_shadow); addr++)
|
||||
snd_iprintf(buffer, " %02x", data->cs4245_shadow[addr]);
|
||||
snd_iprintf(buffer, "\n");
|
||||
}
|
56
sound/pci/oxygen/xonar_dg.h
Normal file
56
sound/pci/oxygen/xonar_dg.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
#ifndef XONAR_DG_H_INCLUDED
|
||||
#define XONAR_DG_H_INCLUDED
|
||||
|
||||
#include "oxygen.h"
|
||||
|
||||
#define GPIO_MAGIC 0x0008
|
||||
#define GPIO_HP_DETECT 0x0010
|
||||
#define GPIO_INPUT_ROUTE 0x0060
|
||||
#define GPIO_HP_REAR 0x0080
|
||||
#define GPIO_OUTPUT_ENABLE 0x0100
|
||||
|
||||
#define CAPTURE_SRC_MIC 0
|
||||
#define CAPTURE_SRC_FP_MIC 1
|
||||
#define CAPTURE_SRC_LINE 2
|
||||
#define CAPTURE_SRC_AUX 3
|
||||
|
||||
#define PLAYBACK_DST_HP 0
|
||||
#define PLAYBACK_DST_HP_FP 1
|
||||
#define PLAYBACK_DST_MULTICH 2
|
||||
|
||||
enum cs4245_shadow_operation {
|
||||
CS4245_SAVE_TO_SHADOW,
|
||||
CS4245_LOAD_FROM_SHADOW
|
||||
};
|
||||
|
||||
struct dg {
|
||||
/* shadow copy of the CS4245 register space */
|
||||
unsigned char cs4245_shadow[17];
|
||||
/* output select: headphone/speakers */
|
||||
unsigned char output_sel;
|
||||
/* volumes for all capture sources */
|
||||
char input_vol[4][2];
|
||||
/* input select: mic/fp mic/line/aux */
|
||||
unsigned char input_sel;
|
||||
};
|
||||
|
||||
/* Xonar DG control routines */
|
||||
int cs4245_write_spi(struct oxygen *chip, u8 reg);
|
||||
int cs4245_read_spi(struct oxygen *chip, u8 reg);
|
||||
int cs4245_shadow_control(struct oxygen *chip, enum cs4245_shadow_operation op);
|
||||
void dg_init(struct oxygen *chip);
|
||||
void set_cs4245_dac_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params);
|
||||
void set_cs4245_adc_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params);
|
||||
unsigned int adjust_dg_dac_routing(struct oxygen *chip,
|
||||
unsigned int play_routing);
|
||||
void dump_cs4245_registers(struct oxygen *chip,
|
||||
struct snd_info_buffer *buffer);
|
||||
void dg_suspend(struct oxygen *chip);
|
||||
void dg_resume(struct oxygen *chip);
|
||||
void dg_cleanup(struct oxygen *chip);
|
||||
|
||||
extern struct oxygen_model model_xonar_dg;
|
||||
|
||||
#endif
|
477
sound/pci/oxygen/xonar_dg_mixer.c
Normal file
477
sound/pci/oxygen/xonar_dg_mixer.c
Normal file
|
@ -0,0 +1,477 @@
|
|||
/*
|
||||
* Mixer controls for the Xonar DG/DGX
|
||||
*
|
||||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||
* Copyright (c) Roman Volkov <v1ron@mail.ru>
|
||||
*
|
||||
* This driver is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2.
|
||||
*
|
||||
* This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "oxygen.h"
|
||||
#include "xonar_dg.h"
|
||||
#include "cs4245.h"
|
||||
|
||||
/* analog output select */
|
||||
|
||||
static int output_select_apply(struct oxygen *chip)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
data->cs4245_shadow[CS4245_SIGNAL_SEL] &= ~CS4245_A_OUT_SEL_MASK;
|
||||
if (data->output_sel == PLAYBACK_DST_HP) {
|
||||
/* mute FP (aux output) amplifier, switch rear jack to CS4245 */
|
||||
oxygen_set_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
|
||||
} else if (data->output_sel == PLAYBACK_DST_HP_FP) {
|
||||
/*
|
||||
* Unmute FP amplifier, switch rear jack to CS4361;
|
||||
* I2S channels 2,3,4 should be inactive.
|
||||
*/
|
||||
oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
|
||||
data->cs4245_shadow[CS4245_SIGNAL_SEL] |= CS4245_A_OUT_SEL_DAC;
|
||||
} else {
|
||||
/*
|
||||
* 2.0, 4.0, 5.1: switch to CS4361, mute FP amp.,
|
||||
* and change playback routing.
|
||||
*/
|
||||
oxygen_clear_bits8(chip, OXYGEN_GPIO_DATA, GPIO_HP_REAR);
|
||||
}
|
||||
return cs4245_write_spi(chip, CS4245_SIGNAL_SEL);
|
||||
}
|
||||
|
||||
static int output_select_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[3] = {
|
||||
"Stereo Headphones",
|
||||
"Stereo Headphones FP",
|
||||
"Multichannel",
|
||||
};
|
||||
|
||||
return snd_ctl_enum_info(info, 1, 3, names);
|
||||
}
|
||||
|
||||
static int output_select_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
value->value.enumerated.item[0] = data->output_sel;
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int output_select_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
unsigned int new = value->value.enumerated.item[0];
|
||||
int changed = 0;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
if (data->output_sel != new) {
|
||||
data->output_sel = new;
|
||||
ret = output_select_apply(chip);
|
||||
changed = ret >= 0 ? 1 : ret;
|
||||
oxygen_update_dac_routing(chip);
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* CS4245 Headphone Channels A&B Volume Control */
|
||||
|
||||
static int hp_stereo_volume_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
info->count = 2;
|
||||
info->value.integer.min = 0;
|
||||
info->value.integer.max = 255;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hp_stereo_volume_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *val)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
unsigned int tmp;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
tmp = (~data->cs4245_shadow[CS4245_DAC_A_CTRL]) & 255;
|
||||
val->value.integer.value[0] = tmp;
|
||||
tmp = (~data->cs4245_shadow[CS4245_DAC_B_CTRL]) & 255;
|
||||
val->value.integer.value[1] = tmp;
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hp_stereo_volume_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *val)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
int ret;
|
||||
int changed = 0;
|
||||
long new1 = val->value.integer.value[0];
|
||||
long new2 = val->value.integer.value[1];
|
||||
|
||||
if ((new1 > 255) || (new1 < 0) || (new2 > 255) || (new2 < 0))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
if ((data->cs4245_shadow[CS4245_DAC_A_CTRL] != ~new1) ||
|
||||
(data->cs4245_shadow[CS4245_DAC_B_CTRL] != ~new2)) {
|
||||
data->cs4245_shadow[CS4245_DAC_A_CTRL] = ~new1;
|
||||
data->cs4245_shadow[CS4245_DAC_B_CTRL] = ~new2;
|
||||
ret = cs4245_write_spi(chip, CS4245_DAC_A_CTRL);
|
||||
if (ret >= 0)
|
||||
ret = cs4245_write_spi(chip, CS4245_DAC_B_CTRL);
|
||||
changed = ret >= 0 ? 1 : ret;
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* Headphone Mute */
|
||||
|
||||
static int hp_mute_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *val)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
val->value.integer.value[0] =
|
||||
!(data->cs4245_shadow[CS4245_DAC_CTRL_1] & CS4245_MUTE_DAC);
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hp_mute_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *val)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
int ret;
|
||||
int changed;
|
||||
|
||||
if (val->value.integer.value[0] > 1)
|
||||
return -EINVAL;
|
||||
mutex_lock(&chip->mutex);
|
||||
data->cs4245_shadow[CS4245_DAC_CTRL_1] &= ~CS4245_MUTE_DAC;
|
||||
data->cs4245_shadow[CS4245_DAC_CTRL_1] |=
|
||||
(~val->value.integer.value[0] << 2) & CS4245_MUTE_DAC;
|
||||
ret = cs4245_write_spi(chip, CS4245_DAC_CTRL_1);
|
||||
changed = ret >= 0 ? 1 : ret;
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* capture volume for all sources */
|
||||
|
||||
static int input_volume_apply(struct oxygen *chip, char left, char right)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
int ret;
|
||||
|
||||
data->cs4245_shadow[CS4245_PGA_A_CTRL] = left;
|
||||
data->cs4245_shadow[CS4245_PGA_B_CTRL] = right;
|
||||
ret = cs4245_write_spi(chip, CS4245_PGA_A_CTRL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return cs4245_write_spi(chip, CS4245_PGA_B_CTRL);
|
||||
}
|
||||
|
||||
static int input_vol_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
||||
info->count = 2;
|
||||
info->value.integer.min = 2 * -12;
|
||||
info->value.integer.max = 2 * 12;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int input_vol_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
unsigned int idx = ctl->private_value;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
value->value.integer.value[0] = data->input_vol[idx][0];
|
||||
value->value.integer.value[1] = data->input_vol[idx][1];
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int input_vol_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
unsigned int idx = ctl->private_value;
|
||||
int changed = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (value->value.integer.value[0] < 2 * -12 ||
|
||||
value->value.integer.value[0] > 2 * 12 ||
|
||||
value->value.integer.value[1] < 2 * -12 ||
|
||||
value->value.integer.value[1] > 2 * 12)
|
||||
return -EINVAL;
|
||||
mutex_lock(&chip->mutex);
|
||||
changed = data->input_vol[idx][0] != value->value.integer.value[0] ||
|
||||
data->input_vol[idx][1] != value->value.integer.value[1];
|
||||
if (changed) {
|
||||
data->input_vol[idx][0] = value->value.integer.value[0];
|
||||
data->input_vol[idx][1] = value->value.integer.value[1];
|
||||
if (idx == data->input_sel) {
|
||||
ret = input_volume_apply(chip,
|
||||
data->input_vol[idx][0],
|
||||
data->input_vol[idx][1]);
|
||||
}
|
||||
changed = ret >= 0 ? 1 : ret;
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* Capture Source */
|
||||
|
||||
static int input_source_apply(struct oxygen *chip)
|
||||
{
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
data->cs4245_shadow[CS4245_ANALOG_IN] &= ~CS4245_SEL_MASK;
|
||||
if (data->input_sel == CAPTURE_SRC_FP_MIC)
|
||||
data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_2;
|
||||
else if (data->input_sel == CAPTURE_SRC_LINE)
|
||||
data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_4;
|
||||
else if (data->input_sel != CAPTURE_SRC_MIC)
|
||||
data->cs4245_shadow[CS4245_ANALOG_IN] |= CS4245_SEL_INPUT_1;
|
||||
return cs4245_write_spi(chip, CS4245_ANALOG_IN);
|
||||
}
|
||||
|
||||
static int input_sel_info(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[4] = {
|
||||
"Mic", "Front Mic", "Line", "Aux"
|
||||
};
|
||||
|
||||
return snd_ctl_enum_info(info, 1, 4, names);
|
||||
}
|
||||
|
||||
static int input_sel_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
value->value.enumerated.item[0] = data->input_sel;
|
||||
mutex_unlock(&chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int input_sel_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
int changed;
|
||||
int ret;
|
||||
|
||||
if (value->value.enumerated.item[0] > 3)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
changed = value->value.enumerated.item[0] != data->input_sel;
|
||||
if (changed) {
|
||||
data->input_sel = value->value.enumerated.item[0];
|
||||
|
||||
ret = input_source_apply(chip);
|
||||
if (ret >= 0)
|
||||
ret = input_volume_apply(chip,
|
||||
data->input_vol[data->input_sel][0],
|
||||
data->input_vol[data->input_sel][1]);
|
||||
changed = ret >= 0 ? 1 : ret;
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* ADC high-pass filter */
|
||||
|
||||
static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
|
||||
{
|
||||
static const char *const names[2] = { "Active", "Frozen" };
|
||||
|
||||
return snd_ctl_enum_info(info, 1, 2, names);
|
||||
}
|
||||
|
||||
static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
|
||||
value->value.enumerated.item[0] =
|
||||
!!(data->cs4245_shadow[CS4245_ADC_CTRL] & CS4245_HPF_FREEZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
struct dg *data = chip->model_data;
|
||||
u8 reg;
|
||||
int changed;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
reg = data->cs4245_shadow[CS4245_ADC_CTRL] & ~CS4245_HPF_FREEZE;
|
||||
if (value->value.enumerated.item[0])
|
||||
reg |= CS4245_HPF_FREEZE;
|
||||
changed = reg != data->cs4245_shadow[CS4245_ADC_CTRL];
|
||||
if (changed) {
|
||||
data->cs4245_shadow[CS4245_ADC_CTRL] = reg;
|
||||
cs4245_write_spi(chip, CS4245_ADC_CTRL);
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
return changed;
|
||||
}
|
||||
|
||||
#define INPUT_VOLUME(xname, index) { \
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
|
||||
.name = xname, \
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
|
||||
.info = input_vol_info, \
|
||||
.get = input_vol_get, \
|
||||
.put = input_vol_put, \
|
||||
.tlv = { .p = pga_db_scale }, \
|
||||
.private_value = index, \
|
||||
}
|
||||
static const DECLARE_TLV_DB_MINMAX(hp_db_scale, -12550, 0);
|
||||
static const DECLARE_TLV_DB_MINMAX(pga_db_scale, -1200, 1200);
|
||||
static const struct snd_kcontrol_new dg_controls[] = {
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Analog Output Playback Enum",
|
||||
.info = output_select_info,
|
||||
.get = output_select_get,
|
||||
.put = output_select_put,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Headphone Playback Volume",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
|
||||
.info = hp_stereo_volume_info,
|
||||
.get = hp_stereo_volume_get,
|
||||
.put = hp_stereo_volume_put,
|
||||
.tlv = { .p = hp_db_scale, },
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Headphone Playback Switch",
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
||||
.info = snd_ctl_boolean_mono_info,
|
||||
.get = hp_mute_get,
|
||||
.put = hp_mute_put,
|
||||
},
|
||||
INPUT_VOLUME("Mic Capture Volume", CAPTURE_SRC_MIC),
|
||||
INPUT_VOLUME("Front Mic Capture Volume", CAPTURE_SRC_FP_MIC),
|
||||
INPUT_VOLUME("Line Capture Volume", CAPTURE_SRC_LINE),
|
||||
INPUT_VOLUME("Aux Capture Volume", CAPTURE_SRC_AUX),
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Capture Source",
|
||||
.info = input_sel_info,
|
||||
.get = input_sel_get,
|
||||
.put = input_sel_put,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "ADC High-pass Filter Capture Enum",
|
||||
.info = hpf_info,
|
||||
.get = hpf_get,
|
||||
.put = hpf_put,
|
||||
},
|
||||
};
|
||||
|
||||
static int dg_control_filter(struct snd_kcontrol_new *template)
|
||||
{
|
||||
if (!strncmp(template->name, "Master Playback ", 16))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dg_mixer_init(struct oxygen *chip)
|
||||
{
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
output_select_apply(chip);
|
||||
input_source_apply(chip);
|
||||
oxygen_update_dac_routing(chip);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dg_controls); ++i) {
|
||||
err = snd_ctl_add(chip->card,
|
||||
snd_ctl_new1(&dg_controls[i], chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct oxygen_model model_xonar_dg = {
|
||||
.longname = "C-Media Oxygen HD Audio",
|
||||
.chip = "CMI8786",
|
||||
.init = dg_init,
|
||||
.control_filter = dg_control_filter,
|
||||
.mixer_init = dg_mixer_init,
|
||||
.cleanup = dg_cleanup,
|
||||
.suspend = dg_suspend,
|
||||
.resume = dg_resume,
|
||||
.set_dac_params = set_cs4245_dac_params,
|
||||
.set_adc_params = set_cs4245_adc_params,
|
||||
.adjust_dac_routing = adjust_dg_dac_routing,
|
||||
.dump_registers = dump_cs4245_registers,
|
||||
.model_data_size = sizeof(struct dg),
|
||||
.device_config = PLAYBACK_0_TO_I2S |
|
||||
PLAYBACK_1_TO_SPDIF |
|
||||
CAPTURE_0_FROM_I2S_1 |
|
||||
CAPTURE_1_FROM_SPDIF,
|
||||
.dac_channels_pcm = 6,
|
||||
.dac_channels_mixer = 0,
|
||||
.function_flags = OXYGEN_FUNCTION_SPI,
|
||||
.dac_mclks = OXYGEN_MCLKS(256, 128, 128),
|
||||
.adc_mclks = OXYGEN_MCLKS(256, 128, 128),
|
||||
.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
|
||||
};
|
128
sound/pci/oxygen/xonar_hdmi.c
Normal file
128
sound/pci/oxygen/xonar_hdmi.c
Normal file
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* helper functions for HDMI models (Xonar HDAV1.3/HDAV1.3 Slim)
|
||||
*
|
||||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||
*
|
||||
*
|
||||
* This driver is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2.
|
||||
*
|
||||
* This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <sound/asoundef.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/tlv.h>
|
||||
#include "xonar.h"
|
||||
|
||||
static void hdmi_write_command(struct oxygen *chip, u8 command,
|
||||
unsigned int count, const u8 *params)
|
||||
{
|
||||
unsigned int i;
|
||||
u8 checksum;
|
||||
|
||||
oxygen_write_uart(chip, 0xfb);
|
||||
oxygen_write_uart(chip, 0xef);
|
||||
oxygen_write_uart(chip, command);
|
||||
oxygen_write_uart(chip, count);
|
||||
for (i = 0; i < count; ++i)
|
||||
oxygen_write_uart(chip, params[i]);
|
||||
checksum = 0xfb + 0xef + command + count;
|
||||
for (i = 0; i < count; ++i)
|
||||
checksum += params[i];
|
||||
oxygen_write_uart(chip, checksum);
|
||||
}
|
||||
|
||||
static void xonar_hdmi_init_commands(struct oxygen *chip,
|
||||
struct xonar_hdmi *hdmi)
|
||||
{
|
||||
u8 param;
|
||||
|
||||
oxygen_reset_uart(chip);
|
||||
param = 0;
|
||||
hdmi_write_command(chip, 0x61, 1, ¶m);
|
||||
param = 1;
|
||||
hdmi_write_command(chip, 0x74, 1, ¶m);
|
||||
hdmi_write_command(chip, 0x54, 5, hdmi->params);
|
||||
}
|
||||
|
||||
void xonar_hdmi_init(struct oxygen *chip, struct xonar_hdmi *hdmi)
|
||||
{
|
||||
hdmi->params[1] = IEC958_AES3_CON_FS_48000;
|
||||
hdmi->params[4] = 1;
|
||||
xonar_hdmi_init_commands(chip, hdmi);
|
||||
}
|
||||
|
||||
void xonar_hdmi_cleanup(struct oxygen *chip)
|
||||
{
|
||||
u8 param = 0;
|
||||
|
||||
hdmi_write_command(chip, 0x74, 1, ¶m);
|
||||
}
|
||||
|
||||
void xonar_hdmi_resume(struct oxygen *chip, struct xonar_hdmi *hdmi)
|
||||
{
|
||||
xonar_hdmi_init_commands(chip, hdmi);
|
||||
}
|
||||
|
||||
void xonar_hdmi_pcm_hardware_filter(unsigned int channel,
|
||||
struct snd_pcm_hardware *hardware)
|
||||
{
|
||||
if (channel == PCM_MULTICH) {
|
||||
hardware->rates = SNDRV_PCM_RATE_44100 |
|
||||
SNDRV_PCM_RATE_48000 |
|
||||
SNDRV_PCM_RATE_96000 |
|
||||
SNDRV_PCM_RATE_192000;
|
||||
hardware->rate_min = 44100;
|
||||
}
|
||||
}
|
||||
|
||||
void xonar_set_hdmi_params(struct oxygen *chip, struct xonar_hdmi *hdmi,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
hdmi->params[0] = 0; /* 1 = non-audio */
|
||||
switch (params_rate(params)) {
|
||||
case 44100:
|
||||
hdmi->params[1] = IEC958_AES3_CON_FS_44100;
|
||||
break;
|
||||
case 48000:
|
||||
hdmi->params[1] = IEC958_AES3_CON_FS_48000;
|
||||
break;
|
||||
default: /* 96000 */
|
||||
hdmi->params[1] = IEC958_AES3_CON_FS_96000;
|
||||
break;
|
||||
case 192000:
|
||||
hdmi->params[1] = IEC958_AES3_CON_FS_192000;
|
||||
break;
|
||||
}
|
||||
hdmi->params[2] = params_channels(params) / 2 - 1;
|
||||
if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)
|
||||
hdmi->params[3] = 0;
|
||||
else
|
||||
hdmi->params[3] = 0xc0;
|
||||
hdmi->params[4] = 1; /* ? */
|
||||
hdmi_write_command(chip, 0x54, 5, hdmi->params);
|
||||
}
|
||||
|
||||
void xonar_hdmi_uart_input(struct oxygen *chip)
|
||||
{
|
||||
if (chip->uart_input_count >= 2 &&
|
||||
chip->uart_input[chip->uart_input_count - 2] == 'O' &&
|
||||
chip->uart_input[chip->uart_input_count - 1] == 'K') {
|
||||
dev_dbg(chip->card->dev, "message from HDMI chip received:\n");
|
||||
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
|
||||
chip->uart_input, chip->uart_input_count);
|
||||
chip->uart_input_count = 0;
|
||||
}
|
||||
}
|
134
sound/pci/oxygen/xonar_lib.c
Normal file
134
sound/pci/oxygen/xonar_lib.c
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* helper functions for Asus Xonar cards
|
||||
*
|
||||
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
|
||||
*
|
||||
*
|
||||
* This driver is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2.
|
||||
*
|
||||
* This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include "xonar.h"
|
||||
|
||||
|
||||
#define GPIO_CS53x1_M_MASK 0x000c
|
||||
#define GPIO_CS53x1_M_SINGLE 0x0000
|
||||
#define GPIO_CS53x1_M_DOUBLE 0x0004
|
||||
#define GPIO_CS53x1_M_QUAD 0x0008
|
||||
|
||||
|
||||
void xonar_enable_output(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_generic *data = chip->model_data;
|
||||
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, data->output_enable_bit);
|
||||
msleep(data->anti_pop_delay);
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
|
||||
}
|
||||
|
||||
void xonar_disable_output(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_generic *data = chip->model_data;
|
||||
|
||||
oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
|
||||
}
|
||||
|
||||
static void xonar_ext_power_gpio_changed(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_generic *data = chip->model_data;
|
||||
u8 has_power;
|
||||
|
||||
has_power = !!(oxygen_read8(chip, data->ext_power_reg)
|
||||
& data->ext_power_bit);
|
||||
if (has_power != data->has_power) {
|
||||
data->has_power = has_power;
|
||||
if (has_power) {
|
||||
dev_notice(chip->card->dev, "power restored\n");
|
||||
} else {
|
||||
dev_crit(chip->card->dev,
|
||||
"Hey! Don't unplug the power cable!\n");
|
||||
/* TODO: stop PCMs */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void xonar_init_ext_power(struct oxygen *chip)
|
||||
{
|
||||
struct xonar_generic *data = chip->model_data;
|
||||
|
||||
oxygen_set_bits8(chip, data->ext_power_int_reg,
|
||||
data->ext_power_bit);
|
||||
chip->interrupt_mask |= OXYGEN_INT_GPIO;
|
||||
chip->model.gpio_changed = xonar_ext_power_gpio_changed;
|
||||
data->has_power = !!(oxygen_read8(chip, data->ext_power_reg)
|
||||
& data->ext_power_bit);
|
||||
}
|
||||
|
||||
void xonar_init_cs53x1(struct oxygen *chip)
|
||||
{
|
||||
oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CS53x1_M_MASK);
|
||||
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
|
||||
GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK);
|
||||
}
|
||||
|
||||
void xonar_set_cs53x1_params(struct oxygen *chip,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
unsigned int value;
|
||||
|
||||
if (params_rate(params) <= 54000)
|
||||
value = GPIO_CS53x1_M_SINGLE;
|
||||
else if (params_rate(params) <= 108000)
|
||||
value = GPIO_CS53x1_M_DOUBLE;
|
||||
else
|
||||
value = GPIO_CS53x1_M_QUAD;
|
||||
oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
|
||||
value, GPIO_CS53x1_M_MASK);
|
||||
}
|
||||
|
||||
int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
u16 bit = ctl->private_value;
|
||||
bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;
|
||||
|
||||
value->value.integer.value[0] =
|
||||
!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit) ^ invert;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
|
||||
struct snd_ctl_elem_value *value)
|
||||
{
|
||||
struct oxygen *chip = ctl->private_data;
|
||||
u16 bit = ctl->private_value;
|
||||
bool invert = ctl->private_value & XONAR_GPIO_BIT_INVERT;
|
||||
u16 old_bits, new_bits;
|
||||
int changed;
|
||||
|
||||
spin_lock_irq(&chip->reg_lock);
|
||||
old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
|
||||
if (!!value->value.integer.value[0] ^ invert)
|
||||
new_bits = old_bits | bit;
|
||||
else
|
||||
new_bits = old_bits & ~bit;
|
||||
changed = new_bits != old_bits;
|
||||
if (changed)
|
||||
oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits);
|
||||
spin_unlock_irq(&chip->reg_lock);
|
||||
return changed;
|
||||
}
|
1299
sound/pci/oxygen/xonar_pcm179x.c
Normal file
1299
sound/pci/oxygen/xonar_pcm179x.c
Normal file
File diff suppressed because it is too large
Load diff
1342
sound/pci/oxygen/xonar_wm87x6.c
Normal file
1342
sound/pci/oxygen/xonar_wm87x6.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue