2007-12-23 21:50:57 +03:00
/*
2009-03-09 11:12:55 +03:00
* C - Media CMI8788 driver for C - Media ' s reference design and similar models
2007-12-23 21:50:57 +03:00
*
* 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
*/
/*
2009-09-28 13:17:36 +04:00
* CMI8788 :
*
2010-12-02 13:42:48 +03:00
* SPI 0 - > 1 st AK4396 ( front )
* SPI 1 - > 2 nd AK4396 ( surround )
* SPI 2 - > 3 rd AK4396 ( center / LFE )
* SPI 3 - > WM8785
* SPI 4 - > 4 th AK4396 ( back )
2007-12-23 21:50:57 +03:00
*
2010-12-02 13:42:48 +03:00
* GPIO 0 - > DFS0 of AK5385
* GPIO 1 - > DFS1 of AK5385
2011-01-10 18:35:38 +03:00
*
* X - Meridian models :
* GPIO 4 - > enable extension S / PDIF input
* GPIO 6 - > enable on - board S / PDIF input
*
* Claro models :
2011-01-11 12:35:29 +03:00
* GPIO 6 - > S / PDIF from optical ( 0 ) or coaxial ( 1 ) input
2011-01-10 18:35:38 +03:00
* GPIO 8 - > enable headphone amplifier
2009-09-28 13:17:36 +04:00
*
* CM9780 :
*
2010-12-02 13:42:48 +03:00
* 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
2007-12-23 21:50:57 +03:00
*/
2008-08-29 15:08:34 +04:00
# include <linux/delay.h>
2008-02-22 20:40:56 +03:00
# include <linux/mutex.h>
2007-12-23 21:50:57 +03:00
# include <linux/pci.h>
2011-07-15 20:38:28 +04:00
# include <linux/module.h>
2008-02-22 20:40:56 +03:00
# include <sound/ac97_codec.h>
2008-01-16 10:32:08 +03:00
# include <sound/control.h>
2007-12-23 21:50:57 +03:00
# include <sound/core.h>
2010-12-02 13:41:10 +03:00
# include <sound/info.h>
2007-12-23 21:50:57 +03:00
# include <sound/initval.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include <sound/tlv.h>
# include "oxygen.h"
2011-01-10 18:20:29 +03:00
# include "xonar_dg.h"
2008-01-25 10:41:52 +03:00
# include "ak4396.h"
2008-03-19 10:14:01 +03:00
# include "wm8785.h"
2007-12-23 21:50:57 +03:00
MODULE_AUTHOR ( " Clemens Ladisch <clemens@ladisch.de> " ) ;
MODULE_DESCRIPTION ( " C-Media CMI8788 driver " ) ;
2008-05-13 11:18:27 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;
2011-01-10 18:20:29 +03:00
MODULE_SUPPORTED_DEVICE ( " {{C-Media,CMI8786} "
" ,{C-Media,CMI8787} "
" ,{C-Media,CMI8788}} " ) ;
2007-12-23 21:50:57 +03:00
static int index [ SNDRV_CARDS ] = SNDRV_DEFAULT_IDX ;
static char * id [ SNDRV_CARDS ] = SNDRV_DEFAULT_STR ;
2011-12-15 07:19:36 +04:00
static bool enable [ SNDRV_CARDS ] = SNDRV_DEFAULT_ENABLE_PNP ;
2007-12-23 21:50:57 +03:00
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 " ) ;
2008-09-22 10:57:24 +04:00
enum {
2010-11-03 13:36:33 +03:00
MODEL_CMEDIA_REF ,
MODEL_MERIDIAN ,
2011-01-10 18:37:19 +03:00
MODEL_MERIDIAN_2G ,
2010-11-03 13:36:33 +03:00
MODEL_CLARO ,
MODEL_CLARO_HALO ,
2010-11-03 14:26:35 +03:00
MODEL_FANTASIA ,
2011-01-10 18:37:19 +03:00
MODEL_SERENADE ,
2010-11-03 14:26:35 +03:00
MODEL_2CH_OUTPUT ,
2011-01-10 18:37:19 +03:00
MODEL_HG2PCI ,
2011-01-10 18:20:29 +03:00
MODEL_XONAR_DG ,
2012-05-01 19:40:30 +04:00
MODEL_XONAR_DGX ,
2008-09-22 10:57:24 +04:00
} ;
2010-02-06 01:21:03 +03:00
static DEFINE_PCI_DEVICE_TABLE ( oxygen_ids ) = {
2010-11-03 13:36:33 +03:00
/* C-Media's reference design */
2008-09-22 10:57:24 +04:00
{ OXYGEN_PCI_SUBID ( 0x10b0 , 0x0216 ) , . driver_data = MODEL_CMEDIA_REF } ,
2011-01-10 18:16:32 +03:00
{ OXYGEN_PCI_SUBID ( 0x10b0 , 0x0217 ) , . driver_data = MODEL_CMEDIA_REF } ,
2008-09-22 10:57:24 +04:00
{ 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 } ,
2010-11-03 13:36:33 +03:00
{ OXYGEN_PCI_SUBID ( 0x1a58 , 0x0910 ) , . driver_data = MODEL_CMEDIA_REF } ,
2011-01-10 18:20:29 +03:00
/* Asus Xonar DG */
{ OXYGEN_PCI_SUBID ( 0x1043 , 0x8467 ) , . driver_data = MODEL_XONAR_DG } ,
2012-05-01 19:40:30 +04:00
/* Asus Xonar DGX */
{ OXYGEN_PCI_SUBID ( 0x1043 , 0x8521 ) , . driver_data = MODEL_XONAR_DGX } ,
2011-01-10 18:16:32 +03:00
/* PCI 2.0 HD Audio */
{ OXYGEN_PCI_SUBID ( 0x13f6 , 0x8782 ) , . driver_data = MODEL_2CH_OUTPUT } ,
2010-11-03 13:36:33 +03:00
/* Kuroutoshikou CMI8787-HG2PCI */
2011-01-10 18:37:19 +03:00
{ OXYGEN_PCI_SUBID ( 0x13f6 , 0xffff ) , . driver_data = MODEL_HG2PCI } ,
2010-11-03 13:36:33 +03:00
/* TempoTec HiFier Fantasia */
2010-11-03 14:26:35 +03:00
{ OXYGEN_PCI_SUBID ( 0x14c3 , 0x1710 ) , . driver_data = MODEL_FANTASIA } ,
/* TempoTec HiFier Serenade */
2011-01-10 18:37:19 +03:00
{ OXYGEN_PCI_SUBID ( 0x14c3 , 0x1711 ) , . driver_data = MODEL_SERENADE } ,
2010-11-03 13:36:33 +03:00
/* AuzenTech X-Meridian */
2008-09-22 10:57:24 +04:00
{ OXYGEN_PCI_SUBID ( 0x415a , 0x5431 ) , . driver_data = MODEL_MERIDIAN } ,
2011-01-10 18:17:26 +03:00
/* AuzenTech X-Meridian 2G */
2011-01-10 18:37:19 +03:00
{ OXYGEN_PCI_SUBID ( 0x5431 , 0x017a ) , . driver_data = MODEL_MERIDIAN_2G } ,
2010-11-03 13:36:33 +03:00
/* HT-Omega Claro */
2009-03-09 11:12:55 +03:00
{ OXYGEN_PCI_SUBID ( 0x7284 , 0x9761 ) , . driver_data = MODEL_CLARO } ,
2010-11-03 13:36:33 +03:00
/* HT-Omega Claro halo */
2009-03-09 11:12:55 +03:00
{ OXYGEN_PCI_SUBID ( 0x7284 , 0x9781 ) , . driver_data = MODEL_CLARO_HALO } ,
2007-12-23 21:50:57 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( pci , oxygen_ids ) ;
2008-01-21 10:50:19 +03:00
# define GPIO_AK5385_DFS_MASK 0x0003
# define GPIO_AK5385_DFS_NORMAL 0x0000
# define GPIO_AK5385_DFS_DOUBLE 0x0001
# define GPIO_AK5385_DFS_QUAD 0x0002
2011-01-10 18:35:38 +03:00
# define GPIO_MERIDIAN_DIG_MASK 0x0050
# define GPIO_MERIDIAN_DIG_EXT 0x0010
# define GPIO_MERIDIAN_DIG_BOARD 0x0040
2011-01-11 12:35:29 +03:00
# define GPIO_CLARO_DIG_COAX 0x0040
2009-03-09 11:12:55 +03:00
# define GPIO_CLARO_HP 0x0100
2008-01-21 10:51:55 +03:00
struct generic_data {
2010-11-02 19:08:37 +03:00
unsigned int dacs ;
2009-09-28 13:18:45 +04:00
u8 ak4396_regs [ 4 ] [ 5 ] ;
2009-09-28 13:21:51 +04:00
u16 wm8785_regs [ 3 ] ;
2008-01-21 10:51:55 +03:00
} ;
2007-12-23 21:50:57 +03:00
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 ] = {
2008-01-14 10:55:03 +03:00
0 , 1 , 2 , 4
2007-12-23 21:50:57 +03:00
} ;
2009-09-28 13:18:45 +04:00
struct generic_data * data = chip - > model_data ;
2008-01-18 11:17:53 +03:00
oxygen_write_spi ( chip , OXYGEN_SPI_TRIGGER |
2007-12-23 21:50:57 +03:00
OXYGEN_SPI_DATA_LENGTH_2 |
2008-01-30 10:38:30 +03:00
OXYGEN_SPI_CLOCK_160 |
2007-12-23 21:50:57 +03:00
( codec_spi_map [ codec ] < < OXYGEN_SPI_CODEC_SHIFT ) |
2008-01-18 11:17:53 +03:00
OXYGEN_SPI_CEN_LATCH_CLOCK_HI ,
2007-12-23 21:50:57 +03:00
AK4396_WRITE | ( reg < < 8 ) | value ) ;
2009-09-28 13:18:45 +04:00
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 ) ;
2007-12-23 21:50:57 +03:00
}
static void wm8785_write ( struct oxygen * chip , u8 reg , unsigned int value )
{
2008-05-13 11:20:51 +04:00
struct generic_data * data = chip - > model_data ;
2008-01-18 11:17:53 +03:00
oxygen_write_spi ( chip , OXYGEN_SPI_TRIGGER |
2007-12-23 21:50:57 +03:00
OXYGEN_SPI_DATA_LENGTH_2 |
2008-01-30 10:38:30 +03:00
OXYGEN_SPI_CLOCK_160 |
2008-01-18 11:17:53 +03:00
( 3 < < OXYGEN_SPI_CODEC_SHIFT ) |
OXYGEN_SPI_CEN_LATCH_CLOCK_LO ,
2007-12-23 21:50:57 +03:00
( reg < < 9 ) | value ) ;
2009-09-28 13:18:45 +04:00
if ( reg < ARRAY_SIZE ( data - > wm8785_regs ) )
data - > wm8785_regs [ reg ] = value ;
2008-05-13 11:21:48 +04:00
}
2008-05-13 11:22:43 +04:00
static void ak4396_registers_init ( struct oxygen * chip )
2007-12-23 21:50:57 +03:00
{
2008-01-21 10:51:55 +03:00
struct generic_data * data = chip - > model_data ;
2007-12-23 21:50:57 +03:00
unsigned int i ;
2010-11-02 19:08:37 +03:00
for ( i = 0 ; i < data - > dacs ; + + i ) {
2009-09-28 13:18:45 +04:00
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 ] ) ;
2007-12-23 21:50:57 +03:00
}
2008-05-13 11:22:43 +04:00
}
static void ak4396_init ( struct oxygen * chip )
{
struct generic_data * data = chip - > model_data ;
2011-01-10 17:59:38 +03:00
data - > dacs = chip - > model . dac_channels_pcm / 2 ;
2009-09-28 13:18:45 +04:00
data - > ak4396_regs [ 0 ] [ AK4396_CONTROL_2 ] =
AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL ;
2008-05-13 11:22:43 +04:00
ak4396_registers_init ( chip ) ;
2007-12-23 21:50:57 +03:00
snd_component_add ( chip - > card , " AK4396 " ) ;
}
static void ak5385_init ( struct oxygen * chip )
{
2008-01-21 10:50:19 +03:00
oxygen_set_bits16 ( chip , OXYGEN_GPIO_CONTROL , GPIO_AK5385_DFS_MASK ) ;
oxygen_clear_bits16 ( chip , OXYGEN_GPIO_DATA , GPIO_AK5385_DFS_MASK ) ;
2007-12-23 21:50:57 +03:00
snd_component_add ( chip - > card , " AK5385 " ) ;
}
2008-05-13 11:22:43 +04:00
static void wm8785_registers_init ( struct oxygen * chip )
2007-12-23 21:50:57 +03:00
{
2008-05-13 11:20:51 +04:00
struct generic_data * data = chip - > model_data ;
2008-01-21 10:50:19 +03:00
wm8785_write ( chip , WM8785_R7 , 0 ) ;
2009-09-28 13:18:45 +04:00
wm8785_write ( chip , WM8785_R0 , data - > wm8785_regs [ 0 ] ) ;
2009-09-28 13:21:51 +04:00
wm8785_write ( chip , WM8785_R2 , data - > wm8785_regs [ 2 ] ) ;
2008-05-13 11:22:43 +04:00
}
2008-05-13 11:20:51 +04:00
2008-05-13 11:22:43 +04:00
static void wm8785_init ( struct oxygen * chip )
{
struct generic_data * data = chip - > model_data ;
2009-09-28 13:18:45 +04:00
data - > wm8785_regs [ 0 ] =
WM8785_MCR_SLAVE | WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST ;
2009-09-28 13:21:51 +04:00
data - > wm8785_regs [ 2 ] = WM8785_HPFR | WM8785_HPFL ;
2008-05-13 11:22:43 +04:00
wm8785_registers_init ( chip ) ;
2007-12-23 21:50:57 +03:00
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 )
{
2011-01-10 18:35:38 +03:00
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 ) ;
2007-12-23 21:50:57 +03:00
ak4396_init ( chip ) ;
ak5385_init ( chip ) ;
}
2009-03-09 11:12:55 +03:00
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 )
{
2011-01-11 12:35:29 +03:00
oxygen_set_bits16 ( chip , OXYGEN_GPIO_CONTROL , GPIO_CLARO_DIG_COAX ) ;
oxygen_clear_bits16 ( chip , OXYGEN_GPIO_DATA , GPIO_CLARO_DIG_COAX ) ;
2009-03-09 11:12:55 +03:00
ak4396_init ( chip ) ;
wm8785_init ( chip ) ;
claro_enable_hp ( chip ) ;
}
static void claro_halo_init ( struct oxygen * chip )
2009-02-20 11:31:14 +03:00
{
2011-01-11 12:35:29 +03:00
oxygen_set_bits16 ( chip , OXYGEN_GPIO_CONTROL , GPIO_CLARO_DIG_COAX ) ;
oxygen_clear_bits16 ( chip , OXYGEN_GPIO_DATA , GPIO_CLARO_DIG_COAX ) ;
2009-02-20 11:31:14 +03:00
ak4396_init ( chip ) ;
ak5385_init ( chip ) ;
2009-03-09 11:12:55 +03:00
claro_enable_hp ( chip ) ;
2009-02-20 11:31:14 +03:00
}
2010-11-03 14:26:35 +03:00
static void fantasia_init ( struct oxygen * chip )
2010-11-02 19:08:37 +03:00
{
ak4396_init ( chip ) ;
snd_component_add ( chip - > card , " CS5340 " ) ;
}
2010-11-03 14:26:35 +03:00
static void stereo_output_init ( struct oxygen * chip )
2010-11-02 19:18:23 +03:00
{
ak4396_init ( chip ) ;
}
2007-12-23 21:50:57 +03:00
static void generic_cleanup ( struct oxygen * chip )
{
}
2009-03-09 11:12:55 +03:00
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 ) ;
}
2008-05-13 11:24:39 +04:00
static void generic_resume ( struct oxygen * chip )
{
ak4396_registers_init ( chip ) ;
wm8785_registers_init ( chip ) ;
}
2008-09-22 11:05:29 +04:00
static void meridian_resume ( struct oxygen * chip )
{
ak4396_registers_init ( chip ) ;
}
2009-03-09 11:12:55 +03:00
static void claro_resume ( struct oxygen * chip )
2009-02-20 11:31:14 +03:00
{
ak4396_registers_init ( chip ) ;
2009-03-09 11:12:55 +03:00
claro_enable_hp ( chip ) ;
2009-02-20 11:31:14 +03:00
}
2010-11-02 19:08:37 +03:00
static void stereo_resume ( struct oxygen * chip )
{
ak4396_registers_init ( chip ) ;
}
2007-12-23 21:50:57 +03:00
static void set_ak4396_params ( struct oxygen * chip ,
struct snd_pcm_hw_params * params )
{
2008-01-21 10:51:55 +03:00
struct generic_data * data = chip - > model_data ;
2007-12-23 21:50:57 +03:00
unsigned int i ;
u8 value ;
2009-09-28 13:18:45 +04:00
value = data - > ak4396_regs [ 0 ] [ AK4396_CONTROL_2 ] & ~ AK4396_DFS_MASK ;
2007-12-23 21:50:57 +03:00
if ( params_rate ( params ) < = 54000 )
value | = AK4396_DFS_NORMAL ;
2008-01-28 10:32:58 +03:00
else if ( params_rate ( params ) < = 108000 )
2007-12-23 21:50:57 +03:00
value | = AK4396_DFS_DOUBLE ;
else
value | = AK4396_DFS_QUAD ;
2008-08-29 15:08:34 +04:00
msleep ( 1 ) ; /* wait for the new MCLK to become stable */
2009-09-28 13:18:45 +04:00
if ( value ! = data - > ak4396_regs [ 0 ] [ AK4396_CONTROL_2 ] ) {
2010-11-02 19:08:37 +03:00
for ( i = 0 ; i < data - > dacs ; + + i ) {
2009-09-28 13:18:45 +04:00
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 )
{
2010-11-02 19:08:37 +03:00
struct generic_data * data = chip - > model_data ;
2009-09-28 13:18:45 +04:00
unsigned int i ;
2010-11-02 19:08:37 +03:00
for ( i = 0 ; i < data - > dacs ; + + i ) {
2009-09-28 13:18:45 +04:00
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 ] ) ;
2007-12-23 21:50:57 +03:00
}
}
static void update_ak4396_mute ( struct oxygen * chip )
{
2008-01-21 10:51:55 +03:00
struct generic_data * data = chip - > model_data ;
2007-12-23 21:50:57 +03:00
unsigned int i ;
u8 value ;
2009-09-28 13:18:45 +04:00
value = data - > ak4396_regs [ 0 ] [ AK4396_CONTROL_2 ] & ~ AK4396_SMUTE ;
2007-12-23 21:50:57 +03:00
if ( chip - > dac_mute )
value | = AK4396_SMUTE ;
2010-11-02 19:08:37 +03:00
for ( i = 0 ; i < data - > dacs ; + + i )
2009-09-28 13:18:45 +04:00
ak4396_write_cached ( chip , i , AK4396_CONTROL_2 , value ) ;
2007-12-23 21:50:57 +03:00
}
static void set_wm8785_params ( struct oxygen * chip ,
struct snd_pcm_hw_params * params )
{
2009-09-28 13:18:45 +04:00
struct generic_data * data = chip - > model_data ;
2007-12-23 21:50:57 +03:00
unsigned int value ;
2008-01-21 10:50:19 +03:00
value = WM8785_MCR_SLAVE | WM8785_FORMAT_LJUST ;
2008-01-21 10:50:51 +03:00
if ( params_rate ( params ) < = 48000 )
value | = WM8785_OSR_SINGLE ;
else if ( params_rate ( params ) < = 96000 )
2007-12-23 21:50:57 +03:00
value | = WM8785_OSR_DOUBLE ;
else
2008-01-21 10:50:51 +03:00
value | = WM8785_OSR_QUAD ;
2009-09-28 13:18:45 +04:00
if ( value ! = data - > wm8785_regs [ 0 ] ) {
wm8785_write ( chip , WM8785_R7 , 0 ) ;
wm8785_write ( chip , WM8785_R0 , value ) ;
2009-09-28 13:21:51 +04:00
wm8785_write ( chip , WM8785_R2 , data - > wm8785_regs [ 2 ] ) ;
2009-09-28 13:18:45 +04:00
}
2007-12-23 21:50:57 +03:00
}
static void set_ak5385_params ( struct oxygen * chip ,
struct snd_pcm_hw_params * params )
{
unsigned int value ;
if ( params_rate ( params ) < = 54000 )
2008-01-21 10:50:19 +03:00
value = GPIO_AK5385_DFS_NORMAL ;
2007-12-23 21:50:57 +03:00
else if ( params_rate ( params ) < = 108000 )
2008-01-21 10:50:19 +03:00
value = GPIO_AK5385_DFS_DOUBLE ;
2007-12-23 21:50:57 +03:00
else
2008-01-21 10:50:19 +03:00
value = GPIO_AK5385_DFS_QUAD ;
oxygen_write16_masked ( chip , OXYGEN_GPIO_DATA ,
value , GPIO_AK5385_DFS_MASK ) ;
2007-12-23 21:50:57 +03:00
}
2010-11-02 19:08:37 +03:00
static void set_no_params ( struct oxygen * chip , struct snd_pcm_hw_params * params )
{
}
2009-09-28 13:21:21 +04:00
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 "
} ;
2011-01-10 18:25:44 +03:00
return snd_ctl_enum_info ( info , 1 , 2 , names ) ;
2009-09-28 13:21:21 +04:00
}
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 ) {
2010-11-02 19:08:37 +03:00
for ( i = 0 ; i < data - > dacs ; + + i )
2009-09-28 13:21:21 +04:00
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 ,
} ;
2009-09-28 13:21:51 +04:00
static int hpf_info ( struct snd_kcontrol * ctl , struct snd_ctl_elem_info * info )
{
static const char * const names [ 2 ] = {
" None " , " High-pass Filter "
} ;
2011-01-10 18:25:44 +03:00
return snd_ctl_enum_info ( info , 1 , 2 , names ) ;
2009-09-28 13:21:51 +04:00
}
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 ,
} ;
2011-01-11 12:35:29 +03:00
static int meridian_dig_source_info ( struct snd_kcontrol * ctl ,
struct snd_ctl_elem_info * info )
2011-01-10 18:35:38 +03:00
{
static const char * const names [ 2 ] = { " On-board " , " Extension " } ;
return snd_ctl_enum_info ( info , 1 , 2 , names ) ;
}
2011-01-11 12:35:29 +03:00
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 )
2011-01-10 18:35:38 +03:00
{
struct oxygen * chip = ctl - > private_data ;
value - > value . enumerated . item [ 0 ] =
! ! ( oxygen_read16 ( chip , OXYGEN_GPIO_DATA ) &
GPIO_MERIDIAN_DIG_EXT ) ;
return 0 ;
}
2011-01-11 12:35:29 +03:00
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 )
2011-01-10 18:35:38 +03:00
{
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 ;
}
2011-01-11 12:35:29 +03:00
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 ;
}
2011-01-10 18:35:38 +03:00
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 ,
} ;
2011-01-11 12:35:29 +03:00
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 ,
} ;
2009-09-28 13:21:21 +04:00
static int generic_mixer_init ( struct oxygen * chip )
{
return snd_ctl_add ( chip - > card , snd_ctl_new1 ( & rolloff_control , chip ) ) ;
}
2009-09-28 13:21:51 +04:00
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 ;
}
2011-01-10 18:35:38 +03:00
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 ;
}
2011-01-11 12:35:29 +03:00
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 ;
}
2010-12-02 13:41:10 +03:00
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 , " \n AK4396 %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 , " \n WM8785: " ) ;
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 ) ;
}
2007-12-23 21:50:57 +03:00
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 ,
2009-09-28 13:21:51 +04:00
. mixer_init = generic_wm8785_mixer_init ,
2007-12-23 21:50:57 +03:00
. cleanup = generic_cleanup ,
2008-05-13 11:24:39 +04:00
. resume = generic_resume ,
2007-12-23 21:50:57 +03:00
. set_dac_params = set_ak4396_params ,
. set_adc_params = set_wm8785_params ,
. update_dac_volume = update_ak4396_volume ,
. update_dac_mute = update_ak4396_mute ,
2010-12-02 13:41:10 +03:00
. dump_registers = dump_oxygen_registers ,
2008-04-16 11:15:45 +04:00
. dac_tlv = ak4396_db_scale ,
2008-01-21 10:51:55 +03:00
. model_data_size = sizeof ( struct generic_data ) ,
2008-09-22 11:02:08 +04:00
. device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
PLAYBACK_2_TO_AC97_1 |
CAPTURE_0_FROM_I2S_1 |
CAPTURE_1_FROM_SPDIF |
2010-10-04 15:21:52 +04:00
CAPTURE_2_FROM_AC97_1 |
AC97_CD_INPUT ,
2011-01-10 17:59:38 +03:00
. dac_channels_pcm = 8 ,
. dac_channels_mixer = 8 ,
2008-04-16 11:13:36 +04:00
. dac_volume_min = 0 ,
. dac_volume_max = 255 ,
2008-03-19 10:20:13 +03:00
. function_flags = OXYGEN_FUNCTION_SPI |
OXYGEN_FUNCTION_ENABLE_SPI_4_5 ,
2011-01-10 18:16:08 +03:00
. dac_mclks = OXYGEN_MCLKS ( 256 , 128 , 128 ) ,
2011-01-10 18:14:52 +03:00
. adc_mclks = OXYGEN_MCLKS ( 256 , 256 , 128 ) ,
2008-01-17 11:05:09 +03:00
. dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST ,
. adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST ,
2007-12-23 21:50:57 +03:00
} ;
2012-12-06 21:35:20 +04:00
static int get_oxygen_model ( struct oxygen * chip ,
const struct pci_device_id * id )
2009-02-19 10:42:44 +03:00
{
2011-01-10 18:37:19 +03:00
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 " ,
} ;
2009-02-19 10:42:44 +03:00
chip - > model = model_generic ;
switch ( id - > driver_data ) {
case MODEL_MERIDIAN :
2011-01-10 18:37:19 +03:00
case MODEL_MERIDIAN_2G :
2009-02-19 10:42:44 +03:00
chip - > model . init = meridian_init ;
2011-01-10 18:35:38 +03:00
chip - > model . mixer_init = meridian_mixer_init ;
2009-02-19 10:42:44 +03:00
chip - > model . resume = meridian_resume ;
chip - > model . set_adc_params = set_ak5385_params ;
2010-12-02 13:41:10 +03:00
chip - > model . dump_registers = dump_ak4396_registers ;
2009-02-19 10:42:44 +03:00
chip - > model . device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2 |
CAPTURE_1_FROM_SPDIF ;
2011-01-11 12:33:40 +03:00
if ( id - > driver_data = = MODEL_MERIDIAN )
chip - > model . device_config | = AC97_CD_INPUT ;
2009-02-19 10:42:44 +03:00
break ;
2009-03-09 11:12:55 +03:00
case MODEL_CLARO :
chip - > model . init = claro_init ;
2011-01-11 12:35:29 +03:00
chip - > model . mixer_init = claro_mixer_init ;
2009-03-09 11:12:55 +03:00
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 ;
2011-01-11 12:35:29 +03:00
chip - > model . mixer_init = claro_halo_mixer_init ;
2009-03-09 11:12:55 +03:00
chip - > model . cleanup = claro_cleanup ;
chip - > model . suspend = claro_suspend ;
chip - > model . resume = claro_resume ;
2009-02-20 11:31:14 +03:00
chip - > model . set_adc_params = set_ak5385_params ;
2010-12-02 13:41:10 +03:00
chip - > model . dump_registers = dump_ak4396_registers ;
2010-09-22 13:07:41 +04:00
chip - > model . device_config = PLAYBACK_0_TO_I2S |
PLAYBACK_1_TO_SPDIF |
CAPTURE_0_FROM_I2S_2 |
CAPTURE_1_FROM_SPDIF ;
2009-02-20 11:31:14 +03:00
break ;
2010-11-03 14:26:35 +03:00
case MODEL_FANTASIA :
2011-01-10 18:37:19 +03:00
case MODEL_SERENADE :
2010-11-03 14:26:35 +03:00
case MODEL_2CH_OUTPUT :
2011-01-10 18:37:19 +03:00
case MODEL_HG2PCI :
2010-11-02 19:08:37 +03:00
chip - > model . shortname = " C-Media CMI8787 " ;
chip - > model . chip = " CMI8787 " ;
2010-11-03 14:26:35 +03:00
if ( id - > driver_data = = MODEL_FANTASIA )
chip - > model . init = fantasia_init ;
2010-11-02 19:18:23 +03:00
else
2010-11-03 14:26:35 +03:00
chip - > model . init = stereo_output_init ;
2010-11-02 19:08:37 +03:00
chip - > model . resume = stereo_resume ;
chip - > model . mixer_init = generic_mixer_init ;
chip - > model . set_adc_params = set_no_params ;
2010-12-02 13:41:10 +03:00
chip - > model . dump_registers = dump_ak4396_registers ;
2010-11-02 19:08:37 +03:00
chip - > model . device_config = PLAYBACK_0_TO_I2S |
2010-11-02 19:18:23 +03:00
PLAYBACK_1_TO_SPDIF ;
2011-01-10 18:16:08 +03:00
if ( id - > driver_data = = MODEL_FANTASIA ) {
2010-11-02 19:18:23 +03:00
chip - > model . device_config | = CAPTURE_0_FROM_I2S_1 ;
2011-01-10 18:16:08 +03:00
chip - > model . adc_mclks = OXYGEN_MCLKS ( 256 , 128 , 128 ) ;
}
2011-01-10 17:59:38 +03:00
chip - > model . dac_channels_pcm = 2 ;
chip - > model . dac_channels_mixer = 2 ;
2010-11-02 19:08:37 +03:00
break ;
2011-01-10 18:20:29 +03:00
case MODEL_XONAR_DG :
chip - > model = model_xonar_dg ;
2012-05-01 19:40:30 +04:00
chip - > model . shortname = " Xonar DG " ;
break ;
case MODEL_XONAR_DGX :
chip - > model = model_xonar_dg ;
chip - > model . shortname = " Xonar DGX " ;
2011-01-10 18:20:29 +03:00
break ;
2009-02-19 10:42:44 +03:00
}
if ( id - > driver_data = = MODEL_MERIDIAN | |
2011-01-11 12:33:40 +03:00
id - > driver_data = = MODEL_MERIDIAN_2G | |
2009-03-09 11:12:55 +03:00
id - > driver_data = = MODEL_CLARO_HALO ) {
2009-02-19 10:42:44 +03:00
chip - > model . misc_flags = OXYGEN_MISC_MIDI ;
chip - > model . device_config | = MIDI_OUTPUT | MIDI_INPUT ;
}
2011-01-10 18:37:19 +03:00
if ( id - > driver_data < ARRAY_SIZE ( names ) & & names [ id - > driver_data ] )
chip - > model . shortname = names [ id - > driver_data ] ;
2009-02-19 10:42:44 +03:00
return 0 ;
}
2012-12-06 21:35:20 +04:00
static int generic_oxygen_probe ( struct pci_dev * pci ,
const struct pci_device_id * pci_id )
2007-12-23 21:50:57 +03:00
{
static int dev ;
int err ;
if ( dev > = SNDRV_CARDS )
return - ENODEV ;
if ( ! enable [ dev ] ) {
+ + dev ;
return - ENOENT ;
}
2009-02-19 10:37:13 +03:00
err = oxygen_pci_probe ( pci , index [ dev ] , id [ dev ] , THIS_MODULE ,
2009-02-19 10:42:44 +03:00
oxygen_ids , get_oxygen_model ) ;
2007-12-23 21:50:57 +03:00
if ( err > = 0 )
+ + dev ;
return err ;
}
static struct pci_driver oxygen_driver = {
2011-06-10 18:20:20 +04:00
. name = KBUILD_MODNAME ,
2007-12-23 21:50:57 +03:00
. id_table = oxygen_ids ,
. probe = generic_oxygen_probe ,
2012-12-06 21:35:20 +04:00
. remove = oxygen_pci_remove ,
2012-08-14 20:12:04 +04:00
# ifdef CONFIG_PM_SLEEP
2012-07-02 17:20:37 +04:00
. driver = {
. pm = & oxygen_pci_pm ,
} ,
2008-05-13 11:24:39 +04:00
# endif
2007-12-23 21:50:57 +03:00
} ;
2012-04-24 14:25:00 +04:00
module_pci_driver ( oxygen_driver ) ;