2007-12-23 21:50:57 +03:00
/*
* 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 <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"
2008-01-21 10:50:19 +03:00
# include "cm9780.h"
2007-12-23 21:50:57 +03:00
MODULE_AUTHOR ( " Clemens Ladisch <clemens@ladisch.de> " ) ;
MODULE_DESCRIPTION ( " C-Media CMI8788 helper library " ) ;
2008-05-13 11:18:27 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;
2007-12-23 21:50:57 +03:00
2009-02-19 10:38:55 +03:00
# define DRIVER "oxygen"
2007-12-23 21:50:57 +03:00
2008-09-22 11:04:43 +04:00
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 ) ;
}
2007-12-23 21:50:57 +03:00
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 |
2008-01-18 11:17:53 +03:00
OXYGEN_INT_SPDIF_IN_DETECT |
2008-01-28 10:34:21 +03:00
OXYGEN_INT_GPIO |
OXYGEN_INT_AC97 ) ;
2007-12-23 21:50:57 +03:00
if ( clear ) {
2008-01-18 11:17:53 +03:00
if ( clear & OXYGEN_INT_SPDIF_IN_DETECT )
chip - > interrupt_mask & = ~ OXYGEN_INT_SPDIF_IN_DETECT ;
2007-12-23 21:50:57 +03:00
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 ] ) ;
2008-01-18 11:17:53 +03:00
if ( status & OXYGEN_INT_SPDIF_IN_DETECT ) {
2007-12-23 21:50:57 +03:00
spin_lock ( & chip - > reg_lock ) ;
i = oxygen_read32 ( chip , OXYGEN_SPDIF_CONTROL ) ;
2008-01-21 10:53:30 +03:00
if ( i & ( OXYGEN_SPDIF_SENSE_INT | OXYGEN_SPDIF_LOCK_INT |
OXYGEN_SPDIF_RATE_INT ) ) {
/* write the interrupt bit(s) to clear */
2007-12-23 21:50:57 +03:00
oxygen_write32 ( chip , OXYGEN_SPDIF_CONTROL , i ) ;
schedule_work ( & chip - > spdif_input_bits_work ) ;
}
spin_unlock ( & chip - > reg_lock ) ;
}
if ( status & OXYGEN_INT_GPIO )
2008-01-28 10:36:55 +03:00
schedule_work ( & chip - > gpio_work ) ;
2007-12-23 21:50:57 +03:00
2008-09-22 11:04:43 +04:00
if ( status & OXYGEN_INT_MIDI ) {
if ( chip - > midi )
snd_mpu401_uart_interrupt ( 0 , chip - > midi - > private_data ) ;
else
oxygen_read_uart ( chip ) ;
}
2007-12-23 21:50:57 +03:00
2008-01-28 10:34:21 +03:00
if ( status & OXYGEN_INT_AC97 )
wake_up ( & chip - > ac97_waitqueue ) ;
2007-12-23 21:50:57 +03:00
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 ) ;
2008-01-21 10:53:30 +03:00
u32 reg ;
2007-12-23 21:50:57 +03:00
2008-01-21 10:53:30 +03:00
/*
* 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 .
*/
2007-12-23 21:50:57 +03:00
msleep ( 1 ) ;
2008-01-21 10:53:30 +03:00
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 ) ;
2007-12-23 21:50:57 +03:00
spin_unlock_irq ( & chip - > reg_lock ) ;
msleep ( 1 ) ;
2008-01-21 10:53:30 +03:00
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 ) ;
}
2007-12-23 21:50:57 +03:00
}
}
2008-01-21 10:53:30 +03:00
spin_unlock_irq ( & chip - > reg_lock ) ;
2007-12-23 21:50:57 +03:00
2008-01-14 10:56:01 +03:00
if ( chip - > controls [ CONTROL_SPDIF_INPUT_BITS ] ) {
2007-12-23 21:50:57 +03:00
spin_lock_irq ( & chip - > reg_lock ) ;
2008-01-18 11:17:53 +03:00
chip - > interrupt_mask | = OXYGEN_INT_SPDIF_IN_DETECT ;
2007-12-23 21:50:57 +03:00
oxygen_write16 ( chip , OXYGEN_INTERRUPT_MASK ,
chip - > interrupt_mask ) ;
spin_unlock_irq ( & chip - > reg_lock ) ;
2008-01-21 10:53:30 +03:00
/*
* We don ' t actually know that any channel status bits have
* changed , but let ' s send a notification just to be sure .
*/
2007-12-23 21:50:57 +03:00
snd_ctl_notify ( chip - > card , SNDRV_CTL_EVENT_MASK_VALUE ,
2008-01-14 10:56:01 +03:00
& chip - > controls [ CONTROL_SPDIF_INPUT_BITS ] - > id ) ;
2007-12-23 21:50:57 +03:00
}
}
2008-01-28 10:36:55 +03:00
static void oxygen_gpio_changed ( struct work_struct * work )
{
struct oxygen * chip = container_of ( work , struct oxygen , gpio_work ) ;
2008-09-22 10:55:19 +04:00
if ( chip - > model . gpio_changed )
chip - > model . gpio_changed ( chip ) ;
2008-01-28 10:36:55 +03:00
}
2007-12-23 21:50:57 +03:00
# 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 ;
snd_iprintf ( buffer , " CMI8788 \n \n " ) ;
2008-05-13 11:19:53 +04:00
for ( i = 0 ; i < OXYGEN_IO_SIZE ; i + = 0x10 ) {
2007-12-23 21:50:57 +03:00
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 ;
2008-01-16 10:28:17 +03:00
if ( chip - > has_ac97_0 ) {
snd_iprintf ( buffer , " \n AC97 \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 , " \n AC97 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 " ) ;
}
2007-12-23 21:50:57 +03:00
}
mutex_unlock ( & chip - > mutex ) ;
}
2008-02-22 20:35:22 +03:00
static void oxygen_proc_init ( struct oxygen * chip )
2007-12-23 21:50:57 +03:00
{
struct snd_info_entry * entry ;
if ( ! snd_card_proc_new ( chip - > card , " cmi8788 " , & entry ) )
snd_info_set_text_ops ( entry , chip , oxygen_proc_read ) ;
}
# else
# define oxygen_proc_init(chip)
# endif
2009-02-19 10:42:44 +03:00
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 ) ;
2009-09-02 20:25:39 +04:00
/* use default ID if EEPROM is missing */
if ( subdevice = = 0xffff )
subdevice = 0x8788 ;
2009-02-19 10:42:44 +03:00
/*
* 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 ;
}
2009-02-19 10:44:12 +03:00
static void oxygen_restore_eeprom ( struct oxygen * chip ,
const struct pci_device_id * id )
{
2009-09-28 13:05:18 +04:00
u16 eeprom_id ;
eeprom_id = oxygen_read_eeprom ( chip , 0 ) ;
if ( eeprom_id ! = OXYGEN_EEPROM_ID & &
( eeprom_id ! = 0xffff | | id - > subdevice ! = 0x8788 ) ) {
2009-02-19 10:44:12 +03:00
/*
* 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 ) ;
snd_printk ( KERN_INFO " EEPROM ID restored \n " ) ;
}
}
2009-09-28 13:05:58 +04:00
static void pci_bridge_magic ( void )
{
struct pci_dev * pci = NULL ;
u32 tmp ;
for ( ; ; ) {
/* If there is any Pericom PI7C9X110 PCI-E/PCI bridge ... */
pci = pci_get_device ( 0x12d8 , 0xe110 , pci ) ;
if ( ! pci )
break ;
/*
* . . . configure its secondary internal arbiter to park to
* the secondary port , instead of to the last master .
*/
if ( ! pci_read_config_dword ( pci , 0x40 , & tmp ) ) {
tmp | = 1 ;
pci_write_config_dword ( pci , 0x40 , tmp ) ;
}
/* Why? Try asking C-Media. */
}
}
2008-02-22 20:35:22 +03:00
static void oxygen_init ( struct oxygen * chip )
2007-12-23 21:50:57 +03:00
{
unsigned int i ;
chip - > dac_routing = 1 ;
for ( i = 0 ; i < 8 ; + + i )
2008-09-22 10:55:19 +04:00
chip - > dac_volume [ i ] = chip - > model . dac_volume_min ;
2008-04-16 11:14:30 +04:00
chip - > dac_mute = 1 ;
2007-12-23 21:50:57 +03:00
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 )
chip - > revision = 2 ;
else
chip - > revision = 1 ;
if ( chip - > revision = = 1 )
2008-01-18 11:17:53 +03:00
oxygen_set_bits8 ( chip , OXYGEN_MISC ,
OXYGEN_MISC_PCI_MEM_W_1_CLOCK ) ;
2007-12-23 21:50:57 +03:00
2008-01-16 10:28:17 +03:00
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 ;
2008-01-25 10:39:26 +03:00
oxygen_write8_masked ( chip , OXYGEN_FUNCTION ,
2008-03-19 10:20:13 +03:00
OXYGEN_FUNCTION_RESET_CODEC |
2008-09-22 10:55:19 +04:00
chip - > model . function_flags ,
2008-03-19 10:20:13 +03:00
OXYGEN_FUNCTION_RESET_CODEC |
OXYGEN_FUNCTION_2WIRE_SPI_MASK |
OXYGEN_FUNCTION_ENABLE_SPI_4_5 ) ;
2008-01-25 10:39:26 +03:00
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 ) ;
2008-03-19 10:20:59 +03:00
oxygen_write8_masked ( chip , OXYGEN_MISC ,
2008-09-22 10:55:19 +04:00
chip - > model . misc_flags ,
2008-01-25 10:39:26 +03:00
OXYGEN_MISC_WRITE_PCI_SUBID |
OXYGEN_MISC_REC_C_FROM_SPDIF |
OXYGEN_MISC_REC_B_FROM_AC97 |
2008-03-19 10:20:59 +03:00
OXYGEN_MISC_REC_A_FROM_MULTICH |
OXYGEN_MISC_MIDI ) ;
2008-01-25 10:39:26 +03:00
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 ) ;
2008-01-21 10:44:24 +03:00
oxygen_write16 ( chip , OXYGEN_I2S_MULTICH_FORMAT ,
2008-09-22 10:55:19 +04:00
OXYGEN_RATE_48000 | chip - > model . dac_i2s_format |
2008-03-19 10:17:33 +03:00
OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |
2008-01-21 10:44:24 +03:00
OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64 ) ;
2008-09-22 11:02:08 +04:00
if ( chip - > model . device_config & CAPTURE_0_FROM_I2S_1 )
2008-03-19 10:21:32 +03:00
oxygen_write16 ( chip , OXYGEN_I2S_A_FORMAT ,
2008-09-22 10:55:19 +04:00
OXYGEN_RATE_48000 | chip - > model . adc_i2s_format |
2008-03-19 10:21:32 +03:00
OXYGEN_I2S_MCLK_256 | 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 ) ;
2008-09-22 11:02:08 +04:00
if ( chip - > model . device_config & ( CAPTURE_0_FROM_I2S_2 |
CAPTURE_2_FROM_I2S_2 ) )
2008-03-19 10:21:32 +03:00
oxygen_write16 ( chip , OXYGEN_I2S_B_FORMAT ,
2008-09-22 10:55:19 +04:00
OXYGEN_RATE_48000 | chip - > model . adc_i2s_format |
2008-03-19 10:21:32 +03:00
OXYGEN_I2S_MCLK_256 | 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 ) ;
2008-01-21 10:44:24 +03:00
oxygen_write16 ( chip , OXYGEN_I2S_C_FORMAT ,
2008-03-19 10:21:32 +03:00
OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK ) ;
2008-04-09 11:16:33 +04:00
oxygen_clear_bits32 ( chip , OXYGEN_SPDIF_CONTROL ,
OXYGEN_SPDIF_OUT_ENABLE |
OXYGEN_SPDIF_LOOPBACK ) ;
2008-09-22 11:02:08 +04:00
if ( chip - > model . device_config & CAPTURE_1_FROM_SPDIF )
2008-04-09 11:16:33 +04:00
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 ) ;
2007-12-23 21:50:57 +03:00
oxygen_write32 ( chip , OXYGEN_SPDIF_OUTPUT_BITS , chip - > spdif_bits ) ;
2008-05-13 11:24:39 +04:00
oxygen_write16 ( chip , OXYGEN_2WIRE_BUS_STATUS ,
OXYGEN_2WIRE_LENGTH_8 |
OXYGEN_2WIRE_INTERRUPT_MASK |
OXYGEN_2WIRE_SPEED_STANDARD ) ;
2008-01-25 10:39:26 +03:00
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 ) ;
2008-01-21 10:44:24 +03:00
oxygen_write16 ( chip , OXYGEN_PLAY_ROUTING ,
2008-01-25 10:39:26 +03:00
OXYGEN_PLAY_MULTICH_I2S_DAC |
OXYGEN_PLAY_SPDIF_SPDIF |
2008-01-21 10:44:24 +03:00
( 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 |
2008-01-25 10:39:26 +03:00
OXYGEN_REC_B_ROUTE_I2S_ADC_2 |
2008-01-21 10:44:24 +03:00
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 ) ) ;
2007-12-23 21:50:57 +03:00
2008-04-09 11:16:33 +04:00
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 ) ;
2008-01-25 10:39:26 +03:00
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 {
2008-01-16 10:28:17 +03:00
oxygen_write_ac97 ( chip , 0 , AC97_RESET , 0 ) ;
msleep ( 1 ) ;
2008-01-21 10:50:19 +03:00
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 ) ;
2008-01-16 10:28:17 +03:00
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 ) ;
2008-04-01 12:02:18 +04:00
oxygen_ac97_clear_bits ( chip , 0 , CM9780_GPIO_STATUS ,
CM9780_GPO0 ) ;
2008-01-16 10:28:17 +03:00
/* 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 ) ;
}
2008-01-25 10:39:26 +03:00
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 ) ;
2008-01-28 10:35:20 +03:00
oxygen_write_ac97 ( chip , 1 , AC97_REC_GAIN , 0x0000 ) ;
2008-01-25 10:39:26 +03:00
oxygen_ac97_set_bits ( chip , 1 , 0x6a , 0x0040 ) ;
}
2007-12-23 21:50:57 +03:00
}
static void oxygen_card_free ( struct snd_card * card )
{
struct oxygen * chip = card - > private_data ;
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 ) ;
2008-04-22 15:50:34 +04:00
if ( chip - > irq > = 0 )
2007-12-23 21:50:57 +03:00
free_irq ( chip - > irq , chip ) ;
flush_scheduled_work ( ) ;
2008-09-22 10:55:19 +04:00
chip - > model . cleanup ( chip ) ;
2009-02-19 10:38:25 +03:00
kfree ( chip - > model_data ) ;
2007-12-23 21:50:57 +03:00
mutex_destroy ( & chip - > mutex ) ;
pci_release_regions ( chip - > pci ) ;
pci_disable_device ( chip - > pci ) ;
}
2008-02-22 20:35:22 +03:00
int oxygen_pci_probe ( struct pci_dev * pci , int index , char * id ,
2009-02-19 10:37:13 +03:00
struct module * owner ,
2009-02-19 10:42:44 +03:00
const struct pci_device_id * ids ,
int ( * get_model ) ( struct oxygen * chip ,
const struct pci_device_id * id
)
)
2007-12-23 21:50:57 +03:00
{
struct snd_card * card ;
struct oxygen * chip ;
2009-02-19 10:42:44 +03:00
const struct pci_device_id * pci_id ;
2007-12-23 21:50:57 +03:00
int err ;
2009-02-19 10:38:25 +03:00
err = snd_card_create ( index , id , owner , sizeof ( * chip ) , & card ) ;
2008-12-28 18:44:30 +03:00
if ( err < 0 )
return err ;
2007-12-23 21:50:57 +03:00
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 ) ;
2008-01-28 10:36:55 +03:00
INIT_WORK ( & chip - > gpio_work , oxygen_gpio_changed ) ;
2008-01-28 10:34:21 +03:00
init_waitqueue_head ( & chip - > ac97_waitqueue ) ;
2007-12-23 21:50:57 +03:00
err = pci_enable_device ( pci ) ;
if ( err < 0 )
goto err_card ;
2009-02-19 10:38:55 +03:00
err = pci_request_regions ( pci , DRIVER ) ;
2007-12-23 21:50:57 +03:00
if ( err < 0 ) {
snd_printk ( KERN_ERR " cannot reserve PCI resources \n " ) ;
goto err_pci_enable ;
}
if ( ! ( pci_resource_flags ( pci , 0 ) & IORESOURCE_IO ) | |
2008-05-13 11:19:53 +04:00
pci_resource_len ( pci , 0 ) < OXYGEN_IO_SIZE ) {
2007-12-23 21:50:57 +03:00
snd_printk ( KERN_ERR " invalid PCI I/O range \n " ) ;
err = - ENXIO ;
goto err_pci_regions ;
}
chip - > addr = pci_resource_start ( pci , 0 ) ;
2009-02-19 10:42:44 +03:00
pci_id = oxygen_search_pci_id ( chip , ids ) ;
if ( ! pci_id ) {
err = - ENODEV ;
goto err_pci_regions ;
}
2009-02-19 10:44:12 +03:00
oxygen_restore_eeprom ( chip , pci_id ) ;
2009-02-19 10:42:44 +03:00
err = get_model ( chip , pci_id ) ;
if ( err < 0 )
goto err_pci_regions ;
2009-02-19 10:38:25 +03:00
if ( chip - > model . model_data_size ) {
2009-02-27 11:27:44 +03:00
chip - > model_data = kzalloc ( chip - > model . model_data_size ,
2009-02-19 10:38:25 +03:00
GFP_KERNEL ) ;
if ( ! chip - > model_data ) {
err = - ENOMEM ;
goto err_pci_regions ;
}
}
2007-12-23 21:50:57 +03:00
pci_set_master ( pci ) ;
snd_card_set_dev ( card , & pci - > dev ) ;
card - > private_free = oxygen_card_free ;
2009-09-28 13:05:58 +04:00
pci_bridge_magic ( ) ;
2007-12-23 21:50:57 +03:00
oxygen_init ( chip ) ;
2008-09-22 10:55:19 +04:00
chip - > model . init ( chip ) ;
2007-12-23 21:50:57 +03:00
err = request_irq ( pci - > irq , oxygen_interrupt , IRQF_SHARED ,
2009-02-19 10:38:55 +03:00
DRIVER , chip ) ;
2007-12-23 21:50:57 +03:00
if ( err < 0 ) {
snd_printk ( KERN_ERR " cannot grab interrupt %d \n " , pci - > irq ) ;
goto err_card ;
}
chip - > irq = pci - > irq ;
2008-09-22 10:55:19 +04:00
strcpy ( card - > driver , chip - > model . chip ) ;
strcpy ( card - > shortname , chip - > model . shortname ) ;
2007-12-23 21:50:57 +03:00
sprintf ( card - > longname , " %s (rev %u) at %#lx, irq %i " ,
2008-09-22 10:55:19 +04:00
chip - > model . longname , chip - > revision , chip - > addr , chip - > irq ) ;
strcpy ( card - > mixername , chip - > model . chip ) ;
snd_component_add ( card , chip - > model . chip ) ;
2007-12-23 21:50:57 +03:00
err = oxygen_pcm_init ( chip ) ;
if ( err < 0 )
goto err_card ;
err = oxygen_mixer_init ( chip ) ;
if ( err < 0 )
goto err_card ;
2008-09-22 11:03:42 +04:00
if ( chip - > model . device_config & ( MIDI_OUTPUT | MIDI_INPUT ) ) {
unsigned int info_flags = MPU401_INFO_INTEGRATED ;
if ( chip - > model . device_config & MIDI_OUTPUT )
info_flags | = MPU401_INFO_OUTPUT ;
if ( chip - > model . device_config & MIDI_INPUT )
info_flags | = MPU401_INFO_INPUT ;
2007-12-23 21:50:57 +03:00
err = snd_mpu401_uart_new ( card , 0 , MPU401_HW_CMIPCI ,
chip - > addr + OXYGEN_MPU401 ,
2008-09-22 11:03:42 +04:00
info_flags , 0 , 0 ,
2007-12-23 21:50:57 +03:00
& chip - > midi ) ;
if ( err < 0 )
goto err_card ;
}
oxygen_proc_init ( chip ) ;
spin_lock_irq ( & chip - > reg_lock ) ;
2008-09-22 11:02:08 +04:00
if ( chip - > model . device_config & CAPTURE_1_FROM_SPDIF )
2008-04-09 11:16:33 +04:00
chip - > interrupt_mask | = OXYGEN_INT_SPDIF_IN_DETECT ;
if ( chip - > has_ac97_0 | chip - > has_ac97_1 )
chip - > interrupt_mask | = OXYGEN_INT_AC97 ;
2007-12-23 21:50:57 +03:00
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 ) ;
2008-02-22 20:35:22 +03:00
void oxygen_pci_remove ( struct pci_dev * pci )
2007-12-23 21:50:57 +03:00
{
snd_card_free ( pci_get_drvdata ( pci ) ) ;
pci_set_drvdata ( pci , NULL ) ;
}
EXPORT_SYMBOL ( oxygen_pci_remove ) ;
2008-05-13 11:24:39 +04:00
# ifdef CONFIG_PM
int oxygen_pci_suspend ( struct pci_dev * pci , pm_message_t state )
{
struct snd_card * card = pci_get_drvdata ( pci ) ;
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 ] ) ;
2008-09-22 10:55:19 +04:00
if ( chip - > model . suspend )
chip - > model . suspend ( chip ) ;
2008-05-13 11:24:39 +04:00
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_scheduled_work ( ) ;
chip - > interrupt_mask = saved_interrupt_mask ;
pci_disable_device ( pci ) ;
pci_save_state ( pci ) ;
pci_set_power_state ( pci , pci_choose_state ( pci , state ) ) ;
return 0 ;
}
EXPORT_SYMBOL ( oxygen_pci_suspend ) ;
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 ] ) ;
}
int oxygen_pci_resume ( struct pci_dev * pci )
{
struct snd_card * card = pci_get_drvdata ( pci ) ;
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 ) {
snd_printk ( KERN_ERR " 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 ) ;
2008-09-22 10:55:19 +04:00
if ( chip - > model . resume )
chip - > model . resume ( chip ) ;
2008-05-13 11:24:39 +04:00
oxygen_write16 ( chip , OXYGEN_INTERRUPT_MASK , chip - > interrupt_mask ) ;
snd_power_change_state ( card , SNDRV_CTL_POWER_D0 ) ;
return 0 ;
}
EXPORT_SYMBOL ( oxygen_pci_resume ) ;
# endif /* CONFIG_PM */