2006-01-09 20:32:39 +03:00
/*
* Programming the mspx4xx sound processor family
*
* ( c ) 1997 - 2001 Gerd Knorr < kraxel @ bytesex . org >
*
* what works and what doesn ' t :
*
* AM - Mono
* Support for Hauppauge cards added ( decoding handled by tuner ) added by
* Frederic Crozat < fcrozat @ mail . dotcom . fr >
*
* FM - Mono
* should work . The stereo modes are backward compatible to FM - mono ,
* therefore FM - Mono should be allways available .
*
* FM - Stereo ( B / G , used in germany )
* should work , with autodetect
*
* FM - Stereo ( satellite )
* should work , no autodetect ( i . e . default is mono , but you can
* switch to stereo - - untested )
*
* NICAM ( B / G , L , used in UK , Scandinavia , Spain and France )
* should work , with autodetect . Support for NICAM was added by
* Pekka Pietikainen < pp @ netppl . fi >
*
* TODO :
* - better SAT support
*
* 980623 Thomas Sailer ( sailer @ ife . ee . ethz . ch )
* using soundcore instead of OSS
*
* 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
2007-12-12 14:25:18 +03:00
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA
* 02110 - 1301 , USA .
2006-01-09 20:32:39 +03:00
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/i2c.h>
2008-11-25 00:16:46 +03:00
# include <linux/kthread.h>
# include <linux/freezer.h>
2009-03-30 15:18:10 +04:00
# include <linux/videodev2.h>
2008-11-25 00:16:46 +03:00
# include <media/v4l2-device.h>
2008-07-20 15:12:02 +04:00
# include <media/v4l2-ioctl.h>
2006-03-19 14:45:38 +03:00
# include <media/msp3400.h>
2008-11-25 00:16:46 +03:00
# include <media/tvaudio.h>
2006-03-19 14:45:38 +03:00
# include "msp3400-driver.h"
2006-01-09 20:32:39 +03:00
/* ---------------------------------------------------------------------- */
MODULE_DESCRIPTION ( " device driver for msp34xx TV sound processor " ) ;
MODULE_AUTHOR ( " Gerd Knorr " ) ;
MODULE_LICENSE ( " GPL " ) ;
/* module parameters */
static int opmode = OPMODE_AUTO ;
2006-01-13 19:10:19 +03:00
int msp_debug ; /* msp_debug output */
2012-01-13 03:02:20 +04:00
bool msp_once ; /* no continuous stereo monitoring */
bool msp_amsound ; /* hard-wire AM sound at 6.5 Hz (france),
2006-01-13 19:10:19 +03:00
the autoscan seems work well only with FM . . . */
2007-12-12 14:25:18 +03:00
int msp_standard = 1 ; /* Override auto detect of audio msp_standard,
if needed . */
2012-01-13 03:02:20 +04:00
bool msp_dolby ;
2006-01-09 20:32:39 +03:00
2006-01-12 00:41:49 +03:00
int msp_stereo_thresh = 0x190 ; /* a2 threshold for stereo/bilingual
2006-01-09 20:32:39 +03:00
( msp34xxg only ) 0x00a0 - 0x03c0 */
/* read-only */
module_param ( opmode , int , 0444 ) ;
/* read-write */
2007-12-12 14:25:18 +03:00
module_param_named ( once , msp_once , bool , 0644 ) ;
module_param_named ( debug , msp_debug , int , 0644 ) ;
module_param_named ( stereo_threshold , msp_stereo_thresh , int , 0644 ) ;
module_param_named ( standard , msp_standard , int , 0644 ) ;
module_param_named ( amsound , msp_amsound , bool , 0644 ) ;
module_param_named ( dolby , msp_dolby , bool , 0644 ) ;
2006-01-09 20:32:39 +03:00
MODULE_PARM_DESC ( opmode , " Forces a MSP3400 opmode. 0=Manual, 1=Autodetect, 2=Autodetect and autoselect " ) ;
MODULE_PARM_DESC ( once , " No continuous stereo monitoring " ) ;
MODULE_PARM_DESC ( debug , " Enable debug messages [0-3] " ) ;
MODULE_PARM_DESC ( stereo_threshold , " Sets signal threshold to activate stereo " ) ;
MODULE_PARM_DESC ( standard , " Specify audio standard: 32 = NTSC, 64 = radio, Default: Autodetect " ) ;
MODULE_PARM_DESC ( amsound , " Hardwire AM sound at 6.5Hz (France), FM can autoscan " ) ;
2011-05-10 12:16:21 +04:00
MODULE_PARM_DESC ( dolby , " Activates Dolby processing " ) ;
2006-01-09 20:32:39 +03:00
/* ---------------------------------------------------------------------- */
/* control subaddress */
# define I2C_MSP_CONTROL 0x00
/* demodulator unit subaddress */
# define I2C_MSP_DEM 0x10
/* DSP unit subaddress */
# define I2C_MSP_DSP 0x12
/* ----------------------------------------------------------------------- */
/* functions for talking to the MSP3400C Sound processor */
int msp_reset ( struct i2c_client * client )
{
/* reset and read revision code */
static u8 reset_off [ 3 ] = { I2C_MSP_CONTROL , 0x80 , 0x00 } ;
static u8 reset_on [ 3 ] = { I2C_MSP_CONTROL , 0x00 , 0x00 } ;
static u8 write [ 3 ] = { I2C_MSP_DSP + 1 , 0x00 , 0x1e } ;
u8 read [ 2 ] ;
struct i2c_msg reset [ 2 ] = {
2012-09-18 15:22:34 +04:00
{
. addr = client - > addr ,
. flags = I2C_M_IGNORE_NAK ,
. len = 3 ,
. buf = reset_off
} ,
{
. addr = client - > addr ,
. flags = I2C_M_IGNORE_NAK ,
. len = 3 ,
. buf = reset_on
} ,
2006-01-09 20:32:39 +03:00
} ;
struct i2c_msg test [ 2 ] = {
2012-09-18 15:22:34 +04:00
{
. addr = client - > addr ,
. len = 3 ,
. buf = write
} ,
{
. addr = client - > addr ,
. flags = I2C_M_RD ,
. len = 2 ,
. buf = read
} ,
2006-01-09 20:32:39 +03:00
} ;
2006-01-12 00:41:49 +03:00
v4l_dbg ( 3 , msp_debug , client , " msp_reset \n " ) ;
2006-01-09 20:32:39 +03:00
if ( i2c_transfer ( client - > adapter , & reset [ 0 ] , 1 ) ! = 1 | |
i2c_transfer ( client - > adapter , & reset [ 1 ] , 1 ) ! = 1 | |
i2c_transfer ( client - > adapter , test , 2 ) ! = 2 ) {
2006-01-09 20:32:40 +03:00
v4l_err ( client , " chip reset failed \n " ) ;
2006-01-09 20:32:39 +03:00
return - 1 ;
}
return 0 ;
}
static int msp_read ( struct i2c_client * client , int dev , int addr )
{
int err , retval ;
u8 write [ 3 ] ;
u8 read [ 2 ] ;
struct i2c_msg msgs [ 2 ] = {
2012-09-18 15:22:34 +04:00
{
. addr = client - > addr ,
. len = 3 ,
. buf = write
} ,
{
. addr = client - > addr ,
. flags = I2C_M_RD ,
. len = 2 ,
. buf = read
}
2006-01-09 20:32:39 +03:00
} ;
write [ 0 ] = dev + 1 ;
write [ 1 ] = addr > > 8 ;
write [ 2 ] = addr & 0xff ;
for ( err = 0 ; err < 3 ; err + + ) {
if ( i2c_transfer ( client - > adapter , msgs , 2 ) = = 2 )
break ;
2006-01-09 20:32:40 +03:00
v4l_warn ( client , " I/O error #%d (read 0x%02x/0x%02x) \n " , err ,
2006-01-09 20:32:39 +03:00
dev , addr ) ;
2007-07-16 17:46:42 +04:00
schedule_timeout_interruptible ( msecs_to_jiffies ( 10 ) ) ;
2006-01-09 20:32:39 +03:00
}
if ( err = = 3 ) {
2007-12-12 14:25:18 +03:00
v4l_warn ( client , " resetting chip, sound will go off. \n " ) ;
2006-01-09 20:32:39 +03:00
msp_reset ( client ) ;
return - 1 ;
}
retval = read [ 0 ] < < 8 | read [ 1 ] ;
2007-12-12 14:25:18 +03:00
v4l_dbg ( 3 , msp_debug , client , " msp_read(0x%x, 0x%x): 0x%x \n " ,
dev , addr , retval ) ;
2006-01-09 20:32:39 +03:00
return retval ;
}
int msp_read_dem ( struct i2c_client * client , int addr )
{
return msp_read ( client , I2C_MSP_DEM , addr ) ;
}
int msp_read_dsp ( struct i2c_client * client , int addr )
{
return msp_read ( client , I2C_MSP_DSP , addr ) ;
}
static int msp_write ( struct i2c_client * client , int dev , int addr , int val )
{
int err ;
u8 buffer [ 5 ] ;
buffer [ 0 ] = dev ;
buffer [ 1 ] = addr > > 8 ;
buffer [ 2 ] = addr & 0xff ;
buffer [ 3 ] = val > > 8 ;
buffer [ 4 ] = val & 0xff ;
2007-12-12 14:25:18 +03:00
v4l_dbg ( 3 , msp_debug , client , " msp_write(0x%x, 0x%x, 0x%x) \n " ,
dev , addr , val ) ;
2006-01-09 20:32:39 +03:00
for ( err = 0 ; err < 3 ; err + + ) {
if ( i2c_master_send ( client , buffer , 5 ) = = 5 )
break ;
2006-01-09 20:32:40 +03:00
v4l_warn ( client , " I/O error #%d (write 0x%02x/0x%02x) \n " , err ,
2006-01-09 20:32:39 +03:00
dev , addr ) ;
2007-07-16 17:46:42 +04:00
schedule_timeout_interruptible ( msecs_to_jiffies ( 10 ) ) ;
2006-01-09 20:32:39 +03:00
}
if ( err = = 3 ) {
2007-12-12 14:25:18 +03:00
v4l_warn ( client , " resetting chip, sound will go off. \n " ) ;
2006-01-09 20:32:39 +03:00
msp_reset ( client ) ;
return - 1 ;
}
return 0 ;
}
int msp_write_dem ( struct i2c_client * client , int addr , int val )
{
return msp_write ( client , I2C_MSP_DEM , addr , val ) ;
}
int msp_write_dsp ( struct i2c_client * client , int addr , int val )
{
return msp_write ( client , I2C_MSP_DSP , addr , val ) ;
}
/* ----------------------------------------------------------------------- *
* bits 9 8 5 - SCART DSP input Select :
* 0 0 0 - SCART 1 to DSP input ( reset position )
* 0 1 0 - MONO to DSP input
* 1 0 0 - SCART 2 to DSP input
* 1 1 1 - Mute DSP input
*
* bits 11 10 6 - SCART 1 Output Select :
* 0 0 0 - undefined ( reset position )
* 0 1 0 - SCART 2 Input to SCART 1 Output ( for devices with 2 SCARTS )
* 1 0 0 - MONO input to SCART 1 Output
* 1 1 0 - SCART 1 DA to SCART 1 Output
* 0 0 1 - SCART 2 DA to SCART 1 Output
* 0 1 1 - SCART 1 Input to SCART 1 Output
* 1 1 1 - Mute SCART 1 Output
*
* bits 13 12 7 - SCART 2 Output Select ( for devices with 2 Output SCART ) :
* 0 0 0 - SCART 1 DA to SCART 2 Output ( reset position )
* 0 1 0 - SCART 1 Input to SCART 2 Output
* 1 0 0 - MONO input to SCART 2 Output
* 0 0 1 - SCART 2 DA to SCART 2 Output
* 0 1 1 - SCART 2 Input to SCART 2 Output
* 1 1 0 - Mute SCART 2 Output
*
* Bits 4 to 0 should be zero .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int scarts [ 3 ] [ 9 ] = {
2007-08-28 01:16:54 +04:00
/* MASK IN1 IN2 IN3 IN4 IN1_DA IN2_DA MONO MUTE */
2006-01-09 20:32:39 +03:00
/* SCART DSP Input select */
2007-08-28 01:16:54 +04:00
{ 0x0320 , 0x0000 , 0x0200 , 0x0300 , 0x0020 , - 1 , - 1 , 0x0100 , 0x0320 } ,
2006-01-09 20:32:39 +03:00
/* SCART1 Output select */
2007-08-28 01:16:54 +04:00
{ 0x0c40 , 0x0440 , 0x0400 , 0x0000 , 0x0840 , 0x0c00 , 0x0040 , 0x0800 , 0x0c40 } ,
2006-01-09 20:32:39 +03:00
/* SCART2 Output select */
2007-08-28 01:16:54 +04:00
{ 0x3080 , 0x1000 , 0x1080 , 0x2080 , 0x3080 , 0x0000 , 0x0080 , 0x2000 , 0x3000 } ,
2006-01-09 20:32:39 +03:00
} ;
static char * scart_names [ ] = {
2007-08-28 01:16:54 +04:00
" in1 " , " in2 " , " in3 " , " in4 " , " in1 da " , " in2 da " , " mono " , " mute "
2006-01-09 20:32:39 +03:00
} ;
void msp_set_scart ( struct i2c_client * client , int in , int out )
{
2008-11-25 00:16:46 +03:00
struct msp_state * state = to_state ( i2c_get_clientdata ( client ) ) ;
2006-01-09 20:32:39 +03:00
2006-03-19 12:48:51 +03:00
state - > in_scart = in ;
2006-01-09 20:32:39 +03:00
2006-03-19 12:48:51 +03:00
if ( in > = 0 & & in < = 7 & & out > = 0 & & out < = 2 ) {
if ( - 1 = = scarts [ out ] [ in + 1 ] )
2006-01-09 20:32:39 +03:00
return ;
2006-03-19 12:48:51 +03:00
state - > acb & = ~ scarts [ out ] [ 0 ] ;
state - > acb | = scarts [ out ] [ in + 1 ] ;
2006-01-09 20:32:39 +03:00
} else
state - > acb = 0xf60 ; /* Mute Input and SCART 1 Output */
2006-01-12 00:41:49 +03:00
v4l_dbg ( 1 , msp_debug , client , " scart switch: %s => %d (ACB=0x%04x) \n " ,
2007-12-12 14:25:18 +03:00
scart_names [ in ] , out , state - > acb ) ;
2006-01-09 20:32:39 +03:00
msp_write_dsp ( client , 0x13 , state - > acb ) ;
/* Sets I2S speed 0 = 1.024 Mbps, 1 = 2.048 Mbps */
2006-03-29 01:32:52 +04:00
if ( state - > has_i2s_conf )
msp_write_dem ( client , 0x40 , state - > i2s_mode ) ;
2006-01-09 20:32:39 +03:00
}
/* ------------------------------------------------------------------------ */
static void msp_wake_thread ( struct i2c_client * client )
{
2008-11-25 00:16:46 +03:00
struct msp_state * state = to_state ( i2c_get_clientdata ( client ) ) ;
2006-01-09 20:32:39 +03:00
if ( NULL = = state - > kthread )
return ;
state - > watch_stereo = 0 ;
state - > restart = 1 ;
wake_up_interruptible ( & state - > wq ) ;
}
int msp_sleep ( struct msp_state * state , int timeout )
{
DECLARE_WAITQUEUE ( wait , current ) ;
add_wait_queue ( & state - > wq , & wait ) ;
if ( ! kthread_should_stop ( ) ) {
if ( timeout < 0 ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
schedule ( ) ;
} else {
schedule_timeout_interruptible
( msecs_to_jiffies ( timeout ) ) ;
}
}
remove_wait_queue ( & state - > wq , & wait ) ;
try_to_freeze ( ) ;
return state - > restart ;
}
/* ------------------------------------------------------------------------ */
2010-05-24 17:01:58 +04:00
static int msp_s_ctrl ( struct v4l2_ctrl * ctrl )
2006-01-09 20:32:39 +03:00
{
2010-05-24 17:01:58 +04:00
struct msp_state * state = ctrl_to_state ( ctrl ) ;
struct i2c_client * client = v4l2_get_subdevdata ( & state - > sd ) ;
int val = ctrl - > val ;
2006-01-09 20:32:39 +03:00
switch ( ctrl - > id ) {
2010-05-24 17:01:58 +04:00
case V4L2_CID_AUDIO_VOLUME : {
/* audio volume cluster */
int reallymuted = state - > muted - > val | state - > scan_in_progress ;
if ( ! reallymuted )
val = ( val * 0x7f / 65535 ) < < 8 ;
v4l_dbg ( 1 , msp_debug , client , " mute=%s scanning=%s volume=%d \n " ,
state - > muted - > val ? " on " : " off " ,
state - > scan_in_progress ? " yes " : " no " ,
state - > volume - > val ) ;
msp_write_dsp ( client , 0x0000 , val ) ;
msp_write_dsp ( client , 0x0007 , reallymuted ? 0x1 : ( val | 0x1 ) ) ;
if ( state - > has_scart2_out_volume )
msp_write_dsp ( client , 0x0040 , reallymuted ? 0x1 : ( val | 0x1 ) ) ;
if ( state - > has_headphones )
msp_write_dsp ( client , 0x0006 , val ) ;
2006-01-09 20:32:39 +03:00
break ;
}
case V4L2_CID_AUDIO_BASS :
2010-05-24 17:01:58 +04:00
val = ( ( val - 32768 ) * 0x60 / 65535 ) < < 8 ;
msp_write_dsp ( client , 0x0002 , val ) ;
if ( state - > has_headphones )
msp_write_dsp ( client , 0x0031 , val ) ;
2006-01-09 20:32:39 +03:00
break ;
case V4L2_CID_AUDIO_TREBLE :
2010-05-24 17:01:58 +04:00
val = ( ( val - 32768 ) * 0x60 / 65535 ) < < 8 ;
msp_write_dsp ( client , 0x0003 , val ) ;
if ( state - > has_headphones )
msp_write_dsp ( client , 0x0032 , val ) ;
2006-01-09 20:32:39 +03:00
break ;
2006-01-09 23:21:36 +03:00
case V4L2_CID_AUDIO_LOUDNESS :
2010-05-24 17:01:58 +04:00
val = val ? ( ( 5 * 4 ) < < 8 ) : 0 ;
msp_write_dsp ( client , 0x0004 , val ) ;
if ( state - > has_headphones )
msp_write_dsp ( client , 0x0033 , val ) ;
2006-01-09 20:32:39 +03:00
break ;
2006-01-09 23:21:36 +03:00
case V4L2_CID_AUDIO_BALANCE :
2010-05-24 17:01:58 +04:00
val = ( u8 ) ( ( val / 256 ) - 128 ) ;
msp_write_dsp ( client , 0x0001 , val < < 8 ) ;
if ( state - > has_headphones )
msp_write_dsp ( client , 0x0030 , val < < 8 ) ;
2006-01-09 20:32:39 +03:00
break ;
default :
return - EINVAL ;
}
return 0 ;
}
2010-05-24 17:01:58 +04:00
void msp_update_volume ( struct msp_state * state )
{
2010-10-17 14:24:20 +04:00
/* Force an update of the volume/mute cluster */
v4l2_ctrl_lock ( state - > volume ) ;
state - > volume - > val = state - > volume - > cur . val ;
state - > muted - > val = state - > muted - > cur . val ;
msp_s_ctrl ( state - > volume ) ;
v4l2_ctrl_unlock ( state - > volume ) ;
2010-05-24 17:01:58 +04:00
}
2008-11-25 00:16:46 +03:00
/* --- v4l2 ioctls --- */
static int msp_s_radio ( struct v4l2_subdev * sd )
{
struct msp_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2006-01-09 20:32:39 +03:00
2008-11-25 00:16:46 +03:00
if ( state - > radio )
2006-01-09 20:32:39 +03:00
return 0 ;
2008-11-25 00:16:46 +03:00
state - > radio = 1 ;
v4l_dbg ( 1 , msp_debug , client , " switching to radio mode \n " ) ;
state - > watch_stereo = 0 ;
switch ( state - > opmode ) {
case OPMODE_MANUAL :
/* set msp3400 to FM radio mode */
msp3400c_set_mode ( client , MSP_MODE_FM_RADIO ) ;
msp3400c_set_carrier ( client , MSP_CARRIER ( 10.7 ) ,
MSP_CARRIER ( 10.7 ) ) ;
2010-05-24 17:01:58 +04:00
msp_update_volume ( state ) ;
2008-11-25 00:16:46 +03:00
break ;
case OPMODE_AUTODETECT :
case OPMODE_AUTOSELECT :
/* the thread will do for us */
msp_wake_thread ( client ) ;
break ;
2006-01-09 20:32:39 +03:00
}
2008-11-25 00:16:46 +03:00
return 0 ;
}
2006-01-09 20:32:39 +03:00
2013-03-19 11:09:26 +04:00
static int msp_s_frequency ( struct v4l2_subdev * sd , const struct v4l2_frequency * freq )
2008-11-25 00:16:46 +03:00
{
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2006-01-09 20:32:39 +03:00
2008-11-25 00:16:46 +03:00
/* new channel -- kick audio carrier scan */
msp_wake_thread ( client ) ;
return 0 ;
}
2006-03-19 18:35:57 +03:00
2011-10-04 16:44:02 +04:00
static int msp_querystd ( struct v4l2_subdev * sd , v4l2_std_id * id )
{
struct msp_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
* id & = state - > detected_std ;
v4l_dbg ( 2 , msp_debug , client ,
" detected standard: %s(0x%08Lx) \n " ,
msp_standard_std_name ( state - > std ) , state - > detected_std ) ;
return 0 ;
}
2008-11-25 00:16:46 +03:00
static int msp_s_std ( struct v4l2_subdev * sd , v4l2_std_id id )
{
struct msp_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
int update = state - > radio | | state - > v4l2_std ! = id ;
state - > v4l2_std = id ;
state - > radio = 0 ;
if ( update )
2006-04-02 15:21:02 +04:00
msp_wake_thread ( client ) ;
2008-11-25 00:16:46 +03:00
return 0 ;
}
2009-04-02 18:26:22 +04:00
static int msp_s_routing ( struct v4l2_subdev * sd ,
u32 input , u32 output , u32 config )
2008-11-25 00:16:46 +03:00
{
struct msp_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2009-04-02 18:26:22 +04:00
int tuner = ( input > > 3 ) & 1 ;
int sc_in = input & 0x7 ;
int sc1_out = output & 0xf ;
int sc2_out = ( output > > 4 ) & 0xf ;
2008-11-25 00:16:46 +03:00
u16 val , reg ;
int i ;
int extern_input = 1 ;
2009-04-02 18:26:22 +04:00
if ( state - > route_in = = input & & state - > route_out = = output )
2008-11-25 00:16:46 +03:00
return 0 ;
2009-04-02 18:26:22 +04:00
state - > route_in = input ;
state - > route_out = output ;
2008-11-25 00:16:46 +03:00
/* check if the tuner input is used */
for ( i = 0 ; i < 5 ; i + + ) {
2009-04-02 18:26:22 +04:00
if ( ( ( input > > ( 4 + i * 4 ) ) & 0xf ) = = 0 )
2008-11-25 00:16:46 +03:00
extern_input = 0 ;
2006-01-09 20:32:39 +03:00
}
2008-11-25 00:16:46 +03:00
state - > mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT ;
state - > rxsubchans = V4L2_TUNER_SUB_STEREO ;
msp_set_scart ( client , sc_in , 0 ) ;
msp_set_scart ( client , sc1_out , 1 ) ;
msp_set_scart ( client , sc2_out , 2 ) ;
msp_set_audmode ( client ) ;
reg = ( state - > opmode = = OPMODE_AUTOSELECT ) ? 0x30 : 0xbb ;
val = msp_read_dem ( client , reg ) ;
msp_write_dem ( client , reg , ( val & ~ 0x100 ) | ( tuner < < 8 ) ) ;
/* wake thread when a new input is chosen */
msp_wake_thread ( client ) ;
return 0 ;
}
2006-01-09 20:32:39 +03:00
2008-11-25 00:16:46 +03:00
static int msp_g_tuner ( struct v4l2_subdev * sd , struct v4l2_tuner * vt )
{
struct msp_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2006-01-09 20:32:39 +03:00
2011-06-26 13:16:03 +04:00
if ( vt - > type ! = V4L2_TUNER_ANALOG_TV )
2008-11-25 00:16:46 +03:00
return 0 ;
2011-06-26 13:16:03 +04:00
if ( ! state - > radio ) {
if ( state - > opmode = = OPMODE_AUTOSELECT )
msp_detect_stereo ( client ) ;
vt - > rxsubchans = state - > rxsubchans ;
}
vt - > audmode = state - > audmode ;
2008-11-25 00:16:46 +03:00
vt - > capability | = V4L2_TUNER_CAP_STEREO |
V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 ;
return 0 ;
}
2006-01-09 20:32:39 +03:00
2013-03-15 13:10:06 +04:00
static int msp_s_tuner ( struct v4l2_subdev * sd , const struct v4l2_tuner * vt )
2008-11-25 00:16:46 +03:00
{
struct msp_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2006-01-09 20:32:39 +03:00
2008-11-25 00:16:46 +03:00
if ( state - > radio ) /* TODO: add mono/stereo support for radio */
return 0 ;
if ( state - > audmode = = vt - > audmode )
return 0 ;
state - > audmode = vt - > audmode ;
/* only set audmode */
msp_set_audmode ( client ) ;
return 0 ;
}
2006-01-09 20:32:39 +03:00
2008-11-25 00:16:46 +03:00
static int msp_s_i2s_clock_freq ( struct v4l2_subdev * sd , u32 freq )
{
struct msp_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2006-01-09 20:32:39 +03:00
2008-11-25 00:16:46 +03:00
v4l_dbg ( 1 , msp_debug , client , " Setting I2S speed to %d \n " , freq ) ;
2006-01-09 20:32:39 +03:00
2008-11-25 00:16:46 +03:00
switch ( freq ) {
2007-12-12 14:25:18 +03:00
case 1024000 :
state - > i2s_mode = 0 ;
break ;
case 2048000 :
state - > i2s_mode = 1 ;
break ;
default :
return - EINVAL ;
2006-01-09 20:32:39 +03:00
}
2008-11-25 00:16:46 +03:00
return 0 ;
}
2006-01-09 20:32:39 +03:00
2008-12-30 13:14:19 +03:00
static int msp_g_chip_ident ( struct v4l2_subdev * sd , struct v4l2_dbg_chip_ident * chip )
2008-11-25 00:16:46 +03:00
{
struct msp_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
2006-01-09 20:32:39 +03:00
2008-11-25 00:16:46 +03:00
return v4l2_chip_ident_i2c_client ( client , chip , state - > ident ,
( state - > rev1 < < 16 ) | state - > rev2 ) ;
}
2006-01-09 23:21:37 +03:00
2008-11-25 00:16:46 +03:00
static int msp_log_status ( struct v4l2_subdev * sd )
{
struct msp_state * state = to_state ( sd ) ;
struct i2c_client * client = v4l2_get_subdevdata ( sd ) ;
const char * p ;
2010-05-24 17:01:58 +04:00
char prefix [ V4L2_SUBDEV_NAME_SIZE + 20 ] ;
2008-11-25 00:16:46 +03:00
if ( state - > opmode = = OPMODE_AUTOSELECT )
msp_detect_stereo ( client ) ;
v4l_info ( client , " %s rev1 = 0x%04x rev2 = 0x%04x \n " ,
client - > name , state - > rev1 , state - > rev2 ) ;
2010-05-24 17:01:58 +04:00
snprintf ( prefix , sizeof ( prefix ) , " %s: Audio: " , sd - > name ) ;
v4l2_ctrl_handler_log_status ( & state - > hdl , prefix ) ;
2008-11-25 00:16:46 +03:00
switch ( state - > mode ) {
2006-01-09 23:21:37 +03:00
case MSP_MODE_AM_DETECT : p = " AM (for carrier detect) " ; break ;
case MSP_MODE_FM_RADIO : p = " FM Radio " ; break ;
2011-03-31 05:57:33 +04:00
case MSP_MODE_FM_TERRA : p = " Terrestrial FM-mono/stereo " ; break ;
2006-01-09 23:21:37 +03:00
case MSP_MODE_FM_SAT : p = " Satellite FM-mono " ; break ;
case MSP_MODE_FM_NICAM1 : p = " NICAM/FM (B/G, D/K) " ; break ;
case MSP_MODE_FM_NICAM2 : p = " NICAM/FM (I) " ; break ;
case MSP_MODE_AM_NICAM : p = " NICAM/AM (L) " ; break ;
case MSP_MODE_BTSC : p = " BTSC " ; break ;
case MSP_MODE_EXTERN : p = " External input " ; break ;
default : p = " unknown " ; break ;
2008-11-25 00:16:46 +03:00
}
if ( state - > mode = = MSP_MODE_EXTERN ) {
v4l_info ( client , " Mode: %s \n " , p ) ;
} else if ( state - > opmode = = OPMODE_MANUAL ) {
v4l_info ( client , " Mode: %s (%s%s) \n " , p ,
2006-01-09 23:21:37 +03:00
( state - > rxsubchans & V4L2_TUNER_SUB_STEREO ) ? " stereo " : " mono " ,
( state - > rxsubchans & V4L2_TUNER_SUB_LANG2 ) ? " , dual " : " " ) ;
2008-11-25 00:16:46 +03:00
} else {
if ( state - > opmode = = OPMODE_AUTODETECT )
v4l_info ( client , " Mode: %s \n " , p ) ;
v4l_info ( client , " Standard: %s (%s%s) \n " ,
2006-01-09 23:21:37 +03:00
msp_standard_std_name ( state - > std ) ,
( state - > rxsubchans & V4L2_TUNER_SUB_STEREO ) ? " stereo " : " mono " ,
( state - > rxsubchans & V4L2_TUNER_SUB_LANG2 ) ? " , dual " : " " ) ;
2006-01-09 20:32:39 +03:00
}
2008-11-25 00:16:46 +03:00
v4l_info ( client , " Audmode: 0x%04x \n " , state - > audmode ) ;
v4l_info ( client , " Routing: 0x%08x (input) 0x%08x (output) \n " ,
2009-04-02 18:26:22 +04:00
state - > route_in , state - > route_out ) ;
2008-11-25 00:16:46 +03:00
v4l_info ( client , " ACB: 0x%04x \n " , state - > acb ) ;
2006-01-09 20:32:39 +03:00
return 0 ;
}
2012-05-03 13:54:51 +04:00
# ifdef CONFIG_PM_SLEEP
static int msp_suspend ( struct device * dev )
2006-01-09 20:32:39 +03:00
{
2012-05-03 13:54:51 +04:00
struct i2c_client * client = to_i2c_client ( dev ) ;
2006-01-12 00:41:49 +03:00
v4l_dbg ( 1 , msp_debug , client , " suspend \n " ) ;
2006-01-09 20:32:39 +03:00
msp_reset ( client ) ;
return 0 ;
}
2012-05-03 13:54:51 +04:00
static int msp_resume ( struct device * dev )
2006-01-09 20:32:39 +03:00
{
2012-05-03 13:54:51 +04:00
struct i2c_client * client = to_i2c_client ( dev ) ;
2006-01-12 00:41:49 +03:00
v4l_dbg ( 1 , msp_debug , client , " resume \n " ) ;
2006-01-09 20:32:39 +03:00
msp_wake_thread ( client ) ;
return 0 ;
}
2012-05-03 13:54:51 +04:00
# endif
2006-01-09 20:32:39 +03:00
2008-11-25 00:16:46 +03:00
/* ----------------------------------------------------------------------- */
2010-05-24 17:01:58 +04:00
static const struct v4l2_ctrl_ops msp_ctrl_ops = {
. s_ctrl = msp_s_ctrl ,
} ;
2008-11-25 00:16:46 +03:00
static const struct v4l2_subdev_core_ops msp_core_ops = {
. log_status = msp_log_status ,
. g_chip_ident = msp_g_chip_ident ,
2010-05-24 17:01:58 +04:00
. g_ext_ctrls = v4l2_subdev_g_ext_ctrls ,
. try_ext_ctrls = v4l2_subdev_try_ext_ctrls ,
. s_ext_ctrls = v4l2_subdev_s_ext_ctrls ,
. g_ctrl = v4l2_subdev_g_ctrl ,
. s_ctrl = v4l2_subdev_s_ctrl ,
. queryctrl = v4l2_subdev_queryctrl ,
. querymenu = v4l2_subdev_querymenu ,
2009-04-01 10:52:39 +04:00
. s_std = msp_s_std ,
2008-11-25 00:16:46 +03:00
} ;
2011-10-04 16:44:02 +04:00
static const struct v4l2_subdev_video_ops msp_video_ops = {
. querystd = msp_querystd ,
} ;
2008-11-25 00:16:46 +03:00
static const struct v4l2_subdev_tuner_ops msp_tuner_ops = {
. s_frequency = msp_s_frequency ,
. g_tuner = msp_g_tuner ,
. s_tuner = msp_s_tuner ,
. s_radio = msp_s_radio ,
} ;
static const struct v4l2_subdev_audio_ops msp_audio_ops = {
. s_routing = msp_s_routing ,
. s_i2s_clock_freq = msp_s_i2s_clock_freq ,
} ;
static const struct v4l2_subdev_ops msp_ops = {
. core = & msp_core_ops ,
2011-10-04 16:44:02 +04:00
. video = & msp_video_ops ,
2008-11-25 00:16:46 +03:00
. tuner = & msp_tuner_ops ,
. audio = & msp_audio_ops ,
} ;
2006-01-09 20:32:39 +03:00
/* ----------------------------------------------------------------------- */
2008-04-30 01:11:39 +04:00
static int msp_probe ( struct i2c_client * client , const struct i2c_device_id * id )
2006-01-09 20:32:39 +03:00
{
struct msp_state * state ;
2008-11-25 00:16:46 +03:00
struct v4l2_subdev * sd ;
2010-05-24 17:01:58 +04:00
struct v4l2_ctrl_handler * hdl ;
2006-01-09 20:32:39 +03:00
int ( * thread_func ) ( void * data ) = NULL ;
2006-01-09 23:21:32 +03:00
int msp_hard ;
int msp_family ;
int msp_revision ;
int msp_product , msp_prod_hi , msp_prod_lo ;
int msp_rom ;
2006-01-09 20:32:39 +03:00
2008-05-18 22:49:40 +04:00
if ( ! id )
strlcpy ( client - > name , " msp3400 " , sizeof ( client - > name ) ) ;
2006-01-09 20:32:39 +03:00
if ( msp_reset ( client ) = = - 1 ) {
2006-01-12 00:41:49 +03:00
v4l_dbg ( 1 , msp_debug , client , " msp3400 not found \n " ) ;
2007-09-16 17:47:15 +04:00
return - ENODEV ;
2006-01-09 20:32:39 +03:00
}
2007-07-31 23:35:32 +04:00
state = kzalloc ( sizeof ( * state ) , GFP_KERNEL ) ;
2007-12-12 14:25:18 +03:00
if ( ! state )
2006-01-09 20:32:39 +03:00
return - ENOMEM ;
2007-07-31 23:35:32 +04:00
2008-11-25 00:16:46 +03:00
sd = & state - > sd ;
v4l2_i2c_subdev_init ( sd , client , & msp_ops ) ;
2006-01-09 20:32:39 +03:00
2006-01-09 23:21:37 +03:00
state - > v4l2_std = V4L2_STD_NTSC ;
2011-10-04 16:44:02 +04:00
state - > detected_std = V4L2_STD_ALL ;
2006-03-31 02:50:34 +04:00
state - > audmode = V4L2_TUNER_MODE_STEREO ;
2006-01-09 20:32:39 +03:00
state - > input = - 1 ;
state - > i2s_mode = 0 ;
init_waitqueue_head ( & state - > wq ) ;
2006-03-19 18:35:57 +03:00
/* These are the reset input/output positions */
2009-04-02 18:26:22 +04:00
state - > route_in = MSP_INPUT_DEFAULT ;
state - > route_out = MSP_OUTPUT_DEFAULT ;
2006-01-09 20:32:39 +03:00
state - > rev1 = msp_read_dsp ( client , 0x1e ) ;
if ( state - > rev1 ! = - 1 )
state - > rev2 = msp_read_dsp ( client , 0x1f ) ;
2007-12-12 14:25:18 +03:00
v4l_dbg ( 1 , msp_debug , client , " rev1=0x%04x, rev2=0x%04x \n " ,
state - > rev1 , state - > rev2 ) ;
2006-01-09 20:32:39 +03:00
if ( state - > rev1 = = - 1 | | ( state - > rev1 = = 0 & & state - > rev2 = = 0 ) ) {
2007-12-12 14:25:18 +03:00
v4l_dbg ( 1 , msp_debug , client ,
" not an msp3400 (cannot read chip version) \n " ) ;
2006-01-09 20:32:39 +03:00
kfree ( state ) ;
2007-09-16 17:47:15 +04:00
return - ENODEV ;
2006-01-09 20:32:39 +03:00
}
2006-01-09 23:21:32 +03:00
msp_family = ( ( state - > rev1 > > 4 ) & 0x0f ) + 3 ;
msp_product = ( state - > rev2 > > 8 ) & 0xff ;
msp_prod_hi = msp_product / 10 ;
msp_prod_lo = msp_product % 10 ;
msp_revision = ( state - > rev1 & 0x0f ) + ' @ ' ;
msp_hard = ( ( state - > rev1 > > 8 ) & 0xff ) + ' @ ' ;
msp_rom = state - > rev2 & 0x1f ;
2007-04-27 19:31:26 +04:00
/* Rev B=2, C=3, D=4, G=7 */
2007-12-12 14:25:18 +03:00
state - > ident = msp_family * 10000 + 4000 + msp_product * 10 +
msp_revision - ' @ ' ;
2006-01-09 23:21:32 +03:00
/* Has NICAM support: all mspx41x and mspx45x products have NICAM */
2007-12-12 14:25:18 +03:00
state - > has_nicam =
msp_prod_hi = = 1 | | msp_prod_hi = = 5 ;
2006-01-09 23:21:32 +03:00
/* Has radio support: was added with revision G */
2007-12-12 14:25:18 +03:00
state - > has_radio =
msp_revision > = ' G ' ;
2006-01-09 23:21:32 +03:00
/* Has headphones output: not for stripped down products */
2007-12-12 14:25:18 +03:00
state - > has_headphones =
msp_prod_lo < 5 ;
2006-03-31 02:50:34 +04:00
/* Has scart2 input: not in stripped down products of the '3' family */
2007-12-12 14:25:18 +03:00
state - > has_scart2 =
msp_family > = 4 | | msp_prod_lo < 7 ;
2006-03-31 02:50:34 +04:00
/* Has scart3 input: not in stripped down products of the '3' family */
2007-12-12 14:25:18 +03:00
state - > has_scart3 =
msp_family > = 4 | | msp_prod_lo < 5 ;
2006-01-09 23:21:32 +03:00
/* Has scart4 input: not in pre D revisions, not in stripped D revs */
2007-12-12 14:25:18 +03:00
state - > has_scart4 =
msp_family > = 4 | | ( msp_revision > = ' D ' & & msp_prod_lo < 5 ) ;
/* Has scart2 output: not in stripped down products of
* the ' 3 ' family */
state - > has_scart2_out =
msp_family > = 4 | | msp_prod_lo < 5 ;
2006-01-09 23:21:36 +03:00
/* Has scart2 a volume control? Not in pre-D revisions. */
2007-12-12 14:25:18 +03:00
state - > has_scart2_out_volume =
msp_revision > ' C ' & & state - > has_scart2_out ;
2006-01-09 23:21:37 +03:00
/* Has a configurable i2s out? */
2007-12-12 14:25:18 +03:00
state - > has_i2s_conf =
msp_revision > = ' G ' & & msp_prod_lo < 7 ;
/* Has subwoofer output: not in pre-D revs and not in stripped down
* products */
state - > has_subwoofer =
msp_revision > = ' D ' & & msp_prod_lo < 5 ;
/* Has soundprocessing (bass/treble/balance/loudness/equalizer):
* not in stripped down products */
state - > has_sound_processing =
msp_prod_lo < 7 ;
2006-01-09 23:21:32 +03:00
/* Has Virtual Dolby Surround: only in msp34x1 */
2007-12-12 14:25:18 +03:00
state - > has_virtual_dolby_surround =
msp_revision = = ' G ' & & msp_prod_lo = = 1 ;
2006-01-09 23:21:32 +03:00
/* Has Virtual Dolby Surround & Dolby Pro Logic: only in msp34x2 */
2007-12-12 14:25:18 +03:00
state - > has_dolby_pro_logic =
msp_revision = = ' G ' & & msp_prod_lo = = 2 ;
/* The msp343xG supports BTSC only and cannot do Automatic Standard
* Detection . */
state - > force_btsc =
msp_family = = 3 & & msp_revision = = ' G ' & & msp_prod_hi = = 3 ;
2006-01-09 20:32:39 +03:00
state - > opmode = opmode ;
if ( state - > opmode = = OPMODE_AUTO ) {
/* MSP revision G and up have both autodetect and autoselect */
2006-01-09 23:21:32 +03:00
if ( msp_revision > = ' G ' )
2006-01-09 20:32:39 +03:00
state - > opmode = OPMODE_AUTOSELECT ;
/* MSP revision D and up have autodetect */
2006-01-09 23:21:32 +03:00
else if ( msp_revision > = ' D ' )
2006-01-09 20:32:39 +03:00
state - > opmode = OPMODE_AUTODETECT ;
else
state - > opmode = OPMODE_MANUAL ;
}
2010-05-24 17:01:58 +04:00
hdl = & state - > hdl ;
v4l2_ctrl_handler_init ( hdl , 6 ) ;
if ( state - > has_sound_processing ) {
v4l2_ctrl_new_std ( hdl , & msp_ctrl_ops ,
V4L2_CID_AUDIO_BASS , 0 , 65535 , 65535 / 100 , 32768 ) ;
v4l2_ctrl_new_std ( hdl , & msp_ctrl_ops ,
V4L2_CID_AUDIO_TREBLE , 0 , 65535 , 65535 / 100 , 32768 ) ;
v4l2_ctrl_new_std ( hdl , & msp_ctrl_ops ,
V4L2_CID_AUDIO_LOUDNESS , 0 , 1 , 1 , 0 ) ;
}
state - > volume = v4l2_ctrl_new_std ( hdl , & msp_ctrl_ops ,
V4L2_CID_AUDIO_VOLUME , 0 , 65535 , 65535 / 100 , 58880 ) ;
v4l2_ctrl_new_std ( hdl , & msp_ctrl_ops ,
V4L2_CID_AUDIO_BALANCE , 0 , 65535 , 65535 / 100 , 32768 ) ;
state - > muted = v4l2_ctrl_new_std ( hdl , & msp_ctrl_ops ,
V4L2_CID_AUDIO_MUTE , 0 , 1 , 1 , 0 ) ;
sd - > ctrl_handler = hdl ;
if ( hdl - > error ) {
int err = hdl - > error ;
v4l2_ctrl_handler_free ( hdl ) ;
kfree ( state ) ;
return err ;
}
v4l2_ctrl_cluster ( 2 , & state - > volume ) ;
v4l2_ctrl_handler_setup ( hdl ) ;
2006-01-09 20:32:39 +03:00
/* hello world :-) */
2008-05-18 22:49:40 +04:00
v4l_info ( client , " MSP%d4%02d%c-%c%d found @ 0x%x (%s) \n " ,
msp_family , msp_product ,
msp_revision , msp_hard , msp_rom ,
2007-12-12 14:25:18 +03:00
client - > addr < < 1 , client - > adapter - > name ) ;
2006-01-09 20:32:40 +03:00
v4l_info ( client , " %s " , client - > name ) ;
2006-01-09 23:21:32 +03:00
if ( state - > has_nicam & & state - > has_radio )
2007-12-12 14:25:18 +03:00
printk ( KERN_CONT " supports nicam and radio, " ) ;
2006-01-09 23:21:32 +03:00
else if ( state - > has_nicam )
2007-12-12 14:25:18 +03:00
printk ( KERN_CONT " supports nicam, " ) ;
2006-01-09 23:21:32 +03:00
else if ( state - > has_radio )
2007-12-12 14:25:18 +03:00
printk ( KERN_CONT " supports radio, " ) ;
printk ( KERN_CONT " mode is " ) ;
2006-01-09 20:32:39 +03:00
/* version-specific initialization */
switch ( state - > opmode ) {
case OPMODE_MANUAL :
2007-12-12 14:25:18 +03:00
printk ( KERN_CONT " manual " ) ;
2006-01-09 20:32:39 +03:00
thread_func = msp3400c_thread ;
break ;
case OPMODE_AUTODETECT :
2007-12-12 14:25:18 +03:00
printk ( KERN_CONT " autodetect " ) ;
2006-01-09 20:32:39 +03:00
thread_func = msp3410d_thread ;
break ;
case OPMODE_AUTOSELECT :
2007-12-12 14:25:18 +03:00
printk ( KERN_CONT " autodetect and autoselect " ) ;
2006-01-09 20:32:39 +03:00
thread_func = msp34xxg_thread ;
break ;
}
2007-12-12 14:25:18 +03:00
printk ( KERN_CONT " \n " ) ;
2006-01-09 20:32:39 +03:00
/* startup control thread if needed */
if ( thread_func ) {
state - > kthread = kthread_run ( thread_func , client , " msp34xx " ) ;
2006-12-20 16:07:30 +03:00
if ( IS_ERR ( state - > kthread ) )
2006-01-09 20:32:40 +03:00
v4l_warn ( client , " kernel_thread() failed \n " ) ;
2006-01-09 20:32:39 +03:00
msp_wake_thread ( client ) ;
}
return 0 ;
}
2007-09-13 18:28:59 +04:00
static int msp_remove ( struct i2c_client * client )
2006-01-09 20:32:39 +03:00
{
2008-11-25 00:16:46 +03:00
struct msp_state * state = to_state ( i2c_get_clientdata ( client ) ) ;
2006-01-09 20:32:39 +03:00
2008-11-25 00:16:46 +03:00
v4l2_device_unregister_subdev ( & state - > sd ) ;
2006-01-09 20:32:39 +03:00
/* shutdown control thread */
if ( state - > kthread ) {
state - > restart = 1 ;
kthread_stop ( state - > kthread ) ;
}
msp_reset ( client ) ;
2010-05-24 17:01:58 +04:00
v4l2_ctrl_handler_free ( & state - > hdl ) ;
2006-01-09 20:32:39 +03:00
kfree ( state ) ;
return 0 ;
}
/* ----------------------------------------------------------------------- */
2012-05-03 13:54:51 +04:00
static const struct dev_pm_ops msp3400_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( msp_suspend , msp_resume )
} ;
2008-05-18 22:49:40 +04:00
static const struct i2c_device_id msp_id [ ] = {
{ " msp3400 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , msp_id ) ;
2010-09-15 22:22:34 +04:00
static struct i2c_driver msp_driver = {
. driver = {
. owner = THIS_MODULE ,
. name = " msp3400 " ,
2012-05-03 13:54:51 +04:00
. pm = & msp3400_pm_ops ,
2010-09-15 22:22:34 +04:00
} ,
. probe = msp_probe ,
. remove = msp_remove ,
. id_table = msp_id ,
2006-01-09 20:32:39 +03:00
} ;
2012-02-12 13:56:32 +04:00
module_i2c_driver ( msp_driver ) ;
2010-09-15 22:22:34 +04:00
2006-01-09 20:32:39 +03:00
/*
* Overrides for Emacs so that we follow Linus ' s tabbing style .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Local variables :
* c - basic - offset : 8
* End :
*/