2005-11-09 08:37:25 +03:00
/*
* wm8775 - driver version 0.0 .1
*
* Copyright ( C ) 2004 Ulf Eklund < ivtv at eklund . to >
*
* Based on saa7115 driver
*
* 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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/ioctl.h>
# include <asm/uaccess.h>
# include <linux/i2c.h>
2005-11-09 08:38:49 +03:00
# include <linux/i2c-id.h>
2005-11-09 08:37:25 +03:00
# include <linux/videodev.h>
# include <media/audiochip.h>
MODULE_DESCRIPTION ( " wm8775 driver " ) ;
MODULE_AUTHOR ( " Ulf Eklund " ) ;
MODULE_LICENSE ( " GPL " ) ;
# define wm8775_err(fmt, arg...) do { \
printk ( KERN_ERR " %s %d-%04x: " fmt , client - > driver - > name , \
2005-11-09 08:37:43 +03:00
i2c_adapter_id ( client - > adapter ) , client - > addr , # # arg ) ; } while ( 0 )
2005-11-09 08:37:25 +03:00
# define wm8775_info(fmt, arg...) do { \
printk ( KERN_INFO " %s %d-%04x: " fmt , client - > driver - > name , \
2005-11-09 08:37:43 +03:00
i2c_adapter_id ( client - > adapter ) , client - > addr , # # arg ) ; } while ( 0 )
2005-11-09 08:37:25 +03:00
static unsigned short normal_i2c [ ] = { 0x36 > > 1 , I2C_CLIENT_END } ;
I2C_CLIENT_INSMOD ;
/* ----------------------------------------------------------------------- */
enum {
R7 = 7 , R11 = 11 ,
R12 , R13 , R14 , R15 , R16 , R17 , R18 , R19 , R20 , R21 , R23 = 23 ,
TOT_REGS
} ;
struct wm8775_state {
u8 input ; /* Last selected input (0-0xf) */
u8 muted ;
} ;
static int wm8775_write ( struct i2c_client * client , int reg , u16 val )
{
int i ;
if ( reg < 0 | | reg > = TOT_REGS ) {
wm8775_err ( " Invalid register R%d \n " , reg ) ;
return - 1 ;
}
for ( i = 0 ; i < 3 ; i + + ) {
if ( i2c_smbus_write_byte_data ( client , ( reg < < 1 ) |
( val > > 8 ) , val & 0xff ) = = 0 ) {
return 0 ;
}
}
wm8775_err ( " I2C: cannot write %03x to register R%d \n " , val , reg ) ;
return - 1 ;
}
static int wm8775_command ( struct i2c_client * client , unsigned int cmd ,
void * arg )
{
struct wm8775_state * state = i2c_get_clientdata ( client ) ;
int * input = arg ;
switch ( cmd ) {
case AUDC_SET_INPUT :
wm8775_write ( client , R21 , 0x0c0 ) ;
wm8775_write ( client , R14 , 0x1d4 ) ;
wm8775_write ( client , R15 , 0x1d4 ) ;
if ( * input = = AUDIO_RADIO ) {
wm8775_write ( client , R21 , 0x108 ) ;
state - > input = 8 ;
state - > muted = 0 ;
break ;
}
if ( * input = = AUDIO_MUTE ) {
state - > muted = 1 ;
break ;
}
if ( * input = = AUDIO_UNMUTE ) {
wm8775_write ( client , R21 , 0x100 + state - > input ) ;
state - > muted = 0 ;
break ;
}
/* All other inputs... */
wm8775_write ( client , R21 , 0x102 ) ;
state - > input = 2 ;
state - > muted = 0 ;
break ;
2005-11-09 08:37:50 +03:00
case VIDIOC_LOG_STATUS :
wm8775_info ( " Input: %s%s \n " ,
state - > input = = 8 ? " radio " : " default " ,
state - > muted ? " (muted) " : " " ) ;
break ;
2005-11-09 08:37:25 +03:00
2005-11-09 08:37:50 +03:00
case VIDIOC_S_FREQUENCY :
2005-11-09 08:37:25 +03:00
/* If I remove this, then it can happen that I have no
sound the first time I tune from static to a valid channel .
It ' s difficult to reproduce and is almost certainly related
to the zero cross detect circuit . */
wm8775_write ( client , R21 , 0x0c0 ) ;
wm8775_write ( client , R14 , 0x1d4 ) ;
wm8775_write ( client , R15 , 0x1d4 ) ;
wm8775_write ( client , R21 , 0x100 + state - > input ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
/* ----------------------------------------------------------------------- */
/* i2c implementation */
/*
* Generic i2c probe
* concerning the addresses : i2c wants 7 bit ( without the r / w bit ) , so ' > > 1 '
*/
static struct i2c_driver i2c_driver ;
static int wm8775_attach ( struct i2c_adapter * adapter , int address , int kind )
{
struct i2c_client * client ;
struct wm8775_state * state ;
/* Check if the adapter supports the needed features */
if ( ! i2c_check_functionality ( adapter , I2C_FUNC_SMBUS_BYTE_DATA ) )
return 0 ;
client = kmalloc ( sizeof ( struct i2c_client ) , GFP_KERNEL ) ;
if ( client = = 0 )
return - ENOMEM ;
memset ( client , 0 , sizeof ( struct i2c_client ) ) ;
client - > addr = address ;
client - > adapter = adapter ;
client - > driver = & i2c_driver ;
client - > flags = I2C_CLIENT_ALLOW_USE ;
snprintf ( client - > name , sizeof ( client - > name ) - 1 , " wm8775 " ) ;
wm8775_info ( " chip found @ 0x%x (%s) \n " , address < < 1 , adapter - > name ) ;
state = kmalloc ( sizeof ( struct wm8775_state ) , GFP_KERNEL ) ;
if ( state = = NULL ) {
kfree ( client ) ;
return - ENOMEM ;
}
state - > input = 2 ;
state - > muted = 0 ;
i2c_set_clientdata ( client , state ) ;
/* initialize wm8775 */
wm8775_write ( client , R23 , 0x000 ) ; /* RESET */
wm8775_write ( client , R7 , 0x000 ) ; /* Disable zero cross detect timeout */
wm8775_write ( client , R11 , 0x021 ) ; /* Left justified, 24-bit mode */
wm8775_write ( client , R12 , 0x102 ) ; /* Master mode, clock ratio 256fs */
wm8775_write ( client , R13 , 0x000 ) ; /* Powered up */
wm8775_write ( client , R14 , 0x1d4 ) ; /* ADC gain +2.5dB, enable zero cross */
wm8775_write ( client , R15 , 0x1d4 ) ; /* ADC gain +2.5dB, enable zero cross */
wm8775_write ( client , R16 , 0x1bf ) ; /* ALC Stereo, ALC target level -1dB FS */
/* max gain +8dB */
wm8775_write ( client , R17 , 0x185 ) ; /* Enable gain control, use zero cross */
/* detection, ALC hold time 42.6 ms */
wm8775_write ( client , R18 , 0x0a2 ) ; /* ALC gain ramp up delay 34 s, */
/* ALC gain ramp down delay 33 ms */
wm8775_write ( client , R19 , 0x005 ) ; /* Enable noise gate, threshold -72dBfs */
wm8775_write ( client , R20 , 0x07a ) ; /* Transient window 4ms, lower PGA gain */
/* limit -1dB */
wm8775_write ( client , R21 , 0x102 ) ; /* LRBOTH = 1, use input 2. */
i2c_attach_client ( client ) ;
return 0 ;
}
static int wm8775_probe ( struct i2c_adapter * adapter )
{
# ifdef I2C_CLASS_TV_ANALOG
if ( adapter - > class & I2C_CLASS_TV_ANALOG )
# else
2005-11-09 08:38:49 +03:00
if ( adapter - > id = = I2C_HW_B_BT848 )
# endif
return i2c_probe ( adapter , & addr_data , wm8775_attach ) ;
2005-11-09 08:37:25 +03:00
return 0 ;
}
static int wm8775_detach ( struct i2c_client * client )
{
int err ;
err = i2c_detach_client ( client ) ;
if ( err ) {
return err ;
}
kfree ( client ) ;
return 0 ;
}
/* ----------------------------------------------------------------------- */
/* i2c implementation */
static struct i2c_driver i2c_driver = {
. name = " wm8775 " ,
. id = I2C_DRIVERID_WM8775 ,
. flags = I2C_DF_NOTIFY ,
. attach_adapter = wm8775_probe ,
. detach_client = wm8775_detach ,
. command = wm8775_command ,
. owner = THIS_MODULE ,
} ;
static int __init wm8775_init_module ( void )
{
return i2c_add_driver ( & i2c_driver ) ;
}
static void __exit wm8775_cleanup_module ( void )
{
i2c_del_driver ( & i2c_driver ) ;
}
module_init ( wm8775_init_module ) ;
module_exit ( wm8775_cleanup_module ) ;