2005-04-17 02:20:36 +04:00
/*
* ALSA driver for ICEnsemble ICE1712 ( Envy24 )
*
* Lowlevel functions for M - Audio Revolution 7.1
*
* Copyright ( c ) 2003 Takashi Iwai < tiwai @ suse . de >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
*/
# include <sound/driver.h>
# include <asm/io.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <sound/core.h>
# include "ice1712.h"
# include "envy24ht.h"
# include "revo.h"
2005-11-17 17:00:18 +03:00
static void revo_i2s_mclk_changed ( struct snd_ice1712 * ice )
2005-04-17 02:20:36 +04:00
{
/* assert PRST# to converters; MT05 bit 7 */
outb ( inb ( ICEMT1724 ( ice , AC97_CMD ) ) | 0x80 , ICEMT1724 ( ice , AC97_CMD ) ) ;
mdelay ( 5 ) ;
/* deassert PRST# */
outb ( inb ( ICEMT1724 ( ice , AC97_CMD ) ) & ~ 0x80 , ICEMT1724 ( ice , AC97_CMD ) ) ;
}
/*
* change the rate of envy24HT , AK4355 and AK4381
*/
2005-11-17 17:00:18 +03:00
static void revo_set_rate_val ( struct snd_akm4xxx * ak , unsigned int rate )
2005-04-17 02:20:36 +04:00
{
unsigned char old , tmp , dfs ;
int reg , shift ;
if ( rate = = 0 ) /* no hint - S/PDIF input is master, simply return */
return ;
/* adjust DFS on codecs */
if ( rate > 96000 )
dfs = 2 ;
else if ( rate > 48000 )
dfs = 1 ;
else
dfs = 0 ;
2005-12-05 21:22:34 +03:00
if ( ak - > type = = SND_AK4355 | | ak - > type = = SND_AK4358 ) {
2005-04-17 02:20:36 +04:00
reg = 2 ;
shift = 4 ;
} else {
reg = 1 ;
shift = 3 ;
}
tmp = snd_akm4xxx_get ( ak , 0 , reg ) ;
old = ( tmp > > shift ) & 0x03 ;
if ( old = = dfs )
return ;
/* reset DFS */
snd_akm4xxx_reset ( ak , 1 ) ;
tmp = snd_akm4xxx_get ( ak , 0 , reg ) ;
tmp & = ~ ( 0x03 < < shift ) ;
tmp | = dfs < < shift ;
// snd_akm4xxx_write(ak, 0, reg, tmp);
snd_akm4xxx_set ( ak , 0 , reg , tmp ) ; /* the value is written in reset(0) */
snd_akm4xxx_reset ( ak , 0 ) ;
}
2006-10-04 20:08:43 +04:00
/*
* I2C access to the PT2258 volume controller on GPIO 6 / 7 ( Revolution 5.1 )
*/
static void revo_i2c_start ( struct snd_i2c_bus * bus )
{
struct snd_ice1712 * ice = bus - > private_data ;
snd_ice1712_save_gpio_status ( ice ) ;
}
static void revo_i2c_stop ( struct snd_i2c_bus * bus )
{
struct snd_ice1712 * ice = bus - > private_data ;
snd_ice1712_restore_gpio_status ( ice ) ;
}
static void revo_i2c_direction ( struct snd_i2c_bus * bus , int clock , int data )
{
struct snd_ice1712 * ice = bus - > private_data ;
unsigned int mask , val ;
val = 0 ;
if ( clock )
val | = VT1724_REVO_I2C_CLOCK ; /* write SCL */
if ( data )
val | = VT1724_REVO_I2C_DATA ; /* write SDA */
mask = VT1724_REVO_I2C_CLOCK | VT1724_REVO_I2C_DATA ;
ice - > gpio . direction & = ~ mask ;
ice - > gpio . direction | = val ;
snd_ice1712_gpio_set_dir ( ice , ice - > gpio . direction ) ;
snd_ice1712_gpio_set_mask ( ice , ~ mask ) ;
}
static void revo_i2c_setlines ( struct snd_i2c_bus * bus , int clk , int data )
{
struct snd_ice1712 * ice = bus - > private_data ;
unsigned int val = 0 ;
if ( clk )
val | = VT1724_REVO_I2C_CLOCK ;
if ( data )
val | = VT1724_REVO_I2C_DATA ;
snd_ice1712_gpio_write_bits ( ice ,
VT1724_REVO_I2C_DATA |
VT1724_REVO_I2C_CLOCK , val ) ;
udelay ( 5 ) ;
}
static int revo_i2c_getdata ( struct snd_i2c_bus * bus , int ack )
{
struct snd_ice1712 * ice = bus - > private_data ;
int bit ;
if ( ack )
udelay ( 5 ) ;
bit = snd_ice1712_gpio_read_bits ( ice , VT1724_REVO_I2C_DATA ) ? 1 : 0 ;
return bit ;
}
static struct snd_i2c_bit_ops revo51_bit_ops = {
. start = revo_i2c_start ,
. stop = revo_i2c_stop ,
. direction = revo_i2c_direction ,
. setlines = revo_i2c_setlines ,
. getdata = revo_i2c_getdata ,
} ;
static int revo51_i2c_init ( struct snd_ice1712 * ice ,
struct snd_pt2258 * pt )
{
int err ;
/* create the I2C bus */
err = snd_i2c_bus_create ( ice - > card , " ICE1724 GPIO6 " , NULL , & ice - > i2c ) ;
if ( err < 0 )
return err ;
ice - > i2c - > private_data = ice ;
ice - > i2c - > hw_ops . bit = & revo51_bit_ops ;
/* create the I2C device */
err = snd_i2c_device_create ( ice - > i2c , " PT2258 " , 0x40 ,
& ice - > spec . revo51 . dev ) ;
if ( err < 0 )
return err ;
pt - > card = ice - > card ;
pt - > i2c_bus = ice - > i2c ;
pt - > i2c_dev = ice - > spec . revo51 . dev ;
ice - > spec . revo51 . pt2258 = pt ;
snd_pt2258_reset ( pt ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
/*
* initialize the chips on M - Audio Revolution cards
*/
2006-08-30 18:49:54 +04:00
# define AK_DAC(xname,xch) { .name = xname, .num_channels = xch }
2006-06-27 17:00:55 +04:00
2006-08-30 18:49:54 +04:00
static struct snd_akm4xxx_dac_channel revo71_front [ ] = {
AK_DAC ( " PCM Playback Volume " , 2 )
} ;
static struct snd_akm4xxx_dac_channel revo71_surround [ ] = {
AK_DAC ( " PCM Center Playback Volume " , 1 ) ,
AK_DAC ( " PCM LFE Playback Volume " , 1 ) ,
AK_DAC ( " PCM Side Playback Volume " , 2 ) ,
AK_DAC ( " PCM Rear Playback Volume " , 2 ) ,
} ;
2006-06-27 17:00:55 +04:00
2006-08-30 18:49:54 +04:00
static struct snd_akm4xxx_dac_channel revo51_dac [ ] = {
AK_DAC ( " PCM Playback Volume " , 2 ) ,
AK_DAC ( " PCM Center Playback Volume " , 1 ) ,
AK_DAC ( " PCM LFE Playback Volume " , 1 ) ,
AK_DAC ( " PCM Rear Playback Volume " , 2 ) ,
} ;
2006-06-27 17:00:55 +04:00
2006-10-04 20:04:10 +04:00
static const char * revo51_adc_input_names [ ] = {
" Mic " ,
" Line " ,
" CD " ,
NULL
} ;
2006-08-30 18:49:54 +04:00
static struct snd_akm4xxx_adc_channel revo51_adc [ ] = {
{
. name = " PCM Capture Volume " ,
. switch_name = " PCM Capture Switch " ,
2006-10-04 20:04:10 +04:00
. num_channels = 2 ,
. input_names = revo51_adc_input_names
2006-08-30 18:49:54 +04:00
} ,
} ;
2006-08-08 23:13:42 +04:00
2005-11-17 17:00:18 +03:00
static struct snd_akm4xxx akm_revo_front __devinitdata = {
2005-04-17 02:20:36 +04:00
. type = SND_AK4381 ,
. num_dacs = 2 ,
. ops = {
. set_rate_val = revo_set_rate_val
2006-06-27 17:00:55 +04:00
} ,
2006-08-30 18:49:54 +04:00
. dac_info = revo71_front ,
2005-04-17 02:20:36 +04:00
} ;
static struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = {
. caddr = 1 ,
. cif = 0 ,
. data_mask = VT1724_REVO_CDOUT ,
. clk_mask = VT1724_REVO_CCLK ,
. cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2 ,
. cs_addr = VT1724_REVO_CS0 | VT1724_REVO_CS2 ,
. cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2 ,
. add_flags = VT1724_REVO_CCLK , /* high at init */
. mask_flags = 0 ,
} ;
2005-11-17 17:00:18 +03:00
static struct snd_akm4xxx akm_revo_surround __devinitdata = {
2005-04-17 02:20:36 +04:00
. type = SND_AK4355 ,
. idx_offset = 1 ,
. num_dacs = 6 ,
. ops = {
. set_rate_val = revo_set_rate_val
2006-06-27 17:00:55 +04:00
} ,
2006-08-30 18:49:54 +04:00
. dac_info = revo71_surround ,
2005-04-17 02:20:36 +04:00
} ;
static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = {
. caddr = 3 ,
. cif = 0 ,
. data_mask = VT1724_REVO_CDOUT ,
. clk_mask = VT1724_REVO_CCLK ,
. cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2 ,
. cs_addr = VT1724_REVO_CS0 | VT1724_REVO_CS1 ,
. cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 | VT1724_REVO_CS2 ,
. add_flags = VT1724_REVO_CCLK , /* high at init */
. mask_flags = 0 ,
} ;
2005-12-05 21:22:34 +03:00
static struct snd_akm4xxx akm_revo51 __devinitdata = {
. type = SND_AK4358 ,
. num_dacs = 6 ,
. ops = {
. set_rate_val = revo_set_rate_val
2006-06-27 17:00:55 +04:00
} ,
2006-08-30 18:49:54 +04:00
. dac_info = revo51_dac ,
2005-12-05 21:22:34 +03:00
} ;
static struct snd_ak4xxx_private akm_revo51_priv __devinitdata = {
. caddr = 2 ,
. cif = 0 ,
. data_mask = VT1724_REVO_CDOUT ,
. clk_mask = VT1724_REVO_CCLK ,
2006-10-04 20:08:43 +04:00
. cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 ,
. cs_addr = VT1724_REVO_CS1 ,
. cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 ,
2006-08-08 23:13:42 +04:00
. add_flags = VT1724_REVO_CCLK , /* high at init */
. mask_flags = 0 ,
} ;
static struct snd_akm4xxx akm_revo51_adc __devinitdata = {
. type = SND_AK5365 ,
. num_adcs = 2 ,
2006-08-30 18:49:54 +04:00
. adc_info = revo51_adc ,
2006-08-08 23:13:42 +04:00
} ;
static struct snd_ak4xxx_private akm_revo51_adc_priv __devinitdata = {
. caddr = 2 ,
. cif = 0 ,
. data_mask = VT1724_REVO_CDOUT ,
. clk_mask = VT1724_REVO_CCLK ,
2006-10-04 20:08:43 +04:00
. cs_mask = VT1724_REVO_CS0 | VT1724_REVO_CS1 ,
. cs_addr = VT1724_REVO_CS0 ,
. cs_none = VT1724_REVO_CS0 | VT1724_REVO_CS1 ,
2005-12-05 21:22:34 +03:00
. add_flags = VT1724_REVO_CCLK , /* high at init */
. mask_flags = 0 ,
} ;
2006-10-04 20:08:43 +04:00
static struct snd_pt2258 ptc_revo51_volume ;
2005-11-17 17:00:18 +03:00
static int __devinit revo_init ( struct snd_ice1712 * ice )
2005-04-17 02:20:36 +04:00
{
2005-11-17 17:00:18 +03:00
struct snd_akm4xxx * ak ;
2005-04-17 02:20:36 +04:00
int err ;
/* determine I2C, DACs and ADCs */
switch ( ice - > eeprom . subvendor ) {
case VT1724_SUBDEVICE_REVOLUTION71 :
ice - > num_total_dacs = 8 ;
ice - > num_total_adcs = 2 ;
2005-12-05 21:22:34 +03:00
ice - > gpio . i2s_mclk_changed = revo_i2s_mclk_changed ;
break ;
case VT1724_SUBDEVICE_REVOLUTION51 :
ice - > num_total_dacs = 6 ;
ice - > num_total_adcs = 2 ;
2005-04-17 02:20:36 +04:00
break ;
default :
snd_BUG ( ) ;
return - EINVAL ;
}
/* second stage of initialization, analog parts and others */
2005-11-17 17:00:18 +03:00
ak = ice - > akm = kcalloc ( 2 , sizeof ( struct snd_akm4xxx ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! ak )
return - ENOMEM ;
ice - > akm_codecs = 2 ;
switch ( ice - > eeprom . subvendor ) {
case VT1724_SUBDEVICE_REVOLUTION71 :
2005-12-05 21:22:34 +03:00
ice - > akm_codecs = 2 ;
2005-04-17 02:20:36 +04:00
if ( ( err = snd_ice1712_akm4xxx_init ( ak , & akm_revo_front , & akm_revo_front_priv , ice ) ) < 0 )
return err ;
if ( ( err = snd_ice1712_akm4xxx_init ( ak + 1 , & akm_revo_surround , & akm_revo_surround_priv , ice ) ) < 0 )
return err ;
/* unmute all codecs */
snd_ice1712_gpio_write_bits ( ice , VT1724_REVO_MUTE , VT1724_REVO_MUTE ) ;
break ;
2005-12-05 21:22:34 +03:00
case VT1724_SUBDEVICE_REVOLUTION51 :
2006-08-08 23:13:42 +04:00
ice - > akm_codecs = 2 ;
2006-10-04 20:08:43 +04:00
err = snd_ice1712_akm4xxx_init ( ak , & akm_revo51 ,
& akm_revo51_priv , ice ) ;
if ( err < 0 )
2005-12-05 21:22:34 +03:00
return err ;
2006-10-04 20:08:43 +04:00
err = snd_ice1712_akm4xxx_init ( ak + 1 , & akm_revo51_adc ,
2006-08-08 23:13:42 +04:00
& akm_revo51_adc_priv , ice ) ;
if ( err < 0 )
return err ;
2006-10-04 20:08:43 +04:00
err = revo51_i2c_init ( ice , & ptc_revo51_volume ) ;
if ( err < 0 )
return err ;
/* unmute all codecs */
snd_ice1712_gpio_write_bits ( ice , VT1724_REVO_MUTE ,
VT1724_REVO_MUTE ) ;
2005-12-05 21:22:34 +03:00
break ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2005-11-17 17:00:18 +03:00
static int __devinit revo_add_controls ( struct snd_ice1712 * ice )
2005-04-17 02:20:36 +04:00
{
int err ;
switch ( ice - > eeprom . subvendor ) {
case VT1724_SUBDEVICE_REVOLUTION71 :
2006-10-04 20:08:43 +04:00
err = snd_ice1712_akm4xxx_build_controls ( ice ) ;
if ( err < 0 )
return err ;
break ;
2005-12-05 21:22:34 +03:00
case VT1724_SUBDEVICE_REVOLUTION51 :
2005-04-17 02:20:36 +04:00
err = snd_ice1712_akm4xxx_build_controls ( ice ) ;
if ( err < 0 )
return err ;
2006-10-04 20:08:43 +04:00
err = snd_pt2258_build_controls ( ice - > spec . revo51 . pt2258 ) ;
if ( err < 0 )
return err ;
break ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
/* entry point */
struct snd_ice1712_card_info snd_vt1724_revo_cards [ ] __devinitdata = {
{
. subvendor = VT1724_SUBDEVICE_REVOLUTION71 ,
. name = " M Audio Revolution-7.1 " ,
. model = " revo71 " ,
. chip_init = revo_init ,
. build_controls = revo_add_controls ,
} ,
2005-12-05 21:22:34 +03:00
{
. subvendor = VT1724_SUBDEVICE_REVOLUTION51 ,
. name = " M Audio Revolution-5.1 " ,
. model = " revo51 " ,
. chip_init = revo_init ,
. build_controls = revo_add_controls ,
} ,
2005-04-17 02:20:36 +04:00
{ } /* terminator */
} ;