2005-04-17 02:20:36 +04:00
/*
2007-10-15 11:50:19 +04:00
* Copyright ( c ) by Jaroslav Kysela < perex @ perex . cz >
2005-04-17 02:20:36 +04:00
* Routines for control of CS4231 ( A ) / CS4232 / InterWave & compatible chips
*
* Bugs :
2008-07-31 23:00:17 +04:00
* - sometimes record brokes playback with WSS portion of
2005-04-17 02:20:36 +04:00
* Yamaha OPL3 - SA3 chip
* - CS4231 ( GUS MAX ) - still trouble with occasional noises
2008-07-31 23:03:41 +04:00
* - broken initialization ?
2005-04-17 02:20:36 +04:00
*
* 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 <linux/delay.h>
# include <linux/pm.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/slab.h>
# include <linux/ioport.h>
2011-07-15 20:38:28 +04:00
# include <linux/module.h>
2015-01-28 18:49:33 +03:00
# include <linux/io.h>
2005-04-17 02:20:36 +04:00
# include <sound/core.h>
2008-07-31 23:02:42 +04:00
# include <sound/wss.h>
2005-04-17 02:20:36 +04:00
# include <sound/pcm_params.h>
2008-07-31 23:08:32 +04:00
# include <sound/tlv.h>
2005-04-17 02:20:36 +04:00
# include <asm/dma.h>
# include <asm/irq.h>
2007-10-15 11:50:19 +04:00
MODULE_AUTHOR ( " Jaroslav Kysela <perex@perex.cz> " ) ;
2005-04-17 02:20:36 +04:00
MODULE_DESCRIPTION ( " Routines for control of CS4231(A)/CS4232/InterWave & compatible chips " ) ;
MODULE_LICENSE ( " GPL " ) ;
#if 0
# define SNDRV_DEBUG_MCE
# endif
/*
* Some variables
*/
static unsigned char freq_bits [ 14 ] = {
/* 5510 */ 0x00 | CS4231_XTAL2 ,
/* 6620 */ 0x0E | CS4231_XTAL2 ,
/* 8000 */ 0x00 | CS4231_XTAL1 ,
/* 9600 */ 0x0E | CS4231_XTAL1 ,
/* 11025 */ 0x02 | CS4231_XTAL2 ,
/* 16000 */ 0x02 | CS4231_XTAL1 ,
/* 18900 */ 0x04 | CS4231_XTAL2 ,
/* 22050 */ 0x06 | CS4231_XTAL2 ,
/* 27042 */ 0x04 | CS4231_XTAL1 ,
/* 32000 */ 0x06 | CS4231_XTAL1 ,
/* 33075 */ 0x0C | CS4231_XTAL2 ,
/* 37800 */ 0x08 | CS4231_XTAL2 ,
/* 44100 */ 0x0A | CS4231_XTAL2 ,
/* 48000 */ 0x0C | CS4231_XTAL1
} ;
2017-06-07 15:16:43 +03:00
static const unsigned int rates [ 14 ] = {
2005-04-17 02:20:36 +04:00
5510 , 6620 , 8000 , 9600 , 11025 , 16000 , 18900 , 22050 ,
27042 , 32000 , 33075 , 37800 , 44100 , 48000
} ;
2017-06-07 15:16:43 +03:00
static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
2007-09-06 17:03:59 +04:00
. count = ARRAY_SIZE ( rates ) ,
2005-04-17 02:20:36 +04:00
. list = rates ,
. mask = 0 ,
} ;
2008-07-31 23:03:41 +04:00
static int snd_wss_xrate ( struct snd_pcm_runtime * runtime )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
return snd_pcm_hw_constraint_list ( runtime , 0 , SNDRV_PCM_HW_PARAM_RATE ,
& hw_constraints_rates ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-31 23:03:41 +04:00
static unsigned char snd_wss_original_image [ 32 ] =
2005-04-17 02:20:36 +04:00
{
0x00 , /* 00/00 - lic */
0x00 , /* 01/01 - ric */
0x9f , /* 02/02 - la1ic */
0x9f , /* 03/03 - ra1ic */
0x9f , /* 04/04 - la2ic */
0x9f , /* 05/05 - ra2ic */
0xbf , /* 06/06 - loc */
0xbf , /* 07/07 - roc */
0x20 , /* 08/08 - pdfr */
CS4231_AUTOCALIB , /* 09/09 - ic */
0x00 , /* 0a/10 - pc */
0x00 , /* 0b/11 - ti */
CS4231_MODE2 , /* 0c/12 - mi */
0xfc , /* 0d/13 - lbc */
0x00 , /* 0e/14 - pbru */
0x00 , /* 0f/15 - pbrl */
0x80 , /* 10/16 - afei */
0x01 , /* 11/17 - afeii */
0x9f , /* 12/18 - llic */
0x9f , /* 13/19 - rlic */
0x00 , /* 14/20 - tlb */
0x00 , /* 15/21 - thb */
0x00 , /* 16/22 - la3mic/reserved */
0x00 , /* 17/23 - ra3mic/reserved */
0x00 , /* 18/24 - afs */
0x00 , /* 19/25 - lamoc/version */
0xcf , /* 1a/26 - mioc */
0x00 , /* 1b/27 - ramoc/reserved */
0x20 , /* 1c/28 - cdfr */
0x00 , /* 1d/29 - res4 */
0x00 , /* 1e/30 - cbru */
0x00 , /* 1f/31 - cbrl */
} ;
2008-06-10 01:07:28 +04:00
static unsigned char snd_opti93x_original_image [ 32 ] =
{
0x00 , /* 00/00 - l_mixout_outctrl */
0x00 , /* 01/01 - r_mixout_outctrl */
0x88 , /* 02/02 - l_cd_inctrl */
0x88 , /* 03/03 - r_cd_inctrl */
0x88 , /* 04/04 - l_a1/fm_inctrl */
0x88 , /* 05/05 - r_a1/fm_inctrl */
0x80 , /* 06/06 - l_dac_inctrl */
0x80 , /* 07/07 - r_dac_inctrl */
0x00 , /* 08/08 - ply_dataform_reg */
0x00 , /* 09/09 - if_conf */
0x00 , /* 0a/10 - pin_ctrl */
0x00 , /* 0b/11 - err_init_reg */
0x0a , /* 0c/12 - id_reg */
0x00 , /* 0d/13 - reserved */
0x00 , /* 0e/14 - ply_upcount_reg */
0x00 , /* 0f/15 - ply_lowcount_reg */
0x88 , /* 10/16 - reserved/l_a1_inctrl */
0x88 , /* 11/17 - reserved/r_a1_inctrl */
0x88 , /* 12/18 - l_line_inctrl */
0x88 , /* 13/19 - r_line_inctrl */
0x88 , /* 14/20 - l_mic_inctrl */
0x88 , /* 15/21 - r_mic_inctrl */
0x80 , /* 16/22 - l_out_outctrl */
0x80 , /* 17/23 - r_out_outctrl */
0x00 , /* 18/24 - reserved */
0x00 , /* 19/25 - reserved */
0x00 , /* 1a/26 - reserved */
0x00 , /* 1b/27 - reserved */
0x00 , /* 1c/28 - cap_dataform_reg */
0x00 , /* 1d/29 - reserved */
0x00 , /* 1e/30 - cap_upcount_reg */
0x00 /* 1f/31 - cap_lowcount_reg */
} ;
2005-04-17 02:20:36 +04:00
/*
* Basic I / O functions
*/
2008-07-31 23:03:41 +04:00
static inline void wss_outb ( struct snd_wss * chip , u8 offset , u8 val )
2005-04-17 02:20:36 +04:00
{
outb ( val , chip - > port + offset ) ;
}
2008-07-31 23:03:41 +04:00
static inline u8 wss_inb ( struct snd_wss * chip , u8 offset )
2005-04-17 02:20:36 +04:00
{
return inb ( chip - > port + offset ) ;
}
2008-07-31 23:03:41 +04:00
static void snd_wss_wait ( struct snd_wss * chip )
2005-04-17 02:20:36 +04:00
{
int timeout ;
for ( timeout = 250 ;
2008-07-31 23:03:41 +04:00
timeout > 0 & & ( wss_inb ( chip , CS4231P ( REGSEL ) ) & CS4231_INIT ) ;
2005-04-17 02:20:36 +04:00
timeout - - )
2008-07-31 23:00:17 +04:00
udelay ( 100 ) ;
2007-09-06 17:03:59 +04:00
}
2008-07-31 23:03:41 +04:00
static void snd_wss_dout ( struct snd_wss * chip , unsigned char reg ,
unsigned char value )
2005-04-17 02:20:36 +04:00
{
int timeout ;
for ( timeout = 250 ;
2008-07-31 23:03:41 +04:00
timeout > 0 & & ( wss_inb ( chip , CS4231P ( REGSEL ) ) & CS4231_INIT ) ;
2005-04-17 02:20:36 +04:00
timeout - - )
2008-07-31 23:00:17 +04:00
udelay ( 10 ) ;
2008-07-31 23:03:41 +04:00
wss_outb ( chip , CS4231P ( REGSEL ) , chip - > mce_bit | reg ) ;
wss_outb ( chip , CS4231P ( REG ) , value ) ;
2005-04-17 02:20:36 +04:00
mb ( ) ;
}
2008-07-31 23:03:41 +04:00
void snd_wss_out ( struct snd_wss * chip , unsigned char reg , unsigned char value )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
snd_wss_wait ( chip ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_SND_DEBUG
2008-07-31 23:03:41 +04:00
if ( wss_inb ( chip , CS4231P ( REGSEL ) ) & CS4231_INIT )
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_DEBUG " out: auto calibration time out "
" - reg = 0x%x, value = 0x%x \n " , reg , value ) ;
2005-04-17 02:20:36 +04:00
# endif
2008-07-31 23:03:41 +04:00
wss_outb ( chip , CS4231P ( REGSEL ) , chip - > mce_bit | reg ) ;
wss_outb ( chip , CS4231P ( REG ) , value ) ;
2005-04-17 02:20:36 +04:00
chip - > image [ reg ] = value ;
mb ( ) ;
2007-09-06 17:03:59 +04:00
snd_printdd ( " codec out - reg 0x%x = 0x%x \n " ,
chip - > mce_bit | reg , value ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-31 23:03:41 +04:00
EXPORT_SYMBOL ( snd_wss_out ) ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:03:41 +04:00
unsigned char snd_wss_in ( struct snd_wss * chip , unsigned char reg )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
snd_wss_wait ( chip ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_SND_DEBUG
2008-07-31 23:03:41 +04:00
if ( wss_inb ( chip , CS4231P ( REGSEL ) ) & CS4231_INIT )
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_DEBUG " in: auto calibration time out "
" - reg = 0x%x \n " , reg ) ;
2005-04-17 02:20:36 +04:00
# endif
2008-07-31 23:03:41 +04:00
wss_outb ( chip , CS4231P ( REGSEL ) , chip - > mce_bit | reg ) ;
2005-04-17 02:20:36 +04:00
mb ( ) ;
2008-07-31 23:03:41 +04:00
return wss_inb ( chip , CS4231P ( REG ) ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-31 23:03:41 +04:00
EXPORT_SYMBOL ( snd_wss_in ) ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:03:41 +04:00
void snd_cs4236_ext_out ( struct snd_wss * chip , unsigned char reg ,
unsigned char val )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
wss_outb ( chip , CS4231P ( REGSEL ) , chip - > mce_bit | 0x17 ) ;
wss_outb ( chip , CS4231P ( REG ) ,
reg | ( chip - > image [ CS4236_EXT_REG ] & 0x01 ) ) ;
wss_outb ( chip , CS4231P ( REG ) , val ) ;
2005-04-17 02:20:36 +04:00
chip - > eimage [ CS4236_REG ( reg ) ] = val ;
#if 0
2009-02-05 17:45:05 +03:00
printk ( KERN_DEBUG " ext out : reg = 0x%x, val = 0x%x \n " , reg , val ) ;
2005-04-17 02:20:36 +04:00
# endif
}
2008-07-31 23:03:41 +04:00
EXPORT_SYMBOL ( snd_cs4236_ext_out ) ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:03:41 +04:00
unsigned char snd_cs4236_ext_in ( struct snd_wss * chip , unsigned char reg )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
wss_outb ( chip , CS4231P ( REGSEL ) , chip - > mce_bit | 0x17 ) ;
wss_outb ( chip , CS4231P ( REG ) ,
reg | ( chip - > image [ CS4236_EXT_REG ] & 0x01 ) ) ;
2005-04-17 02:20:36 +04:00
# if 1
2008-07-31 23:03:41 +04:00
return wss_inb ( chip , CS4231P ( REG ) ) ;
2005-04-17 02:20:36 +04:00
# else
{
unsigned char res ;
2008-07-31 23:03:41 +04:00
res = wss_inb ( chip , CS4231P ( REG ) ) ;
2009-02-05 17:45:05 +03:00
printk ( KERN_DEBUG " ext in : reg = 0x%x, val = 0x%x \n " ,
reg , res ) ;
2005-04-17 02:20:36 +04:00
return res ;
}
# endif
}
2008-07-31 23:03:41 +04:00
EXPORT_SYMBOL ( snd_cs4236_ext_in ) ;
2005-04-17 02:20:36 +04:00
#if 0
2008-07-31 23:03:41 +04:00
static void snd_wss_debug ( struct snd_wss * chip )
{
printk ( KERN_DEBUG
" CS4231 REGS: INDEX = 0x%02x "
" STATUS = 0x%02x \n " ,
2008-08-08 18:39:21 +04:00
wss_inb ( chip , CS4231P ( REGSEL ) ) ,
2008-07-31 23:03:41 +04:00
wss_inb ( chip , CS4231P ( STATUS ) ) ) ;
printk ( KERN_DEBUG
" 0x00: left input = 0x%02x "
" 0x10: alt 1 (CFIG 2) = 0x%02x \n " ,
snd_wss_in ( chip , 0x00 ) ,
snd_wss_in ( chip , 0x10 ) ) ;
printk ( KERN_DEBUG
" 0x01: right input = 0x%02x "
" 0x11: alt 2 (CFIG 3) = 0x%02x \n " ,
snd_wss_in ( chip , 0x01 ) ,
snd_wss_in ( chip , 0x11 ) ) ;
printk ( KERN_DEBUG
" 0x02: GF1 left input = 0x%02x "
" 0x12: left line in = 0x%02x \n " ,
snd_wss_in ( chip , 0x02 ) ,
snd_wss_in ( chip , 0x12 ) ) ;
printk ( KERN_DEBUG
" 0x03: GF1 right input = 0x%02x "
" 0x13: right line in = 0x%02x \n " ,
snd_wss_in ( chip , 0x03 ) ,
snd_wss_in ( chip , 0x13 ) ) ;
printk ( KERN_DEBUG
" 0x04: CD left input = 0x%02x "
" 0x14: timer low = 0x%02x \n " ,
snd_wss_in ( chip , 0x04 ) ,
snd_wss_in ( chip , 0x14 ) ) ;
printk ( KERN_DEBUG
" 0x05: CD right input = 0x%02x "
" 0x15: timer high = 0x%02x \n " ,
snd_wss_in ( chip , 0x05 ) ,
snd_wss_in ( chip , 0x15 ) ) ;
printk ( KERN_DEBUG
" 0x06: left output = 0x%02x "
" 0x16: left MIC (PnP) = 0x%02x \n " ,
snd_wss_in ( chip , 0x06 ) ,
snd_wss_in ( chip , 0x16 ) ) ;
printk ( KERN_DEBUG
" 0x07: right output = 0x%02x "
" 0x17: right MIC (PnP) = 0x%02x \n " ,
snd_wss_in ( chip , 0x07 ) ,
snd_wss_in ( chip , 0x17 ) ) ;
printk ( KERN_DEBUG
" 0x08: playback format = 0x%02x "
" 0x18: IRQ status = 0x%02x \n " ,
snd_wss_in ( chip , 0x08 ) ,
snd_wss_in ( chip , 0x18 ) ) ;
printk ( KERN_DEBUG
" 0x09: iface (CFIG 1) = 0x%02x "
" 0x19: left line out = 0x%02x \n " ,
snd_wss_in ( chip , 0x09 ) ,
snd_wss_in ( chip , 0x19 ) ) ;
printk ( KERN_DEBUG
" 0x0a: pin control = 0x%02x "
" 0x1a: mono control = 0x%02x \n " ,
snd_wss_in ( chip , 0x0a ) ,
snd_wss_in ( chip , 0x1a ) ) ;
printk ( KERN_DEBUG
" 0x0b: init & status = 0x%02x "
" 0x1b: right line out = 0x%02x \n " ,
snd_wss_in ( chip , 0x0b ) ,
snd_wss_in ( chip , 0x1b ) ) ;
printk ( KERN_DEBUG
" 0x0c: revision & mode = 0x%02x "
" 0x1c: record format = 0x%02x \n " ,
snd_wss_in ( chip , 0x0c ) ,
snd_wss_in ( chip , 0x1c ) ) ;
printk ( KERN_DEBUG
" 0x0d: loopback = 0x%02x "
" 0x1d: var freq (PnP) = 0x%02x \n " ,
snd_wss_in ( chip , 0x0d ) ,
snd_wss_in ( chip , 0x1d ) ) ;
printk ( KERN_DEBUG
" 0x0e: ply upr count = 0x%02x "
" 0x1e: ply lwr count = 0x%02x \n " ,
snd_wss_in ( chip , 0x0e ) ,
snd_wss_in ( chip , 0x1e ) ) ;
printk ( KERN_DEBUG
" 0x0f: rec upr count = 0x%02x "
" 0x1f: rec lwr count = 0x%02x \n " ,
snd_wss_in ( chip , 0x0f ) ,
snd_wss_in ( chip , 0x1f ) ) ;
2005-04-17 02:20:36 +04:00
}
# endif
/*
* CS4231 detection / MCE routines
*/
2008-07-31 23:03:41 +04:00
static void snd_wss_busy_wait ( struct snd_wss * chip )
2005-04-17 02:20:36 +04:00
{
int timeout ;
/* huh.. looks like this sequence is proper for CS4231A chip (GUS MAX) */
for ( timeout = 5 ; timeout > 0 ; timeout - - )
2008-07-31 23:03:41 +04:00
wss_inb ( chip , CS4231P ( REGSEL ) ) ;
2005-04-17 02:20:36 +04:00
/* end of cleanup sequence */
2008-07-31 23:09:32 +04:00
for ( timeout = 25000 ;
2008-07-31 23:03:41 +04:00
timeout > 0 & & ( wss_inb ( chip , CS4231P ( REGSEL ) ) & CS4231_INIT ) ;
2005-04-17 02:20:36 +04:00
timeout - - )
2008-07-31 23:00:17 +04:00
udelay ( 10 ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-31 23:03:41 +04:00
void snd_wss_mce_up ( struct snd_wss * chip )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
int timeout ;
2008-07-31 23:03:41 +04:00
snd_wss_wait ( chip ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_SND_DEBUG
2008-07-31 23:03:41 +04:00
if ( wss_inb ( chip , CS4231P ( REGSEL ) ) & CS4231_INIT )
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_DEBUG
" mce_up - auto calibration time out (0) \n " ) ;
2005-04-17 02:20:36 +04:00
# endif
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
chip - > mce_bit | = CS4231_MCE ;
2008-07-31 23:03:41 +04:00
timeout = wss_inb ( chip , CS4231P ( REGSEL ) ) ;
2005-04-17 02:20:36 +04:00
if ( timeout = = 0x80 )
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_DEBUG " mce_up [0x%lx]: "
" serious init problem - codec still busy \n " ,
chip - > port ) ;
2005-04-17 02:20:36 +04:00
if ( ! ( timeout & CS4231_MCE ) )
2008-07-31 23:03:41 +04:00
wss_outb ( chip , CS4231P ( REGSEL ) ,
chip - > mce_bit | ( timeout & 0x1f ) ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
}
2008-07-31 23:03:41 +04:00
EXPORT_SYMBOL ( snd_wss_mce_up ) ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:03:41 +04:00
void snd_wss_mce_down ( struct snd_wss * chip )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
2007-09-11 12:12:14 +04:00
unsigned long end_time ;
2005-04-17 02:20:36 +04:00
int timeout ;
2008-07-31 23:09:32 +04:00
int hw_mask = WSS_HW_CS4231_MASK | WSS_HW_CS4232_MASK | WSS_HW_AD1848 ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:03:41 +04:00
snd_wss_busy_wait ( chip ) ;
2007-09-11 01:22:55 +04:00
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_SND_DEBUG
2008-07-31 23:03:41 +04:00
if ( wss_inb ( chip , CS4231P ( REGSEL ) ) & CS4231_INIT )
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_DEBUG " mce_down [0x%lx] - "
" auto calibration time out (0) \n " ,
( long ) CS4231P ( REGSEL ) ) ;
2005-04-17 02:20:36 +04:00
# endif
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
chip - > mce_bit & = ~ CS4231_MCE ;
2008-07-31 23:03:41 +04:00
timeout = wss_inb ( chip , CS4231P ( REGSEL ) ) ;
wss_outb ( chip , CS4231P ( REGSEL ) , chip - > mce_bit | ( timeout & 0x1f ) ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
if ( timeout = = 0x80 )
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_DEBUG " mce_down [0x%lx]: "
" serious init problem - codec still busy \n " ,
chip - > port ) ;
2008-07-31 23:09:32 +04:00
if ( ( timeout & CS4231_MCE ) = = 0 | | ! ( chip - > hardware & hw_mask ) )
2005-04-17 02:20:36 +04:00
return ;
2007-09-11 01:19:55 +04:00
/*
* Wait for ( possible - - during init auto - calibration may not be set )
2011-03-31 05:57:33 +04:00
* calibration process to start . Needs up to 5 sample periods on AD1848
2007-09-11 01:19:55 +04:00
* which at the slowest possible rate of 5.5125 kHz means 907 us .
*/
msleep ( 1 ) ;
2007-09-11 01:22:55 +04:00
snd_printdd ( " (1) jiffies = %lu \n " , jiffies ) ;
2007-09-11 02:40:42 +04:00
/* check condition up to 250 ms */
2007-09-11 12:12:14 +04:00
end_time = jiffies + msecs_to_jiffies ( 250 ) ;
2008-07-31 23:03:41 +04:00
while ( snd_wss_in ( chip , CS4231_TEST_INIT ) &
2007-09-11 02:40:42 +04:00
CS4231_CALIB_IN_PROGRESS ) {
2007-09-11 12:12:14 +04:00
if ( time_after ( jiffies , end_time ) ) {
2007-09-11 02:40:42 +04:00
snd_printk ( KERN_ERR " mce_down - "
" auto calibration time out (2) \n " ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2007-09-11 12:12:14 +04:00
msleep ( 1 ) ;
2005-04-17 02:20:36 +04:00
}
2007-09-11 01:22:55 +04:00
snd_printdd ( " (2) jiffies = %lu \n " , jiffies ) ;
2007-09-11 02:40:42 +04:00
/* check condition up to 100 ms */
2007-09-11 12:12:14 +04:00
end_time = jiffies + msecs_to_jiffies ( 100 ) ;
2008-07-31 23:03:41 +04:00
while ( wss_inb ( chip , CS4231P ( REGSEL ) ) & CS4231_INIT ) {
2007-09-11 12:12:14 +04:00
if ( time_after ( jiffies , end_time ) ) {
2005-04-17 02:20:36 +04:00
snd_printk ( KERN_ERR " mce_down - auto calibration time out (3) \n " ) ;
return ;
}
2007-09-11 12:12:14 +04:00
msleep ( 1 ) ;
2005-04-17 02:20:36 +04:00
}
2007-09-11 01:22:55 +04:00
snd_printdd ( " (3) jiffies = %lu \n " , jiffies ) ;
2008-07-31 23:03:41 +04:00
snd_printd ( " mce_down - exit = 0x%x \n " , wss_inb ( chip , CS4231P ( REGSEL ) ) ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-31 23:03:41 +04:00
EXPORT_SYMBOL ( snd_wss_mce_down ) ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:03:41 +04:00
static unsigned int snd_wss_get_count ( unsigned char format , unsigned int size )
2005-04-17 02:20:36 +04:00
{
switch ( format & 0xe0 ) {
case CS4231_LINEAR_16 :
case CS4231_LINEAR_16_BIG :
size > > = 1 ;
break ;
case CS4231_ADPCM_16 :
return size > > 2 ;
}
if ( format & CS4231_STEREO )
size > > = 1 ;
return size ;
}
2008-07-31 23:03:41 +04:00
static int snd_wss_trigger ( struct snd_pcm_substream * substream ,
int cmd )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_pcm_substream_chip ( substream ) ;
2005-04-17 02:20:36 +04:00
int result = 0 ;
unsigned int what ;
2005-11-17 16:30:42 +03:00
struct snd_pcm_substream * s ;
2005-04-17 02:20:36 +04:00
int do_start ;
switch ( cmd ) {
case SNDRV_PCM_TRIGGER_START :
case SNDRV_PCM_TRIGGER_RESUME :
do_start = 1 ; break ;
case SNDRV_PCM_TRIGGER_STOP :
case SNDRV_PCM_TRIGGER_SUSPEND :
do_start = 0 ; break ;
default :
return - EINVAL ;
}
what = 0 ;
2007-02-22 14:52:53 +03:00
snd_pcm_group_for_each_entry ( s , substream ) {
2005-04-17 02:20:36 +04:00
if ( s = = chip - > playback_substream ) {
what | = CS4231_PLAYBACK_ENABLE ;
snd_pcm_trigger_done ( s , substream ) ;
} else if ( s = = chip - > capture_substream ) {
what | = CS4231_RECORD_ENABLE ;
snd_pcm_trigger_done ( s , substream ) ;
}
}
spin_lock ( & chip - > reg_lock ) ;
if ( do_start ) {
chip - > image [ CS4231_IFACE_CTRL ] | = what ;
if ( chip - > trigger )
chip - > trigger ( chip , what , 1 ) ;
} else {
chip - > image [ CS4231_IFACE_CTRL ] & = ~ what ;
if ( chip - > trigger )
chip - > trigger ( chip , what , 0 ) ;
}
2008-07-31 23:03:41 +04:00
snd_wss_out ( chip , CS4231_IFACE_CTRL , chip - > image [ CS4231_IFACE_CTRL ] ) ;
2005-04-17 02:20:36 +04:00
spin_unlock ( & chip - > reg_lock ) ;
#if 0
2008-07-31 23:03:41 +04:00
snd_wss_debug ( chip ) ;
2005-04-17 02:20:36 +04:00
# endif
return result ;
}
/*
* CODEC I / O
*/
2008-07-31 23:03:41 +04:00
static unsigned char snd_wss_get_rate ( unsigned int rate )
2005-04-17 02:20:36 +04:00
{
int i ;
2007-09-06 17:03:59 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( rates ) ; i + + )
2005-04-17 02:20:36 +04:00
if ( rate = = rates [ i ] )
return freq_bits [ i ] ;
// snd_BUG();
2007-09-06 17:03:59 +04:00
return freq_bits [ ARRAY_SIZE ( rates ) - 1 ] ;
2005-04-17 02:20:36 +04:00
}
2008-07-31 23:03:41 +04:00
static unsigned char snd_wss_get_format ( struct snd_wss * chip ,
int format ,
int channels )
2005-04-17 02:20:36 +04:00
{
unsigned char rformat ;
rformat = CS4231_LINEAR_8 ;
switch ( format ) {
case SNDRV_PCM_FORMAT_MU_LAW : rformat = CS4231_ULAW_8 ; break ;
case SNDRV_PCM_FORMAT_A_LAW : rformat = CS4231_ALAW_8 ; break ;
case SNDRV_PCM_FORMAT_S16_LE : rformat = CS4231_LINEAR_16 ; break ;
case SNDRV_PCM_FORMAT_S16_BE : rformat = CS4231_LINEAR_16_BIG ; break ;
case SNDRV_PCM_FORMAT_IMA_ADPCM : rformat = CS4231_ADPCM_16 ; break ;
}
if ( channels > 1 )
rformat | = CS4231_STEREO ;
#if 0
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_DEBUG " get_format: 0x%x (mode=0x%x) \n " , format , mode ) ;
2005-04-17 02:20:36 +04:00
# endif
return rformat ;
}
2008-07-31 23:03:41 +04:00
static void snd_wss_calibrate_mute ( struct snd_wss * chip , int mute )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
2008-08-18 00:38:27 +04:00
mute = mute ? 0x80 : 0 ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
if ( chip - > calibrate_mute = = mute ) {
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
return ;
}
if ( ! mute ) {
2008-07-31 23:03:41 +04:00
snd_wss_dout ( chip , CS4231_LEFT_INPUT ,
chip - > image [ CS4231_LEFT_INPUT ] ) ;
snd_wss_dout ( chip , CS4231_RIGHT_INPUT ,
chip - > image [ CS4231_RIGHT_INPUT ] ) ;
snd_wss_dout ( chip , CS4231_LOOPBACK ,
chip - > image [ CS4231_LOOPBACK ] ) ;
2009-01-17 00:47:30 +03:00
} else {
snd_wss_dout ( chip , CS4231_LEFT_INPUT ,
0 ) ;
snd_wss_dout ( chip , CS4231_RIGHT_INPUT ,
0 ) ;
snd_wss_dout ( chip , CS4231_LOOPBACK ,
0xfd ) ;
2005-04-17 02:20:36 +04:00
}
2009-01-17 00:47:30 +03:00
2008-07-31 23:03:41 +04:00
snd_wss_dout ( chip , CS4231_AUX1_LEFT_INPUT ,
2008-08-18 00:38:27 +04:00
mute | chip - > image [ CS4231_AUX1_LEFT_INPUT ] ) ;
2008-07-31 23:03:41 +04:00
snd_wss_dout ( chip , CS4231_AUX1_RIGHT_INPUT ,
2008-08-18 00:38:27 +04:00
mute | chip - > image [ CS4231_AUX1_RIGHT_INPUT ] ) ;
2008-07-31 23:03:41 +04:00
snd_wss_dout ( chip , CS4231_AUX2_LEFT_INPUT ,
2008-08-18 00:38:27 +04:00
mute | chip - > image [ CS4231_AUX2_LEFT_INPUT ] ) ;
2008-07-31 23:03:41 +04:00
snd_wss_dout ( chip , CS4231_AUX2_RIGHT_INPUT ,
2008-08-18 00:38:27 +04:00
mute | chip - > image [ CS4231_AUX2_RIGHT_INPUT ] ) ;
2008-07-31 23:03:41 +04:00
snd_wss_dout ( chip , CS4231_LEFT_OUTPUT ,
2008-08-18 00:38:27 +04:00
mute | chip - > image [ CS4231_LEFT_OUTPUT ] ) ;
2008-07-31 23:03:41 +04:00
snd_wss_dout ( chip , CS4231_RIGHT_OUTPUT ,
2008-08-18 00:38:27 +04:00
mute | chip - > image [ CS4231_RIGHT_OUTPUT ] ) ;
2008-07-31 23:09:32 +04:00
if ( ! ( chip - > hardware & WSS_HW_AD1848_MASK ) ) {
snd_wss_dout ( chip , CS4231_LEFT_LINE_IN ,
2008-08-18 00:38:27 +04:00
mute | chip - > image [ CS4231_LEFT_LINE_IN ] ) ;
2008-07-31 23:09:32 +04:00
snd_wss_dout ( chip , CS4231_RIGHT_LINE_IN ,
2008-08-18 00:38:27 +04:00
mute | chip - > image [ CS4231_RIGHT_LINE_IN ] ) ;
2008-07-31 23:09:32 +04:00
snd_wss_dout ( chip , CS4231_MONO_CTRL ,
mute ? 0xc0 : chip - > image [ CS4231_MONO_CTRL ] ) ;
}
2008-07-31 23:03:41 +04:00
if ( chip - > hardware = = WSS_HW_INTERWAVE ) {
snd_wss_dout ( chip , CS4231_LEFT_MIC_INPUT ,
2008-08-18 00:38:27 +04:00
mute | chip - > image [ CS4231_LEFT_MIC_INPUT ] ) ;
2008-07-31 23:03:41 +04:00
snd_wss_dout ( chip , CS4231_RIGHT_MIC_INPUT ,
2008-08-18 00:38:27 +04:00
mute | chip - > image [ CS4231_RIGHT_MIC_INPUT ] ) ;
2008-07-31 23:03:41 +04:00
snd_wss_dout ( chip , CS4231_LINE_LEFT_OUTPUT ,
2008-08-18 00:38:27 +04:00
mute | chip - > image [ CS4231_LINE_LEFT_OUTPUT ] ) ;
2008-07-31 23:03:41 +04:00
snd_wss_dout ( chip , CS4231_LINE_RIGHT_OUTPUT ,
2008-08-18 00:38:27 +04:00
mute | chip - > image [ CS4231_LINE_RIGHT_OUTPUT ] ) ;
2005-04-17 02:20:36 +04:00
}
chip - > calibrate_mute = mute ;
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
}
2008-07-31 23:03:41 +04:00
static void snd_wss_playback_format ( struct snd_wss * chip ,
2005-11-17 16:30:42 +03:00
struct snd_pcm_hw_params * params ,
2005-04-17 02:20:36 +04:00
unsigned char pdfr )
{
unsigned long flags ;
int full_calib = 1 ;
2006-01-16 18:33:08 +03:00
mutex_lock ( & chip - > mce_mutex ) ;
2008-07-31 23:03:41 +04:00
if ( chip - > hardware = = WSS_HW_CS4231A | |
( chip - > hardware & WSS_HW_CS4232_MASK ) ) {
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
if ( ( chip - > image [ CS4231_PLAYBK_FORMAT ] & 0x0f ) = = ( pdfr & 0x0f ) ) { /* rate is same? */
2008-07-31 23:03:41 +04:00
snd_wss_out ( chip , CS4231_ALT_FEATURE_1 ,
chip - > image [ CS4231_ALT_FEATURE_1 ] | 0x10 ) ;
chip - > image [ CS4231_PLAYBK_FORMAT ] = pdfr ;
snd_wss_out ( chip , CS4231_PLAYBK_FORMAT ,
chip - > image [ CS4231_PLAYBK_FORMAT ] ) ;
snd_wss_out ( chip , CS4231_ALT_FEATURE_1 ,
chip - > image [ CS4231_ALT_FEATURE_1 ] & = ~ 0x10 ) ;
2005-04-17 02:20:36 +04:00
udelay ( 100 ) ; /* Fixes audible clicks at least on GUS MAX */
full_calib = 0 ;
}
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
2009-01-10 01:10:52 +03:00
} else if ( chip - > hardware = = WSS_HW_AD1845 ) {
unsigned rate = params_rate ( params ) ;
/*
* Program the AD1845 correctly for the playback stream .
* Note that we do NOT need to toggle the MCE bit because
* the PLAYBACK_ENABLE bit of the Interface Configuration
* register is set .
*
* NOTE : We seem to need to write to the MSB before the LSB
* to get the correct sample frequency .
*/
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
snd_wss_out ( chip , CS4231_PLAYBK_FORMAT , ( pdfr & 0xf0 ) ) ;
snd_wss_out ( chip , AD1845_UPR_FREQ_SEL , ( rate > > 8 ) & 0xff ) ;
snd_wss_out ( chip , AD1845_LWR_FREQ_SEL , rate & 0xff ) ;
full_calib = 0 ;
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
if ( full_calib ) {
2008-07-31 23:03:41 +04:00
snd_wss_mce_up ( chip ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
2008-07-31 23:03:41 +04:00
if ( chip - > hardware ! = WSS_HW_INTERWAVE & & ! chip - > single_dma ) {
if ( chip - > image [ CS4231_IFACE_CTRL ] & CS4231_RECORD_ENABLE )
pdfr = ( pdfr & 0xf0 ) |
( chip - > image [ CS4231_REC_FORMAT ] & 0x0f ) ;
2005-04-17 02:20:36 +04:00
} else {
2008-07-31 23:03:41 +04:00
chip - > image [ CS4231_PLAYBK_FORMAT ] = pdfr ;
2005-04-17 02:20:36 +04:00
}
2008-07-31 23:03:41 +04:00
snd_wss_out ( chip , CS4231_PLAYBK_FORMAT , pdfr ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
2008-07-31 23:03:41 +04:00
if ( chip - > hardware = = WSS_HW_OPL3SA2 )
2007-07-27 14:20:38 +04:00
udelay ( 100 ) ; /* this seems to help */
2008-07-31 23:03:41 +04:00
snd_wss_mce_down ( chip ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-16 18:33:08 +03:00
mutex_unlock ( & chip - > mce_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-31 23:03:41 +04:00
static void snd_wss_capture_format ( struct snd_wss * chip ,
struct snd_pcm_hw_params * params ,
unsigned char cdfr )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
int full_calib = 1 ;
2006-01-16 18:33:08 +03:00
mutex_lock ( & chip - > mce_mutex ) ;
2008-07-31 23:03:41 +04:00
if ( chip - > hardware = = WSS_HW_CS4231A | |
( chip - > hardware & WSS_HW_CS4232_MASK ) ) {
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
if ( ( chip - > image [ CS4231_PLAYBK_FORMAT ] & 0x0f ) = = ( cdfr & 0x0f ) | | /* rate is same? */
( chip - > image [ CS4231_IFACE_CTRL ] & CS4231_PLAYBACK_ENABLE ) ) {
2008-07-31 23:03:41 +04:00
snd_wss_out ( chip , CS4231_ALT_FEATURE_1 ,
chip - > image [ CS4231_ALT_FEATURE_1 ] | 0x20 ) ;
snd_wss_out ( chip , CS4231_REC_FORMAT ,
chip - > image [ CS4231_REC_FORMAT ] = cdfr ) ;
snd_wss_out ( chip , CS4231_ALT_FEATURE_1 ,
chip - > image [ CS4231_ALT_FEATURE_1 ] & = ~ 0x20 ) ;
2005-04-17 02:20:36 +04:00
full_calib = 0 ;
}
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
2009-01-10 01:10:52 +03:00
} else if ( chip - > hardware = = WSS_HW_AD1845 ) {
unsigned rate = params_rate ( params ) ;
/*
* Program the AD1845 correctly for the capture stream .
* Note that we do NOT need to toggle the MCE bit because
* the PLAYBACK_ENABLE bit of the Interface Configuration
* register is set .
*
* NOTE : We seem to need to write to the MSB before the LSB
* to get the correct sample frequency .
*/
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
snd_wss_out ( chip , CS4231_REC_FORMAT , ( cdfr & 0xf0 ) ) ;
snd_wss_out ( chip , AD1845_UPR_FREQ_SEL , ( rate > > 8 ) & 0xff ) ;
snd_wss_out ( chip , AD1845_LWR_FREQ_SEL , rate & 0xff ) ;
full_calib = 0 ;
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
if ( full_calib ) {
2008-07-31 23:03:41 +04:00
snd_wss_mce_up ( chip ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
2008-07-31 23:03:41 +04:00
if ( chip - > hardware ! = WSS_HW_INTERWAVE & &
! ( chip - > image [ CS4231_IFACE_CTRL ] & CS4231_PLAYBACK_ENABLE ) ) {
if ( chip - > single_dma )
snd_wss_out ( chip , CS4231_PLAYBK_FORMAT , cdfr ) ;
else
snd_wss_out ( chip , CS4231_PLAYBK_FORMAT ,
( chip - > image [ CS4231_PLAYBK_FORMAT ] & 0xf0 ) |
( cdfr & 0x0f ) ) ;
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
snd_wss_mce_down ( chip ) ;
snd_wss_mce_up ( chip ) ;
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-31 23:09:32 +04:00
if ( chip - > hardware & WSS_HW_AD1848_MASK )
snd_wss_out ( chip , CS4231_PLAYBK_FORMAT , cdfr ) ;
else
snd_wss_out ( chip , CS4231_REC_FORMAT , cdfr ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
2008-07-31 23:03:41 +04:00
snd_wss_mce_down ( chip ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-16 18:33:08 +03:00
mutex_unlock ( & chip - > mce_mutex ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Timer interface
*/
2008-07-31 23:03:41 +04:00
static unsigned long snd_wss_timer_resolution ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_timer_chip ( timer ) ;
if ( chip - > hardware & WSS_HW_CS4236B_MASK )
2005-04-17 02:20:36 +04:00
return 14467 ;
else
return chip - > image [ CS4231_PLAYBK_FORMAT ] & 1 ? 9969 : 9920 ;
}
2008-07-31 23:03:41 +04:00
static int snd_wss_timer_start ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
unsigned int ticks ;
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_timer_chip ( timer ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
ticks = timer - > sticks ;
if ( ( chip - > image [ CS4231_ALT_FEATURE_1 ] & CS4231_TIMER_ENABLE ) = = 0 | |
( unsigned char ) ( ticks > > 8 ) ! = chip - > image [ CS4231_TIMER_HIGH ] | |
( unsigned char ) ticks ! = chip - > image [ CS4231_TIMER_LOW ] ) {
2008-07-31 23:03:41 +04:00
chip - > image [ CS4231_TIMER_HIGH ] = ( unsigned char ) ( ticks > > 8 ) ;
snd_wss_out ( chip , CS4231_TIMER_HIGH ,
chip - > image [ CS4231_TIMER_HIGH ] ) ;
chip - > image [ CS4231_TIMER_LOW ] = ( unsigned char ) ticks ;
snd_wss_out ( chip , CS4231_TIMER_LOW ,
chip - > image [ CS4231_TIMER_LOW ] ) ;
snd_wss_out ( chip , CS4231_ALT_FEATURE_1 ,
chip - > image [ CS4231_ALT_FEATURE_1 ] |
CS4231_TIMER_ENABLE ) ;
2005-04-17 02:20:36 +04:00
}
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
return 0 ;
}
2008-07-31 23:03:41 +04:00
static int snd_wss_timer_stop ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_timer_chip ( timer ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
2008-07-31 23:03:41 +04:00
chip - > image [ CS4231_ALT_FEATURE_1 ] & = ~ CS4231_TIMER_ENABLE ;
snd_wss_out ( chip , CS4231_ALT_FEATURE_1 ,
chip - > image [ CS4231_ALT_FEATURE_1 ] ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
return 0 ;
}
2008-07-31 23:03:41 +04:00
static void snd_wss_init ( struct snd_wss * chip )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
2009-01-17 00:47:30 +03:00
snd_wss_calibrate_mute ( chip , 1 ) ;
2008-07-31 23:03:41 +04:00
snd_wss_mce_down ( chip ) ;
2005-04-17 02:20:36 +04:00
# ifdef SNDRV_DEBUG_MCE
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_DEBUG " init: (1) \n " ) ;
2005-04-17 02:20:36 +04:00
# endif
2008-07-31 23:03:41 +04:00
snd_wss_mce_up ( chip ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
2008-07-31 23:03:41 +04:00
chip - > image [ CS4231_IFACE_CTRL ] & = ~ ( CS4231_PLAYBACK_ENABLE |
CS4231_PLAYBACK_PIO |
CS4231_RECORD_ENABLE |
CS4231_RECORD_PIO |
CS4231_CALIB_MODE ) ;
2005-04-17 02:20:36 +04:00
chip - > image [ CS4231_IFACE_CTRL ] | = CS4231_AUTOCALIB ;
2008-07-31 23:03:41 +04:00
snd_wss_out ( chip , CS4231_IFACE_CTRL , chip - > image [ CS4231_IFACE_CTRL ] ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
2008-07-31 23:03:41 +04:00
snd_wss_mce_down ( chip ) ;
2005-04-17 02:20:36 +04:00
# ifdef SNDRV_DEBUG_MCE
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_DEBUG " init: (2) \n " ) ;
2005-04-17 02:20:36 +04:00
# endif
2008-07-31 23:03:41 +04:00
snd_wss_mce_up ( chip ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
2009-01-17 00:47:30 +03:00
chip - > image [ CS4231_IFACE_CTRL ] & = ~ CS4231_AUTOCALIB ;
snd_wss_out ( chip , CS4231_IFACE_CTRL , chip - > image [ CS4231_IFACE_CTRL ] ) ;
2008-07-31 23:03:41 +04:00
snd_wss_out ( chip ,
CS4231_ALT_FEATURE_1 , chip - > image [ CS4231_ALT_FEATURE_1 ] ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
2008-07-31 23:03:41 +04:00
snd_wss_mce_down ( chip ) ;
2005-04-17 02:20:36 +04:00
# ifdef SNDRV_DEBUG_MCE
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_DEBUG " init: (3) - afei = 0x%x \n " ,
2008-07-31 23:03:41 +04:00
chip - > image [ CS4231_ALT_FEATURE_1 ] ) ;
2005-04-17 02:20:36 +04:00
# endif
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
2008-07-31 23:03:41 +04:00
snd_wss_out ( chip , CS4231_ALT_FEATURE_2 ,
chip - > image [ CS4231_ALT_FEATURE_2 ] ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
2008-07-31 23:03:41 +04:00
snd_wss_mce_up ( chip ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
2008-07-31 23:03:41 +04:00
snd_wss_out ( chip , CS4231_PLAYBK_FORMAT ,
chip - > image [ CS4231_PLAYBK_FORMAT ] ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
2008-07-31 23:03:41 +04:00
snd_wss_mce_down ( chip ) ;
2005-04-17 02:20:36 +04:00
# ifdef SNDRV_DEBUG_MCE
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_DEBUG " init: (4) \n " ) ;
2005-04-17 02:20:36 +04:00
# endif
2008-07-31 23:03:41 +04:00
snd_wss_mce_up ( chip ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
2008-07-31 23:09:32 +04:00
if ( ! ( chip - > hardware & WSS_HW_AD1848_MASK ) )
snd_wss_out ( chip , CS4231_REC_FORMAT ,
chip - > image [ CS4231_REC_FORMAT ] ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
2008-07-31 23:03:41 +04:00
snd_wss_mce_down ( chip ) ;
2009-01-17 00:47:30 +03:00
snd_wss_calibrate_mute ( chip , 0 ) ;
2005-04-17 02:20:36 +04:00
# ifdef SNDRV_DEBUG_MCE
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_DEBUG " init: (5) \n " ) ;
2005-04-17 02:20:36 +04:00
# endif
}
2008-07-31 23:03:41 +04:00
static int snd_wss_open ( struct snd_wss * chip , unsigned int mode )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
2006-01-16 18:33:08 +03:00
mutex_lock ( & chip - > open_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( ( chip - > mode & mode ) | |
2008-07-31 23:03:41 +04:00
( ( chip - > mode & WSS_MODE_OPEN ) & & chip - > single_dma ) ) {
2006-01-16 18:33:08 +03:00
mutex_unlock ( & chip - > open_mutex ) ;
2005-04-17 02:20:36 +04:00
return - EAGAIN ;
}
2008-07-31 23:03:41 +04:00
if ( chip - > mode & WSS_MODE_OPEN ) {
2005-04-17 02:20:36 +04:00
chip - > mode | = mode ;
2006-01-16 18:33:08 +03:00
mutex_unlock ( & chip - > open_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/* ok. now enable and ack CODEC IRQ */
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
2008-07-31 23:09:32 +04:00
if ( ! ( chip - > hardware & WSS_HW_AD1848_MASK ) ) {
snd_wss_out ( chip , CS4231_IRQ_STATUS ,
CS4231_PLAYBACK_IRQ |
CS4231_RECORD_IRQ |
CS4231_TIMER_IRQ ) ;
snd_wss_out ( chip , CS4231_IRQ_STATUS , 0 ) ;
}
2008-07-31 23:03:41 +04:00
wss_outb ( chip , CS4231P ( STATUS ) , 0 ) ; /* clear IRQ */
wss_outb ( chip , CS4231P ( STATUS ) , 0 ) ; /* clear IRQ */
2005-04-17 02:20:36 +04:00
chip - > image [ CS4231_PIN_CTRL ] | = CS4231_IRQ_ENABLE ;
2008-07-31 23:03:41 +04:00
snd_wss_out ( chip , CS4231_PIN_CTRL , chip - > image [ CS4231_PIN_CTRL ] ) ;
2008-07-31 23:09:32 +04:00
if ( ! ( chip - > hardware & WSS_HW_AD1848_MASK ) ) {
snd_wss_out ( chip , CS4231_IRQ_STATUS ,
CS4231_PLAYBACK_IRQ |
CS4231_RECORD_IRQ |
CS4231_TIMER_IRQ ) ;
snd_wss_out ( chip , CS4231_IRQ_STATUS , 0 ) ;
}
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
chip - > mode = mode ;
2006-01-16 18:33:08 +03:00
mutex_unlock ( & chip - > open_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-07-31 23:03:41 +04:00
static void snd_wss_close ( struct snd_wss * chip , unsigned int mode )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
2006-01-16 18:33:08 +03:00
mutex_lock ( & chip - > open_mutex ) ;
2005-04-17 02:20:36 +04:00
chip - > mode & = ~ mode ;
2008-07-31 23:03:41 +04:00
if ( chip - > mode & WSS_MODE_OPEN ) {
2006-01-16 18:33:08 +03:00
mutex_unlock ( & chip - > open_mutex ) ;
2005-04-17 02:20:36 +04:00
return ;
}
/* disable IRQ */
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
2008-07-31 23:09:32 +04:00
if ( ! ( chip - > hardware & WSS_HW_AD1848_MASK ) )
snd_wss_out ( chip , CS4231_IRQ_STATUS , 0 ) ;
2008-07-31 23:03:41 +04:00
wss_outb ( chip , CS4231P ( STATUS ) , 0 ) ; /* clear IRQ */
wss_outb ( chip , CS4231P ( STATUS ) , 0 ) ; /* clear IRQ */
2005-04-17 02:20:36 +04:00
chip - > image [ CS4231_PIN_CTRL ] & = ~ CS4231_IRQ_ENABLE ;
2008-07-31 23:03:41 +04:00
snd_wss_out ( chip , CS4231_PIN_CTRL , chip - > image [ CS4231_PIN_CTRL ] ) ;
2005-04-17 02:20:36 +04:00
/* now disable record & playback */
if ( chip - > image [ CS4231_IFACE_CTRL ] & ( CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
CS4231_RECORD_ENABLE | CS4231_RECORD_PIO ) ) {
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
2008-07-31 23:03:41 +04:00
snd_wss_mce_up ( chip ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
chip - > image [ CS4231_IFACE_CTRL ] & = ~ ( CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
CS4231_RECORD_ENABLE | CS4231_RECORD_PIO ) ;
2008-07-31 23:03:41 +04:00
snd_wss_out ( chip , CS4231_IFACE_CTRL ,
chip - > image [ CS4231_IFACE_CTRL ] ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
2008-07-31 23:03:41 +04:00
snd_wss_mce_down ( chip ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
}
/* clear IRQ again */
2008-07-31 23:09:32 +04:00
if ( ! ( chip - > hardware & WSS_HW_AD1848_MASK ) )
snd_wss_out ( chip , CS4231_IRQ_STATUS , 0 ) ;
2008-07-31 23:03:41 +04:00
wss_outb ( chip , CS4231P ( STATUS ) , 0 ) ; /* clear IRQ */
wss_outb ( chip , CS4231P ( STATUS ) , 0 ) ; /* clear IRQ */
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
chip - > mode = 0 ;
2006-01-16 18:33:08 +03:00
mutex_unlock ( & chip - > open_mutex ) ;
2005-04-17 02:20:36 +04:00
}
/*
* timer open / close
*/
2008-07-31 23:03:41 +04:00
static int snd_wss_timer_open ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_timer_chip ( timer ) ;
snd_wss_open ( chip , WSS_MODE_TIMER ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-07-31 23:03:41 +04:00
static int snd_wss_timer_close ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_timer_chip ( timer ) ;
snd_wss_close ( chip , WSS_MODE_TIMER ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-07-31 23:03:41 +04:00
static struct snd_timer_hardware snd_wss_timer_table =
2005-04-17 02:20:36 +04:00
{
. flags = SNDRV_TIMER_HW_AUTO ,
. resolution = 9945 ,
. ticks = 65535 ,
2008-07-31 23:03:41 +04:00
. open = snd_wss_timer_open ,
. close = snd_wss_timer_close ,
. c_resolution = snd_wss_timer_resolution ,
. start = snd_wss_timer_start ,
. stop = snd_wss_timer_stop ,
2005-04-17 02:20:36 +04:00
} ;
/*
* ok . . exported functions . .
*/
2008-07-31 23:03:41 +04:00
static int snd_wss_playback_hw_params ( struct snd_pcm_substream * substream ,
2005-11-17 16:30:42 +03:00
struct snd_pcm_hw_params * hw_params )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_pcm_substream_chip ( substream ) ;
2005-04-17 02:20:36 +04:00
unsigned char new_pdfr ;
int err ;
if ( ( err = snd_pcm_lib_malloc_pages ( substream , params_buffer_bytes ( hw_params ) ) ) < 0 )
return err ;
2008-07-31 23:03:41 +04:00
new_pdfr = snd_wss_get_format ( chip , params_format ( hw_params ) ,
params_channels ( hw_params ) ) |
snd_wss_get_rate ( params_rate ( hw_params ) ) ;
2005-04-17 02:20:36 +04:00
chip - > set_playback_format ( chip , hw_params , new_pdfr ) ;
return 0 ;
}
2008-07-31 23:03:41 +04:00
static int snd_wss_playback_hw_free ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
return snd_pcm_lib_free_pages ( substream ) ;
}
2008-07-31 23:03:41 +04:00
static int snd_wss_playback_prepare ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_pcm_substream_chip ( substream ) ;
2005-11-17 16:30:42 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
unsigned int size = snd_pcm_lib_buffer_bytes ( substream ) ;
unsigned int count = snd_pcm_lib_period_bytes ( substream ) ;
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
chip - > p_dma_size = size ;
chip - > image [ CS4231_IFACE_CTRL ] & = ~ ( CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO ) ;
snd_dma_program ( chip - > dma1 , runtime - > dma_addr , size , DMA_MODE_WRITE | DMA_AUTOINIT ) ;
2008-07-31 23:03:41 +04:00
count = snd_wss_get_count ( chip - > image [ CS4231_PLAYBK_FORMAT ] , count ) - 1 ;
snd_wss_out ( chip , CS4231_PLY_LWR_CNT , ( unsigned char ) count ) ;
snd_wss_out ( chip , CS4231_PLY_UPR_CNT , ( unsigned char ) ( count > > 8 ) ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
#if 0
2008-07-31 23:03:41 +04:00
snd_wss_debug ( chip ) ;
2005-04-17 02:20:36 +04:00
# endif
return 0 ;
}
2008-07-31 23:03:41 +04:00
static int snd_wss_capture_hw_params ( struct snd_pcm_substream * substream ,
2005-11-17 16:30:42 +03:00
struct snd_pcm_hw_params * hw_params )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_pcm_substream_chip ( substream ) ;
2005-04-17 02:20:36 +04:00
unsigned char new_cdfr ;
int err ;
if ( ( err = snd_pcm_lib_malloc_pages ( substream , params_buffer_bytes ( hw_params ) ) ) < 0 )
return err ;
2008-07-31 23:03:41 +04:00
new_cdfr = snd_wss_get_format ( chip , params_format ( hw_params ) ,
params_channels ( hw_params ) ) |
snd_wss_get_rate ( params_rate ( hw_params ) ) ;
2005-04-17 02:20:36 +04:00
chip - > set_capture_format ( chip , hw_params , new_cdfr ) ;
return 0 ;
}
2008-07-31 23:03:41 +04:00
static int snd_wss_capture_hw_free ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
return snd_pcm_lib_free_pages ( substream ) ;
}
2008-07-31 23:03:41 +04:00
static int snd_wss_capture_prepare ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_pcm_substream_chip ( substream ) ;
2005-11-17 16:30:42 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
unsigned int size = snd_pcm_lib_buffer_bytes ( substream ) ;
unsigned int count = snd_pcm_lib_period_bytes ( substream ) ;
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
chip - > c_dma_size = size ;
chip - > image [ CS4231_IFACE_CTRL ] & = ~ ( CS4231_RECORD_ENABLE | CS4231_RECORD_PIO ) ;
snd_dma_program ( chip - > dma2 , runtime - > dma_addr , size , DMA_MODE_READ | DMA_AUTOINIT ) ;
2008-07-31 23:09:32 +04:00
if ( chip - > hardware & WSS_HW_AD1848_MASK )
count = snd_wss_get_count ( chip - > image [ CS4231_PLAYBK_FORMAT ] ,
count ) ;
else
count = snd_wss_get_count ( chip - > image [ CS4231_REC_FORMAT ] ,
count ) ;
count - - ;
2008-07-31 23:03:41 +04:00
if ( chip - > single_dma & & chip - > hardware ! = WSS_HW_INTERWAVE ) {
snd_wss_out ( chip , CS4231_PLY_LWR_CNT , ( unsigned char ) count ) ;
snd_wss_out ( chip , CS4231_PLY_UPR_CNT ,
( unsigned char ) ( count > > 8 ) ) ;
2005-04-17 02:20:36 +04:00
} else {
2008-07-31 23:03:41 +04:00
snd_wss_out ( chip , CS4231_REC_LWR_CNT , ( unsigned char ) count ) ;
snd_wss_out ( chip , CS4231_REC_UPR_CNT ,
( unsigned char ) ( count > > 8 ) ) ;
2005-04-17 02:20:36 +04:00
}
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
return 0 ;
}
2008-07-31 23:03:41 +04:00
void snd_wss_overrange ( struct snd_wss * chip )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
unsigned char res ;
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
2008-07-31 23:03:41 +04:00
res = snd_wss_in ( chip , CS4231_TEST_INIT ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
if ( res & ( 0x08 | 0x02 ) ) /* detect overrange only above 0dB; may be user selectable? */
chip - > capture_substream - > runtime - > overrange + + ;
}
2008-07-31 23:03:41 +04:00
EXPORT_SYMBOL ( snd_wss_overrange ) ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:03:41 +04:00
irqreturn_t snd_wss_interrupt ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = dev_id ;
2005-04-17 02:20:36 +04:00
unsigned char status ;
2008-07-31 23:10:47 +04:00
if ( chip - > hardware & WSS_HW_AD1848_MASK )
/* pretend it was the only possible irq for AD1848 */
status = CS4231_PLAYBACK_IRQ ;
else
status = snd_wss_in ( chip , CS4231_IRQ_STATUS ) ;
2005-04-17 02:20:36 +04:00
if ( status & CS4231_TIMER_IRQ ) {
if ( chip - > timer )
snd_timer_interrupt ( chip - > timer , chip - > timer - > sticks ) ;
2008-07-31 23:00:17 +04:00
}
2008-07-31 23:03:41 +04:00
if ( chip - > single_dma & & chip - > hardware ! = WSS_HW_INTERWAVE ) {
2005-04-17 02:20:36 +04:00
if ( status & CS4231_PLAYBACK_IRQ ) {
2008-07-31 23:03:41 +04:00
if ( chip - > mode & WSS_MODE_PLAY ) {
2005-04-17 02:20:36 +04:00
if ( chip - > playback_substream )
snd_pcm_period_elapsed ( chip - > playback_substream ) ;
}
2008-07-31 23:03:41 +04:00
if ( chip - > mode & WSS_MODE_RECORD ) {
2005-04-17 02:20:36 +04:00
if ( chip - > capture_substream ) {
2008-07-31 23:03:41 +04:00
snd_wss_overrange ( chip ) ;
2005-04-17 02:20:36 +04:00
snd_pcm_period_elapsed ( chip - > capture_substream ) ;
}
}
}
} else {
if ( status & CS4231_PLAYBACK_IRQ ) {
if ( chip - > playback_substream )
snd_pcm_period_elapsed ( chip - > playback_substream ) ;
}
if ( status & CS4231_RECORD_IRQ ) {
if ( chip - > capture_substream ) {
2008-07-31 23:03:41 +04:00
snd_wss_overrange ( chip ) ;
2005-04-17 02:20:36 +04:00
snd_pcm_period_elapsed ( chip - > capture_substream ) ;
}
}
}
spin_lock ( & chip - > reg_lock ) ;
2008-07-31 23:10:47 +04:00
status = ~ CS4231_ALL_IRQS | ~ status ;
if ( chip - > hardware & WSS_HW_AD1848_MASK )
wss_outb ( chip , CS4231P ( STATUS ) , 0 ) ;
else
2009-01-17 00:47:30 +03:00
snd_wss_out ( chip , CS4231_IRQ_STATUS , status ) ;
2005-04-17 02:20:36 +04:00
spin_unlock ( & chip - > reg_lock ) ;
return IRQ_HANDLED ;
}
2008-07-31 23:03:41 +04:00
EXPORT_SYMBOL ( snd_wss_interrupt ) ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:03:41 +04:00
static snd_pcm_uframes_t snd_wss_playback_pointer ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_pcm_substream_chip ( substream ) ;
2005-04-17 02:20:36 +04:00
size_t ptr ;
if ( ! ( chip - > image [ CS4231_IFACE_CTRL ] & CS4231_PLAYBACK_ENABLE ) )
return 0 ;
ptr = snd_dma_pointer ( chip - > dma1 , chip - > p_dma_size ) ;
return bytes_to_frames ( substream - > runtime , ptr ) ;
}
2008-07-31 23:03:41 +04:00
static snd_pcm_uframes_t snd_wss_capture_pointer ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_pcm_substream_chip ( substream ) ;
2005-04-17 02:20:36 +04:00
size_t ptr ;
2008-07-31 23:00:17 +04:00
2005-04-17 02:20:36 +04:00
if ( ! ( chip - > image [ CS4231_IFACE_CTRL ] & CS4231_RECORD_ENABLE ) )
return 0 ;
ptr = snd_dma_pointer ( chip - > dma2 , chip - > c_dma_size ) ;
return bytes_to_frames ( substream - > runtime , ptr ) ;
}
/*
*/
2008-07-31 23:10:47 +04:00
static int snd_ad1848_probe ( struct snd_wss * chip )
2005-04-17 02:20:36 +04:00
{
2008-08-06 10:09:34 +04:00
unsigned long timeout = jiffies + msecs_to_jiffies ( 1000 ) ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
2008-08-06 10:09:34 +04:00
unsigned char r ;
unsigned short hardware = 0 ;
int err = 0 ;
int i ;
2005-04-17 02:20:36 +04:00
2008-08-06 10:09:34 +04:00
while ( wss_inb ( chip , CS4231P ( REGSEL ) ) & CS4231_INIT ) {
if ( time_after ( jiffies , timeout ) )
return - ENODEV ;
cond_resched ( ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-31 23:10:47 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
2008-08-06 10:09:34 +04:00
/* set CS423x MODE 1 */
2008-08-24 20:08:03 +04:00
snd_wss_dout ( chip , CS4231_MISC_INFO , 0 ) ;
2008-08-06 10:09:34 +04:00
2008-08-24 20:08:03 +04:00
snd_wss_dout ( chip , CS4231_RIGHT_INPUT , 0x45 ) ; /* 0x55 & ~0x10 */
2008-08-06 10:09:34 +04:00
r = snd_wss_in ( chip , CS4231_RIGHT_INPUT ) ;
if ( r ! = 0x45 ) {
/* RMGE always high on AD1847 */
if ( ( r & ~ CS4231_ENABLE_MIC_GAIN ) ! = 0x45 ) {
err = - ENODEV ;
goto out ;
}
hardware = WSS_HW_AD1847 ;
} else {
2008-08-24 20:08:03 +04:00
snd_wss_dout ( chip , CS4231_LEFT_INPUT , 0xaa ) ;
2008-08-06 10:09:34 +04:00
r = snd_wss_in ( chip , CS4231_LEFT_INPUT ) ;
/* L/RMGE always low on AT2320 */
if ( ( r | CS4231_ENABLE_MIC_GAIN ) ! = 0xaa ) {
err = - ENODEV ;
goto out ;
2008-07-31 23:10:47 +04:00
}
}
2008-08-06 10:09:34 +04:00
/* clear pending IRQ */
wss_inb ( chip , CS4231P ( STATUS ) ) ;
wss_outb ( chip , CS4231P ( STATUS ) , 0 ) ;
mb ( ) ;
if ( ( chip - > hardware & WSS_HW_TYPE_MASK ) ! = WSS_HW_DETECT )
goto out ;
if ( hardware ) {
chip - > hardware = hardware ;
goto out ;
2008-07-31 23:10:47 +04:00
}
2008-08-06 10:09:34 +04:00
r = snd_wss_in ( chip , CS4231_MISC_INFO ) ;
/* set CS423x MODE 2 */
2008-08-24 20:08:03 +04:00
snd_wss_dout ( chip , CS4231_MISC_INFO , CS4231_MODE2 ) ;
2008-08-06 10:09:34 +04:00
for ( i = 0 ; i < 16 ; i + + ) {
if ( snd_wss_in ( chip , i ) ! = snd_wss_in ( chip , 16 + i ) ) {
/* we have more than 16 registers: check ID */
if ( ( r & 0xf ) ! = 0xa )
goto out_mode ;
/*
* on CMI8330 , CS4231_VERSION is volume control and
* can be set to 0
*/
snd_wss_dout ( chip , CS4231_VERSION , 0 ) ;
r = snd_wss_in ( chip , CS4231_VERSION ) & 0xe7 ;
if ( ! r )
chip - > hardware = WSS_HW_CMI8330 ;
goto out_mode ;
}
}
if ( r & 0x80 )
chip - > hardware = WSS_HW_CS4248 ;
else
chip - > hardware = WSS_HW_AD1848 ;
out_mode :
2008-08-24 20:08:03 +04:00
snd_wss_dout ( chip , CS4231_MISC_INFO , 0 ) ;
2008-08-06 10:09:34 +04:00
out :
2008-07-31 23:10:47 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
2008-08-06 10:09:34 +04:00
return err ;
2008-07-31 23:10:47 +04:00
}
static int snd_wss_probe ( struct snd_wss * chip )
{
unsigned long flags ;
int i , id , rev , regnum ;
unsigned char * ptr ;
unsigned int hw ;
id = snd_ad1848_probe ( chip ) ;
if ( id < 0 )
return id ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:03:41 +04:00
hw = chip - > hardware ;
if ( ( hw & WSS_HW_TYPE_MASK ) = = WSS_HW_DETECT ) {
2008-07-31 23:10:47 +04:00
for ( i = 0 ; i < 50 ; i + + ) {
mb ( ) ;
if ( wss_inb ( chip , CS4231P ( REGSEL ) ) & CS4231_INIT )
msleep ( 2 ) ;
else {
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
snd_wss_out ( chip , CS4231_MISC_INFO ,
CS4231_MODE2 ) ;
id = snd_wss_in ( chip , CS4231_MISC_INFO ) & 0x0f ;
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
if ( id = = 0x0a )
break ; /* this is valid value */
}
}
snd_printdd ( " wss: port = 0x%lx, id = 0x%x \n " , chip - > port , id ) ;
if ( id ! = 0x0a )
return - ENODEV ; /* no valid device found */
2008-07-31 23:03:41 +04:00
rev = snd_wss_in ( chip , CS4231_VERSION ) & 0xe7 ;
2005-04-17 02:20:36 +04:00
snd_printdd ( " CS4231: VERSION (I25) = 0x%x \n " , rev ) ;
if ( rev = = 0x80 ) {
2008-07-31 23:03:41 +04:00
unsigned char tmp = snd_wss_in ( chip , 23 ) ;
snd_wss_out ( chip , 23 , ~ tmp ) ;
if ( snd_wss_in ( chip , 23 ) ! = tmp )
chip - > hardware = WSS_HW_AD1845 ;
2005-04-17 02:20:36 +04:00
else
2008-07-31 23:03:41 +04:00
chip - > hardware = WSS_HW_CS4231 ;
2005-04-17 02:20:36 +04:00
} else if ( rev = = 0xa0 ) {
2008-07-31 23:03:41 +04:00
chip - > hardware = WSS_HW_CS4231A ;
2005-04-17 02:20:36 +04:00
} else if ( rev = = 0xa2 ) {
2008-07-31 23:03:41 +04:00
chip - > hardware = WSS_HW_CS4232 ;
2005-04-17 02:20:36 +04:00
} else if ( rev = = 0xb2 ) {
2008-07-31 23:03:41 +04:00
chip - > hardware = WSS_HW_CS4232A ;
2005-04-17 02:20:36 +04:00
} else if ( rev = = 0x83 ) {
2008-07-31 23:03:41 +04:00
chip - > hardware = WSS_HW_CS4236 ;
2005-04-17 02:20:36 +04:00
} else if ( rev = = 0x03 ) {
2008-07-31 23:03:41 +04:00
chip - > hardware = WSS_HW_CS4236B ;
2005-04-17 02:20:36 +04:00
} else {
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_ERR
" unknown CS chip with version 0x%x \n " , rev ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ; /* unknown CS4231 chip? */
}
}
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
2008-07-31 23:03:41 +04:00
wss_inb ( chip , CS4231P ( STATUS ) ) ; /* clear any pendings IRQ */
wss_outb ( chip , CS4231P ( STATUS ) , 0 ) ;
2005-04-17 02:20:36 +04:00
mb ( ) ;
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
2008-07-31 23:10:47 +04:00
if ( ! ( chip - > hardware & WSS_HW_AD1848_MASK ) )
chip - > image [ CS4231_MISC_INFO ] = CS4231_MODE2 ;
2005-04-17 02:20:36 +04:00
switch ( chip - > hardware ) {
2008-07-31 23:03:41 +04:00
case WSS_HW_INTERWAVE :
2005-04-17 02:20:36 +04:00
chip - > image [ CS4231_MISC_INFO ] = CS4231_IW_MODE3 ;
break ;
2008-07-31 23:03:41 +04:00
case WSS_HW_CS4235 :
case WSS_HW_CS4236B :
case WSS_HW_CS4237B :
case WSS_HW_CS4238B :
case WSS_HW_CS4239 :
if ( hw = = WSS_HW_DETECT3 )
2005-04-17 02:20:36 +04:00
chip - > image [ CS4231_MISC_INFO ] = CS4231_4236_MODE3 ;
else
2008-07-31 23:03:41 +04:00
chip - > hardware = WSS_HW_CS4236 ;
2005-04-17 02:20:36 +04:00
break ;
}
chip - > image [ CS4231_IFACE_CTRL ] =
( chip - > image [ CS4231_IFACE_CTRL ] & ~ CS4231_SINGLE_DMA ) |
( chip - > single_dma ? CS4231_SINGLE_DMA : 0 ) ;
2008-07-31 23:03:41 +04:00
if ( chip - > hardware ! = WSS_HW_OPTI93X ) {
2008-06-10 01:07:28 +04:00
chip - > image [ CS4231_ALT_FEATURE_1 ] = 0x80 ;
chip - > image [ CS4231_ALT_FEATURE_2 ] =
2008-07-31 23:03:41 +04:00
chip - > hardware = = WSS_HW_INTERWAVE ? 0xc2 : 0x01 ;
2008-06-10 01:07:28 +04:00
}
2009-01-10 01:10:52 +03:00
/* enable fine grained frequency selection */
if ( chip - > hardware = = WSS_HW_AD1845 )
chip - > image [ AD1845_PWR_DOWN ] = 8 ;
2005-04-17 02:20:36 +04:00
ptr = ( unsigned char * ) & chip - > image ;
2008-07-31 23:10:47 +04:00
regnum = ( chip - > hardware & WSS_HW_AD1848_MASK ) ? 16 : 32 ;
2008-07-31 23:03:41 +04:00
snd_wss_mce_down ( chip ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
2008-07-31 23:10:47 +04:00
for ( i = 0 ; i < regnum ; i + + ) /* ok.. fill all registers */
2008-07-31 23:03:41 +04:00
snd_wss_out ( chip , i , * ptr + + ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
2008-07-31 23:03:41 +04:00
snd_wss_mce_up ( chip ) ;
snd_wss_mce_down ( chip ) ;
2005-04-17 02:20:36 +04:00
mdelay ( 2 ) ;
/* ok.. try check hardware version for CS4236+ chips */
2008-07-31 23:03:41 +04:00
if ( ( hw & WSS_HW_TYPE_MASK ) = = WSS_HW_DETECT ) {
if ( chip - > hardware = = WSS_HW_CS4236B ) {
2005-04-17 02:20:36 +04:00
rev = snd_cs4236_ext_in ( chip , CS4236_VERSION ) ;
snd_cs4236_ext_out ( chip , CS4236_VERSION , 0xff ) ;
id = snd_cs4236_ext_in ( chip , CS4236_VERSION ) ;
snd_cs4236_ext_out ( chip , CS4236_VERSION , rev ) ;
snd_printdd ( " CS4231: ext version; rev = 0x%x, id = 0x%x \n " , rev , id ) ;
if ( ( id & 0x1f ) = = 0x1d ) { /* CS4235 */
2008-07-31 23:03:41 +04:00
chip - > hardware = WSS_HW_CS4235 ;
2005-04-17 02:20:36 +04:00
switch ( id > > 5 ) {
case 4 :
case 5 :
case 6 :
break ;
default :
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_WARNING
" unknown CS4235 chip "
" (enhanced version = 0x%x) \n " ,
id ) ;
2005-04-17 02:20:36 +04:00
}
} else if ( ( id & 0x1f ) = = 0x0b ) { /* CS4236/B */
switch ( id > > 5 ) {
case 4 :
case 5 :
case 6 :
case 7 :
2008-07-31 23:03:41 +04:00
chip - > hardware = WSS_HW_CS4236B ;
2005-04-17 02:20:36 +04:00
break ;
default :
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_WARNING
" unknown CS4236 chip "
" (enhanced version = 0x%x) \n " ,
id ) ;
2005-04-17 02:20:36 +04:00
}
} else if ( ( id & 0x1f ) = = 0x08 ) { /* CS4237B */
2008-07-31 23:03:41 +04:00
chip - > hardware = WSS_HW_CS4237B ;
2005-04-17 02:20:36 +04:00
switch ( id > > 5 ) {
case 4 :
case 5 :
case 6 :
case 7 :
break ;
default :
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_WARNING
" unknown CS4237B chip "
" (enhanced version = 0x%x) \n " ,
id ) ;
2005-04-17 02:20:36 +04:00
}
} else if ( ( id & 0x1f ) = = 0x09 ) { /* CS4238B */
2008-07-31 23:03:41 +04:00
chip - > hardware = WSS_HW_CS4238B ;
2005-04-17 02:20:36 +04:00
switch ( id > > 5 ) {
case 5 :
case 6 :
case 7 :
break ;
default :
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_WARNING
" unknown CS4238B chip "
" (enhanced version = 0x%x) \n " ,
id ) ;
2005-04-17 02:20:36 +04:00
}
} else if ( ( id & 0x1f ) = = 0x1e ) { /* CS4239 */
2008-07-31 23:03:41 +04:00
chip - > hardware = WSS_HW_CS4239 ;
2005-04-17 02:20:36 +04:00
switch ( id > > 5 ) {
case 4 :
case 5 :
case 6 :
break ;
default :
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_WARNING
" unknown CS4239 chip "
" (enhanced version = 0x%x) \n " ,
id ) ;
2005-04-17 02:20:36 +04:00
}
} else {
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_WARNING
" unknown CS4236/CS423xB chip "
" (enhanced version = 0x%x) \n " , id ) ;
2005-04-17 02:20:36 +04:00
}
}
}
return 0 ; /* all things are ok.. */
}
/*
*/
2008-07-31 23:03:41 +04:00
static struct snd_pcm_hardware snd_wss_playback =
2005-04-17 02:20:36 +04:00
{
. info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_SYNC_START ) ,
. formats = ( SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE ) ,
. rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000 ,
. rate_min = 5510 ,
. rate_max = 48000 ,
. channels_min = 1 ,
. channels_max = 2 ,
. buffer_bytes_max = ( 128 * 1024 ) ,
. period_bytes_min = 64 ,
. period_bytes_max = ( 128 * 1024 ) ,
. periods_min = 1 ,
. periods_max = 1024 ,
. fifo_size = 0 ,
} ;
2008-07-31 23:03:41 +04:00
static struct snd_pcm_hardware snd_wss_capture =
2005-04-17 02:20:36 +04:00
{
. info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_SYNC_START ) ,
. formats = ( SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE ) ,
. rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000 ,
. rate_min = 5510 ,
. rate_max = 48000 ,
. channels_min = 1 ,
. channels_max = 2 ,
. buffer_bytes_max = ( 128 * 1024 ) ,
. period_bytes_min = 64 ,
. period_bytes_max = ( 128 * 1024 ) ,
. periods_min = 1 ,
. periods_max = 1024 ,
. fifo_size = 0 ,
} ;
/*
*/
2008-07-31 23:03:41 +04:00
static int snd_wss_playback_open ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_pcm_substream_chip ( substream ) ;
2005-11-17 16:30:42 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
int err ;
2008-07-31 23:03:41 +04:00
runtime - > hw = snd_wss_playback ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:09:32 +04:00
/* hardware limitation of older chipsets */
if ( chip - > hardware & WSS_HW_AD1848_MASK )
runtime - > hw . formats & = ~ ( SNDRV_PCM_FMTBIT_IMA_ADPCM |
SNDRV_PCM_FMTBIT_S16_BE ) ;
2005-04-17 02:20:36 +04:00
/* hardware bug in InterWave chipset */
2008-07-31 23:03:41 +04:00
if ( chip - > hardware = = WSS_HW_INTERWAVE & & chip - > dma1 > 3 )
2008-07-31 23:00:17 +04:00
runtime - > hw . formats & = ~ SNDRV_PCM_FMTBIT_MU_LAW ;
2005-04-17 02:20:36 +04:00
/* hardware limitation of cheap chips */
2008-07-31 23:03:41 +04:00
if ( chip - > hardware = = WSS_HW_CS4235 | |
chip - > hardware = = WSS_HW_CS4239 )
2005-04-17 02:20:36 +04:00
runtime - > hw . formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE ;
snd_pcm_limit_isa_dma_size ( chip - > dma1 , & runtime - > hw . buffer_bytes_max ) ;
snd_pcm_limit_isa_dma_size ( chip - > dma1 , & runtime - > hw . period_bytes_max ) ;
if ( chip - > claim_dma ) {
if ( ( err = chip - > claim_dma ( chip , chip - > dma_private_data , chip - > dma1 ) ) < 0 )
return err ;
}
2008-07-31 23:03:41 +04:00
err = snd_wss_open ( chip , WSS_MODE_PLAY ) ;
if ( err < 0 ) {
2005-04-17 02:20:36 +04:00
if ( chip - > release_dma )
chip - > release_dma ( chip , chip - > dma_private_data , chip - > dma1 ) ;
snd_free_pages ( runtime - > dma_area , runtime - > dma_bytes ) ;
return err ;
}
chip - > playback_substream = substream ;
snd_pcm_set_sync ( substream ) ;
chip - > rate_constraint ( runtime ) ;
return 0 ;
}
2008-07-31 23:03:41 +04:00
static int snd_wss_capture_open ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_pcm_substream_chip ( substream ) ;
2005-11-17 16:30:42 +03:00
struct snd_pcm_runtime * runtime = substream - > runtime ;
2005-04-17 02:20:36 +04:00
int err ;
2008-07-31 23:03:41 +04:00
runtime - > hw = snd_wss_capture ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:09:32 +04:00
/* hardware limitation of older chipsets */
if ( chip - > hardware & WSS_HW_AD1848_MASK )
runtime - > hw . formats & = ~ ( SNDRV_PCM_FMTBIT_IMA_ADPCM |
SNDRV_PCM_FMTBIT_S16_BE ) ;
2005-04-17 02:20:36 +04:00
/* hardware limitation of cheap chips */
2008-07-31 23:03:41 +04:00
if ( chip - > hardware = = WSS_HW_CS4235 | |
2008-07-31 23:11:46 +04:00
chip - > hardware = = WSS_HW_CS4239 | |
chip - > hardware = = WSS_HW_OPTI93X )
runtime - > hw . formats = SNDRV_PCM_FMTBIT_U8 |
SNDRV_PCM_FMTBIT_S16_LE ;
2005-04-17 02:20:36 +04:00
snd_pcm_limit_isa_dma_size ( chip - > dma2 , & runtime - > hw . buffer_bytes_max ) ;
snd_pcm_limit_isa_dma_size ( chip - > dma2 , & runtime - > hw . period_bytes_max ) ;
if ( chip - > claim_dma ) {
if ( ( err = chip - > claim_dma ( chip , chip - > dma_private_data , chip - > dma2 ) ) < 0 )
return err ;
}
2008-07-31 23:03:41 +04:00
err = snd_wss_open ( chip , WSS_MODE_RECORD ) ;
if ( err < 0 ) {
2005-04-17 02:20:36 +04:00
if ( chip - > release_dma )
chip - > release_dma ( chip , chip - > dma_private_data , chip - > dma2 ) ;
snd_free_pages ( runtime - > dma_area , runtime - > dma_bytes ) ;
return err ;
}
chip - > capture_substream = substream ;
snd_pcm_set_sync ( substream ) ;
chip - > rate_constraint ( runtime ) ;
return 0 ;
}
2008-07-31 23:03:41 +04:00
static int snd_wss_playback_close ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_pcm_substream_chip ( substream ) ;
2005-04-17 02:20:36 +04:00
chip - > playback_substream = NULL ;
2008-07-31 23:03:41 +04:00
snd_wss_close ( chip , WSS_MODE_PLAY ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-07-31 23:03:41 +04:00
static int snd_wss_capture_close ( struct snd_pcm_substream * substream )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_pcm_substream_chip ( substream ) ;
2005-04-17 02:20:36 +04:00
chip - > capture_substream = NULL ;
2008-07-31 23:03:41 +04:00
snd_wss_close ( chip , WSS_MODE_RECORD ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-07-31 23:09:32 +04:00
static void snd_wss_thinkpad_twiddle ( struct snd_wss * chip , int on )
{
int tmp ;
if ( ! chip - > thinkpad_flag )
return ;
outb ( 0x1c , AD1848_THINKPAD_CTL_PORT1 ) ;
tmp = inb ( AD1848_THINKPAD_CTL_PORT2 ) ;
if ( on )
/* turn it on */
tmp | = AD1848_THINKPAD_CS4248_ENABLE_BIT ;
else
/* turn it off */
tmp & = ~ AD1848_THINKPAD_CS4248_ENABLE_BIT ;
outb ( tmp , AD1848_THINKPAD_CTL_PORT2 ) ;
}
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PM
/* lowlevel suspend callback for CS4231 */
2008-07-31 23:03:41 +04:00
static void snd_wss_suspend ( struct snd_wss * chip )
2005-04-17 02:20:36 +04:00
{
int reg ;
unsigned long flags ;
2008-07-31 23:00:17 +04:00
2005-11-17 19:00:17 +03:00
snd_pcm_suspend_all ( chip - > pcm ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
for ( reg = 0 ; reg < 32 ; reg + + )
2008-07-31 23:03:41 +04:00
chip - > image [ reg ] = snd_wss_in ( chip , reg ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
2008-07-31 23:09:32 +04:00
if ( chip - > thinkpad_flag )
snd_wss_thinkpad_twiddle ( chip , 0 ) ;
2005-04-17 02:20:36 +04:00
}
/* lowlevel resume callback for CS4231 */
2008-07-31 23:03:41 +04:00
static void snd_wss_resume ( struct snd_wss * chip )
2005-04-17 02:20:36 +04:00
{
int reg ;
unsigned long flags ;
2005-11-18 20:52:39 +03:00
/* int timeout; */
2008-07-31 23:00:17 +04:00
2008-07-31 23:09:32 +04:00
if ( chip - > thinkpad_flag )
snd_wss_thinkpad_twiddle ( chip , 1 ) ;
2008-07-31 23:03:41 +04:00
snd_wss_mce_up ( chip ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
for ( reg = 0 ; reg < 32 ; reg + + ) {
switch ( reg ) {
case CS4231_VERSION :
break ;
default :
2008-07-31 23:03:41 +04:00
snd_wss_out ( chip , reg , chip - > image [ reg ] ) ;
2005-04-17 02:20:36 +04:00
break ;
}
}
2012-07-16 10:30:18 +04:00
/* Yamaha needs this to resume properly */
if ( chip - > hardware = = WSS_HW_OPL3SA2 )
snd_wss_out ( chip , CS4231_PLAYBK_FORMAT ,
chip - > image [ CS4231_PLAYBK_FORMAT ] ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
2005-11-17 19:48:30 +03:00
# if 1
2008-07-31 23:03:41 +04:00
snd_wss_mce_down ( chip ) ;
2005-04-17 02:20:36 +04:00
# else
/* The following is a workaround to avoid freeze after resume on TP600E.
2008-07-31 23:03:41 +04:00
This is the first half of copy of snd_wss_mce_down ( ) , but doesn ' t
2005-04-17 02:20:36 +04:00
include rescheduling . - - iwai
*/
2008-07-31 23:03:41 +04:00
snd_wss_busy_wait ( chip ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
chip - > mce_bit & = ~ CS4231_MCE ;
2008-07-31 23:03:41 +04:00
timeout = wss_inb ( chip , CS4231P ( REGSEL ) ) ;
wss_outb ( chip , CS4231P ( REGSEL ) , chip - > mce_bit | ( timeout & 0x1f ) ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
if ( timeout = = 0x80 )
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_ERR " down [0x%lx]: serious init problem "
" - codec still busy \n " , chip - > port ) ;
2005-04-17 02:20:36 +04:00
if ( ( timeout & CS4231_MCE ) = = 0 | |
2008-07-31 23:03:41 +04:00
! ( chip - > hardware & ( WSS_HW_CS4231_MASK | WSS_HW_CS4232_MASK ) ) ) {
2005-04-17 02:20:36 +04:00
return ;
}
2008-07-31 23:03:41 +04:00
snd_wss_busy_wait ( chip ) ;
2005-04-17 02:20:36 +04:00
# endif
}
# endif /* CONFIG_PM */
2009-11-05 20:32:41 +03:00
static int snd_wss_free ( struct snd_wss * chip )
2005-04-17 02:20:36 +04:00
{
2005-10-10 13:56:31 +04:00
release_and_free_resource ( chip - > res_port ) ;
release_and_free_resource ( chip - > res_cport ) ;
2005-04-17 02:20:36 +04:00
if ( chip - > irq > = 0 ) {
disable_irq ( chip - > irq ) ;
2008-07-31 23:03:41 +04:00
if ( ! ( chip - > hwshare & WSS_HWSHARE_IRQ ) )
2005-04-17 02:20:36 +04:00
free_irq ( chip - > irq , ( void * ) chip ) ;
}
2008-07-31 23:03:41 +04:00
if ( ! ( chip - > hwshare & WSS_HWSHARE_DMA1 ) & & chip - > dma1 > = 0 ) {
2005-04-17 02:20:36 +04:00
snd_dma_disable ( chip - > dma1 ) ;
free_dma ( chip - > dma1 ) ;
}
2008-07-31 23:03:41 +04:00
if ( ! ( chip - > hwshare & WSS_HWSHARE_DMA2 ) & &
chip - > dma2 > = 0 & & chip - > dma2 ! = chip - > dma1 ) {
2005-04-17 02:20:36 +04:00
snd_dma_disable ( chip - > dma2 ) ;
free_dma ( chip - > dma2 ) ;
}
if ( chip - > timer )
snd_device_free ( chip - > card , chip - > timer ) ;
kfree ( chip ) ;
return 0 ;
}
2008-07-31 23:03:41 +04:00
static int snd_wss_dev_free ( struct snd_device * device )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = device - > device_data ;
return snd_wss_free ( chip ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-31 23:03:41 +04:00
const char * snd_wss_chip_id ( struct snd_wss * chip )
2005-04-17 02:20:36 +04:00
{
switch ( chip - > hardware ) {
2008-07-31 23:03:41 +04:00
case WSS_HW_CS4231 :
return " CS4231 " ;
case WSS_HW_CS4231A :
return " CS4231A " ;
case WSS_HW_CS4232 :
return " CS4232 " ;
case WSS_HW_CS4232A :
return " CS4232A " ;
case WSS_HW_CS4235 :
return " CS4235 " ;
case WSS_HW_CS4236 :
return " CS4236 " ;
case WSS_HW_CS4236B :
return " CS4236B " ;
case WSS_HW_CS4237B :
return " CS4237B " ;
case WSS_HW_CS4238B :
return " CS4238B " ;
case WSS_HW_CS4239 :
return " CS4239 " ;
case WSS_HW_INTERWAVE :
return " AMD InterWave " ;
case WSS_HW_OPL3SA2 :
return chip - > card - > shortname ;
case WSS_HW_AD1845 :
return " AD1845 " ;
case WSS_HW_OPTI93X :
return " OPTi 93x " ;
2008-07-31 23:09:32 +04:00
case WSS_HW_AD1847 :
return " AD1847 " ;
case WSS_HW_AD1848 :
return " AD1848 " ;
case WSS_HW_CS4248 :
return " CS4248 " ;
case WSS_HW_CMI8330 :
return " CMI8330/C3D " ;
2008-07-31 23:03:41 +04:00
default :
return " ??? " ;
2005-04-17 02:20:36 +04:00
}
}
2008-07-31 23:03:41 +04:00
EXPORT_SYMBOL ( snd_wss_chip_id ) ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:03:41 +04:00
static int snd_wss_new ( struct snd_card * card ,
2005-04-17 02:20:36 +04:00
unsigned short hardware ,
unsigned short hwshare ,
2008-07-31 23:03:41 +04:00
struct snd_wss * * rchip )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip ;
2005-04-17 02:20:36 +04:00
* rchip = NULL ;
2005-09-09 16:21:17 +04:00
chip = kzalloc ( sizeof ( * chip ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( chip = = NULL )
return - ENOMEM ;
chip - > hardware = hardware ;
chip - > hwshare = hwshare ;
spin_lock_init ( & chip - > reg_lock ) ;
2006-01-16 18:33:08 +03:00
mutex_init ( & chip - > mce_mutex ) ;
mutex_init ( & chip - > open_mutex ) ;
2005-04-17 02:20:36 +04:00
chip - > card = card ;
2008-07-31 23:03:41 +04:00
chip - > rate_constraint = snd_wss_xrate ;
chip - > set_playback_format = snd_wss_playback_format ;
chip - > set_capture_format = snd_wss_capture_format ;
if ( chip - > hardware = = WSS_HW_OPTI93X )
2008-06-10 01:07:28 +04:00
memcpy ( & chip - > image , & snd_opti93x_original_image ,
sizeof ( snd_opti93x_original_image ) ) ;
else
2008-07-31 23:03:41 +04:00
memcpy ( & chip - > image , & snd_wss_original_image ,
sizeof ( snd_wss_original_image ) ) ;
2008-07-31 23:10:47 +04:00
if ( chip - > hardware & WSS_HW_AD1848_MASK ) {
chip - > image [ CS4231_PIN_CTRL ] = 0 ;
chip - > image [ CS4231_TEST_INIT ] = 0 ;
}
2008-06-10 01:07:28 +04:00
2008-07-31 23:03:41 +04:00
* rchip = chip ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-07-31 23:03:41 +04:00
int snd_wss_create ( struct snd_card * card ,
unsigned long port ,
unsigned long cport ,
2005-04-17 02:20:36 +04:00
int irq , int dma1 , int dma2 ,
unsigned short hardware ,
unsigned short hwshare ,
2008-07-31 23:03:41 +04:00
struct snd_wss * * rchip )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:30:42 +03:00
static struct snd_device_ops ops = {
2008-07-31 23:03:41 +04:00
. dev_free = snd_wss_dev_free ,
2005-04-17 02:20:36 +04:00
} ;
2008-07-31 23:03:41 +04:00
struct snd_wss * chip ;
2005-04-17 02:20:36 +04:00
int err ;
2008-07-31 23:03:41 +04:00
err = snd_wss_new ( card , hardware , hwshare , & chip ) ;
2005-04-17 02:20:36 +04:00
if ( err < 0 )
return err ;
2008-07-31 23:00:17 +04:00
2005-04-17 02:20:36 +04:00
chip - > irq = - 1 ;
chip - > dma1 = - 1 ;
chip - > dma2 = - 1 ;
2008-07-31 23:10:47 +04:00
chip - > res_port = request_region ( port , 4 , " WSS " ) ;
2008-07-31 23:03:41 +04:00
if ( ! chip - > res_port ) {
snd_printk ( KERN_ERR " wss: can't grab port 0x%lx \n " , port ) ;
snd_wss_free ( chip ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
chip - > port = port ;
2008-07-31 23:03:41 +04:00
if ( ( long ) cport > = 0 ) {
chip - > res_cport = request_region ( cport , 8 , " CS4232 Control " ) ;
if ( ! chip - > res_cport ) {
snd_printk ( KERN_ERR
" wss: can't grab control port 0x%lx \n " , cport ) ;
snd_wss_free ( chip ) ;
return - ENODEV ;
}
2005-04-17 02:20:36 +04:00
}
chip - > cport = cport ;
2008-07-31 23:03:41 +04:00
if ( ! ( hwshare & WSS_HWSHARE_IRQ ) )
2011-09-22 12:59:20 +04:00
if ( request_irq ( irq , snd_wss_interrupt , 0 ,
2008-07-31 23:10:47 +04:00
" WSS " , ( void * ) chip ) ) {
2008-07-31 23:03:41 +04:00
snd_printk ( KERN_ERR " wss: can't grab IRQ %d \n " , irq ) ;
snd_wss_free ( chip ) ;
return - EBUSY ;
}
2005-04-17 02:20:36 +04:00
chip - > irq = irq ;
2008-07-31 23:10:47 +04:00
if ( ! ( hwshare & WSS_HWSHARE_DMA1 ) & & request_dma ( dma1 , " WSS - 1 " ) ) {
2008-07-31 23:03:41 +04:00
snd_printk ( KERN_ERR " wss: can't grab DMA1 %d \n " , dma1 ) ;
snd_wss_free ( chip ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
chip - > dma1 = dma1 ;
2008-07-31 23:03:41 +04:00
if ( ! ( hwshare & WSS_HWSHARE_DMA2 ) & & dma1 ! = dma2 & &
2008-07-31 23:10:47 +04:00
dma2 > = 0 & & request_dma ( dma2 , " WSS - 2 " ) ) {
2008-07-31 23:03:41 +04:00
snd_printk ( KERN_ERR " wss: can't grab DMA2 %d \n " , dma2 ) ;
snd_wss_free ( chip ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
if ( dma1 = = dma2 | | dma2 < 0 ) {
chip - > single_dma = 1 ;
chip - > dma2 = chip - > dma1 ;
} else
chip - > dma2 = dma2 ;
2008-07-31 23:10:47 +04:00
if ( hardware = = WSS_HW_THINKPAD ) {
chip - > thinkpad_flag = 1 ;
chip - > hardware = WSS_HW_DETECT ; /* reset */
snd_wss_thinkpad_twiddle ( chip , 1 ) ;
}
2005-04-17 02:20:36 +04:00
/* global setup */
2008-07-31 23:03:41 +04:00
if ( snd_wss_probe ( chip ) < 0 ) {
snd_wss_free ( chip ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
2008-07-31 23:03:41 +04:00
snd_wss_init ( chip ) ;
2005-04-17 02:20:36 +04:00
2005-11-17 19:51:00 +03:00
#if 0
2008-07-31 23:03:41 +04:00
if ( chip - > hardware & WSS_HW_CS4232_MASK ) {
2005-04-17 02:20:36 +04:00
if ( chip - > res_cport = = NULL )
2009-02-05 17:45:05 +03:00
snd_printk ( KERN_ERR " CS4232 control port features are "
" not accessible \n " ) ;
2005-04-17 02:20:36 +04:00
}
2005-11-17 19:51:00 +03:00
# endif
2005-04-17 02:20:36 +04:00
/* Register device */
2008-07-31 23:03:41 +04:00
err = snd_device_new ( card , SNDRV_DEV_LOWLEVEL , chip , & ops ) ;
if ( err < 0 ) {
snd_wss_free ( chip ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
# ifdef CONFIG_PM
/* Power Management */
2008-07-31 23:03:41 +04:00
chip - > suspend = snd_wss_suspend ;
chip - > resume = snd_wss_resume ;
2005-04-17 02:20:36 +04:00
# endif
* rchip = chip ;
return 0 ;
}
2008-07-31 23:03:41 +04:00
EXPORT_SYMBOL ( snd_wss_create ) ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:03:41 +04:00
static struct snd_pcm_ops snd_wss_playback_ops = {
. open = snd_wss_playback_open ,
. close = snd_wss_playback_close ,
2005-04-17 02:20:36 +04:00
. ioctl = snd_pcm_lib_ioctl ,
2008-07-31 23:03:41 +04:00
. hw_params = snd_wss_playback_hw_params ,
. hw_free = snd_wss_playback_hw_free ,
. prepare = snd_wss_playback_prepare ,
. trigger = snd_wss_trigger ,
. pointer = snd_wss_playback_pointer ,
2005-04-17 02:20:36 +04:00
} ;
2008-07-31 23:03:41 +04:00
static struct snd_pcm_ops snd_wss_capture_ops = {
. open = snd_wss_capture_open ,
. close = snd_wss_capture_close ,
2005-04-17 02:20:36 +04:00
. ioctl = snd_pcm_lib_ioctl ,
2008-07-31 23:03:41 +04:00
. hw_params = snd_wss_capture_hw_params ,
. hw_free = snd_wss_capture_hw_free ,
. prepare = snd_wss_capture_prepare ,
. trigger = snd_wss_trigger ,
. pointer = snd_wss_capture_pointer ,
2005-04-17 02:20:36 +04:00
} ;
2015-01-02 14:24:43 +03:00
int snd_wss_pcm ( struct snd_wss * chip , int device )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:30:42 +03:00
struct snd_pcm * pcm ;
2005-04-17 02:20:36 +04:00
int err ;
2008-07-31 23:09:32 +04:00
err = snd_pcm_new ( chip - > card , " WSS " , device , 1 , 1 , & pcm ) ;
if ( err < 0 )
2005-04-17 02:20:36 +04:00
return err ;
2008-07-31 23:03:41 +04:00
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_PLAYBACK , & snd_wss_playback_ops ) ;
snd_pcm_set_ops ( pcm , SNDRV_PCM_STREAM_CAPTURE , & snd_wss_capture_ops ) ;
2008-07-31 23:00:17 +04:00
2005-04-17 02:20:36 +04:00
/* global setup */
pcm - > private_data = chip ;
pcm - > info_flags = 0 ;
if ( chip - > single_dma )
pcm - > info_flags | = SNDRV_PCM_INFO_HALF_DUPLEX ;
2008-07-31 23:03:41 +04:00
if ( chip - > hardware ! = WSS_HW_INTERWAVE )
2005-04-17 02:20:36 +04:00
pcm - > info_flags | = SNDRV_PCM_INFO_JOINT_DUPLEX ;
2008-07-31 23:03:41 +04:00
strcpy ( pcm - > name , snd_wss_chip_id ( chip ) ) ;
2005-04-17 02:20:36 +04:00
snd_pcm_lib_preallocate_pages_for_all ( pcm , SNDRV_DMA_TYPE_DEV ,
snd_dma_isa_data ( ) ,
64 * 1024 , chip - > dma1 > 3 | | chip - > dma2 > 3 ? 128 * 1024 : 64 * 1024 ) ;
chip - > pcm = pcm ;
return 0 ;
}
2008-07-31 23:03:41 +04:00
EXPORT_SYMBOL ( snd_wss_pcm ) ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:03:41 +04:00
static void snd_wss_timer_free ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = timer - > private_data ;
2005-04-17 02:20:36 +04:00
chip - > timer = NULL ;
}
2015-01-02 14:24:43 +03:00
int snd_wss_timer ( struct snd_wss * chip , int device )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:30:42 +03:00
struct snd_timer * timer ;
struct snd_timer_id tid ;
2005-04-17 02:20:36 +04:00
int err ;
/* Timer initialization */
tid . dev_class = SNDRV_TIMER_CLASS_CARD ;
tid . dev_sclass = SNDRV_TIMER_SCLASS_NONE ;
tid . card = chip - > card - > number ;
tid . device = device ;
tid . subdevice = 0 ;
if ( ( err = snd_timer_new ( chip - > card , " CS4231 " , & tid , & timer ) ) < 0 )
return err ;
2008-07-31 23:03:41 +04:00
strcpy ( timer - > name , snd_wss_chip_id ( chip ) ) ;
2005-04-17 02:20:36 +04:00
timer - > private_data = chip ;
2008-07-31 23:03:41 +04:00
timer - > private_free = snd_wss_timer_free ;
timer - > hw = snd_wss_timer_table ;
2005-04-17 02:20:36 +04:00
chip - > timer = timer ;
return 0 ;
}
2008-07-31 23:03:41 +04:00
EXPORT_SYMBOL ( snd_wss_timer ) ;
2008-07-31 23:00:17 +04:00
2005-04-17 02:20:36 +04:00
/*
* MIXER part
*/
2008-07-31 23:03:41 +04:00
static int snd_wss_info_mux ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
2005-04-17 02:20:36 +04:00
{
2014-10-20 20:15:06 +04:00
static const char * const texts [ 4 ] = {
2005-04-17 02:20:36 +04:00
" Line " , " Aux " , " Mic " , " Mix "
} ;
2014-10-20 20:15:06 +04:00
static const char * const opl3sa_texts [ 4 ] = {
2005-04-17 02:20:36 +04:00
" Line " , " CD " , " Mic " , " Mix "
} ;
2014-10-20 20:15:06 +04:00
static const char * const gusmax_texts [ 4 ] = {
2005-04-17 02:20:36 +04:00
" Line " , " Synth " , " Mic " , " Mix "
} ;
2014-10-20 20:15:06 +04:00
const char * const * ptexts = texts ;
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_kcontrol_chip ( kcontrol ) ;
2005-04-17 02:20:36 +04:00
2008-08-08 19:11:45 +04:00
if ( snd_BUG_ON ( ! chip - > card ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
if ( ! strcmp ( chip - > card - > driver , " GUS MAX " ) )
ptexts = gusmax_texts ;
switch ( chip - > hardware ) {
2008-07-31 23:03:41 +04:00
case WSS_HW_INTERWAVE :
ptexts = gusmax_texts ;
break ;
2009-12-10 22:40:18 +03:00
case WSS_HW_OPTI93X :
2008-07-31 23:03:41 +04:00
case WSS_HW_OPL3SA2 :
ptexts = opl3sa_texts ;
break ;
2005-04-17 02:20:36 +04:00
}
2014-10-20 20:15:06 +04:00
return snd_ctl_enum_info ( uinfo , 2 , 4 , ptexts ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-31 23:03:41 +04:00
static int snd_wss_get_mux ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_kcontrol_chip ( kcontrol ) ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
2008-07-31 23:00:17 +04:00
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
ucontrol - > value . enumerated . item [ 0 ] = ( chip - > image [ CS4231_LEFT_INPUT ] & CS4231_MIXS_ALL ) > > 6 ;
ucontrol - > value . enumerated . item [ 1 ] = ( chip - > image [ CS4231_RIGHT_INPUT ] & CS4231_MIXS_ALL ) > > 6 ;
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
return 0 ;
}
2008-07-31 23:03:41 +04:00
static int snd_wss_put_mux ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_kcontrol_chip ( kcontrol ) ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
unsigned short left , right ;
int change ;
2008-07-31 23:00:17 +04:00
2005-04-17 02:20:36 +04:00
if ( ucontrol - > value . enumerated . item [ 0 ] > 3 | |
ucontrol - > value . enumerated . item [ 1 ] > 3 )
return - EINVAL ;
left = ucontrol - > value . enumerated . item [ 0 ] < < 6 ;
right = ucontrol - > value . enumerated . item [ 1 ] < < 6 ;
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
left = ( chip - > image [ CS4231_LEFT_INPUT ] & ~ CS4231_MIXS_ALL ) | left ;
right = ( chip - > image [ CS4231_RIGHT_INPUT ] & ~ CS4231_MIXS_ALL ) | right ;
change = left ! = chip - > image [ CS4231_LEFT_INPUT ] | |
2008-07-31 23:03:41 +04:00
right ! = chip - > image [ CS4231_RIGHT_INPUT ] ;
snd_wss_out ( chip , CS4231_LEFT_INPUT , left ) ;
snd_wss_out ( chip , CS4231_RIGHT_INPUT , right ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
return change ;
}
2008-07-31 23:03:41 +04:00
int snd_wss_info_single ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
2005-04-17 02:20:36 +04:00
{
int mask = ( kcontrol - > private_value > > 16 ) & 0xff ;
uinfo - > type = mask = = 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER ;
uinfo - > count = 1 ;
uinfo - > value . integer . min = 0 ;
uinfo - > value . integer . max = mask ;
return 0 ;
}
2008-07-31 23:03:41 +04:00
EXPORT_SYMBOL ( snd_wss_info_single ) ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:03:41 +04:00
int snd_wss_get_single ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_kcontrol_chip ( kcontrol ) ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
int reg = kcontrol - > private_value & 0xff ;
int shift = ( kcontrol - > private_value > > 8 ) & 0xff ;
int mask = ( kcontrol - > private_value > > 16 ) & 0xff ;
int invert = ( kcontrol - > private_value > > 24 ) & 0xff ;
2008-07-31 23:00:17 +04:00
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
ucontrol - > value . integer . value [ 0 ] = ( chip - > image [ reg ] > > shift ) & mask ;
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
if ( invert )
ucontrol - > value . integer . value [ 0 ] = mask - ucontrol - > value . integer . value [ 0 ] ;
return 0 ;
}
2008-07-31 23:03:41 +04:00
EXPORT_SYMBOL ( snd_wss_get_single ) ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:03:41 +04:00
int snd_wss_put_single ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_kcontrol_chip ( kcontrol ) ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
int reg = kcontrol - > private_value & 0xff ;
int shift = ( kcontrol - > private_value > > 8 ) & 0xff ;
int mask = ( kcontrol - > private_value > > 16 ) & 0xff ;
int invert = ( kcontrol - > private_value > > 24 ) & 0xff ;
int change ;
unsigned short val ;
2008-07-31 23:00:17 +04:00
2005-04-17 02:20:36 +04:00
val = ( ucontrol - > value . integer . value [ 0 ] & mask ) ;
if ( invert )
val = mask - val ;
val < < = shift ;
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
val = ( chip - > image [ reg ] & ~ ( mask < < shift ) ) | val ;
change = val ! = chip - > image [ reg ] ;
2008-07-31 23:03:41 +04:00
snd_wss_out ( chip , reg , val ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
return change ;
}
2008-07-31 23:03:41 +04:00
EXPORT_SYMBOL ( snd_wss_put_single ) ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:03:41 +04:00
int snd_wss_info_double ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
2005-04-17 02:20:36 +04:00
{
int mask = ( kcontrol - > private_value > > 24 ) & 0xff ;
uinfo - > type = mask = = 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER ;
uinfo - > count = 2 ;
uinfo - > value . integer . min = 0 ;
uinfo - > value . integer . max = mask ;
return 0 ;
}
2008-07-31 23:03:41 +04:00
EXPORT_SYMBOL ( snd_wss_info_double ) ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:03:41 +04:00
int snd_wss_get_double ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_kcontrol_chip ( kcontrol ) ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
int left_reg = kcontrol - > private_value & 0xff ;
int right_reg = ( kcontrol - > private_value > > 8 ) & 0xff ;
int shift_left = ( kcontrol - > private_value > > 16 ) & 0x07 ;
int shift_right = ( kcontrol - > private_value > > 19 ) & 0x07 ;
int mask = ( kcontrol - > private_value > > 24 ) & 0xff ;
int invert = ( kcontrol - > private_value > > 22 ) & 1 ;
2008-07-31 23:00:17 +04:00
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
ucontrol - > value . integer . value [ 0 ] = ( chip - > image [ left_reg ] > > shift_left ) & mask ;
ucontrol - > value . integer . value [ 1 ] = ( chip - > image [ right_reg ] > > shift_right ) & mask ;
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
if ( invert ) {
ucontrol - > value . integer . value [ 0 ] = mask - ucontrol - > value . integer . value [ 0 ] ;
ucontrol - > value . integer . value [ 1 ] = mask - ucontrol - > value . integer . value [ 1 ] ;
}
return 0 ;
}
2008-07-31 23:03:41 +04:00
EXPORT_SYMBOL ( snd_wss_get_double ) ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:03:41 +04:00
int snd_wss_put_double ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
2005-04-17 02:20:36 +04:00
{
2008-07-31 23:03:41 +04:00
struct snd_wss * chip = snd_kcontrol_chip ( kcontrol ) ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
int left_reg = kcontrol - > private_value & 0xff ;
int right_reg = ( kcontrol - > private_value > > 8 ) & 0xff ;
int shift_left = ( kcontrol - > private_value > > 16 ) & 0x07 ;
int shift_right = ( kcontrol - > private_value > > 19 ) & 0x07 ;
int mask = ( kcontrol - > private_value > > 24 ) & 0xff ;
int invert = ( kcontrol - > private_value > > 22 ) & 1 ;
int change ;
unsigned short val1 , val2 ;
2008-07-31 23:00:17 +04:00
2005-04-17 02:20:36 +04:00
val1 = ucontrol - > value . integer . value [ 0 ] & mask ;
val2 = ucontrol - > value . integer . value [ 1 ] & mask ;
if ( invert ) {
val1 = mask - val1 ;
val2 = mask - val2 ;
}
val1 < < = shift_left ;
val2 < < = shift_right ;
spin_lock_irqsave ( & chip - > reg_lock , flags ) ;
2008-07-31 23:08:32 +04:00
if ( left_reg ! = right_reg ) {
val1 = ( chip - > image [ left_reg ] & ~ ( mask < < shift_left ) ) | val1 ;
val2 = ( chip - > image [ right_reg ] & ~ ( mask < < shift_right ) ) | val2 ;
change = val1 ! = chip - > image [ left_reg ] | |
val2 ! = chip - > image [ right_reg ] ;
snd_wss_out ( chip , left_reg , val1 ) ;
snd_wss_out ( chip , right_reg , val2 ) ;
} else {
mask = ( mask < < shift_left ) | ( mask < < shift_right ) ;
val1 = ( chip - > image [ left_reg ] & ~ mask ) | val1 | val2 ;
change = val1 ! = chip - > image [ left_reg ] ;
snd_wss_out ( chip , left_reg , val1 ) ;
}
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & chip - > reg_lock , flags ) ;
return change ;
}
2008-07-31 23:03:41 +04:00
EXPORT_SYMBOL ( snd_wss_put_double ) ;
2008-07-31 23:08:32 +04:00
static const DECLARE_TLV_DB_SCALE ( db_scale_6bit , - 9450 , 150 , 0 ) ;
static const DECLARE_TLV_DB_SCALE ( db_scale_5bit_12db_max , - 3450 , 150 , 0 ) ;
static const DECLARE_TLV_DB_SCALE ( db_scale_rec_gain , 0 , 150 , 0 ) ;
2009-10-10 12:25:39 +04:00
static const DECLARE_TLV_DB_SCALE ( db_scale_4bit , - 4500 , 300 , 0 ) ;
2008-07-31 23:08:32 +04:00
2008-07-31 23:03:41 +04:00
static struct snd_kcontrol_new snd_wss_controls [ ] = {
WSS_DOUBLE ( " PCM Playback Switch " , 0 ,
CS4231_LEFT_OUTPUT , CS4231_RIGHT_OUTPUT , 7 , 7 , 1 , 1 ) ,
2009-10-10 12:25:39 +04:00
WSS_DOUBLE_TLV ( " PCM Playback Volume " , 0 ,
CS4231_LEFT_OUTPUT , CS4231_RIGHT_OUTPUT , 0 , 0 , 63 , 1 ,
db_scale_6bit ) ,
2008-07-31 23:03:41 +04:00
WSS_DOUBLE ( " Aux Playback Switch " , 0 ,
CS4231_AUX1_LEFT_INPUT , CS4231_AUX1_RIGHT_INPUT , 7 , 7 , 1 , 1 ) ,
2009-10-10 12:25:39 +04:00
WSS_DOUBLE_TLV ( " Aux Playback Volume " , 0 ,
CS4231_AUX1_LEFT_INPUT , CS4231_AUX1_RIGHT_INPUT , 0 , 0 , 31 , 1 ,
db_scale_5bit_12db_max ) ,
2008-07-31 23:03:41 +04:00
WSS_DOUBLE ( " Aux Playback Switch " , 1 ,
CS4231_AUX2_LEFT_INPUT , CS4231_AUX2_RIGHT_INPUT , 7 , 7 , 1 , 1 ) ,
2009-10-10 12:25:39 +04:00
WSS_DOUBLE_TLV ( " Aux Playback Volume " , 1 ,
CS4231_AUX2_LEFT_INPUT , CS4231_AUX2_RIGHT_INPUT , 0 , 0 , 31 , 1 ,
db_scale_5bit_12db_max ) ,
WSS_DOUBLE_TLV ( " Capture Volume " , 0 , CS4231_LEFT_INPUT , CS4231_RIGHT_INPUT ,
0 , 0 , 15 , 0 , db_scale_rec_gain ) ,
2005-04-17 02:20:36 +04:00
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " Capture Source " ,
2008-07-31 23:03:41 +04:00
. info = snd_wss_info_mux ,
. get = snd_wss_get_mux ,
. put = snd_wss_put_mux ,
2005-04-17 02:20:36 +04:00
} ,
2009-11-17 20:34:54 +03:00
WSS_DOUBLE ( " Mic Boost (+20dB) " , 0 ,
2008-07-31 23:03:41 +04:00
CS4231_LEFT_INPUT , CS4231_RIGHT_INPUT , 5 , 5 , 1 , 0 ) ,
WSS_SINGLE ( " Loopback Capture Switch " , 0 ,
CS4231_LOOPBACK , 0 , 1 , 0 ) ,
2009-10-10 12:25:39 +04:00
WSS_SINGLE_TLV ( " Loopback Capture Volume " , 0 , CS4231_LOOPBACK , 2 , 63 , 1 ,
db_scale_6bit ) ,
2009-10-11 14:38:49 +04:00
WSS_DOUBLE ( " Line Playback Switch " , 0 ,
CS4231_LEFT_LINE_IN , CS4231_RIGHT_LINE_IN , 7 , 7 , 1 , 1 ) ,
WSS_DOUBLE_TLV ( " Line Playback Volume " , 0 ,
CS4231_LEFT_LINE_IN , CS4231_RIGHT_LINE_IN , 0 , 0 , 31 , 1 ,
db_scale_5bit_12db_max ) ,
2009-11-17 20:34:54 +03:00
WSS_SINGLE ( " Beep Playback Switch " , 0 ,
2009-10-11 14:38:49 +04:00
CS4231_MONO_CTRL , 7 , 1 , 1 ) ,
2009-11-17 20:34:54 +03:00
WSS_SINGLE_TLV ( " Beep Playback Volume " , 0 ,
2009-10-11 14:38:49 +04:00
CS4231_MONO_CTRL , 0 , 15 , 1 ,
db_scale_4bit ) ,
WSS_SINGLE ( " Mono Output Playback Switch " , 0 ,
CS4231_MONO_CTRL , 6 , 1 , 1 ) ,
2009-11-17 20:34:54 +03:00
WSS_SINGLE ( " Beep Bypass Playback Switch " , 0 ,
2009-10-11 14:38:49 +04:00
CS4231_MONO_CTRL , 5 , 1 , 0 ) ,
2005-04-17 02:20:36 +04:00
} ;
2008-07-31 23:00:17 +04:00
2008-07-31 23:03:41 +04:00
int snd_wss_mixer ( struct snd_wss * chip )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:30:42 +03:00
struct snd_card * card ;
2005-04-17 02:20:36 +04:00
unsigned int idx ;
int err ;
2009-12-10 22:40:18 +03:00
int count = ARRAY_SIZE ( snd_wss_controls ) ;
2005-04-17 02:20:36 +04:00
2008-08-08 19:11:45 +04:00
if ( snd_BUG_ON ( ! chip | | ! chip - > pcm ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
card = chip - > card ;
strcpy ( card - > mixername , chip - > pcm - > name ) ;
2009-12-10 22:40:18 +03:00
/* Use only the first 11 entries on AD1848 */
if ( chip - > hardware & WSS_HW_AD1848_MASK )
count = 11 ;
/* There is no loopback on OPTI93X */
else if ( chip - > hardware = = WSS_HW_OPTI93X )
count = 9 ;
for ( idx = 0 ; idx < count ; idx + + ) {
err = snd_ctl_add ( card ,
snd_ctl_new1 ( & snd_wss_controls [ idx ] ,
chip ) ) ;
if ( err < 0 )
return err ;
2009-10-11 14:38:49 +04:00
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-07-31 23:03:41 +04:00
EXPORT_SYMBOL ( snd_wss_mixer ) ;
2005-04-17 02:20:36 +04:00
2008-07-31 23:09:32 +04:00
const struct snd_pcm_ops * snd_wss_get_pcm_ops ( int direction )
{
return direction = = SNDRV_PCM_STREAM_PLAYBACK ?
& snd_wss_playback_ops : & snd_wss_capture_ops ;
}
EXPORT_SYMBOL ( snd_wss_get_pcm_ops ) ;
2005-04-17 02:20:36 +04:00
/*
* INIT part
*/
2008-07-31 23:03:41 +04:00
static int __init alsa_wss_init ( void )
2005-04-17 02:20:36 +04:00
{
return 0 ;
}
2008-07-31 23:03:41 +04:00
static void __exit alsa_wss_exit ( void )
2005-04-17 02:20:36 +04:00
{
}
2008-07-31 23:03:41 +04:00
module_init ( alsa_wss_init ) ;
module_exit ( alsa_wss_exit ) ;