summaryrefslogtreecommitdiffstats
path: root/drivers/media/dvb-frontends/drx39xyj/drxj.c
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <m.chehab@samsung.com>2014-01-24 06:25:07 -0300
committerMauro Carvalho Chehab <m.chehab@samsung.com>2014-03-04 14:34:27 -0300
commitb240eacdd536bac23c9d48dfc3d527ed6870ddad (patch)
treee77bc3ab087b942d20ef7cc03d3a2d1d8ef7d6cf /drivers/media/dvb-frontends/drx39xyj/drxj.c
parentad55f6c881bd2d88576c6a38c4964632d4851d11 (diff)
downloadop-kernel-dev-b240eacdd536bac23c9d48dfc3d527ed6870ddad.zip
op-kernel-dev-b240eacdd536bac23c9d48dfc3d527ed6870ddad.tar.gz
[media] drx-j: get rid of drx_driver.c
This file contains just the firmware load code, that it is also somewhat duplicated at drxj.c. Move the code into there. Latter patches will remove the duplicated code. Acked-by: Devin Heitmueller <dheitmueller@kernellabs.com> Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
Diffstat (limited to 'drivers/media/dvb-frontends/drx39xyj/drxj.c')
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drxj.c597
1 files changed, 592 insertions, 5 deletions
diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c
index b1ad26b..a06c45d 100644
--- a/drivers/media/dvb-frontends/drx39xyj/drxj.c
+++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c
@@ -39,6 +39,7 @@ INCLUDE FILES
#include "drxj.h"
#include "drxj_map.h"
+#include "drx_driver.h"
/*============================================================================*/
/*=== DEFINES ================================================================*/
@@ -309,6 +310,40 @@ DEFINES
#define DRX_UIO_MODE_FIRMWARE_SMA DRX_UIO_MODE_FIRMWARE0
#define DRX_UIO_MODE_FIRMWARE_SAW DRX_UIO_MODE_FIRMWARE1
+/*
+ * MICROCODE RELATED DEFINES
+ */
+
+/* Magic word for checking correct Endianess of microcode data */
+#define DRX_UCODE_MAGIC_WORD ((((u16)'H')<<8)+((u16)'L'))
+
+/* CRC flag in ucode header, flags field. */
+#define DRX_UCODE_CRC_FLAG (0x0001)
+
+/*
+ * Maximum size of buffer used to verify the microcode.
+ * Must be an even number
+ */
+#define DRX_UCODE_MAX_BUF_SIZE (DRXDAP_MAX_RCHUNKSIZE)
+
+#if DRX_UCODE_MAX_BUF_SIZE & 1
+#error DRX_UCODE_MAX_BUF_SIZE must be an even number
+#endif
+
+/*
+ * Power mode macros
+ */
+
+#define DRX_ISPOWERDOWNMODE(mode) ((mode == DRX_POWER_MODE_9) || \
+ (mode == DRX_POWER_MODE_10) || \
+ (mode == DRX_POWER_MODE_11) || \
+ (mode == DRX_POWER_MODE_12) || \
+ (mode == DRX_POWER_MODE_13) || \
+ (mode == DRX_POWER_MODE_14) || \
+ (mode == DRX_POWER_MODE_15) || \
+ (mode == DRX_POWER_MODE_16) || \
+ (mode == DRX_POWER_DOWN))
+
#ifdef DRXJ_SPLIT_UCODE_UPLOAD
/*============================================================================*/
/*=== MICROCODE RELATED DEFINES ==============================================*/
@@ -1050,20 +1085,25 @@ struct drxj_hi_cmd {
u16 param6;
};
-#ifdef DRXJ_SPLIT_UCODE_UPLOAD
/*============================================================================*/
/*=== MICROCODE RELATED STRUCTURES ===========================================*/
/*============================================================================*/
+/**
+ * struct drxu_code_block_hdr - Structure of the microcode block headers
+ *
+ * @addr: Destination address of the data in this block
+ * @size: Size of the block data following this header counted in
+ * 16 bits words
+ * @CRC: CRC value of the data block, only valid if CRC flag is
+ * set.
+ */
struct drxu_code_block_hdr {
u32 addr;
u16 size;
- u16 flags; /* bit[15..2]=reserved,
- bit[1]= compression on/off
- bit[0]= CRC on/off */
+ u16 flags;
u16 CRC;
};
-#endif /* DRXJ_SPLIT_UCODE_UPLOAD */
/*-----------------------------------------------------------------------------
FUNCTIONS
@@ -20607,3 +20647,550 @@ drxj_ctrl(struct drx_demod_instance *demod, u32 ctrl, void *ctrl_data)
}
return 0;
}
+
+/*
+ * Microcode related functions
+ */
+
+/**
+ * drx_u_code_compute_crc - Compute CRC of block of microcode data.
+ * @block_data: Pointer to microcode data.
+ * @nr_words: Size of microcode block (number of 16 bits words).
+ *
+ * returns The computed CRC residue.
+ */
+static u16 drx_u_code_compute_crc(u8 *block_data, u16 nr_words)
+{
+ u16 i = 0;
+ u16 j = 0;
+ u32 crc_word = 0;
+ u32 carry = 0;
+
+ while (i < nr_words) {
+ crc_word |= (u32)be16_to_cpu(*(u32 *)(block_data));
+ for (j = 0; j < 16; j++) {
+ crc_word <<= 1;
+ if (carry != 0)
+ crc_word ^= 0x80050000UL;
+ carry = crc_word & 0x80000000UL;
+ }
+ i++;
+ block_data += (sizeof(u16));
+ }
+ return (u16)(crc_word >> 16);
+}
+
+/**
+ * drx_check_firmware - checks if the loaded firmware is valid
+ *
+ * @demod: demod structure
+ * @mc_data: pointer to the start of the firmware
+ * @size: firmware size
+ */
+static int drx_check_firmware(struct drx_demod_instance *demod, u8 *mc_data,
+ unsigned size)
+{
+ struct drxu_code_block_hdr block_hdr;
+ int i;
+ unsigned count = 2 * sizeof(u16);
+ u32 mc_dev_type, mc_version, mc_base_version;
+ u16 mc_nr_of_blks = be16_to_cpu(*(u32 *)(mc_data + sizeof(u16)));
+
+ /*
+ * Scan microcode blocks first for version info
+ * and firmware check
+ */
+
+ /* Clear version block */
+ DRX_ATTR_MCRECORD(demod).aux_type = 0;
+ DRX_ATTR_MCRECORD(demod).mc_dev_type = 0;
+ DRX_ATTR_MCRECORD(demod).mc_version = 0;
+ DRX_ATTR_MCRECORD(demod).mc_base_version = 0;
+
+ for (i = 0; i < mc_nr_of_blks; i++) {
+ if (count + 3 * sizeof(u16) + sizeof(u32) > size)
+ goto eof;
+
+ /* Process block header */
+ block_hdr.addr = be32_to_cpu(*(u32 *)(mc_data + count));
+ count += sizeof(u32);
+ block_hdr.size = be16_to_cpu(*(u32 *)(mc_data + count));
+ count += sizeof(u16);
+ block_hdr.flags = be16_to_cpu(*(u32 *)(mc_data + count));
+ count += sizeof(u16);
+ block_hdr.CRC = be16_to_cpu(*(u32 *)(mc_data + count));
+ count += sizeof(u16);
+
+ pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n",
+ count, block_hdr.addr, block_hdr.size, block_hdr.flags,
+ block_hdr.CRC);
+
+ if (block_hdr.flags & 0x8) {
+ u8 *auxblk = ((void *)mc_data) + block_hdr.addr;
+ u16 auxtype;
+
+ if (block_hdr.addr + sizeof(u16) > size)
+ goto eof;
+
+ auxtype = be16_to_cpu(*(u32 *)(auxblk));
+
+ /* Aux block. Check type */
+ if (DRX_ISMCVERTYPE(auxtype)) {
+ if (block_hdr.addr + 2 * sizeof(u16) + 2 * sizeof (u32) > size)
+ goto eof;
+
+ auxblk += sizeof(u16);
+ mc_dev_type = be32_to_cpu(*(u32 *)(auxblk));
+ auxblk += sizeof(u32);
+ mc_version = be32_to_cpu(*(u32 *)(auxblk));
+ auxblk += sizeof(u32);
+ mc_base_version = be32_to_cpu(*(u32 *)(auxblk));
+
+ DRX_ATTR_MCRECORD(demod).aux_type = auxtype;
+ DRX_ATTR_MCRECORD(demod).mc_dev_type = mc_dev_type;
+ DRX_ATTR_MCRECORD(demod).mc_version = mc_version;
+ DRX_ATTR_MCRECORD(demod).mc_base_version = mc_base_version;
+
+ pr_info("Firmware dev %x, ver %x, base ver %x\n",
+ mc_dev_type, mc_version, mc_base_version);
+
+ }
+ } else if (count + block_hdr.size * sizeof(u16) > size)
+ goto eof;
+
+ count += block_hdr.size * sizeof(u16);
+ }
+ return 0;
+eof:
+ pr_err("Firmware is truncated at pos %u/%u\n", count, size);
+ return -EINVAL;
+}
+
+/**
+ * drx_ctrl_u_code - Handle microcode upload or verify.
+ * @dev_addr: Address of device.
+ * @mc_info: Pointer to information about microcode data.
+ * @action: Either UCODE_UPLOAD or UCODE_VERIFY
+ *
+ * This function returns:
+ * 0:
+ * - In case of UCODE_UPLOAD: code is successfully uploaded.
+ * - In case of UCODE_VERIFY: image on device is equal to
+ * image provided to this control function.
+ * -EIO:
+ * - In case of UCODE_UPLOAD: I2C error.
+ * - In case of UCODE_VERIFY: I2C error or image on device
+ * is not equal to image provided to this control function.
+ * -EINVAL:
+ * - Invalid arguments.
+ * - Provided image is corrupt
+ */
+static int drx_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;
+ const u8 *mc_data_init = NULL;
+ u8 *mc_data = NULL;
+ unsigned size;
+ char *mc_file = mc_info->mc_file;
+
+ /* Check arguments */
+ if (!mc_info || !mc_file)
+ return -EINVAL;
+
+ if (!demod->firmware) {
+ const struct firmware *fw = NULL;
+
+ 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;
+
+ if (demod->firmware->size < 2 * sizeof(u16)) {
+ rc = -EINVAL;
+ pr_err("Firmware is too short!\n");
+ goto release;
+ }
+
+ pr_info("Firmware %s, size %zu\n",
+ mc_file, demod->firmware->size);
+ }
+
+ mc_data_init = demod->firmware->data;
+ size = demod->firmware->size;
+
+ mc_data = (void *)mc_data_init;
+ /* Check data */
+ mc_magic_word = be16_to_cpu(*(u32 *)(mc_data));
+ mc_data += sizeof(u16);
+ mc_nr_of_blks = be16_to_cpu(*(u32 *)(mc_data));
+ mc_data += sizeof(u16);
+
+ if ((mc_magic_word != DRX_UCODE_MAGIC_WORD) || (mc_nr_of_blks == 0)) {
+ rc = -EINVAL;
+ pr_err("Firmware magic word doesn't match\n");
+ goto release;
+ }
+
+ if (action == UCODE_UPLOAD) {
+ rc = drx_check_firmware(demod, (u8 *)mc_data_init, size);
+ if (rc)
+ goto release;
+
+ /* 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) {
+ pr_err("Validate ucode not supported\n");
+ return rc;
+ }
+ pr_info("Uploading firmware %s\n", mc_file);
+ } else if (action == UCODE_VERIFY) {
+ pr_info("Verifying if firmware upload was ok.\n");
+ }
+
+ /* Process microcode blocks */
+ for (i = 0; i < mc_nr_of_blks; i++) {
+ struct drxu_code_block_hdr block_hdr;
+ u16 mc_block_nr_bytes = 0;
+
+ /* Process block header */
+ block_hdr.addr = be32_to_cpu(*(u32 *)(mc_data));
+ mc_data += sizeof(u32);
+ block_hdr.size = be16_to_cpu(*(u32 *)(mc_data));
+ mc_data += sizeof(u16);
+ block_hdr.flags = be16_to_cpu(*(u32 *)(mc_data));
+ mc_data += sizeof(u16);
+ block_hdr.CRC = be16_to_cpu(*(u32 *)(mc_data));
+ mc_data += sizeof(u16);
+
+ pr_debug("%u: addr %u, size %u, flags 0x%04x, CRC 0x%04x\n",
+ (unsigned)(mc_data - mc_data_init), block_hdr.addr,
+ block_hdr.size, block_hdr.flags, block_hdr.CRC);
+
+ /* Check block header on:
+ - data larger than 64Kb
+ - if CRC enabled check CRC
+ */
+ if ((block_hdr.size > 0x7FFF) ||
+ (((block_hdr.flags & DRX_UCODE_CRC_FLAG) != 0) &&
+ (block_hdr.CRC != drx_u_code_compute_crc(mc_data, block_hdr.size)))
+ ) {
+ /* Wrong data ! */
+ 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));
+
+ /* Perform the desired action */
+ switch (action) {
+ case UCODE_UPLOAD: /* Upload microcode */
+ if (demod->my_access_funct->write_block_func(dev_addr,
+ block_hdr.addr,
+ mc_block_nr_bytes,
+ mc_data, 0x0000)) {
+ rc = -EIO;
+ pr_err("error writing firmware at pos %u\n",
+ (unsigned)(mc_data - mc_data_init));
+ goto release;
+ }
+ break;
+ case UCODE_VERIFY: { /* Verify uploaded microcode */
+ int result = 0;
+ 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_comp,
+ (u8 *)mc_data_buffer,
+ 0x0000)) {
+ pr_err("error reading firmware at pos %u\n",
+ (unsigned)(mc_data - mc_data_init));
+ return -EIO;
+ }
+
+ result =drxbsp_hst_memcmp(curr_ptr,
+ mc_data_buffer,
+ bytes_to_comp);
+
+ if (result) {
+ pr_err("error verifying firmware at pos %u\n",
+ (unsigned)(mc_data - mc_data_init));
+ return -EIO;
+ }
+
+ 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;
+
+ }
+ mc_data += mc_block_nr_bytes;
+ }
+
+ return 0;
+
+release:
+ release_firmware(demod->firmware);
+ demod->firmware = NULL;
+
+ return rc;
+}
+
+/*============================================================================*/
+
+/**
+ * drx_ctrl_version - Build list of version information.
+ * @demod: A pointer to a demodulator instance.
+ * @version_list: Pointer to linked list of versions.
+ *
+ * This function returns:
+ * 0: Version information stored in version_list
+ * -EINVAL: Invalid arguments.
+ */
+static int drx_ctrl_version(struct drx_demod_instance *demod,
+ struct drx_version_list **version_list)
+{
+ static char drx_driver_core_module_name[] = "Core driver";
+ static char drx_driver_core_version_text[] =
+ DRX_VERSIONSTRING(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
+
+ static struct drx_version drx_driver_core_version;
+ static struct drx_version_list drx_driver_core_version_list;
+
+ struct drx_version_list *demod_version_list = NULL;
+ int return_status = -EIO;
+
+ /* Check arguments */
+ if (version_list == NULL)
+ return -EINVAL;
+
+ /* Get version info list from demod */
+ return_status = (*(demod->my_demod_funct->ctrl_func)) (demod,
+ DRX_CTRL_VERSION,
+ (void *)
+ &demod_version_list);
+
+ /* Always fill in the information of the driver SW . */
+ drx_driver_core_version.module_type = DRX_MODULE_DRIVERCORE;
+ drx_driver_core_version.module_name = drx_driver_core_module_name;
+ drx_driver_core_version.v_major = VERSION_MAJOR;
+ drx_driver_core_version.v_minor = VERSION_MINOR;
+ drx_driver_core_version.v_patch = VERSION_PATCH;
+ drx_driver_core_version.v_string = drx_driver_core_version_text;
+
+ drx_driver_core_version_list.version = &drx_driver_core_version;
+ drx_driver_core_version_list.next = (struct drx_version_list *) (NULL);
+
+ if ((return_status == 0) && (demod_version_list != NULL)) {
+ /* Append versioninfo from driver to versioninfo from demod */
+ /* Return version info in "bottom-up" order. This way, multiple
+ devices can be handled without using malloc. */
+ struct drx_version_list *current_list_element = demod_version_list;
+ while (current_list_element->next != NULL)
+ current_list_element = current_list_element->next;
+ current_list_element->next = &drx_driver_core_version_list;
+
+ *version_list = demod_version_list;
+ } else {
+ /* Just return versioninfo from driver */
+ *version_list = &drx_driver_core_version_list;
+ }
+
+ return 0;
+}
+
+/*
+ * Exported functions
+ */
+
+/**
+ * drx_open - Open a demodulator instance.
+ * @demod: A pointer to a demodulator instance.
+ *
+ * This function returns:
+ * 0: Opened demod instance with succes.
+ * -EIO: Driver not initialized or unable to initialize
+ * demod.
+ * -EINVAL: Demod instance has invalid content.
+ *
+ */
+
+int drx_open(struct drx_demod_instance *demod)
+{
+ int status = 0;
+
+ if ((demod == NULL) ||
+ (demod->my_demod_funct == NULL) ||
+ (demod->my_common_attr == NULL) ||
+ (demod->my_ext_attr == NULL) ||
+ (demod->my_i2c_dev_addr == NULL) ||
+ (demod->my_common_attr->is_opened)) {
+ return -EINVAL;
+ }
+
+ status = (*(demod->my_demod_funct->open_func)) (demod);
+
+ if (status == 0)
+ demod->my_common_attr->is_opened = true;
+
+ return status;
+}
+
+/*============================================================================*/
+
+/**
+ * drx_close - Close device
+ * @demod: A pointer to a demodulator instance.
+ *
+ * Free resources occupied by device instance.
+ * Put device into sleep mode.
+ *
+ * This function returns:
+ * 0: Closed demod instance with succes.
+ * -EIO: Driver not initialized or error during close
+ * demod.
+ * -EINVAL: Demod instance has invalid content.
+ */
+int drx_close(struct drx_demod_instance *demod)
+{
+ int status = 0;
+
+ if ((demod == NULL) ||
+ (demod->my_demod_funct == NULL) ||
+ (demod->my_common_attr == NULL) ||
+ (demod->my_ext_attr == NULL) ||
+ (demod->my_i2c_dev_addr == NULL) ||
+ (!demod->my_common_attr->is_opened)) {
+ return -EINVAL;
+ }
+
+ status = (*(demod->my_demod_funct->close_func)) (demod);
+
+ DRX_ATTR_ISOPENED(demod) = false;
+
+ return status;
+}
+/**
+ * drx_ctrl - Control the device.
+ * @demod: A pointer to a demodulator instance.
+ * @ctrl: Reference to desired control function.
+ * @ctrl_data: Pointer to data structure for control function.
+ *
+ * Data needed or returned by the control function is stored in ctrl_data.
+ *
+ * This function returns:
+ * 0: Control function completed successfully.
+ * -EIO: Driver not initialized or error during control demod.
+ * -EINVAL: Demod instance or ctrl_data has invalid content.
+ * -ENOTSUPP: Specified control function is not available.
+ */
+
+int drx_ctrl(struct drx_demod_instance *demod, u32 ctrl, void *ctrl_data)
+{
+ int status = -EIO;
+
+ if ((demod == NULL) ||
+ (demod->my_demod_funct == NULL) ||
+ (demod->my_common_attr == NULL) ||
+ (demod->my_ext_attr == NULL) || (demod->my_i2c_dev_addr == NULL)
+ ) {
+ return -EINVAL;
+ }
+
+ if (((!demod->my_common_attr->is_opened) &&
+ (ctrl != DRX_CTRL_PROBE_DEVICE) && (ctrl != DRX_CTRL_VERSION))
+ ) {
+ return -EINVAL;
+ }
+
+ if ((DRX_ISPOWERDOWNMODE(demod->my_common_attr->current_power_mode) &&
+ (ctrl != DRX_CTRL_POWER_MODE) &&
+ (ctrl != DRX_CTRL_PROBE_DEVICE) &&
+ (ctrl != DRX_CTRL_NOP) && (ctrl != DRX_CTRL_VERSION)
+ )
+ ) {
+ return -ENOTSUPP;
+ }
+
+ /* Fixed control functions */
+ switch (ctrl) {
+ /*======================================================================*/
+ case DRX_CTRL_NOP:
+ /* No operation */
+ return 0;
+ break;
+
+ /*======================================================================*/
+ case DRX_CTRL_VERSION:
+ return drx_ctrl_version(demod, (struct drx_version_list **)ctrl_data);
+ break;
+
+ /*======================================================================*/
+ default:
+ /* Do nothing */
+ break;
+ }
+
+ /* Virtual functions */
+ /* First try calling function from derived class */
+ status = (*(demod->my_demod_funct->ctrl_func)) (demod, ctrl, ctrl_data);
+ if (status == -ENOTSUPP) {
+ /* Now try calling a the base class function */
+ switch (ctrl) {
+ /*===================================================================*/
+ case DRX_CTRL_LOAD_UCODE:
+ return drx_ctrl_u_code(demod,
+ (struct drxu_code_info *)ctrl_data,
+ UCODE_UPLOAD);
+ break;
+
+ /*===================================================================*/
+ case DRX_CTRL_VERIFY_UCODE:
+ {
+ return drx_ctrl_u_code(demod,
+ (struct drxu_code_info *)ctrl_data,
+ UCODE_VERIFY);
+ }
+ break;
+
+ /*===================================================================*/
+ default:
+ pr_err("control %d not supported\n", ctrl);
+ return -ENOTSUPP;
+ }
+ } else {
+ return status;
+ }
+
+ return 0;
+} \ No newline at end of file
OpenPOWER on IntegriCloud