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
*
2005-11-14 03:08:05 +03:00
* Copyright ( C ) 2005 Hans Verkuil < hverkuil @ xs4all . nl >
* - Cleanup
* - V4L2 API update
* - sound fixes
*
2005-11-09 08:37:25 +03: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 . , 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>
2008-07-25 12:32:50 +04:00
# include <linux/videodev2.h>
2008-11-29 19:03:22 +03:00
# include <media/v4l2-device.h>
2007-04-27 19:31:26 +04:00
# include <media/v4l2-chip-ident.h>
2009-03-29 15:50:31 +04:00
# include <media/v4l2-i2c-drv.h>
2005-11-09 08:37:25 +03:00
MODULE_DESCRIPTION ( " wm8775 driver " ) ;
2005-11-14 03:08:05 +03:00
MODULE_AUTHOR ( " Ulf Eklund, Hans Verkuil " ) ;
2005-11-09 08:37:25 +03:00
MODULE_LICENSE ( " GPL " ) ;
2007-09-13 18:08:25 +04:00
2005-11-09 08:37:25 +03:00
/* ----------------------------------------------------------------------- */
enum {
R7 = 7 , R11 = 11 ,
R12 , R13 , R14 , R15 , R16 , R17 , R18 , R19 , R20 , R21 , R23 = 23 ,
TOT_REGS
} ;
struct wm8775_state {
2008-11-29 19:03:22 +03:00
struct v4l2_subdev sd ;
2005-11-09 08:37:25 +03:00
u8 input ; /* Last selected input (0-0xf) */
u8 muted ;
} ;
2008-11-29 19:03:22 +03:00
static inline struct wm8775_state * to_state ( struct v4l2_subdev * sd )
2005-11-09 08:37:25 +03:00
{
2008-11-29 19:03:22 +03:00
return container_of ( sd , struct wm8775_state , sd ) ;
}
static int wm8775_write ( struct v4l2_subdev * sd , int reg , u16 val )
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2005-11-09 08:37:25 +03:00
int i ;
if ( reg < 0 | | reg > = TOT_REGS ) {
2008-11-29 19:03:22 +03:00
v4l2_err ( sd , " Invalid register R%d \n " , reg ) ;
2005-11-09 08:37:25 +03:00
return - 1 ;
}
2007-11-01 13:35:41 +03:00
for ( i = 0 ; i < 3 ; i + + )
if ( i2c_smbus_write_byte_data ( client ,
( reg < < 1 ) | ( val > > 8 ) , val & 0xff ) = = 0 )
2005-11-09 08:37:25 +03:00
return 0 ;
2008-11-29 19:03:22 +03:00
v4l2_err ( sd , " I2C: cannot write %03x to register R%d \n " , val , reg ) ;
2005-11-09 08:37:25 +03:00
return - 1 ;
}
2009-04-02 18:26:22 +04:00
static int wm8775_s_routing ( struct v4l2_subdev * sd ,
u32 input , u32 output , u32 config )
2005-11-09 08:37:25 +03:00
{
2008-11-29 19:03:22 +03:00
struct wm8775_state * state = to_state ( sd ) ;
/* There are 4 inputs and one output. Zero or more inputs
are multiplexed together to the output . Hence there are
16 combinations .
If only one input is active ( the normal case ) then the
input values 1 , 2 , 4 or 8 should be used . */
2009-04-02 18:26:22 +04:00
if ( input > 15 ) {
v4l2_err ( sd , " Invalid input %d. \n " , input ) ;
2005-11-09 08:37:25 +03:00
return - EINVAL ;
}
2009-04-02 18:26:22 +04:00
state - > input = input ;
2008-11-29 19:03:22 +03:00
if ( state - > muted )
return 0 ;
wm8775_write ( sd , R21 , 0x0c0 ) ;
wm8775_write ( sd , R14 , 0x1d4 ) ;
wm8775_write ( sd , R15 , 0x1d4 ) ;
wm8775_write ( sd , R21 , 0x100 + state - > input ) ;
return 0 ;
}
static int wm8775_g_ctrl ( struct v4l2_subdev * sd , struct v4l2_control * ctrl )
{
struct wm8775_state * state = to_state ( sd ) ;
if ( ctrl - > id ! = V4L2_CID_AUDIO_MUTE )
return - EINVAL ;
ctrl - > value = state - > muted ;
return 0 ;
}
static int wm8775_s_ctrl ( struct v4l2_subdev * sd , struct v4l2_control * ctrl )
{
struct wm8775_state * state = to_state ( sd ) ;
if ( ctrl - > id ! = V4L2_CID_AUDIO_MUTE )
return - EINVAL ;
state - > muted = ctrl - > value ;
wm8775_write ( sd , R21 , 0x0c0 ) ;
wm8775_write ( sd , R14 , 0x1d4 ) ;
wm8775_write ( sd , R15 , 0x1d4 ) ;
if ( ! state - > muted )
wm8775_write ( sd , R21 , 0x100 + state - > input ) ;
return 0 ;
}
2008-12-30 13:14:19 +03:00
static int wm8775_g_chip_ident ( struct v4l2_subdev * sd , struct v4l2_dbg_chip_ident * chip )
2008-11-29 19:03:22 +03:00
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
return v4l2_chip_ident_i2c_client ( client , chip , V4L2_IDENT_WM8775 , 0 ) ;
}
static int wm8775_log_status ( struct v4l2_subdev * sd )
{
struct wm8775_state * state = to_state ( sd ) ;
v4l2_info ( sd , " Input: %d%s \n " , state - > input ,
state - > muted ? " (muted) " : " " ) ;
2005-11-09 08:37:25 +03:00
return 0 ;
}
2008-11-29 19:03:22 +03:00
static int wm8775_s_frequency ( struct v4l2_subdev * sd , struct v4l2_frequency * freq )
{
struct wm8775_state * state = to_state ( sd ) ;
/* 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 ( sd , R21 , 0x0c0 ) ;
wm8775_write ( sd , R14 , 0x1d4 ) ;
wm8775_write ( sd , R15 , 0x1d4 ) ;
wm8775_write ( sd , R21 , 0x100 + state - > input ) ;
return 0 ;
}
/* ----------------------------------------------------------------------- */
static const struct v4l2_subdev_core_ops wm8775_core_ops = {
. log_status = wm8775_log_status ,
. g_chip_ident = wm8775_g_chip_ident ,
. g_ctrl = wm8775_g_ctrl ,
. s_ctrl = wm8775_s_ctrl ,
} ;
static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = {
. s_frequency = wm8775_s_frequency ,
} ;
static const struct v4l2_subdev_audio_ops wm8775_audio_ops = {
. s_routing = wm8775_s_routing ,
} ;
static const struct v4l2_subdev_ops wm8775_ops = {
. core = & wm8775_core_ops ,
. tuner = & wm8775_tuner_ops ,
. audio = & wm8775_audio_ops ,
} ;
2005-11-09 08:37:25 +03:00
/* ----------------------------------------------------------------------- */
/* i2c implementation */
/*
* Generic i2c probe
* concerning the addresses : i2c wants 7 bit ( without the r / w bit ) , so ' > > 1 '
*/
2008-04-30 01:11:39 +04:00
static int wm8775_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
2005-11-09 08:37:25 +03:00
{
struct wm8775_state * state ;
2008-11-29 19:03:22 +03:00
struct v4l2_subdev * sd ;
2005-11-09 08:37:25 +03:00
/* Check if the adapter supports the needed features */
2007-09-13 18:08:25 +04:00
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_SMBUS_BYTE_DATA ) )
return - EIO ;
2005-11-09 08:37:25 +03:00
2008-11-29 19:03:22 +03:00
v4l_info ( client , " chip found @ 0x%02x (%s) \n " ,
2007-11-01 13:35:41 +03:00
client - > addr < < 1 , client - > adapter - > name ) ;
2005-11-09 08:37:25 +03:00
state = kmalloc ( sizeof ( struct wm8775_state ) , GFP_KERNEL ) ;
2007-11-01 13:35:41 +03:00
if ( state = = NULL )
2005-11-09 08:37:25 +03:00
return - ENOMEM ;
2008-11-29 19:03:22 +03:00
sd = & state - > sd ;
v4l2_i2c_subdev_init ( sd , client , & wm8775_ops ) ;
2005-11-09 08:37:25 +03:00
state - > input = 2 ;
state - > muted = 0 ;
2007-11-01 13:35:41 +03:00
/* Initialize wm8775 */
/* RESET */
2008-11-29 19:03:22 +03:00
wm8775_write ( sd , R23 , 0x000 ) ;
2007-11-01 13:35:41 +03:00
/* Disable zero cross detect timeout */
2008-11-29 19:03:22 +03:00
wm8775_write ( sd , R7 , 0x000 ) ;
2007-11-01 13:35:41 +03:00
/* Left justified, 24-bit mode */
2008-11-29 19:03:22 +03:00
wm8775_write ( sd , R11 , 0x021 ) ;
2007-11-01 13:35:41 +03:00
/* Master mode, clock ratio 256fs */
2008-11-29 19:03:22 +03:00
wm8775_write ( sd , R12 , 0x102 ) ;
2007-11-01 13:35:41 +03:00
/* Powered up */
2008-11-29 19:03:22 +03:00
wm8775_write ( sd , R13 , 0x000 ) ;
2007-11-01 13:35:41 +03:00
/* ADC gain +2.5dB, enable zero cross */
2008-11-29 19:03:22 +03:00
wm8775_write ( sd , R14 , 0x1d4 ) ;
2007-11-01 13:35:41 +03:00
/* ADC gain +2.5dB, enable zero cross */
2008-11-29 19:03:22 +03:00
wm8775_write ( sd , R15 , 0x1d4 ) ;
2007-11-01 13:35:41 +03:00
/* ALC Stereo, ALC target level -1dB FS max gain +8dB */
2008-11-29 19:03:22 +03:00
wm8775_write ( sd , R16 , 0x1bf ) ;
2007-11-01 13:35:41 +03:00
/* Enable gain control, use zero cross detection,
ALC hold time 42.6 ms */
2008-11-29 19:03:22 +03:00
wm8775_write ( sd , R17 , 0x185 ) ;
2007-11-01 13:35:41 +03:00
/* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */
2008-11-29 19:03:22 +03:00
wm8775_write ( sd , R18 , 0x0a2 ) ;
2007-11-01 13:35:41 +03:00
/* Enable noise gate, threshold -72dBfs */
2008-11-29 19:03:22 +03:00
wm8775_write ( sd , R19 , 0x005 ) ;
2007-11-01 13:35:41 +03:00
/* Transient window 4ms, lower PGA gain limit -1dB */
2008-11-29 19:03:22 +03:00
wm8775_write ( sd , R20 , 0x07a ) ;
2007-11-01 13:35:41 +03:00
/* LRBOTH = 1, use input 2. */
2008-11-29 19:03:22 +03:00
wm8775_write ( sd , R21 , 0x102 ) ;
2005-11-09 08:37:25 +03:00
return 0 ;
}
2007-09-13 18:08:25 +04:00
static int wm8775_remove ( struct i2c_client * client )
2005-11-09 08:37:25 +03:00
{
2008-11-29 19:03:22 +03:00
struct v4l2_subdev * sd = i2c_get_clientdata ( client ) ;
v4l2_device_unregister_subdev ( sd ) ;
kfree ( to_state ( sd ) ) ;
2005-11-09 08:37:25 +03:00
return 0 ;
}
2008-05-18 22:49:40 +04:00
static const struct i2c_device_id wm8775_id [ ] = {
{ " wm8775 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , wm8775_id ) ;
2007-09-13 18:08:25 +04:00
static struct v4l2_i2c_driver_data v4l2_i2c_data = {
. name = " wm8775 " ,
. probe = wm8775_probe ,
. remove = wm8775_remove ,
2008-05-18 22:49:40 +04:00
. id_table = wm8775_id ,
2005-11-09 08:37:25 +03:00
} ;