2005-11-13 16:07:56 -08:00
/* cx25840 firmware functions
*
* 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 . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*/
# include <linux/module.h>
# include <linux/i2c.h>
# include <linux/firmware.h>
# include <media/v4l2-common.h>
2006-03-25 10:26:09 -03:00
# include <media/cx25840.h>
2005-11-13 16:07:56 -08:00
2006-03-25 10:26:09 -03:00
# include "cx25840-core.h"
2005-11-13 16:07:56 -08:00
2005-11-13 16:08:12 -08:00
# define FWFILE "v4l-cx25840.fw"
2008-01-10 01:22:39 -03:00
# define FWFILE_CX23885 "v4l-cx23885-avcore-01.fw"
2006-03-25 20:43:14 -03:00
/*
* Mike Isely < isely @ pobox . com > - The FWSEND parameter controls the
* size of the firmware chunks sent down the I2C bus to the chip .
* Previously this had been set to 1024 but unfortunately some I2C
* implementations can ' t transfer data in such big gulps .
* Specifically , the pvrusb2 driver has a hard limit of around 60
* bytes , due to the encapsulation there of I2C traffic into USB
* messages . So we have to significantly reduce this parameter .
*/
# define FWSEND 48
2005-11-13 16:07:56 -08:00
2007-02-15 03:40:34 -03:00
# define FWDEV(x) &((x)->dev)
2005-11-13 16:07:56 -08:00
static char * firmware = FWFILE ;
module_param ( firmware , charp , 0444 ) ;
MODULE_PARM_DESC ( firmware , " Firmware image [default: " FWFILE " ] " ) ;
2006-01-09 15:32:41 -02:00
static void start_fw_load ( struct i2c_client * client )
2005-11-13 16:07:56 -08:00
{
/* DL_ADDR_LB=0 DL_ADDR_HB=0 */
cx25840_write ( client , 0x800 , 0x00 ) ;
cx25840_write ( client , 0x801 , 0x00 ) ;
// DL_MAP=3 DL_AUTO_INC=0 DL_ENABLE=1
cx25840_write ( client , 0x803 , 0x0b ) ;
/* AUTO_INC_DIS=1 */
cx25840_write ( client , 0x000 , 0x20 ) ;
}
2006-01-09 15:32:41 -02:00
static void end_fw_load ( struct i2c_client * client )
2005-11-13 16:07:56 -08:00
{
/* AUTO_INC_DIS=0 */
cx25840_write ( client , 0x000 , 0x00 ) ;
/* DL_ENABLE=0 */
cx25840_write ( client , 0x803 , 0x03 ) ;
}
2006-01-09 15:32:41 -02:00
static int check_fw_load ( struct i2c_client * client , int size )
2005-11-13 16:07:56 -08:00
{
/* DL_ADDR_HB DL_ADDR_LB */
int s = cx25840_read ( client , 0x801 ) < < 8 ;
s | = cx25840_read ( client , 0x800 ) ;
if ( size ! = s ) {
2006-01-09 15:32:40 -02:00
v4l_err ( client , " firmware %s load failed \n " , firmware ) ;
2005-11-13 16:07:56 -08:00
return - EINVAL ;
}
2006-01-09 15:32:40 -02:00
v4l_info ( client , " loaded %s firmware (%d bytes) \n " , firmware , size ) ;
2005-11-13 16:07:56 -08:00
return 0 ;
}
2008-05-23 23:58:24 +01:00
static int fw_write ( struct i2c_client * client , const u8 * data , int size )
2005-11-13 16:07:56 -08:00
{
2008-04-22 14:42:14 -03:00
if ( i2c_master_send ( client , data , size ) < size ) {
2006-04-23 05:54:56 -03:00
v4l_err ( client , " firmware load i2c failure \n " ) ;
return - ENOSYS ;
2005-11-13 16:07:56 -08:00
}
return 0 ;
}
int cx25840_loadfw ( struct i2c_client * client )
{
2008-11-29 12:50:06 -03:00
struct cx25840_state * state = to_state ( i2c_get_clientdata ( client ) ) ;
2005-11-13 16:07:56 -08:00
const struct firmware * fw = NULL ;
2008-05-23 23:58:24 +01:00
u8 buffer [ FWSEND ] ;
const u8 * ptr ;
2008-04-22 14:42:14 -03:00
int size , retval ;
2005-11-13 16:07:56 -08:00
2008-01-10 01:22:39 -03:00
if ( state - > is_cx23885 )
firmware = FWFILE_CX23885 ;
2005-11-13 16:07:56 -08:00
if ( request_firmware ( & fw , firmware , FWDEV ( client ) ) ! = 0 ) {
2006-01-09 15:32:40 -02:00
v4l_err ( client , " unable to open firmware %s \n " , firmware ) ;
2005-11-13 16:07:56 -08:00
return - EINVAL ;
}
start_fw_load ( client ) ;
buffer [ 0 ] = 0x08 ;
buffer [ 1 ] = 0x02 ;
2008-05-23 23:58:24 +01:00
size = fw - > size ;
2005-11-13 16:07:56 -08:00
ptr = fw - > data ;
while ( size > 0 ) {
2008-05-23 23:58:24 +01:00
int len = min ( FWSEND - 2 , size ) ;
memcpy ( buffer + 2 , ptr , len ) ;
retval = fw_write ( client , buffer , len + 2 ) ;
2005-11-13 16:07:56 -08:00
if ( retval < 0 ) {
release_firmware ( fw ) ;
return retval ;
}
2008-05-23 23:58:24 +01:00
size - = len ;
ptr + = len ;
2005-11-13 16:07:56 -08:00
}
end_fw_load ( client ) ;
size = fw - > size ;
release_firmware ( fw ) ;
return check_fw_load ( client , size ) ;
}