Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

[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
Loading
Loading
Loading
Loading
+7 −5
Original line number Diff line number Diff line
@@ -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);
+1 −0
Original line number Diff line number Diff line
@@ -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);
+142 −113
Original line number Diff line number Diff line
/*
  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;
}

/*============================================================================*/
+28 −23
Original line number Diff line number Diff line
@@ -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.
 * 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;
@@ -2043,6 +2045,9 @@ 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;
};

/*-------------------------------------------------------------------------
+3 −3
Original line number Diff line number Diff line
@@ -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 */
Loading