aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/media/dvb-frontends/Kconfig1
-rw-r--r--drivers/media/dvb-frontends/si2165.c107
-rw-r--r--drivers/media/dvb-frontends/si2165_priv.h2
3 files changed, 67 insertions, 43 deletions
diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig
index fe0ddcca192c..2610d71dceb8 100644
--- a/drivers/media/dvb-frontends/Kconfig
+++ b/drivers/media/dvb-frontends/Kconfig
@@ -66,6 +66,7 @@ config DVB_TDA18271C2DD
config DVB_SI2165
tristate "Silicon Labs si2165 based"
depends on DVB_CORE && I2C
+ select CRC_ITU_T
default m if !MEDIA_SUBDRV_AUTOSELECT
help
A DVB-C/T demodulator.
diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c
index 3a2d6c5aded6..fc85fcf9da46 100644
--- a/drivers/media/dvb-frontends/si2165.c
+++ b/drivers/media/dvb-frontends/si2165.c
@@ -25,6 +25,7 @@
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/firmware.h>
+#include <linux/crc-itu-t.h>
#include "dvb_frontend.h"
#include "dvb_math.h"
@@ -329,8 +330,50 @@ static bool si2165_wait_init_done(struct si2165_state *state)
return ret;
}
+static int si2165_count_fw_blocks(struct si2165_state *state, const u8 *data,
+ u32 len)
+{
+ int block_count = 0;
+ u32 offset = 0;
+
+ if (len % 4 != 0) {
+ dev_warn(&state->i2c->dev, "%s: firmware size is not multiple of 4\n",
+ KBUILD_MODNAME);
+ return -EINVAL;
+ }
+
+ while (offset + 4 <= len) {
+ u8 wordcount = data[offset];
+
+ if (wordcount < 1 || data[offset+1] ||
+ data[offset+2] || data[offset+3]) {
+ dev_warn(&state->i2c->dev,
+ "%s: bad fw data[0..3] = %*ph at offset=%d\n",
+ KBUILD_MODNAME, 4, data, offset);
+ return -EINVAL;
+ }
+ offset += 8 + 4 * wordcount;
+ ++block_count;
+ }
+ if (offset != len) {
+ dev_warn(&state->i2c->dev,
+ "%s: firmware length does not match content, len=%d, offset=%d\n",
+ KBUILD_MODNAME, len, offset);
+ return -EINVAL;
+ }
+ if (block_count < 6) {
+ dev_err(&state->i2c->dev, "%s: firmware is too short: Only %d blocks\n",
+ KBUILD_MODNAME, block_count);
+ return -EINVAL;
+ }
+ block_count -= 6;
+ dev_info(&state->i2c->dev, "%s: si2165_upload_firmware counted %d main blocks\n",
+ KBUILD_MODNAME, block_count);
+ return block_count;
+}
+
static int si2165_upload_firmware_block(struct si2165_state *state,
- const u8 *data, u32 len, u32 *poffset, u32 block_count)
+ const u8 *data, u32 len, u32 *poffset, u32 block_count, u16 *crc)
{
int ret;
u8 buf_ctrl[4] = { 0x00, 0x00, 0x00, 0xc0 };
@@ -376,6 +419,10 @@ static int si2165_upload_firmware_block(struct si2165_state *state,
offset += 8;
while (wordcount > 0) {
+ *crc = crc_itu_t_byte(*crc, *(data+offset+3));
+ *crc = crc_itu_t_byte(*crc, *(data+offset+2));
+ *crc = crc_itu_t_byte(*crc, *(data+offset+1));
+ *crc = crc_itu_t_byte(*crc, *(data+offset+0));
ret = si2165_write(state, 0x36c, data+offset, 4);
if (ret < 0)
goto error;
@@ -407,13 +454,12 @@ static int si2165_upload_firmware(struct si2165_state *state)
int ret;
const struct firmware *fw = NULL;
- u8 *fw_file = SI2165_FIRMWARE;
+ u8 *fw_file = SI2165_FIRMWARE_REV_D;
const u8 *data;
u32 len;
- u32 offset;
- u8 patch_version;
- u8 block_count;
- u16 crc_expected;
+ u32 offset = 0;
+ int main_block_count;
+ u16 crc = 0;
/* request the firmware, this will block and timeout */
ret = request_firmware(&fw, fw_file, state->i2c->dev.parent);
@@ -429,32 +475,13 @@ static int si2165_upload_firmware(struct si2165_state *state)
dev_info(&state->i2c->dev, "%s: downloading firmware from file '%s' size=%d\n",
KBUILD_MODNAME, fw_file, len);
- if (len % 4 != 0) {
- dev_warn(&state->i2c->dev, "%s: firmware size is not multiple of 4\n",
- KBUILD_MODNAME);
- ret = -EINVAL;
- goto error;
- }
-
- /* check header (8 bytes) */
- if (len < 8) {
- dev_warn(&state->i2c->dev, "%s: firmware header is missing\n",
- KBUILD_MODNAME);
- ret = -EINVAL;
- goto error;
- }
-
- if (data[0] != 1 || data[1] != 0) {
- dev_warn(&state->i2c->dev, "%s: firmware file version is wrong\n",
- KBUILD_MODNAME);
- ret = -EINVAL;
- goto error;
+ main_block_count = si2165_count_fw_blocks(state, data, len);
+ if (main_block_count < 0) {
+ dev_err(&state->i2c->dev, "%s: si2165_upload_firmware: cannot use firmware, skip\n",
+ KBUILD_MODNAME);
+ return main_block_count;
}
- patch_version = data[2];
- block_count = data[4];
- crc_expected = data[7] << 8 | data[6];
-
/* start uploading fw */
/* boot/wdog status */
ret = si2165_writereg8(state, 0x0341, 0x00);
@@ -480,17 +507,12 @@ static int si2165_upload_firmware(struct si2165_state *state)
if (ret < 0)
goto error;
- /* start right after the header */
- offset = 8;
-
- dev_info(&state->i2c->dev, "%s: si2165_upload_firmware extracted patch_version=0x%02x, block_count=0x%02x, crc_expected=0x%04x\n",
- KBUILD_MODNAME, patch_version, block_count, crc_expected);
-
- ret = si2165_upload_firmware_block(state, data, len, &offset, 1);
+ ret = si2165_upload_firmware_block(state, data, len, &offset, 1, &crc);
if (ret < 0)
goto error;
- ret = si2165_writereg8(state, 0x0344, patch_version);
+ /* patch version, just write a number different from the default 0x00 */
+ ret = si2165_writereg8(state, 0x0344, 0x01);
if (ret < 0)
goto error;
@@ -498,9 +520,10 @@ static int si2165_upload_firmware(struct si2165_state *state)
ret = si2165_writereg8(state, 0x0379, 0x01);
if (ret)
return ret;
+ crc = 0;
ret = si2165_upload_firmware_block(state, data, len,
- &offset, block_count);
+ &offset, main_block_count, &crc);
if (ret < 0) {
dev_err(&state->i2c->dev,
"%s: firmare could not be uploaded\n",
@@ -513,15 +536,15 @@ static int si2165_upload_firmware(struct si2165_state *state)
if (ret)
goto error;
- if (val16 != crc_expected) {
+ if (val16 != crc) {
dev_err(&state->i2c->dev,
"%s: firmware crc mismatch %04x != %04x\n",
- KBUILD_MODNAME, val16, crc_expected);
+ KBUILD_MODNAME, val16, crc);
ret = -EINVAL;
goto error;
}
- ret = si2165_upload_firmware_block(state, data, len, &offset, 5);
+ ret = si2165_upload_firmware_block(state, data, len, &offset, 5, &crc);
if (ret)
goto error;
diff --git a/drivers/media/dvb-frontends/si2165_priv.h b/drivers/media/dvb-frontends/si2165_priv.h
index d4cc93fe1096..52718c375843 100644
--- a/drivers/media/dvb-frontends/si2165_priv.h
+++ b/drivers/media/dvb-frontends/si2165_priv.h
@@ -18,6 +18,6 @@
#ifndef _DVB_SI2165_PRIV
#define _DVB_SI2165_PRIV
-#define SI2165_FIRMWARE "dvb-demod-si2165.fw"
+#define SI2165_FIRMWARE_REV_D "dvb-demod-si2165-d.fw"
#endif /* _DVB_SI2165_PRIV */

Privacy Policy