Commit b48293db authored by Mauro Carvalho Chehab's avatar Mauro Carvalho Chehab

[media] drx-j: dynamically load the firmware

Instead of hardcoding the firmware files together with the driver,
use request_firmware() way, loading it from userspace.

The firmware files are placed at:
	http://linuxtv.org/downloads/firmware/#8

And they'll be latter submitted to linux-firmware git tree.
Acked-by: default avatarDevin Heitmueller <dheitmueller@kernellabs.com>
Signed-off-by: default avatarMauro Carvalho Chehab <m.chehab@samsung.com>
parent 782ae20d
...@@ -29,9 +29,10 @@ ...@@ -29,9 +29,10 @@
#include "dvb_frontend.h" #include "dvb_frontend.h"
#include "drx39xxj.h" #include "drx39xxj.h"
#include "drx_driver.h" #include "drx_driver.h"
#include "drxj_mc.h"
#include "drxj.h" #include "drxj.h"
#define DRX39XX_MAIN_FIRMWARE "dvb-fe-drxj-mc-1.0.8.fw"
static int drx39xxj_set_powerstate(struct dvb_frontend *fe, int enable) static int drx39xxj_set_powerstate(struct dvb_frontend *fe, int enable)
{ {
struct drx39xxj_state *state = fe->demodulator_priv; struct drx39xxj_state *state = fe->demodulator_priv;
...@@ -323,6 +324,8 @@ static void drx39xxj_release(struct dvb_frontend *fe) ...@@ -323,6 +324,8 @@ static void drx39xxj_release(struct dvb_frontend *fe)
kfree(demod->my_ext_attr); kfree(demod->my_ext_attr);
kfree(demod->my_common_attr); kfree(demod->my_common_attr);
kfree(demod->my_i2c_dev_addr); kfree(demod->my_i2c_dev_addr);
if (demod->firmware)
release_firmware(demod->firmware);
kfree(demod); kfree(demod);
kfree(state); kfree(state);
} }
...@@ -377,15 +380,13 @@ struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c) ...@@ -377,15 +380,13 @@ struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c)
demod->my_i2c_dev_addr = demod_addr; demod->my_i2c_dev_addr = demod_addr;
demod->my_common_attr = demod_comm_attr; demod->my_common_attr = demod_comm_attr;
demod->my_i2c_dev_addr->user_data = state; demod->my_i2c_dev_addr->user_data = state;
demod->my_common_attr->microcode = DRXJ_MC_MAIN; demod->my_common_attr->microcode_file = DRX39XX_MAIN_FIRMWARE;
#if 0
demod->my_common_attr->verify_microcode = false;
#endif
demod->my_common_attr->verify_microcode = true; demod->my_common_attr->verify_microcode = true;
demod->my_common_attr->intermediate_freq = 5000; demod->my_common_attr->intermediate_freq = 5000;
demod->my_ext_attr = demod_ext_attr; demod->my_ext_attr = demod_ext_attr;
((struct drxj_data *)demod_ext_attr)->uio_sma_tx_mode = DRX_UIO_MODE_READWRITE; ((struct drxj_data *)demod_ext_attr)->uio_sma_tx_mode = DRX_UIO_MODE_READWRITE;
demod->my_tuner = NULL; demod->my_tuner = NULL;
demod->i2c = i2c;
result = drx_open(demod); result = drx_open(demod);
if (result != 0) { if (result != 0) {
...@@ -455,3 +456,4 @@ static struct dvb_frontend_ops drx39xxj_ops = { ...@@ -455,3 +456,4 @@ static struct dvb_frontend_ops drx39xxj_ops = {
MODULE_DESCRIPTION("Micronas DRX39xxj Frontend"); MODULE_DESCRIPTION("Micronas DRX39xxj Frontend");
MODULE_AUTHOR("Devin Heitmueller"); MODULE_AUTHOR("Devin Heitmueller");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_FIRMWARE(DRX39XX_MAIN_FIRMWARE);
...@@ -33,6 +33,7 @@ struct drx39xxj_state { ...@@ -33,6 +33,7 @@ struct drx39xxj_state {
struct dvb_frontend frontend; struct dvb_frontend frontend;
int powered_up:1; int powered_up:1;
unsigned int i2c_gate_open:1; unsigned int i2c_gate_open:1;
const struct firmware *fw;
}; };
struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c); struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c);
......
/* /*
Generic DRX functionality, DRX driver core.
Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc. Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc.
All rights reserved. All rights reserved.
...@@ -28,12 +30,8 @@ ...@@ -28,12 +30,8 @@
POSSIBILITY OF SUCH DAMAGE. POSSIBILITY OF SUCH DAMAGE.
*/ */
/** #define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
* \file $Id: drx_driver.c,v 1.40 2010/01/12 01:24:56 lfeng Exp $
*
* \brief Generic DRX functionality, DRX driver core.
*
*/
/*------------------------------------------------------------------------------ /*------------------------------------------------------------------------------
INCLUDE FILES INCLUDE FILES
...@@ -957,32 +955,64 @@ static int ...@@ -957,32 +955,64 @@ static int
ctrl_u_code(struct drx_demod_instance *demod, ctrl_u_code(struct drx_demod_instance *demod,
struct drxu_code_info *mc_info, enum drxu_code_action action) struct drxu_code_info *mc_info, enum drxu_code_action action)
{ {
struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
int rc; int rc;
u16 i = 0; u16 i = 0;
u16 mc_nr_of_blks = 0; u16 mc_nr_of_blks = 0;
u16 mc_magic_word = 0; u16 mc_magic_word = 0;
u8 *mc_data = (u8 *)(NULL); const u8 *mc_data_init = NULL;
struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL); u8 *mc_data = NULL;
char *mc_file = mc_info->mc_file;
dev_addr = demod->my_i2c_dev_addr;
/* Check arguments */ /* Check arguments */
if ((mc_info == NULL) || (mc_info->mc_data == NULL)) if (!mc_info || !mc_file)
return -EINVAL; return -EINVAL;
mc_data = mc_info->mc_data; if (demod->firmware) {
mc_data_init = demod->firmware->data;
mc_data = (void *)mc_data_init;
/* Check data */ /* Check data */
mc_magic_word = u_code_read16(mc_data); mc_magic_word = u_code_read16(mc_data);
mc_data += sizeof(u16); mc_data += sizeof(u16);
mc_nr_of_blks = u_code_read16(mc_data); mc_nr_of_blks = u_code_read16(mc_data);
mc_data += sizeof(u16); mc_data += sizeof(u16);
} else {
const struct firmware *fw = NULL;
unsigned size = 0;
rc = request_firmware(&fw, mc_file, demod->i2c->dev.parent);
if (rc < 0) {
pr_err("Couldn't read firmware %s\n", mc_file);
return -ENOENT;
}
demod->firmware = fw;
mc_data_init = demod->firmware->data;
size = demod->firmware->size;
if ((mc_magic_word != DRX_UCODE_MAGIC_WORD) || (mc_nr_of_blks == 0)) pr_info("Firmware %s, size %u\n", mc_file, size);
return -EINVAL; /* wrong endianess or wrong data ? */
mc_data = (void *)mc_data_init;
/* Check data */
if (mc_data - mc_data_init + 2 * sizeof(u16) > size)
goto eof;
mc_magic_word = u_code_read16(mc_data);
mc_data += sizeof(u16);
mc_nr_of_blks = u_code_read16(mc_data);
mc_data += sizeof(u16);
if ((mc_magic_word != DRX_UCODE_MAGIC_WORD) || (mc_nr_of_blks == 0)) {
rc = -EINVAL; /* wrong endianess or wrong data ? */
pr_err("Firmware magic word doesn't match\n");
goto release;
}
/*
* Scan microcode blocks first for version info
* and firmware check
*/
/* Scan microcode blocks first for version info if uploading */
if (action == UCODE_UPLOAD) {
/* Clear version block */ /* Clear version block */
DRX_ATTR_MCRECORD(demod).aux_type = 0; DRX_ATTR_MCRECORD(demod).aux_type = 0;
DRX_ATTR_MCRECORD(demod).mc_dev_type = 0; DRX_ATTR_MCRECORD(demod).mc_dev_type = 0;
...@@ -991,6 +1021,9 @@ ctrl_u_code(struct drx_demod_instance *demod, ...@@ -991,6 +1021,9 @@ ctrl_u_code(struct drx_demod_instance *demod,
for (i = 0; i < mc_nr_of_blks; i++) { for (i = 0; i < mc_nr_of_blks; i++) {
struct drxu_code_block_hdr block_hdr; struct drxu_code_block_hdr block_hdr;
if (mc_data - mc_data_init +
3 * sizeof(u16) + sizeof(u32) > size)
goto eof;
/* Process block header */ /* Process block header */
block_hdr.addr = u_code_read32(mc_data); block_hdr.addr = u_code_read32(mc_data);
mc_data += sizeof(u32); mc_data += sizeof(u32);
...@@ -1002,9 +1035,15 @@ ctrl_u_code(struct drx_demod_instance *demod, ...@@ -1002,9 +1035,15 @@ ctrl_u_code(struct drx_demod_instance *demod,
mc_data += sizeof(u16); mc_data += sizeof(u16);
if (block_hdr.flags & 0x8) { if (block_hdr.flags & 0x8) {
u8 *auxblk = ((void *)mc_data_init) + block_hdr.addr;
u16 auxtype;
if (mc_data - mc_data_init + sizeof(u16) +
2 * sizeof(u32) > size)
goto eof;
/* Aux block. Check type */ /* Aux block. Check type */
u8 *auxblk = mc_info->mc_data + block_hdr.addr; auxtype = u_code_read16(auxblk);
u16 auxtype = u_code_read16(auxblk);
if (DRX_ISMCVERTYPE(auxtype)) { if (DRX_ISMCVERTYPE(auxtype)) {
DRX_ATTR_MCRECORD(demod).aux_type = u_code_read16(auxblk); DRX_ATTR_MCRECORD(demod).aux_type = u_code_read16(auxblk);
auxblk += sizeof(u16); auxblk += sizeof(u16);
...@@ -1015,20 +1054,28 @@ ctrl_u_code(struct drx_demod_instance *demod, ...@@ -1015,20 +1054,28 @@ ctrl_u_code(struct drx_demod_instance *demod,
DRX_ATTR_MCRECORD(demod).mc_base_version = u_code_read32(auxblk); DRX_ATTR_MCRECORD(demod).mc_base_version = u_code_read32(auxblk);
} }
} }
if (mc_data - mc_data_init +
block_hdr.size * sizeof(u16) > size)
goto eof;
/* Next block */ /* Next block */
mc_data += block_hdr.size * sizeof(u16); mc_data += block_hdr.size * sizeof(u16);
} }
/* Restore data pointer */
mc_data = ((void *)mc_data_init) + 2 * sizeof(u16);
}
if (action == UCODE_UPLOAD) {
/* After scanning, validate the microcode. /* After scanning, validate the microcode.
It is also valid if no validation control exists. It is also valid if no validation control exists.
*/ */
rc = drx_ctrl(demod, DRX_CTRL_VALIDATE_UCODE, NULL); rc = drx_ctrl(demod, DRX_CTRL_VALIDATE_UCODE, NULL);
if (rc != 0 && rc != -ENOTSUPP) if (rc != 0 && rc != -ENOTSUPP) {
return rc; pr_err("Validate ucode not supported\n");
goto release;
/* Restore data pointer */ }
mc_data = mc_info->mc_data + 2 * sizeof(u16); pr_info("Uploading firmware %s\n", mc_file);
} }
/* Process microcode blocks */ /* Process microcode blocks */
...@@ -1055,103 +1102,85 @@ ctrl_u_code(struct drx_demod_instance *demod, ...@@ -1055,103 +1102,85 @@ ctrl_u_code(struct drx_demod_instance *demod,
(block_hdr.CRC != u_code_compute_crc(mc_data, block_hdr.size))) (block_hdr.CRC != u_code_compute_crc(mc_data, block_hdr.size)))
) { ) {
/* Wrong data ! */ /* Wrong data ! */
return -EINVAL; rc = -EINVAL;
pr_err("firmware CRC is wrong\n");
goto release;
} }
if (!block_hdr.size)
continue;
mc_block_nr_bytes = block_hdr.size * ((u16) sizeof(u16)); mc_block_nr_bytes = block_hdr.size * ((u16) sizeof(u16));
if (block_hdr.size != 0) {
/* Perform the desired action */ /* Perform the desired action */
switch (action) { switch (action) {
/*================================================================*/
case UCODE_UPLOAD: case UCODE_UPLOAD:
{
/* Upload microcode */ /* Upload microcode */
if (demod->my_access_funct-> if (demod->my_access_funct->write_block_func(dev_addr,
write_block_func(dev_addr, block_hdr.addr,
(dr_xaddr_t) block_hdr. mc_block_nr_bytes,
addr, mc_block_nr_bytes, mc_data, 0x0000)) {
mc_data, pr_err("error writing firmware\n");
0x0000) != goto release;
0) {
return -EIO;
} /* if */
} }
break; break;
case UCODE_VERIFY: {
/*================================================================*/
case UCODE_VERIFY:
{
int result = 0; int result = 0;
u8 mc_data_buffer u8 mc_data_buffer[DRX_UCODE_MAX_BUF_SIZE];
[DRX_UCODE_MAX_BUF_SIZE]; u32 bytes_to_comp = 0;
u32 bytes_to_compare = 0; u32 bytes_left = mc_block_nr_bytes;
u32 bytes_left_to_compare = 0; u32 curr_addr = block_hdr.addr;
u32 curr_addr = (dr_xaddr_t) 0; u8 *curr_ptr = mc_data;
u8 *curr_ptr = NULL;
while (bytes_left != 0) {
bytes_left_to_compare = mc_block_nr_bytes; if (bytes_left > DRX_UCODE_MAX_BUF_SIZE)
curr_addr = block_hdr.addr; bytes_to_comp = DRX_UCODE_MAX_BUF_SIZE;
curr_ptr = mc_data; else
bytes_to_comp = bytes_left;
while (bytes_left_to_compare != 0) {
if (bytes_left_to_compare >
((u32)
DRX_UCODE_MAX_BUF_SIZE)) {
bytes_to_compare =
((u32)
DRX_UCODE_MAX_BUF_SIZE);
} else {
bytes_to_compare =
bytes_left_to_compare;
}
if (demod->my_access_funct-> if (demod->my_access_funct->
read_block_func(dev_addr, read_block_func(dev_addr,
curr_addr, curr_addr,
(u16) (u16)bytes_to_comp,
bytes_to_compare, (u8 *)mc_data_buffer,
(u8 *) 0x0000)) {
mc_data_buffer, pr_err("error reading firmware\n");
0x0000) != goto release;
0) {
return -EIO;
} }
result = result =drxbsp_hst_memcmp(curr_ptr,
drxbsp_hst_memcmp(curr_ptr,
mc_data_buffer, mc_data_buffer,
bytes_to_compare); bytes_to_comp);
if (result != 0) if (result) {
pr_err("error verifying firmware\n");
return -EIO; return -EIO;
}
curr_addr += curr_addr += ((dr_xaddr_t)(bytes_to_comp / 2));
((dr_xaddr_t) curr_ptr =&(curr_ptr[bytes_to_comp]);
(bytes_to_compare / 2)); bytes_left -=((u32) bytes_to_comp);
curr_ptr =
&(curr_ptr[bytes_to_compare]);
bytes_left_to_compare -=
((u32) bytes_to_compare);
} /* while( bytes_to_compare > DRX_UCODE_MAX_BUF_SIZE ) */
} }
break; break;
}
/*================================================================*/
default: default:
return -EINVAL; return -EINVAL;
break; break;
} /* switch ( action ) */
} }
/* if (block_hdr.size != 0 ) */
/* Next block */
mc_data += mc_block_nr_bytes; mc_data += mc_block_nr_bytes;
}
} /* for( i = 0 ; i<mc_nr_of_blks ; i++ ) */
return 0; return 0;
eof:
rc = -ENOENT;
pr_err("Firmware file %s is truncated at pos %lu\n",
mc_file, (unsigned long)(mc_data - mc_data_init));
release:
release_firmware(demod->firmware);
demod->firmware = NULL;
return rc;
} }
/*============================================================================*/ /*============================================================================*/
......
...@@ -33,6 +33,8 @@ ...@@ -33,6 +33,8 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
/* /*
* This structure contains the I2C address, the device ID and a user_data pointer. * This structure contains the I2C address, the device ID and a user_data pointer.
...@@ -1014,13 +1016,14 @@ STRUCTS ...@@ -1014,13 +1016,14 @@ STRUCTS
/*============================================================================*/ /*============================================================================*/
/** /**
* \struct struct drxu_code_info * \brief Parameters for microcode upload and verfiy. * struct drxu_code_info Parameters for microcode upload and verfiy.
* *
* Used by DRX_CTRL_LOAD_UCODE and DRX_CTRL_VERIFY_UCODE * @mc_file: microcode file name
*/ *
* Used by DRX_CTRL_LOAD_UCODE and DRX_CTRL_VERIFY_UCODE
*/
struct drxu_code_info { struct drxu_code_info {
u8 *mc_data; char *mc_file;
/**< Pointer to microcode image. */
}; };
/** /**
...@@ -1929,8 +1932,7 @@ struct drx_reg_dump { ...@@ -1929,8 +1932,7 @@ struct drx_reg_dump {
*/ */
struct drx_common_attr { struct drx_common_attr {
/* Microcode (firmware) attributes */ /* Microcode (firmware) attributes */
u8 *microcode; /**< Pointer to microcode image. */ char *microcode_file; /**< microcode filename */
/**< Size of microcode image in bytes. */
bool verify_microcode; bool verify_microcode;
/**< Use microcode verify or not. */ /**< Use microcode verify or not. */
struct drx_mc_version_rec mcversion; struct drx_mc_version_rec mcversion;
...@@ -2029,7 +2031,7 @@ struct drx_demod_instance; ...@@ -2029,7 +2031,7 @@ struct drx_demod_instance;
/** /**
* \struct struct drx_demod_instance * \brief Top structure of demodulator instance. * \struct struct drx_demod_instance * \brief Top structure of demodulator instance.
*/ */
struct drx_demod_instance { struct drx_demod_instance {
/* type specific demodulator data */ /* type specific demodulator data */
struct drx_demod_func *my_demod_funct; struct drx_demod_func *my_demod_funct;
/**< demodulator functions */ /**< demodulator functions */
...@@ -2043,7 +2045,10 @@ struct drx_demod_instance; ...@@ -2043,7 +2045,10 @@ struct drx_demod_instance;
/**< common DRX attributes */ /**< common DRX attributes */
void *my_ext_attr; /**< device specific attributes */ void *my_ext_attr; /**< device specific attributes */
/* generic demodulator data */ /* generic demodulator data */
};
struct i2c_adapter *i2c;
const struct firmware *firmware;
};
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
MACROS MACROS
......
...@@ -875,7 +875,7 @@ struct i2c_device_addr drxj_default_addr_g = { ...@@ -875,7 +875,7 @@ struct i2c_device_addr drxj_default_addr_g = {
* \brief Default common attributes of a drxj demodulator instance. * \brief Default common attributes of a drxj demodulator instance.
*/ */
struct drx_common_attr drxj_default_comm_attr_g = { struct drx_common_attr drxj_default_comm_attr_g = {
(u8 *)NULL, /* ucode ptr */ NULL, /* ucode file */
true, /* ucode verify switch */ true, /* ucode verify switch */
{0}, /* version record */ {0}, /* version record */
...@@ -20139,11 +20139,11 @@ int drxj_open(struct drx_demod_instance *demod) ...@@ -20139,11 +20139,11 @@ int drxj_open(struct drx_demod_instance *demod)
} }
/* Upload microcode */ /* Upload microcode */
if (common_attr->microcode != NULL) { if (common_attr->microcode_file != NULL) {
/* Dirty trick to use common ucode upload & verify, /* Dirty trick to use common ucode upload & verify,
pretend device is already open */ pretend device is already open */
common_attr->is_opened = true; common_attr->is_opened = true;
ucode_info.mc_data = common_attr->microcode; ucode_info.mc_file = common_attr->microcode_file;
#ifdef DRXJ_SPLIT_UCODE_UPLOAD #ifdef DRXJ_SPLIT_UCODE_UPLOAD
/* Upload microcode without audio part */ /* Upload microcode without audio part */
......
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment