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 @@
#include "dvb_frontend.h"
#include "drx39xxj.h"
#include "drx_driver.h"
#include "drxj_mc.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)
{
struct drx39xxj_state *state = fe->demodulator_priv;
......@@ -323,6 +324,8 @@ static void drx39xxj_release(struct dvb_frontend *fe)
kfree(demod->my_ext_attr);
kfree(demod->my_common_attr);
kfree(demod->my_i2c_dev_addr);
if (demod->firmware)
release_firmware(demod->firmware);
kfree(demod);
kfree(state);
}
......@@ -377,15 +380,13 @@ struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c)
demod->my_i2c_dev_addr = demod_addr;
demod->my_common_attr = demod_comm_attr;
demod->my_i2c_dev_addr->user_data = state;
demod->my_common_attr->microcode = DRXJ_MC_MAIN;
#if 0
demod->my_common_attr->verify_microcode = false;
#endif
demod->my_common_attr->microcode_file = DRX39XX_MAIN_FIRMWARE;
demod->my_common_attr->verify_microcode = true;
demod->my_common_attr->intermediate_freq = 5000;
demod->my_ext_attr = demod_ext_attr;
((struct drxj_data *)demod_ext_attr)->uio_sma_tx_mode = DRX_UIO_MODE_READWRITE;
demod->my_tuner = NULL;
demod->i2c = i2c;
result = drx_open(demod);
if (result != 0) {
......@@ -455,3 +456,4 @@ static struct dvb_frontend_ops drx39xxj_ops = {
MODULE_DESCRIPTION("Micronas DRX39xxj Frontend");
MODULE_AUTHOR("Devin Heitmueller");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(DRX39XX_MAIN_FIRMWARE);
......@@ -33,6 +33,7 @@ struct drx39xxj_state {
struct dvb_frontend frontend;
int powered_up:1;
unsigned int i2c_gate_open:1;
const struct firmware *fw;
};
struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c);
......
/*
Generic DRX functionality, DRX driver core.
Copyright (c), 2004-2005,2007-2010 Trident Microsystems, Inc.
All rights reserved.
......@@ -28,12 +30,8 @@
POSSIBILITY OF SUCH DAMAGE.
*/
/**
* \file $Id: drx_driver.c,v 1.40 2010/01/12 01:24:56 lfeng Exp $
*
* \brief Generic DRX functionality, DRX driver core.
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
/*------------------------------------------------------------------------------
INCLUDE FILES
......@@ -957,32 +955,64 @@ static int
ctrl_u_code(struct drx_demod_instance *demod,
struct drxu_code_info *mc_info, enum drxu_code_action action)
{
struct i2c_device_addr *dev_addr = demod->my_i2c_dev_addr;
int rc;
u16 i = 0;
u16 mc_nr_of_blks = 0;
u16 mc_magic_word = 0;
u8 *mc_data = (u8 *)(NULL);
struct i2c_device_addr *dev_addr = (struct i2c_device_addr *)(NULL);
dev_addr = demod->my_i2c_dev_addr;
const u8 *mc_data_init = NULL;
u8 *mc_data = NULL;
char *mc_file = mc_info->mc_file;
/* Check arguments */
if ((mc_info == NULL) || (mc_info->mc_data == NULL))
if (!mc_info || !mc_file)
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 */
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);
} 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))
return -EINVAL; /* wrong endianess or wrong data ? */
pr_info("Firmware %s, size %u\n", mc_file, size);
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 */
DRX_ATTR_MCRECORD(demod).aux_type = 0;
DRX_ATTR_MCRECORD(demod).mc_dev_type = 0;
......@@ -991,6 +1021,9 @@ ctrl_u_code(struct drx_demod_instance *demod,
for (i = 0; i < mc_nr_of_blks; i++) {
struct drxu_code_block_hdr block_hdr;
if (mc_data - mc_data_init +
3 * sizeof(u16) + sizeof(u32) > size)
goto eof;
/* Process block header */
block_hdr.addr = u_code_read32(mc_data);
mc_data += sizeof(u32);
......@@ -1002,9 +1035,15 @@ ctrl_u_code(struct drx_demod_instance *demod,
mc_data += sizeof(u16);
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 */
u8 *auxblk = mc_info->mc_data + block_hdr.addr;
u16 auxtype = u_code_read16(auxblk);
auxtype = u_code_read16(auxblk);
if (DRX_ISMCVERTYPE(auxtype)) {
DRX_ATTR_MCRECORD(demod).aux_type = u_code_read16(auxblk);
auxblk += sizeof(u16);
......@@ -1015,20 +1054,28 @@ ctrl_u_code(struct drx_demod_instance *demod,
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 */
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.
It is also valid if no validation control exists.
*/
rc = drx_ctrl(demod, DRX_CTRL_VALIDATE_UCODE, NULL);
if (rc != 0 && rc != -ENOTSUPP)
return rc;
/* Restore data pointer */
mc_data = mc_info->mc_data + 2 * sizeof(u16);
if (rc != 0 && rc != -ENOTSUPP) {
pr_err("Validate ucode not supported\n");
goto release;
}
pr_info("Uploading firmware %s\n", mc_file);
}
/* Process microcode blocks */
......@@ -1055,103 +1102,85 @@ ctrl_u_code(struct drx_demod_instance *demod,
(block_hdr.CRC != u_code_compute_crc(mc_data, block_hdr.size)))
) {
/* 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));
if (block_hdr.size != 0) {
/* Perform the desired action */
switch (action) {
/*================================================================*/
case UCODE_UPLOAD:
{
/* Upload microcode */
if (demod->my_access_funct->
write_block_func(dev_addr,
(dr_xaddr_t) block_hdr.
addr, mc_block_nr_bytes,
mc_data,
0x0000) !=
0) {
return -EIO;
} /* if */
if (demod->my_access_funct->write_block_func(dev_addr,
block_hdr.addr,
mc_block_nr_bytes,
mc_data, 0x0000)) {
pr_err("error writing firmware\n");
goto release;
}
break;
/*================================================================*/
case UCODE_VERIFY:
{
case UCODE_VERIFY: {
int result = 0;
u8 mc_data_buffer
[DRX_UCODE_MAX_BUF_SIZE];
u32 bytes_to_compare = 0;
u32 bytes_left_to_compare = 0;
u32 curr_addr = (dr_xaddr_t) 0;
u8 *curr_ptr = NULL;
bytes_left_to_compare = mc_block_nr_bytes;
curr_addr = block_hdr.addr;
curr_ptr = mc_data;
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;
}
u8 mc_data_buffer[DRX_UCODE_MAX_BUF_SIZE];
u32 bytes_to_comp = 0;
u32 bytes_left = mc_block_nr_bytes;
u32 curr_addr = block_hdr.addr;
u8 *curr_ptr = mc_data;
while (bytes_left != 0) {
if (bytes_left > DRX_UCODE_MAX_BUF_SIZE)
bytes_to_comp = DRX_UCODE_MAX_BUF_SIZE;
else
bytes_to_comp = bytes_left;
if (demod->my_access_funct->
read_block_func(dev_addr,
curr_addr,
(u16)
bytes_to_compare,
(u8 *)
mc_data_buffer,
0x0000) !=
0) {
return -EIO;
(u16)bytes_to_comp,
(u8 *)mc_data_buffer,
0x0000)) {
pr_err("error reading firmware\n");
goto release;
}
result =
drxbsp_hst_memcmp(curr_ptr,
result =drxbsp_hst_memcmp(curr_ptr,
mc_data_buffer,
bytes_to_compare);
bytes_to_comp);
if (result != 0)
if (result) {
pr_err("error verifying firmware\n");
return -EIO;
}
curr_addr +=
((dr_xaddr_t)
(bytes_to_compare / 2));
curr_ptr =
&(curr_ptr[bytes_to_compare]);
bytes_left_to_compare -=
((u32) bytes_to_compare);
} /* while( bytes_to_compare > DRX_UCODE_MAX_BUF_SIZE ) */
curr_addr += ((dr_xaddr_t)(bytes_to_comp / 2));
curr_ptr =&(curr_ptr[bytes_to_comp]);
bytes_left -=((u32) bytes_to_comp);
}
break;
/*================================================================*/
}
default:
return -EINVAL;
break;
} /* switch ( action ) */
}
/* if (block_hdr.size != 0 ) */
/* Next block */
mc_data += mc_block_nr_bytes;
} /* for( i = 0 ; i<mc_nr_of_blks ; i++ ) */
}
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 @@
#include <linux/kernel.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.
......@@ -1014,13 +1016,14 @@ STRUCTS
/*============================================================================*/
/**
* \struct struct drxu_code_info * \brief Parameters for microcode upload and verfiy.
*
* Used by DRX_CTRL_LOAD_UCODE and DRX_CTRL_VERIFY_UCODE
*/
* struct drxu_code_info Parameters for microcode upload and verfiy.
*
* @mc_file: microcode file name
*
* Used by DRX_CTRL_LOAD_UCODE and DRX_CTRL_VERIFY_UCODE
*/
struct drxu_code_info {
u8 *mc_data;
/**< Pointer to microcode image. */
char *mc_file;
};
/**
......@@ -1929,8 +1932,7 @@ struct drx_reg_dump {
*/
struct drx_common_attr {
/* Microcode (firmware) attributes */
u8 *microcode; /**< Pointer to microcode image. */
/**< Size of microcode image in bytes. */
char *microcode_file; /**< microcode filename */
bool verify_microcode;
/**< Use microcode verify or not. */
struct drx_mc_version_rec mcversion;
......@@ -2029,7 +2031,7 @@ struct drx_demod_instance;
/**
* \struct struct drx_demod_instance * \brief Top structure of demodulator instance.
*/
struct drx_demod_instance {
struct drx_demod_instance {
/* type specific demodulator data */
struct drx_demod_func *my_demod_funct;
/**< demodulator functions */
......@@ -2043,7 +2045,10 @@ struct drx_demod_instance;
/**< common DRX attributes */
void *my_ext_attr; /**< device specific attributes */
/* generic demodulator data */
};
struct i2c_adapter *i2c;
const struct firmware *firmware;
};
/*-------------------------------------------------------------------------
MACROS
......
......@@ -875,7 +875,7 @@ struct i2c_device_addr drxj_default_addr_g = {
* \brief Default common attributes of a drxj demodulator instance.
*/
struct drx_common_attr drxj_default_comm_attr_g = {
(u8 *)NULL, /* ucode ptr */
NULL, /* ucode file */
true, /* ucode verify switch */
{0}, /* version record */
......@@ -20139,11 +20139,11 @@ int drxj_open(struct drx_demod_instance *demod)
}
/* Upload microcode */
if (common_attr->microcode != NULL) {
if (common_attr->microcode_file != NULL) {
/* Dirty trick to use common ucode upload & verify,
pretend device is already open */
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
/* 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