2005-07-08 04:58:10 +04:00
/* DVB USB compliant linux driver for Conexant USB reference design.
*
* The Conexant reference design I saw on their website was only for analogue
* capturing ( using the cx25842 ) . The box I took to write this driver ( reverse
* engineered ) is the one labeled Medion MD95700 . In addition to the cx25842
* for analogue capturing it also has a cx22702 DVB - T demodulator on the main
* board . Besides it has a atiremote ( X10 ) and a USB2 .0 hub onboard .
*
* Maybe it is a little bit premature to call this driver cxusb , but I assume
* the USB protocol is identical or at least inherited from the reference
* design , so it can be reused for the " analogue-only " device ( if it will
* appear at all ) .
*
* TODO : check if the cx25840 - driver ( from ivtv ) can be used for the analogue
* part
*
* Copyright ( C ) 2005 Patrick Boettcher ( patrick . boettcher @ desy . de )
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation , version 2.
*
* see Documentation / dvb / README . dvb - usb for more information
*/
# include "cxusb.h"
# include "cx22702.h"
/* debug */
int dvb_usb_cxusb_debug ;
module_param_named ( debug , dvb_usb_cxusb_debug , int , 0644 ) ;
MODULE_PARM_DESC ( debug , " set debugging level (1=rc (or-able)). " DVB_USB_DEBUG_STATUS ) ;
static int cxusb_ctrl_msg ( struct dvb_usb_device * d ,
u8 cmd , u8 * wbuf , int wlen , u8 * rbuf , int rlen )
{
int wo = ( rbuf = = NULL | | rlen = = 0 ) ; /* write-only */
u8 sndbuf [ 1 + wlen ] ;
memset ( sndbuf , 0 , 1 + wlen ) ;
sndbuf [ 0 ] = cmd ;
memcpy ( & sndbuf [ 1 ] , wbuf , wlen ) ;
if ( wo )
dvb_usb_generic_write ( d , sndbuf , 1 + wlen ) ;
else
dvb_usb_generic_rw ( d , sndbuf , 1 + wlen , rbuf , rlen , 0 ) ;
return 0 ;
}
/* I2C */
static void cxusb_set_i2c_path ( struct dvb_usb_device * d , enum cxusb_i2c_pathes path )
{
struct cxusb_state * st = d - > priv ;
u8 o [ 2 ] , i ;
if ( path = = st - > cur_i2c_path )
return ;
o [ 0 ] = IOCTL_SET_I2C_PATH ;
switch ( path ) {
case PATH_CX22702 :
o [ 1 ] = 0 ;
break ;
case PATH_TUNER_OTHER :
o [ 1 ] = 1 ;
break ;
default :
err ( " unkown i2c path " ) ;
return ;
}
cxusb_ctrl_msg ( d , CMD_IOCTL , o , 2 , & i , 1 ) ;
if ( i ! = 0x01 )
deb_info ( " i2c_path setting failed. \n " ) ;
st - > cur_i2c_path = path ;
}
static int cxusb_i2c_xfer ( struct i2c_adapter * adap , struct i2c_msg msg [ ] , int num )
{
struct dvb_usb_device * d = i2c_get_adapdata ( adap ) ;
int i ;
if ( down_interruptible ( & d - > i2c_sem ) < 0 )
return - EAGAIN ;
if ( num > 2 )
warn ( " more than 2 i2c messages at a time is not handled yet. TODO. " ) ;
for ( i = 0 ; i < num ; i + + ) {
switch ( msg [ i ] . addr ) {
case 0x63 :
cxusb_set_i2c_path ( d , PATH_CX22702 ) ;
break ;
default :
cxusb_set_i2c_path ( d , PATH_TUNER_OTHER ) ;
break ;
}
/* read request */
if ( i + 1 < num & & ( msg [ i + 1 ] . flags & I2C_M_RD ) ) {
u8 obuf [ 3 + msg [ i ] . len ] , ibuf [ 1 + msg [ i + 1 ] . len ] ;
obuf [ 0 ] = msg [ i ] . len ;
obuf [ 1 ] = msg [ i + 1 ] . len ;
obuf [ 2 ] = msg [ i ] . addr ;
memcpy ( & obuf [ 3 ] , msg [ i ] . buf , msg [ i ] . len ) ;
if ( cxusb_ctrl_msg ( d , CMD_I2C_READ ,
obuf , 3 + msg [ i ] . len ,
ibuf , 1 + msg [ i + 1 ] . len ) < 0 )
break ;
if ( ibuf [ 0 ] ! = 0x08 )
deb_info ( " i2c read could have been failed \n " ) ;
memcpy ( msg [ i + 1 ] . buf , & ibuf [ 1 ] , msg [ i + 1 ] . len ) ;
i + + ;
} else { /* write */
u8 obuf [ 2 + msg [ i ] . len ] , ibuf ;
obuf [ 0 ] = msg [ i ] . addr ;
obuf [ 1 ] = msg [ i ] . len ;
memcpy ( & obuf [ 2 ] , msg [ i ] . buf , msg [ i ] . len ) ;
if ( cxusb_ctrl_msg ( d , CMD_I2C_WRITE , obuf , 2 + msg [ i ] . len , & ibuf , 1 ) < 0 )
break ;
if ( ibuf ! = 0x08 )
deb_info ( " i2c write could have been failed \n " ) ;
}
}
up ( & d - > i2c_sem ) ;
return i ;
}
static u32 cxusb_i2c_func ( struct i2c_adapter * adapter )
{
return I2C_FUNC_I2C ;
}
static struct i2c_algorithm cxusb_i2c_algo = {
. name = " Conexant USB I2C algorithm " ,
. id = I2C_ALGO_BIT ,
. master_xfer = cxusb_i2c_xfer ,
. functionality = cxusb_i2c_func ,
} ;
static int cxusb_power_ctrl ( struct dvb_usb_device * d , int onoff )
{
return 0 ;
}
static int cxusb_streaming_ctrl ( struct dvb_usb_device * d , int onoff )
{
2005-07-08 04:58:15 +04:00
u8 buf [ 2 ] = { 0x03 , 0x00 } ;
if ( onoff )
cxusb_ctrl_msg ( d , 0x36 , buf , 2 , NULL , 0 ) ;
else
cxusb_ctrl_msg ( d , 0x37 , NULL , 0 , NULL , 0 ) ;
2005-07-08 04:58:10 +04:00
return 0 ;
}
struct cx22702_config cxusb_cx22702_config = {
. demod_address = 0x63 ,
2005-07-08 04:58:15 +04:00
. output_mode = CX22702_PARALLEL_OUTPUT ,
2005-07-08 04:58:10 +04:00
. pll_init = dvb_usb_pll_init_i2c ,
. pll_set = dvb_usb_pll_set_i2c ,
} ;
/* Callbacks for DVB USB */
static int cxusb_tuner_attach ( struct dvb_usb_device * d )
{
u8 bpll [ 4 ] = { 0x0b , 0xdc , 0x9c , 0xa0 } ;
d - > pll_addr = 0x61 ;
memcpy ( d - > pll_init , bpll , 4 ) ;
d - > pll_desc = & dvb_pll_fmd1216me ;
return 0 ;
}
static int cxusb_frontend_attach ( struct dvb_usb_device * d )
{
u8 buf [ 2 ] = { 0x03 , 0x00 } ;
u8 b = 0 ;
2005-07-08 04:58:15 +04:00
if ( usb_set_interface ( d - > udev , 0 , 0 ) < 0 )
err ( " set interface to alts=0 failed " ) ;
2005-07-08 04:58:10 +04:00
cxusb_ctrl_msg ( d , 0xde , & b , 0 , NULL , 0 ) ;
cxusb_set_i2c_path ( d , PATH_TUNER_OTHER ) ;
cxusb_ctrl_msg ( d , CMD_POWER_OFF , NULL , 0 , & b , 1 ) ;
if ( usb_set_interface ( d - > udev , 0 , 6 ) < 0 )
2005-07-08 04:58:15 +04:00
err ( " set interface failed " ) ;
2005-07-08 04:58:10 +04:00
cxusb_ctrl_msg ( d , 0x36 , buf , 2 , NULL , 0 ) ;
cxusb_set_i2c_path ( d , PATH_CX22702 ) ;
cxusb_ctrl_msg ( d , CMD_POWER_ON , NULL , 0 , & b , 1 ) ;
if ( ( d - > fe = cx22702_attach ( & cxusb_cx22702_config , & d - > i2c_adap ) ) ! = NULL )
return 0 ;
return - EIO ;
}
/* DVB USB Driver stuff */
static struct dvb_usb_properties cxusb_properties ;
static int cxusb_probe ( struct usb_interface * intf ,
const struct usb_device_id * id )
{
return dvb_usb_device_init ( intf , & cxusb_properties , THIS_MODULE ) ;
}
static struct usb_device_id cxusb_table [ ] = {
{ USB_DEVICE ( USB_VID_MEDION , USB_PID_MEDION_MD95700 ) } ,
{ } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( usb , cxusb_table ) ;
static struct dvb_usb_properties cxusb_properties = {
. caps = DVB_USB_IS_AN_I2C_ADAPTER ,
. usb_ctrl = CYPRESS_FX2 ,
. size_of_priv = sizeof ( struct cxusb_state ) ,
. streaming_ctrl = cxusb_streaming_ctrl ,
. power_ctrl = cxusb_power_ctrl ,
. frontend_attach = cxusb_frontend_attach ,
. tuner_attach = cxusb_tuner_attach ,
. i2c_algo = & cxusb_i2c_algo ,
. generic_bulk_ctrl_endpoint = 0x01 ,
/* parameter for the MPEG2-data transfer */
. urb = {
. type = DVB_USB_ISOC ,
. count = 5 ,
. endpoint = 0x02 ,
. u = {
. isoc = {
2005-07-08 04:58:15 +04:00
. framesperurb = 32 ,
. framesize = 940 ,
. interval = 5 ,
2005-07-08 04:58:10 +04:00
}
}
} ,
. num_device_descs = 1 ,
. devices = {
{ " Medion MD95700 (MDUSBTV-HYBRID) " ,
{ NULL } ,
{ & cxusb_table [ 0 ] , NULL } ,
} ,
}
} ;
static struct usb_driver cxusb_driver = {
. owner = THIS_MODULE ,
. name = " cxusb " ,
. probe = cxusb_probe ,
. disconnect = dvb_usb_device_exit ,
. id_table = cxusb_table ,
} ;
/* module stuff */
static int __init cxusb_module_init ( void )
{
int result ;
if ( ( result = usb_register ( & cxusb_driver ) ) ) {
err ( " usb_register failed. Error number %d " , result ) ;
return result ;
}
return 0 ;
}
static void __exit cxusb_module_exit ( void )
{
/* deregister this driver from the USB subsystem */
usb_deregister ( & cxusb_driver ) ;
}
module_init ( cxusb_module_init ) ;
module_exit ( cxusb_module_exit ) ;
MODULE_AUTHOR ( " Patrick Boettcher <patrick.boettcher@desy.de> " ) ;
MODULE_DESCRIPTION ( " Driver for Conexant USB2.0 hybrid reference design " ) ;
MODULE_VERSION ( " 1.0-alpha " ) ;
MODULE_LICENSE ( " GPL " ) ;